mirror of
https://github.com/cisagov/manage.get.gov.git
synced 2025-07-21 10:16:13 +02:00
Merge pull request #1821 from cisagov/rjm/1578-rejection-reason
Issue 1578: Rejection reason (RJM)
This commit is contained in:
commit
ae4d9547ca
7 changed files with 1401 additions and 881 deletions
|
@ -872,7 +872,7 @@ class DomainApplicationAdmin(ListHeaderAdmin):
|
||||||
]
|
]
|
||||||
|
|
||||||
# Filters
|
# Filters
|
||||||
list_filter = ("status", "organization_type", InvestigatorFilter)
|
list_filter = ("status", "organization_type", "rejection_reason", InvestigatorFilter)
|
||||||
|
|
||||||
# Search
|
# Search
|
||||||
search_fields = [
|
search_fields = [
|
||||||
|
@ -886,7 +886,7 @@ class DomainApplicationAdmin(ListHeaderAdmin):
|
||||||
# Detail view
|
# Detail view
|
||||||
form = DomainApplicationAdminForm
|
form = DomainApplicationAdminForm
|
||||||
fieldsets = [
|
fieldsets = [
|
||||||
(None, {"fields": ["status", "investigator", "creator", "approved_domain", "notes"]}),
|
(None, {"fields": ["status", "rejection_reason", "investigator", "creator", "approved_domain", "notes"]}),
|
||||||
(
|
(
|
||||||
"Type of organization",
|
"Type of organization",
|
||||||
{
|
{
|
||||||
|
@ -987,6 +987,23 @@ class DomainApplicationAdmin(ListHeaderAdmin):
|
||||||
"This action is not permitted. The domain is already active.",
|
"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:
|
else:
|
||||||
if obj.status != original_obj.status:
|
if obj.status != original_obj.status:
|
||||||
status_method_mapping = {
|
status_method_mapping = {
|
||||||
|
|
|
@ -339,3 +339,46 @@ function enableRelatedWidgetButtons(changeLink, deleteLink, viewLink, elementPk,
|
||||||
}
|
}
|
||||||
|
|
||||||
})();
|
})();
|
||||||
|
|
||||||
|
/** An IIFE for admin in DjangoAdmin to listen to changes on the domain request
|
||||||
|
* status select amd to show/hide the rejection reason
|
||||||
|
*/
|
||||||
|
(function (){
|
||||||
|
let rejectionReasonFormGroup = document.querySelector('.field-rejection_reason')
|
||||||
|
|
||||||
|
if (rejectionReasonFormGroup) {
|
||||||
|
let statusSelect = document.getElementById('id_status')
|
||||||
|
|
||||||
|
// Initial handling of rejectionReasonFormGroup display
|
||||||
|
if (statusSelect.value != 'rejected')
|
||||||
|
rejectionReasonFormGroup.style.display = 'none';
|
||||||
|
|
||||||
|
// Listen to change events and handle rejectionReasonFormGroup display, then save status to session storage
|
||||||
|
statusSelect.addEventListener('change', function() {
|
||||||
|
if (statusSelect.value == 'rejected') {
|
||||||
|
rejectionReasonFormGroup.style.display = 'block';
|
||||||
|
sessionStorage.removeItem('hideRejectionReason');
|
||||||
|
} else {
|
||||||
|
rejectionReasonFormGroup.style.display = 'none';
|
||||||
|
sessionStorage.setItem('hideRejectionReason', 'true');
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
// Listen to Back/Forward button navigation and handle rejectionReasonFormGroup display based on session storage
|
||||||
|
|
||||||
|
// When you navigate using forward/back after changing status but not saving, when you land back on the DA page the
|
||||||
|
// status select will say (for example) Rejected but the selected option can be something else. To manage the show/hide
|
||||||
|
// accurately for this edge case, we use cache and test for the back/forward navigation.
|
||||||
|
const observer = new PerformanceObserver((list) => {
|
||||||
|
list.getEntries().forEach((entry) => {
|
||||||
|
if (entry.type === "back_forward") {
|
||||||
|
if (sessionStorage.getItem('hideRejectionReason'))
|
||||||
|
document.querySelector('.field-rejection_reason').style.display = 'none';
|
||||||
|
else
|
||||||
|
document.querySelector('.field-rejection_reason').style.display = 'block';
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
|
observer.observe({ type: "navigation" });
|
||||||
|
})();
|
||||||
|
|
|
@ -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,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
]
|
|
@ -351,12 +351,34 @@ class DomainApplication(TimeStampedModel):
|
||||||
]
|
]
|
||||||
AGENCY_CHOICES = [(v, v) for v in AGENCIES]
|
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 #####
|
# #### Internal fields about the application #####
|
||||||
status = FSMField(
|
status = FSMField(
|
||||||
choices=ApplicationStatus.choices, # possible states as an array of constants
|
choices=ApplicationStatus.choices, # possible states as an array of constants
|
||||||
default=ApplicationStatus.STARTED, # sensible default
|
default=ApplicationStatus.STARTED, # sensible default
|
||||||
protected=False, # can change state directly, particularly in Django admin
|
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
|
# This is the application user who created this application. The contact
|
||||||
# information that they gave is in the `submitter` field
|
# information that they gave is in the `submitter` field
|
||||||
creator = models.ForeignKey(
|
creator = models.ForeignKey(
|
||||||
|
@ -364,6 +386,7 @@ class DomainApplication(TimeStampedModel):
|
||||||
on_delete=models.PROTECT,
|
on_delete=models.PROTECT,
|
||||||
related_name="applications_created",
|
related_name="applications_created",
|
||||||
)
|
)
|
||||||
|
|
||||||
investigator = models.ForeignKey(
|
investigator = models.ForeignKey(
|
||||||
"registrar.User",
|
"registrar.User",
|
||||||
null=True,
|
null=True,
|
||||||
|
@ -688,12 +711,17 @@ class DomainApplication(TimeStampedModel):
|
||||||
|
|
||||||
This action is logged.
|
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
|
As side effects this will delete the domain and domain_information
|
||||||
(will cascade) when they exist."""
|
(will cascade) when they exist."""
|
||||||
|
|
||||||
if self.status == self.ApplicationStatus.APPROVED:
|
if self.status == self.ApplicationStatus.APPROVED:
|
||||||
self.delete_and_clean_up_domain("in_review")
|
self.delete_and_clean_up_domain("in_review")
|
||||||
|
|
||||||
|
if self.status == self.ApplicationStatus.REJECTED:
|
||||||
|
self.rejection_reason = None
|
||||||
|
|
||||||
literal = DomainApplication.ApplicationStatus.IN_REVIEW
|
literal = DomainApplication.ApplicationStatus.IN_REVIEW
|
||||||
# Check if the tuple exists, then grab its value
|
# Check if the tuple exists, then grab its value
|
||||||
in_review = literal if literal is not None else "In Review"
|
in_review = literal if literal is not None else "In Review"
|
||||||
|
@ -715,12 +743,17 @@ class DomainApplication(TimeStampedModel):
|
||||||
|
|
||||||
This action is logged.
|
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
|
As side effects this will delete the domain and domain_information
|
||||||
(will cascade) when they exist."""
|
(will cascade) when they exist."""
|
||||||
|
|
||||||
if self.status == self.ApplicationStatus.APPROVED:
|
if self.status == self.ApplicationStatus.APPROVED:
|
||||||
self.delete_and_clean_up_domain("reject_with_prejudice")
|
self.delete_and_clean_up_domain("reject_with_prejudice")
|
||||||
|
|
||||||
|
if self.status == self.ApplicationStatus.REJECTED:
|
||||||
|
self.rejection_reason = None
|
||||||
|
|
||||||
literal = DomainApplication.ApplicationStatus.ACTION_NEEDED
|
literal = DomainApplication.ApplicationStatus.ACTION_NEEDED
|
||||||
# Check if the tuple is setup correctly, then grab its value
|
# Check if the tuple is setup correctly, then grab its value
|
||||||
action_needed = literal if literal is not None else "Action Needed"
|
action_needed = literal if literal is not None else "Action Needed"
|
||||||
|
@ -739,6 +772,8 @@ class DomainApplication(TimeStampedModel):
|
||||||
def approve(self, send_email=True):
|
def approve(self, send_email=True):
|
||||||
"""Approve an application that has been submitted.
|
"""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
|
This has substantial side-effects because it creates another database
|
||||||
object for the approved Domain and makes the user who created the
|
object for the approved Domain and makes the user who created the
|
||||||
application into an admin on that domain. It also triggers an email
|
application into an admin on that domain. It also triggers an email
|
||||||
|
@ -761,6 +796,9 @@ class DomainApplication(TimeStampedModel):
|
||||||
user=self.creator, domain=created_domain, role=UserDomainRole.Roles.MANAGER
|
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(
|
self._send_status_update_email(
|
||||||
"application approved",
|
"application approved",
|
||||||
"emails/status_change_approved.txt",
|
"emails/status_change_approved.txt",
|
||||||
|
|
|
@ -8,7 +8,56 @@ REQUEST RECEIVED ON: {{ application.submission_date|date }}
|
||||||
STATUS: Rejected
|
STATUS: Rejected
|
||||||
|
|
||||||
----------------------------------------------------------------
|
----------------------------------------------------------------
|
||||||
|
{% if application.rejection_reason != 'other' %}
|
||||||
|
REJECTION REASON{% endif %}{% if application.rejection_reason == 'purpose_not_met' %}
|
||||||
|
Your domain request was rejected because the purpose you provided did not meet our
|
||||||
|
requirements. You didn’t provide enough information about how you intend to use the
|
||||||
|
domain.
|
||||||
|
|
||||||
|
Learn more about:
|
||||||
|
- Eligibility for a .gov domain <https://get.gov/domains/eligibility>
|
||||||
|
- What you can and can’t do with .gov domains <https://get.gov/domains/requirements/>
|
||||||
|
|
||||||
|
If you have questions or comments, reply to this email.{% elif application.rejection_reason == 'requestor_not_eligible' %}
|
||||||
|
Your domain request was rejected because we don’t believe you’re eligible to request a
|
||||||
|
.gov domain on behalf of {{ application.organization_name }}. You must be a government employee, or be
|
||||||
|
working on behalf of a government organization, to request a .gov domain.
|
||||||
|
|
||||||
|
|
||||||
|
DEMONSTRATE ELIGIBILITY
|
||||||
|
If you can provide more information that demonstrates your eligibility, or you want to
|
||||||
|
discuss further, reply to this email.{% elif application.rejection_reason == 'org_has_domain' %}
|
||||||
|
Your domain request was rejected because {{ application.organization_name }} has a .gov domain. Our
|
||||||
|
practice is to approve one domain per online service per government organization. We
|
||||||
|
evaluate additional requests on a case-by-case basis. You did not provide sufficient
|
||||||
|
justification for an additional domain.
|
||||||
|
|
||||||
|
Read more about our practice of approving one domain per online service
|
||||||
|
<https://get.gov/domains/before/#one-domain-per-service>.
|
||||||
|
|
||||||
|
If you have questions or comments, reply to this email.{% elif application.rejection_reason == 'contacts_not_verified' %}
|
||||||
|
Your domain request was rejected because we could not verify the organizational
|
||||||
|
contacts you provided. If you have questions or comments, reply to this email.{% elif application.rejection_reason == 'org_not_eligible' %}
|
||||||
|
Your domain request was rejected because we determined that {{ application.organization_name }} is not
|
||||||
|
eligible for a .gov domain. .Gov domains are only available to official U.S.-based
|
||||||
|
government organizations.
|
||||||
|
|
||||||
|
|
||||||
|
DEMONSTRATE ELIGIBILITY
|
||||||
|
If you can provide documentation that demonstrates your eligibility, reply to this email.
|
||||||
|
This can include links to (or copies of) your authorizing legislation, your founding
|
||||||
|
charter or bylaws, or other similar documentation. Without this, we can’t approve a
|
||||||
|
.gov domain for your organization. Learn more about eligibility for .gov domains
|
||||||
|
<https://get.gov/domains/eligibility/>.{% elif application.rejection_reason == 'naming_not_met' %}
|
||||||
|
Your domain request was rejected because it does not meet our naming requirements.
|
||||||
|
Domains should uniquely identify a government organization and be clear to the
|
||||||
|
general public. Learn more about naming requirements for your type of organization
|
||||||
|
<https://get.gov/domains/choosing/>.
|
||||||
|
|
||||||
|
|
||||||
|
YOU CAN SUBMIT A NEW REQUEST
|
||||||
|
We encourage you to request a domain that meets our requirements. If you have
|
||||||
|
questions or want to discuss potential domain names, reply to this email.{% elif application.rejection_reason == 'other' %}
|
||||||
YOU CAN SUBMIT A NEW REQUEST
|
YOU CAN SUBMIT A NEW REQUEST
|
||||||
If your organization is eligible for a .gov domain and you meet our other requirements, you can submit a new request.
|
If your organization is eligible for a .gov domain and you meet our other requirements, you can submit a new request.
|
||||||
|
|
||||||
|
@ -19,7 +68,7 @@ Learn more about:
|
||||||
|
|
||||||
NEED ASSISTANCE?
|
NEED ASSISTANCE?
|
||||||
If you have questions about this domain request or need help choosing a new domain name, reply to this email.
|
If you have questions about this domain request or need help choosing a new domain name, reply to this email.
|
||||||
|
{% endif %}
|
||||||
|
|
||||||
THANK YOU
|
THANK YOU
|
||||||
.Gov helps the public identify official, trusted information. Thank you for requesting a .gov domain.
|
.Gov helps the public identify official, trusted information. Thank you for requesting a .gov domain.
|
||||||
|
|
|
@ -445,6 +445,7 @@ class TestDomainApplicationAdminForm(TestCase):
|
||||||
self.application = completed_application()
|
self.application = completed_application()
|
||||||
|
|
||||||
def test_form_choices(self):
|
def test_form_choices(self):
|
||||||
|
with less_console_noise():
|
||||||
# Create a form instance with the test application
|
# Create a form instance with the test application
|
||||||
form = DomainApplicationAdminForm(instance=self.application)
|
form = DomainApplicationAdminForm(instance=self.application)
|
||||||
|
|
||||||
|
@ -453,6 +454,7 @@ class TestDomainApplicationAdminForm(TestCase):
|
||||||
self.assertEqual(form.fields["status"].widget.choices, expected_choices)
|
self.assertEqual(form.fields["status"].widget.choices, expected_choices)
|
||||||
|
|
||||||
def test_form_choices_when_no_instance(self):
|
def test_form_choices_when_no_instance(self):
|
||||||
|
with less_console_noise():
|
||||||
# Create a form instance without an instance
|
# Create a form instance without an instance
|
||||||
form = DomainApplicationAdminForm()
|
form = DomainApplicationAdminForm()
|
||||||
|
|
||||||
|
@ -467,6 +469,7 @@ class TestDomainApplicationAdminForm(TestCase):
|
||||||
)
|
)
|
||||||
|
|
||||||
def test_form_choices_when_ineligible(self):
|
def test_form_choices_when_ineligible(self):
|
||||||
|
with less_console_noise():
|
||||||
# Create a form instance with a domain application with ineligible status
|
# Create a form instance with a domain application with ineligible status
|
||||||
ineligible_application = DomainApplication(status="ineligible")
|
ineligible_application = DomainApplication(status="ineligible")
|
||||||
|
|
||||||
|
@ -502,6 +505,7 @@ class TestDomainApplicationAdmin(MockEppLib):
|
||||||
|
|
||||||
def test_domain_sortable(self):
|
def test_domain_sortable(self):
|
||||||
"""Tests if the DomainApplication sorts by domain correctly"""
|
"""Tests if the DomainApplication sorts by domain correctly"""
|
||||||
|
with less_console_noise():
|
||||||
p = "adminpass"
|
p = "adminpass"
|
||||||
self.client.login(username="superuser", password=p)
|
self.client.login(username="superuser", password=p)
|
||||||
|
|
||||||
|
@ -515,6 +519,7 @@ class TestDomainApplicationAdmin(MockEppLib):
|
||||||
|
|
||||||
def test_submitter_sortable(self):
|
def test_submitter_sortable(self):
|
||||||
"""Tests if the DomainApplication sorts by domain correctly"""
|
"""Tests if the DomainApplication sorts by domain correctly"""
|
||||||
|
with less_console_noise():
|
||||||
p = "adminpass"
|
p = "adminpass"
|
||||||
self.client.login(username="superuser", password=p)
|
self.client.login(username="superuser", password=p)
|
||||||
|
|
||||||
|
@ -545,6 +550,7 @@ class TestDomainApplicationAdmin(MockEppLib):
|
||||||
|
|
||||||
def test_investigator_sortable(self):
|
def test_investigator_sortable(self):
|
||||||
"""Tests if the DomainApplication sorts by domain correctly"""
|
"""Tests if the DomainApplication sorts by domain correctly"""
|
||||||
|
with less_console_noise():
|
||||||
p = "adminpass"
|
p = "adminpass"
|
||||||
self.client.login(username="superuser", password=p)
|
self.client.login(username="superuser", password=p)
|
||||||
|
|
||||||
|
@ -576,6 +582,7 @@ class TestDomainApplicationAdmin(MockEppLib):
|
||||||
"""
|
"""
|
||||||
Make sure the short name is displaying in admin on the list page
|
Make sure the short name is displaying in admin on the list page
|
||||||
"""
|
"""
|
||||||
|
with less_console_noise():
|
||||||
self.client.force_login(self.superuser)
|
self.client.force_login(self.superuser)
|
||||||
completed_application()
|
completed_application()
|
||||||
response = self.client.get("/admin/registrar/domainapplication/")
|
response = self.client.get("/admin/registrar/domainapplication/")
|
||||||
|
@ -587,7 +594,7 @@ class TestDomainApplicationAdmin(MockEppLib):
|
||||||
# Now let's make sure the long description does not exist
|
# Now let's make sure the long description does not exist
|
||||||
self.assertNotContains(response, "Federal: an agency of the U.S. government")
|
self.assertNotContains(response, "Federal: an agency of the U.S. government")
|
||||||
|
|
||||||
def transition_state_and_send_email(self, application, status):
|
def transition_state_and_send_email(self, application, status, rejection_reason=None):
|
||||||
"""Helper method for the email test cases."""
|
"""Helper method for the email test cases."""
|
||||||
|
|
||||||
with boto3_mocking.clients.handler_for("sesv2", self.mock_client):
|
with boto3_mocking.clients.handler_for("sesv2", self.mock_client):
|
||||||
|
@ -595,8 +602,9 @@ class TestDomainApplicationAdmin(MockEppLib):
|
||||||
# Create a mock request
|
# Create a mock request
|
||||||
request = self.factory.post("/admin/registrar/domainapplication/{}/change/".format(application.pk))
|
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.status = status
|
||||||
|
application.rejection_reason = rejection_reason
|
||||||
|
|
||||||
# Use the model admin's save_model method
|
# Use the model admin's save_model method
|
||||||
self.admin.save_model(request, application, form=None, change=True)
|
self.admin.save_model(request, application, form=None, change=True)
|
||||||
|
@ -607,6 +615,7 @@ class TestDomainApplicationAdmin(MockEppLib):
|
||||||
"""Helper method for the email test cases.
|
"""Helper method for the email test cases.
|
||||||
email_index is the index of the email in mock_client."""
|
email_index is the index of the email in mock_client."""
|
||||||
|
|
||||||
|
with less_console_noise():
|
||||||
# Access the arguments passed to send_email
|
# Access the arguments passed to send_email
|
||||||
call_args = self.mock_client.EMAILS_SENT
|
call_args = self.mock_client.EMAILS_SENT
|
||||||
kwargs = call_args[email_index]["kwargs"]
|
kwargs = call_args[email_index]["kwargs"]
|
||||||
|
@ -643,6 +652,7 @@ class TestDomainApplicationAdmin(MockEppLib):
|
||||||
Also test that the default email set in settings is NOT BCCd on non-prod whenever
|
Also test that the default email set in settings is NOT BCCd on non-prod whenever
|
||||||
an email does go out."""
|
an email does go out."""
|
||||||
|
|
||||||
|
with less_console_noise():
|
||||||
# Ensure there is no user with this email
|
# Ensure there is no user with this email
|
||||||
EMAIL = "mayor@igorville.gov"
|
EMAIL = "mayor@igorville.gov"
|
||||||
User.objects.filter(email=EMAIL).delete()
|
User.objects.filter(email=EMAIL).delete()
|
||||||
|
@ -697,6 +707,7 @@ class TestDomainApplicationAdmin(MockEppLib):
|
||||||
Also test that the default email set in settings IS BCCd on prod whenever
|
Also test that the default email set in settings IS BCCd on prod whenever
|
||||||
an email does go out."""
|
an email does go out."""
|
||||||
|
|
||||||
|
with less_console_noise():
|
||||||
# Ensure there is no user with this email
|
# Ensure there is no user with this email
|
||||||
EMAIL = "mayor@igorville.gov"
|
EMAIL = "mayor@igorville.gov"
|
||||||
User.objects.filter(email=EMAIL).delete()
|
User.objects.filter(email=EMAIL).delete()
|
||||||
|
@ -747,6 +758,7 @@ class TestDomainApplicationAdmin(MockEppLib):
|
||||||
"""When transitioning to approved on a domain request,
|
"""When transitioning to approved on a domain request,
|
||||||
an email is sent out every time."""
|
an email is sent out every time."""
|
||||||
|
|
||||||
|
with less_console_noise():
|
||||||
# Ensure there is no user with this email
|
# Ensure there is no user with this email
|
||||||
EMAIL = "mayor@igorville.gov"
|
EMAIL = "mayor@igorville.gov"
|
||||||
User.objects.filter(email=EMAIL).delete()
|
User.objects.filter(email=EMAIL).delete()
|
||||||
|
@ -760,7 +772,11 @@ class TestDomainApplicationAdmin(MockEppLib):
|
||||||
self.assertEqual(len(self.mock_client.EMAILS_SENT), 1)
|
self.assertEqual(len(self.mock_client.EMAILS_SENT), 1)
|
||||||
|
|
||||||
# Test Withdrawn Status
|
# Test Withdrawn Status
|
||||||
self.transition_state_and_send_email(application, DomainApplication.ApplicationStatus.REJECTED)
|
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.assert_email_is_accurate("Your .gov domain request has been rejected.", 1, EMAIL)
|
||||||
self.assertEqual(len(self.mock_client.EMAILS_SENT), 2)
|
self.assertEqual(len(self.mock_client.EMAILS_SENT), 2)
|
||||||
|
|
||||||
|
@ -768,10 +784,11 @@ class TestDomainApplicationAdmin(MockEppLib):
|
||||||
self.transition_state_and_send_email(application, DomainApplication.ApplicationStatus.APPROVED)
|
self.transition_state_and_send_email(application, DomainApplication.ApplicationStatus.APPROVED)
|
||||||
self.assertEqual(len(self.mock_client.EMAILS_SENT), 3)
|
self.assertEqual(len(self.mock_client.EMAILS_SENT), 3)
|
||||||
|
|
||||||
def test_save_model_sends_rejected_email(self):
|
def test_save_model_sends_rejected_email_purpose_not_met(self):
|
||||||
"""When transitioning to rejected on a domain request,
|
"""When transitioning to rejected on a domain request, an email is sent
|
||||||
an email is sent out every time."""
|
explaining why when the reason is domain purpose."""
|
||||||
|
|
||||||
|
with less_console_noise():
|
||||||
# Ensure there is no user with this email
|
# Ensure there is no user with this email
|
||||||
EMAIL = "mayor@igorville.gov"
|
EMAIL = "mayor@igorville.gov"
|
||||||
User.objects.filter(email=EMAIL).delete()
|
User.objects.filter(email=EMAIL).delete()
|
||||||
|
@ -779,24 +796,256 @@ class TestDomainApplicationAdmin(MockEppLib):
|
||||||
# Create a sample application
|
# Create a sample application
|
||||||
application = completed_application(status=DomainApplication.ApplicationStatus.IN_REVIEW)
|
application = completed_application(status=DomainApplication.ApplicationStatus.IN_REVIEW)
|
||||||
|
|
||||||
# Test Submitted Status
|
# Reject for reason DOMAIN_PURPOSE and test email
|
||||||
self.transition_state_and_send_email(application, DomainApplication.ApplicationStatus.REJECTED)
|
self.transition_state_and_send_email(
|
||||||
self.assert_email_is_accurate("Your .gov domain request has been rejected.", 0, 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)
|
self.assertEqual(len(self.mock_client.EMAILS_SENT), 1)
|
||||||
|
|
||||||
# Test Withdrawn Status
|
# Approve
|
||||||
self.transition_state_and_send_email(application, DomainApplication.ApplicationStatus.APPROVED)
|
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.assert_email_is_accurate("Congratulations! Your .gov domain request has been approved.", 1, EMAIL)
|
||||||
self.assertEqual(len(self.mock_client.EMAILS_SENT), 2)
|
self.assertEqual(len(self.mock_client.EMAILS_SENT), 2)
|
||||||
|
|
||||||
# Test Submitted Status Again (No new email should be sent)
|
def test_save_model_sends_rejected_email_requestor(self):
|
||||||
self.transition_state_and_send_email(application, DomainApplication.ApplicationStatus.REJECTED)
|
"""When transitioning to rejected on a domain request, an email is sent
|
||||||
self.assertEqual(len(self.mock_client.EMAILS_SENT), 3)
|
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)
|
||||||
|
|
||||||
|
# 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)
|
||||||
|
|
||||||
|
# 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)
|
||||||
|
|
||||||
|
# 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):
|
def test_save_model_sends_withdrawn_email(self):
|
||||||
"""When transitioning to withdrawn on a domain request,
|
"""When transitioning to withdrawn on a domain request,
|
||||||
an email is sent out every time."""
|
an email is sent out every time."""
|
||||||
|
|
||||||
|
with less_console_noise():
|
||||||
# Ensure there is no user with this email
|
# Ensure there is no user with this email
|
||||||
EMAIL = "mayor@igorville.gov"
|
EMAIL = "mayor@igorville.gov"
|
||||||
User.objects.filter(email=EMAIL).delete()
|
User.objects.filter(email=EMAIL).delete()
|
||||||
|
@ -822,6 +1071,7 @@ class TestDomainApplicationAdmin(MockEppLib):
|
||||||
|
|
||||||
def test_save_model_sets_approved_domain(self):
|
def test_save_model_sets_approved_domain(self):
|
||||||
# make sure there is no user with this email
|
# make sure there is no user with this email
|
||||||
|
with less_console_noise():
|
||||||
EMAIL = "mayor@igorville.gov"
|
EMAIL = "mayor@igorville.gov"
|
||||||
User.objects.filter(email=EMAIL).delete()
|
User.objects.filter(email=EMAIL).delete()
|
||||||
|
|
||||||
|
@ -843,6 +1093,7 @@ class TestDomainApplicationAdmin(MockEppLib):
|
||||||
self.assertEqual(application.requested_domain.name, application.approved_domain.name)
|
self.assertEqual(application.requested_domain.name, application.approved_domain.name)
|
||||||
|
|
||||||
def test_save_model_sets_restricted_status_on_user(self):
|
def test_save_model_sets_restricted_status_on_user(self):
|
||||||
|
with less_console_noise():
|
||||||
# make sure there is no user with this email
|
# make sure there is no user with this email
|
||||||
EMAIL = "mayor@igorville.gov"
|
EMAIL = "mayor@igorville.gov"
|
||||||
User.objects.filter(email=EMAIL).delete()
|
User.objects.filter(email=EMAIL).delete()
|
||||||
|
@ -854,7 +1105,6 @@ class TestDomainApplicationAdmin(MockEppLib):
|
||||||
request = self.factory.post("/admin/registrar/domainapplication/{}/change/".format(application.pk))
|
request = self.factory.post("/admin/registrar/domainapplication/{}/change/".format(application.pk))
|
||||||
|
|
||||||
with boto3_mocking.clients.handler_for("sesv2", self.mock_client):
|
with boto3_mocking.clients.handler_for("sesv2", self.mock_client):
|
||||||
with less_console_noise():
|
|
||||||
# Modify the application's property
|
# Modify the application's property
|
||||||
application.status = DomainApplication.ApplicationStatus.INELIGIBLE
|
application.status = DomainApplication.ApplicationStatus.INELIGIBLE
|
||||||
|
|
||||||
|
@ -865,9 +1115,9 @@ class TestDomainApplicationAdmin(MockEppLib):
|
||||||
self.assertEqual(application.creator.status, "restricted")
|
self.assertEqual(application.creator.status, "restricted")
|
||||||
|
|
||||||
def test_readonly_when_restricted_creator(self):
|
def test_readonly_when_restricted_creator(self):
|
||||||
|
with less_console_noise():
|
||||||
application = completed_application(status=DomainApplication.ApplicationStatus.IN_REVIEW)
|
application = completed_application(status=DomainApplication.ApplicationStatus.IN_REVIEW)
|
||||||
with boto3_mocking.clients.handler_for("sesv2", self.mock_client):
|
with boto3_mocking.clients.handler_for("sesv2", self.mock_client):
|
||||||
with less_console_noise():
|
|
||||||
application.creator.status = User.RESTRICTED
|
application.creator.status = User.RESTRICTED
|
||||||
application.creator.save()
|
application.creator.save()
|
||||||
|
|
||||||
|
@ -881,6 +1131,7 @@ class TestDomainApplicationAdmin(MockEppLib):
|
||||||
"created_at",
|
"created_at",
|
||||||
"updated_at",
|
"updated_at",
|
||||||
"status",
|
"status",
|
||||||
|
"rejection_reason",
|
||||||
"creator",
|
"creator",
|
||||||
"investigator",
|
"investigator",
|
||||||
"organization_type",
|
"organization_type",
|
||||||
|
@ -916,6 +1167,7 @@ class TestDomainApplicationAdmin(MockEppLib):
|
||||||
self.assertEqual(readonly_fields, expected_fields)
|
self.assertEqual(readonly_fields, expected_fields)
|
||||||
|
|
||||||
def test_readonly_fields_for_analyst(self):
|
def test_readonly_fields_for_analyst(self):
|
||||||
|
with less_console_noise():
|
||||||
request = self.factory.get("/") # Use the correct method and path
|
request = self.factory.get("/") # Use the correct method and path
|
||||||
request.user = self.staffuser
|
request.user = self.staffuser
|
||||||
|
|
||||||
|
@ -937,6 +1189,7 @@ class TestDomainApplicationAdmin(MockEppLib):
|
||||||
self.assertEqual(readonly_fields, expected_fields)
|
self.assertEqual(readonly_fields, expected_fields)
|
||||||
|
|
||||||
def test_readonly_fields_for_superuser(self):
|
def test_readonly_fields_for_superuser(self):
|
||||||
|
with less_console_noise():
|
||||||
request = self.factory.get("/") # Use the correct method and path
|
request = self.factory.get("/") # Use the correct method and path
|
||||||
request.user = self.superuser
|
request.user = self.superuser
|
||||||
|
|
||||||
|
@ -947,10 +1200,10 @@ class TestDomainApplicationAdmin(MockEppLib):
|
||||||
self.assertEqual(readonly_fields, expected_fields)
|
self.assertEqual(readonly_fields, expected_fields)
|
||||||
|
|
||||||
def test_saving_when_restricted_creator(self):
|
def test_saving_when_restricted_creator(self):
|
||||||
|
with less_console_noise():
|
||||||
# Create an instance of the model
|
# Create an instance of the model
|
||||||
application = completed_application(status=DomainApplication.ApplicationStatus.IN_REVIEW)
|
application = completed_application(status=DomainApplication.ApplicationStatus.IN_REVIEW)
|
||||||
with boto3_mocking.clients.handler_for("sesv2", self.mock_client):
|
with boto3_mocking.clients.handler_for("sesv2", self.mock_client):
|
||||||
with less_console_noise():
|
|
||||||
application.creator.status = User.RESTRICTED
|
application.creator.status = User.RESTRICTED
|
||||||
application.creator.save()
|
application.creator.save()
|
||||||
|
|
||||||
|
@ -972,10 +1225,10 @@ class TestDomainApplicationAdmin(MockEppLib):
|
||||||
self.assertEqual(application.status, DomainApplication.ApplicationStatus.IN_REVIEW)
|
self.assertEqual(application.status, DomainApplication.ApplicationStatus.IN_REVIEW)
|
||||||
|
|
||||||
def test_change_view_with_restricted_creator(self):
|
def test_change_view_with_restricted_creator(self):
|
||||||
|
with less_console_noise():
|
||||||
# Create an instance of the model
|
# Create an instance of the model
|
||||||
application = completed_application(status=DomainApplication.ApplicationStatus.IN_REVIEW)
|
application = completed_application(status=DomainApplication.ApplicationStatus.IN_REVIEW)
|
||||||
with boto3_mocking.clients.handler_for("sesv2", self.mock_client):
|
with boto3_mocking.clients.handler_for("sesv2", self.mock_client):
|
||||||
with less_console_noise():
|
|
||||||
application.creator.status = User.RESTRICTED
|
application.creator.status = User.RESTRICTED
|
||||||
application.creator.save()
|
application.creator.save()
|
||||||
|
|
||||||
|
@ -992,13 +1245,14 @@ class TestDomainApplicationAdmin(MockEppLib):
|
||||||
"Cannot edit an application with a restricted creator.",
|
"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,
|
"""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.
|
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
|
Used to test errors when saving a change with an active domain, also used to test side effects
|
||||||
when saving a change goes through."""
|
when saving a change goes through."""
|
||||||
|
|
||||||
|
with less_console_noise():
|
||||||
# Create an instance of the model
|
# Create an instance of the model
|
||||||
application = completed_application(status=DomainApplication.ApplicationStatus.APPROVED)
|
application = completed_application(status=DomainApplication.ApplicationStatus.APPROVED)
|
||||||
domain = Domain.objects.create(name=application.requested_domain.name)
|
domain = Domain.objects.create(name=application.requested_domain.name)
|
||||||
|
@ -1021,6 +1275,8 @@ class TestDomainApplicationAdmin(MockEppLib):
|
||||||
stack.enter_context(patch.object(messages, "error"))
|
stack.enter_context(patch.object(messages, "error"))
|
||||||
|
|
||||||
application.status = another_state
|
application.status = another_state
|
||||||
|
application.rejection_reason = rejection_reason
|
||||||
|
|
||||||
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
|
# Assert that the error message was called with the correct argument
|
||||||
|
@ -1062,7 +1318,11 @@ class TestDomainApplicationAdmin(MockEppLib):
|
||||||
self.trigger_saving_approved_to_another_state(False, DomainApplication.ApplicationStatus.ACTION_NEEDED)
|
self.trigger_saving_approved_to_another_state(False, DomainApplication.ApplicationStatus.ACTION_NEEDED)
|
||||||
|
|
||||||
def test_side_effects_when_saving_approved_to_rejected(self):
|
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):
|
def test_side_effects_when_saving_approved_to_ineligible(self):
|
||||||
self.trigger_saving_approved_to_another_state(False, DomainApplication.ApplicationStatus.INELIGIBLE)
|
self.trigger_saving_approved_to_another_state(False, DomainApplication.ApplicationStatus.INELIGIBLE)
|
||||||
|
@ -1074,12 +1334,18 @@ class TestDomainApplicationAdmin(MockEppLib):
|
||||||
It retrieves the current list of filters from DomainApplicationAdmin
|
It retrieves the current list of filters from DomainApplicationAdmin
|
||||||
and checks that it matches the expected list of filters.
|
and checks that it matches the expected list of filters.
|
||||||
"""
|
"""
|
||||||
|
with less_console_noise():
|
||||||
request = self.factory.get("/")
|
request = self.factory.get("/")
|
||||||
request.user = self.superuser
|
request.user = self.superuser
|
||||||
|
|
||||||
# Grab the current list of table filters
|
# Grab the current list of table filters
|
||||||
readonly_fields = self.admin.get_list_filter(request)
|
readonly_fields = self.admin.get_list_filter(request)
|
||||||
expected_fields = ("status", "organization_type", DomainApplicationAdmin.InvestigatorFilter)
|
expected_fields = (
|
||||||
|
"status",
|
||||||
|
"organization_type",
|
||||||
|
"rejection_reason",
|
||||||
|
DomainApplicationAdmin.InvestigatorFilter,
|
||||||
|
)
|
||||||
|
|
||||||
self.assertEqual(readonly_fields, expected_fields)
|
self.assertEqual(readonly_fields, expected_fields)
|
||||||
|
|
||||||
|
@ -1093,6 +1359,7 @@ class TestDomainApplicationAdmin(MockEppLib):
|
||||||
that it matches the expected queryset,
|
that it matches the expected queryset,
|
||||||
which is sorted alphabetically by the 'requested_domain__name' field.
|
which is sorted alphabetically by the 'requested_domain__name' field.
|
||||||
"""
|
"""
|
||||||
|
with less_console_noise():
|
||||||
# Creates a list of DomainApplications in scrambled order
|
# Creates a list of DomainApplications in scrambled order
|
||||||
multiple_unalphabetical_domain_objects("application")
|
multiple_unalphabetical_domain_objects("application")
|
||||||
|
|
||||||
|
@ -1125,6 +1392,7 @@ class TestDomainApplicationAdmin(MockEppLib):
|
||||||
the filter displays correctly, when the filter isn't filtering correctly.
|
the filter displays correctly, when the filter isn't filtering correctly.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
|
with less_console_noise():
|
||||||
# Create a mock DomainApplication object, with a fake investigator
|
# Create a mock DomainApplication object, with a fake investigator
|
||||||
application: DomainApplication = generic_domain_object("application", "SomeGuy")
|
application: DomainApplication = generic_domain_object("application", "SomeGuy")
|
||||||
investigator_user = User.objects.filter(username=application.investigator.username).get()
|
investigator_user = User.objects.filter(username=application.investigator.username).get()
|
||||||
|
@ -1168,6 +1436,8 @@ class TestDomainApplicationAdmin(MockEppLib):
|
||||||
It then retrieves the queryset for the 'investigator' dropdown from DomainApplicationAdmin
|
It then retrieves the queryset for the 'investigator' dropdown from DomainApplicationAdmin
|
||||||
and checks that it matches the expected queryset, which only includes staff users.
|
and checks that it matches the expected queryset, which only includes staff users.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
|
with less_console_noise():
|
||||||
# Create a mock DomainApplication object, with a fake investigator
|
# Create a mock DomainApplication object, with a fake investigator
|
||||||
application: DomainApplication = generic_domain_object("application", "SomeGuy")
|
application: DomainApplication = generic_domain_object("application", "SomeGuy")
|
||||||
investigator_user = User.objects.filter(username=application.investigator.username).get()
|
investigator_user = User.objects.filter(username=application.investigator.username).get()
|
||||||
|
@ -1211,6 +1481,7 @@ class TestDomainApplicationAdmin(MockEppLib):
|
||||||
This test verifies that filter list for the 'investigator'
|
This test verifies that filter list for the 'investigator'
|
||||||
is displayed alphabetically
|
is displayed alphabetically
|
||||||
"""
|
"""
|
||||||
|
with less_console_noise():
|
||||||
# Create a mock DomainApplication object, with a fake investigator
|
# Create a mock DomainApplication object, with a fake investigator
|
||||||
application: DomainApplication = generic_domain_object("application", "SomeGuy")
|
application: DomainApplication = generic_domain_object("application", "SomeGuy")
|
||||||
investigator_user = User.objects.filter(username=application.investigator.username).get()
|
investigator_user = User.objects.filter(username=application.investigator.username).get()
|
||||||
|
@ -1273,6 +1544,7 @@ class DomainInvitationAdminTest(TestCase):
|
||||||
|
|
||||||
def test_get_filters(self):
|
def test_get_filters(self):
|
||||||
"""Ensures that our filters are displaying correctly"""
|
"""Ensures that our filters are displaying correctly"""
|
||||||
|
with less_console_noise():
|
||||||
# Have to get creative to get past linter
|
# Have to get creative to get past linter
|
||||||
p = "adminpass"
|
p = "adminpass"
|
||||||
self.client.login(username="superuser", password=p)
|
self.client.login(username="superuser", password=p)
|
||||||
|
@ -1350,6 +1622,7 @@ class TestDomainInformationAdmin(TestCase):
|
||||||
User.objects.all().delete()
|
User.objects.all().delete()
|
||||||
|
|
||||||
def test_readonly_fields_for_analyst(self):
|
def test_readonly_fields_for_analyst(self):
|
||||||
|
with less_console_noise():
|
||||||
"""Ensures that analysts have their permissions setup correctly"""
|
"""Ensures that analysts have their permissions setup correctly"""
|
||||||
request = self.factory.get("/")
|
request = self.factory.get("/")
|
||||||
request.user = self.staffuser
|
request.user = self.staffuser
|
||||||
|
@ -1372,6 +1645,7 @@ class TestDomainInformationAdmin(TestCase):
|
||||||
|
|
||||||
def test_domain_sortable(self):
|
def test_domain_sortable(self):
|
||||||
"""Tests if DomainInformation sorts by domain correctly"""
|
"""Tests if DomainInformation sorts by domain correctly"""
|
||||||
|
with less_console_noise():
|
||||||
p = "adminpass"
|
p = "adminpass"
|
||||||
self.client.login(username="superuser", password=p)
|
self.client.login(username="superuser", password=p)
|
||||||
|
|
||||||
|
@ -1383,6 +1657,7 @@ class TestDomainInformationAdmin(TestCase):
|
||||||
|
|
||||||
def test_submitter_sortable(self):
|
def test_submitter_sortable(self):
|
||||||
"""Tests if DomainInformation sorts by submitter correctly"""
|
"""Tests if DomainInformation sorts by submitter correctly"""
|
||||||
|
with less_console_noise():
|
||||||
p = "adminpass"
|
p = "adminpass"
|
||||||
self.client.login(username="superuser", password=p)
|
self.client.login(username="superuser", password=p)
|
||||||
|
|
||||||
|
@ -1420,6 +1695,7 @@ class UserDomainRoleAdminTest(TestCase):
|
||||||
|
|
||||||
def test_domain_sortable(self):
|
def test_domain_sortable(self):
|
||||||
"""Tests if the UserDomainrole sorts by domain correctly"""
|
"""Tests if the UserDomainrole sorts by domain correctly"""
|
||||||
|
with less_console_noise():
|
||||||
p = "adminpass"
|
p = "adminpass"
|
||||||
self.client.login(username="superuser", password=p)
|
self.client.login(username="superuser", password=p)
|
||||||
|
|
||||||
|
@ -1441,6 +1717,7 @@ class UserDomainRoleAdminTest(TestCase):
|
||||||
|
|
||||||
def test_user_sortable(self):
|
def test_user_sortable(self):
|
||||||
"""Tests if the UserDomainrole sorts by user correctly"""
|
"""Tests if the UserDomainrole sorts by user correctly"""
|
||||||
|
with less_console_noise():
|
||||||
p = "adminpass"
|
p = "adminpass"
|
||||||
self.client.login(username="superuser", password=p)
|
self.client.login(username="superuser", password=p)
|
||||||
|
|
||||||
|
@ -1463,6 +1740,7 @@ class UserDomainRoleAdminTest(TestCase):
|
||||||
def test_email_not_in_search(self):
|
def test_email_not_in_search(self):
|
||||||
"""Tests the search bar in Django Admin for UserDomainRoleAdmin.
|
"""Tests the search bar in Django Admin for UserDomainRoleAdmin.
|
||||||
Should return no results for an invalid email."""
|
Should return no results for an invalid email."""
|
||||||
|
with less_console_noise():
|
||||||
# Have to get creative to get past linter
|
# Have to get creative to get past linter
|
||||||
p = "adminpass"
|
p = "adminpass"
|
||||||
self.client.login(username="superuser", password=p)
|
self.client.login(username="superuser", password=p)
|
||||||
|
@ -1495,6 +1773,7 @@ class UserDomainRoleAdminTest(TestCase):
|
||||||
def test_email_in_search(self):
|
def test_email_in_search(self):
|
||||||
"""Tests the search bar in Django Admin for UserDomainRoleAdmin.
|
"""Tests the search bar in Django Admin for UserDomainRoleAdmin.
|
||||||
Should return results for an valid email."""
|
Should return results for an valid email."""
|
||||||
|
with less_console_noise():
|
||||||
# Have to get creative to get past linter
|
# Have to get creative to get past linter
|
||||||
p = "adminpass"
|
p = "adminpass"
|
||||||
self.client.login(username="superuser", password=p)
|
self.client.login(username="superuser", password=p)
|
||||||
|
@ -1604,6 +1883,7 @@ class MyUserAdminTest(TestCase):
|
||||||
self.admin = MyUserAdmin(model=get_user_model(), admin_site=admin_site)
|
self.admin = MyUserAdmin(model=get_user_model(), admin_site=admin_site)
|
||||||
|
|
||||||
def test_list_display_without_username(self):
|
def test_list_display_without_username(self):
|
||||||
|
with less_console_noise():
|
||||||
request = self.client.request().wsgi_request
|
request = self.client.request().wsgi_request
|
||||||
request.user = create_user()
|
request.user = create_user()
|
||||||
|
|
||||||
|
@ -1620,6 +1900,7 @@ class MyUserAdminTest(TestCase):
|
||||||
self.assertNotIn("username", list_display)
|
self.assertNotIn("username", list_display)
|
||||||
|
|
||||||
def test_get_fieldsets_superuser(self):
|
def test_get_fieldsets_superuser(self):
|
||||||
|
with less_console_noise():
|
||||||
request = self.client.request().wsgi_request
|
request = self.client.request().wsgi_request
|
||||||
request.user = create_superuser()
|
request.user = create_superuser()
|
||||||
fieldsets = self.admin.get_fieldsets(request)
|
fieldsets = self.admin.get_fieldsets(request)
|
||||||
|
@ -1627,6 +1908,7 @@ class MyUserAdminTest(TestCase):
|
||||||
self.assertEqual(fieldsets, expected_fieldsets)
|
self.assertEqual(fieldsets, expected_fieldsets)
|
||||||
|
|
||||||
def test_get_fieldsets_cisa_analyst(self):
|
def test_get_fieldsets_cisa_analyst(self):
|
||||||
|
with less_console_noise():
|
||||||
request = self.client.request().wsgi_request
|
request = self.client.request().wsgi_request
|
||||||
request.user = create_user()
|
request.user = create_user()
|
||||||
fieldsets = self.admin.get_fieldsets(request)
|
fieldsets = self.admin.get_fieldsets(request)
|
||||||
|
@ -1695,6 +1977,7 @@ class AuditedAdminTest(TestCase):
|
||||||
|
|
||||||
# This test case should be refactored in general, as it is too overly specific and engineered
|
# 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):
|
def test_alphabetically_sorted_fk_fields_domain_application(self):
|
||||||
|
with less_console_noise():
|
||||||
tested_fields = [
|
tested_fields = [
|
||||||
DomainApplication.authorizing_official.field,
|
DomainApplication.authorizing_official.field,
|
||||||
DomainApplication.submitter.field,
|
DomainApplication.submitter.field,
|
||||||
|
@ -1752,6 +2035,7 @@ class AuditedAdminTest(TestCase):
|
||||||
)
|
)
|
||||||
|
|
||||||
def test_alphabetically_sorted_fk_fields_domain_information(self):
|
def test_alphabetically_sorted_fk_fields_domain_information(self):
|
||||||
|
with less_console_noise():
|
||||||
tested_fields = [
|
tested_fields = [
|
||||||
DomainInformation.authorizing_official.field,
|
DomainInformation.authorizing_official.field,
|
||||||
DomainInformation.submitter.field,
|
DomainInformation.submitter.field,
|
||||||
|
@ -1811,6 +2095,7 @@ class AuditedAdminTest(TestCase):
|
||||||
)
|
)
|
||||||
|
|
||||||
def test_alphabetically_sorted_fk_fields_domain_invitation(self):
|
def test_alphabetically_sorted_fk_fields_domain_invitation(self):
|
||||||
|
with less_console_noise():
|
||||||
tested_fields = [DomainInvitation.domain.field]
|
tested_fields = [DomainInvitation.domain.field]
|
||||||
|
|
||||||
# Creates multiple domain applications - review status does not matter
|
# Creates multiple domain applications - review status does not matter
|
||||||
|
@ -1884,6 +2169,7 @@ class DomainSessionVariableTest(TestCase):
|
||||||
def test_session_vars_set_correctly(self):
|
def test_session_vars_set_correctly(self):
|
||||||
"""Checks if session variables are being set correctly"""
|
"""Checks if session variables are being set correctly"""
|
||||||
|
|
||||||
|
with less_console_noise():
|
||||||
p = "adminpass"
|
p = "adminpass"
|
||||||
self.client.login(username="superuser", password=p)
|
self.client.login(username="superuser", password=p)
|
||||||
|
|
||||||
|
@ -1899,6 +2185,7 @@ class DomainSessionVariableTest(TestCase):
|
||||||
def test_session_vars_set_correctly_hardcoded_domain(self):
|
def test_session_vars_set_correctly_hardcoded_domain(self):
|
||||||
"""Checks if session variables are being set correctly"""
|
"""Checks if session variables are being set correctly"""
|
||||||
|
|
||||||
|
with less_console_noise():
|
||||||
p = "adminpass"
|
p = "adminpass"
|
||||||
self.client.login(username="superuser", password=p)
|
self.client.login(username="superuser", password=p)
|
||||||
|
|
||||||
|
@ -1913,6 +2200,7 @@ class DomainSessionVariableTest(TestCase):
|
||||||
def test_session_variables_reset_correctly(self):
|
def test_session_variables_reset_correctly(self):
|
||||||
"""Checks if incorrect session variables get overridden"""
|
"""Checks if incorrect session variables get overridden"""
|
||||||
|
|
||||||
|
with less_console_noise():
|
||||||
p = "adminpass"
|
p = "adminpass"
|
||||||
self.client.login(username="superuser", password=p)
|
self.client.login(username="superuser", password=p)
|
||||||
|
|
||||||
|
@ -1930,6 +2218,7 @@ class DomainSessionVariableTest(TestCase):
|
||||||
def test_session_variables_retain_information(self):
|
def test_session_variables_retain_information(self):
|
||||||
"""Checks to see if session variables retain old information"""
|
"""Checks to see if session variables retain old information"""
|
||||||
|
|
||||||
|
with less_console_noise():
|
||||||
p = "adminpass"
|
p = "adminpass"
|
||||||
self.client.login(username="superuser", password=p)
|
self.client.login(username="superuser", password=p)
|
||||||
|
|
||||||
|
@ -1944,6 +2233,7 @@ class DomainSessionVariableTest(TestCase):
|
||||||
def test_session_variables_concurrent_requests(self):
|
def test_session_variables_concurrent_requests(self):
|
||||||
"""Simulates two requests at once"""
|
"""Simulates two requests at once"""
|
||||||
|
|
||||||
|
with less_console_noise():
|
||||||
p = "adminpass"
|
p = "adminpass"
|
||||||
self.client.login(username="superuser", password=p)
|
self.client.login(username="superuser", password=p)
|
||||||
|
|
||||||
|
@ -2003,6 +2293,7 @@ class ContactAdminTest(TestCase):
|
||||||
self.staffuser = create_user()
|
self.staffuser = create_user()
|
||||||
|
|
||||||
def test_readonly_when_restricted_staffuser(self):
|
def test_readonly_when_restricted_staffuser(self):
|
||||||
|
with less_console_noise():
|
||||||
request = self.factory.get("/")
|
request = self.factory.get("/")
|
||||||
request.user = self.staffuser
|
request.user = self.staffuser
|
||||||
|
|
||||||
|
@ -2015,6 +2306,7 @@ class ContactAdminTest(TestCase):
|
||||||
self.assertEqual(readonly_fields, expected_fields)
|
self.assertEqual(readonly_fields, expected_fields)
|
||||||
|
|
||||||
def test_readonly_when_restricted_superuser(self):
|
def test_readonly_when_restricted_superuser(self):
|
||||||
|
with less_console_noise():
|
||||||
request = self.factory.get("/")
|
request = self.factory.get("/")
|
||||||
request.user = self.superuser
|
request.user = self.superuser
|
||||||
|
|
||||||
|
@ -2028,6 +2320,7 @@ class ContactAdminTest(TestCase):
|
||||||
"""Create a contact, join it to 4 domain requests. The 5th join will be a user.
|
"""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."""
|
Assert that the warning on the contact form lists 5 joins."""
|
||||||
|
|
||||||
|
with less_console_noise():
|
||||||
self.client.force_login(self.superuser)
|
self.client.force_login(self.superuser)
|
||||||
|
|
||||||
# Create an instance of the model
|
# Create an instance of the model
|
||||||
|
@ -2109,6 +2402,7 @@ class VerifiedByStaffAdminTestCase(TestCase):
|
||||||
self.factory = RequestFactory()
|
self.factory = RequestFactory()
|
||||||
|
|
||||||
def test_save_model_sets_user_field(self):
|
def test_save_model_sets_user_field(self):
|
||||||
|
with less_console_noise():
|
||||||
self.client.force_login(self.superuser)
|
self.client.force_login(self.superuser)
|
||||||
|
|
||||||
# Create an instance of the admin class
|
# Create an instance of the admin class
|
||||||
|
|
|
@ -574,6 +574,56 @@ class TestDomainApplication(TestCase):
|
||||||
with self.assertRaises(TransitionNotAllowed):
|
with self.assertRaises(TransitionNotAllowed):
|
||||||
self.approved_application.reject_with_prejudice()
|
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):
|
def test_has_rationale_returns_true(self):
|
||||||
"""has_rationale() returns true when an application has no_other_contacts_rationale"""
|
"""has_rationale() returns true when an application has no_other_contacts_rationale"""
|
||||||
with less_console_noise():
|
with less_console_noise():
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue