from __future__ import annotations import sys import tempfile import unittest from pathlib import Path META_ROOT = Path(__file__).resolve().parents[1] RELEASE_ROOT = META_ROOT / "tools" / "release" if str(RELEASE_ROOT) not in sys.path: sys.path.insert(0, str(RELEASE_ROOT)) from govoplan_release.model import ( # noqa: E402 CatalogSnapshot, DashboardSummary, ReleaseDashboard, RepositorySnapshot, RepositorySpec, VersionSnapshot, ) from govoplan_release.selective_planner import build_selective_release_plan # noqa: E402 class ReleasePlanGuidanceTests(unittest.TestCase): def test_unprepared_target_has_structured_remediation_and_recommendation( self, ) -> None: with tempfile.TemporaryDirectory() as temp_dir: workspace = Path(temp_dir) repo_path = workspace / "govoplan-files" repo_path.mkdir() (repo_path / "pyproject.toml").write_text( '[project]\nname = "govoplan-files"\nversion = "1.2.3"\n', encoding="utf-8", ) plan = build_selective_release_plan( dashboard(workspace=workspace, version="1.2.3"), selected_repos=("govoplan-files",), repo_versions={"govoplan-files": "1.2.4"}, ) self.assertEqual("blocked", plan.status) self.assertFalse(plan.source_preflight_ready) finding = next( item for item in plan.gate_findings if item.code == "repository_version_alignment" ) self.assertEqual("govoplan-files", finding.repo) self.assertEqual("pyproject.toml", finding.source) self.assertEqual("1.2.4", finding.expected) self.assertEqual("1.2.3", finding.actual) self.assertIn("Regenerate lockfiles", finding.remediation) self.assertEqual("resolve_release_gate", plan.recommended_action.id) self.assertEqual("govoplan-files", plan.recommended_action.repo) def test_aligned_target_recommends_non_mutating_tag_preview(self) -> None: with tempfile.TemporaryDirectory() as temp_dir: workspace = Path(temp_dir) repo_path = workspace / "govoplan-files" repo_path.mkdir() (repo_path / "pyproject.toml").write_text( '[project]\nname = "govoplan-files"\nversion = "1.2.4"\n', encoding="utf-8", ) plan = build_selective_release_plan( dashboard(workspace=workspace, version="1.2.4"), selected_repos=("govoplan-files",), repo_versions={"govoplan-files": "1.2.4"}, ) self.assertTrue(plan.source_preflight_ready) self.assertEqual("preview_source_release", plan.recommended_action.id) self.assertIn("Preview Tag + Publish", plan.recommended_action.remediation) def test_malformed_version_metadata_is_a_structured_blocker(self) -> None: cases = ( ("package.json", "{not-json\n", "JSONDecodeError"), ("pyproject.toml", "[project\n", "TOMLDecodeError"), ) for relative_path, malformed, error_name in cases: with self.subTest(relative_path=relative_path): with tempfile.TemporaryDirectory() as temp_dir: workspace = Path(temp_dir) repo_path = workspace / "govoplan-files" repo_path.mkdir() (repo_path / "pyproject.toml").write_text( '[project]\nname = "govoplan-files"\nversion = "1.2.4"\n', encoding="utf-8", ) (repo_path / relative_path).write_text(malformed, encoding="utf-8") plan = build_selective_release_plan( dashboard(workspace=workspace, version="1.2.4"), selected_repos=("govoplan-files",), repo_versions={"govoplan-files": "1.2.4"}, ) self.assertEqual("blocked", plan.status) finding = next( item for item in plan.gate_findings if item.code == "repository_version_metadata_unreadable" ) self.assertEqual("govoplan-files", finding.repo) self.assertIn(error_name, finding.message) self.assertIn("Repair the malformed TOML or JSON", finding.remediation) self.assertEqual("resolve_release_gate", plan.recommended_action.id) def test_malformed_webui_metadata_is_a_structured_blocker(self) -> None: with tempfile.TemporaryDirectory() as temp_dir: workspace = Path(temp_dir) repo_path = workspace / "govoplan-files" (repo_path / "webui").mkdir(parents=True) (repo_path / "pyproject.toml").write_text( '[project]\nname = "govoplan-files"\nversion = "1.2.4"\n', encoding="utf-8", ) (repo_path / "webui" / "package.json").write_text( "{not-json\n", encoding="utf-8" ) plan = build_selective_release_plan( dashboard(workspace=workspace, version="1.2.4"), selected_repos=("govoplan-files",), repo_versions={"govoplan-files": "1.2.4"}, ) self.assertEqual("blocked", plan.status) codes = {item.code for item in plan.gate_findings} self.assertIn("repository_version_metadata_unreadable", codes) finding = next( item for item in plan.gate_findings if item.code == "release_webui_metadata_unreadable" ) self.assertIn("JSONDecodeError", finding.message) self.assertIn("Repair malformed JSON", finding.remediation) self.assertEqual("resolve_release_gate", plan.recommended_action.id) def test_webui_renders_recommendation_and_remediation(self) -> None: webui = (RELEASE_ROOT / "webui" / "index.html").read_text(encoding="utf-8") self.assertIn("plan.gate_findings", webui) self.assertIn("plan.recommended_action", webui) self.assertIn("recommended next", webui) self.assertIn('plan.status === "blocked" ? "block"', webui) self.assertIn('pill("recommended next", recommendationKind)', webui) self.assertIn("Remediation:", webui) def dashboard(*, workspace: Path, version: str) -> ReleaseDashboard: repo = RepositorySnapshot( spec=RepositorySpec( name="govoplan-files", category="module", subtype="infrastructure", remote="git@example.test:GovOPlaN/govoplan-files.git", path="govoplan-files", ), absolute_path=str(workspace / "govoplan-files"), exists=True, is_git=True, has_head=True, branch="main", versions=VersionSnapshot(pyproject=version), local_target_tag_exists=False, ) return ReleaseDashboard( generated_at="2026-07-22T00:00:00Z", meta_root=str(workspace / "govoplan"), workspace_root=str(workspace), target_version=None, target_tag=None, online=False, include_migrations=False, summary=DashboardSummary( repository_count=1, missing_count=0, dirty_count=0, ahead_count=0, behind_count=0, no_head_count=0, error_count=0, safe_directory_count=0, local_target_tag_missing_count=0, status="ready", ), repositories=(repo,), catalog=CatalogSnapshot( channel="stable", website_path="", catalog_path="", catalog_exists=False, keyring_path="", keyring_exists=False, ), ) if __name__ == "__main__": unittest.main()