Remove tests for `File` fields declared with models

This commit is contained in:
Yurii Motov 2025-12-10 18:54:33 +01:00
parent 5a3cd6da2d
commit d01931c02b
4 changed files with 47 additions and 992 deletions

View File

@ -1,11 +1,10 @@
from typing import List from typing import List
import pytest import pytest
from dirty_equals import IsDict, IsOneOf, IsPartialDict from dirty_equals import IsDict
from fastapi import FastAPI, File, Form, UploadFile from fastapi import FastAPI, File, UploadFile
from fastapi._compat import PYDANTIC_V2 from fastapi._compat import PYDANTIC_V2
from fastapi.testclient import TestClient from fastapi.testclient import TestClient
from pydantic import BaseModel
from typing_extensions import Annotated from typing_extensions import Annotated
from tests.utils import needs_pydanticv2 from tests.utils import needs_pydanticv2
@ -28,45 +27,11 @@ async def read_list_uploadfile(p: Annotated[List[UploadFile], File()]):
return {"file_size": [file.size for file in p]} return {"file_size": [file.size for file in p]}
class FormModelListBytes(BaseModel):
p: List[bytes] = File()
@app.post("/model-list-bytes", operation_id="model_list_bytes")
async def read_model_list_bytes(
p: Annotated[
FormModelListBytes,
Form(
media_type="multipart/form-data" # Remove media_type when https://github.com/fastapi/fastapi/pull/14343 is fixed
),
],
):
return {"file_size": [len(file) for file in p.p]}
class FormModelListUploadFile(BaseModel):
p: List[UploadFile] = File()
@app.post("/model-list-uploadfile", operation_id="model_list_uploadfile")
async def read_model_list_uploadfile(
p: Annotated[
FormModelListUploadFile,
Form(
media_type="multipart/form-data" # Remove media_type when https://github.com/fastapi/fastapi/pull/14343 is fixed
),
],
):
return {"file_size": [file.size for file in p.p]}
@pytest.mark.parametrize( @pytest.mark.parametrize(
"path", "path",
[ [
"/list-bytes", "/list-bytes",
"/model-list-bytes",
"/list-uploadfile", "/list-uploadfile",
"/model-list-uploadfile",
], ],
) )
def test_list_schema(path: str): def test_list_schema(path: str):
@ -107,9 +72,7 @@ def test_list_schema(path: str):
"path", "path",
[ [
"/list-bytes", "/list-bytes",
"/model-list-bytes",
"/list-uploadfile", "/list-uploadfile",
"/model-list-uploadfile",
], ],
) )
def test_list_missing(path: str): def test_list_missing(path: str):
@ -123,7 +86,7 @@ def test_list_missing(path: str):
"type": "missing", "type": "missing",
"loc": ["body", "p"], "loc": ["body", "p"],
"msg": "Field required", "msg": "Field required",
"input": IsOneOf(None, {}), "input": None,
} }
] ]
} }
@ -145,9 +108,7 @@ def test_list_missing(path: str):
"path", "path",
[ [
"/list-bytes", "/list-bytes",
"/model-list-bytes",
"/list-uploadfile", "/list-uploadfile",
"/model-list-uploadfile",
], ],
) )
def test_list(path: str): def test_list(path: str):
@ -173,38 +134,6 @@ async def read_list_uploadfile_alias(
return {"file_size": [file.size for file in p]} return {"file_size": [file.size for file in p]}
class FormModelListBytesAlias(BaseModel):
p: List[bytes] = File(alias="p_alias")
@app.post("/model-list-bytes-alias", operation_id="model_list_bytes_alias")
async def read_model_list_bytes_alias(
p: Annotated[
FormModelListBytesAlias,
Form(
media_type="multipart/form-data" # Remove media_type when https://github.com/fastapi/fastapi/pull/14343 is fixed
),
],
):
return {"file_size": [len(file) for file in p.p]}
class FormModelListUploadFileAlias(BaseModel):
p: List[UploadFile] = File(alias="p_alias")
@app.post("/model-list-uploadfile-alias", operation_id="model_list_uploadfile_alias")
async def read_model_list_uploadfile_alias(
p: Annotated[
FormModelListUploadFileAlias,
Form(
media_type="multipart/form-data" # Remove media_type when https://github.com/fastapi/fastapi/pull/14343 is fixed
),
],
):
return {"file_size": [file.size for file in p.p]}
@pytest.mark.xfail( @pytest.mark.xfail(
raises=AssertionError, raises=AssertionError,
condition=PYDANTIC_V2, condition=PYDANTIC_V2,
@ -215,9 +144,7 @@ async def read_model_list_uploadfile_alias(
"path", "path",
[ [
"/list-bytes-alias", "/list-bytes-alias",
"/model-list-bytes-alias",
"/list-uploadfile-alias", "/list-uploadfile-alias",
"/model-list-uploadfile-alias",
], ],
) )
def test_list_alias_schema(path: str): def test_list_alias_schema(path: str):
@ -258,25 +185,7 @@ def test_list_alias_schema(path: str):
"path", "path",
[ [
"/list-bytes-alias", "/list-bytes-alias",
pytest.param(
"/model-list-bytes-alias",
marks=pytest.mark.xfail(
raises=AssertionError,
strict=False,
condition=PYDANTIC_V2,
reason="Fails only with PDv2 model",
),
),
"/list-uploadfile-alias", "/list-uploadfile-alias",
pytest.param(
"/model-list-uploadfile-alias",
marks=pytest.mark.xfail(
raises=AssertionError,
strict=False,
condition=PYDANTIC_V2,
reason="Fails only with PDv2 model",
),
),
], ],
) )
def test_list_alias_missing(path: str): def test_list_alias_missing(path: str):
@ -288,9 +197,9 @@ def test_list_alias_missing(path: str):
"detail": [ "detail": [
{ {
"type": "missing", "type": "missing",
"loc": ["body", "p_alias"], # model-list-*-alias fail here "loc": ["body", "p_alias"],
"msg": "Field required", "msg": "Field required",
"input": IsOneOf(None, {}), "input": None,
} }
] ]
} }
@ -312,31 +221,13 @@ def test_list_alias_missing(path: str):
"path", "path",
[ [
"/list-bytes-alias", "/list-bytes-alias",
pytest.param(
"/model-list-bytes-alias",
marks=pytest.mark.xfail(
raises=AssertionError,
condition=PYDANTIC_V2,
reason="Fails only with PDv2 model",
strict=False,
),
),
"/list-uploadfile-alias", "/list-uploadfile-alias",
pytest.param(
"/model-list-uploadfile-alias",
marks=pytest.mark.xfail(
raises=AssertionError,
condition=PYDANTIC_V2,
reason="Fails only with PDv2 model",
strict=False,
),
),
], ],
) )
def test_list_alias_by_name(path: str): def test_list_alias_by_name(path: str):
client = TestClient(app) client = TestClient(app)
response = client.post(path, files=[("p", b"hello"), ("p", b"world")]) response = client.post(path, files=[("p", b"hello"), ("p", b"world")])
assert response.status_code == 422 # model-list-uploadfile-alias fail here assert response.status_code == 422
assert response.json() == IsDict( assert response.json() == IsDict(
{ {
"detail": [ "detail": [
@ -344,10 +235,7 @@ def test_list_alias_by_name(path: str):
"type": "missing", "type": "missing",
"loc": ["body", "p_alias"], "loc": ["body", "p_alias"],
"msg": "Field required", "msg": "Field required",
"input": IsOneOf( # model-list-bytes-alias fail here "input": None,
None,
{"p": [IsPartialDict({"size": 5}), IsPartialDict({"size": 5})]},
),
} }
] ]
} }
@ -369,33 +257,13 @@ def test_list_alias_by_name(path: str):
"path", "path",
[ [
"/list-bytes-alias", "/list-bytes-alias",
pytest.param(
"/model-list-bytes-alias",
marks=pytest.mark.xfail(
raises=AssertionError,
strict=False,
condition=PYDANTIC_V2,
reason="Fails only with PDv2 model",
),
),
"/list-uploadfile-alias", "/list-uploadfile-alias",
pytest.param(
"/model-list-uploadfile-alias",
marks=pytest.mark.xfail(
raises=AssertionError,
strict=False,
condition=PYDANTIC_V2,
reason="Fails only with PDv2 model",
),
),
], ],
) )
def test_list_alias_by_alias(path: str): def test_list_alias_by_alias(path: str):
client = TestClient(app) client = TestClient(app)
response = client.post(path, files=[("p_alias", b"hello"), ("p_alias", b"world")]) response = client.post(path, files=[("p_alias", b"hello"), ("p_alias", b"world")])
assert response.status_code == 200, ( # model-list-*-alias fail here assert response.status_code == 200, response.text
response.text
)
assert response.json() == {"file_size": [5, 5]} assert response.json() == {"file_size": [5, 5]}
@ -420,52 +288,12 @@ def read_list_uploadfile_validation_alias(
return {"file_size": [file.size for file in p]} return {"file_size": [file.size for file in p]}
class FormModelRequiredBytesValidationAlias(BaseModel):
p: List[bytes] = File(validation_alias="p_val_alias")
@app.post(
"/model-list-bytes-validation-alias",
operation_id="model_list_bytes_validation_alias",
)
def read_model_list_bytes_validation_alias(
p: Annotated[
FormModelRequiredBytesValidationAlias,
Form(
media_type="multipart/form-data" # Remove media_type when https://github.com/fastapi/fastapi/pull/14343 is fixed
),
],
):
return {"file_size": [len(file) for file in p.p]} # pragma: no cover
class FormModelRequiredUploadFileValidationAlias(BaseModel):
p: List[UploadFile] = File(validation_alias="p_val_alias")
@app.post(
"/model-list-uploadfile-validation-alias",
operation_id="model_list_uploadfile_validation_alias",
)
def read_model_list_uploadfile_validation_alias(
p: Annotated[
FormModelRequiredUploadFileValidationAlias,
Form(
media_type="multipart/form-data" # Remove media_type when https://github.com/fastapi/fastapi/pull/14343 is fixed
),
],
):
return {"file_size": [file.size for file in p.p]} # pragma: no cover
@needs_pydanticv2 @needs_pydanticv2
@pytest.mark.parametrize( @pytest.mark.parametrize(
"path", "path",
[ [
"/list-bytes-validation-alias", "/list-bytes-validation-alias",
"/model-list-uploadfile-validation-alias",
"/list-uploadfile-validation-alias", "/list-uploadfile-validation-alias",
"/model-list-bytes-validation-alias",
], ],
) )
def test_list_validation_alias_schema(path: str): def test_list_validation_alias_schema(path: str):
@ -510,12 +338,10 @@ def test_list_validation_alias_schema(path: str):
"/list-bytes-validation-alias", "/list-bytes-validation-alias",
marks=pytest.mark.xfail(raises=AssertionError, strict=False), marks=pytest.mark.xfail(raises=AssertionError, strict=False),
), ),
"/model-list-bytes-validation-alias",
pytest.param( pytest.param(
"/list-uploadfile-validation-alias", "/list-uploadfile-validation-alias",
marks=pytest.mark.xfail(raises=AssertionError, strict=False), marks=pytest.mark.xfail(raises=AssertionError, strict=False),
), ),
"/model-list-uploadfile-validation-alias",
], ],
) )
def test_list_validation_alias_missing(path: str): def test_list_validation_alias_missing(path: str):
@ -531,7 +357,7 @@ def test_list_validation_alias_missing(path: str):
"p_val_alias", "p_val_alias",
], ],
"msg": "Field required", "msg": "Field required",
"input": IsOneOf(None, {}), "input": None,
} }
] ]
} }
@ -545,15 +371,10 @@ def test_list_validation_alias_missing(path: str):
"/list-bytes-validation-alias", "/list-bytes-validation-alias",
marks=pytest.mark.xfail(raises=AssertionError, strict=False), marks=pytest.mark.xfail(raises=AssertionError, strict=False),
), ),
pytest.param(
"/model-list-bytes-validation-alias",
marks=pytest.mark.xfail(raises=AssertionError, strict=False),
),
pytest.param( pytest.param(
"/list-uploadfile-validation-alias", "/list-uploadfile-validation-alias",
marks=pytest.mark.xfail(raises=AssertionError, strict=False), marks=pytest.mark.xfail(raises=AssertionError, strict=False),
), ),
"/model-list-uploadfile-validation-alias",
], ],
) )
def test_list_validation_alias_by_name(path: str): def test_list_validation_alias_by_name(path: str):
@ -563,16 +384,13 @@ def test_list_validation_alias_by_name(path: str):
response.text response.text
) )
assert response.json() == { assert response.json() == { # pragma: no cover
"detail": [ "detail": [
{ {
"type": "missing", "type": "missing",
"loc": ["body", "p_val_alias"], "loc": ["body", "p_val_alias"],
"msg": "Field required", "msg": "Field required",
"input": IsOneOf( # /model-list-bytes-validation-alias fails here "input": None,
None,
{"p": [IsPartialDict({"size": 5}), IsPartialDict({"size": 5})]},
),
} }
] ]
} }
@ -584,9 +402,7 @@ def test_list_validation_alias_by_name(path: str):
"path", "path",
[ [
"/list-bytes-validation-alias", "/list-bytes-validation-alias",
"/model-list-bytes-validation-alias",
"/list-uploadfile-validation-alias", "/list-uploadfile-validation-alias",
"/model-list-uploadfile-validation-alias",
], ],
) )
def test_list_validation_alias_by_validation_alias(path: str): def test_list_validation_alias_by_validation_alias(path: str):
@ -594,7 +410,7 @@ def test_list_validation_alias_by_validation_alias(path: str):
response = client.post( response = client.post(
path, files=[("p_val_alias", b"hello"), ("p_val_alias", b"world")] path, files=[("p_val_alias", b"hello"), ("p_val_alias", b"world")]
) )
assert response.status_code == 200, response.text # all 4 fail here assert response.status_code == 200, response.text # all 2 fail here
assert response.json() == {"file_size": [5, 5]} # pragma: no cover assert response.json() == {"file_size": [5, 5]} # pragma: no cover
@ -624,52 +440,12 @@ def read_list_uploadfile_alias_and_validation_alias(
return {"file_size": [file.size for file in p]} return {"file_size": [file.size for file in p]}
class FormModelRequiredBytesAliasAndValidationAlias(BaseModel):
p: List[bytes] = File(alias="p_alias", validation_alias="p_val_alias")
@app.post(
"/model-list-bytes-alias-and-validation-alias",
operation_id="model_list_bytes_alias_and_validation_alias",
)
def read_model_list_bytes_alias_and_validation_alias(
p: Annotated[
FormModelRequiredBytesAliasAndValidationAlias,
Form(
media_type="multipart/form-data" # Remove media_type when https://github.com/fastapi/fastapi/pull/14343 is fixed
),
],
):
return {"file_size": [len(file) for file in p.p]} # pragma: no cover
class FormModelRequiredUploadFileAliasAndValidationAlias(BaseModel):
p: List[UploadFile] = File(alias="p_alias", validation_alias="p_val_alias")
@app.post(
"/model-list-uploadfile-alias-and-validation-alias",
operation_id="model_list_uploadfile_alias_and_validation_alias",
)
def read_model_list_uploadfile_alias_and_validation_alias(
p: Annotated[
FormModelRequiredUploadFileAliasAndValidationAlias,
Form(
media_type="multipart/form-data" # Remove media_type when https://github.com/fastapi/fastapi/pull/14343 is fixed
),
],
):
return {"file_size": [file.size for file in p.p]} # pragma: no cover
@needs_pydanticv2 @needs_pydanticv2
@pytest.mark.parametrize( @pytest.mark.parametrize(
"path", "path",
[ [
"/list-bytes-alias-and-validation-alias", "/list-bytes-alias-and-validation-alias",
"/model-list-bytes-alias-and-validation-alias",
"/list-uploadfile-alias-and-validation-alias", "/list-uploadfile-alias-and-validation-alias",
"/model-list-uploadfile-alias-and-validation-alias",
], ],
) )
def test_list_alias_and_validation_alias_schema(path: str): def test_list_alias_and_validation_alias_schema(path: str):
@ -714,12 +490,10 @@ def test_list_alias_and_validation_alias_schema(path: str):
"/list-bytes-alias-and-validation-alias", "/list-bytes-alias-and-validation-alias",
marks=pytest.mark.xfail(raises=AssertionError, strict=False), marks=pytest.mark.xfail(raises=AssertionError, strict=False),
), ),
"/model-list-bytes-alias-and-validation-alias",
pytest.param( pytest.param(
"/list-uploadfile-alias-and-validation-alias", "/list-uploadfile-alias-and-validation-alias",
marks=pytest.mark.xfail(raises=AssertionError, strict=False), marks=pytest.mark.xfail(raises=AssertionError, strict=False),
), ),
"/model-list-uploadfile-alias-and-validation-alias",
], ],
) )
def test_list_alias_and_validation_alias_missing(path: str): def test_list_alias_and_validation_alias_missing(path: str):
@ -735,7 +509,7 @@ def test_list_alias_and_validation_alias_missing(path: str):
"p_val_alias", # /list-*-alias-and-validation-alias fail here "p_val_alias", # /list-*-alias-and-validation-alias fail here
], ],
"msg": "Field required", "msg": "Field required",
"input": IsOneOf(None, {}), "input": None,
} }
] ]
} }
@ -747,9 +521,7 @@ def test_list_alias_and_validation_alias_missing(path: str):
"path", "path",
[ [
"/list-bytes-alias-and-validation-alias", "/list-bytes-alias-and-validation-alias",
"/model-list-bytes-alias-and-validation-alias",
"/list-uploadfile-alias-and-validation-alias", "/list-uploadfile-alias-and-validation-alias",
"/model-list-uploadfile-alias-and-validation-alias",
], ],
) )
def test_list_alias_and_validation_alias_by_name(path: str): def test_list_alias_and_validation_alias_by_name(path: str):
@ -766,11 +538,7 @@ def test_list_alias_and_validation_alias_by_name(path: str):
"p_val_alias", # /list-*-alias-and-validation-alias fail here "p_val_alias", # /list-*-alias-and-validation-alias fail here
], ],
"msg": "Field required", "msg": "Field required",
"input": IsOneOf( "input": None,
None,
# /model-list-*-alias-and-validation-alias fail here
{"p": [IsPartialDict({"size": 5}), IsPartialDict({"size": 5})]},
),
} }
] ]
} }
@ -784,15 +552,10 @@ def test_list_alias_and_validation_alias_by_name(path: str):
"/list-bytes-alias-and-validation-alias", "/list-bytes-alias-and-validation-alias",
marks=pytest.mark.xfail(raises=AssertionError, strict=False), marks=pytest.mark.xfail(raises=AssertionError, strict=False),
), ),
pytest.param(
"/model-list-bytes-alias-and-validation-alias",
marks=pytest.mark.xfail(raises=AssertionError, strict=False),
),
pytest.param( pytest.param(
"/list-uploadfile-alias-and-validation-alias", "/list-uploadfile-alias-and-validation-alias",
marks=pytest.mark.xfail(raises=AssertionError, strict=False), marks=pytest.mark.xfail(raises=AssertionError, strict=False),
), ),
"/model-list-uploadfile-alias-and-validation-alias",
], ],
) )
def test_list_alias_and_validation_alias_by_alias(path: str): def test_list_alias_and_validation_alias_by_alias(path: str):
@ -802,22 +565,13 @@ def test_list_alias_and_validation_alias_by_alias(path: str):
response.text # /list-*-alias-and-validation-alias fails here response.text # /list-*-alias-and-validation-alias fails here
) )
assert response.json() == { assert response.json() == { # pragma: no cover
"detail": [ "detail": [
{ {
"type": "missing", "type": "missing",
"loc": ["body", "p_val_alias"], "loc": ["body", "p_val_alias"],
"msg": "Field required", "msg": "Field required",
"input": IsOneOf( "input": None,
None,
# /model-list-bytes-alias-and-validation-alias fails here
{
"p_alias": [
IsPartialDict({"size": 5}),
IsPartialDict({"size": 5}),
]
},
),
} }
] ]
} }
@ -829,9 +583,7 @@ def test_list_alias_and_validation_alias_by_alias(path: str):
"path", "path",
[ [
"/list-bytes-alias-and-validation-alias", "/list-bytes-alias-and-validation-alias",
"/model-list-bytes-alias-and-validation-alias",
"/list-uploadfile-alias-and-validation-alias", "/list-uploadfile-alias-and-validation-alias",
"/model-list-uploadfile-alias-and-validation-alias",
], ],
) )
def test_list_alias_and_validation_alias_by_validation_alias(path: str): def test_list_alias_and_validation_alias_by_validation_alias(path: str):
@ -839,7 +591,7 @@ def test_list_alias_and_validation_alias_by_validation_alias(path: str):
response = client.post( response = client.post(
path, files=[("p_val_alias", b"hello"), ("p_val_alias", b"world")] path, files=[("p_val_alias", b"hello"), ("p_val_alias", b"world")]
) )
assert response.status_code == 200, ( # all 4 fail here assert response.status_code == 200, ( # all 2 fail here
response.text response.text
) )
assert response.json() == {"file_size": [5, 5]} # pragma: no cover assert response.json() == {"file_size": [5, 5]} # pragma: no cover

View File

@ -2,10 +2,9 @@ from typing import Optional
import pytest import pytest
from dirty_equals import IsDict from dirty_equals import IsDict
from fastapi import FastAPI, File, Form, UploadFile from fastapi import FastAPI, File, UploadFile
from fastapi._compat import PYDANTIC_V2 from fastapi._compat import PYDANTIC_V2
from fastapi.testclient import TestClient from fastapi.testclient import TestClient
from pydantic import BaseModel
from typing_extensions import Annotated from typing_extensions import Annotated
from tests.utils import needs_pydanticv2 from tests.utils import needs_pydanticv2
@ -28,45 +27,11 @@ async def read_optional_uploadfile(p: Annotated[Optional[UploadFile], File()] =
return {"file_size": p.size if p else None} return {"file_size": p.size if p else None}
class FormModelOptionalBytes(BaseModel):
p: Optional[bytes] = File(default=None)
@app.post("/model-optional-bytes", operation_id="model_optional_bytes")
async def read_model_optional_bytes(
p: Annotated[
FormModelOptionalBytes,
Form(
media_type="multipart/form-data" # Remove media_type when https://github.com/fastapi/fastapi/pull/14343 is fixed
),
],
):
return {"file_size": len(p.p) if p.p else None}
class FormModelOptionalUploadFile(BaseModel):
p: Optional[UploadFile] = File(default=None)
@app.post("/model-optional-uploadfile", operation_id="model_optional_uploadfile")
async def read_model_optional_uploadfile(
p: Annotated[
FormModelOptionalUploadFile,
Form(
media_type="multipart/form-data" # Remove media_type when https://github.com/fastapi/fastapi/pull/14343 is fixed
),
],
):
return {"file_size": p.p.size if p.p else None}
@pytest.mark.parametrize( @pytest.mark.parametrize(
"path", "path",
[ [
"/optional-bytes", "/optional-bytes",
"/model-optional-bytes",
"/optional-uploadfile", "/optional-uploadfile",
"/model-optional-uploadfile",
], ],
) )
def test_optional_schema(path: str): def test_optional_schema(path: str):
@ -100,9 +65,7 @@ def test_optional_schema(path: str):
"path", "path",
[ [
"/optional-bytes", "/optional-bytes",
"/model-optional-bytes",
"/optional-uploadfile", "/optional-uploadfile",
"/model-optional-uploadfile",
], ],
) )
def test_optional_missing(path: str): def test_optional_missing(path: str):
@ -116,9 +79,7 @@ def test_optional_missing(path: str):
"path", "path",
[ [
"/optional-bytes", "/optional-bytes",
"/model-optional-bytes",
"/optional-uploadfile", "/optional-uploadfile",
"/model-optional-uploadfile",
], ],
) )
def test_optional(path: str): def test_optional(path: str):
@ -146,40 +107,6 @@ async def read_optional_uploadfile_alias(
return {"file_size": p.size if p else None} return {"file_size": p.size if p else None}
class FormModelOptionalBytesAlias(BaseModel):
p: Optional[bytes] = File(default=None, alias="p_alias")
@app.post("/model-optional-bytes-alias", operation_id="model_optional_bytes_alias")
async def read_model_optional_bytes_alias(
p: Annotated[
FormModelOptionalBytesAlias,
Form(
media_type="multipart/form-data" # Remove media_type when https://github.com/fastapi/fastapi/pull/14343 is fixed
),
],
):
return {"file_size": len(p.p) if p.p else None}
class FormModelOptionalUploadFileAlias(BaseModel):
p: Optional[UploadFile] = File(default=None, alias="p_alias")
@app.post(
"/model-optional-uploadfile-alias", operation_id="model_optional_uploadfile_alias"
)
async def read_model_optional_uploadfile_alias(
p: Annotated[
FormModelOptionalUploadFileAlias,
Form(
media_type="multipart/form-data" # Remove media_type when https://github.com/fastapi/fastapi/pull/14343 is fixed
),
],
):
return {"file_size": p.p.size if p.p else None}
@pytest.mark.xfail( @pytest.mark.xfail(
raises=AssertionError, raises=AssertionError,
condition=PYDANTIC_V2, condition=PYDANTIC_V2,
@ -190,9 +117,7 @@ async def read_model_optional_uploadfile_alias(
"path", "path",
[ [
"/optional-bytes-alias", "/optional-bytes-alias",
"/model-optional-bytes-alias",
"/optional-uploadfile-alias", "/optional-uploadfile-alias",
"/model-optional-uploadfile-alias",
], ],
) )
def test_optional_alias_schema(path: str): def test_optional_alias_schema(path: str):
@ -226,9 +151,7 @@ def test_optional_alias_schema(path: str):
"path", "path",
[ [
"/optional-bytes-alias", "/optional-bytes-alias",
"/model-optional-bytes-alias",
"/optional-uploadfile-alias", "/optional-uploadfile-alias",
"/model-optional-uploadfile-alias",
], ],
) )
def test_optional_alias_missing(path: str): def test_optional_alias_missing(path: str):
@ -242,64 +165,28 @@ def test_optional_alias_missing(path: str):
"path", "path",
[ [
"/optional-bytes-alias", "/optional-bytes-alias",
pytest.param(
"/model-optional-bytes-alias",
marks=pytest.mark.xfail(
raises=AssertionError,
condition=PYDANTIC_V2,
reason="Fails only with PDv2 model",
strict=False,
),
),
"/optional-uploadfile-alias", "/optional-uploadfile-alias",
pytest.param(
"/model-optional-uploadfile-alias",
marks=pytest.mark.xfail(
raises=AssertionError,
condition=PYDANTIC_V2,
reason="Fails only with PDv2 model",
strict=False,
),
),
], ],
) )
def test_optional_alias_by_name(path: str): def test_optional_alias_by_name(path: str):
client = TestClient(app) client = TestClient(app)
response = client.post(path, files=[("p", b"hello")]) response = client.post(path, files=[("p", b"hello")])
assert response.status_code == 200 assert response.status_code == 200
assert response.json() == {"file_size": None} # model-optional-*-alias fail here assert response.json() == {"file_size": None}
@pytest.mark.parametrize( @pytest.mark.parametrize(
"path", "path",
[ [
"/optional-bytes-alias", "/optional-bytes-alias",
pytest.param(
"/model-optional-bytes-alias",
marks=pytest.mark.xfail(
raises=AssertionError,
strict=False,
condition=PYDANTIC_V2,
reason="Fails only with PDv2 model",
),
),
"/optional-uploadfile-alias", "/optional-uploadfile-alias",
pytest.param(
"/model-optional-uploadfile-alias",
marks=pytest.mark.xfail(
raises=AssertionError,
strict=False,
condition=PYDANTIC_V2,
reason="Fails only with PDv2 model",
),
),
], ],
) )
def test_optional_alias_by_alias(path: str): def test_optional_alias_by_alias(path: str):
client = TestClient(app) client = TestClient(app)
response = client.post(path, files=[("p_alias", b"hello")]) response = client.post(path, files=[("p_alias", b"hello")])
assert response.status_code == 200, response.text assert response.status_code == 200, response.text
assert response.json() == {"file_size": 5} # model-optional-*-alias fail here assert response.json() == {"file_size": 5}
# ===================================================================================== # =====================================================================================
@ -325,52 +212,12 @@ def read_optional_uploadfile_validation_alias(
return {"file_size": p.size if p else None} return {"file_size": p.size if p else None}
class FormModelOptionalBytesValidationAlias(BaseModel):
p: Optional[bytes] = File(default=None, validation_alias="p_val_alias")
@app.post(
"/model-optional-bytes-validation-alias",
operation_id="model_optional_bytes_validation_alias",
)
def read_model_optional_bytes_validation_alias(
p: Annotated[
FormModelOptionalBytesValidationAlias,
Form(
media_type="multipart/form-data" # Remove media_type when https://github.com/fastapi/fastapi/pull/14343 is fixed
),
],
):
return {"file_size": len(p.p) if p.p else None}
class FormModelOptionalUploadFileValidationAlias(BaseModel):
p: Optional[UploadFile] = File(default=None, validation_alias="p_val_alias")
@app.post(
"/model-optional-uploadfile-validation-alias",
operation_id="model_optional_uploadfile_validation_alias",
)
def read_model_optional_uploadfile_validation_alias(
p: Annotated[
FormModelOptionalUploadFileValidationAlias,
Form(
media_type="multipart/form-data" # Remove media_type when https://github.com/fastapi/fastapi/pull/14343 is fixed
),
],
):
return {"file_size": p.p.size if p.p else None}
@needs_pydanticv2 @needs_pydanticv2
@pytest.mark.parametrize( @pytest.mark.parametrize(
"path", "path",
[ [
"/optional-bytes-validation-alias", "/optional-bytes-validation-alias",
"/model-optional-uploadfile-validation-alias",
"/optional-uploadfile-validation-alias", "/optional-uploadfile-validation-alias",
"/model-optional-bytes-validation-alias",
], ],
) )
def test_optional_validation_alias_schema(path: str): def test_optional_validation_alias_schema(path: str):
@ -405,9 +252,7 @@ def test_optional_validation_alias_schema(path: str):
"path", "path",
[ [
"/optional-bytes-validation-alias", "/optional-bytes-validation-alias",
"/model-optional-bytes-validation-alias",
"/optional-uploadfile-validation-alias", "/optional-uploadfile-validation-alias",
"/model-optional-uploadfile-validation-alias",
], ],
) )
def test_optional_validation_alias_missing(path: str): def test_optional_validation_alias_missing(path: str):
@ -425,12 +270,10 @@ def test_optional_validation_alias_missing(path: str):
"/optional-bytes-validation-alias", "/optional-bytes-validation-alias",
marks=pytest.mark.xfail(raises=AssertionError, strict=False), marks=pytest.mark.xfail(raises=AssertionError, strict=False),
), ),
"/model-optional-bytes-validation-alias",
pytest.param( pytest.param(
"/optional-uploadfile-validation-alias", "/optional-uploadfile-validation-alias",
marks=pytest.mark.xfail(raises=AssertionError, strict=False), marks=pytest.mark.xfail(raises=AssertionError, strict=False),
), ),
"/model-optional-uploadfile-validation-alias",
], ],
) )
def test_optional_validation_alias_by_name(path: str): def test_optional_validation_alias_by_name(path: str):
@ -450,23 +293,16 @@ def test_optional_validation_alias_by_name(path: str):
"/optional-bytes-validation-alias", "/optional-bytes-validation-alias",
marks=pytest.mark.xfail(raises=AssertionError, strict=False), marks=pytest.mark.xfail(raises=AssertionError, strict=False),
), ),
pytest.param(
"/model-optional-bytes-validation-alias",
marks=pytest.mark.xfail(raises=AssertionError, strict=False),
),
pytest.param( pytest.param(
"/optional-uploadfile-validation-alias", "/optional-uploadfile-validation-alias",
marks=pytest.mark.xfail(raises=AssertionError, strict=False), marks=pytest.mark.xfail(raises=AssertionError, strict=False),
), ),
"/model-optional-uploadfile-validation-alias",
], ],
) )
def test_optional_validation_alias_by_validation_alias(path: str): def test_optional_validation_alias_by_validation_alias(path: str):
client = TestClient(app) client = TestClient(app)
response = client.post(path, files=[("p_val_alias", b"hello")]) response = client.post(path, files=[("p_val_alias", b"hello")])
assert response.status_code == 200, ( assert response.status_code == 200, response.text
response.text # /model-optional-bytes-validation-alias fail here
)
assert response.json() == {"file_size": 5} # /optional-*-validation-alias fail here assert response.json() == {"file_size": 5} # /optional-*-validation-alias fail here
@ -498,56 +334,12 @@ def read_optional_uploadfile_alias_and_validation_alias(
return {"file_size": p.size if p else None} return {"file_size": p.size if p else None}
class FormModelOptionalBytesAliasAndValidationAlias(BaseModel):
p: Optional[bytes] = File(
default=None, alias="p_alias", validation_alias="p_val_alias"
)
@app.post(
"/model-optional-bytes-alias-and-validation-alias",
operation_id="model_optional_bytes_alias_and_validation_alias",
)
def read_model_optional_bytes_alias_and_validation_alias(
p: Annotated[
FormModelOptionalBytesAliasAndValidationAlias,
Form(
media_type="multipart/form-data" # Remove media_type when https://github.com/fastapi/fastapi/pull/14343 is fixed
),
],
):
return {"file_size": len(p.p) if p.p else None}
class FormModelOptionalUploadFileAliasAndValidationAlias(BaseModel):
p: Optional[UploadFile] = File(
default=None, alias="p_alias", validation_alias="p_val_alias"
)
@app.post(
"/model-optional-uploadfile-alias-and-validation-alias",
operation_id="model_optional_uploadfile_alias_and_validation_alias",
)
def read_model_optional_uploadfile_alias_and_validation_alias(
p: Annotated[
FormModelOptionalUploadFileAliasAndValidationAlias,
Form(
media_type="multipart/form-data" # Remove media_type when https://github.com/fastapi/fastapi/pull/14343 is fixed
),
],
):
return {"file_size": p.p.size if p.p else None}
@needs_pydanticv2 @needs_pydanticv2
@pytest.mark.parametrize( @pytest.mark.parametrize(
"path", "path",
[ [
"/optional-bytes-alias-and-validation-alias", "/optional-bytes-alias-and-validation-alias",
"/model-optional-bytes-alias-and-validation-alias",
"/optional-uploadfile-alias-and-validation-alias", "/optional-uploadfile-alias-and-validation-alias",
"/model-optional-uploadfile-alias-and-validation-alias",
], ],
) )
def test_optional_alias_and_validation_alias_schema(path: str): def test_optional_alias_and_validation_alias_schema(path: str):
@ -582,9 +374,7 @@ def test_optional_alias_and_validation_alias_schema(path: str):
"path", "path",
[ [
"/optional-bytes-alias-and-validation-alias", "/optional-bytes-alias-and-validation-alias",
"/model-optional-bytes-alias-and-validation-alias",
"/optional-uploadfile-alias-and-validation-alias", "/optional-uploadfile-alias-and-validation-alias",
"/model-optional-uploadfile-alias-and-validation-alias",
], ],
) )
def test_optional_alias_and_validation_alias_missing(path: str): def test_optional_alias_and_validation_alias_missing(path: str):
@ -599,9 +389,7 @@ def test_optional_alias_and_validation_alias_missing(path: str):
"path", "path",
[ [
"/optional-bytes-alias-and-validation-alias", "/optional-bytes-alias-and-validation-alias",
"/model-optional-bytes-alias-and-validation-alias",
"/optional-uploadfile-alias-and-validation-alias", "/optional-uploadfile-alias-and-validation-alias",
"/model-optional-uploadfile-alias-and-validation-alias",
], ],
) )
def test_optional_alias_and_validation_alias_by_name(path: str): def test_optional_alias_and_validation_alias_by_name(path: str):
@ -619,21 +407,17 @@ def test_optional_alias_and_validation_alias_by_name(path: str):
"/optional-bytes-alias-and-validation-alias", "/optional-bytes-alias-and-validation-alias",
marks=pytest.mark.xfail(raises=AssertionError, strict=False), marks=pytest.mark.xfail(raises=AssertionError, strict=False),
), ),
"/model-optional-bytes-alias-and-validation-alias",
pytest.param( pytest.param(
"/optional-uploadfile-alias-and-validation-alias", "/optional-uploadfile-alias-and-validation-alias",
marks=pytest.mark.xfail(raises=AssertionError, strict=False), marks=pytest.mark.xfail(raises=AssertionError, strict=False),
), ),
"/model-optional-uploadfile-alias-and-validation-alias",
], ],
) )
def test_optional_alias_and_validation_alias_by_alias(path: str): def test_optional_alias_and_validation_alias_by_alias(path: str):
client = TestClient(app) client = TestClient(app)
response = client.post(path, files=[("p_alias", b"hello")]) response = client.post(path, files=[("p_alias", b"hello")])
assert response.status_code == 200, response.text assert response.status_code == 200, response.text
assert response.json() == { assert response.json() == {"file_size": None}
"file_size": None # model-optional-*-alias-and-validation-alias fail here
}
@needs_pydanticv2 @needs_pydanticv2
@ -644,23 +428,16 @@ def test_optional_alias_and_validation_alias_by_alias(path: str):
"/optional-bytes-alias-and-validation-alias", "/optional-bytes-alias-and-validation-alias",
marks=pytest.mark.xfail(raises=AssertionError, strict=False), marks=pytest.mark.xfail(raises=AssertionError, strict=False),
), ),
pytest.param(
"/model-optional-bytes-alias-and-validation-alias",
marks=pytest.mark.xfail(raises=AssertionError, strict=False),
),
pytest.param( pytest.param(
"/optional-uploadfile-alias-and-validation-alias", "/optional-uploadfile-alias-and-validation-alias",
marks=pytest.mark.xfail(raises=AssertionError, strict=False), marks=pytest.mark.xfail(raises=AssertionError, strict=False),
), ),
"/model-optional-uploadfile-alias-and-validation-alias",
], ],
) )
def test_optional_alias_and_validation_alias_by_validation_alias(path: str): def test_optional_alias_and_validation_alias_by_validation_alias(path: str):
client = TestClient(app) client = TestClient(app)
response = client.post(path, files=[("p_val_alias", b"hello")]) response = client.post(path, files=[("p_val_alias", b"hello")])
assert response.status_code == 200, ( assert response.status_code == 200, response.text
response.text # model-optional-bytes-alias-and-validation-alias fails here
)
assert response.json() == { assert response.json() == {
"file_size": 5 "file_size": 5
} # /optional-*-alias-and-validation-alias fail here } # /optional-*-alias-and-validation-alias fail here

View File

@ -2,10 +2,9 @@ from typing import List, Optional
import pytest import pytest
from dirty_equals import IsDict from dirty_equals import IsDict
from fastapi import FastAPI, File, Form, UploadFile from fastapi import FastAPI, File, UploadFile
from fastapi._compat import PYDANTIC_V2 from fastapi._compat import PYDANTIC_V2
from fastapi.testclient import TestClient from fastapi.testclient import TestClient
from pydantic import BaseModel
from typing_extensions import Annotated from typing_extensions import Annotated
from tests.utils import needs_pydanticv2 from tests.utils import needs_pydanticv2
@ -30,45 +29,11 @@ async def read_optional_list_uploadfile(
return {"file_size": [file.size for file in p] if p else None} return {"file_size": [file.size for file in p] if p else None}
class FormModelOptionalListBytes(BaseModel):
p: Optional[List[bytes]] = File(default=None)
@app.post("/model-optional-list-bytes")
async def read_model_optional_list_bytes(
p: Annotated[
FormModelOptionalListBytes,
Form(
media_type="multipart/form-data" # Remove media_type when https://github.com/fastapi/fastapi/pull/14343 is fixed
),
],
):
return {"file_size": [len(file) for file in p.p] if p.p else None}
class FormModelOptionalListUploadFile(BaseModel):
p: Optional[List[UploadFile]] = File(default=None)
@app.post("/model-optional-list-uploadfile")
async def read_model_optional_list_uploadfile(
p: Annotated[
FormModelOptionalListUploadFile,
Form(
media_type="multipart/form-data" # Remove media_type when https://github.com/fastapi/fastapi/pull/14343 is fixed
),
],
):
return {"file_size": [file.size for file in p.p] if p.p else None}
@pytest.mark.parametrize( @pytest.mark.parametrize(
"path", "path",
[ [
"/optional-list-bytes", "/optional-list-bytes",
"/model-optional-list-bytes",
"/optional-list-uploadfile", "/optional-list-uploadfile",
"/model-optional-list-uploadfile",
], ],
) )
def test_optional_list_schema(path: str): def test_optional_list_schema(path: str):
@ -109,9 +74,7 @@ def test_optional_list_schema(path: str):
"path", "path",
[ [
"/optional-list-bytes", "/optional-list-bytes",
"/model-optional-list-bytes",
"/optional-list-uploadfile", "/optional-list-uploadfile",
"/model-optional-list-uploadfile",
], ],
) )
def test_optional_list_missing(path: str): def test_optional_list_missing(path: str):
@ -133,17 +96,7 @@ def test_optional_list_missing(path: str):
strict=False, strict=False,
), ),
), ),
pytest.param(
"/model-optional-list-bytes",
marks=pytest.mark.xfail(
raises=(TypeError, AssertionError),
condition=PYDANTIC_V2,
reason="Fails only with PDv2 due to #14297",
strict=False,
),
),
"/optional-list-uploadfile", "/optional-list-uploadfile",
"/model-optional-list-uploadfile",
], ],
) )
def test_optional_list(path: str): def test_optional_list(path: str):
@ -171,38 +124,6 @@ async def read_optional_list_uploadfile_alias(
return {"file_size": [file.size for file in p] if p else None} return {"file_size": [file.size for file in p] if p else None}
class FormModelOptionalListBytesAlias(BaseModel):
p: Optional[List[bytes]] = File(default=None, alias="p_alias")
@app.post("/model-optional-list-bytes-alias")
async def read_model_optional_list_bytes_alias(
p: Annotated[
FormModelOptionalListBytesAlias,
Form(
media_type="multipart/form-data" # Remove media_type when https://github.com/fastapi/fastapi/pull/14343 is fixed
),
],
):
return {"file_size": [len(file) for file in p.p] if p.p else None}
class FormModelOptionalListUploadFileAlias(BaseModel):
p: Optional[List[UploadFile]] = File(default=None, alias="p_alias")
@app.post("/model-optional-list-uploadfile-alias")
async def read_model_optional_list_uploadfile_alias(
p: Annotated[
FormModelOptionalListUploadFileAlias,
Form(
media_type="multipart/form-data" # Remove media_type when https://github.com/fastapi/fastapi/pull/14343 is fixed
),
],
):
return {"file_size": [file.size for file in p.p] if p.p else None}
@pytest.mark.xfail( @pytest.mark.xfail(
raises=AssertionError, raises=AssertionError,
condition=PYDANTIC_V2, condition=PYDANTIC_V2,
@ -213,9 +134,7 @@ async def read_model_optional_list_uploadfile_alias(
"path", "path",
[ [
"/optional-list-bytes-alias", "/optional-list-bytes-alias",
"/model-optional-list-bytes-alias",
"/optional-list-uploadfile-alias", "/optional-list-uploadfile-alias",
"/model-optional-list-uploadfile-alias",
], ],
) )
def test_optional_list_alias_schema(path: str): def test_optional_list_alias_schema(path: str):
@ -256,9 +175,7 @@ def test_optional_list_alias_schema(path: str):
"path", "path",
[ [
"/optional-list-bytes-alias", "/optional-list-bytes-alias",
"/model-optional-list-bytes-alias",
"/optional-list-uploadfile-alias", "/optional-list-uploadfile-alias",
"/model-optional-list-uploadfile-alias",
], ],
) )
def test_optional_list_alias_missing(path: str): def test_optional_list_alias_missing(path: str):
@ -272,33 +189,13 @@ def test_optional_list_alias_missing(path: str):
"path", "path",
[ [
"/optional-list-bytes-alias", "/optional-list-bytes-alias",
pytest.param(
"/model-optional-list-bytes-alias",
marks=pytest.mark.xfail(
raises=AssertionError,
condition=PYDANTIC_V2,
reason="Fails only with PDv2 model",
strict=False,
),
),
"/optional-list-uploadfile-alias", "/optional-list-uploadfile-alias",
pytest.param(
"/model-optional-list-uploadfile-alias",
marks=pytest.mark.xfail(
raises=AssertionError,
condition=PYDANTIC_V2,
reason="Fails only with PDv2 model",
strict=False,
),
),
], ],
) )
def test_optional_list_alias_by_name(path: str): def test_optional_list_alias_by_name(path: str):
client = TestClient(app) client = TestClient(app)
response = client.post(path, files=[("p", b"hello"), ("p", b"world")]) response = client.post(path, files=[("p", b"hello"), ("p", b"world")])
assert response.status_code == 200, ( assert response.status_code == 200, response.text
response.text # model-optional-list-*-alias fail here
)
assert response.json() == {"file_size": None} assert response.json() == {"file_size": None}
@ -314,34 +211,14 @@ def test_optional_list_alias_by_name(path: str):
reason="Fails only with PDv2 model due to #14297", reason="Fails only with PDv2 model due to #14297",
), ),
), ),
pytest.param(
"/model-optional-list-bytes-alias",
marks=pytest.mark.xfail(
raises=(TypeError, AssertionError),
strict=False,
condition=PYDANTIC_V2,
reason="Fails only with PDv2 model due to #14297",
),
),
"/optional-list-uploadfile-alias", "/optional-list-uploadfile-alias",
pytest.param(
"/model-optional-list-uploadfile-alias",
marks=pytest.mark.xfail(
raises=AssertionError,
strict=False,
condition=PYDANTIC_V2,
reason="Fails only with PDv2 model",
),
),
], ],
) )
def test_optional_list_alias_by_alias(path: str): def test_optional_list_alias_by_alias(path: str):
client = TestClient(app) client = TestClient(app)
response = client.post(path, files=[("p_alias", b"hello"), ("p_alias", b"world")]) response = client.post(path, files=[("p_alias", b"hello"), ("p_alias", b"world")])
assert response.status_code == 200, response.text assert response.status_code == 200, response.text
assert response.json() == { assert response.json() == {"file_size": [5, 5]}
"file_size": [5, 5] # /model-optional-list-uploadfile-alias fails here
}
# ===================================================================================== # =====================================================================================
@ -364,46 +241,12 @@ def read_optional_list_uploadfile_validation_alias(
return {"file_size": [file.size for file in p] if p else None} return {"file_size": [file.size for file in p] if p else None}
class FormModelOptionalListBytesValidationAlias(BaseModel):
p: Optional[List[bytes]] = File(default=None, validation_alias="p_val_alias")
@app.post("/model-optional-list-bytes-validation-alias")
def read_model_optional_list_bytes_validation_alias(
p: Annotated[
FormModelOptionalListBytesValidationAlias,
Form(
media_type="multipart/form-data" # Remove media_type when https://github.com/fastapi/fastapi/pull/14343 is fixed
),
],
):
return {"file_size": [len(file) for file in p.p] if p.p else None}
class FormModelOptionalListUploadFileValidationAlias(BaseModel):
p: Optional[List[UploadFile]] = File(default=None, validation_alias="p_val_alias")
@app.post("/model-optional-list-uploadfile-validation-alias")
def read_model_optional_list_uploadfile_validation_alias(
p: Annotated[
FormModelOptionalListUploadFileValidationAlias,
Form(
media_type="multipart/form-data" # Remove media_type when https://github.com/fastapi/fastapi/pull/14343 is fixed
),
],
):
return {"file_size": [file.size for file in p.p] if p.p else None}
@needs_pydanticv2 @needs_pydanticv2
@pytest.mark.parametrize( @pytest.mark.parametrize(
"path", "path",
[ [
"/optional-list-bytes-validation-alias", "/optional-list-bytes-validation-alias",
"/model-optional-list-uploadfile-validation-alias",
"/optional-list-uploadfile-validation-alias", "/optional-list-uploadfile-validation-alias",
"/model-optional-list-bytes-validation-alias",
], ],
) )
def test_optional_validation_alias_schema(path: str): def test_optional_validation_alias_schema(path: str):
@ -445,9 +288,7 @@ def test_optional_validation_alias_schema(path: str):
"path", "path",
[ [
"/optional-list-bytes-validation-alias", "/optional-list-bytes-validation-alias",
"/model-optional-list-bytes-validation-alias",
"/optional-list-uploadfile-validation-alias", "/optional-list-uploadfile-validation-alias",
"/model-optional-list-uploadfile-validation-alias",
], ],
) )
def test_optional_validation_alias_missing(path: str): def test_optional_validation_alias_missing(path: str):
@ -469,19 +310,10 @@ def test_optional_validation_alias_missing(path: str):
reason="Fails due to #14297", reason="Fails due to #14297",
), ),
), ),
pytest.param(
"/model-optional-list-bytes-validation-alias",
marks=pytest.mark.xfail(
raises=(TypeError, AssertionError),
strict=False,
reason="Fails due to #14297",
),
),
pytest.param( pytest.param(
"/optional-list-uploadfile-validation-alias", "/optional-list-uploadfile-validation-alias",
marks=pytest.mark.xfail(raises=AssertionError, strict=False), marks=pytest.mark.xfail(raises=AssertionError, strict=False),
), ),
"/model-optional-list-uploadfile-validation-alias",
], ],
) )
def test_optional_validation_alias_by_name(path: str): def test_optional_validation_alias_by_name(path: str):
@ -499,9 +331,7 @@ def test_optional_validation_alias_by_name(path: str):
"path", "path",
[ [
"/optional-list-bytes-validation-alias", "/optional-list-bytes-validation-alias",
"/model-optional-list-bytes-validation-alias",
"/optional-list-uploadfile-validation-alias", "/optional-list-uploadfile-validation-alias",
"/model-optional-list-uploadfile-validation-alias",
], ],
) )
def test_optional_validation_alias_by_validation_alias(path: str): def test_optional_validation_alias_by_validation_alias(path: str):
@ -509,9 +339,7 @@ def test_optional_validation_alias_by_validation_alias(path: str):
response = client.post( response = client.post(
path, files=[("p_val_alias", b"hello"), ("p_val_alias", b"world")] path, files=[("p_val_alias", b"hello"), ("p_val_alias", b"world")]
) )
assert response.status_code == 200, ( assert response.status_code == 200, response.text
response.text # /model-optional-list-*-validation-alias fail here
)
assert response.json() == { assert response.json() == {
"file_size": [5, 5] # /optional-list-*-validation-alias fail here "file_size": [5, 5] # /optional-list-*-validation-alias fail here
} }
@ -540,50 +368,12 @@ def read_optional_list_uploadfile_alias_and_validation_alias(
return {"file_size": [file.size for file in p] if p else None} return {"file_size": [file.size for file in p] if p else None}
class FormModelOptionalListBytesAliasAndValidationAlias(BaseModel):
p: Optional[List[bytes]] = File(
default=None, alias="p_alias", validation_alias="p_val_alias"
)
@app.post("/model-optional-list-bytes-alias-and-validation-alias")
def read_model_optional_list_bytes_alias_and_validation_alias(
p: Annotated[
FormModelOptionalListBytesAliasAndValidationAlias,
Form(
media_type="multipart/form-data" # Remove media_type when https://github.com/fastapi/fastapi/pull/14343 is fixed
),
],
):
return {"file_size": [len(file) for file in p.p] if p.p else None}
class FormModelOptionalListUploadFileAliasAndValidationAlias(BaseModel):
p: Optional[List[UploadFile]] = File(
default=None, alias="p_alias", validation_alias="p_val_alias"
)
@app.post("/model-optional-list-uploadfile-alias-and-validation-alias")
def read_model_optional_list_uploadfile_alias_and_validation_alias(
p: Annotated[
FormModelOptionalListUploadFileAliasAndValidationAlias,
Form(
media_type="multipart/form-data" # Remove media_type when https://github.com/fastapi/fastapi/pull/14343 is fixed
),
],
):
return {"file_size": [file.size for file in p.p] if p.p else None}
@needs_pydanticv2 @needs_pydanticv2
@pytest.mark.parametrize( @pytest.mark.parametrize(
"path", "path",
[ [
"/optional-list-bytes-alias-and-validation-alias", "/optional-list-bytes-alias-and-validation-alias",
"/model-optional-list-bytes-alias-and-validation-alias",
"/optional-list-uploadfile-alias-and-validation-alias", "/optional-list-uploadfile-alias-and-validation-alias",
"/model-optional-list-uploadfile-alias-and-validation-alias",
], ],
) )
def test_optional_list_alias_and_validation_alias_schema(path: str): def test_optional_list_alias_and_validation_alias_schema(path: str):
@ -625,9 +415,7 @@ def test_optional_list_alias_and_validation_alias_schema(path: str):
"path", "path",
[ [
"/optional-list-bytes-alias-and-validation-alias", "/optional-list-bytes-alias-and-validation-alias",
"/model-optional-list-bytes-alias-and-validation-alias",
"/optional-list-uploadfile-alias-and-validation-alias", "/optional-list-uploadfile-alias-and-validation-alias",
"/model-optional-list-uploadfile-alias-and-validation-alias",
], ],
) )
def test_optional_list_alias_and_validation_alias_missing(path: str): def test_optional_list_alias_and_validation_alias_missing(path: str):
@ -642,9 +430,7 @@ def test_optional_list_alias_and_validation_alias_missing(path: str):
"path", "path",
[ [
"/optional-list-bytes-alias-and-validation-alias", "/optional-list-bytes-alias-and-validation-alias",
"/model-optional-list-bytes-alias-and-validation-alias",
"/optional-list-uploadfile-alias-and-validation-alias", "/optional-list-uploadfile-alias-and-validation-alias",
"/model-optional-list-uploadfile-alias-and-validation-alias",
], ],
) )
def test_optional_list_alias_and_validation_alias_by_name(path: str): def test_optional_list_alias_and_validation_alias_by_name(path: str):
@ -666,19 +452,10 @@ def test_optional_list_alias_and_validation_alias_by_name(path: str):
reason="Fails due to #14297", reason="Fails due to #14297",
), ),
), ),
pytest.param(
"/model-optional-list-bytes-alias-and-validation-alias",
marks=pytest.mark.xfail(
raises=(TypeError, AssertionError),
strict=False,
reason="Fails due to #14297",
),
),
pytest.param( pytest.param(
"/optional-list-uploadfile-alias-and-validation-alias", "/optional-list-uploadfile-alias-and-validation-alias",
marks=pytest.mark.xfail(raises=AssertionError, strict=False), marks=pytest.mark.xfail(raises=AssertionError, strict=False),
), ),
"/model-optional-list-uploadfile-alias-and-validation-alias",
], ],
) )
def test_optional_list_alias_and_validation_alias_by_alias(path: str): def test_optional_list_alias_and_validation_alias_by_alias(path: str):
@ -696,9 +473,7 @@ def test_optional_list_alias_and_validation_alias_by_alias(path: str):
"path", "path",
[ [
"/optional-list-bytes-alias-and-validation-alias", "/optional-list-bytes-alias-and-validation-alias",
"/model-optional-list-bytes-alias-and-validation-alias",
"/optional-list-uploadfile-alias-and-validation-alias", "/optional-list-uploadfile-alias-and-validation-alias",
"/model-optional-list-uploadfile-alias-and-validation-alias",
], ],
) )
def test_optional_list_alias_and_validation_alias_by_validation_alias(path: str): def test_optional_list_alias_and_validation_alias_by_validation_alias(path: str):
@ -706,9 +481,7 @@ def test_optional_list_alias_and_validation_alias_by_validation_alias(path: str)
response = client.post( response = client.post(
path, files=[("p_val_alias", b"hello"), ("p_val_alias", b"world")] path, files=[("p_val_alias", b"hello"), ("p_val_alias", b"world")]
) )
assert response.status_code == 200, ( assert response.status_code == 200, response.text
response.text # /model-optional-list-*-alias-and-validation-alias fails here
)
assert response.json() == { assert response.json() == {
"file_size": [5, 5] # /optional-list-*-alias-and-validation-alias fail here "file_size": [5, 5] # /optional-list-*-alias-and-validation-alias fail here
} }

View File

@ -1,9 +1,8 @@
import pytest import pytest
from dirty_equals import IsDict, IsOneOf, IsPartialDict from dirty_equals import IsDict
from fastapi import FastAPI, File, Form, UploadFile from fastapi import FastAPI, File, UploadFile
from fastapi._compat import PYDANTIC_V2 from fastapi._compat import PYDANTIC_V2
from fastapi.testclient import TestClient from fastapi.testclient import TestClient
from pydantic import BaseModel
from typing_extensions import Annotated from typing_extensions import Annotated
from tests.utils import needs_pydanticv2 from tests.utils import needs_pydanticv2
@ -26,45 +25,11 @@ async def read_required_uploadfile(p: Annotated[UploadFile, File()]):
return {"file_size": p.size} return {"file_size": p.size}
class FormModelRequiredBytes(BaseModel):
p: bytes = File()
@app.post("/model-required-bytes", operation_id="model_required_bytes")
async def read_model_required_bytes(
p: Annotated[
FormModelRequiredBytes,
Form(
media_type="multipart/form-data" # Remove media_type when https://github.com/fastapi/fastapi/pull/14343 is fixed
),
],
):
return {"file_size": len(p.p)}
class FormModelRequiredUploadFile(BaseModel):
p: UploadFile = File()
@app.post("/model-required-uploadfile", operation_id="model_required_uploadfile")
async def read_model_required_uploadfile(
p: Annotated[
FormModelRequiredUploadFile,
Form(
media_type="multipart/form-data" # Remove media_type when https://github.com/fastapi/fastapi/pull/14343 is fixed
),
],
):
return {"file_size": p.p.size}
@pytest.mark.parametrize( @pytest.mark.parametrize(
"path", "path",
[ [
"/required-bytes", "/required-bytes",
"/model-required-bytes",
"/required-uploadfile", "/required-uploadfile",
"/model-required-uploadfile",
], ],
) )
def test_required_schema(path: str): def test_required_schema(path: str):
@ -85,9 +50,7 @@ def test_required_schema(path: str):
"path", "path",
[ [
"/required-bytes", "/required-bytes",
"/model-required-bytes",
"/required-uploadfile", "/required-uploadfile",
"/model-required-uploadfile",
], ],
) )
def test_required_missing(path: str): def test_required_missing(path: str):
@ -101,7 +64,7 @@ def test_required_missing(path: str):
"type": "missing", "type": "missing",
"loc": ["body", "p"], "loc": ["body", "p"],
"msg": "Field required", "msg": "Field required",
"input": IsOneOf(None, {}), "input": None,
} }
] ]
} }
@ -123,9 +86,7 @@ def test_required_missing(path: str):
"path", "path",
[ [
"/required-bytes", "/required-bytes",
"/model-required-bytes",
"/required-uploadfile", "/required-uploadfile",
"/model-required-uploadfile",
], ],
) )
def test_required(path: str): def test_required(path: str):
@ -151,40 +112,6 @@ async def read_required_uploadfile_alias(
return {"file_size": p.size} return {"file_size": p.size}
class FormModelRequiredBytesAlias(BaseModel):
p: bytes = File(alias="p_alias")
@app.post("/model-required-bytes-alias", operation_id="model_required_bytes_alias")
async def read_model_required_bytes_alias(
p: Annotated[
FormModelRequiredBytesAlias,
Form(
media_type="multipart/form-data" # Remove media_type when https://github.com/fastapi/fastapi/pull/14343 is fixed
),
],
):
return {"file_size": len(p.p)}
class FormModelRequiredUploadFileAlias(BaseModel):
p: UploadFile = File(alias="p_alias")
@app.post(
"/model-required-uploadfile-alias", operation_id="model_required_uploadfile_alias"
)
async def read_model_required_uploadfile_alias(
p: Annotated[
FormModelRequiredUploadFileAlias,
Form(
media_type="multipart/form-data" # Remove media_type when https://github.com/fastapi/fastapi/pull/14343 is fixed
),
],
):
return {"file_size": p.p.size}
@pytest.mark.xfail( @pytest.mark.xfail(
raises=AssertionError, raises=AssertionError,
condition=PYDANTIC_V2, condition=PYDANTIC_V2,
@ -195,9 +122,7 @@ async def read_model_required_uploadfile_alias(
"path", "path",
[ [
"/required-bytes-alias", "/required-bytes-alias",
"/model-required-bytes-alias",
"/required-uploadfile-alias", "/required-uploadfile-alias",
"/model-required-uploadfile-alias",
], ],
) )
def test_required_alias_schema(path: str): def test_required_alias_schema(path: str):
@ -218,25 +143,7 @@ def test_required_alias_schema(path: str):
"path", "path",
[ [
"/required-bytes-alias", "/required-bytes-alias",
pytest.param(
"/model-required-bytes-alias",
marks=pytest.mark.xfail(
raises=AssertionError,
strict=False,
condition=PYDANTIC_V2,
reason="Fails only with PDv2 model",
),
),
"/required-uploadfile-alias", "/required-uploadfile-alias",
pytest.param(
"/model-required-uploadfile-alias",
marks=pytest.mark.xfail(
raises=AssertionError,
strict=False,
condition=PYDANTIC_V2,
reason="Fails only with PDv2 model",
),
),
], ],
) )
def test_required_alias_missing(path: str): def test_required_alias_missing(path: str):
@ -248,9 +155,9 @@ def test_required_alias_missing(path: str):
"detail": [ "detail": [
{ {
"type": "missing", "type": "missing",
"loc": ["body", "p_alias"], # model-required-*-alias fail here "loc": ["body", "p_alias"],
"msg": "Field required", "msg": "Field required",
"input": IsOneOf(None, {}), "input": None,
} }
] ]
} }
@ -272,31 +179,13 @@ def test_required_alias_missing(path: str):
"path", "path",
[ [
"/required-bytes-alias", "/required-bytes-alias",
pytest.param(
"/model-required-bytes-alias",
marks=pytest.mark.xfail(
raises=AssertionError,
condition=PYDANTIC_V2,
reason="Fails only with PDv2 model",
strict=False,
),
),
"/required-uploadfile-alias", "/required-uploadfile-alias",
pytest.param(
"/model-required-uploadfile-alias",
marks=pytest.mark.xfail(
raises=AssertionError,
condition=PYDANTIC_V2,
reason="Fails only with PDv2 model",
strict=False,
),
),
], ],
) )
def test_required_alias_by_name(path: str): def test_required_alias_by_name(path: str):
client = TestClient(app) client = TestClient(app)
response = client.post(path, files=[("p", b"hello")]) response = client.post(path, files=[("p", b"hello")])
assert response.status_code == 422 # model-required-upload-alias fail here assert response.status_code == 422
assert response.json() == IsDict( assert response.json() == IsDict(
{ {
"detail": [ "detail": [
@ -304,11 +193,7 @@ def test_required_alias_by_name(path: str):
"type": "missing", "type": "missing",
"loc": ["body", "p_alias"], "loc": ["body", "p_alias"],
"msg": "Field required", "msg": "Field required",
"input": IsOneOf( # model-required-bytes-alias fail here "input": None,
None,
{"p": IsPartialDict({"size": 5})},
{"p": b"hello"}, # ToDo: check this
),
} }
] ]
} }
@ -330,33 +215,13 @@ def test_required_alias_by_name(path: str):
"path", "path",
[ [
"/required-bytes-alias", "/required-bytes-alias",
pytest.param(
"/model-required-bytes-alias",
marks=pytest.mark.xfail(
raises=AssertionError,
strict=False,
condition=PYDANTIC_V2,
reason="Fails only with PDv2 model",
),
),
"/required-uploadfile-alias", "/required-uploadfile-alias",
pytest.param(
"/model-required-uploadfile-alias",
marks=pytest.mark.xfail(
raises=AssertionError,
strict=False,
condition=PYDANTIC_V2,
reason="Fails only with PDv2 model",
),
),
], ],
) )
def test_required_alias_by_alias(path: str): def test_required_alias_by_alias(path: str):
client = TestClient(app) client = TestClient(app)
response = client.post(path, files=[("p_alias", b"hello")]) response = client.post(path, files=[("p_alias", b"hello")])
assert response.status_code == 200, ( # model-required-*-alias fail here assert response.status_code == 200, response.text
response.text
)
assert response.json() == {"file_size": 5} assert response.json() == {"file_size": 5}
@ -383,52 +248,12 @@ def read_required_uploadfile_validation_alias(
return {"file_size": p.size} return {"file_size": p.size}
class FormModelRequiredBytesValidationAlias(BaseModel):
p: bytes = File(validation_alias="p_val_alias")
@app.post(
"/model-required-bytes-validation-alias",
operation_id="model_required_bytes_validation_alias",
)
def read_model_required_bytes_validation_alias(
p: Annotated[
FormModelRequiredBytesValidationAlias,
Form(
media_type="multipart/form-data" # Remove media_type when https://github.com/fastapi/fastapi/pull/14343 is fixed
),
],
):
return {"file_size": len(p.p)} # pragma: no cover
class FormModelRequiredUploadFileValidationAlias(BaseModel):
p: UploadFile = File(validation_alias="p_val_alias")
@app.post(
"/model-required-uploadfile-validation-alias",
operation_id="model_required_uploadfile_validation_alias",
)
def read_model_required_uploadfile_validation_alias(
p: Annotated[
FormModelRequiredUploadFileValidationAlias,
Form(
media_type="multipart/form-data" # Remove media_type when https://github.com/fastapi/fastapi/pull/14343 is fixed
),
],
):
return {"file_size": p.p.size}
@needs_pydanticv2 @needs_pydanticv2
@pytest.mark.parametrize( @pytest.mark.parametrize(
"path", "path",
[ [
"/required-bytes-validation-alias", "/required-bytes-validation-alias",
"/model-required-uploadfile-validation-alias",
"/required-uploadfile-validation-alias", "/required-uploadfile-validation-alias",
"/model-required-bytes-validation-alias",
], ],
) )
def test_required_validation_alias_schema(path: str): def test_required_validation_alias_schema(path: str):
@ -457,12 +282,10 @@ def test_required_validation_alias_schema(path: str):
"/required-bytes-validation-alias", "/required-bytes-validation-alias",
marks=pytest.mark.xfail(raises=AssertionError, strict=False), marks=pytest.mark.xfail(raises=AssertionError, strict=False),
), ),
"/model-required-bytes-validation-alias",
pytest.param( pytest.param(
"/required-uploadfile-validation-alias", "/required-uploadfile-validation-alias",
marks=pytest.mark.xfail(raises=AssertionError, strict=False), marks=pytest.mark.xfail(raises=AssertionError, strict=False),
), ),
"/model-required-uploadfile-validation-alias",
], ],
) )
def test_required_validation_alias_missing(path: str): def test_required_validation_alias_missing(path: str):
@ -478,7 +301,7 @@ def test_required_validation_alias_missing(path: str):
"p_val_alias", "p_val_alias",
], ],
"msg": "Field required", "msg": "Field required",
"input": IsOneOf(None, {}), "input": None,
} }
] ]
} }
@ -492,15 +315,10 @@ def test_required_validation_alias_missing(path: str):
"/required-bytes-validation-alias", "/required-bytes-validation-alias",
marks=pytest.mark.xfail(raises=AssertionError, strict=False), marks=pytest.mark.xfail(raises=AssertionError, strict=False),
), ),
pytest.param(
"/model-required-bytes-validation-alias",
marks=pytest.mark.xfail(raises=AssertionError, strict=False),
),
pytest.param( pytest.param(
"/required-uploadfile-validation-alias", "/required-uploadfile-validation-alias",
marks=pytest.mark.xfail(raises=AssertionError, strict=False), marks=pytest.mark.xfail(raises=AssertionError, strict=False),
), ),
"/model-required-uploadfile-validation-alias",
], ],
) )
def test_required_validation_alias_by_name(path: str): def test_required_validation_alias_by_name(path: str):
@ -510,15 +328,13 @@ def test_required_validation_alias_by_name(path: str):
response.text response.text
) )
assert response.json() == { assert response.json() == { # pragma: no cover
"detail": [ "detail": [
{ {
"type": "missing", "type": "missing",
"loc": ["body", "p_val_alias"], "loc": ["body", "p_val_alias"],
"msg": "Field required", "msg": "Field required",
"input": IsOneOf( # /model-required-bytes-validation-alias fails here "input": None,
None, {"p": IsPartialDict({"size": 5})}
),
} }
] ]
} }
@ -532,24 +348,19 @@ def test_required_validation_alias_by_name(path: str):
"/required-bytes-validation-alias", "/required-bytes-validation-alias",
marks=pytest.mark.xfail(raises=AssertionError, strict=False), marks=pytest.mark.xfail(raises=AssertionError, strict=False),
), ),
pytest.param(
"/model-required-bytes-validation-alias",
marks=pytest.mark.xfail(raises=AssertionError, strict=False),
),
pytest.param( pytest.param(
"/required-uploadfile-validation-alias", "/required-uploadfile-validation-alias",
marks=pytest.mark.xfail(raises=AssertionError, strict=False), marks=pytest.mark.xfail(raises=AssertionError, strict=False),
), ),
"/model-required-uploadfile-validation-alias",
], ],
) )
def test_required_validation_alias_by_validation_alias(path: str): def test_required_validation_alias_by_validation_alias(path: str):
client = TestClient(app) client = TestClient(app)
response = client.post(path, files=[("p_val_alias", b"hello")]) response = client.post(path, files=[("p_val_alias", b"hello")])
assert response.status_code == 200, ( # all 3 fail here assert response.status_code == 200, ( # all 2 fail here
response.text response.text
) )
assert response.json() == {"file_size": 5} assert response.json() == {"file_size": 5} # pragma: no cover
# ===================================================================================== # =====================================================================================
@ -576,52 +387,12 @@ def read_required_uploadfile_alias_and_validation_alias(
return {"file_size": p.size} return {"file_size": p.size}
class FormModelRequiredBytesAliasAndValidationAlias(BaseModel):
p: bytes = File(alias="p_alias", validation_alias="p_val_alias")
@app.post(
"/model-required-bytes-alias-and-validation-alias",
operation_id="model_required_bytes_alias_and_validation_alias",
)
def read_model_required_bytes_alias_and_validation_alias(
p: Annotated[
FormModelRequiredBytesAliasAndValidationAlias,
Form(
media_type="multipart/form-data" # Remove media_type when https://github.com/fastapi/fastapi/pull/14343 is fixed
),
],
):
return {"file_size": len(p.p)} # pragma: no cover
class FormModelRequiredUploadFileAliasAndValidationAlias(BaseModel):
p: UploadFile = File(alias="p_alias", validation_alias="p_val_alias")
@app.post(
"/model-required-uploadfile-alias-and-validation-alias",
operation_id="model_required_uploadfile_alias_and_validation_alias",
)
def read_model_required_uploadfile_alias_and_validation_alias(
p: Annotated[
FormModelRequiredUploadFileAliasAndValidationAlias,
Form(
media_type="multipart/form-data" # Remove media_type when https://github.com/fastapi/fastapi/pull/14343 is fixed
),
],
):
return {"file_size": p.p.size}
@needs_pydanticv2 @needs_pydanticv2
@pytest.mark.parametrize( @pytest.mark.parametrize(
"path", "path",
[ [
"/required-bytes-alias-and-validation-alias", "/required-bytes-alias-and-validation-alias",
"/model-required-bytes-alias-and-validation-alias",
"/required-uploadfile-alias-and-validation-alias", "/required-uploadfile-alias-and-validation-alias",
"/model-required-uploadfile-alias-and-validation-alias",
], ],
) )
def test_required_alias_and_validation_alias_schema(path: str): def test_required_alias_and_validation_alias_schema(path: str):
@ -650,12 +421,10 @@ def test_required_alias_and_validation_alias_schema(path: str):
"/required-bytes-alias-and-validation-alias", "/required-bytes-alias-and-validation-alias",
marks=pytest.mark.xfail(raises=AssertionError, strict=False), marks=pytest.mark.xfail(raises=AssertionError, strict=False),
), ),
"/model-required-bytes-alias-and-validation-alias",
pytest.param( pytest.param(
"/required-uploadfile-alias-and-validation-alias", "/required-uploadfile-alias-and-validation-alias",
marks=pytest.mark.xfail(raises=AssertionError, strict=False), marks=pytest.mark.xfail(raises=AssertionError, strict=False),
), ),
"/model-required-uploadfile-alias-and-validation-alias",
], ],
) )
def test_required_alias_and_validation_alias_missing(path: str): def test_required_alias_and_validation_alias_missing(path: str):
@ -671,7 +440,7 @@ def test_required_alias_and_validation_alias_missing(path: str):
"p_val_alias", # /required-*-alias-and-validation-alias fail here "p_val_alias", # /required-*-alias-and-validation-alias fail here
], ],
"msg": "Field required", "msg": "Field required",
"input": IsOneOf(None, {}), "input": None,
} }
] ]
} }
@ -685,12 +454,10 @@ def test_required_alias_and_validation_alias_missing(path: str):
"/required-bytes-alias-and-validation-alias", "/required-bytes-alias-and-validation-alias",
marks=pytest.mark.xfail(raises=AssertionError, strict=False), marks=pytest.mark.xfail(raises=AssertionError, strict=False),
), ),
"/model-required-bytes-alias-and-validation-alias",
pytest.param( pytest.param(
"/required-uploadfile-alias-and-validation-alias", "/required-uploadfile-alias-and-validation-alias",
marks=pytest.mark.xfail(raises=AssertionError, strict=False), marks=pytest.mark.xfail(raises=AssertionError, strict=False),
), ),
"/model-required-uploadfile-alias-and-validation-alias",
], ],
) )
def test_required_alias_and_validation_alias_by_name(path: str): def test_required_alias_and_validation_alias_by_name(path: str):
@ -707,7 +474,7 @@ def test_required_alias_and_validation_alias_by_name(path: str):
"p_val_alias", # /required-*-alias-and-validation-alias fail here "p_val_alias", # /required-*-alias-and-validation-alias fail here
], ],
"msg": "Field required", "msg": "Field required",
"input": IsOneOf(None, {"p": IsPartialDict({"size": 5})}), "input": None,
} }
] ]
} }
@ -721,15 +488,10 @@ def test_required_alias_and_validation_alias_by_name(path: str):
"/required-bytes-alias-and-validation-alias", "/required-bytes-alias-and-validation-alias",
marks=pytest.mark.xfail(raises=AssertionError, strict=False), marks=pytest.mark.xfail(raises=AssertionError, strict=False),
), ),
pytest.param(
"/model-required-bytes-alias-and-validation-alias",
marks=pytest.mark.xfail(raises=AssertionError, strict=False),
),
pytest.param( pytest.param(
"/required-uploadfile-alias-and-validation-alias", "/required-uploadfile-alias-and-validation-alias",
marks=pytest.mark.xfail(raises=AssertionError, strict=False), marks=pytest.mark.xfail(raises=AssertionError, strict=False),
), ),
"/model-required-uploadfile-alias-and-validation-alias",
], ],
) )
def test_required_alias_and_validation_alias_by_alias(path: str): def test_required_alias_and_validation_alias_by_alias(path: str):
@ -739,17 +501,13 @@ def test_required_alias_and_validation_alias_by_alias(path: str):
response.text # /required-*-alias-and-validation-alias fails here response.text # /required-*-alias-and-validation-alias fails here
) )
assert response.json() == { assert response.json() == { # pragma: no cover
"detail": [ "detail": [
{ {
"type": "missing", "type": "missing",
"loc": ["body", "p_val_alias"], "loc": ["body", "p_val_alias"],
"msg": "Field required", "msg": "Field required",
"input": IsOneOf( "input": None,
None,
# /model-required-uploadfile-alias-and-validation-alias fails here
{"p_alias": IsPartialDict({"size": 5})},
),
} }
] ]
} }
@ -763,21 +521,16 @@ def test_required_alias_and_validation_alias_by_alias(path: str):
"/required-bytes-alias-and-validation-alias", "/required-bytes-alias-and-validation-alias",
marks=pytest.mark.xfail(raises=AssertionError, strict=False), marks=pytest.mark.xfail(raises=AssertionError, strict=False),
), ),
pytest.param(
"/model-required-bytes-alias-and-validation-alias",
marks=pytest.mark.xfail(raises=AssertionError, strict=False),
),
pytest.param( pytest.param(
"/required-uploadfile-alias-and-validation-alias", "/required-uploadfile-alias-and-validation-alias",
marks=pytest.mark.xfail(raises=AssertionError, strict=False), marks=pytest.mark.xfail(raises=AssertionError, strict=False),
), ),
"/model-required-uploadfile-alias-and-validation-alias",
], ],
) )
def test_required_alias_and_validation_alias_by_validation_alias(path: str): def test_required_alias_and_validation_alias_by_validation_alias(path: str):
client = TestClient(app) client = TestClient(app)
response = client.post(path, files=[("p_val_alias", b"hello")]) response = client.post(path, files=[("p_val_alias", b"hello")])
assert response.status_code == 200, ( # all 3 fail here assert response.status_code == 200, ( # all 2 fail here
response.text response.text
) )
assert response.json() == {"file_size": 5} assert response.json() == {"file_size": 5} # pragma: no cover