From 7bcdd44fe599dfa7e89ec27b674a4698b660d13c Mon Sep 17 00:00:00 2001 From: Jason Walker <913443@dadeschools.net> Date: Thu, 2 Jul 2026 03:08:39 -0400 Subject: [PATCH] fix: validate gitea_edit_pr no-fields before authentication (#43) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 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) --- mcp_server.py | 10 +++++++--- tests/test_mcp_server.py | 11 +++++++++++ 2 files changed, 18 insertions(+), 3 deletions(-) diff --git a/mcp_server.py b/mcp_server.py index 8f33c43..ae7e711 100644 --- a/mcp_server.py +++ b/mcp_server.py @@ -720,9 +720,9 @@ def gitea_edit_pr( Returns: dict with success status and details of the edited PR. """ - h, o, r = _resolve(remote, host, org, repo) - auth = _auth(h) - url = f"{repo_api_url(h, o, r)}/pulls/{pr_number}" + # Validate inputs BEFORE any auth/profile resolution or API setup: a + # no-fields call is a pure validation error and must not depend on + # credentials, network, or environment configuration. payload = {} if title is not None: payload["title"] = title @@ -736,6 +736,10 @@ def gitea_edit_pr( if not payload: 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, pr_number=pr_number, request_metadata={"fields": sorted(payload)}): data = api_request("PATCH", url, auth, payload) diff --git a/tests/test_mcp_server.py b/tests/test_mcp_server.py index b62c2d3..85a47d9 100644 --- a/tests/test_mcp_server.py +++ b/tests/test_mcp_server.py @@ -737,6 +737,17 @@ class TestEditPR(unittest.TestCase): with self.assertRaises(ValueError): 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