skills-vault/skills/review-bundle-audit/tests/test_review_bundle_audit.py

197 lines
7.8 KiB
Python

from __future__ import annotations
import json
import pathlib
import shutil
import subprocess
import sys
import tempfile
import unittest
import zipfile
SKILL_ROOT = pathlib.Path(__file__).resolve().parents[1]
REPO_ROOT = pathlib.Path(__file__).resolve().parents[3]
SCRIPT = SKILL_ROOT / "scripts" / "review_bundle_audit.py"
TMP_ROOT = REPO_ROOT / "tmp" / "review-bundle-audit-tests"
def run_cli(*args: str) -> subprocess.CompletedProcess[str]:
return subprocess.run(
[sys.executable, str(SCRIPT), *args],
text=True,
capture_output=True,
check=False,
)
def write_text(path: pathlib.Path, text: str = "content") -> pathlib.Path:
path.parent.mkdir(parents=True, exist_ok=True)
path.write_text(text, encoding="utf-8")
return path
def write_json(path: pathlib.Path, data: object) -> pathlib.Path:
path.parent.mkdir(parents=True, exist_ok=True)
path.write_text(json.dumps(data, ensure_ascii=False, indent=2), encoding="utf-8")
return path
def write_zip(path: pathlib.Path) -> pathlib.Path:
path.parent.mkdir(parents=True, exist_ok=True)
with zipfile.ZipFile(path, "w") as archive:
archive.writestr("included.txt", "ok")
return path
def load_report(output_dir: pathlib.Path) -> dict[str, object]:
return json.loads((output_dir / "bundle-audit.json").read_text(encoding="utf-8"))
class ReviewBundleAuditTest(unittest.TestCase):
def setUp(self) -> None:
TMP_ROOT.mkdir(parents=True, exist_ok=True)
self.tmp_dir = tempfile.TemporaryDirectory(dir=TMP_ROOT)
self.bundle_root = pathlib.Path(self.tmp_dir.name) / "ccra_review_bundle" / "round-05"
self.output_dir = pathlib.Path(self.tmp_dir.name) / "outputs"
self.bundle_root.mkdir(parents=True)
def tearDown(self) -> None:
self.tmp_dir.cleanup()
@classmethod
def tearDownClass(cls) -> None:
if TMP_ROOT.exists():
shutil.rmtree(TMP_ROOT)
def write_complete_bundle(self) -> None:
write_text(self.bundle_root / "review-brief.md", "# Brief\n")
write_json(self.bundle_root / "review-manifest.json", {"files": ["review-brief.md"]})
write_json(self.bundle_root / "validation-sidecar.json", {"status": "pass"})
write_text(self.bundle_root / "local-review-report.md", "# Report\n")
write_text(self.bundle_root / "changed-files.md", "- src/model.json\n")
write_zip(self.bundle_root / "raw-review-bundle.zip")
def run_audit(self, *extra_args: str) -> tuple[subprocess.CompletedProcess[str], dict[str, object]]:
result = run_cli(
"--bundle-root",
str(self.bundle_root),
"--output-dir",
str(self.output_dir),
*extra_args,
)
return result, load_report(self.output_dir)
def test_complete_bundle_passes_and_reports_present_files(self) -> None:
self.write_complete_bundle()
result, report = self.run_audit()
self.assertEqual(result.returncode, 0, result.stderr)
self.assertEqual(report["machine_readable_summary"]["status"], "PASS")
self.assertEqual(report["required_files_missing"], [])
self.assertEqual(report["manifest_status"]["status"], "present")
self.assertEqual(report["zip_status"]["status"], "readable")
self.assertEqual(report["validation_sidecar_status"]["status"], "present")
self.assertEqual(report["report_status"]["status"], "present")
self.assertIn("review-brief.md", report["files_discovered"])
def test_missing_required_brief_is_blocking(self) -> None:
self.write_complete_bundle()
(self.bundle_root / "review-brief.md").unlink()
result, report = self.run_audit()
self.assertEqual(result.returncode, 1, result.stderr)
self.assertEqual(report["machine_readable_summary"]["status"], "FAIL")
self.assertIn("brief", report["required_files_missing"])
self.assertTrue(any("brief" in error for error in report["blocking_errors"]))
def test_missing_manifest_is_blocking(self) -> None:
self.write_complete_bundle()
(self.bundle_root / "review-manifest.json").unlink()
result, report = self.run_audit()
self.assertEqual(result.returncode, 1, result.stderr)
self.assertEqual(report["manifest_status"]["status"], "missing")
self.assertIn("manifest", report["required_files_missing"])
def test_missing_validation_sidecar_is_blocking(self) -> None:
self.write_complete_bundle()
(self.bundle_root / "validation-sidecar.json").unlink()
result, report = self.run_audit()
self.assertEqual(result.returncode, 1, result.stderr)
self.assertEqual(report["validation_sidecar_status"]["status"], "missing")
self.assertIn("validation_sidecar", report["required_files_missing"])
def test_unreadable_zip_is_blocking_when_zip_is_present(self) -> None:
self.write_complete_bundle()
write_text(self.bundle_root / "raw-review-bundle.zip", "not a real zip")
result, report = self.run_audit()
self.assertEqual(result.returncode, 1, result.stderr)
self.assertEqual(report["zip_status"]["status"], "unreadable")
self.assertTrue(any("zip" in error.casefold() for error in report["blocking_errors"]))
def test_extra_files_are_warnings_not_blockers(self) -> None:
self.write_complete_bundle()
write_text(self.bundle_root / "scratch.tmp", "operator note")
result, report = self.run_audit()
self.assertEqual(result.returncode, 0, result.stderr)
self.assertEqual(report["machine_readable_summary"]["status"], "PASS")
self.assertEqual(report["blocking_errors"], [])
self.assertTrue(any("scratch.tmp" in warning for warning in report["warnings"]))
def test_utf8_paths_and_chinese_filenames_are_reported(self) -> None:
self.bundle_root = pathlib.Path(self.tmp_dir.name) / "审查包"
self.bundle_root.mkdir()
write_text(self.bundle_root / "审查-brief.md", "# Brief\n")
write_json(self.bundle_root / "审查-manifest.json", {"files": ["审查-brief.md"]})
write_json(self.bundle_root / "验证-validation-sidecar.json", {"status": "pass"})
write_text(self.bundle_root / "审查-report.md", "# Report\n")
write_zip(self.bundle_root / "原始-review-bundle.zip")
result, report = self.run_audit()
self.assertEqual(result.returncode, 0, result.stderr)
self.assertIn("审查-brief.md", report["files_discovered"])
self.assertEqual(report["zip_status"]["files"][0], "原始-review-bundle.zip")
def test_profile_config_can_define_custom_required_patterns(self) -> None:
profile_config = pathlib.Path(self.tmp_dir.name) / "profile.json"
write_text(self.bundle_root / "handoff.txt", "handoff")
write_json(self.bundle_root / "index.json", {"files": ["handoff.txt"]})
write_json(self.bundle_root / "checks.json", {"status": "pass"})
write_json(
profile_config,
{
"profiles": {
"custom": {
"required_files": {
"brief": ["handoff.txt"],
"manifest": ["index.json"],
"validation_sidecar": ["checks.json"],
},
"optional_files": {"report": ["notes.md"], "zip": ["*.zip"]},
"allowed_extra_patterns": ["handoff.txt", "index.json", "checks.json"],
}
}
},
)
result, report = self.run_audit("--profile", "custom", "--profile-config", str(profile_config))
self.assertEqual(result.returncode, 0, result.stderr)
self.assertEqual(report["profile"], "custom")
self.assertEqual(report["required_files_missing"], [])
if __name__ == "__main__":
unittest.main()