2026-10-11 21:30:44 +02:00
|
|
|
import importlib.util
|
|
|
|
|
from pathlib import Path
|
|
|
|
|
import subprocess
|
|
|
|
|
|
|
|
|
|
SCRIPT = Path(__file__).resolve().parents[1] / "scripts/check_generated_only_commit.py"
|
|
|
|
|
spec = importlib.util.spec_from_file_location("guard", SCRIPT)
|
|
|
|
|
guard = importlib.util.module_from_spec(spec)
|
|
|
|
|
spec.loader.exec_module(guard)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
def test_source_change_is_required_with_generated_indexes():
|
|
|
|
|
assert guard.standalone(["WORK-RECORDS.md", "docs/publication-index.md"])
|
|
|
|
|
assert guard.standalone(["docs/source-inventory.json", "generated/index.json"])
|
|
|
|
|
assert not guard.standalone(["WORK-RECORDS.md", "scripts/issuer.py"])
|
|
|
|
|
assert not guard.standalone([])
|
|
|
|
|
assert not guard.standalone(["canon/standards/work-record-types_v0.1.md"])
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
def test_evidence_packets_and_generated_brief_need_source_change():
|
|
|
|
|
assert guard.standalone(["docs/evidence/run/receipt.json", ".custodian-brief.md"])
|
|
|
|
|
assert guard.standalone(["docs/decision-packets/daily-batch.md"])
|
|
|
|
|
assert guard.standalone(["docs/decision-packets-2026-10-03.md"])
|
|
|
|
|
assert guard.standalone(["docs/attended-window/2026-10-11-daily-red-batch.md"])
|
|
|
|
|
assert guard.standalone(["docs/evidence-packets/live-proof.md", "docs/source-index.md"])
|
|
|
|
|
assert not guard.standalone(["docs/evidence/run/receipt.json", "scripts/verify_live.py"])
|
|
|
|
|
assert not guard.standalone(["docs/implementation/delivery-protocol.md"])
|
|
|
|
|
assert not guard.standalone(["docs/decisions/authorization.md"])
|
|
|
|
|
|
|
|
|
|
|
2026-10-19 12:42:11 +02:00
|
|
|
def test_source_inventory_configuration_is_authored_input():
|
|
|
|
|
assert not guard.standalone(["source-inventory.config.json"])
|
|
|
|
|
assert not guard.standalone(["source-inventory.config.json", "source-inventory.json"])
|
|
|
|
|
assert guard.standalone(["source-inventory.json"])
|
|
|
|
|
assert guard.standalone(["source-inventory.config.json.generated"])
|
|
|
|
|
assert guard.standalone(["docs/evidence/source-inventory.config.json"])
|
|
|
|
|
assert guard.standalone(["generated/source-inventory.config.json"])
|
|
|
|
|
|
|
|
|
|
|
2026-10-11 21:30:44 +02:00
|
|
|
def test_ci_checks_each_commit_in_range(tmp_path):
|
|
|
|
|
def git(*args):
|
|
|
|
|
return subprocess.check_output(["git", *args], cwd=tmp_path).decode().strip()
|
|
|
|
|
git("init", "-q")
|
2026-10-11 23:31:09 +02:00
|
|
|
# This fixture deliberately creates a rejected historical commit so CI's
|
|
|
|
|
# range checker can inspect it. Isolate it from the workstation's real hook.
|
|
|
|
|
hooks = tmp_path / "fixture-hooks"
|
|
|
|
|
hooks.mkdir()
|
|
|
|
|
git("config", "core.hooksPath", str(hooks))
|
2026-10-11 21:30:44 +02:00
|
|
|
git("config", "user.name", "Test")
|
|
|
|
|
git("config", "user.email", "test@example.test")
|
|
|
|
|
(tmp_path / "source.py").write_text("source\n")
|
|
|
|
|
git("add", ".")
|
|
|
|
|
git("commit", "-qm", "source")
|
|
|
|
|
base = git("rev-parse", "HEAD")
|
|
|
|
|
(tmp_path / "WORK-RECORDS.md").write_text("generated\n")
|
|
|
|
|
git("add", ".")
|
|
|
|
|
git("commit", "-qm", "generated only")
|
|
|
|
|
(tmp_path / "source.py").write_text("changed\n")
|
|
|
|
|
git("add", ".")
|
|
|
|
|
git("commit", "-qm", "source again")
|
|
|
|
|
result = subprocess.run(["python3", str(SCRIPT), "--range", base + "..HEAD"],
|
|
|
|
|
cwd=tmp_path, capture_output=True, text=True)
|
|
|
|
|
assert result.returncode == 1
|
|
|
|
|
assert "Generated-only commits rejected" in result.stderr
|