"""Tests for merged PR cleanup reconciliation (#269).""" import os import sys import tempfile import unittest from unittest.mock import patch sys.path.insert(0, str(__import__("pathlib").Path(__file__).resolve().parent.parent)) import merged_cleanup_reconcile as mcr # noqa: E402 class TestMergedCleanupAssessment(unittest.TestCase): def test_extract_linked_issue_from_closes(self): issue = mcr.extract_linked_issue( "feat: cleanup (Closes #269)", "No other refs", ) self.assertEqual(issue, 269) def test_merged_remote_branch_safe_when_gates_pass(self): result = mcr.assess_remote_branch_cleanup( pr_number=42, head_branch="feat/issue-269-merged-pr-cleanup-reconcile", merged=True, remote_branch_exists=True, open_pr_heads=set(), head_on_master=True, delete_capability_allowed=True, active_lock=False, ) self.assertTrue(result["safe_to_delete_remote"]) self.assertEqual(result["block_reasons"], []) def test_open_pr_blocks_remote_delete(self): result = mcr.assess_remote_branch_cleanup( pr_number=42, head_branch="feat/issue-9-example", merged=True, remote_branch_exists=True, open_pr_heads={"feat/issue-9-example"}, head_on_master=True, delete_capability_allowed=True, active_lock=False, ) self.assertFalse(result["safe_to_delete_remote"]) self.assertIn("open PR", result["block_reasons"][0]) def test_protected_branch_blocks_remote_delete(self): result = mcr.assess_remote_branch_cleanup( pr_number=1, head_branch="master", merged=True, remote_branch_exists=True, open_pr_heads=set(), head_on_master=True, delete_capability_allowed=True, active_lock=False, ) self.assertFalse(result["safe_to_delete_remote"]) self.assertIn("protected", result["block_reasons"][0]) def test_active_lock_blocks_remote_and_worktree_cleanup(self): remote = mcr.assess_remote_branch_cleanup( pr_number=42, head_branch="feat/issue-9-example", merged=True, remote_branch_exists=True, open_pr_heads=set(), head_on_master=True, delete_capability_allowed=True, active_lock=True, ) local = mcr.assess_local_worktree_cleanup( pr_number=42, head_branch="feat/issue-9-example", merged=True, worktree_state={ "exists": True, "clean": True, "current_branch": "feat/issue-9-example", "worktree_path": "/repo/branches/feat-issue-9-example", }, active_lock=True, ) self.assertFalse(remote["safe_to_delete_remote"]) self.assertFalse(local["safe_to_remove_worktree"]) self.assertIn("active issue lock", remote["block_reasons"][0]) def test_dirty_worktree_blocks_local_cleanup(self): result = mcr.assess_local_worktree_cleanup( pr_number=42, head_branch="feat/issue-9-example", merged=True, worktree_state={ "exists": True, "clean": False, "dirty_files": ["gitea_mcp_server.py"], "current_branch": "feat/issue-9-example", "worktree_path": "/repo/branches/feat-issue-9-example", }, active_lock=False, ) self.assertFalse(result["safe_to_remove_worktree"]) self.assertIn("tracked edits", result["block_reasons"][0]) class TestMergedCleanupReport(unittest.TestCase): def test_build_report_skips_unmerged_closed_prs(self): with tempfile.TemporaryDirectory() as tmp: report = mcr.build_reconciliation_report( project_root=tmp, closed_prs=[ { "number": 10, "title": "closed not merged", "body": "Closes #10", "head": {"ref": "feat/issue-10-x"}, "merged_at": None, }, { "number": 11, "title": "merged", "body": "Closes #11", "head": {"ref": "feat/issue-11-x"}, "merged_at": "2026-07-06T12:00:00Z", "merge_commit_sha": "abc123", }, ], open_prs=[], remote_branch_exists={"feat/issue-11-x": True}, head_on_master={11: True}, delete_capability_allowed=False, ) self.assertEqual(report["merged_pr_count"], 1) entry = report["entries"][0] self.assertEqual(entry["pr_number"], 11) self.assertEqual(entry["issue_number"], 11) self.assertFalse(entry["remote_branch"]["safe_to_delete_remote"]) self.assertIn("delete_branch capability", entry["remote_branch"]["block_reasons"][0]) def test_has_active_issue_lock_reads_lock_file(self): with tempfile.NamedTemporaryFile("w", encoding="utf-8", delete=False) as handle: handle.write('{"branch_name": "feat/issue-9-example"}') lock_path = handle.name try: self.assertTrue( mcr.has_active_issue_lock("feat/issue-9-example", lock_path=lock_path) ) self.assertFalse( mcr.has_active_issue_lock("feat/other-branch", lock_path=lock_path) ) finally: os.remove(lock_path) if __name__ == "__main__": unittest.main()