test_schemas.py•7.75 kB
"""
Unit tests for schema validation.
Tests request/response models and their validation rules.
"""
import pytest
import sys
from pydantic import ValidationError
from mcp_debug_tool.schemas import (
BreakpointRequest,
BreakpointResponse,
StartSessionRequest,
StartSessionResponse,
VariableValue,
)
class TestStartSessionRequest:
"""Tests for StartSessionRequest validation."""
def test_valid_minimal_request(self):
"""Test valid minimal session request."""
request = StartSessionRequest(entry="test.py", pythonPath=sys.executable)
assert request.entry == "test.py"
assert request.args is None
assert request.env is None
assert request.pythonPath == sys.executable
def test_valid_full_request(self):
"""Test valid full session request."""
request = StartSessionRequest(
entry="scripts/test.py",
args=["--verbose", "input.txt"],
env={"DEBUG": "1", "PATH": "/usr/bin"},
pythonPath="/usr/bin/python3",
)
assert request.entry == "scripts/test.py"
assert len(request.args) == 2
assert len(request.env) == 2
assert request.pythonPath == "/usr/bin/python3"
def test_too_many_args(self):
"""Test rejection of too many arguments."""
with pytest.raises(ValidationError) as exc_info:
StartSessionRequest(
entry="test.py",
pythonPath=sys.executable,
args=[f"arg{i}" for i in range(25)], # Max is 20
)
assert "Too many args" in str(exc_info.value)
def test_arg_too_long(self):
"""Test rejection of argument that's too long."""
with pytest.raises(ValidationError) as exc_info:
StartSessionRequest(
entry="test.py",
args=["x" * 600], # Max is 512
pythonPath=sys.executable,
)
assert "Arg too long" in str(exc_info.value)
def test_too_many_env_vars(self):
"""Test rejection of too many environment variables."""
with pytest.raises(ValidationError) as exc_info:
StartSessionRequest(
entry="test.py",
pythonPath=sys.executable,
env={f"VAR{i}": "value" for i in range(60)}, # Max is 50
)
assert "Too many env vars" in str(exc_info.value)
def test_env_key_too_long(self):
"""Test rejection of environment key that's too long."""
with pytest.raises(ValidationError) as exc_info:
StartSessionRequest(
entry="test.py",
env={"x" * 100: "value"}, # Max key length is 64
pythonPath=sys.executable,
)
assert "Env key too long" in str(exc_info.value)
def test_env_value_too_long(self):
"""Test rejection of environment value that's too long."""
with pytest.raises(ValidationError) as exc_info:
StartSessionRequest(
entry="test.py",
env={"KEY": "x" * 2000}, # Max value length is 1024
pythonPath=sys.executable,
)
assert "Env value too long" in str(exc_info.value)
class TestBreakpointRequest:
"""Tests for BreakpointRequest validation."""
def test_valid_request(self):
"""Test valid breakpoint request."""
request = BreakpointRequest(file="test.py", line=10)
assert request.file == "test.py"
assert request.line == 10
def test_line_must_be_positive(self):
"""Test that line number must be >= 1."""
with pytest.raises(ValidationError) as exc_info:
BreakpointRequest(file="test.py", line=0)
assert "greater than or equal to 1" in str(exc_info.value)
def test_line_negative(self):
"""Test rejection of negative line number."""
with pytest.raises(ValidationError) as exc_info:
BreakpointRequest(file="test.py", line=-5)
assert "greater than or equal to 1" in str(exc_info.value)
class TestBreakpointResponse:
"""Tests for BreakpointResponse model."""
def test_hit_response_with_locals(self):
"""Test response when breakpoint is hit."""
response = BreakpointResponse(
hit=True,
completed=False,
frameInfo={"file": "test.py", "line": 10},
locals={
"x": {"type": "int", "repr": "42", "isTruncated": False},
"y": {"type": "str", "repr": "'hello'", "isTruncated": False},
},
)
assert response.hit is True
assert response.completed is False
assert response.frameInfo is not None
assert len(response.locals) == 2
def test_not_hit_response(self):
"""Test response when breakpoint is not hit."""
response = BreakpointResponse(
hit=False,
completed=True,
frameInfo=None,
locals=None,
)
assert response.hit is False
assert response.completed is True
assert response.frameInfo is None
assert response.locals is None
def test_error_response(self):
"""Test error response structure."""
from mcp_debug_tool.schemas import ExecutionError
response = BreakpointResponse(
hit=False,
completed=False,
error=ExecutionError(type="ValueError", message="Invalid input"),
)
assert response.hit is False
assert response.error is not None
assert response.error.type == "ValueError"
assert response.error.message == "Invalid input"
class TestVariableValue:
"""Tests for VariableValue model."""
def test_simple_variable(self):
"""Test simple variable representation."""
var = VariableValue(
name="count",
type="int",
repr="42",
isTruncated=False,
)
assert var.name == "count"
assert var.type == "int"
assert var.repr == "42"
assert var.isTruncated is False
def test_truncated_variable(self):
"""Test truncated variable representation."""
var = VariableValue(
name="long_string",
type="str",
repr="'x' * 1000...",
size=1000,
isTruncated=True,
)
assert var.isTruncated is True
assert var.size == 1000
def test_nested_variable(self):
"""Test nested variable representation."""
var = VariableValue(
name="data",
type="dict",
repr="{'a': 1, 'b': 2}",
size=2,
isTruncated=False,
children=[
VariableValue(name="a", type="int", repr="1", isTruncated=False),
VariableValue(name="b", type="int", repr="2", isTruncated=False),
],
)
assert len(var.children) == 2
assert var.children[0].name == "a"
assert var.children[1].name == "b"
class TestResponseShapes:
"""Tests for consistent response shapes."""
def test_start_session_response(self):
"""Test StartSessionResponse shape."""
response = StartSessionResponse(sessionId="test-123")
assert hasattr(response, "sessionId")
assert isinstance(response.sessionId, str)
def test_all_responses_serializable(self):
"""Test that all responses can be serialized to dict."""
responses = [
StartSessionResponse(sessionId="test-123"),
BreakpointResponse(hit=False, completed=True),
]
for response in responses:
data = response.model_dump()
assert isinstance(data, dict)