diff --git a/tests/components/backup/test_manager.py b/tests/components/backup/test_manager.py index c961230e9e6..48e6db4ae9a 100644 --- a/tests/components/backup/test_manager.py +++ b/tests/components/backup/test_manager.py @@ -2483,3 +2483,265 @@ async def test_restore_backup_wrong_parameters( mocked_write_text.assert_not_called() mocked_service_call.assert_not_called() + + +@pytest.mark.usefixtures("mock_backup_generation") +async def test_restore_backup_when_busy( + hass: HomeAssistant, + hass_ws_client: WebSocketGenerator, +) -> None: + """Test restore backup with busy manager.""" + assert await async_setup_component(hass, DOMAIN, {}) + await hass.async_block_till_done() + ws_client = await hass_ws_client(hass) + + await ws_client.send_json_auto_id( + {"type": "backup/generate", "agent_ids": [LOCAL_AGENT_ID]} + ) + result = await ws_client.receive_json() + + assert result["success"] is True + + await ws_client.send_json_auto_id( + { + "type": "backup/restore", + "backup_id": TEST_BACKUP_ABC123.backup_id, + "agent_id": LOCAL_AGENT_ID, + } + ) + result = await ws_client.receive_json() + + assert result["success"] is False + assert result["error"]["code"] == "home_assistant_error" + assert result["error"]["message"] == "Backup manager busy: create_backup" + + +@pytest.mark.usefixtures("mock_backup_generation") +@pytest.mark.parametrize( + ("exception", "error_code", "error_message"), + [ + (BackupAgentError("Boom!"), "home_assistant_error", "Boom!"), + (Exception("Boom!"), "unknown_error", "Unknown error"), + ], +) +async def test_restore_backup_agent_error( + hass: HomeAssistant, + hass_ws_client: WebSocketGenerator, + exception: Exception, + error_code: str, + error_message: str, +) -> None: + """Test restore backup with agent error.""" + remote_agent = BackupAgentTest("remote", backups=[TEST_BACKUP_ABC123]) + await async_setup_component(hass, DOMAIN, {}) + await hass.async_block_till_done() + await setup_backup_platform( + hass, + domain="test", + platform=Mock( + async_get_backup_agents=AsyncMock(return_value=[remote_agent]), + spec_set=BackupAgentPlatformProtocol, + ), + ) + ws_client = await hass_ws_client(hass) + + await ws_client.send_json_auto_id({"type": "backup/subscribe_events"}) + + result = await ws_client.receive_json() + assert result["event"] == {"manager_state": BackupManagerState.IDLE} + + result = await ws_client.receive_json() + assert result["success"] is True + + with ( + patch("pathlib.Path.open"), + patch("pathlib.Path.write_text") as mocked_write_text, + patch("homeassistant.core.ServiceRegistry.async_call") as mocked_service_call, + patch.object( + remote_agent, "async_download_backup", side_effect=exception + ) as download_mock, + ): + await ws_client.send_json_auto_id( + { + "type": "backup/restore", + "backup_id": TEST_BACKUP_ABC123.backup_id, + "agent_id": remote_agent.agent_id, + } + ) + + result = await ws_client.receive_json() + assert result["event"] == { + "manager_state": BackupManagerState.RESTORE_BACKUP, + "stage": None, + "state": RestoreBackupState.IN_PROGRESS, + } + + result = await ws_client.receive_json() + assert result["event"] == { + "manager_state": BackupManagerState.RESTORE_BACKUP, + "stage": None, + "state": RestoreBackupState.FAILED, + } + + result = await ws_client.receive_json() + assert result["event"] == {"manager_state": BackupManagerState.IDLE} + + result = await ws_client.receive_json() + assert not result["success"] + assert result["error"]["code"] == error_code + assert result["error"]["message"] == error_message + + assert download_mock.call_count == 1 + assert mocked_write_text.call_count == 0 + assert mocked_service_call.call_count == 0 + + +@pytest.mark.usefixtures("mock_backup_generation") +@pytest.mark.parametrize( + ( + "open_call_count", + "open_exception", + "write_call_count", + "write_exception", + "close_call_count", + "close_exception", + "write_text_call_count", + "write_text_exception", + "validate_password_call_count", + ), + [ + ( + 1, + OSError("Boom!"), + 0, + None, + 0, + None, + 0, + None, + 0, + ), + ( + 1, + None, + 1, + OSError("Boom!"), + 1, + None, + 0, + None, + 0, + ), + ( + 1, + None, + 1, + None, + 1, + OSError("Boom!"), + 0, + None, + 0, + ), + ( + 1, + None, + 1, + None, + 1, + None, + 1, + OSError("Boom!"), + 1, + ), + ], +) +async def test_restore_backup_file_error( + hass: HomeAssistant, + hass_ws_client: WebSocketGenerator, + open_call_count: int, + open_exception: list[Exception | None], + write_call_count: int, + write_exception: Exception | None, + close_call_count: int, + close_exception: list[Exception | None], + write_text_call_count: int, + write_text_exception: Exception | None, + validate_password_call_count: int, +) -> None: + """Test restore backup with file error.""" + remote_agent = BackupAgentTest("remote", backups=[TEST_BACKUP_ABC123]) + await async_setup_component(hass, DOMAIN, {}) + await hass.async_block_till_done() + await setup_backup_platform( + hass, + domain="test", + platform=Mock( + async_get_backup_agents=AsyncMock(return_value=[remote_agent]), + spec_set=BackupAgentPlatformProtocol, + ), + ) + ws_client = await hass_ws_client(hass) + + await ws_client.send_json_auto_id({"type": "backup/subscribe_events"}) + + result = await ws_client.receive_json() + assert result["event"] == {"manager_state": BackupManagerState.IDLE} + + result = await ws_client.receive_json() + assert result["success"] is True + + open_mock = mock_open() + open_mock.side_effect = open_exception + open_mock.return_value.write.side_effect = write_exception + open_mock.return_value.close.side_effect = close_exception + + with ( + patch("pathlib.Path.open", open_mock), + patch( + "pathlib.Path.write_text", side_effect=write_text_exception + ) as mocked_write_text, + patch("homeassistant.core.ServiceRegistry.async_call") as mocked_service_call, + patch( + "homeassistant.components.backup.manager.validate_password" + ) as validate_password_mock, + patch.object(remote_agent, "async_download_backup") as download_mock, + ): + download_mock.return_value.__aiter__.return_value = iter((b"backup data",)) + await ws_client.send_json_auto_id( + { + "type": "backup/restore", + "backup_id": TEST_BACKUP_ABC123.backup_id, + "agent_id": remote_agent.agent_id, + } + ) + + result = await ws_client.receive_json() + assert result["event"] == { + "manager_state": BackupManagerState.RESTORE_BACKUP, + "stage": None, + "state": RestoreBackupState.IN_PROGRESS, + } + + result = await ws_client.receive_json() + assert result["event"] == { + "manager_state": BackupManagerState.RESTORE_BACKUP, + "stage": None, + "state": RestoreBackupState.FAILED, + } + + result = await ws_client.receive_json() + assert result["event"] == {"manager_state": BackupManagerState.IDLE} + + result = await ws_client.receive_json() + assert not result["success"] + assert result["error"]["code"] == "unknown_error" + assert result["error"]["message"] == "Unknown error" + + assert download_mock.call_count == 1 + assert validate_password_mock.call_count == validate_password_call_count + assert open_mock.call_count == open_call_count + assert open_mock.return_value.write.call_count == write_call_count + assert open_mock.return_value.close.call_count == close_call_count + assert mocked_write_text.call_count == write_text_call_count + assert mocked_service_call.call_count == 0