From 92acfc1464629eb21b21ba865e103b9f28422eee Mon Sep 17 00:00:00 2001 From: Erik Montnemery Date: Tue, 23 Jul 2024 14:13:08 +0200 Subject: [PATCH] Indicate database migration in /api/core/state response (#122445) * Indicate database migration in /api/core/state response * Change API response according to review comment * Adjust API response * Update test * Add test --- homeassistant/components/api/__init__.py | 9 ++++-- homeassistant/helpers/recorder.py | 12 +++++-- tests/components/api/test_init.py | 41 +++++++++++++++++++++++- tests/helpers/test_recorder.py | 27 ++++++++++------ 4 files changed, 73 insertions(+), 16 deletions(-) diff --git a/homeassistant/components/api/__init__.py b/homeassistant/components/api/__init__.py index 73751daa6cb..9572ed3fbd1 100644 --- a/homeassistant/components/api/__init__.py +++ b/homeassistant/components/api/__init__.py @@ -45,7 +45,7 @@ from homeassistant.exceptions import ( TemplateError, Unauthorized, ) -from homeassistant.helpers import config_validation as cv, template +from homeassistant.helpers import config_validation as cv, recorder, template from homeassistant.helpers.json import json_dumps, json_fragment from homeassistant.helpers.service import async_get_all_descriptions from homeassistant.helpers.typing import ConfigType @@ -118,8 +118,11 @@ class APICoreStateView(HomeAssistantView): Home Assistant core is running. Its primary use case is for supervisor to check if Home Assistant is running. """ - hass = request.app[KEY_HASS] - return self.json({"state": hass.state.value}) + hass: HomeAssistant = request.app[KEY_HASS] + migration = recorder.async_migration_in_progress(hass) + live = recorder.async_migration_is_live(hass) + recorder_state = {"migration_in_progress": migration, "migration_is_live": live} + return self.json({"state": hass.state.value, "recorder_state": recorder_state}) class APIEventStream(HomeAssistantView): diff --git a/homeassistant/helpers/recorder.py b/homeassistant/helpers/recorder.py index ac534a7230a..f6657efc6d7 100644 --- a/homeassistant/helpers/recorder.py +++ b/homeassistant/helpers/recorder.py @@ -20,16 +20,24 @@ class RecorderData: db_connected: asyncio.Future[bool] = field(default_factory=asyncio.Future) +@callback def async_migration_in_progress(hass: HomeAssistant) -> bool: """Check to see if a recorder migration is in progress.""" - if "recorder" not in hass.config.components: - return False # pylint: disable-next=import-outside-toplevel from homeassistant.components import recorder return recorder.util.async_migration_in_progress(hass) +@callback +def async_migration_is_live(hass: HomeAssistant) -> bool: + """Check to see if a recorder migration is live.""" + # pylint: disable-next=import-outside-toplevel + from homeassistant.components import recorder + + return recorder.util.async_migration_is_live(hass) + + @callback def async_initialize_recorder(hass: HomeAssistant) -> None: """Initialize recorder data.""" diff --git a/tests/components/api/test_init.py b/tests/components/api/test_init.py index a1453315dbf..c283aeb718e 100644 --- a/tests/components/api/test_init.py +++ b/tests/components/api/test_init.py @@ -770,4 +770,43 @@ async def test_api_core_state(hass: HomeAssistant, mock_api_client: TestClient) resp = await mock_api_client.get("/api/core/state") assert resp.status == HTTPStatus.OK json = await resp.json() - assert json["state"] == "RUNNING" + assert json == { + "state": "RUNNING", + "recorder_state": {"migration_in_progress": False, "migration_is_live": False}, + } + + +@pytest.mark.parametrize( + ("migration_in_progress", "migration_is_live"), + [ + (False, False), + (False, True), + (True, False), + (True, True), + ], +) +async def test_api_core_state_recorder_migrating( + hass: HomeAssistant, + mock_api_client: TestClient, + migration_in_progress: bool, + migration_is_live: bool, +) -> None: + """Test getting core status.""" + with ( + patch( + "homeassistant.helpers.recorder.async_migration_in_progress", + return_value=migration_in_progress, + ), + patch( + "homeassistant.helpers.recorder.async_migration_is_live", + return_value=migration_is_live, + ), + ): + resp = await mock_api_client.get("/api/core/state") + assert resp.status == HTTPStatus.OK + json = await resp.json() + expected_recorder_state = { + "migration_in_progress": migration_in_progress, + "migration_is_live": migration_is_live, + } + assert json == {"state": "RUNNING", "recorder_state": expected_recorder_state} diff --git a/tests/helpers/test_recorder.py b/tests/helpers/test_recorder.py index 94f30d812bc..8fb8450bcb8 100644 --- a/tests/helpers/test_recorder.py +++ b/tests/helpers/test_recorder.py @@ -18,18 +18,25 @@ async def test_async_migration_in_progress( ): assert recorder.async_migration_in_progress(hass) is False - # The recorder is not loaded - with patch( - "homeassistant.components.recorder.util.async_migration_in_progress", - return_value=True, - ): - assert recorder.async_migration_in_progress(hass) is False - - await async_setup_recorder_instance(hass) - - # The recorder is now loaded with patch( "homeassistant.components.recorder.util.async_migration_in_progress", return_value=True, ): assert recorder.async_migration_in_progress(hass) is True + + +async def test_async_migration_is_live( + async_setup_recorder_instance: RecorderInstanceGenerator, hass: HomeAssistant +) -> None: + """Test async_migration_in_progress wraps the recorder.""" + with patch( + "homeassistant.components.recorder.util.async_migration_is_live", + return_value=False, + ): + assert recorder.async_migration_is_live(hass) is False + + with patch( + "homeassistant.components.recorder.util.async_migration_is_live", + return_value=True, + ): + assert recorder.async_migration_is_live(hass) is True