Repository: cisagov/manage.get.gov

Issue:
Get status Epp domain object
### Issue Description

In the domain.py implement getter for the domain statuses. 
NOTE: the getting portion is partially implemented already
AC:
-If ok is a status, the domain should have a status of READY, if it does not change the state to ready
-log an error in this case
-add tests


### Additional Context (optional)
see the [EPP RFC](https://www.rfc-editor.org/std/std69.txt)
_No response_

### Issue Links

_No response_


Discussion:

Resolving patch:
diff --git a/src/registrar/models/domain.py b/src/registrar/models/domain.py
--- a/src/registrar/models/domain.py
+++ b/src/registrar/models/domain.py
@@ -332,24 +332,23 @@ def nameservers(self, hosts: list[tuple[str]]):
     @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


Test patch:
diff --git a/src/registrar/tests/common.py b/src/registrar/tests/common.py
--- a/src/registrar/tests/common.py
+++ b/src/registrar/tests/common.py
@@ -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",
diff --git a/src/registrar/tests/test_models_domain.py b/src/registrar/tests/test_models_domain.py
--- a/src/registrar/tests/test_models_domain.py
+++ b/src/registrar/tests/test_models_domain.py
@@ -34,6 +34,8 @@ def test_cache_sets_resets(self):
         # (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 @@ def test_cache_sets_resets(self):
                 ),
                 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,15 +109,13 @@ def test_cache_nested_elements(self):
         domain._get_property("hosts")
         self.assertEqual(domain._cache["hosts"], [expectedHostsDict])
 
+    def tearDown(self) -> None:
+        Domain.objects.all().delete()
+        super().tearDown()
 
-class TestDomainCreation(TestCase):
-    """Rule: An approved domain application must result in a domain"""
 
-    def setUp(self):
-        """
-        Background:
-            Given that a valid domain application exists
-        """
+class TestDomainCreation(MockEppLib):
+    """Rule: An approved domain application must result in a domain"""
 
     def test_approved_application_creates_domain_locally(self):
         """
@@ -123,8 +124,6 @@ def test_approved_application_creates_domain_locally(self):
             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 @@ def test_approved_application_creates_domain_locally(self):
         # 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 @@ def test_duplicate_creation(self):
         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):