Files
awoooi/scripts/ci/tests/test_write_gitea_audit_receipts.py

291 lines
11 KiB
Python

#!/usr/bin/env python3
"""Functional tests for linear Gitea audit-branch receipt writeback."""
from __future__ import annotations
import json
import os
import subprocess
import tempfile
import unittest
from pathlib import Path
ROOT = Path(__file__).resolve().parents[3]
SCRIPT = ROOT / "scripts/ci/write-gitea-audit-receipts.sh"
CONTROLLER_TARGET = (
"docs/operations/external-mcp-replays/fixture-controller.snapshot.json"
)
VERIFIER_TARGET = (
"docs/operations/external-mcp-replays/fixture-verifier.snapshot.json"
)
def _run(command: list[str], *, cwd: Path, env: dict[str, str] | None = None) -> str:
completed = subprocess.run(
command,
cwd=cwd,
env=env,
check=False,
capture_output=True,
text=True,
timeout=30,
)
if completed.returncode != 0:
raise AssertionError(
f"command failed: {command}\nstdout={completed.stdout}\nstderr={completed.stderr}"
)
return completed.stdout.strip()
def _receipt(schema: str, run_id: str) -> dict:
return {
"schema_version": schema,
"run_id": run_id,
"trace_id": f"mcp-replay-{run_id}",
"work_item_id": "MCP-REPLAY-PLAYWRIGHT-001",
"status": "fixture_verified",
}
def _write_receipts(directory: Path, run_id: str) -> tuple[Path, Path]:
controller = directory / "controller.json"
verifier = directory / "verifier.json"
controller.write_text(
json.dumps(
_receipt("awoooi_external_mcp_replay_receipt_v1", run_id),
sort_keys=True,
)
+ "\n",
encoding="utf-8",
)
verifier.write_text(
json.dumps(
_receipt("awoooi_external_mcp_replay_verifier_receipt_v1", run_id),
sort_keys=True,
)
+ "\n",
encoding="utf-8",
)
return controller, verifier
class AuditReceiptWritebackTests(unittest.TestCase):
def setUp(self) -> None:
self.temp = tempfile.TemporaryDirectory()
self.root = Path(self.temp.name)
self.remote = self.root / "remote.git"
self.seed = self.root / "seed"
_run(["git", "init", "--bare", str(self.remote)], cwd=self.root)
self.seed.mkdir()
_run(["git", "init"], cwd=self.seed)
_run(["git", "config", "user.email", "fixture@awoooi.internal"], cwd=self.seed)
_run(["git", "config", "user.name", "Fixture"], cwd=self.seed)
(self.seed / "base.txt").write_text("base\n", encoding="utf-8")
_run(["git", "add", "base.txt"], cwd=self.seed)
_run(["git", "commit", "-m", "base"], cwd=self.seed)
_run(["git", "branch", "-M", "main"], cwd=self.seed)
_run(["git", "remote", "add", "origin", f"file://{self.remote}"], cwd=self.seed)
_run(["git", "push", "origin", "main"], cwd=self.seed)
_run(["git", "checkout", "-b", "mcp-replay-receipts"], cwd=self.seed)
initial_controller, initial_verifier = _write_receipts(
self.seed, "00000000-0000-4000-8000-000000000000"
)
(self.seed / CONTROLLER_TARGET).parent.mkdir(parents=True)
(self.seed / CONTROLLER_TARGET).write_bytes(initial_controller.read_bytes())
(self.seed / VERIFIER_TARGET).write_bytes(initial_verifier.read_bytes())
_run(["git", "add", "docs"], cwd=self.seed)
_run(["git", "commit", "-m", "initial audit receipt"], cwd=self.seed)
_run(["git", "push", "origin", "mcp-replay-receipts"], cwd=self.seed)
self.initial_audit_sha = _run(["git", "rev-parse", "HEAD"], cwd=self.seed)
_run(["git", "checkout", "main"], cwd=self.seed)
(self.seed / "main-only.txt").write_text("new main\n", encoding="utf-8")
_run(["git", "add", "main-only.txt"], cwd=self.seed)
_run(["git", "commit", "-m", "advance main"], cwd=self.seed)
_run(["git", "push", "origin", "main"], cwd=self.seed)
def tearDown(self) -> None:
self.temp.cleanup()
def _fresh_main_clone(self, name: str) -> Path:
clone = self.root / name
_run(
[
"git",
"clone",
"--depth=1",
"--branch",
"main",
f"file://{self.remote}",
str(clone),
],
cwd=self.root,
)
_run(["git", "config", "user.email", "fixture@awoooi.internal"], cwd=clone)
_run(["git", "config", "user.name", "Fixture"], cwd=clone)
return clone
def _apply(self, clone: Path, run_id: str) -> str:
source_dir = self.root / f"source-{run_id}"
source_dir.mkdir()
controller, verifier = _write_receipts(source_dir, run_id)
env = {
**os.environ,
"GIT_TERMINAL_PROMPT": "0",
"GITEA_AUDIT_TEST_FILE_REMOTE": "1",
"GITEA_AUDIT_REMOTE_URL": f"file://{self.remote}",
"GITEA_AUDIT_BRANCH": "mcp-replay-receipts",
"GITEA_AUDIT_CONTROLLER_SOURCE": str(controller),
"GITEA_AUDIT_VERIFIER_SOURCE": str(verifier),
"GITEA_AUDIT_CONTROLLER_TARGET": CONTROLLER_TARGET,
"GITEA_AUDIT_VERIFIER_TARGET": VERIFIER_TARGET,
"GITEA_AUDIT_COMMIT_MESSAGE": "fixture audit receipt",
}
return _run(["bash", str(SCRIPT)], cwd=clone, env=env)
def test_second_and_third_writebacks_are_linear_normal_pushes(self) -> None:
first_clone = self._fresh_main_clone("clone-one")
first_output = self._apply(
first_clone, "11111111-1111-4111-8111-111111111111"
)
self.assertIn("receipt_writeback=verified_audit_branch_normal_push", first_output)
first_sha = _run(
["git", "--git-dir", str(self.remote), "rev-parse", "mcp-replay-receipts"],
cwd=self.root,
)
first_parent = _run(
["git", "--git-dir", str(self.remote), "rev-parse", f"{first_sha}^"],
cwd=self.root,
)
self.assertEqual(first_parent, self.initial_audit_sha)
second_clone = self._fresh_main_clone("clone-two")
second_output = self._apply(
second_clone, "22222222-2222-4222-8222-222222222222"
)
self.assertIn("receipt_writeback=verified_audit_branch_normal_push", second_output)
second_sha = _run(
["git", "--git-dir", str(self.remote), "rev-parse", "mcp-replay-receipts"],
cwd=self.root,
)
second_parent = _run(
["git", "--git-dir", str(self.remote), "rev-parse", f"{second_sha}^"],
cwd=self.root,
)
self.assertEqual(second_parent, first_sha)
latest = _run(
[
"git",
"--git-dir",
str(self.remote),
"show",
f"mcp-replay-receipts:{CONTROLLER_TARGET}",
],
cwd=self.root,
)
self.assertEqual(
json.loads(latest)["run_id"],
"22222222-2222-4222-8222-222222222222",
)
def test_non_allowlisted_branch_is_rejected_without_push(self) -> None:
clone = self._fresh_main_clone("clone-rejected")
source_dir = self.root / "source-rejected"
source_dir.mkdir()
controller, verifier = _write_receipts(
source_dir, "33333333-3333-4333-8333-333333333333"
)
env = {
**os.environ,
"GITEA_AUDIT_TEST_FILE_REMOTE": "1",
"GITEA_AUDIT_REMOTE_URL": f"file://{self.remote}",
"GITEA_AUDIT_BRANCH": "main",
"GITEA_AUDIT_CONTROLLER_SOURCE": str(controller),
"GITEA_AUDIT_VERIFIER_SOURCE": str(verifier),
"GITEA_AUDIT_CONTROLLER_TARGET": CONTROLLER_TARGET,
"GITEA_AUDIT_VERIFIER_TARGET": VERIFIER_TARGET,
"GITEA_AUDIT_COMMIT_MESSAGE": "must not commit",
}
completed = subprocess.run(
["bash", str(SCRIPT)],
cwd=clone,
env=env,
check=False,
capture_output=True,
text=True,
timeout=30,
)
self.assertEqual(completed.returncode, 2)
self.assertIn("audit_writeback_branch_not_allowlisted", completed.stdout)
def test_branch_and_receipt_directory_must_match(self) -> None:
clone = self._fresh_main_clone("clone-mismatched-target")
source_dir = self.root / "source-mismatched-target"
source_dir.mkdir()
controller, verifier = _write_receipts(
source_dir, "44444444-4444-4444-8444-444444444444"
)
env = {
**os.environ,
"GITEA_AUDIT_TEST_FILE_REMOTE": "1",
"GITEA_AUDIT_REMOTE_URL": f"file://{self.remote}",
"GITEA_AUDIT_BRANCH": "mcp-artifact-receipts",
"GITEA_AUDIT_CONTROLLER_SOURCE": str(controller),
"GITEA_AUDIT_VERIFIER_SOURCE": str(verifier),
"GITEA_AUDIT_CONTROLLER_TARGET": CONTROLLER_TARGET,
"GITEA_AUDIT_VERIFIER_TARGET": VERIFIER_TARGET,
"GITEA_AUDIT_COMMIT_MESSAGE": "must not commit",
}
completed = subprocess.run(
["bash", str(SCRIPT)],
cwd=clone,
env=env,
check=False,
capture_output=True,
text=True,
timeout=30,
)
self.assertEqual(completed.returncode, 2)
self.assertIn("audit_writeback_branch_target_mismatch", completed.stdout)
def test_controller_and_verifier_identity_must_match(self) -> None:
clone = self._fresh_main_clone("clone-mismatched-identity")
source_dir = self.root / "source-mismatched-identity"
source_dir.mkdir()
controller, verifier = _write_receipts(
source_dir, "55555555-5555-4555-8555-555555555555"
)
verifier_payload = json.loads(verifier.read_text(encoding="utf-8"))
verifier_payload["run_id"] = "66666666-6666-4666-8666-666666666666"
verifier.write_text(
json.dumps(verifier_payload, sort_keys=True) + "\n", encoding="utf-8"
)
env = {
**os.environ,
"GITEA_AUDIT_TEST_FILE_REMOTE": "1",
"GITEA_AUDIT_REMOTE_URL": f"file://{self.remote}",
"GITEA_AUDIT_BRANCH": "mcp-replay-receipts",
"GITEA_AUDIT_CONTROLLER_SOURCE": str(controller),
"GITEA_AUDIT_VERIFIER_SOURCE": str(verifier),
"GITEA_AUDIT_CONTROLLER_TARGET": CONTROLLER_TARGET,
"GITEA_AUDIT_VERIFIER_TARGET": VERIFIER_TARGET,
"GITEA_AUDIT_COMMIT_MESSAGE": "must not commit",
}
completed = subprocess.run(
["bash", str(SCRIPT)],
cwd=clone,
env=env,
check=False,
capture_output=True,
text=True,
timeout=30,
)
self.assertEqual(completed.returncode, 2)
self.assertIn("audit_writeback_receipt_identity_mismatch", completed.stdout)
if __name__ == "__main__":
unittest.main()