@@ -182,3 +182,110 @@ async def test_search_memory_empty_results(mock_vertexai_client):
182182 )
183183
184184 assert len (result .memories ) == 0
185+
186+
187+ @pytest .mark .asyncio
188+ async def test_search_memory_skips_entry_with_none_memory (mock_vertexai_client ):
189+ bad_entry = mock .MagicMock ()
190+ bad_entry .memory = None
191+
192+ good_entry = mock .MagicMock ()
193+ good_entry .memory .fact = 'good fact'
194+ good_entry .memory .update_time = datetime (2024 , 1 , 1 )
195+
196+ mock_vertexai_client .agent_engines .memories .retrieve .return_value = [
197+ bad_entry ,
198+ good_entry ,
199+ ]
200+ memory_service = mock_vertex_ai_memory_bank_service ()
201+
202+ result = await memory_service .search_memory (
203+ app_name = MOCK_APP_NAME , user_id = MOCK_USER_ID , query = 'query'
204+ )
205+
206+ assert len (result .memories ) == 1
207+ assert result .memories [0 ].content .parts [0 ].text == 'good fact'
208+
209+
210+ @pytest .mark .asyncio
211+ async def test_search_memory_skips_entry_with_empty_fact (mock_vertexai_client ):
212+ for empty_fact in [None , '' ]:
213+ bad_entry = mock .MagicMock ()
214+ bad_entry .memory .fact = empty_fact
215+ bad_entry .memory .update_time = datetime (2024 , 1 , 1 )
216+
217+ mock_vertexai_client .agent_engines .memories .retrieve .return_value = [
218+ bad_entry
219+ ]
220+ memory_service = mock_vertex_ai_memory_bank_service ()
221+
222+ result = await memory_service .search_memory (
223+ app_name = MOCK_APP_NAME , user_id = MOCK_USER_ID , query = 'query'
224+ )
225+
226+ assert len (result .memories ) == 0
227+
228+
229+ @pytest .mark .asyncio
230+ async def test_search_memory_handles_missing_update_time (mock_vertexai_client ):
231+ entry = mock .MagicMock ()
232+ entry .memory .fact = 'some fact'
233+ entry .memory .update_time = None
234+
235+ mock_vertexai_client .agent_engines .memories .retrieve .return_value = [entry ]
236+ memory_service = mock_vertex_ai_memory_bank_service ()
237+
238+ result = await memory_service .search_memory (
239+ app_name = MOCK_APP_NAME , user_id = MOCK_USER_ID , query = 'query'
240+ )
241+
242+ assert len (result .memories ) == 1
243+ assert result .memories [0 ].content .parts [0 ].text == 'some fact'
244+ assert result .memories [0 ].timestamp is None
245+
246+
247+ @pytest .mark .asyncio
248+ async def test_search_memory_skips_malformed_entry (mock_vertexai_client ):
249+ malformed = mock .MagicMock (spec = []) # no attributes → AttributeError
250+
251+ good_entry = mock .MagicMock ()
252+ good_entry .memory .fact = 'good fact'
253+ good_entry .memory .update_time = datetime (2024 , 1 , 1 )
254+
255+ mock_vertexai_client .agent_engines .memories .retrieve .return_value = [
256+ malformed ,
257+ good_entry ,
258+ ]
259+ memory_service = mock_vertex_ai_memory_bank_service ()
260+
261+ result = await memory_service .search_memory (
262+ app_name = MOCK_APP_NAME , user_id = MOCK_USER_ID , query = 'query'
263+ )
264+
265+ assert len (result .memories ) == 1
266+ assert result .memories [0 ].content .parts [0 ].text == 'good fact'
267+
268+
269+ @pytest .mark .asyncio
270+ async def test_search_memory_returns_partial_results_on_iterator_error (
271+ mock_vertexai_client ,
272+ ):
273+ good_entry = mock .MagicMock ()
274+ good_entry .memory .fact = 'good fact'
275+ good_entry .memory .update_time = datetime (2024 , 1 , 1 )
276+
277+ def failing_iterator ():
278+ yield good_entry
279+ raise RuntimeError ('API stream error' )
280+
281+ mock_vertexai_client .agent_engines .memories .retrieve .return_value = (
282+ failing_iterator ()
283+ )
284+ memory_service = mock_vertex_ai_memory_bank_service ()
285+
286+ result = await memory_service .search_memory (
287+ app_name = MOCK_APP_NAME , user_id = MOCK_USER_ID , query = 'query'
288+ )
289+
290+ assert len (result .memories ) == 1
291+ assert result .memories [0 ].content .parts [0 ].text == 'good fact'
0 commit comments