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,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