From 062fdd7ac112dd35ece1141a9648d42259ea921e Mon Sep 17 00:00:00 2001 From: Ali Zarinkolah Date: Sat, 22 Aug 2026 15:08:25 +0330 Subject: [PATCH] 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 --- .../qdrant/test_point_repository.py | 32 ++----------------- tests/support/point_contract.py | 26 +++++++++++++++ 2 files changed, 29 insertions(+), 29 deletions(-) diff --git a/tests/integration/qdrant/test_point_repository.py b/tests/integration/qdrant/test_point_repository.py index f9d8116..e1aed41 100644 --- a/tests/integration/qdrant/test_point_repository.py +++ b/tests/integration/qdrant/test_point_repository.py @@ -18,18 +18,12 @@ from collections.abc import Awaitable, Callable import pytest 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.infrastructure.qdrant.collection import ( - DENSE_NOMIC_DIMENSIONS, - DENSE_OPENAI_DIMENSIONS, - ensure_chunks_collection, -) +from src.infrastructure.qdrant.collection import ensure_chunks_collection from src.infrastructure.qdrant.point_repository import QdrantPointRepository from src.infrastructure.qdrant.points import QdrantPointStorage 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 = [ 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( qdrant_client: AsyncQdrantClient, qdrant_settings: QdrantSettings ) -> QdrantPointRepository: await ensure_chunks_collection(qdrant_client, collection=qdrant_settings.collection) storage = QdrantPointStorage(qdrant_client, collection=qdrant_settings.collection) 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 # ordered query can run against a half-built index and return short. diff --git a/tests/support/point_contract.py b/tests/support/point_contract.py index 70dc267..b0e27d9 100644 --- a/tests/support/point_contract.py +++ b/tests/support/point_contract.py @@ -21,8 +21,14 @@ from dataclasses import dataclass from datetime import UTC, datetime 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.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 # 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( tenant_a: uuid.UUID, tenant_b: uuid.UUID,