test_invalid_location.py•13.5 kB
"""
Integration tests for error handling with invalid file/line locations.
Tests that requests with invalid file paths or line numbers are rejected
and return structured error responses.
"""
import pytest
import sys
from mcp_debug_tool.schemas import BreakpointRequest, StartSessionRequest
from mcp_debug_tool.sessions import SessionManager
@pytest.fixture
def workspace_root(tmp_path):
"""Create a temporary workspace."""
return tmp_path
@pytest.fixture
def session_manager(workspace_root):
"""Create a session manager."""
return SessionManager(workspace_root)
@pytest.fixture
def valid_script(workspace_root):
"""Create a valid test script."""
script_path = workspace_root / "valid_script.py"
script_path.write_text("""def test_function():
x = 1
y = 2
return x + y
result = test_function()
print(result)
""")
return script_path
class TestInvalidFileLocation:
"""Tests for invalid file path handling."""
def test_nonexistent_file_in_breakpoint(
self, session_manager, valid_script, workspace_root
):
"""Test that breakpoint on non-existent file fails."""
# Create session with valid script
create_request = StartSessionRequest(
pythonPath=sys.executable,
entry=valid_script.relative_to(workspace_root).as_posix()
)
create_response = session_manager.create_session(create_request)
session_id = create_response.sessionId
# Try to set breakpoint on non-existent file
bp_request = BreakpointRequest(
file="nonexistent.py",
line=1,
)
with pytest.raises(FileNotFoundError):
session_manager.run_to_breakpoint(session_id, bp_request)
# Clean up
session_manager.end_session(session_id)
def test_file_outside_workspace(
self, session_manager, valid_script, workspace_root, tmp_path
):
"""Test that file outside workspace is rejected."""
# Create session
create_request = StartSessionRequest(
pythonPath=sys.executable,
entry=valid_script.relative_to(workspace_root).as_posix()
)
create_response = session_manager.create_session(create_request)
session_id = create_response.sessionId
# Try to set breakpoint on file outside workspace
bp_request = BreakpointRequest(
file="../outside_workspace.py",
line=1,
)
with pytest.raises(ValueError, match="escapes workspace"):
session_manager.run_to_breakpoint(session_id, bp_request)
# Clean up
session_manager.end_session(session_id)
def test_file_is_directory_not_file(self, session_manager, workspace_root):
"""Test that attempting to breakpoint on a directory fails."""
# Create a directory
dir_path = workspace_root / "subdir"
dir_path.mkdir()
# Create a valid script
script_path = workspace_root / "test.py"
script_path.write_text("x = 1")
# Create session
create_request = StartSessionRequest(
pythonPath=sys.executable,
entry=script_path.relative_to(workspace_root).as_posix()
)
create_response = session_manager.create_session(create_request)
session_id = create_response.sessionId
# Try to set breakpoint on directory
bp_request = BreakpointRequest(
file="subdir",
line=1,
)
with pytest.raises(ValueError, match="Not a file"):
session_manager.run_to_breakpoint(session_id, bp_request)
# Clean up
session_manager.end_session(session_id)
def test_file_with_wrong_extension_still_accepted(
self, session_manager, workspace_root
):
"""Test that file with different extension is still accepted if it exists."""
# Create a script with .txt extension (technically valid path)
script_path = workspace_root / "script.txt"
script_path.write_text("x = 1\ny = 2\n")
# Create a valid Python script
entry_script = workspace_root / "entry.py"
entry_script.write_text("x = 1\n")
# Create session
create_request = StartSessionRequest(
pythonPath=sys.executable,
entry=entry_script.relative_to(workspace_root).as_posix()
)
create_response = session_manager.create_session(create_request)
session_id = create_response.sessionId
# Try to set breakpoint on .txt file
bp_request = BreakpointRequest(
file=script_path.relative_to(workspace_root).as_posix(),
line=1,
)
# File exists, so validation should pass (execution might fail, but that's later)
try:
session_manager.run_to_breakpoint(session_id, bp_request)
except (SyntaxError, ValueError):
# Expected - file is not valid Python or line is out of range
pass
# Clean up
session_manager.end_session(session_id)
class TestInvalidLineNumber:
"""Tests for invalid line number handling."""
def test_line_exceeds_file_length(
self, session_manager, valid_script, workspace_root
):
"""Test that line number beyond file length is rejected."""
# Create session
create_request = StartSessionRequest(
pythonPath=sys.executable,
entry=valid_script.relative_to(workspace_root).as_posix()
)
create_response = session_manager.create_session(create_request)
session_id = create_response.sessionId
# Try to set breakpoint beyond file length
bp_request = BreakpointRequest(
file=valid_script.relative_to(workspace_root).as_posix(),
line=9999,
)
with pytest.raises(ValueError, match="exceeds file length"):
session_manager.run_to_breakpoint(session_id, bp_request)
# Clean up
session_manager.end_session(session_id)
def test_line_zero_rejected(self, session_manager, valid_script):
"""Test that line 0 is rejected by schema validation."""
# Schema validation should reject line 0 before reaching execution
with pytest.raises(ValueError):
BreakpointRequest(
file="test.py",
line=0,
)
def test_line_negative_rejected(self, session_manager, valid_script):
"""Test that negative line number is rejected by schema validation."""
# Schema validation should reject negative lines
with pytest.raises(ValueError):
BreakpointRequest(
file="test.py",
line=-1,
)
def test_line_in_middle_of_file(
self, session_manager, workspace_root
):
"""Test that valid line in middle of file is accepted."""
# Create script
script_path = workspace_root / "middle_line_script.py"
script_path.write_text("""line 1
line 2
line 3 # Breakpoint here
line 4
line 5
""")
# Create session
create_request = StartSessionRequest(
pythonPath=sys.executable,
entry=script_path.relative_to(workspace_root).as_posix()
)
create_response = session_manager.create_session(create_request)
session_id = create_response.sessionId
# Set breakpoint on line 3 (should be valid)
bp_request = BreakpointRequest(
file=script_path.relative_to(workspace_root).as_posix(),
line=3,
)
# This should not raise validation error
try:
response = session_manager.run_to_breakpoint(session_id, bp_request)
# Response might indicate breakpoint not hit or hit, but no validation error
except ValueError as e:
pytest.fail(f"Valid line should not raise ValueError: {e}")
# Clean up
session_manager.end_session(session_id)
def test_line_at_file_end(self, session_manager, workspace_root):
"""Test that line at end of file is valid."""
# Create script with known number of lines
script_path = workspace_root / "end_line_script.py"
script_path.write_text("""x = 1
y = 2
z = 3""") # 3 lines total
# Create session
create_request = StartSessionRequest(
pythonPath=sys.executable,
entry=script_path.relative_to(workspace_root).as_posix()
)
create_response = session_manager.create_session(create_request)
session_id = create_response.sessionId
# Set breakpoint on last line (should be valid)
bp_request = BreakpointRequest(
file=script_path.relative_to(workspace_root).as_posix(),
line=3,
)
try:
response = session_manager.run_to_breakpoint(session_id, bp_request)
except ValueError as e:
pytest.fail(f"Valid line should not raise ValueError: {e}")
# Clean up
session_manager.end_session(session_id)
class TestInvalidLocationErrorResponse:
"""Tests for proper error response format for invalid locations."""
def test_invalid_file_error_structure(
self, session_manager, valid_script, workspace_root
):
"""Test that invalid file error has proper structure."""
# Create session
create_request = StartSessionRequest(
pythonPath=sys.executable,
entry=valid_script.relative_to(workspace_root).as_posix()
)
create_response = session_manager.create_session(create_request)
session_id = create_response.sessionId
# Try invalid file
bp_request = BreakpointRequest(
file="nonexistent.py",
line=1,
)
try:
session_manager.run_to_breakpoint(session_id, bp_request)
pytest.fail("Should have raised FileNotFoundError")
except FileNotFoundError as e:
# Verify error message mentions the file
assert "nonexistent.py" in str(e) or "not found" in str(e).lower()
# Clean up
session_manager.end_session(session_id)
def test_invalid_line_error_structure(
self, session_manager, valid_script, workspace_root
):
"""Test that invalid line error has proper structure."""
# Create session
create_request = StartSessionRequest(
pythonPath=sys.executable,
entry=valid_script.relative_to(workspace_root).as_posix()
)
create_response = session_manager.create_session(create_request)
session_id = create_response.sessionId
# Try invalid line
bp_request = BreakpointRequest(
file=valid_script.relative_to(workspace_root).as_posix(),
line=9999,
)
try:
session_manager.run_to_breakpoint(session_id, bp_request)
pytest.fail("Should have raised ValueError")
except ValueError as e:
# Verify error message mentions line and exceeds
assert "exceeds" in str(e) or "line" in str(e).lower()
# Clean up
session_manager.end_session(session_id)
def test_path_escape_error_structure(
self, session_manager, valid_script, workspace_root
):
"""Test that path escape error has proper structure."""
# Create session
create_request = StartSessionRequest(
pythonPath=sys.executable,
entry=valid_script.relative_to(workspace_root).as_posix()
)
create_response = session_manager.create_session(create_request)
session_id = create_response.sessionId
# Try path that escapes workspace
bp_request = BreakpointRequest(
file="../../../etc/passwd",
line=1,
)
try:
session_manager.run_to_breakpoint(session_id, bp_request)
pytest.fail("Should have raised ValueError")
except ValueError as e:
# Verify error message mentions escape/workspace
assert "escapes" in str(e).lower() or "workspace" in str(e).lower()
# Clean up
session_manager.end_session(session_id)
def test_validation_does_not_reach_breakpoint(
self, session_manager, valid_script, workspace_root
):
"""Test that validation errors prevent reaching breakpoint."""
# Create session
create_request = StartSessionRequest(
pythonPath=sys.executable,
entry=valid_script.relative_to(workspace_root).as_posix()
)
create_response = session_manager.create_session(create_request)
session_id = create_response.sessionId
# Try invalid location
bp_request = BreakpointRequest(
file="nonexistent.py",
line=1,
)
try:
session_manager.run_to_breakpoint(session_id, bp_request)
pytest.fail("Should have raised error")
except (FileNotFoundError, ValueError) as e:
# Verify it's a validation error, not an execution error
# Validation errors happen before breakpoint is set
pass
# Verify session is still in idle state (not paused)
state = session_manager.get_state(session_id)
# State might be idle or error depending on implementation
assert state.status in ("idle", "error")
# Clean up
session_manager.end_session(session_id)