import json from textwrap import dedent import six from django.test import tag from mock import ANY from anymail.inbound import AnymailInboundMessage from anymail.signals import AnymailInboundEvent from anymail.webhooks.sendgrid import SendGridInboundWebhookView from .utils import sample_image_content, sample_email_content from .webhook_cases import WebhookTestCase @tag('sendgrid') class SendgridInboundTestCase(WebhookTestCase): def test_inbound_basics(self): raw_event = { 'headers': dedent("""\ Received: from mail.example.org by mx987654321.sendgrid.net ... Received: by mail.example.org for ... DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=example.org; ... MIME-Version: 1.0 Received: by 10.10.1.71 with HTTP; Wed, 11 Oct 2017 18:31:04 -0700 (PDT) From: "Displayed From" Date: Wed, 11 Oct 2017 18:31:04 -0700 Message-ID: Subject: Test subject To: "Test Inbound" , other@example.com Cc: cc@example.com Content-Type: multipart/mixed; boundary="94eb2c115edcf35387055b61f849" """), 'from': 'Displayed From ', 'to': 'Test Inbound , other@example.com', 'subject': "Test subject", 'text': "Test body plain", 'html': "
Test body html
", 'attachments': "0", 'charsets': '{"to":"UTF-8","html":"UTF-8","subject":"UTF-8","from":"UTF-8","text":"UTF-8"}', 'envelope': '{"to":["test@inbound.example.com"],"from":"envelope-from@example.org"}', 'sender_ip': "10.10.1.71", 'dkim': "{@example.org : pass}", # yep, SendGrid uses not-exactly-json for this field 'SPF': "pass", 'spam_score': "1.7", 'spam_report': 'Spam detection software, running on the system "mx987654321.sendgrid.net", ' 'has identified this incoming email as possible spam...', } response = self.client.post('/anymail/sendgrid/inbound/', data=raw_event) self.assertEqual(response.status_code, 200) kwargs = self.assert_handler_called_once_with(self.inbound_handler, sender=SendGridInboundWebhookView, event=ANY, esp_name='SendGrid') # AnymailInboundEvent event = kwargs['event'] self.assertIsInstance(event, AnymailInboundEvent) self.assertEqual(event.event_type, 'inbound') self.assertIsNone(event.timestamp) self.assertIsNone(event.event_id) self.assertIsInstance(event.message, AnymailInboundMessage) self.assertEqual(event.esp_event.POST.dict(), raw_event) # esp_event is a Django HttpRequest # AnymailInboundMessage - convenience properties message = event.message self.assertEqual(message.from_email.display_name, 'Displayed From') self.assertEqual(message.from_email.addr_spec, 'from+test@example.org') self.assertEqual([str(e) for e in message.to], ['Test Inbound ', 'other@example.com']) self.assertEqual([str(e) for e in message.cc], ['cc@example.com']) self.assertEqual(message.subject, 'Test subject') self.assertEqual(message.date.isoformat(" "), "2017-10-11 18:31:04-07:00") self.assertEqual(message.text, 'Test body plain') self.assertEqual(message.html, '
Test body html
') self.assertEqual(message.envelope_sender, 'envelope-from@example.org') self.assertEqual(message.envelope_recipient, 'test@inbound.example.com') self.assertIsNone(message.stripped_text) self.assertIsNone(message.stripped_html) self.assertIsNone(message.spam_detected) # SendGrid doesn't give a simple yes/no; check the score yourself self.assertEqual(message.spam_score, 1.7) # AnymailInboundMessage - other headers self.assertEqual(message['Message-ID'], "") self.assertEqual(message.get_all('Received'), [ "from mail.example.org by mx987654321.sendgrid.net ...", "by mail.example.org for ...", "by 10.10.1.71 with HTTP; Wed, 11 Oct 2017 18:31:04 -0700 (PDT)", ]) def test_attachments(self): att1 = six.BytesIO('test attachment'.encode('utf-8')) att1.name = 'test.txt' image_content = sample_image_content() att2 = six.BytesIO(image_content) att2.name = 'image.png' email_content = sample_email_content() att3 = six.BytesIO(email_content) att3.content_type = 'message/rfc822; charset="us-ascii"' raw_event = { 'headers': '', 'attachments': '3', 'attachment-info': json.dumps({ "attachment3": {"filename": "", "name": "", "charset": "US-ASCII", "type": "message/rfc822"}, "attachment2": {"filename": "image.png", "name": "image.png", "type": "image/png", "content-id": "abc123"}, "attachment1": {"filename": "test.txt", "name": "test.txt", "type": "text/plain"}, }), 'content-ids': '{"abc123": "attachment2"}', 'attachment1': att1, 'attachment2': att2, # inline 'attachment3': att3, } response = self.client.post('/anymail/sendgrid/inbound/', data=raw_event) self.assertEqual(response.status_code, 200) kwargs = self.assert_handler_called_once_with(self.inbound_handler, sender=SendGridInboundWebhookView, event=ANY, esp_name='SendGrid') event = kwargs['event'] message = event.message attachments = message.attachments # AnymailInboundMessage convenience accessor self.assertEqual(len(attachments), 2) self.assertEqual(attachments[0].get_filename(), 'test.txt') self.assertEqual(attachments[0].get_content_type(), 'text/plain') self.assertEqual(attachments[0].get_content_text(), u'test attachment') self.assertEqual(attachments[1].get_content_type(), 'message/rfc822') self.assertEqualIgnoringHeaderFolding(attachments[1].get_content_bytes(), email_content) inlines = message.inline_attachments self.assertEqual(len(inlines), 1) inline = inlines['abc123'] self.assertEqual(inline.get_filename(), 'image.png') self.assertEqual(inline.get_content_type(), 'image/png') self.assertEqual(inline.get_content_bytes(), image_content) def test_inbound_mime(self): # SendGrid has an option to send the full, raw MIME message raw_event = { 'email': dedent("""\ From: A tester Date: Thu, 12 Oct 2017 18:03:30 -0700 Message-ID: Subject: Raw MIME test To: test@inbound.example.com MIME-Version: 1.0 Content-Type: multipart/alternative; boundary="94eb2c05e174adb140055b6339c5" --94eb2c05e174adb140055b6339c5 Content-Type: text/plain; charset="UTF-8" Content-Transfer-Encoding: quoted-printable It's a body=E2=80=A6 --94eb2c05e174adb140055b6339c5 Content-Type: text/html; charset="UTF-8" Content-Transfer-Encoding: quoted-printable
It's a body=E2=80=A6
--94eb2c05e174adb140055b6339c5-- """), 'from': 'A tester ', 'to': 'test@inbound.example.com', 'subject': "Raw MIME test", 'charsets': '{"to":"UTF-8","subject":"UTF-8","from":"UTF-8"}', 'envelope': '{"to":["test@inbound.example.com"],"from":"envelope-from@example.org"}', 'sender_ip': "10.10.1.71", 'dkim': "{@example.org : pass}", # yep, SendGrid uses not-exactly-json for this field 'SPF': "pass", 'spam_score': "1.7", 'spam_report': 'Spam detection software, running on the system "mx987654321.sendgrid.net", ' 'has identified this incoming email as possible spam...', } response = self.client.post('/anymail/sendgrid/inbound/', data=raw_event) self.assertEqual(response.status_code, 200) kwargs = self.assert_handler_called_once_with(self.inbound_handler, sender=SendGridInboundWebhookView, event=ANY, esp_name='SendGrid') event = kwargs['event'] message = event.message self.assertEqual(message.envelope_sender, 'envelope-from@example.org') self.assertEqual(message.envelope_recipient, 'test@inbound.example.com') self.assertEqual(message.subject, 'Raw MIME test') self.assertEqual(message.text, u"It's a body\N{HORIZONTAL ELLIPSIS}\n") self.assertEqual(message.html, u"""
It's a body\N{HORIZONTAL ELLIPSIS}
\n""")