Skip to main content
Glama

Weather MCP Server

test_server.py10.6 kB
""" Unit tests for server functionality. """ import pytest from unittest.mock import AsyncMock, Mock, patch from src.mcp_weather_server.server import ( add_tool_handler, get_tool_handler, register_all_tools, list_tools, call_tool, tool_handlers ) from src.mcp_weather_server.tools.toolhandler import ToolHandler from mcp.types import Tool, TextContent class MockToolHandler(ToolHandler): """Mock tool handler for testing.""" def __init__(self, name="mock_tool"): super().__init__(name) self.get_tool_description_called = False self.run_tool_called = False self.run_tool_args = None def get_tool_description(self) -> Tool: self.get_tool_description_called = True return Tool( name=self.name, description="Mock tool for testing", inputSchema={ "type": "object", "properties": { "test_param": {"type": "string"} }, "required": ["test_param"] } ) async def run_tool(self, args: dict): self.run_tool_called = True self.run_tool_args = args return [TextContent(type="text", text=f"Mock result for {args}")] class TestServerFunctions: """Test cases for server functions.""" def setup_method(self): """Clear tool handlers before each test.""" global tool_handlers tool_handlers.clear() def test_add_tool_handler(self): """Test adding a tool handler.""" handler = MockToolHandler("test_tool") add_tool_handler(handler) assert "test_tool" in tool_handlers assert tool_handlers["test_tool"] == handler def test_get_tool_handler_existing(self): """Test getting an existing tool handler.""" handler = MockToolHandler("existing_tool") add_tool_handler(handler) retrieved = get_tool_handler("existing_tool") assert retrieved == handler def test_get_tool_handler_nonexistent(self): """Test getting a non-existent tool handler.""" retrieved = get_tool_handler("nonexistent_tool") assert retrieved is None def test_register_all_tools(self): """Test registering all tools.""" register_all_tools() # Check that expected tools are registered expected_tools = [ "get_current_weather", "get_weather_byDateTimeRange", "get_weather_details", "get_current_datetime", "get_timezone_info", "convert_time" ] for tool_name in expected_tools: assert tool_name in tool_handlers assert tool_handlers[tool_name] is not None @pytest.mark.asyncio async def test_list_tools_empty(self): """Test listing tools when no tools are registered.""" result = await list_tools() assert result == [] @pytest.mark.asyncio async def test_list_tools_with_handlers(self): """Test listing tools with registered handlers.""" handler1 = MockToolHandler("tool1") handler2 = MockToolHandler("tool2") add_tool_handler(handler1) add_tool_handler(handler2) result = await list_tools() assert len(result) == 2 assert handler1.get_tool_description_called assert handler2.get_tool_description_called tool_names = [tool.name for tool in result] assert "tool1" in tool_names assert "tool2" in tool_names @pytest.mark.asyncio async def test_list_tools_exception_handling(self): """Test list_tools exception handling.""" # Create a mock handler that raises an exception with patch.dict(tool_handlers, {"bad_tool": Mock()}): tool_handlers["bad_tool"].get_tool_description.side_effect = Exception("Test error") with pytest.raises(Exception): await list_tools() @pytest.mark.asyncio async def test_call_tool_success(self): """Test successful tool execution.""" handler = MockToolHandler("test_tool") add_tool_handler(handler) args = {"test_param": "test_value"} result = await call_tool("test_tool", args) assert handler.run_tool_called assert handler.run_tool_args == args assert len(result) == 1 assert isinstance(result[0], TextContent) assert "test_value" in result[0].text @pytest.mark.asyncio async def test_call_tool_nonexistent(self): """Test calling a non-existent tool.""" args = {"test_param": "test_value"} result = await call_tool("nonexistent_tool", args) assert len(result) == 1 assert isinstance(result[0], TextContent) assert "Error executing tool 'nonexistent_tool'" in result[0].text @pytest.mark.asyncio async def test_call_tool_invalid_arguments(self): """Test calling a tool with invalid arguments.""" handler = MockToolHandler("test_tool") add_tool_handler(handler) # Pass non-dict arguments result = await call_tool("test_tool", "invalid_args") assert len(result) == 1 assert isinstance(result[0], TextContent) assert "Error executing tool 'test_tool'" in result[0].text assert "Arguments must be a dictionary" in result[0].text @pytest.mark.asyncio async def test_call_tool_handler_exception(self): """Test calling a tool when handler raises an exception.""" handler = MockToolHandler("test_tool") handler.run_tool = AsyncMock(side_effect=Exception("Handler error")) add_tool_handler(handler) args = {"test_param": "test_value"} result = await call_tool("test_tool", args) assert len(result) == 1 assert isinstance(result[0], TextContent) assert "Error executing tool 'test_tool'" in result[0].text assert "Handler error" in result[0].text @pytest.mark.asyncio async def test_call_tool_with_complex_args(self): """Test calling a tool with complex arguments.""" handler = MockToolHandler("complex_tool") add_tool_handler(handler) complex_args = { "string_param": "test_string", "number_param": 42, "list_param": [1, 2, 3], "dict_param": {"nested": "value"} } result = await call_tool("complex_tool", complex_args) assert handler.run_tool_called assert handler.run_tool_args == complex_args assert len(result) == 1 class TestToolHandlerIntegration: """Integration tests for tool handler registration and execution.""" def setup_method(self): """Clear tool handlers before each test.""" global tool_handlers tool_handlers.clear() @pytest.mark.asyncio async def test_full_workflow(self): """Test complete workflow from registration to execution.""" # Register a tool handler = MockToolHandler("workflow_tool") add_tool_handler(handler) # List tools tools = await list_tools() assert len(tools) == 1 assert tools[0].name == "workflow_tool" # Call the tool args = {"test_param": "workflow_test"} result = await call_tool("workflow_tool", args) assert len(result) == 1 assert "workflow_test" in result[0].text @pytest.mark.asyncio async def test_multiple_tools_workflow(self): """Test workflow with multiple tools.""" # Register multiple tools handler1 = MockToolHandler("tool_one") handler2 = MockToolHandler("tool_two") handler3 = MockToolHandler("tool_three") add_tool_handler(handler1) add_tool_handler(handler2) add_tool_handler(handler3) # List all tools tools = await list_tools() assert len(tools) == 3 tool_names = [tool.name for tool in tools] assert "tool_one" in tool_names assert "tool_two" in tool_names assert "tool_three" in tool_names # Call each tool for tool_name in ["tool_one", "tool_two", "tool_three"]: args = {"test_param": f"test_{tool_name}"} result = await call_tool(tool_name, args) assert len(result) == 1 assert f"test_{tool_name}" in result[0].text @pytest.mark.asyncio async def test_real_tool_registration(self): """Test registration of real tool handlers.""" register_all_tools() # Verify specific tools are registered weather_tool = get_tool_handler("get_current_weather") assert weather_tool is not None time_tool = get_tool_handler("get_current_datetime") assert time_tool is not None # Test tool descriptions tools = await list_tools() assert len(tools) >= 6 # Should have at least 6 tools # Verify tool names tool_names = [tool.name for tool in tools] expected_tools = [ "get_current_weather", "get_weather_byDateTimeRange", "get_weather_details", "get_current_datetime", "get_timezone_info", "convert_time" ] for expected_tool in expected_tools: assert expected_tool in tool_names def test_tool_handler_replacement(self): """Test replacing an existing tool handler.""" # Add initial handler handler1 = MockToolHandler("replaceable_tool") add_tool_handler(handler1) # Verify it's registered assert get_tool_handler("replaceable_tool") == handler1 # Replace with new handler handler2 = MockToolHandler("replaceable_tool") add_tool_handler(handler2) # Verify replacement assert get_tool_handler("replaceable_tool") == handler2 assert get_tool_handler("replaceable_tool") != handler1 def test_tool_handlers_persistence(self): """Test that tool handlers persist across multiple operations.""" handler = MockToolHandler("persistent_tool") add_tool_handler(handler) # Perform multiple operations retrieved1 = get_tool_handler("persistent_tool") retrieved2 = get_tool_handler("persistent_tool") retrieved3 = get_tool_handler("persistent_tool") # All should return the same handler instance assert retrieved1 == handler assert retrieved2 == handler assert retrieved3 == handler assert retrieved1 == retrieved2 == retrieved3

MCP directory API

We provide all the information about MCP servers via our MCP API.

curl -X GET 'https://glama.ai/api/mcp/v1/servers/isdaniel/mcp_weather_server'

If you have feedback or need assistance with the MCP directory API, please join our Discord server