policy-nexus/tests/test_publication.py
tegwick c1b60f322e
All checks were successful
Build and publish policy-nexus image / build-and-push (push) Successful in 1m10s
feat: publish Risk Nexus findings and methods
Assistant: codex
Assistant-Model: gpt-5.6-sol
Assistant-Session: 01a058f3-8ba0-7692-a042-9a870fc3d663
2026-09-01 01:56:46 +02:00

279 lines
11 KiB
Python

from __future__ import annotations
import importlib.util
import json
import datetime as dt
import os
from pathlib import Path
import sys
import tempfile
import unittest
from unittest import mock
ROOT = Path(__file__).parents[1]
sys.path.insert(0, str(ROOT / "tools"))
SPEC = importlib.util.spec_from_file_location("build_site", ROOT / "tools/build_site.py")
build_site = importlib.util.module_from_spec(SPEC)
assert SPEC.loader is not None
SPEC.loader.exec_module(build_site)
def _fixture(tmp_path: Path) -> Path:
repo = tmp_path / "canon-repo"
source = repo / "canon/standards/example.md"
source.parent.mkdir(parents=True)
source.write_text(
"""---
id: example
title: "Example Standard"
status: proposed
revision: "draft-1"
owner: example-owner
last_reviewed: "2026-08-18"
review_interval: 6m
---
# Example
## 1. Rule
| Level | Meaning |
| --- | --- |
| **V0** | No position. |
| **V1** | Restart recovery. |
""",
encoding="utf-8",
)
manifest = tmp_path / "publication.json"
manifest.write_text(
json.dumps(
{
"schema_version": 1,
"site": {"title": "Test", "base_url": "https://example.invalid"},
"repositories": {"canon": {"path": "canon-repo"}},
"documents": [
{
"id": "example",
"source_repo": "canon",
"source_path": "canon/standards/example.md",
"canonical_path": "standards/example/v1/index.html",
"revision_path": "standards/example/v1/revisions/{revision}/index.html",
"legacy_paths": ["example.html"],
"review_interval": "6m",
}
],
}
),
encoding="utf-8",
)
return manifest
class PublicationTest(unittest.TestCase):
def setUp(self) -> None:
self.source_root_env = mock.patch.dict(
os.environ, {"POLICY_NEXUS_SOURCE_ROOT": ""}, clear=False
)
self.source_root_env.start()
def tearDown(self) -> None:
self.source_root_env.stop()
def test_archive_build_accepts_only_exact_source_revision_override(self) -> None:
with tempfile.TemporaryDirectory() as directory:
root = Path(directory)
repo = root / "net-kingdom"
source = repo / "canon/example.md"
source.parent.mkdir(parents=True)
source.write_text("example", encoding="utf-8")
key = "POLICY_NEXUS_SOURCE_REVISION_NET_KINGDOM"
with mock.patch.dict(os.environ, {key: "a" * 40}):
self.assertEqual("a" * 40, build_site._source_revision(repo, source))
with mock.patch.dict(os.environ, {key: "main"}):
with self.assertRaisesRegex(ValueError, "clean 40-hex Git commit"):
build_site._source_revision(repo, source)
def test_archive_build_uses_fetched_source_lock_revision(self) -> None:
with tempfile.TemporaryDirectory() as directory:
root = Path(directory)
source_root = root / "_sources"
source_root.mkdir()
repo = root / "policy-nexus"
source = repo / "canon/example.md"
source.parent.mkdir(parents=True)
source.write_text("example", encoding="utf-8")
revision = "b" * 40
(source_root / "source-lock.json").write_text(
json.dumps(
{
"schema_version": 1,
"repositories": {
"policy-nexus": {"revision": revision}
},
}
),
encoding="utf-8",
)
with mock.patch.dict(
os.environ,
{"POLICY_NEXUS_SOURCE_ROOT": str(source_root)},
clear=False,
):
self.assertEqual(revision, build_site._source_revision(repo, source))
def test_archive_build_rejects_invalid_fetched_source_lock_revision(self) -> None:
with tempfile.TemporaryDirectory() as directory:
root = Path(directory)
repo = root / "the-custodian"
source = repo / "canon/example.md"
source.parent.mkdir(parents=True)
source.write_text("example", encoding="utf-8")
(root / "source-lock.json").write_text(
json.dumps(
{
"schema_version": 1,
"repositories": {
"the-custodian": {"revision": "sha256:" + "b" * 64}
},
}
),
encoding="utf-8",
)
with mock.patch.dict(
os.environ, {"POLICY_NEXUS_SOURCE_ROOT": str(root)}, clear=False
):
with self.assertRaisesRegex(ValueError, "clean 40-hex Git commit"):
build_site._source_revision(repo, source)
def test_manifest_builds_index_current_revision_and_legacy_alias(self) -> None:
with tempfile.TemporaryDirectory() as directory:
root = Path(directory)
manifest = _fixture(root)
output = root / "site"
records = build_site.build(manifest, output, as_of=dt.date(2026, 8, 18))
self.assertEqual("2027-02-18", records[0]["review_due"])
self.assertTrue((output / "index.html").is_file())
current = (output / "standards/example/v1/index.html").read_text(
encoding="utf-8"
)
revision = output / "standards/example/v1/revisions/draft-1/index.html"
self.assertIn("Availability", current)
self.assertIn("policy-source-revision", current)
self.assertIn("policy-source-digest", current)
self.assertIn("Review due: 2027-02-18", current)
self.assertEqual(revision.read_text(encoding="utf-8"), current)
self.assertIn(
"/standards/example/v1/index.html",
(output / "example.html").read_text(encoding="utf-8"),
)
def test_revision_address_refuses_changed_source_under_same_revision(self) -> None:
with tempfile.TemporaryDirectory() as directory:
root = Path(directory)
manifest = _fixture(root)
output = root / "site"
build_site.build(manifest, output, as_of=dt.date(2026, 8, 18))
source = root / "canon-repo/canon/standards/example.md"
source.write_text(
source.read_text(encoding="utf-8") + "\nChanged.\n", encoding="utf-8"
)
with self.assertRaisesRegex(RuntimeError, "immutable revision"):
build_site.build(manifest, output, as_of=dt.date(2026, 8, 18))
def test_same_content_can_move_from_worktree_to_commit_provenance(self) -> None:
with tempfile.TemporaryDirectory() as directory:
root = Path(directory)
manifest = _fixture(root)
output = root / "site"
build_site.build(manifest, output, as_of=dt.date(2026, 8, 18))
revision = output / "standards/example/v1/revisions/draft-1/index.html"
original = revision.read_text(encoding="utf-8")
original_source_revision = build_site._source_revision
try:
build_site._source_revision = lambda _repo, _source: "new-commit"
build_site.build(manifest, output, as_of=dt.date(2026, 8, 18))
finally:
build_site._source_revision = original_source_revision
self.assertEqual(original, revision.read_text(encoding="utf-8"))
current = (output / "standards/example/v1/index.html").read_text(
encoding="utf-8"
)
self.assertIn('policy-source-revision" content="new-commit', current)
def test_stale_and_superseded_notices_do_not_mutate_revision_page(self) -> None:
with tempfile.TemporaryDirectory() as directory:
root = Path(directory)
manifest = _fixture(root)
document = json.loads(manifest.read_text(encoding="utf-8"))
document["documents"][0]["lifecycle"] = "superseded"
document["documents"][0]["successor"] = "/standards/example/v2/"
manifest.write_text(json.dumps(document), encoding="utf-8")
output = root / "site"
build_site.build(manifest, output, as_of=dt.date(2027, 2, 19))
current = (output / "standards/example/v1/index.html").read_text(
encoding="utf-8"
)
revision = (
output / "standards/example/v1/revisions/draft-1/index.html"
).read_text(encoding="utf-8")
self.assertIn("Superseded.", current)
self.assertIn("Review overdue.", current)
self.assertNotIn("Superseded.", revision)
self.assertNotIn("Review overdue.", revision)
def test_manifest_rejects_unsafe_publication_path(self) -> None:
with tempfile.TemporaryDirectory() as directory:
root = Path(directory)
manifest = _fixture(root)
document = json.loads(manifest.read_text(encoding="utf-8"))
document["documents"][0]["canonical_path"] = "../escape.html"
manifest.write_text(json.dumps(document), encoding="utf-8")
with self.assertRaisesRegex(ValueError, "unsafe publication path"):
build_site.load_manifest(manifest)
def test_build_rejects_missing_publication_owner(self) -> None:
with tempfile.TemporaryDirectory() as directory:
root = Path(directory)
manifest = _fixture(root)
source = root / "canon-repo/canon/standards/example.md"
source.write_text(
source.read_text(encoding="utf-8").replace(
"owner: example-owner\n", ""
),
encoding="utf-8",
)
with self.assertRaisesRegex(ValueError, "owner is required"):
build_site.build(manifest, root / "site")
def test_build_rejects_non_public_risk_record(self) -> None:
with tempfile.TemporaryDirectory() as directory:
root = Path(directory)
manifest = _fixture(root)
value = json.loads(manifest.read_text(encoding="utf-8"))
value["repositories"] = {"risk-nexus": {"path": "canon-repo"}}
value["documents"][0]["source_repo"] = "risk-nexus"
manifest.write_text(json.dumps(value), encoding="utf-8")
source = root / "canon-repo/canon/standards/example.md"
source.write_text(
source.read_text(encoding="utf-8").replace(
"status: proposed\n", "status: proposed\ndisclosure: embargoed\n"
),
encoding="utf-8",
)
with self.assertRaisesRegex(ValueError, "require disclosure: public"):
build_site.build(manifest, root / "site")
if __name__ == "__main__":
unittest.main()