import os import unittest from datetime import datetime, timedelta from django.test import SimpleTestCase from django.test.utils import override_settings from anymail.exceptions import AnymailAPIError from anymail.message import AnymailMessage from .utils import AnymailTestMixin, sample_image_path, RUN_LIVE_TESTS SENDGRID_TEST_API_KEY = os.getenv('SENDGRID_TEST_API_KEY') SENDGRID_TEST_TEMPLATE_ID = os.getenv('SENDGRID_TEST_TEMPLATE_ID') @unittest.skipUnless(RUN_LIVE_TESTS, "RUN_LIVE_TESTS disabled in this environment") @unittest.skipUnless(SENDGRID_TEST_API_KEY, "Set SENDGRID_TEST_API_KEY environment variable " "to run SendGrid integration tests") @override_settings(ANYMAIL_SENDGRID_API_KEY=SENDGRID_TEST_API_KEY, ANYMAIL_SENDGRID_SEND_DEFAULTS={"esp_extra": { "mail_settings": {"sandbox_mode": {"enable": True}}, }}, EMAIL_BACKEND="anymail.backends.sendgrid.EmailBackend") class SendGridBackendIntegrationTests(SimpleTestCase, AnymailTestMixin): """SendGrid v3 API integration tests These tests run against the **live** SendGrid API, using the environment variable `SENDGRID_TEST_API_KEY` as the API key If those variables are not set, these tests won't run. The SEND_DEFAULTS above force SendGrid's v3 sandbox mode, which avoids sending mail. (Sandbox sends also don't show in the activity feed, so disable that for live debugging.) 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(SendGridBackendIntegrationTests, self).setUp() self.message = AnymailMessage('Anymail SendGrid integration test', 'Text content', 'from@example.com', ['to@sink.sendgrid.net']) self.message.attach_alternative('

HTML content

', "text/html") def test_simple_send(self): # Example of getting the SendGrid send status and message 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@sink.sendgrid.net'].status message_id = anymail_status.recipients['to@sink.sendgrid.net'].message_id self.assertEqual(sent_status, 'queued') # SendGrid always queues self.assertUUIDIsValid(message_id) # Anymail generates a UUID tracking id 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().replace(microsecond=0) + timedelta(minutes=2) message = AnymailMessage( subject="Anymail all-options integration test", body="This is the text body", from_email='"Test From, with comma" ', to=["to1@sink.sendgrid.net", '"Recipient 2, OK?" '], cc=["cc1@sink.sendgrid.net", "Copy 2 "], bcc=["bcc1@sink.sendgrid.net", "Blind Copy 2 "], reply_to=['"Reply, with comma" '], # v3 only supports single reply-to headers={"X-Anymail-Test": "value", "X-Anymail-Count": 3}, metadata={"meta1": "simple string", "meta2": 2}, send_at=send_at, tags=["tag 1", "tag 2"], track_clicks=True, track_opens=True, # esp_extra={'asm': {'group_id': 1}}, # this breaks activity feed if you don't have an asm group ) 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( "

HTML: with link" "and image: " % cid, "text/html") message.send() self.assertEqual(message.anymail_status.status, {'queued'}) # SendGrid always queues def test_merge_data(self): message = AnymailMessage( subject="Anymail merge_data test: %field%", body="This body includes merge data: %field%", from_email="Test From ", to=["to1@sink.sendgrid.net", "Recipient 2 "], reply_to=['"Merge data in reply name: %field%" '], merge_data={ 'to1@sink.sendgrid.net': {'field': 'one'}, 'to2@sink.sendgrid.net': {'field': 'two'}, }, esp_extra={ 'merge_field_format': '%{}%', }, ) message.send() recipient_status = message.anymail_status.recipients self.assertEqual(recipient_status['to1@sink.sendgrid.net'].status, 'queued') self.assertEqual(recipient_status['to2@sink.sendgrid.net'].status, 'queued') @unittest.skipUnless(SENDGRID_TEST_TEMPLATE_ID, "Set the SENDGRID_TEST_TEMPLATE_ID environment variable " "to a template in your SendGrid account to test stored templates") def test_stored_template(self): message = AnymailMessage( from_email="Test From ", to=["to@sink.sendgrid.net"], template_id=SENDGRID_TEST_TEMPLATE_ID, # The test template in the Anymail Test account has a substitution "-field-": merge_global_data={ 'field': 'value from merge_global_data', }, esp_extra={ 'merge_field_format': '-{}-', }, ) message.send() self.assertEqual(message.anymail_status.status, {'queued'}) @override_settings(ANYMAIL_SENDGRID_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, 401) # Make sure the exception message includes SendGrid's response: self.assertIn("authorization grant is invalid", str(err))