Resolve PR via pulls API before fetching lease comments so gitea_assess_conflict_fix_push fails closed with actionable reasons instead of surfacing HTTP 500 when Gitea returns not-found errors. Add regression tests and document assessment-failure handoff in work-issue.
139 lines
5.1 KiB
Python
139 lines
5.1 KiB
Python
"""Regression tests for gitea_assess_conflict_fix_push structured failures (#519)."""
|
|
|
|
import os
|
|
import sys
|
|
import unittest
|
|
from unittest.mock import patch
|
|
|
|
sys.path.insert(0, str(__import__("pathlib").Path(__file__).resolve().parent.parent))
|
|
|
|
from mcp_server import ( # noqa: E402
|
|
_fetch_pr_lease_comments_safe,
|
|
gitea_assess_conflict_fix_push,
|
|
)
|
|
|
|
FAKE_AUTH = "Basic dGVzdDp0ZXN0"
|
|
AUTHOR_ENV = {
|
|
"GITEA_PROFILE_NAME": "prgs-author",
|
|
"GITEA_ALLOWED_OPERATIONS": "gitea.read,gitea.branch.push,gitea.pr.create",
|
|
}
|
|
|
|
HEAD_BEFORE = "dad1dc8d5108ab01ed83065334116d7425a4471c"
|
|
HEAD_AFTER = "3f3d6cb35d0fe225dcb236247f2a2d0ec193fa35"
|
|
OPEN_PR = {
|
|
"number": 508,
|
|
"state": "open",
|
|
"head": {"sha": HEAD_AFTER},
|
|
}
|
|
|
|
|
|
class TestFetchPrLeaseCommentsSafe(unittest.TestCase):
|
|
@patch("mcp_server.api_request")
|
|
@patch("mcp_server.get_auth_header", return_value=FAKE_AUTH)
|
|
def test_pr_lookup_404_returns_structured_failure(self, _auth, mock_api):
|
|
mock_api.side_effect = RuntimeError(
|
|
'HTTP 404: {"message":"issue does not exist","index":508}'
|
|
)
|
|
result = _fetch_pr_lease_comments_safe(
|
|
508, remote="prgs", host=None, org=None, repo=None)
|
|
self.assertFalse(result["success"])
|
|
self.assertEqual(result["comments"], [])
|
|
self.assertTrue(result["reasons"])
|
|
self.assertIn("PR lookup failed", result["reasons"][0])
|
|
self.assertEqual(result["pr_lookup"], "failed")
|
|
|
|
@patch("mcp_server.api_request")
|
|
@patch("mcp_server.get_auth_header", return_value=FAKE_AUTH)
|
|
def test_comment_fetch_404_after_pr_lookup(self, _auth, mock_api):
|
|
def _api(method, url, auth, *args, **kwargs):
|
|
if "/pulls/" in url:
|
|
return OPEN_PR
|
|
raise RuntimeError(
|
|
'HTTP 404: {"message":"issue does not exist","index":508}'
|
|
)
|
|
|
|
mock_api.side_effect = _api
|
|
result = _fetch_pr_lease_comments_safe(
|
|
508, remote="prgs", host=None, org=None, repo=None)
|
|
self.assertFalse(result["success"])
|
|
self.assertIn("comment fetch failed", result["reasons"][0].lower())
|
|
self.assertEqual(result["pr_lookup"], "ok")
|
|
|
|
@patch("mcp_server.api_request")
|
|
@patch("mcp_server.get_auth_header", return_value=FAKE_AUTH)
|
|
def test_success_returns_comments_and_head_sha(self, _auth, mock_api):
|
|
comment = {"id": 1, "body": "<!-- mcp-conflict-fix-lease:v1 -->"}
|
|
|
|
def _api(method, url, auth, *args, **kwargs):
|
|
if "/pulls/" in url:
|
|
return OPEN_PR
|
|
return [comment]
|
|
|
|
mock_api.side_effect = _api
|
|
result = _fetch_pr_lease_comments_safe(
|
|
508, remote="prgs", host=None, org=None, repo=None)
|
|
self.assertTrue(result["success"])
|
|
self.assertEqual(result["comments"], [comment])
|
|
self.assertEqual(result["head_sha"], OPEN_PR["head"]["sha"])
|
|
|
|
|
|
class TestAssessConflictFixPushTool(unittest.TestCase):
|
|
@patch("mcp_server._fetch_pr_lease_comments_safe")
|
|
@patch("mcp_server._profile_operation_gate", return_value=None)
|
|
def test_returns_structured_block_when_pr_lookup_fails(
|
|
self, _gate, mock_fetch,
|
|
):
|
|
mock_fetch.return_value = {
|
|
"success": False,
|
|
"comments": [],
|
|
"reasons": ["PR lookup failed"],
|
|
"pr_lookup": "failed",
|
|
"resolved_repo": "Scaled-Tech-Consulting/Gitea-Tools",
|
|
"remote": "prgs",
|
|
"pr_number": 508,
|
|
}
|
|
with patch.dict(os.environ, AUTHOR_ENV, clear=True):
|
|
result = gitea_assess_conflict_fix_push(
|
|
pr_number=508,
|
|
branch_head_before=HEAD_BEFORE,
|
|
branch_head_after=HEAD_AFTER,
|
|
worktree_path="/proj/branches/fix-pr508",
|
|
push_cwd="/proj/branches/fix-pr508",
|
|
is_fast_forward=True,
|
|
remote="prgs",
|
|
)
|
|
self.assertFalse(result["push_allowed"])
|
|
self.assertTrue(result.get("assessment_failed"))
|
|
self.assertEqual(result["pr_lookup"], "failed")
|
|
|
|
@patch("mcp_server._fetch_pr_lease_comments_safe")
|
|
@patch("mcp_server._profile_operation_gate", return_value=None)
|
|
def test_valid_push_assessment_when_pr_and_comments_resolve(
|
|
self, _gate, mock_fetch,
|
|
):
|
|
mock_fetch.return_value = {
|
|
"success": True,
|
|
"comments": [],
|
|
"reasons": [],
|
|
"pr_lookup": "ok",
|
|
"resolved_repo": "Scaled-Tech-Consulting/Gitea-Tools",
|
|
"remote": "prgs",
|
|
"pr_number": 508,
|
|
"head_sha": HEAD_AFTER,
|
|
}
|
|
with patch.dict(os.environ, AUTHOR_ENV, clear=True):
|
|
result = gitea_assess_conflict_fix_push(
|
|
pr_number=508,
|
|
branch_head_before=HEAD_BEFORE,
|
|
branch_head_after=HEAD_AFTER,
|
|
worktree_path="/proj/branches/fix-pr508",
|
|
push_cwd="/proj/branches/fix-pr508",
|
|
is_fast_forward=True,
|
|
remote="prgs",
|
|
)
|
|
self.assertTrue(result["push_allowed"])
|
|
self.assertEqual(result.get("live_pr_head_sha"), HEAD_AFTER)
|
|
|
|
|
|
if __name__ == "__main__":
|
|
unittest.main() |