Skip to content

Commit 7a73268

Browse files
committed
chore: Update test coverage for protocol parsing
1 parent e0305cf commit 7a73268

File tree

3 files changed

+86
-47
lines changed

3 files changed

+86
-47
lines changed

roborock/protocols/b01_q10_protocol.py

Lines changed: 24 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -36,9 +36,11 @@ def _convert_datapoints(datapoints: dict[str, Any], message: RoborockMessage) ->
3636
"""Convert the 'dps' dictionary keys from strings to B01_Q10_DP enums."""
3737
result = {}
3838
for key, value in datapoints.items():
39-
if not isinstance(key, str):
40-
raise RoborockException(f"Invalid B01 message format: 'dps' keys should be strings for {message.payload!r}")
41-
dps = B01_Q10_DP.from_code(int(key))
39+
try:
40+
code = int(key)
41+
except ValueError as e:
42+
raise ValueError(f"dps key is not a valid integer: {e} for {message.payload!r}") from e
43+
dps = B01_Q10_DP.from_code(code)
4244
result[dps] = value
4345
return result
4446

@@ -49,16 +51,29 @@ def decode_rpc_response(message: RoborockMessage) -> dict[B01_Q10_DP, Any]:
4951
raise RoborockException("Invalid B01 message format: missing payload")
5052
try:
5153
payload = json.loads(message.payload.decode())
52-
except (json.JSONDecodeError, TypeError, UnicodeDecodeError) as e:
53-
raise RoborockException(f"Invalid B01 message payload: {e} for {message.payload!r}") from e
54+
except (json.JSONDecodeError, UnicodeDecodeError) as e:
55+
raise RoborockException(f"Invalid B01 json payload: {e} for {message.payload!r}") from e
5456

55-
datapoints = payload.get("dps", {})
57+
if (datapoints := payload.get("dps")) is None:
58+
raise RoborockException(f"Invalid B01 json payload: missing 'dps' for {message.payload!r}")
5659
if not isinstance(datapoints, dict):
5760
raise RoborockException(f"Invalid B01 message format: 'dps' should be a dictionary for {message.payload!r}")
5861

59-
result = _convert_datapoints(datapoints, message)
60-
# The COMMON response contains nested datapoints that also need conversion
62+
try:
63+
result = _convert_datapoints(datapoints, message)
64+
except ValueError as e:
65+
raise RoborockException(f"Invalid B01 message format: {e}") from e
66+
67+
# The COMMON response contains nested datapoints that also need conversion.
68+
# We will parse that here for now, but may move elsewhere as we add more
69+
# complex response parsing.
6170
if common_result := result.get(B01_Q10_DP.COMMON):
62-
common_dps_result = _convert_datapoints(common_result, message)
71+
if not isinstance(common_result, dict):
72+
raise RoborockException(f"Invalid dpCommon format: expected dict, got {type(common_result).__name__}")
73+
try:
74+
common_dps_result = _convert_datapoints(common_result, message)
75+
except ValueError as e:
76+
raise RoborockException(f"Invalid dpCommon format: {e}") from e
6377
result[B01_Q10_DP.COMMON] = common_dps_result
78+
6479
return result

tests/protocols/__snapshots__/test_b01_q10_protocol.ambr

Lines changed: 34 additions & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@
1010
'''
1111
{
1212
"dpCommon": {
13-
"90": 0
13+
"dpFault": 0
1414
}
1515
}
1616
'''
@@ -19,52 +19,52 @@
1919
'''
2020
{
2121
"dpCommon": {
22-
"104": 0,
23-
"105": false,
24-
"109": "us",
25-
"207": 0,
26-
"25": 1,
27-
"26": 74,
28-
"29": 0,
29-
"30": 0,
30-
"31": 0,
31-
"37": 1,
32-
"40": 1,
33-
"45": 0,
34-
"47": 0,
35-
"50": 0,
36-
"51": true,
37-
"53": false,
38-
"6": 0,
39-
"60": 1,
40-
"67": 0,
41-
"7": 0,
42-
"76": 0,
43-
"78": 0,
44-
"79": {
22+
"dpBreakpointClean": 0,
23+
"dpValleyPointCharging": false,
24+
"dpRobotCountryCode": "us",
25+
"dpUserPlan": 0,
26+
"dpNotDisturb": 1,
27+
"dpVolume": 74,
28+
"dpTotalCleanArea": 0,
29+
"dpTotalCleanCount": 0,
30+
"dpTotalCleanTime": 0,
31+
"dpDustSwitch": 1,
32+
"dpMopState": 1,
33+
"dpAutoBoost": 0,
34+
"dpChildLock": 0,
35+
"dpDustSetting": 0,
36+
"dpMapSaveSwitch": true,
37+
"dpRecendCleanRecord": false,
38+
"dpCleanTime": 0,
39+
"dpMultiMapSwitch": 1,
40+
"dpSensorLife": 0,
41+
"dpCleanArea": 0,
42+
"dpCarpetCleanType": 0,
43+
"dpCleanLine": 0,
44+
"dpTimeZone": {
4545
"timeZoneCity": "America/Los_Angeles",
4646
"timeZoneSec": -28800
4747
},
48-
"80": 0,
49-
"81": {
48+
"dpAreaUnit": 0,
49+
"dpNetInfo": {
5050
"ipAdress": "1.1.1.2",
5151
"mac": "99:AA:88:BB:77:CC",
5252
"signal": -50,
5353
"wifiName": "wifi-network-name"
5454
},
55-
"83": 1,
56-
"86": 1,
57-
"87": 100,
58-
"88": 0,
59-
"90": 0,
60-
"92": {
55+
"dpRobotType": 1,
56+
"dpLineLaserObstacleAvoidance": 1,
57+
"dpCleanProgess": 100,
58+
"dpGroundClean": 0,
59+
"dpFault": 0,
60+
"dpNotDisturbExpand": {
6161
"disturb_dust_enable": 1,
6262
"disturb_light": 1,
6363
"disturb_resume_clean": 1,
6464
"disturb_voice": 1
6565
},
66-
"93": 1,
67-
"96": 0
66+
"dpTimerType": 1,
67+
"dpAddCleanState": 0
6868
},
6969
"dpStatus": 8,
7070
"dpBattery": 100,

tests/protocols/test_b01_q10_protocol.py

Lines changed: 28 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -6,12 +6,11 @@
66
from typing import Any
77

88
import pytest
9-
from Crypto.Cipher import AES
10-
from Crypto.Util.Padding import unpad
119
from freezegun import freeze_time
1210
from syrupy import SnapshotAssertion
1311

1412
from roborock.data.b01_q10.b01_q10_code_mappings import B01_Q10_DP
13+
from roborock.exceptions import RoborockException
1514
from roborock.protocols.b01_q10_protocol import (
1615
decode_rpc_response,
1716
encode_mqtt_payload,
@@ -49,6 +48,32 @@ def test_decode_rpc_payload(filename: str, snapshot: SnapshotAssertion) -> None:
4948
assert json.dumps(decoded_message, indent=2) == snapshot
5049

5150

51+
@pytest.mark.parametrize(
52+
("payload", "expected_error_message"),
53+
[
54+
(b"", "missing payload"),
55+
(b"n", "Invalid B01 json payload"),
56+
(b"{}", "missing 'dps'"),
57+
(b'{"dps": []}', "'dps' should be a dictionary"),
58+
(b'{"dps": {"not_a_number": 123}}', "dps key is not a valid integer"),
59+
(b'{"dps": {"101": 123}}', "Invalid dpCommon format: expected dict"),
60+
(b'{"dps": {"101": {"not_a_number": 123}}}', "Invalid dpCommon format: dps key is not a valid intege"),
61+
],
62+
)
63+
def test_decode_invalid_rpc_payload(payload: bytes, expected_error_message: str) -> None:
64+
"""Test decoding a B01 RPC response protocol message."""
65+
message = RoborockMessage(
66+
protocol=RoborockMessageProtocol.RPC_RESPONSE,
67+
payload=payload,
68+
seq=12750,
69+
version=b"B01",
70+
random=97431,
71+
timestamp=1652547161,
72+
)
73+
with pytest.raises(RoborockException, match=expected_error_message):
74+
decode_rpc_response(message)
75+
76+
5277
@pytest.mark.parametrize(
5378
("command", "params"),
5479
[
@@ -63,7 +88,6 @@ def test_encode_mqtt_payload(command: B01_Q10_DP, params: dict[str, Any]) -> Non
6388
assert message.protocol == RoborockMessageProtocol.RPC_REQUEST
6489
assert message.version == b"B01"
6590
assert message.payload is not None
66-
unpadded = unpad(message.payload, AES.block_size)
67-
decoded_json = json.loads(unpadded.decode("utf-8"))
91+
decoded_json = json.loads(message.payload.decode("utf-8"))
6892

6993
assert decoded_json == {"dps": {"102": {}}}

0 commit comments

Comments
 (0)