google-adk 0.0.3__py3-none-any.whl → 0.0.5__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.
Files changed (185) hide show
  1. google/adk/agents/run_config.py +4 -0
  2. google/adk/auth/auth_preprocessor.py +19 -16
  3. google/adk/cli/browser/index.html +1 -1
  4. google/adk/cli/browser/{main-SY2WYYGV.js → main-SLIAU2JL.js} +46 -30
  5. google/adk/cli/cli.py +8 -8
  6. google/adk/cli/cli_deploy.py +2 -4
  7. google/adk/cli/cli_tools_click.py +6 -6
  8. google/adk/flows/llm_flows/base_llm_flow.py +1 -1
  9. google/adk/flows/llm_flows/contents.py +21 -1
  10. google/adk/flows/llm_flows/functions.py +3 -1
  11. google/adk/models/google_llm.py +0 -1
  12. google/adk/runners.py +13 -2
  13. google/adk/version.py +1 -1
  14. {google_adk-0.0.3.dist-info → google_adk-0.0.5.dist-info}/METADATA +4 -2
  15. google_adk-0.0.5.dist-info/RECORD +175 -0
  16. {google_adk-0.0.3.dist-info → google_adk-0.0.5.dist-info}/WHEEL +1 -1
  17. google_adk-0.0.5.dist-info/licenses/LICENSE +202 -0
  18. google/adk/._version.py +0 -0
  19. google/adk/docs/Makefile +0 -20
  20. google/adk/docs/build/doctrees/google-adk.doctree +0 -0
  21. google/adk/docs/build/html/_sources/google-adk.rst.txt +0 -98
  22. google/adk/docs/build/html/_sources/index.rst.txt +0 -7
  23. google/adk/docs/build/html/_static/autodoc_pydantic.css +0 -27
  24. google/adk/docs/build/html/_static/basic.css +0 -925
  25. google/adk/docs/build/html/_static/debug.css +0 -85
  26. google/adk/docs/build/html/_static/doctools.js +0 -156
  27. google/adk/docs/build/html/_static/documentation_options.js +0 -29
  28. google/adk/docs/build/html/_static/file.png +0 -0
  29. google/adk/docs/build/html/_static/language_data.js +0 -199
  30. google/adk/docs/build/html/_static/minus.png +0 -0
  31. google/adk/docs/build/html/_static/plus.png +0 -0
  32. google/adk/docs/build/html/_static/pygments.css +0 -274
  33. google/adk/docs/build/html/_static/scripts/furo-extensions.js +0 -16
  34. google/adk/docs/build/html/_static/scripts/furo.js +0 -19
  35. google/adk/docs/build/html/_static/scripts/furo.js.LICENSE.txt +0 -7
  36. google/adk/docs/build/html/_static/scripts/furo.js.map +0 -1
  37. google/adk/docs/build/html/_static/searchtools.js +0 -620
  38. google/adk/docs/build/html/_static/skeleton.css +0 -312
  39. google/adk/docs/build/html/_static/sphinx_highlight.js +0 -170
  40. google/adk/docs/build/html/_static/styles/furo-extensions.css +0 -18
  41. google/adk/docs/build/html/_static/styles/furo-extensions.css.map +0 -1
  42. google/adk/docs/build/html/_static/styles/furo.css +0 -18
  43. google/adk/docs/build/html/_static/styles/furo.css.map +0 -1
  44. google/adk/docs/build/html/genindex.html +0 -861
  45. google/adk/docs/build/html/google-adk.html +0 -5461
  46. google/adk/docs/build/html/index.html +0 -567
  47. google/adk/docs/build/html/objects.inv +0 -0
  48. google/adk/docs/build/html/py-modindex.html +0 -373
  49. google/adk/docs/build/html/search.html +0 -333
  50. google/adk/docs/build/html/searchindex.js +0 -17
  51. google/adk/docs/source/conf.py +0 -133
  52. google/adk/docs/source/google-adk.rst +0 -98
  53. google/adk/docs/source/index.rst +0 -7
  54. google/adk/tests/__init__.py +0 -14
  55. google/adk/tests/integration/.env.example +0 -10
  56. google/adk/tests/integration/__init__.py +0 -18
  57. google/adk/tests/integration/conftest.py +0 -119
  58. google/adk/tests/integration/fixture/__init__.py +0 -14
  59. google/adk/tests/integration/fixture/agent_with_config/__init__.py +0 -15
  60. google/adk/tests/integration/fixture/agent_with_config/agent.py +0 -88
  61. google/adk/tests/integration/fixture/callback_agent/__init__.py +0 -15
  62. google/adk/tests/integration/fixture/callback_agent/agent.py +0 -105
  63. google/adk/tests/integration/fixture/context_update_test/OWNERS +0 -1
  64. google/adk/tests/integration/fixture/context_update_test/__init__.py +0 -15
  65. google/adk/tests/integration/fixture/context_update_test/agent.py +0 -43
  66. google/adk/tests/integration/fixture/context_update_test/successful_test.session.json +0 -582
  67. google/adk/tests/integration/fixture/context_variable_agent/__init__.py +0 -15
  68. google/adk/tests/integration/fixture/context_variable_agent/agent.py +0 -115
  69. google/adk/tests/integration/fixture/customer_support_ma/__init__.py +0 -15
  70. google/adk/tests/integration/fixture/customer_support_ma/agent.py +0 -172
  71. google/adk/tests/integration/fixture/ecommerce_customer_service_agent/__init__.py +0 -15
  72. google/adk/tests/integration/fixture/ecommerce_customer_service_agent/agent.py +0 -338
  73. google/adk/tests/integration/fixture/ecommerce_customer_service_agent/order_query.test.json +0 -69
  74. google/adk/tests/integration/fixture/ecommerce_customer_service_agent/test_config.json +0 -6
  75. google/adk/tests/integration/fixture/flow_complex_spark/__init__.py +0 -15
  76. google/adk/tests/integration/fixture/flow_complex_spark/agent.py +0 -182
  77. google/adk/tests/integration/fixture/flow_complex_spark/sample.session.json +0 -190
  78. google/adk/tests/integration/fixture/hello_world_agent/__init__.py +0 -15
  79. google/adk/tests/integration/fixture/hello_world_agent/agent.py +0 -95
  80. google/adk/tests/integration/fixture/hello_world_agent/roll_die.test.json +0 -24
  81. google/adk/tests/integration/fixture/hello_world_agent/test_config.json +0 -6
  82. google/adk/tests/integration/fixture/home_automation_agent/__init__.py +0 -15
  83. google/adk/tests/integration/fixture/home_automation_agent/agent.py +0 -304
  84. google/adk/tests/integration/fixture/home_automation_agent/simple_test.test.json +0 -5
  85. google/adk/tests/integration/fixture/home_automation_agent/simple_test2.test.json +0 -5
  86. google/adk/tests/integration/fixture/home_automation_agent/test_config.json +0 -5
  87. google/adk/tests/integration/fixture/home_automation_agent/test_files/dependent_tool_calls.test.json +0 -18
  88. google/adk/tests/integration/fixture/home_automation_agent/test_files/memorizing_past_events/eval_data.test.json +0 -17
  89. google/adk/tests/integration/fixture/home_automation_agent/test_files/memorizing_past_events/test_config.json +0 -6
  90. google/adk/tests/integration/fixture/home_automation_agent/test_files/simple_multi_turn_conversation.test.json +0 -18
  91. google/adk/tests/integration/fixture/home_automation_agent/test_files/simple_test.test.json +0 -17
  92. google/adk/tests/integration/fixture/home_automation_agent/test_files/simple_test2.test.json +0 -5
  93. google/adk/tests/integration/fixture/home_automation_agent/test_files/test_config.json +0 -5
  94. google/adk/tests/integration/fixture/tool_agent/__init__.py +0 -15
  95. google/adk/tests/integration/fixture/tool_agent/agent.py +0 -218
  96. google/adk/tests/integration/fixture/tool_agent/files/Agent_test_plan.pdf +0 -0
  97. google/adk/tests/integration/fixture/trip_planner_agent/__init__.py +0 -15
  98. google/adk/tests/integration/fixture/trip_planner_agent/agent.py +0 -110
  99. google/adk/tests/integration/fixture/trip_planner_agent/initial.session.json +0 -13
  100. google/adk/tests/integration/fixture/trip_planner_agent/test_config.json +0 -5
  101. google/adk/tests/integration/fixture/trip_planner_agent/test_files/initial.session.json +0 -13
  102. google/adk/tests/integration/fixture/trip_planner_agent/test_files/test_config.json +0 -5
  103. google/adk/tests/integration/fixture/trip_planner_agent/test_files/trip_inquiry_sub_agent.test.json +0 -7
  104. google/adk/tests/integration/fixture/trip_planner_agent/trip_inquiry.test.json +0 -19
  105. google/adk/tests/integration/models/__init__.py +0 -14
  106. google/adk/tests/integration/models/test_google_llm.py +0 -65
  107. google/adk/tests/integration/test_callback.py +0 -70
  108. google/adk/tests/integration/test_context_variable.py +0 -67
  109. google/adk/tests/integration/test_evalute_agent_in_fixture.py +0 -76
  110. google/adk/tests/integration/test_multi_agent.py +0 -28
  111. google/adk/tests/integration/test_multi_turn.py +0 -42
  112. google/adk/tests/integration/test_single_agent.py +0 -23
  113. google/adk/tests/integration/test_sub_agent.py +0 -26
  114. google/adk/tests/integration/test_system_instruction.py +0 -177
  115. google/adk/tests/integration/test_tools.py +0 -287
  116. google/adk/tests/integration/test_with_test_file.py +0 -34
  117. google/adk/tests/integration/tools/__init__.py +0 -14
  118. google/adk/tests/integration/utils/__init__.py +0 -16
  119. google/adk/tests/integration/utils/asserts.py +0 -75
  120. google/adk/tests/integration/utils/test_runner.py +0 -97
  121. google/adk/tests/unittests/__init__.py +0 -14
  122. google/adk/tests/unittests/agents/__init__.py +0 -14
  123. google/adk/tests/unittests/agents/test_base_agent.py +0 -407
  124. google/adk/tests/unittests/agents/test_langgraph_agent.py +0 -191
  125. google/adk/tests/unittests/agents/test_llm_agent_callbacks.py +0 -138
  126. google/adk/tests/unittests/agents/test_llm_agent_fields.py +0 -231
  127. google/adk/tests/unittests/agents/test_loop_agent.py +0 -136
  128. google/adk/tests/unittests/agents/test_parallel_agent.py +0 -92
  129. google/adk/tests/unittests/agents/test_sequential_agent.py +0 -114
  130. google/adk/tests/unittests/artifacts/__init__.py +0 -14
  131. google/adk/tests/unittests/artifacts/test_artifact_service.py +0 -276
  132. google/adk/tests/unittests/auth/test_auth_handler.py +0 -575
  133. google/adk/tests/unittests/conftest.py +0 -73
  134. google/adk/tests/unittests/fast_api/__init__.py +0 -14
  135. google/adk/tests/unittests/fast_api/test_fast_api.py +0 -269
  136. google/adk/tests/unittests/flows/__init__.py +0 -14
  137. google/adk/tests/unittests/flows/llm_flows/__init__.py +0 -14
  138. google/adk/tests/unittests/flows/llm_flows/_test_examples.py +0 -142
  139. google/adk/tests/unittests/flows/llm_flows/test_agent_transfer.py +0 -311
  140. google/adk/tests/unittests/flows/llm_flows/test_functions_long_running.py +0 -244
  141. google/adk/tests/unittests/flows/llm_flows/test_functions_request_euc.py +0 -346
  142. google/adk/tests/unittests/flows/llm_flows/test_functions_sequential.py +0 -93
  143. google/adk/tests/unittests/flows/llm_flows/test_functions_simple.py +0 -258
  144. google/adk/tests/unittests/flows/llm_flows/test_identity.py +0 -66
  145. google/adk/tests/unittests/flows/llm_flows/test_instructions.py +0 -164
  146. google/adk/tests/unittests/flows/llm_flows/test_model_callbacks.py +0 -142
  147. google/adk/tests/unittests/flows/llm_flows/test_other_configs.py +0 -46
  148. google/adk/tests/unittests/flows/llm_flows/test_tool_callbacks.py +0 -269
  149. google/adk/tests/unittests/models/__init__.py +0 -14
  150. google/adk/tests/unittests/models/test_google_llm.py +0 -224
  151. google/adk/tests/unittests/models/test_litellm.py +0 -804
  152. google/adk/tests/unittests/models/test_models.py +0 -60
  153. google/adk/tests/unittests/sessions/__init__.py +0 -14
  154. google/adk/tests/unittests/sessions/test_session_service.py +0 -227
  155. google/adk/tests/unittests/sessions/test_vertex_ai_session_service.py +0 -246
  156. google/adk/tests/unittests/streaming/__init__.py +0 -14
  157. google/adk/tests/unittests/streaming/test_streaming.py +0 -50
  158. google/adk/tests/unittests/tools/__init__.py +0 -14
  159. google/adk/tests/unittests/tools/apihub_tool/clients/test_apihub_client.py +0 -499
  160. google/adk/tests/unittests/tools/apihub_tool/test_apihub_toolset.py +0 -204
  161. google/adk/tests/unittests/tools/application_integration_tool/clients/test_connections_client.py +0 -600
  162. google/adk/tests/unittests/tools/application_integration_tool/clients/test_integration_client.py +0 -630
  163. google/adk/tests/unittests/tools/application_integration_tool/test_application_integration_toolset.py +0 -345
  164. google/adk/tests/unittests/tools/google_api_tool/__init__.py +0 -13
  165. google/adk/tests/unittests/tools/google_api_tool/test_googleapi_to_openapi_converter.py +0 -657
  166. google/adk/tests/unittests/tools/openapi_tool/auth/credential_exchangers/test_auto_auth_credential_exchanger.py +0 -145
  167. google/adk/tests/unittests/tools/openapi_tool/auth/credential_exchangers/test_base_auth_credential_exchanger.py +0 -68
  168. google/adk/tests/unittests/tools/openapi_tool/auth/credential_exchangers/test_oauth2_exchanger.py +0 -153
  169. google/adk/tests/unittests/tools/openapi_tool/auth/credential_exchangers/test_service_account_exchanger.py +0 -196
  170. google/adk/tests/unittests/tools/openapi_tool/auth/test_auth_helper.py +0 -573
  171. google/adk/tests/unittests/tools/openapi_tool/common/test_common.py +0 -436
  172. google/adk/tests/unittests/tools/openapi_tool/openapi_spec_parser/test.yaml +0 -1367
  173. google/adk/tests/unittests/tools/openapi_tool/openapi_spec_parser/test_openapi_spec_parser.py +0 -628
  174. google/adk/tests/unittests/tools/openapi_tool/openapi_spec_parser/test_openapi_toolset.py +0 -139
  175. google/adk/tests/unittests/tools/openapi_tool/openapi_spec_parser/test_operation_parser.py +0 -406
  176. google/adk/tests/unittests/tools/openapi_tool/openapi_spec_parser/test_rest_api_tool.py +0 -966
  177. google/adk/tests/unittests/tools/openapi_tool/openapi_spec_parser/test_tool_auth_handler.py +0 -201
  178. google/adk/tests/unittests/tools/retrieval/__init__.py +0 -14
  179. google/adk/tests/unittests/tools/retrieval/test_vertex_ai_rag_retrieval.py +0 -147
  180. google/adk/tests/unittests/tools/test_agent_tool.py +0 -167
  181. google/adk/tests/unittests/tools/test_base_tool.py +0 -141
  182. google/adk/tests/unittests/tools/test_build_function_declaration.py +0 -277
  183. google/adk/tests/unittests/utils.py +0 -304
  184. google_adk-0.0.3.dist-info/RECORD +0 -340
  185. {google_adk-0.0.3.dist-info → google_adk-0.0.5.dist-info}/entry_points.txt +0 -0
@@ -1,628 +0,0 @@
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"