Adds mutation-capability table validation so review_pr cannot authorize merge or delete mutations without their own exact capability rows. Co-Authored-By: Claude Opus 4.8 (1M context) <[email protected]>
142 lines
5.1 KiB
Python
142 lines
5.1 KiB
Python
"""Tests for exact per-mutation capability proof in reviewer reports (#405)."""
|
|
|
|
import sys
|
|
import unittest
|
|
from pathlib import Path
|
|
|
|
sys.path.insert(0, str(Path(__file__).resolve().parent.parent))
|
|
|
|
from reviewer_mutation_capability_proof import assess_mutation_capability_proof
|
|
from review_proofs import assess_mutation_capability_proof as proofs_assess
|
|
from final_report_validator import assess_final_report_validator
|
|
|
|
|
|
REVIEW_ONLY = """
|
|
Review decision: approved
|
|
Mutation capability table:
|
|
- gitea_submit_pr_review | review_pr (gitea.pr.review) | submitted | resolved before review
|
|
"""
|
|
|
|
MERGE_PROVEN = """
|
|
Review decision: approved
|
|
Merge result: merged 0123456789ab
|
|
Mutation capability table:
|
|
- gitea_submit_pr_review | review_pr (gitea.pr.review) | submitted | before review
|
|
- gitea_merge_pr | merge_pr (gitea.pr.merge) | merged | resolved before merge
|
|
"""
|
|
|
|
MERGE_AND_DELETE_PROVEN = """
|
|
Review decision: approved
|
|
Merge result: merged 0123456789ab
|
|
Remote branch deleted: feat/issue-x
|
|
Mutation capability table:
|
|
- gitea_submit_pr_review | review_pr (gitea.pr.review) | submitted | before review
|
|
- gitea_merge_pr | merge_pr (gitea.pr.merge) | merged | before merge
|
|
- gitea_delete_branch | delete_branch (gitea.branch.delete) | deleted | before delete
|
|
"""
|
|
|
|
|
|
class TestModule(unittest.TestCase):
|
|
def test_review_only_with_capability_passes(self):
|
|
r = assess_mutation_capability_proof(REVIEW_ONLY)
|
|
self.assertTrue(r["proven"], r["reasons"])
|
|
|
|
def test_merge_with_exact_capability_passes(self):
|
|
r = assess_mutation_capability_proof(MERGE_PROVEN)
|
|
self.assertTrue(r["proven"], r["reasons"])
|
|
|
|
def test_merge_and_delete_fully_proven_passes(self):
|
|
r = assess_mutation_capability_proof(MERGE_AND_DELETE_PROVEN)
|
|
self.assertTrue(r["proven"], r["reasons"])
|
|
|
|
def test_review_pr_does_not_authorize_merge(self):
|
|
report = """
|
|
Review decision: approved
|
|
Merge result: merged 0123456789ab
|
|
Mutation capability table:
|
|
- gitea_submit_pr_review | review_pr (gitea.pr.review) | submitted | before review
|
|
"""
|
|
r = assess_mutation_capability_proof(report)
|
|
self.assertFalse(r["proven"])
|
|
self.assertTrue(any("merge" in x.lower() for x in r["reasons"]), r["reasons"])
|
|
|
|
def test_merge_pr_does_not_authorize_branch_deletion(self):
|
|
report = """
|
|
Review decision: approved
|
|
Merge result: merged 0123456789ab
|
|
Remote branch deleted: feat/issue-x
|
|
Mutation capability table:
|
|
- gitea_submit_pr_review | review_pr (gitea.pr.review) | submitted | before review
|
|
- gitea_merge_pr | merge_pr (gitea.pr.merge) | merged | before merge
|
|
"""
|
|
r = assess_mutation_capability_proof(report)
|
|
self.assertFalse(r["proven"])
|
|
self.assertTrue(any("delet" in x.lower() for x in r["reasons"]), r["reasons"])
|
|
|
|
def test_delete_skipped_when_capability_missing_passes(self):
|
|
report = """
|
|
Review decision: approved
|
|
Merge result: merged 0123456789ab
|
|
Branch deletion: skipped — delete_branch capability not available
|
|
Mutation capability table:
|
|
- gitea_submit_pr_review | review_pr (gitea.pr.review) | submitted | before review
|
|
- gitea_merge_pr | merge_pr (gitea.pr.merge) | merged | before merge
|
|
"""
|
|
r = assess_mutation_capability_proof(report)
|
|
self.assertTrue(r["proven"], r["reasons"])
|
|
|
|
def test_missing_table_when_merging_blocks(self):
|
|
report = """
|
|
Review decision: approved
|
|
Merge result: merged 0123456789ab
|
|
merge_pr gitea.pr.merge resolved
|
|
"""
|
|
r = assess_mutation_capability_proof(report)
|
|
self.assertFalse(r["proven"])
|
|
self.assertTrue(any("table" in x.lower() for x in r["reasons"]), r["reasons"])
|
|
|
|
def test_post_hoc_proof_blocks(self):
|
|
report = """
|
|
Review decision: approved
|
|
Merge result: merged 0123456789ab
|
|
Mutation capability table:
|
|
- gitea_merge_pr | merge_pr (gitea.pr.merge) | merged | capability resolved after merge
|
|
"""
|
|
r = assess_mutation_capability_proof(report)
|
|
self.assertFalse(r["proven"])
|
|
self.assertTrue(any("after" in x.lower() for x in r["reasons"]), r["reasons"])
|
|
|
|
def test_review_without_capability_blocks(self):
|
|
report = "Review decision: approved\nreview submitted\n"
|
|
r = assess_mutation_capability_proof(report)
|
|
self.assertFalse(r["proven"])
|
|
|
|
def test_no_mutation_no_requirement(self):
|
|
r = assess_mutation_capability_proof("Selected PR: #1\nSkipped, no action.")
|
|
self.assertTrue(r["proven"], r["reasons"])
|
|
|
|
|
|
class TestWiring(unittest.TestCase):
|
|
def test_review_proofs_wrapper_matches_module(self):
|
|
self.assertEqual(
|
|
proofs_assess(MERGE_PROVEN)["proven"],
|
|
assess_mutation_capability_proof(MERGE_PROVEN)["proven"],
|
|
)
|
|
|
|
def test_final_report_validator_flags_nearby_capability_merge(self):
|
|
report = """
|
|
## Controller Handoff
|
|
- Task: review_pr
|
|
Review decision: approved
|
|
Merge result: merged 0123456789ab
|
|
Mutation capability table:
|
|
- gitea_submit_pr_review | review_pr (gitea.pr.review) | submitted | before review
|
|
"""
|
|
result = assess_final_report_validator(report, "review_pr")
|
|
rule_ids = [f["rule_id"] for f in result.get("findings", [])]
|
|
self.assertIn("reviewer.mutation_capability_proof", rule_ids)
|
|
|
|
|
|
if __name__ == "__main__":
|
|
unittest.main()
|