Coverage for app/backend/src/tests/test_smtp.py: 100%
31 statements
« prev ^ index » next coverage.py v7.15.4, created at 2026-08-27 22:00 +0000
« prev ^ index » next coverage.py v7.15.4, created at 2026-08-27 22:00 +0000
1import re
3import pytest
5from couchers.email.rendering import template_folder
6from couchers.email.smtp import email_proto_to_message, embed_html_relative_images
7from couchers.proto.internal.jobs_pb2 import EmailPart, SendEmailPayload
10def test_verbatim_attachment_headers():
11 """
12 Test that specified Content-Type/Content-Disposition are preserved verbatim (with original quoting).
13 This ensures we can create attachments compatible with older email clients.
14 """
16 send_email_payload = SendEmailPayload(
17 sender_name="alice",
18 sender_email="alice@couchers.org",
19 recipient="bob@couchers.org",
20 subject="greeting",
21 plain="hello",
22 attachments=[
23 EmailPart(
24 data=bytes([0, 255]), # Force base64 encoding
25 content_type='maintype/subtype; quoted-header="value"; unquoted-header=value',
26 content_disposition="attachment",
27 )
28 ],
29 )
31 email_message = email_proto_to_message(send_email_payload, couchers_id="42")
32 smtp_str = email_message.as_string()
34 expected_snippet = """
35Content-Type: maintype/subtype; quoted-header="value"; unquoted-header=value
36Content-Transfer-Encoding: base64
37Content-Disposition: attachment
38MIME-Version: 1.0
39 """.strip()
41 assert expected_snippet in smtp_str
44def test_embed_html_relative_images() -> None:
45 html = """
46 <img src="attachment_imgs/logo-grey.png"/>
47 <img src="https://example.com/foo.png"/>
48 """
50 html, related_parts = embed_html_relative_images(html, base_dir=template_folder, content_id_domain="example.com")
52 content_id_match = re.search(r'src="cid:(.*@example.com)"', html)
53 assert content_id_match is not None, "html was not modified to include content id"
55 assert "https://example.com/foo.png" in html, "Absolute image URL was replaced"
57 assert len(related_parts) == 1
58 related_part = related_parts[0]
59 assert related_part.content_type == 'image/png; name="logo-grey.png"'
60 assert related_part.content_disposition == 'inline; filename="logo-grey.png"'
61 assert related_part.content_id == f"<{content_id_match.group(1)}>"
64@pytest.mark.parametrize(
65 ("html", "is_image_ref"),
66 [
67 ('<img src="attachment_imgs/logo-grey.png"/>', True),
68 ('<img\n src="attachment_imgs/logo-grey.png"/>', True),
69 # A url that merely mentions "src=" never yields the `src="` the pattern looks for
70 ('<a href="https://example.com/?src=foo" style="color: #777;">unsubscribe</a>', False),
71 # ...but one that *ends* in "src=" does, and then runs on into the next attribute. Our urls end in
72 # base64 tokens, whose "=" padding makes them end in "src=" about one time in 65k.
73 ('<a href="https://example.com/quick-link?sig=src=" style="color: #777;">unsubscribe</a>', False),
74 # The same url with no attribute following it, so there is no later quote to run the match into
75 ('<a href="https://example.com/quick-link?sig=src=">unsubscribe</a>', False),
76 ],
77)
78def test_embed_html_relative_images_only_matches_src_attributes(html: str, is_image_ref: bool) -> None:
79 """Only a genuine src="" attribute is an image reference, no matter what a url in the markup contains."""
80 embedded_html, related_parts = embed_html_relative_images(
81 html, base_dir=template_folder, content_id_domain="example.com"
82 )
84 if is_image_ref:
85 assert len(related_parts) == 1
86 assert related_parts[0].content_disposition == 'inline; filename="logo-grey.png"'
87 assert f'src="cid:{related_parts[0].content_id[1:-1]}"' in embedded_html
88 else:
89 assert related_parts == []
90 assert embedded_html == html, "markup was rewritten"