Update hassio to use the backup integration to make backups before update (#136235)

Co-authored-by: Martin Hjelmare <marhje52@gmail.com>
This commit is contained in:
Erik Montnemery
2025-01-27 08:25:22 +01:00
committed by GitHub
parent 69938545df
commit 245ee2498e
10 changed files with 979 additions and 64 deletions

View File

@@ -1,9 +1,15 @@
"""Test websocket API."""
from unittest.mock import AsyncMock
import os
from typing import Any
from unittest.mock import AsyncMock, patch
from aiohasupervisor import SupervisorError
from aiohasupervisor.models import HomeAssistantUpdateOptions, StoreAddonUpdate
import pytest
from homeassistant.components.backup import BackupManagerError
from homeassistant.components.hassio import DOMAIN
from homeassistant.components.hassio.const import (
ATTR_DATA,
ATTR_ENDPOINT,
@@ -15,14 +21,17 @@ from homeassistant.components.hassio.const import (
WS_TYPE_API,
WS_TYPE_SUBSCRIBE,
)
from homeassistant.const import __version__ as HAVERSION
from homeassistant.core import HomeAssistant
from homeassistant.helpers.dispatcher import async_dispatcher_send
from homeassistant.setup import async_setup_component
from tests.common import MockUser, async_mock_signal
from tests.common import MockConfigEntry, MockUser, async_mock_signal
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(
@@ -56,7 +65,7 @@ def mock_all(
)
aioclient_mock.get(
"http://127.0.0.1/core/info",
json={"result": "ok", "data": {"version_latest": "1.0.0"}},
json={"result": "ok", "data": {"version_latest": "1.0.0", "version": "1.0.0"}},
)
aioclient_mock.get(
"http://127.0.0.1/os/info",
@@ -64,11 +73,42 @@ def mock_all(
)
aioclient_mock.get(
"http://127.0.0.1/supervisor/info",
json={"result": "ok", "data": {"version_latest": "1.0.0"}},
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")
@@ -279,3 +319,407 @@ async def test_websocket_non_admin_user(
msg = await websocket_client.receive_json()
assert msg["error"]["message"] == "Unauthorized"
async def test_update_addon(
hass: HomeAssistant,
hass_ws_client: WebSocketGenerator,
update_addon: AsyncMock,
) -> None:
"""Test updating addon."""
client = await hass_ws_client(hass)
config_entry = MockConfigEntry(domain=DOMAIN, data={}, unique_id=DOMAIN)
config_entry.add_to_hass(hass)
with patch.dict(os.environ, MOCK_ENVIRON):
result = await async_setup_component(
hass,
"hassio",
{"http": {"server_port": 9999, "server_host": "127.0.0.1"}, "hassio": {}},
)
assert result
await hass.async_block_till_done()
with patch(
"homeassistant.components.backup.manager.BackupManager.async_create_backup",
) as mock_create_backup:
await client.send_json_auto_id(
{"type": "hassio/update/addon", "addon": "test", "backup": False}
)
result = await client.receive_json()
assert result["success"]
mock_create_backup.assert_not_called()
update_addon.assert_called_once_with("test", StoreAddonUpdate(backup=False))
@pytest.mark.parametrize(
("commands", "default_mount", "expected_kwargs"),
[
(
[],
None,
{
"agent_ids": ["hassio.local"],
"include_addons": ["test"],
"include_all_addons": False,
"include_database": False,
"include_folders": None,
"include_homeassistant": False,
"name": "test 2.0.0",
"password": None,
},
),
(
[],
"my_nas",
{
"agent_ids": ["hassio.my_nas"],
"include_addons": ["test"],
"include_all_addons": False,
"include_database": False,
"include_folders": None,
"include_homeassistant": False,
"name": "test 2.0.0",
"password": None,
},
),
(
[
{
"type": "backup/config/update",
"create_backup": {
"agent_ids": ["test-agent"],
"include_addons": ["my-addon"],
"include_all_addons": True,
"include_database": False,
"include_folders": ["share"],
"name": "cool_backup",
"password": "hunter2",
},
},
],
None,
{
"agent_ids": ["hassio.local"],
"include_addons": ["test"],
"include_all_addons": False,
"include_database": False,
"include_folders": None,
"include_homeassistant": False,
"name": "test 2.0.0",
"password": "hunter2",
},
),
],
)
async def test_update_addon_with_backup(
hass: HomeAssistant,
hass_ws_client: WebSocketGenerator,
supervisor_client: AsyncMock,
update_addon: AsyncMock,
commands: list[dict[str, Any]],
default_mount: str | None,
expected_kwargs: dict[str, Any],
) -> None:
"""Test updating addon with backup."""
config_entry = MockConfigEntry(domain=DOMAIN, data={}, unique_id=DOMAIN)
config_entry.add_to_hass(hass)
with patch.dict(os.environ, MOCK_ENVIRON):
result = await async_setup_component(
hass,
"hassio",
{"http": {"server_port": 9999, "server_host": "127.0.0.1"}, "hassio": {}},
)
assert result
assert await async_setup_component(hass, "backup", {})
await hass.async_block_till_done()
client = await hass_ws_client(hass)
for command in 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 = default_mount
with patch(
"homeassistant.components.backup.manager.BackupManager.async_create_backup",
) as mock_create_backup:
await client.send_json_auto_id(
{"type": "hassio/update/addon", "addon": "test", "backup": True}
)
result = await client.receive_json()
assert result["success"]
mock_create_backup.assert_called_once_with(**expected_kwargs)
update_addon.assert_called_once_with("test", StoreAddonUpdate(backup=False))
async def test_update_core(
hass: HomeAssistant,
hass_ws_client: WebSocketGenerator,
supervisor_client: AsyncMock,
) -> None:
"""Test updating core."""
client = await hass_ws_client(hass)
config_entry = MockConfigEntry(domain=DOMAIN, data={}, unique_id=DOMAIN)
config_entry.add_to_hass(hass)
with patch.dict(os.environ, MOCK_ENVIRON):
result = await async_setup_component(
hass,
"hassio",
{"http": {"server_port": 9999, "server_host": "127.0.0.1"}, "hassio": {}},
)
assert result
await hass.async_block_till_done()
supervisor_client.homeassistant.update.return_value = None
with patch(
"homeassistant.components.backup.manager.BackupManager.async_create_backup",
) as mock_create_backup:
await client.send_json_auto_id({"type": "hassio/update/core", "backup": False})
result = await client.receive_json()
assert result["success"]
mock_create_backup.assert_not_called()
supervisor_client.homeassistant.update.assert_called_once_with(
HomeAssistantUpdateOptions(version=None, backup=False)
)
@pytest.mark.parametrize(
("commands", "default_mount", "expected_kwargs"),
[
(
[],
None,
{
"agent_ids": ["hassio.local"],
"include_addons": None,
"include_all_addons": False,
"include_database": True,
"include_folders": None,
"include_homeassistant": True,
"name": f"Home Assistant Core {HAVERSION}",
"password": None,
},
),
(
[],
"my_nas",
{
"agent_ids": ["hassio.my_nas"],
"include_addons": None,
"include_all_addons": False,
"include_database": True,
"include_folders": None,
"include_homeassistant": True,
"name": f"Home Assistant Core {HAVERSION}",
"password": None,
},
),
(
[
{
"type": "backup/config/update",
"create_backup": {
"agent_ids": ["test-agent"],
"include_addons": ["my-addon"],
"include_all_addons": True,
"include_database": False,
"include_folders": ["share"],
"name": "cool_backup",
"password": "hunter2",
},
},
],
None,
{
"agent_ids": ["test-agent"],
"include_addons": ["my-addon"],
"include_all_addons": True,
"include_database": False,
"include_folders": ["share"],
"include_homeassistant": True,
"name": "cool_backup",
"password": "hunter2",
"with_automatic_settings": True,
},
),
],
)
async def test_update_core_with_backup(
hass: HomeAssistant,
hass_ws_client: WebSocketGenerator,
supervisor_client: AsyncMock,
commands: list[dict[str, Any]],
default_mount: str | None,
expected_kwargs: dict[str, Any],
) -> None:
"""Test updating core with backup."""
config_entry = MockConfigEntry(domain=DOMAIN, data={}, unique_id=DOMAIN)
config_entry.add_to_hass(hass)
with patch.dict(os.environ, MOCK_ENVIRON):
result = await async_setup_component(
hass,
"hassio",
{"http": {"server_port": 9999, "server_host": "127.0.0.1"}, "hassio": {}},
)
assert result
assert await async_setup_component(hass, "backup", {})
await hass.async_block_till_done()
client = await hass_ws_client(hass)
for command in commands:
await client.send_json_auto_id(command)
result = await client.receive_json()
assert result["success"]
supervisor_client.homeassistant.update.return_value = None
supervisor_client.mounts.info.return_value.default_backup_mount = default_mount
with patch(
"homeassistant.components.backup.manager.BackupManager.async_create_backup",
) as mock_create_backup:
await client.send_json_auto_id({"type": "hassio/update/core", "backup": True})
result = await client.receive_json()
assert result["success"]
mock_create_backup.assert_called_once_with(**expected_kwargs)
supervisor_client.homeassistant.update.assert_called_once_with(
HomeAssistantUpdateOptions(version=None, backup=False)
)
async def test_update_addon_with_error(
hass: HomeAssistant,
hass_ws_client: WebSocketGenerator,
update_addon: AsyncMock,
) -> None:
"""Test updating addon with error."""
client = await hass_ws_client(hass)
config_entry = MockConfigEntry(domain=DOMAIN, data={}, unique_id=DOMAIN)
config_entry.add_to_hass(hass)
with patch.dict(os.environ, MOCK_ENVIRON):
assert await async_setup_component(
hass,
"hassio",
{"http": {"server_port": 9999, "server_host": "127.0.0.1"}, "hassio": {}},
)
await hass.async_block_till_done()
update_addon.side_effect = SupervisorError
await client.send_json_auto_id(
{"type": "hassio/update/addon", "addon": "test", "backup": False}
)
result = await client.receive_json()
assert not result["success"]
assert result["error"] == {
"code": "home_assistant_error",
"message": "Error updating test: ",
}
async def test_update_addon_with_backup_and_error(
hass: HomeAssistant,
hass_ws_client: WebSocketGenerator,
supervisor_client: AsyncMock,
) -> None:
"""Test updating addon with backup and error."""
client = await hass_ws_client(hass)
config_entry = MockConfigEntry(domain=DOMAIN, data={}, unique_id=DOMAIN)
config_entry.add_to_hass(hass)
with patch.dict(os.environ, MOCK_ENVIRON):
result = await async_setup_component(
hass,
"hassio",
{"http": {"server_port": 9999, "server_host": "127.0.0.1"}, "hassio": {}},
)
assert result
assert await async_setup_component(hass, "backup", {})
await hass.async_block_till_done()
supervisor_client.homeassistant.update.return_value = None
supervisor_client.mounts.info.return_value.default_backup_mount = None
with (
patch(
"homeassistant.components.backup.manager.BackupManager.async_create_backup",
side_effect=BackupManagerError,
),
):
await client.send_json_auto_id(
{"type": "hassio/update/addon", "addon": "test", "backup": True}
)
result = await client.receive_json()
assert not result["success"]
assert result["error"] == {
"code": "home_assistant_error",
"message": "Error creating backup: ",
}
async def test_update_core_with_error(
hass: HomeAssistant,
hass_ws_client: WebSocketGenerator,
supervisor_client: AsyncMock,
) -> None:
"""Test updating core with error."""
client = await hass_ws_client(hass)
config_entry = MockConfigEntry(domain=DOMAIN, data={}, unique_id=DOMAIN)
config_entry.add_to_hass(hass)
with patch.dict(os.environ, MOCK_ENVIRON):
assert await async_setup_component(
hass,
"hassio",
{"http": {"server_port": 9999, "server_host": "127.0.0.1"}, "hassio": {}},
)
await hass.async_block_till_done()
supervisor_client.homeassistant.update.side_effect = SupervisorError
await client.send_json_auto_id({"type": "hassio/update/core", "backup": False})
result = await client.receive_json()
assert not result["success"]
assert result["error"] == {
"code": "home_assistant_error",
"message": "Error updating Home Assistant Core: ",
}
async def test_update_core_with_backup_and_error(
hass: HomeAssistant,
hass_ws_client: WebSocketGenerator,
supervisor_client: AsyncMock,
) -> None:
"""Test updating core with backup and error."""
client = await hass_ws_client(hass)
config_entry = MockConfigEntry(domain=DOMAIN, data={}, unique_id=DOMAIN)
config_entry.add_to_hass(hass)
with patch.dict(os.environ, MOCK_ENVIRON):
result = await async_setup_component(
hass,
"hassio",
{"http": {"server_port": 9999, "server_host": "127.0.0.1"}, "hassio": {}},
)
assert result
assert await async_setup_component(hass, "backup", {})
await hass.async_block_till_done()
supervisor_client.homeassistant.update.return_value = None
supervisor_client.mounts.info.return_value.default_backup_mount = None
with (
patch(
"homeassistant.components.backup.manager.BackupManager.async_create_backup",
side_effect=BackupManagerError,
),
):
await client.send_json_auto_id(
{"type": "hassio/update/addon", "addon": "test", "backup": True}
)
result = await client.receive_json()
assert not result["success"]
assert result["error"] == {
"code": "home_assistant_error",
"message": "Error creating backup: ",
}