Remove EntityType from juju watcher and workaround juju bug for retrieving the status

- The juju watcher was doing an unnecessary translation with the entity types. The entity already provides an attribute .entity_type
- Juju has a bug in version 2.8.2 that returns a wrong status. Therefore, charms were getting stuck in waiting for ever.
Change-Id: I44709190acc41601e8a67f4c52074fda00c3d495
Signed-off-by: David Garcia <david.garcia@canonical.com>
diff --git a/n2vc/tests/unit/test_juju_watcher.py b/n2vc/tests/unit/test_juju_watcher.py
index 56b4bbd..593ff0d 100644
--- a/n2vc/tests/unit/test_juju_watcher.py
+++ b/n2vc/tests/unit/test_juju_watcher.py
@@ -15,12 +15,16 @@
 import asynctest
 import asyncio
 
-from unittest import mock
+from unittest import mock, TestCase
 from unittest.mock import Mock
-from n2vc.juju_watcher import JujuModelWatcher
-from n2vc.utils import EntityType
+from n2vc.juju_watcher import JujuModelWatcher, entity_ready, status
 from n2vc.exceptions import EntityInvalidException
 from .utils import FakeN2VC, AsyncMock, Deltas, FakeWatcher
+from juju.application import Application
+from juju.model import Model
+from juju.annotation import Annotation
+from juju.machine import Machine
+from juju.action import Action
 
 
 class JujuWatcherTest(asynctest.TestCase):
@@ -32,9 +36,7 @@
     def test_get_status(self):
         tests = Deltas
         for test in tests:
-            (status, message, vca_status) = JujuModelWatcher.get_status(
-                test.delta, test.entity.type
-            )
+            (status, message, vca_status) = JujuModelWatcher.get_status(test.delta)
             self.assertEqual(status, test.entity_status.status)
             self.assertEqual(message, test.entity_status.message)
             self.assertEqual(vca_status, test.entity_status.vca_status)
@@ -61,22 +63,18 @@
             self.n2vc.last_written_values = None
 
     @mock.patch("n2vc.juju_watcher.asyncio.wait")
-    @mock.patch("n2vc.juju_watcher.EntityType.get_entity")
-    def test_wait_for(self, get_entity, wait):
+    def test_wait_for(self, wait):
         wait.return_value = asyncio.Future()
         wait.return_value.set_result(None)
-        get_entity.return_value = EntityType.MACHINE
 
         machine = AsyncMock()
         self.loop.run_until_complete(JujuModelWatcher.wait_for(self.model, machine))
 
     @mock.patch("n2vc.juju_watcher.asyncio.wait")
-    @mock.patch("n2vc.juju_watcher.EntityType.get_entity")
-    def test_wait_for_exception(self, get_entity, wait):
+    def test_wait_for_exception(self, wait):
         wait.return_value = asyncio.Future()
         wait.return_value.set_result(None)
         wait.side_effect = Exception("error")
-        get_entity.return_value = EntityType.MACHINE
 
         machine = AsyncMock()
         with self.assertRaises(Exception):
@@ -85,5 +83,60 @@
     def test_wait_for_invalid_entity_exception(self):
         with self.assertRaises(EntityInvalidException):
             self.loop.run_until_complete(
-                JujuModelWatcher.wait_for(self.model, AsyncMock(), total_timeout=0)
+                JujuModelWatcher.wait_for(
+                    self.model,
+                    Annotation(0, self.model),
+                    total_timeout=None,
+                    progress_timeout=None,
+                )
             )
+
+
+class EntityReadyTest(TestCase):
+    @mock.patch("juju.application.Application.units")
+    def setUp(self, mock_units):
+        self.model = Model()
+        self.model._connector = mock.MagicMock()
+
+    def test_invalid_entity(self):
+        with self.assertRaises(EntityInvalidException):
+            entity_ready(Annotation(0, self.model))
+
+    @mock.patch("juju.machine.Machine.agent_status")
+    def test_machine_entity(self, mock_machine_agent_status):
+        entity = Machine(0, self.model)
+        self.assertEqual(entity.entity_type, "machine")
+        self.assertTrue(isinstance(entity_ready(entity), bool))
+
+    @mock.patch("juju.action.Action.status")
+    def test_action_entity(self, mock_action_status):
+        entity = Action(0, self.model)
+        self.assertEqual(entity.entity_type, "action")
+        self.assertTrue(isinstance(entity_ready(entity), bool))
+
+    @mock.patch("juju.application.Application.status")
+    def test_application_entity(self, mock_application_status):
+        entity = Application(0, self.model)
+        self.assertEqual(entity.entity_type, "application")
+        self.assertTrue(isinstance(entity_ready(entity), bool))
+
+
+class StatusTest(TestCase):
+    def setUp(self):
+        self.model = Model()
+        self.model._connector = mock.MagicMock()
+
+    @mock.patch("n2vc.juju_watcher.derive_status")
+    def test_invalid_entity(self, mock_derive_status):
+        application = mock.MagicMock()
+        mock_derive_status.return_value = "active"
+
+        class FakeUnit:
+            @property
+            def workload_status(self):
+                return "active"
+
+        application.units = [FakeUnit()]
+        value = status(application)
+        mock_derive_status.assert_called_once()
+        self.assertTrue(isinstance(value, str))
diff --git a/n2vc/tests/unit/test_utils.py b/n2vc/tests/unit/test_utils.py
index 3bab705..c5ab84f 100644
--- a/n2vc/tests/unit/test_utils.py
+++ b/n2vc/tests/unit/test_utils.py
@@ -40,14 +40,14 @@
     def test_juju_status_to_osm(self):
         tests = [
             {
-                "entity_type": EntityType.MACHINE,
+                "entity_type": "machine",
                 "status": [
                     {"juju": "pending", "osm": N2VCDeploymentStatus.PENDING},
                     {"juju": "started", "osm": N2VCDeploymentStatus.COMPLETED},
                 ],
             },
             {
-                "entity_type": EntityType.APPLICATION,
+                "entity_type": "application",
                 "status": [
                     {"juju": "waiting", "osm": N2VCDeploymentStatus.RUNNING},
                     {"juju": "maintenance", "osm": N2VCDeploymentStatus.RUNNING},
@@ -57,7 +57,7 @@
                 ],
             },
             {
-                "entity_type": EntityType.UNIT,
+                "entity_type": "unit",
                 "status": [
                     {"juju": "waiting", "osm": N2VCDeploymentStatus.RUNNING},
                     {"juju": "maintenance", "osm": N2VCDeploymentStatus.RUNNING},
@@ -67,7 +67,7 @@
                 ],
             },
             {
-                "entity_type": EntityType.ACTION,
+                "entity_type": "action",
                 "status": [
                     {"juju": "running", "osm": N2VCDeploymentStatus.RUNNING},
                     {"juju": "completed", "osm": N2VCDeploymentStatus.COMPLETED},
diff --git a/n2vc/tests/unit/utils.py b/n2vc/tests/unit/utils.py
index fe7362e..ee4dd96 100644
--- a/n2vc/tests/unit/utils.py
+++ b/n2vc/tests/unit/utils.py
@@ -14,7 +14,7 @@
 
 import asyncio
 
-from n2vc.utils import Dict, EntityType, N2VCDeploymentStatus
+from n2vc.utils import Dict, N2VCDeploymentStatus
 from n2vc.n2vc_conn import N2VCConnector
 from unittest.mock import MagicMock
 
@@ -55,7 +55,7 @@
     entity_id = "2"
     dns_name = "FAKE ENDPOINT"
     model_name = "FAKE MODEL"
-    entity_type = EntityType.MACHINE
+    entity_type = "machine"
 
     async def destroy(self, force):
         pass
@@ -201,8 +201,8 @@
 Deltas = [
     Dict(
         {
-            "entity": Dict({"id": "2", "type": EntityType.MACHINE}),
-            "filter": Dict({"entity_id": "2", "entity_type": EntityType.MACHINE}),
+            "entity": Dict({"id": "2", "type": "machine"}),
+            "filter": Dict({"entity_id": "2", "entity_type": "machine"}),
             "delta": FAKE_DELTA_MACHINE_PENDING,
             "entity_status": Dict(
                 {"status": "pending", "message": "Running", "vca_status": "running"}
@@ -224,8 +224,8 @@
     ),
     Dict(
         {
-            "entity": Dict({"id": "2", "type": EntityType.MACHINE}),
-            "filter": Dict({"entity_id": "1", "entity_type": EntityType.MACHINE}),
+            "entity": Dict({"id": "2", "type": "machine"}),
+            "filter": Dict({"entity_id": "1", "entity_type": "machine"}),
             "delta": FAKE_DELTA_MACHINE_PENDING,
             "entity_status": Dict(
                 {"status": "pending", "message": "Running", "vca_status": "running"}
@@ -235,8 +235,8 @@
     ),
     Dict(
         {
-            "entity": Dict({"id": "2", "type": EntityType.MACHINE}),
-            "filter": Dict({"entity_id": "2", "entity_type": EntityType.MACHINE}),
+            "entity": Dict({"id": "2", "type": "machine"}),
+            "filter": Dict({"entity_id": "2", "entity_type": "machine"}),
             "delta": FAKE_DELTA_MACHINE_STARTED,
             "entity_status": Dict(
                 {"status": "started", "message": "Running", "vca_status": "running"}
@@ -258,8 +258,8 @@
     ),
     Dict(
         {
-            "entity": Dict({"id": "2", "type": EntityType.MACHINE}),
-            "filter": Dict({"entity_id": "1", "entity_type": EntityType.MACHINE}),
+            "entity": Dict({"id": "2", "type": "machine"}),
+            "filter": Dict({"entity_id": "1", "entity_type": "machine"}),
             "delta": FAKE_DELTA_MACHINE_STARTED,
             "entity_status": Dict(
                 {"status": "started", "message": "Running", "vca_status": "running"}
@@ -269,8 +269,8 @@
     ),
     Dict(
         {
-            "entity": Dict({"id": "git/0", "type": EntityType.UNIT}),
-            "filter": Dict({"entity_id": "git", "entity_type": EntityType.APPLICATION}),
+            "entity": Dict({"id": "git/0", "type": "unit"}),
+            "filter": Dict({"entity_id": "git", "entity_type": "application"}),
             "delta": FAKE_DELTA_UNIT_PENDING,
             "entity_status": Dict(
                 {"status": "waiting", "message": "", "vca_status": "waiting"}
@@ -292,8 +292,8 @@
     ),
     Dict(
         {
-            "entity": Dict({"id": "git/0", "type": EntityType.UNIT}),
-            "filter": Dict({"entity_id": "2", "entity_type": EntityType.MACHINE}),
+            "entity": Dict({"id": "git/0", "type": "unit"}),
+            "filter": Dict({"entity_id": "2", "entity_type": "machine"}),
             "delta": FAKE_DELTA_UNIT_PENDING,
             "entity_status": Dict(
                 {"status": "waiting", "message": "", "vca_status": "waiting"}
@@ -303,8 +303,8 @@
     ),
     Dict(
         {
-            "entity": Dict({"id": "git/0", "type": EntityType.UNIT}),
-            "filter": Dict({"entity_id": "git", "entity_type": EntityType.APPLICATION}),
+            "entity": Dict({"id": "git/0", "type": "unit"}),
+            "filter": Dict({"entity_id": "git", "entity_type": "application"}),
             "delta": FAKE_DELTA_UNIT_STARTED,
             "entity_status": Dict(
                 {"status": "active", "message": "", "vca_status": "active"}
@@ -326,8 +326,8 @@
     ),
     Dict(
         {
-            "entity": Dict({"id": "git/0", "type": EntityType.UNIT}),
-            "filter": Dict({"entity_id": "1", "entity_type": EntityType.ACTION}),
+            "entity": Dict({"id": "git/0", "type": "unit"}),
+            "filter": Dict({"entity_id": "1", "entity_type": "action"}),
             "delta": FAKE_DELTA_UNIT_STARTED,
             "entity_status": Dict(
                 {"status": "active", "message": "", "vca_status": "active"}
@@ -337,8 +337,8 @@
     ),
     Dict(
         {
-            "entity": Dict({"id": "git", "type": EntityType.APPLICATION}),
-            "filter": Dict({"entity_id": "git", "entity_type": EntityType.APPLICATION}),
+            "entity": Dict({"id": "git", "type": "application"}),
+            "filter": Dict({"entity_id": "git", "entity_type": "application"}),
             "delta": FAKE_DELTA_APPLICATION_MAINTENANCE,
             "entity_status": Dict(
                 {
@@ -364,8 +364,8 @@
     ),
     Dict(
         {
-            "entity": Dict({"id": "git", "type": EntityType.APPLICATION}),
-            "filter": Dict({"entity_id": "2", "entity_type": EntityType.MACHINE}),
+            "entity": Dict({"id": "git", "type": "application"}),
+            "filter": Dict({"entity_id": "2", "entity_type": "machine"}),
             "delta": FAKE_DELTA_APPLICATION_MAINTENANCE,
             "entity_status": Dict(
                 {
@@ -379,8 +379,8 @@
     ),
     Dict(
         {
-            "entity": Dict({"id": "git", "type": EntityType.APPLICATION}),
-            "filter": Dict({"entity_id": "git", "entity_type": EntityType.APPLICATION}),
+            "entity": Dict({"id": "git", "type": "application"}),
+            "filter": Dict({"entity_id": "git", "entity_type": "application"}),
             "delta": FAKE_DELTA_APPLICATION_ACTIVE,
             "entity_status": Dict(
                 {"status": "active", "message": "Ready!", "vca_status": "active"}
@@ -402,8 +402,8 @@
     ),
     Dict(
         {
-            "entity": Dict({"id": "git", "type": EntityType.APPLICATION}),
-            "filter": Dict({"entity_id": "1", "entity_type": EntityType.ACTION}),
+            "entity": Dict({"id": "git", "type": "application"}),
+            "filter": Dict({"entity_id": "1", "entity_type": "action"}),
             "delta": FAKE_DELTA_APPLICATION_ACTIVE,
             "entity_status": Dict(
                 {"status": "active", "message": "Ready!", "vca_status": "active"}
@@ -413,8 +413,8 @@
     ),
     Dict(
         {
-            "entity": Dict({"id": "1", "type": EntityType.ACTION}),
-            "filter": Dict({"entity_id": "1", "entity_type": EntityType.ACTION}),
+            "entity": Dict({"id": "1", "type": "action"}),
+            "filter": Dict({"entity_id": "1", "entity_type": "action"}),
             "delta": FAKE_DELTA_ACTION_COMPLETED,
             "entity_status": Dict(
                 {
@@ -440,8 +440,8 @@
     ),
     Dict(
         {
-            "entity": Dict({"id": "git", "type": EntityType.ACTION}),
-            "filter": Dict({"entity_id": "1", "entity_type": EntityType.MACHINE}),
+            "entity": Dict({"id": "git", "type": "action"}),
+            "filter": Dict({"entity_id": "1", "entity_type": "machine"}),
             "delta": FAKE_DELTA_ACTION_COMPLETED,
             "entity_status": Dict(
                 {