Files
govoplan/tests/test_version_alignment.py

185 lines
7.8 KiB
Python

from __future__ import annotations
import json
from pathlib import Path
import sys
import tempfile
import unittest
META_ROOT = Path(__file__).resolve().parents[1]
RELEASE_TOOLS_ROOT = META_ROOT / "tools" / "release"
if str(RELEASE_TOOLS_ROOT) not in sys.path:
sys.path.insert(0, str(RELEASE_TOOLS_ROOT))
from govoplan_release.version_alignment import ( # noqa: E402
candidate_catalog_version_issues,
release_composition_issues,
repository_version_issues,
selected_repository_version_issues,
)
from govoplan_release.model import RepositorySnapshot, RepositorySpec, VersionSnapshot # noqa: E402
from govoplan_release.selective_planner import build_unit # noqa: E402
from govoplan_release.selective_catalog import enforce_selected_version_alignment # noqa: E402
class VersionAlignmentTests(unittest.TestCase):
def test_candidate_catalog_refs_and_selected_units_must_match_versions(self) -> None:
payload = {
"core_release": {
"version": "1.2.3",
"python_ref": "govoplan-core @ git+ssh://git@example.test/acme/govoplan-core.git@v1.2.3",
},
"modules": [
{
"module_id": "files",
"version": "1.2.3",
"python_ref": "govoplan-files @ git+ssh://git@example.test/acme/govoplan-files.git@v1.2.4",
"webui_package": "@govoplan/files-webui",
"webui_ref": "git+ssh://git@example.test/acme/govoplan-files.git#v1.2.3",
}
],
"release": {
"selected_units": [
{"repo": "govoplan-files", "version": "1.2.5", "tag": "v1.2.6"},
]
},
}
issues = candidate_catalog_version_issues(payload)
messages = {issue.message for issue in issues}
self.assertIn("Python ref tag must match the catalog entry version", messages)
self.assertIn("selected-unit tag must match its version", messages)
self.assertIn("selected-unit version must match the catalog entry", messages)
def test_catalog_candidate_gate_rejects_selected_repo_drift(self) -> None:
with tempfile.TemporaryDirectory() as tmp:
workspace = Path(tmp)
repo = workspace / "govoplan-files"
(repo / "webui").mkdir(parents=True)
(repo / "pyproject.toml").write_text('[project]\nname="govoplan-files"\nversion="1.2.3"\n')
(repo / "webui" / "package.json").write_text('{"version":"1.2.4"}\n')
with self.assertRaisesRegex(ValueError, "Version alignment gate failed"):
enforce_selected_version_alignment(
repo_versions={"govoplan-files": "1.2.5"},
workspace=workspace,
)
def test_catalog_candidate_gate_rejects_requested_version_that_differs_from_source(self) -> None:
with tempfile.TemporaryDirectory() as tmp:
workspace = Path(tmp)
repo = workspace / "govoplan-files"
(repo / "webui").mkdir(parents=True)
(repo / "pyproject.toml").write_text('[project]\nname="govoplan-files"\nversion="1.2.3"\n')
(repo / "webui" / "package.json").write_text('{"version":"1.2.3"}\n')
with self.assertRaisesRegex(ValueError, "source version must match the requested release version"):
enforce_selected_version_alignment(
repo_versions={"govoplan-files": "1.2.4"},
workspace=workspace,
)
def test_selected_repository_gate_reports_missing_version_metadata(self) -> None:
with tempfile.TemporaryDirectory() as tmp:
workspace = Path(tmp)
(workspace / "govoplan-files").mkdir()
issues = selected_repository_version_issues(
repo_versions={"govoplan-files": "1.2.3"},
workspace=workspace,
)
self.assertEqual(1, len(issues))
self.assertEqual("repository has no version metadata", issues[0].message)
def test_selective_release_unit_is_blocked_by_version_drift(self) -> None:
repo = RepositorySnapshot(
spec=RepositorySpec(
name="govoplan-example",
category="module",
subtype="domain",
remote="git@example.test:acme/govoplan-example.git",
path="govoplan-example",
),
absolute_path="/tmp/govoplan-example",
exists=True,
is_git=True,
has_head=True,
versions=VersionSnapshot(pyproject="1.2.3", webui_package="1.2.4", manifests=("1.2.3",)),
)
unit = build_unit(repo, target_version="1.2.5", contracts=None)
self.assertEqual("blocked", unit.status)
self.assertIn("version metadata is not aligned", unit.blockers[0])
def test_repository_versions_and_lockfiles_must_match(self) -> None:
with tempfile.TemporaryDirectory() as tmp:
root = Path(tmp) / "govoplan-example"
(root / "src" / "govoplan_example" / "backend").mkdir(parents=True)
(root / "webui").mkdir()
(root / "pyproject.toml").write_text('[project]\nname = "govoplan-example"\nversion = "1.2.3"\n')
(root / "package.json").write_text('{"name":"@govoplan/example","version":"1.2.3"}\n')
(root / "webui" / "package.json").write_text('{"name":"@govoplan/example-webui","version":"1.2.4"}\n')
(root / "webui" / "package-lock.json").write_text(
json.dumps({"packages": {"": {"version": "1.2.2"}}})
)
(root / "src" / "govoplan_example" / "backend" / "manifest.py").write_text(
'manifest = ModuleManifest(\n version="1.2.3",\n)\n'
)
issues = repository_version_issues(root)
self.assertEqual(2, len(issues))
self.assertEqual({"webui/package.json", "webui/package-lock.json"}, {issue.source for issue in issues})
def test_public_runtime_version_must_match_package_metadata(self) -> None:
with tempfile.TemporaryDirectory() as tmp:
root = Path(tmp) / "govoplan-example"
package = root / "src" / "govoplan_example"
package.mkdir(parents=True)
(root / "pyproject.toml").write_text(
'[project]\nname = "govoplan-example"\nversion = "1.2.3"\n'
)
(package / "__init__.py").write_text('__version__ = "1.2.2"\n')
issues = repository_version_issues(root)
self.assertEqual(1, len(issues))
self.assertEqual("package __init__.py 1", issues[0].source)
self.assertEqual("1.2.3", issues[0].expected)
self.assertEqual("1.2.2", issues[0].actual)
def test_release_backend_and_frontend_refs_must_match(self) -> None:
with tempfile.TemporaryDirectory() as tmp:
meta = Path(tmp) / "govoplan"
core = Path(tmp) / "govoplan-core"
meta.mkdir()
(core / "webui").mkdir(parents=True)
(meta / "requirements-release.txt").write_text(
"govoplan-example @ git+ssh://git@example.test/acme/govoplan-example.git@v1.2.3\n"
)
(core / "webui" / "package.release.json").write_text(
json.dumps(
{
"dependencies": {
"@govoplan/example-webui": (
"git+ssh://git@example.test/acme/govoplan-example.git#v1.2.4"
)
}
}
)
)
issues = release_composition_issues(meta, core_root=core)
self.assertEqual(1, len(issues))
self.assertEqual("1.2.3", issues[0].expected)
self.assertEqual("1.2.4", issues[0].actual)
if __name__ == "__main__":
unittest.main()