82 lines
2.6 KiB
Python
82 lines
2.6 KiB
Python
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 == []
|