Drop use of async_setup_recorder_instance fixture in recorder purge tests (#121193)

This commit is contained in:
Erik Montnemery 2024-07-04 17:39:24 +02:00 committed by GitHub
parent a1e6f8c2ec
commit 6df15ad8fc
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
2 changed files with 229 additions and 264 deletions

View file

@ -12,7 +12,7 @@ from sqlalchemy.exc import DatabaseError, OperationalError
from sqlalchemy.orm.session import Session
from voluptuous.error import MultipleInvalid
from homeassistant.components import recorder
from homeassistant.components.recorder import DOMAIN as RECORDER_DOMAIN, Recorder
from homeassistant.components.recorder.const import SupportedDialect
from homeassistant.components.recorder.db_schema import (
Events,
@ -35,7 +35,6 @@ from homeassistant.components.recorder.tasks import PurgeTask
from homeassistant.components.recorder.util import session_scope
from homeassistant.const import EVENT_STATE_CHANGED, EVENT_THEMES_UPDATED, STATE_ON
from homeassistant.core import HomeAssistant
from homeassistant.helpers.typing import ConfigType
from homeassistant.util import dt as dt_util
from .common import (
@ -58,6 +57,13 @@ TEST_EVENT_TYPES = (
)
@pytest.fixture
async def mock_recorder_before_hass(
async_test_recorder: RecorderInstanceGenerator,
) -> None:
"""Set up recorder."""
@pytest.fixture(name="use_sqlite")
def mock_use_sqlite(request: pytest.FixtureRequest) -> Generator[None]:
"""Pytest fixture to switch purge method."""
@ -70,20 +76,15 @@ def mock_use_sqlite(request: pytest.FixtureRequest) -> Generator[None]:
yield
async def test_purge_big_database(
async_setup_recorder_instance: RecorderInstanceGenerator, hass: HomeAssistant
) -> None:
async def test_purge_big_database(hass: HomeAssistant, recorder_mock: Recorder) -> None:
"""Test deleting 2/3 old states from a big database."""
instance = await async_setup_recorder_instance(hass)
for _ in range(12):
await _add_test_states(hass, wait_recording_done=False)
await async_wait_recording_done(hass)
with (
patch.object(instance, "max_bind_vars", 72),
patch.object(instance.database_engine, "max_bind_vars", 72),
patch.object(recorder_mock, "max_bind_vars", 72),
patch.object(recorder_mock.database_engine, "max_bind_vars", 72),
session_scope(hass=hass) as session,
):
states = session.query(States)
@ -94,7 +95,7 @@ async def test_purge_big_database(
purge_before = dt_util.utcnow() - timedelta(days=4)
finished = purge_old_data(
instance,
recorder_mock,
purge_before,
states_batch_size=1,
events_batch_size=1,
@ -105,12 +106,8 @@ async def test_purge_big_database(
assert state_attributes.count() == 1
async def test_purge_old_states(
async_setup_recorder_instance: RecorderInstanceGenerator, hass: HomeAssistant
) -> None:
async def test_purge_old_states(hass: HomeAssistant, recorder_mock: Recorder) -> None:
"""Test deleting old states."""
instance = await async_setup_recorder_instance(hass)
await _add_test_states(hass)
# make sure we start with 6 states
@ -125,13 +122,13 @@ async def test_purge_old_states(
events = session.query(Events).filter(Events.event_type == "state_changed")
assert events.count() == 0
assert "test.recorder2" in instance.states_manager._last_committed_id
assert "test.recorder2" in recorder_mock.states_manager._last_committed_id
purge_before = dt_util.utcnow() - timedelta(days=4)
# run purge_old_data()
finished = purge_old_data(
instance,
recorder_mock,
purge_before,
states_batch_size=1,
events_batch_size=1,
@ -141,7 +138,7 @@ async def test_purge_old_states(
assert states.count() == 2
assert state_attributes.count() == 1
assert "test.recorder2" in instance.states_manager._last_committed_id
assert "test.recorder2" in recorder_mock.states_manager._last_committed_id
states_after_purge = list(session.query(States))
# Since these states are deleted in batches, we can't guarantee the order
@ -153,17 +150,17 @@ async def test_purge_old_states(
assert dontpurgeme_5.old_state_id == dontpurgeme_4.state_id
assert dontpurgeme_4.old_state_id is None
finished = purge_old_data(instance, purge_before, repack=False)
finished = purge_old_data(recorder_mock, purge_before, repack=False)
assert finished
assert states.count() == 2
assert state_attributes.count() == 1
assert "test.recorder2" in instance.states_manager._last_committed_id
assert "test.recorder2" in recorder_mock.states_manager._last_committed_id
# run purge_old_data again
purge_before = dt_util.utcnow()
finished = purge_old_data(
instance,
recorder_mock,
purge_before,
states_batch_size=1,
events_batch_size=1,
@ -173,7 +170,7 @@ async def test_purge_old_states(
assert states.count() == 0
assert state_attributes.count() == 0
assert "test.recorder2" not in instance.states_manager._last_committed_id
assert "test.recorder2" not in recorder_mock.states_manager._last_committed_id
# Add some more states
await _add_test_states(hass)
@ -187,27 +184,22 @@ async def test_purge_old_states(
events = session.query(Events).filter(Events.event_type == "state_changed")
assert events.count() == 0
assert "test.recorder2" in instance.states_manager._last_committed_id
assert "test.recorder2" in recorder_mock.states_manager._last_committed_id
state_attributes = session.query(StateAttributes)
assert state_attributes.count() == 3
@pytest.mark.skip_on_db_engine(["mysql", "postgresql"])
@pytest.mark.usefixtures("skip_by_db_engine")
@pytest.mark.usefixtures("recorder_mock", "skip_by_db_engine")
async def test_purge_old_states_encouters_database_corruption(
async_setup_recorder_instance: RecorderInstanceGenerator,
hass: HomeAssistant,
recorder_db_url: str,
) -> None:
"""Test database image image is malformed while deleting old states.
This test is specific for SQLite, wiping the database on error only happens
with SQLite.
"""
await async_setup_recorder_instance(hass)
await _add_test_states(hass)
await async_wait_recording_done(hass)
@ -223,7 +215,7 @@ async def test_purge_old_states_encouters_database_corruption(
side_effect=sqlite3_exception,
),
):
await hass.services.async_call(recorder.DOMAIN, SERVICE_PURGE, {"keep_days": 0})
await hass.services.async_call(RECORDER_DOMAIN, SERVICE_PURGE, {"keep_days": 0})
await hass.async_block_till_done()
await async_wait_recording_done(hass)
@ -236,13 +228,11 @@ async def test_purge_old_states_encouters_database_corruption(
async def test_purge_old_states_encounters_temporary_mysql_error(
async_setup_recorder_instance: RecorderInstanceGenerator,
hass: HomeAssistant,
recorder_mock: Recorder,
caplog: pytest.LogCaptureFixture,
) -> None:
"""Test retry on specific mysql operational errors."""
instance = await async_setup_recorder_instance(hass)
await _add_test_states(hass)
await async_wait_recording_done(hass)
@ -255,9 +245,9 @@ async def test_purge_old_states_encounters_temporary_mysql_error(
"homeassistant.components.recorder.purge._purge_old_recorder_runs",
side_effect=[mysql_exception, None],
),
patch.object(instance.engine.dialect, "name", "mysql"),
patch.object(recorder_mock.engine.dialect, "name", "mysql"),
):
await hass.services.async_call(recorder.DOMAIN, SERVICE_PURGE, {"keep_days": 0})
await hass.services.async_call(RECORDER_DOMAIN, SERVICE_PURGE, {"keep_days": 0})
await hass.async_block_till_done()
await async_wait_recording_done(hass)
await async_wait_recording_done(hass)
@ -266,14 +256,12 @@ async def test_purge_old_states_encounters_temporary_mysql_error(
assert sleep_mock.called
@pytest.mark.usefixtures("recorder_mock")
async def test_purge_old_states_encounters_operational_error(
async_setup_recorder_instance: RecorderInstanceGenerator,
hass: HomeAssistant,
caplog: pytest.LogCaptureFixture,
) -> None:
"""Test error on operational errors that are not mysql does not retry."""
await async_setup_recorder_instance(hass)
await _add_test_states(hass)
await async_wait_recording_done(hass)
@ -283,7 +271,7 @@ async def test_purge_old_states_encounters_operational_error(
"homeassistant.components.recorder.purge._purge_old_recorder_runs",
side_effect=exception,
):
await hass.services.async_call(recorder.DOMAIN, SERVICE_PURGE, {"keep_days": 0})
await hass.services.async_call(RECORDER_DOMAIN, SERVICE_PURGE, {"keep_days": 0})
await hass.async_block_till_done()
await async_wait_recording_done(hass)
await async_wait_recording_done(hass)
@ -292,12 +280,8 @@ async def test_purge_old_states_encounters_operational_error(
assert "Error executing purge" in caplog.text
async def test_purge_old_events(
async_setup_recorder_instance: RecorderInstanceGenerator, hass: HomeAssistant
) -> None:
async def test_purge_old_events(hass: HomeAssistant, recorder_mock: Recorder) -> None:
"""Test deleting old events."""
instance = await async_setup_recorder_instance(hass)
await _add_test_events(hass)
with session_scope(hass=hass) as session:
@ -310,7 +294,7 @@ async def test_purge_old_events(
# run purge_old_data()
finished = purge_old_data(
instance,
recorder_mock,
purge_before,
repack=False,
events_batch_size=1,
@ -322,7 +306,7 @@ async def test_purge_old_events(
# we should only have 2 events left
finished = purge_old_data(
instance,
recorder_mock,
purge_before,
repack=False,
events_batch_size=1,
@ -333,11 +317,9 @@ async def test_purge_old_events(
async def test_purge_old_recorder_runs(
async_setup_recorder_instance: RecorderInstanceGenerator, hass: HomeAssistant
hass: HomeAssistant, recorder_mock: Recorder
) -> None:
"""Test deleting old recorder runs keeps current run."""
instance = await async_setup_recorder_instance(hass)
await _add_test_recorder_runs(hass)
# make sure we start with 7 recorder runs
@ -349,7 +331,7 @@ async def test_purge_old_recorder_runs(
# run purge_old_data()
finished = purge_old_data(
instance,
recorder_mock,
purge_before,
repack=False,
events_batch_size=1,
@ -358,7 +340,7 @@ async def test_purge_old_recorder_runs(
assert not finished
finished = purge_old_data(
instance,
recorder_mock,
purge_before,
repack=False,
events_batch_size=1,
@ -369,11 +351,9 @@ async def test_purge_old_recorder_runs(
async def test_purge_old_statistics_runs(
async_setup_recorder_instance: RecorderInstanceGenerator, hass: HomeAssistant
hass: HomeAssistant, recorder_mock: Recorder
) -> None:
"""Test deleting old statistics runs keeps the latest run."""
instance = await async_setup_recorder_instance(hass)
await _add_test_statistics_runs(hass)
# make sure we start with 7 statistics runs
@ -384,17 +364,17 @@ async def test_purge_old_statistics_runs(
purge_before = dt_util.utcnow()
# run purge_old_data()
finished = purge_old_data(instance, purge_before, repack=False)
finished = purge_old_data(recorder_mock, purge_before, repack=False)
assert not finished
finished = purge_old_data(instance, purge_before, repack=False)
finished = purge_old_data(recorder_mock, purge_before, repack=False)
assert finished
assert statistics_runs.count() == 1
@pytest.mark.parametrize("use_sqlite", [True, False], indirect=True)
@pytest.mark.usefixtures("recorder_mock")
async def test_purge_method(
async_setup_recorder_instance: RecorderInstanceGenerator,
hass: HomeAssistant,
caplog: pytest.LogCaptureFixture,
use_sqlite: bool,
@ -412,8 +392,6 @@ async def test_purge_method(
assert run1.run_id == run2.run_id
assert run1.start == run2.start
await async_setup_recorder_instance(hass)
service_data = {"keep_days": 4}
await _add_test_events(hass)
await _add_test_states(hass)
@ -519,8 +497,8 @@ async def test_purge_method(
@pytest.mark.parametrize("use_sqlite", [True, False], indirect=True)
async def test_purge_edge_case(
async_setup_recorder_instance: RecorderInstanceGenerator,
hass: HomeAssistant,
recorder_mock: Recorder,
use_sqlite: bool,
) -> None:
"""Test states and events are purged even if they occurred shortly before purge_before."""
@ -554,11 +532,9 @@ async def test_purge_edge_case(
attributes_id=1002,
)
)
instance = recorder.get_instance(hass)
convert_pending_events_to_event_types(instance, session)
convert_pending_states_to_meta(instance, session)
convert_pending_events_to_event_types(recorder_mock, session)
convert_pending_states_to_meta(recorder_mock, session)
await async_setup_recorder_instance(hass, None)
await async_wait_purge_done(hass)
service_data = {"keep_days": 2}
@ -577,7 +553,7 @@ async def test_purge_edge_case(
)
assert events.count() == 1
await hass.services.async_call(recorder.DOMAIN, SERVICE_PURGE, service_data)
await hass.services.async_call(RECORDER_DOMAIN, SERVICE_PURGE, service_data)
await hass.async_block_till_done()
await async_recorder_block_till_done(hass)
@ -592,10 +568,7 @@ async def test_purge_edge_case(
assert events.count() == 0
async def test_purge_cutoff_date(
async_setup_recorder_instance: RecorderInstanceGenerator,
hass: HomeAssistant,
) -> None:
async def test_purge_cutoff_date(hass: HomeAssistant, recorder_mock: Recorder) -> None:
"""Test states and events are purged only if they occurred before "now() - keep_days"."""
async def _add_db_entries(hass: HomeAssistant, cutoff: datetime, rows: int) -> None:
@ -658,10 +631,9 @@ async def test_purge_cutoff_date(
attributes_id=1000 + row,
)
)
convert_pending_events_to_event_types(instance, session)
convert_pending_states_to_meta(instance, session)
convert_pending_events_to_event_types(recorder_mock, session)
convert_pending_states_to_meta(recorder_mock, session)
instance = await async_setup_recorder_instance(hass, None)
await async_wait_purge_done(hass)
service_data = {"keep_days": 2}
@ -697,7 +669,7 @@ async def test_purge_cutoff_date(
== 1
)
instance.queue_task(PurgeTask(cutoff, repack=False, apply_filter=False))
recorder_mock.queue_task(PurgeTask(cutoff, repack=False, apply_filter=False))
await hass.async_block_till_done()
await async_recorder_block_till_done(hass)
await async_wait_purge_done(hass)
@ -738,7 +710,9 @@ async def test_purge_cutoff_date(
)
# Make sure we can purge everything
instance.queue_task(PurgeTask(dt_util.utcnow(), repack=False, apply_filter=False))
recorder_mock.queue_task(
PurgeTask(dt_util.utcnow(), repack=False, apply_filter=False)
)
await async_recorder_block_till_done(hass)
await async_wait_purge_done(hass)
@ -749,7 +723,9 @@ async def test_purge_cutoff_date(
assert state_attributes.count() == 0
# Make sure we can purge everything when the db is already empty
instance.queue_task(PurgeTask(dt_util.utcnow(), repack=False, apply_filter=False))
recorder_mock.queue_task(
PurgeTask(dt_util.utcnow(), repack=False, apply_filter=False)
)
await async_recorder_block_till_done(hass)
await async_wait_purge_done(hass)
@ -761,15 +737,16 @@ async def test_purge_cutoff_date(
@pytest.mark.parametrize("use_sqlite", [True, False], indirect=True)
@pytest.mark.parametrize(
"recorder_config", [{"exclude": {"entities": ["sensor.excluded"]}}]
)
async def test_purge_filtered_states(
async_setup_recorder_instance: RecorderInstanceGenerator,
hass: HomeAssistant,
recorder_mock: Recorder,
use_sqlite: bool,
) -> None:
"""Test filtered states are purged."""
config: ConfigType = {"exclude": {"entities": ["sensor.excluded"]}}
instance = await async_setup_recorder_instance(hass, config)
assert instance.entity_filter("sensor.excluded") is False
assert recorder_mock.entity_filter("sensor.excluded") is False
def _add_db_entries(hass: HomeAssistant) -> None:
with session_scope(hass=hass) as session:
@ -852,8 +829,8 @@ async def test_purge_filtered_states(
time_fired_ts=dt_util.utc_to_timestamp(timestamp),
)
)
convert_pending_states_to_meta(instance, session)
convert_pending_events_to_event_types(instance, session)
convert_pending_states_to_meta(recorder_mock, session)
convert_pending_events_to_event_types(recorder_mock, session)
service_data = {"keep_days": 10}
_add_db_entries(hass)
@ -867,7 +844,7 @@ async def test_purge_filtered_states(
assert events_keep.count() == 1
# Normal purge doesn't remove excluded entities
await hass.services.async_call(recorder.DOMAIN, SERVICE_PURGE, service_data)
await hass.services.async_call(RECORDER_DOMAIN, SERVICE_PURGE, service_data)
await hass.async_block_till_done()
await async_recorder_block_till_done(hass)
@ -883,7 +860,7 @@ async def test_purge_filtered_states(
# Test with 'apply_filter' = True
service_data["apply_filter"] = True
await hass.services.async_call(recorder.DOMAIN, SERVICE_PURGE, service_data)
await hass.services.async_call(RECORDER_DOMAIN, SERVICE_PURGE, service_data)
await hass.async_block_till_done()
await async_recorder_block_till_done(hass)
@ -931,7 +908,7 @@ async def test_purge_filtered_states(
assert session.query(StateAttributes).count() == 11
# Do it again to make sure nothing changes
await hass.services.async_call(recorder.DOMAIN, SERVICE_PURGE, service_data)
await hass.services.async_call(RECORDER_DOMAIN, SERVICE_PURGE, service_data)
await async_recorder_block_till_done(hass)
await async_wait_purge_done(hass)
@ -943,7 +920,7 @@ async def test_purge_filtered_states(
assert session.query(StateAttributes).count() == 11
service_data = {"keep_days": 0}
await hass.services.async_call(recorder.DOMAIN, SERVICE_PURGE, service_data)
await hass.services.async_call(RECORDER_DOMAIN, SERVICE_PURGE, service_data)
await async_recorder_block_till_done(hass)
await async_wait_purge_done(hass)
@ -956,15 +933,16 @@ async def test_purge_filtered_states(
@pytest.mark.parametrize("use_sqlite", [True, False], indirect=True)
@pytest.mark.parametrize(
"recorder_config", [{"exclude": {"entities": ["sensor.excluded"]}}]
)
async def test_purge_filtered_states_to_empty(
async_setup_recorder_instance: RecorderInstanceGenerator,
hass: HomeAssistant,
recorder_mock: Recorder,
use_sqlite: bool,
) -> None:
"""Test filtered states are purged all the way to an empty db."""
config: ConfigType = {"exclude": {"entities": ["sensor.excluded"]}}
instance = await async_setup_recorder_instance(hass, config)
assert instance.entity_filter("sensor.excluded") is False
assert recorder_mock.entity_filter("sensor.excluded") is False
def _add_db_entries(hass: HomeAssistant) -> None:
with session_scope(hass=hass) as session:
@ -979,7 +957,7 @@ async def test_purge_filtered_states_to_empty(
timestamp,
event_id * days,
)
convert_pending_states_to_meta(instance, session)
convert_pending_states_to_meta(recorder_mock, session)
service_data = {"keep_days": 10}
_add_db_entries(hass)
@ -992,7 +970,7 @@ async def test_purge_filtered_states_to_empty(
# Test with 'apply_filter' = True
service_data["apply_filter"] = True
await hass.services.async_call(recorder.DOMAIN, SERVICE_PURGE, service_data)
await hass.services.async_call(RECORDER_DOMAIN, SERVICE_PURGE, service_data)
await async_recorder_block_till_done(hass)
await async_wait_purge_done(hass)
@ -1004,21 +982,22 @@ async def test_purge_filtered_states_to_empty(
# Do it again to make sure nothing changes
# Why do we do this? Should we check the end result?
await hass.services.async_call(recorder.DOMAIN, SERVICE_PURGE, service_data)
await hass.services.async_call(RECORDER_DOMAIN, SERVICE_PURGE, service_data)
await async_recorder_block_till_done(hass)
await async_wait_purge_done(hass)
@pytest.mark.parametrize("use_sqlite", [True, False], indirect=True)
@pytest.mark.parametrize(
"recorder_config", [{"exclude": {"entities": ["sensor.old_format"]}}]
)
async def test_purge_without_state_attributes_filtered_states_to_empty(
async_setup_recorder_instance: RecorderInstanceGenerator,
hass: HomeAssistant,
recorder_mock: Recorder,
use_sqlite: bool,
) -> None:
"""Test filtered legacy states without state attributes are purged all the way to an empty db."""
config: ConfigType = {"exclude": {"entities": ["sensor.old_format"]}}
instance = await async_setup_recorder_instance(hass, config)
assert instance.entity_filter("sensor.old_format") is False
assert recorder_mock.entity_filter("sensor.old_format") is False
def _add_db_entries(hass: HomeAssistant) -> None:
with session_scope(hass=hass) as session:
@ -1055,8 +1034,8 @@ async def test_purge_without_state_attributes_filtered_states_to_empty(
time_fired_ts=dt_util.utc_to_timestamp(timestamp),
)
)
convert_pending_states_to_meta(instance, session)
convert_pending_events_to_event_types(instance, session)
convert_pending_states_to_meta(recorder_mock, session)
convert_pending_events_to_event_types(recorder_mock, session)
service_data = {"keep_days": 10}
_add_db_entries(hass)
@ -1069,7 +1048,7 @@ async def test_purge_without_state_attributes_filtered_states_to_empty(
# Test with 'apply_filter' = True
service_data["apply_filter"] = True
await hass.services.async_call(recorder.DOMAIN, SERVICE_PURGE, service_data)
await hass.services.async_call(RECORDER_DOMAIN, SERVICE_PURGE, service_data)
await async_recorder_block_till_done(hass)
await async_wait_purge_done(hass)
@ -1081,18 +1060,18 @@ async def test_purge_without_state_attributes_filtered_states_to_empty(
# Do it again to make sure nothing changes
# Why do we do this? Should we check the end result?
await hass.services.async_call(recorder.DOMAIN, SERVICE_PURGE, service_data)
await hass.services.async_call(RECORDER_DOMAIN, SERVICE_PURGE, service_data)
await async_recorder_block_till_done(hass)
await async_wait_purge_done(hass)
@pytest.mark.parametrize(
"recorder_config", [{"exclude": {"event_types": ["EVENT_PURGE"]}}]
)
async def test_purge_filtered_events(
async_setup_recorder_instance: RecorderInstanceGenerator,
hass: HomeAssistant,
hass: HomeAssistant, recorder_mock: Recorder
) -> None:
"""Test filtered events are purged."""
config: ConfigType = {"exclude": {"event_types": ["EVENT_PURGE"]}}
instance = await async_setup_recorder_instance(hass, config)
await async_wait_recording_done(hass)
def _add_db_entries(hass: HomeAssistant) -> None:
@ -1121,11 +1100,11 @@ async def test_purge_filtered_events(
timestamp,
event_id,
)
convert_pending_events_to_event_types(instance, session)
convert_pending_states_to_meta(instance, session)
convert_pending_events_to_event_types(recorder_mock, session)
convert_pending_states_to_meta(recorder_mock, session)
service_data = {"keep_days": 10}
await instance.async_add_executor_job(_add_db_entries, hass)
await recorder_mock.async_add_executor_job(_add_db_entries, hass)
await async_wait_recording_done(hass)
with session_scope(hass=hass, read_only=True) as session:
@ -1137,7 +1116,7 @@ async def test_purge_filtered_events(
assert states.count() == 10
# Normal purge doesn't remove excluded events
await hass.services.async_call(recorder.DOMAIN, SERVICE_PURGE, service_data)
await hass.services.async_call(RECORDER_DOMAIN, SERVICE_PURGE, service_data)
await hass.async_block_till_done()
await async_recorder_block_till_done(hass)
@ -1153,7 +1132,7 @@ async def test_purge_filtered_events(
# Test with 'apply_filter' = True
service_data["apply_filter"] = True
await hass.services.async_call(recorder.DOMAIN, SERVICE_PURGE, service_data)
await hass.services.async_call(RECORDER_DOMAIN, SERVICE_PURGE, service_data)
await hass.async_block_till_done()
await async_recorder_block_till_done(hass)
@ -1171,23 +1150,26 @@ async def test_purge_filtered_events(
assert states.count() == 10
async def test_purge_filtered_events_state_changed(
async_setup_recorder_instance: RecorderInstanceGenerator,
hass: HomeAssistant,
) -> None:
"""Test filtered state_changed events are purged. This should also remove all states."""
config: ConfigType = {
@pytest.mark.parametrize(
"recorder_config",
[
{
"exclude": {
"event_types": ["excluded_event"],
"entities": ["sensor.excluded", "sensor.old_format"],
}
}
instance = await async_setup_recorder_instance(hass, config)
],
)
async def test_purge_filtered_events_state_changed(
hass: HomeAssistant, recorder_mock: Recorder
) -> None:
"""Test filtered state_changed events are purged. This should also remove all states."""
# Assert entity_id is NOT excluded
assert instance.entity_filter("sensor.excluded") is False
assert instance.entity_filter("sensor.old_format") is False
assert instance.entity_filter("sensor.keep") is True
assert "excluded_event" in instance.exclude_event_types
assert recorder_mock.entity_filter("sensor.excluded") is False
assert recorder_mock.entity_filter("sensor.old_format") is False
assert recorder_mock.entity_filter("sensor.keep") is True
assert "excluded_event" in recorder_mock.exclude_event_types
def _add_db_entries(hass: HomeAssistant) -> None:
with session_scope(hass=hass) as session:
@ -1260,8 +1242,8 @@ async def test_purge_filtered_events_state_changed(
last_updated_ts=dt_util.utc_to_timestamp(timestamp),
)
)
convert_pending_events_to_event_types(instance, session)
convert_pending_states_to_meta(instance, session)
convert_pending_events_to_event_types(recorder_mock, session)
convert_pending_states_to_meta(recorder_mock, session)
service_data = {"keep_days": 10, "apply_filter": True}
_add_db_entries(hass)
@ -1279,7 +1261,7 @@ async def test_purge_filtered_events_state_changed(
assert events_purge.count() == 1
assert states.count() == 64
await hass.services.async_call(recorder.DOMAIN, SERVICE_PURGE, service_data)
await hass.services.async_call(RECORDER_DOMAIN, SERVICE_PURGE, service_data)
await hass.async_block_till_done()
for _ in range(4):
@ -1313,11 +1295,8 @@ async def test_purge_filtered_events_state_changed(
) # should have been kept
async def test_purge_entities(
async_setup_recorder_instance: RecorderInstanceGenerator, hass: HomeAssistant
) -> None:
async def test_purge_entities(hass: HomeAssistant, recorder_mock: Recorder) -> None:
"""Test purging of specific entities."""
instance = await async_setup_recorder_instance(hass)
async def _purge_entities(hass, entity_ids, domains, entity_globs):
service_data = {
@ -1327,7 +1306,7 @@ async def test_purge_entities(
}
await hass.services.async_call(
recorder.DOMAIN, SERVICE_PURGE_ENTITIES, service_data
RECORDER_DOMAIN, SERVICE_PURGE_ENTITIES, service_data
)
await hass.async_block_till_done()
@ -1365,8 +1344,8 @@ async def test_purge_entities(
timestamp,
event_id * days,
)
convert_pending_states_to_meta(instance, session)
convert_pending_events_to_event_types(instance, session)
convert_pending_states_to_meta(recorder_mock, session)
convert_pending_events_to_event_types(recorder_mock, session)
def _add_keep_records(hass: HomeAssistant) -> None:
with session_scope(hass=hass) as session:
@ -1380,8 +1359,8 @@ async def test_purge_entities(
timestamp,
event_id,
)
convert_pending_states_to_meta(instance, session)
convert_pending_events_to_event_types(instance, session)
convert_pending_states_to_meta(recorder_mock, session)
convert_pending_events_to_event_types(recorder_mock, session)
_add_purge_records(hass)
_add_keep_records(hass)
@ -1659,15 +1638,14 @@ def _add_state_with_state_attributes(
@pytest.mark.timeout(30)
async def test_purge_many_old_events(
async_setup_recorder_instance: RecorderInstanceGenerator, hass: HomeAssistant
hass: HomeAssistant, recorder_mock: Recorder
) -> None:
"""Test deleting old events."""
old_events_count = 5
instance = await async_setup_recorder_instance(hass)
with (
patch.object(instance, "max_bind_vars", old_events_count),
patch.object(instance.database_engine, "max_bind_vars", old_events_count),
patch.object(recorder_mock, "max_bind_vars", old_events_count),
patch.object(recorder_mock.database_engine, "max_bind_vars", old_events_count),
):
await _add_test_events(hass, old_events_count)
@ -1681,7 +1659,7 @@ async def test_purge_many_old_events(
# run purge_old_data()
finished = purge_old_data(
instance,
recorder_mock,
purge_before,
repack=False,
states_batch_size=3,
@ -1692,7 +1670,7 @@ async def test_purge_many_old_events(
# we should only have 2 groups of events left
finished = purge_old_data(
instance,
recorder_mock,
purge_before,
repack=False,
states_batch_size=3,
@ -1703,7 +1681,7 @@ async def test_purge_many_old_events(
# we should now purge everything
finished = purge_old_data(
instance,
recorder_mock,
dt_util.utcnow(),
repack=False,
states_batch_size=20,
@ -1714,11 +1692,10 @@ async def test_purge_many_old_events(
async def test_purge_old_events_purges_the_event_type_ids(
async_setup_recorder_instance: RecorderInstanceGenerator, hass: HomeAssistant
hass: HomeAssistant, recorder_mock: Recorder
) -> None:
"""Test deleting old events purges event type ids."""
instance = await async_setup_recorder_instance(hass)
assert instance.event_type_manager.active is True
assert recorder_mock.event_type_manager.active is True
utcnow = dt_util.utcnow()
five_days_ago = utcnow - timedelta(days=5)
@ -1762,7 +1739,7 @@ async def test_purge_old_events_purges_the_event_type_ids(
time_fired_ts=dt_util.utc_to_timestamp(timestamp),
)
)
return instance.event_type_manager.get_many(
return recorder_mock.event_type_manager.get_many(
[
"EVENT_TEST_AUTOPURGE",
"EVENT_TEST_PURGE",
@ -1772,7 +1749,7 @@ async def test_purge_old_events_purges_the_event_type_ids(
session,
)
event_type_to_id = await instance.async_add_executor_job(_insert_events)
event_type_to_id = await recorder_mock.async_add_executor_job(_insert_events)
test_event_type_ids = event_type_to_id.values()
with session_scope(hass=hass) as session:
events = session.query(Events).where(
@ -1787,7 +1764,7 @@ async def test_purge_old_events_purges_the_event_type_ids(
# run purge_old_data()
finished = purge_old_data(
instance,
recorder_mock,
far_past,
repack=False,
)
@ -1796,12 +1773,12 @@ async def test_purge_old_events_purges_the_event_type_ids(
# We should remove the unused event type
assert event_types.count() == 3
assert "EVENT_TEST_UNUSED" not in instance.event_type_manager._id_map
assert "EVENT_TEST_UNUSED" not in recorder_mock.event_type_manager._id_map
# we should only have 10 events left since
# only one event type was recorded now
finished = purge_old_data(
instance,
recorder_mock,
utcnow,
repack=False,
)
@ -1811,7 +1788,7 @@ async def test_purge_old_events_purges_the_event_type_ids(
# Purge everything
finished = purge_old_data(
instance,
recorder_mock,
utcnow + timedelta(seconds=1),
repack=False,
)
@ -1821,11 +1798,10 @@ async def test_purge_old_events_purges_the_event_type_ids(
async def test_purge_old_states_purges_the_state_metadata_ids(
async_setup_recorder_instance: RecorderInstanceGenerator, hass: HomeAssistant
hass: HomeAssistant, recorder_mock: Recorder
) -> None:
"""Test deleting old states purges state metadata_ids."""
instance = await async_setup_recorder_instance(hass)
assert instance.states_meta_manager.active is True
assert recorder_mock.states_meta_manager.active is True
utcnow = dt_util.utcnow()
five_days_ago = utcnow - timedelta(days=5)
@ -1869,13 +1845,15 @@ async def test_purge_old_states_purges_the_state_metadata_ids(
last_updated_ts=dt_util.utc_to_timestamp(timestamp),
)
)
return instance.states_meta_manager.get_many(
return recorder_mock.states_meta_manager.get_many(
["sensor.one", "sensor.two", "sensor.three", "sensor.unused"],
session,
True,
)
entity_id_to_metadata_id = await instance.async_add_executor_job(_insert_states)
entity_id_to_metadata_id = await recorder_mock.async_add_executor_job(
_insert_states
)
test_metadata_ids = entity_id_to_metadata_id.values()
with session_scope(hass=hass) as session:
states = session.query(States).where(States.metadata_id.in_(test_metadata_ids))
@ -1888,7 +1866,7 @@ async def test_purge_old_states_purges_the_state_metadata_ids(
# run purge_old_data()
finished = purge_old_data(
instance,
recorder_mock,
far_past,
repack=False,
)
@ -1897,12 +1875,12 @@ async def test_purge_old_states_purges_the_state_metadata_ids(
# We should remove the unused entity_id
assert states_meta.count() == 3
assert "sensor.unused" not in instance.event_type_manager._id_map
assert "sensor.unused" not in recorder_mock.event_type_manager._id_map
# we should only have 10 states left since
# only one event type was recorded now
finished = purge_old_data(
instance,
recorder_mock,
utcnow,
repack=False,
)
@ -1912,7 +1890,7 @@ async def test_purge_old_states_purges_the_state_metadata_ids(
# Purge everything
finished = purge_old_data(
instance,
recorder_mock,
utcnow + timedelta(seconds=1),
repack=False,
)
@ -1922,11 +1900,9 @@ async def test_purge_old_states_purges_the_state_metadata_ids(
async def test_purge_entities_keep_days(
async_setup_recorder_instance: RecorderInstanceGenerator,
hass: HomeAssistant,
hass: HomeAssistant, recorder_mock: Recorder
) -> None:
"""Test purging states with an entity filter and keep_days."""
instance = await async_setup_recorder_instance(hass, {})
await hass.async_block_till_done()
await async_wait_recording_done(hass)
start = dt_util.utcnow()
@ -1948,7 +1924,7 @@ async def test_purge_entities_keep_days(
hass.states.async_set("sensor.keep", "now")
await async_recorder_block_till_done(hass)
states = await instance.async_add_executor_job(
states = await recorder_mock.async_add_executor_job(
get_significant_states,
hass,
one_month_ago,
@ -1959,7 +1935,7 @@ async def test_purge_entities_keep_days(
assert len(states["sensor.purge"]) == 3
await hass.services.async_call(
recorder.DOMAIN,
RECORDER_DOMAIN,
SERVICE_PURGE_ENTITIES,
{
"entity_id": "sensor.purge",
@ -1969,7 +1945,7 @@ async def test_purge_entities_keep_days(
await async_recorder_block_till_done(hass)
await async_wait_purge_done(hass)
states = await instance.async_add_executor_job(
states = await recorder_mock.async_add_executor_job(
get_significant_states,
hass,
one_month_ago,
@ -1980,7 +1956,7 @@ async def test_purge_entities_keep_days(
assert len(states["sensor.purge"]) == 1
await hass.services.async_call(
recorder.DOMAIN,
RECORDER_DOMAIN,
SERVICE_PURGE_ENTITIES,
{
"entity_id": "sensor.purge",
@ -1989,7 +1965,7 @@ async def test_purge_entities_keep_days(
await async_recorder_block_till_done(hass)
await async_wait_purge_done(hass)
states = await instance.async_add_executor_job(
states = await recorder_mock.async_add_executor_job(
get_significant_states,
hass,
one_month_ago,

View file

@ -12,8 +12,11 @@ from sqlalchemy import text, update
from sqlalchemy.exc import DatabaseError, OperationalError
from sqlalchemy.orm.session import Session
from homeassistant.components import recorder
from homeassistant.components.recorder import migration
from homeassistant.components.recorder import (
DOMAIN as RECORDER_DOMAIN,
Recorder,
migration,
)
from homeassistant.components.recorder.const import SupportedDialect
from homeassistant.components.recorder.history import get_significant_states
from homeassistant.components.recorder.purge import purge_old_data
@ -47,6 +50,13 @@ from .db_schema_32 import (
from tests.typing import RecorderInstanceGenerator
@pytest.fixture
async def mock_recorder_before_hass(
async_test_recorder: RecorderInstanceGenerator,
) -> None:
"""Set up recorder."""
@pytest.fixture(autouse=True)
def db_schema_32():
"""Fixture to initialize the db with the old schema 32."""
@ -66,11 +76,8 @@ def mock_use_sqlite(request: pytest.FixtureRequest) -> Generator[None]:
yield
async def test_purge_old_states(
async_setup_recorder_instance: RecorderInstanceGenerator, hass: HomeAssistant
) -> None:
async def test_purge_old_states(hass: HomeAssistant, recorder_mock: Recorder) -> None:
"""Test deleting old states."""
instance = await async_setup_recorder_instance(hass)
await async_attach_db_engine(hass)
await _add_test_states(hass)
@ -87,13 +94,13 @@ async def test_purge_old_states(
events = session.query(Events).filter(Events.event_type == "state_changed")
assert events.count() == 0
assert "test.recorder2" in instance.states_manager._last_committed_id
assert "test.recorder2" in recorder_mock.states_manager._last_committed_id
purge_before = dt_util.utcnow() - timedelta(days=4)
# run purge_old_data()
finished = purge_old_data(
instance,
recorder_mock,
purge_before,
states_batch_size=1,
events_batch_size=1,
@ -103,7 +110,7 @@ async def test_purge_old_states(
assert states.count() == 2
assert state_attributes.count() == 1
assert "test.recorder2" in instance.states_manager._last_committed_id
assert "test.recorder2" in recorder_mock.states_manager._last_committed_id
states_after_purge = list(session.query(States))
# Since these states are deleted in batches, we can't guarantee the order
@ -115,17 +122,17 @@ async def test_purge_old_states(
assert dontpurgeme_5.old_state_id == dontpurgeme_4.state_id
assert dontpurgeme_4.old_state_id is None
finished = purge_old_data(instance, purge_before, repack=False)
finished = purge_old_data(recorder_mock, purge_before, repack=False)
assert finished
assert states.count() == 2
assert state_attributes.count() == 1
assert "test.recorder2" in instance.states_manager._last_committed_id
assert "test.recorder2" in recorder_mock.states_manager._last_committed_id
# run purge_old_data again
purge_before = dt_util.utcnow()
finished = purge_old_data(
instance,
recorder_mock,
purge_before,
states_batch_size=1,
events_batch_size=1,
@ -135,7 +142,7 @@ async def test_purge_old_states(
assert states.count() == 0
assert state_attributes.count() == 0
assert "test.recorder2" not in instance.states_manager._last_committed_id
assert "test.recorder2" not in recorder_mock.states_manager._last_committed_id
# Add some more states
await _add_test_states(hass)
@ -149,26 +156,22 @@ async def test_purge_old_states(
events = session.query(Events).filter(Events.event_type == "state_changed")
assert events.count() == 0
assert "test.recorder2" in instance.states_manager._last_committed_id
assert "test.recorder2" in recorder_mock.states_manager._last_committed_id
state_attributes = session.query(StateAttributes)
assert state_attributes.count() == 3
@pytest.mark.skip_on_db_engine(["mysql", "postgresql"])
@pytest.mark.usefixtures("skip_by_db_engine")
@pytest.mark.usefixtures("recorder_mock", "skip_by_db_engine")
async def test_purge_old_states_encouters_database_corruption(
async_setup_recorder_instance: RecorderInstanceGenerator,
hass: HomeAssistant,
recorder_db_url: str,
) -> None:
"""Test database image image is malformed while deleting old states.
This test is specific for SQLite, wiping the database on error only happens
with SQLite.
"""
await async_setup_recorder_instance(hass)
await async_attach_db_engine(hass)
await _add_test_states(hass)
@ -186,7 +189,7 @@ async def test_purge_old_states_encouters_database_corruption(
side_effect=sqlite3_exception,
),
):
await hass.services.async_call(recorder.DOMAIN, SERVICE_PURGE, {"keep_days": 0})
await hass.services.async_call(RECORDER_DOMAIN, SERVICE_PURGE, {"keep_days": 0})
await hass.async_block_till_done()
await async_wait_recording_done(hass)
@ -199,12 +202,11 @@ async def test_purge_old_states_encouters_database_corruption(
async def test_purge_old_states_encounters_temporary_mysql_error(
async_setup_recorder_instance: RecorderInstanceGenerator,
hass: HomeAssistant,
recorder_mock: Recorder,
caplog: pytest.LogCaptureFixture,
) -> None:
"""Test retry on specific mysql operational errors."""
instance = await async_setup_recorder_instance(hass)
await async_attach_db_engine(hass)
await _add_test_states(hass)
@ -219,9 +221,9 @@ async def test_purge_old_states_encounters_temporary_mysql_error(
"homeassistant.components.recorder.purge._purge_old_recorder_runs",
side_effect=[mysql_exception, None],
),
patch.object(instance.engine.dialect, "name", "mysql"),
patch.object(recorder_mock.engine.dialect, "name", "mysql"),
):
await hass.services.async_call(recorder.DOMAIN, SERVICE_PURGE, {"keep_days": 0})
await hass.services.async_call(RECORDER_DOMAIN, SERVICE_PURGE, {"keep_days": 0})
await hass.async_block_till_done()
await async_wait_recording_done(hass)
await async_wait_recording_done(hass)
@ -230,13 +232,12 @@ async def test_purge_old_states_encounters_temporary_mysql_error(
assert sleep_mock.called
@pytest.mark.usefixtures("recorder_mock")
async def test_purge_old_states_encounters_operational_error(
async_setup_recorder_instance: RecorderInstanceGenerator,
hass: HomeAssistant,
caplog: pytest.LogCaptureFixture,
) -> None:
"""Test error on operational errors that are not mysql does not retry."""
await async_setup_recorder_instance(hass)
await async_attach_db_engine(hass)
await _add_test_states(hass)
@ -248,7 +249,7 @@ async def test_purge_old_states_encounters_operational_error(
"homeassistant.components.recorder.purge._purge_old_recorder_runs",
side_effect=exception,
):
await hass.services.async_call(recorder.DOMAIN, SERVICE_PURGE, {"keep_days": 0})
await hass.services.async_call(RECORDER_DOMAIN, SERVICE_PURGE, {"keep_days": 0})
await hass.async_block_till_done()
await async_wait_recording_done(hass)
await async_wait_recording_done(hass)
@ -257,11 +258,8 @@ async def test_purge_old_states_encounters_operational_error(
assert "Error executing purge" in caplog.text
async def test_purge_old_events(
async_setup_recorder_instance: RecorderInstanceGenerator, hass: HomeAssistant
) -> None:
async def test_purge_old_events(hass: HomeAssistant, recorder_mock: Recorder) -> None:
"""Test deleting old events."""
instance = await async_setup_recorder_instance(hass)
await async_attach_db_engine(hass)
await _add_test_events(hass)
@ -274,7 +272,7 @@ async def test_purge_old_events(
# run purge_old_data()
finished = purge_old_data(
instance,
recorder_mock,
purge_before,
repack=False,
events_batch_size=1,
@ -285,7 +283,7 @@ async def test_purge_old_events(
# we should only have 2 events left
finished = purge_old_data(
instance,
recorder_mock,
purge_before,
repack=False,
events_batch_size=1,
@ -296,10 +294,9 @@ async def test_purge_old_events(
async def test_purge_old_recorder_runs(
async_setup_recorder_instance: RecorderInstanceGenerator, hass: HomeAssistant
hass: HomeAssistant, recorder_mock: Recorder
) -> None:
"""Test deleting old recorder runs keeps current run."""
instance = await async_setup_recorder_instance(hass)
await async_attach_db_engine(hass)
await _add_test_recorder_runs(hass)
@ -313,7 +310,7 @@ async def test_purge_old_recorder_runs(
# run purge_old_data()
finished = purge_old_data(
instance,
recorder_mock,
purge_before,
repack=False,
events_batch_size=1,
@ -322,7 +319,7 @@ async def test_purge_old_recorder_runs(
assert not finished
finished = purge_old_data(
instance,
recorder_mock,
purge_before,
repack=False,
events_batch_size=1,
@ -333,10 +330,9 @@ async def test_purge_old_recorder_runs(
async def test_purge_old_statistics_runs(
async_setup_recorder_instance: RecorderInstanceGenerator, hass: HomeAssistant
hass: HomeAssistant, recorder_mock: Recorder
) -> None:
"""Test deleting old statistics runs keeps the latest run."""
instance = await async_setup_recorder_instance(hass)
await async_attach_db_engine(hass)
await _add_test_statistics_runs(hass)
@ -349,17 +345,17 @@ async def test_purge_old_statistics_runs(
purge_before = dt_util.utcnow()
# run purge_old_data()
finished = purge_old_data(instance, purge_before, repack=False)
finished = purge_old_data(recorder_mock, purge_before, repack=False)
assert not finished
finished = purge_old_data(instance, purge_before, repack=False)
finished = purge_old_data(recorder_mock, purge_before, repack=False)
assert finished
assert statistics_runs.count() == 1
@pytest.mark.parametrize("use_sqlite", [True, False], indirect=True)
@pytest.mark.usefixtures("recorder_mock")
async def test_purge_method(
async_setup_recorder_instance: RecorderInstanceGenerator,
hass: HomeAssistant,
caplog: pytest.LogCaptureFixture,
use_sqlite: bool,
@ -377,7 +373,6 @@ async def test_purge_method(
assert run1.run_id == run2.run_id
assert run1.start == run2.start
await async_setup_recorder_instance(hass)
await async_attach_db_engine(hass)
service_data = {"keep_days": 4}
@ -478,11 +473,8 @@ async def test_purge_method(
@pytest.mark.parametrize("use_sqlite", [True, False], indirect=True)
async def test_purge_edge_case(
async_setup_recorder_instance: RecorderInstanceGenerator,
hass: HomeAssistant,
use_sqlite: bool,
) -> None:
@pytest.mark.usefixtures("recorder_mock")
async def test_purge_edge_case(hass: HomeAssistant, use_sqlite: bool) -> None:
"""Test states and events are purged even if they occurred shortly before purge_before."""
async def _add_db_entries(hass: HomeAssistant, timestamp: datetime) -> None:
@ -515,7 +507,6 @@ async def test_purge_edge_case(
)
)
await async_setup_recorder_instance(hass, None)
await async_attach_db_engine(hass)
await async_wait_purge_done(hass)
@ -534,7 +525,7 @@ async def test_purge_edge_case(
events = session.query(Events).filter(Events.event_type == "EVENT_TEST_PURGE")
assert events.count() == 1
await hass.services.async_call(recorder.DOMAIN, SERVICE_PURGE, service_data)
await hass.services.async_call(RECORDER_DOMAIN, SERVICE_PURGE, service_data)
await hass.async_block_till_done()
await async_recorder_block_till_done(hass)
@ -547,10 +538,7 @@ async def test_purge_edge_case(
assert events.count() == 0
async def test_purge_cutoff_date(
async_setup_recorder_instance: RecorderInstanceGenerator,
hass: HomeAssistant,
) -> None:
async def test_purge_cutoff_date(hass: HomeAssistant, recorder_mock: Recorder) -> None:
"""Test states and events are purged only if they occurred before "now() - keep_days"."""
async def _add_db_entries(hass: HomeAssistant, cutoff: datetime, rows: int) -> None:
@ -614,7 +602,6 @@ async def test_purge_cutoff_date(
)
)
instance = await async_setup_recorder_instance(hass, None)
await async_attach_db_engine(hass)
await async_wait_purge_done(hass)
@ -643,7 +630,7 @@ async def test_purge_cutoff_date(
assert events.filter(Events.event_type == "PURGE").count() == rows - 1
assert events.filter(Events.event_type == "KEEP").count() == 1
instance.queue_task(PurgeTask(cutoff, repack=False, apply_filter=False))
recorder_mock.queue_task(PurgeTask(cutoff, repack=False, apply_filter=False))
await hass.async_block_till_done()
await async_recorder_block_till_done(hass)
await async_wait_purge_done(hass)
@ -674,7 +661,9 @@ async def test_purge_cutoff_date(
assert events.filter(Events.event_type == "KEEP").count() == 1
# Make sure we can purge everything
instance.queue_task(PurgeTask(dt_util.utcnow(), repack=False, apply_filter=False))
recorder_mock.queue_task(
PurgeTask(dt_util.utcnow(), repack=False, apply_filter=False)
)
await async_recorder_block_till_done(hass)
await async_wait_purge_done(hass)
@ -685,7 +674,9 @@ async def test_purge_cutoff_date(
assert state_attributes.count() == 0
# Make sure we can purge everything when the db is already empty
instance.queue_task(PurgeTask(dt_util.utcnow(), repack=False, apply_filter=False))
recorder_mock.queue_task(
PurgeTask(dt_util.utcnow(), repack=False, apply_filter=False)
)
await async_recorder_block_till_done(hass)
await async_wait_purge_done(hass)
@ -938,16 +929,15 @@ def _add_state_and_state_changed_event(
async def test_purge_many_old_events(
async_setup_recorder_instance: RecorderInstanceGenerator, hass: HomeAssistant
hass: HomeAssistant, recorder_mock: Recorder
) -> None:
"""Test deleting old events."""
instance = await async_setup_recorder_instance(hass)
await async_attach_db_engine(hass)
old_events_count = 5
with (
patch.object(instance, "max_bind_vars", old_events_count),
patch.object(instance.database_engine, "max_bind_vars", old_events_count),
patch.object(recorder_mock, "max_bind_vars", old_events_count),
patch.object(recorder_mock.database_engine, "max_bind_vars", old_events_count),
):
await _add_test_events(hass, old_events_count)
@ -959,7 +949,7 @@ async def test_purge_many_old_events(
# run purge_old_data()
finished = purge_old_data(
instance,
recorder_mock,
purge_before,
repack=False,
states_batch_size=3,
@ -970,7 +960,7 @@ async def test_purge_many_old_events(
# we should only have 2 groups of events left
finished = purge_old_data(
instance,
recorder_mock,
purge_before,
repack=False,
states_batch_size=3,
@ -981,7 +971,7 @@ async def test_purge_many_old_events(
# we should now purge everything
finished = purge_old_data(
instance,
recorder_mock,
dt_util.utcnow(),
repack=False,
states_batch_size=20,
@ -992,23 +982,24 @@ async def test_purge_many_old_events(
async def test_purge_can_mix_legacy_and_new_format(
async_setup_recorder_instance: RecorderInstanceGenerator, hass: HomeAssistant
hass: HomeAssistant, recorder_mock: Recorder
) -> None:
"""Test purging with legacy and new events."""
instance = await async_setup_recorder_instance(hass)
await async_attach_db_engine(hass)
await async_wait_recording_done(hass)
# New databases are no longer created with the legacy events index
assert instance.use_legacy_events_index is False
assert recorder_mock.use_legacy_events_index is False
def _recreate_legacy_events_index():
"""Recreate the legacy events index since its no longer created on new instances."""
migration._create_index(instance.get_session, "states", "ix_states_event_id")
instance.use_legacy_events_index = True
migration._create_index(
recorder_mock.get_session, "states", "ix_states_event_id"
)
recorder_mock.use_legacy_events_index = True
await instance.async_add_executor_job(_recreate_legacy_events_index)
assert instance.use_legacy_events_index is True
await recorder_mock.async_add_executor_job(_recreate_legacy_events_index)
assert recorder_mock.use_legacy_events_index is True
utcnow = dt_util.utcnow()
eleven_days_ago = utcnow - timedelta(days=11)
@ -1049,7 +1040,7 @@ async def test_purge_can_mix_legacy_and_new_format(
purge_before = dt_util.utcnow() - timedelta(days=4)
finished = purge_old_data(
instance,
recorder_mock,
purge_before,
repack=False,
)
@ -1060,7 +1051,7 @@ async def test_purge_can_mix_legacy_and_new_format(
# and we switch methods
purge_before = dt_util.utcnow() - timedelta(days=4)
finished = purge_old_data(
instance,
recorder_mock,
purge_before,
repack=False,
events_batch_size=1,
@ -1073,7 +1064,7 @@ async def test_purge_can_mix_legacy_and_new_format(
assert states_with_event_id.count() == 0
assert states_without_event_id.count() == 1
finished = purge_old_data(
instance,
recorder_mock,
purge_before,
repack=False,
events_batch_size=100,
@ -1088,7 +1079,7 @@ async def test_purge_can_mix_legacy_and_new_format(
assert states_with_event_id.count() == 0
assert states_without_event_id.count() == 2
finished = purge_old_data(
instance,
recorder_mock,
purge_before,
repack=False,
)
@ -1102,29 +1093,29 @@ async def test_purge_can_mix_legacy_and_new_format(
@pytest.mark.skip_on_db_engine(["mysql", "postgresql"])
@pytest.mark.usefixtures("skip_by_db_engine")
async def test_purge_can_mix_legacy_and_new_format_with_detached_state(
async_setup_recorder_instance: RecorderInstanceGenerator,
hass: HomeAssistant,
recorder_mock: Recorder,
recorder_db_url: str,
) -> None:
"""Test purging with legacy and new events with a detached state.
This tests disables foreign key checks on SQLite.
"""
instance = await async_setup_recorder_instance(hass)
await async_attach_db_engine(hass)
await async_wait_recording_done(hass)
# New databases are no longer created with the legacy events index
assert instance.use_legacy_events_index is False
assert recorder_mock.use_legacy_events_index is False
def _recreate_legacy_events_index():
"""Recreate the legacy events index since its no longer created on new instances."""
migration._create_index(instance.get_session, "states", "ix_states_event_id")
instance.use_legacy_events_index = True
migration._create_index(
recorder_mock.get_session, "states", "ix_states_event_id"
)
recorder_mock.use_legacy_events_index = True
await instance.async_add_executor_job(_recreate_legacy_events_index)
assert instance.use_legacy_events_index is True
await recorder_mock.async_add_executor_job(_recreate_legacy_events_index)
assert recorder_mock.use_legacy_events_index is True
with session_scope(hass=hass) as session:
session.execute(text("PRAGMA foreign_keys = OFF"))
@ -1196,7 +1187,7 @@ async def test_purge_can_mix_legacy_and_new_format_with_detached_state(
purge_before = dt_util.utcnow() - timedelta(days=4)
finished = purge_old_data(
instance,
recorder_mock,
purge_before,
repack=False,
)
@ -1207,7 +1198,7 @@ async def test_purge_can_mix_legacy_and_new_format_with_detached_state(
# and we switch methods
purge_before = dt_util.utcnow() - timedelta(days=4)
finished = purge_old_data(
instance,
recorder_mock,
purge_before,
repack=False,
events_batch_size=1,
@ -1220,7 +1211,7 @@ async def test_purge_can_mix_legacy_and_new_format_with_detached_state(
assert states_with_event_id.count() == 0
assert states_without_event_id.count() == 1
finished = purge_old_data(
instance,
recorder_mock,
purge_before,
repack=False,
events_batch_size=100,
@ -1235,7 +1226,7 @@ async def test_purge_can_mix_legacy_and_new_format_with_detached_state(
assert states_with_event_id.count() == 0
assert states_without_event_id.count() == 2
finished = purge_old_data(
instance,
recorder_mock,
purge_before,
repack=False,
)
@ -1247,11 +1238,9 @@ async def test_purge_can_mix_legacy_and_new_format_with_detached_state(
async def test_purge_entities_keep_days(
async_setup_recorder_instance: RecorderInstanceGenerator,
hass: HomeAssistant,
hass: HomeAssistant, recorder_mock: Recorder
) -> None:
"""Test purging states with an entity filter and keep_days."""
instance = await async_setup_recorder_instance(hass, {})
await async_attach_db_engine(hass)
await hass.async_block_till_done()
@ -1275,7 +1264,7 @@ async def test_purge_entities_keep_days(
hass.states.async_set("sensor.keep", "now")
await async_recorder_block_till_done(hass)
states = await instance.async_add_executor_job(
states = await recorder_mock.async_add_executor_job(
get_significant_states,
hass,
one_month_ago,
@ -1286,7 +1275,7 @@ async def test_purge_entities_keep_days(
assert len(states["sensor.purge"]) == 3
await hass.services.async_call(
recorder.DOMAIN,
RECORDER_DOMAIN,
SERVICE_PURGE_ENTITIES,
{
"entity_id": "sensor.purge",
@ -1296,7 +1285,7 @@ async def test_purge_entities_keep_days(
await async_recorder_block_till_done(hass)
await async_wait_purge_done(hass)
states = await instance.async_add_executor_job(
states = await recorder_mock.async_add_executor_job(
get_significant_states,
hass,
one_month_ago,
@ -1307,7 +1296,7 @@ async def test_purge_entities_keep_days(
assert len(states["sensor.purge"]) == 1
await hass.services.async_call(
recorder.DOMAIN,
RECORDER_DOMAIN,
SERVICE_PURGE_ENTITIES,
{
"entity_id": "sensor.purge",
@ -1316,7 +1305,7 @@ async def test_purge_entities_keep_days(
await async_recorder_block_till_done(hass)
await async_wait_purge_done(hass)
states = await instance.async_add_executor_job(
states = await recorder_mock.async_add_executor_job(
get_significant_states,
hass,
one_month_ago,