"""Git ref mutations must be reported, never hidden as diagnostics (#297). ``git fetch`` updates remote-tracking refs even though it edits no files. Reports that ran fetch (or any ref-updating command) must carry a ``Git ref mutations`` entry and may not claim ``Git/worktree mutations: None`` or classify the fetch as read-only diagnostics. """ import sys import unittest sys.path.insert(0, str(__import__("pathlib").Path(__file__).resolve().parent.parent)) import review_proofs def _assess(report, commands): return review_proofs.assess_git_ref_mutation_report( report, command_log=commands) class TestGitRefMutationDetection(unittest.TestCase): def test_fetch_only_review_with_proper_ledger_passes(self): report = "\n".join([ "Git ref mutations: fetched prgs/master", "Review decision: APPROVE", ]) res = _assess(report, ["git fetch prgs master"]) self.assertTrue(res["proven"], res["reasons"]) self.assertEqual(res["ref_mutating_commands"], ["git fetch prgs master"]) def test_fetch_without_ref_mutation_line_is_blocked(self): report = "Review decision: APPROVE\nMutations: None\n" res = _assess(report, ["git fetch prgs master"]) self.assertFalse(res["proven"]) self.assertTrue( any("git ref mutations" in r.lower() for r in res["reasons"])) def test_fetch_with_remote_branch_requires_fetched_target_in_report(self): report = "Git ref mutations: some refs updated\n" res = _assess(report, ["git fetch prgs master"]) self.assertFalse(res["proven"]) self.assertTrue( any("prgs/master" in r for r in res["reasons"])) def test_git_worktree_mutations_none_rejected_when_fetch_occurred(self): report = "\n".join([ "Git ref mutations: fetched prgs/master", "Git/worktree mutations: None", ]) res = _assess(report, ["git fetch prgs master"]) self.assertFalse(res["proven"]) self.assertTrue( any("git/worktree mutations" in r.lower() for r in res["reasons"])) def test_fetch_classified_as_read_only_diagnostics_rejected(self): report = "\n".join([ "Git ref mutations: fetched prgs/master", "Read-only diagnostics: git fetch prgs master, git status", ]) res = _assess(report, ["git fetch prgs master"]) self.assertFalse(res["proven"]) self.assertTrue( any("read-only" in r.lower() for r in res["reasons"])) def test_worktree_creation_is_not_a_ref_mutation(self): report = "Worktree mutations: created branches/review-pr9\n" res = _assess(report, ["git worktree add branches/review-pr9 prgs/master"]) self.assertTrue(res["proven"], res["reasons"]) self.assertEqual(res["ref_mutating_commands"], []) def test_merge_command_counts_as_ref_mutation(self): report = "Review decision: APPROVE\n" res = _assess(report, ["git merge --no-ff feat/x"]) self.assertFalse(res["proven"]) self.assertIn("git merge --no-ff feat/x", res["ref_mutating_commands"]) def test_cleanup_branch_delete_counts_as_ref_mutation(self): report = "Cleanup mutations: deleted branch feat/x\n" res = _assess(report, ["git branch -d feat/x"]) self.assertFalse(res["proven"]) self.assertIn("git branch -d feat/x", res["ref_mutating_commands"]) def test_no_mutation_blocked_handoff_passes(self): report = "\n".join([ "Git/worktree mutations: None", "Current status: blocked handoff, no mutations performed", ]) res = _assess(report, ["git status --porcelain", "git log --oneline -1"]) self.assertTrue(res["proven"], res["reasons"]) self.assertEqual(res["ref_mutating_commands"], []) def test_command_log_dict_entries_supported(self): report = "Git ref mutations: fetched prgs/master\n" res = _assess(report, [{"command": "git fetch prgs master"}]) self.assertTrue(res["proven"], res["reasons"]) self.assertEqual( res["ref_mutating_commands"], ["git fetch prgs master"]) if __name__ == "__main__": unittest.main()