From c7276621eb44d2a1d898d3aee2c725154a2b4633 Mon Sep 17 00:00:00 2001 From: Josef Zweck Date: Wed, 18 Feb 2026 23:32:23 +0100 Subject: [PATCH] Add metadata validation for missing backup files in OneDrive backup agent (#163072) --- homeassistant/components/onedrive/backup.py | 15 +++++++++++ .../onedrive_for_business/backup.py | 2 +- tests/components/onedrive/test_backup.py | 25 +++++++++++++++++++ 3 files changed, 41 insertions(+), 1 deletion(-) diff --git a/homeassistant/components/onedrive/backup.py b/homeassistant/components/onedrive/backup.py index 232e8b1ad12..a76d6df820a 100644 --- a/homeassistant/components/onedrive/backup.py +++ b/homeassistant/components/onedrive/backup.py @@ -257,9 +257,24 @@ class OneDriveBackupAgent(BackupAgent): ) items = await self._client.list_drive_items(self._folder_id) + + # Build a set of backup filenames to check for orphaned metadata + backup_filenames = { + item.name for item in items if item.name and item.name.endswith(".tar") + } + metadata_files: dict[str, AgentBackup] = {} for item in items: if item.name and item.name.endswith(".metadata.json"): + # Check if corresponding backup file exists + backup_filename = f"{item.name[: -len('.metadata.json')]}.tar" + if backup_filename not in backup_filenames: + _LOGGER.warning( + "Backup file %s not found for metadata %s", + backup_filename, + item.name, + ) + continue if metadata := await _download_metadata(item.id): metadata_files[metadata.backup_id] = metadata diff --git a/homeassistant/components/onedrive_for_business/backup.py b/homeassistant/components/onedrive_for_business/backup.py index 661b616f3cb..52ce8af8941 100644 --- a/homeassistant/components/onedrive_for_business/backup.py +++ b/homeassistant/components/onedrive_for_business/backup.py @@ -255,7 +255,7 @@ class OneDriveBackupAgent(BackupAgent): for item in items: if item.name and item.name.endswith(".metadata.json"): # Check if corresponding backup file exists - backup_filename = item.name.replace(".metadata.json", ".tar") + backup_filename = f"{item.name[: -len('.metadata.json')]}.tar" if backup_filename not in backup_filenames: _LOGGER.warning( "Backup file %s not found for metadata %s", diff --git a/tests/components/onedrive/test_backup.py b/tests/components/onedrive/test_backup.py index 78e8964bcc8..d15cfcfa6c6 100644 --- a/tests/components/onedrive/test_backup.py +++ b/tests/components/onedrive/test_backup.py @@ -157,6 +157,31 @@ async def test_agents_get_backup( } +async def test_agents_get_backup_missing_file( + hass: HomeAssistant, + hass_ws_client: WebSocketGenerator, + mock_config_entry: MockConfigEntry, + mock_onedrive_client: MagicMock, + mock_metadata_file: File, + caplog: pytest.LogCaptureFixture, +) -> None: + """Test what happens when only metadata exists.""" + mock_onedrive_client.list_drive_items.return_value = [mock_metadata_file] + + backup_id = BACKUP_METADATA["backup_id"] + client = await hass_ws_client(hass) + await client.send_json_auto_id({"type": "backup/details", "backup_id": backup_id}) + response = await client.receive_json() + + assert response["success"] + assert response["result"]["agent_errors"] == {} + assert response["result"]["backup"] is None + assert ( + "Backup file 23e64aec.tar not found for metadata 23e64aec.metadata.json" + in caplog.text + ) + + async def test_agents_delete( hass: HomeAssistant, hass_ws_client: WebSocketGenerator,