11"""Tests for tools module."""
22
33import pytest
4- from unittest .mock import AsyncMock , patch , PropertyMock , Mock
4+ from unittest .mock import AsyncMock , patch , Mock
55import httpx
66from pia_mcp_server .tools .search_tools import (
77 handle_pia_search ,
@@ -52,6 +52,96 @@ async def test_pia_search_success():
5252 assert "Test Document" in result [0 ].text
5353
5454
55+ @pytest .mark .asyncio
56+ async def test_pia_search_with_odata_filter ():
57+ """Test PIA search with OData filter parameter."""
58+ mock_response = {
59+ "jsonrpc" : "2.0" ,
60+ "id" : 1 ,
61+ "result" : {
62+ "documents" : [
63+ {
64+ "title" : "GAO Fraud Report" ,
65+ "id" : "gao_123" ,
66+ "summary" : "GAO fraud investigation" ,
67+ }
68+ ],
69+ "total" : 1 ,
70+ },
71+ }
72+
73+ with patch .object (Settings , "_get_api_key_from_args" , return_value = "test_key" ):
74+ with patch ("httpx.AsyncClient" ) as mock_client :
75+ mock_response_obj = Mock ()
76+ mock_response_obj .json .return_value = mock_response
77+ mock_response_obj .raise_for_status .return_value = None
78+
79+ mock_client_instance = AsyncMock ()
80+ mock_client_instance .post .return_value = mock_response_obj
81+ mock_client .return_value .__aenter__ .return_value = mock_client_instance
82+
83+ # Test with actual field names from the remote implementation
84+ result = await handle_pia_search (
85+ {"query" : "fraud" , "filter" : "SourceDocumentDataSource eq 'GAO'" }
86+ )
87+
88+ # Verify the request was made with the filter
89+ mock_client_instance .post .assert_called_once ()
90+ call_args = mock_client_instance .post .call_args
91+ request_data = call_args [1 ]["json" ]
92+ assert (
93+ request_data ["params" ]["arguments" ]["filter" ]
94+ == "SourceDocumentDataSource eq 'GAO'"
95+ )
96+
97+ assert len (result ) == 1
98+ assert "GAO Fraud Report" in result [0 ].text
99+
100+
101+ @pytest .mark .asyncio
102+ async def test_pia_search_with_complex_odata_filter ():
103+ """Test PIA search with complex OData filter."""
104+ mock_response = {
105+ "jsonrpc" : "2.0" ,
106+ "id" : 1 ,
107+ "result" : {
108+ "documents" : [
109+ {
110+ "title" : "High Priority GAO Report" ,
111+ "id" : "gao_456" ,
112+ "summary" : "High priority integrity violation" ,
113+ }
114+ ],
115+ "total" : 1 ,
116+ },
117+ }
118+
119+ with patch .object (Settings , "_get_api_key_from_args" , return_value = "test_key" ):
120+ with patch ("httpx.AsyncClient" ) as mock_client :
121+ mock_response_obj = Mock ()
122+ mock_response_obj .json .return_value = mock_response
123+ mock_response_obj .raise_for_status .return_value = None
124+
125+ mock_client_instance = AsyncMock ()
126+ mock_client_instance .post .return_value = mock_response_obj
127+ mock_client .return_value .__aenter__ .return_value = mock_client_instance
128+
129+ # Test complex boolean logic filter
130+ complex_filter = "(SourceDocumentDataSource eq 'GAO' or SourceDocumentDataSource eq 'OIG') and RecPriorityFlag in ('High', 'Critical')"
131+ result = await handle_pia_search (
132+ {"query" : "integrity violations" , "filter" : complex_filter }
133+ )
134+
135+ # Verify the complex filter was passed correctly
136+ mock_client_instance .post .assert_called_once ()
137+ call_args = mock_client_instance .post .call_args
138+ request_data = call_args [1 ]["json" ]
139+ assert request_data ["params" ]["arguments" ]["filter" ] == complex_filter
140+
141+ assert len (result ) == 1
142+ assert "High Priority GAO Report" in result [0 ].text
143+
144+
55145@pytest .mark .asyncio
56146async def test_pia_search_api_error ():
57147 """Test PIA search with API error."""
@@ -118,9 +208,10 @@ async def test_pia_search_facets_success():
118208 "id" : 1 ,
119209 "result" : {
120210 "facets" : {
121- "data_source" : ["OIG" , "GAO" , "CMS" ],
122- "document_type" : ["audit_report" , "investigation" , "guidance" ],
123- "agency" : ["HHS" , "DOD" , "VA" ],
211+ "SourceDocumentDataSource" : ["OIG" , "GAO" , "CMS" ],
212+ "RecStatus" : ["Open" , "Closed" , "In Progress" ],
213+ "RecPriorityFlag" : ["High" , "Medium" , "Low" , "Critical" ],
214+ "IsIntegrityRelated" : ["Yes" , "No" ],
124215 }
125216 },
126217 }
@@ -138,9 +229,56 @@ async def test_pia_search_facets_success():
138229 result = await handle_pia_search_facets ({"query" : "healthcare" })
139230
140231 assert len (result ) == 1
141- assert "data_source " in result [0 ].text
232+ assert "SourceDocumentDataSource " in result [0 ].text
142233 assert "OIG" in result [0 ].text
143- assert "document_type" in result [0 ].text
234+ assert "RecStatus" in result [0 ].text
235+
236+
237+ @pytest .mark .asyncio
238+ async def test_pia_search_facets_with_filter ():
239+ """Test PIA search facets with OData filter parameter."""
240+ mock_response = {
241+ "jsonrpc" : "2.0" ,
242+ "id" : 1 ,
243+ "result" : {
244+ "facets" : {
245+ "SourceDocumentDataSource" : ["GAO" ],
246+ "RecStatus" : ["Open" , "In Progress" ],
247+ "RecPriorityFlag" : ["High" , "Critical" ],
248+ }
249+ },
250+ }
251+
252+ with patch .object (Settings , "_get_api_key_from_args" , return_value = "test_key" ):
253+ with patch ("httpx.AsyncClient" ) as mock_client :
254+ mock_response_obj = Mock ()
255+ mock_response_obj .json .return_value = mock_response
256+ mock_response_obj .raise_for_status .return_value = None
257+
258+ mock_client_instance = AsyncMock ()
259+ mock_client_instance .post .return_value = mock_response_obj
260+ mock_client .return_value .__aenter__ .return_value = mock_client_instance
261+
262+ # Test facets with filter parameter
263+ result = await handle_pia_search_facets (
264+ {
265+ "query" : "fraud" ,
266+ "filter" : "SourceDocumentDataSource eq 'GAO' and RecStatus ne 'Closed'" ,
267+ }
268+ )
269+
270+ # Verify the filter was passed correctly
271+ mock_client_instance .post .assert_called_once ()
272+ call_args = mock_client_instance .post .call_args
273+ request_data = call_args [1 ]["json" ]
274+ assert (
275+ request_data ["params" ]["arguments" ]["filter" ]
276+ == "SourceDocumentDataSource eq 'GAO' and RecStatus ne 'Closed'"
277+ )
278+
279+ assert len (result ) == 1
280+ assert "SourceDocumentDataSource" in result [0 ].text
281+ assert "GAO" in result [0 ].text
144282
145283
146284@pytest .mark .asyncio
@@ -189,3 +327,123 @@ async def test_pia_search_facets_http_error():
189327
190328 assert len (result ) == 1
191329 assert "HTTP Error 403" in result [0 ].text
330+
331+
332+ @pytest .mark .asyncio
333+ async def test_pia_search_empty_filter ():
334+ """Test PIA search with empty filter parameter."""
335+ mock_response = {
336+ "jsonrpc" : "2.0" ,
337+ "id" : 1 ,
338+ "result" : {
339+ "documents" : [
340+ {"title" : "Test Document" , "id" : "123" , "summary" : "Test summary" }
341+ ],
342+ "total" : 1 ,
343+ },
344+ }
345+
346+ with patch .object (Settings , "_get_api_key_from_args" , return_value = "test_key" ):
347+ with patch ("httpx.AsyncClient" ) as mock_client :
348+ mock_response_obj = Mock ()
349+ mock_response_obj .json .return_value = mock_response
350+ mock_response_obj .raise_for_status .return_value = None
351+
352+ mock_client_instance = AsyncMock ()
353+ mock_client_instance .post .return_value = mock_response_obj
354+ mock_client .return_value .__aenter__ .return_value = mock_client_instance
355+
356+ # Test with empty filter (should work normally)
357+ result = await handle_pia_search ({"query" : "test query" , "filter" : "" })
358+
359+ assert len (result ) == 1
360+ assert "Test Document" in result [0 ].text
361+
362+
363+ @pytest .mark .asyncio
364+ async def test_pia_search_with_all_parameters ():
365+ """Test PIA search with all parameters including filter."""
366+ mock_response = {
367+ "jsonrpc" : "2.0" ,
368+ "id" : 1 ,
369+ "result" : {
370+ "documents" : [
371+ {"title" : "Complete Test" , "id" : "456" , "summary" : "Full test" }
372+ ],
373+ "total" : 1 ,
374+ },
375+ }
376+
377+ with patch .object (Settings , "_get_api_key_from_args" , return_value = "test_key" ):
378+ with patch ("httpx.AsyncClient" ) as mock_client :
379+ mock_response_obj = Mock ()
380+ mock_response_obj .json .return_value = mock_response
381+ mock_response_obj .raise_for_status .return_value = None
382+
383+ mock_client_instance = AsyncMock ()
384+ mock_client_instance .post .return_value = mock_response_obj
385+ mock_client .return_value .__aenter__ .return_value = mock_client_instance
386+
387+ # Test with all parameters
388+ result = await handle_pia_search (
389+ {
390+ "query" : "comprehensive test" ,
391+ "filter" : "SourceDocumentDataSource eq 'GAO' and IsIntegrityRelated eq 'Yes'" ,
392+ "page" : 2 ,
393+ "page_size" : 5 ,
394+ "search_mode" : "titles" ,
395+ "limit" : 10 ,
396+ "include_facets" : True ,
397+ }
398+ )
399+
400+ # Verify all parameters were passed correctly
401+ mock_client_instance .post .assert_called_once ()
402+ call_args = mock_client_instance .post .call_args
403+ request_data = call_args [1 ]["json" ]
404+ arguments = request_data ["params" ]["arguments" ]
405+
406+ assert arguments ["query" ] == "comprehensive test"
407+ assert (
408+ arguments ["filter" ]
409+ == "SourceDocumentDataSource eq 'GAO' and IsIntegrityRelated eq 'Yes'"
410+ )
411+ assert arguments ["page" ] == 2
412+ assert arguments ["page_size" ] == 5
413+ assert arguments ["search_mode" ] == "titles"
414+ assert arguments ["limit" ] == 10
415+ assert arguments ["include_facets" ] is True
416+
417+ assert len (result ) == 1
418+ assert "Complete Test" in result [0 ].text
419+
420+
421+ @pytest .mark .asyncio
422+ async def test_pia_search_facets_empty_filter ():
423+ """Test PIA search facets with empty filter parameter."""
424+ mock_response = {
425+ "jsonrpc" : "2.0" ,
426+ "id" : 1 ,
427+ "result" : {
428+ "facets" : {
429+ "SourceDocumentDataSource" : ["OIG" , "GAO" , "CMS" ],
430+ "RecStatus" : ["Open" , "Closed" ],
431+ }
432+ },
433+ }
434+
435+ with patch .object (Settings , "_get_api_key_from_args" , return_value = "test_key" ):
436+ with patch ("httpx.AsyncClient" ) as mock_client :
437+ mock_response_obj = Mock ()
438+ mock_response_obj .json .return_value = mock_response
439+ mock_response_obj .raise_for_status .return_value = None
440+
441+ mock_client_instance = AsyncMock ()
442+ mock_client_instance .post .return_value = mock_response_obj
443+ mock_client .return_value .__aenter__ .return_value = mock_client_instance
444+
445+ # Test facets with empty filter
446+ result = await handle_pia_search_facets ({"query" : "test" , "filter" : "" })
447+
448+ assert len (result ) == 1
449+ assert "SourceDocumentDataSource" in result [0 ].text
0 commit comments