Add SKYWALKER_MOCK=1 mode for hardware-free MCP integration testing

Extract MockSkyWalker1 to shared mock_device.py (used by both unit tests
and server lifespan). Server checks SKYWALKER_MOCK env var at startup —
when set, uses mock device instead of USB hardware, enabling full MCP
transport testing via claude -p without the dongle connected.

Verified: 64 unit tests pass, claude -p integration tests exercise
identify_frequency, get_device_status, and sweep_spectrum through the
complete JSON-RPC pipeline.
This commit is contained in:
Ryan Malloy 2026-02-17 15:23:05 -07:00
parent 4a63dbbb9d
commit cbfbbf6bfe
4 changed files with 150 additions and 133 deletions

View file

@ -6,6 +6,7 @@ function accessible to LLMs. Thread-safe concurrent access via asyncio.to_thread
and a reentrant lock, following the same USBBridge pattern used by the TUI.
"""
import os
import sys
import asyncio
import threading
@ -100,9 +101,18 @@ _bridge: DeviceBridge | None = None
@asynccontextmanager
async def lifespan(server: FastMCP):
"""Open the USB device on startup, close on shutdown."""
"""Open the USB device on startup, close on shutdown.
Set SKYWALKER_MOCK=1 to use a mock device for integration testing
without USB hardware.
"""
global _bridge
dev = SkyWalker1(verbose=False)
if os.environ.get("SKYWALKER_MOCK"):
from skywalker_mcp.mock_device import MockSkyWalker1
dev = MockSkyWalker1(verbose=False)
print("skywalker-mcp: MOCK MODE — no USB hardware", file=sys.stderr)
else:
dev = SkyWalker1(verbose=False)
try:
dev.open()
dev.ensure_booted()