Skip to content

Commit a88e961

Browse files
committed
feat: Add a trait for sending commands
1 parent a6f889d commit a88e961

File tree

2 files changed

+82
-0
lines changed

2 files changed

+82
-0
lines changed
Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
from typing import Any
2+
3+
from roborock import RoborockCommand
4+
5+
6+
class CommandTrait:
7+
"""Trait for sending commands to Roborock devices."""
8+
9+
def __post_init__(self) -> None:
10+
"""Post-initialization to set up the RPC channel.
11+
12+
This is called automatically after the dataclass is initialized by the
13+
device setup code.
14+
"""
15+
self._rpc_channel = None
16+
17+
async def send(self, command: RoborockCommand, params: dict[str, Any] | None = None) -> None:
18+
"""Send a command to the device."""
19+
if not self._rpc_channel:
20+
raise ValueError("Device trait in invalid state")
21+
await self._rpc_channel.send_command(command, params=params)
Lines changed: 61 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,61 @@
1+
"""Tests for the CommandTrait class."""
2+
3+
from unittest.mock import AsyncMock
4+
5+
import pytest
6+
7+
from roborock.devices.traits.v1.command import CommandTrait
8+
from roborock.exceptions import RoborockException
9+
from roborock.roborock_typing import RoborockCommand
10+
11+
12+
@pytest.fixture(name="command_trait")
13+
def command_trait_fixture() -> CommandTrait:
14+
"""Create a CommandTrait instance with a mocked RPC channel."""
15+
trait = CommandTrait()
16+
trait._rpc_channel = AsyncMock() # type: ignore[assignment]
17+
return trait
18+
19+
20+
async def test_send_command_success(command_trait: CommandTrait) -> None:
21+
"""Test successfully sending a command."""
22+
mock_rpc_channel = command_trait._rpc_channel
23+
assert mock_rpc_channel is not None
24+
mock_rpc_channel.send_command.return_value = {"result": "ok"}
25+
26+
# Call the method
27+
result = await command_trait.send(RoborockCommand.APP_START)
28+
29+
# Verify the result
30+
assert result is None
31+
32+
# Verify the RPC call was made correctly
33+
mock_rpc_channel.send_command.assert_called_once_with(RoborockCommand.APP_START, params=None)
34+
35+
36+
async def test_send_command_with_params(command_trait: CommandTrait) -> None:
37+
"""Test successfully sending a command with parameters."""
38+
mock_rpc_channel = command_trait._rpc_channel
39+
assert mock_rpc_channel is not None
40+
mock_rpc_channel.send_command.return_value = {"result": "ok"}
41+
params = {"segments": [1, 2, 3]}
42+
43+
# Call the method
44+
result = await command_trait.send(RoborockCommand.APP_SEGMENT_CLEAN, params)
45+
46+
# Verify the result
47+
assert result is None
48+
49+
# Verify the RPC call was made correctly
50+
mock_rpc_channel.send_command.assert_called_once_with(RoborockCommand.APP_SEGMENT_CLEAN, params=params)
51+
52+
53+
async def test_send_command_propagates_exception(command_trait: CommandTrait) -> None:
54+
"""Test that exceptions from RPC channel are propagated."""
55+
mock_rpc_channel = command_trait._rpc_channel
56+
assert mock_rpc_channel is not None
57+
mock_rpc_channel.send_command.side_effect = RoborockException("Communication error")
58+
59+
# Verify the exception is propagated
60+
with pytest.raises(RoborockException, match="Communication error"):
61+
await command_trait.send(RoborockCommand.APP_START)

0 commit comments

Comments
 (0)