"""Tests for canonical review-workflow citation and version proof (#296). PR review/merge reports must prove they loaded the canonical workflow (workflows/review-merge-pr.md) and cite the workflow version/hash used; a stale or missing hash fails so prompt drift is caught. """ import sys import unittest from pathlib import Path sys.path.insert(0, str(Path(__file__).resolve().parent.parent)) from review_proofs import ( # noqa: E402 assess_review_workflow_source, compute_workflow_hash, ) CANONICAL_TEXT = "# review-merge-pr workflow v1\nstep one\n" CANONICAL_HASH = compute_workflow_hash(CANONICAL_TEXT) def _report(citation=True, version=CANONICAL_HASH): lines = ["Task: review next eligible PR"] if citation: lines.append( "Workflow source: skills/llm-project-workflow/" "workflows/review-merge-pr.md (loaded via mcp_get_skill_guide)" ) if version is not None: lines.append(f"- Workflow version: {version}") return "\n".join(lines) + "\n" class TestComputeWorkflowHash(unittest.TestCase): def test_deterministic(self): self.assertEqual( compute_workflow_hash(CANONICAL_TEXT), compute_workflow_hash(CANONICAL_TEXT), ) def test_changes_with_content(self): self.assertNotEqual( compute_workflow_hash(CANONICAL_TEXT), compute_workflow_hash(CANONICAL_TEXT + "extra rule\n"), ) def test_short_hex(self): h = compute_workflow_hash(CANONICAL_TEXT) self.assertEqual(len(h), 12) int(h, 16) # raises if not hex class TestReviewWorkflowSource(unittest.TestCase): def test_citation_and_matching_hash_passes(self): result = assess_review_workflow_source( _report(), canonical_hash=CANONICAL_HASH ) self.assertTrue(result["complete"]) self.assertEqual(result["reasons"], []) def test_missing_citation_fails(self): result = assess_review_workflow_source( _report(citation=False), canonical_hash=CANONICAL_HASH ) self.assertFalse(result["complete"]) self.assertTrue( any("review-merge-pr" in r.lower() for r in result["reasons"]) ) def test_missing_version_field_fails(self): result = assess_review_workflow_source( _report(version=None), canonical_hash=CANONICAL_HASH ) self.assertFalse(result["complete"]) self.assertTrue( any("workflow version" in r.lower() for r in result["reasons"]) ) def test_stale_hash_fails(self): result = assess_review_workflow_source( _report(version="deadbeef0000"), canonical_hash=CANONICAL_HASH ) self.assertFalse(result["complete"]) self.assertTrue( any("stale" in r.lower() or "does not match" in r.lower() for r in result["reasons"]) ) def test_version_none_fails(self): result = assess_review_workflow_source( _report(version="none"), canonical_hash=CANONICAL_HASH ) self.assertFalse(result["complete"]) def test_without_canonical_hash_field_still_required(self): # No canonical hash supplied (e.g. offline validation): citation # and a populated version field are still required. self.assertTrue( assess_review_workflow_source(_report())["complete"] ) self.assertFalse( assess_review_workflow_source(_report(version=None))["complete"] ) if __name__ == "__main__": unittest.main()