import base64 import json import pytest import httpx import respx from 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"