google-adk 0.0.1__py3-none-any.whl → 0.0.2__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- google/adk/__init__.py +20 -0
- google/adk/agents/__init__.py +32 -0
- google/adk/agents/active_streaming_tool.py +38 -0
- google/adk/agents/base_agent.py +345 -0
- google/adk/agents/callback_context.py +112 -0
- google/adk/agents/invocation_context.py +181 -0
- google/adk/agents/langgraph_agent.py +140 -0
- google/adk/agents/live_request_queue.py +64 -0
- google/adk/agents/llm_agent.py +376 -0
- google/adk/agents/loop_agent.py +62 -0
- google/adk/agents/parallel_agent.py +96 -0
- google/adk/agents/readonly_context.py +46 -0
- google/adk/agents/remote_agent.py +50 -0
- google/adk/agents/run_config.py +87 -0
- google/adk/agents/sequential_agent.py +45 -0
- google/adk/agents/transcription_entry.py +34 -0
- google/adk/artifacts/__init__.py +23 -0
- google/adk/artifacts/base_artifact_service.py +128 -0
- google/adk/artifacts/gcs_artifact_service.py +195 -0
- google/adk/artifacts/in_memory_artifact_service.py +133 -0
- google/adk/auth/__init__.py +22 -0
- google/adk/auth/auth_credential.py +220 -0
- google/adk/auth/auth_handler.py +268 -0
- google/adk/auth/auth_preprocessor.py +116 -0
- google/adk/auth/auth_schemes.py +67 -0
- google/adk/auth/auth_tool.py +55 -0
- google/adk/cli/__init__.py +15 -0
- google/adk/cli/__main__.py +18 -0
- google/adk/cli/agent_graph.py +122 -0
- google/adk/cli/browser/adk_favicon.svg +17 -0
- google/adk/cli/browser/assets/audio-processor.js +51 -0
- google/adk/cli/browser/assets/config/runtime-config.json +3 -0
- google/adk/cli/browser/index.html +33 -0
- google/adk/cli/browser/main-XUU6OGCC.js +75 -0
- google/adk/cli/browser/polyfills-FFHMD2TL.js +18 -0
- google/adk/cli/browser/styles-4VDSPQ37.css +17 -0
- google/adk/cli/cli.py +181 -0
- google/adk/cli/cli_deploy.py +181 -0
- google/adk/cli/cli_eval.py +282 -0
- google/adk/cli/cli_tools_click.py +479 -0
- google/adk/cli/fast_api.py +774 -0
- google/adk/cli/media_streamer/__init__.py +19 -0
- google/adk/cli/media_streamer/index.html +228 -0
- google/adk/cli/utils/__init__.py +49 -0
- google/adk/cli/utils/envs.py +57 -0
- google/adk/cli/utils/evals.py +93 -0
- google/adk/cli/utils/logs.py +72 -0
- google/adk/code_executors/__init__.py +49 -0
- google/adk/code_executors/base_code_executor.py +97 -0
- google/adk/code_executors/code_execution_utils.py +256 -0
- google/adk/code_executors/code_executor_context.py +202 -0
- google/adk/code_executors/container_code_executor.py +196 -0
- google/adk/code_executors/unsafe_local_code_executor.py +71 -0
- google/adk/code_executors/vertex_ai_code_executor.py +234 -0
- google/adk/evaluation/__init__.py +31 -0
- google/adk/evaluation/agent_evaluator.py +329 -0
- google/adk/evaluation/evaluation_constants.py +24 -0
- google/adk/evaluation/evaluation_generator.py +270 -0
- google/adk/evaluation/response_evaluator.py +135 -0
- google/adk/evaluation/trajectory_evaluator.py +184 -0
- google/adk/events/__init__.py +21 -0
- google/adk/events/event.py +130 -0
- google/adk/events/event_actions.py +55 -0
- google/adk/examples/__init__.py +28 -0
- google/adk/examples/base_example_provider.py +35 -0
- google/adk/examples/example.py +27 -0
- google/adk/examples/example_util.py +123 -0
- google/adk/examples/vertex_ai_example_store.py +104 -0
- google/adk/flows/__init__.py +14 -0
- google/adk/flows/llm_flows/__init__.py +20 -0
- google/adk/flows/llm_flows/_base_llm_processor.py +52 -0
- google/adk/flows/llm_flows/_code_execution.py +458 -0
- google/adk/flows/llm_flows/_nl_planning.py +129 -0
- google/adk/flows/llm_flows/agent_transfer.py +132 -0
- google/adk/flows/llm_flows/audio_transcriber.py +109 -0
- google/adk/flows/llm_flows/auto_flow.py +49 -0
- google/adk/flows/llm_flows/base_llm_flow.py +559 -0
- google/adk/flows/llm_flows/basic.py +72 -0
- google/adk/flows/llm_flows/contents.py +370 -0
- google/adk/flows/llm_flows/functions.py +486 -0
- google/adk/flows/llm_flows/identity.py +47 -0
- google/adk/flows/llm_flows/instructions.py +137 -0
- google/adk/flows/llm_flows/single_flow.py +57 -0
- google/adk/memory/__init__.py +35 -0
- google/adk/memory/base_memory_service.py +74 -0
- google/adk/memory/in_memory_memory_service.py +62 -0
- google/adk/memory/vertex_ai_rag_memory_service.py +177 -0
- google/adk/models/__init__.py +31 -0
- google/adk/models/anthropic_llm.py +243 -0
- google/adk/models/base_llm.py +87 -0
- google/adk/models/base_llm_connection.py +76 -0
- google/adk/models/gemini_llm_connection.py +200 -0
- google/adk/models/google_llm.py +331 -0
- google/adk/models/lite_llm.py +673 -0
- google/adk/models/llm_request.py +98 -0
- google/adk/models/llm_response.py +111 -0
- google/adk/models/registry.py +102 -0
- google/adk/planners/__init__.py +23 -0
- google/adk/planners/base_planner.py +66 -0
- google/adk/planners/built_in_planner.py +75 -0
- google/adk/planners/plan_re_act_planner.py +208 -0
- google/adk/runners.py +456 -0
- google/adk/sessions/__init__.py +41 -0
- google/adk/sessions/base_session_service.py +133 -0
- google/adk/sessions/database_session_service.py +522 -0
- google/adk/sessions/in_memory_session_service.py +206 -0
- google/adk/sessions/session.py +54 -0
- google/adk/sessions/state.py +71 -0
- google/adk/sessions/vertex_ai_session_service.py +356 -0
- google/adk/telemetry.py +189 -0
- google/adk/tests/__init__.py +14 -0
- google/adk/tests/integration/.env.example +10 -0
- google/adk/tests/integration/__init__.py +18 -0
- google/adk/tests/integration/conftest.py +119 -0
- google/adk/tests/integration/fixture/__init__.py +14 -0
- google/adk/tests/integration/fixture/agent_with_config/__init__.py +15 -0
- google/adk/tests/integration/fixture/agent_with_config/agent.py +88 -0
- google/adk/tests/integration/fixture/callback_agent/__init__.py +15 -0
- google/adk/tests/integration/fixture/callback_agent/agent.py +105 -0
- google/adk/tests/integration/fixture/context_update_test/OWNERS +1 -0
- google/adk/tests/integration/fixture/context_update_test/__init__.py +15 -0
- google/adk/tests/integration/fixture/context_update_test/agent.py +43 -0
- google/adk/tests/integration/fixture/context_update_test/successful_test.session.json +582 -0
- google/adk/tests/integration/fixture/context_variable_agent/__init__.py +15 -0
- google/adk/tests/integration/fixture/context_variable_agent/agent.py +115 -0
- google/adk/tests/integration/fixture/customer_support_ma/__init__.py +15 -0
- google/adk/tests/integration/fixture/customer_support_ma/agent.py +172 -0
- google/adk/tests/integration/fixture/ecommerce_customer_service_agent/__init__.py +15 -0
- google/adk/tests/integration/fixture/ecommerce_customer_service_agent/agent.py +338 -0
- google/adk/tests/integration/fixture/ecommerce_customer_service_agent/order_query.test.json +69 -0
- google/adk/tests/integration/fixture/ecommerce_customer_service_agent/test_config.json +6 -0
- google/adk/tests/integration/fixture/flow_complex_spark/__init__.py +15 -0
- google/adk/tests/integration/fixture/flow_complex_spark/agent.py +182 -0
- google/adk/tests/integration/fixture/flow_complex_spark/sample.debug.log +243 -0
- google/adk/tests/integration/fixture/flow_complex_spark/sample.session.json +190 -0
- google/adk/tests/integration/fixture/hello_world_agent/__init__.py +15 -0
- google/adk/tests/integration/fixture/hello_world_agent/agent.py +95 -0
- google/adk/tests/integration/fixture/hello_world_agent/roll_die.test.json +24 -0
- google/adk/tests/integration/fixture/hello_world_agent/test_config.json +6 -0
- google/adk/tests/integration/fixture/home_automation_agent/__init__.py +15 -0
- google/adk/tests/integration/fixture/home_automation_agent/agent.py +304 -0
- google/adk/tests/integration/fixture/home_automation_agent/simple_test.test.json +5 -0
- google/adk/tests/integration/fixture/home_automation_agent/simple_test2.test.json +5 -0
- google/adk/tests/integration/fixture/home_automation_agent/test_config.json +5 -0
- google/adk/tests/integration/fixture/home_automation_agent/test_files/dependent_tool_calls.test.json +18 -0
- google/adk/tests/integration/fixture/home_automation_agent/test_files/memorizing_past_events/eval_data.test.json +17 -0
- google/adk/tests/integration/fixture/home_automation_agent/test_files/memorizing_past_events/test_config.json +6 -0
- google/adk/tests/integration/fixture/home_automation_agent/test_files/simple_multi_turn_conversation.test.json +18 -0
- google/adk/tests/integration/fixture/home_automation_agent/test_files/simple_test.test.json +17 -0
- google/adk/tests/integration/fixture/home_automation_agent/test_files/simple_test2.test.json +5 -0
- google/adk/tests/integration/fixture/home_automation_agent/test_files/test_config.json +5 -0
- google/adk/tests/integration/fixture/tool_agent/__init__.py +15 -0
- google/adk/tests/integration/fixture/tool_agent/agent.py +218 -0
- google/adk/tests/integration/fixture/tool_agent/files/Agent_test_plan.pdf +0 -0
- google/adk/tests/integration/fixture/trip_planner_agent/__init__.py +15 -0
- google/adk/tests/integration/fixture/trip_planner_agent/agent.py +110 -0
- google/adk/tests/integration/fixture/trip_planner_agent/initial.session.json +13 -0
- google/adk/tests/integration/fixture/trip_planner_agent/test_config.json +5 -0
- google/adk/tests/integration/fixture/trip_planner_agent/test_files/initial.session.json +13 -0
- google/adk/tests/integration/fixture/trip_planner_agent/test_files/test_config.json +5 -0
- google/adk/tests/integration/fixture/trip_planner_agent/test_files/trip_inquiry_sub_agent.test.json +7 -0
- google/adk/tests/integration/fixture/trip_planner_agent/trip_inquiry.test.json +19 -0
- google/adk/tests/integration/models/__init__.py +14 -0
- google/adk/tests/integration/models/test_google_llm.py +65 -0
- google/adk/tests/integration/test_callback.py +70 -0
- google/adk/tests/integration/test_context_variable.py +67 -0
- google/adk/tests/integration/test_evalute_agent_in_fixture.py +76 -0
- google/adk/tests/integration/test_multi_agent.py +28 -0
- google/adk/tests/integration/test_multi_turn.py +42 -0
- google/adk/tests/integration/test_single_agent.py +23 -0
- google/adk/tests/integration/test_sub_agent.py +26 -0
- google/adk/tests/integration/test_system_instruction.py +177 -0
- google/adk/tests/integration/test_tools.py +287 -0
- google/adk/tests/integration/test_with_test_file.py +34 -0
- google/adk/tests/integration/tools/__init__.py +14 -0
- google/adk/tests/integration/utils/__init__.py +16 -0
- google/adk/tests/integration/utils/asserts.py +75 -0
- google/adk/tests/integration/utils/test_runner.py +97 -0
- google/adk/tests/unittests/__init__.py +14 -0
- google/adk/tests/unittests/agents/__init__.py +14 -0
- google/adk/tests/unittests/agents/test_base_agent.py +407 -0
- google/adk/tests/unittests/agents/test_langgraph_agent.py +191 -0
- google/adk/tests/unittests/agents/test_llm_agent_callbacks.py +138 -0
- google/adk/tests/unittests/agents/test_llm_agent_fields.py +231 -0
- google/adk/tests/unittests/agents/test_loop_agent.py +136 -0
- google/adk/tests/unittests/agents/test_parallel_agent.py +92 -0
- google/adk/tests/unittests/agents/test_sequential_agent.py +114 -0
- google/adk/tests/unittests/artifacts/__init__.py +14 -0
- google/adk/tests/unittests/artifacts/test_artifact_service.py +276 -0
- google/adk/tests/unittests/auth/test_auth_handler.py +575 -0
- google/adk/tests/unittests/conftest.py +73 -0
- google/adk/tests/unittests/fast_api/__init__.py +14 -0
- google/adk/tests/unittests/fast_api/test_fast_api.py +269 -0
- google/adk/tests/unittests/flows/__init__.py +14 -0
- google/adk/tests/unittests/flows/llm_flows/__init__.py +14 -0
- google/adk/tests/unittests/flows/llm_flows/_test_examples.py +142 -0
- google/adk/tests/unittests/flows/llm_flows/test_agent_transfer.py +311 -0
- google/adk/tests/unittests/flows/llm_flows/test_functions_long_running.py +244 -0
- google/adk/tests/unittests/flows/llm_flows/test_functions_request_euc.py +346 -0
- google/adk/tests/unittests/flows/llm_flows/test_functions_sequential.py +93 -0
- google/adk/tests/unittests/flows/llm_flows/test_functions_simple.py +258 -0
- google/adk/tests/unittests/flows/llm_flows/test_identity.py +66 -0
- google/adk/tests/unittests/flows/llm_flows/test_instructions.py +164 -0
- google/adk/tests/unittests/flows/llm_flows/test_model_callbacks.py +142 -0
- google/adk/tests/unittests/flows/llm_flows/test_other_configs.py +46 -0
- google/adk/tests/unittests/flows/llm_flows/test_tool_callbacks.py +269 -0
- google/adk/tests/unittests/models/__init__.py +14 -0
- google/adk/tests/unittests/models/test_google_llm.py +224 -0
- google/adk/tests/unittests/models/test_litellm.py +804 -0
- google/adk/tests/unittests/models/test_models.py +60 -0
- google/adk/tests/unittests/sessions/__init__.py +14 -0
- google/adk/tests/unittests/sessions/test_session_service.py +227 -0
- google/adk/tests/unittests/sessions/test_vertex_ai_session_service.py +246 -0
- google/adk/tests/unittests/streaming/__init__.py +14 -0
- google/adk/tests/unittests/streaming/test_streaming.py +50 -0
- google/adk/tests/unittests/tools/__init__.py +14 -0
- google/adk/tests/unittests/tools/apihub_tool/clients/test_apihub_client.py +499 -0
- google/adk/tests/unittests/tools/apihub_tool/test_apihub_toolset.py +204 -0
- google/adk/tests/unittests/tools/application_integration_tool/clients/test_connections_client.py +600 -0
- google/adk/tests/unittests/tools/application_integration_tool/clients/test_integration_client.py +630 -0
- google/adk/tests/unittests/tools/application_integration_tool/test_application_integration_toolset.py +345 -0
- google/adk/tests/unittests/tools/google_api_tool/__init__.py +13 -0
- google/adk/tests/unittests/tools/google_api_tool/test_googleapi_to_openapi_converter.py +657 -0
- google/adk/tests/unittests/tools/openapi_tool/auth/credential_exchangers/test_auto_auth_credential_exchanger.py +145 -0
- google/adk/tests/unittests/tools/openapi_tool/auth/credential_exchangers/test_base_auth_credential_exchanger.py +68 -0
- google/adk/tests/unittests/tools/openapi_tool/auth/credential_exchangers/test_oauth2_exchanger.py +153 -0
- google/adk/tests/unittests/tools/openapi_tool/auth/credential_exchangers/test_service_account_exchanger.py +196 -0
- google/adk/tests/unittests/tools/openapi_tool/auth/test_auth_helper.py +573 -0
- google/adk/tests/unittests/tools/openapi_tool/common/test_common.py +436 -0
- google/adk/tests/unittests/tools/openapi_tool/openapi_spec_parser/test.yaml +1367 -0
- google/adk/tests/unittests/tools/openapi_tool/openapi_spec_parser/test_openapi_spec_parser.py +628 -0
- google/adk/tests/unittests/tools/openapi_tool/openapi_spec_parser/test_openapi_toolset.py +139 -0
- google/adk/tests/unittests/tools/openapi_tool/openapi_spec_parser/test_operation_parser.py +406 -0
- google/adk/tests/unittests/tools/openapi_tool/openapi_spec_parser/test_rest_api_tool.py +966 -0
- google/adk/tests/unittests/tools/openapi_tool/openapi_spec_parser/test_tool_auth_handler.py +201 -0
- google/adk/tests/unittests/tools/retrieval/__init__.py +14 -0
- google/adk/tests/unittests/tools/retrieval/test_vertex_ai_rag_retrieval.py +147 -0
- google/adk/tests/unittests/tools/test_agent_tool.py +167 -0
- google/adk/tests/unittests/tools/test_base_tool.py +141 -0
- google/adk/tests/unittests/tools/test_build_function_declaration.py +277 -0
- google/adk/tests/unittests/utils.py +304 -0
- google/adk/tools/__init__.py +51 -0
- google/adk/tools/_automatic_function_calling_util.py +346 -0
- google/adk/tools/agent_tool.py +176 -0
- google/adk/tools/apihub_tool/__init__.py +19 -0
- google/adk/tools/apihub_tool/apihub_toolset.py +209 -0
- google/adk/tools/apihub_tool/clients/__init__.py +13 -0
- google/adk/tools/apihub_tool/clients/apihub_client.py +332 -0
- google/adk/tools/apihub_tool/clients/secret_client.py +115 -0
- google/adk/tools/application_integration_tool/__init__.py +19 -0
- google/adk/tools/application_integration_tool/application_integration_toolset.py +230 -0
- google/adk/tools/application_integration_tool/clients/connections_client.py +903 -0
- google/adk/tools/application_integration_tool/clients/integration_client.py +253 -0
- google/adk/tools/base_tool.py +144 -0
- google/adk/tools/built_in_code_execution_tool.py +59 -0
- google/adk/tools/crewai_tool.py +72 -0
- google/adk/tools/example_tool.py +62 -0
- google/adk/tools/exit_loop_tool.py +23 -0
- google/adk/tools/function_parameter_parse_util.py +307 -0
- google/adk/tools/function_tool.py +87 -0
- google/adk/tools/get_user_choice_tool.py +28 -0
- google/adk/tools/google_api_tool/__init__.py +14 -0
- google/adk/tools/google_api_tool/google_api_tool.py +59 -0
- google/adk/tools/google_api_tool/google_api_tool_set.py +107 -0
- google/adk/tools/google_api_tool/google_api_tool_sets.py +55 -0
- google/adk/tools/google_api_tool/googleapi_to_openapi_converter.py +521 -0
- google/adk/tools/google_search_tool.py +68 -0
- google/adk/tools/langchain_tool.py +86 -0
- google/adk/tools/load_artifacts_tool.py +113 -0
- google/adk/tools/load_memory_tool.py +58 -0
- google/adk/tools/load_web_page.py +41 -0
- google/adk/tools/long_running_tool.py +39 -0
- google/adk/tools/mcp_tool/__init__.py +42 -0
- google/adk/tools/mcp_tool/conversion_utils.py +161 -0
- google/adk/tools/mcp_tool/mcp_tool.py +113 -0
- google/adk/tools/mcp_tool/mcp_toolset.py +272 -0
- google/adk/tools/openapi_tool/__init__.py +21 -0
- google/adk/tools/openapi_tool/auth/__init__.py +19 -0
- google/adk/tools/openapi_tool/auth/auth_helpers.py +498 -0
- google/adk/tools/openapi_tool/auth/credential_exchangers/__init__.py +25 -0
- google/adk/tools/openapi_tool/auth/credential_exchangers/auto_auth_credential_exchanger.py +105 -0
- google/adk/tools/openapi_tool/auth/credential_exchangers/base_credential_exchanger.py +55 -0
- google/adk/tools/openapi_tool/auth/credential_exchangers/oauth2_exchanger.py +117 -0
- google/adk/tools/openapi_tool/auth/credential_exchangers/service_account_exchanger.py +97 -0
- google/adk/tools/openapi_tool/common/__init__.py +19 -0
- google/adk/tools/openapi_tool/common/common.py +300 -0
- google/adk/tools/openapi_tool/openapi_spec_parser/__init__.py +32 -0
- google/adk/tools/openapi_tool/openapi_spec_parser/openapi_spec_parser.py +231 -0
- google/adk/tools/openapi_tool/openapi_spec_parser/openapi_toolset.py +144 -0
- google/adk/tools/openapi_tool/openapi_spec_parser/operation_parser.py +260 -0
- google/adk/tools/openapi_tool/openapi_spec_parser/rest_api_tool.py +496 -0
- google/adk/tools/openapi_tool/openapi_spec_parser/tool_auth_handler.py +268 -0
- google/adk/tools/preload_memory_tool.py +72 -0
- google/adk/tools/retrieval/__init__.py +36 -0
- google/adk/tools/retrieval/base_retrieval_tool.py +37 -0
- google/adk/tools/retrieval/files_retrieval.py +33 -0
- google/adk/tools/retrieval/llama_index_retrieval.py +41 -0
- google/adk/tools/retrieval/vertex_ai_rag_retrieval.py +107 -0
- google/adk/tools/tool_context.py +90 -0
- google/adk/tools/toolbox_tool.py +46 -0
- google/adk/tools/transfer_to_agent_tool.py +21 -0
- google/adk/tools/vertex_ai_search_tool.py +96 -0
- google/adk/version.py +16 -0
- google_adk-0.0.1.dist-info/LICENSE.txt → google_adk-0.0.2.dist-info/LICENSE +32 -0
- google_adk-0.0.2.dist-info/METADATA +73 -0
- google_adk-0.0.2.dist-info/RECORD +308 -0
- {google_adk-0.0.1.dist-info → google_adk-0.0.2.dist-info}/WHEEL +1 -2
- google_adk-0.0.2.dist-info/entry_points.txt +3 -0
- agent_kit/__init__.py +0 -0
- google_adk-0.0.1.dist-info/METADATA +0 -15
- google_adk-0.0.1.dist-info/RECORD +0 -6
- google_adk-0.0.1.dist-info/top_level.txt +0 -1
@@ -0,0 +1,628 @@
|
|
1
|
+
# Copyright 2025 Google LLC
|
2
|
+
#
|
3
|
+
# Licensed under the Apache License, Version 2.0 (the "License");
|
4
|
+
# you may not use this file except in compliance with the License.
|
5
|
+
# You may obtain a copy of the License at
|
6
|
+
#
|
7
|
+
# http://www.apache.org/licenses/LICENSE-2.0
|
8
|
+
#
|
9
|
+
# Unless required by applicable law or agreed to in writing, software
|
10
|
+
# distributed under the License is distributed on an "AS IS" BASIS,
|
11
|
+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
12
|
+
# See the License for the specific language governing permissions and
|
13
|
+
# limitations under the License.
|
14
|
+
|
15
|
+
from typing import Any
|
16
|
+
from typing import Dict
|
17
|
+
|
18
|
+
from google.adk.tools.openapi_tool.openapi_spec_parser.openapi_spec_parser import OpenApiSpecParser
|
19
|
+
import pytest
|
20
|
+
|
21
|
+
|
22
|
+
def create_minimal_openapi_spec() -> Dict[str, Any]:
|
23
|
+
"""Creates a minimal valid OpenAPI spec."""
|
24
|
+
return {
|
25
|
+
"openapi": "3.1.0",
|
26
|
+
"info": {"title": "Minimal API", "version": "1.0.0"},
|
27
|
+
"paths": {
|
28
|
+
"/test": {
|
29
|
+
"get": {
|
30
|
+
"summary": "Test GET endpoint",
|
31
|
+
"operationId": "testGet",
|
32
|
+
"responses": {
|
33
|
+
"200": {
|
34
|
+
"description": "Successful response",
|
35
|
+
"content": {
|
36
|
+
"application/json": {"schema": {"type": "string"}}
|
37
|
+
},
|
38
|
+
}
|
39
|
+
},
|
40
|
+
}
|
41
|
+
}
|
42
|
+
},
|
43
|
+
}
|
44
|
+
|
45
|
+
|
46
|
+
@pytest.fixture
|
47
|
+
def openapi_spec_generator():
|
48
|
+
"""Fixture for creating an OperationGenerator instance."""
|
49
|
+
return OpenApiSpecParser()
|
50
|
+
|
51
|
+
|
52
|
+
def test_parse_minimal_spec(openapi_spec_generator):
|
53
|
+
"""Test parsing a minimal OpenAPI specification."""
|
54
|
+
openapi_spec = create_minimal_openapi_spec()
|
55
|
+
|
56
|
+
parsed_operations = openapi_spec_generator.parse(openapi_spec)
|
57
|
+
op = parsed_operations[0]
|
58
|
+
|
59
|
+
assert len(parsed_operations) == 1
|
60
|
+
assert op.name == "test_get"
|
61
|
+
assert op.endpoint.path == "/test"
|
62
|
+
assert op.endpoint.method == "get"
|
63
|
+
assert op.return_value.type_value == str
|
64
|
+
|
65
|
+
|
66
|
+
def test_parse_spec_with_no_operation_id(openapi_spec_generator):
|
67
|
+
"""Test parsing a spec where operationId is missing (auto-generation)."""
|
68
|
+
openapi_spec = create_minimal_openapi_spec()
|
69
|
+
del openapi_spec["paths"]["/test"]["get"]["operationId"] # Remove operationId
|
70
|
+
|
71
|
+
parsed_operations = openapi_spec_generator.parse(openapi_spec)
|
72
|
+
|
73
|
+
assert len(parsed_operations) == 1
|
74
|
+
# Check if operationId is auto generated based on path and method.
|
75
|
+
assert parsed_operations[0].name == "test_get"
|
76
|
+
|
77
|
+
|
78
|
+
def test_parse_spec_with_multiple_methods(openapi_spec_generator):
|
79
|
+
"""Test parsing a spec with multiple HTTP methods for the same path."""
|
80
|
+
openapi_spec = create_minimal_openapi_spec()
|
81
|
+
openapi_spec["paths"]["/test"]["post"] = {
|
82
|
+
"summary": "Test POST endpoint",
|
83
|
+
"operationId": "testPost",
|
84
|
+
"responses": {"200": {"description": "Successful response"}},
|
85
|
+
}
|
86
|
+
|
87
|
+
parsed_operations = openapi_spec_generator.parse(openapi_spec)
|
88
|
+
operation_names = {op.name for op in parsed_operations}
|
89
|
+
|
90
|
+
assert len(parsed_operations) == 2
|
91
|
+
assert "test_get" in operation_names
|
92
|
+
assert "test_post" in operation_names
|
93
|
+
|
94
|
+
|
95
|
+
def test_parse_spec_with_parameters(openapi_spec_generator):
|
96
|
+
openapi_spec = create_minimal_openapi_spec()
|
97
|
+
openapi_spec["paths"]["/test"]["get"]["parameters"] = [
|
98
|
+
{"name": "param1", "in": "query", "schema": {"type": "string"}},
|
99
|
+
{"name": "param2", "in": "header", "schema": {"type": "integer"}},
|
100
|
+
]
|
101
|
+
|
102
|
+
parsed_operations = openapi_spec_generator.parse(openapi_spec)
|
103
|
+
|
104
|
+
assert len(parsed_operations[0].parameters) == 2
|
105
|
+
assert parsed_operations[0].parameters[0].original_name == "param1"
|
106
|
+
assert parsed_operations[0].parameters[0].param_location == "query"
|
107
|
+
assert parsed_operations[0].parameters[1].original_name == "param2"
|
108
|
+
assert parsed_operations[0].parameters[1].param_location == "header"
|
109
|
+
|
110
|
+
|
111
|
+
def test_parse_spec_with_request_body(openapi_spec_generator):
|
112
|
+
openapi_spec = create_minimal_openapi_spec()
|
113
|
+
openapi_spec["paths"]["/test"]["post"] = {
|
114
|
+
"summary": "Endpoint with request body",
|
115
|
+
"operationId": "testPostWithBody",
|
116
|
+
"requestBody": {
|
117
|
+
"content": {
|
118
|
+
"application/json": {
|
119
|
+
"schema": {
|
120
|
+
"type": "object",
|
121
|
+
"properties": {"name": {"type": "string"}},
|
122
|
+
}
|
123
|
+
}
|
124
|
+
}
|
125
|
+
},
|
126
|
+
"responses": {"200": {"description": "OK"}},
|
127
|
+
}
|
128
|
+
|
129
|
+
parsed_operations = openapi_spec_generator.parse(openapi_spec)
|
130
|
+
post_operations = [
|
131
|
+
op for op in parsed_operations if op.endpoint.method == "post"
|
132
|
+
]
|
133
|
+
op = post_operations[0]
|
134
|
+
|
135
|
+
assert len(post_operations) == 1
|
136
|
+
assert op.name == "test_post_with_body"
|
137
|
+
assert len(op.parameters) == 1
|
138
|
+
assert op.parameters[0].original_name == "name"
|
139
|
+
assert op.parameters[0].type_value == str
|
140
|
+
|
141
|
+
|
142
|
+
def test_parse_spec_with_reference(openapi_spec_generator):
|
143
|
+
"""Test parsing a specification with $ref."""
|
144
|
+
openapi_spec = {
|
145
|
+
"openapi": "3.1.0",
|
146
|
+
"info": {"title": "API with Refs", "version": "1.0.0"},
|
147
|
+
"paths": {
|
148
|
+
"/test_ref": {
|
149
|
+
"get": {
|
150
|
+
"summary": "Endpoint with ref",
|
151
|
+
"operationId": "testGetRef",
|
152
|
+
"responses": {
|
153
|
+
"200": {
|
154
|
+
"description": "Success",
|
155
|
+
"content": {
|
156
|
+
"application/json": {
|
157
|
+
"schema": {
|
158
|
+
"$ref": "#/components/schemas/MySchema"
|
159
|
+
}
|
160
|
+
}
|
161
|
+
},
|
162
|
+
}
|
163
|
+
},
|
164
|
+
}
|
165
|
+
}
|
166
|
+
},
|
167
|
+
"components": {
|
168
|
+
"schemas": {
|
169
|
+
"MySchema": {
|
170
|
+
"type": "object",
|
171
|
+
"properties": {"name": {"type": "string"}},
|
172
|
+
}
|
173
|
+
}
|
174
|
+
},
|
175
|
+
}
|
176
|
+
parsed_operations = openapi_spec_generator.parse(openapi_spec)
|
177
|
+
op = parsed_operations[0]
|
178
|
+
|
179
|
+
assert len(parsed_operations) == 1
|
180
|
+
assert op.return_value.type_value.__origin__ is dict
|
181
|
+
|
182
|
+
|
183
|
+
def test_parse_spec_with_circular_reference(openapi_spec_generator):
|
184
|
+
"""Test correct handling of circular $ref (important!)."""
|
185
|
+
openapi_spec = {
|
186
|
+
"openapi": "3.1.0",
|
187
|
+
"info": {"title": "Circular Ref API", "version": "1.0.0"},
|
188
|
+
"paths": {
|
189
|
+
"/circular": {
|
190
|
+
"get": {
|
191
|
+
"responses": {
|
192
|
+
"200": {
|
193
|
+
"description": "OK",
|
194
|
+
"content": {
|
195
|
+
"application/json": {
|
196
|
+
"schema": {"$ref": "#/components/schemas/A"}
|
197
|
+
}
|
198
|
+
},
|
199
|
+
}
|
200
|
+
}
|
201
|
+
}
|
202
|
+
}
|
203
|
+
},
|
204
|
+
"components": {
|
205
|
+
"schemas": {
|
206
|
+
"A": {
|
207
|
+
"type": "object",
|
208
|
+
"properties": {"b": {"$ref": "#/components/schemas/B"}},
|
209
|
+
},
|
210
|
+
"B": {
|
211
|
+
"type": "object",
|
212
|
+
"properties": {"a": {"$ref": "#/components/schemas/A"}},
|
213
|
+
},
|
214
|
+
}
|
215
|
+
},
|
216
|
+
}
|
217
|
+
|
218
|
+
parsed_operations = openapi_spec_generator.parse(openapi_spec)
|
219
|
+
assert len(parsed_operations) == 1
|
220
|
+
|
221
|
+
op = parsed_operations[0]
|
222
|
+
assert op.return_value.type_value.__origin__ is dict
|
223
|
+
assert op.return_value.type_hint == "Dict[str, Any]"
|
224
|
+
|
225
|
+
|
226
|
+
def test_parse_no_paths(openapi_spec_generator):
|
227
|
+
"""Test with a spec that has no paths defined."""
|
228
|
+
openapi_spec = {
|
229
|
+
"openapi": "3.1.0",
|
230
|
+
"info": {"title": "No Paths API", "version": "1.0.0"},
|
231
|
+
}
|
232
|
+
parsed_operations = openapi_spec_generator.parse(openapi_spec)
|
233
|
+
assert len(parsed_operations) == 0 # Should be empty
|
234
|
+
|
235
|
+
|
236
|
+
def test_parse_empty_path_item(openapi_spec_generator):
|
237
|
+
"""Test a path item that is present but empty."""
|
238
|
+
openapi_spec = {
|
239
|
+
"openapi": "3.1.0",
|
240
|
+
"info": {"title": "Empty Path Item API", "version": "1.0.0"},
|
241
|
+
"paths": {"/empty": None},
|
242
|
+
}
|
243
|
+
|
244
|
+
parsed_operations = openapi_spec_generator.parse(openapi_spec)
|
245
|
+
|
246
|
+
assert len(parsed_operations) == 0
|
247
|
+
|
248
|
+
|
249
|
+
def test_parse_spec_with_global_auth_scheme(openapi_spec_generator):
|
250
|
+
"""Test parsing with a global security scheme."""
|
251
|
+
openapi_spec = create_minimal_openapi_spec()
|
252
|
+
openapi_spec["security"] = [{"api_key": []}]
|
253
|
+
openapi_spec["components"] = {
|
254
|
+
"securitySchemes": {
|
255
|
+
"api_key": {"type": "apiKey", "in": "header", "name": "X-API-Key"}
|
256
|
+
}
|
257
|
+
}
|
258
|
+
|
259
|
+
parsed_operations = openapi_spec_generator.parse(openapi_spec)
|
260
|
+
op = parsed_operations[0]
|
261
|
+
|
262
|
+
assert len(parsed_operations) == 1
|
263
|
+
assert op.auth_scheme is not None
|
264
|
+
assert op.auth_scheme.type_.value == "apiKey"
|
265
|
+
|
266
|
+
|
267
|
+
def test_parse_spec_with_local_auth_scheme(openapi_spec_generator):
|
268
|
+
"""Test parsing with a local (operation-level) security scheme."""
|
269
|
+
openapi_spec = create_minimal_openapi_spec()
|
270
|
+
openapi_spec["paths"]["/test"]["get"]["security"] = [{"local_auth": []}]
|
271
|
+
openapi_spec["components"] = {
|
272
|
+
"securitySchemes": {"local_auth": {"type": "http", "scheme": "bearer"}}
|
273
|
+
}
|
274
|
+
|
275
|
+
parsed_operations = openapi_spec_generator.parse(openapi_spec)
|
276
|
+
op = parsed_operations[0]
|
277
|
+
|
278
|
+
assert op.auth_scheme is not None
|
279
|
+
assert op.auth_scheme.type_.value == "http"
|
280
|
+
assert op.auth_scheme.scheme == "bearer"
|
281
|
+
|
282
|
+
|
283
|
+
def test_parse_spec_with_servers(openapi_spec_generator):
|
284
|
+
"""Test parsing with server URLs."""
|
285
|
+
openapi_spec = create_minimal_openapi_spec()
|
286
|
+
openapi_spec["servers"] = [
|
287
|
+
{"url": "https://api.example.com"},
|
288
|
+
{"url": "http://localhost:8000"},
|
289
|
+
]
|
290
|
+
|
291
|
+
parsed_operations = openapi_spec_generator.parse(openapi_spec)
|
292
|
+
|
293
|
+
assert len(parsed_operations) == 1
|
294
|
+
assert parsed_operations[0].endpoint.base_url == "https://api.example.com"
|
295
|
+
|
296
|
+
|
297
|
+
def test_parse_spec_with_no_servers(openapi_spec_generator):
|
298
|
+
"""Test with no servers defined (should default to empty string)."""
|
299
|
+
openapi_spec = create_minimal_openapi_spec()
|
300
|
+
if "servers" in openapi_spec:
|
301
|
+
del openapi_spec["servers"]
|
302
|
+
|
303
|
+
parsed_operations = openapi_spec_generator.parse(openapi_spec)
|
304
|
+
|
305
|
+
assert len(parsed_operations) == 1
|
306
|
+
assert parsed_operations[0].endpoint.base_url == ""
|
307
|
+
|
308
|
+
|
309
|
+
def test_parse_spec_with_description(openapi_spec_generator):
|
310
|
+
openapi_spec = create_minimal_openapi_spec()
|
311
|
+
expected_description = "This is a test description."
|
312
|
+
openapi_spec["paths"]["/test"]["get"]["description"] = expected_description
|
313
|
+
|
314
|
+
parsed_operations = openapi_spec_generator.parse(openapi_spec)
|
315
|
+
|
316
|
+
assert len(parsed_operations) == 1
|
317
|
+
assert parsed_operations[0].description == expected_description
|
318
|
+
|
319
|
+
|
320
|
+
def test_parse_spec_with_empty_description(openapi_spec_generator):
|
321
|
+
openapi_spec = create_minimal_openapi_spec()
|
322
|
+
openapi_spec["paths"]["/test"]["get"]["description"] = ""
|
323
|
+
openapi_spec["paths"]["/test"]["get"]["summary"] = ""
|
324
|
+
|
325
|
+
parsed_operations = openapi_spec_generator.parse(openapi_spec)
|
326
|
+
|
327
|
+
assert len(parsed_operations) == 1
|
328
|
+
assert parsed_operations[0].description == ""
|
329
|
+
|
330
|
+
|
331
|
+
def test_parse_spec_with_no_description(openapi_spec_generator):
|
332
|
+
openapi_spec = create_minimal_openapi_spec()
|
333
|
+
|
334
|
+
# delete description
|
335
|
+
if "description" in openapi_spec["paths"]["/test"]["get"]:
|
336
|
+
del openapi_spec["paths"]["/test"]["get"]["description"]
|
337
|
+
if "summary" in openapi_spec["paths"]["/test"]["get"]:
|
338
|
+
del openapi_spec["paths"]["/test"]["get"]["summary"]
|
339
|
+
|
340
|
+
parsed_operations = openapi_spec_generator.parse(openapi_spec)
|
341
|
+
|
342
|
+
assert len(parsed_operations) == 1
|
343
|
+
assert (
|
344
|
+
parsed_operations[0].description == ""
|
345
|
+
) # it should be initialized with empty string
|
346
|
+
|
347
|
+
|
348
|
+
def test_parse_invalid_openapi_spec_type(openapi_spec_generator):
|
349
|
+
"""Test that passing a non-dict object to parse raises TypeError"""
|
350
|
+
with pytest.raises(AttributeError):
|
351
|
+
openapi_spec_generator.parse(123) # type: ignore
|
352
|
+
|
353
|
+
with pytest.raises(AttributeError):
|
354
|
+
openapi_spec_generator.parse("openapi_spec") # type: ignore
|
355
|
+
|
356
|
+
with pytest.raises(AttributeError):
|
357
|
+
openapi_spec_generator.parse([]) # type: ignore
|
358
|
+
|
359
|
+
|
360
|
+
def test_parse_external_ref_raises_error(openapi_spec_generator):
|
361
|
+
"""Check that external references (not starting with #) raise ValueError."""
|
362
|
+
openapi_spec = {
|
363
|
+
"openapi": "3.1.0",
|
364
|
+
"info": {"title": "External Ref API", "version": "1.0.0"},
|
365
|
+
"paths": {
|
366
|
+
"/external": {
|
367
|
+
"get": {
|
368
|
+
"responses": {
|
369
|
+
"200": {
|
370
|
+
"description": "OK",
|
371
|
+
"content": {
|
372
|
+
"application/json": {
|
373
|
+
"schema": {
|
374
|
+
"$ref": (
|
375
|
+
"external_file.json#/components/schemas/ExternalSchema"
|
376
|
+
)
|
377
|
+
}
|
378
|
+
}
|
379
|
+
},
|
380
|
+
}
|
381
|
+
}
|
382
|
+
}
|
383
|
+
}
|
384
|
+
},
|
385
|
+
}
|
386
|
+
with pytest.raises(ValueError):
|
387
|
+
openapi_spec_generator.parse(openapi_spec)
|
388
|
+
|
389
|
+
|
390
|
+
def test_parse_spec_with_multiple_paths_deep_refs(openapi_spec_generator):
|
391
|
+
"""Test specs with multiple paths, request/response bodies using deep refs."""
|
392
|
+
openapi_spec = {
|
393
|
+
"openapi": "3.1.0",
|
394
|
+
"info": {"title": "Multiple Paths Deep Refs API", "version": "1.0.0"},
|
395
|
+
"paths": {
|
396
|
+
"/path1": {
|
397
|
+
"post": {
|
398
|
+
"operationId": "postPath1",
|
399
|
+
"requestBody": {
|
400
|
+
"content": {
|
401
|
+
"application/json": {
|
402
|
+
"schema": {
|
403
|
+
"$ref": "#/components/schemas/Request1"
|
404
|
+
}
|
405
|
+
}
|
406
|
+
}
|
407
|
+
},
|
408
|
+
"responses": {
|
409
|
+
"200": {
|
410
|
+
"description": "OK",
|
411
|
+
"content": {
|
412
|
+
"application/json": {
|
413
|
+
"schema": {
|
414
|
+
"$ref": "#/components/schemas/Response1"
|
415
|
+
}
|
416
|
+
}
|
417
|
+
},
|
418
|
+
}
|
419
|
+
},
|
420
|
+
}
|
421
|
+
},
|
422
|
+
"/path2": {
|
423
|
+
"put": {
|
424
|
+
"operationId": "putPath2",
|
425
|
+
"requestBody": {
|
426
|
+
"content": {
|
427
|
+
"application/json": {
|
428
|
+
"schema": {
|
429
|
+
"$ref": "#/components/schemas/Request2"
|
430
|
+
}
|
431
|
+
}
|
432
|
+
}
|
433
|
+
},
|
434
|
+
"responses": {
|
435
|
+
"200": {
|
436
|
+
"description": "OK",
|
437
|
+
"content": {
|
438
|
+
"application/json": {
|
439
|
+
"schema": {
|
440
|
+
"$ref": "#/components/schemas/Response2"
|
441
|
+
}
|
442
|
+
}
|
443
|
+
},
|
444
|
+
}
|
445
|
+
},
|
446
|
+
},
|
447
|
+
"get": {
|
448
|
+
"operationId": "getPath2",
|
449
|
+
"responses": {
|
450
|
+
"200": {
|
451
|
+
"description": "OK",
|
452
|
+
"content": {
|
453
|
+
"application/json": {
|
454
|
+
"schema": {
|
455
|
+
"$ref": "#/components/schemas/Response2"
|
456
|
+
}
|
457
|
+
}
|
458
|
+
},
|
459
|
+
}
|
460
|
+
},
|
461
|
+
},
|
462
|
+
},
|
463
|
+
},
|
464
|
+
"components": {
|
465
|
+
"schemas": {
|
466
|
+
"Request1": {
|
467
|
+
"type": "object",
|
468
|
+
"properties": {
|
469
|
+
"req1_prop1": {"$ref": "#/components/schemas/Level1_1"}
|
470
|
+
},
|
471
|
+
},
|
472
|
+
"Response1": {
|
473
|
+
"type": "object",
|
474
|
+
"properties": {
|
475
|
+
"res1_prop1": {"$ref": "#/components/schemas/Level1_2"}
|
476
|
+
},
|
477
|
+
},
|
478
|
+
"Request2": {
|
479
|
+
"type": "object",
|
480
|
+
"properties": {
|
481
|
+
"req2_prop1": {"$ref": "#/components/schemas/Level1_1"}
|
482
|
+
},
|
483
|
+
},
|
484
|
+
"Response2": {
|
485
|
+
"type": "object",
|
486
|
+
"properties": {
|
487
|
+
"res2_prop1": {"$ref": "#/components/schemas/Level1_2"}
|
488
|
+
},
|
489
|
+
},
|
490
|
+
"Level1_1": {
|
491
|
+
"type": "object",
|
492
|
+
"properties": {
|
493
|
+
"level1_1_prop1": {
|
494
|
+
"$ref": "#/components/schemas/Level2_1"
|
495
|
+
}
|
496
|
+
},
|
497
|
+
},
|
498
|
+
"Level1_2": {
|
499
|
+
"type": "object",
|
500
|
+
"properties": {
|
501
|
+
"level1_2_prop1": {
|
502
|
+
"$ref": "#/components/schemas/Level2_2"
|
503
|
+
}
|
504
|
+
},
|
505
|
+
},
|
506
|
+
"Level2_1": {
|
507
|
+
"type": "object",
|
508
|
+
"properties": {
|
509
|
+
"level2_1_prop1": {"$ref": "#/components/schemas/Level3"}
|
510
|
+
},
|
511
|
+
},
|
512
|
+
"Level2_2": {
|
513
|
+
"type": "object",
|
514
|
+
"properties": {"level2_2_prop1": {"type": "string"}},
|
515
|
+
},
|
516
|
+
"Level3": {"type": "integer"},
|
517
|
+
}
|
518
|
+
},
|
519
|
+
}
|
520
|
+
|
521
|
+
parsed_operations = openapi_spec_generator.parse(openapi_spec)
|
522
|
+
assert len(parsed_operations) == 3
|
523
|
+
|
524
|
+
# Verify Path 1
|
525
|
+
path1_ops = [op for op in parsed_operations if op.endpoint.path == "/path1"]
|
526
|
+
assert len(path1_ops) == 1
|
527
|
+
path1_op = path1_ops[0]
|
528
|
+
assert path1_op.name == "post_path1"
|
529
|
+
|
530
|
+
assert len(path1_op.parameters) == 1
|
531
|
+
assert path1_op.parameters[0].original_name == "req1_prop1"
|
532
|
+
assert (
|
533
|
+
path1_op.parameters[0]
|
534
|
+
.param_schema.properties["level1_1_prop1"]
|
535
|
+
.properties["level2_1_prop1"]
|
536
|
+
.type
|
537
|
+
== "integer"
|
538
|
+
)
|
539
|
+
assert (
|
540
|
+
path1_op.return_value.param_schema.properties["res1_prop1"]
|
541
|
+
.properties["level1_2_prop1"]
|
542
|
+
.properties["level2_2_prop1"]
|
543
|
+
.type
|
544
|
+
== "string"
|
545
|
+
)
|
546
|
+
|
547
|
+
# Verify Path 2
|
548
|
+
path2_ops = [
|
549
|
+
op
|
550
|
+
for op in parsed_operations
|
551
|
+
if op.endpoint.path == "/path2" and op.name == "put_path2"
|
552
|
+
]
|
553
|
+
path2_op = path2_ops[0]
|
554
|
+
assert path2_op is not None
|
555
|
+
assert len(path2_op.parameters) == 1
|
556
|
+
assert path2_op.parameters[0].original_name == "req2_prop1"
|
557
|
+
assert (
|
558
|
+
path2_op.parameters[0]
|
559
|
+
.param_schema.properties["level1_1_prop1"]
|
560
|
+
.properties["level2_1_prop1"]
|
561
|
+
.type
|
562
|
+
== "integer"
|
563
|
+
)
|
564
|
+
assert (
|
565
|
+
path2_op.return_value.param_schema.properties["res2_prop1"]
|
566
|
+
.properties["level1_2_prop1"]
|
567
|
+
.properties["level2_2_prop1"]
|
568
|
+
.type
|
569
|
+
== "string"
|
570
|
+
)
|
571
|
+
|
572
|
+
|
573
|
+
def test_parse_spec_with_duplicate_parameter_names(openapi_spec_generator):
|
574
|
+
"""Test handling of duplicate parameter names (one in query, one in body).
|
575
|
+
|
576
|
+
The expected behavior is that both parameters should be captured but with
|
577
|
+
different suffix, and
|
578
|
+
their `original_name` attributes should reflect their origin (query or body).
|
579
|
+
"""
|
580
|
+
openapi_spec = {
|
581
|
+
"openapi": "3.1.0",
|
582
|
+
"info": {"title": "Duplicate Parameter Names API", "version": "1.0.0"},
|
583
|
+
"paths": {
|
584
|
+
"/duplicate": {
|
585
|
+
"post": {
|
586
|
+
"operationId": "createWithDuplicate",
|
587
|
+
"parameters": [{
|
588
|
+
"name": "name",
|
589
|
+
"in": "query",
|
590
|
+
"schema": {"type": "string"},
|
591
|
+
}],
|
592
|
+
"requestBody": {
|
593
|
+
"content": {
|
594
|
+
"application/json": {
|
595
|
+
"schema": {
|
596
|
+
"type": "object",
|
597
|
+
"properties": {"name": {"type": "integer"}},
|
598
|
+
}
|
599
|
+
}
|
600
|
+
}
|
601
|
+
},
|
602
|
+
"responses": {"200": {"description": "OK"}},
|
603
|
+
}
|
604
|
+
}
|
605
|
+
},
|
606
|
+
}
|
607
|
+
|
608
|
+
parsed_operations = openapi_spec_generator.parse(openapi_spec)
|
609
|
+
assert len(parsed_operations) == 1
|
610
|
+
op = parsed_operations[0]
|
611
|
+
assert op.name == "create_with_duplicate"
|
612
|
+
assert len(op.parameters) == 2
|
613
|
+
|
614
|
+
query_param = None
|
615
|
+
body_param = None
|
616
|
+
for param in op.parameters:
|
617
|
+
if param.param_location == "query" and param.original_name == "name":
|
618
|
+
query_param = param
|
619
|
+
elif param.param_location == "body" and param.original_name == "name":
|
620
|
+
body_param = param
|
621
|
+
|
622
|
+
assert query_param is not None
|
623
|
+
assert query_param.original_name == "name"
|
624
|
+
assert query_param.py_name == "name"
|
625
|
+
|
626
|
+
assert body_param is not None
|
627
|
+
assert body_param.original_name == "name"
|
628
|
+
assert body_param.py_name == "name_0"
|