import asyncio import pytest from bleak.exc import BleakDeviceNotFoundError import k1link.device_plugins.xgrids_k1.ble.ap_activation as ap_module import k1link.device_plugins.xgrids_k1.ble.scanner as scanner_module from k1link.device_plugins.xgrids_k1.ble.ap_activation import ( COMMAND_OFFSET, ENABLE_AP_COMMAND, FRAME_LENGTH, build_ap_activation_frame, is_ap_ready_status, ) from k1link.device_plugins.xgrids_k1.ble.wifi_provisioning import WifiStatus def test_build_ap_activation_frame_matches_reviewed_lixelgo_layout() -> None: frame = build_ap_activation_frame() assert len(frame) == FRAME_LENGTH == 100 assert frame[COMMAND_OFFSET] == ENABLE_AP_COMMAND == 1 assert frame[:COMMAND_OFFSET] == bytes(COMMAND_OFFSET) def _status(*, reserved: int) -> WifiStatus: return { "value_length": 52, "mode": "WIFI_AP", "ipv4": "192.168.56.1", "status_code": 1, "reserved": reserved, "trailer_hex": "", } def test_ap_control_mode_without_ready_flag_is_not_ready() -> None: assert not is_ap_ready_status(_status(reserved=0)) def test_ap_ready_requires_the_reviewed_byte_51_flag() -> None: assert is_ap_ready_status(_status(reserved=1)) def test_ap_activation_requires_fresh_rediscovery_before_connecting( monkeypatch: pytest.MonkeyPatch, ) -> None: device_id = "synthetic-corebluetooth-uuid" stale_handle = object() rediscovery_calls: list[tuple[str, float]] = [] client_calls: list[object] = [] async def missing_device(address: str, *, timeout: float) -> None: rediscovery_calls.append((address, timeout)) return None class ForbiddenClient: def __init__(self, device: object, **_kwargs: object) -> None: client_calls.append(device) raise AssertionError("a failed fresh discovery must stop before the BLE write session") # Seed the real retained-handle cache so the test fails if the mutating # path ever regresses to discovered_device(...)-first behavior. monkeypatch.setitem(scanner_module._runtime_handles, device_id, stale_handle) # noqa: SLF001 monkeypatch.setattr( ap_module.BleakScanner, "find_device_by_address", missing_device, ) monkeypatch.setattr(ap_module, "BleakClient", ForbiddenClient) with pytest.raises(BleakDeviceNotFoundError): asyncio.run( ap_module.activate_device_ap_once( device_id, timeout_seconds=1.0, ) ) assert rediscovery_calls == [(device_id, 1.0)] assert client_calls == []