Merge prgs/master into feat/issue-274-branches-only-worktrees; keep both already_landed_reconcile and author_mutation_worktree imports. Allow branches/ worktrees when PROJECT_ROOT is the session worktree; align commit-payload tests with branches-only guard (#274).
109 lines
3.8 KiB
Python
109 lines
3.8 KiB
Python
"""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() |