"""Tests for live PR head re-pin before conflict-fix classification (#522).""" from __future__ import annotations import unittest from conflict_fix_classification import ( CLASSIFICATION_CONFLICT_FIX_NEEDED, CLASSIFICATION_INCOMPLETE, CLASSIFICATION_LIVE_MERGEABLE, CLASSIFICATION_STALE_INVENTORY_SKIP, assess_conflict_fix_classification, assess_conflict_fix_classification_final_report, ) def _sha(prefix: str) -> str: return (prefix + "0" * 40)[:40] class TestConflictFixClassification(unittest.TestCase): def test_stale_inventory_mergeable_skip(self): """PR #508 style: inventory mergeable:false/stale head, live mergeable:true.""" result = assess_conflict_fix_classification( pr_number=508, inventory_head_sha=_sha("dad1dc8"), inventory_mergeable=False, live_head_sha=_sha("3f3d6cb"), live_mergeable=True, ) self.assertEqual(result["classification"], CLASSIFICATION_STALE_INVENTORY_SKIP) self.assertTrue(result["skip_author_mutation"]) self.assertFalse(result["worktree_allowed"]) self.assertTrue(result["inventory_stale_head"]) self.assertTrue(result["inventory_stale_mergeable"]) self.assertEqual(result["pinned_head_sha"], _sha("3f3d6cb")) def test_stale_inventory_head_only_live_mergeable_true(self): """PR #493 style: head moved; live still mergeable.""" result = assess_conflict_fix_classification( pr_number=493, inventory_head_sha=_sha("685e627"), inventory_mergeable=True, live_head_sha=_sha("4561d7a"), live_mergeable=True, ) self.assertEqual(result["classification"], CLASSIFICATION_STALE_INVENTORY_SKIP) self.assertTrue(result["skip_author_mutation"]) self.assertFalse(result["worktree_allowed"]) self.assertTrue(result["inventory_stale_head"]) self.assertEqual(result["pinned_head_sha"], _sha("4561d7a")) def test_live_conflict_allows_worktree_on_pinned_head(self): result = assess_conflict_fix_classification( pr_number=99, inventory_head_sha=_sha("aaaaaaa"), inventory_mergeable=False, live_head_sha=_sha("bbbbbbb"), live_mergeable=False, ) self.assertEqual(result["classification"], CLASSIFICATION_CONFLICT_FIX_NEEDED) self.assertTrue(result["worktree_allowed"]) self.assertFalse(result["skip_author_mutation"]) self.assertTrue(result["inventory_stale_head"]) self.assertEqual(result["pinned_head_sha"], _sha("bbbbbbb")) def test_matching_inventory_live_mergeable_skip(self): head = _sha("cccccccc") result = assess_conflict_fix_classification( pr_number=10, inventory_head_sha=head, inventory_mergeable=True, live_head_sha=head, live_mergeable=True, ) self.assertEqual(result["classification"], CLASSIFICATION_LIVE_MERGEABLE) self.assertFalse(result["worktree_allowed"]) self.assertTrue(result["skip_author_mutation"]) self.assertFalse(result["inventory_stale_head"]) def test_missing_live_head_incomplete(self): result = assess_conflict_fix_classification( pr_number=1, inventory_head_sha=_sha("ddddddd"), inventory_mergeable=False, live_head_sha=None, live_mergeable=False, ) self.assertEqual(result["classification"], CLASSIFICATION_INCOMPLETE) self.assertFalse(result["worktree_allowed"]) self.assertTrue(result["skip_author_mutation"]) self.assertTrue(any("live PR head" in r for r in result["reasons"])) def test_short_sha_rejected(self): result = assess_conflict_fix_classification( pr_number=1, inventory_head_sha="abc1234", inventory_mergeable=False, live_head_sha="def5678", live_mergeable=False, ) self.assertEqual(result["classification"], CLASSIFICATION_INCOMPLETE) self.assertFalse(result["worktree_allowed"]) class TestConflictFixClassificationFinalReport(unittest.TestCase): def test_non_conflict_report_not_applicable(self): result = assess_conflict_fix_classification_final_report( "Implemented feature without merge issues." ) self.assertTrue(result["proven"]) self.assertFalse(result["applicable"]) def test_conflict_report_requires_tool_live_head_classification(self): result = assess_conflict_fix_classification_final_report( "Did conflict-fix work on PR #99." ) self.assertFalse(result["proven"]) self.assertTrue(result["applicable"]) joined = " ".join(result["reasons"]) self.assertIn("gitea_assess_conflict_fix_classification", joined) self.assertIn("Live head SHA", joined) self.assertIn("classification", joined.lower()) def test_good_conflict_report_passes(self): live = _sha("3f3d6cb") inv = _sha("dad1dc8") text = f""" Conflict-fix classification: stale_inventory_skip Called gitea_assess_conflict_fix_classification before worktree creation. Inventory head SHA: {inv} Live head SHA: {live} Use live head only; skip author mutation. """ result = assess_conflict_fix_classification_final_report(text) self.assertTrue(result["proven"], msg=result.get("reasons")) self.assertEqual(result["live_head_sha"], live) self.assertEqual(result["inventory_head_sha"], inv) if __name__ == "__main__": unittest.main()