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