# E2E Test Scenarios ## Overview Tests require both MCP servers running simultaneously: - **mcbluetooth** — controls the Linux BlueZ stack (host side) - **mcbluetooth-esp32** — controls the ESP32 peripheral (device side) An LLM orchestrates both servers, issuing tool calls to each side to execute the test flow. ## Prerequisites ```bash # Terminal 1: Start ESP32 MCP server ESP32_SERIAL_PORT=/dev/ttyUSB4 uvx mcbluetooth-esp32 # Terminal 2: mcbluetooth is already running (or add to Claude Code config) ``` --- ## Test 1: SSP Just Works Both devices have `no_io` capability — pairing auto-completes without user interaction. ### Flow ```python # ESP32 side: Configure as headset (no_io) esp32_load_persona("headset") # io_cap=no_io → Just Works esp32_classic_enable() esp32_classic_set_discoverable(True) # Linux side: Discover and pair bt_scan(adapter="hci0", mode="classic", timeout=10) # → Find "BT Headset" in scan results bt_pair(adapter="hci0", address="D8:13:2A:7F:47:C0", pairing_mode="auto") # Just Works: auto-accepts on both sides # Verify esp32_wait_event("pair_complete", timeout=15) # → {"address": "XX:XX:XX:XX:XX:XX", "success": true} bt_device_info(adapter="hci0", address="D8:13:2A:7F:47:C0") # → paired: true ``` ### Expected Result - Pairing completes without any passkey exchange - Both sides report success --- ## Test 2: SSP Numeric Comparison Both devices have `display_yesno` — both display a 6-digit passkey that must match. ### Flow ```python # ESP32 side: Configure as phone (keyboard_display → numeric comparison) esp32_load_persona("phone") esp32_classic_enable() esp32_classic_set_discoverable(True) # Linux side: Scan and initiate pairing bt_scan(adapter="hci0", mode="classic") bt_pair(adapter="hci0", address="D8:13:2A:7F:47:C0", pairing_mode="interactive") # Both sides receive the passkey esp32_wait_event("pair_request", timeout=15) # → {"type": "numeric_comparison", "passkey": 123456, "address": "..."} bt_pairing_status() # → passkey: 123456 (should match ESP32's passkey!) # Both sides confirm esp32_classic_pair_respond(address="XX:XX:XX:XX:XX:XX", accept=True) bt_pair_confirm(adapter="hci0", address="D8:13:2A:7F:47:C0", accept=True) # Verify esp32_wait_event("pair_complete") # → {"success": true} ``` ### Expected Result - Both sides display the same 6-digit passkey - After both confirm, pairing succeeds --- ## Test 3: SSP Passkey Entry One side displays a passkey, the other must enter it. ### Flow (ESP32 displays, Linux enters) ```python # ESP32: display_only → shows passkey esp32_configure(io_cap="display_only") esp32_classic_enable() esp32_classic_set_discoverable(True) # Linux: Initiate pairing bt_pair(adapter="hci0", address="D8:13:2A:7F:47:C0", pairing_mode="interactive") # ESP32 displays passkey esp32_wait_event("pair_request") # → {"type": "passkey_display", "passkey": 654321} # Linux enters the passkey bt_pair_confirm(adapter="hci0", address="D8:13:2A:7F:47:C0", passkey=654321, accept=True) # Verify esp32_wait_event("pair_complete") # → {"success": true} ``` ### Flow (Linux displays, ESP32 enters) ```python # ESP32: keyboard_only → must enter passkey esp32_configure(io_cap="keyboard_only") esp32_classic_enable() esp32_classic_set_discoverable(True) # Linux initiates pairing — Linux displays passkey bt_pair(adapter="hci0", address="D8:13:2A:7F:47:C0", pairing_mode="interactive") bt_pairing_status() # → passkey: 789012 (displayed on Linux) # ESP32 receives passkey entry request esp32_wait_event("pair_request") # → {"type": "passkey_entry"} # ESP32 enters the passkey shown on Linux esp32_classic_pair_respond(address="XX:XX:XX:XX:XX:XX", accept=True, passkey=789012) # Verify esp32_wait_event("pair_complete") ``` --- ## Test 4: Legacy PIN ESP32 configured with a legacy PIN code. ```python esp32_configure(pin_code="1234") esp32_classic_enable() esp32_classic_set_discoverable(True) bt_pair(adapter="hci0", address="D8:13:2A:7F:47:C0", pairing_mode="interactive") # Linux enters PIN bt_pair_confirm(adapter="hci0", address="D8:13:2A:7F:47:C0", pin="1234", accept=True) ``` --- ## Test 5: BLE GATT Read/Write ESP32 creates an Environmental Sensing service with a Temperature characteristic. ```python # ESP32: Set up as sensor esp32_load_persona("sensor") esp32_gatt_add_service(uuid="0000181a-0000-1000-8000-00805f9b34fb", primary=True) # → {"service_handle": 40} esp32_gatt_add_characteristic( service_handle=40, uuid="00002a6e-0000-1000-8000-00805f9b34fb", # Temperature properties=["read", "notify"], value="e803" # 25.0°C (0x03e8 = 1000 in little-endian → 10.00°C? or raw) ) # → {"char_handle": 42} esp32_ble_advertise(enable=True) # Linux: Scan, connect, read bt_ble_scan(adapter="hci0", timeout=5) bt_connect(adapter="hci0", address="D8:13:2A:7F:47:C0") bt_ble_read(adapter="hci0", address="D8:13:2A:7F:47:C0", char_uuid="00002a6e-0000-1000-8000-00805f9b34fb") # → {"hex": "e803", ...} # ESP32: Update value esp32_gatt_set_value(char_handle=42, value="f003") # 25.5°C ``` --- ## Test 6: BLE GATT Notifications ```python # Linux: Subscribe to temperature notifications bt_ble_notify(adapter="hci0", address="D8:13:2A:7F:47:C0", char_uuid="00002a6e-0000-1000-8000-00805f9b34fb", enable=True) # ESP32: Verify subscription esp32_wait_event("gatt_subscribe") # → {"char_handle": 42, "subscribed": true} # ESP32: Update and notify esp32_gatt_set_value(char_handle=42, value="0004") esp32_gatt_notify(char_handle=42) # Linux should receive the updated value bt_ble_read(adapter="hci0", address="D8:13:2A:7F:47:C0", char_uuid="00002a6e-0000-1000-8000-00805f9b34fb") # → {"hex": "0004"} ``` --- ## Test 7: Persona Switching Verify device identity changes are visible from the Linux side. ```python # Load different personas and scan each time for persona in ["headset", "speaker", "keyboard", "sensor", "phone", "bare"]: esp32_load_persona(persona) esp32_classic_enable() esp32_classic_set_discoverable(True) bt_scan(adapter="hci0", mode="both", timeout=5) # Verify device name and class match persona definition esp32_classic_disable() ``` --- ## Running Tests ```bash # Unit tests only (no hardware needed) make test-unit # Integration tests (requires ESP32 on /dev/ttyUSB4) ESP32_SERIAL_PORT=/dev/ttyUSB4 make test-integration # Full suite make test ``` ## Test Matrix | Test | SSP Mode | ESP32 IO Cap | Linux IO Cap | Auto? | |------|----------|--------------|--------------|-------| | Just Works | NoInputNoOutput | no_io | no_io | Yes | | Numeric Comparison | NumericComparison | keyboard_display | display_yesno | No (confirm) | | Passkey Entry (ESP32 displays) | PasskeyEntry | display_only | keyboard_only | No (enter) | | Passkey Entry (Linux displays) | PasskeyEntry | keyboard_only | display_only | No (enter) | | Legacy PIN | LegacyPIN | n/a | n/a | No (PIN) |