"""Replay the captured reply through asynchronous FFA1/FFA2 operations.""" import queue import sys import types import unittest from unittest.mock import AsyncMock, patch from controller.ble_link import NOTIFY, SERVICE, WRITE, BLELink class BLELinkTests(unittest.TestCase): def run_peer(self, write_error=False): operations = [] closed = [] write = types.SimpleNamespace(uuid=WRITE, properties=["write"]) notify = types.SimpleNamespace(uuid=NOTIFY, properties=["notify"]) service = types.SimpleNamespace( get_characteristic=lambda uuid: {WRITE: write, NOTIFY: notify}.get(uuid) ) class Events(queue.Queue): def put(self, event, *args, **kwargs): super().put(event, *args, **kwargs) if event[0] == "ready": link.stop() events = Events() link = BLELink("12:34:56:78:9A:BC", events) class Peer: services = types.SimpleNamespace( get_service=lambda uuid: service if uuid == SERVICE else None ) is_connected = True async def __aenter__(self): return self async def __aexit__(self, *args): closed.append(True) async def start_notify(self, characteristic, callback): operations.append(("subscribe", characteristic.uuid)) self.callback = callback async def write_gatt_char(self, characteristic, data, response): operations.append(("write", characteristic.uuid, data, response)) if write_error: raise OSError("GATT write failed") if data[6:-1] == b"\x01": self.callback(notify, bytes.fromhex("00 bb 00 01 00 00 01")) reply = bytes.fromhex("00 bb 01 1d 00 0b 02 10 01 34 30 04 01 02 04 05 01 b0") self.callback(notify, reply[:8]) self.callback(notify, reply[8:]) elif data[6:-1] == b"\x04": self.callback(notify, bytes.fromhex("00 bb 00 02 00 00 02")) scanner = types.SimpleNamespace(find_device_by_address=AsyncMock(return_value=object())) with patch.dict( sys.modules, { "bleak": types.SimpleNamespace( BleakScanner=scanner, BleakClient=lambda *a, **k: Peer() ) }, ): link.run() return operations, list(events.queue), closed def test_subscribe_before_handshake_and_reassemble_notifications(self): operations, events, closed = self.run_peer() self.assertEqual(operations[0], ("subscribe", NOTIFY)) writes = operations[1:] self.assertEqual([op[1] for op in writes], [WRITE, WRITE]) self.assertEqual( [op[2] for op in writes], [ bytes.fromhex("00 bb 01 01 00 01 01 04"), bytes.fromhex("00 bb 01 02 00 01 04 08"), ], ) self.assertTrue(all(op[3] is True for op in writes)) self.assertEqual(sum(kind == "ready" for kind, _ in events), 1) self.assertFalse(any(kind == "error" for kind, _ in events)) self.assertEqual(closed, [True]) def test_write_error_disconnects_without_enabling_controls(self): _, events, closed = self.run_peer(write_error=True) self.assertFalse(any(kind == "ready" for kind, _ in events)) self.assertTrue( any(kind == "error" and "GATT write failed" in value for kind, value in events) ) self.assertEqual(events[-1], ("closed", None)) self.assertEqual(closed, [True])