mirror of
https://github.com/cisagov/manage.get.gov.git
synced 2025-08-13 21:19:42 +02:00
Merge branch 'main' into dk/1751-oidc-outages
This commit is contained in:
commit
1896bf5380
12 changed files with 430 additions and 302 deletions
|
@ -884,14 +884,11 @@ class DomainApplicationAdmin(ListHeaderAdmin):
|
||||||
if (
|
if (
|
||||||
obj
|
obj
|
||||||
and original_obj.status == models.DomainApplication.ApplicationStatus.APPROVED
|
and original_obj.status == models.DomainApplication.ApplicationStatus.APPROVED
|
||||||
and (
|
and obj.status != models.DomainApplication.ApplicationStatus.APPROVED
|
||||||
obj.status == models.DomainApplication.ApplicationStatus.REJECTED
|
|
||||||
or obj.status == models.DomainApplication.ApplicationStatus.INELIGIBLE
|
|
||||||
)
|
|
||||||
and not obj.domain_is_not_active()
|
and not obj.domain_is_not_active()
|
||||||
):
|
):
|
||||||
# If an admin tried to set an approved application to
|
# If an admin tried to set an approved application to
|
||||||
# rejected or ineligible and the related domain is already
|
# another status and the related domain is already
|
||||||
# active, shortcut the action and throw a friendly
|
# active, shortcut the action and throw a friendly
|
||||||
# error message. This action would still not go through
|
# error message. This action would still not go through
|
||||||
# shortcut or not as the rules are duplicated on the model,
|
# shortcut or not as the rules are duplicated on the model,
|
||||||
|
|
|
@ -18,4 +18,7 @@
|
||||||
left: 1rem !important;
|
left: 1rem !important;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
.usa-alert__body.margin-left-1 {
|
||||||
|
margin-left: 0.5rem!important;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -116,6 +116,10 @@ in the form $setting: value,
|
||||||
$theme-color-success-light: $dhs-green-30,
|
$theme-color-success-light: $dhs-green-30,
|
||||||
$theme-color-success-lighter: $dhs-green-15,
|
$theme-color-success-lighter: $dhs-green-15,
|
||||||
|
|
||||||
|
/*---------------------------
|
||||||
|
## Emergency state
|
||||||
|
----------------------------*/
|
||||||
|
$theme-color-emergency: #FFC3F9,
|
||||||
|
|
||||||
/*---------------------------
|
/*---------------------------
|
||||||
# Input settings
|
# Input settings
|
||||||
|
|
|
@ -578,6 +578,19 @@ class DomainApplication(TimeStampedModel):
|
||||||
return not self.approved_domain.is_active()
|
return not self.approved_domain.is_active()
|
||||||
return True
|
return True
|
||||||
|
|
||||||
|
def delete_and_clean_up_domain(self, called_from):
|
||||||
|
try:
|
||||||
|
domain_state = self.approved_domain.state
|
||||||
|
# Only reject if it exists on EPP
|
||||||
|
if domain_state != Domain.State.UNKNOWN:
|
||||||
|
self.approved_domain.deletedInEpp()
|
||||||
|
self.approved_domain.save()
|
||||||
|
self.approved_domain.delete()
|
||||||
|
self.approved_domain = None
|
||||||
|
except Exception as err:
|
||||||
|
logger.error(err)
|
||||||
|
logger.error(f"Can't query an approved domain while attempting {called_from}")
|
||||||
|
|
||||||
def _send_status_update_email(self, new_status, email_template, email_template_subject, send_email=True):
|
def _send_status_update_email(self, new_status, email_template, email_template_subject, send_email=True):
|
||||||
"""Send a status update email to the submitter.
|
"""Send a status update email to the submitter.
|
||||||
|
|
||||||
|
@ -641,6 +654,10 @@ class DomainApplication(TimeStampedModel):
|
||||||
self.submission_date = timezone.now().date()
|
self.submission_date = timezone.now().date()
|
||||||
self.save()
|
self.save()
|
||||||
|
|
||||||
|
# Limit email notifications to transitions from Started and Withdrawn
|
||||||
|
limited_statuses = [self.ApplicationStatus.STARTED, self.ApplicationStatus.WITHDRAWN]
|
||||||
|
|
||||||
|
if self.status in limited_statuses:
|
||||||
self._send_status_update_email(
|
self._send_status_update_email(
|
||||||
"submission confirmation",
|
"submission confirmation",
|
||||||
"emails/submission_confirmation.txt",
|
"emails/submission_confirmation.txt",
|
||||||
|
@ -657,11 +674,19 @@ class DomainApplication(TimeStampedModel):
|
||||||
ApplicationStatus.INELIGIBLE,
|
ApplicationStatus.INELIGIBLE,
|
||||||
],
|
],
|
||||||
target=ApplicationStatus.IN_REVIEW,
|
target=ApplicationStatus.IN_REVIEW,
|
||||||
|
conditions=[domain_is_not_active],
|
||||||
)
|
)
|
||||||
def in_review(self):
|
def in_review(self):
|
||||||
"""Investigate an application that has been submitted.
|
"""Investigate an application that has been submitted.
|
||||||
|
|
||||||
This action is logged."""
|
This action is logged.
|
||||||
|
|
||||||
|
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")
|
||||||
|
|
||||||
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"
|
||||||
|
@ -676,11 +701,19 @@ class DomainApplication(TimeStampedModel):
|
||||||
ApplicationStatus.INELIGIBLE,
|
ApplicationStatus.INELIGIBLE,
|
||||||
],
|
],
|
||||||
target=ApplicationStatus.ACTION_NEEDED,
|
target=ApplicationStatus.ACTION_NEEDED,
|
||||||
|
conditions=[domain_is_not_active],
|
||||||
)
|
)
|
||||||
def action_needed(self):
|
def action_needed(self):
|
||||||
"""Send back an application that is under investigation or rejected.
|
"""Send back an application that is under investigation or rejected.
|
||||||
|
|
||||||
This action is logged."""
|
This action is logged.
|
||||||
|
|
||||||
|
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")
|
||||||
|
|
||||||
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"
|
||||||
|
@ -735,6 +768,7 @@ class DomainApplication(TimeStampedModel):
|
||||||
)
|
)
|
||||||
def withdraw(self):
|
def withdraw(self):
|
||||||
"""Withdraw an application that has been submitted."""
|
"""Withdraw an application that has been submitted."""
|
||||||
|
|
||||||
self._send_status_update_email(
|
self._send_status_update_email(
|
||||||
"withdraw",
|
"withdraw",
|
||||||
"emails/domain_request_withdrawn.txt",
|
"emails/domain_request_withdrawn.txt",
|
||||||
|
@ -752,18 +786,9 @@ class DomainApplication(TimeStampedModel):
|
||||||
|
|
||||||
As side effects this will delete the domain and domain_information
|
As side effects this will delete the domain and domain_information
|
||||||
(will cascade), and send an email notification."""
|
(will cascade), and send an email notification."""
|
||||||
|
|
||||||
if self.status == self.ApplicationStatus.APPROVED:
|
if self.status == self.ApplicationStatus.APPROVED:
|
||||||
try:
|
self.delete_and_clean_up_domain("reject")
|
||||||
domain_state = self.approved_domain.state
|
|
||||||
# Only reject if it exists on EPP
|
|
||||||
if domain_state != Domain.State.UNKNOWN:
|
|
||||||
self.approved_domain.deletedInEpp()
|
|
||||||
self.approved_domain.save()
|
|
||||||
self.approved_domain.delete()
|
|
||||||
self.approved_domain = None
|
|
||||||
except Exception as err:
|
|
||||||
logger.error(err)
|
|
||||||
logger.error("Can't query an approved domain while attempting a DA reject()")
|
|
||||||
|
|
||||||
self._send_status_update_email(
|
self._send_status_update_email(
|
||||||
"action needed",
|
"action needed",
|
||||||
|
@ -792,17 +817,7 @@ class DomainApplication(TimeStampedModel):
|
||||||
and domain_information (will cascade) when they exist."""
|
and domain_information (will cascade) when they exist."""
|
||||||
|
|
||||||
if self.status == self.ApplicationStatus.APPROVED:
|
if self.status == self.ApplicationStatus.APPROVED:
|
||||||
try:
|
self.delete_and_clean_up_domain("reject_with_prejudice")
|
||||||
domain_state = self.approved_domain.state
|
|
||||||
# Only reject if it exists on EPP
|
|
||||||
if domain_state != Domain.State.UNKNOWN:
|
|
||||||
self.approved_domain.deletedInEpp()
|
|
||||||
self.approved_domain.save()
|
|
||||||
self.approved_domain.delete()
|
|
||||||
self.approved_domain = None
|
|
||||||
except Exception as err:
|
|
||||||
logger.error(err)
|
|
||||||
logger.error("Can't query an approved domain while attempting a DA reject_with_prejudice()")
|
|
||||||
|
|
||||||
self.creator.restrict_user()
|
self.creator.restrict_user()
|
||||||
|
|
||||||
|
|
|
@ -24,21 +24,39 @@
|
||||||
{% block title %}{% if subtitle %}{{ subtitle }} | {% endif %}{{ title }} | {{ site_title|default:_('Django site admin') }}{% endblock %}
|
{% block title %}{% if subtitle %}{{ subtitle }} | {% endif %}{{ title }} | {{ site_title|default:_('Django site admin') }}{% endblock %}
|
||||||
|
|
||||||
{% block extrastyle %}{{ block.super }}
|
{% block extrastyle %}{{ block.super }}
|
||||||
<link rel="stylesheet" type="text/css" href="{% static "css/styles.css" %}" />
|
<link rel="stylesheet" type="text/css" href="{% static 'css/styles.css' %}" />
|
||||||
{% endblock %}
|
{% endblock %}
|
||||||
|
|
||||||
{% block branding %}
|
{% block header %}
|
||||||
<h1 id="site-name"><a href="{% url 'admin:index' %}">.gov admin</a></h1>
|
{% if not IS_PRODUCTION %}
|
||||||
{% if user.is_anonymous %}
|
{% with add_body_class="margin-left-1" %}
|
||||||
|
{% include "includes/non-production-alert.html" %}
|
||||||
|
{% endwith %}
|
||||||
|
{% endif %}
|
||||||
|
|
||||||
|
{# Djando update: this div will change to header #}
|
||||||
|
<div id="header">
|
||||||
|
<div id="branding">
|
||||||
|
{% block branding %}
|
||||||
|
<h1 id="site-name"><a href="{% url 'admin:index' %}">.gov admin</a></h1>
|
||||||
|
{% if user.is_anonymous %}
|
||||||
{% include "admin/color_theme_toggle.html" %}
|
{% include "admin/color_theme_toggle.html" %}
|
||||||
{% endif %}
|
{% endif %}
|
||||||
{% endblock %}
|
{% endblock %}
|
||||||
{% comment %}
|
</div>
|
||||||
|
{% block usertools %}
|
||||||
|
{% if has_permission %}
|
||||||
|
<div id="user-tools">
|
||||||
|
{% block welcome-msg %}
|
||||||
|
{% translate 'Welcome,' %}
|
||||||
|
<strong>{% firstof user.get_short_name user.get_username %}</strong>.
|
||||||
|
{% endblock %}
|
||||||
|
{% comment %}
|
||||||
This was copied from the 'userlinks' template, with a few minor changes.
|
This was copied from the 'userlinks' template, with a few minor changes.
|
||||||
You can find that here:
|
You can find that here:
|
||||||
https://github.com/django/django/blob/d25f3892114466d689fd6936f79f3bd9a9acc30e/django/contrib/admin/templates/admin/base.html#L59
|
https://github.com/django/django/blob/d25f3892114466d689fd6936f79f3bd9a9acc30e/django/contrib/admin/templates/admin/base.html#L59
|
||||||
{% endcomment %}
|
{% endcomment %}
|
||||||
{% block userlinks %}
|
{% block userlinks %}
|
||||||
{% if site_url %}
|
{% if site_url %}
|
||||||
<a href="{{ site_url }}">{% translate 'View site' %}</a> /
|
<a href="{{ site_url }}">{% translate 'View site' %}</a> /
|
||||||
{% endif %}
|
{% endif %}
|
||||||
|
@ -54,4 +72,9 @@
|
||||||
<a href="{% url 'admin:logout' %}" id="admin-logout-button">{% translate 'Log out' %}</a>
|
<a href="{% url 'admin:logout' %}" id="admin-logout-button">{% translate 'Log out' %}</a>
|
||||||
{% include "admin/color_theme_toggle.html" %}
|
{% include "admin/color_theme_toggle.html" %}
|
||||||
{% endblock %}
|
{% endblock %}
|
||||||
{% block nav-global %}{% endblock %}
|
</div>
|
||||||
|
{% endif %}
|
||||||
|
{% endblock %}
|
||||||
|
{% block nav-global %}{% endblock %}
|
||||||
|
</div>
|
||||||
|
{% endblock %}
|
||||||
|
|
|
@ -70,6 +70,10 @@
|
||||||
<script src="{% static 'js/uswds.min.js' %}" defer></script>
|
<script src="{% static 'js/uswds.min.js' %}" defer></script>
|
||||||
<a class="usa-skipnav" href="#main-content">Skip to main content</a>
|
<a class="usa-skipnav" href="#main-content">Skip to main content</a>
|
||||||
|
|
||||||
|
{% if not IS_PRODUCTION %}
|
||||||
|
{% include "includes/non-production-alert.html" %}
|
||||||
|
{% endif %}
|
||||||
|
|
||||||
<section class="usa-banner" aria-label="Official website of the United States government">
|
<section class="usa-banner" aria-label="Official website of the United States government">
|
||||||
<div class="usa-accordion">
|
<div class="usa-accordion">
|
||||||
<header class="usa-banner__header">
|
<header class="usa-banner__header">
|
||||||
|
|
|
@ -18,7 +18,7 @@
|
||||||
<button
|
<button
|
||||||
type="submit"
|
type="submit"
|
||||||
class="usa-button"
|
class="usa-button"
|
||||||
>Add user</button>
|
>Add a domain manager</button>
|
||||||
</form>
|
</form>
|
||||||
|
|
||||||
{% endblock %} {# domain_content #}
|
{% endblock %} {# domain_content #}
|
||||||
|
|
|
@ -0,0 +1,5 @@
|
||||||
|
<div class="usa-alert usa-alert--emergency margin-y-0 {% if add_class %}{{ add_class }}{% endif %}">
|
||||||
|
<div class="usa-alert__body {% if add_body_class %}{{ add_body_class }}{% endif %}">
|
||||||
|
<b>Attention:</b> You are on a test site.
|
||||||
|
</div>
|
||||||
|
</div>
|
|
@ -306,6 +306,7 @@ class TestDomainApplicationAdminForm(TestCase):
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
|
@boto3_mocking.patching
|
||||||
class TestDomainApplicationAdmin(MockEppLib):
|
class TestDomainApplicationAdmin(MockEppLib):
|
||||||
def setUp(self):
|
def setUp(self):
|
||||||
super().setUp()
|
super().setUp()
|
||||||
|
@ -411,83 +412,166 @@ 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")
|
||||||
|
|
||||||
@boto3_mocking.patching
|
def transition_state_and_send_email(self, application, status):
|
||||||
def test_save_model_sends_submitted_email(self):
|
"""Helper method for the email test cases."""
|
||||||
# make sure there is no user with this email
|
|
||||||
EMAIL = "mayor@igorville.gov"
|
|
||||||
User.objects.filter(email=EMAIL).delete()
|
|
||||||
|
|
||||||
with boto3_mocking.clients.handler_for("sesv2", self.mock_client):
|
with boto3_mocking.clients.handler_for("sesv2", self.mock_client):
|
||||||
with less_console_noise():
|
with less_console_noise():
|
||||||
|
# Create a mock request
|
||||||
|
request = self.factory.post("/admin/registrar/domainapplication/{}/change/".format(application.pk))
|
||||||
|
|
||||||
|
# Modify the application's property
|
||||||
|
application.status = status
|
||||||
|
|
||||||
|
# Use the model admin's save_model method
|
||||||
|
self.admin.save_model(request, application, form=None, change=True)
|
||||||
|
|
||||||
|
def assert_email_is_accurate(self, expected_string, email_index, email_address):
|
||||||
|
"""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"]
|
||||||
|
|
||||||
|
# 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)
|
||||||
|
|
||||||
|
def test_save_model_sends_submitted_email(self):
|
||||||
|
"""When transitioning to submitted from started or withdrawn on a domain request,
|
||||||
|
an email is sent out.
|
||||||
|
|
||||||
|
When transitioning to submitted from dns needed or in review on a domain request,
|
||||||
|
no email is sent out."""
|
||||||
|
|
||||||
|
# Ensure there is no user with this email
|
||||||
|
EMAIL = "mayor@igorville.gov"
|
||||||
|
User.objects.filter(email=EMAIL).delete()
|
||||||
|
|
||||||
# Create a sample application
|
# Create a sample application
|
||||||
application = completed_application()
|
application = completed_application()
|
||||||
|
|
||||||
# Create a mock request
|
# Test Submitted Status from started
|
||||||
request = self.factory.post("/admin/registrar/domainapplication/{}/change/".format(application.pk))
|
self.transition_state_and_send_email(application, DomainApplication.ApplicationStatus.SUBMITTED)
|
||||||
|
self.assert_email_is_accurate("We received your .gov domain request.", 0, EMAIL)
|
||||||
# Modify the application's property
|
|
||||||
application.status = DomainApplication.ApplicationStatus.SUBMITTED
|
|
||||||
|
|
||||||
# Use the model admin's save_model method
|
|
||||||
self.admin.save_model(request, application, form=None, change=True)
|
|
||||||
|
|
||||||
# Access the arguments passed to send_email
|
|
||||||
call_args = self.mock_client.EMAILS_SENT
|
|
||||||
kwargs = call_args[0]["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"]
|
|
||||||
|
|
||||||
# Assert or perform other checks on the email details
|
|
||||||
expected_string = "We received your .gov domain request."
|
|
||||||
self.assertEqual(from_email, settings.DEFAULT_FROM_EMAIL)
|
|
||||||
self.assertEqual(to_email, EMAIL)
|
|
||||||
self.assertIn(expected_string, email_body)
|
|
||||||
|
|
||||||
self.assertEqual(len(self.mock_client.EMAILS_SENT), 1)
|
self.assertEqual(len(self.mock_client.EMAILS_SENT), 1)
|
||||||
|
|
||||||
@boto3_mocking.patching
|
# 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.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)
|
||||||
|
|
||||||
|
# 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)
|
||||||
|
|
||||||
|
# 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):
|
def test_save_model_sends_approved_email(self):
|
||||||
# make sure there is no user with this email
|
"""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"
|
EMAIL = "mayor@igorville.gov"
|
||||||
User.objects.filter(email=EMAIL).delete()
|
User.objects.filter(email=EMAIL).delete()
|
||||||
|
|
||||||
with boto3_mocking.clients.handler_for("sesv2", self.mock_client):
|
|
||||||
with less_console_noise():
|
|
||||||
# Create a sample application
|
# Create a sample application
|
||||||
application = completed_application(status=DomainApplication.ApplicationStatus.IN_REVIEW)
|
application = completed_application(status=DomainApplication.ApplicationStatus.IN_REVIEW)
|
||||||
|
|
||||||
# Create a mock request
|
# Test Submitted Status
|
||||||
request = self.factory.post("/admin/registrar/domainapplication/{}/change/".format(application.pk))
|
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)
|
||||||
# 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)
|
|
||||||
|
|
||||||
# Access the arguments passed to send_email
|
|
||||||
call_args = self.mock_client.EMAILS_SENT
|
|
||||||
kwargs = call_args[0]["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"]
|
|
||||||
|
|
||||||
# Assert or perform other checks on the email details
|
|
||||||
expected_string = "Congratulations! Your .gov domain request has been approved."
|
|
||||||
self.assertEqual(from_email, settings.DEFAULT_FROM_EMAIL)
|
|
||||||
self.assertEqual(to_email, EMAIL)
|
|
||||||
self.assertIn(expected_string, email_body)
|
|
||||||
|
|
||||||
self.assertEqual(len(self.mock_client.EMAILS_SENT), 1)
|
self.assertEqual(len(self.mock_client.EMAILS_SENT), 1)
|
||||||
|
|
||||||
@boto3_mocking.patching
|
# 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)
|
||||||
|
|
||||||
|
# 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)
|
||||||
|
|
||||||
|
def test_save_model_sends_rejected_email(self):
|
||||||
|
"""When transitioning to rejected 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()
|
||||||
|
|
||||||
|
# Create a sample application
|
||||||
|
application = completed_application(status=DomainApplication.ApplicationStatus.IN_REVIEW)
|
||||||
|
|
||||||
|
# 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)
|
||||||
|
|
||||||
|
# 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)
|
||||||
|
|
||||||
|
# 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)
|
||||||
|
|
||||||
|
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()
|
||||||
|
|
||||||
|
# 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 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)
|
||||||
|
|
||||||
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
|
||||||
EMAIL = "mayor@igorville.gov"
|
EMAIL = "mayor@igorville.gov"
|
||||||
|
@ -510,45 +594,6 @@ class TestDomainApplicationAdmin(MockEppLib):
|
||||||
# Test that approved domain exists and equals requested domain
|
# Test that approved domain exists and equals requested domain
|
||||||
self.assertEqual(application.requested_domain.name, application.approved_domain.name)
|
self.assertEqual(application.requested_domain.name, application.approved_domain.name)
|
||||||
|
|
||||||
@boto3_mocking.patching
|
|
||||||
def test_save_model_sends_rejected_email(self):
|
|
||||||
# make sure there is no user with this email
|
|
||||||
EMAIL = "mayor@igorville.gov"
|
|
||||||
User.objects.filter(email=EMAIL).delete()
|
|
||||||
|
|
||||||
with boto3_mocking.clients.handler_for("sesv2", self.mock_client):
|
|
||||||
with less_console_noise():
|
|
||||||
# Create a sample application
|
|
||||||
application = completed_application(status=DomainApplication.ApplicationStatus.IN_REVIEW)
|
|
||||||
|
|
||||||
# Create a mock request
|
|
||||||
request = self.factory.post("/admin/registrar/domainapplication/{}/change/".format(application.pk))
|
|
||||||
|
|
||||||
# Modify the application's property
|
|
||||||
application.status = DomainApplication.ApplicationStatus.REJECTED
|
|
||||||
|
|
||||||
# Use the model admin's save_model method
|
|
||||||
self.admin.save_model(request, application, form=None, change=True)
|
|
||||||
|
|
||||||
# Access the arguments passed to send_email
|
|
||||||
call_args = self.mock_client.EMAILS_SENT
|
|
||||||
kwargs = call_args[0]["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"]
|
|
||||||
|
|
||||||
# Assert or perform other checks on the email details
|
|
||||||
expected_string = "Your .gov domain request has been rejected."
|
|
||||||
self.assertEqual(from_email, settings.DEFAULT_FROM_EMAIL)
|
|
||||||
self.assertEqual(to_email, EMAIL)
|
|
||||||
self.assertIn(expected_string, email_body)
|
|
||||||
|
|
||||||
self.assertEqual(len(self.mock_client.EMAILS_SENT), 1)
|
|
||||||
|
|
||||||
@boto3_mocking.patching
|
|
||||||
def test_save_model_sets_restricted_status_on_user(self):
|
def test_save_model_sets_restricted_status_on_user(self):
|
||||||
# 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"
|
||||||
|
@ -699,41 +744,13 @@ class TestDomainApplicationAdmin(MockEppLib):
|
||||||
"Cannot edit an application with a restricted creator.",
|
"Cannot edit an application with a restricted creator.",
|
||||||
)
|
)
|
||||||
|
|
||||||
@boto3_mocking.patching
|
def trigger_saving_approved_to_another_state(self, domain_is_active, another_state):
|
||||||
def test_error_when_saving_approved_to_rejected_and_domain_is_active(self):
|
"""Helper method that triggers domain request state changes from approved to another state,
|
||||||
# Create an instance of the model
|
with an associated domain that can be either active (READY) or not.
|
||||||
application = completed_application(status=DomainApplication.ApplicationStatus.APPROVED)
|
|
||||||
domain = Domain.objects.create(name=application.requested_domain.name)
|
|
||||||
application.approved_domain = domain
|
|
||||||
application.save()
|
|
||||||
|
|
||||||
# Create a request object with a superuser
|
Used to test errors when saving a change with an active domain, also used to test side effects
|
||||||
request = self.factory.post("/admin/registrar/domainapplication/{}/change/".format(application.pk))
|
when saving a change goes through."""
|
||||||
request.user = self.superuser
|
|
||||||
|
|
||||||
# Define a custom implementation for is_active
|
|
||||||
def custom_is_active(self):
|
|
||||||
return True # 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"))
|
|
||||||
|
|
||||||
with boto3_mocking.clients.handler_for("sesv2", self.mock_client):
|
|
||||||
with less_console_noise():
|
|
||||||
# Simulate saving the model
|
|
||||||
application.status = DomainApplication.ApplicationStatus.REJECTED
|
|
||||||
self.admin.save_model(request, application, None, True)
|
|
||||||
|
|
||||||
# Assert that the error message was called with the correct argument
|
|
||||||
messages.error.assert_called_once_with(
|
|
||||||
request,
|
|
||||||
"This action is not permitted. The domain " + "is already active.",
|
|
||||||
)
|
|
||||||
|
|
||||||
def test_side_effects_when_saving_approved_to_rejected(self):
|
|
||||||
# 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)
|
||||||
|
@ -747,19 +764,24 @@ class TestDomainApplicationAdmin(MockEppLib):
|
||||||
|
|
||||||
# Define a custom implementation for is_active
|
# Define a custom implementation for is_active
|
||||||
def custom_is_active(self):
|
def custom_is_active(self):
|
||||||
return False # Override to return False
|
return domain_is_active # Override to return True
|
||||||
|
|
||||||
# Use ExitStack to combine patch contexts
|
# Use ExitStack to combine patch contexts
|
||||||
with ExitStack() as stack:
|
with ExitStack() as stack:
|
||||||
# Patch Domain.is_active and django.contrib.messages.error simultaneously
|
# 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(Domain, "is_active", custom_is_active))
|
||||||
stack.enter_context(patch.object(messages, "error"))
|
stack.enter_context(patch.object(messages, "error"))
|
||||||
with boto3_mocking.clients.handler_for("sesv2", self.mock_client):
|
|
||||||
with less_console_noise():
|
application.status = another_state
|
||||||
# Simulate saving the model
|
|
||||||
application.status = DomainApplication.ApplicationStatus.REJECTED
|
|
||||||
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
|
# Assert that the error message was never called
|
||||||
messages.error.assert_not_called()
|
messages.error.assert_not_called()
|
||||||
|
|
||||||
|
@ -773,75 +795,29 @@ class TestDomainApplicationAdmin(MockEppLib):
|
||||||
with self.assertRaises(DomainInformation.DoesNotExist):
|
with self.assertRaises(DomainInformation.DoesNotExist):
|
||||||
domain_information.refresh_from_db()
|
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)
|
||||||
|
|
||||||
|
def test_error_when_saving_approved_to_action_needed_and_domain_is_active(self):
|
||||||
|
self.trigger_saving_approved_to_another_state(True, DomainApplication.ApplicationStatus.ACTION_NEEDED)
|
||||||
|
|
||||||
|
def test_error_when_saving_approved_to_rejected_and_domain_is_active(self):
|
||||||
|
self.trigger_saving_approved_to_another_state(True, DomainApplication.ApplicationStatus.REJECTED)
|
||||||
|
|
||||||
def test_error_when_saving_approved_to_ineligible_and_domain_is_active(self):
|
def test_error_when_saving_approved_to_ineligible_and_domain_is_active(self):
|
||||||
# Create an instance of the model
|
self.trigger_saving_approved_to_another_state(True, DomainApplication.ApplicationStatus.INELIGIBLE)
|
||||||
application = completed_application(status=DomainApplication.ApplicationStatus.APPROVED)
|
|
||||||
domain = Domain.objects.create(name=application.requested_domain.name)
|
|
||||||
application.approved_domain = domain
|
|
||||||
application.save()
|
|
||||||
|
|
||||||
# Create a request object with a superuser
|
def test_side_effects_when_saving_approved_to_in_review(self):
|
||||||
request = self.factory.post("/admin/registrar/domainapplication/{}/change/".format(application.pk))
|
self.trigger_saving_approved_to_another_state(False, DomainApplication.ApplicationStatus.IN_REVIEW)
|
||||||
request.user = self.superuser
|
|
||||||
|
|
||||||
# Define a custom implementation for is_active
|
def test_side_effects_when_saving_approved_to_action_needed(self):
|
||||||
def custom_is_active(self):
|
self.trigger_saving_approved_to_another_state(False, DomainApplication.ApplicationStatus.ACTION_NEEDED)
|
||||||
return True # Override to return True
|
|
||||||
|
|
||||||
# Use ExitStack to combine patch contexts
|
def test_side_effects_when_saving_approved_to_rejected(self):
|
||||||
with ExitStack() as stack:
|
self.trigger_saving_approved_to_another_state(False, DomainApplication.ApplicationStatus.REJECTED)
|
||||||
# Patch Domain.is_active and django.contrib.messages.error simultaneously
|
|
||||||
stack.enter_context(patch.object(Domain, "is_active", custom_is_active))
|
|
||||||
stack.enter_context(patch.object(messages, "error"))
|
|
||||||
|
|
||||||
# Simulate saving the model
|
|
||||||
application.status = DomainApplication.ApplicationStatus.INELIGIBLE
|
|
||||||
self.admin.save_model(request, application, None, True)
|
|
||||||
|
|
||||||
# Assert that the error message was called with the correct argument
|
|
||||||
messages.error.assert_called_once_with(
|
|
||||||
request,
|
|
||||||
"This action is not permitted. The domain " + "is already active.",
|
|
||||||
)
|
|
||||||
|
|
||||||
def test_side_effects_when_saving_approved_to_ineligible(self):
|
def test_side_effects_when_saving_approved_to_ineligible(self):
|
||||||
# Create an instance of the model
|
self.trigger_saving_approved_to_another_state(False, DomainApplication.ApplicationStatus.INELIGIBLE)
|
||||||
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
|
|
||||||
|
|
||||||
# Define a custom implementation for is_active
|
|
||||||
def custom_is_active(self):
|
|
||||||
return False # Override to return False
|
|
||||||
|
|
||||||
# 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"))
|
|
||||||
|
|
||||||
# Simulate saving the model
|
|
||||||
application.status = DomainApplication.ApplicationStatus.INELIGIBLE
|
|
||||||
self.admin.save_model(request, application, None, True)
|
|
||||||
|
|
||||||
# Assert that the error message was never called
|
|
||||||
messages.error.assert_not_called()
|
|
||||||
|
|
||||||
self.assertEqual(application.approved_domain, None)
|
|
||||||
|
|
||||||
# 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()
|
|
||||||
|
|
||||||
def test_has_correct_filters(self):
|
def test_has_correct_filters(self):
|
||||||
"""
|
"""
|
||||||
|
|
31
src/registrar/tests/test_environment_variables_effects.py
Normal file
31
src/registrar/tests/test_environment_variables_effects.py
Normal file
|
@ -0,0 +1,31 @@
|
||||||
|
from django.test import Client, TestCase, override_settings
|
||||||
|
from django.contrib.auth import get_user_model
|
||||||
|
|
||||||
|
|
||||||
|
class MyTestCase(TestCase):
|
||||||
|
def setUp(self):
|
||||||
|
self.client = Client()
|
||||||
|
username = "test_user"
|
||||||
|
first_name = "First"
|
||||||
|
last_name = "Last"
|
||||||
|
email = "info@example.com"
|
||||||
|
self.user = get_user_model().objects.create(
|
||||||
|
username=username, first_name=first_name, last_name=last_name, email=email
|
||||||
|
)
|
||||||
|
self.client.force_login(self.user)
|
||||||
|
|
||||||
|
def tearDown(self):
|
||||||
|
super().tearDown()
|
||||||
|
self.user.delete()
|
||||||
|
|
||||||
|
@override_settings(IS_PRODUCTION=True)
|
||||||
|
def test_production_environment(self):
|
||||||
|
"""No banner on prod."""
|
||||||
|
home_page = self.client.get("/")
|
||||||
|
self.assertNotContains(home_page, "You are on a test site.")
|
||||||
|
|
||||||
|
@override_settings(IS_PRODUCTION=False)
|
||||||
|
def test_non_production_environment(self):
|
||||||
|
"""Banner on non-prod."""
|
||||||
|
home_page = self.client.get("/")
|
||||||
|
self.assertContains(home_page, "You are on a test site.")
|
|
@ -161,33 +161,63 @@ class TestDomainApplication(TestCase):
|
||||||
application.submit()
|
application.submit()
|
||||||
self.assertEqual(application.status, application.ApplicationStatus.SUBMITTED)
|
self.assertEqual(application.status, application.ApplicationStatus.SUBMITTED)
|
||||||
|
|
||||||
def test_submit_sends_email(self):
|
def check_email_sent(self, application, msg, action, expected_count):
|
||||||
"""Create an application and submit it and see if email was sent."""
|
"""Check if an email was sent after performing an action."""
|
||||||
with less_console_noise():
|
|
||||||
user, _ = User.objects.get_or_create(username="testy")
|
|
||||||
contact = Contact.objects.create(email="test@test.gov")
|
|
||||||
domain, _ = DraftDomain.objects.get_or_create(name="igorville.gov")
|
|
||||||
application = DomainApplication.objects.create(
|
|
||||||
creator=user,
|
|
||||||
requested_domain=domain,
|
|
||||||
submitter=contact,
|
|
||||||
)
|
|
||||||
application.save()
|
|
||||||
|
|
||||||
|
with self.subTest(msg=msg, action=action):
|
||||||
with boto3_mocking.clients.handler_for("sesv2", self.mock_client):
|
with boto3_mocking.clients.handler_for("sesv2", self.mock_client):
|
||||||
application.submit()
|
with less_console_noise():
|
||||||
|
# Perform the specified action
|
||||||
|
action_method = getattr(application, action)
|
||||||
|
action_method()
|
||||||
|
|
||||||
# check to see if an email was sent
|
# Check if an email was sent
|
||||||
self.assertGreater(
|
sent_emails = [
|
||||||
len(
|
|
||||||
[
|
|
||||||
email
|
email
|
||||||
for email in MockSESClient.EMAILS_SENT
|
for email in MockSESClient.EMAILS_SENT
|
||||||
if "test@test.gov" in email["kwargs"]["Destination"]["ToAddresses"]
|
if "mayor@igorville.gov" in email["kwargs"]["Destination"]["ToAddresses"]
|
||||||
]
|
]
|
||||||
),
|
self.assertEqual(len(sent_emails), expected_count)
|
||||||
0,
|
|
||||||
)
|
def test_submit_from_started_sends_email(self):
|
||||||
|
msg = "Create an application and submit it and see if email was sent."
|
||||||
|
application = completed_application()
|
||||||
|
self.check_email_sent(application, msg, "submit", 1)
|
||||||
|
|
||||||
|
def test_submit_from_withdrawn_sends_email(self):
|
||||||
|
msg = "Create a withdrawn application and submit it and see if email was sent."
|
||||||
|
application = completed_application(status=DomainApplication.ApplicationStatus.WITHDRAWN)
|
||||||
|
self.check_email_sent(application, msg, "submit", 1)
|
||||||
|
|
||||||
|
def test_submit_from_action_needed_does_not_send_email(self):
|
||||||
|
msg = "Create an application with ACTION_NEEDED status and submit it, check if email was not sent."
|
||||||
|
application = completed_application(status=DomainApplication.ApplicationStatus.ACTION_NEEDED)
|
||||||
|
self.check_email_sent(application, msg, "submit", 0)
|
||||||
|
|
||||||
|
def test_submit_from_in_review_does_not_send_email(self):
|
||||||
|
msg = "Create a withdrawn application and submit it and see if email was sent."
|
||||||
|
application = completed_application(status=DomainApplication.ApplicationStatus.IN_REVIEW)
|
||||||
|
self.check_email_sent(application, msg, "submit", 0)
|
||||||
|
|
||||||
|
def test_approve_sends_email(self):
|
||||||
|
msg = "Create an application and approve it and see if email was sent."
|
||||||
|
application = completed_application(status=DomainApplication.ApplicationStatus.IN_REVIEW)
|
||||||
|
self.check_email_sent(application, msg, "approve", 1)
|
||||||
|
|
||||||
|
def test_withdraw_sends_email(self):
|
||||||
|
msg = "Create an application and withdraw it and see if email was sent."
|
||||||
|
application = completed_application(status=DomainApplication.ApplicationStatus.IN_REVIEW)
|
||||||
|
self.check_email_sent(application, msg, "withdraw", 1)
|
||||||
|
|
||||||
|
def test_reject_sends_email(self):
|
||||||
|
msg = "Create an application and reject it and see if email was sent."
|
||||||
|
application = completed_application(status=DomainApplication.ApplicationStatus.APPROVED)
|
||||||
|
self.check_email_sent(application, msg, "reject", 1)
|
||||||
|
|
||||||
|
def test_reject_with_prejudice_does_not_send_email(self):
|
||||||
|
msg = "Create an application and reject it with prejudice and see if email was sent."
|
||||||
|
application = completed_application(status=DomainApplication.ApplicationStatus.APPROVED)
|
||||||
|
self.check_email_sent(application, msg, "reject_with_prejudice", 0)
|
||||||
|
|
||||||
def test_submit_transition_allowed(self):
|
def test_submit_transition_allowed(self):
|
||||||
"""
|
"""
|
||||||
|
@ -464,6 +494,46 @@ class TestDomainApplication(TestCase):
|
||||||
with self.assertRaises(exception_type):
|
with self.assertRaises(exception_type):
|
||||||
application.reject_with_prejudice()
|
application.reject_with_prejudice()
|
||||||
|
|
||||||
|
def test_transition_not_allowed_approved_in_review_when_domain_is_active(self):
|
||||||
|
"""Create an application with status approved, create a matching domain that
|
||||||
|
is active, and call in_review against transition rules"""
|
||||||
|
|
||||||
|
domain = Domain.objects.create(name=self.approved_application.requested_domain.name)
|
||||||
|
self.approved_application.approved_domain = domain
|
||||||
|
self.approved_application.save()
|
||||||
|
|
||||||
|
# Define a custom implementation for is_active
|
||||||
|
def custom_is_active(self):
|
||||||
|
return True # Override to return True
|
||||||
|
|
||||||
|
with boto3_mocking.clients.handler_for("sesv2", self.mock_client):
|
||||||
|
with less_console_noise():
|
||||||
|
# Use patch to temporarily replace is_active with the custom implementation
|
||||||
|
with patch.object(Domain, "is_active", custom_is_active):
|
||||||
|
# Now, when you call is_active on Domain, it will return True
|
||||||
|
with self.assertRaises(TransitionNotAllowed):
|
||||||
|
self.approved_application.in_review()
|
||||||
|
|
||||||
|
def test_transition_not_allowed_approved_action_needed_when_domain_is_active(self):
|
||||||
|
"""Create an application with status approved, create a matching domain that
|
||||||
|
is active, and call action_needed against transition rules"""
|
||||||
|
|
||||||
|
domain = Domain.objects.create(name=self.approved_application.requested_domain.name)
|
||||||
|
self.approved_application.approved_domain = domain
|
||||||
|
self.approved_application.save()
|
||||||
|
|
||||||
|
# Define a custom implementation for is_active
|
||||||
|
def custom_is_active(self):
|
||||||
|
return True # Override to return True
|
||||||
|
|
||||||
|
with boto3_mocking.clients.handler_for("sesv2", self.mock_client):
|
||||||
|
with less_console_noise():
|
||||||
|
# Use patch to temporarily replace is_active with the custom implementation
|
||||||
|
with patch.object(Domain, "is_active", custom_is_active):
|
||||||
|
# Now, when you call is_active on Domain, it will return True
|
||||||
|
with self.assertRaises(TransitionNotAllowed):
|
||||||
|
self.approved_application.action_needed()
|
||||||
|
|
||||||
def test_transition_not_allowed_approved_rejected_when_domain_is_active(self):
|
def test_transition_not_allowed_approved_rejected_when_domain_is_active(self):
|
||||||
"""Create an application with status approved, create a matching domain that
|
"""Create an application with status approved, create a matching domain that
|
||||||
is active, and call reject against transition rules"""
|
is active, and call reject against transition rules"""
|
||||||
|
|
|
@ -555,7 +555,7 @@ class DomainYourContactInformationView(DomainFormBaseView):
|
||||||
# Post to DB using values from the form
|
# Post to DB using values from the form
|
||||||
form.save()
|
form.save()
|
||||||
|
|
||||||
messages.success(self.request, "Your contact information has been updated.")
|
messages.success(self.request, "Your contact information for all your domains has been updated.")
|
||||||
|
|
||||||
# superclass has the redirect
|
# superclass has the redirect
|
||||||
return super().form_valid(form)
|
return super().form_valid(form)
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue