mirror of
https://github.com/home-assistant/core.git
synced 2025-04-24 01:08:12 +00:00
Store update settings in hassio store (#142526)
This commit is contained in:
parent
12ae70630f
commit
d5476a1da1
@ -55,7 +55,6 @@ from homeassistant.helpers.issue_registry import IssueSeverity, async_create_iss
|
||||
from homeassistant.helpers.service_info.hassio import (
|
||||
HassioServiceInfo as _HassioServiceInfo,
|
||||
)
|
||||
from homeassistant.helpers.storage import Store
|
||||
from homeassistant.helpers.typing import ConfigType
|
||||
from homeassistant.loader import bind_hass
|
||||
from homeassistant.util.async_ import create_eager_task
|
||||
@ -78,6 +77,7 @@ from . import ( # noqa: F401
|
||||
from .addon_manager import AddonError, AddonInfo, AddonManager, AddonState # noqa: F401
|
||||
from .addon_panel import async_setup_addon_panel
|
||||
from .auth import async_setup_auth_view
|
||||
from .config import HassioConfig
|
||||
from .const import (
|
||||
ADDONS_COORDINATOR,
|
||||
ATTR_ADDON,
|
||||
@ -91,6 +91,7 @@ from .const import (
|
||||
ATTR_PASSWORD,
|
||||
ATTR_SLUG,
|
||||
DATA_COMPONENT,
|
||||
DATA_CONFIG_STORE,
|
||||
DATA_CORE_INFO,
|
||||
DATA_HOST_INFO,
|
||||
DATA_INFO,
|
||||
@ -144,8 +145,6 @@ _DEPRECATED_HassioServiceInfo = DeprecatedConstant(
|
||||
"2025.11",
|
||||
)
|
||||
|
||||
STORAGE_KEY = DOMAIN
|
||||
STORAGE_VERSION = 1
|
||||
# If new platforms are added, be sure to import them above
|
||||
# so we do not make other components that depend on hassio
|
||||
# wait for the import of the platforms
|
||||
@ -335,13 +334,14 @@ async def async_setup(hass: HomeAssistant, config: ConfigType) -> bool: # noqa:
|
||||
except SupervisorError:
|
||||
_LOGGER.warning("Not connected with the supervisor / system too busy!")
|
||||
|
||||
store = Store[dict[str, str]](hass, STORAGE_VERSION, STORAGE_KEY)
|
||||
if (data := await store.async_load()) is None:
|
||||
data = {}
|
||||
# Load the store
|
||||
config_store = HassioConfig(hass)
|
||||
await config_store.load()
|
||||
hass.data[DATA_CONFIG_STORE] = config_store
|
||||
|
||||
refresh_token = None
|
||||
if "hassio_user" in data:
|
||||
user = await hass.auth.async_get_user(data["hassio_user"])
|
||||
if (hassio_user := config_store.data.hassio_user) is not None:
|
||||
user = await hass.auth.async_get_user(hassio_user)
|
||||
if user and user.refresh_tokens:
|
||||
refresh_token = list(user.refresh_tokens.values())[0]
|
||||
|
||||
@ -358,8 +358,7 @@ async def async_setup(hass: HomeAssistant, config: ConfigType) -> bool: # noqa:
|
||||
HASSIO_USER_NAME, group_ids=[GROUP_ID_ADMIN]
|
||||
)
|
||||
refresh_token = await hass.auth.async_create_refresh_token(user)
|
||||
data["hassio_user"] = user.id
|
||||
await store.async_save(data)
|
||||
config_store.update(hassio_user=user.id)
|
||||
|
||||
# This overrides the normal API call that would be forwarded
|
||||
development_repo = config.get(DOMAIN, {}).get(CONF_FRONTEND_REPO)
|
||||
|
@ -57,7 +57,7 @@ from homeassistant.helpers.dispatcher import async_dispatcher_connect
|
||||
from homeassistant.util import dt as dt_util
|
||||
from homeassistant.util.enum import try_parse_enum
|
||||
|
||||
from .const import DOMAIN, EVENT_SUPERVISOR_EVENT
|
||||
from .const import DATA_CONFIG_STORE, DOMAIN, EVENT_SUPERVISOR_EVENT
|
||||
from .handler import get_supervisor_client
|
||||
|
||||
MOUNT_JOBS = ("mount_manager_create_mount", "mount_manager_remove_mount")
|
||||
@ -729,6 +729,18 @@ async def backup_addon_before_update(
|
||||
if backup.extra_metadata.get(TAG_ADDON_UPDATE) == addon
|
||||
}
|
||||
|
||||
def _delete_filter(
|
||||
backups: dict[str, ManagerBackup],
|
||||
) -> dict[str, ManagerBackup]:
|
||||
"""Return oldest backups more numerous than copies to delete."""
|
||||
update_config = hass.data[DATA_CONFIG_STORE].data.update_config
|
||||
return dict(
|
||||
sorted(
|
||||
backups.items(),
|
||||
key=lambda backup_item: backup_item[1].date,
|
||||
)[: max(len(backups) - update_config.add_on_backup_retain_copies, 0)]
|
||||
)
|
||||
|
||||
try:
|
||||
await backup_manager.async_create_backup(
|
||||
agent_ids=[await _default_agent(client)],
|
||||
@ -747,7 +759,7 @@ async def backup_addon_before_update(
|
||||
try:
|
||||
await backup_manager.async_delete_filtered_backups(
|
||||
include_filter=addon_update_backup_filter,
|
||||
delete_filter=lambda backups: backups,
|
||||
delete_filter=_delete_filter,
|
||||
)
|
||||
except BackupManagerError as err:
|
||||
raise HomeAssistantError(f"Error deleting old backups: {err}") from err
|
||||
|
148
homeassistant/components/hassio/config.py
Normal file
148
homeassistant/components/hassio/config.py
Normal file
@ -0,0 +1,148 @@
|
||||
"""Provide persistent configuration for the hassio integration."""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
from dataclasses import dataclass, replace
|
||||
from typing import Required, Self, TypedDict
|
||||
|
||||
from homeassistant.core import HomeAssistant, callback
|
||||
from homeassistant.helpers.storage import Store
|
||||
from homeassistant.helpers.typing import UNDEFINED, UndefinedType
|
||||
|
||||
from .const import DOMAIN
|
||||
|
||||
STORE_DELAY_SAVE = 30
|
||||
STORAGE_KEY = DOMAIN
|
||||
STORAGE_VERSION = 1
|
||||
STORAGE_VERSION_MINOR = 1
|
||||
|
||||
|
||||
class HassioConfig:
|
||||
"""Handle update config."""
|
||||
|
||||
def __init__(self, hass: HomeAssistant) -> None:
|
||||
"""Initialize update config."""
|
||||
self.data = HassioConfigData(
|
||||
hassio_user=None,
|
||||
update_config=HassioUpdateConfig(),
|
||||
)
|
||||
self._hass = hass
|
||||
self._store = HassioConfigStore(hass, self)
|
||||
|
||||
async def load(self) -> None:
|
||||
"""Load config."""
|
||||
if not (store_data := await self._store.load()):
|
||||
return
|
||||
self.data = HassioConfigData.from_dict(store_data)
|
||||
|
||||
@callback
|
||||
def update(
|
||||
self,
|
||||
*,
|
||||
hassio_user: str | UndefinedType = UNDEFINED,
|
||||
update_config: HassioUpdateParametersDict | UndefinedType = UNDEFINED,
|
||||
) -> None:
|
||||
"""Update config."""
|
||||
if hassio_user is not UNDEFINED:
|
||||
self.data.hassio_user = hassio_user
|
||||
if update_config is not UNDEFINED:
|
||||
self.data.update_config = replace(self.data.update_config, **update_config)
|
||||
|
||||
self._store.save()
|
||||
|
||||
|
||||
@dataclass(kw_only=True)
|
||||
class HassioConfigData:
|
||||
"""Represent loaded update config data."""
|
||||
|
||||
hassio_user: str | None
|
||||
update_config: HassioUpdateConfig
|
||||
|
||||
@classmethod
|
||||
def from_dict(cls, data: StoredHassioConfig) -> Self:
|
||||
"""Initialize update config data from a dict."""
|
||||
if update_data := data.get("update_config"):
|
||||
update_config = HassioUpdateConfig(
|
||||
add_on_backup_before_update=update_data["add_on_backup_before_update"],
|
||||
add_on_backup_retain_copies=update_data["add_on_backup_retain_copies"],
|
||||
core_backup_before_update=update_data["core_backup_before_update"],
|
||||
)
|
||||
else:
|
||||
update_config = HassioUpdateConfig()
|
||||
return cls(
|
||||
hassio_user=data["hassio_user"],
|
||||
update_config=update_config,
|
||||
)
|
||||
|
||||
def to_dict(self) -> StoredHassioConfig:
|
||||
"""Convert update config data to a dict."""
|
||||
return StoredHassioConfig(
|
||||
hassio_user=self.hassio_user,
|
||||
update_config=self.update_config.to_dict(),
|
||||
)
|
||||
|
||||
|
||||
@dataclass(kw_only=True)
|
||||
class HassioUpdateConfig:
|
||||
"""Represent the backup retention configuration."""
|
||||
|
||||
add_on_backup_before_update: bool = False
|
||||
add_on_backup_retain_copies: int = 1
|
||||
core_backup_before_update: bool = False
|
||||
|
||||
def to_dict(self) -> StoredHassioUpdateConfig:
|
||||
"""Convert backup retention configuration to a dict."""
|
||||
return StoredHassioUpdateConfig(
|
||||
add_on_backup_before_update=self.add_on_backup_before_update,
|
||||
add_on_backup_retain_copies=self.add_on_backup_retain_copies,
|
||||
core_backup_before_update=self.core_backup_before_update,
|
||||
)
|
||||
|
||||
|
||||
class HassioUpdateParametersDict(TypedDict, total=False):
|
||||
"""Represent the parameters for update."""
|
||||
|
||||
add_on_backup_before_update: bool
|
||||
add_on_backup_retain_copies: int
|
||||
core_backup_before_update: bool
|
||||
|
||||
|
||||
class HassioConfigStore:
|
||||
"""Store hassio config."""
|
||||
|
||||
def __init__(self, hass: HomeAssistant, config: HassioConfig) -> None:
|
||||
"""Initialize the hassio config store."""
|
||||
self._hass = hass
|
||||
self._config = config
|
||||
self._store: Store[StoredHassioConfig] = Store(
|
||||
hass, STORAGE_VERSION, STORAGE_KEY, minor_version=STORAGE_VERSION_MINOR
|
||||
)
|
||||
|
||||
async def load(self) -> StoredHassioConfig | None:
|
||||
"""Load the store."""
|
||||
return await self._store.async_load()
|
||||
|
||||
@callback
|
||||
def save(self) -> None:
|
||||
"""Save config."""
|
||||
self._store.async_delay_save(self._data_to_save, STORE_DELAY_SAVE)
|
||||
|
||||
@callback
|
||||
def _data_to_save(self) -> StoredHassioConfig:
|
||||
"""Return data to save."""
|
||||
return self._config.data.to_dict()
|
||||
|
||||
|
||||
class StoredHassioConfig(TypedDict, total=False):
|
||||
"""Represent the stored hassio config."""
|
||||
|
||||
hassio_user: Required[str | None]
|
||||
update_config: StoredHassioUpdateConfig
|
||||
|
||||
|
||||
class StoredHassioUpdateConfig(TypedDict):
|
||||
"""Represent the stored update config."""
|
||||
|
||||
add_on_backup_before_update: bool
|
||||
add_on_backup_retain_copies: int
|
||||
core_backup_before_update: bool
|
@ -9,6 +9,7 @@ from typing import TYPE_CHECKING
|
||||
from homeassistant.util.hass_dict import HassKey
|
||||
|
||||
if TYPE_CHECKING:
|
||||
from .config import HassioConfig
|
||||
from .handler import HassIO
|
||||
|
||||
|
||||
@ -74,6 +75,7 @@ ADDONS_COORDINATOR = "hassio_addons_coordinator"
|
||||
|
||||
|
||||
DATA_COMPONENT: HassKey[HassIO] = HassKey(DOMAIN)
|
||||
DATA_CONFIG_STORE: HassKey[HassioConfig] = HassKey("hassio_config_store")
|
||||
DATA_CORE_INFO = "hassio_core_info"
|
||||
DATA_CORE_STATS = "hassio_core_stats"
|
||||
DATA_HOST_INFO = "hassio_host_info"
|
||||
|
@ -3,7 +3,7 @@
|
||||
import logging
|
||||
from numbers import Number
|
||||
import re
|
||||
from typing import Any
|
||||
from typing import Any, cast
|
||||
|
||||
import voluptuous as vol
|
||||
|
||||
@ -19,6 +19,7 @@ from homeassistant.helpers.dispatcher import (
|
||||
)
|
||||
|
||||
from . import HassioAPIError
|
||||
from .config import HassioUpdateParametersDict
|
||||
from .const import (
|
||||
ATTR_DATA,
|
||||
ATTR_ENDPOINT,
|
||||
@ -29,6 +30,7 @@ from .const import (
|
||||
ATTR_VERSION,
|
||||
ATTR_WS_EVENT,
|
||||
DATA_COMPONENT,
|
||||
DATA_CONFIG_STORE,
|
||||
EVENT_SUPERVISOR_EVENT,
|
||||
WS_ID,
|
||||
WS_TYPE,
|
||||
@ -65,6 +67,8 @@ def async_load_websocket_api(hass: HomeAssistant) -> None:
|
||||
websocket_api.async_register_command(hass, websocket_subscribe)
|
||||
websocket_api.async_register_command(hass, websocket_update_addon)
|
||||
websocket_api.async_register_command(hass, websocket_update_core)
|
||||
websocket_api.async_register_command(hass, websocket_update_config_info)
|
||||
websocket_api.async_register_command(hass, websocket_update_config_update)
|
||||
|
||||
|
||||
@callback
|
||||
@ -185,3 +189,42 @@ async def websocket_update_core(
|
||||
"""Websocket handler to update Home Assistant Core."""
|
||||
await update_core(hass, None, msg["backup"])
|
||||
connection.send_result(msg[WS_ID])
|
||||
|
||||
|
||||
@callback
|
||||
@websocket_api.require_admin
|
||||
@websocket_api.websocket_command({vol.Required("type"): "hassio/update/config/info"})
|
||||
def websocket_update_config_info(
|
||||
hass: HomeAssistant,
|
||||
connection: websocket_api.ActiveConnection,
|
||||
msg: dict[str, Any],
|
||||
) -> None:
|
||||
"""Send the stored backup config."""
|
||||
connection.send_result(
|
||||
msg["id"], hass.data[DATA_CONFIG_STORE].data.update_config.to_dict()
|
||||
)
|
||||
|
||||
|
||||
@callback
|
||||
@websocket_api.require_admin
|
||||
@websocket_api.websocket_command(
|
||||
{
|
||||
vol.Required("type"): "hassio/update/config/update",
|
||||
vol.Optional("add_on_backup_before_update"): bool,
|
||||
vol.Optional("add_on_backup_retain_copies"): vol.All(int, vol.Range(min=1)),
|
||||
vol.Optional("core_backup_before_update"): bool,
|
||||
}
|
||||
)
|
||||
def websocket_update_config_update(
|
||||
hass: HomeAssistant,
|
||||
connection: websocket_api.ActiveConnection,
|
||||
msg: dict[str, Any],
|
||||
) -> None:
|
||||
"""Update the stored backup config."""
|
||||
changes = dict(msg)
|
||||
changes.pop("id")
|
||||
changes.pop("type")
|
||||
hass.data[DATA_CONFIG_STORE].update(
|
||||
update_config=cast(HassioUpdateParametersDict, changes)
|
||||
)
|
||||
connection.send_result(msg["id"])
|
||||
|
46
tests/components/hassio/snapshots/test_config.ambr
Normal file
46
tests/components/hassio/snapshots/test_config.ambr
Normal file
@ -0,0 +1,46 @@
|
||||
# serializer version: 1
|
||||
# name: test_load_config_store[storage_data0]
|
||||
dict({
|
||||
'hassio_user': '766572795f764572b95f72616e646f6d',
|
||||
'update_config': dict({
|
||||
'add_on_backup_before_update': False,
|
||||
'add_on_backup_retain_copies': 1,
|
||||
'core_backup_before_update': False,
|
||||
}),
|
||||
})
|
||||
# ---
|
||||
# name: test_load_config_store[storage_data1]
|
||||
dict({
|
||||
'hassio_user': '00112233445566778899aabbccddeeff',
|
||||
'update_config': dict({
|
||||
'add_on_backup_before_update': False,
|
||||
'add_on_backup_retain_copies': 1,
|
||||
'core_backup_before_update': False,
|
||||
}),
|
||||
})
|
||||
# ---
|
||||
# name: test_load_config_store[storage_data2]
|
||||
dict({
|
||||
'hassio_user': '00112233445566778899aabbccddeeff',
|
||||
'update_config': dict({
|
||||
'add_on_backup_before_update': True,
|
||||
'add_on_backup_retain_copies': 2,
|
||||
'core_backup_before_update': True,
|
||||
}),
|
||||
})
|
||||
# ---
|
||||
# name: test_save_config_store
|
||||
dict({
|
||||
'data': dict({
|
||||
'hassio_user': '766572795f764572b95f72616e646f6d',
|
||||
'update_config': dict({
|
||||
'add_on_backup_before_update': False,
|
||||
'add_on_backup_retain_copies': 1,
|
||||
'core_backup_before_update': False,
|
||||
}),
|
||||
}),
|
||||
'key': 'hassio',
|
||||
'minor_version': 1,
|
||||
'version': 1,
|
||||
})
|
||||
# ---
|
33
tests/components/hassio/snapshots/test_websocket_api.ambr
Normal file
33
tests/components/hassio/snapshots/test_websocket_api.ambr
Normal file
@ -0,0 +1,33 @@
|
||||
# serializer version: 1
|
||||
# name: test_read_update_config
|
||||
dict({
|
||||
'id': 1,
|
||||
'result': dict({
|
||||
'add_on_backup_before_update': False,
|
||||
'add_on_backup_retain_copies': 1,
|
||||
'core_backup_before_update': False,
|
||||
}),
|
||||
'success': True,
|
||||
'type': 'result',
|
||||
})
|
||||
# ---
|
||||
# name: test_read_update_config.1
|
||||
dict({
|
||||
'id': 2,
|
||||
'result': None,
|
||||
'success': True,
|
||||
'type': 'result',
|
||||
})
|
||||
# ---
|
||||
# name: test_read_update_config.2
|
||||
dict({
|
||||
'id': 3,
|
||||
'result': dict({
|
||||
'add_on_backup_before_update': True,
|
||||
'add_on_backup_retain_copies': 2,
|
||||
'core_backup_before_update': True,
|
||||
}),
|
||||
'success': True,
|
||||
'type': 'result',
|
||||
})
|
||||
# ---
|
182
tests/components/hassio/test_config.py
Normal file
182
tests/components/hassio/test_config.py
Normal file
@ -0,0 +1,182 @@
|
||||
"""Test websocket API."""
|
||||
|
||||
from typing import Any
|
||||
from unittest.mock import AsyncMock, patch
|
||||
from uuid import UUID
|
||||
|
||||
import pytest
|
||||
from syrupy import SnapshotAssertion
|
||||
|
||||
from homeassistant.auth.const import GROUP_ID_ADMIN
|
||||
from homeassistant.components.hassio.const import DATA_CONFIG_STORE, DOMAIN
|
||||
from homeassistant.core import HomeAssistant
|
||||
from homeassistant.setup import async_setup_component
|
||||
|
||||
from tests.common import MockUser
|
||||
from tests.test_util.aiohttp import AiohttpClientMocker
|
||||
from tests.typing import WebSocketGenerator
|
||||
|
||||
MOCK_ENVIRON = {"SUPERVISOR": "127.0.0.1", "SUPERVISOR_TOKEN": "abcdefgh"}
|
||||
|
||||
|
||||
@pytest.fixture(autouse=True)
|
||||
def mock_all(
|
||||
aioclient_mock: AiohttpClientMocker,
|
||||
supervisor_is_connected: AsyncMock,
|
||||
resolution_info: AsyncMock,
|
||||
addon_info: AsyncMock,
|
||||
) -> None:
|
||||
"""Mock all setup requests."""
|
||||
aioclient_mock.post("http://127.0.0.1/homeassistant/options", json={"result": "ok"})
|
||||
aioclient_mock.post("http://127.0.0.1/supervisor/options", json={"result": "ok"})
|
||||
aioclient_mock.get(
|
||||
"http://127.0.0.1/info",
|
||||
json={
|
||||
"result": "ok",
|
||||
"data": {"supervisor": "222", "homeassistant": "0.110.0", "hassos": None},
|
||||
},
|
||||
)
|
||||
aioclient_mock.get(
|
||||
"http://127.0.0.1/host/info",
|
||||
json={
|
||||
"result": "ok",
|
||||
"data": {
|
||||
"result": "ok",
|
||||
"data": {
|
||||
"chassis": "vm",
|
||||
"operating_system": "Debian GNU/Linux 10 (buster)",
|
||||
"kernel": "4.19.0-6-amd64",
|
||||
},
|
||||
},
|
||||
},
|
||||
)
|
||||
aioclient_mock.get(
|
||||
"http://127.0.0.1/core/info",
|
||||
json={"result": "ok", "data": {"version_latest": "1.0.0", "version": "1.0.0"}},
|
||||
)
|
||||
aioclient_mock.get(
|
||||
"http://127.0.0.1/os/info",
|
||||
json={"result": "ok", "data": {"version_latest": "1.0.0"}},
|
||||
)
|
||||
aioclient_mock.get(
|
||||
"http://127.0.0.1/supervisor/info",
|
||||
json={
|
||||
"result": "ok",
|
||||
"data": {
|
||||
"version": "1.0.0",
|
||||
"version_latest": "1.0.0",
|
||||
"auto_update": True,
|
||||
"addons": [
|
||||
{
|
||||
"name": "test",
|
||||
"state": "started",
|
||||
"slug": "test",
|
||||
"installed": True,
|
||||
"update_available": True,
|
||||
"icon": False,
|
||||
"version": "2.0.0",
|
||||
"version_latest": "2.0.1",
|
||||
"repository": "core",
|
||||
"url": "https://github.com/home-assistant/addons/test",
|
||||
},
|
||||
],
|
||||
},
|
||||
},
|
||||
)
|
||||
aioclient_mock.get(
|
||||
"http://127.0.0.1/ingress/panels", json={"result": "ok", "data": {"panels": {}}}
|
||||
)
|
||||
aioclient_mock.get(
|
||||
"http://127.0.0.1/network/info",
|
||||
json={
|
||||
"result": "ok",
|
||||
"data": {
|
||||
"host_internet": True,
|
||||
"supervisor_internet": True,
|
||||
},
|
||||
},
|
||||
)
|
||||
|
||||
|
||||
@pytest.mark.usefixtures("hassio_env")
|
||||
@pytest.mark.parametrize(
|
||||
"storage_data",
|
||||
[
|
||||
{},
|
||||
{
|
||||
"hassio": {
|
||||
"data": {
|
||||
"hassio_user": "00112233445566778899aabbccddeeff",
|
||||
"update_config": {
|
||||
"add_on_backup_before_update": False,
|
||||
"add_on_backup_retain_copies": 1,
|
||||
"core_backup_before_update": False,
|
||||
},
|
||||
},
|
||||
"key": "hassio",
|
||||
"minor_version": 1,
|
||||
"version": 1,
|
||||
}
|
||||
},
|
||||
{
|
||||
"hassio": {
|
||||
"data": {
|
||||
"hassio_user": "00112233445566778899aabbccddeeff",
|
||||
"update_config": {
|
||||
"add_on_backup_before_update": True,
|
||||
"add_on_backup_retain_copies": 2,
|
||||
"core_backup_before_update": True,
|
||||
},
|
||||
},
|
||||
"key": "hassio",
|
||||
"minor_version": 1,
|
||||
"version": 1,
|
||||
}
|
||||
},
|
||||
],
|
||||
)
|
||||
async def test_load_config_store(
|
||||
hass: HomeAssistant,
|
||||
hass_ws_client: WebSocketGenerator,
|
||||
supervisor_client: AsyncMock,
|
||||
hass_storage: dict[str, Any],
|
||||
storage_data: dict[str, dict[str, Any]],
|
||||
snapshot: SnapshotAssertion,
|
||||
) -> None:
|
||||
"""Test loading the config store."""
|
||||
hass_storage.update(storage_data)
|
||||
|
||||
user = MockUser(id="00112233445566778899aabbccddeeff", system_generated=True)
|
||||
user.add_to_hass(hass)
|
||||
await hass.auth.async_create_refresh_token(user)
|
||||
await hass.auth.async_update_user(user, group_ids=[GROUP_ID_ADMIN])
|
||||
|
||||
with (
|
||||
patch("homeassistant.components.hassio.config.STORE_DELAY_SAVE", 0),
|
||||
patch("uuid.uuid4", return_value=UUID(bytes=b"very_very_random", version=4)),
|
||||
):
|
||||
assert await async_setup_component(hass, "hassio", {})
|
||||
await hass.async_block_till_done()
|
||||
await hass.async_block_till_done()
|
||||
|
||||
assert hass.data[DATA_CONFIG_STORE].data.to_dict() == snapshot
|
||||
|
||||
|
||||
@pytest.mark.usefixtures("hassio_env")
|
||||
async def test_save_config_store(
|
||||
hass: HomeAssistant,
|
||||
hass_ws_client: WebSocketGenerator,
|
||||
supervisor_client: AsyncMock,
|
||||
hass_storage: dict[str, Any],
|
||||
snapshot: SnapshotAssertion,
|
||||
) -> None:
|
||||
"""Test saving the config store."""
|
||||
with (
|
||||
patch("homeassistant.components.hassio.config.STORE_DELAY_SAVE", 0),
|
||||
patch("uuid.uuid4", return_value=UUID(bytes=b"very_very_random", version=4)),
|
||||
):
|
||||
assert await async_setup_component(hass, "hassio", {})
|
||||
await hass.async_block_till_done()
|
||||
await hass.async_block_till_done()
|
||||
|
||||
assert hass_storage[DOMAIN] == snapshot
|
@ -17,12 +17,12 @@ from homeassistant.components.binary_sensor import DOMAIN as BINARY_SENSOR_DOMAI
|
||||
from homeassistant.components.hassio import (
|
||||
ADDONS_COORDINATOR,
|
||||
DOMAIN,
|
||||
STORAGE_KEY,
|
||||
get_core_info,
|
||||
get_supervisor_ip,
|
||||
hostname_from_addon_slug,
|
||||
is_hassio as deprecated_is_hassio,
|
||||
)
|
||||
from homeassistant.components.hassio.config import STORAGE_KEY
|
||||
from homeassistant.components.hassio.const import REQUEST_REFRESH_DELAY
|
||||
from homeassistant.components.sensor import DOMAIN as SENSOR_DOMAIN
|
||||
from homeassistant.core import HomeAssistant
|
||||
@ -309,7 +309,10 @@ async def test_setup_api_push_api_data_default(
|
||||
supervisor_client: AsyncMock,
|
||||
) -> None:
|
||||
"""Test setup with API push default data."""
|
||||
with patch.dict(os.environ, MOCK_ENVIRON):
|
||||
with (
|
||||
patch.dict(os.environ, MOCK_ENVIRON),
|
||||
patch("homeassistant.components.hassio.config.STORE_DELAY_SAVE", 0),
|
||||
):
|
||||
result = await async_setup_component(hass, "hassio", {"http": {}, "hassio": {}})
|
||||
await hass.async_block_till_done()
|
||||
|
||||
|
@ -7,6 +7,7 @@ from unittest.mock import AsyncMock, MagicMock, patch
|
||||
from aiohasupervisor import SupervisorError
|
||||
from aiohasupervisor.models import HomeAssistantUpdateOptions, StoreAddonUpdate
|
||||
import pytest
|
||||
from syrupy import SnapshotAssertion
|
||||
|
||||
from homeassistant.components.backup import BackupManagerError, ManagerBackup
|
||||
|
||||
@ -469,13 +470,15 @@ async def test_update_addon_with_backup(
|
||||
|
||||
|
||||
@pytest.mark.parametrize(
|
||||
("backups", "removed_backups"),
|
||||
("ws_commands", "backups", "removed_backups"),
|
||||
[
|
||||
(
|
||||
[],
|
||||
{},
|
||||
[],
|
||||
),
|
||||
(
|
||||
[],
|
||||
{
|
||||
"backup-1": MagicMock(
|
||||
agents={"hassio.local": MagicMock(spec=AgentBackupStatus)},
|
||||
@ -520,6 +523,52 @@ async def test_update_addon_with_backup(
|
||||
},
|
||||
["backup-5"],
|
||||
),
|
||||
(
|
||||
[{"type": "hassio/update/config/update", "add_on_backup_retain_copies": 2}],
|
||||
{
|
||||
"backup-1": MagicMock(
|
||||
agents={"hassio.local": MagicMock(spec=AgentBackupStatus)},
|
||||
date="2024-11-10T04:45:00+01:00",
|
||||
with_automatic_settings=True,
|
||||
spec=ManagerBackup,
|
||||
),
|
||||
"backup-2": MagicMock(
|
||||
agents={"hassio.local": MagicMock(spec=AgentBackupStatus)},
|
||||
date="2024-11-11T04:45:00+01:00",
|
||||
with_automatic_settings=False,
|
||||
spec=ManagerBackup,
|
||||
),
|
||||
"backup-3": MagicMock(
|
||||
agents={"hassio.local": MagicMock(spec=AgentBackupStatus)},
|
||||
date="2024-11-11T04:45:00+01:00",
|
||||
extra_metadata={"supervisor.addon_update": "other"},
|
||||
with_automatic_settings=True,
|
||||
spec=ManagerBackup,
|
||||
),
|
||||
"backup-4": MagicMock(
|
||||
agents={"hassio.local": MagicMock(spec=AgentBackupStatus)},
|
||||
date="2024-11-11T04:45:00+01:00",
|
||||
extra_metadata={"supervisor.addon_update": "other"},
|
||||
with_automatic_settings=True,
|
||||
spec=ManagerBackup,
|
||||
),
|
||||
"backup-5": MagicMock(
|
||||
agents={"hassio.local": MagicMock(spec=AgentBackupStatus)},
|
||||
date="2024-11-11T04:45:00+01:00",
|
||||
extra_metadata={"supervisor.addon_update": "test"},
|
||||
with_automatic_settings=True,
|
||||
spec=ManagerBackup,
|
||||
),
|
||||
"backup-6": MagicMock(
|
||||
agents={"hassio.local": MagicMock(spec=AgentBackupStatus)},
|
||||
date="2024-11-12T04:45:00+01:00",
|
||||
extra_metadata={"supervisor.addon_update": "test"},
|
||||
with_automatic_settings=True,
|
||||
spec=ManagerBackup,
|
||||
),
|
||||
},
|
||||
[],
|
||||
),
|
||||
],
|
||||
)
|
||||
async def test_update_addon_with_backup_removes_old_backups(
|
||||
@ -527,6 +576,7 @@ async def test_update_addon_with_backup_removes_old_backups(
|
||||
hass_ws_client: WebSocketGenerator,
|
||||
supervisor_client: AsyncMock,
|
||||
update_addon: AsyncMock,
|
||||
ws_commands: list[dict[str, Any]],
|
||||
backups: dict[str, ManagerBackup],
|
||||
removed_backups: list[str],
|
||||
) -> None:
|
||||
@ -544,6 +594,12 @@ async def test_update_addon_with_backup_removes_old_backups(
|
||||
await setup_backup_integration(hass)
|
||||
|
||||
client = await hass_ws_client(hass)
|
||||
|
||||
for command in ws_commands:
|
||||
await client.send_json_auto_id(command)
|
||||
result = await client.receive_json()
|
||||
assert result["success"]
|
||||
|
||||
supervisor_client.mounts.info.return_value.default_backup_mount = None
|
||||
with (
|
||||
patch(
|
||||
@ -856,3 +912,31 @@ async def test_update_core_with_backup_and_error(
|
||||
"code": "home_assistant_error",
|
||||
"message": "Error creating backup: ",
|
||||
}
|
||||
|
||||
|
||||
@pytest.mark.usefixtures("hassio_env")
|
||||
async def test_read_update_config(
|
||||
hass: HomeAssistant,
|
||||
hass_ws_client: WebSocketGenerator,
|
||||
supervisor_client: AsyncMock,
|
||||
snapshot: SnapshotAssertion,
|
||||
) -> None:
|
||||
"""Test read and update config."""
|
||||
assert await async_setup_component(hass, "hassio", {})
|
||||
websocket_client = await hass_ws_client(hass)
|
||||
|
||||
await websocket_client.send_json_auto_id({"type": "hassio/update/config/info"})
|
||||
assert await websocket_client.receive_json() == snapshot
|
||||
|
||||
await websocket_client.send_json_auto_id(
|
||||
{
|
||||
"type": "hassio/update/config/update",
|
||||
"add_on_backup_before_update": True,
|
||||
"add_on_backup_retain_copies": 2,
|
||||
"core_backup_before_update": True,
|
||||
}
|
||||
)
|
||||
assert await websocket_client.receive_json() == snapshot
|
||||
|
||||
await websocket_client.send_json_auto_id({"type": "hassio/update/config/info"})
|
||||
assert await websocket_client.receive_json() == snapshot
|
||||
|
Loading…
x
Reference in New Issue
Block a user