Files
a-limasov-ii db5a43b19a refactor: make gitea a self-contained Cline skill
Relocate the script into the skill (skills/gitea/scripts/gitea_mcp.py) and add a
PEP 723 dependency block, so `uv run scripts/gitea_mcp.py` provisions deps with no
install step and the skill folder is portable (drop into ~/.cline/skills/). Drop
the non-standard `metadata` frontmatter — Cline only recognizes name + description.

- .mcp.json points at the relocated script and runs with --no-project (PEP 723)
- SKILL.md uses relative scripts/ paths; metadata block removed
- find_config walks up the tree to locate config.json (skill root or plugin root)
- tests import gitea_mcp via pytest pythonpath; README/CLAUDE paths updated

Tests: 119 passed, 92% coverage. Standalone list-tools verified via uv --no-project.
2026-06-24 14:35:28 +03:00

90 lines
2.7 KiB
Python

import json
import pytest
import httpx
import respx
from gitea_mcp import list_branches, get_branch, create_branch, delete_branch, list_tags
BASE = "https://gitea.test/api/v1"
@respx.mock
def test_list_branches():
route = respx.get(f"{BASE}/repos/owner/repo/branches").mock(
return_value=httpx.Response(200, json=[{"name": "main"}])
)
result = list_branches("owner", "repo")
assert route.called
assert result[0]["name"] == "main"
assert dict(route.calls[0].request.url.params)["limit"] == "50"
@respx.mock
def test_get_branch():
route = respx.get(f"{BASE}/repos/owner/repo/branches/main").mock(
return_value=httpx.Response(200, json={"name": "main"})
)
result = get_branch("owner", "repo", "main")
assert route.called
assert result["name"] == "main"
@respx.mock
def test_get_branch_with_slash_in_name():
route = respx.get(f"{BASE}/repos/owner/repo/branches/feature%2Ffoo").mock(
return_value=httpx.Response(200, json={"name": "feature/foo"})
)
result = get_branch("owner", "repo", "feature/foo")
assert route.called
@respx.mock
def test_get_branch_not_found():
respx.get(f"{BASE}/repos/owner/repo/branches/missing").mock(
return_value=httpx.Response(404, json={"message": "not found"})
)
with pytest.raises(RuntimeError, match="404"):
get_branch("owner", "repo", "missing")
@respx.mock
def test_create_branch():
route = respx.post(f"{BASE}/repos/owner/repo/branches").mock(
return_value=httpx.Response(201, json={"name": "feature/new"})
)
result = create_branch("owner", "repo", "feature/new")
assert route.called
body = json.loads(route.calls[0].request.content)
assert body["new_branch_name"] == "feature/new"
assert "old_branch_name" not in body
@respx.mock
def test_create_branch_from_specific():
route = respx.post(f"{BASE}/repos/owner/repo/branches").mock(
return_value=httpx.Response(201, json={"name": "hotfix"})
)
create_branch("owner", "repo", "hotfix", old_branch_name="main")
body = json.loads(route.calls[0].request.content)
assert body["old_branch_name"] == "main"
@respx.mock
def test_delete_branch():
route = respx.delete(f"{BASE}/repos/owner/repo/branches/old-branch").mock(
return_value=httpx.Response(204)
)
result = delete_branch("owner", "repo", "old-branch")
assert route.called
assert result == {"success": True}
@respx.mock
def test_list_tags():
route = respx.get(f"{BASE}/repos/owner/repo/tags").mock(
return_value=httpx.Response(200, json=[{"name": "v1.0.0"}])
)
result = list_tags("owner", "repo")
assert route.called
assert result[0]["name"] == "v1.0.0"
assert dict(route.calls[0].request.url.params)["limit"] == "20"