mirror of
https://github.com/home-assistant/core.git
synced 2025-06-06 14:17:06 +00:00
Test storage save and load for evohome (#122510)
* test storage save and load * fix bug exposed by test * refactor test * add JSON for test account/location * create helpers to load JSON * refactor test * baseline refactor * tweak * update requiremenst * rationalise code * remove conditional in test * refactor test * mypy fix * tweak tests * working test * working test 4 * working test 5 * add typed dicts * working dtms * lint * fix dtm asserts * doc strings * list * tweak conditional * tweak test data sets to extend coverage * leverage conftest.py for subsequent tests * revert test storage * revert part two * rename symbols * remove anachronism * stop unwanted DNS lookup * Clean up type ignores * Format --------- Co-authored-by: Martin Hjelmare <marhje52@gmail.com>
This commit is contained in:
parent
6684f61a54
commit
bb31fc1ec7
@ -433,6 +433,7 @@ build.json @home-assistant/supervisor
|
|||||||
/homeassistant/components/evil_genius_labs/ @balloob
|
/homeassistant/components/evil_genius_labs/ @balloob
|
||||||
/tests/components/evil_genius_labs/ @balloob
|
/tests/components/evil_genius_labs/ @balloob
|
||||||
/homeassistant/components/evohome/ @zxdavb
|
/homeassistant/components/evohome/ @zxdavb
|
||||||
|
/tests/components/evohome/ @zxdavb
|
||||||
/homeassistant/components/ezviz/ @RenierM26 @baqs
|
/homeassistant/components/ezviz/ @RenierM26 @baqs
|
||||||
/tests/components/ezviz/ @RenierM26 @baqs
|
/tests/components/ezviz/ @RenierM26 @baqs
|
||||||
/homeassistant/components/faa_delays/ @ntilley905
|
/homeassistant/components/faa_delays/ @ntilley905
|
||||||
|
@ -717,6 +717,9 @@ eternalegypt==0.0.16
|
|||||||
# homeassistant.components.eufylife_ble
|
# homeassistant.components.eufylife_ble
|
||||||
eufylife-ble-client==0.1.8
|
eufylife-ble-client==0.1.8
|
||||||
|
|
||||||
|
# homeassistant.components.evohome
|
||||||
|
evohome-async==0.4.20
|
||||||
|
|
||||||
# homeassistant.components.bryant_evolution
|
# homeassistant.components.bryant_evolution
|
||||||
evolutionhttp==0.0.18
|
evolutionhttp==0.0.18
|
||||||
|
|
||||||
|
1
tests/components/evohome/__init__.py
Normal file
1
tests/components/evohome/__init__.py
Normal file
@ -0,0 +1 @@
|
|||||||
|
"""The tests for the evohome integration."""
|
111
tests/components/evohome/conftest.py
Normal file
111
tests/components/evohome/conftest.py
Normal file
@ -0,0 +1,111 @@
|
|||||||
|
"""Fixtures and helpers for the evohome tests."""
|
||||||
|
|
||||||
|
from __future__ import annotations
|
||||||
|
|
||||||
|
from datetime import datetime, timedelta
|
||||||
|
from typing import Any, Final
|
||||||
|
from unittest.mock import MagicMock, patch
|
||||||
|
|
||||||
|
from aiohttp import ClientSession
|
||||||
|
from evohomeasync2 import EvohomeClient
|
||||||
|
from evohomeasync2.broker import Broker
|
||||||
|
import pytest
|
||||||
|
|
||||||
|
from homeassistant.components.evohome import CONF_PASSWORD, CONF_USERNAME, DOMAIN
|
||||||
|
from homeassistant.core import HomeAssistant
|
||||||
|
from homeassistant.setup import async_setup_component
|
||||||
|
from homeassistant.util.json import JsonArrayType, JsonObjectType
|
||||||
|
|
||||||
|
from .const import ACCESS_TOKEN, REFRESH_TOKEN
|
||||||
|
|
||||||
|
from tests.common import load_json_array_fixture, load_json_object_fixture
|
||||||
|
|
||||||
|
TEST_CONFIG: Final = {
|
||||||
|
CONF_USERNAME: "username",
|
||||||
|
CONF_PASSWORD: "password",
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
def user_account_config_fixture() -> JsonObjectType:
|
||||||
|
"""Load JSON for the config of a user's account."""
|
||||||
|
return load_json_object_fixture("user_account.json", DOMAIN)
|
||||||
|
|
||||||
|
|
||||||
|
def user_locations_config_fixture() -> JsonArrayType:
|
||||||
|
"""Load JSON for the config of a user's installation (a list of locations)."""
|
||||||
|
return load_json_array_fixture("user_locations.json", DOMAIN)
|
||||||
|
|
||||||
|
|
||||||
|
def location_status_fixture(loc_id: str) -> JsonObjectType:
|
||||||
|
"""Load JSON for the status of a specific location."""
|
||||||
|
return load_json_object_fixture(f"status_{loc_id}.json", DOMAIN)
|
||||||
|
|
||||||
|
|
||||||
|
def dhw_schedule_fixture() -> JsonObjectType:
|
||||||
|
"""Load JSON for the schedule of a domesticHotWater zone."""
|
||||||
|
return load_json_object_fixture("schedule_dhw.json", DOMAIN)
|
||||||
|
|
||||||
|
|
||||||
|
def zone_schedule_fixture() -> JsonObjectType:
|
||||||
|
"""Load JSON for the schedule of a temperatureZone zone."""
|
||||||
|
return load_json_object_fixture("schedule_zone.json", DOMAIN)
|
||||||
|
|
||||||
|
|
||||||
|
async def mock_get(
|
||||||
|
self: Broker, url: str, **kwargs: Any
|
||||||
|
) -> JsonArrayType | JsonObjectType:
|
||||||
|
"""Return the JSON for a HTTP get of a given URL."""
|
||||||
|
|
||||||
|
# a proxy for the behaviour of the real web API
|
||||||
|
if self.refresh_token is None:
|
||||||
|
self.refresh_token = f"new_{REFRESH_TOKEN}"
|
||||||
|
|
||||||
|
if self.access_token_expires is None or self.access_token_expires < datetime.now():
|
||||||
|
self.access_token = f"new_{ACCESS_TOKEN}"
|
||||||
|
self.access_token_expires = datetime.now() + timedelta(minutes=30)
|
||||||
|
|
||||||
|
# assume a valid GET, and return the JSON for that web API
|
||||||
|
if url == "userAccount": # userAccount
|
||||||
|
return user_account_config_fixture()
|
||||||
|
|
||||||
|
if url.startswith("location"):
|
||||||
|
if "installationInfo" in url: # location/installationInfo?userId={id}
|
||||||
|
return user_locations_config_fixture()
|
||||||
|
if "location" in url: # location/{id}/status
|
||||||
|
return location_status_fixture("2738909")
|
||||||
|
|
||||||
|
elif "schedule" in url:
|
||||||
|
if url.startswith("domesticHotWater"): # domesticHotWater/{id}/schedule
|
||||||
|
return dhw_schedule_fixture()
|
||||||
|
if url.startswith("temperatureZone"): # temperatureZone/{id}/schedule
|
||||||
|
return zone_schedule_fixture()
|
||||||
|
|
||||||
|
pytest.xfail(f"Unexpected URL: {url}")
|
||||||
|
|
||||||
|
|
||||||
|
@patch("evohomeasync2.broker.Broker.get", mock_get)
|
||||||
|
async def setup_evohome(hass: HomeAssistant, test_config: dict[str, str]) -> MagicMock:
|
||||||
|
"""Set up the evohome integration and return its client.
|
||||||
|
|
||||||
|
The class is mocked here to check the client was instantiated with the correct args.
|
||||||
|
"""
|
||||||
|
|
||||||
|
with (
|
||||||
|
patch("homeassistant.components.evohome.evo.EvohomeClient") as mock_client,
|
||||||
|
patch("homeassistant.components.evohome.ev1.EvohomeClient", return_value=None),
|
||||||
|
):
|
||||||
|
mock_client.side_effect = EvohomeClient
|
||||||
|
|
||||||
|
assert await async_setup_component(hass, DOMAIN, {DOMAIN: test_config})
|
||||||
|
await hass.async_block_till_done()
|
||||||
|
|
||||||
|
mock_client.assert_called_once()
|
||||||
|
|
||||||
|
assert mock_client.call_args.args[0] == test_config[CONF_USERNAME]
|
||||||
|
assert mock_client.call_args.args[1] == test_config[CONF_PASSWORD]
|
||||||
|
|
||||||
|
assert isinstance(mock_client.call_args.kwargs["session"], ClientSession)
|
||||||
|
|
||||||
|
assert mock_client.account_info is not None
|
||||||
|
|
||||||
|
return mock_client
|
10
tests/components/evohome/const.py
Normal file
10
tests/components/evohome/const.py
Normal file
@ -0,0 +1,10 @@
|
|||||||
|
"""Constants for the evohome tests."""
|
||||||
|
|
||||||
|
from __future__ import annotations
|
||||||
|
|
||||||
|
from typing import Final
|
||||||
|
|
||||||
|
ACCESS_TOKEN: Final = "at_1dc7z657UKzbhKA..."
|
||||||
|
REFRESH_TOKEN: Final = "rf_jg68ZCKYdxEI3fF..."
|
||||||
|
SESSION_ID: Final = "F7181186..."
|
||||||
|
USERNAME: Final = "test_user@gmail.com"
|
81
tests/components/evohome/fixtures/schedule_dhw.json
Normal file
81
tests/components/evohome/fixtures/schedule_dhw.json
Normal file
@ -0,0 +1,81 @@
|
|||||||
|
{
|
||||||
|
"dailySchedules": [
|
||||||
|
{
|
||||||
|
"dayOfWeek": "Monday",
|
||||||
|
"switchpoints": [
|
||||||
|
{ "dhwState": "On", "timeOfDay": "06:30:00" },
|
||||||
|
{ "dhwState": "Off", "timeOfDay": "08:30:00" },
|
||||||
|
{ "dhwState": "On", "timeOfDay": "12:00:00" },
|
||||||
|
{ "dhwState": "Off", "timeOfDay": "13:00:00" },
|
||||||
|
{ "dhwState": "On", "timeOfDay": "16:30:00" },
|
||||||
|
{ "dhwState": "Off", "timeOfDay": "22:30:00" }
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"dayOfWeek": "Tuesday",
|
||||||
|
"switchpoints": [
|
||||||
|
{ "dhwState": "On", "timeOfDay": "06:30:00" },
|
||||||
|
{ "dhwState": "Off", "timeOfDay": "08:30:00" },
|
||||||
|
{ "dhwState": "On", "timeOfDay": "12:00:00" },
|
||||||
|
{ "dhwState": "Off", "timeOfDay": "13:00:00" },
|
||||||
|
{ "dhwState": "On", "timeOfDay": "16:30:00" },
|
||||||
|
{ "dhwState": "Off", "timeOfDay": "22:30:00" }
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"dayOfWeek": "Wednesday",
|
||||||
|
"switchpoints": [
|
||||||
|
{ "dhwState": "On", "timeOfDay": "06:30:00" },
|
||||||
|
{ "dhwState": "Off", "timeOfDay": "08:30:00" },
|
||||||
|
{ "dhwState": "On", "timeOfDay": "12:00:00" },
|
||||||
|
{ "dhwState": "Off", "timeOfDay": "13:00:00" },
|
||||||
|
{ "dhwState": "On", "timeOfDay": "16:30:00" },
|
||||||
|
{ "dhwState": "Off", "timeOfDay": "22:30:00" }
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"dayOfWeek": "Thursday",
|
||||||
|
"switchpoints": [
|
||||||
|
{ "dhwState": "On", "timeOfDay": "06:30:00" },
|
||||||
|
{ "dhwState": "Off", "timeOfDay": "08:30:00" },
|
||||||
|
{ "dhwState": "On", "timeOfDay": "12:00:00" },
|
||||||
|
{ "dhwState": "Off", "timeOfDay": "13:00:00" },
|
||||||
|
{ "dhwState": "On", "timeOfDay": "16:30:00" },
|
||||||
|
{ "dhwState": "Off", "timeOfDay": "22:30:00" }
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"dayOfWeek": "Friday",
|
||||||
|
"switchpoints": [
|
||||||
|
{ "dhwState": "On", "timeOfDay": "06:30:00" },
|
||||||
|
{ "dhwState": "Off", "timeOfDay": "08:30:00" },
|
||||||
|
{ "dhwState": "On", "timeOfDay": "12:00:00" },
|
||||||
|
{ "dhwState": "Off", "timeOfDay": "13:00:00" },
|
||||||
|
{ "dhwState": "On", "timeOfDay": "16:30:00" },
|
||||||
|
{ "dhwState": "Off", "timeOfDay": "22:30:00" }
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"dayOfWeek": "Saturday",
|
||||||
|
"switchpoints": [
|
||||||
|
{ "dhwState": "On", "timeOfDay": "06:30:00" },
|
||||||
|
{ "dhwState": "Off", "timeOfDay": "09:30:00" },
|
||||||
|
{ "dhwState": "On", "timeOfDay": "12:00:00" },
|
||||||
|
{ "dhwState": "Off", "timeOfDay": "13:00:00" },
|
||||||
|
{ "dhwState": "On", "timeOfDay": "16:30:00" },
|
||||||
|
{ "dhwState": "Off", "timeOfDay": "23:00:00" }
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"dayOfWeek": "Sunday",
|
||||||
|
"switchpoints": [
|
||||||
|
{ "dhwState": "On", "timeOfDay": "06:30:00" },
|
||||||
|
{ "dhwState": "Off", "timeOfDay": "09:30:00" },
|
||||||
|
{ "dhwState": "On", "timeOfDay": "12:00:00" },
|
||||||
|
{ "dhwState": "Off", "timeOfDay": "13:00:00" },
|
||||||
|
{ "dhwState": "On", "timeOfDay": "16:30:00" },
|
||||||
|
{ "dhwState": "Off", "timeOfDay": "23:00:00" }
|
||||||
|
]
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
67
tests/components/evohome/fixtures/schedule_zone.json
Normal file
67
tests/components/evohome/fixtures/schedule_zone.json
Normal file
@ -0,0 +1,67 @@
|
|||||||
|
{
|
||||||
|
"dailySchedules": [
|
||||||
|
{
|
||||||
|
"dayOfWeek": "Monday",
|
||||||
|
"switchpoints": [
|
||||||
|
{ "heatSetpoint": 18.1, "timeOfDay": "07:00:00" },
|
||||||
|
{ "heatSetpoint": 16.0, "timeOfDay": "08:00:00" },
|
||||||
|
{ "heatSetpoint": 18.6, "timeOfDay": "22:10:00" },
|
||||||
|
{ "heatSetpoint": 15.9, "timeOfDay": "23:00:00" }
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"dayOfWeek": "Tuesday",
|
||||||
|
"switchpoints": [
|
||||||
|
{ "heatSetpoint": 18.1, "timeOfDay": "07:00:00" },
|
||||||
|
{ "heatSetpoint": 16.0, "timeOfDay": "08:00:00" },
|
||||||
|
{ "heatSetpoint": 18.6, "timeOfDay": "22:10:00" },
|
||||||
|
{ "heatSetpoint": 15.9, "timeOfDay": "23:00:00" }
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"dayOfWeek": "Wednesday",
|
||||||
|
"switchpoints": [
|
||||||
|
{ "heatSetpoint": 18.1, "timeOfDay": "07:00:00" },
|
||||||
|
{ "heatSetpoint": 16.0, "timeOfDay": "08:00:00" },
|
||||||
|
{ "heatSetpoint": 18.6, "timeOfDay": "22:10:00" },
|
||||||
|
{ "heatSetpoint": 15.9, "timeOfDay": "23:00:00" }
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"dayOfWeek": "Thursday",
|
||||||
|
"switchpoints": [
|
||||||
|
{ "heatSetpoint": 18.1, "timeOfDay": "07:00:00" },
|
||||||
|
{ "heatSetpoint": 16.0, "timeOfDay": "08:00:00" },
|
||||||
|
{ "heatSetpoint": 18.6, "timeOfDay": "22:10:00" },
|
||||||
|
{ "heatSetpoint": 15.9, "timeOfDay": "23:00:00" }
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"dayOfWeek": "Friday",
|
||||||
|
"switchpoints": [
|
||||||
|
{ "heatSetpoint": 18.1, "timeOfDay": "07:00:00" },
|
||||||
|
{ "heatSetpoint": 16.0, "timeOfDay": "08:00:00" },
|
||||||
|
{ "heatSetpoint": 18.6, "timeOfDay": "22:10:00" },
|
||||||
|
{ "heatSetpoint": 15.9, "timeOfDay": "23:00:00" }
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"dayOfWeek": "Saturday",
|
||||||
|
"switchpoints": [
|
||||||
|
{ "heatSetpoint": 18.5, "timeOfDay": "07:00:00" },
|
||||||
|
{ "heatSetpoint": 16.0, "timeOfDay": "08:30:00" },
|
||||||
|
{ "heatSetpoint": 18.6, "timeOfDay": "22:10:00" },
|
||||||
|
{ "heatSetpoint": 15.9, "timeOfDay": "23:00:00" }
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"dayOfWeek": "Sunday",
|
||||||
|
"switchpoints": [
|
||||||
|
{ "heatSetpoint": 18.5, "timeOfDay": "07:00:00" },
|
||||||
|
{ "heatSetpoint": 16.0, "timeOfDay": "08:30:00" },
|
||||||
|
{ "heatSetpoint": 18.6, "timeOfDay": "22:10:00" },
|
||||||
|
{ "heatSetpoint": 15.9, "timeOfDay": "23:00:00" }
|
||||||
|
]
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
125
tests/components/evohome/fixtures/status_2738909.json
Normal file
125
tests/components/evohome/fixtures/status_2738909.json
Normal file
@ -0,0 +1,125 @@
|
|||||||
|
{
|
||||||
|
"locationId": "2738909",
|
||||||
|
"gateways": [
|
||||||
|
{
|
||||||
|
"gatewayId": "2499896",
|
||||||
|
"temperatureControlSystems": [
|
||||||
|
{
|
||||||
|
"systemId": "3432522",
|
||||||
|
"zones": [
|
||||||
|
{
|
||||||
|
"zoneId": "3432521",
|
||||||
|
"name": "Dead Zone",
|
||||||
|
"temperatureStatus": { "isAvailable": false },
|
||||||
|
"setpointStatus": {
|
||||||
|
"targetHeatTemperature": 17.0,
|
||||||
|
"setpointMode": "FollowSchedule"
|
||||||
|
},
|
||||||
|
"activeFaults": []
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"zoneId": "3432576",
|
||||||
|
"name": "Main Room",
|
||||||
|
"temperatureStatus": { "temperature": 19.0, "isAvailable": true },
|
||||||
|
"setpointStatus": {
|
||||||
|
"targetHeatTemperature": 17.0,
|
||||||
|
"setpointMode": "PermanentOverride"
|
||||||
|
},
|
||||||
|
"activeFaults": [
|
||||||
|
{
|
||||||
|
"faultType": "TempZoneActuatorCommunicationLost",
|
||||||
|
"since": "2022-03-02T15:56:01"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"zoneId": "3432577",
|
||||||
|
"name": "Front Room",
|
||||||
|
"temperatureStatus": { "temperature": 19.0, "isAvailable": true },
|
||||||
|
"setpointStatus": {
|
||||||
|
"targetHeatTemperature": 21.0,
|
||||||
|
"setpointMode": "TemporaryOverride",
|
||||||
|
"until": "2022-03-07T19:00:00Z"
|
||||||
|
},
|
||||||
|
"activeFaults": [
|
||||||
|
{
|
||||||
|
"faultType": "TempZoneActuatorLowBattery",
|
||||||
|
"since": "2022-03-02T04:50:20"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"zoneId": "3432578",
|
||||||
|
"temperatureStatus": { "temperature": 20.0, "isAvailable": true },
|
||||||
|
"activeFaults": [],
|
||||||
|
"setpointStatus": {
|
||||||
|
"targetHeatTemperature": 17.0,
|
||||||
|
"setpointMode": "FollowSchedule"
|
||||||
|
},
|
||||||
|
"name": "Kitchen"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"zoneId": "3432579",
|
||||||
|
"temperatureStatus": { "temperature": 20.0, "isAvailable": true },
|
||||||
|
"activeFaults": [],
|
||||||
|
"setpointStatus": {
|
||||||
|
"targetHeatTemperature": 16.0,
|
||||||
|
"setpointMode": "FollowSchedule"
|
||||||
|
},
|
||||||
|
"name": "Bathroom Dn"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"zoneId": "3432580",
|
||||||
|
"temperatureStatus": { "temperature": 21.0, "isAvailable": true },
|
||||||
|
"activeFaults": [],
|
||||||
|
"setpointStatus": {
|
||||||
|
"targetHeatTemperature": 16.0,
|
||||||
|
"setpointMode": "FollowSchedule"
|
||||||
|
},
|
||||||
|
"name": "Main Bedroom"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"zoneId": "3449703",
|
||||||
|
"temperatureStatus": { "temperature": 19.5, "isAvailable": true },
|
||||||
|
"activeFaults": [],
|
||||||
|
"setpointStatus": {
|
||||||
|
"targetHeatTemperature": 17.0,
|
||||||
|
"setpointMode": "FollowSchedule"
|
||||||
|
},
|
||||||
|
"name": "Kids Room"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"zoneId": "3449740",
|
||||||
|
"temperatureStatus": { "temperature": 21.5, "isAvailable": true },
|
||||||
|
"activeFaults": [],
|
||||||
|
"setpointStatus": {
|
||||||
|
"targetHeatTemperature": 16.5,
|
||||||
|
"setpointMode": "FollowSchedule"
|
||||||
|
},
|
||||||
|
"name": ""
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"zoneId": "3450733",
|
||||||
|
"temperatureStatus": { "temperature": 19.5, "isAvailable": true },
|
||||||
|
"activeFaults": [],
|
||||||
|
"setpointStatus": {
|
||||||
|
"targetHeatTemperature": 14.0,
|
||||||
|
"setpointMode": "PermanentOverride"
|
||||||
|
},
|
||||||
|
"name": "Spare Room"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"dhw": {
|
||||||
|
"dhwId": "3933910",
|
||||||
|
"temperatureStatus": { "temperature": 23.0, "isAvailable": true },
|
||||||
|
"stateStatus": { "state": "Off", "mode": "PermanentOverride" },
|
||||||
|
"activeFaults": []
|
||||||
|
},
|
||||||
|
"activeFaults": [],
|
||||||
|
"systemModeStatus": { "mode": "AutoWithEco", "isPermanent": true }
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"activeFaults": []
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
11
tests/components/evohome/fixtures/user_account.json
Normal file
11
tests/components/evohome/fixtures/user_account.json
Normal file
@ -0,0 +1,11 @@
|
|||||||
|
{
|
||||||
|
"userId": "2263181",
|
||||||
|
"username": "user_2263181@gmail.com",
|
||||||
|
"firstname": "John",
|
||||||
|
"lastname": "Smith",
|
||||||
|
"streetAddress": "1 Main Street",
|
||||||
|
"city": "London",
|
||||||
|
"postcode": "E1 1AA",
|
||||||
|
"country": "UnitedKingdom",
|
||||||
|
"language": "enGB"
|
||||||
|
}
|
346
tests/components/evohome/fixtures/user_locations.json
Normal file
346
tests/components/evohome/fixtures/user_locations.json
Normal file
@ -0,0 +1,346 @@
|
|||||||
|
[
|
||||||
|
{
|
||||||
|
"locationInfo": {
|
||||||
|
"locationId": "2738909",
|
||||||
|
"name": "My Home",
|
||||||
|
"streetAddress": "1 Main Street",
|
||||||
|
"city": "London",
|
||||||
|
"country": "UnitedKingdom",
|
||||||
|
"postcode": "E1 1AA",
|
||||||
|
"locationType": "Residential",
|
||||||
|
"useDaylightSaveSwitching": true,
|
||||||
|
"timeZone": {
|
||||||
|
"timeZoneId": "GMTStandardTime",
|
||||||
|
"displayName": "(UTC+00:00) Dublin, Edinburgh, Lisbon, London",
|
||||||
|
"offsetMinutes": 0,
|
||||||
|
"currentOffsetMinutes": 60,
|
||||||
|
"supportsDaylightSaving": true
|
||||||
|
},
|
||||||
|
"locationOwner": {
|
||||||
|
"userId": "2263181",
|
||||||
|
"username": "user_2263181@gmail.com",
|
||||||
|
"firstname": "John",
|
||||||
|
"lastname": "Smith"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"gateways": [
|
||||||
|
{
|
||||||
|
"gatewayInfo": {
|
||||||
|
"gatewayId": "2499896",
|
||||||
|
"mac": "00D02DEE0000",
|
||||||
|
"crc": "1234",
|
||||||
|
"isWiFi": false
|
||||||
|
},
|
||||||
|
"temperatureControlSystems": [
|
||||||
|
{
|
||||||
|
"systemId": "3432522",
|
||||||
|
"modelType": "EvoTouch",
|
||||||
|
"zones": [
|
||||||
|
{
|
||||||
|
"zoneId": "3432521",
|
||||||
|
"modelType": "HeatingZone",
|
||||||
|
"setpointCapabilities": {
|
||||||
|
"maxHeatSetpoint": 35.0,
|
||||||
|
"minHeatSetpoint": 5.0,
|
||||||
|
"valueResolution": 0.5,
|
||||||
|
"canControlHeat": true,
|
||||||
|
"canControlCool": false,
|
||||||
|
"allowedSetpointModes": [
|
||||||
|
"PermanentOverride",
|
||||||
|
"FollowSchedule",
|
||||||
|
"TemporaryOverride"
|
||||||
|
],
|
||||||
|
"maxDuration": "1.00:00:00",
|
||||||
|
"timingResolution": "00:10:00"
|
||||||
|
},
|
||||||
|
"scheduleCapabilities": {
|
||||||
|
"maxSwitchpointsPerDay": 6,
|
||||||
|
"minSwitchpointsPerDay": 1,
|
||||||
|
"timingResolution": "00:10:00",
|
||||||
|
"setpointValueResolution": 0.5
|
||||||
|
},
|
||||||
|
"name": "Dead Zone",
|
||||||
|
"zoneType": "RadiatorZone"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"zoneId": "3432576",
|
||||||
|
"modelType": "HeatingZone",
|
||||||
|
"setpointCapabilities": {
|
||||||
|
"maxHeatSetpoint": 35.0,
|
||||||
|
"minHeatSetpoint": 5.0,
|
||||||
|
"valueResolution": 0.5,
|
||||||
|
"canControlHeat": true,
|
||||||
|
"canControlCool": false,
|
||||||
|
"allowedSetpointModes": [
|
||||||
|
"PermanentOverride",
|
||||||
|
"FollowSchedule",
|
||||||
|
"TemporaryOverride"
|
||||||
|
],
|
||||||
|
"maxDuration": "1.00:00:00",
|
||||||
|
"timingResolution": "00:10:00"
|
||||||
|
},
|
||||||
|
"scheduleCapabilities": {
|
||||||
|
"maxSwitchpointsPerDay": 6,
|
||||||
|
"minSwitchpointsPerDay": 1,
|
||||||
|
"timingResolution": "00:10:00",
|
||||||
|
"setpointValueResolution": 0.5
|
||||||
|
},
|
||||||
|
"name": "Main Room",
|
||||||
|
"zoneType": "RadiatorZone"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"zoneId": "3432577",
|
||||||
|
"modelType": "HeatingZone",
|
||||||
|
"setpointCapabilities": {
|
||||||
|
"maxHeatSetpoint": 35.0,
|
||||||
|
"minHeatSetpoint": 5.0,
|
||||||
|
"valueResolution": 0.5,
|
||||||
|
"canControlHeat": true,
|
||||||
|
"canControlCool": false,
|
||||||
|
"allowedSetpointModes": [
|
||||||
|
"PermanentOverride",
|
||||||
|
"FollowSchedule",
|
||||||
|
"TemporaryOverride"
|
||||||
|
],
|
||||||
|
"maxDuration": "1.00:00:00",
|
||||||
|
"timingResolution": "00:10:00"
|
||||||
|
},
|
||||||
|
"scheduleCapabilities": {
|
||||||
|
"maxSwitchpointsPerDay": 6,
|
||||||
|
"minSwitchpointsPerDay": 1,
|
||||||
|
"timingResolution": "00:10:00",
|
||||||
|
"setpointValueResolution": 0.5
|
||||||
|
},
|
||||||
|
"name": "Front Room",
|
||||||
|
"zoneType": "RadiatorZone"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"zoneId": "3432578",
|
||||||
|
"modelType": "HeatingZone",
|
||||||
|
"setpointCapabilities": {
|
||||||
|
"maxHeatSetpoint": 35.0,
|
||||||
|
"minHeatSetpoint": 5.0,
|
||||||
|
"valueResolution": 0.5,
|
||||||
|
"canControlHeat": true,
|
||||||
|
"canControlCool": false,
|
||||||
|
"allowedSetpointModes": [
|
||||||
|
"PermanentOverride",
|
||||||
|
"FollowSchedule",
|
||||||
|
"TemporaryOverride"
|
||||||
|
],
|
||||||
|
"maxDuration": "1.00:00:00",
|
||||||
|
"timingResolution": "00:10:00"
|
||||||
|
},
|
||||||
|
"scheduleCapabilities": {
|
||||||
|
"maxSwitchpointsPerDay": 6,
|
||||||
|
"minSwitchpointsPerDay": 1,
|
||||||
|
"timingResolution": "00:10:00",
|
||||||
|
"setpointValueResolution": 0.5
|
||||||
|
},
|
||||||
|
"name": "Kitchen",
|
||||||
|
"zoneType": "RadiatorZone"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"zoneId": "3432579",
|
||||||
|
"modelType": "HeatingZone",
|
||||||
|
"setpointCapabilities": {
|
||||||
|
"maxHeatSetpoint": 35.0,
|
||||||
|
"minHeatSetpoint": 5.0,
|
||||||
|
"valueResolution": 0.5,
|
||||||
|
"canControlHeat": true,
|
||||||
|
"canControlCool": false,
|
||||||
|
"allowedSetpointModes": [
|
||||||
|
"PermanentOverride",
|
||||||
|
"FollowSchedule",
|
||||||
|
"TemporaryOverride"
|
||||||
|
],
|
||||||
|
"maxDuration": "1.00:00:00",
|
||||||
|
"timingResolution": "00:10:00"
|
||||||
|
},
|
||||||
|
"scheduleCapabilities": {
|
||||||
|
"maxSwitchpointsPerDay": 6,
|
||||||
|
"minSwitchpointsPerDay": 1,
|
||||||
|
"timingResolution": "00:10:00",
|
||||||
|
"setpointValueResolution": 0.5
|
||||||
|
},
|
||||||
|
"name": "Bathroom Dn",
|
||||||
|
"zoneType": "RadiatorZone"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"zoneId": "3432580",
|
||||||
|
"modelType": "HeatingZone",
|
||||||
|
"setpointCapabilities": {
|
||||||
|
"maxHeatSetpoint": 35.0,
|
||||||
|
"minHeatSetpoint": 5.0,
|
||||||
|
"valueResolution": 0.5,
|
||||||
|
"canControlHeat": true,
|
||||||
|
"canControlCool": false,
|
||||||
|
"allowedSetpointModes": [
|
||||||
|
"PermanentOverride",
|
||||||
|
"FollowSchedule",
|
||||||
|
"TemporaryOverride"
|
||||||
|
],
|
||||||
|
"maxDuration": "1.00:00:00",
|
||||||
|
"timingResolution": "00:10:00"
|
||||||
|
},
|
||||||
|
"scheduleCapabilities": {
|
||||||
|
"maxSwitchpointsPerDay": 6,
|
||||||
|
"minSwitchpointsPerDay": 1,
|
||||||
|
"timingResolution": "00:10:00",
|
||||||
|
"setpointValueResolution": 0.5
|
||||||
|
},
|
||||||
|
"name": "Main Bedroom",
|
||||||
|
"zoneType": "RadiatorZone"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"zoneId": "3449703",
|
||||||
|
"modelType": "HeatingZone",
|
||||||
|
"setpointCapabilities": {
|
||||||
|
"maxHeatSetpoint": 35.0,
|
||||||
|
"minHeatSetpoint": 5.0,
|
||||||
|
"valueResolution": 0.5,
|
||||||
|
"canControlHeat": true,
|
||||||
|
"canControlCool": false,
|
||||||
|
"allowedSetpointModes": [
|
||||||
|
"PermanentOverride",
|
||||||
|
"FollowSchedule",
|
||||||
|
"TemporaryOverride"
|
||||||
|
],
|
||||||
|
"maxDuration": "1.00:00:00",
|
||||||
|
"timingResolution": "00:10:00"
|
||||||
|
},
|
||||||
|
"scheduleCapabilities": {
|
||||||
|
"maxSwitchpointsPerDay": 6,
|
||||||
|
"minSwitchpointsPerDay": 1,
|
||||||
|
"timingResolution": "00:10:00",
|
||||||
|
"setpointValueResolution": 0.5
|
||||||
|
},
|
||||||
|
"name": "Kids Room",
|
||||||
|
"zoneType": "RadiatorZone"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"zoneId": "3449740",
|
||||||
|
"modelType": "Unknown",
|
||||||
|
"setpointCapabilities": {
|
||||||
|
"maxHeatSetpoint": 35.0,
|
||||||
|
"minHeatSetpoint": 5.0,
|
||||||
|
"valueResolution": 0.5,
|
||||||
|
"canControlHeat": true,
|
||||||
|
"canControlCool": false,
|
||||||
|
"allowedSetpointModes": [
|
||||||
|
"PermanentOverride",
|
||||||
|
"FollowSchedule",
|
||||||
|
"TemporaryOverride"
|
||||||
|
],
|
||||||
|
"maxDuration": "1.00:00:00",
|
||||||
|
"timingResolution": "00:10:00"
|
||||||
|
},
|
||||||
|
"scheduleCapabilities": {
|
||||||
|
"maxSwitchpointsPerDay": 6,
|
||||||
|
"minSwitchpointsPerDay": 1,
|
||||||
|
"timingResolution": "00:10:00",
|
||||||
|
"setpointValueResolution": 0.5
|
||||||
|
},
|
||||||
|
"name": "",
|
||||||
|
"zoneType": "Unknown"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"zoneId": "3450733",
|
||||||
|
"modelType": "xx",
|
||||||
|
"setpointCapabilities": {
|
||||||
|
"maxHeatSetpoint": 35.0,
|
||||||
|
"minHeatSetpoint": 5.0,
|
||||||
|
"valueResolution": 0.5,
|
||||||
|
"canControlHeat": true,
|
||||||
|
"canControlCool": false,
|
||||||
|
"allowedSetpointModes": [
|
||||||
|
"PermanentOverride",
|
||||||
|
"FollowSchedule",
|
||||||
|
"TemporaryOverride"
|
||||||
|
],
|
||||||
|
"maxDuration": "1.00:00:00",
|
||||||
|
"timingResolution": "00:10:00"
|
||||||
|
},
|
||||||
|
"scheduleCapabilities": {
|
||||||
|
"maxSwitchpointsPerDay": 6,
|
||||||
|
"minSwitchpointsPerDay": 1,
|
||||||
|
"timingResolution": "00:10:00",
|
||||||
|
"setpointValueResolution": 0.5
|
||||||
|
},
|
||||||
|
"name": "Spare Room",
|
||||||
|
"zoneType": "xx"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"dhw": {
|
||||||
|
"dhwId": "3933910",
|
||||||
|
"dhwStateCapabilitiesResponse": {
|
||||||
|
"allowedStates": ["On", "Off"],
|
||||||
|
"allowedModes": [
|
||||||
|
"FollowSchedule",
|
||||||
|
"PermanentOverride",
|
||||||
|
"TemporaryOverride"
|
||||||
|
],
|
||||||
|
"maxDuration": "1.00:00:00",
|
||||||
|
"timingResolution": "00:10:00"
|
||||||
|
},
|
||||||
|
"scheduleCapabilitiesResponse": {
|
||||||
|
"maxSwitchpointsPerDay": 6,
|
||||||
|
"minSwitchpointsPerDay": 1,
|
||||||
|
"timingResolution": "00:10:00"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"allowedSystemModes": [
|
||||||
|
{
|
||||||
|
"systemMode": "HeatingOff",
|
||||||
|
"canBePermanent": true,
|
||||||
|
"canBeTemporary": false
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"systemMode": "Auto",
|
||||||
|
"canBePermanent": true,
|
||||||
|
"canBeTemporary": false
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"systemMode": "AutoWithReset",
|
||||||
|
"canBePermanent": true,
|
||||||
|
"canBeTemporary": false
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"systemMode": "AutoWithEco",
|
||||||
|
"canBePermanent": true,
|
||||||
|
"canBeTemporary": true,
|
||||||
|
"maxDuration": "1.00:00:00",
|
||||||
|
"timingResolution": "01:00:00",
|
||||||
|
"timingMode": "Duration"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"systemMode": "Away",
|
||||||
|
"canBePermanent": true,
|
||||||
|
"canBeTemporary": true,
|
||||||
|
"maxDuration": "99.00:00:00",
|
||||||
|
"timingResolution": "1.00:00:00",
|
||||||
|
"timingMode": "Period"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"systemMode": "DayOff",
|
||||||
|
"canBePermanent": true,
|
||||||
|
"canBeTemporary": true,
|
||||||
|
"maxDuration": "99.00:00:00",
|
||||||
|
"timingResolution": "1.00:00:00",
|
||||||
|
"timingMode": "Period"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"systemMode": "Custom",
|
||||||
|
"canBePermanent": true,
|
||||||
|
"canBeTemporary": true,
|
||||||
|
"maxDuration": "99.00:00:00",
|
||||||
|
"timingResolution": "1.00:00:00",
|
||||||
|
"timingMode": "Period"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
]
|
208
tests/components/evohome/test_storage.py
Normal file
208
tests/components/evohome/test_storage.py
Normal file
@ -0,0 +1,208 @@
|
|||||||
|
"""The tests for evohome storage load & save."""
|
||||||
|
|
||||||
|
from __future__ import annotations
|
||||||
|
|
||||||
|
from datetime import datetime, timedelta
|
||||||
|
from typing import Any, Final, NotRequired, TypedDict
|
||||||
|
|
||||||
|
import pytest
|
||||||
|
|
||||||
|
from homeassistant.components.evohome import (
|
||||||
|
CONF_PASSWORD,
|
||||||
|
CONF_USERNAME,
|
||||||
|
DOMAIN,
|
||||||
|
STORAGE_KEY,
|
||||||
|
STORAGE_VER,
|
||||||
|
dt_aware_to_naive,
|
||||||
|
)
|
||||||
|
from homeassistant.core import HomeAssistant
|
||||||
|
import homeassistant.util.dt as dt_util
|
||||||
|
|
||||||
|
from .conftest import setup_evohome
|
||||||
|
from .const import ACCESS_TOKEN, REFRESH_TOKEN, SESSION_ID, USERNAME
|
||||||
|
|
||||||
|
|
||||||
|
class _SessionDataT(TypedDict):
|
||||||
|
sessionId: str
|
||||||
|
|
||||||
|
|
||||||
|
class _TokenStoreT(TypedDict):
|
||||||
|
username: str
|
||||||
|
refresh_token: str
|
||||||
|
access_token: str
|
||||||
|
access_token_expires: str # 2024-07-27T23:57:30+01:00
|
||||||
|
user_data: NotRequired[_SessionDataT]
|
||||||
|
|
||||||
|
|
||||||
|
class _EmptyStoreT(TypedDict):
|
||||||
|
pass
|
||||||
|
|
||||||
|
|
||||||
|
SZ_USERNAME: Final = "username"
|
||||||
|
SZ_REFRESH_TOKEN: Final = "refresh_token"
|
||||||
|
SZ_ACCESS_TOKEN: Final = "access_token"
|
||||||
|
SZ_ACCESS_TOKEN_EXPIRES: Final = "access_token_expires"
|
||||||
|
SZ_USER_DATA: Final = "user_data"
|
||||||
|
|
||||||
|
|
||||||
|
def dt_pair(dt_dtm: datetime) -> tuple[datetime, str]:
|
||||||
|
"""Return a datetime without milliseconds and its string representation."""
|
||||||
|
dt_str = dt_dtm.isoformat(timespec="seconds") # e.g. 2024-07-28T00:57:29+01:00
|
||||||
|
return dt_util.parse_datetime(dt_str, raise_on_error=True), dt_str
|
||||||
|
|
||||||
|
|
||||||
|
ACCESS_TOKEN_EXP_DTM, ACCESS_TOKEN_EXP_STR = dt_pair(dt_util.now() + timedelta(hours=1))
|
||||||
|
|
||||||
|
USERNAME_DIFF: Final = f"not_{USERNAME}"
|
||||||
|
USERNAME_SAME: Final = USERNAME
|
||||||
|
|
||||||
|
TEST_CONFIG: Final = {
|
||||||
|
CONF_USERNAME: USERNAME_SAME,
|
||||||
|
CONF_PASSWORD: "password",
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_DATA: Final[dict[str, _TokenStoreT]] = {
|
||||||
|
"sans_session_id": {
|
||||||
|
SZ_USERNAME: USERNAME_SAME,
|
||||||
|
SZ_REFRESH_TOKEN: REFRESH_TOKEN,
|
||||||
|
SZ_ACCESS_TOKEN: ACCESS_TOKEN,
|
||||||
|
SZ_ACCESS_TOKEN_EXPIRES: ACCESS_TOKEN_EXP_STR,
|
||||||
|
},
|
||||||
|
"with_session_id": {
|
||||||
|
SZ_USERNAME: USERNAME_SAME,
|
||||||
|
SZ_REFRESH_TOKEN: REFRESH_TOKEN,
|
||||||
|
SZ_ACCESS_TOKEN: ACCESS_TOKEN,
|
||||||
|
SZ_ACCESS_TOKEN_EXPIRES: ACCESS_TOKEN_EXP_STR,
|
||||||
|
SZ_USER_DATA: {"sessionId": SESSION_ID},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_DATA_NULL: Final[dict[str, _EmptyStoreT | None]] = {
|
||||||
|
"store_is_absent": None,
|
||||||
|
"store_was_reset": {},
|
||||||
|
}
|
||||||
|
|
||||||
|
DOMAIN_STORAGE_BASE: Final = {
|
||||||
|
"version": STORAGE_VER,
|
||||||
|
"minor_version": 1,
|
||||||
|
"key": STORAGE_KEY,
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.mark.parametrize("idx", TEST_DATA_NULL)
|
||||||
|
async def test_auth_tokens_null(
|
||||||
|
hass: HomeAssistant,
|
||||||
|
hass_storage: dict[str, Any],
|
||||||
|
idx: str,
|
||||||
|
) -> None:
|
||||||
|
"""Test loading/saving authentication tokens when no cached tokens in the store."""
|
||||||
|
|
||||||
|
hass_storage[DOMAIN] = DOMAIN_STORAGE_BASE | {"data": TEST_DATA_NULL[idx]}
|
||||||
|
|
||||||
|
mock_client = await setup_evohome(hass, TEST_CONFIG)
|
||||||
|
|
||||||
|
# Confirm client was instantiated without tokens, as cache was empty...
|
||||||
|
assert SZ_REFRESH_TOKEN not in mock_client.call_args.kwargs
|
||||||
|
assert SZ_ACCESS_TOKEN not in mock_client.call_args.kwargs
|
||||||
|
assert SZ_ACCESS_TOKEN_EXPIRES not in mock_client.call_args.kwarg
|
||||||
|
|
||||||
|
# Confirm the expected tokens were cached to storage...
|
||||||
|
data: _TokenStoreT = hass_storage[DOMAIN]["data"]
|
||||||
|
|
||||||
|
assert data[SZ_USERNAME] == USERNAME_SAME
|
||||||
|
assert data[SZ_REFRESH_TOKEN] == f"new_{REFRESH_TOKEN}"
|
||||||
|
assert data[SZ_ACCESS_TOKEN] == f"new_{ACCESS_TOKEN}"
|
||||||
|
assert (
|
||||||
|
dt_util.parse_datetime(data[SZ_ACCESS_TOKEN_EXPIRES], raise_on_error=True)
|
||||||
|
> dt_util.now()
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.mark.parametrize("idx", TEST_DATA)
|
||||||
|
async def test_auth_tokens_same(
|
||||||
|
hass: HomeAssistant, hass_storage: dict[str, Any], idx: str
|
||||||
|
) -> None:
|
||||||
|
"""Test loading/saving authentication tokens when matching username."""
|
||||||
|
|
||||||
|
hass_storage[DOMAIN] = DOMAIN_STORAGE_BASE | {"data": TEST_DATA[idx]}
|
||||||
|
|
||||||
|
mock_client = await setup_evohome(hass, TEST_CONFIG)
|
||||||
|
|
||||||
|
# Confirm client was instantiated with the cached tokens...
|
||||||
|
assert mock_client.call_args.kwargs[SZ_REFRESH_TOKEN] == REFRESH_TOKEN
|
||||||
|
assert mock_client.call_args.kwargs[SZ_ACCESS_TOKEN] == ACCESS_TOKEN
|
||||||
|
assert mock_client.call_args.kwargs[SZ_ACCESS_TOKEN_EXPIRES] == dt_aware_to_naive(
|
||||||
|
ACCESS_TOKEN_EXP_DTM
|
||||||
|
)
|
||||||
|
|
||||||
|
# Confirm the expected tokens were cached to storage...
|
||||||
|
data: _TokenStoreT = hass_storage[DOMAIN]["data"]
|
||||||
|
|
||||||
|
assert data[SZ_USERNAME] == USERNAME_SAME
|
||||||
|
assert data[SZ_REFRESH_TOKEN] == REFRESH_TOKEN
|
||||||
|
assert data[SZ_ACCESS_TOKEN] == ACCESS_TOKEN
|
||||||
|
assert dt_util.parse_datetime(data[SZ_ACCESS_TOKEN_EXPIRES]) == ACCESS_TOKEN_EXP_DTM
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.mark.parametrize("idx", TEST_DATA)
|
||||||
|
async def test_auth_tokens_past(
|
||||||
|
hass: HomeAssistant, hass_storage: dict[str, Any], idx: str
|
||||||
|
) -> None:
|
||||||
|
"""Test loading/saving authentication tokens with matching username, but expired."""
|
||||||
|
|
||||||
|
dt_dtm, dt_str = dt_pair(dt_util.now() - timedelta(hours=1))
|
||||||
|
|
||||||
|
# make this access token have expired in the past...
|
||||||
|
test_data = TEST_DATA[idx].copy() # shallow copy is OK here
|
||||||
|
test_data[SZ_ACCESS_TOKEN_EXPIRES] = dt_str
|
||||||
|
|
||||||
|
hass_storage[DOMAIN] = DOMAIN_STORAGE_BASE | {"data": test_data}
|
||||||
|
|
||||||
|
mock_client = await setup_evohome(hass, TEST_CONFIG)
|
||||||
|
|
||||||
|
# Confirm client was instantiated with the cached tokens...
|
||||||
|
assert mock_client.call_args.kwargs[SZ_REFRESH_TOKEN] == REFRESH_TOKEN
|
||||||
|
assert mock_client.call_args.kwargs[SZ_ACCESS_TOKEN] == ACCESS_TOKEN
|
||||||
|
assert mock_client.call_args.kwargs[SZ_ACCESS_TOKEN_EXPIRES] == dt_aware_to_naive(
|
||||||
|
dt_dtm
|
||||||
|
)
|
||||||
|
|
||||||
|
# Confirm the expected tokens were cached to storage...
|
||||||
|
data: _TokenStoreT = hass_storage[DOMAIN]["data"]
|
||||||
|
|
||||||
|
assert data[SZ_USERNAME] == USERNAME_SAME
|
||||||
|
assert data[SZ_REFRESH_TOKEN] == REFRESH_TOKEN
|
||||||
|
assert data[SZ_ACCESS_TOKEN] == f"new_{ACCESS_TOKEN}"
|
||||||
|
assert (
|
||||||
|
dt_util.parse_datetime(data[SZ_ACCESS_TOKEN_EXPIRES], raise_on_error=True)
|
||||||
|
> dt_util.now()
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.mark.parametrize("idx", TEST_DATA)
|
||||||
|
async def test_auth_tokens_diff(
|
||||||
|
hass: HomeAssistant, hass_storage: dict[str, Any], idx: str
|
||||||
|
) -> None:
|
||||||
|
"""Test loading/saving authentication tokens when unmatched username."""
|
||||||
|
|
||||||
|
hass_storage[DOMAIN] = DOMAIN_STORAGE_BASE | {"data": TEST_DATA[idx]}
|
||||||
|
|
||||||
|
mock_client = await setup_evohome(
|
||||||
|
hass, TEST_CONFIG | {CONF_USERNAME: USERNAME_DIFF}
|
||||||
|
)
|
||||||
|
|
||||||
|
# Confirm client was instantiated without tokens, as username was different...
|
||||||
|
assert SZ_REFRESH_TOKEN not in mock_client.call_args.kwargs
|
||||||
|
assert SZ_ACCESS_TOKEN not in mock_client.call_args.kwargs
|
||||||
|
assert SZ_ACCESS_TOKEN_EXPIRES not in mock_client.call_args.kwarg
|
||||||
|
|
||||||
|
# Confirm the expected tokens were cached to storage...
|
||||||
|
data: _TokenStoreT = hass_storage[DOMAIN]["data"]
|
||||||
|
|
||||||
|
assert data[SZ_USERNAME] == USERNAME_DIFF
|
||||||
|
assert data[SZ_REFRESH_TOKEN] == f"new_{REFRESH_TOKEN}"
|
||||||
|
assert data[SZ_ACCESS_TOKEN] == f"new_{ACCESS_TOKEN}"
|
||||||
|
assert (
|
||||||
|
dt_util.parse_datetime(data[SZ_ACCESS_TOKEN_EXPIRES], raise_on_error=True)
|
||||||
|
> dt_util.now()
|
||||||
|
)
|
Loading…
x
Reference in New Issue
Block a user