feat(evidence): define source and corpus contracts
This commit is contained in:
@@ -0,0 +1,106 @@
|
||||
from datetime import UTC, datetime
|
||||
|
||||
import pytest
|
||||
from pydantic import ValidationError
|
||||
|
||||
from tht.corpus.models import CanonicalChunk, CanonicalDocument, CorpusManifest
|
||||
|
||||
|
||||
def document(source_uri: str = "https://host/a.md") -> CanonicalDocument:
|
||||
return CanonicalDocument(
|
||||
document_id="doc:abc",
|
||||
source_id="source:a",
|
||||
source_uri=source_uri,
|
||||
source_fingerprint="etag:abc",
|
||||
content_hash="sha256:def",
|
||||
title="A",
|
||||
content="# A",
|
||||
media_type="text/markdown",
|
||||
pipeline_version="normalize-v1",
|
||||
)
|
||||
|
||||
|
||||
def chunk() -> CanonicalChunk:
|
||||
return CanonicalChunk(
|
||||
chunk_id="chunk:abc:0",
|
||||
document_id="doc:abc",
|
||||
ordinal=0,
|
||||
content="# A",
|
||||
content_hash="sha256:def",
|
||||
source_uri="https://host/a.md",
|
||||
pipeline_version="chunk-v1",
|
||||
)
|
||||
|
||||
|
||||
def test_manifest_contains_provenance_without_credentials():
|
||||
manifest = CorpusManifest(
|
||||
manifest_id="manifest:abc",
|
||||
created_at=datetime(2026, 7, 12, tzinfo=UTC),
|
||||
pipeline_version="evidence-v1",
|
||||
embedding_model="nomic-embed-text",
|
||||
embedding_dimensions=768,
|
||||
documents=[document()],
|
||||
chunks=[chunk()],
|
||||
)
|
||||
|
||||
payload = manifest.model_dump_json()
|
||||
|
||||
assert "https://host/a.md" in payload
|
||||
assert "etag:abc" in payload
|
||||
assert "evidence-v1" in payload
|
||||
assert "nomic-embed-text" in payload
|
||||
assert "api_key" not in payload
|
||||
|
||||
|
||||
def test_manifest_can_be_assembled_before_publish_identifiers_are_assigned():
|
||||
manifest = CorpusManifest(documents=[document()])
|
||||
|
||||
assert manifest.documents[0].source_uri == "https://host/a.md"
|
||||
assert manifest.manifest_id is None
|
||||
|
||||
|
||||
@pytest.mark.parametrize("model", [document(), chunk()])
|
||||
def test_canonical_records_are_frozen(model):
|
||||
with pytest.raises(ValidationError):
|
||||
model.pipeline_version = "changed" # type: ignore[misc]
|
||||
|
||||
|
||||
def test_manifest_collections_have_independent_defaults():
|
||||
first = CorpusManifest(
|
||||
manifest_id="one", created_at=datetime.now(UTC), pipeline_version="v1"
|
||||
)
|
||||
second = CorpusManifest(
|
||||
manifest_id="two", created_at=datetime.now(UTC), pipeline_version="v1"
|
||||
)
|
||||
|
||||
first.documents.append(document())
|
||||
|
||||
assert second.documents == []
|
||||
|
||||
|
||||
def test_manifest_validates_embedding_compatibility_fields():
|
||||
with pytest.raises(ValidationError):
|
||||
CorpusManifest(
|
||||
manifest_id="bad",
|
||||
created_at=datetime.now(UTC),
|
||||
pipeline_version="v1",
|
||||
embedding_dimensions=0,
|
||||
)
|
||||
|
||||
|
||||
def test_canonical_metadata_rejects_secrets_and_non_json_values():
|
||||
with pytest.raises(ValidationError, match="credential-like"):
|
||||
CanonicalDocument.model_validate(
|
||||
{**document().model_dump(), "metadata": {"password": "secret"}}
|
||||
)
|
||||
with pytest.raises(ValidationError):
|
||||
CanonicalChunk(
|
||||
chunk_id="c",
|
||||
document_id="d",
|
||||
ordinal=0,
|
||||
content="x",
|
||||
content_hash="sha256:x",
|
||||
source_uri="file:///x",
|
||||
pipeline_version="v1",
|
||||
metadata={"bad": object()},
|
||||
)
|
||||
Reference in New Issue
Block a user