188 lines
6.7 KiB
Python
188 lines
6.7 KiB
Python
from __future__ import annotations
|
|
|
|
import importlib.util
|
|
from pathlib import Path
|
|
import sys
|
|
import unittest
|
|
|
|
|
|
ROOT = Path(__file__).resolve().parents[1]
|
|
SCRIPT = ROOT / "scripts" / "release-doctor.py"
|
|
|
|
|
|
def load_doctor_module():
|
|
spec = importlib.util.spec_from_file_location("release_doctor", SCRIPT)
|
|
if spec is None or spec.loader is None:
|
|
raise RuntimeError(f"Could not load {SCRIPT}")
|
|
module = importlib.util.module_from_spec(spec)
|
|
sys.modules[spec.name] = module
|
|
spec.loader.exec_module(module)
|
|
return module
|
|
|
|
|
|
class ReleaseDoctorTests(unittest.TestCase):
|
|
def test_release_repo_names_include_catalog_and_migration_baseline_owners(self) -> None:
|
|
doctor = load_doctor_module()
|
|
|
|
names = set(doctor.release_repo_names(ROOT.parent))
|
|
|
|
self.assertIn("govoplan-core", names)
|
|
self.assertIn("govoplan-files", names)
|
|
self.assertIn("govoplan-idm", names)
|
|
|
|
def test_repo_findings_detect_dirty_repositories(self) -> None:
|
|
doctor = load_doctor_module()
|
|
repo = doctor.RepoState(
|
|
name="govoplan-files",
|
|
path="/tmp/govoplan-files",
|
|
exists=True,
|
|
dirty_entries=(" M pyproject.toml",),
|
|
pyproject_version="0.1.7",
|
|
local_tag_exists=True,
|
|
)
|
|
|
|
findings = doctor.repo_findings((repo,), target_version="0.1.7", target_tag="v0.1.7")
|
|
|
|
self.assertEqual("blocker", findings[0].severity)
|
|
self.assertEqual("repo-dirty", findings[0].check_id)
|
|
self.assertIn("git status --short", [item.command for item in findings[0].commands])
|
|
|
|
def test_dubious_ownership_errors_suggest_safe_directory_command(self) -> None:
|
|
doctor = load_doctor_module()
|
|
repo = doctor.RepoState(
|
|
name="govoplan-files",
|
|
path="/tmp/govoplan-files",
|
|
exists=True,
|
|
safe_directory_required=True,
|
|
safe_directory_command="git config --global --add safe.directory /tmp/govoplan-files",
|
|
errors=("fatal: detected dubious ownership in repository",),
|
|
)
|
|
|
|
findings = doctor.repo_findings((repo,), target_version="0.1.7", target_tag="v0.1.7")
|
|
|
|
self.assertEqual("repo-safe-directory", findings[0].check_id)
|
|
self.assertEqual("blocker", findings[0].severity)
|
|
self.assertTrue(findings[0].commands[0].mutating)
|
|
self.assertIn("safe.directory", findings[0].commands[0].command)
|
|
|
|
def test_dubious_ownership_detection_matches_git_error(self) -> None:
|
|
doctor = load_doctor_module()
|
|
|
|
self.assertTrue(
|
|
doctor.is_dubious_ownership_error(
|
|
"fatal: detected dubious ownership in repository at '/workspace/repo'\n"
|
|
"git config --global --add safe.directory /workspace/repo"
|
|
)
|
|
)
|
|
|
|
def test_release_migration_strict_errors_suggest_recording_baseline(self) -> None:
|
|
doctor = load_doctor_module()
|
|
|
|
findings = doctor.migration_findings(
|
|
{
|
|
"release": {
|
|
"ok": True,
|
|
"graph_errors": [],
|
|
"strict_errors": ["current migration heads are not recorded"],
|
|
},
|
|
"dev": {"ok": True, "graph_errors": [], "strict_errors": []},
|
|
},
|
|
target_version="0.2.0",
|
|
)
|
|
|
|
self.assertEqual("migration-release-baseline", findings[0].check_id)
|
|
self.assertEqual("blocker", findings[0].severity)
|
|
commands = [item.command for item in findings[0].commands]
|
|
self.assertTrue(any("--record-release 0.2.0" in item for item in commands))
|
|
|
|
def test_catalog_findings_detect_missing_signatures(self) -> None:
|
|
doctor = load_doctor_module()
|
|
|
|
findings = doctor.catalog_findings(
|
|
{
|
|
"catalog_exists": True,
|
|
"catalog_path": "/tmp/stable.json",
|
|
"core_release_version": "0.1.7",
|
|
"signature_count": 0,
|
|
"keyring_exists": True,
|
|
"key_count": 1,
|
|
},
|
|
target_version="0.1.7",
|
|
online=False,
|
|
)
|
|
|
|
self.assertEqual("catalog-signatures", findings[0].check_id)
|
|
self.assertEqual("blocker", findings[0].severity)
|
|
|
|
def test_suggested_commands_are_deduplicated(self) -> None:
|
|
doctor = load_doctor_module()
|
|
suggested = doctor.SuggestedCommand(title="Status", command="git status --short", cwd="/tmp/repo")
|
|
report = doctor.DoctorReport(
|
|
generated_at="2026-07-11T00:00:00Z",
|
|
workspace_root="/tmp",
|
|
target_version="0.1.7",
|
|
target_tag="v0.1.7",
|
|
online=False,
|
|
overall_status="blocked",
|
|
repos=(),
|
|
findings=(
|
|
doctor.Finding("a", "blocker", "A", "", (suggested,)),
|
|
doctor.Finding("b", "warning", "B", "", (suggested,)),
|
|
),
|
|
)
|
|
|
|
self.assertEqual((suggested,), doctor.collect_suggested_commands(report))
|
|
|
|
def test_default_report_is_concise_and_keeps_details_out(self) -> None:
|
|
doctor = load_doctor_module()
|
|
repo = doctor.RepoState(
|
|
name="govoplan-files",
|
|
path="/tmp/govoplan-files",
|
|
exists=True,
|
|
dirty_entries=(" M pyproject.toml",),
|
|
)
|
|
report = doctor.DoctorReport(
|
|
generated_at="2026-07-11T00:00:00Z",
|
|
workspace_root="/tmp",
|
|
target_version="0.1.7",
|
|
target_tag="v0.1.7",
|
|
online=False,
|
|
overall_status="blocked",
|
|
repos=(repo,),
|
|
findings=(doctor.Finding("repo-dirty", "blocker", "govoplan-files has uncommitted changes", "M pyproject.toml"),),
|
|
)
|
|
|
|
concise = doctor.render_text_report(report, detailed=False, include_commands=False)
|
|
detailed = doctor.render_text_report(report, detailed=True, include_commands=False)
|
|
|
|
self.assertIn("dirty=1", concise)
|
|
self.assertNotIn("M pyproject.toml", concise)
|
|
self.assertIn("M pyproject.toml", detailed)
|
|
|
|
def test_interactive_actions_offer_dirty_bulk_commit_push(self) -> None:
|
|
doctor = load_doctor_module()
|
|
repo = doctor.RepoState(
|
|
name="govoplan-files",
|
|
path="/tmp/govoplan-files",
|
|
exists=True,
|
|
dirty_entries=(" M pyproject.toml",),
|
|
)
|
|
report = doctor.DoctorReport(
|
|
generated_at="2026-07-11T00:00:00Z",
|
|
workspace_root="/tmp",
|
|
target_version="0.1.7",
|
|
target_tag="v0.1.7",
|
|
online=False,
|
|
overall_status="blocked",
|
|
repos=(repo,),
|
|
findings=(),
|
|
)
|
|
|
|
actions = dict(doctor.interactive_actions(report))
|
|
|
|
self.assertIn("commit-push-dirty", actions)
|
|
|
|
|
|
if __name__ == "__main__":
|
|
unittest.main()
|