2121 MCPToolsResponse ,
2222)
2323from rag_solution .schemas .search_schema import SearchOutput
24- from vectordbs .data_types import DocumentMetadata , QueryResult
24+ from vectordbs .data_types import DocumentChunkWithScore , DocumentMetadata , QueryResult
2525
2626
2727class TestSearchResultEnricher :
@@ -50,17 +50,19 @@ def mock_search_output(self):
5050 answer = "This is a test answer" ,
5151 documents = [
5252 DocumentMetadata (
53- doc_id = "doc1" ,
54- file_name = "test.pdf" ,
55- file_type = "pdf" ,
56- created_at = datetime .utcnow (),
53+ document_name = "test.pdf" ,
54+ title = "Test Document" ,
55+ creation_date = datetime .utcnow (),
5756 )
5857 ],
5958 query_results = [
6059 QueryResult (
6160 score = 0.95 ,
62- text = "This is relevant text from the document." ,
63- document_chunk_id = "chunk1" ,
61+ chunk = DocumentChunkWithScore (
62+ chunk_id = "chunk1" ,
63+ text = "This is relevant text from the document." ,
64+ document_id = "doc1" ,
65+ ),
6466 )
6567 ],
6668 rewritten_query = "test query" ,
@@ -102,15 +104,16 @@ async def test_enrich_config_disabled_returns_original(self, enricher, mock_sear
102104 @pytest .mark .asyncio
103105 async def test_enrich_gateway_unavailable (self , enricher , mock_search_output ):
104106 """Test enrichment handles unavailable gateway gracefully."""
105- with patch .object (enricher , "mcp_client" ) as mock_client :
106- mock_client .is_available = AsyncMock (return_value = False )
107+ mock_client = MagicMock ()
108+ mock_client .is_available = AsyncMock (return_value = False )
109+ enricher ._mcp_client = mock_client
107110
108- result = await enricher .enrich (mock_search_output )
111+ result = await enricher .enrich (mock_search_output )
109112
110- assert result .metadata is not None
111- assert "mcp_enrichment" in result .metadata
112- assert result .metadata ["mcp_enrichment" ]["success" ] is False
113- assert "unavailable" in result .metadata ["mcp_enrichment" ]["error" ].lower ()
113+ assert result .metadata is not None
114+ assert "mcp_enrichment" in result .metadata
115+ assert result .metadata ["mcp_enrichment" ]["success" ] is False
116+ assert "unavailable" in result .metadata ["mcp_enrichment" ]["error" ].lower ()
114117
115118 @pytest .mark .asyncio
116119 async def test_enrich_success_with_tools (self , enricher , mock_search_output ):
@@ -134,19 +137,20 @@ async def test_enrich_success_with_tools(self, enricher, mock_search_output):
134137 execution_time_ms = 100.0 ,
135138 )
136139
137- with patch .object (enricher , "mcp_client" ) as mock_client :
138- mock_client .is_available = AsyncMock (return_value = True )
139- mock_client .list_tools = AsyncMock (return_value = mock_tools_response )
140- mock_client .invoke_tool = AsyncMock (return_value = mock_invocation_result )
140+ mock_client = MagicMock ()
141+ mock_client .is_available = AsyncMock (return_value = True )
142+ mock_client .list_tools = AsyncMock (return_value = mock_tools_response )
143+ mock_client .invoke_tool = AsyncMock (return_value = mock_invocation_result )
144+ enricher ._mcp_client = mock_client
141145
142- result = await enricher .enrich (mock_search_output )
146+ result = await enricher .enrich (mock_search_output )
143147
144- assert result .metadata is not None
145- assert "mcp_enrichment" in result .metadata
146- assert result .metadata ["mcp_enrichment" ]["success" ] is True
147- assert len (result .metadata ["mcp_enrichment" ]["tools" ]) == 1
148- assert result .metadata ["mcp_enrichment" ]["tools" ][0 ]["name" ] == "summarizer"
149- assert result .metadata ["mcp_enrichment" ]["tools" ][0 ]["success" ] is True
148+ assert result .metadata is not None
149+ assert "mcp_enrichment" in result .metadata
150+ assert result .metadata ["mcp_enrichment" ]["success" ] is True
151+ assert len (result .metadata ["mcp_enrichment" ]["tools" ]) == 1
152+ assert result .metadata ["mcp_enrichment" ]["tools" ][0 ]["name" ] == "summarizer"
153+ assert result .metadata ["mcp_enrichment" ]["tools" ][0 ]["success" ] is True
150154
151155 @pytest .mark .asyncio
152156 async def test_enrich_with_specific_tools (self , enricher , mock_search_output ):
@@ -164,16 +168,17 @@ async def test_enrich_with_specific_tools(self, enricher, mock_search_output):
164168 execution_time_ms = 50.0 ,
165169 )
166170
167- with patch .object (enricher , "mcp_client" ) as mock_client :
168- mock_client .is_available = AsyncMock (return_value = True )
169- mock_client .invoke_tool = AsyncMock (return_value = mock_invocation_result )
171+ mock_client = MagicMock ()
172+ mock_client .is_available = AsyncMock (return_value = True )
173+ mock_client .invoke_tool = AsyncMock (return_value = mock_invocation_result )
174+ enricher ._mcp_client = mock_client
170175
171- result = await enricher .enrich (mock_search_output , config )
176+ result = await enricher .enrich (mock_search_output , config )
172177
173- # Should use custom_tool from config
174- mock_client .invoke_tool .assert_called_once ()
175- call_args = mock_client .invoke_tool .call_args
176- assert call_args [0 ][0 ] == "custom_tool"
178+ # Should use custom_tool from config
179+ mock_client .invoke_tool .assert_called_once ()
180+ call_args = mock_client .invoke_tool .call_args
181+ assert call_args [0 ][0 ] == "custom_tool"
177182
178183 @pytest .mark .asyncio
179184 async def test_enrich_parallel_execution (self , enricher , mock_search_output ):
@@ -184,13 +189,6 @@ async def test_enrich_parallel_execution(self, enricher, mock_search_output):
184189 parallel = True ,
185190 )
186191
187- mock_result = MCPInvocationOutput (
188- tool_name = "" ,
189- status = MCPInvocationStatus .SUCCESS ,
190- result = {"data" : "result" },
191- execution_time_ms = 50.0 ,
192- )
193-
194192 call_count = 0
195193
196194 async def mock_invoke (name , args , timeout = None ):
@@ -203,15 +201,16 @@ async def mock_invoke(name, args, timeout=None):
203201 execution_time_ms = 50.0 ,
204202 )
205203
206- with patch .object (enricher , "mcp_client" ) as mock_client :
207- mock_client .is_available = AsyncMock (return_value = True )
208- mock_client .invoke_tool = AsyncMock (side_effect = mock_invoke )
204+ mock_client = MagicMock ()
205+ mock_client .is_available = AsyncMock (return_value = True )
206+ mock_client .invoke_tool = AsyncMock (side_effect = mock_invoke )
207+ enricher ._mcp_client = mock_client
209208
210- result = await enricher .enrich (mock_search_output , config )
209+ result = await enricher .enrich (mock_search_output , config )
211210
212- # All tools should be called
213- assert call_count == 3
214- assert len (result .metadata ["mcp_enrichment" ]["tools" ]) == 3
211+ # All tools should be called
212+ assert call_count == 3
213+ assert len (result .metadata ["mcp_enrichment" ]["tools" ]) == 3
215214
216215 @pytest .mark .asyncio
217216 async def test_enrich_sequential_execution (self , enricher , mock_search_output ):
@@ -233,14 +232,15 @@ async def mock_invoke(name, args, timeout=None):
233232 execution_time_ms = 50.0 ,
234233 )
235234
236- with patch .object (enricher , "mcp_client" ) as mock_client :
237- mock_client .is_available = AsyncMock (return_value = True )
238- mock_client .invoke_tool = AsyncMock (side_effect = mock_invoke )
235+ mock_client = MagicMock ()
236+ mock_client .is_available = AsyncMock (return_value = True )
237+ mock_client .invoke_tool = AsyncMock (side_effect = mock_invoke )
238+ enricher ._mcp_client = mock_client
239239
240- await enricher .enrich (mock_search_output , config )
240+ await enricher .enrich (mock_search_output , config )
241241
242- # Should be in order for sequential execution
243- assert execution_order == ["tool1" , "tool2" ]
242+ # Should be in order for sequential execution
243+ assert execution_order == ["tool1" , "tool2" ]
244244
245245 @pytest .mark .asyncio
246246 async def test_enrich_handles_tool_failure (self , enricher , mock_search_output ):
@@ -266,22 +266,23 @@ async def mock_invoke(name, args, timeout=None):
266266 execution_time_ms = 50.0 ,
267267 )
268268
269- with patch .object (enricher , "mcp_client" ) as mock_client :
270- mock_client .is_available = AsyncMock (return_value = True )
271- mock_client .invoke_tool = AsyncMock (side_effect = mock_invoke )
269+ mock_client = MagicMock ()
270+ mock_client .is_available = AsyncMock (return_value = True )
271+ mock_client .invoke_tool = AsyncMock (side_effect = mock_invoke )
272+ enricher ._mcp_client = mock_client
272273
273- result = await enricher .enrich (mock_search_output , config )
274+ result = await enricher .enrich (mock_search_output , config )
274275
275- # Should still have results, with one success and one failure
276- tools = result .metadata ["mcp_enrichment" ]["tools" ]
277- assert len (tools ) == 2
276+ # Should still have results, with one success and one failure
277+ tools = result .metadata ["mcp_enrichment" ]["tools" ]
278+ assert len (tools ) == 2
278279
279- working = next (t for t in tools if t ["name" ] == "working_tool" )
280- failing = next (t for t in tools if t ["name" ] == "failing_tool" )
280+ working = next (t for t in tools if t ["name" ] == "working_tool" )
281+ failing = next (t for t in tools if t ["name" ] == "failing_tool" )
281282
282- assert working ["success" ] is True
283- assert failing ["success" ] is False
284- assert failing ["error" ] == "Tool failed"
283+ assert working ["success" ] is True
284+ assert failing ["success" ] is False
285+ assert failing ["error" ] == "Tool failed"
285286
286287 @pytest .mark .asyncio
287288 async def test_enrich_preserves_original_output (self , enricher , mock_search_output ):
@@ -292,30 +293,37 @@ async def test_enrich_preserves_original_output(self, enricher, mock_search_outp
292293
293294 config = MCPEnrichmentConfig (enabled = True , tools = ["tool1" ])
294295
295- with patch .object (enricher , "mcp_client" ) as mock_client :
296- mock_client .is_available = AsyncMock (return_value = True )
297- mock_client .invoke_tool = AsyncMock (
298- return_value = MCPInvocationOutput (
299- tool_name = "tool1" ,
300- status = MCPInvocationStatus .SUCCESS ,
301- result = {"modified" : True },
302- execution_time_ms = 50.0 ,
303- )
296+ mock_client = MagicMock ()
297+ mock_client .is_available = AsyncMock (return_value = True )
298+ mock_client .invoke_tool = AsyncMock (
299+ return_value = MCPInvocationOutput (
300+ tool_name = "tool1" ,
301+ status = MCPInvocationStatus .SUCCESS ,
302+ result = {"modified" : True },
303+ execution_time_ms = 50.0 ,
304304 )
305+ )
306+ enricher ._mcp_client = mock_client
305307
306- result = await enricher .enrich (mock_search_output , config )
308+ result = await enricher .enrich (mock_search_output , config )
307309
308- # Original fields should be unchanged
309- assert result .answer == original_answer
310- assert result .documents == original_docs
311- assert result .query_results == original_results
310+ # Original fields should be unchanged
311+ assert result .answer == original_answer
312+ assert result .documents == original_docs
313+ assert result .query_results == original_results
312314
313315 @pytest .mark .asyncio
314316 async def test_enrich_query_results_single_tool (self , enricher ):
315317 """Test enriching individual query results with a tool."""
316318 query_results = [
317- QueryResult (score = 0.9 , text = "Text 1" , document_chunk_id = "c1" ),
318- QueryResult (score = 0.8 , text = "Text 2" , document_chunk_id = "c2" ),
319+ QueryResult (
320+ score = 0.9 ,
321+ chunk = DocumentChunkWithScore (chunk_id = "c1" , text = "Text 1" , document_id = "d1" ),
322+ ),
323+ QueryResult (
324+ score = 0.8 ,
325+ chunk = DocumentChunkWithScore (chunk_id = "c2" , text = "Text 2" , document_id = "d2" ),
326+ ),
319327 ]
320328
321329 async def mock_invoke (name , args , timeout = None ):
@@ -326,20 +334,21 @@ async def mock_invoke(name, args, timeout=None):
326334 execution_time_ms = 30.0 ,
327335 )
328336
329- with patch .object (enricher , "mcp_client" ) as mock_client :
330- mock_client .invoke_tool = AsyncMock (side_effect = mock_invoke )
337+ mock_client = MagicMock ()
338+ mock_client .invoke_tool = AsyncMock (side_effect = mock_invoke )
339+ enricher ._mcp_client = mock_client
331340
332- results = await enricher .enrich_query_results (
333- query_results ,
334- "entity_extractor" ,
335- {"extract_types" : ["person" , "org" ]},
336- )
341+ results = await enricher .enrich_query_results (
342+ query_results ,
343+ "entity_extractor" ,
344+ {"extract_types" : ["person" , "org" ]},
345+ )
337346
338- assert len (results ) == 2
339- assert results [0 ].original_score == 0.9
340- assert results [1 ].original_score == 0.8
341- assert len (results [0 ].enrichments ) == 1
342- assert results [0 ].enrichments [0 ].success is True
347+ assert len (results ) == 2
348+ assert results [0 ].original_score == 0.9
349+ assert results [1 ].original_score == 0.8
350+ assert len (results [0 ].enrichments ) == 1
351+ assert results [0 ].enrichments [0 ].success is True
343352
344353 @pytest .mark .asyncio
345354 async def test_enrich_empty_tools_list (self , enricher , mock_search_output ):
@@ -350,14 +359,15 @@ async def test_enrich_empty_tools_list(self, enricher, mock_search_output):
350359 gateway_healthy = True ,
351360 )
352361
353- with patch .object (enricher , "mcp_client" ) as mock_client :
354- mock_client .is_available = AsyncMock (return_value = True )
355- mock_client .list_tools = AsyncMock (return_value = mock_tools_response )
362+ mock_client = MagicMock ()
363+ mock_client .is_available = AsyncMock (return_value = True )
364+ mock_client .list_tools = AsyncMock (return_value = mock_tools_response )
365+ enricher ._mcp_client = mock_client
356366
357- result = await enricher .enrich (mock_search_output )
367+ result = await enricher .enrich (mock_search_output )
358368
359- # Should return original without enrichment metadata
360- assert result .answer == mock_search_output .answer
369+ # Should return original without enrichment metadata
370+ assert result .answer == mock_search_output .answer
361371
362372 @pytest .mark .asyncio
363373 async def test_enrich_merges_with_existing_metadata (self , enricher ):
@@ -371,22 +381,23 @@ async def test_enrich_merges_with_existing_metadata(self, enricher):
371381
372382 config = MCPEnrichmentConfig (enabled = True , tools = ["tool1" ])
373383
374- with patch .object (enricher , "mcp_client" ) as mock_client :
375- mock_client .is_available = AsyncMock (return_value = True )
376- mock_client .invoke_tool = AsyncMock (
377- return_value = MCPInvocationOutput (
378- tool_name = "tool1" ,
379- status = MCPInvocationStatus .SUCCESS ,
380- result = {},
381- execution_time_ms = 50.0 ,
382- )
384+ mock_client = MagicMock ()
385+ mock_client .is_available = AsyncMock (return_value = True )
386+ mock_client .invoke_tool = AsyncMock (
387+ return_value = MCPInvocationOutput (
388+ tool_name = "tool1" ,
389+ status = MCPInvocationStatus .SUCCESS ,
390+ result = {},
391+ execution_time_ms = 50.0 ,
383392 )
393+ )
394+ enricher ._mcp_client = mock_client
384395
385- result = await enricher .enrich (search_output , config )
396+ result = await enricher .enrich (search_output , config )
386397
387- # Both old and new metadata should exist
388- assert result .metadata ["existing_key" ] == "existing_value"
389- assert "mcp_enrichment" in result .metadata
398+ # Both old and new metadata should exist
399+ assert result .metadata ["existing_key" ] == "existing_value"
400+ assert "mcp_enrichment" in result .metadata
390401
391402
392403class TestEnrichmentConfig :
0 commit comments