mirror of
https://github.com/home-assistant/core.git
synced 2025-08-18 03:51:51 +02:00
Add additional options to WS command backup/generate (#130530)
* Add additional options to WS command backup/generate * Improve test * Improve test
This commit is contained in:
@@ -39,7 +39,13 @@ async def async_setup(hass: HomeAssistant, config: ConfigType) -> bool:
|
|||||||
|
|
||||||
async def async_handle_create_service(call: ServiceCall) -> None:
|
async def async_handle_create_service(call: ServiceCall) -> None:
|
||||||
"""Service handler for creating backups."""
|
"""Service handler for creating backups."""
|
||||||
await backup_manager.async_create_backup(on_progress=None)
|
await backup_manager.async_create_backup(
|
||||||
|
addons_included=None,
|
||||||
|
database_included=True,
|
||||||
|
folders_included=None,
|
||||||
|
name=None,
|
||||||
|
on_progress=None,
|
||||||
|
)
|
||||||
if backup_task := backup_manager.backup_task:
|
if backup_task := backup_manager.backup_task:
|
||||||
await backup_task
|
await backup_task
|
||||||
|
|
||||||
|
@@ -26,3 +26,8 @@ EXCLUDE_FROM_BACKUP = [
|
|||||||
"OZW_Log.txt",
|
"OZW_Log.txt",
|
||||||
"tts/*",
|
"tts/*",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
EXCLUDE_DATABASE_FROM_BACKUP = [
|
||||||
|
"home-assistant_v2.db",
|
||||||
|
"home-assistant_v2.db-wal",
|
||||||
|
]
|
||||||
|
@@ -32,7 +32,7 @@ from homeassistant.util import dt as dt_util
|
|||||||
from homeassistant.util.json import json_loads_object
|
from homeassistant.util.json import json_loads_object
|
||||||
|
|
||||||
from .agent import BackupAgent, BackupAgentPlatformProtocol
|
from .agent import BackupAgent, BackupAgentPlatformProtocol
|
||||||
from .const import DOMAIN, EXCLUDE_FROM_BACKUP, LOGGER
|
from .const import DOMAIN, EXCLUDE_DATABASE_FROM_BACKUP, EXCLUDE_FROM_BACKUP, LOGGER
|
||||||
from .models import BackupUploadMetadata, BaseBackup
|
from .models import BackupUploadMetadata, BaseBackup
|
||||||
|
|
||||||
BUF_SIZE = 2**20 * 4 # 4MB
|
BUF_SIZE = 2**20 * 4 # 4MB
|
||||||
@@ -180,10 +180,18 @@ class BaseBackupManager(abc.ABC, Generic[_BackupT]):
|
|||||||
async def async_create_backup(
|
async def async_create_backup(
|
||||||
self,
|
self,
|
||||||
*,
|
*,
|
||||||
|
addons_included: list[str] | None,
|
||||||
|
database_included: bool,
|
||||||
|
folders_included: list[str] | None,
|
||||||
|
name: str | None,
|
||||||
on_progress: Callable[[BackupProgress], None] | None,
|
on_progress: Callable[[BackupProgress], None] | None,
|
||||||
**kwargs: Any,
|
**kwargs: Any,
|
||||||
) -> NewBackup:
|
) -> NewBackup:
|
||||||
"""Generate a backup."""
|
"""Initiate generating a backup.
|
||||||
|
|
||||||
|
:param on_progress: A callback that will be called with the progress of the
|
||||||
|
backup.
|
||||||
|
"""
|
||||||
|
|
||||||
@abc.abstractmethod
|
@abc.abstractmethod
|
||||||
async def async_get_backups(self, **kwargs: Any) -> dict[str, _BackupT]:
|
async def async_get_backups(self, **kwargs: Any) -> dict[str, _BackupT]:
|
||||||
@@ -380,17 +388,29 @@ class BackupManager(BaseBackupManager[Backup]):
|
|||||||
async def async_create_backup(
|
async def async_create_backup(
|
||||||
self,
|
self,
|
||||||
*,
|
*,
|
||||||
|
addons_included: list[str] | None,
|
||||||
|
database_included: bool,
|
||||||
|
folders_included: list[str] | None,
|
||||||
|
name: str | None,
|
||||||
on_progress: Callable[[BackupProgress], None] | None,
|
on_progress: Callable[[BackupProgress], None] | None,
|
||||||
**kwargs: Any,
|
**kwargs: Any,
|
||||||
) -> NewBackup:
|
) -> NewBackup:
|
||||||
"""Generate a backup."""
|
"""Initiate generating a backup."""
|
||||||
if self.backup_task:
|
if self.backup_task:
|
||||||
raise HomeAssistantError("Backup already in progress")
|
raise HomeAssistantError("Backup already in progress")
|
||||||
backup_name = f"Core {HAVERSION}"
|
backup_name = name or f"Core {HAVERSION}"
|
||||||
date_str = dt_util.now().isoformat()
|
date_str = dt_util.now().isoformat()
|
||||||
slug = _generate_slug(date_str, backup_name)
|
slug = _generate_slug(date_str, backup_name)
|
||||||
self.backup_task = self.hass.async_create_task(
|
self.backup_task = self.hass.async_create_task(
|
||||||
self._async_create_backup(backup_name, date_str, slug, on_progress),
|
self._async_create_backup(
|
||||||
|
addons_included=addons_included,
|
||||||
|
backup_name=backup_name,
|
||||||
|
database_included=database_included,
|
||||||
|
date_str=date_str,
|
||||||
|
folders_included=folders_included,
|
||||||
|
on_progress=on_progress,
|
||||||
|
slug=slug,
|
||||||
|
),
|
||||||
name="backup_manager_create_backup",
|
name="backup_manager_create_backup",
|
||||||
eager_start=False, # To ensure the task is not started before we return
|
eager_start=False, # To ensure the task is not started before we return
|
||||||
)
|
)
|
||||||
@@ -398,10 +418,14 @@ class BackupManager(BaseBackupManager[Backup]):
|
|||||||
|
|
||||||
async def _async_create_backup(
|
async def _async_create_backup(
|
||||||
self,
|
self,
|
||||||
|
*,
|
||||||
|
addons_included: list[str] | None,
|
||||||
|
database_included: bool,
|
||||||
backup_name: str,
|
backup_name: str,
|
||||||
date_str: str,
|
date_str: str,
|
||||||
slug: str,
|
folders_included: list[str] | None,
|
||||||
on_progress: Callable[[BackupProgress], None] | None,
|
on_progress: Callable[[BackupProgress], None] | None,
|
||||||
|
slug: str,
|
||||||
) -> Backup:
|
) -> Backup:
|
||||||
"""Generate a backup."""
|
"""Generate a backup."""
|
||||||
success = False
|
success = False
|
||||||
@@ -414,7 +438,10 @@ class BackupManager(BaseBackupManager[Backup]):
|
|||||||
"date": date_str,
|
"date": date_str,
|
||||||
"type": "partial",
|
"type": "partial",
|
||||||
"folders": ["homeassistant"],
|
"folders": ["homeassistant"],
|
||||||
"homeassistant": {"version": HAVERSION},
|
"homeassistant": {
|
||||||
|
"exclude_database": not database_included,
|
||||||
|
"version": HAVERSION,
|
||||||
|
},
|
||||||
"compressed": True,
|
"compressed": True,
|
||||||
}
|
}
|
||||||
tar_file_path = Path(self.backup_dir, f"{backup_data['slug']}.tar")
|
tar_file_path = Path(self.backup_dir, f"{backup_data['slug']}.tar")
|
||||||
@@ -422,6 +449,7 @@ class BackupManager(BaseBackupManager[Backup]):
|
|||||||
self._mkdir_and_generate_backup_contents,
|
self._mkdir_and_generate_backup_contents,
|
||||||
tar_file_path,
|
tar_file_path,
|
||||||
backup_data,
|
backup_data,
|
||||||
|
database_included,
|
||||||
)
|
)
|
||||||
backup = Backup(
|
backup = Backup(
|
||||||
slug=slug,
|
slug=slug,
|
||||||
@@ -445,12 +473,17 @@ class BackupManager(BaseBackupManager[Backup]):
|
|||||||
self,
|
self,
|
||||||
tar_file_path: Path,
|
tar_file_path: Path,
|
||||||
backup_data: dict[str, Any],
|
backup_data: dict[str, Any],
|
||||||
|
database_included: bool,
|
||||||
) -> int:
|
) -> int:
|
||||||
"""Generate backup contents and return the size."""
|
"""Generate backup contents and return the size."""
|
||||||
if not self.backup_dir.exists():
|
if not self.backup_dir.exists():
|
||||||
LOGGER.debug("Creating backup directory")
|
LOGGER.debug("Creating backup directory")
|
||||||
self.backup_dir.mkdir()
|
self.backup_dir.mkdir()
|
||||||
|
|
||||||
|
excludes = EXCLUDE_FROM_BACKUP
|
||||||
|
if not database_included:
|
||||||
|
excludes = excludes + EXCLUDE_DATABASE_FROM_BACKUP
|
||||||
|
|
||||||
outer_secure_tarfile = SecureTarFile(
|
outer_secure_tarfile = SecureTarFile(
|
||||||
tar_file_path, "w", gzip=False, bufsize=BUF_SIZE
|
tar_file_path, "w", gzip=False, bufsize=BUF_SIZE
|
||||||
)
|
)
|
||||||
@@ -467,7 +500,7 @@ class BackupManager(BaseBackupManager[Backup]):
|
|||||||
atomic_contents_add(
|
atomic_contents_add(
|
||||||
tar_file=core_tar,
|
tar_file=core_tar,
|
||||||
origin_path=Path(self.hass.config.path()),
|
origin_path=Path(self.hass.config.path()),
|
||||||
excludes=EXCLUDE_FROM_BACKUP,
|
excludes=excludes,
|
||||||
arcname="data",
|
arcname="data",
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@@ -112,7 +112,15 @@ async def handle_restore(
|
|||||||
|
|
||||||
|
|
||||||
@websocket_api.require_admin
|
@websocket_api.require_admin
|
||||||
@websocket_api.websocket_command({vol.Required("type"): "backup/generate"})
|
@websocket_api.websocket_command(
|
||||||
|
{
|
||||||
|
vol.Required("type"): "backup/generate",
|
||||||
|
vol.Optional("addons_included"): [str],
|
||||||
|
vol.Optional("database_included", default=True): bool,
|
||||||
|
vol.Optional("folders_included"): [str],
|
||||||
|
vol.Optional("name"): str,
|
||||||
|
}
|
||||||
|
)
|
||||||
@websocket_api.async_response
|
@websocket_api.async_response
|
||||||
async def handle_create(
|
async def handle_create(
|
||||||
hass: HomeAssistant,
|
hass: HomeAssistant,
|
||||||
@@ -124,7 +132,13 @@ async def handle_create(
|
|||||||
def on_progress(progress: BackupProgress) -> None:
|
def on_progress(progress: BackupProgress) -> None:
|
||||||
connection.send_message(websocket_api.event_message(msg["id"], progress))
|
connection.send_message(websocket_api.event_message(msg["id"], progress))
|
||||||
|
|
||||||
backup = await hass.data[DATA_MANAGER].async_create_backup(on_progress=on_progress)
|
backup = await hass.data[DATA_MANAGER].async_create_backup(
|
||||||
|
addons_included=msg.get("addons_included"),
|
||||||
|
database_included=msg["database_included"],
|
||||||
|
folders_included=msg.get("folders_included"),
|
||||||
|
name=msg.get("name"),
|
||||||
|
on_progress=on_progress,
|
||||||
|
)
|
||||||
connection.send_result(msg["id"], backup)
|
connection.send_result(msg["id"], backup)
|
||||||
|
|
||||||
|
|
||||||
|
@@ -43,11 +43,12 @@ def mock_backup_generation_fixture(
|
|||||||
Path("test.txt"),
|
Path("test.txt"),
|
||||||
Path(".DS_Store"),
|
Path(".DS_Store"),
|
||||||
Path(".storage"),
|
Path(".storage"),
|
||||||
|
Path("home-assistant_v2.db"),
|
||||||
]
|
]
|
||||||
|
|
||||||
with (
|
with (
|
||||||
patch("pathlib.Path.iterdir", _mock_iterdir),
|
patch("pathlib.Path.iterdir", _mock_iterdir),
|
||||||
patch("pathlib.Path.stat", MagicMock(st_size=123)),
|
patch("pathlib.Path.stat", return_value=MagicMock(st_size=123)),
|
||||||
patch("pathlib.Path.is_file", lambda x: x.name != ".storage"),
|
patch("pathlib.Path.is_file", lambda x: x.name != ".storage"),
|
||||||
patch(
|
patch(
|
||||||
"pathlib.Path.is_dir",
|
"pathlib.Path.is_dir",
|
||||||
|
@@ -403,6 +403,26 @@
|
|||||||
'type': 'event',
|
'type': 'event',
|
||||||
})
|
})
|
||||||
# ---
|
# ---
|
||||||
|
# name: test_generate_without_hassio[params0-expected_extra_call_params0]
|
||||||
|
dict({
|
||||||
|
'id': 1,
|
||||||
|
'result': dict({
|
||||||
|
'slug': 'abc123',
|
||||||
|
}),
|
||||||
|
'success': True,
|
||||||
|
'type': 'result',
|
||||||
|
})
|
||||||
|
# ---
|
||||||
|
# name: test_generate_without_hassio[params1-expected_extra_call_params1]
|
||||||
|
dict({
|
||||||
|
'id': 1,
|
||||||
|
'result': dict({
|
||||||
|
'slug': 'abc123',
|
||||||
|
}),
|
||||||
|
'success': True,
|
||||||
|
'type': 'result',
|
||||||
|
})
|
||||||
|
# ---
|
||||||
# name: test_info[with_hassio]
|
# name: test_info[with_hassio]
|
||||||
dict({
|
dict({
|
||||||
'error': dict({
|
'error': dict({
|
||||||
|
@@ -4,7 +4,7 @@ from __future__ import annotations
|
|||||||
|
|
||||||
import asyncio
|
import asyncio
|
||||||
from typing import Any
|
from typing import Any
|
||||||
from unittest.mock import AsyncMock, MagicMock, Mock, mock_open, patch
|
from unittest.mock import ANY, AsyncMock, MagicMock, Mock, call, mock_open, patch
|
||||||
|
|
||||||
import aiohttp
|
import aiohttp
|
||||||
from multidict import CIMultiDict, CIMultiDictProxy
|
from multidict import CIMultiDict, CIMultiDictProxy
|
||||||
@@ -24,9 +24,20 @@ from .common import TEST_BACKUP, BackupAgentTest
|
|||||||
|
|
||||||
from tests.common import MockPlatform, mock_platform
|
from tests.common import MockPlatform, mock_platform
|
||||||
|
|
||||||
|
_EXPECTED_FILES_WITH_DATABASE = {
|
||||||
|
True: ["test.txt", ".storage", "home-assistant_v2.db"],
|
||||||
|
False: ["test.txt", ".storage"],
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
async def _mock_backup_generation(
|
async def _mock_backup_generation(
|
||||||
manager: BackupManager, mocked_json_bytes: Mock, mocked_tarfile: Mock
|
hass: HomeAssistant,
|
||||||
|
manager: BackupManager,
|
||||||
|
mocked_json_bytes: Mock,
|
||||||
|
mocked_tarfile: Mock,
|
||||||
|
*,
|
||||||
|
database_included: bool = True,
|
||||||
|
name: str | None = "Core 2025.1.0",
|
||||||
) -> None:
|
) -> None:
|
||||||
"""Mock backup generator."""
|
"""Mock backup generator."""
|
||||||
|
|
||||||
@@ -37,7 +48,13 @@ async def _mock_backup_generation(
|
|||||||
progress.append(_progress)
|
progress.append(_progress)
|
||||||
|
|
||||||
assert manager.backup_task is None
|
assert manager.backup_task is None
|
||||||
await manager.async_create_backup(on_progress=on_progress)
|
await manager.async_create_backup(
|
||||||
|
addons_included=[],
|
||||||
|
database_included=database_included,
|
||||||
|
folders_included=[],
|
||||||
|
name=name,
|
||||||
|
on_progress=on_progress,
|
||||||
|
)
|
||||||
assert manager.backup_task is not None
|
assert manager.backup_task is not None
|
||||||
assert progress == []
|
assert progress == []
|
||||||
|
|
||||||
@@ -47,8 +64,26 @@ async def _mock_backup_generation(
|
|||||||
assert mocked_json_bytes.call_count == 1
|
assert mocked_json_bytes.call_count == 1
|
||||||
backup_json_dict = mocked_json_bytes.call_args[0][0]
|
backup_json_dict = mocked_json_bytes.call_args[0][0]
|
||||||
assert isinstance(backup_json_dict, dict)
|
assert isinstance(backup_json_dict, dict)
|
||||||
assert backup_json_dict["homeassistant"] == {"version": "2025.1.0"}
|
assert backup_json_dict == {
|
||||||
|
"compressed": True,
|
||||||
|
"date": ANY,
|
||||||
|
"folders": ["homeassistant"],
|
||||||
|
"homeassistant": {
|
||||||
|
"exclude_database": not database_included,
|
||||||
|
"version": "2025.1.0",
|
||||||
|
},
|
||||||
|
"name": name,
|
||||||
|
"slug": ANY,
|
||||||
|
"type": "partial",
|
||||||
|
}
|
||||||
assert manager.backup_dir.as_posix() in str(mocked_tarfile.call_args_list[0][0][0])
|
assert manager.backup_dir.as_posix() in str(mocked_tarfile.call_args_list[0][0][0])
|
||||||
|
outer_tar = mocked_tarfile.return_value
|
||||||
|
core_tar = outer_tar.create_inner_tar.return_value.__enter__.return_value
|
||||||
|
expected_files = [call(hass.config.path(), arcname="data", recursive=False)] + [
|
||||||
|
call(file, arcname=f"data/{file}", recursive=False)
|
||||||
|
for file in _EXPECTED_FILES_WITH_DATABASE[database_included]
|
||||||
|
]
|
||||||
|
assert core_tar.add.call_args_list == expected_files
|
||||||
|
|
||||||
return backup
|
return backup
|
||||||
|
|
||||||
@@ -158,22 +193,35 @@ async def test_async_create_backup_when_backing_up(hass: HomeAssistant) -> None:
|
|||||||
manager = BackupManager(hass)
|
manager = BackupManager(hass)
|
||||||
manager.backup_task = hass.async_create_task(event.wait())
|
manager.backup_task = hass.async_create_task(event.wait())
|
||||||
with pytest.raises(HomeAssistantError, match="Backup already in progress"):
|
with pytest.raises(HomeAssistantError, match="Backup already in progress"):
|
||||||
await manager.async_create_backup(on_progress=None)
|
await manager.async_create_backup(
|
||||||
|
addons_included=[],
|
||||||
|
database_included=True,
|
||||||
|
folders_included=[],
|
||||||
|
name=None,
|
||||||
|
on_progress=None,
|
||||||
|
)
|
||||||
event.set()
|
event.set()
|
||||||
|
|
||||||
|
|
||||||
@pytest.mark.usefixtures("mock_backup_generation")
|
@pytest.mark.usefixtures("mock_backup_generation")
|
||||||
|
@pytest.mark.parametrize(
|
||||||
|
"params",
|
||||||
|
[{}, {"database_included": True, "name": "abc123"}, {"database_included": False}],
|
||||||
|
)
|
||||||
async def test_async_create_backup(
|
async def test_async_create_backup(
|
||||||
hass: HomeAssistant,
|
hass: HomeAssistant,
|
||||||
caplog: pytest.LogCaptureFixture,
|
caplog: pytest.LogCaptureFixture,
|
||||||
mocked_json_bytes: Mock,
|
mocked_json_bytes: Mock,
|
||||||
mocked_tarfile: Mock,
|
mocked_tarfile: Mock,
|
||||||
|
params: dict,
|
||||||
) -> None:
|
) -> None:
|
||||||
"""Test generate backup."""
|
"""Test generate backup."""
|
||||||
manager = BackupManager(hass)
|
manager = BackupManager(hass)
|
||||||
manager.loaded_backups = True
|
manager.loaded_backups = True
|
||||||
|
|
||||||
await _mock_backup_generation(manager, mocked_json_bytes, mocked_tarfile)
|
await _mock_backup_generation(
|
||||||
|
hass, manager, mocked_json_bytes, mocked_tarfile, **params
|
||||||
|
)
|
||||||
|
|
||||||
assert "Generated new backup with slug " in caplog.text
|
assert "Generated new backup with slug " in caplog.text
|
||||||
assert "Creating backup directory" in caplog.text
|
assert "Creating backup directory" in caplog.text
|
||||||
@@ -280,7 +328,9 @@ async def test_syncing_backup(
|
|||||||
await manager.load_platforms()
|
await manager.load_platforms()
|
||||||
await hass.async_block_till_done()
|
await hass.async_block_till_done()
|
||||||
|
|
||||||
backup = await _mock_backup_generation(manager, mocked_json_bytes, mocked_tarfile)
|
backup = await _mock_backup_generation(
|
||||||
|
hass, manager, mocked_json_bytes, mocked_tarfile
|
||||||
|
)
|
||||||
|
|
||||||
with (
|
with (
|
||||||
patch(
|
patch(
|
||||||
@@ -338,7 +388,9 @@ async def test_syncing_backup_with_exception(
|
|||||||
await manager.load_platforms()
|
await manager.load_platforms()
|
||||||
await hass.async_block_till_done()
|
await hass.async_block_till_done()
|
||||||
|
|
||||||
backup = await _mock_backup_generation(manager, mocked_json_bytes, mocked_tarfile)
|
backup = await _mock_backup_generation(
|
||||||
|
hass, manager, mocked_json_bytes, mocked_tarfile
|
||||||
|
)
|
||||||
|
|
||||||
with (
|
with (
|
||||||
patch(
|
patch(
|
||||||
@@ -391,7 +443,9 @@ async def test_syncing_backup_no_agents(
|
|||||||
await manager.load_platforms()
|
await manager.load_platforms()
|
||||||
await hass.async_block_till_done()
|
await hass.async_block_till_done()
|
||||||
|
|
||||||
backup = await _mock_backup_generation(manager, mocked_json_bytes, mocked_tarfile)
|
backup = await _mock_backup_generation(
|
||||||
|
hass, manager, mocked_json_bytes, mocked_tarfile
|
||||||
|
)
|
||||||
with patch(
|
with patch(
|
||||||
"homeassistant.components.backup.agent.BackupAgent.async_upload_backup"
|
"homeassistant.components.backup.agent.BackupAgent.async_upload_backup"
|
||||||
) as mocked_async_upload_backup:
|
) as mocked_async_upload_backup:
|
||||||
@@ -419,7 +473,7 @@ async def test_exception_plaform_pre(
|
|||||||
)
|
)
|
||||||
|
|
||||||
with pytest.raises(HomeAssistantError):
|
with pytest.raises(HomeAssistantError):
|
||||||
await _mock_backup_generation(manager, mocked_json_bytes, mocked_tarfile)
|
await _mock_backup_generation(hass, manager, mocked_json_bytes, mocked_tarfile)
|
||||||
|
|
||||||
|
|
||||||
async def test_exception_plaform_post(
|
async def test_exception_plaform_post(
|
||||||
@@ -442,7 +496,7 @@ async def test_exception_plaform_post(
|
|||||||
)
|
)
|
||||||
|
|
||||||
with pytest.raises(HomeAssistantError):
|
with pytest.raises(HomeAssistantError):
|
||||||
await _mock_backup_generation(manager, mocked_json_bytes, mocked_tarfile)
|
await _mock_backup_generation(hass, manager, mocked_json_bytes, mocked_tarfile)
|
||||||
|
|
||||||
|
|
||||||
async def test_loading_platforms_when_running_async_pre_backup_actions(
|
async def test_loading_platforms_when_running_async_pre_backup_actions(
|
||||||
|
@@ -1,13 +1,14 @@
|
|||||||
"""Tests for the Backup integration."""
|
"""Tests for the Backup integration."""
|
||||||
|
|
||||||
from pathlib import Path
|
from pathlib import Path
|
||||||
from unittest.mock import AsyncMock, patch
|
from unittest.mock import ANY, AsyncMock, patch
|
||||||
|
|
||||||
from freezegun.api import FrozenDateTimeFactory
|
from freezegun.api import FrozenDateTimeFactory
|
||||||
import pytest
|
import pytest
|
||||||
from syrupy import SnapshotAssertion
|
from syrupy import SnapshotAssertion
|
||||||
|
|
||||||
from homeassistant.components.backup.const import DATA_MANAGER
|
from homeassistant.components.backup.const import DATA_MANAGER
|
||||||
|
from homeassistant.components.backup.manager import NewBackup
|
||||||
from homeassistant.components.backup.models import BaseBackup
|
from homeassistant.components.backup.models import BaseBackup
|
||||||
from homeassistant.core import HomeAssistant
|
from homeassistant.core import HomeAssistant
|
||||||
from homeassistant.exceptions import HomeAssistantError
|
from homeassistant.exceptions import HomeAssistantError
|
||||||
@@ -153,6 +154,60 @@ async def test_generate(
|
|||||||
assert await client.receive_json() == snapshot
|
assert await client.receive_json() == snapshot
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.mark.usefixtures("mock_backup_generation")
|
||||||
|
@pytest.mark.parametrize(
|
||||||
|
("params", "expected_extra_call_params"),
|
||||||
|
[
|
||||||
|
({}, {}),
|
||||||
|
(
|
||||||
|
{
|
||||||
|
"addons_included": ["ssl"],
|
||||||
|
"database_included": False,
|
||||||
|
"folders_included": ["media"],
|
||||||
|
"name": "abc123",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"addons_included": ["ssl"],
|
||||||
|
"database_included": False,
|
||||||
|
"folders_included": ["media"],
|
||||||
|
"name": "abc123",
|
||||||
|
},
|
||||||
|
),
|
||||||
|
],
|
||||||
|
)
|
||||||
|
async def test_generate_without_hassio(
|
||||||
|
hass: HomeAssistant,
|
||||||
|
hass_ws_client: WebSocketGenerator,
|
||||||
|
freezer: FrozenDateTimeFactory,
|
||||||
|
snapshot: SnapshotAssertion,
|
||||||
|
params: dict,
|
||||||
|
expected_extra_call_params: tuple,
|
||||||
|
) -> None:
|
||||||
|
"""Test generating a backup."""
|
||||||
|
await setup_backup_integration(hass, with_hassio=False)
|
||||||
|
|
||||||
|
client = await hass_ws_client(hass)
|
||||||
|
freezer.move_to("2024-11-13 12:01:00+01:00")
|
||||||
|
await hass.async_block_till_done()
|
||||||
|
|
||||||
|
with patch(
|
||||||
|
"homeassistant.components.backup.manager.BackupManager.async_create_backup",
|
||||||
|
return_value=NewBackup("abc123"),
|
||||||
|
) as generate_backup:
|
||||||
|
await client.send_json_auto_id({"type": "backup/generate"} | params)
|
||||||
|
assert await client.receive_json() == snapshot
|
||||||
|
generate_backup.assert_called_once_with(
|
||||||
|
**{
|
||||||
|
"addons_included": None,
|
||||||
|
"database_included": True,
|
||||||
|
"folders_included": None,
|
||||||
|
"name": None,
|
||||||
|
"on_progress": ANY,
|
||||||
|
}
|
||||||
|
| expected_extra_call_params
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
@pytest.mark.parametrize(
|
@pytest.mark.parametrize(
|
||||||
"with_hassio",
|
"with_hassio",
|
||||||
[
|
[
|
||||||
|
Reference in New Issue
Block a user