manage.get.gov/src/registrar/tests/test_models.py
David Kennedy 6f0bc4ce6c
unit test
2024-11-15 07:21:20 -05:00

1813 lines
86 KiB
Python

from django.forms import ValidationError
from django.test import TestCase
from unittest.mock import patch
from django.test import RequestFactory
from registrar.models import (
Contact,
DomainRequest,
DomainInformation,
User,
Website,
Domain,
DraftDomain,
DomainInvitation,
UserDomainRole,
FederalAgency,
UserPortfolioPermission,
AllowedEmail,
)
import boto3_mocking
from registrar.models.portfolio import Portfolio
from registrar.models.portfolio_invitation import PortfolioInvitation
from registrar.models.transition_domain import TransitionDomain
from registrar.models.utility.portfolio_helper import UserPortfolioPermissionChoices, UserPortfolioRoleChoices
from registrar.models.verified_by_staff import VerifiedByStaff # type: ignore
from .common import (
MockSESClient,
completed_domain_request,
create_test_user,
)
from waffle.testutils import override_flag
from api.tests.common import less_console_noise_decorator
class TestDomainInformation(TestCase):
"""Test the DomainInformation model, when approved or otherwise"""
def setUp(self):
super().setUp()
self.mock_client = MockSESClient()
def tearDown(self):
super().tearDown()
self.mock_client.EMAILS_SENT.clear()
Domain.objects.all().delete()
DomainInformation.objects.all().delete()
DomainRequest.objects.all().delete()
User.objects.all().delete()
DraftDomain.objects.all().delete()
@boto3_mocking.patching
@less_console_noise_decorator
def test_approval_creates_info(self):
draft_domain, _ = DraftDomain.objects.get_or_create(name="igorville.gov")
user, _ = User.objects.get_or_create()
investigator, _ = User.objects.get_or_create(username="frenchtoast", is_staff=True)
domain_request = DomainRequest.objects.create(
creator=user, requested_domain=draft_domain, notes="test notes", investigator=investigator
)
with boto3_mocking.clients.handler_for("sesv2", self.mock_client):
# skip using the submit method
domain_request.status = DomainRequest.DomainRequestStatus.SUBMITTED
domain_request.approve()
# should be an information present for this domain
domain = Domain.objects.get(name="igorville.gov")
domain_information = DomainInformation.objects.filter(domain=domain)
self.assertTrue(domain_information.exists())
# Test that both objects are what we expect
current_domain_information = domain_information.get().__dict__
expected_domain_information = DomainInformation(
creator=user,
domain=domain,
notes="test notes",
domain_request=domain_request,
federal_agency=FederalAgency.objects.get(agency="Non-Federal Agency"),
).__dict__
# Test the two records for consistency
self.assertEqual(self.clean_dict(current_domain_information), self.clean_dict(expected_domain_information))
def clean_dict(self, dict_obj):
"""Cleans dynamic fields in a dictionary"""
bad_fields = ["_state", "created_at", "id", "updated_at"]
return {k: v for k, v in dict_obj.items() if k not in bad_fields}
class TestDomainInvitations(TestCase):
"""Test the retrieval of domain invitations."""
@less_console_noise_decorator
def setUp(self):
self.domain, _ = Domain.objects.get_or_create(name="igorville.gov")
self.email = "mayor@igorville.gov"
self.invitation, _ = DomainInvitation.objects.get_or_create(email=self.email, domain=self.domain)
self.user, _ = User.objects.get_or_create(email=self.email)
def tearDown(self):
super().tearDown()
# clean out the roles each time
UserDomainRole.objects.all().delete()
self.domain.delete()
self.invitation.delete()
User.objects.all().delete()
@less_console_noise_decorator
def test_retrieval_creates_role(self):
self.invitation.retrieve()
self.assertTrue(UserDomainRole.objects.get(user=self.user, domain=self.domain))
@less_console_noise_decorator
def test_retrieve_missing_user_error(self):
# get rid of matching users
User.objects.filter(email=self.email).delete()
with self.assertRaises(RuntimeError):
self.invitation.retrieve()
@less_console_noise_decorator
def test_retrieve_existing_role_no_error(self):
# make the overlapping role
UserDomainRole.objects.get_or_create(user=self.user, domain=self.domain, role=UserDomainRole.Roles.MANAGER)
# this is not an error but does produce a console warning
self.invitation.retrieve()
self.assertEqual(self.invitation.status, DomainInvitation.DomainInvitationStatus.RETRIEVED)
@less_console_noise_decorator
def test_retrieve_on_each_login(self):
"""A user's authenticate on_each_login callback retrieves their invitations."""
self.user.on_each_login()
self.assertTrue(UserDomainRole.objects.get(user=self.user, domain=self.domain))
class TestPortfolioInvitations(TestCase):
"""Test the retrieval of portfolio invitations."""
@less_console_noise_decorator
def setUp(self):
self.email = "mayor@igorville.gov"
self.email2 = "creator@igorville.gov"
self.user, _ = User.objects.get_or_create(email=self.email)
self.user2, _ = User.objects.get_or_create(email=self.email2, username="creator")
self.portfolio, _ = Portfolio.objects.get_or_create(creator=self.user2, organization_name="Hotel California")
self.portfolio_role_base = UserPortfolioRoleChoices.ORGANIZATION_MEMBER
self.portfolio_role_admin = UserPortfolioRoleChoices.ORGANIZATION_ADMIN
self.portfolio_permission_1 = UserPortfolioPermissionChoices.VIEW_ALL_REQUESTS
self.portfolio_permission_2 = UserPortfolioPermissionChoices.EDIT_REQUESTS
self.invitation, _ = PortfolioInvitation.objects.get_or_create(
email=self.email,
portfolio=self.portfolio,
roles=[self.portfolio_role_base, self.portfolio_role_admin],
additional_permissions=[self.portfolio_permission_1, self.portfolio_permission_2],
)
def tearDown(self):
super().tearDown()
DomainInvitation.objects.all().delete()
DomainInformation.objects.all().delete()
Domain.objects.all().delete()
UserPortfolioPermission.objects.all().delete()
Portfolio.objects.all().delete()
PortfolioInvitation.objects.all().delete()
User.objects.all().delete()
@less_console_noise_decorator
def test_retrieval(self):
portfolio_role_exists = UserPortfolioPermission.objects.filter(
user=self.user, portfolio=self.portfolio
).exists()
self.assertFalse(portfolio_role_exists)
self.invitation.retrieve()
self.user.refresh_from_db()
created_role = UserPortfolioPermission.objects.get(user=self.user, portfolio=self.portfolio)
self.assertEqual(created_role.portfolio.organization_name, "Hotel California")
self.assertEqual(created_role.roles, [self.portfolio_role_base, self.portfolio_role_admin])
self.assertEqual(
created_role.additional_permissions, [self.portfolio_permission_1, self.portfolio_permission_2]
)
self.assertEqual(self.invitation.status, PortfolioInvitation.PortfolioInvitationStatus.RETRIEVED)
@less_console_noise_decorator
def test_retrieve_missing_user_error(self):
# get rid of matching users
User.objects.filter(email=self.email).delete()
with self.assertRaises(RuntimeError):
self.invitation.retrieve()
@less_console_noise_decorator
def test_retrieve_user_already_member_error(self):
portfolio_role_exists = UserPortfolioPermission.objects.filter(
user=self.user, portfolio=self.portfolio
).exists()
self.assertFalse(portfolio_role_exists)
portfolio_role, _ = UserPortfolioPermission.objects.get_or_create(user=self.user, portfolio=self.portfolio)
self.assertEqual(portfolio_role.portfolio.organization_name, "Hotel California")
self.user.check_portfolio_invitations_on_login()
self.user.refresh_from_db()
roles = UserPortfolioPermission.objects.filter(user=self.user)
self.assertEqual(len(roles), 1)
self.assertEqual(self.invitation.status, PortfolioInvitation.PortfolioInvitationStatus.INVITED)
@less_console_noise_decorator
def test_retrieve_user_multiple_invitations(self):
"""Retrieve user portfolio invitations when there are multiple and multiple_options flag true."""
# create a 2nd portfolio and a 2nd portfolio invitation to self.user
portfolio2, _ = Portfolio.objects.get_or_create(creator=self.user2, organization_name="Take It Easy")
PortfolioInvitation.objects.get_or_create(
email=self.email,
portfolio=portfolio2,
roles=[self.portfolio_role_base, self.portfolio_role_admin],
additional_permissions=[self.portfolio_permission_1, self.portfolio_permission_2],
)
with override_flag("multiple_portfolios", active=True):
self.user.check_portfolio_invitations_on_login()
self.user.refresh_from_db()
roles = UserPortfolioPermission.objects.filter(user=self.user)
self.assertEqual(len(roles), 2)
updated_invitation1, _ = PortfolioInvitation.objects.get_or_create(
email=self.email, portfolio=self.portfolio
)
self.assertEqual(updated_invitation1.status, PortfolioInvitation.PortfolioInvitationStatus.RETRIEVED)
updated_invitation2, _ = PortfolioInvitation.objects.get_or_create(email=self.email, portfolio=portfolio2)
self.assertEqual(updated_invitation2.status, PortfolioInvitation.PortfolioInvitationStatus.RETRIEVED)
@less_console_noise_decorator
def test_retrieve_user_multiple_invitations_when_multiple_portfolios_inactive(self):
"""Attempt to retrieve user portfolio invitations when there are multiple
but multiple_portfolios flag set to False"""
# create a 2nd portfolio and a 2nd portfolio invitation to self.user
portfolio2, _ = Portfolio.objects.get_or_create(creator=self.user2, organization_name="Take It Easy")
PortfolioInvitation.objects.get_or_create(
email=self.email,
portfolio=portfolio2,
roles=[self.portfolio_role_base, self.portfolio_role_admin],
additional_permissions=[self.portfolio_permission_1, self.portfolio_permission_2],
)
self.user.check_portfolio_invitations_on_login()
self.user.refresh_from_db()
roles = UserPortfolioPermission.objects.filter(user=self.user)
self.assertEqual(len(roles), 1)
updated_invitation1, _ = PortfolioInvitation.objects.get_or_create(email=self.email, portfolio=self.portfolio)
self.assertEqual(updated_invitation1.status, PortfolioInvitation.PortfolioInvitationStatus.RETRIEVED)
updated_invitation2, _ = PortfolioInvitation.objects.get_or_create(email=self.email, portfolio=portfolio2)
self.assertEqual(updated_invitation2.status, PortfolioInvitation.PortfolioInvitationStatus.INVITED)
@less_console_noise_decorator
def test_get_managed_domains_count(self):
"""Test that the correct number of domains, which are associated with the portfolio and
have invited the email of the portfolio invitation, are returned."""
# Add three domains, one which is in the portfolio and email is invited to,
# one which is in the portfolio and email is not invited to,
# and one which is email is invited to and not in the portfolio.
# Arrange
# domain_in_portfolio should not be included in the count
domain_in_portfolio, _ = Domain.objects.get_or_create(name="domain_in_portfolio.gov", state=Domain.State.READY)
DomainInformation.objects.get_or_create(creator=self.user, domain=domain_in_portfolio, portfolio=self.portfolio)
# domain_in_portfolio_and_invited should be included in the count
domain_in_portfolio_and_invited, _ = Domain.objects.get_or_create(
name="domain_in_portfolio_and_invited.gov", state=Domain.State.READY
)
DomainInformation.objects.get_or_create(
creator=self.user, domain=domain_in_portfolio_and_invited, portfolio=self.portfolio
)
DomainInvitation.objects.get_or_create(email=self.email, domain=domain_in_portfolio_and_invited)
# domain_invited should not be included in the count
domain_invited, _ = Domain.objects.get_or_create(name="domain_invited.gov", state=Domain.State.READY)
DomainInformation.objects.get_or_create(creator=self.user, domain=domain_invited)
DomainInvitation.objects.get_or_create(email=self.email, domain=domain_invited)
# Assert
self.assertEqual(self.invitation.get_managed_domains_count(), 1)
@less_console_noise_decorator
def test_get_portfolio_permissions(self):
"""Test that get_portfolio_permissions returns the expected list of permissions,
based on the roles and permissions assigned to the invitation."""
# Arrange
test_permission_list = set()
# add the arrays that are defined in UserPortfolioPermission for member and admin
test_permission_list.update(
UserPortfolioPermission.PORTFOLIO_ROLE_PERMISSIONS.get(UserPortfolioRoleChoices.ORGANIZATION_MEMBER, [])
)
test_permission_list.update(
UserPortfolioPermission.PORTFOLIO_ROLE_PERMISSIONS.get(UserPortfolioRoleChoices.ORGANIZATION_ADMIN, [])
)
# add the permissions that are added to the invitation as additional_permissions
test_permission_list.update([self.portfolio_permission_1, self.portfolio_permission_2])
perm_list = list(test_permission_list)
# Verify
self.assertEquals(self.invitation.get_portfolio_permissions(), perm_list)
class TestUserPortfolioPermission(TestCase):
@less_console_noise_decorator
def setUp(self):
self.user, _ = User.objects.get_or_create(email="mayor@igorville.gov")
self.user2, _ = User.objects.get_or_create(email="user2@igorville.gov", username="user2")
super().setUp()
def tearDown(self):
super().tearDown()
Domain.objects.all().delete()
DomainInformation.objects.all().delete()
DomainRequest.objects.all().delete()
UserPortfolioPermission.objects.all().delete()
Portfolio.objects.all().delete()
User.objects.all().delete()
UserDomainRole.objects.all().delete()
@less_console_noise_decorator
@override_flag("multiple_portfolios", active=True)
def test_clean_on_multiple_portfolios_when_flag_active(self):
"""Ensures that a user can create multiple portfolio permission objects when the flag is enabled"""
# Create an instance of User with a portfolio but no roles or additional permissions
portfolio, _ = Portfolio.objects.get_or_create(creator=self.user, organization_name="Hotel California")
portfolio_2, _ = Portfolio.objects.get_or_create(creator=self.user, organization_name="Motel California")
portfolio_permission, _ = UserPortfolioPermission.objects.get_or_create(
portfolio=portfolio, user=self.user, roles=[UserPortfolioRoleChoices.ORGANIZATION_ADMIN]
)
portfolio_permission_2 = UserPortfolioPermission(
portfolio=portfolio_2, user=self.user, roles=[UserPortfolioRoleChoices.ORGANIZATION_ADMIN]
)
# Clean should pass on both of these objects
try:
portfolio_permission.clean()
portfolio_permission_2.clean()
except ValidationError as error:
self.fail(f"Raised ValidationError unexpectedly: {error}")
@less_console_noise_decorator
@override_flag("multiple_portfolios", active=False)
def test_clean_on_creates_multiple_portfolios(self):
"""Ensures that a user cannot create multiple portfolio permission objects when the flag is disabled"""
# Create an instance of User with a single portfolio
portfolio, _ = Portfolio.objects.get_or_create(creator=self.user, organization_name="Hotel California")
portfolio_permission, _ = UserPortfolioPermission.objects.get_or_create(
portfolio=portfolio, user=self.user, roles=[UserPortfolioRoleChoices.ORGANIZATION_ADMIN]
)
portfolio_2, _ = Portfolio.objects.get_or_create(creator=self.user, organization_name="Motel California")
portfolio_permission_2 = UserPortfolioPermission(
portfolio=portfolio_2, user=self.user, roles=[UserPortfolioRoleChoices.ORGANIZATION_ADMIN]
)
# This should work as intended
portfolio_permission.clean()
# Test if the ValidationError is raised with the correct message
with self.assertRaises(ValidationError) as cm:
portfolio_permission_2.clean()
self.assertEqual(
cm.exception.message,
(
"This user is already assigned to a portfolio. "
"Based on current waffle flag settings, users cannot be assigned to multiple portfolios."
),
)
@less_console_noise_decorator
@override_flag("multiple_portfolios", active=False)
def test_multiple_portfolio_reassignment(self):
"""Ensures that a user cannot be assigned to multiple portfolios based on reassignment"""
# Create an instance of two users with separate portfolios
portfolio, _ = Portfolio.objects.get_or_create(creator=self.user, organization_name="Hotel California")
portfolio_permission, _ = UserPortfolioPermission.objects.get_or_create(
portfolio=portfolio, user=self.user, roles=[UserPortfolioRoleChoices.ORGANIZATION_ADMIN]
)
portfolio_2, _ = Portfolio.objects.get_or_create(creator=self.user2, organization_name="Motel California")
portfolio_permission_2 = UserPortfolioPermission(
portfolio=portfolio_2, user=self.user2, roles=[UserPortfolioRoleChoices.ORGANIZATION_ADMIN]
)
# This should work as intended
portfolio_permission.clean()
portfolio_permission_2.clean()
# Reassign the portfolio of "user2" to "user" (this should throw an error
# preventing "user" from having multiple portfolios)
with self.assertRaises(ValidationError) as cm:
portfolio_permission_2.user = self.user
portfolio_permission_2.clean()
self.assertEqual(
cm.exception.message,
(
"This user is already assigned to a portfolio. "
"Based on current waffle flag settings, users cannot be assigned to multiple portfolios."
),
)
@less_console_noise_decorator
def test_get_managed_domains_count(self):
"""Test that the correct number of managed domains associated with the portfolio
are returned."""
# Add three domains, one which is in the portfolio and managed by the user,
# one which is in the portfolio and not managed by the user,
# and one which is managed by the user and not in the portfolio.
# Arrange
portfolio, _ = Portfolio.objects.get_or_create(creator=self.user, organization_name="Hotel California")
test_user = create_test_user()
portfolio_permission, _ = UserPortfolioPermission.objects.get_or_create(
portfolio=portfolio, user=test_user, roles=[UserPortfolioRoleChoices.ORGANIZATION_ADMIN]
)
# domain_in_portfolio should not be included in the count
domain_in_portfolio, _ = Domain.objects.get_or_create(name="domain_in_portfolio.gov", state=Domain.State.READY)
DomainInformation.objects.get_or_create(creator=self.user, domain=domain_in_portfolio, portfolio=portfolio)
# domain_in_portfolio_and_managed should be included in the count
domain_in_portfolio_and_managed, _ = Domain.objects.get_or_create(
name="domain_in_portfolio_and_managed.gov", state=Domain.State.READY
)
DomainInformation.objects.get_or_create(
creator=self.user, domain=domain_in_portfolio_and_managed, portfolio=portfolio
)
UserDomainRole.objects.get_or_create(
user=test_user, domain=domain_in_portfolio_and_managed, role=UserDomainRole.Roles.MANAGER
)
# domain_managed should not be included in the count
domain_managed, _ = Domain.objects.get_or_create(name="domain_managed.gov", state=Domain.State.READY)
DomainInformation.objects.get_or_create(creator=self.user, domain=domain_managed)
UserDomainRole.objects.get_or_create(user=test_user, domain=domain_managed, role=UserDomainRole.Roles.MANAGER)
# Assert
self.assertEqual(portfolio_permission.get_managed_domains_count(), 1)
class TestUser(TestCase):
"""Test actions that occur on user login,
test class method that controls how users get validated."""
@less_console_noise_decorator
def setUp(self):
self.email = "mayor@igorville.gov"
self.domain_name = "igorvilleInTransition.gov"
self.domain, _ = Domain.objects.get_or_create(name="igorville.gov")
self.user, _ = User.objects.get_or_create(email=self.email)
self.factory = RequestFactory()
self.portfolio = Portfolio.objects.create(organization_name="Test Portfolio", creator=self.user)
def tearDown(self):
super().tearDown()
Domain.objects.all().delete()
DomainInvitation.objects.all().delete()
DomainInformation.objects.all().delete()
DomainRequest.objects.all().delete()
DraftDomain.objects.all().delete()
TransitionDomain.objects.all().delete()
UserPortfolioPermission.objects.all().delete()
Portfolio.objects.all().delete()
User.objects.all().delete()
UserDomainRole.objects.all().delete()
@patch.object(User, "has_edit_suborganization_portfolio_permission", return_value=True)
def test_portfolio_role_summary_admin(self, mock_edit_suborganization):
# Test if the user is recognized as an Admin
self.assertEqual(self.user.portfolio_role_summary(self.portfolio), ["Admin"])
@patch.multiple(
User,
has_view_all_domains_portfolio_permission=lambda self, portfolio: True,
has_any_requests_portfolio_permission=lambda self, portfolio: True,
has_edit_request_portfolio_permission=lambda self, portfolio: True,
)
def test_portfolio_role_summary_view_only_admin_and_domain_requestor(self):
# Test if the user has both 'View-only admin' and 'Domain requestor' roles
self.assertEqual(self.user.portfolio_role_summary(self.portfolio), ["View-only admin", "Domain requestor"])
@patch.multiple(
User,
has_view_all_domains_portfolio_permission=lambda self, portfolio: True,
has_any_requests_portfolio_permission=lambda self, portfolio: True,
)
def test_portfolio_role_summary_view_only_admin(self):
# Test if the user is recognized as a View-only admin
self.assertEqual(self.user.portfolio_role_summary(self.portfolio), ["View-only admin"])
@patch.multiple(
User,
has_base_portfolio_permission=lambda self, portfolio: True,
has_edit_request_portfolio_permission=lambda self, portfolio: True,
has_any_domains_portfolio_permission=lambda self, portfolio: True,
)
def test_portfolio_role_summary_member_domain_requestor_domain_manager(self):
# Test if the user has 'Member', 'Domain requestor', and 'Domain manager' roles
self.assertEqual(self.user.portfolio_role_summary(self.portfolio), ["Domain requestor", "Domain manager"])
@patch.multiple(
User,
has_base_portfolio_permission=lambda self, portfolio: True,
has_edit_request_portfolio_permission=lambda self, portfolio: True,
)
def test_portfolio_role_summary_member_domain_requestor(self):
# Test if the user has 'Member' and 'Domain requestor' roles
self.assertEqual(self.user.portfolio_role_summary(self.portfolio), ["Domain requestor"])
@patch.multiple(
User,
has_base_portfolio_permission=lambda self, portfolio: True,
has_any_domains_portfolio_permission=lambda self, portfolio: True,
)
def test_portfolio_role_summary_member_domain_manager(self):
# Test if the user has 'Member' and 'Domain manager' roles
self.assertEqual(self.user.portfolio_role_summary(self.portfolio), ["Domain manager"])
@patch.multiple(User, has_base_portfolio_permission=lambda self, portfolio: True)
def test_portfolio_role_summary_member(self):
# Test if the user is recognized as a Member
self.assertEqual(self.user.portfolio_role_summary(self.portfolio), ["Member"])
def test_portfolio_role_summary_empty(self):
# Test if the user has no roles
self.assertEqual(self.user.portfolio_role_summary(self.portfolio), [])
@patch("registrar.models.User._has_portfolio_permission")
def test_has_base_portfolio_permission(self, mock_has_permission):
mock_has_permission.return_value = True
self.assertTrue(self.user.has_base_portfolio_permission(self.portfolio))
mock_has_permission.assert_called_once_with(self.portfolio, UserPortfolioPermissionChoices.VIEW_PORTFOLIO)
@patch("registrar.models.User._has_portfolio_permission")
def test_has_edit_org_portfolio_permission(self, mock_has_permission):
mock_has_permission.return_value = True
self.assertTrue(self.user.has_edit_org_portfolio_permission(self.portfolio))
mock_has_permission.assert_called_once_with(self.portfolio, UserPortfolioPermissionChoices.EDIT_PORTFOLIO)
@patch("registrar.models.User._has_portfolio_permission")
def test_has_any_domains_portfolio_permission(self, mock_has_permission):
mock_has_permission.side_effect = [False, True] # First permission false, second permission true
self.assertTrue(self.user.has_any_domains_portfolio_permission(self.portfolio))
self.assertEqual(mock_has_permission.call_count, 2)
mock_has_permission.assert_any_call(self.portfolio, UserPortfolioPermissionChoices.VIEW_ALL_DOMAINS)
mock_has_permission.assert_any_call(self.portfolio, UserPortfolioPermissionChoices.VIEW_MANAGED_DOMAINS)
@patch("registrar.models.User._has_portfolio_permission")
def test_has_view_all_domains_portfolio_permission(self, mock_has_permission):
mock_has_permission.return_value = True
self.assertTrue(self.user.has_view_all_domains_portfolio_permission(self.portfolio))
mock_has_permission.assert_called_once_with(self.portfolio, UserPortfolioPermissionChoices.VIEW_ALL_DOMAINS)
@patch("registrar.models.User._has_portfolio_permission")
@override_flag("organization_requests", active=True)
def test_has_any_requests_portfolio_permission(self, mock_has_permission):
mock_has_permission.side_effect = [False, True] # First permission false, second permission true
self.assertTrue(self.user.has_any_requests_portfolio_permission(self.portfolio))
self.assertEqual(mock_has_permission.call_count, 2)
mock_has_permission.assert_any_call(self.portfolio, UserPortfolioPermissionChoices.VIEW_ALL_REQUESTS)
mock_has_permission.assert_any_call(self.portfolio, UserPortfolioPermissionChoices.EDIT_REQUESTS)
@patch("registrar.models.User._has_portfolio_permission")
def test_has_view_all_requests_portfolio_permission(self, mock_has_permission):
mock_has_permission.return_value = True
self.assertTrue(self.user.has_view_all_requests_portfolio_permission(self.portfolio))
mock_has_permission.assert_called_once_with(self.portfolio, UserPortfolioPermissionChoices.VIEW_ALL_REQUESTS)
@patch("registrar.models.User._has_portfolio_permission")
def test_has_edit_request_portfolio_permission(self, mock_has_permission):
mock_has_permission.return_value = True
self.assertTrue(self.user.has_edit_request_portfolio_permission(self.portfolio))
mock_has_permission.assert_called_once_with(self.portfolio, UserPortfolioPermissionChoices.EDIT_REQUESTS)
@patch("registrar.models.User._has_portfolio_permission")
def test_has_view_suborganization_portfolio_permission(self, mock_has_permission):
mock_has_permission.return_value = True
self.assertTrue(self.user.has_view_suborganization_portfolio_permission(self.portfolio))
mock_has_permission.assert_called_once_with(self.portfolio, UserPortfolioPermissionChoices.VIEW_SUBORGANIZATION)
@patch("registrar.models.User._has_portfolio_permission")
def test_has_edit_suborganization_portfolio_permission(self, mock_has_permission):
mock_has_permission.return_value = True
self.assertTrue(self.user.has_edit_suborganization_portfolio_permission(self.portfolio))
mock_has_permission.assert_called_once_with(self.portfolio, UserPortfolioPermissionChoices.EDIT_SUBORGANIZATION)
@less_console_noise_decorator
def test_check_transition_domains_without_domains_on_login(self):
"""A user's on_each_login callback does not check transition domains.
This test makes sure that in the event a domain does not exist
for a given transition domain, both a domain and domain invitation
are created."""
self.user.on_each_login()
self.assertFalse(Domain.objects.filter(name=self.domain_name).exists())
@less_console_noise_decorator
def test_identity_verification_with_domain_manager(self):
"""A domain manager should return False when tested with class
method needs_identity_verification"""
UserDomainRole.objects.get_or_create(user=self.user, domain=self.domain, role=UserDomainRole.Roles.MANAGER)
self.assertFalse(User.needs_identity_verification(self.user.email, self.user.username))
@less_console_noise_decorator
def test_identity_verification_with_transition_user(self):
"""A user from the Verisign transition should return False
when tested with class method needs_identity_verification"""
TransitionDomain.objects.get_or_create(username=self.user.email, domain_name=self.domain_name)
self.assertFalse(User.needs_identity_verification(self.user.email, self.user.username))
@less_console_noise_decorator
def test_identity_verification_with_very_important_person(self):
"""A Very Important Person should return False
when tested with class method needs_identity_verification"""
VerifiedByStaff.objects.get_or_create(email=self.user.email)
self.assertFalse(User.needs_identity_verification(self.user.email, self.user.username))
@less_console_noise_decorator
def test_identity_verification_with_invited_user(self):
"""An invited user should return False when tested with class
method needs_identity_verification"""
DomainInvitation.objects.get_or_create(email=self.user.email, domain=self.domain)
self.assertFalse(User.needs_identity_verification(self.user.email, self.user.username))
@less_console_noise_decorator
def test_identity_verification_with_new_user(self):
"""A new user who's neither transitioned nor invited should
return True when tested with class method needs_identity_verification"""
self.assertTrue(User.needs_identity_verification(self.user.email, self.user.username))
@less_console_noise_decorator
def test_check_domain_invitations_on_login_caps_email(self):
"""A DomainInvitation with an email address with capital letters should match
a User record whose email address is not in caps"""
# create DomainInvitation with CAPS email that matches User email
# on a case-insensitive match
caps_email = "MAYOR@igorville.gov"
# mock the domain invitation save routine
with patch("registrar.models.DomainInvitation.save") as save_mock:
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
# Domain Invitation, then save routine should be called exactly once
save_mock.assert_called_once()
@less_console_noise_decorator
def test_approved_domains_count(self):
"""Test that the correct approved domain count is returned for a user"""
# with no associated approved domains, expect this to return 0
self.assertEquals(self.user.get_approved_domains_count(), 0)
# with one approved domain, expect this to return 1
UserDomainRole.objects.get_or_create(user=self.user, domain=self.domain, role=UserDomainRole.Roles.MANAGER)
self.assertEquals(self.user.get_approved_domains_count(), 1)
# with one approved domain, expect this to return 1 (domain2 is deleted, so not considered approved)
domain2, _ = Domain.objects.get_or_create(name="igorville2.gov", state=Domain.State.DELETED)
UserDomainRole.objects.get_or_create(user=self.user, domain=domain2, role=UserDomainRole.Roles.MANAGER)
self.assertEquals(self.user.get_approved_domains_count(), 1)
# with two approved domains, expect this to return 2
domain3, _ = Domain.objects.get_or_create(name="igorville3.gov", state=Domain.State.DNS_NEEDED)
UserDomainRole.objects.get_or_create(user=self.user, domain=domain3, role=UserDomainRole.Roles.MANAGER)
self.assertEquals(self.user.get_approved_domains_count(), 2)
# with three approved domains, expect this to return 3
domain4, _ = Domain.objects.get_or_create(name="igorville4.gov", state=Domain.State.ON_HOLD)
UserDomainRole.objects.get_or_create(user=self.user, domain=domain4, role=UserDomainRole.Roles.MANAGER)
self.assertEquals(self.user.get_approved_domains_count(), 3)
# with four approved domains, expect this to return 4
domain5, _ = Domain.objects.get_or_create(name="igorville5.gov", state=Domain.State.READY)
UserDomainRole.objects.get_or_create(user=self.user, domain=domain5, role=UserDomainRole.Roles.MANAGER)
self.assertEquals(self.user.get_approved_domains_count(), 4)
@less_console_noise_decorator
def test_active_requests_count(self):
"""Test that the correct active domain requests count is returned for a user"""
# with no associated active requests, expect this to return 0
self.assertEquals(self.user.get_active_requests_count(), 0)
# with one active request, expect this to return 1
draft_domain, _ = DraftDomain.objects.get_or_create(name="igorville1.gov")
DomainRequest.objects.create(
creator=self.user, requested_domain=draft_domain, status=DomainRequest.DomainRequestStatus.SUBMITTED
)
self.assertEquals(self.user.get_active_requests_count(), 1)
# with two active requests, expect this to return 2
draft_domain, _ = DraftDomain.objects.get_or_create(name="igorville2.gov")
DomainRequest.objects.create(
creator=self.user, requested_domain=draft_domain, status=DomainRequest.DomainRequestStatus.IN_REVIEW
)
self.assertEquals(self.user.get_active_requests_count(), 2)
# with three active requests, expect this to return 3
draft_domain, _ = DraftDomain.objects.get_or_create(name="igorville3.gov")
DomainRequest.objects.create(
creator=self.user, requested_domain=draft_domain, status=DomainRequest.DomainRequestStatus.ACTION_NEEDED
)
self.assertEquals(self.user.get_active_requests_count(), 3)
# with three active requests, expect this to return 3 (STARTED is not considered active)
draft_domain, _ = DraftDomain.objects.get_or_create(name="igorville4.gov")
DomainRequest.objects.create(
creator=self.user, requested_domain=draft_domain, status=DomainRequest.DomainRequestStatus.STARTED
)
self.assertEquals(self.user.get_active_requests_count(), 3)
@less_console_noise_decorator
def test_rejected_requests_count(self):
"""Test that the correct rejected domain requests count is returned for a user"""
# with no associated rejected requests, expect this to return 0
self.assertEquals(self.user.get_rejected_requests_count(), 0)
# with one rejected request, expect this to return 1
draft_domain, _ = DraftDomain.objects.get_or_create(name="igorville1.gov")
DomainRequest.objects.create(
creator=self.user, requested_domain=draft_domain, status=DomainRequest.DomainRequestStatus.REJECTED
)
self.assertEquals(self.user.get_rejected_requests_count(), 1)
@less_console_noise_decorator
def test_ineligible_requests_count(self):
"""Test that the correct ineligible domain requests count is returned for a user"""
# with no associated ineligible requests, expect this to return 0
self.assertEquals(self.user.get_ineligible_requests_count(), 0)
# with one ineligible request, expect this to return 1
draft_domain, _ = DraftDomain.objects.get_or_create(name="igorville1.gov")
DomainRequest.objects.create(
creator=self.user, requested_domain=draft_domain, status=DomainRequest.DomainRequestStatus.INELIGIBLE
)
self.assertEquals(self.user.get_ineligible_requests_count(), 1)
@less_console_noise_decorator
def test_has_contact_info(self):
"""Test that has_contact_info properly returns"""
# test with a user with contact info defined
self.assertTrue(self.user.has_contact_info())
# test with a user without contact info defined
self.user.title = None
self.user.email = None
self.user.phone = None
self.assertFalse(self.user.has_contact_info())
@less_console_noise_decorator
@override_flag("organization_requests", active=True)
def test_has_portfolio_permission(self):
"""
0. Returns False when user does not have a permission
1. Returns False when a user does not have a portfolio
2. Returns True when user has direct permission
3. Returns True when user has permission through a role
Note: This tests _get_portfolio_permissions as a side effect
"""
portfolio, _ = Portfolio.objects.get_or_create(creator=self.user, organization_name="Hotel California")
user_can_view_all_domains = self.user.has_any_domains_portfolio_permission(portfolio)
user_can_view_all_requests = self.user.has_any_requests_portfolio_permission(portfolio)
self.assertFalse(user_can_view_all_domains)
self.assertFalse(user_can_view_all_requests)
portfolio_permission, _ = UserPortfolioPermission.objects.get_or_create(
portfolio=portfolio,
user=self.user,
additional_permissions=[
UserPortfolioPermissionChoices.VIEW_PORTFOLIO,
UserPortfolioPermissionChoices.VIEW_ALL_DOMAINS,
],
)
user_can_view_all_domains = self.user.has_any_domains_portfolio_permission(portfolio)
user_can_view_all_requests = self.user.has_any_requests_portfolio_permission(portfolio)
self.assertTrue(user_can_view_all_domains)
self.assertFalse(user_can_view_all_requests)
portfolio_permission.roles = [UserPortfolioRoleChoices.ORGANIZATION_ADMIN]
portfolio_permission.save()
portfolio_permission.refresh_from_db()
user_can_view_all_domains = self.user.has_any_domains_portfolio_permission(portfolio)
user_can_view_all_requests = self.user.has_any_requests_portfolio_permission(portfolio)
self.assertTrue(user_can_view_all_domains)
self.assertTrue(user_can_view_all_requests)
UserDomainRole.objects.get_or_create(user=self.user, domain=self.domain, role=UserDomainRole.Roles.MANAGER)
user_can_view_all_domains = self.user.has_any_domains_portfolio_permission(portfolio)
user_can_view_all_requests = self.user.has_any_requests_portfolio_permission(portfolio)
self.assertTrue(user_can_view_all_domains)
self.assertTrue(user_can_view_all_requests)
Portfolio.objects.all().delete()
@less_console_noise_decorator
def test_user_with_portfolio_but_no_roles(self):
# Create an instance of User with a portfolio but no roles or additional permissions
portfolio, _ = Portfolio.objects.get_or_create(creator=self.user, organization_name="Hotel California")
portfolio_permission, _ = UserPortfolioPermission.objects.get_or_create(portfolio=portfolio, user=self.user)
# Try to remove the role
portfolio_permission.portfolio = portfolio
portfolio_permission.roles = []
# Test if the ValidationError is raised with the correct message
with self.assertRaises(ValidationError) as cm:
portfolio_permission.clean()
self.assertEqual(
cm.exception.message, "When portfolio is assigned, portfolio roles or additional permissions are required."
)
Portfolio.objects.all().delete()
@less_console_noise_decorator
def test_user_with_portfolio_roles_but_no_portfolio(self):
portfolio, _ = Portfolio.objects.get_or_create(creator=self.user, organization_name="Hotel California")
portfolio_permission, _ = UserPortfolioPermission.objects.get_or_create(
portfolio=portfolio, user=self.user, roles=[UserPortfolioRoleChoices.ORGANIZATION_ADMIN]
)
# Try to remove the portfolio
portfolio_permission.portfolio = None
portfolio_permission.roles = [UserPortfolioRoleChoices.ORGANIZATION_ADMIN]
# Test if the ValidationError is raised with the correct message
with self.assertRaises(ValidationError) as cm:
portfolio_permission.clean()
self.assertEqual(
cm.exception.message, "When portfolio roles or additional permissions are assigned, portfolio is required."
)
@less_console_noise_decorator
def test_user_with_admin_portfolio_role(self):
portfolio, _ = Portfolio.objects.get_or_create(creator=self.user, organization_name="Hotel California")
self.assertFalse(self.user.is_portfolio_admin(portfolio))
UserPortfolioPermission.objects.get_or_create(
portfolio=portfolio, user=self.user, roles=[UserPortfolioRoleChoices.ORGANIZATION_ADMIN]
)
self.assertTrue(self.user.is_portfolio_admin(portfolio))
class TestContact(TestCase):
@less_console_noise_decorator
def setUp(self):
self.email = "mayor@igorville.gov"
self.user, _ = User.objects.get_or_create(
email=self.email, first_name="Jeff", last_name="Lebowski", phone="123456789"
)
self.contact, _ = Contact.objects.get_or_create(
first_name="Jeff",
last_name="Lebowski",
)
self.contact_as_so, _ = Contact.objects.get_or_create(email="newguy@igorville.gov")
self.domain_request = DomainRequest.objects.create(creator=self.user, senior_official=self.contact_as_so)
def tearDown(self):
super().tearDown()
DomainRequest.objects.all().delete()
Contact.objects.all().delete()
User.objects.all().delete()
def test_has_more_than_one_join(self):
"""Test the Contact model method, has_more_than_one_join"""
# test for a contact which is assigned as a senior official on a domain request
self.assertFalse(self.contact_as_so.has_more_than_one_join("senior_official"))
self.assertTrue(self.contact_as_so.has_more_than_one_join("submitted_domain_requests"))
@less_console_noise_decorator
def test_has_contact_info(self):
"""Test that has_contact_info properly returns"""
self.contact.title = "Title"
# test with a contact with contact info defined
self.assertTrue(self.contact.has_contact_info())
# test with a contact without contact info defined
self.contact.title = None
self.contact.email = None
self.contact.phone = None
self.assertFalse(self.contact.has_contact_info())
class TestDomainRequestCustomSave(TestCase):
"""Tests custom save behaviour on the DomainRequest object"""
def tearDown(self):
DomainRequest.objects.all().delete()
super().tearDown()
@less_console_noise_decorator
def test_create_or_update_organization_type_new_instance(self):
"""Test create_or_update_organization_type when creating a new instance"""
domain_request = completed_domain_request(
status=DomainRequest.DomainRequestStatus.STARTED,
name="started.gov",
generic_org_type=DomainRequest.OrganizationChoices.CITY,
is_election_board=True,
)
self.assertEqual(domain_request.organization_type, DomainRequest.OrgChoicesElectionOffice.CITY_ELECTION)
@less_console_noise_decorator
def test_create_or_update_organization_type_new_instance_federal_does_nothing(self):
"""Test if create_or_update_organization_type does nothing when creating a new instance for federal"""
domain_request = completed_domain_request(
status=DomainRequest.DomainRequestStatus.STARTED,
name="started.gov",
generic_org_type=DomainRequest.OrganizationChoices.FEDERAL,
is_election_board=True,
)
self.assertEqual(domain_request.organization_type, DomainRequest.OrgChoicesElectionOffice.FEDERAL)
self.assertEqual(domain_request.is_election_board, None)
@less_console_noise_decorator
def test_create_or_update_organization_type_existing_instance_updates_election_board(self):
"""Test create_or_update_organization_type for an existing instance."""
domain_request = completed_domain_request(
status=DomainRequest.DomainRequestStatus.STARTED,
name="started.gov",
generic_org_type=DomainRequest.OrganizationChoices.CITY,
is_election_board=False,
)
domain_request.is_election_board = True
domain_request.save()
self.assertEqual(domain_request.is_election_board, True)
self.assertEqual(domain_request.organization_type, DomainRequest.OrgChoicesElectionOffice.CITY_ELECTION)
# Try reverting the election board value
domain_request.is_election_board = False
domain_request.save()
self.assertEqual(domain_request.is_election_board, False)
self.assertEqual(domain_request.organization_type, DomainRequest.OrgChoicesElectionOffice.CITY)
@less_console_noise_decorator
def test_existing_instance_updates_election_board_to_none(self):
"""Test create_or_update_organization_type for an existing instance, first to True and then to None.
Start our with is_election_board as none to simulate a situation where the request was started, but
only completed to the point of filling out the generic_org_type."""
domain_request = completed_domain_request(
status=DomainRequest.DomainRequestStatus.STARTED,
name="started.gov",
generic_org_type=DomainRequest.OrganizationChoices.CITY,
is_election_board=None,
)
domain_request.is_election_board = True
domain_request.save()
self.assertEqual(domain_request.is_election_board, True)
self.assertEqual(domain_request.organization_type, DomainRequest.OrgChoicesElectionOffice.CITY_ELECTION)
# Try reverting the election board value.
domain_request.is_election_board = None
domain_request.save()
self.assertEqual(domain_request.is_election_board, None)
self.assertEqual(domain_request.organization_type, DomainRequest.OrgChoicesElectionOffice.CITY)
@less_console_noise_decorator
def test_create_or_update_organization_type_existing_instance_updates_generic_org_type(self):
"""Test create_or_update_organization_type when modifying generic_org_type on an existing instance."""
domain_request = completed_domain_request(
status=DomainRequest.DomainRequestStatus.STARTED,
name="started.gov",
generic_org_type=DomainRequest.OrganizationChoices.CITY,
is_election_board=True,
)
domain_request.generic_org_type = DomainRequest.OrganizationChoices.INTERSTATE
domain_request.save()
# Election board should be None because interstate cannot have an election board.
self.assertEqual(domain_request.is_election_board, None)
self.assertEqual(domain_request.organization_type, DomainRequest.OrgChoicesElectionOffice.INTERSTATE)
# Try changing the org Type to something that CAN have an election board.
domain_request_tribal = completed_domain_request(
status=DomainRequest.DomainRequestStatus.STARTED,
name="startedTribal.gov",
generic_org_type=DomainRequest.OrganizationChoices.TRIBAL,
is_election_board=True,
)
self.assertEqual(
domain_request_tribal.organization_type, DomainRequest.OrgChoicesElectionOffice.TRIBAL_ELECTION
)
# Change the org type
domain_request_tribal.generic_org_type = DomainRequest.OrganizationChoices.STATE_OR_TERRITORY
domain_request_tribal.save()
self.assertEqual(domain_request_tribal.is_election_board, True)
self.assertEqual(
domain_request_tribal.organization_type, DomainRequest.OrgChoicesElectionOffice.STATE_OR_TERRITORY_ELECTION
)
@less_console_noise_decorator
def test_create_or_update_organization_type_no_update(self):
"""Test create_or_update_organization_type when there are no values to update."""
# Test for when both generic_org_type and organization_type is declared,
# and are both non-election board
domain_request = completed_domain_request(
status=DomainRequest.DomainRequestStatus.STARTED,
name="started.gov",
generic_org_type=DomainRequest.OrganizationChoices.CITY,
is_election_board=False,
)
domain_request.save()
self.assertEqual(domain_request.organization_type, DomainRequest.OrgChoicesElectionOffice.CITY)
self.assertEqual(domain_request.is_election_board, False)
self.assertEqual(domain_request.generic_org_type, DomainRequest.OrganizationChoices.CITY)
# Test for when both generic_org_type and organization_type is declared,
# and are both election board
domain_request_election = completed_domain_request(
status=DomainRequest.DomainRequestStatus.STARTED,
name="startedElection.gov",
generic_org_type=DomainRequest.OrganizationChoices.CITY,
is_election_board=True,
organization_type=DomainRequest.OrgChoicesElectionOffice.CITY_ELECTION,
)
self.assertEqual(
domain_request_election.organization_type, DomainRequest.OrgChoicesElectionOffice.CITY_ELECTION
)
self.assertEqual(domain_request_election.is_election_board, True)
self.assertEqual(domain_request_election.generic_org_type, DomainRequest.OrganizationChoices.CITY)
# Modify an unrelated existing value for both, and ensure that everything is still consistent
domain_request.city = "Fudge"
domain_request_election.city = "Caramel"
domain_request.save()
domain_request_election.save()
self.assertEqual(domain_request.city, "Fudge")
self.assertEqual(domain_request_election.city, "Caramel")
# Test for non-election
self.assertEqual(domain_request.organization_type, DomainRequest.OrgChoicesElectionOffice.CITY)
self.assertEqual(domain_request.is_election_board, False)
self.assertEqual(domain_request.generic_org_type, DomainRequest.OrganizationChoices.CITY)
# Test for election
self.assertEqual(
domain_request_election.organization_type, DomainRequest.OrgChoicesElectionOffice.CITY_ELECTION
)
self.assertEqual(domain_request_election.is_election_board, True)
self.assertEqual(domain_request_election.generic_org_type, DomainRequest.OrganizationChoices.CITY)
class TestDomainInformationCustomSave(TestCase):
"""Tests custom save behaviour on the DomainInformation object"""
def tearDown(self):
DomainInformation.objects.all().delete()
DomainRequest.objects.all().delete()
Domain.objects.all().delete()
super().tearDown()
@less_console_noise_decorator
def test_create_or_update_organization_type_new_instance(self):
"""Test create_or_update_organization_type when creating a new instance"""
domain_request = completed_domain_request(
status=DomainRequest.DomainRequestStatus.STARTED,
name="started.gov",
generic_org_type=DomainRequest.OrganizationChoices.CITY,
is_election_board=True,
)
domain_information = DomainInformation.create_from_da(domain_request)
self.assertEqual(domain_information.organization_type, DomainRequest.OrgChoicesElectionOffice.CITY_ELECTION)
@less_console_noise_decorator
def test_create_or_update_organization_type_new_instance_federal_does_nothing(self):
"""Test if create_or_update_organization_type does nothing when creating a new instance for federal"""
domain_request = completed_domain_request(
status=DomainRequest.DomainRequestStatus.STARTED,
name="started.gov",
generic_org_type=DomainRequest.OrganizationChoices.FEDERAL,
is_election_board=True,
)
domain_information = DomainInformation.create_from_da(domain_request)
self.assertEqual(domain_information.organization_type, DomainRequest.OrgChoicesElectionOffice.FEDERAL)
self.assertEqual(domain_information.is_election_board, None)
@less_console_noise_decorator
def test_create_or_update_organization_type_existing_instance_updates_election_board(self):
"""Test create_or_update_organization_type for an existing instance."""
domain_request = completed_domain_request(
status=DomainRequest.DomainRequestStatus.STARTED,
name="started.gov",
generic_org_type=DomainRequest.OrganizationChoices.CITY,
is_election_board=False,
)
domain_information = DomainInformation.create_from_da(domain_request)
domain_information.is_election_board = True
domain_information.save()
self.assertEqual(domain_information.is_election_board, True)
self.assertEqual(domain_information.organization_type, DomainRequest.OrgChoicesElectionOffice.CITY_ELECTION)
# Try reverting the election board value
domain_information.is_election_board = False
domain_information.save()
domain_information.refresh_from_db()
self.assertEqual(domain_information.is_election_board, False)
self.assertEqual(domain_information.organization_type, DomainRequest.OrgChoicesElectionOffice.CITY)
@less_console_noise_decorator
def test_existing_instance_update_election_board_to_none(self):
"""Test create_or_update_organization_type for an existing instance, first to True and then to None.
Start our with is_election_board as none to simulate a situation where the request was started, but
only completed to the point of filling out the generic_org_type."""
domain_request = completed_domain_request(
status=DomainRequest.DomainRequestStatus.STARTED,
name="started.gov",
generic_org_type=DomainRequest.OrganizationChoices.CITY,
is_election_board=None,
)
domain_information = DomainInformation.create_from_da(domain_request)
domain_information.is_election_board = True
domain_information.save()
self.assertEqual(domain_information.is_election_board, True)
self.assertEqual(domain_information.organization_type, DomainRequest.OrgChoicesElectionOffice.CITY_ELECTION)
# Try reverting the election board value
domain_information.is_election_board = None
domain_information.save()
domain_information.refresh_from_db()
self.assertEqual(domain_information.is_election_board, None)
self.assertEqual(domain_information.organization_type, DomainRequest.OrgChoicesElectionOffice.CITY)
@less_console_noise_decorator
def test_create_or_update_organization_type_existing_instance_updates_generic_org_type(self):
"""Test create_or_update_organization_type when modifying generic_org_type on an existing instance."""
domain_request = completed_domain_request(
status=DomainRequest.DomainRequestStatus.STARTED,
name="started.gov",
generic_org_type=DomainRequest.OrganizationChoices.CITY,
is_election_board=True,
)
domain_information = DomainInformation.create_from_da(domain_request)
domain_information.generic_org_type = DomainRequest.OrganizationChoices.INTERSTATE
domain_information.save()
# Election board should be None because interstate cannot have an election board.
self.assertEqual(domain_information.is_election_board, None)
self.assertEqual(domain_information.organization_type, DomainRequest.OrgChoicesElectionOffice.INTERSTATE)
# Try changing the org Type to something that CAN have an election board.
domain_request_tribal = completed_domain_request(
status=DomainRequest.DomainRequestStatus.STARTED,
name="startedTribal.gov",
generic_org_type=DomainRequest.OrganizationChoices.TRIBAL,
is_election_board=True,
)
domain_information_tribal = DomainInformation.create_from_da(domain_request_tribal)
self.assertEqual(
domain_information_tribal.organization_type, DomainRequest.OrgChoicesElectionOffice.TRIBAL_ELECTION
)
# Change the org type
domain_information_tribal.generic_org_type = DomainRequest.OrganizationChoices.STATE_OR_TERRITORY
domain_information_tribal.save()
self.assertEqual(domain_information_tribal.is_election_board, True)
self.assertEqual(
domain_information_tribal.organization_type,
DomainRequest.OrgChoicesElectionOffice.STATE_OR_TERRITORY_ELECTION,
)
@less_console_noise_decorator
def test_create_or_update_organization_type_no_update(self):
"""Test create_or_update_organization_type when there are no values to update."""
# Test for when both generic_org_type and organization_type is declared,
# and are both non-election board
domain_request = completed_domain_request(
status=DomainRequest.DomainRequestStatus.STARTED,
name="started.gov",
generic_org_type=DomainRequest.OrganizationChoices.CITY,
is_election_board=False,
)
domain_information = DomainInformation.create_from_da(domain_request)
domain_information.save()
self.assertEqual(domain_information.organization_type, DomainRequest.OrgChoicesElectionOffice.CITY)
self.assertEqual(domain_information.is_election_board, False)
self.assertEqual(domain_information.generic_org_type, DomainRequest.OrganizationChoices.CITY)
# Test for when both generic_org_type and organization_type is declared,
# and are both election board
domain_request_election = completed_domain_request(
status=DomainRequest.DomainRequestStatus.STARTED,
name="startedElection.gov",
generic_org_type=DomainRequest.OrganizationChoices.CITY,
is_election_board=True,
organization_type=DomainRequest.OrgChoicesElectionOffice.CITY_ELECTION,
)
domain_information_election = DomainInformation.create_from_da(domain_request_election)
self.assertEqual(
domain_information_election.organization_type, DomainRequest.OrgChoicesElectionOffice.CITY_ELECTION
)
self.assertEqual(domain_information_election.is_election_board, True)
self.assertEqual(domain_information_election.generic_org_type, DomainRequest.OrganizationChoices.CITY)
# Modify an unrelated existing value for both, and ensure that everything is still consistent
domain_information.city = "Fudge"
domain_information_election.city = "Caramel"
domain_information.save()
domain_information_election.save()
self.assertEqual(domain_information.city, "Fudge")
self.assertEqual(domain_information_election.city, "Caramel")
# Test for non-election
self.assertEqual(domain_information.organization_type, DomainRequest.OrgChoicesElectionOffice.CITY)
self.assertEqual(domain_information.is_election_board, False)
self.assertEqual(domain_information.generic_org_type, DomainRequest.OrganizationChoices.CITY)
# Test for election
self.assertEqual(
domain_information_election.organization_type, DomainRequest.OrgChoicesElectionOffice.CITY_ELECTION
)
self.assertEqual(domain_information_election.is_election_board, True)
self.assertEqual(domain_information_election.generic_org_type, DomainRequest.OrganizationChoices.CITY)
class TestDomainRequestIncomplete(TestCase):
@classmethod
def setUpClass(cls):
super().setUpClass()
cls.factory = RequestFactory()
cls.user = create_test_user()
@less_console_noise_decorator
def setUp(self):
super().setUp()
so, _ = Contact.objects.get_or_create(
first_name="Meowy",
last_name="Meoward",
title="Chief Cat",
email="meoward@chiefcat.com",
phone="(206) 206 2060",
)
draft_domain, _ = DraftDomain.objects.get_or_create(name="MeowardMeowardMeoward.gov")
you, _ = Contact.objects.get_or_create(
first_name="Testy you",
last_name="Tester you",
title="Admin Tester",
email="testy-admin@town.com",
phone="(555) 555 5556",
)
other, _ = Contact.objects.get_or_create(
first_name="Testy2",
last_name="Tester2",
title="Another Tester",
email="testy2@town.com",
phone="(555) 555 5557",
)
alt, _ = Website.objects.get_or_create(website="MeowardMeowardMeoward1.gov")
current, _ = Website.objects.get_or_create(website="MeowardMeowardMeoward.com")
self.amtrak, _ = FederalAgency.objects.get_or_create(agency="AMTRAK")
self.domain_request = DomainRequest.objects.create(
generic_org_type=DomainRequest.OrganizationChoices.FEDERAL,
federal_type="executive",
federal_agency=FederalAgency.objects.get(agency="AMTRAK"),
about_your_organization="Some description",
is_election_board=True,
tribe_name="Some tribe name",
organization_name="Some organization",
address_line1="address 1",
state_territory="CA",
zipcode="94044",
senior_official=so,
requested_domain=draft_domain,
purpose="Some purpose",
no_other_contacts_rationale=None,
has_cisa_representative=True,
cisa_representative_email="somerep@cisa.com",
has_anything_else_text=True,
anything_else="Anything else",
is_policy_acknowledged=True,
creator=self.user,
)
self.domain_request.other_contacts.add(other)
self.domain_request.current_websites.add(current)
self.domain_request.alternative_domains.add(alt)
def tearDown(self):
super().tearDown()
DomainRequest.objects.all().delete()
Contact.objects.all().delete()
self.amtrak.delete()
@classmethod
def tearDownClass(cls):
super().tearDownClass()
cls.user.delete()
@less_console_noise_decorator
def test_is_federal_complete(self):
self.assertTrue(self.domain_request._is_federal_complete())
self.domain_request.federal_type = None
self.domain_request.save()
self.assertFalse(self.domain_request._is_federal_complete())
@less_console_noise_decorator
def test_is_interstate_complete(self):
self.domain_request.generic_org_type = DomainRequest.OrganizationChoices.INTERSTATE
self.domain_request.about_your_organization = "Something something about your organization"
self.domain_request.save()
self.assertTrue(self.domain_request._is_interstate_complete())
self.domain_request.about_your_organization = None
self.domain_request.save()
self.assertFalse(self.domain_request._is_interstate_complete())
@less_console_noise_decorator
def test_is_state_or_territory_complete(self):
self.domain_request.generic_org_type = DomainRequest.OrganizationChoices.STATE_OR_TERRITORY
self.domain_request.is_election_board = True
self.domain_request.save()
self.assertTrue(self.domain_request._is_state_or_territory_complete())
self.domain_request.is_election_board = None
self.domain_request.save()
self.assertFalse(self.domain_request._is_state_or_territory_complete())
@less_console_noise_decorator
def test_is_tribal_complete(self):
self.domain_request.generic_org_type = DomainRequest.OrganizationChoices.TRIBAL
self.domain_request.tribe_name = "Tribe Name"
self.domain_request.is_election_board = False
self.domain_request.save()
self.assertTrue(self.domain_request._is_tribal_complete())
self.domain_request.is_election_board = None
self.domain_request.save()
self.assertFalse(self.domain_request._is_tribal_complete())
self.domain_request.tribe_name = None
self.domain_request.save()
self.assertFalse(self.domain_request._is_tribal_complete())
@less_console_noise_decorator
def test_is_county_complete(self):
self.domain_request.generic_org_type = DomainRequest.OrganizationChoices.COUNTY
self.domain_request.is_election_board = False
self.domain_request.save()
self.assertTrue(self.domain_request._is_county_complete())
self.domain_request.is_election_board = None
self.domain_request.save()
self.assertFalse(self.domain_request._is_county_complete())
@less_console_noise_decorator
def test_is_city_complete(self):
self.domain_request.generic_org_type = DomainRequest.OrganizationChoices.CITY
self.domain_request.is_election_board = False
self.domain_request.save()
self.assertTrue(self.domain_request._is_city_complete())
self.domain_request.is_election_board = None
self.domain_request.save()
self.assertFalse(self.domain_request._is_city_complete())
@less_console_noise_decorator
def test_is_special_district_complete(self):
self.domain_request.generic_org_type = DomainRequest.OrganizationChoices.SPECIAL_DISTRICT
self.domain_request.about_your_organization = "Something something about your organization"
self.domain_request.is_election_board = False
self.domain_request.save()
self.assertTrue(self.domain_request._is_special_district_complete())
self.domain_request.is_election_board = None
self.domain_request.save()
self.assertFalse(self.domain_request._is_special_district_complete())
self.domain_request.about_your_organization = None
self.domain_request.save()
self.assertFalse(self.domain_request._is_special_district_complete())
@less_console_noise_decorator
def test_is_organization_name_and_address_complete(self):
self.assertTrue(self.domain_request._is_organization_name_and_address_complete())
self.domain_request.organization_name = None
self.domain_request.address_line1 = None
self.domain_request.save()
self.assertTrue(self.domain_request._is_organization_name_and_address_complete())
@less_console_noise_decorator
def test_is_senior_official_complete(self):
self.assertTrue(self.domain_request._is_senior_official_complete())
self.domain_request.senior_official = None
self.domain_request.save()
self.assertFalse(self.domain_request._is_senior_official_complete())
@less_console_noise_decorator
def test_is_requested_domain_complete(self):
self.assertTrue(self.domain_request._is_requested_domain_complete())
self.domain_request.requested_domain = None
self.domain_request.save()
self.assertFalse(self.domain_request._is_requested_domain_complete())
@less_console_noise_decorator
def test_is_purpose_complete(self):
self.assertTrue(self.domain_request._is_purpose_complete())
self.domain_request.purpose = None
self.domain_request.save()
self.assertFalse(self.domain_request._is_purpose_complete())
@less_console_noise_decorator
def test_is_other_contacts_complete_missing_one_field(self):
self.assertTrue(self.domain_request._is_other_contacts_complete())
contact = self.domain_request.other_contacts.first()
contact.first_name = None
contact.save()
self.assertFalse(self.domain_request._is_other_contacts_complete())
@less_console_noise_decorator
def test_is_other_contacts_complete_all_none(self):
self.domain_request.other_contacts.clear()
self.assertFalse(self.domain_request._is_other_contacts_complete())
@less_console_noise_decorator
def test_is_other_contacts_False_and_has_rationale(self):
# Click radio button "No" for no other contacts and give rationale
self.domain_request.other_contacts.clear()
self.domain_request.other_contacts.exists = False
self.domain_request.no_other_contacts_rationale = "Some rationale"
self.assertTrue(self.domain_request._is_other_contacts_complete())
@less_console_noise_decorator
def test_is_other_contacts_False_and_NO_rationale(self):
# Click radio button "No" for no other contacts and DONT give rationale
self.domain_request.other_contacts.clear()
self.domain_request.other_contacts.exists = False
self.domain_request.no_other_contacts_rationale = None
self.assertFalse(self.domain_request._is_other_contacts_complete())
@less_console_noise_decorator
def test_is_additional_details_complete(self):
test_cases = [
# CISA Rep - Yes
# Firstname - Yes
# Lastname - Yes
# Email - Yes
# Anything Else Radio - Yes
# Anything Else Text - Yes
{
"has_cisa_representative": True,
"cisa_representative_first_name": "cisa-first-name",
"cisa_representative_last_name": "cisa-last-name",
"cisa_representative_email": "some@cisarepemail.com",
"has_anything_else_text": True,
"anything_else": "Some text",
"expected": True,
},
# CISA Rep - Yes
# Firstname - Yes
# Lastname - Yes
# Email - Yes
# Anything Else Radio - Yes
# Anything Else Text - None
{
"has_cisa_representative": True,
"cisa_representative_first_name": "cisa-first-name",
"cisa_representative_last_name": "cisa-last-name",
"cisa_representative_email": "some@cisarepemail.com",
"has_anything_else_text": True,
"anything_else": None,
"expected": True,
},
# CISA Rep - Yes
# Firstname - Yes
# Lastname - Yes
# Email - None >> e-mail is optional so it should not change anything setting this to None
# Anything Else Radio - No
# Anything Else Text - No
{
"has_cisa_representative": True,
"cisa_representative_first_name": "cisa-first-name",
"cisa_representative_last_name": "cisa-last-name",
"cisa_representative_email": None,
"has_anything_else_text": False,
"anything_else": None,
"expected": True,
},
# CISA Rep - Yes
# Firstname - Yes
# Lastname - Yes
# Email - None
# Anything Else Radio - None
# Anything Else Text - None
{
"has_cisa_representative": True,
"cisa_representative_first_name": "cisa-first-name",
"cisa_representative_last_name": "cisa-last-name",
"cisa_representative_email": None,
"has_anything_else_text": None,
"anything_else": None,
"expected": False,
},
# CISA Rep - Yes
# Firstname - None
# Lastname - None
# Email - None
# Anything Else Radio - None
# Anything Else Text - None
{
"has_cisa_representative": True,
"cisa_representative_first_name": None,
"cisa_representative_last_name": None,
"cisa_representative_email": None,
"has_anything_else_text": None,
"anything_else": None,
"expected": False,
},
# CISA Rep - Yes
# Firstname - None
# Lastname - None
# Email - None
# Anything Else Radio - No
# Anything Else Text - No
# sync_yes_no will override has_cisa_representative to be False if cisa_representative_first_name is None
# therefore, our expected will be True
{
"has_cisa_representative": True,
# Above will be overridden to False if cisa_representative_first_name is None
"cisa_representative_first_name": None,
"cisa_representative_last_name": None,
"cisa_representative_email": None,
"has_anything_else_text": False,
"anything_else": None,
"expected": True,
},
# CISA Rep - Yes
# Firstname - None
# Lastname - None
# Email - None
# Anything Else Radio - Yes
# Anything Else Text - None
# NOTE: We should never have an instance where only firstname or only lastname are populated
# (they are both required)
{
"has_cisa_representative": True,
# Above will be overridden to False if cisa_representative_first_name is None or
# cisa_representative_last_name is None bc of sync_yes_no_form_fields
"cisa_representative_first_name": None,
"cisa_representative_last_name": None,
"cisa_representative_email": None,
"has_anything_else_text": True,
"anything_else": None,
"expected": True,
},
# CISA Rep - Yes
# Firstname - None
# Lastname - None
# Email - None
# Anything Else Radio - Yes
# Anything Else Text - Yes
{
"has_cisa_representative": True,
# Above will be overridden to False if cisa_representative_first_name is None or
# cisa_representative_last_name is None bc of sync_yes_no_form_fields
"cisa_representative_first_name": None,
"cisa_representative_last_name": None,
"cisa_representative_email": None,
"has_anything_else_text": True,
"anything_else": "Some text",
"expected": True,
},
# CISA Rep - No
# Anything Else Radio - Yes
# Anything Else Text - Yes
{
"has_cisa_representative": False,
"cisa_representative_first_name": None,
"cisa_representative_last_name": None,
"cisa_representative_email": None,
"has_anything_else_text": True,
"anything_else": "Some text",
"expected": True,
},
# CISA Rep - No
# Anything Else Radio - Yes
# Anything Else Text - None
{
"has_cisa_representative": False,
"cisa_representative_first_name": None,
"cisa_representative_last_name": None,
"cisa_representative_email": None,
"has_anything_else_text": True,
"anything_else": None,
"expected": True,
},
# CISA Rep - No
# Anything Else Radio - None
# Anything Else Text - None
{
"has_cisa_representative": False,
"cisa_representative_first_name": None,
"cisa_representative_last_name": None,
"cisa_representative_email": None,
"has_anything_else_text": None,
"anything_else": None,
# Above is both None, so it does NOT get overwritten
"expected": False,
},
# CISA Rep - No
# Anything Else Radio - No
# Anything Else Text - No
{
"has_cisa_representative": False,
"cisa_representative_first_name": None,
"cisa_representative_last_name": None,
"cisa_representative_email": None,
"has_anything_else_text": False,
"anything_else": None,
"expected": True,
},
# CISA Rep - None
# Anything Else Radio - None
{
"has_cisa_representative": None,
"cisa_representative_first_name": None,
"cisa_representative_last_name": None,
"cisa_representative_email": None,
"has_anything_else_text": None,
"anything_else": None,
"expected": False,
},
]
for case in test_cases:
with self.subTest(case=case):
self.domain_request.has_cisa_representative = case["has_cisa_representative"]
self.domain_request.cisa_representative_email = case["cisa_representative_email"]
self.domain_request.has_anything_else_text = case["has_anything_else_text"]
self.domain_request.anything_else = case["anything_else"]
self.domain_request.save()
self.domain_request.refresh_from_db()
self.assertEqual(
self.domain_request._is_additional_details_complete(),
case["expected"],
msg=f"Failed for case: {case}",
)
@less_console_noise_decorator
def test_is_policy_acknowledgement_complete(self):
self.assertTrue(self.domain_request._is_policy_acknowledgement_complete())
self.domain_request.is_policy_acknowledged = False
self.assertTrue(self.domain_request._is_policy_acknowledgement_complete())
self.domain_request.is_policy_acknowledged = None
self.assertFalse(self.domain_request._is_policy_acknowledgement_complete())
@less_console_noise_decorator
def test_form_complete(self):
request = self.factory.get("/")
request.user = self.user
self.assertTrue(self.domain_request._form_complete(request))
self.domain_request.generic_org_type = None
self.domain_request.save()
self.assertFalse(self.domain_request._form_complete(request))
class TestPortfolio(TestCase):
def setUp(self):
self.user, _ = User.objects.get_or_create(
username="intern@igorville.com", email="intern@igorville.com", first_name="Lava", last_name="World"
)
super().setUp()
def tearDown(self):
super().tearDown()
Portfolio.objects.all().delete()
User.objects.all().delete()
def test_urbanization_field_resets_when_not_puetro_rico(self):
"""The urbanization field should only be populated when the state is puetro rico.
Otherwise, this field should be empty."""
# Start out as PR, then change the field
portfolio = Portfolio.objects.create(
creator=self.user,
organization_name="Test Portfolio",
state_territory=DomainRequest.StateTerritoryChoices.PUERTO_RICO,
urbanization="test",
)
self.assertEqual(portfolio.urbanization, "test")
self.assertEqual(portfolio.state_territory, DomainRequest.StateTerritoryChoices.PUERTO_RICO)
portfolio.state_territory = DomainRequest.StateTerritoryChoices.ALABAMA
portfolio.save()
self.assertEqual(portfolio.urbanization, None)
self.assertEqual(portfolio.state_territory, DomainRequest.StateTerritoryChoices.ALABAMA)
def test_can_add_urbanization_field(self):
"""Ensures that you can populate the urbanization field when conditions are right"""
# Create a portfolio that cannot have this field
portfolio = Portfolio.objects.create(
creator=self.user,
organization_name="Test Portfolio",
state_territory=DomainRequest.StateTerritoryChoices.ALABAMA,
urbanization="test",
)
# Implicitly check if this gets cleared on create. It should.
self.assertEqual(portfolio.urbanization, None)
self.assertEqual(portfolio.state_territory, DomainRequest.StateTerritoryChoices.ALABAMA)
portfolio.state_territory = DomainRequest.StateTerritoryChoices.PUERTO_RICO
portfolio.urbanization = "test123"
portfolio.save()
self.assertEqual(portfolio.urbanization, "test123")
self.assertEqual(portfolio.state_territory, DomainRequest.StateTerritoryChoices.PUERTO_RICO)
class TestAllowedEmail(TestCase):
"""Tests our allowed email whitelist"""
@less_console_noise_decorator
def setUp(self):
self.email = "mayor@igorville.gov"
self.email_2 = "cake@igorville.gov"
self.plus_email = "mayor+1@igorville.gov"
self.invalid_plus_email = "1+mayor@igorville.gov"
def tearDown(self):
super().tearDown()
AllowedEmail.objects.all().delete()
def test_email_in_whitelist(self):
"""Test for a normal email defined in the whitelist"""
AllowedEmail.objects.create(email=self.email)
is_allowed = AllowedEmail.is_allowed_email(self.email)
self.assertTrue(is_allowed)
def test_email_not_in_whitelist(self):
"""Test for a normal email NOT defined in the whitelist"""
# Check a email not in the list
is_allowed = AllowedEmail.is_allowed_email(self.email_2)
self.assertFalse(AllowedEmail.objects.filter(email=self.email_2).exists())
self.assertFalse(is_allowed)
def test_plus_email_in_whitelist(self):
"""Test for a +1 email defined in the whitelist"""
AllowedEmail.objects.create(email=self.plus_email)
plus_email_allowed = AllowedEmail.is_allowed_email(self.plus_email)
self.assertTrue(plus_email_allowed)
def test_plus_email_not_in_whitelist(self):
"""Test for a +1 email not defined in the whitelist"""
# This email should not be allowed.
# Checks that we do more than just a regex check on the record.
plus_email_allowed = AllowedEmail.is_allowed_email(self.plus_email)
self.assertFalse(plus_email_allowed)
def test_plus_email_not_in_whitelist_but_base_email_is(self):
"""
Test for a +1 email NOT defined in the whitelist, but the normal one is defined.
Example:
normal (in whitelist) - joe@igorville.com
+1 email (not in whitelist) - joe+1@igorville.com
"""
AllowedEmail.objects.create(email=self.email)
base_email_allowed = AllowedEmail.is_allowed_email(self.email)
self.assertTrue(base_email_allowed)
# The plus email should also be allowed
plus_email_allowed = AllowedEmail.is_allowed_email(self.plus_email)
self.assertTrue(plus_email_allowed)
# This email shouldn't exist in the DB
self.assertFalse(AllowedEmail.objects.filter(email=self.plus_email).exists())
def test_plus_email_in_whitelist_but_base_email_is_not(self):
"""
Test for a +1 email defined in the whitelist, but the normal is NOT defined.
Example:
normal (not in whitelist) - joe@igorville.com
+1 email (in whitelist) - joe+1@igorville.com
"""
AllowedEmail.objects.create(email=self.plus_email)
plus_email_allowed = AllowedEmail.is_allowed_email(self.plus_email)
self.assertTrue(plus_email_allowed)
# The base email should also be allowed
base_email_allowed = AllowedEmail.is_allowed_email(self.email)
self.assertTrue(base_email_allowed)
# This email shouldn't exist in the DB
self.assertFalse(AllowedEmail.objects.filter(email=self.email).exists())
def test_invalid_regex_for_plus_email(self):
"""
Test for an invalid email that contains a '+'.
This base email should still pass, but the regex rule should not.
Our regex should only pass for emails that end with a '+'
Example:
Invalid email - 1+joe@igorville.com
Valid email: - joe+1@igorville.com
"""
AllowedEmail.objects.create(email=self.invalid_plus_email)
invalid_plus_email = AllowedEmail.is_allowed_email(self.invalid_plus_email)
# We still expect that this will pass, it exists in the db
self.assertTrue(invalid_plus_email)
# The base email SHOULD NOT pass, as it doesn't match our regex
base_email = AllowedEmail.is_allowed_email(self.email)
self.assertFalse(base_email)
# For good measure, also check the other plus email
regular_plus_email = AllowedEmail.is_allowed_email(self.plus_email)
self.assertFalse(regular_plus_email)