Coverage for tests / test_openapi_separate_input_output_schemas.py: 100%
84 statements
« prev ^ index » next coverage.py v7.13.3, created at 2026-02-12 18:15 +0000
« prev ^ index » next coverage.py v7.13.3, created at 2026-02-12 18:15 +0000
1from typing import Optional 1abcd
3from fastapi import FastAPI 1abcd
4from fastapi.testclient import TestClient 1abcd
5from inline_snapshot import snapshot 1abcd
6from pydantic import BaseModel, computed_field 1abcd
9class SubItem(BaseModel): 1abcd
10 subname: str 1abcd
11 sub_description: Optional[str] = None 1abcd
12 tags: list[str] = [] 1abcd
13 model_config = {"json_schema_serialization_defaults_required": True} 1abcd
16class Item(BaseModel): 1abcd
17 name: str 1abcd
18 description: Optional[str] = None 1abcd
19 sub: Optional[SubItem] = None 1abcd
20 model_config = {"json_schema_serialization_defaults_required": True} 1abcd
23class WithComputedField(BaseModel): 1abcd
24 name: str 1abcd
26 @computed_field 1abcd
27 @property 1abcd
28 def computed_field(self) -> str: 1abcd
29 return f"computed {self.name}" 1efk
32def get_app_client(separate_input_output_schemas: bool = True) -> TestClient: 1abcd
33 app = FastAPI(separate_input_output_schemas=separate_input_output_schemas) 1lghtumenijvwofrpqxysk
35 @app.post("/items/", responses={402: {"model": Item}}) 1lghtumenijvwofrpqxysk
36 def create_item(item: Item) -> Item: 1lghtumenijvwofrpqxysk
37 return item 1lhnjrq
39 @app.post("/items-list/") 1lghtumenijvwofrpqxysk
40 def create_item_list(item: list[Item]): 1lghtumenijvwofrpqxysk
41 return item 1gip
43 @app.get("/items/") 1lghtumenijvwofrpqxysk
44 def read_items() -> list[Item]: 1lghtumenijvwofrpqxysk
45 return [ 1mos
46 Item(
47 name="Portal Gun",
48 description="Device to travel through the multi-rick-verse",
49 sub=SubItem(subname="subname"),
50 ),
51 Item(name="Plumbus"),
52 ]
54 @app.post("/with-computed-field/") 1lghtumenijvwofrpqxysk
55 def create_with_computed_field( 1lghtumenijvwofrpqxysk
56 with_computed_field: WithComputedField,
57 ) -> WithComputedField:
58 return with_computed_field 1efk
60 client = TestClient(app) 1lghtumenijvwofrpqxysk
61 return client 1lghtumenijvwofrpqxysk
64def test_create_item(): 1abcd
65 client = get_app_client() 1lnr
66 client_no = get_app_client(separate_input_output_schemas=False) 1lnr
67 response = client.post("/items/", json={"name": "Plumbus"}) 1lnr
68 response2 = client_no.post("/items/", json={"name": "Plumbus"}) 1lnr
69 assert response.status_code == response2.status_code == 200, response.text 1lnr
70 assert ( 1ln
71 response.json()
72 == response2.json()
73 == {"name": "Plumbus", "description": None, "sub": None}
74 )
77def test_create_item_with_sub(): 1abcd
78 client = get_app_client() 1hjq
79 client_no = get_app_client(separate_input_output_schemas=False) 1hjq
80 data = { 1hjq
81 "name": "Plumbus",
82 "sub": {"subname": "SubPlumbus", "sub_description": "Sub WTF"},
83 }
84 response = client.post("/items/", json=data) 1hjq
85 response2 = client_no.post("/items/", json=data) 1hjq
86 assert response.status_code == response2.status_code == 200, response.text 1hjq
87 assert ( 1hj
88 response.json()
89 == response2.json()
90 == {
91 "name": "Plumbus",
92 "description": None,
93 "sub": {"subname": "SubPlumbus", "sub_description": "Sub WTF", "tags": []},
94 }
95 )
98def test_create_item_list(): 1abcd
99 client = get_app_client() 1gip
100 client_no = get_app_client(separate_input_output_schemas=False) 1gip
101 data = [ 1gip
102 {"name": "Plumbus"},
103 {
104 "name": "Portal Gun",
105 "description": "Device to travel through the multi-rick-verse",
106 },
107 ]
108 response = client.post("/items-list/", json=data) 1gip
109 response2 = client_no.post("/items-list/", json=data) 1gip
110 assert response.status_code == response2.status_code == 200, response.text 1gip
111 assert ( 1gi
112 response.json()
113 == response2.json()
114 == [
115 {"name": "Plumbus", "description": None, "sub": None},
116 {
117 "name": "Portal Gun",
118 "description": "Device to travel through the multi-rick-verse",
119 "sub": None,
120 },
121 ]
122 )
125def test_read_items(): 1abcd
126 client = get_app_client() 1mos
127 client_no = get_app_client(separate_input_output_schemas=False) 1mos
128 response = client.get("/items/") 1mos
129 response2 = client_no.get("/items/") 1mos
130 assert response.status_code == response2.status_code == 200, response.text 1mos
131 assert ( 1mo
132 response.json()
133 == response2.json()
134 == [
135 {
136 "name": "Portal Gun",
137 "description": "Device to travel through the multi-rick-verse",
138 "sub": {"subname": "subname", "sub_description": None, "tags": []},
139 },
140 {"name": "Plumbus", "description": None, "sub": None},
141 ]
142 )
145def test_with_computed_field(): 1abcd
146 client = get_app_client() 1efk
147 client_no = get_app_client(separate_input_output_schemas=False) 1efk
148 response = client.post("/with-computed-field/", json={"name": "example"}) 1efk
149 response2 = client_no.post("/with-computed-field/", json={"name": "example"}) 1efk
150 assert response.status_code == response2.status_code == 200, response.text 1efk
151 assert ( 1ef
152 response.json()
153 == response2.json()
154 == {
155 "name": "example",
156 "computed_field": "computed example",
157 }
158 )
161def test_openapi_schema(): 1abcd
162 client = get_app_client() 1tvx
163 response = client.get("/openapi.json") 1tvx
164 assert response.status_code == 200, response.text 1tvx
165 assert response.json() == snapshot( 1tvx
166 {
167 "openapi": "3.1.0",
168 "info": {"title": "FastAPI", "version": "0.1.0"},
169 "paths": {
170 "/items/": {
171 "get": {
172 "summary": "Read Items",
173 "operationId": "read_items_items__get",
174 "responses": {
175 "200": {
176 "description": "Successful Response",
177 "content": {
178 "application/json": {
179 "schema": {
180 "items": {
181 "$ref": "#/components/schemas/Item-Output"
182 },
183 "type": "array",
184 "title": "Response Read Items Items Get",
185 }
186 }
187 },
188 }
189 },
190 },
191 "post": {
192 "summary": "Create Item",
193 "operationId": "create_item_items__post",
194 "requestBody": {
195 "content": {
196 "application/json": {
197 "schema": {
198 "$ref": "#/components/schemas/Item-Input"
199 }
200 }
201 },
202 "required": True,
203 },
204 "responses": {
205 "200": {
206 "description": "Successful Response",
207 "content": {
208 "application/json": {
209 "schema": {
210 "$ref": "#/components/schemas/Item-Output"
211 }
212 }
213 },
214 },
215 "402": {
216 "description": "Payment Required",
217 "content": {
218 "application/json": {
219 "schema": {
220 "$ref": "#/components/schemas/Item-Output"
221 }
222 }
223 },
224 },
225 "422": {
226 "description": "Validation Error",
227 "content": {
228 "application/json": {
229 "schema": {
230 "$ref": "#/components/schemas/HTTPValidationError"
231 }
232 }
233 },
234 },
235 },
236 },
237 },
238 "/items-list/": {
239 "post": {
240 "summary": "Create Item List",
241 "operationId": "create_item_list_items_list__post",
242 "requestBody": {
243 "content": {
244 "application/json": {
245 "schema": {
246 "items": {
247 "$ref": "#/components/schemas/Item-Input"
248 },
249 "type": "array",
250 "title": "Item",
251 }
252 }
253 },
254 "required": True,
255 },
256 "responses": {
257 "200": {
258 "description": "Successful Response",
259 "content": {"application/json": {"schema": {}}},
260 },
261 "422": {
262 "description": "Validation Error",
263 "content": {
264 "application/json": {
265 "schema": {
266 "$ref": "#/components/schemas/HTTPValidationError"
267 }
268 }
269 },
270 },
271 },
272 }
273 },
274 "/with-computed-field/": {
275 "post": {
276 "summary": "Create With Computed Field",
277 "operationId": "create_with_computed_field_with_computed_field__post",
278 "requestBody": {
279 "content": {
280 "application/json": {
281 "schema": {
282 "$ref": "#/components/schemas/WithComputedField-Input"
283 }
284 }
285 },
286 "required": True,
287 },
288 "responses": {
289 "200": {
290 "description": "Successful Response",
291 "content": {
292 "application/json": {
293 "schema": {
294 "$ref": "#/components/schemas/WithComputedField-Output"
295 }
296 }
297 },
298 },
299 "422": {
300 "description": "Validation Error",
301 "content": {
302 "application/json": {
303 "schema": {
304 "$ref": "#/components/schemas/HTTPValidationError"
305 }
306 }
307 },
308 },
309 },
310 },
311 },
312 },
313 "components": {
314 "schemas": {
315 "HTTPValidationError": {
316 "properties": {
317 "detail": {
318 "items": {
319 "$ref": "#/components/schemas/ValidationError"
320 },
321 "type": "array",
322 "title": "Detail",
323 }
324 },
325 "type": "object",
326 "title": "HTTPValidationError",
327 },
328 "Item-Input": {
329 "properties": {
330 "name": {"type": "string", "title": "Name"},
331 "description": {
332 "anyOf": [{"type": "string"}, {"type": "null"}],
333 "title": "Description",
334 },
335 "sub": {
336 "anyOf": [
337 {"$ref": "#/components/schemas/SubItem-Input"},
338 {"type": "null"},
339 ]
340 },
341 },
342 "type": "object",
343 "required": ["name"],
344 "title": "Item",
345 },
346 "Item-Output": {
347 "properties": {
348 "name": {"type": "string", "title": "Name"},
349 "description": {
350 "anyOf": [{"type": "string"}, {"type": "null"}],
351 "title": "Description",
352 },
353 "sub": {
354 "anyOf": [
355 {"$ref": "#/components/schemas/SubItem-Output"},
356 {"type": "null"},
357 ]
358 },
359 },
360 "type": "object",
361 "required": ["name", "description", "sub"],
362 "title": "Item",
363 },
364 "SubItem-Input": {
365 "properties": {
366 "subname": {"type": "string", "title": "Subname"},
367 "sub_description": {
368 "anyOf": [{"type": "string"}, {"type": "null"}],
369 "title": "Sub Description",
370 },
371 "tags": {
372 "items": {"type": "string"},
373 "type": "array",
374 "title": "Tags",
375 "default": [],
376 },
377 },
378 "type": "object",
379 "required": ["subname"],
380 "title": "SubItem",
381 },
382 "SubItem-Output": {
383 "properties": {
384 "subname": {"type": "string", "title": "Subname"},
385 "sub_description": {
386 "anyOf": [{"type": "string"}, {"type": "null"}],
387 "title": "Sub Description",
388 },
389 "tags": {
390 "items": {"type": "string"},
391 "type": "array",
392 "title": "Tags",
393 "default": [],
394 },
395 },
396 "type": "object",
397 "required": ["subname", "sub_description", "tags"],
398 "title": "SubItem",
399 },
400 "WithComputedField-Input": {
401 "properties": {"name": {"type": "string", "title": "Name"}},
402 "type": "object",
403 "required": ["name"],
404 "title": "WithComputedField",
405 },
406 "WithComputedField-Output": {
407 "properties": {
408 "name": {"type": "string", "title": "Name"},
409 "computed_field": {
410 "type": "string",
411 "title": "Computed Field",
412 "readOnly": True,
413 },
414 },
415 "type": "object",
416 "required": ["name", "computed_field"],
417 "title": "WithComputedField",
418 },
419 "ValidationError": {
420 "properties": {
421 "ctx": {"title": "Context", "type": "object"},
422 "input": {"title": "Input"},
423 "loc": {
424 "items": {
425 "anyOf": [{"type": "string"}, {"type": "integer"}]
426 },
427 "type": "array",
428 "title": "Location",
429 },
430 "msg": {"type": "string", "title": "Message"},
431 "type": {"type": "string", "title": "Error Type"},
432 },
433 "type": "object",
434 "required": ["loc", "msg", "type"],
435 "title": "ValidationError",
436 },
437 }
438 },
439 }
440 )
443def test_openapi_schema_no_separate(): 1abcd
444 client = get_app_client(separate_input_output_schemas=False) 1uwy
445 response = client.get("/openapi.json") 1uwy
446 assert response.status_code == 200, response.text 1uwy
447 assert response.json() == snapshot( 1uwy
448 {
449 "openapi": "3.1.0",
450 "info": {"title": "FastAPI", "version": "0.1.0"},
451 "paths": {
452 "/items/": {
453 "get": {
454 "summary": "Read Items",
455 "operationId": "read_items_items__get",
456 "responses": {
457 "200": {
458 "description": "Successful Response",
459 "content": {
460 "application/json": {
461 "schema": {
462 "items": {
463 "$ref": "#/components/schemas/Item"
464 },
465 "type": "array",
466 "title": "Response Read Items Items Get",
467 }
468 }
469 },
470 }
471 },
472 },
473 "post": {
474 "summary": "Create Item",
475 "operationId": "create_item_items__post",
476 "requestBody": {
477 "content": {
478 "application/json": {
479 "schema": {"$ref": "#/components/schemas/Item"}
480 }
481 },
482 "required": True,
483 },
484 "responses": {
485 "200": {
486 "description": "Successful Response",
487 "content": {
488 "application/json": {
489 "schema": {"$ref": "#/components/schemas/Item"}
490 }
491 },
492 },
493 "402": {
494 "description": "Payment Required",
495 "content": {
496 "application/json": {
497 "schema": {"$ref": "#/components/schemas/Item"}
498 }
499 },
500 },
501 "422": {
502 "description": "Validation Error",
503 "content": {
504 "application/json": {
505 "schema": {
506 "$ref": "#/components/schemas/HTTPValidationError"
507 }
508 }
509 },
510 },
511 },
512 },
513 },
514 "/items-list/": {
515 "post": {
516 "summary": "Create Item List",
517 "operationId": "create_item_list_items_list__post",
518 "requestBody": {
519 "content": {
520 "application/json": {
521 "schema": {
522 "items": {"$ref": "#/components/schemas/Item"},
523 "type": "array",
524 "title": "Item",
525 }
526 }
527 },
528 "required": True,
529 },
530 "responses": {
531 "200": {
532 "description": "Successful Response",
533 "content": {"application/json": {"schema": {}}},
534 },
535 "422": {
536 "description": "Validation Error",
537 "content": {
538 "application/json": {
539 "schema": {
540 "$ref": "#/components/schemas/HTTPValidationError"
541 }
542 }
543 },
544 },
545 },
546 }
547 },
548 "/with-computed-field/": {
549 "post": {
550 "summary": "Create With Computed Field",
551 "operationId": "create_with_computed_field_with_computed_field__post",
552 "requestBody": {
553 "content": {
554 "application/json": {
555 "schema": {
556 "$ref": "#/components/schemas/WithComputedField-Input"
557 }
558 }
559 },
560 "required": True,
561 },
562 "responses": {
563 "200": {
564 "description": "Successful Response",
565 "content": {
566 "application/json": {
567 "schema": {
568 "$ref": "#/components/schemas/WithComputedField-Output"
569 }
570 }
571 },
572 },
573 "422": {
574 "description": "Validation Error",
575 "content": {
576 "application/json": {
577 "schema": {
578 "$ref": "#/components/schemas/HTTPValidationError"
579 }
580 }
581 },
582 },
583 },
584 },
585 },
586 },
587 "components": {
588 "schemas": {
589 "HTTPValidationError": {
590 "properties": {
591 "detail": {
592 "items": {
593 "$ref": "#/components/schemas/ValidationError"
594 },
595 "type": "array",
596 "title": "Detail",
597 }
598 },
599 "type": "object",
600 "title": "HTTPValidationError",
601 },
602 "Item": {
603 "properties": {
604 "name": {"type": "string", "title": "Name"},
605 "description": {
606 "anyOf": [{"type": "string"}, {"type": "null"}],
607 "title": "Description",
608 },
609 "sub": {
610 "anyOf": [
611 {"$ref": "#/components/schemas/SubItem"},
612 {"type": "null"},
613 ]
614 },
615 },
616 "type": "object",
617 "required": ["name"],
618 "title": "Item",
619 },
620 "SubItem": {
621 "properties": {
622 "subname": {"type": "string", "title": "Subname"},
623 "sub_description": {
624 "anyOf": [{"type": "string"}, {"type": "null"}],
625 "title": "Sub Description",
626 },
627 "tags": {
628 "items": {"type": "string"},
629 "type": "array",
630 "title": "Tags",
631 "default": [],
632 },
633 },
634 "type": "object",
635 "required": ["subname"],
636 "title": "SubItem",
637 },
638 "WithComputedField-Input": {
639 "properties": {"name": {"type": "string", "title": "Name"}},
640 "type": "object",
641 "required": ["name"],
642 "title": "WithComputedField",
643 },
644 "WithComputedField-Output": {
645 "properties": {
646 "name": {"type": "string", "title": "Name"},
647 "computed_field": {
648 "type": "string",
649 "title": "Computed Field",
650 "readOnly": True,
651 },
652 },
653 "type": "object",
654 "required": ["name", "computed_field"],
655 "title": "WithComputedField",
656 },
657 "ValidationError": {
658 "properties": {
659 "ctx": {"title": "Context", "type": "object"},
660 "input": {"title": "Input"},
661 "loc": {
662 "items": {
663 "anyOf": [{"type": "string"}, {"type": "integer"}]
664 },
665 "type": "array",
666 "title": "Location",
667 },
668 "msg": {"type": "string", "title": "Message"},
669 "type": {"type": "string", "title": "Error Type"},
670 },
671 "type": "object",
672 "required": ["loc", "msg", "type"],
673 "title": "ValidationError",
674 },
675 }
676 },
677 }
678 )