collab-runtime 0.2.9__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (82) hide show
  1. collab/__init__.py +77 -0
  2. collab/__main__.py +11 -0
  3. collab_runtime-0.2.9.dist-info/METADATA +218 -0
  4. collab_runtime-0.2.9.dist-info/RECORD +82 -0
  5. collab_runtime-0.2.9.dist-info/WHEEL +5 -0
  6. collab_runtime-0.2.9.dist-info/entry_points.txt +3 -0
  7. collab_runtime-0.2.9.dist-info/licenses/LICENSE +21 -0
  8. collab_runtime-0.2.9.dist-info/top_level.txt +10 -0
  9. scripts/cleanup.py +395 -0
  10. scripts/collab_git_hook.py +190 -0
  11. scripts/format_code.py +594 -0
  12. scripts/generate_tests.py +560 -0
  13. scripts/validate_code.py +1397 -0
  14. src/__init__.py +4 -0
  15. src/dashboard/index.html +1131 -0
  16. src/live_locks_watcher.py +1982 -0
  17. src/lock_client.py +4268 -0
  18. src/logging_config.py +259 -0
  19. src/main.py +436 -0
  20. tests/backend/__init__.py +0 -0
  21. tests/backend/functional/__init__.py +0 -0
  22. tests/backend/functional/test_package_imports.py +43 -0
  23. tests/backend/integration/__init__.py +0 -0
  24. tests/backend/integration/test_cli_contract_parity.py +220 -0
  25. tests/backend/performance/__init__.py +0 -0
  26. tests/backend/reliability/__init__.py +0 -0
  27. tests/backend/security/__init__.py +0 -0
  28. tests/backend/unit/live_locks_watcher/__init__.py +5 -0
  29. tests/backend/unit/live_locks_watcher/_helpers.py +123 -0
  30. tests/backend/unit/live_locks_watcher/conftest.py +18 -0
  31. tests/backend/unit/live_locks_watcher/test_live_locks_watcher_dashboard.py +188 -0
  32. tests/backend/unit/live_locks_watcher/test_live_locks_watcher_developer.py +56 -0
  33. tests/backend/unit/live_locks_watcher/test_live_locks_watcher_graceful_shutdown.py +459 -0
  34. tests/backend/unit/live_locks_watcher/test_live_locks_watcher_main.py +1925 -0
  35. tests/backend/unit/live_locks_watcher/test_live_locks_watcher_module.py +187 -0
  36. tests/backend/unit/live_locks_watcher/test_live_locks_watcher_multi_session.py +320 -0
  37. tests/backend/unit/live_locks_watcher/test_live_locks_watcher_notify.py +67 -0
  38. tests/backend/unit/live_locks_watcher/test_live_locks_watcher_parsing.py +155 -0
  39. tests/backend/unit/live_locks_watcher/test_live_locks_watcher_process_helpers.py +684 -0
  40. tests/backend/unit/live_locks_watcher/test_live_locks_watcher_processing.py +173 -0
  41. tests/backend/unit/live_locks_watcher/test_live_locks_watcher_prompt_abort.py +71 -0
  42. tests/backend/unit/live_locks_watcher/test_live_locks_watcher_reconcile.py +516 -0
  43. tests/backend/unit/live_locks_watcher/test_live_locks_watcher_scan.py +296 -0
  44. tests/backend/unit/lock_client/__init__.py +1 -0
  45. tests/backend/unit/lock_client/_helpers.py +132 -0
  46. tests/backend/unit/lock_client/test_lock_client_acquire.py +214 -0
  47. tests/backend/unit/lock_client/test_lock_client_active.py +104 -0
  48. tests/backend/unit/lock_client/test_lock_client_api.py +63 -0
  49. tests/backend/unit/lock_client/test_lock_client_cli.py +682 -0
  50. tests/backend/unit/lock_client/test_lock_client_daemon.py +3730 -0
  51. tests/backend/unit/lock_client/test_lock_client_dashboard.py +438 -0
  52. tests/backend/unit/lock_client/test_lock_client_discover.py +241 -0
  53. tests/backend/unit/lock_client/test_lock_client_force_release.py +354 -0
  54. tests/backend/unit/lock_client/test_lock_client_helper_branches.py +1890 -0
  55. tests/backend/unit/lock_client/test_lock_client_history.py +301 -0
  56. tests/backend/unit/lock_client/test_lock_client_isolation.py +316 -0
  57. tests/backend/unit/lock_client/test_lock_client_pid.py +75 -0
  58. tests/backend/unit/lock_client/test_lock_client_reconcile.py +464 -0
  59. tests/backend/unit/lock_client/test_lock_client_release.py +77 -0
  60. tests/backend/unit/lock_client/test_lock_client_shutdown.py +1110 -0
  61. tests/backend/unit/lock_client/test_lock_client_utils.py +474 -0
  62. tests/backend/unit/lock_client/test_lock_client_watch.py +866 -0
  63. tests/backend/unit/scripts/__init__.py +1 -0
  64. tests/backend/unit/scripts/_helpers.py +42 -0
  65. tests/backend/unit/scripts/test_cleanup.py +285 -0
  66. tests/backend/unit/scripts/test_collab_git_hook.py +280 -0
  67. tests/backend/unit/scripts/test_collab_git_hook_ported.py +50 -0
  68. tests/backend/unit/scripts/test_format_code.py +368 -0
  69. tests/backend/unit/scripts/test_format_code_ported.py +177 -0
  70. tests/backend/unit/scripts/test_generate_tests.py +305 -0
  71. tests/backend/unit/scripts/test_hook_templates.py +357 -0
  72. tests/backend/unit/scripts/test_setup_hook_overlay.py +95 -0
  73. tests/backend/unit/scripts/test_validate_code.py +867 -0
  74. tests/backend/unit/scripts/test_validate_code_ported.py +237 -0
  75. tests/backend/unit/test_entrypoints_main_run.py +83 -0
  76. tests/backend/unit/test_logging_config.py +529 -0
  77. tests/backend/unit/test_main_watch_pid_file.py +278 -0
  78. tests/conftest.py +167 -0
  79. tests/frontend/__init__.py +0 -0
  80. tests/frontend/jest/__init__.py +0 -0
  81. tests/frontend/playwright/__init__.py +0 -0
  82. tests/packaging/test_smoke_install.py +76 -0
@@ -0,0 +1,173 @@
1
+ """Processing helpers tests for live_locks_watcher."""
2
+
3
+ from __future__ import annotations
4
+
5
+ import subprocess
6
+
7
+ from ._helpers import load_watcher_module
8
+
9
+
10
+ def test_process_new_files_handles_local_add_exception(monkeypatch):
11
+ mod = load_watcher_module()
12
+
13
+ # Replace _local_owned_locks with an object whose add() raises
14
+ class BadSet:
15
+ def add(self, *a, **k):
16
+ raise RuntimeError("boom add")
17
+
18
+ old = mod._local_owned_locks
19
+ mod._local_owned_locks = BadSet()
20
+
21
+ # Fake client returns success (no conflict)
22
+ class RpcClient:
23
+ def rpc(self, *a, **k):
24
+ return self
25
+
26
+ def execute(self):
27
+ return type("R", (), {"data": []})()
28
+
29
+ client = RpcClient()
30
+ mod.DEVELOPER_ID = "tester"
31
+
32
+ # Should not raise even though add() raises inside
33
+ mod._process_new_files(client, "main", {"src/a.py"})
34
+
35
+ # restore
36
+ mod._local_owned_locks = old
37
+
38
+
39
+ def test_process_releases_handles_discard_exception(monkeypatch):
40
+ mod = load_watcher_module()
41
+
42
+ # Replace _local_owned_locks with object whose discard raises
43
+ class BadSet:
44
+ def discard(self, *a, **k):
45
+ raise RuntimeError("boom discard")
46
+
47
+ old = mod._local_owned_locks
48
+ mod._local_owned_locks = BadSet()
49
+
50
+ # Fake client for delete.execute()
51
+ class FakeClientLocal2:
52
+ """Fake Supabase client with fluent CRUD interface for testing."""
53
+
54
+ def __init__(self, data=None, explode=False):
55
+ self._data = data if data is not None else []
56
+ self._explode = explode
57
+ self._rows = list(self._data)
58
+
59
+ def table(self, *a, **k):
60
+ return self
61
+
62
+ def select(self, *a, **k):
63
+ return self
64
+
65
+ def insert(self, *a, **k):
66
+ return self
67
+
68
+ def update(self, *a, **k):
69
+ return self
70
+
71
+ def delete(self):
72
+ return self
73
+
74
+ def eq(self, *a, **k):
75
+ return self
76
+
77
+ def ilike(self, *a, **k):
78
+ return self
79
+
80
+ def order(self, *a, **k):
81
+ return self
82
+
83
+ def limit(self, *a, **k):
84
+ return self
85
+
86
+ def rpc(self, *a, **k):
87
+ return self
88
+
89
+ def execute(self):
90
+ if self._explode:
91
+ raise RuntimeError("backend down")
92
+
93
+ class R:
94
+ data = self._data
95
+
96
+ return R()
97
+
98
+ fake = FakeClientLocal2(data=[])
99
+ mod.DEVELOPER_ID = "tester"
100
+
101
+ # Should not raise even though discard() raises inside
102
+ mod._process_releases(fake, {"src/b.py"})
103
+
104
+ mod._local_owned_locks = old
105
+
106
+
107
+ # RESTORED: test_process_new_files_and_releases_moved (from HEAD)
108
+ def test_process_new_files_and_releases_moved(monkeypatch):
109
+ mod = load_watcher_module()
110
+
111
+ # prepare a fake client that returns conflict for a specific file
112
+ class Res:
113
+ def __init__(self, data):
114
+ self.data = data
115
+
116
+ def execute(self):
117
+ return self
118
+
119
+ class Client:
120
+ def rpc(self, name, params):
121
+ return Res([{"status": "conflict", "owner": "bob"}])
122
+
123
+ def table(self, name):
124
+ class Q:
125
+ def delete(self):
126
+ return self
127
+
128
+ def eq(self, *a, **k):
129
+ return self
130
+
131
+ def execute(self):
132
+ return None
133
+
134
+ return Q()
135
+
136
+ mod.DEVELOPER_ID = "alice"
137
+ # ensure sets are clean
138
+ mod._active_conflicts.clear()
139
+ mod._local_owned_locks.clear()
140
+ client = Client()
141
+ mod._process_new_files(client, "main", {"a.txt"})
142
+ assert "a.txt" in mod._active_conflicts
143
+
144
+ # test _process_releases for ephemeral dev
145
+ monkeypatch.setattr(mod, "DEVELOPER_ID", "test_dev_1")
146
+ mod._process_releases(client, {"a.txt"})
147
+
148
+
149
+ def test_get_modified_and_unpushed_files_status_and_diff_migrated(
150
+ monkeypatch, tmp_path
151
+ ):
152
+ mod = load_watcher_module()
153
+ # Simulate git status output by creating a fake repo structure
154
+ repo = tmp_path / "repo"
155
+ repo.mkdir()
156
+ # Create a fake file and ensure _get_modified_and_unpushed_files handles it
157
+ f = repo / "src" / "new.py"
158
+ f.parent.mkdir(parents=True, exist_ok=True)
159
+ f.write_text("print('hi')")
160
+
161
+ # Monkeypatch subprocess.check_output used by watcher to get git status
162
+ def fake_check_output(cmd, *a, **k):
163
+ if "status" in cmd:
164
+ return b" M src/new.py\n"
165
+ if "rev-list" in cmd:
166
+ return b""
167
+ raise subprocess.CalledProcessError(1, cmd)
168
+
169
+ monkeypatch.setattr(subprocess, "check_output", fake_check_output)
170
+ # The watcher implementation reads from module-level _PROJECT_ROOT
171
+ monkeypatch.setattr(mod, "_PROJECT_ROOT", str(repo))
172
+ changed = mod._get_modified_and_unpushed_files()
173
+ assert isinstance(changed, set)
@@ -0,0 +1,71 @@
1
+ """Cover post-restart conflict abort branch in watcher prompt."""
2
+
3
+ from __future__ import annotations
4
+
5
+ import builtins
6
+ import sys
7
+
8
+ import pytest
9
+
10
+ from ._helpers import load_watcher_module
11
+
12
+ watcher = load_watcher_module()
13
+
14
+
15
+ def test_handle_post_restart_conflict_abort_choice(monkeypatch):
16
+ shutdown_called = []
17
+
18
+ class _Stdin:
19
+ @staticmethod
20
+ def isatty():
21
+ return True
22
+
23
+ monkeypatch.setattr(sys, "stdin", _Stdin())
24
+ monkeypatch.setattr(builtins, "input", lambda _prompt="": "4")
25
+ monkeypatch.setattr(
26
+ watcher,
27
+ "_graceful_shutdown",
28
+ lambda: shutdown_called.append(True),
29
+ )
30
+
31
+ exits = []
32
+
33
+ def _fake_exit(code):
34
+ exits.append(code)
35
+ raise SystemExit(code)
36
+
37
+ monkeypatch.setattr(sys, "exit", _fake_exit)
38
+
39
+ with pytest.raises(SystemExit) as exc:
40
+ watcher._handle_post_restart_conflict(
41
+ client=object(),
42
+ fp="src/live_locks_watcher.py",
43
+ lock_data={"owner": "dev", "branch": "main", "reason": "test"},
44
+ )
45
+
46
+ assert exc.value.code == 1
47
+ assert exits == [1]
48
+ assert shutdown_called == [True]
49
+
50
+
51
+ def test_handle_post_restart_conflict_dashboard_unavailable(monkeypatch, capsys):
52
+ choices = iter(["3", "1"])
53
+
54
+ class _Stdin:
55
+ @staticmethod
56
+ def isatty():
57
+ return True
58
+
59
+ monkeypatch.setattr(sys, "stdin", _Stdin())
60
+ monkeypatch.setattr(builtins, "input", lambda _prompt="": next(choices))
61
+ monkeypatch.setattr(watcher, "_dashboard_url", None)
62
+ monkeypatch.setattr(watcher, "_start_dashboard_server", lambda: None)
63
+
64
+ watcher._handle_post_restart_conflict(
65
+ client=object(),
66
+ fp="src/live_locks_watcher.py",
67
+ lock_data={"owner": "dev", "branch": "main", "reason": "test"},
68
+ )
69
+
70
+ out = capsys.readouterr().out
71
+ assert "Dashboard unavailable" in out