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