"""Vector store targets — registry, doc_key contract, and per-target roundtrips against a deterministic stub engine (no model download). The per-target roundtrips are skipped automatically when a target's library is not installed; the registry tests always run. """ from __future__ import annotations import pytest from thicket.chunker import Chunk from thicket.vector_stores import ( TARGETS, VectorStoreError, create_store, doc_key, ) CHUNKS = [ Chunk(header="Alpha", text="alpha content one", contextual_text="[Source: T | Section: Alpha]\nalpha content one"), Chunk(header="Beta", text="beta content two", contextual_text="[Source: T | Section: Beta]\nbeta content two"), ] class StubEngine: """Deterministic 4-dim embeddings: direction keyed by first word.""" model_name = "stub-model" dim = 4 _BASIS = { "alpha": [1.0, 0.0, 0.0, 0.0], "beta": [0.0, 1.0, 0.0, 0.0], } def embed(self, texts: list[str]) -> list[list[float]]: return [self._vector(t) for t in texts] def embed_query(self, text: str) -> list[float]: return self._vector(text) @classmethod def _vector(cls, text: str) -> list[float]: for word, vec in cls._BASIS.items(): if word in text.lower(): return list(vec) return [0.0, 0.0, 1.0, 0.0] def _roundtrip(store) -> None: store.set_embedder(StubEngine()) store.ensure_collection() written = store.replace_document("Doc One", "Ingested_Brain/doc-one.md", CHUNKS) assert written == 2 hits = store.search(StubEngine().embed_query("alpha query"), limit=2) assert hits, "expected at least one hit" top = hits[0] assert top["payload"]["document_title"] == "Doc One" assert top["payload"]["section_header"] == "Alpha" assert "doc_key" not in top["payload"] # internal key never surfaces # Exact replacement: shrink to one chunk, count must follow exactly. store.replace_document("Doc One", "Ingested_Brain/doc-one.md", CHUNKS[:1]) hits_after = store.search(StubEngine().embed_query("alpha query"), limit=10) alpha_hits = [h for h in hits_after if h["payload"]["section_header"] == "Alpha"] beta_hits = [h for h in hits_after if h["payload"]["section_header"] == "Beta"] assert len(alpha_hits) == 1 and not beta_hits, \ "re-ingest must replace exactly, leaving no stale chunks" # ── registry (always runs) ── def test_registry_covers_ten_targets(): assert sorted(TARGETS) == [ "chroma", "duckdb", "faiss", "lancedb", "mariadb", "milvus", "pgvector", "qdrant", "sqlitevec", "weaviate", ] def test_service_requirements_are_exact(): services = {key: spec.service for key, spec in TARGETS.items()} assert services == { "qdrant": "qdrant", "pgvector": "postgres", "weaviate": "weaviate", "mariadb": "mariadb", "chroma": None, "lancedb": None, "faiss": None, "milvus": None, "duckdb": None, "sqlitevec": None, } def test_identifier_mappers_never_emit_raw_names(): from thicket.vector_stores import _mariadb_ident, _weaviate_name assert _mariadb_ident("second-brain; DROP TABLE x") == "second_brain__DROP_TABLE_x" assert _weaviate_name("second_brain") == "Second_brain" assert _mariadb_ident("") == "thicket" def test_doc_key_is_stable_and_injective(): a = doc_key("Title", "path/one.md") assert a == doc_key("Title", "path/one.md") assert a != doc_key("Title", "path/two.md") assert a != doc_key("Other", "path/one.md") def test_unknown_target_names_every_choice(): with pytest.raises(VectorStoreError, match="known:"): create_store("vespa", collection="x", dim=4) def test_missing_module_error_names_the_fix(): from thicket.vector_stores import BaseVectorStore base = BaseVectorStore("c", 4) with pytest.raises(VectorStoreError, match="pip install"): base._require_module("definitely_not_a_module_xyz") # ── per-target roundtrips (skip when library absent) ── def test_qdrant_roundtrip_needs_service(): assert TARGETS["qdrant"].service == "qdrant" def test_chroma_roundtrip(tmp_path): pytest.importorskip("chromadb") from thicket.vector_stores import ChromaStore _roundtrip(ChromaStore(data_dir=tmp_path / "data", collection="test_col", dim=4)) def test_lancedb_roundtrip(tmp_path): pytest.importorskip("lancedb") from thicket.vector_stores import LanceStore _roundtrip(LanceStore(data_dir=tmp_path, collection="t", dim=4)) def test_faiss_roundtrip(tmp_path): pytest.importorskip("faiss") from thicket.vector_stores import FaissStore _roundtrip(FaissStore(data_dir=tmp_path, collection="t", dim=4)) def test_milvus_roundtrip(tmp_path): pytest.importorskip("pymilvus") from thicket.vector_stores import MilvusStore _roundtrip(MilvusStore(data_dir=tmp_path / "data", collection="test_col", dim=4)) def test_duckdb_roundtrip(tmp_path): pytest.importorskip("duckdb") from thicket.vector_stores import DuckStore _roundtrip(DuckStore(data_dir=tmp_path / "data", collection="test_col", dim=4)) def test_sqlitevec_roundtrip(tmp_path): pytest.importorskip("sqlite_vec") from thicket.vector_stores import SqliteVecStore _roundtrip(SqliteVecStore(data_dir=tmp_path / "data", collection="test_col", dim=4)) def test_weaviate_name_mapping(): from thicket.vector_stores import _weaviate_name assert _weaviate_name("second_brain") == "Second_brain" assert _weaviate_name("my-papers 2") == "My_papers_2" def test_payload_carries_code_provenance(): from thicket.chunker import Chunk from thicket.vector_stores import _payload chunk = Chunk("H", "def x(): pass", "[S|H]\ndef x(): pass", kind="code", lang="python", source="pkg/mod.py") payload = _payload("Doc", "notes/doc.md", chunk, 0) assert payload["chunk_kind"] == "code" assert payload["lang"] == "python" assert payload["source_path"] == "pkg/mod.py" assert payload["doc_key"] plain = Chunk("H", "prose", "[S|H]\nprose") payload = _payload("Doc", "notes/doc.md", plain, 0) assert "lang" not in payload and "source_path" not in payload