@@ -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