"""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("「」『』()/・") # Legitimate content must pass 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): # Bypass add_fact's junk gate to plant salad directly, as the old defect did 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 # Identity category is protected even from real purges 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"]