import json import pytest import httpx import respx from gitea_mcp import ( list_issues, get_issue, create_issue, update_issue, close_issue, reopen_issue, list_issue_comments, add_issue_comment, edit_issue_comment, delete_issue_comment, ) BASE = "https://gitea.test/api/v1" @respx.mock def test_list_issues_defaults(): route = respx.get(f"{BASE}/repos/owner/repo/issues").mock( return_value=httpx.Response(200, json=[]) ) list_issues("owner", "repo") params = dict(route.calls[0].request.url.params) assert params["state"] == "open" assert params["type"] == "issues" assert params["limit"] == "20" @respx.mock def test_list_issues_with_filters(): route = respx.get(f"{BASE}/repos/owner/repo/issues").mock( return_value=httpx.Response(200, json=[]) ) list_issues("owner", "repo", state="closed", labels="bug", assignee="alice") params = dict(route.calls[0].request.url.params) assert params["state"] == "closed" assert params["labels"] == "bug" assert params["assignee"] == "alice" @respx.mock def test_get_issue(): route = respx.get(f"{BASE}/repos/owner/repo/issues/42").mock( return_value=httpx.Response(200, json={"number": 42, "title": "Bug"}) ) result = get_issue("owner", "repo", 42) assert route.called assert result["number"] == 42 @respx.mock def test_get_issue_not_found(): respx.get(f"{BASE}/repos/owner/repo/issues/999").mock( return_value=httpx.Response(404, json={"message": "not found"}) ) with pytest.raises(RuntimeError, match="404"): get_issue("owner", "repo", 999) @respx.mock def test_create_issue(): route = respx.post(f"{BASE}/repos/owner/repo/issues").mock( return_value=httpx.Response(201, json={"number": 1, "title": "New bug"}) ) result = create_issue("owner", "repo", "New bug", body="description") assert route.called body = json.loads(route.calls[0].request.content) assert body["title"] == "New bug" assert body["body"] == "description" @respx.mock def test_create_issue_with_assignees_and_labels(): route = respx.post(f"{BASE}/repos/owner/repo/issues").mock( return_value=httpx.Response(201, json={"number": 2}) ) create_issue("owner", "repo", "Task", assignees=["alice"], labels=[1, 2]) body = json.loads(route.calls[0].request.content) assert body["assignees"] == ["alice"] assert body["labels"] == [1, 2] @respx.mock def test_update_issue(): route = respx.patch(f"{BASE}/repos/owner/repo/issues/5").mock( return_value=httpx.Response(200, json={"number": 5}) ) update_issue("owner", "repo", 5, title="Updated title") body = json.loads(route.calls[0].request.content) assert body["title"] == "Updated title" assert "body" not in body @respx.mock def test_close_issue(): route = respx.patch(f"{BASE}/repos/owner/repo/issues/3").mock( return_value=httpx.Response(200, json={"state": "closed"}) ) result = close_issue("owner", "repo", 3) assert route.called body = json.loads(route.calls[0].request.content) assert body["state"] == "closed" @respx.mock def test_reopen_issue(): route = respx.patch(f"{BASE}/repos/owner/repo/issues/3").mock( return_value=httpx.Response(200, json={"state": "open"}) ) reopen_issue("owner", "repo", 3) body = json.loads(route.calls[0].request.content) assert body["state"] == "open" @respx.mock def test_list_issue_comments(): route = respx.get(f"{BASE}/repos/owner/repo/issues/7/comments").mock( return_value=httpx.Response(200, json=[{"id": 1, "body": "hi"}]) ) result = list_issue_comments("owner", "repo", 7) assert route.called assert result[0]["id"] == 1 @respx.mock def test_add_issue_comment(): route = respx.post(f"{BASE}/repos/owner/repo/issues/7/comments").mock( return_value=httpx.Response(201, json={"id": 10, "body": "LGTM"}) ) result = add_issue_comment("owner", "repo", 7, "LGTM") assert route.called body = json.loads(route.calls[0].request.content) assert body["body"] == "LGTM" @respx.mock def test_edit_issue_comment(): route = respx.patch(f"{BASE}/repos/owner/repo/issues/comments/42").mock( return_value=httpx.Response(200, json={"id": 42}) ) edit_issue_comment("owner", "repo", 42, "edited body") body = json.loads(route.calls[0].request.content) assert body["body"] == "edited body" @respx.mock def test_delete_issue_comment(): route = respx.delete(f"{BASE}/repos/owner/repo/issues/comments/42").mock( return_value=httpx.Response(204) ) result = delete_issue_comment("owner", "repo", 42) assert route.called assert result == {"success": True}