Tests: Simplify Django version compatibility

* Restructure runtests.py as suggested in
  https://docs.djangoproject.com/en/1.9/topics/testing/advanced/#using-the-django-test-runner-to-test-reusable-applications

* Load version-specific Django settings modules
  (rather than trying to make a single settings.configure()
  work with all supported versions).

* Use `django-admin startproject` defaults for all
  settings modules. (Tests compatibility with typical
  apps, middleware, and other settings.)

* Set up tests-specific url config; switch to literal
  urls in test cases. (Eliminates url `reverse` from
  tests.)

* Make runtests.py executable

Closes #18
This commit is contained in:
medmunds
2016-05-31 11:01:45 -07:00
parent 296f6cab50
commit 34af81aee6
10 changed files with 435 additions and 106 deletions

68
runtests.py Normal file → Executable file
View File

@@ -1,57 +1,33 @@
#!/usr/bin/env python
# python setup.py test # python setup.py test
# or # or
# python runtests.py [anymail.tests.test_x anymail.tests.test_y.SomeTestCase ...] # runtests.py [tests.test_x tests.test_y.SomeTestCase ...]
import sys import sys
import django
import os
import warnings import warnings
from django import setup
from django.conf import settings from django.conf import settings
from django.test.runner import DiscoverRunner as TestRunner from django.test.utils import get_runner
warnings.simplefilter('default') # show DeprecationWarning and other default-ignored warnings
APP = 'anymail'
settings.configure(
DEBUG=True,
DATABASES={
'default': {
'ENGINE': 'django.db.backends.sqlite3',
}
},
ROOT_URLCONF=APP+'.urls',
INSTALLED_APPS=(
'django.contrib.auth',
'django.contrib.contenttypes',
'django.contrib.sessions',
'django.contrib.admin',
APP,
),
MIDDLEWARE_CLASSES=(
'django.middleware.common.CommonMiddleware',
'django.contrib.sessions.middleware.SessionMiddleware',
'django.middleware.csrf.CsrfViewMiddleware',
'django.contrib.auth.middleware.AuthenticationMiddleware',
),
TEMPLATES=[
# Anymail doesn't have any templates, but tests need a TEMPLATES
# setting to avoid warnings from the Django 1.8+ test client.
{
'BACKEND': 'django.template.backends.django.DjangoTemplates',
},
],
)
# Initialize Django app registry
setup()
def runtests(*args): def runtests(test_labels=None):
"""Discover and run project tests. Returns number of failures."""
test_labels = test_labels or ['tests']
# noinspection PyStringFormat
os.environ['DJANGO_SETTINGS_MODULE'] = \
'tests.test_settings.settings_%d_%d' % django.VERSION[:2]
django.setup()
TestRunner = get_runner(settings)
test_runner = TestRunner(verbosity=1) test_runner = TestRunner(verbosity=1)
test_labels = args if len(args) > 0 else ['tests'] return test_runner.run_tests(test_labels)
failures = test_runner.run_tests(test_labels)
sys.exit(failures)
if __name__ == '__main__': if __name__ == '__main__':
runtests(*sys.argv[1:]) warnings.simplefilter('default') # show DeprecationWarning and other default-ignored warnings
failures = runtests(test_labels=sys.argv[1:])
sys.exit(bool(failures))

View File

@@ -4,7 +4,6 @@ from datetime import datetime
import hashlib import hashlib
import hmac import hmac
from django.core.exceptions import ImproperlyConfigured from django.core.exceptions import ImproperlyConfigured
from django.core.urlresolvers import reverse
from django.test import override_settings from django.test import override_settings
from django.utils.timezone import utc from django.utils.timezone import utc
from mock import ANY from mock import ANY
@@ -42,9 +41,9 @@ def querydict_to_postdict(qd):
class MailgunWebhookSettingsTestCase(WebhookTestCase): class MailgunWebhookSettingsTestCase(WebhookTestCase):
def test_requires_api_key(self): def test_requires_api_key(self):
webhook = reverse('mailgun_tracking_webhook')
with self.assertRaises(ImproperlyConfigured): with self.assertRaises(ImproperlyConfigured):
self.client.post(webhook, data=mailgun_sign({'event': 'delivered'})) self.client.post('/anymail/mailgun/tracking/',
data=mailgun_sign({'event': 'delivered'}))
@override_settings(ANYMAIL_MAILGUN_API_KEY=TEST_API_KEY) @override_settings(ANYMAIL_MAILGUN_API_KEY=TEST_API_KEY)
@@ -52,25 +51,24 @@ class MailgunWebhookSecurityTestCase(WebhookTestCase, WebhookBasicAuthTestsMixin
should_warn_if_no_auth = False # because we check webhook signature should_warn_if_no_auth = False # because we check webhook signature
def call_webhook(self): def call_webhook(self):
webhook = reverse('mailgun_tracking_webhook') return self.client.post('/anymail/mailgun/tracking/',
return self.client.post(webhook, data=mailgun_sign({'event': 'delivered'})) data=mailgun_sign({'event': 'delivered'}))
# Additional tests are in WebhookBasicAuthTestsMixin # Additional tests are in WebhookBasicAuthTestsMixin
def test_verifies_correct_signature(self): def test_verifies_correct_signature(self):
webhook = reverse('mailgun_tracking_webhook') response = self.client.post('/anymail/mailgun/tracking/',
response = self.client.post(webhook, data=mailgun_sign({'event': 'delivered'})) data=mailgun_sign({'event': 'delivered'}))
self.assertEqual(response.status_code, 200) self.assertEqual(response.status_code, 200)
def test_verifies_missing_signature(self): def test_verifies_missing_signature(self):
webhook = reverse('mailgun_tracking_webhook') response = self.client.post('/anymail/mailgun/tracking/',
response = self.client.post(webhook, data={'event': 'delivered'}) data={'event': 'delivered'})
self.assertEqual(response.status_code, 400) self.assertEqual(response.status_code, 400)
def test_verifies_bad_signature(self): def test_verifies_bad_signature(self):
webhook = reverse('mailgun_tracking_webhook')
data = mailgun_sign({'event': 'delivered'}, api_key="wrong API key") data = mailgun_sign({'event': 'delivered'}, api_key="wrong API key")
response = self.client.post(webhook, data=data) response = self.client.post('/anymail/mailgun/tracking/', data=data)
self.assertEqual(response.status_code, 400) self.assertEqual(response.status_code, 400)
@@ -99,8 +97,7 @@ class MailgunDeliveryTestCase(WebhookTestCase):
'recipient': 'recipient@example.com', 'recipient': 'recipient@example.com',
'event': 'delivered', 'event': 'delivered',
}) })
webhook = reverse('mailgun_tracking_webhook') response = self.client.post('/anymail/mailgun/tracking/', data=raw_event)
response = self.client.post(webhook, data=raw_event)
self.assertEqual(response.status_code, 200) self.assertEqual(response.status_code, 200)
kwargs = self.assert_handler_called_once_with(self.tracking_handler, sender=MailgunTrackingWebhookView, kwargs = self.assert_handler_called_once_with(self.tracking_handler, sender=MailgunTrackingWebhookView,
event=ANY, esp_name='Mailgun') event=ANY, esp_name='Mailgun')
@@ -137,8 +134,7 @@ class MailgunDeliveryTestCase(WebhookTestCase):
'X-Mailgun-Sid': 'WyI3Y2VjMyIsICJib3VuY2VAZXhhbXBsZS5jb20iLCAiZjFjNzgyIl0=', 'X-Mailgun-Sid': 'WyI3Y2VjMyIsICJib3VuY2VAZXhhbXBsZS5jb20iLCAiZjFjNzgyIl0=',
'token': 'a3fe1fa1640349ac552b84ddde373014b4c41645830c8dd3fc', 'token': 'a3fe1fa1640349ac552b84ddde373014b4c41645830c8dd3fc',
}) })
webhook = reverse('mailgun_tracking_webhook') response = self.client.post('/anymail/mailgun/tracking/', data=raw_event)
response = self.client.post(webhook, data=raw_event)
self.assertEqual(response.status_code, 200) self.assertEqual(response.status_code, 200)
kwargs = self.assert_handler_called_once_with(self.tracking_handler, sender=MailgunTrackingWebhookView, kwargs = self.assert_handler_called_once_with(self.tracking_handler, sender=MailgunTrackingWebhookView,
event=ANY, esp_name='Mailgun') event=ANY, esp_name='Mailgun')
@@ -162,8 +158,7 @@ class MailgunDeliveryTestCase(WebhookTestCase):
'recipient': 'complaint@example.com', 'recipient': 'complaint@example.com',
# (omitting some fields that aren't relevant to the test) # (omitting some fields that aren't relevant to the test)
}) })
webhook = reverse('mailgun_tracking_webhook') response = self.client.post('/anymail/mailgun/tracking/', data=raw_event)
response = self.client.post(webhook, data=raw_event)
self.assertEqual(response.status_code, 200) self.assertEqual(response.status_code, 200)
kwargs = self.assert_handler_called_once_with(self.tracking_handler, sender=MailgunTrackingWebhookView, kwargs = self.assert_handler_called_once_with(self.tracking_handler, sender=MailgunTrackingWebhookView,
event=ANY, esp_name='Mailgun') event=ANY, esp_name='Mailgun')
@@ -181,8 +176,7 @@ class MailgunDeliveryTestCase(WebhookTestCase):
'recipient': 'complaint@example.com', 'recipient': 'complaint@example.com',
# (omitting some fields that aren't relevant to the test) # (omitting some fields that aren't relevant to the test)
}) })
webhook = reverse('mailgun_tracking_webhook') response = self.client.post('/anymail/mailgun/tracking/', data=raw_event)
response = self.client.post(webhook, data=raw_event)
self.assertEqual(response.status_code, 200) self.assertEqual(response.status_code, 200)
kwargs = self.assert_handler_called_once_with(self.tracking_handler, sender=MailgunTrackingWebhookView, kwargs = self.assert_handler_called_once_with(self.tracking_handler, sender=MailgunTrackingWebhookView,
event=ANY, esp_name='Mailgun') event=ANY, esp_name='Mailgun')
@@ -201,8 +195,7 @@ class MailgunDeliveryTestCase(WebhookTestCase):
'recipient': 'noreply@example.com', 'recipient': 'noreply@example.com',
# (omitting some fields that aren't relevant to the test) # (omitting some fields that aren't relevant to the test)
}) })
webhook = reverse('mailgun_tracking_webhook') response = self.client.post('/anymail/mailgun/tracking/', data=raw_event)
response = self.client.post(webhook, data=raw_event)
self.assertEqual(response.status_code, 200) self.assertEqual(response.status_code, 200)
kwargs = self.assert_handler_called_once_with(self.tracking_handler, sender=MailgunTrackingWebhookView, kwargs = self.assert_handler_called_once_with(self.tracking_handler, sender=MailgunTrackingWebhookView,
event=ANY, esp_name='Mailgun') event=ANY, esp_name='Mailgun')
@@ -221,7 +214,7 @@ class MailgunDeliveryTestCase(WebhookTestCase):
'custom1': 'value', 'custom1': 'value',
'custom2': '{"key":"value"}', # you can store JSON, but you'll need to unpack it yourself 'custom2': '{"key":"value"}', # you can store JSON, but you'll need to unpack it yourself
}) })
self.client.post(reverse('mailgun_tracking_webhook'), data=raw_event) self.client.post('/anymail/mailgun/tracking/', data=raw_event)
kwargs = self.assert_handler_called_once_with(self.tracking_handler) kwargs = self.assert_handler_called_once_with(self.tracking_handler)
event = kwargs['event'] event = kwargs['event']
self.assertEqual(event.metadata, {"custom1": "value1", "custom2": '{"key":"value"}'}) self.assertEqual(event.metadata, {"custom1": "value1", "custom2": '{"key":"value"}'})
@@ -232,7 +225,7 @@ class MailgunDeliveryTestCase(WebhookTestCase):
'tag': ['tag1', 'tag2'], # Django TestClient encodes list as multiple field values 'tag': ['tag1', 'tag2'], # Django TestClient encodes list as multiple field values
'event': 'opened', 'event': 'opened',
}) })
self.client.post(reverse('mailgun_tracking_webhook'), data=raw_event) self.client.post('/anymail/mailgun/tracking/', data=raw_event)
kwargs = self.assert_handler_called_once_with(self.tracking_handler) kwargs = self.assert_handler_called_once_with(self.tracking_handler)
event = kwargs['event'] event = kwargs['event']
self.assertEqual(event.tags, ["tag1", "tag2"]) self.assertEqual(event.tags, ["tag1", "tag2"])
@@ -243,8 +236,7 @@ class MailgunDeliveryTestCase(WebhookTestCase):
'X-Mailgun-Tag': ['tag1', 'tag2'], 'X-Mailgun-Tag': ['tag1', 'tag2'],
'event': 'delivered', 'event': 'delivered',
}) })
self.client.post(reverse('mailgun_tracking_webhook'), data=raw_event) self.client.post('/anymail/mailgun/tracking/', data=raw_event)
kwargs = self.assert_handler_called_once_with(self.tracking_handler)
kwargs = self.assert_handler_called_once_with(self.tracking_handler) kwargs = self.assert_handler_called_once_with(self.tracking_handler)
event = kwargs['event'] event = kwargs['event']
self.assertEqual(event.tags, ["tag1", "tag2"]) self.assertEqual(event.tags, ["tag1", "tag2"])

View File

@@ -7,7 +7,6 @@ import hashlib
import hmac import hmac
from base64 import b64encode from base64 import b64encode
from django.core.exceptions import ImproperlyConfigured from django.core.exceptions import ImproperlyConfigured
from django.core.urlresolvers import reverse
from django.test import override_settings from django.test import override_settings
from django.utils.timezone import utc from django.utils.timezone import utc
from mock import ANY from mock import ANY
@@ -20,14 +19,14 @@ from .webhook_cases import WebhookTestCase, WebhookBasicAuthTestsMixin
TEST_WEBHOOK_KEY = 'TEST_WEBHOOK_KEY' TEST_WEBHOOK_KEY = 'TEST_WEBHOOK_KEY'
def mandrill_args(events=None, urlname='mandrill_tracking_webhook', key=TEST_WEBHOOK_KEY): def mandrill_args(events=None, url='/anymail/mandrill/tracking/', key=TEST_WEBHOOK_KEY):
"""Returns TestClient.post kwargs for Mandrill webhook call with events """Returns TestClient.post kwargs for Mandrill webhook call with events
Computes correct signature. Computes correct signature.
""" """
if events is None: if events is None:
events = [] events = []
url = urljoin('http://testserver/', reverse(urlname)) url = urljoin('http://testserver/', url)
mandrill_events = json.dumps(events) mandrill_events = json.dumps(events)
signed_data = url + 'mandrill_events' + mandrill_events signed_data = url + 'mandrill_events' + mandrill_events
signature = b64encode(hmac.new(key=key.encode('ascii'), signature = b64encode(hmac.new(key=key.encode('ascii'),
@@ -42,9 +41,9 @@ def mandrill_args(events=None, urlname='mandrill_tracking_webhook', key=TEST_WEB
class MandrillWebhookSettingsTestCase(WebhookTestCase): class MandrillWebhookSettingsTestCase(WebhookTestCase):
def test_requires_webhook_key(self): def test_requires_webhook_key(self):
webhook = reverse('mandrill_tracking_webhook')
with self.assertRaises(ImproperlyConfigured): with self.assertRaises(ImproperlyConfigured):
self.client.post(webhook, data={'mandrill_events': '[]'}) self.client.post('/anymail/mandrill/tracking/',
data={'mandrill_events': '[]'})
@override_settings(ANYMAIL_MANDRILL_WEBHOOK_KEY=TEST_WEBHOOK_KEY) @override_settings(ANYMAIL_MANDRILL_WEBHOOK_KEY=TEST_WEBHOOK_KEY)
@@ -63,8 +62,8 @@ class MandrillWebhookSecurityTestCase(WebhookTestCase, WebhookBasicAuthTestsMixi
self.assertEqual(response.status_code, 200) self.assertEqual(response.status_code, 200)
def test_verifies_missing_signature(self): def test_verifies_missing_signature(self):
webhook = reverse('mandrill_tracking_webhook') response = self.client.post('/anymail/mandrill/tracking/',
response = self.client.post(webhook, data={'mandrill_events': '[{"event":"send"}]'}) data={'mandrill_events': '[{"event":"send"}]'})
self.assertEqual(response.status_code, 400) self.assertEqual(response.status_code, 400)
def test_verifies_bad_signature(self): def test_verifies_bad_signature(self):
@@ -78,8 +77,7 @@ class MandrillTrackingTestCase(WebhookTestCase):
def test_head_request(self): def test_head_request(self):
# Mandrill verifies webhooks at config time with a HEAD request # Mandrill verifies webhooks at config time with a HEAD request
webhook = reverse('mandrill_tracking_webhook') response = self.client.head('/anymail/mandrill/tracking/')
response = self.client.head(webhook)
self.assertEqual(response.status_code, 200) self.assertEqual(response.status_code, 200)
def test_post_request_invalid_json(self): def test_post_request_invalid_json(self):

View File

@@ -1,7 +1,6 @@
import json import json
from datetime import datetime from datetime import datetime
from django.core.urlresolvers import reverse
from django.utils.timezone import get_fixed_timezone from django.utils.timezone import get_fixed_timezone
from mock import ANY from mock import ANY
@@ -12,8 +11,8 @@ from .webhook_cases import WebhookBasicAuthTestsMixin, WebhookTestCase
class PostmarkWebhookSecurityTestCase(WebhookTestCase, WebhookBasicAuthTestsMixin): class PostmarkWebhookSecurityTestCase(WebhookTestCase, WebhookBasicAuthTestsMixin):
def call_webhook(self): def call_webhook(self):
webhook = reverse('postmark_tracking_webhook') return self.client.post('/anymail/postmark/tracking/',
return self.client.post(webhook, content_type='application/json', data=json.dumps({})) content_type='application/json', data=json.dumps({}))
# Actual tests are in WebhookBasicAuthTestsMixin # Actual tests are in WebhookBasicAuthTestsMixin
@@ -36,8 +35,8 @@ class PostmarkDeliveryTestCase(WebhookTestCase):
"Subject": "Postmark event test", "Subject": "Postmark event test",
"Content": "..." "Content": "..."
} }
webhook = reverse('postmark_tracking_webhook') response = self.client.post('/anymail/postmark/tracking/',
response = self.client.post(webhook, content_type='application/json', data=json.dumps(raw_event)) content_type='application/json', data=json.dumps(raw_event))
self.assertEqual(response.status_code, 200) self.assertEqual(response.status_code, 200)
kwargs = self.assert_handler_called_once_with(self.tracking_handler, sender=PostmarkTrackingWebhookView, kwargs = self.assert_handler_called_once_with(self.tracking_handler, sender=PostmarkTrackingWebhookView,
event=ANY, esp_name='Postmark') event=ANY, esp_name='Postmark')
@@ -69,8 +68,8 @@ class PostmarkDeliveryTestCase(WebhookTestCase):
"ReceivedAt": "2016-04-27T16:21:41.2493688-04:00", "ReceivedAt": "2016-04-27T16:21:41.2493688-04:00",
"Recipient": "recipient@example.com" "Recipient": "recipient@example.com"
} }
webhook = reverse('postmark_tracking_webhook') response = self.client.post('/anymail/postmark/tracking/',
response = self.client.post(webhook, content_type='application/json', data=json.dumps(raw_event)) content_type='application/json', data=json.dumps(raw_event))
self.assertEqual(response.status_code, 200) self.assertEqual(response.status_code, 200)
kwargs = self.assert_handler_called_once_with(self.tracking_handler, sender=PostmarkTrackingWebhookView, kwargs = self.assert_handler_called_once_with(self.tracking_handler, sender=PostmarkTrackingWebhookView,
event=ANY, esp_name='Postmark') event=ANY, esp_name='Postmark')

View File

@@ -1,7 +1,6 @@
import json import json
from datetime import datetime from datetime import datetime
from django.core.urlresolvers import reverse
from django.utils.timezone import utc from django.utils.timezone import utc
from mock import ANY from mock import ANY
@@ -12,8 +11,8 @@ from .webhook_cases import WebhookBasicAuthTestsMixin, WebhookTestCase
class SendGridWebhookSecurityTestCase(WebhookTestCase, WebhookBasicAuthTestsMixin): class SendGridWebhookSecurityTestCase(WebhookTestCase, WebhookBasicAuthTestsMixin):
def call_webhook(self): def call_webhook(self):
webhook = reverse('sendgrid_tracking_webhook') return self.client.post('/anymail/sendgrid/tracking/',
return self.client.post(webhook, content_type='application/json', data=json.dumps([])) content_type='application/json', data=json.dumps([]))
# Actual tests are in WebhookBasicAuthTestsMixin # Actual tests are in WebhookBasicAuthTestsMixin
@@ -32,8 +31,8 @@ class SendGridDeliveryTestCase(WebhookTestCase):
"custom1": "value1", "custom1": "value1",
"custom2": "value2", "custom2": "value2",
}] }]
webhook = reverse('sendgrid_tracking_webhook') response = self.client.post('/anymail/sendgrid/tracking/',
response = self.client.post(webhook, content_type='application/json', data=json.dumps(raw_events)) content_type='application/json', data=json.dumps(raw_events))
self.assertEqual(response.status_code, 200) self.assertEqual(response.status_code, 200)
kwargs = self.assert_handler_called_once_with(self.tracking_handler, sender=SendGridTrackingWebhookView, kwargs = self.assert_handler_called_once_with(self.tracking_handler, sender=SendGridTrackingWebhookView,
event=ANY, esp_name='SendGrid') event=ANY, esp_name='SendGrid')
@@ -60,8 +59,8 @@ class SendGridDeliveryTestCase(WebhookTestCase):
"timestamp": 1461095250, "timestamp": 1461095250,
"smtp-id": "<wrfRRvF7Q0GgwUo2CvDmEA@example.com>" "smtp-id": "<wrfRRvF7Q0GgwUo2CvDmEA@example.com>"
}] }]
webhook = reverse('sendgrid_tracking_webhook') response = self.client.post('/anymail/sendgrid/tracking/',
response = self.client.post(webhook, content_type='application/json', data=json.dumps(raw_events)) content_type='application/json', data=json.dumps(raw_events))
self.assertEqual(response.status_code, 200) self.assertEqual(response.status_code, 200)
kwargs = self.assert_handler_called_once_with(self.tracking_handler, sender=SendGridTrackingWebhookView, kwargs = self.assert_handler_called_once_with(self.tracking_handler, sender=SendGridTrackingWebhookView,
event=ANY, esp_name='SendGrid') event=ANY, esp_name='SendGrid')
@@ -87,8 +86,8 @@ class SendGridDeliveryTestCase(WebhookTestCase):
"reason": "Invalid", "reason": "Invalid",
"event": "dropped" "event": "dropped"
}] }]
webhook = reverse('sendgrid_tracking_webhook') response = self.client.post('/anymail/sendgrid/tracking/',
response = self.client.post(webhook, content_type='application/json', data=json.dumps(raw_events)) content_type='application/json', data=json.dumps(raw_events))
self.assertEqual(response.status_code, 200) self.assertEqual(response.status_code, 200)
kwargs = self.assert_handler_called_once_with(self.tracking_handler, sender=SendGridTrackingWebhookView, kwargs = self.assert_handler_called_once_with(self.tracking_handler, sender=SendGridTrackingWebhookView,
event=ANY, esp_name='SendGrid') event=ANY, esp_name='SendGrid')
@@ -112,8 +111,8 @@ class SendGridDeliveryTestCase(WebhookTestCase):
"reason": "Unsubscribed Address", "reason": "Unsubscribed Address",
"event": "dropped" "event": "dropped"
}] }]
webhook = reverse('sendgrid_tracking_webhook') response = self.client.post('/anymail/sendgrid/tracking/',
response = self.client.post(webhook, content_type='application/json', data=json.dumps(raw_events)) content_type='application/json', data=json.dumps(raw_events))
self.assertEqual(response.status_code, 200) self.assertEqual(response.status_code, 200)
kwargs = self.assert_handler_called_once_with(self.tracking_handler, sender=SendGridTrackingWebhookView, kwargs = self.assert_handler_called_once_with(self.tracking_handler, sender=SendGridTrackingWebhookView,
event=ANY, esp_name='SendGrid') event=ANY, esp_name='SendGrid')
@@ -141,8 +140,8 @@ class SendGridDeliveryTestCase(WebhookTestCase):
"smtp-id": "<Lli-03HcQ5-JLybO9fXsJg@example.com>", "smtp-id": "<Lli-03HcQ5-JLybO9fXsJg@example.com>",
"type": "bounce" "type": "bounce"
}] }]
webhook = reverse('sendgrid_tracking_webhook') response = self.client.post('/anymail/sendgrid/tracking/',
response = self.client.post(webhook, content_type='application/json', data=json.dumps(raw_events)) content_type='application/json', data=json.dumps(raw_events))
self.assertEqual(response.status_code, 200) self.assertEqual(response.status_code, 200)
kwargs = self.assert_handler_called_once_with(self.tracking_handler, sender=SendGridTrackingWebhookView, kwargs = self.assert_handler_called_once_with(self.tracking_handler, sender=SendGridTrackingWebhookView,
event=ANY, esp_name='SendGrid') event=ANY, esp_name='SendGrid')
@@ -166,8 +165,8 @@ class SendGridDeliveryTestCase(WebhookTestCase):
"timestamp": 1461200990, "timestamp": 1461200990,
"smtp-id": "<20160421010427.2847.6797@example.com>", "smtp-id": "<20160421010427.2847.6797@example.com>",
}] }]
webhook = reverse('sendgrid_tracking_webhook') response = self.client.post('/anymail/sendgrid/tracking/',
response = self.client.post(webhook, content_type='application/json', data=json.dumps(raw_events)) content_type='application/json', data=json.dumps(raw_events))
self.assertEqual(response.status_code, 200) self.assertEqual(response.status_code, 200)
kwargs = self.assert_handler_called_once_with(self.tracking_handler, sender=SendGridTrackingWebhookView, kwargs = self.assert_handler_called_once_with(self.tracking_handler, sender=SendGridTrackingWebhookView,
event=ANY, esp_name='SendGrid') event=ANY, esp_name='SendGrid')
@@ -192,8 +191,8 @@ class SendGridDeliveryTestCase(WebhookTestCase):
"useragent": "Mozilla/5.0 (Windows NT 5.1; rv:11.0) Gecko Firefox/11.0", "useragent": "Mozilla/5.0 (Windows NT 5.1; rv:11.0) Gecko Firefox/11.0",
"event": "open" "event": "open"
}] }]
webhook = reverse('sendgrid_tracking_webhook') response = self.client.post('/anymail/sendgrid/tracking/',
response = self.client.post(webhook, content_type='application/json', data=json.dumps(raw_events)) content_type='application/json', data=json.dumps(raw_events))
self.assertEqual(response.status_code, 200) self.assertEqual(response.status_code, 200)
kwargs = self.assert_handler_called_once_with(self.tracking_handler, sender=SendGridTrackingWebhookView, kwargs = self.assert_handler_called_once_with(self.tracking_handler, sender=SendGridTrackingWebhookView,
event=ANY, esp_name='SendGrid') event=ANY, esp_name='SendGrid')
@@ -219,8 +218,8 @@ class SendGridDeliveryTestCase(WebhookTestCase):
"timestamp": 1461095250, "timestamp": 1461095250,
"url": "http://www.example.com" "url": "http://www.example.com"
}] }]
webhook = reverse('sendgrid_tracking_webhook') response = self.client.post('/anymail/sendgrid/tracking/',
response = self.client.post(webhook, content_type='application/json', data=json.dumps(raw_events)) content_type='application/json', data=json.dumps(raw_events))
self.assertEqual(response.status_code, 200) self.assertEqual(response.status_code, 200)
kwargs = self.assert_handler_called_once_with(self.tracking_handler, sender=SendGridTrackingWebhookView, kwargs = self.assert_handler_called_once_with(self.tracking_handler, sender=SendGridTrackingWebhookView,
event=ANY, esp_name='SendGrid') event=ANY, esp_name='SendGrid')

View File

@@ -0,0 +1,14 @@
# These are the default "django-admin startproject" settings.py files
# for each supported version of Django, with:
#
# * "anymail" appended to INSTALLED_APPS
# * ROOT_URLCONF = 'tests.test_settings.urls'
#
# Keeping a file for each Django version is simpler than trying
# to maintain warning-free compatibility with all Django versions
# in a single settings list.
#
# It also helps ensure compatibility with default apps, middleware, etc.
#
# You can typically find the default settings.py template in
# SITE_PACKAGES/django/conf/project_template/project_name/settings.py-tpl

View File

@@ -0,0 +1,121 @@
"""
Django settings for Anymail tests.
Generated by 'django-admin startproject' using Django 1.10.
For more information on this file, see
https://docs.djangoproject.com/en/1.10/topics/settings/
For the full list of settings and their values, see
https://docs.djangoproject.com/en/1.10/ref/settings/
"""
import os
# Build paths inside the project like this: os.path.join(BASE_DIR, ...)
BASE_DIR = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
# Quick-start development settings - unsuitable for production
# See https://docs.djangoproject.com/en/1.10/howto/deployment/checklist/
# SECURITY WARNING: keep the secret key used in production secret!
SECRET_KEY = 'NOT_FOR_PRODUCTION_USE'
# SECURITY WARNING: don't run with debug turned on in production!
DEBUG = True
ALLOWED_HOSTS = []
# Application definition
INSTALLED_APPS = [
'django.contrib.admin',
'django.contrib.auth',
'django.contrib.contenttypes',
'django.contrib.sessions',
'django.contrib.messages',
'django.contrib.staticfiles',
'anymail',
]
MIDDLEWARE = [
'django.middleware.security.SecurityMiddleware',
'django.contrib.sessions.middleware.SessionMiddleware',
'django.middleware.common.CommonMiddleware',
'django.middleware.csrf.CsrfViewMiddleware',
'django.contrib.auth.middleware.AuthenticationMiddleware',
'django.contrib.messages.middleware.MessageMiddleware',
'django.middleware.clickjacking.XFrameOptionsMiddleware',
]
ROOT_URLCONF = 'tests.test_settings.urls'
TEMPLATES = [
{
'BACKEND': 'django.template.backends.django.DjangoTemplates',
'DIRS': [],
'APP_DIRS': True,
'OPTIONS': {
'context_processors': [
'django.template.context_processors.debug',
'django.template.context_processors.request',
'django.contrib.auth.context_processors.auth',
'django.contrib.messages.context_processors.messages',
],
},
},
]
WSGI_APPLICATION = 'tests.wsgi.application'
# Database
# https://docs.djangoproject.com/en/1.10/ref/settings/#databases
DATABASES = {
'default': {
'ENGINE': 'django.db.backends.sqlite3',
'NAME': os.path.join(BASE_DIR, 'db.sqlite3'),
}
}
# Password validation
# https://docs.djangoproject.com/en/1.10/ref/settings/#auth-password-validators
AUTH_PASSWORD_VALIDATORS = [
{
'NAME': 'django.contrib.auth.password_validation.UserAttributeSimilarityValidator',
},
{
'NAME': 'django.contrib.auth.password_validation.MinimumLengthValidator',
},
{
'NAME': 'django.contrib.auth.password_validation.CommonPasswordValidator',
},
{
'NAME': 'django.contrib.auth.password_validation.NumericPasswordValidator',
},
]
# Internationalization
# https://docs.djangoproject.com/en/1.10/topics/i18n/
LANGUAGE_CODE = 'en-us'
TIME_ZONE = 'UTC'
USE_I18N = True
USE_L10N = True
USE_TZ = True
# Static files (CSS, JavaScript, Images)
# https://docs.djangoproject.com/en/1.10/howto/static-files/
STATIC_URL = '/static/'

View File

@@ -0,0 +1,103 @@
"""
Django settings for Anymail tests.
Generated by 'django-admin startproject' using Django 1.8.
For more information on this file, see
https://docs.djangoproject.com/en/1.8/topics/settings/
For the full list of settings and their values, see
https://docs.djangoproject.com/en/1.8/ref/settings/
"""
# Build paths inside the project like this: os.path.join(BASE_DIR, ...)
import os
BASE_DIR = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
# Quick-start development settings - unsuitable for production
# See https://docs.djangoproject.com/en/1.8/howto/deployment/checklist/
# SECURITY WARNING: keep the secret key used in production secret!
SECRET_KEY = 'NOT_FOR_PRODUCTION_USE'
# SECURITY WARNING: don't run with debug turned on in production!
DEBUG = True
ALLOWED_HOSTS = []
# Application definition
INSTALLED_APPS = (
'django.contrib.admin',
'django.contrib.auth',
'django.contrib.contenttypes',
'django.contrib.sessions',
'django.contrib.messages',
'django.contrib.staticfiles',
'anymail',
)
MIDDLEWARE_CLASSES = (
'django.contrib.sessions.middleware.SessionMiddleware',
'django.middleware.common.CommonMiddleware',
'django.middleware.csrf.CsrfViewMiddleware',
'django.contrib.auth.middleware.AuthenticationMiddleware',
'django.contrib.auth.middleware.SessionAuthenticationMiddleware',
'django.contrib.messages.middleware.MessageMiddleware',
'django.middleware.clickjacking.XFrameOptionsMiddleware',
'django.middleware.security.SecurityMiddleware',
)
ROOT_URLCONF = 'tests.test_settings.urls'
TEMPLATES = [
{
'BACKEND': 'django.template.backends.django.DjangoTemplates',
'DIRS': [],
'APP_DIRS': True,
'OPTIONS': {
'context_processors': [
'django.template.context_processors.debug',
'django.template.context_processors.request',
'django.contrib.auth.context_processors.auth',
'django.contrib.messages.context_processors.messages',
],
},
},
]
WSGI_APPLICATION = 'tests.wsgi.application'
# Database
# https://docs.djangoproject.com/en/1.8/ref/settings/#databases
DATABASES = {
'default': {
'ENGINE': 'django.db.backends.sqlite3',
'NAME': os.path.join(BASE_DIR, 'db.sqlite3'),
}
}
# Internationalization
# https://docs.djangoproject.com/en/1.8/topics/i18n/
LANGUAGE_CODE = 'en-us'
TIME_ZONE = 'UTC'
USE_I18N = True
USE_L10N = True
USE_TZ = True
# Static files (CSS, JavaScript, Images)
# https://docs.djangoproject.com/en/1.8/howto/static-files/
STATIC_URL = '/static/'

View File

@@ -0,0 +1,122 @@
"""
Django settings for Anymail tests.
Generated by 'django-admin startproject' using Django 1.9.
For more information on this file, see
https://docs.djangoproject.com/en/1.9/topics/settings/
For the full list of settings and their values, see
https://docs.djangoproject.com/en/1.9/ref/settings/
"""
import os
# Build paths inside the project like this: os.path.join(BASE_DIR, ...)
BASE_DIR = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
# Quick-start development settings - unsuitable for production
# See https://docs.djangoproject.com/en/1.9/howto/deployment/checklist/
# SECURITY WARNING: keep the secret key used in production secret!
SECRET_KEY = 'NOT_FOR_PRODUCTION_USE'
# SECURITY WARNING: don't run with debug turned on in production!
DEBUG = True
ALLOWED_HOSTS = []
# Application definition
INSTALLED_APPS = [
'django.contrib.admin',
'django.contrib.auth',
'django.contrib.contenttypes',
'django.contrib.sessions',
'django.contrib.messages',
'django.contrib.staticfiles',
'anymail',
]
MIDDLEWARE_CLASSES = [
'django.middleware.security.SecurityMiddleware',
'django.contrib.sessions.middleware.SessionMiddleware',
'django.middleware.common.CommonMiddleware',
'django.middleware.csrf.CsrfViewMiddleware',
'django.contrib.auth.middleware.AuthenticationMiddleware',
'django.contrib.auth.middleware.SessionAuthenticationMiddleware',
'django.contrib.messages.middleware.MessageMiddleware',
'django.middleware.clickjacking.XFrameOptionsMiddleware',
]
ROOT_URLCONF = 'tests.test_settings.urls'
TEMPLATES = [
{
'BACKEND': 'django.template.backends.django.DjangoTemplates',
'DIRS': [],
'APP_DIRS': True,
'OPTIONS': {
'context_processors': [
'django.template.context_processors.debug',
'django.template.context_processors.request',
'django.contrib.auth.context_processors.auth',
'django.contrib.messages.context_processors.messages',
],
},
},
]
WSGI_APPLICATION = 'tests.wsgi.application'
# Database
# https://docs.djangoproject.com/en/1.9/ref/settings/#databases
DATABASES = {
'default': {
'ENGINE': 'django.db.backends.sqlite3',
'NAME': os.path.join(BASE_DIR, 'db.sqlite3'),
}
}
# Password validation
# https://docs.djangoproject.com/en/1.9/ref/settings/#auth-password-validators
AUTH_PASSWORD_VALIDATORS = [
{
'NAME': 'django.contrib.auth.password_validation.UserAttributeSimilarityValidator',
},
{
'NAME': 'django.contrib.auth.password_validation.MinimumLengthValidator',
},
{
'NAME': 'django.contrib.auth.password_validation.CommonPasswordValidator',
},
{
'NAME': 'django.contrib.auth.password_validation.NumericPasswordValidator',
},
]
# Internationalization
# https://docs.djangoproject.com/en/1.9/topics/i18n/
LANGUAGE_CODE = 'en-us'
TIME_ZONE = 'UTC'
USE_I18N = True
USE_L10N = True
USE_TZ = True
# Static files (CSS, JavaScript, Images)
# https://docs.djangoproject.com/en/1.9/howto/static-files/
STATIC_URL = '/static/'

View File

@@ -0,0 +1,5 @@
from django.conf.urls import include, url
urlpatterns = [
url(r'^anymail/', include('anymail.urls')),
]