google-adk 0.0.2__py3-none-any.whl → 0.0.4__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 (153) hide show
  1. google/adk/agents/run_config.py +4 -0
  2. google/adk/auth/auth_preprocessor.py +19 -16
  3. google/adk/cli/agent_graph.py +31 -5
  4. google/adk/cli/browser/index.html +1 -1
  5. google/adk/cli/browser/{main-XUU6OGCC.js → main-CU22TRPI.js} +30 -30
  6. google/adk/cli/cli.py +8 -8
  7. google/adk/cli/cli_deploy.py +2 -4
  8. google/adk/cli/cli_tools_click.py +57 -12
  9. google/adk/cli/fast_api.py +19 -9
  10. google/adk/flows/llm_flows/contents.py +21 -1
  11. google/adk/flows/llm_flows/functions.py +3 -1
  12. google/adk/models/google_llm.py +0 -1
  13. google/adk/runners.py +13 -2
  14. google/adk/version.py +1 -1
  15. {google_adk-0.0.2.dist-info → google_adk-0.0.4.dist-info}/METADATA +4 -2
  16. google_adk-0.0.4.dist-info/RECORD +175 -0
  17. {google_adk-0.0.2.dist-info → google_adk-0.0.4.dist-info}/WHEEL +1 -1
  18. google/adk/cli/media_streamer/__init__.py +0 -19
  19. google/adk/cli/media_streamer/index.html +0 -228
  20. google/adk/tests/__init__.py +0 -14
  21. google/adk/tests/integration/.env.example +0 -10
  22. google/adk/tests/integration/__init__.py +0 -18
  23. google/adk/tests/integration/conftest.py +0 -119
  24. google/adk/tests/integration/fixture/__init__.py +0 -14
  25. google/adk/tests/integration/fixture/agent_with_config/__init__.py +0 -15
  26. google/adk/tests/integration/fixture/agent_with_config/agent.py +0 -88
  27. google/adk/tests/integration/fixture/callback_agent/__init__.py +0 -15
  28. google/adk/tests/integration/fixture/callback_agent/agent.py +0 -105
  29. google/adk/tests/integration/fixture/context_update_test/OWNERS +0 -1
  30. google/adk/tests/integration/fixture/context_update_test/__init__.py +0 -15
  31. google/adk/tests/integration/fixture/context_update_test/agent.py +0 -43
  32. google/adk/tests/integration/fixture/context_update_test/successful_test.session.json +0 -582
  33. google/adk/tests/integration/fixture/context_variable_agent/__init__.py +0 -15
  34. google/adk/tests/integration/fixture/context_variable_agent/agent.py +0 -115
  35. google/adk/tests/integration/fixture/customer_support_ma/__init__.py +0 -15
  36. google/adk/tests/integration/fixture/customer_support_ma/agent.py +0 -172
  37. google/adk/tests/integration/fixture/ecommerce_customer_service_agent/__init__.py +0 -15
  38. google/adk/tests/integration/fixture/ecommerce_customer_service_agent/agent.py +0 -338
  39. google/adk/tests/integration/fixture/ecommerce_customer_service_agent/order_query.test.json +0 -69
  40. google/adk/tests/integration/fixture/ecommerce_customer_service_agent/test_config.json +0 -6
  41. google/adk/tests/integration/fixture/flow_complex_spark/__init__.py +0 -15
  42. google/adk/tests/integration/fixture/flow_complex_spark/agent.py +0 -182
  43. google/adk/tests/integration/fixture/flow_complex_spark/sample.debug.log +0 -243
  44. google/adk/tests/integration/fixture/flow_complex_spark/sample.session.json +0 -190
  45. google/adk/tests/integration/fixture/hello_world_agent/__init__.py +0 -15
  46. google/adk/tests/integration/fixture/hello_world_agent/agent.py +0 -95
  47. google/adk/tests/integration/fixture/hello_world_agent/roll_die.test.json +0 -24
  48. google/adk/tests/integration/fixture/hello_world_agent/test_config.json +0 -6
  49. google/adk/tests/integration/fixture/home_automation_agent/__init__.py +0 -15
  50. google/adk/tests/integration/fixture/home_automation_agent/agent.py +0 -304
  51. google/adk/tests/integration/fixture/home_automation_agent/simple_test.test.json +0 -5
  52. google/adk/tests/integration/fixture/home_automation_agent/simple_test2.test.json +0 -5
  53. google/adk/tests/integration/fixture/home_automation_agent/test_config.json +0 -5
  54. google/adk/tests/integration/fixture/home_automation_agent/test_files/dependent_tool_calls.test.json +0 -18
  55. google/adk/tests/integration/fixture/home_automation_agent/test_files/memorizing_past_events/eval_data.test.json +0 -17
  56. google/adk/tests/integration/fixture/home_automation_agent/test_files/memorizing_past_events/test_config.json +0 -6
  57. google/adk/tests/integration/fixture/home_automation_agent/test_files/simple_multi_turn_conversation.test.json +0 -18
  58. google/adk/tests/integration/fixture/home_automation_agent/test_files/simple_test.test.json +0 -17
  59. google/adk/tests/integration/fixture/home_automation_agent/test_files/simple_test2.test.json +0 -5
  60. google/adk/tests/integration/fixture/home_automation_agent/test_files/test_config.json +0 -5
  61. google/adk/tests/integration/fixture/tool_agent/__init__.py +0 -15
  62. google/adk/tests/integration/fixture/tool_agent/agent.py +0 -218
  63. google/adk/tests/integration/fixture/tool_agent/files/Agent_test_plan.pdf +0 -0
  64. google/adk/tests/integration/fixture/trip_planner_agent/__init__.py +0 -15
  65. google/adk/tests/integration/fixture/trip_planner_agent/agent.py +0 -110
  66. google/adk/tests/integration/fixture/trip_planner_agent/initial.session.json +0 -13
  67. google/adk/tests/integration/fixture/trip_planner_agent/test_config.json +0 -5
  68. google/adk/tests/integration/fixture/trip_planner_agent/test_files/initial.session.json +0 -13
  69. google/adk/tests/integration/fixture/trip_planner_agent/test_files/test_config.json +0 -5
  70. google/adk/tests/integration/fixture/trip_planner_agent/test_files/trip_inquiry_sub_agent.test.json +0 -7
  71. google/adk/tests/integration/fixture/trip_planner_agent/trip_inquiry.test.json +0 -19
  72. google/adk/tests/integration/models/__init__.py +0 -14
  73. google/adk/tests/integration/models/test_google_llm.py +0 -65
  74. google/adk/tests/integration/test_callback.py +0 -70
  75. google/adk/tests/integration/test_context_variable.py +0 -67
  76. google/adk/tests/integration/test_evalute_agent_in_fixture.py +0 -76
  77. google/adk/tests/integration/test_multi_agent.py +0 -28
  78. google/adk/tests/integration/test_multi_turn.py +0 -42
  79. google/adk/tests/integration/test_single_agent.py +0 -23
  80. google/adk/tests/integration/test_sub_agent.py +0 -26
  81. google/adk/tests/integration/test_system_instruction.py +0 -177
  82. google/adk/tests/integration/test_tools.py +0 -287
  83. google/adk/tests/integration/test_with_test_file.py +0 -34
  84. google/adk/tests/integration/tools/__init__.py +0 -14
  85. google/adk/tests/integration/utils/__init__.py +0 -16
  86. google/adk/tests/integration/utils/asserts.py +0 -75
  87. google/adk/tests/integration/utils/test_runner.py +0 -97
  88. google/adk/tests/unittests/__init__.py +0 -14
  89. google/adk/tests/unittests/agents/__init__.py +0 -14
  90. google/adk/tests/unittests/agents/test_base_agent.py +0 -407
  91. google/adk/tests/unittests/agents/test_langgraph_agent.py +0 -191
  92. google/adk/tests/unittests/agents/test_llm_agent_callbacks.py +0 -138
  93. google/adk/tests/unittests/agents/test_llm_agent_fields.py +0 -231
  94. google/adk/tests/unittests/agents/test_loop_agent.py +0 -136
  95. google/adk/tests/unittests/agents/test_parallel_agent.py +0 -92
  96. google/adk/tests/unittests/agents/test_sequential_agent.py +0 -114
  97. google/adk/tests/unittests/artifacts/__init__.py +0 -14
  98. google/adk/tests/unittests/artifacts/test_artifact_service.py +0 -276
  99. google/adk/tests/unittests/auth/test_auth_handler.py +0 -575
  100. google/adk/tests/unittests/conftest.py +0 -73
  101. google/adk/tests/unittests/fast_api/__init__.py +0 -14
  102. google/adk/tests/unittests/fast_api/test_fast_api.py +0 -269
  103. google/adk/tests/unittests/flows/__init__.py +0 -14
  104. google/adk/tests/unittests/flows/llm_flows/__init__.py +0 -14
  105. google/adk/tests/unittests/flows/llm_flows/_test_examples.py +0 -142
  106. google/adk/tests/unittests/flows/llm_flows/test_agent_transfer.py +0 -311
  107. google/adk/tests/unittests/flows/llm_flows/test_functions_long_running.py +0 -244
  108. google/adk/tests/unittests/flows/llm_flows/test_functions_request_euc.py +0 -346
  109. google/adk/tests/unittests/flows/llm_flows/test_functions_sequential.py +0 -93
  110. google/adk/tests/unittests/flows/llm_flows/test_functions_simple.py +0 -258
  111. google/adk/tests/unittests/flows/llm_flows/test_identity.py +0 -66
  112. google/adk/tests/unittests/flows/llm_flows/test_instructions.py +0 -164
  113. google/adk/tests/unittests/flows/llm_flows/test_model_callbacks.py +0 -142
  114. google/adk/tests/unittests/flows/llm_flows/test_other_configs.py +0 -46
  115. google/adk/tests/unittests/flows/llm_flows/test_tool_callbacks.py +0 -269
  116. google/adk/tests/unittests/models/__init__.py +0 -14
  117. google/adk/tests/unittests/models/test_google_llm.py +0 -224
  118. google/adk/tests/unittests/models/test_litellm.py +0 -804
  119. google/adk/tests/unittests/models/test_models.py +0 -60
  120. google/adk/tests/unittests/sessions/__init__.py +0 -14
  121. google/adk/tests/unittests/sessions/test_session_service.py +0 -227
  122. google/adk/tests/unittests/sessions/test_vertex_ai_session_service.py +0 -246
  123. google/adk/tests/unittests/streaming/__init__.py +0 -14
  124. google/adk/tests/unittests/streaming/test_streaming.py +0 -50
  125. google/adk/tests/unittests/tools/__init__.py +0 -14
  126. google/adk/tests/unittests/tools/apihub_tool/clients/test_apihub_client.py +0 -499
  127. google/adk/tests/unittests/tools/apihub_tool/test_apihub_toolset.py +0 -204
  128. google/adk/tests/unittests/tools/application_integration_tool/clients/test_connections_client.py +0 -600
  129. google/adk/tests/unittests/tools/application_integration_tool/clients/test_integration_client.py +0 -630
  130. google/adk/tests/unittests/tools/application_integration_tool/test_application_integration_toolset.py +0 -345
  131. google/adk/tests/unittests/tools/google_api_tool/__init__.py +0 -13
  132. google/adk/tests/unittests/tools/google_api_tool/test_googleapi_to_openapi_converter.py +0 -657
  133. google/adk/tests/unittests/tools/openapi_tool/auth/credential_exchangers/test_auto_auth_credential_exchanger.py +0 -145
  134. google/adk/tests/unittests/tools/openapi_tool/auth/credential_exchangers/test_base_auth_credential_exchanger.py +0 -68
  135. google/adk/tests/unittests/tools/openapi_tool/auth/credential_exchangers/test_oauth2_exchanger.py +0 -153
  136. google/adk/tests/unittests/tools/openapi_tool/auth/credential_exchangers/test_service_account_exchanger.py +0 -196
  137. google/adk/tests/unittests/tools/openapi_tool/auth/test_auth_helper.py +0 -573
  138. google/adk/tests/unittests/tools/openapi_tool/common/test_common.py +0 -436
  139. google/adk/tests/unittests/tools/openapi_tool/openapi_spec_parser/test.yaml +0 -1367
  140. google/adk/tests/unittests/tools/openapi_tool/openapi_spec_parser/test_openapi_spec_parser.py +0 -628
  141. google/adk/tests/unittests/tools/openapi_tool/openapi_spec_parser/test_openapi_toolset.py +0 -139
  142. google/adk/tests/unittests/tools/openapi_tool/openapi_spec_parser/test_operation_parser.py +0 -406
  143. google/adk/tests/unittests/tools/openapi_tool/openapi_spec_parser/test_rest_api_tool.py +0 -966
  144. google/adk/tests/unittests/tools/openapi_tool/openapi_spec_parser/test_tool_auth_handler.py +0 -201
  145. google/adk/tests/unittests/tools/retrieval/__init__.py +0 -14
  146. google/adk/tests/unittests/tools/retrieval/test_vertex_ai_rag_retrieval.py +0 -147
  147. google/adk/tests/unittests/tools/test_agent_tool.py +0 -167
  148. google/adk/tests/unittests/tools/test_base_tool.py +0 -141
  149. google/adk/tests/unittests/tools/test_build_function_declaration.py +0 -277
  150. google/adk/tests/unittests/utils.py +0 -304
  151. google_adk-0.0.2.dist-info/RECORD +0 -308
  152. {google_adk-0.0.2.dist-info → google_adk-0.0.4.dist-info}/entry_points.txt +0 -0
  153. {google_adk-0.0.2.dist-info → google_adk-0.0.4.dist-info/licenses}/LICENSE +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"