import sys
from pathlib import Path
import warnings
import pytest
# Mark all tests in this module as asyncio tests
pytestmark = pytest.mark.asyncio
# Suppress async cleanup warnings
warnings.filterwarnings("ignore", category=RuntimeWarning, message=".*cancel scope.*")
# Add parent directory to path to import main
sys.path.insert(0, str(Path(__file__).parent.parent))
from main import server
from mcp.shared.memory import create_connected_server_and_client_session
from mcp.client.session import ClientSession
@pytest.fixture
async def mcp_client():
"""Fixture that provides a low-level server client for testing."""
try:
async with create_connected_server_and_client_session(server) as client:
yield client
except RuntimeError as e:
# Suppress teardown errors from async context cleanup
if "cancel scope" not in str(e):
raise
async def test_server_info(mcp_client: ClientSession):
"""Test that server provides basic info."""
# Initialize the connection to get server info
result = await mcp_client.initialize()
assert result is not None
assert result.serverInfo.name == "MCP Server Template"
async def test_list_tools(mcp_client: ClientSession):
"""Test that server lists available tools."""
tools_result = await mcp_client.list_tools()
tools = tools_result.tools
assert len(tools) == 1
assert tools[0].name == "add_numbers"
assert tools[0].description == "Calculate the sum of two numbers"
@pytest.mark.parametrize(
"number1, number2, expected",
[
(1, 2, 3),
(2, 3, 5),
(3, 4, 7),
(10, 5, 15),
(0, 0, 0),
(-5, 5, 0),
(1.5, 2.5, 4.0),
],
)
async def test_add_numbers(
number1: float,
number2: float,
expected: float,
mcp_client: ClientSession,
):
"""Test add_numbers tool with various inputs."""
result = await mcp_client.call_tool(
name="add_numbers",
arguments={"number1": number1, "number2": number2}
)
assert result.content is not None, "Tool should return content"
assert len(result.content) > 0, "Result should have content"
# The official SDK returns content as TextContent
from mcp.types import TextContent
text_content = result.content[0]
assert isinstance(text_content, TextContent), "Content should be TextContent"
# Parse the result from the text
import json
result_data = json.loads(text_content.text)
assert isinstance(result_data, dict), "Result should be a dictionary"
assert "result" in result_data, "Result should contain 'result' key"
assert result_data["result"] == expected, f"Expected {expected}, got {result_data['result']}"
async def test_add_numbers_with_negative_numbers(mcp_client: ClientSession):
"""Test add_numbers specifically with negative numbers."""
result = await mcp_client.call_tool(
name="add_numbers",
arguments={"number1": -10, "number2": -20}
)
import json
from mcp.types import TextContent
text_content = result.content[0]
result_data = json.loads(text_content.text)
assert result_data["result"] == -30
async def test_add_numbers_with_floats(mcp_client: ClientSession):
"""Test add_numbers with floating point precision."""
result = await mcp_client.call_tool(
name="add_numbers",
arguments={"number1": 0.1, "number2": 0.2}
)
import json
from mcp.types import TextContent
text_content = result.content[0]
result_data = json.loads(text_content.text)
# Account for floating point precision
assert abs(result_data["result"] - 0.3) < 0.0001
if __name__ == "__main__":
pytest.main([__file__, "-v"])