Files
ThothII/harness/tests/test_vector_port_contract.py
T

157 lines
5.0 KiB
Python

from unittest.mock import MagicMock
import pytest
from tht.adapters.vector.thoth_http import ThothHttpVectorStore
from tht.evidence.model import EvidenceDoc
from tht.ports.vector import (
VectorHit,
VectorRecord,
VectorStore,
VectorWriteRecord,
VectorWriteUnavailable,
)
from tht.vectorstore.records import evidence_records
def test_http_store_reports_reader_without_writer():
reader = MagicMock()
store = ThothHttpVectorStore(reader=reader, writer=None)
assert store.capabilities.search is True
assert store.capabilities.upsert is False
with pytest.raises(VectorWriteUnavailable):
store.upsert("memory", [])
def test_http_store_keeps_reader_and_writer_operations_separate():
reader = MagicMock()
reader.search_similar.return_value = [
{
"similarity": 0.75,
"metadata": {
"record_key": "m1",
"kind": "memory",
"ref": "session:s1",
"title": "Choice",
"content": "Use the curated table",
},
}
]
writer = MagicMock()
writer.existing_hashes.return_value = {"m1": "abc"}
writer.upsert_records.return_value = 1
store = ThothHttpVectorStore(reader=reader, writer=writer)
hits = store.search(["memory"], [0.1, 0.2], limit=3, kinds=["memory"])
assert hits == [
VectorHit(
id="m1",
kind="memory",
ref="session:s1",
title="Choice",
content="Use the curated table",
metadata={
"record_key": "m1",
"kind": "memory",
"ref": "session:s1",
"title": "Choice",
"content": "Use the curated table",
},
similarity=0.75,
)
]
reader.search_similar.assert_called_once_with(
"memory", [0.1, 0.2], 3, kinds=["memory"]
)
writer.search_similar.assert_not_called()
assert store.existing_hashes("memory", ["memory"]) == {"m1": "abc"}
writer.existing_hashes.assert_called_once_with("memory", ["memory"])
records = [
VectorWriteRecord(
record=VectorRecord(
id="m1",
kind="memory",
ref="session:s1",
title="Choice",
content="Use the curated table",
),
embedding=[0.1, 0.2],
content_hash="abc",
)
]
assert store.upsert("memory", records) == 1
writer.upsert_records.assert_called_once()
reader.upsert_records.assert_not_called()
def test_http_upsert_serializes_a_canonical_builder_record():
record = evidence_records(
[EvidenceDoc(id="joins", title="Join guidance", body="Use the curated join")],
max_chunk_chars=1000,
)[0]
writer = MagicMock()
writer.upsert_records.return_value = 1
store = ThothHttpVectorStore(reader=MagicMock(), writer=writer)
assert store.upsert(
"evidence",
[VectorWriteRecord(record=record, embedding=[0.2, 0.3], content_hash="digest")],
) == 1
row = writer.upsert_records.call_args.args[1][0]
assert row["record_key"] == "evidence:joins:0"
assert row["metadata"]["status"] == "reviewed"
assert row["embedding"] == [0.2, 0.3]
assert row["content_hash"] == "digest"
def test_http_upsert_preserves_metadata_named_like_transport_fields():
record = VectorRecord(
id="collision",
kind="memory",
ref="session:s1",
title="Collision",
content="Semantic metadata must survive",
metadata={"embedding": "semantic embedding", "content_hash": "semantic hash"},
)
writer = MagicMock()
store = ThothHttpVectorStore(reader=MagicMock(), writer=writer)
store.upsert(
"memory",
[VectorWriteRecord(record=record, embedding=[0.4], content_hash="transport hash")],
)
row = writer.upsert_records.call_args.args[1][0]
assert row["embedding"] == [0.4]
assert row["content_hash"] == "transport hash"
assert row["metadata"]["embedding"] == "semantic embedding"
assert row["metadata"]["content_hash"] == "semantic hash"
def test_http_store_is_runtime_vector_store():
store = ThothHttpVectorStore(reader=MagicMock(), writer=None)
assert isinstance(store, VectorStore)
def test_vector_contract_is_exported_from_public_packages():
from tht.adapters.vector import ThothHttpVectorStore as PublicHttpStore
from tht.ports import VectorStore as PublicVectorStore
from tht.ports import VectorWriteRecord as PublicVectorWriteRecord
assert PublicHttpStore is ThothHttpVectorStore
assert PublicVectorStore is VectorStore
assert PublicVectorWriteRecord is VectorWriteRecord
def test_http_health_uses_reader_list_tables_and_reports_failure():
reader = MagicMock()
store = ThothHttpVectorStore(reader=reader, writer=None)
assert store.health().ok is True
reader.list_tables.side_effect = RuntimeError("offline")
health = store.health()
assert health.ok is False
assert health.detail == "offline"