fix: validate gitea_edit_pr no-fields before authentication (#43)

gitea_edit_pr called _auth() (and resolved the remote) before checking whether
any editable field was provided, so a pure validation error (no fields) surfaced
as a RuntimeError "no credentials" in environments without Gitea auth — making
test_edit_pr_no_fields_raises depend on credentials/network/env.

Move the payload build + no-fields ValueError ahead of _resolve/_auth/URL setup.
Behavior is unchanged when fields are provided (same _resolve → _auth → audited
PATCH path). No change to auth, retry/backoff, audit, config profiles, or
worktree helpers.

Tests: add test_edit_pr_no_fields_validates_before_auth asserting the no-fields
path raises ValueError and calls neither get_auth_header nor api_request (even
with auth mocked to None). Existing edit-PR tests unchanged.

Full suite passes with no Gitea credentials (287 passed, 0 failures) — the
no-fields test no longer depends on the environment.

Closes #43.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
2026-07-02 03:08:39 -04:00
parent 46db3f73e8
commit 7bcdd44fe5
2 changed files with 18 additions and 3 deletions
+7 -3
View File
@@ -720,9 +720,9 @@ def gitea_edit_pr(
Returns: Returns:
dict with success status and details of the edited PR. dict with success status and details of the edited PR.
""" """
h, o, r = _resolve(remote, host, org, repo) # Validate inputs BEFORE any auth/profile resolution or API setup: a
auth = _auth(h) # no-fields call is a pure validation error and must not depend on
url = f"{repo_api_url(h, o, r)}/pulls/{pr_number}" # credentials, network, or environment configuration.
payload = {} payload = {}
if title is not None: if title is not None:
payload["title"] = title payload["title"] = title
@@ -736,6 +736,10 @@ def gitea_edit_pr(
if not payload: if not payload:
raise ValueError("At least one field to edit (title, body, state, base) must be provided.") raise ValueError("At least one field to edit (title, body, state, base) must be provided.")
h, o, r = _resolve(remote, host, org, repo)
auth = _auth(h)
url = f"{repo_api_url(h, o, r)}/pulls/{pr_number}"
with _audited("edit_pr", host=h, remote=remote, org=o, repo=r, with _audited("edit_pr", host=h, remote=remote, org=o, repo=r,
pr_number=pr_number, request_metadata={"fields": sorted(payload)}): pr_number=pr_number, request_metadata={"fields": sorted(payload)}):
data = api_request("PATCH", url, auth, payload) data = api_request("PATCH", url, auth, payload)
+11
View File
@@ -737,6 +737,17 @@ class TestEditPR(unittest.TestCase):
with self.assertRaises(ValueError): with self.assertRaises(ValueError):
gitea_edit_pr(pr_number=1) gitea_edit_pr(pr_number=1)
@patch("mcp_server.api_request")
@patch("mcp_server.get_auth_header")
def test_edit_pr_no_fields_validates_before_auth(self, mock_auth, mock_api):
# No-fields validation must not depend on credentials/network: it raises
# ValueError before touching auth or the API, even with no creds.
mock_auth.return_value = None # simulate an unauthenticated environment
with self.assertRaises(ValueError):
gitea_edit_pr(pr_number=1)
mock_auth.assert_not_called()
mock_api.assert_not_called()
# --------------------------------------------------------------------------- # ---------------------------------------------------------------------------
# Get File # Get File