-
Notifications
You must be signed in to change notification settings - Fork 4
fix for the issue#42 #44
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -1,4 +1,7 @@ | ||
| import pytest | ||
| from unittest.mock import DEFAULT | ||
|
|
||
| from qdrant_client.http.exceptions import UnexpectedResponse | ||
|
|
||
| from qql.ast_nodes import ( | ||
| AlterCollectionStmt, | ||
|
|
@@ -52,8 +55,20 @@ def collection_exists(_name): | |
| def create_collection(**_kwargs): | ||
| state["exists"] = True | ||
|
|
||
| def get_collection(_name): | ||
| if not (state["exists"] or bool(client.collection_exists.return_value)): | ||
| raise UnexpectedResponse( | ||
| status_code=404, | ||
| reason_phrase="Not Found", | ||
| content=b"Not Found", | ||
| headers={}, | ||
| ) | ||
| # Collection exists — fall through to the configured return_value | ||
| return DEFAULT | ||
|
|
||
| client.collection_exists.side_effect = collection_exists | ||
| client.create_collection.side_effect = create_collection | ||
| client.get_collection.side_effect = get_collection | ||
| return client | ||
|
|
||
|
|
||
|
|
@@ -3492,3 +3507,107 @@ def test_grouped_hybrid_search_uses_build_hybrid_vectors(self, executor, mock_cl | |
| ) | ||
| executor.execute(node) | ||
| helper.assert_called_once() | ||
|
|
||
|
|
||
| # ── Regression: single metadata fetch path ──────────────────────────────────── | ||
|
|
||
|
|
||
| class TestCollectionMetadataFetchCount: | ||
| """Verify that every high-level operation issues exactly ONE get_collection() | ||
| call (Issue #42 — eliminate duplicate Qdrant API calls). | ||
|
|
||
| _resolve_topology() must delegate to _fetch_collection_info() which calls | ||
| get_collection() once and handles 404 as exists=False without a prior | ||
| collection_exists() round-trip. | ||
| """ | ||
|
|
||
| def test_insert_existing_dense_collection_fetches_metadata_once( | ||
| self, executor, mock_client | ||
| ): | ||
| """Dense INSERT to an existing collection must call get_collection() exactly once.""" | ||
| mock_client.collection_exists.return_value = True | ||
| mock_client.get_collection.return_value.config.params.vectors.size = 384 | ||
| # Use a plain unnamed-dense mock (non-dict vectors) | ||
| mock_client.get_collection.return_value.config.params.sparse_vectors = None | ||
|
|
||
| node = InsertStmt(collection="docs", values={"text": "hello"}, model=None) | ||
| executor.execute(node) | ||
|
|
||
| mock_client.get_collection.assert_called_once() | ||
|
|
||
|
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. mock_client.collection_exists.assert_not_called() |
||
| def test_insert_existing_hybrid_collection_fetches_metadata_once( | ||
| self, executor, mock_client | ||
| ): | ||
| """Hybrid auto-detect INSERT must call get_collection() exactly once.""" | ||
| from qdrant_client.models import Distance, SparseVectorParams, VectorParams | ||
|
|
||
| mock_client.collection_exists.return_value = True | ||
| mock_client.get_collection.return_value.config.params.vectors = { | ||
| "dense": VectorParams(size=384, distance=Distance.COSINE) | ||
| } | ||
| mock_client.get_collection.return_value.config.params.sparse_vectors = { | ||
| "sparse": SparseVectorParams() | ||
| } | ||
|
|
||
| node = InsertStmt(collection="docs", values={"text": "hello"}, model=None) | ||
| executor.execute(node) | ||
|
|
||
| mock_client.get_collection.assert_called_once() | ||
|
|
||
|
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. mock_client.collection_exists.assert_not_called() |
||
| def test_dimension_mismatch_fetches_metadata_once(self, executor, mock_client): | ||
| """A dimension-mismatch error must be raised without a second metadata fetch.""" | ||
| from qdrant_client.models import Distance, VectorParams | ||
|
|
||
| mock_client.collection_exists.return_value = True | ||
| mock_client.get_collection.return_value.config.params.vectors = { | ||
| "dense": VectorParams(size=768, distance=Distance.COSINE) | ||
| } | ||
| mock_client.get_collection.return_value.config.params.sparse_vectors = None | ||
|
|
||
| node = InsertStmt( | ||
| collection="docs", | ||
| values={"text": "hello"}, | ||
| model=None, | ||
| dense_vector="dense", | ||
| ) | ||
| from qql.exceptions import QQLRuntimeError | ||
|
|
||
| with pytest.raises(QQLRuntimeError, match="dimension mismatch"): | ||
| executor.execute(node) | ||
|
|
||
| # Only one metadata call despite the error | ||
| mock_client.get_collection.assert_called_once() | ||
|
|
||
|
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. mock_client.collection_exists.assert_not_called() |
||
| def test_insert_nonexistent_collection_issues_one_get_collection_call( | ||
| self, executor, mock_client | ||
|
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Change it to |
||
| ): | ||
| """Inserting into a brand-new collection calls get_collection() once | ||
| (which returns 404 and is handled as exists=False) then creates the collection.""" | ||
| mock_client.collection_exists.return_value = False | ||
|
|
||
|
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. create_collection = mocker.patch.object(executor, "_create_collection_and_wait") |
||
| node = InsertStmt(collection="new_col", values={"text": "hello"}, model=None) | ||
| executor.execute(node) | ||
|
|
||
| # _fetch_collection_info() must have been called once (got 404 → new collection) | ||
| mock_client.get_collection.assert_called_once() | ||
| mock_client.create_collection.assert_called_once() | ||
|
|
||
|
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. mock_client.collection_exists.assert_not_called() |
||
| def test_search_existing_collection_fetches_metadata_once( | ||
| self, executor, mock_client | ||
| ): | ||
| """SEARCH on an existing dense collection must call get_collection() exactly once.""" | ||
| from qdrant_client.models import Distance, VectorParams | ||
|
|
||
| mock_client.collection_exists.return_value = True | ||
| mock_client.get_collection.return_value.config.params.vectors = { | ||
| "dense": VectorParams(size=384, distance=Distance.COSINE) | ||
| } | ||
| mock_client.get_collection.return_value.config.params.sparse_vectors = None | ||
| mock_client.query_points.return_value.points = [] | ||
|
|
||
| node = SearchStmt( | ||
| collection="docs", query_text="hello", limit=5, model=None | ||
| ) | ||
| executor.execute(node) | ||
|
|
||
| mock_client.get_collection.assert_called_once() | ||
|
Comment on lines
+3524
to
+3613
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Coverage gap: assert that These tests currently prove one ✅ Suggested test hardening class TestCollectionMetadataFetchCount:
@@
def test_insert_existing_dense_collection_fetches_metadata_once(
self, executor, mock_client
):
@@
executor.execute(node)
mock_client.get_collection.assert_called_once()
+ mock_client.collection_exists.assert_not_called()
@@
def test_insert_existing_hybrid_collection_fetches_metadata_once(
self, executor, mock_client
):
@@
executor.execute(node)
mock_client.get_collection.assert_called_once()
+ mock_client.collection_exists.assert_not_called()
@@
def test_dimension_mismatch_fetches_metadata_once(self, executor, mock_client):
@@
with pytest.raises(QQLRuntimeError, match="dimension mismatch"):
executor.execute(node)
# Only one metadata call despite the error
mock_client.get_collection.assert_called_once()
+ mock_client.collection_exists.assert_not_called()
@@
def test_insert_nonexistent_collection_issues_one_get_collection_call(
self, executor, mock_client
):
@@
# _fetch_collection_info() must have been called once (got 404 → new collection)
mock_client.get_collection.assert_called_once()
+ mock_client.collection_exists.assert_not_called()
mock_client.create_collection.assert_called_once()
@@
def test_search_existing_collection_fetches_metadata_once(
self, executor, mock_client
):
@@
executor.execute(node)
mock_client.get_collection.assert_called_once()
+ mock_client.collection_exists.assert_not_called()🤖 Prompt for AI Agents
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. mock_client.collection_exists.assert_not_called()
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. def test_local_qdrant_missing_collection_resolves_as_absent(self, cfg): |
||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
add something like this .. so that get_collection() raises ValueError instead of HTTP UnexpectedResponse(404)