CI/Docs: move to anymail.dev

Move all integration tests and contact emails to anymail.dev. 
(Stop using anymail.info.)
This commit is contained in:
Mike Edmunds
2022-02-02 13:59:09 -08:00
committed by GitHub
parent 4d93183e5e
commit 5fdc285e82
13 changed files with 161 additions and 130 deletions

View File

@@ -71,16 +71,22 @@ jobs:
TOX_FORCE_IGNORE_OUTCOME: false
ANYMAIL_RUN_LIVE_TESTS: true
ANYMAIL_TEST_AMAZON_SES_ACCESS_KEY_ID: ${{ secrets.ANYMAIL_TEST_AMAZON_SES_ACCESS_KEY_ID }}
ANYMAIL_TEST_AMAZON_SES_DOMAIN: ${{ secrets.ANYMAIL_TEST_AMAZON_SES_DOMAIN }}
ANYMAIL_TEST_AMAZON_SES_REGION_NAME: ${{ secrets.ANYMAIL_TEST_AMAZON_SES_REGION_NAME }}
ANYMAIL_TEST_AMAZON_SES_SECRET_ACCESS_KEY: ${{ secrets.ANYMAIL_TEST_AMAZON_SES_SECRET_ACCESS_KEY }}
ANYMAIL_TEST_MAILGUN_API_KEY: ${{ secrets.ANYMAIL_TEST_MAILGUN_API_KEY }}
ANYMAIL_TEST_MAILGUN_DOMAIN: ${{ secrets.ANYMAIL_TEST_MAILGUN_DOMAIN }}
ANYMAIL_TEST_MAILJET_API_KEY: ${{ secrets.ANYMAIL_TEST_MAILJET_API_KEY }}
ANYMAIL_TEST_MAILJET_DOMAIN: ${{ secrets.ANYMAIL_TEST_MAILJET_DOMAIN }}
ANYMAIL_TEST_MAILJET_SECRET_KEY: ${{ secrets.ANYMAIL_TEST_MAILJET_SECRET_KEY }}
ANYMAIL_TEST_MANDRILL_API_KEY: ${{ secrets.ANYMAIL_TEST_MANDRILL_API_KEY }}
ANYMAIL_TEST_POSTMARK_DOMAIN: ${{ secrets.ANYMAIL_TEST_POSTMARK_DOMAIN }}
ANYMAIL_TEST_POSTMARK_SERVER_TOKEN: ${{ secrets.ANYMAIL_TEST_POSTMARK_SERVER_TOKEN }}
ANYMAIL_TEST_POSTMARK_TEMPLATE_ID: ${{ secrets.ANYMAIL_TEST_POSTMARK_TEMPLATE_ID }}
ANYMAIL_TEST_SENDGRID_API_KEY: ${{ secrets.ANYMAIL_TEST_SENDGRID_API_KEY }}
ANYMAIL_TEST_SENDGRID_DOMAIN: ${{ secrets.ANYMAIL_TEST_SENDGRID_DOMAIN }}
ANYMAIL_TEST_SENDGRID_TEMPLATE_ID: ${{ secrets.ANYMAIL_TEST_SENDGRID_TEMPLATE_ID }}
ANYMAIL_TEST_SENDINBLUE_API_KEY: ${{ secrets.ANYMAIL_TEST_SENDINBLUE_API_KEY }}
ANYMAIL_TEST_SENDINBLUE_DOMAIN: ${{ secrets.ANYMAIL_TEST_SENDINBLUE_DOMAIN }}
ANYMAIL_TEST_SPARKPOST_API_KEY: ${{ secrets.ANYMAIL_TEST_SPARKPOST_API_KEY }}
ANYMAIL_TEST_SPARKPOST_DOMAIN: ${{ secrets.ANYMAIL_TEST_SPARKPOST_DOMAIN }}

View File

@@ -38,7 +38,7 @@ Bugs
You can report problems or request features in `Anymail's GitHub issue tracker`_.
(For a security-related issue that should not be disclosed publicly, instead email
Anymail's maintainers at security<AT>anymail<DOT>info.)
Anymail's maintainers at *security\<at>anymail\<dot>dev*.)
We also have some :ref:`troubleshooting` information that may be helpful.

View File

@@ -37,7 +37,7 @@ analytics tracking, see the "Information for Visitors of Sites and Apps Using
Google Analytics" section of Google's `Safeguarding your data`_ document.
Questions about privacy and information practices related to this Anymail
documentation site can be emailed to *privacy\<at>anymail\<dot>info*.
documentation site can be emailed to *privacy\<at>anymail\<dot>dev*.
(This is not an appropriate contact for questions about *using* Anymail;
see :ref:`help` if you need assistance with your code.)

View File

@@ -55,7 +55,7 @@ Here's how to contact the Anymail community:
**"I found a security issue!"**
Contact the Anymail maintainers by emailing *security<AT>anymail<DOT>info.*
Contact the Anymail maintainers by emailing *security\<at>anymail\<dot>dev.*
(Please don't open a GitHub issue or post publicly about potential security problems.)
**"Could Anymail support this ESP or feature...?"**

View File

@@ -1,6 +1,7 @@
import os
import unittest
import warnings
from email.utils import formataddr
from django.test import SimpleTestCase, override_settings, tag
@@ -13,11 +14,15 @@ from .utils import AnymailTestMixin, sample_image_path
ANYMAIL_TEST_AMAZON_SES_ACCESS_KEY_ID = os.getenv("ANYMAIL_TEST_AMAZON_SES_ACCESS_KEY_ID")
ANYMAIL_TEST_AMAZON_SES_SECRET_ACCESS_KEY = os.getenv("ANYMAIL_TEST_AMAZON_SES_SECRET_ACCESS_KEY")
ANYMAIL_TEST_AMAZON_SES_REGION_NAME = os.getenv("ANYMAIL_TEST_AMAZON_SES_REGION_NAME", "us-east-1")
ANYMAIL_TEST_AMAZON_SES_DOMAIN = os.getenv("ANYMAIL_TEST_AMAZON_SES_DOMAIN")
@unittest.skipUnless(ANYMAIL_TEST_AMAZON_SES_ACCESS_KEY_ID and ANYMAIL_TEST_AMAZON_SES_SECRET_ACCESS_KEY,
@unittest.skipUnless(
ANYMAIL_TEST_AMAZON_SES_ACCESS_KEY_ID
and ANYMAIL_TEST_AMAZON_SES_SECRET_ACCESS_KEY
and ANYMAIL_TEST_AMAZON_SES_DOMAIN,
"Set ANYMAIL_TEST_AMAZON_SES_ACCESS_KEY_ID and ANYMAIL_TEST_AMAZON_SES_SECRET_ACCESS_KEY "
"environment variables to run Amazon SES integration tests")
"and ANYMAIL_TEST_AMAZON_SES_DOMAIN environment variables to run Amazon SES integration tests")
@override_settings(
EMAIL_BACKEND="anymail.backends.amazon_ses.EmailBackend",
ANYMAIL={
@@ -47,17 +52,13 @@ class AmazonSESBackendIntegrationTests(AnymailTestMixin, SimpleTestCase):
To avoid stacking up a pile of undeliverable @example.com
emails, the tests use Amazon's @simulator.amazonses.com addresses.
https://docs.aws.amazon.com/ses/latest/DeveloperGuide/mailbox-simulator.html
Amazon SES also doesn't support arbitrary senders (so no from@example.com).
We've set up @test-ses.anymail.info as a validated sending domain for these tests.
You may need to change the from_email to your own address when testing.
"""
def setUp(self):
super().setUp()
self.from_email = 'test@%s' % ANYMAIL_TEST_AMAZON_SES_DOMAIN
self.message = AnymailMessage('Anymail Amazon SES integration test', 'Text content',
'test@test-ses.anymail.info', ['success@simulator.amazonses.com'])
self.from_email, ['success@simulator.amazonses.com'])
self.message.attach_alternative('<p>HTML content</p>', "text/html")
# boto3 relies on GC to close connections. Python 3 warns about unclosed ssl.SSLSocket during cleanup.
@@ -85,7 +86,7 @@ class AmazonSESBackendIntegrationTests(AnymailTestMixin, SimpleTestCase):
message = AnymailMessage(
subject="Anymail Amazon SES all-options integration test",
body="This is the text body",
from_email='"Test From" <test@test-ses.anymail.info>',
from_email=formataddr(("Test From, with comma", self.from_email)),
to=["success+to1@simulator.amazonses.com", "Recipient 2 <success+to2@simulator.amazonses.com>"],
cc=["success+cc1@simulator.amazonses.com", "Copy 2 <success+cc2@simulator.amazonses.com>"],
bcc=["success+bcc1@simulator.amazonses.com", "Blind Copy 2 <success+bcc2@simulator.amazonses.com>"],
@@ -119,7 +120,7 @@ class AmazonSESBackendIntegrationTests(AnymailTestMixin, SimpleTestCase):
# })
message = AnymailMessage(
template_id='TestTemplate',
from_email='"Test From" <test@test-ses.anymail.info>',
from_email=formataddr(("Test From", self.from_email)),
to=["First Recipient <success+to1@simulator.amazonses.com>",
"success+to2@simulator.amazonses.com"],
merge_data={

View File

@@ -2,6 +2,7 @@ import logging
import os
import unittest
from datetime import datetime, timedelta
from email.utils import formataddr
from time import sleep
import requests
@@ -36,8 +37,9 @@ class MailgunBackendIntegrationTests(AnymailTestMixin, SimpleTestCase):
def setUp(self):
super().setUp()
self.from_email = 'from@%s' % ANYMAIL_TEST_MAILGUN_DOMAIN
self.message = AnymailMessage('Anymail Mailgun integration test', 'Text content',
'from@example.com', ['test+to1@anymail.info'])
self.from_email, ['test+to1@anymail.dev'])
self.message.attach_alternative('<p>HTML content</p>', "text/html")
def fetch_mailgun_events(self, message_id, event=None,
@@ -86,8 +88,8 @@ class MailgunBackendIntegrationTests(AnymailTestMixin, SimpleTestCase):
self.assertEqual(sent_count, 1)
anymail_status = self.message.anymail_status
sent_status = anymail_status.recipients['test+to1@anymail.info'].status
message_id = anymail_status.recipients['test+to1@anymail.info'].message_id
sent_status = anymail_status.recipients['test+to1@anymail.dev'].status
message_id = anymail_status.recipients['test+to1@anymail.dev'].message_id
self.assertEqual(sent_status, 'queued') # Mailgun always queues
self.assertGreater(len(message_id), 0) # don't know what it'll be, but it should exist
@@ -98,13 +100,14 @@ class MailgunBackendIntegrationTests(AnymailTestMixin, SimpleTestCase):
def test_all_options(self):
send_at = datetime.now().replace(microsecond=0) + timedelta(minutes=2)
send_at_timestamp = send_at.timestamp()
from_email = formataddr(("Test From, with comma", self.from_email))
message = AnymailMessage(
subject="Anymail Mailgun all-options integration test",
body="This is the text body",
from_email="Test From <from@example.com>, also-from@example.com",
to=["test+to1@anymail.info", "Recipient 2 <test+to2@anymail.info>"],
cc=["test+cc1@anymail.info", "Copy 2 <test+cc2@anymail.info>"],
bcc=["test+bcc1@anymail.info", "Blind Copy 2 <test+bcc2@anymail.info>"],
from_email=from_email,
to=["test+to1@anymail.dev", "Recipient 2 <test+to2@anymail.dev>"],
cc=["test+cc1@anymail.dev", "Copy 2 <test+cc2@anymail.dev>"],
bcc=["test+bcc1@anymail.dev", "Blind Copy 2 <test+bcc2@anymail.dev>"],
reply_to=["reply1@example.com", "Reply 2 <reply2@example.com>"],
headers={"X-Anymail-Test": "value"},
@@ -136,14 +139,14 @@ class MailgunBackendIntegrationTests(AnymailTestMixin, SimpleTestCase):
{"meta1": "simple string", "meta2": "2"}) # all metadata values become strings
self.assertEqual(event["message"]["scheduled-for"], send_at_timestamp)
self.assertIn(event["recipient"], ['test+to1@anymail.info', 'test+to2@anymail.info',
'test+cc1@anymail.info', 'test+cc2@anymail.info',
'test+bcc1@anymail.info', 'test+bcc2@anymail.info'])
self.assertIn(event["recipient"], ['test+to1@anymail.dev', 'test+to2@anymail.dev',
'test+cc1@anymail.dev', 'test+cc2@anymail.dev',
'test+bcc1@anymail.dev', 'test+bcc2@anymail.dev'])
headers = event["message"]["headers"]
self.assertEqual(headers["from"], "Test From <from@example.com>, also-from@example.com")
self.assertEqual(headers["from"], from_email)
self.assertEqual(headers["to"],
"test+to1@anymail.info, Recipient 2 <test+to2@anymail.info>")
"test+to1@anymail.dev, Recipient 2 <test+to2@anymail.dev>")
self.assertEqual(headers["subject"], "Anymail Mailgun all-options integration test")
attachments = event["message"]["attachments"]
@@ -167,12 +170,12 @@ class MailgunBackendIntegrationTests(AnymailTestMixin, SimpleTestCase):
message = AnymailMessage(
template_id='test-template', # name of a real template named in Anymail's Mailgun test account
subject='Your order %recipient.order%', # Mailgun templates don't define subject
from_email='Test From <from@example.com>', # Mailgun templates don't define sender
to=["test+to1@anymail.info"],
from_email=formataddr(('Test From>', self.from_email)), # Mailgun templates don't define sender
to=["test+to1@anymail.dev"],
# metadata and merge_data must not have any conflicting keys when using template_id
metadata={"meta1": "simple string", "meta2": 2},
merge_data={
'test+to1@anymail.info': {
'test+to1@anymail.dev': {
'name': "Test Recipient",
}
},
@@ -182,7 +185,7 @@ class MailgunBackendIntegrationTests(AnymailTestMixin, SimpleTestCase):
)
message.send()
recipient_status = message.anymail_status.recipients
self.assertEqual(recipient_status['test+to1@anymail.info'].status, 'queued')
self.assertEqual(recipient_status['test+to1@anymail.dev'].status, 'queued')
# As of Anymail 0.10, this test is no longer possible, because
# Anymail now raises AnymailInvalidAddress without even calling Mailgun

View File

@@ -1,5 +1,6 @@
import os
import unittest
from email.utils import formataddr
from django.test import SimpleTestCase, override_settings, tag
@@ -10,11 +11,13 @@ from .utils import AnymailTestMixin, sample_image_path
ANYMAIL_TEST_MAILJET_API_KEY = os.getenv('ANYMAIL_TEST_MAILJET_API_KEY')
ANYMAIL_TEST_MAILJET_SECRET_KEY = os.getenv('ANYMAIL_TEST_MAILJET_SECRET_KEY')
ANYMAIL_TEST_MAILJET_DOMAIN = os.getenv('ANYMAIL_TEST_MAILJET_DOMAIN')
@tag('mailjet', 'live')
@unittest.skipUnless(ANYMAIL_TEST_MAILJET_API_KEY and ANYMAIL_TEST_MAILJET_SECRET_KEY,
"Set ANYMAIL_TEST_MAILJET_API_KEY and ANYMAIL_TEST_MAILJET_SECRET_KEY "
@unittest.skipUnless(
ANYMAIL_TEST_MAILJET_API_KEY and ANYMAIL_TEST_MAILJET_SECRET_KEY and ANYMAIL_TEST_MAILJET_DOMAIN,
"Set ANYMAIL_TEST_MAILJET_API_KEY and ANYMAIL_TEST_MAILJET_SECRET_KEY and ANYMAIL_TEST_MAILJET_DOMAIN "
"environment variables to run Mailjet integration tests")
@override_settings(ANYMAIL={"MAILJET_API_KEY": ANYMAIL_TEST_MAILJET_API_KEY,
"MAILJET_SECRET_KEY": ANYMAIL_TEST_MAILJET_SECRET_KEY,
@@ -26,20 +29,18 @@ class MailjetBackendIntegrationTests(AnymailTestMixin, SimpleTestCase):
These tests run against the **live** Mailjet API, using the
environment variables `ANYMAIL_TEST_MAILJET_API_KEY` and `ANYMAIL_TEST_MAILJET_SECRET_KEY`
as the API key and API secret key, respectively.
If those variables are not set, these tests won't run.
as the API key and API secret key, respectively, and `ANYMAIL_TEST_MAILJET_DOMAIN` as
a validated Mailjet sending domain. If those variables are not set, these tests won't run.
These tests enable Mailjet's SandboxMode to avoid sending any email;
remove the esp_extra setting above if you are trying to actually send test messages.
Mailjet also doesn't support unverified senders (so no from@example.com).
We've set up @test-mj.anymail.info as a validated sending domain for these tests.
"""
def setUp(self):
super().setUp()
self.from_email = 'test@%s' % ANYMAIL_TEST_MAILJET_DOMAIN
self.message = AnymailMessage('Anymail Mailjet integration test', 'Text content',
'test@test-mj.anymail.info', ['test+to1@anymail.info'])
self.from_email, ['test+to1@anymail.dev'])
self.message.attach_alternative('<p>HTML content</p>', "text/html")
def test_simple_send(self):
@@ -48,8 +49,8 @@ class MailjetBackendIntegrationTests(AnymailTestMixin, SimpleTestCase):
self.assertEqual(sent_count, 1)
anymail_status = self.message.anymail_status
sent_status = anymail_status.recipients['test+to1@anymail.info'].status
message_id = anymail_status.recipients['test+to1@anymail.info'].message_id
sent_status = anymail_status.recipients['test+to1@anymail.dev'].status
message_id = anymail_status.recipients['test+to1@anymail.dev'].message_id
self.assertEqual(sent_status, 'sent')
self.assertRegex(message_id, r'.+')
@@ -60,10 +61,10 @@ class MailjetBackendIntegrationTests(AnymailTestMixin, SimpleTestCase):
message = AnymailMessage(
subject="Anymail Mailjet all-options integration test",
body="This is the text body",
from_email='"Test Sender, Inc." <test@test-mj.anymail.info>',
to=['test+to1@anymail.info', '"Recipient, 2nd" <test+to2@anymail.info>'],
cc=['test+cc1@anymail.info', 'Copy 2 <test+cc1@anymail.info>'],
bcc=['test+bcc1@anymail.info', 'Blind Copy 2 <test+bcc2@anymail.info>'],
from_email=formataddr(("Test Sender, Inc.", self.from_email)),
to=['test+to1@anymail.dev', '"Recipient, 2nd" <test+to2@anymail.dev>'],
cc=['test+cc1@anymail.dev', 'Copy 2 <test+cc1@anymail.dev>'],
bcc=['test+bcc1@anymail.dev', 'Blind Copy 2 <test+bcc2@anymail.dev>'],
reply_to=['"Reply, To" <reply2@example.com>'], # Mailjet only supports single reply_to
headers={"X-Anymail-Test": "value"},
@@ -88,11 +89,11 @@ class MailjetBackendIntegrationTests(AnymailTestMixin, SimpleTestCase):
subject="Anymail Mailjet merge_data test", # Mailjet doesn't support merge fields in the subject
body="This body includes merge data: [[var:value]]\n"
"And global merge data: [[var:global]]",
from_email="Test From <test@test-mj.anymail.info>",
to=["test+to1@anymail.info", "Recipient 2 <test+to2@anymail.info>"],
from_email=formataddr(("Test From", self.from_email)),
to=["test+to1@anymail.dev", "Recipient 2 <test+to2@anymail.dev>"],
merge_data={
'test+to1@anymail.info': {'value': 'one'},
'test+to2@anymail.info': {'value': 'two'},
'test+to1@anymail.dev': {'value': 'one'},
'test+to2@anymail.dev': {'value': 'two'},
},
merge_global_data={
'global': 'global_value'
@@ -100,15 +101,15 @@ class MailjetBackendIntegrationTests(AnymailTestMixin, SimpleTestCase):
)
message.send()
recipient_status = message.anymail_status.recipients
self.assertEqual(recipient_status['test+to1@anymail.info'].status, 'sent')
self.assertEqual(recipient_status['test+to2@anymail.info'].status, 'sent')
self.assertEqual(recipient_status['test+to1@anymail.dev'].status, 'sent')
self.assertEqual(recipient_status['test+to2@anymail.dev'].status, 'sent')
def test_stored_template(self):
message = AnymailMessage(
template_id='176375', # ID of the real template named 'test-template' in our Mailjet test account
to=["test+to1@anymail.info"],
to=["test+to1@anymail.dev"],
merge_data={
'test+to1@anymail.info': {
'test+to1@anymail.dev': {
'name': "Test Recipient",
}
},
@@ -119,7 +120,7 @@ class MailjetBackendIntegrationTests(AnymailTestMixin, SimpleTestCase):
message.from_email = None # use the template's sender email/name
message.send()
recipient_status = message.anymail_status.recipients
self.assertEqual(recipient_status['test+to1@anymail.info'].status, 'sent')
self.assertEqual(recipient_status['test+to1@anymail.dev'].status, 'sent')
@override_settings(ANYMAIL={"MAILJET_API_KEY": "Hey, that's not an API key!",
"MAILJET_SECRET_KEY": "and this isn't the secret for it"})

View File

@@ -1,5 +1,6 @@
import os
import unittest
from email.utils import formataddr
from django.core import mail
from django.test import SimpleTestCase, override_settings, tag
@@ -10,11 +11,13 @@ from anymail.message import AnymailMessage
from .utils import AnymailTestMixin, sample_image_path
ANYMAIL_TEST_MANDRILL_API_KEY = os.getenv('ANYMAIL_TEST_MANDRILL_API_KEY')
ANYMAIL_TEST_MANDRILL_DOMAIN = os.getenv('ANYMAIL_TEST_MANDRILL_DOMAIN')
@tag('mandrill', 'live')
@unittest.skipUnless(ANYMAIL_TEST_MANDRILL_API_KEY,
"Set ANYMAIL_TEST_MANDRILL_API_KEY environment variable to run integration tests")
@unittest.skipUnless(ANYMAIL_TEST_MANDRILL_API_KEY and ANYMAIL_TEST_MANDRILL_DOMAIN,
"Set ANYMAIL_TEST_MANDRILL_API_KEY and ANYMAIL_TEST_MANDRILL_DOMAIN "
"environment variables to run integration tests")
@override_settings(MANDRILL_API_KEY=ANYMAIL_TEST_MANDRILL_API_KEY,
EMAIL_BACKEND="anymail.backends.mandrill.EmailBackend")
class MandrillBackendIntegrationTests(AnymailTestMixin, SimpleTestCase):
@@ -26,13 +29,13 @@ class MandrillBackendIntegrationTests(AnymailTestMixin, SimpleTestCase):
See https://mandrill.zendesk.com/hc/en-us/articles/205582447
for info on Mandrill test keys.
"""
def setUp(self):
super().setUp()
self.from_email = 'from@%s' % ANYMAIL_TEST_MANDRILL_DOMAIN
self.message = mail.EmailMultiAlternatives('Anymail Mandrill integration test', 'Text content',
'from@example.com', ['test+to1@anymail.info'])
self.from_email, ['test+to1@anymail.dev'])
self.message.attach_alternative('<p>HTML content</p>', "text/html")
def test_simple_send(self):
@@ -42,8 +45,8 @@ class MandrillBackendIntegrationTests(AnymailTestMixin, SimpleTestCase):
# noinspection PyUnresolvedReferences
anymail_status = self.message.anymail_status
sent_status = anymail_status.recipients['test+to1@anymail.info'].status
message_id = anymail_status.recipients['test+to1@anymail.info'].message_id
sent_status = anymail_status.recipients['test+to1@anymail.dev'].status
message_id = anymail_status.recipients['test+to1@anymail.dev'].message_id
self.assertIn(sent_status, ['sent', 'queued']) # successful send (could still bounce later)
self.assertGreater(len(message_id), 0) # don't know what it'll be, but it should exist
@@ -55,10 +58,10 @@ class MandrillBackendIntegrationTests(AnymailTestMixin, SimpleTestCase):
message = AnymailMessage(
subject="Anymail Mandrill all-options integration test",
body="This is the text body",
from_email="Test From <from@example.com>",
to=["test+to1@anymail.info", "Recipient 2 <test+to2@anymail.info>"],
cc=["test+cc1@anymail.info", "Copy 2 <test+cc2@anymail.info>"],
bcc=["test+bcc1@anymail.info", "Blind Copy 2 <test+bcc2@anymail.info>"],
from_email=formataddr(("Test From, with comma", self.from_email)),
to=["test+to1@anymail.dev", "Recipient 2 <test+to2@anymail.dev>"],
cc=["test+cc1@anymail.dev", "Copy 2 <test+cc2@anymail.dev>"],
bcc=["test+bcc1@anymail.dev", "Blind Copy 2 <test+bcc2@anymail.dev>"],
reply_to=["reply1@example.com", "Reply 2 <reply2@example.com>"],
headers={"X-Anymail-Test": "value"},

View File

@@ -1,5 +1,6 @@
import os
import unittest
from email.utils import formataddr
from django.test import SimpleTestCase, override_settings, tag
@@ -11,11 +12,13 @@ from .utils import AnymailTestMixin
ANYMAIL_TEST_POSTAL_API_KEY = os.getenv('ANYMAIL_TEST_POSTAL_API_KEY')
ANYMAIL_TEST_POSTAL_API_URL = os.getenv('ANYMAIL_TEST_POSTAL_API_URL')
ANYMAIL_TEST_POSTAL_DOMAIN = os.getenv('ANYMAIL_TEST_POSTAL_DOMAIN')
@tag('postal', 'live')
@unittest.skipUnless(ANYMAIL_TEST_POSTAL_API_KEY and ANYMAIL_TEST_POSTAL_API_URL,
"Set ANYMAIL_TEST_POSTAL_API_KEY and ANYMAIL_TEST_POSTAL_API_URL "
@unittest.skipUnless(
ANYMAIL_TEST_POSTAL_API_KEY and ANYMAIL_TEST_POSTAL_API_URL and ANYMAIL_TEST_POSTAL_DOMAIN,
"Set ANYMAIL_TEST_POSTAL_API_KEY and ANYMAIL_TEST_POSTAL_API_URL and ANYMAIL_TEST_POSTAL_DOMAIN "
"environment variables to run Postal integration tests")
@override_settings(ANYMAIL_POSTAL_API_KEY=ANYMAIL_TEST_POSTAL_API_KEY,
ANYMAIL_POSTAL_API_URL=ANYMAIL_TEST_POSTAL_API_URL,
@@ -31,8 +34,9 @@ class PostalBackendIntegrationTests(AnymailTestMixin, SimpleTestCase):
def setUp(self):
super().setUp()
self.from_email = 'from@%s' % ANYMAIL_TEST_POSTAL_DOMAIN
self.message = AnymailMessage('Anymail Postal integration test', 'Text content',
'from@example.com', ['test+to1@anymail.info'])
self.from_email, ['test+to1@anymail.dev'])
self.message.attach_alternative('<p>HTML content</p>', "text/html")
def test_simple_send(self):
@@ -41,8 +45,8 @@ class PostalBackendIntegrationTests(AnymailTestMixin, SimpleTestCase):
self.assertEqual(sent_count, 1)
anymail_status = self.message.anymail_status
sent_status = anymail_status.recipients['test+to1@anymail.info'].status
message_id = anymail_status.recipients['test+to1@anymail.info'].message_id
sent_status = anymail_status.recipients['test+to1@anymail.dev'].status
message_id = anymail_status.recipients['test+to1@anymail.dev'].message_id
self.assertEqual(sent_status, 'queued')
self.assertGreater(len(message_id), 0) # non-empty string
@@ -53,11 +57,11 @@ class PostalBackendIntegrationTests(AnymailTestMixin, SimpleTestCase):
message = AnymailMessage(
subject="Anymail Postal all-options integration test",
body="This is the text body",
from_email="Test From <from@example.com>",
envelope_sender="bounces@example.com",
to=["test+to1@anymail.info", "Recipient 2 <test+to2@anymail.info>"],
cc=["test+cc1@anymail.info", "Copy 2 <test+cc2@anymail.info>"],
bcc=["test+bcc1@anymail.info", "Blind Copy 2 <test+bcc2@anymail.info>"],
from_email=formataddr(("Test From, with comma", self.from_email)),
envelope_sender="bounces@%s" % ANYMAIL_TEST_POSTAL_DOMAIN,
to=["test+to1@anymail.dev", "Recipient 2 <test+to2@anymail.dev>"],
cc=["test+cc1@anymail.dev", "Copy 2 <test+cc2@anymail.dev>"],
bcc=["test+bcc1@anymail.dev", "Blind Copy 2 <test+bcc2@anymail.dev>"],
reply_to=["reply1@example.com"],
headers={"X-Anymail-Test": "value"},
tags=["tag 1"], # max one tag
@@ -67,11 +71,11 @@ class PostalBackendIntegrationTests(AnymailTestMixin, SimpleTestCase):
message.send()
self.assertEqual(message.anymail_status.status, {'queued'})
self.assertEqual(message.anymail_status.recipients['test+to1@anymail.info'].status, 'queued')
self.assertEqual(message.anymail_status.recipients['test+to2@anymail.info'].status, 'queued')
self.assertEqual(message.anymail_status.recipients['test+to1@anymail.dev'].status, 'queued')
self.assertEqual(message.anymail_status.recipients['test+to2@anymail.dev'].status, 'queued')
# distinct messages should have different message_ids:
self.assertNotEqual(message.anymail_status.recipients['test+to1@anymail.info'].message_id,
message.anymail_status.recipients['teset+to2@anymail.info'].message_id)
self.assertNotEqual(message.anymail_status.recipients['test+to1@anymail.dev'].message_id,
message.anymail_status.recipients['teset+to2@anymail.dev'].message_id)
def test_invalid_from(self):
self.message.from_email = 'webmaster@localhost' # Django's default From

View File

@@ -1,5 +1,6 @@
import os
import unittest
from email.utils import formataddr
from django.test import SimpleTestCase, override_settings, tag
@@ -13,9 +14,13 @@ from .utils import AnymailTestMixin, sample_image_path
# But to test template sends, a real Postmark server token and template id are needed:
ANYMAIL_TEST_POSTMARK_SERVER_TOKEN = os.getenv('ANYMAIL_TEST_POSTMARK_SERVER_TOKEN')
ANYMAIL_TEST_POSTMARK_TEMPLATE_ID = os.getenv('ANYMAIL_TEST_POSTMARK_TEMPLATE_ID')
ANYMAIL_TEST_POSTMARK_DOMAIN = os.getenv('ANYMAIL_TEST_POSTMARK_DOMAIN')
@tag('postmark', 'live')
@unittest.skipUnless(ANYMAIL_TEST_POSTMARK_DOMAIN,
"Set ANYMAIL_TEST_POSTMARK_DOMAIN environment variable "
"to run Postmark template integration tests")
@override_settings(ANYMAIL_POSTMARK_SERVER_TOKEN="POSTMARK_API_TEST",
EMAIL_BACKEND="anymail.backends.postmark.EmailBackend")
class PostmarkBackendIntegrationTests(AnymailTestMixin, SimpleTestCase):
@@ -27,8 +32,9 @@ class PostmarkBackendIntegrationTests(AnymailTestMixin, SimpleTestCase):
def setUp(self):
super().setUp()
self.from_email = 'from@%s' % ANYMAIL_TEST_POSTMARK_DOMAIN
self.message = AnymailMessage('Anymail Postmark integration test', 'Text content',
'from@example.com', ['test+to1@anymail.info'])
self.from_email, ['test+to1@anymail.dev'])
self.message.attach_alternative('<p>HTML content</p>', "text/html")
def test_simple_send(self):
@@ -37,8 +43,8 @@ class PostmarkBackendIntegrationTests(AnymailTestMixin, SimpleTestCase):
self.assertEqual(sent_count, 1)
anymail_status = self.message.anymail_status
sent_status = anymail_status.recipients['test+to1@anymail.info'].status
message_id = anymail_status.recipients['test+to1@anymail.info'].message_id
sent_status = anymail_status.recipients['test+to1@anymail.dev'].status
message_id = anymail_status.recipients['test+to1@anymail.dev'].message_id
self.assertEqual(sent_status, 'sent')
self.assertGreater(len(message_id), 0) # non-empty string
@@ -49,11 +55,10 @@ class PostmarkBackendIntegrationTests(AnymailTestMixin, SimpleTestCase):
message = AnymailMessage(
subject="Anymail Postmark all-options integration test",
body="This is the text body",
# Postmark accepts multiple from_email addresses, but truncates to the first on their end
from_email="Test From <from@example.com>, also-from@example.com",
to=["test+to1@anymail.info", "Recipient 2 <test+to2@anymail.info>"],
cc=["test+cc1@anymail.info", "Copy 2 <test+cc2@anymail.info>"],
bcc=["test+bcc1@anymail.info", "Blind Copy 2 <test+bcc2@anymail.info>"],
from_email=formataddr(("Test From, with comma", self.from_email)),
to=["test+to1@anymail.dev", "Recipient 2 <test+to2@anymail.dev>"],
cc=["test+cc1@anymail.dev", "Copy 2 <test+cc2@anymail.dev>"],
bcc=["test+bcc1@anymail.dev", "Blind Copy 2 <test+bcc2@anymail.dev>"],
reply_to=["reply1@example.com", "Reply 2 <reply2@example.com>"],
headers={"X-Anymail-Test": "value"},
@@ -74,11 +79,11 @@ class PostmarkBackendIntegrationTests(AnymailTestMixin, SimpleTestCase):
message.send()
self.assertEqual(message.anymail_status.status, {'sent'})
self.assertEqual(message.anymail_status.recipients['test+to1@anymail.info'].status, 'sent')
self.assertEqual(message.anymail_status.recipients['test+to2@anymail.info'].status, 'sent')
self.assertEqual(message.anymail_status.recipients['test+to1@anymail.dev'].status, 'sent')
self.assertEqual(message.anymail_status.recipients['test+to2@anymail.dev'].status, 'sent')
# distinct messages should have different message_ids:
self.assertNotEqual(message.anymail_status.recipients['test+to1@anymail.info'].message_id,
message.anymail_status.recipients['test+to2@anymail.info'].message_id)
self.assertNotEqual(message.anymail_status.recipients['test+to1@anymail.dev'].message_id,
message.anymail_status.recipients['test+to2@anymail.dev'].message_id)
def test_invalid_from(self):
self.message.from_email = 'webmaster@localhost' # Django's default From
@@ -88,18 +93,21 @@ class PostmarkBackendIntegrationTests(AnymailTestMixin, SimpleTestCase):
self.assertEqual(err.status_code, 422)
self.assertIn("Invalid 'From' address", str(err))
@unittest.skipUnless(ANYMAIL_TEST_POSTMARK_SERVER_TOKEN and ANYMAIL_TEST_POSTMARK_TEMPLATE_ID,
@unittest.skipUnless(
ANYMAIL_TEST_POSTMARK_SERVER_TOKEN
and ANYMAIL_TEST_POSTMARK_TEMPLATE_ID
and ANYMAIL_TEST_POSTMARK_DOMAIN,
"Set ANYMAIL_TEST_POSTMARK_SERVER_TOKEN and ANYMAIL_TEST_POSTMARK_TEMPLATE_ID "
"environment variables to run Postmark template integration tests")
"and ANYMAIL_TEST_POSTMARK_DOMAIN environment variables to run Postmark template integration tests")
@override_settings(ANYMAIL_POSTMARK_SERVER_TOKEN=ANYMAIL_TEST_POSTMARK_SERVER_TOKEN)
def test_template(self):
message = AnymailMessage(
from_email="from@test-pm.anymail.info",
to=["test+to1@anymail.info", "Second Recipient <test+to2@anymail.info>"],
from_email=self.from_email,
to=["test+to1@anymail.dev", "Second Recipient <test+to2@anymail.dev>"],
template_id=ANYMAIL_TEST_POSTMARK_TEMPLATE_ID,
merge_data={
"test+to1@anymail.info": {"name": "Recipient 1", "order_no": "12345"},
"test+to2@anymail.info": {"order_no": "6789"},
"test+to1@anymail.dev": {"name": "Recipient 1", "order_no": "12345"},
"test+to2@anymail.dev": {"order_no": "6789"},
},
merge_global_data={"name": "Valued Customer"},
)

View File

@@ -1,6 +1,7 @@
import os
import unittest
from datetime import datetime, timedelta
from email.utils import formataddr
from django.test import SimpleTestCase, override_settings, tag
@@ -11,12 +12,13 @@ from .utils import AnymailTestMixin, sample_image_path
ANYMAIL_TEST_SENDGRID_API_KEY = os.getenv('ANYMAIL_TEST_SENDGRID_API_KEY')
ANYMAIL_TEST_SENDGRID_TEMPLATE_ID = os.getenv('ANYMAIL_TEST_SENDGRID_TEMPLATE_ID')
ANYMAIL_TEST_SENDGRID_DOMAIN = os.getenv('ANYMAIL_TEST_SENDGRID_DOMAIN')
@tag('sendgrid', 'live')
@unittest.skipUnless(ANYMAIL_TEST_SENDGRID_API_KEY,
"Set ANYMAIL_TEST_SENDGRID_API_KEY environment variable "
"to run SendGrid integration tests")
@unittest.skipUnless(ANYMAIL_TEST_SENDGRID_API_KEY and ANYMAIL_TEST_SENDGRID_DOMAIN,
"Set ANYMAIL_TEST_SENDGRID_API_KEY and ANYMAIL_TEST_SENDGRID_DOMAIN "
"environment variables to run SendGrid integration tests")
@override_settings(ANYMAIL_SENDGRID_API_KEY=ANYMAIL_TEST_SENDGRID_API_KEY,
ANYMAIL_SENDGRID_SEND_DEFAULTS={"esp_extra": {
"mail_settings": {"sandbox_mode": {"enable": True}},
@@ -34,13 +36,13 @@ class SendGridBackendIntegrationTests(AnymailTestMixin, SimpleTestCase):
The tests also use SendGrid's "sink domain" @sink.sendgrid.net for recipient addresses.
https://support.sendgrid.com/hc/en-us/articles/201995663-Safely-Test-Your-Sending-Speed
"""
def setUp(self):
super().setUp()
self.from_email = 'from@%s' % ANYMAIL_TEST_SENDGRID_DOMAIN
self.message = AnymailMessage('Anymail SendGrid integration test', 'Text content',
'from@example.com', ['to@sink.sendgrid.net'])
self.from_email, ['to@sink.sendgrid.net'])
self.message.attach_alternative('<p>HTML content</p>', "text/html")
def test_simple_send(self):
@@ -62,7 +64,7 @@ class SendGridBackendIntegrationTests(AnymailTestMixin, SimpleTestCase):
message = AnymailMessage(
subject="Anymail all-options integration test",
body="This is the text body",
from_email='"Test From, with comma" <from@example.com>',
from_email=formataddr(("Test From, with comma", self.from_email)),
to=["to1@sink.sendgrid.net", '"Recipient 2, OK?" <to2@sink.sendgrid.net>'],
cc=["cc1@sink.sendgrid.net", "Copy 2 <cc2@sink.sendgrid.net>"],
bcc=["bcc1@sink.sendgrid.net", "Blind Copy 2 <bcc2@sink.sendgrid.net>"],
@@ -91,7 +93,7 @@ class SendGridBackendIntegrationTests(AnymailTestMixin, SimpleTestCase):
message = AnymailMessage(
subject="Anymail merge_data test: %field%",
body="This body includes merge data: %field%",
from_email="Test From <from@example.com>",
from_email=formataddr(("Test From", self.from_email)),
to=["to1@sink.sendgrid.net", "Recipient 2 <to2@sink.sendgrid.net>"],
reply_to=['"Merge data in reply name: %field%" <reply@example.com>'],
merge_data={
@@ -112,7 +114,7 @@ class SendGridBackendIntegrationTests(AnymailTestMixin, SimpleTestCase):
"to a template in your SendGrid account to test stored templates")
def test_stored_template(self):
message = AnymailMessage(
from_email="Test From <from@example.com>",
from_email=formataddr(("Test From", self.from_email)),
to=["to@sink.sendgrid.net"],
# Anymail's live test template has merge fields "name", "order_no", and "dept"...
template_id=ANYMAIL_TEST_SENDGRID_TEMPLATE_ID,

View File

@@ -1,5 +1,6 @@
import os
import unittest
from email.utils import formataddr
from django.test import SimpleTestCase, override_settings, tag
@@ -9,12 +10,13 @@ from anymail.message import AnymailMessage
from .utils import AnymailTestMixin
ANYMAIL_TEST_SENDINBLUE_API_KEY = os.getenv('ANYMAIL_TEST_SENDINBLUE_API_KEY')
ANYMAIL_TEST_SENDINBLUE_DOMAIN = os.getenv('ANYMAIL_TEST_SENDINBLUE_DOMAIN')
@tag('sendinblue', 'live')
@unittest.skipUnless(ANYMAIL_TEST_SENDINBLUE_API_KEY,
"Set ANYMAIL_TEST_SENDINBLUE_API_KEY environment variable "
"to run SendinBlue integration tests")
@unittest.skipUnless(ANYMAIL_TEST_SENDINBLUE_API_KEY and ANYMAIL_TEST_SENDINBLUE_DOMAIN,
"Set ANYMAIL_TEST_SENDINBLUE_API_KEY and ANYMAIL_TEST_SENDINBLUE_DOMAIN "
"environment variables to run SendinBlue integration tests")
@override_settings(ANYMAIL_SENDINBLUE_API_KEY=ANYMAIL_TEST_SENDINBLUE_API_KEY,
ANYMAIL_SENDINBLUE_SEND_DEFAULTS=dict(),
EMAIL_BACKEND="anymail.backends.sendinblue.EmailBackend")
@@ -23,7 +25,8 @@ class SendinBlueBackendIntegrationTests(AnymailTestMixin, SimpleTestCase):
SendinBlue doesn't have sandbox so these tests run
against the **live** SendinBlue API, using the
environment variable `ANYMAIL_TEST_SENDINBLUE_API_KEY` as the API key
environment variable `ANYMAIL_TEST_SENDINBLUE_API_KEY` as the API key,
and `ANYMAIL_TEST_SENDINBLUE_DOMAIN` to construct sender addresses.
If those variables are not set, these tests won't run.
https://developers.sendinblue.com/docs/faq#section-how-can-i-test-the-api-
@@ -32,9 +35,9 @@ class SendinBlueBackendIntegrationTests(AnymailTestMixin, SimpleTestCase):
def setUp(self):
super().setUp()
self.from_email = 'from@%s' % ANYMAIL_TEST_SENDINBLUE_DOMAIN
self.message = AnymailMessage('Anymail SendinBlue integration test', 'Text content',
'from@test-sb.anymail.info', ['test+to1@anymail.info'])
self.from_email, ['test+to1@anymail.dev'])
self.message.attach_alternative('<p>HTML content</p>', "text/html")
def test_simple_send(self):
@@ -43,8 +46,8 @@ class SendinBlueBackendIntegrationTests(AnymailTestMixin, SimpleTestCase):
self.assertEqual(sent_count, 1)
anymail_status = self.message.anymail_status
sent_status = anymail_status.recipients['test+to1@anymail.info'].status
message_id = anymail_status.recipients['test+to1@anymail.info'].message_id
sent_status = anymail_status.recipients['test+to1@anymail.dev'].status
message_id = anymail_status.recipients['test+to1@anymail.dev'].message_id
self.assertEqual(sent_status, 'queued') # SendinBlue always queues
self.assertRegex(message_id, r'\<.+@.+\>') # Message-ID can be ...@smtp-relay.mail.fr or .sendinblue.com
@@ -55,10 +58,10 @@ class SendinBlueBackendIntegrationTests(AnymailTestMixin, SimpleTestCase):
message = AnymailMessage(
subject="Anymail SendinBlue all-options integration test",
body="This is the text body",
from_email='"Test From, with comma" <from@test-sb.anymail.info>',
to=["test+to1@anymail.info", '"Recipient 2, OK?" <test+to2@anymail.info>'],
cc=["test+cc1@anymail.info", "Copy 2 <test+cc2@anymail.info>"],
bcc=["test+bcc1@anymail.info", "Blind Copy 2 <test+bcc2@anymail.info>"],
from_email=formataddr(("Test From, with comma", self.from_email)),
to=["test+to1@anymail.dev", '"Recipient 2, OK?" <test+to2@anymail.dev>'],
cc=["test+cc1@anymail.dev", "Copy 2 <test+cc2@anymail.dev>"],
bcc=["test+bcc1@anymail.dev", "Blind Copy 2 <test+bcc2@anymail.dev>"],
reply_to=['"Reply, with comma" <reply@example.com>'], # SendinBlue API v3 only supports single reply-to
headers={"X-Anymail-Test": "value", "X-Anymail-Count": 3},
@@ -77,9 +80,9 @@ class SendinBlueBackendIntegrationTests(AnymailTestMixin, SimpleTestCase):
def test_template(self):
message = AnymailMessage(
template_id=5, # There is a *new-style* template with this id in the Anymail test account
from_email='Sender <from@test-sb.anymail.info>', # Override template sender
to=["Recipient <test+to1@anymail.info>"], # No batch send (so max one recipient suggested)
reply_to=["Do not reply <reply@example.com>"],
from_email=formataddr(('Sender', self.from_email)), # Override template sender
to=["Recipient <test+to1@anymail.dev>"], # No batch send (so max one recipient suggested)
reply_to=["Do not reply <reply@example.dev>"],
tags=["using-template"],
headers={"X-Anymail-Test": "group: A, variation: C"},
merge_global_data={

View File

@@ -1,6 +1,7 @@
import os
import unittest
from datetime import datetime, timedelta
from email.utils import formataddr
from django.test import SimpleTestCase, override_settings, tag
@@ -10,12 +11,13 @@ from anymail.message import AnymailMessage
from .utils import AnymailTestMixin, sample_image_path
ANYMAIL_TEST_SPARKPOST_API_KEY = os.getenv('ANYMAIL_TEST_SPARKPOST_API_KEY')
ANYMAIL_TEST_SPARKPOST_DOMAIN = os.getenv('ANYMAIL_TEST_SPARKPOST_DOMAIN')
@tag('sparkpost', 'live')
@unittest.skipUnless(ANYMAIL_TEST_SPARKPOST_API_KEY,
"Set ANYMAIL_TEST_SPARKPOST_API_KEY environment variable "
"to run SparkPost integration tests")
@unittest.skipUnless(ANYMAIL_TEST_SPARKPOST_API_KEY and ANYMAIL_TEST_SPARKPOST_DOMAIN,
"Set ANYMAIL_TEST_SPARKPOST_API_KEY and ANYMAIL_TEST_SPARKPOST_DOMAIN "
"environment variables to run SparkPost integration tests")
@override_settings(ANYMAIL_SPARKPOST_API_KEY=ANYMAIL_TEST_SPARKPOST_API_KEY,
EMAIL_BACKEND="anymail.backends.sparkpost.EmailBackend")
class SparkPostBackendIntegrationTests(AnymailTestMixin, SimpleTestCase):
@@ -29,15 +31,13 @@ class SparkPostBackendIntegrationTests(AnymailTestMixin, SimpleTestCase):
you ask. To avoid stacking up a pile of undeliverable @example.com
emails, the tests use SparkPost's "sink domain" @*.sink.sparkpostmail.com.
https://www.sparkpost.com/docs/faq/using-sink-server/
SparkPost also doesn't support arbitrary senders (so no from@example.com).
We've set up @test-sp.anymail.info as a validated sending domain for these tests.
"""
def setUp(self):
super().setUp()
self.from_email = 'test@%s' % ANYMAIL_TEST_SPARKPOST_DOMAIN
self.message = AnymailMessage('Anymail SparkPost integration test', 'Text content',
'test@test-sp.anymail.info', ['to@test.sink.sparkpostmail.com'])
self.from_email, ['to@test.sink.sparkpostmail.com'])
self.message.attach_alternative('<p>HTML content</p>', "text/html")
def test_simple_send(self):
@@ -59,7 +59,7 @@ class SparkPostBackendIntegrationTests(AnymailTestMixin, SimpleTestCase):
message = AnymailMessage(
subject="Anymail all-options integration test",
body="This is the text body",
from_email="Test From <test@test-sp.anymail.info>",
from_email=formataddr(("Test From, with comma", self.from_email)),
to=["to1@test.sink.sparkpostmail.com", "Recipient 2 <to2@test.sink.sparkpostmail.com>"],
# Limit the live b/cc's to avoid running through our small monthly allowance:
# cc=["cc1@test.sink.sparkpostmail.com", "Copy 2 <cc2@test.sink.sparkpostmail.com>"],
@@ -90,7 +90,7 @@ class SparkPostBackendIntegrationTests(AnymailTestMixin, SimpleTestCase):
subject="Anymail merge_data test: {{ value }}",
body="This body includes merge data: {{ value }}\n"
"And global merge data: {{ global }}",
from_email="Test From <test@test-sp.anymail.info>",
from_email=formataddr(("Test From", self.from_email)),
to=["to1@test.sink.sparkpostmail.com", "Recipient 2 <to2@test.sink.sparkpostmail.com>"],
merge_data={
'to1@test.sink.sparkpostmail.com': {'value': 'one'},