Skip to content

Commit f1c47eb

Browse files
committed
Mailtrap: add integration tests
1 parent d143a06 commit f1c47eb

File tree

3 files changed

+153
-8
lines changed

3 files changed

+153
-8
lines changed

anymail/backends/mailtrap.py

Lines changed: 8 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -144,14 +144,19 @@ def set_html_body(self, body):
144144

145145
def add_attachment(self, attachment: Attachment):
146146
att: MailtrapAttachment = {
147-
"filename": attachment.name or "",
147+
"disposition": "attachment",
148+
"filename": attachment.name,
148149
"content": attachment.b64content,
149150
}
150151
if attachment.mimetype:
151152
att["type"] = attachment.mimetype
152153
if attachment.inline:
154+
if not attachment.cid:
155+
self.unsupported_feature("inline attachment without content-id")
153156
att["disposition"] = "inline"
154157
att["content_id"] = attachment.cid
158+
elif not attachment.name:
159+
self.unsupported_feature("attachment without filename")
155160
self.data.setdefault("attachments", []).append(att)
156161

157162
def set_tags(self, tags: List[str]):
@@ -261,12 +266,9 @@ def parse_recipient_status(
261266
]
262267
recipient_status = {
263268
email: AnymailRecipientStatus(
264-
message_id=message_id,
269+
message_id=parsed_response["message_ids"][0],
265270
status="sent",
266271
)
267-
for email, message_id in zip(
268-
recipient_status_order, parsed_response["message_ids"]
269-
)
272+
for email in recipient_status_order
270273
}
271-
272274
return recipient_status

tests/test_mailtrap_backend.py

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,3 @@
1-
# FILE: tests/test_mailtrap_backend.py
2-
31
import unittest
42
from datetime import datetime
53
from decimal import Decimal

tests/test_mailtrap_integration.py

Lines changed: 145 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,145 @@
1+
import os
2+
import unittest
3+
from email.utils import formataddr
4+
5+
from django.test import SimpleTestCase, override_settings, tag
6+
7+
from anymail.exceptions import AnymailAPIError
8+
from anymail.message import AnymailMessage
9+
10+
from .utils import AnymailTestMixin, sample_image_path
11+
12+
ANYMAIL_TEST_MAILTRAP_API_TOKEN = os.getenv("ANYMAIL_TEST_MAILTRAP_API_TOKEN")
13+
ANYMAIL_TEST_MAILTRAP_TEST_INBOX_ID = os.getenv("ANYMAIL_TEST_MAILTRAP_TEST_INBOX_ID")
14+
# Optional: if provided, use for nicer From address; sandbox doesn't require this
15+
ANYMAIL_TEST_MAILTRAP_DOMAIN = os.getenv("ANYMAIL_TEST_MAILTRAP_DOMAIN")
16+
ANYMAIL_TEST_MAILTRAP_TEMPLATE_UUID = os.getenv("ANYMAIL_TEST_MAILTRAP_TEMPLATE_UUID")
17+
18+
19+
@tag("mailtrap", "live")
20+
@unittest.skipUnless(
21+
ANYMAIL_TEST_MAILTRAP_API_TOKEN and ANYMAIL_TEST_MAILTRAP_TEST_INBOX_ID,
22+
"Set ANYMAIL_TEST_MAILTRAP_API_TOKEN and ANYMAIL_TEST_MAILTRAP_TEST_INBOX_ID"
23+
" environment variables to run Mailtrap integration tests",
24+
)
25+
@override_settings(
26+
ANYMAIL={
27+
"MAILTRAP_API_TOKEN": ANYMAIL_TEST_MAILTRAP_API_TOKEN,
28+
# Use Mailtrap sandbox (testing) API so we don't actually send email
29+
"MAILTRAP_TESTING": True,
30+
"MAILTRAP_TEST_INBOX_ID": ANYMAIL_TEST_MAILTRAP_TEST_INBOX_ID,
31+
# You can override MAILTRAP_TEST_API_URL via env if needed; default is fine
32+
},
33+
EMAIL_BACKEND="anymail.backends.mailtrap.EmailBackend",
34+
)
35+
class MailtrapBackendIntegrationTests(AnymailTestMixin, SimpleTestCase):
36+
"""Mailtrap API integration tests (using sandbox testing inbox)
37+
38+
These tests run against the live Mailtrap API in testing mode, using
39+
ANYMAIL_TEST_MAILTRAP_API_TOKEN for authentication and
40+
ANYMAIL_TEST_MAILTRAP_TEST_INBOX_ID for the sandbox inbox id. No real
41+
email is sent in this mode.
42+
"""
43+
44+
def setUp(self):
45+
super().setUp()
46+
from_domain = ANYMAIL_TEST_MAILTRAP_DOMAIN or "anymail.dev"
47+
self.from_email = f"from@{from_domain}"
48+
self.message = AnymailMessage(
49+
"Anymail Mailtrap integration test",
50+
"Text content",
51+
self.from_email,
52+
53+
)
54+
self.message.attach_alternative("<p>HTML content</p>", "text/html")
55+
56+
def test_simple_send(self):
57+
# Example of getting the Mailtrap send status and message id from the message
58+
sent_count = self.message.send()
59+
self.assertEqual(sent_count, 1)
60+
61+
anymail_status = self.message.anymail_status
62+
sent_status = anymail_status.recipients["[email protected]"].status
63+
message_id = anymail_status.recipients["[email protected]"].message_id
64+
65+
self.assertEqual(sent_status, "sent") # Mailtrap reports sent on success
66+
self.assertRegex(message_id, r".+") # non-empty string
67+
# set of all recipient statuses:
68+
self.assertEqual(anymail_status.status, {sent_status})
69+
self.assertEqual(anymail_status.message_id, message_id)
70+
71+
def test_all_options(self):
72+
message = AnymailMessage(
73+
subject="Anymail Mailtrap all-options integration test",
74+
body="This is the text body",
75+
from_email=formataddr(("Test From, with comma", self.from_email)),
76+
to=[
77+
78+
"Recipient 2 <[email protected]>",
79+
],
80+
81+
bcc=["[email protected]", "Blind Copy 2 <[email protected]>"],
82+
reply_to=[
83+
'"Reply, with comma" <[email protected]>',
84+
85+
],
86+
headers={"X-Anymail-Test": "value", "X-Anymail-Count": "3"},
87+
metadata={"meta1": "simple string", "meta2": 2},
88+
# Mailtrap supports only a single tag/category
89+
tags=["tag 1"],
90+
track_clicks=True,
91+
track_opens=True,
92+
)
93+
message.attach("attachment1.txt", "Here is some\ntext for you", "text/plain")
94+
message.attach("attachment2.csv", "ID,Name\n1,Amy Lina", "text/csv")
95+
cid = message.attach_inline_image_file(sample_image_path())
96+
message.attach_alternative(
97+
"<p><b>HTML:</b> with <a href='http://example.com'>link</a>"
98+
f"and image: <img src='cid:{cid}'></div>",
99+
"text/html",
100+
)
101+
102+
message.send()
103+
self.assertEqual(message.anymail_status.status, {"sent"})
104+
self.assertEqual(
105+
message.anymail_status.recipients["[email protected]"].status, "sent"
106+
)
107+
self.assertEqual(
108+
message.anymail_status.recipients["[email protected]"].status, "sent"
109+
)
110+
111+
@unittest.skipUnless(
112+
ANYMAIL_TEST_MAILTRAP_TEMPLATE_UUID,
113+
"Set ANYMAIL_TEST_MAILTRAP_TEMPLATE_UUID to test Mailtrap stored templates",
114+
)
115+
def test_stored_template(self):
116+
message = AnymailMessage(
117+
# UUID of a template available in your Mailtrap account
118+
template_id=ANYMAIL_TEST_MAILTRAP_TEMPLATE_UUID,
119+
to=["[email protected]", "Second Recipient <[email protected]>"],
120+
merge_global_data={ # Mailtrap uses template_variables for global vars
121+
"company_info_name": "Test_Company_info_name",
122+
"name": "Test_Name",
123+
"company_info_address": "Test_Company_info_address",
124+
"company_info_city": "Test_Company_info_city",
125+
"company_info_zip_code": "Test_Company_info_zip_code",
126+
"company_info_country": "Test_Company_info_country",
127+
},
128+
)
129+
# Use template's configured sender if desired
130+
message.from_email = self.from_email
131+
message.send()
132+
self.assertEqual(message.anymail_status.status, {"sent"})
133+
134+
@override_settings(
135+
ANYMAIL={
136+
"MAILTRAP_API_TOKEN": "Hey, that's not an API token!",
137+
"MAILTRAP_TESTING": True,
138+
"MAILTRAP_TEST_INBOX_ID": ANYMAIL_TEST_MAILTRAP_TEST_INBOX_ID,
139+
}
140+
)
141+
def test_invalid_api_token(self):
142+
with self.assertRaises(AnymailAPIError) as cm:
143+
self.message.send()
144+
err = cm.exception
145+
self.assertEqual(err.status_code, 401)

0 commit comments

Comments
 (0)