From b930c85c3fae78803d131b545e9110645a5a60a3 Mon Sep 17 00:00:00 2001 From: richard-dds Date: Tue, 16 Oct 2018 13:04:30 -0400 Subject: [PATCH] Idea for using composition rather than inheritance --- atst/utils/mailer.py | 92 +++++++++++++++++++++----------------- tests/utils/test_mailer.py | 32 ++++++------- 2 files changed, 65 insertions(+), 59 deletions(-) diff --git a/atst/utils/mailer.py b/atst/utils/mailer.py index a2b7aeb2..0958f296 100644 --- a/atst/utils/mailer.py +++ b/atst/utils/mailer.py @@ -1,8 +1,19 @@ +from contextlib import contextmanager import smtplib from email.message import EmailMessage -class _HostConnection: +class MailConnection(object): + + def send(self, message): + raise NotImplementedError() + + @property + def messages(self): + raise NotImplementedError() + + +class SMTPConnection(MailConnection): def __init__(self, server, port, username, password, use_tls=False): self.server = server self.port = port @@ -11,7 +22,10 @@ class _HostConnection: self.use_tls = use_tls self.host = None - def __enter__(self): + @contextmanager + def _host(self): + host = None + if self.use_tls: self.host = smtplib.SMTP(self.server, self.port) self.host.starttls() @@ -19,52 +33,20 @@ class _HostConnection: self.host = smtplib.SMTP_SSL(self.server, self.port) self.host.login(self.username, self.password) - return self.host + yield host - def __exit__(self, *args): - if self.host: - self.host.quit() + host.quit() - -class BaseMailer: - def __init__(self, server, port, sender, password, use_tls=False): - self.server = server - self.port = port - self.sender = sender - self.password = password - self.use_tls = use_tls - - def _message(self, recipients, subject, body): - msg = EmailMessage() - msg.set_content(body) - msg["From"] = self.sender - msg["To"] = ", ".join(recipients) - msg["Subject"] = subject - - return msg - - def send(self, recipients, subject, body): - pass - - # do not collect messages by default @property def messages(self): return [] - -class Mailer(BaseMailer): - def connection(self): - return _HostConnection( - self.server, self.port, self.sender, self.password, use_tls=self.use_tls - ) - - def send(self, recipients, subject, body): - message = self._message(recipients, subject, body) - with self.connection() as conn: - conn.send_message(message) + def send(self, message): + with self._host() as host: + host.send_message(message) -class RedisMailer(BaseMailer): +class RedisConnection(MailConnection): def __init__(self, redis, **kwargs): super().__init__(**kwargs) self.redis = redis @@ -77,6 +59,34 @@ class RedisMailer(BaseMailer): def messages(self): return [msg.decode() for msg in self.redis.lrange("atat_inbox", 0, -1)] + def send(self, message): + self.redis.lpush("atat_inbox", str(message)) + + +class Mailer(object): + + def __init__(self, connection, sender): + self.connection = connection + self.sender = sender + + def _message(self, recipients, subject, body): + msg = EmailMessage() + msg.set_content(body) + msg["From"] = self.sender + msg["To"] = ", ".join(recipients) + msg["Subject"] = subject + + return msg + def send(self, recipients, subject, body): message = self._message(recipients, subject, body) - self.redis.lpush("atat_inbox", str(message)) + self.connection.send(message) + + @property + def messages(self): + return self.connection.messages + + +class RedisMailer(object): + def __init__(self, *args, **kwargs): + pass diff --git a/tests/utils/test_mailer.py b/tests/utils/test_mailer.py index 0506cfdc..8b40ea48 100644 --- a/tests/utils/test_mailer.py +++ b/tests/utils/test_mailer.py @@ -1,44 +1,40 @@ import pytest -from atst.utils.mailer import Mailer, RedisMailer +from atst.utils.mailer import Mailer, Mailer, MailConnection, RedisConnection -class MockHost: +class MockConnection(MailConnection): def __init__(self): - self.messages = [] + self._messages = [] - def __enter__(self): - return self + def send(self, message): + self._messages.append(message) - def __exit__(self, *args): - pass - - def send_message(self, message): - self.messages.append(message) + @property + def messages(self): + return self._messages @pytest.fixture -def mail_host(): - return MockHost() +def mailer(): + return Mailer(MockConnection(), "test@atat.com") -def test_mailer_can_send_mail(monkeypatch, mail_host): - monkeypatch.setattr("atst.utils.mailer.Mailer.connection", lambda *args: mail_host) - mailer = Mailer("localhost", 456, "leia@rebellion.net", "droidsyourelookingfor") +def test_mailer_can_send_mail(mailer): message_data = { "recipients": ["ben@tattoine.org"], "subject": "help", "body": "you're my only hope", } mailer.send(**message_data) - assert len(mail_host.messages) == 1 - message = mail_host.messages[0] + assert len(mailer.messages) == 1 + message = mailer.messages[0] assert message["To"] == message_data["recipients"][0] assert message["Subject"] == message_data["subject"] assert message.get_content().strip() == message_data["body"] def test_redis_mailer_can_save_messages(app): - mailer = RedisMailer(app.redis, server=None, port=None, sender=None, password=None) + mailer = Mailer(RedisConnection(app.redis), "test@atat.com") message_data = { "recipients": ["ben@tattoine.org"], "subject": "help",