From 91577e13de3168cfecfaa81b3163cfdc828c5f86 Mon Sep 17 00:00:00 2001 From: zandercymatics <141044360+zandercymatics@users.noreply.github.com> Date: Wed, 20 Dec 2023 15:27:41 -0700 Subject: [PATCH 01/34] Add an excessive number of boto3_mocking mocks --- src/registrar/tests/test_models.py | 287 +++++++++++++++++++---------- 1 file changed, 190 insertions(+), 97 deletions(-) diff --git a/src/registrar/tests/test_models.py b/src/registrar/tests/test_models.py index 0e0839382..d2f1a5550 100644 --- a/src/registrar/tests/test_models.py +++ b/src/registrar/tests/test_models.py @@ -1,6 +1,6 @@ from django.test import TestCase from django.db.utils import IntegrityError -from unittest.mock import patch +from unittest.mock import MagicMock, patch from registrar.models import ( Contact, @@ -97,17 +97,23 @@ class TestDomainApplication(TestCase): def test_status_fsm_submit_fail(self): user, _ = User.objects.get_or_create() application = DomainApplication.objects.create(creator=user) - with self.assertRaises(ValueError): - # can't submit an application with a null domain name - application.submit() + + mock_client = MagicMock() + with boto3_mocking.clients.handler_for("sesv2", mock_client): + with self.assertRaises(ValueError): + # can't submit an application with a null domain name + application.submit() def test_status_fsm_submit_succeed(self): user, _ = User.objects.get_or_create() site = DraftDomain.objects.create(name="igorville.gov") application = DomainApplication.objects.create(creator=user, requested_domain=site) + # no submitter email so this emits a log warning - with less_console_noise(): - application.submit() + mock_client = MagicMock() + with boto3_mocking.clients.handler_for("sesv2", mock_client): + with less_console_noise(): + application.submit() self.assertEqual(application.status, application.ApplicationStatus.SUBMITTED) def test_submit_sends_email(self): @@ -121,7 +127,10 @@ class TestDomainApplication(TestCase): submitter=contact, ) application.save() - application.submit() + + mock_client = MagicMock() + with boto3_mocking.clients.handler_for("sesv2", mock_client): + application.submit() # check to see if an email was sent self.assertGreater( @@ -141,8 +150,10 @@ class TestDomainApplication(TestCase): application = completed_application(status=DomainApplication.ApplicationStatus.SUBMITTED) - with self.assertRaises(TransitionNotAllowed): - application.submit() + mock_client = MagicMock() + with boto3_mocking.clients.handler_for("sesv2", mock_client): + with self.assertRaises(TransitionNotAllowed): + application.submit() def test_transition_not_allowed_in_review_submitted(self): """Create an application with status in review and call submit @@ -150,8 +161,10 @@ class TestDomainApplication(TestCase): application = completed_application(status=DomainApplication.ApplicationStatus.IN_REVIEW) - with self.assertRaises(TransitionNotAllowed): - application.submit() + mock_client = MagicMock() + with boto3_mocking.clients.handler_for("sesv2", mock_client): + with self.assertRaises(TransitionNotAllowed): + application.submit() def test_transition_not_allowed_approved_submitted(self): """Create an application with status approved and call submit @@ -159,8 +172,10 @@ class TestDomainApplication(TestCase): application = completed_application(status=DomainApplication.ApplicationStatus.APPROVED) - with self.assertRaises(TransitionNotAllowed): - application.submit() + mock_client = MagicMock() + with boto3_mocking.clients.handler_for("sesv2", mock_client): + with self.assertRaises(TransitionNotAllowed): + application.submit() def test_transition_not_allowed_rejected_submitted(self): """Create an application with status rejected and call submit @@ -168,8 +183,10 @@ class TestDomainApplication(TestCase): application = completed_application(status=DomainApplication.ApplicationStatus.REJECTED) - with self.assertRaises(TransitionNotAllowed): - application.submit() + mock_client = MagicMock() + with boto3_mocking.clients.handler_for("sesv2", mock_client): + with self.assertRaises(TransitionNotAllowed): + application.submit() def test_transition_not_allowed_ineligible_submitted(self): """Create an application with status ineligible and call submit @@ -177,8 +194,10 @@ class TestDomainApplication(TestCase): application = completed_application(status=DomainApplication.ApplicationStatus.INELIGIBLE) - with self.assertRaises(TransitionNotAllowed): - application.submit() + mock_client = MagicMock() + with boto3_mocking.clients.handler_for("sesv2", mock_client): + with self.assertRaises(TransitionNotAllowed): + application.submit() def test_transition_not_allowed_started_in_review(self): """Create an application with status started and call in_review @@ -186,8 +205,10 @@ class TestDomainApplication(TestCase): application = completed_application(status=DomainApplication.ApplicationStatus.STARTED) - with self.assertRaises(TransitionNotAllowed): - application.in_review() + mock_client = MagicMock() + with boto3_mocking.clients.handler_for("sesv2", mock_client): + with self.assertRaises(TransitionNotAllowed): + application.in_review() def test_transition_not_allowed_in_review_in_review(self): """Create an application with status in review and call in_review @@ -195,8 +216,10 @@ class TestDomainApplication(TestCase): application = completed_application(status=DomainApplication.ApplicationStatus.IN_REVIEW) - with self.assertRaises(TransitionNotAllowed): - application.in_review() + mock_client = MagicMock() + with boto3_mocking.clients.handler_for("sesv2", mock_client): + with self.assertRaises(TransitionNotAllowed): + application.in_review() def test_transition_not_allowed_approved_in_review(self): """Create an application with status approved and call in_review @@ -204,8 +227,10 @@ class TestDomainApplication(TestCase): application = completed_application(status=DomainApplication.ApplicationStatus.APPROVED) - with self.assertRaises(TransitionNotAllowed): - application.in_review() + mock_client = MagicMock() + with boto3_mocking.clients.handler_for("sesv2", mock_client): + with self.assertRaises(TransitionNotAllowed): + application.in_review() def test_transition_not_allowed_action_needed_in_review(self): """Create an application with status action needed and call in_review @@ -213,8 +238,10 @@ class TestDomainApplication(TestCase): application = completed_application(status=DomainApplication.ApplicationStatus.ACTION_NEEDED) - with self.assertRaises(TransitionNotAllowed): - application.in_review() + mock_client = MagicMock() + with boto3_mocking.clients.handler_for("sesv2", mock_client): + with self.assertRaises(TransitionNotAllowed): + application.in_review() def test_transition_not_allowed_rejected_in_review(self): """Create an application with status rejected and call in_review @@ -222,8 +249,10 @@ class TestDomainApplication(TestCase): application = completed_application(status=DomainApplication.ApplicationStatus.REJECTED) - with self.assertRaises(TransitionNotAllowed): - application.in_review() + mock_client = MagicMock() + with boto3_mocking.clients.handler_for("sesv2", mock_client): + with self.assertRaises(TransitionNotAllowed): + application.in_review() def test_transition_not_allowed_withdrawn_in_review(self): """Create an application with status withdrawn and call in_review @@ -231,8 +260,10 @@ class TestDomainApplication(TestCase): application = completed_application(status=DomainApplication.ApplicationStatus.WITHDRAWN) - with self.assertRaises(TransitionNotAllowed): - application.in_review() + mock_client = MagicMock() + with boto3_mocking.clients.handler_for("sesv2", mock_client): + with self.assertRaises(TransitionNotAllowed): + application.in_review() def test_transition_not_allowed_ineligible_in_review(self): """Create an application with status ineligible and call in_review @@ -240,8 +271,10 @@ class TestDomainApplication(TestCase): application = completed_application(status=DomainApplication.ApplicationStatus.INELIGIBLE) - with self.assertRaises(TransitionNotAllowed): - application.in_review() + mock_client = MagicMock() + with boto3_mocking.clients.handler_for("sesv2", mock_client): + with self.assertRaises(TransitionNotAllowed): + application.in_review() def test_transition_not_allowed_started_action_needed(self): """Create an application with status started and call action_needed @@ -249,8 +282,10 @@ class TestDomainApplication(TestCase): application = completed_application(status=DomainApplication.ApplicationStatus.STARTED) - with self.assertRaises(TransitionNotAllowed): - application.action_needed() + mock_client = MagicMock() + with boto3_mocking.clients.handler_for("sesv2", mock_client): + with self.assertRaises(TransitionNotAllowed): + application.action_needed() def test_transition_not_allowed_submitted_action_needed(self): """Create an application with status submitted and call action_needed @@ -258,8 +293,10 @@ class TestDomainApplication(TestCase): application = completed_application(status=DomainApplication.ApplicationStatus.SUBMITTED) - with self.assertRaises(TransitionNotAllowed): - application.action_needed() + mock_client = MagicMock() + with boto3_mocking.clients.handler_for("sesv2", mock_client): + with self.assertRaises(TransitionNotAllowed): + application.action_needed() def test_transition_not_allowed_action_needed_action_needed(self): """Create an application with status action needed and call action_needed @@ -267,17 +304,21 @@ class TestDomainApplication(TestCase): application = completed_application(status=DomainApplication.ApplicationStatus.ACTION_NEEDED) - with self.assertRaises(TransitionNotAllowed): - application.action_needed() + mock_client = MagicMock() + with boto3_mocking.clients.handler_for("sesv2", mock_client): + with self.assertRaises(TransitionNotAllowed): + application.action_needed() def test_transition_not_allowed_approved_action_needed(self): """Create an application with status approved and call action_needed against transition rules""" application = completed_application(status=DomainApplication.ApplicationStatus.APPROVED) - - with self.assertRaises(TransitionNotAllowed): - application.action_needed() + + mock_client = MagicMock() + with boto3_mocking.clients.handler_for("sesv2", mock_client): + with self.assertRaises(TransitionNotAllowed): + application.action_needed() def test_transition_not_allowed_withdrawn_action_needed(self): """Create an application with status withdrawn and call action_needed @@ -285,8 +326,10 @@ class TestDomainApplication(TestCase): application = completed_application(status=DomainApplication.ApplicationStatus.WITHDRAWN) - with self.assertRaises(TransitionNotAllowed): - application.action_needed() + mock_client = MagicMock() + with boto3_mocking.clients.handler_for("sesv2", mock_client): + with self.assertRaises(TransitionNotAllowed): + application.action_needed() def test_transition_not_allowed_ineligible_action_needed(self): """Create an application with status ineligible and call action_needed @@ -294,8 +337,10 @@ class TestDomainApplication(TestCase): application = completed_application(status=DomainApplication.ApplicationStatus.INELIGIBLE) - with self.assertRaises(TransitionNotAllowed): - application.action_needed() + mock_client = MagicMock() + with boto3_mocking.clients.handler_for("sesv2", mock_client): + with self.assertRaises(TransitionNotAllowed): + application.action_needed() def test_transition_not_allowed_started_approved(self): """Create an application with status started and call approve @@ -303,8 +348,10 @@ class TestDomainApplication(TestCase): application = completed_application(status=DomainApplication.ApplicationStatus.STARTED) - with self.assertRaises(TransitionNotAllowed): - application.approve() + mock_client = MagicMock() + with boto3_mocking.clients.handler_for("sesv2", mock_client): + with self.assertRaises(TransitionNotAllowed): + application.approve() def test_transition_not_allowed_approved_approved(self): """Create an application with status approved and call approve @@ -312,8 +359,10 @@ class TestDomainApplication(TestCase): application = completed_application(status=DomainApplication.ApplicationStatus.APPROVED) - with self.assertRaises(TransitionNotAllowed): - application.approve() + mock_client = MagicMock() + with boto3_mocking.clients.handler_for("sesv2", mock_client): + with self.assertRaises(TransitionNotAllowed): + application.approve() def test_transition_not_allowed_action_needed_approved(self): """Create an application with status action needed and call approve @@ -321,8 +370,10 @@ class TestDomainApplication(TestCase): application = completed_application(status=DomainApplication.ApplicationStatus.ACTION_NEEDED) - with self.assertRaises(TransitionNotAllowed): - application.approve() + mock_client = MagicMock() + with boto3_mocking.clients.handler_for("sesv2", mock_client): + with self.assertRaises(TransitionNotAllowed): + application.approve() def test_transition_not_allowed_withdrawn_approved(self): """Create an application with status withdrawn and call approve @@ -330,8 +381,10 @@ class TestDomainApplication(TestCase): application = completed_application(status=DomainApplication.ApplicationStatus.WITHDRAWN) - with self.assertRaises(TransitionNotAllowed): - application.approve() + mock_client = MagicMock() + with boto3_mocking.clients.handler_for("sesv2", mock_client): + with self.assertRaises(TransitionNotAllowed): + application.approve() def test_transition_not_allowed_started_withdrawn(self): """Create an application with status started and call withdraw @@ -339,8 +392,10 @@ class TestDomainApplication(TestCase): application = completed_application(status=DomainApplication.ApplicationStatus.STARTED) - with self.assertRaises(TransitionNotAllowed): - application.withdraw() + mock_client = MagicMock() + with boto3_mocking.clients.handler_for("sesv2", mock_client): + with self.assertRaises(TransitionNotAllowed): + application.withdraw() def test_transition_not_allowed_approved_withdrawn(self): """Create an application with status approved and call withdraw @@ -348,8 +403,10 @@ class TestDomainApplication(TestCase): application = completed_application(status=DomainApplication.ApplicationStatus.APPROVED) - with self.assertRaises(TransitionNotAllowed): - application.withdraw() + mock_client = MagicMock() + with boto3_mocking.clients.handler_for("sesv2", mock_client): + with self.assertRaises(TransitionNotAllowed): + application.withdraw() def test_transition_not_allowed_action_needed_withdrawn(self): """Create an application with status action needed and call withdraw @@ -357,8 +414,10 @@ class TestDomainApplication(TestCase): application = completed_application(status=DomainApplication.ApplicationStatus.ACTION_NEEDED) - with self.assertRaises(TransitionNotAllowed): - application.withdraw() + mock_client = MagicMock() + with boto3_mocking.clients.handler_for("sesv2", mock_client): + with self.assertRaises(TransitionNotAllowed): + application.withdraw() def test_transition_not_allowed_rejected_withdrawn(self): """Create an application with status rejected and call withdraw @@ -366,8 +425,10 @@ class TestDomainApplication(TestCase): application = completed_application(status=DomainApplication.ApplicationStatus.REJECTED) - with self.assertRaises(TransitionNotAllowed): - application.withdraw() + mock_client = MagicMock() + with boto3_mocking.clients.handler_for("sesv2", mock_client): + with self.assertRaises(TransitionNotAllowed): + application.withdraw() def test_transition_not_allowed_withdrawn_withdrawn(self): """Create an application with status withdrawn and call withdraw @@ -375,8 +436,10 @@ class TestDomainApplication(TestCase): application = completed_application(status=DomainApplication.ApplicationStatus.WITHDRAWN) - with self.assertRaises(TransitionNotAllowed): - application.withdraw() + mock_client = MagicMock() + with boto3_mocking.clients.handler_for("sesv2", mock_client): + with self.assertRaises(TransitionNotAllowed): + application.withdraw() def test_transition_not_allowed_ineligible_withdrawn(self): """Create an application with status ineligible and call withdraw @@ -384,8 +447,10 @@ class TestDomainApplication(TestCase): application = completed_application(status=DomainApplication.ApplicationStatus.INELIGIBLE) - with self.assertRaises(TransitionNotAllowed): - application.withdraw() + mock_client = MagicMock() + with boto3_mocking.clients.handler_for("sesv2", mock_client): + with self.assertRaises(TransitionNotAllowed): + application.withdraw() def test_transition_not_allowed_started_rejected(self): """Create an application with status started and call reject @@ -393,8 +458,10 @@ class TestDomainApplication(TestCase): application = completed_application(status=DomainApplication.ApplicationStatus.STARTED) - with self.assertRaises(TransitionNotAllowed): - application.reject() + mock_client = MagicMock() + with boto3_mocking.clients.handler_for("sesv2", mock_client): + with self.assertRaises(TransitionNotAllowed): + application.reject() def test_transition_not_allowed_submitted_rejected(self): """Create an application with status submitted and call reject @@ -402,8 +469,10 @@ class TestDomainApplication(TestCase): application = completed_application(status=DomainApplication.ApplicationStatus.SUBMITTED) - with self.assertRaises(TransitionNotAllowed): - application.reject() + mock_client = MagicMock() + with boto3_mocking.clients.handler_for("sesv2", mock_client): + with self.assertRaises(TransitionNotAllowed): + application.reject() def test_transition_not_allowed_action_needed_rejected(self): """Create an application with status action needed and call reject @@ -411,8 +480,10 @@ class TestDomainApplication(TestCase): application = completed_application(status=DomainApplication.ApplicationStatus.ACTION_NEEDED) - with self.assertRaises(TransitionNotAllowed): - application.reject() + mock_client = MagicMock() + with boto3_mocking.clients.handler_for("sesv2", mock_client): + with self.assertRaises(TransitionNotAllowed): + application.reject() def test_transition_not_allowed_withdrawn_rejected(self): """Create an application with status withdrawn and call reject @@ -420,8 +491,10 @@ class TestDomainApplication(TestCase): application = completed_application(status=DomainApplication.ApplicationStatus.WITHDRAWN) - with self.assertRaises(TransitionNotAllowed): - application.reject() + mock_client = MagicMock() + with boto3_mocking.clients.handler_for("sesv2", mock_client): + with self.assertRaises(TransitionNotAllowed): + application.reject() def test_transition_not_allowed_rejected_rejected(self): """Create an application with status rejected and call reject @@ -429,8 +502,10 @@ class TestDomainApplication(TestCase): application = completed_application(status=DomainApplication.ApplicationStatus.REJECTED) - with self.assertRaises(TransitionNotAllowed): - application.reject() + mock_client = MagicMock() + with boto3_mocking.clients.handler_for("sesv2", mock_client): + with self.assertRaises(TransitionNotAllowed): + application.reject() def test_transition_not_allowed_ineligible_rejected(self): """Create an application with status ineligible and call reject @@ -438,8 +513,10 @@ class TestDomainApplication(TestCase): application = completed_application(status=DomainApplication.ApplicationStatus.INELIGIBLE) - with self.assertRaises(TransitionNotAllowed): - application.reject() + mock_client = MagicMock() + with boto3_mocking.clients.handler_for("sesv2", mock_client): + with self.assertRaises(TransitionNotAllowed): + application.reject() def test_transition_not_allowed_approved_rejected_when_domain_is_active(self): """Create an application with status approved, create a matching domain that @@ -454,11 +531,13 @@ class TestDomainApplication(TestCase): def custom_is_active(self): return True # Override to return True - # Use patch to temporarily replace is_active with the custom implementation - with patch.object(Domain, "is_active", custom_is_active): - # Now, when you call is_active on Domain, it will return True - with self.assertRaises(TransitionNotAllowed): - application.reject() + mock_client = MagicMock() + with boto3_mocking.clients.handler_for("sesv2", mock_client): + # Use patch to temporarily replace is_active with the custom implementation + with patch.object(Domain, "is_active", custom_is_active): + # Now, when you call is_active on Domain, it will return True + with self.assertRaises(TransitionNotAllowed): + application.reject() def test_transition_not_allowed_started_ineligible(self): """Create an application with status started and call reject @@ -466,8 +545,10 @@ class TestDomainApplication(TestCase): application = completed_application(status=DomainApplication.ApplicationStatus.STARTED) - with self.assertRaises(TransitionNotAllowed): - application.reject_with_prejudice() + mock_client = MagicMock() + with boto3_mocking.clients.handler_for("sesv2", mock_client): + with self.assertRaises(TransitionNotAllowed): + application.reject_with_prejudice() def test_transition_not_allowed_submitted_ineligible(self): """Create an application with status submitted and call reject @@ -475,8 +556,10 @@ class TestDomainApplication(TestCase): application = completed_application(status=DomainApplication.ApplicationStatus.SUBMITTED) - with self.assertRaises(TransitionNotAllowed): - application.reject_with_prejudice() + mock_client = MagicMock() + with boto3_mocking.clients.handler_for("sesv2", mock_client): + with self.assertRaises(TransitionNotAllowed): + application.reject_with_prejudice() def test_transition_not_allowed_action_needed_ineligible(self): """Create an application with status action needed and call reject @@ -484,8 +567,10 @@ class TestDomainApplication(TestCase): application = completed_application(status=DomainApplication.ApplicationStatus.ACTION_NEEDED) - with self.assertRaises(TransitionNotAllowed): - application.reject_with_prejudice() + mock_client = MagicMock() + with boto3_mocking.clients.handler_for("sesv2", mock_client): + with self.assertRaises(TransitionNotAllowed): + application.reject_with_prejudice() def test_transition_not_allowed_withdrawn_ineligible(self): """Create an application with status withdrawn and call reject @@ -493,8 +578,10 @@ class TestDomainApplication(TestCase): application = completed_application(status=DomainApplication.ApplicationStatus.WITHDRAWN) - with self.assertRaises(TransitionNotAllowed): - application.reject_with_prejudice() + mock_client = MagicMock() + with boto3_mocking.clients.handler_for("sesv2", mock_client): + with self.assertRaises(TransitionNotAllowed): + application.reject_with_prejudice() def test_transition_not_allowed_rejected_ineligible(self): """Create an application with status rejected and call reject @@ -502,8 +589,10 @@ class TestDomainApplication(TestCase): application = completed_application(status=DomainApplication.ApplicationStatus.REJECTED) - with self.assertRaises(TransitionNotAllowed): - application.reject_with_prejudice() + mock_client = MagicMock() + with boto3_mocking.clients.handler_for("sesv2", mock_client): + with self.assertRaises(TransitionNotAllowed): + application.reject_with_prejudice() def test_transition_not_allowed_ineligible_ineligible(self): """Create an application with status ineligible and call reject @@ -511,8 +600,10 @@ class TestDomainApplication(TestCase): application = completed_application(status=DomainApplication.ApplicationStatus.INELIGIBLE) - with self.assertRaises(TransitionNotAllowed): - application.reject_with_prejudice() + mock_client = MagicMock() + with boto3_mocking.clients.handler_for("sesv2", mock_client): + with self.assertRaises(TransitionNotAllowed): + application.reject_with_prejudice() def test_transition_not_allowed_approved_ineligible_when_domain_is_active(self): """Create an application with status approved, create a matching domain that @@ -527,11 +618,13 @@ class TestDomainApplication(TestCase): def custom_is_active(self): return True # Override to return True + mock_client = MagicMock() + with boto3_mocking.clients.handler_for("sesv2", mock_client): # Use patch to temporarily replace is_active with the custom implementation - with patch.object(Domain, "is_active", custom_is_active): - # Now, when you call is_active on Domain, it will return True - with self.assertRaises(TransitionNotAllowed): - application.reject_with_prejudice() + with patch.object(Domain, "is_active", custom_is_active): + # Now, when you call is_active on Domain, it will return True + with self.assertRaises(TransitionNotAllowed): + application.reject_with_prejudice() class TestPermissions(TestCase): From 9e65d687acbbbca97eeaa6dbb8fd8129ccd7fc11 Mon Sep 17 00:00:00 2001 From: zandercymatics <141044360+zandercymatics@users.noreply.github.com> Date: Thu, 21 Dec 2023 10:06:45 -0700 Subject: [PATCH 02/34] Limit email chatter in fixtures --- src/registrar/fixtures_applications.py | 5 ++++- src/registrar/models/domain_application.py | 14 ++++++++++---- 2 files changed, 14 insertions(+), 5 deletions(-) diff --git a/src/registrar/fixtures_applications.py b/src/registrar/fixtures_applications.py index ad3ae0820..ae82ddd8b 100644 --- a/src/registrar/fixtures_applications.py +++ b/src/registrar/fixtures_applications.py @@ -218,5 +218,8 @@ class DomainFixture(DomainApplicationFixture): creator=user, status=DomainApplication.ApplicationStatus.IN_REVIEW ).last() logger.debug(f"Approving {application} for {user}") - application.approve() + + # We don't want fixtures sending out real emails to + # fake email addresses, so we just skip that and log it instead + application.approve(do_fake_send_email=True) application.save() diff --git a/src/registrar/models/domain_application.py b/src/registrar/models/domain_application.py index 303f0cd22..495b7e171 100644 --- a/src/registrar/models/domain_application.py +++ b/src/registrar/models/domain_application.py @@ -561,8 +561,8 @@ class DomainApplication(TimeStampedModel): return not self.approved_domain.is_active() return True - def _send_status_update_email(self, new_status, email_template, email_template_subject): - """Send a atatus update email to the submitter. + def _send_status_update_email(self, new_status, email_template, email_template_subject, do_fake_send_email=False): + """Send a status update email to the submitter. The email goes to the email address that the submitter gave as their contact information. If there is not submitter information, then do @@ -571,7 +571,12 @@ class DomainApplication(TimeStampedModel): if self.submitter is None or self.submitter.email is None: logger.warning(f"Cannot send {new_status} email, no submitter email address.") - return + return None + + if do_fake_send_email: + logger.info(f"Email was not sent. Would send {new_status} to email: {self.submitter.email}") + return None + try: send_templated_email( email_template, @@ -651,7 +656,7 @@ class DomainApplication(TimeStampedModel): ], target=ApplicationStatus.APPROVED, ) - def approve(self): + def approve(self, do_fake_send_email=False): """Approve an application that has been submitted. This has substantial side-effects because it creates another database @@ -680,6 +685,7 @@ class DomainApplication(TimeStampedModel): "application approved", "emails/status_change_approved.txt", "emails/status_change_approved_subject.txt", + do_fake_send_email, ) @transition( From 5c892dcec5c23369e1c308f8e6f00d2b7e0e2218 Mon Sep 17 00:00:00 2001 From: zandercymatics <141044360+zandercymatics@users.noreply.github.com> Date: Thu, 21 Dec 2023 10:25:13 -0700 Subject: [PATCH 03/34] Add mocks --- src/registrar/models/domain_application.py | 2 +- src/registrar/tests/test_models.py | 96 +++++++++++----------- src/registrar/utility/email.py | 2 +- 3 files changed, 49 insertions(+), 51 deletions(-) diff --git a/src/registrar/models/domain_application.py b/src/registrar/models/domain_application.py index 495b7e171..acb563d17 100644 --- a/src/registrar/models/domain_application.py +++ b/src/registrar/models/domain_application.py @@ -574,7 +574,7 @@ class DomainApplication(TimeStampedModel): return None if do_fake_send_email: - logger.info(f"Email was not sent. Would send {new_status} to email: {self.submitter.email}") + logger.info(f"Email was not sent. Would send {new_status} email: {self.submitter.email}") return None try: diff --git a/src/registrar/tests/test_models.py b/src/registrar/tests/test_models.py index d2f1a5550..b38b2a5f4 100644 --- a/src/registrar/tests/test_models.py +++ b/src/registrar/tests/test_models.py @@ -98,7 +98,7 @@ class TestDomainApplication(TestCase): user, _ = User.objects.get_or_create() application = DomainApplication.objects.create(creator=user) - mock_client = MagicMock() + mock_client = MockSESClient with boto3_mocking.clients.handler_for("sesv2", mock_client): with self.assertRaises(ValueError): # can't submit an application with a null domain name @@ -110,7 +110,7 @@ class TestDomainApplication(TestCase): application = DomainApplication.objects.create(creator=user, requested_domain=site) # no submitter email so this emits a log warning - mock_client = MagicMock() + mock_client = MockSESClient with boto3_mocking.clients.handler_for("sesv2", mock_client): with less_console_noise(): application.submit() @@ -128,9 +128,9 @@ class TestDomainApplication(TestCase): ) application.save() - mock_client = MagicMock() - with boto3_mocking.clients.handler_for("sesv2", mock_client): - application.submit() + with boto3_mocking.clients.handler_for("sesv2", MockSESClient): + with less_console_noise(): + application.submit() # check to see if an email was sent self.assertGreater( @@ -150,8 +150,7 @@ class TestDomainApplication(TestCase): application = completed_application(status=DomainApplication.ApplicationStatus.SUBMITTED) - mock_client = MagicMock() - with boto3_mocking.clients.handler_for("sesv2", mock_client): + with boto3_mocking.clients.handler_for("sesv2", MockSESClient): with self.assertRaises(TransitionNotAllowed): application.submit() @@ -161,8 +160,7 @@ class TestDomainApplication(TestCase): application = completed_application(status=DomainApplication.ApplicationStatus.IN_REVIEW) - mock_client = MagicMock() - with boto3_mocking.clients.handler_for("sesv2", mock_client): + with boto3_mocking.clients.handler_for("sesv2", MockSESClient): with self.assertRaises(TransitionNotAllowed): application.submit() @@ -172,7 +170,7 @@ class TestDomainApplication(TestCase): application = completed_application(status=DomainApplication.ApplicationStatus.APPROVED) - mock_client = MagicMock() + mock_client = MockSESClient with boto3_mocking.clients.handler_for("sesv2", mock_client): with self.assertRaises(TransitionNotAllowed): application.submit() @@ -183,7 +181,7 @@ class TestDomainApplication(TestCase): application = completed_application(status=DomainApplication.ApplicationStatus.REJECTED) - mock_client = MagicMock() + mock_client = MockSESClient with boto3_mocking.clients.handler_for("sesv2", mock_client): with self.assertRaises(TransitionNotAllowed): application.submit() @@ -194,7 +192,7 @@ class TestDomainApplication(TestCase): application = completed_application(status=DomainApplication.ApplicationStatus.INELIGIBLE) - mock_client = MagicMock() + mock_client = MockSESClient with boto3_mocking.clients.handler_for("sesv2", mock_client): with self.assertRaises(TransitionNotAllowed): application.submit() @@ -205,7 +203,7 @@ class TestDomainApplication(TestCase): application = completed_application(status=DomainApplication.ApplicationStatus.STARTED) - mock_client = MagicMock() + mock_client = MockSESClient with boto3_mocking.clients.handler_for("sesv2", mock_client): with self.assertRaises(TransitionNotAllowed): application.in_review() @@ -216,7 +214,7 @@ class TestDomainApplication(TestCase): application = completed_application(status=DomainApplication.ApplicationStatus.IN_REVIEW) - mock_client = MagicMock() + mock_client = MockSESClient with boto3_mocking.clients.handler_for("sesv2", mock_client): with self.assertRaises(TransitionNotAllowed): application.in_review() @@ -227,7 +225,7 @@ class TestDomainApplication(TestCase): application = completed_application(status=DomainApplication.ApplicationStatus.APPROVED) - mock_client = MagicMock() + mock_client = MockSESClient with boto3_mocking.clients.handler_for("sesv2", mock_client): with self.assertRaises(TransitionNotAllowed): application.in_review() @@ -238,7 +236,7 @@ class TestDomainApplication(TestCase): application = completed_application(status=DomainApplication.ApplicationStatus.ACTION_NEEDED) - mock_client = MagicMock() + mock_client = MockSESClient with boto3_mocking.clients.handler_for("sesv2", mock_client): with self.assertRaises(TransitionNotAllowed): application.in_review() @@ -249,7 +247,7 @@ class TestDomainApplication(TestCase): application = completed_application(status=DomainApplication.ApplicationStatus.REJECTED) - mock_client = MagicMock() + mock_client = MockSESClient with boto3_mocking.clients.handler_for("sesv2", mock_client): with self.assertRaises(TransitionNotAllowed): application.in_review() @@ -260,7 +258,7 @@ class TestDomainApplication(TestCase): application = completed_application(status=DomainApplication.ApplicationStatus.WITHDRAWN) - mock_client = MagicMock() + mock_client = MockSESClient with boto3_mocking.clients.handler_for("sesv2", mock_client): with self.assertRaises(TransitionNotAllowed): application.in_review() @@ -271,7 +269,7 @@ class TestDomainApplication(TestCase): application = completed_application(status=DomainApplication.ApplicationStatus.INELIGIBLE) - mock_client = MagicMock() + mock_client = MockSESClient with boto3_mocking.clients.handler_for("sesv2", mock_client): with self.assertRaises(TransitionNotAllowed): application.in_review() @@ -282,7 +280,7 @@ class TestDomainApplication(TestCase): application = completed_application(status=DomainApplication.ApplicationStatus.STARTED) - mock_client = MagicMock() + mock_client = MockSESClient with boto3_mocking.clients.handler_for("sesv2", mock_client): with self.assertRaises(TransitionNotAllowed): application.action_needed() @@ -293,7 +291,7 @@ class TestDomainApplication(TestCase): application = completed_application(status=DomainApplication.ApplicationStatus.SUBMITTED) - mock_client = MagicMock() + mock_client = MockSESClient with boto3_mocking.clients.handler_for("sesv2", mock_client): with self.assertRaises(TransitionNotAllowed): application.action_needed() @@ -304,7 +302,7 @@ class TestDomainApplication(TestCase): application = completed_application(status=DomainApplication.ApplicationStatus.ACTION_NEEDED) - mock_client = MagicMock() + mock_client = MockSESClient with boto3_mocking.clients.handler_for("sesv2", mock_client): with self.assertRaises(TransitionNotAllowed): application.action_needed() @@ -315,7 +313,7 @@ class TestDomainApplication(TestCase): application = completed_application(status=DomainApplication.ApplicationStatus.APPROVED) - mock_client = MagicMock() + mock_client = MockSESClient with boto3_mocking.clients.handler_for("sesv2", mock_client): with self.assertRaises(TransitionNotAllowed): application.action_needed() @@ -326,7 +324,7 @@ class TestDomainApplication(TestCase): application = completed_application(status=DomainApplication.ApplicationStatus.WITHDRAWN) - mock_client = MagicMock() + mock_client = MockSESClient with boto3_mocking.clients.handler_for("sesv2", mock_client): with self.assertRaises(TransitionNotAllowed): application.action_needed() @@ -337,7 +335,7 @@ class TestDomainApplication(TestCase): application = completed_application(status=DomainApplication.ApplicationStatus.INELIGIBLE) - mock_client = MagicMock() + mock_client = MockSESClient with boto3_mocking.clients.handler_for("sesv2", mock_client): with self.assertRaises(TransitionNotAllowed): application.action_needed() @@ -348,7 +346,7 @@ class TestDomainApplication(TestCase): application = completed_application(status=DomainApplication.ApplicationStatus.STARTED) - mock_client = MagicMock() + mock_client = MockSESClient with boto3_mocking.clients.handler_for("sesv2", mock_client): with self.assertRaises(TransitionNotAllowed): application.approve() @@ -359,7 +357,7 @@ class TestDomainApplication(TestCase): application = completed_application(status=DomainApplication.ApplicationStatus.APPROVED) - mock_client = MagicMock() + mock_client = MockSESClient with boto3_mocking.clients.handler_for("sesv2", mock_client): with self.assertRaises(TransitionNotAllowed): application.approve() @@ -370,7 +368,7 @@ class TestDomainApplication(TestCase): application = completed_application(status=DomainApplication.ApplicationStatus.ACTION_NEEDED) - mock_client = MagicMock() + mock_client = MockSESClient with boto3_mocking.clients.handler_for("sesv2", mock_client): with self.assertRaises(TransitionNotAllowed): application.approve() @@ -381,7 +379,7 @@ class TestDomainApplication(TestCase): application = completed_application(status=DomainApplication.ApplicationStatus.WITHDRAWN) - mock_client = MagicMock() + mock_client = MockSESClient with boto3_mocking.clients.handler_for("sesv2", mock_client): with self.assertRaises(TransitionNotAllowed): application.approve() @@ -392,7 +390,7 @@ class TestDomainApplication(TestCase): application = completed_application(status=DomainApplication.ApplicationStatus.STARTED) - mock_client = MagicMock() + mock_client = MockSESClient with boto3_mocking.clients.handler_for("sesv2", mock_client): with self.assertRaises(TransitionNotAllowed): application.withdraw() @@ -403,7 +401,7 @@ class TestDomainApplication(TestCase): application = completed_application(status=DomainApplication.ApplicationStatus.APPROVED) - mock_client = MagicMock() + mock_client = MockSESClient with boto3_mocking.clients.handler_for("sesv2", mock_client): with self.assertRaises(TransitionNotAllowed): application.withdraw() @@ -414,7 +412,7 @@ class TestDomainApplication(TestCase): application = completed_application(status=DomainApplication.ApplicationStatus.ACTION_NEEDED) - mock_client = MagicMock() + mock_client = MockSESClient with boto3_mocking.clients.handler_for("sesv2", mock_client): with self.assertRaises(TransitionNotAllowed): application.withdraw() @@ -425,7 +423,7 @@ class TestDomainApplication(TestCase): application = completed_application(status=DomainApplication.ApplicationStatus.REJECTED) - mock_client = MagicMock() + mock_client = MockSESClient with boto3_mocking.clients.handler_for("sesv2", mock_client): with self.assertRaises(TransitionNotAllowed): application.withdraw() @@ -436,7 +434,7 @@ class TestDomainApplication(TestCase): application = completed_application(status=DomainApplication.ApplicationStatus.WITHDRAWN) - mock_client = MagicMock() + mock_client = MockSESClient with boto3_mocking.clients.handler_for("sesv2", mock_client): with self.assertRaises(TransitionNotAllowed): application.withdraw() @@ -447,7 +445,7 @@ class TestDomainApplication(TestCase): application = completed_application(status=DomainApplication.ApplicationStatus.INELIGIBLE) - mock_client = MagicMock() + mock_client = MockSESClient with boto3_mocking.clients.handler_for("sesv2", mock_client): with self.assertRaises(TransitionNotAllowed): application.withdraw() @@ -458,7 +456,7 @@ class TestDomainApplication(TestCase): application = completed_application(status=DomainApplication.ApplicationStatus.STARTED) - mock_client = MagicMock() + mock_client = MockSESClient with boto3_mocking.clients.handler_for("sesv2", mock_client): with self.assertRaises(TransitionNotAllowed): application.reject() @@ -469,7 +467,7 @@ class TestDomainApplication(TestCase): application = completed_application(status=DomainApplication.ApplicationStatus.SUBMITTED) - mock_client = MagicMock() + mock_client = MockSESClient with boto3_mocking.clients.handler_for("sesv2", mock_client): with self.assertRaises(TransitionNotAllowed): application.reject() @@ -480,7 +478,7 @@ class TestDomainApplication(TestCase): application = completed_application(status=DomainApplication.ApplicationStatus.ACTION_NEEDED) - mock_client = MagicMock() + mock_client = MockSESClient with boto3_mocking.clients.handler_for("sesv2", mock_client): with self.assertRaises(TransitionNotAllowed): application.reject() @@ -491,7 +489,7 @@ class TestDomainApplication(TestCase): application = completed_application(status=DomainApplication.ApplicationStatus.WITHDRAWN) - mock_client = MagicMock() + mock_client = MockSESClient with boto3_mocking.clients.handler_for("sesv2", mock_client): with self.assertRaises(TransitionNotAllowed): application.reject() @@ -502,7 +500,7 @@ class TestDomainApplication(TestCase): application = completed_application(status=DomainApplication.ApplicationStatus.REJECTED) - mock_client = MagicMock() + mock_client = MockSESClient with boto3_mocking.clients.handler_for("sesv2", mock_client): with self.assertRaises(TransitionNotAllowed): application.reject() @@ -513,7 +511,7 @@ class TestDomainApplication(TestCase): application = completed_application(status=DomainApplication.ApplicationStatus.INELIGIBLE) - mock_client = MagicMock() + mock_client = MockSESClient with boto3_mocking.clients.handler_for("sesv2", mock_client): with self.assertRaises(TransitionNotAllowed): application.reject() @@ -531,7 +529,7 @@ class TestDomainApplication(TestCase): def custom_is_active(self): return True # Override to return True - mock_client = MagicMock() + mock_client = MockSESClient with boto3_mocking.clients.handler_for("sesv2", mock_client): # Use patch to temporarily replace is_active with the custom implementation with patch.object(Domain, "is_active", custom_is_active): @@ -545,7 +543,7 @@ class TestDomainApplication(TestCase): application = completed_application(status=DomainApplication.ApplicationStatus.STARTED) - mock_client = MagicMock() + mock_client = MockSESClient with boto3_mocking.clients.handler_for("sesv2", mock_client): with self.assertRaises(TransitionNotAllowed): application.reject_with_prejudice() @@ -556,7 +554,7 @@ class TestDomainApplication(TestCase): application = completed_application(status=DomainApplication.ApplicationStatus.SUBMITTED) - mock_client = MagicMock() + mock_client = MockSESClient with boto3_mocking.clients.handler_for("sesv2", mock_client): with self.assertRaises(TransitionNotAllowed): application.reject_with_prejudice() @@ -567,7 +565,7 @@ class TestDomainApplication(TestCase): application = completed_application(status=DomainApplication.ApplicationStatus.ACTION_NEEDED) - mock_client = MagicMock() + mock_client = MockSESClient with boto3_mocking.clients.handler_for("sesv2", mock_client): with self.assertRaises(TransitionNotAllowed): application.reject_with_prejudice() @@ -578,7 +576,7 @@ class TestDomainApplication(TestCase): application = completed_application(status=DomainApplication.ApplicationStatus.WITHDRAWN) - mock_client = MagicMock() + mock_client = MockSESClient with boto3_mocking.clients.handler_for("sesv2", mock_client): with self.assertRaises(TransitionNotAllowed): application.reject_with_prejudice() @@ -589,7 +587,7 @@ class TestDomainApplication(TestCase): application = completed_application(status=DomainApplication.ApplicationStatus.REJECTED) - mock_client = MagicMock() + mock_client = MockSESClient with boto3_mocking.clients.handler_for("sesv2", mock_client): with self.assertRaises(TransitionNotAllowed): application.reject_with_prejudice() @@ -600,7 +598,7 @@ class TestDomainApplication(TestCase): application = completed_application(status=DomainApplication.ApplicationStatus.INELIGIBLE) - mock_client = MagicMock() + mock_client = MockSESClient with boto3_mocking.clients.handler_for("sesv2", mock_client): with self.assertRaises(TransitionNotAllowed): application.reject_with_prejudice() @@ -618,7 +616,7 @@ class TestDomainApplication(TestCase): def custom_is_active(self): return True # Override to return True - mock_client = MagicMock() + mock_client = MockSESClient with boto3_mocking.clients.handler_for("sesv2", mock_client): # Use patch to temporarily replace is_active with the custom implementation with patch.object(Domain, "is_active", custom_is_active): diff --git a/src/registrar/utility/email.py b/src/registrar/utility/email.py index 79d2d43ce..110ae117e 100644 --- a/src/registrar/utility/email.py +++ b/src/registrar/utility/email.py @@ -20,7 +20,7 @@ def send_templated_email(template_name: str, subject_template_name: str, to_addr context as Django's HTML templates. context gives additional information that the template may use. """ - + print(f"An email was sent! Template name: {template_name} to {to_address}") template = get_template(template_name) email_body = template.render(context=context) From bf46be5cbc9c9053d00f5f3d17c0613dda5acb22 Mon Sep 17 00:00:00 2001 From: zandercymatics <141044360+zandercymatics@users.noreply.github.com> Date: Thu, 21 Dec 2023 10:40:51 -0700 Subject: [PATCH 04/34] Change log level --- src/registrar/utility/email.py | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/src/registrar/utility/email.py b/src/registrar/utility/email.py index 110ae117e..cb1550801 100644 --- a/src/registrar/utility/email.py +++ b/src/registrar/utility/email.py @@ -1,11 +1,13 @@ """Utilities for sending emails.""" import boto3 - +import logging from django.conf import settings from django.template.loader import get_template +logger = logging.getLogger(__name__) + class EmailSendingError(RuntimeError): """Local error for handling all failures when sending email.""" @@ -20,7 +22,7 @@ def send_templated_email(template_name: str, subject_template_name: str, to_addr context as Django's HTML templates. context gives additional information that the template may use. """ - print(f"An email was sent! Template name: {template_name} to {to_address}") + logger.info(f"An email was sent! Template name: {template_name} to {to_address}") template = get_template(template_name) email_body = template.render(context=context) From ad9e64f0638a090092ab459c55fc4b7200b5aa91 Mon Sep 17 00:00:00 2001 From: zandercymatics <141044360+zandercymatics@users.noreply.github.com> Date: Thu, 21 Dec 2023 12:12:14 -0700 Subject: [PATCH 05/34] Add more mocks --- src/registrar/tests/test_admin.py | 112 ++++++++++++---------- src/registrar/tests/test_models.py | 63 +++++++----- src/registrar/tests/test_models_domain.py | 18 ++-- src/registrar/tests/test_views.py | 39 ++++++-- 4 files changed, 145 insertions(+), 87 deletions(-) diff --git a/src/registrar/tests/test_admin.py b/src/registrar/tests/test_admin.py index 9d6add249..55a7c788d 100644 --- a/src/registrar/tests/test_admin.py +++ b/src/registrar/tests/test_admin.py @@ -26,6 +26,7 @@ from registrar.models.user_domain_role import UserDomainRole from .common import ( completed_application, generic_domain_object, + less_console_noise, mock_user, create_superuser, create_user, @@ -348,17 +349,18 @@ class TestDomainApplicationAdmin(MockEppLib): mock_client_instance = mock_client.return_value with boto3_mocking.clients.handler_for("sesv2", mock_client): - # Create a sample application - application = completed_application() + with less_console_noise(): + # Create a sample application + application = completed_application() - # Create a mock request - request = self.factory.post("/admin/registrar/domainapplication/{}/change/".format(application.pk)) + # Create a mock request + request = self.factory.post("/admin/registrar/domainapplication/{}/change/".format(application.pk)) - # Modify the application's property - application.status = DomainApplication.ApplicationStatus.SUBMITTED + # Modify the application's property + application.status = DomainApplication.ApplicationStatus.SUBMITTED - # Use the model admin's save_model method - self.admin.save_model(request, application, form=None, change=True) + # Use the model admin's save_model method + self.admin.save_model(request, application, form=None, change=True) # Access the arguments passed to send_email call_args = mock_client_instance.send_email.call_args @@ -389,17 +391,18 @@ class TestDomainApplicationAdmin(MockEppLib): mock_client_instance = mock_client.return_value with boto3_mocking.clients.handler_for("sesv2", mock_client): - # Create a sample application - application = completed_application(status=DomainApplication.ApplicationStatus.SUBMITTED) + with less_console_noise(): + # Create a sample application + application = completed_application(status=DomainApplication.ApplicationStatus.SUBMITTED) - # Create a mock request - request = self.factory.post("/admin/registrar/domainapplication/{}/change/".format(application.pk)) + # Create a mock request + request = self.factory.post("/admin/registrar/domainapplication/{}/change/".format(application.pk)) - # Modify the application's property - application.status = DomainApplication.ApplicationStatus.IN_REVIEW + # Modify the application's property + application.status = DomainApplication.ApplicationStatus.IN_REVIEW - # Use the model admin's save_model method - self.admin.save_model(request, application, form=None, change=True) + # Use the model admin's save_model method + self.admin.save_model(request, application, form=None, change=True) # Access the arguments passed to send_email call_args = mock_client_instance.send_email.call_args @@ -430,17 +433,18 @@ class TestDomainApplicationAdmin(MockEppLib): mock_client_instance = mock_client.return_value with boto3_mocking.clients.handler_for("sesv2", mock_client): - # Create a sample application - application = completed_application(status=DomainApplication.ApplicationStatus.IN_REVIEW) + with less_console_noise(): + # Create a sample application + application = completed_application(status=DomainApplication.ApplicationStatus.IN_REVIEW) - # Create a mock request - request = self.factory.post("/admin/registrar/domainapplication/{}/change/".format(application.pk)) + # Create a mock request + request = self.factory.post("/admin/registrar/domainapplication/{}/change/".format(application.pk)) - # Modify the application's property - application.status = DomainApplication.ApplicationStatus.APPROVED + # Modify the application's property + application.status = DomainApplication.ApplicationStatus.APPROVED - # Use the model admin's save_model method - self.admin.save_model(request, application, form=None, change=True) + # Use the model admin's save_model method + self.admin.save_model(request, application, form=None, change=True) # Access the arguments passed to send_email call_args = mock_client_instance.send_email.call_args @@ -461,6 +465,7 @@ class TestDomainApplicationAdmin(MockEppLib): # Perform assertions on the mock call itself mock_client_instance.send_email.assert_called_once() + @boto3_mocking.patching def test_save_model_sets_approved_domain(self): # make sure there is no user with this email EMAIL = "mayor@igorville.gov" @@ -472,11 +477,14 @@ class TestDomainApplicationAdmin(MockEppLib): # Create a mock request request = self.factory.post("/admin/registrar/domainapplication/{}/change/".format(application.pk)) - # Modify the application's property - application.status = DomainApplication.ApplicationStatus.APPROVED + mock_client = MagicMock() + with boto3_mocking.clients.handler_for("sesv2", mock_client): + with less_console_noise(): + # Modify the application's property + application.status = DomainApplication.ApplicationStatus.APPROVED - # Use the model admin's save_model method - self.admin.save_model(request, application, form=None, change=True) + # Use the model admin's save_model method + self.admin.save_model(request, application, form=None, change=True) # Test that approved domain exists and equals requested domain self.assertEqual(application.requested_domain.name, application.approved_domain.name) @@ -491,17 +499,18 @@ class TestDomainApplicationAdmin(MockEppLib): mock_client_instance = mock_client.return_value with boto3_mocking.clients.handler_for("sesv2", mock_client): - # Create a sample application - application = completed_application(status=DomainApplication.ApplicationStatus.IN_REVIEW) + with less_console_noise(): + # Create a sample application + application = completed_application(status=DomainApplication.ApplicationStatus.IN_REVIEW) - # Create a mock request - request = self.factory.post("/admin/registrar/domainapplication/{}/change/".format(application.pk)) + # Create a mock request + request = self.factory.post("/admin/registrar/domainapplication/{}/change/".format(application.pk)) - # Modify the application's property - application.status = DomainApplication.ApplicationStatus.ACTION_NEEDED + # Modify the application's property + application.status = DomainApplication.ApplicationStatus.ACTION_NEEDED - # Use the model admin's save_model method - self.admin.save_model(request, application, form=None, change=True) + # Use the model admin's save_model method + self.admin.save_model(request, application, form=None, change=True) # Access the arguments passed to send_email call_args = mock_client_instance.send_email.call_args @@ -532,17 +541,18 @@ class TestDomainApplicationAdmin(MockEppLib): mock_client_instance = mock_client.return_value with boto3_mocking.clients.handler_for("sesv2", mock_client): - # Create a sample application - application = completed_application(status=DomainApplication.ApplicationStatus.IN_REVIEW) + with less_console_noise(): + # Create a sample application + application = completed_application(status=DomainApplication.ApplicationStatus.IN_REVIEW) - # Create a mock request - request = self.factory.post("/admin/registrar/domainapplication/{}/change/".format(application.pk)) + # Create a mock request + request = self.factory.post("/admin/registrar/domainapplication/{}/change/".format(application.pk)) - # Modify the application's property - application.status = DomainApplication.ApplicationStatus.REJECTED + # Modify the application's property + application.status = DomainApplication.ApplicationStatus.REJECTED - # Use the model admin's save_model method - self.admin.save_model(request, application, form=None, change=True) + # Use the model admin's save_model method + self.admin.save_model(request, application, form=None, change=True) # Access the arguments passed to send_email call_args = mock_client_instance.send_email.call_args @@ -562,7 +572,8 @@ class TestDomainApplicationAdmin(MockEppLib): # Perform assertions on the mock call itself mock_client_instance.send_email.assert_called_once() - + + @boto3_mocking.patching def test_save_model_sets_restricted_status_on_user(self): # make sure there is no user with this email EMAIL = "mayor@igorville.gov" @@ -574,11 +585,14 @@ class TestDomainApplicationAdmin(MockEppLib): # Create a mock request request = self.factory.post("/admin/registrar/domainapplication/{}/change/".format(application.pk)) - # Modify the application's property - application.status = DomainApplication.ApplicationStatus.INELIGIBLE + mock_client = MagicMock() + with boto3_mocking.clients.handler_for("sesv2", mock_client): + with less_console_noise(): + # Modify the application's property + application.status = DomainApplication.ApplicationStatus.INELIGIBLE - # Use the model admin's save_model method - self.admin.save_model(request, application, form=None, change=True) + # Use the model admin's save_model method + self.admin.save_model(request, application, form=None, change=True) # Test that approved domain exists and equals requested domain self.assertEqual(application.creator.status, "restricted") diff --git a/src/registrar/tests/test_models.py b/src/registrar/tests/test_models.py index b38b2a5f4..b496e2358 100644 --- a/src/registrar/tests/test_models.py +++ b/src/registrar/tests/test_models.py @@ -260,8 +260,9 @@ class TestDomainApplication(TestCase): mock_client = MockSESClient with boto3_mocking.clients.handler_for("sesv2", mock_client): - with self.assertRaises(TransitionNotAllowed): - application.in_review() + with less_console_noise(): + with self.assertRaises(TransitionNotAllowed): + application.in_review() def test_transition_not_allowed_ineligible_in_review(self): """Create an application with status ineligible and call in_review @@ -326,8 +327,9 @@ class TestDomainApplication(TestCase): mock_client = MockSESClient with boto3_mocking.clients.handler_for("sesv2", mock_client): - with self.assertRaises(TransitionNotAllowed): - application.action_needed() + with less_console_noise(): + with self.assertRaises(TransitionNotAllowed): + application.action_needed() def test_transition_not_allowed_ineligible_action_needed(self): """Create an application with status ineligible and call action_needed @@ -381,8 +383,9 @@ class TestDomainApplication(TestCase): mock_client = MockSESClient with boto3_mocking.clients.handler_for("sesv2", mock_client): - with self.assertRaises(TransitionNotAllowed): - application.approve() + with less_console_noise(): + with self.assertRaises(TransitionNotAllowed): + application.approve() def test_transition_not_allowed_started_withdrawn(self): """Create an application with status started and call withdraw @@ -436,8 +439,9 @@ class TestDomainApplication(TestCase): mock_client = MockSESClient with boto3_mocking.clients.handler_for("sesv2", mock_client): - with self.assertRaises(TransitionNotAllowed): - application.withdraw() + with less_console_noise(): + with self.assertRaises(TransitionNotAllowed): + application.withdraw() def test_transition_not_allowed_ineligible_withdrawn(self): """Create an application with status ineligible and call withdraw @@ -491,8 +495,9 @@ class TestDomainApplication(TestCase): mock_client = MockSESClient with boto3_mocking.clients.handler_for("sesv2", mock_client): - with self.assertRaises(TransitionNotAllowed): - application.reject() + with less_console_noise(): + with self.assertRaises(TransitionNotAllowed): + application.reject() def test_transition_not_allowed_rejected_rejected(self): """Create an application with status rejected and call reject @@ -578,8 +583,9 @@ class TestDomainApplication(TestCase): mock_client = MockSESClient with boto3_mocking.clients.handler_for("sesv2", mock_client): - with self.assertRaises(TransitionNotAllowed): - application.reject_with_prejudice() + with less_console_noise(): + with self.assertRaises(TransitionNotAllowed): + application.reject_with_prejudice() def test_transition_not_allowed_rejected_ineligible(self): """Create an application with status rejected and call reject @@ -629,13 +635,18 @@ class TestPermissions(TestCase): """Test the User-Domain-Role connection.""" + @boto3_mocking.patching def test_approval_creates_role(self): draft_domain, _ = DraftDomain.objects.get_or_create(name="igorville.gov") user, _ = User.objects.get_or_create() application = DomainApplication.objects.create(creator=user, requested_domain=draft_domain) - # skip using the submit method - application.status = DomainApplication.ApplicationStatus.SUBMITTED - application.approve() + + mock_client = MagicMock() + with boto3_mocking.clients.handler_for("sesv2", mock_client): + with less_console_noise(): + # skip using the submit method + application.status = DomainApplication.ApplicationStatus.SUBMITTED + application.approve() # should be a role for this user domain = Domain.objects.get(name="igorville.gov") @@ -646,13 +657,18 @@ class TestDomainInfo(TestCase): """Test creation of Domain Information when approved.""" + @boto3_mocking.patching def test_approval_creates_info(self): draft_domain, _ = DraftDomain.objects.get_or_create(name="igorville.gov") user, _ = User.objects.get_or_create() application = DomainApplication.objects.create(creator=user, requested_domain=draft_domain) - # skip using the submit method - application.status = DomainApplication.ApplicationStatus.SUBMITTED - application.approve() + + mock_client = MagicMock() + with boto3_mocking.clients.handler_for("sesv2", mock_client): + with less_console_noise(): + # skip using the submit method + application.status = DomainApplication.ApplicationStatus.SUBMITTED + application.approve() # should be an information present for this domain domain = Domain.objects.get(name="igorville.gov") @@ -754,11 +770,12 @@ class TestUser(TestCase): caps_email = "MAYOR@igorville.gov" # mock the domain invitation save routine with patch("registrar.models.DomainInvitation.save") as save_mock: - DomainInvitation.objects.get_or_create(email=caps_email, domain=self.domain) - self.user.check_domain_invitations_on_login() - # if check_domain_invitations_on_login properly matches exactly one - # Domain Invitation, then save routine should be called exactly once - save_mock.assert_called_once() + with less_console_noise(): + DomainInvitation.objects.get_or_create(email=caps_email, domain=self.domain) + self.user.check_domain_invitations_on_login() + # if check_domain_invitations_on_login properly matches exactly one + # Domain Invitation, then save routine should be called exactly once + save_mock.assert_called_once() class TestContact(TestCase): diff --git a/src/registrar/tests/test_models_domain.py b/src/registrar/tests/test_models_domain.py index 39f63c942..9c3e46c6e 100644 --- a/src/registrar/tests/test_models_domain.py +++ b/src/registrar/tests/test_models_domain.py @@ -29,8 +29,9 @@ from epplibwrapper import ( RegistryError, ErrorCode, ) -from .common import MockEppLib +from .common import MockEppLib, less_console_noise import logging +import boto3_mocking # type: ignore logger = logging.getLogger(__name__) @@ -249,7 +250,8 @@ class TestDomainCache(MockEppLib): class TestDomainCreation(MockEppLib): """Rule: An approved domain application must result in a domain""" - + + @boto3_mocking.patching def test_approved_application_creates_domain_locally(self): """ Scenario: Analyst approves a domain application @@ -260,10 +262,14 @@ class TestDomainCreation(MockEppLib): draft_domain, _ = DraftDomain.objects.get_or_create(name="igorville.gov") user, _ = User.objects.get_or_create() application = DomainApplication.objects.create(creator=user, requested_domain=draft_domain) - # skip using the submit method - application.status = DomainApplication.ApplicationStatus.SUBMITTED - # transition to approve state - application.approve() + + mock_client = MagicMock() + with boto3_mocking.clients.handler_for("sesv2", mock_client): + with less_console_noise(): + # skip using the submit method + application.status = DomainApplication.ApplicationStatus.SUBMITTED + # transition to approve state + application.approve() # should have information present for this domain domain = Domain.objects.get(name="igorville.gov") self.assertTrue(domain) diff --git a/src/registrar/tests/test_views.py b/src/registrar/tests/test_views.py index 57fa03f52..ea6b95c2e 100644 --- a/src/registrar/tests/test_views.py +++ b/src/registrar/tests/test_views.py @@ -1349,6 +1349,7 @@ class TestDomainManagers(TestDomainOverview): response = self.client.get(reverse("domain-users-add", kwargs={"pk": self.domain.id})) self.assertContains(response, "Add a domain manager") + @boto3_mocking.patching def test_domain_user_add_form(self): """Adding an existing user works.""" other_user, _ = get_user_model().objects.get_or_create(email="mayor@igorville.gov") @@ -1358,7 +1359,11 @@ class TestDomainManagers(TestDomainOverview): add_page.form["email"] = "mayor@igorville.gov" self.app.set_cookie(settings.SESSION_COOKIE_NAME, session_id) - success_result = add_page.form.submit() + + mock_client = MagicMock() + with boto3_mocking.clients.handler_for("sesv2", mock_client): + with less_console_noise(): + success_result = add_page.form.submit() self.assertEqual(success_result.status_code, 302) self.assertEqual( @@ -1387,7 +1392,12 @@ class TestDomainManagers(TestDomainOverview): session_id = self.app.cookies[settings.SESSION_COOKIE_NAME] add_page.form["email"] = email_address self.app.set_cookie(settings.SESSION_COOKIE_NAME, session_id) - success_result = add_page.form.submit() + + mock_client = MagicMock() + with boto3_mocking.clients.handler_for("sesv2", mock_client): + with less_console_noise(): + success_result = add_page.form.submit() + self.app.set_cookie(settings.SESSION_COOKIE_NAME, session_id) success_page = success_result.follow() @@ -1413,7 +1423,12 @@ class TestDomainManagers(TestDomainOverview): session_id = self.app.cookies[settings.SESSION_COOKIE_NAME] add_page.form["email"] = caps_email_address self.app.set_cookie(settings.SESSION_COOKIE_NAME, session_id) - success_result = add_page.form.submit() + + mock_client = MagicMock() + with boto3_mocking.clients.handler_for("sesv2", mock_client): + with less_console_noise(): + success_result = add_page.form.submit() + self.app.set_cookie(settings.SESSION_COOKIE_NAME, session_id) success_page = success_result.follow() @@ -1433,11 +1448,12 @@ class TestDomainManagers(TestDomainOverview): mock_client = MagicMock() mock_client_instance = mock_client.return_value with boto3_mocking.clients.handler_for("sesv2", mock_client): - add_page = self.app.get(reverse("domain-users-add", kwargs={"pk": self.domain.id})) - session_id = self.app.cookies[settings.SESSION_COOKIE_NAME] - add_page.form["email"] = email_address - self.app.set_cookie(settings.SESSION_COOKIE_NAME, session_id) - add_page.form.submit() + with less_console_noise(): + add_page = self.app.get(reverse("domain-users-add", kwargs={"pk": self.domain.id})) + session_id = self.app.cookies[settings.SESSION_COOKIE_NAME] + add_page.form["email"] = email_address + self.app.set_cookie(settings.SESSION_COOKIE_NAME, session_id) + add_page.form.submit() # check the mock instance to see if `send_email` was called right mock_client_instance.send_email.assert_called_once_with( FromEmailAddress=settings.DEFAULT_FROM_EMAIL, @@ -1478,7 +1494,11 @@ class TestDomainManagers(TestDomainOverview): session_id = self.app.cookies[settings.SESSION_COOKIE_NAME] add_page.form["email"] = email_address self.app.set_cookie(settings.SESSION_COOKIE_NAME, session_id) - add_page.form.submit() + + mock_client = MagicMock() + with boto3_mocking.clients.handler_for("sesv2", mock_client): + with less_console_noise(): + add_page.form.submit() # user was invited, create them new_user = User.objects.create(username=email_address, email=email_address) @@ -1533,6 +1553,7 @@ class TestDomainNameservers(TestDomainOverview): # attempt to submit the form without two hosts, both subdomains, # only one has ips nameservers_page.form["form-1-server"] = "ns2.igorville.gov" + with less_console_noise(): # swallow log warning message result = nameservers_page.form.submit() # form submission was a post with an error, response should be a 200 From d3ceb6495652f151a55b366204e9c4cd1bd7e1a2 Mon Sep 17 00:00:00 2001 From: zandercymatics <141044360+zandercymatics@users.noreply.github.com> Date: Thu, 21 Dec 2023 12:27:18 -0700 Subject: [PATCH 06/34] Black formatting --- src/registrar/tests/test_admin.py | 2 +- src/registrar/tests/test_models.py | 6 +++--- src/registrar/tests/test_models_domain.py | 2 +- src/registrar/tests/test_views.py | 2 +- src/registrar/utility/email.py | 1 + 5 files changed, 7 insertions(+), 6 deletions(-) diff --git a/src/registrar/tests/test_admin.py b/src/registrar/tests/test_admin.py index 55a7c788d..ab2609c40 100644 --- a/src/registrar/tests/test_admin.py +++ b/src/registrar/tests/test_admin.py @@ -572,7 +572,7 @@ class TestDomainApplicationAdmin(MockEppLib): # Perform assertions on the mock call itself mock_client_instance.send_email.assert_called_once() - + @boto3_mocking.patching def test_save_model_sets_restricted_status_on_user(self): # make sure there is no user with this email diff --git a/src/registrar/tests/test_models.py b/src/registrar/tests/test_models.py index b496e2358..d6053efb5 100644 --- a/src/registrar/tests/test_models.py +++ b/src/registrar/tests/test_models.py @@ -313,7 +313,7 @@ class TestDomainApplication(TestCase): against transition rules""" application = completed_application(status=DomainApplication.ApplicationStatus.APPROVED) - + mock_client = MockSESClient with boto3_mocking.clients.handler_for("sesv2", mock_client): with self.assertRaises(TransitionNotAllowed): @@ -624,7 +624,7 @@ class TestDomainApplication(TestCase): mock_client = MockSESClient with boto3_mocking.clients.handler_for("sesv2", mock_client): - # Use patch to temporarily replace is_active with the custom implementation + # Use patch to temporarily replace is_active with the custom implementation with patch.object(Domain, "is_active", custom_is_active): # Now, when you call is_active on Domain, it will return True with self.assertRaises(TransitionNotAllowed): @@ -640,7 +640,7 @@ class TestPermissions(TestCase): draft_domain, _ = DraftDomain.objects.get_or_create(name="igorville.gov") user, _ = User.objects.get_or_create() application = DomainApplication.objects.create(creator=user, requested_domain=draft_domain) - + mock_client = MagicMock() with boto3_mocking.clients.handler_for("sesv2", mock_client): with less_console_noise(): diff --git a/src/registrar/tests/test_models_domain.py b/src/registrar/tests/test_models_domain.py index 9c3e46c6e..74d442eea 100644 --- a/src/registrar/tests/test_models_domain.py +++ b/src/registrar/tests/test_models_domain.py @@ -250,7 +250,7 @@ class TestDomainCache(MockEppLib): class TestDomainCreation(MockEppLib): """Rule: An approved domain application must result in a domain""" - + @boto3_mocking.patching def test_approved_application_creates_domain_locally(self): """ diff --git a/src/registrar/tests/test_views.py b/src/registrar/tests/test_views.py index ea6b95c2e..caecb6c7b 100644 --- a/src/registrar/tests/test_views.py +++ b/src/registrar/tests/test_views.py @@ -1553,7 +1553,7 @@ class TestDomainNameservers(TestDomainOverview): # attempt to submit the form without two hosts, both subdomains, # only one has ips nameservers_page.form["form-1-server"] = "ns2.igorville.gov" - + with less_console_noise(): # swallow log warning message result = nameservers_page.form.submit() # form submission was a post with an error, response should be a 200 diff --git a/src/registrar/utility/email.py b/src/registrar/utility/email.py index cb1550801..d56c02cbf 100644 --- a/src/registrar/utility/email.py +++ b/src/registrar/utility/email.py @@ -8,6 +8,7 @@ from django.template.loader import get_template logger = logging.getLogger(__name__) + class EmailSendingError(RuntimeError): """Local error for handling all failures when sending email.""" From 493edd3834b3adea8d26b47550c6a892b887884d Mon Sep 17 00:00:00 2001 From: zandercymatics <141044360+zandercymatics@users.noreply.github.com> Date: Thu, 21 Dec 2023 12:42:10 -0700 Subject: [PATCH 07/34] O(mock) --- src/registrar/tests/test_emails.py | 35 +-- src/registrar/tests/test_models.py | 199 +++++++++++------- .../test_transition_domain_migrations.py | 29 ++- 3 files changed, 160 insertions(+), 103 deletions(-) diff --git a/src/registrar/tests/test_emails.py b/src/registrar/tests/test_emails.py index eb6da072d..61c950255 100644 --- a/src/registrar/tests/test_emails.py +++ b/src/registrar/tests/test_emails.py @@ -3,7 +3,7 @@ from unittest.mock import MagicMock from django.test import TestCase -from .common import completed_application +from .common import completed_application, less_console_noise import boto3_mocking # type: ignore @@ -20,7 +20,8 @@ class TestEmails(TestCase): application = completed_application() with boto3_mocking.clients.handler_for("sesv2", self.mock_client_class): - application.submit() + with less_console_noise(): + application.submit() # check that an email was sent self.assertTrue(self.mock_client.send_email.called) @@ -56,7 +57,8 @@ class TestEmails(TestCase): """Test line spacing without current_website.""" application = completed_application(has_current_website=False) with boto3_mocking.clients.handler_for("sesv2", self.mock_client_class): - application.submit() + with less_console_noise(): + application.submit() _, kwargs = self.mock_client.send_email.call_args body = kwargs["Content"]["Simple"]["Body"]["Text"]["Data"] self.assertNotIn("Current website for your organization:", body) @@ -68,7 +70,8 @@ class TestEmails(TestCase): """Test line spacing with current_website.""" application = completed_application(has_current_website=True) with boto3_mocking.clients.handler_for("sesv2", self.mock_client_class): - application.submit() + with less_console_noise(): + application.submit() _, kwargs = self.mock_client.send_email.call_args body = kwargs["Content"]["Simple"]["Body"]["Text"]["Data"] self.assertIn("Current website for your organization:", body) @@ -81,7 +84,8 @@ class TestEmails(TestCase): """Test line spacing with other contacts.""" application = completed_application(has_other_contacts=True) with boto3_mocking.clients.handler_for("sesv2", self.mock_client_class): - application.submit() + with less_console_noise(): + application.submit() _, kwargs = self.mock_client.send_email.call_args body = kwargs["Content"]["Simple"]["Body"]["Text"]["Data"] self.assertIn("Other employees from your organization:", body) @@ -94,7 +98,8 @@ class TestEmails(TestCase): """Test line spacing without other contacts.""" application = completed_application(has_other_contacts=False) with boto3_mocking.clients.handler_for("sesv2", self.mock_client_class): - application.submit() + with less_console_noise(): + application.submit() _, kwargs = self.mock_client.send_email.call_args body = kwargs["Content"]["Simple"]["Body"]["Text"]["Data"] self.assertNotIn("Other employees from your organization:", body) @@ -106,7 +111,8 @@ class TestEmails(TestCase): """Test line spacing with alternative .gov domain.""" application = completed_application(has_alternative_gov_domain=True) with boto3_mocking.clients.handler_for("sesv2", self.mock_client_class): - application.submit() + with less_console_noise(): + application.submit() _, kwargs = self.mock_client.send_email.call_args body = kwargs["Content"]["Simple"]["Body"]["Text"]["Data"] self.assertIn("city1.gov", body) @@ -118,7 +124,8 @@ class TestEmails(TestCase): """Test line spacing without alternative .gov domain.""" application = completed_application(has_alternative_gov_domain=False) with boto3_mocking.clients.handler_for("sesv2", self.mock_client_class): - application.submit() + with less_console_noise(): + application.submit() _, kwargs = self.mock_client.send_email.call_args body = kwargs["Content"]["Simple"]["Body"]["Text"]["Data"] self.assertNotIn("city1.gov", body) @@ -130,7 +137,8 @@ class TestEmails(TestCase): """Test line spacing with about your organization.""" application = completed_application(has_about_your_organization=True) with boto3_mocking.clients.handler_for("sesv2", self.mock_client_class): - application.submit() + with less_console_noise(): + application.submit() _, kwargs = self.mock_client.send_email.call_args body = kwargs["Content"]["Simple"]["Body"]["Text"]["Data"] self.assertIn("About your organization:", body) @@ -142,7 +150,8 @@ class TestEmails(TestCase): """Test line spacing without about your organization.""" application = completed_application(has_about_your_organization=False) with boto3_mocking.clients.handler_for("sesv2", self.mock_client_class): - application.submit() + with less_console_noise(): + application.submit() _, kwargs = self.mock_client.send_email.call_args body = kwargs["Content"]["Simple"]["Body"]["Text"]["Data"] self.assertNotIn("About your organization:", body) @@ -154,7 +163,8 @@ class TestEmails(TestCase): """Test line spacing with anything else.""" application = completed_application(has_anything_else=True) with boto3_mocking.clients.handler_for("sesv2", self.mock_client_class): - application.submit() + with less_console_noise(): + application.submit() _, kwargs = self.mock_client.send_email.call_args body = kwargs["Content"]["Simple"]["Body"]["Text"]["Data"] # spacing should be right between adjacent elements @@ -165,7 +175,8 @@ class TestEmails(TestCase): """Test line spacing without anything else.""" application = completed_application(has_anything_else=False) with boto3_mocking.clients.handler_for("sesv2", self.mock_client_class): - application.submit() + with less_console_noise(): + application.submit() _, kwargs = self.mock_client.send_email.call_args body = kwargs["Content"]["Simple"]["Body"]["Text"]["Data"] self.assertNotIn("Anything else", body) diff --git a/src/registrar/tests/test_models.py b/src/registrar/tests/test_models.py index d6053efb5..1e776c891 100644 --- a/src/registrar/tests/test_models.py +++ b/src/registrar/tests/test_models.py @@ -100,9 +100,10 @@ class TestDomainApplication(TestCase): mock_client = MockSESClient with boto3_mocking.clients.handler_for("sesv2", mock_client): - with self.assertRaises(ValueError): - # can't submit an application with a null domain name - application.submit() + with less_console_noise(): + with self.assertRaises(ValueError): + # can't submit an application with a null domain name + application.submit() def test_status_fsm_submit_succeed(self): user, _ = User.objects.get_or_create() @@ -151,8 +152,9 @@ class TestDomainApplication(TestCase): application = completed_application(status=DomainApplication.ApplicationStatus.SUBMITTED) with boto3_mocking.clients.handler_for("sesv2", MockSESClient): - with self.assertRaises(TransitionNotAllowed): - application.submit() + with less_console_noise(): + with self.assertRaises(TransitionNotAllowed): + application.submit() def test_transition_not_allowed_in_review_submitted(self): """Create an application with status in review and call submit @@ -161,8 +163,9 @@ class TestDomainApplication(TestCase): application = completed_application(status=DomainApplication.ApplicationStatus.IN_REVIEW) with boto3_mocking.clients.handler_for("sesv2", MockSESClient): - with self.assertRaises(TransitionNotAllowed): - application.submit() + with less_console_noise(): + with self.assertRaises(TransitionNotAllowed): + application.submit() def test_transition_not_allowed_approved_submitted(self): """Create an application with status approved and call submit @@ -172,8 +175,9 @@ class TestDomainApplication(TestCase): mock_client = MockSESClient with boto3_mocking.clients.handler_for("sesv2", mock_client): - with self.assertRaises(TransitionNotAllowed): - application.submit() + with less_console_noise(): + with self.assertRaises(TransitionNotAllowed): + application.submit() def test_transition_not_allowed_rejected_submitted(self): """Create an application with status rejected and call submit @@ -183,8 +187,9 @@ class TestDomainApplication(TestCase): mock_client = MockSESClient with boto3_mocking.clients.handler_for("sesv2", mock_client): - with self.assertRaises(TransitionNotAllowed): - application.submit() + with less_console_noise(): + with self.assertRaises(TransitionNotAllowed): + application.submit() def test_transition_not_allowed_ineligible_submitted(self): """Create an application with status ineligible and call submit @@ -194,8 +199,9 @@ class TestDomainApplication(TestCase): mock_client = MockSESClient with boto3_mocking.clients.handler_for("sesv2", mock_client): - with self.assertRaises(TransitionNotAllowed): - application.submit() + with less_console_noise(): + with self.assertRaises(TransitionNotAllowed): + application.submit() def test_transition_not_allowed_started_in_review(self): """Create an application with status started and call in_review @@ -205,8 +211,9 @@ class TestDomainApplication(TestCase): mock_client = MockSESClient with boto3_mocking.clients.handler_for("sesv2", mock_client): - with self.assertRaises(TransitionNotAllowed): - application.in_review() + with less_console_noise(): + with self.assertRaises(TransitionNotAllowed): + application.in_review() def test_transition_not_allowed_in_review_in_review(self): """Create an application with status in review and call in_review @@ -216,8 +223,9 @@ class TestDomainApplication(TestCase): mock_client = MockSESClient with boto3_mocking.clients.handler_for("sesv2", mock_client): - with self.assertRaises(TransitionNotAllowed): - application.in_review() + with less_console_noise(): + with self.assertRaises(TransitionNotAllowed): + application.in_review() def test_transition_not_allowed_approved_in_review(self): """Create an application with status approved and call in_review @@ -227,8 +235,9 @@ class TestDomainApplication(TestCase): mock_client = MockSESClient with boto3_mocking.clients.handler_for("sesv2", mock_client): - with self.assertRaises(TransitionNotAllowed): - application.in_review() + with less_console_noise(): + with self.assertRaises(TransitionNotAllowed): + application.in_review() def test_transition_not_allowed_action_needed_in_review(self): """Create an application with status action needed and call in_review @@ -238,8 +247,9 @@ class TestDomainApplication(TestCase): mock_client = MockSESClient with boto3_mocking.clients.handler_for("sesv2", mock_client): - with self.assertRaises(TransitionNotAllowed): - application.in_review() + with less_console_noise(): + with self.assertRaises(TransitionNotAllowed): + application.in_review() def test_transition_not_allowed_rejected_in_review(self): """Create an application with status rejected and call in_review @@ -249,8 +259,9 @@ class TestDomainApplication(TestCase): mock_client = MockSESClient with boto3_mocking.clients.handler_for("sesv2", mock_client): - with self.assertRaises(TransitionNotAllowed): - application.in_review() + with less_console_noise(): + with self.assertRaises(TransitionNotAllowed): + application.in_review() def test_transition_not_allowed_withdrawn_in_review(self): """Create an application with status withdrawn and call in_review @@ -272,8 +283,9 @@ class TestDomainApplication(TestCase): mock_client = MockSESClient with boto3_mocking.clients.handler_for("sesv2", mock_client): - with self.assertRaises(TransitionNotAllowed): - application.in_review() + with less_console_noise(): + with self.assertRaises(TransitionNotAllowed): + application.in_review() def test_transition_not_allowed_started_action_needed(self): """Create an application with status started and call action_needed @@ -283,8 +295,9 @@ class TestDomainApplication(TestCase): mock_client = MockSESClient with boto3_mocking.clients.handler_for("sesv2", mock_client): - with self.assertRaises(TransitionNotAllowed): - application.action_needed() + with less_console_noise(): + with self.assertRaises(TransitionNotAllowed): + application.action_needed() def test_transition_not_allowed_submitted_action_needed(self): """Create an application with status submitted and call action_needed @@ -294,8 +307,9 @@ class TestDomainApplication(TestCase): mock_client = MockSESClient with boto3_mocking.clients.handler_for("sesv2", mock_client): - with self.assertRaises(TransitionNotAllowed): - application.action_needed() + with less_console_noise(): + with self.assertRaises(TransitionNotAllowed): + application.action_needed() def test_transition_not_allowed_action_needed_action_needed(self): """Create an application with status action needed and call action_needed @@ -305,8 +319,9 @@ class TestDomainApplication(TestCase): mock_client = MockSESClient with boto3_mocking.clients.handler_for("sesv2", mock_client): - with self.assertRaises(TransitionNotAllowed): - application.action_needed() + with less_console_noise(): + with self.assertRaises(TransitionNotAllowed): + application.action_needed() def test_transition_not_allowed_approved_action_needed(self): """Create an application with status approved and call action_needed @@ -316,8 +331,9 @@ class TestDomainApplication(TestCase): mock_client = MockSESClient with boto3_mocking.clients.handler_for("sesv2", mock_client): - with self.assertRaises(TransitionNotAllowed): - application.action_needed() + with less_console_noise(): + with self.assertRaises(TransitionNotAllowed): + application.action_needed() def test_transition_not_allowed_withdrawn_action_needed(self): """Create an application with status withdrawn and call action_needed @@ -339,8 +355,9 @@ class TestDomainApplication(TestCase): mock_client = MockSESClient with boto3_mocking.clients.handler_for("sesv2", mock_client): - with self.assertRaises(TransitionNotAllowed): - application.action_needed() + with less_console_noise(): + with self.assertRaises(TransitionNotAllowed): + application.action_needed() def test_transition_not_allowed_started_approved(self): """Create an application with status started and call approve @@ -350,8 +367,9 @@ class TestDomainApplication(TestCase): mock_client = MockSESClient with boto3_mocking.clients.handler_for("sesv2", mock_client): - with self.assertRaises(TransitionNotAllowed): - application.approve() + with less_console_noise(): + with self.assertRaises(TransitionNotAllowed): + application.approve() def test_transition_not_allowed_approved_approved(self): """Create an application with status approved and call approve @@ -361,8 +379,9 @@ class TestDomainApplication(TestCase): mock_client = MockSESClient with boto3_mocking.clients.handler_for("sesv2", mock_client): - with self.assertRaises(TransitionNotAllowed): - application.approve() + with less_console_noise(): + with self.assertRaises(TransitionNotAllowed): + application.approve() def test_transition_not_allowed_action_needed_approved(self): """Create an application with status action needed and call approve @@ -372,8 +391,9 @@ class TestDomainApplication(TestCase): mock_client = MockSESClient with boto3_mocking.clients.handler_for("sesv2", mock_client): - with self.assertRaises(TransitionNotAllowed): - application.approve() + with less_console_noise(): + with self.assertRaises(TransitionNotAllowed): + application.approve() def test_transition_not_allowed_withdrawn_approved(self): """Create an application with status withdrawn and call approve @@ -395,8 +415,9 @@ class TestDomainApplication(TestCase): mock_client = MockSESClient with boto3_mocking.clients.handler_for("sesv2", mock_client): - with self.assertRaises(TransitionNotAllowed): - application.withdraw() + with less_console_noise(): + with self.assertRaises(TransitionNotAllowed): + application.withdraw() def test_transition_not_allowed_approved_withdrawn(self): """Create an application with status approved and call withdraw @@ -406,8 +427,9 @@ class TestDomainApplication(TestCase): mock_client = MockSESClient with boto3_mocking.clients.handler_for("sesv2", mock_client): - with self.assertRaises(TransitionNotAllowed): - application.withdraw() + with less_console_noise(): + with self.assertRaises(TransitionNotAllowed): + application.withdraw() def test_transition_not_allowed_action_needed_withdrawn(self): """Create an application with status action needed and call withdraw @@ -417,8 +439,9 @@ class TestDomainApplication(TestCase): mock_client = MockSESClient with boto3_mocking.clients.handler_for("sesv2", mock_client): - with self.assertRaises(TransitionNotAllowed): - application.withdraw() + with less_console_noise(): + with self.assertRaises(TransitionNotAllowed): + application.withdraw() def test_transition_not_allowed_rejected_withdrawn(self): """Create an application with status rejected and call withdraw @@ -428,8 +451,9 @@ class TestDomainApplication(TestCase): mock_client = MockSESClient with boto3_mocking.clients.handler_for("sesv2", mock_client): - with self.assertRaises(TransitionNotAllowed): - application.withdraw() + with less_console_noise(): + with self.assertRaises(TransitionNotAllowed): + application.withdraw() def test_transition_not_allowed_withdrawn_withdrawn(self): """Create an application with status withdrawn and call withdraw @@ -451,8 +475,9 @@ class TestDomainApplication(TestCase): mock_client = MockSESClient with boto3_mocking.clients.handler_for("sesv2", mock_client): - with self.assertRaises(TransitionNotAllowed): - application.withdraw() + with less_console_noise(): + with self.assertRaises(TransitionNotAllowed): + application.withdraw() def test_transition_not_allowed_started_rejected(self): """Create an application with status started and call reject @@ -462,8 +487,9 @@ class TestDomainApplication(TestCase): mock_client = MockSESClient with boto3_mocking.clients.handler_for("sesv2", mock_client): - with self.assertRaises(TransitionNotAllowed): - application.reject() + with less_console_noise(): + with self.assertRaises(TransitionNotAllowed): + application.reject() def test_transition_not_allowed_submitted_rejected(self): """Create an application with status submitted and call reject @@ -473,8 +499,9 @@ class TestDomainApplication(TestCase): mock_client = MockSESClient with boto3_mocking.clients.handler_for("sesv2", mock_client): - with self.assertRaises(TransitionNotAllowed): - application.reject() + with less_console_noise(): + with self.assertRaises(TransitionNotAllowed): + application.reject() def test_transition_not_allowed_action_needed_rejected(self): """Create an application with status action needed and call reject @@ -484,8 +511,9 @@ class TestDomainApplication(TestCase): mock_client = MockSESClient with boto3_mocking.clients.handler_for("sesv2", mock_client): - with self.assertRaises(TransitionNotAllowed): - application.reject() + with less_console_noise(): + with self.assertRaises(TransitionNotAllowed): + application.reject() def test_transition_not_allowed_withdrawn_rejected(self): """Create an application with status withdrawn and call reject @@ -507,8 +535,9 @@ class TestDomainApplication(TestCase): mock_client = MockSESClient with boto3_mocking.clients.handler_for("sesv2", mock_client): - with self.assertRaises(TransitionNotAllowed): - application.reject() + with less_console_noise(): + with self.assertRaises(TransitionNotAllowed): + application.reject() def test_transition_not_allowed_ineligible_rejected(self): """Create an application with status ineligible and call reject @@ -518,8 +547,9 @@ class TestDomainApplication(TestCase): mock_client = MockSESClient with boto3_mocking.clients.handler_for("sesv2", mock_client): - with self.assertRaises(TransitionNotAllowed): - application.reject() + with less_console_noise(): + with self.assertRaises(TransitionNotAllowed): + application.reject() def test_transition_not_allowed_approved_rejected_when_domain_is_active(self): """Create an application with status approved, create a matching domain that @@ -536,11 +566,12 @@ class TestDomainApplication(TestCase): mock_client = MockSESClient with boto3_mocking.clients.handler_for("sesv2", mock_client): - # Use patch to temporarily replace is_active with the custom implementation - with patch.object(Domain, "is_active", custom_is_active): - # Now, when you call is_active on Domain, it will return True - with self.assertRaises(TransitionNotAllowed): - application.reject() + with less_console_noise(): + # Use patch to temporarily replace is_active with the custom implementation + with patch.object(Domain, "is_active", custom_is_active): + # Now, when you call is_active on Domain, it will return True + with self.assertRaises(TransitionNotAllowed): + application.reject() def test_transition_not_allowed_started_ineligible(self): """Create an application with status started and call reject @@ -550,8 +581,9 @@ class TestDomainApplication(TestCase): mock_client = MockSESClient with boto3_mocking.clients.handler_for("sesv2", mock_client): - with self.assertRaises(TransitionNotAllowed): - application.reject_with_prejudice() + with less_console_noise(): + with self.assertRaises(TransitionNotAllowed): + application.reject_with_prejudice() def test_transition_not_allowed_submitted_ineligible(self): """Create an application with status submitted and call reject @@ -561,8 +593,9 @@ class TestDomainApplication(TestCase): mock_client = MockSESClient with boto3_mocking.clients.handler_for("sesv2", mock_client): - with self.assertRaises(TransitionNotAllowed): - application.reject_with_prejudice() + with less_console_noise(): + with self.assertRaises(TransitionNotAllowed): + application.reject_with_prejudice() def test_transition_not_allowed_action_needed_ineligible(self): """Create an application with status action needed and call reject @@ -572,8 +605,9 @@ class TestDomainApplication(TestCase): mock_client = MockSESClient with boto3_mocking.clients.handler_for("sesv2", mock_client): - with self.assertRaises(TransitionNotAllowed): - application.reject_with_prejudice() + with less_console_noise(): + with self.assertRaises(TransitionNotAllowed): + application.reject_with_prejudice() def test_transition_not_allowed_withdrawn_ineligible(self): """Create an application with status withdrawn and call reject @@ -595,8 +629,9 @@ class TestDomainApplication(TestCase): mock_client = MockSESClient with boto3_mocking.clients.handler_for("sesv2", mock_client): - with self.assertRaises(TransitionNotAllowed): - application.reject_with_prejudice() + with less_console_noise(): + with self.assertRaises(TransitionNotAllowed): + application.reject_with_prejudice() def test_transition_not_allowed_ineligible_ineligible(self): """Create an application with status ineligible and call reject @@ -606,8 +641,9 @@ class TestDomainApplication(TestCase): mock_client = MockSESClient with boto3_mocking.clients.handler_for("sesv2", mock_client): - with self.assertRaises(TransitionNotAllowed): - application.reject_with_prejudice() + with less_console_noise(): + with self.assertRaises(TransitionNotAllowed): + application.reject_with_prejudice() def test_transition_not_allowed_approved_ineligible_when_domain_is_active(self): """Create an application with status approved, create a matching domain that @@ -624,11 +660,12 @@ class TestDomainApplication(TestCase): mock_client = MockSESClient with boto3_mocking.clients.handler_for("sesv2", mock_client): - # Use patch to temporarily replace is_active with the custom implementation - with patch.object(Domain, "is_active", custom_is_active): - # Now, when you call is_active on Domain, it will return True - with self.assertRaises(TransitionNotAllowed): - application.reject_with_prejudice() + with less_console_noise(): + # Use patch to temporarily replace is_active with the custom implementation + with patch.object(Domain, "is_active", custom_is_active): + # Now, when you call is_active on Domain, it will return True + with self.assertRaises(TransitionNotAllowed): + application.reject_with_prejudice() class TestPermissions(TestCase): diff --git a/src/registrar/tests/test_transition_domain_migrations.py b/src/registrar/tests/test_transition_domain_migrations.py index f3fd76e88..b19d79981 100644 --- a/src/registrar/tests/test_transition_domain_migrations.py +++ b/src/registrar/tests/test_transition_domain_migrations.py @@ -14,11 +14,12 @@ from registrar.models import ( ) from django.core.management import call_command -from unittest.mock import patch +from unittest.mock import MagicMock, patch from registrar.models.contact import Contact from .common import MockEppLib, less_console_noise +import boto3_mocking # type: ignore class TestExtendExpirationDates(MockEppLib): @@ -1018,7 +1019,8 @@ class TestMigrations(TestCase): expected_missing_domain_informations, expected_missing_domain_invitations, ) - + + @boto3_mocking.patching def test_send_domain_invitations_email(self): """Can send only a single domain invitation email.""" with less_console_noise(): @@ -1027,9 +1029,12 @@ class TestMigrations(TestCase): # this is one of the email addresses in data/test_contacts.txt output_stream = StringIO() - # also have to re-point the logging handlers to output_stream - with less_console_noise(output_stream): - call_command("send_domain_invitations", "testuser@gmail.com", stdout=output_stream) + + mock_client = MagicMock() + with boto3_mocking.clients.handler_for("sesv2", mock_client): + # also have to re-point the logging handlers to output_stream + with less_console_noise(output_stream): + call_command("send_domain_invitations", "testuser@gmail.com", stdout=output_stream) # Check that we had the right numbers in our output output = output_stream.getvalue() @@ -1037,6 +1042,7 @@ class TestMigrations(TestCase): self.assertIn("Found 1 transition domains", output) self.assertTrue("would send email to testuser@gmail.com", output) + @boto3_mocking.patching def test_send_domain_invitations_two_emails(self): """Can send only a single domain invitation email.""" with less_console_noise(): @@ -1045,11 +1051,14 @@ class TestMigrations(TestCase): # these are two email addresses in data/test_contacts.txt output_stream = StringIO() - # also have to re-point the logging handlers to output_stream - with less_console_noise(output_stream): - call_command( - "send_domain_invitations", "testuser@gmail.com", "agustina.wyman7@test.com", stdout=output_stream - ) + + mock_client = MagicMock() + with boto3_mocking.clients.handler_for("sesv2", mock_client): + # also have to re-point the logging handlers to output_stream + with less_console_noise(output_stream): + call_command( + "send_domain_invitations", "testuser@gmail.com", "agustina.wyman7@test.com", stdout=output_stream + ) # Check that we had the right numbers in our output output = output_stream.getvalue() From 0933a260b9abb8512e2cfa2f8cf745a262ff2368 Mon Sep 17 00:00:00 2001 From: zandercymatics <141044360+zandercymatics@users.noreply.github.com> Date: Thu, 21 Dec 2023 12:43:27 -0700 Subject: [PATCH 08/34] Update test_transition_domain_migrations.py --- src/registrar/tests/test_transition_domain_migrations.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/registrar/tests/test_transition_domain_migrations.py b/src/registrar/tests/test_transition_domain_migrations.py index b19d79981..62df96efc 100644 --- a/src/registrar/tests/test_transition_domain_migrations.py +++ b/src/registrar/tests/test_transition_domain_migrations.py @@ -1019,7 +1019,7 @@ class TestMigrations(TestCase): expected_missing_domain_informations, expected_missing_domain_invitations, ) - + @boto3_mocking.patching def test_send_domain_invitations_email(self): """Can send only a single domain invitation email.""" @@ -1029,7 +1029,7 @@ class TestMigrations(TestCase): # this is one of the email addresses in data/test_contacts.txt output_stream = StringIO() - + mock_client = MagicMock() with boto3_mocking.clients.handler_for("sesv2", mock_client): # also have to re-point the logging handlers to output_stream From a75ddb213c43bff7daf111ffc23bbfb262234f80 Mon Sep 17 00:00:00 2001 From: zandercymatics <141044360+zandercymatics@users.noreply.github.com> Date: Thu, 21 Dec 2023 13:23:27 -0700 Subject: [PATCH 09/34] Update test_admin.py --- src/registrar/tests/test_admin.py | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/src/registrar/tests/test_admin.py b/src/registrar/tests/test_admin.py index ab2609c40..bd9cc8293 100644 --- a/src/registrar/tests/test_admin.py +++ b/src/registrar/tests/test_admin.py @@ -716,6 +716,7 @@ class TestDomainApplicationAdmin(MockEppLib): "Cannot edit an application with a restricted creator.", ) + @boto3_mocking.patching def test_error_when_saving_approved_to_rejected_and_domain_is_active(self): # Create an instance of the model application = completed_application(status=DomainApplication.ApplicationStatus.APPROVED) @@ -737,9 +738,12 @@ class TestDomainApplicationAdmin(MockEppLib): stack.enter_context(patch.object(Domain, "is_active", custom_is_active)) stack.enter_context(patch.object(messages, "error")) - # Simulate saving the model - application.status = DomainApplication.ApplicationStatus.REJECTED - self.admin.save_model(request, application, None, True) + mock_client = MagicMock() + with boto3_mocking.clients.handler_for("sesv2", mock_client): + with less_console_noise(): + # Simulate saving the model + application.status = DomainApplication.ApplicationStatus.REJECTED + self.admin.save_model(request, application, None, True) # Assert that the error message was called with the correct argument messages.error.assert_called_once_with( From 4506ee65f8c3425b0cc3a3202efb496a8d1bfccd Mon Sep 17 00:00:00 2001 From: zandercymatics <141044360+zandercymatics@users.noreply.github.com> Date: Fri, 22 Dec 2023 09:02:26 -0700 Subject: [PATCH 10/34] Update test_transition_domain_migrations.py --- src/registrar/tests/test_transition_domain_migrations.py | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/src/registrar/tests/test_transition_domain_migrations.py b/src/registrar/tests/test_transition_domain_migrations.py index 62df96efc..a757059dd 100644 --- a/src/registrar/tests/test_transition_domain_migrations.py +++ b/src/registrar/tests/test_transition_domain_migrations.py @@ -18,7 +18,7 @@ from unittest.mock import MagicMock, patch from registrar.models.contact import Contact -from .common import MockEppLib, less_console_noise +from .common import MockEppLib, MockSESClient, less_console_noise import boto3_mocking # type: ignore @@ -1030,8 +1030,7 @@ class TestMigrations(TestCase): # this is one of the email addresses in data/test_contacts.txt output_stream = StringIO() - mock_client = MagicMock() - with boto3_mocking.clients.handler_for("sesv2", mock_client): + with boto3_mocking.clients.handler_for("sesv2", MockSESClient): # also have to re-point the logging handlers to output_stream with less_console_noise(output_stream): call_command("send_domain_invitations", "testuser@gmail.com", stdout=output_stream) @@ -1052,8 +1051,7 @@ class TestMigrations(TestCase): # these are two email addresses in data/test_contacts.txt output_stream = StringIO() - mock_client = MagicMock() - with boto3_mocking.clients.handler_for("sesv2", mock_client): + with boto3_mocking.clients.handler_for("sesv2", MockSESClient): # also have to re-point the logging handlers to output_stream with less_console_noise(output_stream): call_command( From a64732a30161d5ec0b3a320cadb34321357f7461 Mon Sep 17 00:00:00 2001 From: zandercymatics <141044360+zandercymatics@users.noreply.github.com> Date: Fri, 22 Dec 2023 12:22:29 -0700 Subject: [PATCH 11/34] Remove unused import --- src/registrar/tests/test_transition_domain_migrations.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/registrar/tests/test_transition_domain_migrations.py b/src/registrar/tests/test_transition_domain_migrations.py index a757059dd..1fe513999 100644 --- a/src/registrar/tests/test_transition_domain_migrations.py +++ b/src/registrar/tests/test_transition_domain_migrations.py @@ -14,7 +14,7 @@ from registrar.models import ( ) from django.core.management import call_command -from unittest.mock import MagicMock, patch +from unittest.mock import patch from registrar.models.contact import Contact From cf92d4d7e9a758f5112b64d6cf4237cabafb21cc Mon Sep 17 00:00:00 2001 From: zandercymatics <141044360+zandercymatics@users.noreply.github.com> Date: Fri, 22 Dec 2023 14:42:35 -0700 Subject: [PATCH 12/34] Test case refactor --- src/registrar/fixtures_applications.py | 2 +- src/registrar/models/domain_application.py | 8 +- src/registrar/tests/test_models.py | 107 +++++++++++++++------ src/registrar/tests/test_models_domain.py | 5 +- 4 files changed, 86 insertions(+), 36 deletions(-) diff --git a/src/registrar/fixtures_applications.py b/src/registrar/fixtures_applications.py index ae82ddd8b..92094b876 100644 --- a/src/registrar/fixtures_applications.py +++ b/src/registrar/fixtures_applications.py @@ -221,5 +221,5 @@ class DomainFixture(DomainApplicationFixture): # We don't want fixtures sending out real emails to # fake email addresses, so we just skip that and log it instead - application.approve(do_fake_send_email=True) + application.approve(send_email=False) application.save() diff --git a/src/registrar/models/domain_application.py b/src/registrar/models/domain_application.py index 16b24cf40..8fca5918b 100644 --- a/src/registrar/models/domain_application.py +++ b/src/registrar/models/domain_application.py @@ -561,7 +561,7 @@ class DomainApplication(TimeStampedModel): return not self.approved_domain.is_active() return True - def _send_status_update_email(self, new_status, email_template, email_template_subject, do_fake_send_email=False): + def _send_status_update_email(self, new_status, email_template, email_template_subject, send_email=True): """Send a status update email to the submitter. The email goes to the email address that the submitter gave as their @@ -573,7 +573,7 @@ class DomainApplication(TimeStampedModel): logger.warning(f"Cannot send {new_status} email, no submitter email address.") return None - if do_fake_send_email: + if not send_email: logger.info(f"Email was not sent. Would send {new_status} email: {self.submitter.email}") return None @@ -676,7 +676,7 @@ class DomainApplication(TimeStampedModel): ], target=ApplicationStatus.APPROVED, ) - def approve(self, do_fake_send_email=False): + def approve(self, send_email=True): """Approve an application that has been submitted. This has substantial side-effects because it creates another database @@ -705,7 +705,7 @@ class DomainApplication(TimeStampedModel): "application approved", "emails/status_change_approved.txt", "emails/status_change_approved_subject.txt", - do_fake_send_email, + send_email, ) @transition( diff --git a/src/registrar/tests/test_models.py b/src/registrar/tests/test_models.py index 332f2b3ff..4d75ce6a3 100644 --- a/src/registrar/tests/test_models.py +++ b/src/registrar/tests/test_models.py @@ -19,8 +19,8 @@ from registrar.models.transition_domain import TransitionDomain # type: ignore from .common import MockSESClient, less_console_noise, completed_application from django_fsm import TransitionNotAllowed -boto3_mocking.clients.register_handler("sesv2", MockSESClient) +boto3_mocking.clients.register_handler("sesv2", MockSESClient) # The DomainApplication submit method has a side effect of sending an email # with AWS SES, so mock that out in all of these test cases @@ -52,6 +52,12 @@ class TestDomainApplication(TestCase): status=DomainApplication.ApplicationStatus.INELIGIBLE, name="ineligible.gov" ) + self.mock_client = MockSESClient() + + def tearDown(self): + super().tearDown() + self.mock_client.EMAILS_SENT.clear() + def assertNotRaises(self, exception_type): """Helper method for testing allowed transitions.""" return self.assertRaises(Exception, None, exception_type) @@ -130,8 +136,8 @@ class TestDomainApplication(TestCase): user, _ = User.objects.get_or_create(username="testy") application = DomainApplication.objects.create(creator=user) - mock_client = MockSESClient - with boto3_mocking.clients.handler_for("sesv2", mock_client): + + with boto3_mocking.clients.handler_for("sesv2", self.mock_client): with less_console_noise(): with self.assertRaises(ValueError): # can't submit an application with a null domain name @@ -143,8 +149,8 @@ class TestDomainApplication(TestCase): application = DomainApplication.objects.create(creator=user, requested_domain=site) # no submitter email so this emits a log warning - mock_client = MockSESClient - with boto3_mocking.clients.handler_for("sesv2", mock_client): + + with boto3_mocking.clients.handler_for("sesv2", self.mock_client): with less_console_noise(): application.submit() self.assertEqual(application.status, application.ApplicationStatus.SUBMITTED) @@ -161,7 +167,8 @@ class TestDomainApplication(TestCase): ) application.save() - with boto3_mocking.clients.handler_for("sesv2", MockSESClient): + + with boto3_mocking.clients.handler_for("sesv2", self.mock_client): with less_console_noise(): application.submit() @@ -187,8 +194,9 @@ class TestDomainApplication(TestCase): (self.action_needed_application, TransitionNotAllowed), (self.withdrawn_application, TransitionNotAllowed), ] - - with boto3_mocking.clients.handler_for("sesv2", MockSESClient): + + + with boto3_mocking.clients.handler_for("sesv2", self.mock_client): with less_console_noise(): for application, exception_type in test_cases: with self.subTest(application=application, exception_type=exception_type): @@ -208,7 +216,8 @@ class TestDomainApplication(TestCase): (self.ineligible_application, TransitionNotAllowed), ] - with boto3_mocking.clients.handler_for("sesv2", MockSESClient): + + with boto3_mocking.clients.handler_for("sesv2", self.mock_client): with less_console_noise(): for application, exception_type in test_cases: with self.subTest(application=application, exception_type=exception_type): @@ -227,7 +236,8 @@ class TestDomainApplication(TestCase): (self.ineligible_application, TransitionNotAllowed), ] - with boto3_mocking.clients.handler_for("sesv2", MockSESClient): + + with boto3_mocking.clients.handler_for("sesv2", self.mock_client): with less_console_noise(): for application, exception_type in test_cases: with self.subTest(application=application, exception_type=exception_type): @@ -246,7 +256,8 @@ class TestDomainApplication(TestCase): (self.withdrawn_application, TransitionNotAllowed), ] - with boto3_mocking.clients.handler_for("sesv2", MockSESClient): + + with boto3_mocking.clients.handler_for("sesv2", self.mock_client): with less_console_noise(): for application, exception_type in test_cases: with self.subTest(application=application, exception_type=exception_type): @@ -264,7 +275,8 @@ class TestDomainApplication(TestCase): (self.ineligible_application, TransitionNotAllowed), ] - with boto3_mocking.clients.handler_for("sesv2", MockSESClient): + + with boto3_mocking.clients.handler_for("sesv2", self.mock_client): with less_console_noise(): for application, exception_type in test_cases: with self.subTest(application=application, exception_type=exception_type): @@ -284,7 +296,8 @@ class TestDomainApplication(TestCase): (self.withdrawn_application, TransitionNotAllowed), ] - with boto3_mocking.clients.handler_for("sesv2", MockSESClient): + + with boto3_mocking.clients.handler_for("sesv2", self.mock_client): with less_console_noise(): for application, exception_type in test_cases: with self.subTest(application=application, exception_type=exception_type): @@ -302,7 +315,8 @@ class TestDomainApplication(TestCase): (self.rejected_application, TransitionNotAllowed), ] - with boto3_mocking.clients.handler_for("sesv2", MockSESClient): + + with boto3_mocking.clients.handler_for("sesv2", self.mock_client): with less_console_noise(): for application, exception_type in test_cases: with self.subTest(application=application, exception_type=exception_type): @@ -310,6 +324,19 @@ class TestDomainApplication(TestCase): application.approve() except TransitionNotAllowed: self.fail("TransitionNotAllowed was raised, but it was not expected.") + + def test_approved_skips_sending_email(self): + """ + Test that calling .approve with send_email=False doesn't actually send + an email + """ + + with boto3_mocking.clients.handler_for("sesv2", self.mock_client): + with less_console_noise(): + self.submitted_application.approve(send_email=False) + + # Assert that no emails were sent + self.assertEqual(len(self.mock_client.EMAILS_SENT), 0) def test_approved_transition_not_allowed(self): """ @@ -322,7 +349,8 @@ class TestDomainApplication(TestCase): (self.ineligible_application, TransitionNotAllowed), ] - with boto3_mocking.clients.handler_for("sesv2", MockSESClient): + + with boto3_mocking.clients.handler_for("sesv2", self.mock_client): with less_console_noise(): for application, exception_type in test_cases: with self.subTest(application=application, exception_type=exception_type): @@ -339,7 +367,8 @@ class TestDomainApplication(TestCase): (self.action_needed_application, TransitionNotAllowed), ] - with boto3_mocking.clients.handler_for("sesv2", MockSESClient): + + with boto3_mocking.clients.handler_for("sesv2", self.mock_client): with less_console_noise(): for application, exception_type in test_cases: with self.subTest(application=application, exception_type=exception_type): @@ -360,7 +389,8 @@ class TestDomainApplication(TestCase): (self.ineligible_application, TransitionNotAllowed), ] - with boto3_mocking.clients.handler_for("sesv2", MockSESClient): + + with boto3_mocking.clients.handler_for("sesv2", self.mock_client): with less_console_noise(): for application, exception_type in test_cases: with self.subTest(application=application, exception_type=exception_type): @@ -377,7 +407,8 @@ class TestDomainApplication(TestCase): (self.approved_application, TransitionNotAllowed), ] - with boto3_mocking.clients.handler_for("sesv2", MockSESClient): + + with boto3_mocking.clients.handler_for("sesv2", self.mock_client): with less_console_noise(): for application, exception_type in test_cases: with self.subTest(application=application, exception_type=exception_type): @@ -398,7 +429,8 @@ class TestDomainApplication(TestCase): (self.ineligible_application, TransitionNotAllowed), ] - with boto3_mocking.clients.handler_for("sesv2", MockSESClient): + + with boto3_mocking.clients.handler_for("sesv2", self.mock_client): with less_console_noise(): for application, exception_type in test_cases: with self.subTest(application=application, exception_type=exception_type): @@ -416,7 +448,8 @@ class TestDomainApplication(TestCase): (self.rejected_application, TransitionNotAllowed), ] - with boto3_mocking.clients.handler_for("sesv2", MockSESClient): + + with boto3_mocking.clients.handler_for("sesv2", self.mock_client): with less_console_noise(): for application, exception_type in test_cases: with self.subTest(application=application, exception_type=exception_type): @@ -436,7 +469,8 @@ class TestDomainApplication(TestCase): (self.ineligible_application, TransitionNotAllowed), ] - with boto3_mocking.clients.handler_for("sesv2", MockSESClient): + + with boto3_mocking.clients.handler_for("sesv2", self.mock_client): with less_console_noise(): for application, exception_type in test_cases: with self.subTest(application=application, exception_type=exception_type): @@ -455,7 +489,8 @@ class TestDomainApplication(TestCase): def custom_is_active(self): return True # Override to return True - with boto3_mocking.clients.handler_for("sesv2", MockSESClient): + + with boto3_mocking.clients.handler_for("sesv2", self.mock_client): with less_console_noise(): # Use patch to temporarily replace is_active with the custom implementation with patch.object(Domain, "is_active", custom_is_active): @@ -475,7 +510,8 @@ class TestDomainApplication(TestCase): def custom_is_active(self): return True # Override to return True - with boto3_mocking.clients.handler_for("sesv2", MockSESClient): + + with boto3_mocking.clients.handler_for("sesv2", self.mock_client): with less_console_noise(): # Use patch to temporarily replace is_active with the custom implementation with patch.object(Domain, "is_active", custom_is_active): @@ -485,17 +521,24 @@ class TestDomainApplication(TestCase): class TestPermissions(TestCase): - """Test the User-Domain-Role connection.""" + def setUp(self): + super().setUp() + self.mock_client = MockSESClient() + + def tearDown(self): + super().tearDown() + self.mock_client.EMAILS_SENT.clear() + @boto3_mocking.patching def test_approval_creates_role(self): draft_domain, _ = DraftDomain.objects.get_or_create(name="igorville.gov") user, _ = User.objects.get_or_create() application = DomainApplication.objects.create(creator=user, requested_domain=draft_domain) - mock_client = MagicMock() - with boto3_mocking.clients.handler_for("sesv2", mock_client): + + with boto3_mocking.clients.handler_for("sesv2", self.mock_client): with less_console_noise(): # skip using the submit method application.status = DomainApplication.ApplicationStatus.SUBMITTED @@ -510,14 +553,22 @@ class TestDomainInfo(TestCase): """Test creation of Domain Information when approved.""" + def setUp(self): + super().setUp() + self.mock_client = MockSESClient() + + def tearDown(self): + super().tearDown() + self.mock_client.EMAILS_SENT.clear() + @boto3_mocking.patching def test_approval_creates_info(self): draft_domain, _ = DraftDomain.objects.get_or_create(name="igorville.gov") user, _ = User.objects.get_or_create() application = DomainApplication.objects.create(creator=user, requested_domain=draft_domain) - mock_client = MagicMock() - with boto3_mocking.clients.handler_for("sesv2", mock_client): + + with boto3_mocking.clients.handler_for("sesv2", self.mock_client): with less_console_noise(): # skip using the submit method application.status = DomainApplication.ApplicationStatus.SUBMITTED diff --git a/src/registrar/tests/test_models_domain.py b/src/registrar/tests/test_models_domain.py index 07d45bbdc..b76e2000c 100644 --- a/src/registrar/tests/test_models_domain.py +++ b/src/registrar/tests/test_models_domain.py @@ -29,7 +29,7 @@ from epplibwrapper import ( RegistryError, ErrorCode, ) -from .common import MockEppLib, less_console_noise +from .common import MockEppLib, MockSESClient, less_console_noise import logging import boto3_mocking # type: ignore @@ -263,8 +263,7 @@ class TestDomainCreation(MockEppLib): user, _ = User.objects.get_or_create() application = DomainApplication.objects.create(creator=user, requested_domain=draft_domain) - mock_client = MagicMock() - with boto3_mocking.clients.handler_for("sesv2", mock_client): + with boto3_mocking.clients.handler_for("sesv2", MockSESClient): with less_console_noise(): # skip using the submit method application.status = DomainApplication.ApplicationStatus.SUBMITTED From 4d3db8782ce2c82521cab9cbeaae32ee6bb3753a Mon Sep 17 00:00:00 2001 From: zandercymatics <141044360+zandercymatics@users.noreply.github.com> Date: Fri, 22 Dec 2023 14:43:22 -0700 Subject: [PATCH 13/34] Update test_models.py --- src/registrar/tests/test_models.py | 2 -- 1 file changed, 2 deletions(-) diff --git a/src/registrar/tests/test_models.py b/src/registrar/tests/test_models.py index 4d75ce6a3..6e23a220e 100644 --- a/src/registrar/tests/test_models.py +++ b/src/registrar/tests/test_models.py @@ -20,8 +20,6 @@ from .common import MockSESClient, less_console_noise, completed_application from django_fsm import TransitionNotAllowed -boto3_mocking.clients.register_handler("sesv2", MockSESClient) - # The DomainApplication submit method has a side effect of sending an email # with AWS SES, so mock that out in all of these test cases @boto3_mocking.patching From a6e8141c1b5b722c53c26c67f3d301ebc9c3f5e0 Mon Sep 17 00:00:00 2001 From: zandercymatics <141044360+zandercymatics@users.noreply.github.com> Date: Wed, 27 Dec 2023 12:16:09 -0700 Subject: [PATCH 14/34] Add mock to master script --- .../test_transition_domain_migrations.py | 30 +++++++++++-------- 1 file changed, 17 insertions(+), 13 deletions(-) diff --git a/src/registrar/tests/test_transition_domain_migrations.py b/src/registrar/tests/test_transition_domain_migrations.py index 1fe513999..fab669d53 100644 --- a/src/registrar/tests/test_transition_domain_migrations.py +++ b/src/registrar/tests/test_transition_domain_migrations.py @@ -707,17 +707,19 @@ class TestMigrations(TestCase): def run_master_script(self): # noqa here (E501) because splitting this up makes it # confusing to read. - with patch( - "registrar.management.commands.utility.terminal_helper.TerminalHelper.query_yes_no_exit", # noqa - return_value=True, - ): - call_command( - "master_domain_migrations", - runMigrations=True, - migrationDirectory=self.test_data_file_location, - migrationJSON=self.migration_json_filename, - disablePrompts=True, - ) + mock_client = MockSESClient() + with boto3_mocking.clients.handler_for("sesv2", mock_client): + with patch( + "registrar.management.commands.utility.terminal_helper.TerminalHelper.query_yes_no_exit", # noqa + return_value=True, + ): + call_command( + "master_domain_migrations", + runMigrations=True, + migrationDirectory=self.test_data_file_location, + migrationJSON=self.migration_json_filename, + disablePrompts=True, + ) def compare_tables( self, @@ -1030,7 +1032,8 @@ class TestMigrations(TestCase): # this is one of the email addresses in data/test_contacts.txt output_stream = StringIO() - with boto3_mocking.clients.handler_for("sesv2", MockSESClient): + mock_client = MockSESClient() + with boto3_mocking.clients.handler_for("sesv2", mock_client): # also have to re-point the logging handlers to output_stream with less_console_noise(output_stream): call_command("send_domain_invitations", "testuser@gmail.com", stdout=output_stream) @@ -1051,7 +1054,8 @@ class TestMigrations(TestCase): # these are two email addresses in data/test_contacts.txt output_stream = StringIO() - with boto3_mocking.clients.handler_for("sesv2", MockSESClient): + mock_client = MockSESClient() + with boto3_mocking.clients.handler_for("sesv2", mock_client): # also have to re-point the logging handlers to output_stream with less_console_noise(output_stream): call_command( From f1a5b4abea01ba4e41a99f591fea3d5a1218b1c3 Mon Sep 17 00:00:00 2001 From: zandercymatics <141044360+zandercymatics@users.noreply.github.com> Date: Wed, 27 Dec 2023 12:29:26 -0700 Subject: [PATCH 15/34] Linting --- src/registrar/tests/test_models.py | 34 ++++++------------------------ 1 file changed, 7 insertions(+), 27 deletions(-) diff --git a/src/registrar/tests/test_models.py b/src/registrar/tests/test_models.py index 6e23a220e..069359625 100644 --- a/src/registrar/tests/test_models.py +++ b/src/registrar/tests/test_models.py @@ -1,6 +1,6 @@ from django.test import TestCase from django.db.utils import IntegrityError -from unittest.mock import MagicMock, patch +from unittest.mock import patch from registrar.models import ( Contact, @@ -51,7 +51,7 @@ class TestDomainApplication(TestCase): ) self.mock_client = MockSESClient() - + def tearDown(self): super().tearDown() self.mock_client.EMAILS_SENT.clear() @@ -134,7 +134,6 @@ class TestDomainApplication(TestCase): user, _ = User.objects.get_or_create(username="testy") application = DomainApplication.objects.create(creator=user) - with boto3_mocking.clients.handler_for("sesv2", self.mock_client): with less_console_noise(): with self.assertRaises(ValueError): @@ -147,7 +146,7 @@ class TestDomainApplication(TestCase): application = DomainApplication.objects.create(creator=user, requested_domain=site) # no submitter email so this emits a log warning - + with boto3_mocking.clients.handler_for("sesv2", self.mock_client): with less_console_noise(): application.submit() @@ -165,7 +164,6 @@ class TestDomainApplication(TestCase): ) application.save() - with boto3_mocking.clients.handler_for("sesv2", self.mock_client): with less_console_noise(): application.submit() @@ -192,8 +190,7 @@ class TestDomainApplication(TestCase): (self.action_needed_application, TransitionNotAllowed), (self.withdrawn_application, TransitionNotAllowed), ] - - + with boto3_mocking.clients.handler_for("sesv2", self.mock_client): with less_console_noise(): for application, exception_type in test_cases: @@ -214,7 +211,6 @@ class TestDomainApplication(TestCase): (self.ineligible_application, TransitionNotAllowed), ] - with boto3_mocking.clients.handler_for("sesv2", self.mock_client): with less_console_noise(): for application, exception_type in test_cases: @@ -234,7 +230,6 @@ class TestDomainApplication(TestCase): (self.ineligible_application, TransitionNotAllowed), ] - with boto3_mocking.clients.handler_for("sesv2", self.mock_client): with less_console_noise(): for application, exception_type in test_cases: @@ -254,7 +249,6 @@ class TestDomainApplication(TestCase): (self.withdrawn_application, TransitionNotAllowed), ] - with boto3_mocking.clients.handler_for("sesv2", self.mock_client): with less_console_noise(): for application, exception_type in test_cases: @@ -273,7 +267,6 @@ class TestDomainApplication(TestCase): (self.ineligible_application, TransitionNotAllowed), ] - with boto3_mocking.clients.handler_for("sesv2", self.mock_client): with less_console_noise(): for application, exception_type in test_cases: @@ -294,7 +287,6 @@ class TestDomainApplication(TestCase): (self.withdrawn_application, TransitionNotAllowed), ] - with boto3_mocking.clients.handler_for("sesv2", self.mock_client): with less_console_noise(): for application, exception_type in test_cases: @@ -313,7 +305,6 @@ class TestDomainApplication(TestCase): (self.rejected_application, TransitionNotAllowed), ] - with boto3_mocking.clients.handler_for("sesv2", self.mock_client): with less_console_noise(): for application, exception_type in test_cases: @@ -322,7 +313,7 @@ class TestDomainApplication(TestCase): application.approve() except TransitionNotAllowed: self.fail("TransitionNotAllowed was raised, but it was not expected.") - + def test_approved_skips_sending_email(self): """ Test that calling .approve with send_email=False doesn't actually send @@ -347,7 +338,6 @@ class TestDomainApplication(TestCase): (self.ineligible_application, TransitionNotAllowed), ] - with boto3_mocking.clients.handler_for("sesv2", self.mock_client): with less_console_noise(): for application, exception_type in test_cases: @@ -365,7 +355,6 @@ class TestDomainApplication(TestCase): (self.action_needed_application, TransitionNotAllowed), ] - with boto3_mocking.clients.handler_for("sesv2", self.mock_client): with less_console_noise(): for application, exception_type in test_cases: @@ -387,7 +376,6 @@ class TestDomainApplication(TestCase): (self.ineligible_application, TransitionNotAllowed), ] - with boto3_mocking.clients.handler_for("sesv2", self.mock_client): with less_console_noise(): for application, exception_type in test_cases: @@ -405,7 +393,6 @@ class TestDomainApplication(TestCase): (self.approved_application, TransitionNotAllowed), ] - with boto3_mocking.clients.handler_for("sesv2", self.mock_client): with less_console_noise(): for application, exception_type in test_cases: @@ -427,7 +414,6 @@ class TestDomainApplication(TestCase): (self.ineligible_application, TransitionNotAllowed), ] - with boto3_mocking.clients.handler_for("sesv2", self.mock_client): with less_console_noise(): for application, exception_type in test_cases: @@ -446,7 +432,6 @@ class TestDomainApplication(TestCase): (self.rejected_application, TransitionNotAllowed), ] - with boto3_mocking.clients.handler_for("sesv2", self.mock_client): with less_console_noise(): for application, exception_type in test_cases: @@ -467,7 +452,6 @@ class TestDomainApplication(TestCase): (self.ineligible_application, TransitionNotAllowed), ] - with boto3_mocking.clients.handler_for("sesv2", self.mock_client): with less_console_noise(): for application, exception_type in test_cases: @@ -487,7 +471,6 @@ class TestDomainApplication(TestCase): def custom_is_active(self): return True # Override to return True - with boto3_mocking.clients.handler_for("sesv2", self.mock_client): with less_console_noise(): # Use patch to temporarily replace is_active with the custom implementation @@ -508,7 +491,6 @@ class TestDomainApplication(TestCase): def custom_is_active(self): return True # Override to return True - with boto3_mocking.clients.handler_for("sesv2", self.mock_client): with less_console_noise(): # Use patch to temporarily replace is_active with the custom implementation @@ -524,7 +506,7 @@ class TestPermissions(TestCase): def setUp(self): super().setUp() self.mock_client = MockSESClient() - + def tearDown(self): super().tearDown() self.mock_client.EMAILS_SENT.clear() @@ -535,7 +517,6 @@ class TestPermissions(TestCase): user, _ = User.objects.get_or_create() application = DomainApplication.objects.create(creator=user, requested_domain=draft_domain) - with boto3_mocking.clients.handler_for("sesv2", self.mock_client): with less_console_noise(): # skip using the submit method @@ -554,7 +535,7 @@ class TestDomainInfo(TestCase): def setUp(self): super().setUp() self.mock_client = MockSESClient() - + def tearDown(self): super().tearDown() self.mock_client.EMAILS_SENT.clear() @@ -565,7 +546,6 @@ class TestDomainInfo(TestCase): user, _ = User.objects.get_or_create() application = DomainApplication.objects.create(creator=user, requested_domain=draft_domain) - with boto3_mocking.clients.handler_for("sesv2", self.mock_client): with less_console_noise(): # skip using the submit method From 3e9b8e9c82e839ca34cd2d7f07c50523ef2cea19 Mon Sep 17 00:00:00 2001 From: zandercymatics <141044360+zandercymatics@users.noreply.github.com> Date: Thu, 28 Dec 2023 10:46:53 -0700 Subject: [PATCH 16/34] Add additional patch for testing purposes --- .../test_transition_domain_migrations.py | 19 ++++++++++++------- 1 file changed, 12 insertions(+), 7 deletions(-) diff --git a/src/registrar/tests/test_transition_domain_migrations.py b/src/registrar/tests/test_transition_domain_migrations.py index fab669d53..0d2b35ccb 100644 --- a/src/registrar/tests/test_transition_domain_migrations.py +++ b/src/registrar/tests/test_transition_domain_migrations.py @@ -713,13 +713,18 @@ class TestMigrations(TestCase): "registrar.management.commands.utility.terminal_helper.TerminalHelper.query_yes_no_exit", # noqa return_value=True, ): - call_command( - "master_domain_migrations", - runMigrations=True, - migrationDirectory=self.test_data_file_location, - migrationJSON=self.migration_json_filename, - disablePrompts=True, - ) + with patch( + "registrar.utility.email.send_templated_email", + return_value=None + ): + call_command( + "master_domain_migrations", + runMigrations=True, + migrationDirectory=self.test_data_file_location, + migrationJSON=self.migration_json_filename, + disablePrompts=True, + ) + print(f"here: {mock_client.EMAILS_SENT}") def compare_tables( self, From 4bf0a4d6a9e7f45a904e0d473a1acefd51cfceae Mon Sep 17 00:00:00 2001 From: zandercymatics <141044360+zandercymatics@users.noreply.github.com> Date: Thu, 28 Dec 2023 15:04:12 -0700 Subject: [PATCH 17/34] Script for patching agency info --- .../commands/patch_federal_agency_info.py | 148 ++++++++++++++++++ .../utility/extra_transition_domain_helper.py | 66 ++++++++ 2 files changed, 214 insertions(+) create mode 100644 src/registrar/management/commands/patch_federal_agency_info.py diff --git a/src/registrar/management/commands/patch_federal_agency_info.py b/src/registrar/management/commands/patch_federal_agency_info.py new file mode 100644 index 000000000..87fa0972a --- /dev/null +++ b/src/registrar/management/commands/patch_federal_agency_info.py @@ -0,0 +1,148 @@ +"""""" +import argparse +import json +import logging + +import os +from typing import List + +from django.core.management import BaseCommand +from registrar.management.commands.utility.epp_data_containers import AgencyAdhoc, AuthorityAdhoc, DomainAdditionalData, EnumFilenames +from registrar.management.commands.utility.extra_transition_domain_helper import ExtraTransitionDomain, MigrationDataFileLoader +from registrar.management.commands.utility.terminal_helper import TerminalColors, TerminalHelper +from registrar.management.commands.utility.transition_domain_arguments import TransitionDomainArguments +from registrar.models.domain_information import DomainInformation +from django.db.models import Q + +from registrar.models.transition_domain import TransitionDomain + +logger = logging.getLogger(__name__) + + +class Command(BaseCommand): + help = "Runs the cat command on files from /tmp into the getgov directory." + + def __init__(self): + super().__init__() + self.di_to_update: List[DomainInformation] = [] + + # Stores the domain_name for logging purposes + self.di_failed_to_update: List[str] = [] + self.di_skipped: List[str] = [] + + def add_arguments(self, parser): + """Adds command line arguments""" + parser.add_argument("--debug", action=argparse.BooleanOptionalAction) + + def handle(self, **options): + debug = options.get("debug") + domain_info_to_fix = DomainInformation.objects.filter(Q(federal_agency=None) | Q(federal_agency="")) + + domain_names = domain_info_to_fix.values_list('domain__name', flat=True) + transition_domains = TransitionDomain.objects.filter(domain_name__in=domain_names) + + # Get the domain names from TransitionDomain + td_agencies = transition_domains.values_list("domain_name", "federal_agency").distinct() + + TerminalHelper.prompt_for_execution( + system_exit_on_terminate=True, + info_to_inspect=f""" + ==Proposed Changes== + Number of DomainInformation objects to change: {len(td_agencies)} + The following DomainInformation objects will be modified: {td_agencies} + """, + prompt_title="Do you wish to update organization address data for DomainInformation as well?", + ) + logger.info("Updating...") + + # Create a dictionary mapping of domain_name to federal_agency + td_dict = dict(td_agencies) + + for di in domain_info_to_fix: + domain_name = di.domain.name + if domain_name in td_dict and td_dict.get(domain_name) is not None: + # Grab existing federal_agency data + di.federal_agency = td_dict.get(domain_name) + # Append it to our update list + self.di_to_update.append(di) + if debug: + logger.info( + f"{TerminalColors.OKCYAN}Updated {di}{TerminalColors.ENDC}" + ) + else: + self.di_skipped.append(di) + if debug: + logger.info( + f"{TerminalColors.YELLOW}Skipping update for {di}{TerminalColors.ENDC}" + ) + + DomainInformation.objects.bulk_update(self.di_to_update, ["federal_agency"]) + + # After the update has happened, do a sweep of what we get back. + # If the fields we expect to update are still None, then something is wrong. + for di in domain_info_to_fix: + if domain_name in td_dict and td_dict.get(domain_name) is not None: + logger.info( + f"{TerminalColors.FAIL}Failed to update {di}{TerminalColors.ENDC}" + ) + self.di_failed_to_update.append(di) + + # === Log results and return data === # + self.log_script_run_summary(debug) + + def log_script_run_summary(self, debug): + """Prints success, failed, and skipped counts, as well as + all affected objects.""" + update_success_count = len(self.di_to_update) + update_failed_count = len(self.di_failed_to_update) + update_skipped_count = len(self.di_skipped) + + # Prepare debug messages + debug_messages = { + "success": (f"{TerminalColors.OKCYAN}Updated: {self.di_to_update}{TerminalColors.ENDC}\n"), + "skipped": (f"{TerminalColors.YELLOW}Skipped: {self.di_skipped}{TerminalColors.ENDC}\n"), + "failed": ( + f"{TerminalColors.FAIL}Failed: {self.di_failed_to_update}{TerminalColors.ENDC}\n" + ), + } + + # Print out a list of everything that was changed, if we have any changes to log. + # Otherwise, don't print anything. + TerminalHelper.print_conditional( + debug, + f"{debug_messages.get('success') if update_success_count > 0 else ''}" + f"{debug_messages.get('skipped') if update_skipped_count > 0 else ''}" + f"{debug_messages.get('failed') if update_failed_count > 0 else ''}", + ) + + if update_failed_count == 0 and update_skipped_count == 0: + logger.info( + f"""{TerminalColors.OKGREEN} + ============= FINISHED =============== + Updated {update_success_count} DomainInformation entries + {TerminalColors.ENDC} + """ + ) + elif update_failed_count == 0: + logger.info( + f"""{TerminalColors.YELLOW} + ============= FINISHED =============== + Updated {update_success_count} DomainInformation entries + + ----- SOME AGENCY DATA WAS NONE ----- + Skipped updating {update_skipped_count} DomainInformation entries + {TerminalColors.ENDC} + """ + ) + else: + logger.info( + f"""{TerminalColors.FAIL} + ============= FINISHED =============== + Updated {update_success_count} DomainInformation entries + + ----- UPDATE FAILED ----- + Failed to update {update_failed_count} DomainInformation entries, + Skipped updating {update_skipped_count} DomainInformation entries + {TerminalColors.ENDC} + """ + ) \ No newline at end of file diff --git a/src/registrar/management/commands/utility/extra_transition_domain_helper.py b/src/registrar/management/commands/utility/extra_transition_domain_helper.py index 54f68d5c8..7c59e62a8 100644 --- a/src/registrar/management/commands/utility/extra_transition_domain_helper.py +++ b/src/registrar/management/commands/utility/extra_transition_domain_helper.py @@ -944,6 +944,72 @@ class OrganizationDataLoader: else: logger.warning(f"Updated existing {field_name} to '{changed_value}' on {domain_name}") +class MigrationDataFileLoader: + def __init__(self, files_to_load: List[EnumFilenames], options: TransitionDomainArguments): + all_valid_files = self.get_file_map(options) + + # Get the list of files we want to load, and coerce them + # into the right format. + desired_files = [] + for file in all_valid_files: + if file[0] in files_to_load: + desired_files.append(file) + else: + raise Exception("Invalid file type specified") + + # Specify which files we want to load + options.pattern_map_params = desired_files + + self.file_data_helper = ExtraTransitionDomain(options) + + # Load data from all specified files + self.file_data_helper.parse_all_files(infer_filenames=False) + + # Store the file data in a variable + self.file_data = self.file_data_helper.file_data + + def get_file_map(self, options: TransitionDomainArguments): + """Returns metadata about how we should parse desired files""" + file_map = [ + ( + EnumFilenames.AGENCY_ADHOC, + options.agency_adhoc_filename, + AgencyAdhoc, + "agencyid", + ), + ( + EnumFilenames.DOMAIN_ADDITIONAL, + options.domain_additional_filename, + DomainAdditionalData, + "domainname", + ), + ( + EnumFilenames.DOMAIN_ESCROW, + options.domain_escrow_filename, + DomainEscrow, + "domainname", + ), + ( + EnumFilenames.DOMAIN_ADHOC, + options.domain_adhoc_filename, + DomainTypeAdhoc, + "domaintypeid", + ), + ( + EnumFilenames.ORGANIZATION_ADHOC, + options.organization_adhoc_filename, + OrganizationAdhoc, + "orgid", + ), + ( + EnumFilenames.AUTHORITY_ADHOC, + options.authority_adhoc_filename, + AuthorityAdhoc, + "authorityid", + ), + ] + return file_map + class ExtraTransitionDomain: """Helper class to aid in storing TransitionDomain data spread across From 9b790406c2552e6b8dcfc3a125cc3726ed0c2978 Mon Sep 17 00:00:00 2001 From: zandercymatics <141044360+zandercymatics@users.noreply.github.com> Date: Thu, 28 Dec 2023 15:05:18 -0700 Subject: [PATCH 18/34] Remove unused code --- .../utility/extra_transition_domain_helper.py | 66 ------------------- 1 file changed, 66 deletions(-) diff --git a/src/registrar/management/commands/utility/extra_transition_domain_helper.py b/src/registrar/management/commands/utility/extra_transition_domain_helper.py index 7c59e62a8..54f68d5c8 100644 --- a/src/registrar/management/commands/utility/extra_transition_domain_helper.py +++ b/src/registrar/management/commands/utility/extra_transition_domain_helper.py @@ -944,72 +944,6 @@ class OrganizationDataLoader: else: logger.warning(f"Updated existing {field_name} to '{changed_value}' on {domain_name}") -class MigrationDataFileLoader: - def __init__(self, files_to_load: List[EnumFilenames], options: TransitionDomainArguments): - all_valid_files = self.get_file_map(options) - - # Get the list of files we want to load, and coerce them - # into the right format. - desired_files = [] - for file in all_valid_files: - if file[0] in files_to_load: - desired_files.append(file) - else: - raise Exception("Invalid file type specified") - - # Specify which files we want to load - options.pattern_map_params = desired_files - - self.file_data_helper = ExtraTransitionDomain(options) - - # Load data from all specified files - self.file_data_helper.parse_all_files(infer_filenames=False) - - # Store the file data in a variable - self.file_data = self.file_data_helper.file_data - - def get_file_map(self, options: TransitionDomainArguments): - """Returns metadata about how we should parse desired files""" - file_map = [ - ( - EnumFilenames.AGENCY_ADHOC, - options.agency_adhoc_filename, - AgencyAdhoc, - "agencyid", - ), - ( - EnumFilenames.DOMAIN_ADDITIONAL, - options.domain_additional_filename, - DomainAdditionalData, - "domainname", - ), - ( - EnumFilenames.DOMAIN_ESCROW, - options.domain_escrow_filename, - DomainEscrow, - "domainname", - ), - ( - EnumFilenames.DOMAIN_ADHOC, - options.domain_adhoc_filename, - DomainTypeAdhoc, - "domaintypeid", - ), - ( - EnumFilenames.ORGANIZATION_ADHOC, - options.organization_adhoc_filename, - OrganizationAdhoc, - "orgid", - ), - ( - EnumFilenames.AUTHORITY_ADHOC, - options.authority_adhoc_filename, - AuthorityAdhoc, - "authorityid", - ), - ] - return file_map - class ExtraTransitionDomain: """Helper class to aid in storing TransitionDomain data spread across From 79112f9e18b48ea15ec3b1d84439c0cfc593bde9 Mon Sep 17 00:00:00 2001 From: zandercymatics <141044360+zandercymatics@users.noreply.github.com> Date: Thu, 28 Dec 2023 15:06:17 -0700 Subject: [PATCH 19/34] Revert "Script for patching agency info" This reverts commit 4bf0a4d6a9e7f45a904e0d473a1acefd51cfceae. --- .../commands/patch_federal_agency_info.py | 148 ------------------ 1 file changed, 148 deletions(-) delete mode 100644 src/registrar/management/commands/patch_federal_agency_info.py diff --git a/src/registrar/management/commands/patch_federal_agency_info.py b/src/registrar/management/commands/patch_federal_agency_info.py deleted file mode 100644 index 87fa0972a..000000000 --- a/src/registrar/management/commands/patch_federal_agency_info.py +++ /dev/null @@ -1,148 +0,0 @@ -"""""" -import argparse -import json -import logging - -import os -from typing import List - -from django.core.management import BaseCommand -from registrar.management.commands.utility.epp_data_containers import AgencyAdhoc, AuthorityAdhoc, DomainAdditionalData, EnumFilenames -from registrar.management.commands.utility.extra_transition_domain_helper import ExtraTransitionDomain, MigrationDataFileLoader -from registrar.management.commands.utility.terminal_helper import TerminalColors, TerminalHelper -from registrar.management.commands.utility.transition_domain_arguments import TransitionDomainArguments -from registrar.models.domain_information import DomainInformation -from django.db.models import Q - -from registrar.models.transition_domain import TransitionDomain - -logger = logging.getLogger(__name__) - - -class Command(BaseCommand): - help = "Runs the cat command on files from /tmp into the getgov directory." - - def __init__(self): - super().__init__() - self.di_to_update: List[DomainInformation] = [] - - # Stores the domain_name for logging purposes - self.di_failed_to_update: List[str] = [] - self.di_skipped: List[str] = [] - - def add_arguments(self, parser): - """Adds command line arguments""" - parser.add_argument("--debug", action=argparse.BooleanOptionalAction) - - def handle(self, **options): - debug = options.get("debug") - domain_info_to_fix = DomainInformation.objects.filter(Q(federal_agency=None) | Q(federal_agency="")) - - domain_names = domain_info_to_fix.values_list('domain__name', flat=True) - transition_domains = TransitionDomain.objects.filter(domain_name__in=domain_names) - - # Get the domain names from TransitionDomain - td_agencies = transition_domains.values_list("domain_name", "federal_agency").distinct() - - TerminalHelper.prompt_for_execution( - system_exit_on_terminate=True, - info_to_inspect=f""" - ==Proposed Changes== - Number of DomainInformation objects to change: {len(td_agencies)} - The following DomainInformation objects will be modified: {td_agencies} - """, - prompt_title="Do you wish to update organization address data for DomainInformation as well?", - ) - logger.info("Updating...") - - # Create a dictionary mapping of domain_name to federal_agency - td_dict = dict(td_agencies) - - for di in domain_info_to_fix: - domain_name = di.domain.name - if domain_name in td_dict and td_dict.get(domain_name) is not None: - # Grab existing federal_agency data - di.federal_agency = td_dict.get(domain_name) - # Append it to our update list - self.di_to_update.append(di) - if debug: - logger.info( - f"{TerminalColors.OKCYAN}Updated {di}{TerminalColors.ENDC}" - ) - else: - self.di_skipped.append(di) - if debug: - logger.info( - f"{TerminalColors.YELLOW}Skipping update for {di}{TerminalColors.ENDC}" - ) - - DomainInformation.objects.bulk_update(self.di_to_update, ["federal_agency"]) - - # After the update has happened, do a sweep of what we get back. - # If the fields we expect to update are still None, then something is wrong. - for di in domain_info_to_fix: - if domain_name in td_dict and td_dict.get(domain_name) is not None: - logger.info( - f"{TerminalColors.FAIL}Failed to update {di}{TerminalColors.ENDC}" - ) - self.di_failed_to_update.append(di) - - # === Log results and return data === # - self.log_script_run_summary(debug) - - def log_script_run_summary(self, debug): - """Prints success, failed, and skipped counts, as well as - all affected objects.""" - update_success_count = len(self.di_to_update) - update_failed_count = len(self.di_failed_to_update) - update_skipped_count = len(self.di_skipped) - - # Prepare debug messages - debug_messages = { - "success": (f"{TerminalColors.OKCYAN}Updated: {self.di_to_update}{TerminalColors.ENDC}\n"), - "skipped": (f"{TerminalColors.YELLOW}Skipped: {self.di_skipped}{TerminalColors.ENDC}\n"), - "failed": ( - f"{TerminalColors.FAIL}Failed: {self.di_failed_to_update}{TerminalColors.ENDC}\n" - ), - } - - # Print out a list of everything that was changed, if we have any changes to log. - # Otherwise, don't print anything. - TerminalHelper.print_conditional( - debug, - f"{debug_messages.get('success') if update_success_count > 0 else ''}" - f"{debug_messages.get('skipped') if update_skipped_count > 0 else ''}" - f"{debug_messages.get('failed') if update_failed_count > 0 else ''}", - ) - - if update_failed_count == 0 and update_skipped_count == 0: - logger.info( - f"""{TerminalColors.OKGREEN} - ============= FINISHED =============== - Updated {update_success_count} DomainInformation entries - {TerminalColors.ENDC} - """ - ) - elif update_failed_count == 0: - logger.info( - f"""{TerminalColors.YELLOW} - ============= FINISHED =============== - Updated {update_success_count} DomainInformation entries - - ----- SOME AGENCY DATA WAS NONE ----- - Skipped updating {update_skipped_count} DomainInformation entries - {TerminalColors.ENDC} - """ - ) - else: - logger.info( - f"""{TerminalColors.FAIL} - ============= FINISHED =============== - Updated {update_success_count} DomainInformation entries - - ----- UPDATE FAILED ----- - Failed to update {update_failed_count} DomainInformation entries, - Skipped updating {update_skipped_count} DomainInformation entries - {TerminalColors.ENDC} - """ - ) \ No newline at end of file From 5570831aa64f4e65ac5b4743caa6317086b0c331 Mon Sep 17 00:00:00 2001 From: zandercymatics <141044360+zandercymatics@users.noreply.github.com> Date: Fri, 29 Dec 2023 15:21:44 -0700 Subject: [PATCH 20/34] Linting --- src/registrar/tests/test_transition_domain_migrations.py | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/src/registrar/tests/test_transition_domain_migrations.py b/src/registrar/tests/test_transition_domain_migrations.py index 0d2b35ccb..4774e085f 100644 --- a/src/registrar/tests/test_transition_domain_migrations.py +++ b/src/registrar/tests/test_transition_domain_migrations.py @@ -713,10 +713,7 @@ class TestMigrations(TestCase): "registrar.management.commands.utility.terminal_helper.TerminalHelper.query_yes_no_exit", # noqa return_value=True, ): - with patch( - "registrar.utility.email.send_templated_email", - return_value=None - ): + with patch("registrar.utility.email.send_templated_email", return_value=None): call_command( "master_domain_migrations", runMigrations=True, From 9064c760691784a7e1791466965f26b23dc88cf7 Mon Sep 17 00:00:00 2001 From: Neil Martinsen-Burrell Date: Wed, 3 Jan 2024 08:44:52 -0600 Subject: [PATCH 21/34] Redirect to get.gov after logout --- src/registrar/config/settings.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/registrar/config/settings.py b/src/registrar/config/settings.py index bc46c60ba..2de7e6eb2 100644 --- a/src/registrar/config/settings.py +++ b/src/registrar/config/settings.py @@ -519,7 +519,7 @@ LOGIN_REQUIRED_IGNORE_PATHS = [ ] # where to go after logging out -LOGOUT_REDIRECT_URL = "home" +LOGOUT_REDIRECT_URL = "https://get.gov/" # disable dynamic client registration, # only the OP inside OIDC_PROVIDERS will be available From f33a9ecd6c249e397b2391a070ac593bb455a1f8 Mon Sep 17 00:00:00 2001 From: Neil Martinsen-Burrell Date: Wed, 3 Jan 2024 09:04:38 -0600 Subject: [PATCH 22/34] fix another zap false positive --- src/zap.conf | 1 + 1 file changed, 1 insertion(+) diff --git a/src/zap.conf b/src/zap.conf index e7dc980b0..7a1e5c96d 100644 --- a/src/zap.conf +++ b/src/zap.conf @@ -67,6 +67,7 @@ 10038 OUTOFSCOPE http://app:8080/dns/nameservers 10038 OUTOFSCOPE http://app:8080/dns/dnssec 10038 OUTOFSCOPE http://app:8080/dns/dnssec/dsdata +10038 OUTOFSCOPE http://app:8080/org-name-address # This URL always returns 404, so include it as well. 10038 OUTOFSCOPE http://app:8080/todo # OIDC isn't configured in the test environment and DEBUG=True so this gives a 500 without CSP headers From 5a58db1375d52e0a41ade6efe4e363a8441a6a55 Mon Sep 17 00:00:00 2001 From: zandercymatics <141044360+zandercymatics@users.noreply.github.com> Date: Wed, 3 Jan 2024 11:55:55 -0700 Subject: [PATCH 23/34] Add additional mockses clients --- src/registrar/tests/test_admin.py | 15 ++++++++------- 1 file changed, 8 insertions(+), 7 deletions(-) diff --git a/src/registrar/tests/test_admin.py b/src/registrar/tests/test_admin.py index 9c9da29cc..3b3d38a55 100644 --- a/src/registrar/tests/test_admin.py +++ b/src/registrar/tests/test_admin.py @@ -18,6 +18,7 @@ from registrar.admin import ( from registrar.models import Domain, DomainApplication, DomainInformation, User, DomainInvitation, Contact, Website from registrar.models.user_domain_role import UserDomainRole from .common import ( + MockSESClient, completed_application, generic_domain_object, less_console_noise, @@ -340,7 +341,7 @@ class TestDomainApplicationAdmin(MockEppLib): EMAIL = "mayor@igorville.gov" User.objects.filter(email=EMAIL).delete() - mock_client = MagicMock() + mock_client = MockSESClient() mock_client_instance = mock_client.return_value with boto3_mocking.clients.handler_for("sesv2", mock_client): @@ -382,7 +383,7 @@ class TestDomainApplicationAdmin(MockEppLib): EMAIL = "mayor@igorville.gov" User.objects.filter(email=EMAIL).delete() - mock_client = MagicMock() + mock_client = MockSESClient() mock_client_instance = mock_client.return_value with boto3_mocking.clients.handler_for("sesv2", mock_client): @@ -424,7 +425,7 @@ class TestDomainApplicationAdmin(MockEppLib): EMAIL = "mayor@igorville.gov" User.objects.filter(email=EMAIL).delete() - mock_client = MagicMock() + mock_client = MockSESClient() mock_client_instance = mock_client.return_value with boto3_mocking.clients.handler_for("sesv2", mock_client): @@ -472,7 +473,7 @@ class TestDomainApplicationAdmin(MockEppLib): # Create a mock request request = self.factory.post("/admin/registrar/domainapplication/{}/change/".format(application.pk)) - mock_client = MagicMock() + mock_client = MockSESClient() with boto3_mocking.clients.handler_for("sesv2", mock_client): with less_console_noise(): # Modify the application's property @@ -490,7 +491,7 @@ class TestDomainApplicationAdmin(MockEppLib): EMAIL = "mayor@igorville.gov" User.objects.filter(email=EMAIL).delete() - mock_client = MagicMock() + mock_client = MockSESClient() mock_client_instance = mock_client.return_value with boto3_mocking.clients.handler_for("sesv2", mock_client): @@ -532,7 +533,7 @@ class TestDomainApplicationAdmin(MockEppLib): EMAIL = "mayor@igorville.gov" User.objects.filter(email=EMAIL).delete() - mock_client = MagicMock() + mock_client = MockSESClient() mock_client_instance = mock_client.return_value with boto3_mocking.clients.handler_for("sesv2", mock_client): @@ -580,7 +581,7 @@ class TestDomainApplicationAdmin(MockEppLib): # Create a mock request request = self.factory.post("/admin/registrar/domainapplication/{}/change/".format(application.pk)) - mock_client = MagicMock() + mock_client = MockSESClient() with boto3_mocking.clients.handler_for("sesv2", mock_client): with less_console_noise(): # Modify the application's property From 3a929ab33c73e8abf1c06291754239772dd3b134 Mon Sep 17 00:00:00 2001 From: zandercymatics <141044360+zandercymatics@users.noreply.github.com> Date: Wed, 3 Jan 2024 12:34:25 -0700 Subject: [PATCH 24/34] Fix emails sending --- src/registrar/tests/test_admin.py | 80 ++++++++++++------------------- 1 file changed, 30 insertions(+), 50 deletions(-) diff --git a/src/registrar/tests/test_admin.py b/src/registrar/tests/test_admin.py index 09758ae5b..17aa88252 100644 --- a/src/registrar/tests/test_admin.py +++ b/src/registrar/tests/test_admin.py @@ -328,6 +328,7 @@ class TestDomainApplicationAdmin(MockEppLib): url="/admin/registrar/DomainApplication/", model=DomainApplication, ) + self.mock_client = MockSESClient() def test_domain_sortable(self): """Tests if the DomainApplication sorts by domain correctly""" @@ -422,10 +423,7 @@ class TestDomainApplicationAdmin(MockEppLib): EMAIL = "mayor@igorville.gov" User.objects.filter(email=EMAIL).delete() - mock_client = MockSESClient() - mock_client_instance = mock_client.return_value - - with boto3_mocking.clients.handler_for("sesv2", mock_client): + with boto3_mocking.clients.handler_for("sesv2", self.mock_client): with less_console_noise(): # Create a sample application application = completed_application() @@ -440,8 +438,8 @@ class TestDomainApplicationAdmin(MockEppLib): self.admin.save_model(request, application, form=None, change=True) # Access the arguments passed to send_email - call_args = mock_client_instance.send_email.call_args - args, kwargs = call_args + call_args = self.mock_client.EMAILS_SENT + kwargs = call_args[0]["kwargs"] # Retrieve the email details from the arguments from_email = kwargs.get("FromEmailAddress") @@ -455,8 +453,7 @@ class TestDomainApplicationAdmin(MockEppLib): self.assertEqual(to_email, EMAIL) self.assertIn(expected_string, email_body) - # Perform assertions on the mock call itself - mock_client_instance.send_email.assert_called_once() + self.assertEqual(len(self.mock_client.EMAILS_SENT), 1) @boto3_mocking.patching def test_save_model_sends_in_review_email(self): @@ -464,10 +461,7 @@ class TestDomainApplicationAdmin(MockEppLib): EMAIL = "mayor@igorville.gov" User.objects.filter(email=EMAIL).delete() - mock_client = MockSESClient() - mock_client_instance = mock_client.return_value - - with boto3_mocking.clients.handler_for("sesv2", mock_client): + with boto3_mocking.clients.handler_for("sesv2", self.mock_client): with less_console_noise(): # Create a sample application application = completed_application(status=DomainApplication.ApplicationStatus.SUBMITTED) @@ -482,8 +476,8 @@ class TestDomainApplicationAdmin(MockEppLib): self.admin.save_model(request, application, form=None, change=True) # Access the arguments passed to send_email - call_args = mock_client_instance.send_email.call_args - args, kwargs = call_args + call_args = self.mock_client.EMAILS_SENT + kwargs = call_args[0]["kwargs"] # Retrieve the email details from the arguments from_email = kwargs.get("FromEmailAddress") @@ -497,8 +491,7 @@ class TestDomainApplicationAdmin(MockEppLib): self.assertEqual(to_email, EMAIL) self.assertIn(expected_string, email_body) - # Perform assertions on the mock call itself - mock_client_instance.send_email.assert_called_once() + self.assertEqual(len(self.mock_client.EMAILS_SENT), 1) @boto3_mocking.patching def test_save_model_sends_approved_email(self): @@ -506,10 +499,7 @@ class TestDomainApplicationAdmin(MockEppLib): EMAIL = "mayor@igorville.gov" User.objects.filter(email=EMAIL).delete() - mock_client = MockSESClient() - mock_client_instance = mock_client.return_value - - with boto3_mocking.clients.handler_for("sesv2", mock_client): + with boto3_mocking.clients.handler_for("sesv2", self.mock_client): with less_console_noise(): # Create a sample application application = completed_application(status=DomainApplication.ApplicationStatus.IN_REVIEW) @@ -524,8 +514,8 @@ class TestDomainApplicationAdmin(MockEppLib): self.admin.save_model(request, application, form=None, change=True) # Access the arguments passed to send_email - call_args = mock_client_instance.send_email.call_args - args, kwargs = call_args + call_args = self.mock_client.EMAILS_SENT + kwargs = call_args[0]["kwargs"] # Retrieve the email details from the arguments from_email = kwargs.get("FromEmailAddress") @@ -539,8 +529,7 @@ class TestDomainApplicationAdmin(MockEppLib): self.assertEqual(to_email, EMAIL) self.assertIn(expected_string, email_body) - # Perform assertions on the mock call itself - mock_client_instance.send_email.assert_called_once() + self.assertEqual(len(self.mock_client.EMAILS_SENT), 1) @boto3_mocking.patching def test_save_model_sets_approved_domain(self): @@ -554,8 +543,7 @@ class TestDomainApplicationAdmin(MockEppLib): # Create a mock request request = self.factory.post("/admin/registrar/domainapplication/{}/change/".format(application.pk)) - mock_client = MockSESClient() - with boto3_mocking.clients.handler_for("sesv2", mock_client): + with boto3_mocking.clients.handler_for("sesv2", self.mock_client): with less_console_noise(): # Modify the application's property application.status = DomainApplication.ApplicationStatus.APPROVED @@ -572,10 +560,7 @@ class TestDomainApplicationAdmin(MockEppLib): EMAIL = "mayor@igorville.gov" User.objects.filter(email=EMAIL).delete() - mock_client = MockSESClient() - mock_client_instance = mock_client.return_value - - with boto3_mocking.clients.handler_for("sesv2", mock_client): + with boto3_mocking.clients.handler_for("sesv2", self.mock_client): with less_console_noise(): # Create a sample application application = completed_application(status=DomainApplication.ApplicationStatus.IN_REVIEW) @@ -590,8 +575,8 @@ class TestDomainApplicationAdmin(MockEppLib): self.admin.save_model(request, application, form=None, change=True) # Access the arguments passed to send_email - call_args = mock_client_instance.send_email.call_args - args, kwargs = call_args + call_args = self.mock_client.EMAILS_SENT + kwargs = call_args[0]["kwargs"] # Retrieve the email details from the arguments from_email = kwargs.get("FromEmailAddress") @@ -605,8 +590,7 @@ class TestDomainApplicationAdmin(MockEppLib): self.assertEqual(to_email, EMAIL) self.assertIn(expected_string, email_body) - # Perform assertions on the mock call itself - mock_client_instance.send_email.assert_called_once() + self.assertEqual(len(self.mock_client.EMAILS_SENT), 1) @boto3_mocking.patching def test_save_model_sends_rejected_email(self): @@ -614,10 +598,7 @@ class TestDomainApplicationAdmin(MockEppLib): EMAIL = "mayor@igorville.gov" User.objects.filter(email=EMAIL).delete() - mock_client = MockSESClient() - mock_client_instance = mock_client.return_value - - with boto3_mocking.clients.handler_for("sesv2", mock_client): + with boto3_mocking.clients.handler_for("sesv2", self.mock_client): with less_console_noise(): # Create a sample application application = completed_application(status=DomainApplication.ApplicationStatus.IN_REVIEW) @@ -632,8 +613,8 @@ class TestDomainApplicationAdmin(MockEppLib): self.admin.save_model(request, application, form=None, change=True) # Access the arguments passed to send_email - call_args = mock_client_instance.send_email.call_args - args, kwargs = call_args + call_args = self.mock_client.EMAILS_SENT + kwargs = call_args[0]["kwargs"] # Retrieve the email details from the arguments from_email = kwargs.get("FromEmailAddress") @@ -647,8 +628,7 @@ class TestDomainApplicationAdmin(MockEppLib): self.assertEqual(to_email, EMAIL) self.assertIn(expected_string, email_body) - # Perform assertions on the mock call itself - mock_client_instance.send_email.assert_called_once() + self.assertEqual(len(self.mock_client.EMAILS_SENT), 1) @boto3_mocking.patching def test_save_model_sets_restricted_status_on_user(self): @@ -662,8 +642,7 @@ class TestDomainApplicationAdmin(MockEppLib): # Create a mock request request = self.factory.post("/admin/registrar/domainapplication/{}/change/".format(application.pk)) - mock_client = MockSESClient() - with boto3_mocking.clients.handler_for("sesv2", mock_client): + with boto3_mocking.clients.handler_for("sesv2", self.mock_client): with less_console_noise(): # Modify the application's property application.status = DomainApplication.ApplicationStatus.INELIGIBLE @@ -816,8 +795,7 @@ class TestDomainApplicationAdmin(MockEppLib): stack.enter_context(patch.object(Domain, "is_active", custom_is_active)) stack.enter_context(patch.object(messages, "error")) - mock_client = MagicMock() - with boto3_mocking.clients.handler_for("sesv2", mock_client): + with boto3_mocking.clients.handler_for("sesv2", self.mock_client): with less_console_noise(): # Simulate saving the model application.status = DomainApplication.ApplicationStatus.REJECTED @@ -850,10 +828,11 @@ class TestDomainApplicationAdmin(MockEppLib): # Patch Domain.is_active and django.contrib.messages.error simultaneously stack.enter_context(patch.object(Domain, "is_active", custom_is_active)) stack.enter_context(patch.object(messages, "error")) - - # Simulate saving the model - application.status = DomainApplication.ApplicationStatus.REJECTED - self.admin.save_model(request, application, None, True) + with boto3_mocking.clients.handler_for("sesv2", self.mock_client): + with less_console_noise(): + # Simulate saving the model + application.status = DomainApplication.ApplicationStatus.REJECTED + self.admin.save_model(request, application, None, True) # Assert that the error message was never called messages.error.assert_not_called() @@ -1110,6 +1089,7 @@ class TestDomainApplicationAdmin(MockEppLib): User.objects.all().delete() Contact.objects.all().delete() Website.objects.all().delete() + self.mock_client.EMAILS_SENT.clear() class DomainInvitationAdminTest(TestCase): From df06cb4b15049ddb857ffd56ec4bd0e779a53e53 Mon Sep 17 00:00:00 2001 From: zandercymatics <141044360+zandercymatics@users.noreply.github.com> Date: Wed, 3 Jan 2024 12:40:51 -0700 Subject: [PATCH 25/34] Linting change --- src/registrar/tests/test_admin.py | 1 - 1 file changed, 1 deletion(-) diff --git a/src/registrar/tests/test_admin.py b/src/registrar/tests/test_admin.py index 17aa88252..923b7b06a 100644 --- a/src/registrar/tests/test_admin.py +++ b/src/registrar/tests/test_admin.py @@ -37,7 +37,6 @@ from unittest.mock import patch from unittest import skip from django.conf import settings -from unittest.mock import MagicMock import boto3_mocking # type: ignore import logging From 089ef40b97237c16c3e3c96b86245342d1abb50d Mon Sep 17 00:00:00 2001 From: zandercymatics <141044360+zandercymatics@users.noreply.github.com> Date: Wed, 3 Jan 2024 12:59:57 -0700 Subject: [PATCH 26/34] Add more mocking --- src/registrar/tests/test_views.py | 84 ++++++++++++++++++------------- 1 file changed, 49 insertions(+), 35 deletions(-) diff --git a/src/registrar/tests/test_views.py b/src/registrar/tests/test_views.py index abd58b190..01a243b38 100644 --- a/src/registrar/tests/test_views.py +++ b/src/registrar/tests/test_views.py @@ -5,7 +5,7 @@ from django.conf import settings from django.test import Client, TestCase from django.urls import reverse from django.contrib.auth import get_user_model -from .common import MockEppLib, completed_application, create_user # type: ignore +from .common import MockEppLib, MockSESClient, completed_application, create_user # type: ignore from django_webtest import WebTest # type: ignore import boto3_mocking # type: ignore @@ -1451,7 +1451,7 @@ class TestDomainManagers(TestDomainOverview): self.app.set_cookie(settings.SESSION_COOKIE_NAME, session_id) - mock_client = MagicMock() + mock_client = MockSESClient() with boto3_mocking.clients.handler_for("sesv2", mock_client): with less_console_noise(): success_result = add_page.form.submit() @@ -1484,7 +1484,7 @@ class TestDomainManagers(TestDomainOverview): add_page.form["email"] = email_address self.app.set_cookie(settings.SESSION_COOKIE_NAME, session_id) - mock_client = MagicMock() + mock_client = MockSESClient() with boto3_mocking.clients.handler_for("sesv2", mock_client): with less_console_noise(): success_result = add_page.form.submit() @@ -1515,7 +1515,7 @@ class TestDomainManagers(TestDomainOverview): add_page.form["email"] = caps_email_address self.app.set_cookie(settings.SESSION_COOKIE_NAME, session_id) - mock_client = MagicMock() + mock_client = MockSESClient() with boto3_mocking.clients.handler_for("sesv2", mock_client): with less_console_noise(): success_result = add_page.form.submit() @@ -1545,6 +1545,7 @@ class TestDomainManagers(TestDomainOverview): add_page.form["email"] = email_address self.app.set_cookie(settings.SESSION_COOKIE_NAME, session_id) add_page.form.submit() + # check the mock instance to see if `send_email` was called right mock_client_instance.send_email.assert_called_once_with( FromEmailAddress=settings.DEFAULT_FROM_EMAIL, @@ -1565,11 +1566,12 @@ class TestDomainManagers(TestDomainOverview): mock_client_instance = mock_client.return_value with boto3_mocking.clients.handler_for("sesv2", mock_client): - add_page = self.app.get(reverse("domain-users-add", kwargs={"pk": self.domain.id})) - session_id = self.app.cookies[settings.SESSION_COOKIE_NAME] - add_page.form["email"] = email_address - self.app.set_cookie(settings.SESSION_COOKIE_NAME, session_id) - add_page.form.submit() + with less_console_noise(): + add_page = self.app.get(reverse("domain-users-add", kwargs={"pk": self.domain.id})) + session_id = self.app.cookies[settings.SESSION_COOKIE_NAME] + add_page.form["email"] = email_address + self.app.set_cookie(settings.SESSION_COOKIE_NAME, session_id) + add_page.form.submit() # check the mock instance to see if `send_email` was called right mock_client_instance.send_email.assert_called_once_with( @@ -1603,11 +1605,12 @@ class TestDomainManagers(TestDomainOverview): mock_client_instance = mock_client.return_value with boto3_mocking.clients.handler_for("sesv2", mock_client): - add_page = self.app.get(reverse("domain-users-add", kwargs={"pk": self.domain.id})) - session_id = self.app.cookies[settings.SESSION_COOKIE_NAME] - add_page.form["email"] = email_address - self.app.set_cookie(settings.SESSION_COOKIE_NAME, session_id) - add_page.form.submit() + with less_console_noise(): + add_page = self.app.get(reverse("domain-users-add", kwargs={"pk": self.domain.id})) + session_id = self.app.cookies[settings.SESSION_COOKIE_NAME] + add_page.form["email"] = email_address + self.app.set_cookie(settings.SESSION_COOKIE_NAME, session_id) + add_page.form.submit() # check the mock instance to see if `send_email` was called right mock_client_instance.send_email.assert_called_once_with( @@ -1645,11 +1648,12 @@ class TestDomainManagers(TestDomainOverview): mock_client_instance = mock_client.return_value with boto3_mocking.clients.handler_for("sesv2", mock_client): - add_page = self.app.get(reverse("domain-users-add", kwargs={"pk": self.domain.id})) - session_id = self.app.cookies[settings.SESSION_COOKIE_NAME] - add_page.form["email"] = email_address - self.app.set_cookie(settings.SESSION_COOKIE_NAME, session_id) - add_page.form.submit() + with less_console_noise(): + add_page = self.app.get(reverse("domain-users-add", kwargs={"pk": self.domain.id})) + session_id = self.app.cookies[settings.SESSION_COOKIE_NAME] + add_page.form["email"] = email_address + self.app.set_cookie(settings.SESSION_COOKIE_NAME, session_id) + add_page.form.submit() # check the mock instance to see if `send_email` was called right mock_client_instance.send_email.assert_called_once_with( @@ -1684,15 +1688,15 @@ class TestDomainManagers(TestDomainOverview): self.domain_information, _ = DomainInformation.objects.get_or_create(creator=self.user, domain=self.domain) mock_client = MagicMock() - mock_error_message = MagicMock() with boto3_mocking.clients.handler_for("sesv2", mock_client): with patch("django.contrib.messages.error") as mock_error_message: - add_page = self.app.get(reverse("domain-users-add", kwargs={"pk": self.domain.id})) - session_id = self.app.cookies[settings.SESSION_COOKIE_NAME] - add_page.form["email"] = email_address - self.app.set_cookie(settings.SESSION_COOKIE_NAME, session_id) - add_page.form.submit().follow() + with less_console_noise(): + add_page = self.app.get(reverse("domain-users-add", kwargs={"pk": self.domain.id})) + session_id = self.app.cookies[settings.SESSION_COOKIE_NAME] + add_page.form["email"] = email_address + self.app.set_cookie(settings.SESSION_COOKIE_NAME, session_id) + add_page.form.submit().follow() expected_message_content = "Can't send invitation email. No email is associated with your account." @@ -1721,11 +1725,12 @@ class TestDomainManagers(TestDomainOverview): mock_error_message = MagicMock() with boto3_mocking.clients.handler_for("sesv2", mock_client): with patch("django.contrib.messages.error") as mock_error_message: - add_page = self.app.get(reverse("domain-users-add", kwargs={"pk": self.domain.id})) - session_id = self.app.cookies[settings.SESSION_COOKIE_NAME] - add_page.form["email"] = email_address - self.app.set_cookie(settings.SESSION_COOKIE_NAME, session_id) - add_page.form.submit().follow() + with less_console_noise(): + add_page = self.app.get(reverse("domain-users-add", kwargs={"pk": self.domain.id})) + session_id = self.app.cookies[settings.SESSION_COOKIE_NAME] + add_page.form["email"] = email_address + self.app.set_cookie(settings.SESSION_COOKIE_NAME, session_id) + add_page.form.submit().follow() expected_message_content = "Can't send invitation email. No email is associated with your account." @@ -1739,7 +1744,11 @@ class TestDomainManagers(TestDomainOverview): """Posting to the delete view deletes an invitation.""" email_address = "mayor@igorville.gov" invitation, _ = DomainInvitation.objects.get_or_create(domain=self.domain, email=email_address) - self.client.post(reverse("invitation-delete", kwargs={"pk": invitation.id})) + mock_client = MockSESClient() + with boto3_mocking.clients.handler_for("sesv2", mock_client): + with less_console_noise(): + self.client.post(reverse("invitation-delete", kwargs={"pk": invitation.id})) + mock_client.EMAILS_SENT.clear() with self.assertRaises(DomainInvitation.DoesNotExist): DomainInvitation.objects.get(id=invitation.id) @@ -1751,8 +1760,11 @@ class TestDomainManagers(TestDomainOverview): other_user = User() other_user.save() self.client.force_login(other_user) - with less_console_noise(): # permission denied makes console errors - result = self.client.post(reverse("invitation-delete", kwargs={"pk": invitation.id})) + mock_client = MagicMock() + with boto3_mocking.clients.handler_for("sesv2", mock_client): + with less_console_noise(): # permission denied makes console errors + result = self.client.post(reverse("invitation-delete", kwargs={"pk": invitation.id})) + self.assertEqual(result.status_code, 403) @boto3_mocking.patching @@ -2163,8 +2175,10 @@ class TestDomainSecurityEmail(TestDomainOverview): session_id = self.app.cookies[settings.SESSION_COOKIE_NAME] security_email_page.form["security_email"] = "mayor@igorville.gov" self.app.set_cookie(settings.SESSION_COOKIE_NAME, session_id) - with less_console_noise(): # swallow log warning message - result = security_email_page.form.submit() + mock_client = MagicMock() + with boto3_mocking.clients.handler_for("sesv2", mock_client): + with less_console_noise(): # swallow log warning message + result = security_email_page.form.submit() self.assertEqual(result.status_code, 302) self.assertEqual( result["Location"], From 401ad77385a41e7a329d89d30419f87f67ea2b7b Mon Sep 17 00:00:00 2001 From: zandercymatics <141044360+zandercymatics@users.noreply.github.com> Date: Wed, 3 Jan 2024 13:04:44 -0700 Subject: [PATCH 27/34] Update test_admin.py --- src/registrar/tests/test_admin.py | 18 ++++++++++++------ 1 file changed, 12 insertions(+), 6 deletions(-) diff --git a/src/registrar/tests/test_admin.py b/src/registrar/tests/test_admin.py index 923b7b06a..153c87b42 100644 --- a/src/registrar/tests/test_admin.py +++ b/src/registrar/tests/test_admin.py @@ -654,8 +654,10 @@ class TestDomainApplicationAdmin(MockEppLib): def test_readonly_when_restricted_creator(self): application = completed_application(status=DomainApplication.ApplicationStatus.IN_REVIEW) - application.creator.status = User.RESTRICTED - application.creator.save() + with boto3_mocking.clients.handler_for("sesv2", self.mock_client): + with less_console_noise(): + application.creator.status = User.RESTRICTED + application.creator.save() request = self.factory.get("/") request.user = self.superuser @@ -733,8 +735,10 @@ class TestDomainApplicationAdmin(MockEppLib): def test_saving_when_restricted_creator(self): # Create an instance of the model application = completed_application(status=DomainApplication.ApplicationStatus.IN_REVIEW) - application.creator.status = User.RESTRICTED - application.creator.save() + with boto3_mocking.clients.handler_for("sesv2", self.mock_client): + with less_console_noise(): + application.creator.status = User.RESTRICTED + application.creator.save() # Create a request object with a superuser request = self.factory.get("/") @@ -756,8 +760,10 @@ class TestDomainApplicationAdmin(MockEppLib): def test_change_view_with_restricted_creator(self): # Create an instance of the model application = completed_application(status=DomainApplication.ApplicationStatus.IN_REVIEW) - application.creator.status = User.RESTRICTED - application.creator.save() + with boto3_mocking.clients.handler_for("sesv2", self.mock_client): + with less_console_noise(): + application.creator.status = User.RESTRICTED + application.creator.save() with patch("django.contrib.messages.warning") as mock_warning: # Create a request object with a superuser From 3ffad88b5da6babf5aa0747601ee8510cc4f7f54 Mon Sep 17 00:00:00 2001 From: zandercymatics <141044360+zandercymatics@users.noreply.github.com> Date: Wed, 3 Jan 2024 13:10:51 -0700 Subject: [PATCH 28/34] Catch strangler --- src/registrar/tests/test_admin.py | 4 +++- src/registrar/tests/test_models_domain.py | 3 ++- 2 files changed, 5 insertions(+), 2 deletions(-) diff --git a/src/registrar/tests/test_admin.py b/src/registrar/tests/test_admin.py index 153c87b42..a35dbc7e3 100644 --- a/src/registrar/tests/test_admin.py +++ b/src/registrar/tests/test_admin.py @@ -59,7 +59,9 @@ class TestDomainAdmin(MockEppLib): """ self.client.force_login(self.superuser) application = completed_application(status=DomainApplication.ApplicationStatus.IN_REVIEW) - application.approve() + with boto3_mocking.clients.handler_for("sesv2", self.mock_client): + with less_console_noise(): + application.approve() response = self.client.get("/admin/registrar/domain/") diff --git a/src/registrar/tests/test_models_domain.py b/src/registrar/tests/test_models_domain.py index 9db7257b3..b86941183 100644 --- a/src/registrar/tests/test_models_domain.py +++ b/src/registrar/tests/test_models_domain.py @@ -265,7 +265,8 @@ class TestDomainCreation(MockEppLib): user, _ = User.objects.get_or_create() application = DomainApplication.objects.create(creator=user, requested_domain=draft_domain) - with boto3_mocking.clients.handler_for("sesv2", MockSESClient): + mock_client = MockSESClient() + with boto3_mocking.clients.handler_for("sesv2", mock_client): with less_console_noise(): # skip using the submit method application.status = DomainApplication.ApplicationStatus.SUBMITTED From 8febb199ea99ac4e0fa288f9d6e8ae0f6725ab09 Mon Sep 17 00:00:00 2001 From: zandercymatics <141044360+zandercymatics@users.noreply.github.com> Date: Wed, 3 Jan 2024 13:11:12 -0700 Subject: [PATCH 29/34] Update test_admin.py --- src/registrar/tests/test_admin.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/registrar/tests/test_admin.py b/src/registrar/tests/test_admin.py index a35dbc7e3..8dded9de9 100644 --- a/src/registrar/tests/test_admin.py +++ b/src/registrar/tests/test_admin.py @@ -59,7 +59,8 @@ class TestDomainAdmin(MockEppLib): """ self.client.force_login(self.superuser) application = completed_application(status=DomainApplication.ApplicationStatus.IN_REVIEW) - with boto3_mocking.clients.handler_for("sesv2", self.mock_client): + mock_client = MockSESClient() + with boto3_mocking.clients.handler_for("sesv2", mock_client): with less_console_noise(): application.approve() From 311296e24a4fdbd3164dba703ff1f3f7fe632738 Mon Sep 17 00:00:00 2001 From: zandercymatics <141044360+zandercymatics@users.noreply.github.com> Date: Wed, 3 Jan 2024 13:12:14 -0700 Subject: [PATCH 30/34] Linting --- src/registrar/tests/test_views.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/registrar/tests/test_views.py b/src/registrar/tests/test_views.py index 01a243b38..7a69fe99a 100644 --- a/src/registrar/tests/test_views.py +++ b/src/registrar/tests/test_views.py @@ -1764,7 +1764,7 @@ class TestDomainManagers(TestDomainOverview): with boto3_mocking.clients.handler_for("sesv2", mock_client): with less_console_noise(): # permission denied makes console errors result = self.client.post(reverse("invitation-delete", kwargs={"pk": invitation.id})) - + self.assertEqual(result.status_code, 403) @boto3_mocking.patching From 9ab1be9ee14d22a0006e2d36c0811d1130bb4e86 Mon Sep 17 00:00:00 2001 From: zandercymatics <141044360+zandercymatics@users.noreply.github.com> Date: Wed, 3 Jan 2024 13:18:54 -0700 Subject: [PATCH 31/34] More stragglers --- src/registrar/tests/test_views.py | 7 +++++-- src/registrar/views/application.py | 11 ++++++++--- 2 files changed, 13 insertions(+), 5 deletions(-) diff --git a/src/registrar/tests/test_views.py b/src/registrar/tests/test_views.py index 7a69fe99a..a21fffd5a 100644 --- a/src/registrar/tests/test_views.py +++ b/src/registrar/tests/test_views.py @@ -149,8 +149,11 @@ class DomainApplicationTests(TestWithUser, WebTest): """Test that an info message appears when user has multiple applications already""" # create and submit an application application = completed_application(user=self.user) - application.submit() - application.save() + mock_client = MockSESClient() + with boto3_mocking.clients.handler_for("sesv2", mock_client): + with less_console_noise(): + application.submit() + application.save() # now, attempt to create another one with less_console_noise(): diff --git a/src/registrar/views/application.py b/src/registrar/views/application.py index 41052e164..1d74284c8 100644 --- a/src/registrar/views/application.py +++ b/src/registrar/views/application.py @@ -1,5 +1,5 @@ import logging - +import boto3_mocking # type: ignore from django.http import Http404, HttpResponse, HttpResponseRedirect from django.shortcuts import redirect, render from django.urls import resolve, reverse @@ -7,9 +7,11 @@ from django.utils.safestring import mark_safe from django.utils.translation import gettext_lazy as _ from django.views.generic import TemplateView from django.contrib import messages +from api.tests.common import less_console_noise from registrar.forms import application_wizard as forms from registrar.models import DomainApplication +from registrar.tests.common import MockSESClient from registrar.utility import StrEnum from registrar.views.utility import StepsHelper @@ -569,6 +571,9 @@ class ApplicationWithdrawn(DomainApplicationPermissionWithdrawView): to withdraw and send back to homepage. """ application = DomainApplication.objects.get(id=self.kwargs["pk"]) - application.withdraw() - application.save() + mock_client = MockSESClient() + with boto3_mocking.clients.handler_for("sesv2", mock_client): + with less_console_noise(): + application.withdraw() + application.save() return HttpResponseRedirect(reverse("home")) From ef25506b4a95bd4a44671a80fe7a4275a6aded47 Mon Sep 17 00:00:00 2001 From: zandercymatics <141044360+zandercymatics@users.noreply.github.com> Date: Wed, 3 Jan 2024 13:23:45 -0700 Subject: [PATCH 32/34] Update application.py --- src/registrar/views/application.py | 10 ++-------- 1 file changed, 2 insertions(+), 8 deletions(-) diff --git a/src/registrar/views/application.py b/src/registrar/views/application.py index 1d74284c8..a5da1def0 100644 --- a/src/registrar/views/application.py +++ b/src/registrar/views/application.py @@ -1,5 +1,4 @@ import logging -import boto3_mocking # type: ignore from django.http import Http404, HttpResponse, HttpResponseRedirect from django.shortcuts import redirect, render from django.urls import resolve, reverse @@ -7,11 +6,9 @@ from django.utils.safestring import mark_safe from django.utils.translation import gettext_lazy as _ from django.views.generic import TemplateView from django.contrib import messages -from api.tests.common import less_console_noise from registrar.forms import application_wizard as forms from registrar.models import DomainApplication -from registrar.tests.common import MockSESClient from registrar.utility import StrEnum from registrar.views.utility import StepsHelper @@ -571,9 +568,6 @@ class ApplicationWithdrawn(DomainApplicationPermissionWithdrawView): to withdraw and send back to homepage. """ application = DomainApplication.objects.get(id=self.kwargs["pk"]) - mock_client = MockSESClient() - with boto3_mocking.clients.handler_for("sesv2", mock_client): - with less_console_noise(): - application.withdraw() - application.save() + application.withdraw() + application.save() return HttpResponseRedirect(reverse("home")) From c29b886dbd1ec1fcee5130ed0fba036e2a7cad6f Mon Sep 17 00:00:00 2001 From: zandercymatics <141044360+zandercymatics@users.noreply.github.com> Date: Wed, 3 Jan 2024 13:27:06 -0700 Subject: [PATCH 33/34] Add function comment --- src/registrar/models/domain_application.py | 3 +++ src/registrar/views/application.py | 1 + 2 files changed, 4 insertions(+) diff --git a/src/registrar/models/domain_application.py b/src/registrar/models/domain_application.py index a1ade7890..e181849ac 100644 --- a/src/registrar/models/domain_application.py +++ b/src/registrar/models/domain_application.py @@ -576,6 +576,9 @@ class DomainApplication(TimeStampedModel): The email goes to the email address that the submitter gave as their contact information. If there is not submitter information, then do nothing. + + send_email: bool -> Used to bypass the send_templated_email function, in the event + we just want to log that an email would have been sent, rather than actually sending one. """ if self.submitter is None or self.submitter.email is None: diff --git a/src/registrar/views/application.py b/src/registrar/views/application.py index a5da1def0..41052e164 100644 --- a/src/registrar/views/application.py +++ b/src/registrar/views/application.py @@ -1,4 +1,5 @@ import logging + from django.http import Http404, HttpResponse, HttpResponseRedirect from django.shortcuts import redirect, render from django.urls import resolve, reverse From 6eaed35b3ec75a6597e4c73d4d8c317fe84fb2d5 Mon Sep 17 00:00:00 2001 From: zandercymatics <141044360+zandercymatics@users.noreply.github.com> Date: Wed, 3 Jan 2024 13:30:20 -0700 Subject: [PATCH 34/34] Patch withdrawn email --- src/registrar/tests/test_views.py | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/src/registrar/tests/test_views.py b/src/registrar/tests/test_views.py index a21fffd5a..8f812b815 100644 --- a/src/registrar/tests/test_views.py +++ b/src/registrar/tests/test_views.py @@ -2527,9 +2527,12 @@ class TestApplicationStatus(TestWithUser, WebTest): self.assertContains(detail_page, "Admin Tester") self.assertContains(detail_page, "Status:") # click the "Withdraw request" button - withdraw_page = detail_page.click("Withdraw request") - self.assertContains(withdraw_page, "Withdraw request for") - home_page = withdraw_page.click("Withdraw request") + mock_client = MockSESClient() + with boto3_mocking.clients.handler_for("sesv2", mock_client): + with less_console_noise(): + withdraw_page = detail_page.click("Withdraw request") + self.assertContains(withdraw_page, "Withdraw request for") + home_page = withdraw_page.click("Withdraw request") # confirm that it has redirected, and the status has been updated to withdrawn self.assertRedirects( home_page,