diff --git a/CHANGES.md b/CHANGES.md index ebe283d..d827f3d 100644 --- a/CHANGES.md +++ b/CHANGES.md @@ -4,6 +4,7 @@ * Added `ThreadEmailService` for simple async sending of emails * Added basic logging with privacy configuration to mail class * Restructured documentation + * Restructured unit-tests * *2.0.0* (2024-04-11) * Dropped Django 3.2 & 4.1 support (via `ambient-package-update`) diff --git a/django_pony_express/services/base.py b/django_pony_express/services/base.py index c428708..f551214 100644 --- a/django_pony_express/services/base.py +++ b/django_pony_express/services/base.py @@ -309,7 +309,6 @@ def _send_and_log_email(self, msg: EmailMultiAlternatives) -> bool: """ Method to be called by the thread. Enables logging since we won't have any sync return values. """ - # TODO(RV): test me result = False recipients_as_string = " ".join(self.recipient_email_list) try: diff --git a/settings.py b/settings.py index d6eac2c..a64a223 100644 --- a/settings.py +++ b/settings.py @@ -60,3 +60,7 @@ TIME_ZONE = "UTC" LOCALE_PATHS = [str(BASE_PATH) + "/django_pony_express/locale"] + + +# Pony express +DJANGO_PONY_EXPRESS_LOG_RECIPIENTS = False diff --git a/tests/services/__init__.py b/tests/services/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/tests/services/base/__init__.py b/tests/services/base/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/tests/services/base/test_base_mail_factory.py b/tests/services/base/test_base_mail_factory.py new file mode 100644 index 0000000..125cdd7 --- /dev/null +++ b/tests/services/base/test_base_mail_factory.py @@ -0,0 +1,82 @@ +from django.test import TestCase + +from django_pony_express.errors import EmailServiceConfigError +from django_pony_express.services.base import BaseEmailService, BaseEmailServiceFactory + + +class BaseEmailServiceFactoryTest(TestCase): + class TestMailService(BaseEmailService): + subject = "My subject" + template_name = "testapp/test_email.html" + + @classmethod + def setUpTestData(cls): + super().setUpTestData() + + def test_init_recipient_list_is_set(self): + email = "albertus.magnus@example.com" + factory = BaseEmailServiceFactory([email]) + self.assertEqual(factory.recipient_email_list, [email]) + + def test_is_valid_positive_case(self): + email = "albertus.magnus@example.com" + factory = BaseEmailServiceFactory(recipient_email_list=[email]) + factory.service_class = BaseEmailService + self.assertTrue(factory.is_valid()) + + def test_is_valid_no_recipients(self): + factory = BaseEmailServiceFactory() + factory.service_class = BaseEmailService + with self.assertRaises(EmailServiceConfigError): + factory.is_valid() + + def test_is_valid_no_service_class(self): + email = "albertus.magnus@example.com" + factory = BaseEmailServiceFactory(recipient_email_list=[email]) + with self.assertRaises(EmailServiceConfigError): + factory.is_valid() + + def test_is_valid_no_exception_raised(self): + factory = BaseEmailServiceFactory() + factory.is_valid(raise_exception=False) + self.assertEqual(len(factory.errors), 2) + + def test_has_errors_positive_case(self): + factory = BaseEmailServiceFactory() + factory.is_valid(raise_exception=False) + self.assertTrue(factory.has_errors()) + + def test_has_errors_negative_case(self): + email = "albertus.magnus@example.com" + factory = BaseEmailServiceFactory(recipient_email_list=[email]) + factory.service_class = BaseEmailService + self.assertFalse(factory.has_errors()) + + def test_get_recipient_list_regular(self): + email_1 = "albertus.magnus@example.com" + email_2 = "thomas.von.aquin@example.com" + factory = BaseEmailServiceFactory(recipient_email_list=[email_1, email_2]) + self.assertEqual(factory.get_recipient_list(), [email_1, email_2]) + + def test_get_email_from_recipient_regular(self): + email_1 = "albertus.magnus@example.com" + email_2 = "thomas.von.aquin@example.com" + factory = BaseEmailServiceFactory(recipient_email_list=[email_1, email_2]) + self.assertEqual(factory.get_email_from_recipient(factory.get_recipient_list()[1]), email_2) + + def test_get_context_data_regular(self): + factory = BaseEmailServiceFactory() + self.assertEqual(factory.get_context_data(), {}) + + def test_process_regular(self): + email_1 = "albertus.magnus@example.com" + email_2 = "thomas.von.aquin@example.com" + factory = BaseEmailServiceFactory(recipient_email_list=[email_1, email_2]) + factory.service_class = self.TestMailService + self.assertEqual(factory.process(), 2) + + def test_process_with_exception(self): + factory = BaseEmailServiceFactory() + factory.service_class = self.TestMailService + with self.assertRaises(EmailServiceConfigError): + factory.process() diff --git a/tests/test_mail_services.py b/tests/services/base/test_base_mail_service.py similarity index 78% rename from tests/test_mail_services.py rename to tests/services/base/test_base_mail_service.py index a32a364..15b02b9 100644 --- a/tests/test_mail_services.py +++ b/tests/services/base/test_base_mail_service.py @@ -1,4 +1,6 @@ +import logging from os.path import basename +from unittest import mock from django.conf import settings from django.core.mail import EmailMultiAlternatives @@ -6,85 +8,7 @@ from freezegun import freeze_time from django_pony_express.errors import EmailServiceAttachmentError, EmailServiceConfigError -from django_pony_express.services.base import BaseEmailService, BaseEmailServiceFactory - - -class BaseEmailServiceFactoryTest(TestCase): - class TestMailService(BaseEmailService): - subject = "My subject" - template_name = "testapp/test_email.html" - - @classmethod - def setUpTestData(cls): - super().setUpTestData() - - def test_init_recipient_list_is_set(self): - email = "albertus.magnus@example.com" - factory = BaseEmailServiceFactory([email]) - self.assertEqual(factory.recipient_email_list, [email]) - - def test_is_valid_positive_case(self): - email = "albertus.magnus@example.com" - factory = BaseEmailServiceFactory(recipient_email_list=[email]) - factory.service_class = BaseEmailService - self.assertTrue(factory.is_valid()) - - def test_is_valid_no_recipients(self): - factory = BaseEmailServiceFactory() - factory.service_class = BaseEmailService - with self.assertRaises(EmailServiceConfigError): - factory.is_valid() - - def test_is_valid_no_service_class(self): - email = "albertus.magnus@example.com" - factory = BaseEmailServiceFactory(recipient_email_list=[email]) - with self.assertRaises(EmailServiceConfigError): - factory.is_valid() - - def test_is_valid_no_exception_raised(self): - factory = BaseEmailServiceFactory() - factory.is_valid(raise_exception=False) - self.assertEqual(len(factory.errors), 2) - - def test_has_errors_positive_case(self): - factory = BaseEmailServiceFactory() - factory.is_valid(raise_exception=False) - self.assertTrue(factory.has_errors()) - - def test_has_errors_negative_case(self): - email = "albertus.magnus@example.com" - factory = BaseEmailServiceFactory(recipient_email_list=[email]) - factory.service_class = BaseEmailService - self.assertFalse(factory.has_errors()) - - def test_get_recipient_list_regular(self): - email_1 = "albertus.magnus@example.com" - email_2 = "thomas.von.aquin@example.com" - factory = BaseEmailServiceFactory(recipient_email_list=[email_1, email_2]) - self.assertEqual(factory.get_recipient_list(), [email_1, email_2]) - - def test_get_email_from_recipient_regular(self): - email_1 = "albertus.magnus@example.com" - email_2 = "thomas.von.aquin@example.com" - factory = BaseEmailServiceFactory(recipient_email_list=[email_1, email_2]) - self.assertEqual(factory.get_email_from_recipient(factory.get_recipient_list()[1]), email_2) - - def test_get_context_data_regular(self): - factory = BaseEmailServiceFactory() - self.assertEqual(factory.get_context_data(), {}) - - def test_process_regular(self): - email_1 = "albertus.magnus@example.com" - email_2 = "thomas.von.aquin@example.com" - factory = BaseEmailServiceFactory(recipient_email_list=[email_1, email_2]) - factory.service_class = self.TestMailService - self.assertEqual(factory.process(), 2) - - def test_process_with_excepetion(self): - factory = BaseEmailServiceFactory() - factory.service_class = self.TestMailService - with self.assertRaises(EmailServiceConfigError): - factory.process() +from django_pony_express.services.base import BaseEmailService class BaseEmailServiceTest(TestCase): @@ -102,6 +26,17 @@ def test_init_recipient_and_context_are_initialised_empty(self): self.assertEqual(service.recipient_email_list, []) self.assertEqual(service.context_data, {}) + def test_get_logger_logger_not_set(self): + service = BaseEmailService() + email_logger = service._get_logger() + self.assertEqual(service._logger, email_logger) + + def test_get_logger_logger_set(self): + service = BaseEmailService() + service._logger = logging.getLogger("my_logger") + email_logger = service._get_logger() + self.assertEqual(service._logger, email_logger) + def test_get_context_data_regular(self): data = {"city": "Cologne"} service = BaseEmailService(context_data=data) @@ -114,7 +49,7 @@ def test_get_subject_no_prefix(self): self.assertEqual(service.get_subject(), subject) def test_get_subject_with_prefix(self): - prefix = "Ai: Core" + prefix = "Pony Express" subject = "I am a subject!" service = BaseEmailService() service.SUBJECT_PREFIX = prefix @@ -373,6 +308,57 @@ def test_has_errors_negative_case(self): service.template_name = "testapp/test_email.html" self.assertFalse(service.has_errors()) + @mock.patch("django_pony_express.services.base.BaseEmailService._logger") + def test_send_and_log_email_success_privacy_active(self, mock_logger): + service = BaseEmailService(recipient_email_list=["thomas.aquin@example.com"]) + result = service._send_and_log_email( + msg=EmailMultiAlternatives(subject="The Pony Express", to=["thomas.aquin@example.com"]) + ) + + mock_logger.debug.assert_called_with('Email "%s" successfully sent.', "The Pony Express") + self.assertEqual(result, 1) + + @mock.patch("django_pony_express.services.base.BaseEmailService._logger") + @mock.patch("django_pony_express.services.base.PONY_LOG_RECIPIENTS", True) + def test_send_and_log_success_privacy_inactive(self, mock_logger): + service = BaseEmailService(recipient_email_list=["thomas.aquin@example.com"]) + result = service._send_and_log_email( + msg=EmailMultiAlternatives(subject="The Pony Express", to=["thomas.aquin@example.com"]) + ) + + mock_logger.debug.assert_called_with( + 'Email "%s" successfully sent to %s.', "The Pony Express", "thomas.aquin@example.com" + ) + self.assertEqual(result, 1) + + @mock.patch.object(EmailMultiAlternatives, "send", side_effect=Exception("Broken pony")) + @mock.patch("django_pony_express.services.base.BaseEmailService._logger") + def test_send_and_log_email_failure_privacy_active(self, mock_logger, *args): + service = BaseEmailService(recipient_email_list=["thomas.aquin@example.com"]) + result = service._send_and_log_email( + msg=EmailMultiAlternatives(subject="The Pony Express", to=["thomas.aquin@example.com"]) + ) + + mock_logger.error('An error occurred sending email "%s": %s', "The Pony Express", "Broken pony") + self.assertFalse(result) + + @mock.patch.object(EmailMultiAlternatives, "send", side_effect=Exception("Broken pony")) + @mock.patch("django_pony_express.services.base.BaseEmailService._logger") + @mock.patch("django_pony_express.services.base.PONY_LOG_RECIPIENTS", True) + def test_send_and_log_success_privacy_inactive(self, mock_logger, *args): + service = BaseEmailService(recipient_email_list=["thomas.aquin@example.com"]) + result = service._send_and_log_email( + msg=EmailMultiAlternatives(subject="The Pony Express", to=["thomas.aquin@example.com"]) + ) + + mock_logger.error( + 'An error occurred sending email "%s" to %s: %s', + "The Pony Express", + "thomas.aquin@example.com", + "Broken pony", + ) + self.assertFalse(result) + def test_process_regular(self): email = "albertus.magnus@example.com" subject = "Test email" diff --git a/tests/services/tests/__init__.py b/tests/services/tests/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/tests/test_email_test_service.py b/tests/services/tests/test_email_test_service.py similarity index 100% rename from tests/test_email_test_service.py rename to tests/services/tests/test_email_test_service.py