def test_attachment_relative(tmpdir): """Attachment with a relative file path is relative to template dir.""" # Simple attachment attachment_path = Path(tmpdir/"attachment.txt") attachment_path.write_text(u"Hello world\n") # Simple template template_path = Path(tmpdir/"template.txt") template_path.write_text(textwrap.dedent(u"""\ TO: [email protected] FROM: [email protected] ATTACHMENT: attachment.txt Hello world """)) # Render template_message = TemplateMessage(template_path) _, _, message = template_message.render({}) # Verify directory used to render is different from template directory assert os.getcwd() != tmpdir # Verify attachment attachments = extract_attachments(message) filename, content = attachments[0] assert filename == "attachment.txt" assert content == b"Hello world\n"
def test_emoji_markdown(tmp_path): """Verify emoji are encoded in Markdown formatted messages.""" template_path = tmp_path / "template.txt" template_path.write_text( textwrap.dedent(u"""\ TO: [email protected] SUBJECT: Testing mailmerge FROM: [email protected] CONTENT-TYPE: text/markdown ``` emoji_string = 😀 ``` """)) # grinning face emoji template_message = TemplateMessage(template_path) _, _, message = template_message.render({}) # Message should contain an unrendered Markdown plaintext part and a # rendered Markdown HTML part plaintext_part, html_part = message.get_payload() # Verify encodings assert str(plaintext_part.get_charset()) == "utf-8" assert str(html_part.get_charset()) == "utf-8" assert plaintext_part["Content-Transfer-Encoding"] == "base64" assert html_part["Content-Transfer-Encoding"] == "base64" # Verify content, which is base64 encoded grinning face emoji plaintext = plaintext_part.get_payload(decode=True).decode("utf-8") htmltext = html_part.get_payload(decode=True).decode("utf-8") assert plaintext == u'```\nemoji_string = \U0001f600\n```' assert htmltext == (u"<html><body><p><code>" u"emoji_string = \U0001f600" u"</code></p></body></html>")
def test_utf8_database(tmp_path): """Verify UTF8 support when template is rendered with UTF-8 value.""" # Simple template template_path = tmp_path / "template.txt" template_path.write_text( textwrap.dedent(u"""\ TO: [email protected] FROM: [email protected] Hi {{name}} """)) # Render template with context containing unicode characters template_message = TemplateMessage(template_path) sender, recipients, message = template_message.render({ "name": u"Laȝamon", }) # Verify sender and recipients assert sender == "*****@*****.**" assert recipients == ["*****@*****.**"] # Verify message encoding. The template was ASCII, but when the template # is rendered with UTF-8 data, the result is UTF-8 encoding. assert message.get_content_maintype() == "text" assert message.get_content_subtype() == "plain" assert message.get_content_charset() == "utf-8" # Verify content plaintext = message.get_payload(decode=True).decode("utf-8") assert plaintext == u"Hi Laȝamon"
def test_emoji_database(tmp_path): """Verify emoji are encoded when they are substituted via template db. The template is ASCII encoded, but after rendering the template, an emoji character will substituted into the template. The result should be a utf-8 encoded message. """ template_path = tmp_path / "template.txt" template_path.write_text( textwrap.dedent(u"""\ TO: [email protected] SUBJECT: Testing mailmerge FROM: [email protected] Hi {{emoji}} """)) template_message = TemplateMessage(template_path) _, _, message = template_message.render({ "emoji": u"😀" # grinning face }) # Verify encoding assert message.get_charset() == "utf-8" assert message["Content-Transfer-Encoding"] == "base64" # Verify content plaintext = message.get_payload(decode=True).decode("utf-8") assert plaintext == u"Hi 😀"
def test_attachment_simple(tmpdir): """Verify a simple attachment.""" # Simple attachment attachment_path = Path(tmpdir/"attachment.txt") attachment_path.write_text(u"Hello world\n") # Simple template template_path = Path(tmpdir/"template.txt") template_path.write_text(textwrap.dedent(u"""\ TO: [email protected] FROM: [email protected] ATTACHMENT: attachment.txt Hello world """)) # Render in tmpdir with tmpdir.as_cwd(): template_message = TemplateMessage(template_path) sender, recipients, message = template_message.render({}) # Verify sender and recipients assert sender == "*****@*****.**" assert recipients == ["*****@*****.**"] # Verify message is multipart and contains attachment assert message.is_multipart() attachments = extract_attachments(message) assert len(attachments) == 1 # Verify attachment filename, content = attachments[0] assert filename == "attachment.txt" assert content == b"Hello world\n"
def test_content_id_header_for_attachments(tmpdir): """All attachments should get a content-id header.""" attachment_path = Path(tmpdir / "attachment.txt") attachment_path.write_text("Hello world\n", encoding="utf8") # Simple template template_path = Path(tmpdir / "template.txt") template_path.write_text(textwrap.dedent("""\ TO: [email protected] FROM: [email protected] ATTACHMENT: attachment.txt Hello world """), encoding="utf8") # Render in tmpdir with tmpdir.as_cwd(): template_message = TemplateMessage(template_path) _, _, message = template_message.render({}) # Verify message is multipart and contains attachment assert message.is_multipart() attachments = extract_attachments(message) assert len(attachments) == 1 # Verify attachment filename, content, cid_header = attachments[0] assert filename == "attachment.txt" assert content == b"Hello world\n" assert re.match(r'<[\d\w]+(\.[\d\w]+)*@mailmerge\.invalid>', cid_header)
def test_attachment_template(tmpdir): """Attachment with template as part of file path.""" # Simple attachment lives in sub directory attachments_dir = tmpdir.mkdir("attachments") attachment_path = Path(attachments_dir/"attachment.txt") attachment_path.write_text(u"Hello world\n") # Simple template template_path = Path(tmpdir/"template.txt") template_path.write_text(textwrap.dedent(u"""\ TO: [email protected] FROM: [email protected] ATTACHMENT: {{filename}} Hello world """)) # Render in tmpdir with tmpdir.as_cwd(): template_message = TemplateMessage(template_path) _, _, message = template_message.render({ "filename": str(attachment_path), }) # Verify attachment attachments = extract_attachments(message) filename, content = attachments[0] assert filename == "attachment.txt" assert content == b"Hello world\n"
def test_attachment_absolute(tmpdir): """Attachment with absolute file path.""" # Simple attachment lives in sub directory attachments_dir = tmpdir.mkdir("attachments") attachment_path = Path(attachments_dir / "attachment.txt") attachment_path.write_text("Hello world\n", encoding="utf8") # Simple template template_path = Path(tmpdir / "template.txt") template_path.write_text(textwrap.dedent(f"""\ TO: [email protected] FROM: [email protected] ATTACHMENT: {attachment_path} Hello world """), encoding="utf8") # Render in tmpdir with tmpdir.as_cwd(): template_message = TemplateMessage(template_path) _, _, message = template_message.render({}) # Verify attachment attachments = extract_attachments(message) filename, content, _ = attachments[0] assert filename == "attachment.txt" assert content == b"Hello world\n"
def test_encoding_multipart_mismatch(tmp_path): """Render a utf-8 template with multipart encoding and wrong headers. Content-Type headers say "us-ascii", but the message contains utf-8. """ template_path = tmp_path / "template.txt" template_path.write_text( textwrap.dedent(u"""\ TO: [email protected] FROM: [email protected] MIME-Version: 1.0 Content-Type: multipart/alternative; boundary="boundary" This is a MIME-encoded message. If you are seeing this, your mail reader is old. --boundary Content-Type: text/plain; charset=us-ascii Hello Laȝamon --boundary Content-Type: text/html; charset=us-ascii <html> <body> <p>Hello Laȝamon</p> </body> </html> """)) template_message = TemplateMessage(template_path) sender, recipients, message = template_message.render({}) # Verify sender and recipients assert sender == "*****@*****.**" assert recipients == ["*****@*****.**"] # Should be multipart: plaintext and HTML assert message.is_multipart() parts = message.get_payload() assert len(parts) == 2 plaintext_part, html_part = parts # Verify plaintext part assert plaintext_part.get_charset() == "utf-8" assert plaintext_part.get_content_charset() == "utf-8" assert plaintext_part.get_content_type() == "text/plain" plaintext = plaintext_part.get_payload(decode=True).decode("utf-8") plaintext = plaintext.strip() assert plaintext == u"Hello Laȝamon" # Verify html part assert html_part.get_charset() == "utf-8" assert html_part.get_content_charset() == "utf-8" assert html_part.get_content_type() == "text/html" htmltext = html_part.get_payload(decode=True).decode("utf-8") htmltext = re.sub(r"\s+", "", htmltext) # Strip whitespace assert htmltext == u"<html><body><p>HelloLaȝamon</p></body></html>"
def test_html_plaintext(tmp_path): """Verify HTML and plaintest multipart template.""" template_path = tmp_path / "template.txt" template_path.write_text(textwrap.dedent(u"""\ TO: [email protected] SUBJECT: Testing mailmerge FROM: [email protected] MIME-Version: 1.0 Content-Type: multipart/alternative; boundary="boundary" This is a MIME-encoded message. If you are seeing this, your mail reader is old. --boundary Content-Type: text/plain; charset=us-ascii {{message}} --boundary Content-Type: text/html; charset=us-ascii <html> <body> <p>{{message}}</p> </body> </html> """)) template_message = TemplateMessage(template_path) sender, recipients, message = template_message.render({ "message": "Hello world" }) # Verify sender and recipients assert sender == "*****@*****.**" assert recipients == ["*****@*****.**"] # Should be multipart: plaintext and HTML assert message.is_multipart() parts = message.get_payload() assert len(parts) == 2 plaintext_part, html_part = parts # Verify plaintext part assert plaintext_part.get_charset() == "us-ascii" assert plaintext_part.get_content_charset() == "us-ascii" assert plaintext_part.get_content_type() == "text/plain" plaintext = plaintext_part.get_payload() plaintext = plaintext.strip() assert plaintext == "Hello world" # Verify html part assert html_part.get_charset() == "us-ascii" assert html_part.get_content_charset() == "us-ascii" assert html_part.get_content_type() == "text/html" htmltext = html_part.get_payload() htmltext = re.sub(r"\s+", "", htmltext) # Strip whitespace assert htmltext == "<html><body><p>Helloworld</p></body></html>"
def test_attachment_image_in_markdown(tmp_path): """Images sent as attachments should get linked correctly in images.""" shutil.copy(str(utils.TESTDATA / "attachment_3.jpg"), str(tmp_path)) # Create template .txt file template_path = tmp_path / "template.txt" template_path.write_text(textwrap.dedent("""\ TO: {{email}} SUBJECT: Testing mailmerge FROM: My Self <*****@*****.**> ATTACHMENT: attachment_3.jpg CONTENT-TYPE: text/markdown ![](./attachment_3.jpg) """), encoding="utf8") template_message = TemplateMessage(template_path) sender, recipients, message = template_message.render( {"email": "*****@*****.**"}) # Verify sender and recipients assert sender == "My Self <*****@*****.**>" assert recipients == ["*****@*****.**"] # Verify message is multipart assert message.is_multipart() # Make sure there is a message body and the attachment payload = message.get_payload() assert len(payload) == 2 # Markdown: Make sure there is a plaintext part and an HTML part message_payload = payload[0].get_payload() assert len(message_payload) == 2 plaintext = extract_text_from_markdown_payload(message_payload[0], 'text/plain') htmltext = extract_text_from_markdown_payload(message_payload[1], 'text/html') assert plaintext.strip() == "![](./attachment_3.jpg)" attachments = extract_attachments(message) assert len(attachments) == 1 filename, content, cid = attachments[0] cid = cid[1:-1] assert filename == "attachment_3.jpg" assert len(content) == 697 expected = html5lib.parse( textwrap.dedent(f"""\ <html><head /> <body><p><img src="cid:{cid}" alt="" /></p></body> </html> """)) assert html_docs_equal(html5lib.parse(htmltext), expected)
def test_utf8_template(tmp_path): """Verify UTF8 support in email template.""" template_path = tmp_path / "template.txt" template_path.write_text( textwrap.dedent(u"""\ TO: [email protected] SUBJECT: Testing mailmerge FROM: [email protected] From the Tagelied of Wolfram von Eschenbach (Middle High German): Sîne klâwen durh die wolken sint geslagen, er stîget ûf mit grôzer kraft, ich sih in grâwen tägelîch als er wil tagen, den tac, der im geselleschaft erwenden wil, dem werden man, den ich mit sorgen în verliez. ich bringe in hinnen, ob ich kan. sîn vil manegiu tugent michz leisten hiez. http://www.columbia.edu/~fdc/utf8/ """)) template_message = TemplateMessage(template_path) sender, recipients, message = template_message.render({ "email": "*****@*****.**", }) # Verify encoding assert message.get_content_maintype() == "text" assert message.get_content_subtype() == "plain" assert message.get_content_charset() == "utf-8" # Verify sender and recipients assert sender == "*****@*****.**" assert recipients == ["*****@*****.**"] # Verify content plaintext = message.get_payload(decode=True).decode("utf-8") assert plaintext == textwrap.dedent(u"""\ From the Tagelied of Wolfram von Eschenbach (Middle High German): Sîne klâwen durh die wolken sint geslagen, er stîget ûf mit grôzer kraft, ich sih in grâwen tägelîch als er wil tagen, den tac, der im geselleschaft erwenden wil, dem werden man, den ich mit sorgen în verliez. ich bringe in hinnen, ob ich kan. sîn vil manegiu tugent michz leisten hiez. http://www.columbia.edu/~fdc/utf8/""")
def test_encoding_us_ascii(tmp_path): """Render a simple template with us-ascii encoding.""" template_path = tmp_path / "template.txt" template_path.write_text(textwrap.dedent("""\ TO: [email protected] FROM: [email protected] Hello world """)) template_message = TemplateMessage(template_path) _, _, message = template_message.render({}) assert message.get_charset() == "us-ascii" assert message.get_content_charset() == "us-ascii" assert message.get_payload() == "Hello world"
def test_encoding_utf8(tmp_path): """Render a simple template with UTF-8 encoding.""" template_path = tmp_path / "template.txt" template_path.write_text(textwrap.dedent("""\ TO: [email protected] FROM: [email protected] Hello Laȝamon """)) template_message = TemplateMessage(template_path) _, _, message = template_message.render({}) assert message.get_charset() == "utf-8" assert message.get_content_charset() == "utf-8" plaintext = message.get_payload(decode=True).decode("utf-8") assert plaintext == "Hello Laȝamon"
def test_attachment_blank(tmpdir): """Attachment header without a filename is an error.""" template_path = Path(tmpdir/"template.txt") template_path.write_text(textwrap.dedent(u"""\ TO: [email protected] FROM: [email protected] ATTACHMENT: Hello world """)) template_message = TemplateMessage(template_path) with pytest.raises(MailmergeError) as err: with tmpdir.as_cwd(): template_message.render({}) assert "Empty attachment header" in str(err)
def test_no_substitutions(tmp_path): """Render a template with an empty context.""" template_path = tmp_path / "template.txt" template_path.write_text(textwrap.dedent(u"""\ TO: [email protected] SUBJECT: Testing mailmerge FROM: [email protected] Hello world! """)) template_message = TemplateMessage(template_path) sender, recipients, message = template_message.render({}) assert sender == "*****@*****.**" assert recipients == ["*****@*****.**"] plaintext = message.get_payload() assert "Hello world!" in plaintext
def test_utf8_subject(tmp_path): """Verify UTF8 support in SUBJECT field.""" template_path = tmp_path / "template.txt" template_path.write_text(textwrap.dedent("""\ TO: [email protected] FROM: [email protected] SUBJECT: Laȝamon {{message}} """)) template_message = TemplateMessage(template_path) _, _, message = template_message.render({ "message": "hello", }) # Verify subject assert message["subject"] == "Laȝamon"
def test_utf8_to(tmp_path): """Verify UTF8 support in TO field.""" template_path = tmp_path / "template.txt" template_path.write_text(textwrap.dedent("""\ TO: Laȝamon <*****@*****.**> FROM: [email protected] {{message}} """)) template_message = TemplateMessage(template_path) _, recipients, message = template_message.render({ "message": "hello", }) # Verify recipient name and email assert recipients == ["*****@*****.**"] assert message["to"] == "Laȝamon <*****@*****.**>"
def test_utf8_from(tmp_path): """Verify UTF8 support in FROM field.""" template_path = tmp_path / "template.txt" template_path.write_text(textwrap.dedent("""\ TO: [email protected] FROM: Laȝamon <*****@*****.**> {{message}} """)) template_message = TemplateMessage(template_path) sender, _, message = template_message.render({ "message": "hello", }) # Verify sender name and email assert sender == "Laȝamon <*****@*****.**>" assert message["from"] == "Laȝamon <*****@*****.**>"
def test_attachment_not_found(tmpdir): """Attachment file not found.""" # Template specifying an attachment that doesn't exist template_path = Path(tmpdir/"template.txt") template_path.write_text(textwrap.dedent(u"""\ TO: [email protected] FROM: [email protected] ATTACHMENT: attachment.txt Hello world """)) # Render in tmpdir, which lacks attachment.txt template_message = TemplateMessage(template_path) with pytest.raises(MailmergeError): with tmpdir.as_cwd(): template_message.render({})
def test_attachment_tilde_path(tmpdir): """Attachment with home directory tilde notation file path.""" template_path = Path(tmpdir/"template.txt") template_path.write_text(textwrap.dedent(u"""\ TO: [email protected] FROM: [email protected] ATTACHMENT: ~/attachment.txt Hello world """)) # Render will throw an error because we didn't create a file in the # user's home directory. We'll just check the filename. template_message = TemplateMessage(template_path) with pytest.raises(MailmergeError) as err: template_message.render({}) correct_path = Path.home() / "attachment.txt" assert str(correct_path) in str(err)
def test_encoding_is8859_1(tmp_path): """Render a simple template with IS8859-1 encoding. Mailmerge will coerce the encoding to UTF-8. """ template_path = tmp_path / "template.txt" template_path.write_text(textwrap.dedent("""\ TO: [email protected] FROM: [email protected] Hello L'Haÿ-les-Roses """)) template_message = TemplateMessage(template_path) _, _, message = template_message.render({}) assert message.get_charset() == "utf-8" assert message.get_content_charset() == "utf-8" plaintext = message.get_payload(decode=True).decode("utf-8") assert plaintext == "Hello L'Haÿ-les-Roses"
def test_simple(tmp_path): """Render a simple template.""" template_path = tmp_path / "template.txt" template_path.write_text(textwrap.dedent(u"""\ TO: [email protected] SUBJECT: Testing mailmerge FROM: [email protected] Hello {{name}}! """)) template_message = TemplateMessage(template_path) sender, recipients, message = template_message.render({ "name": "world", }) assert sender == "*****@*****.**" assert recipients == ["*****@*****.**"] plaintext = message.get_payload() assert "Hello world!" in plaintext
def test_encoding_mismatch(tmp_path): """Render a simple template that lies about its encoding. Header says us-ascii, but it contains utf-8. """ template_path = tmp_path / "template.txt" template_path.write_text(textwrap.dedent("""\ TO: [email protected] FROM: [email protected] Content-Type: text/plain; charset="us-ascii" Hello Laȝamon """)) template_message = TemplateMessage(template_path) _, _, message = template_message.render({}) assert message.get_charset() == "utf-8" assert message.get_content_charset() == "utf-8" plaintext = message.get_payload(decode=True).decode("utf-8") assert plaintext == "Hello Laȝamon"
def test_emoji(tmp_path): """Verify emoji are encoded.""" template_path = tmp_path / "template.txt" template_path.write_text(textwrap.dedent("""\ TO: [email protected] SUBJECT: Testing mailmerge FROM: [email protected] Hi 😀 """)) # grinning face emoji template_message = TemplateMessage(template_path) _, _, message = template_message.render({}) # Verify encoding assert message.get_charset() == "utf-8" assert message["Content-Transfer-Encoding"] == "base64" # Verify content plaintext = message.get_payload(decode=True).decode("utf-8") assert plaintext == "Hi 😀"