Coverage for tests/test_openapi_separate_input_output_schemas.py: 100%
73 statements
« prev ^ index » next coverage.py v7.6.1, created at 2025-12-04 08:29 +0000
« prev ^ index » next coverage.py v7.6.1, created at 2025-12-04 08:29 +0000
1from typing import List, Optional 1abcdefg
3from fastapi import FastAPI 1abcdefg
4from fastapi.testclient import TestClient 1abcdefg
5from inline_snapshot import snapshot 1abcdefg
6from pydantic import BaseModel 1abcdefg
8from .utils import PYDANTIC_V2, needs_pydanticv2 1abcdefg
11class SubItem(BaseModel): 1abcdefg
12 subname: str 1abcdefg
13 sub_description: Optional[str] = None 1abcdefg
14 tags: List[str] = [] 1abcdefg
15 if PYDANTIC_V2: 1abcdefg
16 model_config = {"json_schema_serialization_defaults_required": True} 1abcdefg
19class Item(BaseModel): 1abcdefg
20 name: str 1abcdefg
21 description: Optional[str] = None 1abcdefg
22 sub: Optional[SubItem] = None 1abcdefg
23 if PYDANTIC_V2: 1abcdefg
24 model_config = {"json_schema_serialization_defaults_required": True} 1abcdefg
27def get_app_client(separate_input_output_schemas: bool = True) -> TestClient: 1abcdefg
28 app = FastAPI(separate_input_output_schemas=separate_input_output_schemas) 1vhiJKwxjkLMyzlmNOABnoPQCDpqRSEFrsTUGHtuVWI
30 @app.post("/items/", responses={402: {"model": Item}}) 1vhiJKwxjkLMyzlmNOABnoPQCDpqRSEFrsTUGHtuVWI
31 def create_item(item: Item) -> Item: 1vhiJKwxjkLMyzlmNOABnoPQCDpqRSEFrsTUGHtuVWI
32 return item 1vixkzmBoDqFsHu
34 @app.post("/items-list/") 1vhiJKwxjkLMyzlmNOABnoPQCDpqRSEFrsTUGHtuVWI
35 def create_item_list(item: List[Item]): 1vhiJKwxjkLMyzlmNOABnoPQCDpqRSEFrsTUGHtuVWI
36 return item 1hjlnprt
38 @app.get("/items/") 1vhiJKwxjkLMyzlmNOABnoPQCDpqRSEFrsTUGHtuVWI
39 def read_items() -> List[Item]: 1vhiJKwxjkLMyzlmNOABnoPQCDpqRSEFrsTUGHtuVWI
40 return [ 1wyACEGI
41 Item(
42 name="Portal Gun",
43 description="Device to travel through the multi-rick-verse",
44 sub=SubItem(subname="subname"),
45 ),
46 Item(name="Plumbus"),
47 ]
49 client = TestClient(app) 1vhiJKwxjkLMyzlmNOABnoPQCDpqRSEFrsTUGHtuVWI
50 return client 1vhiJKwxjkLMyzlmNOABnoPQCDpqRSEFrsTUGHtuVWI
53def test_create_item(): 1abcdefg
54 client = get_app_client() 1vxzBDFH
55 client_no = get_app_client(separate_input_output_schemas=False) 1vxzBDFH
56 response = client.post("/items/", json={"name": "Plumbus"}) 1vxzBDFH
57 response2 = client_no.post("/items/", json={"name": "Plumbus"}) 1vxzBDFH
58 assert response.status_code == response2.status_code == 200, response.text 1vxzBDFH
59 assert ( 1vxzBDFH
60 response.json()
61 == response2.json()
62 == {"name": "Plumbus", "description": None, "sub": None}
63 )
66def test_create_item_with_sub(): 1abcdefg
67 client = get_app_client() 1ikmoqsu
68 client_no = get_app_client(separate_input_output_schemas=False) 1ikmoqsu
69 data = { 1ikmoqsu
70 "name": "Plumbus",
71 "sub": {"subname": "SubPlumbus", "sub_description": "Sub WTF"},
72 }
73 response = client.post("/items/", json=data) 1ikmoqsu
74 response2 = client_no.post("/items/", json=data) 1ikmoqsu
75 assert response.status_code == response2.status_code == 200, response.text 1ikmoqsu
76 assert ( 1ikmoqsu
77 response.json()
78 == response2.json()
79 == {
80 "name": "Plumbus",
81 "description": None,
82 "sub": {"subname": "SubPlumbus", "sub_description": "Sub WTF", "tags": []},
83 }
84 )
87def test_create_item_list(): 1abcdefg
88 client = get_app_client() 1hjlnprt
89 client_no = get_app_client(separate_input_output_schemas=False) 1hjlnprt
90 data = [ 1hjlnprt
91 {"name": "Plumbus"},
92 {
93 "name": "Portal Gun",
94 "description": "Device to travel through the multi-rick-verse",
95 },
96 ]
97 response = client.post("/items-list/", json=data) 1hjlnprt
98 response2 = client_no.post("/items-list/", json=data) 1hjlnprt
99 assert response.status_code == response2.status_code == 200, response.text 1hjlnprt
100 assert ( 1hjlnprt
101 response.json()
102 == response2.json()
103 == [
104 {"name": "Plumbus", "description": None, "sub": None},
105 {
106 "name": "Portal Gun",
107 "description": "Device to travel through the multi-rick-verse",
108 "sub": None,
109 },
110 ]
111 )
114def test_read_items(): 1abcdefg
115 client = get_app_client() 1wyACEGI
116 client_no = get_app_client(separate_input_output_schemas=False) 1wyACEGI
117 response = client.get("/items/") 1wyACEGI
118 response2 = client_no.get("/items/") 1wyACEGI
119 assert response.status_code == response2.status_code == 200, response.text 1wyACEGI
120 assert ( 1wyACEGI
121 response.json()
122 == response2.json()
123 == [
124 {
125 "name": "Portal Gun",
126 "description": "Device to travel through the multi-rick-verse",
127 "sub": {"subname": "subname", "sub_description": None, "tags": []},
128 },
129 {"name": "Plumbus", "description": None, "sub": None},
130 ]
131 )
134@needs_pydanticv2 1abcdefg
135def test_openapi_schema(): 1abcdefg
136 client = get_app_client() 1JLNPRTV
137 response = client.get("/openapi.json") 1JLNPRTV
138 assert response.status_code == 200, response.text 1JLNPRTV
139 assert response.json() == snapshot( 1JLNPRTV
140 {
141 "openapi": "3.1.0",
142 "info": {"title": "FastAPI", "version": "0.1.0"},
143 "paths": {
144 "/items/": {
145 "get": {
146 "summary": "Read Items",
147 "operationId": "read_items_items__get",
148 "responses": {
149 "200": {
150 "description": "Successful Response",
151 "content": {
152 "application/json": {
153 "schema": {
154 "items": {
155 "$ref": "#/components/schemas/Item-Output"
156 },
157 "type": "array",
158 "title": "Response Read Items Items Get",
159 }
160 }
161 },
162 }
163 },
164 },
165 "post": {
166 "summary": "Create Item",
167 "operationId": "create_item_items__post",
168 "requestBody": {
169 "content": {
170 "application/json": {
171 "schema": {
172 "$ref": "#/components/schemas/Item-Input"
173 }
174 }
175 },
176 "required": True,
177 },
178 "responses": {
179 "200": {
180 "description": "Successful Response",
181 "content": {
182 "application/json": {
183 "schema": {
184 "$ref": "#/components/schemas/Item-Output"
185 }
186 }
187 },
188 },
189 "402": {
190 "description": "Payment Required",
191 "content": {
192 "application/json": {
193 "schema": {
194 "$ref": "#/components/schemas/Item-Output"
195 }
196 }
197 },
198 },
199 "422": {
200 "description": "Validation Error",
201 "content": {
202 "application/json": {
203 "schema": {
204 "$ref": "#/components/schemas/HTTPValidationError"
205 }
206 }
207 },
208 },
209 },
210 },
211 },
212 "/items-list/": {
213 "post": {
214 "summary": "Create Item List",
215 "operationId": "create_item_list_items_list__post",
216 "requestBody": {
217 "content": {
218 "application/json": {
219 "schema": {
220 "items": {
221 "$ref": "#/components/schemas/Item-Input"
222 },
223 "type": "array",
224 "title": "Item",
225 }
226 }
227 },
228 "required": True,
229 },
230 "responses": {
231 "200": {
232 "description": "Successful Response",
233 "content": {"application/json": {"schema": {}}},
234 },
235 "422": {
236 "description": "Validation Error",
237 "content": {
238 "application/json": {
239 "schema": {
240 "$ref": "#/components/schemas/HTTPValidationError"
241 }
242 }
243 },
244 },
245 },
246 }
247 },
248 },
249 "components": {
250 "schemas": {
251 "HTTPValidationError": {
252 "properties": {
253 "detail": {
254 "items": {
255 "$ref": "#/components/schemas/ValidationError"
256 },
257 "type": "array",
258 "title": "Detail",
259 }
260 },
261 "type": "object",
262 "title": "HTTPValidationError",
263 },
264 "Item-Input": {
265 "properties": {
266 "name": {"type": "string", "title": "Name"},
267 "description": {
268 "anyOf": [{"type": "string"}, {"type": "null"}],
269 "title": "Description",
270 },
271 "sub": {
272 "anyOf": [
273 {"$ref": "#/components/schemas/SubItem-Input"},
274 {"type": "null"},
275 ]
276 },
277 },
278 "type": "object",
279 "required": ["name"],
280 "title": "Item",
281 },
282 "Item-Output": {
283 "properties": {
284 "name": {"type": "string", "title": "Name"},
285 "description": {
286 "anyOf": [{"type": "string"}, {"type": "null"}],
287 "title": "Description",
288 },
289 "sub": {
290 "anyOf": [
291 {"$ref": "#/components/schemas/SubItem-Output"},
292 {"type": "null"},
293 ]
294 },
295 },
296 "type": "object",
297 "required": ["name", "description", "sub"],
298 "title": "Item",
299 },
300 "SubItem-Input": {
301 "properties": {
302 "subname": {"type": "string", "title": "Subname"},
303 "sub_description": {
304 "anyOf": [{"type": "string"}, {"type": "null"}],
305 "title": "Sub Description",
306 },
307 "tags": {
308 "items": {"type": "string"},
309 "type": "array",
310 "title": "Tags",
311 "default": [],
312 },
313 },
314 "type": "object",
315 "required": ["subname"],
316 "title": "SubItem",
317 },
318 "SubItem-Output": {
319 "properties": {
320 "subname": {"type": "string", "title": "Subname"},
321 "sub_description": {
322 "anyOf": [{"type": "string"}, {"type": "null"}],
323 "title": "Sub Description",
324 },
325 "tags": {
326 "items": {"type": "string"},
327 "type": "array",
328 "title": "Tags",
329 "default": [],
330 },
331 },
332 "type": "object",
333 "required": ["subname", "sub_description", "tags"],
334 "title": "SubItem",
335 },
336 "ValidationError": {
337 "properties": {
338 "loc": {
339 "items": {
340 "anyOf": [{"type": "string"}, {"type": "integer"}]
341 },
342 "type": "array",
343 "title": "Location",
344 },
345 "msg": {"type": "string", "title": "Message"},
346 "type": {"type": "string", "title": "Error Type"},
347 },
348 "type": "object",
349 "required": ["loc", "msg", "type"],
350 "title": "ValidationError",
351 },
352 }
353 },
354 }
355 )
358@needs_pydanticv2 1abcdefg
359def test_openapi_schema_no_separate(): 1abcdefg
360 client = get_app_client(separate_input_output_schemas=False) 1KMOQSUW
361 response = client.get("/openapi.json") 1KMOQSUW
362 assert response.status_code == 200, response.text 1KMOQSUW
363 assert response.json() == { 1KMOQSUW
364 "openapi": "3.1.0",
365 "info": {"title": "FastAPI", "version": "0.1.0"},
366 "paths": {
367 "/items/": {
368 "get": {
369 "summary": "Read Items",
370 "operationId": "read_items_items__get",
371 "responses": {
372 "200": {
373 "description": "Successful Response",
374 "content": {
375 "application/json": {
376 "schema": {
377 "items": {"$ref": "#/components/schemas/Item"},
378 "type": "array",
379 "title": "Response Read Items Items Get",
380 }
381 }
382 },
383 }
384 },
385 },
386 "post": {
387 "summary": "Create Item",
388 "operationId": "create_item_items__post",
389 "requestBody": {
390 "content": {
391 "application/json": {
392 "schema": {"$ref": "#/components/schemas/Item"}
393 }
394 },
395 "required": True,
396 },
397 "responses": {
398 "200": {
399 "description": "Successful Response",
400 "content": {
401 "application/json": {
402 "schema": {"$ref": "#/components/schemas/Item"}
403 }
404 },
405 },
406 "402": {
407 "description": "Payment Required",
408 "content": {
409 "application/json": {
410 "schema": {"$ref": "#/components/schemas/Item"}
411 }
412 },
413 },
414 "422": {
415 "description": "Validation Error",
416 "content": {
417 "application/json": {
418 "schema": {
419 "$ref": "#/components/schemas/HTTPValidationError"
420 }
421 }
422 },
423 },
424 },
425 },
426 },
427 "/items-list/": {
428 "post": {
429 "summary": "Create Item List",
430 "operationId": "create_item_list_items_list__post",
431 "requestBody": {
432 "content": {
433 "application/json": {
434 "schema": {
435 "items": {"$ref": "#/components/schemas/Item"},
436 "type": "array",
437 "title": "Item",
438 }
439 }
440 },
441 "required": True,
442 },
443 "responses": {
444 "200": {
445 "description": "Successful Response",
446 "content": {"application/json": {"schema": {}}},
447 },
448 "422": {
449 "description": "Validation Error",
450 "content": {
451 "application/json": {
452 "schema": {
453 "$ref": "#/components/schemas/HTTPValidationError"
454 }
455 }
456 },
457 },
458 },
459 }
460 },
461 },
462 "components": {
463 "schemas": {
464 "HTTPValidationError": {
465 "properties": {
466 "detail": {
467 "items": {"$ref": "#/components/schemas/ValidationError"},
468 "type": "array",
469 "title": "Detail",
470 }
471 },
472 "type": "object",
473 "title": "HTTPValidationError",
474 },
475 "Item": {
476 "properties": {
477 "name": {"type": "string", "title": "Name"},
478 "description": {
479 "anyOf": [{"type": "string"}, {"type": "null"}],
480 "title": "Description",
481 },
482 "sub": {
483 "anyOf": [
484 {"$ref": "#/components/schemas/SubItem"},
485 {"type": "null"},
486 ]
487 },
488 },
489 "type": "object",
490 "required": ["name"],
491 "title": "Item",
492 },
493 "SubItem": {
494 "properties": {
495 "subname": {"type": "string", "title": "Subname"},
496 "sub_description": {
497 "anyOf": [{"type": "string"}, {"type": "null"}],
498 "title": "Sub Description",
499 },
500 "tags": {
501 "items": {"type": "string"},
502 "type": "array",
503 "title": "Tags",
504 "default": [],
505 },
506 },
507 "type": "object",
508 "required": ["subname"],
509 "title": "SubItem",
510 },
511 "ValidationError": {
512 "properties": {
513 "loc": {
514 "items": {
515 "anyOf": [{"type": "string"}, {"type": "integer"}]
516 },
517 "type": "array",
518 "title": "Location",
519 },
520 "msg": {"type": "string", "title": "Message"},
521 "type": {"type": "string", "title": "Error Type"},
522 },
523 "type": "object",
524 "required": ["loc", "msg", "type"],
525 "title": "ValidationError",
526 },
527 }
528 },
529 }