mirror of
https://github.com/cisagov/manage.get.gov.git
synced 2025-07-14 06:55:08 +02:00
1072 lines
46 KiB
Python
1072 lines
46 KiB
Python
from django.test import TestCase
|
|
from django.db.utils import IntegrityError
|
|
from django.db import transaction
|
|
from unittest.mock import patch
|
|
|
|
|
|
from registrar.models import (
|
|
Contact,
|
|
DomainRequest,
|
|
DomainInformation,
|
|
User,
|
|
Website,
|
|
Domain,
|
|
DraftDomain,
|
|
FederalAgency,
|
|
AllowedEmail,
|
|
Portfolio,
|
|
)
|
|
|
|
import boto3_mocking
|
|
from registrar.utility.constants import BranchChoices
|
|
from registrar.utility.errors import FSMDomainRequestError
|
|
|
|
from .common import (
|
|
MockSESClient,
|
|
less_console_noise,
|
|
completed_domain_request,
|
|
set_domain_request_investigators,
|
|
)
|
|
from django_fsm import TransitionNotAllowed
|
|
|
|
from api.tests.common import less_console_noise_decorator
|
|
|
|
|
|
@boto3_mocking.patching
|
|
class TestDomainRequest(TestCase):
|
|
@less_console_noise_decorator
|
|
def setUp(self):
|
|
|
|
self.dummy_user, _ = Contact.objects.get_or_create(
|
|
email="mayor@igorville.com", first_name="Hello", last_name="World"
|
|
)
|
|
self.dummy_user_2, _ = User.objects.get_or_create(
|
|
username="intern@igorville.com", email="intern@igorville.com", first_name="Lava", last_name="World"
|
|
)
|
|
self.started_domain_request = completed_domain_request(
|
|
status=DomainRequest.DomainRequestStatus.STARTED,
|
|
name="started.gov",
|
|
)
|
|
self.submitted_domain_request = completed_domain_request(
|
|
status=DomainRequest.DomainRequestStatus.SUBMITTED,
|
|
name="submitted.gov",
|
|
)
|
|
self.in_review_domain_request = completed_domain_request(
|
|
status=DomainRequest.DomainRequestStatus.IN_REVIEW,
|
|
name="in-review.gov",
|
|
)
|
|
self.action_needed_domain_request = completed_domain_request(
|
|
status=DomainRequest.DomainRequestStatus.ACTION_NEEDED,
|
|
name="action-needed.gov",
|
|
)
|
|
self.approved_domain_request = completed_domain_request(
|
|
status=DomainRequest.DomainRequestStatus.APPROVED,
|
|
name="approved.gov",
|
|
)
|
|
self.withdrawn_domain_request = completed_domain_request(
|
|
status=DomainRequest.DomainRequestStatus.WITHDRAWN,
|
|
name="withdrawn.gov",
|
|
)
|
|
self.rejected_domain_request = completed_domain_request(
|
|
status=DomainRequest.DomainRequestStatus.REJECTED,
|
|
name="rejected.gov",
|
|
)
|
|
self.ineligible_domain_request = completed_domain_request(
|
|
status=DomainRequest.DomainRequestStatus.INELIGIBLE,
|
|
name="ineligible.gov",
|
|
)
|
|
|
|
# Store all domain request statuses in a variable for ease of use
|
|
self.all_domain_requests = [
|
|
self.started_domain_request,
|
|
self.submitted_domain_request,
|
|
self.in_review_domain_request,
|
|
self.action_needed_domain_request,
|
|
self.approved_domain_request,
|
|
self.withdrawn_domain_request,
|
|
self.rejected_domain_request,
|
|
self.ineligible_domain_request,
|
|
]
|
|
|
|
self.mock_client = MockSESClient()
|
|
|
|
def tearDown(self):
|
|
super().tearDown()
|
|
DomainInformation.objects.all().delete()
|
|
DomainRequest.objects.all().delete()
|
|
DraftDomain.objects.all().delete()
|
|
Domain.objects.all().delete()
|
|
Portfolio.objects.all().delete()
|
|
User.objects.all().delete()
|
|
self.mock_client.EMAILS_SENT.clear()
|
|
|
|
def assertNotRaises(self, exception_type):
|
|
"""Helper method for testing allowed transitions."""
|
|
with less_console_noise():
|
|
return self.assertRaises(Exception, None, exception_type)
|
|
|
|
@less_console_noise_decorator
|
|
def test_request_is_withdrawable(self):
|
|
"""Tests the is_withdrawable function"""
|
|
domain_request_1 = completed_domain_request(
|
|
status=DomainRequest.DomainRequestStatus.SUBMITTED,
|
|
name="city2.gov",
|
|
)
|
|
domain_request_2 = completed_domain_request(
|
|
status=DomainRequest.DomainRequestStatus.IN_REVIEW,
|
|
name="city3.gov",
|
|
)
|
|
domain_request_3 = completed_domain_request(
|
|
status=DomainRequest.DomainRequestStatus.ACTION_NEEDED,
|
|
name="city4.gov",
|
|
)
|
|
domain_request_4 = completed_domain_request(
|
|
status=DomainRequest.DomainRequestStatus.REJECTED,
|
|
name="city5.gov",
|
|
)
|
|
self.assertTrue(domain_request_1.is_withdrawable())
|
|
self.assertTrue(domain_request_2.is_withdrawable())
|
|
self.assertTrue(domain_request_3.is_withdrawable())
|
|
self.assertFalse(domain_request_4.is_withdrawable())
|
|
|
|
@less_console_noise_decorator
|
|
def test_request_is_awaiting_review(self):
|
|
"""Tests the is_awaiting_review function"""
|
|
domain_request_1 = completed_domain_request(
|
|
status=DomainRequest.DomainRequestStatus.SUBMITTED,
|
|
name="city2.gov",
|
|
)
|
|
domain_request_2 = completed_domain_request(
|
|
status=DomainRequest.DomainRequestStatus.IN_REVIEW,
|
|
name="city3.gov",
|
|
)
|
|
domain_request_3 = completed_domain_request(
|
|
status=DomainRequest.DomainRequestStatus.ACTION_NEEDED,
|
|
name="city4.gov",
|
|
)
|
|
domain_request_4 = completed_domain_request(
|
|
status=DomainRequest.DomainRequestStatus.REJECTED,
|
|
name="city5.gov",
|
|
)
|
|
self.assertTrue(domain_request_1.is_awaiting_review())
|
|
self.assertTrue(domain_request_2.is_awaiting_review())
|
|
self.assertFalse(domain_request_3.is_awaiting_review())
|
|
self.assertFalse(domain_request_4.is_awaiting_review())
|
|
|
|
@less_console_noise_decorator
|
|
def test_federal_agency_set_to_non_federal_on_approve(self):
|
|
"""Ensures that when the federal_agency field is 'none' when .approve() is called,
|
|
the field is set to the 'Non-Federal Agency' record"""
|
|
domain_request = completed_domain_request(
|
|
status=DomainRequest.DomainRequestStatus.IN_REVIEW,
|
|
name="city2.gov",
|
|
federal_agency=None,
|
|
)
|
|
|
|
# Ensure that the federal agency is None
|
|
self.assertEqual(domain_request.federal_agency, None)
|
|
|
|
# Approve the request
|
|
domain_request.approve()
|
|
self.assertEqual(domain_request.status, DomainRequest.DomainRequestStatus.APPROVED)
|
|
|
|
# After approval, it should be "Non-Federal agency"
|
|
expected_federal_agency = FederalAgency.objects.filter(agency="Non-Federal Agency").first()
|
|
self.assertEqual(domain_request.federal_agency, expected_federal_agency)
|
|
|
|
def test_empty_create_fails(self):
|
|
"""Can't create a completely empty domain request."""
|
|
with less_console_noise():
|
|
with transaction.atomic():
|
|
with self.assertRaisesRegex(IntegrityError, "creator"):
|
|
DomainRequest.objects.create()
|
|
|
|
@less_console_noise_decorator
|
|
def test_minimal_create(self):
|
|
"""Can create with just a creator."""
|
|
user, _ = User.objects.get_or_create(username="testy")
|
|
domain_request = DomainRequest.objects.create(creator=user)
|
|
self.assertEqual(domain_request.status, DomainRequest.DomainRequestStatus.STARTED)
|
|
|
|
@less_console_noise_decorator
|
|
def test_full_create(self):
|
|
"""Can create with all fields."""
|
|
user, _ = User.objects.get_or_create(username="testy")
|
|
contact = Contact.objects.create()
|
|
com_website, _ = Website.objects.get_or_create(website="igorville.com")
|
|
gov_website, _ = Website.objects.get_or_create(website="igorville.gov")
|
|
domain, _ = DraftDomain.objects.get_or_create(name="igorville.gov")
|
|
domain_request = DomainRequest.objects.create(
|
|
creator=user,
|
|
investigator=user,
|
|
generic_org_type=DomainRequest.OrganizationChoices.FEDERAL,
|
|
federal_type=BranchChoices.EXECUTIVE,
|
|
is_election_board=False,
|
|
organization_name="Test",
|
|
address_line1="100 Main St.",
|
|
address_line2="APT 1A",
|
|
state_territory="CA",
|
|
zipcode="12345-6789",
|
|
senior_official=contact,
|
|
requested_domain=domain,
|
|
purpose="Igorville rules!",
|
|
anything_else="All of Igorville loves the dotgov program.",
|
|
is_policy_acknowledged=True,
|
|
)
|
|
domain_request.current_websites.add(com_website)
|
|
domain_request.alternative_domains.add(gov_website)
|
|
domain_request.other_contacts.add(contact)
|
|
domain_request.save()
|
|
|
|
@less_console_noise_decorator
|
|
def test_domain_info(self):
|
|
"""Can create domain info with all fields."""
|
|
user, _ = User.objects.get_or_create(username="testy")
|
|
contact = Contact.objects.create()
|
|
domain, _ = Domain.objects.get_or_create(name="igorville.gov")
|
|
information = DomainInformation.objects.create(
|
|
creator=user,
|
|
generic_org_type=DomainInformation.OrganizationChoices.FEDERAL,
|
|
federal_type=BranchChoices.EXECUTIVE,
|
|
is_election_board=False,
|
|
organization_name="Test",
|
|
address_line1="100 Main St.",
|
|
address_line2="APT 1A",
|
|
state_territory="CA",
|
|
zipcode="12345-6789",
|
|
senior_official=contact,
|
|
purpose="Igorville rules!",
|
|
anything_else="All of Igorville loves the dotgov program.",
|
|
is_policy_acknowledged=True,
|
|
domain=domain,
|
|
)
|
|
information.other_contacts.add(contact)
|
|
information.save()
|
|
self.assertEqual(information.domain.id, domain.id)
|
|
self.assertEqual(information.id, domain.domain_info.id)
|
|
|
|
@less_console_noise_decorator
|
|
def test_status_fsm_submit_fail(self):
|
|
user, _ = User.objects.get_or_create(username="testy")
|
|
domain_request = DomainRequest.objects.create(creator=user)
|
|
|
|
with boto3_mocking.clients.handler_for("sesv2", self.mock_client):
|
|
with less_console_noise():
|
|
with self.assertRaises(ValueError):
|
|
# can't submit a domain request with a null domain name
|
|
domain_request.submit()
|
|
|
|
@less_console_noise_decorator
|
|
def test_status_fsm_submit_succeed(self):
|
|
user, _ = User.objects.get_or_create(username="testy")
|
|
site = DraftDomain.objects.create(name="igorville.gov")
|
|
domain_request = DomainRequest.objects.create(creator=user, requested_domain=site)
|
|
|
|
# no email sent to creator so this emits a log warning
|
|
|
|
with boto3_mocking.clients.handler_for("sesv2", self.mock_client):
|
|
with less_console_noise():
|
|
domain_request.submit()
|
|
self.assertEqual(domain_request.status, domain_request.DomainRequestStatus.SUBMITTED)
|
|
|
|
def check_email_sent(
|
|
self, domain_request, msg, action, expected_count, expected_content=None, expected_email="mayor@igorville.com"
|
|
):
|
|
"""Check if an email was sent after performing an action."""
|
|
email_allowed, _ = AllowedEmail.objects.get_or_create(email=expected_email)
|
|
with self.subTest(msg=msg, action=action):
|
|
with boto3_mocking.clients.handler_for("sesv2", self.mock_client):
|
|
# Perform the specified action
|
|
action_method = getattr(domain_request, action)
|
|
action_method()
|
|
domain_request.save()
|
|
|
|
# Check if an email was sent
|
|
sent_emails = [
|
|
email
|
|
for email in MockSESClient.EMAILS_SENT
|
|
if expected_email in email["kwargs"]["Destination"]["ToAddresses"]
|
|
]
|
|
self.assertEqual(len(sent_emails), expected_count)
|
|
|
|
if expected_content:
|
|
email_content = sent_emails[0]["kwargs"]["Content"]["Simple"]["Body"]["Text"]["Data"]
|
|
self.assertIn(expected_content, email_content)
|
|
|
|
email_allowed.delete()
|
|
|
|
@less_console_noise_decorator
|
|
def test_submit_from_started_sends_email_to_creator(self):
|
|
"""tests that we send an email to the creator"""
|
|
msg = "Create a domain request and submit it and see if email was sent when the feature flag is on."
|
|
domain_request = completed_domain_request(user=self.dummy_user_2)
|
|
self.check_email_sent(
|
|
domain_request, msg, "submit", 1, expected_content="Lava", expected_email="intern@igorville.com"
|
|
)
|
|
|
|
@less_console_noise_decorator
|
|
def test_submit_from_withdrawn_sends_email(self):
|
|
msg = "Create a withdrawn domain request and submit it and see if email was sent."
|
|
user, _ = User.objects.get_or_create(username="testy", email="testy@town.com")
|
|
domain_request = completed_domain_request(status=DomainRequest.DomainRequestStatus.WITHDRAWN, user=user)
|
|
self.check_email_sent(domain_request, msg, "submit", 1, expected_content="Hi", expected_email=user.email)
|
|
|
|
@less_console_noise_decorator
|
|
def test_submit_from_action_needed_does_not_send_email(self):
|
|
msg = "Create a domain request with ACTION_NEEDED status and submit it, check if email was not sent."
|
|
domain_request = completed_domain_request(status=DomainRequest.DomainRequestStatus.ACTION_NEEDED)
|
|
self.check_email_sent(domain_request, msg, "submit", 0)
|
|
|
|
@less_console_noise_decorator
|
|
def test_submit_from_in_review_does_not_send_email(self):
|
|
msg = "Create a withdrawn domain request and submit it and see if email was sent."
|
|
domain_request = completed_domain_request(status=DomainRequest.DomainRequestStatus.IN_REVIEW)
|
|
self.check_email_sent(domain_request, msg, "submit", 0)
|
|
|
|
@less_console_noise_decorator
|
|
def test_approve_sends_email(self):
|
|
msg = "Create a domain request and approve it and see if email was sent."
|
|
user, _ = User.objects.get_or_create(username="testy", email="testy@town.com")
|
|
domain_request = completed_domain_request(status=DomainRequest.DomainRequestStatus.IN_REVIEW, user=user)
|
|
self.check_email_sent(domain_request, msg, "approve", 1, expected_content="approved", expected_email=user.email)
|
|
|
|
@less_console_noise_decorator
|
|
def test_withdraw_sends_email(self):
|
|
msg = "Create a domain request and withdraw it and see if email was sent."
|
|
user, _ = User.objects.get_or_create(username="testy", email="testy@town.com")
|
|
domain_request = completed_domain_request(status=DomainRequest.DomainRequestStatus.IN_REVIEW, user=user)
|
|
self.check_email_sent(
|
|
domain_request, msg, "withdraw", 1, expected_content="withdrawn", expected_email=user.email
|
|
)
|
|
|
|
def test_reject_sends_email(self):
|
|
"Create a domain request and reject it and see if email was sent."
|
|
user, _ = User.objects.get_or_create(username="testy", email="testy@town.com")
|
|
domain_request = completed_domain_request(status=DomainRequest.DomainRequestStatus.APPROVED, user=user)
|
|
expected_email = user.email
|
|
email_allowed, _ = AllowedEmail.objects.get_or_create(email=expected_email)
|
|
with boto3_mocking.clients.handler_for("sesv2", self.mock_client):
|
|
domain_request.reject()
|
|
domain_request.rejection_reason = domain_request.RejectionReasons.CONTACTS_NOT_VERIFIED
|
|
domain_request.rejection_reason_email = "test"
|
|
domain_request.save()
|
|
|
|
# Check if an email was sent
|
|
sent_emails = [
|
|
email
|
|
for email in MockSESClient.EMAILS_SENT
|
|
if expected_email in email["kwargs"]["Destination"]["ToAddresses"]
|
|
]
|
|
self.assertEqual(len(sent_emails), 1)
|
|
|
|
email_content = sent_emails[0]["kwargs"]["Content"]["Simple"]["Body"]["Text"]["Data"]
|
|
self.assertIn("test", email_content)
|
|
|
|
email_allowed.delete()
|
|
|
|
@less_console_noise_decorator
|
|
def test_reject_with_prejudice_does_not_send_email(self):
|
|
msg = "Create a domain request and reject it with prejudice and see if email was sent."
|
|
domain_request = completed_domain_request(status=DomainRequest.DomainRequestStatus.APPROVED)
|
|
self.check_email_sent(domain_request, msg, "reject_with_prejudice", 0)
|
|
|
|
@less_console_noise_decorator
|
|
def assert_fsm_transition_raises_error(self, test_cases, method_to_run):
|
|
"""Given a list of test cases, check if each transition throws the intended error"""
|
|
with boto3_mocking.clients.handler_for("sesv2", self.mock_client), less_console_noise():
|
|
for domain_request, exception_type in test_cases:
|
|
with self.subTest(domain_request=domain_request, exception_type=exception_type):
|
|
with self.assertRaises(exception_type):
|
|
# Retrieve the method by name from the domain_request object and call it
|
|
method = getattr(domain_request, method_to_run)
|
|
# Call the method
|
|
method()
|
|
|
|
@less_console_noise_decorator
|
|
def assert_fsm_transition_does_not_raise_error(self, test_cases, method_to_run):
|
|
"""Given a list of test cases, ensure that none of them throw transition errors"""
|
|
with boto3_mocking.clients.handler_for("sesv2", self.mock_client), less_console_noise():
|
|
for domain_request, exception_type in test_cases:
|
|
with self.subTest(domain_request=domain_request, exception_type=exception_type):
|
|
try:
|
|
# Retrieve the method by name from the DomainRequest object and call it
|
|
method = getattr(domain_request, method_to_run)
|
|
# Call the method
|
|
method()
|
|
except exception_type:
|
|
self.fail(f"{exception_type} was raised, but it was not expected.")
|
|
|
|
@less_console_noise_decorator
|
|
def test_submit_transition_allowed_with_no_investigator(self):
|
|
"""
|
|
Tests for attempting to transition without an investigator.
|
|
For submit, this should be valid in all cases.
|
|
"""
|
|
|
|
test_cases = [
|
|
(self.started_domain_request, TransitionNotAllowed),
|
|
(self.in_review_domain_request, TransitionNotAllowed),
|
|
(self.action_needed_domain_request, TransitionNotAllowed),
|
|
(self.withdrawn_domain_request, TransitionNotAllowed),
|
|
]
|
|
|
|
# Set all investigators to none
|
|
set_domain_request_investigators(self.all_domain_requests, None)
|
|
|
|
self.assert_fsm_transition_does_not_raise_error(test_cases, "submit")
|
|
|
|
@less_console_noise_decorator
|
|
def test_submit_transition_allowed_with_investigator_not_staff(self):
|
|
"""
|
|
Tests for attempting to transition with an investigator user that is not staff.
|
|
For submit, this should be valid in all cases.
|
|
"""
|
|
|
|
test_cases = [
|
|
(self.in_review_domain_request, TransitionNotAllowed),
|
|
(self.action_needed_domain_request, TransitionNotAllowed),
|
|
]
|
|
|
|
# Set all investigators to a user with no staff privs
|
|
user, _ = User.objects.get_or_create(username="pancakesyrup", is_staff=False)
|
|
set_domain_request_investigators(self.all_domain_requests, user)
|
|
|
|
self.assert_fsm_transition_does_not_raise_error(test_cases, "submit")
|
|
|
|
@less_console_noise_decorator
|
|
def test_submit_transition_allowed(self):
|
|
"""
|
|
Test that calling submit from allowable statuses does raises TransitionNotAllowed.
|
|
"""
|
|
test_cases = [
|
|
(self.started_domain_request, TransitionNotAllowed),
|
|
(self.in_review_domain_request, TransitionNotAllowed),
|
|
(self.action_needed_domain_request, TransitionNotAllowed),
|
|
(self.withdrawn_domain_request, TransitionNotAllowed),
|
|
]
|
|
|
|
self.assert_fsm_transition_does_not_raise_error(test_cases, "submit")
|
|
|
|
@less_console_noise_decorator
|
|
def test_submit_transition_allowed_twice(self):
|
|
"""
|
|
Test that rotating between submit and in_review doesn't throw an error
|
|
"""
|
|
with boto3_mocking.clients.handler_for("sesv2", self.mock_client):
|
|
try:
|
|
# Make a submission
|
|
self.in_review_domain_request.submit()
|
|
|
|
# Rerun the old method to get back to the original state
|
|
self.in_review_domain_request.in_review()
|
|
|
|
# Make another submission
|
|
self.in_review_domain_request.submit()
|
|
except TransitionNotAllowed:
|
|
self.fail("TransitionNotAllowed was raised, but it was not expected.")
|
|
|
|
self.assertEqual(self.in_review_domain_request.status, DomainRequest.DomainRequestStatus.SUBMITTED)
|
|
|
|
@less_console_noise_decorator
|
|
def test_submit_transition_not_allowed(self):
|
|
"""
|
|
Test that calling submit against transition rules raises TransitionNotAllowed.
|
|
"""
|
|
test_cases = [
|
|
(self.submitted_domain_request, TransitionNotAllowed),
|
|
(self.approved_domain_request, TransitionNotAllowed),
|
|
(self.rejected_domain_request, TransitionNotAllowed),
|
|
(self.ineligible_domain_request, TransitionNotAllowed),
|
|
]
|
|
|
|
self.assert_fsm_transition_raises_error(test_cases, "submit")
|
|
|
|
@less_console_noise_decorator
|
|
def test_in_review_transition_allowed(self):
|
|
"""
|
|
Test that calling in_review from allowable statuses does raises TransitionNotAllowed.
|
|
"""
|
|
test_cases = [
|
|
(self.submitted_domain_request, TransitionNotAllowed),
|
|
(self.action_needed_domain_request, TransitionNotAllowed),
|
|
(self.approved_domain_request, TransitionNotAllowed),
|
|
(self.rejected_domain_request, TransitionNotAllowed),
|
|
(self.ineligible_domain_request, TransitionNotAllowed),
|
|
]
|
|
|
|
self.assert_fsm_transition_does_not_raise_error(test_cases, "in_review")
|
|
|
|
@less_console_noise_decorator
|
|
def test_in_review_transition_not_allowed_with_no_investigator(self):
|
|
"""
|
|
Tests for attempting to transition without an investigator
|
|
"""
|
|
|
|
test_cases = [
|
|
(self.action_needed_domain_request, TransitionNotAllowed),
|
|
(self.approved_domain_request, TransitionNotAllowed),
|
|
(self.rejected_domain_request, TransitionNotAllowed),
|
|
(self.ineligible_domain_request, TransitionNotAllowed),
|
|
]
|
|
|
|
# Set all investigators to none
|
|
set_domain_request_investigators(self.all_domain_requests, None)
|
|
|
|
self.assert_fsm_transition_raises_error(test_cases, "in_review")
|
|
|
|
@less_console_noise_decorator
|
|
def test_in_review_transition_not_allowed_with_investigator_not_staff(self):
|
|
"""
|
|
Tests for attempting to transition with an investigator that is not staff.
|
|
This should throw an exception.
|
|
"""
|
|
|
|
test_cases = [
|
|
(self.action_needed_domain_request, TransitionNotAllowed),
|
|
(self.approved_domain_request, TransitionNotAllowed),
|
|
(self.rejected_domain_request, TransitionNotAllowed),
|
|
(self.ineligible_domain_request, TransitionNotAllowed),
|
|
]
|
|
|
|
# Set all investigators to a user with no staff privs
|
|
user, _ = User.objects.get_or_create(username="pancakesyrup", is_staff=False)
|
|
set_domain_request_investigators(self.all_domain_requests, user)
|
|
|
|
self.assert_fsm_transition_raises_error(test_cases, "in_review")
|
|
|
|
@less_console_noise_decorator
|
|
def test_in_review_transition_not_allowed(self):
|
|
"""
|
|
Test that calling in_review against transition rules raises TransitionNotAllowed.
|
|
"""
|
|
test_cases = [
|
|
(self.started_domain_request, TransitionNotAllowed),
|
|
(self.in_review_domain_request, TransitionNotAllowed),
|
|
(self.withdrawn_domain_request, TransitionNotAllowed),
|
|
]
|
|
|
|
self.assert_fsm_transition_raises_error(test_cases, "in_review")
|
|
|
|
@less_console_noise_decorator
|
|
def test_action_needed_transition_allowed(self):
|
|
"""
|
|
Test that calling action_needed from allowable statuses does raises TransitionNotAllowed.
|
|
"""
|
|
test_cases = [
|
|
(self.in_review_domain_request, TransitionNotAllowed),
|
|
(self.approved_domain_request, TransitionNotAllowed),
|
|
(self.rejected_domain_request, TransitionNotAllowed),
|
|
(self.ineligible_domain_request, TransitionNotAllowed),
|
|
]
|
|
|
|
self.assert_fsm_transition_does_not_raise_error(test_cases, "action_needed")
|
|
|
|
@less_console_noise_decorator
|
|
def test_action_needed_transition_not_allowed_with_no_investigator(self):
|
|
"""
|
|
Tests for attempting to transition without an investigator
|
|
"""
|
|
|
|
test_cases = [
|
|
(self.in_review_domain_request, TransitionNotAllowed),
|
|
(self.approved_domain_request, TransitionNotAllowed),
|
|
(self.rejected_domain_request, TransitionNotAllowed),
|
|
(self.ineligible_domain_request, TransitionNotAllowed),
|
|
]
|
|
|
|
# Set all investigators to none
|
|
set_domain_request_investigators(self.all_domain_requests, None)
|
|
|
|
self.assert_fsm_transition_raises_error(test_cases, "action_needed")
|
|
|
|
@less_console_noise_decorator
|
|
def test_action_needed_transition_not_allowed_with_investigator_not_staff(self):
|
|
"""
|
|
Tests for attempting to transition with an investigator that is not staff
|
|
"""
|
|
|
|
test_cases = [
|
|
(self.in_review_domain_request, TransitionNotAllowed),
|
|
(self.approved_domain_request, TransitionNotAllowed),
|
|
(self.rejected_domain_request, TransitionNotAllowed),
|
|
(self.ineligible_domain_request, TransitionNotAllowed),
|
|
]
|
|
|
|
# Set all investigators to a user with no staff privs
|
|
user, _ = User.objects.get_or_create(username="pancakesyrup", is_staff=False)
|
|
set_domain_request_investigators(self.all_domain_requests, user)
|
|
|
|
self.assert_fsm_transition_raises_error(test_cases, "action_needed")
|
|
|
|
@less_console_noise_decorator
|
|
def test_action_needed_transition_not_allowed(self):
|
|
"""
|
|
Test that calling action_needed against transition rules raises TransitionNotAllowed.
|
|
"""
|
|
test_cases = [
|
|
(self.started_domain_request, TransitionNotAllowed),
|
|
(self.submitted_domain_request, TransitionNotAllowed),
|
|
(self.action_needed_domain_request, TransitionNotAllowed),
|
|
(self.withdrawn_domain_request, TransitionNotAllowed),
|
|
]
|
|
|
|
self.assert_fsm_transition_raises_error(test_cases, "action_needed")
|
|
|
|
@less_console_noise_decorator
|
|
def test_approved_transition_allowed(self):
|
|
"""
|
|
Test that calling action_needed from allowable statuses does raises TransitionNotAllowed.
|
|
"""
|
|
test_cases = [
|
|
(self.submitted_domain_request, TransitionNotAllowed),
|
|
(self.in_review_domain_request, TransitionNotAllowed),
|
|
(self.action_needed_domain_request, TransitionNotAllowed),
|
|
(self.rejected_domain_request, TransitionNotAllowed),
|
|
]
|
|
|
|
self.assert_fsm_transition_does_not_raise_error(test_cases, "approve")
|
|
|
|
@less_console_noise_decorator
|
|
def test_approved_transition_not_allowed_with_no_investigator(self):
|
|
"""
|
|
Tests for attempting to transition without an investigator
|
|
"""
|
|
|
|
test_cases = [
|
|
(self.in_review_domain_request, TransitionNotAllowed),
|
|
(self.action_needed_domain_request, TransitionNotAllowed),
|
|
(self.rejected_domain_request, TransitionNotAllowed),
|
|
]
|
|
|
|
# Set all investigators to none
|
|
set_domain_request_investigators(self.all_domain_requests, None)
|
|
|
|
self.assert_fsm_transition_raises_error(test_cases, "approve")
|
|
|
|
@less_console_noise_decorator
|
|
def test_approved_transition_not_allowed_with_investigator_not_staff(self):
|
|
"""
|
|
Tests for attempting to transition with an investigator that is not staff
|
|
"""
|
|
|
|
test_cases = [
|
|
(self.in_review_domain_request, TransitionNotAllowed),
|
|
(self.action_needed_domain_request, TransitionNotAllowed),
|
|
(self.rejected_domain_request, TransitionNotAllowed),
|
|
]
|
|
|
|
# Set all investigators to a user with no staff privs
|
|
user, _ = User.objects.get_or_create(username="pancakesyrup", is_staff=False)
|
|
set_domain_request_investigators(self.all_domain_requests, user)
|
|
|
|
self.assert_fsm_transition_raises_error(test_cases, "approve")
|
|
|
|
@less_console_noise_decorator
|
|
def test_approved_skips_sending_email(self):
|
|
"""
|
|
Test that calling .approve with send_email=False doesn't actually send
|
|
an email
|
|
"""
|
|
|
|
with boto3_mocking.clients.handler_for("sesv2", self.mock_client):
|
|
self.submitted_domain_request.approve(send_email=False)
|
|
|
|
# Assert that no emails were sent
|
|
self.assertEqual(len(self.mock_client.EMAILS_SENT), 0)
|
|
|
|
@less_console_noise_decorator
|
|
def test_approved_transition_not_allowed(self):
|
|
"""
|
|
Test that calling approve against transition rules raises TransitionNotAllowed.
|
|
"""
|
|
test_cases = [
|
|
(self.started_domain_request, TransitionNotAllowed),
|
|
(self.approved_domain_request, TransitionNotAllowed),
|
|
(self.withdrawn_domain_request, TransitionNotAllowed),
|
|
(self.ineligible_domain_request, TransitionNotAllowed),
|
|
]
|
|
self.assert_fsm_transition_raises_error(test_cases, "approve")
|
|
|
|
@less_console_noise_decorator
|
|
def test_approved_transition_not_allowed_when_domain_already_approved(self):
|
|
"""
|
|
Test that calling approve whith an already approved requested domain raises
|
|
TransitionNotAllowed.
|
|
"""
|
|
Domain.objects.all().create(name=self.submitted_domain_request.requested_domain.name)
|
|
test_cases = [
|
|
(self.submitted_domain_request, FSMDomainRequestError),
|
|
]
|
|
self.assert_fsm_transition_raises_error(test_cases, "approve")
|
|
|
|
@less_console_noise_decorator
|
|
def test_withdraw_transition_allowed(self):
|
|
"""
|
|
Test that calling action_needed from allowable statuses does raises TransitionNotAllowed.
|
|
"""
|
|
test_cases = [
|
|
(self.submitted_domain_request, TransitionNotAllowed),
|
|
(self.in_review_domain_request, TransitionNotAllowed),
|
|
(self.action_needed_domain_request, TransitionNotAllowed),
|
|
]
|
|
|
|
self.assert_fsm_transition_does_not_raise_error(test_cases, "withdraw")
|
|
|
|
@less_console_noise_decorator
|
|
def test_withdraw_transition_allowed_with_no_investigator(self):
|
|
"""
|
|
Tests for attempting to transition without an investigator.
|
|
For withdraw, this should be valid in all cases.
|
|
"""
|
|
|
|
test_cases = [
|
|
(self.submitted_domain_request, TransitionNotAllowed),
|
|
(self.in_review_domain_request, TransitionNotAllowed),
|
|
(self.action_needed_domain_request, TransitionNotAllowed),
|
|
]
|
|
|
|
# Set all investigators to none
|
|
set_domain_request_investigators(self.all_domain_requests, None)
|
|
|
|
self.assert_fsm_transition_does_not_raise_error(test_cases, "withdraw")
|
|
|
|
@less_console_noise_decorator
|
|
def test_withdraw_transition_allowed_with_investigator_not_staff(self):
|
|
"""
|
|
Tests for attempting to transition when investigator is not staff.
|
|
For withdraw, this should be valid in all cases.
|
|
"""
|
|
|
|
test_cases = [
|
|
(self.submitted_domain_request, TransitionNotAllowed),
|
|
(self.in_review_domain_request, TransitionNotAllowed),
|
|
(self.action_needed_domain_request, TransitionNotAllowed),
|
|
]
|
|
|
|
# Set all investigators to a user with no staff privs
|
|
user, _ = User.objects.get_or_create(username="pancakesyrup", is_staff=False)
|
|
set_domain_request_investigators(self.all_domain_requests, user)
|
|
|
|
self.assert_fsm_transition_does_not_raise_error(test_cases, "withdraw")
|
|
|
|
@less_console_noise_decorator
|
|
def test_withdraw_transition_not_allowed(self):
|
|
"""
|
|
Test that calling action_needed against transition rules raises TransitionNotAllowed.
|
|
"""
|
|
test_cases = [
|
|
(self.started_domain_request, TransitionNotAllowed),
|
|
(self.approved_domain_request, TransitionNotAllowed),
|
|
(self.withdrawn_domain_request, TransitionNotAllowed),
|
|
(self.rejected_domain_request, TransitionNotAllowed),
|
|
(self.ineligible_domain_request, TransitionNotAllowed),
|
|
]
|
|
|
|
self.assert_fsm_transition_raises_error(test_cases, "withdraw")
|
|
|
|
@less_console_noise_decorator
|
|
def test_reject_transition_allowed(self):
|
|
"""
|
|
Test that calling action_needed from allowable statuses does raises TransitionNotAllowed.
|
|
"""
|
|
test_cases = [
|
|
(self.in_review_domain_request, TransitionNotAllowed),
|
|
(self.action_needed_domain_request, TransitionNotAllowed),
|
|
(self.approved_domain_request, TransitionNotAllowed),
|
|
]
|
|
|
|
self.assert_fsm_transition_does_not_raise_error(test_cases, "reject")
|
|
|
|
@less_console_noise_decorator
|
|
def test_reject_transition_not_allowed_with_no_investigator(self):
|
|
"""
|
|
Tests for attempting to transition without an investigator
|
|
"""
|
|
|
|
test_cases = [
|
|
(self.in_review_domain_request, TransitionNotAllowed),
|
|
(self.action_needed_domain_request, TransitionNotAllowed),
|
|
(self.approved_domain_request, TransitionNotAllowed),
|
|
]
|
|
|
|
# Set all investigators to none
|
|
set_domain_request_investigators(self.all_domain_requests, None)
|
|
|
|
self.assert_fsm_transition_raises_error(test_cases, "reject")
|
|
|
|
@less_console_noise_decorator
|
|
def test_reject_transition_not_allowed_with_investigator_not_staff(self):
|
|
"""
|
|
Tests for attempting to transition when investigator is not staff
|
|
"""
|
|
|
|
test_cases = [
|
|
(self.in_review_domain_request, TransitionNotAllowed),
|
|
(self.action_needed_domain_request, TransitionNotAllowed),
|
|
(self.approved_domain_request, TransitionNotAllowed),
|
|
]
|
|
|
|
# Set all investigators to a user with no staff privs
|
|
user, _ = User.objects.get_or_create(username="pancakesyrup", is_staff=False)
|
|
set_domain_request_investigators(self.all_domain_requests, user)
|
|
|
|
self.assert_fsm_transition_raises_error(test_cases, "reject")
|
|
|
|
@less_console_noise_decorator
|
|
def test_reject_transition_not_allowed(self):
|
|
"""
|
|
Test that calling action_needed against transition rules raises TransitionNotAllowed.
|
|
"""
|
|
test_cases = [
|
|
(self.started_domain_request, TransitionNotAllowed),
|
|
(self.submitted_domain_request, TransitionNotAllowed),
|
|
(self.withdrawn_domain_request, TransitionNotAllowed),
|
|
(self.rejected_domain_request, TransitionNotAllowed),
|
|
(self.ineligible_domain_request, TransitionNotAllowed),
|
|
]
|
|
|
|
self.assert_fsm_transition_raises_error(test_cases, "reject")
|
|
|
|
@less_console_noise_decorator
|
|
def test_reject_with_prejudice_transition_allowed(self):
|
|
"""
|
|
Test that calling action_needed from allowable statuses does raises TransitionNotAllowed.
|
|
"""
|
|
test_cases = [
|
|
(self.in_review_domain_request, TransitionNotAllowed),
|
|
(self.action_needed_domain_request, TransitionNotAllowed),
|
|
(self.approved_domain_request, TransitionNotAllowed),
|
|
(self.rejected_domain_request, TransitionNotAllowed),
|
|
]
|
|
|
|
self.assert_fsm_transition_does_not_raise_error(test_cases, "reject_with_prejudice")
|
|
|
|
@less_console_noise_decorator
|
|
def test_reject_with_prejudice_transition_not_allowed_with_no_investigator(self):
|
|
"""
|
|
Tests for attempting to transition without an investigator
|
|
"""
|
|
|
|
test_cases = [
|
|
(self.in_review_domain_request, TransitionNotAllowed),
|
|
(self.action_needed_domain_request, TransitionNotAllowed),
|
|
(self.approved_domain_request, TransitionNotAllowed),
|
|
(self.rejected_domain_request, TransitionNotAllowed),
|
|
]
|
|
|
|
# Set all investigators to none
|
|
set_domain_request_investigators(self.all_domain_requests, None)
|
|
|
|
self.assert_fsm_transition_raises_error(test_cases, "reject_with_prejudice")
|
|
|
|
@less_console_noise_decorator
|
|
def test_reject_with_prejudice_not_allowed_with_investigator_not_staff(self):
|
|
"""
|
|
Tests for attempting to transition when investigator is not staff
|
|
"""
|
|
|
|
test_cases = [
|
|
(self.in_review_domain_request, TransitionNotAllowed),
|
|
(self.action_needed_domain_request, TransitionNotAllowed),
|
|
(self.approved_domain_request, TransitionNotAllowed),
|
|
(self.rejected_domain_request, TransitionNotAllowed),
|
|
]
|
|
|
|
# Set all investigators to a user with no staff privs
|
|
user, _ = User.objects.get_or_create(username="pancakesyrup", is_staff=False)
|
|
set_domain_request_investigators(self.all_domain_requests, user)
|
|
|
|
self.assert_fsm_transition_raises_error(test_cases, "reject_with_prejudice")
|
|
|
|
@less_console_noise_decorator
|
|
def test_reject_with_prejudice_transition_not_allowed(self):
|
|
"""
|
|
Test that calling action_needed against transition rules raises TransitionNotAllowed.
|
|
"""
|
|
test_cases = [
|
|
(self.started_domain_request, TransitionNotAllowed),
|
|
(self.submitted_domain_request, TransitionNotAllowed),
|
|
(self.withdrawn_domain_request, TransitionNotAllowed),
|
|
(self.ineligible_domain_request, TransitionNotAllowed),
|
|
]
|
|
|
|
self.assert_fsm_transition_raises_error(test_cases, "reject_with_prejudice")
|
|
|
|
@less_console_noise_decorator
|
|
def test_transition_not_allowed_approved_in_review_when_domain_is_active(self):
|
|
"""Create a domain request with status approved, create a matching domain that
|
|
is active, and call in_review against transition rules"""
|
|
|
|
domain = Domain.objects.create(name=self.approved_domain_request.requested_domain.name)
|
|
self.approved_domain_request.approved_domain = domain
|
|
self.approved_domain_request.save()
|
|
|
|
# Define a custom implementation for is_active
|
|
def custom_is_active(self):
|
|
return True # Override to return True
|
|
|
|
with boto3_mocking.clients.handler_for("sesv2", self.mock_client):
|
|
# Use patch to temporarily replace is_active with the custom implementation
|
|
with patch.object(Domain, "is_active", custom_is_active):
|
|
# Now, when you call is_active on Domain, it will return True
|
|
with self.assertRaises(TransitionNotAllowed):
|
|
self.approved_domain_request.in_review()
|
|
|
|
@less_console_noise_decorator
|
|
def test_transition_not_allowed_approved_action_needed_when_domain_is_active(self):
|
|
"""Create a domain request with status approved, create a matching domain that
|
|
is active, and call action_needed against transition rules"""
|
|
|
|
domain = Domain.objects.create(name=self.approved_domain_request.requested_domain.name)
|
|
self.approved_domain_request.approved_domain = domain
|
|
self.approved_domain_request.save()
|
|
|
|
# Define a custom implementation for is_active
|
|
def custom_is_active(self):
|
|
return True # Override to return True
|
|
|
|
with boto3_mocking.clients.handler_for("sesv2", self.mock_client):
|
|
# Use patch to temporarily replace is_active with the custom implementation
|
|
with patch.object(Domain, "is_active", custom_is_active):
|
|
# Now, when you call is_active on Domain, it will return True
|
|
with self.assertRaises(TransitionNotAllowed):
|
|
self.approved_domain_request.action_needed()
|
|
|
|
@less_console_noise_decorator
|
|
def test_transition_not_allowed_approved_rejected_when_domain_is_active(self):
|
|
"""Create a domain request with status approved, create a matching domain that
|
|
is active, and call reject against transition rules"""
|
|
|
|
domain = Domain.objects.create(name=self.approved_domain_request.requested_domain.name)
|
|
self.approved_domain_request.approved_domain = domain
|
|
self.approved_domain_request.save()
|
|
|
|
# Define a custom implementation for is_active
|
|
def custom_is_active(self):
|
|
return True # Override to return True
|
|
|
|
with boto3_mocking.clients.handler_for("sesv2", self.mock_client):
|
|
# Use patch to temporarily replace is_active with the custom implementation
|
|
with patch.object(Domain, "is_active", custom_is_active):
|
|
# Now, when you call is_active on Domain, it will return True
|
|
with self.assertRaises(TransitionNotAllowed):
|
|
self.approved_domain_request.reject()
|
|
|
|
@less_console_noise_decorator
|
|
def test_transition_not_allowed_approved_ineligible_when_domain_is_active(self):
|
|
"""Create a domain request with status approved, create a matching domain that
|
|
is active, and call reject_with_prejudice against transition rules"""
|
|
|
|
domain = Domain.objects.create(name=self.approved_domain_request.requested_domain.name)
|
|
self.approved_domain_request.approved_domain = domain
|
|
self.approved_domain_request.save()
|
|
|
|
# Define a custom implementation for is_active
|
|
def custom_is_active(self):
|
|
return True # Override to return True
|
|
|
|
with boto3_mocking.clients.handler_for("sesv2", self.mock_client):
|
|
# Use patch to temporarily replace is_active with the custom implementation
|
|
with patch.object(Domain, "is_active", custom_is_active):
|
|
# Now, when you call is_active on Domain, it will return True
|
|
with self.assertRaises(TransitionNotAllowed):
|
|
self.approved_domain_request.reject_with_prejudice()
|
|
|
|
@less_console_noise_decorator
|
|
def test_approve_from_rejected_clears_rejection_reason(self):
|
|
"""When transitioning from rejected to approved on a domain request,
|
|
the rejection_reason is cleared."""
|
|
|
|
# Create a sample domain request
|
|
domain_request = completed_domain_request(status=DomainRequest.DomainRequestStatus.REJECTED)
|
|
domain_request.rejection_reason = DomainRequest.RejectionReasons.DOMAIN_PURPOSE
|
|
|
|
# Approve
|
|
with boto3_mocking.clients.handler_for("sesv2", self.mock_client):
|
|
domain_request.approve()
|
|
|
|
self.assertEqual(domain_request.status, DomainRequest.DomainRequestStatus.APPROVED)
|
|
self.assertEqual(domain_request.rejection_reason, None)
|
|
|
|
@less_console_noise_decorator
|
|
def test_in_review_from_rejected_clears_rejection_reason(self):
|
|
"""When transitioning from rejected to in_review on a domain request,
|
|
the rejection_reason is cleared."""
|
|
|
|
# Create a sample domain request
|
|
domain_request = completed_domain_request(status=DomainRequest.DomainRequestStatus.REJECTED)
|
|
domain_request.domain_is_not_active = True
|
|
domain_request.rejection_reason = DomainRequest.RejectionReasons.DOMAIN_PURPOSE
|
|
|
|
# Approve
|
|
with boto3_mocking.clients.handler_for("sesv2", self.mock_client):
|
|
domain_request.in_review()
|
|
|
|
self.assertEqual(domain_request.status, DomainRequest.DomainRequestStatus.IN_REVIEW)
|
|
self.assertEqual(domain_request.rejection_reason, None)
|
|
|
|
@less_console_noise_decorator
|
|
def test_action_needed_from_rejected_clears_rejection_reason(self):
|
|
"""When transitioning from rejected to action_needed on a domain request,
|
|
the rejection_reason is cleared."""
|
|
|
|
# Create a sample domain request
|
|
domain_request = completed_domain_request(status=DomainRequest.DomainRequestStatus.REJECTED)
|
|
domain_request.domain_is_not_active = True
|
|
domain_request.rejection_reason = DomainRequest.RejectionReasons.DOMAIN_PURPOSE
|
|
|
|
# Approve
|
|
with boto3_mocking.clients.handler_for("sesv2", self.mock_client):
|
|
domain_request.action_needed()
|
|
|
|
self.assertEqual(domain_request.status, DomainRequest.DomainRequestStatus.ACTION_NEEDED)
|
|
self.assertEqual(domain_request.rejection_reason, None)
|
|
|
|
@less_console_noise_decorator
|
|
def test_has_rationale_returns_true(self):
|
|
"""has_rationale() returns true when a domain request has no_other_contacts_rationale"""
|
|
self.started_domain_request.no_other_contacts_rationale = "You talkin' to me?"
|
|
self.started_domain_request.save()
|
|
self.assertEquals(self.started_domain_request.has_rationale(), True)
|
|
|
|
@less_console_noise_decorator
|
|
def test_has_rationale_returns_false(self):
|
|
"""has_rationale() returns false when a domain request has no no_other_contacts_rationale"""
|
|
self.assertEquals(self.started_domain_request.has_rationale(), False)
|
|
|
|
@less_console_noise_decorator
|
|
def test_has_other_contacts_returns_true(self):
|
|
"""has_other_contacts() returns true when a domain request has other_contacts"""
|
|
# completed_domain_request has other contacts by default
|
|
self.assertEquals(self.started_domain_request.has_other_contacts(), True)
|
|
|
|
@less_console_noise_decorator
|
|
def test_has_other_contacts_returns_false(self):
|
|
"""has_other_contacts() returns false when a domain request has no other_contacts"""
|
|
domain_request = completed_domain_request(
|
|
status=DomainRequest.DomainRequestStatus.STARTED, name="no-others.gov", has_other_contacts=False
|
|
)
|
|
self.assertEquals(domain_request.has_other_contacts(), False)
|
|
|
|
@less_console_noise_decorator
|
|
def test_converted_type(self):
|
|
"""test that new property fields works as expected to pull domain req info such as fed agency,
|
|
generic org type, and others from portfolio"""
|
|
fed_agency = FederalAgency.objects.filter(agency="Non-Federal Agency").first()
|
|
portfolio = Portfolio.objects.create(
|
|
organization_name="Test Portfolio",
|
|
creator=self.dummy_user_2,
|
|
federal_agency=fed_agency,
|
|
organization_type=DomainRequest.OrganizationChoices.FEDERAL,
|
|
)
|
|
|
|
domain_request = completed_domain_request(name="domainre1.gov", portfolio=portfolio)
|
|
|
|
self.assertEqual(portfolio.organization_type, domain_request.converted_generic_org_type)
|
|
self.assertEqual(portfolio.federal_agency, domain_request.converted_federal_agency)
|
|
|
|
domain_request2 = completed_domain_request(
|
|
name="domainreq2.gov", federal_agency=fed_agency, generic_org_type=DomainRequest.OrganizationChoices.TRIBAL
|
|
)
|
|
self.assertEqual(domain_request2.generic_org_type, domain_request2.converted_generic_org_type)
|
|
self.assertEqual(domain_request2.federal_agency, domain_request2.converted_federal_agency)
|