import importlib.util import json from pathlib import Path import sys from types import SimpleNamespace from zipfile import ZipFile import pytest SCRIPT_PATH = Path(__file__).resolve().parents[1] / "scripts" / "submission_bundle.py" SPEC = importlib.util.spec_from_file_location("submission_bundle", SCRIPT_PATH) submission_bundle = importlib.util.module_from_spec(SPEC) assert SPEC.loader is not None SPEC.loader.exec_module(submission_bundle) def write_fixture_file(root: Path, relative_path: str, content: bytes | str = "ok") -> None: path = root / relative_path path.parent.mkdir(parents=True, exist_ok=True) if isinstance(content, bytes): path.write_bytes(content) else: path.write_text(content, encoding="utf-8") def write_bundle_fixtures(root: Path) -> None: for _, relative_path, _ in submission_bundle.ARTIFACTS: if relative_path.endswith(".png"): write_fixture_file(root, relative_path, b"\x89PNG\r\n\x1a\nfixture") elif relative_path == "tasks.json": write_fixture_file( root, relative_path, json.dumps( { "project": "ProofFrame", "updated_at": "2026-06-27T00:00:00Z", "tasks": [ { "id": task_id, "title": f"{task_id} gate", "phase": "P4 Submit", "owner": "tester", "status": "done", "done_criteria": "Gate passes.", "notes": "", } for task_id in ["T020", "T021", "T040", "T041", "T041A", "T042"] ], } ), ) elif relative_path.endswith("devpost-submission-packet.json"): write_fixture_file( root, relative_path, json.dumps( { "mode": "pre_live_safe", "project_name": "ProofFrame", "tagline": "A provenance-first vault for generated media.", "repository_url": "https://github.com/adjcjh777/backblaze-proofframe", "demo_url": "https://adjcjh-backblaze-proofframe.hf.space/?judge=1", "claim_warning": "safe", } ), ) elif relative_path.endswith("sponsor-fit-audit.json"): write_fixture_file(root, relative_path, json.dumps({"ok": True})) elif relative_path.endswith("award-readiness-report.json"): write_fixture_file(root, relative_path, json.dumps({"score": 80})) elif relative_path.endswith("secret-scan-report.json"): write_fixture_file( root, relative_path, json.dumps( { "schema": "proofframe.secret_scan.v1", "mode": "clear", "ok": True, } ), ) elif relative_path.endswith("submission-audit-report.json"): write_fixture_file( root, relative_path, json.dumps( { "schema": "proofframe.submission_audit.v1", "mode": "pre_submit_audit_ready", "ok": True, } ), ) elif relative_path.endswith("devpost-submission-receipt.json"): write_fixture_file( root, relative_path, json.dumps( { "schema": "proofframe.devpost_submission_receipt.v1", "mode": "submitted", "ok": True, } ), ) else: write_fixture_file(root, relative_path, "# ProofFrame\n") def write_final_gate_fixtures(root: Path, *, closeout_created_at: str = "2026-08-03T21:10:00Z") -> None: write_bundle_fixtures(root) tasks = { "project": "ProofFrame", "updated_at": "2026-08-03T21:15:00Z", "tasks": [ { "id": task_id, "title": f"{task_id} gate", "phase": "P4 Submit", "owner": "tester", "status": "done", "done_criteria": "Gate passes.", "notes": "", } for task_id in ["T020", "T021", "T040", "T041", "T041A", "T042"] ], } write_fixture_file(root, "tasks.json", json.dumps(tasks)) write_fixture_file( root, "docs/assets/devpost-submission-packet.json", json.dumps( { "mode": "post_live_verified", "project_name": "ProofFrame", "tagline": "A provenance-first vault for generated media.", "repository_url": "https://github.com/adjcjh777/backblaze-proofframe", "demo_url": "https://adjcjh-backblaze-proofframe.hf.space/?judge=1", "claim_warning": "final", } ), ) write_fixture_file( root, "docs/assets/final-live-proof-evidence.json", json.dumps( { "ok": True, "storage_backend": "b2", "generation_backend": "genblaze", "asset_storage_backend": "b2", "asset_provider": "genblaze/gmicloud-image", "asset_sha256": "a" * 64, "manifest_sha256": "b" * 64, "asset_storage_key": "campaigns/final/asset.png", "manifest_key": "campaigns/final/manifest.json", } ), ) write_fixture_file( root, "docs/assets/secret-scan-report.json", json.dumps( { "schema": "proofframe.secret_scan.v1", "created_at": closeout_created_at, "mode": "clear", "ok": True, } ), ) write_fixture_file( root, "docs/assets/submission-audit-report.json", json.dumps( { "schema": "proofframe.submission_audit.v1", "created_at": closeout_created_at, "mode": "pre_submit_audit_ready", "ok": True, } ), ) write_fixture_file( root, "docs/assets/devpost-submission-receipt.json", json.dumps( { "schema": "proofframe.devpost_submission_receipt.v1", "created_at": "2026-08-03T21:05:00Z", "mode": "submitted", "ok": True, } ), ) write_fixture_file( root, "docs/assets/final-submission-control.json", json.dumps( { "schema": "proofframe.final_submission_control.v1", "created_at": closeout_created_at, "mode": "final_submit_ready", "safe_to_submit": True, } ), ) write_fixture_file( root, "docs/assets/final-closeout-status.json", json.dumps( { "schema": "proofframe.final_closeout_status.v1", "created_at": closeout_created_at, "mode": "final_closeout_ready", "ok": True, "safe_to_submit": True, "closeout_health_ok": True, } ), ) write_fixture_file( root, "docs/assets/final-video-publish-kit.json", json.dumps( { "schema": "proofframe.final_video_publish_kit.v1", "created_at": closeout_created_at, "mode": "public_video_ready", "ok": True, "safe_to_submit": True, "final_video_ready": True, } ), ) write_fixture_file( root, "docs/assets/final-launch-plan.json", json.dumps( { "schema": "proofframe.final_launch_plan.v1", "created_at": closeout_created_at, "mode": "submitted", "ok": True, } ), ) write_fixture_file( root, "docs/assets/devpost-submission-preview.json", json.dumps( { "schema": "proofframe.devpost_submission_preview.v1", "created_at": closeout_created_at, "mode": "final_preview_ready", "safe_to_submit": True, "ok": True, } ), ) write_fixture_file( root, "docs/assets/judge-decision-brief.json", json.dumps( { "schema": "proofframe.judge_decision_brief.v1", "created_at": closeout_created_at, "mode": "final_decision_ready", "ok": True, "safe_to_submit": True, } ), ) def test_submission_bundle_fails_closed_when_artifacts_are_missing(tmp_path): manifest = submission_bundle.build_manifest(tmp_path) assert manifest["safe_to_share"] is False assert "README.md" in manifest["missing_artifacts"] assert manifest["submission_gate"]["mode"] == "pre_live_safe" def test_submission_bundle_manifest_records_artifacts_and_gate(tmp_path): write_bundle_fixtures(tmp_path) manifest = submission_bundle.build_manifest(tmp_path) assert manifest["safe_to_share"] is True assert manifest["safe_to_submit"] is False assert manifest["devpost_packet"]["project_name"] == "ProofFrame" assert manifest["submission_gate"]["summary"]["done"] == 6 assert manifest["submission_gate"]["report_gate"]["status"] == "verified" assert all("sha256" in artifact for artifact in manifest["artifacts"]) def test_submission_bundle_writes_markdown_json_and_optional_zip(tmp_path): write_bundle_fixtures(tmp_path) manifest = submission_bundle.build_manifest(tmp_path) json_path = tmp_path / "out" / "bundle.json" markdown_path = tmp_path / "out" / "bundle.md" zip_path = tmp_path / "out" / "bundle.zip" submission_bundle.write_outputs( manifest, json_path=json_path, markdown_path=markdown_path, zip_path=zip_path, root=tmp_path, ) saved = json.loads(json_path.read_text(encoding="utf-8")) markdown = markdown_path.read_text(encoding="utf-8") assert saved["schema"] == "proofframe.submission_bundle.v1" assert "# ProofFrame Submission Bundle" in markdown assert "No credentials" in markdown with ZipFile(zip_path) as archive: names = set(archive.namelist()) assert "submission-bundle-manifest.json" in names assert "README.md" in names def test_submission_bundle_cli_ok_tracks_final_gate_not_shareability(tmp_path): write_bundle_fixtures(tmp_path) manifest = submission_bundle.build_manifest(tmp_path) args = SimpleNamespace( json_out=tmp_path / "bundle.json", markdown_out=tmp_path / "bundle.md", zip_out=None, ) summary = submission_bundle.cli_summary(manifest, args) assert summary["safe_to_share"] is True assert summary["safe_to_submit"] is False assert summary["ok"] is False def test_submission_bundle_requires_post_receipt_closeout_reports(tmp_path): write_final_gate_fixtures(tmp_path) manifest = submission_bundle.build_manifest(tmp_path) assert manifest["safe_to_share"] is True assert manifest["submission_gate"]["ok"] is True assert manifest["closeout_gate"]["ok"] is True assert manifest["safe_to_submit"] is True report_ids = {report["id"] for report in manifest["closeout_gate"]["reports"]} assert "final_video_publish_kit_after_receipt" in report_ids assert "judge_decision_brief_after_receipt" in report_ids assert "final_closeout_status_after_receipt" in report_ids def test_submission_bundle_requires_final_closeout_status_report(tmp_path): write_final_gate_fixtures(tmp_path) (tmp_path / "docs/assets/final-closeout-status.json").unlink() manifest = submission_bundle.build_manifest(tmp_path) assert manifest["submission_gate"]["ok"] is True assert manifest["closeout_gate"]["ok"] is False assert manifest["safe_to_submit"] is False closeout_report = next( report for report in manifest["closeout_gate"]["reports"] if report["id"] == "final_closeout_status_after_receipt" ) assert closeout_report["present"] is False assert "missing" in closeout_report["findings"][0] def test_submission_bundle_blocks_stale_closeout_reports(tmp_path): write_final_gate_fixtures(tmp_path, closeout_created_at="2026-08-03T21:00:00Z") manifest = submission_bundle.build_manifest(tmp_path) assert manifest["submission_gate"]["ok"] is True assert manifest["closeout_gate"]["ok"] is False assert manifest["safe_to_submit"] is False assert any( "before the Devpost receipt" in finding for report in manifest["closeout_gate"]["reports"] for finding in report["findings"] ) def test_submission_bundle_strict_final_exits_when_gate_is_not_ready(tmp_path, monkeypatch): write_bundle_fixtures(tmp_path) monkeypatch.setattr(submission_bundle, "ROOT", tmp_path) monkeypatch.setattr( sys, "argv", [ "submission_bundle.py", "--strict-final", "--json-out", str(tmp_path / "bundle.json"), "--markdown-out", str(tmp_path / "bundle.md"), ], ) with pytest.raises(SystemExit) as exc: submission_bundle.main() assert exc.value.code == 2