refactor(test): share the Qdrant point-seeding helper
Why: - The helper turning a SeedSpec into an upsertable ChunkPoint lived inside one integration test. A second suite now needs to seed real Qdrant the same way, and a copy would let the two drift. Changes: - Move `_chunk_point` into tests/support/point_contract.py as `chunk_point_for`, beside the `build_point` read model it derives its payload from. Impact: - Pure move. No behaviour change; enables the API suite that follows. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
@@ -18,18 +18,12 @@ from collections.abc import Awaitable, Callable
|
|||||||
import pytest
|
import pytest
|
||||||
from qdrant_client import AsyncQdrantClient, models
|
from qdrant_client import AsyncQdrantClient, models
|
||||||
|
|
||||||
from src.application.ingestion.models import SparseVector
|
|
||||||
from src.application.points.models import ChunkPoint
|
|
||||||
from src.config import QdrantSettings
|
from src.config import QdrantSettings
|
||||||
from src.infrastructure.qdrant.collection import (
|
from src.infrastructure.qdrant.collection import ensure_chunks_collection
|
||||||
DENSE_NOMIC_DIMENSIONS,
|
|
||||||
DENSE_OPENAI_DIMENSIONS,
|
|
||||||
ensure_chunks_collection,
|
|
||||||
)
|
|
||||||
from src.infrastructure.qdrant.point_repository import QdrantPointRepository
|
from src.infrastructure.qdrant.point_repository import QdrantPointRepository
|
||||||
from src.infrastructure.qdrant.points import QdrantPointStorage
|
from src.infrastructure.qdrant.points import QdrantPointStorage
|
||||||
from tests.support import point_contract
|
from tests.support import point_contract
|
||||||
from tests.support.point_contract import SeedSpec, build_point, seed_specs
|
from tests.support.point_contract import chunk_point_for, seed_specs
|
||||||
|
|
||||||
pytestmark = [
|
pytestmark = [
|
||||||
pytest.mark.integration,
|
pytest.mark.integration,
|
||||||
@@ -54,33 +48,13 @@ IDS = {
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
def _chunk_point(spec: SeedSpec) -> ChunkPoint:
|
|
||||||
"""The contract's `Point` re-expressed as something upsertable.
|
|
||||||
|
|
||||||
`build_point` produces the read model; Qdrant needs vectors and a flat
|
|
||||||
payload dict, so the payload is taken straight off the model to guarantee
|
|
||||||
the two representations cannot drift apart.
|
|
||||||
"""
|
|
||||||
point = build_point(spec)
|
|
||||||
payload = point.model_dump(mode="json", exclude={"point_id", "vectors"})
|
|
||||||
return ChunkPoint(
|
|
||||||
point_id=point.point_id,
|
|
||||||
dense={
|
|
||||||
"dense_nomic": [0.1] * DENSE_NOMIC_DIMENSIONS,
|
|
||||||
"dense_openai": [0.2] * DENSE_OPENAI_DIMENSIONS,
|
|
||||||
},
|
|
||||||
sparse=SparseVector(indices=[1, 2], values=[0.5, 0.25]),
|
|
||||||
payload=payload,
|
|
||||||
)
|
|
||||||
|
|
||||||
|
|
||||||
async def _seeded(
|
async def _seeded(
|
||||||
qdrant_client: AsyncQdrantClient, qdrant_settings: QdrantSettings
|
qdrant_client: AsyncQdrantClient, qdrant_settings: QdrantSettings
|
||||||
) -> QdrantPointRepository:
|
) -> QdrantPointRepository:
|
||||||
await ensure_chunks_collection(qdrant_client, collection=qdrant_settings.collection)
|
await ensure_chunks_collection(qdrant_client, collection=qdrant_settings.collection)
|
||||||
storage = QdrantPointStorage(qdrant_client, collection=qdrant_settings.collection)
|
storage = QdrantPointStorage(qdrant_client, collection=qdrant_settings.collection)
|
||||||
await storage.upsert_points(
|
await storage.upsert_points(
|
||||||
[_chunk_point(spec) for spec in seed_specs(TENANT_A, TENANT_B, FILE_A, FILE_B, FILE_C)]
|
[chunk_point_for(spec) for spec in seed_specs(TENANT_A, TENANT_B, FILE_A, FILE_B, FILE_C)]
|
||||||
)
|
)
|
||||||
# Payload indexes are built asynchronously; without waiting, a full-text or
|
# Payload indexes are built asynchronously; without waiting, a full-text or
|
||||||
# ordered query can run against a half-built index and return short.
|
# ordered query can run against a half-built index and return short.
|
||||||
|
|||||||
@@ -21,8 +21,14 @@ from dataclasses import dataclass
|
|||||||
from datetime import UTC, datetime
|
from datetime import UTC, datetime
|
||||||
|
|
||||||
from src.application.ingestion.chunking import chunk_id_for
|
from src.application.ingestion.chunking import chunk_id_for
|
||||||
|
from src.application.ingestion.models import SparseVector
|
||||||
|
from src.application.points.models import ChunkPoint
|
||||||
from src.application.points.point import Point
|
from src.application.points.point import Point
|
||||||
from src.application.ports.point_repository import PayloadPatch, PointRepository
|
from src.application.ports.point_repository import PayloadPatch, PointRepository
|
||||||
|
from src.infrastructure.qdrant.collection import (
|
||||||
|
DENSE_NOMIC_DIMENSIONS,
|
||||||
|
DENSE_OPENAI_DIMENSIONS,
|
||||||
|
)
|
||||||
|
|
||||||
# Two tenants and two files, so every scenario can assert both that the right
|
# Two tenants and two files, so every scenario can assert both that the right
|
||||||
# rows come back and that the wrong ones do not.
|
# rows come back and that the wrong ones do not.
|
||||||
@@ -76,6 +82,26 @@ def build_point(spec: SeedSpec) -> Point:
|
|||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
|
def chunk_point_for(spec: SeedSpec) -> ChunkPoint:
|
||||||
|
"""A `SeedSpec` as something upsertable into real Qdrant.
|
||||||
|
|
||||||
|
The payload is taken straight off `build_point`'s read model rather than
|
||||||
|
hand-written, so the write shape and the read shape cannot drift apart. The
|
||||||
|
vectors are constant filler: nothing in plan 002's read paths scores by
|
||||||
|
similarity, so their values are irrelevant and their dimensions are not.
|
||||||
|
"""
|
||||||
|
point = build_point(spec)
|
||||||
|
return ChunkPoint(
|
||||||
|
point_id=point.point_id,
|
||||||
|
dense={
|
||||||
|
"dense_nomic": [0.1] * DENSE_NOMIC_DIMENSIONS,
|
||||||
|
"dense_openai": [0.2] * DENSE_OPENAI_DIMENSIONS,
|
||||||
|
},
|
||||||
|
sparse=SparseVector(indices=[1, 2], values=[0.5, 0.25]),
|
||||||
|
payload=point.model_dump(mode="json", exclude={"point_id", "vectors"}),
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
def seed_specs(
|
def seed_specs(
|
||||||
tenant_a: uuid.UUID,
|
tenant_a: uuid.UUID,
|
||||||
tenant_b: uuid.UUID,
|
tenant_b: uuid.UUID,
|
||||||
|
|||||||
Reference in New Issue
Block a user