diff --git a/tests/components/backup/common.py b/tests/components/backup/common.py index afdb5e47a2e8c..b4ebfd70fcdd0 100644 --- a/tests/components/backup/common.py +++ b/tests/components/backup/common.py @@ -18,7 +18,6 @@ ) from homeassistant.components.backup.const import DATA_MANAGER from homeassistant.core import HomeAssistant -from homeassistant.helpers.typing import ConfigType from homeassistant.setup import async_setup_component from tests.common import MockPlatform, mock_platform @@ -64,87 +63,37 @@ async def aiter_from_iter(iterable: Iterable) -> AsyncIterator: yield i -class BackupAgentTest(BackupAgent): - """Test backup agent.""" - - domain = "test" - - def __init__(self, name: str, backups: list[AgentBackup] | None = None) -> None: - """Initialize the backup agent.""" - self.name = name - self.unique_id = name - if backups is None: - backups = [ - AgentBackup( - addons=[AddonInfo(name="Test", slug="test", version="1.0.0")], - backup_id="abc123", - database_included=True, - date="1970-01-01T00:00:00Z", - extra_metadata={}, - folders=[Folder.MEDIA, Folder.SHARE], - homeassistant_included=True, - homeassistant_version="2024.12.0", - name="Test", - protected=False, - size=13, - ) - ] - - self._backup_data: bytearray | None = None - self._backups = {backup.backup_id: backup for backup in backups} - - async def async_download_backup( - self, - backup_id: str, - **kwargs: Any, - ) -> AsyncIterator[bytes]: - """Download a backup file.""" - return AsyncMock(spec_set=["__aiter__"]) +def mock_backup_agent(name: str, backups: list[AgentBackup] | None = None) -> Mock: + """Create a mock backup agent.""" + + async def download_backup(backup_id: str, **kwargs: Any) -> AsyncIterator[bytes]: + """Mock download.""" + if not await get_backup(backup_id): + raise BackupNotFound + return aiter_from_iter((backups_data.get(backup_id, b"backup data"),)) + + async def get_backup(backup_id: str, **kwargs: Any) -> AgentBackup | None: + """Get a backup.""" + return next((b for b in backups if b.backup_id == backup_id), None) - async def async_upload_backup( - self, + async def upload_backup( *, open_stream: Callable[[], Coroutine[Any, Any, AsyncIterator[bytes]]], backup: AgentBackup, **kwargs: Any, ) -> None: """Upload a backup.""" - self._backups[backup.backup_id] = backup + backups.append(backup) backup_stream = await open_stream() - self._backup_data = bytearray() + backup_data = bytearray() async for chunk in backup_stream: - self._backup_data += chunk - - async def async_list_backups(self, **kwargs: Any) -> list[AgentBackup]: - """List backups.""" - return list(self._backups.values()) - - async def async_get_backup( - self, - backup_id: str, - **kwargs: Any, - ) -> AgentBackup | None: - """Return a backup.""" - return self._backups.get(backup_id) - - async def async_delete_backup( - self, - backup_id: str, - **kwargs: Any, - ) -> None: - """Delete a backup file.""" - - -def mock_backup_agent(name: str, backups: list[AgentBackup] | None = None) -> Mock: - """Create a mock backup agent.""" - - async def get_backup(backup_id: str, **kwargs: Any) -> AgentBackup | None: - """Get a backup.""" - return next((b for b in backups if b.backup_id == backup_id), None) + backup_data += chunk + backups_data[backup.backup_id] = backup_data backups = backups or [] + backups_data: dict[str, bytes] = {} mock_agent = Mock(spec=BackupAgent) - mock_agent.domain = "test" + mock_agent.domain = TEST_DOMAIN mock_agent.name = name mock_agent.unique_id = name type(mock_agent).agent_id = BackupAgent.agent_id @@ -152,7 +101,7 @@ async def get_backup(backup_id: str, **kwargs: Any) -> AgentBackup | None: spec_set=[BackupAgent.async_delete_backup] ) mock_agent.async_download_backup = AsyncMock( - side_effect=BackupNotFound, spec_set=[BackupAgent.async_download_backup] + side_effect=download_backup, spec_set=[BackupAgent.async_download_backup] ) mock_agent.async_get_backup = AsyncMock( side_effect=get_backup, spec_set=[BackupAgent.async_get_backup] @@ -161,7 +110,8 @@ async def get_backup(backup_id: str, **kwargs: Any) -> AgentBackup | None: return_value=backups, spec_set=[BackupAgent.async_list_backups] ) mock_agent.async_upload_backup = AsyncMock( - spec_set=[BackupAgent.async_upload_backup] + side_effect=upload_backup, + spec_set=[BackupAgent.async_upload_backup], ) return mock_agent @@ -169,12 +119,12 @@ async def get_backup(backup_id: str, **kwargs: Any) -> AgentBackup | None: async def setup_backup_integration( hass: HomeAssistant, with_hassio: bool = False, - configuration: ConfigType | None = None, *, backups: dict[str, list[AgentBackup]] | None = None, remote_agents: list[str] | None = None, -) -> bool: +) -> dict[str, Mock]: """Set up the Backup integration.""" + backups = backups or {} with ( patch("homeassistant.components.backup.is_hassio", return_value=with_hassio), patch( @@ -182,36 +132,34 @@ async def setup_backup_integration( ), ): remote_agents = remote_agents or [] + remote_agents_dict = {} + for agent in remote_agents: + if not agent.startswith(f"{TEST_DOMAIN}."): + raise ValueError(f"Invalid agent_id: {agent}") + name = agent.partition(".")[2] + remote_agents_dict[agent] = mock_backup_agent(name, backups.get(agent)) platform = Mock( async_get_backup_agents=AsyncMock( - return_value=[BackupAgentTest(agent, []) for agent in remote_agents] + return_value=list(remote_agents_dict.values()) ), spec_set=BackupAgentPlatformProtocol, ) mock_platform(hass, f"{TEST_DOMAIN}.backup", platform or MockPlatform()) assert await async_setup_component(hass, TEST_DOMAIN, {}) - - result = await async_setup_component(hass, DOMAIN, configuration or {}) + assert await async_setup_component(hass, DOMAIN, {}) await hass.async_block_till_done() - if not backups: - return result - for agent_id, agent_backups in backups.items(): - if with_hassio and agent_id == LOCAL_AGENT_ID: - continue - agent = hass.data[DATA_MANAGER].backup_agents[agent_id] + if LOCAL_AGENT_ID not in backups or with_hassio: + return remote_agents_dict - async def open_stream() -> AsyncIterator[bytes]: - """Open a stream.""" - return aiter_from_iter((b"backup data",)) + agent = hass.data[DATA_MANAGER].backup_agents[LOCAL_AGENT_ID] - for backup in agent_backups: - await agent.async_upload_backup(open_stream=open_stream, backup=backup) - if agent_id == LOCAL_AGENT_ID: - agent._loaded_backups = True + for backup in backups[LOCAL_AGENT_ID]: + await agent.async_upload_backup(open_stream=None, backup=backup) + agent._loaded_backups = True - return result + return remote_agents_dict async def setup_backup_platform( diff --git a/tests/components/backup/snapshots/test_websocket.ambr b/tests/components/backup/snapshots/test_websocket.ambr index 2f063262f346c..9236a0cbe0f9e 100644 --- a/tests/components/backup/snapshots/test_websocket.ambr +++ b/tests/components/backup/snapshots/test_websocket.ambr @@ -3482,13 +3482,15 @@ 'agents': dict({ 'domain.test': dict({ 'protected': False, - 'size': 13, + 'size': 0, }), }), 'backup_id': 'abc123', 'database_included': True, - 'date': '1970-01-01T00:00:00Z', + 'date': '1970-01-01T00:00:00.000Z', 'extra_metadata': dict({ + 'instance_id': 'our_uuid', + 'with_automatic_settings': True, }), 'failed_agent_ids': list([ ]), @@ -3499,7 +3501,7 @@ 'homeassistant_included': True, 'homeassistant_version': '2024.12.0', 'name': 'Test', - 'with_automatic_settings': None, + 'with_automatic_settings': True, }), ]), 'last_attempted_automatic_backup': None, @@ -3543,13 +3545,15 @@ 'agents': dict({ 'domain.test': dict({ 'protected': False, - 'size': 13, + 'size': 0, }), }), 'backup_id': 'abc123', 'database_included': True, - 'date': '1970-01-01T00:00:00Z', + 'date': '1970-01-01T00:00:00.000Z', 'extra_metadata': dict({ + 'instance_id': 'our_uuid', + 'with_automatic_settings': True, }), 'failed_agent_ids': list([ 'test.remote', @@ -3561,7 +3565,7 @@ 'homeassistant_included': True, 'homeassistant_version': '2024.12.0', 'name': 'Test', - 'with_automatic_settings': None, + 'with_automatic_settings': True, }), ]), 'last_attempted_automatic_backup': None, @@ -3604,13 +3608,15 @@ 'agents': dict({ 'domain.test': dict({ 'protected': False, - 'size': 13, + 'size': 0, }), }), 'backup_id': 'abc123', 'database_included': True, - 'date': '1970-01-01T00:00:00Z', + 'date': '1970-01-01T00:00:00.000Z', 'extra_metadata': dict({ + 'instance_id': 'our_uuid', + 'with_automatic_settings': True, }), 'failed_agent_ids': list([ ]), @@ -3621,7 +3627,7 @@ 'homeassistant_included': True, 'homeassistant_version': '2024.12.0', 'name': 'Test', - 'with_automatic_settings': None, + 'with_automatic_settings': True, }), ]), 'last_attempted_automatic_backup': None, @@ -3664,13 +3670,15 @@ 'agents': dict({ 'domain.test': dict({ 'protected': False, - 'size': 13, + 'size': 0, }), }), 'backup_id': 'abc123', 'database_included': True, - 'date': '1970-01-01T00:00:00Z', + 'date': '1970-01-01T00:00:00.000Z', 'extra_metadata': dict({ + 'instance_id': 'our_uuid', + 'with_automatic_settings': True, }), 'failed_agent_ids': list([ ]), @@ -3681,7 +3689,7 @@ 'homeassistant_included': True, 'homeassistant_version': '2024.12.0', 'name': 'Test', - 'with_automatic_settings': None, + 'with_automatic_settings': True, }), ]), 'last_attempted_automatic_backup': None, @@ -3725,13 +3733,15 @@ 'agents': dict({ 'domain.test': dict({ 'protected': False, - 'size': 13, + 'size': 0, }), }), 'backup_id': 'abc123', 'database_included': True, - 'date': '1970-01-01T00:00:00Z', + 'date': '1970-01-01T00:00:00.000Z', 'extra_metadata': dict({ + 'instance_id': 'our_uuid', + 'with_automatic_settings': True, }), 'failed_agent_ids': list([ ]), @@ -3742,7 +3752,7 @@ 'homeassistant_included': True, 'homeassistant_version': '2024.12.0', 'name': 'Test', - 'with_automatic_settings': None, + 'with_automatic_settings': True, }), ]), 'last_attempted_automatic_backup': None, @@ -3786,13 +3796,15 @@ 'agents': dict({ 'domain.test': dict({ 'protected': False, - 'size': 13, + 'size': 0, }), }), 'backup_id': 'abc123', 'database_included': True, - 'date': '1970-01-01T00:00:00Z', + 'date': '1970-01-01T00:00:00.000Z', 'extra_metadata': dict({ + 'instance_id': 'our_uuid', + 'with_automatic_settings': True, }), 'failed_agent_ids': list([ 'test.remote', @@ -3804,7 +3816,7 @@ 'homeassistant_included': True, 'homeassistant_version': '2024.12.0', 'name': 'Test', - 'with_automatic_settings': None, + 'with_automatic_settings': True, }), ]), 'last_attempted_automatic_backup': None, diff --git a/tests/components/backup/test_http.py b/tests/components/backup/test_http.py index 9ebf3e8bd40ab..a2f32d93fc325 100644 --- a/tests/components/backup/test_http.py +++ b/tests/components/backup/test_http.py @@ -23,7 +23,6 @@ from .common import ( TEST_BACKUP_ABC123, - BackupAgentTest, aiter_from_iter, mock_backup_agent, setup_backup_integration, @@ -65,19 +64,16 @@ async def test_downloading_remote_backup( hass_client: ClientSessionGenerator, ) -> None: """Test downloading a remote backup.""" + await setup_backup_integration( - hass, backups={"test.test": [TEST_BACKUP_ABC123]}, remote_agents=["test"] + hass, backups={"test.test": [TEST_BACKUP_ABC123]}, remote_agents=["test.test"] ) client = await hass_client() - with ( - patch.object(BackupAgentTest, "async_download_backup") as download_mock, - ): - download_mock.return_value.__aiter__.return_value = iter((b"backup data",)) - resp = await client.get("/api/backup/download/abc123?agent_id=test.test") - assert resp.status == 200 - assert await resp.content.read() == b"backup data" + resp = await client.get("/api/backup/download/abc123?agent_id=test.test") + assert resp.status == 200 + assert await resp.content.read() == b"backup data" async def test_downloading_local_encrypted_backup_file_not_found( diff --git a/tests/components/backup/test_init.py b/tests/components/backup/test_init.py index 925e2cb9b7acb..8a0cc2b97c081 100644 --- a/tests/components/backup/test_init.py +++ b/tests/components/backup/test_init.py @@ -20,11 +20,7 @@ async def test_setup_with_hassio( caplog: pytest.LogCaptureFixture, ) -> None: """Test the setup of the integration with hassio enabled.""" - assert await setup_backup_integration( - hass=hass, - with_hassio=True, - configuration={DOMAIN: {}}, - ) + await setup_backup_integration(hass=hass, with_hassio=True) manager = hass.data[DATA_MANAGER] assert not manager.backup_agents @@ -59,6 +55,7 @@ async def test_create_service( ) +@pytest.mark.usefixtures("supervisor_client") async def test_create_service_with_hassio(hass: HomeAssistant) -> None: """Test action backup.create does not exist with hassio.""" await setup_backup_integration(hass, with_hassio=True) diff --git a/tests/components/backup/test_manager.py b/tests/components/backup/test_manager.py index bdcb9f068b6bb..b2c01774531b7 100644 --- a/tests/components/backup/test_manager.py +++ b/tests/components/backup/test_manager.py @@ -3,7 +3,7 @@ from __future__ import annotations import asyncio -from collections.abc import Generator +from collections.abc import Callable, Generator from dataclasses import replace from io import StringIO import json @@ -58,7 +58,7 @@ TEST_BACKUP_DEF456, TEST_BACKUP_PATH_ABC123, TEST_BACKUP_PATH_DEF456, - BackupAgentTest, + mock_backup_agent, setup_backup_platform, ) @@ -524,7 +524,7 @@ async def test_initiate_backup( ) -> None: """Test generate backup.""" local_agent = local_backup_platform.CoreLocalBackupAgent(hass) - remote_agent = BackupAgentTest("remote", backups=[]) + remote_agent = mock_backup_agent("remote") with patch( "homeassistant.components.backup.backup.async_get_backup_agents" @@ -771,7 +771,7 @@ async def test_initiate_backup_with_agent_error( "with_automatic_settings": True, }, ] - remote_agent = BackupAgentTest("remote", backups=[backup_1, backup_2, backup_3]) + remote_agent = mock_backup_agent("remote", backups=[backup_1, backup_2, backup_3]) with patch( "homeassistant.components.backup.backup.async_get_backup_agents" @@ -1120,7 +1120,7 @@ async def test_create_backup_failure_raises_issue( issues_after_create_backup: dict[tuple[str, str], dict[str, Any]], ) -> None: """Test backup issue is cleared after backup is created.""" - remote_agent = BackupAgentTest("remote", backups=[]) + remote_agent = mock_backup_agent("remote") await async_setup_component(hass, DOMAIN, {}) await hass.async_block_till_done() @@ -1180,7 +1180,7 @@ async def test_initiate_backup_non_agent_upload_error( """Test an unknown or writer upload error during backup generation.""" agent_ids = [LOCAL_AGENT_ID, "test.remote"] local_agent = local_backup_platform.CoreLocalBackupAgent(hass) - remote_agent = BackupAgentTest("remote", backups=[]) + remote_agent = mock_backup_agent("remote") with patch( "homeassistant.components.backup.backup.async_get_backup_agents" @@ -1298,7 +1298,7 @@ async def test_initiate_backup_with_task_error( create_backup.return_value = (NewBackup(backup_job_id="abc123"), backup_task) agent_ids = [LOCAL_AGENT_ID, "test.remote"] local_agent = local_backup_platform.CoreLocalBackupAgent(hass) - remote_agent = BackupAgentTest("remote", backups=[]) + remote_agent = mock_backup_agent("remote") with patch( "homeassistant.components.backup.backup.async_get_backup_agents" @@ -1409,7 +1409,8 @@ async def test_initiate_backup_file_error( """Test file error during generate backup.""" agent_ids = ["test.remote"] local_agent = local_backup_platform.CoreLocalBackupAgent(hass) - remote_agent = BackupAgentTest("remote", backups=[]) + + remote_agent = mock_backup_agent("remote") with patch( "homeassistant.components.backup.backup.async_get_backup_agents" ) as core_get_backup_agents: @@ -1513,26 +1514,21 @@ async def test_initiate_backup_file_error( assert unlink_mock.call_count == unlink_call_count -class LocalBackupAgentTest(BackupAgentTest, LocalBackupAgent): - """Local backup agent.""" - - def get_backup_path(self, backup_id: str) -> Path: - """Return the local path to an existing backup.""" - return Path("test.tar") - - def get_new_backup_path(self, backup: AgentBackup) -> Path: - """Return the local path to a new backup.""" - return Path("test.tar") +def _mock_local_backup_agent(name: str) -> Mock: + local_agent = mock_backup_agent(name) + # This makes the local_agent pass isinstance checks for LocalBackupAgent + local_agent.mock_add_spec(LocalBackupAgent) + return local_agent @pytest.mark.parametrize( - ("agent_class", "num_local_agents"), - [(LocalBackupAgentTest, 2), (BackupAgentTest, 1)], + ("agent_creator", "num_local_agents"), + [(_mock_local_backup_agent, 2), (mock_backup_agent, 1)], ) async def test_loading_platform_with_listener( hass: HomeAssistant, hass_ws_client: WebSocketGenerator, - agent_class: type[BackupAgentTest], + agent_creator: Callable[[str], Mock], num_local_agents: int, ) -> None: """Test loading a backup agent platform which can be listened to.""" @@ -1540,7 +1536,7 @@ async def test_loading_platform_with_listener( assert await async_setup_component(hass, DOMAIN, {}) manager = hass.data[DATA_MANAGER] - get_agents_mock = AsyncMock(return_value=[agent_class("remote1", backups=[])]) + get_agents_mock = AsyncMock(return_value=[agent_creator("remote1")]) register_listener_mock = Mock() await setup_backup_platform( @@ -1565,7 +1561,7 @@ async def test_loading_platform_with_listener( register_listener_mock.assert_called_once_with(hass, listener=ANY) get_agents_mock.reset_mock() - get_agents_mock.return_value = [agent_class("remote2", backups=[])] + get_agents_mock.return_value = [agent_creator("remote2")] listener = register_listener_mock.call_args[1]["listener"] listener() @@ -1609,7 +1605,7 @@ async def test_exception_platform_pre(hass: HomeAssistant) -> None: async def _mock_step(hass: HomeAssistant) -> None: raise HomeAssistantError("Test exception") - remote_agent = BackupAgentTest("remote", backups=[]) + remote_agent = mock_backup_agent("remote") await setup_backup_platform( hass, domain="test", @@ -1639,7 +1635,7 @@ async def test_exception_platform_post(hass: HomeAssistant) -> None: async def _mock_step(hass: HomeAssistant) -> None: raise HomeAssistantError("Test exception") - remote_agent = BackupAgentTest("remote", backups=[]) + remote_agent = mock_backup_agent("remote") await setup_backup_platform( hass, domain="test", @@ -1678,7 +1674,7 @@ async def _mock_step(hass: HomeAssistant) -> None: 2, 1, ["Test_1970-01-01_00.00_00000000.tar"], - {TEST_BACKUP_ABC123.backup_id: TEST_BACKUP_ABC123}, + {TEST_BACKUP_ABC123.backup_id: (TEST_BACKUP_ABC123, b"test")}, b"test", 0, ), @@ -1696,7 +1692,7 @@ async def _mock_step(hass: HomeAssistant) -> None: 2, 0, [], - {TEST_BACKUP_ABC123.backup_id: TEST_BACKUP_ABC123}, + {TEST_BACKUP_ABC123.backup_id: (TEST_BACKUP_ABC123, b"test")}, b"test", 1, ), @@ -1714,7 +1710,7 @@ async def test_receive_backup( temp_file_unlink_call_count: int, ) -> None: """Test receive backup and upload to the local and a remote agent.""" - remote_agent = BackupAgentTest("remote", backups=[]) + remote_agent = mock_backup_agent("remote") await setup_backup_platform( hass, domain="test", @@ -1754,8 +1750,12 @@ async def test_receive_backup( assert move_mock.call_count == move_call_count for index, name in enumerate(move_path_names): assert move_mock.call_args_list[index].args[1].name == name - assert remote_agent._backups == remote_agent_backups - assert remote_agent._backup_data == remote_agent_backup_data + for backup_id, (backup, expected_backup_data) in remote_agent_backups.items(): + assert await remote_agent.async_get_backup(backup_id) == backup + backup_data = bytearray() + async for chunk in await remote_agent.async_download_backup(backup_id): + backup_data += chunk + assert backup_data == expected_backup_data assert unlink_mock.call_count == temp_file_unlink_call_count @@ -1911,7 +1911,7 @@ async def test_receive_backup_agent_error( "with_automatic_settings": True, }, ] - remote_agent = BackupAgentTest("remote", backups=[backup_1, backup_2, backup_3]) + remote_agent = mock_backup_agent("remote", backups=[backup_1, backup_2, backup_3]) with patch( "homeassistant.components.backup.backup.async_get_backup_agents" @@ -2065,7 +2065,7 @@ async def test_receive_backup_non_agent_upload_error( ) -> None: """Test non agent upload error during backup receive.""" local_agent = local_backup_platform.CoreLocalBackupAgent(hass) - remote_agent = BackupAgentTest("remote", backups=[]) + remote_agent = mock_backup_agent("remote") with patch( "homeassistant.components.backup.backup.async_get_backup_agents" @@ -2193,7 +2193,7 @@ async def test_receive_backup_file_write_error( ) -> None: """Test file write error during backup receive.""" local_agent = local_backup_platform.CoreLocalBackupAgent(hass) - remote_agent = BackupAgentTest("remote", backups=[]) + remote_agent = mock_backup_agent("remote") with patch( "homeassistant.components.backup.backup.async_get_backup_agents" ) as core_get_backup_agents: @@ -2304,7 +2304,7 @@ async def test_receive_backup_read_tar_error( ) -> None: """Test read tar error during backup receive.""" local_agent = local_backup_platform.CoreLocalBackupAgent(hass) - remote_agent = BackupAgentTest("remote", backups=[]) + remote_agent = mock_backup_agent("remote") with patch( "homeassistant.components.backup.backup.async_get_backup_agents" ) as core_get_backup_agents: @@ -2484,7 +2484,8 @@ async def test_receive_backup_file_read_error( ) -> None: """Test file read error during backup receive.""" local_agent = local_backup_platform.CoreLocalBackupAgent(hass) - remote_agent = BackupAgentTest("remote", backups=[]) + + remote_agent = mock_backup_agent("remote") with patch( "homeassistant.components.backup.backup.async_get_backup_agents" ) as core_get_backup_agents: @@ -2654,7 +2655,7 @@ async def test_restore_backup( ) -> None: """Test restore backup.""" password = password_param.get("password") - remote_agent = BackupAgentTest("remote", backups=[TEST_BACKUP_ABC123]) + remote_agent = mock_backup_agent("remote", backups=[TEST_BACKUP_ABC123]) await async_setup_component(hass, DOMAIN, {}) await hass.async_block_till_done() await setup_backup_platform( @@ -2761,7 +2762,7 @@ async def test_restore_backup_wrong_password( ) -> None: """Test restore backup wrong password.""" password = "hunter2" - remote_agent = BackupAgentTest("remote", backups=[TEST_BACKUP_ABC123]) + remote_agent = mock_backup_agent("remote", backups=[TEST_BACKUP_ABC123]) await async_setup_component(hass, DOMAIN, {}) await hass.async_block_till_done() await setup_backup_platform( @@ -2988,7 +2989,7 @@ async def test_restore_backup_agent_error( expected_reason: str, ) -> None: """Test restore backup with agent error.""" - remote_agent = BackupAgentTest("remote", backups=[TEST_BACKUP_ABC123]) + remote_agent = mock_backup_agent("remote", backups=[TEST_BACKUP_ABC123]) await async_setup_component(hass, DOMAIN, {}) await hass.async_block_till_done() await setup_backup_platform( @@ -3128,7 +3129,7 @@ async def test_restore_backup_file_error( validate_password_call_count: int, ) -> None: """Test restore backup with file error.""" - remote_agent = BackupAgentTest("remote", backups=[TEST_BACKUP_ABC123]) + remote_agent = mock_backup_agent("remote", backups=[TEST_BACKUP_ABC123]) await async_setup_component(hass, DOMAIN, {}) await hass.async_block_till_done() await setup_backup_platform( @@ -3346,7 +3347,7 @@ async def test_initiate_backup_per_agent_encryption( ) -> None: """Test generate backup where encryption is selectively set on agents.""" local_agent = local_backup_platform.CoreLocalBackupAgent(hass) - remote_agent = BackupAgentTest("remote", backups=[]) + remote_agent = mock_backup_agent("remote") with patch( "homeassistant.components.backup.backup.async_get_backup_agents" diff --git a/tests/components/backup/test_websocket.py b/tests/components/backup/test_websocket.py index 773256bdd0b10..e97183fc53f2e 100644 --- a/tests/components/backup/test_websocket.py +++ b/tests/components/backup/test_websocket.py @@ -34,7 +34,7 @@ LOCAL_AGENT_ID, TEST_BACKUP_ABC123, TEST_BACKUP_DEF456, - BackupAgentTest, + mock_backup_agent, setup_backup_integration, setup_backup_platform, ) @@ -112,9 +112,9 @@ def mock_get_backups() -> Generator[AsyncMock]: ("remote_agents", "remote_backups"), [ ([], {}), - (["remote"], {}), - (["remote"], {"test.remote": [TEST_BACKUP_ABC123]}), - (["remote"], {"test.remote": [TEST_BACKUP_DEF456]}), + (["test.remote"], {}), + (["test.remote"], {"test.remote": [TEST_BACKUP_ABC123]}), + (["test.remote"], {"test.remote": [TEST_BACKUP_DEF456]}), ], ) async def test_info( @@ -153,25 +153,26 @@ async def test_info_with_errors( await setup_backup_integration( hass, with_hassio=False, backups={LOCAL_AGENT_ID: [TEST_BACKUP_ABC123]} ) - hass.data[DATA_MANAGER].backup_agents["domain.test"] = BackupAgentTest("test") + mock_agent = mock_backup_agent("test") + mock_agent.async_list_backups.side_effect = side_effect + hass.data[DATA_MANAGER].backup_agents["domain.test"] = mock_agent client = await hass_ws_client(hass) await hass.async_block_till_done() - with patch.object(BackupAgentTest, "async_list_backups", side_effect=side_effect): - await client.send_json_auto_id({"type": "backup/info"}) - assert await client.receive_json() == snapshot + await client.send_json_auto_id({"type": "backup/info"}) + assert await client.receive_json() == snapshot @pytest.mark.parametrize( ("remote_agents", "backups"), [ ([], {}), - (["remote"], {LOCAL_AGENT_ID: [TEST_BACKUP_ABC123]}), - (["remote"], {"test.remote": [TEST_BACKUP_ABC123]}), - (["remote"], {"test.remote": [TEST_BACKUP_DEF456]}), + (["test.remote"], {LOCAL_AGENT_ID: [TEST_BACKUP_ABC123]}), + (["test.remote"], {"test.remote": [TEST_BACKUP_ABC123]}), + (["test.remote"], {"test.remote": [TEST_BACKUP_DEF456]}), ( - ["remote"], + ["test.remote"], { LOCAL_AGENT_ID: [TEST_BACKUP_ABC123], "test.remote": [TEST_BACKUP_ABC123], @@ -215,15 +216,14 @@ async def test_details_with_errors( await setup_backup_integration( hass, with_hassio=False, backups={LOCAL_AGENT_ID: [TEST_BACKUP_ABC123]} ) - hass.data[DATA_MANAGER].backup_agents["domain.test"] = BackupAgentTest("test") + mock_agent = mock_backup_agent("test") + mock_agent.async_get_backup.side_effect = side_effect + hass.data[DATA_MANAGER].backup_agents["domain.test"] = mock_agent client = await hass_ws_client(hass) await hass.async_block_till_done() - with ( - patch("pathlib.Path.exists", return_value=True), - patch.object(BackupAgentTest, "async_get_backup", side_effect=side_effect), - ): + with patch("pathlib.Path.exists", return_value=True): await client.send_json_auto_id( {"type": "backup/details", "backup_id": "abc123"} ) @@ -234,11 +234,11 @@ async def test_details_with_errors( ("remote_agents", "backups"), [ ([], {}), - (["remote"], {LOCAL_AGENT_ID: [TEST_BACKUP_ABC123]}), - (["remote"], {"test.remote": [TEST_BACKUP_ABC123]}), - (["remote"], {"test.remote": [TEST_BACKUP_DEF456]}), + (["test.remote"], {LOCAL_AGENT_ID: [TEST_BACKUP_ABC123]}), + (["test.remote"], {"test.remote": [TEST_BACKUP_ABC123]}), + (["test.remote"], {"test.remote": [TEST_BACKUP_DEF456]}), ( - ["remote"], + ["test.remote"], { LOCAL_AGENT_ID: [TEST_BACKUP_ABC123], "test.remote": [TEST_BACKUP_ABC123], @@ -307,14 +307,15 @@ async def test_delete_with_errors( await setup_backup_integration( hass, with_hassio=False, backups={LOCAL_AGENT_ID: [TEST_BACKUP_ABC123]} ) - hass.data[DATA_MANAGER].backup_agents["domain.test"] = BackupAgentTest("test") + mock_agent = mock_backup_agent("test", [TEST_BACKUP_ABC123]) + mock_agent.async_delete_backup.side_effect = side_effect + hass.data[DATA_MANAGER].backup_agents["domain.test"] = mock_agent client = await hass_ws_client(hass) await hass.async_block_till_done() - with patch.object(BackupAgentTest, "async_delete_backup", side_effect=side_effect): - await client.send_json_auto_id({"type": "backup/delete", "backup_id": "abc123"}) - assert await client.receive_json() == snapshot + await client.send_json_auto_id({"type": "backup/delete", "backup_id": "abc123"}) + assert await client.receive_json() == snapshot await client.send_json_auto_id({"type": "backup/info"}) assert await client.receive_json() == snapshot @@ -327,21 +328,21 @@ async def test_agent_delete_backup( ) -> None: """Test deleting a backup file with a mock agent.""" await setup_backup_integration(hass) - hass.data[DATA_MANAGER].backup_agents = {"domain.test": BackupAgentTest("test")} + mock_agent = mock_backup_agent("test") + hass.data[DATA_MANAGER].backup_agents = {"domain.test": mock_agent} client = await hass_ws_client(hass) await hass.async_block_till_done() - with patch.object(BackupAgentTest, "async_delete_backup") as delete_mock: - await client.send_json_auto_id( - { - "type": "backup/delete", - "backup_id": "abc123", - } - ) - assert await client.receive_json() == snapshot + await client.send_json_auto_id( + { + "type": "backup/delete", + "backup_id": "abc123", + } + ) + assert await client.receive_json() == snapshot - assert delete_mock.call_args == call("abc123") + assert mock_agent.async_delete_backup.call_args == call("abc123") @pytest.mark.parametrize( @@ -588,7 +589,7 @@ async def test_generate_with_default_settings_calls_create( client = await hass_ws_client(hass) await hass.config.async_set_time_zone("Europe/Amsterdam") freezer.move_to("2024-11-13T12:01:00+01:00") - remote_agent = BackupAgentTest("remote", backups=[]) + remote_agent = mock_backup_agent("remote") await setup_backup_platform( hass, domain="test", @@ -688,8 +689,8 @@ async def test_restore_local_agent( @pytest.mark.parametrize( ("remote_agents", "backups"), [ - (["remote"], {}), - (["remote"], {"test.remote": [TEST_BACKUP_ABC123]}), + (["test.remote"], {}), + (["test.remote"], {"test.remote": [TEST_BACKUP_ABC123]}), ], ) async def test_restore_remote_agent( @@ -700,6 +701,7 @@ async def test_restore_remote_agent( snapshot: SnapshotAssertion, ) -> None: """Test calling the restore command.""" + await setup_backup_integration( hass, with_hassio=False, backups=backups, remote_agents=remote_agents ) @@ -892,7 +894,7 @@ async def test_agents_info( ) -> None: """Test getting backup agents info.""" await setup_backup_integration(hass, with_hassio=False) - hass.data[DATA_MANAGER].backup_agents["domain.test"] = BackupAgentTest("test") + hass.data[DATA_MANAGER].backup_agents["domain.test"] = mock_backup_agent("test") client = await hass_ws_client(hass) await hass.async_block_till_done() @@ -1730,7 +1732,7 @@ async def test_config_schedule_logic( await hass.config.async_set_time_zone("Europe/Amsterdam") freezer.move_to("2024-11-11 12:00:00+01:00") - await setup_backup_integration(hass, remote_agents=["test-agent"]) + await setup_backup_integration(hass, remote_agents=["test.test-agent"]) await hass.async_block_till_done() for command in commands: @@ -1773,7 +1775,7 @@ async def test_config_schedule_logic( "command", "backups", "get_backups_agent_errors", - "agent_delete_backup_side_effects", + "delete_backup_side_effects", "last_backup_time", "next_time", "backup_time", @@ -2345,7 +2347,7 @@ async def test_config_retention_copies_logic( command: dict[str, Any], backups: dict[str, Any], get_backups_agent_errors: dict[str, Exception], - agent_delete_backup_side_effects: dict[str, Exception], + delete_backup_side_effects: dict[str, Exception], last_backup_time: str, next_time: str, backup_time: str, @@ -2392,14 +2394,13 @@ async def test_config_retention_copies_logic( await hass.config.async_set_time_zone("Europe/Amsterdam") freezer.move_to("2024-11-11 12:00:00+01:00") - await setup_backup_integration(hass, remote_agents=["test-agent", "test-agent2"]) + mock_agents = await setup_backup_integration( + hass, remote_agents=["test.test-agent", "test.test-agent2"] + ) await hass.async_block_till_done() - manager = hass.data[DATA_MANAGER] - for agent_id, agent in manager.backup_agents.items(): - agent.async_delete_backup = AsyncMock( - side_effect=agent_delete_backup_side_effects.get(agent_id), autospec=True - ) + for agent_id, agent in mock_agents.items(): + agent.async_delete_backup.side_effect = delete_backup_side_effects.get(agent_id) await client.send_json_auto_id(command) result = await client.receive_json() @@ -2411,7 +2412,7 @@ async def test_config_retention_copies_logic( await hass.async_block_till_done() assert create_backup.call_count == backup_calls assert get_backups.call_count == get_backups_calls - for agent_id, agent in manager.backup_agents.items(): + for agent_id, agent in mock_agents.items(): agent_delete_calls = delete_calls.get(agent_id, []) assert agent.async_delete_backup.call_count == len(agent_delete_calls) assert agent.async_delete_backup.call_args_list == agent_delete_calls @@ -2671,13 +2672,11 @@ async def test_config_retention_copies_logic_manual_backup( await hass.config.async_set_time_zone("Europe/Amsterdam") freezer.move_to("2024-11-11 12:00:00+01:00") - await setup_backup_integration(hass, remote_agents=["test-agent"]) + mock_agents = await setup_backup_integration( + hass, remote_agents=["test.test-agent"] + ) await hass.async_block_till_done() - manager = hass.data[DATA_MANAGER] - for agent in manager.backup_agents.values(): - agent.async_delete_backup = AsyncMock(autospec=True) - await client.send_json_auto_id(config_command) result = await client.receive_json() assert result["success"] @@ -2692,7 +2691,7 @@ async def test_config_retention_copies_logic_manual_backup( assert create_backup.call_count == backup_calls assert get_backups.call_count == get_backups_calls - for agent_id, agent in manager.backup_agents.items(): + for agent_id, agent in mock_agents.items(): agent_delete_calls = delete_calls.get(agent_id, []) assert agent.async_delete_backup.call_count == len(agent_delete_calls) assert agent.async_delete_backup.call_args_list == agent_delete_calls @@ -2714,7 +2713,7 @@ async def test_config_retention_copies_logic_manual_backup( "commands", "backups", "get_backups_agent_errors", - "agent_delete_backup_side_effects", + "delete_backup_side_effects", "last_backup_time", "start_time", "next_time", @@ -3077,7 +3076,7 @@ async def test_config_retention_days_logic( commands: list[dict[str, Any]], backups: dict[str, Any], get_backups_agent_errors: dict[str, Exception], - agent_delete_backup_side_effects: dict[str, Exception], + delete_backup_side_effects: dict[str, Exception], last_backup_time: str, start_time: str, next_time: str, @@ -3120,14 +3119,13 @@ async def test_config_retention_days_logic( await hass.config.async_set_time_zone("Europe/Amsterdam") freezer.move_to(start_time) - await setup_backup_integration(hass, remote_agents=["test-agent"]) + mock_agents = await setup_backup_integration( + hass, remote_agents=["test.test-agent"] + ) await hass.async_block_till_done() - manager = hass.data[DATA_MANAGER] - for agent_id, agent in manager.backup_agents.items(): - agent.async_delete_backup = AsyncMock( - side_effect=agent_delete_backup_side_effects.get(agent_id), autospec=True - ) + for agent_id, agent in mock_agents.items(): + agent.async_delete_backup.side_effect = delete_backup_side_effects.get(agent_id) for command in commands: await client.send_json_auto_id(command) @@ -3138,7 +3136,7 @@ async def test_config_retention_days_logic( async_fire_time_changed(hass) await hass.async_block_till_done() assert get_backups.call_count == get_backups_calls - for agent_id, agent in manager.backup_agents.items(): + for agent_id, agent in mock_agents.items(): agent_delete_calls = delete_calls.get(agent_id, []) assert agent.async_delete_backup.call_count == len(agent_delete_calls) assert agent.async_delete_backup.call_args_list == agent_delete_calls @@ -3222,21 +3220,21 @@ async def test_can_decrypt_on_download_with_agent_error( ) -> None: """Test can decrypt on download.""" - await setup_backup_integration( + mock_agents = await setup_backup_integration( hass, with_hassio=False, backups={"test.remote": [TEST_BACKUP_ABC123]}, - remote_agents=["remote"], + remote_agents=["test.remote"], ) client = await hass_ws_client(hass) - with patch.object(BackupAgentTest, "async_download_backup", side_effect=error): - await client.send_json_auto_id( - { - "type": "backup/can_decrypt_on_download", - "backup_id": TEST_BACKUP_ABC123.backup_id, - "agent_id": "test.remote", - "password": "hunter2", - } - ) - assert await client.receive_json() == snapshot + mock_agents["test.remote"].async_download_backup.side_effect = error + await client.send_json_auto_id( + { + "type": "backup/can_decrypt_on_download", + "backup_id": TEST_BACKUP_ABC123.backup_id, + "agent_id": "test.remote", + "password": "hunter2", + } + ) + assert await client.receive_json() == snapshot