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,867 @@
1
+ """Tests for scripts/validate_code.py."""
2
+
3
+ from __future__ import annotations
4
+
5
+ import importlib.util
6
+ import io
7
+ import sys
8
+ from pathlib import Path
9
+ from types import SimpleNamespace
10
+ from unittest.mock import MagicMock
11
+
12
+ from tests.backend.unit.scripts._helpers import load_script_module
13
+
14
+ validate_code = load_script_module("validate_code.py", "validate_code_under_test")
15
+
16
+
17
+ def test_validate_backend_clears_mypy_cache_on_exists(monkeypatch, tmp_path):
18
+ """Verify that validate_python_backend clears stale .mypy_cache."""
19
+ # Create a mock mypy_cache directory
20
+ cache_dir = tmp_path / ".mypy_cache"
21
+ cache_dir.mkdir()
22
+ (cache_dir / "test_marker.txt").touch()
23
+
24
+ # Mock Path(".mypy_cache") to return our test directory
25
+ original_path = Path
26
+
27
+ def mock_path(p):
28
+ if p == ".mypy_cache":
29
+ return cache_dir
30
+ return original_path(p)
31
+
32
+ monkeypatch.setattr(validate_code, "Path", mock_path)
33
+
34
+ # Mock run_command to succeed for all commands
35
+ commands_seen = []
36
+
37
+ def mock_run_command(cmd, *_a, **_k):
38
+ commands_seen.append(cmd)
39
+ return True, ""
40
+
41
+ monkeypatch.setattr(validate_code, "run_command", mock_run_command)
42
+
43
+ # Run with files=None to trigger full backend validation
44
+ validate_code.validate_python_backend(quick=False, files=None)
45
+
46
+ # Verify cache was removed (directory should not exist anymore)
47
+ assert not cache_dir.exists(), "mypy_cache should have been removed"
48
+
49
+
50
+ def test_validate_backend_handles_cache_cleanup_error(monkeypatch, capsys):
51
+ """Verify graceful handling when cache cleanup fails."""
52
+ mock_cache = MagicMock()
53
+ mock_cache.exists.return_value = True
54
+
55
+ def mock_path(p):
56
+ if p == ".mypy_cache":
57
+ return mock_cache
58
+ return Path(p)
59
+
60
+ def mock_rmtree(path):
61
+ raise OSError("Permission denied")
62
+
63
+ monkeypatch.setattr(validate_code, "Path", mock_path)
64
+ monkeypatch.setattr(validate_code.shutil, "rmtree", mock_rmtree)
65
+
66
+ commands_seen = []
67
+
68
+ def mock_run_command(cmd, *_a, **_k):
69
+ commands_seen.append(cmd)
70
+ return True, ""
71
+
72
+ monkeypatch.setattr(validate_code, "run_command", mock_run_command)
73
+
74
+ # Should not crash even if cache removal fails
75
+ validate_code.validate_python_backend(quick=False, files=None)
76
+
77
+ # Verify warning was printed
78
+ captured = capsys.readouterr()
79
+ assert "Could not remove .mypy_cache" in captured.out or "Could not remove" in str(
80
+ captured
81
+ )
82
+
83
+
84
+ def test_format_failure_output_pytest_sections():
85
+ noisy_stdout = "\n".join(
86
+ [f"test_{index:03d} PASSED" for index in range(60)]
87
+ + [
88
+ "============================= FAILURES =============================",
89
+ "__________________________ test_example ___________________________",
90
+ "E AssertionError: assert 1 == 2",
91
+ "====================== short test summary info ======================",
92
+ "FAILED tests/backend/unit/test_example.py::test_example - AssertionError",
93
+ ]
94
+ )
95
+
96
+ formatted = validate_code.format_failure_output(noisy_stdout, "")
97
+ assert "Pytest short summary" in formatted
98
+ assert "FAILED tests/backend/unit/test_example.py::test_example" in formatted
99
+ assert "AssertionError" in formatted
100
+ assert "test_000 PASSED" not in formatted
101
+
102
+
103
+ def test_format_failure_output_generic_fallback():
104
+ stdout = "\n".join([f"line {index}" for index in range(220)])
105
+ formatted = validate_code.format_failure_output(stdout, "")
106
+ assert "First lines" in formatted
107
+ assert "Last lines" in formatted
108
+ assert "line 0" in formatted
109
+ assert "line 219" in formatted
110
+
111
+
112
+ def test_python_module_fallback_command_maps_known_tools():
113
+ cmd = validate_code._python_module_fallback_command(["ruff", "check", "src"])
114
+ assert cmd is not None
115
+ assert cmd[0].lower() == validate_code.sys.executable.lower()
116
+ assert cmd[1:3] == ["-m", "ruff"]
117
+
118
+ diff_cmd = validate_code._python_module_fallback_command(
119
+ ["diff-cover", "coverage.xml"]
120
+ )
121
+ assert diff_cmd is not None
122
+ assert diff_cmd[0].lower() == validate_code.sys.executable.lower()
123
+ assert diff_cmd[1:3] == ["-m", "diff_cover.diff_cover_tool"]
124
+
125
+ assert validate_code._python_module_fallback_command(["unknown-tool"]) is None
126
+
127
+
128
+ def test_run_command_merges_env_and_windows_roots(monkeypatch):
129
+ captured = {}
130
+
131
+ def _fake_run(*_args, **kwargs):
132
+ captured["env"] = kwargs["env"]
133
+ return SimpleNamespace(returncode=0, stdout="ok", stderr="")
134
+
135
+ monkeypatch.setattr(validate_code.subprocess, "run", _fake_run)
136
+ monkeypatch.setenv("SYSTEMDRIVE", "C:")
137
+ monkeypatch.setenv("PROGRAMDATA", r"C:\ProgramData")
138
+
139
+ success, _ = validate_code.run_command(
140
+ ["python", "-V"],
141
+ "python version",
142
+ check=False,
143
+ env={"CUSTOM_FLAG": "enabled"},
144
+ )
145
+
146
+ assert success is True
147
+ assert captured["env"]["CUSTOM_FLAG"] == "enabled"
148
+ assert captured["env"]["SYSTEMDRIVE"] == "C:"
149
+
150
+
151
+ def test_configure_coverage_data_file_skips_ci_environments(monkeypatch):
152
+ """Verify that CI environment routing is skipped when CI env vars are set."""
153
+ # Clear COVERAGE_FILE if it exists
154
+ monkeypatch.delenv("COVERAGE_FILE", raising=False)
155
+
156
+ # Set GITHUB_ACTIONS to simulate GitHub Actions CI
157
+ monkeypatch.setenv("GITHUB_ACTIONS", "true")
158
+
159
+ # Call the function — should return early due to CI detection
160
+ validate_code._configure_coverage_data_file()
161
+
162
+ # Verify COVERAGE_FILE was NOT set (early return due to CI check)
163
+ assert validate_code.os.getenv("COVERAGE_FILE") is None
164
+
165
+
166
+ def test_configure_coverage_data_file_applies_temp_routing_locally(monkeypatch):
167
+ """Verify temp directory routing is applied when NOT in CI."""
168
+ # Clear CI/GITHUB_ACTIONS and COVERAGE_FILE
169
+ monkeypatch.delenv("COVERAGE_FILE", raising=False)
170
+ monkeypatch.delenv("CI", raising=False)
171
+ monkeypatch.delenv("GITHUB_ACTIONS", raising=False)
172
+
173
+ # Call the function — should apply temp directory routing
174
+ validate_code._configure_coverage_data_file()
175
+
176
+ # Verify COVERAGE_FILE was set (temp directory routing applied)
177
+ assert validate_code.os.getenv("COVERAGE_FILE") is not None
178
+ assert "collab" in validate_code.os.getenv("COVERAGE_FILE")
179
+
180
+
181
+ def test_run_command_uses_python_module_resolution(monkeypatch):
182
+ calls = []
183
+
184
+ def _fake_run(cmd, **_kwargs):
185
+ calls.append(cmd)
186
+ return MagicMock(returncode=0, stdout="ok", stderr="")
187
+
188
+ monkeypatch.setattr(validate_code.subprocess, "run", _fake_run)
189
+
190
+ success, output = validate_code.run_command(
191
+ ["ruff", "check", "src"],
192
+ "Ruff linting",
193
+ check=False,
194
+ )
195
+
196
+ assert success is True
197
+ assert output == "ok"
198
+ assert len(calls) == 1
199
+ # Should use python -m resolution pre-emptively
200
+ assert calls[0][0].lower() == validate_code.sys.executable.lower()
201
+ assert calls[0][1:3] == ["-m", "ruff"]
202
+
203
+
204
+ def test_get_changed_files_collects_all_three_sources(monkeypatch):
205
+ payloads = [
206
+ "src/main.py\n",
207
+ "scripts/validate_code.py\n",
208
+ "new_file.py\n",
209
+ ]
210
+
211
+ def _run(*_a, **_k):
212
+ return SimpleNamespace(returncode=0, stdout=payloads.pop(0), stderr="")
213
+
214
+ monkeypatch.setattr(validate_code.subprocess, "run", _run)
215
+ changed = validate_code._get_changed_files()
216
+ assert "src/main.py" in changed
217
+ assert "scripts/validate_code.py" in changed
218
+ assert "new_file.py" in changed
219
+
220
+
221
+ def test_expand_input_paths_for_file_and_directory(tmp_path):
222
+ pkg = tmp_path / "pkg"
223
+ pkg.mkdir(parents=True)
224
+ f = pkg / "mod.py"
225
+ f.write_text("x = 1\n", encoding="utf-8")
226
+
227
+ expanded = validate_code._expand_input_paths([str(pkg), str(f), "raw\\x.py"])
228
+ assert any(p.endswith("pkg/mod.py") for p in expanded)
229
+ assert any(p.endswith("x.py") for p in expanded)
230
+
231
+
232
+ def test_detect_changed_scopes_branches(monkeypatch):
233
+ monkeypatch.setattr(validate_code, "_get_changed_files", lambda: [])
234
+ scopes = validate_code.detect_changed_scopes()
235
+ assert scopes["full_suite"] is True
236
+ assert scopes["changed_files"] == []
237
+
238
+ monkeypatch.setattr(validate_code, "_get_changed_files", lambda: ["pyproject.toml"])
239
+ scopes = validate_code.detect_changed_scopes()
240
+ assert scopes["full_suite"] is True
241
+ assert "Global config changed" in (scopes["reason"] or "")
242
+
243
+ monkeypatch.setattr(
244
+ validate_code,
245
+ "_get_changed_files",
246
+ lambda: ["scripts/validate_code.py"],
247
+ )
248
+ scopes = validate_code.detect_changed_scopes()
249
+ assert scopes["full_suite"] is True
250
+ assert "Infrastructure file changed" in (scopes["reason"] or "")
251
+
252
+ monkeypatch.setattr(
253
+ validate_code,
254
+ "_get_changed_files",
255
+ lambda: ["src/lock_client.py"],
256
+ )
257
+ scopes = validate_code.detect_changed_scopes()
258
+ assert scopes["full_suite"] is False
259
+ assert "tests/backend/unit/" in scopes["backend"]
260
+
261
+
262
+ def test_validate_python_backend_full_mode(monkeypatch):
263
+ commands = []
264
+
265
+ def _run_command(cmd, *_a, **_k):
266
+ commands.append(cmd)
267
+ return True, ""
268
+
269
+ monkeypatch.setattr(validate_code, "run_command", _run_command)
270
+ monkeypatch.setattr(validate_code.os.path, "exists", lambda p: p == "coverage.xml")
271
+
272
+ assert validate_code.validate_python_backend(quick=False, files=None) is True
273
+ assert any(cmd and cmd[0] == "isort" for cmd in commands)
274
+ assert any(cmd and cmd[0] == "diff-cover" for cmd in commands)
275
+
276
+
277
+ def test_validate_python_backend_quick_modes(monkeypatch):
278
+ monkeypatch.setattr(validate_code.os.path, "exists", lambda p: p == "coverage.xml")
279
+
280
+ seen = []
281
+
282
+ def _run_command(cmd, *_a, **_k):
283
+ seen.append(cmd)
284
+ # fail only diff-cover validation command
285
+ if cmd[:2] == ["diff-cover", "coverage.xml"]:
286
+ return False, "below threshold"
287
+ return True, ""
288
+
289
+ monkeypatch.setattr(validate_code, "run_command", _run_command)
290
+ monkeypatch.setattr(
291
+ validate_code,
292
+ "detect_changed_scopes",
293
+ lambda *a, **k: {
294
+ "full_suite": False,
295
+ "backend": ["tests/backend/unit/"],
296
+ "frontend": [],
297
+ "reason": None,
298
+ "changed_files": ["src/main.py", "tests/backend/unit/test_x.py"],
299
+ },
300
+ )
301
+
302
+ assert (
303
+ validate_code.validate_python_backend(
304
+ quick=True,
305
+ files=["src/main.py"],
306
+ )
307
+ is False
308
+ )
309
+ assert any("--include" in cmd for cmd in seen if cmd and cmd[0] == "diff-cover")
310
+
311
+ monkeypatch.setattr(
312
+ validate_code,
313
+ "detect_changed_scopes",
314
+ lambda *a, **k: {
315
+ "full_suite": False,
316
+ "backend": [],
317
+ "frontend": [],
318
+ "reason": None,
319
+ "changed_files": [],
320
+ },
321
+ )
322
+ monkeypatch.setattr(validate_code.os.path, "exists", lambda _p: False)
323
+ seen.clear()
324
+ assert (
325
+ validate_code.validate_python_backend(
326
+ quick=True,
327
+ files=["scripts/validate_code.py"],
328
+ )
329
+ is True
330
+ )
331
+
332
+
333
+ def test_validate_python_backend_diff_cover_soft_skip(monkeypatch):
334
+ monkeypatch.setattr(validate_code.os.path, "exists", lambda p: p == "coverage.xml")
335
+
336
+ def _run_command(cmd, *_a, **_k):
337
+ if cmd == ["diff-cover", "--version"]:
338
+ return False, "missing"
339
+ return True, ""
340
+
341
+ monkeypatch.setattr(validate_code, "run_command", _run_command)
342
+ assert validate_code.validate_python_backend(quick=False, files=None) is True
343
+
344
+
345
+ def test_validate_others_branches(monkeypatch):
346
+ monkeypatch.setattr(
347
+ validate_code.subprocess,
348
+ "run",
349
+ lambda *_a, **_k: SimpleNamespace(returncode=1),
350
+ )
351
+ assert validate_code.validate_others(files=["docs/readme.md"]) is True
352
+
353
+ calls = []
354
+
355
+ def _run(*_a, **_k):
356
+ return SimpleNamespace(returncode=0)
357
+
358
+ def _cmd(cmd, *_a, **_k):
359
+ calls.append(cmd)
360
+ return True, ""
361
+
362
+ monkeypatch.setattr(validate_code.subprocess, "run", _run)
363
+ monkeypatch.setattr(validate_code, "run_command", _cmd)
364
+ assert validate_code.validate_others(files=["docs/readme.md"]) is True
365
+ assert any(cmd and cmd[0] == "npx" for cmd in calls)
366
+
367
+
368
+ def test_validate_frontend_branches(monkeypatch):
369
+ monkeypatch.setattr(validate_code.shutil, "which", lambda name: None)
370
+ assert validate_code.validate_javascript_frontend(quick=False, files=None) is True
371
+
372
+ monkeypatch.setattr(validate_code.shutil, "which", lambda name: "/usr/bin/npm")
373
+ assert (
374
+ validate_code.validate_javascript_frontend(
375
+ quick=False,
376
+ files=["src/main.py"],
377
+ )
378
+ is True
379
+ )
380
+
381
+ calls = []
382
+
383
+ def _cmd(cmd, *_a, **_k):
384
+ calls.append(cmd)
385
+ return True, ""
386
+
387
+ monkeypatch.setattr(validate_code, "run_command", _cmd)
388
+ assert (
389
+ validate_code.validate_javascript_frontend(
390
+ quick=False,
391
+ files=["src/dashboard/app.js"],
392
+ )
393
+ is True
394
+ )
395
+ assert any(cmd and cmd[0] == "npx" for cmd in calls)
396
+
397
+
398
+ def test_main_exit_codes(monkeypatch):
399
+ monkeypatch.setattr(validate_code, "validate_python_backend", lambda **_k: True)
400
+ monkeypatch.setattr(
401
+ validate_code,
402
+ "validate_javascript_frontend",
403
+ lambda **_k: True,
404
+ )
405
+ monkeypatch.setattr(validate_code, "validate_others", lambda **_k: True)
406
+ monkeypatch.setattr(validate_code, "_run_cleanup", lambda: None)
407
+
408
+ monkeypatch.setattr(validate_code.sys, "argv", ["validate_code.py", "--backend"])
409
+ assert validate_code.main() == 0
410
+
411
+ monkeypatch.setattr(validate_code, "validate_python_backend", lambda **_k: False)
412
+ monkeypatch.setattr(validate_code.sys, "argv", ["validate_code.py", "--backend"])
413
+ assert validate_code.main() == 1
414
+
415
+
416
+ def test_main_file_filtering_short_circuit(monkeypatch):
417
+ monkeypatch.setattr(validate_code, "_run_cleanup", lambda: None)
418
+ monkeypatch.setattr(
419
+ validate_code.sys,
420
+ "argv",
421
+ ["validate_code.py", "README.txt"],
422
+ )
423
+ assert validate_code.main() == 0
424
+
425
+
426
+ def test_run_cleanup_output_paths(monkeypatch, capsys):
427
+ monkeypatch.setattr(validate_code, "clean_default", lambda dry_run=False: 2)
428
+ monkeypatch.setattr(validate_code, "clean_packaging", lambda dry_run=False: 0)
429
+ validate_code._run_cleanup()
430
+ assert "Removed 2 generated artifact(s)" in capsys.readouterr().out
431
+
432
+ monkeypatch.setattr(validate_code, "clean_default", lambda dry_run=False: 0)
433
+ monkeypatch.setattr(validate_code, "clean_packaging", lambda dry_run=False: 0)
434
+ validate_code._run_cleanup()
435
+ assert "Nothing to clean" in capsys.readouterr().out
436
+
437
+
438
+ def test_resolve_diff_compare_branch_paths(monkeypatch):
439
+ assert validate_code._resolve_diff_compare_branch(quick=True) == ("HEAD", None)
440
+
441
+ monkeypatch.setattr(validate_code, "_git_ref_exists", lambda ref: ref == "HEAD~1")
442
+ monkeypatch.setattr(validate_code, "_git_remote_origin_exists", lambda: False)
443
+ branch, warning = validate_code._resolve_diff_compare_branch(quick=False)
444
+ assert branch == "HEAD~1"
445
+ assert warning is not None and "HEAD~1" in warning
446
+
447
+ monkeypatch.setattr(validate_code, "_git_ref_exists", lambda _ref: False)
448
+ monkeypatch.setattr(validate_code, "_git_remote_origin_exists", lambda: False)
449
+ branch, warning = validate_code._resolve_diff_compare_branch(quick=False)
450
+ assert branch is None
451
+ assert warning is not None and "Unable to resolve" in warning
452
+
453
+ state: dict[str, bool] = {"fetched": False}
454
+
455
+ def _fake_ref(ref: str) -> bool:
456
+ if ref == "origin/main":
457
+ return state["fetched"]
458
+ if ref in {"main", "HEAD~1"}:
459
+ return False
460
+ return False
461
+
462
+ def _fake_run(cmd, **_kwargs):
463
+ if cmd[:3] == ["git", "fetch", "origin"]:
464
+ state["fetched"] = True
465
+ return SimpleNamespace(returncode=0, stdout="", stderr="")
466
+
467
+ monkeypatch.setattr(validate_code, "_git_ref_exists", _fake_ref)
468
+ monkeypatch.setattr(validate_code, "_git_remote_origin_exists", lambda: True)
469
+ monkeypatch.setattr(validate_code.subprocess, "run", _fake_run)
470
+ branch, warning = validate_code._resolve_diff_compare_branch(quick=False)
471
+ assert branch == "origin/main"
472
+ assert warning is not None and "resolved after fetching" in warning
473
+
474
+
475
+ def test_validate_backend_diff_cover_unresolved_compare_branch(monkeypatch):
476
+ commands = []
477
+
478
+ def _run_command(cmd, *_a, **_k):
479
+ commands.append(cmd)
480
+ return True, ""
481
+
482
+ monkeypatch.setattr(validate_code, "run_command", _run_command)
483
+ monkeypatch.setattr(validate_code.os.path, "exists", lambda p: p == "coverage.xml")
484
+ monkeypatch.setattr(
485
+ validate_code,
486
+ "_resolve_diff_compare_branch",
487
+ lambda _quick: (None, "missing compare branch"),
488
+ )
489
+
490
+ assert validate_code.validate_python_backend(quick=False, files=None) is False
491
+ assert not any(cmd[:2] == ["diff-cover", "coverage.xml"] for cmd in commands)
492
+
493
+
494
+ def test_validate_backend_diff_cover_warning_and_include_filter(monkeypatch):
495
+ seen = []
496
+
497
+ def _run_command(cmd, *_a, **_k):
498
+ seen.append(cmd)
499
+ return True, ""
500
+
501
+ monkeypatch.setattr(validate_code, "run_command", _run_command)
502
+ monkeypatch.setattr(validate_code.os.path, "exists", lambda p: p == "coverage.xml")
503
+ monkeypatch.setattr(
504
+ validate_code,
505
+ "detect_changed_scopes",
506
+ lambda *a, **k: {
507
+ "full_suite": False,
508
+ "backend": ["tests/backend/unit/"],
509
+ "frontend": [],
510
+ "reason": None,
511
+ "changed_files": ["scripts/validate_code.py", "README.md", "run.py"],
512
+ },
513
+ )
514
+ monkeypatch.setattr(
515
+ validate_code,
516
+ "_resolve_diff_compare_branch",
517
+ lambda _quick: ("HEAD", "branch warning"),
518
+ )
519
+
520
+ assert (
521
+ validate_code.validate_python_backend(
522
+ quick=True, files=["scripts/validate_code.py"]
523
+ )
524
+ is True
525
+ )
526
+ diff_cmd = next(cmd for cmd in seen if cmd[:2] == ["diff-cover", "coverage.xml"])
527
+ assert "--include" in diff_cmd
528
+ assert "scripts/validate_code.py" in diff_cmd
529
+ assert "run.py" in diff_cmd
530
+ assert "README.md" not in diff_cmd
531
+
532
+
533
+ def test_validate_others_early_return_and_exception(monkeypatch):
534
+ assert validate_code.validate_others(files=["src/main.py"]) is True
535
+
536
+ monkeypatch.setattr(
537
+ validate_code.subprocess,
538
+ "run",
539
+ lambda *_a, **_k: (_ for _ in ()).throw(RuntimeError("boom")),
540
+ )
541
+ assert validate_code.validate_others(files=["docs/readme.md"]) is True
542
+
543
+
544
+ def test_validate_frontend_glob_empty_and_failure(monkeypatch):
545
+ monkeypatch.setattr(validate_code.shutil, "which", lambda _name: "/usr/bin/npm")
546
+
547
+ # No frontend files provided should short-circuit successfully.
548
+ assert validate_code.validate_javascript_frontend(quick=False, files=[]) is True
549
+
550
+ monkeypatch.setattr(validate_code, "run_command", lambda *_a, **_k: (False, "bad"))
551
+ # Frontend validation now soft-skips strict failure when tooling is missing.
552
+ assert (
553
+ validate_code.validate_javascript_frontend(
554
+ quick=False,
555
+ files=["src/dashboard/app.js"],
556
+ )
557
+ is True
558
+ )
559
+
560
+
561
+ def test_validate_frontend_skips_jest_without_test_script(monkeypatch, capsys):
562
+ seen = []
563
+
564
+ def _cmd(cmd, *_a, **_k):
565
+ seen.append(cmd)
566
+ return True, ""
567
+
568
+ monkeypatch.setattr(validate_code.shutil, "which", lambda _name: "/usr/bin/npm")
569
+ monkeypatch.setattr(validate_code, "run_command", _cmd)
570
+ monkeypatch.setattr(
571
+ validate_code,
572
+ "_load_package_json_scripts",
573
+ lambda: {"validate": "python scripts/validate_code.py"},
574
+ )
575
+ monkeypatch.setattr(validate_code, "_has_playwright_test_files", lambda: False)
576
+
577
+ assert (
578
+ validate_code.validate_javascript_frontend(
579
+ quick=False,
580
+ files=["tests/frontend/playwright/test-utils.js"],
581
+ )
582
+ is True
583
+ )
584
+
585
+ out = capsys.readouterr().out
586
+ assert "No npm 'test' script configured — skipping Jest coverage run." in out
587
+ assert ["npm", "run", "test", "--", "--coverage"] not in seen
588
+
589
+
590
+ def test_validate_frontend_skips_playwright_without_test_files(monkeypatch, capsys):
591
+ seen = []
592
+
593
+ def _cmd(cmd, *_a, **_k):
594
+ seen.append(cmd)
595
+ return True, ""
596
+
597
+ monkeypatch.setattr(validate_code.shutil, "which", lambda _name: "/usr/bin/npm")
598
+ monkeypatch.setattr(validate_code, "run_command", _cmd)
599
+ monkeypatch.setattr(validate_code, "_load_package_json_scripts", lambda: {})
600
+ monkeypatch.setattr(validate_code, "_has_playwright_test_files", lambda: False)
601
+
602
+ assert (
603
+ validate_code.validate_javascript_frontend(
604
+ quick=False,
605
+ files=["tests/frontend/playwright/test-utils.js"],
606
+ )
607
+ is True
608
+ )
609
+
610
+ out = capsys.readouterr().out
611
+ assert "No Playwright test files found — skipping E2E validation." in out
612
+ assert ["npx", "playwright", "test", "--project=chromium"] not in seen
613
+
614
+
615
+ def test_load_package_json_scripts_handles_missing_invalid_and_non_dict(tmp_path):
616
+ current_dir = Path.cwd()
617
+ try:
618
+ validate_code.os.chdir(tmp_path)
619
+
620
+ assert validate_code._load_package_json_scripts() == {}
621
+
622
+ package_json = tmp_path / "package.json"
623
+ package_json.write_text("{invalid json", encoding="utf-8")
624
+ assert validate_code._load_package_json_scripts() == {}
625
+
626
+ package_json.write_text('{"scripts": []}', encoding="utf-8")
627
+ assert validate_code._load_package_json_scripts() == {}
628
+ finally:
629
+ validate_code.os.chdir(current_dir)
630
+
631
+
632
+ def test_load_package_json_scripts_returns_stringified_scripts(tmp_path):
633
+ current_dir = Path.cwd()
634
+ try:
635
+ validate_code.os.chdir(tmp_path)
636
+ (tmp_path / "package.json").write_text(
637
+ '{"scripts": {"test": "npm test", "lint": 123}}',
638
+ encoding="utf-8",
639
+ )
640
+
641
+ scripts = validate_code._load_package_json_scripts()
642
+ assert scripts == {"test": "npm test", "lint": "123"}
643
+ finally:
644
+ validate_code.os.chdir(current_dir)
645
+
646
+
647
+ def test_has_playwright_test_files_handles_missing_and_present(tmp_path):
648
+ current_dir = Path.cwd()
649
+ try:
650
+ validate_code.os.chdir(tmp_path)
651
+ assert validate_code._has_playwright_test_files() is False
652
+
653
+ test_dir = tmp_path / "tests" / "frontend" / "playwright"
654
+ test_dir.mkdir(parents=True)
655
+ (test_dir / "sample.spec.ts").write_text(
656
+ "test('x', () => {});\n",
657
+ encoding="utf-8",
658
+ )
659
+
660
+ assert validate_code._has_playwright_test_files() is True
661
+ finally:
662
+ validate_code.os.chdir(current_dir)
663
+
664
+
665
+ def test_validate_frontend_soft_skips_when_jest_command_fails(monkeypatch, capsys):
666
+ calls = []
667
+
668
+ def _cmd(cmd, *_a, **_k):
669
+ calls.append(cmd)
670
+ if cmd[:3] == ["npm", "run", "test"]:
671
+ return False, "jest failed"
672
+ return True, ""
673
+
674
+ monkeypatch.setattr(validate_code.shutil, "which", lambda _name: "/usr/bin/npm")
675
+ monkeypatch.setattr(validate_code, "run_command", _cmd)
676
+ monkeypatch.setattr(
677
+ validate_code,
678
+ "_load_package_json_scripts",
679
+ lambda: {"test": "jest"},
680
+ )
681
+ monkeypatch.setattr(validate_code, "_has_playwright_test_files", lambda: False)
682
+
683
+ assert validate_code.validate_javascript_frontend(quick=False, files=None) is True
684
+
685
+ out = capsys.readouterr().out
686
+ assert "Jest tests failed; skipping strict frontend failure." in out
687
+ assert any(cmd[:3] == ["npm", "run", "test"] for cmd in calls)
688
+
689
+
690
+ def test_validate_frontend_soft_skips_when_playwright_command_fails(
691
+ monkeypatch, capsys
692
+ ):
693
+ calls = []
694
+
695
+ def _cmd(cmd, *_a, **_k):
696
+ calls.append(cmd)
697
+ if cmd[:3] == ["npx", "playwright", "test"]:
698
+ return False, "playwright failed"
699
+ return True, ""
700
+
701
+ monkeypatch.setattr(validate_code.shutil, "which", lambda _name: "/usr/bin/npm")
702
+ monkeypatch.setattr(validate_code, "run_command", _cmd)
703
+ monkeypatch.setattr(validate_code, "_load_package_json_scripts", lambda: {})
704
+ monkeypatch.setattr(validate_code, "_has_playwright_test_files", lambda: True)
705
+
706
+ assert validate_code.validate_javascript_frontend(quick=False, files=None) is True
707
+
708
+ out = capsys.readouterr().out
709
+ assert "Playwright tests failed; skipping strict frontend failure." in out
710
+ assert any(cmd[:3] == ["npx", "playwright", "test"] for cmd in calls)
711
+
712
+
713
+ def test_summary_helper_prints_skipped(capsys):
714
+ validate_code._print_check_summary("Jest Tests", "skipped")
715
+ out = capsys.readouterr().out
716
+ assert "[SKIPPED] Jest Tests" in out
717
+
718
+
719
+ def test_validate_backend_summary_marks_skipped_checks(monkeypatch):
720
+ seen = []
721
+
722
+ def _run_command(cmd, *_a, **_k):
723
+ if cmd[:2] == ["diff-cover", "--version"]:
724
+ return False, "missing"
725
+ return True, ""
726
+
727
+ monkeypatch.setattr(
728
+ validate_code,
729
+ "_print_check_summary",
730
+ lambda name, status: seen.append((name, status)),
731
+ )
732
+ monkeypatch.setattr(validate_code, "run_command", _run_command)
733
+ monkeypatch.setattr(validate_code.os.path, "exists", lambda _p: False)
734
+ monkeypatch.setattr(
735
+ validate_code,
736
+ "detect_changed_scopes",
737
+ lambda *a, **k: {
738
+ "full_suite": False,
739
+ "backend": [],
740
+ "frontend": [],
741
+ "reason": None,
742
+ "changed_files": [],
743
+ },
744
+ )
745
+
746
+ assert (
747
+ validate_code.validate_python_backend(
748
+ quick=True,
749
+ files=["scripts/validate_code.py"],
750
+ )
751
+ is True
752
+ )
753
+ assert ("Tests", "skipped") in seen
754
+ assert ("Total Coverage Threshold", "skipped") in seen
755
+ assert ("Diff Coverage", "skipped") in seen
756
+
757
+
758
+ def test_validate_frontend_summary_marks_skipped_checks(monkeypatch, capsys):
759
+ monkeypatch.setattr(validate_code.shutil, "which", lambda _name: "/usr/bin/npm")
760
+ monkeypatch.setattr(validate_code, "run_command", lambda *_a, **_k: (True, ""))
761
+ monkeypatch.setattr(validate_code, "_load_package_json_scripts", lambda: {})
762
+ monkeypatch.setattr(validate_code, "_has_playwright_test_files", lambda: False)
763
+
764
+ assert validate_code.validate_javascript_frontend(quick=False, files=None) is True
765
+ out = capsys.readouterr().out
766
+ assert "[SKIPPED] Jest Tests" in out
767
+ assert "[SKIPPED] E2E Tests" in out
768
+
769
+
770
+ def test_validate_others_summary_marks_skipped_checks(monkeypatch, capsys):
771
+ monkeypatch.setattr(
772
+ validate_code.subprocess,
773
+ "run",
774
+ lambda *_a, **_k: SimpleNamespace(returncode=1),
775
+ )
776
+
777
+ assert validate_code.validate_others(files=["docs/readme.md"]) is True
778
+ out = capsys.readouterr().out
779
+ assert "[SKIPPED] Documentation Linting" in out
780
+
781
+
782
+ def test_print_helpers_and_tail_paths(capsys):
783
+ validate_code._print_failure_output("", "")
784
+ out = capsys.readouterr().out
785
+ assert out == ""
786
+
787
+ validate_code._print_failure_output("line\n", "")
788
+ out = capsys.readouterr().out
789
+ assert "Failure details" in out
790
+
791
+ validate_code._print_output_tail("", "Label", validate_code.Colors.OKCYAN)
792
+ assert capsys.readouterr().out == ""
793
+
794
+ long_text = "\n".join(f"line {i}" for i in range(220))
795
+ validate_code._print_output_tail(long_text, "Tail", validate_code.Colors.OKCYAN)
796
+ out = capsys.readouterr().out
797
+ assert "showing last" in out
798
+
799
+
800
+ def test_module_import_branches_for_encoding_and_missing_dotenv(monkeypatch):
801
+ class _DummyStream:
802
+ def __init__(self):
803
+ self.buffer = io.BytesIO()
804
+ self.encoding = "cp1252"
805
+
806
+ def write(self, text):
807
+ return len(text)
808
+
809
+ def flush(self):
810
+ return None
811
+
812
+ def reconfigure(self, **_kwargs):
813
+ raise RuntimeError("no reconfigure")
814
+
815
+ dummy_out = _DummyStream()
816
+ dummy_err = _DummyStream()
817
+
818
+ monkeypatch.setattr(sys, "stdout", dummy_out)
819
+ monkeypatch.setattr(sys, "stderr", dummy_err)
820
+ monkeypatch.setattr(sys, "platform", "win32", raising=False)
821
+ monkeypatch.setitem(sys.modules, "dotenv", None)
822
+
823
+ script_path = Path("scripts/validate_code.py").resolve()
824
+ spec = importlib.util.spec_from_file_location(
825
+ "validate_code_import_branch_ut",
826
+ script_path,
827
+ )
828
+ assert spec and spec.loader
829
+ mod = importlib.util.module_from_spec(spec)
830
+ spec.loader.exec_module(mod)
831
+
832
+ assert mod._load_dotenv is None
833
+
834
+
835
+ def test_git_helper_error_paths(monkeypatch):
836
+ monkeypatch.setattr(
837
+ validate_code.subprocess,
838
+ "run",
839
+ lambda *_a, **_k: (_ for _ in ()).throw(OSError("x")),
840
+ )
841
+ assert validate_code._git_ref_exists("origin/main") is False
842
+ assert validate_code._git_remote_origin_exists() is False
843
+
844
+
845
+ def test_main_unknown_args_and_multi_category_paths(monkeypatch):
846
+ monkeypatch.setattr(validate_code, "_run_cleanup", lambda: None)
847
+ monkeypatch.setattr(validate_code, "_expand_input_paths", lambda paths: paths)
848
+ monkeypatch.setattr(
849
+ validate_code,
850
+ "validate_javascript_frontend",
851
+ lambda **_k: False,
852
+ )
853
+ monkeypatch.setattr(validate_code, "validate_others", lambda **_k: True)
854
+ monkeypatch.setattr(validate_code, "validate_python_backend", lambda **_k: True)
855
+ monkeypatch.setattr(
856
+ validate_code.sys,
857
+ "argv",
858
+ [
859
+ "validate_code.py",
860
+ "--frontend",
861
+ "--docs",
862
+ "src/dashboard/app.js",
863
+ "README.md",
864
+ "--unknown-flag",
865
+ ],
866
+ )
867
+ assert validate_code.main() == 1