203 lines
6.2 KiB
Python
203 lines
6.2 KiB
Python
"""Tests for MCP protocol message models."""
|
|
|
|
import pytest
|
|
from pydantic import ValidationError
|
|
|
|
from mcp_server_cli.models import (
|
|
MCPRequest,
|
|
MCPResponse,
|
|
MCPNotification,
|
|
MCPMethod,
|
|
ToolDefinition,
|
|
ToolSchema,
|
|
ToolParameter,
|
|
ToolCallParams,
|
|
ToolCallResult,
|
|
InitializeParams,
|
|
InitializeResult,
|
|
ServerConfig,
|
|
LocalLLMConfig,
|
|
SecurityConfig,
|
|
AppConfig,
|
|
)
|
|
|
|
|
|
class TestMCPRequest:
|
|
"""Tests for MCPRequest model."""
|
|
|
|
def test_create_request(self):
|
|
"""Test creating a basic request."""
|
|
request = MCPRequest(
|
|
id=1,
|
|
method=MCPMethod.INITIALIZE,
|
|
params={"protocol_version": "2024-11-05"},
|
|
)
|
|
assert request.jsonrpc == "2.0"
|
|
assert request.id == 1
|
|
assert request.method == MCPMethod.INITIALIZE
|
|
assert request.params["protocol_version"] == "2024-11-05"
|
|
|
|
def test_request_without_id(self):
|
|
"""Test request without id (notification)."""
|
|
request = MCPRequest(
|
|
method=MCPMethod.TOOLS_LIST,
|
|
)
|
|
assert request.id is None
|
|
assert request.method == MCPMethod.TOOLS_LIST
|
|
|
|
|
|
class TestMCPResponse:
|
|
"""Tests for MCPResponse model."""
|
|
|
|
def test_create_response_with_result(self):
|
|
"""Test creating a response with result."""
|
|
response = MCPResponse(
|
|
id=1,
|
|
result={"tools": [{"name": "test_tool"}]},
|
|
)
|
|
assert response.jsonrpc == "2.0"
|
|
assert response.id == 1
|
|
assert response.result["tools"][0]["name"] == "test_tool"
|
|
|
|
def test_create_response_with_error(self):
|
|
"""Test creating a response with error."""
|
|
response = MCPResponse(
|
|
id=1,
|
|
error={"code": -32600, "message": "Invalid Request"},
|
|
)
|
|
assert response.error["code"] == -32600
|
|
assert response.error["message"] == "Invalid Request"
|
|
|
|
|
|
class TestToolModels:
|
|
"""Tests for tool-related models."""
|
|
|
|
def test_tool_definition(self):
|
|
"""Test creating a tool definition."""
|
|
tool = ToolDefinition(
|
|
name="read_file",
|
|
description="Read a file from disk",
|
|
input_schema=ToolSchema(
|
|
properties={
|
|
"path": ToolParameter(
|
|
name="path",
|
|
type="string",
|
|
description="File path",
|
|
required=True,
|
|
)
|
|
},
|
|
required=["path"],
|
|
),
|
|
)
|
|
assert tool.name == "read_file"
|
|
assert "path" in tool.input_schema.required
|
|
|
|
def test_tool_name_validation(self):
|
|
"""Test that tool names must be valid identifiers."""
|
|
with pytest.raises(ValidationError):
|
|
ToolDefinition(
|
|
name="invalid-name",
|
|
description="Tool with invalid name",
|
|
)
|
|
|
|
def test_tool_parameter_with_enum(self):
|
|
"""Test tool parameter with enum values."""
|
|
param = ToolParameter(
|
|
name="operation",
|
|
type="string",
|
|
enum=["read", "write", "delete"],
|
|
)
|
|
assert param.enum == ["read", "write", "delete"]
|
|
|
|
|
|
class TestConfigModels:
|
|
"""Tests for configuration models."""
|
|
|
|
def test_server_config_defaults(self):
|
|
"""Test server config default values."""
|
|
config = ServerConfig()
|
|
assert config.host == "127.0.0.1"
|
|
assert config.port == 3000
|
|
assert config.log_level == "INFO"
|
|
|
|
def test_local_llm_config_defaults(self):
|
|
"""Test local LLM config defaults."""
|
|
config = LocalLLMConfig()
|
|
assert config.enabled is False
|
|
assert config.base_url == "http://localhost:11434"
|
|
assert config.model == "llama2"
|
|
|
|
def test_security_config_defaults(self):
|
|
"""Test security config defaults."""
|
|
config = SecurityConfig()
|
|
assert "ls" in config.allowed_commands
|
|
assert "/etc" in config.blocked_paths
|
|
assert config.max_shell_timeout == 30
|
|
|
|
def test_app_config_composition(self):
|
|
"""Test app config with all components."""
|
|
config = AppConfig(
|
|
server=ServerConfig(port=8080),
|
|
llm=LocalLLMConfig(enabled=True),
|
|
security=SecurityConfig(allowed_commands=["echo"]),
|
|
)
|
|
assert config.server.port == 8080
|
|
assert config.llm.enabled is True
|
|
assert "echo" in config.security.allowed_commands
|
|
|
|
|
|
class TestToolCallModels:
|
|
"""Tests for tool call models."""
|
|
|
|
def test_tool_call_params(self):
|
|
"""Test tool call parameters."""
|
|
params = ToolCallParams(
|
|
name="read_file",
|
|
arguments={"path": "/tmp/test.txt"},
|
|
)
|
|
assert params.name == "read_file"
|
|
assert params.arguments["path"] == "/tmp/test.txt"
|
|
|
|
def test_tool_call_result(self):
|
|
"""Test tool call result."""
|
|
result = ToolCallResult(
|
|
content=[{"type": "text", "text": "Hello"}],
|
|
is_error=False,
|
|
)
|
|
assert result.content[0]["text"] == "Hello"
|
|
assert result.is_error is False
|
|
|
|
def test_tool_call_result_error(self):
|
|
"""Test tool call result with error."""
|
|
result = ToolCallResult(
|
|
content=[],
|
|
is_error=True,
|
|
error_message="File not found",
|
|
)
|
|
assert result.is_error is True
|
|
assert result.error_message == "File not found"
|
|
|
|
|
|
class TestInitializeModels:
|
|
"""Tests for initialize-related models."""
|
|
|
|
def test_initialize_params(self):
|
|
"""Test initialize parameters."""
|
|
params = InitializeParams(
|
|
protocol_version="2024-11-05",
|
|
capabilities={"tools": {}},
|
|
client_info={"name": "test-client"},
|
|
)
|
|
assert params.protocol_version == "2024-11-05"
|
|
assert params.client_info["name"] == "test-client"
|
|
|
|
def test_initialize_result(self):
|
|
"""Test initialize result."""
|
|
result = InitializeResult(
|
|
protocol_version="2024-11-05",
|
|
server_info={"name": "mcp-server", "version": "0.1.0"},
|
|
capabilities={"tools": {"listChanged": True}},
|
|
)
|
|
assert result.server_info.name == "mcp-server"
|
|
assert result.capabilities.tools["listChanged"] is True
|