mirror of
https://github.com/cisagov/manage.get.gov.git
synced 2025-06-02 02:28:32 +02:00
Merge branch 'main' into dk/661-other-contacts
This commit is contained in:
commit
185c1f0cb6
15 changed files with 791 additions and 330 deletions
|
@ -519,7 +519,7 @@ LOGIN_REQUIRED_IGNORE_PATHS = [
|
|||
]
|
||||
|
||||
# where to go after logging out
|
||||
LOGOUT_REDIRECT_URL = "home"
|
||||
LOGOUT_REDIRECT_URL = "https://get.gov/"
|
||||
|
||||
# disable dynamic client registration,
|
||||
# only the OP inside OIDC_PROVIDERS will be available
|
||||
|
|
|
@ -218,5 +218,8 @@ class DomainFixture(DomainApplicationFixture):
|
|||
creator=user, status=DomainApplication.ApplicationStatus.IN_REVIEW
|
||||
).last()
|
||||
logger.debug(f"Approving {application} for {user}")
|
||||
application.approve()
|
||||
|
||||
# We don't want fixtures sending out real emails to
|
||||
# fake email addresses, so we just skip that and log it instead
|
||||
application.approve(send_email=False)
|
||||
application.save()
|
||||
|
|
|
@ -0,0 +1,21 @@
|
|||
# Generated by Django 4.2.7 on 2023-12-23 01:31
|
||||
|
||||
from django.db import migrations, models
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
dependencies = [
|
||||
("registrar", "0060_domain_deleted_domain_first_ready"),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.AddField(
|
||||
model_name="domain",
|
||||
name="security_contact_registry_id",
|
||||
field=models.TextField(
|
||||
editable=False,
|
||||
help_text="Duplication of registry's security contact id for when registry unavailable",
|
||||
null=True,
|
||||
),
|
||||
),
|
||||
]
|
|
@ -31,7 +31,7 @@ from epplibwrapper import (
|
|||
|
||||
from registrar.models.utility.contact_error import ContactError, ContactErrorCodes
|
||||
|
||||
from django.db.models import DateField
|
||||
from django.db.models import DateField, TextField
|
||||
from .utility.domain_field import DomainField
|
||||
from .utility.domain_helper import DomainHelper
|
||||
from .utility.time_stamped_model import TimeStampedModel
|
||||
|
@ -974,6 +974,12 @@ class Domain(TimeStampedModel, DomainHelper):
|
|||
help_text=("Duplication of registry's expiration date saved for ease of reporting"),
|
||||
)
|
||||
|
||||
security_contact_registry_id = TextField(
|
||||
null=True,
|
||||
help_text=("Duplication of registry's security contact id for when registry unavailable"),
|
||||
editable=False,
|
||||
)
|
||||
|
||||
deleted = DateField(
|
||||
null=True,
|
||||
editable=False,
|
||||
|
@ -1127,9 +1133,15 @@ class Domain(TimeStampedModel, DomainHelper):
|
|||
# Grab from cache
|
||||
contacts = self._get_property(desired_property)
|
||||
except KeyError as error:
|
||||
# if contact type is security, attempt to retrieve registry id
|
||||
# for the security contact from domain.security_contact_registry_id
|
||||
if contact_type_choice == PublicContact.ContactTypeChoices.SECURITY and self.security_contact_registry_id:
|
||||
logger.info(f"Could not access registry, using fallback value of {self.security_contact_registry_id}")
|
||||
contacts = {PublicContact.ContactTypeChoices.SECURITY: self.security_contact_registry_id}
|
||||
else:
|
||||
logger.error(f"Could not find {contact_type_choice}: {error}")
|
||||
return None
|
||||
else:
|
||||
|
||||
cached_contact = self.get_contact_in_keys(contacts, contact_type_choice)
|
||||
if cached_contact is None:
|
||||
# TODO - #1103
|
||||
|
@ -1630,6 +1642,8 @@ class Domain(TimeStampedModel, DomainHelper):
|
|||
self._update_hosts_and_contacts(cleaned, fetch_hosts, fetch_contacts)
|
||||
if fetch_hosts:
|
||||
self._update_hosts_and_ips_in_db(cleaned)
|
||||
if fetch_contacts:
|
||||
self._update_security_contact_in_db(cleaned)
|
||||
self._update_dates(cleaned)
|
||||
|
||||
self._cache = cleaned
|
||||
|
@ -1739,6 +1753,23 @@ class Domain(TimeStampedModel, DomainHelper):
|
|||
for ip_address in cleaned_ips:
|
||||
HostIP.objects.get_or_create(address=ip_address, host=host_in_db)
|
||||
|
||||
def _update_security_contact_in_db(self, cleaned):
|
||||
"""Update security contact registry id in database if retrieved from registry.
|
||||
If no value is retrieved from registry, set to empty string in db.
|
||||
|
||||
Parameters:
|
||||
self: the domain to be updated with security from cleaned
|
||||
cleaned: dict containing contact registry ids. Security contact is of type
|
||||
PublicContact.ContactTypeChoices.SECURITY
|
||||
"""
|
||||
cleaned_contacts = cleaned["contacts"]
|
||||
security_contact_registry_id = ""
|
||||
security_contact = cleaned_contacts[PublicContact.ContactTypeChoices.SECURITY]
|
||||
if security_contact:
|
||||
security_contact_registry_id = security_contact
|
||||
self.security_contact_registry_id = security_contact_registry_id
|
||||
self.save()
|
||||
|
||||
def _update_dates(self, cleaned):
|
||||
"""Update dates (expiration and creation) from cleaned"""
|
||||
requires_save = False
|
||||
|
|
|
@ -570,17 +570,25 @@ class DomainApplication(TimeStampedModel):
|
|||
return not self.approved_domain.is_active()
|
||||
return True
|
||||
|
||||
def _send_status_update_email(self, new_status, email_template, email_template_subject):
|
||||
"""Send a atatus update email to the submitter.
|
||||
def _send_status_update_email(self, new_status, email_template, email_template_subject, send_email=True):
|
||||
"""Send a status update email to the submitter.
|
||||
|
||||
The email goes to the email address that the submitter gave as their
|
||||
contact information. If there is not submitter information, then do
|
||||
nothing.
|
||||
|
||||
send_email: bool -> Used to bypass the send_templated_email function, in the event
|
||||
we just want to log that an email would have been sent, rather than actually sending one.
|
||||
"""
|
||||
|
||||
if self.submitter is None or self.submitter.email is None:
|
||||
logger.warning(f"Cannot send {new_status} email, no submitter email address.")
|
||||
return
|
||||
return None
|
||||
|
||||
if not send_email:
|
||||
logger.info(f"Email was not sent. Would send {new_status} email: {self.submitter.email}")
|
||||
return None
|
||||
|
||||
try:
|
||||
send_templated_email(
|
||||
email_template,
|
||||
|
@ -684,7 +692,7 @@ class DomainApplication(TimeStampedModel):
|
|||
],
|
||||
target=ApplicationStatus.APPROVED,
|
||||
)
|
||||
def approve(self):
|
||||
def approve(self, send_email=True):
|
||||
"""Approve an application that has been submitted.
|
||||
|
||||
This has substantial side-effects because it creates another database
|
||||
|
@ -713,6 +721,7 @@ class DomainApplication(TimeStampedModel):
|
|||
"application approved",
|
||||
"emails/status_change_approved.txt",
|
||||
"emails/status_change_approved_subject.txt",
|
||||
send_email,
|
||||
)
|
||||
|
||||
@transition(
|
||||
|
|
34
src/registrar/templates/application_intro.html
Normal file
34
src/registrar/templates/application_intro.html
Normal file
|
@ -0,0 +1,34 @@
|
|||
{% extends 'base.html' %}
|
||||
{% load static form_helpers url_helpers %}
|
||||
|
||||
{% block content %}
|
||||
<main id="main-content" class="grid-container">
|
||||
<div class="grid-col desktop:grid-offset-2 desktop:grid-col-8">
|
||||
|
||||
<form class="usa-form usa-form--extra-large" method="post" novalidate>
|
||||
{% csrf_token %}
|
||||
|
||||
<h1>You’re about to start your .gov domain request.</h1>
|
||||
<p>You don’t have to complete the process in one session. You can save what you enter and come back to it when you’re ready.</p>
|
||||
<p>We’ll use the information you provide to verify your organization’s eligibility for a .gov domain. We’ll also verify that the domain you request meets our guidelines.</p>
|
||||
<h2>Time to complete the form</h2>
|
||||
<p>If you have <a href="{% public_site_url 'domains/before/#information-you%E2%80%99ll-need-to-complete-the-domain-request-form' %}" target="_blank" class="usa-link">all the information you need</a>,
|
||||
completing your domain request might take around 15 minutes.</p>
|
||||
<p><a href="{% public_site_url 'contact/' %}" target="_blank" rel="noopener noreferrer" class="usa-link">Contact us if you need help with your request</a>.</p>
|
||||
|
||||
{% block form_buttons %}
|
||||
<div class="stepnav">
|
||||
<button
|
||||
type="submit"
|
||||
name="submit_button"
|
||||
value="intro_acknowledge"
|
||||
class="usa-button"
|
||||
>Continue</button>
|
||||
</div>
|
||||
{% endblock %}
|
||||
|
||||
</form>
|
||||
|
||||
</div>
|
||||
</main>
|
||||
{% endblock %}
|
|
@ -18,9 +18,11 @@ from registrar.admin import (
|
|||
from registrar.models import Domain, DomainApplication, DomainInformation, User, DomainInvitation, Contact, Website
|
||||
from registrar.models.user_domain_role import UserDomainRole
|
||||
from .common import (
|
||||
MockSESClient,
|
||||
AuditedAdminMockData,
|
||||
completed_application,
|
||||
generic_domain_object,
|
||||
less_console_noise,
|
||||
mock_user,
|
||||
create_superuser,
|
||||
create_user,
|
||||
|
@ -35,7 +37,6 @@ from unittest.mock import patch
|
|||
from unittest import skip
|
||||
|
||||
from django.conf import settings
|
||||
from unittest.mock import MagicMock
|
||||
import boto3_mocking # type: ignore
|
||||
import logging
|
||||
|
||||
|
@ -58,6 +59,9 @@ class TestDomainAdmin(MockEppLib):
|
|||
"""
|
||||
self.client.force_login(self.superuser)
|
||||
application = completed_application(status=DomainApplication.ApplicationStatus.IN_REVIEW)
|
||||
mock_client = MockSESClient()
|
||||
with boto3_mocking.clients.handler_for("sesv2", mock_client):
|
||||
with less_console_noise():
|
||||
application.approve()
|
||||
|
||||
response = self.client.get("/admin/registrar/domain/")
|
||||
|
@ -326,6 +330,7 @@ class TestDomainApplicationAdmin(MockEppLib):
|
|||
url="/admin/registrar/DomainApplication/",
|
||||
model=DomainApplication,
|
||||
)
|
||||
self.mock_client = MockSESClient()
|
||||
|
||||
def test_domain_sortable(self):
|
||||
"""Tests if the DomainApplication sorts by domain correctly"""
|
||||
|
@ -420,10 +425,8 @@ class TestDomainApplicationAdmin(MockEppLib):
|
|||
EMAIL = "mayor@igorville.gov"
|
||||
User.objects.filter(email=EMAIL).delete()
|
||||
|
||||
mock_client = MagicMock()
|
||||
mock_client_instance = mock_client.return_value
|
||||
|
||||
with boto3_mocking.clients.handler_for("sesv2", mock_client):
|
||||
with boto3_mocking.clients.handler_for("sesv2", self.mock_client):
|
||||
with less_console_noise():
|
||||
# Create a sample application
|
||||
application = completed_application()
|
||||
|
||||
|
@ -437,8 +440,8 @@ class TestDomainApplicationAdmin(MockEppLib):
|
|||
self.admin.save_model(request, application, form=None, change=True)
|
||||
|
||||
# Access the arguments passed to send_email
|
||||
call_args = mock_client_instance.send_email.call_args
|
||||
args, kwargs = call_args
|
||||
call_args = self.mock_client.EMAILS_SENT
|
||||
kwargs = call_args[0]["kwargs"]
|
||||
|
||||
# Retrieve the email details from the arguments
|
||||
from_email = kwargs.get("FromEmailAddress")
|
||||
|
@ -452,8 +455,7 @@ class TestDomainApplicationAdmin(MockEppLib):
|
|||
self.assertEqual(to_email, EMAIL)
|
||||
self.assertIn(expected_string, email_body)
|
||||
|
||||
# Perform assertions on the mock call itself
|
||||
mock_client_instance.send_email.assert_called_once()
|
||||
self.assertEqual(len(self.mock_client.EMAILS_SENT), 1)
|
||||
|
||||
@boto3_mocking.patching
|
||||
def test_save_model_sends_in_review_email(self):
|
||||
|
@ -461,10 +463,8 @@ class TestDomainApplicationAdmin(MockEppLib):
|
|||
EMAIL = "mayor@igorville.gov"
|
||||
User.objects.filter(email=EMAIL).delete()
|
||||
|
||||
mock_client = MagicMock()
|
||||
mock_client_instance = mock_client.return_value
|
||||
|
||||
with boto3_mocking.clients.handler_for("sesv2", mock_client):
|
||||
with boto3_mocking.clients.handler_for("sesv2", self.mock_client):
|
||||
with less_console_noise():
|
||||
# Create a sample application
|
||||
application = completed_application(status=DomainApplication.ApplicationStatus.SUBMITTED)
|
||||
|
||||
|
@ -478,8 +478,8 @@ class TestDomainApplicationAdmin(MockEppLib):
|
|||
self.admin.save_model(request, application, form=None, change=True)
|
||||
|
||||
# Access the arguments passed to send_email
|
||||
call_args = mock_client_instance.send_email.call_args
|
||||
args, kwargs = call_args
|
||||
call_args = self.mock_client.EMAILS_SENT
|
||||
kwargs = call_args[0]["kwargs"]
|
||||
|
||||
# Retrieve the email details from the arguments
|
||||
from_email = kwargs.get("FromEmailAddress")
|
||||
|
@ -493,8 +493,7 @@ class TestDomainApplicationAdmin(MockEppLib):
|
|||
self.assertEqual(to_email, EMAIL)
|
||||
self.assertIn(expected_string, email_body)
|
||||
|
||||
# Perform assertions on the mock call itself
|
||||
mock_client_instance.send_email.assert_called_once()
|
||||
self.assertEqual(len(self.mock_client.EMAILS_SENT), 1)
|
||||
|
||||
@boto3_mocking.patching
|
||||
def test_save_model_sends_approved_email(self):
|
||||
|
@ -502,10 +501,8 @@ class TestDomainApplicationAdmin(MockEppLib):
|
|||
EMAIL = "mayor@igorville.gov"
|
||||
User.objects.filter(email=EMAIL).delete()
|
||||
|
||||
mock_client = MagicMock()
|
||||
mock_client_instance = mock_client.return_value
|
||||
|
||||
with boto3_mocking.clients.handler_for("sesv2", mock_client):
|
||||
with boto3_mocking.clients.handler_for("sesv2", self.mock_client):
|
||||
with less_console_noise():
|
||||
# Create a sample application
|
||||
application = completed_application(status=DomainApplication.ApplicationStatus.IN_REVIEW)
|
||||
|
||||
|
@ -519,8 +516,8 @@ class TestDomainApplicationAdmin(MockEppLib):
|
|||
self.admin.save_model(request, application, form=None, change=True)
|
||||
|
||||
# Access the arguments passed to send_email
|
||||
call_args = mock_client_instance.send_email.call_args
|
||||
args, kwargs = call_args
|
||||
call_args = self.mock_client.EMAILS_SENT
|
||||
kwargs = call_args[0]["kwargs"]
|
||||
|
||||
# Retrieve the email details from the arguments
|
||||
from_email = kwargs.get("FromEmailAddress")
|
||||
|
@ -534,9 +531,9 @@ class TestDomainApplicationAdmin(MockEppLib):
|
|||
self.assertEqual(to_email, EMAIL)
|
||||
self.assertIn(expected_string, email_body)
|
||||
|
||||
# Perform assertions on the mock call itself
|
||||
mock_client_instance.send_email.assert_called_once()
|
||||
self.assertEqual(len(self.mock_client.EMAILS_SENT), 1)
|
||||
|
||||
@boto3_mocking.patching
|
||||
def test_save_model_sets_approved_domain(self):
|
||||
# make sure there is no user with this email
|
||||
EMAIL = "mayor@igorville.gov"
|
||||
|
@ -548,6 +545,8 @@ class TestDomainApplicationAdmin(MockEppLib):
|
|||
# Create a mock request
|
||||
request = self.factory.post("/admin/registrar/domainapplication/{}/change/".format(application.pk))
|
||||
|
||||
with boto3_mocking.clients.handler_for("sesv2", self.mock_client):
|
||||
with less_console_noise():
|
||||
# Modify the application's property
|
||||
application.status = DomainApplication.ApplicationStatus.APPROVED
|
||||
|
||||
|
@ -563,10 +562,8 @@ class TestDomainApplicationAdmin(MockEppLib):
|
|||
EMAIL = "mayor@igorville.gov"
|
||||
User.objects.filter(email=EMAIL).delete()
|
||||
|
||||
mock_client = MagicMock()
|
||||
mock_client_instance = mock_client.return_value
|
||||
|
||||
with boto3_mocking.clients.handler_for("sesv2", mock_client):
|
||||
with boto3_mocking.clients.handler_for("sesv2", self.mock_client):
|
||||
with less_console_noise():
|
||||
# Create a sample application
|
||||
application = completed_application(status=DomainApplication.ApplicationStatus.IN_REVIEW)
|
||||
|
||||
|
@ -580,8 +577,8 @@ class TestDomainApplicationAdmin(MockEppLib):
|
|||
self.admin.save_model(request, application, form=None, change=True)
|
||||
|
||||
# Access the arguments passed to send_email
|
||||
call_args = mock_client_instance.send_email.call_args
|
||||
args, kwargs = call_args
|
||||
call_args = self.mock_client.EMAILS_SENT
|
||||
kwargs = call_args[0]["kwargs"]
|
||||
|
||||
# Retrieve the email details from the arguments
|
||||
from_email = kwargs.get("FromEmailAddress")
|
||||
|
@ -595,8 +592,7 @@ class TestDomainApplicationAdmin(MockEppLib):
|
|||
self.assertEqual(to_email, EMAIL)
|
||||
self.assertIn(expected_string, email_body)
|
||||
|
||||
# Perform assertions on the mock call itself
|
||||
mock_client_instance.send_email.assert_called_once()
|
||||
self.assertEqual(len(self.mock_client.EMAILS_SENT), 1)
|
||||
|
||||
@boto3_mocking.patching
|
||||
def test_save_model_sends_rejected_email(self):
|
||||
|
@ -604,10 +600,8 @@ class TestDomainApplicationAdmin(MockEppLib):
|
|||
EMAIL = "mayor@igorville.gov"
|
||||
User.objects.filter(email=EMAIL).delete()
|
||||
|
||||
mock_client = MagicMock()
|
||||
mock_client_instance = mock_client.return_value
|
||||
|
||||
with boto3_mocking.clients.handler_for("sesv2", mock_client):
|
||||
with boto3_mocking.clients.handler_for("sesv2", self.mock_client):
|
||||
with less_console_noise():
|
||||
# Create a sample application
|
||||
application = completed_application(status=DomainApplication.ApplicationStatus.IN_REVIEW)
|
||||
|
||||
|
@ -621,8 +615,8 @@ class TestDomainApplicationAdmin(MockEppLib):
|
|||
self.admin.save_model(request, application, form=None, change=True)
|
||||
|
||||
# Access the arguments passed to send_email
|
||||
call_args = mock_client_instance.send_email.call_args
|
||||
args, kwargs = call_args
|
||||
call_args = self.mock_client.EMAILS_SENT
|
||||
kwargs = call_args[0]["kwargs"]
|
||||
|
||||
# Retrieve the email details from the arguments
|
||||
from_email = kwargs.get("FromEmailAddress")
|
||||
|
@ -636,9 +630,9 @@ class TestDomainApplicationAdmin(MockEppLib):
|
|||
self.assertEqual(to_email, EMAIL)
|
||||
self.assertIn(expected_string, email_body)
|
||||
|
||||
# Perform assertions on the mock call itself
|
||||
mock_client_instance.send_email.assert_called_once()
|
||||
self.assertEqual(len(self.mock_client.EMAILS_SENT), 1)
|
||||
|
||||
@boto3_mocking.patching
|
||||
def test_save_model_sets_restricted_status_on_user(self):
|
||||
# make sure there is no user with this email
|
||||
EMAIL = "mayor@igorville.gov"
|
||||
|
@ -650,6 +644,8 @@ class TestDomainApplicationAdmin(MockEppLib):
|
|||
# Create a mock request
|
||||
request = self.factory.post("/admin/registrar/domainapplication/{}/change/".format(application.pk))
|
||||
|
||||
with boto3_mocking.clients.handler_for("sesv2", self.mock_client):
|
||||
with less_console_noise():
|
||||
# Modify the application's property
|
||||
application.status = DomainApplication.ApplicationStatus.INELIGIBLE
|
||||
|
||||
|
@ -661,6 +657,8 @@ class TestDomainApplicationAdmin(MockEppLib):
|
|||
|
||||
def test_readonly_when_restricted_creator(self):
|
||||
application = completed_application(status=DomainApplication.ApplicationStatus.IN_REVIEW)
|
||||
with boto3_mocking.clients.handler_for("sesv2", self.mock_client):
|
||||
with less_console_noise():
|
||||
application.creator.status = User.RESTRICTED
|
||||
application.creator.save()
|
||||
|
||||
|
@ -740,6 +738,8 @@ class TestDomainApplicationAdmin(MockEppLib):
|
|||
def test_saving_when_restricted_creator(self):
|
||||
# Create an instance of the model
|
||||
application = completed_application(status=DomainApplication.ApplicationStatus.IN_REVIEW)
|
||||
with boto3_mocking.clients.handler_for("sesv2", self.mock_client):
|
||||
with less_console_noise():
|
||||
application.creator.status = User.RESTRICTED
|
||||
application.creator.save()
|
||||
|
||||
|
@ -763,6 +763,8 @@ class TestDomainApplicationAdmin(MockEppLib):
|
|||
def test_change_view_with_restricted_creator(self):
|
||||
# Create an instance of the model
|
||||
application = completed_application(status=DomainApplication.ApplicationStatus.IN_REVIEW)
|
||||
with boto3_mocking.clients.handler_for("sesv2", self.mock_client):
|
||||
with less_console_noise():
|
||||
application.creator.status = User.RESTRICTED
|
||||
application.creator.save()
|
||||
|
||||
|
@ -779,6 +781,7 @@ class TestDomainApplicationAdmin(MockEppLib):
|
|||
"Cannot edit an application with a restricted creator.",
|
||||
)
|
||||
|
||||
@boto3_mocking.patching
|
||||
def test_error_when_saving_approved_to_rejected_and_domain_is_active(self):
|
||||
# Create an instance of the model
|
||||
application = completed_application(status=DomainApplication.ApplicationStatus.APPROVED)
|
||||
|
@ -800,6 +803,8 @@ class TestDomainApplicationAdmin(MockEppLib):
|
|||
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)
|
||||
|
@ -831,7 +836,8 @@ class TestDomainApplicationAdmin(MockEppLib):
|
|||
# Patch Domain.is_active and django.contrib.messages.error simultaneously
|
||||
stack.enter_context(patch.object(Domain, "is_active", custom_is_active))
|
||||
stack.enter_context(patch.object(messages, "error"))
|
||||
|
||||
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)
|
||||
|
@ -1091,6 +1097,7 @@ class TestDomainApplicationAdmin(MockEppLib):
|
|||
User.objects.all().delete()
|
||||
Contact.objects.all().delete()
|
||||
Website.objects.all().delete()
|
||||
self.mock_client.EMAILS_SENT.clear()
|
||||
|
||||
|
||||
class DomainInvitationAdminTest(TestCase):
|
||||
|
|
|
@ -3,7 +3,7 @@
|
|||
from unittest.mock import MagicMock
|
||||
|
||||
from django.test import TestCase
|
||||
from .common import completed_application
|
||||
from .common import completed_application, less_console_noise
|
||||
|
||||
|
||||
import boto3_mocking # type: ignore
|
||||
|
@ -20,6 +20,7 @@ class TestEmails(TestCase):
|
|||
application = completed_application()
|
||||
|
||||
with boto3_mocking.clients.handler_for("sesv2", self.mock_client_class):
|
||||
with less_console_noise():
|
||||
application.submit()
|
||||
|
||||
# check that an email was sent
|
||||
|
@ -56,6 +57,7 @@ class TestEmails(TestCase):
|
|||
"""Test line spacing without current_website."""
|
||||
application = completed_application(has_current_website=False)
|
||||
with boto3_mocking.clients.handler_for("sesv2", self.mock_client_class):
|
||||
with less_console_noise():
|
||||
application.submit()
|
||||
_, kwargs = self.mock_client.send_email.call_args
|
||||
body = kwargs["Content"]["Simple"]["Body"]["Text"]["Data"]
|
||||
|
@ -68,6 +70,7 @@ class TestEmails(TestCase):
|
|||
"""Test line spacing with current_website."""
|
||||
application = completed_application(has_current_website=True)
|
||||
with boto3_mocking.clients.handler_for("sesv2", self.mock_client_class):
|
||||
with less_console_noise():
|
||||
application.submit()
|
||||
_, kwargs = self.mock_client.send_email.call_args
|
||||
body = kwargs["Content"]["Simple"]["Body"]["Text"]["Data"]
|
||||
|
@ -81,6 +84,7 @@ class TestEmails(TestCase):
|
|||
"""Test line spacing with other contacts."""
|
||||
application = completed_application(has_other_contacts=True)
|
||||
with boto3_mocking.clients.handler_for("sesv2", self.mock_client_class):
|
||||
with less_console_noise():
|
||||
application.submit()
|
||||
_, kwargs = self.mock_client.send_email.call_args
|
||||
body = kwargs["Content"]["Simple"]["Body"]["Text"]["Data"]
|
||||
|
@ -94,6 +98,7 @@ class TestEmails(TestCase):
|
|||
"""Test line spacing without other contacts."""
|
||||
application = completed_application(has_other_contacts=False)
|
||||
with boto3_mocking.clients.handler_for("sesv2", self.mock_client_class):
|
||||
with less_console_noise():
|
||||
application.submit()
|
||||
_, kwargs = self.mock_client.send_email.call_args
|
||||
body = kwargs["Content"]["Simple"]["Body"]["Text"]["Data"]
|
||||
|
@ -106,6 +111,7 @@ class TestEmails(TestCase):
|
|||
"""Test line spacing with alternative .gov domain."""
|
||||
application = completed_application(has_alternative_gov_domain=True)
|
||||
with boto3_mocking.clients.handler_for("sesv2", self.mock_client_class):
|
||||
with less_console_noise():
|
||||
application.submit()
|
||||
_, kwargs = self.mock_client.send_email.call_args
|
||||
body = kwargs["Content"]["Simple"]["Body"]["Text"]["Data"]
|
||||
|
@ -118,6 +124,7 @@ class TestEmails(TestCase):
|
|||
"""Test line spacing without alternative .gov domain."""
|
||||
application = completed_application(has_alternative_gov_domain=False)
|
||||
with boto3_mocking.clients.handler_for("sesv2", self.mock_client_class):
|
||||
with less_console_noise():
|
||||
application.submit()
|
||||
_, kwargs = self.mock_client.send_email.call_args
|
||||
body = kwargs["Content"]["Simple"]["Body"]["Text"]["Data"]
|
||||
|
@ -130,6 +137,7 @@ class TestEmails(TestCase):
|
|||
"""Test line spacing with about your organization."""
|
||||
application = completed_application(has_about_your_organization=True)
|
||||
with boto3_mocking.clients.handler_for("sesv2", self.mock_client_class):
|
||||
with less_console_noise():
|
||||
application.submit()
|
||||
_, kwargs = self.mock_client.send_email.call_args
|
||||
body = kwargs["Content"]["Simple"]["Body"]["Text"]["Data"]
|
||||
|
@ -142,6 +150,7 @@ class TestEmails(TestCase):
|
|||
"""Test line spacing without about your organization."""
|
||||
application = completed_application(has_about_your_organization=False)
|
||||
with boto3_mocking.clients.handler_for("sesv2", self.mock_client_class):
|
||||
with less_console_noise():
|
||||
application.submit()
|
||||
_, kwargs = self.mock_client.send_email.call_args
|
||||
body = kwargs["Content"]["Simple"]["Body"]["Text"]["Data"]
|
||||
|
@ -154,6 +163,7 @@ class TestEmails(TestCase):
|
|||
"""Test line spacing with anything else."""
|
||||
application = completed_application(has_anything_else=True)
|
||||
with boto3_mocking.clients.handler_for("sesv2", self.mock_client_class):
|
||||
with less_console_noise():
|
||||
application.submit()
|
||||
_, kwargs = self.mock_client.send_email.call_args
|
||||
body = kwargs["Content"]["Simple"]["Body"]["Text"]["Data"]
|
||||
|
@ -165,6 +175,7 @@ class TestEmails(TestCase):
|
|||
"""Test line spacing without anything else."""
|
||||
application = completed_application(has_anything_else=False)
|
||||
with boto3_mocking.clients.handler_for("sesv2", self.mock_client_class):
|
||||
with less_console_noise():
|
||||
application.submit()
|
||||
_, kwargs = self.mock_client.send_email.call_args
|
||||
body = kwargs["Content"]["Simple"]["Body"]["Text"]["Data"]
|
||||
|
|
|
@ -19,8 +19,6 @@ from registrar.models.transition_domain import TransitionDomain # type: ignore
|
|||
from .common import MockSESClient, less_console_noise, completed_application
|
||||
from django_fsm import TransitionNotAllowed
|
||||
|
||||
boto3_mocking.clients.register_handler("sesv2", MockSESClient)
|
||||
|
||||
|
||||
# Test comment for push -- will remove
|
||||
# The DomainApplication submit method has a side effect of sending an email
|
||||
|
@ -53,6 +51,12 @@ class TestDomainApplication(TestCase):
|
|||
status=DomainApplication.ApplicationStatus.INELIGIBLE, name="ineligible.gov"
|
||||
)
|
||||
|
||||
self.mock_client = MockSESClient()
|
||||
|
||||
def tearDown(self):
|
||||
super().tearDown()
|
||||
self.mock_client.EMAILS_SENT.clear()
|
||||
|
||||
def assertNotRaises(self, exception_type):
|
||||
"""Helper method for testing allowed transitions."""
|
||||
return self.assertRaises(Exception, None, exception_type)
|
||||
|
@ -130,6 +134,9 @@ class TestDomainApplication(TestCase):
|
|||
def test_status_fsm_submit_fail(self):
|
||||
user, _ = User.objects.get_or_create(username="testy")
|
||||
application = DomainApplication.objects.create(creator=user)
|
||||
|
||||
with boto3_mocking.clients.handler_for("sesv2", self.mock_client):
|
||||
with less_console_noise():
|
||||
with self.assertRaises(ValueError):
|
||||
# can't submit an application with a null domain name
|
||||
application.submit()
|
||||
|
@ -138,7 +145,10 @@ class TestDomainApplication(TestCase):
|
|||
user, _ = User.objects.get_or_create(username="testy")
|
||||
site = DraftDomain.objects.create(name="igorville.gov")
|
||||
application = DomainApplication.objects.create(creator=user, requested_domain=site)
|
||||
|
||||
# no submitter email so this emits a log warning
|
||||
|
||||
with boto3_mocking.clients.handler_for("sesv2", self.mock_client):
|
||||
with less_console_noise():
|
||||
application.submit()
|
||||
self.assertEqual(application.status, application.ApplicationStatus.SUBMITTED)
|
||||
|
@ -154,6 +164,9 @@ class TestDomainApplication(TestCase):
|
|||
submitter=contact,
|
||||
)
|
||||
application.save()
|
||||
|
||||
with boto3_mocking.clients.handler_for("sesv2", self.mock_client):
|
||||
with less_console_noise():
|
||||
application.submit()
|
||||
|
||||
# check to see if an email was sent
|
||||
|
@ -179,6 +192,8 @@ class TestDomainApplication(TestCase):
|
|||
(self.withdrawn_application, TransitionNotAllowed),
|
||||
]
|
||||
|
||||
with boto3_mocking.clients.handler_for("sesv2", self.mock_client):
|
||||
with less_console_noise():
|
||||
for application, exception_type in test_cases:
|
||||
with self.subTest(application=application, exception_type=exception_type):
|
||||
try:
|
||||
|
@ -197,6 +212,8 @@ class TestDomainApplication(TestCase):
|
|||
(self.ineligible_application, TransitionNotAllowed),
|
||||
]
|
||||
|
||||
with boto3_mocking.clients.handler_for("sesv2", self.mock_client):
|
||||
with less_console_noise():
|
||||
for application, exception_type in test_cases:
|
||||
with self.subTest(application=application, exception_type=exception_type):
|
||||
with self.assertRaises(exception_type):
|
||||
|
@ -214,6 +231,8 @@ class TestDomainApplication(TestCase):
|
|||
(self.ineligible_application, TransitionNotAllowed),
|
||||
]
|
||||
|
||||
with boto3_mocking.clients.handler_for("sesv2", self.mock_client):
|
||||
with less_console_noise():
|
||||
for application, exception_type in test_cases:
|
||||
with self.subTest(application=application, exception_type=exception_type):
|
||||
try:
|
||||
|
@ -231,6 +250,8 @@ class TestDomainApplication(TestCase):
|
|||
(self.withdrawn_application, TransitionNotAllowed),
|
||||
]
|
||||
|
||||
with boto3_mocking.clients.handler_for("sesv2", self.mock_client):
|
||||
with less_console_noise():
|
||||
for application, exception_type in test_cases:
|
||||
with self.subTest(application=application, exception_type=exception_type):
|
||||
with self.assertRaises(exception_type):
|
||||
|
@ -247,6 +268,8 @@ class TestDomainApplication(TestCase):
|
|||
(self.ineligible_application, TransitionNotAllowed),
|
||||
]
|
||||
|
||||
with boto3_mocking.clients.handler_for("sesv2", self.mock_client):
|
||||
with less_console_noise():
|
||||
for application, exception_type in test_cases:
|
||||
with self.subTest(application=application, exception_type=exception_type):
|
||||
try:
|
||||
|
@ -265,6 +288,8 @@ class TestDomainApplication(TestCase):
|
|||
(self.withdrawn_application, TransitionNotAllowed),
|
||||
]
|
||||
|
||||
with boto3_mocking.clients.handler_for("sesv2", self.mock_client):
|
||||
with less_console_noise():
|
||||
for application, exception_type in test_cases:
|
||||
with self.subTest(application=application, exception_type=exception_type):
|
||||
with self.assertRaises(exception_type):
|
||||
|
@ -281,6 +306,8 @@ class TestDomainApplication(TestCase):
|
|||
(self.rejected_application, TransitionNotAllowed),
|
||||
]
|
||||
|
||||
with boto3_mocking.clients.handler_for("sesv2", self.mock_client):
|
||||
with less_console_noise():
|
||||
for application, exception_type in test_cases:
|
||||
with self.subTest(application=application, exception_type=exception_type):
|
||||
try:
|
||||
|
@ -288,6 +315,19 @@ class TestDomainApplication(TestCase):
|
|||
except TransitionNotAllowed:
|
||||
self.fail("TransitionNotAllowed was raised, but it was not expected.")
|
||||
|
||||
def test_approved_skips_sending_email(self):
|
||||
"""
|
||||
Test that calling .approve with send_email=False doesn't actually send
|
||||
an email
|
||||
"""
|
||||
|
||||
with boto3_mocking.clients.handler_for("sesv2", self.mock_client):
|
||||
with less_console_noise():
|
||||
self.submitted_application.approve(send_email=False)
|
||||
|
||||
# Assert that no emails were sent
|
||||
self.assertEqual(len(self.mock_client.EMAILS_SENT), 0)
|
||||
|
||||
def test_approved_transition_not_allowed(self):
|
||||
"""
|
||||
Test that calling action_needed against transition rules raises TransitionNotAllowed.
|
||||
|
@ -299,6 +339,8 @@ class TestDomainApplication(TestCase):
|
|||
(self.ineligible_application, TransitionNotAllowed),
|
||||
]
|
||||
|
||||
with boto3_mocking.clients.handler_for("sesv2", self.mock_client):
|
||||
with less_console_noise():
|
||||
for application, exception_type in test_cases:
|
||||
with self.subTest(application=application, exception_type=exception_type):
|
||||
with self.assertRaises(exception_type):
|
||||
|
@ -314,6 +356,8 @@ class TestDomainApplication(TestCase):
|
|||
(self.action_needed_application, TransitionNotAllowed),
|
||||
]
|
||||
|
||||
with boto3_mocking.clients.handler_for("sesv2", self.mock_client):
|
||||
with less_console_noise():
|
||||
for application, exception_type in test_cases:
|
||||
with self.subTest(application=application, exception_type=exception_type):
|
||||
try:
|
||||
|
@ -333,6 +377,8 @@ class TestDomainApplication(TestCase):
|
|||
(self.ineligible_application, TransitionNotAllowed),
|
||||
]
|
||||
|
||||
with boto3_mocking.clients.handler_for("sesv2", self.mock_client):
|
||||
with less_console_noise():
|
||||
for application, exception_type in test_cases:
|
||||
with self.subTest(application=application, exception_type=exception_type):
|
||||
with self.assertRaises(exception_type):
|
||||
|
@ -348,6 +394,8 @@ class TestDomainApplication(TestCase):
|
|||
(self.approved_application, TransitionNotAllowed),
|
||||
]
|
||||
|
||||
with boto3_mocking.clients.handler_for("sesv2", self.mock_client):
|
||||
with less_console_noise():
|
||||
for application, exception_type in test_cases:
|
||||
with self.subTest(application=application, exception_type=exception_type):
|
||||
try:
|
||||
|
@ -367,6 +415,8 @@ class TestDomainApplication(TestCase):
|
|||
(self.ineligible_application, TransitionNotAllowed),
|
||||
]
|
||||
|
||||
with boto3_mocking.clients.handler_for("sesv2", self.mock_client):
|
||||
with less_console_noise():
|
||||
for application, exception_type in test_cases:
|
||||
with self.subTest(application=application, exception_type=exception_type):
|
||||
with self.assertRaises(exception_type):
|
||||
|
@ -383,6 +433,8 @@ class TestDomainApplication(TestCase):
|
|||
(self.rejected_application, TransitionNotAllowed),
|
||||
]
|
||||
|
||||
with boto3_mocking.clients.handler_for("sesv2", self.mock_client):
|
||||
with less_console_noise():
|
||||
for application, exception_type in test_cases:
|
||||
with self.subTest(application=application, exception_type=exception_type):
|
||||
try:
|
||||
|
@ -401,6 +453,8 @@ class TestDomainApplication(TestCase):
|
|||
(self.ineligible_application, TransitionNotAllowed),
|
||||
]
|
||||
|
||||
with boto3_mocking.clients.handler_for("sesv2", self.mock_client):
|
||||
with less_console_noise():
|
||||
for application, exception_type in test_cases:
|
||||
with self.subTest(application=application, exception_type=exception_type):
|
||||
with self.assertRaises(exception_type):
|
||||
|
@ -418,6 +472,8 @@ class TestDomainApplication(TestCase):
|
|||
def custom_is_active(self):
|
||||
return True # Override to return True
|
||||
|
||||
with boto3_mocking.clients.handler_for("sesv2", self.mock_client):
|
||||
with less_console_noise():
|
||||
# Use patch to temporarily replace is_active with the custom implementation
|
||||
with patch.object(Domain, "is_active", custom_is_active):
|
||||
# Now, when you call is_active on Domain, it will return True
|
||||
|
@ -436,6 +492,8 @@ class TestDomainApplication(TestCase):
|
|||
def custom_is_active(self):
|
||||
return True # Override to return True
|
||||
|
||||
with boto3_mocking.clients.handler_for("sesv2", self.mock_client):
|
||||
with less_console_noise():
|
||||
# Use patch to temporarily replace is_active with the custom implementation
|
||||
with patch.object(Domain, "is_active", custom_is_active):
|
||||
# Now, when you call is_active on Domain, it will return True
|
||||
|
@ -466,13 +524,24 @@ class TestDomainApplication(TestCase):
|
|||
|
||||
|
||||
class TestPermissions(TestCase):
|
||||
|
||||
"""Test the User-Domain-Role connection."""
|
||||
|
||||
def setUp(self):
|
||||
super().setUp()
|
||||
self.mock_client = MockSESClient()
|
||||
|
||||
def tearDown(self):
|
||||
super().tearDown()
|
||||
self.mock_client.EMAILS_SENT.clear()
|
||||
|
||||
@boto3_mocking.patching
|
||||
def test_approval_creates_role(self):
|
||||
draft_domain, _ = DraftDomain.objects.get_or_create(name="igorville.gov")
|
||||
user, _ = User.objects.get_or_create()
|
||||
application = DomainApplication.objects.create(creator=user, requested_domain=draft_domain)
|
||||
|
||||
with boto3_mocking.clients.handler_for("sesv2", self.mock_client):
|
||||
with less_console_noise():
|
||||
# skip using the submit method
|
||||
application.status = DomainApplication.ApplicationStatus.SUBMITTED
|
||||
application.approve()
|
||||
|
@ -486,10 +555,22 @@ class TestDomainInfo(TestCase):
|
|||
|
||||
"""Test creation of Domain Information when approved."""
|
||||
|
||||
def setUp(self):
|
||||
super().setUp()
|
||||
self.mock_client = MockSESClient()
|
||||
|
||||
def tearDown(self):
|
||||
super().tearDown()
|
||||
self.mock_client.EMAILS_SENT.clear()
|
||||
|
||||
@boto3_mocking.patching
|
||||
def test_approval_creates_info(self):
|
||||
draft_domain, _ = DraftDomain.objects.get_or_create(name="igorville.gov")
|
||||
user, _ = User.objects.get_or_create()
|
||||
application = DomainApplication.objects.create(creator=user, requested_domain=draft_domain)
|
||||
|
||||
with boto3_mocking.clients.handler_for("sesv2", self.mock_client):
|
||||
with less_console_noise():
|
||||
# skip using the submit method
|
||||
application.status = DomainApplication.ApplicationStatus.SUBMITTED
|
||||
application.approve()
|
||||
|
@ -594,6 +675,7 @@ class TestUser(TestCase):
|
|||
caps_email = "MAYOR@igorville.gov"
|
||||
# mock the domain invitation save routine
|
||||
with patch("registrar.models.DomainInvitation.save") as save_mock:
|
||||
with less_console_noise():
|
||||
DomainInvitation.objects.get_or_create(email=caps_email, domain=self.domain)
|
||||
self.user.check_domain_invitations_on_login()
|
||||
# if check_domain_invitations_on_login properly matches exactly one
|
||||
|
|
|
@ -29,8 +29,9 @@ from epplibwrapper import (
|
|||
RegistryError,
|
||||
ErrorCode,
|
||||
)
|
||||
from .common import MockEppLib
|
||||
from .common import MockEppLib, MockSESClient, less_console_noise
|
||||
import logging
|
||||
import boto3_mocking # type: ignore
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
|
@ -252,6 +253,7 @@ class TestDomainCache(MockEppLib):
|
|||
class TestDomainCreation(MockEppLib):
|
||||
"""Rule: An approved domain application must result in a domain"""
|
||||
|
||||
@boto3_mocking.patching
|
||||
def test_approved_application_creates_domain_locally(self):
|
||||
"""
|
||||
Scenario: Analyst approves a domain application
|
||||
|
@ -262,6 +264,10 @@ class TestDomainCreation(MockEppLib):
|
|||
draft_domain, _ = DraftDomain.objects.get_or_create(name="igorville.gov")
|
||||
user, _ = User.objects.get_or_create()
|
||||
application = DomainApplication.objects.create(creator=user, requested_domain=draft_domain)
|
||||
|
||||
mock_client = MockSESClient()
|
||||
with boto3_mocking.clients.handler_for("sesv2", mock_client):
|
||||
with less_console_noise():
|
||||
# skip using the submit method
|
||||
application.status = DomainApplication.ApplicationStatus.SUBMITTED
|
||||
# transition to approve state
|
||||
|
@ -739,6 +745,50 @@ class TestRegistrantContacts(MockEppLib):
|
|||
self.mockedSendFunction.assert_has_calls(expected_calls, any_order=True)
|
||||
self.assertEqual(PublicContact.objects.filter(domain=self.domain).count(), 1)
|
||||
|
||||
def test_security_email_returns_on_registry_error(self):
|
||||
"""
|
||||
Scenario: Security email previously set through EPP and stored in registrar's database.
|
||||
Registry is unavailable and throws exception when attempting to build cache from
|
||||
registry. Security email retrieved from database.
|
||||
"""
|
||||
# Use self.domain_contact which has been initialized with existing contacts, including securityContact
|
||||
|
||||
# call get_security_email to initially set the security_contact_registry_id in the domain model
|
||||
self.domain_contact.get_security_email()
|
||||
# invalidate the cache so the next time get_security_email is called, it has to attempt to populate cache
|
||||
self.domain_contact._invalidate_cache()
|
||||
|
||||
# mock that registry throws an error on the EPP send
|
||||
def side_effect(_request, cleaned):
|
||||
raise RegistryError(code=ErrorCode.COMMAND_FAILED)
|
||||
|
||||
patcher = patch("registrar.models.domain.registry.send")
|
||||
mocked_send = patcher.start()
|
||||
mocked_send.side_effect = side_effect
|
||||
|
||||
# when get_security_email is called, the registry error will force the security contact
|
||||
# to be retrieved using the security_contact_registry_id in the domain model
|
||||
security_email = self.domain_contact.get_security_email()
|
||||
|
||||
# assert that the proper security contact was retrieved by testing the email matches expected value
|
||||
self.assertEqual(security_email, "security@mail.gov")
|
||||
patcher.stop()
|
||||
|
||||
def test_security_email_stored_on_fetch_cache(self):
|
||||
"""
|
||||
Scenario: Security email is stored in db when security contact is retrieved from fetch_cache.
|
||||
Verify the success of this by asserting get_or_create calls to db.
|
||||
The mocked data for the EPP calls for the freeman.gov domain returns a security
|
||||
contact with registry id of securityContact when InfoContact is called
|
||||
"""
|
||||
# Use self.domain_contact which has been initialized with existing contacts, including securityContact
|
||||
|
||||
# force fetch_cache to be called, which will return above documented mocked hosts
|
||||
self.domain_contact.get_security_email()
|
||||
|
||||
# assert that the security_contact_registry_id in the db matches "securityContact"
|
||||
self.assertEqual(self.domain_contact.security_contact_registry_id, "securityContact")
|
||||
|
||||
def test_not_disclosed_on_other_contacts(self):
|
||||
"""
|
||||
Scenario: Registrant creates a new domain with multiple contacts
|
||||
|
|
|
@ -18,7 +18,8 @@ from unittest.mock import patch
|
|||
|
||||
from registrar.models.contact import Contact
|
||||
|
||||
from .common import MockEppLib, less_console_noise
|
||||
from .common import MockEppLib, MockSESClient, less_console_noise
|
||||
import boto3_mocking # type: ignore
|
||||
|
||||
|
||||
class TestExtendExpirationDates(MockEppLib):
|
||||
|
@ -706,10 +707,13 @@ class TestMigrations(TestCase):
|
|||
def run_master_script(self):
|
||||
# noqa here (E501) because splitting this up makes it
|
||||
# confusing to read.
|
||||
mock_client = MockSESClient()
|
||||
with boto3_mocking.clients.handler_for("sesv2", mock_client):
|
||||
with patch(
|
||||
"registrar.management.commands.utility.terminal_helper.TerminalHelper.query_yes_no_exit", # noqa
|
||||
return_value=True,
|
||||
):
|
||||
with patch("registrar.utility.email.send_templated_email", return_value=None):
|
||||
call_command(
|
||||
"master_domain_migrations",
|
||||
runMigrations=True,
|
||||
|
@ -717,6 +721,7 @@ class TestMigrations(TestCase):
|
|||
migrationJSON=self.migration_json_filename,
|
||||
disablePrompts=True,
|
||||
)
|
||||
print(f"here: {mock_client.EMAILS_SENT}")
|
||||
|
||||
def compare_tables(
|
||||
self,
|
||||
|
@ -1019,6 +1024,7 @@ class TestMigrations(TestCase):
|
|||
expected_missing_domain_invitations,
|
||||
)
|
||||
|
||||
@boto3_mocking.patching
|
||||
def test_send_domain_invitations_email(self):
|
||||
"""Can send only a single domain invitation email."""
|
||||
with less_console_noise():
|
||||
|
@ -1027,6 +1033,9 @@ class TestMigrations(TestCase):
|
|||
|
||||
# this is one of the email addresses in data/test_contacts.txt
|
||||
output_stream = StringIO()
|
||||
|
||||
mock_client = MockSESClient()
|
||||
with boto3_mocking.clients.handler_for("sesv2", mock_client):
|
||||
# also have to re-point the logging handlers to output_stream
|
||||
with less_console_noise(output_stream):
|
||||
call_command("send_domain_invitations", "testuser@gmail.com", stdout=output_stream)
|
||||
|
@ -1037,6 +1046,7 @@ class TestMigrations(TestCase):
|
|||
self.assertIn("Found 1 transition domains", output)
|
||||
self.assertTrue("would send email to testuser@gmail.com", output)
|
||||
|
||||
@boto3_mocking.patching
|
||||
def test_send_domain_invitations_two_emails(self):
|
||||
"""Can send only a single domain invitation email."""
|
||||
with less_console_noise():
|
||||
|
@ -1045,6 +1055,9 @@ class TestMigrations(TestCase):
|
|||
|
||||
# these are two email addresses in data/test_contacts.txt
|
||||
output_stream = StringIO()
|
||||
|
||||
mock_client = MockSESClient()
|
||||
with boto3_mocking.clients.handler_for("sesv2", mock_client):
|
||||
# also have to re-point the logging handlers to output_stream
|
||||
with less_console_noise(output_stream):
|
||||
call_command(
|
||||
|
|
|
@ -5,7 +5,7 @@ from django.conf import settings
|
|||
from django.test import Client, TestCase
|
||||
from django.urls import reverse
|
||||
from django.contrib.auth import get_user_model
|
||||
from .common import MockEppLib, completed_application, create_user # type: ignore
|
||||
from .common import MockEppLib, MockSESClient, completed_application, create_user # type: ignore
|
||||
from django_webtest import WebTest # type: ignore
|
||||
import boto3_mocking # type: ignore
|
||||
|
||||
|
@ -110,7 +110,7 @@ class LoggedInTests(TestWithUser):
|
|||
response = self.client.get("/register/", follow=True)
|
||||
self.assertContains(
|
||||
response,
|
||||
"What kind of U.S.-based government organization do you represent?",
|
||||
"You’re about to start your .gov domain request.",
|
||||
)
|
||||
|
||||
def test_domain_application_form_with_ineligible_user(self):
|
||||
|
@ -139,24 +139,70 @@ class DomainApplicationTests(TestWithUser, WebTest):
|
|||
self.app.set_user(self.user.username)
|
||||
self.TITLES = ApplicationWizard.TITLES
|
||||
|
||||
def test_application_form_intro_acknowledgement(self):
|
||||
"""Tests that user is presented with intro acknowledgement page"""
|
||||
intro_page = self.app.get(reverse("application:"))
|
||||
self.assertContains(intro_page, "You’re about to start your .gov domain request")
|
||||
|
||||
def test_application_form_intro_is_skipped_when_edit_access(self):
|
||||
"""Tests that user is NOT presented with intro acknowledgement page when accessed through 'edit'"""
|
||||
completed_application(status=DomainApplication.ApplicationStatus.STARTED, user=self.user)
|
||||
home_page = self.app.get("/")
|
||||
self.assertContains(home_page, "city.gov")
|
||||
# click the "Edit" link
|
||||
detail_page = home_page.click("Edit", index=0)
|
||||
# Check that the response is a redirect
|
||||
self.assertEqual(detail_page.status_code, 302)
|
||||
# You can access the 'Location' header to get the redirect URL
|
||||
redirect_url = detail_page.url
|
||||
self.assertEqual(redirect_url, "/register/organization_type/")
|
||||
|
||||
def test_application_form_empty_submit(self):
|
||||
# 302 redirect to the first form
|
||||
page = self.app.get(reverse("application:")).follow()
|
||||
"""Tests empty submit on the first page after the acknowledgement page"""
|
||||
intro_page = self.app.get(reverse("application:"))
|
||||
# django-webtest does not handle cookie-based sessions well because it keeps
|
||||
# resetting the session key on each new request, thus destroying the concept
|
||||
# of a "session". We are going to do it manually, saving the session ID here
|
||||
# and then setting the cookie on each request.
|
||||
session_id = self.app.cookies[settings.SESSION_COOKIE_NAME]
|
||||
|
||||
intro_form = intro_page.forms[0]
|
||||
self.app.set_cookie(settings.SESSION_COOKIE_NAME, session_id)
|
||||
intro_result = intro_form.submit()
|
||||
|
||||
# follow first redirect
|
||||
self.app.set_cookie(settings.SESSION_COOKIE_NAME, session_id)
|
||||
type_page = intro_result.follow()
|
||||
session_id = self.app.cookies[settings.SESSION_COOKIE_NAME]
|
||||
|
||||
# submitting should get back the same page if the required field is empty
|
||||
result = page.forms[0].submit()
|
||||
result = type_page.forms[0].submit()
|
||||
self.assertIn("What kind of U.S.-based government organization do you represent?", result)
|
||||
|
||||
def test_application_multiple_applications_exist(self):
|
||||
"""Test that an info message appears when user has multiple applications already"""
|
||||
# create and submit an application
|
||||
application = completed_application(user=self.user)
|
||||
mock_client = MockSESClient()
|
||||
with boto3_mocking.clients.handler_for("sesv2", mock_client):
|
||||
with less_console_noise():
|
||||
application.submit()
|
||||
application.save()
|
||||
|
||||
# now, attempt to create another one
|
||||
with less_console_noise():
|
||||
page = self.app.get("/register/").follow()
|
||||
self.assertContains(page, "You cannot submit this request yet")
|
||||
intro_page = self.app.get(reverse("application:"))
|
||||
session_id = self.app.cookies[settings.SESSION_COOKIE_NAME]
|
||||
intro_form = intro_page.forms[0]
|
||||
self.app.set_cookie(settings.SESSION_COOKIE_NAME, session_id)
|
||||
intro_result = intro_form.submit()
|
||||
|
||||
# follow first redirect
|
||||
self.app.set_cookie(settings.SESSION_COOKIE_NAME, session_id)
|
||||
type_page = intro_result.follow()
|
||||
session_id = self.app.cookies[settings.SESSION_COOKIE_NAME]
|
||||
|
||||
self.assertContains(type_page, "You cannot submit this request yet")
|
||||
|
||||
@boto3_mocking.patching
|
||||
def test_application_form_submission(self):
|
||||
|
@ -175,13 +221,22 @@ class DomainApplicationTests(TestWithUser, WebTest):
|
|||
SKIPPED_PAGES = 3
|
||||
num_pages = len(self.TITLES) - SKIPPED_PAGES
|
||||
|
||||
type_page = self.app.get(reverse("application:")).follow()
|
||||
intro_page = self.app.get(reverse("application:"))
|
||||
# django-webtest does not handle cookie-based sessions well because it keeps
|
||||
# resetting the session key on each new request, thus destroying the concept
|
||||
# of a "session". We are going to do it manually, saving the session ID here
|
||||
# and then setting the cookie on each request.
|
||||
session_id = self.app.cookies[settings.SESSION_COOKIE_NAME]
|
||||
|
||||
intro_form = intro_page.forms[0]
|
||||
self.app.set_cookie(settings.SESSION_COOKIE_NAME, session_id)
|
||||
intro_result = intro_form.submit()
|
||||
|
||||
# follow first redirect
|
||||
self.app.set_cookie(settings.SESSION_COOKIE_NAME, session_id)
|
||||
type_page = intro_result.follow()
|
||||
session_id = self.app.cookies[settings.SESSION_COOKIE_NAME]
|
||||
|
||||
# ---- TYPE PAGE ----
|
||||
type_form = type_page.forms[0]
|
||||
type_form["organization_type-organization_type"] = "federal"
|
||||
|
@ -543,13 +598,22 @@ class DomainApplicationTests(TestWithUser, WebTest):
|
|||
|
||||
def test_application_form_conditional_federal(self):
|
||||
"""Federal branch question is shown for federal organizations."""
|
||||
type_page = self.app.get(reverse("application:")).follow()
|
||||
intro_page = self.app.get(reverse("application:"))
|
||||
# django-webtest does not handle cookie-based sessions well because it keeps
|
||||
# resetting the session key on each new request, thus destroying the concept
|
||||
# of a "session". We are going to do it manually, saving the session ID here
|
||||
# and then setting the cookie on each request.
|
||||
session_id = self.app.cookies[settings.SESSION_COOKIE_NAME]
|
||||
|
||||
intro_form = intro_page.forms[0]
|
||||
self.app.set_cookie(settings.SESSION_COOKIE_NAME, session_id)
|
||||
intro_result = intro_form.submit()
|
||||
|
||||
# follow first redirect
|
||||
self.app.set_cookie(settings.SESSION_COOKIE_NAME, session_id)
|
||||
type_page = intro_result.follow()
|
||||
session_id = self.app.cookies[settings.SESSION_COOKIE_NAME]
|
||||
|
||||
# ---- TYPE PAGE ----
|
||||
|
||||
# the conditional step titles shouldn't appear initially
|
||||
|
@ -589,13 +653,22 @@ class DomainApplicationTests(TestWithUser, WebTest):
|
|||
|
||||
def test_application_form_conditional_elections(self):
|
||||
"""Election question is shown for other organizations."""
|
||||
type_page = self.app.get(reverse("application:")).follow()
|
||||
intro_page = self.app.get(reverse("application:"))
|
||||
# django-webtest does not handle cookie-based sessions well because it keeps
|
||||
# resetting the session key on each new request, thus destroying the concept
|
||||
# of a "session". We are going to do it manually, saving the session ID here
|
||||
# and then setting the cookie on each request.
|
||||
session_id = self.app.cookies[settings.SESSION_COOKIE_NAME]
|
||||
|
||||
intro_form = intro_page.forms[0]
|
||||
self.app.set_cookie(settings.SESSION_COOKIE_NAME, session_id)
|
||||
intro_result = intro_form.submit()
|
||||
|
||||
# follow first redirect
|
||||
self.app.set_cookie(settings.SESSION_COOKIE_NAME, session_id)
|
||||
type_page = intro_result.follow()
|
||||
session_id = self.app.cookies[settings.SESSION_COOKIE_NAME]
|
||||
|
||||
# ---- TYPE PAGE ----
|
||||
|
||||
# the conditional step titles shouldn't appear initially
|
||||
|
@ -634,13 +707,22 @@ class DomainApplicationTests(TestWithUser, WebTest):
|
|||
|
||||
def test_application_form_section_skipping(self):
|
||||
"""Can skip forward and back in sections"""
|
||||
type_page = self.app.get(reverse("application:")).follow()
|
||||
intro_page = self.app.get(reverse("application:"))
|
||||
# django-webtest does not handle cookie-based sessions well because it keeps
|
||||
# resetting the session key on each new request, thus destroying the concept
|
||||
# of a "session". We are going to do it manually, saving the session ID here
|
||||
# and then setting the cookie on each request.
|
||||
session_id = self.app.cookies[settings.SESSION_COOKIE_NAME]
|
||||
|
||||
intro_form = intro_page.forms[0]
|
||||
self.app.set_cookie(settings.SESSION_COOKIE_NAME, session_id)
|
||||
intro_result = intro_form.submit()
|
||||
|
||||
# follow first redirect
|
||||
self.app.set_cookie(settings.SESSION_COOKIE_NAME, session_id)
|
||||
type_page = intro_result.follow()
|
||||
session_id = self.app.cookies[settings.SESSION_COOKIE_NAME]
|
||||
|
||||
type_form = type_page.forms[0]
|
||||
type_form["organization_type-organization_type"] = "federal"
|
||||
self.app.set_cookie(settings.SESSION_COOKIE_NAME, session_id)
|
||||
|
@ -662,13 +744,22 @@ class DomainApplicationTests(TestWithUser, WebTest):
|
|||
|
||||
def test_application_form_nonfederal(self):
|
||||
"""Non-federal organizations don't have to provide their federal agency."""
|
||||
type_page = self.app.get(reverse("application:")).follow()
|
||||
intro_page = self.app.get(reverse("application:"))
|
||||
# django-webtest does not handle cookie-based sessions well because it keeps
|
||||
# resetting the session key on each new request, thus destroying the concept
|
||||
# of a "session". We are going to do it manually, saving the session ID here
|
||||
# and then setting the cookie on each request.
|
||||
session_id = self.app.cookies[settings.SESSION_COOKIE_NAME]
|
||||
|
||||
intro_form = intro_page.forms[0]
|
||||
self.app.set_cookie(settings.SESSION_COOKIE_NAME, session_id)
|
||||
intro_result = intro_form.submit()
|
||||
|
||||
# follow first redirect
|
||||
self.app.set_cookie(settings.SESSION_COOKIE_NAME, session_id)
|
||||
type_page = intro_result.follow()
|
||||
session_id = self.app.cookies[settings.SESSION_COOKIE_NAME]
|
||||
|
||||
type_form = type_page.forms[0]
|
||||
type_form["organization_type-organization_type"] = DomainApplication.OrganizationChoices.INTERSTATE
|
||||
self.app.set_cookie(settings.SESSION_COOKIE_NAME, session_id)
|
||||
|
@ -698,13 +789,22 @@ class DomainApplicationTests(TestWithUser, WebTest):
|
|||
|
||||
def test_application_about_your_organization_special(self):
|
||||
"""Special districts have to answer an additional question."""
|
||||
type_page = self.app.get(reverse("application:")).follow()
|
||||
intro_page = self.app.get(reverse("application:"))
|
||||
# django-webtest does not handle cookie-based sessions well because it keeps
|
||||
# resetting the session key on each new request, thus destroying the concept
|
||||
# of a "session". We are going to do it manually, saving the session ID here
|
||||
# and then setting the cookie on each request.
|
||||
session_id = self.app.cookies[settings.SESSION_COOKIE_NAME]
|
||||
|
||||
intro_form = intro_page.forms[0]
|
||||
self.app.set_cookie(settings.SESSION_COOKIE_NAME, session_id)
|
||||
intro_result = intro_form.submit()
|
||||
|
||||
# follow first redirect
|
||||
self.app.set_cookie(settings.SESSION_COOKIE_NAME, session_id)
|
||||
type_page = intro_result.follow()
|
||||
session_id = self.app.cookies[settings.SESSION_COOKIE_NAME]
|
||||
|
||||
type_form = type_page.forms[0]
|
||||
type_form["organization_type-organization_type"] = DomainApplication.OrganizationChoices.SPECIAL_DISTRICT
|
||||
self.app.set_cookie(settings.SESSION_COOKIE_NAME, session_id)
|
||||
|
@ -1068,13 +1168,22 @@ class DomainApplicationTests(TestWithUser, WebTest):
|
|||
|
||||
def test_application_about_your_organiztion_interstate(self):
|
||||
"""Special districts have to answer an additional question."""
|
||||
type_page = self.app.get(reverse("application:")).follow()
|
||||
intro_page = self.app.get(reverse("application:"))
|
||||
# django-webtest does not handle cookie-based sessions well because it keeps
|
||||
# resetting the session key on each new request, thus destroying the concept
|
||||
# of a "session". We are going to do it manually, saving the session ID here
|
||||
# and then setting the cookie on each request.
|
||||
session_id = self.app.cookies[settings.SESSION_COOKIE_NAME]
|
||||
|
||||
intro_form = intro_page.forms[0]
|
||||
self.app.set_cookie(settings.SESSION_COOKIE_NAME, session_id)
|
||||
intro_result = intro_form.submit()
|
||||
|
||||
# follow first redirect
|
||||
self.app.set_cookie(settings.SESSION_COOKIE_NAME, session_id)
|
||||
type_page = intro_result.follow()
|
||||
session_id = self.app.cookies[settings.SESSION_COOKIE_NAME]
|
||||
|
||||
type_form = type_page.forms[0]
|
||||
type_form["organization_type-organization_type"] = DomainApplication.OrganizationChoices.INTERSTATE
|
||||
self.app.set_cookie(settings.SESSION_COOKIE_NAME, session_id)
|
||||
|
@ -1087,12 +1196,22 @@ class DomainApplicationTests(TestWithUser, WebTest):
|
|||
|
||||
def test_application_tribal_government(self):
|
||||
"""Tribal organizations have to answer an additional question."""
|
||||
type_page = self.app.get(reverse("application:")).follow()
|
||||
intro_page = self.app.get(reverse("application:"))
|
||||
# django-webtest does not handle cookie-based sessions well because it keeps
|
||||
# resetting the session key on each new request, thus destroying the concept
|
||||
# of a "session". We are going to do it manually, saving the session ID here
|
||||
# and then setting the cookie on each request.
|
||||
session_id = self.app.cookies[settings.SESSION_COOKIE_NAME]
|
||||
|
||||
intro_form = intro_page.forms[0]
|
||||
self.app.set_cookie(settings.SESSION_COOKIE_NAME, session_id)
|
||||
intro_result = intro_form.submit()
|
||||
|
||||
# follow first redirect
|
||||
self.app.set_cookie(settings.SESSION_COOKIE_NAME, session_id)
|
||||
type_page = intro_result.follow()
|
||||
session_id = self.app.cookies[settings.SESSION_COOKIE_NAME]
|
||||
|
||||
type_form = type_page.forms[0]
|
||||
type_form["organization_type-organization_type"] = DomainApplication.OrganizationChoices.TRIBAL
|
||||
self.app.set_cookie(settings.SESSION_COOKIE_NAME, session_id)
|
||||
|
@ -1107,13 +1226,22 @@ class DomainApplicationTests(TestWithUser, WebTest):
|
|||
self.assertContains(tribal_government_page, self.TITLES[Step.TRIBAL_GOVERNMENT])
|
||||
|
||||
def test_application_ao_dynamic_text(self):
|
||||
type_page = self.app.get(reverse("application:")).follow()
|
||||
intro_page = self.app.get(reverse("application:"))
|
||||
# django-webtest does not handle cookie-based sessions well because it keeps
|
||||
# resetting the session key on each new request, thus destroying the concept
|
||||
# of a "session". We are going to do it manually, saving the session ID here
|
||||
# and then setting the cookie on each request.
|
||||
session_id = self.app.cookies[settings.SESSION_COOKIE_NAME]
|
||||
|
||||
intro_form = intro_page.forms[0]
|
||||
self.app.set_cookie(settings.SESSION_COOKIE_NAME, session_id)
|
||||
intro_result = intro_form.submit()
|
||||
|
||||
# follow first redirect
|
||||
self.app.set_cookie(settings.SESSION_COOKIE_NAME, session_id)
|
||||
type_page = intro_result.follow()
|
||||
session_id = self.app.cookies[settings.SESSION_COOKIE_NAME]
|
||||
|
||||
# ---- TYPE PAGE ----
|
||||
type_form = type_page.forms[0]
|
||||
type_form["organization_type-organization_type"] = "federal"
|
||||
|
@ -1169,12 +1297,22 @@ class DomainApplicationTests(TestWithUser, WebTest):
|
|||
self.assertContains(ao_page, "Domain requests from cities")
|
||||
|
||||
def test_application_dotgov_domain_dynamic_text(self):
|
||||
type_page = self.app.get(reverse("application:")).follow()
|
||||
intro_page = self.app.get(reverse("application:"))
|
||||
# django-webtest does not handle cookie-based sessions well because it keeps
|
||||
# resetting the session key on each new request, thus destroying the concept
|
||||
# of a "session". We are going to do it manually, saving the session ID here
|
||||
# and then setting the cookie on each request.
|
||||
session_id = self.app.cookies[settings.SESSION_COOKIE_NAME]
|
||||
|
||||
intro_form = intro_page.forms[0]
|
||||
self.app.set_cookie(settings.SESSION_COOKIE_NAME, session_id)
|
||||
intro_result = intro_form.submit()
|
||||
|
||||
# follow first redirect
|
||||
self.app.set_cookie(settings.SESSION_COOKIE_NAME, session_id)
|
||||
type_page = intro_result.follow()
|
||||
session_id = self.app.cookies[settings.SESSION_COOKIE_NAME]
|
||||
|
||||
# ---- TYPE PAGE ----
|
||||
type_form = type_page.forms[0]
|
||||
type_form["organization_type-organization_type"] = "federal"
|
||||
|
@ -1418,8 +1556,22 @@ class DomainApplicationTests(TestWithUser, WebTest):
|
|||
Make sure the long name is displaying in the application form,
|
||||
org step
|
||||
"""
|
||||
request = self.app.get(reverse("application:")).follow()
|
||||
self.assertContains(request, "Federal: an agency of the U.S. government")
|
||||
intro_page = self.app.get(reverse("application:"))
|
||||
# django-webtest does not handle cookie-based sessions well because it keeps
|
||||
# resetting the session key on each new request, thus destroying the concept
|
||||
# of a "session". We are going to do it manually, saving the session ID here
|
||||
# and then setting the cookie on each request.
|
||||
session_id = self.app.cookies[settings.SESSION_COOKIE_NAME]
|
||||
|
||||
intro_form = intro_page.forms[0]
|
||||
self.app.set_cookie(settings.SESSION_COOKIE_NAME, session_id)
|
||||
intro_result = intro_form.submit()
|
||||
|
||||
# follow first redirect
|
||||
self.app.set_cookie(settings.SESSION_COOKIE_NAME, session_id)
|
||||
type_page = intro_result.follow()
|
||||
|
||||
self.assertContains(type_page, "Federal: an agency of the U.S. government")
|
||||
|
||||
def test_long_org_name_in_application_manage(self):
|
||||
"""
|
||||
|
@ -1693,6 +1845,7 @@ class TestDomainManagers(TestDomainOverview):
|
|||
response = self.client.get(reverse("domain-users-add", kwargs={"pk": self.domain.id}))
|
||||
self.assertContains(response, "Add a domain manager")
|
||||
|
||||
@boto3_mocking.patching
|
||||
def test_domain_user_add_form(self):
|
||||
"""Adding an existing user works."""
|
||||
other_user, _ = get_user_model().objects.get_or_create(email="mayor@igorville.gov")
|
||||
|
@ -1702,6 +1855,10 @@ class TestDomainManagers(TestDomainOverview):
|
|||
add_page.form["email"] = "mayor@igorville.gov"
|
||||
|
||||
self.app.set_cookie(settings.SESSION_COOKIE_NAME, session_id)
|
||||
|
||||
mock_client = MockSESClient()
|
||||
with boto3_mocking.clients.handler_for("sesv2", mock_client):
|
||||
with less_console_noise():
|
||||
success_result = add_page.form.submit()
|
||||
|
||||
self.assertEqual(success_result.status_code, 302)
|
||||
|
@ -1731,7 +1888,12 @@ class TestDomainManagers(TestDomainOverview):
|
|||
session_id = self.app.cookies[settings.SESSION_COOKIE_NAME]
|
||||
add_page.form["email"] = email_address
|
||||
self.app.set_cookie(settings.SESSION_COOKIE_NAME, session_id)
|
||||
|
||||
mock_client = MockSESClient()
|
||||
with boto3_mocking.clients.handler_for("sesv2", mock_client):
|
||||
with less_console_noise():
|
||||
success_result = add_page.form.submit()
|
||||
|
||||
self.app.set_cookie(settings.SESSION_COOKIE_NAME, session_id)
|
||||
success_page = success_result.follow()
|
||||
|
||||
|
@ -1757,7 +1919,12 @@ class TestDomainManagers(TestDomainOverview):
|
|||
session_id = self.app.cookies[settings.SESSION_COOKIE_NAME]
|
||||
add_page.form["email"] = caps_email_address
|
||||
self.app.set_cookie(settings.SESSION_COOKIE_NAME, session_id)
|
||||
|
||||
mock_client = MockSESClient()
|
||||
with boto3_mocking.clients.handler_for("sesv2", mock_client):
|
||||
with less_console_noise():
|
||||
success_result = add_page.form.submit()
|
||||
|
||||
self.app.set_cookie(settings.SESSION_COOKIE_NAME, session_id)
|
||||
success_page = success_result.follow()
|
||||
|
||||
|
@ -1777,6 +1944,7 @@ class TestDomainManagers(TestDomainOverview):
|
|||
mock_client = MagicMock()
|
||||
mock_client_instance = mock_client.return_value
|
||||
with boto3_mocking.clients.handler_for("sesv2", mock_client):
|
||||
with less_console_noise():
|
||||
add_page = self.app.get(reverse("domain-users-add", kwargs={"pk": self.domain.id}))
|
||||
session_id = self.app.cookies[settings.SESSION_COOKIE_NAME]
|
||||
add_page.form["email"] = email_address
|
||||
|
@ -1803,6 +1971,7 @@ class TestDomainManagers(TestDomainOverview):
|
|||
mock_client_instance = mock_client.return_value
|
||||
|
||||
with boto3_mocking.clients.handler_for("sesv2", mock_client):
|
||||
with less_console_noise():
|
||||
add_page = self.app.get(reverse("domain-users-add", kwargs={"pk": self.domain.id}))
|
||||
session_id = self.app.cookies[settings.SESSION_COOKIE_NAME]
|
||||
add_page.form["email"] = email_address
|
||||
|
@ -1841,6 +2010,7 @@ class TestDomainManagers(TestDomainOverview):
|
|||
mock_client_instance = mock_client.return_value
|
||||
|
||||
with boto3_mocking.clients.handler_for("sesv2", mock_client):
|
||||
with less_console_noise():
|
||||
add_page = self.app.get(reverse("domain-users-add", kwargs={"pk": self.domain.id}))
|
||||
session_id = self.app.cookies[settings.SESSION_COOKIE_NAME]
|
||||
add_page.form["email"] = email_address
|
||||
|
@ -1883,6 +2053,7 @@ class TestDomainManagers(TestDomainOverview):
|
|||
mock_client_instance = mock_client.return_value
|
||||
|
||||
with boto3_mocking.clients.handler_for("sesv2", mock_client):
|
||||
with less_console_noise():
|
||||
add_page = self.app.get(reverse("domain-users-add", kwargs={"pk": self.domain.id}))
|
||||
session_id = self.app.cookies[settings.SESSION_COOKIE_NAME]
|
||||
add_page.form["email"] = email_address
|
||||
|
@ -1922,10 +2093,10 @@ class TestDomainManagers(TestDomainOverview):
|
|||
self.domain_information, _ = DomainInformation.objects.get_or_create(creator=self.user, domain=self.domain)
|
||||
|
||||
mock_client = MagicMock()
|
||||
|
||||
mock_error_message = MagicMock()
|
||||
with boto3_mocking.clients.handler_for("sesv2", mock_client):
|
||||
with patch("django.contrib.messages.error") as mock_error_message:
|
||||
with less_console_noise():
|
||||
add_page = self.app.get(reverse("domain-users-add", kwargs={"pk": self.domain.id}))
|
||||
session_id = self.app.cookies[settings.SESSION_COOKIE_NAME]
|
||||
add_page.form["email"] = email_address
|
||||
|
@ -1959,6 +2130,7 @@ class TestDomainManagers(TestDomainOverview):
|
|||
mock_error_message = MagicMock()
|
||||
with boto3_mocking.clients.handler_for("sesv2", mock_client):
|
||||
with patch("django.contrib.messages.error") as mock_error_message:
|
||||
with less_console_noise():
|
||||
add_page = self.app.get(reverse("domain-users-add", kwargs={"pk": self.domain.id}))
|
||||
session_id = self.app.cookies[settings.SESSION_COOKIE_NAME]
|
||||
add_page.form["email"] = email_address
|
||||
|
@ -1977,7 +2149,11 @@ class TestDomainManagers(TestDomainOverview):
|
|||
"""Posting to the delete view deletes an invitation."""
|
||||
email_address = "mayor@igorville.gov"
|
||||
invitation, _ = DomainInvitation.objects.get_or_create(domain=self.domain, email=email_address)
|
||||
mock_client = MockSESClient()
|
||||
with boto3_mocking.clients.handler_for("sesv2", mock_client):
|
||||
with less_console_noise():
|
||||
self.client.post(reverse("invitation-delete", kwargs={"pk": invitation.id}))
|
||||
mock_client.EMAILS_SENT.clear()
|
||||
with self.assertRaises(DomainInvitation.DoesNotExist):
|
||||
DomainInvitation.objects.get(id=invitation.id)
|
||||
|
||||
|
@ -1989,8 +2165,11 @@ class TestDomainManagers(TestDomainOverview):
|
|||
other_user = User()
|
||||
other_user.save()
|
||||
self.client.force_login(other_user)
|
||||
mock_client = MagicMock()
|
||||
with boto3_mocking.clients.handler_for("sesv2", mock_client):
|
||||
with less_console_noise(): # permission denied makes console errors
|
||||
result = self.client.post(reverse("invitation-delete", kwargs={"pk": invitation.id}))
|
||||
|
||||
self.assertEqual(result.status_code, 403)
|
||||
|
||||
@boto3_mocking.patching
|
||||
|
@ -2006,6 +2185,10 @@ class TestDomainManagers(TestDomainOverview):
|
|||
session_id = self.app.cookies[settings.SESSION_COOKIE_NAME]
|
||||
add_page.form["email"] = email_address
|
||||
self.app.set_cookie(settings.SESSION_COOKIE_NAME, session_id)
|
||||
|
||||
mock_client = MagicMock()
|
||||
with boto3_mocking.clients.handler_for("sesv2", mock_client):
|
||||
with less_console_noise():
|
||||
add_page.form.submit()
|
||||
|
||||
# user was invited, create them
|
||||
|
@ -2061,6 +2244,7 @@ class TestDomainNameservers(TestDomainOverview):
|
|||
# attempt to submit the form without two hosts, both subdomains,
|
||||
# only one has ips
|
||||
nameservers_page.form["form-1-server"] = "ns2.igorville.gov"
|
||||
|
||||
with less_console_noise(): # swallow log warning message
|
||||
result = nameservers_page.form.submit()
|
||||
# form submission was a post with an error, response should be a 200
|
||||
|
@ -2396,6 +2580,8 @@ class TestDomainSecurityEmail(TestDomainOverview):
|
|||
session_id = self.app.cookies[settings.SESSION_COOKIE_NAME]
|
||||
security_email_page.form["security_email"] = "mayor@igorville.gov"
|
||||
self.app.set_cookie(settings.SESSION_COOKIE_NAME, session_id)
|
||||
mock_client = MagicMock()
|
||||
with boto3_mocking.clients.handler_for("sesv2", mock_client):
|
||||
with less_console_noise(): # swallow log warning message
|
||||
result = security_email_page.form.submit()
|
||||
self.assertEqual(result.status_code, 302)
|
||||
|
@ -2743,6 +2929,9 @@ class TestApplicationStatus(TestWithUser, WebTest):
|
|||
self.assertContains(detail_page, "Admin Tester")
|
||||
self.assertContains(detail_page, "Status:")
|
||||
# click the "Withdraw request" button
|
||||
mock_client = MockSESClient()
|
||||
with boto3_mocking.clients.handler_for("sesv2", mock_client):
|
||||
with less_console_noise():
|
||||
withdraw_page = detail_page.click("Withdraw request")
|
||||
self.assertContains(withdraw_page, "Withdraw request for")
|
||||
home_page = withdraw_page.click("Withdraw request")
|
||||
|
|
|
@ -1,11 +1,14 @@
|
|||
"""Utilities for sending emails."""
|
||||
|
||||
import boto3
|
||||
|
||||
import logging
|
||||
from django.conf import settings
|
||||
from django.template.loader import get_template
|
||||
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
|
||||
class EmailSendingError(RuntimeError):
|
||||
|
||||
"""Local error for handling all failures when sending email."""
|
||||
|
@ -20,7 +23,7 @@ def send_templated_email(template_name: str, subject_template_name: str, to_addr
|
|||
context as Django's HTML templates. context gives additional information
|
||||
that the template may use.
|
||||
"""
|
||||
|
||||
logger.info(f"An email was sent! Template name: {template_name} to {to_address}")
|
||||
template = get_template(template_name)
|
||||
email_body = template.render(context=context)
|
||||
|
||||
|
|
|
@ -206,15 +206,15 @@ class ApplicationWizard(ApplicationWizardPermissionView, TemplateView):
|
|||
# if accessing this class directly, redirect to the first step
|
||||
# in other words, if `ApplicationWizard` is called as view
|
||||
# directly by some redirect or url handler, we'll send users
|
||||
# to the first step in the processes; subclasses will NOT
|
||||
# be redirected. The purpose of this is to allow code to
|
||||
# either to an acknowledgement page or to the first step in
|
||||
# the processes (if an edit rather than a new request); subclasses
|
||||
# will NOT be redirected. The purpose of this is to allow code to
|
||||
# send users "to the application wizard" without needing to
|
||||
# know which view is first in the list of steps.
|
||||
if self.__class__ == ApplicationWizard:
|
||||
# if starting a new application, clear the storage
|
||||
if request.path_info == self.NEW_URL_NAME:
|
||||
del self.storage
|
||||
|
||||
return render(request, "application_intro.html")
|
||||
else:
|
||||
return self.goto(self.steps.first)
|
||||
|
||||
self.steps.current = current_url
|
||||
|
@ -370,13 +370,20 @@ class ApplicationWizard(ApplicationWizardPermissionView, TemplateView):
|
|||
|
||||
def post(self, request, *args, **kwargs) -> HttpResponse:
|
||||
"""This method handles POST requests."""
|
||||
# if accessing this class directly, redirect to the first step
|
||||
if self.__class__ == ApplicationWizard:
|
||||
return self.goto(self.steps.first)
|
||||
|
||||
# which button did the user press?
|
||||
button: str = request.POST.get("submit_button", "")
|
||||
|
||||
# if user has acknowledged the intro message
|
||||
if button == "intro_acknowledge":
|
||||
if request.path_info == self.NEW_URL_NAME:
|
||||
del self.storage
|
||||
return self.goto(self.steps.first)
|
||||
|
||||
# if accessing this class directly, redirect to the first step
|
||||
if self.__class__ == ApplicationWizard:
|
||||
return self.goto(self.steps.first)
|
||||
|
||||
forms = self.get_forms(use_post=True)
|
||||
if self.is_valid(forms):
|
||||
# always save progress
|
||||
|
|
|
@ -67,6 +67,7 @@
|
|||
10038 OUTOFSCOPE http://app:8080/dns/nameservers
|
||||
10038 OUTOFSCOPE http://app:8080/dns/dnssec
|
||||
10038 OUTOFSCOPE http://app:8080/dns/dnssec/dsdata
|
||||
10038 OUTOFSCOPE http://app:8080/org-name-address
|
||||
# This URL always returns 404, so include it as well.
|
||||
10038 OUTOFSCOPE http://app:8080/todo
|
||||
# OIDC isn't configured in the test environment and DEBUG=True so this gives a 500 without CSP headers
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue