Avoid using the backup manager in restore tests (#135757)

* Fix typing

* Refactor test restore backup

* Refactor test restore backup wrong password

* Refactor test restore backup wrong parameters

* Update manager state after rebase

* Remove not needed patch
This commit is contained in:
Martin Hjelmare 2025-01-16 12:49:27 +01:00 committed by GitHub
parent 9a1b965c7f
commit 421f9aa638
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
2 changed files with 185 additions and 88 deletions

View File

@ -74,7 +74,7 @@ def mock_create_backup() -> Generator[AsyncMock]:
mock_written_backup.backup.backup_id = "abc123" mock_written_backup.backup.backup_id = "abc123"
mock_written_backup.open_stream = AsyncMock() mock_written_backup.open_stream = AsyncMock()
mock_written_backup.release_stream = AsyncMock() mock_written_backup.release_stream = AsyncMock()
fut = Future() fut: Future[MagicMock] = Future()
fut.set_result(mock_written_backup) fut.set_result(mock_written_backup)
with patch( with patch(
"homeassistant.components.backup.CoreBackupReaderWriter.async_create_backup" "homeassistant.components.backup.CoreBackupReaderWriter.async_create_backup"

View File

@ -45,6 +45,7 @@ from homeassistant.components.backup.manager import (
NewBackup, NewBackup,
ReceiveBackupStage, ReceiveBackupStage,
ReceiveBackupState, ReceiveBackupState,
RestoreBackupState,
WrittenBackup, WrittenBackup,
) )
from homeassistant.core import HomeAssistant from homeassistant.core import HomeAssistant
@ -682,7 +683,7 @@ async def test_create_backup_success_clears_issue(
assert set(issue_registry.issues) == issues_after_create_backup assert set(issue_registry.issues) == issues_after_create_backup
async def delayed_boom(*args, **kwargs) -> None: async def delayed_boom(*args, **kwargs) -> tuple[NewBackup, Any]:
"""Raise an exception after a delay.""" """Raise an exception after a delay."""
async def delayed_boom() -> None: async def delayed_boom() -> None:
@ -2224,42 +2225,47 @@ async def test_receive_backup_file_read_error(
assert unlink_mock.call_count == unlink_call_count assert unlink_mock.call_count == unlink_call_count
@pytest.mark.usefixtures("path_glob")
@pytest.mark.parametrize( @pytest.mark.parametrize(
("agent_id", "password", "restore_database", "restore_homeassistant", "dir"), ("agent_id", "password_param", "restore_database", "restore_homeassistant", "dir"),
[ [
(LOCAL_AGENT_ID, None, True, False, "backups"), (LOCAL_AGENT_ID, {}, True, False, "backups"),
(LOCAL_AGENT_ID, "abc123", False, True, "backups"), (LOCAL_AGENT_ID, {"password": "abc123"}, False, True, "backups"),
("test.remote", None, True, True, "tmp_backups"), ("test.remote", {}, True, True, "tmp_backups"),
], ],
) )
async def test_async_trigger_restore( async def test_restore_backup(
hass: HomeAssistant, hass: HomeAssistant,
hass_ws_client: WebSocketGenerator,
agent_id: str, agent_id: str,
password: str | None, password_param: dict[str, str],
restore_database: bool, restore_database: bool,
restore_homeassistant: bool, restore_homeassistant: bool,
dir: str, dir: str,
) -> None: ) -> None:
"""Test trigger restore.""" """Test restore backup."""
manager = BackupManager(hass, CoreBackupReaderWriter(hass)) password = password_param.get("password")
hass.data[DATA_MANAGER] = manager remote_agent = BackupAgentTest("remote", backups=[TEST_BACKUP_ABC123])
await async_setup_component(hass, DOMAIN, {})
await setup_backup_platform(hass, domain=DOMAIN, platform=local_backup_platform) await hass.async_block_till_done()
await setup_backup_platform( await setup_backup_platform(
hass, hass,
domain="test", domain="test",
platform=Mock( platform=Mock(
async_get_backup_agents=AsyncMock( async_get_backup_agents=AsyncMock(return_value=[remote_agent]),
return_value=[BackupAgentTest("remote", backups=[TEST_BACKUP_ABC123])]
),
spec_set=BackupAgentPlatformProtocol, spec_set=BackupAgentPlatformProtocol,
), ),
) )
await manager.load_platforms()
local_agent = manager.backup_agents[LOCAL_AGENT_ID] ws_client = await hass_ws_client(hass)
local_agent._backups = {TEST_BACKUP_ABC123.backup_id: TEST_BACKUP_ABC123}
local_agent._loaded_backups = True 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 ( with (
patch("pathlib.Path.exists", return_value=True), patch("pathlib.Path.exists", return_value=True),
@ -2269,90 +2275,152 @@ async def test_async_trigger_restore(
patch( patch(
"homeassistant.components.backup.manager.validate_password" "homeassistant.components.backup.manager.validate_password"
) as validate_password_mock, ) as validate_password_mock,
patch.object(BackupAgentTest, "async_download_backup") as download_mock, patch.object(remote_agent, "async_download_backup") as download_mock,
patch(
"homeassistant.components.backup.backup.read_backup",
return_value=TEST_BACKUP_ABC123,
),
): ):
download_mock.return_value.__aiter__.return_value = iter((b"backup data",)) download_mock.return_value.__aiter__.return_value = iter((b"backup data",))
await manager.async_restore_backup( await ws_client.send_json_auto_id(
TEST_BACKUP_ABC123.backup_id,
agent_id=agent_id,
password=password,
restore_addons=None,
restore_database=restore_database,
restore_folders=None,
restore_homeassistant=restore_homeassistant,
)
backup_path = f"{hass.config.path()}/{dir}/abc123.tar"
expected_restore_file = json.dumps(
{ {
"path": backup_path, "type": "backup/restore",
"password": password, "backup_id": TEST_BACKUP_ABC123.backup_id,
"remove_after_restore": agent_id != LOCAL_AGENT_ID, "agent_id": agent_id,
"restore_database": restore_database, "restore_database": restore_database,
"restore_homeassistant": restore_homeassistant, "restore_homeassistant": restore_homeassistant,
} }
| password_param
) )
validate_password_mock.assert_called_once_with(Path(backup_path), password)
assert mocked_write_text.call_args[0][0] == expected_restore_file result = await ws_client.receive_json()
assert mocked_service_call.called 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.COMPLETED,
}
result = await ws_client.receive_json()
assert result["event"] == {"manager_state": BackupManagerState.IDLE}
result = await ws_client.receive_json()
assert result["success"] is True
backup_path = f"{hass.config.path()}/{dir}/abc123.tar"
expected_restore_file = json.dumps(
{
"path": backup_path,
"password": password,
"remove_after_restore": agent_id != LOCAL_AGENT_ID,
"restore_database": restore_database,
"restore_homeassistant": restore_homeassistant,
}
)
validate_password_mock.assert_called_once_with(Path(backup_path), password)
assert mocked_write_text.call_args[0][0] == expected_restore_file
assert mocked_service_call.called
async def test_async_trigger_restore_wrong_password(hass: HomeAssistant) -> None: @pytest.mark.usefixtures("path_glob")
"""Test trigger restore.""" @pytest.mark.parametrize(
("agent_id", "dir"), [(LOCAL_AGENT_ID, "backups"), ("test.remote", "tmp_backups")]
)
async def test_restore_backup_wrong_password(
hass: HomeAssistant,
hass_ws_client: WebSocketGenerator,
agent_id: str,
dir: str,
) -> None:
"""Test restore backup wrong password."""
password = "hunter2" password = "hunter2"
manager = BackupManager(hass, CoreBackupReaderWriter(hass)) remote_agent = BackupAgentTest("remote", backups=[TEST_BACKUP_ABC123])
hass.data[DATA_MANAGER] = manager await async_setup_component(hass, DOMAIN, {})
await hass.async_block_till_done()
await setup_backup_platform(hass, domain=DOMAIN, platform=local_backup_platform)
await setup_backup_platform( await setup_backup_platform(
hass, hass,
domain="test", domain="test",
platform=Mock( platform=Mock(
async_get_backup_agents=AsyncMock( async_get_backup_agents=AsyncMock(return_value=[remote_agent]),
return_value=[BackupAgentTest("remote", backups=[TEST_BACKUP_ABC123])]
),
spec_set=BackupAgentPlatformProtocol, spec_set=BackupAgentPlatformProtocol,
), ),
) )
await manager.load_platforms()
local_agent = manager.backup_agents[LOCAL_AGENT_ID] ws_client = await hass_ws_client(hass)
local_agent._backups = {TEST_BACKUP_ABC123.backup_id: TEST_BACKUP_ABC123}
local_agent._loaded_backups = True 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 ( with (
patch("pathlib.Path.exists", return_value=True), patch("pathlib.Path.exists", return_value=True),
patch("pathlib.Path.open"),
patch("pathlib.Path.write_text") as mocked_write_text, patch("pathlib.Path.write_text") as mocked_write_text,
patch("homeassistant.core.ServiceRegistry.async_call") as mocked_service_call, patch("homeassistant.core.ServiceRegistry.async_call") as mocked_service_call,
patch( patch(
"homeassistant.components.backup.manager.validate_password" "homeassistant.components.backup.manager.validate_password"
) as validate_password_mock, ) as validate_password_mock,
patch.object(remote_agent, "async_download_backup") as download_mock,
patch(
"homeassistant.components.backup.backup.read_backup",
return_value=TEST_BACKUP_ABC123,
),
): ):
download_mock.return_value.__aiter__.return_value = iter((b"backup data",))
validate_password_mock.return_value = False validate_password_mock.return_value = False
with pytest.raises( await ws_client.send_json_auto_id(
HomeAssistantError, match="The password provided is incorrect." {
): "type": "backup/restore",
await manager.async_restore_backup( "backup_id": TEST_BACKUP_ABC123.backup_id,
TEST_BACKUP_ABC123.backup_id, "agent_id": agent_id,
agent_id=LOCAL_AGENT_ID, "password": password,
password=password, }
restore_addons=None, )
restore_database=True,
restore_folders=None,
restore_homeassistant=True,
)
backup_path = f"{hass.config.path()}/backups/abc123.tar" result = await ws_client.receive_json()
validate_password_mock.assert_called_once_with(Path(backup_path), password) assert result["event"] == {
mocked_write_text.assert_not_called() "manager_state": BackupManagerState.RESTORE_BACKUP,
mocked_service_call.assert_not_called() "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"] == "password_incorrect"
backup_path = f"{hass.config.path()}/{dir}/abc123.tar"
validate_password_mock.assert_called_once_with(Path(backup_path), password)
mocked_write_text.assert_not_called()
mocked_service_call.assert_not_called()
@pytest.mark.usefixtures("path_glob")
@pytest.mark.parametrize( @pytest.mark.parametrize(
("parameters", "expected_error"), ("parameters", "expected_error"),
[ [
( (
{"backup_id": TEST_BACKUP_DEF456.backup_id}, {"backup_id": TEST_BACKUP_DEF456.backup_id},
"Backup def456 not found", f"Backup def456 not found in agent {LOCAL_AGENT_ID}",
), ),
( (
{"restore_addons": ["blah"]}, {"restore_addons": ["blah"]},
@ -2368,36 +2436,65 @@ async def test_async_trigger_restore_wrong_password(hass: HomeAssistant) -> None
), ),
], ],
) )
async def test_async_trigger_restore_wrong_parameters( async def test_restore_backup_wrong_parameters(
hass: HomeAssistant, parameters: dict[str, Any], expected_error: str hass: HomeAssistant,
hass_ws_client: WebSocketGenerator,
parameters: dict[str, Any],
expected_error: str,
) -> None: ) -> None:
"""Test trigger restore.""" """Test restore backup wrong parameters."""
manager = BackupManager(hass, CoreBackupReaderWriter(hass)) await async_setup_component(hass, DOMAIN, {})
await hass.async_block_till_done()
await setup_backup_platform(hass, domain=DOMAIN, platform=local_backup_platform) ws_client = await hass_ws_client(hass)
await manager.load_platforms()
local_agent = manager.backup_agents[LOCAL_AGENT_ID] await ws_client.send_json_auto_id({"type": "backup/subscribe_events"})
local_agent._backups = {TEST_BACKUP_ABC123.backup_id: TEST_BACKUP_ABC123}
local_agent._loaded_backups = True
default_parameters = { result = await ws_client.receive_json()
"agent_id": LOCAL_AGENT_ID, assert result["event"] == {"manager_state": BackupManagerState.IDLE}
"backup_id": TEST_BACKUP_ABC123.backup_id,
"password": None, result = await ws_client.receive_json()
"restore_addons": None, assert result["success"] is True
"restore_database": True,
"restore_folders": None,
"restore_homeassistant": True,
}
with ( with (
patch("pathlib.Path.exists", return_value=True), patch("pathlib.Path.exists", return_value=True),
patch("pathlib.Path.write_text") as mocked_write_text, patch("pathlib.Path.write_text") as mocked_write_text,
patch("homeassistant.core.ServiceRegistry.async_call") as mocked_service_call, patch("homeassistant.core.ServiceRegistry.async_call") as mocked_service_call,
pytest.raises(HomeAssistantError, match=expected_error), patch(
"homeassistant.components.backup.backup.read_backup",
return_value=TEST_BACKUP_ABC123,
),
): ):
await manager.async_restore_backup(**(default_parameters | parameters)) await ws_client.send_json_auto_id(
{
"type": "backup/restore",
"backup_id": TEST_BACKUP_ABC123.backup_id,
"agent_id": LOCAL_AGENT_ID,
}
| parameters
)
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"] == "home_assistant_error"
assert result["error"]["message"] == expected_error
mocked_write_text.assert_not_called() mocked_write_text.assert_not_called()
mocked_service_call.assert_not_called() mocked_service_call.assert_not_called()