Coverage for tests/test_openapi_separate_input_output_schemas.py: 100%

72 statements  

« prev     ^ index     » next       coverage.py v7.6.1, created at 2025-01-13 13:38 +0000

1from typing import List, Optional 1abcde

2 

3from fastapi import FastAPI 1abcde

4from fastapi.testclient import TestClient 1abcde

5from pydantic import BaseModel 1abcde

6 

7from .utils import PYDANTIC_V2, needs_pydanticv2 1abcde

8 

9 

10class SubItem(BaseModel): 1abcde

11 subname: str 1abcde

12 sub_description: Optional[str] = None 1abcde

13 tags: List[str] = [] 1abcde

14 if PYDANTIC_V2: 1abcde

15 model_config = {"json_schema_serialization_defaults_required": True} 1abcde

16 

17 

18class Item(BaseModel): 1abcde

19 name: str 1abcde

20 description: Optional[str] = None 1abcde

21 sub: Optional[SubItem] = None 1abcde

22 if PYDANTIC_V2: 1abcde

23 model_config = {"json_schema_serialization_defaults_required": True} 1abcde

24 

25 

26def get_app_client(separate_input_output_schemas: bool = True) -> TestClient: 1abcde

27 app = FastAPI(separate_input_output_schemas=separate_input_output_schemas) 1pfgzAqrhiBCstjkDEuvlmFGwxnoHIy

28 

29 @app.post("/items/", responses={402: {"model": Item}}) 1pfgzAqrhiBCstjkDEuvlmFGwxnoHIy

30 def create_item(item: Item) -> Item: 1pfgzAqrhiBCstjkDEuvlmFGwxnoHIy

31 return item 1pgritkvmxo

32 

33 @app.post("/items-list/") 1pfgzAqrhiBCstjkDEuvlmFGwxnoHIy

34 def create_item_list(item: List[Item]): 1pfgzAqrhiBCstjkDEuvlmFGwxnoHIy

35 return item 1fhjln

36 

37 @app.get("/items/") 1pfgzAqrhiBCstjkDEuvlmFGwxnoHIy

38 def read_items() -> List[Item]: 1pfgzAqrhiBCstjkDEuvlmFGwxnoHIy

39 return [ 1qsuwy

40 Item( 

41 name="Portal Gun", 

42 description="Device to travel through the multi-rick-verse", 

43 sub=SubItem(subname="subname"), 

44 ), 

45 Item(name="Plumbus"), 

46 ] 

47 

48 client = TestClient(app) 1pfgzAqrhiBCstjkDEuvlmFGwxnoHIy

49 return client 1pfgzAqrhiBCstjkDEuvlmFGwxnoHIy

50 

51 

52def test_create_item(): 1abcde

53 client = get_app_client() 1prtvx

54 client_no = get_app_client(separate_input_output_schemas=False) 1prtvx

55 response = client.post("/items/", json={"name": "Plumbus"}) 1prtvx

56 response2 = client_no.post("/items/", json={"name": "Plumbus"}) 1prtvx

57 assert response.status_code == response2.status_code == 200, response.text 1prtvx

58 assert ( 1prtvx

59 response.json() 

60 == response2.json() 

61 == {"name": "Plumbus", "description": None, "sub": None} 

62 ) 

63 

64 

65def test_create_item_with_sub(): 1abcde

66 client = get_app_client() 1gikmo

67 client_no = get_app_client(separate_input_output_schemas=False) 1gikmo

68 data = { 1gikmo

69 "name": "Plumbus", 

70 "sub": {"subname": "SubPlumbus", "sub_description": "Sub WTF"}, 

71 } 

72 response = client.post("/items/", json=data) 1gikmo

73 response2 = client_no.post("/items/", json=data) 1gikmo

74 assert response.status_code == response2.status_code == 200, response.text 1gikmo

75 assert ( 1gikmo

76 response.json() 

77 == response2.json() 

78 == { 

79 "name": "Plumbus", 

80 "description": None, 

81 "sub": {"subname": "SubPlumbus", "sub_description": "Sub WTF", "tags": []}, 

82 } 

83 ) 

84 

85 

86def test_create_item_list(): 1abcde

87 client = get_app_client() 1fhjln

88 client_no = get_app_client(separate_input_output_schemas=False) 1fhjln

89 data = [ 1fhjln

90 {"name": "Plumbus"}, 

91 { 

92 "name": "Portal Gun", 

93 "description": "Device to travel through the multi-rick-verse", 

94 }, 

95 ] 

96 response = client.post("/items-list/", json=data) 1fhjln

97 response2 = client_no.post("/items-list/", json=data) 1fhjln

98 assert response.status_code == response2.status_code == 200, response.text 1fhjln

99 assert ( 1fhjln

100 response.json() 

101 == response2.json() 

102 == [ 

103 {"name": "Plumbus", "description": None, "sub": None}, 

104 { 

105 "name": "Portal Gun", 

106 "description": "Device to travel through the multi-rick-verse", 

107 "sub": None, 

108 }, 

109 ] 

110 ) 

111 

112 

113def test_read_items(): 1abcde

114 client = get_app_client() 1qsuwy

115 client_no = get_app_client(separate_input_output_schemas=False) 1qsuwy

116 response = client.get("/items/") 1qsuwy

117 response2 = client_no.get("/items/") 1qsuwy

118 assert response.status_code == response2.status_code == 200, response.text 1qsuwy

119 assert ( 1qsuwy

120 response.json() 

121 == response2.json() 

122 == [ 

123 { 

124 "name": "Portal Gun", 

125 "description": "Device to travel through the multi-rick-verse", 

126 "sub": {"subname": "subname", "sub_description": None, "tags": []}, 

127 }, 

128 {"name": "Plumbus", "description": None, "sub": None}, 

129 ] 

130 ) 

131 

132 

133@needs_pydanticv2 1abcde

134def test_openapi_schema(): 1abcde

135 client = get_app_client() 1zBDFH

136 response = client.get("/openapi.json") 1zBDFH

137 assert response.status_code == 200, response.text 1zBDFH

138 assert response.json() == { 1zBDFH

139 "openapi": "3.1.0", 

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

141 "paths": { 

142 "/items/": { 

143 "get": { 

144 "summary": "Read Items", 

145 "operationId": "read_items_items__get", 

146 "responses": { 

147 "200": { 

148 "description": "Successful Response", 

149 "content": { 

150 "application/json": { 

151 "schema": { 

152 "items": { 

153 "$ref": "#/components/schemas/Item-Output" 

154 }, 

155 "type": "array", 

156 "title": "Response Read Items Items Get", 

157 } 

158 } 

159 }, 

160 } 

161 }, 

162 }, 

163 "post": { 

164 "summary": "Create Item", 

165 "operationId": "create_item_items__post", 

166 "requestBody": { 

167 "content": { 

168 "application/json": { 

169 "schema": {"$ref": "#/components/schemas/Item-Input"} 

170 } 

171 }, 

172 "required": True, 

173 }, 

174 "responses": { 

175 "200": { 

176 "description": "Successful Response", 

177 "content": { 

178 "application/json": { 

179 "schema": { 

180 "$ref": "#/components/schemas/Item-Output" 

181 } 

182 } 

183 }, 

184 }, 

185 "402": { 

186 "description": "Payment Required", 

187 "content": { 

188 "application/json": { 

189 "schema": { 

190 "$ref": "#/components/schemas/Item-Output" 

191 } 

192 } 

193 }, 

194 }, 

195 "422": { 

196 "description": "Validation Error", 

197 "content": { 

198 "application/json": { 

199 "schema": { 

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

201 } 

202 } 

203 }, 

204 }, 

205 }, 

206 }, 

207 }, 

208 "/items-list/": { 

209 "post": { 

210 "summary": "Create Item List", 

211 "operationId": "create_item_list_items_list__post", 

212 "requestBody": { 

213 "content": { 

214 "application/json": { 

215 "schema": { 

216 "items": { 

217 "$ref": "#/components/schemas/Item-Input" 

218 }, 

219 "type": "array", 

220 "title": "Item", 

221 } 

222 } 

223 }, 

224 "required": True, 

225 }, 

226 "responses": { 

227 "200": { 

228 "description": "Successful Response", 

229 "content": {"application/json": {"schema": {}}}, 

230 }, 

231 "422": { 

232 "description": "Validation Error", 

233 "content": { 

234 "application/json": { 

235 "schema": { 

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

237 } 

238 } 

239 }, 

240 }, 

241 }, 

242 } 

243 }, 

244 }, 

245 "components": { 

246 "schemas": { 

247 "HTTPValidationError": { 

248 "properties": { 

249 "detail": { 

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

251 "type": "array", 

252 "title": "Detail", 

253 } 

254 }, 

255 "type": "object", 

256 "title": "HTTPValidationError", 

257 }, 

258 "Item-Input": { 

259 "properties": { 

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

261 "description": { 

262 "anyOf": [{"type": "string"}, {"type": "null"}], 

263 "title": "Description", 

264 }, 

265 "sub": { 

266 "anyOf": [ 

267 {"$ref": "#/components/schemas/SubItem-Input"}, 

268 {"type": "null"}, 

269 ] 

270 }, 

271 }, 

272 "type": "object", 

273 "required": ["name"], 

274 "title": "Item", 

275 }, 

276 "Item-Output": { 

277 "properties": { 

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

279 "description": { 

280 "anyOf": [{"type": "string"}, {"type": "null"}], 

281 "title": "Description", 

282 }, 

283 "sub": { 

284 "anyOf": [ 

285 {"$ref": "#/components/schemas/SubItem-Output"}, 

286 {"type": "null"}, 

287 ] 

288 }, 

289 }, 

290 "type": "object", 

291 "required": ["name", "description", "sub"], 

292 "title": "Item", 

293 }, 

294 "SubItem-Input": { 

295 "properties": { 

296 "subname": {"type": "string", "title": "Subname"}, 

297 "sub_description": { 

298 "anyOf": [{"type": "string"}, {"type": "null"}], 

299 "title": "Sub Description", 

300 }, 

301 "tags": { 

302 "items": {"type": "string"}, 

303 "type": "array", 

304 "title": "Tags", 

305 "default": [], 

306 }, 

307 }, 

308 "type": "object", 

309 "required": ["subname"], 

310 "title": "SubItem", 

311 }, 

312 "SubItem-Output": { 

313 "properties": { 

314 "subname": {"type": "string", "title": "Subname"}, 

315 "sub_description": { 

316 "anyOf": [{"type": "string"}, {"type": "null"}], 

317 "title": "Sub Description", 

318 }, 

319 "tags": { 

320 "items": {"type": "string"}, 

321 "type": "array", 

322 "title": "Tags", 

323 "default": [], 

324 }, 

325 }, 

326 "type": "object", 

327 "required": ["subname", "sub_description", "tags"], 

328 "title": "SubItem", 

329 }, 

330 "ValidationError": { 

331 "properties": { 

332 "loc": { 

333 "items": { 

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

335 }, 

336 "type": "array", 

337 "title": "Location", 

338 }, 

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

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

341 }, 

342 "type": "object", 

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

344 "title": "ValidationError", 

345 }, 

346 } 

347 }, 

348 } 

349 

350 

351@needs_pydanticv2 1abcde

352def test_openapi_schema_no_separate(): 1abcde

353 client = get_app_client(separate_input_output_schemas=False) 1ACEGI

354 response = client.get("/openapi.json") 1ACEGI

355 assert response.status_code == 200, response.text 1ACEGI

356 assert response.json() == { 1ACEGI

357 "openapi": "3.1.0", 

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

359 "paths": { 

360 "/items/": { 

361 "get": { 

362 "summary": "Read Items", 

363 "operationId": "read_items_items__get", 

364 "responses": { 

365 "200": { 

366 "description": "Successful Response", 

367 "content": { 

368 "application/json": { 

369 "schema": { 

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

371 "type": "array", 

372 "title": "Response Read Items Items Get", 

373 } 

374 } 

375 }, 

376 } 

377 }, 

378 }, 

379 "post": { 

380 "summary": "Create Item", 

381 "operationId": "create_item_items__post", 

382 "requestBody": { 

383 "content": { 

384 "application/json": { 

385 "schema": {"$ref": "#/components/schemas/Item"} 

386 } 

387 }, 

388 "required": True, 

389 }, 

390 "responses": { 

391 "200": { 

392 "description": "Successful Response", 

393 "content": { 

394 "application/json": { 

395 "schema": {"$ref": "#/components/schemas/Item"} 

396 } 

397 }, 

398 }, 

399 "402": { 

400 "description": "Payment Required", 

401 "content": { 

402 "application/json": { 

403 "schema": {"$ref": "#/components/schemas/Item"} 

404 } 

405 }, 

406 }, 

407 "422": { 

408 "description": "Validation Error", 

409 "content": { 

410 "application/json": { 

411 "schema": { 

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

413 } 

414 } 

415 }, 

416 }, 

417 }, 

418 }, 

419 }, 

420 "/items-list/": { 

421 "post": { 

422 "summary": "Create Item List", 

423 "operationId": "create_item_list_items_list__post", 

424 "requestBody": { 

425 "content": { 

426 "application/json": { 

427 "schema": { 

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

429 "type": "array", 

430 "title": "Item", 

431 } 

432 } 

433 }, 

434 "required": True, 

435 }, 

436 "responses": { 

437 "200": { 

438 "description": "Successful Response", 

439 "content": {"application/json": {"schema": {}}}, 

440 }, 

441 "422": { 

442 "description": "Validation Error", 

443 "content": { 

444 "application/json": { 

445 "schema": { 

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

447 } 

448 } 

449 }, 

450 }, 

451 }, 

452 } 

453 }, 

454 }, 

455 "components": { 

456 "schemas": { 

457 "HTTPValidationError": { 

458 "properties": { 

459 "detail": { 

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

461 "type": "array", 

462 "title": "Detail", 

463 } 

464 }, 

465 "type": "object", 

466 "title": "HTTPValidationError", 

467 }, 

468 "Item": { 

469 "properties": { 

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

471 "description": { 

472 "anyOf": [{"type": "string"}, {"type": "null"}], 

473 "title": "Description", 

474 }, 

475 "sub": { 

476 "anyOf": [ 

477 {"$ref": "#/components/schemas/SubItem"}, 

478 {"type": "null"}, 

479 ] 

480 }, 

481 }, 

482 "type": "object", 

483 "required": ["name"], 

484 "title": "Item", 

485 }, 

486 "SubItem": { 

487 "properties": { 

488 "subname": {"type": "string", "title": "Subname"}, 

489 "sub_description": { 

490 "anyOf": [{"type": "string"}, {"type": "null"}], 

491 "title": "Sub Description", 

492 }, 

493 "tags": { 

494 "items": {"type": "string"}, 

495 "type": "array", 

496 "title": "Tags", 

497 "default": [], 

498 }, 

499 }, 

500 "type": "object", 

501 "required": ["subname"], 

502 "title": "SubItem", 

503 }, 

504 "ValidationError": { 

505 "properties": { 

506 "loc": { 

507 "items": { 

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

509 }, 

510 "type": "array", 

511 "title": "Location", 

512 }, 

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

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

515 }, 

516 "type": "object", 

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

518 "title": "ValidationError", 

519 }, 

520 } 

521 }, 

522 }