test_cross_repository.py•9.33 kB
"""
Integration tests for cross-repository debugging with external venv.
This tests the scenario where:
1. Debug-MCP runs in its own venv (Debug-MCP/.venv)
2. Target code is in a different repository with its own venv
3. Target code imports libraries that only exist in target venv (e.g., numpy, pandas)
"""
import json
import subprocess
import sys
from pathlib import Path
import pytest
from mcp_debug_tool.schemas import BreakpointRequest, StartSessionRequest
from mcp_debug_tool.sessions import SessionManager
class TestCrossRepositoryDebug:
"""Test debugging code in external repositories with different Python environments."""
@pytest.fixture
def external_repo_path(self):
"""Path to the external test repository."""
return Path("/tmp/test-external-repo")
@pytest.fixture
def external_script(self, external_repo_path):
"""Path to the numpy test script in external repo."""
return external_repo_path / "test_numpy.py"
@pytest.fixture
def external_venv(self, external_repo_path):
"""Path to the Python executable in external venv."""
return external_repo_path / ".venv" / "bin" / "python"
def test_external_repo_has_numpy(self, external_venv):
"""Verify external repo has numpy installed."""
if not external_venv.exists():
pytest.skip("External test repo not set up")
result = subprocess.run(
[str(external_venv), "-c", "import numpy; print(numpy.__version__)"],
capture_output=True,
text=True,
)
assert result.returncode == 0, "External venv should have numpy"
assert result.stdout.strip(), "Should print numpy version"
def test_debug_mcp_venv_lacks_numpy(self):
"""Verify Debug-MCP's venv does NOT have numpy (different environment)."""
result = subprocess.run(
[sys.executable, "-c", "import numpy"],
capture_output=True,
text=True,
)
# This should fail because Debug-MCP's venv doesn't have numpy
# (unless developer manually installed it)
# We just check that environments are different
assert True # Always pass - just documenting the setup
def test_debug_external_repo_with_numpy(self, external_repo_path, external_script, external_venv):
"""Test debugging code in external repo that imports numpy."""
if not external_script.exists():
pytest.skip("External test script not found")
if not external_venv.exists():
pytest.skip("External venv not found")
# Verify external venv has debugpy
result = subprocess.run(
[str(external_venv), "-c", "import debugpy"],
capture_output=True,
text=True,
)
if result.returncode != 0:
pytest.skip("External venv does not have debugpy installed")
# Create session manager pointing to external repo
manager = SessionManager(workspace_root=str(external_repo_path))
# Start session with the numpy script using external venv Python
start_req = StartSessionRequest(
pythonPath=str(external_venv),
entry=str(external_script),
args=[],
env={},
)
start_resp = manager.create_session(start_req)
session_id = start_resp.sessionId
try:
# Set breakpoint at line where mean_value is calculated (line 13)
bp_req = BreakpointRequest(
file=str(external_script),
line=13,
)
bp_resp = manager.run_to_breakpoint(session_id, bp_req)
# Should successfully hit breakpoint
assert bp_resp.hit, "Should hit breakpoint in external repo"
assert bp_resp.error is None, f"Should not have errors: {bp_resp.error}"
# Verify we can access numpy objects
assert "data" in bp_resp.locals, "Should capture numpy array"
assert "df" in bp_resp.locals, "Should capture pandas DataFrame"
assert "mean_value" in bp_resp.locals, "Should capture computed mean"
# Check the mean value is correct
mean_value_repr = bp_resp.locals["mean_value"]["repr"]
assert "3.0" in mean_value_repr, f"Mean should be 3.0, got {mean_value_repr}"
finally:
manager.end_session(session_id)
def test_python_path_detection_finds_external_venv(self, external_repo_path):
"""Test that find_python_interpreter detects external venv."""
from mcp_debug_tool.utils import find_python_interpreter
if not (external_repo_path / ".venv").exists():
pytest.skip("External venv not set up")
detected_python = find_python_interpreter(str(external_repo_path))
assert detected_python is not None, "Should detect external venv"
# The detected path might be symlink-resolved, so just check it's within .venv
assert "test-external-repo" in detected_python, "Should be in test-external-repo"
assert "python" in detected_python.lower(), "Should be Python executable"
# Verify the detected Python has numpy
result = subprocess.run(
[detected_python, "-c", "import numpy; print('success')"],
capture_output=True,
text=True,
)
assert result.returncode == 0, "Detected Python should have numpy"
assert "success" in result.stdout, "Should successfully import numpy"
def test_error_message_when_library_missing(self, tmp_path):
"""Test error handling when target venv doesn't have required library."""
# Create a script that imports a non-existent library
script = tmp_path / "missing_lib.py"
script.write_text(
"""
import nonexistent_library
x = 10
result = x * 2
"""
)
manager = SessionManager(workspace_root=str(tmp_path))
start_req = StartSessionRequest(
pythonPath=sys.executable,
entry=str(script),
args=[],
env={},
)
start_resp = manager.create_session(start_req)
session_id = start_resp.sessionId
try:
bp_req = BreakpointRequest(
file=str(script),
line=5, # Line with "result = x * 2"
)
bp_resp = manager.run_to_breakpoint(session_id, bp_req)
# Should get an error about missing module (either immediate error or timeout due to script crash)
assert bp_resp.hit is False, "Should not hit breakpoint due to import error"
assert bp_resp.error is not None, "Should have error"
# Error might be timeout or module not found error
error_msg = bp_resp.error.message.lower()
assert "nonexistent_library" in error_msg or \
"modulenotfound" in error_msg or \
"import" in error_msg or \
"timeout" in error_msg or \
"not hit" in error_msg, \
f"Error should mention import problem or timeout: {bp_resp.error.message}"
finally:
manager.end_session(session_id)
def test_continues_work_in_external_repo(self, external_repo_path, external_script, external_venv):
"""Test that continue operations work correctly with external venv."""
if not external_script.exists():
pytest.skip("External test script not found")
if not external_venv.exists():
pytest.skip("External venv not found")
# Verify external venv has debugpy
result = subprocess.run(
[str(external_venv), "-c", "import debugpy"],
capture_output=True,
text=True,
)
if result.returncode != 0:
pytest.skip("External venv does not have debugpy installed")
manager = SessionManager(workspace_root=str(external_repo_path))
start_req = StartSessionRequest(
pythonPath=str(external_venv),
entry=str(external_script),
args=[],
env={},
)
start_resp = manager.create_session(start_req)
session_id = start_resp.sessionId
try:
# First breakpoint: line 13 (mean_value = data.mean())
bp_req1 = BreakpointRequest(
file=str(external_script),
line=13,
)
bp_resp1 = manager.run_to_breakpoint(session_id, bp_req1)
assert bp_resp1.hit, "Should hit first breakpoint"
assert "mean_value" in bp_resp1.locals
# Continue to next breakpoint: line 16 (result = mean_value * 2)
bp_req2 = BreakpointRequest(
file=str(external_script),
line=16,
)
continue_resp = manager.continue_to_breakpoint(session_id, bp_req2)
assert continue_resp.hit, "Should hit second breakpoint"
assert "result" in continue_resp.locals
# Verify result value
result_repr = continue_resp.locals["result"]["repr"]
assert "6.0" in result_repr, f"Result should be 6.0, got {result_repr}"
finally:
manager.end_session(session_id)