Store: copy pending data (#59934)

This commit is contained in:
Paulus Schoutsen 2021-11-18 15:56:22 -08:00 committed by GitHub
parent 0fb21af07f
commit 442597928e
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
8 changed files with 42 additions and 11 deletions

View file

@ -4,6 +4,7 @@ from __future__ import annotations
import asyncio
from collections.abc import Callable
from contextlib import suppress
from copy import deepcopy
import inspect
from json import JSONEncoder
import logging
@ -133,6 +134,10 @@ class Store:
# If we didn't generate data yet, do it now.
if "data_func" in data:
data["data"] = data.pop("data_func")()
# We make a copy because code might assume it's safe to mutate loaded data
# and we don't want that to mess with what we're trying to store.
data = deepcopy(data)
else:
data = await self.hass.async_add_executor_job(
json_util.load_json, self.path

View file

@ -1,5 +1,6 @@
{
"version": 1,
"minor_version": 1,
"key": "trace.saved_traces",
"data": {
"automation.sun": [

View file

@ -1,5 +1,6 @@
{
"version": 1,
"minor_version": 1,
"key": "trace.saved_traces",
"data": {
"script.sun": [

View file

@ -49,7 +49,7 @@ import homeassistant.util.dt as dt_util
from tests.common import MockConfigEntry, async_fire_time_changed
DEFAULT_CONFIG_ENTRY_ID = 1
DEFAULT_CONFIG_ENTRY_ID = "1"
DEFAULT_HOST = "1.2.3.4"
DEFAULT_SITE = "site_id"

View file

@ -1089,7 +1089,7 @@ async def test_restoring_client(hass, aioclient_mock):
data=ENTRY_CONFIG,
source="test",
options={},
entry_id=1,
entry_id="1",
)
registry = er.async_get(hass)

View file

@ -14,7 +14,7 @@ from .test_controller import (
setup_unifi_integration,
)
from tests.common import MockConfigEntry
from tests.common import MockConfigEntry, flush_store
async def test_setup_with_no_config(hass):
@ -110,6 +110,7 @@ async def test_wireless_clients(hass, hass_storage, aioclient_mock):
config_entry = await setup_unifi_integration(
hass, aioclient_mock, clients_response=[client_1, client_2]
)
await flush_store(hass.data[unifi.UNIFI_WIRELESS_CLIENTS]._store)
for mac in [
"00:00:00:00:00:00",

View file

@ -22,6 +22,7 @@ from homeassistant.helpers.dispatcher import async_dispatcher_send
from .test_controller import (
CONTROLLER_HOST,
DEFAULT_CONFIG_ENTRY_ID,
DESCRIPTION,
ENTRY_CONFIG,
setup_unifi_integration,
@ -857,7 +858,7 @@ async def test_restore_client_succeed(hass, aioclient_mock):
data=ENTRY_CONFIG,
source="test",
options={},
entry_id=1,
entry_id=DEFAULT_CONFIG_ENTRY_ID,
)
registry = er.async_get(hass)
@ -947,7 +948,7 @@ async def test_restore_client_no_old_state(hass, aioclient_mock):
data=ENTRY_CONFIG,
source="test",
options={},
entry_id=1,
entry_id=DEFAULT_CONFIG_ENTRY_ID,
)
registry = er.async_get(hass)

View file

@ -28,13 +28,13 @@ MOCK_DATA2 = {"goodbye": "cruel world"}
@pytest.fixture
def store(hass):
"""Fixture of a store that prevents writing on Home Assistant stop."""
yield storage.Store(hass, MOCK_VERSION, MOCK_KEY)
return storage.Store(hass, MOCK_VERSION, MOCK_KEY)
@pytest.fixture
def store_v_1_1(hass):
"""Fixture of a store that prevents writing on Home Assistant stop."""
yield storage.Store(
return storage.Store(
hass, MOCK_VERSION, MOCK_KEY, minor_version=MOCK_MINOR_VERSION_1
)
@ -42,7 +42,7 @@ def store_v_1_1(hass):
@pytest.fixture
def store_v_1_2(hass):
"""Fixture of a store that prevents writing on Home Assistant stop."""
yield storage.Store(
return storage.Store(
hass, MOCK_VERSION, MOCK_KEY, minor_version=MOCK_MINOR_VERSION_2
)
@ -50,7 +50,7 @@ def store_v_1_2(hass):
@pytest.fixture
def store_v_2_1(hass):
"""Fixture of a store that prevents writing on Home Assistant stop."""
yield storage.Store(
return storage.Store(
hass, MOCK_VERSION_2, MOCK_KEY, minor_version=MOCK_MINOR_VERSION_1
)
@ -91,8 +91,8 @@ async def test_loading_parallel(hass, store, hass_storage, caplog):
results = await asyncio.gather(store.async_load(), store.async_load())
assert results[0] is MOCK_DATA
assert results[1] is MOCK_DATA
assert results[0] == MOCK_DATA
assert results[0] is results[1]
assert caplog.text.count(f"Loading data for {store.key}")
@ -436,3 +436,25 @@ async def test_legacy_migration(hass, hass_storage, store_v_1_2):
"minor_version": 1,
"data": MOCK_DATA,
}
async def test_changing_delayed_written_data(hass, store, hass_storage):
"""Test changing data that is written with delay."""
data_to_store = {"hello": "world"}
store.async_delay_save(lambda: data_to_store, 1)
assert store.key not in hass_storage
loaded_data = await store.async_load()
assert loaded_data == data_to_store
assert loaded_data is not data_to_store
loaded_data["hello"] = "earth"
async_fire_time_changed(hass, dt.utcnow() + timedelta(seconds=1))
await hass.async_block_till_done()
assert hass_storage[store.key] == {
"version": MOCK_VERSION,
"minor_version": 1,
"key": MOCK_KEY,
"data": {"hello": "world"},
}