mirror of
https://github.com/cisagov/manage.get.gov.git
synced 2025-07-26 04:28:39 +02:00
3901 lines
162 KiB
Python
3901 lines
162 KiB
Python
from datetime import datetime
|
|
from django.utils import timezone
|
|
from django.test import TestCase, RequestFactory, Client
|
|
from django.contrib.admin.sites import AdminSite
|
|
from registrar.utility.email import EmailSendingError
|
|
from registrar.utility.errors import MissingEmailError
|
|
from waffle.testutils import override_flag
|
|
from django_webtest import WebTest # type: ignore
|
|
from api.tests.common import less_console_noise_decorator
|
|
from django.urls import reverse
|
|
from registrar.admin import (
|
|
DomainAdmin,
|
|
DomainInvitationAdmin,
|
|
ListHeaderAdmin,
|
|
MyUserAdmin,
|
|
AuditedAdmin,
|
|
ContactAdmin,
|
|
DomainInformationAdmin,
|
|
MyHostAdmin,
|
|
PortfolioInvitationAdmin,
|
|
UserDomainRoleAdmin,
|
|
VerifiedByStaffAdmin,
|
|
FsmModelResource,
|
|
WebsiteAdmin,
|
|
DraftDomainAdmin,
|
|
FederalAgencyAdmin,
|
|
PublicContactAdmin,
|
|
TransitionDomainAdmin,
|
|
UserGroupAdmin,
|
|
PortfolioAdmin,
|
|
)
|
|
from registrar.models import (
|
|
Domain,
|
|
DomainRequest,
|
|
DomainInformation,
|
|
DraftDomain,
|
|
User,
|
|
DomainInvitation,
|
|
Contact,
|
|
PublicContact,
|
|
Host,
|
|
Website,
|
|
FederalAgency,
|
|
UserGroup,
|
|
TransitionDomain,
|
|
Portfolio,
|
|
Suborganization,
|
|
UserPortfolioPermission,
|
|
UserDomainRole,
|
|
SeniorOfficial,
|
|
PortfolioInvitation,
|
|
VerifiedByStaff,
|
|
)
|
|
from .common import (
|
|
MockDbForSharedTests,
|
|
AuditedAdminMockData,
|
|
completed_domain_request,
|
|
create_test_user,
|
|
generic_domain_object,
|
|
less_console_noise,
|
|
mock_user,
|
|
create_superuser,
|
|
create_user,
|
|
multiple_unalphabetical_domain_objects,
|
|
GenericTestHelper,
|
|
)
|
|
from registrar.models.utility.portfolio_helper import UserPortfolioPermissionChoices, UserPortfolioRoleChoices
|
|
from django.contrib.sessions.backends.db import SessionStore
|
|
from django.contrib.auth import get_user_model
|
|
from django.contrib import messages
|
|
|
|
from unittest.mock import ANY, call, patch, Mock
|
|
|
|
import logging
|
|
|
|
logger = logging.getLogger(__name__)
|
|
|
|
|
|
class TestFsmModelResource(TestCase):
|
|
def setUp(self):
|
|
self.resource = FsmModelResource()
|
|
|
|
@less_console_noise_decorator
|
|
def test_init_instance(self):
|
|
"""Test initializing an instance of a class with a FSM field"""
|
|
|
|
# Mock a row with FSMField data
|
|
row_data = {"state": "ready"}
|
|
|
|
self.resource._meta.model = Domain
|
|
|
|
instance = self.resource.init_instance(row=row_data)
|
|
|
|
# Assert that the instance is initialized correctly
|
|
self.assertIsInstance(instance, Domain)
|
|
self.assertEqual(instance.state, "ready")
|
|
|
|
@less_console_noise_decorator
|
|
def test_import_field(self):
|
|
"""Test that importing a field does not import FSM field"""
|
|
|
|
# Mock a FSMField and a non-FSM-field
|
|
fsm_field_mock = Mock(attribute="state", column_name="state")
|
|
field_mock = Mock(attribute="name", column_name="name")
|
|
# Mock the data
|
|
data_mock = {"state": "unknown", "name": "test"}
|
|
# Define a mock Domain
|
|
obj = Domain(state=Domain.State.UNKNOWN, name="test")
|
|
|
|
# Mock the save() method of fields so that we can test if save is called
|
|
# save() is only supposed to be called for non FSM fields
|
|
field_mock.save = Mock()
|
|
fsm_field_mock.save = Mock()
|
|
|
|
# Call the method with FSMField and non-FSMField
|
|
self.resource.import_field(fsm_field_mock, obj, data=data_mock, is_m2m=False)
|
|
self.resource.import_field(field_mock, obj, data=data_mock, is_m2m=False)
|
|
|
|
# Assert that field.save() in super().import_field() is called only for non-FSMField
|
|
field_mock.save.assert_called_once()
|
|
fsm_field_mock.save.assert_not_called()
|
|
|
|
|
|
class TestDomainInvitationAdmin(WebTest):
|
|
"""Tests for the DomainInvitationAdmin class as super user
|
|
|
|
Notes:
|
|
all tests share superuser; do not change this model in tests
|
|
tests have available superuser, client, and admin
|
|
"""
|
|
|
|
# csrf checks do not work with WebTest.
|
|
# We disable them here. TODO for another ticket.
|
|
csrf_checks = False
|
|
|
|
@classmethod
|
|
def setUpClass(self):
|
|
super().setUpClass()
|
|
self.site = AdminSite()
|
|
self.factory = RequestFactory()
|
|
self.superuser = create_superuser()
|
|
|
|
def setUp(self):
|
|
super().setUp()
|
|
self.admin = ListHeaderAdmin(model=DomainInvitationAdmin, admin_site=AdminSite())
|
|
self.domain = Domain.objects.create(name="example.com")
|
|
self.portfolio = Portfolio.objects.create(organization_name="new portfolio", creator=self.superuser)
|
|
DomainInformation.objects.create(domain=self.domain, portfolio=self.portfolio, creator=self.superuser)
|
|
"""Create a client object"""
|
|
self.client = Client(HTTP_HOST="localhost:8080")
|
|
self.client.force_login(self.superuser)
|
|
self.app.set_user(self.superuser.username)
|
|
|
|
def tearDown(self):
|
|
"""Delete all DomainInvitation objects"""
|
|
PortfolioInvitation.objects.all().delete()
|
|
DomainInvitation.objects.all().delete()
|
|
DomainInformation.objects.all().delete()
|
|
Portfolio.objects.all().delete()
|
|
Domain.objects.all().delete()
|
|
Contact.objects.all().delete()
|
|
User.objects.all().delete()
|
|
|
|
@less_console_noise_decorator
|
|
def test_has_model_description(self):
|
|
"""Tests if this model has a model description on the table view"""
|
|
self.client.force_login(self.superuser)
|
|
response = self.client.get(
|
|
"/admin/registrar/domaininvitation/",
|
|
follow=True,
|
|
)
|
|
|
|
# Make sure that the page is loaded correctly
|
|
self.assertEqual(response.status_code, 200)
|
|
|
|
# Test for a description snippet
|
|
self.assertContains(
|
|
response, "Domain invitations contain all individuals who have been invited to manage a .gov domain."
|
|
)
|
|
self.assertContains(response, "Show more")
|
|
|
|
@less_console_noise_decorator
|
|
def test_has_change_form_description(self):
|
|
"""Tests if this model has a model description on the change form view"""
|
|
self.client.force_login(self.superuser)
|
|
|
|
domain, _ = Domain.objects.get_or_create(name="systemofadown.com")
|
|
|
|
domain_invitation, _ = DomainInvitation.objects.get_or_create(email="toxicity@systemofadown.com", domain=domain)
|
|
|
|
response = self.client.get(
|
|
"/admin/registrar/domaininvitation/{}/change/".format(domain_invitation.pk),
|
|
follow=True,
|
|
)
|
|
|
|
# Make sure that the page is loaded correctly
|
|
self.assertEqual(response.status_code, 200)
|
|
|
|
# Test for a description snippet
|
|
self.assertContains(
|
|
response,
|
|
"If you add someone to a domain here, it will trigger emails to the invitee and all managers of the domain",
|
|
)
|
|
|
|
@less_console_noise_decorator
|
|
def test_get_filters(self):
|
|
"""Ensures that our filters are displaying correctly"""
|
|
with less_console_noise():
|
|
self.client.force_login(self.superuser)
|
|
|
|
response = self.client.get(
|
|
"/admin/registrar/domaininvitation/",
|
|
{},
|
|
follow=True,
|
|
)
|
|
|
|
# Assert that the filters are added
|
|
self.assertContains(response, "invited", count=6)
|
|
self.assertContains(response, "Invited", count=2)
|
|
self.assertContains(response, "retrieved", count=3)
|
|
self.assertContains(response, "Retrieved", count=2)
|
|
|
|
# Check for the HTML context specificially
|
|
invited_html = '<a id="status-filter-invited" href="?status__exact=invited">Invited</a>'
|
|
retrieved_html = '<a id="status-filter-retrieved" href="?status__exact=retrieved">Retrieved</a>'
|
|
|
|
self.assertContains(response, invited_html, count=1)
|
|
self.assertContains(response, retrieved_html, count=1)
|
|
|
|
@less_console_noise_decorator
|
|
@override_flag("organization_feature", active=True)
|
|
@patch("registrar.admin.send_domain_invitation_email")
|
|
@patch("registrar.admin.send_portfolio_invitation_email")
|
|
@patch("django.contrib.messages.success")
|
|
def test_add_domain_invitation_success_when_user_not_portfolio_member(
|
|
self, mock_messages_success, mock_send_portfolio_email, mock_send_domain_email
|
|
):
|
|
"""Test saving a domain invitation when the user exists and is not a portfolio member.
|
|
|
|
Should send out domain and portfolio invites.
|
|
Should trigger success messages for both email sends.
|
|
Should attempt to retrieve the domain invitation.
|
|
Should attempt to retrieve the portfolio invitation."""
|
|
|
|
user = User.objects.create_user(email="test@example.com", username="username")
|
|
|
|
# Create a domain invitation instance
|
|
invitation = DomainInvitation(email="test@example.com", domain=self.domain)
|
|
|
|
admin_instance = DomainInvitationAdmin(DomainInvitation, admin_site=None)
|
|
|
|
# Create a request object
|
|
request = self.factory.post("/admin/registrar/DomainInvitation/add/")
|
|
request.user = self.superuser
|
|
|
|
admin_instance.save_model(request, invitation, form=None, change=False)
|
|
|
|
# Assert sends appropriate emails - domain and portfolio invites
|
|
mock_send_domain_email.assert_called_once_with(
|
|
email="test@example.com",
|
|
requestor=self.superuser,
|
|
domains=self.domain,
|
|
is_member_of_different_org=None,
|
|
requested_user=user,
|
|
)
|
|
mock_send_portfolio_email.assert_called_once_with(
|
|
email="test@example.com",
|
|
requestor=self.superuser,
|
|
portfolio=self.portfolio,
|
|
is_admin_invitation=False,
|
|
)
|
|
|
|
# Assert success message
|
|
mock_messages_success.assert_has_calls(
|
|
[
|
|
call(request, "test@example.com has been invited to the organization: new portfolio"),
|
|
call(request, "test@example.com has been invited to the domain: example.com"),
|
|
]
|
|
)
|
|
|
|
# Assert the invitations were saved
|
|
self.assertEqual(DomainInvitation.objects.count(), 1)
|
|
self.assertEqual(DomainInvitation.objects.first().email, "test@example.com")
|
|
self.assertEqual(PortfolioInvitation.objects.count(), 1)
|
|
self.assertEqual(PortfolioInvitation.objects.first().email, "test@example.com")
|
|
|
|
# Assert invitations were retrieved
|
|
domain_invitation = DomainInvitation.objects.get(email=user.email, domain=self.domain)
|
|
portfolio_invitation = PortfolioInvitation.objects.get(email=user.email, portfolio=self.portfolio)
|
|
|
|
self.assertEqual(domain_invitation.status, DomainInvitation.DomainInvitationStatus.RETRIEVED)
|
|
self.assertEqual(portfolio_invitation.status, PortfolioInvitation.PortfolioInvitationStatus.RETRIEVED)
|
|
self.assertEqual(UserDomainRole.objects.count(), 1)
|
|
self.assertEqual(UserDomainRole.objects.first().user, user)
|
|
self.assertEqual(UserPortfolioPermission.objects.count(), 1)
|
|
self.assertEqual(UserPortfolioPermission.objects.first().user, user)
|
|
|
|
@less_console_noise_decorator
|
|
@override_flag("organization_feature", active=False)
|
|
@patch("registrar.admin.send_domain_invitation_email")
|
|
@patch("registrar.admin.send_portfolio_invitation_email")
|
|
@patch("django.contrib.messages.success")
|
|
def test_add_domain_invitation_success_when_user_not_portfolio_member_and_organization_feature_off(
|
|
self, mock_messages_success, mock_send_portfolio_email, mock_send_domain_email
|
|
):
|
|
"""Test saving a domain invitation when the user exists and organization_feature flag is off.
|
|
|
|
Should send out a domain invitation.
|
|
Should not send a out portfolio invitation.
|
|
Should trigger success message for the domain invitation.
|
|
Should retrieve the domain invitation.
|
|
Should not create a portfolio invitation."""
|
|
|
|
user = User.objects.create_user(email="test@example.com", username="username")
|
|
|
|
# Create a domain invitation instance
|
|
invitation = DomainInvitation(email="test@example.com", domain=self.domain)
|
|
|
|
admin_instance = DomainInvitationAdmin(DomainInvitation, admin_site=None)
|
|
|
|
# Create a request object
|
|
request = self.factory.post("/admin/registrar/DomainInvitation/add/")
|
|
request.user = self.superuser
|
|
|
|
admin_instance.save_model(request, invitation, form=None, change=False)
|
|
|
|
# Assert sends appropriate emails - domain but not portfolio
|
|
mock_send_domain_email.assert_called_once_with(
|
|
email="test@example.com",
|
|
requestor=self.superuser,
|
|
domains=self.domain,
|
|
is_member_of_different_org=None,
|
|
requested_user=user,
|
|
)
|
|
mock_send_portfolio_email.assert_not_called()
|
|
|
|
# Assert correct invite was created
|
|
self.assertEqual(DomainInvitation.objects.count(), 1)
|
|
self.assertEqual(PortfolioInvitation.objects.count(), 0)
|
|
|
|
# Assert success message
|
|
mock_messages_success.assert_called_once_with(
|
|
request, "test@example.com has been invited to the domain: example.com"
|
|
)
|
|
|
|
# Assert the domain invitation was saved
|
|
self.assertEqual(DomainInvitation.objects.count(), 1)
|
|
self.assertEqual(DomainInvitation.objects.first().email, "test@example.com")
|
|
self.assertEqual(PortfolioInvitation.objects.count(), 0)
|
|
|
|
# Assert the domain invitation was retrieved
|
|
domain_invitation = DomainInvitation.objects.get(email=user.email, domain=self.domain)
|
|
|
|
self.assertEqual(domain_invitation.status, DomainInvitation.DomainInvitationStatus.RETRIEVED)
|
|
self.assertEqual(UserDomainRole.objects.count(), 1)
|
|
self.assertEqual(UserDomainRole.objects.first().user, user)
|
|
self.assertEqual(UserPortfolioPermission.objects.count(), 0)
|
|
|
|
@less_console_noise_decorator
|
|
@override_flag("organization_feature", active=True)
|
|
@override_flag("multiple_portfolios", active=True)
|
|
@patch("registrar.admin.send_domain_invitation_email")
|
|
@patch("registrar.admin.send_portfolio_invitation_email")
|
|
@patch("django.contrib.messages.success")
|
|
def test_add_domain_invitation_success_when_user_not_portfolio_member_and_multiple_portfolio_feature_on(
|
|
self, mock_messages_success, mock_send_portfolio_email, mock_send_domain_email
|
|
):
|
|
"""Test saving a domain invitation when the user exists and multiple_portfolio flag is on.
|
|
|
|
Should send out a domain invitation.
|
|
Should not send a out portfolio invitation.
|
|
Should trigger success message for the domain invitation.
|
|
Should retrieve the domain invitation.
|
|
Should not create a portfolio invitation.
|
|
|
|
NOTE: This test may need to be reworked when the multiple_portfolio flag is fully fleshed out.
|
|
"""
|
|
|
|
user = User.objects.create_user(email="test@example.com", username="username")
|
|
|
|
# Create a domain invitation instance
|
|
invitation = DomainInvitation(email="test@example.com", domain=self.domain)
|
|
|
|
admin_instance = DomainInvitationAdmin(DomainInvitation, admin_site=None)
|
|
|
|
# Create a request object
|
|
request = self.factory.post("/admin/registrar/DomainInvitation/add/")
|
|
request.user = self.superuser
|
|
|
|
admin_instance.save_model(request, invitation, form=None, change=False)
|
|
|
|
# Assert sends appropriate emails - domain but not portfolio
|
|
mock_send_domain_email.assert_called_once_with(
|
|
email="test@example.com",
|
|
requestor=self.superuser,
|
|
domains=self.domain,
|
|
is_member_of_different_org=None,
|
|
requested_user=user,
|
|
)
|
|
mock_send_portfolio_email.assert_not_called()
|
|
|
|
# Assert correct invite was created
|
|
self.assertEqual(DomainInvitation.objects.count(), 1)
|
|
self.assertEqual(PortfolioInvitation.objects.count(), 0)
|
|
|
|
# Assert success message
|
|
mock_messages_success.assert_called_once_with(
|
|
request, "test@example.com has been invited to the domain: example.com"
|
|
)
|
|
|
|
# Assert the domain invitation was saved
|
|
self.assertEqual(DomainInvitation.objects.count(), 1)
|
|
self.assertEqual(DomainInvitation.objects.first().email, "test@example.com")
|
|
self.assertEqual(PortfolioInvitation.objects.count(), 0)
|
|
|
|
# Assert the domain invitation was retrieved
|
|
domain_invitation = DomainInvitation.objects.get(email=user.email, domain=self.domain)
|
|
|
|
self.assertEqual(domain_invitation.status, DomainInvitation.DomainInvitationStatus.RETRIEVED)
|
|
self.assertEqual(UserDomainRole.objects.count(), 1)
|
|
self.assertEqual(UserDomainRole.objects.first().user, user)
|
|
self.assertEqual(UserPortfolioPermission.objects.count(), 0)
|
|
|
|
@less_console_noise_decorator
|
|
@override_flag("organization_feature", active=True)
|
|
@patch("registrar.admin.send_domain_invitation_email")
|
|
@patch("registrar.admin.send_portfolio_invitation_email")
|
|
@patch("django.contrib.messages.success")
|
|
def test_add_domain_invitation_success_when_user_existing_portfolio_member(
|
|
self, mock_messages_success, mock_send_portfolio_email, mock_send_domain_email
|
|
):
|
|
"""Test saving a domain invitation when the user exists and a portfolio invitation exists.
|
|
|
|
Should send out domain invitation only.
|
|
Should trigger success message for the domain invitation.
|
|
Should retrieve the domain invitation."""
|
|
|
|
user = User.objects.create_user(email="test@example.com", username="username")
|
|
|
|
# Create a domain invitation instance
|
|
invitation = DomainInvitation(email="test@example.com", domain=self.domain)
|
|
|
|
UserPortfolioPermission.objects.create(
|
|
user=user, portfolio=self.portfolio, roles=[UserPortfolioRoleChoices.ORGANIZATION_MEMBER]
|
|
)
|
|
|
|
admin_instance = DomainInvitationAdmin(DomainInvitation, admin_site=None)
|
|
|
|
# Create a request object
|
|
request = self.factory.post("/admin/registrar/DomainInvitation/add/")
|
|
request.user = self.superuser
|
|
|
|
# Patch the retrieve method to ensure it is not called
|
|
with patch.object(DomainInvitation, "retrieve") as domain_invitation_mock_retrieve:
|
|
with patch.object(PortfolioInvitation, "retrieve") as portfolio_invitation_mock_retrieve:
|
|
admin_instance.save_model(request, invitation, form=None, change=False)
|
|
|
|
# Assert sends appropriate emails - domain and portfolio invites
|
|
mock_send_domain_email.assert_called_once_with(
|
|
email="test@example.com",
|
|
requestor=self.superuser,
|
|
domains=self.domain,
|
|
is_member_of_different_org=None,
|
|
requested_user=user,
|
|
)
|
|
mock_send_portfolio_email.assert_not_called
|
|
|
|
# Assert retrieve was not called
|
|
domain_invitation_mock_retrieve.assert_called_once()
|
|
portfolio_invitation_mock_retrieve.assert_not_called()
|
|
|
|
# Assert success message
|
|
mock_messages_success.assert_called_once_with(
|
|
request, "test@example.com has been invited to the domain: example.com"
|
|
)
|
|
|
|
# Assert the invitations were saved
|
|
self.assertEqual(DomainInvitation.objects.count(), 1)
|
|
self.assertEqual(DomainInvitation.objects.first().email, "test@example.com")
|
|
self.assertEqual(PortfolioInvitation.objects.count(), 0)
|
|
|
|
@less_console_noise_decorator
|
|
@override_flag("organization_feature", active=True)
|
|
@patch("registrar.admin.send_domain_invitation_email")
|
|
@patch("registrar.admin.send_portfolio_invitation_email")
|
|
@patch("django.contrib.messages.error")
|
|
def test_add_domain_invitation_when_user_not_portfolio_member_raises_exception_sending_portfolio_email(
|
|
self, mock_messages_error, mock_send_portfolio_email, mock_send_domain_email
|
|
):
|
|
"""Test saving a domain invitation when the user exists and is not a portfolio member raises
|
|
sending portfolio email exception.
|
|
|
|
Should only attempt to send the portfolio invitation.
|
|
Should trigger error message on portfolio invitation.
|
|
Should not attempt to retrieve the domain invitation."""
|
|
|
|
mock_send_portfolio_email.side_effect = MissingEmailError("craving a burger")
|
|
|
|
User.objects.create_user(email="test@example.com", username="username")
|
|
|
|
# Create a domain invitation instance
|
|
invitation = DomainInvitation(email="test@example.com", domain=self.domain)
|
|
|
|
admin_instance = DomainInvitationAdmin(DomainInvitation, admin_site=None)
|
|
|
|
# Create a request object
|
|
request = self.factory.post("/admin/registrar/DomainInvitation/add/")
|
|
request.user = self.superuser
|
|
|
|
# Patch the retrieve method to ensure it is not called
|
|
with patch.object(DomainInvitation, "retrieve") as domain_invitation_mock_retrieve:
|
|
with patch.object(PortfolioInvitation, "retrieve") as portfolio_invitation_mock_retrieve:
|
|
admin_instance.save_model(request, invitation, form=None, change=False)
|
|
|
|
# Assert sends appropriate emails - domain and portfolio invites
|
|
mock_send_domain_email.assert_not_called()
|
|
mock_send_portfolio_email.assert_called_once_with(
|
|
email="test@example.com",
|
|
requestor=self.superuser,
|
|
portfolio=self.portfolio,
|
|
is_admin_invitation=False,
|
|
)
|
|
|
|
# Assert retrieve on domain invite only was called
|
|
domain_invitation_mock_retrieve.assert_not_called()
|
|
portfolio_invitation_mock_retrieve.assert_not_called()
|
|
|
|
# Assert error message
|
|
mock_messages_error.assert_called_once_with(
|
|
request, "Can't send invitation email. No email is associated with your user account."
|
|
)
|
|
|
|
# Assert the invitations were saved
|
|
self.assertEqual(DomainInvitation.objects.count(), 0)
|
|
self.assertEqual(PortfolioInvitation.objects.count(), 0)
|
|
|
|
@less_console_noise_decorator
|
|
@override_flag("organization_feature", active=True)
|
|
@patch("registrar.admin.send_domain_invitation_email")
|
|
@patch("registrar.admin.send_portfolio_invitation_email")
|
|
@patch("django.contrib.messages.success")
|
|
@patch("django.contrib.messages.error")
|
|
def test_add_domain_invitation_when_user_not_portfolio_member_raises_exception_sending_domain_email(
|
|
self, mock_messages_error, mock_messages_success, mock_send_portfolio_email, mock_send_domain_email
|
|
):
|
|
"""Test saving a domain invitation when the user exists and is not a portfolio member raises
|
|
sending domain email exception.
|
|
|
|
Should send out the portfolio invitation and attempt to send the domain invitation.
|
|
Should trigger portfolio invitation success message.
|
|
Should trigger domain invitation error message.
|
|
Should not attempt to retrieve the domain invitation.
|
|
Should attempt to retrieve the portfolio invitation."""
|
|
|
|
mock_send_domain_email.side_effect = MissingEmailError("craving a burger")
|
|
|
|
user = User.objects.create_user(email="test@example.com", username="username")
|
|
|
|
# Create a domain invitation instance
|
|
invitation = DomainInvitation(email="test@example.com", domain=self.domain)
|
|
|
|
admin_instance = DomainInvitationAdmin(DomainInvitation, admin_site=None)
|
|
|
|
# Create a request object
|
|
request = self.factory.post("/admin/registrar/DomainInvitation/add/")
|
|
request.user = self.superuser
|
|
|
|
# Patch the retrieve method to ensure it is not called
|
|
with patch.object(DomainInvitation, "retrieve") as domain_invitation_mock_retrieve:
|
|
with patch.object(PortfolioInvitation, "retrieve") as portfolio_invitation_mock_retrieve:
|
|
admin_instance.save_model(request, invitation, form=None, change=False)
|
|
|
|
# Assert sends appropriate emails - domain and portfolio invites
|
|
mock_send_domain_email.assert_called_once_with(
|
|
email="test@example.com",
|
|
requestor=self.superuser,
|
|
domains=self.domain,
|
|
is_member_of_different_org=None,
|
|
requested_user=user,
|
|
)
|
|
mock_send_portfolio_email.assert_called_once_with(
|
|
email="test@example.com",
|
|
requestor=self.superuser,
|
|
portfolio=self.portfolio,
|
|
is_admin_invitation=False,
|
|
)
|
|
|
|
# Assert retrieve on domain invite only was called
|
|
domain_invitation_mock_retrieve.assert_not_called()
|
|
portfolio_invitation_mock_retrieve.assert_called_once()
|
|
|
|
# Assert success message
|
|
mock_messages_success.assert_called_once_with(
|
|
request, "test@example.com has been invited to the organization: new portfolio"
|
|
)
|
|
|
|
# Assert error message
|
|
mock_messages_error.assert_called_once_with(
|
|
request, "Can't send invitation email. No email is associated with your user account."
|
|
)
|
|
|
|
# Assert the invitations were saved
|
|
self.assertEqual(DomainInvitation.objects.count(), 0)
|
|
self.assertEqual(PortfolioInvitation.objects.count(), 1)
|
|
|
|
@less_console_noise_decorator
|
|
@override_flag("organization_feature", active=True)
|
|
@patch("registrar.admin.send_domain_invitation_email")
|
|
@patch("registrar.admin.send_portfolio_invitation_email")
|
|
@patch("django.contrib.messages.success")
|
|
@patch("django.contrib.messages.error")
|
|
def test_add_domain_invitation_when_user_existing_portfolio_member_raises_exception_sending_domain_email(
|
|
self, mock_messages_error, mock_messages_success, mock_send_portfolio_email, mock_send_domain_email
|
|
):
|
|
"""Test saving a domain invitation when the user exists and is not a portfolio member raises
|
|
sending domain email exception.
|
|
|
|
Should send out the portfolio invitation and attempt to send the domain invitation.
|
|
Should trigger portfolio invitation success message.
|
|
Should trigger domain invitation error message.
|
|
Should not attempt to retrieve the domain invitation.
|
|
Should attempt to retrieve the portfolio invitation."""
|
|
|
|
mock_send_domain_email.side_effect = MissingEmailError("craving a burger")
|
|
|
|
user = User.objects.create_user(email="test@example.com", username="username")
|
|
|
|
UserPortfolioPermission.objects.create(
|
|
user=user, portfolio=self.portfolio, roles=[UserPortfolioRoleChoices.ORGANIZATION_MEMBER]
|
|
)
|
|
|
|
# Create a domain invitation instance
|
|
invitation = DomainInvitation(email="test@example.com", domain=self.domain)
|
|
|
|
admin_instance = DomainInvitationAdmin(DomainInvitation, admin_site=None)
|
|
|
|
# Create a request object
|
|
request = self.factory.post("/admin/registrar/DomainInvitation/add/")
|
|
request.user = self.superuser
|
|
|
|
# Patch the retrieve method to ensure it is not called
|
|
with patch.object(DomainInvitation, "retrieve") as domain_invitation_mock_retrieve:
|
|
with patch.object(PortfolioInvitation, "retrieve") as portfolio_invitation_mock_retrieve:
|
|
admin_instance.save_model(request, invitation, form=None, change=False)
|
|
|
|
# Assert sends appropriate emails - domain and portfolio invites
|
|
mock_send_domain_email.assert_called_once_with(
|
|
email="test@example.com",
|
|
requestor=self.superuser,
|
|
domains=self.domain,
|
|
is_member_of_different_org=None,
|
|
requested_user=user,
|
|
)
|
|
mock_send_portfolio_email.assert_not_called()
|
|
|
|
# Assert retrieve on domain invite only was called
|
|
domain_invitation_mock_retrieve.assert_not_called()
|
|
portfolio_invitation_mock_retrieve.assert_not_called()
|
|
|
|
# Assert success message
|
|
mock_messages_success.assert_not_called()
|
|
|
|
# Assert error message
|
|
mock_messages_error.assert_called_once_with(
|
|
request, "Can't send invitation email. No email is associated with your user account."
|
|
)
|
|
|
|
# Assert the invitations were saved
|
|
self.assertEqual(DomainInvitation.objects.count(), 0)
|
|
self.assertEqual(PortfolioInvitation.objects.count(), 0)
|
|
|
|
@less_console_noise_decorator
|
|
@override_flag("organization_feature", active=True)
|
|
@patch("registrar.admin.send_domain_invitation_email")
|
|
@patch("registrar.admin.send_portfolio_invitation_email")
|
|
@patch("django.contrib.messages.success")
|
|
def test_add_domain_invitation_success_when_email_not_portfolio_member(
|
|
self, mock_messages_success, mock_send_portfolio_email, mock_send_domain_email
|
|
):
|
|
"""Test saving a domain invitation when the user does not exist.
|
|
|
|
Should send out domain and portfolio invitations.
|
|
Should trigger success messages.
|
|
Should not attempt to retrieve the domain invitation.
|
|
Should not attempt to retrieve the portfolio invitation."""
|
|
# Create a domain invitation instance
|
|
invitation = DomainInvitation(email="nonexistent@example.com", domain=self.domain)
|
|
|
|
admin_instance = DomainInvitationAdmin(DomainInvitation, admin_site=None)
|
|
|
|
# Create a request object
|
|
request = self.factory.post("/admin/registrar/DomainInvitation/add/")
|
|
request.user = self.superuser
|
|
|
|
# Patch the retrieve method to ensure it is not called
|
|
with patch.object(DomainInvitation, "retrieve") as domain_invitation_mock_retrieve:
|
|
with patch.object(PortfolioInvitation, "retrieve") as portfolio_invitation_mock_retrieve:
|
|
admin_instance.save_model(request, invitation, form=None, change=False)
|
|
|
|
# Assert sends appropriate emails - domain and portfolio invites
|
|
mock_send_domain_email.assert_called_once_with(
|
|
email="nonexistent@example.com",
|
|
requestor=self.superuser,
|
|
domains=self.domain,
|
|
is_member_of_different_org=None,
|
|
requested_user=None,
|
|
)
|
|
mock_send_portfolio_email.assert_called_once_with(
|
|
email="nonexistent@example.com",
|
|
requestor=self.superuser,
|
|
portfolio=self.portfolio,
|
|
is_admin_invitation=False,
|
|
)
|
|
|
|
# Assert retrieve was not called
|
|
domain_invitation_mock_retrieve.assert_not_called()
|
|
portfolio_invitation_mock_retrieve.assert_not_called()
|
|
|
|
# Assert success message
|
|
mock_messages_success.assert_has_calls(
|
|
[
|
|
call(request, "nonexistent@example.com has been invited to the organization: new portfolio"),
|
|
call(request, "nonexistent@example.com has been invited to the domain: example.com"),
|
|
]
|
|
)
|
|
|
|
# Assert the invitations were saved
|
|
self.assertEqual(DomainInvitation.objects.count(), 1)
|
|
self.assertEqual(DomainInvitation.objects.first().email, "nonexistent@example.com")
|
|
self.assertEqual(PortfolioInvitation.objects.count(), 1)
|
|
self.assertEqual(PortfolioInvitation.objects.first().email, "nonexistent@example.com")
|
|
|
|
@less_console_noise_decorator
|
|
@override_flag("organization_feature", active=False)
|
|
@patch("registrar.admin.send_domain_invitation_email")
|
|
@patch("registrar.admin.send_portfolio_invitation_email")
|
|
@patch("django.contrib.messages.success")
|
|
def test_add_domain_invitation_success_when_email_not_portfolio_member_and_organization_feature_off(
|
|
self, mock_messages_success, mock_send_portfolio_email, mock_send_domain_email
|
|
):
|
|
"""Test saving a domain invitation when the user does not exist and organization_feature flag is off.
|
|
|
|
Should send out a domain invitation.
|
|
Should not send a out portfolio invitation.
|
|
Should trigger success message for domain invitation.
|
|
Should not retrieve the domain invitation.
|
|
Should not create a portfolio invitation."""
|
|
# Create a domain invitation instance
|
|
invitation = DomainInvitation(email="nonexistent@example.com", domain=self.domain)
|
|
|
|
admin_instance = DomainInvitationAdmin(DomainInvitation, admin_site=None)
|
|
|
|
# Create a request object
|
|
request = self.factory.post("/admin/registrar/DomainInvitation/add/")
|
|
request.user = self.superuser
|
|
|
|
# Patch the retrieve method to ensure it is not called
|
|
with patch.object(DomainInvitation, "retrieve") as domain_invitation_mock_retrieve:
|
|
with patch.object(PortfolioInvitation, "retrieve") as portfolio_invitation_mock_retrieve:
|
|
admin_instance.save_model(request, invitation, form=None, change=False)
|
|
|
|
# Assert sends appropriate emails - domain but not portfolio
|
|
mock_send_domain_email.assert_called_once_with(
|
|
email="nonexistent@example.com",
|
|
requestor=self.superuser,
|
|
domains=self.domain,
|
|
is_member_of_different_org=None,
|
|
requested_user=None,
|
|
)
|
|
mock_send_portfolio_email.assert_not_called()
|
|
|
|
# Assert retrieve on domain invite only was called
|
|
domain_invitation_mock_retrieve.assert_not_called()
|
|
portfolio_invitation_mock_retrieve.assert_not_called()
|
|
|
|
# Assert success message
|
|
mock_messages_success.assert_called_once_with(
|
|
request, "nonexistent@example.com has been invited to the domain: example.com"
|
|
)
|
|
|
|
# Assert the domain invitation was saved
|
|
self.assertEqual(DomainInvitation.objects.count(), 1)
|
|
self.assertEqual(DomainInvitation.objects.first().email, "nonexistent@example.com")
|
|
self.assertEqual(PortfolioInvitation.objects.count(), 0)
|
|
|
|
@less_console_noise_decorator
|
|
@override_flag("organization_feature", active=True)
|
|
@override_flag("multiple_portfolios", active=True)
|
|
@patch("registrar.admin.send_domain_invitation_email")
|
|
@patch("registrar.admin.send_portfolio_invitation_email")
|
|
@patch("django.contrib.messages.success")
|
|
def test_add_domain_invitation_success_when_email_not_portfolio_member_and_multiple_portfolio_feature_on(
|
|
self, mock_messages_success, mock_send_portfolio_email, mock_send_domain_email
|
|
):
|
|
"""Test saving a domain invitation when the user does not exist and multiple_portfolio flag is on.
|
|
|
|
Should send out a domain invitation.
|
|
Should not send a out portfolio invitation.
|
|
Should trigger success message for domain invitation.
|
|
Should not retrieve the domain invitation.
|
|
Should not create a portfolio invitation."""
|
|
# Create a domain invitation instance
|
|
invitation = DomainInvitation(email="nonexistent@example.com", domain=self.domain)
|
|
|
|
admin_instance = DomainInvitationAdmin(DomainInvitation, admin_site=None)
|
|
|
|
# Create a request object
|
|
request = self.factory.post("/admin/registrar/DomainInvitation/add/")
|
|
request.user = self.superuser
|
|
|
|
# Patch the retrieve method to ensure it is not called
|
|
with patch.object(DomainInvitation, "retrieve") as domain_invitation_mock_retrieve:
|
|
with patch.object(PortfolioInvitation, "retrieve") as portfolio_invitation_mock_retrieve:
|
|
admin_instance.save_model(request, invitation, form=None, change=False)
|
|
|
|
# Assert sends appropriate emails - domain but not portfolio
|
|
mock_send_domain_email.assert_called_once_with(
|
|
email="nonexistent@example.com",
|
|
requestor=self.superuser,
|
|
domains=self.domain,
|
|
is_member_of_different_org=None,
|
|
requested_user=None,
|
|
)
|
|
mock_send_portfolio_email.assert_not_called()
|
|
|
|
# Assert retrieve on domain invite only was called
|
|
domain_invitation_mock_retrieve.assert_not_called()
|
|
portfolio_invitation_mock_retrieve.assert_not_called()
|
|
|
|
# Assert success message
|
|
mock_messages_success.assert_called_once_with(
|
|
request, "nonexistent@example.com has been invited to the domain: example.com"
|
|
)
|
|
|
|
# Assert the domain invitation was saved
|
|
self.assertEqual(DomainInvitation.objects.count(), 1)
|
|
self.assertEqual(DomainInvitation.objects.first().email, "nonexistent@example.com")
|
|
self.assertEqual(PortfolioInvitation.objects.count(), 0)
|
|
|
|
@less_console_noise_decorator
|
|
@override_flag("organization_feature", active=True)
|
|
@patch("registrar.admin.send_domain_invitation_email")
|
|
@patch("registrar.admin.send_portfolio_invitation_email")
|
|
@patch("django.contrib.messages.success")
|
|
def test_add_domain_invitation_success_when_email_existing_portfolio_invitation(
|
|
self, mock_messages_success, mock_send_portfolio_email, mock_send_domain_email
|
|
):
|
|
"""Test saving a domain invitation when the user does not exist and a portfolio invitation exists.
|
|
|
|
Should send out domain invitation only.
|
|
Should trigger success message for the domain invitation.
|
|
Should not attempt to retrieve the domain invitation.
|
|
Should not attempt to retrieve the portfolio invitation."""
|
|
|
|
PortfolioInvitation.objects.create(
|
|
email="nonexistent@example.com",
|
|
portfolio=self.portfolio,
|
|
roles=[UserPortfolioRoleChoices.ORGANIZATION_MEMBER],
|
|
)
|
|
|
|
# Create a domain invitation instance
|
|
invitation = DomainInvitation(email="nonexistent@example.com", domain=self.domain)
|
|
|
|
admin_instance = DomainInvitationAdmin(DomainInvitation, admin_site=None)
|
|
|
|
# Create a request object
|
|
request = self.factory.post("/admin/registrar/DomainInvitation/add/")
|
|
request.user = self.superuser
|
|
|
|
# Patch the retrieve method to ensure it is not called
|
|
with patch.object(DomainInvitation, "retrieve") as domain_invitation_mock_retrieve:
|
|
with patch.object(PortfolioInvitation, "retrieve") as portfolio_invitation_mock_retrieve:
|
|
admin_instance.save_model(request, invitation, form=None, change=False)
|
|
|
|
# Assert sends appropriate emails - domain and portfolio invites
|
|
mock_send_domain_email.assert_called_once_with(
|
|
email="nonexistent@example.com",
|
|
requestor=self.superuser,
|
|
domains=self.domain,
|
|
is_member_of_different_org=False,
|
|
requested_user=None,
|
|
)
|
|
mock_send_portfolio_email.assert_not_called
|
|
|
|
# Assert retrieve was not called
|
|
domain_invitation_mock_retrieve.assert_not_called()
|
|
portfolio_invitation_mock_retrieve.assert_not_called()
|
|
|
|
# Assert success message
|
|
mock_messages_success.assert_called_once_with(
|
|
request, "nonexistent@example.com has been invited to the domain: example.com"
|
|
)
|
|
|
|
# Assert the invitations were saved
|
|
self.assertEqual(DomainInvitation.objects.count(), 1)
|
|
self.assertEqual(DomainInvitation.objects.first().email, "nonexistent@example.com")
|
|
self.assertEqual(PortfolioInvitation.objects.count(), 1)
|
|
self.assertEqual(PortfolioInvitation.objects.first().email, "nonexistent@example.com")
|
|
|
|
@less_console_noise_decorator
|
|
@override_flag("organization_feature", active=True)
|
|
@patch("registrar.admin.send_domain_invitation_email")
|
|
@patch("registrar.admin.send_portfolio_invitation_email")
|
|
@patch("django.contrib.messages.error")
|
|
def test_add_domain_invitation_when_user_not_portfolio_email_raises_exception_sending_portfolio_email(
|
|
self, mock_messages_error, mock_send_portfolio_email, mock_send_domain_email
|
|
):
|
|
"""Test saving a domain invitation when the user exists and is not a portfolio member raises
|
|
sending portfolio email exception.
|
|
|
|
Should only attempt to send the portfolio invitation.
|
|
Should trigger error message on portfolio invitation.
|
|
Should not attempt to retrieve the domain invitation.
|
|
Should not attempt to retrieve the portfolio invitation."""
|
|
|
|
mock_send_portfolio_email.side_effect = MissingEmailError("craving a burger")
|
|
|
|
# Create a domain invitation instance
|
|
invitation = DomainInvitation(email="nonexistent@example.com", domain=self.domain)
|
|
|
|
admin_instance = DomainInvitationAdmin(DomainInvitation, admin_site=None)
|
|
|
|
# Create a request object
|
|
request = self.factory.post("/admin/registrar/DomainInvitation/add/")
|
|
request.user = self.superuser
|
|
|
|
# Patch the retrieve method to ensure it is not called
|
|
with patch.object(DomainInvitation, "retrieve") as domain_invitation_mock_retrieve:
|
|
with patch.object(PortfolioInvitation, "retrieve") as portfolio_invitation_mock_retrieve:
|
|
admin_instance.save_model(request, invitation, form=None, change=False)
|
|
|
|
# Assert sends appropriate emails - domain and portfolio invites
|
|
mock_send_domain_email.assert_not_called()
|
|
mock_send_portfolio_email.assert_called_once_with(
|
|
email="nonexistent@example.com",
|
|
requestor=self.superuser,
|
|
portfolio=self.portfolio,
|
|
is_admin_invitation=False,
|
|
)
|
|
|
|
# Assert retrieve on domain invite only was called
|
|
domain_invitation_mock_retrieve.assert_not_called()
|
|
portfolio_invitation_mock_retrieve.assert_not_called()
|
|
|
|
# Assert error message
|
|
mock_messages_error.assert_called_once_with(
|
|
request, "Can't send invitation email. No email is associated with your user account."
|
|
)
|
|
|
|
# Assert the invitations were saved
|
|
self.assertEqual(DomainInvitation.objects.count(), 0)
|
|
self.assertEqual(PortfolioInvitation.objects.count(), 0)
|
|
|
|
@less_console_noise_decorator
|
|
@override_flag("organization_feature", active=True)
|
|
@patch("registrar.admin.send_domain_invitation_email")
|
|
@patch("registrar.admin.send_portfolio_invitation_email")
|
|
@patch("django.contrib.messages.success")
|
|
@patch("django.contrib.messages.error")
|
|
def test_add_domain_invitation_when_user_not_portfolio_email_raises_exception_sending_domain_email(
|
|
self, mock_messages_error, mock_messages_success, mock_send_portfolio_email, mock_send_domain_email
|
|
):
|
|
"""Test saving a domain invitation when the user exists and is not a portfolio member
|
|
raises sending domain email exception.
|
|
|
|
Should send out the portfolio invitation and attempt to send the domain invitation.
|
|
Should trigger portfolio invitation success message.
|
|
Should trigger domain invitation error message.
|
|
Should not attempt to retrieve the domain invitation.
|
|
Should attempt to retrieve the portfolio invitation."""
|
|
|
|
mock_send_domain_email.side_effect = MissingEmailError("craving a burger")
|
|
|
|
# Create a domain invitation instance
|
|
invitation = DomainInvitation(email="nonexistent@example.com", domain=self.domain)
|
|
|
|
admin_instance = DomainInvitationAdmin(DomainInvitation, admin_site=None)
|
|
|
|
# Create a request object
|
|
request = self.factory.post("/admin/registrar/DomainInvitation/add/")
|
|
request.user = self.superuser
|
|
|
|
# Patch the retrieve method to ensure it is not called
|
|
with patch.object(DomainInvitation, "retrieve") as domain_invitation_mock_retrieve:
|
|
with patch.object(PortfolioInvitation, "retrieve") as portfolio_invitation_mock_retrieve:
|
|
admin_instance.save_model(request, invitation, form=None, change=False)
|
|
|
|
# Assert sends appropriate emails - domain and portfolio invites
|
|
mock_send_domain_email.assert_called_once_with(
|
|
email="nonexistent@example.com",
|
|
requestor=self.superuser,
|
|
domains=self.domain,
|
|
is_member_of_different_org=None,
|
|
requested_user=None,
|
|
)
|
|
mock_send_portfolio_email.assert_called_once_with(
|
|
email="nonexistent@example.com",
|
|
requestor=self.superuser,
|
|
portfolio=self.portfolio,
|
|
is_admin_invitation=False,
|
|
)
|
|
|
|
# Assert retrieve on domain invite only was called
|
|
domain_invitation_mock_retrieve.assert_not_called()
|
|
portfolio_invitation_mock_retrieve.assert_not_called()
|
|
|
|
# Assert success message
|
|
mock_messages_success.assert_called_once_with(
|
|
request, "nonexistent@example.com has been invited to the organization: new portfolio"
|
|
)
|
|
|
|
# Assert error message
|
|
mock_messages_error.assert_called_once_with(
|
|
request, "Can't send invitation email. No email is associated with your user account."
|
|
)
|
|
|
|
# Assert the invitations were saved
|
|
self.assertEqual(DomainInvitation.objects.count(), 0)
|
|
self.assertEqual(PortfolioInvitation.objects.count(), 1)
|
|
|
|
@less_console_noise_decorator
|
|
@override_flag("organization_feature", active=True)
|
|
@patch("registrar.admin.send_domain_invitation_email")
|
|
@patch("registrar.admin.send_portfolio_invitation_email")
|
|
@patch("django.contrib.messages.success")
|
|
@patch("django.contrib.messages.error")
|
|
def test_add_domain_invitation_when_user_existing_portfolio_email_raises_exception_sending_domain_email(
|
|
self, mock_messages_error, mock_messages_success, mock_send_portfolio_email, mock_send_domain_email
|
|
):
|
|
"""Test saving a domain invitation when the user exists and is not a portfolio member
|
|
raises sending domain email exception.
|
|
|
|
Should send out the portfolio invitation and attempt to send the domain invitation.
|
|
Should trigger portfolio invitation success message.
|
|
Should trigger domain invitation error message.
|
|
Should not attempt to retrieve the domain invitation.
|
|
Should attempt to retrieve the portfolio invitation."""
|
|
|
|
mock_send_domain_email.side_effect = MissingEmailError("craving a burger")
|
|
|
|
PortfolioInvitation.objects.create(
|
|
email="nonexistent@example.com",
|
|
portfolio=self.portfolio,
|
|
roles=[UserPortfolioRoleChoices.ORGANIZATION_MEMBER],
|
|
)
|
|
|
|
# Create a domain invitation instance
|
|
invitation = DomainInvitation(email="nonexistent@example.com", domain=self.domain)
|
|
|
|
admin_instance = DomainInvitationAdmin(DomainInvitation, admin_site=None)
|
|
|
|
# Create a request object
|
|
request = self.factory.post("/admin/registrar/DomainInvitation/add/")
|
|
request.user = self.superuser
|
|
|
|
# Patch the retrieve method to ensure it is not called
|
|
with patch.object(DomainInvitation, "retrieve") as domain_invitation_mock_retrieve:
|
|
with patch.object(PortfolioInvitation, "retrieve") as portfolio_invitation_mock_retrieve:
|
|
admin_instance.save_model(request, invitation, form=None, change=False)
|
|
|
|
# Assert sends appropriate emails - domain and portfolio invites
|
|
mock_send_domain_email.assert_called_once_with(
|
|
email="nonexistent@example.com",
|
|
requestor=self.superuser,
|
|
domains=self.domain,
|
|
is_member_of_different_org=False,
|
|
requested_user=None,
|
|
)
|
|
mock_send_portfolio_email.assert_not_called()
|
|
|
|
# Assert retrieve on domain invite only was called
|
|
domain_invitation_mock_retrieve.assert_not_called()
|
|
portfolio_invitation_mock_retrieve.assert_not_called()
|
|
|
|
# Assert success message
|
|
mock_messages_success.assert_not_called()
|
|
|
|
# Assert error message
|
|
mock_messages_error.assert_called_once_with(
|
|
request, "Can't send invitation email. No email is associated with your user account."
|
|
)
|
|
|
|
# Assert the invitations were saved
|
|
self.assertEqual(DomainInvitation.objects.count(), 0)
|
|
self.assertEqual(PortfolioInvitation.objects.count(), 1)
|
|
|
|
@less_console_noise_decorator
|
|
def test_custom_delete_confirmation_page(self):
|
|
"""Tests if custom alerts display on Domain Invitation delete page"""
|
|
self.client.force_login(self.superuser)
|
|
self.app.set_user(self.superuser.username)
|
|
domain, _ = Domain.objects.get_or_create(name="domain-invitation-test.gov", state=Domain.State.READY)
|
|
domain_invitation, _ = DomainInvitation.objects.get_or_create(domain=domain)
|
|
|
|
domain_invitation_change_page = self.app.get(
|
|
reverse("admin:registrar_domaininvitation_change", args=[domain_invitation.pk])
|
|
)
|
|
|
|
self.assertContains(domain_invitation_change_page, "domain-invitation-test.gov")
|
|
# click the "Delete" link
|
|
confirmation_page = domain_invitation_change_page.click("Delete", index=0)
|
|
|
|
custom_alert_content = "If you cancel the domain invitation here"
|
|
self.assertContains(confirmation_page, custom_alert_content)
|
|
|
|
@less_console_noise_decorator
|
|
def test_custom_selected_delete_confirmation_page(self):
|
|
"""Tests if custom alerts display on Domain Invitation selected delete page from Domain Invitation table"""
|
|
domain, _ = Domain.objects.get_or_create(name="domain-invitation-test.gov", state=Domain.State.READY)
|
|
domain_invitation, _ = DomainInvitation.objects.get_or_create(domain=domain)
|
|
|
|
# Get the index. The post expects the index to be encoded as a string
|
|
index = f"{domain_invitation.id}"
|
|
|
|
test_helper = GenericTestHelper(
|
|
factory=self.factory,
|
|
user=self.superuser,
|
|
admin=self.admin,
|
|
url=reverse("admin:registrar_domaininvitation_changelist"),
|
|
model=Domain,
|
|
client=self.client,
|
|
)
|
|
|
|
# Simulate selecting a single record, then clicking "Delete selected domains"
|
|
response = test_helper.get_table_delete_confirmation_page("0", index)
|
|
|
|
# Check for custom alert message
|
|
custom_alert_content = "If you cancel the domain invitation here"
|
|
self.assertContains(response, custom_alert_content)
|
|
|
|
|
|
class TestUserPortfolioPermissionAdmin(TestCase):
|
|
"""Tests for the PortfolioInivtationAdmin class"""
|
|
|
|
def setUp(self):
|
|
"""Create a client object"""
|
|
self.client = Client(HTTP_HOST="localhost:8080")
|
|
self.superuser = create_superuser()
|
|
self.testuser = create_test_user()
|
|
self.portfolio = Portfolio.objects.create(organization_name="Test Portfolio", creator=self.superuser)
|
|
|
|
def tearDown(self):
|
|
"""Delete all DomainInvitation objects"""
|
|
Portfolio.objects.all().delete()
|
|
Contact.objects.all().delete()
|
|
User.objects.all().delete()
|
|
UserPortfolioPermission.objects.all().delete()
|
|
|
|
@less_console_noise_decorator
|
|
def test_has_change_form_description(self):
|
|
"""Tests if this model has a model description on the change form view"""
|
|
self.client.force_login(self.superuser)
|
|
|
|
user_portfolio_permission, _ = UserPortfolioPermission.objects.get_or_create(
|
|
user=self.superuser, portfolio=self.portfolio, roles=[UserPortfolioRoleChoices.ORGANIZATION_ADMIN]
|
|
)
|
|
|
|
response = self.client.get(
|
|
"/admin/registrar/userportfoliopermission/{}/change/".format(user_portfolio_permission.pk),
|
|
follow=True,
|
|
)
|
|
|
|
# Make sure that the page is loaded correctly
|
|
self.assertEqual(response.status_code, 200)
|
|
|
|
# Test for a description snippet
|
|
self.assertContains(
|
|
response,
|
|
"If you add someone to a portfolio here, it will not trigger an invitation email.",
|
|
)
|
|
|
|
@less_console_noise_decorator
|
|
def test_delete_confirmation_page_contains_static_message(self):
|
|
"""Ensure the custom message appears in the delete confirmation page."""
|
|
self.client.force_login(self.superuser)
|
|
# Create a test portfolio permission
|
|
self.permission = UserPortfolioPermission.objects.create(
|
|
user=self.testuser, portfolio=self.portfolio, roles=["organization_member"]
|
|
)
|
|
delete_url = reverse("admin:registrar_userportfoliopermission_delete", args=[self.permission.pk])
|
|
response = self.client.get(delete_url)
|
|
|
|
# Check if the response contains the expected static message
|
|
expected_message = "If you remove someone from a portfolio here, it will not send any emails"
|
|
self.assertIn(expected_message, response.content.decode("utf-8"))
|
|
|
|
|
|
class TestPortfolioInvitationAdmin(TestCase):
|
|
"""Tests for the PortfolioInvitationAdmin class as super user
|
|
|
|
Notes:
|
|
all tests share superuser; do not change this model in tests
|
|
tests have available superuser, client, and admin
|
|
"""
|
|
|
|
@classmethod
|
|
def setUpClass(cls):
|
|
cls.factory = RequestFactory()
|
|
cls.admin = ListHeaderAdmin(model=PortfolioInvitationAdmin, admin_site=AdminSite())
|
|
cls.superuser = create_superuser()
|
|
|
|
def setUp(self):
|
|
"""Create a client object"""
|
|
self.client = Client(HTTP_HOST="localhost:8080")
|
|
self.portfolio = Portfolio.objects.create(organization_name="Test Portfolio", creator=self.superuser)
|
|
|
|
def tearDown(self):
|
|
"""Delete all DomainInvitation objects"""
|
|
Portfolio.objects.all().delete()
|
|
PortfolioInvitation.objects.all().delete()
|
|
Contact.objects.all().delete()
|
|
User.objects.all().delete()
|
|
|
|
@classmethod
|
|
def tearDownClass(self):
|
|
User.objects.all().delete()
|
|
|
|
@less_console_noise_decorator
|
|
def test_has_model_description(self):
|
|
"""Tests if this model has a model description on the table view"""
|
|
self.client.force_login(self.superuser)
|
|
response = self.client.get(
|
|
"/admin/registrar/portfolioinvitation/",
|
|
follow=True,
|
|
)
|
|
|
|
# Make sure that the page is loaded correctly
|
|
self.assertEqual(response.status_code, 200)
|
|
|
|
# Test for a description snippet
|
|
self.assertContains(
|
|
response,
|
|
"Portfolio invitations contain all individuals who have been invited to become members of an organization.",
|
|
)
|
|
self.assertContains(response, "Show more")
|
|
|
|
@less_console_noise_decorator
|
|
def test_has_change_form_description(self):
|
|
"""Tests if this model has a model description on the change form view"""
|
|
self.client.force_login(self.superuser)
|
|
|
|
invitation, _ = PortfolioInvitation.objects.get_or_create(
|
|
email=self.superuser.email, portfolio=self.portfolio, roles=[UserPortfolioRoleChoices.ORGANIZATION_ADMIN]
|
|
)
|
|
|
|
response = self.client.get(
|
|
"/admin/registrar/portfolioinvitation/{}/change/".format(invitation.pk),
|
|
follow=True,
|
|
)
|
|
|
|
# Make sure that the page is loaded correctly
|
|
self.assertEqual(response.status_code, 200)
|
|
|
|
# Test for a description snippet
|
|
self.assertContains(
|
|
response,
|
|
"If you add someone to a portfolio here, it will trigger an invitation email when you click",
|
|
)
|
|
|
|
@less_console_noise_decorator
|
|
def test_get_filters(self):
|
|
"""Ensures that our filters are displaying correctly"""
|
|
self.client.force_login(self.superuser)
|
|
|
|
response = self.client.get(
|
|
"/admin/registrar/portfolioinvitation/",
|
|
{},
|
|
follow=True,
|
|
)
|
|
|
|
# Assert that the filters are added
|
|
self.assertContains(response, "invited", count=5)
|
|
self.assertContains(response, "Invited", count=2)
|
|
self.assertContains(response, "retrieved", count=3)
|
|
self.assertContains(response, "Retrieved", count=2)
|
|
|
|
# Check for the HTML context specificially
|
|
invited_html = '<a id="status-filter-invited" href="?status__exact=invited">Invited</a>'
|
|
retrieved_html = '<a id="status-filter-retrieved" href="?status__exact=retrieved">Retrieved</a>'
|
|
|
|
self.assertContains(response, invited_html, count=1)
|
|
self.assertContains(response, retrieved_html, count=1)
|
|
|
|
@less_console_noise_decorator
|
|
@patch("registrar.admin.send_portfolio_invitation_email")
|
|
@patch("django.contrib.messages.success") # Mock the `messages.success` call
|
|
def test_save_sends_email(self, mock_messages_success, mock_send_email):
|
|
"""On save_model, an email is sent if an invitation already exists."""
|
|
|
|
# Create an instance of the admin class
|
|
admin_instance = PortfolioInvitationAdmin(PortfolioInvitation, admin_site=None)
|
|
|
|
# Create a PortfolioInvitation instance
|
|
portfolio_invitation = PortfolioInvitation(
|
|
email="james.gordon@gotham.gov",
|
|
portfolio=self.portfolio,
|
|
roles=[UserPortfolioRoleChoices.ORGANIZATION_ADMIN],
|
|
)
|
|
|
|
# Create a request object
|
|
request = self.factory.post("/admin/registrar/PortfolioInvitation/add/")
|
|
request.user = self.superuser
|
|
|
|
# Call the save_model method
|
|
admin_instance.save_model(request, portfolio_invitation, None, None)
|
|
|
|
# Assert that send_portfolio_invitation_email is called
|
|
mock_send_email.assert_called()
|
|
|
|
# Get the arguments passed to send_portfolio_invitation_email
|
|
_, called_kwargs = mock_send_email.call_args
|
|
|
|
# Assert the email content
|
|
self.assertEqual(called_kwargs["email"], "james.gordon@gotham.gov")
|
|
self.assertEqual(called_kwargs["requestor"], self.superuser)
|
|
self.assertEqual(called_kwargs["portfolio"], self.portfolio)
|
|
|
|
# Assert that a warning message was triggered
|
|
mock_messages_success.assert_called_once_with(request, "james.gordon@gotham.gov has been invited.")
|
|
|
|
@less_console_noise_decorator
|
|
@patch("registrar.admin.send_portfolio_invitation_email")
|
|
@patch("django.contrib.messages.warning") # Mock the `messages.warning` call
|
|
def test_save_does_not_send_email_if_requested_user_exists(self, mock_messages_warning, mock_send_email):
|
|
"""On save_model, an email is NOT sent if an the requested email belongs to an existing user.
|
|
It also throws a warning."""
|
|
self.client.force_login(self.superuser)
|
|
|
|
# Create an instance of the admin class
|
|
admin_instance = PortfolioInvitationAdmin(PortfolioInvitation, admin_site=None)
|
|
|
|
# Mock the UserPortfolioPermission query to simulate the invitation already existing
|
|
existing_user = create_user()
|
|
UserPortfolioPermission.objects.create(user=existing_user, portfolio=self.portfolio)
|
|
|
|
# Create a PortfolioInvitation instance
|
|
portfolio_invitation = PortfolioInvitation(
|
|
email=existing_user.email,
|
|
portfolio=self.portfolio,
|
|
roles=[UserPortfolioRoleChoices.ORGANIZATION_ADMIN],
|
|
)
|
|
|
|
# Create a request object
|
|
request = self.factory.post("/admin/registrar/PortfolioInvitation/add/")
|
|
request.user = self.superuser
|
|
|
|
# Call the save_model method
|
|
admin_instance.save_model(request, portfolio_invitation, None, None)
|
|
|
|
# Assert that send_portfolio_invitation_email is not called
|
|
mock_send_email.assert_not_called()
|
|
|
|
# Assert that a warning message was triggered
|
|
mock_messages_warning.assert_called_once_with(request, "User is already a member of this portfolio.")
|
|
|
|
@less_console_noise_decorator
|
|
@patch("registrar.admin.send_portfolio_invitation_email")
|
|
@patch("django.contrib.messages.success") # Mock the `messages.warning` call
|
|
def test_add_portfolio_invitation_auto_retrieves_invitation_when_user_exists(
|
|
self, mock_messages_success, mock_send_email
|
|
):
|
|
"""On save_model, we create and retrieve a portfolio invitation if the user exists."""
|
|
|
|
# Create an instance of the admin class
|
|
admin_instance = PortfolioInvitationAdmin(PortfolioInvitation, admin_site=None)
|
|
|
|
User.objects.create_user(email="james.gordon@gotham.gov", username="username")
|
|
|
|
# Create a PortfolioInvitation instance
|
|
portfolio_invitation = PortfolioInvitation(
|
|
email="james.gordon@gotham.gov",
|
|
portfolio=self.portfolio,
|
|
roles=[UserPortfolioRoleChoices.ORGANIZATION_ADMIN],
|
|
)
|
|
|
|
# Create a request object
|
|
request = self.factory.post("/admin/registrar/PortfolioInvitation/add/")
|
|
request.user = self.superuser
|
|
|
|
# Call the save_model method
|
|
with patch.object(PortfolioInvitation, "retrieve") as portfolio_invitation_mock_retrieve:
|
|
admin_instance.save_model(request, portfolio_invitation, None, None)
|
|
|
|
# Assert that send_portfolio_invitation_email is called
|
|
mock_send_email.assert_called()
|
|
|
|
# Get the arguments passed to send_portfolio_invitation_email
|
|
_, called_kwargs = mock_send_email.call_args
|
|
|
|
# Assert the email content
|
|
self.assertEqual(called_kwargs["email"], "james.gordon@gotham.gov")
|
|
self.assertEqual(called_kwargs["requestor"], self.superuser)
|
|
self.assertEqual(called_kwargs["portfolio"], self.portfolio)
|
|
|
|
# Assert that a warning message was triggered
|
|
mock_messages_success.assert_called_once_with(request, "james.gordon@gotham.gov has been invited.")
|
|
|
|
# The invitation is not retrieved
|
|
portfolio_invitation_mock_retrieve.assert_called_once()
|
|
|
|
@less_console_noise_decorator
|
|
@patch("registrar.admin.send_portfolio_invitation_email")
|
|
@patch("django.contrib.messages.success") # Mock the `messages.warning` call
|
|
def test_add_portfolio_invitation_does_not_retrieve_invitation_when_no_user(
|
|
self, mock_messages_success, mock_send_email
|
|
):
|
|
"""On save_model, we create but do not retrieve a portfolio invitation if the user does not exist."""
|
|
|
|
# Create an instance of the admin class
|
|
admin_instance = PortfolioInvitationAdmin(PortfolioInvitation, admin_site=None)
|
|
|
|
# Create a PortfolioInvitation instance
|
|
portfolio_invitation = PortfolioInvitation(
|
|
email="james.gordon@gotham.gov",
|
|
portfolio=self.portfolio,
|
|
roles=[UserPortfolioRoleChoices.ORGANIZATION_ADMIN],
|
|
)
|
|
|
|
# Create a request object
|
|
request = self.factory.post("/admin/registrar/PortfolioInvitation/add/")
|
|
request.user = self.superuser
|
|
|
|
# Call the save_model method
|
|
with patch.object(PortfolioInvitation, "retrieve") as portfolio_invitation_mock_retrieve:
|
|
admin_instance.save_model(request, portfolio_invitation, None, None)
|
|
|
|
# Assert that send_portfolio_invitation_email is called
|
|
mock_send_email.assert_called()
|
|
|
|
# Get the arguments passed to send_portfolio_invitation_email
|
|
_, called_kwargs = mock_send_email.call_args
|
|
|
|
# Assert the email content
|
|
self.assertEqual(called_kwargs["email"], "james.gordon@gotham.gov")
|
|
self.assertEqual(called_kwargs["requestor"], self.superuser)
|
|
self.assertEqual(called_kwargs["portfolio"], self.portfolio)
|
|
|
|
# Assert that a warning message was triggered
|
|
mock_messages_success.assert_called_once_with(request, "james.gordon@gotham.gov has been invited.")
|
|
|
|
# The invitation is not retrieved
|
|
portfolio_invitation_mock_retrieve.assert_not_called()
|
|
|
|
@less_console_noise_decorator
|
|
@patch("registrar.admin.send_portfolio_invitation_email")
|
|
@patch("django.contrib.messages.error") # Mock the `messages.error` call
|
|
def test_save_exception_email_sending_error(self, mock_messages_error, mock_send_email):
|
|
"""Handle EmailSendingError correctly when sending the portfolio invitation fails."""
|
|
self.client.force_login(self.superuser)
|
|
|
|
# Mock the email sending function to raise EmailSendingError
|
|
mock_send_email.side_effect = EmailSendingError("Email service unavailable")
|
|
|
|
# Create an instance of the admin class
|
|
admin_instance = PortfolioInvitationAdmin(PortfolioInvitation, admin_site=None)
|
|
|
|
# Create a PortfolioInvitation instance
|
|
portfolio_invitation = PortfolioInvitation(
|
|
email="james.gordon@gotham.gov",
|
|
portfolio=self.portfolio,
|
|
roles=[UserPortfolioRoleChoices.ORGANIZATION_ADMIN],
|
|
)
|
|
|
|
# Create a request object
|
|
request = self.factory.post("/admin/registrar/PortfolioInvitation/add/")
|
|
request.user = self.superuser
|
|
|
|
# Call the save_model method
|
|
admin_instance.save_model(request, portfolio_invitation, None, None)
|
|
|
|
# Assert that messages.error was called with the correct message
|
|
mock_messages_error.assert_called_once_with(request, "Email service unavailable")
|
|
|
|
@less_console_noise_decorator
|
|
@patch("registrar.admin.send_portfolio_invitation_email")
|
|
@patch("django.contrib.messages.error") # Mock the `messages.error` call
|
|
def test_save_exception_missing_email_error(self, mock_messages_error, mock_send_email):
|
|
"""Handle MissingEmailError correctly when no email exists for the requestor."""
|
|
self.client.force_login(self.superuser)
|
|
|
|
# Mock the email sending function to raise MissingEmailError
|
|
mock_send_email.side_effect = MissingEmailError()
|
|
|
|
# Create an instance of the admin class
|
|
admin_instance = PortfolioInvitationAdmin(PortfolioInvitation, admin_site=None)
|
|
|
|
# Create a PortfolioInvitation instance
|
|
portfolio_invitation = PortfolioInvitation(
|
|
email="james.gordon@gotham.gov",
|
|
portfolio=self.portfolio,
|
|
roles=[UserPortfolioRoleChoices.ORGANIZATION_ADMIN],
|
|
)
|
|
|
|
# Create a request object
|
|
request = self.factory.post("/admin/registrar/PortfolioInvitation/add/")
|
|
request.user = self.superuser
|
|
|
|
# Call the save_model method
|
|
admin_instance.save_model(request, portfolio_invitation, None, None)
|
|
|
|
# Assert that messages.error was called with the correct message
|
|
mock_messages_error.assert_called_once_with(
|
|
request,
|
|
"Can't send invitation email. No email is associated with your user account.",
|
|
)
|
|
|
|
@less_console_noise_decorator
|
|
@patch("registrar.admin.send_portfolio_invitation_email")
|
|
@patch("django.contrib.messages.error") # Mock the `messages.error` call
|
|
def test_save_exception_generic_error(self, mock_messages_error, mock_send_email):
|
|
"""Handle generic exceptions correctly during portfolio invitation."""
|
|
self.client.force_login(self.superuser)
|
|
|
|
# Mock the email sending function to raise a generic exception
|
|
mock_send_email.side_effect = Exception("Unexpected error")
|
|
|
|
# Create an instance of the admin class
|
|
admin_instance = PortfolioInvitationAdmin(PortfolioInvitation, admin_site=None)
|
|
|
|
# Create a PortfolioInvitation instance
|
|
portfolio_invitation = PortfolioInvitation(
|
|
email="james.gordon@gotham.gov",
|
|
portfolio=self.portfolio,
|
|
roles=[UserPortfolioRoleChoices.ORGANIZATION_ADMIN],
|
|
)
|
|
|
|
# Create a request object
|
|
request = self.factory.post("/admin/registrar/PortfolioInvitation/add/")
|
|
request.user = self.superuser
|
|
|
|
# Call the save_model method
|
|
admin_instance.save_model(request, portfolio_invitation, None, None)
|
|
|
|
# Assert that messages.error was called with the correct message
|
|
mock_messages_error.assert_called_once_with(request, "Could not send email invitation.")
|
|
|
|
@less_console_noise_decorator
|
|
@patch("registrar.admin.send_portfolio_admin_addition_emails")
|
|
def test_save_existing_sends_email_notification(self, mock_send_email):
|
|
"""On save_model to an existing invitation, an email is set to notify existing
|
|
admins, if the invitation changes from member to admin."""
|
|
|
|
# Create an instance of the admin class
|
|
admin_instance = PortfolioInvitationAdmin(PortfolioInvitation, admin_site=None)
|
|
|
|
# Mock the response value of the email send
|
|
mock_send_email.return_value = True
|
|
|
|
# Create and save a PortfolioInvitation instance
|
|
portfolio_invitation = PortfolioInvitation.objects.create(
|
|
email="james.gordon@gotham.gov",
|
|
portfolio=self.portfolio,
|
|
roles=[UserPortfolioRoleChoices.ORGANIZATION_MEMBER], # Initially NOT an admin
|
|
status=PortfolioInvitation.PortfolioInvitationStatus.INVITED, # Must be "INVITED"
|
|
)
|
|
|
|
# Create a request object
|
|
request = self.factory.post(f"/admin/registrar/PortfolioInvitation/{portfolio_invitation.pk}/change/")
|
|
request.user = self.superuser
|
|
|
|
# Change roles from MEMBER to ADMIN
|
|
portfolio_invitation.roles = [UserPortfolioRoleChoices.ORGANIZATION_ADMIN]
|
|
|
|
# Call the save_model method
|
|
admin_instance.save_model(request, portfolio_invitation, None, True)
|
|
|
|
# Assert that send_portfolio_admin_addition_emails is called
|
|
mock_send_email.assert_called_once()
|
|
|
|
# Get the arguments passed to send_portfolio_admin_addition_emails
|
|
_, called_kwargs = mock_send_email.call_args
|
|
|
|
# Assert the email content
|
|
self.assertEqual(called_kwargs["email"], "james.gordon@gotham.gov")
|
|
self.assertEqual(called_kwargs["requestor"], self.superuser)
|
|
self.assertEqual(called_kwargs["portfolio"], self.portfolio)
|
|
|
|
@less_console_noise_decorator
|
|
@patch("registrar.admin.send_portfolio_admin_addition_emails")
|
|
@patch("django.contrib.messages.warning") # Mock the `messages.warning` call
|
|
def test_save_existing_email_notification_warning(self, mock_messages_warning, mock_send_email):
|
|
"""On save_model for an existing invitation, a warning is displayed if method to
|
|
send email to notify admins returns False."""
|
|
|
|
# Create an instance of the admin class
|
|
admin_instance = PortfolioInvitationAdmin(PortfolioInvitation, admin_site=None)
|
|
|
|
# Mock the response value of the email send
|
|
mock_send_email.return_value = False
|
|
|
|
# Create and save a PortfolioInvitation instance
|
|
portfolio_invitation = PortfolioInvitation.objects.create(
|
|
email="james.gordon@gotham.gov",
|
|
portfolio=self.portfolio,
|
|
roles=[UserPortfolioRoleChoices.ORGANIZATION_MEMBER], # Initially NOT an admin
|
|
status=PortfolioInvitation.PortfolioInvitationStatus.INVITED, # Must be "INVITED"
|
|
)
|
|
|
|
# Create a request object
|
|
request = self.factory.post(f"/admin/registrar/PortfolioInvitation/{portfolio_invitation.pk}/change/")
|
|
request.user = self.superuser
|
|
|
|
# Change roles from MEMBER to ADMIN
|
|
portfolio_invitation.roles = [UserPortfolioRoleChoices.ORGANIZATION_ADMIN]
|
|
|
|
# Call the save_model method
|
|
admin_instance.save_model(request, portfolio_invitation, None, True)
|
|
|
|
# Assert that send_portfolio_admin_addition_emails is called
|
|
mock_send_email.assert_called_once()
|
|
|
|
# Get the arguments passed to send_portfolio_admin_addition_emails
|
|
_, called_kwargs = mock_send_email.call_args
|
|
|
|
# Assert the email content
|
|
self.assertEqual(called_kwargs["email"], "james.gordon@gotham.gov")
|
|
self.assertEqual(called_kwargs["requestor"], self.superuser)
|
|
self.assertEqual(called_kwargs["portfolio"], self.portfolio)
|
|
|
|
# Assert that messages.error was called with the correct message
|
|
mock_messages_warning.assert_called_once_with(
|
|
request, "Could not send email notification to existing organization admins."
|
|
)
|
|
|
|
@less_console_noise_decorator
|
|
def test_delete_confirmation_page_contains_static_message(self):
|
|
"""Ensure the custom message appears in the delete confirmation page."""
|
|
self.client.force_login(self.superuser)
|
|
# Create a test portfolio invitation
|
|
self.invitation = PortfolioInvitation.objects.create(
|
|
email="testuser@example.com", portfolio=self.portfolio, roles=["organization_member"]
|
|
)
|
|
delete_url = reverse("admin:registrar_portfolioinvitation_delete", args=[self.invitation.pk])
|
|
response = self.client.get(delete_url)
|
|
|
|
# Check if the response contains the expected static message
|
|
expected_message = "If you cancel the portfolio invitation here"
|
|
self.assertIn(expected_message, response.content.decode("utf-8"))
|
|
|
|
|
|
class TestHostAdmin(TestCase):
|
|
"""Tests for the HostAdmin class as super user
|
|
|
|
Notes:
|
|
all tests share superuser; do not change this model in tests
|
|
tests have available superuser, client, and admin
|
|
"""
|
|
|
|
@classmethod
|
|
def setUpClass(cls):
|
|
cls.site = AdminSite()
|
|
cls.factory = RequestFactory()
|
|
cls.admin = MyHostAdmin(model=Host, admin_site=cls.site)
|
|
cls.superuser = create_superuser()
|
|
|
|
def setUp(self):
|
|
"""Setup environment for a mock admin user"""
|
|
super().setUp()
|
|
self.client = Client(HTTP_HOST="localhost:8080")
|
|
|
|
def tearDown(self):
|
|
super().tearDown()
|
|
Host.objects.all().delete()
|
|
Domain.objects.all().delete()
|
|
|
|
@classmethod
|
|
def tearDownClass(cls):
|
|
User.objects.all().delete()
|
|
|
|
@less_console_noise_decorator
|
|
def test_has_model_description(self):
|
|
"""Tests if this model has a model description on the table view"""
|
|
self.client.force_login(self.superuser)
|
|
response = self.client.get(
|
|
"/admin/registrar/host/",
|
|
follow=True,
|
|
)
|
|
|
|
# Make sure that the page is loaded correctly
|
|
self.assertEqual(response.status_code, 200)
|
|
|
|
# Test for a description snippet
|
|
self.assertContains(response, "Entries in the Hosts table indicate the relationship between an approved domain")
|
|
self.assertContains(response, "Show more")
|
|
|
|
@less_console_noise_decorator
|
|
def test_helper_text(self):
|
|
"""
|
|
Tests for the correct helper text on this page
|
|
"""
|
|
domain, _ = Domain.objects.get_or_create(name="fake.gov", state=Domain.State.READY)
|
|
# Create a fake host
|
|
host, _ = Host.objects.get_or_create(name="ns1.test.gov", domain=domain)
|
|
|
|
self.client.force_login(self.superuser)
|
|
response = self.client.get(
|
|
"/admin/registrar/host/{}/change/".format(host.pk),
|
|
follow=True,
|
|
)
|
|
|
|
# Make sure the page loaded
|
|
self.assertEqual(response.status_code, 200)
|
|
|
|
self.test_helper = GenericTestHelper(
|
|
factory=self.factory,
|
|
user=self.superuser,
|
|
admin=self.admin,
|
|
url="/admin/registrar/Host/",
|
|
model=Host,
|
|
)
|
|
# These should exist in the response
|
|
expected_values = [
|
|
("domain", "Domain associated with this host"),
|
|
]
|
|
self.test_helper.assert_response_contains_distinct_values(response, expected_values)
|
|
|
|
|
|
class TestDomainInformationAdmin(TestCase):
|
|
"""Tests for the DomainInformationAdmin class as super or staff user
|
|
|
|
Notes:
|
|
all tests share superuser/staffuser; do not change these models in tests
|
|
tests have available staffuser, superuser, client, test_helper and admin
|
|
"""
|
|
|
|
@classmethod
|
|
def setUpClass(cls):
|
|
"""Setup environment for a mock admin user"""
|
|
cls.site = AdminSite()
|
|
cls.factory = RequestFactory()
|
|
cls.admin = DomainInformationAdmin(model=DomainInformation, admin_site=cls.site)
|
|
cls.superuser = create_superuser()
|
|
cls.staffuser = create_user()
|
|
cls.mock_data_generator = AuditedAdminMockData()
|
|
cls.test_helper = GenericTestHelper(
|
|
factory=cls.factory,
|
|
user=cls.superuser,
|
|
admin=cls.admin,
|
|
url="/admin/registrar/DomainInformation/",
|
|
model=DomainInformation,
|
|
)
|
|
|
|
def setUp(self):
|
|
self.client = Client(HTTP_HOST="localhost:8080")
|
|
|
|
def tearDown(self):
|
|
"""Delete all Users, Domains, and UserDomainRoles"""
|
|
DomainInformation.objects.all().delete()
|
|
DomainRequest.objects.all().delete()
|
|
Domain.objects.all().delete()
|
|
Contact.objects.all().delete()
|
|
|
|
@classmethod
|
|
def tearDownClass(cls):
|
|
User.objects.all().delete()
|
|
SeniorOfficial.objects.all().delete()
|
|
|
|
@less_console_noise_decorator
|
|
def test_domain_information_senior_official_is_alphabetically_sorted(self):
|
|
"""Tests if the senior offical dropdown is alphanetically sorted in the django admin display"""
|
|
|
|
SeniorOfficial.objects.get_or_create(first_name="mary", last_name="joe", title="some other guy")
|
|
SeniorOfficial.objects.get_or_create(first_name="alex", last_name="smoe", title="some guy")
|
|
SeniorOfficial.objects.get_or_create(first_name="Zoup", last_name="Soup", title="title")
|
|
|
|
contact, _ = Contact.objects.get_or_create(first_name="Henry", last_name="McFakerson")
|
|
domain_request = completed_domain_request(
|
|
name="city1244.gov", status=DomainRequest.DomainRequestStatus.IN_REVIEW
|
|
)
|
|
domain_request.approve()
|
|
|
|
domain_info = DomainInformation.objects.get(domain_request=domain_request)
|
|
request = self.factory.post("/admin/registrar/domaininformation/{}/change/".format(domain_info.pk))
|
|
model_admin = AuditedAdmin(DomainInformation, self.site)
|
|
|
|
# Get the queryset that would be returned for the list
|
|
senior_offical_queryset = model_admin.formfield_for_foreignkey(
|
|
DomainInformation.senior_official.field, request
|
|
).queryset
|
|
|
|
# Make the list we're comparing on a bit prettier display-wise. Optional step.
|
|
current_sort_order = []
|
|
for official in senior_offical_queryset:
|
|
current_sort_order.append(f"{official.first_name} {official.last_name}")
|
|
|
|
expected_sort_order = ["alex smoe", "mary joe", "Zoup Soup"]
|
|
|
|
self.assertEqual(current_sort_order, expected_sort_order)
|
|
|
|
@less_console_noise_decorator
|
|
def test_admin_can_see_cisa_region_federal(self):
|
|
"""Tests if admins can see CISA Region: N/A"""
|
|
|
|
# Create a fake domain request
|
|
_domain_request = completed_domain_request(status=DomainRequest.DomainRequestStatus.IN_REVIEW)
|
|
_domain_request.approve()
|
|
|
|
domain_information = DomainInformation.objects.filter(domain_request=_domain_request).get()
|
|
|
|
self.client.force_login(self.superuser)
|
|
response = self.client.get(
|
|
"/admin/registrar/domaininformation/{}/change/".format(domain_information.pk),
|
|
follow=True,
|
|
)
|
|
|
|
# Make sure the page loaded, and that we're on the right page
|
|
self.assertEqual(response.status_code, 200)
|
|
self.assertContains(response, domain_information.domain.name)
|
|
|
|
# Test if the page has the right CISA region
|
|
expected_html = '<div class="flex-container margin-top-2"><span>CISA region: N/A</span></div>'
|
|
# Remove whitespace from expected_html
|
|
expected_html = "".join(expected_html.split())
|
|
|
|
# Remove whitespace from response content
|
|
response_content = "".join(response.content.decode().split())
|
|
|
|
# Check if response contains expected_html
|
|
self.assertIn(expected_html, response_content)
|
|
|
|
@less_console_noise_decorator
|
|
def test_admin_can_see_cisa_region_non_federal(self):
|
|
"""Tests if admins can see the correct CISA region"""
|
|
|
|
# Create a fake domain request. State will be NY (2).
|
|
_domain_request = completed_domain_request(
|
|
status=DomainRequest.DomainRequestStatus.IN_REVIEW, generic_org_type="interstate"
|
|
)
|
|
_domain_request.approve()
|
|
|
|
domain_information = DomainInformation.objects.filter(domain_request=_domain_request).get()
|
|
self.client.force_login(self.superuser)
|
|
response = self.client.get(
|
|
"/admin/registrar/domaininformation/{}/change/".format(domain_information.pk),
|
|
follow=True,
|
|
)
|
|
|
|
# Make sure the page loaded, and that we're on the right page
|
|
self.assertEqual(response.status_code, 200)
|
|
self.assertContains(response, domain_information.domain.name)
|
|
|
|
# Test if the page has the right CISA region
|
|
expected_html = '<div class="flex-container margin-top-2"><span>CISA region: 2</span></div>'
|
|
# Remove whitespace from expected_html
|
|
expected_html = "".join(expected_html.split())
|
|
|
|
# Remove whitespace from response content
|
|
response_content = "".join(response.content.decode().split())
|
|
|
|
# Check if response contains expected_html
|
|
self.assertIn(expected_html, response_content)
|
|
|
|
@less_console_noise_decorator
|
|
def test_has_model_description(self):
|
|
"""Tests if this model has a model description on the table view"""
|
|
self.client.force_login(self.superuser)
|
|
response = self.client.get(
|
|
"/admin/registrar/domaininformation/",
|
|
follow=True,
|
|
)
|
|
|
|
# Make sure that the page is loaded correctly
|
|
self.assertEqual(response.status_code, 200)
|
|
|
|
# Test for a description snippet
|
|
self.assertContains(response, "Domain information represents the basic metadata")
|
|
self.assertContains(response, "Show more")
|
|
|
|
@less_console_noise_decorator
|
|
def test_helper_text(self):
|
|
"""
|
|
Tests for the correct helper text on this page
|
|
"""
|
|
|
|
# Create a fake domain request and domain
|
|
domain_request = completed_domain_request(status=DomainRequest.DomainRequestStatus.IN_REVIEW)
|
|
domain_request.approve()
|
|
domain_info = DomainInformation.objects.filter(domain=domain_request.approved_domain).get()
|
|
|
|
self.client.force_login(self.superuser)
|
|
response = self.client.get(
|
|
"/admin/registrar/domaininformation/{}/change/".format(domain_info.pk),
|
|
follow=True,
|
|
)
|
|
|
|
# Make sure the page loaded, and that we're on the right page
|
|
self.assertEqual(response.status_code, 200)
|
|
self.assertContains(response, domain_info.domain.name)
|
|
|
|
# These should exist in the response
|
|
expected_values = [
|
|
("creator", "Person who submitted the domain request"),
|
|
("domain_request", "Request associated with this domain"),
|
|
("no_other_contacts_rationale", "Required if creator does not list other employees"),
|
|
("urbanization", "Required for Puerto Rico only"),
|
|
]
|
|
self.test_helper.assert_response_contains_distinct_values(response, expected_values)
|
|
|
|
@less_console_noise_decorator
|
|
def test_other_contacts_has_readonly_link(self):
|
|
"""Tests if the readonly other_contacts field has links"""
|
|
|
|
# Create a fake domain request and domain
|
|
domain_request = completed_domain_request(status=DomainRequest.DomainRequestStatus.IN_REVIEW)
|
|
domain_request.approve()
|
|
domain_info = DomainInformation.objects.filter(domain=domain_request.approved_domain).get()
|
|
|
|
# Get the other contact
|
|
other_contact = domain_info.other_contacts.all().first()
|
|
|
|
self.client.force_login(self.superuser)
|
|
|
|
response = self.client.get(
|
|
"/admin/registrar/domaininformation/{}/change/".format(domain_info.pk),
|
|
follow=True,
|
|
)
|
|
|
|
# Make sure the page loaded, and that we're on the right page
|
|
self.assertEqual(response.status_code, 200)
|
|
self.assertContains(response, domain_info.domain.name)
|
|
|
|
# Check that the page contains the url we expect
|
|
expected_href = reverse("admin:registrar_contact_change", args=[other_contact.id])
|
|
self.assertContains(response, expected_href)
|
|
|
|
# Check that the page contains the link we expect.
|
|
# Since the url is dynamic (populated by JS), we can test for its existence
|
|
# by checking for the end tag.
|
|
expected_url = "Testy Tester</a>"
|
|
self.assertContains(response, expected_url)
|
|
|
|
@less_console_noise_decorator
|
|
def test_analyst_cant_access_domain_information(self):
|
|
"""Ensures that analysts can't directly access the DomainInformation page through /admin"""
|
|
# Create fake creator
|
|
_creator = User.objects.create(
|
|
username="MrMeoward",
|
|
first_name="Meoward",
|
|
last_name="Jones",
|
|
)
|
|
|
|
# Create a fake domain request
|
|
domain_request = completed_domain_request(status=DomainRequest.DomainRequestStatus.IN_REVIEW, user=_creator)
|
|
domain_request.approve()
|
|
domain_info = DomainInformation.objects.filter(domain=domain_request.approved_domain).get()
|
|
|
|
self.client.force_login(self.staffuser)
|
|
response = self.client.get(
|
|
"/admin/registrar/domaininformation/{}/change/".format(domain_info.pk),
|
|
follow=True,
|
|
)
|
|
|
|
# Make sure that we're denied access
|
|
self.assertEqual(response.status_code, 403)
|
|
|
|
# To make sure that its not a fluke, swap to an admin user
|
|
# and try to access the same page. This should succeed.
|
|
self.client.force_login(self.superuser)
|
|
response = self.client.get(
|
|
"/admin/registrar/domaininformation/{}/change/".format(domain_info.pk),
|
|
follow=True,
|
|
)
|
|
|
|
# Make sure the page loaded, and that we're on the right page
|
|
self.assertEqual(response.status_code, 200)
|
|
self.assertContains(response, domain_info.domain.name)
|
|
|
|
@less_console_noise_decorator
|
|
def test_contact_fields_have_detail_table(self):
|
|
"""Tests if the contact fields have the detail table which displays title, email, and phone"""
|
|
|
|
# Create fake creator
|
|
_creator = User.objects.create(
|
|
username="MrMeoward",
|
|
first_name="Meoward",
|
|
last_name="Jones",
|
|
email="meoward.jones@igorville.gov",
|
|
phone="(555) 123 12345",
|
|
title="Treat inspector",
|
|
)
|
|
|
|
# Create a fake domain request
|
|
domain_request = completed_domain_request(status=DomainRequest.DomainRequestStatus.IN_REVIEW, user=_creator)
|
|
domain_request.approve()
|
|
domain_info = DomainInformation.objects.filter(domain=domain_request.approved_domain).get()
|
|
|
|
self.client.force_login(self.superuser)
|
|
response = self.client.get(
|
|
"/admin/registrar/domaininformation/{}/change/".format(domain_info.pk),
|
|
follow=True,
|
|
)
|
|
|
|
# Make sure the page loaded, and that we're on the right page
|
|
self.assertEqual(response.status_code, 200)
|
|
self.assertContains(response, domain_info.domain.name)
|
|
|
|
# Check that the modal has the right content
|
|
# Check for the header
|
|
|
|
# == Check for the creator == #
|
|
|
|
# Check for the right title and phone number in the response.
|
|
# We only need to check for the end tag
|
|
# (Otherwise this test will fail if we change classes, etc)
|
|
expected_creator_fields = [
|
|
# Field, expected value
|
|
("title", "Treat inspector"),
|
|
("phone", "(555) 123 12345"),
|
|
]
|
|
self.test_helper.assert_response_contains_distinct_values(response, expected_creator_fields)
|
|
self.assertContains(response, "meoward.jones@igorville.gov")
|
|
|
|
# Check for the field itself
|
|
self.assertContains(response, "Meoward Jones")
|
|
|
|
# == Check for the senior_official == #
|
|
self.assertContains(response, "testy@town.com", count=2)
|
|
expected_so_fields = [
|
|
# Field, expected value
|
|
("title", "Chief Tester"),
|
|
("phone", "(555) 555 5555"),
|
|
]
|
|
self.test_helper.assert_response_contains_distinct_values(response, expected_so_fields)
|
|
|
|
self.assertContains(response, "Testy Tester", count=10)
|
|
|
|
# == Test the other_employees field == #
|
|
self.assertContains(response, "testy2@town.com", count=2)
|
|
expected_other_employees_fields = [
|
|
# Field, expected value
|
|
("title", "Another Tester"),
|
|
("phone", "(555) 555 5557"),
|
|
]
|
|
self.test_helper.assert_response_contains_distinct_values(response, expected_other_employees_fields)
|
|
|
|
# Test for the copy link
|
|
# We expect 4 in the form + 2 from the js module copy-to-clipboard.js
|
|
# that gets pulled in the test in django.contrib.staticfiles.finders.FileSystemFinder
|
|
self.assertContains(response, "copy-to-clipboard", count=6)
|
|
|
|
# cleanup this test
|
|
domain_info.delete()
|
|
domain_request.delete()
|
|
_creator.delete()
|
|
|
|
def test_readonly_fields_for_analyst(self):
|
|
"""Ensures that analysts have their permissions setup correctly"""
|
|
with less_console_noise():
|
|
request = self.factory.get("/")
|
|
request.user = self.staffuser
|
|
|
|
readonly_fields = self.admin.get_readonly_fields(request)
|
|
|
|
expected_fields = [
|
|
"portfolio_senior_official",
|
|
"portfolio_organization_type",
|
|
"portfolio_federal_type",
|
|
"portfolio_organization_name",
|
|
"portfolio_federal_agency",
|
|
"portfolio_state_territory",
|
|
"portfolio_address_line1",
|
|
"portfolio_address_line2",
|
|
"portfolio_city",
|
|
"portfolio_zipcode",
|
|
"portfolio_urbanization",
|
|
"other_contacts",
|
|
"is_election_board",
|
|
"federal_agency",
|
|
"creator",
|
|
"type_of_work",
|
|
"more_organization_information",
|
|
"domain",
|
|
"domain_request",
|
|
"no_other_contacts_rationale",
|
|
"anything_else",
|
|
"is_policy_acknowledged",
|
|
]
|
|
|
|
self.assertEqual(readonly_fields, expected_fields)
|
|
|
|
def test_domain_sortable(self):
|
|
"""Tests if DomainInformation sorts by domain correctly"""
|
|
with less_console_noise():
|
|
self.client.force_login(self.superuser)
|
|
|
|
# Assert that our sort works correctly
|
|
self.test_helper.assert_table_sorted("1", ("domain__name",))
|
|
|
|
# Assert that sorting in reverse works correctly
|
|
self.test_helper.assert_table_sorted("-1", ("-domain__name",))
|
|
|
|
def test_creator_sortable(self):
|
|
"""Tests if DomainInformation sorts by creator correctly"""
|
|
with less_console_noise():
|
|
self.client.force_login(self.superuser)
|
|
|
|
# Assert that our sort works correctly
|
|
self.test_helper.assert_table_sorted(
|
|
"4",
|
|
("creator__first_name", "creator__last_name"),
|
|
)
|
|
|
|
# Assert that sorting in reverse works correctly
|
|
self.test_helper.assert_table_sorted("-4", ("-creator__first_name", "-creator__last_name"))
|
|
|
|
|
|
class TestUserDomainRoleAdmin(WebTest):
|
|
"""Tests for the UserDomainRoleAdmin class as super user
|
|
|
|
Notes:
|
|
all tests share superuser; do not change this model in tests
|
|
tests have available superuser, client, test_helper and admin
|
|
"""
|
|
|
|
@classmethod
|
|
def setUpClass(cls):
|
|
super().setUpClass()
|
|
cls.site = AdminSite()
|
|
cls.factory = RequestFactory()
|
|
cls.admin = UserDomainRoleAdmin(model=UserDomainRole, admin_site=cls.site)
|
|
cls.superuser = create_superuser()
|
|
cls.test_helper = GenericTestHelper(
|
|
factory=cls.factory,
|
|
user=cls.superuser,
|
|
admin=cls.admin,
|
|
url="/admin/registrar/UserDomainRole/",
|
|
model=UserDomainRole,
|
|
)
|
|
|
|
def setUp(self):
|
|
"""Setup environment for a mock admin user"""
|
|
super().setUp()
|
|
self.client = Client(HTTP_HOST="localhost:8080")
|
|
self.client.force_login(self.superuser)
|
|
self.app.set_user(self.superuser.username)
|
|
|
|
def tearDown(self):
|
|
"""Delete all Users, Domains, and UserDomainRoles"""
|
|
super().tearDown()
|
|
UserDomainRole.objects.all().delete()
|
|
Domain.objects.all().delete()
|
|
User.objects.exclude(username="superuser").delete()
|
|
|
|
@classmethod
|
|
def tearDownClass(cls):
|
|
super().tearDownClass()
|
|
User.objects.all().delete()
|
|
|
|
@less_console_noise_decorator
|
|
def test_has_model_description(self):
|
|
"""Tests if this model has a model description on the table view"""
|
|
self.client.force_login(self.superuser)
|
|
response = self.client.get(
|
|
"/admin/registrar/userdomainrole/",
|
|
follow=True,
|
|
)
|
|
|
|
# Make sure that the page is loaded correctly
|
|
self.assertEqual(response.status_code, 200)
|
|
|
|
# Test for a description snippet
|
|
self.assertContains(
|
|
response, "This table represents the managers who are assigned to each domain in the registrar"
|
|
)
|
|
self.assertContains(response, "Show more")
|
|
|
|
@less_console_noise_decorator
|
|
def test_has_change_form_description(self):
|
|
"""Tests if this model has a model description on the change form view"""
|
|
self.client.force_login(self.superuser)
|
|
|
|
domain, _ = Domain.objects.get_or_create(name="systemofadown.com")
|
|
|
|
user_domain_role, _ = UserDomainRole.objects.get_or_create(
|
|
user=self.superuser, domain=domain, role=[UserDomainRole.Roles.MANAGER]
|
|
)
|
|
|
|
response = self.client.get(
|
|
"/admin/registrar/userdomainrole/{}/change/".format(user_domain_role.pk),
|
|
follow=True,
|
|
)
|
|
|
|
# Make sure that the page is loaded correctly
|
|
self.assertEqual(response.status_code, 200)
|
|
|
|
# Test for a description snippet
|
|
self.assertContains(
|
|
response,
|
|
"If you add someone to a domain here, it will not trigger any emails.",
|
|
)
|
|
|
|
def test_domain_sortable(self):
|
|
"""Tests if the UserDomainrole sorts by domain correctly"""
|
|
with less_console_noise():
|
|
self.client.force_login(self.superuser)
|
|
|
|
fake_user = User.objects.create(
|
|
username="dummyuser", first_name="Stewart", last_name="Jones", email="AntarcticPolarBears@example.com"
|
|
)
|
|
|
|
# Create a list of UserDomainRoles that are in random order
|
|
mocks_to_create = ["jkl.gov", "ghi.gov", "abc.gov", "def.gov"]
|
|
for name in mocks_to_create:
|
|
fake_domain = Domain.objects.create(name=name)
|
|
UserDomainRole.objects.create(user=fake_user, domain=fake_domain, role="manager")
|
|
|
|
# Assert that our sort works correctly
|
|
self.test_helper.assert_table_sorted("2", ("domain__name",))
|
|
|
|
# Assert that sorting in reverse works correctly
|
|
self.test_helper.assert_table_sorted("-2", ("-domain__name",))
|
|
|
|
def test_user_sortable(self):
|
|
"""Tests if the UserDomainrole sorts by user correctly"""
|
|
with less_console_noise():
|
|
self.client.force_login(self.superuser)
|
|
|
|
mock_data_generator = AuditedAdminMockData()
|
|
|
|
fake_domain = Domain.objects.create(name="igorville.gov")
|
|
# Create a list of UserDomainRoles that are in random order
|
|
mocks_to_create = ["jkl", "ghi", "abc", "def"]
|
|
for name in mocks_to_create:
|
|
# Creates a fake "User" object
|
|
fake_user = mock_data_generator.dummy_user(name, "user")
|
|
UserDomainRole.objects.create(user=fake_user, domain=fake_domain, role="manager")
|
|
|
|
# Assert that our sort works correctly
|
|
self.test_helper.assert_table_sorted("1", ("user__first_name", "user__last_name"))
|
|
|
|
# Assert that sorting in reverse works correctly
|
|
self.test_helper.assert_table_sorted("-1", ("-user__first_name", "-user__last_name"))
|
|
|
|
def test_email_not_in_search(self):
|
|
"""Tests the search bar in Django Admin for UserDomainRoleAdmin.
|
|
Should return no results for an invalid email."""
|
|
with less_console_noise():
|
|
# Have to get creative to get past linter
|
|
self.client.force_login(self.superuser)
|
|
|
|
fake_user = User.objects.create(
|
|
username="dummyuser", first_name="Stewart", last_name="Jones", email="AntarcticPolarBears@example.com"
|
|
)
|
|
fake_domain = Domain.objects.create(name="test123")
|
|
UserDomainRole.objects.create(user=fake_user, domain=fake_domain, role="manager")
|
|
# Make the request using the Client class
|
|
# which handles CSRF
|
|
# Follow=True handles the redirect
|
|
response = self.client.get(
|
|
"/admin/registrar/userdomainrole/",
|
|
{
|
|
"q": "testmail@igorville.com",
|
|
},
|
|
follow=True,
|
|
)
|
|
|
|
# Assert that the query is added to the extra_context
|
|
self.assertIn("search_query", response.context)
|
|
# Assert the content of filters and search_query
|
|
search_query = response.context["search_query"]
|
|
self.assertEqual(search_query, "testmail@igorville.com")
|
|
|
|
# We only need to check for the end of the HTML string
|
|
self.assertNotContains(response, "Stewart Jones AntarcticPolarBears@example.com</a></th>")
|
|
|
|
def test_email_in_search(self):
|
|
"""Tests the search bar in Django Admin for UserDomainRoleAdmin.
|
|
Should return results for an valid email."""
|
|
with less_console_noise():
|
|
# Have to get creative to get past linter
|
|
self.client.force_login(self.superuser)
|
|
|
|
fake_user = User.objects.create(
|
|
username="dummyuser", first_name="Joe", last_name="Jones", email="AntarcticPolarBears@example.com"
|
|
)
|
|
fake_domain = Domain.objects.create(name="fake")
|
|
UserDomainRole.objects.create(user=fake_user, domain=fake_domain, role="manager")
|
|
# Make the request using the Client class
|
|
# which handles CSRF
|
|
# Follow=True handles the redirect
|
|
response = self.client.get(
|
|
"/admin/registrar/userdomainrole/",
|
|
{
|
|
"q": "AntarcticPolarBears@example.com",
|
|
},
|
|
follow=True,
|
|
)
|
|
|
|
# Assert that the query is added to the extra_context
|
|
self.assertIn("search_query", response.context)
|
|
|
|
search_query = response.context["search_query"]
|
|
self.assertEqual(search_query, "AntarcticPolarBears@example.com")
|
|
|
|
# We only need to check for the end of the HTML string
|
|
self.assertContains(response, "Joe Jones AntarcticPolarBears@example.com</a></th>", count=1)
|
|
|
|
@less_console_noise_decorator
|
|
def test_custom_delete_confirmation_page(self):
|
|
"""Tests if custom alerts display on User Domain Role delete page"""
|
|
domain, _ = Domain.objects.get_or_create(name="user-domain-role-test.gov", state=Domain.State.READY)
|
|
domain_role, _ = UserDomainRole.objects.get_or_create(domain=domain, user=self.superuser)
|
|
|
|
domain_invitation_change_page = self.app.get(
|
|
reverse("admin:registrar_userdomainrole_change", args=[domain_role.pk])
|
|
)
|
|
|
|
self.assertContains(domain_invitation_change_page, "user-domain-role-test.gov")
|
|
# click the "Delete" link
|
|
confirmation_page = domain_invitation_change_page.click("Delete", index=0)
|
|
|
|
custom_alert_content = "If you remove someone from a domain here"
|
|
self.assertContains(confirmation_page, custom_alert_content)
|
|
|
|
@less_console_noise_decorator
|
|
def test_custom_selected_delete_confirmation_page(self):
|
|
"""Tests if custom alerts display on selected delete page from User Domain Roles table"""
|
|
domain, _ = Domain.objects.get_or_create(name="domain-invitation-test.gov", state=Domain.State.READY)
|
|
domain_role, _ = UserDomainRole.objects.get_or_create(domain=domain, user=self.superuser)
|
|
|
|
# Get the index. The post expects the index to be encoded as a string
|
|
index = f"{domain_role.id}"
|
|
|
|
test_helper = GenericTestHelper(
|
|
factory=self.factory,
|
|
user=self.superuser,
|
|
admin=self.admin,
|
|
url=reverse("admin:registrar_userdomainrole_changelist"),
|
|
model=Domain,
|
|
client=self.client,
|
|
)
|
|
|
|
# Simulate selecting a single record, then clicking "Delete selected domains"
|
|
response = test_helper.get_table_delete_confirmation_page("0", index)
|
|
|
|
# Check for custom alert message
|
|
custom_alert_content = "If you remove someone from a domain here"
|
|
self.assertContains(response, custom_alert_content)
|
|
|
|
|
|
class TestListHeaderAdmin(TestCase):
|
|
"""Tests for the ListHeaderAdmin class as super user
|
|
|
|
Notes:
|
|
all tests share superuser; do not change this model in tests
|
|
tests have available superuser, client and admin
|
|
"""
|
|
|
|
@classmethod
|
|
def setUpClass(cls):
|
|
super().setUpClass()
|
|
cls.site = AdminSite()
|
|
cls.factory = RequestFactory()
|
|
cls.admin = ListHeaderAdmin(model=DomainRequest, admin_site=None)
|
|
cls.superuser = create_superuser()
|
|
|
|
def setUp(self):
|
|
super().setUp()
|
|
self.client = Client(HTTP_HOST="localhost:8080")
|
|
|
|
def tearDown(self):
|
|
# delete any domain requests too
|
|
DomainInformation.objects.all().delete()
|
|
DomainRequest.objects.all().delete()
|
|
|
|
@classmethod
|
|
def tearDownClass(cls):
|
|
super().tearDownClass()
|
|
User.objects.all().delete()
|
|
|
|
def test_changelist_view(self):
|
|
with less_console_noise():
|
|
self.client.force_login(self.superuser)
|
|
# Mock a user
|
|
user = mock_user()
|
|
# Make the request using the Client class
|
|
# which handles CSRF
|
|
# Follow=True handles the redirect
|
|
response = self.client.get(
|
|
"/admin/registrar/domainrequest/",
|
|
{
|
|
"status__exact": "started",
|
|
"investigator__id__exact": user.id,
|
|
"q": "Hello",
|
|
},
|
|
follow=True,
|
|
)
|
|
# Assert that the filters and search_query are added to the extra_context
|
|
self.assertIn("filters", response.context)
|
|
self.assertIn("search_query", response.context)
|
|
# Assert the content of filters and search_query
|
|
filters = response.context["filters"]
|
|
search_query = response.context["search_query"]
|
|
self.assertEqual(search_query, "Hello")
|
|
self.assertEqual(
|
|
filters,
|
|
[
|
|
{"parameter_name": "status", "parameter_value": "started"},
|
|
{
|
|
"parameter_name": "investigator",
|
|
"parameter_value": user.first_name + " " + user.last_name,
|
|
},
|
|
],
|
|
)
|
|
|
|
def test_get_filters(self):
|
|
with less_console_noise():
|
|
# Create a mock request object
|
|
request = self.factory.get("/admin/yourmodel/")
|
|
# Set the GET parameters for testing
|
|
request.GET = {
|
|
"status": "started",
|
|
"investigator": "Jeff Lebowski",
|
|
"q": "search_value",
|
|
}
|
|
# Call the get_filters method
|
|
filters = self.admin.get_filters(request)
|
|
# Assert the filters extracted from the request GET
|
|
self.assertEqual(
|
|
filters,
|
|
[
|
|
{"parameter_name": "status", "parameter_value": "started"},
|
|
{"parameter_name": "investigator", "parameter_value": "Jeff Lebowski"},
|
|
],
|
|
)
|
|
|
|
|
|
class TestMyUserAdmin(MockDbForSharedTests, WebTest):
|
|
"""Tests for the MyUserAdmin class as super or staff user
|
|
|
|
Notes:
|
|
all tests share superuser/staffuser; do not change these models in tests
|
|
all tests share MockDb; do not change models defined therein in tests
|
|
tests have available staffuser, superuser, client, test_helper and admin
|
|
"""
|
|
|
|
@classmethod
|
|
def setUpClass(cls):
|
|
super().setUpClass()
|
|
admin_site = AdminSite()
|
|
cls.admin = MyUserAdmin(model=get_user_model(), admin_site=admin_site)
|
|
cls.superuser = create_superuser()
|
|
cls.staffuser = create_user()
|
|
cls.test_helper = GenericTestHelper(admin=cls.admin)
|
|
|
|
def setUp(self):
|
|
super().setUp()
|
|
self.app.set_user(self.superuser.username)
|
|
self.client = Client(HTTP_HOST="localhost:8080")
|
|
|
|
def tearDown(self):
|
|
super().tearDown()
|
|
DomainRequest.objects.all().delete()
|
|
|
|
@classmethod
|
|
def tearDownClass(cls):
|
|
super().tearDownClass()
|
|
User.objects.all().delete()
|
|
|
|
@less_console_noise_decorator
|
|
def test_has_model_description(self):
|
|
"""Tests if this model has a model description on the table view"""
|
|
self.client.force_login(self.superuser)
|
|
response = self.client.get(
|
|
"/admin/registrar/user/",
|
|
follow=True,
|
|
)
|
|
|
|
# Make sure that the page is loaded correctly
|
|
self.assertEqual(response.status_code, 200)
|
|
|
|
# Test for a description snippet
|
|
self.assertContains(response, "A user is anyone who has access to the registrar.")
|
|
self.assertContains(response, "Show more")
|
|
|
|
@less_console_noise_decorator
|
|
def test_helper_text(self):
|
|
"""
|
|
Tests for the correct helper text on this page
|
|
"""
|
|
user = self.staffuser
|
|
|
|
self.client.force_login(self.superuser)
|
|
response = self.client.get(
|
|
"/admin/registrar/user/{}/change/".format(user.pk),
|
|
follow=True,
|
|
)
|
|
|
|
# Make sure the page loaded
|
|
self.assertEqual(response.status_code, 200)
|
|
|
|
# These should exist in the response
|
|
expected_values = [
|
|
("password", "Raw passwords are not stored, so they will not display here."),
|
|
("status", 'Users in "restricted" status cannot make updates in the registrar or start a new request.'),
|
|
("is_staff", "Designates whether the user can log in to this admin site"),
|
|
("is_superuser", "For development purposes only; provides superuser access on the database level"),
|
|
]
|
|
self.test_helper.assert_response_contains_distinct_values(response, expected_values)
|
|
|
|
@less_console_noise_decorator
|
|
def test_list_display_without_username(self):
|
|
request = self.client.request().wsgi_request
|
|
request.user = self.staffuser
|
|
|
|
list_display = self.admin.get_list_display(request)
|
|
expected_list_display = [
|
|
"email",
|
|
"first_name",
|
|
"last_name",
|
|
"group",
|
|
"status",
|
|
]
|
|
|
|
self.assertEqual(list_display, expected_list_display)
|
|
self.assertNotIn("username", list_display)
|
|
|
|
def test_get_fieldsets_superuser(self):
|
|
with less_console_noise():
|
|
request = self.client.request().wsgi_request
|
|
request.user = self.superuser
|
|
fieldsets = self.admin.get_fieldsets(request)
|
|
|
|
expected_fieldsets = super(MyUserAdmin, self.admin).get_fieldsets(request)
|
|
self.assertEqual(fieldsets, expected_fieldsets)
|
|
|
|
def test_get_fieldsets_cisa_analyst(self):
|
|
with less_console_noise():
|
|
request = self.client.request().wsgi_request
|
|
request.user = self.staffuser
|
|
fieldsets = self.admin.get_fieldsets(request)
|
|
expected_fieldsets = (
|
|
(
|
|
None,
|
|
{
|
|
"fields": (
|
|
"status",
|
|
"verification_type",
|
|
)
|
|
},
|
|
),
|
|
("User profile", {"fields": ("first_name", "middle_name", "last_name", "title", "email", "phone")}),
|
|
(
|
|
"Permissions",
|
|
{
|
|
"fields": (
|
|
"is_active",
|
|
"groups",
|
|
)
|
|
},
|
|
),
|
|
("Important dates", {"fields": ("last_login", "date_joined")}),
|
|
)
|
|
self.assertEqual(fieldsets, expected_fieldsets)
|
|
|
|
@less_console_noise_decorator
|
|
def test_analyst_can_see_related_domains_and_requests_in_user_form(self):
|
|
"""Tests if an analyst can see the related domains and domain requests for a user in that user's form"""
|
|
|
|
# From MockDb, we have self.meoward_user which we'll use as creator
|
|
# Create fake domain requests
|
|
domain_request_started = completed_domain_request(
|
|
status=DomainRequest.DomainRequestStatus.STARTED, user=self.meoward_user, name="started.gov"
|
|
)
|
|
domain_request_submitted = completed_domain_request(
|
|
status=DomainRequest.DomainRequestStatus.SUBMITTED, user=self.meoward_user, name="submitted.gov"
|
|
)
|
|
domain_request_in_review = completed_domain_request(
|
|
status=DomainRequest.DomainRequestStatus.IN_REVIEW, user=self.meoward_user, name="in-review.gov"
|
|
)
|
|
domain_request_withdrawn = completed_domain_request(
|
|
status=DomainRequest.DomainRequestStatus.WITHDRAWN, user=self.meoward_user, name="withdrawn.gov"
|
|
)
|
|
domain_request_approved = completed_domain_request(
|
|
status=DomainRequest.DomainRequestStatus.APPROVED, user=self.meoward_user, name="approved.gov"
|
|
)
|
|
domain_request_rejected = completed_domain_request(
|
|
status=DomainRequest.DomainRequestStatus.REJECTED, user=self.meoward_user, name="rejected.gov"
|
|
)
|
|
domain_request_ineligible = completed_domain_request(
|
|
status=DomainRequest.DomainRequestStatus.INELIGIBLE, user=self.meoward_user, name="ineligible.gov"
|
|
)
|
|
|
|
# From MockDb, we have sel.meoward_user who's admin on
|
|
# self.domain_1 - READY
|
|
# self.domain_2 - DNS_NEEDED
|
|
# self.domain_11 - READY
|
|
# self.domain_12 - READY
|
|
# DELETED:
|
|
domain_deleted, _ = Domain.objects.get_or_create(
|
|
name="domain_deleted.gov", state=Domain.State.DELETED, deleted=timezone.make_aware(datetime(2024, 4, 2))
|
|
)
|
|
role, _ = UserDomainRole.objects.get_or_create(
|
|
user=self.meoward_user, domain=domain_deleted, role=UserDomainRole.Roles.MANAGER
|
|
)
|
|
|
|
self.client.force_login(self.staffuser)
|
|
response = self.client.get(
|
|
"/admin/registrar/user/{}/change/".format(self.meoward_user.id),
|
|
follow=True,
|
|
)
|
|
|
|
# Make sure the page loaded and contains the expected domain request names and links to the domain requests
|
|
self.assertEqual(response.status_code, 200)
|
|
|
|
self.assertContains(response, domain_request_submitted.requested_domain.name)
|
|
expected_href = reverse("admin:registrar_domainrequest_change", args=[domain_request_submitted.pk])
|
|
self.assertContains(response, expected_href)
|
|
|
|
self.assertContains(response, domain_request_in_review.requested_domain.name)
|
|
expected_href = reverse("admin:registrar_domainrequest_change", args=[domain_request_in_review.pk])
|
|
self.assertContains(response, expected_href)
|
|
|
|
self.assertContains(response, domain_request_approved.requested_domain.name)
|
|
expected_href = reverse("admin:registrar_domainrequest_change", args=[domain_request_approved.pk])
|
|
self.assertContains(response, expected_href)
|
|
|
|
self.assertContains(response, domain_request_rejected.requested_domain.name)
|
|
expected_href = reverse("admin:registrar_domainrequest_change", args=[domain_request_rejected.pk])
|
|
self.assertContains(response, expected_href)
|
|
|
|
self.assertContains(response, domain_request_ineligible.requested_domain.name)
|
|
expected_href = reverse("admin:registrar_domainrequest_change", args=[domain_request_ineligible.pk])
|
|
self.assertContains(response, expected_href)
|
|
|
|
# We filter out those requests
|
|
# STARTED
|
|
self.assertNotContains(response, domain_request_started.requested_domain.name)
|
|
expected_href = reverse("admin:registrar_domainrequest_change", args=[domain_request_started.pk])
|
|
self.assertNotContains(response, expected_href)
|
|
|
|
# WITHDRAWN
|
|
self.assertNotContains(response, domain_request_withdrawn.requested_domain.name)
|
|
expected_href = reverse("admin:registrar_domainrequest_change", args=[domain_request_withdrawn.pk])
|
|
self.assertNotContains(response, expected_href)
|
|
|
|
# Make sure the page contains the expected domain names and links to the domains
|
|
self.assertContains(response, self.domain_1.name)
|
|
expected_href = reverse("admin:registrar_domain_change", args=[self.domain_1.pk])
|
|
self.assertContains(response, expected_href)
|
|
|
|
# We filter out DELETED
|
|
self.assertNotContains(response, domain_deleted.name)
|
|
expected_href = reverse("admin:registrar_domain_change", args=[domain_deleted.pk])
|
|
self.assertNotContains(response, expected_href)
|
|
|
|
# Must clean up within test since MockDB is shared across tests for performance reasons
|
|
domain_request_started_id = domain_request_started.id
|
|
domain_request_submitted_id = domain_request_submitted.id
|
|
domain_request_in_review_id = domain_request_in_review.id
|
|
domain_request_withdrawn_id = domain_request_withdrawn.id
|
|
domain_request_approved_id = domain_request_approved.id
|
|
domain_request_rejected_id = domain_request_rejected.id
|
|
domain_request_ineligible_id = domain_request_ineligible.id
|
|
domain_request_ids = [
|
|
domain_request_started_id,
|
|
domain_request_submitted_id,
|
|
domain_request_in_review_id,
|
|
domain_request_withdrawn_id,
|
|
domain_request_approved_id,
|
|
domain_request_rejected_id,
|
|
domain_request_ineligible_id,
|
|
]
|
|
DomainRequest.objects.filter(id__in=domain_request_ids).delete()
|
|
domain_deleted.delete()
|
|
role.delete()
|
|
|
|
def test_analyst_cannot_see_selects_for_portfolio_role_and_permissions_in_user_form(self):
|
|
"""Can only test for the presence of a base element. The multiselects and the h2->h3 conversion are all
|
|
dynamically generated."""
|
|
|
|
self.client.force_login(self.staffuser)
|
|
response = self.client.get(
|
|
"/admin/registrar/user/{}/change/".format(self.meoward_user.id),
|
|
follow=True,
|
|
)
|
|
|
|
self.assertEqual(response.status_code, 200)
|
|
|
|
self.assertNotContains(response, "Portfolio roles:")
|
|
self.assertNotContains(response, "Portfolio additional permissions:")
|
|
|
|
@less_console_noise_decorator
|
|
def test_user_can_see_related_portfolios(self):
|
|
"""Tests if a user can see the portfolios they are associated with on the user page"""
|
|
portfolio, _ = Portfolio.objects.get_or_create(organization_name="test", creator=self.superuser)
|
|
permission, _ = UserPortfolioPermission.objects.get_or_create(
|
|
user=self.superuser, portfolio=portfolio, roles=[UserPortfolioRoleChoices.ORGANIZATION_ADMIN]
|
|
)
|
|
response = self.app.get(reverse("admin:registrar_user_change", args=[self.superuser.pk]))
|
|
expected_href = reverse("admin:registrar_portfolio_change", args=[portfolio.pk])
|
|
self.assertContains(response, expected_href)
|
|
self.assertContains(response, str(portfolio))
|
|
permission.delete()
|
|
portfolio.delete()
|
|
|
|
|
|
class AuditedAdminTest(TestCase):
|
|
|
|
@classmethod
|
|
def setUpClass(cls):
|
|
super().setUpClass()
|
|
cls.site = AdminSite()
|
|
cls.factory = RequestFactory()
|
|
|
|
def setUp(self):
|
|
super().setUp()
|
|
self.client = Client(HTTP_HOST="localhost:8080")
|
|
self.staffuser = create_user()
|
|
|
|
def tearDown(self):
|
|
super().tearDown()
|
|
DomainInformation.objects.all().delete()
|
|
DomainRequest.objects.all().delete()
|
|
DomainInvitation.objects.all().delete()
|
|
|
|
def order_by_desired_field_helper(self, obj_to_sort: AuditedAdmin, request, field_name, *obj_names):
|
|
with less_console_noise():
|
|
formatted_sort_fields = []
|
|
for obj in obj_names:
|
|
formatted_sort_fields.append("{}__{}".format(field_name, obj))
|
|
|
|
ordered_list = list(
|
|
obj_to_sort.get_queryset(request).order_by(*formatted_sort_fields).values_list(*formatted_sort_fields)
|
|
)
|
|
|
|
return ordered_list
|
|
|
|
@less_console_noise_decorator
|
|
def test_alphabetically_sorted_domain_request_investigator(self):
|
|
"""Tests if the investigator field is alphabetically sorted by mimicking
|
|
the call event flow"""
|
|
# Creates multiple domain requests - review status does not matter
|
|
domain_requests = multiple_unalphabetical_domain_objects("domain_request")
|
|
|
|
# Create a mock request
|
|
domain_request_request = self.factory.post(
|
|
"/admin/registrar/domainrequest/{}/change/".format(domain_requests[0].pk)
|
|
)
|
|
|
|
# Get the formfield data from the domain request page
|
|
domain_request_admin = AuditedAdmin(DomainRequest, self.site)
|
|
field = DomainRequest.investigator.field
|
|
domain_request_queryset = domain_request_admin.formfield_for_foreignkey(field, domain_request_request).queryset
|
|
|
|
request = self.factory.post(
|
|
"/admin/autocomplete/?app_label=registrar&model_name=domainrequest&field_name=investigator"
|
|
)
|
|
|
|
sorted_fields = ["first_name", "last_name", "email"]
|
|
desired_sort_order = list(User.objects.filter(is_staff=True).order_by(*sorted_fields))
|
|
|
|
# Grab the data returned from get search results
|
|
admin = MyUserAdmin(User, self.site)
|
|
search_queryset = admin.get_search_results(request, domain_request_queryset, None)[0]
|
|
current_sort_order = list(search_queryset)
|
|
|
|
self.assertEqual(
|
|
desired_sort_order,
|
|
current_sort_order,
|
|
"Investigator is not ordered alphabetically",
|
|
)
|
|
|
|
# This test case should be refactored in general, as it is too overly specific and engineered
|
|
def test_alphabetically_sorted_fk_fields_domain_request(self):
|
|
with less_console_noise():
|
|
tested_fields = [
|
|
# Senior offical is commented out for now - this is alphabetized
|
|
# and this test does not accurately reflect that.
|
|
# DomainRequest.senior_official.field,
|
|
# DomainRequest.investigator.field,
|
|
DomainRequest.creator.field,
|
|
DomainRequest.requested_domain.field,
|
|
]
|
|
|
|
# Creates multiple domain requests - review status does not matter
|
|
domain_requests = multiple_unalphabetical_domain_objects("domain_request")
|
|
|
|
# Create a mock request
|
|
request = self.factory.post("/admin/registrar/domainrequest/{}/change/".format(domain_requests[0].pk))
|
|
|
|
model_admin = AuditedAdmin(DomainRequest, self.site)
|
|
|
|
sorted_fields = []
|
|
# Typically we wouldn't want two nested for fields,
|
|
# but both fields are of a fixed length.
|
|
# For test case purposes, this should be performant.
|
|
for field in tested_fields:
|
|
with self.subTest(field=field):
|
|
isNamefield: bool = field == DomainRequest.requested_domain.field
|
|
if isNamefield:
|
|
sorted_fields = ["name"]
|
|
else:
|
|
sorted_fields = ["first_name", "last_name"]
|
|
# We want both of these to be lists, as it is richer test wise.
|
|
|
|
desired_order = self.order_by_desired_field_helper(model_admin, request, field.name, *sorted_fields)
|
|
current_sort_order = list(model_admin.formfield_for_foreignkey(field, request).queryset)
|
|
|
|
# Conforms to the same object structure as desired_order
|
|
current_sort_order_coerced_type = []
|
|
|
|
# This is necessary as .queryset and get_queryset
|
|
# return lists of different types/structures.
|
|
# We need to parse this data and coerce them into the same type.
|
|
for contact in current_sort_order:
|
|
if not isNamefield:
|
|
first = contact.first_name
|
|
last = contact.last_name
|
|
else:
|
|
first = contact.name
|
|
last = None
|
|
|
|
name_tuple = self.coerced_fk_field_helper(first, last, field.name, ":")
|
|
if name_tuple is not None:
|
|
current_sort_order_coerced_type.append(name_tuple)
|
|
|
|
self.assertEqual(
|
|
desired_order,
|
|
current_sort_order_coerced_type,
|
|
"{} is not ordered alphabetically".format(field.name),
|
|
)
|
|
|
|
def test_alphabetically_sorted_fk_fields_domain_information(self):
|
|
with less_console_noise():
|
|
tested_fields = [
|
|
# Senior offical is commented out for now - this is alphabetized
|
|
# and this test does not accurately reflect that.
|
|
# DomainInformation.senior_official.field,
|
|
# DomainInformation.creator.field,
|
|
(DomainInformation.domain.field, ["name"]),
|
|
(DomainInformation.domain_request.field, ["requested_domain__name"]),
|
|
]
|
|
# Creates multiple domain requests - review status does not matter
|
|
domain_requests = multiple_unalphabetical_domain_objects("information")
|
|
|
|
# Create a mock request
|
|
request = self.factory.post("/admin/registrar/domaininformation/{}/change/".format(domain_requests[0].pk))
|
|
|
|
model_admin = AuditedAdmin(DomainInformation, self.site)
|
|
|
|
sorted_fields = []
|
|
# Typically we wouldn't want two nested for fields,
|
|
# but both fields are of a fixed length.
|
|
# For test case purposes, this should be performant.
|
|
for field in tested_fields:
|
|
isOtherOrderfield: bool = isinstance(field, tuple)
|
|
field_obj = None
|
|
if isOtherOrderfield:
|
|
sorted_fields = field[1]
|
|
field_obj = field[0]
|
|
else:
|
|
sorted_fields = ["first_name", "last_name"]
|
|
field_obj = field
|
|
# We want both of these to be lists, as it is richer test wise.
|
|
desired_order = self.order_by_desired_field_helper(model_admin, request, field_obj.name, *sorted_fields)
|
|
current_sort_order = list(model_admin.formfield_for_foreignkey(field_obj, request).queryset)
|
|
|
|
# Conforms to the same object structure as desired_order
|
|
current_sort_order_coerced_type = []
|
|
# This is necessary as .queryset and get_queryset
|
|
# return lists of different types/structures.
|
|
# We need to parse this data and coerce them into the same type.
|
|
for obj in current_sort_order:
|
|
last = None
|
|
if not isOtherOrderfield:
|
|
first = obj.first_name
|
|
last = obj.last_name
|
|
elif field_obj == DomainInformation.domain.field:
|
|
first = obj.name
|
|
elif field_obj == DomainInformation.domain_request.field:
|
|
first = obj.requested_domain.name
|
|
|
|
name_tuple = self.coerced_fk_field_helper(first, last, field_obj.name, ":")
|
|
if name_tuple is not None:
|
|
current_sort_order_coerced_type.append(name_tuple)
|
|
|
|
self.assertEqual(
|
|
desired_order,
|
|
current_sort_order_coerced_type,
|
|
"{} is not ordered alphabetically".format(field_obj.name),
|
|
)
|
|
|
|
def test_alphabetically_sorted_fk_fields_domain_invitation(self):
|
|
with less_console_noise():
|
|
tested_fields = [DomainInvitation.domain.field]
|
|
|
|
# Creates multiple domain requests - review status does not matter
|
|
domain_requests = multiple_unalphabetical_domain_objects("invitation")
|
|
|
|
# Create a mock request
|
|
request = self.factory.post("/admin/registrar/domaininvitation/{}/change/".format(domain_requests[0].pk))
|
|
|
|
model_admin = AuditedAdmin(DomainInvitation, self.site)
|
|
|
|
sorted_fields = []
|
|
# Typically we wouldn't want two nested for fields,
|
|
# but both fields are of a fixed length.
|
|
# For test case purposes, this should be performant.
|
|
for field in tested_fields:
|
|
sorted_fields = ["name"]
|
|
# We want both of these to be lists, as it is richer test wise.
|
|
|
|
desired_order = self.order_by_desired_field_helper(model_admin, request, field.name, *sorted_fields)
|
|
current_sort_order = list(model_admin.formfield_for_foreignkey(field, request).queryset)
|
|
|
|
# Conforms to the same object structure as desired_order
|
|
current_sort_order_coerced_type = []
|
|
|
|
# This is necessary as .queryset and get_queryset
|
|
# return lists of different types/structures.
|
|
# We need to parse this data and coerce them into the same type.
|
|
for contact in current_sort_order:
|
|
first = contact.name
|
|
last = None
|
|
|
|
name_tuple = self.coerced_fk_field_helper(first, last, field.name, ":")
|
|
if name_tuple is not None:
|
|
current_sort_order_coerced_type.append(name_tuple)
|
|
|
|
self.assertEqual(
|
|
desired_order,
|
|
current_sort_order_coerced_type,
|
|
"{} is not ordered alphabetically".format(field.name),
|
|
)
|
|
|
|
def coerced_fk_field_helper(self, first_name, last_name, field_name, queryset_shorthand):
|
|
"""Handles edge cases for test cases"""
|
|
if first_name is None:
|
|
raise ValueError("Invalid value for first_name, must be defined")
|
|
|
|
returned_tuple = (first_name, last_name)
|
|
# Handles edge case for names - structured strangely
|
|
if last_name is None:
|
|
return (first_name,)
|
|
|
|
split_name = first_name.split(queryset_shorthand)
|
|
if len(split_name) == 2 and split_name[1] == field_name:
|
|
return returned_tuple
|
|
else:
|
|
return None
|
|
|
|
|
|
class DomainSessionVariableTest(TestCase):
|
|
"""Test cases for session variables in Django Admin"""
|
|
|
|
@classmethod
|
|
def setUpClass(cls):
|
|
super().setUpClass()
|
|
cls.factory = RequestFactory()
|
|
cls.admin = DomainAdmin(Domain, None)
|
|
cls.superuser = create_superuser()
|
|
|
|
def setUp(self):
|
|
super().setUp()
|
|
self.client = Client(HTTP_HOST="localhost:8080")
|
|
|
|
@classmethod
|
|
def tearDownClass(cls):
|
|
super().tearDownClass()
|
|
User.objects.all().delete()
|
|
|
|
def test_session_vars_set_correctly(self):
|
|
"""Checks if session variables are being set correctly"""
|
|
|
|
with less_console_noise():
|
|
self.client.force_login(self.superuser)
|
|
|
|
dummy_domain_information = generic_domain_object("information", "session")
|
|
request = self.get_factory_post_edit_domain(dummy_domain_information.domain.pk)
|
|
self.populate_session_values(request, dummy_domain_information.domain)
|
|
self.assertEqual(request.session["analyst_action"], "edit")
|
|
self.assertEqual(
|
|
request.session["analyst_action_location"],
|
|
dummy_domain_information.domain.pk,
|
|
)
|
|
|
|
def test_session_vars_set_correctly_hardcoded_domain(self):
|
|
"""Checks if session variables are being set correctly"""
|
|
|
|
with less_console_noise():
|
|
self.client.force_login(self.superuser)
|
|
|
|
dummy_domain_information: Domain = generic_domain_object("information", "session")
|
|
dummy_domain_information.domain.pk = 1
|
|
|
|
request = self.get_factory_post_edit_domain(dummy_domain_information.domain.pk)
|
|
self.populate_session_values(request, dummy_domain_information.domain)
|
|
self.assertEqual(request.session["analyst_action"], "edit")
|
|
self.assertEqual(request.session["analyst_action_location"], 1)
|
|
|
|
def test_session_variables_reset_correctly(self):
|
|
"""Checks if incorrect session variables get overridden"""
|
|
|
|
with less_console_noise():
|
|
self.client.force_login(self.superuser)
|
|
|
|
dummy_domain_information = generic_domain_object("information", "session")
|
|
request = self.get_factory_post_edit_domain(dummy_domain_information.domain.pk)
|
|
|
|
self.populate_session_values(request, dummy_domain_information.domain, preload_bad_data=True)
|
|
|
|
self.assertEqual(request.session["analyst_action"], "edit")
|
|
self.assertEqual(
|
|
request.session["analyst_action_location"],
|
|
dummy_domain_information.domain.pk,
|
|
)
|
|
|
|
def test_session_variables_retain_information(self):
|
|
"""Checks to see if session variables retain old information"""
|
|
|
|
with less_console_noise():
|
|
self.client.force_login(self.superuser)
|
|
|
|
dummy_domain_information_list = multiple_unalphabetical_domain_objects("information")
|
|
for item in dummy_domain_information_list:
|
|
request = self.get_factory_post_edit_domain(item.domain.pk)
|
|
self.populate_session_values(request, item.domain)
|
|
|
|
self.assertEqual(request.session["analyst_action"], "edit")
|
|
self.assertEqual(request.session["analyst_action_location"], item.domain.pk)
|
|
|
|
def test_session_variables_concurrent_requests(self):
|
|
"""Simulates two requests at once"""
|
|
|
|
with less_console_noise():
|
|
self.client.force_login(self.superuser)
|
|
|
|
info_first = generic_domain_object("information", "session")
|
|
info_second = generic_domain_object("information", "session2")
|
|
|
|
request_first = self.get_factory_post_edit_domain(info_first.domain.pk)
|
|
request_second = self.get_factory_post_edit_domain(info_second.domain.pk)
|
|
|
|
self.populate_session_values(request_first, info_first.domain, True)
|
|
self.populate_session_values(request_second, info_second.domain, True)
|
|
|
|
# Check if anything got nulled out
|
|
self.assertNotEqual(request_first.session["analyst_action"], None)
|
|
self.assertNotEqual(request_second.session["analyst_action"], None)
|
|
self.assertNotEqual(request_first.session["analyst_action_location"], None)
|
|
self.assertNotEqual(request_second.session["analyst_action_location"], None)
|
|
|
|
# Check if they are both the same action 'type'
|
|
self.assertEqual(request_first.session["analyst_action"], "edit")
|
|
self.assertEqual(request_second.session["analyst_action"], "edit")
|
|
|
|
# Check their locations, and ensure they aren't the same across both
|
|
self.assertNotEqual(
|
|
request_first.session["analyst_action_location"],
|
|
request_second.session["analyst_action_location"],
|
|
)
|
|
|
|
def populate_session_values(self, request, domain_object, preload_bad_data=False):
|
|
"""Boilerplate for creating mock sessions"""
|
|
request.user = self.client
|
|
request.session = SessionStore()
|
|
request.session.create()
|
|
if preload_bad_data:
|
|
request.session["analyst_action"] = "invalid"
|
|
request.session["analyst_action_location"] = "bad location"
|
|
self.admin.response_change(request, domain_object)
|
|
|
|
def get_factory_post_edit_domain(self, primary_key):
|
|
"""Posts to registrar domain change
|
|
with the edit domain button 'clicked',
|
|
then returns the factory object"""
|
|
return self.factory.post(
|
|
reverse("admin:registrar_domain_change", args=(primary_key,)),
|
|
{"_edit_domain": "true"},
|
|
follow=True,
|
|
)
|
|
|
|
|
|
class TestContactAdmin(TestCase):
|
|
|
|
@classmethod
|
|
def setUpClass(cls):
|
|
super().setUpClass()
|
|
cls.site = AdminSite()
|
|
cls.factory = RequestFactory()
|
|
cls.admin = ContactAdmin(model=Contact, admin_site=None)
|
|
cls.superuser = create_superuser()
|
|
cls.staffuser = create_user()
|
|
|
|
def setUp(self):
|
|
super().setUp()
|
|
self.client = Client(HTTP_HOST="localhost:8080")
|
|
|
|
def tearDown(self):
|
|
super().tearDown()
|
|
DomainRequest.objects.all().delete()
|
|
Contact.objects.all().delete()
|
|
|
|
@classmethod
|
|
def tearDownClass(cls):
|
|
super().tearDownClass()
|
|
User.objects.all().delete()
|
|
|
|
@less_console_noise_decorator
|
|
def test_has_model_description(self):
|
|
"""Tests if this model has a model description on the table view"""
|
|
self.client.force_login(self.superuser)
|
|
response = self.client.get(
|
|
"/admin/registrar/contact/",
|
|
follow=True,
|
|
)
|
|
|
|
# Make sure that the page is loaded correctly
|
|
self.assertEqual(response.status_code, 200)
|
|
|
|
# Test for a description snippet
|
|
self.assertContains(response, "Contacts include anyone who has access to the registrar (known as “users”)")
|
|
self.assertContains(response, "Show more")
|
|
|
|
def test_readonly_when_restricted_staffuser(self):
|
|
with less_console_noise():
|
|
request = self.factory.get("/")
|
|
request.user = self.staffuser
|
|
|
|
readonly_fields = self.admin.get_readonly_fields(request)
|
|
|
|
expected_fields = ["email"]
|
|
|
|
self.assertEqual(readonly_fields, expected_fields)
|
|
|
|
def test_readonly_when_restricted_superuser(self):
|
|
with less_console_noise():
|
|
request = self.factory.get("/")
|
|
request.user = self.superuser
|
|
|
|
readonly_fields = self.admin.get_readonly_fields(request)
|
|
|
|
expected_fields = []
|
|
|
|
self.assertEqual(readonly_fields, expected_fields)
|
|
|
|
|
|
class TestVerifiedByStaffAdmin(TestCase):
|
|
|
|
@classmethod
|
|
def setUpClass(cls):
|
|
super().setUpClass()
|
|
cls.site = AdminSite()
|
|
cls.superuser = create_superuser()
|
|
cls.admin = VerifiedByStaffAdmin(model=VerifiedByStaff, admin_site=cls.site)
|
|
cls.factory = RequestFactory()
|
|
cls.test_helper = GenericTestHelper(admin=cls.admin)
|
|
|
|
def setUp(self):
|
|
super().setUp()
|
|
self.client = Client(HTTP_HOST="localhost:8080")
|
|
|
|
def tearDown(self):
|
|
super().tearDown()
|
|
VerifiedByStaff.objects.all().delete()
|
|
|
|
@classmethod
|
|
def tearDownClass(cls):
|
|
super().tearDownClass()
|
|
User.objects.all().delete()
|
|
|
|
@less_console_noise_decorator
|
|
def test_has_model_description(self):
|
|
"""Tests if this model has a model description on the table view"""
|
|
self.client.force_login(self.superuser)
|
|
response = self.client.get(
|
|
"/admin/registrar/verifiedbystaff/",
|
|
follow=True,
|
|
)
|
|
|
|
# Make sure that the page is loaded correctly
|
|
self.assertEqual(response.status_code, 200)
|
|
|
|
# Test for a description snippet
|
|
self.assertContains(
|
|
response, "This table contains users who have been allowed to bypass " "identity proofing through Login.gov"
|
|
)
|
|
self.assertContains(response, "Show more")
|
|
|
|
@less_console_noise_decorator
|
|
def test_helper_text(self):
|
|
"""
|
|
Tests for the correct helper text on this page
|
|
"""
|
|
vip_instance, _ = VerifiedByStaff.objects.get_or_create(email="test@example.com", notes="Test Notes")
|
|
|
|
self.client.force_login(self.superuser)
|
|
response = self.client.get(
|
|
"/admin/registrar/verifiedbystaff/{}/change/".format(vip_instance.pk),
|
|
follow=True,
|
|
)
|
|
|
|
# Make sure the page loaded
|
|
self.assertEqual(response.status_code, 200)
|
|
|
|
# These should exist in the response
|
|
expected_values = [
|
|
("requestor", "Person who verified this user"),
|
|
]
|
|
self.test_helper.assert_response_contains_distinct_values(response, expected_values)
|
|
|
|
def test_save_model_sets_user_field(self):
|
|
with less_console_noise():
|
|
self.client.force_login(self.superuser)
|
|
|
|
# Create an instance of the admin class
|
|
admin_instance = VerifiedByStaffAdmin(model=VerifiedByStaff, admin_site=None)
|
|
|
|
# Create a VerifiedByStaff instance
|
|
vip_instance = VerifiedByStaff(email="test@example.com", notes="Test Notes")
|
|
|
|
# Create a request object
|
|
request = self.factory.post("/admin/yourapp/VerifiedByStaff/add/")
|
|
request.user = self.superuser
|
|
|
|
# Call the save_model method
|
|
admin_instance.save_model(request, vip_instance, None, None)
|
|
|
|
# Check that the user field is set to the request.user
|
|
self.assertEqual(vip_instance.requestor, self.superuser)
|
|
|
|
|
|
class TestWebsiteAdmin(TestCase):
|
|
def setUp(self):
|
|
super().setUp()
|
|
self.site = AdminSite()
|
|
self.superuser = create_superuser()
|
|
self.admin = WebsiteAdmin(model=Website, admin_site=self.site)
|
|
self.factory = RequestFactory()
|
|
self.client = Client(HTTP_HOST="localhost:8080")
|
|
self.test_helper = GenericTestHelper(admin=self.admin)
|
|
|
|
def tearDown(self):
|
|
super().tearDown()
|
|
Website.objects.all().delete()
|
|
User.objects.all().delete()
|
|
|
|
@less_console_noise_decorator
|
|
def test_has_model_description(self):
|
|
"""Tests if this model has a model description on the table view"""
|
|
self.client.force_login(self.superuser)
|
|
response = self.client.get(
|
|
"/admin/registrar/website/",
|
|
follow=True,
|
|
)
|
|
|
|
# Make sure that the page is loaded correctly
|
|
self.assertEqual(response.status_code, 200)
|
|
|
|
# Test for a description snippet
|
|
self.assertContains(response, "This table lists all the “current websites” and “alternative domains”")
|
|
self.assertContains(response, "Show more")
|
|
|
|
|
|
class TestDraftDomain(TestCase):
|
|
|
|
@classmethod
|
|
def setUpClass(cls):
|
|
super().setUpClass()
|
|
cls.site = AdminSite()
|
|
cls.superuser = create_superuser()
|
|
cls.admin = DraftDomainAdmin(model=DraftDomain, admin_site=cls.site)
|
|
cls.factory = RequestFactory()
|
|
cls.test_helper = GenericTestHelper(admin=cls.admin)
|
|
|
|
def setUp(self):
|
|
super().setUp()
|
|
self.client = Client(HTTP_HOST="localhost:8080")
|
|
|
|
def tearDown(self):
|
|
super().tearDown()
|
|
DraftDomain.objects.all().delete()
|
|
|
|
@classmethod
|
|
def tearDownClass(cls):
|
|
super().tearDownClass()
|
|
User.objects.all().delete()
|
|
|
|
@less_console_noise_decorator
|
|
def test_has_model_description(self):
|
|
"""Tests if this model has a model description on the table view"""
|
|
self.client.force_login(self.superuser)
|
|
response = self.client.get(
|
|
"/admin/registrar/draftdomain/",
|
|
follow=True,
|
|
)
|
|
|
|
# Make sure that the page is loaded correctly
|
|
self.assertEqual(response.status_code, 200)
|
|
|
|
# Test for a description snippet
|
|
self.assertContains(
|
|
response, "This table represents all “requested domains” that have been saved within a domain"
|
|
)
|
|
self.assertContains(response, "Show more")
|
|
|
|
|
|
class TestFederalAgency(TestCase):
|
|
|
|
@classmethod
|
|
def setUpClass(cls):
|
|
super().setUpClass()
|
|
cls.site = AdminSite()
|
|
cls.superuser = create_superuser()
|
|
cls.admin = FederalAgencyAdmin(model=FederalAgency, admin_site=cls.site)
|
|
cls.factory = RequestFactory()
|
|
cls.test_helper = GenericTestHelper(admin=cls.admin)
|
|
|
|
def setUp(self):
|
|
self.client = Client(HTTP_HOST="localhost:8080")
|
|
|
|
@classmethod
|
|
def tearDownClass(cls):
|
|
super().tearDownClass()
|
|
User.objects.all().delete()
|
|
|
|
@less_console_noise_decorator
|
|
def test_has_model_description(self):
|
|
"""Tests if this model has a model description on the table view"""
|
|
self.client.force_login(self.superuser)
|
|
response = self.client.get(
|
|
"/admin/registrar/federalagency/",
|
|
follow=True,
|
|
)
|
|
|
|
# Make sure that the page is loaded correctly
|
|
self.assertEqual(response.status_code, 200)
|
|
|
|
# Test for a description snippet
|
|
self.assertContains(response, "This table does not have a description yet.")
|
|
self.assertContains(response, "Show more")
|
|
|
|
|
|
class TestPublicContact(TestCase):
|
|
def setUp(self):
|
|
super().setUp()
|
|
self.site = AdminSite()
|
|
self.superuser = create_superuser()
|
|
self.admin = PublicContactAdmin(model=PublicContact, admin_site=self.site)
|
|
self.factory = RequestFactory()
|
|
self.client = Client(HTTP_HOST="localhost:8080")
|
|
self.test_helper = GenericTestHelper(admin=self.admin)
|
|
|
|
def tearDown(self):
|
|
super().tearDown()
|
|
PublicContact.objects.all().delete()
|
|
User.objects.all().delete()
|
|
|
|
@less_console_noise_decorator
|
|
def test_has_model_description(self):
|
|
"""Tests if this model has a model description on the table view"""
|
|
p = "adminpass"
|
|
self.client.login(username="superuser", password=p)
|
|
response = self.client.get(
|
|
"/admin/registrar/publiccontact/",
|
|
follow=True,
|
|
)
|
|
|
|
# Make sure that the page is loaded correctly
|
|
self.assertEqual(response.status_code, 200)
|
|
|
|
# Test for a description snippet
|
|
self.assertContains(response, "Public contacts represent the three registry contact types")
|
|
self.assertContains(response, "Show more")
|
|
|
|
|
|
class TestTransitionDomain(TestCase):
|
|
def setUp(self):
|
|
super().setUp()
|
|
self.site = AdminSite()
|
|
self.superuser = create_superuser()
|
|
self.admin = TransitionDomainAdmin(model=TransitionDomain, admin_site=self.site)
|
|
self.factory = RequestFactory()
|
|
self.client = Client(HTTP_HOST="localhost:8080")
|
|
self.test_helper = GenericTestHelper(admin=self.admin)
|
|
|
|
def tearDown(self):
|
|
super().tearDown()
|
|
PublicContact.objects.all().delete()
|
|
User.objects.all().delete()
|
|
|
|
@less_console_noise_decorator
|
|
def test_has_model_description(self):
|
|
"""Tests if this model has a model description on the table view"""
|
|
self.client.force_login(self.superuser)
|
|
response = self.client.get(
|
|
"/admin/registrar/transitiondomain/",
|
|
follow=True,
|
|
)
|
|
|
|
# Make sure that the page is loaded correctly
|
|
self.assertEqual(response.status_code, 200)
|
|
|
|
# Test for a description snippet
|
|
self.assertContains(response, "This table represents the domains that were transitioned from the old registry")
|
|
self.assertContains(response, "Show more")
|
|
|
|
|
|
class TestUserGroup(TestCase):
|
|
def setUp(self):
|
|
super().setUp()
|
|
self.site = AdminSite()
|
|
self.superuser = create_superuser()
|
|
self.admin = UserGroupAdmin(model=UserGroup, admin_site=self.site)
|
|
self.factory = RequestFactory()
|
|
self.client = Client(HTTP_HOST="localhost:8080")
|
|
self.test_helper = GenericTestHelper(admin=self.admin)
|
|
|
|
def tearDown(self):
|
|
super().tearDown()
|
|
User.objects.all().delete()
|
|
|
|
@less_console_noise_decorator
|
|
def test_has_model_description(self):
|
|
"""Tests if this model has a model description on the table view"""
|
|
self.client.force_login(self.superuser)
|
|
response = self.client.get(
|
|
"/admin/registrar/usergroup/",
|
|
follow=True,
|
|
)
|
|
|
|
# Make sure that the page is loaded correctly
|
|
self.assertEqual(response.status_code, 200)
|
|
|
|
# Test for a description snippet
|
|
self.assertContains(
|
|
response, "Groups are a way to bundle admin permissions so they can be easily assigned to multiple users."
|
|
)
|
|
self.assertContains(response, "Show more")
|
|
|
|
|
|
class TestPortfolioAdmin(TestCase):
|
|
@classmethod
|
|
def setUpClass(cls):
|
|
super().setUpClass()
|
|
cls.site = AdminSite()
|
|
cls.superuser = create_superuser()
|
|
cls.admin = PortfolioAdmin(model=Portfolio, admin_site=cls.site)
|
|
cls.factory = RequestFactory()
|
|
|
|
def setUp(self):
|
|
self.client = Client(HTTP_HOST="localhost:8080")
|
|
self.portfolio = Portfolio.objects.create(organization_name="Test Portfolio", creator=self.superuser)
|
|
|
|
def tearDown(self):
|
|
Suborganization.objects.all().delete()
|
|
DomainInformation.objects.all().delete()
|
|
DomainRequest.objects.all().delete()
|
|
Domain.objects.all().delete()
|
|
Portfolio.objects.all().delete()
|
|
User.objects.all().delete()
|
|
|
|
@less_console_noise_decorator
|
|
def test_created_on_display(self):
|
|
"""Tests the custom created on which is a reskin of the created_at field"""
|
|
created_on = self.admin.created_on(self.portfolio)
|
|
expected_date = self.portfolio.created_at.strftime("%b %d, %Y")
|
|
self.assertEqual(created_on, expected_date)
|
|
|
|
@less_console_noise_decorator
|
|
def test_suborganizations_display(self):
|
|
"""Tests the custom suborg field which displays all related suborgs"""
|
|
Suborganization.objects.create(name="Sub1", portfolio=self.portfolio)
|
|
Suborganization.objects.create(name="Sub2", portfolio=self.portfolio)
|
|
|
|
suborganizations = self.admin.suborganizations(self.portfolio)
|
|
self.assertIn("Sub1", suborganizations)
|
|
self.assertIn("Sub2", suborganizations)
|
|
self.assertIn('<ul class="add-list-reset">', suborganizations)
|
|
|
|
@less_console_noise_decorator
|
|
def test_domains_display(self):
|
|
"""Tests the custom domains field which displays all related domains"""
|
|
request_1 = completed_domain_request(
|
|
name="request1.gov", portfolio=self.portfolio, status=DomainRequest.DomainRequestStatus.IN_REVIEW
|
|
)
|
|
request_2 = completed_domain_request(
|
|
name="request2.gov", portfolio=self.portfolio, status=DomainRequest.DomainRequestStatus.IN_REVIEW
|
|
)
|
|
|
|
# Create some domain objects
|
|
request_1.approve()
|
|
request_2.approve()
|
|
|
|
domain_1 = DomainInformation.objects.get(domain_request=request_1).domain
|
|
domain_1.name = "domain1.gov"
|
|
domain_1.save()
|
|
domain_2 = DomainInformation.objects.get(domain_request=request_2).domain
|
|
domain_2.name = "domain2.gov"
|
|
domain_2.save()
|
|
|
|
domains = self.admin.domains(self.portfolio)
|
|
self.assertIn("2 domains", domains)
|
|
|
|
@less_console_noise_decorator
|
|
def test_domain_requests_display(self):
|
|
"""Tests the custom domains requests field which displays all related requests"""
|
|
completed_domain_request(name="request1.gov", portfolio=self.portfolio)
|
|
completed_domain_request(name="request2.gov", portfolio=self.portfolio)
|
|
|
|
domain_requests = self.admin.domain_requests(self.portfolio)
|
|
self.assertIn("2 domain requests", domain_requests)
|
|
|
|
@less_console_noise_decorator
|
|
def test_portfolio_members_display(self):
|
|
"""Tests the custom portfolio members field, admin and member sections"""
|
|
admin_user_1 = User.objects.create(
|
|
username="testuser1",
|
|
first_name="Gerald",
|
|
last_name="Meoward",
|
|
title="Captain",
|
|
email="meaoward@gov.gov",
|
|
)
|
|
|
|
UserPortfolioPermission.objects.all().create(
|
|
user=admin_user_1, portfolio=self.portfolio, roles=[UserPortfolioRoleChoices.ORGANIZATION_ADMIN]
|
|
)
|
|
|
|
admin_user_2 = User.objects.create(
|
|
username="testuser2",
|
|
first_name="Arnold",
|
|
last_name="Poopy",
|
|
title="Major",
|
|
email="poopy@gov.gov",
|
|
)
|
|
|
|
UserPortfolioPermission.objects.all().create(
|
|
user=admin_user_2, portfolio=self.portfolio, roles=[UserPortfolioRoleChoices.ORGANIZATION_ADMIN]
|
|
)
|
|
|
|
admin_user_3 = User.objects.create(
|
|
username="testuser3",
|
|
first_name="Mad",
|
|
last_name="Max",
|
|
title="Road warrior",
|
|
email="madmax@gov.gov",
|
|
)
|
|
|
|
UserPortfolioPermission.objects.all().create(
|
|
user=admin_user_3, portfolio=self.portfolio, roles=[UserPortfolioRoleChoices.ORGANIZATION_MEMBER]
|
|
)
|
|
|
|
admin_user_4 = User.objects.create(
|
|
username="testuser4",
|
|
first_name="Agent",
|
|
last_name="Smith",
|
|
title="Program",
|
|
email="thematrix@gov.gov",
|
|
)
|
|
|
|
UserPortfolioPermission.objects.all().create(
|
|
user=admin_user_4,
|
|
portfolio=self.portfolio,
|
|
additional_permissions=[
|
|
UserPortfolioPermissionChoices.VIEW_PORTFOLIO,
|
|
UserPortfolioPermissionChoices.EDIT_REQUESTS,
|
|
],
|
|
)
|
|
|
|
display_admins = self.admin.display_admins(self.portfolio)
|
|
url = reverse("admin:registrar_userportfoliopermission_changelist") + f"?portfolio={self.portfolio.id}"
|
|
self.assertIn(f'<a href="{url}">2 administrators</a>', display_admins)
|
|
|
|
display_members = self.admin.display_members(self.portfolio)
|
|
self.assertIn(f'<a href="{url}">2 members</a>', display_members)
|
|
|
|
@less_console_noise_decorator
|
|
def test_senior_official_readonly_for_federal_org(self):
|
|
"""Test that senior_official field is readonly for federal organizations"""
|
|
request = self.factory.get("/")
|
|
request.user = self.superuser
|
|
|
|
# Create a federal portfolio
|
|
portfolio = Portfolio.objects.create(
|
|
organization_name="Test Federal Org",
|
|
organization_type=DomainRequest.OrganizationChoices.FEDERAL,
|
|
creator=self.superuser,
|
|
)
|
|
|
|
readonly_fields = self.admin.get_readonly_fields(request, portfolio)
|
|
self.assertIn("senior_official", readonly_fields)
|
|
|
|
# Change to non-federal org
|
|
portfolio.organization_type = DomainRequest.OrganizationChoices.CITY
|
|
readonly_fields = self.admin.get_readonly_fields(request, portfolio)
|
|
self.assertNotIn("senior_official", readonly_fields)
|
|
|
|
@less_console_noise_decorator
|
|
def test_senior_official_auto_assignment(self):
|
|
"""Test automatic senior official assignment based on organization type and federal agency"""
|
|
request = self.factory.get("/")
|
|
request.user = self.superuser
|
|
|
|
# Create a federal agency with a senior official
|
|
federal_agency = FederalAgency.objects.create(agency="Test Agency")
|
|
senior_official = SeniorOfficial.objects.create(
|
|
first_name="Test",
|
|
last_name="Official",
|
|
title="Some guy",
|
|
email="test@example.gov",
|
|
federal_agency=federal_agency,
|
|
)
|
|
|
|
# Create a federal portfolio
|
|
portfolio = Portfolio.objects.create(
|
|
organization_name="Test Federal Org",
|
|
organization_type=DomainRequest.OrganizationChoices.FEDERAL,
|
|
creator=self.superuser,
|
|
)
|
|
|
|
# Test that the federal org gets senior official from agency when federal
|
|
portfolio.federal_agency = federal_agency
|
|
self.admin.save_model(request, portfolio, form=None, change=False)
|
|
self.assertEqual(portfolio.senior_official, senior_official)
|
|
|
|
# Test non-federal org clears senior official when not city
|
|
portfolio.organization_type = DomainRequest.OrganizationChoices.CITY
|
|
self.admin.save_model(request, portfolio, form=None, change=True)
|
|
self.assertIsNone(portfolio.senior_official)
|
|
self.assertEqual(portfolio.federal_agency.agency, "Non-Federal Agency")
|
|
|
|
# Cleanup
|
|
senior_official.delete()
|
|
federal_agency.delete()
|
|
portfolio.delete()
|
|
|
|
|
|
class TestTransferUser(WebTest):
|
|
"""User transfer custom admin page"""
|
|
|
|
# csrf checks do not work well with WebTest.
|
|
# We disable them here.
|
|
csrf_checks = False
|
|
|
|
@classmethod
|
|
def setUpClass(cls):
|
|
super().setUpClass()
|
|
cls.site = AdminSite()
|
|
cls.superuser = create_superuser()
|
|
cls.admin = PortfolioAdmin(model=Portfolio, admin_site=cls.site)
|
|
cls.factory = RequestFactory()
|
|
|
|
def setUp(self):
|
|
self.app.set_user(self.superuser)
|
|
self.user1, _ = User.objects.get_or_create(
|
|
username="madmax", first_name="Max", last_name="Rokatanski", title="Road warrior"
|
|
)
|
|
self.user2, _ = User.objects.get_or_create(
|
|
username="furiosa", first_name="Furiosa", last_name="Jabassa", title="Imperator"
|
|
)
|
|
|
|
def tearDown(self):
|
|
Suborganization.objects.all().delete()
|
|
DomainInformation.objects.all().delete()
|
|
DomainRequest.objects.all().delete()
|
|
Domain.objects.all().delete()
|
|
Portfolio.objects.all().delete()
|
|
UserDomainRole.objects.all().delete()
|
|
|
|
@less_console_noise_decorator
|
|
def test_transfer_user_shows_current_and_selected_user_information(self):
|
|
"""Assert we pull the current user info and display it on the transfer page"""
|
|
completed_domain_request(user=self.user1, name="wasteland.gov")
|
|
domain_request = completed_domain_request(
|
|
user=self.user1, name="citadel.gov", status=DomainRequest.DomainRequestStatus.SUBMITTED
|
|
)
|
|
domain_request.status = DomainRequest.DomainRequestStatus.APPROVED
|
|
domain_request.save()
|
|
portfolio1 = Portfolio.objects.create(organization_name="Hotel California", creator=self.user2)
|
|
UserPortfolioPermission.objects.create(
|
|
user=self.user1, portfolio=portfolio1, roles=[UserPortfolioRoleChoices.ORGANIZATION_ADMIN]
|
|
)
|
|
portfolio2 = Portfolio.objects.create(organization_name="Tokyo Hotel", creator=self.user2)
|
|
UserPortfolioPermission.objects.create(
|
|
user=self.user2, portfolio=portfolio2, roles=[UserPortfolioRoleChoices.ORGANIZATION_ADMIN]
|
|
)
|
|
|
|
user_transfer_page = self.app.get(reverse("transfer_user", args=[self.user1.pk]))
|
|
|
|
self.assertContains(user_transfer_page, "madmax")
|
|
self.assertContains(user_transfer_page, "Max")
|
|
self.assertContains(user_transfer_page, "Rokatanski")
|
|
self.assertContains(user_transfer_page, "Road warrior")
|
|
self.assertContains(user_transfer_page, "wasteland.gov")
|
|
self.assertContains(user_transfer_page, "citadel.gov")
|
|
self.assertContains(user_transfer_page, "Hotel California")
|
|
|
|
select_form = user_transfer_page.forms[0]
|
|
select_form["selected_user"] = str(self.user2.id)
|
|
preview_result = select_form.submit()
|
|
|
|
self.assertContains(preview_result, "furiosa")
|
|
self.assertContains(preview_result, "Furiosa")
|
|
self.assertContains(preview_result, "Jabassa")
|
|
self.assertContains(preview_result, "Imperator")
|
|
self.assertContains(preview_result, "Tokyo Hotel")
|
|
|
|
@less_console_noise_decorator
|
|
def test_transfer_user_transfers_user_portfolio_roles(self):
|
|
"""Assert that a portfolio user role gets transferred"""
|
|
portfolio = Portfolio.objects.create(organization_name="Hotel California", creator=self.user2)
|
|
user_portfolio_permission = UserPortfolioPermission.objects.create(
|
|
user=self.user2, portfolio=portfolio, roles=[UserPortfolioRoleChoices.ORGANIZATION_ADMIN]
|
|
)
|
|
|
|
user_transfer_page = self.app.get(reverse("transfer_user", args=[self.user1.pk]))
|
|
|
|
submit_form = user_transfer_page.forms[1]
|
|
submit_form["selected_user"] = self.user2.pk
|
|
submit_form.submit()
|
|
|
|
user_portfolio_permission.refresh_from_db()
|
|
|
|
self.assertEquals(user_portfolio_permission.user, self.user1)
|
|
|
|
@less_console_noise_decorator
|
|
def test_transfer_user_transfers_user_portfolio_roles_no_error_when_duplicates(self):
|
|
"""Assert that duplicate portfolio user roles do not throw errors"""
|
|
portfolio1 = Portfolio.objects.create(organization_name="Hotel California", creator=self.user2)
|
|
UserPortfolioPermission.objects.create(
|
|
user=self.user1, portfolio=portfolio1, roles=[UserPortfolioRoleChoices.ORGANIZATION_ADMIN]
|
|
)
|
|
UserPortfolioPermission.objects.create(
|
|
user=self.user2, portfolio=portfolio1, roles=[UserPortfolioRoleChoices.ORGANIZATION_ADMIN]
|
|
)
|
|
|
|
with patch.object(messages, "error"):
|
|
user_transfer_page = self.app.get(reverse("transfer_user", args=[self.user1.pk]))
|
|
|
|
submit_form = user_transfer_page.forms[1]
|
|
submit_form["selected_user"] = self.user2.pk
|
|
submit_form.submit()
|
|
|
|
# Verify portfolio permissions remain valid for the original user
|
|
self.assertTrue(
|
|
UserPortfolioPermission.objects.filter(
|
|
user=self.user1, portfolio=portfolio1, roles=[UserPortfolioRoleChoices.ORGANIZATION_ADMIN]
|
|
).exists()
|
|
)
|
|
|
|
messages.error.assert_not_called()
|
|
|
|
@less_console_noise_decorator
|
|
def test_transfer_user_transfers_domain_request_creator_and_investigator(self):
|
|
"""Assert that domain request fields get transferred"""
|
|
domain_request = completed_domain_request(user=self.user2, name="wasteland.gov", investigator=self.user2)
|
|
|
|
self.assertEquals(domain_request.creator, self.user2)
|
|
self.assertEquals(domain_request.investigator, self.user2)
|
|
|
|
user_transfer_page = self.app.get(reverse("transfer_user", args=[self.user1.pk]))
|
|
submit_form = user_transfer_page.forms[1]
|
|
submit_form["selected_user"] = self.user2.pk
|
|
submit_form.submit()
|
|
domain_request.refresh_from_db()
|
|
|
|
self.assertEquals(domain_request.creator, self.user1)
|
|
self.assertEquals(domain_request.investigator, self.user1)
|
|
|
|
@less_console_noise_decorator
|
|
def test_transfer_user_transfers_domain_information_creator(self):
|
|
"""Assert that domain fields get transferred"""
|
|
domain_information, _ = DomainInformation.objects.get_or_create(creator=self.user2)
|
|
|
|
self.assertEquals(domain_information.creator, self.user2)
|
|
|
|
user_transfer_page = self.app.get(reverse("transfer_user", args=[self.user1.pk]))
|
|
submit_form = user_transfer_page.forms[1]
|
|
submit_form["selected_user"] = self.user2.pk
|
|
submit_form.submit()
|
|
domain_information.refresh_from_db()
|
|
|
|
self.assertEquals(domain_information.creator, self.user1)
|
|
|
|
@less_console_noise_decorator
|
|
def test_transfer_user_transfers_domain_role(self):
|
|
"""Assert that user domain role get transferred"""
|
|
domain_1, _ = Domain.objects.get_or_create(name="chrome.gov", state=Domain.State.READY)
|
|
domain_2, _ = Domain.objects.get_or_create(name="v8.gov", state=Domain.State.READY)
|
|
user_domain_role1, _ = UserDomainRole.objects.get_or_create(
|
|
user=self.user2, domain=domain_1, role=UserDomainRole.Roles.MANAGER
|
|
)
|
|
user_domain_role2, _ = UserDomainRole.objects.get_or_create(
|
|
user=self.user2, domain=domain_2, role=UserDomainRole.Roles.MANAGER
|
|
)
|
|
|
|
user_transfer_page = self.app.get(reverse("transfer_user", args=[self.user1.pk]))
|
|
submit_form = user_transfer_page.forms[1]
|
|
submit_form["selected_user"] = self.user2.pk
|
|
submit_form.submit()
|
|
user_domain_role1.refresh_from_db()
|
|
user_domain_role2.refresh_from_db()
|
|
|
|
self.assertEquals(user_domain_role1.user, self.user1)
|
|
self.assertEquals(user_domain_role2.user, self.user1)
|
|
|
|
@less_console_noise_decorator
|
|
def test_transfer_user_transfers_domain_role_no_error_when_duplicate(self):
|
|
"""Assert that duplicate user domain roles do not throw errors"""
|
|
domain_1, _ = Domain.objects.get_or_create(name="chrome.gov", state=Domain.State.READY)
|
|
domain_2, _ = Domain.objects.get_or_create(name="v8.gov", state=Domain.State.READY)
|
|
UserDomainRole.objects.get_or_create(user=self.user1, domain=domain_1, role=UserDomainRole.Roles.MANAGER)
|
|
UserDomainRole.objects.get_or_create(user=self.user2, domain=domain_1, role=UserDomainRole.Roles.MANAGER)
|
|
UserDomainRole.objects.get_or_create(user=self.user2, domain=domain_2, role=UserDomainRole.Roles.MANAGER)
|
|
|
|
with patch.object(messages, "error"):
|
|
|
|
user_transfer_page = self.app.get(reverse("transfer_user", args=[self.user1.pk]))
|
|
submit_form = user_transfer_page.forms[1]
|
|
submit_form["selected_user"] = self.user2.pk
|
|
submit_form.submit()
|
|
|
|
self.assertTrue(
|
|
UserDomainRole.objects.filter(
|
|
user=self.user1, domain=domain_1, role=UserDomainRole.Roles.MANAGER
|
|
).exists()
|
|
)
|
|
self.assertTrue(
|
|
UserDomainRole.objects.filter(
|
|
user=self.user1, domain=domain_2, role=UserDomainRole.Roles.MANAGER
|
|
).exists()
|
|
)
|
|
|
|
messages.error.assert_not_called()
|
|
|
|
@less_console_noise_decorator
|
|
def test_transfer_user_transfers_verified_by_staff_requestor(self):
|
|
"""Assert that verified by staff creator gets transferred"""
|
|
vip, _ = VerifiedByStaff.objects.get_or_create(requestor=self.user2, email="immortan.joe@citadel.com")
|
|
|
|
user_transfer_page = self.app.get(reverse("transfer_user", args=[self.user1.pk]))
|
|
submit_form = user_transfer_page.forms[1]
|
|
submit_form["selected_user"] = self.user2.pk
|
|
submit_form.submit()
|
|
vip.refresh_from_db()
|
|
|
|
self.assertEquals(vip.requestor, self.user1)
|
|
|
|
@less_console_noise_decorator
|
|
def test_transfer_user_deletes_old_user(self):
|
|
"""Assert that the slected user gets deleted"""
|
|
user_transfer_page = self.app.get(reverse("transfer_user", args=[self.user1.pk]))
|
|
submit_form = user_transfer_page.forms[1]
|
|
submit_form["selected_user"] = self.user2.pk
|
|
submit_form.submit()
|
|
# Refresh user2 from the database and check if it still exists
|
|
with self.assertRaises(User.DoesNotExist):
|
|
self.user2.refresh_from_db()
|
|
|
|
@less_console_noise_decorator
|
|
def test_transfer_user_throws_transfer_and_delete_success_messages(self):
|
|
"""Test that success messages for data transfer and user deletion are displayed."""
|
|
# Ensure the setup for VerifiedByStaff
|
|
VerifiedByStaff.objects.get_or_create(requestor=self.user2, email="immortan.joe@citadel.com")
|
|
|
|
# Access the transfer user page
|
|
user_transfer_page = self.app.get(reverse("transfer_user", args=[self.user1.pk]))
|
|
|
|
with patch("django.contrib.messages.success") as mock_success_message:
|
|
|
|
# Fill the form with the selected user and submit
|
|
submit_form = user_transfer_page.forms[1]
|
|
submit_form["selected_user"] = self.user2.pk
|
|
after_submit = submit_form.submit().follow()
|
|
|
|
self.assertContains(after_submit, "<h1>Change user</h1>")
|
|
|
|
mock_success_message.assert_any_call(
|
|
ANY,
|
|
(
|
|
"Data transferred successfully for the following objects: ['Changed requestor "
|
|
+ "from Furiosa Jabassa to Max Rokatanski on immortan.joe@citadel.com']"
|
|
),
|
|
)
|
|
|
|
mock_success_message.assert_any_call(ANY, f"Deleted {self.user2} {self.user2.username}")
|
|
|
|
@less_console_noise_decorator
|
|
def test_transfer_user_throws_error_message(self):
|
|
"""Test that an error message is thrown if the transfer fails."""
|
|
with patch(
|
|
"registrar.views.TransferUserView.transfer_related_fields_and_log", side_effect=Exception("Simulated Error")
|
|
):
|
|
with patch("django.contrib.messages.error") as mock_error:
|
|
# Access the transfer user page
|
|
user_transfer_page = self.app.get(reverse("transfer_user", args=[self.user1.pk]))
|
|
|
|
# Fill the form with the selected user and submit
|
|
submit_form = user_transfer_page.forms[1]
|
|
submit_form["selected_user"] = self.user2.pk
|
|
submit_form.submit().follow()
|
|
|
|
# Assert that the error message was called with the correct argument
|
|
mock_error.assert_called_once_with(ANY, "An error occurred during the transfer: Simulated Error")
|
|
|
|
@less_console_noise_decorator
|
|
def test_transfer_user_modal(self):
|
|
"""Assert modal on page"""
|
|
user_transfer_page = self.app.get(reverse("transfer_user", args=[self.user1.pk]))
|
|
self.assertContains(user_transfer_page, "This action cannot be undone.")
|