mirror of https://github.com/home-assistant/core
278 lines
9.0 KiB
Python
278 lines
9.0 KiB
Python
"""Common fixtures for Anova."""
|
|
|
|
from __future__ import annotations
|
|
|
|
import asyncio
|
|
from dataclasses import dataclass
|
|
import json
|
|
from typing import Any
|
|
from unittest.mock import AsyncMock, patch
|
|
|
|
from aiohttp import ClientSession
|
|
from anova_wifi import (
|
|
AnovaApi,
|
|
AnovaWebsocketHandler,
|
|
InvalidLogin,
|
|
NoDevicesFound,
|
|
WebsocketFailure,
|
|
)
|
|
import pytest
|
|
|
|
from homeassistant.core import HomeAssistant
|
|
|
|
DUMMY_ID = "anova_id"
|
|
|
|
|
|
@dataclass
|
|
class MockedanovaWebsocketMessage:
|
|
"""Mock the websocket message for Anova."""
|
|
|
|
input_data: dict[str, Any]
|
|
data: str = ""
|
|
|
|
def __post_init__(self) -> None:
|
|
"""Set up data after creation."""
|
|
self.data = json.dumps(self.input_data)
|
|
|
|
|
|
class MockedAnovaWebsocketStream:
|
|
"""Mock the websocket stream for Anova."""
|
|
|
|
def __init__(self, messages: list[MockedanovaWebsocketMessage]) -> None:
|
|
"""Initialize a Anova Websocket Stream that can be manipulated for tests."""
|
|
self.messages = messages
|
|
|
|
def __aiter__(self) -> MockedAnovaWebsocketStream:
|
|
"""Handle async iteration."""
|
|
return self
|
|
|
|
async def __anext__(self) -> MockedanovaWebsocketMessage:
|
|
"""Get the next message in the websocket stream."""
|
|
if self.messages:
|
|
return self.messages.pop(0)
|
|
raise StopAsyncIteration
|
|
|
|
def clear(self) -> None:
|
|
"""Clear the Websocket stream."""
|
|
self.messages.clear()
|
|
|
|
|
|
class MockedAnovaWebsocketHandler(AnovaWebsocketHandler):
|
|
"""Mock the Anova websocket handler."""
|
|
|
|
def __init__(
|
|
self,
|
|
firebase_jwt: str,
|
|
jwt: str,
|
|
session: ClientSession,
|
|
connect_messages: list[MockedanovaWebsocketMessage],
|
|
post_connect_messages: list[MockedanovaWebsocketMessage],
|
|
) -> None:
|
|
"""Initialize the websocket handler with whatever messages you want."""
|
|
super().__init__(firebase_jwt, jwt, session)
|
|
self.connect_messages = connect_messages
|
|
self.post_connect_messages = post_connect_messages
|
|
|
|
async def connect(self) -> None:
|
|
"""Create a future for the message listener."""
|
|
self.ws = MockedAnovaWebsocketStream(self.connect_messages)
|
|
await self.message_listener()
|
|
self.ws = MockedAnovaWebsocketStream(self.post_connect_messages)
|
|
# RUF006 ignored as it replicates the parent library
|
|
# https://github.com/Lash-L/anova_wifi/issues/35
|
|
asyncio.ensure_future(self.message_listener()) # noqa: RUF006
|
|
|
|
|
|
def anova_api_mock(
|
|
connect_messages: list[MockedanovaWebsocketMessage] | None = None,
|
|
post_connect_messages: list[MockedanovaWebsocketMessage] | None = None,
|
|
) -> AsyncMock:
|
|
"""Mock the api for Anova."""
|
|
api_mock = AsyncMock()
|
|
|
|
async def authenticate_side_effect() -> None:
|
|
api_mock.jwt = "my_test_jwt"
|
|
api_mock._firebase_jwt = "my_test_firebase_jwt"
|
|
|
|
async def create_websocket_side_effect() -> None:
|
|
api_mock.websocket_handler = MockedAnovaWebsocketHandler(
|
|
firebase_jwt=api_mock._firebase_jwt,
|
|
jwt=api_mock.jwt,
|
|
session=AsyncMock(),
|
|
connect_messages=connect_messages
|
|
if connect_messages is not None
|
|
else [
|
|
MockedanovaWebsocketMessage(
|
|
{
|
|
"command": "EVENT_APC_WIFI_LIST",
|
|
"payload": [
|
|
{
|
|
"cookerId": DUMMY_ID,
|
|
"type": "a5",
|
|
"pairedAt": "2023-08-12T02:33:20.917716Z",
|
|
"name": "Anova Precision Cooker",
|
|
}
|
|
],
|
|
}
|
|
),
|
|
],
|
|
post_connect_messages=post_connect_messages
|
|
if post_connect_messages is not None
|
|
else [
|
|
MockedanovaWebsocketMessage(
|
|
{
|
|
"command": "EVENT_APC_STATE",
|
|
"payload": {
|
|
"cookerId": DUMMY_ID,
|
|
"state": {
|
|
"boot-id": "8620610049456548422",
|
|
"job": {
|
|
"cook-time-seconds": 0,
|
|
"id": "8759286e3125b0c547",
|
|
"mode": "IDLE",
|
|
"ota-url": "",
|
|
"target-temperature": 54.72,
|
|
"temperature-unit": "F",
|
|
},
|
|
"job-status": {
|
|
"cook-time-remaining": 0,
|
|
"job-start-systick": 599679,
|
|
"provisioning-pairing-code": 7514,
|
|
"state": "",
|
|
"state-change-systick": 599679,
|
|
},
|
|
"pin-info": {
|
|
"device-safe": 0,
|
|
"water-leak": 0,
|
|
"water-level-critical": 0,
|
|
"water-temp-too-high": 0,
|
|
},
|
|
"system-info": {
|
|
"class": "A5",
|
|
"firmware-version": "2.2.0",
|
|
"type": "RA2L1-128",
|
|
},
|
|
"system-info-details": {
|
|
"firmware-version-raw": "VM178_A_02.02.00_MKE15-128",
|
|
"systick": 607026,
|
|
"version-string": "VM171_A_02.02.00 RA2L1-128",
|
|
},
|
|
"temperature-info": {
|
|
"heater-temperature": 22.37,
|
|
"triac-temperature": 36.04,
|
|
"water-temperature": 18.33,
|
|
},
|
|
},
|
|
},
|
|
}
|
|
),
|
|
],
|
|
)
|
|
await api_mock.websocket_handler.connect()
|
|
if not api_mock.websocket_handler.devices:
|
|
raise NoDevicesFound("No devices were found on the websocket.")
|
|
|
|
api_mock.authenticate.side_effect = authenticate_side_effect
|
|
api_mock.create_websocket.side_effect = create_websocket_side_effect
|
|
return api_mock
|
|
|
|
|
|
@pytest.fixture
|
|
async def anova_api(
|
|
hass: HomeAssistant,
|
|
) -> AnovaApi:
|
|
"""Mock the api for Anova."""
|
|
api_mock = anova_api_mock()
|
|
|
|
with (
|
|
patch("homeassistant.components.anova.AnovaApi", return_value=api_mock),
|
|
patch(
|
|
"homeassistant.components.anova.config_flow.AnovaApi", return_value=api_mock
|
|
),
|
|
):
|
|
api = AnovaApi(
|
|
None,
|
|
"sample@gmail.com",
|
|
"sample",
|
|
)
|
|
yield api
|
|
|
|
|
|
@pytest.fixture
|
|
async def anova_api_no_devices(
|
|
hass: HomeAssistant,
|
|
) -> AnovaApi:
|
|
"""Mock the api for Anova with no online devices."""
|
|
api_mock = anova_api_mock(connect_messages=[], post_connect_messages=[])
|
|
|
|
with (
|
|
patch("homeassistant.components.anova.AnovaApi", return_value=api_mock),
|
|
patch(
|
|
"homeassistant.components.anova.config_flow.AnovaApi", return_value=api_mock
|
|
),
|
|
):
|
|
api = AnovaApi(
|
|
None,
|
|
"sample@gmail.com",
|
|
"sample",
|
|
)
|
|
yield api
|
|
|
|
|
|
@pytest.fixture
|
|
async def anova_api_wrong_login(
|
|
hass: HomeAssistant,
|
|
) -> AnovaApi:
|
|
"""Mock the api for Anova with a wrong login."""
|
|
api_mock = anova_api_mock()
|
|
|
|
async def authenticate_side_effect():
|
|
raise InvalidLogin
|
|
|
|
api_mock.authenticate.side_effect = authenticate_side_effect
|
|
|
|
with patch("homeassistant.components.anova.AnovaApi", return_value=api_mock):
|
|
api = AnovaApi(
|
|
None,
|
|
"sample@gmail.com",
|
|
"sample",
|
|
)
|
|
yield api
|
|
|
|
|
|
@pytest.fixture
|
|
async def anova_api_no_data(
|
|
hass: HomeAssistant,
|
|
) -> AnovaApi:
|
|
"""Mock the api for Anova with a wrong login."""
|
|
api_mock = anova_api_mock(post_connect_messages=[])
|
|
|
|
with patch("homeassistant.components.anova.AnovaApi", return_value=api_mock):
|
|
api = AnovaApi(
|
|
None,
|
|
"sample@gmail.com",
|
|
"sample",
|
|
)
|
|
yield api
|
|
|
|
|
|
@pytest.fixture
|
|
async def anova_api_websocket_failure(
|
|
hass: HomeAssistant,
|
|
) -> AnovaApi:
|
|
"""Mock the api for Anova with a websocket failure."""
|
|
api_mock = anova_api_mock()
|
|
|
|
async def create_websocket_side_effect():
|
|
raise WebsocketFailure
|
|
|
|
api_mock.create_websocket.side_effect = create_websocket_side_effect
|
|
|
|
with patch("homeassistant.components.anova.AnovaApi", return_value=api_mock):
|
|
api = AnovaApi(
|
|
None,
|
|
"sample@gmail.com",
|
|
"sample",
|
|
)
|
|
yield api
|