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()