async def test_mail_pop(redis): db = RedisDb() await db.connect("redis://*****:*****@example.com"], "cc": [], "subject": "Test Subject", "text": "", "html": "", "template_id": "", "parameters": None, "meta": None, } await db.add_mail(Mail(**email_dict)) async def async_add_mail(email_dict): await asyncio.sleep(1) await db.add_mail(email_dict) async with db.mail_consumer("worker1") as consumer: async for mail in consumer.mail_queue(): assert mail.to[0] == "*****@*****.**" assert (await consumer.consumer_queue_size()) == 1 await consumer.ack_mail(mail) assert (await consumer.consumer_queue_size()) == 0 consumer.stopped_event.set()
async def mail_queue(self, stopped_event=None): for _ in range(await self.db.redis.llen(settings.redis_mail_queue)): mail = await self.db.redis.rpop(settings.redis_mail_queue) if mail is not None: await self.db.redis.lpush(self.consumer_queue, mail) yield Mail(**unpack(mail)) else: break
async def mail_queue(self, stopped_event: Optional[Event] = None): """Pop one email""" self.stopped_event = stopped_event or Event() while not self.stopped_event.is_set(): mail = await self.db.redis.brpoplpush(settings.redis_mail_queue, self.consumer_queue, timeout=1) if mail is not None: yield Mail(**unpack(mail))
def mail_generator(count: int) -> List[Mail]: for x in range(count): yield Mail( to=[f"mail{x}@example.com"], subject=f"[{x}] test subject", parameters={ "non_secret": "test", "secret_data": "secret" }, template_id="none", text="", )
def test_send(test_cli, mock_aioredis): response = test_cli.post( "/api/v1/send", json={ "to": ["*****@*****.**"], "cc": [], "subject": "Test Email", "text": "Some text", }, ) assert response.status_code == 200, response.text mail = Mail(**unpack(mock_aioredis.me[settings.redis_mail_queue].pop())) assert mail.to[0] == "*****@*****.**"
async def report_failed_mail(self, mail: Mail, traceback: Dict[str, Any]): """Report failed mail to a failed mails queue which is: "{settings.redis_mail_queue}::failed" Args: mail (Mail): mail object traceback (Dict[str, Any]): serialized traceback with tblib """ new_mail_parameters = dict( (key, "X" * len(value) if key.startswith("secret_") else value) for key, value in mail.parameters.items()) failed_mail = mail.dict() failed_mail["parameters"] = new_mail_parameters payload = {"mail": failed_mail, "traceback": traceback} await self.db.redis.lpush(f"{settings.redis_mail_queue}::failed", pack(payload))
async def test_sending_through_connector(monkeypatch): mock_send = CoroutineMock() def mock_get_connector(module): return mock_send monkeypatch.setattr(pyuubin.mailer, "get_connector", mock_get_connector) mail = Mail(**{ "to": ["*****@*****.**"], "subject": "test subject", "text": "yo" }) templates = Templates({}) await send_mail(mail, templates) mock_send.assert_awaited_once_with(mail, templates)
@pytest.fixture def mock_smtp(monkeypatch): mocked_smtp = MockSMTP() monkeypatch.setattr(pyuubin.connectors.smtp, "SMTP", mocked_smtp) return mocked_smtp mail = Mail( to=["*****@*****.**"], subject="test subject", template_id="undefined", parameters={"message": "test"}, text="Something", ) templates = Templates({}) async def test_connect_error(mock_smtp): mock_smtp.raise_on_connect(SMTPAuthenticationError(404, "can't connect")) with pytest.raises(CannotSendMessages): await send(mail, templates) mock_smtp.raise_on_connect(SMTPConnectError("can't connect"))
async def ack_mail(self, mail: Mail): """Confirm email sent.""" await self.db.redis.lrem(self.consumer_queue, 1, pack(mail.dict()))
async def add_mail(self, mail: Mail, failure=False): try: await self.redis.lpush(settings.redis_mail_queue, pack(mail.dict())) except TypeError as e: raise TypeError(f"Wrong keywords for Mail: {e}")