import unittest
from types import SimpleNamespace
from typing import List
from unittest.mock import patch
from bot.services import user_email_notifications as module
class _FakeI18n:
def gettext(self, language, key, **kwargs):
values = {
"email_payment_failed_subject": "Payment failed",
"email_user_notification_intro": "Account notification.",
"email_user_notification_cta": "Open dashboard",
"email_user_notification_text_dashboard": "Dashboard: {url}",
"email_footer_auto": "Sent automatically by {brand}.",
}
return values.get(key, key).format(**kwargs)
class _FakeEmailService:
instances: List["_FakeEmailService"] = []
def __init__(self, settings, i18n=None):
self.settings = settings
self.i18n = i18n
self.sent: List[dict] = []
_FakeEmailService.instances.append(self)
async def send_rendered_email(self, *, email, content):
self.sent.append({"email": email, "content": content})
def _settings(**overrides):
values = {
"email_auth_configured": True,
"DEFAULT_LANGUAGE": "en",
"WEBAPP_PRIMARY_COLOR": "#00fe7a",
"WEBAPP_TITLE": "Mini Shop",
"WEBAPP_LOGO_URL": "",
"WEBAPP_LOGO_USE_EMOJI": False,
}
values.update(overrides)
return SimpleNamespace(**values)
class SendUserNotificationEmailTests(unittest.IsolatedAsyncioTestCase):
def setUp(self) -> None:
_FakeEmailService.instances = []
async def test_sends_rendered_notification_to_linked_email(self):
user = SimpleNamespace(email="user@example.com", language_code="en")
with patch.object(module, "EmailAuthService", _FakeEmailService):
sent = await module.send_user_notification_email(
settings=_settings(),
i18n=_FakeI18n(),
user=user,
subject_key="email_payment_failed_subject",
message_text="Important\n",
dashboard_url="https://app.example.com/account",
)
self.assertTrue(sent)
self.assertEqual(len(_FakeEmailService.instances), 1)
payload = _FakeEmailService.instances[0].sent[0]
self.assertEqual(payload["email"], "user@example.com")
content = payload["content"]
self.assertEqual(content.subject, "Payment failed")
self.assertIn("Important", content.text)
self.assertIn("Dashboard: https://app.example.com/account", content.text)
self.assertIn("Important", content.html)
self.assertIn("<script>alert(1)</script>", content.html)
async def test_skips_when_smtp_is_not_configured(self):
user = SimpleNamespace(email="user@example.com", language_code="en")
with patch.object(module, "EmailAuthService", _FakeEmailService):
sent = await module.send_user_notification_email(
settings=_settings(email_auth_configured=False),
i18n=_FakeI18n(),
user=user,
subject_key="email_payment_failed_subject",
message_text="Payment failed",
)
self.assertFalse(sent)
self.assertEqual(_FakeEmailService.instances, [])
async def test_skips_when_user_has_no_email(self):
user = SimpleNamespace(email=" ", language_code="en")
with patch.object(module, "EmailAuthService", _FakeEmailService):
sent = await module.send_user_notification_email(
settings=_settings(),
i18n=_FakeI18n(),
user=user,
subject_key="email_payment_failed_subject",
message_text="Payment failed",
)
self.assertFalse(sent)
self.assertEqual(_FakeEmailService.instances, [])
if __name__ == "__main__": # pragma: no cover
unittest.main()