From dd29081fffa65f1a151f6c011f8cd233fe3d019b Mon Sep 17 00:00:00 2001 From: Rachid Mrad Date: Tue, 1 Oct 2024 18:51:10 -0400 Subject: [PATCH 01/18] Fix bugs with the copy email button UI plus code cleanup --- src/registrar/admin.py | 2 +- src/registrar/assets/js/get-gov-admin.js | 21 ++-- src/registrar/assets/sass/_theme/_admin.scss | 95 +++++++------------ .../admin/change_form_object_tools.html | 3 +- .../templates/admin/input_with_clipboard.html | 30 +++--- .../admin/includes/contact_detail_list.html | 2 +- .../admin/includes/detail_table_fieldset.html | 3 +- 7 files changed, 63 insertions(+), 93 deletions(-) diff --git a/src/registrar/admin.py b/src/registrar/admin.py index 8718da9ba..a282e6c25 100644 --- a/src/registrar/admin.py +++ b/src/registrar/admin.py @@ -3112,7 +3112,7 @@ class PortfolioAdmin(ListHeaderAdmin): admin_details += f'{escape(portfolio_admin)}
' admin_details += f"{escape(portfolio_admin.title)}
" admin_details += f"{escape(portfolio_admin.email)}" - admin_details += " {% else %} -
+ -{% endif %} +{% endif %} \ No newline at end of file diff --git a/src/registrar/templates/django/admin/includes/contact_detail_list.html b/src/registrar/templates/django/admin/includes/contact_detail_list.html index 84fb07f33..0a28a6532 100644 --- a/src/registrar/templates/django/admin/includes/contact_detail_list.html +++ b/src/registrar/templates/django/admin/includes/contact_detail_list.html @@ -26,7 +26,7 @@ {% if user.email %} {{ user.email }} {% include "admin/input_with_clipboard.html" with field=user invisible_input_field=True %} -
+
{% else %} None
{% endif %} diff --git a/src/registrar/templates/django/admin/includes/detail_table_fieldset.html b/src/registrar/templates/django/admin/includes/detail_table_fieldset.html index 2369f235b..6b755724e 100644 --- a/src/registrar/templates/django/admin/includes/detail_table_fieldset.html +++ b/src/registrar/templates/django/admin/includes/detail_table_fieldset.html @@ -254,7 +254,7 @@ This is using a custom implementation fieldset.html (see admin/fieldset.html) - + @@ -267,31 +267,18 @@ This is using a custom implementation fieldset.html (see admin/fieldset.html) {% endfor %} diff --git a/src/registrar/tests/test_admin.py b/src/registrar/tests/test_admin.py index 9c5e3b582..cdc3c97de 100644 --- a/src/registrar/tests/test_admin.py +++ b/src/registrar/tests/test_admin.py @@ -654,7 +654,7 @@ class TestDomainInformationAdmin(TestCase): self.test_helper.assert_response_contains_distinct_values(response, expected_other_employees_fields) # Test for the copy link - self.assertContains(response, "copy-to-clipboard", count=3) + self.assertContains(response, "button--clipboard", count=3) # cleanup this test domain_info.delete() diff --git a/src/registrar/tests/test_admin_domain.py b/src/registrar/tests/test_admin_domain.py index f02b59a91..a9b94781f 100644 --- a/src/registrar/tests/test_admin_domain.py +++ b/src/registrar/tests/test_admin_domain.py @@ -535,7 +535,7 @@ class TestDomainAdminWithClient(TestCase): self.assertContains(response, "Testy Tester") # Test for the copy link - self.assertContains(response, "copy-to-clipboard") + self.assertContains(response, "button--clipboard") # cleanup from this test domain.delete() diff --git a/src/registrar/tests/test_admin_request.py b/src/registrar/tests/test_admin_request.py index f19008ca1..a9b073472 100644 --- a/src/registrar/tests/test_admin_request.py +++ b/src/registrar/tests/test_admin_request.py @@ -1511,7 +1511,7 @@ class TestDomainRequestAdmin(MockEppLib): self.test_helper.assert_response_contains_distinct_values(response, expected_other_employees_fields) # Test for the copy link - self.assertContains(response, "copy-to-clipboard", count=4) + self.assertContains(response, "button--clipboard", count=4) # Test that Creator counts display properly self.assertNotContains(response, "Approved domains") @@ -1846,58 +1846,6 @@ class TestDomainRequestAdmin(MockEppLib): def test_side_effects_when_saving_approved_to_ineligible(self): self.trigger_saving_approved_to_another_state(False, DomainRequest.DomainRequestStatus.INELIGIBLE) - @less_console_noise - def test_error_when_saving_to_approved_and_domain_exists(self): - """Redundant admin check on model transition not allowed.""" - Domain.objects.create(name="wabbitseason.gov") - - new_request = completed_domain_request( - status=DomainRequest.DomainRequestStatus.SUBMITTED, name="wabbitseason.gov" - ) - - # Create a request object with a superuser - request = self.factory.post("/admin/registrar/domainrequest/{}/change/".format(new_request.pk)) - request.user = self.superuser - - request.session = {} - - # Use ExitStack to combine patch contexts - with ExitStack() as stack: - # Patch django.contrib.messages.error - stack.enter_context(patch.object(messages, "error")) - - new_request.status = DomainRequest.DomainRequestStatus.APPROVED - - self.admin.save_model(request, new_request, None, True) - - messages.error.assert_called_once_with( - request, - "Cannot approve. Requested domain is already in use.", - ) - - @less_console_noise - def test_no_error_when_saving_to_approved_and_domain_exists(self): - """The negative of the redundant admin check on model transition not allowed.""" - new_request = completed_domain_request(status=DomainRequest.DomainRequestStatus.SUBMITTED) - - # Create a request object with a superuser - request = self.factory.post("/admin/registrar/domainrequest/{}/change/".format(new_request.pk)) - request.user = self.superuser - - request.session = {} - - # Use ExitStack to combine patch contexts - with ExitStack() as stack: - # Patch Domain.is_active and django.contrib.messages.error simultaneously - stack.enter_context(patch.object(messages, "error")) - - new_request.status = DomainRequest.DomainRequestStatus.APPROVED - - self.admin.save_model(request, new_request, None, True) - - # Assert that the error message was never called - messages.error.assert_not_called() - def test_has_correct_filters(self): """ This test verifies that DomainRequestAdmin has the correct filters set up. diff --git a/src/registrar/tests/test_models.py b/src/registrar/tests/test_models.py index f1d25ece9..681d04c92 100644 --- a/src/registrar/tests/test_models.py +++ b/src/registrar/tests/test_models.py @@ -1,5 +1,7 @@ from django.forms import ValidationError from django.test import TestCase +from django.db.utils import IntegrityError +from django.db import transaction from unittest.mock import patch from django.test import RequestFactory @@ -18,18 +20,23 @@ from registrar.models import ( UserPortfolioPermission, AllowedEmail, ) + import boto3_mocking from registrar.models.portfolio import Portfolio from registrar.models.portfolio_invitation import PortfolioInvitation from registrar.models.transition_domain import TransitionDomain from registrar.models.utility.portfolio_helper import UserPortfolioPermissionChoices, UserPortfolioRoleChoices from registrar.models.verified_by_staff import VerifiedByStaff # type: ignore +from registrar.utility.constants import BranchChoices from .common import ( MockSESClient, + less_console_noise, completed_domain_request, + set_domain_request_investigators, create_test_user, ) +from django_fsm import TransitionNotAllowed from waffle.testutils import override_flag from api.tests.common import less_console_noise_decorator diff --git a/src/registrar/tests/test_models_requests.py b/src/registrar/tests/test_models_requests.py deleted file mode 100644 index 9e86f5f9c..000000000 --- a/src/registrar/tests/test_models_requests.py +++ /dev/null @@ -1,1029 +0,0 @@ -from django.test import TestCase -from django.db.utils import IntegrityError -from django.db import transaction -from unittest.mock import patch - - -from registrar.models import ( - Contact, - DomainRequest, - DomainInformation, - User, - Website, - Domain, - DraftDomain, - FederalAgency, - AllowedEmail, -) - -import boto3_mocking -from registrar.utility.constants import BranchChoices -from registrar.utility.errors import FSMDomainRequestError - -from .common import ( - MockSESClient, - less_console_noise, - completed_domain_request, - set_domain_request_investigators, -) -from django_fsm import TransitionNotAllowed - -from api.tests.common import less_console_noise_decorator - - -@boto3_mocking.patching -class TestDomainRequest(TestCase): - @less_console_noise_decorator - def setUp(self): - - self.dummy_user, _ = Contact.objects.get_or_create( - email="mayor@igorville.com", first_name="Hello", last_name="World" - ) - self.dummy_user_2, _ = User.objects.get_or_create( - username="intern@igorville.com", email="intern@igorville.com", first_name="Lava", last_name="World" - ) - self.started_domain_request = completed_domain_request( - status=DomainRequest.DomainRequestStatus.STARTED, - name="started.gov", - ) - self.submitted_domain_request = completed_domain_request( - status=DomainRequest.DomainRequestStatus.SUBMITTED, - name="submitted.gov", - ) - self.in_review_domain_request = completed_domain_request( - status=DomainRequest.DomainRequestStatus.IN_REVIEW, - name="in-review.gov", - ) - self.action_needed_domain_request = completed_domain_request( - status=DomainRequest.DomainRequestStatus.ACTION_NEEDED, - name="action-needed.gov", - ) - self.approved_domain_request = completed_domain_request( - status=DomainRequest.DomainRequestStatus.APPROVED, - name="approved.gov", - ) - self.withdrawn_domain_request = completed_domain_request( - status=DomainRequest.DomainRequestStatus.WITHDRAWN, - name="withdrawn.gov", - ) - self.rejected_domain_request = completed_domain_request( - status=DomainRequest.DomainRequestStatus.REJECTED, - name="rejected.gov", - ) - self.ineligible_domain_request = completed_domain_request( - status=DomainRequest.DomainRequestStatus.INELIGIBLE, - name="ineligible.gov", - ) - - # Store all domain request statuses in a variable for ease of use - self.all_domain_requests = [ - self.started_domain_request, - self.submitted_domain_request, - self.in_review_domain_request, - self.action_needed_domain_request, - self.approved_domain_request, - self.withdrawn_domain_request, - self.rejected_domain_request, - self.ineligible_domain_request, - ] - - self.mock_client = MockSESClient() - - def tearDown(self): - super().tearDown() - DomainInformation.objects.all().delete() - DomainRequest.objects.all().delete() - DraftDomain.objects.all().delete() - Domain.objects.all().delete() - User.objects.all().delete() - self.mock_client.EMAILS_SENT.clear() - - def assertNotRaises(self, exception_type): - """Helper method for testing allowed transitions.""" - with less_console_noise(): - return self.assertRaises(Exception, None, exception_type) - - @less_console_noise_decorator - def test_request_is_withdrawable(self): - """Tests the is_withdrawable function""" - domain_request_1 = completed_domain_request( - status=DomainRequest.DomainRequestStatus.SUBMITTED, - name="city2.gov", - ) - domain_request_2 = completed_domain_request( - status=DomainRequest.DomainRequestStatus.IN_REVIEW, - name="city3.gov", - ) - domain_request_3 = completed_domain_request( - status=DomainRequest.DomainRequestStatus.ACTION_NEEDED, - name="city4.gov", - ) - domain_request_4 = completed_domain_request( - status=DomainRequest.DomainRequestStatus.REJECTED, - name="city5.gov", - ) - self.assertTrue(domain_request_1.is_withdrawable()) - self.assertTrue(domain_request_2.is_withdrawable()) - self.assertTrue(domain_request_3.is_withdrawable()) - self.assertFalse(domain_request_4.is_withdrawable()) - - @less_console_noise_decorator - def test_request_is_awaiting_review(self): - """Tests the is_awaiting_review function""" - domain_request_1 = completed_domain_request( - status=DomainRequest.DomainRequestStatus.SUBMITTED, - name="city2.gov", - ) - domain_request_2 = completed_domain_request( - status=DomainRequest.DomainRequestStatus.IN_REVIEW, - name="city3.gov", - ) - domain_request_3 = completed_domain_request( - status=DomainRequest.DomainRequestStatus.ACTION_NEEDED, - name="city4.gov", - ) - domain_request_4 = completed_domain_request( - status=DomainRequest.DomainRequestStatus.REJECTED, - name="city5.gov", - ) - self.assertTrue(domain_request_1.is_awaiting_review()) - self.assertTrue(domain_request_2.is_awaiting_review()) - self.assertFalse(domain_request_3.is_awaiting_review()) - self.assertFalse(domain_request_4.is_awaiting_review()) - - @less_console_noise_decorator - def test_federal_agency_set_to_non_federal_on_approve(self): - """Ensures that when the federal_agency field is 'none' when .approve() is called, - the field is set to the 'Non-Federal Agency' record""" - domain_request = completed_domain_request( - status=DomainRequest.DomainRequestStatus.IN_REVIEW, - name="city2.gov", - federal_agency=None, - ) - - # Ensure that the federal agency is None - self.assertEqual(domain_request.federal_agency, None) - - # Approve the request - domain_request.approve() - self.assertEqual(domain_request.status, DomainRequest.DomainRequestStatus.APPROVED) - - # After approval, it should be "Non-Federal agency" - expected_federal_agency = FederalAgency.objects.filter(agency="Non-Federal Agency").first() - self.assertEqual(domain_request.federal_agency, expected_federal_agency) - - def test_empty_create_fails(self): - """Can't create a completely empty domain request.""" - with less_console_noise(): - with transaction.atomic(): - with self.assertRaisesRegex(IntegrityError, "creator"): - DomainRequest.objects.create() - - @less_console_noise_decorator - def test_minimal_create(self): - """Can create with just a creator.""" - user, _ = User.objects.get_or_create(username="testy") - domain_request = DomainRequest.objects.create(creator=user) - self.assertEqual(domain_request.status, DomainRequest.DomainRequestStatus.STARTED) - - @less_console_noise_decorator - def test_full_create(self): - """Can create with all fields.""" - user, _ = User.objects.get_or_create(username="testy") - contact = Contact.objects.create() - com_website, _ = Website.objects.get_or_create(website="igorville.com") - gov_website, _ = Website.objects.get_or_create(website="igorville.gov") - domain, _ = DraftDomain.objects.get_or_create(name="igorville.gov") - domain_request = DomainRequest.objects.create( - creator=user, - investigator=user, - generic_org_type=DomainRequest.OrganizationChoices.FEDERAL, - federal_type=BranchChoices.EXECUTIVE, - is_election_board=False, - organization_name="Test", - address_line1="100 Main St.", - address_line2="APT 1A", - state_territory="CA", - zipcode="12345-6789", - senior_official=contact, - requested_domain=domain, - purpose="Igorville rules!", - anything_else="All of Igorville loves the dotgov program.", - is_policy_acknowledged=True, - ) - domain_request.current_websites.add(com_website) - domain_request.alternative_domains.add(gov_website) - domain_request.other_contacts.add(contact) - domain_request.save() - - @less_console_noise_decorator - def test_domain_info(self): - """Can create domain info with all fields.""" - user, _ = User.objects.get_or_create(username="testy") - contact = Contact.objects.create() - domain, _ = Domain.objects.get_or_create(name="igorville.gov") - information = DomainInformation.objects.create( - creator=user, - generic_org_type=DomainInformation.OrganizationChoices.FEDERAL, - federal_type=BranchChoices.EXECUTIVE, - is_election_board=False, - organization_name="Test", - address_line1="100 Main St.", - address_line2="APT 1A", - state_territory="CA", - zipcode="12345-6789", - senior_official=contact, - purpose="Igorville rules!", - anything_else="All of Igorville loves the dotgov program.", - is_policy_acknowledged=True, - domain=domain, - ) - information.other_contacts.add(contact) - information.save() - self.assertEqual(information.domain.id, domain.id) - self.assertEqual(information.id, domain.domain_info.id) - - @less_console_noise_decorator - def test_status_fsm_submit_fail(self): - user, _ = User.objects.get_or_create(username="testy") - domain_request = DomainRequest.objects.create(creator=user) - - with boto3_mocking.clients.handler_for("sesv2", self.mock_client): - with less_console_noise(): - with self.assertRaises(ValueError): - # can't submit a domain request with a null domain name - domain_request.submit() - - @less_console_noise_decorator - def test_status_fsm_submit_succeed(self): - user, _ = User.objects.get_or_create(username="testy") - site = DraftDomain.objects.create(name="igorville.gov") - domain_request = DomainRequest.objects.create(creator=user, requested_domain=site) - - # no email sent to creator so this emits a log warning - - with boto3_mocking.clients.handler_for("sesv2", self.mock_client): - with less_console_noise(): - domain_request.submit() - self.assertEqual(domain_request.status, domain_request.DomainRequestStatus.SUBMITTED) - - @less_console_noise_decorator - def check_email_sent( - self, domain_request, msg, action, expected_count, expected_content=None, expected_email="mayor@igorville.com" - ): - """Check if an email was sent after performing an action.""" - email_allowed, _ = AllowedEmail.objects.get_or_create(email=expected_email) - with self.subTest(msg=msg, action=action): - with boto3_mocking.clients.handler_for("sesv2", self.mock_client): - # Perform the specified action - action_method = getattr(domain_request, action) - action_method() - - # Check if an email was sent - sent_emails = [ - email - for email in MockSESClient.EMAILS_SENT - if expected_email in email["kwargs"]["Destination"]["ToAddresses"] - ] - self.assertEqual(len(sent_emails), expected_count) - - if expected_content: - email_content = sent_emails[0]["kwargs"]["Content"]["Simple"]["Body"]["Text"]["Data"] - self.assertIn(expected_content, email_content) - - email_allowed.delete() - - @less_console_noise_decorator - def test_submit_from_started_sends_email_to_creator(self): - """tests that we send an email to the creator""" - msg = "Create a domain request and submit it and see if email was sent when the feature flag is on." - domain_request = completed_domain_request(user=self.dummy_user_2) - self.check_email_sent( - domain_request, msg, "submit", 1, expected_content="Lava", expected_email="intern@igorville.com" - ) - - @less_console_noise_decorator - def test_submit_from_withdrawn_sends_email(self): - msg = "Create a withdrawn domain request and submit it and see if email was sent." - user, _ = User.objects.get_or_create(username="testy") - domain_request = completed_domain_request(status=DomainRequest.DomainRequestStatus.WITHDRAWN, user=user) - self.check_email_sent(domain_request, msg, "submit", 1, expected_content="Hi", expected_email=user.email) - - @less_console_noise_decorator - def test_submit_from_action_needed_does_not_send_email(self): - msg = "Create a domain request with ACTION_NEEDED status and submit it, check if email was not sent." - domain_request = completed_domain_request(status=DomainRequest.DomainRequestStatus.ACTION_NEEDED) - self.check_email_sent(domain_request, msg, "submit", 0) - - @less_console_noise_decorator - def test_submit_from_in_review_does_not_send_email(self): - msg = "Create a withdrawn domain request and submit it and see if email was sent." - domain_request = completed_domain_request(status=DomainRequest.DomainRequestStatus.IN_REVIEW) - self.check_email_sent(domain_request, msg, "submit", 0) - - @less_console_noise_decorator - def test_approve_sends_email(self): - msg = "Create a domain request and approve it and see if email was sent." - user, _ = User.objects.get_or_create(username="testy") - domain_request = completed_domain_request(status=DomainRequest.DomainRequestStatus.IN_REVIEW, user=user) - self.check_email_sent(domain_request, msg, "approve", 1, expected_content="approved", expected_email=user.email) - - @less_console_noise_decorator - def test_withdraw_sends_email(self): - msg = "Create a domain request and withdraw it and see if email was sent." - user, _ = User.objects.get_or_create(username="testy") - domain_request = completed_domain_request(status=DomainRequest.DomainRequestStatus.IN_REVIEW, user=user) - self.check_email_sent( - domain_request, msg, "withdraw", 1, expected_content="withdrawn", expected_email=user.email - ) - - @less_console_noise_decorator - def test_reject_sends_email(self): - msg = "Create a domain request and reject it and see if email was sent." - user, _ = User.objects.get_or_create(username="testy") - domain_request = completed_domain_request(status=DomainRequest.DomainRequestStatus.APPROVED, user=user) - self.check_email_sent(domain_request, msg, "reject", 1, expected_content="Hi", expected_email=user.email) - - @less_console_noise_decorator - def test_reject_with_prejudice_does_not_send_email(self): - msg = "Create a domain request and reject it with prejudice and see if email was sent." - domain_request = completed_domain_request(status=DomainRequest.DomainRequestStatus.APPROVED) - self.check_email_sent(domain_request, msg, "reject_with_prejudice", 0) - - @less_console_noise_decorator - def assert_fsm_transition_raises_error(self, test_cases, method_to_run): - """Given a list of test cases, check if each transition throws the intended error""" - with boto3_mocking.clients.handler_for("sesv2", self.mock_client), less_console_noise(): - for domain_request, exception_type in test_cases: - with self.subTest(domain_request=domain_request, exception_type=exception_type): - with self.assertRaises(exception_type): - # Retrieve the method by name from the domain_request object and call it - method = getattr(domain_request, method_to_run) - # Call the method - method() - - @less_console_noise_decorator - def assert_fsm_transition_does_not_raise_error(self, test_cases, method_to_run): - """Given a list of test cases, ensure that none of them throw transition errors""" - with boto3_mocking.clients.handler_for("sesv2", self.mock_client), less_console_noise(): - for domain_request, exception_type in test_cases: - with self.subTest(domain_request=domain_request, exception_type=exception_type): - try: - # Retrieve the method by name from the DomainRequest object and call it - method = getattr(domain_request, method_to_run) - # Call the method - method() - except exception_type: - self.fail(f"{exception_type} was raised, but it was not expected.") - - @less_console_noise_decorator - def test_submit_transition_allowed_with_no_investigator(self): - """ - Tests for attempting to transition without an investigator. - For submit, this should be valid in all cases. - """ - - test_cases = [ - (self.started_domain_request, TransitionNotAllowed), - (self.in_review_domain_request, TransitionNotAllowed), - (self.action_needed_domain_request, TransitionNotAllowed), - (self.withdrawn_domain_request, TransitionNotAllowed), - ] - - # Set all investigators to none - set_domain_request_investigators(self.all_domain_requests, None) - - self.assert_fsm_transition_does_not_raise_error(test_cases, "submit") - - @less_console_noise_decorator - def test_submit_transition_allowed_with_investigator_not_staff(self): - """ - Tests for attempting to transition with an investigator user that is not staff. - For submit, this should be valid in all cases. - """ - - test_cases = [ - (self.in_review_domain_request, TransitionNotAllowed), - (self.action_needed_domain_request, TransitionNotAllowed), - ] - - # Set all investigators to a user with no staff privs - user, _ = User.objects.get_or_create(username="pancakesyrup", is_staff=False) - set_domain_request_investigators(self.all_domain_requests, user) - - self.assert_fsm_transition_does_not_raise_error(test_cases, "submit") - - @less_console_noise_decorator - def test_submit_transition_allowed(self): - """ - Test that calling submit from allowable statuses does raises TransitionNotAllowed. - """ - test_cases = [ - (self.started_domain_request, TransitionNotAllowed), - (self.in_review_domain_request, TransitionNotAllowed), - (self.action_needed_domain_request, TransitionNotAllowed), - (self.withdrawn_domain_request, TransitionNotAllowed), - ] - - self.assert_fsm_transition_does_not_raise_error(test_cases, "submit") - - @less_console_noise_decorator - def test_submit_transition_allowed_twice(self): - """ - Test that rotating between submit and in_review doesn't throw an error - """ - with boto3_mocking.clients.handler_for("sesv2", self.mock_client): - try: - # Make a submission - self.in_review_domain_request.submit() - - # Rerun the old method to get back to the original state - self.in_review_domain_request.in_review() - - # Make another submission - self.in_review_domain_request.submit() - except TransitionNotAllowed: - self.fail("TransitionNotAllowed was raised, but it was not expected.") - - self.assertEqual(self.in_review_domain_request.status, DomainRequest.DomainRequestStatus.SUBMITTED) - - @less_console_noise_decorator - def test_submit_transition_not_allowed(self): - """ - Test that calling submit against transition rules raises TransitionNotAllowed. - """ - test_cases = [ - (self.submitted_domain_request, TransitionNotAllowed), - (self.approved_domain_request, TransitionNotAllowed), - (self.rejected_domain_request, TransitionNotAllowed), - (self.ineligible_domain_request, TransitionNotAllowed), - ] - - self.assert_fsm_transition_raises_error(test_cases, "submit") - - @less_console_noise_decorator - def test_in_review_transition_allowed(self): - """ - Test that calling in_review from allowable statuses does raises TransitionNotAllowed. - """ - test_cases = [ - (self.submitted_domain_request, TransitionNotAllowed), - (self.action_needed_domain_request, TransitionNotAllowed), - (self.approved_domain_request, TransitionNotAllowed), - (self.rejected_domain_request, TransitionNotAllowed), - (self.ineligible_domain_request, TransitionNotAllowed), - ] - - self.assert_fsm_transition_does_not_raise_error(test_cases, "in_review") - - @less_console_noise_decorator - def test_in_review_transition_not_allowed_with_no_investigator(self): - """ - Tests for attempting to transition without an investigator - """ - - test_cases = [ - (self.action_needed_domain_request, TransitionNotAllowed), - (self.approved_domain_request, TransitionNotAllowed), - (self.rejected_domain_request, TransitionNotAllowed), - (self.ineligible_domain_request, TransitionNotAllowed), - ] - - # Set all investigators to none - set_domain_request_investigators(self.all_domain_requests, None) - - self.assert_fsm_transition_raises_error(test_cases, "in_review") - - @less_console_noise_decorator - def test_in_review_transition_not_allowed_with_investigator_not_staff(self): - """ - Tests for attempting to transition with an investigator that is not staff. - This should throw an exception. - """ - - test_cases = [ - (self.action_needed_domain_request, TransitionNotAllowed), - (self.approved_domain_request, TransitionNotAllowed), - (self.rejected_domain_request, TransitionNotAllowed), - (self.ineligible_domain_request, TransitionNotAllowed), - ] - - # Set all investigators to a user with no staff privs - user, _ = User.objects.get_or_create(username="pancakesyrup", is_staff=False) - set_domain_request_investigators(self.all_domain_requests, user) - - self.assert_fsm_transition_raises_error(test_cases, "in_review") - - @less_console_noise_decorator - def test_in_review_transition_not_allowed(self): - """ - Test that calling in_review against transition rules raises TransitionNotAllowed. - """ - test_cases = [ - (self.started_domain_request, TransitionNotAllowed), - (self.in_review_domain_request, TransitionNotAllowed), - (self.withdrawn_domain_request, TransitionNotAllowed), - ] - - self.assert_fsm_transition_raises_error(test_cases, "in_review") - - @less_console_noise_decorator - def test_action_needed_transition_allowed(self): - """ - Test that calling action_needed from allowable statuses does raises TransitionNotAllowed. - """ - test_cases = [ - (self.in_review_domain_request, TransitionNotAllowed), - (self.approved_domain_request, TransitionNotAllowed), - (self.rejected_domain_request, TransitionNotAllowed), - (self.ineligible_domain_request, TransitionNotAllowed), - ] - - self.assert_fsm_transition_does_not_raise_error(test_cases, "action_needed") - - @less_console_noise_decorator - def test_action_needed_transition_not_allowed_with_no_investigator(self): - """ - Tests for attempting to transition without an investigator - """ - - test_cases = [ - (self.in_review_domain_request, TransitionNotAllowed), - (self.approved_domain_request, TransitionNotAllowed), - (self.rejected_domain_request, TransitionNotAllowed), - (self.ineligible_domain_request, TransitionNotAllowed), - ] - - # Set all investigators to none - set_domain_request_investigators(self.all_domain_requests, None) - - self.assert_fsm_transition_raises_error(test_cases, "action_needed") - - @less_console_noise_decorator - def test_action_needed_transition_not_allowed_with_investigator_not_staff(self): - """ - Tests for attempting to transition with an investigator that is not staff - """ - - test_cases = [ - (self.in_review_domain_request, TransitionNotAllowed), - (self.approved_domain_request, TransitionNotAllowed), - (self.rejected_domain_request, TransitionNotAllowed), - (self.ineligible_domain_request, TransitionNotAllowed), - ] - - # Set all investigators to a user with no staff privs - user, _ = User.objects.get_or_create(username="pancakesyrup", is_staff=False) - set_domain_request_investigators(self.all_domain_requests, user) - - self.assert_fsm_transition_raises_error(test_cases, "action_needed") - - @less_console_noise_decorator - def test_action_needed_transition_not_allowed(self): - """ - Test that calling action_needed against transition rules raises TransitionNotAllowed. - """ - test_cases = [ - (self.started_domain_request, TransitionNotAllowed), - (self.submitted_domain_request, TransitionNotAllowed), - (self.action_needed_domain_request, TransitionNotAllowed), - (self.withdrawn_domain_request, TransitionNotAllowed), - ] - - self.assert_fsm_transition_raises_error(test_cases, "action_needed") - - @less_console_noise_decorator - def test_approved_transition_allowed(self): - """ - Test that calling action_needed from allowable statuses does raises TransitionNotAllowed. - """ - test_cases = [ - (self.submitted_domain_request, TransitionNotAllowed), - (self.in_review_domain_request, TransitionNotAllowed), - (self.action_needed_domain_request, TransitionNotAllowed), - (self.rejected_domain_request, TransitionNotAllowed), - ] - - self.assert_fsm_transition_does_not_raise_error(test_cases, "approve") - - @less_console_noise_decorator - def test_approved_transition_not_allowed_with_no_investigator(self): - """ - Tests for attempting to transition without an investigator - """ - - test_cases = [ - (self.in_review_domain_request, TransitionNotAllowed), - (self.action_needed_domain_request, TransitionNotAllowed), - (self.rejected_domain_request, TransitionNotAllowed), - ] - - # Set all investigators to none - set_domain_request_investigators(self.all_domain_requests, None) - - self.assert_fsm_transition_raises_error(test_cases, "approve") - - @less_console_noise_decorator - def test_approved_transition_not_allowed_with_investigator_not_staff(self): - """ - Tests for attempting to transition with an investigator that is not staff - """ - - test_cases = [ - (self.in_review_domain_request, TransitionNotAllowed), - (self.action_needed_domain_request, TransitionNotAllowed), - (self.rejected_domain_request, TransitionNotAllowed), - ] - - # Set all investigators to a user with no staff privs - user, _ = User.objects.get_or_create(username="pancakesyrup", is_staff=False) - set_domain_request_investigators(self.all_domain_requests, user) - - self.assert_fsm_transition_raises_error(test_cases, "approve") - - @less_console_noise_decorator - 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): - self.submitted_domain_request.approve(send_email=False) - - # Assert that no emails were sent - self.assertEqual(len(self.mock_client.EMAILS_SENT), 0) - - @less_console_noise_decorator - def test_approved_transition_not_allowed(self): - """ - Test that calling approve against transition rules raises TransitionNotAllowed. - """ - test_cases = [ - (self.started_domain_request, TransitionNotAllowed), - (self.approved_domain_request, TransitionNotAllowed), - (self.withdrawn_domain_request, TransitionNotAllowed), - (self.ineligible_domain_request, TransitionNotAllowed), - ] - self.assert_fsm_transition_raises_error(test_cases, "approve") - - @less_console_noise_decorator - def test_approved_transition_not_allowed_when_domain_already_approved(self): - """ - Test that calling approve whith an already approved requested domain raises - TransitionNotAllowed. - """ - Domain.objects.all().create(name=self.submitted_domain_request.requested_domain.name) - test_cases = [ - (self.submitted_domain_request, FSMDomainRequestError), - ] - self.assert_fsm_transition_raises_error(test_cases, "approve") - - @less_console_noise_decorator - def test_withdraw_transition_allowed(self): - """ - Test that calling action_needed from allowable statuses does raises TransitionNotAllowed. - """ - test_cases = [ - (self.submitted_domain_request, TransitionNotAllowed), - (self.in_review_domain_request, TransitionNotAllowed), - (self.action_needed_domain_request, TransitionNotAllowed), - ] - - self.assert_fsm_transition_does_not_raise_error(test_cases, "withdraw") - - @less_console_noise_decorator - def test_withdraw_transition_allowed_with_no_investigator(self): - """ - Tests for attempting to transition without an investigator. - For withdraw, this should be valid in all cases. - """ - - test_cases = [ - (self.submitted_domain_request, TransitionNotAllowed), - (self.in_review_domain_request, TransitionNotAllowed), - (self.action_needed_domain_request, TransitionNotAllowed), - ] - - # Set all investigators to none - set_domain_request_investigators(self.all_domain_requests, None) - - self.assert_fsm_transition_does_not_raise_error(test_cases, "withdraw") - - @less_console_noise_decorator - def test_withdraw_transition_allowed_with_investigator_not_staff(self): - """ - Tests for attempting to transition when investigator is not staff. - For withdraw, this should be valid in all cases. - """ - - test_cases = [ - (self.submitted_domain_request, TransitionNotAllowed), - (self.in_review_domain_request, TransitionNotAllowed), - (self.action_needed_domain_request, TransitionNotAllowed), - ] - - # Set all investigators to a user with no staff privs - user, _ = User.objects.get_or_create(username="pancakesyrup", is_staff=False) - set_domain_request_investigators(self.all_domain_requests, user) - - self.assert_fsm_transition_does_not_raise_error(test_cases, "withdraw") - - @less_console_noise_decorator - def test_withdraw_transition_not_allowed(self): - """ - Test that calling action_needed against transition rules raises TransitionNotAllowed. - """ - test_cases = [ - (self.started_domain_request, TransitionNotAllowed), - (self.approved_domain_request, TransitionNotAllowed), - (self.withdrawn_domain_request, TransitionNotAllowed), - (self.rejected_domain_request, TransitionNotAllowed), - (self.ineligible_domain_request, TransitionNotAllowed), - ] - - self.assert_fsm_transition_raises_error(test_cases, "withdraw") - - @less_console_noise_decorator - def test_reject_transition_allowed(self): - """ - Test that calling action_needed from allowable statuses does raises TransitionNotAllowed. - """ - test_cases = [ - (self.in_review_domain_request, TransitionNotAllowed), - (self.action_needed_domain_request, TransitionNotAllowed), - (self.approved_domain_request, TransitionNotAllowed), - ] - - self.assert_fsm_transition_does_not_raise_error(test_cases, "reject") - - @less_console_noise_decorator - def test_reject_transition_not_allowed_with_no_investigator(self): - """ - Tests for attempting to transition without an investigator - """ - - test_cases = [ - (self.in_review_domain_request, TransitionNotAllowed), - (self.action_needed_domain_request, TransitionNotAllowed), - (self.approved_domain_request, TransitionNotAllowed), - ] - - # Set all investigators to none - set_domain_request_investigators(self.all_domain_requests, None) - - self.assert_fsm_transition_raises_error(test_cases, "reject") - - @less_console_noise_decorator - def test_reject_transition_not_allowed_with_investigator_not_staff(self): - """ - Tests for attempting to transition when investigator is not staff - """ - - test_cases = [ - (self.in_review_domain_request, TransitionNotAllowed), - (self.action_needed_domain_request, TransitionNotAllowed), - (self.approved_domain_request, TransitionNotAllowed), - ] - - # Set all investigators to a user with no staff privs - user, _ = User.objects.get_or_create(username="pancakesyrup", is_staff=False) - set_domain_request_investigators(self.all_domain_requests, user) - - self.assert_fsm_transition_raises_error(test_cases, "reject") - - @less_console_noise_decorator - def test_reject_transition_not_allowed(self): - """ - Test that calling action_needed against transition rules raises TransitionNotAllowed. - """ - test_cases = [ - (self.started_domain_request, TransitionNotAllowed), - (self.submitted_domain_request, TransitionNotAllowed), - (self.withdrawn_domain_request, TransitionNotAllowed), - (self.rejected_domain_request, TransitionNotAllowed), - (self.ineligible_domain_request, TransitionNotAllowed), - ] - - self.assert_fsm_transition_raises_error(test_cases, "reject") - - @less_console_noise_decorator - def test_reject_with_prejudice_transition_allowed(self): - """ - Test that calling action_needed from allowable statuses does raises TransitionNotAllowed. - """ - test_cases = [ - (self.in_review_domain_request, TransitionNotAllowed), - (self.action_needed_domain_request, TransitionNotAllowed), - (self.approved_domain_request, TransitionNotAllowed), - (self.rejected_domain_request, TransitionNotAllowed), - ] - - self.assert_fsm_transition_does_not_raise_error(test_cases, "reject_with_prejudice") - - @less_console_noise_decorator - def test_reject_with_prejudice_transition_not_allowed_with_no_investigator(self): - """ - Tests for attempting to transition without an investigator - """ - - test_cases = [ - (self.in_review_domain_request, TransitionNotAllowed), - (self.action_needed_domain_request, TransitionNotAllowed), - (self.approved_domain_request, TransitionNotAllowed), - (self.rejected_domain_request, TransitionNotAllowed), - ] - - # Set all investigators to none - set_domain_request_investigators(self.all_domain_requests, None) - - self.assert_fsm_transition_raises_error(test_cases, "reject_with_prejudice") - - @less_console_noise_decorator - def test_reject_with_prejudice_not_allowed_with_investigator_not_staff(self): - """ - Tests for attempting to transition when investigator is not staff - """ - - test_cases = [ - (self.in_review_domain_request, TransitionNotAllowed), - (self.action_needed_domain_request, TransitionNotAllowed), - (self.approved_domain_request, TransitionNotAllowed), - (self.rejected_domain_request, TransitionNotAllowed), - ] - - # Set all investigators to a user with no staff privs - user, _ = User.objects.get_or_create(username="pancakesyrup", is_staff=False) - set_domain_request_investigators(self.all_domain_requests, user) - - self.assert_fsm_transition_raises_error(test_cases, "reject_with_prejudice") - - @less_console_noise_decorator - def test_reject_with_prejudice_transition_not_allowed(self): - """ - Test that calling action_needed against transition rules raises TransitionNotAllowed. - """ - test_cases = [ - (self.started_domain_request, TransitionNotAllowed), - (self.submitted_domain_request, TransitionNotAllowed), - (self.withdrawn_domain_request, TransitionNotAllowed), - (self.ineligible_domain_request, TransitionNotAllowed), - ] - - self.assert_fsm_transition_raises_error(test_cases, "reject_with_prejudice") - - @less_console_noise_decorator - def test_transition_not_allowed_approved_in_review_when_domain_is_active(self): - """Create a domain request with status approved, create a matching domain that - is active, and call in_review against transition rules""" - - domain = Domain.objects.create(name=self.approved_domain_request.requested_domain.name) - self.approved_domain_request.approved_domain = domain - self.approved_domain_request.save() - - # Define a custom implementation for is_active - def custom_is_active(self): - return True # Override to return True - - with boto3_mocking.clients.handler_for("sesv2", self.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): - self.approved_domain_request.in_review() - - @less_console_noise_decorator - def test_transition_not_allowed_approved_action_needed_when_domain_is_active(self): - """Create a domain request with status approved, create a matching domain that - is active, and call action_needed against transition rules""" - - domain = Domain.objects.create(name=self.approved_domain_request.requested_domain.name) - self.approved_domain_request.approved_domain = domain - self.approved_domain_request.save() - - # Define a custom implementation for is_active - def custom_is_active(self): - return True # Override to return True - - with boto3_mocking.clients.handler_for("sesv2", self.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): - self.approved_domain_request.action_needed() - - @less_console_noise_decorator - def test_transition_not_allowed_approved_rejected_when_domain_is_active(self): - """Create a domain request with status approved, create a matching domain that - is active, and call reject against transition rules""" - - domain = Domain.objects.create(name=self.approved_domain_request.requested_domain.name) - self.approved_domain_request.approved_domain = domain - self.approved_domain_request.save() - - # Define a custom implementation for is_active - def custom_is_active(self): - return True # Override to return True - - with boto3_mocking.clients.handler_for("sesv2", self.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): - self.approved_domain_request.reject() - - @less_console_noise_decorator - def test_transition_not_allowed_approved_ineligible_when_domain_is_active(self): - """Create a domain request with status approved, create a matching domain that - is active, and call reject_with_prejudice against transition rules""" - - domain = Domain.objects.create(name=self.approved_domain_request.requested_domain.name) - self.approved_domain_request.approved_domain = domain - self.approved_domain_request.save() - - # Define a custom implementation for is_active - def custom_is_active(self): - return True # Override to return True - - with boto3_mocking.clients.handler_for("sesv2", self.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): - self.approved_domain_request.reject_with_prejudice() - - @less_console_noise_decorator - def test_approve_from_rejected_clears_rejection_reason(self): - """When transitioning from rejected to approved on a domain request, - the rejection_reason is cleared.""" - - # Create a sample domain request - domain_request = completed_domain_request(status=DomainRequest.DomainRequestStatus.REJECTED) - domain_request.rejection_reason = DomainRequest.RejectionReasons.DOMAIN_PURPOSE - - # Approve - with boto3_mocking.clients.handler_for("sesv2", self.mock_client): - domain_request.approve() - - self.assertEqual(domain_request.status, DomainRequest.DomainRequestStatus.APPROVED) - self.assertEqual(domain_request.rejection_reason, None) - - @less_console_noise_decorator - def test_in_review_from_rejected_clears_rejection_reason(self): - """When transitioning from rejected to in_review on a domain request, - the rejection_reason is cleared.""" - - # Create a sample domain request - domain_request = completed_domain_request(status=DomainRequest.DomainRequestStatus.REJECTED) - domain_request.domain_is_not_active = True - domain_request.rejection_reason = DomainRequest.RejectionReasons.DOMAIN_PURPOSE - - # Approve - with boto3_mocking.clients.handler_for("sesv2", self.mock_client): - domain_request.in_review() - - self.assertEqual(domain_request.status, DomainRequest.DomainRequestStatus.IN_REVIEW) - self.assertEqual(domain_request.rejection_reason, None) - - @less_console_noise_decorator - def test_action_needed_from_rejected_clears_rejection_reason(self): - """When transitioning from rejected to action_needed on a domain request, - the rejection_reason is cleared.""" - - # Create a sample domain request - domain_request = completed_domain_request(status=DomainRequest.DomainRequestStatus.REJECTED) - domain_request.domain_is_not_active = True - domain_request.rejection_reason = DomainRequest.RejectionReasons.DOMAIN_PURPOSE - - # Approve - with boto3_mocking.clients.handler_for("sesv2", self.mock_client): - domain_request.action_needed() - - self.assertEqual(domain_request.status, DomainRequest.DomainRequestStatus.ACTION_NEEDED) - self.assertEqual(domain_request.rejection_reason, None) - - @less_console_noise_decorator - def test_has_rationale_returns_true(self): - """has_rationale() returns true when a domain request has no_other_contacts_rationale""" - self.started_domain_request.no_other_contacts_rationale = "You talkin' to me?" - self.started_domain_request.save() - self.assertEquals(self.started_domain_request.has_rationale(), True) - - @less_console_noise_decorator - def test_has_rationale_returns_false(self): - """has_rationale() returns false when a domain request has no no_other_contacts_rationale""" - self.assertEquals(self.started_domain_request.has_rationale(), False) - - @less_console_noise_decorator - def test_has_other_contacts_returns_true(self): - """has_other_contacts() returns true when a domain request has other_contacts""" - # completed_domain_request has other contacts by default - self.assertEquals(self.started_domain_request.has_other_contacts(), True) - - @less_console_noise_decorator - def test_has_other_contacts_returns_false(self): - """has_other_contacts() returns false when a domain request has no other_contacts""" - domain_request = completed_domain_request( - status=DomainRequest.DomainRequestStatus.STARTED, name="no-others.gov", has_other_contacts=False - ) - self.assertEquals(domain_request.has_other_contacts(), False) diff --git a/src/registrar/tests/test_views_request.py b/src/registrar/tests/test_views_request.py index ff2e61939..8530859e2 100644 --- a/src/registrar/tests/test_views_request.py +++ b/src/registrar/tests/test_views_request.py @@ -82,6 +82,7 @@ class DomainRequestTests(TestWithUser, WebTest): response = self.app.get(f"/domain-request/{domain_request.id}") # Ensure that the date is still set to None self.assertIsNone(domain_request.last_status_update) + print(response) # We should still grab a date for this field in this event - but it should come from the audit log instead self.assertContains(response, "Started on:") self.assertContains(response, fixed_date.strftime("%B %-d, %Y"))
Other contact informationOther contact information
{{ contact.phone }} - {% if contact.email %} - - - {% endif %} + + + Copy email +