Coverage for tests / test_request_params / test_file / test_optional_list.py: 100%
122 statements
« prev ^ index » next coverage.py v7.13.3, created at 2026-02-21 17:29 +0000
« prev ^ index » next coverage.py v7.13.3, created at 2026-02-21 17:29 +0000
1from typing import Annotated 1abcd
3import pytest 1abcd
4from fastapi import FastAPI, File, UploadFile 1abcd
5from fastapi.testclient import TestClient 1abcd
7from .utils import get_body_model_name 1abcd
9app = FastAPI() 1abcd
11# =====================================================================================
12# Without aliases
15@app.post("/optional-list-bytes") 1abcd
16async def read_optional_list_bytes(p: Annotated[list[bytes] | None, File()] = None): 1abcd
17 return {"file_size": [len(file) for file in p] if p else None} 1efghij
20@app.post("/optional-list-uploadfile") 1abcd
21async def read_optional_list_uploadfile( 1abcd
22 p: Annotated[list[UploadFile] | None, File()] = None,
23):
24 return {"file_size": [file.size for file in p] if p else None} 1efghij
27@pytest.mark.parametrize( 1abcd
28 "path",
29 [
30 "/optional-list-bytes",
31 "/optional-list-uploadfile",
32 ],
33)
34def test_optional_list_schema(path: str): 1abcd
35 openapi = app.openapi() 1OPQ
36 body_model_name = get_body_model_name(openapi, path) 1OPQ
38 assert app.openapi()["components"]["schemas"][body_model_name] == { 1OPQ
39 "properties": {
40 "p": {
41 "anyOf": [
42 {
43 "type": "array",
44 "items": {
45 "type": "string",
46 "contentMediaType": "application/octet-stream",
47 },
48 },
49 {"type": "null"},
50 ],
51 "title": "P",
52 }
53 },
54 "title": body_model_name,
55 "type": "object",
56 }
59@pytest.mark.parametrize( 1abcd
60 "path",
61 [
62 "/optional-list-bytes",
63 "/optional-list-uploadfile",
64 ],
65)
66def test_optional_list_missing(path: str): 1abcd
67 client = TestClient(app) 1fhj
68 response = client.post(path) 1fhj
69 assert response.status_code == 200, response.text 1fhj
70 assert response.json() == {"file_size": None} 1fhj
73@pytest.mark.parametrize( 1abcd
74 "path",
75 [
76 "/optional-list-bytes",
77 "/optional-list-uploadfile",
78 ],
79)
80def test_optional_list(path: str): 1abcd
81 client = TestClient(app) 1egi
82 response = client.post(path, files=[("p", b"hello"), ("p", b"world")]) 1egi
83 assert response.status_code == 200 1egi
84 assert response.json() == {"file_size": [5, 5]} 1egi
87# =====================================================================================
88# Alias
91@app.post("/optional-list-bytes-alias") 1abcd
92async def read_optional_list_bytes_alias( 1abcd
93 p: Annotated[list[bytes] | None, File(alias="p_alias")] = None,
94):
95 return {"file_size": [len(file) for file in p] if p else None} 1klmnopqrs
98@app.post("/optional-list-uploadfile-alias") 1abcd
99async def read_optional_list_uploadfile_alias( 1abcd
100 p: Annotated[list[UploadFile] | None, File(alias="p_alias")] = None,
101):
102 return {"file_size": [file.size for file in p] if p else None} 1klmnopqrs
105@pytest.mark.parametrize( 1abcd
106 "path",
107 [
108 "/optional-list-bytes-alias",
109 "/optional-list-uploadfile-alias",
110 ],
111)
112def test_optional_list_alias_schema(path: str): 1abcd
113 openapi = app.openapi() 1RST
114 body_model_name = get_body_model_name(openapi, path) 1RST
116 assert app.openapi()["components"]["schemas"][body_model_name] == { 1RST
117 "properties": {
118 "p_alias": {
119 "anyOf": [
120 {
121 "type": "array",
122 "items": {
123 "type": "string",
124 "contentMediaType": "application/octet-stream",
125 },
126 },
127 {"type": "null"},
128 ],
129 "title": "P Alias",
130 }
131 },
132 "title": body_model_name,
133 "type": "object",
134 }
137@pytest.mark.parametrize( 1abcd
138 "path",
139 [
140 "/optional-list-bytes-alias",
141 "/optional-list-uploadfile-alias",
142 ],
143)
144def test_optional_list_alias_missing(path: str): 1abcd
145 client = TestClient(app) 1mps
146 response = client.post(path) 1mps
147 assert response.status_code == 200 1mps
148 assert response.json() == {"file_size": None} 1mps
151@pytest.mark.parametrize( 1abcd
152 "path",
153 [
154 "/optional-list-bytes-alias",
155 "/optional-list-uploadfile-alias",
156 ],
157)
158def test_optional_list_alias_by_name(path: str): 1abcd
159 client = TestClient(app) 1lor
160 response = client.post(path, files=[("p", b"hello"), ("p", b"world")]) 1lor
161 assert response.status_code == 200, response.text 1lor
162 assert response.json() == {"file_size": None} 1lor
165@pytest.mark.parametrize( 1abcd
166 "path",
167 [
168 "/optional-list-bytes-alias",
169 "/optional-list-uploadfile-alias",
170 ],
171)
172def test_optional_list_alias_by_alias(path: str): 1abcd
173 client = TestClient(app) 1knq
174 response = client.post(path, files=[("p_alias", b"hello"), ("p_alias", b"world")]) 1knq
175 assert response.status_code == 200, response.text 1knq
176 assert response.json() == {"file_size": [5, 5]} 1knq
179# =====================================================================================
180# Validation alias
183@app.post("/optional-list-bytes-validation-alias") 1abcd
184def read_optional_list_bytes_validation_alias( 1abcd
185 p: Annotated[list[bytes] | None, File(validation_alias="p_val_alias")] = None,
186):
187 return {"file_size": [len(file) for file in p] if p else None} 1tuvwxyzAB
190@app.post("/optional-list-uploadfile-validation-alias") 1abcd
191def read_optional_list_uploadfile_validation_alias( 1abcd
192 p: Annotated[list[UploadFile] | None, File(validation_alias="p_val_alias")] = None,
193):
194 return {"file_size": [file.size for file in p] if p else None} 1tuvwxyzAB
197@pytest.mark.parametrize( 1abcd
198 "path",
199 [
200 "/optional-list-bytes-validation-alias",
201 "/optional-list-uploadfile-validation-alias",
202 ],
203)
204def test_optional_validation_alias_schema(path: str): 1abcd
205 openapi = app.openapi() 1UVW
206 body_model_name = get_body_model_name(openapi, path) 1UVW
208 assert app.openapi()["components"]["schemas"][body_model_name] == { 1UVW
209 "properties": {
210 "p_val_alias": {
211 "anyOf": [
212 {
213 "type": "array",
214 "items": {
215 "type": "string",
216 "contentMediaType": "application/octet-stream",
217 },
218 },
219 {"type": "null"},
220 ],
221 "title": "P Val Alias",
222 }
223 },
224 "title": body_model_name,
225 "type": "object",
226 }
229@pytest.mark.parametrize( 1abcd
230 "path",
231 [
232 "/optional-list-bytes-validation-alias",
233 "/optional-list-uploadfile-validation-alias",
234 ],
235)
236def test_optional_validation_alias_missing(path: str): 1abcd
237 client = TestClient(app) 1vyB
238 response = client.post(path) 1vyB
239 assert response.status_code == 200 1vyB
240 assert response.json() == {"file_size": None} 1vyB
243@pytest.mark.parametrize( 1abcd
244 "path",
245 [
246 "/optional-list-bytes-validation-alias",
247 "/optional-list-uploadfile-validation-alias",
248 ],
249)
250def test_optional_validation_alias_by_name(path: str): 1abcd
251 client = TestClient(app) 1twz
252 response = client.post(path, files=[("p", b"hello"), ("p", b"world")]) 1twz
253 assert response.status_code == 200, response.text 1twz
254 assert response.json() == {"file_size": None} 1twz
257@pytest.mark.parametrize( 1abcd
258 "path",
259 [
260 "/optional-list-bytes-validation-alias",
261 "/optional-list-uploadfile-validation-alias",
262 ],
263)
264def test_optional_validation_alias_by_validation_alias(path: str): 1abcd
265 client = TestClient(app) 1uxA
266 response = client.post( 1uxA
267 path, files=[("p_val_alias", b"hello"), ("p_val_alias", b"world")]
268 )
269 assert response.status_code == 200, response.text 1uxA
270 assert response.json() == {"file_size": [5, 5]} 1uxA
273# =====================================================================================
274# Alias and validation alias
277@app.post("/optional-list-bytes-alias-and-validation-alias") 1abcd
278def read_optional_list_bytes_alias_and_validation_alias( 1abcd
279 p: Annotated[
280 list[bytes] | None, File(alias="p_alias", validation_alias="p_val_alias")
281 ] = None,
282):
283 return {"file_size": [len(file) for file in p] if p else None} 1CDEFGHIJKLMN
286@app.post("/optional-list-uploadfile-alias-and-validation-alias") 1abcd
287def read_optional_list_uploadfile_alias_and_validation_alias( 1abcd
288 p: Annotated[
289 list[UploadFile] | None,
290 File(alias="p_alias", validation_alias="p_val_alias"),
291 ] = None,
292):
293 return {"file_size": [file.size for file in p] if p else None} 1CDEFGHIJKLMN
296@pytest.mark.parametrize( 1abcd
297 "path",
298 [
299 "/optional-list-bytes-alias-and-validation-alias",
300 "/optional-list-uploadfile-alias-and-validation-alias",
301 ],
302)
303def test_optional_list_alias_and_validation_alias_schema(path: str): 1abcd
304 openapi = app.openapi() 1XYZ
305 body_model_name = get_body_model_name(openapi, path) 1XYZ
307 assert app.openapi()["components"]["schemas"][body_model_name] == { 1XYZ
308 "properties": {
309 "p_val_alias": {
310 "anyOf": [
311 {
312 "type": "array",
313 "items": {
314 "type": "string",
315 "contentMediaType": "application/octet-stream",
316 },
317 },
318 {"type": "null"},
319 ],
320 "title": "P Val Alias",
321 }
322 },
323 "title": body_model_name,
324 "type": "object",
325 }
328@pytest.mark.parametrize( 1abcd
329 "path",
330 [
331 "/optional-list-bytes-alias-and-validation-alias",
332 "/optional-list-uploadfile-alias-and-validation-alias",
333 ],
334)
335def test_optional_list_alias_and_validation_alias_missing(path: str): 1abcd
336 client = TestClient(app) 1FJN
337 response = client.post(path) 1FJN
338 assert response.status_code == 200 1FJN
339 assert response.json() == {"file_size": None} 1FJN
342@pytest.mark.parametrize( 1abcd
343 "path",
344 [
345 "/optional-list-bytes-alias-and-validation-alias",
346 "/optional-list-uploadfile-alias-and-validation-alias",
347 ],
348)
349def test_optional_list_alias_and_validation_alias_by_name(path: str): 1abcd
350 client = TestClient(app) 1DHL
351 response = client.post(path, files={"p": "hello"}) 1DHL
352 assert response.status_code == 200 1DHL
353 assert response.json() == {"file_size": None} 1DHL
356@pytest.mark.parametrize( 1abcd
357 "path",
358 [
359 "/optional-list-bytes-alias-and-validation-alias",
360 "/optional-list-uploadfile-alias-and-validation-alias",
361 ],
362)
363def test_optional_list_alias_and_validation_alias_by_alias(path: str): 1abcd
364 client = TestClient(app) 1CGK
365 response = client.post(path, files=[("p_alias", b"hello"), ("p_alias", b"world")]) 1CGK
366 assert response.status_code == 200, response.text 1CGK
367 assert response.json() == {"file_size": None} 1CGK
370@pytest.mark.parametrize( 1abcd
371 "path",
372 [
373 "/optional-list-bytes-alias-and-validation-alias",
374 "/optional-list-uploadfile-alias-and-validation-alias",
375 ],
376)
377def test_optional_list_alias_and_validation_alias_by_validation_alias(path: str): 1abcd
378 client = TestClient(app) 1EIM
379 response = client.post( 1EIM
380 path, files=[("p_val_alias", b"hello"), ("p_val_alias", b"world")]
381 )
382 assert response.status_code == 200, response.text 1EIM
383 assert response.json() == {"file_size": [5, 5]} 1EIM