Update typing of BackupAgent.async_get_backup (#139923)

* Update typing of BackupAgent.async_get_backup

* Remove manual reset of frame helper
This commit is contained in:
Erik Montnemery 2025-03-06 17:25:34 +01:00 committed by GitHub
parent 88f18fdfdc
commit 6ba45a32c0
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
6 changed files with 162 additions and 2 deletions

View File

@ -83,7 +83,7 @@ class BackupAgent(abc.ABC):
self,
backup_id: str,
**kwargs: Any,
) -> AgentBackup | None:
) -> AgentBackup:
"""Return a backup.
Raises BackupNotFound if the backup does not exist.

View File

@ -15,6 +15,7 @@ from multidict import istr
from homeassistant.components.http import KEY_HASS, HomeAssistantView, require_admin
from homeassistant.core import HomeAssistant, callback
from homeassistant.exceptions import HomeAssistantError
from homeassistant.helpers import frame
from homeassistant.util import slugify
from . import util
@ -66,7 +67,12 @@ class DownloadBackupView(HomeAssistantView):
# Check for None to be backwards compatible with the old BackupAgent API,
# this can be removed in HA Core 2025.10
if backup is None:
if not backup:
frame.report_usage(
"returns None from BackupAgent.async_get_backup",
breaks_in_ha_version="2025.10",
integration_domain=agent_id.partition(".")[0],
)
return Response(status=HTTPStatus.NOT_FOUND)
headers = {

View File

@ -30,6 +30,7 @@ from homeassistant.backup_restore import (
from homeassistant.const import __version__ as HAVERSION
from homeassistant.core import HomeAssistant, callback
from homeassistant.helpers import (
frame,
instance_id,
integration_platform,
issue_registry as ir,
@ -665,6 +666,11 @@ class BackupManager:
# Check for None to be backwards compatible with the old BackupAgent API,
# this can be removed in HA Core 2025.10
if not result:
frame.report_usage(
"returns None from BackupAgent.async_get_backup",
breaks_in_ha_version="2025.10",
integration_domain=agent_id.partition(".")[0],
)
continue
if backup is None:
if known_backup := self.known_backups.get(backup_id):
@ -1280,6 +1286,11 @@ class BackupManager:
# Check for None to be backwards compatible with the old BackupAgent API,
# this can be removed in HA Core 2025.10
if not backup:
frame.report_usage(
"returns None from BackupAgent.async_get_backup",
breaks_in_ha_version="2025.10",
integration_domain=agent_id.partition(".")[0],
)
raise BackupManagerError(
f"Backup {backup_id} not found in agent {agent_id}"
)
@ -1376,6 +1387,11 @@ class BackupManager:
# Check for None to be backwards compatible with the old BackupAgent API,
# this can be removed in HA Core 2025.10
if not backup:
frame.report_usage(
"returns None from BackupAgent.async_get_backup",
breaks_in_ha_version="2025.10",
integration_domain=agent_id.partition(".")[0],
)
raise BackupManagerError(
f"Backup {backup_id} not found in agent {agent_id}"
)

View File

@ -229,6 +229,17 @@
'type': 'result',
})
# ---
# name: test_can_decrypt_on_download_get_backup_returns_none
dict({
'error': dict({
'code': 'home_assistant_error',
'message': 'Backup abc123 not found in agent test.remote',
}),
'id': 1,
'success': False,
'type': 'result',
})
# ---
# name: test_can_decrypt_on_download_with_agent_error[BackupAgentError]
dict({
'error': dict({
@ -4930,6 +4941,18 @@
'type': 'result',
})
# ---
# name: test_details_get_backup_returns_none
dict({
'id': 1,
'result': dict({
'agent_errors': dict({
}),
'backup': None,
}),
'success': True,
'type': 'result',
})
# ---
# name: test_details_with_errors[BackupAgentUnreachableError]
dict({
'id': 1,
@ -5728,6 +5751,17 @@
# name: test_restore_remote_agent[remote_agents1-backups1].1
1
# ---
# name: test_restore_remote_agent_get_backup_returns_none
dict({
'error': dict({
'code': 'home_assistant_error',
'message': 'Backup abc123 not found in agent test.remote',
}),
'id': 1,
'success': False,
'type': 'result',
})
# ---
# name: test_restore_wrong_password
dict({
'error': dict({

View File

@ -234,6 +234,26 @@ async def test_downloading_backup_not_found(
assert resp.status == 404
async def test_downloading_backup_not_found_get_backup_returns_none(
hass: HomeAssistant,
hass_client: ClientSessionGenerator,
caplog: pytest.LogCaptureFixture,
) -> None:
"""Test downloading a backup file that does not exist."""
mock_agents = await setup_backup_integration(hass, remote_agents=["test.test"])
mock_agents["test.test"].async_get_backup.return_value = None
mock_agents["test.test"].async_get_backup.side_effect = None
client = await hass_client()
resp = await client.get("/api/backup/download/abc123?agent_id=test.test")
assert resp.status == 404
assert (
"Detected that integration 'test' returns None from BackupAgent.async_get_backup."
in caplog.text
)
async def test_downloading_as_non_admin(
hass: HomeAssistant,
hass_client: ClientSessionGenerator,

View File

@ -234,6 +234,31 @@ async def test_details_with_errors(
assert await client.receive_json() == snapshot
async def test_details_get_backup_returns_none(
hass: HomeAssistant,
hass_ws_client: WebSocketGenerator,
caplog: pytest.LogCaptureFixture,
snapshot: SnapshotAssertion,
) -> None:
"""Test getting backup info when the agent returns None from get_backup."""
mock_agents = await setup_backup_integration(hass, remote_agents=["test.remote"])
mock_agents["test.remote"].async_get_backup.return_value = None
mock_agents["test.remote"].async_get_backup.side_effect = None
client = await hass_ws_client(hass)
await hass.async_block_till_done()
with patch("pathlib.Path.exists", return_value=True):
await client.send_json_auto_id(
{"type": "backup/details", "backup_id": "abc123"}
)
assert await client.receive_json() == snapshot
assert (
"Detected that integration 'test' returns None from BackupAgent.async_get_backup."
in caplog.text
)
@pytest.mark.parametrize(
("remote_agents", "backups"),
[
@ -724,6 +749,36 @@ async def test_restore_remote_agent(
assert len(restart_calls) == snapshot
async def test_restore_remote_agent_get_backup_returns_none(
hass: HomeAssistant,
hass_ws_client: WebSocketGenerator,
caplog: pytest.LogCaptureFixture,
snapshot: SnapshotAssertion,
) -> None:
"""Test calling the restore command when the agent returns None from get_backup."""
mock_agents = await setup_backup_integration(hass, remote_agents=["test.remote"])
mock_agents["test.remote"].async_get_backup.return_value = None
mock_agents["test.remote"].async_get_backup.side_effect = None
restart_calls = async_mock_service(hass, "homeassistant", "restart")
client = await hass_ws_client(hass)
await hass.async_block_till_done()
await client.send_json_auto_id(
{
"type": "backup/restore",
"backup_id": "abc123",
"agent_id": "test.remote",
}
)
assert await client.receive_json() == snapshot
assert len(restart_calls) == 0
assert (
"Detected that integration 'test' returns None from BackupAgent.async_get_backup."
in caplog.text
)
async def test_restore_wrong_password(
hass: HomeAssistant,
hass_ws_client: WebSocketGenerator,
@ -3543,3 +3598,32 @@ async def test_can_decrypt_on_download_with_agent_error(
}
)
assert await client.receive_json() == snapshot
@pytest.mark.usefixtures("mock_backups")
async def test_can_decrypt_on_download_get_backup_returns_none(
hass: HomeAssistant,
hass_ws_client: WebSocketGenerator,
caplog: pytest.LogCaptureFixture,
snapshot: SnapshotAssertion,
) -> None:
"""Test can decrypt on download when the agent returns None from get_backup."""
mock_agents = await setup_backup_integration(hass, remote_agents=["test.remote"])
mock_agents["test.remote"].async_get_backup.return_value = None
mock_agents["test.remote"].async_get_backup.side_effect = None
client = await hass_ws_client(hass)
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
assert (
"Detected that integration 'test' returns None from BackupAgent.async_get_backup."
in caplog.text
)