From 3ed4c0e4fbbbfdf5b69d7a6d4c1af0d5ec90a61c Mon Sep 17 00:00:00 2001 From: Erin <121973038+erinysong@users.noreply.github.com> Date: Tue, 17 Oct 2023 08:37:03 -0700 Subject: [PATCH 1/5] Update availability API to use EPP availability check --- src/! | 1 + src/api/tests/test_available.py | 78 +++++++++++++++++++++++-------- src/api/views.py | 6 +-- src/registrar/tests/common.py | 37 ++++++++++++++- src/registrar/tests/test_forms.py | 11 ++++- 5 files changed, 108 insertions(+), 25 deletions(-) create mode 100644 src/! diff --git a/src/! b/src/! new file mode 100644 index 000000000..19765bd50 --- /dev/null +++ b/src/! @@ -0,0 +1 @@ +null diff --git a/src/api/tests/test_available.py b/src/api/tests/test_available.py index 0bbe01f03..3d01228c3 100644 --- a/src/api/tests/test_available.py +++ b/src/api/tests/test_available.py @@ -7,21 +7,34 @@ from django.test import TestCase, RequestFactory from ..views import available, _domains, in_domains from .common import less_console_noise +from registrar.tests.common import MockEppLib +from unittest.mock import MagicMock, patch, call + +from epplibwrapper import ( + commands, + common, + extensions, + responses, + RegistryError, + ErrorCode, +) API_BASE_PATH = "/api/v1/available/" +from registrar.models import Domain - -class AvailableViewTest(TestCase): +class AvailableViewTest(MockEppLib): """Test that the view function works as expected.""" def setUp(self): + super().setUp() self.user = get_user_model().objects.create(username="username") self.factory = RequestFactory() def test_view_function(self): request = self.factory.get(API_BASE_PATH + "test.gov") request.user = self.user + response = available(request, domain="test.gov") # has the right text in it self.assertContains(response, "available") @@ -29,28 +42,43 @@ class AvailableViewTest(TestCase): response_object = json.loads(response.content) self.assertIn("available", response_object) - def test_domain_list(self): - """Test the domain list that is returned from Github. + def test_makes_calls(self): + gsa_available = in_domains("gsa.gov") + igorville_available = in_domains("igorvilleremixed.gov") - This does not mock out the external file, it is actually fetched from - the internet. - """ - domains = _domains() - self.assertIn("gsa.gov", domains) - # entries are all lowercase so GSA.GOV is not in the set - self.assertNotIn("GSA.GOV", domains) - self.assertNotIn("igorvilleremixed.gov", domains) - # all the entries have dots - self.assertNotIn("gsa", domains) + self.mockedSendFunction.assert_has_calls( + [ + call( + commands.CheckDomain( + ["gsa.gov"], + ), + cleaned=True, + ), + call( + commands.CheckDomain( + ["igorvilleremixed.gov"], + ), + cleaned=True, + ) + ] + ) def test_in_domains(self): - self.assertTrue(in_domains("gsa.gov")) + gsa_available = in_domains("gsa.gov") + gsa_caps_available = in_domains("GSA.gov") + igorville_available = in_domains("igorvilleremixed.gov") + + self.assertTrue(gsa_available) # input is lowercased so GSA.GOV should be found - self.assertTrue(in_domains("GSA.GOV")) + self.assertTrue(gsa_caps_available) # This domain should not have been registered - self.assertFalse(in_domains("igorvilleremixed.gov")) - + self.assertFalse(igorville_available) + def test_in_domains_dotgov(self): + gsa_available = in_domains("gsa.gov") + gsa_caps_available = in_domains("GSA.gov") + igorville_available = in_domains("igorvilleremixed.gov") + """Domain searches work without trailing .gov""" self.assertTrue(in_domains("gsa")) # input is lowercased so GSA.GOV should be found @@ -58,6 +86,14 @@ class AvailableViewTest(TestCase): # This domain should not have been registered self.assertFalse(in_domains("igorvilleremixed")) + def test_in_domains_capitalized(self): + gsa_available = in_domains("gsa.gov") + capitalized_gsa_available = in_domains("GSA.gov") + + """Domain searches work without case sensitivity""" + self.assertTrue(in_domains("gsa.gov")) + self.assertTrue(in_domains("GSA.gov")) + def test_not_available_domain(self): """gsa.gov is not available""" request = self.factory.get(API_BASE_PATH + "gsa.gov") @@ -86,13 +122,17 @@ class AvailableViewTest(TestCase): request.user = self.user response = available(request, domain=bad_string) self.assertFalse(json.loads(response.content)["available"]) + # domain set to raise error successfully raises error + with self.assertRaises(RegistryError): + error_domain_available = available(request, "errordomain.gov") -class AvailableAPITest(TestCase): +class AvailableAPITest(MockEppLib): """Test that the API can be called as expected.""" def setUp(self): + super().setUp() self.user = get_user_model().objects.create(username="username") def test_available_get(self): diff --git a/src/api/views.py b/src/api/views.py index e19e060ef..02e419a91 100644 --- a/src/api/views.py +++ b/src/api/views.py @@ -59,12 +59,12 @@ def in_domains(domain): given domain doesn't end with .gov, ".gov" is added when looking for a match. """ - domain = domain.lower() + Domain = apps.get_model("registrar.Domain") if domain.endswith(".gov"): - return domain.lower() in _domains() + return Domain.available(domain) else: # domain search string doesn't end with .gov, add it on here - return (domain + ".gov") in _domains() + return Domain.available(domain + ".gov") @require_http_methods(["GET"]) diff --git a/src/registrar/tests/common.py b/src/registrar/tests/common.py index b8fea7f93..0144738e2 100644 --- a/src/registrar/tests/common.py +++ b/src/registrar/tests/common.py @@ -30,6 +30,7 @@ from epplibwrapper import ( info, RegistryError, ErrorCode, + responses, ) logger = logging.getLogger(__name__) @@ -824,7 +825,41 @@ class MockEppLib(TestCase): raise RegistryError( code=ErrorCode.OBJECT_ASSOCIATION_PROHIBITS_OPERATION ) - + elif isinstance(_request, commands.CheckDomain): + if "gsa.gov" in getattr(_request, "names", None): + return MagicMock( + res_data=[ + responses.check.CheckDomainResultData( + name="gsa.gov", avail=True, reason=None + ), + ] + ) + elif "GSA.gov" in getattr(_request, "names", None): + return MagicMock( + res_data=[ + responses.check.CheckDomainResultData( + name="GSA.gov", avail=True, reason=None + ), + ] + ) + elif "igorvilleremixed.gov" in getattr(_request, "names", None): + return MagicMock( + res_data=[ + responses.check.CheckDomainResultData( + name="igorvilleremixed.gov", avail=False, reason=None + ), + ] + ) + elif "errordomain.gov" in getattr(_request, "names", None): + raise RegistryError("Registry cannot find domain availability.") + else: + return MagicMock( + res_data=[ + responses.check.CheckDomainResultData( + name="domainnotfound.gov", avail=False, reason="In Use" + ) + ], + ) return MagicMock(res_data=[self.mockDataInfoHosts]) def setUp(self): diff --git a/src/registrar/tests/test_forms.py b/src/registrar/tests/test_forms.py index 95be195ba..4b1aeb12c 100644 --- a/src/registrar/tests/test_forms.py +++ b/src/registrar/tests/test_forms.py @@ -1,6 +1,6 @@ """Test form validation requirements.""" -from django.test import TestCase +from django.test import TestCase, RequestFactory from registrar.forms.application_wizard import ( CurrentSitesForm, @@ -16,9 +16,16 @@ from registrar.forms.application_wizard import ( AboutYourOrganizationForm, ) from registrar.forms.domain import ContactForm +from registrar.tests.common import MockEppLib +from django.contrib.auth import get_user_model -class TestFormValidation(TestCase): +class TestFormValidation(MockEppLib): + def setUp(self): + super().setUp() + self.user = get_user_model().objects.create(username="username") + self.factory = RequestFactory() + def test_org_contact_zip_invalid(self): form = OrganizationContactForm(data={"zipcode": "nah"}) self.assertEqual( From dcaaa7099bd8ec1bdb22a18bae46b7e69819974e Mon Sep 17 00:00:00 2001 From: Erin <121973038+erinysong@users.noreply.github.com> Date: Tue, 17 Oct 2023 08:53:04 -0700 Subject: [PATCH 2/5] Delete unknown ! file added --- src/! | 1 - 1 file changed, 1 deletion(-) delete mode 100644 src/! diff --git a/src/! b/src/! deleted file mode 100644 index 19765bd50..000000000 --- a/src/! +++ /dev/null @@ -1 +0,0 @@ -null From 981af109dff15247c62144d779f8dd0838544b5e Mon Sep 17 00:00:00 2001 From: Erin <121973038+erinysong@users.noreply.github.com> Date: Tue, 17 Oct 2023 14:00:34 -0700 Subject: [PATCH 3/5] Fix subset of linter errors --- src/api/tests/test_available.py | 50 ++++++++++------------------- src/registrar/tests/common.py | 57 +++++++++++++-------------------- 2 files changed, 40 insertions(+), 67 deletions(-) diff --git a/src/api/tests/test_available.py b/src/api/tests/test_available.py index 3d01228c3..9eab17bf7 100644 --- a/src/api/tests/test_available.py +++ b/src/api/tests/test_available.py @@ -3,24 +3,20 @@ import json from django.contrib.auth import get_user_model -from django.test import TestCase, RequestFactory +from django.test import RequestFactory -from ..views import available, _domains, in_domains +from ..views import available, in_domains from .common import less_console_noise from registrar.tests.common import MockEppLib -from unittest.mock import MagicMock, patch, call +from unittest.mock import call from epplibwrapper import ( commands, - common, - extensions, - responses, RegistryError, - ErrorCode, ) API_BASE_PATH = "/api/v1/available/" -from registrar.models import Domain + class AvailableViewTest(MockEppLib): @@ -34,7 +30,6 @@ class AvailableViewTest(MockEppLib): def test_view_function(self): request = self.factory.get(API_BASE_PATH + "test.gov") request.user = self.user - response = available(request, domain="test.gov") # has the right text in it self.assertContains(response, "available") @@ -42,10 +37,12 @@ class AvailableViewTest(MockEppLib): response_object = json.loads(response.content) self.assertIn("available", response_object) - def test_makes_calls(self): + def test_in_domains_makes_calls_(self): + """Domain searches successfully make correct mock EPP calls""" gsa_available = in_domains("gsa.gov") igorville_available = in_domains("igorvilleremixed.gov") + """Domain searches successfully make mock EPP calls""" self.mockedSendFunction.assert_has_calls( [ call( @@ -59,26 +56,20 @@ class AvailableViewTest(MockEppLib): ["igorvilleremixed.gov"], ), cleaned=True, - ) + ), ] ) - - def test_in_domains(self): - gsa_available = in_domains("gsa.gov") - gsa_caps_available = in_domains("GSA.gov") - igorville_available = in_domains("igorvilleremixed.gov") - + """Domain searches return correct availability results""" self.assertTrue(gsa_available) - # input is lowercased so GSA.GOV should be found - self.assertTrue(gsa_caps_available) - # This domain should not have been registered self.assertFalse(igorville_available) - - def test_in_domains_dotgov(self): - gsa_available = in_domains("gsa.gov") - gsa_caps_available = in_domains("GSA.gov") - igorville_available = in_domains("igorvilleremixed.gov") + def test_in_domains_capitalized(self): + """Domain searches work without case sensitivity""" + self.assertTrue(in_domains("gsa.gov")) + # input is lowercased so GSA.GOV should be found + self.assertTrue(in_domains("GSA.gov")) + + def test_in_domains_dotgov(self): """Domain searches work without trailing .gov""" self.assertTrue(in_domains("gsa")) # input is lowercased so GSA.GOV should be found @@ -86,14 +77,6 @@ class AvailableViewTest(MockEppLib): # This domain should not have been registered self.assertFalse(in_domains("igorvilleremixed")) - def test_in_domains_capitalized(self): - gsa_available = in_domains("gsa.gov") - capitalized_gsa_available = in_domains("GSA.gov") - - """Domain searches work without case sensitivity""" - self.assertTrue(in_domains("gsa.gov")) - self.assertTrue(in_domains("GSA.gov")) - def test_not_available_domain(self): """gsa.gov is not available""" request = self.factory.get(API_BASE_PATH + "gsa.gov") @@ -125,6 +108,7 @@ class AvailableViewTest(MockEppLib): # domain set to raise error successfully raises error with self.assertRaises(RegistryError): error_domain_available = available(request, "errordomain.gov") + self.assertFalse(json.loads(error_domain_available.content)["available"]) class AvailableAPITest(MockEppLib): diff --git a/src/registrar/tests/common.py b/src/registrar/tests/common.py index 0144738e2..32117e2db 100644 --- a/src/registrar/tests/common.py +++ b/src/registrar/tests/common.py @@ -761,6 +761,28 @@ class MockEppLib(TestCase): return MagicMock(res_data=[self.infoDomainThreeHosts]) return MagicMock(res_data=[self.mockDataInfoDomain]) + def _mockDomainName(self, _name, _avail=False): + return MagicMock( + res_data=[ + responses.check.CheckDomainResultData( + name=_name, avail=_avail, reason=None + ), + ] + ) + + def _handleCheckDomain(self, _request): + print(getattr(_request, "names", None)) + if "gsa.gov" in getattr(_request, "names", None): + return self._mockDomainName("gsa.gov", True) + elif "GSA.gov" in getattr(_request, "names", None): + return self._mockDomainName("GSA.gov", True) + elif "igorvilleremixed.gov" in getattr(_request, "names", None): + return self._mockDomainName("igorvilleremixed.gov", False) + elif "errordomain.gov" in getattr(_request, "names", None): + raise RegistryError("Registry cannot find domain availability.") + else: + return self._mockDomainName("domainnotfound.gov", False) + def mockSend(self, _request, cleaned): """Mocks the registry.send function used inside of domain.py registry is imported from epplibwrapper @@ -826,40 +848,7 @@ class MockEppLib(TestCase): code=ErrorCode.OBJECT_ASSOCIATION_PROHIBITS_OPERATION ) elif isinstance(_request, commands.CheckDomain): - if "gsa.gov" in getattr(_request, "names", None): - return MagicMock( - res_data=[ - responses.check.CheckDomainResultData( - name="gsa.gov", avail=True, reason=None - ), - ] - ) - elif "GSA.gov" in getattr(_request, "names", None): - return MagicMock( - res_data=[ - responses.check.CheckDomainResultData( - name="GSA.gov", avail=True, reason=None - ), - ] - ) - elif "igorvilleremixed.gov" in getattr(_request, "names", None): - return MagicMock( - res_data=[ - responses.check.CheckDomainResultData( - name="igorvilleremixed.gov", avail=False, reason=None - ), - ] - ) - elif "errordomain.gov" in getattr(_request, "names", None): - raise RegistryError("Registry cannot find domain availability.") - else: - return MagicMock( - res_data=[ - responses.check.CheckDomainResultData( - name="domainnotfound.gov", avail=False, reason="In Use" - ) - ], - ) + return self._handleCheckDomain(_request) return MagicMock(res_data=[self.mockDataInfoHosts]) def setUp(self): From 7d6155de1340a0dfba51b87af878200f39502c0f Mon Sep 17 00:00:00 2001 From: Erin <121973038+erinysong@users.noreply.github.com> Date: Tue, 17 Oct 2023 16:06:57 -0700 Subject: [PATCH 4/5] Refactor mockSend to match linter --- src/registrar/tests/common.py | 98 ++++++++++++++++++----------------- 1 file changed, 50 insertions(+), 48 deletions(-) diff --git a/src/registrar/tests/common.py b/src/registrar/tests/common.py index 32117e2db..61444d37f 100644 --- a/src/registrar/tests/common.py +++ b/src/registrar/tests/common.py @@ -769,9 +769,8 @@ class MockEppLib(TestCase): ), ] ) - + def _handleCheckDomain(self, _request): - print(getattr(_request, "names", None)) if "gsa.gov" in getattr(_request, "names", None): return self._mockDomainName("gsa.gov", True) elif "GSA.gov" in getattr(_request, "names", None): @@ -788,28 +787,8 @@ class MockEppLib(TestCase): registry is imported from epplibwrapper returns objects that simulate what would be in a epp response but only relevant pieces for tests""" - if isinstance(_request, commands.InfoDomain): - return self._getattrInfoDomain(_request) - - elif isinstance(_request, commands.InfoContact): - mocked_result: info.InfoContactResultData - - # For testing contact types - match getattr(_request, "id", None): - case "securityContact": - mocked_result = self.mockSecurityContact - case "technicalContact": - mocked_result = self.mockTechnicalContact - case "adminContact": - mocked_result = self.mockAdministrativeContact - case "regContact": - mocked_result = self.mockRegistrantContact - case _: - # Default contact return - mocked_result = self.mockDataInfoContact - - return MagicMock(res_data=[mocked_result]) - elif ( + print(type(_request) == commands.CheckDomain) + if ( isinstance(_request, commands.CreateContact) and getattr(_request, "id", None) == "fail" and self.mockedSendFunction.call_count == 3 @@ -817,27 +796,8 @@ class MockEppLib(TestCase): # use this for when a contact is being updated # sets the second send() to fail raise RegistryError(code=ErrorCode.OBJECT_EXISTS) - elif isinstance(_request, commands.CreateHost): - return MagicMock( - res_data=[self.mockDataHostChange], - code=ErrorCode.COMMAND_COMPLETED_SUCCESSFULLY, - ) - elif isinstance(_request, commands.UpdateHost): - return MagicMock( - res_data=[self.mockDataHostChange], - code=ErrorCode.COMMAND_COMPLETED_SUCCESSFULLY, - ) - elif isinstance(_request, commands.UpdateDomain): - return MagicMock( - res_data=[self.mockDataHostChange], - code=ErrorCode.COMMAND_COMPLETED_SUCCESSFULLY, - ) - elif isinstance(_request, commands.DeleteHost): - return MagicMock( - res_data=[self.mockDataHostChange], - code=ErrorCode.COMMAND_COMPLETED_SUCCESSFULLY, - ) - elif ( + + if ( isinstance(_request, commands.DeleteDomain) and getattr(_request, "name", None) == "failDelete.gov" ): @@ -847,9 +807,51 @@ class MockEppLib(TestCase): raise RegistryError( code=ErrorCode.OBJECT_ASSOCIATION_PROHIBITS_OPERATION ) - elif isinstance(_request, commands.CheckDomain): - return self._handleCheckDomain(_request) - return MagicMock(res_data=[self.mockDataInfoHosts]) + + match type(_request): + case commands.InfoDomain: + return self._getattrInfoDomain(_request) + case commands.InfoContact: + mocked_result: info.InfoContactResultData + + # For testing contact types + match getattr(_request, "id", None): + case "securityContact": + mocked_result = self.mockSecurityContact + case "technicalContact": + mocked_result = self.mockTechnicalContact + case "adminContact": + mocked_result = self.mockAdministrativeContact + case "regContact": + mocked_result = self.mockRegistrantContact + case _: + # Default contact return + mocked_result = self.mockDataInfoContact + return MagicMock(res_data=[mocked_result]) + case commands.CreateHost: + return MagicMock( + res_data=[self.mockDataHostChange], + code=ErrorCode.COMMAND_COMPLETED_SUCCESSFULLY, + ) + case commands.UpdateHost: + return MagicMock( + res_data=[self.mockDataHostChange], + code=ErrorCode.COMMAND_COMPLETED_SUCCESSFULLY, + ) + case commands.UpdateDomain: + return MagicMock( + res_data=[self.mockDataHostChange], + code=ErrorCode.COMMAND_COMPLETED_SUCCESSFULLY, + ) + case commands.DeleteHost: + return MagicMock( + res_data=[self.mockDataHostChange], + code=ErrorCode.COMMAND_COMPLETED_SUCCESSFULLY, + ) + case commands.CheckDomain: + return self._handleCheckDomain(_request) + case _: + return MagicMock(res_data=[self.mockDataInfoHosts]) def setUp(self): """mock epp send function as this will fail locally""" From 81b58167a61b7ceee66230af314f73dba3da0be1 Mon Sep 17 00:00:00 2001 From: Erin <121973038+erinysong@users.noreply.github.com> Date: Thu, 19 Oct 2023 11:13:55 -0700 Subject: [PATCH 5/5] Fix lint errors --- src/registrar/tests/common.py | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/registrar/tests/common.py b/src/registrar/tests/common.py index c61b827fc..f6539466d 100644 --- a/src/registrar/tests/common.py +++ b/src/registrar/tests/common.py @@ -851,7 +851,7 @@ class MockEppLib(TestCase): res_data=[self.mockDataHostChange], code=ErrorCode.COMMAND_COMPLETED_SUCCESSFULLY, ) - + def mockDeleteDomainCommands(self, _request, cleaned): if getattr(_request, "name", None) == "failDelete.gov": name = getattr(_request, "name", None) @@ -862,7 +862,6 @@ class MockEppLib(TestCase): ) return None - def mockInfoDomainCommands(self, _request, cleaned): request_name = getattr(_request, "name", None)