"""Tests for reconciliation MCP assessment tools (#301).""" import sys import unittest from unittest.mock import patch sys.path.insert(0, str(__import__("pathlib").Path(__file__).resolve().parent.parent)) import mcp_server from mcp_server import ( gitea_assess_already_landed_reconciliation, gitea_scan_already_landed_open_prs, ) AUTHOR_PROFILE = { "profile_name": "prgs-author", "allowed_operations": [ "gitea.read", "gitea.pr.comment", "gitea.issue.comment", "gitea.issue.close", ], "forbidden_operations": ["gitea.pr.close"], "audit_label": "prgs-author", } OPEN_PR = { "number": 278, "title": "Landed (Closes #263)", "body": "", "state": "open", "head": {"ref": "feat/x", "sha": "a" * 40}, "base": {"ref": "master"}, } class TestReconciliationMcpTools(unittest.TestCase): def setUp(self): self.mock_api = patch("mcp_server.api_request").start() self.mock_auth = patch( "mcp_server.get_auth_header", return_value="token test" ).start() patch("mcp_server.get_profile", return_value=AUTHOR_PROFILE).start() mcp_server._IDENTITY_CACHE.clear() def tearDown(self): patch.stopall() mcp_server._IDENTITY_CACHE.clear() def test_assess_returns_plan_without_mutations(self): self.mock_api.return_value = OPEN_PR with patch( "mcp_server.reconciliation_workflow.assess_open_pr_reconciliation", return_value={ "reconciliation_allowed": True, "eligibility_class": "ALREADY_LANDED_RECONCILE_REQUIRED", "candidate_head_sha": OPEN_PR["head"]["sha"], "target_branch_sha": "deadbeef", "linked_issue": 263, "review_merge_allowed": False, }, ): res = gitea_assess_already_landed_reconciliation( pr_number=278, remote="prgs" ) self.assertTrue(res["success"]) self.assertFalse(res["performed"]) self.assertEqual(res["plan"]["outcome"], "PARTIAL_RECONCILE_COMMENT_THEN_STOP") self.assertFalse(res["review_merge_allowed"]) patch_calls = [ c for c in self.mock_api.call_args_list if c.args[0] == "PATCH" ] self.assertEqual(patch_calls, []) def test_scan_returns_candidates_with_pagination(self): with patch( "mcp_server.api_fetch_page", return_value=([OPEN_PR], { "page": 1, "per_page": 50, "is_final_page": True, "has_more": False, "next_page": None, }), ), patch( "mcp_server.reconciliation_workflow.fetch_target_branch", return_value={ "success": True, "target_branch_sha": "deadbeef", "git_fetch_command": "git fetch prgs master", }, ), patch( "mcp_server.reconciliation_workflow.assess_open_pr_reconciliation", return_value={ "pr_number": 278, "eligibility_class": "ALREADY_LANDED_RECONCILE_REQUIRED", "reconciliation_allowed": True, }, ): res = gitea_scan_already_landed_open_prs(remote="prgs", limit=50) self.assertTrue(res["success"]) self.assertEqual(res["candidate_count"], 1) self.assertTrue(res["pagination"]["inventory_complete"]) if __name__ == "__main__": unittest.main()