Coverage for tests/test_generate_unique_id_function.py: 100%
125 statements
« prev ^ index » next coverage.py v7.6.1, created at 2024-08-08 03:53 +0000
« prev ^ index » next coverage.py v7.6.1, created at 2024-08-08 03:53 +0000
1import warnings 1abcde
2from typing import List 1abcde
4from fastapi import APIRouter, FastAPI 1abcde
5from fastapi.routing import APIRoute 1abcde
6from fastapi.testclient import TestClient 1abcde
7from pydantic import BaseModel 1abcde
10def custom_generate_unique_id(route: APIRoute): 1abcde
11 return f"foo_{route.name}" 1abcde
14def custom_generate_unique_id2(route: APIRoute): 1abcde
15 return f"bar_{route.name}" 1abcde
18def custom_generate_unique_id3(route: APIRoute): 1abcde
19 return f"baz_{route.name}" 1abcde
22class Item(BaseModel): 1abcde
23 name: str 1abcde
24 price: float 1abcde
27class Message(BaseModel): 1abcde
28 title: str 1abcde
29 description: str 1abcde
32def test_top_level_generate_unique_id(): 1abcde
33 app = FastAPI(generate_unique_id_function=custom_generate_unique_id) 1abcde
34 router = APIRouter() 1abcde
36 @app.post("/", response_model=List[Item], responses={404: {"model": List[Message]}}) 1abcde
37 def post_root(item1: Item, item2: Item): 1abcde
38 return item1, item2 # pragma: nocover
40 @router.post( 1abcde
41 "/router", response_model=List[Item], responses={404: {"model": List[Message]}}
42 )
43 def post_router(item1: Item, item2: Item): 1abcde
44 return item1, item2 # pragma: nocover
46 app.include_router(router) 1abcde
47 client = TestClient(app) 1abcde
48 response = client.get("/openapi.json") 1abcde
49 data = response.json() 1abcde
50 assert data == { 1abcde
51 "openapi": "3.1.0",
52 "info": {"title": "FastAPI", "version": "0.1.0"},
53 "paths": {
54 "/": {
55 "post": {
56 "summary": "Post Root",
57 "operationId": "foo_post_root",
58 "requestBody": {
59 "content": {
60 "application/json": {
61 "schema": {
62 "$ref": "#/components/schemas/Body_foo_post_root"
63 }
64 }
65 },
66 "required": True,
67 },
68 "responses": {
69 "200": {
70 "description": "Successful Response",
71 "content": {
72 "application/json": {
73 "schema": {
74 "title": "Response Foo Post Root",
75 "type": "array",
76 "items": {"$ref": "#/components/schemas/Item"},
77 }
78 }
79 },
80 },
81 "404": {
82 "description": "Not Found",
83 "content": {
84 "application/json": {
85 "schema": {
86 "title": "Response 404 Foo Post Root",
87 "type": "array",
88 "items": {
89 "$ref": "#/components/schemas/Message"
90 },
91 }
92 }
93 },
94 },
95 "422": {
96 "description": "Validation Error",
97 "content": {
98 "application/json": {
99 "schema": {
100 "$ref": "#/components/schemas/HTTPValidationError"
101 }
102 }
103 },
104 },
105 },
106 }
107 },
108 "/router": {
109 "post": {
110 "summary": "Post Router",
111 "operationId": "foo_post_router",
112 "requestBody": {
113 "content": {
114 "application/json": {
115 "schema": {
116 "$ref": "#/components/schemas/Body_foo_post_router"
117 }
118 }
119 },
120 "required": True,
121 },
122 "responses": {
123 "200": {
124 "description": "Successful Response",
125 "content": {
126 "application/json": {
127 "schema": {
128 "title": "Response Foo Post Router",
129 "type": "array",
130 "items": {"$ref": "#/components/schemas/Item"},
131 }
132 }
133 },
134 },
135 "404": {
136 "description": "Not Found",
137 "content": {
138 "application/json": {
139 "schema": {
140 "title": "Response 404 Foo Post Router",
141 "type": "array",
142 "items": {
143 "$ref": "#/components/schemas/Message"
144 },
145 }
146 }
147 },
148 },
149 "422": {
150 "description": "Validation Error",
151 "content": {
152 "application/json": {
153 "schema": {
154 "$ref": "#/components/schemas/HTTPValidationError"
155 }
156 }
157 },
158 },
159 },
160 }
161 },
162 },
163 "components": {
164 "schemas": {
165 "Body_foo_post_root": {
166 "title": "Body_foo_post_root",
167 "required": ["item1", "item2"],
168 "type": "object",
169 "properties": {
170 "item1": {"$ref": "#/components/schemas/Item"},
171 "item2": {"$ref": "#/components/schemas/Item"},
172 },
173 },
174 "Body_foo_post_router": {
175 "title": "Body_foo_post_router",
176 "required": ["item1", "item2"],
177 "type": "object",
178 "properties": {
179 "item1": {"$ref": "#/components/schemas/Item"},
180 "item2": {"$ref": "#/components/schemas/Item"},
181 },
182 },
183 "HTTPValidationError": {
184 "title": "HTTPValidationError",
185 "type": "object",
186 "properties": {
187 "detail": {
188 "title": "Detail",
189 "type": "array",
190 "items": {"$ref": "#/components/schemas/ValidationError"},
191 }
192 },
193 },
194 "Item": {
195 "title": "Item",
196 "required": ["name", "price"],
197 "type": "object",
198 "properties": {
199 "name": {"title": "Name", "type": "string"},
200 "price": {"title": "Price", "type": "number"},
201 },
202 },
203 "Message": {
204 "title": "Message",
205 "required": ["title", "description"],
206 "type": "object",
207 "properties": {
208 "title": {"title": "Title", "type": "string"},
209 "description": {"title": "Description", "type": "string"},
210 },
211 },
212 "ValidationError": {
213 "title": "ValidationError",
214 "required": ["loc", "msg", "type"],
215 "type": "object",
216 "properties": {
217 "loc": {
218 "title": "Location",
219 "type": "array",
220 "items": {
221 "anyOf": [{"type": "string"}, {"type": "integer"}]
222 },
223 },
224 "msg": {"title": "Message", "type": "string"},
225 "type": {"title": "Error Type", "type": "string"},
226 },
227 },
228 }
229 },
230 }
233def test_router_overrides_generate_unique_id(): 1abcde
234 app = FastAPI(generate_unique_id_function=custom_generate_unique_id) 1abcde
235 router = APIRouter(generate_unique_id_function=custom_generate_unique_id2) 1abcde
237 @app.post("/", response_model=List[Item], responses={404: {"model": List[Message]}}) 1abcde
238 def post_root(item1: Item, item2: Item): 1abcde
239 return item1, item2 # pragma: nocover
241 @router.post( 1abcde
242 "/router", response_model=List[Item], responses={404: {"model": List[Message]}}
243 )
244 def post_router(item1: Item, item2: Item): 1abcde
245 return item1, item2 # pragma: nocover
247 app.include_router(router) 1abcde
248 client = TestClient(app) 1abcde
249 response = client.get("/openapi.json") 1abcde
250 data = response.json() 1abcde
251 assert data == { 1abcde
252 "openapi": "3.1.0",
253 "info": {"title": "FastAPI", "version": "0.1.0"},
254 "paths": {
255 "/": {
256 "post": {
257 "summary": "Post Root",
258 "operationId": "foo_post_root",
259 "requestBody": {
260 "content": {
261 "application/json": {
262 "schema": {
263 "$ref": "#/components/schemas/Body_foo_post_root"
264 }
265 }
266 },
267 "required": True,
268 },
269 "responses": {
270 "200": {
271 "description": "Successful Response",
272 "content": {
273 "application/json": {
274 "schema": {
275 "title": "Response Foo Post Root",
276 "type": "array",
277 "items": {"$ref": "#/components/schemas/Item"},
278 }
279 }
280 },
281 },
282 "404": {
283 "description": "Not Found",
284 "content": {
285 "application/json": {
286 "schema": {
287 "title": "Response 404 Foo Post Root",
288 "type": "array",
289 "items": {
290 "$ref": "#/components/schemas/Message"
291 },
292 }
293 }
294 },
295 },
296 "422": {
297 "description": "Validation Error",
298 "content": {
299 "application/json": {
300 "schema": {
301 "$ref": "#/components/schemas/HTTPValidationError"
302 }
303 }
304 },
305 },
306 },
307 }
308 },
309 "/router": {
310 "post": {
311 "summary": "Post Router",
312 "operationId": "bar_post_router",
313 "requestBody": {
314 "content": {
315 "application/json": {
316 "schema": {
317 "$ref": "#/components/schemas/Body_bar_post_router"
318 }
319 }
320 },
321 "required": True,
322 },
323 "responses": {
324 "200": {
325 "description": "Successful Response",
326 "content": {
327 "application/json": {
328 "schema": {
329 "title": "Response Bar Post Router",
330 "type": "array",
331 "items": {"$ref": "#/components/schemas/Item"},
332 }
333 }
334 },
335 },
336 "404": {
337 "description": "Not Found",
338 "content": {
339 "application/json": {
340 "schema": {
341 "title": "Response 404 Bar Post Router",
342 "type": "array",
343 "items": {
344 "$ref": "#/components/schemas/Message"
345 },
346 }
347 }
348 },
349 },
350 "422": {
351 "description": "Validation Error",
352 "content": {
353 "application/json": {
354 "schema": {
355 "$ref": "#/components/schemas/HTTPValidationError"
356 }
357 }
358 },
359 },
360 },
361 }
362 },
363 },
364 "components": {
365 "schemas": {
366 "Body_bar_post_router": {
367 "title": "Body_bar_post_router",
368 "required": ["item1", "item2"],
369 "type": "object",
370 "properties": {
371 "item1": {"$ref": "#/components/schemas/Item"},
372 "item2": {"$ref": "#/components/schemas/Item"},
373 },
374 },
375 "Body_foo_post_root": {
376 "title": "Body_foo_post_root",
377 "required": ["item1", "item2"],
378 "type": "object",
379 "properties": {
380 "item1": {"$ref": "#/components/schemas/Item"},
381 "item2": {"$ref": "#/components/schemas/Item"},
382 },
383 },
384 "HTTPValidationError": {
385 "title": "HTTPValidationError",
386 "type": "object",
387 "properties": {
388 "detail": {
389 "title": "Detail",
390 "type": "array",
391 "items": {"$ref": "#/components/schemas/ValidationError"},
392 }
393 },
394 },
395 "Item": {
396 "title": "Item",
397 "required": ["name", "price"],
398 "type": "object",
399 "properties": {
400 "name": {"title": "Name", "type": "string"},
401 "price": {"title": "Price", "type": "number"},
402 },
403 },
404 "Message": {
405 "title": "Message",
406 "required": ["title", "description"],
407 "type": "object",
408 "properties": {
409 "title": {"title": "Title", "type": "string"},
410 "description": {"title": "Description", "type": "string"},
411 },
412 },
413 "ValidationError": {
414 "title": "ValidationError",
415 "required": ["loc", "msg", "type"],
416 "type": "object",
417 "properties": {
418 "loc": {
419 "title": "Location",
420 "type": "array",
421 "items": {
422 "anyOf": [{"type": "string"}, {"type": "integer"}]
423 },
424 },
425 "msg": {"title": "Message", "type": "string"},
426 "type": {"title": "Error Type", "type": "string"},
427 },
428 },
429 }
430 },
431 }
434def test_router_include_overrides_generate_unique_id(): 1abcde
435 app = FastAPI(generate_unique_id_function=custom_generate_unique_id) 1abcde
436 router = APIRouter(generate_unique_id_function=custom_generate_unique_id2) 1abcde
438 @app.post("/", response_model=List[Item], responses={404: {"model": List[Message]}}) 1abcde
439 def post_root(item1: Item, item2: Item): 1abcde
440 return item1, item2 # pragma: nocover
442 @router.post( 1abcde
443 "/router", response_model=List[Item], responses={404: {"model": List[Message]}}
444 )
445 def post_router(item1: Item, item2: Item): 1abcde
446 return item1, item2 # pragma: nocover
448 app.include_router(router, generate_unique_id_function=custom_generate_unique_id3) 1abcde
449 client = TestClient(app) 1abcde
450 response = client.get("/openapi.json") 1abcde
451 data = response.json() 1abcde
452 assert data == { 1abcde
453 "openapi": "3.1.0",
454 "info": {"title": "FastAPI", "version": "0.1.0"},
455 "paths": {
456 "/": {
457 "post": {
458 "summary": "Post Root",
459 "operationId": "foo_post_root",
460 "requestBody": {
461 "content": {
462 "application/json": {
463 "schema": {
464 "$ref": "#/components/schemas/Body_foo_post_root"
465 }
466 }
467 },
468 "required": True,
469 },
470 "responses": {
471 "200": {
472 "description": "Successful Response",
473 "content": {
474 "application/json": {
475 "schema": {
476 "title": "Response Foo Post Root",
477 "type": "array",
478 "items": {"$ref": "#/components/schemas/Item"},
479 }
480 }
481 },
482 },
483 "404": {
484 "description": "Not Found",
485 "content": {
486 "application/json": {
487 "schema": {
488 "title": "Response 404 Foo Post Root",
489 "type": "array",
490 "items": {
491 "$ref": "#/components/schemas/Message"
492 },
493 }
494 }
495 },
496 },
497 "422": {
498 "description": "Validation Error",
499 "content": {
500 "application/json": {
501 "schema": {
502 "$ref": "#/components/schemas/HTTPValidationError"
503 }
504 }
505 },
506 },
507 },
508 }
509 },
510 "/router": {
511 "post": {
512 "summary": "Post Router",
513 "operationId": "bar_post_router",
514 "requestBody": {
515 "content": {
516 "application/json": {
517 "schema": {
518 "$ref": "#/components/schemas/Body_bar_post_router"
519 }
520 }
521 },
522 "required": True,
523 },
524 "responses": {
525 "200": {
526 "description": "Successful Response",
527 "content": {
528 "application/json": {
529 "schema": {
530 "title": "Response Bar Post Router",
531 "type": "array",
532 "items": {"$ref": "#/components/schemas/Item"},
533 }
534 }
535 },
536 },
537 "404": {
538 "description": "Not Found",
539 "content": {
540 "application/json": {
541 "schema": {
542 "title": "Response 404 Bar Post Router",
543 "type": "array",
544 "items": {
545 "$ref": "#/components/schemas/Message"
546 },
547 }
548 }
549 },
550 },
551 "422": {
552 "description": "Validation Error",
553 "content": {
554 "application/json": {
555 "schema": {
556 "$ref": "#/components/schemas/HTTPValidationError"
557 }
558 }
559 },
560 },
561 },
562 }
563 },
564 },
565 "components": {
566 "schemas": {
567 "Body_bar_post_router": {
568 "title": "Body_bar_post_router",
569 "required": ["item1", "item2"],
570 "type": "object",
571 "properties": {
572 "item1": {"$ref": "#/components/schemas/Item"},
573 "item2": {"$ref": "#/components/schemas/Item"},
574 },
575 },
576 "Body_foo_post_root": {
577 "title": "Body_foo_post_root",
578 "required": ["item1", "item2"],
579 "type": "object",
580 "properties": {
581 "item1": {"$ref": "#/components/schemas/Item"},
582 "item2": {"$ref": "#/components/schemas/Item"},
583 },
584 },
585 "HTTPValidationError": {
586 "title": "HTTPValidationError",
587 "type": "object",
588 "properties": {
589 "detail": {
590 "title": "Detail",
591 "type": "array",
592 "items": {"$ref": "#/components/schemas/ValidationError"},
593 }
594 },
595 },
596 "Item": {
597 "title": "Item",
598 "required": ["name", "price"],
599 "type": "object",
600 "properties": {
601 "name": {"title": "Name", "type": "string"},
602 "price": {"title": "Price", "type": "number"},
603 },
604 },
605 "Message": {
606 "title": "Message",
607 "required": ["title", "description"],
608 "type": "object",
609 "properties": {
610 "title": {"title": "Title", "type": "string"},
611 "description": {"title": "Description", "type": "string"},
612 },
613 },
614 "ValidationError": {
615 "title": "ValidationError",
616 "required": ["loc", "msg", "type"],
617 "type": "object",
618 "properties": {
619 "loc": {
620 "title": "Location",
621 "type": "array",
622 "items": {
623 "anyOf": [{"type": "string"}, {"type": "integer"}]
624 },
625 },
626 "msg": {"title": "Message", "type": "string"},
627 "type": {"title": "Error Type", "type": "string"},
628 },
629 },
630 }
631 },
632 }
635def test_subrouter_top_level_include_overrides_generate_unique_id(): 1abcde
636 app = FastAPI(generate_unique_id_function=custom_generate_unique_id) 1abcde
637 router = APIRouter() 1abcde
638 sub_router = APIRouter(generate_unique_id_function=custom_generate_unique_id2) 1abcde
640 @app.post("/", response_model=List[Item], responses={404: {"model": List[Message]}}) 1abcde
641 def post_root(item1: Item, item2: Item): 1abcde
642 return item1, item2 # pragma: nocover
644 @router.post( 1abcde
645 "/router", response_model=List[Item], responses={404: {"model": List[Message]}}
646 )
647 def post_router(item1: Item, item2: Item): 1abcde
648 return item1, item2 # pragma: nocover
650 @sub_router.post( 1abcde
651 "/subrouter",
652 response_model=List[Item],
653 responses={404: {"model": List[Message]}},
654 )
655 def post_subrouter(item1: Item, item2: Item): 1abcde
656 return item1, item2 # pragma: nocover
658 router.include_router(sub_router) 1abcde
659 app.include_router(router, generate_unique_id_function=custom_generate_unique_id3) 1abcde
660 client = TestClient(app) 1abcde
661 response = client.get("/openapi.json") 1abcde
662 data = response.json() 1abcde
663 assert data == { 1abcde
664 "openapi": "3.1.0",
665 "info": {"title": "FastAPI", "version": "0.1.0"},
666 "paths": {
667 "/": {
668 "post": {
669 "summary": "Post Root",
670 "operationId": "foo_post_root",
671 "requestBody": {
672 "content": {
673 "application/json": {
674 "schema": {
675 "$ref": "#/components/schemas/Body_foo_post_root"
676 }
677 }
678 },
679 "required": True,
680 },
681 "responses": {
682 "200": {
683 "description": "Successful Response",
684 "content": {
685 "application/json": {
686 "schema": {
687 "title": "Response Foo Post Root",
688 "type": "array",
689 "items": {"$ref": "#/components/schemas/Item"},
690 }
691 }
692 },
693 },
694 "404": {
695 "description": "Not Found",
696 "content": {
697 "application/json": {
698 "schema": {
699 "title": "Response 404 Foo Post Root",
700 "type": "array",
701 "items": {
702 "$ref": "#/components/schemas/Message"
703 },
704 }
705 }
706 },
707 },
708 "422": {
709 "description": "Validation Error",
710 "content": {
711 "application/json": {
712 "schema": {
713 "$ref": "#/components/schemas/HTTPValidationError"
714 }
715 }
716 },
717 },
718 },
719 }
720 },
721 "/router": {
722 "post": {
723 "summary": "Post Router",
724 "operationId": "baz_post_router",
725 "requestBody": {
726 "content": {
727 "application/json": {
728 "schema": {
729 "$ref": "#/components/schemas/Body_baz_post_router"
730 }
731 }
732 },
733 "required": True,
734 },
735 "responses": {
736 "200": {
737 "description": "Successful Response",
738 "content": {
739 "application/json": {
740 "schema": {
741 "title": "Response Baz Post Router",
742 "type": "array",
743 "items": {"$ref": "#/components/schemas/Item"},
744 }
745 }
746 },
747 },
748 "404": {
749 "description": "Not Found",
750 "content": {
751 "application/json": {
752 "schema": {
753 "title": "Response 404 Baz Post Router",
754 "type": "array",
755 "items": {
756 "$ref": "#/components/schemas/Message"
757 },
758 }
759 }
760 },
761 },
762 "422": {
763 "description": "Validation Error",
764 "content": {
765 "application/json": {
766 "schema": {
767 "$ref": "#/components/schemas/HTTPValidationError"
768 }
769 }
770 },
771 },
772 },
773 }
774 },
775 "/subrouter": {
776 "post": {
777 "summary": "Post Subrouter",
778 "operationId": "bar_post_subrouter",
779 "requestBody": {
780 "content": {
781 "application/json": {
782 "schema": {
783 "$ref": "#/components/schemas/Body_bar_post_subrouter"
784 }
785 }
786 },
787 "required": True,
788 },
789 "responses": {
790 "200": {
791 "description": "Successful Response",
792 "content": {
793 "application/json": {
794 "schema": {
795 "title": "Response Bar Post Subrouter",
796 "type": "array",
797 "items": {"$ref": "#/components/schemas/Item"},
798 }
799 }
800 },
801 },
802 "404": {
803 "description": "Not Found",
804 "content": {
805 "application/json": {
806 "schema": {
807 "title": "Response 404 Bar Post Subrouter",
808 "type": "array",
809 "items": {
810 "$ref": "#/components/schemas/Message"
811 },
812 }
813 }
814 },
815 },
816 "422": {
817 "description": "Validation Error",
818 "content": {
819 "application/json": {
820 "schema": {
821 "$ref": "#/components/schemas/HTTPValidationError"
822 }
823 }
824 },
825 },
826 },
827 }
828 },
829 },
830 "components": {
831 "schemas": {
832 "Body_bar_post_subrouter": {
833 "title": "Body_bar_post_subrouter",
834 "required": ["item1", "item2"],
835 "type": "object",
836 "properties": {
837 "item1": {"$ref": "#/components/schemas/Item"},
838 "item2": {"$ref": "#/components/schemas/Item"},
839 },
840 },
841 "Body_baz_post_router": {
842 "title": "Body_baz_post_router",
843 "required": ["item1", "item2"],
844 "type": "object",
845 "properties": {
846 "item1": {"$ref": "#/components/schemas/Item"},
847 "item2": {"$ref": "#/components/schemas/Item"},
848 },
849 },
850 "Body_foo_post_root": {
851 "title": "Body_foo_post_root",
852 "required": ["item1", "item2"],
853 "type": "object",
854 "properties": {
855 "item1": {"$ref": "#/components/schemas/Item"},
856 "item2": {"$ref": "#/components/schemas/Item"},
857 },
858 },
859 "HTTPValidationError": {
860 "title": "HTTPValidationError",
861 "type": "object",
862 "properties": {
863 "detail": {
864 "title": "Detail",
865 "type": "array",
866 "items": {"$ref": "#/components/schemas/ValidationError"},
867 }
868 },
869 },
870 "Item": {
871 "title": "Item",
872 "required": ["name", "price"],
873 "type": "object",
874 "properties": {
875 "name": {"title": "Name", "type": "string"},
876 "price": {"title": "Price", "type": "number"},
877 },
878 },
879 "Message": {
880 "title": "Message",
881 "required": ["title", "description"],
882 "type": "object",
883 "properties": {
884 "title": {"title": "Title", "type": "string"},
885 "description": {"title": "Description", "type": "string"},
886 },
887 },
888 "ValidationError": {
889 "title": "ValidationError",
890 "required": ["loc", "msg", "type"],
891 "type": "object",
892 "properties": {
893 "loc": {
894 "title": "Location",
895 "type": "array",
896 "items": {
897 "anyOf": [{"type": "string"}, {"type": "integer"}]
898 },
899 },
900 "msg": {"title": "Message", "type": "string"},
901 "type": {"title": "Error Type", "type": "string"},
902 },
903 },
904 }
905 },
906 }
909def test_router_path_operation_overrides_generate_unique_id(): 1abcde
910 app = FastAPI(generate_unique_id_function=custom_generate_unique_id) 1abcde
911 router = APIRouter(generate_unique_id_function=custom_generate_unique_id2) 1abcde
913 @app.post("/", response_model=List[Item], responses={404: {"model": List[Message]}}) 1abcde
914 def post_root(item1: Item, item2: Item): 1abcde
915 return item1, item2 # pragma: nocover
917 @router.post( 1abcde
918 "/router",
919 response_model=List[Item],
920 responses={404: {"model": List[Message]}},
921 generate_unique_id_function=custom_generate_unique_id3,
922 )
923 def post_router(item1: Item, item2: Item): 1abcde
924 return item1, item2 # pragma: nocover
926 app.include_router(router) 1abcde
927 client = TestClient(app) 1abcde
928 response = client.get("/openapi.json") 1abcde
929 data = response.json() 1abcde
930 assert data == { 1abcde
931 "openapi": "3.1.0",
932 "info": {"title": "FastAPI", "version": "0.1.0"},
933 "paths": {
934 "/": {
935 "post": {
936 "summary": "Post Root",
937 "operationId": "foo_post_root",
938 "requestBody": {
939 "content": {
940 "application/json": {
941 "schema": {
942 "$ref": "#/components/schemas/Body_foo_post_root"
943 }
944 }
945 },
946 "required": True,
947 },
948 "responses": {
949 "200": {
950 "description": "Successful Response",
951 "content": {
952 "application/json": {
953 "schema": {
954 "title": "Response Foo Post Root",
955 "type": "array",
956 "items": {"$ref": "#/components/schemas/Item"},
957 }
958 }
959 },
960 },
961 "404": {
962 "description": "Not Found",
963 "content": {
964 "application/json": {
965 "schema": {
966 "title": "Response 404 Foo Post Root",
967 "type": "array",
968 "items": {
969 "$ref": "#/components/schemas/Message"
970 },
971 }
972 }
973 },
974 },
975 "422": {
976 "description": "Validation Error",
977 "content": {
978 "application/json": {
979 "schema": {
980 "$ref": "#/components/schemas/HTTPValidationError"
981 }
982 }
983 },
984 },
985 },
986 }
987 },
988 "/router": {
989 "post": {
990 "summary": "Post Router",
991 "operationId": "baz_post_router",
992 "requestBody": {
993 "content": {
994 "application/json": {
995 "schema": {
996 "$ref": "#/components/schemas/Body_baz_post_router"
997 }
998 }
999 },
1000 "required": True,
1001 },
1002 "responses": {
1003 "200": {
1004 "description": "Successful Response",
1005 "content": {
1006 "application/json": {
1007 "schema": {
1008 "title": "Response Baz Post Router",
1009 "type": "array",
1010 "items": {"$ref": "#/components/schemas/Item"},
1011 }
1012 }
1013 },
1014 },
1015 "404": {
1016 "description": "Not Found",
1017 "content": {
1018 "application/json": {
1019 "schema": {
1020 "title": "Response 404 Baz Post Router",
1021 "type": "array",
1022 "items": {
1023 "$ref": "#/components/schemas/Message"
1024 },
1025 }
1026 }
1027 },
1028 },
1029 "422": {
1030 "description": "Validation Error",
1031 "content": {
1032 "application/json": {
1033 "schema": {
1034 "$ref": "#/components/schemas/HTTPValidationError"
1035 }
1036 }
1037 },
1038 },
1039 },
1040 }
1041 },
1042 },
1043 "components": {
1044 "schemas": {
1045 "Body_baz_post_router": {
1046 "title": "Body_baz_post_router",
1047 "required": ["item1", "item2"],
1048 "type": "object",
1049 "properties": {
1050 "item1": {"$ref": "#/components/schemas/Item"},
1051 "item2": {"$ref": "#/components/schemas/Item"},
1052 },
1053 },
1054 "Body_foo_post_root": {
1055 "title": "Body_foo_post_root",
1056 "required": ["item1", "item2"],
1057 "type": "object",
1058 "properties": {
1059 "item1": {"$ref": "#/components/schemas/Item"},
1060 "item2": {"$ref": "#/components/schemas/Item"},
1061 },
1062 },
1063 "HTTPValidationError": {
1064 "title": "HTTPValidationError",
1065 "type": "object",
1066 "properties": {
1067 "detail": {
1068 "title": "Detail",
1069 "type": "array",
1070 "items": {"$ref": "#/components/schemas/ValidationError"},
1071 }
1072 },
1073 },
1074 "Item": {
1075 "title": "Item",
1076 "required": ["name", "price"],
1077 "type": "object",
1078 "properties": {
1079 "name": {"title": "Name", "type": "string"},
1080 "price": {"title": "Price", "type": "number"},
1081 },
1082 },
1083 "Message": {
1084 "title": "Message",
1085 "required": ["title", "description"],
1086 "type": "object",
1087 "properties": {
1088 "title": {"title": "Title", "type": "string"},
1089 "description": {"title": "Description", "type": "string"},
1090 },
1091 },
1092 "ValidationError": {
1093 "title": "ValidationError",
1094 "required": ["loc", "msg", "type"],
1095 "type": "object",
1096 "properties": {
1097 "loc": {
1098 "title": "Location",
1099 "type": "array",
1100 "items": {
1101 "anyOf": [{"type": "string"}, {"type": "integer"}]
1102 },
1103 },
1104 "msg": {"title": "Message", "type": "string"},
1105 "type": {"title": "Error Type", "type": "string"},
1106 },
1107 },
1108 }
1109 },
1110 }
1113def test_app_path_operation_overrides_generate_unique_id(): 1abcde
1114 app = FastAPI(generate_unique_id_function=custom_generate_unique_id) 1abcde
1115 router = APIRouter(generate_unique_id_function=custom_generate_unique_id2) 1abcde
1117 @app.post( 1abcde
1118 "/",
1119 response_model=List[Item],
1120 responses={404: {"model": List[Message]}},
1121 generate_unique_id_function=custom_generate_unique_id3,
1122 )
1123 def post_root(item1: Item, item2: Item): 1abcde
1124 return item1, item2 # pragma: nocover
1126 @router.post( 1abcde
1127 "/router",
1128 response_model=List[Item],
1129 responses={404: {"model": List[Message]}},
1130 )
1131 def post_router(item1: Item, item2: Item): 1abcde
1132 return item1, item2 # pragma: nocover
1134 app.include_router(router) 1abcde
1135 client = TestClient(app) 1abcde
1136 response = client.get("/openapi.json") 1abcde
1137 data = response.json() 1abcde
1138 assert data == { 1abcde
1139 "openapi": "3.1.0",
1140 "info": {"title": "FastAPI", "version": "0.1.0"},
1141 "paths": {
1142 "/": {
1143 "post": {
1144 "summary": "Post Root",
1145 "operationId": "baz_post_root",
1146 "requestBody": {
1147 "content": {
1148 "application/json": {
1149 "schema": {
1150 "$ref": "#/components/schemas/Body_baz_post_root"
1151 }
1152 }
1153 },
1154 "required": True,
1155 },
1156 "responses": {
1157 "200": {
1158 "description": "Successful Response",
1159 "content": {
1160 "application/json": {
1161 "schema": {
1162 "title": "Response Baz Post Root",
1163 "type": "array",
1164 "items": {"$ref": "#/components/schemas/Item"},
1165 }
1166 }
1167 },
1168 },
1169 "404": {
1170 "description": "Not Found",
1171 "content": {
1172 "application/json": {
1173 "schema": {
1174 "title": "Response 404 Baz Post Root",
1175 "type": "array",
1176 "items": {
1177 "$ref": "#/components/schemas/Message"
1178 },
1179 }
1180 }
1181 },
1182 },
1183 "422": {
1184 "description": "Validation Error",
1185 "content": {
1186 "application/json": {
1187 "schema": {
1188 "$ref": "#/components/schemas/HTTPValidationError"
1189 }
1190 }
1191 },
1192 },
1193 },
1194 }
1195 },
1196 "/router": {
1197 "post": {
1198 "summary": "Post Router",
1199 "operationId": "bar_post_router",
1200 "requestBody": {
1201 "content": {
1202 "application/json": {
1203 "schema": {
1204 "$ref": "#/components/schemas/Body_bar_post_router"
1205 }
1206 }
1207 },
1208 "required": True,
1209 },
1210 "responses": {
1211 "200": {
1212 "description": "Successful Response",
1213 "content": {
1214 "application/json": {
1215 "schema": {
1216 "title": "Response Bar Post Router",
1217 "type": "array",
1218 "items": {"$ref": "#/components/schemas/Item"},
1219 }
1220 }
1221 },
1222 },
1223 "404": {
1224 "description": "Not Found",
1225 "content": {
1226 "application/json": {
1227 "schema": {
1228 "title": "Response 404 Bar Post Router",
1229 "type": "array",
1230 "items": {
1231 "$ref": "#/components/schemas/Message"
1232 },
1233 }
1234 }
1235 },
1236 },
1237 "422": {
1238 "description": "Validation Error",
1239 "content": {
1240 "application/json": {
1241 "schema": {
1242 "$ref": "#/components/schemas/HTTPValidationError"
1243 }
1244 }
1245 },
1246 },
1247 },
1248 }
1249 },
1250 },
1251 "components": {
1252 "schemas": {
1253 "Body_bar_post_router": {
1254 "title": "Body_bar_post_router",
1255 "required": ["item1", "item2"],
1256 "type": "object",
1257 "properties": {
1258 "item1": {"$ref": "#/components/schemas/Item"},
1259 "item2": {"$ref": "#/components/schemas/Item"},
1260 },
1261 },
1262 "Body_baz_post_root": {
1263 "title": "Body_baz_post_root",
1264 "required": ["item1", "item2"],
1265 "type": "object",
1266 "properties": {
1267 "item1": {"$ref": "#/components/schemas/Item"},
1268 "item2": {"$ref": "#/components/schemas/Item"},
1269 },
1270 },
1271 "HTTPValidationError": {
1272 "title": "HTTPValidationError",
1273 "type": "object",
1274 "properties": {
1275 "detail": {
1276 "title": "Detail",
1277 "type": "array",
1278 "items": {"$ref": "#/components/schemas/ValidationError"},
1279 }
1280 },
1281 },
1282 "Item": {
1283 "title": "Item",
1284 "required": ["name", "price"],
1285 "type": "object",
1286 "properties": {
1287 "name": {"title": "Name", "type": "string"},
1288 "price": {"title": "Price", "type": "number"},
1289 },
1290 },
1291 "Message": {
1292 "title": "Message",
1293 "required": ["title", "description"],
1294 "type": "object",
1295 "properties": {
1296 "title": {"title": "Title", "type": "string"},
1297 "description": {"title": "Description", "type": "string"},
1298 },
1299 },
1300 "ValidationError": {
1301 "title": "ValidationError",
1302 "required": ["loc", "msg", "type"],
1303 "type": "object",
1304 "properties": {
1305 "loc": {
1306 "title": "Location",
1307 "type": "array",
1308 "items": {
1309 "anyOf": [{"type": "string"}, {"type": "integer"}]
1310 },
1311 },
1312 "msg": {"title": "Message", "type": "string"},
1313 "type": {"title": "Error Type", "type": "string"},
1314 },
1315 },
1316 }
1317 },
1318 }
1321def test_callback_override_generate_unique_id(): 1abcde
1322 app = FastAPI(generate_unique_id_function=custom_generate_unique_id) 1abcde
1323 callback_router = APIRouter(generate_unique_id_function=custom_generate_unique_id2) 1abcde
1325 @callback_router.post( 1abcde
1326 "/post-callback",
1327 response_model=List[Item],
1328 responses={404: {"model": List[Message]}},
1329 generate_unique_id_function=custom_generate_unique_id3,
1330 )
1331 def post_callback(item1: Item, item2: Item): 1abcde
1332 return item1, item2 # pragma: nocover
1334 @app.post( 1abcde
1335 "/",
1336 response_model=List[Item],
1337 responses={404: {"model": List[Message]}},
1338 generate_unique_id_function=custom_generate_unique_id3,
1339 callbacks=callback_router.routes,
1340 )
1341 def post_root(item1: Item, item2: Item): 1abcde
1342 return item1, item2 # pragma: nocover
1344 @app.post( 1abcde
1345 "/tocallback",
1346 response_model=List[Item],
1347 responses={404: {"model": List[Message]}},
1348 )
1349 def post_with_callback(item1: Item, item2: Item): 1abcde
1350 return item1, item2 # pragma: nocover
1352 client = TestClient(app) 1abcde
1353 response = client.get("/openapi.json") 1abcde
1354 data = response.json() 1abcde
1355 assert data == { 1abcde
1356 "openapi": "3.1.0",
1357 "info": {"title": "FastAPI", "version": "0.1.0"},
1358 "paths": {
1359 "/": {
1360 "post": {
1361 "summary": "Post Root",
1362 "operationId": "baz_post_root",
1363 "requestBody": {
1364 "content": {
1365 "application/json": {
1366 "schema": {
1367 "$ref": "#/components/schemas/Body_baz_post_root"
1368 }
1369 }
1370 },
1371 "required": True,
1372 },
1373 "responses": {
1374 "200": {
1375 "description": "Successful Response",
1376 "content": {
1377 "application/json": {
1378 "schema": {
1379 "title": "Response Baz Post Root",
1380 "type": "array",
1381 "items": {"$ref": "#/components/schemas/Item"},
1382 }
1383 }
1384 },
1385 },
1386 "404": {
1387 "description": "Not Found",
1388 "content": {
1389 "application/json": {
1390 "schema": {
1391 "title": "Response 404 Baz Post Root",
1392 "type": "array",
1393 "items": {
1394 "$ref": "#/components/schemas/Message"
1395 },
1396 }
1397 }
1398 },
1399 },
1400 "422": {
1401 "description": "Validation Error",
1402 "content": {
1403 "application/json": {
1404 "schema": {
1405 "$ref": "#/components/schemas/HTTPValidationError"
1406 }
1407 }
1408 },
1409 },
1410 },
1411 "callbacks": {
1412 "post_callback": {
1413 "/post-callback": {
1414 "post": {
1415 "summary": "Post Callback",
1416 "operationId": "baz_post_callback",
1417 "requestBody": {
1418 "content": {
1419 "application/json": {
1420 "schema": {
1421 "$ref": "#/components/schemas/Body_baz_post_callback"
1422 }
1423 }
1424 },
1425 "required": True,
1426 },
1427 "responses": {
1428 "200": {
1429 "description": "Successful Response",
1430 "content": {
1431 "application/json": {
1432 "schema": {
1433 "title": "Response Baz Post Callback",
1434 "type": "array",
1435 "items": {
1436 "$ref": "#/components/schemas/Item"
1437 },
1438 }
1439 }
1440 },
1441 },
1442 "404": {
1443 "description": "Not Found",
1444 "content": {
1445 "application/json": {
1446 "schema": {
1447 "title": "Response 404 Baz Post Callback",
1448 "type": "array",
1449 "items": {
1450 "$ref": "#/components/schemas/Message"
1451 },
1452 }
1453 }
1454 },
1455 },
1456 "422": {
1457 "description": "Validation Error",
1458 "content": {
1459 "application/json": {
1460 "schema": {
1461 "$ref": "#/components/schemas/HTTPValidationError"
1462 }
1463 }
1464 },
1465 },
1466 },
1467 }
1468 }
1469 }
1470 },
1471 }
1472 },
1473 "/tocallback": {
1474 "post": {
1475 "summary": "Post With Callback",
1476 "operationId": "foo_post_with_callback",
1477 "requestBody": {
1478 "content": {
1479 "application/json": {
1480 "schema": {
1481 "$ref": "#/components/schemas/Body_foo_post_with_callback"
1482 }
1483 }
1484 },
1485 "required": True,
1486 },
1487 "responses": {
1488 "200": {
1489 "description": "Successful Response",
1490 "content": {
1491 "application/json": {
1492 "schema": {
1493 "title": "Response Foo Post With Callback",
1494 "type": "array",
1495 "items": {"$ref": "#/components/schemas/Item"},
1496 }
1497 }
1498 },
1499 },
1500 "404": {
1501 "description": "Not Found",
1502 "content": {
1503 "application/json": {
1504 "schema": {
1505 "title": "Response 404 Foo Post With Callback",
1506 "type": "array",
1507 "items": {
1508 "$ref": "#/components/schemas/Message"
1509 },
1510 }
1511 }
1512 },
1513 },
1514 "422": {
1515 "description": "Validation Error",
1516 "content": {
1517 "application/json": {
1518 "schema": {
1519 "$ref": "#/components/schemas/HTTPValidationError"
1520 }
1521 }
1522 },
1523 },
1524 },
1525 }
1526 },
1527 },
1528 "components": {
1529 "schemas": {
1530 "Body_baz_post_callback": {
1531 "title": "Body_baz_post_callback",
1532 "required": ["item1", "item2"],
1533 "type": "object",
1534 "properties": {
1535 "item1": {"$ref": "#/components/schemas/Item"},
1536 "item2": {"$ref": "#/components/schemas/Item"},
1537 },
1538 },
1539 "Body_baz_post_root": {
1540 "title": "Body_baz_post_root",
1541 "required": ["item1", "item2"],
1542 "type": "object",
1543 "properties": {
1544 "item1": {"$ref": "#/components/schemas/Item"},
1545 "item2": {"$ref": "#/components/schemas/Item"},
1546 },
1547 },
1548 "Body_foo_post_with_callback": {
1549 "title": "Body_foo_post_with_callback",
1550 "required": ["item1", "item2"],
1551 "type": "object",
1552 "properties": {
1553 "item1": {"$ref": "#/components/schemas/Item"},
1554 "item2": {"$ref": "#/components/schemas/Item"},
1555 },
1556 },
1557 "HTTPValidationError": {
1558 "title": "HTTPValidationError",
1559 "type": "object",
1560 "properties": {
1561 "detail": {
1562 "title": "Detail",
1563 "type": "array",
1564 "items": {"$ref": "#/components/schemas/ValidationError"},
1565 }
1566 },
1567 },
1568 "Item": {
1569 "title": "Item",
1570 "required": ["name", "price"],
1571 "type": "object",
1572 "properties": {
1573 "name": {"title": "Name", "type": "string"},
1574 "price": {"title": "Price", "type": "number"},
1575 },
1576 },
1577 "Message": {
1578 "title": "Message",
1579 "required": ["title", "description"],
1580 "type": "object",
1581 "properties": {
1582 "title": {"title": "Title", "type": "string"},
1583 "description": {"title": "Description", "type": "string"},
1584 },
1585 },
1586 "ValidationError": {
1587 "title": "ValidationError",
1588 "required": ["loc", "msg", "type"],
1589 "type": "object",
1590 "properties": {
1591 "loc": {
1592 "title": "Location",
1593 "type": "array",
1594 "items": {
1595 "anyOf": [{"type": "string"}, {"type": "integer"}]
1596 },
1597 },
1598 "msg": {"title": "Message", "type": "string"},
1599 "type": {"title": "Error Type", "type": "string"},
1600 },
1601 },
1602 }
1603 },
1604 }
1607def test_warn_duplicate_operation_id(): 1abcde
1608 def broken_operation_id(route: APIRoute): 1abcde
1609 return "foo" 1abcde
1611 app = FastAPI(generate_unique_id_function=broken_operation_id) 1abcde
1613 @app.post("/") 1abcde
1614 def post_root(item1: Item): 1abcde
1615 return item1 # pragma: nocover
1617 @app.post("/second") 1abcde
1618 def post_second(item1: Item): 1abcde
1619 return item1 # pragma: nocover
1621 @app.post("/third") 1abcde
1622 def post_third(item1: Item): 1abcde
1623 return item1 # pragma: nocover
1625 client = TestClient(app) 1abcde
1626 with warnings.catch_warnings(record=True) as w: 1abcde
1627 warnings.simplefilter("always") 1abcde
1628 client.get("/openapi.json") 1abcde
1629 assert len(w) >= 2 1abcde
1630 duplicate_warnings = [ 1abcde
1631 warning for warning in w if issubclass(warning.category, UserWarning)
1632 ]
1633 assert len(duplicate_warnings) > 0 1abcde
1634 assert "Duplicate Operation ID" in str(duplicate_warnings[0].message) 1abcde