"""Store tests (T03): idempotent ingest, usage accounting, eviction.""" import os import sys sys.path.insert(0, os.path.dirname(os.path.dirname(os.path.abspath(__file__)))) from session_memory.adapters.claude import Normalized # noqa: E402 from session_memory.core.schema import Cost, Session, SessionEvent # noqa: E402 from session_memory.core.store import Store # noqa: E402 def _bundle(uid_native="s1", n_events=3): s = Session( session_uid=Session.make_uid("claude", uid_native), flavor="claude", native_session_id=uid_native, repo="agentic-resources", domain="helix_forge", cost=Cost(input_tokens=10), ) events, blobs = [], {} for i in range(n_events): ref = f"blob://{uid_native}/{i}" events.append(SessionEvent(session_uid=s.session_uid, seq=i, kind="assistant_msg", payload_ref=ref, summary=f"msg {i}")) blobs[ref] = "x" * 100 return Normalized(session=s, events=events, blobs=blobs) def _store(tmp_path): return Store(str(tmp_path / "mem.db"), str(tmp_path / "blobs")) def test_ingest_and_read_back(tmp_path): st = _store(tmp_path) b = _bundle("s1", 3) st.ingest(b) s = st.get_session(b.session.session_uid) assert s is not None and s.ingested_at is not None assert st.count_events(b.session.session_uid) == 3 assert st.tier1_usage_bytes() > 0 def test_ingest_is_idempotent(tmp_path): st = _store(tmp_path) b = _bundle("s1", 3) st.ingest(b) before = st.tier1_usage_bytes() st.ingest(b) # re-run same sweep assert st.count_events(b.session.session_uid) == 3 # no duplicate rows assert st.tier1_usage_bytes() == before # blobs upserted, not doubled def test_digest_sets_analyzed_and_tier2_bytes(tmp_path): st = _store(tmp_path) b = _bundle("s1", 2) st.ingest(b) assert st.get_session(b.session.session_uid).analyzed_at is None st.write_digest(b.session.session_uid, {"outcome": "success", "tools": {"Edit": 1}}) assert st.get_session(b.session.session_uid).analyzed_at is not None assert st.tier2_usage_bytes() > 0 assert st.get_digest(b.session.session_uid)["outcome"] == "success" def test_evict_raw_keeps_digest_drops_raw(tmp_path): st = _store(tmp_path) b = _bundle("s1", 3) st.ingest(b) st.write_digest(b.session.session_uid, {"outcome": "unknown"}) blob_dir_files_before = sum(len(f) for _, _, f in os.walk(str(tmp_path / "blobs"))) assert blob_dir_files_before > 0 freed = st.evict_raw(b.session.session_uid) assert freed > 0 assert st.count_events(b.session.session_uid) == 0 # raw gone assert st.get_events(b.session.session_uid) == [] assert st.get_session(b.session.session_uid).evicted_at is not None assert st.get_digest(b.session.session_uid) is not None # Tier 2 preserved # blob files removed from disk remaining = [f for _, _, fs in os.walk(str(tmp_path / "blobs")) for f in fs] assert remaining == [] # evicted session no longer counts toward Tier 1 usage assert st.tier1_usage_bytes() == 0