diff --git a/djrill/tests/mock_backend.py b/djrill/tests/mock_backend.py index 2fc0389..a806282 100644 --- a/djrill/tests/mock_backend.py +++ b/djrill/tests/mock_backend.py @@ -1,19 +1,22 @@ import json from mock import patch -from django.conf import settings from django.test import TestCase +from .utils import override_settings + +@override_settings(MANDRILL_API_KEY="FAKE_API_KEY_FOR_TESTING", + EMAIL_BACKEND="djrill.mail.backends.djrill.DjrillBackend") class DjrillBackendMockAPITestCase(TestCase): """TestCase that uses Djrill EmailBackend with a mocked Mandrill API""" class MockResponse: """requests.post return value mock sufficient for DjrillBackend""" - def __init__(self, status_code=200, content="{}", json=['']): + def __init__(self, status_code=200, content="{}", json=None): self.status_code = status_code self.content = content - self._json = json + self._json = json if json is not None else [''] def json(self): return self._json @@ -23,15 +26,8 @@ class DjrillBackendMockAPITestCase(TestCase): self.mock_post = self.patch.start() self.mock_post.return_value = self.MockResponse() - settings.MANDRILL_API_KEY = "FAKE_API_KEY_FOR_TESTING" - - # Django TestCase sets up locmem EmailBackend; override it here - self.original_email_backend = settings.EMAIL_BACKEND - settings.EMAIL_BACKEND = "djrill.mail.backends.djrill.DjrillBackend" - def tearDown(self): self.patch.stop() - settings.EMAIL_BACKEND = self.original_email_backend def assert_mandrill_called(self, endpoint): """Verifies the (mock) Mandrill API was called on endpoint. diff --git a/djrill/tests/test_mandrill_send.py b/djrill/tests/test_mandrill_send.py index d6a5cea..583d5b1 100644 --- a/djrill/tests/test_mandrill_send.py +++ b/djrill/tests/test_mandrill_send.py @@ -4,13 +4,15 @@ from email.mime.base import MIMEBase from email.mime.image import MIMEImage import os -from django.conf import settings from django.core import mail from django.core.exceptions import ImproperlyConfigured from django.core.mail import make_msgid +from django.test import TestCase from djrill import MandrillAPIError, NotSupportedByMandrillError -from djrill.tests.mock_backend import DjrillBackendMockAPITestCase +from .mock_backend import DjrillBackendMockAPITestCase +from .utils import override_settings + def decode_att(att): """Returns the original data from base64-encoded attachment content""" @@ -46,12 +48,6 @@ class DjrillBackendTests(DjrillBackendMockAPITestCase): self.assertEqual(len(data['message']['to']), 1) self.assertEqual(data['message']['to'][0]['email'], "to@example.com") - def test_missing_api_key(self): - del settings.MANDRILL_API_KEY - with self.assertRaises(ImproperlyConfigured): - mail.send_mail('Subject', 'Message', 'from@example.com', - ['to@example.com']) - def test_name_addr(self): """Make sure RFC2822 name-addr format (with display-name) is allowed @@ -462,3 +458,13 @@ class DjrillMandrillFeatureTests(DjrillBackendMockAPITestCase): sent = msg.send(fail_silently=True) self.assertEqual(sent, 0) self.assertIsNone(msg.mandrill_response) + + +@override_settings(EMAIL_BACKEND="djrill.mail.backends.djrill.DjrillBackend") +class DjrillImproperlyConfiguredTests(TestCase): + """Test Djrill backend without Djrill-specific settings in place""" + + def test_missing_api_key(self): + with self.assertRaises(ImproperlyConfigured): + mail.send_mail('Subject', 'Message', 'from@example.com', + ['to@example.com']) diff --git a/djrill/tests/test_mandrill_subaccounts.py b/djrill/tests/test_mandrill_subaccounts.py index 015f069..3d9a1e8 100644 --- a/djrill/tests/test_mandrill_subaccounts.py +++ b/djrill/tests/test_mandrill_subaccounts.py @@ -1,19 +1,13 @@ from django.core import mail -from djrill.tests.mock_backend import DjrillBackendMockAPITestCase +from .mock_backend import DjrillBackendMockAPITestCase +from .utils import override_settings -from django.conf import settings class DjrillMandrillSubaccountTests(DjrillBackendMockAPITestCase): """Test Djrill backend support for Mandrill subaccounts""" def test_send_basic(self): - # Make sure we don't have a MANDRILL_SUBACCOUNT from a previous test - try: - del settings.MANDRILL_SUBACCOUNT - except AttributeError: - pass - mail.send_mail('Subject here', 'Here is the message.', 'from@example.com', ['to@example.com'], fail_silently=False) self.assert_mandrill_called("/messages/send.json") @@ -26,9 +20,8 @@ class DjrillMandrillSubaccountTests(DjrillBackendMockAPITestCase): self.assertEqual(data['message']['to'][0]['email'], "to@example.com") self.assertFalse('subaccount' in data['message']) + @override_settings(MANDRILL_SUBACCOUNT="test_subaccount") def test_send_from_subaccount(self): - settings.MANDRILL_SUBACCOUNT = "test_subaccount" - mail.send_mail('Subject here', 'Here is the message.', 'from@example.com', ['to@example.com'], fail_silently=False) self.assert_mandrill_called("/messages/send.json") @@ -39,10 +32,10 @@ class DjrillMandrillSubaccountTests(DjrillBackendMockAPITestCase): self.assertEqual(data['message']['from_email'], "from@example.com") self.assertEqual(len(data['message']['to']), 1) self.assertEqual(data['message']['to'][0]['email'], "to@example.com") - self.assertEqual(data['message']['subaccount'], settings.MANDRILL_SUBACCOUNT) + self.assertEqual(data['message']['subaccount'], "test_subaccount") + @override_settings(MANDRILL_SUBACCOUNT="global_setting_subaccount") def test_subaccount_message_overrides_setting(self): - settings.MANDRILL_SUBACCOUNT = "global_setting_subaccount" message = mail.EmailMessage( 'Subject here', 'Here is the message', 'from@example.com', ['to@example.com']) diff --git a/djrill/tests/test_mandrill_webhook.py b/djrill/tests/test_mandrill_webhook.py index d2969e4..52c3f64 100644 --- a/djrill/tests/test_mandrill_webhook.py +++ b/djrill/tests/test_mandrill_webhook.py @@ -3,12 +3,13 @@ import hashlib import hmac import json -from django.test import TestCase -from django.core.exceptions import ImproperlyConfigured from django.conf import settings +from django.core.exceptions import ImproperlyConfigured +from django.test import TestCase from ..compat import b from ..signals import webhook_event +from .utils import override_settings class DjrillWebhookSecretMixinTests(TestCase): @@ -17,71 +18,59 @@ class DjrillWebhookSecretMixinTests(TestCase): """ def test_missing_secret(self): - del settings.DJRILL_WEBHOOK_SECRET - with self.assertRaises(ImproperlyConfigured): self.client.get('/webhook/') + @override_settings(DJRILL_WEBHOOK_SECRET='abc123') def test_incorrect_secret(self): - settings.DJRILL_WEBHOOK_SECRET = 'abc123' - response = self.client.head('/webhook/?secret=wrong') self.assertEqual(response.status_code, 403) + @override_settings(DJRILL_WEBHOOK_SECRET='abc123') def test_default_secret_name(self): - del settings.DJRILL_WEBHOOK_SECRET_NAME - settings.DJRILL_WEBHOOK_SECRET = 'abc123' - response = self.client.head('/webhook/?secret=abc123') self.assertEqual(response.status_code, 200) + @override_settings(DJRILL_WEBHOOK_SECRET='abc123', DJRILL_WEBHOOK_SECRET_NAME='verysecret') def test_custom_secret_name(self): - settings.DJRILL_WEBHOOK_SECRET = 'abc123' - settings.DJRILL_WEBHOOK_SECRET_NAME = 'verysecret' - response = self.client.head('/webhook/?verysecret=abc123') self.assertEqual(response.status_code, 200) +@override_settings(DJRILL_WEBHOOK_SECRET='abc123', + DJRILL_WEBHOOK_SIGNATURE_KEY="signature") class DjrillWebhookSignatureMixinTests(TestCase): """ Test mixin used in optional Mandrill webhook signature support """ - def setUp(self): - settings.DJRILL_WEBHOOK_SECRET = 'abc123' - settings.DJRILL_WEBHOOK_SIGNATURE_KEY = "signature" - settings.DJRILL_WEBHOOK_URL = "/webhook/?secret=abc123" - def test_incorrect_settings(self): - del settings.DJRILL_WEBHOOK_URL with self.assertRaises(ImproperlyConfigured): self.client.post('/webhook/?secret=abc123') - settings.DJRILL_WEBHOOK_URL = "/webhook/?secret=abc123" + @override_settings(DJRILL_WEBHOOK_URL="/webhook/?secret=abc123", + DJRILL_WEBHOOK_SIGNATURE_KEY = "anothersignature") def test_unauthorized(self): - settings.DJRILL_WEBHOOK_SIGNATURE_KEY = "anothersignature" response = self.client.post(settings.DJRILL_WEBHOOK_URL) self.assertEqual(response.status_code, 403) + @override_settings(DJRILL_WEBHOOK_URL="/webhook/?secret=abc123") def test_signature(self): - signature = hmac.new(key=b(settings.DJRILL_WEBHOOK_SIGNATURE_KEY), msg = b(settings.DJRILL_WEBHOOK_URL+"mandrill_events[]"), digestmod=hashlib.sha1) + signature = hmac.new(key=b(settings.DJRILL_WEBHOOK_SIGNATURE_KEY), + msg=b(settings.DJRILL_WEBHOOK_URL+"mandrill_events[]"), + digestmod=hashlib.sha1) hash_string = b64encode(signature.digest()) - response = self.client.post('/webhook/?secret=abc123', data={"mandrill_events":"[]"}, **{"HTTP_X_MANDRILL_SIGNATURE" : hash_string}) + response = self.client.post('/webhook/?secret=abc123', data={"mandrill_events":"[]"}, + **{"HTTP_X_MANDRILL_SIGNATURE": hash_string}) self.assertEqual(response.status_code, 200) - def tearDown(self): - del settings.DJRILL_WEBHOOK_SIGNATURE_KEY - del settings.DJRILL_WEBHOOK_URL +@override_settings(DJRILL_WEBHOOK_SECRET='abc123') class DjrillWebhookViewTests(TestCase): """ Test optional Mandrill webhook view """ - def setUp(self): - settings.DJRILL_WEBHOOK_SECRET = 'abc123' - def test_head_request(self): response = self.client.head('/webhook/?secret=abc123') self.assertEqual(response.status_code, 200) diff --git a/djrill/tests/utils.py b/djrill/tests/utils.py new file mode 100644 index 0000000..7871ce4 --- /dev/null +++ b/djrill/tests/utils.py @@ -0,0 +1,68 @@ +__all__ = ( + 'override_settings', +) + +try: + from django.test.utils import override_settings + +except ImportError: + # Back-port override_settings from Django 1.4 + # https://github.com/django/django/blob/stable/1.4.x/django/test/utils.py + from django.conf import settings, UserSettingsHolder + from django.utils.functional import wraps + + class override_settings(object): + """ + Acts as either a decorator, or a context manager. If it's a decorator it + takes a function and returns a wrapped function. If it's a contextmanager + it's used with the ``with`` statement. In either event entering/exiting + are called before and after, respectively, the function/block is executed. + """ + def __init__(self, **kwargs): + self.options = kwargs + self.wrapped = settings._wrapped + + def __enter__(self): + self.enable() + + def __exit__(self, exc_type, exc_value, traceback): + self.disable() + + def __call__(self, test_func): + from django.test import TransactionTestCase + if isinstance(test_func, type) and issubclass(test_func, TransactionTestCase): + original_pre_setup = test_func._pre_setup + original_post_teardown = test_func._post_teardown + def _pre_setup(innerself): + self.enable() + original_pre_setup(innerself) + def _post_teardown(innerself): + original_post_teardown(innerself) + self.disable() + test_func._pre_setup = _pre_setup + test_func._post_teardown = _post_teardown + return test_func + else: + @wraps(test_func) + def inner(*args, **kwargs): + with self: + return test_func(*args, **kwargs) + return inner + + def enable(self): + override = UserSettingsHolder(settings._wrapped) + for key, new_value in self.options.items(): + setattr(override, key, new_value) + settings._wrapped = override + # No setting_changed signal in Django 1.3 + # for key, new_value in self.options.items(): + # setting_changed.send(sender=settings._wrapped.__class__, + # setting=key, value=new_value) + + def disable(self): + settings._wrapped = self.wrapped + # No setting_changed signal in Django 1.3 + # for key in self.options: + # new_value = getattr(settings, key, None) + # setting_changed.send(sender=settings._wrapped.__class__, + # setting=key, value=new_value)