"""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