Skip to main content
Glama
test_dap_integration.py7.94 kB
""" Integration test for DAP (Debug Adapter Protocol) integration. This is a proof-of-concept test for Phase 1 of the DAP integration proposal. It verifies that the DAPClient and DAPSyncWrapper work correctly with debugpy. Test Goals: 1. Launch debugpy server 2. Connect DAP client 3. Set a breakpoint 4. Capture variables at breakpoint 5. Verify no sys.modules corruption """ import sys import tempfile from pathlib import Path import pytest from mcp_debug_tool.dap_wrapper import DAPSyncWrapper @pytest.fixture def simple_script(): """Create a simple Python script for testing.""" code = ''' x = 10 y = 20 z = x + y # Breakpoint here print(f"Result: {z}") ''' with tempfile.NamedTemporaryFile(mode='w', suffix='.py', delete=False) as f: f.write(code) script_path = Path(f.name) yield script_path # Cleanup script_path.unlink(missing_ok=True) @pytest.fixture def multi_breakpoint_script(): """Create a script with multiple breakpoints for testing.""" code = ''' def calculate(a, b): result = a + b # Breakpoint 1 return result x = 5 y = 10 total = calculate(x, y) # Breakpoint 2 print(f"Total: {total}") ''' with tempfile.NamedTemporaryFile(mode='w', suffix='.py', delete=False) as f: f.write(code) script_path = Path(f.name) yield script_path # Cleanup script_path.unlink(missing_ok=True) def test_dap_basic_connection(): """Test basic DAP connection and initialization.""" # This test just verifies we can import the modules from mcp_debug_tool.dap_client import DAPClient from mcp_debug_tool.dap_wrapper import DAPSyncWrapper # Verify classes exist assert DAPClient is not None assert DAPSyncWrapper is not None def test_dap_single_breakpoint(simple_script): """ Test setting a single breakpoint and capturing variables. This is the core proof-of-concept test for Phase 1. """ python_path = sys.executable with DAPSyncWrapper() as wrapper: # Initialize and launch wrapper.initialize_and_launch( python_path=python_path, script_path=simple_script, args=[], env=None, cwd=None, ) # Run to breakpoint (line 4: z = x + y) response = wrapper.run_to_breakpoint( file=str(simple_script), line=4, timeout=10.0, ) # Verify breakpoint was hit assert response.hit is True, f"Breakpoint not hit. Error: {response.error}" assert response.completed is False assert response.error is None # Verify frame info assert response.frameInfo is not None assert response.frameInfo.line == 4 # Verify variables were captured assert response.locals is not None assert 'x' in response.locals assert 'y' in response.locals # Verify variable values (as strings in repr) assert '10' in response.locals['x']['repr'] assert '20' in response.locals['y']['repr'] def test_dap_no_sys_modules_corruption(simple_script): """ Test that running debugpy multiple times doesn't corrupt sys.modules. This addresses one of the key problems with the bdb-based implementation. """ python_path = sys.executable # Run first execution with DAPSyncWrapper() as wrapper1: wrapper1.initialize_and_launch( python_path=python_path, script_path=simple_script, args=[], env=None, cwd=None, ) response1 = wrapper1.run_to_breakpoint( file=str(simple_script), line=4, timeout=10.0, ) assert response1.hit is True # Run second execution - should not have sys.modules issues with DAPSyncWrapper() as wrapper2: wrapper2.initialize_and_launch( python_path=python_path, script_path=simple_script, args=[], env=None, cwd=None, ) response2 = wrapper2.run_to_breakpoint( file=str(simple_script), line=4, timeout=10.0, ) assert response2.hit is True # If we get here without KeyError, sys.modules is not corrupted def test_dap_timeout_handling(simple_script): """ Test that debugpy handles invalid breakpoint lines gracefully. Note: DAP (debugpy) doesn't validate breakpoint line numbers during setBreakpoints. Instead, it silently ignores invalid line numbers and execution continues normally, hitting the next valid executable line (in this case, the print statement). This test verifies that debugpy handles this gracefully rather than throwing an error. """ python_path = sys.executable with DAPSyncWrapper() as wrapper: wrapper.initialize_and_launch( python_path=python_path, script_path=simple_script, args=[], env=None, cwd=None, ) # Try to set breakpoint on non-existent line 999 # DAP silently ignores this and execution continues, hitting the print statement response = wrapper.run_to_breakpoint( file=str(simple_script), line=999, # Non-existent line - DAP ignores this timeout=5.0, ) # DAP ignores the invalid line and execution continues normally. # The script executes and hits the print statement (line 5). # This is expected DAP behavior - invalid breakpoint lines are silently ignored. assert response.hit is True # Execution continues and hits the print statement assert response.frameInfo is not None assert response.frameInfo.line == 5 # print(f"Result: {z}") def test_dap_multiple_breakpoints(multi_breakpoint_script): """ Test hitting multiple breakpoints in sequence. Phase 3 implementation now supports continue_execution for hitting multiple breakpoints. """ python_path = sys.executable with DAPSyncWrapper() as wrapper: wrapper.initialize_and_launch( python_path=python_path, script_path=multi_breakpoint_script, args=[], env=None, cwd=None, ) # Hit first breakpoint response1 = wrapper.run_to_breakpoint( file=str(multi_breakpoint_script), line=3, # result = a + b timeout=10.0, ) assert response1.hit is True def test_dap_error_handling(simple_script): """Test error handling when script has errors.""" python_path = sys.executable # Create a script with syntax error error_script = simple_script.parent / 'error_script.py' error_script.write_text('x = 10\ny = \n') # Syntax error try: with DAPSyncWrapper() as wrapper: # This should handle the error gracefully try: wrapper.initialize_and_launch( python_path=python_path, script_path=error_script, args=[], env=None, cwd=None, ) response = wrapper.run_to_breakpoint( file=str(error_script), line=1, timeout=5.0, ) # Should indicate error assert response.hit is False except Exception as e: # Acceptable - errors should be caught assert e is not None finally: error_script.unlink(missing_ok=True) if __name__ == '__main__': pytest.main([__file__, '-v'])

Latest Blog Posts

MCP directory API

We provide all the information about MCP servers via our MCP API.

curl -X GET 'https://glama.ai/api/mcp/v1/servers/Kaina3/Debug-MCP'

If you have feedback or need assistance with the MCP directory API, please join our Discord server