1+ """Tests for parameter normalization functionality."""
2+
3+ import pytest
4+ import json
5+ from utils .errors import normalize_data_source_ids
6+
7+
8+ class TestNormalizeDataSourceIds :
9+ """Test the normalize_data_source_ids function with various input formats."""
10+
11+ def test_proper_array_input (self ):
12+ """Test that proper arrays are passed through unchanged."""
13+ input_data = ["repo1" , "repo2" , "repo3" ]
14+ result = normalize_data_source_ids (input_data )
15+ assert result == ["repo1" , "repo2" , "repo3" ]
16+
17+ def test_single_string_input (self ):
18+ """Test that single string is converted to array."""
19+ input_data = "repo1"
20+ result = normalize_data_source_ids (input_data )
21+ assert result == ["repo1" ]
22+
23+ def test_json_encoded_string_input (self ):
24+ """Test that JSON-encoded strings are properly parsed."""
25+ input_data = '["repo1", "repo2"]'
26+ result = normalize_data_source_ids (input_data )
27+ assert result == ["repo1" , "repo2" ]
28+
29+ def test_malformed_json_string_fallback (self ):
30+ """Test that malformed JSON strings fall back to single ID."""
31+ input_data = '["repo1", "repo2"' # Missing closing bracket
32+ result = normalize_data_source_ids (input_data )
33+ assert result == ['["repo1", "repo2"' ] # Treated as single ID
34+
35+ def test_empty_inputs (self ):
36+ """Test various empty input types."""
37+ assert normalize_data_source_ids (None ) == []
38+ assert normalize_data_source_ids ("" ) == []
39+ assert normalize_data_source_ids ([]) == []
40+
41+ def test_mixed_array_with_dicts (self ):
42+ """Test arrays containing both strings and dict objects."""
43+ input_data = [
44+ "repo1" ,
45+ {"id" : "repo2" , "type" : "repository" },
46+ "repo3" ,
47+ {"id" : "workspace1" , "type" : "workspace" }
48+ ]
49+ result = normalize_data_source_ids (input_data )
50+ assert result == ["repo1" , "repo2" , "repo3" , "workspace1" ]
51+
52+ def test_dict_without_id (self ):
53+ """Test that dicts without 'id' field are skipped."""
54+ input_data = [
55+ "repo1" ,
56+ {"name" : "some-repo" , "type" : "repository" }, # No 'id' field
57+ "repo2"
58+ ]
59+ result = normalize_data_source_ids (input_data )
60+ assert result == ["repo1" , "repo2" ]
61+
62+ def test_empty_strings_preserved (self ):
63+ """Test that empty strings in arrays are preserved (might be intentional)."""
64+ input_data = ["repo1" , "" , "repo2" , " " , "repo3" ]
65+ result = normalize_data_source_ids (input_data )
66+ assert result == ["repo1" , "" , "repo2" , " " , "repo3" ] # All strings preserved
67+
68+ def test_non_list_non_string_input (self ):
69+ """Test handling of unexpected input types."""
70+ result = normalize_data_source_ids (123 )
71+ assert result == ["123" ]
72+
73+ result = normalize_data_source_ids ({"id" : "repo1" })
74+ assert result == ["{'id': 'repo1'}" ]
75+
76+ def test_claude_desktop_scenarios (self ):
77+ """Test specific scenarios from Claude Desktop serialization issues."""
78+ # Scenario 1: JSON string as seen in Claude Desktop logs
79+ claude_input_1 = '["67db4097fa23c0a98a8495c2"]'
80+ result_1 = normalize_data_source_ids (claude_input_1 )
81+ assert result_1 == ["67db4097fa23c0a98a8495c2" ]
82+
83+ # Scenario 2: Plain string as seen in Claude Desktop logs
84+ claude_input_2 = "67db4097fa23c0a98a8495c2"
85+ result_2 = normalize_data_source_ids (claude_input_2 )
86+ assert result_2 == ["67db4097fa23c0a98a8495c2" ]
87+
88+ # Scenario 3: Multiple IDs in JSON string
89+ claude_input_3 = '["repo1", "repo2", "workspace1"]'
90+ result_3 = normalize_data_source_ids (claude_input_3 )
91+ assert result_3 == ["repo1" , "repo2" , "workspace1" ]
92+
93+ def test_edge_cases (self ):
94+ """Test various edge cases."""
95+ # Whitespace-only JSON string
96+ assert normalize_data_source_ids ("[]" ) == []
97+ assert normalize_data_source_ids ("[ ]" ) == []
98+
99+ # Single item JSON array
100+ assert normalize_data_source_ids ('["single"]' ) == ["single" ]
101+
102+ # JSON array with empty strings
103+ assert normalize_data_source_ids ('["repo1", "", "repo2"]' ) == ["repo1" , "" , "repo2" ]
104+
105+
106+ class TestParameterNormalizationIntegration :
107+ """Integration tests to ensure parameter normalization works in tool contexts."""
108+
109+ def test_search_tool_parameter_handling (self ):
110+ """Test that search tool properly normalizes various parameter formats."""
111+ from tools .search import codebase_search
112+ import inspect
113+
114+ # Verify the function accepts Union[str, List[str]]
115+ sig = inspect .signature (codebase_search )
116+ data_source_ids_param = sig .parameters ['data_source_ids' ]
117+
118+ # The annotation should accept both str and List[str]
119+ assert 'Union' in str (data_source_ids_param .annotation ) or 'str' in str (data_source_ids_param .annotation )
120+
121+ def test_consultant_tool_parameter_handling (self ):
122+ """Test that consultant tool properly normalizes various parameter formats."""
123+ from tools .chat import codebase_consultant
124+ import inspect
125+
126+ # Verify the function accepts Union[str, List[str]]
127+ sig = inspect .signature (codebase_consultant )
128+ data_sources_param = sig .parameters ['data_sources' ]
129+
130+ # The annotation should accept both str and List[str]
131+ assert 'Union' in str (data_sources_param .annotation ) or 'str' in str (data_sources_param .annotation )
132+
133+
134+ if __name__ == "__main__" :
135+ pytest .main ([__file__ , "-v" ])
0 commit comments