mirror of
https://github.com/pacnpal/django-anymail.git
synced 2025-12-20 03:41:05 -05:00
Test without optional packages
Tox: * Add Tox factor for extras (all, none, individual ESP). For now, only break out ESPs that have specific extra dependencies (amazon_ses, sparkpost). * Install most package dependencies (including extras) through the package itself. * Use new runtests.py environment vars to limit test tags when Tox isn't installing all extras. Travis: * Rework matrix to request specific TOXENVs directly; drop tox-travis. Test runner (runtests.py): * Centralize RUN_LIVE_TESTS logic in runtests.py * Add ANYMAIL_ONLY_TEST and ANYMAIL_SKIP_TESTS env vars (comma-separated lists of tags) Test implementations: * Tag all ESP-specific tests with ESP * Tag live tests with "live" * Don't import ESP-specific packages at test module level. (Test discovery imports test modules before tag-based filtering.) Closes #104
This commit is contained in:
51
.travis.yml
51
.travis.yml
@@ -10,9 +10,15 @@ branches:
|
|||||||
- master
|
- master
|
||||||
- /^v\d+\.\d+(\.\d+)?(-\S*)?$/
|
- /^v\d+\.\d+(\.\d+)?(-\S*)?$/
|
||||||
|
|
||||||
|
env:
|
||||||
|
global:
|
||||||
|
# Let Travis report failures that tox.ini would normally ignore:
|
||||||
|
- TOX_FORCE_IGNORE_OUTCOME=false
|
||||||
|
|
||||||
matrix:
|
matrix:
|
||||||
include:
|
include:
|
||||||
- { env: LINT_AND_DOCS=true, python: 3.6 }
|
- python: 3.6
|
||||||
|
env: TOXENV="lint,docs"
|
||||||
|
|
||||||
# Anymail supports the same Python versions as Django, plus PyPy.
|
# Anymail supports the same Python versions as Django, plus PyPy.
|
||||||
# https://docs.djangoproject.com/en/dev/faq/install/#what-python-version-can-i-use-with-django
|
# https://docs.djangoproject.com/en/dev/faq/install/#what-python-version-can-i-use-with-django
|
||||||
@@ -21,36 +27,41 @@ matrix:
|
|||||||
# combinations, to avoid rapidly consuming the testing accounts' entire send allotments.
|
# combinations, to avoid rapidly consuming the testing accounts' entire send allotments.
|
||||||
|
|
||||||
# Django 1.11: Python 2.7, 3.4, 3.5, or 3.6
|
# Django 1.11: Python 2.7, 3.4, 3.5, or 3.6
|
||||||
- { env: DJANGO=1.11 RUN_LIVE_TESTS=true, python: 2.7 }
|
- { env: TOXENV=django111-py27-all RUN_LIVE_TESTS=true, python: 2.7 }
|
||||||
- { env: DJANGO=1.11, python: 3.4 }
|
- { env: TOXENV=django111-py34-all, python: 3.4 }
|
||||||
- { env: DJANGO=1.11, python: 3.5 }
|
- { env: TOXENV=django111-py35-all, python: 3.5 }
|
||||||
- { env: DJANGO=1.11, python: 3.6 }
|
- { env: TOXENV=django111-py36-all, python: 3.6 }
|
||||||
- { env: DJANGO=1.11, python: pypy2.7-6.0 }
|
- { env: TOXENV=django111-pypy-all, python: pypy2.7-6.0 }
|
||||||
# Django 2.0: Python 3.5+
|
# Django 2.0: Python 3.5+
|
||||||
- { env: DJANGO=2.0, python: 3.5 }
|
- { env: TOXENV=django20-py35-all, python: 3.5 }
|
||||||
- { env: DJANGO=2.0, python: 3.6 }
|
- { env: TOXENV=django20-py36-all, python: 3.6 }
|
||||||
- { env: DJANGO=2.0, python: pypy3.5-6.0 }
|
- { env: TOXENV=django20-pypy3-all, python: pypy3.5-6.0 }
|
||||||
# Django 2.1: Python 3.5, 3.6, or 3.7
|
# Django 2.1: Python 3.5, 3.6, or 3.7
|
||||||
- { env: DJANGO=2.1, python: 3.5 }
|
- { env: TOXENV=django21-py35-all, python: 3.5 }
|
||||||
- { env: DJANGO=2.1 RUN_LIVE_TESTS=true, python: 3.6 }
|
- { env: TOXENV=django21-py36-all RUN_LIVE_TESTS=true, python: 3.6 }
|
||||||
- { env: DJANGO=2.1, python: 3.7 }
|
- { env: TOXENV=django21-py37-all, python: 3.7 }
|
||||||
- { env: DJANGO=2.1, python: pypy3.5-6.0 }
|
- { env: TOXENV=django21-pypy3-all, python: pypy3.5-6.0 }
|
||||||
# Django 2.2: Python 3.5, 3.6, or 3.7
|
# Django 2.2: Python 3.5, 3.6, or 3.7
|
||||||
- { env: DJANGO=2.2, python: 3.5 }
|
- { env: TOXENV=django22-py35-all, python: 3.5 }
|
||||||
- { env: DJANGO=2.2, python: 3.6 }
|
- { env: TOXENV=django22-py36-all, python: 3.6 }
|
||||||
- { env: DJANGO=2.2, python: 3.7 }
|
- { env: TOXENV=django22-py37-all, python: 3.7 }
|
||||||
- { env: DJANGO=2.2, python: pypy3.5-6.0 }
|
- { env: TOXENV=django22-pypy3-all, python: pypy3.5-6.0 }
|
||||||
# Django development master (direct from GitHub source):
|
# Django development master (direct from GitHub source):
|
||||||
- { env: DJANGO=master, python: 3.6 }
|
- { env: TOXENV=djangoMaster-py36-all, python: 3.6 }
|
||||||
|
# Install without optional extras (don't need to cover entire matrix)
|
||||||
|
- { env: TOXENV=django21-py37-none, python: 3.7 }
|
||||||
|
- { env: TOXENV=django21-py37-amazon_ses, python: 3.7 }
|
||||||
|
- { env: TOXENV=django21-py37-sparkpost, python: 3.7 }
|
||||||
|
|
||||||
allow_failures:
|
allow_failures:
|
||||||
- env: DJANGO=master
|
- env: TOXENV=djangoMaster-py36-all
|
||||||
python: 3.6
|
python: 3.6
|
||||||
|
|
||||||
cache: pip
|
cache: pip
|
||||||
|
|
||||||
install:
|
install:
|
||||||
- pip install tox-travis
|
# pin tox to avoid https://github.com/tox-dev/tox/issues/1160
|
||||||
|
- pip install tox~=3.6.1
|
||||||
|
|
||||||
script:
|
script:
|
||||||
- tox
|
- tox
|
||||||
|
|||||||
4
Pipfile
4
Pipfile
@@ -14,12 +14,12 @@ six = "*"
|
|||||||
sparkpost = "*"
|
sparkpost = "*"
|
||||||
|
|
||||||
[dev-packages]
|
[dev-packages]
|
||||||
detox = "*"
|
detox = "==0.18"
|
||||||
flake8 = "*"
|
flake8 = "*"
|
||||||
mock = "*"
|
mock = "*"
|
||||||
sphinx = "*"
|
sphinx = "*"
|
||||||
sphinx-rtd-theme = "*"
|
sphinx-rtd-theme = "*"
|
||||||
tox = "*"
|
tox = "~=3.6.1"
|
||||||
twine = "*"
|
twine = "*"
|
||||||
|
|
||||||
[requires]
|
[requires]
|
||||||
|
|||||||
@@ -71,7 +71,7 @@ and Python versions. Tests are run at least once a week, to check whether ESP AP
|
|||||||
and other dependencies have changed out from under Anymail.
|
and other dependencies have changed out from under Anymail.
|
||||||
|
|
||||||
For local development, the recommended test command is
|
For local development, the recommended test command is
|
||||||
:shell:`tox -e django20-py36,django18-py27,lint`, which tests a representative
|
:shell:`tox -e django21-py36-all,django111-py27-all,lint`, which tests a representative
|
||||||
combination of Python and Django versions. It also runs :pypi:`flake8` and other
|
combination of Python and Django versions. It also runs :pypi:`flake8` and other
|
||||||
code-style checkers. Some other test options are covered below, but using this
|
code-style checkers. Some other test options are covered below, but using this
|
||||||
tox command catches most problems, and is a good pre-pull-request check.
|
tox command catches most problems, and is a good pre-pull-request check.
|
||||||
@@ -104,10 +104,10 @@ with those, `pyenv`_ is a helpful way to install and manage multiple Python vers
|
|||||||
.. code-block:: console
|
.. code-block:: console
|
||||||
|
|
||||||
$ pip install tox # (if you haven't already)
|
$ pip install tox # (if you haven't already)
|
||||||
$ tox -e django20-py36,django18-py27,lint # test recommended environments
|
$ tox -e django21-py36-all,django111-py27-all,lint # test recommended environments
|
||||||
|
|
||||||
## you can also run just some test cases, e.g.:
|
## you can also run just some test cases, e.g.:
|
||||||
$ tox -e django20-py36,django18-py27 tests.test_mailgun_backend tests.test_utils
|
$ tox -e django21-py36-all,django111-py27-all tests.test_mailgun_backend tests.test_utils
|
||||||
|
|
||||||
## to test more Python/Django versions:
|
## to test more Python/Django versions:
|
||||||
$ tox # ALL 20+ envs! (grab a coffee, or use `detox` to run tests in parallel)
|
$ tox # ALL 20+ envs! (grab a coffee, or use `detox` to run tests in parallel)
|
||||||
@@ -121,7 +121,7 @@ API keys or other settings. For example:
|
|||||||
|
|
||||||
$ export MAILGUN_TEST_API_KEY='your-Mailgun-API-key'
|
$ export MAILGUN_TEST_API_KEY='your-Mailgun-API-key'
|
||||||
$ export MAILGUN_TEST_DOMAIN='mail.example.com' # sending domain for that API key
|
$ export MAILGUN_TEST_DOMAIN='mail.example.com' # sending domain for that API key
|
||||||
$ tox -e django20-py36 tests.test_mailgun_integration
|
$ tox -e django21-py36-all tests.test_mailgun_integration
|
||||||
|
|
||||||
Check the ``*_integration_tests.py`` files in the `tests source`_ to see which variables
|
Check the ``*_integration_tests.py`` files in the `tests source`_ to see which variables
|
||||||
are required for each ESP. Depending on the supported features, the integration tests for
|
are required for each ESP. Depending on the supported features, the integration tests for
|
||||||
|
|||||||
41
runtests.py
41
runtests.py
@@ -4,7 +4,9 @@
|
|||||||
# or
|
# or
|
||||||
# runtests.py [tests.test_x tests.test_y.SomeTestCase ...]
|
# runtests.py [tests.test_x tests.test_y.SomeTestCase ...]
|
||||||
|
|
||||||
|
from __future__ import print_function
|
||||||
import sys
|
import sys
|
||||||
|
from distutils.util import strtobool
|
||||||
|
|
||||||
import django
|
import django
|
||||||
import os
|
import os
|
||||||
@@ -17,6 +19,18 @@ def setup_and_run_tests(test_labels=None):
|
|||||||
"""Discover and run project tests. Returns number of failures."""
|
"""Discover and run project tests. Returns number of failures."""
|
||||||
test_labels = test_labels or ['tests']
|
test_labels = test_labels or ['tests']
|
||||||
|
|
||||||
|
tags = envlist('ANYMAIL_ONLY_TEST')
|
||||||
|
exclude_tags = envlist('ANYMAIL_SKIP_TESTS')
|
||||||
|
|
||||||
|
# In automated testing, don't run live tests unless specifically requested
|
||||||
|
if envbool('CONTINUOUS_INTEGRATION') and not envbool('RUN_LIVE_TESTS'):
|
||||||
|
exclude_tags.append('live')
|
||||||
|
|
||||||
|
if tags:
|
||||||
|
print("Only running tests tagged: %r" % tags)
|
||||||
|
if exclude_tags:
|
||||||
|
print("Excluding tests tagged: %r" % exclude_tags)
|
||||||
|
|
||||||
warnings.simplefilter('default') # show DeprecationWarning and other default-ignored warnings
|
warnings.simplefilter('default') # show DeprecationWarning and other default-ignored warnings
|
||||||
|
|
||||||
# noinspection PyStringFormat
|
# noinspection PyStringFormat
|
||||||
@@ -25,7 +39,7 @@ def setup_and_run_tests(test_labels=None):
|
|||||||
django.setup()
|
django.setup()
|
||||||
|
|
||||||
TestRunner = get_runner(settings)
|
TestRunner = get_runner(settings)
|
||||||
test_runner = TestRunner(verbosity=1)
|
test_runner = TestRunner(verbosity=1, tags=tags, exclude_tags=exclude_tags)
|
||||||
return test_runner.run_tests(test_labels)
|
return test_runner.run_tests(test_labels)
|
||||||
|
|
||||||
|
|
||||||
@@ -36,5 +50,30 @@ def runtests(test_labels=None):
|
|||||||
sys.exit(bool(failures))
|
sys.exit(bool(failures))
|
||||||
|
|
||||||
|
|
||||||
|
def envbool(var, default=False):
|
||||||
|
"""Returns value of environment variable var as a bool, or default if not set.
|
||||||
|
|
||||||
|
Converts `'true'` to `True`, and `'false'` to `False`.
|
||||||
|
See :func:`~distutils.util.strtobool` for full list of allowable values.
|
||||||
|
"""
|
||||||
|
val = os.getenv(var, None)
|
||||||
|
if val is None:
|
||||||
|
return default
|
||||||
|
else:
|
||||||
|
return strtobool(val)
|
||||||
|
|
||||||
|
|
||||||
|
def envlist(var):
|
||||||
|
"""Returns value of environment variable var split in a comma-separated list.
|
||||||
|
|
||||||
|
Returns an empty list if variable is empty or not set.
|
||||||
|
"""
|
||||||
|
val = os.getenv(var, "").split(',')
|
||||||
|
if val == ['']:
|
||||||
|
# "Splitting an empty string with a specified separator returns ['']"
|
||||||
|
val = []
|
||||||
|
return val
|
||||||
|
|
||||||
|
|
||||||
if __name__ == '__main__':
|
if __name__ == '__main__':
|
||||||
runtests(test_labels=sys.argv[1:])
|
runtests(test_labels=sys.argv[1:])
|
||||||
|
|||||||
@@ -5,13 +5,10 @@ import json
|
|||||||
from datetime import datetime
|
from datetime import datetime
|
||||||
from email.mime.application import MIMEApplication
|
from email.mime.application import MIMEApplication
|
||||||
|
|
||||||
import botocore.config
|
|
||||||
import botocore.exceptions
|
|
||||||
import six
|
import six
|
||||||
from django.core import mail
|
from django.core import mail
|
||||||
from django.core.mail import BadHeaderError
|
from django.core.mail import BadHeaderError
|
||||||
from django.test import SimpleTestCase
|
from django.test import SimpleTestCase, override_settings, tag
|
||||||
from django.test.utils import override_settings
|
|
||||||
from mock import ANY, patch
|
from mock import ANY, patch
|
||||||
|
|
||||||
from anymail.exceptions import AnymailAPIError, AnymailUnsupportedFeature
|
from anymail.exceptions import AnymailAPIError, AnymailUnsupportedFeature
|
||||||
@@ -20,6 +17,7 @@ from anymail.message import attach_inline_image_file, AnymailMessage
|
|||||||
from .utils import AnymailTestMixin, SAMPLE_IMAGE_FILENAME, sample_image_content, sample_image_path
|
from .utils import AnymailTestMixin, SAMPLE_IMAGE_FILENAME, sample_image_content, sample_image_path
|
||||||
|
|
||||||
|
|
||||||
|
@tag('amazon_ses')
|
||||||
@override_settings(EMAIL_BACKEND='anymail.backends.amazon_ses.EmailBackend')
|
@override_settings(EMAIL_BACKEND='anymail.backends.amazon_ses.EmailBackend')
|
||||||
class AmazonSESBackendMockAPITestCase(SimpleTestCase, AnymailTestMixin):
|
class AmazonSESBackendMockAPITestCase(SimpleTestCase, AnymailTestMixin):
|
||||||
"""TestCase that uses the Amazon SES EmailBackend with a mocked boto3 client"""
|
"""TestCase that uses the Amazon SES EmailBackend with a mocked boto3 client"""
|
||||||
@@ -61,8 +59,9 @@ class AmazonSESBackendMockAPITestCase(SimpleTestCase, AnymailTestMixin):
|
|||||||
return mock_operation.return_value
|
return mock_operation.return_value
|
||||||
|
|
||||||
def set_mock_failure(self, response, operation_name="send_raw_email"):
|
def set_mock_failure(self, response, operation_name="send_raw_email"):
|
||||||
|
from botocore.exceptions import ClientError
|
||||||
mock_operation = getattr(self.mock_client_instance, operation_name)
|
mock_operation = getattr(self.mock_client_instance, operation_name)
|
||||||
mock_operation.side_effect = botocore.exceptions.ClientError(response, operation_name=operation_name)
|
mock_operation.side_effect = ClientError(response, operation_name=operation_name)
|
||||||
|
|
||||||
def get_session_params(self):
|
def get_session_params(self):
|
||||||
if self.mock_session.call_args is None:
|
if self.mock_session.call_args is None:
|
||||||
@@ -111,6 +110,7 @@ class AmazonSESBackendMockAPITestCase(SimpleTestCase, AnymailTestMixin):
|
|||||||
raise AssertionError(msg or "ESP API was called and shouldn't have been")
|
raise AssertionError(msg or "ESP API was called and shouldn't have been")
|
||||||
|
|
||||||
|
|
||||||
|
@tag('amazon_ses')
|
||||||
class AmazonSESBackendStandardEmailTests(AmazonSESBackendMockAPITestCase):
|
class AmazonSESBackendStandardEmailTests(AmazonSESBackendMockAPITestCase):
|
||||||
"""Test backend support for Django standard email features"""
|
"""Test backend support for Django standard email features"""
|
||||||
|
|
||||||
@@ -318,6 +318,7 @@ class AmazonSESBackendStandardEmailTests(AmazonSESBackendMockAPITestCase):
|
|||||||
headers={"X-Header": "custom header value\r\ninjected"}).send()
|
headers={"X-Header": "custom header value\r\ninjected"}).send()
|
||||||
|
|
||||||
|
|
||||||
|
@tag('amazon_ses')
|
||||||
class AmazonSESBackendAnymailFeatureTests(AmazonSESBackendMockAPITestCase):
|
class AmazonSESBackendAnymailFeatureTests(AmazonSESBackendMockAPITestCase):
|
||||||
"""Test backend support for Anymail added features"""
|
"""Test backend support for Anymail added features"""
|
||||||
|
|
||||||
@@ -589,6 +590,7 @@ class AmazonSESBackendAnymailFeatureTests(AmazonSESBackendMockAPITestCase):
|
|||||||
self.assertEqual(self.message.anymail_status.esp_response, response_content)
|
self.assertEqual(self.message.anymail_status.esp_response, response_content)
|
||||||
|
|
||||||
|
|
||||||
|
@tag('amazon_ses')
|
||||||
class AmazonSESBackendConfigurationTests(AmazonSESBackendMockAPITestCase):
|
class AmazonSESBackendConfigurationTests(AmazonSESBackendMockAPITestCase):
|
||||||
"""Test configuration options"""
|
"""Test configuration options"""
|
||||||
|
|
||||||
@@ -635,7 +637,8 @@ class AmazonSESBackendConfigurationTests(AmazonSESBackendMockAPITestCase):
|
|||||||
|
|
||||||
def test_client_params_in_connection_init(self):
|
def test_client_params_in_connection_init(self):
|
||||||
"""You can also supply credentials specifically for a particular EmailBackend connection instance"""
|
"""You can also supply credentials specifically for a particular EmailBackend connection instance"""
|
||||||
boto_config = botocore.config.Config(connect_timeout=30)
|
from botocore.config import Config
|
||||||
|
boto_config = Config(connect_timeout=30)
|
||||||
conn = mail.get_connection(
|
conn = mail.get_connection(
|
||||||
'anymail.backends.amazon_ses.EmailBackend',
|
'anymail.backends.amazon_ses.EmailBackend',
|
||||||
client_params={"aws_session_token": "test-session-token", "config": boto_config})
|
client_params={"aws_session_token": "test-session-token", "config": boto_config})
|
||||||
|
|||||||
@@ -5,7 +5,7 @@ from base64 import b64encode
|
|||||||
from datetime import datetime
|
from datetime import datetime
|
||||||
from textwrap import dedent
|
from textwrap import dedent
|
||||||
|
|
||||||
import botocore.exceptions
|
from django.test import tag
|
||||||
from django.utils.timezone import utc
|
from django.utils.timezone import utc
|
||||||
from mock import ANY, patch
|
from mock import ANY, patch
|
||||||
|
|
||||||
@@ -18,6 +18,7 @@ from .test_amazon_ses_webhooks import AmazonSESWebhookTestsMixin
|
|||||||
from .webhook_cases import WebhookTestCase
|
from .webhook_cases import WebhookTestCase
|
||||||
|
|
||||||
|
|
||||||
|
@tag('amazon_ses')
|
||||||
class AmazonSESInboundTests(WebhookTestCase, AmazonSESWebhookTestsMixin):
|
class AmazonSESInboundTests(WebhookTestCase, AmazonSESWebhookTestsMixin):
|
||||||
|
|
||||||
def setUp(self):
|
def setUp(self):
|
||||||
@@ -270,7 +271,8 @@ class AmazonSESInboundTests(WebhookTestCase, AmazonSESWebhookTestsMixin):
|
|||||||
def test_inbound_s3_failure_message(self):
|
def test_inbound_s3_failure_message(self):
|
||||||
"""Issue a helpful error when S3 download fails"""
|
"""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")
|
||||||
self.mock_s3.download_fileobj.side_effect = botocore.exceptions.ClientError(
|
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 = {
|
raw_ses_event = {
|
||||||
@@ -290,7 +292,7 @@ class AmazonSESInboundTests(WebhookTestCase, AmazonSESWebhookTestsMixin):
|
|||||||
"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:
|
) as cm:
|
||||||
self.post_from_sns('/anymail/amazon_ses/inbound/', raw_sns_message)
|
self.post_from_sns('/anymail/amazon_ses/inbound/', raw_sns_message)
|
||||||
self.assertIsInstance(cm.exception, botocore.exceptions.ClientError) # both Boto and Anymail exception class
|
self.assertIsInstance(cm.exception, ClientError) # both Boto and Anymail exception class
|
||||||
self.assertIn("ClientError: An error occurred (403) when calling the HeadObject operation: Forbidden",
|
self.assertIn("ClientError: An error occurred (403) when calling the HeadObject operation: Forbidden",
|
||||||
str(cm.exception)) # original Boto message included
|
str(cm.exception)) # original Boto message included
|
||||||
|
|
||||||
|
|||||||
@@ -5,13 +5,12 @@ import os
|
|||||||
import unittest
|
import unittest
|
||||||
import warnings
|
import warnings
|
||||||
|
|
||||||
from django.test import SimpleTestCase
|
from django.test import SimpleTestCase, override_settings, tag
|
||||||
from django.test.utils import override_settings
|
|
||||||
|
|
||||||
from anymail.exceptions import AnymailAPIError
|
from anymail.exceptions import AnymailAPIError
|
||||||
from anymail.message import AnymailMessage
|
from anymail.message import AnymailMessage
|
||||||
|
|
||||||
from .utils import AnymailTestMixin, sample_image_path, RUN_LIVE_TESTS
|
from .utils import AnymailTestMixin, sample_image_path
|
||||||
|
|
||||||
try:
|
try:
|
||||||
ResourceWarning
|
ResourceWarning
|
||||||
@@ -24,7 +23,6 @@ AMAZON_SES_TEST_SECRET_ACCESS_KEY = os.getenv("AMAZON_SES_TEST_SECRET_ACCESS_KEY
|
|||||||
AMAZON_SES_TEST_REGION_NAME = os.getenv("AMAZON_SES_TEST_REGION_NAME", "us-east-1")
|
AMAZON_SES_TEST_REGION_NAME = os.getenv("AMAZON_SES_TEST_REGION_NAME", "us-east-1")
|
||||||
|
|
||||||
|
|
||||||
@unittest.skipUnless(RUN_LIVE_TESTS, "RUN_LIVE_TESTS disabled in this environment")
|
|
||||||
@unittest.skipUnless(AMAZON_SES_TEST_ACCESS_KEY_ID and AMAZON_SES_TEST_SECRET_ACCESS_KEY,
|
@unittest.skipUnless(AMAZON_SES_TEST_ACCESS_KEY_ID and AMAZON_SES_TEST_SECRET_ACCESS_KEY,
|
||||||
"Set AMAZON_SES_TEST_ACCESS_KEY_ID and AMAZON_SES_TEST_SECRET_ACCESS_KEY "
|
"Set AMAZON_SES_TEST_ACCESS_KEY_ID and AMAZON_SES_TEST_SECRET_ACCESS_KEY "
|
||||||
"environment variables to run Amazon SES integration tests")
|
"environment variables to run Amazon SES integration tests")
|
||||||
@@ -43,6 +41,7 @@ AMAZON_SES_TEST_REGION_NAME = os.getenv("AMAZON_SES_TEST_REGION_NAME", "us-east-
|
|||||||
},
|
},
|
||||||
"AMAZON_SES_CONFIGURATION_SET_NAME": "TestConfigurationSet", # actual config set in Anymail test account
|
"AMAZON_SES_CONFIGURATION_SET_NAME": "TestConfigurationSet", # actual config set in Anymail test account
|
||||||
})
|
})
|
||||||
|
@tag('amazon_ses', 'live')
|
||||||
class AmazonSESBackendIntegrationTests(SimpleTestCase, AnymailTestMixin):
|
class AmazonSESBackendIntegrationTests(SimpleTestCase, AnymailTestMixin):
|
||||||
"""Amazon SES API integration tests
|
"""Amazon SES API integration tests
|
||||||
|
|
||||||
|
|||||||
@@ -2,8 +2,7 @@ import json
|
|||||||
import warnings
|
import warnings
|
||||||
from datetime import datetime
|
from datetime import datetime
|
||||||
|
|
||||||
import botocore.exceptions
|
from django.test import override_settings, tag
|
||||||
from django.test import override_settings
|
|
||||||
from django.utils.timezone import utc
|
from django.utils.timezone import utc
|
||||||
from mock import ANY, patch
|
from mock import ANY, patch
|
||||||
|
|
||||||
@@ -27,6 +26,7 @@ class AmazonSESWebhookTestsMixin(object):
|
|||||||
**kwargs)
|
**kwargs)
|
||||||
|
|
||||||
|
|
||||||
|
@tag('amazon_ses')
|
||||||
class AmazonSESWebhookSecurityTests(WebhookTestCase, AmazonSESWebhookTestsMixin, WebhookBasicAuthTestsMixin):
|
class AmazonSESWebhookSecurityTests(WebhookTestCase, AmazonSESWebhookTestsMixin, WebhookBasicAuthTestsMixin):
|
||||||
def call_webhook(self):
|
def call_webhook(self):
|
||||||
return self.post_from_sns('/anymail/amazon_ses/tracking/',
|
return self.post_from_sns('/anymail/amazon_ses/tracking/',
|
||||||
@@ -43,6 +43,7 @@ class AmazonSESWebhookSecurityTests(WebhookTestCase, AmazonSESWebhookTestsMixin,
|
|||||||
self.assertEqual(response["WWW-Authenticate"], 'Basic realm="Anymail WEBHOOK_SECRET"')
|
self.assertEqual(response["WWW-Authenticate"], 'Basic realm="Anymail WEBHOOK_SECRET"')
|
||||||
|
|
||||||
|
|
||||||
|
@tag('amazon_ses')
|
||||||
class AmazonSESNotificationsTests(WebhookTestCase, AmazonSESWebhookTestsMixin):
|
class AmazonSESNotificationsTests(WebhookTestCase, AmazonSESWebhookTestsMixin):
|
||||||
def test_bounce_event(self):
|
def test_bounce_event(self):
|
||||||
# This test includes a complete Amazon SES example event. (Later tests omit some payload for brevity.)
|
# This test includes a complete Amazon SES example event. (Later tests omit some payload for brevity.)
|
||||||
@@ -404,6 +405,7 @@ class AmazonSESNotificationsTests(WebhookTestCase, AmazonSESWebhookTestsMixin):
|
|||||||
self.post_from_sns('/anymail/amazon_ses/tracking/', raw_sns_message)
|
self.post_from_sns('/anymail/amazon_ses/tracking/', raw_sns_message)
|
||||||
|
|
||||||
|
|
||||||
|
@tag('amazon_ses')
|
||||||
class AmazonSESSubscriptionManagementTests(WebhookTestCase, AmazonSESWebhookTestsMixin):
|
class AmazonSESSubscriptionManagementTests(WebhookTestCase, AmazonSESWebhookTestsMixin):
|
||||||
# Anymail will automatically respond to SNS subscription notifications
|
# Anymail will automatically respond to SNS subscription notifications
|
||||||
# if Anymail is configured to require basic auth via WEBHOOK_SECRET.
|
# if Anymail is configured to require basic auth via WEBHOOK_SECRET.
|
||||||
@@ -450,7 +452,8 @@ class AmazonSESSubscriptionManagementTests(WebhookTestCase, AmazonSESWebhookTest
|
|||||||
|
|
||||||
def test_sns_subscription_confirmation_failure(self):
|
def test_sns_subscription_confirmation_failure(self):
|
||||||
"""Auto-confirmation allows error through if confirm call fails"""
|
"""Auto-confirmation allows error through if confirm call fails"""
|
||||||
self.mock_client_instance.confirm_subscription.side_effect = botocore.exceptions.ClientError({
|
from botocore.exceptions import ClientError
|
||||||
|
self.mock_client_instance.confirm_subscription.side_effect = ClientError({
|
||||||
'Error': {
|
'Error': {
|
||||||
'Type': 'Sender',
|
'Type': 'Sender',
|
||||||
'Code': 'InternalError',
|
'Code': 'InternalError',
|
||||||
@@ -461,7 +464,7 @@ class AmazonSESSubscriptionManagementTests(WebhookTestCase, AmazonSESWebhookTest
|
|||||||
'HTTPStatusCode': 500,
|
'HTTPStatusCode': 500,
|
||||||
}
|
}
|
||||||
}, operation_name="confirm_subscription")
|
}, operation_name="confirm_subscription")
|
||||||
with self.assertRaisesMessage(botocore.exceptions.ClientError, "Gremlins!"):
|
with self.assertRaisesMessage(ClientError, "Gremlins!"):
|
||||||
self.post_from_sns('/anymail/amazon_ses/tracking/', self.SNS_SUBSCRIPTION_CONFIRMATION)
|
self.post_from_sns('/anymail/amazon_ses/tracking/', self.SNS_SUBSCRIPTION_CONFIRMATION)
|
||||||
# didn't notify receivers:
|
# didn't notify receivers:
|
||||||
self.assertEqual(self.tracking_handler.call_count, 0)
|
self.assertEqual(self.tracking_handler.call_count, 0)
|
||||||
|
|||||||
@@ -16,8 +16,7 @@ from email.mime.image import MIMEImage
|
|||||||
|
|
||||||
from django.core import mail
|
from django.core import mail
|
||||||
from django.core.exceptions import ImproperlyConfigured
|
from django.core.exceptions import ImproperlyConfigured
|
||||||
from django.test import SimpleTestCase
|
from django.test import SimpleTestCase, override_settings, tag
|
||||||
from django.test.utils import override_settings
|
|
||||||
from django.utils.timezone import get_fixed_timezone, override as override_current_timezone
|
from django.utils.timezone import get_fixed_timezone, override as override_current_timezone
|
||||||
|
|
||||||
from anymail.exceptions import (
|
from anymail.exceptions import (
|
||||||
@@ -30,6 +29,7 @@ from .utils import (AnymailTestMixin, sample_email_content,
|
|||||||
sample_image_content, sample_image_path, SAMPLE_IMAGE_FILENAME)
|
sample_image_content, sample_image_path, SAMPLE_IMAGE_FILENAME)
|
||||||
|
|
||||||
|
|
||||||
|
@tag('mailgun')
|
||||||
@override_settings(EMAIL_BACKEND='anymail.backends.mailgun.EmailBackend',
|
@override_settings(EMAIL_BACKEND='anymail.backends.mailgun.EmailBackend',
|
||||||
ANYMAIL={'MAILGUN_API_KEY': 'test_api_key'})
|
ANYMAIL={'MAILGUN_API_KEY': 'test_api_key'})
|
||||||
class MailgunBackendMockAPITestCase(RequestsBackendMockAPITestCase):
|
class MailgunBackendMockAPITestCase(RequestsBackendMockAPITestCase):
|
||||||
@@ -44,6 +44,7 @@ class MailgunBackendMockAPITestCase(RequestsBackendMockAPITestCase):
|
|||||||
self.message = mail.EmailMultiAlternatives('Subject', 'Text Body', 'from@example.com', ['to@example.com'])
|
self.message = mail.EmailMultiAlternatives('Subject', 'Text Body', 'from@example.com', ['to@example.com'])
|
||||||
|
|
||||||
|
|
||||||
|
@tag('mailgun')
|
||||||
class MailgunBackendStandardEmailTests(MailgunBackendMockAPITestCase):
|
class MailgunBackendStandardEmailTests(MailgunBackendMockAPITestCase):
|
||||||
"""Test backend support for Django standard email features"""
|
"""Test backend support for Django standard email features"""
|
||||||
|
|
||||||
@@ -374,6 +375,7 @@ class MailgunBackendStandardEmailTests(MailgunBackendMockAPITestCase):
|
|||||||
self.assertEqual(sent, 0)
|
self.assertEqual(sent, 0)
|
||||||
|
|
||||||
|
|
||||||
|
@tag('mailgun')
|
||||||
class MailgunBackendAnymailFeatureTests(MailgunBackendMockAPITestCase):
|
class MailgunBackendAnymailFeatureTests(MailgunBackendMockAPITestCase):
|
||||||
"""Test backend support for Anymail added features"""
|
"""Test backend support for Anymail added features"""
|
||||||
|
|
||||||
@@ -563,6 +565,7 @@ class MailgunBackendAnymailFeatureTests(MailgunBackendMockAPITestCase):
|
|||||||
# (Anything that requests can serialize as a form field will work with Mailgun)
|
# (Anything that requests can serialize as a form field will work with Mailgun)
|
||||||
|
|
||||||
|
|
||||||
|
@tag('mailgun')
|
||||||
class MailgunBackendRecipientsRefusedTests(MailgunBackendMockAPITestCase):
|
class MailgunBackendRecipientsRefusedTests(MailgunBackendMockAPITestCase):
|
||||||
"""Should raise AnymailRecipientsRefused when *all* recipients are rejected or invalid"""
|
"""Should raise AnymailRecipientsRefused when *all* recipients are rejected or invalid"""
|
||||||
|
|
||||||
@@ -594,11 +597,13 @@ class MailgunBackendRecipientsRefusedTests(MailgunBackendMockAPITestCase):
|
|||||||
self.assertEqual(sent, 0)
|
self.assertEqual(sent, 0)
|
||||||
|
|
||||||
|
|
||||||
|
@tag('mailgun')
|
||||||
class MailgunBackendSessionSharingTestCase(SessionSharingTestCasesMixin, MailgunBackendMockAPITestCase):
|
class MailgunBackendSessionSharingTestCase(SessionSharingTestCasesMixin, MailgunBackendMockAPITestCase):
|
||||||
"""Requests session sharing tests"""
|
"""Requests session sharing tests"""
|
||||||
pass # tests are defined in the mixin
|
pass # tests are defined in the mixin
|
||||||
|
|
||||||
|
|
||||||
|
@tag('mailgun')
|
||||||
@override_settings(EMAIL_BACKEND="anymail.backends.mailgun.EmailBackend")
|
@override_settings(EMAIL_BACKEND="anymail.backends.mailgun.EmailBackend")
|
||||||
class MailgunBackendImproperlyConfiguredTests(SimpleTestCase, AnymailTestMixin):
|
class MailgunBackendImproperlyConfiguredTests(SimpleTestCase, AnymailTestMixin):
|
||||||
"""Test ESP backend without required settings in place"""
|
"""Test ESP backend without required settings in place"""
|
||||||
|
|||||||
@@ -3,7 +3,7 @@ from datetime import datetime
|
|||||||
from textwrap import dedent
|
from textwrap import dedent
|
||||||
|
|
||||||
import six
|
import six
|
||||||
from django.test import override_settings
|
from django.test import override_settings, tag
|
||||||
from django.utils.timezone import utc
|
from django.utils.timezone import utc
|
||||||
from mock import ANY
|
from mock import ANY
|
||||||
|
|
||||||
@@ -19,6 +19,7 @@ from .utils import sample_image_content, sample_email_content
|
|||||||
from .webhook_cases import WebhookTestCase
|
from .webhook_cases import WebhookTestCase
|
||||||
|
|
||||||
|
|
||||||
|
@tag('mailgun')
|
||||||
@override_settings(ANYMAIL_MAILGUN_API_KEY=TEST_API_KEY)
|
@override_settings(ANYMAIL_MAILGUN_API_KEY=TEST_API_KEY)
|
||||||
class MailgunInboundTestCase(WebhookTestCase):
|
class MailgunInboundTestCase(WebhookTestCase):
|
||||||
def test_inbound_basics(self):
|
def test_inbound_basics(self):
|
||||||
|
|||||||
@@ -9,19 +9,18 @@ from datetime import datetime, timedelta
|
|||||||
from time import mktime, sleep
|
from time import mktime, sleep
|
||||||
|
|
||||||
import requests
|
import requests
|
||||||
from django.test import SimpleTestCase
|
from django.test import SimpleTestCase, override_settings, tag
|
||||||
from django.test.utils import override_settings
|
|
||||||
|
|
||||||
from anymail.exceptions import AnymailAPIError
|
from anymail.exceptions import AnymailAPIError
|
||||||
from anymail.message import AnymailMessage
|
from anymail.message import AnymailMessage
|
||||||
|
|
||||||
from .utils import AnymailTestMixin, sample_image_path, RUN_LIVE_TESTS
|
from .utils import AnymailTestMixin, sample_image_path
|
||||||
|
|
||||||
MAILGUN_TEST_API_KEY = os.getenv('MAILGUN_TEST_API_KEY')
|
MAILGUN_TEST_API_KEY = os.getenv('MAILGUN_TEST_API_KEY')
|
||||||
MAILGUN_TEST_DOMAIN = os.getenv('MAILGUN_TEST_DOMAIN')
|
MAILGUN_TEST_DOMAIN = os.getenv('MAILGUN_TEST_DOMAIN')
|
||||||
|
|
||||||
|
|
||||||
@unittest.skipUnless(RUN_LIVE_TESTS, "RUN_LIVE_TESTS disabled in this environment")
|
@tag('mailgun', 'live')
|
||||||
@unittest.skipUnless(MAILGUN_TEST_API_KEY and MAILGUN_TEST_DOMAIN,
|
@unittest.skipUnless(MAILGUN_TEST_API_KEY and MAILGUN_TEST_DOMAIN,
|
||||||
"Set MAILGUN_TEST_API_KEY and MAILGUN_TEST_DOMAIN environment variables "
|
"Set MAILGUN_TEST_API_KEY and MAILGUN_TEST_DOMAIN environment variables "
|
||||||
"to run Mailgun integration tests")
|
"to run Mailgun integration tests")
|
||||||
|
|||||||
@@ -4,7 +4,7 @@ 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.test import override_settings
|
from django.test import override_settings, tag
|
||||||
from django.utils.timezone import utc
|
from django.utils.timezone import utc
|
||||||
from mock import ANY
|
from mock import ANY
|
||||||
|
|
||||||
@@ -59,6 +59,7 @@ def querydict_to_postdict(qd):
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@tag('mailgun')
|
||||||
class MailgunWebhookSettingsTestCase(WebhookTestCase):
|
class MailgunWebhookSettingsTestCase(WebhookTestCase):
|
||||||
def test_requires_api_key(self):
|
def test_requires_api_key(self):
|
||||||
with self.assertRaises(ImproperlyConfigured):
|
with self.assertRaises(ImproperlyConfigured):
|
||||||
@@ -66,6 +67,7 @@ class MailgunWebhookSettingsTestCase(WebhookTestCase):
|
|||||||
data=json.dumps(mailgun_sign_payload({'event-data': {'event': 'delivered'}})))
|
data=json.dumps(mailgun_sign_payload({'event-data': {'event': 'delivered'}})))
|
||||||
|
|
||||||
|
|
||||||
|
@tag('mailgun')
|
||||||
@override_settings(ANYMAIL_MAILGUN_API_KEY=TEST_API_KEY)
|
@override_settings(ANYMAIL_MAILGUN_API_KEY=TEST_API_KEY)
|
||||||
class MailgunWebhookSecurityTestCase(WebhookTestCase, WebhookBasicAuthTestsMixin):
|
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
|
||||||
@@ -94,6 +96,7 @@ class MailgunWebhookSecurityTestCase(WebhookTestCase, WebhookBasicAuthTestsMixin
|
|||||||
self.assertEqual(response.status_code, 400)
|
self.assertEqual(response.status_code, 400)
|
||||||
|
|
||||||
|
|
||||||
|
@tag('mailgun')
|
||||||
@override_settings(ANYMAIL_MAILGUN_API_KEY=TEST_API_KEY)
|
@override_settings(ANYMAIL_MAILGUN_API_KEY=TEST_API_KEY)
|
||||||
class MailgunTestCase(WebhookTestCase):
|
class MailgunTestCase(WebhookTestCase):
|
||||||
# Tests for Mailgun's new webhooks (announced 2018-06-29)
|
# Tests for Mailgun's new webhooks (announced 2018-06-29)
|
||||||
@@ -445,6 +448,7 @@ class MailgunTestCase(WebhookTestCase):
|
|||||||
self.assertEqual(event.click_url, "https://example.com/test")
|
self.assertEqual(event.click_url, "https://example.com/test")
|
||||||
|
|
||||||
|
|
||||||
|
@tag('mailgun')
|
||||||
@override_settings(ANYMAIL_MAILGUN_API_KEY=TEST_API_KEY)
|
@override_settings(ANYMAIL_MAILGUN_API_KEY=TEST_API_KEY)
|
||||||
class MailgunLegacyTestCase(WebhookTestCase):
|
class MailgunLegacyTestCase(WebhookTestCase):
|
||||||
# Tests for Mailgun's "legacy" webhooks
|
# Tests for Mailgun's "legacy" webhooks
|
||||||
|
|||||||
@@ -7,8 +7,7 @@ from email.mime.image import MIMEImage
|
|||||||
|
|
||||||
from django.core import mail
|
from django.core import mail
|
||||||
from django.core.exceptions import ImproperlyConfigured
|
from django.core.exceptions import ImproperlyConfigured
|
||||||
from django.test import SimpleTestCase
|
from django.test import SimpleTestCase, override_settings, tag
|
||||||
from django.test.utils import override_settings
|
|
||||||
|
|
||||||
from anymail.exceptions import (AnymailAPIError, AnymailSerializationError,
|
from anymail.exceptions import (AnymailAPIError, AnymailSerializationError,
|
||||||
AnymailUnsupportedFeature,
|
AnymailUnsupportedFeature,
|
||||||
@@ -19,6 +18,7 @@ from .mock_requests_backend import RequestsBackendMockAPITestCase, SessionSharin
|
|||||||
from .utils import sample_image_content, sample_image_path, SAMPLE_IMAGE_FILENAME, AnymailTestMixin, decode_att
|
from .utils import sample_image_content, sample_image_path, SAMPLE_IMAGE_FILENAME, AnymailTestMixin, decode_att
|
||||||
|
|
||||||
|
|
||||||
|
@tag('mailjet')
|
||||||
@override_settings(EMAIL_BACKEND='anymail.backends.mailjet.EmailBackend',
|
@override_settings(EMAIL_BACKEND='anymail.backends.mailjet.EmailBackend',
|
||||||
ANYMAIL={
|
ANYMAIL={
|
||||||
'MAILJET_API_KEY': '',
|
'MAILJET_API_KEY': '',
|
||||||
@@ -64,6 +64,7 @@ class MailjetBackendMockAPITestCase(RequestsBackendMockAPITestCase):
|
|||||||
])
|
])
|
||||||
|
|
||||||
|
|
||||||
|
@tag('mailjet')
|
||||||
class MailjetBackendStandardEmailTests(MailjetBackendMockAPITestCase):
|
class MailjetBackendStandardEmailTests(MailjetBackendMockAPITestCase):
|
||||||
"""Test backend support for Django standard email features"""
|
"""Test backend support for Django standard email features"""
|
||||||
|
|
||||||
@@ -354,6 +355,7 @@ class MailjetBackendStandardEmailTests(MailjetBackendMockAPITestCase):
|
|||||||
self.message.send()
|
self.message.send()
|
||||||
|
|
||||||
|
|
||||||
|
@tag('mailjet')
|
||||||
class MailjetBackendAnymailFeatureTests(MailjetBackendMockAPITestCase):
|
class MailjetBackendAnymailFeatureTests(MailjetBackendMockAPITestCase):
|
||||||
"""Test backend support for Anymail added features"""
|
"""Test backend support for Anymail added features"""
|
||||||
|
|
||||||
@@ -623,11 +625,13 @@ class MailjetBackendAnymailFeatureTests(MailjetBackendMockAPITestCase):
|
|||||||
self.message.send()
|
self.message.send()
|
||||||
|
|
||||||
|
|
||||||
|
@tag('mailjet')
|
||||||
class MailjetBackendSessionSharingTestCase(SessionSharingTestCasesMixin, MailjetBackendMockAPITestCase):
|
class MailjetBackendSessionSharingTestCase(SessionSharingTestCasesMixin, MailjetBackendMockAPITestCase):
|
||||||
"""Requests session sharing tests"""
|
"""Requests session sharing tests"""
|
||||||
pass # tests are defined in the mixin
|
pass # tests are defined in the mixin
|
||||||
|
|
||||||
|
|
||||||
|
@tag('mailjet')
|
||||||
@override_settings(EMAIL_BACKEND="anymail.backends.mailjet.EmailBackend")
|
@override_settings(EMAIL_BACKEND="anymail.backends.mailjet.EmailBackend")
|
||||||
class MailjetBackendImproperlyConfiguredTests(SimpleTestCase, AnymailTestMixin):
|
class MailjetBackendImproperlyConfiguredTests(SimpleTestCase, AnymailTestMixin):
|
||||||
"""Test ESP backend without required settings in place"""
|
"""Test ESP backend without required settings in place"""
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
import json
|
import json
|
||||||
from base64 import b64encode
|
from base64 import b64encode
|
||||||
|
|
||||||
|
from django.test import tag
|
||||||
from mock import ANY
|
from mock import ANY
|
||||||
|
|
||||||
from anymail.inbound import AnymailInboundMessage
|
from anymail.inbound import AnymailInboundMessage
|
||||||
@@ -11,6 +12,7 @@ from .utils import sample_image_content, sample_email_content
|
|||||||
from .webhook_cases import WebhookTestCase
|
from .webhook_cases import WebhookTestCase
|
||||||
|
|
||||||
|
|
||||||
|
@tag('mailjet')
|
||||||
class MailjetInboundTestCase(WebhookTestCase):
|
class MailjetInboundTestCase(WebhookTestCase):
|
||||||
def test_inbound_basics(self):
|
def test_inbound_basics(self):
|
||||||
raw_event = {
|
raw_event = {
|
||||||
|
|||||||
@@ -1,19 +1,18 @@
|
|||||||
import os
|
import os
|
||||||
import unittest
|
import unittest
|
||||||
|
|
||||||
from django.test import SimpleTestCase
|
from django.test import SimpleTestCase, override_settings, tag
|
||||||
from django.test.utils import override_settings
|
|
||||||
|
|
||||||
from anymail.exceptions import AnymailAPIError
|
from anymail.exceptions import AnymailAPIError
|
||||||
from anymail.message import AnymailMessage
|
from anymail.message import AnymailMessage
|
||||||
|
|
||||||
from .utils import AnymailTestMixin, sample_image_path, RUN_LIVE_TESTS
|
from .utils import AnymailTestMixin, sample_image_path
|
||||||
|
|
||||||
MAILJET_TEST_API_KEY = os.getenv('MAILJET_TEST_API_KEY')
|
MAILJET_TEST_API_KEY = os.getenv('MAILJET_TEST_API_KEY')
|
||||||
MAILJET_TEST_SECRET_KEY = os.getenv('MAILJET_TEST_SECRET_KEY')
|
MAILJET_TEST_SECRET_KEY = os.getenv('MAILJET_TEST_SECRET_KEY')
|
||||||
|
|
||||||
|
|
||||||
@unittest.skipUnless(RUN_LIVE_TESTS, "RUN_LIVE_TESTS disabled in this environment")
|
@tag('mailjet', 'live')
|
||||||
@unittest.skipUnless(MAILJET_TEST_API_KEY and MAILJET_TEST_SECRET_KEY,
|
@unittest.skipUnless(MAILJET_TEST_API_KEY and MAILJET_TEST_SECRET_KEY,
|
||||||
"Set MAILJET_TEST_API_KEY and MAILJET_TEST_SECRET_KEY "
|
"Set MAILJET_TEST_API_KEY and MAILJET_TEST_SECRET_KEY "
|
||||||
"environment variables to run Mailjet integration tests")
|
"environment variables to run Mailjet integration tests")
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
import json
|
import json
|
||||||
from datetime import datetime
|
from datetime import datetime
|
||||||
|
|
||||||
|
from django.test import tag
|
||||||
from django.utils.timezone import utc
|
from django.utils.timezone import utc
|
||||||
from mock import ANY
|
from mock import ANY
|
||||||
|
|
||||||
@@ -9,6 +10,7 @@ from anymail.webhooks.mailjet import MailjetTrackingWebhookView
|
|||||||
from .webhook_cases import WebhookBasicAuthTestsMixin, WebhookTestCase
|
from .webhook_cases import WebhookBasicAuthTestsMixin, WebhookTestCase
|
||||||
|
|
||||||
|
|
||||||
|
@tag('mailjet')
|
||||||
class MailjetWebhookSecurityTestCase(WebhookTestCase, WebhookBasicAuthTestsMixin):
|
class MailjetWebhookSecurityTestCase(WebhookTestCase, WebhookBasicAuthTestsMixin):
|
||||||
def call_webhook(self):
|
def call_webhook(self):
|
||||||
return self.client.post('/anymail/mailjet/tracking/',
|
return self.client.post('/anymail/mailjet/tracking/',
|
||||||
@@ -17,6 +19,7 @@ class MailjetWebhookSecurityTestCase(WebhookTestCase, WebhookBasicAuthTestsMixin
|
|||||||
# Actual tests are in WebhookBasicAuthTestsMixin
|
# Actual tests are in WebhookBasicAuthTestsMixin
|
||||||
|
|
||||||
|
|
||||||
|
@tag('mailjet')
|
||||||
class MailjetDeliveryTestCase(WebhookTestCase):
|
class MailjetDeliveryTestCase(WebhookTestCase):
|
||||||
|
|
||||||
def test_sent_event(self):
|
def test_sent_event(self):
|
||||||
|
|||||||
@@ -7,8 +7,7 @@ from email.mime.image import MIMEImage
|
|||||||
|
|
||||||
from django.core import mail
|
from django.core import mail
|
||||||
from django.core.exceptions import ImproperlyConfigured
|
from django.core.exceptions import ImproperlyConfigured
|
||||||
from django.test import SimpleTestCase
|
from django.test import SimpleTestCase, override_settings, tag
|
||||||
from django.test.utils import override_settings
|
|
||||||
from django.utils.timezone import get_fixed_timezone, override as override_current_timezone
|
from django.utils.timezone import get_fixed_timezone, override as override_current_timezone
|
||||||
|
|
||||||
from anymail.exceptions import (AnymailAPIError, AnymailRecipientsRefused,
|
from anymail.exceptions import (AnymailAPIError, AnymailRecipientsRefused,
|
||||||
@@ -19,6 +18,7 @@ from .mock_requests_backend import RequestsBackendMockAPITestCase, SessionSharin
|
|||||||
from .utils import sample_image_content, sample_image_path, SAMPLE_IMAGE_FILENAME, AnymailTestMixin, decode_att
|
from .utils import sample_image_content, sample_image_path, SAMPLE_IMAGE_FILENAME, AnymailTestMixin, decode_att
|
||||||
|
|
||||||
|
|
||||||
|
@tag('mandrill')
|
||||||
@override_settings(EMAIL_BACKEND='anymail.backends.mandrill.EmailBackend',
|
@override_settings(EMAIL_BACKEND='anymail.backends.mandrill.EmailBackend',
|
||||||
ANYMAIL={'MANDRILL_API_KEY': 'test_api_key'})
|
ANYMAIL={'MANDRILL_API_KEY': 'test_api_key'})
|
||||||
class MandrillBackendMockAPITestCase(RequestsBackendMockAPITestCase):
|
class MandrillBackendMockAPITestCase(RequestsBackendMockAPITestCase):
|
||||||
@@ -35,6 +35,7 @@ class MandrillBackendMockAPITestCase(RequestsBackendMockAPITestCase):
|
|||||||
self.message = mail.EmailMultiAlternatives('Subject', 'Text Body', 'from@example.com', ['to@example.com'])
|
self.message = mail.EmailMultiAlternatives('Subject', 'Text Body', 'from@example.com', ['to@example.com'])
|
||||||
|
|
||||||
|
|
||||||
|
@tag('mandrill')
|
||||||
class MandrillBackendStandardEmailTests(MandrillBackendMockAPITestCase):
|
class MandrillBackendStandardEmailTests(MandrillBackendMockAPITestCase):
|
||||||
"""Test backend support for Django mail wrappers"""
|
"""Test backend support for Django mail wrappers"""
|
||||||
|
|
||||||
@@ -267,6 +268,7 @@ class MandrillBackendStandardEmailTests(MandrillBackendMockAPITestCase):
|
|||||||
self.message.send()
|
self.message.send()
|
||||||
|
|
||||||
|
|
||||||
|
@tag('mandrill')
|
||||||
class MandrillBackendAnymailFeatureTests(MandrillBackendMockAPITestCase):
|
class MandrillBackendAnymailFeatureTests(MandrillBackendMockAPITestCase):
|
||||||
"""Test backend support for Anymail added features"""
|
"""Test backend support for Anymail added features"""
|
||||||
|
|
||||||
@@ -534,6 +536,7 @@ class MandrillBackendAnymailFeatureTests(MandrillBackendMockAPITestCase):
|
|||||||
self.assertRegex(str(err), r"Decimal.*is not JSON serializable") # original message
|
self.assertRegex(str(err), r"Decimal.*is not JSON serializable") # original message
|
||||||
|
|
||||||
|
|
||||||
|
@tag('mandrill')
|
||||||
class MandrillBackendRecipientsRefusedTests(MandrillBackendMockAPITestCase):
|
class MandrillBackendRecipientsRefusedTests(MandrillBackendMockAPITestCase):
|
||||||
"""Should raise AnymailRecipientsRefused when *all* recipients are rejected or invalid"""
|
"""Should raise AnymailRecipientsRefused when *all* recipients are rejected or invalid"""
|
||||||
|
|
||||||
@@ -588,11 +591,13 @@ class MandrillBackendRecipientsRefusedTests(MandrillBackendMockAPITestCase):
|
|||||||
self.assertEqual(sent, 1) # refused message is included in sent count
|
self.assertEqual(sent, 1) # refused message is included in sent count
|
||||||
|
|
||||||
|
|
||||||
|
@tag('mandrill')
|
||||||
class MandrillBackendSessionSharingTestCase(SessionSharingTestCasesMixin, MandrillBackendMockAPITestCase):
|
class MandrillBackendSessionSharingTestCase(SessionSharingTestCasesMixin, MandrillBackendMockAPITestCase):
|
||||||
"""Requests session sharing tests"""
|
"""Requests session sharing tests"""
|
||||||
pass # tests are defined in the mixin
|
pass # tests are defined in the mixin
|
||||||
|
|
||||||
|
|
||||||
|
@tag('mandrill')
|
||||||
@override_settings(EMAIL_BACKEND="anymail.backends.mandrill.EmailBackend")
|
@override_settings(EMAIL_BACKEND="anymail.backends.mandrill.EmailBackend")
|
||||||
class MandrillBackendImproperlyConfiguredTests(SimpleTestCase, AnymailTestMixin):
|
class MandrillBackendImproperlyConfiguredTests(SimpleTestCase, AnymailTestMixin):
|
||||||
"""Test backend without required settings"""
|
"""Test backend without required settings"""
|
||||||
|
|||||||
@@ -1,12 +1,13 @@
|
|||||||
from datetime import date
|
from datetime import date
|
||||||
from django.core import mail
|
from django.core import mail
|
||||||
from django.test import override_settings
|
from django.test import override_settings, tag
|
||||||
|
|
||||||
from anymail.exceptions import AnymailSerializationError
|
from anymail.exceptions import AnymailSerializationError
|
||||||
|
|
||||||
from .test_mandrill_backend import MandrillBackendMockAPITestCase
|
from .test_mandrill_backend import MandrillBackendMockAPITestCase
|
||||||
|
|
||||||
|
|
||||||
|
@tag('mandrill')
|
||||||
class MandrillBackendDjrillFeatureTests(MandrillBackendMockAPITestCase):
|
class MandrillBackendDjrillFeatureTests(MandrillBackendMockAPITestCase):
|
||||||
"""Test backend support for deprecated features leftover from Djrill"""
|
"""Test backend support for deprecated features leftover from Djrill"""
|
||||||
|
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
from textwrap import dedent
|
from textwrap import dedent
|
||||||
|
|
||||||
from django.test import override_settings
|
from django.test import override_settings, tag
|
||||||
from mock import ANY
|
from mock import ANY
|
||||||
|
|
||||||
from anymail.inbound import AnymailInboundMessage
|
from anymail.inbound import AnymailInboundMessage
|
||||||
@@ -11,6 +11,7 @@ from .test_mandrill_webhooks import TEST_WEBHOOK_KEY, mandrill_args
|
|||||||
from .webhook_cases import WebhookTestCase
|
from .webhook_cases import WebhookTestCase
|
||||||
|
|
||||||
|
|
||||||
|
@tag('mandrill')
|
||||||
@override_settings(ANYMAIL_MANDRILL_WEBHOOK_KEY=TEST_WEBHOOK_KEY)
|
@override_settings(ANYMAIL_MANDRILL_WEBHOOK_KEY=TEST_WEBHOOK_KEY)
|
||||||
class MandrillInboundTestCase(WebhookTestCase):
|
class MandrillInboundTestCase(WebhookTestCase):
|
||||||
def test_inbound_basics(self):
|
def test_inbound_basics(self):
|
||||||
|
|||||||
@@ -2,18 +2,17 @@ import os
|
|||||||
import unittest
|
import unittest
|
||||||
|
|
||||||
from django.core import mail
|
from django.core import mail
|
||||||
from django.test import SimpleTestCase
|
from django.test import SimpleTestCase, override_settings, tag
|
||||||
from django.test.utils import override_settings
|
|
||||||
|
|
||||||
from anymail.exceptions import AnymailAPIError, AnymailRecipientsRefused
|
from anymail.exceptions import AnymailAPIError, AnymailRecipientsRefused
|
||||||
from anymail.message import AnymailMessage
|
from anymail.message import AnymailMessage
|
||||||
|
|
||||||
from .utils import AnymailTestMixin, sample_image_path, RUN_LIVE_TESTS
|
from .utils import AnymailTestMixin, sample_image_path
|
||||||
|
|
||||||
MANDRILL_TEST_API_KEY = os.getenv('MANDRILL_TEST_API_KEY')
|
MANDRILL_TEST_API_KEY = os.getenv('MANDRILL_TEST_API_KEY')
|
||||||
|
|
||||||
|
|
||||||
@unittest.skipUnless(RUN_LIVE_TESTS, "RUN_LIVE_TESTS disabled in this environment")
|
@tag('mandrill', 'live')
|
||||||
@unittest.skipUnless(MANDRILL_TEST_API_KEY,
|
@unittest.skipUnless(MANDRILL_TEST_API_KEY,
|
||||||
"Set MANDRILL_TEST_API_KEY environment variable to run integration tests")
|
"Set MANDRILL_TEST_API_KEY environment variable to run integration tests")
|
||||||
@override_settings(MANDRILL_API_KEY=MANDRILL_TEST_API_KEY,
|
@override_settings(MANDRILL_API_KEY=MANDRILL_TEST_API_KEY,
|
||||||
|
|||||||
@@ -6,7 +6,7 @@ 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.test import override_settings
|
from django.test import override_settings, tag
|
||||||
from django.utils.timezone import utc
|
from django.utils.timezone import utc
|
||||||
from mock import ANY
|
from mock import ANY
|
||||||
|
|
||||||
@@ -48,6 +48,7 @@ def mandrill_args(events=None,
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@tag('mandrill')
|
||||||
class MandrillWebhookSettingsTestCase(WebhookTestCase):
|
class MandrillWebhookSettingsTestCase(WebhookTestCase):
|
||||||
def test_requires_webhook_key(self):
|
def test_requires_webhook_key(self):
|
||||||
with self.assertRaisesRegex(ImproperlyConfigured, r'MANDRILL_WEBHOOK_KEY'):
|
with self.assertRaisesRegex(ImproperlyConfigured, r'MANDRILL_WEBHOOK_KEY'):
|
||||||
@@ -62,6 +63,7 @@ class MandrillWebhookSettingsTestCase(WebhookTestCase):
|
|||||||
self.assertEqual(response.status_code, 200)
|
self.assertEqual(response.status_code, 200)
|
||||||
|
|
||||||
|
|
||||||
|
@tag('mandrill')
|
||||||
@override_settings(ANYMAIL_MANDRILL_WEBHOOK_KEY=TEST_WEBHOOK_KEY)
|
@override_settings(ANYMAIL_MANDRILL_WEBHOOK_KEY=TEST_WEBHOOK_KEY)
|
||||||
class MandrillWebhookSecurityTestCase(WebhookTestCase, WebhookBasicAuthTestsMixin):
|
class MandrillWebhookSecurityTestCase(WebhookTestCase, WebhookBasicAuthTestsMixin):
|
||||||
should_warn_if_no_auth = False # because we check webhook signature
|
should_warn_if_no_auth = False # because we check webhook signature
|
||||||
@@ -127,6 +129,7 @@ class MandrillWebhookSecurityTestCase(WebhookTestCase, WebhookBasicAuthTestsMixi
|
|||||||
self.assertEqual(response.status_code, 400)
|
self.assertEqual(response.status_code, 400)
|
||||||
|
|
||||||
|
|
||||||
|
@tag('mandrill')
|
||||||
@override_settings(ANYMAIL_MANDRILL_WEBHOOK_KEY=TEST_WEBHOOK_KEY)
|
@override_settings(ANYMAIL_MANDRILL_WEBHOOK_KEY=TEST_WEBHOOK_KEY)
|
||||||
class MandrillTrackingTestCase(WebhookTestCase):
|
class MandrillTrackingTestCase(WebhookTestCase):
|
||||||
|
|
||||||
|
|||||||
@@ -7,8 +7,7 @@ from email.mime.image import MIMEImage
|
|||||||
|
|
||||||
from django.core import mail
|
from django.core import mail
|
||||||
from django.core.exceptions import ImproperlyConfigured
|
from django.core.exceptions import ImproperlyConfigured
|
||||||
from django.test import SimpleTestCase
|
from django.test import SimpleTestCase, override_settings, tag
|
||||||
from django.test.utils import override_settings
|
|
||||||
|
|
||||||
from anymail.exceptions import (
|
from anymail.exceptions import (
|
||||||
AnymailAPIError, AnymailSerializationError,
|
AnymailAPIError, AnymailSerializationError,
|
||||||
@@ -19,6 +18,7 @@ from .mock_requests_backend import RequestsBackendMockAPITestCase, SessionSharin
|
|||||||
from .utils import sample_image_content, sample_image_path, SAMPLE_IMAGE_FILENAME, AnymailTestMixin, decode_att
|
from .utils import sample_image_content, sample_image_path, SAMPLE_IMAGE_FILENAME, AnymailTestMixin, decode_att
|
||||||
|
|
||||||
|
|
||||||
|
@tag('postmark')
|
||||||
@override_settings(EMAIL_BACKEND='anymail.backends.postmark.EmailBackend',
|
@override_settings(EMAIL_BACKEND='anymail.backends.postmark.EmailBackend',
|
||||||
ANYMAIL={'POSTMARK_SERVER_TOKEN': 'test_server_token'})
|
ANYMAIL={'POSTMARK_SERVER_TOKEN': 'test_server_token'})
|
||||||
class PostmarkBackendMockAPITestCase(RequestsBackendMockAPITestCase):
|
class PostmarkBackendMockAPITestCase(RequestsBackendMockAPITestCase):
|
||||||
@@ -36,6 +36,7 @@ class PostmarkBackendMockAPITestCase(RequestsBackendMockAPITestCase):
|
|||||||
self.message = mail.EmailMultiAlternatives('Subject', 'Text Body', 'from@example.com', ['to@example.com'])
|
self.message = mail.EmailMultiAlternatives('Subject', 'Text Body', 'from@example.com', ['to@example.com'])
|
||||||
|
|
||||||
|
|
||||||
|
@tag('postmark')
|
||||||
class PostmarkBackendStandardEmailTests(PostmarkBackendMockAPITestCase):
|
class PostmarkBackendStandardEmailTests(PostmarkBackendMockAPITestCase):
|
||||||
"""Test backend support for Django standard email features"""
|
"""Test backend support for Django standard email features"""
|
||||||
|
|
||||||
@@ -318,6 +319,7 @@ class PostmarkBackendStandardEmailTests(PostmarkBackendMockAPITestCase):
|
|||||||
self.message.send()
|
self.message.send()
|
||||||
|
|
||||||
|
|
||||||
|
@tag('postmark')
|
||||||
class PostmarkBackendAnymailFeatureTests(PostmarkBackendMockAPITestCase):
|
class PostmarkBackendAnymailFeatureTests(PostmarkBackendMockAPITestCase):
|
||||||
"""Test backend support for Anymail added features"""
|
"""Test backend support for Anymail added features"""
|
||||||
|
|
||||||
@@ -605,6 +607,7 @@ class PostmarkBackendAnymailFeatureTests(PostmarkBackendMockAPITestCase):
|
|||||||
self.assertRegex(str(err), r"Decimal.*is not JSON serializable") # original message
|
self.assertRegex(str(err), r"Decimal.*is not JSON serializable") # original message
|
||||||
|
|
||||||
|
|
||||||
|
@tag('postmark')
|
||||||
class PostmarkBackendRecipientsRefusedTests(PostmarkBackendMockAPITestCase):
|
class PostmarkBackendRecipientsRefusedTests(PostmarkBackendMockAPITestCase):
|
||||||
"""Should raise AnymailRecipientsRefused when *all* recipients are rejected or invalid"""
|
"""Should raise AnymailRecipientsRefused when *all* recipients are rejected or invalid"""
|
||||||
|
|
||||||
@@ -699,11 +702,13 @@ class PostmarkBackendRecipientsRefusedTests(PostmarkBackendMockAPITestCase):
|
|||||||
self.assertEqual(status.recipients['spam@example.com'].status, 'rejected')
|
self.assertEqual(status.recipients['spam@example.com'].status, 'rejected')
|
||||||
|
|
||||||
|
|
||||||
|
@tag('postmark')
|
||||||
class PostmarkBackendSessionSharingTestCase(SessionSharingTestCasesMixin, PostmarkBackendMockAPITestCase):
|
class PostmarkBackendSessionSharingTestCase(SessionSharingTestCasesMixin, PostmarkBackendMockAPITestCase):
|
||||||
"""Requests session sharing tests"""
|
"""Requests session sharing tests"""
|
||||||
pass # tests are defined in the mixin
|
pass # tests are defined in the mixin
|
||||||
|
|
||||||
|
|
||||||
|
@tag('postmark')
|
||||||
@override_settings(EMAIL_BACKEND="anymail.backends.postmark.EmailBackend")
|
@override_settings(EMAIL_BACKEND="anymail.backends.postmark.EmailBackend")
|
||||||
class PostmarkBackendImproperlyConfiguredTests(SimpleTestCase, AnymailTestMixin):
|
class PostmarkBackendImproperlyConfiguredTests(SimpleTestCase, AnymailTestMixin):
|
||||||
"""Test ESP backend without required settings in place"""
|
"""Test ESP backend without required settings in place"""
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
import json
|
import json
|
||||||
from base64 import b64encode
|
from base64 import b64encode
|
||||||
|
|
||||||
|
from django.test import tag
|
||||||
from mock import ANY
|
from mock import ANY
|
||||||
|
|
||||||
from anymail.exceptions import AnymailConfigurationError
|
from anymail.exceptions import AnymailConfigurationError
|
||||||
@@ -12,6 +13,7 @@ from .utils import sample_image_content, sample_email_content
|
|||||||
from .webhook_cases import WebhookTestCase
|
from .webhook_cases import WebhookTestCase
|
||||||
|
|
||||||
|
|
||||||
|
@tag('postmark')
|
||||||
class PostmarkInboundTestCase(WebhookTestCase):
|
class PostmarkInboundTestCase(WebhookTestCase):
|
||||||
def test_inbound_basics(self):
|
def test_inbound_basics(self):
|
||||||
raw_event = {
|
raw_event = {
|
||||||
|
|||||||
@@ -1,13 +1,12 @@
|
|||||||
import os
|
import os
|
||||||
import unittest
|
import unittest
|
||||||
|
|
||||||
from django.test import SimpleTestCase
|
from django.test import SimpleTestCase, override_settings, tag
|
||||||
from django.test.utils import override_settings
|
|
||||||
|
|
||||||
from anymail.exceptions import AnymailAPIError
|
from anymail.exceptions import AnymailAPIError
|
||||||
from anymail.message import AnymailMessage
|
from anymail.message import AnymailMessage
|
||||||
|
|
||||||
from .utils import AnymailTestMixin, sample_image_path, RUN_LIVE_TESTS
|
from .utils import AnymailTestMixin, sample_image_path
|
||||||
|
|
||||||
|
|
||||||
# For most integration tests, Postmark's sandboxed "POSTMARK_API_TEST" token is used.
|
# For most integration tests, Postmark's sandboxed "POSTMARK_API_TEST" token is used.
|
||||||
@@ -16,7 +15,7 @@ POSTMARK_TEST_SERVER_TOKEN = os.getenv('POSTMARK_TEST_SERVER_TOKEN')
|
|||||||
POSTMARK_TEST_TEMPLATE_ID = os.getenv('POSTMARK_TEST_TEMPLATE_ID')
|
POSTMARK_TEST_TEMPLATE_ID = os.getenv('POSTMARK_TEST_TEMPLATE_ID')
|
||||||
|
|
||||||
|
|
||||||
@unittest.skipUnless(RUN_LIVE_TESTS, "RUN_LIVE_TESTS disabled in this environment")
|
@tag('postmark', 'live')
|
||||||
@override_settings(ANYMAIL_POSTMARK_SERVER_TOKEN="POSTMARK_API_TEST",
|
@override_settings(ANYMAIL_POSTMARK_SERVER_TOKEN="POSTMARK_API_TEST",
|
||||||
EMAIL_BACKEND="anymail.backends.postmark.EmailBackend")
|
EMAIL_BACKEND="anymail.backends.postmark.EmailBackend")
|
||||||
class PostmarkBackendIntegrationTests(SimpleTestCase, AnymailTestMixin):
|
class PostmarkBackendIntegrationTests(SimpleTestCase, AnymailTestMixin):
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
import json
|
import json
|
||||||
from datetime import datetime
|
from datetime import datetime
|
||||||
|
|
||||||
|
from django.test import tag
|
||||||
from django.utils.timezone import get_fixed_timezone, utc
|
from django.utils.timezone import get_fixed_timezone, utc
|
||||||
from mock import ANY
|
from mock import ANY
|
||||||
|
|
||||||
@@ -10,6 +11,7 @@ from anymail.webhooks.postmark import PostmarkTrackingWebhookView
|
|||||||
from .webhook_cases import WebhookBasicAuthTestsMixin, WebhookTestCase
|
from .webhook_cases import WebhookBasicAuthTestsMixin, WebhookTestCase
|
||||||
|
|
||||||
|
|
||||||
|
@tag('postmark')
|
||||||
class PostmarkWebhookSecurityTestCase(WebhookTestCase, WebhookBasicAuthTestsMixin):
|
class PostmarkWebhookSecurityTestCase(WebhookTestCase, WebhookBasicAuthTestsMixin):
|
||||||
def call_webhook(self):
|
def call_webhook(self):
|
||||||
return self.client.post('/anymail/postmark/tracking/',
|
return self.client.post('/anymail/postmark/tracking/',
|
||||||
@@ -18,6 +20,7 @@ class PostmarkWebhookSecurityTestCase(WebhookTestCase, WebhookBasicAuthTestsMixi
|
|||||||
# Actual tests are in WebhookBasicAuthTestsMixin
|
# Actual tests are in WebhookBasicAuthTestsMixin
|
||||||
|
|
||||||
|
|
||||||
|
@tag('postmark')
|
||||||
class PostmarkDeliveryTestCase(WebhookTestCase):
|
class PostmarkDeliveryTestCase(WebhookTestCase):
|
||||||
def test_bounce_event(self):
|
def test_bounce_event(self):
|
||||||
raw_event = {
|
raw_event = {
|
||||||
|
|||||||
@@ -9,8 +9,7 @@ from email.mime.image import MIMEImage
|
|||||||
|
|
||||||
import six
|
import six
|
||||||
from django.core import mail
|
from django.core import mail
|
||||||
from django.test import SimpleTestCase
|
from django.test import SimpleTestCase, override_settings, tag
|
||||||
from django.test.utils import override_settings
|
|
||||||
from django.utils.timezone import get_fixed_timezone, override as override_current_timezone
|
from django.utils.timezone import get_fixed_timezone, override as override_current_timezone
|
||||||
|
|
||||||
from anymail.exceptions import (AnymailAPIError, AnymailConfigurationError, AnymailSerializationError,
|
from anymail.exceptions import (AnymailAPIError, AnymailConfigurationError, AnymailSerializationError,
|
||||||
@@ -24,6 +23,7 @@ from .utils import sample_image_content, sample_image_path, SAMPLE_IMAGE_FILENAM
|
|||||||
longtype = int if six.PY3 else long # NOQA: F821
|
longtype = int if six.PY3 else long # NOQA: F821
|
||||||
|
|
||||||
|
|
||||||
|
@tag('sendgrid')
|
||||||
@override_settings(EMAIL_BACKEND='anymail.backends.sendgrid.EmailBackend',
|
@override_settings(EMAIL_BACKEND='anymail.backends.sendgrid.EmailBackend',
|
||||||
ANYMAIL={'SENDGRID_API_KEY': 'test_api_key'})
|
ANYMAIL={'SENDGRID_API_KEY': 'test_api_key'})
|
||||||
class SendGridBackendMockAPITestCase(RequestsBackendMockAPITestCase):
|
class SendGridBackendMockAPITestCase(RequestsBackendMockAPITestCase):
|
||||||
@@ -36,6 +36,7 @@ class SendGridBackendMockAPITestCase(RequestsBackendMockAPITestCase):
|
|||||||
self.message = mail.EmailMultiAlternatives('Subject', 'Text Body', 'from@example.com', ['to@example.com'])
|
self.message = mail.EmailMultiAlternatives('Subject', 'Text Body', 'from@example.com', ['to@example.com'])
|
||||||
|
|
||||||
|
|
||||||
|
@tag('sendgrid')
|
||||||
class SendGridBackendStandardEmailTests(SendGridBackendMockAPITestCase):
|
class SendGridBackendStandardEmailTests(SendGridBackendMockAPITestCase):
|
||||||
"""Test backend support for Django standard email features"""
|
"""Test backend support for Django standard email features"""
|
||||||
|
|
||||||
@@ -328,6 +329,7 @@ class SendGridBackendStandardEmailTests(SendGridBackendMockAPITestCase):
|
|||||||
self.message.send()
|
self.message.send()
|
||||||
|
|
||||||
|
|
||||||
|
@tag('sendgrid')
|
||||||
class SendGridBackendAnymailFeatureTests(SendGridBackendMockAPITestCase):
|
class SendGridBackendAnymailFeatureTests(SendGridBackendMockAPITestCase):
|
||||||
"""Test backend support for Anymail added features"""
|
"""Test backend support for Anymail added features"""
|
||||||
|
|
||||||
@@ -709,6 +711,7 @@ class SendGridBackendAnymailFeatureTests(SendGridBackendMockAPITestCase):
|
|||||||
{"email": "from@example.com", "name": "Sender, Inc."})
|
{"email": "from@example.com", "name": "Sender, Inc."})
|
||||||
|
|
||||||
|
|
||||||
|
@tag('sendgrid')
|
||||||
class SendGridBackendRecipientsRefusedTests(SendGridBackendMockAPITestCase):
|
class SendGridBackendRecipientsRefusedTests(SendGridBackendMockAPITestCase):
|
||||||
"""Should raise AnymailRecipientsRefused when *all* recipients are rejected or invalid"""
|
"""Should raise AnymailRecipientsRefused when *all* recipients are rejected or invalid"""
|
||||||
|
|
||||||
@@ -718,11 +721,13 @@ class SendGridBackendRecipientsRefusedTests(SendGridBackendMockAPITestCase):
|
|||||||
pass # not applicable to this backend
|
pass # not applicable to this backend
|
||||||
|
|
||||||
|
|
||||||
|
@tag('sendgrid')
|
||||||
class SendGridBackendSessionSharingTestCase(SessionSharingTestCasesMixin, SendGridBackendMockAPITestCase):
|
class SendGridBackendSessionSharingTestCase(SessionSharingTestCasesMixin, SendGridBackendMockAPITestCase):
|
||||||
"""Requests session sharing tests"""
|
"""Requests session sharing tests"""
|
||||||
pass # tests are defined in the mixin
|
pass # tests are defined in the mixin
|
||||||
|
|
||||||
|
|
||||||
|
@tag('sendgrid')
|
||||||
@override_settings(EMAIL_BACKEND="anymail.backends.sendgrid.EmailBackend")
|
@override_settings(EMAIL_BACKEND="anymail.backends.sendgrid.EmailBackend")
|
||||||
class SendGridBackendImproperlyConfiguredTests(SimpleTestCase, AnymailTestMixin):
|
class SendGridBackendImproperlyConfiguredTests(SimpleTestCase, AnymailTestMixin):
|
||||||
"""Test ESP backend without required settings in place"""
|
"""Test ESP backend without required settings in place"""
|
||||||
@@ -732,6 +737,7 @@ class SendGridBackendImproperlyConfiguredTests(SimpleTestCase, AnymailTestMixin)
|
|||||||
mail.send_mail('Subject', 'Message', 'from@example.com', ['to@example.com'])
|
mail.send_mail('Subject', 'Message', 'from@example.com', ['to@example.com'])
|
||||||
|
|
||||||
|
|
||||||
|
@tag('sendgrid')
|
||||||
@override_settings(EMAIL_BACKEND="anymail.backends.sendgrid.EmailBackend")
|
@override_settings(EMAIL_BACKEND="anymail.backends.sendgrid.EmailBackend")
|
||||||
class SendGridBackendDisallowsV2Tests(SimpleTestCase, AnymailTestMixin):
|
class SendGridBackendDisallowsV2Tests(SimpleTestCase, AnymailTestMixin):
|
||||||
"""Using v2-API-only features should cause errors with v3 backend"""
|
"""Using v2-API-only features should cause errors with v3 backend"""
|
||||||
|
|||||||
@@ -2,6 +2,7 @@ import json
|
|||||||
from textwrap import dedent
|
from textwrap import dedent
|
||||||
|
|
||||||
import six
|
import six
|
||||||
|
from django.test import tag
|
||||||
from mock import ANY
|
from mock import ANY
|
||||||
|
|
||||||
from anymail.inbound import AnymailInboundMessage
|
from anymail.inbound import AnymailInboundMessage
|
||||||
@@ -12,6 +13,7 @@ from .utils import sample_image_content, sample_email_content
|
|||||||
from .webhook_cases import WebhookTestCase
|
from .webhook_cases import WebhookTestCase
|
||||||
|
|
||||||
|
|
||||||
|
@tag('sendgrid')
|
||||||
class SendgridInboundTestCase(WebhookTestCase):
|
class SendgridInboundTestCase(WebhookTestCase):
|
||||||
def test_inbound_basics(self):
|
def test_inbound_basics(self):
|
||||||
raw_event = {
|
raw_event = {
|
||||||
|
|||||||
@@ -2,19 +2,18 @@ import os
|
|||||||
import unittest
|
import unittest
|
||||||
from datetime import datetime, timedelta
|
from datetime import datetime, timedelta
|
||||||
|
|
||||||
from django.test import SimpleTestCase
|
from django.test import SimpleTestCase, override_settings, tag
|
||||||
from django.test.utils import override_settings
|
|
||||||
|
|
||||||
from anymail.exceptions import AnymailAPIError
|
from anymail.exceptions import AnymailAPIError
|
||||||
from anymail.message import AnymailMessage
|
from anymail.message import AnymailMessage
|
||||||
|
|
||||||
from .utils import AnymailTestMixin, sample_image_path, RUN_LIVE_TESTS
|
from .utils import AnymailTestMixin, sample_image_path
|
||||||
|
|
||||||
SENDGRID_TEST_API_KEY = os.getenv('SENDGRID_TEST_API_KEY')
|
SENDGRID_TEST_API_KEY = os.getenv('SENDGRID_TEST_API_KEY')
|
||||||
SENDGRID_TEST_TEMPLATE_ID = os.getenv('SENDGRID_TEST_TEMPLATE_ID')
|
SENDGRID_TEST_TEMPLATE_ID = os.getenv('SENDGRID_TEST_TEMPLATE_ID')
|
||||||
|
|
||||||
|
|
||||||
@unittest.skipUnless(RUN_LIVE_TESTS, "RUN_LIVE_TESTS disabled in this environment")
|
@tag('sendgrid', 'live')
|
||||||
@unittest.skipUnless(SENDGRID_TEST_API_KEY,
|
@unittest.skipUnless(SENDGRID_TEST_API_KEY,
|
||||||
"Set SENDGRID_TEST_API_KEY environment variable "
|
"Set SENDGRID_TEST_API_KEY environment variable "
|
||||||
"to run SendGrid integration tests")
|
"to run SendGrid integration tests")
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
import json
|
import json
|
||||||
from datetime import datetime
|
from datetime import datetime
|
||||||
|
|
||||||
|
from django.test import tag
|
||||||
from django.utils.timezone import utc
|
from django.utils.timezone import utc
|
||||||
from mock import ANY
|
from mock import ANY
|
||||||
|
|
||||||
@@ -9,6 +10,7 @@ from anymail.webhooks.sendgrid import SendGridTrackingWebhookView
|
|||||||
from .webhook_cases import WebhookBasicAuthTestsMixin, WebhookTestCase
|
from .webhook_cases import WebhookBasicAuthTestsMixin, WebhookTestCase
|
||||||
|
|
||||||
|
|
||||||
|
@tag('sendgrid')
|
||||||
class SendGridWebhookSecurityTestCase(WebhookTestCase, WebhookBasicAuthTestsMixin):
|
class SendGridWebhookSecurityTestCase(WebhookTestCase, WebhookBasicAuthTestsMixin):
|
||||||
def call_webhook(self):
|
def call_webhook(self):
|
||||||
return self.client.post('/anymail/sendgrid/tracking/',
|
return self.client.post('/anymail/sendgrid/tracking/',
|
||||||
@@ -17,6 +19,7 @@ class SendGridWebhookSecurityTestCase(WebhookTestCase, WebhookBasicAuthTestsMixi
|
|||||||
# Actual tests are in WebhookBasicAuthTestsMixin
|
# Actual tests are in WebhookBasicAuthTestsMixin
|
||||||
|
|
||||||
|
|
||||||
|
@tag('sendgrid')
|
||||||
class SendGridDeliveryTestCase(WebhookTestCase):
|
class SendGridDeliveryTestCase(WebhookTestCase):
|
||||||
|
|
||||||
def test_processed_event(self):
|
def test_processed_event(self):
|
||||||
|
|||||||
@@ -10,8 +10,7 @@ from email.mime.image import MIMEImage
|
|||||||
|
|
||||||
import six
|
import six
|
||||||
from django.core import mail
|
from django.core import mail
|
||||||
from django.test import SimpleTestCase
|
from django.test import SimpleTestCase, override_settings, tag
|
||||||
from django.test.utils import override_settings
|
|
||||||
from django.utils.timezone import get_fixed_timezone, override as override_current_timezone
|
from django.utils.timezone import get_fixed_timezone, override as override_current_timezone
|
||||||
|
|
||||||
from anymail.exceptions import (AnymailAPIError, AnymailConfigurationError, AnymailSerializationError,
|
from anymail.exceptions import (AnymailAPIError, AnymailConfigurationError, AnymailSerializationError,
|
||||||
@@ -25,6 +24,7 @@ from .utils import sample_image_content, sample_image_path, SAMPLE_IMAGE_FILENAM
|
|||||||
longtype = int if six.PY3 else long # NOQA: F821
|
longtype = int if six.PY3 else long # NOQA: F821
|
||||||
|
|
||||||
|
|
||||||
|
@tag('sendinblue')
|
||||||
@override_settings(EMAIL_BACKEND='anymail.backends.sendinblue.EmailBackend',
|
@override_settings(EMAIL_BACKEND='anymail.backends.sendinblue.EmailBackend',
|
||||||
ANYMAIL={'SENDINBLUE_API_KEY': 'test_api_key'})
|
ANYMAIL={'SENDINBLUE_API_KEY': 'test_api_key'})
|
||||||
class SendinBlueBackendMockAPITestCase(RequestsBackendMockAPITestCase):
|
class SendinBlueBackendMockAPITestCase(RequestsBackendMockAPITestCase):
|
||||||
@@ -38,6 +38,7 @@ class SendinBlueBackendMockAPITestCase(RequestsBackendMockAPITestCase):
|
|||||||
self.message = mail.EmailMultiAlternatives('Subject', 'Text Body', 'from@example.com', ['to@example.com'])
|
self.message = mail.EmailMultiAlternatives('Subject', 'Text Body', 'from@example.com', ['to@example.com'])
|
||||||
|
|
||||||
|
|
||||||
|
@tag('sendinblue')
|
||||||
class SendinBlueBackendStandardEmailTests(SendinBlueBackendMockAPITestCase):
|
class SendinBlueBackendStandardEmailTests(SendinBlueBackendMockAPITestCase):
|
||||||
"""Test backend support for Django standard email features"""
|
"""Test backend support for Django standard email features"""
|
||||||
|
|
||||||
@@ -274,6 +275,7 @@ class SendinBlueBackendStandardEmailTests(SendinBlueBackendMockAPITestCase):
|
|||||||
self.message.send()
|
self.message.send()
|
||||||
|
|
||||||
|
|
||||||
|
@tag('sendinblue')
|
||||||
class SendinBlueBackendAnymailFeatureTests(SendinBlueBackendMockAPITestCase):
|
class SendinBlueBackendAnymailFeatureTests(SendinBlueBackendMockAPITestCase):
|
||||||
"""Test backend support for Anymail added features"""
|
"""Test backend support for Anymail added features"""
|
||||||
|
|
||||||
@@ -510,6 +512,7 @@ class SendinBlueBackendAnymailFeatureTests(SendinBlueBackendMockAPITestCase):
|
|||||||
self.assertRegex(str(err), r"Decimal.*is not JSON serializable") # original message
|
self.assertRegex(str(err), r"Decimal.*is not JSON serializable") # original message
|
||||||
|
|
||||||
|
|
||||||
|
@tag('sendinblue')
|
||||||
class SendinBlueBackendRecipientsRefusedTests(SendinBlueBackendMockAPITestCase):
|
class SendinBlueBackendRecipientsRefusedTests(SendinBlueBackendMockAPITestCase):
|
||||||
"""Should raise AnymailRecipientsRefused when *all* recipients are rejected or invalid"""
|
"""Should raise AnymailRecipientsRefused when *all* recipients are rejected or invalid"""
|
||||||
|
|
||||||
@@ -519,11 +522,13 @@ class SendinBlueBackendRecipientsRefusedTests(SendinBlueBackendMockAPITestCase):
|
|||||||
pass # not applicable to this backend
|
pass # not applicable to this backend
|
||||||
|
|
||||||
|
|
||||||
|
@tag('sendinblue')
|
||||||
class SendinBlueBackendSessionSharingTestCase(SessionSharingTestCasesMixin, SendinBlueBackendMockAPITestCase):
|
class SendinBlueBackendSessionSharingTestCase(SessionSharingTestCasesMixin, SendinBlueBackendMockAPITestCase):
|
||||||
"""Requests session sharing tests"""
|
"""Requests session sharing tests"""
|
||||||
pass # tests are defined in the mixin
|
pass # tests are defined in the mixin
|
||||||
|
|
||||||
|
|
||||||
|
@tag('sendinblue')
|
||||||
@override_settings(EMAIL_BACKEND="anymail.backends.sendinblue.EmailBackend")
|
@override_settings(EMAIL_BACKEND="anymail.backends.sendinblue.EmailBackend")
|
||||||
class SendinBlueBackendImproperlyConfiguredTests(SimpleTestCase, AnymailTestMixin):
|
class SendinBlueBackendImproperlyConfiguredTests(SimpleTestCase, AnymailTestMixin):
|
||||||
"""Test ESP backend without required settings in place"""
|
"""Test ESP backend without required settings in place"""
|
||||||
|
|||||||
@@ -1,18 +1,17 @@
|
|||||||
import os
|
import os
|
||||||
import unittest
|
import unittest
|
||||||
|
|
||||||
from django.test import SimpleTestCase
|
from django.test import SimpleTestCase, override_settings, tag
|
||||||
from django.test.utils import override_settings
|
|
||||||
|
|
||||||
from anymail.exceptions import AnymailAPIError
|
from anymail.exceptions import AnymailAPIError
|
||||||
from anymail.message import AnymailMessage
|
from anymail.message import AnymailMessage
|
||||||
|
|
||||||
from .utils import AnymailTestMixin, RUN_LIVE_TESTS
|
from .utils import AnymailTestMixin
|
||||||
|
|
||||||
SENDINBLUE_TEST_API_KEY = os.getenv('SENDINBLUE_TEST_API_KEY')
|
SENDINBLUE_TEST_API_KEY = os.getenv('SENDINBLUE_TEST_API_KEY')
|
||||||
|
|
||||||
|
|
||||||
@unittest.skipUnless(RUN_LIVE_TESTS, "RUN_LIVE_TESTS disabled in this environment")
|
@tag('sendinblue', 'live')
|
||||||
@unittest.skipUnless(SENDINBLUE_TEST_API_KEY,
|
@unittest.skipUnless(SENDINBLUE_TEST_API_KEY,
|
||||||
"Set SENDINBLUE_TEST_API_KEY environment variable "
|
"Set SENDINBLUE_TEST_API_KEY environment variable "
|
||||||
"to run SendinBlue integration tests")
|
"to run SendinBlue integration tests")
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
import json
|
import json
|
||||||
from datetime import datetime
|
from datetime import datetime
|
||||||
|
|
||||||
|
from django.test import tag
|
||||||
from django.utils.timezone import utc
|
from django.utils.timezone import utc
|
||||||
from mock import ANY
|
from mock import ANY
|
||||||
|
|
||||||
@@ -9,6 +10,7 @@ from anymail.webhooks.sendinblue import SendinBlueTrackingWebhookView
|
|||||||
from .webhook_cases import WebhookBasicAuthTestsMixin, WebhookTestCase
|
from .webhook_cases import WebhookBasicAuthTestsMixin, WebhookTestCase
|
||||||
|
|
||||||
|
|
||||||
|
@tag('sendinblue')
|
||||||
class SendinBlueWebhookSecurityTestCase(WebhookTestCase, WebhookBasicAuthTestsMixin):
|
class SendinBlueWebhookSecurityTestCase(WebhookTestCase, WebhookBasicAuthTestsMixin):
|
||||||
def call_webhook(self):
|
def call_webhook(self):
|
||||||
return self.client.post('/anymail/sendinblue/tracking/',
|
return self.client.post('/anymail/sendinblue/tracking/',
|
||||||
@@ -17,6 +19,7 @@ class SendinBlueWebhookSecurityTestCase(WebhookTestCase, WebhookBasicAuthTestsMi
|
|||||||
# Actual tests are in WebhookBasicAuthTestsMixin
|
# Actual tests are in WebhookBasicAuthTestsMixin
|
||||||
|
|
||||||
|
|
||||||
|
@tag('sendinblue')
|
||||||
class SendinBlueDeliveryTestCase(WebhookTestCase):
|
class SendinBlueDeliveryTestCase(WebhookTestCase):
|
||||||
# SendinBlue's webhook payload data doesn't seem to be documented anywhere.
|
# SendinBlue's webhook payload data doesn't seem to be documented anywhere.
|
||||||
# There's a list of webhook events at https://apidocs.sendinblue.com/webhooks/#3.
|
# There's a list of webhook events at https://apidocs.sendinblue.com/webhooks/#3.
|
||||||
|
|||||||
@@ -8,11 +8,9 @@ import os
|
|||||||
import requests
|
import requests
|
||||||
import six
|
import six
|
||||||
from django.core import mail
|
from django.core import mail
|
||||||
from django.test import SimpleTestCase
|
from django.test import SimpleTestCase, override_settings, tag
|
||||||
from django.test.utils import override_settings
|
|
||||||
from django.utils.timezone import get_fixed_timezone, override as override_current_timezone, utc
|
from django.utils.timezone import get_fixed_timezone, override as override_current_timezone, utc
|
||||||
from mock import patch
|
from mock import patch
|
||||||
from sparkpost.exceptions import SparkPostAPIException
|
|
||||||
|
|
||||||
from anymail.exceptions import (AnymailAPIError, AnymailUnsupportedFeature, AnymailRecipientsRefused,
|
from anymail.exceptions import (AnymailAPIError, AnymailUnsupportedFeature, AnymailRecipientsRefused,
|
||||||
AnymailConfigurationError, AnymailInvalidAddress)
|
AnymailConfigurationError, AnymailInvalidAddress)
|
||||||
@@ -21,6 +19,7 @@ from anymail.message import attach_inline_image_file
|
|||||||
from .utils import AnymailTestMixin, decode_att, SAMPLE_IMAGE_FILENAME, sample_image_path, sample_image_content
|
from .utils import AnymailTestMixin, decode_att, SAMPLE_IMAGE_FILENAME, sample_image_path, sample_image_content
|
||||||
|
|
||||||
|
|
||||||
|
@tag('sparkpost')
|
||||||
@override_settings(EMAIL_BACKEND='anymail.backends.sparkpost.EmailBackend',
|
@override_settings(EMAIL_BACKEND='anymail.backends.sparkpost.EmailBackend',
|
||||||
ANYMAIL={'SPARKPOST_API_KEY': 'test_api_key'})
|
ANYMAIL={'SPARKPOST_API_KEY': 'test_api_key'})
|
||||||
class SparkPostBackendMockAPITestCase(SimpleTestCase, AnymailTestMixin):
|
class SparkPostBackendMockAPITestCase(SimpleTestCase, AnymailTestMixin):
|
||||||
@@ -48,6 +47,7 @@ class SparkPostBackendMockAPITestCase(SimpleTestCase, AnymailTestMixin):
|
|||||||
return self.mock_send.return_value
|
return self.mock_send.return_value
|
||||||
|
|
||||||
def set_mock_failure(self, status_code=400, raw=b'{"errors":[{"message":"test error"}]}', encoding='utf-8'):
|
def set_mock_failure(self, status_code=400, raw=b'{"errors":[{"message":"test error"}]}', encoding='utf-8'):
|
||||||
|
from sparkpost.exceptions import SparkPostAPIException
|
||||||
# Need to build a real(-ish) requests.Response for SparkPostAPIException
|
# Need to build a real(-ish) requests.Response for SparkPostAPIException
|
||||||
response = requests.Response()
|
response = requests.Response()
|
||||||
response.status_code = status_code
|
response.status_code = status_code
|
||||||
@@ -82,6 +82,7 @@ class SparkPostBackendMockAPITestCase(SimpleTestCase, AnymailTestMixin):
|
|||||||
raise AssertionError(msg or "ESP API was called and shouldn't have been")
|
raise AssertionError(msg or "ESP API was called and shouldn't have been")
|
||||||
|
|
||||||
|
|
||||||
|
@tag('sparkpost')
|
||||||
class SparkPostBackendStandardEmailTests(SparkPostBackendMockAPITestCase):
|
class SparkPostBackendStandardEmailTests(SparkPostBackendMockAPITestCase):
|
||||||
"""Test backend support for Django standard email features"""
|
"""Test backend support for Django standard email features"""
|
||||||
|
|
||||||
@@ -325,6 +326,7 @@ class SparkPostBackendStandardEmailTests(SparkPostBackendMockAPITestCase):
|
|||||||
self.message.send()
|
self.message.send()
|
||||||
|
|
||||||
|
|
||||||
|
@tag('sparkpost')
|
||||||
class SparkPostBackendAnymailFeatureTests(SparkPostBackendMockAPITestCase):
|
class SparkPostBackendAnymailFeatureTests(SparkPostBackendMockAPITestCase):
|
||||||
"""Test backend support for Anymail added features"""
|
"""Test backend support for Anymail added features"""
|
||||||
|
|
||||||
@@ -545,6 +547,7 @@ class SparkPostBackendAnymailFeatureTests(SparkPostBackendMockAPITestCase):
|
|||||||
# modify those errors.
|
# modify those errors.
|
||||||
|
|
||||||
|
|
||||||
|
@tag('sparkpost')
|
||||||
class SparkPostBackendRecipientsRefusedTests(SparkPostBackendMockAPITestCase):
|
class SparkPostBackendRecipientsRefusedTests(SparkPostBackendMockAPITestCase):
|
||||||
"""Should raise AnymailRecipientsRefused when *all* recipients are rejected or invalid"""
|
"""Should raise AnymailRecipientsRefused when *all* recipients are rejected or invalid"""
|
||||||
|
|
||||||
@@ -586,6 +589,7 @@ class SparkPostBackendRecipientsRefusedTests(SparkPostBackendMockAPITestCase):
|
|||||||
self.assertEqual(sent, 1) # refused message is included in sent count
|
self.assertEqual(sent, 1) # refused message is included in sent count
|
||||||
|
|
||||||
|
|
||||||
|
@tag('sparkpost')
|
||||||
@override_settings(EMAIL_BACKEND="anymail.backends.sparkpost.EmailBackend")
|
@override_settings(EMAIL_BACKEND="anymail.backends.sparkpost.EmailBackend")
|
||||||
class SparkPostBackendConfigurationTests(SimpleTestCase, AnymailTestMixin):
|
class SparkPostBackendConfigurationTests(SimpleTestCase, AnymailTestMixin):
|
||||||
"""Test various SparkPost client options"""
|
"""Test various SparkPost client options"""
|
||||||
|
|||||||
@@ -2,6 +2,7 @@ import json
|
|||||||
from base64 import b64encode
|
from base64 import b64encode
|
||||||
from textwrap import dedent
|
from textwrap import dedent
|
||||||
|
|
||||||
|
from django.test import tag
|
||||||
from mock import ANY
|
from mock import ANY
|
||||||
|
|
||||||
from anymail.inbound import AnymailInboundMessage
|
from anymail.inbound import AnymailInboundMessage
|
||||||
@@ -12,6 +13,7 @@ from .utils import sample_image_content, sample_email_content
|
|||||||
from .webhook_cases import WebhookTestCase
|
from .webhook_cases import WebhookTestCase
|
||||||
|
|
||||||
|
|
||||||
|
@tag('sparkpost')
|
||||||
class SparkpostInboundTestCase(WebhookTestCase):
|
class SparkpostInboundTestCase(WebhookTestCase):
|
||||||
def test_inbound_basics(self):
|
def test_inbound_basics(self):
|
||||||
event = {
|
event = {
|
||||||
|
|||||||
@@ -2,18 +2,17 @@ import os
|
|||||||
import unittest
|
import unittest
|
||||||
from datetime import datetime, timedelta
|
from datetime import datetime, timedelta
|
||||||
|
|
||||||
from django.test import SimpleTestCase
|
from django.test import SimpleTestCase, override_settings, tag
|
||||||
from django.test.utils import override_settings
|
|
||||||
|
|
||||||
from anymail.exceptions import AnymailAPIError
|
from anymail.exceptions import AnymailAPIError
|
||||||
from anymail.message import AnymailMessage
|
from anymail.message import AnymailMessage
|
||||||
|
|
||||||
from .utils import AnymailTestMixin, sample_image_path, RUN_LIVE_TESTS
|
from .utils import AnymailTestMixin, sample_image_path
|
||||||
|
|
||||||
SPARKPOST_TEST_API_KEY = os.getenv('SPARKPOST_TEST_API_KEY')
|
SPARKPOST_TEST_API_KEY = os.getenv('SPARKPOST_TEST_API_KEY')
|
||||||
|
|
||||||
|
|
||||||
@unittest.skipUnless(RUN_LIVE_TESTS, "RUN_LIVE_TESTS disabled in this environment")
|
@tag('sparkpost', 'live')
|
||||||
@unittest.skipUnless(SPARKPOST_TEST_API_KEY,
|
@unittest.skipUnless(SPARKPOST_TEST_API_KEY,
|
||||||
"Set SPARKPOST_TEST_API_KEY environment variable "
|
"Set SPARKPOST_TEST_API_KEY environment variable "
|
||||||
"to run SparkPost integration tests")
|
"to run SparkPost integration tests")
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
import json
|
import json
|
||||||
from datetime import datetime
|
from datetime import datetime
|
||||||
|
|
||||||
|
from django.test import tag
|
||||||
from django.utils.timezone import utc
|
from django.utils.timezone import utc
|
||||||
from mock import ANY
|
from mock import ANY
|
||||||
|
|
||||||
@@ -10,6 +11,7 @@ from anymail.webhooks.sparkpost import SparkPostTrackingWebhookView
|
|||||||
from .webhook_cases import WebhookBasicAuthTestsMixin, WebhookTestCase
|
from .webhook_cases import WebhookBasicAuthTestsMixin, WebhookTestCase
|
||||||
|
|
||||||
|
|
||||||
|
@tag('sparkpost')
|
||||||
class SparkPostWebhookSecurityTestCase(WebhookTestCase, WebhookBasicAuthTestsMixin):
|
class SparkPostWebhookSecurityTestCase(WebhookTestCase, WebhookBasicAuthTestsMixin):
|
||||||
def call_webhook(self):
|
def call_webhook(self):
|
||||||
return self.client.post('/anymail/sparkpost/tracking/',
|
return self.client.post('/anymail/sparkpost/tracking/',
|
||||||
@@ -18,6 +20,7 @@ class SparkPostWebhookSecurityTestCase(WebhookTestCase, WebhookBasicAuthTestsMix
|
|||||||
# Actual tests are in WebhookBasicAuthTestsMixin
|
# Actual tests are in WebhookBasicAuthTestsMixin
|
||||||
|
|
||||||
|
|
||||||
|
@tag('sparkpost')
|
||||||
class SparkPostDeliveryTestCase(WebhookTestCase):
|
class SparkPostDeliveryTestCase(WebhookTestCase):
|
||||||
|
|
||||||
def test_ping_event(self):
|
def test_ping_event(self):
|
||||||
|
|||||||
@@ -8,32 +8,12 @@ import uuid
|
|||||||
import warnings
|
import warnings
|
||||||
from base64 import b64decode
|
from base64 import b64decode
|
||||||
from contextlib import contextmanager
|
from contextlib import contextmanager
|
||||||
from distutils.util import strtobool
|
|
||||||
|
|
||||||
import six
|
import six
|
||||||
from django.test import Client
|
from django.test import Client
|
||||||
from six.moves import StringIO
|
from six.moves import StringIO
|
||||||
|
|
||||||
|
|
||||||
def envbool(var, default=False):
|
|
||||||
"""Returns value of environment variable var as a bool, or default if not set.
|
|
||||||
|
|
||||||
Converts `'true'` to `True`, and `'false'` to `False`.
|
|
||||||
See :func:`~distutils.util.strtobool` for full list of allowable values.
|
|
||||||
"""
|
|
||||||
val = os.getenv(var, None)
|
|
||||||
if val is None:
|
|
||||||
return default
|
|
||||||
else:
|
|
||||||
return strtobool(val)
|
|
||||||
|
|
||||||
|
|
||||||
# RUN_LIVE_TESTS: whether to run live API integration tests.
|
|
||||||
# True by default, except in CONTINUOUS_INTEGRATION job.
|
|
||||||
# (See comments and overrides in .travis.yml.)
|
|
||||||
RUN_LIVE_TESTS = envbool('RUN_LIVE_TESTS', default=not envbool('CONTINUOUS_INTEGRATION'))
|
|
||||||
|
|
||||||
|
|
||||||
def decode_att(att):
|
def decode_att(att):
|
||||||
"""Returns the original data from base64-encoded attachment content"""
|
"""Returns the original data from base64-encoded attachment content"""
|
||||||
return b64decode(att.encode('ascii'))
|
return b64decode(att.encode('ascii'))
|
||||||
|
|||||||
62
tox.ini
62
tox.ini
@@ -1,17 +1,20 @@
|
|||||||
[tox]
|
[tox]
|
||||||
envlist =
|
envlist =
|
||||||
|
# Factors: django-python-extras
|
||||||
# Test these environments first, to catch most errors early...
|
# Test these environments first, to catch most errors early...
|
||||||
lint
|
lint
|
||||||
django21-py36
|
django21-py36-all
|
||||||
django111-py27
|
django111-py27-all
|
||||||
docs
|
docs
|
||||||
# ... then test all the other supported combinations:
|
# ... then test all the other supported combinations:
|
||||||
django21-py{35,37,py3}
|
django21-py{35,37,py3}-all
|
||||||
django20-py{35,36,py3}
|
django20-py{35,36,py3}-all
|
||||||
django111-py{34,35,36,py}
|
django111-py{34,35,36,py}-all
|
||||||
# ... then prereleases (if available):
|
# ... then prereleases (if available):
|
||||||
django22-py{35,36,37,py3}
|
django22-py{35,36,37,py3}-all
|
||||||
djangoMaster-py{36,37}
|
djangoMaster-py{36,37}-all
|
||||||
|
# ... then partial installation (limit extras):
|
||||||
|
django21-py37-{none,amazon_ses,sparkpost}
|
||||||
|
|
||||||
[testenv]
|
[testenv]
|
||||||
deps =
|
deps =
|
||||||
@@ -20,19 +23,27 @@ deps =
|
|||||||
django21: django~=2.1.0
|
django21: django~=2.1.0
|
||||||
django22: django>=2.2a1
|
django22: django>=2.2a1
|
||||||
djangoMaster: https://github.com/django/django/tarball/master
|
djangoMaster: https://github.com/django/django/tarball/master
|
||||||
# testing dependencies (duplicates setup.py tests_require):
|
# testing dependencies (duplicates setup.py tests_require, less optional extras):
|
||||||
mock
|
mock
|
||||||
boto3
|
extras =
|
||||||
sparkpost
|
all,amazon_ses: amazon_ses
|
||||||
|
all,sparkpost: sparkpost
|
||||||
|
setenv =
|
||||||
|
# tell runtests.py to limit some test tags based on extras factor
|
||||||
|
none: ANYMAIL_SKIP_TESTS=amazon_ses,sparkpost
|
||||||
|
amazon_ses: ANYMAIL_ONLY_TEST=amazon_ses
|
||||||
|
sparkpost: ANYMAIL_ONLY_TEST=sparkpost
|
||||||
ignore_outcome =
|
ignore_outcome =
|
||||||
djangoMaster: True
|
# CI that wants to handle errors itself can set TOX_FORCE_IGNORE_OUTCOME=false
|
||||||
usedevelop = True
|
djangoMaster: {env:TOX_FORCE_IGNORE_OUTCOME:true}
|
||||||
args_are_paths = False
|
args_are_paths = false
|
||||||
|
commands_pre =
|
||||||
|
python -VV
|
||||||
commands =
|
commands =
|
||||||
python --version
|
|
||||||
# pip install .[mailgun,...,sparkpost] ## usedevelop=True + manual deps is much faster on repeat runs
|
|
||||||
python runtests.py {posargs}
|
python runtests.py {posargs}
|
||||||
passenv =
|
passenv =
|
||||||
|
ANYMAIL_ONLY_TEST
|
||||||
|
ANYMAIL_SKIP_TESTS
|
||||||
RUN_LIVE_TESTS
|
RUN_LIVE_TESTS
|
||||||
CONTINUOUS_INTEGRATION
|
CONTINUOUS_INTEGRATION
|
||||||
AMAZON_SES_TEST_*
|
AMAZON_SES_TEST_*
|
||||||
@@ -46,7 +57,7 @@ passenv =
|
|||||||
|
|
||||||
[testenv:lint]
|
[testenv:lint]
|
||||||
basepython = python3
|
basepython = python3
|
||||||
skip_install = True
|
skip_install = true
|
||||||
passenv =
|
passenv =
|
||||||
CONTINUOUS_INTEGRATION
|
CONTINUOUS_INTEGRATION
|
||||||
# (but not any of the live test API keys)
|
# (but not any of the live test API keys)
|
||||||
@@ -59,7 +70,7 @@ commands =
|
|||||||
|
|
||||||
[testenv:docs]
|
[testenv:docs]
|
||||||
basepython = python3
|
basepython = python3
|
||||||
skip_install = True
|
skip_install = true
|
||||||
passenv =
|
passenv =
|
||||||
CONTINUOUS_INTEGRATION
|
CONTINUOUS_INTEGRATION
|
||||||
# (but not any of the live test API keys)
|
# (but not any of the live test API keys)
|
||||||
@@ -78,20 +89,3 @@ commands =
|
|||||||
/bin/bash -c 'python setup.py --long-description \
|
/bin/bash -c 'python setup.py --long-description \
|
||||||
| rst2html5.py --config=docs/_readme/docutils.cfg \
|
| rst2html5.py --config=docs/_readme/docutils.cfg \
|
||||||
> {env:DOCS_BUILD_DIR}/readme.html'
|
> {env:DOCS_BUILD_DIR}/readme.html'
|
||||||
|
|
||||||
[travis]
|
|
||||||
unignore_outcomes = True
|
|
||||||
python =
|
|
||||||
3.6: py36, lint, docs
|
|
||||||
|
|
||||||
[travis:env]
|
|
||||||
DJANGO =
|
|
||||||
1.11: django111
|
|
||||||
2.0: django20
|
|
||||||
2.1: django21
|
|
||||||
2.2: django22
|
|
||||||
master: djangoMaster
|
|
||||||
LINT_AND_DOCS =
|
|
||||||
true: lint, docs
|
|
||||||
docs: docs
|
|
||||||
lint: lint
|
|
||||||
|
|||||||
Reference in New Issue
Block a user