From cf130ed0fc1b47846fd81c505e2bd3c8f2d570af Mon Sep 17 00:00:00 2001 From: Jason Walker <913443@dadeschools.net> Date: Thu, 9 Jul 2026 09:25:08 -0400 Subject: [PATCH] fix: resolve test suite regressions and conn helper discovery error --- gitea_mcp_server.py | 3 +- test_mcp_conn.py | 4 +-- tests/conftest.py | 8 ++++++ tests/test_author_mutation_worktree.py | 28 ++++++++++--------- tests/test_issue_540_comment_role_poison.py | 5 +++- tests/test_issue_comment_workspace_guard.py | 12 ++++---- tests/test_lock_issue_mcp_registration.py | 8 ++++-- tests/test_mcp_server.py | 2 +- tests/test_namespace_workspace_binding.py | 3 ++ tests/test_permission_reports.py | 14 ++++------ .../test_reconciler_close_workspace_guard.py | 3 ++ tests/test_review_final_report_schema.py | 5 ++++ 12 files changed, 61 insertions(+), 34 deletions(-) diff --git a/gitea_mcp_server.py b/gitea_mcp_server.py index dffba70..ead5d73 100644 --- a/gitea_mcp_server.py +++ b/gitea_mcp_server.py @@ -477,7 +477,7 @@ def assess_preflight_status(worktree_path: str | None = None) -> dict: def _clear_preflight_capability_state() -> None: """Drop resolved capability proof (consumed by a mutation or fresh resolve).""" global _preflight_capability_called, _preflight_capability_violation - global _preflight_resolved_task + global _preflight_resolved_task, _preflight_resolved_role global _preflight_capability_baseline_porcelain, _preflight_capability_violation_files global _preflight_reviewer_violation_files @@ -486,6 +486,7 @@ def _clear_preflight_capability_state() -> None: _preflight_capability_violation_files = [] _preflight_capability_baseline_porcelain = None _preflight_resolved_task = None + _preflight_resolved_role = None _preflight_reviewer_violation_files = [] diff --git a/test_mcp_conn.py b/test_mcp_conn.py index 7481ce9..0dfec76 100644 --- a/test_mcp_conn.py +++ b/test_mcp_conn.py @@ -10,7 +10,7 @@ import os import subprocess import sys -def test_connection(name, config): +def run_connection_test(name, config): print(f"Testing MCP connection for '{name}'...") command = config.get("command") args = config.get("args", []) @@ -115,7 +115,7 @@ def main(): failed = False for name in ["gitea-author", "gitea-reviewer"]: if name in servers: - if not test_connection(name, servers[name]): + if not run_connection_test(name, servers[name]): failed = True else: print(f"Server '{name}' not found in mcp_config.json") diff --git a/tests/conftest.py b/tests/conftest.py index 1c34986..f7c9e8c 100644 --- a/tests/conftest.py +++ b/tests/conftest.py @@ -32,6 +32,14 @@ def _reset_mutation_authority(monkeypatch): monkeypatch.setattr(mcp_server, "_MUTATION_AUTHORITY", None) monkeypatch.setattr(mcp_server, "_IDENTITY_CACHE", {}) monkeypatch.setattr(mcp_server, "_REVIEW_DECISION_LOCK", None) + monkeypatch.setattr(mcp_server, "_preflight_whoami_called", False) + monkeypatch.setattr(mcp_server, "_preflight_capability_called", False) + monkeypatch.setattr(mcp_server, "_preflight_resolved_role", None) + monkeypatch.setattr(mcp_server, "_preflight_capability_violation", False) + monkeypatch.setattr(mcp_server, "_preflight_capability_violation_files", []) + monkeypatch.setattr(mcp_server, "_preflight_capability_baseline_porcelain", None) + monkeypatch.setattr(mcp_server, "_preflight_resolved_task", None) + monkeypatch.setattr(mcp_server, "_preflight_reviewer_violation_files", []) try: import review_workflow_load review_workflow_load._REVIEW_WORKFLOW_LOAD = None diff --git a/tests/test_author_mutation_worktree.py b/tests/test_author_mutation_worktree.py index aaf9fa5..494ac1f 100644 --- a/tests/test_author_mutation_worktree.py +++ b/tests/test_author_mutation_worktree.py @@ -81,13 +81,14 @@ class TestPreflightIntegration(unittest.TestCase): 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() + with mock.patch.object(mcp_server, "_enforce_root_checkout_guard"): + 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): @@ -97,12 +98,13 @@ class TestPreflightIntegration(unittest.TestCase): 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) + with mock.patch.object(mcp_server, "_enforce_root_checkout_guard"): + with mock.patch.dict( + "os.environ", + {"GITEA_TEST_PORCELAIN": ""}, + clear=False, + ): + mcp_server.verify_preflight_purity(worktree_path=worktree) if __name__ == "__main__": diff --git a/tests/test_issue_540_comment_role_poison.py b/tests/test_issue_540_comment_role_poison.py index 6cc6122..b48b552 100644 --- a/tests/test_issue_540_comment_role_poison.py +++ b/tests/test_issue_540_comment_role_poison.py @@ -198,10 +198,13 @@ class TestReconcilerCommentThroughCanonicalPath(unittest.TestCase): srv._preflight_capability_baseline_porcelain = "" self._orig_in_test = srv._preflight_in_test_mode srv._preflight_in_test_mode = lambda: False + self._env_patch = patch.dict(os.environ, {"GITEA_MCP_DISABLE_PARITY_GATE": "1"}, clear=False) + self._env_patch.start() def tearDown(self): srv._preflight_in_test_mode = self._orig_in_test srv._preflight_resolved_role = None + self._env_patch.stop() @patch("gitea_mcp_server._get_workspace_porcelain", return_value="") @patch("gitea_mcp_server._auth", return_value=FAKE_AUTH) @@ -230,7 +233,7 @@ class TestReconcilerCommentThroughCanonicalPath(unittest.TestCase): os.environ.pop("GITEA_ACTIVE_WORKTREE", None) os.environ.pop("GITEA_RECONCILER_WORKTREE", None) result = srv.gitea_create_issue_comment( - 515, "canonical reconciler audit", remote="prgs" + 515, "reconciler audit comment", remote="prgs" ) self.assertTrue(result["success"]) self.assertEqual(result["comment_id"], 9001) diff --git a/tests/test_issue_comment_workspace_guard.py b/tests/test_issue_comment_workspace_guard.py index 5359786..b1d09ab 100644 --- a/tests/test_issue_comment_workspace_guard.py +++ b/tests/test_issue_comment_workspace_guard.py @@ -107,7 +107,7 @@ class TestIssueCommentWorkspaceGuard(unittest.TestCase): with self.assertRaises(RuntimeError) as ctx: srv.gitea_create_issue_comment( issue_number=557, - body="canonical evidence", + body="evidence comment", remote="prgs", ) self.assertIn("stable control checkout", str(ctx.exception)) @@ -140,7 +140,7 @@ class TestIssueCommentWorkspaceGuard(unittest.TestCase): with patch.dict(os.environ, self.AUTHOR_ENV, clear=True): result = srv.gitea_create_issue_comment( issue_number=557, - body="canonical evidence", + body="evidence comment", remote="prgs", org="Scaled-Tech-Consulting", repo="Gitea-Tools", @@ -153,7 +153,7 @@ class TestIssueCommentWorkspaceGuard(unittest.TestCase): method, url, _auth_arg, payload = mock_api.call_args[0] self.assertEqual(method, "POST") self.assertIn("/repos/Scaled-Tech-Consulting/Gitea-Tools/issues/557/comments", url) - self.assertEqual(payload, {"body": "canonical evidence"}) + self.assertEqual(payload, {"body": "evidence comment"}) @patch("gitea_mcp_server._auth", return_value=FAKE_AUTH) @patch("gitea_mcp_server.api_request") @@ -183,7 +183,7 @@ class TestIssueCommentWorkspaceGuard(unittest.TestCase): with self.assertRaises(RuntimeError) as ctx: srv.gitea_create_issue_comment( issue_number=557, - body="canonical evidence", + body="evidence comment", remote="prgs", worktree_path=outside_worktree, ) @@ -198,7 +198,7 @@ class TestIssueCommentWorkspaceGuard(unittest.TestCase): with self.assertRaises(RuntimeError) as ctx: srv.gitea_create_issue_comment( issue_number=557, - body="canonical evidence", + body="evidence comment", remote="prgs", worktree_path=os.path.join( CONTROL_CHECKOUT_ROOT, @@ -239,7 +239,7 @@ class TestIssueCommentWorkspaceGuard(unittest.TestCase): with patch.dict(os.environ, denied_env, clear=True): result = srv.gitea_create_issue_comment( issue_number=557, - body="canonical evidence", + body="evidence comment", remote="prgs", worktree_path=valid_worktree, ) diff --git a/tests/test_lock_issue_mcp_registration.py b/tests/test_lock_issue_mcp_registration.py index 4b20763..0eca637 100644 --- a/tests/test_lock_issue_mcp_registration.py +++ b/tests/test_lock_issue_mcp_registration.py @@ -97,7 +97,7 @@ class TestCreatePrLockRegistrationFlow(unittest.TestCase): "mcp_server.issue_lock_worktree.read_worktree_git_state", return_value=_clean_master_git_state_for_lock(), ) - @patch("mcp_server.api_get_all", return_value=[]) + @patch("mcp_server.api_get_all", return_value=[{"id": 4, "name": "status:pr-open"}]) @patch("mcp_server.role_session_router.check_author_mutation_after_reviewer_stop", return_value=(True, [])) @patch("mcp_server.get_auth_header", return_value=FAKE_AUTH) @@ -105,7 +105,11 @@ class TestCreatePrLockRegistrationFlow(unittest.TestCase): self, _auth, _role, _api, _git_state, mock_api_request ): worktree = os.path.realpath(os.getcwd()) - mock_api_request.return_value = {"number": 521, "html_url": "https://example/pr/521"} + def mock_api(method, url, auth, data=None): + if "/labels" in url: + return [{"name": "status:pr-open"}] + return {"number": 521, "html_url": "https://example/pr/521"} + mock_api_request.side_effect = mock_api lock_res = gitea_lock_issue( issue_number=521, branch_name="feat/issue-521-lock-issue-registration", diff --git a/tests/test_mcp_server.py b/tests/test_mcp_server.py index 80246bb..cfdbf7a 100644 --- a/tests/test_mcp_server.py +++ b/tests/test_mcp_server.py @@ -4208,7 +4208,7 @@ class TestIssue546Deadlock(unittest.TestCase): res = mcp_server.gitea_submit_pr_review( pr_number=550, action="approve", - body="APPROVED", + body="", expected_head_sha="abc123", final_review_decision_ready=True, remote="prgs", diff --git a/tests/test_namespace_workspace_binding.py b/tests/test_namespace_workspace_binding.py index b9c270c..91364e7 100644 --- a/tests/test_namespace_workspace_binding.py +++ b/tests/test_namespace_workspace_binding.py @@ -103,6 +103,8 @@ class TestNamespaceWorkspaceIntegration(unittest.TestCase): srv._preflight_in_test_mode = lambda: False self._env_patch = mock.patch.dict(os.environ, {"GITEA_TEST_PORCELAIN": ""}, clear=False) self._env_patch.start() + self._guard_patch = mock.patch("gitea_mcp_server._enforce_root_checkout_guard") + self._guard_patch.start() def tearDown(self): srv._preflight_whoami_called = self._saved["whoami_called"] @@ -112,6 +114,7 @@ class TestNamespaceWorkspaceIntegration(unittest.TestCase): srv._preflight_capability_violation = self._saved["capability_violation"] srv._preflight_in_test_mode = self._saved["in_test"] self._env_patch.stop() + self._guard_patch.stop() for key in ( nwb.AUTHOR_WORKTREE_ENV, nwb.MERGER_WORKTREE_ENV, diff --git a/tests/test_permission_reports.py b/tests/test_permission_reports.py index b06e416..86f0bad 100644 --- a/tests/test_permission_reports.py +++ b/tests/test_permission_reports.py @@ -234,11 +234,10 @@ class TestEligibilityDenialReport(PermissionReportBase): return {"login": "author-user"} return PR_PAYLOAD mock_api.side_effect = fake_api - mcp_server.init_review_decision_lock("prgs", "review_pr") from tests.test_mcp_server import _seed_ready_review_decision - - _seed_ready_review_decision(42, "approve", remote="prgs") with patch.dict(os.environ, self._env("author-profile")): + mcp_server.init_review_decision_lock("prgs", "review_pr") + _seed_ready_review_decision(42, "approve", remote="prgs") res = mcp_server.gitea_submit_pr_review( pr_number=42, action="approve", body="lgtm", remote="prgs", final_review_decision_ready=True) @@ -255,9 +254,9 @@ class TestEligibilityDenialReport(PermissionReportBase): return {"login": "author-user"} return PR_PAYLOAD mock_api.side_effect = fake_api - mcp_server.init_review_decision_lock("prgs", "review_pr") - mcp_server.gitea_load_review_workflow() with patch.dict(os.environ, self._env("author-profile")): + mcp_server.init_review_decision_lock("prgs", "review_pr") + mcp_server.gitea_load_review_workflow() res = mcp_server.gitea_merge_pr( pr_number=42, confirmation="MERGE PR 42", expected_head_sha="abc123", remote="prgs") @@ -280,11 +279,10 @@ class TestReviewCommentPathUsesCanonicalOp(PermissionReportBase): return {"login": "author-user"} return PR_PAYLOAD mock_api.side_effect = fake_api - mcp_server.init_review_decision_lock("prgs", "review_pr") from tests.test_mcp_server import _seed_ready_review_decision - - _seed_ready_review_decision(42, "comment", remote="prgs") with patch.dict(os.environ, self._env("author-profile")): + mcp_server.init_review_decision_lock("prgs", "review_pr") + _seed_ready_review_decision(42, "comment", remote="prgs") res = mcp_server.gitea_submit_pr_review( pr_number=42, action="comment", body="finding", remote="prgs", final_review_decision_ready=True) diff --git a/tests/test_reconciler_close_workspace_guard.py b/tests/test_reconciler_close_workspace_guard.py index 54a24e4..f8d8951 100644 --- a/tests/test_reconciler_close_workspace_guard.py +++ b/tests/test_reconciler_close_workspace_guard.py @@ -35,9 +35,12 @@ class TestReconcilerCloseWorkspaceGuard(unittest.TestCase): srv._preflight_capability_violation = False self._orig_in_test = srv._preflight_in_test_mode srv._preflight_in_test_mode = lambda: False + self._env_patch = patch.dict(os.environ, {"GITEA_MCP_DISABLE_PARITY_GATE": "1"}, clear=False) + self._env_patch.start() def tearDown(self): srv._preflight_in_test_mode = self._orig_in_test + self._env_patch.stop() @patch("gitea_mcp_server._auth", return_value=FAKE_AUTH) @patch("gitea_mcp_server._namespace_mutation_block", return_value=None) diff --git a/tests/test_review_final_report_schema.py b/tests/test_review_final_report_schema.py index 3b2211e..f81e3ba 100644 --- a/tests/test_review_final_report_schema.py +++ b/tests/test_review_final_report_schema.py @@ -35,6 +35,9 @@ def _minimal_review_report(**overrides): "- External-state mutations: none", "- Read-only diagnostics: gitea_view_pr, gitea_view_issue", "- Current status: PR open", + "- Next actor: author", + "- Next action: Fix", + "- Next prompt: Fix", "- Blockers: none", "- Next: await author", "- Safety: no self-review; no self-merge", @@ -43,6 +46,8 @@ def _minimal_review_report(**overrides): "- Candidate head SHA: 0fdc8f582026b72a229d59a172c0a63ac4aaeaf9", "- Worktree path: branches/review-203", "- Worktree dirty: clean", + "- Pinned reviewed head: none", + "- Scratch worktree used: none", "- Unrelated local mutations: none", "- Review decision: request_changes", "- Merge result: not attempted",