def test_no_dry_run(mock_getpass, mock_SMTP_SSL, tmp_path): """Verify --no-dry-run calls SMTP sendmail().""" config_path = tmp_path/"server.conf" config_path.write_text(textwrap.dedent(u"""\ [smtp_server] host = open-smtp.example.com port = 465 security = SSL/TLS username = admin """)) sendmail_client = SendmailClient(config_path, dry_run=False) message = email.message_from_string(u""" TO: [email protected] SUBJECT: Testing mailmerge FROM: [email protected] Hello world """) # Mock the password entry mock_getpass.return_value = "password" # Send a message sendmail_client.sendmail( sender="*****@*****.**", recipients=["*****@*****.**"], message=message, ) # Verify function calls for password and sendmail() assert mock_getpass.call_count == 1 smtp = mock_SMTP_SSL.return_value.__enter__.return_value assert smtp.sendmail.call_count == 1
def test_socket_error(mock_getpass, mock_SMTP_SSL, tmp_path): """Failed socket connection.""" # Config for SSL SMTP server config_path = tmp_path/"server.conf" config_path.write_text(textwrap.dedent(u"""\ [smtp_server] host = smtp.gmail.com port = 465 security = SSL/TLS username = awdeorio """)) # Simple template sendmail_client = SendmailClient(config_path, dry_run=False) message = email.message_from_string(u"Hello world") # Mock the password entry mock_getpass.return_value = "password" # Configure SMTP_SSL constructor to raise an exception mock_SMTP_SSL.return_value.__enter__ = mock.Mock( side_effect=socket.error("Dummy error message") ) # Send a message with pytest.raises(MailmergeError) as err: sendmail_client.sendmail( sender="*****@*****.**", recipients=["*****@*****.**"], message=message, ) # Verify exception string assert "Dummy error message" in str(err.value)
def test_dry_run(mock_getpass, mock_SMTP, tmp_path): """Verify no sendmail() calls when dry_run=True.""" config_path = tmp_path/"server.conf" config_path.write_text(textwrap.dedent(u"""\ [smtp_server] host = open-smtp.example.com port = 25 security = Never """)) sendmail_client = SendmailClient( config_path, dry_run=True, ) message = email.message_from_string(u""" TO: [email protected] SUBJECT: Testing mailmerge FROM: [email protected] Hello world """) sendmail_client.sendmail( sender="*****@*****.**", recipients=["*****@*****.**"], message=message, ) # Verify SMTP wasn't called and password wasn't used assert mock_getpass.call_count == 0 smtp = mock_SMTP.return_value.__enter__.return_value assert smtp.sendmail.call_count == 0
def test_smtp(mock_SMTP, tmp_path): """Verify SMTP library calls.""" config_path = tmp_path/"server.conf" config_path.write_text(textwrap.dedent(u"""\ [smtp_server] host = open-smtp.example.com port = 25 """)) sendmail_client = SendmailClient( config_path, dry_run=False, ) message = email.message_from_string(u""" TO: [email protected] SUBJECT: Testing mailmerge FROM: [email protected] Hello world """) sendmail_client.sendmail( sender="*****@*****.**", recipients=["*****@*****.**"], message=message, ) # Mock smtp object with function calls recorded smtp = mock_SMTP.return_value.__enter__.return_value assert smtp.sendmail.call_count == 1
def test_security_ssl(mock_getpass, mock_SMTP_SSL, mock_SMTP, tmp_path): """Verify open (Never) security configuration.""" # Config for SSL SMTP server config_path = tmp_path/"server.conf" config_path.write_text(textwrap.dedent(u"""\ [smtp_server] host = smtp.mail.umich.edu port = 465 security = SSL/TLS username = YOUR_USERNAME_HERE """)) # Simple template sendmail_client = SendmailClient(config_path, dry_run=False) message = email.message_from_string(u"Hello world") # Mock the password entry mock_getpass.return_value = "password" # Send a message sendmail_client.sendmail( sender="*****@*****.**", recipients=["*****@*****.**"], message=message, ) # Verify SMTP library calls assert mock_getpass.call_count == 1 assert mock_SMTP.call_count == 0 assert mock_SMTP_SSL.call_count == 1 smtp = mock_SMTP_SSL.return_value.__enter__.return_value assert smtp.ehlo.call_count == 0 assert smtp.starttls.call_count == 0 assert smtp.login.call_count == 1 assert smtp.sendmail.call_count == 1
def test_security_open_legacy(mock_SMTP, tmp_path): """Verify legacy "security = Never" configuration.""" # Config SMTP server with "security = Never" legacy option config_path = tmp_path/"server.conf" config_path.write_text(textwrap.dedent(u"""\ [smtp_server] host = open-smtp.example.com port = 25 security = Never """)) # Simple template sendmail_client = SendmailClient(config_path, dry_run=False) message = email.message_from_string(u"Hello world") # Send a message sendmail_client.sendmail( sender="*****@*****.**", recipients=["*****@*****.**"], message=message, ) # Verify SMTP library calls smtp = mock_SMTP.return_value.__enter__.return_value assert smtp.sendmail.call_count == 1
def test_security_open(mock_getpass, mock_SMTP_SSL, mock_SMTP, tmp_path): """Verify open (Never) security configuration.""" # Config for no security SMTP server config_path = tmp_path/"server.conf" config_path.write_text(textwrap.dedent(u"""\ [smtp_server] host = open-smtp.example.com port = 25 """)) # Simple template sendmail_client = SendmailClient(config_path, dry_run=False) message = email.message_from_string(u"Hello world") # Send a message sendmail_client.sendmail( sender="*****@*****.**", recipients=["*****@*****.**"], message=message, ) # Verify SMTP library calls assert mock_getpass.call_count == 0 assert mock_SMTP.call_count == 1 assert mock_SMTP_SSL.call_count == 0 smtp = mock_SMTP.return_value.__enter__.return_value assert smtp.sendmail.call_count == 1 assert smtp.login.call_count == 0
def test_smtp_login_error(mock_getpass, mock_SMTP_SSL, tmp_path): """Login failure.""" # Config for SSL SMTP server config_path = tmp_path/"server.conf" config_path.write_text(textwrap.dedent(u"""\ [smtp_server] host = smtp.gmail.com port = 465 security = SSL/TLS username = awdeorio """)) # Simple template sendmail_client = SendmailClient(config_path, dry_run=False) message = email.message_from_string(u"Hello world") # Mock the password entry mock_getpass.return_value = "password" # Configure SMTP login() to raise an exception mock_SMTP_SSL.return_value.__enter__.return_value.login = mock.Mock( side_effect=smtplib.SMTPAuthenticationError( code=535, msg=( "5.7.8 Username and Password not accepted. Learn more at " "5.7.8 https://support.google.com/mail/?p=BadCredentials " "xyzxyz.32 - gsmtp" ) ) ) # Send a message with pytest.raises(MailmergeError) as err: sendmail_client.sendmail( sender="*****@*****.**", recipients=["*****@*****.**"], message=message, ) # Verify exception string assert "smtp.gmail.com:465 failed to authenticate user 'awdeorio'" in\ str(err.value) assert "535" in str(err.value) assert ( "5.7.8 Username and Password not accepted. Learn more at " "5.7.8 https://support.google.com/mail/?p=BadCredentials " "xyzxyz.32 - gsmtp" ) in str(err.value)
def test_sendmail_ratelimit(mocker, tmp_path): """Verify SMTP library calls.""" config_path = tmp_path / "server.conf" config_path.write_text(textwrap.dedent("""\ [smtp_server] host = open-smtp.example.com port = 25 ratelimit = 60 """), encoding="utf8") sendmail_client = SendmailClient( config_path, dry_run=False, ) message = email.message_from_string(""" TO: [email protected] SUBJECT: Testing mailmerge FROM: [email protected] Hello world """) # Mock SMTP mock_smtp = mocker.patch('smtplib.SMTP') # First message sendmail_client.sendmail( sender="*****@*****.**", recipients=["*****@*****.**"], message=message, ) smtp = mock_smtp.return_value.__enter__.return_value assert smtp.sendmail.call_count == 1 # Second message exceeds the rate limit, doesn't try to send a message with pytest.raises(MailmergeRateLimitError): sendmail_client.sendmail( sender="*****@*****.**", recipients=["*****@*****.**"], message=message, ) assert smtp.sendmail.call_count == 1 # Retry the second message after 1 s because the rate limit is 60 messages # per minute # # Mock the time to be 1.1 s in the future # Ref: https://github.com/spulec/freezegun now = datetime.datetime.now() with freezegun.freeze_time(now + datetime.timedelta(seconds=1)): sendmail_client.sendmail( sender="*****@*****.**", recipients=["*****@*****.**"], message=message, ) assert smtp.sendmail.call_count == 2
def test_bad_config_key(tmp_path): """Verify config file with bad key throws an exception.""" config_path = tmp_path/"server.conf" config_path.write_text(textwrap.dedent(u"""\ [smtp_server] badkey = open-smtp.example.com """)) with pytest.raises(MailmergeError): SendmailClient(config_path, dry_run=True)
def test_missing_username(tmp_path): """Verify exception on missing username.""" config_path = tmp_path/"server.conf" config_path.write_text(textwrap.dedent(u"""\ [smtp_server] host = smtp.mail.umich.edu port = 465 security = SSL/TLS """)) with pytest.raises(MailmergeError): SendmailClient(config_path, dry_run=False)
def test_security_error(tmp_path): """Verify config file with bad security type throws an exception.""" config_path = tmp_path/"server.conf" config_path.write_text(textwrap.dedent(u"""\ [smtp_server] host = smtp.mail.umich.edu port = 465 security = bad_value username = YOUR_USERNAME_HERE """)) with pytest.raises(MailmergeError): SendmailClient(config_path, dry_run=False)