113 lines
3.3 KiB
Python
113 lines
3.3 KiB
Python
"""Tests for VectorStore port — verify the protocol contract is implementable."""
|
|
|
|
from __future__ import annotations
|
|
|
|
import pytest
|
|
|
|
from domain.ports import VectorStore
|
|
from domain.vector_schema import IndexedChunk, SearchResult
|
|
|
|
|
|
class FakeVectorStore:
|
|
"""Minimal concrete implementation to verify the protocol is implementable."""
|
|
|
|
async def ensure_index(self, index_name: str, mapping: dict) -> None:
|
|
pass
|
|
|
|
async def index_chunks(self, index_name: str, chunks: list[IndexedChunk]) -> int:
|
|
return len(chunks)
|
|
|
|
async def search_similar(
|
|
self,
|
|
index_name: str,
|
|
embedding: list[float],
|
|
*,
|
|
k: int = 10,
|
|
doc_id: str | None = None,
|
|
) -> list[SearchResult]:
|
|
return []
|
|
|
|
async def get_chunks(
|
|
self,
|
|
index_name: str,
|
|
doc_id: str,
|
|
*,
|
|
limit: int = 1000,
|
|
) -> list[SearchResult]:
|
|
return []
|
|
|
|
async def delete_document(self, index_name: str, doc_id: str) -> int:
|
|
return 0
|
|
|
|
|
|
class TestVectorStorePort:
|
|
def test_fake_satisfies_protocol(self):
|
|
"""A class implementing all methods is accepted as a VectorStore."""
|
|
store: VectorStore = FakeVectorStore()
|
|
assert store is not None
|
|
|
|
@pytest.mark.asyncio
|
|
async def test_ensure_index(self):
|
|
store = FakeVectorStore()
|
|
await store.ensure_index("test-index", {"mappings": {}})
|
|
|
|
@pytest.mark.asyncio
|
|
async def test_index_chunks(self):
|
|
store = FakeVectorStore()
|
|
chunk = IndexedChunk(
|
|
doc_id="d1",
|
|
filename="test.pdf",
|
|
content="Hello",
|
|
embedding=[0.1] * 384,
|
|
chunk_index=0,
|
|
chunk_type="text",
|
|
page_number=1,
|
|
)
|
|
count = await store.index_chunks("test-index", [chunk])
|
|
assert count == 1
|
|
|
|
@pytest.mark.asyncio
|
|
async def test_search_similar(self):
|
|
store = FakeVectorStore()
|
|
results = await store.search_similar("test-index", [0.1] * 384, k=5)
|
|
assert results == []
|
|
|
|
@pytest.mark.asyncio
|
|
async def test_search_similar_with_doc_filter(self):
|
|
store = FakeVectorStore()
|
|
results = await store.search_similar("test-index", [0.1] * 384, k=5, doc_id="d1")
|
|
assert results == []
|
|
|
|
@pytest.mark.asyncio
|
|
async def test_get_chunks(self):
|
|
store = FakeVectorStore()
|
|
results = await store.get_chunks("test-index", "d1")
|
|
assert results == []
|
|
|
|
@pytest.mark.asyncio
|
|
async def test_get_chunks_with_limit(self):
|
|
store = FakeVectorStore()
|
|
results = await store.get_chunks("test-index", "d1", limit=50)
|
|
assert results == []
|
|
|
|
@pytest.mark.asyncio
|
|
async def test_delete_document(self):
|
|
store = FakeVectorStore()
|
|
count = await store.delete_document("test-index", "d1")
|
|
assert count == 0
|
|
|
|
def test_protocol_methods_list(self):
|
|
"""Verify the protocol exposes the expected methods."""
|
|
expected = {
|
|
"ensure_index",
|
|
"index_chunks",
|
|
"search_similar",
|
|
"get_chunks",
|
|
"delete_document",
|
|
}
|
|
protocol_methods = {
|
|
name
|
|
for name in dir(VectorStore)
|
|
if not name.startswith("_") and callable(getattr(VectorStore, name, None))
|
|
}
|
|
assert expected.issubset(protocol_methods)
|