@@ -189,3 +189,197 @@ def test_no_operationid_raises_error():
189189 openapi_document_path = no_op_path ,
190190 execution_settings = None ,
191191 )
192+
193+
194+ def test_parse_blocks_external_http_refs_by_default ():
195+ """Verify that external HTTP $ref resolution is not enabled by default."""
196+ from unittest .mock import MagicMock , patch
197+
198+ from prance .util .resolver import RESOLVE_HTTP , RESOLVE_INTERNAL
199+
200+ with patch ("semantic_kernel.connectors.openapi_plugin.openapi_parser.ResolvingParser" ) as mock_parser_cls :
201+ mock_parser_cls .return_value = MagicMock (specification = {"openapi" : "3.0.0" })
202+ parser = OpenApiParser ()
203+ parser .parse ("dummy_path.yaml" )
204+
205+ mock_parser_cls .assert_called_once ()
206+ call_kwargs = mock_parser_cls .call_args
207+ resolve_types = call_kwargs .kwargs .get ("resolve_types" ) or call_kwargs [1 ].get ("resolve_types" )
208+ assert resolve_types == RESOLVE_INTERNAL , (
209+ f"Expected only RESOLVE_INTERNAL ({ RESOLVE_INTERNAL } ), got { resolve_types } "
210+ )
211+ assert not (resolve_types & RESOLVE_HTTP ), "RESOLVE_HTTP should not be set by default"
212+
213+
214+ def test_parse_resolves_internal_refs_by_default (tmp_path ):
215+ """Verify that internal $ref references are still resolved by default."""
216+ openapi_spec = tmp_path / "spec_with_internal_ref.yaml"
217+ openapi_spec .write_text (
218+ """
219+ openapi: 3.0.0
220+ info:
221+ title: Internal Ref Test
222+ version: 1.0.0
223+ servers:
224+ - url: http://example.com
225+ paths:
226+ /test:
227+ get:
228+ operationId: testOp
229+ responses:
230+ "200":
231+ description: ok
232+ content:
233+ application/json:
234+ schema:
235+ $ref: "#/components/schemas/TestSchema"
236+ components:
237+ schemas:
238+ TestSchema:
239+ type: object
240+ properties:
241+ name:
242+ type: string
243+ """ ,
244+ encoding = "utf-8" ,
245+ )
246+
247+ parser = OpenApiParser ()
248+ result = parser .parse (str (openapi_spec ))
249+
250+ # Internal $ref should be resolved
251+ response_schema = result ["paths" ]["/test" ]["get" ]["responses" ]["200" ]["content" ]["application/json" ]["schema" ]
252+ assert "$ref" not in response_schema , "Internal $ref should be resolved"
253+ assert response_schema ["type" ] == "object"
254+ assert "name" in response_schema ["properties" ]
255+
256+
257+ def test_parse_blocks_file_refs_by_default ():
258+ """Verify that local file $ref resolution is not enabled by default."""
259+ from unittest .mock import MagicMock , patch
260+
261+ from prance .util .resolver import RESOLVE_FILES , RESOLVE_INTERNAL
262+
263+ with patch ("semantic_kernel.connectors.openapi_plugin.openapi_parser.ResolvingParser" ) as mock_parser_cls :
264+ mock_parser_cls .return_value = MagicMock (specification = {"openapi" : "3.0.0" })
265+ parser = OpenApiParser ()
266+ parser .parse ("dummy_path.yaml" )
267+
268+ call_kwargs = mock_parser_cls .call_args
269+ resolve_types = call_kwargs .kwargs .get ("resolve_types" ) or call_kwargs [1 ].get ("resolve_types" )
270+ assert resolve_types == RESOLVE_INTERNAL , (
271+ f"Expected only RESOLVE_INTERNAL ({ RESOLVE_INTERNAL } ), got { resolve_types } "
272+ )
273+ assert not (resolve_types & RESOLVE_FILES ), "RESOLVE_FILES should not be set by default"
274+
275+
276+ def test_parse_enables_file_refs_when_requested ():
277+ """Verify that local file $ref resolution is enabled when explicitly requested."""
278+ from unittest .mock import MagicMock , patch
279+
280+ from prance .util .resolver import RESOLVE_FILES , RESOLVE_INTERNAL
281+
282+ with patch ("semantic_kernel.connectors.openapi_plugin.openapi_parser.ResolvingParser" ) as mock_parser_cls :
283+ mock_parser_cls .return_value = MagicMock (specification = {"openapi" : "3.0.0" })
284+ parser = OpenApiParser ()
285+ parser .parse ("dummy_path.yaml" , enable_file_ref_resolution = True )
286+
287+ call_kwargs = mock_parser_cls .call_args
288+ resolve_types = call_kwargs .kwargs .get ("resolve_types" ) or call_kwargs [1 ].get ("resolve_types" )
289+ assert resolve_types == (RESOLVE_INTERNAL | RESOLVE_FILES ), (
290+ f"Expected RESOLVE_INTERNAL | RESOLVE_FILES, got { resolve_types } "
291+ )
292+
293+
294+ def test_parse_enables_http_refs_when_requested ():
295+ """Verify that HTTP $ref resolution is enabled when explicitly requested."""
296+ from unittest .mock import MagicMock , patch
297+
298+ from prance .util .resolver import RESOLVE_HTTP , RESOLVE_INTERNAL
299+
300+ with patch ("semantic_kernel.connectors.openapi_plugin.openapi_parser.ResolvingParser" ) as mock_parser_cls :
301+ mock_parser_cls .return_value = MagicMock (specification = {"openapi" : "3.0.0" })
302+ parser = OpenApiParser ()
303+ parser .parse ("dummy_path.yaml" , enable_http_ref_resolution = True )
304+
305+ call_kwargs = mock_parser_cls .call_args
306+ resolve_types = call_kwargs .kwargs .get ("resolve_types" ) or call_kwargs [1 ].get ("resolve_types" )
307+ assert resolve_types == (RESOLVE_INTERNAL | RESOLVE_HTTP ), (
308+ f"Expected RESOLVE_INTERNAL | RESOLVE_HTTP, got { resolve_types } "
309+ )
310+
311+
312+ def test_parse_enables_both_file_and_http_refs_when_requested ():
313+ """Verify both file and HTTP $ref resolution work together."""
314+ from unittest .mock import MagicMock , patch
315+
316+ from prance .util .resolver import RESOLVE_FILES , RESOLVE_HTTP , RESOLVE_INTERNAL
317+
318+ with patch ("semantic_kernel.connectors.openapi_plugin.openapi_parser.ResolvingParser" ) as mock_parser_cls :
319+ mock_parser_cls .return_value = MagicMock (specification = {"openapi" : "3.0.0" })
320+ parser = OpenApiParser ()
321+ parser .parse ("dummy_path.yaml" , enable_file_ref_resolution = True , enable_http_ref_resolution = True )
322+
323+ call_kwargs = mock_parser_cls .call_args
324+ resolve_types = call_kwargs .kwargs .get ("resolve_types" ) or call_kwargs [1 ].get ("resolve_types" )
325+ assert resolve_types == (RESOLVE_INTERNAL | RESOLVE_FILES | RESOLVE_HTTP ), (
326+ f"Expected RESOLVE_INTERNAL | RESOLVE_FILES | RESOLVE_HTTP, got { resolve_types } "
327+ )
328+
329+
330+ def test_create_functions_propagates_enable_http_ref_resolution ():
331+ """Verify enable_http_ref_resolution=True is propagated from settings to parser."""
332+ from unittest .mock import patch
333+
334+ from semantic_kernel .connectors .openapi_plugin .openapi_function_execution_parameters import (
335+ OpenAPIFunctionExecutionParameters ,
336+ )
337+
338+ minimal_spec = {
339+ "openapi" : "3.0.0" ,
340+ "info" : {"title" : "Test" , "version" : "1.0.0" },
341+ "paths" : {},
342+ }
343+
344+ settings = OpenAPIFunctionExecutionParameters (enable_http_ref_resolution = True )
345+
346+ with patch .object (OpenApiParser , "parse" , return_value = minimal_spec ) as mock_parse :
347+ create_functions_from_openapi (
348+ plugin_name = "testPlugin" ,
349+ openapi_document_path = "dummy.yaml" ,
350+ execution_settings = settings ,
351+ )
352+ mock_parse .assert_called_once_with (
353+ "dummy.yaml" ,
354+ enable_file_ref_resolution = False ,
355+ enable_http_ref_resolution = True ,
356+ )
357+
358+
359+ def test_create_functions_propagates_enable_file_ref_resolution ():
360+ """Verify enable_file_ref_resolution=True is propagated from settings to parser."""
361+ from unittest .mock import patch
362+
363+ from semantic_kernel .connectors .openapi_plugin .openapi_function_execution_parameters import (
364+ OpenAPIFunctionExecutionParameters ,
365+ )
366+
367+ minimal_spec = {
368+ "openapi" : "3.0.0" ,
369+ "info" : {"title" : "Test" , "version" : "1.0.0" },
370+ "paths" : {},
371+ }
372+
373+ settings = OpenAPIFunctionExecutionParameters (enable_file_ref_resolution = True )
374+
375+ with patch .object (OpenApiParser , "parse" , return_value = minimal_spec ) as mock_parse :
376+ create_functions_from_openapi (
377+ plugin_name = "testPlugin" ,
378+ openapi_document_path = "dummy.yaml" ,
379+ execution_settings = settings ,
380+ )
381+ mock_parse .assert_called_once_with (
382+ "dummy.yaml" ,
383+ enable_file_ref_resolution = True ,
384+ enable_http_ref_resolution = False ,
385+ )
0 commit comments