Coverage for tests/test_generate_unique_id_function.py: 100%
125 statements
« prev ^ index » next coverage.py v7.6.1, created at 2025-05-05 00:03 +0000
« prev ^ index » next coverage.py v7.6.1, created at 2025-05-05 00:03 +0000
1import warnings 1abcdef
2from typing import List 1abcdef
4from fastapi import APIRouter, FastAPI 1abcdef
5from fastapi.routing import APIRoute 1abcdef
6from fastapi.testclient import TestClient 1abcdef
7from pydantic import BaseModel 1abcdef
10def custom_generate_unique_id(route: APIRoute): 1abcdef
11 return f"foo_{route.name}" 1syzAgWtBCDhXuEFGiYvHIJjZwKLMk0xNOPl1
14def custom_generate_unique_id2(route: APIRoute): 1abcdef
15 return f"bar_{route.name}" 1QyzgRBChSEFiTHIjUKLkVNOl
18def custom_generate_unique_id3(route: APIRoute): 1abcdef
19 return f"baz_{route.name}" 1QsAgRtDhSuGiTvJjUwMkVxPl
22class Item(BaseModel): 1abcdef
23 name: str 1abcdef
24 price: float 1abcdef
27class Message(BaseModel): 1abcdef
28 title: str 1abcdef
29 description: str 1abcdef
32def test_top_level_generate_unique_id(): 1abcdef
33 app = FastAPI(generate_unique_id_function=custom_generate_unique_id) 1WXYZ01
34 router = APIRouter() 1WXYZ01
36 @app.post("/", response_model=List[Item], responses={404: {"model": List[Message]}}) 1WXYZ01
37 def post_root(item1: Item, item2: Item): 1WXYZ01
38 return item1, item2 # pragma: nocover
40 @router.post( 1WXYZ01
41 "/router", response_model=List[Item], responses={404: {"model": List[Message]}}
42 )
43 def post_router(item1: Item, item2: Item): 1WXYZ01
44 return item1, item2 # pragma: nocover
46 app.include_router(router) 1WXYZ01
47 client = TestClient(app) 1WXYZ01
48 response = client.get("/openapi.json") 1WXYZ01
49 data = response.json() 1WXYZ01
50 assert data == { 1WXYZ01
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(): 1abcdef
234 app = FastAPI(generate_unique_id_function=custom_generate_unique_id) 1zCFILO
235 router = APIRouter(generate_unique_id_function=custom_generate_unique_id2) 1zCFILO
237 @app.post("/", response_model=List[Item], responses={404: {"model": List[Message]}}) 1zCFILO
238 def post_root(item1: Item, item2: Item): 1zCFILO
239 return item1, item2 # pragma: nocover
241 @router.post( 1zCFILO
242 "/router", response_model=List[Item], responses={404: {"model": List[Message]}}
243 )
244 def post_router(item1: Item, item2: Item): 1zCFILO
245 return item1, item2 # pragma: nocover
247 app.include_router(router) 1zCFILO
248 client = TestClient(app) 1zCFILO
249 response = client.get("/openapi.json") 1zCFILO
250 data = response.json() 1zCFILO
251 assert data == { 1zCFILO
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(): 1abcdef
435 app = FastAPI(generate_unique_id_function=custom_generate_unique_id) 1yBEHKN
436 router = APIRouter(generate_unique_id_function=custom_generate_unique_id2) 1yBEHKN
438 @app.post("/", response_model=List[Item], responses={404: {"model": List[Message]}}) 1yBEHKN
439 def post_root(item1: Item, item2: Item): 1yBEHKN
440 return item1, item2 # pragma: nocover
442 @router.post( 1yBEHKN
443 "/router", response_model=List[Item], responses={404: {"model": List[Message]}}
444 )
445 def post_router(item1: Item, item2: Item): 1yBEHKN
446 return item1, item2 # pragma: nocover
448 app.include_router(router, generate_unique_id_function=custom_generate_unique_id3) 1yBEHKN
449 client = TestClient(app) 1yBEHKN
450 response = client.get("/openapi.json") 1yBEHKN
451 data = response.json() 1yBEHKN
452 assert data == { 1yBEHKN
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(): 1abcdef
636 app = FastAPI(generate_unique_id_function=custom_generate_unique_id) 1ghijkl
637 router = APIRouter() 1ghijkl
638 sub_router = APIRouter(generate_unique_id_function=custom_generate_unique_id2) 1ghijkl
640 @app.post("/", response_model=List[Item], responses={404: {"model": List[Message]}}) 1ghijkl
641 def post_root(item1: Item, item2: Item): 1ghijkl
642 return item1, item2 # pragma: nocover
644 @router.post( 1ghijkl
645 "/router", response_model=List[Item], responses={404: {"model": List[Message]}}
646 )
647 def post_router(item1: Item, item2: Item): 1ghijkl
648 return item1, item2 # pragma: nocover
650 @sub_router.post( 1ghijkl
651 "/subrouter",
652 response_model=List[Item],
653 responses={404: {"model": List[Message]}},
654 )
655 def post_subrouter(item1: Item, item2: Item): 1ghijkl
656 return item1, item2 # pragma: nocover
658 router.include_router(sub_router) 1ghijkl
659 app.include_router(router, generate_unique_id_function=custom_generate_unique_id3) 1ghijkl
660 client = TestClient(app) 1ghijkl
661 response = client.get("/openapi.json") 1ghijkl
662 data = response.json() 1ghijkl
663 assert data == { 1ghijkl
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(): 1abcdef
910 app = FastAPI(generate_unique_id_function=custom_generate_unique_id) 1ADGJMP
911 router = APIRouter(generate_unique_id_function=custom_generate_unique_id2) 1ADGJMP
913 @app.post("/", response_model=List[Item], responses={404: {"model": List[Message]}}) 1ADGJMP
914 def post_root(item1: Item, item2: Item): 1ADGJMP
915 return item1, item2 # pragma: nocover
917 @router.post( 1ADGJMP
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): 1ADGJMP
924 return item1, item2 # pragma: nocover
926 app.include_router(router) 1ADGJMP
927 client = TestClient(app) 1ADGJMP
928 response = client.get("/openapi.json") 1ADGJMP
929 data = response.json() 1ADGJMP
930 assert data == { 1ADGJMP
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(): 1abcdef
1114 app = FastAPI(generate_unique_id_function=custom_generate_unique_id) 1QRSTUV
1115 router = APIRouter(generate_unique_id_function=custom_generate_unique_id2) 1QRSTUV
1117 @app.post( 1QRSTUV
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): 1QRSTUV
1124 return item1, item2 # pragma: nocover
1126 @router.post( 1QRSTUV
1127 "/router",
1128 response_model=List[Item],
1129 responses={404: {"model": List[Message]}},
1130 )
1131 def post_router(item1: Item, item2: Item): 1QRSTUV
1132 return item1, item2 # pragma: nocover
1134 app.include_router(router) 1QRSTUV
1135 client = TestClient(app) 1QRSTUV
1136 response = client.get("/openapi.json") 1QRSTUV
1137 data = response.json() 1QRSTUV
1138 assert data == { 1QRSTUV
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(): 1abcdef
1322 app = FastAPI(generate_unique_id_function=custom_generate_unique_id) 1stuvwx
1323 callback_router = APIRouter(generate_unique_id_function=custom_generate_unique_id2) 1stuvwx
1325 @callback_router.post( 1stuvwx
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): 1stuvwx
1332 return item1, item2 # pragma: nocover
1334 @app.post( 1stuvwx
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): 1stuvwx
1342 return item1, item2 # pragma: nocover
1344 @app.post( 1stuvwx
1345 "/tocallback",
1346 response_model=List[Item],
1347 responses={404: {"model": List[Message]}},
1348 )
1349 def post_with_callback(item1: Item, item2: Item): 1stuvwx
1350 return item1, item2 # pragma: nocover
1352 client = TestClient(app) 1stuvwx
1353 response = client.get("/openapi.json") 1stuvwx
1354 data = response.json() 1stuvwx
1355 assert data == { 1stuvwx
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(): 1abcdef
1608 def broken_operation_id(route: APIRoute): 1mnopqr
1609 return "foo" 1mnopqr
1611 app = FastAPI(generate_unique_id_function=broken_operation_id) 1mnopqr
1613 @app.post("/") 1mnopqr
1614 def post_root(item1: Item): 1mnopqr
1615 return item1 # pragma: nocover
1617 @app.post("/second") 1mnopqr
1618 def post_second(item1: Item): 1mnopqr
1619 return item1 # pragma: nocover
1621 @app.post("/third") 1mnopqr
1622 def post_third(item1: Item): 1mnopqr
1623 return item1 # pragma: nocover
1625 client = TestClient(app) 1mnopqr
1626 with warnings.catch_warnings(record=True) as w: 1mnopqr
1627 warnings.simplefilter("always") 1mnopqr
1628 client.get("/openapi.json") 1mnopqr
1629 assert len(w) >= 2 1mnopqr
1630 duplicate_warnings = [ 1mnopqr
1631 warning for warning in w if issubclass(warning.category, UserWarning)
1632 ]
1633 assert len(duplicate_warnings) > 0 1mnopqr
1634 assert "Duplicate Operation ID" in str(duplicate_warnings[0].message) 1mnopqr