Repository: MatterMiners/tardis

Issue:
Fix code scanning alert - Implicit string concatenation in a list
This seems to be a bug in the SqliteRegistry. 

Tracking issue for:
- [ ] https://github.com/MatterMiners/tardis/security/code-scanning/5


Discussion:
codecov-commenter: ## [Codecov](https://app.codecov.io/gh/MatterMiners/tardis/pull/317?src=pr&el=h1&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=MatterMiners) Report
All modified and coverable lines are covered by tests :white_check_mark:
> Comparison is base [(`36ac7c1`)](https://app.codecov.io/gh/MatterMiners/tardis/commit/36ac7c1bd58b1d0a86bddb4f1bea1c259caab3fc?el=desc&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=MatterMiners) 98.86% compared to head [(`288ccbb`)](https://app.codecov.io/gh/MatterMiners/tardis/pull/317?src=pr&el=desc&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=MatterMiners) 98.86%.

<details><summary>Additional details and impacted files</summary>


```diff
@@           Coverage Diff           @@
##           master     #317   +/-   ##
=======================================
  Coverage   98.86%   98.86%           
=======================================
  Files          55       55           
  Lines        2210     2210           
=======================================
  Hits         2185     2185           
  Misses         25       25           
```


| [Files](https://app.codecov.io/gh/MatterMiners/tardis/pull/317?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=MatterMiners) | Coverage Δ | |
|---|---|---|
| [tardis/plugins/sqliteregistry.py](https://app.codecov.io/gh/MatterMiners/tardis/pull/317?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=MatterMiners#diff-dGFyZGlzL3BsdWdpbnMvc3FsaXRlcmVnaXN0cnkucHk=) | `100.00% <ø> (ø)` | |


</details>

[:umbrella: View full report in Codecov by Sentry](https://app.codecov.io/gh/MatterMiners/tardis/pull/317?src=pr&el=continue&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=MatterMiners).   
:loudspeaker: Have feedback on the report? [Share it here](https://about.codecov.io/codecov-pr-comment-feedback/?utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=MatterMiners).


Patch:
diff --git a/tardis/plugins/sqliteregistry.py b/tardis/plugins/sqliteregistry.py
index 99b2ad6..5e931b0 100644
--- a/tardis/plugins/sqliteregistry.py
+++ b/tardis/plugins/sqliteregistry.py
@@ -96,7 +96,7 @@ class SqliteRegistry(Plugin):
                 "CONSTRAINT unique_machine_type_per_site UNIQUE (machine_type, site_id)",  # noqa B950
             ],
             "Resources": [
-                "id INTEGER PRIMARY KEY AUTOINCREMENT,"
+                "id INTEGER PRIMARY KEY AUTOINCREMENT",
                 "remote_resource_uuid VARCHAR(255)",
                 "drone_uuid VARCHAR(255) UNIQUE",
                 "state_id INTEGER",
diff --git a/tardis/utilities/asynccachemap.py b/tardis/utilities/asynccachemap.py
index 2b1733b..d05427c 100644
--- a/tardis/utilities/asynccachemap.py
+++ b/tardis/utilities/asynccachemap.py
@@ -55,3 +55,15 @@ class AsyncCacheMap(Mapping):
 
     def __len__(self):
         return len(self._data)
+
+    def __eq__(self, other):
+        if not isinstance(other, AsyncCacheMap):
+            return False
+
+        return (
+            self._update_coroutine == other._update_coroutine
+            and self._max_age == other._max_age
+            and self._last_update == other._last_update
+            and self._data == other._data
+            and self._lock == other._lock
+        )
diff --git a/tardis/utilities/staticmapping.py b/tardis/utilities/staticmapping.py
index 6e5278d..f8cd428 100644
--- a/tardis/utilities/staticmapping.py
+++ b/tardis/utilities/staticmapping.py
@@ -13,3 +13,8 @@ class StaticMapping(Mapping):
 
     def __len__(self):
         return len(self._data)
+
+    def __eq__(self, other):
+        if not isinstance(other, StaticMapping):
+            return False
+        return self._data == other._data


Test patch:
diff --git a/tests/plugins_t/test_prometheusmonitoring.py b/tests/plugins_t/test_prometheusmonitoring.py
index 28aef63..7e04087 100644
--- a/tests/plugins_t/test_prometheusmonitoring.py
+++ b/tests/plugins_t/test_prometheusmonitoring.py
@@ -25,9 +25,10 @@ class TestPrometheusMonitoring(TestCase):
 
     @patch("tardis.plugins.prometheusmonitoring.logging", Mock())
     def setUp(self):
+        ip = "127.0.0.1"
         self.config = self.mock_config.return_value
-        self.config.Plugins.PrometheusMonitoring.addr = "127.0.0.1"
-        self.config.Plugins.PrometheusMonitoring.port = get_free_port()
+        self.config.Plugins.PrometheusMonitoring.addr = ip
+        self.config.Plugins.PrometheusMonitoring.port = get_free_port(ip)
 
         self.plugin = PrometheusMonitoring()
 
diff --git a/tests/utilities/utilities.py b/tests/utilities/utilities.py
index 66691ab..c5373bd 100644
--- a/tests/utilities/utilities.py
+++ b/tests/utilities/utilities.py
@@ -11,9 +11,9 @@ def async_return(*args, return_value=None, **kwargs):
     return f
 
 
-def get_free_port():  # from https://gist.github.com/dbrgn/3979133
+def get_free_port(ip: str):  # from https://gist.github.com/dbrgn/3979133
     s = socket.socket()
-    s.bind(("", 0))
+    s.bind((ip, 0))
     port = s.getsockname()[1]
     s.close()
     return port
diff --git a/tests/utilities_t/test_asynccachemap.py b/tests/utilities_t/test_asynccachemap.py
index e67825b..249cf91 100644
--- a/tests/utilities_t/test_asynccachemap.py
+++ b/tests/utilities_t/test_asynccachemap.py
@@ -70,3 +70,33 @@ class TestAsyncCacheMap(TestCase):
         self.assertTrue(
             datetime.now() - self.async_cache_map.last_update < timedelta(seconds=1)
         )
+
+    def test_eq_async_cache_map(self):
+        test_cache_map = AsyncCacheMap(
+            update_coroutine=self.async_cache_map._update_coroutine
+        )
+        # Since both objects have been recently initialized, all values (self._max_age,
+        # self._last_update, self._data and self._lock) are still the defaults
+        self.assertTrue(self.async_cache_map == test_cache_map)
+
+        # Test the opposite
+        self.assertFalse(self.async_cache_map != test_cache_map)
+
+        # change default values
+        run_async(self.async_cache_map.update_status)
+        self.assertFalse(self.async_cache_map == test_cache_map)
+
+        # update default values, self._last_update, self._lock still differ
+        run_async(test_cache_map.update_status)
+        self.assertFalse(self.async_cache_map == test_cache_map)
+
+        # Assimilate lock, self._last_update still differs
+        test_cache_map._lock = self.async_cache_map._lock
+        self.assertFalse(self.async_cache_map == test_cache_map)
+
+        # Make them equal again
+        test_cache_map._last_update = self.async_cache_map._last_update
+        self.assertTrue(self.async_cache_map == test_cache_map)
+
+        # Test different class
+        self.assertFalse(self.async_cache_map == self.test_data)
diff --git a/tests/utilities_t/test_staticmapping.py b/tests/utilities_t/test_staticmapping.py
index b906108..5c17468 100644
--- a/tests/utilities_t/test_staticmapping.py
+++ b/tests/utilities_t/test_staticmapping.py
@@ -26,3 +26,17 @@ class TestStaticMapping(TestCase):
             self.static_map["testB"] = 456
         with self.assertRaises(TypeError):
             self.static_map["testC"] = 456
+
+    def test_eq_async_cache_map(self):
+        test_static_map = StaticMapping(**self.test_data)
+        self.assertTrue(self.static_map == test_static_map)
+
+        # Test the opposite
+        self.assertFalse(self.static_map != test_static_map)
+
+        # Change the data dictionary
+        test_static_map = StaticMapping(test=123)
+        self.assertFalse(self.static_map == test_static_map)
+
+        # Test different class
+        self.assertFalse(self.static_map == self.test_data)