Skip to content

Commit c8dcd34

Browse files
authored
chore: add local api test coverage (#284)
1 parent be52b3d commit c8dcd34

File tree

3 files changed

+82
-1
lines changed

3 files changed

+82
-1
lines changed

tests/conftest.py

Lines changed: 44 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,8 @@
1+
import asyncio
12
import io
23
import logging
34
import re
5+
from asyncio import Protocol
46
from collections.abc import Callable, Generator
57
from queue import Queue
68
from typing import Any
@@ -11,8 +13,9 @@
1113

1214
from roborock import HomeData, UserData
1315
from roborock.containers import DeviceData
16+
from roborock.version_1_apis.roborock_local_client_v1 import RoborockLocalClientV1
1417
from roborock.version_1_apis.roborock_mqtt_client_v1 import RoborockMqttClientV1
15-
from tests.mock_data import HOME_DATA_RAW, USER_DATA
18+
from tests.mock_data import HOME_DATA_RAW, TEST_LOCAL_API_HOST, USER_DATA
1619

1720
_LOGGER = logging.getLogger(__name__)
1821

@@ -191,3 +194,43 @@ def mock_rest() -> aioresponses:
191194
payload={"api": None, "code": 200, "result": HOME_DATA_RAW, "status": "ok", "success": True},
192195
)
193196
yield mocked
197+
198+
199+
@pytest.fixture(name="mock_create_local_connection")
200+
def create_local_connection_fixture(request_handler: RequestHandler) -> Generator[None, None, None]:
201+
"""Fixture that overrides the transport creation to wire it up to the mock socket."""
202+
203+
async def create_connection(protocol_factory: Callable[[], Protocol], *args) -> tuple[Any, Any]:
204+
protocol = protocol_factory()
205+
206+
def handle_write(data: bytes) -> None:
207+
_LOGGER.debug("Received: %s", data)
208+
response = request_handler(data)
209+
if response is not None:
210+
_LOGGER.debug("Replying with %s", response)
211+
loop = asyncio.get_running_loop()
212+
loop.call_soon(protocol.data_received, response)
213+
214+
closed = asyncio.Event()
215+
216+
mock_transport = Mock()
217+
mock_transport.write = handle_write
218+
mock_transport.close = closed.set
219+
mock_transport.is_reading = lambda: not closed.is_set()
220+
221+
return (mock_transport, "proto")
222+
223+
with patch("roborock.api.get_running_loop_or_create_one") as mock_loop:
224+
mock_loop.return_value.create_connection.side_effect = create_connection
225+
yield
226+
227+
228+
@pytest.fixture(name="local_client")
229+
def local_client_fixture(mock_create_local_connection: None) -> Generator[RoborockLocalClientV1, None, None]:
230+
home_data = HomeData.from_dict(HOME_DATA_RAW)
231+
device_info = DeviceData(
232+
device=home_data.devices[0],
233+
model=home_data.products[0].model,
234+
host=TEST_LOCAL_API_HOST,
235+
)
236+
yield RoborockLocalClientV1(device_info)

tests/mock_data.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -347,3 +347,4 @@
347347
GET_CODE_RESPONSE = {"code": 200, "msg": "success", "data": None}
348348
HASHED_USER = hashlib.md5((USER_ID + ":" + K_VALUE).encode()).hexdigest()[2:10]
349349
MQTT_PUBLISH_TOPIC = f"rr/m/o/{USER_ID}/{HASHED_USER}/{PRODUCT_ID}"
350+
TEST_LOCAL_API_HOST = "1.1.1.1"

tests/test_local_api_v1.py

Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
"""Tests for the Roborock Local Client V1."""
2+
3+
from queue import Queue
4+
5+
from roborock.protocol import MessageParser
6+
from roborock.roborock_message import RoborockMessage, RoborockMessageProtocol
7+
from roborock.version_1_apis import RoborockLocalClientV1
8+
9+
from .mock_data import LOCAL_KEY
10+
11+
12+
def build_rpc_response(protocol: RoborockMessageProtocol, seq: int) -> bytes:
13+
"""Build an encoded RPC response message."""
14+
message = RoborockMessage(
15+
protocol=protocol,
16+
random=23,
17+
seq=seq,
18+
payload=b"ignored",
19+
)
20+
return MessageParser.build(message, local_key=LOCAL_KEY)
21+
22+
23+
async def test_async_connect(
24+
local_client: RoborockLocalClientV1,
25+
received_requests: Queue,
26+
response_queue: Queue,
27+
):
28+
"""Test that we can connect to the Roborock device."""
29+
response_queue.put(build_rpc_response(RoborockMessageProtocol.HELLO_RESPONSE, 1))
30+
response_queue.put(build_rpc_response(RoborockMessageProtocol.PING_RESPONSE, 2))
31+
32+
await local_client.async_connect()
33+
assert local_client.is_connected()
34+
assert received_requests.qsize() == 2
35+
36+
await local_client.async_disconnect()
37+
assert not local_client.is_connected()

0 commit comments

Comments
 (0)