# -*- coding: utf-8; -*- from unittest import TestCase from unittest.mock import patch, MagicMock import pytest from wuttjamaican.email import handler as mod from wuttjamaican.email import Message from wuttjamaican.conf import WuttaConfig from wuttjamaican.util import resource_path from wuttjamaican.exc import ConfigurationError class TestEmailHandler(TestCase): def setUp(self): try: import mako except ImportError: pytest.skip("test not relevant without mako") self.config = WuttaConfig() self.app = self.config.get_app() def make_handler(self, **kwargs): return mod.EmailHandler(self.config, **kwargs) def test_constructor_lookups(self): # empty lookup paths by default, if no providers with patch.object(self.app, 'providers', new={}): handler = self.make_handler() self.assertEqual(handler.txt_templates.directories, []) self.assertEqual(handler.html_templates.directories, []) # provider may specify paths as list providers = { 'wuttatest': MagicMock(email_templates=['wuttjamaican.email:templates']), } with patch.object(self.app, 'providers', new=providers): handler = self.make_handler() path = resource_path('wuttjamaican.email:templates') self.assertEqual(handler.txt_templates.directories, [path]) self.assertEqual(handler.html_templates.directories, [path]) # provider may specify paths as string providers = { 'wuttatest': MagicMock(email_templates='wuttjamaican.email:templates'), } with patch.object(self.app, 'providers', new=providers): handler = self.make_handler() path = resource_path('wuttjamaican.email:templates') self.assertEqual(handler.txt_templates.directories, [path]) self.assertEqual(handler.html_templates.directories, [path]) def test_make_message(self): handler = self.make_handler() msg = handler.make_message() self.assertIsInstance(msg, Message) def test_make_auto_message(self): handler = self.make_handler() # error if default sender not defined self.assertRaises(ConfigurationError, handler.make_auto_message, 'foo') # so let's define that self.config.setdefault('wutta.email.default.sender', 'bob@example.com') # message is empty by default msg = handler.make_auto_message('foo') self.assertIsInstance(msg, Message) self.assertEqual(msg.key, 'foo') self.assertEqual(msg.sender, 'bob@example.com') self.assertEqual(msg.subject, "Automated message") self.assertEqual(msg.to, []) self.assertEqual(msg.cc, []) self.assertEqual(msg.bcc, []) self.assertIsNone(msg.replyto) self.assertIsNone(msg.txt_body) self.assertIsNone(msg.html_body) # but if there is a proper email profile configured for key, # then we should get back a more complete message self.config.setdefault('wutta.email.test_foo.subject', "hello foo") self.config.setdefault('wutta.email.test_foo.to', 'sally@example.com') self.config.setdefault('wutta.email.templates', 'tests.email:templates') handler = self.make_handler() msg = handler.make_auto_message('test_foo') self.assertEqual(msg.key, 'test_foo') self.assertEqual(msg.sender, 'bob@example.com') self.assertEqual(msg.subject, "hello foo") self.assertEqual(msg.to, ['sally@example.com']) self.assertEqual(msg.cc, []) self.assertEqual(msg.bcc, []) self.assertIsNone(msg.replyto) self.assertEqual(msg.txt_body, "hello from foo txt template\n") self.assertEqual(msg.html_body, "
hello from foo html template
\n") # *some* auto methods get skipped if caller specifies the # kwarg at all; others get skipped if kwarg is empty # sender with patch.object(handler, 'get_auto_sender') as get_auto_sender: msg = handler.make_auto_message('foo', sender=None) get_auto_sender.assert_not_called() msg = handler.make_auto_message('foo') get_auto_sender.assert_called_once_with('foo') # subject with patch.object(handler, 'get_auto_subject') as get_auto_subject: msg = handler.make_auto_message('foo', subject=None) get_auto_subject.assert_not_called() msg = handler.make_auto_message('foo') get_auto_subject.assert_called_once_with('foo', {}) # to with patch.object(handler, 'get_auto_to') as get_auto_to: msg = handler.make_auto_message('foo', to=None) get_auto_to.assert_not_called() get_auto_to.return_value = None msg = handler.make_auto_message('foo') get_auto_to.assert_called_once_with('foo') # cc with patch.object(handler, 'get_auto_cc') as get_auto_cc: msg = handler.make_auto_message('foo', cc=None) get_auto_cc.assert_not_called() get_auto_cc.return_value = None msg = handler.make_auto_message('foo') get_auto_cc.assert_called_once_with('foo') # bcc with patch.object(handler, 'get_auto_bcc') as get_auto_bcc: msg = handler.make_auto_message('foo', bcc=None) get_auto_bcc.assert_not_called() get_auto_bcc.return_value = None msg = handler.make_auto_message('foo') get_auto_bcc.assert_called_once_with('foo') # txt_body with patch.object(handler, 'get_auto_txt_body') as get_auto_txt_body: msg = handler.make_auto_message('foo', txt_body=None) get_auto_txt_body.assert_not_called() msg = handler.make_auto_message('foo') get_auto_txt_body.assert_called_once_with('foo', {}) # html_body with patch.object(handler, 'get_auto_html_body') as get_auto_html_body: msg = handler.make_auto_message('foo', html_body=None) get_auto_html_body.assert_not_called() msg = handler.make_auto_message('foo') get_auto_html_body.assert_called_once_with('foo', {}) def test_get_auto_sender(self): handler = self.make_handler() # error if none configured self.assertRaises(ConfigurationError, handler.get_auto_sender, 'foo') # can set global default self.config.setdefault('wutta.email.default.sender', 'bob@example.com') self.assertEqual(handler.get_auto_sender('foo'), 'bob@example.com') # can set for key self.config.setdefault('wutta.email.foo.sender', 'sally@example.com') self.assertEqual(handler.get_auto_sender('foo'), 'sally@example.com') def test_get_auto_subject_template(self): handler = self.make_handler() # global default template = handler.get_auto_subject_template('foo') self.assertEqual(template, "Automated message") # can configure alternate global default self.config.setdefault('wutta.email.default.subject', "Wutta Message") template = handler.get_auto_subject_template('foo') self.assertEqual(template, "Wutta Message") # can configure just for key self.config.setdefault('wutta.email.foo.subject', "Foo Message") template = handler.get_auto_subject_template('foo') self.assertEqual(template, "Foo Message") def test_get_auto_subject(self): handler = self.make_handler() # global default subject = handler.get_auto_subject('foo') self.assertEqual(subject, "Automated message") # can configure alternate global default self.config.setdefault('wutta.email.default.subject', "Wutta Message") subject = handler.get_auto_subject('foo') self.assertEqual(subject, "Wutta Message") # can configure just for key self.config.setdefault('wutta.email.foo.subject', "Foo Message") subject = handler.get_auto_subject('foo') self.assertEqual(subject, "Foo Message") # proper template is rendered self.config.setdefault('wutta.email.bar.subject', "${foo} Message") subject = handler.get_auto_subject('bar', {'foo': "FOO"}) self.assertEqual(subject, "FOO Message") # unless we ask it not to subject = handler.get_auto_subject('bar', {'foo': "FOO"}, rendered=False) self.assertEqual(subject, "${foo} Message") def test_get_auto_recips(self): handler = self.make_handler() # error if bad type requested self.assertRaises(ValueError, handler.get_auto_recips, 'foo', 'doesnotexist') # can configure global default self.config.setdefault('wutta.email.default.to', 'admin@example.com') recips = handler.get_auto_recips('foo', 'to') self.assertEqual(recips, ['admin@example.com']) # can configure just for key self.config.setdefault('wutta.email.foo.to', 'bob@example.com') recips = handler.get_auto_recips('foo', 'to') self.assertEqual(recips, ['bob@example.com']) def test_get_auto_body_template(self): from mako.template import Template handler = self.make_handler() # error if bad request self.assertRaises(ValueError, handler.get_auto_body_template, 'foo', 'BADTYPE') # empty by default template = handler.get_auto_body_template('foo', 'txt') self.assertIsNone(template) # but returns a template if it exists providers = { 'wuttatest': MagicMock(email_templates=['tests.email:templates']), } with patch.object(self.app, 'providers', new=providers): handler = self.make_handler() template = handler.get_auto_body_template('test_foo', 'txt') self.assertIsInstance(template, Template) self.assertEqual(template.uri, 'test_foo.txt.mako') def test_get_auto_txt_body(self): handler = self.make_handler() # empty by default body = handler.get_auto_txt_body('some-random-email') self.assertIsNone(body) # but returns body if template exists providers = { 'wuttatest': MagicMock(email_templates=['tests.email:templates']), } with patch.object(self.app, 'providers', new=providers): handler = self.make_handler() body = handler.get_auto_txt_body('test_foo') self.assertEqual(body, 'hello from foo txt template\n') def test_get_auto_html_body(self): handler = self.make_handler() # empty by default body = handler.get_auto_html_body('some-random-email') self.assertIsNone(body) # but returns body if template exists providers = { 'wuttatest': MagicMock(email_templates=['tests.email:templates']), } with patch.object(self.app, 'providers', new=providers): handler = self.make_handler() body = handler.get_auto_html_body('test_foo') self.assertEqual(body, 'hello from foo html template
\n') def test_deliver_message(self): handler = self.make_handler() msg = handler.make_message(sender='bob@example.com', to='sally@example.com') with patch.object(msg, 'as_string', return_value='msg-str'): # no smtp session since sending email is disabled by default with patch.object(mod, 'smtplib') as smtplib: session = MagicMock() smtplib.SMTP.return_value = session handler.deliver_message(msg) smtplib.SMTP.assert_not_called() session.login.assert_not_called() session.sendmail.assert_not_called() # now let's enable sending self.config.setdefault('wutta.mail.send_emails', 'true') # smtp login not attempted by default with patch.object(mod, 'smtplib') as smtplib: session = MagicMock() smtplib.SMTP.return_value = session handler.deliver_message(msg) smtplib.SMTP.assert_called_once_with('localhost') session.login.assert_not_called() session.sendmail.assert_called_once_with('bob@example.com', {'sally@example.com'}, 'msg-str') # but login attempted if config has credentials self.config.setdefault('wutta.mail.smtp.username', 'bob') self.config.setdefault('wutta.mail.smtp.password', 'seekrit') with patch.object(mod, 'smtplib') as smtplib: session = MagicMock() smtplib.SMTP.return_value = session handler.deliver_message(msg) smtplib.SMTP.assert_called_once_with('localhost') session.login.assert_called_once_with('bob', 'seekrit') session.sendmail.assert_called_once_with('bob@example.com', {'sally@example.com'}, 'msg-str') # error if no sender msg = handler.make_message(to='sally@example.com') self.assertRaises(ValueError, handler.deliver_message, msg) # error if no recips msg = handler.make_message(sender='bob@example.com') self.assertRaises(ValueError, handler.deliver_message, msg) # can set recips as list msg = handler.make_message(sender='bob@example.com') with patch.object(msg, 'as_string', return_value='msg-str'): with patch.object(mod, 'smtplib') as smtplib: session = MagicMock() smtplib.SMTP.return_value = session handler.deliver_message(msg, recips=['sally@example.com']) smtplib.SMTP.assert_called_once_with('localhost') session.sendmail.assert_called_once_with('bob@example.com', {'sally@example.com'}, 'msg-str') # can set recips as string msg = handler.make_message(sender='bob@example.com') with patch.object(msg, 'as_string', return_value='msg-str'): with patch.object(mod, 'smtplib') as smtplib: session = MagicMock() smtplib.SMTP.return_value = session handler.deliver_message(msg, recips='sally@example.com') smtplib.SMTP.assert_called_once_with('localhost') session.sendmail.assert_called_once_with('bob@example.com', {'sally@example.com'}, 'msg-str') # can set recips via to msg = handler.make_message(sender='bob@example.com', to='sally@example.com') with patch.object(msg, 'as_string', return_value='msg-str'): with patch.object(mod, 'smtplib') as smtplib: session = MagicMock() smtplib.SMTP.return_value = session handler.deliver_message(msg) smtplib.SMTP.assert_called_once_with('localhost') session.sendmail.assert_called_once_with('bob@example.com', {'sally@example.com'}, 'msg-str') # can set recips via cc msg = handler.make_message(sender='bob@example.com', cc='sally@example.com') with patch.object(msg, 'as_string', return_value='msg-str'): with patch.object(mod, 'smtplib') as smtplib: session = MagicMock() smtplib.SMTP.return_value = session handler.deliver_message(msg) smtplib.SMTP.assert_called_once_with('localhost') session.sendmail.assert_called_once_with('bob@example.com', {'sally@example.com'}, 'msg-str') # can set recips via bcc msg = handler.make_message(sender='bob@example.com', bcc='sally@example.com') with patch.object(msg, 'as_string', return_value='msg-str'): with patch.object(mod, 'smtplib') as smtplib: session = MagicMock() smtplib.SMTP.return_value = session handler.deliver_message(msg) smtplib.SMTP.assert_called_once_with('localhost') session.sendmail.assert_called_once_with('bob@example.com', {'sally@example.com'}, 'msg-str') def test_sending_is_enabled(self): handler = self.make_handler() # off by default self.assertFalse(handler.sending_is_enabled()) # but can be turned on self.config.setdefault('wutta.mail.send_emails', 'true') self.assertTrue(handler.sending_is_enabled()) def test_send_email(self): with patch.object(mod.EmailHandler, 'deliver_message') as deliver_message: handler = self.make_handler() # deliver_message() is called handler.send_email('foo', sender='bob@example.com', to='sally@example.com', txt_body='hello world') deliver_message.assert_called_once() # make_auto_message() called only if needed with patch.object(handler, 'make_auto_message') as make_auto_message: msg = handler.make_message() handler.send_email(message=msg) make_auto_message.assert_not_called() handler.send_email('foo', sender='bob@example.com', to='sally@example.com', txt_body='hello world') make_auto_message.assert_called_once_with('foo', {}, sender='bob@example.com', to='sally@example.com', txt_body='hello world')