Coverage for app/backend/src/tests/test_smtp.py: 100%
21 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
1import re
3from couchers.email.rendering import template_folder
4from couchers.email.smtp import email_proto_to_message, embed_html_relative_images
5from couchers.proto.internal.jobs_pb2 import EmailPart, SendEmailPayload
8def test_verbatim_attachment_headers():
9 """
10 Test that specified Content-Type/Content-Disposition are preserved verbatim (with original quoting).
11 This ensures we can create attachments compatible with older email clients.
12 """
14 send_email_payload = SendEmailPayload(
15 sender_name="alice",
16 sender_email="alice@couchers.org",
17 recipient="bob@couchers.org",
18 subject="greeting",
19 plain="hello",
20 attachments=[
21 EmailPart(
22 data=bytes([0, 255]), # Force base64 encoding
23 content_type='maintype/subtype; quoted-header="value"; unquoted-header=value',
24 content_disposition="attachment",
25 )
26 ],
27 )
29 email_message = email_proto_to_message(send_email_payload, couchers_id="42")
30 smtp_str = email_message.as_string()
32 expected_snippet = """
33Content-Type: maintype/subtype; quoted-header="value"; unquoted-header=value
34Content-Transfer-Encoding: base64
35Content-Disposition: attachment
36MIME-Version: 1.0
37 """.strip()
39 assert expected_snippet in smtp_str
42def test_embed_html_relative_images() -> None:
43 html = """
44 <img src="attachment_imgs/logo-grey.png"/>
45 <img src="https://example.com/foo.png"/>
46 """
48 html, related_parts = embed_html_relative_images(html, base_dir=template_folder, content_id_domain="example.com")
50 content_id_match = re.search(r'src="cid:(.*@example.com)"', html)
51 assert content_id_match is not None, "html was not modified to include content id"
53 assert "https://example.com/foo.png" in html, "Absolute image URL was replaced"
55 assert len(related_parts) == 1
56 related_part = related_parts[0]
57 assert related_part.content_type == 'image/png; name="logo-grey.png"'
58 assert related_part.content_disposition == 'inline; filename="logo-grey.png"'
59 assert related_part.content_id == f"<{content_id_match.group(1)}>"