Coverage for tests / test_request_params / test_header / test_list.py: 100%

124 statements  

« prev     ^ index     » next       coverage.py v7.13.3, created at 2026-02-12 18:15 +0000

1from typing import Annotated 1abcd

2 

3import pytest 1abcd

4from dirty_equals import AnyThing, IsOneOf, IsPartialDict 1abcd

5from fastapi import FastAPI, Header 1abcd

6from fastapi.testclient import TestClient 1abcd

7from inline_snapshot import snapshot 1abcd

8from pydantic import BaseModel, Field 1abcd

9 

10app = FastAPI() 1abcd

11 

12# ===================================================================================== 

13# Without aliases 

14 

15 

16@app.get("/required-list-str") 1abcd

17async def read_required_list_str(p: Annotated[list[str], Header()]): 1abcd

18 return {"p": p} 1efg

19 

20 

21class HeaderModelRequiredListStr(BaseModel): 1abcd

22 p: list[str] 1abcd

23 

24 

25@app.get("/model-required-list-str") 1abcd

26def read_model_required_list_str(p: Annotated[HeaderModelRequiredListStr, Header()]): 1abcd

27 return {"p": p.p} 1efg

28 

29 

30@pytest.mark.parametrize( 1abcd

31 "path", 

32 ["/required-list-str", "/model-required-list-str"], 

33) 

34def test_required_list_str_schema(path: str): 1abcd

35 assert app.openapi()["paths"][path]["get"]["parameters"] == snapshot( 1OPQ

36 [ 

37 { 

38 "required": True, 

39 "schema": { 

40 "title": "P", 

41 "type": "array", 

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

43 }, 

44 "name": "p", 

45 "in": "header", 

46 } 

47 ] 

48 ) 

49 

50 

51@pytest.mark.parametrize( 1abcd

52 "path", 

53 ["/required-list-str", "/model-required-list-str"], 

54) 

55def test_required_list_str_missing(path: str): 1abcd

56 client = TestClient(app) 1qrs

57 response = client.get(path) 1qrs

58 assert response.status_code == 422 1qrs

59 assert response.json() == { 1qrs

60 "detail": [ 

61 { 

62 "type": "missing", 

63 "loc": ["header", "p"], 

64 "msg": "Field required", 

65 "input": AnyThing, 

66 } 

67 ] 

68 } 

69 

70 

71@pytest.mark.parametrize( 1abcd

72 "path", 

73 ["/required-list-str", "/model-required-list-str"], 

74) 

75def test_required_list_str(path: str): 1abcd

76 client = TestClient(app) 1efg

77 response = client.get(path, headers=[("p", "hello"), ("p", "world")]) 1efg

78 assert response.status_code == 200 1efg

79 assert response.json() == {"p": ["hello", "world"]} 1efg

80 

81 

82# ===================================================================================== 

83# Alias 

84 

85 

86@app.get("/required-list-alias") 1abcd

87async def read_required_list_alias(p: Annotated[list[str], Header(alias="p_alias")]): 1abcd

88 return {"p": p} 1hij

89 

90 

91class HeaderModelRequiredListAlias(BaseModel): 1abcd

92 p: list[str] = Field(alias="p_alias") 1abcd

93 

94 

95@app.get("/model-required-list-alias") 1abcd

96async def read_model_required_list_alias( 1abcd

97 p: Annotated[HeaderModelRequiredListAlias, Header()], 

98): 

99 return {"p": p.p} 1hij

100 

101 

102@pytest.mark.parametrize( 1abcd

103 "path", 

104 ["/required-list-alias", "/model-required-list-alias"], 

105) 

106def test_required_list_str_alias_schema(path: str): 1abcd

107 assert app.openapi()["paths"][path]["get"]["parameters"] == snapshot( 1RST

108 [ 

109 { 

110 "required": True, 

111 "schema": { 

112 "title": "P Alias", 

113 "type": "array", 

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

115 }, 

116 "name": "p_alias", 

117 "in": "header", 

118 } 

119 ] 

120 ) 

121 

122 

123@pytest.mark.parametrize( 1abcd

124 "path", 

125 ["/required-list-alias", "/model-required-list-alias"], 

126) 

127def test_required_list_alias_missing(path: str): 1abcd

128 client = TestClient(app) 1tuv

129 response = client.get(path) 1tuv

130 assert response.status_code == 422 1tuv

131 assert response.json() == { 1tuv

132 "detail": [ 

133 { 

134 "type": "missing", 

135 "loc": ["header", "p_alias"], 

136 "msg": "Field required", 

137 "input": AnyThing, 

138 } 

139 ] 

140 } 

141 

142 

143@pytest.mark.parametrize( 1abcd

144 "path", 

145 [ 

146 "/required-list-alias", 

147 "/model-required-list-alias", 

148 ], 

149) 

150def test_required_list_alias_by_name(path: str): 1abcd

151 client = TestClient(app) 1wxy

152 response = client.get(path, headers=[("p", "hello"), ("p", "world")]) 1wxy

153 assert response.status_code == 422 1wxy

154 assert response.json() == { 1wxy

155 "detail": [ 

156 { 

157 "type": "missing", 

158 "loc": ["header", "p_alias"], 

159 "msg": "Field required", 

160 "input": IsOneOf(None, IsPartialDict({"p": ["hello", "world"]})), 

161 } 

162 ] 

163 } 

164 

165 

166@pytest.mark.parametrize( 1abcd

167 "path", 

168 [ 

169 "/required-list-alias", 

170 "/model-required-list-alias", 

171 ], 

172) 

173def test_required_list_alias_by_alias(path: str): 1abcd

174 client = TestClient(app) 1hij

175 response = client.get(path, headers=[("p_alias", "hello"), ("p_alias", "world")]) 1hij

176 assert response.status_code == 200, response.text 1hij

177 assert response.json() == {"p": ["hello", "world"]} 1hij

178 

179 

180# ===================================================================================== 

181# Validation alias 

182 

183 

184@app.get("/required-list-validation-alias") 1abcd

185def read_required_list_validation_alias( 1abcd

186 p: Annotated[list[str], Header(validation_alias="p_val_alias")], 

187): 

188 return {"p": p} 1klm

189 

190 

191class HeaderModelRequiredListValidationAlias(BaseModel): 1abcd

192 p: list[str] = Field(validation_alias="p_val_alias") 1abcd

193 

194 

195@app.get("/model-required-list-validation-alias") 1abcd

196async def read_model_required_list_validation_alias( 1abcd

197 p: Annotated[HeaderModelRequiredListValidationAlias, Header()], 

198): 

199 return {"p": p.p} 1klm

200 

201 

202@pytest.mark.parametrize( 1abcd

203 "path", 

204 ["/required-list-validation-alias", "/model-required-list-validation-alias"], 

205) 

206def test_required_list_validation_alias_schema(path: str): 1abcd

207 assert app.openapi()["paths"][path]["get"]["parameters"] == snapshot( 1UVW

208 [ 

209 { 

210 "required": True, 

211 "schema": { 

212 "title": "P Val Alias", 

213 "type": "array", 

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

215 }, 

216 "name": "p_val_alias", 

217 "in": "header", 

218 } 

219 ] 

220 ) 

221 

222 

223@pytest.mark.parametrize( 1abcd

224 "path", 

225 [ 

226 "/required-list-validation-alias", 

227 "/model-required-list-validation-alias", 

228 ], 

229) 

230def test_required_list_validation_alias_missing(path: str): 1abcd

231 client = TestClient(app) 1zAB

232 response = client.get(path) 1zAB

233 assert response.status_code == 422 1zAB

234 assert response.json() == { 1zAB

235 "detail": [ 

236 { 

237 "type": "missing", 

238 "loc": [ 

239 "header", 

240 "p_val_alias", 

241 ], 

242 "msg": "Field required", 

243 "input": AnyThing, 

244 } 

245 ] 

246 } 

247 

248 

249@pytest.mark.parametrize( 1abcd

250 "path", 

251 [ 

252 "/required-list-validation-alias", 

253 "/model-required-list-validation-alias", 

254 ], 

255) 

256def test_required_list_validation_alias_by_name(path: str): 1abcd

257 client = TestClient(app) 1CDE

258 response = client.get(path, headers=[("p", "hello"), ("p", "world")]) 1CDE

259 assert response.status_code == 422 1CDE

260 

261 assert response.json() == { 1CDE

262 "detail": [ 

263 { 

264 "type": "missing", 

265 "loc": ["header", "p_val_alias"], 

266 "msg": "Field required", 

267 "input": IsOneOf(None, IsPartialDict({"p": ["hello", "world"]})), 

268 } 

269 ] 

270 } 

271 

272 

273@pytest.mark.parametrize( 1abcd

274 "path", 

275 ["/required-list-validation-alias", "/model-required-list-validation-alias"], 

276) 

277def test_required_list_validation_alias_by_validation_alias(path: str): 1abcd

278 client = TestClient(app) 1klm

279 response = client.get( 1klm

280 path, headers=[("p_val_alias", "hello"), ("p_val_alias", "world")] 

281 ) 

282 assert response.status_code == 200, response.text 1klm

283 

284 assert response.json() == {"p": ["hello", "world"]} 1klm

285 

286 

287# ===================================================================================== 

288# Alias and validation alias 

289 

290 

291@app.get("/required-list-alias-and-validation-alias") 1abcd

292def read_required_list_alias_and_validation_alias( 1abcd

293 p: Annotated[list[str], Header(alias="p_alias", validation_alias="p_val_alias")], 

294): 

295 return {"p": p} 1nop

296 

297 

298class HeaderModelRequiredListAliasAndValidationAlias(BaseModel): 1abcd

299 p: list[str] = Field(alias="p_alias", validation_alias="p_val_alias") 1abcd

300 

301 

302@app.get("/model-required-list-alias-and-validation-alias") 1abcd

303def read_model_required_list_alias_and_validation_alias( 1abcd

304 p: Annotated[HeaderModelRequiredListAliasAndValidationAlias, Header()], 

305): 

306 return {"p": p.p} 1nop

307 

308 

309@pytest.mark.parametrize( 1abcd

310 "path", 

311 [ 

312 "/required-list-alias-and-validation-alias", 

313 "/model-required-list-alias-and-validation-alias", 

314 ], 

315) 

316def test_required_list_alias_and_validation_alias_schema(path: str): 1abcd

317 assert app.openapi()["paths"][path]["get"]["parameters"] == snapshot( 1XYZ

318 [ 

319 { 

320 "required": True, 

321 "schema": { 

322 "title": "P Val Alias", 

323 "type": "array", 

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

325 }, 

326 "name": "p_val_alias", 

327 "in": "header", 

328 } 

329 ] 

330 ) 

331 

332 

333@pytest.mark.parametrize( 1abcd

334 "path", 

335 [ 

336 "/required-list-alias-and-validation-alias", 

337 "/model-required-list-alias-and-validation-alias", 

338 ], 

339) 

340def test_required_list_alias_and_validation_alias_missing(path: str): 1abcd

341 client = TestClient(app) 1FGH

342 response = client.get(path) 1FGH

343 assert response.status_code == 422 1FGH

344 assert response.json() == { 1FGH

345 "detail": [ 

346 { 

347 "type": "missing", 

348 "loc": [ 

349 "header", 

350 "p_val_alias", 

351 ], 

352 "msg": "Field required", 

353 "input": AnyThing, 

354 } 

355 ] 

356 } 

357 

358 

359@pytest.mark.parametrize( 1abcd

360 "path", 

361 [ 

362 "/required-list-alias-and-validation-alias", 

363 "/model-required-list-alias-and-validation-alias", 

364 ], 

365) 

366def test_required_list_alias_and_validation_alias_by_name(path: str): 1abcd

367 client = TestClient(app) 1IJK

368 response = client.get(path, headers=[("p", "hello"), ("p", "world")]) 1IJK

369 assert response.status_code == 422 1IJK

370 assert response.json() == { 1IJK

371 "detail": [ 

372 { 

373 "type": "missing", 

374 "loc": [ 

375 "header", 

376 "p_val_alias", 

377 ], 

378 "msg": "Field required", 

379 "input": IsOneOf( 

380 None, 

381 IsPartialDict({"p": ["hello", "world"]}), 

382 ), 

383 } 

384 ] 

385 } 

386 

387 

388@pytest.mark.parametrize( 1abcd

389 "path", 

390 [ 

391 "/required-list-alias-and-validation-alias", 

392 "/model-required-list-alias-and-validation-alias", 

393 ], 

394) 

395def test_required_list_alias_and_validation_alias_by_alias(path: str): 1abcd

396 client = TestClient(app) 1LMN

397 response = client.get(path, headers=[("p_alias", "hello"), ("p_alias", "world")]) 1LMN

398 assert response.status_code == 422 1LMN

399 assert response.json() == { 1LMN

400 "detail": [ 

401 { 

402 "type": "missing", 

403 "loc": ["header", "p_val_alias"], 

404 "msg": "Field required", 

405 "input": IsOneOf( 

406 None, 

407 IsPartialDict({"p_alias": ["hello", "world"]}), 

408 ), 

409 } 

410 ] 

411 } 

412 

413 

414@pytest.mark.parametrize( 1abcd

415 "path", 

416 [ 

417 "/required-list-alias-and-validation-alias", 

418 "/model-required-list-alias-and-validation-alias", 

419 ], 

420) 

421def test_required_list_alias_and_validation_alias_by_validation_alias(path: str): 1abcd

422 client = TestClient(app) 1nop

423 response = client.get( 1nop

424 path, headers=[("p_val_alias", "hello"), ("p_val_alias", "world")] 

425 ) 

426 assert response.status_code == 200, response.text 1nop

427 assert response.json() == {"p": ["hello", "world"]} 1nop