"""Tests for issue-lock worktree validation (#249).""" import os import sys import unittest from unittest.mock import patch sys.path.insert(0, str(__import__("pathlib").Path(__file__).resolve().parent.parent)) import issue_lock_worktree # noqa: E402 class TestIssueLockWorktreeAssessment(unittest.TestCase): def test_clean_base_branch_passes(self): result = issue_lock_worktree.assess_issue_lock_worktree( worktree_path="/scratch/wt", current_branch="master", porcelain_status="", ) self.assertTrue(result["proven"]) self.assertFalse(result["block"]) def test_dirty_tracked_files_fail(self): result = issue_lock_worktree.assess_issue_lock_worktree( worktree_path="/scratch/wt", current_branch="master", porcelain_status=" M gitea_mcp_server.py\n", ) self.assertFalse(result["proven"]) self.assertIn("tracked file edits exist before issue lock", result["reasons"][0]) def test_feature_branch_fails(self): result = issue_lock_worktree.assess_issue_lock_worktree( worktree_path="/scratch/wt", current_branch="feat/issue-243-forbidden-git-gaps", porcelain_status="", ) self.assertFalse(result["proven"]) self.assertIn("base-equivalence could not be proven", result["reasons"][0]) def test_base_equivalent_feature_branch_passes(self): result = issue_lock_worktree.assess_issue_lock_worktree( worktree_path="/repo/branches/issue-275", current_branch="feat/issue-275-claim-lock-branches-worktree", porcelain_status="", base_equivalent=True, inspected_git_root="/repo/branches/issue-275", base_branch="origin/master", ) self.assertTrue(result["proven"]) self.assertFalse(result["block"]) self.assertEqual(result["base_branch"], "origin/master") def test_non_base_equivalent_branch_fails(self): result = issue_lock_worktree.assess_issue_lock_worktree( worktree_path="/repo/branches/issue-275", current_branch="feat/issue-275-claim-lock-branches-worktree", porcelain_status="", base_equivalent=False, inspected_git_root="/repo/branches/issue-275", base_branch=None, ) self.assertFalse(result["proven"]) self.assertIn("must be base-equivalent", result["reasons"][0]) def test_untracked_files_do_not_block(self): result = issue_lock_worktree.assess_issue_lock_worktree( worktree_path="/scratch/wt", current_branch="main", porcelain_status="?? notes.txt\n", ) self.assertTrue(result["proven"]) class TestStackedBaseEquivalence(unittest.TestCase): """extra_bases (an approved stacked base) can anchor base-equivalence (#484).""" def _git(self, *args): import subprocess subprocess.run( ["git", "-C", self.repo, *args], check=True, capture_output=True, text=True, ) def setUp(self): import subprocess import tempfile self.tmp = tempfile.TemporaryDirectory() self.repo = self.tmp.name subprocess.run(["git", "init", "-q", self.repo], check=True, capture_output=True) self._git("config", "user.email", "t@t") self._git("config", "user.name", "t") self._git("commit", "--allow-empty", "-q", "-m", "base") # Rename the default branch away from master/main/dev so no *base* branch # exists at HEAD — otherwise HEAD would be base-equivalent for free. self._git("branch", "-m", "trunk") # Create a non-master "dependency" branch at the same commit, then a # feature branch off it — mirrors a stacked worktree. self._git("branch", "feat/issue-100-dep") self._git("checkout", "-q", "-b", "feat/issue-101-stacked") def tearDown(self): self.tmp.cleanup() def test_stacked_base_not_equivalent_without_extra_bases(self): state = issue_lock_worktree.read_worktree_git_state(self.repo) # HEAD does not match master/main/dev, so base-equivalence is False. self.assertFalse(state["base_equivalent"]) def test_stacked_base_equivalent_with_extra_bases(self): state = issue_lock_worktree.read_worktree_git_state( self.repo, extra_bases=("feat/issue-100-dep",) ) self.assertTrue(state["base_equivalent"]) self.assertEqual(state["base_branch"], "feat/issue-100-dep") def test_unrelated_extra_base_does_not_anchor(self): state = issue_lock_worktree.read_worktree_git_state( self.repo, extra_bases=("feat/does-not-exist",) ) self.assertFalse(state["base_equivalent"]) class TestIssueLockWorktreeResolution(unittest.TestCase): def test_explicit_path_wins(self): resolved = issue_lock_worktree.resolve_author_worktree_path( "/tmp/scratch/wt", "/shared/dev" ) self.assertEqual(resolved, os.path.realpath("/tmp/scratch/wt")) def test_env_var_when_explicit_missing(self): with patch.dict(os.environ, {"GITEA_AUTHOR_WORKTREE": "/tmp/from-env"}): resolved = issue_lock_worktree.resolve_author_worktree_path( None, "/shared/dev" ) self.assertEqual(resolved, os.path.realpath("/tmp/from-env")) def test_project_root_fallback(self): resolved = issue_lock_worktree.resolve_author_worktree_path( None, "/shared/dev" ) self.assertEqual(resolved, os.path.realpath("/shared/dev")) class TestPrWorktreeMatch(unittest.TestCase): def test_matching_paths_pass(self): result = issue_lock_worktree.verify_pr_worktree_matches_lock( "/tmp/scratch/wt", "/tmp/scratch/wt", "/shared/dev", ) self.assertTrue(result["proven"]) def test_mismatch_fails(self): result = issue_lock_worktree.verify_pr_worktree_matches_lock( "/tmp/scratch/wt", "/shared/dev", "/shared/dev", ) self.assertFalse(result["proven"]) self.assertIn("does not match locked worktree", result["reasons"][0]) def test_missing_locked_path_skips_check(self): result = issue_lock_worktree.verify_pr_worktree_matches_lock( None, "/any/path", "/shared/dev", ) self.assertTrue(result["proven"]) if __name__ == "__main__": unittest.main()