Improve saved state of RestoreSensor when using freezegun (#152740)

This commit is contained in:
Erik Montnemery
2025-09-30 18:27:56 +02:00
committed by GitHub
parent d7269cfcc6
commit 7f63ba2087
6 changed files with 104 additions and 22 deletions
@@ -30,7 +30,7 @@
'zmanim': dict({
'candle_lighting_offset': 40,
'date': dict({
'__type': "<class 'freezegun.api.FakeDate'>",
'__type': "<class 'datetime.date'>",
'isoformat': '2025-05-19',
}),
'havdalah_offset': 0,
@@ -86,7 +86,7 @@
'zmanim': dict({
'candle_lighting_offset': 18,
'date': dict({
'__type': "<class 'freezegun.api.FakeDate'>",
'__type': "<class 'datetime.date'>",
'isoformat': '2025-05-19',
}),
'havdalah_offset': 0,
@@ -142,7 +142,7 @@
'zmanim': dict({
'candle_lighting_offset': 18,
'date': dict({
'__type': "<class 'freezegun.api.FakeDate'>",
'__type': "<class 'datetime.date'>",
'isoformat': '2025-05-19',
}),
'havdalah_offset': 0,
@@ -1739,7 +1739,7 @@
'entry_type': <MealplanEntryType.DINNER: 'dinner'>,
'group_id': '0bf60b2e-ca89-42a9-94d4-8f67ca72b157',
'household_id': None,
'mealplan_date': FakeDate(2024, 1, 22),
'mealplan_date': HAFakeDate(2024, 1, 22),
'mealplan_id': 230,
'recipe': dict({
'description': "Een traybake is eigenlijk altijd een goed idee. Deze zoete aardappel curry traybake dus ook. Waarom? Omdat je alleen maar wat groenten - en in dit geval kip - op een bakplaat (traybake dus) legt, hier wat kruiden aan toevoegt en deze in de oven schuift. Ideaal dus als je geen zin hebt om lang in de keuken te staan. Maar gewoon lekker op de bank wil ploffen om te wachten tot de oven klaar is. Joe! That\\'s what we like. Deze zoete aardappel curry traybake bevat behalve zoete aardappel en curry ook kikkererwten, kippendijfilet en bloemkoolroosjes. Je gebruikt yoghurt en limoen als een soort dressing. En je serveert deze heerlijke traybake met naanbrood. Je kunt natuurljk ook voor deze traybake met chipolataworstjes gaan. Wil je graag meer ovengerechten? Dan moet je eigenlijk even kijken naar onze Ovenbijbel. Onmisbaar in je keuken! We willen je deze zoete aardappelstamppot met prei ook niet onthouden. Megalekker bordje comfortfood als je \\'t ons vraagt.",
@@ -1764,7 +1764,7 @@
'entry_type': <MealplanEntryType.BREAKFAST: 'breakfast'>,
'group_id': '0bf60b2e-ca89-42a9-94d4-8f67ca72b157',
'household_id': None,
'mealplan_date': FakeDate(2024, 1, 23),
'mealplan_date': HAFakeDate(2024, 1, 23),
'mealplan_id': 229,
'recipe': dict({
'description': 'The BEST Roast Chicken recipe is simple, budget friendly, and gives you a tender, mouth-watering chicken full of flavor! Served with roasted vegetables, this recipe is simple enough for any cook!',
@@ -1789,7 +1789,7 @@
'entry_type': <MealplanEntryType.LUNCH: 'lunch'>,
'group_id': '0bf60b2e-ca89-42a9-94d4-8f67ca72b157',
'household_id': None,
'mealplan_date': FakeDate(2024, 1, 23),
'mealplan_date': HAFakeDate(2024, 1, 23),
'mealplan_id': 226,
'recipe': dict({
'description': 'Te explicamos paso a paso, de manera sencilla, la elaboración de la receta de pollo al curry con leche de coco en 10 minutos. Ingredientes, tiempo de...',
@@ -1814,7 +1814,7 @@
'entry_type': <MealplanEntryType.LUNCH: 'lunch'>,
'group_id': '0bf60b2e-ca89-42a9-94d4-8f67ca72b157',
'household_id': None,
'mealplan_date': FakeDate(2024, 1, 23),
'mealplan_date': HAFakeDate(2024, 1, 23),
'mealplan_id': 224,
'recipe': dict({
'description': 'bourguignon, oignon, carotte, bouquet garni, vin rouge, beurre, sel, poivre',
@@ -1839,7 +1839,7 @@
'entry_type': <MealplanEntryType.DINNER: 'dinner'>,
'group_id': '0bf60b2e-ca89-42a9-94d4-8f67ca72b157',
'household_id': None,
'mealplan_date': FakeDate(2024, 1, 23),
'mealplan_date': HAFakeDate(2024, 1, 23),
'mealplan_id': 222,
'recipe': dict({
'description': 'Εύκολη μακαρονάδα με κεφτεδάκια στον φούρνο από τον Άκη Πετρετζίκη. Φτιάξτε την πιο εύκολη μακαρονάδα με κεφτεδάκια σε μόνο ένα σκεύος.',
@@ -1864,7 +1864,7 @@
'entry_type': <MealplanEntryType.DINNER: 'dinner'>,
'group_id': '0bf60b2e-ca89-42a9-94d4-8f67ca72b157',
'household_id': None,
'mealplan_date': FakeDate(2024, 1, 23),
'mealplan_date': HAFakeDate(2024, 1, 23),
'mealplan_id': 221,
'recipe': dict({
'description': 'Delicious Greek turkey meatballs with lemon orzo, tender veggies, and a creamy feta yogurt sauce. These healthy baked Greek turkey meatballs are filled with tons of wonderful herbs and make the perfect protein-packed weeknight meal!',
@@ -1889,7 +1889,7 @@
'entry_type': <MealplanEntryType.SIDE: 'side'>,
'group_id': '0bf60b2e-ca89-42a9-94d4-8f67ca72b157',
'household_id': None,
'mealplan_date': FakeDate(2024, 1, 23),
'mealplan_date': HAFakeDate(2024, 1, 23),
'mealplan_id': 220,
'recipe': dict({
'description': 'Einfacher Nudelauflauf mit Brokkoli, Sahnesauce und extra Käse. Dieses vegetarische 5 Zutaten Rezept ist super schnell gemacht und SO gut!',
@@ -1914,7 +1914,7 @@
'entry_type': <MealplanEntryType.DINNER: 'dinner'>,
'group_id': '0bf60b2e-ca89-42a9-94d4-8f67ca72b157',
'household_id': None,
'mealplan_date': FakeDate(2024, 1, 23),
'mealplan_date': HAFakeDate(2024, 1, 23),
'mealplan_id': 219,
'recipe': dict({
'description': 'This is a modified Pampered Chef recipe. You can use a trifle bowl or large glass punch/salad bowl to show it off. It is really easy to make and I never have any leftovers. Cook time includes chill time.',
@@ -1939,7 +1939,7 @@
'entry_type': <MealplanEntryType.DINNER: 'dinner'>,
'group_id': '0bf60b2e-ca89-42a9-94d4-8f67ca72b157',
'household_id': None,
'mealplan_date': FakeDate(2024, 1, 22),
'mealplan_date': HAFakeDate(2024, 1, 22),
'mealplan_id': 217,
'recipe': dict({
'description': 'Cheeseburger Sliders are juicy, cheesy and beefy - everything we love about classic burgers! These sliders are quick and easy plus they are make-ahead and reheat really well.',
@@ -1964,7 +1964,7 @@
'entry_type': <MealplanEntryType.LUNCH: 'lunch'>,
'group_id': '0bf60b2e-ca89-42a9-94d4-8f67ca72b157',
'household_id': None,
'mealplan_date': FakeDate(2024, 1, 22),
'mealplan_date': HAFakeDate(2024, 1, 22),
'mealplan_id': 216,
'recipe': dict({
'description': 'This All-American beef stew recipe includes tender beef coated in a rich, intense sauce and vegetables that bring complementary texture and flavor.',
@@ -1989,7 +1989,7 @@
'entry_type': <MealplanEntryType.DINNER: 'dinner'>,
'group_id': '0bf60b2e-ca89-42a9-94d4-8f67ca72b157',
'household_id': None,
'mealplan_date': FakeDate(2024, 1, 23),
'mealplan_date': HAFakeDate(2024, 1, 23),
'mealplan_id': 212,
'recipe': dict({
'description': 'This All-American beef stew recipe includes tender beef coated in a rich, intense sauce and vegetables that bring complementary texture and flavor.',
@@ -2014,7 +2014,7 @@
'entry_type': <MealplanEntryType.DINNER: 'dinner'>,
'group_id': '0bf60b2e-ca89-42a9-94d4-8f67ca72b157',
'household_id': None,
'mealplan_date': FakeDate(2024, 1, 22),
'mealplan_date': HAFakeDate(2024, 1, 22),
'mealplan_id': 211,
'recipe': dict({
'description': 'Einfacher Nudelauflauf mit Brokkoli, Sahnesauce und extra Käse. Dieses vegetarische 5 Zutaten Rezept ist super schnell gemacht und SO gut!',
@@ -2039,7 +2039,7 @@
'entry_type': <MealplanEntryType.DINNER: 'dinner'>,
'group_id': '0bf60b2e-ca89-42a9-94d4-8f67ca72b157',
'household_id': None,
'mealplan_date': FakeDate(2024, 1, 23),
'mealplan_date': HAFakeDate(2024, 1, 23),
'mealplan_id': 196,
'recipe': dict({
'description': 'Simple to prepare and ready in 25 minutes, this vegetarian miso noodle recipe can be eaten on its own or served as a side.',
@@ -2064,7 +2064,7 @@
'entry_type': <MealplanEntryType.DINNER: 'dinner'>,
'group_id': '0bf60b2e-ca89-42a9-94d4-8f67ca72b157',
'household_id': None,
'mealplan_date': FakeDate(2024, 1, 22),
'mealplan_date': HAFakeDate(2024, 1, 22),
'mealplan_id': 195,
'recipe': dict({
'description': 'Avis aux nostalgiques des années 1980, la mousse de saumon est de retour dans une présentation adaptée au goût du jour. On utilise une technique sans faille : un saumon frais cuit au micro-ondes et mélangé au robot avec du fromage à la crème et de la crème sure. On obtient ainsi une texture onctueuse à tartiner, qui na rien à envier aux préparations gélatineuses dantan !',
@@ -2089,7 +2089,7 @@
'entry_type': <MealplanEntryType.DINNER: 'dinner'>,
'group_id': '3931df86-0679-4579-8c63-4bedc9ca9a85',
'household_id': None,
'mealplan_date': FakeDate(2024, 1, 21),
'mealplan_date': HAFakeDate(2024, 1, 21),
'mealplan_id': 1,
'recipe': None,
'title': 'Aquavite',
+57
View File
@@ -8,6 +8,7 @@ from decimal import Decimal
from typing import Any
from unittest.mock import patch
from freezegun.api import freeze_time
import pytest
from homeassistant.components import sensor
@@ -475,6 +476,62 @@ async def test_restore_sensor_save_state(
assert type(extra_data["native_value"]) is native_value_type
@freeze_time("2020-02-08 15:00:00")
async def test_restore_sensor_save_state_frozen_time_datetime(
hass: HomeAssistant,
hass_storage: dict[str, Any],
) -> None:
"""Test RestoreSensor."""
entity0 = MockRestoreSensor(
name="Test",
native_value=dt_util.utcnow(),
native_unit_of_measurement=None,
device_class=SensorDeviceClass.TIMESTAMP,
)
setup_test_component_platform(hass, sensor.DOMAIN, [entity0])
assert await async_setup_component(hass, "sensor", {"sensor": {"platform": "test"}})
await hass.async_block_till_done()
# Trigger saving state
await async_mock_restore_state_shutdown_restart(hass)
assert len(hass_storage[RESTORE_STATE_KEY]["data"]) == 1
state = hass_storage[RESTORE_STATE_KEY]["data"][0]["state"]
assert state["entity_id"] == entity0.entity_id
extra_data = hass_storage[RESTORE_STATE_KEY]["data"][0]["extra_data"]
assert extra_data == RESTORE_DATA["datetime"]
assert type(extra_data["native_value"]) is dict
@freeze_time("2020-02-08 15:00:00")
async def test_restore_sensor_save_state_frozen_time_date(
hass: HomeAssistant,
hass_storage: dict[str, Any],
) -> None:
"""Test RestoreSensor."""
entity0 = MockRestoreSensor(
name="Test",
native_value=dt_util.utcnow().date(),
native_unit_of_measurement=None,
device_class=SensorDeviceClass.DATE,
)
setup_test_component_platform(hass, sensor.DOMAIN, [entity0])
assert await async_setup_component(hass, "sensor", {"sensor": {"platform": "test"}})
await hass.async_block_till_done()
# Trigger saving state
await async_mock_restore_state_shutdown_restart(hass)
assert len(hass_storage[RESTORE_STATE_KEY]["data"]) == 1
state = hass_storage[RESTORE_STATE_KEY]["data"][0]["state"]
assert state["entity_id"] == entity0.entity_id
extra_data = hass_storage[RESTORE_STATE_KEY]["data"][0]["extra_data"]
assert extra_data == RESTORE_DATA["date"]
assert type(extra_data["native_value"]) is dict
@pytest.mark.parametrize(
("native_value", "native_value_type", "extra_data", "device_class", "uom"),
[
@@ -1464,7 +1464,7 @@ async def test_saving_auto_off(
freezer.move_to("2022-02-02 02:02:00+00:00")
fake_extra_data = {
"auto_off_time": {
"__type": "<class 'tests.patch_time.HAFakeDatetime'>",
"__type": "<class 'datetime.datetime'>",
"isoformat": "2022-02-02T02:02:02+00:00",
},
}
+3 -1
View File
@@ -186,11 +186,13 @@ def pytest_runtest_setup() -> None:
destinations will be allowed.
freezegun:
Modified to include https://github.com/spulec/freezegun/pull/424
Modified to include https://github.com/spulec/freezegun/pull/424 and improve class str.
"""
pytest_socket.socket_allow_hosts(["127.0.0.1"])
pytest_socket.disable_socket(allow_unix_socket=True)
freezegun.api.FakeDate = patch_time.HAFakeDate # type: ignore[attr-defined]
freezegun.api.datetime_to_fakedatetime = patch_time.ha_datetime_to_fakedatetime # type: ignore[attr-defined]
freezegun.api.FakeDatetime = patch_time.HAFakeDatetime # type: ignore[attr-defined]
+25 -2
View File
@@ -26,8 +26,31 @@ def ha_datetime_to_fakedatetime(datetime) -> freezegun.api.FakeDatetime: # type
)
class HAFakeDatetime(freezegun.api.FakeDatetime): # type: ignore[name-defined]
"""Modified to include https://github.com/spulec/freezegun/pull/424."""
class HAFakeDateMeta(freezegun.api.FakeDateMeta):
"""Modified to override the string representation."""
def __str__(cls) -> str: # noqa: N805 (ruff doesn't know this is a metaclass)
"""Return the string representation of the class."""
return "<class 'datetime.date'>"
class HAFakeDate(freezegun.api.FakeDate, metaclass=HAFakeDateMeta): # type: ignore[name-defined]
"""Modified to improve class str."""
class HAFakeDatetimeMeta(freezegun.api.FakeDatetimeMeta):
"""Modified to override the string representation."""
def __str__(cls) -> str: # noqa: N805 (ruff doesn't know this is a metaclass)
"""Return the string representation of the class."""
return "<class 'datetime.datetime'>"
class HAFakeDatetime(freezegun.api.FakeDatetime, metaclass=HAFakeDatetimeMeta): # type: ignore[name-defined]
"""Modified to include basic fold support and improve class str.
Fold support submitted to upstream in https://github.com/spulec/freezegun/pull/424.
"""
@classmethod
def now(cls, tz=None):