Add assess_merge_simulation_report to reject local merge simulations listed as read-only diagnostics and require Worktree/index mutations documentation with worktree path, pre/merge/abort/post-clean proof when simulation commands appear in the session log. Co-Authored-By: Claude Opus 4.8 (1M context) <[email protected]>
135 lines
4.9 KiB
Python
135 lines
4.9 KiB
Python
"""Tests for merge-simulation worktree mutation verifier (#317)."""
|
|
import sys
|
|
import unittest
|
|
from pathlib import Path
|
|
|
|
sys.path.insert(0, str(Path(__file__).resolve().parent.parent))
|
|
|
|
from reviewer_merge_simulation import ( # noqa: E402
|
|
assess_merge_simulation_report,
|
|
merge_simulation_commands,
|
|
)
|
|
|
|
|
|
def _good_simulation_report(**overrides) -> str:
|
|
lines = [
|
|
"Worktree/index mutations: merge simulation in branches/review-pr281",
|
|
"Worktree path: branches/review-pr281",
|
|
"Pre-simulation clean status: clean (git status --porcelain empty)",
|
|
"Merge result: conflicts in review_proofs.py",
|
|
"Abort command: git merge --abort",
|
|
"Post-abort clean status: clean after abort (git status --porcelain empty)",
|
|
]
|
|
text = "\n".join(lines)
|
|
for key, value in overrides.items():
|
|
text = text.replace(f"{key}:", f"{key}: {value}")
|
|
return text
|
|
|
|
|
|
class TestMergeSimulationDetection(unittest.TestCase):
|
|
def test_detects_no_commit_merge(self):
|
|
commands = merge_simulation_commands([
|
|
"git merge prgs/master --no-commit --no-ff",
|
|
"git status",
|
|
])
|
|
self.assertEqual(commands, ["git merge prgs/master --no-commit --no-ff"])
|
|
|
|
def test_detects_merge_abort(self):
|
|
commands = merge_simulation_commands([
|
|
"git merge prgs/master --no-commit --no-ff",
|
|
"git merge --abort",
|
|
])
|
|
self.assertIn("git merge --abort", commands)
|
|
|
|
|
|
class TestMergeSimulationReport(unittest.TestCase):
|
|
def test_no_simulation_passes(self):
|
|
result = assess_merge_simulation_report(
|
|
"Review complete. Worktree/index mutations: none",
|
|
command_log=["git status --porcelain", "git diff prgs/master...HEAD"],
|
|
)
|
|
self.assertTrue(result["proven"])
|
|
|
|
def test_documented_simulation_passes(self):
|
|
report = _good_simulation_report()
|
|
result = assess_merge_simulation_report(
|
|
report,
|
|
command_log=[
|
|
"git merge prgs/master --no-commit --no-ff",
|
|
"git merge --abort",
|
|
],
|
|
)
|
|
self.assertTrue(result["proven"], result["reasons"])
|
|
|
|
def test_readonly_classification_blocks(self):
|
|
report = "\n".join([
|
|
"Read-only diagnostics: git merge prgs/master --no-commit --no-ff, git status",
|
|
"Worktree/index mutations: none",
|
|
])
|
|
result = assess_merge_simulation_report(
|
|
report,
|
|
command_log=["git merge prgs/master --no-commit --no-ff"],
|
|
)
|
|
self.assertFalse(result["proven"])
|
|
self.assertTrue(result["block"])
|
|
self.assertTrue(any("read-only" in r.lower() for r in result["reasons"]))
|
|
|
|
def test_missing_worktree_field_blocks(self):
|
|
report = "Merge simulation ran but only noted in prose."
|
|
result = assess_merge_simulation_report(
|
|
report,
|
|
command_log=["git merge prgs/master --no-commit --no-ff"],
|
|
)
|
|
self.assertFalse(result["proven"])
|
|
self.assertTrue(any("worktree/index mutations" in r.lower() for r in result["reasons"]))
|
|
|
|
def test_conflict_simulation_requires_merge_result(self):
|
|
report = _good_simulation_report().replace(
|
|
"Merge result: conflicts in review_proofs.py",
|
|
"",
|
|
)
|
|
result = assess_merge_simulation_report(
|
|
report,
|
|
command_log=["git merge prgs/master --no-commit --no-ff"],
|
|
)
|
|
self.assertFalse(result["proven"])
|
|
self.assertTrue(any("merge result" in r.lower() for r in result["reasons"]))
|
|
|
|
def test_aborted_simulation_requires_post_clean_proof(self):
|
|
report = _good_simulation_report().replace(
|
|
"Post-abort clean status: clean after abort (git status --porcelain empty)",
|
|
"",
|
|
)
|
|
result = assess_merge_simulation_report(
|
|
report,
|
|
command_log=[
|
|
"git merge prgs/master --no-commit --no-ff",
|
|
"git merge --abort",
|
|
],
|
|
)
|
|
self.assertFalse(result["proven"])
|
|
self.assertTrue(any("post-abort" in r.lower() for r in result["reasons"]))
|
|
|
|
def test_successful_simulation_without_abort_omits_abort_fields(self):
|
|
report = "\n".join([
|
|
"Worktree/index mutations: merge simulation completed cleanly",
|
|
"Worktree path: branches/review-pr281",
|
|
"Pre-simulation clean status: clean before simulation",
|
|
"Merge result: clean fast-forward simulation",
|
|
])
|
|
result = assess_merge_simulation_report(
|
|
report,
|
|
command_log=["git merge prgs/master --no-commit --no-ff"],
|
|
)
|
|
self.assertTrue(result["proven"], result["reasons"])
|
|
|
|
|
|
class TestExport(unittest.TestCase):
|
|
def test_review_proofs_reexport(self):
|
|
from review_proofs import assess_merge_simulation_report as exported
|
|
|
|
self.assertTrue(callable(exported))
|
|
|
|
|
|
if __name__ == "__main__":
|
|
unittest.main() |