22#
33# SPDX-License-Identifier: Apache-2.0
44
5+ import inspect
56from typing import Any , Protocol
67
78import pytest
@@ -40,6 +41,58 @@ async def delete_documents_async(self, document_ids: list[str]) -> None:
4041 ...
4142
4243
44+ class DeleteAllAsyncTest :
45+ """
46+ Tests for Document Store delete_all_documents_async().
47+
48+ To use it create a custom test class and override the `document_store` fixture.
49+ Only mix in for stores that implement delete_all_documents_async.
50+ """
51+
52+ @staticmethod
53+ def _delete_all_supports_recreate (document_store : AsyncDocumentStore ) -> tuple [bool , str | None ]:
54+ """
55+ Return (True, param_name) if delete_all_documents_async has recreate_index or recreate_collection.
56+ """
57+ sig = inspect .signature (document_store .delete_all_documents_async ) # type:ignore[attr-defined]
58+ if "recreate_index" in sig .parameters :
59+ return True , "recreate_index"
60+ if "recreate_collection" in sig .parameters :
61+ return True , "recreate_collection"
62+ return False , None
63+
64+ @staticmethod
65+ @pytest .mark .asyncio
66+ async def test_delete_all_documents_async (document_store : AsyncDocumentStore ):
67+ """
68+ Test delete_all_documents_async() normal behaviour.
69+
70+ This test verifies that delete_all_documents_async() removes all documents from the store
71+ and that the store remains functional after deletion.
72+ """
73+ docs = [Document (content = "first doc" , id = "1" ), Document (content = "second doc" , id = "2" )]
74+ await document_store .write_documents_async (docs )
75+ assert await document_store .count_documents_async () == 2
76+
77+ await document_store .delete_all_documents_async () # type:ignore[attr-defined]
78+ assert await document_store .count_documents_async () == 0
79+
80+ new_doc = Document (content = "new doc after delete all" , id = "3" )
81+ await document_store .write_documents_async ([new_doc ])
82+ assert await document_store .count_documents_async () == 1
83+
84+ @staticmethod
85+ @pytest .mark .asyncio
86+ async def test_delete_all_documents_empty_store_async (document_store : AsyncDocumentStore ):
87+ """
88+ Test delete_all_documents_async() on an empty store.
89+
90+ This should not raise an error and should leave the store empty.
91+ """
92+ assert await document_store .count_documents_async () == 0
93+ await document_store .delete_all_documents_async () # type:ignore[attr-defined]
94+
95+
4396class CountDocumentsAsyncTest :
4497 """
4598 Utility class to test a Document Store `count_documents_async` method.
@@ -63,6 +116,148 @@ async def test_count_empty_async(document_store: AsyncDocumentStore):
63116
64117 @staticmethod
65118 @pytest .mark .asyncio
119+ async def test_delete_all_documents_without_recreate_index_async (document_store : AsyncDocumentStore ):
120+ """
121+ Test delete_all_documents_async() with recreate_index/recreate_collection=False when supported.
122+
123+ Skipped if the store's delete_all_documents_async does not have recreate_index or recreate_collection.
124+ """
125+ supports , param_name = DeleteAllAsyncTest ._delete_all_supports_recreate (document_store )
126+ if not supports or param_name is None :
127+ pytest .skip ("delete_all_documents_async has no recreate_index or recreate_collection parameter" )
128+
129+ docs = [Document (id = "1" , content = "A first document" ), Document (id = "2" , content = "Second document" )]
130+ await document_store .write_documents_async (docs )
131+ assert await document_store .count_documents_async () == 2
132+
133+ await document_store .delete_all_documents_async (** {param_name : False }) # type:ignore[attr-defined]
134+ assert await document_store .count_documents_async () == 0
135+
136+ new_doc = Document (id = "3" , content = "New document after delete all" )
137+ await document_store .write_documents_async ([new_doc ])
138+ assert await document_store .count_documents_async () == 1
139+
140+ @staticmethod
141+ @pytest .mark .asyncio
142+ async def test_delete_all_documents_with_recreate_index_async (document_store : AsyncDocumentStore ):
143+ """
144+ Test delete_all_documents_async() with recreate_index/recreate_collection=True when supported.
145+
146+ Skipped if the store's delete_all_documents_async does not have recreate_index or recreate_collection.
147+ """
148+ supports , param_name = DeleteAllAsyncTest ._delete_all_supports_recreate (document_store )
149+ if not supports or param_name is None :
150+ pytest .skip ("delete_all_documents_async has no recreate_index or recreate_collection parameter" )
151+
152+ docs = [Document (id = "1" , content = "A first document" ), Document (id = "2" , content = "Second document" )]
153+ await document_store .write_documents_async (docs )
154+ assert await document_store .count_documents_async () == 2
155+
156+ await document_store .delete_all_documents_async (** {param_name : True }) # type:ignore[attr-defined]
157+ assert await document_store .count_documents_async () == 0
158+
159+ new_doc = Document (id = "3" , content = "New document after delete all with recreate" )
160+ await document_store .write_documents_async ([new_doc ])
161+ assert await document_store .count_documents_async () == 1
162+
163+ retrieved = await document_store .filter_documents_async ()
164+ assert len (retrieved ) == 1
165+ assert retrieved [0 ].content == "New document after delete all with recreate"
166+
167+
168+ class DeleteByFilterAsyncTest :
169+ """
170+ Tests for Document Store delete_by_filter_async().
171+ """
172+
173+ @staticmethod
174+ def _delete_by_filter_params (document_store : AsyncDocumentStore ) -> dict [str , bool ]:
175+ """
176+ Return optional parameters supported by delete_by_filter_async.
177+ """
178+ sig = inspect .signature (document_store .delete_by_filter_async ) # type:ignore[attr-defined]
179+ return {"refresh" : True } if "refresh" in sig .parameters else {}
180+
181+ @staticmethod
182+ @pytest .mark .asyncio
183+ async def test_delete_by_filter_async (document_store : AsyncDocumentStore ):
184+ """Delete documents matching a filter and verify count and remaining docs."""
185+ docs = [
186+ Document (content = "Doc 1" , meta = {"category" : "Alpha" }),
187+ Document (content = "Doc 2" , meta = {"category" : "Beta" }),
188+ Document (content = "Doc 3" , meta = {"category" : "Alpha" }),
189+ ]
190+ await document_store .write_documents_async (docs )
191+ assert await document_store .count_documents_async () == 3
192+
193+ params = DeleteByFilterAsyncTest ._delete_by_filter_params (document_store )
194+ deleted_count = await document_store .delete_by_filter_async ( # type:ignore[attr-defined]
195+ filters = {"field" : "meta.category" , "operator" : "==" , "value" : "Alpha" }, ** params
196+ )
197+ assert deleted_count == 2
198+ assert await document_store .count_documents_async () == 1
199+
200+ remaining_docs = await document_store .filter_documents_async ()
201+ assert len (remaining_docs ) == 1
202+ assert remaining_docs [0 ].meta ["category" ] == "Beta"
203+
204+ @staticmethod
205+ @pytest .mark .asyncio
206+ async def test_delete_by_filter_no_matches_async (document_store : AsyncDocumentStore ):
207+ """Delete with a filter that matches no documents returns 0 and leaves store unchanged."""
208+ docs = [
209+ Document (content = "Doc 1" , meta = {"category" : "Alpha" }),
210+ Document (content = "Doc 2" , meta = {"category" : "Beta" }),
211+ ]
212+ await document_store .write_documents_async (docs )
213+ assert await document_store .count_documents_async () == 2
214+
215+ params = DeleteByFilterAsyncTest ._delete_by_filter_params (document_store )
216+ deleted_count = await document_store .delete_by_filter_async ( # type:ignore[attr-defined]
217+ filters = {"field" : "meta.category" , "operator" : "==" , "value" : "Gamma" }, ** params
218+ )
219+ assert deleted_count == 0
220+ assert await document_store .count_documents_async () == 2
221+
222+ @staticmethod
223+ @pytest .mark .asyncio
224+ async def test_delete_by_filter_advanced_filters_async (document_store : AsyncDocumentStore ):
225+ """Delete with AND/OR filter combinations and verify remaining documents."""
226+ docs = [
227+ Document (content = "Doc 1" , meta = {"category" : "Alpha" , "year" : 2023 , "status" : "draft" }),
228+ Document (content = "Doc 2" , meta = {"category" : "Alpha" , "year" : 2024 , "status" : "published" }),
229+ Document (content = "Doc 3" , meta = {"category" : "Beta" , "year" : 2023 , "status" : "draft" }),
230+ ]
231+ await document_store .write_documents_async (docs )
232+ assert await document_store .count_documents_async () == 3
233+
234+ params = DeleteByFilterAsyncTest ._delete_by_filter_params (document_store )
235+ deleted_count = await document_store .delete_by_filter_async ( # type:ignore[attr-defined]
236+ filters = {
237+ "operator" : "AND" ,
238+ "conditions" : [
239+ {"field" : "meta.category" , "operator" : "==" , "value" : "Alpha" },
240+ {"field" : "meta.year" , "operator" : "==" , "value" : 2023 },
241+ ],
242+ },
243+ ** params ,
244+ )
245+ assert deleted_count == 1
246+ assert await document_store .count_documents_async () == 2
247+
248+ deleted_count = await document_store .delete_by_filter_async ( # type:ignore[attr-defined]
249+ filters = {
250+ "operator" : "OR" ,
251+ "conditions" : [
252+ {"field" : "meta.category" , "operator" : "==" , "value" : "Beta" },
253+ {"field" : "meta.status" , "operator" : "==" , "value" : "published" },
254+ ],
255+ },
256+ ** params ,
257+ )
258+ assert deleted_count == 2
259+ assert await document_store .count_documents_async () == 0
260+
66261 async def test_count_not_empty_async (document_store : AsyncDocumentStore ):
67262 """Test count is greater than zero if the document store contains documents."""
68263 await document_store .write_documents_async (
0 commit comments