From 55d6ae0f290463943e19ec77fec38301fe093cc9 Mon Sep 17 00:00:00 2001
From: Frank Bessou <frank.bessou@logilab.fr>
Date: Wed, 24 Jul 2024 17:15:39 +0200
Subject: [PATCH 1/6] refactor: rename sync_repos into repo_synchronizer

---
 git_hg_sync/__main__.py                            |  2 +-
 .../{sync_repos.py => repo_synchronizer.py}        |  0
 ...test_sync_repo.py => test_repo_synchronizer.py} | 14 +++++++-------
 3 files changed, 8 insertions(+), 8 deletions(-)
 rename git_hg_sync/{sync_repos.py => repo_synchronizer.py} (100%)
 rename tests/{test_sync_repo.py => test_repo_synchronizer.py} (81%)

diff --git a/git_hg_sync/__main__.py b/git_hg_sync/__main__.py
index 6197915..0dc3af1 100644
--- a/git_hg_sync/__main__.py
+++ b/git_hg_sync/__main__.py
@@ -7,7 +7,7 @@
 
 from git_hg_sync import config
 from git_hg_sync.pulse_consumer import Worker
-from git_hg_sync.sync_repos import RepoSynchronyzer
+from git_hg_sync.repo_synchronizer import RepoSynchronyzer
 
 HERE = Path(__file__).parent
 
diff --git a/git_hg_sync/sync_repos.py b/git_hg_sync/repo_synchronizer.py
similarity index 100%
rename from git_hg_sync/sync_repos.py
rename to git_hg_sync/repo_synchronizer.py
diff --git a/tests/test_sync_repo.py b/tests/test_repo_synchronizer.py
similarity index 81%
rename from tests/test_sync_repo.py
rename to tests/test_repo_synchronizer.py
index 18bdf2b..1253f15 100644
--- a/tests/test_sync_repo.py
+++ b/tests/test_repo_synchronizer.py
@@ -3,7 +3,7 @@
 import mozlog
 import pytest
 
-from git_hg_sync import __main__, sync_repos
+from git_hg_sync import __main__, repo_synchronizer
 
 HERE = Path(__file__).parent
 
@@ -61,21 +61,21 @@ def setup_module():
 
 
 def test_parse_entity():
-    syncrepos = sync_repos.RepoSynchronyzer(None)
+    syncrepos = repo_synchronizer.RepoSynchronyzer(None)
     push_entity = syncrepos.parse_entity(raw_push_entity)
-    assert isinstance(push_entity, sync_repos.Push)
+    assert isinstance(push_entity, repo_synchronizer.Push)
     tag_entity = syncrepos.parse_entity(raw_tag_entity)
-    assert isinstance(tag_entity, sync_repos.Tag)
+    assert isinstance(tag_entity, repo_synchronizer.Tag)
 
 
 def test_sync_process_with_bad_type():
-    syncrepos = sync_repos.RepoSynchronyzer(None)
-    with pytest.raises(sync_repos.EntityTypeError):
+    syncrepos = repo_synchronizer.RepoSynchronyzer(None)
+    with pytest.raises(repo_synchronizer.EntityTypeError):
         syncrepos.sync({"type": "badType"})
 
 
 def test_sync_process_with_bad_repo(repos_config):
-    syncrepos = sync_repos.RepoSynchronyzer(repos_config=repos_config)
+    syncrepos = repo_synchronizer.RepoSynchronyzer(repos_config=repos_config)
     with pytest.raises(AssertionError) as e:
         syncrepos.sync(raw_push_entity)
     assert str(e.value) == f"clone {repos_config['repo_url']['clone']} doesn't exists"

From fb8a4db3ddda7a3b3f4f283c3ac5abe203309fb5 Mon Sep 17 00:00:00 2001
From: Frank Bessou <frank.bessou@logilab.fr>
Date: Wed, 24 Jul 2024 17:34:40 +0200
Subject: [PATCH 2/6] refactor: rename Worker class/module to PulseWorker

---
 git_hg_sync/__main__.py                            | 4 ++--
 git_hg_sync/{pulse_consumer.py => pulse_worker.py} | 2 +-
 2 files changed, 3 insertions(+), 3 deletions(-)
 rename git_hg_sync/{pulse_consumer.py => pulse_worker.py} (95%)

diff --git a/git_hg_sync/__main__.py b/git_hg_sync/__main__.py
index 0dc3af1..a27b4c9 100644
--- a/git_hg_sync/__main__.py
+++ b/git_hg_sync/__main__.py
@@ -6,7 +6,7 @@
 from mozlog import commandline
 
 from git_hg_sync import config
-from git_hg_sync.pulse_consumer import Worker
+from git_hg_sync.pulse_worker import PulseWorker
 from git_hg_sync.repo_synchronizer import RepoSynchronyzer
 
 HERE = Path(__file__).parent
@@ -52,7 +52,7 @@ def main():
     repo_synchronyzer = RepoSynchronyzer(repos_config=repos_config)
     with connection as conn:
         logger.info(f"connected to {conn.host}")
-        worker = Worker(conn, queue, repo_synchronyzer=repo_synchronyzer)
+        worker = PulseWorker(conn, queue, repo_synchronyzer=repo_synchronyzer)
         worker.run()
 
 
diff --git a/git_hg_sync/pulse_consumer.py b/git_hg_sync/pulse_worker.py
similarity index 95%
rename from git_hg_sync/pulse_consumer.py
rename to git_hg_sync/pulse_worker.py
index 1d6a42b..6b2ecc8 100644
--- a/git_hg_sync/pulse_consumer.py
+++ b/git_hg_sync/pulse_worker.py
@@ -4,7 +4,7 @@
 logger = get_proxy_logger("pluse_consumer")
 
 
-class Worker(ConsumerMixin):
+class PulseWorker(ConsumerMixin):
 
     def __init__(self, connection, queue, *, repo_synchronyzer):
         self.connection = connection

From 7eaefd0f63be7dc0989019802683f60225cdcd53 Mon Sep 17 00:00:00 2001
From: Frank Bessou <frank.bessou@logilab.fr>
Date: Wed, 24 Jul 2024 17:35:10 +0200
Subject: [PATCH 3/6] refactor: deduplicate type information from messages

Use match/case when possible.
---
 git_hg_sync/repo_synchronizer.py | 26 ++++++++---------
 tests/test_repo_synchronizer.py  | 50 +++++++++++++++++---------------
 2 files changed, 39 insertions(+), 37 deletions(-)

diff --git a/git_hg_sync/repo_synchronizer.py b/git_hg_sync/repo_synchronizer.py
index 06f0a82..a496028 100644
--- a/git_hg_sync/repo_synchronizer.py
+++ b/git_hg_sync/repo_synchronizer.py
@@ -13,7 +13,6 @@ class EntityTypeError(Exception):
 
 @dataclass
 class Push:
-    type: str
     repo_url: str
     heads: list[str]
     commits: list[str]
@@ -25,7 +24,6 @@ class Push:
 
 @dataclass
 class Tag:
-    type: str
     repo_url: str
     tag: str
     commit: str
@@ -42,13 +40,14 @@ def __init__(self, repos_config):
 
     def parse_entity(self, raw_entity):
         logger.debug(f"parse_entity: {raw_entity}")
-        if raw_entity["type"] == "push":
-            entity = Push(**raw_entity)
-        elif raw_entity["type"] == "tag":
-            entity = Tag(**raw_entity)
-        else:
-            raise EntityTypeError(f"unsupported type {raw_entity['type']}")
-        return entity
+        message_type = raw_entity.pop("type")
+        match message_type:
+            case "push":
+                return Push(**raw_entity)
+            case "tag":
+                return Tag(**raw_entity)
+            case _:
+                raise EntityTypeError(f"unsupported type {message_type}")
 
     def get_remote(self, repo, remote_url):
         """
@@ -70,10 +69,11 @@ def handle_commits(self, entity, clone_dir, remote_url, remote_target):
         remote = self.get_remote(repo, remote_url)
         # fetch new commits
         remote.fetch()
-        if entity.type == "push":
-            remote.pull("branches/default/tip")
-        elif entity.type == "tag":
-            pass  # TODO
+        match entity:
+            case Push():
+                remote.pull("branches/default/tip")
+            case _:
+                pass  # TODO
         # push on good repo/branch
         remote = repo.remote(remote_target)
         remote.push()
diff --git a/tests/test_repo_synchronizer.py b/tests/test_repo_synchronizer.py
index 1253f15..32735c5 100644
--- a/tests/test_repo_synchronizer.py
+++ b/tests/test_repo_synchronizer.py
@@ -31,28 +31,30 @@ def repos_config():
         }
     }
 
+def raw_push_entity():
+    return {
+        "type": "push",
+        "repo_url": "repo_url",
+        "heads": ["head"],
+        "commits": ["commit"],
+        "time": 0,
+        "pushid": 0,
+        "user": "user",
+        "push_json_url": "push_json_url",
+    }
+
 
-raw_push_entity = {
-    "type": "push",
-    "repo_url": "repo_url",
-    "heads": ["head"],
-    "commits": ["commit"],
-    "time": 0,
-    "pushid": 0,
-    "user": "user",
-    "push_json_url": "push_json_url",
-}
-
-raw_tag_entity = {
-    "type": "tag",
-    "repo_url": "repo_url",
-    "tag": "tag",
-    "commit": "commit",
-    "time": 0,
-    "pushid": 0,
-    "user": "user",
-    "push_json_url": "push_json_url",
-}
+def raw_tag_entity():
+    return {
+        "type": "tag",
+        "repo_url": "repo_url",
+        "tag": "tag",
+        "commit": "commit",
+        "time": 0,
+        "pushid": 0,
+        "user": "user",
+        "push_json_url": "push_json_url",
+    }
 
 
 def setup_module():
@@ -62,9 +64,9 @@ def setup_module():
 
 def test_parse_entity():
     syncrepos = repo_synchronizer.RepoSynchronyzer(None)
-    push_entity = syncrepos.parse_entity(raw_push_entity)
+    push_entity = syncrepos.parse_entity(raw_push_entity())
     assert isinstance(push_entity, repo_synchronizer.Push)
-    tag_entity = syncrepos.parse_entity(raw_tag_entity)
+    tag_entity = syncrepos.parse_entity(raw_tag_entity())
     assert isinstance(tag_entity, repo_synchronizer.Tag)
 
 
@@ -77,7 +79,7 @@ def test_sync_process_with_bad_type():
 def test_sync_process_with_bad_repo(repos_config):
     syncrepos = repo_synchronizer.RepoSynchronyzer(repos_config=repos_config)
     with pytest.raises(AssertionError) as e:
-        syncrepos.sync(raw_push_entity)
+        syncrepos.sync(raw_push_entity())
     assert str(e.value) == f"clone {repos_config['repo_url']['clone']} doesn't exists"
 
 

From 6ffab615a0a3ce741ccfd2b450f549d9d1423eb1 Mon Sep 17 00:00:00 2001
From: Frank Bessou <frank.bessou@logilab.fr>
Date: Thu, 25 Jul 2024 01:31:11 +0200
Subject: [PATCH 4/6] refactor(tests): share mozlog setup through a autouse
 fixture in conftest.py

---
 tests/conftest.py               | 10 ++++++++++
 tests/test_repo_synchronizer.py |  6 ------
 2 files changed, 10 insertions(+), 6 deletions(-)
 create mode 100644 tests/conftest.py

diff --git a/tests/conftest.py b/tests/conftest.py
new file mode 100644
index 0000000..f48c0a8
--- /dev/null
+++ b/tests/conftest.py
@@ -0,0 +1,10 @@
+import pytest
+import mozlog
+
+@pytest.fixture(autouse=True)
+def mozlog_logging():
+    logger = mozlog.structuredlog.StructuredLogger("tests")
+    mozlog.structuredlog.set_default_logger(logger)
+
+
+
diff --git a/tests/test_repo_synchronizer.py b/tests/test_repo_synchronizer.py
index 32735c5..d1f0512 100644
--- a/tests/test_repo_synchronizer.py
+++ b/tests/test_repo_synchronizer.py
@@ -1,6 +1,5 @@
 from pathlib import Path
 
-import mozlog
 import pytest
 
 from git_hg_sync import __main__, repo_synchronizer
@@ -57,11 +56,6 @@ def raw_tag_entity():
     }
 
 
-def setup_module():
-    logger = mozlog.structuredlog.StructuredLogger("tests")
-    mozlog.structuredlog.set_default_logger(logger)
-
-
 def test_parse_entity():
     syncrepos = repo_synchronizer.RepoSynchronyzer(None)
     push_entity = syncrepos.parse_entity(raw_push_entity())

From 6b81ddf2fbbae9aa81af2e3d9997ba46e0838334 Mon Sep 17 00:00:00 2001
From: Frank Bessou <frank.bessou@logilab.fr>
Date: Thu, 25 Jul 2024 01:33:03 +0200
Subject: [PATCH 5/6] refactor: move json message parsing to pulse_worker

---
 git_hg_sync/pulse_worker.py      | 21 ++++++++++++-
 git_hg_sync/repo_synchronizer.py | 18 +----------
 tests/test_pulse_worker.py       | 40 ++++++++++++++++++++++++
 tests/test_repo_synchronizer.py  | 53 ++++----------------------------
 4 files changed, 67 insertions(+), 65 deletions(-)
 create mode 100644 tests/test_pulse_worker.py

diff --git a/git_hg_sync/pulse_worker.py b/git_hg_sync/pulse_worker.py
index 6b2ecc8..47c8319 100644
--- a/git_hg_sync/pulse_worker.py
+++ b/git_hg_sync/pulse_worker.py
@@ -1,9 +1,15 @@
 from kombu.mixins import ConsumerMixin
 from mozlog import get_proxy_logger
 
+from git_hg_sync.repo_synchronizer import Push, Tag
+
 logger = get_proxy_logger("pluse_consumer")
 
 
+class EntityTypeError(Exception):
+    pass
+
+
 class PulseWorker(ConsumerMixin):
 
     def __init__(self, connection, queue, *, repo_synchronyzer):
@@ -11,6 +17,18 @@ def __init__(self, connection, queue, *, repo_synchronyzer):
         self.task_queue = queue
         self.repo_synchronyzer = repo_synchronyzer
 
+    @staticmethod
+    def parse_entity(raw_entity):
+        logger.debug(f"parse_entity: {raw_entity}")
+        message_type = raw_entity.pop("type")
+        match message_type:
+            case "push":
+                return Push(**raw_entity)
+            case "tag":
+                return Tag(**raw_entity)
+            case _:
+                raise EntityTypeError(f"unsupported type {message_type}")
+
     def get_consumers(self, Consumer, channel):
         consumer = Consumer(
             self.task_queue, auto_declare=False, callbacks=[self.on_task]
@@ -21,4 +39,5 @@ def on_task(self, body, message):
         logger.info(f"Received message: {body}")
         message.ack()
         raw_entity = body["payload"]
-        self.repo_synchronyzer.sync(raw_entity)
+        parsed_message = PulseWorker.parse_entity(raw_entity)
+        self.repo_synchronyzer.sync(parsed_message)
diff --git a/git_hg_sync/repo_synchronizer.py b/git_hg_sync/repo_synchronizer.py
index a496028..82b8883 100644
--- a/git_hg_sync/repo_synchronizer.py
+++ b/git_hg_sync/repo_synchronizer.py
@@ -7,10 +7,6 @@
 logger = get_proxy_logger("sync_repo")
 
 
-class EntityTypeError(Exception):
-    pass
-
-
 @dataclass
 class Push:
     repo_url: str
@@ -38,17 +34,6 @@ class RepoSynchronyzer:
     def __init__(self, repos_config):
         self._repos_config = repos_config
 
-    def parse_entity(self, raw_entity):
-        logger.debug(f"parse_entity: {raw_entity}")
-        message_type = raw_entity.pop("type")
-        match message_type:
-            case "push":
-                return Push(**raw_entity)
-            case "tag":
-                return Tag(**raw_entity)
-            case _:
-                raise EntityTypeError(f"unsupported type {message_type}")
-
     def get_remote(self, repo, remote_url):
         """
         get the repo if it exists, create it otherwise
@@ -79,8 +64,7 @@ def handle_commits(self, entity, clone_dir, remote_url, remote_target):
         remote.push()
         logger.info(f"Done for entity {entity.pushid}")
 
-    def sync(self, raw_entity):
-        entity = self.parse_entity(raw_entity)
+    def sync(self, entity: Push | Tag) -> None:
         repo_config = self._repos_config.get(entity.repo_url)
         if not repo_config:
             logger.warning(f"repo {entity.repo_url} is not supported yet")
diff --git a/tests/test_pulse_worker.py b/tests/test_pulse_worker.py
new file mode 100644
index 0000000..cb02c5c
--- /dev/null
+++ b/tests/test_pulse_worker.py
@@ -0,0 +1,40 @@
+import pytest
+
+from git_hg_sync.pulse_worker import PulseWorker, EntityTypeError
+from git_hg_sync.repo_synchronizer import Push, Tag 
+
+def raw_push_entity():
+    return {
+        "type": "push",
+        "repo_url": "repo_url",
+        "heads": ["head"],
+        "commits": ["commit"],
+        "time": 0,
+        "pushid": 0,
+        "user": "user",
+        "push_json_url": "push_json_url",
+    }
+
+
+def raw_tag_entity():
+    return {
+        "type": "tag",
+        "repo_url": "repo_url",
+        "tag": "tag",
+        "commit": "commit",
+        "time": 0,
+        "pushid": 0,
+        "user": "user",
+        "push_json_url": "push_json_url",
+    }
+
+def test_parse_entity_valid():
+    push_entity = PulseWorker.parse_entity(raw_push_entity())
+    assert isinstance(push_entity, Push)
+    tag_entity = PulseWorker.parse_entity(raw_tag_entity())
+    assert isinstance(tag_entity, Tag)
+
+
+def test_parse_invalid_type():
+    with pytest.raises(EntityTypeError):
+        PulseWorker.parse_entity({"type": "unknown"})
diff --git a/tests/test_repo_synchronizer.py b/tests/test_repo_synchronizer.py
index d1f0512..693627f 100644
--- a/tests/test_repo_synchronizer.py
+++ b/tests/test_repo_synchronizer.py
@@ -2,10 +2,8 @@
 
 import pytest
 
-from git_hg_sync import __main__, repo_synchronizer
-
-HERE = Path(__file__).parent
-
+from git_hg_sync.__main__ import get_connection, get_queue
+from git_hg_sync.repo_synchronizer import RepoSynchronyzer, Push
 
 @pytest.fixture
 def pulse_config():
@@ -30,56 +28,17 @@ def repos_config():
         }
     }
 
-def raw_push_entity():
-    return {
-        "type": "push",
-        "repo_url": "repo_url",
-        "heads": ["head"],
-        "commits": ["commit"],
-        "time": 0,
-        "pushid": 0,
-        "user": "user",
-        "push_json_url": "push_json_url",
-    }
-
-
-def raw_tag_entity():
-    return {
-        "type": "tag",
-        "repo_url": "repo_url",
-        "tag": "tag",
-        "commit": "commit",
-        "time": 0,
-        "pushid": 0,
-        "user": "user",
-        "push_json_url": "push_json_url",
-    }
-
-
-def test_parse_entity():
-    syncrepos = repo_synchronizer.RepoSynchronyzer(None)
-    push_entity = syncrepos.parse_entity(raw_push_entity())
-    assert isinstance(push_entity, repo_synchronizer.Push)
-    tag_entity = syncrepos.parse_entity(raw_tag_entity())
-    assert isinstance(tag_entity, repo_synchronizer.Tag)
-
-
-def test_sync_process_with_bad_type():
-    syncrepos = repo_synchronizer.RepoSynchronyzer(None)
-    with pytest.raises(repo_synchronizer.EntityTypeError):
-        syncrepos.sync({"type": "badType"})
-
 
 def test_sync_process_with_bad_repo(repos_config):
-    syncrepos = repo_synchronizer.RepoSynchronyzer(repos_config=repos_config)
+    syncrepos = RepoSynchronyzer(repos_config=repos_config)
     with pytest.raises(AssertionError) as e:
-        syncrepos.sync(raw_push_entity())
+        syncrepos.sync(Push(repo_url="repo_url", heads=["head"], commits=["commits"], time=0, pushid=0, user="user", push_json_url="push_json_url"))
     assert str(e.value) == f"clone {repos_config['repo_url']['clone']} doesn't exists"
 
 
 def test_get_connection_and_queue(pulse_config):
-    connection = __main__.get_connection(pulse_config)
-    queue = __main__.get_queue(pulse_config)
+    connection = get_connection(pulse_config)
+    queue = get_queue(pulse_config)
     assert connection.userid == pulse_config["userid"]
     assert connection.host == f"{pulse_config['host']}:{pulse_config['port']}"
     assert queue.name == pulse_config["queue"]

From 6774a1e3306e3a398461e0c37f0e7e46244ee4e8 Mon Sep 17 00:00:00 2001
From: Frank Bessou <frank.bessou@logilab.fr>
Date: Thu, 25 Jul 2024 01:58:57 +0200
Subject: [PATCH 6/6] style: run black

---
 tests/conftest.py               |  4 +---
 tests/test_pulse_worker.py      |  4 +++-
 tests/test_repo_synchronizer.py | 13 ++++++++++++-
 3 files changed, 16 insertions(+), 5 deletions(-)

diff --git a/tests/conftest.py b/tests/conftest.py
index f48c0a8..347e4ed 100644
--- a/tests/conftest.py
+++ b/tests/conftest.py
@@ -1,10 +1,8 @@
 import pytest
 import mozlog
 
+
 @pytest.fixture(autouse=True)
 def mozlog_logging():
     logger = mozlog.structuredlog.StructuredLogger("tests")
     mozlog.structuredlog.set_default_logger(logger)
-
-
-
diff --git a/tests/test_pulse_worker.py b/tests/test_pulse_worker.py
index cb02c5c..b5d464b 100644
--- a/tests/test_pulse_worker.py
+++ b/tests/test_pulse_worker.py
@@ -1,7 +1,8 @@
 import pytest
 
 from git_hg_sync.pulse_worker import PulseWorker, EntityTypeError
-from git_hg_sync.repo_synchronizer import Push, Tag 
+from git_hg_sync.repo_synchronizer import Push, Tag
+
 
 def raw_push_entity():
     return {
@@ -28,6 +29,7 @@ def raw_tag_entity():
         "push_json_url": "push_json_url",
     }
 
+
 def test_parse_entity_valid():
     push_entity = PulseWorker.parse_entity(raw_push_entity())
     assert isinstance(push_entity, Push)
diff --git a/tests/test_repo_synchronizer.py b/tests/test_repo_synchronizer.py
index 693627f..16c03dd 100644
--- a/tests/test_repo_synchronizer.py
+++ b/tests/test_repo_synchronizer.py
@@ -5,6 +5,7 @@
 from git_hg_sync.__main__ import get_connection, get_queue
 from git_hg_sync.repo_synchronizer import RepoSynchronyzer, Push
 
+
 @pytest.fixture
 def pulse_config():
     return {
@@ -32,7 +33,17 @@ def repos_config():
 def test_sync_process_with_bad_repo(repos_config):
     syncrepos = RepoSynchronyzer(repos_config=repos_config)
     with pytest.raises(AssertionError) as e:
-        syncrepos.sync(Push(repo_url="repo_url", heads=["head"], commits=["commits"], time=0, pushid=0, user="user", push_json_url="push_json_url"))
+        syncrepos.sync(
+            Push(
+                repo_url="repo_url",
+                heads=["head"],
+                commits=["commits"],
+                time=0,
+                pushid=0,
+                user="user",
+                push_json_url="push_json_url",
+            )
+        )
     assert str(e.value) == f"clone {repos_config['repo_url']['clone']} doesn't exists"