Files
govoplan/tests/test_release_plan_guidance.py
T
zemion f1fd143ef5
Dependency Audit / dependency-audit (push) Successful in 2m35s
Deployment Installer / deployment-installer (push) Successful in 6s
Security Audit / security-audit (push) Failing after 15s
Complete guided release console workflow
2026-07-31 05:46:50 +02:00

317 lines
12 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_gets_bounded_version_and_commit_steps(
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("attention", plan.status)
self.assertTrue(plan.source_preflight_ready)
self.assertFalse(
any(
item.code == "repository_version_alignment"
for item in plan.gate_findings
)
)
steps = {step.id: step for step in plan.dry_run_steps}
self.assertEqual("planned", steps["govoplan-files:version"].status)
self.assertEqual("planned", steps["govoplan-files:commit"].status)
self.assertIn("pyproject.toml", steps["govoplan-files:version"].detail)
self.assertTrue(
any(
"version metadata will be updated" in warning
for warning in plan.units[0].warnings
)
)
self.assertIn("python-package", plan.units[0].capabilities)
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_mixed_plan_tags_modules_before_core_and_aligns_before_push(
self,
) -> None:
with tempfile.TemporaryDirectory() as temp_dir:
workspace = Path(temp_dir)
for repo_name in ("govoplan-core", "govoplan-files"):
repo_path = workspace / repo_name
repo_path.mkdir()
(repo_path / "pyproject.toml").write_text(
f'[project]\nname = "{repo_name}"\nversion = "1.2.3"\n',
encoding="utf-8",
)
repositories = tuple(
RepositorySnapshot(
spec=RepositorySpec(
name=repo_name,
category="core" if repo_name == "govoplan-core" else "module",
subtype="platform" if repo_name == "govoplan-core" else "infrastructure",
remote=f"git@example.test:GovOPlaN/{repo_name}.git",
path=repo_name,
),
absolute_path=str(workspace / repo_name),
exists=True,
is_git=True,
has_head=True,
branch="main",
versions=VersionSnapshot(pyproject="1.2.3"),
local_target_tag_exists=False,
)
for repo_name in ("govoplan-core", "govoplan-files")
)
base = dashboard(workspace=workspace, version="1.2.3")
mixed = ReleaseDashboard(
generated_at=base.generated_at,
meta_root=base.meta_root,
workspace_root=base.workspace_root,
target_version=None,
target_tag=None,
online=False,
include_migrations=False,
summary=DashboardSummary(
repository_count=2,
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=repositories,
catalog=base.catalog,
)
plan = build_selective_release_plan(
mixed,
selected_repos=("govoplan-core", "govoplan-files"),
repo_versions={
"govoplan-core": "1.2.3",
"govoplan-files": "1.2.3",
},
)
self.assertEqual(
["govoplan-files", "govoplan-core"],
[unit.repo for unit in plan.units],
)
step_ids = [step.id for step in plan.dry_run_steps]
self.assertLess(
step_ids.index("govoplan-files:tag"),
step_ids.index("govoplan-core:tag"),
)
self.assertLess(
step_ids.index("govoplan-core:tag"),
step_ids.index("release:alignment"),
)
self.assertLess(
step_ids.index("release:alignment"),
step_ids.index("govoplan-files:push"),
)
self.assertEqual("release:install-verify", step_ids[-1])
core = next(unit for unit in plan.units if unit.repo == "govoplan-core")
self.assertIn("core-release-bundle", core.capabilities)
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 test_webui_projects_release_state_into_a_guided_workflow(self) -> None:
webui = (RELEASE_ROOT / "webui" / "index.html").read_text(encoding="utf-8")
for phase in (
"Inspect",
"Targets",
"Validate",
"Source",
"Package",
"Publish",
"Verify",
):
self.assertIn(f'label: "{phase}"', webui)
self.assertIn("releaseWorkflowPhases", webui)
self.assertIn("workflowPrimaryAction", webui)
self.assertIn("data-run-step-id", webui)
self.assertIn("invalidateReleaseDraft", webui)
self.assertIn("Installation Verification", webui)
self.assertIn("receipt-bound local gate", webui)
self.assertIn('step.id === "release:install-verify"', webui)
self.assertIn('const inspectionNotices = (summary.missing_count || 0)', webui)
self.assertIn('(summary.repository_count || 0) === 0', 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()