Coverage for app/backend/src/tests/test_email_localization.py: 100%
29 statements
« prev ^ index » next coverage.py v7.15.1, created at 2026-07-14 20:06 +0000
« prev ^ index » next coverage.py v7.15.1, created at 2026-07-14 20:06 +0000
1"""
2Guardrail tests that every email fully renders in English.
4These emails build their localization keys at runtime (sometimes dynamically from internal
5state, e.g. a host request status), so a missing or mistyped key only blows up when that
6specific email is sent in production. Here we render every email -- and every variant of
7emails whose output depends on internal state -- and assert that no localization key is
8missing. See `EmailBase.test_instances`.
9"""
11import inspect
12from datetime import UTC
14import pytest
16import couchers.email.emails
17from couchers.email.blocks import (
18 EmailBase,
19 EmailFooter,
20 UnsubscribeInfo,
21 UnsubscribeLink,
22)
23from couchers.email.rendering import render_email
24from couchers.i18n import LocalizationContext
27def _all_email_variants() -> list[tuple[str, EmailBase]]:
28 variants: list[tuple[str, EmailBase]] = []
29 for _, email_class in inspect.getmembers(
30 couchers.email.emails, lambda o: inspect.isclass(o) and o.__base__ == EmailBase
31 ):
32 instances = email_class.test_instances()
33 for i, instance in enumerate(instances):
34 variant_id = email_class.__name__ if len(instances) == 1 else f"{email_class.__name__}-{i}"
35 variants.append((variant_id, instance))
36 return variants
39_VARIANTS = _all_email_variants()
41_FOOTER = EmailFooter(
42 timezone_name="UTC",
43 unsubscribe_info=UnsubscribeInfo(
44 manage_notifications_url="https://example.com/manage-notifications",
45 do_not_email_url="https://example.com/do-not-email",
46 topic_action_link=UnsubscribeLink(text="topic-action", url="https://example.com/unsubscribe"),
47 ),
48)
51@pytest.fixture(autouse=True)
52def _(testconfig):
53 pass
56@pytest.mark.parametrize("email", [v for _, v in _VARIANTS], ids=[i for i, _ in _VARIANTS])
57def test_email_renders_in_english(email: EmailBase):
58 loc_context = LocalizationContext(locale="en", timezone=UTC)
60 # Render all email strings, since each resolve their own keys.
61 rendered = render_email(email, _FOOTER, loc_context)
62 assert rendered.subject
63 assert "<" not in rendered.subject, "Subject line shoudn't contain HTML"
64 assert rendered.body_plaintext
65 assert rendered.body_html
66 assert len(rendered.html_image_parts) > 0, "Images should have been embedded"