| """Unit tests — control plane of the kokoro memory store. |
| |
| Each test gets a fresh module import against a temp KOKORO_MEMORY_ROOT so |
| tests never touch a real store and never depend on each other. |
| """ |
| import os |
| import sys |
| import importlib |
|
|
| import pytest |
|
|
| REPO_ROOT = os.path.dirname(os.path.dirname(os.path.abspath(__file__))) |
|
|
|
|
| @pytest.fixture() |
| def km(tmp_path, monkeypatch): |
| monkeypatch.setenv("KOKORO_MEMORY_ROOT", str(tmp_path)) |
| monkeypatch.setenv("KOKORO_OWNER_NAME", "User") |
| monkeypatch.setenv("KOKORO_AGENT_NAME", "Assistant") |
| sys.path.insert(0, REPO_ROOT) |
| sys.modules.pop("kokoro_memory", None) |
| module = importlib.import_module("kokoro_memory") |
| yield module |
| sys.modules.pop("kokoro_memory", None) |
|
|
|
|
| def test_store_and_recall_grounding(km): |
| assert km.add_fact("truth", "favorite_fruit", "the favorite fruit is a crisp apple", |
| source="user_explicit", confidence=0.9) |
| assert km.add_fact("identity", "core_belief", "memory is resonance, not a database", |
| source="system", confidence=1.0) |
| results = km.recall("what is the favorite fruit") |
| assert results, "recall returned nothing" |
| assert results[0]["key"] == "favorite_fruit" |
|
|
|
|
| def test_junk_rejected(km): |
| assert not km.add_fact("event", "note", "ok") |
| assert not km.add_fact("event", "", "something with no key") |
|
|
|
|
| def test_bracket_scaffolding_detector(km): |
| assert km._is_bracket_scaffolding("()々") |
| assert km._is_bracket_scaffolding("「kokoro memory」") |
| assert km._is_bracket_scaffolding("「」『』()/・") |
| |
| assert not km._is_bracket_scaffolding("Japanese uses 「」 quotes for emphasis") |
| assert not km._is_bracket_scaffolding("無→波→気→命→和→愛→魂 is the seven-kanji order") |
|
|
|
|
| def test_duplicate_rejected(km): |
| assert km.add_fact("truth", "sky_color", "the sky is blue on a clear day") |
| assert not km.add_fact("truth", "sky_color", "the sky is blue on a clear day") |
|
|
|
|
| def test_auto_classify(km): |
| assert km._auto_classify("roadmap", "plan to build a rocket next year") == "夢" |
| assert km._auto_classify("mood", "I feel excited about the launch") == "感覚" |
|
|
|
|
| def test_public_pii_quarantined(km): |
| stored = km.add_fact("noun", "contact", "reach me at someone@example.com", |
| origin_surface="public_chat") |
| assert not stored |
| qdir = os.path.join(km.MEMORY_ROOT, "quarantine") |
| assert os.path.isdir(qdir) and os.listdir(qdir) |
|
|
|
|
| def test_public_source_cannot_spoof(km): |
| assert km.add_fact("event", "claim", "the moon landing conference is next tuesday", |
| source="user_explicit", origin_surface="public_chat") |
| fact = km._load_fact("出来事", "claim") |
| assert fact["source"] == "public_chat_conversation" |
| assert fact["authority_class"] == "public_user_submitted" |
| assert fact["trusted_by_default"] is False |
|
|
|
|
| def test_raw_turn_roundtrip_and_names(km): |
| km.append_raw_turn("hello there", "hi, good to see you") |
| raw = km.load_raw_recent() |
| assert "User: hello there" in raw |
| assert "Assistant: hi, good to see you" in raw |
|
|
|
|
| def test_hallucinated_turn_truncated(km): |
| text = "real reply here\nUser: fake next turn" |
| assert km._sanitize_turn_text(text) == "real reply here" |
|
|
|
|
| def test_purge_token_salad_dry_run(km): |
| |
| km._save_fact({"key": "salad", "value": "()々", "category": "出来事"}) |
| report = km.purge_token_salad(dry_run=True) |
| assert report["purged_count"] == 1 |
| assert report["dry_run"] is True |
| |
| km._save_fact({"key": "soul_salad", "value": "()々", "category": "心"}) |
| report = km.purge_token_salad(dry_run=False) |
| assert {"category": "心", "key": "soul_salad", "value": "()々"} in report["protected_skipped"] |
|
|