Verified with the coordinated workspace changes by devkit full run 2026-09-08T225814-186389-0000-3e3ed7cd (all seven phases passed). This shared UI pass does not mark the individual module reviews complete.
439 lines
16 KiB
Python
Executable File
439 lines
16 KiB
Python
Executable File
"""Audit the selected fixture workspace, never a fuller neighboring checkout."""
|
|
|
|
from argparse import Namespace
|
|
import importlib.util
|
|
import json
|
|
import os
|
|
from pathlib import Path
|
|
import shutil
|
|
import subprocess
|
|
import sys
|
|
from types import ModuleType, SimpleNamespace
|
|
|
|
import pytest
|
|
|
|
|
|
META_ROOT = Path(__file__).resolve().parents[1]
|
|
SCRIPT = META_ROOT / "tools/inventory/platform-interface-inventory.py"
|
|
WEBUI_SCRIPT = META_ROOT / "tools/inventory/extract-webui-structure.mjs"
|
|
SPEC = importlib.util.spec_from_file_location("audit_scope_inventory", SCRIPT)
|
|
assert SPEC is not None and SPEC.loader is not None
|
|
inventory = importlib.util.module_from_spec(SPEC)
|
|
SPEC.loader.exec_module(inventory)
|
|
|
|
sys.path.insert(0, str(META_ROOT / "tools/devkit"))
|
|
from govoplan_devkit import docs, issues, runner # noqa: E402
|
|
from govoplan_devkit.workspace import Project, Repository # noqa: E402
|
|
|
|
|
|
def write(path, content):
|
|
path.parent.mkdir(parents=True, exist_ok=True)
|
|
path.write_text(content, encoding="utf-8")
|
|
|
|
|
|
@pytest.fixture
|
|
def workspaces(tmp_path):
|
|
selected = tmp_path / "selected"
|
|
default = tmp_path / "fuller-default"
|
|
meta = tmp_path / "legacy-siblings/govoplan"
|
|
selected.mkdir()
|
|
meta.mkdir(parents=True)
|
|
catalog = {
|
|
"default_parent": str(default),
|
|
"repositories": [
|
|
{"name": "govoplan-core", "path": "govoplan-core"},
|
|
{"name": "govoplan-example", "path": "nested/example"},
|
|
{"name": "govoplan-optional", "path": "govoplan-optional"},
|
|
],
|
|
}
|
|
write(meta / "repositories.json", json.dumps(catalog))
|
|
write(
|
|
selected / "nested/example/webui/src/Page.tsx",
|
|
"export const page = <h1>SELECTED_ONLY</h1>;\n",
|
|
)
|
|
write(
|
|
default / "nested/example/webui/src/Page.tsx",
|
|
"export const page = <h1>DEFAULT_ONLY</h1>;\n",
|
|
)
|
|
write(
|
|
default / "govoplan-optional/webui/src/Page.tsx",
|
|
"export const page = <h1>DEFAULT_OPTIONAL</h1>;\n",
|
|
)
|
|
for item in catalog["repositories"]:
|
|
(default / item["path"] / "src").mkdir(parents=True)
|
|
return selected, default, meta, catalog
|
|
|
|
|
|
def test_explicit_partial_root_beats_fuller_legacy_discovery(workspaces, monkeypatch):
|
|
selected, default, meta, catalog = workspaces
|
|
monkeypatch.setattr(inventory, "META_ROOT", meta)
|
|
assert inventory._resolve_workspace_root(catalog) == default
|
|
assert inventory._resolve_workspace_root(catalog, selected) == selected
|
|
with pytest.raises(ValueError, match="existing directory"):
|
|
inventory._resolve_workspace_root(catalog, selected / "missing")
|
|
|
|
|
|
@pytest.mark.parametrize("relative", ["../fuller-default", "/absolute/path"])
|
|
def test_repository_paths_cannot_escape_explicit_root(workspaces, relative):
|
|
selected, _, _, _ = workspaces
|
|
with pytest.raises(ValueError, match="inside the selected workspace"):
|
|
inventory._validate_repository_roots(
|
|
{"repositories": [{"path": relative}]}, selected
|
|
)
|
|
|
|
|
|
def test_linked_repository_cannot_borrow_default_sources(workspaces):
|
|
selected, default, _, catalog = workspaces
|
|
(selected / "govoplan-core").symlink_to(
|
|
default / "govoplan-core", target_is_directory=True
|
|
)
|
|
with pytest.raises(ValueError, match="escapes the selected workspace"):
|
|
inventory._validate_repository_roots(catalog, selected)
|
|
|
|
|
|
def test_python_forwards_selected_root_and_configured_node(workspaces, monkeypatch):
|
|
selected, _, meta, _ = workspaces
|
|
monkeypatch.setattr(inventory, "META_ROOT", meta)
|
|
monkeypatch.setenv("NODE", "/selected/toolchain/node")
|
|
calls = []
|
|
|
|
def run(argv, **kwargs):
|
|
calls.append((argv, kwargs))
|
|
return SimpleNamespace(stdout=json.dumps({"workspaceRoot": str(selected)}))
|
|
|
|
monkeypatch.setattr(inventory.subprocess, "run", run)
|
|
assert inventory._extract_webui(selected)["workspaceRoot"] == str(selected)
|
|
argv, options = calls[0]
|
|
assert argv == [
|
|
"/selected/toolchain/node",
|
|
str(meta / "tools/inventory/extract-webui-structure.mjs"),
|
|
str(meta),
|
|
"--workspace-root",
|
|
str(selected),
|
|
]
|
|
assert options == {"check": True, "capture_output": True, "text": True}
|
|
|
|
|
|
def test_python_rejects_webui_result_from_another_root(workspaces, monkeypatch):
|
|
selected, default, _, _ = workspaces
|
|
monkeypatch.setattr(
|
|
inventory.subprocess,
|
|
"run",
|
|
lambda *a, **k: SimpleNamespace(
|
|
stdout=json.dumps({"workspaceRoot": str(default)})
|
|
),
|
|
)
|
|
with pytest.raises(ValueError, match="did not confirm"):
|
|
inventory._extract_webui(selected)
|
|
|
|
|
|
def test_main_forwards_same_root_to_every_collector(workspaces, monkeypatch):
|
|
selected, _, meta, catalog = workspaces
|
|
monkeypatch.setattr(inventory, "META_ROOT", meta)
|
|
roots = []
|
|
monkeypatch.setattr(
|
|
inventory, "_extract_webui", lambda root: roots.append(root) or {}
|
|
)
|
|
monkeypatch.setattr(
|
|
inventory,
|
|
"_extract_backend_endpoints",
|
|
lambda data, root: roots.append(root) or [],
|
|
)
|
|
monkeypatch.setattr(
|
|
inventory, "_extract_manifests", lambda data, root: roots.append(root) or []
|
|
)
|
|
monkeypatch.setattr(inventory, "_load_endpoint_declarations", lambda _: {})
|
|
monkeypatch.setattr(inventory, "_load_high_risk_help_baseline", lambda _: {})
|
|
monkeypatch.setattr(inventory, "_assemble_inventory", lambda **kwargs: {})
|
|
monkeypatch.setattr(inventory, "_render_markdown", lambda data: "fixture\n")
|
|
output = selected / "output"
|
|
monkeypatch.setattr(
|
|
sys,
|
|
"argv",
|
|
[str(SCRIPT), "--workspace-root", str(selected), "--output-dir", str(output)],
|
|
)
|
|
assert inventory.main() == 0
|
|
assert roots == [selected, selected, selected]
|
|
report = json.loads((output / "platform-interface-inventory.json").read_text())
|
|
assert report["workspace_root"] == str(selected)
|
|
assert report["workspace_selection"] == "explicit"
|
|
|
|
|
|
def install_fixture_compiler(root):
|
|
compiler = META_ROOT.parent / "govoplan-core/webui/node_modules/typescript"
|
|
if not compiler.is_dir() or not shutil.which("node"):
|
|
pytest.skip("Node and the installed Core TypeScript parser are required")
|
|
target = root / "govoplan-core/webui/node_modules/typescript"
|
|
target.parent.mkdir(parents=True, exist_ok=True)
|
|
# Parser dependencies may be shared; audited source checkouts may not.
|
|
target.symlink_to(compiler, target_is_directory=True)
|
|
|
|
|
|
def collect_webui(meta, selected=None):
|
|
argv = [shutil.which("node") or "node", str(WEBUI_SCRIPT), str(meta)]
|
|
if selected is not None:
|
|
argv += ["--workspace-root", str(selected)]
|
|
return subprocess.run(argv, capture_output=True, text=True, timeout=30)
|
|
|
|
|
|
def test_javascript_explicit_root_is_authoritative_and_legacy_cli_still_works(
|
|
workspaces,
|
|
):
|
|
selected, default, meta, _ = workspaces
|
|
install_fixture_compiler(selected)
|
|
install_fixture_compiler(default)
|
|
explicit = collect_webui(meta, selected)
|
|
assert explicit.returncode == 0, explicit.stderr
|
|
report = json.loads(explicit.stdout)
|
|
assert report["workspaceRoot"] == str(selected)
|
|
assert [item["value"] for item in report["visibleText"]] == ["SELECTED_ONLY"]
|
|
assert report["visibleText"][0]["file"] == "webui/src/Page.tsx"
|
|
legacy = collect_webui(meta)
|
|
assert legacy.returncode == 0, legacy.stderr
|
|
assert json.loads(legacy.stdout)["workspaceRoot"] == str(default)
|
|
assert {item["value"] for item in json.loads(legacy.stdout)["visibleText"]} == {
|
|
"DEFAULT_ONLY",
|
|
"DEFAULT_OPTIONAL",
|
|
}
|
|
|
|
|
|
def test_javascript_does_not_borrow_missing_core_dependencies(workspaces):
|
|
selected, default, meta, _ = workspaces
|
|
install_fixture_compiler(default)
|
|
result = collect_webui(meta, selected)
|
|
assert result.returncode != 0
|
|
assert (
|
|
str(selected / "govoplan-core/webui/node_modules/typescript") in result.stderr
|
|
)
|
|
assert not result.stdout
|
|
|
|
|
|
def test_javascript_rejects_source_root_linked_outside_workspace(workspaces):
|
|
selected, default, meta, _ = workspaces
|
|
install_fixture_compiler(selected)
|
|
linked = selected / "govoplan-optional/webui/src"
|
|
linked.parent.mkdir(parents=True)
|
|
linked.symlink_to(default / "govoplan-optional/webui/src", target_is_directory=True)
|
|
result = collect_webui(meta, selected)
|
|
assert result.returncode != 0
|
|
assert "source root escapes the selected workspace" in result.stderr
|
|
|
|
|
|
def test_backend_does_not_borrow_optional_endpoints(workspaces):
|
|
selected, default, _, catalog = workspaces
|
|
source = "from fastapi import APIRouter\nrouter = APIRouter()\n@router.get('/selected')\ndef endpoint(): pass\n"
|
|
write(selected / "nested/example/src/routes.py", source)
|
|
write(
|
|
default / "govoplan-optional/src/routes.py", source.replace("selected", "other")
|
|
)
|
|
endpoints = inventory._extract_backend_endpoints(catalog, selected)
|
|
assert [item["path"] for item in endpoints] == ["/selected"]
|
|
(selected / "nested/example/src/foreign.py").symlink_to(
|
|
default / "govoplan-optional/src/routes.py"
|
|
)
|
|
with pytest.raises(ValueError, match="Backend source path escapes"):
|
|
inventory._extract_backend_endpoints(catalog, selected)
|
|
|
|
|
|
def test_manifests_require_selected_core_sources(workspaces, monkeypatch):
|
|
selected, default, _, catalog = workspaces
|
|
write(
|
|
default / "govoplan-core/src/govoplan_core/core/platform_interfaces.py",
|
|
"raise AssertionError('foreign source imported')",
|
|
)
|
|
monkeypatch.syspath_prepend(str(default / "govoplan-core/src"))
|
|
with pytest.raises(ValueError, match="requires Core interface sources"):
|
|
inventory._extract_manifests(catalog, selected)
|
|
|
|
|
|
def test_cached_application_import_cannot_replace_missing_checkout(
|
|
workspaces, monkeypatch
|
|
):
|
|
selected, default, _, _ = workspaces
|
|
# Meta's own devkit/release packages may audit another workspace.
|
|
tooling = ModuleType("govoplan_release")
|
|
tooling.__file__ = str(default / "tools/release/govoplan_release/__init__.py")
|
|
modules = {"govoplan_release": tooling, "govoplan_devkit.docs": docs}
|
|
# Isolate the fixture from application packages collected by unrelated
|
|
# suites, without removing or replacing those real cached imports.
|
|
monkeypatch.setattr(inventory, "sys", SimpleNamespace(modules=modules))
|
|
inventory._assert_workspace_imports(selected)
|
|
application = ModuleType("govoplan_audit_fixture")
|
|
application.__file__ = str(
|
|
default / "govoplan-optional/src/govoplan_audit_fixture/__init__.py"
|
|
)
|
|
modules["govoplan_audit_fixture"] = application
|
|
with pytest.raises(ValueError, match="outside the selected inventory workspace"):
|
|
inventory._assert_workspace_imports(selected)
|
|
|
|
|
|
def test_partial_manifest_collection_uses_selected_sources_in_fresh_process(workspaces):
|
|
selected, default, _, catalog = workspaces
|
|
core = selected / "govoplan-core/src/govoplan_core"
|
|
write(core / "__init__.py", "")
|
|
write(core / "core/__init__.py", "")
|
|
write(
|
|
core / "core/platform_interfaces.py",
|
|
"def manifest_interface_catalog(manifest): return {'selected': True}\n",
|
|
)
|
|
write(
|
|
default / "govoplan-core/src/govoplan_core/__init__.py",
|
|
"raise AssertionError('foreign Core loaded')\n",
|
|
)
|
|
module = selected / "nested/example/src/govoplan_example"
|
|
write(module / "__init__.py", "")
|
|
write(module / "backend/__init__.py", "")
|
|
manifest = (
|
|
"from types import SimpleNamespace as S\n"
|
|
"def get_manifest():\n"
|
|
" return S(id='selected', name='Selected', version='1', dependencies=(), "
|
|
"optional_dependencies=(), required_capabilities=(), provides_interfaces=(), "
|
|
"capability_factories={}, permissions=(), documentation=(), architecture=None, "
|
|
"information_governance=S(to_dict=lambda: {}), frontend=None)\n"
|
|
)
|
|
write(module / "backend/manifest.py", manifest)
|
|
write(
|
|
default / "govoplan-optional/src/govoplan_optional/backend/manifest.py",
|
|
manifest.replace("selected", "foreign"),
|
|
)
|
|
code = (
|
|
"import importlib.util, json; from pathlib import Path; "
|
|
f"spec=importlib.util.spec_from_file_location('fixture', {str(SCRIPT)!r}); "
|
|
"module=importlib.util.module_from_spec(spec); spec.loader.exec_module(module); "
|
|
f"print(json.dumps(module._extract_manifests({catalog!r}, Path({str(selected)!r}))))"
|
|
)
|
|
env = {
|
|
**os.environ,
|
|
"PYTHONPATH": os.pathsep.join(
|
|
str(default / item["path"] / "src") for item in catalog["repositories"]
|
|
),
|
|
}
|
|
result = subprocess.run(
|
|
[sys.executable, "-c", code],
|
|
env=env,
|
|
capture_output=True,
|
|
text=True,
|
|
timeout=30,
|
|
)
|
|
assert result.returncode == 0, result.stderr
|
|
manifests = json.loads(result.stdout)
|
|
assert [manifest["id"] for manifest in manifests] == ["selected"]
|
|
assert manifests[0]["interface_catalog"] == {"selected": True}
|
|
|
|
|
|
def doc_plan(workspace, monkeypatch):
|
|
repos = tuple(
|
|
Repository(name, workspace / name)
|
|
for name in ("govoplan", "govoplan-core", "govoplan-example")
|
|
)
|
|
project = Project("fixture", repos, {})
|
|
monkeypatch.setattr(docs, "load_project", lambda *a: project)
|
|
args = Namespace(
|
|
workspace_root=workspace,
|
|
project=None,
|
|
state_dir=workspace.parent / "state",
|
|
repo=["govoplan-example"],
|
|
changed=False,
|
|
)
|
|
return docs.build_doc_stages(args)
|
|
|
|
|
|
def test_docs_plan_forwards_root_and_persists_all_limitations(workspaces, monkeypatch):
|
|
selected, _, _, _ = workspaces
|
|
stages = doc_plan(selected, monkeypatch)
|
|
by_id = {stage["id"]: stage for stage in stages}
|
|
for stage_id in (
|
|
"docs.manifests",
|
|
"docs.interface-inventory",
|
|
"docs.plain-display-labels",
|
|
):
|
|
argv = by_id[stage_id]["argv"]
|
|
assert argv[argv.index("--workspace-root") + 1] == str(selected)
|
|
assert by_id["docs.translation-structure"]["argv"][1] == str(
|
|
selected / "govoplan-core/webui/scripts/audit-i18n-structural.mjs"
|
|
)
|
|
assert issues.coverage_notes(stages) == docs.LIMITATIONS
|
|
assert by_id["docs.plain-display-labels"]["argv"][-2:] == [
|
|
"--repo",
|
|
"govoplan-example",
|
|
]
|
|
|
|
|
|
def test_docs_limitations_survive_real_receipt_and_issue_evidence(
|
|
workspaces, monkeypatch, tmp_path
|
|
):
|
|
selected, _, _, _ = workspaces
|
|
stages = doc_plan(selected, monkeypatch)
|
|
repo = selected / "example"
|
|
repo.mkdir()
|
|
subprocess.run(["git", "init", "-q", str(repo)], check=True, timeout=10)
|
|
write(repo / "source.txt", "fixture\n")
|
|
subprocess.run(
|
|
["git", "-C", str(repo), "add", "source.txt"], check=True, timeout=10
|
|
)
|
|
subprocess.run(
|
|
[
|
|
"git",
|
|
"-C",
|
|
str(repo),
|
|
"-c",
|
|
"user.name=Fixture",
|
|
"-c",
|
|
"user.email=fixture@example.invalid",
|
|
"commit",
|
|
"-qm",
|
|
"fixture",
|
|
],
|
|
check=True,
|
|
timeout=10,
|
|
)
|
|
project = tmp_path / "project.json"
|
|
write(
|
|
project,
|
|
json.dumps(
|
|
{
|
|
"schema_version": 1,
|
|
"name": "fixture",
|
|
"repositories": [{"name": "example", "path": "example"}],
|
|
"checks": [],
|
|
"profiles": {},
|
|
}
|
|
),
|
|
)
|
|
args = Namespace(
|
|
workspace_root=selected,
|
|
project=project,
|
|
state_dir=tmp_path / "state",
|
|
dry_run=False,
|
|
jobs=2,
|
|
profile="docs",
|
|
resume=None,
|
|
)
|
|
for stage in stages:
|
|
stage.update(
|
|
argv=[sys.executable, "-c", "print('fixture audit')"],
|
|
cwd=str(repo),
|
|
deps=[],
|
|
resources=[],
|
|
timeout_seconds=10,
|
|
)
|
|
monkeypatch.setattr(docs, "build_doc_stages", lambda _: stages)
|
|
monkeypatch.setattr(
|
|
runner, "environment_fingerprint", lambda *a, **k: "fixture-env"
|
|
)
|
|
result = docs.audit(args)
|
|
assert result["status"] == "passed", result
|
|
assert all(result["summary"].count(note) == 1 for note in docs.LIMITATIONS)
|
|
receipt = runner.read_receipt(selected, args.state_dir, result["run_id"])
|
|
assert issues.coverage_notes(receipt["stages"]) == docs.LIMITATIONS
|
|
evidence = issues.evidence_record(result["run_id"], args)
|
|
assert evidence["coverage_notes"] == docs.LIMITATIONS
|
|
assert evidence["source_state"] == "matches-current"
|
|
target = issues.NoteTarget(
|
|
repo, "https://gitea.example.invalid", "fixture", "example", 1
|
|
)
|
|
_, body = issues.render_note(
|
|
{"summary": [], "next": [], "body": ""}, evidence, target, "fixture"
|
|
)
|
|
assert all(note in body for note in docs.LIMITATIONS)
|