Files
OpenJarvis/tests/memory/test_fact_store.py
T
eb2b612c7c fix(memory): address service follow-ups (#591)
Closes #582. Route fact-store construction through a new FactStoreRegistry (local backend registered by default); align the default facts path with get_config_dir(); wire completed chat exchanges (streamed and non-streamed) through the EventBus so the memory service captures them consistently; reload the local fact store from disk before operations so external clears don't resurrect stale facts; make the affected config/persona/memory/CLI/route tests hermetic; and refresh uv.lock with the current resolver (locks pytest-xdist + transitive deps, drops py3.14 artifacts since the project constrains Python <3.14).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-24 19:58:49 -07:00

148 lines
4.2 KiB
Python

"""Tests for the persistent fact store (openjarvis.memory.store)."""
from __future__ import annotations
import json
import pytest
from openjarvis.core.registry import FactStoreRegistry
from openjarvis.memory.store import LocalFactStore, create_fact_store
def test_add_and_list(tmp_path):
store = LocalFactStore(tmp_path / "facts.jsonl")
assert store.add("User prefers concise answers") is True
assert store.add("User lives in Berlin") is True
facts = store.list()
assert [f.text for f in facts] == [
"User prefers concise answers",
"User lives in Berlin",
]
assert store.count() == 2
def test_add_dedupes_case_insensitive(tmp_path):
store = LocalFactStore(tmp_path / "facts.jsonl")
assert store.add("Likes coffee") is True
assert store.add("likes coffee") is False # duplicate
assert store.count() == 1
def test_add_skips_empty(tmp_path):
store = LocalFactStore(tmp_path / "facts.jsonl")
assert store.add("") is False
assert store.add(" ") is False
assert store.count() == 0
def test_add_many(tmp_path):
store = LocalFactStore(tmp_path / "facts.jsonl")
added = store.add_many(["a", "b", "a", "c"]) # one dupe
assert added == 3
assert store.count() == 3
def test_max_facts_evicts_oldest(tmp_path):
store = LocalFactStore(tmp_path / "facts.jsonl", max_facts=2)
store.add("first")
store.add("second")
store.add("third")
facts = [f.text for f in store.list()]
assert facts == ["second", "third"] # oldest dropped
def test_persistence_across_instances(tmp_path):
path = tmp_path / "facts.jsonl"
store = LocalFactStore(path)
store.add("durable fact")
reloaded = LocalFactStore(path)
assert [f.text for f in reloaded.list()] == ["durable fact"]
def test_clear(tmp_path):
path = tmp_path / "facts.jsonl"
store = LocalFactStore(path)
store.add("one")
store.add("two")
removed = store.clear()
assert removed == 2
assert store.count() == 0
# A fresh instance also sees an empty store.
assert LocalFactStore(path).count() == 0
def test_external_clear_does_not_resurrect_stale_facts(tmp_path):
"""A running store instance must not re-flush facts cleared elsewhere."""
path = tmp_path / "facts.jsonl"
running = LocalFactStore(path)
cli = LocalFactStore(path)
running.add("old fact")
assert cli.clear() == 1
running.add("new fact")
assert [f.text for f in LocalFactStore(path).list()] == ["new fact"]
def test_load_skips_malformed_lines(tmp_path):
path = tmp_path / "facts.jsonl"
path.write_text(
'{"text": "good fact"}\n'
"this is not json\n"
'{"text": ""}\n' # empty text ignored
'{"text": "another good"}\n',
encoding="utf-8",
)
store = LocalFactStore(path)
assert [f.text for f in store.list()] == ["good fact", "another good"]
def test_jsonl_round_trip_is_valid_json(tmp_path):
path = tmp_path / "facts.jsonl"
store = LocalFactStore(path)
store.add("fact one", source="auto")
lines = [
line for line in path.read_text(encoding="utf-8").splitlines() if line.strip()
]
assert len(lines) == 1
obj = json.loads(lines[0])
assert obj["text"] == "fact one"
assert obj["source"] == "auto"
assert "created_at" in obj
def test_create_fact_store_local(tmp_path):
store = create_fact_store("local", path=tmp_path / "f.jsonl", max_facts=5)
assert isinstance(store, LocalFactStore)
def test_create_fact_store_uses_fact_store_registry(tmp_path):
class CustomFactStore(LocalFactStore):
pass
FactStoreRegistry.register_value("custom", CustomFactStore)
store = create_fact_store("custom", path=tmp_path / "f.jsonl", max_facts=5)
assert isinstance(store, CustomFactStore)
def test_create_fact_store_default_path_uses_openjarvis_home(tmp_path, monkeypatch):
monkeypatch.setenv("OPENJARVIS_HOME", str(tmp_path))
store = create_fact_store("local")
assert isinstance(store, LocalFactStore)
assert store.path == tmp_path / "memory_facts.jsonl"
def test_create_fact_store_unknown_backend(tmp_path):
with pytest.raises(ValueError):
create_fact_store("cloud", path=tmp_path / "f.jsonl")