@@ -80,6 +80,193 @@ def test_init_with_data_store_specs_without_search_engine_id_raises_error(
8080 data_store_id = "test_data_store" , data_store_specs = [{"id" : "123" }]
8181 )
8282
83+ @pytest .mark .parametrize (
84+ ("tool_kwargs" , "expected_endpoint" ),
85+ [
86+ (
87+ {
88+ "data_store_id" : (
89+ "projects/test/locations/eu/collections/default_collection/"
90+ "dataStores/test_data_store"
91+ )
92+ },
93+ "eu-discoveryengine.googleapis.com" ,
94+ ),
95+ (
96+ {
97+ "search_engine_id" : (
98+ "projects/test/locations/us/collections/default_collection/"
99+ "engines/test_search_engine"
100+ )
101+ },
102+ "us-discoveryengine.googleapis.com" ,
103+ ),
104+ (
105+ {
106+ "data_store_id" : (
107+ "projects/test/locations/europe-west1/collections/"
108+ "default_collection/dataStores/test_data_store"
109+ )
110+ },
111+ "europe-west1-discoveryengine.googleapis.com" ,
112+ ),
113+ ],
114+ )
115+ @mock .patch .object (discovery_engine_search_tool , "client_options" )
116+ @mock .patch .object (discoveryengine , "SearchServiceClient" )
117+ def test_init_with_regional_location_uses_regional_endpoint (
118+ self ,
119+ mock_search_client ,
120+ mock_client_options ,
121+ tool_kwargs ,
122+ expected_endpoint ,
123+ ):
124+ """Test initialization uses the expected regional API endpoint."""
125+ DiscoveryEngineSearchTool (** tool_kwargs )
126+
127+ mock_client_options .ClientOptions .assert_called_once_with (
128+ api_endpoint = expected_endpoint
129+ )
130+ mock_search_client .assert_called_once_with (
131+ credentials = "credentials" ,
132+ client_options = mock_client_options .ClientOptions .return_value ,
133+ )
134+
135+ @mock .patch .object (discovery_engine_search_tool , "client_options" )
136+ @mock .patch .object (discoveryengine , "SearchServiceClient" )
137+ def test_init_with_explicit_location_override_uses_input_location (
138+ self , mock_search_client , mock_client_options
139+ ):
140+ """Test initialization uses explicit location when resource has none."""
141+ DiscoveryEngineSearchTool (
142+ data_store_id = "test_data_store" ,
143+ location = "eu" ,
144+ )
145+
146+ mock_client_options .ClientOptions .assert_called_once_with (
147+ api_endpoint = "eu-discoveryengine.googleapis.com"
148+ )
149+ mock_search_client .assert_called_once_with (
150+ credentials = "credentials" ,
151+ client_options = mock_client_options .ClientOptions .return_value ,
152+ )
153+
154+ @mock .patch .object (discoveryengine , "SearchServiceClient" )
155+ def test_init_with_mismatched_location_raises_error (self , mock_search_client ):
156+ """Test initialization rejects mismatched location overrides."""
157+ with pytest .raises (
158+ ValueError ,
159+ match = (
160+ "location must match the location in data_store_id or "
161+ "search_engine_id."
162+ ),
163+ ):
164+ DiscoveryEngineSearchTool (
165+ data_store_id = (
166+ "projects/test/locations/us/collections/default_collection/"
167+ "dataStores/test_data_store"
168+ ),
169+ location = "eu" ,
170+ )
171+
172+ mock_search_client .assert_not_called ()
173+
174+ @mock .patch .object (discoveryengine , "SearchServiceClient" )
175+ def test_init_with_empty_location_raises_error (self , mock_search_client ):
176+ """Test initialization rejects an empty location override."""
177+ with pytest .raises (
178+ ValueError , match = "location must not be empty if specified."
179+ ):
180+ DiscoveryEngineSearchTool (
181+ data_store_id = (
182+ "projects/test/locations/us/collections/default_collection/"
183+ "dataStores/test_data_store"
184+ ),
185+ location = " " ,
186+ )
187+
188+ mock_search_client .assert_not_called ()
189+
190+ @mock .patch .object (discoveryengine , "SearchServiceClient" )
191+ def test_init_with_invalid_override_location_raises_error (
192+ self , mock_search_client
193+ ):
194+ """Test initialization rejects invalid override location characters."""
195+ with pytest .raises (
196+ ValueError ,
197+ match = "location must contain only letters, digits, and hyphens." ,
198+ ):
199+ DiscoveryEngineSearchTool (
200+ data_store_id = "test_data_store" ,
201+ location = "attacker.com#" ,
202+ )
203+
204+ mock_search_client .assert_not_called ()
205+
206+ @mock .patch .object (discoveryengine , "SearchServiceClient" )
207+ def test_init_with_invalid_resource_location_raises_error (
208+ self , mock_search_client
209+ ):
210+ """Test initialization rejects invalid resource location characters."""
211+ with pytest .raises (
212+ ValueError ,
213+ match = "Invalid location in data_store_id or search_engine_id." ,
214+ ):
215+ DiscoveryEngineSearchTool (
216+ data_store_id = (
217+ "projects/test/locations/attacker.com#/collections/"
218+ "default_collection/dataStores/test_data_store"
219+ )
220+ )
221+
222+ mock_search_client .assert_not_called ()
223+
224+ @mock .patch .object (discovery_engine_search_tool , "client_options" )
225+ @mock .patch .object (discoveryengine , "SearchServiceClient" )
226+ def test_init_with_global_location_keeps_default_endpoint (
227+ self , mock_search_client , mock_client_options
228+ ):
229+ """Test initialization keeps default API endpoint for global location."""
230+ DiscoveryEngineSearchTool (
231+ data_store_id = (
232+ "projects/test/locations/global/collections/default_collection/"
233+ "dataStores/test_data_store"
234+ )
235+ )
236+
237+ mock_client_options .ClientOptions .assert_not_called ()
238+ mock_search_client .assert_called_once_with (
239+ credentials = "credentials" , client_options = None
240+ )
241+
242+ @mock .patch .object (discovery_engine_search_tool , "client_options" )
243+ @mock .patch .object (discoveryengine , "SearchServiceClient" )
244+ def test_init_with_regional_location_and_quota_project_id (
245+ self , mock_search_client , mock_client_options
246+ ):
247+ """Test initialization uses endpoint and quota project id together."""
248+ mock_credentials = mock .MagicMock ()
249+ mock_credentials .quota_project_id = "test-quota-project"
250+
251+ with mock .patch .object (
252+ auth , "default" , return_value = (mock_credentials , "project" )
253+ ):
254+ DiscoveryEngineSearchTool (
255+ data_store_id = (
256+ "projects/test/locations/eu/collections/default_collection/"
257+ "dataStores/test_data_store"
258+ )
259+ )
260+
261+ mock_client_options .ClientOptions .assert_called_once_with (
262+ api_endpoint = "eu-discoveryengine.googleapis.com" ,
263+ quota_project_id = "test-quota-project" ,
264+ )
265+ mock_search_client .assert_called_once_with (
266+ credentials = mock_credentials ,
267+ client_options = mock_client_options .ClientOptions .return_value ,
268+ )
269+
83270 @mock .patch .object (discovery_engine_search_tool , "client_options" )
84271 @mock .patch .object (
85272 discoveryengine ,
0 commit comments