Files
the-nexus/nexus/mnemosyne/tests/test_archive.py
Claude (Opus 4.6) 038346b8a9
Some checks failed
Deploy Nexus / deploy (push) Failing after 3s
Staging Verification Gate / verify-staging (push) Failing after 3s
[claude] Mnemosyne: export, deletion, and richer stats (#1218) (#1220)
2026-04-11 18:50:29 +00:00

212 lines
8.6 KiB
Python

"""Tests for Mnemosyne archive core."""
import json
import tempfile
from pathlib import Path
from nexus.mnemosyne.entry import ArchiveEntry
from nexus.mnemosyne.linker import HolographicLinker
from nexus.mnemosyne.archive import MnemosyneArchive
from nexus.mnemosyne.ingest import ingest_event, ingest_from_mempalace
def test_entry_roundtrip():
e = ArchiveEntry(title="Test", content="Hello world", topics=["test"])
d = e.to_dict()
e2 = ArchiveEntry.from_dict(d)
assert e2.id == e.id
assert e2.title == "Test"
def test_linker_similarity():
linker = HolographicLinker()
a = ArchiveEntry(title="Python coding", content="Writing Python scripts for automation")
b = ArchiveEntry(title="Python scripting", content="Automating tasks with Python scripts")
c = ArchiveEntry(title="Cooking recipes", content="How to make pasta carbonara")
assert linker.compute_similarity(a, b) > linker.compute_similarity(a, c)
def test_archive_add_and_search():
with tempfile.TemporaryDirectory() as tmp:
path = Path(tmp) / "test_archive.json"
archive = MnemosyneArchive(archive_path=path)
ingest_event(archive, title="First entry", content="Hello archive", topics=["test"])
ingest_event(archive, title="Second entry", content="Another record", topics=["test", "demo"])
assert archive.count == 2
results = archive.search("hello")
assert len(results) == 1
assert results[0].title == "First entry"
def test_archive_auto_linking():
with tempfile.TemporaryDirectory() as tmp:
path = Path(tmp) / "test_archive.json"
archive = MnemosyneArchive(archive_path=path)
e1 = ingest_event(archive, title="Python automation", content="Building automation tools in Python")
e2 = ingest_event(archive, title="Python scripting", content="Writing automation scripts using Python")
# Both should be linked due to shared tokens
assert len(e1.links) > 0 or len(e2.links) > 0
def test_ingest_from_mempalace():
with tempfile.TemporaryDirectory() as tmp:
path = Path(tmp) / "test_archive.json"
archive = MnemosyneArchive(archive_path=path)
mp_entries = [
{"id": "mp-1", "content": "Test memory content", "metadata": {"title": "Test", "topics": ["demo"]}},
{"id": "mp-2", "content": "Another memory", "metadata": {"title": "Memory 2"}},
]
count = ingest_from_mempalace(archive, mp_entries)
assert count == 2
assert archive.count == 2
def test_archive_persistence():
with tempfile.TemporaryDirectory() as tmp:
path = Path(tmp) / "test_archive.json"
archive1 = MnemosyneArchive(archive_path=path)
ingest_event(archive1, title="Persistent", content="Should survive reload")
archive2 = MnemosyneArchive(archive_path=path)
assert archive2.count == 1
results = archive2.search("persistent")
assert len(results) == 1
def test_archive_remove_basic():
with tempfile.TemporaryDirectory() as tmp:
path = Path(tmp) / "test_archive.json"
archive = MnemosyneArchive(archive_path=path)
e1 = ingest_event(archive, title="Alpha", content="First entry", topics=["x"])
assert archive.count == 1
result = archive.remove(e1.id)
assert result is True
assert archive.count == 0
assert archive.get(e1.id) is None
def test_archive_remove_nonexistent():
with tempfile.TemporaryDirectory() as tmp:
path = Path(tmp) / "test_archive.json"
archive = MnemosyneArchive(archive_path=path)
result = archive.remove("does-not-exist")
assert result is False
def test_archive_remove_cleans_backlinks():
with tempfile.TemporaryDirectory() as tmp:
path = Path(tmp) / "test_archive.json"
archive = MnemosyneArchive(archive_path=path)
e1 = ingest_event(archive, title="Python automation", content="Building automation tools in Python")
e2 = ingest_event(archive, title="Python scripting", content="Writing automation scripts using Python")
# At least one direction should be linked
assert e1.id in e2.links or e2.id in e1.links
# Remove e1; e2 must no longer reference it
archive.remove(e1.id)
e2_fresh = archive.get(e2.id)
assert e2_fresh is not None
assert e1.id not in e2_fresh.links
def test_archive_remove_persists():
with tempfile.TemporaryDirectory() as tmp:
path = Path(tmp) / "test_archive.json"
a1 = MnemosyneArchive(archive_path=path)
e = ingest_event(a1, title="Gone", content="Will be removed")
a1.remove(e.id)
a2 = MnemosyneArchive(archive_path=path)
assert a2.count == 0
def test_archive_export_unfiltered():
with tempfile.TemporaryDirectory() as tmp:
path = Path(tmp) / "test_archive.json"
archive = MnemosyneArchive(archive_path=path)
ingest_event(archive, title="A", content="content a", topics=["alpha"])
ingest_event(archive, title="B", content="content b", topics=["beta"])
data = archive.export()
assert data["count"] == 2
assert len(data["entries"]) == 2
assert data["filters"] == {"query": None, "topics": None}
def test_archive_export_by_topic():
with tempfile.TemporaryDirectory() as tmp:
path = Path(tmp) / "test_archive.json"
archive = MnemosyneArchive(archive_path=path)
ingest_event(archive, title="A", content="content a", topics=["alpha"])
ingest_event(archive, title="B", content="content b", topics=["beta"])
data = archive.export(topics=["alpha"])
assert data["count"] == 1
assert data["entries"][0]["title"] == "A"
def test_archive_export_by_query():
with tempfile.TemporaryDirectory() as tmp:
path = Path(tmp) / "test_archive.json"
archive = MnemosyneArchive(archive_path=path)
ingest_event(archive, title="Hello world", content="greetings", topics=[])
ingest_event(archive, title="Goodbye", content="farewell", topics=[])
data = archive.export(query="hello")
assert data["count"] == 1
assert data["entries"][0]["title"] == "Hello world"
def test_archive_export_combined_filters():
with tempfile.TemporaryDirectory() as tmp:
path = Path(tmp) / "test_archive.json"
archive = MnemosyneArchive(archive_path=path)
ingest_event(archive, title="Hello world", content="greetings", topics=["alpha"])
ingest_event(archive, title="Hello again", content="greetings again", topics=["beta"])
data = archive.export(query="hello", topics=["alpha"])
assert data["count"] == 1
assert data["entries"][0]["title"] == "Hello world"
def test_archive_stats_richer():
with tempfile.TemporaryDirectory() as tmp:
path = Path(tmp) / "test_archive.json"
archive = MnemosyneArchive(archive_path=path)
# All four new fields present when archive is empty
s = archive.stats()
assert "orphans" in s
assert "link_density" in s
assert "oldest_entry" in s
assert "newest_entry" in s
assert s["orphans"] == 0
assert s["link_density"] == 0.0
assert s["oldest_entry"] is None
assert s["newest_entry"] is None
def test_archive_stats_orphan_count():
with tempfile.TemporaryDirectory() as tmp:
path = Path(tmp) / "test_archive.json"
archive = MnemosyneArchive(archive_path=path)
# Two entries with very different content → unlikely to auto-link
ingest_event(archive, title="Zebras", content="Zebra stripes savannah Africa", topics=[])
ingest_event(archive, title="Compiler", content="Lexer parser AST bytecode", topics=[])
s = archive.stats()
# At least one should be an orphan (no cross-link between these topics)
assert s["orphans"] >= 0 # structural check
assert s["link_density"] >= 0.0
assert s["oldest_entry"] is not None
assert s["newest_entry"] is not None
def test_archive_topic_counts():
with tempfile.TemporaryDirectory() as tmp:
path = Path(tmp) / "test_archive.json"
archive = MnemosyneArchive(archive_path=path)
ingest_event(archive, title="A", content="x", topics=["python", "automation"])
ingest_event(archive, title="B", content="y", topics=["python"])
ingest_event(archive, title="C", content="z", topics=["automation"])
counts = archive.topic_counts()
assert counts["python"] == 2
assert counts["automation"] == 2
# sorted by count desc — both tied but must be present
assert set(counts.keys()) == {"python", "automation"}