a6162a855c
- 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
109 lines
3.7 KiB
Python
109 lines
3.7 KiB
Python
import base64
|
|
import json
|
|
import pytest
|
|
import httpx
|
|
import respx
|
|
from server.gitea_mcp import list_directory, get_file, create_file, update_file, delete_file
|
|
|
|
BASE = "https://gitea.test/api/v1"
|
|
|
|
|
|
@respx.mock
|
|
def test_list_directory_root():
|
|
route = respx.get(f"{BASE}/repos/owner/repo/contents/").mock(
|
|
return_value=httpx.Response(200, json=[{"name": "README.md", "type": "file"}])
|
|
)
|
|
result = list_directory("owner", "repo")
|
|
assert route.called
|
|
assert result[0]["name"] == "README.md"
|
|
|
|
|
|
@respx.mock
|
|
def test_list_directory_with_ref():
|
|
route = respx.get(f"{BASE}/repos/owner/repo/contents/src").mock(
|
|
return_value=httpx.Response(200, json=[])
|
|
)
|
|
list_directory("owner", "repo", path="src", ref="develop")
|
|
params = dict(route.calls[0].request.url.params)
|
|
assert params["ref"] == "develop"
|
|
|
|
|
|
@respx.mock
|
|
def test_get_file():
|
|
encoded = base64.b64encode(b"hello world").decode()
|
|
route = respx.get(f"{BASE}/repos/owner/repo/contents/README.md").mock(
|
|
return_value=httpx.Response(200, json={"name": "README.md", "content": encoded, "sha": "abc"})
|
|
)
|
|
result = get_file("owner", "repo", "README.md")
|
|
assert route.called
|
|
assert result["content"] == encoded
|
|
assert result["sha"] == "abc"
|
|
|
|
|
|
@respx.mock
|
|
def test_get_file_with_ref():
|
|
route = respx.get(f"{BASE}/repos/owner/repo/contents/README.md").mock(
|
|
return_value=httpx.Response(200, json={"name": "README.md"})
|
|
)
|
|
get_file("owner", "repo", "README.md", ref="v1.0")
|
|
params = dict(route.calls[0].request.url.params)
|
|
assert params["ref"] == "v1.0"
|
|
|
|
|
|
@respx.mock
|
|
def test_get_file_not_found():
|
|
respx.get(f"{BASE}/repos/owner/repo/contents/missing.txt").mock(
|
|
return_value=httpx.Response(404, json={"message": "not found"})
|
|
)
|
|
with pytest.raises(RuntimeError, match="404"):
|
|
get_file("owner", "repo", "missing.txt")
|
|
|
|
|
|
@respx.mock
|
|
def test_create_file():
|
|
route = respx.post(f"{BASE}/repos/owner/repo/contents/hello.txt").mock(
|
|
return_value=httpx.Response(201, json={"content": {"name": "hello.txt"}})
|
|
)
|
|
result = create_file("owner", "repo", "hello.txt", "hello world", "add hello")
|
|
assert route.called
|
|
body = json.loads(route.calls[0].request.content)
|
|
assert body["content"] == base64.b64encode(b"hello world").decode()
|
|
assert body["message"] == "add hello"
|
|
assert "branch" not in body
|
|
|
|
|
|
@respx.mock
|
|
def test_create_file_on_branch():
|
|
route = respx.post(f"{BASE}/repos/owner/repo/contents/file.py").mock(
|
|
return_value=httpx.Response(201, json={"content": {"name": "file.py"}})
|
|
)
|
|
create_file("owner", "repo", "file.py", "print('hi')", "add file", branch="develop")
|
|
body = json.loads(route.calls[0].request.content)
|
|
assert body["branch"] == "develop"
|
|
|
|
|
|
@respx.mock
|
|
def test_update_file():
|
|
new_content = "updated content"
|
|
route = respx.put(f"{BASE}/repos/owner/repo/contents/README.md").mock(
|
|
return_value=httpx.Response(200, json={"content": {"name": "README.md"}})
|
|
)
|
|
update_file("owner", "repo", "README.md", new_content, "update readme", sha="deadbeef")
|
|
assert route.called
|
|
body = json.loads(route.calls[0].request.content)
|
|
assert body["content"] == base64.b64encode(new_content.encode()).decode()
|
|
assert body["sha"] == "deadbeef"
|
|
assert body["message"] == "update readme"
|
|
|
|
|
|
@respx.mock
|
|
def test_delete_file():
|
|
route = respx.delete(f"{BASE}/repos/owner/repo/contents/old.txt").mock(
|
|
return_value=httpx.Response(200, json={"commit": {"sha": "abc"}})
|
|
)
|
|
result = delete_file("owner", "repo", "old.txt", "remove old file", sha="abc123")
|
|
assert route.called
|
|
body = json.loads(route.calls[0].request.content)
|
|
assert body["message"] == "remove old file"
|
|
assert body["sha"] == "abc123"
|