Coverage for tests/test_generate_unique_id_function.py: 100%

125 statements  

« prev     ^ index     » next       coverage.py v7.6.1, created at 2024-08-08 03:53 +0000

1import warnings 1abcde

2from typing import List 1abcde

3 

4from fastapi import APIRouter, FastAPI 1abcde

5from fastapi.routing import APIRoute 1abcde

6from fastapi.testclient import TestClient 1abcde

7from pydantic import BaseModel 1abcde

8 

9 

10def custom_generate_unique_id(route: APIRoute): 1abcde

11 return f"foo_{route.name}" 1abcde

12 

13 

14def custom_generate_unique_id2(route: APIRoute): 1abcde

15 return f"bar_{route.name}" 1abcde

16 

17 

18def custom_generate_unique_id3(route: APIRoute): 1abcde

19 return f"baz_{route.name}" 1abcde

20 

21 

22class Item(BaseModel): 1abcde

23 name: str 1abcde

24 price: float 1abcde

25 

26 

27class Message(BaseModel): 1abcde

28 title: str 1abcde

29 description: str 1abcde

30 

31 

32def test_top_level_generate_unique_id(): 1abcde

33 app = FastAPI(generate_unique_id_function=custom_generate_unique_id) 1abcde

34 router = APIRouter() 1abcde

35 

36 @app.post("/", response_model=List[Item], responses={404: {"model": List[Message]}}) 1abcde

37 def post_root(item1: Item, item2: Item): 1abcde

38 return item1, item2 # pragma: nocover 

39 

40 @router.post( 1abcde

41 "/router", response_model=List[Item], responses={404: {"model": List[Message]}} 

42 ) 

43 def post_router(item1: Item, item2: Item): 1abcde

44 return item1, item2 # pragma: nocover 

45 

46 app.include_router(router) 1abcde

47 client = TestClient(app) 1abcde

48 response = client.get("/openapi.json") 1abcde

49 data = response.json() 1abcde

50 assert data == { 1abcde

51 "openapi": "3.1.0", 

52 "info": {"title": "FastAPI", "version": "0.1.0"}, 

53 "paths": { 

54 "/": { 

55 "post": { 

56 "summary": "Post Root", 

57 "operationId": "foo_post_root", 

58 "requestBody": { 

59 "content": { 

60 "application/json": { 

61 "schema": { 

62 "$ref": "#/components/schemas/Body_foo_post_root" 

63 } 

64 } 

65 }, 

66 "required": True, 

67 }, 

68 "responses": { 

69 "200": { 

70 "description": "Successful Response", 

71 "content": { 

72 "application/json": { 

73 "schema": { 

74 "title": "Response Foo Post Root", 

75 "type": "array", 

76 "items": {"$ref": "#/components/schemas/Item"}, 

77 } 

78 } 

79 }, 

80 }, 

81 "404": { 

82 "description": "Not Found", 

83 "content": { 

84 "application/json": { 

85 "schema": { 

86 "title": "Response 404 Foo Post Root", 

87 "type": "array", 

88 "items": { 

89 "$ref": "#/components/schemas/Message" 

90 }, 

91 } 

92 } 

93 }, 

94 }, 

95 "422": { 

96 "description": "Validation Error", 

97 "content": { 

98 "application/json": { 

99 "schema": { 

100 "$ref": "#/components/schemas/HTTPValidationError" 

101 } 

102 } 

103 }, 

104 }, 

105 }, 

106 } 

107 }, 

108 "/router": { 

109 "post": { 

110 "summary": "Post Router", 

111 "operationId": "foo_post_router", 

112 "requestBody": { 

113 "content": { 

114 "application/json": { 

115 "schema": { 

116 "$ref": "#/components/schemas/Body_foo_post_router" 

117 } 

118 } 

119 }, 

120 "required": True, 

121 }, 

122 "responses": { 

123 "200": { 

124 "description": "Successful Response", 

125 "content": { 

126 "application/json": { 

127 "schema": { 

128 "title": "Response Foo Post Router", 

129 "type": "array", 

130 "items": {"$ref": "#/components/schemas/Item"}, 

131 } 

132 } 

133 }, 

134 }, 

135 "404": { 

136 "description": "Not Found", 

137 "content": { 

138 "application/json": { 

139 "schema": { 

140 "title": "Response 404 Foo Post Router", 

141 "type": "array", 

142 "items": { 

143 "$ref": "#/components/schemas/Message" 

144 }, 

145 } 

146 } 

147 }, 

148 }, 

149 "422": { 

150 "description": "Validation Error", 

151 "content": { 

152 "application/json": { 

153 "schema": { 

154 "$ref": "#/components/schemas/HTTPValidationError" 

155 } 

156 } 

157 }, 

158 }, 

159 }, 

160 } 

161 }, 

162 }, 

163 "components": { 

164 "schemas": { 

165 "Body_foo_post_root": { 

166 "title": "Body_foo_post_root", 

167 "required": ["item1", "item2"], 

168 "type": "object", 

169 "properties": { 

170 "item1": {"$ref": "#/components/schemas/Item"}, 

171 "item2": {"$ref": "#/components/schemas/Item"}, 

172 }, 

173 }, 

174 "Body_foo_post_router": { 

175 "title": "Body_foo_post_router", 

176 "required": ["item1", "item2"], 

177 "type": "object", 

178 "properties": { 

179 "item1": {"$ref": "#/components/schemas/Item"}, 

180 "item2": {"$ref": "#/components/schemas/Item"}, 

181 }, 

182 }, 

183 "HTTPValidationError": { 

184 "title": "HTTPValidationError", 

185 "type": "object", 

186 "properties": { 

187 "detail": { 

188 "title": "Detail", 

189 "type": "array", 

190 "items": {"$ref": "#/components/schemas/ValidationError"}, 

191 } 

192 }, 

193 }, 

194 "Item": { 

195 "title": "Item", 

196 "required": ["name", "price"], 

197 "type": "object", 

198 "properties": { 

199 "name": {"title": "Name", "type": "string"}, 

200 "price": {"title": "Price", "type": "number"}, 

201 }, 

202 }, 

203 "Message": { 

204 "title": "Message", 

205 "required": ["title", "description"], 

206 "type": "object", 

207 "properties": { 

208 "title": {"title": "Title", "type": "string"}, 

209 "description": {"title": "Description", "type": "string"}, 

210 }, 

211 }, 

212 "ValidationError": { 

213 "title": "ValidationError", 

214 "required": ["loc", "msg", "type"], 

215 "type": "object", 

216 "properties": { 

217 "loc": { 

218 "title": "Location", 

219 "type": "array", 

220 "items": { 

221 "anyOf": [{"type": "string"}, {"type": "integer"}] 

222 }, 

223 }, 

224 "msg": {"title": "Message", "type": "string"}, 

225 "type": {"title": "Error Type", "type": "string"}, 

226 }, 

227 }, 

228 } 

229 }, 

230 } 

231 

232 

233def test_router_overrides_generate_unique_id(): 1abcde

234 app = FastAPI(generate_unique_id_function=custom_generate_unique_id) 1abcde

235 router = APIRouter(generate_unique_id_function=custom_generate_unique_id2) 1abcde

236 

237 @app.post("/", response_model=List[Item], responses={404: {"model": List[Message]}}) 1abcde

238 def post_root(item1: Item, item2: Item): 1abcde

239 return item1, item2 # pragma: nocover 

240 

241 @router.post( 1abcde

242 "/router", response_model=List[Item], responses={404: {"model": List[Message]}} 

243 ) 

244 def post_router(item1: Item, item2: Item): 1abcde

245 return item1, item2 # pragma: nocover 

246 

247 app.include_router(router) 1abcde

248 client = TestClient(app) 1abcde

249 response = client.get("/openapi.json") 1abcde

250 data = response.json() 1abcde

251 assert data == { 1abcde

252 "openapi": "3.1.0", 

253 "info": {"title": "FastAPI", "version": "0.1.0"}, 

254 "paths": { 

255 "/": { 

256 "post": { 

257 "summary": "Post Root", 

258 "operationId": "foo_post_root", 

259 "requestBody": { 

260 "content": { 

261 "application/json": { 

262 "schema": { 

263 "$ref": "#/components/schemas/Body_foo_post_root" 

264 } 

265 } 

266 }, 

267 "required": True, 

268 }, 

269 "responses": { 

270 "200": { 

271 "description": "Successful Response", 

272 "content": { 

273 "application/json": { 

274 "schema": { 

275 "title": "Response Foo Post Root", 

276 "type": "array", 

277 "items": {"$ref": "#/components/schemas/Item"}, 

278 } 

279 } 

280 }, 

281 }, 

282 "404": { 

283 "description": "Not Found", 

284 "content": { 

285 "application/json": { 

286 "schema": { 

287 "title": "Response 404 Foo Post Root", 

288 "type": "array", 

289 "items": { 

290 "$ref": "#/components/schemas/Message" 

291 }, 

292 } 

293 } 

294 }, 

295 }, 

296 "422": { 

297 "description": "Validation Error", 

298 "content": { 

299 "application/json": { 

300 "schema": { 

301 "$ref": "#/components/schemas/HTTPValidationError" 

302 } 

303 } 

304 }, 

305 }, 

306 }, 

307 } 

308 }, 

309 "/router": { 

310 "post": { 

311 "summary": "Post Router", 

312 "operationId": "bar_post_router", 

313 "requestBody": { 

314 "content": { 

315 "application/json": { 

316 "schema": { 

317 "$ref": "#/components/schemas/Body_bar_post_router" 

318 } 

319 } 

320 }, 

321 "required": True, 

322 }, 

323 "responses": { 

324 "200": { 

325 "description": "Successful Response", 

326 "content": { 

327 "application/json": { 

328 "schema": { 

329 "title": "Response Bar Post Router", 

330 "type": "array", 

331 "items": {"$ref": "#/components/schemas/Item"}, 

332 } 

333 } 

334 }, 

335 }, 

336 "404": { 

337 "description": "Not Found", 

338 "content": { 

339 "application/json": { 

340 "schema": { 

341 "title": "Response 404 Bar Post Router", 

342 "type": "array", 

343 "items": { 

344 "$ref": "#/components/schemas/Message" 

345 }, 

346 } 

347 } 

348 }, 

349 }, 

350 "422": { 

351 "description": "Validation Error", 

352 "content": { 

353 "application/json": { 

354 "schema": { 

355 "$ref": "#/components/schemas/HTTPValidationError" 

356 } 

357 } 

358 }, 

359 }, 

360 }, 

361 } 

362 }, 

363 }, 

364 "components": { 

365 "schemas": { 

366 "Body_bar_post_router": { 

367 "title": "Body_bar_post_router", 

368 "required": ["item1", "item2"], 

369 "type": "object", 

370 "properties": { 

371 "item1": {"$ref": "#/components/schemas/Item"}, 

372 "item2": {"$ref": "#/components/schemas/Item"}, 

373 }, 

374 }, 

375 "Body_foo_post_root": { 

376 "title": "Body_foo_post_root", 

377 "required": ["item1", "item2"], 

378 "type": "object", 

379 "properties": { 

380 "item1": {"$ref": "#/components/schemas/Item"}, 

381 "item2": {"$ref": "#/components/schemas/Item"}, 

382 }, 

383 }, 

384 "HTTPValidationError": { 

385 "title": "HTTPValidationError", 

386 "type": "object", 

387 "properties": { 

388 "detail": { 

389 "title": "Detail", 

390 "type": "array", 

391 "items": {"$ref": "#/components/schemas/ValidationError"}, 

392 } 

393 }, 

394 }, 

395 "Item": { 

396 "title": "Item", 

397 "required": ["name", "price"], 

398 "type": "object", 

399 "properties": { 

400 "name": {"title": "Name", "type": "string"}, 

401 "price": {"title": "Price", "type": "number"}, 

402 }, 

403 }, 

404 "Message": { 

405 "title": "Message", 

406 "required": ["title", "description"], 

407 "type": "object", 

408 "properties": { 

409 "title": {"title": "Title", "type": "string"}, 

410 "description": {"title": "Description", "type": "string"}, 

411 }, 

412 }, 

413 "ValidationError": { 

414 "title": "ValidationError", 

415 "required": ["loc", "msg", "type"], 

416 "type": "object", 

417 "properties": { 

418 "loc": { 

419 "title": "Location", 

420 "type": "array", 

421 "items": { 

422 "anyOf": [{"type": "string"}, {"type": "integer"}] 

423 }, 

424 }, 

425 "msg": {"title": "Message", "type": "string"}, 

426 "type": {"title": "Error Type", "type": "string"}, 

427 }, 

428 }, 

429 } 

430 }, 

431 } 

432 

433 

434def test_router_include_overrides_generate_unique_id(): 1abcde

435 app = FastAPI(generate_unique_id_function=custom_generate_unique_id) 1abcde

436 router = APIRouter(generate_unique_id_function=custom_generate_unique_id2) 1abcde

437 

438 @app.post("/", response_model=List[Item], responses={404: {"model": List[Message]}}) 1abcde

439 def post_root(item1: Item, item2: Item): 1abcde

440 return item1, item2 # pragma: nocover 

441 

442 @router.post( 1abcde

443 "/router", response_model=List[Item], responses={404: {"model": List[Message]}} 

444 ) 

445 def post_router(item1: Item, item2: Item): 1abcde

446 return item1, item2 # pragma: nocover 

447 

448 app.include_router(router, generate_unique_id_function=custom_generate_unique_id3) 1abcde

449 client = TestClient(app) 1abcde

450 response = client.get("/openapi.json") 1abcde

451 data = response.json() 1abcde

452 assert data == { 1abcde

453 "openapi": "3.1.0", 

454 "info": {"title": "FastAPI", "version": "0.1.0"}, 

455 "paths": { 

456 "/": { 

457 "post": { 

458 "summary": "Post Root", 

459 "operationId": "foo_post_root", 

460 "requestBody": { 

461 "content": { 

462 "application/json": { 

463 "schema": { 

464 "$ref": "#/components/schemas/Body_foo_post_root" 

465 } 

466 } 

467 }, 

468 "required": True, 

469 }, 

470 "responses": { 

471 "200": { 

472 "description": "Successful Response", 

473 "content": { 

474 "application/json": { 

475 "schema": { 

476 "title": "Response Foo Post Root", 

477 "type": "array", 

478 "items": {"$ref": "#/components/schemas/Item"}, 

479 } 

480 } 

481 }, 

482 }, 

483 "404": { 

484 "description": "Not Found", 

485 "content": { 

486 "application/json": { 

487 "schema": { 

488 "title": "Response 404 Foo Post Root", 

489 "type": "array", 

490 "items": { 

491 "$ref": "#/components/schemas/Message" 

492 }, 

493 } 

494 } 

495 }, 

496 }, 

497 "422": { 

498 "description": "Validation Error", 

499 "content": { 

500 "application/json": { 

501 "schema": { 

502 "$ref": "#/components/schemas/HTTPValidationError" 

503 } 

504 } 

505 }, 

506 }, 

507 }, 

508 } 

509 }, 

510 "/router": { 

511 "post": { 

512 "summary": "Post Router", 

513 "operationId": "bar_post_router", 

514 "requestBody": { 

515 "content": { 

516 "application/json": { 

517 "schema": { 

518 "$ref": "#/components/schemas/Body_bar_post_router" 

519 } 

520 } 

521 }, 

522 "required": True, 

523 }, 

524 "responses": { 

525 "200": { 

526 "description": "Successful Response", 

527 "content": { 

528 "application/json": { 

529 "schema": { 

530 "title": "Response Bar Post Router", 

531 "type": "array", 

532 "items": {"$ref": "#/components/schemas/Item"}, 

533 } 

534 } 

535 }, 

536 }, 

537 "404": { 

538 "description": "Not Found", 

539 "content": { 

540 "application/json": { 

541 "schema": { 

542 "title": "Response 404 Bar Post Router", 

543 "type": "array", 

544 "items": { 

545 "$ref": "#/components/schemas/Message" 

546 }, 

547 } 

548 } 

549 }, 

550 }, 

551 "422": { 

552 "description": "Validation Error", 

553 "content": { 

554 "application/json": { 

555 "schema": { 

556 "$ref": "#/components/schemas/HTTPValidationError" 

557 } 

558 } 

559 }, 

560 }, 

561 }, 

562 } 

563 }, 

564 }, 

565 "components": { 

566 "schemas": { 

567 "Body_bar_post_router": { 

568 "title": "Body_bar_post_router", 

569 "required": ["item1", "item2"], 

570 "type": "object", 

571 "properties": { 

572 "item1": {"$ref": "#/components/schemas/Item"}, 

573 "item2": {"$ref": "#/components/schemas/Item"}, 

574 }, 

575 }, 

576 "Body_foo_post_root": { 

577 "title": "Body_foo_post_root", 

578 "required": ["item1", "item2"], 

579 "type": "object", 

580 "properties": { 

581 "item1": {"$ref": "#/components/schemas/Item"}, 

582 "item2": {"$ref": "#/components/schemas/Item"}, 

583 }, 

584 }, 

585 "HTTPValidationError": { 

586 "title": "HTTPValidationError", 

587 "type": "object", 

588 "properties": { 

589 "detail": { 

590 "title": "Detail", 

591 "type": "array", 

592 "items": {"$ref": "#/components/schemas/ValidationError"}, 

593 } 

594 }, 

595 }, 

596 "Item": { 

597 "title": "Item", 

598 "required": ["name", "price"], 

599 "type": "object", 

600 "properties": { 

601 "name": {"title": "Name", "type": "string"}, 

602 "price": {"title": "Price", "type": "number"}, 

603 }, 

604 }, 

605 "Message": { 

606 "title": "Message", 

607 "required": ["title", "description"], 

608 "type": "object", 

609 "properties": { 

610 "title": {"title": "Title", "type": "string"}, 

611 "description": {"title": "Description", "type": "string"}, 

612 }, 

613 }, 

614 "ValidationError": { 

615 "title": "ValidationError", 

616 "required": ["loc", "msg", "type"], 

617 "type": "object", 

618 "properties": { 

619 "loc": { 

620 "title": "Location", 

621 "type": "array", 

622 "items": { 

623 "anyOf": [{"type": "string"}, {"type": "integer"}] 

624 }, 

625 }, 

626 "msg": {"title": "Message", "type": "string"}, 

627 "type": {"title": "Error Type", "type": "string"}, 

628 }, 

629 }, 

630 } 

631 }, 

632 } 

633 

634 

635def test_subrouter_top_level_include_overrides_generate_unique_id(): 1abcde

636 app = FastAPI(generate_unique_id_function=custom_generate_unique_id) 1abcde

637 router = APIRouter() 1abcde

638 sub_router = APIRouter(generate_unique_id_function=custom_generate_unique_id2) 1abcde

639 

640 @app.post("/", response_model=List[Item], responses={404: {"model": List[Message]}}) 1abcde

641 def post_root(item1: Item, item2: Item): 1abcde

642 return item1, item2 # pragma: nocover 

643 

644 @router.post( 1abcde

645 "/router", response_model=List[Item], responses={404: {"model": List[Message]}} 

646 ) 

647 def post_router(item1: Item, item2: Item): 1abcde

648 return item1, item2 # pragma: nocover 

649 

650 @sub_router.post( 1abcde

651 "/subrouter", 

652 response_model=List[Item], 

653 responses={404: {"model": List[Message]}}, 

654 ) 

655 def post_subrouter(item1: Item, item2: Item): 1abcde

656 return item1, item2 # pragma: nocover 

657 

658 router.include_router(sub_router) 1abcde

659 app.include_router(router, generate_unique_id_function=custom_generate_unique_id3) 1abcde

660 client = TestClient(app) 1abcde

661 response = client.get("/openapi.json") 1abcde

662 data = response.json() 1abcde

663 assert data == { 1abcde

664 "openapi": "3.1.0", 

665 "info": {"title": "FastAPI", "version": "0.1.0"}, 

666 "paths": { 

667 "/": { 

668 "post": { 

669 "summary": "Post Root", 

670 "operationId": "foo_post_root", 

671 "requestBody": { 

672 "content": { 

673 "application/json": { 

674 "schema": { 

675 "$ref": "#/components/schemas/Body_foo_post_root" 

676 } 

677 } 

678 }, 

679 "required": True, 

680 }, 

681 "responses": { 

682 "200": { 

683 "description": "Successful Response", 

684 "content": { 

685 "application/json": { 

686 "schema": { 

687 "title": "Response Foo Post Root", 

688 "type": "array", 

689 "items": {"$ref": "#/components/schemas/Item"}, 

690 } 

691 } 

692 }, 

693 }, 

694 "404": { 

695 "description": "Not Found", 

696 "content": { 

697 "application/json": { 

698 "schema": { 

699 "title": "Response 404 Foo Post Root", 

700 "type": "array", 

701 "items": { 

702 "$ref": "#/components/schemas/Message" 

703 }, 

704 } 

705 } 

706 }, 

707 }, 

708 "422": { 

709 "description": "Validation Error", 

710 "content": { 

711 "application/json": { 

712 "schema": { 

713 "$ref": "#/components/schemas/HTTPValidationError" 

714 } 

715 } 

716 }, 

717 }, 

718 }, 

719 } 

720 }, 

721 "/router": { 

722 "post": { 

723 "summary": "Post Router", 

724 "operationId": "baz_post_router", 

725 "requestBody": { 

726 "content": { 

727 "application/json": { 

728 "schema": { 

729 "$ref": "#/components/schemas/Body_baz_post_router" 

730 } 

731 } 

732 }, 

733 "required": True, 

734 }, 

735 "responses": { 

736 "200": { 

737 "description": "Successful Response", 

738 "content": { 

739 "application/json": { 

740 "schema": { 

741 "title": "Response Baz Post Router", 

742 "type": "array", 

743 "items": {"$ref": "#/components/schemas/Item"}, 

744 } 

745 } 

746 }, 

747 }, 

748 "404": { 

749 "description": "Not Found", 

750 "content": { 

751 "application/json": { 

752 "schema": { 

753 "title": "Response 404 Baz Post Router", 

754 "type": "array", 

755 "items": { 

756 "$ref": "#/components/schemas/Message" 

757 }, 

758 } 

759 } 

760 }, 

761 }, 

762 "422": { 

763 "description": "Validation Error", 

764 "content": { 

765 "application/json": { 

766 "schema": { 

767 "$ref": "#/components/schemas/HTTPValidationError" 

768 } 

769 } 

770 }, 

771 }, 

772 }, 

773 } 

774 }, 

775 "/subrouter": { 

776 "post": { 

777 "summary": "Post Subrouter", 

778 "operationId": "bar_post_subrouter", 

779 "requestBody": { 

780 "content": { 

781 "application/json": { 

782 "schema": { 

783 "$ref": "#/components/schemas/Body_bar_post_subrouter" 

784 } 

785 } 

786 }, 

787 "required": True, 

788 }, 

789 "responses": { 

790 "200": { 

791 "description": "Successful Response", 

792 "content": { 

793 "application/json": { 

794 "schema": { 

795 "title": "Response Bar Post Subrouter", 

796 "type": "array", 

797 "items": {"$ref": "#/components/schemas/Item"}, 

798 } 

799 } 

800 }, 

801 }, 

802 "404": { 

803 "description": "Not Found", 

804 "content": { 

805 "application/json": { 

806 "schema": { 

807 "title": "Response 404 Bar Post Subrouter", 

808 "type": "array", 

809 "items": { 

810 "$ref": "#/components/schemas/Message" 

811 }, 

812 } 

813 } 

814 }, 

815 }, 

816 "422": { 

817 "description": "Validation Error", 

818 "content": { 

819 "application/json": { 

820 "schema": { 

821 "$ref": "#/components/schemas/HTTPValidationError" 

822 } 

823 } 

824 }, 

825 }, 

826 }, 

827 } 

828 }, 

829 }, 

830 "components": { 

831 "schemas": { 

832 "Body_bar_post_subrouter": { 

833 "title": "Body_bar_post_subrouter", 

834 "required": ["item1", "item2"], 

835 "type": "object", 

836 "properties": { 

837 "item1": {"$ref": "#/components/schemas/Item"}, 

838 "item2": {"$ref": "#/components/schemas/Item"}, 

839 }, 

840 }, 

841 "Body_baz_post_router": { 

842 "title": "Body_baz_post_router", 

843 "required": ["item1", "item2"], 

844 "type": "object", 

845 "properties": { 

846 "item1": {"$ref": "#/components/schemas/Item"}, 

847 "item2": {"$ref": "#/components/schemas/Item"}, 

848 }, 

849 }, 

850 "Body_foo_post_root": { 

851 "title": "Body_foo_post_root", 

852 "required": ["item1", "item2"], 

853 "type": "object", 

854 "properties": { 

855 "item1": {"$ref": "#/components/schemas/Item"}, 

856 "item2": {"$ref": "#/components/schemas/Item"}, 

857 }, 

858 }, 

859 "HTTPValidationError": { 

860 "title": "HTTPValidationError", 

861 "type": "object", 

862 "properties": { 

863 "detail": { 

864 "title": "Detail", 

865 "type": "array", 

866 "items": {"$ref": "#/components/schemas/ValidationError"}, 

867 } 

868 }, 

869 }, 

870 "Item": { 

871 "title": "Item", 

872 "required": ["name", "price"], 

873 "type": "object", 

874 "properties": { 

875 "name": {"title": "Name", "type": "string"}, 

876 "price": {"title": "Price", "type": "number"}, 

877 }, 

878 }, 

879 "Message": { 

880 "title": "Message", 

881 "required": ["title", "description"], 

882 "type": "object", 

883 "properties": { 

884 "title": {"title": "Title", "type": "string"}, 

885 "description": {"title": "Description", "type": "string"}, 

886 }, 

887 }, 

888 "ValidationError": { 

889 "title": "ValidationError", 

890 "required": ["loc", "msg", "type"], 

891 "type": "object", 

892 "properties": { 

893 "loc": { 

894 "title": "Location", 

895 "type": "array", 

896 "items": { 

897 "anyOf": [{"type": "string"}, {"type": "integer"}] 

898 }, 

899 }, 

900 "msg": {"title": "Message", "type": "string"}, 

901 "type": {"title": "Error Type", "type": "string"}, 

902 }, 

903 }, 

904 } 

905 }, 

906 } 

907 

908 

909def test_router_path_operation_overrides_generate_unique_id(): 1abcde

910 app = FastAPI(generate_unique_id_function=custom_generate_unique_id) 1abcde

911 router = APIRouter(generate_unique_id_function=custom_generate_unique_id2) 1abcde

912 

913 @app.post("/", response_model=List[Item], responses={404: {"model": List[Message]}}) 1abcde

914 def post_root(item1: Item, item2: Item): 1abcde

915 return item1, item2 # pragma: nocover 

916 

917 @router.post( 1abcde

918 "/router", 

919 response_model=List[Item], 

920 responses={404: {"model": List[Message]}}, 

921 generate_unique_id_function=custom_generate_unique_id3, 

922 ) 

923 def post_router(item1: Item, item2: Item): 1abcde

924 return item1, item2 # pragma: nocover 

925 

926 app.include_router(router) 1abcde

927 client = TestClient(app) 1abcde

928 response = client.get("/openapi.json") 1abcde

929 data = response.json() 1abcde

930 assert data == { 1abcde

931 "openapi": "3.1.0", 

932 "info": {"title": "FastAPI", "version": "0.1.0"}, 

933 "paths": { 

934 "/": { 

935 "post": { 

936 "summary": "Post Root", 

937 "operationId": "foo_post_root", 

938 "requestBody": { 

939 "content": { 

940 "application/json": { 

941 "schema": { 

942 "$ref": "#/components/schemas/Body_foo_post_root" 

943 } 

944 } 

945 }, 

946 "required": True, 

947 }, 

948 "responses": { 

949 "200": { 

950 "description": "Successful Response", 

951 "content": { 

952 "application/json": { 

953 "schema": { 

954 "title": "Response Foo Post Root", 

955 "type": "array", 

956 "items": {"$ref": "#/components/schemas/Item"}, 

957 } 

958 } 

959 }, 

960 }, 

961 "404": { 

962 "description": "Not Found", 

963 "content": { 

964 "application/json": { 

965 "schema": { 

966 "title": "Response 404 Foo Post Root", 

967 "type": "array", 

968 "items": { 

969 "$ref": "#/components/schemas/Message" 

970 }, 

971 } 

972 } 

973 }, 

974 }, 

975 "422": { 

976 "description": "Validation Error", 

977 "content": { 

978 "application/json": { 

979 "schema": { 

980 "$ref": "#/components/schemas/HTTPValidationError" 

981 } 

982 } 

983 }, 

984 }, 

985 }, 

986 } 

987 }, 

988 "/router": { 

989 "post": { 

990 "summary": "Post Router", 

991 "operationId": "baz_post_router", 

992 "requestBody": { 

993 "content": { 

994 "application/json": { 

995 "schema": { 

996 "$ref": "#/components/schemas/Body_baz_post_router" 

997 } 

998 } 

999 }, 

1000 "required": True, 

1001 }, 

1002 "responses": { 

1003 "200": { 

1004 "description": "Successful Response", 

1005 "content": { 

1006 "application/json": { 

1007 "schema": { 

1008 "title": "Response Baz Post Router", 

1009 "type": "array", 

1010 "items": {"$ref": "#/components/schemas/Item"}, 

1011 } 

1012 } 

1013 }, 

1014 }, 

1015 "404": { 

1016 "description": "Not Found", 

1017 "content": { 

1018 "application/json": { 

1019 "schema": { 

1020 "title": "Response 404 Baz Post Router", 

1021 "type": "array", 

1022 "items": { 

1023 "$ref": "#/components/schemas/Message" 

1024 }, 

1025 } 

1026 } 

1027 }, 

1028 }, 

1029 "422": { 

1030 "description": "Validation Error", 

1031 "content": { 

1032 "application/json": { 

1033 "schema": { 

1034 "$ref": "#/components/schemas/HTTPValidationError" 

1035 } 

1036 } 

1037 }, 

1038 }, 

1039 }, 

1040 } 

1041 }, 

1042 }, 

1043 "components": { 

1044 "schemas": { 

1045 "Body_baz_post_router": { 

1046 "title": "Body_baz_post_router", 

1047 "required": ["item1", "item2"], 

1048 "type": "object", 

1049 "properties": { 

1050 "item1": {"$ref": "#/components/schemas/Item"}, 

1051 "item2": {"$ref": "#/components/schemas/Item"}, 

1052 }, 

1053 }, 

1054 "Body_foo_post_root": { 

1055 "title": "Body_foo_post_root", 

1056 "required": ["item1", "item2"], 

1057 "type": "object", 

1058 "properties": { 

1059 "item1": {"$ref": "#/components/schemas/Item"}, 

1060 "item2": {"$ref": "#/components/schemas/Item"}, 

1061 }, 

1062 }, 

1063 "HTTPValidationError": { 

1064 "title": "HTTPValidationError", 

1065 "type": "object", 

1066 "properties": { 

1067 "detail": { 

1068 "title": "Detail", 

1069 "type": "array", 

1070 "items": {"$ref": "#/components/schemas/ValidationError"}, 

1071 } 

1072 }, 

1073 }, 

1074 "Item": { 

1075 "title": "Item", 

1076 "required": ["name", "price"], 

1077 "type": "object", 

1078 "properties": { 

1079 "name": {"title": "Name", "type": "string"}, 

1080 "price": {"title": "Price", "type": "number"}, 

1081 }, 

1082 }, 

1083 "Message": { 

1084 "title": "Message", 

1085 "required": ["title", "description"], 

1086 "type": "object", 

1087 "properties": { 

1088 "title": {"title": "Title", "type": "string"}, 

1089 "description": {"title": "Description", "type": "string"}, 

1090 }, 

1091 }, 

1092 "ValidationError": { 

1093 "title": "ValidationError", 

1094 "required": ["loc", "msg", "type"], 

1095 "type": "object", 

1096 "properties": { 

1097 "loc": { 

1098 "title": "Location", 

1099 "type": "array", 

1100 "items": { 

1101 "anyOf": [{"type": "string"}, {"type": "integer"}] 

1102 }, 

1103 }, 

1104 "msg": {"title": "Message", "type": "string"}, 

1105 "type": {"title": "Error Type", "type": "string"}, 

1106 }, 

1107 }, 

1108 } 

1109 }, 

1110 } 

1111 

1112 

1113def test_app_path_operation_overrides_generate_unique_id(): 1abcde

1114 app = FastAPI(generate_unique_id_function=custom_generate_unique_id) 1abcde

1115 router = APIRouter(generate_unique_id_function=custom_generate_unique_id2) 1abcde

1116 

1117 @app.post( 1abcde

1118 "/", 

1119 response_model=List[Item], 

1120 responses={404: {"model": List[Message]}}, 

1121 generate_unique_id_function=custom_generate_unique_id3, 

1122 ) 

1123 def post_root(item1: Item, item2: Item): 1abcde

1124 return item1, item2 # pragma: nocover 

1125 

1126 @router.post( 1abcde

1127 "/router", 

1128 response_model=List[Item], 

1129 responses={404: {"model": List[Message]}}, 

1130 ) 

1131 def post_router(item1: Item, item2: Item): 1abcde

1132 return item1, item2 # pragma: nocover 

1133 

1134 app.include_router(router) 1abcde

1135 client = TestClient(app) 1abcde

1136 response = client.get("/openapi.json") 1abcde

1137 data = response.json() 1abcde

1138 assert data == { 1abcde

1139 "openapi": "3.1.0", 

1140 "info": {"title": "FastAPI", "version": "0.1.0"}, 

1141 "paths": { 

1142 "/": { 

1143 "post": { 

1144 "summary": "Post Root", 

1145 "operationId": "baz_post_root", 

1146 "requestBody": { 

1147 "content": { 

1148 "application/json": { 

1149 "schema": { 

1150 "$ref": "#/components/schemas/Body_baz_post_root" 

1151 } 

1152 } 

1153 }, 

1154 "required": True, 

1155 }, 

1156 "responses": { 

1157 "200": { 

1158 "description": "Successful Response", 

1159 "content": { 

1160 "application/json": { 

1161 "schema": { 

1162 "title": "Response Baz Post Root", 

1163 "type": "array", 

1164 "items": {"$ref": "#/components/schemas/Item"}, 

1165 } 

1166 } 

1167 }, 

1168 }, 

1169 "404": { 

1170 "description": "Not Found", 

1171 "content": { 

1172 "application/json": { 

1173 "schema": { 

1174 "title": "Response 404 Baz Post Root", 

1175 "type": "array", 

1176 "items": { 

1177 "$ref": "#/components/schemas/Message" 

1178 }, 

1179 } 

1180 } 

1181 }, 

1182 }, 

1183 "422": { 

1184 "description": "Validation Error", 

1185 "content": { 

1186 "application/json": { 

1187 "schema": { 

1188 "$ref": "#/components/schemas/HTTPValidationError" 

1189 } 

1190 } 

1191 }, 

1192 }, 

1193 }, 

1194 } 

1195 }, 

1196 "/router": { 

1197 "post": { 

1198 "summary": "Post Router", 

1199 "operationId": "bar_post_router", 

1200 "requestBody": { 

1201 "content": { 

1202 "application/json": { 

1203 "schema": { 

1204 "$ref": "#/components/schemas/Body_bar_post_router" 

1205 } 

1206 } 

1207 }, 

1208 "required": True, 

1209 }, 

1210 "responses": { 

1211 "200": { 

1212 "description": "Successful Response", 

1213 "content": { 

1214 "application/json": { 

1215 "schema": { 

1216 "title": "Response Bar Post Router", 

1217 "type": "array", 

1218 "items": {"$ref": "#/components/schemas/Item"}, 

1219 } 

1220 } 

1221 }, 

1222 }, 

1223 "404": { 

1224 "description": "Not Found", 

1225 "content": { 

1226 "application/json": { 

1227 "schema": { 

1228 "title": "Response 404 Bar Post Router", 

1229 "type": "array", 

1230 "items": { 

1231 "$ref": "#/components/schemas/Message" 

1232 }, 

1233 } 

1234 } 

1235 }, 

1236 }, 

1237 "422": { 

1238 "description": "Validation Error", 

1239 "content": { 

1240 "application/json": { 

1241 "schema": { 

1242 "$ref": "#/components/schemas/HTTPValidationError" 

1243 } 

1244 } 

1245 }, 

1246 }, 

1247 }, 

1248 } 

1249 }, 

1250 }, 

1251 "components": { 

1252 "schemas": { 

1253 "Body_bar_post_router": { 

1254 "title": "Body_bar_post_router", 

1255 "required": ["item1", "item2"], 

1256 "type": "object", 

1257 "properties": { 

1258 "item1": {"$ref": "#/components/schemas/Item"}, 

1259 "item2": {"$ref": "#/components/schemas/Item"}, 

1260 }, 

1261 }, 

1262 "Body_baz_post_root": { 

1263 "title": "Body_baz_post_root", 

1264 "required": ["item1", "item2"], 

1265 "type": "object", 

1266 "properties": { 

1267 "item1": {"$ref": "#/components/schemas/Item"}, 

1268 "item2": {"$ref": "#/components/schemas/Item"}, 

1269 }, 

1270 }, 

1271 "HTTPValidationError": { 

1272 "title": "HTTPValidationError", 

1273 "type": "object", 

1274 "properties": { 

1275 "detail": { 

1276 "title": "Detail", 

1277 "type": "array", 

1278 "items": {"$ref": "#/components/schemas/ValidationError"}, 

1279 } 

1280 }, 

1281 }, 

1282 "Item": { 

1283 "title": "Item", 

1284 "required": ["name", "price"], 

1285 "type": "object", 

1286 "properties": { 

1287 "name": {"title": "Name", "type": "string"}, 

1288 "price": {"title": "Price", "type": "number"}, 

1289 }, 

1290 }, 

1291 "Message": { 

1292 "title": "Message", 

1293 "required": ["title", "description"], 

1294 "type": "object", 

1295 "properties": { 

1296 "title": {"title": "Title", "type": "string"}, 

1297 "description": {"title": "Description", "type": "string"}, 

1298 }, 

1299 }, 

1300 "ValidationError": { 

1301 "title": "ValidationError", 

1302 "required": ["loc", "msg", "type"], 

1303 "type": "object", 

1304 "properties": { 

1305 "loc": { 

1306 "title": "Location", 

1307 "type": "array", 

1308 "items": { 

1309 "anyOf": [{"type": "string"}, {"type": "integer"}] 

1310 }, 

1311 }, 

1312 "msg": {"title": "Message", "type": "string"}, 

1313 "type": {"title": "Error Type", "type": "string"}, 

1314 }, 

1315 }, 

1316 } 

1317 }, 

1318 } 

1319 

1320 

1321def test_callback_override_generate_unique_id(): 1abcde

1322 app = FastAPI(generate_unique_id_function=custom_generate_unique_id) 1abcde

1323 callback_router = APIRouter(generate_unique_id_function=custom_generate_unique_id2) 1abcde

1324 

1325 @callback_router.post( 1abcde

1326 "/post-callback", 

1327 response_model=List[Item], 

1328 responses={404: {"model": List[Message]}}, 

1329 generate_unique_id_function=custom_generate_unique_id3, 

1330 ) 

1331 def post_callback(item1: Item, item2: Item): 1abcde

1332 return item1, item2 # pragma: nocover 

1333 

1334 @app.post( 1abcde

1335 "/", 

1336 response_model=List[Item], 

1337 responses={404: {"model": List[Message]}}, 

1338 generate_unique_id_function=custom_generate_unique_id3, 

1339 callbacks=callback_router.routes, 

1340 ) 

1341 def post_root(item1: Item, item2: Item): 1abcde

1342 return item1, item2 # pragma: nocover 

1343 

1344 @app.post( 1abcde

1345 "/tocallback", 

1346 response_model=List[Item], 

1347 responses={404: {"model": List[Message]}}, 

1348 ) 

1349 def post_with_callback(item1: Item, item2: Item): 1abcde

1350 return item1, item2 # pragma: nocover 

1351 

1352 client = TestClient(app) 1abcde

1353 response = client.get("/openapi.json") 1abcde

1354 data = response.json() 1abcde

1355 assert data == { 1abcde

1356 "openapi": "3.1.0", 

1357 "info": {"title": "FastAPI", "version": "0.1.0"}, 

1358 "paths": { 

1359 "/": { 

1360 "post": { 

1361 "summary": "Post Root", 

1362 "operationId": "baz_post_root", 

1363 "requestBody": { 

1364 "content": { 

1365 "application/json": { 

1366 "schema": { 

1367 "$ref": "#/components/schemas/Body_baz_post_root" 

1368 } 

1369 } 

1370 }, 

1371 "required": True, 

1372 }, 

1373 "responses": { 

1374 "200": { 

1375 "description": "Successful Response", 

1376 "content": { 

1377 "application/json": { 

1378 "schema": { 

1379 "title": "Response Baz Post Root", 

1380 "type": "array", 

1381 "items": {"$ref": "#/components/schemas/Item"}, 

1382 } 

1383 } 

1384 }, 

1385 }, 

1386 "404": { 

1387 "description": "Not Found", 

1388 "content": { 

1389 "application/json": { 

1390 "schema": { 

1391 "title": "Response 404 Baz Post Root", 

1392 "type": "array", 

1393 "items": { 

1394 "$ref": "#/components/schemas/Message" 

1395 }, 

1396 } 

1397 } 

1398 }, 

1399 }, 

1400 "422": { 

1401 "description": "Validation Error", 

1402 "content": { 

1403 "application/json": { 

1404 "schema": { 

1405 "$ref": "#/components/schemas/HTTPValidationError" 

1406 } 

1407 } 

1408 }, 

1409 }, 

1410 }, 

1411 "callbacks": { 

1412 "post_callback": { 

1413 "/post-callback": { 

1414 "post": { 

1415 "summary": "Post Callback", 

1416 "operationId": "baz_post_callback", 

1417 "requestBody": { 

1418 "content": { 

1419 "application/json": { 

1420 "schema": { 

1421 "$ref": "#/components/schemas/Body_baz_post_callback" 

1422 } 

1423 } 

1424 }, 

1425 "required": True, 

1426 }, 

1427 "responses": { 

1428 "200": { 

1429 "description": "Successful Response", 

1430 "content": { 

1431 "application/json": { 

1432 "schema": { 

1433 "title": "Response Baz Post Callback", 

1434 "type": "array", 

1435 "items": { 

1436 "$ref": "#/components/schemas/Item" 

1437 }, 

1438 } 

1439 } 

1440 }, 

1441 }, 

1442 "404": { 

1443 "description": "Not Found", 

1444 "content": { 

1445 "application/json": { 

1446 "schema": { 

1447 "title": "Response 404 Baz Post Callback", 

1448 "type": "array", 

1449 "items": { 

1450 "$ref": "#/components/schemas/Message" 

1451 }, 

1452 } 

1453 } 

1454 }, 

1455 }, 

1456 "422": { 

1457 "description": "Validation Error", 

1458 "content": { 

1459 "application/json": { 

1460 "schema": { 

1461 "$ref": "#/components/schemas/HTTPValidationError" 

1462 } 

1463 } 

1464 }, 

1465 }, 

1466 }, 

1467 } 

1468 } 

1469 } 

1470 }, 

1471 } 

1472 }, 

1473 "/tocallback": { 

1474 "post": { 

1475 "summary": "Post With Callback", 

1476 "operationId": "foo_post_with_callback", 

1477 "requestBody": { 

1478 "content": { 

1479 "application/json": { 

1480 "schema": { 

1481 "$ref": "#/components/schemas/Body_foo_post_with_callback" 

1482 } 

1483 } 

1484 }, 

1485 "required": True, 

1486 }, 

1487 "responses": { 

1488 "200": { 

1489 "description": "Successful Response", 

1490 "content": { 

1491 "application/json": { 

1492 "schema": { 

1493 "title": "Response Foo Post With Callback", 

1494 "type": "array", 

1495 "items": {"$ref": "#/components/schemas/Item"}, 

1496 } 

1497 } 

1498 }, 

1499 }, 

1500 "404": { 

1501 "description": "Not Found", 

1502 "content": { 

1503 "application/json": { 

1504 "schema": { 

1505 "title": "Response 404 Foo Post With Callback", 

1506 "type": "array", 

1507 "items": { 

1508 "$ref": "#/components/schemas/Message" 

1509 }, 

1510 } 

1511 } 

1512 }, 

1513 }, 

1514 "422": { 

1515 "description": "Validation Error", 

1516 "content": { 

1517 "application/json": { 

1518 "schema": { 

1519 "$ref": "#/components/schemas/HTTPValidationError" 

1520 } 

1521 } 

1522 }, 

1523 }, 

1524 }, 

1525 } 

1526 }, 

1527 }, 

1528 "components": { 

1529 "schemas": { 

1530 "Body_baz_post_callback": { 

1531 "title": "Body_baz_post_callback", 

1532 "required": ["item1", "item2"], 

1533 "type": "object", 

1534 "properties": { 

1535 "item1": {"$ref": "#/components/schemas/Item"}, 

1536 "item2": {"$ref": "#/components/schemas/Item"}, 

1537 }, 

1538 }, 

1539 "Body_baz_post_root": { 

1540 "title": "Body_baz_post_root", 

1541 "required": ["item1", "item2"], 

1542 "type": "object", 

1543 "properties": { 

1544 "item1": {"$ref": "#/components/schemas/Item"}, 

1545 "item2": {"$ref": "#/components/schemas/Item"}, 

1546 }, 

1547 }, 

1548 "Body_foo_post_with_callback": { 

1549 "title": "Body_foo_post_with_callback", 

1550 "required": ["item1", "item2"], 

1551 "type": "object", 

1552 "properties": { 

1553 "item1": {"$ref": "#/components/schemas/Item"}, 

1554 "item2": {"$ref": "#/components/schemas/Item"}, 

1555 }, 

1556 }, 

1557 "HTTPValidationError": { 

1558 "title": "HTTPValidationError", 

1559 "type": "object", 

1560 "properties": { 

1561 "detail": { 

1562 "title": "Detail", 

1563 "type": "array", 

1564 "items": {"$ref": "#/components/schemas/ValidationError"}, 

1565 } 

1566 }, 

1567 }, 

1568 "Item": { 

1569 "title": "Item", 

1570 "required": ["name", "price"], 

1571 "type": "object", 

1572 "properties": { 

1573 "name": {"title": "Name", "type": "string"}, 

1574 "price": {"title": "Price", "type": "number"}, 

1575 }, 

1576 }, 

1577 "Message": { 

1578 "title": "Message", 

1579 "required": ["title", "description"], 

1580 "type": "object", 

1581 "properties": { 

1582 "title": {"title": "Title", "type": "string"}, 

1583 "description": {"title": "Description", "type": "string"}, 

1584 }, 

1585 }, 

1586 "ValidationError": { 

1587 "title": "ValidationError", 

1588 "required": ["loc", "msg", "type"], 

1589 "type": "object", 

1590 "properties": { 

1591 "loc": { 

1592 "title": "Location", 

1593 "type": "array", 

1594 "items": { 

1595 "anyOf": [{"type": "string"}, {"type": "integer"}] 

1596 }, 

1597 }, 

1598 "msg": {"title": "Message", "type": "string"}, 

1599 "type": {"title": "Error Type", "type": "string"}, 

1600 }, 

1601 }, 

1602 } 

1603 }, 

1604 } 

1605 

1606 

1607def test_warn_duplicate_operation_id(): 1abcde

1608 def broken_operation_id(route: APIRoute): 1abcde

1609 return "foo" 1abcde

1610 

1611 app = FastAPI(generate_unique_id_function=broken_operation_id) 1abcde

1612 

1613 @app.post("/") 1abcde

1614 def post_root(item1: Item): 1abcde

1615 return item1 # pragma: nocover 

1616 

1617 @app.post("/second") 1abcde

1618 def post_second(item1: Item): 1abcde

1619 return item1 # pragma: nocover 

1620 

1621 @app.post("/third") 1abcde

1622 def post_third(item1: Item): 1abcde

1623 return item1 # pragma: nocover 

1624 

1625 client = TestClient(app) 1abcde

1626 with warnings.catch_warnings(record=True) as w: 1abcde

1627 warnings.simplefilter("always") 1abcde

1628 client.get("/openapi.json") 1abcde

1629 assert len(w) >= 2 1abcde

1630 duplicate_warnings = [ 1abcde

1631 warning for warning in w if issubclass(warning.category, UserWarning) 

1632 ] 

1633 assert len(duplicate_warnings) > 0 1abcde

1634 assert "Duplicate Operation ID" in str(duplicate_warnings[0].message) 1abcde