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