From ef8d8f17bf29a83ed3a18e2fa1c8b710be4d9836 Mon Sep 17 00:00:00 2001 From: zandercymatics <141044360+zandercymatics@users.noreply.github.com> Date: Thu, 2 Jan 2025 11:51:05 -0700 Subject: [PATCH] Add unit test for requesting entity page --- src/package-lock.json | 55 ++++++++++++++++++--- src/registrar/models/domain_request.py | 4 +- src/registrar/tests/test_admin_request.py | 44 ++++++----------- src/registrar/tests/test_views_portfolio.py | 40 +++++++++++++++ 4 files changed, 103 insertions(+), 40 deletions(-) diff --git a/src/package-lock.json b/src/package-lock.json index d78b5132f..e93413312 100644 --- a/src/package-lock.json +++ b/src/package-lock.json @@ -6921,6 +6921,16 @@ "validate-npm-package-license": "^3.0.1" } }, + "node_modules/normalize-package-data/node_modules/semver": { + "version": "5.7.2", + "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.2.tgz", + "integrity": "sha512-cBznnQ9KjJqU67B52RMC65CMarK2600WFnbkcaiwWq3xy/5haFJlshgnpjovMVJ+Hff49d8GEn0b87C5pDQ10g==", + "dev": true, + "license": "ISC", + "bin": { + "semver": "bin/semver" + } + }, "node_modules/normalize-path": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-3.0.0.tgz", @@ -7297,6 +7307,39 @@ "node": ">= 12" } }, + "node_modules/pa11y/node_modules/lru-cache": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz", + "integrity": "sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==", + "license": "ISC", + "dependencies": { + "yallist": "^4.0.0" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/pa11y/node_modules/semver": { + "version": "7.3.8", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.3.8.tgz", + "integrity": "sha512-NB1ctGL5rlHrPJtFDVIVzTyQylMLu9N9VICA6HSFJo8MCGVTMW6gfpicwKmmK/dAjTOrqu5l63JJOpDSrAis3A==", + "license": "ISC", + "dependencies": { + "lru-cache": "^6.0.0" + }, + "bin": { + "semver": "bin/semver.js" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/pa11y/node_modules/yallist": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", + "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==", + "license": "ISC" + }, "node_modules/parse-filepath": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/parse-filepath/-/parse-filepath-1.0.2.tgz", @@ -8845,15 +8888,13 @@ } }, "node_modules/semver": { - "version": "7.6.3", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.6.3.tgz", - "integrity": "sha512-oVekP1cKtI+CTDvHWYFUcMtsK/00wmAEfyqKfNdARm8u1wNVhSgaX7A8d4UuIlUI5e84iEwOhs7ZPYRmzU9U6A==", + "version": "6.3.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", + "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==", + "dev": true, "license": "ISC", "bin": { "semver": "bin/semver.js" - }, - "engines": { - "node": ">=10" } }, "node_modules/semver-greatest-satisfied-range": { @@ -10623,4 +10664,4 @@ } } } -} +} \ No newline at end of file diff --git a/src/registrar/models/domain_request.py b/src/registrar/models/domain_request.py index 533a22ced..3d3aac769 100644 --- a/src/registrar/models/domain_request.py +++ b/src/registrar/models/domain_request.py @@ -705,9 +705,7 @@ class DomainRequest(TimeStampedModel): "This suborganization already exists. " "Choose a new name, or select it directly if you would like to use it." ) - errors = { - "requested_suborganization": ValidationError(msg) - } + errors = {"requested_suborganization": ValidationError(msg)} raise ValidationError(errors) elif self.portfolio and not self.sub_organization: # You cannot create a new suborganization without these fields diff --git a/src/registrar/tests/test_admin_request.py b/src/registrar/tests/test_admin_request.py index a294c127f..8a2f49ee7 100644 --- a/src/registrar/tests/test_admin_request.py +++ b/src/registrar/tests/test_admin_request.py @@ -100,17 +100,11 @@ class TestDomainRequestAdmin(MockEppLib): 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 - ) - + portfolio = Portfolio.objects.create(organization_name="Test Portfolio", creator=self.superuser) + # Create an existing suborganization - Suborganization.objects.create( - name="Existing Suborg", - portfolio=portfolio - ) - + 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( @@ -124,11 +118,8 @@ class TestDomainRequestAdmin(MockEppLib): # 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) - ) + + 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" @@ -138,11 +129,8 @@ class TestDomainRequestAdmin(MockEppLib): @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 - ) - + 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", @@ -153,21 +141,17 @@ class TestDomainRequestAdmin(MockEppLib): # 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) - ) - + 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." + 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 @@ -178,7 +162,7 @@ class TestDomainRequestAdmin(MockEppLib): 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 diff --git a/src/registrar/tests/test_views_portfolio.py b/src/registrar/tests/test_views_portfolio.py index 01383ae77..2f02f9ed9 100644 --- a/src/registrar/tests/test_views_portfolio.py +++ b/src/registrar/tests/test_views_portfolio.py @@ -2268,6 +2268,46 @@ class TestRequestingEntity(WebTest): User.objects.all().delete() super().tearDown() + @less_console_noise_decorator + @override_flag("organization_feature", active=True) + @override_flag("organization_requests", active=True) + def test_form_validates_duplicate_suborganization(self): + """Tests that form validation prevents duplicate suborganization names within the same portfolio""" + # Create an existing suborganization + suborganization = Suborganization.objects.create(name="Existing Suborg", portfolio=self.portfolio) + + # Start the domain request process + response = self.app.get(reverse("domain-request:start")) + session_id = self.app.cookies[settings.SESSION_COOKIE_NAME] + + # Navigate past the intro page + self.app.set_cookie(settings.SESSION_COOKIE_NAME, session_id) + form = response.forms[0] + response = form.submit().follow() + + # Fill out the requesting entity form + form = response.forms[0] + form["portfolio_requesting_entity-requesting_entity_is_suborganization"] = "True" + form["portfolio_requesting_entity-is_requesting_new_suborganization"] = "True" + form["portfolio_requesting_entity-requested_suborganization"] = suborganization.name.lower() + form["portfolio_requesting_entity-suborganization_city"] = "Eggnog" + form["portfolio_requesting_entity-suborganization_state_territory"] = DomainRequest.StateTerritoryChoices.OHIO + + # Submit form and verify error + session_id = self.app.cookies[settings.SESSION_COOKIE_NAME] + self.app.set_cookie(settings.SESSION_COOKIE_NAME, session_id) + response = form.submit() + self.assertContains(response, "This suborganization already exists") + + # Test that a different name is allowed + form["portfolio_requesting_entity-requested_suborganization"] = "New Suborg" + session_id = self.app.cookies[settings.SESSION_COOKIE_NAME] + self.app.set_cookie(settings.SESSION_COOKIE_NAME, session_id) + response = form.submit().follow() + + # Verify successful submission by checking we're on the next page + self.assertContains(response, "Current websites") + @override_flag("organization_feature", active=True) @override_flag("organization_requests", active=True) @less_console_noise_decorator