mirror of
https://github.com/pacnpal/django-anymail.git
synced 2025-12-20 03:41:05 -05:00
SparkPost has a very small monthly send allowance (and no API test mode), so limit the number of recipients in live integration tests to avoid running through our whole quota. This means we aren't fully testing cc and bcc handling like we're able to for other ESPs.
134 lines
6.1 KiB
Python
134 lines
6.1 KiB
Python
import os
|
|
import unittest
|
|
from datetime import datetime, timedelta
|
|
|
|
from django.test import SimpleTestCase, override_settings, tag
|
|
|
|
from anymail.exceptions import AnymailAPIError
|
|
from anymail.message import AnymailMessage
|
|
|
|
from .utils import AnymailTestMixin, sample_image_path
|
|
|
|
SPARKPOST_TEST_API_KEY = os.getenv('SPARKPOST_TEST_API_KEY')
|
|
|
|
|
|
@tag('sparkpost', 'live')
|
|
@unittest.skipUnless(SPARKPOST_TEST_API_KEY,
|
|
"Set SPARKPOST_TEST_API_KEY environment variable "
|
|
"to run SparkPost integration tests")
|
|
@override_settings(ANYMAIL_SPARKPOST_API_KEY=SPARKPOST_TEST_API_KEY,
|
|
EMAIL_BACKEND="anymail.backends.sparkpost.EmailBackend")
|
|
class SparkPostBackendIntegrationTests(SimpleTestCase, AnymailTestMixin):
|
|
"""SparkPost API integration tests
|
|
|
|
These tests run against the **live** SparkPost API, using the
|
|
environment variable `SPARKPOST_TEST_API_KEY` as the API key
|
|
If that variable is not set, these tests won't run.
|
|
|
|
SparkPost doesn't offer a test mode -- it tries to send everything
|
|
you ask. To avoid stacking up a pile of undeliverable @example.com
|
|
emails, the tests use SparkPost's "sink domain" @*.sink.sparkpostmail.com.
|
|
https://support.sparkpost.com/customer/en/portal/articles/2361300-how-to-test-integrations
|
|
|
|
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(SparkPostBackendIntegrationTests, self).setUp()
|
|
self.message = AnymailMessage('Anymail SparkPost integration test', 'Text content',
|
|
'test@test-sp.anymail.info', ['to@test.sink.sparkpostmail.com'])
|
|
self.message.attach_alternative('<p>HTML content</p>', "text/html")
|
|
|
|
def test_simple_send(self):
|
|
# Example of getting the SparkPost send status and transmission id from the message
|
|
sent_count = self.message.send()
|
|
self.assertEqual(sent_count, 1)
|
|
|
|
anymail_status = self.message.anymail_status
|
|
sent_status = anymail_status.recipients['to@test.sink.sparkpostmail.com'].status
|
|
message_id = anymail_status.recipients['to@test.sink.sparkpostmail.com'].message_id
|
|
|
|
self.assertEqual(sent_status, 'queued') # SparkPost always queues
|
|
self.assertRegex(message_id, r'.+') # this is actually the transmission_id; should be non-blank
|
|
self.assertEqual(anymail_status.status, {sent_status}) # set of all recipient statuses
|
|
self.assertEqual(anymail_status.message_id, message_id)
|
|
|
|
def test_all_options(self):
|
|
send_at = datetime.now() + timedelta(minutes=2)
|
|
message = AnymailMessage(
|
|
subject="Anymail all-options integration test",
|
|
body="This is the text body",
|
|
from_email="Test From <test@test-sp.anymail.info>, also-from@test-sp.anymail.info",
|
|
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>"],
|
|
# bcc=["bcc1@test.sink.sparkpostmail.com", "Blind Copy 2 <bcc2@test.sink.sparkpostmail.com>"],
|
|
cc=["Copy To <cc@test.sink.sparkpostmail.com>"],
|
|
reply_to=["reply1@example.com", "Reply 2 <reply2@example.com>"],
|
|
headers={"X-Anymail-Test": "value"},
|
|
|
|
metadata={"meta1": "simple string", "meta2": 2},
|
|
send_at=send_at,
|
|
tags=["tag 1"], # SparkPost only supports single tags
|
|
track_clicks=True,
|
|
track_opens=True,
|
|
)
|
|
message.attach("attachment1.txt", "Here is some\ntext for you", "text/plain")
|
|
message.attach("attachment2.csv", "ID,Name\n1,Amy Lina", "text/csv")
|
|
cid = message.attach_inline_image_file(sample_image_path())
|
|
message.attach_alternative(
|
|
"<p><b>HTML:</b> with <a href='http://example.com'>link</a>"
|
|
"and image: <img src='cid:%s'></div>" % cid,
|
|
"text/html")
|
|
|
|
message.send()
|
|
self.assertEqual(message.anymail_status.status, {'queued'}) # SparkPost always queues
|
|
|
|
def test_merge_data(self):
|
|
message = AnymailMessage(
|
|
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>",
|
|
to=["to1@test.sink.sparkpostmail.com", "Recipient 2 <to2@test.sink.sparkpostmail.com>"],
|
|
merge_data={
|
|
'to1@test.sink.sparkpostmail.com': {'value': 'one'},
|
|
'to2@test.sink.sparkpostmail.com': {'value': 'two'},
|
|
},
|
|
merge_global_data={
|
|
'global': 'global_value'
|
|
},
|
|
)
|
|
message.send()
|
|
recipient_status = message.anymail_status.recipients
|
|
self.assertEqual(recipient_status['to1@test.sink.sparkpostmail.com'].status, 'queued')
|
|
self.assertEqual(recipient_status['to2@test.sink.sparkpostmail.com'].status, 'queued')
|
|
|
|
def test_stored_template(self):
|
|
message = AnymailMessage(
|
|
template_id='test-template', # a real template in our SparkPost test account
|
|
to=["to1@test.sink.sparkpostmail.com"],
|
|
merge_data={
|
|
'to1@test.sink.sparkpostmail.com': {
|
|
'name': "Test Recipient",
|
|
}
|
|
},
|
|
merge_global_data={
|
|
'order': '12345',
|
|
},
|
|
)
|
|
message.send()
|
|
recipient_status = message.anymail_status.recipients
|
|
self.assertEqual(recipient_status['to1@test.sink.sparkpostmail.com'].status, 'queued')
|
|
|
|
@override_settings(ANYMAIL_SPARKPOST_API_KEY="Hey, that's not an API key!")
|
|
def test_invalid_api_key(self):
|
|
with self.assertRaises(AnymailAPIError) as cm:
|
|
self.message.send()
|
|
err = cm.exception
|
|
self.assertEqual(err.status_code, 403)
|
|
# Make sure the exception message includes SparkPost's response:
|
|
self.assertIn("Forbidden", str(err))
|