test: add 106 unit + smoke tests, 93% coverage

- pytest + respx + pytest-cov added as dev dependencies
- tests/smoke/test_registration.py: verify all 66 tools registered and unique
- tests/unit/: 12 files covering every tool — HTTP method, URL, params/body, errors
- tests/config.test.json + conftest.py: zero-network test setup via GITEA_CONFIG
- CLAUDE.md: developer guide with testing philosophy, pyramid, and commands
- server/gitea_mcp.py: get_commit, list_pr_files tools added
- README.md: tool count corrected to 66
This commit is contained in:
2026-06-08 09:09:44 +03:00
parent 50cd8e85e6
commit a6162a855c
23 changed files with 1722 additions and 4 deletions
+79
View File
@@ -0,0 +1,79 @@
import json
import pytest
import httpx
import respx
from server.gitea_mcp import list_releases, get_latest_release, create_release, delete_release
BASE = "https://gitea.test/api/v1"
@respx.mock
def test_list_releases():
route = respx.get(f"{BASE}/repos/owner/repo/releases").mock(
return_value=httpx.Response(200, json=[{"id": 1, "tag_name": "v1.0.0"}])
)
result = list_releases("owner", "repo")
assert route.called
assert result[0]["tag_name"] == "v1.0.0"
assert dict(route.calls[0].request.url.params)["limit"] == "10"
@respx.mock
def test_get_latest_release():
route = respx.get(f"{BASE}/repos/owner/repo/releases/latest").mock(
return_value=httpx.Response(200, json={"id": 3, "tag_name": "v2.0.0"})
)
result = get_latest_release("owner", "repo")
assert route.called
assert result["tag_name"] == "v2.0.0"
@respx.mock
def test_get_latest_release_not_found():
respx.get(f"{BASE}/repos/owner/repo/releases/latest").mock(
return_value=httpx.Response(404, json={"message": "not found"})
)
with pytest.raises(RuntimeError, match="404"):
get_latest_release("owner", "repo")
@respx.mock
def test_create_release():
route = respx.post(f"{BASE}/repos/owner/repo/releases").mock(
return_value=httpx.Response(201, json={"id": 5, "tag_name": "v3.0.0"})
)
result = create_release("owner", "repo", "v3.0.0", "Release 3.0.0")
assert route.called
body = json.loads(route.calls[0].request.content)
assert body["tag_name"] == "v3.0.0"
assert body["name"] == "Release 3.0.0"
assert body["draft"] is False
assert body["prerelease"] is False
assert "body" not in body
@respx.mock
def test_create_release_draft_with_notes():
route = respx.post(f"{BASE}/repos/owner/repo/releases").mock(
return_value=httpx.Response(201, json={"id": 6})
)
create_release(
"owner", "repo", "v4.0.0-beta", "Beta release",
body="## Changes\n- new feature", draft=True, prerelease=True,
target_commitish="develop",
)
body = json.loads(route.calls[0].request.content)
assert body["body"] == "## Changes\n- new feature"
assert body["draft"] is True
assert body["prerelease"] is True
assert body["target_commitish"] == "develop"
@respx.mock
def test_delete_release():
route = respx.delete(f"{BASE}/repos/owner/repo/releases/5").mock(
return_value=httpx.Response(204)
)
result = delete_release("owner", "repo", 5)
assert route.called
assert result == {"success": True}