"""Tests for branches-only author mutation worktree guard (#274).""" from __future__ import annotations import sys import unittest from pathlib import Path from unittest import mock sys.path.insert(0, str(Path(__file__).resolve().parent.parent)) import author_mutation_worktree as amw # noqa: E402 class TestPathUnderBranches(unittest.TestCase): ROOT = "/repo/Gitea-Tools" def test_branches_subdirectory_passes(self): self.assertTrue( amw.is_path_under_branches(f"{self.ROOT}/branches/issue-274", self.ROOT) ) def test_control_checkout_fails(self): self.assertFalse(amw.is_path_under_branches(self.ROOT, self.ROOT)) def test_sibling_directory_fails(self): self.assertFalse( amw.is_path_under_branches("/repo/other-checkout", self.ROOT) ) class TestAssessAuthorMutationWorktree(unittest.TestCase): ROOT = "/repo/Gitea-Tools" def test_branches_worktree_passes(self): result = amw.assess_author_mutation_worktree( workspace_path=f"{self.ROOT}/branches/issue-274", project_root=self.ROOT, current_branch="feat/issue-274-branches-only-worktrees", ) self.assertTrue(result["proven"]) self.assertFalse(result["block"]) def test_control_checkout_blocks(self): result = amw.assess_author_mutation_worktree( workspace_path=self.ROOT, project_root=self.ROOT, current_branch="master", ) self.assertFalse(result["proven"]) self.assertTrue(result["block"]) self.assertIn("control checkout", result["reasons"][0]) def test_drifted_control_branch_blocks(self): result = amw.assess_author_mutation_worktree( workspace_path=self.ROOT, project_root=self.ROOT, current_branch="feat/some-task", ) self.assertTrue(result["block"]) self.assertTrue(any("drift" in r for r in result["reasons"])) def test_branches_worktree_as_project_root_passes(self): """MCP launched from a branches/ worktree uses that path as PROJECT_ROOT.""" worktree_root = f"{self.ROOT}/branches/issue-274" result = amw.assess_author_mutation_worktree( workspace_path=worktree_root, project_root=worktree_root, current_branch="feat/issue-274-branches-only-worktrees", ) self.assertTrue(result["proven"]) self.assertFalse(result["block"]) class TestPreflightIntegration(unittest.TestCase): def test_verify_preflight_blocks_control_checkout_with_test_porcelain(self): import mcp_server mcp_server._preflight_whoami_called = True mcp_server._preflight_capability_called = True mcp_server._preflight_resolved_role = "author" control_root = "/repo/Gitea-Tools" with mock.patch.object(mcp_server, "PROJECT_ROOT", control_root): with mock.patch.dict( "os.environ", {"GITEA_TEST_PORCELAIN": ""}, clear=False, ): with self.assertRaises(RuntimeError) as ctx: mcp_server.verify_preflight_purity() self.assertIn("Branches-only mutation guard", str(ctx.exception)) def test_verify_preflight_allows_branches_worktree(self): import mcp_server mcp_server._preflight_whoami_called = True mcp_server._preflight_capability_called = True mcp_server._preflight_resolved_role = "author" worktree = "/repo/Gitea-Tools/branches/issue-274" with mock.patch.dict( "os.environ", {"GITEA_TEST_PORCELAIN": ""}, clear=False, ): mcp_server.verify_preflight_purity(worktree_path=worktree) if __name__ == "__main__": unittest.main()