def test_inet_loopstop(self, autostop_loop, runner): """ Verify behavior when the loop is stopped before controller is stopped """ autostop_loop.set_debug(True) cont = UnthreadedController(Sink(), loop=autostop_loop) cont.begin() # Make sure event loop is not running (will be started in thread) assert autostop_loop.is_running() is False runner(autostop_loop) # Make sure event loop is up and running (started within thread) assert autostop_loop.is_running() is True # Check we can connect with SMTPClient(cont.hostname, cont.port, timeout=AUTOSTOP_DELAY) as client: code, _ = client.helo("example.org") assert code == 250 # Wait until thread ends, which it will be when the loop autostops runner.join(timeout=AUTOSTOP_DELAY) assert runner.is_alive() is False catchup_delay() assert autostop_loop.is_running() is False # At this point, the loop _has_ stopped, but the task is still listening, # so rather than socket.timeout, we'll get a refusal instead, thus causing # SMTPServerDisconnected with pytest.raises(SMTPServerDisconnected): SMTPClient(cont.hostname, cont.port, timeout=0.1) cont.end() catchup_delay() cont.ended.wait() # Now the listener has gone away, and thus we will end up with socket.timeout # or ConnectionError (depending on OS) # noinspection PyTypeChecker with pytest.raises((socket.timeout, ConnectionError)): SMTPClient(cont.hostname, cont.port, timeout=0.1)
def test_process_message(self): for local_ip in '::1', '127.0.0.1': listen_server = self.server_v6 if '::1' == local_ip \ else self.server_v4 msg = self.fake.email_msg() msg_from = msg['From'] msg_to = msg['To'] with SMTPClient(local_ip, self.listen_port) as smtp: channel = listen_server.channels[0] self.assertEqual(channel._auth_login_stage, SMTPuttAuthStatus.AUTH_NONE) smtp.login('testuser1', 'testpass1') self.assertEqual(channel._auth_login_stage, SMTPuttAuthStatus.AUTH_SUCCESSFUL) # Replace the channel's server for testing. with self.create_mock_server(local_ip) as mock_server: channel.smtp_server = mock_server smtp.sendmail(msg['From'], msg['To'], msg.as_string()) mock_server.process_message.assert_called_once() self.assertIsNotNone(self.relay.last_msg) self.assertEqual(self.relay.last_msg['To'], msg_to) self.assertEqual(self.relay.last_msg['From'], msg_from)
def test_failauth_message(self): msg = self.fake.email_msg() # TODO: IPv6 test_failauth_message. with self.assertRaises(SMTPResponseException): with SMTPClient('127.0.0.1', self.listen_port) as smtp: attempt = 0 while 0 >= len(self.server_v4.channels) and attempt < 5: time.sleep(1) attempt += 1 channel = self.server_v4.channels[0] self.assertEqual(channel._auth_login_stage, SMTPuttAuthStatus.AUTH_NONE) with self.assertRaises(SMTPAuthenticationError): smtp.login('testuser1', 'testpass2') self.assertEqual(channel._auth_login_stage, SMTPuttAuthStatus.AUTH_NONE) # Replace the channel's server for testing. with self.create_mock_server('127.0.0.1') as mock_server: channel.smtp_server = mock_server smtp.sendmail(msg['From'], msg['To'], msg.as_string()) mock_server.process_message.assert_not_called() self.assertIsNone(self.relay.last_msg)
def test_auth(self): # TODO: IPv6 test_auth. with SMTPClient('127.0.0.1', self.listen_port) as smtp: self.assertEqual(self.server_v4.channels[0]._auth_login_stage, SMTPuttAuthStatus.AUTH_NONE) smtp.login('testuser1', 'testpass1') self.assertEqual(self.server_v4.channels[0]._auth_login_stage, SMTPuttAuthStatus.AUTH_SUCCESSFUL)
def test_inet_contstop(self, temp_event_loop, runner): """ Verify behavior when the controller is stopped before loop is stopped """ cont = UnthreadedController(Sink(), loop=temp_event_loop) cont.begin() # Make sure event loop is not running (will be started in thread) assert temp_event_loop.is_running() is False runner(temp_event_loop) # Make sure event loop is up and running assert temp_event_loop.is_running() is True try: # Check that we can connect with SMTPClient(cont.hostname, cont.port, timeout=AUTOSTOP_DELAY) as client: code, _ = client.helo("example.org") assert code == 250 client.quit() catchup_delay() temp_event_loop.call_soon_threadsafe(cont.end) for _ in range(10): # 10 is arbitrary catchup_delay( ) # effectively yield to other threads/event loop if cont.ended.wait(1.0): break assert temp_event_loop.is_running() is True # Because we've called .end() there, the server listener should've gone # away, so we should end up with a socket.timeout or ConnectionError or # SMTPServerDisconnected (depending on lotsa factors) expect_errs = (socket.timeout, ConnectionError, SMTPServerDisconnected) # noinspection PyTypeChecker with pytest.raises(expect_errs): SMTPClient(cont.hostname, cont.port, timeout=0.1) finally: # Wrap up, or else we'll hang temp_event_loop.call_soon_threadsafe(cont.cancel_tasks) catchup_delay() runner.join() assert runner.is_alive() is False assert temp_event_loop.is_running() is False assert temp_event_loop.is_closed() is False
def _okay(self, handshake: bytes): with socket.socket(socket.AF_INET, socket.SOCK_STREAM) as sock: sock.connect(Global.SrvAddr) sock.sendall(handshake) resp = sock.makefile("rb").readline() assert resp.startswith(b"220 ") with SMTPClient() as client: client.sock = sock code, mesg = client.ehlo("example.org") assert code == 250 code, mesg = client.quit() assert code == 221
def test_auth_fail(self): # TODO: IPv6 test_auth_fail. try: with SMTPClient('127.0.0.1', self.listen_port) as smtp: self.assertEqual(self.server_v4.channels[0]._auth_login_stage, SMTPuttAuthStatus.AUTH_NONE) with self.assertRaises(SMTPAuthenticationError): smtp.login('testuser1', 'testpass2') self.assertEqual(self.server_v4.channels[0]._auth_login_stage, SMTPuttAuthStatus.AUTH_NONE) except SMTPResponseException: pass
def client(request) -> Generator[SMTPClient, None, None]: """ Generic SMTP Client, will connect to the ``host:port`` defined in ``Global.SrvAddr`` unless overriden using :func:`client_data` marker. """ marker = request.node.get_closest_marker("client_data") if marker: markerdata = marker.kwargs or {} else: markerdata = {} addrport = markerdata.get("connect_to", Global.SrvAddr) with SMTPClient(*addrport) as client: yield client
def test_hiccup(self, plain_controller, handshake): assert plain_controller.smtpd._proxy_timeout > 0.0 with socket.socket(socket.AF_INET, socket.SOCK_STREAM) as sock: sock.connect(Global.SrvAddr) sock.sendall(handshake[0:20]) time.sleep(0.1) sock.sendall(handshake[20:]) resp = sock.makefile("rb").readline() assert resp.startswith(b"220 ") with SMTPClient() as client: client.sock = sock code, mesg = client.ehlo("example.org") assert code == 250 code, mesg = client.quit() assert code == 221
def test_auth_crash(self): msg = self.fake.email_msg() # TODO: IPv6 test_auth_crash. with SMTPClient('127.0.0.1', self.listen_port) as smtp: attempt = 0 while 0 >= len(self.server_v4.channels) and attempt < 5: time.sleep(1) attempt += 1 channel = self.server_v4.channels[0] channel.auth_classes[0].authorize = \ Mock( side_effect=ConnectionError( 'this is expected' ) ) with self.assertRaises(SMTPAuthenticationError) as exc: smtp.login('testuser1', 'testpass1') self.assertEqual(454, exc.exception.smtp_code)
def watch_for_tls(ready_flag, retq: MP.Queue): has_tls = False req_tls = False ready_flag.set() start = time.monotonic() delay = AUTOSTOP_DELAY * 1.5 while (time.monotonic() - start) <= delay: try: with SMTPClient("localhost", 8025) as client: resp = client.docmd("HELP", "HELO") if resp == S.S530_STARTTLS_FIRST: req_tls = True client.ehlo("exemple.org") if "starttls" in client.esmtp_features: has_tls = True break except Exception: time.sleep(0.05) retq.put(has_tls) retq.put(req_tls)
def test_simple(self, plain_controller, handshake, handler_retval): assert plain_controller.smtpd._proxy_timeout > 0.0 assert isinstance(plain_controller.handler, ProxyPeekerHandler) plain_controller.handler.retval = handler_retval if handler_retval: oper = operator.ne # See "Parametrizing conditional raising" in # https://docs.pytest.org/en/stable/example/parametrize.html expect = does_not_raise() else: oper = operator.eq expect = pytest.raises(SMTPServerDisconnected) oper = operator.ne if handler_retval else operator.eq with socket.socket(socket.AF_INET, socket.SOCK_STREAM) as sock: sock.connect(Global.SrvAddr) sock.sendall(handshake) resp = sock.recv(4096) assert oper(resp, b"") with expect, SMTPClient() as client: client.sock = sock code, mesg = client.ehlo("example.org") assert code == 250
def test_relay_crash(self): self.relay.send_email = Mock(side_effect=ConnectionError) msg = self.fake.email_msg() # TODO: IPv6 test_relay_crash. with SMTPClient('127.0.0.1', self.listen_port) as smtp: attempt = 0 while 0 >= len(self.server_v4.channels) and attempt < 5: time.sleep(1) attempt += 1 channel = self.server_v4.channels[0] self.assertEqual(channel._auth_login_stage, SMTPuttAuthStatus.AUTH_NONE) smtp.login('testuser1', 'testpass1') self.assertEqual(channel._auth_login_stage, SMTPuttAuthStatus.AUTH_SUCCESSFUL) # Replace the channel's server for testing. with self.create_mock_server('127.0.0.1') as mock_server: channel.smtp_server = mock_server with self.assertRaises(SMTPDataError) as exc: smtp.sendmail(msg['From'], msg['To'], msg.as_string()) self.assertEqual(421, exc.exception.smtp_code)