core/tests/helpers/test_storage.py

190 lines
5.7 KiB
Python
Raw Normal View History

"""Tests for the storage helper."""
import asyncio
from datetime import timedelta
import json
from unittest.mock import Mock, patch
import pytest
from homeassistant.const import EVENT_HOMEASSISTANT_FINAL_WRITE
from homeassistant.helpers import storage
from homeassistant.util import dt
from tests.common import async_fire_time_changed, mock_coro
MOCK_VERSION = 1
2019-07-31 19:25:30 +00:00
MOCK_KEY = "storage-test"
MOCK_DATA = {"hello": "world"}
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)
async def test_loading(hass, store):
"""Test we can save and load data."""
await store.async_save(MOCK_DATA)
data = await store.async_load()
assert data == MOCK_DATA
async def test_custom_encoder(hass):
"""Test we can save and load data."""
2019-07-31 19:25:30 +00:00
class JSONEncoder(json.JSONEncoder):
"""Mock JSON encoder."""
def default(self, o):
"""Mock JSON encode method."""
return "9"
store = storage.Store(hass, MOCK_VERSION, MOCK_KEY, encoder=JSONEncoder)
await store.async_save(Mock())
data = await store.async_load()
assert data == "9"
async def test_loading_non_existing(hass, store):
"""Test we can save and load data."""
2019-07-31 19:25:30 +00:00
with patch("homeassistant.util.json.open", side_effect=FileNotFoundError):
data = await store.async_load()
assert data is None
async def test_loading_parallel(hass, store, hass_storage, caplog):
"""Test we can save and load data."""
2019-07-31 19:25:30 +00:00
hass_storage[store.key] = {"version": MOCK_VERSION, "data": MOCK_DATA}
2019-07-31 19:25:30 +00:00
results = await asyncio.gather(store.async_load(), store.async_load())
assert results[0] is MOCK_DATA
assert results[1] is MOCK_DATA
assert caplog.text.count(f"Loading data for {store.key}")
async def test_saving_with_delay(hass, store, hass_storage):
"""Test saving data after a delay."""
store.async_delay_save(lambda: MOCK_DATA, 1)
assert store.key not in hass_storage
async_fire_time_changed(hass, dt.utcnow() + timedelta(seconds=1))
await hass.async_block_till_done()
assert hass_storage[store.key] == {
2019-07-31 19:25:30 +00:00
"version": MOCK_VERSION,
"key": MOCK_KEY,
"data": MOCK_DATA,
}
async def test_saving_on_stop(hass, hass_storage):
"""Test delayed saves trigger when we quit Home Assistant."""
store = storage.Store(hass, MOCK_VERSION, MOCK_KEY)
store.async_delay_save(lambda: MOCK_DATA, 1)
assert store.key not in hass_storage
hass.bus.async_fire(EVENT_HOMEASSISTANT_FINAL_WRITE)
await hass.async_block_till_done()
assert hass_storage[store.key] == {
2019-07-31 19:25:30 +00:00
"version": MOCK_VERSION,
"key": MOCK_KEY,
"data": MOCK_DATA,
}
async def test_loading_while_delay(hass, store, hass_storage):
"""Test we load new data even if not written yet."""
2019-07-31 19:25:30 +00:00
await store.async_save({"delay": "no"})
assert hass_storage[store.key] == {
2019-07-31 19:25:30 +00:00
"version": MOCK_VERSION,
"key": MOCK_KEY,
"data": {"delay": "no"},
}
2019-07-31 19:25:30 +00:00
store.async_delay_save(lambda: {"delay": "yes"}, 1)
assert hass_storage[store.key] == {
2019-07-31 19:25:30 +00:00
"version": MOCK_VERSION,
"key": MOCK_KEY,
"data": {"delay": "no"},
}
data = await store.async_load()
2019-07-31 19:25:30 +00:00
assert data == {"delay": "yes"}
async def test_writing_while_writing_delay(hass, store, hass_storage):
"""Test a write while a write with delay is active."""
2019-07-31 19:25:30 +00:00
store.async_delay_save(lambda: {"delay": "yes"}, 1)
assert store.key not in hass_storage
2019-07-31 19:25:30 +00:00
await store.async_save({"delay": "no"})
assert hass_storage[store.key] == {
2019-07-31 19:25:30 +00:00
"version": MOCK_VERSION,
"key": MOCK_KEY,
"data": {"delay": "no"},
}
async_fire_time_changed(hass, dt.utcnow() + timedelta(seconds=1))
await hass.async_block_till_done()
assert hass_storage[store.key] == {
2019-07-31 19:25:30 +00:00
"version": MOCK_VERSION,
"key": MOCK_KEY,
"data": {"delay": "no"},
}
data = await store.async_load()
2019-07-31 19:25:30 +00:00
assert data == {"delay": "no"}
async def test_migrator_no_existing_config(hass, store, hass_storage):
"""Test migrator with no existing config."""
2019-07-31 19:25:30 +00:00
with patch("os.path.isfile", return_value=False), patch.object(
store, "async_load", return_value=mock_coro({"cur": "config"})
):
data = await storage.async_migrator(hass, "old-path", store)
2019-07-31 19:25:30 +00:00
assert data == {"cur": "config"}
assert store.key not in hass_storage
async def test_migrator_existing_config(hass, store, hass_storage):
"""Test migrating existing config."""
2019-07-31 19:25:30 +00:00
with patch("os.path.isfile", return_value=True), patch("os.remove") as mock_remove:
data = await storage.async_migrator(
2019-07-31 19:25:30 +00:00
hass, "old-path", store, old_conf_load_func=lambda _: {"old": "config"}
)
assert len(mock_remove.mock_calls) == 1
2019-07-31 19:25:30 +00:00
assert data == {"old": "config"}
assert hass_storage[store.key] == {
2019-07-31 19:25:30 +00:00
"key": MOCK_KEY,
"version": MOCK_VERSION,
"data": data,
}
async def test_migrator_transforming_config(hass, store, hass_storage):
"""Test migrating config to new format."""
2019-07-31 19:25:30 +00:00
async def old_conf_migrate_func(old_config):
"""Migrate old config to new format."""
2019-07-31 19:25:30 +00:00
return {"new": old_config["old"]}
2019-07-31 19:25:30 +00:00
with patch("os.path.isfile", return_value=True), patch("os.remove") as mock_remove:
data = await storage.async_migrator(
2019-07-31 19:25:30 +00:00
hass,
"old-path",
store,
old_conf_migrate_func=old_conf_migrate_func,
2019-07-31 19:25:30 +00:00
old_conf_load_func=lambda _: {"old": "config"},
)
assert len(mock_remove.mock_calls) == 1
2019-07-31 19:25:30 +00:00
assert data == {"new": "config"}
assert hass_storage[store.key] == {
2019-07-31 19:25:30 +00:00
"key": MOCK_KEY,
"version": MOCK_VERSION,
"data": data,
}