test: expand coverage from 20 to 92 tests, migrate to uv

- Migrate from setuptools to hatchling build backend
- Add dependency-groups (PEP 735) for dev tooling (pytest, ruff)
- Remove redundant requirements.txt in favor of uv.lock
- Fix __version__ mismatch (2.0.0 -> 3.0.0 to match pyproject.toml)

New test files:
- conftest.py: shared fixtures (isolated palace, KG, ChromaDB collection)
- test_knowledge_graph.py: 17 tests (entity CRUD, temporal queries, timeline)
- test_mcp_server.py: 25 tests (protocol dispatch, read/write/KG/diary tools)
- test_searcher.py: 7 tests (search_memories API, filters, error handling)
- test_dialect.py: 13 tests (AAAK compression, entity/emotion detection, zettel encoding)

All 92 tests pass on Python 3.13 with chromadb 0.6.3.
This commit is contained in:
Igor Lins e Silva
2026-04-07 17:07:02 -03:00
parent 0b0e123f42
commit 72c548b729
8 changed files with 3943 additions and 11 deletions
+106
View File
@@ -0,0 +1,106 @@
"""
conftest.py — Shared fixtures for MemPalace tests.
Provides isolated palace and knowledge graph instances so tests never
touch the user's real data or leak temp files on failure.
"""
import os
import shutil
import tempfile
import chromadb
import pytest
from mempalace.config import MempalaceConfig
from mempalace.knowledge_graph import KnowledgeGraph
@pytest.fixture
def tmp_dir():
"""Create and auto-cleanup a temporary directory."""
d = tempfile.mkdtemp(prefix="mempalace_test_")
yield d
shutil.rmtree(d, ignore_errors=True)
@pytest.fixture
def palace_path(tmp_dir):
"""Path to an empty palace directory inside tmp_dir."""
p = os.path.join(tmp_dir, "palace")
os.makedirs(p)
return p
@pytest.fixture
def config(tmp_dir, palace_path):
"""A MempalaceConfig pointing at the temp palace."""
cfg_dir = os.path.join(tmp_dir, "config")
os.makedirs(cfg_dir)
import json
with open(os.path.join(cfg_dir, "config.json"), "w") as f:
json.dump({"palace_path": palace_path}, f)
return MempalaceConfig(config_dir=cfg_dir)
@pytest.fixture
def collection(palace_path):
"""A ChromaDB collection pre-seeded in the temp palace."""
client = chromadb.PersistentClient(path=palace_path)
col = client.get_or_create_collection("mempalace_drawers")
return col
@pytest.fixture
def seeded_collection(collection):
"""Collection with a handful of representative drawers."""
collection.add(
ids=[
"drawer_proj_backend_aaa",
"drawer_proj_backend_bbb",
"drawer_proj_frontend_ccc",
"drawer_notes_planning_ddd",
],
documents=[
"The authentication module uses JWT tokens for session management. "
"Tokens expire after 24 hours. Refresh tokens are stored in HttpOnly cookies.",
"Database migrations are handled by Alembic. We use PostgreSQL 15 "
"with connection pooling via pgbouncer.",
"The React frontend uses TanStack Query for server state management. "
"All API calls go through a centralized fetch wrapper.",
"Sprint planning: migrate auth to passkeys by Q3. "
"Evaluate ChromaDB alternatives for vector search.",
],
metadatas=[
{"wing": "project", "room": "backend", "source_file": "auth.py", "chunk_index": 0, "added_by": "miner", "filed_at": "2026-01-01T00:00:00"},
{"wing": "project", "room": "backend", "source_file": "db.py", "chunk_index": 0, "added_by": "miner", "filed_at": "2026-01-02T00:00:00"},
{"wing": "project", "room": "frontend", "source_file": "App.tsx", "chunk_index": 0, "added_by": "miner", "filed_at": "2026-01-03T00:00:00"},
{"wing": "notes", "room": "planning", "source_file": "sprint.md", "chunk_index": 0, "added_by": "miner", "filed_at": "2026-01-04T00:00:00"},
],
)
return collection
@pytest.fixture
def kg(tmp_dir):
"""An isolated KnowledgeGraph using a temp SQLite file."""
db_path = os.path.join(tmp_dir, "test_kg.sqlite3")
return KnowledgeGraph(db_path=db_path)
@pytest.fixture
def seeded_kg(kg):
"""KnowledgeGraph pre-loaded with sample triples."""
kg.add_entity("Alice", entity_type="person")
kg.add_entity("Max", entity_type="person")
kg.add_entity("swimming", entity_type="activity")
kg.add_entity("chess", entity_type="activity")
kg.add_triple("Alice", "parent_of", "Max", valid_from="2015-04-01")
kg.add_triple("Max", "does", "swimming", valid_from="2025-01-01")
kg.add_triple("Max", "does", "chess", valid_from="2024-06-01")
kg.add_triple("Alice", "works_at", "Acme Corp", valid_from="2020-01-01", valid_to="2024-12-31")
kg.add_triple("Alice", "works_at", "NewCo", valid_from="2025-01-01")
return kg