db5a43b19a
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.
69 lines
2.2 KiB
Python
69 lines
2.2 KiB
Python
import json
|
|
import pytest
|
|
import httpx
|
|
import respx
|
|
from gitea_mcp import list_repo_webhooks, create_repo_webhook, delete_repo_webhook
|
|
|
|
BASE = "https://gitea.test/api/v1"
|
|
|
|
|
|
@respx.mock
|
|
def test_list_repo_webhooks():
|
|
route = respx.get(f"{BASE}/repos/owner/repo/hooks").mock(
|
|
return_value=httpx.Response(200, json=[{"id": 1, "type": "gitea"}])
|
|
)
|
|
result = list_repo_webhooks("owner", "repo")
|
|
assert route.called
|
|
assert result[0]["id"] == 1
|
|
|
|
|
|
@respx.mock
|
|
def test_create_repo_webhook_minimal():
|
|
route = respx.post(f"{BASE}/repos/owner/repo/hooks").mock(
|
|
return_value=httpx.Response(201, json={"id": 2})
|
|
)
|
|
create_repo_webhook("owner", "repo", "https://example.com/hook", ["push"])
|
|
assert route.called
|
|
body = json.loads(route.calls[0].request.content)
|
|
assert body["type"] == "gitea"
|
|
assert body["config"]["url"] == "https://example.com/hook"
|
|
assert body["config"]["content_type"] == "json"
|
|
assert "secret" not in body["config"]
|
|
assert body["events"] == ["push"]
|
|
assert body["active"] is True
|
|
|
|
|
|
@respx.mock
|
|
def test_create_repo_webhook_with_secret():
|
|
route = respx.post(f"{BASE}/repos/owner/repo/hooks").mock(
|
|
return_value=httpx.Response(201, json={"id": 3})
|
|
)
|
|
create_repo_webhook(
|
|
"owner", "repo", "https://ci.example.com/hook",
|
|
["push", "pull_request", "issues"],
|
|
secret="mysecret", active=False,
|
|
)
|
|
body = json.loads(route.calls[0].request.content)
|
|
assert body["config"]["secret"] == "mysecret"
|
|
assert body["events"] == ["push", "pull_request", "issues"]
|
|
assert body["active"] is False
|
|
|
|
|
|
@respx.mock
|
|
def test_delete_repo_webhook():
|
|
route = respx.delete(f"{BASE}/repos/owner/repo/hooks/7").mock(
|
|
return_value=httpx.Response(204)
|
|
)
|
|
result = delete_repo_webhook("owner", "repo", 7)
|
|
assert route.called
|
|
assert result == {"success": True}
|
|
|
|
|
|
@respx.mock
|
|
def test_create_repo_webhook_error():
|
|
respx.post(f"{BASE}/repos/owner/repo/hooks").mock(
|
|
return_value=httpx.Response(422, json={"message": "invalid url"})
|
|
)
|
|
with pytest.raises(RuntimeError, match="422"):
|
|
create_repo_webhook("owner", "repo", "not-a-url", ["push"])
|