From 538d6d964a8395be633b2c1ee4bd40e968cd36cc Mon Sep 17 00:00:00 2001 From: latte Date: Mon, 22 Jun 2026 15:58:59 +0200 Subject: [PATCH] test: cover write-tool auth and backend error branches Every write tool's `except (Auth...)` re-raise and `except GiteaError -> RuntimeError` wrapping was previously untested, leaving write_tools at 60% coverage and the repo below the 80% gate. Adds parametrized error-path tests for all 15 write tools (backend error wrapping + auth propagation), raising write_tools coverage to 99% and total coverage above the gate. --- tests/test_tools_extended.py | 61 +++++++++++++++++++++++++++++++++++- 1 file changed, 60 insertions(+), 1 deletion(-) diff --git a/tests/test_tools_extended.py b/tests/test_tools_extended.py index 5749c75..0a97d70 100644 --- a/tests/test_tools_extended.py +++ b/tests/test_tools_extended.py @@ -3,7 +3,7 @@ import pytest from aegis_gitea_mcp.config import reset_settings -from aegis_gitea_mcp.gitea_client import GiteaError +from aegis_gitea_mcp.gitea_client import GiteaAuthenticationError, GiteaError from aegis_gitea_mcp.tools.read_tools import ( compare_refs_tool, get_branch_tool, @@ -402,3 +402,62 @@ def test_create_label_args_reject_invalid_color() -> None: with pytest.raises(pydantic.ValidationError): CreateLabelArgs(owner="o", repo="r", name="bug", color="red") + + +# (tool, valid_args) for every write tool, used to exercise error branches. +WRITE_TOOL_ERROR_CASES = [ + (create_issue_tool, {"owner": "acme", "repo": "app", "title": "Issue"}), + (update_issue_tool, {"owner": "acme", "repo": "app", "issue_number": 1, "title": "x"}), + (create_issue_comment_tool, {"owner": "acme", "repo": "app", "issue_number": 1, "body": "c"}), + (create_pr_comment_tool, {"owner": "acme", "repo": "app", "pull_number": 1, "body": "c"}), + (add_labels_tool, {"owner": "acme", "repo": "app", "issue_number": 1, "labels": ["bug"]}), + (assign_issue_tool, {"owner": "acme", "repo": "app", "issue_number": 1, "assignees": ["al"]}), + (create_label_tool, {"owner": "acme", "repo": "app", "name": "bug", "color": "#ff0000"}), + (update_label_tool, {"owner": "acme", "repo": "app", "name": "bug", "new_name": "defect"}), + (remove_labels_tool, {"owner": "acme", "repo": "app", "issue_number": 1, "labels": ["bug"]}), + ( + create_pull_request_tool, + {"owner": "acme", "repo": "app", "title": "PR", "head": "feature", "base": "main"}, + ), + (create_release_tool, {"owner": "acme", "repo": "app", "tag_name": "v1.0.0"}), + (edit_release_tool, {"owner": "acme", "repo": "app", "release_id": 3, "name": "x"}), + (create_branch_tool, {"owner": "acme", "repo": "app", "new_branch_name": "feature/x"}), + (create_milestone_tool, {"owner": "acme", "repo": "app", "title": "M1"}), + (edit_issue_comment_tool, {"owner": "acme", "repo": "app", "comment_id": 5, "body": "e"}), +] + + +class _WriteBackendErrorGitea: + """Stub whose every method raises a generic Gitea backend error.""" + + def __getattr__(self, name: str): + async def _raise(*args: object, **kwargs: object) -> object: + raise GiteaError("backend failure") + + return _raise + + +class _WriteAuthErrorGitea: + """Stub whose every method raises an authentication error.""" + + def __getattr__(self, name: str): + async def _raise(*args: object, **kwargs: object) -> object: + raise GiteaAuthenticationError("token expired") + + return _raise + + +@pytest.mark.asyncio +@pytest.mark.parametrize("tool,args", WRITE_TOOL_ERROR_CASES) +async def test_write_tools_wrap_backend_errors(tool, args) -> None: + """Every write tool wraps a backend GiteaError as a RuntimeError.""" + with pytest.raises(RuntimeError): + await tool(_WriteBackendErrorGitea(), args) + + +@pytest.mark.asyncio +@pytest.mark.parametrize("tool,args", WRITE_TOOL_ERROR_CASES) +async def test_write_tools_propagate_auth_errors(tool, args) -> None: + """Every write tool lets auth failures surface for re-authorization.""" + with pytest.raises(GiteaAuthenticationError): + await tool(_WriteAuthErrorGitea(), args)