mirror of
https://github.com/zvx-echo6/meshai.git
synced 2026-06-11 01:14:45 +02:00
Closes Rule-20 dispatcher gap from audit doc v0.6-phase1-audit.md finding #1. Pre-this-commit the cold-start anchor, 4 drop counters, per-toggle cooldown map, and dedup OrderedDict all lived in Dispatcher instance memory and were lost on every container restart. v5.sql adds three tables: - dispatcher_state (singleton id=1): cold_start_anchor + 4 drop counters - dispatcher_cooldowns ((toggle,category,region) keyed): last_fired_at - dispatcher_dedup ((source,event_id) keyed): seen_at Dispatcher refactor: - __init__ calls _restore_from_db -- counters, cold-start anchor, cooldown map, and dedup LRU (most-recent 10k by seen_at) all rehydrated from the three new tables - write-through on every mutation: _persist_state for counter/anchor, _persist_cooldown for cooldown UPSERT + 2*cooldown_s prune, _persist_dedup for dedup INSERT OR REPLACE + 7-day cleanup - in-memory caches stay authoritative on the fast read path - cumulative-since-install counters (NOT since-boot); LLM will be able to answer "we have dropped 47 stale events this week" after commit #5 (env_reporter) lands - graceful degrade: missing v5 tables / persistence outage falls back to fresh in-memory state without crashing the constructor Tests: - tests/test_dispatcher_persistence.py (17 tests): state restore on init, counter+cooldown+dedup survival across simulated restart, cooldown rearm within 2x window, dedup LRU rebuild caps at 10k, 7-day cleanup on insert, INSERT OR REPLACE on duplicate source+event_id, v5 migration idempotent, synthetic storm (50 events) -> restart -> replay (5 incl 1 duplicate) with the duplicate dedup-rejected and counters NOT resetting - tests/conftest.py (new): autouse MESHAI_DB_PATH redirection to per-test tmp file, so the dispatcher_* tables on production /data dont get polluted by tests that construct Dispatcher() without an explicit fixture - tests/test_notification_toggles.py: _dispatch helper wipes dedup/cooldown/ state tables between calls (per-call independence preserved; pre-v0.6-2 in-memory-only Dispatcher reset naturally per instance) Test count: 680 -> 697 (+17 new, 0 regressions). Refs audit doc v0.6-phase1-audit.md finding #1.
31 lines
1.3 KiB
Python
31 lines
1.3 KiB
Python
"""Pytest fixture isolation for meshai persistence (v0.6-2).
|
|
|
|
Before v0.6-2 the dispatcher held all state in instance memory, so tests
|
|
that constructed `Dispatcher(...)` were inert w.r.t. SQLite. v0.6-2 made
|
|
`Dispatcher.__init__` read/restore from the persistence layer, which by
|
|
default points at `/data/meshai.sqlite`. Without isolation every test
|
|
would now read+write production state, polluting across tests and across
|
|
pytest invocations.
|
|
|
|
This autouse fixture redirects `MESHAI_DB_PATH` to a per-test tmp file
|
|
and clears the persistence-layer threading.local caches around each test.
|
|
Existing tests that don't reference any fixture get isolation for free;
|
|
tests that explicitly use a `db_path` (or similar) fixture can still
|
|
override the env var inside their own fixture body -- last setenv wins.
|
|
"""
|
|
import pytest
|
|
|
|
from meshai.persistence import close_thread_connection
|
|
from meshai.persistence import db as _persistence_db
|
|
|
|
|
|
@pytest.fixture(autouse=True)
|
|
def _isolate_meshai_db(tmp_path, monkeypatch):
|
|
"""Point MESHAI_DB_PATH at a tmp file per test."""
|
|
p = str(tmp_path / "meshai-test-isolated.sqlite")
|
|
monkeypatch.setenv("MESHAI_DB_PATH", p)
|
|
_persistence_db._initialised.clear()
|
|
close_thread_connection()
|
|
yield p
|
|
close_thread_connection()
|
|
_persistence_db._initialised.discard(p)
|