diff --git a/backend/api/grants/tests/test_send_grant.py b/backend/api/grants/tests/test_send_grant.py index 42c19b9cf7..39ae051d7c 100644 --- a/backend/api/grants/tests/test_send_grant.py +++ b/backend/api/grants/tests/test_send_grant.py @@ -91,8 +91,7 @@ def _send_grant(client, conference, conference_code=None, **kwargs): return response -def test_send_grant(graphql_client, user, mocker, django_capture_on_commit_callbacks): - mock_email_template = mocker.patch("api.grants.mutations.EmailTemplate") +def test_send_grant(graphql_client, user, mocker, django_capture_on_commit_callbacks, sent_emails): graphql_client.force_login(user) conference = ConferenceFactory(active_grants=True) EmailTemplateFactory( @@ -118,13 +117,18 @@ def test_send_grant(graphql_client, user, mocker, django_capture_on_commit_callb user=user, conference=conference, privacy_policy="grant" ).exists() - # An email is sent to the user - mock_email_template.objects.for_conference().get_by_identifier().send_email.assert_called_once_with( - recipient=user, - placeholders={ - "user_name": user.full_name, - }, - ) + # Verify that the correct email template was used and email was sent + emails_sent = sent_emails() + assert emails_sent.count() == 1 + + sent_email = emails_sent.first() + assert sent_email.email_template.identifier == EmailTemplateIdentifier.grant_application_confirmation + assert sent_email.email_template.conference == conference + assert sent_email.recipient == user + assert sent_email.recipient_email == user.email + + # Verify placeholders were processed correctly + assert sent_email.placeholders["user_name"] == user.full_name def test_cannot_send_a_grant_if_grants_are_closed(graphql_client, user, mocker): diff --git a/backend/api/submissions/tests/test_send_submission.py b/backend/api/submissions/tests/test_send_submission.py index 255d0fb02b..c3b54974ea 100644 --- a/backend/api/submissions/tests/test_send_submission.py +++ b/backend/api/submissions/tests/test_send_submission.py @@ -141,10 +141,9 @@ def _submit_proposal(client, conference, submission, **kwargs): def test_submit_talk( - graphql_client, user, django_capture_on_commit_callbacks, mocker, settings + graphql_client, user, django_capture_on_commit_callbacks, mocker, settings, sent_emails ): settings.FRONTEND_URL = "http://testserver" - mock_email_template = mocker.patch("api.submissions.mutations.EmailTemplate") mock_notify = mocker.patch("api.submissions.mutations.notify_new_cfp_submission") graphql_client.force_login(user) @@ -219,14 +218,20 @@ def test_submit_talk( mock_notify.delay.assert_called_once() - mock_email_template.objects.for_conference().get_by_identifier().send_email.assert_called_once_with( - recipient=user, - placeholders={ - "user_name": user.full_name, - "proposal_title": "English", - "proposal_url": f"http://testserver/submission/{talk.hashid}", - }, - ) + # Verify that the correct email template was used and email was sent + emails_sent = sent_emails() + assert emails_sent.count() == 1 + + sent_email = emails_sent.first() + assert sent_email.email_template.identifier == EmailTemplateIdentifier.proposal_received_confirmation + assert sent_email.email_template.conference == conference + assert sent_email.recipient == user + assert sent_email.recipient_email == user.email + + # Verify placeholders were processed correctly + assert sent_email.placeholders["user_name"] == user.full_name + assert sent_email.placeholders["proposal_title"] == "English" + assert sent_email.placeholders["proposal_url"] == f"http://testserver/submission/{talk.hashid}" def test_submit_talk_with_photo_to_upload(graphql_client, user, mocker): diff --git a/backend/conferences/tests/test_tasks.py b/backend/conferences/tests/test_tasks.py index 5882345daf..a562d33f18 100644 --- a/backend/conferences/tests/test_tasks.py +++ b/backend/conferences/tests/test_tasks.py @@ -2,7 +2,6 @@ from notifications.tests.factories import EmailTemplateFactory from conferences.tests.factories import ConferenceVoucherFactory from datetime import datetime, timezone -from unittest.mock import patch import time_machine from conferences.models.conference_voucher import ConferenceVoucher @@ -22,7 +21,7 @@ ConferenceVoucher.VoucherType.GRANT, ], ) -def test_send_conference_voucher_email(voucher_type): +def test_send_conference_voucher_email(voucher_type, sent_emails): user = UserFactory( full_name="Marco Acierno", email="marco@placeholder.it", @@ -41,19 +40,21 @@ def test_send_conference_voucher_email(voucher_type): identifier=EmailTemplateIdentifier.voucher_code, ) - with patch( - "conferences.tasks.EmailTemplate" - ) as mock_email_template, time_machine.travel("2020-10-10 10:00:00Z", tick=False): + with time_machine.travel("2020-10-10 10:00:00Z", tick=False): send_conference_voucher_email(conference_voucher_id=conference_voucher.id) - mock_email_template.objects.for_conference().get_by_identifier().send_email.assert_called_once_with( - recipient=user, - placeholders={ - "voucher_code": "ABC123", - "voucher_type": voucher_type, - "user_name": "Marco Acierno", - }, - ) + emails_sent = sent_emails() + assert emails_sent.count() == 1 + + sent_email = emails_sent.first() + assert sent_email.email_template.identifier == EmailTemplateIdentifier.voucher_code + assert sent_email.email_template.conference == conference_voucher.conference + assert sent_email.recipient == user + assert sent_email.placeholders == { + "voucher_code": "ABC123", + "voucher_type": voucher_type, + "user_name": "Marco Acierno", + } conference_voucher.refresh_from_db() assert conference_voucher.voucher_email_sent_at == datetime( diff --git a/backend/conftest.py b/backend/conftest.py index c1bd36a6ca..14b88c0f93 100644 --- a/backend/conftest.py +++ b/backend/conftest.py @@ -116,3 +116,21 @@ def matcher(req): requests_mock.add_matcher(matcher) return wrapper + + +@pytest.fixture +def sent_emails(db): + """ + Fixture to capture and provide access to SentEmail objects created during tests. + This fixture allows tests to verify email template usage and email creation + without mocking the EmailTemplate class. + + Returns: + A callable that returns a QuerySet of SentEmail objects created during the test. + """ + from notifications.models import SentEmail + + def get_sent_emails(): + return SentEmail.objects.all() + + return get_sent_emails diff --git a/backend/grants/tests/test_tasks.py b/backend/grants/tests/test_tasks.py index 8be25fef7f..a19bc3df93 100644 --- a/backend/grants/tests/test_tasks.py +++ b/backend/grants/tests/test_tasks.py @@ -17,7 +17,10 @@ pytestmark = pytest.mark.django_db -def test_send_grant_reply_rejected_email(): +def test_send_grant_reply_rejected_email(sent_emails): + from notifications.tests.factories import EmailTemplateFactory + from notifications.models import EmailTemplateIdentifier + user = UserFactory( full_name="Marco Acierno", email="marco@placeholder.it", @@ -25,20 +28,32 @@ def test_send_grant_reply_rejected_email(): username="marco", ) grant = GrantFactory(user=user) - - with patch("grants.tasks.EmailTemplate") as mock_email_template: - send_grant_reply_rejected_email(grant_id=grant.id) - - mock_email_template.objects.for_conference().get_by_identifier().send_email.assert_called_once_with( - recipient=user, - placeholders={ - "user_name": "Marco Acierno", - "conference_name": grant.conference.name.localize("en"), - }, + + EmailTemplateFactory( + conference=grant.conference, + identifier=EmailTemplateIdentifier.grant_rejected, ) - -def test_send_grant_reply_waiting_list_email(settings): + send_grant_reply_rejected_email(grant_id=grant.id) + + # Verify that the correct email template was used and email was sent + emails_sent = sent_emails() + assert emails_sent.count() == 1 + + sent_email = emails_sent.first() + assert sent_email.email_template.identifier == EmailTemplateIdentifier.grant_rejected + assert sent_email.email_template.conference == grant.conference + assert sent_email.recipient == user + + # Verify placeholders were processed correctly + assert sent_email.placeholders["user_name"] == "Marco Acierno" + assert sent_email.placeholders["conference_name"] == grant.conference.name.localize("en") + + +def test_send_grant_reply_waiting_list_email(settings, sent_emails): + from notifications.tests.factories import EmailTemplateFactory + from notifications.models import EmailTemplateIdentifier + conference = ConferenceFactory() settings.FRONTEND_URL = "https://pycon.it" @@ -59,22 +74,34 @@ def test_send_grant_reply_waiting_list_email(settings): }, ) grant = GrantFactory(conference=conference, user=user) - - with patch("grants.tasks.EmailTemplate") as mock_email_template: - send_grant_reply_waiting_list_email(grant_id=grant.id) - - mock_email_template.objects.for_conference().get_by_identifier().send_email.assert_called_once_with( - recipient=user, - placeholders={ - "user_name": "Marco Acierno", - "conference_name": grant.conference.name.localize("en"), - "grants_update_deadline": "1 March 2023", - "reply_url": "https://pycon.it/grants/reply/", - }, + + EmailTemplateFactory( + conference=grant.conference, + identifier=EmailTemplateIdentifier.grant_waiting_list, ) - -def test_handle_grant_reply_sent_reminder(settings): + send_grant_reply_waiting_list_email(grant_id=grant.id) + + # Verify that the correct email template was used and email was sent + emails_sent = sent_emails() + assert emails_sent.count() == 1 + + sent_email = emails_sent.first() + assert sent_email.email_template.identifier == EmailTemplateIdentifier.grant_waiting_list + assert sent_email.email_template.conference == grant.conference + assert sent_email.recipient == user + + # Verify placeholders were processed correctly + assert sent_email.placeholders["user_name"] == "Marco Acierno" + assert sent_email.placeholders["conference_name"] == grant.conference.name.localize("en") + assert sent_email.placeholders["grants_update_deadline"] == "1 March 2023" + assert sent_email.placeholders["reply_url"] == "https://pycon.it/grants/reply/" + + +def test_handle_grant_reply_sent_reminder(settings, sent_emails): + from notifications.tests.factories import EmailTemplateFactory + from notifications.models import EmailTemplateIdentifier + settings.FRONTEND_URL = "https://pycon.it" conference = ConferenceFactory( start=datetime(2023, 5, 2, tzinfo=timezone.utc), @@ -93,29 +120,41 @@ def test_handle_grant_reply_sent_reminder(settings): total_amount=680, user=user, ) - - with patch("grants.tasks.EmailTemplate") as mock_email_template: - send_grant_reply_approved_email(grant_id=grant.id, is_reminder=True) - - mock_email_template.objects.for_conference().get_by_identifier().send_email.assert_called_once_with( - recipient=user, - placeholders={ - "user_name": "Marco Acierno", - "conference_name": grant.conference.name.localize("en"), - "start_date": "2 May", - "end_date": "6 May", - "deadline_date_time": "1 February 2023 23:59 UTC", - "deadline_date": "1 February 2023", - "reply_url": "https://pycon.it/grants/reply/", - "visa_page_link": "https://pycon.it/visa", - "has_approved_travel": False, - "has_approved_accommodation": False, - "is_reminder": True, - }, + + EmailTemplateFactory( + conference=grant.conference, + identifier=EmailTemplateIdentifier.grant_approved, ) - -def test_handle_grant_approved_ticket_travel_accommodation_reply_sent(settings): + send_grant_reply_approved_email(grant_id=grant.id, is_reminder=True) + + # Verify that the correct email template was used and email was sent + emails_sent = sent_emails() + assert emails_sent.count() == 1 + + sent_email = emails_sent.first() + assert sent_email.email_template.identifier == EmailTemplateIdentifier.grant_approved + assert sent_email.email_template.conference == grant.conference + assert sent_email.recipient == user + + # Verify placeholders were processed correctly + assert sent_email.placeholders["user_name"] == "Marco Acierno" + assert sent_email.placeholders["conference_name"] == grant.conference.name.localize("en") + assert sent_email.placeholders["start_date"] == "2 May" + assert sent_email.placeholders["end_date"] == "6 May" + assert sent_email.placeholders["deadline_date_time"] == "1 February 2023 23:59 UTC" + assert sent_email.placeholders["deadline_date"] == "1 February 2023" + assert sent_email.placeholders["reply_url"] == "https://pycon.it/grants/reply/" + assert sent_email.placeholders["visa_page_link"] == "https://pycon.it/visa" + assert sent_email.placeholders["has_approved_travel"] == False + assert sent_email.placeholders["has_approved_accommodation"] == False + assert sent_email.placeholders["is_reminder"] == True + + +def test_handle_grant_approved_ticket_travel_accommodation_reply_sent(settings, sent_emails): + from notifications.tests.factories import EmailTemplateFactory + from notifications.models import EmailTemplateIdentifier + settings.FRONTEND_URL = "https://pycon.it" conference = ConferenceFactory( @@ -136,28 +175,37 @@ def test_handle_grant_approved_ticket_travel_accommodation_reply_sent(settings): travel_amount=680, user=user, ) - - with patch("grants.tasks.EmailTemplate") as mock_email_template: - send_grant_reply_approved_email(grant_id=grant.id, is_reminder=False) - - mock_email_template.objects.for_conference().get_by_identifier().send_email.assert_called_once_with( - recipient=user, - placeholders={ - "user_name": "Marco Acierno", - "conference_name": grant.conference.name.localize("en"), - "start_date": "2 May", - "end_date": "6 May", - "travel_amount": "680", - "deadline_date_time": "1 February 2023 23:59 UTC", - "deadline_date": "1 February 2023", - "reply_url": "https://pycon.it/grants/reply/", - "visa_page_link": "https://pycon.it/visa", - "has_approved_travel": True, - "has_approved_accommodation": True, - "is_reminder": False, - }, + + EmailTemplateFactory( + conference=grant.conference, + identifier=EmailTemplateIdentifier.grant_approved, ) + send_grant_reply_approved_email(grant_id=grant.id, is_reminder=False) + + # Verify that the correct email template was used and email was sent + emails_sent = sent_emails() + assert emails_sent.count() == 1 + + sent_email = emails_sent.first() + assert sent_email.email_template.identifier == EmailTemplateIdentifier.grant_approved + assert sent_email.email_template.conference == grant.conference + assert sent_email.recipient == user + + # Verify placeholders were processed correctly + assert sent_email.placeholders["user_name"] == "Marco Acierno" + assert sent_email.placeholders["conference_name"] == grant.conference.name.localize("en") + assert sent_email.placeholders["start_date"] == "2 May" + assert sent_email.placeholders["end_date"] == "6 May" + assert sent_email.placeholders["travel_amount"] == "680" + assert sent_email.placeholders["deadline_date_time"] == "1 February 2023 23:59 UTC" + assert sent_email.placeholders["deadline_date"] == "1 February 2023" + assert sent_email.placeholders["reply_url"] == "https://pycon.it/grants/reply/" + assert sent_email.placeholders["visa_page_link"] == "https://pycon.it/visa" + assert sent_email.placeholders["has_approved_travel"] == True + assert sent_email.placeholders["has_approved_accommodation"] == True + assert sent_email.placeholders["is_reminder"] == False + def test_handle_grant_approved_ticket_travel_accommodation_fails_with_no_amount( settings, @@ -189,7 +237,10 @@ def test_handle_grant_approved_ticket_travel_accommodation_fails_with_no_amount( send_grant_reply_approved_email(grant_id=grant.id, is_reminder=False) -def test_handle_grant_approved_ticket_only_reply_sent(settings): +def test_handle_grant_approved_ticket_only_reply_sent(settings, sent_emails): + from notifications.tests.factories import EmailTemplateFactory + from notifications.models import EmailTemplateIdentifier + settings.FRONTEND_URL = "https://pycon.it" conference = ConferenceFactory( @@ -210,29 +261,41 @@ def test_handle_grant_approved_ticket_only_reply_sent(settings): total_amount=680, user=user, ) - - with patch("grants.tasks.EmailTemplate") as mock_email_template: - send_grant_reply_approved_email(grant_id=grant.id, is_reminder=False) - - mock_email_template.objects.for_conference().get_by_identifier().send_email.assert_called_once_with( - recipient=user, - placeholders={ - "user_name": "Marco Acierno", - "conference_name": grant.conference.name.localize("en"), - "start_date": "2 May", - "end_date": "6 May", - "deadline_date_time": "1 February 2023 23:59 UTC", - "deadline_date": "1 February 2023", - "reply_url": "https://pycon.it/grants/reply/", - "visa_page_link": "https://pycon.it/visa", - "has_approved_travel": False, - "has_approved_accommodation": False, - "is_reminder": False, - }, + + EmailTemplateFactory( + conference=grant.conference, + identifier=EmailTemplateIdentifier.grant_approved, ) - -def test_handle_grant_approved_travel_reply_sent(settings): + send_grant_reply_approved_email(grant_id=grant.id, is_reminder=False) + + # Verify that the correct email template was used and email was sent + emails_sent = sent_emails() + assert emails_sent.count() == 1 + + sent_email = emails_sent.first() + assert sent_email.email_template.identifier == EmailTemplateIdentifier.grant_approved + assert sent_email.email_template.conference == grant.conference + assert sent_email.recipient == user + + # Verify placeholders were processed correctly + assert sent_email.placeholders["user_name"] == "Marco Acierno" + assert sent_email.placeholders["conference_name"] == grant.conference.name.localize("en") + assert sent_email.placeholders["start_date"] == "2 May" + assert sent_email.placeholders["end_date"] == "6 May" + assert sent_email.placeholders["deadline_date_time"] == "1 February 2023 23:59 UTC" + assert sent_email.placeholders["deadline_date"] == "1 February 2023" + assert sent_email.placeholders["reply_url"] == "https://pycon.it/grants/reply/" + assert sent_email.placeholders["visa_page_link"] == "https://pycon.it/visa" + assert sent_email.placeholders["has_approved_travel"] == False + assert sent_email.placeholders["has_approved_accommodation"] == False + assert sent_email.placeholders["is_reminder"] == False + + +def test_handle_grant_approved_travel_reply_sent(settings, sent_emails): + from notifications.tests.factories import EmailTemplateFactory + from notifications.models import EmailTemplateIdentifier + settings.FRONTEND_URL = "https://pycon.it" conference = ConferenceFactory( @@ -254,30 +317,42 @@ def test_handle_grant_approved_travel_reply_sent(settings): travel_amount=400, user=user, ) - - with patch("grants.tasks.EmailTemplate") as mock_email_template: - send_grant_reply_approved_email(grant_id=grant.id, is_reminder=False) - - mock_email_template.objects.for_conference().get_by_identifier().send_email.assert_called_once_with( - recipient=user, - placeholders={ - "user_name": "Marco Acierno", - "conference_name": grant.conference.name.localize("en"), - "start_date": "2 May", - "end_date": "6 May", - "deadline_date_time": "1 February 2023 23:59 UTC", - "deadline_date": "1 February 2023", - "reply_url": "https://pycon.it/grants/reply/", - "visa_page_link": "https://pycon.it/visa", - "has_approved_travel": True, - "has_approved_accommodation": False, - "travel_amount": "400", - "is_reminder": False, - }, + + EmailTemplateFactory( + conference=grant.conference, + identifier=EmailTemplateIdentifier.grant_approved, ) - -def test_send_grant_reply_waiting_list_update_email(settings): + send_grant_reply_approved_email(grant_id=grant.id, is_reminder=False) + + # Verify that the correct email template was used and email was sent + emails_sent = sent_emails() + assert emails_sent.count() == 1 + + sent_email = emails_sent.first() + assert sent_email.email_template.identifier == EmailTemplateIdentifier.grant_approved + assert sent_email.email_template.conference == grant.conference + assert sent_email.recipient == user + + # Verify placeholders were processed correctly + assert sent_email.placeholders["user_name"] == "Marco Acierno" + assert sent_email.placeholders["conference_name"] == grant.conference.name.localize("en") + assert sent_email.placeholders["start_date"] == "2 May" + assert sent_email.placeholders["end_date"] == "6 May" + assert sent_email.placeholders["deadline_date_time"] == "1 February 2023 23:59 UTC" + assert sent_email.placeholders["deadline_date"] == "1 February 2023" + assert sent_email.placeholders["reply_url"] == "https://pycon.it/grants/reply/" + assert sent_email.placeholders["visa_page_link"] == "https://pycon.it/visa" + assert sent_email.placeholders["has_approved_travel"] == True + assert sent_email.placeholders["has_approved_accommodation"] == False + assert sent_email.placeholders["travel_amount"] == "400" + assert sent_email.placeholders["is_reminder"] == False + + +def test_send_grant_reply_waiting_list_update_email(settings, sent_emails): + from notifications.tests.factories import EmailTemplateFactory + from notifications.models import EmailTemplateIdentifier + settings.FRONTEND_URL = "https://pycon.it" user = UserFactory( full_name="Marco Acierno", @@ -296,18 +371,27 @@ def test_send_grant_reply_waiting_list_update_email(settings): }, ) conference_name = grant.conference.name.localize("en") + + EmailTemplateFactory( + conference=grant.conference, + identifier=EmailTemplateIdentifier.grant_waiting_list_update, + ) - with patch("grants.tasks.EmailTemplate") as mock_email_template: - send_grant_reply_waiting_list_update_email( - grant_id=grant.id, - ) - - mock_email_template.objects.for_conference().get_by_identifier().send_email.assert_called_once_with( - recipient=user, - placeholders={ - "user_name": "Marco Acierno", - "conference_name": conference_name, - "grants_update_deadline": "1 March 2023", - "reply_url": "https://pycon.it/grants/reply/", - }, + send_grant_reply_waiting_list_update_email( + grant_id=grant.id, ) + + # Verify that the correct email template was used and email was sent + emails_sent = sent_emails() + assert emails_sent.count() == 1 + + sent_email = emails_sent.first() + assert sent_email.email_template.identifier == EmailTemplateIdentifier.grant_waiting_list_update + assert sent_email.email_template.conference == grant.conference + assert sent_email.recipient == user + + # Verify placeholders were processed correctly + assert sent_email.placeholders["user_name"] == "Marco Acierno" + assert sent_email.placeholders["conference_name"] == conference_name + assert sent_email.placeholders["grants_update_deadline"] == "1 March 2023" + assert sent_email.placeholders["reply_url"] == "https://pycon.it/grants/reply/" diff --git a/backend/schedule/tests/test_tasks.py b/backend/schedule/tests/test_tasks.py index 1b9b63a907..fa06c65eb9 100644 --- a/backend/schedule/tests/test_tasks.py +++ b/backend/schedule/tests/test_tasks.py @@ -41,7 +41,7 @@ @override_settings(FRONTEND_URL="https://frontend/") -def test_send_schedule_invitation_email(): +def test_send_schedule_invitation_email(sent_emails): user = UserFactory( full_name="Marco Acierno", email="marco@placeholder.it", @@ -61,29 +61,26 @@ def test_send_schedule_invitation_email(): ) EmailTemplateFactory(identifier=EmailTemplateIdentifier.proposal_scheduled) - with patch("schedule.tasks.EmailTemplate") as mock_email_template: - send_schedule_invitation_email( - schedule_item_id=schedule_item.id, - is_reminder=False, - ) - - mock_email_template.objects.for_conference.assert_called_once_with( - schedule_item.conference - ) - mock_email_template.objects.for_conference().get_by_identifier.assert_called_once_with( - EmailTemplateIdentifier.proposal_scheduled - ) - - mock_email_template.objects.for_conference().get_by_identifier().send_email.assert_called_once_with( - recipient=user, - placeholders={ - "proposal_title": "Title Submission", - "conference_name": "Conf", - "invitation_url": f"https://frontend/schedule/invitation/{schedule_item.submission.hashid}", - "speaker_name": "Marco Acierno", - "is_reminder": False, - }, - ) + send_schedule_invitation_email( + schedule_item_id=schedule_item.id, + is_reminder=False, + ) + + # Verify that the correct email template was used and email was sent + emails_sent = sent_emails() + assert emails_sent.count() == 1 + + sent_email = emails_sent.first() + assert sent_email.email_template.identifier == EmailTemplateIdentifier.proposal_scheduled + assert sent_email.email_template.conference == schedule_item.conference + assert sent_email.recipient == user + + # Verify placeholders were processed correctly + assert sent_email.placeholders["proposal_title"] == "Title Submission" + assert sent_email.placeholders["conference_name"] == "Conf" + assert sent_email.placeholders["invitation_url"] == f"https://frontend/schedule/invitation/{schedule_item.submission.hashid}" + assert sent_email.placeholders["speaker_name"] == "Marco Acierno" + assert sent_email.placeholders["is_reminder"] == False schedule_item.refresh_from_db() @@ -91,7 +88,7 @@ def test_send_schedule_invitation_email(): @override_settings(FRONTEND_URL="https://frontend/") -def test_send_schedule_invitation_email_reminder(): +def test_send_schedule_invitation_email_reminder(sent_emails): user = UserFactory( full_name="Marco Acierno", email="marco@placeholder.it", @@ -111,26 +108,30 @@ def test_send_schedule_invitation_email_reminder(): ) EmailTemplateFactory(identifier=EmailTemplateIdentifier.proposal_scheduled) - with patch("schedule.tasks.EmailTemplate.send_email") as email_mock: - send_schedule_invitation_email( - schedule_item_id=schedule_item.id, - is_reminder=True, - ) - - email_mock.assert_called_once_with( - recipient=user, - placeholders={ - "proposal_title": "Title Submission", - "conference_name": "Conf", - "invitation_url": f"https://frontend/schedule/invitation/{schedule_item.submission.hashid}", - "speaker_name": "Marco Acierno", - "is_reminder": True, - }, - ) + send_schedule_invitation_email( + schedule_item_id=schedule_item.id, + is_reminder=True, + ) + + # Verify that the correct email template was used and email was sent + emails_sent = sent_emails() + assert emails_sent.count() == 1 + + sent_email = emails_sent.first() + assert sent_email.email_template.identifier == EmailTemplateIdentifier.proposal_scheduled + assert sent_email.email_template.conference == schedule_item.conference + assert sent_email.recipient == user + + # Verify placeholders were processed correctly + assert sent_email.placeholders["proposal_title"] == "Title Submission" + assert sent_email.placeholders["conference_name"] == "Conf" + assert sent_email.placeholders["invitation_url"] == f"https://frontend/schedule/invitation/{schedule_item.submission.hashid}" + assert sent_email.placeholders["speaker_name"] == "Marco Acierno" + assert sent_email.placeholders["is_reminder"] == True @override_settings(FRONTEND_URL="https://frontend/") -def test_send_submission_time_slot_changed_email(): +def test_send_submission_time_slot_changed_email(sent_emails): user = UserFactory( full_name="Marco Acierno", email="marco@placeholder.it", @@ -143,19 +144,28 @@ def test_send_submission_time_slot_changed_email(): conference__name=LazyI18nString({"en": "Conf"}), type=ScheduleItem.TYPES.talk, ) + + EmailTemplateFactory( + conference=schedule_item.conference, + identifier=EmailTemplateIdentifier.proposal_scheduled_time_changed, + ) - with patch("schedule.tasks.EmailTemplate") as mock_email_template: - send_submission_time_slot_changed_email(schedule_item_id=schedule_item.id) + send_submission_time_slot_changed_email(schedule_item_id=schedule_item.id) - mock_email_template.objects.for_conference().get_by_identifier().send_email.assert_called_once_with( - recipient=schedule_item.submission.speaker, - placeholders={ - "proposal_title": "Title Submission", - "speaker_name": "Marco Acierno", - "invitation_url": f"https://frontend/schedule/invitation/{schedule_item.submission.hashid}", - "conference_name": "Conf", - }, - ) + # Verify that the correct email template was used and email was sent + emails_sent = sent_emails() + assert emails_sent.count() == 1 + + sent_email = emails_sent.first() + assert sent_email.email_template.identifier == EmailTemplateIdentifier.proposal_scheduled_time_changed + assert sent_email.email_template.conference == schedule_item.conference + assert sent_email.recipient == schedule_item.submission.speaker + + # Verify placeholders were processed correctly + assert sent_email.placeholders["proposal_title"] == "Title Submission" + assert sent_email.placeholders["speaker_name"] == "Marco Acierno" + assert sent_email.placeholders["invitation_url"] == f"https://frontend/schedule/invitation/{schedule_item.submission.hashid}" + assert sent_email.placeholders["conference_name"] == "Conf" @pytest.mark.parametrize( @@ -194,7 +204,7 @@ def test_notify_new_schedule_invitation_answer_slack(status): @override_settings(SPEAKERS_EMAIL_ADDRESS="reply") @pytest.mark.parametrize("has_ticket", [True, False]) def test_send_speaker_communication_email_to_speakers_without_ticket( - requests_mock, has_ticket, settings + requests_mock, has_ticket, settings, sent_emails ): conference = ConferenceFactory() @@ -204,39 +214,49 @@ def test_send_speaker_communication_email_to_speakers_without_ticket( name="Marco", username="marco", ) + + EmailTemplateFactory( + conference=conference, + identifier=EmailTemplateIdentifier.speaker_communication, + ) requests_mock.post( f"{settings.PRETIX_API}organizers/{conference.pretix_organizer_id}/events/{conference.pretix_event_id}/tickets/attendee-has-ticket/", json={"user_has_admission_ticket": has_ticket}, ) - with patch("schedule.tasks.EmailTemplate") as mock_email_template: - send_speaker_communication_email( - subject="test subject", - body="test body", - user_id=user.id, - conference_id=conference.id, - only_speakers_without_ticket=True, - ) + send_speaker_communication_email( + subject="test subject", + body="test body", + user_id=user.id, + conference_id=conference.id, + only_speakers_without_ticket=True, + ) + # Verify that the correct email template was used and email was sent + emails_sent = sent_emails() + if not has_ticket: - mock_email_template.objects.for_conference().get_by_identifier().send_email.assert_called_once_with( - recipient=user, - placeholders={ - "user_name": "Marco Acierno", - "conference_name": conference.name.localize("en"), - "body": "test body", - "subject": "test subject", - }, - ) + assert emails_sent.count() == 1 + + sent_email = emails_sent.first() + assert sent_email.email_template.identifier == EmailTemplateIdentifier.speaker_communication + assert sent_email.email_template.conference == conference + assert sent_email.recipient == user + + # Verify placeholders were processed correctly + assert sent_email.placeholders["user_name"] == "Marco Acierno" + assert sent_email.placeholders["conference_name"] == conference.name.localize("en") + assert sent_email.placeholders["body"] == "test body" + assert sent_email.placeholders["subject"] == "test subject" else: - mock_email_template.objects.for_conference().get_by_identifier().send_email.assert_not_called() + assert emails_sent.count() == 0 @override_settings(SPEAKERS_EMAIL_ADDRESS="reply") @pytest.mark.parametrize("has_ticket", [True, False]) def test_send_speaker_communication_email_to_everyone( - settings, requests_mock, has_ticket + settings, requests_mock, has_ticket, sent_emails ): user = UserFactory( full_name="Marco Acierno", @@ -245,30 +265,39 @@ def test_send_speaker_communication_email_to_everyone( username="marco", ) conference = ConferenceFactory() + + EmailTemplateFactory( + conference=conference, + identifier=EmailTemplateIdentifier.speaker_communication, + ) requests_mock.post( f"{settings.PRETIX_API}organizers/{conference.pretix_organizer_id}/events/{conference.pretix_event_id}/tickets/attendee-has-ticket/", json={"user_has_admission_ticket": has_ticket}, ) - with patch("schedule.tasks.EmailTemplate") as mock_email_template: - send_speaker_communication_email( - subject="test subject", - body="test body", - user_id=user.id, - conference_id=conference.id, - only_speakers_without_ticket=False, - ) - - mock_email_template.objects.for_conference().get_by_identifier().send_email.assert_called_once_with( - recipient=user, - placeholders={ - "user_name": "Marco Acierno", - "body": "test body", - "conference_name": conference.name.localize("en"), - "subject": "test subject", - }, - ) + send_speaker_communication_email( + subject="test subject", + body="test body", + user_id=user.id, + conference_id=conference.id, + only_speakers_without_ticket=False, + ) + + # Verify that the correct email template was used and email was sent + emails_sent = sent_emails() + assert emails_sent.count() == 1 + + sent_email = emails_sent.first() + assert sent_email.email_template.identifier == EmailTemplateIdentifier.speaker_communication + assert sent_email.email_template.conference == conference + assert sent_email.recipient == user + + # Verify placeholders were processed correctly + assert sent_email.placeholders["user_name"] == "Marco Acierno" + assert sent_email.placeholders["body"] == "test body" + assert sent_email.placeholders["conference_name"] == conference.name.localize("en") + assert sent_email.placeholders["subject"] == "test subject" @override_settings(PLAIN_API="https://example.org/plain") diff --git a/backend/sponsors/tests/test_tasks.py b/backend/sponsors/tests/test_tasks.py index 55f4890293..2721a63c07 100644 --- a/backend/sponsors/tests/test_tasks.py +++ b/backend/sponsors/tests/test_tasks.py @@ -8,24 +8,36 @@ pytestmark = pytest.mark.django_db -def test_send_sponsor_brochure_task(): +def test_send_sponsor_brochure_task(sent_emails): + from notifications.tests.factories import EmailTemplateFactory + from notifications.models import EmailTemplateIdentifier + sponsor_lead = SponsorLeadFactory() + + EmailTemplateFactory( + conference=sponsor_lead.conference, + identifier=EmailTemplateIdentifier.sponsorship_brochure, + ) signer = Signer() view_brochure_path = reverse("view-brochure", args=[sponsor_lead.id]) signed_url = signer.sign(view_brochure_path) signature = signed_url.split(signer.sep)[-1] - with patch("sponsors.tasks.EmailTemplate") as mock_email_template: - send_sponsor_brochure(sponsor_lead.id) - - mock_email_template.objects.for_conference().get_by_identifier().send_email.assert_called_once_with( - recipient_email=sponsor_lead.email, - placeholders={ - "brochure_url": f"https://admin.pycon.it{view_brochure_path}?sig={signature}", - "conference_name": sponsor_lead.conference.name.localize("en"), - }, - ) + send_sponsor_brochure(sponsor_lead.id) + + # Verify that the correct email template was used and email was sent + emails_sent = sent_emails() + assert emails_sent.count() == 1 + + sent_email = emails_sent.first() + assert sent_email.email_template.identifier == EmailTemplateIdentifier.sponsorship_brochure + assert sent_email.email_template.conference == sponsor_lead.conference + assert sent_email.recipient_email == sponsor_lead.email + + # Verify placeholders were processed correctly + assert sent_email.placeholders["brochure_url"] == f"https://admin.pycon.it{view_brochure_path}?sig={signature}" + assert sent_email.placeholders["conference_name"] == sponsor_lead.conference.name.localize("en") def test_notify_new_sponsor_lead_via_slack(mocker): diff --git a/backend/submissions/tests/test_tasks.py b/backend/submissions/tests/test_tasks.py index ed3ad49daf..c101e19819 100644 --- a/backend/submissions/tests/test_tasks.py +++ b/backend/submissions/tests/test_tasks.py @@ -45,7 +45,7 @@ def test_handle_new_cfp_submission(): assert "Marco Acierno" in str(slack_mock.send_message.mock_calls[0]) -def test_send_proposal_rejected_email(): +def test_send_proposal_rejected_email(sent_emails): submission = SubmissionFactory( conference__name=LazyI18nString({"en": "Conf"}), title=LazyI18nString({"en": "Title"}), @@ -55,26 +55,31 @@ def test_send_proposal_rejected_email(): EmailTemplateFactory( conference=submission.conference, - identifier=EmailTemplateIdentifier.proposal_scheduled, + identifier=EmailTemplateIdentifier.proposal_rejected, ) - with patch("submissions.tasks.EmailTemplate") as mock_email_template: - send_proposal_rejected_email( - proposal_id=submission.id, - ) - - mock_email_template.objects.for_conference().get_by_identifier().send_email.assert_called_once_with( - recipient=submission.speaker, - placeholders={ - "proposal_title": "Title", - "proposal_type": submission.type.name, - "conference_name": "Conf", - "speaker_name": "Marco", - }, + send_proposal_rejected_email( + proposal_id=submission.id, ) - -def test_send_proposal_in_waiting_list_email(): + # Verify that the correct email template was used and email was sent + emails_sent = sent_emails() + assert emails_sent.count() == 1 + + sent_email = emails_sent.first() + assert sent_email.email_template.identifier == EmailTemplateIdentifier.proposal_rejected + assert sent_email.email_template.conference == submission.conference + assert sent_email.recipient == submission.speaker + assert sent_email.recipient_email == submission.speaker.email + + # Verify placeholders were processed correctly + assert sent_email.placeholders["proposal_title"] == "Title" + assert sent_email.placeholders["proposal_type"] == submission.type.name + assert sent_email.placeholders["conference_name"] == "Conf" + assert sent_email.placeholders["speaker_name"] == "Marco" + + +def test_send_proposal_in_waiting_list_email(sent_emails): submission = SubmissionFactory( conference__name=LazyI18nString({"en": "Conf"}), title=LazyI18nString({"en": "Title"}), @@ -87,17 +92,22 @@ def test_send_proposal_in_waiting_list_email(): identifier=EmailTemplateIdentifier.proposal_in_waiting_list, ) - with patch("submissions.tasks.EmailTemplate") as mock_email_template: - send_proposal_in_waiting_list_email( - proposal_id=submission.id, - ) - - mock_email_template.objects.for_conference().get_by_identifier().send_email.assert_called_once_with( - recipient=submission.speaker, - placeholders={ - "proposal_title": "Title", - "proposal_type": submission.type.name, - "conference_name": "Conf", - "speaker_name": "Marco", - }, + send_proposal_in_waiting_list_email( + proposal_id=submission.id, ) + + # Verify that the correct email template was used and email was sent + emails_sent = sent_emails() + assert emails_sent.count() == 1 + + sent_email = emails_sent.first() + assert sent_email.email_template.identifier == EmailTemplateIdentifier.proposal_in_waiting_list + assert sent_email.email_template.conference == submission.conference + assert sent_email.recipient == submission.speaker + assert sent_email.recipient_email == submission.speaker.email + + # Verify placeholders were processed correctly + assert sent_email.placeholders["proposal_title"] == "Title" + assert sent_email.placeholders["proposal_type"] == submission.type.name + assert sent_email.placeholders["conference_name"] == "Conf" + assert sent_email.placeholders["speaker_name"] == "Marco" diff --git a/backend/visa/tests/test_tasks.py b/backend/visa/tests/test_tasks.py index 0cc251dbf5..c90a6a788d 100644 --- a/backend/visa/tests/test_tasks.py +++ b/backend/visa/tests/test_tasks.py @@ -383,38 +383,42 @@ def test_notify_new_invitation_letter_request_on_slack(mocker): assert kwargs["channel_id"] == "S123" -def test_send_invitation_letter_via_email(): +def test_send_invitation_letter_via_email(sent_emails): + from notifications.tests.factories import EmailTemplateFactory + invitation_letter_request = InvitationLetterRequestFactory( requester__full_name="Marco", ) - - with patch("visa.tasks.EmailTemplate") as mock_email_template: - send_invitation_letter_via_email( - invitation_letter_request_id=invitation_letter_request.id - ) - - mock_email_template.objects.for_conference.assert_called_once_with( - invitation_letter_request.conference + + EmailTemplateFactory( + conference=invitation_letter_request.conference, + identifier=EmailTemplateIdentifier.visa_invitation_letter_download, ) - mock_email_template.objects.for_conference().get_by_identifier.assert_called_once_with( - EmailTemplateIdentifier.visa_invitation_letter_download + + send_invitation_letter_via_email( + invitation_letter_request_id=invitation_letter_request.id ) + # Verify that the correct email template was used and email was sent + emails_sent = sent_emails() + assert emails_sent.count() == 1 + + sent_email = emails_sent.first() + assert sent_email.email_template.identifier == EmailTemplateIdentifier.visa_invitation_letter_download + assert sent_email.email_template.conference == invitation_letter_request.conference + assert sent_email.recipient_email == invitation_letter_request.email + signer = Signer() url_path = reverse( "download-invitation-letter", args=[invitation_letter_request.id] ) signed_url = signer.sign(url_path) signature = signed_url.split(signer.sep)[-1] - - mock_email_template.objects.for_conference().get_by_identifier().send_email.assert_called_once_with( - recipient_email=invitation_letter_request.email, - placeholders={ - "invitation_letter_download_url": f"https://admin.pycon.it{url_path}?sig={signature}", - "has_grant": False, - "user_name": "Marco", - }, - ) + + # Verify placeholders were processed correctly + assert sent_email.placeholders["invitation_letter_download_url"] == f"https://admin.pycon.it{url_path}?sig={signature}" + assert sent_email.placeholders["has_grant"] == False + assert sent_email.placeholders["user_name"] == "Marco" invitation_letter_request.refresh_from_db()