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.
- collab/__init__.py +77 -0
- collab/__main__.py +11 -0
- collab_runtime-0.2.9.dist-info/METADATA +218 -0
- collab_runtime-0.2.9.dist-info/RECORD +82 -0
- collab_runtime-0.2.9.dist-info/WHEEL +5 -0
- collab_runtime-0.2.9.dist-info/entry_points.txt +3 -0
- collab_runtime-0.2.9.dist-info/licenses/LICENSE +21 -0
- collab_runtime-0.2.9.dist-info/top_level.txt +10 -0
- scripts/cleanup.py +395 -0
- scripts/collab_git_hook.py +190 -0
- scripts/format_code.py +594 -0
- scripts/generate_tests.py +560 -0
- scripts/validate_code.py +1397 -0
- src/__init__.py +4 -0
- src/dashboard/index.html +1131 -0
- src/live_locks_watcher.py +1982 -0
- src/lock_client.py +4268 -0
- src/logging_config.py +259 -0
- src/main.py +436 -0
- tests/backend/__init__.py +0 -0
- tests/backend/functional/__init__.py +0 -0
- tests/backend/functional/test_package_imports.py +43 -0
- tests/backend/integration/__init__.py +0 -0
- tests/backend/integration/test_cli_contract_parity.py +220 -0
- tests/backend/performance/__init__.py +0 -0
- tests/backend/reliability/__init__.py +0 -0
- tests/backend/security/__init__.py +0 -0
- tests/backend/unit/live_locks_watcher/__init__.py +5 -0
- tests/backend/unit/live_locks_watcher/_helpers.py +123 -0
- tests/backend/unit/live_locks_watcher/conftest.py +18 -0
- tests/backend/unit/live_locks_watcher/test_live_locks_watcher_dashboard.py +188 -0
- tests/backend/unit/live_locks_watcher/test_live_locks_watcher_developer.py +56 -0
- tests/backend/unit/live_locks_watcher/test_live_locks_watcher_graceful_shutdown.py +459 -0
- tests/backend/unit/live_locks_watcher/test_live_locks_watcher_main.py +1925 -0
- tests/backend/unit/live_locks_watcher/test_live_locks_watcher_module.py +187 -0
- tests/backend/unit/live_locks_watcher/test_live_locks_watcher_multi_session.py +320 -0
- tests/backend/unit/live_locks_watcher/test_live_locks_watcher_notify.py +67 -0
- tests/backend/unit/live_locks_watcher/test_live_locks_watcher_parsing.py +155 -0
- tests/backend/unit/live_locks_watcher/test_live_locks_watcher_process_helpers.py +684 -0
- tests/backend/unit/live_locks_watcher/test_live_locks_watcher_processing.py +173 -0
- tests/backend/unit/live_locks_watcher/test_live_locks_watcher_prompt_abort.py +71 -0
- tests/backend/unit/live_locks_watcher/test_live_locks_watcher_reconcile.py +516 -0
- tests/backend/unit/live_locks_watcher/test_live_locks_watcher_scan.py +296 -0
- tests/backend/unit/lock_client/__init__.py +1 -0
- tests/backend/unit/lock_client/_helpers.py +132 -0
- tests/backend/unit/lock_client/test_lock_client_acquire.py +214 -0
- tests/backend/unit/lock_client/test_lock_client_active.py +104 -0
- tests/backend/unit/lock_client/test_lock_client_api.py +63 -0
- tests/backend/unit/lock_client/test_lock_client_cli.py +682 -0
- tests/backend/unit/lock_client/test_lock_client_daemon.py +3730 -0
- tests/backend/unit/lock_client/test_lock_client_dashboard.py +438 -0
- tests/backend/unit/lock_client/test_lock_client_discover.py +241 -0
- tests/backend/unit/lock_client/test_lock_client_force_release.py +354 -0
- tests/backend/unit/lock_client/test_lock_client_helper_branches.py +1890 -0
- tests/backend/unit/lock_client/test_lock_client_history.py +301 -0
- tests/backend/unit/lock_client/test_lock_client_isolation.py +316 -0
- tests/backend/unit/lock_client/test_lock_client_pid.py +75 -0
- tests/backend/unit/lock_client/test_lock_client_reconcile.py +464 -0
- tests/backend/unit/lock_client/test_lock_client_release.py +77 -0
- tests/backend/unit/lock_client/test_lock_client_shutdown.py +1110 -0
- tests/backend/unit/lock_client/test_lock_client_utils.py +474 -0
- tests/backend/unit/lock_client/test_lock_client_watch.py +866 -0
- tests/backend/unit/scripts/__init__.py +1 -0
- tests/backend/unit/scripts/_helpers.py +42 -0
- tests/backend/unit/scripts/test_cleanup.py +285 -0
- tests/backend/unit/scripts/test_collab_git_hook.py +280 -0
- tests/backend/unit/scripts/test_collab_git_hook_ported.py +50 -0
- tests/backend/unit/scripts/test_format_code.py +368 -0
- tests/backend/unit/scripts/test_format_code_ported.py +177 -0
- tests/backend/unit/scripts/test_generate_tests.py +305 -0
- tests/backend/unit/scripts/test_hook_templates.py +357 -0
- tests/backend/unit/scripts/test_setup_hook_overlay.py +95 -0
- tests/backend/unit/scripts/test_validate_code.py +867 -0
- tests/backend/unit/scripts/test_validate_code_ported.py +237 -0
- tests/backend/unit/test_entrypoints_main_run.py +83 -0
- tests/backend/unit/test_logging_config.py +529 -0
- tests/backend/unit/test_main_watch_pid_file.py +278 -0
- tests/conftest.py +167 -0
- tests/frontend/__init__.py +0 -0
- tests/frontend/jest/__init__.py +0 -0
- tests/frontend/playwright/__init__.py +0 -0
- tests/packaging/test_smoke_install.py +76 -0
|
@@ -0,0 +1,104 @@
|
|
|
1
|
+
"""Active-lock related tests for LockClient."""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
from ._helpers import (
|
|
6
|
+
FakeClient,
|
|
7
|
+
FakeResponse,
|
|
8
|
+
load_lock_client_module,
|
|
9
|
+
make_create_client,
|
|
10
|
+
)
|
|
11
|
+
|
|
12
|
+
mod = load_lock_client_module()
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
def test_active_locks(monkeypatch):
|
|
16
|
+
"""Test retrieving all active locks."""
|
|
17
|
+
monkeypatch.setenv("SUPABASE_URL", "https://test.supabase.co")
|
|
18
|
+
monkeypatch.setenv("SUPABASE_ANON_KEY", "test_key")
|
|
19
|
+
|
|
20
|
+
locks_data = [
|
|
21
|
+
{"file_path": "src/app.py", "developer_id": "user1"},
|
|
22
|
+
{"file_path": "src/routes.py", "developer_id": "user2"},
|
|
23
|
+
]
|
|
24
|
+
response = FakeResponse(status=200, data=locks_data)
|
|
25
|
+
monkeypatch.setattr(mod, "_get_create_client", lambda: make_create_client(response))
|
|
26
|
+
|
|
27
|
+
lc = mod.LockClient(developer_id="test_user")
|
|
28
|
+
locks = lc.active()
|
|
29
|
+
assert len(locks) == 2
|
|
30
|
+
assert locks[0]["file_path"] == "src/app.py"
|
|
31
|
+
|
|
32
|
+
|
|
33
|
+
def test_active_locks_exception(monkeypatch):
|
|
34
|
+
"""Test active() returns empty list on API exception."""
|
|
35
|
+
monkeypatch.setenv("SUPABASE_URL", "https://test.supabase.co")
|
|
36
|
+
monkeypatch.setenv("SUPABASE_ANON_KEY", "test_key")
|
|
37
|
+
|
|
38
|
+
class ExplodingClient(FakeClient):
|
|
39
|
+
def execute(self):
|
|
40
|
+
raise RuntimeError("Network error")
|
|
41
|
+
|
|
42
|
+
monkeypatch.setattr(
|
|
43
|
+
mod,
|
|
44
|
+
"_get_create_client",
|
|
45
|
+
lambda: (lambda url, key: ExplodingClient(FakeResponse())),
|
|
46
|
+
)
|
|
47
|
+
|
|
48
|
+
lc = mod.LockClient(developer_id="test_user")
|
|
49
|
+
locks = lc.active()
|
|
50
|
+
assert locks == []
|
|
51
|
+
|
|
52
|
+
|
|
53
|
+
def test_active_locks_with_error(monkeypatch):
|
|
54
|
+
"""Test active() returns empty list when response has error."""
|
|
55
|
+
monkeypatch.setenv("SUPABASE_URL", "https://test.supabase.co")
|
|
56
|
+
monkeypatch.setenv("SUPABASE_ANON_KEY", "test_key")
|
|
57
|
+
|
|
58
|
+
response = FakeResponse(status=500, data=None, error="Error")
|
|
59
|
+
monkeypatch.setattr(mod, "_get_create_client", lambda: make_create_client(response))
|
|
60
|
+
|
|
61
|
+
lc = mod.LockClient(developer_id="test_user")
|
|
62
|
+
locks = lc.active()
|
|
63
|
+
assert locks == []
|
|
64
|
+
|
|
65
|
+
|
|
66
|
+
def test_get_lock_status_expired(monkeypatch):
|
|
67
|
+
"""Test get_lock_status marks expired locks as unlocked (server-side expiry not
|
|
68
|
+
enforced)."""
|
|
69
|
+
monkeypatch.setenv("SUPABASE_URL", "https://test.supabase.co")
|
|
70
|
+
monkeypatch.setenv("SUPABASE_ANON_KEY", "test_key")
|
|
71
|
+
|
|
72
|
+
from datetime import datetime, timedelta, timezone
|
|
73
|
+
|
|
74
|
+
past = (datetime.now(timezone.utc) - timedelta(hours=1)).isoformat()
|
|
75
|
+
lock_data = {
|
|
76
|
+
"file_path": "src/app.py",
|
|
77
|
+
"developer_id": "other_user",
|
|
78
|
+
"acquired_at": "2025-01-01T10:00:00+00:00",
|
|
79
|
+
"expires_at": past,
|
|
80
|
+
}
|
|
81
|
+
response = FakeResponse(status=200, data=[lock_data])
|
|
82
|
+
monkeypatch.setattr(mod, "_get_create_client", lambda: make_create_client(response))
|
|
83
|
+
|
|
84
|
+
lc = mod.LockClient(developer_id="test_user")
|
|
85
|
+
status = lc.get_lock_status("src/app.py")
|
|
86
|
+
# With server-side expiry disabled, presence of a DB row implies an active
|
|
87
|
+
# lock until explicitly released. The client does not evaluate expires_at.
|
|
88
|
+
assert status["is_locked"] is True
|
|
89
|
+
assert status["locked_by"] == "other_user"
|
|
90
|
+
assert status["can_edit"] is False
|
|
91
|
+
|
|
92
|
+
|
|
93
|
+
# RESTORED: test_get_lock_status_no_lock
|
|
94
|
+
def test_get_lock_status_no_lock(monkeypatch):
|
|
95
|
+
monkeypatch.setenv("SUPABASE_URL", "https://example.invalid")
|
|
96
|
+
monkeypatch.setenv("SUPABASE_ANON_KEY", "anon:fake")
|
|
97
|
+
response = FakeResponse(status=200, data=[])
|
|
98
|
+
monkeypatch.setattr(mod, "_get_create_client", lambda: make_create_client(response))
|
|
99
|
+
|
|
100
|
+
lc = mod.LockClient(developer_id="tester")
|
|
101
|
+
status = lc.get_lock_status("some/file.py")
|
|
102
|
+
assert isinstance(status, dict)
|
|
103
|
+
assert status.get("is_locked") is False
|
|
104
|
+
assert status.get("can_edit") is True
|
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
from ._helpers import FakeClient, FakeResponse, load_lock_client_module
|
|
2
|
+
|
|
3
|
+
# acquire tests moved to test_lock_client_acquire.py
|
|
4
|
+
# force_release tests moved to test_lock_client_force_release.py
|
|
5
|
+
# release tests moved to test_lock_client_release.py
|
|
6
|
+
# This file now covers only active() and get_lock_status()
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
def test_active_and_get_lock_status(monkeypatch):
|
|
10
|
+
mod = load_lock_client_module()
|
|
11
|
+
client = mod.LockClient(local_only=True, developer_id="frank")
|
|
12
|
+
client._client = FakeClient(
|
|
13
|
+
FakeResponse(
|
|
14
|
+
status=200,
|
|
15
|
+
data=[
|
|
16
|
+
{
|
|
17
|
+
"file_path": "a",
|
|
18
|
+
"developer_id": "frank",
|
|
19
|
+
"acquired_at": "t",
|
|
20
|
+
"reason": "r",
|
|
21
|
+
}
|
|
22
|
+
],
|
|
23
|
+
)
|
|
24
|
+
)
|
|
25
|
+
act = client.active()
|
|
26
|
+
assert isinstance(act, list) and act
|
|
27
|
+
info = client.get_lock_status("a")
|
|
28
|
+
assert info.get("is_locked") is True
|
|
29
|
+
assert info.get("locked_by") == "frank"
|
|
30
|
+
|
|
31
|
+
|
|
32
|
+
def test_force_release_all_chunking(monkeypatch):
|
|
33
|
+
mod = load_lock_client_module()
|
|
34
|
+
client = mod.LockClient(local_only=True, developer_id="root")
|
|
35
|
+
client._is_admin = True
|
|
36
|
+
|
|
37
|
+
# Simulate many active locks
|
|
38
|
+
monkeypatch.setattr(
|
|
39
|
+
client, "active", lambda: [{"file_path": f"f{i}"} for i in range(5)]
|
|
40
|
+
)
|
|
41
|
+
|
|
42
|
+
class RespNone:
|
|
43
|
+
status = 200
|
|
44
|
+
data = None
|
|
45
|
+
error = None
|
|
46
|
+
|
|
47
|
+
class CustomClient:
|
|
48
|
+
def table(self, *a, **k):
|
|
49
|
+
return self
|
|
50
|
+
|
|
51
|
+
def delete(self, *a, **k):
|
|
52
|
+
return self
|
|
53
|
+
|
|
54
|
+
def in_(self, *a, **k):
|
|
55
|
+
return self
|
|
56
|
+
|
|
57
|
+
def execute(self):
|
|
58
|
+
return RespNone()
|
|
59
|
+
|
|
60
|
+
client._client = CustomClient()
|
|
61
|
+
released = client.force_release_all()
|
|
62
|
+
assert isinstance(released, int)
|
|
63
|
+
assert released >= 0
|