from __future__ import annotations import json import sys from pathlib import Path ROOT = Path(__file__).resolve().parents[1] sys.path.insert(0, str(ROOT / "scripts")) from hai_gate import Status, evaluate, main, render_owner_packet def test_blocks_secret_file_without_owner_approval() -> None: result = evaluate( changed_files=[".env", "src/app.py"], pr_body=""" ## Goal Change app config. ## Verification Tests run: pytest Result: passed """, ) assert result.status == Status.BLOCK assert result.should_block is True def test_code_change_without_test_evidence_requires_evidence() -> None: result = evaluate( changed_files=["src/payments/retry.py"], pr_body=""" ## Goal Fix retry handling. ## Verification Not run. """, ) assert result.status == Status.EVIDENCE_REQUIRED assert result.should_block is True def test_large_scope_requires_owner_decision() -> None: result = evaluate( changed_files=[f"docs/file_{idx}.md" for idx in range(13)], pr_body=""" ## Goal Refresh docs. ## Verification Docs-only review. Result: passed. """, ) assert result.status == Status.OWNER_DECISION assert result.should_block is True def test_docs_with_evidence_passes() -> None: result = evaluate( changed_files=["README.md"], pr_body=""" ## Goal Clarify usage. ## Verification Tests run: docs-only check Result: passed ## Risk Low. """, ) assert result.status == Status.PASS assert result.should_block is False def test_follow_up_is_parked_but_not_blocking() -> None: result = evaluate( changed_files=["README.md"], pr_body=""" ## Goal Clarify usage. ## Verification Tests run: docs-only check Result: passed ## Follow-up - Nice-to-have: add screenshots later. """, ) assert result.status == Status.PARK assert result.should_block is False assert "nice-to-have" in result.findings[0].evidence.lower() def test_owner_approval_allows_risky_file_when_evidence_exists() -> None: result = evaluate( changed_files=[".github/workflows/test.yml"], pr_body=""" ## Goal Add CI. ## Verification Tests run: python scripts/hai_gate.py Result: passed ## Owner decision Approved-by: Samuel """, ) assert result.status == Status.PASS def test_owner_packet_has_single_decision() -> None: result = evaluate(changed_files=["src/app.py"], pr_body="") packet = render_owner_packet(result) assert "## HAI Owner Packet" in packet assert "### One Decision" in packet assert "Do not merge" in packet assert "Merge is blocked" in packet def test_cli_writes_summary_and_blocks(tmp_path: Path) -> None: event = tmp_path / "event.json" files = tmp_path / "files.txt" summary = tmp_path / "summary.md" event.write_text(json.dumps({"pull_request": {"body": ""}}), encoding="utf-8") files.write_text("src/app.py\n", encoding="utf-8") code = main(["--event", str(event), "--files", str(files), "--summary", str(summary)]) assert code == 1 assert "EVIDENCE_REQUIRED" in summary.read_text(encoding="utf-8")