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:
Joakim Sørensen 2024-02-22 10:25:38 +01:00 committed by GitHub
parent 52621f9609
commit ec4e6c3a74
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
9 changed files with 467 additions and 158 deletions

View file

@ -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

View file

@ -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

View file

@ -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"])

View file

@ -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"])

View 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',
})
# ---

View file

@ -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"

View file

@ -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()

View file

@ -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"),
[ [

View file

@ -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."""