generated from personal-projects/leo-claude-mktplace
Added comprehensive test coverage for all tool modules: Test Files Created: - tests/conftest.py: Shared fixtures for all tests - tests/test_issues.py: Complete coverage for issue tools - tests/test_labels.py: Complete coverage for label tools - tests/test_milestones.py: Complete coverage for milestone tools Test Coverage: - Tool definition validation (schema structure) - Handler function routing - Successful API response formatting - Error handling (GiteaClientError) - Required parameter validation - Optional parameter handling - Mock Gitea API responses Configuration Updates: - Added pytest-cov>=4.0.0 to dev dependencies - Created run_tests.sh script for easy test execution All tests use pytest-asyncio for async functions and mock the GiteaClient to avoid real API calls. Tests validate tool schemas, handler routing, response formatting, and error handling. Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
257 lines
8.2 KiB
Python
257 lines
8.2 KiB
Python
"""Tests for label operations tools."""
|
|
|
|
import pytest
|
|
from mcp.types import Tool, TextContent
|
|
from gitea_mcp.tools.labels import (
|
|
get_label_tools,
|
|
handle_label_tool,
|
|
_list_labels,
|
|
_create_label,
|
|
)
|
|
from gitea_mcp.client import GiteaClientError
|
|
|
|
|
|
class TestLabelToolDefinitions:
|
|
"""Test label tool schema definitions."""
|
|
|
|
def test_get_label_tools_returns_list(self):
|
|
"""Test that get_label_tools returns a list of Tool objects."""
|
|
tools = get_label_tools()
|
|
assert isinstance(tools, list)
|
|
assert len(tools) == 2
|
|
assert all(isinstance(tool, Tool) for tool in tools)
|
|
|
|
def test_list_labels_tool_schema(self):
|
|
"""Test gitea_list_labels tool has correct schema."""
|
|
tools = get_label_tools()
|
|
list_tool = next(t for t in tools if t.name == "gitea_list_labels")
|
|
|
|
assert list_tool.name == "gitea_list_labels"
|
|
assert "list" in list_tool.description.lower()
|
|
assert "label" in list_tool.description.lower()
|
|
|
|
schema = list_tool.inputSchema
|
|
assert schema["type"] == "object"
|
|
assert set(schema["required"]) == {"owner", "repo"}
|
|
assert "owner" in schema["properties"]
|
|
assert "repo" in schema["properties"]
|
|
|
|
def test_create_label_tool_schema(self):
|
|
"""Test gitea_create_label tool has correct schema."""
|
|
tools = get_label_tools()
|
|
create_tool = next(t for t in tools if t.name == "gitea_create_label")
|
|
|
|
assert create_tool.name == "gitea_create_label"
|
|
assert "create" in create_tool.description.lower()
|
|
|
|
schema = create_tool.inputSchema
|
|
assert set(schema["required"]) == {"owner", "repo", "name", "color"}
|
|
assert "name" in schema["properties"]
|
|
assert "color" in schema["properties"]
|
|
assert "description" in schema["properties"]
|
|
assert "hex" in schema["properties"]["color"]["description"].lower()
|
|
|
|
|
|
class TestListLabels:
|
|
"""Test _list_labels function."""
|
|
|
|
@pytest.mark.asyncio
|
|
async def test_list_labels_success(self, mock_client, sample_label):
|
|
"""Test successful label listing."""
|
|
additional_label = {
|
|
"id": 2,
|
|
"name": "enhancement",
|
|
"color": "00ff00",
|
|
"description": "New feature or request",
|
|
}
|
|
mock_client.get.return_value = [sample_label, additional_label]
|
|
|
|
arguments = {
|
|
"owner": "testowner",
|
|
"repo": "testrepo",
|
|
}
|
|
|
|
result = await _list_labels(mock_client, arguments)
|
|
|
|
assert isinstance(result, list)
|
|
assert len(result) == 1
|
|
assert isinstance(result[0], TextContent)
|
|
assert "bug" in result[0].text
|
|
assert "enhancement" in result[0].text
|
|
assert "#ff0000" in result[0].text
|
|
assert "#00ff00" in result[0].text
|
|
assert "Something isn't working" in result[0].text
|
|
assert "New feature" in result[0].text
|
|
|
|
mock_client.get.assert_called_once_with("/repos/testowner/testrepo/labels")
|
|
|
|
@pytest.mark.asyncio
|
|
async def test_list_labels_empty(self, mock_client):
|
|
"""Test listing when no labels found."""
|
|
mock_client.get.return_value = []
|
|
|
|
arguments = {
|
|
"owner": "testowner",
|
|
"repo": "testrepo",
|
|
}
|
|
|
|
result = await _list_labels(mock_client, arguments)
|
|
|
|
assert len(result) == 1
|
|
assert "No labels found" in result[0].text
|
|
assert "testowner/testrepo" in result[0].text
|
|
|
|
@pytest.mark.asyncio
|
|
async def test_list_labels_without_description(self, mock_client):
|
|
"""Test listing labels without descriptions."""
|
|
label_no_desc = {
|
|
"id": 1,
|
|
"name": "bug",
|
|
"color": "ff0000",
|
|
}
|
|
mock_client.get.return_value = [label_no_desc]
|
|
|
|
arguments = {
|
|
"owner": "testowner",
|
|
"repo": "testrepo",
|
|
}
|
|
|
|
result = await _list_labels(mock_client, arguments)
|
|
|
|
assert len(result) == 1
|
|
assert "bug" in result[0].text
|
|
assert "#ff0000" in result[0].text
|
|
|
|
|
|
class TestCreateLabel:
|
|
"""Test _create_label function."""
|
|
|
|
@pytest.mark.asyncio
|
|
async def test_create_label_minimal(self, mock_client, sample_label):
|
|
"""Test creating label with minimal required fields."""
|
|
mock_client.post.return_value = sample_label
|
|
|
|
arguments = {
|
|
"owner": "testowner",
|
|
"repo": "testrepo",
|
|
"name": "bug",
|
|
"color": "ff0000",
|
|
}
|
|
|
|
result = await _create_label(mock_client, arguments)
|
|
|
|
assert len(result) == 1
|
|
assert "Created label 'bug'" in result[0].text
|
|
assert "#ff0000" in result[0].text
|
|
assert "testowner/testrepo" in result[0].text
|
|
|
|
mock_client.post.assert_called_once()
|
|
call_args = mock_client.post.call_args
|
|
assert call_args[0][0] == "/repos/testowner/testrepo/labels"
|
|
payload = call_args[0][1]
|
|
assert payload["name"] == "bug"
|
|
assert payload["color"] == "ff0000"
|
|
|
|
@pytest.mark.asyncio
|
|
async def test_create_label_with_description(self, mock_client, sample_label):
|
|
"""Test creating label with description."""
|
|
mock_client.post.return_value = sample_label
|
|
|
|
arguments = {
|
|
"owner": "testowner",
|
|
"repo": "testrepo",
|
|
"name": "bug",
|
|
"color": "ff0000",
|
|
"description": "Something isn't working",
|
|
}
|
|
|
|
result = await _create_label(mock_client, arguments)
|
|
|
|
assert len(result) == 1
|
|
|
|
call_args = mock_client.post.call_args
|
|
payload = call_args[0][1]
|
|
assert payload["description"] == "Something isn't working"
|
|
|
|
@pytest.mark.asyncio
|
|
async def test_create_label_strips_hash(self, mock_client, sample_label):
|
|
"""Test creating label with # prefix is handled correctly."""
|
|
mock_client.post.return_value = sample_label
|
|
|
|
arguments = {
|
|
"owner": "testowner",
|
|
"repo": "testrepo",
|
|
"name": "bug",
|
|
"color": "#ff0000",
|
|
}
|
|
|
|
result = await _create_label(mock_client, arguments)
|
|
|
|
call_args = mock_client.post.call_args
|
|
payload = call_args[0][1]
|
|
assert payload["color"] == "ff0000"
|
|
assert not payload["color"].startswith("#")
|
|
|
|
|
|
class TestLabelToolHandler:
|
|
"""Test handle_label_tool function."""
|
|
|
|
@pytest.mark.asyncio
|
|
async def test_handle_list_labels(self, mock_client, sample_label):
|
|
"""Test handler routes to _list_labels."""
|
|
mock_client.get.return_value = [sample_label]
|
|
|
|
arguments = {
|
|
"owner": "testowner",
|
|
"repo": "testrepo",
|
|
}
|
|
|
|
result = await handle_label_tool(mock_client, "gitea_list_labels", arguments)
|
|
|
|
assert len(result) == 1
|
|
assert isinstance(result[0], TextContent)
|
|
assert "bug" in result[0].text
|
|
mock_client.get.assert_called_once()
|
|
|
|
@pytest.mark.asyncio
|
|
async def test_handle_create_label(self, mock_client, sample_label):
|
|
"""Test handler routes to _create_label."""
|
|
mock_client.post.return_value = sample_label
|
|
|
|
arguments = {
|
|
"owner": "testowner",
|
|
"repo": "testrepo",
|
|
"name": "bug",
|
|
"color": "ff0000",
|
|
}
|
|
|
|
result = await handle_label_tool(mock_client, "gitea_create_label", arguments)
|
|
|
|
assert len(result) == 1
|
|
assert "Created label" in result[0].text
|
|
mock_client.post.assert_called_once()
|
|
|
|
@pytest.mark.asyncio
|
|
async def test_handle_unknown_tool(self, mock_client):
|
|
"""Test handler with unknown tool name."""
|
|
result = await handle_label_tool(mock_client, "gitea_unknown_tool", {})
|
|
|
|
assert len(result) == 1
|
|
assert "Unknown label tool" in result[0].text
|
|
|
|
@pytest.mark.asyncio
|
|
async def test_handle_client_error(self, mock_client):
|
|
"""Test handler gracefully handles GiteaClientError."""
|
|
mock_client.get.side_effect = GiteaClientError("API Error")
|
|
|
|
arguments = {
|
|
"owner": "testowner",
|
|
"repo": "testrepo",
|
|
}
|
|
|
|
result = await handle_label_tool(mock_client, "gitea_list_labels", arguments)
|
|
|
|
assert len(result) == 1
|
|
assert "Gitea API error:" in result[0].text
|
|
assert "API Error" in result[0].text
|