mirror of
https://github.com/home-assistant/core.git
synced 2026-07-15 18:44:20 +01:00
179 lines
5.8 KiB
Python
179 lines
5.8 KiB
Python
"""The tests for evohome storage load & save."""
|
|
|
|
from datetime import datetime, timedelta
|
|
from typing import Any, Final, TypedDict
|
|
|
|
from evohomeasync.auth import SZ_SESSION_ID, SZ_SESSION_ID_EXPIRES
|
|
from evohomeasync2.auth import (
|
|
SZ_ACCESS_TOKEN,
|
|
SZ_ACCESS_TOKEN_EXPIRES,
|
|
SZ_REFRESH_TOKEN,
|
|
)
|
|
import pytest
|
|
|
|
from homeassistant.components.evohome.const import DOMAIN, STORAGE_KEY, STORAGE_VER
|
|
from homeassistant.components.evohome.storage import _TokenStoreT
|
|
from homeassistant.const import CONF_USERNAME
|
|
from homeassistant.core import HomeAssistant
|
|
from homeassistant.util import dt as dt_util
|
|
|
|
from .conftest import setup_evohome
|
|
from .const import ACCESS_TOKEN, REFRESH_TOKEN, SESSION_ID, USERNAME
|
|
|
|
|
|
class _EmptyStoreT(TypedDict):
|
|
pass
|
|
|
|
|
|
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))
|
|
_, SESSION_ID_EXP_STR = dt_pair(dt_util.now() + timedelta(minutes=15))
|
|
|
|
_TEST_STORAGE_BASE: Final[_TokenStoreT] = {
|
|
CONF_USERNAME: USERNAME,
|
|
SZ_REFRESH_TOKEN: REFRESH_TOKEN,
|
|
SZ_ACCESS_TOKEN: ACCESS_TOKEN,
|
|
SZ_ACCESS_TOKEN_EXPIRES: ACCESS_TOKEN_EXP_STR,
|
|
}
|
|
|
|
TEST_STORAGE_DATA: Final[dict[str, _TokenStoreT]] = {
|
|
"sans_session_id": _TEST_STORAGE_BASE,
|
|
"with_session_id": _TEST_STORAGE_BASE
|
|
| {
|
|
SZ_SESSION_ID: SESSION_ID,
|
|
SZ_SESSION_ID_EXPIRES: SESSION_ID_EXP_STR,
|
|
}, # pyright: ignore[reportAssignmentType]
|
|
}
|
|
|
|
TEST_STORAGE_NULL: Final[dict[str, _EmptyStoreT | None]] = {
|
|
"store_is_absent": None,
|
|
"store_was_reset": {},
|
|
}
|
|
|
|
DOMAIN_STORAGE_ROOT: Final = {
|
|
"version": STORAGE_VER,
|
|
"minor_version": 1,
|
|
"key": STORAGE_KEY,
|
|
}
|
|
|
|
|
|
@pytest.mark.parametrize("install", ["minimal"])
|
|
@pytest.mark.parametrize("idx", TEST_STORAGE_NULL)
|
|
async def test_auth_tokens_null(
|
|
hass: HomeAssistant,
|
|
hass_storage: dict[str, Any],
|
|
config: dict[str, str],
|
|
idx: str,
|
|
install: str,
|
|
) -> None:
|
|
"""Test credentials manager when cache is empty."""
|
|
|
|
hass_storage[DOMAIN] = DOMAIN_STORAGE_ROOT | {"data": TEST_STORAGE_NULL[idx]}
|
|
|
|
async for _ in setup_evohome(hass, config, install=install):
|
|
pass
|
|
|
|
data: _TokenStoreT = hass_storage[DOMAIN]["data"]
|
|
|
|
# Confirm the expected tokens were cached to storage...
|
|
assert data[CONF_USERNAME] == USERNAME
|
|
assert data[SZ_REFRESH_TOKEN] == f"new_{REFRESH_TOKEN}"
|
|
assert data[SZ_ACCESS_TOKEN] == f"new_{ACCESS_TOKEN}"
|
|
|
|
assert (expires := data.get(SZ_ACCESS_TOKEN_EXPIRES)) is not None
|
|
assert dt_util.parse_datetime(expires, raise_on_error=True) > dt_util.now()
|
|
|
|
|
|
@pytest.mark.parametrize("install", ["minimal"])
|
|
@pytest.mark.parametrize("idx", TEST_STORAGE_DATA)
|
|
async def test_auth_tokens_same(
|
|
hass: HomeAssistant,
|
|
hass_storage: dict[str, Any],
|
|
config: dict[str, str],
|
|
idx: str,
|
|
install: str,
|
|
) -> None:
|
|
"""Test credentials manager when cache contains valid data for this user."""
|
|
|
|
hass_storage[DOMAIN] = DOMAIN_STORAGE_ROOT | {"data": TEST_STORAGE_DATA[idx]}
|
|
|
|
async for _ in setup_evohome(hass, config, install=install):
|
|
pass
|
|
|
|
data: _TokenStoreT = hass_storage[DOMAIN]["data"]
|
|
|
|
# Confirm the expected tokens were cached to storage...
|
|
assert data[CONF_USERNAME] == USERNAME
|
|
assert data[SZ_REFRESH_TOKEN] == REFRESH_TOKEN
|
|
assert data[SZ_ACCESS_TOKEN] == ACCESS_TOKEN
|
|
|
|
assert (expires := data[SZ_ACCESS_TOKEN_EXPIRES]) is not None
|
|
assert dt_util.parse_datetime(expires, raise_on_error=True) == ACCESS_TOKEN_EXP_DTM
|
|
|
|
|
|
@pytest.mark.parametrize("install", ["minimal"])
|
|
@pytest.mark.parametrize("idx", TEST_STORAGE_DATA)
|
|
async def test_auth_tokens_past(
|
|
hass: HomeAssistant,
|
|
hass_storage: dict[str, Any],
|
|
config: dict[str, str],
|
|
idx: str,
|
|
install: str,
|
|
) -> None:
|
|
"""Test credentials manager when cache contains expired data for this user."""
|
|
|
|
# Make this access token have expired in the past...
|
|
_, dt_str = dt_pair(dt_util.now() - timedelta(hours=1))
|
|
|
|
test_data = TEST_STORAGE_DATA[idx].copy() # shallow copy is OK here
|
|
test_data[SZ_ACCESS_TOKEN_EXPIRES] = dt_str
|
|
|
|
hass_storage[DOMAIN] = DOMAIN_STORAGE_ROOT | {"data": test_data}
|
|
|
|
async for _ in setup_evohome(hass, config, install=install):
|
|
pass
|
|
|
|
data: _TokenStoreT = hass_storage[DOMAIN]["data"]
|
|
|
|
# Confirm the expected tokens were cached to storage...
|
|
assert data[CONF_USERNAME] == USERNAME
|
|
assert data[SZ_REFRESH_TOKEN] == f"new_{REFRESH_TOKEN}"
|
|
assert data[SZ_ACCESS_TOKEN] == f"new_{ACCESS_TOKEN}"
|
|
|
|
assert (expires := data[SZ_ACCESS_TOKEN_EXPIRES]) is not None
|
|
assert dt_util.parse_datetime(expires, raise_on_error=True) > dt_util.now()
|
|
|
|
|
|
@pytest.mark.parametrize("install", ["minimal"])
|
|
@pytest.mark.parametrize("idx", TEST_STORAGE_DATA)
|
|
async def test_auth_tokens_diff(
|
|
hass: HomeAssistant,
|
|
hass_storage: dict[str, Any],
|
|
config: dict[str, str],
|
|
idx: str,
|
|
install: str,
|
|
) -> None:
|
|
"""Test credentials manager when cache contains data for a different user."""
|
|
|
|
# Make this access token be for a different user...
|
|
hass_storage[DOMAIN] = DOMAIN_STORAGE_ROOT | {"data": TEST_STORAGE_DATA[idx]}
|
|
config[CONF_USERNAME] = f"new_{USERNAME}"
|
|
|
|
async for _ in setup_evohome(hass, config, install=install):
|
|
pass
|
|
|
|
data: _TokenStoreT = hass_storage[DOMAIN]["data"]
|
|
|
|
# Confirm the expected tokens were cached to storage...
|
|
assert data[CONF_USERNAME] == f"new_{USERNAME}"
|
|
assert data[SZ_REFRESH_TOKEN] == f"new_{REFRESH_TOKEN}"
|
|
assert data[SZ_ACCESS_TOKEN] == f"new_{ACCESS_TOKEN}"
|
|
|
|
assert (expires := data[SZ_ACCESS_TOKEN_EXPIRES]) is not None
|
|
assert dt_util.parse_datetime(expires, raise_on_error=True) > dt_util.now()
|