core/tests/components/anova/conftest.py

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