204 lines
7.8 KiB
Python
204 lines
7.8 KiB
Python
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("<strong>Remediation:</strong>", 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()
|