Move backup/* WS commands to the backup integration (#110651)
* Move backup/* WS commands to the backup integration * Call correct command * Use debug for logging * Remove assertion of hass.data for setup test * parametrize token fixture
This commit is contained in:
parent
52621f9609
commit
ec4e6c3a74
9 changed files with 467 additions and 158 deletions
|
@ -115,6 +115,7 @@ DEFAULT_INTEGRATIONS = {
|
||||||
#
|
#
|
||||||
# Integrations providing core functionality:
|
# Integrations providing core functionality:
|
||||||
"application_credentials",
|
"application_credentials",
|
||||||
|
"backup",
|
||||||
"frontend",
|
"frontend",
|
||||||
"hardware",
|
"hardware",
|
||||||
"logger",
|
"logger",
|
||||||
|
@ -148,10 +149,6 @@ DEFAULT_INTEGRATIONS_SUPERVISOR = {
|
||||||
# These integrations are set up if using the Supervisor
|
# These integrations are set up if using the Supervisor
|
||||||
"hassio",
|
"hassio",
|
||||||
}
|
}
|
||||||
DEFAULT_INTEGRATIONS_NON_SUPERVISOR = {
|
|
||||||
# These integrations are set up if not using the Supervisor
|
|
||||||
"backup",
|
|
||||||
}
|
|
||||||
CRITICAL_INTEGRATIONS = {
|
CRITICAL_INTEGRATIONS = {
|
||||||
# Recovery mode is activated if these integrations fail to set up
|
# Recovery mode is activated if these integrations fail to set up
|
||||||
"frontend",
|
"frontend",
|
||||||
|
@ -541,8 +538,6 @@ def _get_domains(hass: core.HomeAssistant, config: dict[str, Any]) -> set[str]:
|
||||||
# Add domains depending on if the Supervisor is used or not
|
# Add domains depending on if the Supervisor is used or not
|
||||||
if "SUPERVISOR" in os.environ:
|
if "SUPERVISOR" in os.environ:
|
||||||
domains.update(DEFAULT_INTEGRATIONS_SUPERVISOR)
|
domains.update(DEFAULT_INTEGRATIONS_SUPERVISOR)
|
||||||
else:
|
|
||||||
domains.update(DEFAULT_INTEGRATIONS_NON_SUPERVISOR)
|
|
||||||
|
|
||||||
return domains
|
return domains
|
||||||
|
|
||||||
|
|
|
@ -14,15 +14,20 @@ CONFIG_SCHEMA = cv.empty_config_schema(DOMAIN)
|
||||||
|
|
||||||
async def async_setup(hass: HomeAssistant, config: ConfigType) -> bool:
|
async def async_setup(hass: HomeAssistant, config: ConfigType) -> bool:
|
||||||
"""Set up the Backup integration."""
|
"""Set up the Backup integration."""
|
||||||
if is_hassio(hass):
|
backup_manager = BackupManager(hass)
|
||||||
|
hass.data[DOMAIN] = backup_manager
|
||||||
|
|
||||||
|
with_hassio = is_hassio(hass)
|
||||||
|
|
||||||
|
async_register_websocket_handlers(hass, with_hassio)
|
||||||
|
|
||||||
|
if with_hassio:
|
||||||
|
if DOMAIN in config:
|
||||||
LOGGER.error(
|
LOGGER.error(
|
||||||
"The backup integration is not supported on this installation method, "
|
"The backup integration is not supported on this installation method, "
|
||||||
"please remove it from your configuration"
|
"please remove it from your configuration"
|
||||||
)
|
)
|
||||||
return False
|
return True
|
||||||
|
|
||||||
backup_manager = BackupManager(hass)
|
|
||||||
hass.data[DOMAIN] = backup_manager
|
|
||||||
|
|
||||||
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."""
|
||||||
|
@ -30,7 +35,6 @@ async def async_setup(hass: HomeAssistant, config: ConfigType) -> bool:
|
||||||
|
|
||||||
hass.services.async_register(DOMAIN, "create", async_handle_create_service)
|
hass.services.async_register(DOMAIN, "create", async_handle_create_service)
|
||||||
|
|
||||||
async_register_websocket_handlers(hass)
|
|
||||||
async_register_http_views(hass)
|
async_register_http_views(hass)
|
||||||
|
|
||||||
return True
|
return True
|
||||||
|
|
|
@ -6,13 +6,18 @@ import voluptuous as vol
|
||||||
from homeassistant.components import websocket_api
|
from homeassistant.components import websocket_api
|
||||||
from homeassistant.core import HomeAssistant, callback
|
from homeassistant.core import HomeAssistant, callback
|
||||||
|
|
||||||
from .const import DOMAIN
|
from .const import DOMAIN, LOGGER
|
||||||
from .manager import BackupManager
|
from .manager import BackupManager
|
||||||
|
|
||||||
|
|
||||||
@callback
|
@callback
|
||||||
def async_register_websocket_handlers(hass: HomeAssistant) -> None:
|
def async_register_websocket_handlers(hass: HomeAssistant, with_hassio: bool) -> None:
|
||||||
"""Register websocket commands."""
|
"""Register websocket commands."""
|
||||||
|
if with_hassio:
|
||||||
|
websocket_api.async_register_command(hass, handle_backup_end)
|
||||||
|
websocket_api.async_register_command(hass, handle_backup_start)
|
||||||
|
return
|
||||||
|
|
||||||
websocket_api.async_register_command(hass, handle_info)
|
websocket_api.async_register_command(hass, handle_info)
|
||||||
websocket_api.async_register_command(hass, handle_create)
|
websocket_api.async_register_command(hass, handle_create)
|
||||||
websocket_api.async_register_command(hass, handle_remove)
|
websocket_api.async_register_command(hass, handle_remove)
|
||||||
|
@ -69,3 +74,47 @@ async def handle_create(
|
||||||
manager: BackupManager = hass.data[DOMAIN]
|
manager: BackupManager = hass.data[DOMAIN]
|
||||||
backup = await manager.generate_backup()
|
backup = await manager.generate_backup()
|
||||||
connection.send_result(msg["id"], backup)
|
connection.send_result(msg["id"], backup)
|
||||||
|
|
||||||
|
|
||||||
|
@websocket_api.ws_require_user(only_supervisor=True)
|
||||||
|
@websocket_api.websocket_command({vol.Required("type"): "backup/start"})
|
||||||
|
@websocket_api.async_response
|
||||||
|
async def handle_backup_start(
|
||||||
|
hass: HomeAssistant,
|
||||||
|
connection: websocket_api.ActiveConnection,
|
||||||
|
msg: dict[str, Any],
|
||||||
|
) -> None:
|
||||||
|
"""Backup start notification."""
|
||||||
|
manager: BackupManager = hass.data[DOMAIN]
|
||||||
|
manager.backing_up = True
|
||||||
|
LOGGER.debug("Backup start notification")
|
||||||
|
|
||||||
|
try:
|
||||||
|
await manager.pre_backup_actions()
|
||||||
|
except Exception as err: # pylint: disable=broad-except
|
||||||
|
connection.send_error(msg["id"], "pre_backup_actions_failed", str(err))
|
||||||
|
return
|
||||||
|
|
||||||
|
connection.send_result(msg["id"])
|
||||||
|
|
||||||
|
|
||||||
|
@websocket_api.ws_require_user(only_supervisor=True)
|
||||||
|
@websocket_api.websocket_command({vol.Required("type"): "backup/end"})
|
||||||
|
@websocket_api.async_response
|
||||||
|
async def handle_backup_end(
|
||||||
|
hass: HomeAssistant,
|
||||||
|
connection: websocket_api.ActiveConnection,
|
||||||
|
msg: dict[str, Any],
|
||||||
|
) -> None:
|
||||||
|
"""Backup end notification."""
|
||||||
|
manager: BackupManager = hass.data[DOMAIN]
|
||||||
|
manager.backing_up = False
|
||||||
|
LOGGER.debug("Backup end notification")
|
||||||
|
|
||||||
|
try:
|
||||||
|
await manager.post_backup_actions()
|
||||||
|
except Exception as err: # pylint: disable=broad-except
|
||||||
|
connection.send_error(msg["id"], "post_backup_actions_failed", str(err))
|
||||||
|
return
|
||||||
|
|
||||||
|
connection.send_result(msg["id"])
|
||||||
|
|
|
@ -2,7 +2,6 @@
|
||||||
from __future__ import annotations
|
from __future__ import annotations
|
||||||
|
|
||||||
from datetime import datetime as dt
|
from datetime import datetime as dt
|
||||||
import logging
|
|
||||||
from typing import Any, Literal, cast
|
from typing import Any, Literal, cast
|
||||||
|
|
||||||
import voluptuous as vol
|
import voluptuous as vol
|
||||||
|
@ -46,8 +45,6 @@ from .statistics import (
|
||||||
)
|
)
|
||||||
from .util import PERIOD_SCHEMA, get_instance, resolve_period
|
from .util import PERIOD_SCHEMA, get_instance, resolve_period
|
||||||
|
|
||||||
_LOGGER: logging.Logger = logging.getLogger(__package__)
|
|
||||||
|
|
||||||
UNIT_SCHEMA = vol.Schema(
|
UNIT_SCHEMA = vol.Schema(
|
||||||
{
|
{
|
||||||
vol.Optional("data_rate"): vol.In(DataRateConverter.VALID_UNITS),
|
vol.Optional("data_rate"): vol.In(DataRateConverter.VALID_UNITS),
|
||||||
|
@ -73,8 +70,6 @@ UNIT_SCHEMA = vol.Schema(
|
||||||
def async_setup(hass: HomeAssistant) -> None:
|
def async_setup(hass: HomeAssistant) -> None:
|
||||||
"""Set up the recorder websocket API."""
|
"""Set up the recorder websocket API."""
|
||||||
websocket_api.async_register_command(hass, ws_adjust_sum_statistics)
|
websocket_api.async_register_command(hass, ws_adjust_sum_statistics)
|
||||||
websocket_api.async_register_command(hass, ws_backup_end)
|
|
||||||
websocket_api.async_register_command(hass, ws_backup_start)
|
|
||||||
websocket_api.async_register_command(hass, ws_change_statistics_unit)
|
websocket_api.async_register_command(hass, ws_change_statistics_unit)
|
||||||
websocket_api.async_register_command(hass, ws_clear_statistics)
|
websocket_api.async_register_command(hass, ws_clear_statistics)
|
||||||
websocket_api.async_register_command(hass, ws_get_statistic_during_period)
|
websocket_api.async_register_command(hass, ws_get_statistic_during_period)
|
||||||
|
@ -517,38 +512,3 @@ def ws_info(
|
||||||
"thread_running": is_running,
|
"thread_running": is_running,
|
||||||
}
|
}
|
||||||
connection.send_result(msg["id"], recorder_info)
|
connection.send_result(msg["id"], recorder_info)
|
||||||
|
|
||||||
|
|
||||||
@websocket_api.ws_require_user(only_supervisor=True)
|
|
||||||
@websocket_api.websocket_command({vol.Required("type"): "backup/start"})
|
|
||||||
@websocket_api.async_response
|
|
||||||
async def ws_backup_start(
|
|
||||||
hass: HomeAssistant, connection: websocket_api.ActiveConnection, msg: dict[str, Any]
|
|
||||||
) -> None:
|
|
||||||
"""Backup start notification."""
|
|
||||||
|
|
||||||
_LOGGER.info("Backup start notification, locking database for writes")
|
|
||||||
instance = get_instance(hass)
|
|
||||||
try:
|
|
||||||
await instance.lock_database()
|
|
||||||
except TimeoutError as err:
|
|
||||||
connection.send_error(msg["id"], "timeout_error", str(err))
|
|
||||||
return
|
|
||||||
connection.send_result(msg["id"])
|
|
||||||
|
|
||||||
|
|
||||||
@websocket_api.ws_require_user(only_supervisor=True)
|
|
||||||
@websocket_api.websocket_command({vol.Required("type"): "backup/end"})
|
|
||||||
@websocket_api.async_response
|
|
||||||
async def ws_backup_end(
|
|
||||||
hass: HomeAssistant, connection: websocket_api.ActiveConnection, msg: dict[str, Any]
|
|
||||||
) -> None:
|
|
||||||
"""Backup end notification."""
|
|
||||||
|
|
||||||
instance = get_instance(hass)
|
|
||||||
_LOGGER.info("Backup end notification, releasing write lock")
|
|
||||||
if not instance.unlock_database():
|
|
||||||
connection.send_error(
|
|
||||||
msg["id"], "database_unlock_failed", "Failed to unlock database."
|
|
||||||
)
|
|
||||||
connection.send_result(msg["id"])
|
|
||||||
|
|
223
tests/components/backup/snapshots/test_websocket.ambr
Normal file
223
tests/components/backup/snapshots/test_websocket.ambr
Normal file
|
@ -0,0 +1,223 @@
|
||||||
|
# serializer version: 1
|
||||||
|
# name: test_backup_end[with_hassio-hass_access_token]
|
||||||
|
dict({
|
||||||
|
'error': dict({
|
||||||
|
'code': 'only_supervisor',
|
||||||
|
'message': 'Only allowed as Supervisor',
|
||||||
|
}),
|
||||||
|
'id': 1,
|
||||||
|
'success': False,
|
||||||
|
'type': 'result',
|
||||||
|
})
|
||||||
|
# ---
|
||||||
|
# name: test_backup_end[with_hassio-hass_supervisor_access_token]
|
||||||
|
dict({
|
||||||
|
'id': 1,
|
||||||
|
'result': None,
|
||||||
|
'success': True,
|
||||||
|
'type': 'result',
|
||||||
|
})
|
||||||
|
# ---
|
||||||
|
# name: test_backup_end[without_hassio-hass_access_token]
|
||||||
|
dict({
|
||||||
|
'error': dict({
|
||||||
|
'code': 'unknown_command',
|
||||||
|
'message': 'Unknown command.',
|
||||||
|
}),
|
||||||
|
'id': 1,
|
||||||
|
'success': False,
|
||||||
|
'type': 'result',
|
||||||
|
})
|
||||||
|
# ---
|
||||||
|
# name: test_backup_end[without_hassio-hass_supervisor_access_token]
|
||||||
|
dict({
|
||||||
|
'error': dict({
|
||||||
|
'code': 'unknown_command',
|
||||||
|
'message': 'Unknown command.',
|
||||||
|
}),
|
||||||
|
'id': 1,
|
||||||
|
'success': False,
|
||||||
|
'type': 'result',
|
||||||
|
})
|
||||||
|
# ---
|
||||||
|
# name: test_backup_end_excepion[exception0]
|
||||||
|
dict({
|
||||||
|
'error': dict({
|
||||||
|
'code': 'post_backup_actions_failed',
|
||||||
|
'message': '',
|
||||||
|
}),
|
||||||
|
'id': 1,
|
||||||
|
'success': False,
|
||||||
|
'type': 'result',
|
||||||
|
})
|
||||||
|
# ---
|
||||||
|
# name: test_backup_end_excepion[exception1]
|
||||||
|
dict({
|
||||||
|
'error': dict({
|
||||||
|
'code': 'post_backup_actions_failed',
|
||||||
|
'message': 'Boom',
|
||||||
|
}),
|
||||||
|
'id': 1,
|
||||||
|
'success': False,
|
||||||
|
'type': 'result',
|
||||||
|
})
|
||||||
|
# ---
|
||||||
|
# name: test_backup_end_excepion[exception2]
|
||||||
|
dict({
|
||||||
|
'error': dict({
|
||||||
|
'code': 'post_backup_actions_failed',
|
||||||
|
'message': 'Boom',
|
||||||
|
}),
|
||||||
|
'id': 1,
|
||||||
|
'success': False,
|
||||||
|
'type': 'result',
|
||||||
|
})
|
||||||
|
# ---
|
||||||
|
# name: test_backup_start[with_hassio-hass_access_token]
|
||||||
|
dict({
|
||||||
|
'error': dict({
|
||||||
|
'code': 'only_supervisor',
|
||||||
|
'message': 'Only allowed as Supervisor',
|
||||||
|
}),
|
||||||
|
'id': 1,
|
||||||
|
'success': False,
|
||||||
|
'type': 'result',
|
||||||
|
})
|
||||||
|
# ---
|
||||||
|
# name: test_backup_start[with_hassio-hass_supervisor_access_token]
|
||||||
|
dict({
|
||||||
|
'id': 1,
|
||||||
|
'result': None,
|
||||||
|
'success': True,
|
||||||
|
'type': 'result',
|
||||||
|
})
|
||||||
|
# ---
|
||||||
|
# name: test_backup_start[without_hassio-hass_access_token]
|
||||||
|
dict({
|
||||||
|
'error': dict({
|
||||||
|
'code': 'unknown_command',
|
||||||
|
'message': 'Unknown command.',
|
||||||
|
}),
|
||||||
|
'id': 1,
|
||||||
|
'success': False,
|
||||||
|
'type': 'result',
|
||||||
|
})
|
||||||
|
# ---
|
||||||
|
# name: test_backup_start[without_hassio-hass_supervisor_access_token]
|
||||||
|
dict({
|
||||||
|
'error': dict({
|
||||||
|
'code': 'unknown_command',
|
||||||
|
'message': 'Unknown command.',
|
||||||
|
}),
|
||||||
|
'id': 1,
|
||||||
|
'success': False,
|
||||||
|
'type': 'result',
|
||||||
|
})
|
||||||
|
# ---
|
||||||
|
# name: test_backup_start_excepion[exception0]
|
||||||
|
dict({
|
||||||
|
'error': dict({
|
||||||
|
'code': 'pre_backup_actions_failed',
|
||||||
|
'message': '',
|
||||||
|
}),
|
||||||
|
'id': 1,
|
||||||
|
'success': False,
|
||||||
|
'type': 'result',
|
||||||
|
})
|
||||||
|
# ---
|
||||||
|
# name: test_backup_start_excepion[exception1]
|
||||||
|
dict({
|
||||||
|
'error': dict({
|
||||||
|
'code': 'pre_backup_actions_failed',
|
||||||
|
'message': 'Boom',
|
||||||
|
}),
|
||||||
|
'id': 1,
|
||||||
|
'success': False,
|
||||||
|
'type': 'result',
|
||||||
|
})
|
||||||
|
# ---
|
||||||
|
# name: test_backup_start_excepion[exception2]
|
||||||
|
dict({
|
||||||
|
'error': dict({
|
||||||
|
'code': 'pre_backup_actions_failed',
|
||||||
|
'message': 'Boom',
|
||||||
|
}),
|
||||||
|
'id': 1,
|
||||||
|
'success': False,
|
||||||
|
'type': 'result',
|
||||||
|
})
|
||||||
|
# ---
|
||||||
|
# name: test_generate[with_hassio]
|
||||||
|
dict({
|
||||||
|
'error': dict({
|
||||||
|
'code': 'unknown_command',
|
||||||
|
'message': 'Unknown command.',
|
||||||
|
}),
|
||||||
|
'id': 1,
|
||||||
|
'success': False,
|
||||||
|
'type': 'result',
|
||||||
|
})
|
||||||
|
# ---
|
||||||
|
# name: test_generate[without_hassio]
|
||||||
|
dict({
|
||||||
|
'id': 1,
|
||||||
|
'result': dict({
|
||||||
|
'date': '1970-01-01T00:00:00.000Z',
|
||||||
|
'name': 'Test',
|
||||||
|
'path': 'abc123.tar',
|
||||||
|
'size': 0.0,
|
||||||
|
'slug': 'abc123',
|
||||||
|
}),
|
||||||
|
'success': True,
|
||||||
|
'type': 'result',
|
||||||
|
})
|
||||||
|
# ---
|
||||||
|
# name: test_info[with_hassio]
|
||||||
|
dict({
|
||||||
|
'error': dict({
|
||||||
|
'code': 'unknown_command',
|
||||||
|
'message': 'Unknown command.',
|
||||||
|
}),
|
||||||
|
'id': 1,
|
||||||
|
'success': False,
|
||||||
|
'type': 'result',
|
||||||
|
})
|
||||||
|
# ---
|
||||||
|
# name: test_info[without_hassio]
|
||||||
|
dict({
|
||||||
|
'id': 1,
|
||||||
|
'result': dict({
|
||||||
|
'backing_up': False,
|
||||||
|
'backups': list([
|
||||||
|
dict({
|
||||||
|
'date': '1970-01-01T00:00:00.000Z',
|
||||||
|
'name': 'Test',
|
||||||
|
'path': 'abc123.tar',
|
||||||
|
'size': 0.0,
|
||||||
|
'slug': 'abc123',
|
||||||
|
}),
|
||||||
|
]),
|
||||||
|
}),
|
||||||
|
'success': True,
|
||||||
|
'type': 'result',
|
||||||
|
})
|
||||||
|
# ---
|
||||||
|
# name: test_remove[with_hassio]
|
||||||
|
dict({
|
||||||
|
'error': dict({
|
||||||
|
'code': 'unknown_command',
|
||||||
|
'message': 'Unknown command.',
|
||||||
|
}),
|
||||||
|
'id': 1,
|
||||||
|
'success': False,
|
||||||
|
'type': 'result',
|
||||||
|
})
|
||||||
|
# ---
|
||||||
|
# name: test_remove[without_hassio]
|
||||||
|
dict({
|
||||||
|
'id': 1,
|
||||||
|
'result': None,
|
||||||
|
'success': True,
|
||||||
|
'type': 'result',
|
||||||
|
})
|
||||||
|
# ---
|
|
@ -14,7 +14,11 @@ async def test_setup_with_hassio(
|
||||||
caplog: pytest.LogCaptureFixture,
|
caplog: pytest.LogCaptureFixture,
|
||||||
) -> None:
|
) -> None:
|
||||||
"""Test the setup of the integration with hassio enabled."""
|
"""Test the setup of the integration with hassio enabled."""
|
||||||
assert not await setup_backup_integration(hass=hass, with_hassio=True)
|
assert await setup_backup_integration(
|
||||||
|
hass=hass,
|
||||||
|
with_hassio=True,
|
||||||
|
configuration={DOMAIN: {}},
|
||||||
|
)
|
||||||
assert (
|
assert (
|
||||||
"The backup integration is not supported on this installation method, please"
|
"The backup integration is not supported on this installation method, please"
|
||||||
" remove it from your configuration"
|
" remove it from your configuration"
|
||||||
|
|
|
@ -2,20 +2,43 @@
|
||||||
from unittest.mock import patch
|
from unittest.mock import patch
|
||||||
|
|
||||||
import pytest
|
import pytest
|
||||||
|
from syrupy import SnapshotAssertion
|
||||||
|
|
||||||
from homeassistant.core import HomeAssistant
|
from homeassistant.core import HomeAssistant
|
||||||
|
from homeassistant.exceptions import HomeAssistantError
|
||||||
|
|
||||||
from .common import TEST_BACKUP, setup_backup_integration
|
from .common import TEST_BACKUP, setup_backup_integration
|
||||||
|
|
||||||
from tests.typing import WebSocketGenerator
|
from tests.typing import WebSocketGenerator
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.fixture
|
||||||
|
def sync_access_token_proxy(
|
||||||
|
access_token_fixture_name: str,
|
||||||
|
request: pytest.FixtureRequest,
|
||||||
|
) -> str:
|
||||||
|
"""Non-async proxy for the *_access_token fixture.
|
||||||
|
|
||||||
|
Workaround for https://github.com/pytest-dev/pytest-asyncio/issues/112
|
||||||
|
"""
|
||||||
|
return request.getfixturevalue(access_token_fixture_name)
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.mark.parametrize(
|
||||||
|
"with_hassio",
|
||||||
|
(
|
||||||
|
pytest.param(True, id="with_hassio"),
|
||||||
|
pytest.param(False, id="without_hassio"),
|
||||||
|
),
|
||||||
|
)
|
||||||
async def test_info(
|
async def test_info(
|
||||||
hass: HomeAssistant,
|
hass: HomeAssistant,
|
||||||
hass_ws_client: WebSocketGenerator,
|
hass_ws_client: WebSocketGenerator,
|
||||||
|
snapshot: SnapshotAssertion,
|
||||||
|
with_hassio: bool,
|
||||||
) -> None:
|
) -> None:
|
||||||
"""Test getting backup info."""
|
"""Test getting backup info."""
|
||||||
await setup_backup_integration(hass)
|
await setup_backup_integration(hass, with_hassio=with_hassio)
|
||||||
|
|
||||||
client = await hass_ws_client(hass)
|
client = await hass_ws_client(hass)
|
||||||
await hass.async_block_till_done()
|
await hass.async_block_till_done()
|
||||||
|
@ -24,21 +47,25 @@ async def test_info(
|
||||||
"homeassistant.components.backup.websocket.BackupManager.get_backups",
|
"homeassistant.components.backup.websocket.BackupManager.get_backups",
|
||||||
return_value={TEST_BACKUP.slug: TEST_BACKUP},
|
return_value={TEST_BACKUP.slug: TEST_BACKUP},
|
||||||
):
|
):
|
||||||
await client.send_json({"id": 1, "type": "backup/info"})
|
await client.send_json_auto_id({"type": "backup/info"})
|
||||||
msg = await client.receive_json()
|
assert snapshot == await client.receive_json()
|
||||||
|
|
||||||
assert msg["id"] == 1
|
|
||||||
assert msg["success"]
|
|
||||||
assert msg["result"] == {"backing_up": False, "backups": [TEST_BACKUP.as_dict()]}
|
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.mark.parametrize(
|
||||||
|
"with_hassio",
|
||||||
|
(
|
||||||
|
pytest.param(True, id="with_hassio"),
|
||||||
|
pytest.param(False, id="without_hassio"),
|
||||||
|
),
|
||||||
|
)
|
||||||
async def test_remove(
|
async def test_remove(
|
||||||
hass: HomeAssistant,
|
hass: HomeAssistant,
|
||||||
hass_ws_client: WebSocketGenerator,
|
hass_ws_client: WebSocketGenerator,
|
||||||
caplog: pytest.LogCaptureFixture,
|
snapshot: SnapshotAssertion,
|
||||||
|
with_hassio: bool,
|
||||||
) -> None:
|
) -> None:
|
||||||
"""Test removing a backup file."""
|
"""Test removing a backup file."""
|
||||||
await setup_backup_integration(hass)
|
await setup_backup_integration(hass, with_hassio=with_hassio)
|
||||||
|
|
||||||
client = await hass_ws_client(hass)
|
client = await hass_ws_client(hass)
|
||||||
await hass.async_block_till_done()
|
await hass.async_block_till_done()
|
||||||
|
@ -46,19 +73,25 @@ async def test_remove(
|
||||||
with patch(
|
with patch(
|
||||||
"homeassistant.components.backup.websocket.BackupManager.remove_backup",
|
"homeassistant.components.backup.websocket.BackupManager.remove_backup",
|
||||||
):
|
):
|
||||||
await client.send_json({"id": 1, "type": "backup/remove", "slug": "abc123"})
|
await client.send_json_auto_id({"type": "backup/remove", "slug": "abc123"})
|
||||||
msg = await client.receive_json()
|
assert snapshot == await client.receive_json()
|
||||||
|
|
||||||
assert msg["id"] == 1
|
|
||||||
assert msg["success"]
|
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.mark.parametrize(
|
||||||
|
"with_hassio",
|
||||||
|
(
|
||||||
|
pytest.param(True, id="with_hassio"),
|
||||||
|
pytest.param(False, id="without_hassio"),
|
||||||
|
),
|
||||||
|
)
|
||||||
async def test_generate(
|
async def test_generate(
|
||||||
hass: HomeAssistant,
|
hass: HomeAssistant,
|
||||||
hass_ws_client: WebSocketGenerator,
|
hass_ws_client: WebSocketGenerator,
|
||||||
|
snapshot: SnapshotAssertion,
|
||||||
|
with_hassio: bool,
|
||||||
) -> None:
|
) -> None:
|
||||||
"""Test removing a backup file."""
|
"""Test generating a backup."""
|
||||||
await setup_backup_integration(hass)
|
await setup_backup_integration(hass, with_hassio=with_hassio)
|
||||||
|
|
||||||
client = await hass_ws_client(hass)
|
client = await hass_ws_client(hass)
|
||||||
await hass.async_block_till_done()
|
await hass.async_block_till_done()
|
||||||
|
@ -67,9 +100,130 @@ async def test_generate(
|
||||||
"homeassistant.components.backup.websocket.BackupManager.generate_backup",
|
"homeassistant.components.backup.websocket.BackupManager.generate_backup",
|
||||||
return_value=TEST_BACKUP,
|
return_value=TEST_BACKUP,
|
||||||
):
|
):
|
||||||
await client.send_json({"id": 1, "type": "backup/generate"})
|
await client.send_json_auto_id({"type": "backup/generate"})
|
||||||
msg = await client.receive_json()
|
assert snapshot == await client.receive_json()
|
||||||
|
|
||||||
assert msg["id"] == 1
|
|
||||||
assert msg["success"]
|
@pytest.mark.parametrize(
|
||||||
assert msg["result"] == TEST_BACKUP.as_dict()
|
"access_token_fixture_name",
|
||||||
|
["hass_access_token", "hass_supervisor_access_token"],
|
||||||
|
)
|
||||||
|
@pytest.mark.parametrize(
|
||||||
|
("with_hassio"),
|
||||||
|
(
|
||||||
|
pytest.param(True, id="with_hassio"),
|
||||||
|
pytest.param(False, id="without_hassio"),
|
||||||
|
),
|
||||||
|
)
|
||||||
|
async def test_backup_end(
|
||||||
|
hass: HomeAssistant,
|
||||||
|
hass_ws_client: WebSocketGenerator,
|
||||||
|
snapshot: SnapshotAssertion,
|
||||||
|
request: pytest.FixtureRequest,
|
||||||
|
sync_access_token_proxy: str,
|
||||||
|
*,
|
||||||
|
access_token_fixture_name: str,
|
||||||
|
with_hassio: bool,
|
||||||
|
) -> None:
|
||||||
|
"""Test handling of post backup actions from a WS command."""
|
||||||
|
await setup_backup_integration(hass, with_hassio=with_hassio)
|
||||||
|
|
||||||
|
client = await hass_ws_client(hass, sync_access_token_proxy)
|
||||||
|
await hass.async_block_till_done()
|
||||||
|
|
||||||
|
with patch(
|
||||||
|
"homeassistant.components.backup.websocket.BackupManager.post_backup_actions",
|
||||||
|
):
|
||||||
|
await client.send_json_auto_id({"type": "backup/end"})
|
||||||
|
assert snapshot == await client.receive_json()
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.mark.parametrize(
|
||||||
|
"access_token_fixture_name",
|
||||||
|
["hass_access_token", "hass_supervisor_access_token"],
|
||||||
|
)
|
||||||
|
@pytest.mark.parametrize(
|
||||||
|
("with_hassio"),
|
||||||
|
(
|
||||||
|
pytest.param(True, id="with_hassio"),
|
||||||
|
pytest.param(False, id="without_hassio"),
|
||||||
|
),
|
||||||
|
)
|
||||||
|
async def test_backup_start(
|
||||||
|
hass: HomeAssistant,
|
||||||
|
hass_ws_client: WebSocketGenerator,
|
||||||
|
snapshot: SnapshotAssertion,
|
||||||
|
sync_access_token_proxy: str,
|
||||||
|
*,
|
||||||
|
access_token_fixture_name: str,
|
||||||
|
with_hassio: bool,
|
||||||
|
) -> None:
|
||||||
|
"""Test handling of pre backup actions from a WS command."""
|
||||||
|
await setup_backup_integration(hass, with_hassio=with_hassio)
|
||||||
|
|
||||||
|
client = await hass_ws_client(hass, sync_access_token_proxy)
|
||||||
|
await hass.async_block_till_done()
|
||||||
|
|
||||||
|
with patch(
|
||||||
|
"homeassistant.components.backup.websocket.BackupManager.pre_backup_actions",
|
||||||
|
):
|
||||||
|
await client.send_json_auto_id({"type": "backup/start"})
|
||||||
|
assert snapshot == await client.receive_json()
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.mark.parametrize(
|
||||||
|
"exception",
|
||||||
|
(
|
||||||
|
TimeoutError(),
|
||||||
|
HomeAssistantError("Boom"),
|
||||||
|
Exception("Boom"),
|
||||||
|
),
|
||||||
|
)
|
||||||
|
async def test_backup_end_excepion(
|
||||||
|
hass: HomeAssistant,
|
||||||
|
hass_ws_client: WebSocketGenerator,
|
||||||
|
snapshot: SnapshotAssertion,
|
||||||
|
hass_supervisor_access_token: str,
|
||||||
|
exception: Exception,
|
||||||
|
) -> None:
|
||||||
|
"""Test exception handling while running post backup actions from a WS command."""
|
||||||
|
await setup_backup_integration(hass, with_hassio=True)
|
||||||
|
|
||||||
|
client = await hass_ws_client(hass, hass_supervisor_access_token)
|
||||||
|
await hass.async_block_till_done()
|
||||||
|
|
||||||
|
with patch(
|
||||||
|
"homeassistant.components.backup.websocket.BackupManager.post_backup_actions",
|
||||||
|
side_effect=exception,
|
||||||
|
):
|
||||||
|
await client.send_json_auto_id({"type": "backup/end"})
|
||||||
|
assert snapshot == await client.receive_json()
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.mark.parametrize(
|
||||||
|
"exception",
|
||||||
|
(
|
||||||
|
TimeoutError(),
|
||||||
|
HomeAssistantError("Boom"),
|
||||||
|
Exception("Boom"),
|
||||||
|
),
|
||||||
|
)
|
||||||
|
async def test_backup_start_excepion(
|
||||||
|
hass: HomeAssistant,
|
||||||
|
hass_ws_client: WebSocketGenerator,
|
||||||
|
snapshot: SnapshotAssertion,
|
||||||
|
hass_supervisor_access_token: str,
|
||||||
|
exception: Exception,
|
||||||
|
) -> None:
|
||||||
|
"""Test exception handling while running pre backup actions from a WS command."""
|
||||||
|
await setup_backup_integration(hass, with_hassio=True)
|
||||||
|
|
||||||
|
client = await hass_ws_client(hass, hass_supervisor_access_token)
|
||||||
|
await hass.async_block_till_done()
|
||||||
|
|
||||||
|
with patch(
|
||||||
|
"homeassistant.components.backup.websocket.BackupManager.pre_backup_actions",
|
||||||
|
side_effect=exception,
|
||||||
|
):
|
||||||
|
await client.send_json_auto_id({"type": "backup/start"})
|
||||||
|
assert snapshot == await client.receive_json()
|
||||||
|
|
|
@ -2227,77 +2227,6 @@ async def test_backup_start_no_recorder(
|
||||||
assert response["error"]["code"] == "unknown_command"
|
assert response["error"]["code"] == "unknown_command"
|
||||||
|
|
||||||
|
|
||||||
async def test_backup_start_timeout(
|
|
||||||
recorder_mock: Recorder,
|
|
||||||
hass: HomeAssistant,
|
|
||||||
hass_ws_client: WebSocketGenerator,
|
|
||||||
hass_supervisor_access_token: str,
|
|
||||||
recorder_db_url: str,
|
|
||||||
) -> None:
|
|
||||||
"""Test getting backup start when recorder is not present."""
|
|
||||||
if recorder_db_url.startswith(("mysql://", "postgresql://")):
|
|
||||||
# This test is specific for SQLite: Locking is not implemented for other engines
|
|
||||||
return
|
|
||||||
|
|
||||||
client = await hass_ws_client(hass, hass_supervisor_access_token)
|
|
||||||
|
|
||||||
# Ensure there are no queued events
|
|
||||||
await async_wait_recording_done(hass)
|
|
||||||
|
|
||||||
with patch.object(recorder.core, "DB_LOCK_TIMEOUT", 0):
|
|
||||||
try:
|
|
||||||
await client.send_json_auto_id({"type": "backup/start"})
|
|
||||||
response = await client.receive_json()
|
|
||||||
assert not response["success"]
|
|
||||||
assert response["error"]["code"] == "timeout_error"
|
|
||||||
finally:
|
|
||||||
await client.send_json_auto_id({"type": "backup/end"})
|
|
||||||
|
|
||||||
|
|
||||||
async def test_backup_end(
|
|
||||||
recorder_mock: Recorder,
|
|
||||||
hass: HomeAssistant,
|
|
||||||
hass_ws_client: WebSocketGenerator,
|
|
||||||
hass_supervisor_access_token: str,
|
|
||||||
) -> None:
|
|
||||||
"""Test backup start."""
|
|
||||||
client = await hass_ws_client(hass, hass_supervisor_access_token)
|
|
||||||
|
|
||||||
# Ensure there are no queued events
|
|
||||||
await async_wait_recording_done(hass)
|
|
||||||
|
|
||||||
await client.send_json_auto_id({"type": "backup/start"})
|
|
||||||
response = await client.receive_json()
|
|
||||||
assert response["success"]
|
|
||||||
|
|
||||||
await client.send_json_auto_id({"type": "backup/end"})
|
|
||||||
response = await client.receive_json()
|
|
||||||
assert response["success"]
|
|
||||||
|
|
||||||
|
|
||||||
async def test_backup_end_without_start(
|
|
||||||
recorder_mock: Recorder,
|
|
||||||
hass: HomeAssistant,
|
|
||||||
hass_ws_client: WebSocketGenerator,
|
|
||||||
hass_supervisor_access_token: str,
|
|
||||||
recorder_db_url: str,
|
|
||||||
) -> None:
|
|
||||||
"""Test backup start."""
|
|
||||||
if recorder_db_url.startswith(("mysql://", "postgresql://")):
|
|
||||||
# This test is specific for SQLite: Locking is not implemented for other engines
|
|
||||||
return
|
|
||||||
|
|
||||||
client = await hass_ws_client(hass, hass_supervisor_access_token)
|
|
||||||
|
|
||||||
# Ensure there are no queued events
|
|
||||||
await async_wait_recording_done(hass)
|
|
||||||
|
|
||||||
await client.send_json_auto_id({"type": "backup/end"})
|
|
||||||
response = await client.receive_json()
|
|
||||||
assert not response["success"]
|
|
||||||
assert response["error"]["code"] == "database_unlock_failed"
|
|
||||||
|
|
||||||
|
|
||||||
@pytest.mark.parametrize(
|
@pytest.mark.parametrize(
|
||||||
("units", "attributes", "unit", "unit_class"),
|
("units", "attributes", "unit", "unit_class"),
|
||||||
[
|
[
|
||||||
|
|
|
@ -95,15 +95,6 @@ async def test_load_hassio(hass: HomeAssistant) -> None:
|
||||||
assert "hassio" in bootstrap._get_domains(hass, {})
|
assert "hassio" in bootstrap._get_domains(hass, {})
|
||||||
|
|
||||||
|
|
||||||
async def test_load_backup(hass: HomeAssistant) -> None:
|
|
||||||
"""Test that we load the backup integration when not using Supervisor."""
|
|
||||||
with patch.dict(os.environ, {}, clear=True):
|
|
||||||
assert "backup" in bootstrap._get_domains(hass, {})
|
|
||||||
|
|
||||||
with patch.dict(os.environ, {"SUPERVISOR": "1"}):
|
|
||||||
assert "backup" not in bootstrap._get_domains(hass, {})
|
|
||||||
|
|
||||||
|
|
||||||
@pytest.mark.parametrize("load_registries", [False])
|
@pytest.mark.parametrize("load_registries", [False])
|
||||||
async def test_empty_setup(hass: HomeAssistant) -> None:
|
async def test_empty_setup(hass: HomeAssistant) -> None:
|
||||||
"""Test an empty set up loads the core."""
|
"""Test an empty set up loads the core."""
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue