Include error reason in backup events (#136697)

* Include error reason in backup events

* Update hassio backup tests

* Sort code

* Remove catching BackupError in async_receive_backup
This commit is contained in:
Erik Montnemery 2025-01-28 14:44:09 +01:00 committed by GitHub
parent 9a4b73a834
commit abb58ec785
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
7 changed files with 219 additions and 36 deletions

View File

@ -10,18 +10,20 @@ from typing import Any, Protocol
from propcache.api import cached_property from propcache.api import cached_property
from homeassistant.core import HomeAssistant, callback from homeassistant.core import HomeAssistant, callback
from homeassistant.exceptions import HomeAssistantError
from .models import AgentBackup from .models import AgentBackup, BackupError
class BackupAgentError(HomeAssistantError): class BackupAgentError(BackupError):
"""Base class for backup agent errors.""" """Base class for backup agent errors."""
error_code = "backup_agent_error"
class BackupAgentUnreachableError(BackupAgentError): class BackupAgentUnreachableError(BackupAgentError):
"""Raised when the agent can't reach its API.""" """Raised when the agent can't reach its API."""
error_code = "backup_agent_unreachable"
_message = "The backup agent is unreachable." _message = "The backup agent is unreachable."

View File

@ -22,7 +22,6 @@ from securetar import SecureTarFile, atomic_contents_add
from homeassistant.backup_restore import RESTORE_BACKUP_FILE, password_to_key from homeassistant.backup_restore import RESTORE_BACKUP_FILE, password_to_key
from homeassistant.const import __version__ as HAVERSION from homeassistant.const import __version__ as HAVERSION
from homeassistant.core import HomeAssistant, callback from homeassistant.core import HomeAssistant, callback
from homeassistant.exceptions import HomeAssistantError
from homeassistant.helpers import ( from homeassistant.helpers import (
instance_id, instance_id,
integration_platform, integration_platform,
@ -47,7 +46,7 @@ from .const import (
EXCLUDE_FROM_BACKUP, EXCLUDE_FROM_BACKUP,
LOGGER, LOGGER,
) )
from .models import AgentBackup, BackupManagerError, Folder from .models import AgentBackup, BackupError, BackupManagerError, Folder
from .store import BackupStore from .store import BackupStore
from .util import ( from .util import (
AsyncIteratorReader, AsyncIteratorReader,
@ -171,6 +170,7 @@ class CreateBackupEvent(ManagerStateEvent):
"""Backup in progress.""" """Backup in progress."""
manager_state: BackupManagerState = BackupManagerState.CREATE_BACKUP manager_state: BackupManagerState = BackupManagerState.CREATE_BACKUP
reason: str | None
stage: CreateBackupStage | None stage: CreateBackupStage | None
state: CreateBackupState state: CreateBackupState
@ -180,6 +180,7 @@ class ReceiveBackupEvent(ManagerStateEvent):
"""Backup receive.""" """Backup receive."""
manager_state: BackupManagerState = BackupManagerState.RECEIVE_BACKUP manager_state: BackupManagerState = BackupManagerState.RECEIVE_BACKUP
reason: str | None
stage: ReceiveBackupStage | None stage: ReceiveBackupStage | None
state: ReceiveBackupState state: ReceiveBackupState
@ -189,6 +190,7 @@ class RestoreBackupEvent(ManagerStateEvent):
"""Backup restore.""" """Backup restore."""
manager_state: BackupManagerState = BackupManagerState.RESTORE_BACKUP manager_state: BackupManagerState = BackupManagerState.RESTORE_BACKUP
reason: str | None
stage: RestoreBackupStage | None stage: RestoreBackupStage | None
state: RestoreBackupState state: RestoreBackupState
@ -250,19 +252,23 @@ class BackupReaderWriter(abc.ABC):
"""Restore a backup.""" """Restore a backup."""
class BackupReaderWriterError(HomeAssistantError): class BackupReaderWriterError(BackupError):
"""Backup reader/writer error.""" """Backup reader/writer error."""
error_code = "backup_reader_writer_error"
class IncorrectPasswordError(BackupReaderWriterError): class IncorrectPasswordError(BackupReaderWriterError):
"""Raised when the password is incorrect.""" """Raised when the password is incorrect."""
error_code = "password_incorrect"
_message = "The password provided is incorrect." _message = "The password provided is incorrect."
class DecryptOnDowloadNotSupported(BackupManagerError): class DecryptOnDowloadNotSupported(BackupManagerError):
"""Raised when on-the-fly decryption is not supported.""" """Raised when on-the-fly decryption is not supported."""
error_code = "decrypt_on_download_not_supported"
_message = "On-the-fly decryption is not supported for this backup." _message = "On-the-fly decryption is not supported for this backup."
@ -619,18 +625,30 @@ class BackupManager:
if self.state is not BackupManagerState.IDLE: if self.state is not BackupManagerState.IDLE:
raise BackupManagerError(f"Backup manager busy: {self.state}") raise BackupManagerError(f"Backup manager busy: {self.state}")
self.async_on_backup_event( self.async_on_backup_event(
ReceiveBackupEvent(stage=None, state=ReceiveBackupState.IN_PROGRESS) ReceiveBackupEvent(
reason=None,
stage=None,
state=ReceiveBackupState.IN_PROGRESS,
)
) )
try: try:
await self._async_receive_backup(agent_ids=agent_ids, contents=contents) await self._async_receive_backup(agent_ids=agent_ids, contents=contents)
except Exception: except Exception:
self.async_on_backup_event( self.async_on_backup_event(
ReceiveBackupEvent(stage=None, state=ReceiveBackupState.FAILED) ReceiveBackupEvent(
reason="unknown_error",
stage=None,
state=ReceiveBackupState.FAILED,
)
) )
raise raise
else: else:
self.async_on_backup_event( self.async_on_backup_event(
ReceiveBackupEvent(stage=None, state=ReceiveBackupState.COMPLETED) ReceiveBackupEvent(
reason=None,
stage=None,
state=ReceiveBackupState.COMPLETED,
)
) )
finally: finally:
self.async_on_backup_event(IdleEvent()) self.async_on_backup_event(IdleEvent())
@ -645,6 +663,7 @@ class BackupManager:
contents.chunk_size = BUF_SIZE contents.chunk_size = BUF_SIZE
self.async_on_backup_event( self.async_on_backup_event(
ReceiveBackupEvent( ReceiveBackupEvent(
reason=None,
stage=ReceiveBackupStage.RECEIVE_FILE, stage=ReceiveBackupStage.RECEIVE_FILE,
state=ReceiveBackupState.IN_PROGRESS, state=ReceiveBackupState.IN_PROGRESS,
) )
@ -656,6 +675,7 @@ class BackupManager:
) )
self.async_on_backup_event( self.async_on_backup_event(
ReceiveBackupEvent( ReceiveBackupEvent(
reason=None,
stage=ReceiveBackupStage.UPLOAD_TO_AGENTS, stage=ReceiveBackupStage.UPLOAD_TO_AGENTS,
state=ReceiveBackupState.IN_PROGRESS, state=ReceiveBackupState.IN_PROGRESS,
) )
@ -739,7 +759,11 @@ class BackupManager:
self.store.save() self.store.save()
self.async_on_backup_event( self.async_on_backup_event(
CreateBackupEvent(stage=None, state=CreateBackupState.IN_PROGRESS) CreateBackupEvent(
reason=None,
stage=None,
state=CreateBackupState.IN_PROGRESS,
)
) )
try: try:
return await self._async_create_backup( return await self._async_create_backup(
@ -755,9 +779,14 @@ class BackupManager:
raise_task_error=raise_task_error, raise_task_error=raise_task_error,
with_automatic_settings=with_automatic_settings, with_automatic_settings=with_automatic_settings,
) )
except Exception: except Exception as err:
reason = err.error_code if isinstance(err, BackupError) else "unknown_error"
self.async_on_backup_event( self.async_on_backup_event(
CreateBackupEvent(stage=None, state=CreateBackupState.FAILED) CreateBackupEvent(
reason=reason,
stage=None,
state=CreateBackupState.FAILED,
)
) )
self.async_on_backup_event(IdleEvent()) self.async_on_backup_event(IdleEvent())
if with_automatic_settings: if with_automatic_settings:
@ -861,6 +890,7 @@ class BackupManager:
) )
self.async_on_backup_event( self.async_on_backup_event(
CreateBackupEvent( CreateBackupEvent(
reason=None,
stage=CreateBackupStage.UPLOAD_TO_AGENTS, stage=CreateBackupStage.UPLOAD_TO_AGENTS,
state=CreateBackupState.IN_PROGRESS, state=CreateBackupState.IN_PROGRESS,
) )
@ -891,12 +921,20 @@ class BackupManager:
finally: finally:
self._backup_task = None self._backup_task = None
self._backup_finish_task = None self._backup_finish_task = None
if backup_success:
self.async_on_backup_event( self.async_on_backup_event(
CreateBackupEvent( CreateBackupEvent(
reason=None,
stage=None, stage=None,
state=CreateBackupState.COMPLETED state=CreateBackupState.COMPLETED,
if backup_success )
else CreateBackupState.FAILED, )
else:
self.async_on_backup_event(
CreateBackupEvent(
reason="upload_failed",
stage=None,
state=CreateBackupState.FAILED,
) )
) )
self.async_on_backup_event(IdleEvent()) self.async_on_backup_event(IdleEvent())
@ -917,7 +955,11 @@ class BackupManager:
raise BackupManagerError(f"Backup manager busy: {self.state}") raise BackupManagerError(f"Backup manager busy: {self.state}")
self.async_on_backup_event( self.async_on_backup_event(
RestoreBackupEvent(stage=None, state=RestoreBackupState.IN_PROGRESS) RestoreBackupEvent(
reason=None,
stage=None,
state=RestoreBackupState.IN_PROGRESS,
)
) )
try: try:
await self._async_restore_backup( await self._async_restore_backup(
@ -930,11 +972,28 @@ class BackupManager:
restore_homeassistant=restore_homeassistant, restore_homeassistant=restore_homeassistant,
) )
self.async_on_backup_event( self.async_on_backup_event(
RestoreBackupEvent(stage=None, state=RestoreBackupState.COMPLETED) RestoreBackupEvent(
reason=None,
stage=None,
state=RestoreBackupState.COMPLETED,
) )
)
except BackupError as err:
self.async_on_backup_event(
RestoreBackupEvent(
reason=err.error_code,
stage=None,
state=RestoreBackupState.FAILED,
)
)
raise
except Exception: except Exception:
self.async_on_backup_event( self.async_on_backup_event(
RestoreBackupEvent(stage=None, state=RestoreBackupState.FAILED) RestoreBackupEvent(
reason="unknown_error",
stage=None,
state=RestoreBackupState.FAILED,
)
) )
raise raise
finally: finally:
@ -1210,6 +1269,7 @@ class CoreBackupReaderWriter(BackupReaderWriter):
on_progress( on_progress(
CreateBackupEvent( CreateBackupEvent(
reason=None,
stage=CreateBackupStage.HOME_ASSISTANT, stage=CreateBackupStage.HOME_ASSISTANT,
state=CreateBackupState.IN_PROGRESS, state=CreateBackupState.IN_PROGRESS,
) )
@ -1469,7 +1529,11 @@ class CoreBackupReaderWriter(BackupReaderWriter):
await self._hass.async_add_executor_job(_write_restore_file) await self._hass.async_add_executor_job(_write_restore_file)
on_progress( on_progress(
RestoreBackupEvent(stage=None, state=RestoreBackupState.CORE_RESTART) RestoreBackupEvent(
reason=None,
stage=None,
state=RestoreBackupState.CORE_RESTART,
)
) )
await self._hass.services.async_call("homeassistant", "restart", blocking=True) await self._hass.services.async_call("homeassistant", "restart", blocking=True)

View File

@ -71,5 +71,13 @@ class AgentBackup:
) )
class BackupManagerError(HomeAssistantError): class BackupError(HomeAssistantError):
"""Base class for backup errors."""
error_code = "unknown"
class BackupManagerError(BackupError):
"""Backup manager error.""" """Backup manager error."""
error_code = "backup_manager_error"

View File

@ -3383,6 +3383,7 @@
dict({ dict({
'event': dict({ 'event': dict({
'manager_state': 'create_backup', 'manager_state': 'create_backup',
'reason': None,
'stage': None, 'stage': None,
'state': 'in_progress', 'state': 'in_progress',
}), }),
@ -3404,6 +3405,7 @@
dict({ dict({
'event': dict({ 'event': dict({
'manager_state': 'create_backup', 'manager_state': 'create_backup',
'reason': None,
'stage': 'home_assistant', 'stage': 'home_assistant',
'state': 'in_progress', 'state': 'in_progress',
}), }),
@ -3415,6 +3417,7 @@
dict({ dict({
'event': dict({ 'event': dict({
'manager_state': 'create_backup', 'manager_state': 'create_backup',
'reason': None,
'stage': 'upload_to_agents', 'stage': 'upload_to_agents',
'state': 'in_progress', 'state': 'in_progress',
}), }),
@ -3426,6 +3429,7 @@
dict({ dict({
'event': dict({ 'event': dict({
'manager_state': 'create_backup', 'manager_state': 'create_backup',
'reason': None,
'stage': None, 'stage': None,
'state': 'completed', 'state': 'completed',
}), }),
@ -3454,6 +3458,7 @@
dict({ dict({
'event': dict({ 'event': dict({
'manager_state': 'create_backup', 'manager_state': 'create_backup',
'reason': None,
'stage': None, 'stage': None,
'state': 'in_progress', 'state': 'in_progress',
}), }),
@ -3475,6 +3480,7 @@
dict({ dict({
'event': dict({ 'event': dict({
'manager_state': 'create_backup', 'manager_state': 'create_backup',
'reason': None,
'stage': 'home_assistant', 'stage': 'home_assistant',
'state': 'in_progress', 'state': 'in_progress',
}), }),
@ -3486,6 +3492,7 @@
dict({ dict({
'event': dict({ 'event': dict({
'manager_state': 'create_backup', 'manager_state': 'create_backup',
'reason': None,
'stage': 'upload_to_agents', 'stage': 'upload_to_agents',
'state': 'in_progress', 'state': 'in_progress',
}), }),
@ -3497,6 +3504,7 @@
dict({ dict({
'event': dict({ 'event': dict({
'manager_state': 'create_backup', 'manager_state': 'create_backup',
'reason': None,
'stage': None, 'stage': None,
'state': 'completed', 'state': 'completed',
}), }),
@ -3525,6 +3533,7 @@
dict({ dict({
'event': dict({ 'event': dict({
'manager_state': 'create_backup', 'manager_state': 'create_backup',
'reason': None,
'stage': None, 'stage': None,
'state': 'in_progress', 'state': 'in_progress',
}), }),
@ -3546,6 +3555,7 @@
dict({ dict({
'event': dict({ 'event': dict({
'manager_state': 'create_backup', 'manager_state': 'create_backup',
'reason': None,
'stage': 'home_assistant', 'stage': 'home_assistant',
'state': 'in_progress', 'state': 'in_progress',
}), }),
@ -3557,6 +3567,7 @@
dict({ dict({
'event': dict({ 'event': dict({
'manager_state': 'create_backup', 'manager_state': 'create_backup',
'reason': None,
'stage': 'upload_to_agents', 'stage': 'upload_to_agents',
'state': 'in_progress', 'state': 'in_progress',
}), }),
@ -3568,6 +3579,7 @@
dict({ dict({
'event': dict({ 'event': dict({
'manager_state': 'create_backup', 'manager_state': 'create_backup',
'reason': None,
'stage': None, 'stage': None,
'state': 'completed', 'state': 'completed',
}), }),
@ -3912,6 +3924,7 @@
dict({ dict({
'event': dict({ 'event': dict({
'manager_state': 'create_backup', 'manager_state': 'create_backup',
'reason': None,
'stage': None, 'stage': None,
'state': 'in_progress', 'state': 'in_progress',
}), }),

View File

@ -419,6 +419,7 @@ async def test_initiate_backup(
result = await ws_client.receive_json() result = await ws_client.receive_json()
assert result["event"] == { assert result["event"] == {
"manager_state": BackupManagerState.CREATE_BACKUP, "manager_state": BackupManagerState.CREATE_BACKUP,
"reason": None,
"stage": None, "stage": None,
"state": CreateBackupState.IN_PROGRESS, "state": CreateBackupState.IN_PROGRESS,
} }
@ -433,6 +434,7 @@ async def test_initiate_backup(
result = await ws_client.receive_json() result = await ws_client.receive_json()
assert result["event"] == { assert result["event"] == {
"manager_state": BackupManagerState.CREATE_BACKUP, "manager_state": BackupManagerState.CREATE_BACKUP,
"reason": None,
"stage": CreateBackupStage.HOME_ASSISTANT, "stage": CreateBackupStage.HOME_ASSISTANT,
"state": CreateBackupState.IN_PROGRESS, "state": CreateBackupState.IN_PROGRESS,
} }
@ -440,6 +442,7 @@ async def test_initiate_backup(
result = await ws_client.receive_json() result = await ws_client.receive_json()
assert result["event"] == { assert result["event"] == {
"manager_state": BackupManagerState.CREATE_BACKUP, "manager_state": BackupManagerState.CREATE_BACKUP,
"reason": None,
"stage": CreateBackupStage.UPLOAD_TO_AGENTS, "stage": CreateBackupStage.UPLOAD_TO_AGENTS,
"state": CreateBackupState.IN_PROGRESS, "state": CreateBackupState.IN_PROGRESS,
} }
@ -447,6 +450,7 @@ async def test_initiate_backup(
result = await ws_client.receive_json() result = await ws_client.receive_json()
assert result["event"] == { assert result["event"] == {
"manager_state": BackupManagerState.CREATE_BACKUP, "manager_state": BackupManagerState.CREATE_BACKUP,
"reason": None,
"stage": None, "stage": None,
"state": CreateBackupState.COMPLETED, "state": CreateBackupState.COMPLETED,
} }
@ -670,6 +674,7 @@ async def test_initiate_backup_with_agent_error(
assert result["event"] == { assert result["event"] == {
"manager_state": BackupManagerState.CREATE_BACKUP, "manager_state": BackupManagerState.CREATE_BACKUP,
"stage": None, "stage": None,
"reason": None,
"state": CreateBackupState.IN_PROGRESS, "state": CreateBackupState.IN_PROGRESS,
} }
result = await ws_client.receive_json() result = await ws_client.receive_json()
@ -683,6 +688,7 @@ async def test_initiate_backup_with_agent_error(
result = await ws_client.receive_json() result = await ws_client.receive_json()
assert result["event"] == { assert result["event"] == {
"manager_state": BackupManagerState.CREATE_BACKUP, "manager_state": BackupManagerState.CREATE_BACKUP,
"reason": None,
"stage": CreateBackupStage.HOME_ASSISTANT, "stage": CreateBackupStage.HOME_ASSISTANT,
"state": CreateBackupState.IN_PROGRESS, "state": CreateBackupState.IN_PROGRESS,
} }
@ -690,6 +696,7 @@ async def test_initiate_backup_with_agent_error(
result = await ws_client.receive_json() result = await ws_client.receive_json()
assert result["event"] == { assert result["event"] == {
"manager_state": BackupManagerState.CREATE_BACKUP, "manager_state": BackupManagerState.CREATE_BACKUP,
"reason": None,
"stage": CreateBackupStage.UPLOAD_TO_AGENTS, "stage": CreateBackupStage.UPLOAD_TO_AGENTS,
"state": CreateBackupState.IN_PROGRESS, "state": CreateBackupState.IN_PROGRESS,
} }
@ -697,6 +704,7 @@ async def test_initiate_backup_with_agent_error(
result = await ws_client.receive_json() result = await ws_client.receive_json()
assert result["event"] == { assert result["event"] == {
"manager_state": BackupManagerState.CREATE_BACKUP, "manager_state": BackupManagerState.CREATE_BACKUP,
"reason": "upload_failed",
"stage": None, "stage": None,
"state": CreateBackupState.FAILED, "state": CreateBackupState.FAILED,
} }
@ -1025,6 +1033,7 @@ async def test_initiate_backup_non_agent_upload_error(
result = await ws_client.receive_json() result = await ws_client.receive_json()
assert result["event"] == { assert result["event"] == {
"manager_state": BackupManagerState.CREATE_BACKUP, "manager_state": BackupManagerState.CREATE_BACKUP,
"reason": None,
"stage": None, "stage": None,
"state": CreateBackupState.IN_PROGRESS, "state": CreateBackupState.IN_PROGRESS,
} }
@ -1039,6 +1048,7 @@ async def test_initiate_backup_non_agent_upload_error(
result = await ws_client.receive_json() result = await ws_client.receive_json()
assert result["event"] == { assert result["event"] == {
"manager_state": BackupManagerState.CREATE_BACKUP, "manager_state": BackupManagerState.CREATE_BACKUP,
"reason": None,
"stage": CreateBackupStage.HOME_ASSISTANT, "stage": CreateBackupStage.HOME_ASSISTANT,
"state": CreateBackupState.IN_PROGRESS, "state": CreateBackupState.IN_PROGRESS,
} }
@ -1046,6 +1056,7 @@ async def test_initiate_backup_non_agent_upload_error(
result = await ws_client.receive_json() result = await ws_client.receive_json()
assert result["event"] == { assert result["event"] == {
"manager_state": BackupManagerState.CREATE_BACKUP, "manager_state": BackupManagerState.CREATE_BACKUP,
"reason": None,
"stage": CreateBackupStage.UPLOAD_TO_AGENTS, "stage": CreateBackupStage.UPLOAD_TO_AGENTS,
"state": CreateBackupState.IN_PROGRESS, "state": CreateBackupState.IN_PROGRESS,
} }
@ -1053,6 +1064,7 @@ async def test_initiate_backup_non_agent_upload_error(
result = await ws_client.receive_json() result = await ws_client.receive_json()
assert result["event"] == { assert result["event"] == {
"manager_state": BackupManagerState.CREATE_BACKUP, "manager_state": BackupManagerState.CREATE_BACKUP,
"reason": "upload_failed",
"stage": None, "stage": None,
"state": CreateBackupState.FAILED, "state": CreateBackupState.FAILED,
} }
@ -1131,6 +1143,7 @@ async def test_initiate_backup_with_task_error(
result = await ws_client.receive_json() result = await ws_client.receive_json()
assert result["event"] == { assert result["event"] == {
"manager_state": BackupManagerState.CREATE_BACKUP, "manager_state": BackupManagerState.CREATE_BACKUP,
"reason": None,
"stage": None, "stage": None,
"state": CreateBackupState.IN_PROGRESS, "state": CreateBackupState.IN_PROGRESS,
} }
@ -1138,6 +1151,7 @@ async def test_initiate_backup_with_task_error(
result = await ws_client.receive_json() result = await ws_client.receive_json()
assert result["event"] == { assert result["event"] == {
"manager_state": BackupManagerState.CREATE_BACKUP, "manager_state": BackupManagerState.CREATE_BACKUP,
"reason": "upload_failed",
"stage": None, "stage": None,
"state": CreateBackupState.FAILED, "state": CreateBackupState.FAILED,
} }
@ -1245,6 +1259,7 @@ async def test_initiate_backup_file_error(
result = await ws_client.receive_json() result = await ws_client.receive_json()
assert result["event"] == { assert result["event"] == {
"manager_state": BackupManagerState.CREATE_BACKUP, "manager_state": BackupManagerState.CREATE_BACKUP,
"reason": None,
"stage": None, "stage": None,
"state": CreateBackupState.IN_PROGRESS, "state": CreateBackupState.IN_PROGRESS,
} }
@ -1259,6 +1274,7 @@ async def test_initiate_backup_file_error(
result = await ws_client.receive_json() result = await ws_client.receive_json()
assert result["event"] == { assert result["event"] == {
"manager_state": BackupManagerState.CREATE_BACKUP, "manager_state": BackupManagerState.CREATE_BACKUP,
"reason": None,
"stage": CreateBackupStage.HOME_ASSISTANT, "stage": CreateBackupStage.HOME_ASSISTANT,
"state": CreateBackupState.IN_PROGRESS, "state": CreateBackupState.IN_PROGRESS,
} }
@ -1266,6 +1282,7 @@ async def test_initiate_backup_file_error(
result = await ws_client.receive_json() result = await ws_client.receive_json()
assert result["event"] == { assert result["event"] == {
"manager_state": BackupManagerState.CREATE_BACKUP, "manager_state": BackupManagerState.CREATE_BACKUP,
"reason": None,
"stage": CreateBackupStage.UPLOAD_TO_AGENTS, "stage": CreateBackupStage.UPLOAD_TO_AGENTS,
"state": CreateBackupState.IN_PROGRESS, "state": CreateBackupState.IN_PROGRESS,
} }
@ -1273,6 +1290,7 @@ async def test_initiate_backup_file_error(
result = await ws_client.receive_json() result = await ws_client.receive_json()
assert result["event"] == { assert result["event"] == {
"manager_state": BackupManagerState.CREATE_BACKUP, "manager_state": BackupManagerState.CREATE_BACKUP,
"reason": "upload_failed",
"stage": None, "stage": None,
"state": CreateBackupState.FAILED, "state": CreateBackupState.FAILED,
} }
@ -1559,6 +1577,7 @@ async def test_receive_backup_busy_manager(
result = await ws_client.receive_json() result = await ws_client.receive_json()
assert result["event"] == { assert result["event"] == {
"manager_state": "create_backup", "manager_state": "create_backup",
"reason": None,
"stage": None, "stage": None,
"state": "in_progress", "state": "in_progress",
} }
@ -1752,6 +1771,7 @@ async def test_receive_backup_agent_error(
result = await ws_client.receive_json() result = await ws_client.receive_json()
assert result["event"] == { assert result["event"] == {
"manager_state": BackupManagerState.RECEIVE_BACKUP, "manager_state": BackupManagerState.RECEIVE_BACKUP,
"reason": None,
"stage": None, "stage": None,
"state": ReceiveBackupState.IN_PROGRESS, "state": ReceiveBackupState.IN_PROGRESS,
} }
@ -1759,6 +1779,7 @@ async def test_receive_backup_agent_error(
result = await ws_client.receive_json() result = await ws_client.receive_json()
assert result["event"] == { assert result["event"] == {
"manager_state": BackupManagerState.RECEIVE_BACKUP, "manager_state": BackupManagerState.RECEIVE_BACKUP,
"reason": None,
"stage": ReceiveBackupStage.RECEIVE_FILE, "stage": ReceiveBackupStage.RECEIVE_FILE,
"state": ReceiveBackupState.IN_PROGRESS, "state": ReceiveBackupState.IN_PROGRESS,
} }
@ -1766,6 +1787,7 @@ async def test_receive_backup_agent_error(
result = await ws_client.receive_json() result = await ws_client.receive_json()
assert result["event"] == { assert result["event"] == {
"manager_state": BackupManagerState.RECEIVE_BACKUP, "manager_state": BackupManagerState.RECEIVE_BACKUP,
"reason": None,
"stage": ReceiveBackupStage.UPLOAD_TO_AGENTS, "stage": ReceiveBackupStage.UPLOAD_TO_AGENTS,
"state": ReceiveBackupState.IN_PROGRESS, "state": ReceiveBackupState.IN_PROGRESS,
} }
@ -1773,6 +1795,7 @@ async def test_receive_backup_agent_error(
result = await ws_client.receive_json() result = await ws_client.receive_json()
assert result["event"] == { assert result["event"] == {
"manager_state": BackupManagerState.RECEIVE_BACKUP, "manager_state": BackupManagerState.RECEIVE_BACKUP,
"reason": None,
"stage": None, "stage": None,
"state": ReceiveBackupState.COMPLETED, "state": ReceiveBackupState.COMPLETED,
} }
@ -1885,6 +1908,7 @@ async def test_receive_backup_non_agent_upload_error(
result = await ws_client.receive_json() result = await ws_client.receive_json()
assert result["event"] == { assert result["event"] == {
"manager_state": BackupManagerState.RECEIVE_BACKUP, "manager_state": BackupManagerState.RECEIVE_BACKUP,
"reason": None,
"stage": None, "stage": None,
"state": ReceiveBackupState.IN_PROGRESS, "state": ReceiveBackupState.IN_PROGRESS,
} }
@ -1892,6 +1916,7 @@ async def test_receive_backup_non_agent_upload_error(
result = await ws_client.receive_json() result = await ws_client.receive_json()
assert result["event"] == { assert result["event"] == {
"manager_state": BackupManagerState.RECEIVE_BACKUP, "manager_state": BackupManagerState.RECEIVE_BACKUP,
"reason": None,
"stage": ReceiveBackupStage.RECEIVE_FILE, "stage": ReceiveBackupStage.RECEIVE_FILE,
"state": ReceiveBackupState.IN_PROGRESS, "state": ReceiveBackupState.IN_PROGRESS,
} }
@ -1899,6 +1924,7 @@ async def test_receive_backup_non_agent_upload_error(
result = await ws_client.receive_json() result = await ws_client.receive_json()
assert result["event"] == { assert result["event"] == {
"manager_state": BackupManagerState.RECEIVE_BACKUP, "manager_state": BackupManagerState.RECEIVE_BACKUP,
"reason": None,
"stage": ReceiveBackupStage.UPLOAD_TO_AGENTS, "stage": ReceiveBackupStage.UPLOAD_TO_AGENTS,
"state": ReceiveBackupState.IN_PROGRESS, "state": ReceiveBackupState.IN_PROGRESS,
} }
@ -2007,6 +2033,7 @@ async def test_receive_backup_file_write_error(
result = await ws_client.receive_json() result = await ws_client.receive_json()
assert result["event"] == { assert result["event"] == {
"manager_state": BackupManagerState.RECEIVE_BACKUP, "manager_state": BackupManagerState.RECEIVE_BACKUP,
"reason": None,
"stage": None, "stage": None,
"state": ReceiveBackupState.IN_PROGRESS, "state": ReceiveBackupState.IN_PROGRESS,
} }
@ -2014,6 +2041,7 @@ async def test_receive_backup_file_write_error(
result = await ws_client.receive_json() result = await ws_client.receive_json()
assert result["event"] == { assert result["event"] == {
"manager_state": BackupManagerState.RECEIVE_BACKUP, "manager_state": BackupManagerState.RECEIVE_BACKUP,
"reason": None,
"stage": ReceiveBackupStage.RECEIVE_FILE, "stage": ReceiveBackupStage.RECEIVE_FILE,
"state": ReceiveBackupState.IN_PROGRESS, "state": ReceiveBackupState.IN_PROGRESS,
} }
@ -2021,6 +2049,7 @@ async def test_receive_backup_file_write_error(
result = await ws_client.receive_json() result = await ws_client.receive_json()
assert result["event"] == { assert result["event"] == {
"manager_state": BackupManagerState.RECEIVE_BACKUP, "manager_state": BackupManagerState.RECEIVE_BACKUP,
"reason": "unknown_error",
"stage": None, "stage": None,
"state": ReceiveBackupState.FAILED, "state": ReceiveBackupState.FAILED,
} }
@ -2114,6 +2143,7 @@ async def test_receive_backup_read_tar_error(
result = await ws_client.receive_json() result = await ws_client.receive_json()
assert result["event"] == { assert result["event"] == {
"manager_state": BackupManagerState.RECEIVE_BACKUP, "manager_state": BackupManagerState.RECEIVE_BACKUP,
"reason": None,
"stage": None, "stage": None,
"state": ReceiveBackupState.IN_PROGRESS, "state": ReceiveBackupState.IN_PROGRESS,
} }
@ -2121,6 +2151,7 @@ async def test_receive_backup_read_tar_error(
result = await ws_client.receive_json() result = await ws_client.receive_json()
assert result["event"] == { assert result["event"] == {
"manager_state": BackupManagerState.RECEIVE_BACKUP, "manager_state": BackupManagerState.RECEIVE_BACKUP,
"reason": None,
"stage": ReceiveBackupStage.RECEIVE_FILE, "stage": ReceiveBackupStage.RECEIVE_FILE,
"state": ReceiveBackupState.IN_PROGRESS, "state": ReceiveBackupState.IN_PROGRESS,
} }
@ -2128,6 +2159,7 @@ async def test_receive_backup_read_tar_error(
result = await ws_client.receive_json() result = await ws_client.receive_json()
assert result["event"] == { assert result["event"] == {
"manager_state": BackupManagerState.RECEIVE_BACKUP, "manager_state": BackupManagerState.RECEIVE_BACKUP,
"reason": "unknown_error",
"stage": None, "stage": None,
"state": ReceiveBackupState.FAILED, "state": ReceiveBackupState.FAILED,
} }
@ -2151,6 +2183,7 @@ async def test_receive_backup_read_tar_error(
"unlink_call_count", "unlink_call_count",
"unlink_exception", "unlink_exception",
"final_state", "final_state",
"final_state_reason",
"response_status", "response_status",
), ),
[ [
@ -2164,6 +2197,7 @@ async def test_receive_backup_read_tar_error(
1, 1,
None, None,
ReceiveBackupState.COMPLETED, ReceiveBackupState.COMPLETED,
None,
201, 201,
), ),
( (
@ -2176,6 +2210,7 @@ async def test_receive_backup_read_tar_error(
1, 1,
None, None,
ReceiveBackupState.COMPLETED, ReceiveBackupState.COMPLETED,
None,
201, 201,
), ),
( (
@ -2188,6 +2223,7 @@ async def test_receive_backup_read_tar_error(
1, 1,
None, None,
ReceiveBackupState.COMPLETED, ReceiveBackupState.COMPLETED,
None,
201, 201,
), ),
( (
@ -2200,6 +2236,7 @@ async def test_receive_backup_read_tar_error(
1, 1,
OSError("Boom!"), OSError("Boom!"),
ReceiveBackupState.FAILED, ReceiveBackupState.FAILED,
"unknown_error",
500, 500,
), ),
], ],
@ -2218,6 +2255,7 @@ async def test_receive_backup_file_read_error(
unlink_call_count: int, unlink_call_count: int,
unlink_exception: Exception | None, unlink_exception: Exception | None,
final_state: ReceiveBackupState, final_state: ReceiveBackupState,
final_state_reason: str | None,
response_status: int, response_status: int,
) -> None: ) -> None:
"""Test file read error during backup receive.""" """Test file read error during backup receive."""
@ -2288,6 +2326,7 @@ async def test_receive_backup_file_read_error(
result = await ws_client.receive_json() result = await ws_client.receive_json()
assert result["event"] == { assert result["event"] == {
"manager_state": BackupManagerState.RECEIVE_BACKUP, "manager_state": BackupManagerState.RECEIVE_BACKUP,
"reason": None,
"stage": None, "stage": None,
"state": ReceiveBackupState.IN_PROGRESS, "state": ReceiveBackupState.IN_PROGRESS,
} }
@ -2295,6 +2334,7 @@ async def test_receive_backup_file_read_error(
result = await ws_client.receive_json() result = await ws_client.receive_json()
assert result["event"] == { assert result["event"] == {
"manager_state": BackupManagerState.RECEIVE_BACKUP, "manager_state": BackupManagerState.RECEIVE_BACKUP,
"reason": None,
"stage": ReceiveBackupStage.RECEIVE_FILE, "stage": ReceiveBackupStage.RECEIVE_FILE,
"state": ReceiveBackupState.IN_PROGRESS, "state": ReceiveBackupState.IN_PROGRESS,
} }
@ -2302,6 +2342,7 @@ async def test_receive_backup_file_read_error(
result = await ws_client.receive_json() result = await ws_client.receive_json()
assert result["event"] == { assert result["event"] == {
"manager_state": BackupManagerState.RECEIVE_BACKUP, "manager_state": BackupManagerState.RECEIVE_BACKUP,
"reason": None,
"stage": ReceiveBackupStage.UPLOAD_TO_AGENTS, "stage": ReceiveBackupStage.UPLOAD_TO_AGENTS,
"state": ReceiveBackupState.IN_PROGRESS, "state": ReceiveBackupState.IN_PROGRESS,
} }
@ -2309,6 +2350,7 @@ async def test_receive_backup_file_read_error(
result = await ws_client.receive_json() result = await ws_client.receive_json()
assert result["event"] == { assert result["event"] == {
"manager_state": BackupManagerState.RECEIVE_BACKUP, "manager_state": BackupManagerState.RECEIVE_BACKUP,
"reason": final_state_reason,
"stage": None, "stage": None,
"state": final_state, "state": final_state,
} }
@ -2394,6 +2436,7 @@ async def test_restore_backup(
result = await ws_client.receive_json() result = await ws_client.receive_json()
assert result["event"] == { assert result["event"] == {
"manager_state": BackupManagerState.RESTORE_BACKUP, "manager_state": BackupManagerState.RESTORE_BACKUP,
"reason": None,
"stage": None, "stage": None,
"state": RestoreBackupState.IN_PROGRESS, "state": RestoreBackupState.IN_PROGRESS,
} }
@ -2401,6 +2444,7 @@ async def test_restore_backup(
result = await ws_client.receive_json() result = await ws_client.receive_json()
assert result["event"] == { assert result["event"] == {
"manager_state": BackupManagerState.RESTORE_BACKUP, "manager_state": BackupManagerState.RESTORE_BACKUP,
"reason": None,
"stage": None, "stage": None,
"state": RestoreBackupState.CORE_RESTART, "state": RestoreBackupState.CORE_RESTART,
} }
@ -2410,6 +2454,7 @@ async def test_restore_backup(
result = await ws_client.receive_json() result = await ws_client.receive_json()
assert result["event"] == { assert result["event"] == {
"manager_state": BackupManagerState.RESTORE_BACKUP, "manager_state": BackupManagerState.RESTORE_BACKUP,
"reason": None,
"stage": None, "stage": None,
"state": RestoreBackupState.COMPLETED, "state": RestoreBackupState.COMPLETED,
} }
@ -2497,6 +2542,7 @@ async def test_restore_backup_wrong_password(
result = await ws_client.receive_json() result = await ws_client.receive_json()
assert result["event"] == { assert result["event"] == {
"manager_state": BackupManagerState.RESTORE_BACKUP, "manager_state": BackupManagerState.RESTORE_BACKUP,
"reason": None,
"stage": None, "stage": None,
"state": RestoreBackupState.IN_PROGRESS, "state": RestoreBackupState.IN_PROGRESS,
} }
@ -2504,6 +2550,7 @@ async def test_restore_backup_wrong_password(
result = await ws_client.receive_json() result = await ws_client.receive_json()
assert result["event"] == { assert result["event"] == {
"manager_state": BackupManagerState.RESTORE_BACKUP, "manager_state": BackupManagerState.RESTORE_BACKUP,
"reason": "password_incorrect",
"stage": None, "stage": None,
"state": RestoreBackupState.FAILED, "state": RestoreBackupState.FAILED,
} }
@ -2523,23 +2570,27 @@ async def test_restore_backup_wrong_password(
@pytest.mark.usefixtures("path_glob") @pytest.mark.usefixtures("path_glob")
@pytest.mark.parametrize( @pytest.mark.parametrize(
("parameters", "expected_error"), ("parameters", "expected_error", "expected_reason"),
[ [
( (
{"backup_id": TEST_BACKUP_DEF456.backup_id}, {"backup_id": TEST_BACKUP_DEF456.backup_id},
f"Backup def456 not found in agent {LOCAL_AGENT_ID}", f"Backup def456 not found in agent {LOCAL_AGENT_ID}",
"backup_manager_error",
), ),
( (
{"restore_addons": ["blah"]}, {"restore_addons": ["blah"]},
"Addons and folders are not supported in core restore", "Addons and folders are not supported in core restore",
"backup_reader_writer_error",
), ),
( (
{"restore_folders": [Folder.ADDONS]}, {"restore_folders": [Folder.ADDONS]},
"Addons and folders are not supported in core restore", "Addons and folders are not supported in core restore",
"backup_reader_writer_error",
), ),
( (
{"restore_database": False, "restore_homeassistant": False}, {"restore_database": False, "restore_homeassistant": False},
"Home Assistant or database must be included in restore", "Home Assistant or database must be included in restore",
"backup_reader_writer_error",
), ),
], ],
) )
@ -2548,6 +2599,7 @@ async def test_restore_backup_wrong_parameters(
hass_ws_client: WebSocketGenerator, hass_ws_client: WebSocketGenerator,
parameters: dict[str, Any], parameters: dict[str, Any],
expected_error: str, expected_error: str,
expected_reason: str,
) -> None: ) -> None:
"""Test restore backup wrong parameters.""" """Test restore backup wrong parameters."""
await async_setup_component(hass, DOMAIN, {}) await async_setup_component(hass, DOMAIN, {})
@ -2584,6 +2636,7 @@ async def test_restore_backup_wrong_parameters(
result = await ws_client.receive_json() result = await ws_client.receive_json()
assert result["event"] == { assert result["event"] == {
"manager_state": BackupManagerState.RESTORE_BACKUP, "manager_state": BackupManagerState.RESTORE_BACKUP,
"reason": None,
"stage": None, "stage": None,
"state": RestoreBackupState.IN_PROGRESS, "state": RestoreBackupState.IN_PROGRESS,
} }
@ -2591,6 +2644,7 @@ async def test_restore_backup_wrong_parameters(
result = await ws_client.receive_json() result = await ws_client.receive_json()
assert result["event"] == { assert result["event"] == {
"manager_state": BackupManagerState.RESTORE_BACKUP, "manager_state": BackupManagerState.RESTORE_BACKUP,
"reason": expected_reason,
"stage": None, "stage": None,
"state": RestoreBackupState.FAILED, "state": RestoreBackupState.FAILED,
} }
@ -2640,10 +2694,20 @@ async def test_restore_backup_when_busy(
@pytest.mark.usefixtures("mock_backup_generation") @pytest.mark.usefixtures("mock_backup_generation")
@pytest.mark.parametrize( @pytest.mark.parametrize(
("exception", "error_code", "error_message"), ("exception", "error_code", "error_message", "expected_reason"),
[ [
(BackupAgentError("Boom!"), "home_assistant_error", "Boom!"), (
(Exception("Boom!"), "unknown_error", "Unknown error"), BackupAgentError("Boom!"),
"home_assistant_error",
"Boom!",
"backup_agent_error",
),
(
Exception("Boom!"),
"unknown_error",
"Unknown error",
"unknown_error",
),
], ],
) )
async def test_restore_backup_agent_error( async def test_restore_backup_agent_error(
@ -2652,6 +2716,7 @@ async def test_restore_backup_agent_error(
exception: Exception, exception: Exception,
error_code: str, error_code: str,
error_message: str, error_message: str,
expected_reason: str,
) -> None: ) -> None:
"""Test restore backup with agent error.""" """Test restore backup with agent error."""
remote_agent = BackupAgentTest("remote", backups=[TEST_BACKUP_ABC123]) remote_agent = BackupAgentTest("remote", backups=[TEST_BACKUP_ABC123])
@ -2694,6 +2759,7 @@ async def test_restore_backup_agent_error(
result = await ws_client.receive_json() result = await ws_client.receive_json()
assert result["event"] == { assert result["event"] == {
"manager_state": BackupManagerState.RESTORE_BACKUP, "manager_state": BackupManagerState.RESTORE_BACKUP,
"reason": None,
"stage": None, "stage": None,
"state": RestoreBackupState.IN_PROGRESS, "state": RestoreBackupState.IN_PROGRESS,
} }
@ -2701,6 +2767,7 @@ async def test_restore_backup_agent_error(
result = await ws_client.receive_json() result = await ws_client.receive_json()
assert result["event"] == { assert result["event"] == {
"manager_state": BackupManagerState.RESTORE_BACKUP, "manager_state": BackupManagerState.RESTORE_BACKUP,
"reason": expected_reason,
"stage": None, "stage": None,
"state": RestoreBackupState.FAILED, "state": RestoreBackupState.FAILED,
} }
@ -2841,6 +2908,7 @@ async def test_restore_backup_file_error(
result = await ws_client.receive_json() result = await ws_client.receive_json()
assert result["event"] == { assert result["event"] == {
"manager_state": BackupManagerState.RESTORE_BACKUP, "manager_state": BackupManagerState.RESTORE_BACKUP,
"reason": None,
"stage": None, "stage": None,
"state": RestoreBackupState.IN_PROGRESS, "state": RestoreBackupState.IN_PROGRESS,
} }
@ -2848,6 +2916,7 @@ async def test_restore_backup_file_error(
result = await ws_client.receive_json() result = await ws_client.receive_json()
assert result["event"] == { assert result["event"] == {
"manager_state": BackupManagerState.RESTORE_BACKUP, "manager_state": BackupManagerState.RESTORE_BACKUP,
"reason": "unknown_error",
"stage": None, "stage": None,
"state": RestoreBackupState.FAILED, "state": RestoreBackupState.FAILED,
} }

View File

@ -2816,7 +2816,7 @@ async def test_subscribe_event(
assert await client.receive_json() == snapshot assert await client.receive_json() == snapshot
manager.async_on_backup_event( manager.async_on_backup_event(
CreateBackupEvent(stage=None, state=CreateBackupState.IN_PROGRESS) CreateBackupEvent(stage=None, state=CreateBackupState.IN_PROGRESS, reason=None)
) )
assert await client.receive_json() == snapshot assert await client.receive_json() == snapshot

View File

@ -753,6 +753,7 @@ async def test_reader_writer_create(
response = await client.receive_json() response = await client.receive_json()
assert response["event"] == { assert response["event"] == {
"manager_state": "create_backup", "manager_state": "create_backup",
"reason": None,
"stage": None, "stage": None,
"state": "in_progress", "state": "in_progress",
} }
@ -780,6 +781,7 @@ async def test_reader_writer_create(
response = await client.receive_json() response = await client.receive_json()
assert response["event"] == { assert response["event"] == {
"manager_state": "create_backup", "manager_state": "create_backup",
"reason": None,
"stage": "upload_to_agents", "stage": "upload_to_agents",
"state": "in_progress", "state": "in_progress",
} }
@ -787,6 +789,7 @@ async def test_reader_writer_create(
response = await client.receive_json() response = await client.receive_json()
assert response["event"] == { assert response["event"] == {
"manager_state": "create_backup", "manager_state": "create_backup",
"reason": None,
"stage": None, "stage": None,
"state": "completed", "state": "completed",
} }
@ -800,14 +803,20 @@ async def test_reader_writer_create(
@pytest.mark.usefixtures("hassio_client", "setup_integration") @pytest.mark.usefixtures("hassio_client", "setup_integration")
@pytest.mark.parametrize( @pytest.mark.parametrize(
("side_effect", "error_code", "error_message"), ("side_effect", "error_code", "error_message", "expected_reason"),
[ [
( (
SupervisorError("Boom!"), SupervisorError("Boom!"),
"home_assistant_error", "home_assistant_error",
"Error creating backup: Boom!", "Error creating backup: Boom!",
"backup_manager_error",
),
(
Exception("Boom!"),
"unknown_error",
"Unknown error",
"unknown_error",
), ),
(Exception("Boom!"), "unknown_error", "Unknown error"),
], ],
) )
async def test_reader_writer_create_partial_backup_error( async def test_reader_writer_create_partial_backup_error(
@ -817,6 +826,7 @@ async def test_reader_writer_create_partial_backup_error(
side_effect: Exception, side_effect: Exception,
error_code: str, error_code: str,
error_message: str, error_message: str,
expected_reason: str,
) -> None: ) -> None:
"""Test client partial backup error when generating a backup.""" """Test client partial backup error when generating a backup."""
client = await hass_ws_client(hass) client = await hass_ws_client(hass)
@ -834,6 +844,7 @@ async def test_reader_writer_create_partial_backup_error(
response = await client.receive_json() response = await client.receive_json()
assert response["event"] == { assert response["event"] == {
"manager_state": "create_backup", "manager_state": "create_backup",
"reason": None,
"stage": None, "stage": None,
"state": "in_progress", "state": "in_progress",
} }
@ -841,6 +852,7 @@ async def test_reader_writer_create_partial_backup_error(
response = await client.receive_json() response = await client.receive_json()
assert response["event"] == { assert response["event"] == {
"manager_state": "create_backup", "manager_state": "create_backup",
"reason": expected_reason,
"stage": None, "stage": None,
"state": "failed", "state": "failed",
} }
@ -878,6 +890,7 @@ async def test_reader_writer_create_missing_reference_error(
response = await client.receive_json() response = await client.receive_json()
assert response["event"] == { assert response["event"] == {
"manager_state": "create_backup", "manager_state": "create_backup",
"reason": None,
"stage": None, "stage": None,
"state": "in_progress", "state": "in_progress",
} }
@ -903,6 +916,7 @@ async def test_reader_writer_create_missing_reference_error(
response = await client.receive_json() response = await client.receive_json()
assert response["event"] == { assert response["event"] == {
"manager_state": "create_backup", "manager_state": "create_backup",
"reason": "upload_failed",
"stage": None, "stage": None,
"state": "failed", "state": "failed",
} }
@ -961,6 +975,7 @@ async def test_reader_writer_create_download_remove_error(
response = await client.receive_json() response = await client.receive_json()
assert response["event"] == { assert response["event"] == {
"manager_state": "create_backup", "manager_state": "create_backup",
"reason": None,
"stage": None, "stage": None,
"state": "in_progress", "state": "in_progress",
} }
@ -986,6 +1001,7 @@ async def test_reader_writer_create_download_remove_error(
response = await client.receive_json() response = await client.receive_json()
assert response["event"] == { assert response["event"] == {
"manager_state": "create_backup", "manager_state": "create_backup",
"reason": None,
"stage": "upload_to_agents", "stage": "upload_to_agents",
"state": "in_progress", "state": "in_progress",
} }
@ -993,6 +1009,7 @@ async def test_reader_writer_create_download_remove_error(
response = await client.receive_json() response = await client.receive_json()
assert response["event"] == { assert response["event"] == {
"manager_state": "create_backup", "manager_state": "create_backup",
"reason": "upload_failed",
"stage": None, "stage": None,
"state": "failed", "state": "failed",
} }
@ -1042,6 +1059,7 @@ async def test_reader_writer_create_info_error(
response = await client.receive_json() response = await client.receive_json()
assert response["event"] == { assert response["event"] == {
"manager_state": "create_backup", "manager_state": "create_backup",
"reason": None,
"stage": None, "stage": None,
"state": "in_progress", "state": "in_progress",
} }
@ -1067,6 +1085,7 @@ async def test_reader_writer_create_info_error(
response = await client.receive_json() response = await client.receive_json()
assert response["event"] == { assert response["event"] == {
"manager_state": "create_backup", "manager_state": "create_backup",
"reason": "upload_failed",
"stage": None, "stage": None,
"state": "failed", "state": "failed",
} }
@ -1114,6 +1133,7 @@ async def test_reader_writer_create_remote_backup(
response = await client.receive_json() response = await client.receive_json()
assert response["event"] == { assert response["event"] == {
"manager_state": "create_backup", "manager_state": "create_backup",
"reason": None,
"stage": None, "stage": None,
"state": "in_progress", "state": "in_progress",
} }
@ -1141,6 +1161,7 @@ async def test_reader_writer_create_remote_backup(
response = await client.receive_json() response = await client.receive_json()
assert response["event"] == { assert response["event"] == {
"manager_state": "create_backup", "manager_state": "create_backup",
"reason": None,
"stage": "upload_to_agents", "stage": "upload_to_agents",
"state": "in_progress", "state": "in_progress",
} }
@ -1148,6 +1169,7 @@ async def test_reader_writer_create_remote_backup(
response = await client.receive_json() response = await client.receive_json()
assert response["event"] == { assert response["event"] == {
"manager_state": "create_backup", "manager_state": "create_backup",
"reason": None,
"stage": None, "stage": None,
"state": "completed", "state": "completed",
} }
@ -1204,6 +1226,7 @@ async def test_reader_writer_create_wrong_parameters(
response = await client.receive_json() response = await client.receive_json()
assert response["event"] == { assert response["event"] == {
"manager_state": "create_backup", "manager_state": "create_backup",
"reason": None,
"stage": None, "stage": None,
"state": "in_progress", "state": "in_progress",
} }
@ -1211,6 +1234,7 @@ async def test_reader_writer_create_wrong_parameters(
response = await client.receive_json() response = await client.receive_json()
assert response["event"] == { assert response["event"] == {
"manager_state": "create_backup", "manager_state": "create_backup",
"reason": "unknown_error",
"stage": None, "stage": None,
"state": "failed", "state": "failed",
} }
@ -1316,6 +1340,7 @@ async def test_reader_writer_restore(
response = await client.receive_json() response = await client.receive_json()
assert response["event"] == { assert response["event"] == {
"manager_state": "restore_backup", "manager_state": "restore_backup",
"reason": None,
"stage": None, "stage": None,
"state": "in_progress", "state": "in_progress",
} }
@ -1347,6 +1372,7 @@ async def test_reader_writer_restore(
response = await client.receive_json() response = await client.receive_json()
assert response["event"] == { assert response["event"] == {
"manager_state": "restore_backup", "manager_state": "restore_backup",
"reason": None,
"stage": None, "stage": None,
"state": "completed", "state": "completed",
} }
@ -1360,15 +1386,13 @@ async def test_reader_writer_restore(
@pytest.mark.parametrize( @pytest.mark.parametrize(
("supervisor_error_string", "expected_error_code"), ("supervisor_error_string", "expected_error_code", "expected_reason"),
[ [
( ("Invalid password for backup", "password_incorrect", "password_incorrect"),
"Invalid password for backup",
"password_incorrect",
),
( (
"Backup was made on supervisor version 2025.12.0, can't restore on 2024.12.0. Must update supervisor first.", "Backup was made on supervisor version 2025.12.0, can't restore on 2024.12.0. Must update supervisor first.",
"home_assistant_error", "home_assistant_error",
"unknown_error",
), ),
], ],
) )
@ -1379,6 +1403,7 @@ async def test_reader_writer_restore_error(
supervisor_client: AsyncMock, supervisor_client: AsyncMock,
supervisor_error_string: str, supervisor_error_string: str,
expected_error_code: str, expected_error_code: str,
expected_reason: str,
) -> None: ) -> None:
"""Test restoring a backup.""" """Test restoring a backup."""
client = await hass_ws_client(hass) client = await hass_ws_client(hass)
@ -1400,6 +1425,7 @@ async def test_reader_writer_restore_error(
response = await client.receive_json() response = await client.receive_json()
assert response["event"] == { assert response["event"] == {
"manager_state": "restore_backup", "manager_state": "restore_backup",
"reason": None,
"stage": None, "stage": None,
"state": "in_progress", "state": "in_progress",
} }
@ -1419,6 +1445,7 @@ async def test_reader_writer_restore_error(
response = await client.receive_json() response = await client.receive_json()
assert response["event"] == { assert response["event"] == {
"manager_state": "restore_backup", "manager_state": "restore_backup",
"reason": expected_reason,
"stage": None, "stage": None,
"state": "failed", "state": "failed",
} }