Skip to content

Commit 488d368

Browse files
committed
fixed the connection closing scenario
1 parent 65c8ecd commit 488d368

3 files changed

Lines changed: 37 additions & 3 deletions

File tree

README.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -178,7 +178,7 @@ Tests do not require a running Qdrant instance — the Qdrant client is mocked.
178178
pytest tests/ -v
179179
```
180180

181-
Expected: **500 tests passing**.
181+
Expected: **549 tests passing**.
182182

183183
---
184184

src/qql/__init__.py

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -36,11 +36,15 @@ def run_query(
3636
) -> ExecutionResult:
3737
"""One-shot convenience function kept for backward compatibility.
3838
39-
Creates a :class:`Connection`, runs one query, and closes the connection.
39+
Creates a :class:`Connection`, runs one query, closes the connection, and
40+
returns the result. The underlying ``QdrantClient`` is always released —
41+
even if the query raises — so repeated calls do not leak resources.
42+
4043
For workloads that issue multiple queries, prefer :class:`Connection`
4144
directly — it reuses a single client across all calls::
4245
4346
with Connection(url, secret=secret) as conn:
4447
result = conn.run_query(query)
4548
"""
46-
return Connection(url=url, secret=secret, default_model=default_model).run_query(query)
49+
with Connection(url=url, secret=secret, default_model=default_model) as conn:
50+
return conn.run_query(query)

tests/test_connection.py

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -150,13 +150,43 @@ def test_run_query_delegates_to_connection(self, mocker):
150150
conn_instance.run_query.return_value = ExecutionResult(
151151
success=True, message="ok", data=[]
152152
)
153+
# Context-manager protocol: __enter__ returns the mock, __exit__ is a no-op
154+
conn_instance.__enter__ = mocker.MagicMock(return_value=conn_instance)
155+
conn_instance.__exit__ = mocker.MagicMock(return_value=False)
153156
conn_cls = mocker.patch("qql.Connection", return_value=conn_instance)
154157
run_query("SHOW COLLECTIONS", url="http://localhost:6333")
155158
conn_cls.assert_called_once_with(
156159
url="http://localhost:6333", secret=None, default_model=None
157160
)
158161
conn_instance.run_query.assert_called_once_with("SHOW COLLECTIONS")
159162

163+
def test_run_query_closes_connection_after_query(self, mocker):
164+
"""run_query() must call close() — it must not leak the QdrantClient."""
165+
mock_client = mocker.MagicMock()
166+
mocker.patch("qdrant_client.QdrantClient", return_value=mock_client)
167+
mock_executor = mocker.MagicMock()
168+
mock_executor.execute.return_value = ExecutionResult(
169+
success=True, message="ok", data=[]
170+
)
171+
mocker.patch("qql.connection.Executor", return_value=mock_executor)
172+
run_query("SHOW COLLECTIONS", url="http://localhost:6333")
173+
# close() must have been called exactly once
174+
mock_client.close.assert_called_once()
175+
176+
def test_run_query_closes_connection_even_when_query_raises(self, mocker):
177+
"""run_query() must call close() even if the query throws."""
178+
mock_client = mocker.MagicMock()
179+
mocker.patch("qdrant_client.QdrantClient", return_value=mock_client)
180+
# Make the query raise a runtime error (e.g. collection not found)
181+
from qql.exceptions import QQLRuntimeError
182+
mock_executor = mocker.MagicMock()
183+
mock_executor.execute.side_effect = QQLRuntimeError("collection 'x' does not exist")
184+
mocker.patch("qql.connection.Executor", return_value=mock_executor)
185+
with pytest.raises(QQLRuntimeError):
186+
run_query("SEARCH x SIMILAR TO 'q' LIMIT 5", url="http://localhost:6333")
187+
# close() still called
188+
mock_client.close.assert_called_once()
189+
160190
def test_run_query_invalid_syntax_still_raises(self, mocker):
161191
mocker.patch("qdrant_client.QdrantClient")
162192
with pytest.raises(QQLSyntaxError):

0 commit comments

Comments
 (0)