Improve error message when failing to create backups (#139262)
* Improve error message when failing to create backups * Check for expected error message in testspull/139340/head
parent
0f827fbf22
commit
ee01aa73b8
|
@ -1620,7 +1620,13 @@ class CoreBackupReaderWriter(BackupReaderWriter):
|
|||
"""Generate backup contents and return the size."""
|
||||
if not tar_file_path:
|
||||
tar_file_path = self.temp_backup_dir / f"{backup_data['slug']}.tar"
|
||||
make_backup_dir(tar_file_path.parent)
|
||||
try:
|
||||
make_backup_dir(tar_file_path.parent)
|
||||
except OSError as err:
|
||||
raise BackupReaderWriterError(
|
||||
f"Failed to create dir {tar_file_path.parent}: "
|
||||
f"{err} ({err.__class__.__name__})"
|
||||
) from err
|
||||
|
||||
excludes = EXCLUDE_FROM_BACKUP
|
||||
if not database_included:
|
||||
|
@ -1658,7 +1664,14 @@ class CoreBackupReaderWriter(BackupReaderWriter):
|
|||
file_filter=is_excluded_by_filter,
|
||||
arcname="data",
|
||||
)
|
||||
return (tar_file_path, tar_file_path.stat().st_size)
|
||||
try:
|
||||
stat_result = tar_file_path.stat()
|
||||
except OSError as err:
|
||||
raise BackupReaderWriterError(
|
||||
f"Error getting size of {tar_file_path}: "
|
||||
f"{err} ({err.__class__.__name__})"
|
||||
) from err
|
||||
return (tar_file_path, stat_result.st_size)
|
||||
|
||||
async def async_receive_backup(
|
||||
self,
|
||||
|
|
|
@ -1311,7 +1311,7 @@ async def test_initiate_backup_with_task_error(
|
|||
(1, None, 1, None, 1, None, 1, OSError("Boom!")),
|
||||
],
|
||||
)
|
||||
async def test_initiate_backup_file_error(
|
||||
async def test_initiate_backup_file_error_upload_to_agents(
|
||||
hass: HomeAssistant,
|
||||
hass_ws_client: WebSocketGenerator,
|
||||
generate_backup_id: MagicMock,
|
||||
|
@ -1325,7 +1325,7 @@ async def test_initiate_backup_file_error(
|
|||
unlink_call_count: int,
|
||||
unlink_exception: Exception | None,
|
||||
) -> None:
|
||||
"""Test file error during generate backup."""
|
||||
"""Test file error during generate backup, while uploading to agents."""
|
||||
agent_ids = ["test.remote"]
|
||||
|
||||
await setup_backup_integration(hass, remote_agents=["test.remote"])
|
||||
|
@ -1418,6 +1418,122 @@ async def test_initiate_backup_file_error(
|
|||
assert unlink_mock.call_count == unlink_call_count
|
||||
|
||||
|
||||
@pytest.mark.usefixtures("mock_backup_generation")
|
||||
@pytest.mark.parametrize(
|
||||
(
|
||||
"mkdir_call_count",
|
||||
"mkdir_exception",
|
||||
"atomic_contents_add_call_count",
|
||||
"atomic_contents_add_exception",
|
||||
"stat_call_count",
|
||||
"stat_exception",
|
||||
"error_message",
|
||||
),
|
||||
[
|
||||
(1, OSError("Boom!"), 0, None, 0, None, "Failed to create dir"),
|
||||
(1, None, 1, OSError("Boom!"), 0, None, "Boom!"),
|
||||
(1, None, 1, None, 1, OSError("Boom!"), "Error getting size"),
|
||||
],
|
||||
)
|
||||
async def test_initiate_backup_file_error_create_backup(
|
||||
hass: HomeAssistant,
|
||||
hass_ws_client: WebSocketGenerator,
|
||||
generate_backup_id: MagicMock,
|
||||
path_glob: MagicMock,
|
||||
caplog: pytest.LogCaptureFixture,
|
||||
mkdir_call_count: int,
|
||||
mkdir_exception: Exception | None,
|
||||
atomic_contents_add_call_count: int,
|
||||
atomic_contents_add_exception: Exception | None,
|
||||
stat_call_count: int,
|
||||
stat_exception: Exception | None,
|
||||
error_message: str,
|
||||
) -> None:
|
||||
"""Test file error during generate backup, while creating backup."""
|
||||
agent_ids = ["test.remote"]
|
||||
|
||||
await setup_backup_integration(hass, remote_agents=["test.remote"])
|
||||
|
||||
ws_client = await hass_ws_client(hass)
|
||||
|
||||
path_glob.return_value = []
|
||||
|
||||
await ws_client.send_json_auto_id({"type": "backup/info"})
|
||||
result = await ws_client.receive_json()
|
||||
|
||||
assert result["success"] is True
|
||||
assert result["result"] == {
|
||||
"backups": [],
|
||||
"agent_errors": {},
|
||||
"last_attempted_automatic_backup": None,
|
||||
"last_completed_automatic_backup": None,
|
||||
"last_non_idle_event": None,
|
||||
"next_automatic_backup": None,
|
||||
"next_automatic_backup_additional": False,
|
||||
"state": "idle",
|
||||
}
|
||||
|
||||
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(
|
||||
"homeassistant.components.backup.manager.atomic_contents_add",
|
||||
side_effect=atomic_contents_add_exception,
|
||||
) as atomic_contents_add_mock,
|
||||
patch("pathlib.Path.mkdir", side_effect=mkdir_exception) as mkdir_mock,
|
||||
patch("pathlib.Path.stat", side_effect=stat_exception) as stat_mock,
|
||||
):
|
||||
await ws_client.send_json_auto_id(
|
||||
{"type": "backup/generate", "agent_ids": agent_ids}
|
||||
)
|
||||
|
||||
result = await ws_client.receive_json()
|
||||
assert result["event"] == {
|
||||
"manager_state": BackupManagerState.CREATE_BACKUP,
|
||||
"reason": None,
|
||||
"stage": None,
|
||||
"state": CreateBackupState.IN_PROGRESS,
|
||||
}
|
||||
result = await ws_client.receive_json()
|
||||
assert result["success"] is True
|
||||
|
||||
backup_id = result["result"]["backup_job_id"]
|
||||
assert backup_id == generate_backup_id.return_value
|
||||
|
||||
await hass.async_block_till_done()
|
||||
|
||||
result = await ws_client.receive_json()
|
||||
assert result["event"] == {
|
||||
"manager_state": BackupManagerState.CREATE_BACKUP,
|
||||
"reason": None,
|
||||
"stage": CreateBackupStage.HOME_ASSISTANT,
|
||||
"state": CreateBackupState.IN_PROGRESS,
|
||||
}
|
||||
|
||||
result = await ws_client.receive_json()
|
||||
assert result["event"] == {
|
||||
"manager_state": BackupManagerState.CREATE_BACKUP,
|
||||
"reason": "upload_failed",
|
||||
"stage": None,
|
||||
"state": CreateBackupState.FAILED,
|
||||
}
|
||||
|
||||
result = await ws_client.receive_json()
|
||||
assert result["event"] == {"manager_state": BackupManagerState.IDLE}
|
||||
|
||||
assert atomic_contents_add_mock.call_count == atomic_contents_add_call_count
|
||||
assert mkdir_mock.call_count == mkdir_call_count
|
||||
assert stat_mock.call_count == stat_call_count
|
||||
|
||||
assert error_message in caplog.text
|
||||
|
||||
|
||||
def _mock_local_backup_agent(name: str) -> Mock:
|
||||
local_agent = mock_backup_agent(name)
|
||||
# This makes the local_agent pass isinstance checks for LocalBackupAgent
|
||||
|
|
Loading…
Reference in New Issue