Merge pull request #1046 from cisagov/rjm/851-statuses-getter

Issue 851: Domain statuses getter
This commit is contained in:
rachidatecs 2023-09-20 14:46:14 -04:00 committed by GitHub
commit d92f289556
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
3 changed files with 125 additions and 37 deletions

View file

@ -332,24 +332,23 @@ class Domain(TimeStampedModel, DomainHelper):
@Cache
def statuses(self) -> list[str]:
"""
Get or set the domain `status` elements from the registry.
Get the domain `status` elements from the registry.
A domain's status indicates various properties. See Domain.Status.
"""
# implementation note: the Status object from EPP stores the string in
# a dataclass property `state`, not to be confused with the `state` field here
if "statuses" not in self._cache:
self._fetch_cache()
if "statuses" not in self._cache:
raise Exception("Can't retreive status from domain info")
else:
return self._cache["statuses"]
try:
return self._get_property("statuses")
except KeyError:
logger.error("Can't retrieve status from domain info")
return []
@statuses.setter # type: ignore
def statuses(self, statuses: list[str]):
# TODO: there are a long list of rules in the RFC about which statuses
# can be combined; check that here and raise errors for invalid combinations -
# some statuses cannot be set by the client at all
"""
We will not implement this. Statuses are set by the registry
when we run delete and client hold, and these are the only statuses
we will be triggering.
"""
raise NotImplementedError()
@Cache

View file

@ -547,17 +547,29 @@ class MockEppLib(TestCase):
class fakedEppObject(object):
""""""
def __init__(self, auth_info=..., cr_date=..., contacts=..., hosts=...):
def __init__(
self,
auth_info=...,
cr_date=...,
contacts=...,
hosts=...,
statuses=...,
):
self.auth_info = auth_info
self.cr_date = cr_date
self.contacts = contacts
self.hosts = hosts
self.statuses = statuses
mockDataInfoDomain = fakedEppObject(
"fakepw",
cr_date=datetime.datetime(2023, 5, 25, 19, 45, 35),
contacts=[common.DomainContact(contact="123", type="security")],
hosts=["fake.host.com"],
statuses=[
common.Status(state="serverTransferProhibited", description="", lang="en"),
common.Status(state="inactive", description="", lang="en"),
],
)
infoDomainNoContact = fakedEppObject(
"security",

View file

@ -34,6 +34,8 @@ class TestDomainCache(MockEppLib):
# (see InfoDomainResult)
self.assertEquals(domain._cache["auth_info"], self.mockDataInfoDomain.auth_info)
self.assertEquals(domain._cache["cr_date"], self.mockDataInfoDomain.cr_date)
status_list = [status.state for status in self.mockDataInfoDomain.statuses]
self.assertEquals(domain._cache["statuses"], status_list)
self.assertFalse("avail" in domain._cache.keys())
# using a setter should clear the cache
@ -49,7 +51,8 @@ class TestDomainCache(MockEppLib):
),
call(commands.InfoContact(id="123", auth_info=None), cleaned=True),
call(commands.InfoHost(name="fake.host.com"), cleaned=True),
]
],
any_order=False, # Ensure calls are in the specified order
)
def test_cache_used_when_avail(self):
@ -106,16 +109,14 @@ class TestDomainCache(MockEppLib):
domain._get_property("hosts")
self.assertEqual(domain._cache["hosts"], [expectedHostsDict])
def tearDown(self) -> None:
Domain.objects.all().delete()
super().tearDown()
class TestDomainCreation(TestCase):
class TestDomainCreation(MockEppLib):
"""Rule: An approved domain application must result in a domain"""
def setUp(self):
"""
Background:
Given that a valid domain application exists
"""
def test_approved_application_creates_domain_locally(self):
"""
Scenario: Analyst approves a domain application
@ -123,8 +124,6 @@ class TestDomainCreation(TestCase):
Then a Domain exists in the database with the same `name`
But a domain object does not exist in the registry
"""
patcher = patch("registrar.models.domain.Domain._get_or_create_domain")
mocked_domain_creation = patcher.start()
draft_domain, _ = DraftDomain.objects.get_or_create(name="igorville.gov")
user, _ = User.objects.get_or_create()
application = DomainApplication.objects.create(
@ -137,19 +136,46 @@ class TestDomainCreation(TestCase):
# should hav information present for this domain
domain = Domain.objects.get(name="igorville.gov")
self.assertTrue(domain)
mocked_domain_creation.assert_not_called()
self.mockedSendFunction.assert_not_called()
@skip("not implemented yet")
def test_accessing_domain_properties_creates_domain_in_registry(self):
"""
Scenario: A registrant checks the status of a newly approved domain
Given that no domain object exists in the registry
When a property is accessed
Then Domain sends `commands.CreateDomain` to the registry
And `domain.state` is set to `CREATED`
And `domain.state` is set to `UNKNOWN`
And `domain.is_active()` returns False
"""
raise
domain = Domain.objects.create(name="beef-tongue.gov")
# trigger getter
_ = domain.statuses
# contacts = PublicContact.objects.filter(domain=domain,
# type=PublicContact.ContactTypeChoices.REGISTRANT).get()
# Called in _fetch_cache
self.mockedSendFunction.assert_has_calls(
[
# TODO: due to complexity of the test, will return to it in
# a future ticket
# call(
# commands.CreateDomain(name="beef-tongue.gov",
# id=contact.registry_id, auth_info=None),
# cleaned=True,
# ),
call(
commands.InfoDomain(name="beef-tongue.gov", auth_info=None),
cleaned=True,
),
call(commands.InfoContact(id="123", auth_info=None), cleaned=True),
call(commands.InfoHost(name="fake.host.com"), cleaned=True),
],
any_order=False, # Ensure calls are in the specified order
)
self.assertEqual(domain.state, Domain.State.UNKNOWN)
self.assertEqual(domain.is_active(), False)
@skip("assertion broken with mock addition")
def test_empty_domain_creation(self):
@ -168,20 +194,71 @@ class TestDomainCreation(TestCase):
with self.assertRaisesRegex(IntegrityError, "name"):
Domain.objects.create(name="igorville.gov")
@skip("cannot activate a domain without mock registry")
def test_get_status(self):
"""Returns proper status based on `state`."""
domain = Domain.objects.create(name="igorville.gov")
domain.save()
self.assertEqual(None, domain.status)
domain.activate()
domain.save()
self.assertIn("ok", domain.status)
def tearDown(self) -> None:
DomainInformation.objects.all().delete()
DomainApplication.objects.all().delete()
Domain.objects.all().delete()
super().tearDown()
class TestDomainStatuses(MockEppLib):
"""Domain statuses are set by the registry"""
def test_get_status(self):
"""Domain 'statuses' getter returns statuses by calling epp"""
domain, _ = Domain.objects.get_or_create(name="chicken-liver.gov")
# trigger getter
_ = domain.statuses
status_list = [status.state for status in self.mockDataInfoDomain.statuses]
self.assertEquals(domain._cache["statuses"], status_list)
# Called in _fetch_cache
self.mockedSendFunction.assert_has_calls(
[
call(
commands.InfoDomain(name="chicken-liver.gov", auth_info=None),
cleaned=True,
),
call(commands.InfoContact(id="123", auth_info=None), cleaned=True),
call(commands.InfoHost(name="fake.host.com"), cleaned=True),
],
any_order=False, # Ensure calls are in the specified order
)
def test_get_status_returns_empty_list_when_value_error(self):
"""Domain 'statuses' getter returns an empty list
when value error"""
domain, _ = Domain.objects.get_or_create(name="pig-knuckles.gov")
def side_effect(self):
raise KeyError
patcher = patch("registrar.models.domain.Domain._get_property")
mocked_get = patcher.start()
mocked_get.side_effect = side_effect
# trigger getter
_ = domain.statuses
with self.assertRaises(KeyError):
_ = domain._cache["statuses"]
self.assertEquals(_, [])
patcher.stop()
@skip("not implemented yet")
def test_place_client_hold_sets_status(self):
"""Domain 'place_client_hold' method causes the registry to change statuses"""
raise
@skip("not implemented yet")
def test_revert_client_hold_sets_status(self):
"""Domain 'revert_client_hold' method causes the registry to change statuses"""
raise
def tearDown(self) -> None:
Domain.objects.all().delete()
super().tearDown()
class TestRegistrantContacts(MockEppLib):