From 1bf545eb25f20fc27fe161691a94531cba7e005c Mon Sep 17 00:00:00 2001
From: Erik <erik@montnemery.com>
Date: Fri, 28 Feb 2025 10:28:49 +0100
Subject: [PATCH] Don't allow creating backups if hass is not running

---
 homeassistant/components/backup/manager.py |  8 ++--
 tests/components/backup/test_manager.py    | 47 +++++++++++++++++++++-
 2 files changed, 51 insertions(+), 4 deletions(-)

diff --git a/homeassistant/components/backup/manager.py b/homeassistant/components/backup/manager.py
index 317de85b823..df5d2aa606d 100644
--- a/homeassistant/components/backup/manager.py
+++ b/homeassistant/components/backup/manager.py
@@ -27,7 +27,7 @@ from homeassistant.backup_restore import (
     password_to_key,
 )
 from homeassistant.const import __version__ as HAVERSION
-from homeassistant.core import HomeAssistant, callback
+from homeassistant.core import CoreState, HomeAssistant, callback
 from homeassistant.helpers import (
     instance_id,
     integration_platform,
@@ -976,6 +976,8 @@ class BackupManager:
         with_automatic_settings: bool = False,
     ) -> NewBackup:
         """Initiate generating a backup."""
+        if self.hass.state is not CoreState.running:
+            raise BackupManagerError("Home Assistant is not running")
         if self.state is not BackupManagerState.IDLE:
             raise BackupManagerError(f"Backup manager busy: {self.state}")
 
@@ -991,7 +993,7 @@ class BackupManager:
             )
         )
         try:
-            return await self._async_create_backup(
+            return await self._async_initiate_backup(
                 agent_ids=agent_ids,
                 extra_metadata=extra_metadata,
                 include_addons=include_addons,
@@ -1018,7 +1020,7 @@ class BackupManager:
                 self._update_issue_backup_failed()
             raise
 
-    async def _async_create_backup(
+    async def _async_initiate_backup(
         self,
         *,
         agent_ids: list[str],
diff --git a/tests/components/backup/test_manager.py b/tests/components/backup/test_manager.py
index 6e626e63748..28aca6f8a69 100644
--- a/tests/components/backup/test_manager.py
+++ b/tests/components/backup/test_manager.py
@@ -4,6 +4,7 @@ from __future__ import annotations
 
 import asyncio
 from collections.abc import Callable, Generator
+from contextlib import AbstractContextManager, nullcontext as does_not_raise
 from dataclasses import replace
 from io import StringIO
 import json
@@ -34,6 +35,7 @@ from homeassistant.components.backup import (
 from homeassistant.components.backup.agent import BackupAgentError
 from homeassistant.components.backup.const import DATA_MANAGER
 from homeassistant.components.backup.manager import (
+    BackupManager,
     BackupManagerError,
     BackupManagerState,
     CreateBackupStage,
@@ -45,7 +47,7 @@ from homeassistant.components.backup.manager import (
     WrittenBackup,
 )
 from homeassistant.components.backup.util import password_to_key
-from homeassistant.core import HomeAssistant
+from homeassistant.core import CoreState, HomeAssistant
 from homeassistant.exceptions import HomeAssistantError
 from homeassistant.helpers import issue_registry as ir
 
@@ -659,6 +661,49 @@ async def test_initiate_backup(
     assert tar_file_path == f"{backup_directory}/{expected_filename}"
 
 
+RAISES_HASS_NOT_RUNNING = pytest.raises(
+    HomeAssistantError, match="Home Assistant is not running"
+)
+
+
+@pytest.mark.parametrize(
+    ("core_state", "expected_result", "initiate_backup_calls"),
+    [
+        (CoreState.final_write, RAISES_HASS_NOT_RUNNING, 0),
+        (CoreState.not_running, RAISES_HASS_NOT_RUNNING, 0),
+        (CoreState.running, does_not_raise(), 1),
+        (CoreState.starting, RAISES_HASS_NOT_RUNNING, 0),
+        (CoreState.stopped, RAISES_HASS_NOT_RUNNING, 0),
+        (CoreState.stopping, RAISES_HASS_NOT_RUNNING, 0),
+    ],
+)
+async def test_async_pre_backup_core_state(
+    hass: HomeAssistant,
+    core_state: CoreState,
+    expected_result: AbstractContextManager,
+    initiate_backup_calls: int,
+) -> None:
+    """Test pre backup in different core states."""
+    await setup_backup_integration(hass)
+    manager = hass.data[DATA_MANAGER]
+    hass.set_state(core_state)
+    with (  # pylint: disable=confusing-with-statement
+        patch.object(BackupManager, "_async_initiate_backup") as initiate_backup_mock,
+        expected_result,
+    ):
+        await manager.async_initiate_backup(
+            agent_ids=["backup.local"],
+            include_addons=[],
+            include_all_addons=False,
+            include_database=False,
+            include_folders=[],
+            include_homeassistant=True,
+            name=None,
+            password=None,
+        )
+    assert len(initiate_backup_mock.mock_calls) == initiate_backup_calls
+
+
 @pytest.mark.usefixtures("mock_backup_generation")
 @pytest.mark.parametrize("exception", [BackupAgentError("Boom!"), Exception("Boom!")])
 async def test_initiate_backup_with_agent_error(
-- 
GitLab