mirror of
https://github.com/django/django.git
synced 2024-12-22 09:05:43 +00:00
Refs #35581 -- Added tests for email parameters, attachments, MIME structure, bcc header, encoding and sending.
This commit is contained in:
parent
5d7001b578
commit
ea34de3bd7
@ -7,11 +7,15 @@ import tempfile
|
||||
from email import charset, message_from_binary_file
|
||||
from email import message_from_bytes as _message_from_bytes
|
||||
from email import policy
|
||||
from email.message import EmailMessage as PyEmailMessage
|
||||
from email.message import Message as PyMessage
|
||||
from email.mime.image import MIMEImage
|
||||
from email.mime.text import MIMEText
|
||||
from io import StringIO
|
||||
from pathlib import Path
|
||||
from smtplib import SMTP, SMTPException
|
||||
from ssl import SSLError
|
||||
from textwrap import dedent
|
||||
from unittest import mock, skipUnless
|
||||
|
||||
from django.core import mail
|
||||
@ -139,6 +143,26 @@ class MailTestsMixin:
|
||||
for attachment in self.get_raw_attachments(django_message)
|
||||
]
|
||||
|
||||
def get_message_structure(self, message, level=0):
|
||||
"""
|
||||
Return a multiline indented string representation
|
||||
of the message's MIME content-type structure, e.g.:
|
||||
|
||||
multipart/mixed
|
||||
multipart/alternative
|
||||
text/plain
|
||||
text/html
|
||||
image/jpg
|
||||
text/calendar
|
||||
"""
|
||||
# Adapted from email.iterators._structure().
|
||||
indent = " " * (level * 4)
|
||||
structure = [f"{indent}{message.get_content_type()}\n"]
|
||||
if message.is_multipart():
|
||||
for subpart in message.get_payload():
|
||||
structure.append(self.get_message_structure(subpart, level + 1))
|
||||
return "".join(structure)
|
||||
|
||||
|
||||
class MailTests(MailTestsMixin, SimpleTestCase):
|
||||
"""
|
||||
@ -299,6 +323,20 @@ class MailTests(MailTestsMixin, SimpleTestCase):
|
||||
).message()
|
||||
self.assertEqual(message.get_all("Cc"), ["foo@example.com"])
|
||||
|
||||
def test_bcc_not_in_headers(self):
|
||||
"""
|
||||
A bcc address should be in the recipients,
|
||||
but not in the (visible) message headers.
|
||||
"""
|
||||
email = EmailMessage(
|
||||
to=["to@example.com"],
|
||||
bcc=["bcc@example.com"],
|
||||
)
|
||||
message = email.message()
|
||||
self.assertNotIn("Bcc", message)
|
||||
self.assertNotIn("bcc@example.com", message.as_string())
|
||||
self.assertEqual(email.recipients(), ["to@example.com", "bcc@example.com"])
|
||||
|
||||
def test_reply_to(self):
|
||||
email = EmailMessage(
|
||||
"Subject",
|
||||
@ -875,6 +913,97 @@ class MailTests(MailTestsMixin, SimpleTestCase):
|
||||
self.assertEqual(content, b"\xff")
|
||||
self.assertEqual(mimetype, "application/octet-stream")
|
||||
|
||||
def test_attach_mime_image(self):
|
||||
"""
|
||||
EmailMessage.attach() docs: "You can pass it
|
||||
a single argument that is a MIMEBase instance."
|
||||
"""
|
||||
# This also verifies complex attachments with extra header fields.
|
||||
email = EmailMessage()
|
||||
image = MIMEImage(b"GIF89a...", "gif")
|
||||
image["Content-Disposition"] = "inline"
|
||||
image["Content-ID"] = "<content-id@example.org>"
|
||||
email.attach(image)
|
||||
|
||||
attachments = self.get_raw_attachments(email)
|
||||
self.assertEqual(len(attachments), 1)
|
||||
image_att = attachments[0]
|
||||
self.assertEqual(image_att.get_content_type(), "image/gif")
|
||||
self.assertEqual(image_att.get_content_disposition(), "inline")
|
||||
self.assertEqual(image_att["Content-ID"], "<content-id@example.org>")
|
||||
self.assertEqual(image_att.get_content(), b"GIF89a...")
|
||||
self.assertIsNone(image_att.get_filename())
|
||||
|
||||
def test_attach_mime_image_in_constructor(self):
|
||||
image = MIMEImage(b"\x89PNG...", "png")
|
||||
image["Content-Disposition"] = "attachment; filename=test.png"
|
||||
email = EmailMessage(attachments=[image])
|
||||
|
||||
attachments = self.get_raw_attachments(email)
|
||||
self.assertEqual(len(attachments), 1)
|
||||
image_att = attachments[0]
|
||||
self.assertEqual(image_att.get_content_type(), "image/png")
|
||||
self.assertEqual(image_att.get_content(), b"\x89PNG...")
|
||||
self.assertEqual(image_att.get_content_disposition(), "attachment")
|
||||
self.assertEqual(image_att.get_filename(), "test.png")
|
||||
|
||||
def test_attach_rfc822_message(self):
|
||||
"""
|
||||
EmailMessage.attach() docs: "If you specify a mimetype of message/rfc822,
|
||||
it will also accept django.core.mail.EmailMessage and email.message.Message."
|
||||
"""
|
||||
# django.core.mail.EmailMessage
|
||||
django_email = EmailMessage("child subject", "child body")
|
||||
# email.message.Message
|
||||
py_message = PyMessage()
|
||||
py_message["Subject"] = "child subject"
|
||||
py_message.set_payload("child body")
|
||||
# email.message.EmailMessage
|
||||
py_email_message = PyEmailMessage()
|
||||
py_email_message["Subject"] = "child subject"
|
||||
py_email_message.set_content("child body")
|
||||
|
||||
cases = [
|
||||
django_email,
|
||||
py_message,
|
||||
py_email_message,
|
||||
# Should also allow message serialized as str or bytes.
|
||||
py_message.as_string(),
|
||||
py_message.as_bytes(),
|
||||
]
|
||||
|
||||
for child_message in cases:
|
||||
with self.subTest(child_type=child_message.__class__):
|
||||
email = EmailMessage("parent message", "parent body")
|
||||
email.attach(content=child_message, mimetype="message/rfc822")
|
||||
self.assertEqual(len(email.attachments), 1)
|
||||
self.assertIsInstance(email.attachments[0], EmailAttachment)
|
||||
self.assertEqual(email.attachments[0].mimetype, "message/rfc822")
|
||||
|
||||
# Make sure it is serialized correctly: a message/rfc822 attachment
|
||||
# whose "body" content (payload) is the "encapsulated" (child) message.
|
||||
attachments = self.get_raw_attachments(email)
|
||||
self.assertEqual(len(attachments), 1)
|
||||
rfc822_attachment = attachments[0]
|
||||
self.assertEqual(rfc822_attachment.get_content_type(), "message/rfc822")
|
||||
|
||||
attached_message = rfc822_attachment.get_content()
|
||||
self.assertEqual(attached_message["Subject"], "child subject")
|
||||
self.assertEqual(attached_message.get_content().rstrip(), "child body")
|
||||
|
||||
# Regression for #18967: Per RFC 2046 5.2.1, "No encoding other
|
||||
# than '7bit', '8bit', or 'binary' is permitted for the body of
|
||||
# a 'message/rfc822' entity." (Default CTE is "7bit".)
|
||||
cte = rfc822_attachment.get("Content-Transfer-Encoding", "7bit")
|
||||
self.assertIn(cte, ("7bit", "8bit", "binary"))
|
||||
|
||||
# Any properly declared CTE is allowed for the attached message itself
|
||||
# (including quoted-printable or base64). For the plain ASCII content
|
||||
# in this test, we'd expect 7bit.
|
||||
child_cte = attached_message.get("Content-Transfer-Encoding", "7bit")
|
||||
self.assertEqual(child_cte, "7bit")
|
||||
self.assertEqual(attached_message.get_content_type(), "text/plain")
|
||||
|
||||
def test_attach_mimebase_prohibits_other_params(self):
|
||||
email_msg = EmailMessage()
|
||||
txt = MIMEText("content")
|
||||
@ -1039,63 +1168,6 @@ class MailTests(MailTestsMixin, SimpleTestCase):
|
||||
s = msg.message().as_string()
|
||||
self.assertIn("Content-Transfer-Encoding: 8bit", s)
|
||||
|
||||
def test_dont_base64_encode_message_rfc822(self):
|
||||
# Ticket #18967
|
||||
# Shouldn't use base64 encoding for a child EmailMessage attachment.
|
||||
# Create a child message first
|
||||
child_msg = EmailMessage(
|
||||
"Child Subject",
|
||||
"Some body of child message",
|
||||
"bounce@example.com",
|
||||
["to@example.com"],
|
||||
headers={"From": "from@example.com"},
|
||||
)
|
||||
child_s = child_msg.message().as_string()
|
||||
|
||||
# Now create a parent
|
||||
parent_msg = EmailMessage(
|
||||
"Parent Subject",
|
||||
"Some parent body",
|
||||
"bounce@example.com",
|
||||
["to@example.com"],
|
||||
headers={"From": "from@example.com"},
|
||||
)
|
||||
|
||||
# Attach to parent as a string
|
||||
parent_msg.attach(content=child_s, mimetype="message/rfc822")
|
||||
parent_s = parent_msg.message().as_string()
|
||||
|
||||
# The child message header is not base64 encoded
|
||||
self.assertIn("Child Subject", parent_s)
|
||||
|
||||
# Feature test: try attaching email.Message object directly to the mail.
|
||||
parent_msg = EmailMessage(
|
||||
"Parent Subject",
|
||||
"Some parent body",
|
||||
"bounce@example.com",
|
||||
["to@example.com"],
|
||||
headers={"From": "from@example.com"},
|
||||
)
|
||||
parent_msg.attach(content=child_msg.message(), mimetype="message/rfc822")
|
||||
parent_s = parent_msg.message().as_string()
|
||||
|
||||
# The child message header is not base64 encoded
|
||||
self.assertIn("Child Subject", parent_s)
|
||||
|
||||
# Feature test: try attaching Django's EmailMessage object directly to the mail.
|
||||
parent_msg = EmailMessage(
|
||||
"Parent Subject",
|
||||
"Some parent body",
|
||||
"bounce@example.com",
|
||||
["to@example.com"],
|
||||
headers={"From": "from@example.com"},
|
||||
)
|
||||
parent_msg.attach(content=child_msg, mimetype="message/rfc822")
|
||||
parent_s = parent_msg.message().as_string()
|
||||
|
||||
# The child message header is not base64 encoded
|
||||
self.assertIn("Child Subject", parent_s)
|
||||
|
||||
def test_custom_utf8_encoding(self):
|
||||
"""A UTF-8 charset with a custom body encoding is respected."""
|
||||
body = "Body with latin characters: àáä."
|
||||
@ -1239,6 +1311,121 @@ class MailTests(MailTestsMixin, SimpleTestCase):
|
||||
with self.assertRaisesMessage(ValueError, msg):
|
||||
email_msg.attach_alternative("<p>content</p>", None)
|
||||
|
||||
def test_mime_structure(self):
|
||||
"""
|
||||
Check generated messages have the expected MIME parts and nesting.
|
||||
"""
|
||||
html_body = EmailAlternative("<p>HTML</p>", "text/html")
|
||||
image = EmailAttachment("image.gif", b"\x89PNG...", "image/png")
|
||||
rfc822_attachment = EmailAttachment(
|
||||
None, EmailMessage(body="text"), "message/rfc822"
|
||||
)
|
||||
cases = [
|
||||
# name, email (EmailMessage or subclass), expected structure
|
||||
(
|
||||
"single body",
|
||||
EmailMessage(body="text"),
|
||||
"""
|
||||
text/plain
|
||||
""",
|
||||
),
|
||||
(
|
||||
"single body with attachment",
|
||||
EmailMessage(body="text", attachments=[image]),
|
||||
"""
|
||||
multipart/mixed
|
||||
text/plain
|
||||
image/png
|
||||
""",
|
||||
),
|
||||
(
|
||||
"alternative bodies",
|
||||
EmailMultiAlternatives(body="text", alternatives=[html_body]),
|
||||
"""
|
||||
multipart/alternative
|
||||
text/plain
|
||||
text/html
|
||||
""",
|
||||
),
|
||||
(
|
||||
"alternative bodies with attachments",
|
||||
EmailMultiAlternatives(
|
||||
body="text", alternatives=[html_body], attachments=[image]
|
||||
),
|
||||
"""
|
||||
multipart/mixed
|
||||
multipart/alternative
|
||||
text/plain
|
||||
text/html
|
||||
image/png
|
||||
""",
|
||||
),
|
||||
(
|
||||
"alternative bodies with rfc822 attachment",
|
||||
EmailMultiAlternatives(
|
||||
body="text",
|
||||
alternatives=[html_body],
|
||||
attachments=[rfc822_attachment],
|
||||
),
|
||||
"""
|
||||
multipart/mixed
|
||||
multipart/alternative
|
||||
text/plain
|
||||
text/html
|
||||
message/rfc822
|
||||
text/plain
|
||||
""",
|
||||
),
|
||||
(
|
||||
"attachment only",
|
||||
EmailMessage(attachments=[image]),
|
||||
# Avoid empty text/plain body.
|
||||
"""
|
||||
multipart/mixed
|
||||
image/png
|
||||
""",
|
||||
),
|
||||
(
|
||||
"alternative only",
|
||||
EmailMultiAlternatives(alternatives=[html_body]),
|
||||
# Avoid empty text/plain body.
|
||||
"""
|
||||
multipart/alternative
|
||||
text/html
|
||||
""",
|
||||
),
|
||||
(
|
||||
"alternative and attachment only",
|
||||
EmailMultiAlternatives(alternatives=[html_body], attachments=[image]),
|
||||
"""
|
||||
multipart/mixed
|
||||
multipart/alternative
|
||||
text/html
|
||||
image/png
|
||||
""",
|
||||
),
|
||||
(
|
||||
"empty EmailMessage",
|
||||
EmailMessage(),
|
||||
"""
|
||||
text/plain
|
||||
""",
|
||||
),
|
||||
(
|
||||
"empty EmailMultiAlternatives",
|
||||
EmailMultiAlternatives(),
|
||||
"""
|
||||
text/plain
|
||||
""",
|
||||
),
|
||||
]
|
||||
for name, email, expected in cases:
|
||||
expected = dedent(expected).lstrip()
|
||||
with self.subTest(name=name):
|
||||
message = email.message()
|
||||
structure = self.get_message_structure(message)
|
||||
self.assertEqual(structure, expected)
|
||||
|
||||
def test_body_contains(self):
|
||||
email_msg = EmailMultiAlternatives()
|
||||
email_msg.body = "I am content."
|
||||
@ -1257,6 +1444,114 @@ class MailTests(MailTestsMixin, SimpleTestCase):
|
||||
email_msg.attach_alternative(b"I am a song.", "audio/mpeg")
|
||||
self.assertIs(email_msg.body_contains("I am content"), True)
|
||||
|
||||
def test_all_params_optional(self):
|
||||
"""
|
||||
EmailMessage class docs: "All parameters are optional"
|
||||
"""
|
||||
email = EmailMessage()
|
||||
self.assertIsInstance(email.message(), PyMessage) # force serialization.
|
||||
|
||||
email = EmailMultiAlternatives()
|
||||
self.assertIsInstance(email.message(), PyMessage) # force serialization.
|
||||
|
||||
def test_positional_arguments_order(self):
|
||||
"""
|
||||
EmailMessage class docs: "… is initialized with the following parameters
|
||||
(in the given order, if positional arguments are used)."
|
||||
"""
|
||||
connection = mail.get_connection()
|
||||
email = EmailMessage(
|
||||
# (If you need to insert/remove/reorder any params here,
|
||||
# that indicates a breaking change to documented behavior.)
|
||||
"subject",
|
||||
"body",
|
||||
"from@example.com",
|
||||
["to@example.com"],
|
||||
["bcc@example.com"],
|
||||
connection,
|
||||
[EmailAttachment("file.txt", "attachment", "text/plain")],
|
||||
{"X-Header": "custom header"},
|
||||
["cc@example.com"],
|
||||
["reply-to@example.com"],
|
||||
# (New options can be added below here, ideally as keyword-only args.)
|
||||
)
|
||||
|
||||
message = email.message()
|
||||
self.assertEqual(message.get_all("Subject"), ["subject"])
|
||||
self.assertEqual(message.get_all("From"), ["from@example.com"])
|
||||
self.assertEqual(message.get_all("To"), ["to@example.com"])
|
||||
self.assertEqual(message.get_all("X-Header"), ["custom header"])
|
||||
self.assertEqual(message.get_all("Cc"), ["cc@example.com"])
|
||||
self.assertEqual(message.get_all("Reply-To"), ["reply-to@example.com"])
|
||||
self.assertEqual(message.get_payload(0).get_payload(), "body")
|
||||
self.assertEqual(
|
||||
self.get_decoded_attachments(email),
|
||||
[("file.txt", "attachment", "text/plain")],
|
||||
)
|
||||
self.assertEqual(
|
||||
email.recipients(), ["to@example.com", "cc@example.com", "bcc@example.com"]
|
||||
)
|
||||
self.assertIs(email.get_connection(), connection)
|
||||
|
||||
def test_all_params_can_be_set_before_send(self):
|
||||
"""
|
||||
EmailMessage class docs: "All parameters … can be set at any time
|
||||
prior to calling the send() method."
|
||||
"""
|
||||
# This is meant to verify EmailMessage.__init__() doesn't apply any
|
||||
# special processing that would be missing for properties set later.
|
||||
original_connection = mail.get_connection(username="original")
|
||||
new_connection = mail.get_connection(username="new")
|
||||
email = EmailMessage(
|
||||
"original subject",
|
||||
"original body",
|
||||
"original-from@example.com",
|
||||
["original-to@example.com"],
|
||||
["original-bcc@example.com"],
|
||||
original_connection,
|
||||
[EmailAttachment("original.txt", "original attachment", "text/plain")],
|
||||
{"X-Header": "original header"},
|
||||
["original-cc@example.com"],
|
||||
["original-reply-to@example.com"],
|
||||
)
|
||||
email.subject = "new subject"
|
||||
email.body = "new body"
|
||||
email.from_email = "new-from@example.com"
|
||||
email.to = ["new-to@example.com"]
|
||||
email.bcc = ["new-bcc@example.com"]
|
||||
email.connection = new_connection
|
||||
email.attachments = [
|
||||
("new1.txt", "new attachment 1", "text/plain"), # plain tuple.
|
||||
EmailAttachment("new2.txt", "new attachment 2", "text/csv"),
|
||||
MIMEImage(b"GIF89a...", "gif"),
|
||||
]
|
||||
email.extra_headers = {"X-Header": "new header"}
|
||||
email.cc = ["new-cc@example.com"]
|
||||
email.reply_to = ["new-reply-to@example.com"]
|
||||
|
||||
message = email.message()
|
||||
self.assertEqual(message.get_all("Subject"), ["new subject"])
|
||||
self.assertEqual(message.get_all("From"), ["new-from@example.com"])
|
||||
self.assertEqual(message.get_all("To"), ["new-to@example.com"])
|
||||
self.assertEqual(message.get_all("X-Header"), ["new header"])
|
||||
self.assertEqual(message.get_all("Cc"), ["new-cc@example.com"])
|
||||
self.assertEqual(message.get_all("Reply-To"), ["new-reply-to@example.com"])
|
||||
self.assertEqual(message.get_payload(0).get_payload(), "new body")
|
||||
self.assertEqual(
|
||||
self.get_decoded_attachments(email),
|
||||
[
|
||||
("new1.txt", "new attachment 1", "text/plain"),
|
||||
("new2.txt", "new attachment 2", "text/csv"),
|
||||
(None, b"GIF89a...", "image/gif"),
|
||||
],
|
||||
)
|
||||
self.assertEqual(
|
||||
email.recipients(),
|
||||
["new-to@example.com", "new-cc@example.com", "new-bcc@example.com"],
|
||||
)
|
||||
self.assertIs(email.get_connection(), new_connection)
|
||||
self.assertNotIn("original", message.as_string())
|
||||
|
||||
|
||||
@requires_tz_support
|
||||
class MailTimeZoneTests(MailTestsMixin, SimpleTestCase):
|
||||
@ -1806,6 +2101,7 @@ class ConsoleBackendTests(BaseEmailBackendTests, SimpleTestCase):
|
||||
class SMTPHandler:
|
||||
def __init__(self, *args, **kwargs):
|
||||
self.mailbox = []
|
||||
self.smtp_envelopes = []
|
||||
|
||||
async def handle_DATA(self, server, session, envelope):
|
||||
data = envelope.content
|
||||
@ -1820,10 +2116,17 @@ class SMTPHandler:
|
||||
if mail_from != header_from:
|
||||
return f"553 '{mail_from}' != '{header_from}'"
|
||||
self.mailbox.append(message)
|
||||
self.smtp_envelopes.append(
|
||||
{
|
||||
"mail_from": envelope.mail_from,
|
||||
"rcpt_tos": envelope.rcpt_tos,
|
||||
}
|
||||
)
|
||||
return "250 OK"
|
||||
|
||||
def flush_mailbox(self):
|
||||
self.mailbox[:] = []
|
||||
self.smtp_envelopes[:] = []
|
||||
|
||||
|
||||
@skipUnless(HAS_AIOSMTPD, "No aiosmtpd library detected.")
|
||||
@ -1870,6 +2173,9 @@ class SMTPBackendTests(BaseEmailBackendTests, SMTPBackendTestsBase):
|
||||
def get_mailbox_content(self):
|
||||
return self.smtp_handler.mailbox
|
||||
|
||||
def get_smtp_envelopes(self):
|
||||
return self.smtp_handler.smtp_envelopes
|
||||
|
||||
@override_settings(
|
||||
EMAIL_HOST_USER="not empty username",
|
||||
EMAIL_HOST_PASSWORD="not empty password",
|
||||
@ -2094,6 +2400,85 @@ class SMTPBackendTests(BaseEmailBackendTests, SMTPBackendTestsBase):
|
||||
sent = backend.send_messages([email])
|
||||
self.assertEqual(sent, 0)
|
||||
|
||||
def test_avoids_sending_to_invalid_addresses(self):
|
||||
"""
|
||||
Verify invalid addresses can't sneak into SMTP commands through
|
||||
EmailMessage.all_recipients() (which is distinct from message header fields).
|
||||
"""
|
||||
backend = smtp.EmailBackend()
|
||||
backend.connection = mock.Mock()
|
||||
for email_address in (
|
||||
# Invalid address with two @ signs.
|
||||
"to@other.com@example.com",
|
||||
# Invalid address without the quotes.
|
||||
"to@other.com <to@example.com>",
|
||||
# Other invalid addresses.
|
||||
"@",
|
||||
"to@",
|
||||
"@example.com",
|
||||
# CR/NL in addr-spec. (SMTP strips display-name.)
|
||||
'"evil@example.com\r\nto"@example.com',
|
||||
"to\nevil@example.com",
|
||||
):
|
||||
with self.subTest(email_address=email_address):
|
||||
# Use bcc (which is only processed by SMTP backend) to ensure
|
||||
# error is coming from SMTP backend, not EmailMessage.message().
|
||||
email = EmailMessage(bcc=[email_address])
|
||||
with self.assertRaisesMessage(ValueError, "Invalid address"):
|
||||
backend.send_messages([email])
|
||||
|
||||
def test_encodes_idna_in_smtp_commands(self):
|
||||
"""
|
||||
SMTP backend must encode non-ASCII domains for the SMTP envelope
|
||||
(which can be distinct from the email headers).
|
||||
"""
|
||||
email = EmailMessage(
|
||||
from_email="lists@discussão.example.org",
|
||||
to=["To Example <to@漢字.example.com>"],
|
||||
bcc=["monitor@discussão.example.org"],
|
||||
headers={
|
||||
"From": "Gestor de listas <lists@discussão.example.org>",
|
||||
"To": "Discussão Django <django@discussão.example.org>",
|
||||
},
|
||||
)
|
||||
backend = smtp.EmailBackend()
|
||||
backend.send_messages([email])
|
||||
envelope = self.get_smtp_envelopes()[0]
|
||||
self.assertEqual(envelope["mail_from"], "lists@xn--discusso-xza.example.org")
|
||||
self.assertEqual(
|
||||
envelope["rcpt_tos"],
|
||||
["to@xn--p8s937b.example.com", "monitor@xn--discusso-xza.example.org"],
|
||||
)
|
||||
|
||||
def test_does_not_reencode_idna(self):
|
||||
"""
|
||||
SMTP backend should not downgrade IDNA 2008 to IDNA 2003.
|
||||
|
||||
Django does not currently handle IDNA 2008 encoding, but should retain
|
||||
it for addresses that have been pre-encoded.
|
||||
"""
|
||||
# Test all four EmailMessage attrs accessed by the SMTP email backend.
|
||||
# These are IDNA 2008 encoded domains that would be different
|
||||
# in IDNA 2003, from https://www.unicode.org/reports/tr46/#Deviations.
|
||||
email = EmailMessage(
|
||||
from_email='"βόλος" <from@xn--fa-hia.example.com>',
|
||||
to=['"faß" <to@xn--10cl1a0b660p.example.com>'],
|
||||
cc=['"ශ්රී" <cc@xn--nxasmm1c.example.com>'],
|
||||
bcc=['"نامهای." <bcc@xn--mgba3gch31f060k.example.com>'],
|
||||
)
|
||||
backend = smtp.EmailBackend()
|
||||
backend.send_messages([email])
|
||||
envelope = self.get_smtp_envelopes()[0]
|
||||
self.assertEqual(envelope["mail_from"], "from@xn--fa-hia.example.com")
|
||||
self.assertEqual(
|
||||
envelope["rcpt_tos"],
|
||||
[
|
||||
"to@xn--10cl1a0b660p.example.com",
|
||||
"cc@xn--nxasmm1c.example.com",
|
||||
"bcc@xn--mgba3gch31f060k.example.com",
|
||||
],
|
||||
)
|
||||
|
||||
|
||||
@skipUnless(HAS_AIOSMTPD, "No aiosmtpd library detected.")
|
||||
class SMTPBackendStoppedServerTests(SMTPBackendTestsBase):
|
||||
|
Loading…
Reference in New Issue
Block a user