mirror of
https://github.com/cisagov/manage.get.gov.git
synced 2025-05-17 18:09:25 +02:00
Add unit tests
This commit is contained in:
parent
b7b9f96f7a
commit
610d00b7a0
6 changed files with 119 additions and 12 deletions
|
@ -2003,6 +2003,9 @@ class DomainRequestAdmin(ListHeaderAdmin, ImportExportModelAdmin):
|
||||||
|
|
||||||
custom_requested_domain.admin_order_field = "requested_domain__name" # type: ignore
|
custom_requested_domain.admin_order_field = "requested_domain__name" # type: ignore
|
||||||
|
|
||||||
|
# ------ Converted fields ------
|
||||||
|
# These fields map to @Property methods and
|
||||||
|
# require these custom definitions to work properly
|
||||||
@admin.display(description=_("Generic Org Type"))
|
@admin.display(description=_("Generic Org Type"))
|
||||||
def converted_generic_org_type(self, obj):
|
def converted_generic_org_type(self, obj):
|
||||||
return obj.converted_generic_org_type_display
|
return obj.converted_generic_org_type_display
|
||||||
|
|
|
@ -645,7 +645,6 @@ function handleSuborgFieldsAndButtons() {
|
||||||
const suborganizationCity = document.getElementById("id_suborganization_city");
|
const suborganizationCity = document.getElementById("id_suborganization_city");
|
||||||
const suborganizationStateTerritory = document.getElementById("id_suborganization_state_territory");
|
const suborganizationStateTerritory = document.getElementById("id_suborganization_state_territory");
|
||||||
const rejectButton = document.querySelector("#clear-requested-suborganization");
|
const rejectButton = document.querySelector("#clear-requested-suborganization");
|
||||||
console.log("test12345678")
|
|
||||||
|
|
||||||
// Ensure that every variable is present before proceeding
|
// Ensure that every variable is present before proceeding
|
||||||
if (!requestedSuborganizationField || !suborganizationCity || !suborganizationStateTerritory || !rejectButton) {
|
if (!requestedSuborganizationField || !suborganizationCity || !suborganizationStateTerritory || !rejectButton) {
|
||||||
|
|
|
@ -495,6 +495,7 @@ export function handlePortfolioSelection(
|
||||||
if (requestedSuborganizationField) hideElement(requestedSuborganizationField);
|
if (requestedSuborganizationField) hideElement(requestedSuborganizationField);
|
||||||
if (suborganizationCity) hideElement(suborganizationCity);
|
if (suborganizationCity) hideElement(suborganizationCity);
|
||||||
if (suborganizationStateTerritory) hideElement(suborganizationStateTerritory);
|
if (suborganizationStateTerritory) hideElement(suborganizationStateTerritory);
|
||||||
|
|
||||||
// == LOGIC FOR THE DOMAIN REQUEST PAGE == //
|
// == LOGIC FOR THE DOMAIN REQUEST PAGE == //
|
||||||
if (rejectSuborganizationButton) hideElement(rejectSuborganizationButton);
|
if (rejectSuborganizationButton) hideElement(rejectSuborganizationButton);
|
||||||
}
|
}
|
||||||
|
|
|
@ -244,7 +244,6 @@ class Domain(TimeStampedModel, DomainHelper):
|
||||||
is called in the validate function on the request/domain page
|
is called in the validate function on the request/domain page
|
||||||
|
|
||||||
throws- RegistryError or InvalidDomainError"""
|
throws- RegistryError or InvalidDomainError"""
|
||||||
return True
|
|
||||||
if not cls.string_could_be_domain(domain):
|
if not cls.string_could_be_domain(domain):
|
||||||
logger.warning("Not a valid domain: %s" % str(domain))
|
logger.warning("Not a valid domain: %s" % str(domain))
|
||||||
# throw invalid domain error so that it can be caught in
|
# throw invalid domain error so that it can be caught in
|
||||||
|
|
|
@ -697,10 +697,18 @@ class DomainRequest(TimeStampedModel):
|
||||||
portfolio__isnull=False,
|
portfolio__isnull=False,
|
||||||
).exists()
|
).exists()
|
||||||
):
|
):
|
||||||
raise ValidationError(
|
# Add a field-level error to requested_suborganization.
|
||||||
|
# To pass in field-specific errors, we need to embed a dict of
|
||||||
|
# field: validationerror then pass that into a validation error itself.
|
||||||
|
# This is slightly confusing, but it just adds it at that level.
|
||||||
|
msg = (
|
||||||
"This suborganization already exists. "
|
"This suborganization already exists. "
|
||||||
"Choose a new name, or select it directly if you would like to use it."
|
"Choose a new name, or select it directly if you would like to use it."
|
||||||
)
|
)
|
||||||
|
errors = {
|
||||||
|
"requested_suborganization": ValidationError(msg)
|
||||||
|
}
|
||||||
|
raise ValidationError(errors)
|
||||||
elif self.portfolio and not self.sub_organization:
|
elif self.portfolio and not self.sub_organization:
|
||||||
# You cannot create a new suborganization without these fields
|
# You cannot create a new suborganization without these fields
|
||||||
required_suborg_fields = {
|
required_suborg_fields = {
|
||||||
|
@ -708,16 +716,16 @@ class DomainRequest(TimeStampedModel):
|
||||||
"suborganization_city": self.suborganization_city,
|
"suborganization_city": self.suborganization_city,
|
||||||
"suborganization_state_territory": self.suborganization_state_territory,
|
"suborganization_state_territory": self.suborganization_state_territory,
|
||||||
}
|
}
|
||||||
|
# If at least one value is populated, enforce a all-or-nothing rule
|
||||||
if any(bool(value) for value in required_suborg_fields.values()):
|
if any(bool(value) for value in required_suborg_fields.values()):
|
||||||
# Find which fields are empty
|
# Find which fields are empty and throw an error on the field
|
||||||
errors_dict = {
|
errors = {}
|
||||||
field_name: [f"This field is required when creating a new suborganization."]
|
for field_name, value in required_suborg_fields.items():
|
||||||
for field_name, value in required_suborg_fields.items()
|
if not value:
|
||||||
if not value
|
errors[field_name] = ValidationError(
|
||||||
}
|
"This field is required when creating a new suborganization.",
|
||||||
# Adds a validation error to each missing field
|
)
|
||||||
raise ValidationError({k: ValidationError(v, code="required") for k, v in errors_dict.items()})
|
raise ValidationError(errors)
|
||||||
|
|
||||||
def save(self, *args, **kwargs):
|
def save(self, *args, **kwargs):
|
||||||
"""Save override for custom properties"""
|
"""Save override for custom properties"""
|
||||||
|
|
|
@ -1,5 +1,7 @@
|
||||||
from datetime import datetime
|
from datetime import datetime
|
||||||
|
from django.forms import ValidationError
|
||||||
from django.utils import timezone
|
from django.utils import timezone
|
||||||
|
from waffle.testutils import override_flag
|
||||||
import re
|
import re
|
||||||
from django.test import RequestFactory, Client, TestCase, override_settings
|
from django.test import RequestFactory, Client, TestCase, override_settings
|
||||||
from django.contrib.admin.sites import AdminSite
|
from django.contrib.admin.sites import AdminSite
|
||||||
|
@ -25,6 +27,7 @@ from registrar.models import (
|
||||||
Portfolio,
|
Portfolio,
|
||||||
AllowedEmail,
|
AllowedEmail,
|
||||||
)
|
)
|
||||||
|
from registrar.models.suborganization import Suborganization
|
||||||
from .common import (
|
from .common import (
|
||||||
MockSESClient,
|
MockSESClient,
|
||||||
completed_domain_request,
|
completed_domain_request,
|
||||||
|
@ -82,6 +85,7 @@ class TestDomainRequestAdmin(MockEppLib):
|
||||||
Contact.objects.all().delete()
|
Contact.objects.all().delete()
|
||||||
Website.objects.all().delete()
|
Website.objects.all().delete()
|
||||||
SeniorOfficial.objects.all().delete()
|
SeniorOfficial.objects.all().delete()
|
||||||
|
Suborganization.objects.all().delete()
|
||||||
Portfolio.objects.all().delete()
|
Portfolio.objects.all().delete()
|
||||||
self.mock_client.EMAILS_SENT.clear()
|
self.mock_client.EMAILS_SENT.clear()
|
||||||
|
|
||||||
|
@ -91,6 +95,99 @@ class TestDomainRequestAdmin(MockEppLib):
|
||||||
User.objects.all().delete()
|
User.objects.all().delete()
|
||||||
AllowedEmail.objects.all().delete()
|
AllowedEmail.objects.all().delete()
|
||||||
|
|
||||||
|
@override_flag("organization_feature", active=True)
|
||||||
|
@less_console_noise_decorator
|
||||||
|
def test_clean_validates_duplicate_suborganization(self):
|
||||||
|
"""Tests that clean() prevents duplicate suborganization names within the same portfolio"""
|
||||||
|
# Create a portfolio and existing suborganization
|
||||||
|
portfolio = Portfolio.objects.create(
|
||||||
|
organization_name="Test Portfolio",
|
||||||
|
creator=self.superuser
|
||||||
|
)
|
||||||
|
|
||||||
|
# Create an existing suborganization
|
||||||
|
Suborganization.objects.create(
|
||||||
|
name="Existing Suborg",
|
||||||
|
portfolio=portfolio
|
||||||
|
)
|
||||||
|
|
||||||
|
# Create a domain request trying to use the same suborganization name
|
||||||
|
# (intentionally lowercase)
|
||||||
|
domain_request = completed_domain_request(
|
||||||
|
name="test1234.gov",
|
||||||
|
portfolio=portfolio,
|
||||||
|
requested_suborganization="existing suborg",
|
||||||
|
suborganization_city="Rome",
|
||||||
|
suborganization_state_territory=DomainRequest.StateTerritoryChoices.OHIO,
|
||||||
|
)
|
||||||
|
|
||||||
|
# Assert that the validation error is raised
|
||||||
|
with self.assertRaises(ValidationError) as err:
|
||||||
|
domain_request.clean()
|
||||||
|
|
||||||
|
self.assertIn(
|
||||||
|
"This suborganization already exists",
|
||||||
|
str(err.exception)
|
||||||
|
)
|
||||||
|
|
||||||
|
# Test that a different name is allowed. Should not raise a error.
|
||||||
|
domain_request.requested_suborganization = "New Suborg"
|
||||||
|
domain_request.clean()
|
||||||
|
|
||||||
|
@less_console_noise_decorator
|
||||||
|
@override_flag("organization_feature", active=True)
|
||||||
|
def test_clean_validates_partial_suborganization_fields(self):
|
||||||
|
"""Tests that clean() enforces all-or-nothing rule for suborganization fields"""
|
||||||
|
portfolio = Portfolio.objects.create(
|
||||||
|
organization_name="Test Portfolio",
|
||||||
|
creator=self.superuser
|
||||||
|
)
|
||||||
|
|
||||||
|
# Create domain request with only city filled out
|
||||||
|
domain_request = completed_domain_request(
|
||||||
|
name="test1234.gov",
|
||||||
|
portfolio=portfolio,
|
||||||
|
suborganization_city="Test City",
|
||||||
|
)
|
||||||
|
|
||||||
|
# Assert validation error is raised with correct missing fields
|
||||||
|
with self.assertRaises(ValidationError) as err:
|
||||||
|
domain_request.clean()
|
||||||
|
|
||||||
|
error_dict = err.exception.error_dict
|
||||||
|
expected_missing = ["requested_suborganization", "suborganization_state_territory"]
|
||||||
|
|
||||||
|
# Verify correct fields are flagged as required
|
||||||
|
self.assertEqual(
|
||||||
|
sorted(error_dict.keys()),
|
||||||
|
sorted(expected_missing)
|
||||||
|
)
|
||||||
|
|
||||||
|
# Verify error message
|
||||||
|
for field in expected_missing:
|
||||||
|
self.assertEqual(
|
||||||
|
str(error_dict[field][0].message),
|
||||||
|
"This field is required when creating a new suborganization."
|
||||||
|
)
|
||||||
|
|
||||||
|
# When all data is passed in, this should validate correctly
|
||||||
|
domain_request.requested_suborganization = "Complete Suborg"
|
||||||
|
domain_request.suborganization_state_territory = DomainRequest.StateTerritoryChoices.OHIO
|
||||||
|
# Assert that no ValidationError is raised
|
||||||
|
try:
|
||||||
|
domain_request.clean()
|
||||||
|
except ValidationError as e:
|
||||||
|
self.fail(f"ValidationError was raised unexpectedly: {e}")
|
||||||
|
|
||||||
|
# Also ensure that no validation error is raised if nothing is passed in at all
|
||||||
|
domain_request.suborganization_city = None
|
||||||
|
domain_request.requested_suborganization = None
|
||||||
|
domain_request.suborganization_state_territory = None
|
||||||
|
try:
|
||||||
|
domain_request.clean()
|
||||||
|
except ValidationError as e:
|
||||||
|
self.fail(f"ValidationError was raised unexpectedly: {e}")
|
||||||
|
|
||||||
@less_console_noise_decorator
|
@less_console_noise_decorator
|
||||||
def test_domain_request_senior_official_is_alphabetically_sorted(self):
|
def test_domain_request_senior_official_is_alphabetically_sorted(self):
|
||||||
"""Tests if the senior offical dropdown is alphanetically sorted in the django admin display"""
|
"""Tests if the senior offical dropdown is alphanetically sorted in the django admin display"""
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue