Repository: cisagov/manage.get.gov

Issue:
In registrar admin, allow an analyst to set a "reason for rejection" and have the registrar send an email based upon the reason
### Issue description

As an analyst
I want to tell a requestor why their domain name was rejected in the same place I reject it
so that...
* we track rejection reasons as a category in the registrar
* users don't have to receive two emails about their request being rejected
* I don't have to write a separate email 

In the past, people whose request had been rejected got an automated email from the registrar simply stating the request was rejected _and_ a bespoke, analyst-crafted email that included the rejection reason. We've now [summarized the rejection reasons](https://github.com/cisagov/manage.get.gov/issues?q=is%3Aissue+is%3Aopen+reject+author%3Agabydisarli+) into 6 different circumstances. 

In the registrar admin, after setting the rejection status, an analyst should be able to select the "rejected reason". Once saved, the requestor should be sent the email template that corresponds to the rejected reason.

[Rejection email templates are in our Google Drive](https://drive.google.com/drive/folders/1gkUyKEad42GiwzN7carJtMAw1RHyG8KC)

### Acceptance criteria

- [ ] After setting a rejection status in the domain request, an analyst can select from the 6 rejection reasons (ideally without having to save the reject status first before viewing the reasons).
- [ ] After an analyst saves the domain request, the requestor receives a [reject-reason-dependent email](https://drive.google.com/drive/folders/1gkUyKEad42GiwzN7carJtMAw1RHyG8KC). 
- [ ] Analyst is able to **filter** by Rejection Reason code

### Additional context

When Status = Rejected, another field should be shown, "Reason for rejection". Mocked example:

<img width="557" alt="image" src="https://github.com/cisagov/manage.get.gov/assets/603901/9fd2d720-4336-47f4-8f99-c1541c188a31">



### Links to other issues

🔄 Relates to #608 


Discussion:
Refinement 12-29

Resolving patch:
diff --git a/src/registrar/admin.py b/src/registrar/admin.py
--- a/src/registrar/admin.py
+++ b/src/registrar/admin.py
@@ -872,7 +872,7 @@ def queryset(self, request, queryset):
     ]
 
     # Filters
-    list_filter = ("status", "organization_type", InvestigatorFilter)
+    list_filter = ("status", "organization_type", "rejection_reason", InvestigatorFilter)
 
     # Search
     search_fields = [
@@ -886,7 +886,7 @@ def queryset(self, request, queryset):
     # Detail view
     form = DomainApplicationAdminForm
     fieldsets = [
-        (None, {"fields": ["status", "investigator", "creator", "approved_domain", "notes"]}),
+        (None, {"fields": ["status", "rejection_reason", "investigator", "creator", "approved_domain", "notes"]}),
         (
             "Type of organization",
             {
@@ -987,6 +987,23 @@ def save_model(self, request, obj, form, change):
                         "This action is not permitted. The domain is already active.",
                     )
 
+                elif (
+                    obj
+                    and obj.status == models.DomainApplication.ApplicationStatus.REJECTED
+                    and not obj.rejection_reason
+                ):
+                    # This condition should never be triggered.
+                    # The opposite of this condition is acceptable (rejected -> other status and rejection_reason)
+                    # because we clean up the rejection reason in the transition in the model.
+
+                    # Clear the success message
+                    messages.set_level(request, messages.ERROR)
+
+                    messages.error(
+                        request,
+                        "A rejection reason is required.",
+                    )
+
                 else:
                     if obj.status != original_obj.status:
                         status_method_mapping = {
diff --git a/src/registrar/migrations/0070_domainapplication_rejection_reason.py b/src/registrar/migrations/0070_domainapplication_rejection_reason.py
new file mode 100644
--- /dev/null
+++ b/src/registrar/migrations/0070_domainapplication_rejection_reason.py
@@ -0,0 +1,29 @@
+# Generated by Django 4.2.7 on 2024-02-26 22:12
+
+from django.db import migrations, models
+
+
+class Migration(migrations.Migration):
+    dependencies = [
+        ("registrar", "0069_alter_contact_email_alter_contact_first_name_and_more"),
+    ]
+
+    operations = [
+        migrations.AddField(
+            model_name="domainapplication",
+            name="rejection_reason",
+            field=models.TextField(
+                blank=True,
+                choices=[
+                    ("purpose_not_met", "Purpose requirements not met"),
+                    ("requestor_not_eligible", "Requestor not eligible to make request"),
+                    ("org_has_domain", "Org already has a .gov domain"),
+                    ("contacts_not_verified", "Org contacts couldn't be verified"),
+                    ("org_not_eligible", "Org not eligible for a .gov domain"),
+                    ("naming_not_met", "Naming requirements not met"),
+                    ("other", "Other/Unspecified"),
+                ],
+                null=True,
+            ),
+        ),
+    ]
diff --git a/src/registrar/models/domain_application.py b/src/registrar/models/domain_application.py
--- a/src/registrar/models/domain_application.py
+++ b/src/registrar/models/domain_application.py
@@ -351,12 +351,34 @@ class BranchChoices(models.TextChoices):
     ]
     AGENCY_CHOICES = [(v, v) for v in AGENCIES]
 
+    class RejectionReasons(models.TextChoices):
+        DOMAIN_PURPOSE = "purpose_not_met", "Purpose requirements not met"
+        REQUESTOR = "requestor_not_eligible", "Requestor not eligible to make request"
+        SECOND_DOMAIN_REASONING = (
+            "org_has_domain",
+            "Org already has a .gov domain",
+        )
+        CONTACTS_OR_ORGANIZATION_LEGITIMACY = (
+            "contacts_not_verified",
+            "Org contacts couldn't be verified",
+        )
+        ORGANIZATION_ELIGIBILITY = "org_not_eligible", "Org not eligible for a .gov domain"
+        NAMING_REQUIREMENTS = "naming_not_met", "Naming requirements not met"
+        OTHER = "other", "Other/Unspecified"
+
     # #### Internal fields about the application #####
     status = FSMField(
         choices=ApplicationStatus.choices,  # possible states as an array of constants
         default=ApplicationStatus.STARTED,  # sensible default
         protected=False,  # can change state directly, particularly in Django admin
     )
+
+    rejection_reason = models.TextField(
+        choices=RejectionReasons.choices,
+        null=True,
+        blank=True,
+    )
+
     # This is the application user who created this application. The contact
     # information that they gave is in the `submitter` field
     creator = models.ForeignKey(
@@ -364,6 +386,7 @@ class BranchChoices(models.TextChoices):
         on_delete=models.PROTECT,
         related_name="applications_created",
     )
+
     investigator = models.ForeignKey(
         "registrar.User",
         null=True,
@@ -688,12 +711,17 @@ def in_review(self):
 
         This action is logged.
 
+        This action cleans up the rejection status if moving away from rejected.
+
         As side effects this will delete the domain and domain_information
         (will cascade) when they exist."""
 
         if self.status == self.ApplicationStatus.APPROVED:
             self.delete_and_clean_up_domain("in_review")
 
+        if self.status == self.ApplicationStatus.REJECTED:
+            self.rejection_reason = None
+
         literal = DomainApplication.ApplicationStatus.IN_REVIEW
         # Check if the tuple exists, then grab its value
         in_review = literal if literal is not None else "In Review"
@@ -715,12 +743,17 @@ def action_needed(self):
 
         This action is logged.
 
+        This action cleans up the rejection status if moving away from rejected.
+
         As side effects this will delete the domain and domain_information
         (will cascade) when they exist."""
 
         if self.status == self.ApplicationStatus.APPROVED:
             self.delete_and_clean_up_domain("reject_with_prejudice")
 
+        if self.status == self.ApplicationStatus.REJECTED:
+            self.rejection_reason = None
+
         literal = DomainApplication.ApplicationStatus.ACTION_NEEDED
         # Check if the tuple is setup correctly, then grab its value
         action_needed = literal if literal is not None else "Action Needed"
@@ -739,6 +772,8 @@ def action_needed(self):
     def approve(self, send_email=True):
         """Approve an application that has been submitted.
 
+        This action cleans up the rejection status if moving away from rejected.
+
         This has substantial side-effects because it creates another database
         object for the approved Domain and makes the user who created the
         application into an admin on that domain. It also triggers an email
@@ -761,6 +796,9 @@ def approve(self, send_email=True):
             user=self.creator, domain=created_domain, role=UserDomainRole.Roles.MANAGER
         )
 
+        if self.status == self.ApplicationStatus.REJECTED:
+            self.rejection_reason = None
+
         self._send_status_update_email(
             "application approved",
             "emails/status_change_approved.txt",


Test patch:
diff --git a/src/registrar/tests/test_admin.py b/src/registrar/tests/test_admin.py
--- a/src/registrar/tests/test_admin.py
+++ b/src/registrar/tests/test_admin.py
@@ -445,40 +445,43 @@ def setUp(self):
         self.application = completed_application()
 
     def test_form_choices(self):
-        # Create a form instance with the test application
-        form = DomainApplicationAdminForm(instance=self.application)
+        with less_console_noise():
+            # Create a form instance with the test application
+            form = DomainApplicationAdminForm(instance=self.application)
 
-        # Verify that the form choices match the available transitions for started
-        expected_choices = [("started", "Started"), ("submitted", "Submitted")]
-        self.assertEqual(form.fields["status"].widget.choices, expected_choices)
+            # Verify that the form choices match the available transitions for started
+            expected_choices = [("started", "Started"), ("submitted", "Submitted")]
+            self.assertEqual(form.fields["status"].widget.choices, expected_choices)
 
     def test_form_choices_when_no_instance(self):
-        # Create a form instance without an instance
-        form = DomainApplicationAdminForm()
-
-        # Verify that the form choices show all choices when no instance is provided;
-        # this is necessary to show all choices when creating a new domain
-        # application in django admin;
-        # note that FSM ensures that no domain application exists with invalid status,
-        # so don't need to test for invalid status
-        self.assertEqual(
-            form.fields["status"].widget.choices,
-            DomainApplication._meta.get_field("status").choices,
-        )
+        with less_console_noise():
+            # Create a form instance without an instance
+            form = DomainApplicationAdminForm()
+
+            # Verify that the form choices show all choices when no instance is provided;
+            # this is necessary to show all choices when creating a new domain
+            # application in django admin;
+            # note that FSM ensures that no domain application exists with invalid status,
+            # so don't need to test for invalid status
+            self.assertEqual(
+                form.fields["status"].widget.choices,
+                DomainApplication._meta.get_field("status").choices,
+            )
 
     def test_form_choices_when_ineligible(self):
-        # Create a form instance with a domain application with ineligible status
-        ineligible_application = DomainApplication(status="ineligible")
+        with less_console_noise():
+            # Create a form instance with a domain application with ineligible status
+            ineligible_application = DomainApplication(status="ineligible")
 
-        # Attempt to create a form with the ineligible application
-        # The form should not raise an error, but choices should be the
-        # full list of possible choices
-        form = DomainApplicationAdminForm(instance=ineligible_application)
+            # Attempt to create a form with the ineligible application
+            # The form should not raise an error, but choices should be the
+            # full list of possible choices
+            form = DomainApplicationAdminForm(instance=ineligible_application)
 
-        self.assertEqual(
-            form.fields["status"].widget.choices,
-            DomainApplication._meta.get_field("status").choices,
-        )
+            self.assertEqual(
+                form.fields["status"].widget.choices,
+                DomainApplication._meta.get_field("status").choices,
+            )
 
 
 @boto3_mocking.patching
@@ -502,92 +505,96 @@ def setUp(self):
 
     def test_domain_sortable(self):
         """Tests if the DomainApplication sorts by domain correctly"""
-        p = "adminpass"
-        self.client.login(username="superuser", password=p)
+        with less_console_noise():
+            p = "adminpass"
+            self.client.login(username="superuser", password=p)
 
-        multiple_unalphabetical_domain_objects("application")
+            multiple_unalphabetical_domain_objects("application")
 
-        # Assert that our sort works correctly
-        self.test_helper.assert_table_sorted("1", ("requested_domain__name",))
+            # Assert that our sort works correctly
+            self.test_helper.assert_table_sorted("1", ("requested_domain__name",))
 
-        # Assert that sorting in reverse works correctly
-        self.test_helper.assert_table_sorted("-1", ("-requested_domain__name",))
+            # Assert that sorting in reverse works correctly
+            self.test_helper.assert_table_sorted("-1", ("-requested_domain__name",))
 
     def test_submitter_sortable(self):
         """Tests if the DomainApplication sorts by domain correctly"""
-        p = "adminpass"
-        self.client.login(username="superuser", password=p)
-
-        multiple_unalphabetical_domain_objects("application")
-
-        additional_application = generic_domain_object("application", "Xylophone")
-        new_user = User.objects.filter(username=additional_application.investigator.username).get()
-        new_user.first_name = "Xylophonic"
-        new_user.save()
-
-        # Assert that our sort works correctly
-        self.test_helper.assert_table_sorted(
-            "5",
-            (
-                "submitter__first_name",
-                "submitter__last_name",
-            ),
-        )
+        with less_console_noise():
+            p = "adminpass"
+            self.client.login(username="superuser", password=p)
 
-        # Assert that sorting in reverse works correctly
-        self.test_helper.assert_table_sorted(
-            "-5",
-            (
-                "-submitter__first_name",
-                "-submitter__last_name",
-            ),
-        )
+            multiple_unalphabetical_domain_objects("application")
+
+            additional_application = generic_domain_object("application", "Xylophone")
+            new_user = User.objects.filter(username=additional_application.investigator.username).get()
+            new_user.first_name = "Xylophonic"
+            new_user.save()
+
+            # Assert that our sort works correctly
+            self.test_helper.assert_table_sorted(
+                "5",
+                (
+                    "submitter__first_name",
+                    "submitter__last_name",
+                ),
+            )
+
+            # Assert that sorting in reverse works correctly
+            self.test_helper.assert_table_sorted(
+                "-5",
+                (
+                    "-submitter__first_name",
+                    "-submitter__last_name",
+                ),
+            )
 
     def test_investigator_sortable(self):
         """Tests if the DomainApplication sorts by domain correctly"""
-        p = "adminpass"
-        self.client.login(username="superuser", password=p)
-
-        multiple_unalphabetical_domain_objects("application")
-        additional_application = generic_domain_object("application", "Xylophone")
-        new_user = User.objects.filter(username=additional_application.investigator.username).get()
-        new_user.first_name = "Xylophonic"
-        new_user.save()
-
-        # Assert that our sort works correctly
-        self.test_helper.assert_table_sorted(
-            "6",
-            (
-                "investigator__first_name",
-                "investigator__last_name",
-            ),
-        )
+        with less_console_noise():
+            p = "adminpass"
+            self.client.login(username="superuser", password=p)
 
-        # Assert that sorting in reverse works correctly
-        self.test_helper.assert_table_sorted(
-            "-6",
-            (
-                "-investigator__first_name",
-                "-investigator__last_name",
-            ),
-        )
+            multiple_unalphabetical_domain_objects("application")
+            additional_application = generic_domain_object("application", "Xylophone")
+            new_user = User.objects.filter(username=additional_application.investigator.username).get()
+            new_user.first_name = "Xylophonic"
+            new_user.save()
+
+            # Assert that our sort works correctly
+            self.test_helper.assert_table_sorted(
+                "6",
+                (
+                    "investigator__first_name",
+                    "investigator__last_name",
+                ),
+            )
+
+            # Assert that sorting in reverse works correctly
+            self.test_helper.assert_table_sorted(
+                "-6",
+                (
+                    "-investigator__first_name",
+                    "-investigator__last_name",
+                ),
+            )
 
     def test_short_org_name_in_applications_list(self):
         """
         Make sure the short name is displaying in admin on the list page
         """
-        self.client.force_login(self.superuser)
-        completed_application()
-        response = self.client.get("/admin/registrar/domainapplication/")
-        # There are 3 template references to Federal (3) plus one reference in the table
-        # for our actual application
-        self.assertContains(response, "Federal", count=4)
-        # This may be a bit more robust
-        self.assertContains(response, '<td class="field-organization_type">Federal</td>', count=1)
-        # Now let's make sure the long description does not exist
-        self.assertNotContains(response, "Federal: an agency of the U.S. government")
-
-    def transition_state_and_send_email(self, application, status):
+        with less_console_noise():
+            self.client.force_login(self.superuser)
+            completed_application()
+            response = self.client.get("/admin/registrar/domainapplication/")
+            # There are 3 template references to Federal (3) plus one reference in the table
+            # for our actual application
+            self.assertContains(response, "Federal", count=4)
+            # This may be a bit more robust
+            self.assertContains(response, '<td class="field-organization_type">Federal</td>', count=1)
+            # Now let's make sure the long description does not exist
+            self.assertNotContains(response, "Federal: an agency of the U.S. government")
+
+    def transition_state_and_send_email(self, application, status, rejection_reason=None):
         """Helper method for the email test cases."""
 
         with boto3_mocking.clients.handler_for("sesv2", self.mock_client):
@@ -595,8 +602,9 @@ def transition_state_and_send_email(self, application, status):
                 # Create a mock request
                 request = self.factory.post("/admin/registrar/domainapplication/{}/change/".format(application.pk))
 
-                # Modify the application's property
+                # Modify the application's properties
                 application.status = status
+                application.rejection_reason = rejection_reason
 
                 # Use the model admin's save_model method
                 self.admin.save_model(request, application, form=None, change=True)
@@ -607,20 +615,21 @@ def assert_email_is_accurate(
         """Helper method for the email test cases.
         email_index is the index of the email in mock_client."""
 
-        # Access the arguments passed to send_email
-        call_args = self.mock_client.EMAILS_SENT
-        kwargs = call_args[email_index]["kwargs"]
+        with less_console_noise():
+            # Access the arguments passed to send_email
+            call_args = self.mock_client.EMAILS_SENT
+            kwargs = call_args[email_index]["kwargs"]
 
-        # Retrieve the email details from the arguments
-        from_email = kwargs.get("FromEmailAddress")
-        to_email = kwargs["Destination"]["ToAddresses"][0]
-        email_content = kwargs["Content"]
-        email_body = email_content["Simple"]["Body"]["Text"]["Data"]
+            # Retrieve the email details from the arguments
+            from_email = kwargs.get("FromEmailAddress")
+            to_email = kwargs["Destination"]["ToAddresses"][0]
+            email_content = kwargs["Content"]
+            email_body = email_content["Simple"]["Body"]["Text"]["Data"]
 
-        # Assert or perform other checks on the email details
-        self.assertEqual(from_email, settings.DEFAULT_FROM_EMAIL)
-        self.assertEqual(to_email, email_address)
-        self.assertIn(expected_string, email_body)
+            # Assert or perform other checks on the email details
+            self.assertEqual(from_email, settings.DEFAULT_FROM_EMAIL)
+            self.assertEqual(to_email, email_address)
+            self.assertIn(expected_string, email_body)
 
         if test_that_no_bcc:
             _ = ""
@@ -643,48 +652,49 @@ def test_save_model_sends_submitted_email(self):
         Also test that the default email set in settings is NOT BCCd on non-prod whenever
         an email does go out."""
 
-        # Ensure there is no user with this email
-        EMAIL = "mayor@igorville.gov"
-        User.objects.filter(email=EMAIL).delete()
-
-        # Create a sample application
-        application = completed_application()
-
-        # Test Submitted Status from started
-        self.transition_state_and_send_email(application, DomainApplication.ApplicationStatus.SUBMITTED)
-        self.assert_email_is_accurate("We received your .gov domain request.", 0, EMAIL, True)
-        self.assertEqual(len(self.mock_client.EMAILS_SENT), 1)
-
-        # Test Withdrawn Status
-        self.transition_state_and_send_email(application, DomainApplication.ApplicationStatus.WITHDRAWN)
-        self.assert_email_is_accurate(
-            "Your .gov domain request has been withdrawn and will not be reviewed by our team.", 1, EMAIL, True
-        )
-        self.assertEqual(len(self.mock_client.EMAILS_SENT), 2)
+        with less_console_noise():
+            # Ensure there is no user with this email
+            EMAIL = "mayor@igorville.gov"
+            User.objects.filter(email=EMAIL).delete()
+
+            # Create a sample application
+            application = completed_application()
+
+            # Test Submitted Status from started
+            self.transition_state_and_send_email(application, DomainApplication.ApplicationStatus.SUBMITTED)
+            self.assert_email_is_accurate("We received your .gov domain request.", 0, EMAIL, True)
+            self.assertEqual(len(self.mock_client.EMAILS_SENT), 1)
+
+            # Test Withdrawn Status
+            self.transition_state_and_send_email(application, DomainApplication.ApplicationStatus.WITHDRAWN)
+            self.assert_email_is_accurate(
+                "Your .gov domain request has been withdrawn and will not be reviewed by our team.", 1, EMAIL, True
+            )
+            self.assertEqual(len(self.mock_client.EMAILS_SENT), 2)
 
-        # Test Submitted Status Again (from withdrawn)
-        self.transition_state_and_send_email(application, DomainApplication.ApplicationStatus.SUBMITTED)
-        self.assertEqual(len(self.mock_client.EMAILS_SENT), 3)
+            # Test Submitted Status Again (from withdrawn)
+            self.transition_state_and_send_email(application, DomainApplication.ApplicationStatus.SUBMITTED)
+            self.assertEqual(len(self.mock_client.EMAILS_SENT), 3)
 
-        # Move it to IN_REVIEW
-        self.transition_state_and_send_email(application, DomainApplication.ApplicationStatus.IN_REVIEW)
-        self.assertEqual(len(self.mock_client.EMAILS_SENT), 3)
+            # Move it to IN_REVIEW
+            self.transition_state_and_send_email(application, DomainApplication.ApplicationStatus.IN_REVIEW)
+            self.assertEqual(len(self.mock_client.EMAILS_SENT), 3)
 
-        # Test Submitted Status Again from in IN_REVIEW, no new email should be sent
-        self.transition_state_and_send_email(application, DomainApplication.ApplicationStatus.SUBMITTED)
-        self.assertEqual(len(self.mock_client.EMAILS_SENT), 3)
+            # Test Submitted Status Again from in IN_REVIEW, no new email should be sent
+            self.transition_state_and_send_email(application, DomainApplication.ApplicationStatus.SUBMITTED)
+            self.assertEqual(len(self.mock_client.EMAILS_SENT), 3)
 
-        # Move it to IN_REVIEW
-        self.transition_state_and_send_email(application, DomainApplication.ApplicationStatus.IN_REVIEW)
-        self.assertEqual(len(self.mock_client.EMAILS_SENT), 3)
+            # Move it to IN_REVIEW
+            self.transition_state_and_send_email(application, DomainApplication.ApplicationStatus.IN_REVIEW)
+            self.assertEqual(len(self.mock_client.EMAILS_SENT), 3)
 
-        # Move it to ACTION_NEEDED
-        self.transition_state_and_send_email(application, DomainApplication.ApplicationStatus.ACTION_NEEDED)
-        self.assertEqual(len(self.mock_client.EMAILS_SENT), 3)
+            # Move it to ACTION_NEEDED
+            self.transition_state_and_send_email(application, DomainApplication.ApplicationStatus.ACTION_NEEDED)
+            self.assertEqual(len(self.mock_client.EMAILS_SENT), 3)
 
-        # Test Submitted Status Again from in ACTION_NEEDED, no new email should be sent
-        self.transition_state_and_send_email(application, DomainApplication.ApplicationStatus.SUBMITTED)
-        self.assertEqual(len(self.mock_client.EMAILS_SENT), 3)
+            # Test Submitted Status Again from in ACTION_NEEDED, no new email should be sent
+            self.transition_state_and_send_email(application, DomainApplication.ApplicationStatus.SUBMITTED)
+            self.assertEqual(len(self.mock_client.EMAILS_SENT), 3)
 
     @override_settings(IS_PRODUCTION=True)
     def test_save_model_sends_submitted_email_with_bcc_on_prod(self):
@@ -697,351 +707,597 @@ def test_save_model_sends_submitted_email_with_bcc_on_prod(self):
         Also test that the default email set in settings IS BCCd on prod whenever
         an email does go out."""
 
-        # Ensure there is no user with this email
-        EMAIL = "mayor@igorville.gov"
-        User.objects.filter(email=EMAIL).delete()
+        with less_console_noise():
+            # Ensure there is no user with this email
+            EMAIL = "mayor@igorville.gov"
+            User.objects.filter(email=EMAIL).delete()
 
-        BCC_EMAIL = settings.DEFAULT_FROM_EMAIL
+            BCC_EMAIL = settings.DEFAULT_FROM_EMAIL
 
-        # Create a sample application
-        application = completed_application()
+            # Create a sample application
+            application = completed_application()
 
-        # Test Submitted Status from started
-        self.transition_state_and_send_email(application, DomainApplication.ApplicationStatus.SUBMITTED)
-        self.assert_email_is_accurate("We received your .gov domain request.", 0, EMAIL, False, BCC_EMAIL)
-        self.assertEqual(len(self.mock_client.EMAILS_SENT), 1)
+            # Test Submitted Status from started
+            self.transition_state_and_send_email(application, DomainApplication.ApplicationStatus.SUBMITTED)
+            self.assert_email_is_accurate("We received your .gov domain request.", 0, EMAIL, False, BCC_EMAIL)
+            self.assertEqual(len(self.mock_client.EMAILS_SENT), 1)
 
-        # Test Withdrawn Status
-        self.transition_state_and_send_email(application, DomainApplication.ApplicationStatus.WITHDRAWN)
-        self.assert_email_is_accurate(
-            "Your .gov domain request has been withdrawn and will not be reviewed by our team.", 1, EMAIL
-        )
-        self.assertEqual(len(self.mock_client.EMAILS_SENT), 2)
+            # Test Withdrawn Status
+            self.transition_state_and_send_email(application, DomainApplication.ApplicationStatus.WITHDRAWN)
+            self.assert_email_is_accurate(
+                "Your .gov domain request has been withdrawn and will not be reviewed by our team.", 1, EMAIL
+            )
+            self.assertEqual(len(self.mock_client.EMAILS_SENT), 2)
 
-        # Test Submitted Status Again (from withdrawn)
-        self.transition_state_and_send_email(application, DomainApplication.ApplicationStatus.SUBMITTED)
-        self.assert_email_is_accurate("We received your .gov domain request.", 0, EMAIL, False, BCC_EMAIL)
-        self.assertEqual(len(self.mock_client.EMAILS_SENT), 3)
+            # Test Submitted Status Again (from withdrawn)
+            self.transition_state_and_send_email(application, DomainApplication.ApplicationStatus.SUBMITTED)
+            self.assert_email_is_accurate("We received your .gov domain request.", 0, EMAIL, False, BCC_EMAIL)
+            self.assertEqual(len(self.mock_client.EMAILS_SENT), 3)
 
-        # Move it to IN_REVIEW
-        self.transition_state_and_send_email(application, DomainApplication.ApplicationStatus.IN_REVIEW)
-        self.assertEqual(len(self.mock_client.EMAILS_SENT), 3)
+            # Move it to IN_REVIEW
+            self.transition_state_and_send_email(application, DomainApplication.ApplicationStatus.IN_REVIEW)
+            self.assertEqual(len(self.mock_client.EMAILS_SENT), 3)
 
-        # Test Submitted Status Again from in IN_REVIEW, no new email should be sent
-        self.transition_state_and_send_email(application, DomainApplication.ApplicationStatus.SUBMITTED)
-        self.assertEqual(len(self.mock_client.EMAILS_SENT), 3)
+            # Test Submitted Status Again from in IN_REVIEW, no new email should be sent
+            self.transition_state_and_send_email(application, DomainApplication.ApplicationStatus.SUBMITTED)
+            self.assertEqual(len(self.mock_client.EMAILS_SENT), 3)
 
-        # Move it to IN_REVIEW
-        self.transition_state_and_send_email(application, DomainApplication.ApplicationStatus.IN_REVIEW)
-        self.assertEqual(len(self.mock_client.EMAILS_SENT), 3)
+            # Move it to IN_REVIEW
+            self.transition_state_and_send_email(application, DomainApplication.ApplicationStatus.IN_REVIEW)
+            self.assertEqual(len(self.mock_client.EMAILS_SENT), 3)
 
-        # Move it to ACTION_NEEDED
-        self.transition_state_and_send_email(application, DomainApplication.ApplicationStatus.ACTION_NEEDED)
-        self.assertEqual(len(self.mock_client.EMAILS_SENT), 3)
+            # Move it to ACTION_NEEDED
+            self.transition_state_and_send_email(application, DomainApplication.ApplicationStatus.ACTION_NEEDED)
+            self.assertEqual(len(self.mock_client.EMAILS_SENT), 3)
 
-        # Test Submitted Status Again from in ACTION_NEEDED, no new email should be sent
-        self.transition_state_and_send_email(application, DomainApplication.ApplicationStatus.SUBMITTED)
-        self.assertEqual(len(self.mock_client.EMAILS_SENT), 3)
+            # Test Submitted Status Again from in ACTION_NEEDED, no new email should be sent
+            self.transition_state_and_send_email(application, DomainApplication.ApplicationStatus.SUBMITTED)
+            self.assertEqual(len(self.mock_client.EMAILS_SENT), 3)
 
     def test_save_model_sends_approved_email(self):
         """When transitioning to approved on a domain request,
         an email is sent out every time."""
 
-        # Ensure there is no user with this email
-        EMAIL = "mayor@igorville.gov"
-        User.objects.filter(email=EMAIL).delete()
+        with less_console_noise():
+            # Ensure there is no user with this email
+            EMAIL = "mayor@igorville.gov"
+            User.objects.filter(email=EMAIL).delete()
+
+            # Create a sample application
+            application = completed_application(status=DomainApplication.ApplicationStatus.IN_REVIEW)
+
+            # Test Submitted Status
+            self.transition_state_and_send_email(application, DomainApplication.ApplicationStatus.APPROVED)
+            self.assert_email_is_accurate("Congratulations! Your .gov domain request has been approved.", 0, EMAIL)
+            self.assertEqual(len(self.mock_client.EMAILS_SENT), 1)
 
-        # Create a sample application
-        application = completed_application(status=DomainApplication.ApplicationStatus.IN_REVIEW)
+            # Test Withdrawn Status
+            self.transition_state_and_send_email(
+                application,
+                DomainApplication.ApplicationStatus.REJECTED,
+                DomainApplication.RejectionReasons.DOMAIN_PURPOSE,
+            )
+            self.assert_email_is_accurate("Your .gov domain request has been rejected.", 1, EMAIL)
+            self.assertEqual(len(self.mock_client.EMAILS_SENT), 2)
 
-        # Test Submitted Status
-        self.transition_state_and_send_email(application, DomainApplication.ApplicationStatus.APPROVED)
-        self.assert_email_is_accurate("Congratulations! Your .gov domain request has been approved.", 0, EMAIL)
-        self.assertEqual(len(self.mock_client.EMAILS_SENT), 1)
+            # Test Submitted Status Again (No new email should be sent)
+            self.transition_state_and_send_email(application, DomainApplication.ApplicationStatus.APPROVED)
+            self.assertEqual(len(self.mock_client.EMAILS_SENT), 3)
 
-        # Test Withdrawn Status
-        self.transition_state_and_send_email(application, DomainApplication.ApplicationStatus.REJECTED)
-        self.assert_email_is_accurate("Your .gov domain request has been rejected.", 1, EMAIL)
-        self.assertEqual(len(self.mock_client.EMAILS_SENT), 2)
+    def test_save_model_sends_rejected_email_purpose_not_met(self):
+        """When transitioning to rejected on a domain request, an email is sent
+        explaining why when the reason is domain purpose."""
 
-        # Test Submitted Status Again (No new email should be sent)
-        self.transition_state_and_send_email(application, DomainApplication.ApplicationStatus.APPROVED)
-        self.assertEqual(len(self.mock_client.EMAILS_SENT), 3)
+        with less_console_noise():
+            # Ensure there is no user with this email
+            EMAIL = "mayor@igorville.gov"
+            User.objects.filter(email=EMAIL).delete()
 
-    def test_save_model_sends_rejected_email(self):
-        """When transitioning to rejected on a domain request,
-        an email is sent out every time."""
+            # Create a sample application
+            application = completed_application(status=DomainApplication.ApplicationStatus.IN_REVIEW)
+
+            # Reject for reason DOMAIN_PURPOSE and test email
+            self.transition_state_and_send_email(
+                application,
+                DomainApplication.ApplicationStatus.REJECTED,
+                DomainApplication.RejectionReasons.DOMAIN_PURPOSE,
+            )
+            self.assert_email_is_accurate(
+                "Your domain request was rejected because the purpose you provided did not meet our \nrequirements.",
+                0,
+                EMAIL,
+            )
+            self.assertEqual(len(self.mock_client.EMAILS_SENT), 1)
 
-        # Ensure there is no user with this email
-        EMAIL = "mayor@igorville.gov"
-        User.objects.filter(email=EMAIL).delete()
+            # Approve
+            self.transition_state_and_send_email(application, DomainApplication.ApplicationStatus.APPROVED)
+            self.assert_email_is_accurate("Congratulations! Your .gov domain request has been approved.", 1, EMAIL)
+            self.assertEqual(len(self.mock_client.EMAILS_SENT), 2)
 
-        # Create a sample application
-        application = completed_application(status=DomainApplication.ApplicationStatus.IN_REVIEW)
+    def test_save_model_sends_rejected_email_requestor(self):
+        """When transitioning to rejected on a domain request, an email is sent
+        explaining why when the reason is requestor."""
+
+        with less_console_noise():
+            # Ensure there is no user with this email
+            EMAIL = "mayor@igorville.gov"
+            User.objects.filter(email=EMAIL).delete()
+
+            # Create a sample application
+            application = completed_application(status=DomainApplication.ApplicationStatus.IN_REVIEW)
+
+            # Reject for reason REQUESTOR and test email including dynamic organization name
+            self.transition_state_and_send_email(
+                application, DomainApplication.ApplicationStatus.REJECTED, DomainApplication.RejectionReasons.REQUESTOR
+            )
+            self.assert_email_is_accurate(
+                "Your domain request was rejected because we don’t believe you’re eligible to request a \n.gov "
+                "domain on behalf of Testorg",
+                0,
+                EMAIL,
+            )
+            self.assertEqual(len(self.mock_client.EMAILS_SENT), 1)
 
-        # Test Submitted Status
-        self.transition_state_and_send_email(application, DomainApplication.ApplicationStatus.REJECTED)
-        self.assert_email_is_accurate("Your .gov domain request has been rejected.", 0, EMAIL)
-        self.assertEqual(len(self.mock_client.EMAILS_SENT), 1)
+            # Approve
+            self.transition_state_and_send_email(application, DomainApplication.ApplicationStatus.APPROVED)
+            self.assert_email_is_accurate("Congratulations! Your .gov domain request has been approved.", 1, EMAIL)
+            self.assertEqual(len(self.mock_client.EMAILS_SENT), 2)
+
+    def test_save_model_sends_rejected_email_org_has_domain(self):
+        """When transitioning to rejected on a domain request, an email is sent
+        explaining why when the reason is second domain."""
+
+        with less_console_noise():
+            # Ensure there is no user with this email
+            EMAIL = "mayor@igorville.gov"
+            User.objects.filter(email=EMAIL).delete()
+
+            # Create a sample application
+            application = completed_application(status=DomainApplication.ApplicationStatus.IN_REVIEW)
+
+            # Reject for reason SECOND_DOMAIN_REASONING and test email including dynamic organization name
+            self.transition_state_and_send_email(
+                application,
+                DomainApplication.ApplicationStatus.REJECTED,
+                DomainApplication.RejectionReasons.SECOND_DOMAIN_REASONING,
+            )
+            self.assert_email_is_accurate(
+                "Your domain request was rejected because Testorg has a .gov domain.", 0, EMAIL
+            )
+            self.assertEqual(len(self.mock_client.EMAILS_SENT), 1)
+
+            # Approve
+            self.transition_state_and_send_email(application, DomainApplication.ApplicationStatus.APPROVED)
+            self.assert_email_is_accurate("Congratulations! Your .gov domain request has been approved.", 1, EMAIL)
+            self.assertEqual(len(self.mock_client.EMAILS_SENT), 2)
+
+    def test_save_model_sends_rejected_email_contacts_or_org_legitimacy(self):
+        """When transitioning to rejected on a domain request, an email is sent
+        explaining why when the reason is contacts or org legitimacy."""
+
+        with less_console_noise():
+            # Ensure there is no user with this email
+            EMAIL = "mayor@igorville.gov"
+            User.objects.filter(email=EMAIL).delete()
+
+            # Create a sample application
+            application = completed_application(status=DomainApplication.ApplicationStatus.IN_REVIEW)
 
-        # Test Withdrawn Status
-        self.transition_state_and_send_email(application, DomainApplication.ApplicationStatus.APPROVED)
-        self.assert_email_is_accurate("Congratulations! Your .gov domain request has been approved.", 1, EMAIL)
-        self.assertEqual(len(self.mock_client.EMAILS_SENT), 2)
+            # Reject for reason CONTACTS_OR_ORGANIZATION_LEGITIMACY and test email including dynamic organization name
+            self.transition_state_and_send_email(
+                application,
+                DomainApplication.ApplicationStatus.REJECTED,
+                DomainApplication.RejectionReasons.CONTACTS_OR_ORGANIZATION_LEGITIMACY,
+            )
+            self.assert_email_is_accurate(
+                "Your domain request was rejected because we could not verify the organizational \n"
+                "contacts you provided. If you have questions or comments, reply to this email.",
+                0,
+                EMAIL,
+            )
+            self.assertEqual(len(self.mock_client.EMAILS_SENT), 1)
+
+            # Approve
+            self.transition_state_and_send_email(application, DomainApplication.ApplicationStatus.APPROVED)
+            self.assert_email_is_accurate("Congratulations! Your .gov domain request has been approved.", 1, EMAIL)
+            self.assertEqual(len(self.mock_client.EMAILS_SENT), 2)
+
+    def test_save_model_sends_rejected_email_org_eligibility(self):
+        """When transitioning to rejected on a domain request, an email is sent
+        explaining why when the reason is org eligibility."""
+
+        with less_console_noise():
+            # Ensure there is no user with this email
+            EMAIL = "mayor@igorville.gov"
+            User.objects.filter(email=EMAIL).delete()
+
+            # Create a sample application
+            application = completed_application(status=DomainApplication.ApplicationStatus.IN_REVIEW)
+
+            # Reject for reason ORGANIZATION_ELIGIBILITY and test email including dynamic organization name
+            self.transition_state_and_send_email(
+                application,
+                DomainApplication.ApplicationStatus.REJECTED,
+                DomainApplication.RejectionReasons.ORGANIZATION_ELIGIBILITY,
+            )
+            self.assert_email_is_accurate(
+                "Your domain request was rejected because we determined that Testorg is not \neligible for "
+                "a .gov domain.",
+                0,
+                EMAIL,
+            )
+            self.assertEqual(len(self.mock_client.EMAILS_SENT), 1)
+
+            # Approve
+            self.transition_state_and_send_email(application, DomainApplication.ApplicationStatus.APPROVED)
+            self.assert_email_is_accurate("Congratulations! Your .gov domain request has been approved.", 1, EMAIL)
+            self.assertEqual(len(self.mock_client.EMAILS_SENT), 2)
+
+    def test_save_model_sends_rejected_email_naming(self):
+        """When transitioning to rejected on a domain request, an email is sent
+        explaining why when the reason is naming."""
+
+        with less_console_noise():
+            # Ensure there is no user with this email
+            EMAIL = "mayor@igorville.gov"
+            User.objects.filter(email=EMAIL).delete()
+
+            # Create a sample application
+            application = completed_application(status=DomainApplication.ApplicationStatus.IN_REVIEW)
+
+            # Reject for reason NAMING_REQUIREMENTS and test email including dynamic organization name
+            self.transition_state_and_send_email(
+                application,
+                DomainApplication.ApplicationStatus.REJECTED,
+                DomainApplication.RejectionReasons.NAMING_REQUIREMENTS,
+            )
+            self.assert_email_is_accurate(
+                "Your domain request was rejected because it does not meet our naming requirements.", 0, EMAIL
+            )
+            self.assertEqual(len(self.mock_client.EMAILS_SENT), 1)
+
+            # Approve
+            self.transition_state_and_send_email(application, DomainApplication.ApplicationStatus.APPROVED)
+            self.assert_email_is_accurate("Congratulations! Your .gov domain request has been approved.", 1, EMAIL)
+            self.assertEqual(len(self.mock_client.EMAILS_SENT), 2)
+
+    def test_save_model_sends_rejected_email_other(self):
+        """When transitioning to rejected on a domain request, an email is sent
+        explaining why when the reason is other."""
+
+        with less_console_noise():
+            # Ensure there is no user with this email
+            EMAIL = "mayor@igorville.gov"
+            User.objects.filter(email=EMAIL).delete()
+
+            # Create a sample application
+            application = completed_application(status=DomainApplication.ApplicationStatus.IN_REVIEW)
+
+            # Reject for reason NAMING_REQUIREMENTS and test email including dynamic organization name
+            self.transition_state_and_send_email(
+                application,
+                DomainApplication.ApplicationStatus.REJECTED,
+                DomainApplication.RejectionReasons.OTHER,
+            )
+            self.assert_email_is_accurate("Choosing a .gov domain name", 0, EMAIL)
+            self.assertEqual(len(self.mock_client.EMAILS_SENT), 1)
 
-        # Test Submitted Status Again (No new email should be sent)
-        self.transition_state_and_send_email(application, DomainApplication.ApplicationStatus.REJECTED)
-        self.assertEqual(len(self.mock_client.EMAILS_SENT), 3)
+            # Approve
+            self.transition_state_and_send_email(application, DomainApplication.ApplicationStatus.APPROVED)
+            self.assert_email_is_accurate("Congratulations! Your .gov domain request has been approved.", 1, EMAIL)
+            self.assertEqual(len(self.mock_client.EMAILS_SENT), 2)
+
+    def test_transition_to_rejected_without_rejection_reason_does_trigger_error(self):
+        """
+        When transitioning to rejected without a rejection reason, admin throws a user friendly message.
+
+        The transition fails.
+        """
+
+        with less_console_noise():
+            application = completed_application(status=DomainApplication.ApplicationStatus.APPROVED)
+
+            # Create a request object with a superuser
+            request = self.factory.post("/admin/registrar/domainapplication/{}/change/".format(application.pk))
+            request.user = self.superuser
+
+            with ExitStack() as stack:
+                stack.enter_context(patch.object(messages, "error"))
+                application.status = DomainApplication.ApplicationStatus.REJECTED
+
+                self.admin.save_model(request, application, None, True)
+
+                messages.error.assert_called_once_with(
+                    request,
+                    "A rejection reason is required.",
+                )
+
+            application.refresh_from_db()
+            self.assertEqual(application.status, DomainApplication.ApplicationStatus.APPROVED)
+
+    def test_transition_to_rejected_with_rejection_reason_does_not_trigger_error(self):
+        """
+        When transitioning to rejected with a rejection reason, admin does not throw an error alert.
+
+        The transition is successful.
+        """
+
+        with less_console_noise():
+            application = completed_application(status=DomainApplication.ApplicationStatus.APPROVED)
+
+            # Create a request object with a superuser
+            request = self.factory.post("/admin/registrar/domainapplication/{}/change/".format(application.pk))
+            request.user = self.superuser
+
+            with ExitStack() as stack:
+                stack.enter_context(patch.object(messages, "error"))
+                application.status = DomainApplication.ApplicationStatus.REJECTED
+                application.rejection_reason = DomainApplication.RejectionReasons.CONTACTS_OR_ORGANIZATION_LEGITIMACY
+
+                self.admin.save_model(request, application, None, True)
+
+                messages.error.assert_not_called()
+
+            application.refresh_from_db()
+            self.assertEqual(application.status, DomainApplication.ApplicationStatus.REJECTED)
 
     def test_save_model_sends_withdrawn_email(self):
         """When transitioning to withdrawn on a domain request,
         an email is sent out every time."""
 
-        # Ensure there is no user with this email
-        EMAIL = "mayor@igorville.gov"
-        User.objects.filter(email=EMAIL).delete()
+        with less_console_noise():
+            # Ensure there is no user with this email
+            EMAIL = "mayor@igorville.gov"
+            User.objects.filter(email=EMAIL).delete()
 
-        # Create a sample application
-        application = completed_application(status=DomainApplication.ApplicationStatus.IN_REVIEW)
+            # Create a sample application
+            application = completed_application(status=DomainApplication.ApplicationStatus.IN_REVIEW)
 
-        # Test Submitted Status
-        self.transition_state_and_send_email(application, DomainApplication.ApplicationStatus.WITHDRAWN)
-        self.assert_email_is_accurate(
-            "Your .gov domain request has been withdrawn and will not be reviewed by our team.", 0, EMAIL
-        )
-        self.assertEqual(len(self.mock_client.EMAILS_SENT), 1)
+            # Test Submitted Status
+            self.transition_state_and_send_email(application, DomainApplication.ApplicationStatus.WITHDRAWN)
+            self.assert_email_is_accurate(
+                "Your .gov domain request has been withdrawn and will not be reviewed by our team.", 0, EMAIL
+            )
+            self.assertEqual(len(self.mock_client.EMAILS_SENT), 1)
 
-        # Test Withdrawn Status
-        self.transition_state_and_send_email(application, DomainApplication.ApplicationStatus.SUBMITTED)
-        self.assert_email_is_accurate("We received your .gov domain request.", 1, EMAIL)
-        self.assertEqual(len(self.mock_client.EMAILS_SENT), 2)
+            # Test Withdrawn Status
+            self.transition_state_and_send_email(application, DomainApplication.ApplicationStatus.SUBMITTED)
+            self.assert_email_is_accurate("We received your .gov domain request.", 1, EMAIL)
+            self.assertEqual(len(self.mock_client.EMAILS_SENT), 2)
 
-        # Test Submitted Status Again (No new email should be sent)
-        self.transition_state_and_send_email(application, DomainApplication.ApplicationStatus.WITHDRAWN)
-        self.assertEqual(len(self.mock_client.EMAILS_SENT), 3)
+            # Test Submitted Status Again (No new email should be sent)
+            self.transition_state_and_send_email(application, DomainApplication.ApplicationStatus.WITHDRAWN)
+            self.assertEqual(len(self.mock_client.EMAILS_SENT), 3)
 
     def test_save_model_sets_approved_domain(self):
         # make sure there is no user with this email
-        EMAIL = "mayor@igorville.gov"
-        User.objects.filter(email=EMAIL).delete()
+        with less_console_noise():
+            EMAIL = "mayor@igorville.gov"
+            User.objects.filter(email=EMAIL).delete()
 
-        # Create a sample application
-        application = completed_application(status=DomainApplication.ApplicationStatus.IN_REVIEW)
+            # 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))
 
-        with boto3_mocking.clients.handler_for("sesv2", self.mock_client):
-            with less_console_noise():
-                # Modify the application's property
-                application.status = DomainApplication.ApplicationStatus.APPROVED
+            with boto3_mocking.clients.handler_for("sesv2", self.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)
+            # Test that approved domain exists and equals requested domain
+            self.assertEqual(application.requested_domain.name, application.approved_domain.name)
 
     def test_save_model_sets_restricted_status_on_user(self):
-        # make sure there is no user with this email
-        EMAIL = "mayor@igorville.gov"
-        User.objects.filter(email=EMAIL).delete()
+        with less_console_noise():
+            # make sure there is no user with this email
+            EMAIL = "mayor@igorville.gov"
+            User.objects.filter(email=EMAIL).delete()
 
-        # Create a sample application
-        application = completed_application(status=DomainApplication.ApplicationStatus.IN_REVIEW)
+            # 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))
 
-        with boto3_mocking.clients.handler_for("sesv2", self.mock_client):
-            with less_console_noise():
+            with boto3_mocking.clients.handler_for("sesv2", self.mock_client):
                 # 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)
 
-        # Test that approved domain exists and equals requested domain
-        self.assertEqual(application.creator.status, "restricted")
+            # Test that approved domain exists and equals requested domain
+            self.assertEqual(application.creator.status, "restricted")
 
     def test_readonly_when_restricted_creator(self):
-        application = completed_application(status=DomainApplication.ApplicationStatus.IN_REVIEW)
-        with boto3_mocking.clients.handler_for("sesv2", self.mock_client):
-            with less_console_noise():
+        with less_console_noise():
+            application = completed_application(status=DomainApplication.ApplicationStatus.IN_REVIEW)
+            with boto3_mocking.clients.handler_for("sesv2", self.mock_client):
                 application.creator.status = User.RESTRICTED
                 application.creator.save()
 
-        request = self.factory.get("/")
-        request.user = self.superuser
-
-        readonly_fields = self.admin.get_readonly_fields(request, application)
-
-        expected_fields = [
-            "id",
-            "created_at",
-            "updated_at",
-            "status",
-            "creator",
-            "investigator",
-            "organization_type",
-            "federally_recognized_tribe",
-            "state_recognized_tribe",
-            "tribe_name",
-            "federal_agency",
-            "federal_type",
-            "is_election_board",
-            "organization_name",
-            "address_line1",
-            "address_line2",
-            "city",
-            "state_territory",
-            "zipcode",
-            "urbanization",
-            "about_your_organization",
-            "authorizing_official",
-            "approved_domain",
-            "requested_domain",
-            "submitter",
-            "purpose",
-            "no_other_contacts_rationale",
-            "anything_else",
-            "is_policy_acknowledged",
-            "submission_date",
-            "notes",
-            "current_websites",
-            "other_contacts",
-            "alternative_domains",
-        ]
-
-        self.assertEqual(readonly_fields, expected_fields)
+            request = self.factory.get("/")
+            request.user = self.superuser
+
+            readonly_fields = self.admin.get_readonly_fields(request, application)
+
+            expected_fields = [
+                "id",
+                "created_at",
+                "updated_at",
+                "status",
+                "rejection_reason",
+                "creator",
+                "investigator",
+                "organization_type",
+                "federally_recognized_tribe",
+                "state_recognized_tribe",
+                "tribe_name",
+                "federal_agency",
+                "federal_type",
+                "is_election_board",
+                "organization_name",
+                "address_line1",
+                "address_line2",
+                "city",
+                "state_territory",
+                "zipcode",
+                "urbanization",
+                "about_your_organization",
+                "authorizing_official",
+                "approved_domain",
+                "requested_domain",
+                "submitter",
+                "purpose",
+                "no_other_contacts_rationale",
+                "anything_else",
+                "is_policy_acknowledged",
+                "submission_date",
+                "notes",
+                "current_websites",
+                "other_contacts",
+                "alternative_domains",
+            ]
+
+            self.assertEqual(readonly_fields, expected_fields)
 
     def test_readonly_fields_for_analyst(self):
-        request = self.factory.get("/")  # Use the correct method and path
-        request.user = self.staffuser
-
-        readonly_fields = self.admin.get_readonly_fields(request)
-
-        expected_fields = [
-            "creator",
-            "about_your_organization",
-            "requested_domain",
-            "approved_domain",
-            "alternative_domains",
-            "purpose",
-            "submitter",
-            "no_other_contacts_rationale",
-            "anything_else",
-            "is_policy_acknowledged",
-        ]
-
-        self.assertEqual(readonly_fields, expected_fields)
+        with less_console_noise():
+            request = self.factory.get("/")  # Use the correct method and path
+            request.user = self.staffuser
+
+            readonly_fields = self.admin.get_readonly_fields(request)
+
+            expected_fields = [
+                "creator",
+                "about_your_organization",
+                "requested_domain",
+                "approved_domain",
+                "alternative_domains",
+                "purpose",
+                "submitter",
+                "no_other_contacts_rationale",
+                "anything_else",
+                "is_policy_acknowledged",
+            ]
+
+            self.assertEqual(readonly_fields, expected_fields)
 
     def test_readonly_fields_for_superuser(self):
-        request = self.factory.get("/")  # Use the correct method and path
-        request.user = self.superuser
+        with less_console_noise():
+            request = self.factory.get("/")  # Use the correct method and path
+            request.user = self.superuser
 
-        readonly_fields = self.admin.get_readonly_fields(request)
+            readonly_fields = self.admin.get_readonly_fields(request)
 
-        expected_fields = []
+            expected_fields = []
 
-        self.assertEqual(readonly_fields, expected_fields)
+            self.assertEqual(readonly_fields, expected_fields)
 
     def test_saving_when_restricted_creator(self):
-        # Create an instance of the model
-        application = completed_application(status=DomainApplication.ApplicationStatus.IN_REVIEW)
-        with boto3_mocking.clients.handler_for("sesv2", self.mock_client):
-            with less_console_noise():
+        with less_console_noise():
+            # Create an instance of the model
+            application = completed_application(status=DomainApplication.ApplicationStatus.IN_REVIEW)
+            with boto3_mocking.clients.handler_for("sesv2", self.mock_client):
                 application.creator.status = User.RESTRICTED
                 application.creator.save()
 
-        # Create a request object with a superuser
-        request = self.factory.get("/")
-        request.user = self.superuser
+            # Create a request object with a superuser
+            request = self.factory.get("/")
+            request.user = self.superuser
 
-        with patch("django.contrib.messages.error") as mock_error:
-            # Simulate saving the model
-            self.admin.save_model(request, application, None, False)
+            with patch("django.contrib.messages.error") as mock_error:
+                # Simulate saving the model
+                self.admin.save_model(request, application, None, False)
 
-            # Assert that the error message was called with the correct argument
-            mock_error.assert_called_once_with(
-                request,
-                "This action is not permitted for applications with a restricted creator.",
-            )
+                # Assert that the error message was called with the correct argument
+                mock_error.assert_called_once_with(
+                    request,
+                    "This action is not permitted for applications with a restricted creator.",
+                )
 
-        # Assert that the status has not changed
-        self.assertEqual(application.status, DomainApplication.ApplicationStatus.IN_REVIEW)
+            # Assert that the status has not changed
+            self.assertEqual(application.status, DomainApplication.ApplicationStatus.IN_REVIEW)
 
     def test_change_view_with_restricted_creator(self):
-        # Create an instance of the model
-        application = completed_application(status=DomainApplication.ApplicationStatus.IN_REVIEW)
-        with boto3_mocking.clients.handler_for("sesv2", self.mock_client):
-            with less_console_noise():
+        with less_console_noise():
+            # Create an instance of the model
+            application = completed_application(status=DomainApplication.ApplicationStatus.IN_REVIEW)
+            with boto3_mocking.clients.handler_for("sesv2", self.mock_client):
                 application.creator.status = User.RESTRICTED
                 application.creator.save()
 
-        with patch("django.contrib.messages.warning") as mock_warning:
-            # Create a request object with a superuser
-            request = self.factory.get("/admin/your_app/domainapplication/{}/change/".format(application.pk))
-            request.user = self.superuser
+            with patch("django.contrib.messages.warning") as mock_warning:
+                # Create a request object with a superuser
+                request = self.factory.get("/admin/your_app/domainapplication/{}/change/".format(application.pk))
+                request.user = self.superuser
 
-            self.admin.display_restricted_warning(request, application)
+                self.admin.display_restricted_warning(request, application)
 
-            # Assert that the error message was called with the correct argument
-            mock_warning.assert_called_once_with(
-                request,
-                "Cannot edit an application with a restricted creator.",
-            )
+                # Assert that the error message was called with the correct argument
+                mock_warning.assert_called_once_with(
+                    request,
+                    "Cannot edit an application with a restricted creator.",
+                )
 
-    def trigger_saving_approved_to_another_state(self, domain_is_active, another_state):
+    def trigger_saving_approved_to_another_state(self, domain_is_active, another_state, rejection_reason=None):
         """Helper method that triggers domain request state changes from approved to another state,
         with an associated domain that can be either active (READY) or not.
 
         Used to test errors when saving a change with an active domain, also used to test side effects
         when saving a change goes through."""
 
-        # Create an instance of the model
-        application = completed_application(status=DomainApplication.ApplicationStatus.APPROVED)
-        domain = Domain.objects.create(name=application.requested_domain.name)
-        domain_information = DomainInformation.objects.create(creator=self.superuser, domain=domain)
-        application.approved_domain = domain
-        application.save()
+        with less_console_noise():
+            # Create an instance of the model
+            application = completed_application(status=DomainApplication.ApplicationStatus.APPROVED)
+            domain = Domain.objects.create(name=application.requested_domain.name)
+            domain_information = DomainInformation.objects.create(creator=self.superuser, domain=domain)
+            application.approved_domain = domain
+            application.save()
+
+            # Create a request object with a superuser
+            request = self.factory.post("/admin/registrar/domainapplication/{}/change/".format(application.pk))
+            request.user = self.superuser
 
-        # Create a request object with a superuser
-        request = self.factory.post("/admin/registrar/domainapplication/{}/change/".format(application.pk))
-        request.user = self.superuser
+            # Define a custom implementation for is_active
+            def custom_is_active(self):
+                return domain_is_active  # Override to return True
 
-        # Define a custom implementation for is_active
-        def custom_is_active(self):
-            return domain_is_active  # Override to return True
+            # 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(Domain, "is_active", custom_is_active))
+                stack.enter_context(patch.object(messages, "error"))
 
-        # 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(Domain, "is_active", custom_is_active))
-            stack.enter_context(patch.object(messages, "error"))
+                application.status = another_state
+                application.rejection_reason = rejection_reason
 
-            application.status = another_state
-            self.admin.save_model(request, application, None, True)
+                self.admin.save_model(request, application, None, True)
 
-            # Assert that the error message was called with the correct argument
-            if domain_is_active:
-                messages.error.assert_called_once_with(
-                    request,
-                    "This action is not permitted. The domain " + "is already active.",
-                )
-            else:
-                # Assert that the error message was never called
-                messages.error.assert_not_called()
+                # Assert that the error message was called with the correct argument
+                if domain_is_active:
+                    messages.error.assert_called_once_with(
+                        request,
+                        "This action is not permitted. The domain " + "is already active.",
+                    )
+                else:
+                    # Assert that the error message was never called
+                    messages.error.assert_not_called()
 
-                self.assertEqual(application.approved_domain, None)
+                    self.assertEqual(application.approved_domain, None)
 
-                # Assert that Domain got Deleted
-                with self.assertRaises(Domain.DoesNotExist):
-                    domain.refresh_from_db()
+                    # Assert that Domain got Deleted
+                    with self.assertRaises(Domain.DoesNotExist):
+                        domain.refresh_from_db()
 
-                # Assert that DomainInformation got Deleted
-                with self.assertRaises(DomainInformation.DoesNotExist):
-                    domain_information.refresh_from_db()
+                    # Assert that DomainInformation got Deleted
+                    with self.assertRaises(DomainInformation.DoesNotExist):
+                        domain_information.refresh_from_db()
 
     def test_error_when_saving_approved_to_in_review_and_domain_is_active(self):
         self.trigger_saving_approved_to_another_state(True, DomainApplication.ApplicationStatus.IN_REVIEW)
@@ -1062,7 +1318,11 @@ def test_side_effects_when_saving_approved_to_action_needed(self):
         self.trigger_saving_approved_to_another_state(False, DomainApplication.ApplicationStatus.ACTION_NEEDED)
 
     def test_side_effects_when_saving_approved_to_rejected(self):
-        self.trigger_saving_approved_to_another_state(False, DomainApplication.ApplicationStatus.REJECTED)
+        self.trigger_saving_approved_to_another_state(
+            False,
+            DomainApplication.ApplicationStatus.REJECTED,
+            DomainApplication.RejectionReasons.CONTACTS_OR_ORGANIZATION_LEGITIMACY,
+        )
 
     def test_side_effects_when_saving_approved_to_ineligible(self):
         self.trigger_saving_approved_to_another_state(False, DomainApplication.ApplicationStatus.INELIGIBLE)
@@ -1074,14 +1334,20 @@ def test_has_correct_filters(self):
         It retrieves the current list of filters from DomainApplicationAdmin
         and checks that it matches the expected list of filters.
         """
-        request = self.factory.get("/")
-        request.user = self.superuser
+        with less_console_noise():
+            request = self.factory.get("/")
+            request.user = self.superuser
 
-        # Grab the current list of table filters
-        readonly_fields = self.admin.get_list_filter(request)
-        expected_fields = ("status", "organization_type", DomainApplicationAdmin.InvestigatorFilter)
+            # Grab the current list of table filters
+            readonly_fields = self.admin.get_list_filter(request)
+            expected_fields = (
+                "status",
+                "organization_type",
+                "rejection_reason",
+                DomainApplicationAdmin.InvestigatorFilter,
+            )
 
-        self.assertEqual(readonly_fields, expected_fields)
+            self.assertEqual(readonly_fields, expected_fields)
 
     def test_table_sorted_alphabetically(self):
         """
@@ -1093,23 +1359,24 @@ def test_table_sorted_alphabetically(self):
         that it matches the expected queryset,
         which is sorted alphabetically by the 'requested_domain__name' field.
         """
-        # Creates a list of DomainApplications in scrambled order
-        multiple_unalphabetical_domain_objects("application")
+        with less_console_noise():
+            # Creates a list of DomainApplications in scrambled order
+            multiple_unalphabetical_domain_objects("application")
 
-        request = self.factory.get("/")
-        request.user = self.superuser
+            request = self.factory.get("/")
+            request.user = self.superuser
 
-        # Get the expected list of alphabetically sorted DomainApplications
-        expected_order = DomainApplication.objects.order_by("requested_domain__name")
+            # Get the expected list of alphabetically sorted DomainApplications
+            expected_order = DomainApplication.objects.order_by("requested_domain__name")
 
-        # Get the returned queryset
-        queryset = self.admin.get_queryset(request)
+            # Get the returned queryset
+            queryset = self.admin.get_queryset(request)
 
-        # Check the order
-        self.assertEqual(
-            list(queryset),
-            list(expected_order),
-        )
+            # Check the order
+            self.assertEqual(
+                list(queryset),
+                list(expected_order),
+            )
 
     def test_displays_investigator_filter(self):
         """
@@ -1125,37 +1392,38 @@ def test_displays_investigator_filter(self):
         the filter displays correctly, when the filter isn't filtering correctly.
         """
 
-        # Create a mock DomainApplication object, with a fake investigator
-        application: DomainApplication = generic_domain_object("application", "SomeGuy")
-        investigator_user = User.objects.filter(username=application.investigator.username).get()
-        investigator_user.is_staff = True
-        investigator_user.save()
+        with less_console_noise():
+            # Create a mock DomainApplication object, with a fake investigator
+            application: DomainApplication = generic_domain_object("application", "SomeGuy")
+            investigator_user = User.objects.filter(username=application.investigator.username).get()
+            investigator_user.is_staff = True
+            investigator_user.save()
 
-        p = "userpass"
-        self.client.login(username="staffuser", password=p)
-        response = self.client.get(
-            "/admin/registrar/domainapplication/",
-            {
-                "investigator__id__exact": investigator_user.id,
-            },
-            follow=True,
-        )
+            p = "userpass"
+            self.client.login(username="staffuser", password=p)
+            response = self.client.get(
+                "/admin/registrar/domainapplication/",
+                {
+                    "investigator__id__exact": investigator_user.id,
+                },
+                follow=True,
+            )
 
-        # Then, test if the filter actually exists
-        self.assertIn("filters", response.context)
+            # Then, test if the filter actually exists
+            self.assertIn("filters", response.context)
 
-        # Assert the content of filters and search_query
-        filters = response.context["filters"]
+            # Assert the content of filters and search_query
+            filters = response.context["filters"]
 
-        self.assertEqual(
-            filters,
-            [
-                {
-                    "parameter_name": "investigator",
-                    "parameter_value": "SomeGuy first_name:investigator SomeGuy last_name:investigator",
-                },
-            ],
-        )
+            self.assertEqual(
+                filters,
+                [
+                    {
+                        "parameter_name": "investigator",
+                        "parameter_value": "SomeGuy first_name:investigator SomeGuy last_name:investigator",
+                    },
+                ],
+            )
 
     def test_investigator_dropdown_displays_only_staff(self):
         """
@@ -1168,83 +1436,86 @@ def test_investigator_dropdown_displays_only_staff(self):
         It then retrieves the queryset for the 'investigator' dropdown from DomainApplicationAdmin
         and checks that it matches the expected queryset, which only includes staff users.
         """
-        # Create a mock DomainApplication object, with a fake investigator
-        application: DomainApplication = generic_domain_object("application", "SomeGuy")
-        investigator_user = User.objects.filter(username=application.investigator.username).get()
-        investigator_user.is_staff = True
-        investigator_user.save()
-
-        # Create a mock DomainApplication object, with a user that is not staff
-        application_2: DomainApplication = generic_domain_object("application", "SomeOtherGuy")
-        investigator_user_2 = User.objects.filter(username=application_2.investigator.username).get()
-        investigator_user_2.is_staff = False
-        investigator_user_2.save()
 
-        p = "userpass"
-        self.client.login(username="staffuser", password=p)
+        with less_console_noise():
+            # Create a mock DomainApplication object, with a fake investigator
+            application: DomainApplication = generic_domain_object("application", "SomeGuy")
+            investigator_user = User.objects.filter(username=application.investigator.username).get()
+            investigator_user.is_staff = True
+            investigator_user.save()
+
+            # Create a mock DomainApplication object, with a user that is not staff
+            application_2: DomainApplication = generic_domain_object("application", "SomeOtherGuy")
+            investigator_user_2 = User.objects.filter(username=application_2.investigator.username).get()
+            investigator_user_2.is_staff = False
+            investigator_user_2.save()
 
-        request = self.factory.post("/admin/registrar/domainapplication/{}/change/".format(application.pk))
+            p = "userpass"
+            self.client.login(username="staffuser", password=p)
 
-        # Get the actual field from the model's meta information
-        investigator_field = DomainApplication._meta.get_field("investigator")
+            request = self.factory.post("/admin/registrar/domainapplication/{}/change/".format(application.pk))
 
-        # We should only be displaying staff users, in alphabetical order
-        sorted_fields = ["first_name", "last_name", "email"]
-        expected_dropdown = list(User.objects.filter(is_staff=True).order_by(*sorted_fields))
+            # Get the actual field from the model's meta information
+            investigator_field = DomainApplication._meta.get_field("investigator")
 
-        # Grab the current dropdown. We do an API call to autocomplete to get this info.
-        application_queryset = self.admin.formfield_for_foreignkey(investigator_field, request).queryset
-        user_request = self.factory.post(
-            "/admin/autocomplete/?app_label=registrar&model_name=domainapplication&field_name=investigator"
-        )
-        user_admin = MyUserAdmin(User, self.site)
-        user_queryset = user_admin.get_search_results(user_request, application_queryset, None)[0]
-        current_dropdown = list(user_queryset)
+            # We should only be displaying staff users, in alphabetical order
+            sorted_fields = ["first_name", "last_name", "email"]
+            expected_dropdown = list(User.objects.filter(is_staff=True).order_by(*sorted_fields))
+
+            # Grab the current dropdown. We do an API call to autocomplete to get this info.
+            application_queryset = self.admin.formfield_for_foreignkey(investigator_field, request).queryset
+            user_request = self.factory.post(
+                "/admin/autocomplete/?app_label=registrar&model_name=domainapplication&field_name=investigator"
+            )
+            user_admin = MyUserAdmin(User, self.site)
+            user_queryset = user_admin.get_search_results(user_request, application_queryset, None)[0]
+            current_dropdown = list(user_queryset)
 
-        self.assertEqual(expected_dropdown, current_dropdown)
+            self.assertEqual(expected_dropdown, current_dropdown)
 
-        # Non staff users should not be in the list
-        self.assertNotIn(application_2, current_dropdown)
+            # Non staff users should not be in the list
+            self.assertNotIn(application_2, current_dropdown)
 
     def test_investigator_list_is_alphabetically_sorted(self):
         """
         This test verifies that filter list for the 'investigator'
         is displayed alphabetically
         """
-        # Create a mock DomainApplication object, with a fake investigator
-        application: DomainApplication = generic_domain_object("application", "SomeGuy")
-        investigator_user = User.objects.filter(username=application.investigator.username).get()
-        investigator_user.is_staff = True
-        investigator_user.save()
-
-        application_2: DomainApplication = generic_domain_object("application", "AGuy")
-        investigator_user_2 = User.objects.filter(username=application_2.investigator.username).get()
-        investigator_user_2.first_name = "AGuy"
-        investigator_user_2.is_staff = True
-        investigator_user_2.save()
-
-        application_3: DomainApplication = generic_domain_object("application", "FinalGuy")
-        investigator_user_3 = User.objects.filter(username=application_3.investigator.username).get()
-        investigator_user_3.first_name = "FinalGuy"
-        investigator_user_3.is_staff = True
-        investigator_user_3.save()
+        with less_console_noise():
+            # Create a mock DomainApplication object, with a fake investigator
+            application: DomainApplication = generic_domain_object("application", "SomeGuy")
+            investigator_user = User.objects.filter(username=application.investigator.username).get()
+            investigator_user.is_staff = True
+            investigator_user.save()
+
+            application_2: DomainApplication = generic_domain_object("application", "AGuy")
+            investigator_user_2 = User.objects.filter(username=application_2.investigator.username).get()
+            investigator_user_2.first_name = "AGuy"
+            investigator_user_2.is_staff = True
+            investigator_user_2.save()
+
+            application_3: DomainApplication = generic_domain_object("application", "FinalGuy")
+            investigator_user_3 = User.objects.filter(username=application_3.investigator.username).get()
+            investigator_user_3.first_name = "FinalGuy"
+            investigator_user_3.is_staff = True
+            investigator_user_3.save()
 
-        p = "userpass"
-        self.client.login(username="staffuser", password=p)
-        request = RequestFactory().get("/")
+            p = "userpass"
+            self.client.login(username="staffuser", password=p)
+            request = RequestFactory().get("/")
 
-        # These names have metadata embedded in them. :investigator implicitly tests if
-        # these are actually from the attribute "investigator".
-        expected_list = [
-            "AGuy AGuy last_name:investigator",
-            "FinalGuy FinalGuy last_name:investigator",
-            "SomeGuy first_name:investigator SomeGuy last_name:investigator",
-        ]
+            # These names have metadata embedded in them. :investigator implicitly tests if
+            # these are actually from the attribute "investigator".
+            expected_list = [
+                "AGuy AGuy last_name:investigator",
+                "FinalGuy FinalGuy last_name:investigator",
+                "SomeGuy first_name:investigator SomeGuy last_name:investigator",
+            ]
 
-        # Get the actual sorted list of investigators from the lookups method
-        actual_list = [item for _, item in self.admin.InvestigatorFilter.lookups(self, request, self.admin)]
+            # Get the actual sorted list of investigators from the lookups method
+            actual_list = [item for _, item in self.admin.InvestigatorFilter.lookups(self, request, self.admin)]
 
-        self.assertEqual(expected_list, actual_list)
+            self.assertEqual(expected_list, actual_list)
 
     def tearDown(self):
         super().tearDown()
@@ -1273,28 +1544,29 @@ def tearDown(self):
 
     def test_get_filters(self):
         """Ensures that our filters are displaying correctly"""
-        # Have to get creative to get past linter
-        p = "adminpass"
-        self.client.login(username="superuser", password=p)
+        with less_console_noise():
+            # Have to get creative to get past linter
+            p = "adminpass"
+            self.client.login(username="superuser", password=p)
 
-        response = self.client.get(
-            "/admin/registrar/domaininvitation/",
-            {},
-            follow=True,
-        )
+            response = self.client.get(
+                "/admin/registrar/domaininvitation/",
+                {},
+                follow=True,
+            )
 
-        # Assert that the filters are added
-        self.assertContains(response, "invited", count=2)
-        self.assertContains(response, "Invited", count=2)
-        self.assertContains(response, "retrieved", count=2)
-        self.assertContains(response, "Retrieved", count=2)
+            # Assert that the filters are added
+            self.assertContains(response, "invited", count=2)
+            self.assertContains(response, "Invited", count=2)
+            self.assertContains(response, "retrieved", count=2)
+            self.assertContains(response, "Retrieved", count=2)
 
-        # Check for the HTML context specificially
-        invited_html = '<a href="?status__exact=invited">Invited</a>'
-        retrieved_html = '<a href="?status__exact=retrieved">Retrieved</a>'
+            # Check for the HTML context specificially
+            invited_html = '<a href="?status__exact=invited">Invited</a>'
+            retrieved_html = '<a href="?status__exact=retrieved">Retrieved</a>'
 
-        self.assertContains(response, invited_html, count=1)
-        self.assertContains(response, retrieved_html, count=1)
+            self.assertContains(response, invited_html, count=1)
+            self.assertContains(response, retrieved_html, count=1)
 
 
 class TestDomainInformationAdmin(TestCase):
@@ -1350,50 +1622,53 @@ def tearDown(self):
         User.objects.all().delete()
 
     def test_readonly_fields_for_analyst(self):
-        """Ensures that analysts have their permissions setup correctly"""
-        request = self.factory.get("/")
-        request.user = self.staffuser
-
-        readonly_fields = self.admin.get_readonly_fields(request)
-
-        expected_fields = [
-            "creator",
-            "type_of_work",
-            "more_organization_information",
-            "domain",
-            "domain_application",
-            "submitter",
-            "no_other_contacts_rationale",
-            "anything_else",
-            "is_policy_acknowledged",
-        ]
-
-        self.assertEqual(readonly_fields, expected_fields)
+        with less_console_noise():
+            """Ensures that analysts have their permissions setup correctly"""
+            request = self.factory.get("/")
+            request.user = self.staffuser
+
+            readonly_fields = self.admin.get_readonly_fields(request)
+
+            expected_fields = [
+                "creator",
+                "type_of_work",
+                "more_organization_information",
+                "domain",
+                "domain_application",
+                "submitter",
+                "no_other_contacts_rationale",
+                "anything_else",
+                "is_policy_acknowledged",
+            ]
+
+            self.assertEqual(readonly_fields, expected_fields)
 
     def test_domain_sortable(self):
         """Tests if DomainInformation sorts by domain correctly"""
-        p = "adminpass"
-        self.client.login(username="superuser", password=p)
+        with less_console_noise():
+            p = "adminpass"
+            self.client.login(username="superuser", password=p)
 
-        # Assert that our sort works correctly
-        self.test_helper.assert_table_sorted("1", ("domain__name",))
+            # Assert that our sort works correctly
+            self.test_helper.assert_table_sorted("1", ("domain__name",))
 
-        # Assert that sorting in reverse works correctly
-        self.test_helper.assert_table_sorted("-1", ("-domain__name",))
+            # Assert that sorting in reverse works correctly
+            self.test_helper.assert_table_sorted("-1", ("-domain__name",))
 
     def test_submitter_sortable(self):
         """Tests if DomainInformation sorts by submitter correctly"""
-        p = "adminpass"
-        self.client.login(username="superuser", password=p)
+        with less_console_noise():
+            p = "adminpass"
+            self.client.login(username="superuser", password=p)
 
-        # Assert that our sort works correctly
-        self.test_helper.assert_table_sorted(
-            "4",
-            ("submitter__first_name", "submitter__last_name"),
-        )
+            # Assert that our sort works correctly
+            self.test_helper.assert_table_sorted(
+                "4",
+                ("submitter__first_name", "submitter__last_name"),
+            )
 
-        # Assert that sorting in reverse works correctly
-        self.test_helper.assert_table_sorted("-4", ("-submitter__first_name", "-submitter__last_name"))
+            # Assert that sorting in reverse works correctly
+            self.test_helper.assert_table_sorted("-4", ("-submitter__first_name", "-submitter__last_name"))
 
 
 class UserDomainRoleAdminTest(TestCase):
@@ -1420,109 +1695,113 @@ def tearDown(self):
 
     def test_domain_sortable(self):
         """Tests if the UserDomainrole sorts by domain correctly"""
-        p = "adminpass"
-        self.client.login(username="superuser", password=p)
+        with less_console_noise():
+            p = "adminpass"
+            self.client.login(username="superuser", password=p)
 
-        fake_user = User.objects.create(
-            username="dummyuser", first_name="Stewart", last_name="Jones", email="AntarcticPolarBears@example.com"
-        )
+            fake_user = User.objects.create(
+                username="dummyuser", first_name="Stewart", last_name="Jones", email="AntarcticPolarBears@example.com"
+            )
 
-        # Create a list of UserDomainRoles that are in random order
-        mocks_to_create = ["jkl.gov", "ghi.gov", "abc.gov", "def.gov"]
-        for name in mocks_to_create:
-            fake_domain = Domain.objects.create(name=name)
-            UserDomainRole.objects.create(user=fake_user, domain=fake_domain, role="manager")
+            # Create a list of UserDomainRoles that are in random order
+            mocks_to_create = ["jkl.gov", "ghi.gov", "abc.gov", "def.gov"]
+            for name in mocks_to_create:
+                fake_domain = Domain.objects.create(name=name)
+                UserDomainRole.objects.create(user=fake_user, domain=fake_domain, role="manager")
 
-        # Assert that our sort works correctly
-        self.test_helper.assert_table_sorted("2", ("domain__name",))
+            # Assert that our sort works correctly
+            self.test_helper.assert_table_sorted("2", ("domain__name",))
 
-        # Assert that sorting in reverse works correctly
-        self.test_helper.assert_table_sorted("-2", ("-domain__name",))
+            # Assert that sorting in reverse works correctly
+            self.test_helper.assert_table_sorted("-2", ("-domain__name",))
 
     def test_user_sortable(self):
         """Tests if the UserDomainrole sorts by user correctly"""
-        p = "adminpass"
-        self.client.login(username="superuser", password=p)
+        with less_console_noise():
+            p = "adminpass"
+            self.client.login(username="superuser", password=p)
 
-        mock_data_generator = AuditedAdminMockData()
+            mock_data_generator = AuditedAdminMockData()
 
-        fake_domain = Domain.objects.create(name="igorville.gov")
-        # Create a list of UserDomainRoles that are in random order
-        mocks_to_create = ["jkl", "ghi", "abc", "def"]
-        for name in mocks_to_create:
-            # Creates a fake "User" object
-            fake_user = mock_data_generator.dummy_user(name, "user")
-            UserDomainRole.objects.create(user=fake_user, domain=fake_domain, role="manager")
+            fake_domain = Domain.objects.create(name="igorville.gov")
+            # Create a list of UserDomainRoles that are in random order
+            mocks_to_create = ["jkl", "ghi", "abc", "def"]
+            for name in mocks_to_create:
+                # Creates a fake "User" object
+                fake_user = mock_data_generator.dummy_user(name, "user")
+                UserDomainRole.objects.create(user=fake_user, domain=fake_domain, role="manager")
 
-        # Assert that our sort works correctly
-        self.test_helper.assert_table_sorted("1", ("user__first_name", "user__last_name"))
+            # Assert that our sort works correctly
+            self.test_helper.assert_table_sorted("1", ("user__first_name", "user__last_name"))
 
-        # Assert that sorting in reverse works correctly
-        self.test_helper.assert_table_sorted("-1", ("-user__first_name", "-user__last_name"))
+            # Assert that sorting in reverse works correctly
+            self.test_helper.assert_table_sorted("-1", ("-user__first_name", "-user__last_name"))
 
     def test_email_not_in_search(self):
         """Tests the search bar in Django Admin for UserDomainRoleAdmin.
         Should return no results for an invalid email."""
-        # Have to get creative to get past linter
-        p = "adminpass"
-        self.client.login(username="superuser", password=p)
+        with less_console_noise():
+            # Have to get creative to get past linter
+            p = "adminpass"
+            self.client.login(username="superuser", password=p)
 
-        fake_user = User.objects.create(
-            username="dummyuser", first_name="Stewart", last_name="Jones", email="AntarcticPolarBears@example.com"
-        )
-        fake_domain = Domain.objects.create(name="test123")
-        UserDomainRole.objects.create(user=fake_user, domain=fake_domain, role="manager")
-        # Make the request using the Client class
-        # which handles CSRF
-        # Follow=True handles the redirect
-        response = self.client.get(
-            "/admin/registrar/userdomainrole/",
-            {
-                "q": "testmail@igorville.com",
-            },
-            follow=True,
-        )
+            fake_user = User.objects.create(
+                username="dummyuser", first_name="Stewart", last_name="Jones", email="AntarcticPolarBears@example.com"
+            )
+            fake_domain = Domain.objects.create(name="test123")
+            UserDomainRole.objects.create(user=fake_user, domain=fake_domain, role="manager")
+            # Make the request using the Client class
+            # which handles CSRF
+            # Follow=True handles the redirect
+            response = self.client.get(
+                "/admin/registrar/userdomainrole/",
+                {
+                    "q": "testmail@igorville.com",
+                },
+                follow=True,
+            )
 
-        # Assert that the query is added to the extra_context
-        self.assertIn("search_query", response.context)
-        # Assert the content of filters and search_query
-        search_query = response.context["search_query"]
-        self.assertEqual(search_query, "testmail@igorville.com")
+            # Assert that the query is added to the extra_context
+            self.assertIn("search_query", response.context)
+            # Assert the content of filters and search_query
+            search_query = response.context["search_query"]
+            self.assertEqual(search_query, "testmail@igorville.com")
 
-        # We only need to check for the end of the HTML string
-        self.assertNotContains(response, "Stewart Jones AntarcticPolarBears@example.com</a></th>")
+            # We only need to check for the end of the HTML string
+            self.assertNotContains(response, "Stewart Jones AntarcticPolarBears@example.com</a></th>")
 
     def test_email_in_search(self):
         """Tests the search bar in Django Admin for UserDomainRoleAdmin.
         Should return results for an valid email."""
-        # Have to get creative to get past linter
-        p = "adminpass"
-        self.client.login(username="superuser", password=p)
+        with less_console_noise():
+            # Have to get creative to get past linter
+            p = "adminpass"
+            self.client.login(username="superuser", password=p)
 
-        fake_user = User.objects.create(
-            username="dummyuser", first_name="Joe", last_name="Jones", email="AntarcticPolarBears@example.com"
-        )
-        fake_domain = Domain.objects.create(name="fake")
-        UserDomainRole.objects.create(user=fake_user, domain=fake_domain, role="manager")
-        # Make the request using the Client class
-        # which handles CSRF
-        # Follow=True handles the redirect
-        response = self.client.get(
-            "/admin/registrar/userdomainrole/",
-            {
-                "q": "AntarcticPolarBears@example.com",
-            },
-            follow=True,
-        )
+            fake_user = User.objects.create(
+                username="dummyuser", first_name="Joe", last_name="Jones", email="AntarcticPolarBears@example.com"
+            )
+            fake_domain = Domain.objects.create(name="fake")
+            UserDomainRole.objects.create(user=fake_user, domain=fake_domain, role="manager")
+            # Make the request using the Client class
+            # which handles CSRF
+            # Follow=True handles the redirect
+            response = self.client.get(
+                "/admin/registrar/userdomainrole/",
+                {
+                    "q": "AntarcticPolarBears@example.com",
+                },
+                follow=True,
+            )
 
-        # Assert that the query is added to the extra_context
-        self.assertIn("search_query", response.context)
+            # Assert that the query is added to the extra_context
+            self.assertIn("search_query", response.context)
 
-        search_query = response.context["search_query"]
-        self.assertEqual(search_query, "AntarcticPolarBears@example.com")
+            search_query = response.context["search_query"]
+            self.assertEqual(search_query, "AntarcticPolarBears@example.com")
 
-        # We only need to check for the end of the HTML string
-        self.assertContains(response, "Joe Jones AntarcticPolarBears@example.com</a></th>", count=1)
+            # We only need to check for the end of the HTML string
+            self.assertContains(response, "Joe Jones AntarcticPolarBears@example.com</a></th>", count=1)
 
 
 class ListHeaderAdminTest(TestCase):
@@ -1604,39 +1883,42 @@ def setUp(self):
         self.admin = MyUserAdmin(model=get_user_model(), admin_site=admin_site)
 
     def test_list_display_without_username(self):
-        request = self.client.request().wsgi_request
-        request.user = create_user()
+        with less_console_noise():
+            request = self.client.request().wsgi_request
+            request.user = create_user()
 
-        list_display = self.admin.get_list_display(request)
-        expected_list_display = [
-            "email",
-            "first_name",
-            "last_name",
-            "group",
-            "status",
-        ]
+            list_display = self.admin.get_list_display(request)
+            expected_list_display = [
+                "email",
+                "first_name",
+                "last_name",
+                "group",
+                "status",
+            ]
 
-        self.assertEqual(list_display, expected_list_display)
-        self.assertNotIn("username", list_display)
+            self.assertEqual(list_display, expected_list_display)
+            self.assertNotIn("username", list_display)
 
     def test_get_fieldsets_superuser(self):
-        request = self.client.request().wsgi_request
-        request.user = create_superuser()
-        fieldsets = self.admin.get_fieldsets(request)
-        expected_fieldsets = super(MyUserAdmin, self.admin).get_fieldsets(request)
-        self.assertEqual(fieldsets, expected_fieldsets)
+        with less_console_noise():
+            request = self.client.request().wsgi_request
+            request.user = create_superuser()
+            fieldsets = self.admin.get_fieldsets(request)
+            expected_fieldsets = super(MyUserAdmin, self.admin).get_fieldsets(request)
+            self.assertEqual(fieldsets, expected_fieldsets)
 
     def test_get_fieldsets_cisa_analyst(self):
-        request = self.client.request().wsgi_request
-        request.user = create_user()
-        fieldsets = self.admin.get_fieldsets(request)
-        expected_fieldsets = (
-            (None, {"fields": ("password", "status")}),
-            ("Personal Info", {"fields": ("first_name", "last_name", "email")}),
-            ("Permissions", {"fields": ("is_active", "groups")}),
-            ("Important dates", {"fields": ("last_login", "date_joined")}),
-        )
-        self.assertEqual(fieldsets, expected_fieldsets)
+        with less_console_noise():
+            request = self.client.request().wsgi_request
+            request.user = create_user()
+            fieldsets = self.admin.get_fieldsets(request)
+            expected_fieldsets = (
+                (None, {"fields": ("password", "status")}),
+                ("Personal Info", {"fields": ("first_name", "last_name", "email")}),
+                ("Permissions", {"fields": ("is_active", "groups")}),
+                ("Important dates", {"fields": ("last_login", "date_joined")}),
+            )
+            self.assertEqual(fieldsets, expected_fieldsets)
 
     def tearDown(self):
         User.objects.all().delete()
@@ -1695,34 +1977,35 @@ def test_alphabetically_sorted_domain_application_investigator(self):
 
     # This test case should be refactored in general, as it is too overly specific and engineered
     def test_alphabetically_sorted_fk_fields_domain_application(self):
-        tested_fields = [
-            DomainApplication.authorizing_official.field,
-            DomainApplication.submitter.field,
-            # DomainApplication.investigator.field,
-            DomainApplication.creator.field,
-            DomainApplication.requested_domain.field,
-        ]
-
-        # Creates multiple domain applications - review status does not matter
-        applications = multiple_unalphabetical_domain_objects("application")
-
-        # Create a mock request
-        request = self.factory.post("/admin/registrar/domainapplication/{}/change/".format(applications[0].pk))
-
-        model_admin = AuditedAdmin(DomainApplication, self.site)
-
-        sorted_fields = []
-        # Typically we wouldn't want two nested for fields,
-        # but both fields are of a fixed length.
-        # For test case purposes, this should be performant.
-        for field in tested_fields:
-            with self.subTest(field=field):
-                isNamefield: bool = field == DomainApplication.requested_domain.field
-                if isNamefield:
-                    sorted_fields = ["name"]
-                else:
-                    sorted_fields = ["first_name", "last_name"]
-                # We want both of these to be lists, as it is richer test wise.
+        with less_console_noise():
+            tested_fields = [
+                DomainApplication.authorizing_official.field,
+                DomainApplication.submitter.field,
+                # DomainApplication.investigator.field,
+                DomainApplication.creator.field,
+                DomainApplication.requested_domain.field,
+            ]
+
+            # Creates multiple domain applications - review status does not matter
+            applications = multiple_unalphabetical_domain_objects("application")
+
+            # Create a mock request
+            request = self.factory.post("/admin/registrar/domainapplication/{}/change/".format(applications[0].pk))
+
+            model_admin = AuditedAdmin(DomainApplication, self.site)
+
+            sorted_fields = []
+            # Typically we wouldn't want two nested for fields,
+            # but both fields are of a fixed length.
+            # For test case purposes, this should be performant.
+            for field in tested_fields:
+                with self.subTest(field=field):
+                    isNamefield: bool = field == DomainApplication.requested_domain.field
+                    if isNamefield:
+                        sorted_fields = ["name"]
+                    else:
+                        sorted_fields = ["first_name", "last_name"]
+                    # We want both of these to be lists, as it is richer test wise.
 
                 desired_order = self.order_by_desired_field_helper(model_admin, request, field.name, *sorted_fields)
                 current_sort_order = list(model_admin.formfield_for_foreignkey(field, request).queryset)
@@ -1752,105 +2035,107 @@ def test_alphabetically_sorted_fk_fields_domain_application(self):
                 )
 
     def test_alphabetically_sorted_fk_fields_domain_information(self):
-        tested_fields = [
-            DomainInformation.authorizing_official.field,
-            DomainInformation.submitter.field,
-            # DomainInformation.creator.field,
-            (DomainInformation.domain.field, ["name"]),
-            (DomainInformation.domain_application.field, ["requested_domain__name"]),
-        ]
-        # Creates multiple domain applications - review status does not matter
-        applications = multiple_unalphabetical_domain_objects("information")
+        with less_console_noise():
+            tested_fields = [
+                DomainInformation.authorizing_official.field,
+                DomainInformation.submitter.field,
+                # DomainInformation.creator.field,
+                (DomainInformation.domain.field, ["name"]),
+                (DomainInformation.domain_application.field, ["requested_domain__name"]),
+            ]
+            # Creates multiple domain applications - review status does not matter
+            applications = multiple_unalphabetical_domain_objects("information")
+
+            # Create a mock request
+            request = self.factory.post("/admin/registrar/domaininformation/{}/change/".format(applications[0].pk))
+
+            model_admin = AuditedAdmin(DomainInformation, self.site)
+
+            sorted_fields = []
+            # Typically we wouldn't want two nested for fields,
+            # but both fields are of a fixed length.
+            # For test case purposes, this should be performant.
+            for field in tested_fields:
+                isOtherOrderfield: bool = isinstance(field, tuple)
+                field_obj = None
+                if isOtherOrderfield:
+                    sorted_fields = field[1]
+                    field_obj = field[0]
+                else:
+                    sorted_fields = ["first_name", "last_name"]
+                    field_obj = field
+                # We want both of these to be lists, as it is richer test wise.
+                desired_order = self.order_by_desired_field_helper(model_admin, request, field_obj.name, *sorted_fields)
+                current_sort_order = list(model_admin.formfield_for_foreignkey(field_obj, request).queryset)
 
-        # Create a mock request
-        request = self.factory.post("/admin/registrar/domaininformation/{}/change/".format(applications[0].pk))
-
-        model_admin = AuditedAdmin(DomainInformation, self.site)
-
-        sorted_fields = []
-        # Typically we wouldn't want two nested for fields,
-        # but both fields are of a fixed length.
-        # For test case purposes, this should be performant.
-        for field in tested_fields:
-            isOtherOrderfield: bool = isinstance(field, tuple)
-            field_obj = None
-            if isOtherOrderfield:
-                sorted_fields = field[1]
-                field_obj = field[0]
-            else:
-                sorted_fields = ["first_name", "last_name"]
-                field_obj = field
-            # We want both of these to be lists, as it is richer test wise.
-            desired_order = self.order_by_desired_field_helper(model_admin, request, field_obj.name, *sorted_fields)
-            current_sort_order = list(model_admin.formfield_for_foreignkey(field_obj, request).queryset)
-
-            # Conforms to the same object structure as desired_order
-            current_sort_order_coerced_type = []
-
-            # This is necessary as .queryset and get_queryset
-            # return lists of different types/structures.
-            # We need to parse this data and coerce them into the same type.
-            for obj in current_sort_order:
-                last = None
-                if not isOtherOrderfield:
-                    first = obj.first_name
-                    last = obj.last_name
-                elif field_obj == DomainInformation.domain.field:
-                    first = obj.name
-                elif field_obj == DomainInformation.domain_application.field:
-                    first = obj.requested_domain.name
-
-                name_tuple = self.coerced_fk_field_helper(first, last, field_obj.name, ":")
-                if name_tuple is not None:
-                    current_sort_order_coerced_type.append(name_tuple)
+                # Conforms to the same object structure as desired_order
+                current_sort_order_coerced_type = []
 
-            self.assertEqual(
-                desired_order,
-                current_sort_order_coerced_type,
-                "{} is not ordered alphabetically".format(field_obj.name),
-            )
+                # This is necessary as .queryset and get_queryset
+                # return lists of different types/structures.
+                # We need to parse this data and coerce them into the same type.
+                for obj in current_sort_order:
+                    last = None
+                    if not isOtherOrderfield:
+                        first = obj.first_name
+                        last = obj.last_name
+                    elif field_obj == DomainInformation.domain.field:
+                        first = obj.name
+                    elif field_obj == DomainInformation.domain_application.field:
+                        first = obj.requested_domain.name
+
+                    name_tuple = self.coerced_fk_field_helper(first, last, field_obj.name, ":")
+                    if name_tuple is not None:
+                        current_sort_order_coerced_type.append(name_tuple)
+
+                self.assertEqual(
+                    desired_order,
+                    current_sort_order_coerced_type,
+                    "{} is not ordered alphabetically".format(field_obj.name),
+                )
 
     def test_alphabetically_sorted_fk_fields_domain_invitation(self):
-        tested_fields = [DomainInvitation.domain.field]
+        with less_console_noise():
+            tested_fields = [DomainInvitation.domain.field]
 
-        # Creates multiple domain applications - review status does not matter
-        applications = multiple_unalphabetical_domain_objects("invitation")
+            # Creates multiple domain applications - review status does not matter
+            applications = multiple_unalphabetical_domain_objects("invitation")
 
-        # Create a mock request
-        request = self.factory.post("/admin/registrar/domaininvitation/{}/change/".format(applications[0].pk))
+            # Create a mock request
+            request = self.factory.post("/admin/registrar/domaininvitation/{}/change/".format(applications[0].pk))
 
-        model_admin = AuditedAdmin(DomainInvitation, self.site)
+            model_admin = AuditedAdmin(DomainInvitation, self.site)
 
-        sorted_fields = []
-        # Typically we wouldn't want two nested for fields,
-        # but both fields are of a fixed length.
-        # For test case purposes, this should be performant.
-        for field in tested_fields:
-            sorted_fields = ["name"]
-            # We want both of these to be lists, as it is richer test wise.
+            sorted_fields = []
+            # Typically we wouldn't want two nested for fields,
+            # but both fields are of a fixed length.
+            # For test case purposes, this should be performant.
+            for field in tested_fields:
+                sorted_fields = ["name"]
+                # We want both of these to be lists, as it is richer test wise.
 
-            desired_order = self.order_by_desired_field_helper(model_admin, request, field.name, *sorted_fields)
-            current_sort_order = list(model_admin.formfield_for_foreignkey(field, request).queryset)
+                desired_order = self.order_by_desired_field_helper(model_admin, request, field.name, *sorted_fields)
+                current_sort_order = list(model_admin.formfield_for_foreignkey(field, request).queryset)
 
-            # Conforms to the same object structure as desired_order
-            current_sort_order_coerced_type = []
+                # Conforms to the same object structure as desired_order
+                current_sort_order_coerced_type = []
 
-            # This is necessary as .queryset and get_queryset
-            # return lists of different types/structures.
-            # We need to parse this data and coerce them into the same type.
-            for contact in current_sort_order:
-                first = contact.name
-                last = None
+                # This is necessary as .queryset and get_queryset
+                # return lists of different types/structures.
+                # We need to parse this data and coerce them into the same type.
+                for contact in current_sort_order:
+                    first = contact.name
+                    last = None
 
-                name_tuple = self.coerced_fk_field_helper(first, last, field.name, ":")
-                if name_tuple is not None:
-                    current_sort_order_coerced_type.append(name_tuple)
+                    name_tuple = self.coerced_fk_field_helper(first, last, field.name, ":")
+                    if name_tuple is not None:
+                        current_sort_order_coerced_type.append(name_tuple)
 
-            self.assertEqual(
-                desired_order,
-                current_sort_order_coerced_type,
-                "{} is not ordered alphabetically".format(field.name),
-            )
+                self.assertEqual(
+                    desired_order,
+                    current_sort_order_coerced_type,
+                    "{} is not ordered alphabetically".format(field.name),
+                )
 
     def coerced_fk_field_helper(self, first_name, last_name, field_name, queryset_shorthand):
         """Handles edge cases for test cases"""
@@ -1884,93 +2169,98 @@ def setUp(self):
     def test_session_vars_set_correctly(self):
         """Checks if session variables are being set correctly"""
 
-        p = "adminpass"
-        self.client.login(username="superuser", password=p)
+        with less_console_noise():
+            p = "adminpass"
+            self.client.login(username="superuser", password=p)
 
-        dummy_domain_information = generic_domain_object("information", "session")
-        request = self.get_factory_post_edit_domain(dummy_domain_information.domain.pk)
-        self.populate_session_values(request, dummy_domain_information.domain)
-        self.assertEqual(request.session["analyst_action"], "edit")
-        self.assertEqual(
-            request.session["analyst_action_location"],
-            dummy_domain_information.domain.pk,
-        )
+            dummy_domain_information = generic_domain_object("information", "session")
+            request = self.get_factory_post_edit_domain(dummy_domain_information.domain.pk)
+            self.populate_session_values(request, dummy_domain_information.domain)
+            self.assertEqual(request.session["analyst_action"], "edit")
+            self.assertEqual(
+                request.session["analyst_action_location"],
+                dummy_domain_information.domain.pk,
+            )
 
     def test_session_vars_set_correctly_hardcoded_domain(self):
         """Checks if session variables are being set correctly"""
 
-        p = "adminpass"
-        self.client.login(username="superuser", password=p)
+        with less_console_noise():
+            p = "adminpass"
+            self.client.login(username="superuser", password=p)
 
-        dummy_domain_information: Domain = generic_domain_object("information", "session")
-        dummy_domain_information.domain.pk = 1
+            dummy_domain_information: Domain = generic_domain_object("information", "session")
+            dummy_domain_information.domain.pk = 1
 
-        request = self.get_factory_post_edit_domain(dummy_domain_information.domain.pk)
-        self.populate_session_values(request, dummy_domain_information.domain)
-        self.assertEqual(request.session["analyst_action"], "edit")
-        self.assertEqual(request.session["analyst_action_location"], 1)
+            request = self.get_factory_post_edit_domain(dummy_domain_information.domain.pk)
+            self.populate_session_values(request, dummy_domain_information.domain)
+            self.assertEqual(request.session["analyst_action"], "edit")
+            self.assertEqual(request.session["analyst_action_location"], 1)
 
     def test_session_variables_reset_correctly(self):
         """Checks if incorrect session variables get overridden"""
 
-        p = "adminpass"
-        self.client.login(username="superuser", password=p)
+        with less_console_noise():
+            p = "adminpass"
+            self.client.login(username="superuser", password=p)
 
-        dummy_domain_information = generic_domain_object("information", "session")
-        request = self.get_factory_post_edit_domain(dummy_domain_information.domain.pk)
+            dummy_domain_information = generic_domain_object("information", "session")
+            request = self.get_factory_post_edit_domain(dummy_domain_information.domain.pk)
 
-        self.populate_session_values(request, dummy_domain_information.domain, preload_bad_data=True)
+            self.populate_session_values(request, dummy_domain_information.domain, preload_bad_data=True)
 
-        self.assertEqual(request.session["analyst_action"], "edit")
-        self.assertEqual(
-            request.session["analyst_action_location"],
-            dummy_domain_information.domain.pk,
-        )
+            self.assertEqual(request.session["analyst_action"], "edit")
+            self.assertEqual(
+                request.session["analyst_action_location"],
+                dummy_domain_information.domain.pk,
+            )
 
     def test_session_variables_retain_information(self):
         """Checks to see if session variables retain old information"""
 
-        p = "adminpass"
-        self.client.login(username="superuser", password=p)
+        with less_console_noise():
+            p = "adminpass"
+            self.client.login(username="superuser", password=p)
 
-        dummy_domain_information_list = multiple_unalphabetical_domain_objects("information")
-        for item in dummy_domain_information_list:
-            request = self.get_factory_post_edit_domain(item.domain.pk)
-            self.populate_session_values(request, item.domain)
+            dummy_domain_information_list = multiple_unalphabetical_domain_objects("information")
+            for item in dummy_domain_information_list:
+                request = self.get_factory_post_edit_domain(item.domain.pk)
+                self.populate_session_values(request, item.domain)
 
-            self.assertEqual(request.session["analyst_action"], "edit")
-            self.assertEqual(request.session["analyst_action_location"], item.domain.pk)
+                self.assertEqual(request.session["analyst_action"], "edit")
+                self.assertEqual(request.session["analyst_action_location"], item.domain.pk)
 
     def test_session_variables_concurrent_requests(self):
         """Simulates two requests at once"""
 
-        p = "adminpass"
-        self.client.login(username="superuser", password=p)
+        with less_console_noise():
+            p = "adminpass"
+            self.client.login(username="superuser", password=p)
 
-        info_first = generic_domain_object("information", "session")
-        info_second = generic_domain_object("information", "session2")
+            info_first = generic_domain_object("information", "session")
+            info_second = generic_domain_object("information", "session2")
 
-        request_first = self.get_factory_post_edit_domain(info_first.domain.pk)
-        request_second = self.get_factory_post_edit_domain(info_second.domain.pk)
+            request_first = self.get_factory_post_edit_domain(info_first.domain.pk)
+            request_second = self.get_factory_post_edit_domain(info_second.domain.pk)
 
-        self.populate_session_values(request_first, info_first.domain, True)
-        self.populate_session_values(request_second, info_second.domain, True)
+            self.populate_session_values(request_first, info_first.domain, True)
+            self.populate_session_values(request_second, info_second.domain, True)
 
-        # Check if anything got nulled out
-        self.assertNotEqual(request_first.session["analyst_action"], None)
-        self.assertNotEqual(request_second.session["analyst_action"], None)
-        self.assertNotEqual(request_first.session["analyst_action_location"], None)
-        self.assertNotEqual(request_second.session["analyst_action_location"], None)
+            # Check if anything got nulled out
+            self.assertNotEqual(request_first.session["analyst_action"], None)
+            self.assertNotEqual(request_second.session["analyst_action"], None)
+            self.assertNotEqual(request_first.session["analyst_action_location"], None)
+            self.assertNotEqual(request_second.session["analyst_action_location"], None)
 
-        # Check if they are both the same action 'type'
-        self.assertEqual(request_first.session["analyst_action"], "edit")
-        self.assertEqual(request_second.session["analyst_action"], "edit")
+            # Check if they are both the same action 'type'
+            self.assertEqual(request_first.session["analyst_action"], "edit")
+            self.assertEqual(request_second.session["analyst_action"], "edit")
 
-        # Check their locations, and ensure they aren't the same across both
-        self.assertNotEqual(
-            request_first.session["analyst_action_location"],
-            request_second.session["analyst_action_location"],
-        )
+            # Check their locations, and ensure they aren't the same across both
+            self.assertNotEqual(
+                request_first.session["analyst_action_location"],
+                request_second.session["analyst_action_location"],
+            )
 
     def populate_session_values(self, request, domain_object, preload_bad_data=False):
         """Boilerplate for creating mock sessions"""
@@ -2003,63 +2293,66 @@ def setUp(self):
         self.staffuser = create_user()
 
     def test_readonly_when_restricted_staffuser(self):
-        request = self.factory.get("/")
-        request.user = self.staffuser
+        with less_console_noise():
+            request = self.factory.get("/")
+            request.user = self.staffuser
 
-        readonly_fields = self.admin.get_readonly_fields(request)
+            readonly_fields = self.admin.get_readonly_fields(request)
 
-        expected_fields = [
-            "user",
-        ]
+            expected_fields = [
+                "user",
+            ]
 
-        self.assertEqual(readonly_fields, expected_fields)
+            self.assertEqual(readonly_fields, expected_fields)
 
     def test_readonly_when_restricted_superuser(self):
-        request = self.factory.get("/")
-        request.user = self.superuser
+        with less_console_noise():
+            request = self.factory.get("/")
+            request.user = self.superuser
 
-        readonly_fields = self.admin.get_readonly_fields(request)
+            readonly_fields = self.admin.get_readonly_fields(request)
 
-        expected_fields = []
+            expected_fields = []
 
-        self.assertEqual(readonly_fields, expected_fields)
+            self.assertEqual(readonly_fields, expected_fields)
 
     def test_change_view_for_joined_contact_five_or_less(self):
         """Create a contact, join it to 4 domain requests. The 5th join will be a user.
         Assert that the warning on the contact form lists 5 joins."""
 
-        self.client.force_login(self.superuser)
+        with less_console_noise():
+            self.client.force_login(self.superuser)
 
-        # Create an instance of the model
-        contact, _ = Contact.objects.get_or_create(user=self.staffuser)
-
-        # join it to 4 domain requests. The 5th join will be a user.
-        application1 = completed_application(submitter=contact, name="city1.gov")
-        application2 = completed_application(submitter=contact, name="city2.gov")
-        application3 = completed_application(submitter=contact, name="city3.gov")
-        application4 = completed_application(submitter=contact, name="city4.gov")
-
-        with patch("django.contrib.messages.warning") as mock_warning:
-            # Use the test client to simulate the request
-            response = self.client.get(reverse("admin:registrar_contact_change", args=[contact.pk]))
-
-            # Assert that the error message was called with the correct argument
-            # Note: The 5th join will be a user.
-            mock_warning.assert_called_once_with(
-                response.wsgi_request,
-                "<ul class='messagelist_content-list--unstyled'>"
-                "<li>Joined to DomainApplication: <a href='/admin/registrar/"
-                f"domainapplication/{application1.pk}/change/'>city1.gov</a></li>"
-                "<li>Joined to DomainApplication: <a href='/admin/registrar/"
-                f"domainapplication/{application2.pk}/change/'>city2.gov</a></li>"
-                "<li>Joined to DomainApplication: <a href='/admin/registrar/"
-                f"domainapplication/{application3.pk}/change/'>city3.gov</a></li>"
-                "<li>Joined to DomainApplication: <a href='/admin/registrar/"
-                f"domainapplication/{application4.pk}/change/'>city4.gov</a></li>"
-                "<li>Joined to User: <a href='/admin/registrar/"
-                f"user/{self.staffuser.pk}/change/'>staff@example.com</a></li>"
-                "</ul>",
-            )
+            # Create an instance of the model
+            contact, _ = Contact.objects.get_or_create(user=self.staffuser)
+
+            # join it to 4 domain requests. The 5th join will be a user.
+            application1 = completed_application(submitter=contact, name="city1.gov")
+            application2 = completed_application(submitter=contact, name="city2.gov")
+            application3 = completed_application(submitter=contact, name="city3.gov")
+            application4 = completed_application(submitter=contact, name="city4.gov")
+
+            with patch("django.contrib.messages.warning") as mock_warning:
+                # Use the test client to simulate the request
+                response = self.client.get(reverse("admin:registrar_contact_change", args=[contact.pk]))
+
+                # Assert that the error message was called with the correct argument
+                # Note: The 5th join will be a user.
+                mock_warning.assert_called_once_with(
+                    response.wsgi_request,
+                    "<ul class='messagelist_content-list--unstyled'>"
+                    "<li>Joined to DomainApplication: <a href='/admin/registrar/"
+                    f"domainapplication/{application1.pk}/change/'>city1.gov</a></li>"
+                    "<li>Joined to DomainApplication: <a href='/admin/registrar/"
+                    f"domainapplication/{application2.pk}/change/'>city2.gov</a></li>"
+                    "<li>Joined to DomainApplication: <a href='/admin/registrar/"
+                    f"domainapplication/{application3.pk}/change/'>city3.gov</a></li>"
+                    "<li>Joined to DomainApplication: <a href='/admin/registrar/"
+                    f"domainapplication/{application4.pk}/change/'>city4.gov</a></li>"
+                    "<li>Joined to User: <a href='/admin/registrar/"
+                    f"user/{self.staffuser.pk}/change/'>staff@example.com</a></li>"
+                    "</ul>",
+                )
 
     def test_change_view_for_joined_contact_five_or_more(self):
         """Create a contact, join it to 5 domain requests. The 6th join will be a user.
@@ -2109,20 +2402,21 @@ def setUp(self):
         self.factory = RequestFactory()
 
     def test_save_model_sets_user_field(self):
-        self.client.force_login(self.superuser)
+        with less_console_noise():
+            self.client.force_login(self.superuser)
 
-        # Create an instance of the admin class
-        admin_instance = VerifiedByStaffAdmin(model=VerifiedByStaff, admin_site=None)
+            # Create an instance of the admin class
+            admin_instance = VerifiedByStaffAdmin(model=VerifiedByStaff, admin_site=None)
 
-        # Create a VerifiedByStaff instance
-        vip_instance = VerifiedByStaff(email="test@example.com", notes="Test Notes")
+            # Create a VerifiedByStaff instance
+            vip_instance = VerifiedByStaff(email="test@example.com", notes="Test Notes")
 
-        # Create a request object
-        request = self.factory.post("/admin/yourapp/VerifiedByStaff/add/")
-        request.user = self.superuser
+            # Create a request object
+            request = self.factory.post("/admin/yourapp/VerifiedByStaff/add/")
+            request.user = self.superuser
 
-        # Call the save_model method
-        admin_instance.save_model(request, vip_instance, None, None)
+            # Call the save_model method
+            admin_instance.save_model(request, vip_instance, None, None)
 
-        # Check that the user field is set to the request.user
-        self.assertEqual(vip_instance.requestor, self.superuser)
+            # Check that the user field is set to the request.user
+            self.assertEqual(vip_instance.requestor, self.superuser)
diff --git a/src/registrar/tests/test_models.py b/src/registrar/tests/test_models.py
--- a/src/registrar/tests/test_models.py
+++ b/src/registrar/tests/test_models.py
@@ -574,6 +574,56 @@ def custom_is_active(self):
                     with self.assertRaises(TransitionNotAllowed):
                         self.approved_application.reject_with_prejudice()
 
+    def test_approve_from_rejected_clears_rejection_reason(self):
+        """When transitioning from rejected to approved on a domain request,
+        the rejection_reason is cleared."""
+
+        with less_console_noise():
+            # Create a sample application
+            application = completed_application(status=DomainApplication.ApplicationStatus.REJECTED)
+            application.rejection_reason = DomainApplication.RejectionReasons.DOMAIN_PURPOSE
+
+            # Approve
+            with boto3_mocking.clients.handler_for("sesv2", self.mock_client):
+                application.approve()
+
+            self.assertEqual(application.status, DomainApplication.ApplicationStatus.APPROVED)
+            self.assertEqual(application.rejection_reason, None)
+
+    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."""
+
+        with less_console_noise():
+            # Create a sample application
+            application = completed_application(status=DomainApplication.ApplicationStatus.REJECTED)
+            application.domain_is_not_active = True
+            application.rejection_reason = DomainApplication.RejectionReasons.DOMAIN_PURPOSE
+
+            # Approve
+            with boto3_mocking.clients.handler_for("sesv2", self.mock_client):
+                application.in_review()
+
+            self.assertEqual(application.status, DomainApplication.ApplicationStatus.IN_REVIEW)
+            self.assertEqual(application.rejection_reason, None)
+
+    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."""
+
+        with less_console_noise():
+            # Create a sample application
+            application = completed_application(status=DomainApplication.ApplicationStatus.REJECTED)
+            application.domain_is_not_active = True
+            application.rejection_reason = DomainApplication.RejectionReasons.DOMAIN_PURPOSE
+
+            # Approve
+            with boto3_mocking.clients.handler_for("sesv2", self.mock_client):
+                application.action_needed()
+
+            self.assertEqual(application.status, DomainApplication.ApplicationStatus.ACTION_NEEDED)
+            self.assertEqual(application.rejection_reason, None)
+
     def test_has_rationale_returns_true(self):
         """has_rationale() returns true when an application has no_other_contacts_rationale"""
         with less_console_noise():