#!/usr/bin/env python3 """Regression tests for worktree cleanup audit integrity (#404).""" from __future__ import annotations import os import sys import unittest sys.path.insert(0, os.path.dirname(os.path.dirname(os.path.abspath(__file__)))) from worktree_cleanup_audit import ( # noqa: E402 assess_cleanup_audit_final_report, assess_cleanup_audit_integrity, assess_worktree_cleanup_integrity, classify_branches_entry, parse_worktree_list_porcelain, reconcile_cleanup_audit, ) def _entry( path: str, *, classification: str, registered: bool = True, preserve: bool | None = None, ) -> dict: preserve_flag = preserve if preserve is not None else classification in { "active_open_pr", "active_issue_work", "dirty_local_worktree", "unsafe_unknown", } return { "path": path, "classification": classification, "preserve": preserve_flag, "registered_worktree": registered, "worktree_state": {"exists": True, "clean": classification == "clean_stale_removable"}, "worktree_record": {"branch": "feat/x"} if registered else None, } def _snapshot(entries: list[dict]) -> dict: return {"entries": entries} class TestParseWorktreePorcelain(unittest.TestCase): def test_parses_multiple_worktrees(self): text = "\n".join([ "worktree /proj/branches/foo", "HEAD abcdef0123456789abcdef0123456789abcdef0", "branch refs/heads/feat/foo", "", "worktree /proj", "HEAD 1111111111111111111111111111111111111111", "branch refs/heads/master", ]) parsed = parse_worktree_list_porcelain(text) self.assertEqual(len(parsed), 2) self.assertEqual(parsed[0]["branch"], "feat/foo") class TestClassifyEntry(unittest.TestCase): def test_active_pr_classification(self): result = classify_branches_entry( rel_path="branches/feat-issue-1-x", worktree_record={"branch": "feat/issue-1-x"}, worktree_state={"exists": True, "clean": True, "dirty_files": []}, open_pr_branches={"feat/issue-1-x"}, ) self.assertEqual(result, "active_open_pr") def test_dirty_classification(self): result = classify_branches_entry( rel_path="branches/dirty-one", worktree_record={"branch": "feat/dirty-one"}, worktree_state={"exists": True, "dirty_files": ["a.py"]}, open_pr_branches=set(), ) self.assertEqual(result, "dirty_local_worktree") class TestCleanupIntegrity(unittest.TestCase): def test_preserved_worktree_remains(self): path = "branches/keep-me" before = _snapshot([_entry(path, classification="clean_stale_removable", preserve=False)]) after = _snapshot([_entry(path, classification="clean_stale_removable", preserve=False)]) result = assess_worktree_cleanup_integrity(before=before, after=after) self.assertTrue(result["integrity_passed"]) def test_intentional_removal_passes(self): path = "branches/remove-me" before = _snapshot([_entry(path, classification="clean_stale_removable", preserve=False)]) after = _snapshot([]) result = assess_worktree_cleanup_integrity( before=before, after=after, removals=[{ "path": path, "method": "git worktree remove", "pre_removal_proof": "clean status", }], ) self.assertTrue(result["integrity_passed"]) def test_dirty_worktree_disappears_fails(self): path = "branches/dirty-wt" before = _snapshot([_entry(path, classification="dirty_local_worktree")]) after = _snapshot([]) recon = reconcile_cleanup_audit(before, after) result = assess_cleanup_audit_integrity(recon) self.assertFalse(result["proven"]) def test_active_pr_worktree_disappears_fails(self): path = "branches/review-pr99" before = _snapshot([_entry(path, classification="active_open_pr")]) after = _snapshot([]) result = assess_worktree_cleanup_integrity(before=before, after=after) self.assertFalse(result["integrity_passed"]) def test_explained_missing_allowed(self): path = "branches/review-pr382" before = _snapshot([ _entry(path, classification="detached_review_leftover", preserve=False), ]) after = _snapshot([]) result = assess_worktree_cleanup_integrity( before=before, after=after, explained_missing={path: "removed concurrently by sibling session"}, ) self.assertTrue(result["integrity_passed"]) def test_removal_log_omits_clean_stale_fails(self): path = "branches/a" before = _snapshot([_entry(path, classification="clean_stale_removable", preserve=False)]) after = _snapshot([]) recon = reconcile_cleanup_audit(before, after, removal_log=[]) self.assertFalse(recon["removal_log_complete"]) class TestCleanupReportProof(unittest.TestCase): def test_complete_report_passes(self): report = "\n".join([ "Cleanup audit reconciliation table:", "Initial count: 10", "Removed count: 3", "Preserved count: 7", "Missing-unexplained count: 0", "Final count: 7", "Final verification: git worktree list proof attached", ]) result = assess_cleanup_audit_final_report(report) self.assertTrue(result["proven"]) def test_incomplete_report_fails(self): result = assess_cleanup_audit_final_report("removed some worktrees") self.assertFalse(result["proven"]) if __name__ == "__main__": unittest.main()