fix: harden qdrant payload and paging
This commit is contained in:
@@ -38,6 +38,7 @@ class FakeQdrantHttp:
|
||||
self.fail_request: Exception | None = None
|
||||
self.malformed_query = False
|
||||
self.malformed_scroll = False
|
||||
self.scroll_pages: list[dict] | None = None
|
||||
|
||||
def request(self, method, url, *, json=None, timeout=None):
|
||||
self.calls.append((method, url, json))
|
||||
@@ -98,11 +99,23 @@ class FakeQdrantHttp:
|
||||
if method == "POST" and path == "/collections/workspace-semantic/points/scroll":
|
||||
if self.malformed_scroll:
|
||||
return FakeResponse(200, {"result": {"points": "bad"}})
|
||||
if self.scroll_pages is not None:
|
||||
offset = json.get("offset")
|
||||
for page in self.scroll_pages:
|
||||
if page["offset"] == offset:
|
||||
filtered = _match_points(page["points"], json["filter"])
|
||||
return FakeResponse(200, {
|
||||
"result": {
|
||||
"points": filtered,
|
||||
"next_page_offset": page["next_page_offset"],
|
||||
}
|
||||
})
|
||||
raise AssertionError(("unexpected offset", offset, self.scroll_pages))
|
||||
wanted = sorted(
|
||||
_match_points(self.points.values(), json["filter"]),
|
||||
key=lambda point: point["payload"]["record_key"],
|
||||
)
|
||||
return FakeResponse(200, {"result": {"points": wanted}})
|
||||
return FakeResponse(200, {"result": {"points": wanted, "next_page_offset": None}})
|
||||
|
||||
if method == "POST" and path == "/collections/workspace-semantic/points/delete":
|
||||
doomed = [point["id"] for point in _match_points(self.points.values(), json["filter"])]
|
||||
@@ -310,3 +323,96 @@ def test_sanitizes_timeout_and_malformed_responses():
|
||||
fake.malformed_scroll = True
|
||||
with pytest.raises(VectorStoreError, match="Qdrant returned malformed scroll response"):
|
||||
store.existing_hashes("memory", ["memory"])
|
||||
|
||||
|
||||
def test_upsert_payload_keeps_canonical_identity_when_metadata_collides():
|
||||
fake = FakeQdrantHttp()
|
||||
store = _store(fake)
|
||||
record = _write_record(
|
||||
"memory:1",
|
||||
"memory",
|
||||
metadata={
|
||||
"workspace_id": "evil",
|
||||
"kind": "evil",
|
||||
"record_kind": "evil",
|
||||
"record_key": "evil",
|
||||
"content_hash": "evil",
|
||||
},
|
||||
)
|
||||
|
||||
store.upsert("memory", [record])
|
||||
|
||||
payload = next(iter(fake.points.values()))["payload"]
|
||||
assert payload["workspace_id"] == "demo"
|
||||
assert payload["kind"] == "memory"
|
||||
assert payload["record_kind"] == "memory"
|
||||
assert payload["record_key"] == "memory:1"
|
||||
assert payload["content_hash"] == "sha256:" + "a" * 64
|
||||
|
||||
|
||||
def test_scroll_based_operations_paginate_until_next_page_offset_is_absent():
|
||||
fake = FakeQdrantHttp()
|
||||
generation_a = "gen:" + "1" * 32
|
||||
generation_b = "gen:" + "2" * 32
|
||||
fake.scroll_pages = [
|
||||
{
|
||||
"offset": None,
|
||||
"points": [
|
||||
{
|
||||
"id": "p1",
|
||||
"payload": {
|
||||
"workspace_id": "demo",
|
||||
"kind": "evidence",
|
||||
"record_kind": "evidence",
|
||||
"record_key": f"demo:{generation_a}:chunk:1",
|
||||
"content_hash": "sha256:" + "a" * 64,
|
||||
"vector_generation": generation_a,
|
||||
},
|
||||
}
|
||||
],
|
||||
"next_page_offset": "page-2",
|
||||
},
|
||||
{
|
||||
"offset": "page-2",
|
||||
"points": [
|
||||
{
|
||||
"id": "p2",
|
||||
"payload": {
|
||||
"workspace_id": "demo",
|
||||
"kind": "evidence",
|
||||
"record_kind": "evidence",
|
||||
"record_key": f"demo:{generation_a}:chunk:2",
|
||||
"content_hash": "sha256:" + "b" * 64,
|
||||
"vector_generation": generation_a,
|
||||
},
|
||||
},
|
||||
{
|
||||
"id": "p3",
|
||||
"payload": {
|
||||
"workspace_id": "demo",
|
||||
"kind": "evidence",
|
||||
"record_kind": "evidence",
|
||||
"record_key": f"demo:{generation_b}:chunk:3",
|
||||
"content_hash": "sha256:" + "c" * 64,
|
||||
"vector_generation": generation_b,
|
||||
},
|
||||
},
|
||||
],
|
||||
"next_page_offset": None,
|
||||
},
|
||||
]
|
||||
store = _store(fake)
|
||||
|
||||
assert store.existing_hashes("evidence", ["evidence"]) == {
|
||||
f"demo:{generation_a}:chunk:1": "sha256:" + "a" * 64,
|
||||
f"demo:{generation_a}:chunk:2": "sha256:" + "b" * 64,
|
||||
f"demo:{generation_b}:chunk:3": "sha256:" + "c" * 64,
|
||||
}
|
||||
assert store.list_evidence_generations("evidence", "demo") == [generation_a, generation_b]
|
||||
assert store.delete_generation("evidence", generation_a, "demo") == 2
|
||||
offsets = [
|
||||
call[2].get("offset")
|
||||
for call in fake.calls
|
||||
if call[0] == "POST" and call[1].endswith("/points/scroll")
|
||||
]
|
||||
assert offsets[:2] == [None, "page-2"]
|
||||
|
||||
Reference in New Issue
Block a user