mirror of
https://github.com/pacnpal/django-anymail.git
synced 2025-12-20 03:41:05 -05:00
Reformat code with automated tools
Apply standardized code style
This commit is contained in:
@@ -15,26 +15,30 @@ from .test_amazon_ses_webhooks import AmazonSESWebhookTestsMixin
|
||||
from .webhook_cases import WebhookTestCase
|
||||
|
||||
|
||||
@tag('amazon_ses')
|
||||
@tag("amazon_ses")
|
||||
class AmazonSESInboundTests(WebhookTestCase, AmazonSESWebhookTestsMixin):
|
||||
|
||||
def setUp(self):
|
||||
super().setUp()
|
||||
# Mock boto3.session.Session().client('s3').download_fileobj
|
||||
# (We could also use botocore.stub.Stubber, but mock works well with our test structure)
|
||||
self.patch_boto3_session = patch('anymail.webhooks.amazon_ses.boto3.session.Session', autospec=True)
|
||||
# Mock boto3.session.Session().client('s3').download_fileobj. (We could also
|
||||
# use botocore.stub.Stubber, but mock works well with our test structure.)
|
||||
self.patch_boto3_session = patch(
|
||||
"anymail.webhooks.amazon_ses.boto3.session.Session", autospec=True
|
||||
)
|
||||
self.mock_session = self.patch_boto3_session.start() # boto3.session.Session
|
||||
self.addCleanup(self.patch_boto3_session.stop)
|
||||
|
||||
def mock_download_fileobj(bucket, key, fileobj):
|
||||
fileobj.write(self.mock_s3_downloadables[bucket][key])
|
||||
|
||||
self.mock_s3_downloadables = {} # bucket: key: bytes
|
||||
self.mock_client = self.mock_session.return_value.client # boto3.session.Session().client
|
||||
self.mock_s3 = self.mock_client.return_value # boto3.session.Session().client('s3', ...)
|
||||
self.mock_s3_downloadables = {} #: bucket: key: bytes
|
||||
#: boto3.session.Session().client
|
||||
self.mock_client = self.mock_session.return_value.client
|
||||
#: boto3.session.Session().client('s3', ...)
|
||||
self.mock_s3 = self.mock_client.return_value
|
||||
self.mock_s3.download_fileobj.side_effect = mock_download_fileobj
|
||||
|
||||
TEST_MIME_MESSAGE = dedent("""\
|
||||
TEST_MIME_MESSAGE = dedent(
|
||||
"""\
|
||||
Return-Path: <bounce-handler@mail.example.org>
|
||||
Received: from mail.example.org by inbound-smtp.us-east-1.amazonaws.com...
|
||||
MIME-Version: 1.0
|
||||
@@ -59,7 +63,8 @@ class AmazonSESInboundTests(WebhookTestCase, AmazonSESWebhookTestsMixin):
|
||||
<div dir=3D"ltr">It's a body=E2=80=A6</div>
|
||||
|
||||
--94eb2c05e174adb140055b6339c5--
|
||||
""").replace("\n", "\r\n")
|
||||
"""
|
||||
).replace("\n", "\r\n")
|
||||
|
||||
def test_inbound_sns_utf8(self):
|
||||
raw_ses_event = {
|
||||
@@ -67,27 +72,50 @@ class AmazonSESInboundTests(WebhookTestCase, AmazonSESWebhookTestsMixin):
|
||||
"mail": {
|
||||
"timestamp": "2018-03-30T17:21:51.636Z",
|
||||
"source": "envelope-from@example.org",
|
||||
"messageId": "jili9m351il3gkburn7o2f0u6788stij94c8ld01", # assigned by Amazon SES
|
||||
# messageId is assigned by Amazon SES:
|
||||
"messageId": "jili9m351il3gkburn7o2f0u6788stij94c8ld01",
|
||||
"destination": ["inbound@example.com", "someone-else@example.org"],
|
||||
"headersTruncated": False,
|
||||
"headers": [
|
||||
# (omitting a few headers that Amazon SES adds on receipt)
|
||||
{"name": "Return-Path", "value": "<bounce-handler@mail.example.org>"},
|
||||
{"name": "Received", "value": "from mail.example.org by inbound-smtp.us-east-1.amazonaws.com..."},
|
||||
{
|
||||
"name": "Return-Path",
|
||||
"value": "<bounce-handler@mail.example.org>",
|
||||
},
|
||||
{
|
||||
"name": "Received",
|
||||
"value": "from mail.example.org by"
|
||||
" inbound-smtp.us-east-1.amazonaws.com...",
|
||||
},
|
||||
{"name": "MIME-Version", "value": "1.0"},
|
||||
{"name": "Received", "value": "by 10.1.1.1 with HTTP; Fri, 30 Mar 2018 10:21:49 -0700 (PDT)"},
|
||||
{
|
||||
"name": "Received",
|
||||
"value": "by 10.1.1.1 with HTTP;"
|
||||
" Fri, 30 Mar 2018 10:21:49 -0700 (PDT)",
|
||||
},
|
||||
{"name": "From", "value": '"Sender, Inc." <from@example.org>'},
|
||||
{"name": "Date", "value": "Fri, 30 Mar 2018 10:21:50 -0700"},
|
||||
{"name": "Message-ID", "value": "<CAEPk3RKsi@mail.example.org>"},
|
||||
{"name": "Subject", "value": "Test inbound message"},
|
||||
{"name": "To", "value": "Recipient <inbound@example.com>, someone-else@example.org"},
|
||||
{"name": "Content-Type", "value": 'multipart/alternative; boundary="94eb2c05e174adb140055b6339c5"'},
|
||||
{
|
||||
"name": "To",
|
||||
"value": "Recipient <inbound@example.com>,"
|
||||
" someone-else@example.org",
|
||||
},
|
||||
{
|
||||
"name": "Content-Type",
|
||||
"value": "multipart/alternative;"
|
||||
' boundary="94eb2c05e174adb140055b6339c5"',
|
||||
},
|
||||
],
|
||||
"commonHeaders": {
|
||||
"returnPath": "bounce-handler@mail.example.org",
|
||||
"from": ['"Sender, Inc." <from@example.org>'],
|
||||
"date": "Fri, 30 Mar 2018 10:21:50 -0700",
|
||||
"to": ["Recipient <inbound@example.com>", "someone-else@example.org"],
|
||||
"to": [
|
||||
"Recipient <inbound@example.com>",
|
||||
"someone-else@example.org",
|
||||
],
|
||||
"messageId": "<CAEPk3RKsi@mail.example.org>",
|
||||
"subject": "Test inbound message",
|
||||
},
|
||||
@@ -119,32 +147,46 @@ class AmazonSESInboundTests(WebhookTestCase, AmazonSESWebhookTestsMixin):
|
||||
"Timestamp": "2018-03-30T17:17:36.516Z",
|
||||
"SignatureVersion": "1",
|
||||
"Signature": "EXAMPLE_SIGNATURE==",
|
||||
"SigningCertURL": "https://sns.us-east-1.amazonaws.com/SimpleNotificationService-12345abcde.pem",
|
||||
"UnsubscribeURL": "https://sns.us-east-1.amazonaws.com/?Action=Unsubscribe&SubscriptionArn=arn...",
|
||||
"SigningCertURL": "https://sns.us-east-1.amazonaws.com"
|
||||
"/SimpleNotificationService-12345abcde.pem",
|
||||
"UnsubscribeURL": "https://sns.us-east-1.amazonaws.com"
|
||||
"/?Action=Unsubscribe&SubscriptionArn=arn...",
|
||||
}
|
||||
|
||||
response = self.post_from_sns('/anymail/amazon_ses/inbound/', raw_sns_message)
|
||||
response = self.post_from_sns("/anymail/amazon_ses/inbound/", raw_sns_message)
|
||||
self.assertEqual(response.status_code, 200)
|
||||
kwargs = self.assert_handler_called_once_with(self.inbound_handler, sender=AmazonSESInboundWebhookView,
|
||||
event=ANY, esp_name='Amazon SES')
|
||||
event = kwargs['event']
|
||||
kwargs = self.assert_handler_called_once_with(
|
||||
self.inbound_handler,
|
||||
sender=AmazonSESInboundWebhookView,
|
||||
event=ANY,
|
||||
esp_name="Amazon SES",
|
||||
)
|
||||
event = kwargs["event"]
|
||||
self.assertIsInstance(event, AnymailInboundEvent)
|
||||
self.assertEqual(event.event_type, 'inbound')
|
||||
self.assertEqual(event.timestamp, datetime(2018, 3, 30, 17, 21, 51, microsecond=636000, tzinfo=timezone.utc))
|
||||
self.assertEqual(event.event_type, "inbound")
|
||||
self.assertEqual(
|
||||
event.timestamp,
|
||||
datetime(2018, 3, 30, 17, 21, 51, microsecond=636000, tzinfo=timezone.utc),
|
||||
)
|
||||
self.assertEqual(event.event_id, "jili9m351il3gkburn7o2f0u6788stij94c8ld01")
|
||||
self.assertIsInstance(event.message, AnymailInboundMessage)
|
||||
self.assertEqual(event.esp_event, raw_ses_event)
|
||||
|
||||
message = event.message
|
||||
self.assertIsInstance(message, AnymailInboundMessage)
|
||||
self.assertEqual(message.envelope_sender, 'envelope-from@example.org')
|
||||
self.assertEqual(message.envelope_recipient, 'inbound@example.com')
|
||||
self.assertEqual(message.envelope_sender, "envelope-from@example.org")
|
||||
self.assertEqual(message.envelope_recipient, "inbound@example.com")
|
||||
self.assertEqual(str(message.from_email), '"Sender, Inc." <from@example.org>')
|
||||
self.assertEqual([str(to) for to in message.to],
|
||||
['Recipient <inbound@example.com>', 'someone-else@example.org'])
|
||||
self.assertEqual(message.subject, 'Test inbound message')
|
||||
self.assertEqual(
|
||||
[str(to) for to in message.to],
|
||||
["Recipient <inbound@example.com>", "someone-else@example.org"],
|
||||
)
|
||||
self.assertEqual(message.subject, "Test inbound message")
|
||||
self.assertEqual(message.text, "It's a body\N{HORIZONTAL ELLIPSIS}\r\n")
|
||||
self.assertEqual(message.html, """<div dir="ltr">It's a body\N{HORIZONTAL ELLIPSIS}</div>\r\n""")
|
||||
self.assertEqual(
|
||||
message.html,
|
||||
"""<div dir="ltr">It's a body\N{HORIZONTAL ELLIPSIS}</div>\r\n""",
|
||||
)
|
||||
self.assertIs(message.spam_detected, False)
|
||||
|
||||
def test_inbound_sns_base64(self):
|
||||
@@ -155,7 +197,8 @@ class AmazonSESInboundTests(WebhookTestCase, AmazonSESWebhookTestsMixin):
|
||||
"mail": {
|
||||
"source": "envelope-from@example.org",
|
||||
"timestamp": "2018-03-30T17:21:51.636Z",
|
||||
"messageId": "jili9m351il3gkburn7o2f0u6788stij94c8ld01", # assigned by Amazon SES
|
||||
# messageId is assigned by Amazon SES
|
||||
"messageId": "jili9m351il3gkburn7o2f0u6788stij94c8ld01",
|
||||
"destination": ["inbound@example.com", "someone-else@example.org"],
|
||||
},
|
||||
"receipt": {
|
||||
@@ -167,7 +210,9 @@ class AmazonSESInboundTests(WebhookTestCase, AmazonSESWebhookTestsMixin):
|
||||
},
|
||||
"spamVerdict": {"status": "FAIL"},
|
||||
},
|
||||
"content": b64encode(self.TEST_MIME_MESSAGE.encode('ascii')).decode('ascii'),
|
||||
"content": b64encode(self.TEST_MIME_MESSAGE.encode("ascii")).decode(
|
||||
"ascii"
|
||||
),
|
||||
}
|
||||
|
||||
raw_sns_message = {
|
||||
@@ -177,35 +222,49 @@ class AmazonSESInboundTests(WebhookTestCase, AmazonSESWebhookTestsMixin):
|
||||
"Message": json.dumps(raw_ses_event),
|
||||
}
|
||||
|
||||
response = self.post_from_sns('/anymail/amazon_ses/inbound/', raw_sns_message)
|
||||
response = self.post_from_sns("/anymail/amazon_ses/inbound/", raw_sns_message)
|
||||
self.assertEqual(response.status_code, 200)
|
||||
kwargs = self.assert_handler_called_once_with(self.inbound_handler, sender=AmazonSESInboundWebhookView,
|
||||
event=ANY, esp_name='Amazon SES')
|
||||
event = kwargs['event']
|
||||
kwargs = self.assert_handler_called_once_with(
|
||||
self.inbound_handler,
|
||||
sender=AmazonSESInboundWebhookView,
|
||||
event=ANY,
|
||||
esp_name="Amazon SES",
|
||||
)
|
||||
event = kwargs["event"]
|
||||
self.assertIsInstance(event, AnymailInboundEvent)
|
||||
self.assertEqual(event.event_type, 'inbound')
|
||||
self.assertEqual(event.timestamp, datetime(2018, 3, 30, 17, 21, 51, microsecond=636000, tzinfo=timezone.utc))
|
||||
self.assertEqual(event.event_type, "inbound")
|
||||
self.assertEqual(
|
||||
event.timestamp,
|
||||
datetime(2018, 3, 30, 17, 21, 51, microsecond=636000, tzinfo=timezone.utc),
|
||||
)
|
||||
self.assertEqual(event.event_id, "jili9m351il3gkburn7o2f0u6788stij94c8ld01")
|
||||
self.assertIsInstance(event.message, AnymailInboundMessage)
|
||||
self.assertEqual(event.esp_event, raw_ses_event)
|
||||
|
||||
message = event.message
|
||||
self.assertIsInstance(message, AnymailInboundMessage)
|
||||
self.assertEqual(message.envelope_sender, 'envelope-from@example.org')
|
||||
self.assertEqual(message.envelope_recipient, 'inbound@example.com')
|
||||
self.assertEqual(message.envelope_sender, "envelope-from@example.org")
|
||||
self.assertEqual(message.envelope_recipient, "inbound@example.com")
|
||||
self.assertEqual(str(message.from_email), '"Sender, Inc." <from@example.org>')
|
||||
self.assertEqual([str(to) for to in message.to],
|
||||
['Recipient <inbound@example.com>', 'someone-else@example.org'])
|
||||
self.assertEqual(message.subject, 'Test inbound message')
|
||||
self.assertEqual(
|
||||
[str(to) for to in message.to],
|
||||
["Recipient <inbound@example.com>", "someone-else@example.org"],
|
||||
)
|
||||
self.assertEqual(message.subject, "Test inbound message")
|
||||
self.assertEqual(message.text, "It's a body\N{HORIZONTAL ELLIPSIS}\r\n")
|
||||
self.assertEqual(message.html, """<div dir="ltr">It's a body\N{HORIZONTAL ELLIPSIS}</div>\r\n""")
|
||||
self.assertEqual(
|
||||
message.html,
|
||||
"""<div dir="ltr">It's a body\N{HORIZONTAL ELLIPSIS}</div>\r\n""",
|
||||
)
|
||||
self.assertIs(message.spam_detected, True)
|
||||
|
||||
def test_inbound_s3(self):
|
||||
"""Should handle 'S3' receipt action"""
|
||||
|
||||
self.mock_s3_downloadables["InboundEmailBucket-KeepPrivate"] = {
|
||||
"inbound/fqef5sop459utgdf4o9lqbsv7jeo73pejig34301": self.TEST_MIME_MESSAGE.encode('ascii')
|
||||
"inbound/fqef5sop459utgdf4o9lqbsv7jeo73pejig34301": (
|
||||
self.TEST_MIME_MESSAGE.encode("ascii")
|
||||
)
|
||||
}
|
||||
|
||||
raw_ses_event = {
|
||||
@@ -214,7 +273,8 @@ class AmazonSESInboundTests(WebhookTestCase, AmazonSESWebhookTestsMixin):
|
||||
"mail": {
|
||||
"source": "envelope-from@example.org",
|
||||
"timestamp": "2018-03-30T17:21:51.636Z",
|
||||
"messageId": "fqef5sop459utgdf4o9lqbsv7jeo73pejig34301", # assigned by Amazon SES
|
||||
# messageId is assigned by Amazon SES
|
||||
"messageId": "fqef5sop459utgdf4o9lqbsv7jeo73pejig34301",
|
||||
"destination": ["inbound@example.com", "someone-else@example.org"],
|
||||
},
|
||||
"receipt": {
|
||||
@@ -224,7 +284,7 @@ class AmazonSESInboundTests(WebhookTestCase, AmazonSESWebhookTestsMixin):
|
||||
"topicArn": "arn:aws:sns:us-east-1:111111111111:SES_Inbound",
|
||||
"bucketName": "InboundEmailBucket-KeepPrivate",
|
||||
"objectKeyPrefix": "inbound",
|
||||
"objectKey": "inbound/fqef5sop459utgdf4o9lqbsv7jeo73pejig34301"
|
||||
"objectKey": "inbound/fqef5sop459utgdf4o9lqbsv7jeo73pejig34301",
|
||||
},
|
||||
"spamVerdict": {"status": "GRAY"},
|
||||
},
|
||||
@@ -235,46 +295,69 @@ class AmazonSESInboundTests(WebhookTestCase, AmazonSESWebhookTestsMixin):
|
||||
"TopicArn": "arn:aws:sns:us-east-1:111111111111:SES_Inbound",
|
||||
"Message": json.dumps(raw_ses_event),
|
||||
}
|
||||
response = self.post_from_sns('/anymail/amazon_ses/inbound/', raw_sns_message)
|
||||
response = self.post_from_sns("/anymail/amazon_ses/inbound/", raw_sns_message)
|
||||
self.assertEqual(response.status_code, 200)
|
||||
|
||||
self.mock_client.assert_called_once_with('s3', config=ANY)
|
||||
self.mock_client.assert_called_once_with("s3", config=ANY)
|
||||
self.mock_s3.download_fileobj.assert_called_once_with(
|
||||
"InboundEmailBucket-KeepPrivate", "inbound/fqef5sop459utgdf4o9lqbsv7jeo73pejig34301", ANY)
|
||||
"InboundEmailBucket-KeepPrivate",
|
||||
"inbound/fqef5sop459utgdf4o9lqbsv7jeo73pejig34301",
|
||||
ANY,
|
||||
)
|
||||
|
||||
kwargs = self.assert_handler_called_once_with(self.inbound_handler, sender=AmazonSESInboundWebhookView,
|
||||
event=ANY, esp_name='Amazon SES')
|
||||
event = kwargs['event']
|
||||
kwargs = self.assert_handler_called_once_with(
|
||||
self.inbound_handler,
|
||||
sender=AmazonSESInboundWebhookView,
|
||||
event=ANY,
|
||||
esp_name="Amazon SES",
|
||||
)
|
||||
event = kwargs["event"]
|
||||
self.assertIsInstance(event, AnymailInboundEvent)
|
||||
self.assertEqual(event.event_type, 'inbound')
|
||||
self.assertEqual(event.timestamp, datetime(2018, 3, 30, 17, 21, 51, microsecond=636000, tzinfo=timezone.utc))
|
||||
self.assertEqual(event.event_type, "inbound")
|
||||
self.assertEqual(
|
||||
event.timestamp,
|
||||
datetime(2018, 3, 30, 17, 21, 51, microsecond=636000, tzinfo=timezone.utc),
|
||||
)
|
||||
self.assertEqual(event.event_id, "fqef5sop459utgdf4o9lqbsv7jeo73pejig34301")
|
||||
self.assertIsInstance(event.message, AnymailInboundMessage)
|
||||
self.assertEqual(event.esp_event, raw_ses_event)
|
||||
|
||||
message = event.message
|
||||
self.assertIsInstance(message, AnymailInboundMessage)
|
||||
self.assertEqual(message.envelope_sender, 'envelope-from@example.org')
|
||||
self.assertEqual(message.envelope_recipient, 'inbound@example.com')
|
||||
self.assertEqual(message.envelope_sender, "envelope-from@example.org")
|
||||
self.assertEqual(message.envelope_recipient, "inbound@example.com")
|
||||
self.assertEqual(str(message.from_email), '"Sender, Inc." <from@example.org>')
|
||||
self.assertEqual([str(to) for to in message.to],
|
||||
['Recipient <inbound@example.com>', 'someone-else@example.org'])
|
||||
self.assertEqual(message.subject, 'Test inbound message')
|
||||
self.assertEqual(
|
||||
[str(to) for to in message.to],
|
||||
["Recipient <inbound@example.com>", "someone-else@example.org"],
|
||||
)
|
||||
self.assertEqual(message.subject, "Test inbound message")
|
||||
self.assertEqual(message.text, "It's a body\N{HORIZONTAL ELLIPSIS}\n")
|
||||
self.assertEqual(message.html, """<div dir="ltr">It's a body\N{HORIZONTAL ELLIPSIS}</div>\n""")
|
||||
self.assertEqual(
|
||||
message.html,
|
||||
"""<div dir="ltr">It's a body\N{HORIZONTAL ELLIPSIS}</div>\n""",
|
||||
)
|
||||
self.assertIsNone(message.spam_detected)
|
||||
|
||||
def test_inbound_s3_failure_message(self):
|
||||
"""Issue a helpful error when S3 download fails"""
|
||||
# Boto's error: "An error occurred (403) when calling the HeadObject operation: Forbidden")
|
||||
# Boto's error:
|
||||
# "An error occurred (403) when calling the HeadObject operation: Forbidden"
|
||||
from botocore.exceptions import ClientError
|
||||
|
||||
self.mock_s3.download_fileobj.side_effect = ClientError(
|
||||
{'Error': {'Code': 403, 'Message': 'Forbidden'}}, operation_name='HeadObject')
|
||||
{"Error": {"Code": 403, "Message": "Forbidden"}},
|
||||
operation_name="HeadObject",
|
||||
)
|
||||
|
||||
raw_ses_event = {
|
||||
"notificationType": "Received",
|
||||
"receipt": {
|
||||
"action": {"type": "S3", "bucketName": "YourBucket", "objectKey": "inbound/the_object_key"}
|
||||
"action": {
|
||||
"type": "S3",
|
||||
"bucketName": "YourBucket",
|
||||
"objectKey": "inbound/the_object_key",
|
||||
}
|
||||
},
|
||||
}
|
||||
raw_sns_message = {
|
||||
@@ -285,12 +368,18 @@ class AmazonSESInboundTests(WebhookTestCase, AmazonSESWebhookTestsMixin):
|
||||
}
|
||||
with self.assertRaisesMessage(
|
||||
AnymailAPIError,
|
||||
"Anymail AmazonSESInboundWebhookView couldn't download S3 object 'YourBucket:inbound/the_object_key'"
|
||||
"Anymail AmazonSESInboundWebhookView couldn't download"
|
||||
" S3 object 'YourBucket:inbound/the_object_key'",
|
||||
) as cm:
|
||||
self.post_from_sns('/anymail/amazon_ses/inbound/', raw_sns_message)
|
||||
self.assertIsInstance(cm.exception, ClientError) # both Boto and Anymail exception class
|
||||
self.assertIn("ClientError: An error occurred (403) when calling the HeadObject operation: Forbidden",
|
||||
str(cm.exception)) # original Boto message included
|
||||
self.post_from_sns("/anymail/amazon_ses/inbound/", raw_sns_message)
|
||||
# both Boto and Anymail exception class:
|
||||
self.assertIsInstance(cm.exception, ClientError)
|
||||
# original Boto message included:
|
||||
self.assertIn(
|
||||
"ClientError: An error occurred (403) when calling"
|
||||
" the HeadObject operation: Forbidden",
|
||||
str(cm.exception),
|
||||
)
|
||||
|
||||
def test_incorrect_tracking_event(self):
|
||||
"""The inbound webhook should warn if it receives tracking events"""
|
||||
@@ -303,7 +392,8 @@ class AmazonSESInboundTests(WebhookTestCase, AmazonSESWebhookTestsMixin):
|
||||
|
||||
with self.assertRaisesMessage(
|
||||
AnymailConfigurationError,
|
||||
"You seem to have set an Amazon SES *sending* event or notification to publish to an SNS Topic "
|
||||
"that posts to Anymail's *inbound* webhook URL. (SNS TopicArn arn:...:111111111111:SES_Tracking)"
|
||||
"You seem to have set an Amazon SES *sending* event or notification"
|
||||
" to publish to an SNS Topic that posts to Anymail's *inbound* webhook URL."
|
||||
" (SNS TopicArn arn:...:111111111111:SES_Tracking)",
|
||||
):
|
||||
self.post_from_sns('/anymail/amazon_ses/inbound/', raw_sns_message)
|
||||
self.post_from_sns("/anymail/amazon_ses/inbound/", raw_sns_message)
|
||||
|
||||
Reference in New Issue
Block a user