Browse Source

Fix File model tests

pull/14293/head
Yurii Motov 8 months ago
parent
commit
73a1b8f5c2
  1. 168
      tests/test_aliases/test_file_model_aliases.py
  2. 102
      tests/test_aliases/test_uploadfile_model_aliases.py

168
tests/test_aliases/test_file_model_aliases.py

@ -1,9 +1,9 @@
from typing import List, Optional from typing import List, Optional
import pytest import pytest
from fastapi import FastAPI, File from fastapi import FastAPI, File, Form
from fastapi.testclient import TestClient from fastapi.testclient import TestClient
from pydantic import BaseModel, Field from pydantic import BaseModel
from ..utils import needs_pydanticv2 from ..utils import needs_pydanticv2
@ -13,30 +13,33 @@ app = FastAPI()
# ===================================================================================== # =====================================================================================
# Field(alias=...) # Field(alias=...)
# Current situation: doesn't work due to validation error # Current situation: doesn't work (neither validation nor schema generation)
# ------------------------------ # ------------------------------
# required field # required field
class RequiredFieldAliasModel(BaseModel): class RequiredFieldAliasModel(BaseModel):
file: bytes = Field(alias="file_alias") file: bytes = File(alias="file_alias")
@app.post("/required-field-alias-model") @app.post("/required-field-alias-model")
async def required_field_alias_model( # pragma: no cover (remove `no cover` when bug fixed) async def required_field_alias_model(data: RequiredFieldAliasModel = Form()):
data: RequiredFieldAliasModel = File(...),
):
return {"file_size": len(data.file)} return {"file_size": len(data.file)}
@pytest.mark.xfail(raises=AssertionError, strict=False)
def test_required_field_alias_model_by_name(): def test_required_field_alias_model_by_name():
client = TestClient(app) client = TestClient(app)
resp = client.post("/required-field-alias-model", files={"file": b"content"}) resp = client.post("/required-field-alias-model", files={"file": b"content"})
assert resp.status_code == 422 assert resp.status_code == 422
detail = resp.json()["detail"] detail = resp.json()["detail"]
assert detail[0]["msg"] == "Field required" assert detail[0]["msg"] == "Field required"
assert "file_alias" in detail[0]["loc"] # Currently fails due to issue with aliases:
# AssertionError: assert 'Input should be a valid bytes' == 'Field required'
# Uncomment when the assertion above passes:
# assert "file_alias" in detail[0]["loc"]
@pytest.mark.xfail(raises=AssertionError, strict=False) @pytest.mark.xfail(raises=AssertionError, strict=False)
@ -44,20 +47,25 @@ def test_required_field_alias_model_by_alias():
client = TestClient(app) client = TestClient(app)
resp = client.post("/required-field-alias-model", files={"file_alias": b"content"}) resp = client.post("/required-field-alias-model", files={"file_alias": b"content"})
assert resp.status_code == 200, resp.text assert resp.status_code == 200, resp.text
# Fails with: # Currently fails due to issue with aliases:
# AssertionError: assert 422 == 200 # AssertionError: assert 422 == 200
# {"detail":[{"type":"bytes_type","loc":["body","file_alias"],"msg":"Input should be a valid bytes","input":{"filename":"upload","file":{"_file":{},"_max_size":1048576,"_rolled":false,"_TemporaryFileArgs":{"mode":"w+b","buffering":-1,"suffix":null,"prefix":null,"encoding":null,"newline":null,"dir":null,"errors":null}},"size":7,"headers":{"content-disposition":"form-data; name=\"file_alias\"; filename=\"upload\"","content-type":"application/octet-stream"},"_max_mem_size":1048576}}]} # {"detail":[{"type":"missing","loc":["body","file"],"msg":"Field required","input":{"file_alias":"content"}}]}
# Uncomment when the assertion above passes: # Uncomment when the assertion above passes:
# assert resp.json() == {"file_size": 7} # assert resp.json() == {"file_size": 7}
@pytest.mark.xfail(raises=AssertionError, strict=False)
def test_required_field_alias_model_schema(): def test_required_field_alias_model_schema():
openapi = app.openapi() openapi = app.openapi()
body_schema = openapi["components"]["schemas"]["RequiredFieldAliasModel"] body_schema = openapi["components"]["schemas"]["RequiredFieldAliasModel"]
assert body_schema["properties"] == { assert body_schema["properties"] == {
"file_alias": {"title": "File Alias", "type": "string", "format": "binary"} "file_alias": {"title": "File Alias", "type": "string", "format": "binary"}
} }
# Currently fails due to issue with aliases:
# AssertionError: assert
# {'file': {'type': 'string', 'format': 'binary', 'title': 'File'}} ==
# {'file_alias': {'title': 'File Alias', 'type': 'string', 'format': 'binary'}}
# ------------------------------ # ------------------------------
@ -65,23 +73,27 @@ def test_required_field_alias_model_schema():
class OptionalFieldAliasModel(BaseModel): class OptionalFieldAliasModel(BaseModel):
file: Optional[bytes] = Field(None, alias="file_alias") file: Optional[bytes] = File(None, alias="file_alias")
@app.post("/optional-field-alias-model") @app.post("/optional-field-alias-model")
async def optional_field_alias_model( # pragma: no cover (remove `no cover` when bug fixed) async def optional_field_alias_model(data: OptionalFieldAliasModel = Form()):
data: OptionalFieldAliasModel = File(...),
):
if data.file is None: if data.file is None:
return {"file_size": None} return {"file_size": None}
return {"file_size": len(data.file)} return {"file_size": len(data.file)}
@pytest.mark.xfail(raises=AssertionError, strict=False)
def test_optional_field_alias_model_by_name(): def test_optional_field_alias_model_by_name():
client = TestClient(app) client = TestClient(app)
resp = client.post("/optional-field-alias-model", files={"file": b"content"}) resp = client.post("/optional-field-alias-model", files={"file": b"content"})
assert resp.status_code == 200 assert resp.status_code == 200, resp.text
assert resp.json() == {"file_size": None} # Currently fails due to issue with aliases:
# AssertionError: assert 422 == 200
# {"detail":[{"type":"bytes_type","loc":["body","file"],"msg":"Input should be a valid bytes","input":{"filename":"upload","file":{"_file":{},"_max_size":1048576,"_rolled":false,"_TemporaryFileArgs":{"mode":"w+b","buffering":-1,"suffix":null,"prefix":null,"encoding":null,"newline":null,"dir":null,"errors":null}},"size":7,"headers":{"content-disposition":"form-data; name=\"file\"; filename=\"upload\"","content-type":"application/octet-stream"},"_max_mem_size":1048576}}]}
# Uncomment when the assertion above passes:
# assert resp.json() == {"file_size": None}
@pytest.mark.xfail(raises=AssertionError, strict=False) @pytest.mark.xfail(raises=AssertionError, strict=False)
@ -89,14 +101,12 @@ def test_optional_field_alias_model_by_alias():
client = TestClient(app) client = TestClient(app)
resp = client.post("/optional-field-alias-model", files={"file_alias": b"content"}) resp = client.post("/optional-field-alias-model", files={"file_alias": b"content"})
assert resp.status_code == 200, resp.text assert resp.status_code == 200, resp.text
# Fails with: assert resp.json() == {"file_size": 7}
# AssertionError: assert 422 == 200 # Currently fails due to issue with aliases:
# {"detail":[{"type":"bytes_type","loc":["body","file_alias"],"msg":"Input should be a valid bytes","input":{"filename":"upload","file":{"_file":{},"_max_size":1048576,"_rolled":false,"_TemporaryFileArgs":{"mode":"w+b","buffering":-1,"suffix":null,"prefix":null,"encoding":null,"newline":null,"dir":null,"errors":null}},"size":7,"headers":{"content-disposition":"form-data; name=\"file_alias\"; filename=\"upload\"","content-type":"application/octet-stream"},"_max_mem_size":1048576}}]} # AssertionError: assert {'file_size': None} == {'file_size': 7}
# Uncomment when the assertion above passes:
# assert resp.json() == {"file_size": 7}
@pytest.mark.xfail(raises=AssertionError, strict=False)
def test_optional_field_alias_model_schema(): def test_optional_field_alias_model_schema():
openapi = app.openapi() openapi = app.openapi()
body_schema = openapi["components"]["schemas"]["OptionalFieldAliasModel"] body_schema = openapi["components"]["schemas"]["OptionalFieldAliasModel"]
@ -106,6 +116,10 @@ def test_optional_field_alias_model_schema():
"title": "File Alias", "title": "File Alias",
}, },
} }
# Currently fails due to issue with aliases:
# AssertionError: assert
# {'file': {'anyOf': [{'type': 'string', 'format': 'binary'}, {'type': 'null'}], 'title': 'File'}} ==
# {'file_alias': {'anyOf': [{'type': 'string', 'format': 'binary'}, {'type': 'null'}], 'title': 'File Alias'}}
# ------------------------------ # ------------------------------
@ -113,16 +127,15 @@ def test_optional_field_alias_model_schema():
class ListFieldAliasModel(BaseModel): class ListFieldAliasModel(BaseModel):
files: List[bytes] = Field(alias="files_alias") files: List[bytes] = File(alias="files_alias")
@app.post("/list-field-alias-model") @app.post("/list-field-alias-model")
async def list_field_alias_model( # pragma: no cover (remove `no cover` when bug fixed) async def list_field_alias_model(data: ListFieldAliasModel = Form()):
data: ListFieldAliasModel = File(...),
):
return {"file_sizes": [len(file) for file in data.files]} return {"file_sizes": [len(file) for file in data.files]}
@pytest.mark.xfail(raises=AssertionError, strict=False)
def test_list_field_alias_model_by_name(): def test_list_field_alias_model_by_name():
client = TestClient(app) client = TestClient(app)
resp = client.post( resp = client.post(
@ -132,7 +145,11 @@ def test_list_field_alias_model_by_name():
assert resp.status_code == 422 assert resp.status_code == 422
detail = resp.json()["detail"] detail = resp.json()["detail"]
assert detail[0]["msg"] == "Field required" assert detail[0]["msg"] == "Field required"
assert "files_alias" in detail[0]["loc"] # Currently fails due to issue with aliases:
# AssertionError: assert 'Input should be a valid bytes' == 'Field required'
# Uncomment when the assertion above passes:
# assert "files_alias" in detail[0]["loc"]
@pytest.mark.xfail(raises=AssertionError, strict=False) @pytest.mark.xfail(raises=AssertionError, strict=False)
@ -143,14 +160,15 @@ def test_list_field_alias_model_by_alias():
files=[("files_alias", b"content1"), ("files_alias", b"content2")], files=[("files_alias", b"content1"), ("files_alias", b"content2")],
) )
assert resp.status_code == 200, resp.text assert resp.status_code == 200, resp.text
# Fails with: # Currently fails due to issue with aliases:
# AssertionError: assert 422 == 200 # AssertionError: assert 422 == 200
# {"detail":[{"type":"bytes_type","loc":["body","files_alias",0],"msg":"Input should be a valid bytes","input":{"filename":"upload","file":{"_file":{},"_max_size":1048576,"_rolled":false,"_TemporaryFileArgs":{"mode":"w+b","buffering":-1,"suffix":null,"prefix":null,"encoding":null,"newline":null,"dir":null,"errors":null}},"size":8,"headers":{"content-disposition":"form-data; name=\"files_alias\"; filename=\"upload\"","content-type":"application/octet-stream"},"_max_mem_size":1048576}},{"type":"bytes_type","loc":["body","files_alias",1],"msg":"Input should be a valid bytes","input":{"filename":"upload","file":{"_file":{},"_max_size":1048576,"_rolled":false,"_TemporaryFileArgs":{"mode":"w+b","buffering":-1,"suffix":null,"prefix":null,"encoding":null,"newline":null,"dir":null,"errors":null}},"size":8,"headers":{"content-disposition":"form-data; name=\"files_alias\"; filename=\"upload\"","content-type":"application/octet-stream"},"_max_mem_size":1048576}}]} # {"detail":[{"type":"missing","loc":["body","files"],"msg":"Field required","input":{"files_alias":["content1","content2"]}}]}
# Uncomment when the assertion above passes: # Uncomment when the assertion above passes:
# assert resp.json() == {"file_sizes": [8, 8]} # assert resp.json() == {"file_sizes": [8, 8]}
@pytest.mark.xfail(raises=AssertionError, strict=False)
def test_list_field_alias_model_schema(): def test_list_field_alias_model_schema():
openapi = app.openapi() openapi = app.openapi()
body_schema = openapi["components"]["schemas"]["ListFieldAliasModel"] body_schema = openapi["components"]["schemas"]["ListFieldAliasModel"]
@ -161,6 +179,10 @@ def test_list_field_alias_model_schema():
"type": "array", "type": "array",
}, },
} }
# Currently fails due to issue with aliases:
# AssertionError: assert
# {'files': {'items': {'type': 'string', 'format': 'binary'}, 'type': 'array', 'title': 'Files'}} ==
# {'files_alias': {'items': {'type': 'string', 'format': 'binary'}, 'title': 'Files Alias', 'type': 'array'}}
# ------------------------------ # ------------------------------
@ -168,26 +190,28 @@ def test_list_field_alias_model_schema():
class OptionalListFieldAliasModel(BaseModel): class OptionalListFieldAliasModel(BaseModel):
files: Optional[List[bytes]] = Field(None, alias="files_alias") files: Optional[List[bytes]] = File(None, alias="files_alias")
@app.post("/optional-list-field-alias-model") @app.post("/optional-list-field-alias-model")
async def optional_list_field_alias_model( # pragma: no cover (remove `no cover` when bug fixed) async def optional_list_field_alias_model(data: OptionalListFieldAliasModel = Form()):
data: OptionalListFieldAliasModel = File(),
):
if data.files is None: if data.files is None:
return {"file_sizes": None} return {"file_sizes": None}
return {"file_sizes": [len(file) for file in data.files]} return {"file_sizes": [len(file) for file in data.files]}
@pytest.mark.xfail(raises=AssertionError, strict=False)
def test_optional_list_field_alias_model_by_name(): def test_optional_list_field_alias_model_by_name():
client = TestClient(app) client = TestClient(app)
resp = client.post( resp = client.post(
"/optional-list-field-alias-model", "/optional-list-field-alias-model",
files=[("files", b"content1"), ("files", b"content2")], files=[("files", b"content1"), ("files", b"content2")],
) )
assert resp.status_code == 200 assert resp.status_code == 200, resp.text
assert resp.json() == {"file_sizes": None} # Currently fails due to issue with aliases
# Uncomment when the assertion above passes:
# assert resp.json() == {"file_sizes": None}
@pytest.mark.xfail(raises=AssertionError, strict=False) @pytest.mark.xfail(raises=AssertionError, strict=False)
@ -198,14 +222,12 @@ def test_optional_list_field_alias_model_by_alias():
files=[("files_alias", b"content1"), ("files_alias", b"content2")], files=[("files_alias", b"content1"), ("files_alias", b"content2")],
) )
assert resp.status_code == 200, resp.text assert resp.status_code == 200, resp.text
# Fails with: assert resp.json() == {"file_sizes": [8, 8]}
# AssertionError: assert 422 == 200 # Currently fails due to issue with aliases
# {"detail":[{"type":"bytes_type","loc":["body","files_alias",0],"msg":"Input should be a valid bytes","input":{"filename":"upload","file":{"_file":{},"_max_size":1048576,"_rolled":false,"_TemporaryFileArgs":{"mode":"w+b","buffering":-1,"suffix":null,"prefix":null,"encoding":null,"newline":null,"dir":null,"errors":null}},"size":8,"headers":{"content-disposition":"form-data; name=\"files_alias\"; filename=\"upload\"","content-type":"application/octet-stream"},"_max_mem_size":1048576}},{"type":"bytes_type","loc":["body","files_alias",1],"msg":"Input should be a valid bytes","input":{"filename":"upload","file":{"_file":{},"_max_size":1048576,"_rolled":false,"_TemporaryFileArgs":{"mode":"w+b","buffering":-1,"suffix":null,"prefix":null,"encoding":null,"newline":null,"dir":null,"errors":null}},"size":8,"headers":{"content-disposition":"form-data; name=\"files_alias\"; filename=\"upload\"","content-type":"application/octet-stream"},"_max_mem_size":1048576}}]} # AssertionError: assert {'file_sizes': None} == {'file_sizes': [8, 8]}
# Uncomment when the assertion above passes:
# assert resp.json() == {"file_sizes": [8, 8]}
@pytest.mark.xfail(raises=AssertionError, strict=False)
def test_optional_list_field_alias_model_schema(): def test_optional_list_field_alias_model_schema():
openapi = app.openapi() openapi = app.openapi()
body_schema = openapi["components"]["schemas"]["OptionalListFieldAliasModel"] body_schema = openapi["components"]["schemas"]["OptionalListFieldAliasModel"]
@ -218,11 +240,15 @@ def test_optional_list_field_alias_model_schema():
"title": "Files Alias", "title": "Files Alias",
} }
} }
# Currently fails due to issue with aliases:
# AssertionError: assert
# {'files': {'anyOf': [{'items': {'type': 'string', 'format': 'binary'}, 'type': 'array'}, {'type': 'null'}], 'title': 'Files'}} ==
# {'files_alias': {'anyOf': [{'items': {'type': 'string', 'format': 'binary'}, 'type': 'array'}, {'type': 'null'}], 'title': 'Files Alias'}}
# ===================================================================================== # =====================================================================================
# Field(validation_alias=...) # Field(validation_alias=...)
# Current situation: doesn't work due to validation error # Current situation: validation doesn't work, but schema generation works
# ------------------------------ # ------------------------------
@ -230,15 +256,15 @@ def test_optional_list_field_alias_model_schema():
class RequiredFieldValidationAliasModel(BaseModel): class RequiredFieldValidationAliasModel(BaseModel):
file: bytes = Field(validation_alias="file_val_alias") file: bytes = File(validation_alias="file_val_alias")
@app.post( @app.post(
"/required-field-validation-alias-model", "/required-field-validation-alias-model",
operation_id="required_field_validation_alias_model", operation_id="required_field_validation_alias_model",
) )
async def required_field_validation_alias_model( # pragma: no cover (remove `no cover` when bug fixed) async def required_field_validation_alias_model(
data: RequiredFieldValidationAliasModel = File(...), data: RequiredFieldValidationAliasModel = Form(),
): ):
return {"file_size": len(data.file)} return {"file_size": len(data.file)}
@ -261,7 +287,6 @@ def test_required_field_validation_alias_model_by_alias():
"/required-field-validation-alias-model", files={"file_val_alias": b"content"} "/required-field-validation-alias-model", files={"file_val_alias": b"content"}
) )
assert resp.status_code == 200, resp.text assert resp.status_code == 200, resp.text
# Fails with:
# AssertionError: assert 422 == 200 # AssertionError: assert 422 == 200
# {"detail":[{"type":"bytes_type","loc":["body","file_val_alias"],"msg":"Input should be a valid bytes","input":{"filename":"upload","file":{"_file":{},"_max_size":1048576,"_rolled":false,"_TemporaryFileArgs":{"mode":"w+b","buffering":-1,"suffix":null,"prefix":null,"encoding":null,"newline":null,"dir":null,"errors":null}},"size":7,"headers":{"content-disposition":"form-data; name=\"file_val_alias\"; filename=\"upload\"","content-type":"application/octet-stream"},"_max_mem_size":1048576}}]} # {"detail":[{"type":"bytes_type","loc":["body","file_val_alias"],"msg":"Input should be a valid bytes","input":{"filename":"upload","file":{"_file":{},"_max_size":1048576,"_rolled":false,"_TemporaryFileArgs":{"mode":"w+b","buffering":-1,"suffix":null,"prefix":null,"encoding":null,"newline":null,"dir":null,"errors":null}},"size":7,"headers":{"content-disposition":"form-data; name=\"file_val_alias\"; filename=\"upload\"","content-type":"application/octet-stream"},"_max_mem_size":1048576}}]}
@ -286,15 +311,15 @@ def test_required_field_validation_alias_model_schema():
class OptionalFieldValidationAliasModel(BaseModel): class OptionalFieldValidationAliasModel(BaseModel):
file: Optional[bytes] = Field(None, validation_alias="file_val_alias") file: Optional[bytes] = File(None, validation_alias="file_val_alias")
@app.post( @app.post(
"/optional-field-validation-alias-model", "/optional-field-validation-alias-model",
operation_id="optional_field_validation_alias_model", operation_id="optional_field_validation_alias_model",
) )
async def optional_field_validation_alias_model( # pragma: no cover (remove `no cover` when bug fixed) async def optional_field_validation_alias_model(
data: OptionalFieldValidationAliasModel = File(...), data: OptionalFieldValidationAliasModel = Form(),
): ):
if data.file is None: if data.file is None:
return {"file_size": None} return {"file_size": None}
@ -317,7 +342,6 @@ def test_optional_field_validation_alias_model_by_alias():
"/optional-field-validation-alias-model", files={"file_val_alias": b"content"} "/optional-field-validation-alias-model", files={"file_val_alias": b"content"}
) )
assert resp.status_code == 200, resp.text assert resp.status_code == 200, resp.text
# Fails with:
# AssertionError: assert 422 == 200 # AssertionError: assert 422 == 200
# {"detail":[{"type":"bytes_type","loc":["body","file_val_alias"],"msg":"Input should be a valid bytes","input":{"filename":"upload","file":{"_file":{},"_max_size":1048576,"_rolled":false,"_TemporaryFileArgs":{"mode":"w+b","buffering":-1,"suffix":null,"prefix":null,"encoding":null,"newline":null,"dir":null,"errors":null}},"size":7,"headers":{"content-disposition":"form-data; name=\"file_val_alias\"; filename=\"upload\"","content-type":"application/octet-stream"},"_max_mem_size":1048576}}]} # {"detail":[{"type":"bytes_type","loc":["body","file_val_alias"],"msg":"Input should be a valid bytes","input":{"filename":"upload","file":{"_file":{},"_max_size":1048576,"_rolled":false,"_TemporaryFileArgs":{"mode":"w+b","buffering":-1,"suffix":null,"prefix":null,"encoding":null,"newline":null,"dir":null,"errors":null}},"size":7,"headers":{"content-disposition":"form-data; name=\"file_val_alias\"; filename=\"upload\"","content-type":"application/octet-stream"},"_max_mem_size":1048576}}]}
@ -341,15 +365,15 @@ def test_optional_field_validation_alias_model_schema():
class ListFieldValidationAliasModel(BaseModel): class ListFieldValidationAliasModel(BaseModel):
files: List[bytes] = Field(validation_alias="files_val_alias") files: List[bytes] = File(validation_alias="files_val_alias")
@app.post( @app.post(
"/list-field-validation-alias-model", "/list-field-validation-alias-model",
operation_id="list_field_validation_alias_model", operation_id="list_field_validation_alias_model",
) )
async def list_field_validation_alias_model( # pragma: no cover (remove `no cover` when bug fixed) async def list_field_validation_alias_model(
data: ListFieldValidationAliasModel = File(...), data: ListFieldValidationAliasModel = Form(),
): ):
return {"file_sizes": [len(file) for file in data.files]} return {"file_sizes": [len(file) for file in data.files]}
@ -374,7 +398,6 @@ def test_list_field_validation_alias_model_by_alias():
files=[("files_val_alias", b"content1"), ("files_val_alias", b"content2")], files=[("files_val_alias", b"content1"), ("files_val_alias", b"content2")],
) )
assert resp.status_code == 200, resp.text assert resp.status_code == 200, resp.text
# Fails with:
# AssertionError: assert 422 == 200 # AssertionError: assert 422 == 200
# {"detail":[{"type":"bytes_type","loc":["body","files_val_alias",0],"msg":"Input should be a valid bytes","input":{"filename":"upload","file":{"_file":{},"_max_size":1048576,"_rolled":false,"_TemporaryFileArgs":{"mode":"w+b","buffering":-1,"suffix":null,"prefix":null,"encoding":null,"newline":null,"dir":null,"errors":null}},"size":8,"headers":{"content-disposition":"form-data; name=\"files_val_alias\"; filename=\"upload\"","content-type":"application/octet-stream"},"_max_mem_size":1048576}},{"type":"bytes_type","loc":["body","files_val_alias",1],"msg":"Input should be a valid bytes","input":{"filename":"upload","file":{"_file":{},"_max_size":1048576,"_rolled":false,"_TemporaryFileArgs":{"mode":"w+b","buffering":-1,"suffix":null,"prefix":null,"encoding":null,"newline":null,"dir":null,"errors":null}},"size":8,"headers":{"content-disposition":"form-data; name=\"files_val_alias\"; filename=\"upload\"","content-type":"application/octet-stream"},"_max_mem_size":1048576}}]} # {"detail":[{"type":"bytes_type","loc":["body","files_val_alias",0],"msg":"Input should be a valid bytes","input":{"filename":"upload","file":{"_file":{},"_max_size":1048576,"_rolled":false,"_TemporaryFileArgs":{"mode":"w+b","buffering":-1,"suffix":null,"prefix":null,"encoding":null,"newline":null,"dir":null,"errors":null}},"size":8,"headers":{"content-disposition":"form-data; name=\"files_val_alias\"; filename=\"upload\"","content-type":"application/octet-stream"},"_max_mem_size":1048576}},{"type":"bytes_type","loc":["body","files_val_alias",1],"msg":"Input should be a valid bytes","input":{"filename":"upload","file":{"_file":{},"_max_size":1048576,"_rolled":false,"_TemporaryFileArgs":{"mode":"w+b","buffering":-1,"suffix":null,"prefix":null,"encoding":null,"newline":null,"dir":null,"errors":null}},"size":8,"headers":{"content-disposition":"form-data; name=\"files_val_alias\"; filename=\"upload\"","content-type":"application/octet-stream"},"_max_mem_size":1048576}}]}
@ -399,15 +422,15 @@ def test_list_field_validation_alias_model_schema():
class OptionalListFieldValidationAliasModel(BaseModel): class OptionalListFieldValidationAliasModel(BaseModel):
files: Optional[List[bytes]] = Field(None, validation_alias="files_val_alias") files: Optional[List[bytes]] = File(None, validation_alias="files_val_alias")
@app.post( @app.post(
"/optional-list-field-validation-alias-model", "/optional-list-field-validation-alias-model",
operation_id="optional_list_field_validation_alias_model", operation_id="optional_list_field_validation_alias_model",
) )
async def optional_list_field_validation_alias_model( # pragma: no cover (remove `no cover` when bug fixed) async def optional_list_field_validation_alias_model(
data: OptionalListFieldValidationAliasModel = File(...), data: OptionalListFieldValidationAliasModel = Form(),
): ):
if data.files is None: if data.files is None:
return {"file_sizes": None} return {"file_sizes": None}
@ -432,7 +455,6 @@ def test_optional_list_field_validation_alias_model_by_alias():
files=[("files_val_alias", b"content1"), ("files_val_alias", b"content2")], files=[("files_val_alias", b"content1"), ("files_val_alias", b"content2")],
) )
assert resp.status_code == 200, resp.text assert resp.status_code == 200, resp.text
# Fails with:
# AssertionError: assert 422 == 200 # AssertionError: assert 422 == 200
# {"detail":[{"type":"bytes_type","loc":["body","files_val_alias",0],"msg":"Input should be a valid bytes","input":{"filename":"upload","file":{"_file":{},"_max_size":1048576,"_rolled":false,"_TemporaryFileArgs":{"mode":"w+b","buffering":-1,"suffix":null,"prefix":null,"encoding":null,"newline":null,"dir":null,"errors":null}},"size":8,"headers":{"content-disposition":"form-data; name=\"files_val_alias\"; filename=\"upload\"","content-type":"application/octet-stream"},"_max_mem_size":1048576}},{"type":"bytes_type","loc":["body","files_val_alias",1],"msg":"Input should be a valid bytes","input":{"filename":"upload","file":{"_file":{},"_max_size":1048576,"_rolled":false,"_TemporaryFileArgs":{"mode":"w+b","buffering":-1,"suffix":null,"prefix":null,"encoding":null,"newline":null,"dir":null,"errors":null}},"size":8,"headers":{"content-disposition":"form-data; name=\"files_val_alias\"; filename=\"upload\"","content-type":"application/octet-stream"},"_max_mem_size":1048576}}]} # {"detail":[{"type":"bytes_type","loc":["body","files_val_alias",0],"msg":"Input should be a valid bytes","input":{"filename":"upload","file":{"_file":{},"_max_size":1048576,"_rolled":false,"_TemporaryFileArgs":{"mode":"w+b","buffering":-1,"suffix":null,"prefix":null,"encoding":null,"newline":null,"dir":null,"errors":null}},"size":8,"headers":{"content-disposition":"form-data; name=\"files_val_alias\"; filename=\"upload\"","content-type":"application/octet-stream"},"_max_mem_size":1048576}},{"type":"bytes_type","loc":["body","files_val_alias",1],"msg":"Input should be a valid bytes","input":{"filename":"upload","file":{"_file":{},"_max_size":1048576,"_rolled":false,"_TemporaryFileArgs":{"mode":"w+b","buffering":-1,"suffix":null,"prefix":null,"encoding":null,"newline":null,"dir":null,"errors":null}},"size":8,"headers":{"content-disposition":"form-data; name=\"files_val_alias\"; filename=\"upload\"","content-type":"application/octet-stream"},"_max_mem_size":1048576}}]}
@ -458,22 +480,22 @@ def test_optional_list_field_validation_alias_model_schema():
# ===================================================================================== # =====================================================================================
# Field(alias=..., validation_alias=...) # Field(alias=..., validation_alias=...)
# Current situation: doesn't work due to validation error # Current situation: validation doesn't work, but schema generation works
# ------------------------------ # ------------------------------
# required field # required field
class RequiredFieldAliasAndValidationAliasModel(BaseModel): class RequiredFieldAliasAndValidationAliasModel(BaseModel):
file: bytes = Field(alias="file_alias", validation_alias="file_val_alias") file: bytes = File(alias="file_alias", validation_alias="file_val_alias")
@app.post( @app.post(
"/required-field-alias-and-validation-alias-model", "/required-field-alias-and-validation-alias-model",
operation_id="required_field_alias_and_validation_alias_model", operation_id="required_field_alias_and_validation_alias_model",
) )
async def required_field_alias_and_validation_alias_model( # pragma: no cover (remove `no cover` when bug fixed) async def required_field_alias_and_validation_alias_model(
data: RequiredFieldAliasAndValidationAliasModel = File(...), data: RequiredFieldAliasAndValidationAliasModel = Form(),
): ):
return {"file_size": len(data.file)} return {"file_size": len(data.file)}
@ -509,7 +531,6 @@ def test_required_field_alias_and_validation_alias_model_by_validation_alias():
files={"file_val_alias": b"content"}, files={"file_val_alias": b"content"},
) )
assert resp.status_code == 200, resp.text assert resp.status_code == 200, resp.text
# Fails with:
# AssertionError: assert 422 == 200 # AssertionError: assert 422 == 200
# {"detail":[{"type":"bytes_type","loc":["body","file_val_alias"],"msg":"Input should be a valid bytes","input":{"filename":"upload","file":{"_file":{},"_max_size":1048576,"_rolled":false,"_TemporaryFileArgs":{"mode":"w+b","buffering":-1,"suffix":null,"prefix":null,"encoding":null,"newline":null,"dir":null,"errors":null}},"size":7,"headers":{"content-disposition":"form-data; name=\"file_val_alias\"; filename=\"upload\"","content-type":"application/octet-stream"},"_max_mem_size":1048576}}]} # {"detail":[{"type":"bytes_type","loc":["body","file_val_alias"],"msg":"Input should be a valid bytes","input":{"filename":"upload","file":{"_file":{},"_max_size":1048576,"_rolled":false,"_TemporaryFileArgs":{"mode":"w+b","buffering":-1,"suffix":null,"prefix":null,"encoding":null,"newline":null,"dir":null,"errors":null}},"size":7,"headers":{"content-disposition":"form-data; name=\"file_val_alias\"; filename=\"upload\"","content-type":"application/octet-stream"},"_max_mem_size":1048576}}]}
@ -536,7 +557,7 @@ def test_required_field_alias_and_validation_alias_model_schema():
class OptionalFieldAliasAndValidationAliasModel(BaseModel): class OptionalFieldAliasAndValidationAliasModel(BaseModel):
file: Optional[bytes] = Field( file: Optional[bytes] = File(
None, alias="file_alias", validation_alias="file_val_alias" None, alias="file_alias", validation_alias="file_val_alias"
) )
@ -545,8 +566,8 @@ class OptionalFieldAliasAndValidationAliasModel(BaseModel):
"/optional-field-alias-and-validation-alias-model", "/optional-field-alias-and-validation-alias-model",
operation_id="optional_field_alias_and_validation_alias_model", operation_id="optional_field_alias_and_validation_alias_model",
) )
async def optional_field_alias_and_validation_alias_model( # pragma: no cover (remove `no cover` when bug fixed) async def optional_field_alias_and_validation_alias_model(
data: OptionalFieldAliasAndValidationAliasModel = File(...), data: OptionalFieldAliasAndValidationAliasModel = Form(),
): ):
if data.file is None: if data.file is None:
return {"file_size": None} return {"file_size": None}
@ -580,7 +601,6 @@ def test_optional_field_alias_and_validation_alias_model_by_validation_alias():
files={"file_val_alias": b"content"}, files={"file_val_alias": b"content"},
) )
assert resp.status_code == 200, resp.text assert resp.status_code == 200, resp.text
# Fails with:
# AssertionError: assert 422 == 200 # AssertionError: assert 422 == 200
# {"detail":[{"type":"bytes_type","loc":["body","file_val_alias"],"msg":"Input should be a valid bytes","input":{"filename":"upload","file":{"_file":{},"_max_size":1048576,"_rolled":false,"_TemporaryFileArgs":{"mode":"w+b","buffering":-1,"suffix":null,"prefix":null,"encoding":null,"newline":null,"dir":null,"errors":null}},"size":7,"headers":{"content-disposition":"form-data; name=\"file_val_alias\"; filename=\"upload\"","content-type":"application/octet-stream"},"_max_mem_size":1048576}}]} # {"detail":[{"type":"bytes_type","loc":["body","file_val_alias"],"msg":"Input should be a valid bytes","input":{"filename":"upload","file":{"_file":{},"_max_size":1048576,"_rolled":false,"_TemporaryFileArgs":{"mode":"w+b","buffering":-1,"suffix":null,"prefix":null,"encoding":null,"newline":null,"dir":null,"errors":null}},"size":7,"headers":{"content-disposition":"form-data; name=\"file_val_alias\"; filename=\"upload\"","content-type":"application/octet-stream"},"_max_mem_size":1048576}}]}
@ -606,15 +626,15 @@ def test_optional_field_alias_and_validation_alias_model_schema():
class ListFieldAliasAndValidationAliasModel(BaseModel): class ListFieldAliasAndValidationAliasModel(BaseModel):
files: List[bytes] = Field(alias="files_alias", validation_alias="files_val_alias") files: List[bytes] = File(alias="files_alias", validation_alias="files_val_alias")
@app.post( @app.post(
"/list-field-alias-and-validation-alias-model", "/list-field-alias-and-validation-alias-model",
operation_id="list_field_alias_and_validation_alias_model", operation_id="list_field_alias_and_validation_alias_model",
) )
async def list_field_alias_and_validation_alias_model( # pragma: no cover (remove `no cover` when bug fixed) async def list_field_alias_and_validation_alias_model(
data: ListFieldAliasAndValidationAliasModel = File(...), data: ListFieldAliasAndValidationAliasModel = Form(),
): ):
return {"file_sizes": [len(file) for file in data.files]} return {"file_sizes": [len(file) for file in data.files]}
@ -651,7 +671,6 @@ def test_list_field_alias_and_validation_alias_model_by_validation_alias():
files=[("files_val_alias", b"content1"), ("files_val_alias", b"content2")], files=[("files_val_alias", b"content1"), ("files_val_alias", b"content2")],
) )
assert resp.status_code == 200, resp.text assert resp.status_code == 200, resp.text
# Fails with:
# AssertionError: assert 422 == 200 # AssertionError: assert 422 == 200
# {"detail":[{"type":"bytes_type","loc":["body","files_val_alias",0],"msg":"Input should be a valid bytes","input":{"filename":"upload","file":{"_file":{},"_max_size":1048576,"_rolled":false,"_TemporaryFileArgs":{"mode":"w+b","buffering":-1,"suffix":null,"prefix":null,"encoding":null,"newline":null,"dir":null,"errors":null}},"size":8,"headers":{"content-disposition":"form-data; name=\"files_val_alias\"; filename=\"upload\"","content-type":"application/octet-stream"},"_max_mem_size":1048576}},{"type":"bytes_type","loc":["body","files_val_alias",1],"msg":"Input should be a valid bytes","input":{"filename":"upload","file":{"_file":{},"_max_size":1048576,"_rolled":false,"_TemporaryFileArgs":{"mode":"w+b","buffering":-1,"suffix":null,"prefix":null,"encoding":null,"newline":null,"dir":null,"errors":null}},"size":8,"headers":{"content-disposition":"form-data; name=\"files_val_alias\"; filename=\"upload\"","content-type":"application/octet-stream"},"_max_mem_size":1048576}}]} # {"detail":[{"type":"bytes_type","loc":["body","files_val_alias",0],"msg":"Input should be a valid bytes","input":{"filename":"upload","file":{"_file":{},"_max_size":1048576,"_rolled":false,"_TemporaryFileArgs":{"mode":"w+b","buffering":-1,"suffix":null,"prefix":null,"encoding":null,"newline":null,"dir":null,"errors":null}},"size":8,"headers":{"content-disposition":"form-data; name=\"files_val_alias\"; filename=\"upload\"","content-type":"application/octet-stream"},"_max_mem_size":1048576}},{"type":"bytes_type","loc":["body","files_val_alias",1],"msg":"Input should be a valid bytes","input":{"filename":"upload","file":{"_file":{},"_max_size":1048576,"_rolled":false,"_TemporaryFileArgs":{"mode":"w+b","buffering":-1,"suffix":null,"prefix":null,"encoding":null,"newline":null,"dir":null,"errors":null}},"size":8,"headers":{"content-disposition":"form-data; name=\"files_val_alias\"; filename=\"upload\"","content-type":"application/octet-stream"},"_max_mem_size":1048576}}]}
@ -678,7 +697,7 @@ def test_list_field_alias_and_validation_alias_model_schema():
class OptionalListFieldAliasAndValidationAliasModel(BaseModel): class OptionalListFieldAliasAndValidationAliasModel(BaseModel):
files: Optional[List[bytes]] = Field( files: Optional[List[bytes]] = File(
None, alias="files_alias", validation_alias="files_val_alias" None, alias="files_alias", validation_alias="files_val_alias"
) )
@ -687,8 +706,8 @@ class OptionalListFieldAliasAndValidationAliasModel(BaseModel):
"/optional-list-field-alias-and-validation-alias-model", "/optional-list-field-alias-and-validation-alias-model",
operation_id="optional_list_field_alias_and_validation_alias_model", operation_id="optional_list_field_alias_and_validation_alias_model",
) )
async def optional_list_field_alias_and_validation_alias_model( # pragma: no cover (remove `no cover` when bug fixed) async def optional_list_field_alias_and_validation_alias_model(
data: OptionalListFieldAliasAndValidationAliasModel = File(...), data: OptionalListFieldAliasAndValidationAliasModel = Form(),
): ):
if data.files is None: if data.files is None:
return {"file_sizes": None} return {"file_sizes": None}
@ -723,7 +742,6 @@ def test_optional_list_field_alias_and_validation_alias_model_by_validation_alia
files=[("files_val_alias", b"content1"), ("files_val_alias", b"content2")], files=[("files_val_alias", b"content1"), ("files_val_alias", b"content2")],
) )
assert resp.status_code == 200, resp.text assert resp.status_code == 200, resp.text
# Fails with:
# AssertionError: assert 422 == 200 # AssertionError: assert 422 == 200
# {"detail":[{"type":"bytes_type","loc":["body","files_val_alias",0],"msg":"Input should be a valid bytes","input":{"filename":"upload","file":{"_file":{},"_max_size":1048576,"_rolled":false,"_TemporaryFileArgs":{"mode":"w+b","buffering":-1,"suffix":null,"prefix":null,"encoding":null,"newline":null,"dir":null,"errors":null}},"size":8,"headers":{"content-disposition":"form-data; name=\"files_val_alias\"; filename=\"upload\"","content-type":"application/octet-stream"},"_max_mem_size":1048576}},{"type":"bytes_type","loc":["body","files_val_alias",1],"msg":"Input should be a valid bytes","input":{"filename":"upload","file":{"_file":{},"_max_size":1048576,"_rolled":false,"_TemporaryFileArgs":{"mode":"w+b","buffering":-1,"suffix":null,"prefix":null,"encoding":null,"newline":null,"dir":null,"errors":null}},"size":8,"headers":{"content-disposition":"form-data; name=\"files_val_alias\"; filename=\"upload\"","content-type":"application/octet-stream"},"_max_mem_size":1048576}}]} # {"detail":[{"type":"bytes_type","loc":["body","files_val_alias",0],"msg":"Input should be a valid bytes","input":{"filename":"upload","file":{"_file":{},"_max_size":1048576,"_rolled":false,"_TemporaryFileArgs":{"mode":"w+b","buffering":-1,"suffix":null,"prefix":null,"encoding":null,"newline":null,"dir":null,"errors":null}},"size":8,"headers":{"content-disposition":"form-data; name=\"files_val_alias\"; filename=\"upload\"","content-type":"application/octet-stream"},"_max_mem_size":1048576}},{"type":"bytes_type","loc":["body","files_val_alias",1],"msg":"Input should be a valid bytes","input":{"filename":"upload","file":{"_file":{},"_max_size":1048576,"_rolled":false,"_TemporaryFileArgs":{"mode":"w+b","buffering":-1,"suffix":null,"prefix":null,"encoding":null,"newline":null,"dir":null,"errors":null}},"size":8,"headers":{"content-disposition":"form-data; name=\"files_val_alias\"; filename=\"upload\"","content-type":"application/octet-stream"},"_max_mem_size":1048576}}]}

102
tests/test_aliases/test_uploadfile_model_aliases.py

@ -1,8 +1,9 @@
from typing import List, Optional from typing import List, Optional
from fastapi import FastAPI, File, UploadFile import pytest
from fastapi import FastAPI, File, Form, UploadFile
from fastapi.testclient import TestClient from fastapi.testclient import TestClient
from pydantic import BaseModel, Field from pydantic import BaseModel
from ..utils import needs_pydanticv2 from ..utils import needs_pydanticv2
@ -12,43 +13,56 @@ app = FastAPI()
# ===================================================================================== # =====================================================================================
# Field(alias=...) # Field(alias=...)
# Current situation: Works # Current situation: doesn't work (neither validation nor schema generation)
# ------------------------------ # ------------------------------
# required field # required field
class RequiredFieldAliasModel(BaseModel): class RequiredFieldAliasModel(BaseModel):
file: UploadFile = Field(alias="file_alias") file: UploadFile = File(alias="file_alias")
@app.post("/required-field-alias-model") @app.post("/required-field-alias-model")
async def required_field_alias_model(data: RequiredFieldAliasModel = File(...)): async def required_field_alias_model(data: RequiredFieldAliasModel = Form()):
return {"file_size": data.file.size} return {"file_size": data.file.size}
@pytest.mark.xfail(raises=AssertionError, strict=False)
def test_required_field_alias_model_by_name(): def test_required_field_alias_model_by_name():
client = TestClient(app) client = TestClient(app)
resp = client.post("/required-field-alias-model", files={"file": b"content"}) resp = client.post("/required-field-alias-model", files={"file": b"content"})
assert resp.status_code == 422 assert resp.status_code == 422
detail = resp.json()["detail"] # assert 200 == 422
assert detail[0]["msg"] == "Field required"
assert "file_alias" in detail[0]["loc"] # Uncomment when the assertion above passes:
# detail = resp.json()["detail"]
# assert detail[0]["msg"] == "Field required"
# assert "file_alias" in detail[0]["loc"]
@pytest.mark.xfail(raises=AssertionError, strict=False)
def test_required_field_alias_model_by_alias(): def test_required_field_alias_model_by_alias():
client = TestClient(app) client = TestClient(app)
resp = client.post("/required-field-alias-model", files={"file_alias": b"content"}) resp = client.post("/required-field-alias-model", files={"file_alias": b"content"})
assert resp.status_code == 200, resp.text assert resp.status_code == 200, resp.text
assert resp.json() == {"file_size": 7} # AssertionError: assert 422 == 200
# {"detail":[{"type":"missing","loc":["body","file"],"msg":"Field required","input":{"file_alias":{"filename":"upload","file":{"_file":{},"_max_size":1048576,"_rolled":false,"_TemporaryFileArgs":{"mode":"w+b","buffering":-1,"suffix":null,"prefix":null,"encoding":null,"newline":null,"dir":null,"errors":null}},"size":7,"headers":{"content-disposition":"form-data; name=\"file_alias\"; filename=\"upload\"","content-type":"application/octet-stream"},"_max_mem_size":1048576}}}]}
# Uncomment when the assertion above passes:
# assert resp.json() == {"file_size": 7}
@pytest.mark.xfail(raises=AssertionError, strict=False)
def test_required_field_alias_model_schema(): def test_required_field_alias_model_schema():
openapi = app.openapi() openapi = app.openapi()
body_schema = openapi["components"]["schemas"]["RequiredFieldAliasModel"] body_schema = openapi["components"]["schemas"]["RequiredFieldAliasModel"]
assert body_schema["properties"] == { assert body_schema["properties"] == {
"file_alias": {"title": "File Alias", "type": "string", "format": "binary"} "file_alias": {"title": "File Alias", "type": "string", "format": "binary"}
} }
# AssertionError: assert
# {'file': {'type': 'string', 'format': 'binary', 'title': 'File'}} ==
# {'file_alias': {'title': 'File Alias', 'type': 'string', 'format': 'binary'}}
# ------------------------------ # ------------------------------
@ -56,30 +70,35 @@ def test_required_field_alias_model_schema():
class OptionalFieldAliasModel(BaseModel): class OptionalFieldAliasModel(BaseModel):
file: Optional[UploadFile] = Field(None, alias="file_alias") file: Optional[UploadFile] = File(None, alias="file_alias")
@app.post("/optional-field-alias-model") @app.post("/optional-field-alias-model")
async def optional_field_alias_model(data: OptionalFieldAliasModel = File(...)): async def optional_field_alias_model(data: OptionalFieldAliasModel = Form()):
if data.file is None: if data.file is None:
return {"file_size": None} return {"file_size": None}
return {"file_size": data.file.size} return {"file_size": data.file.size}
@pytest.mark.xfail(raises=AssertionError, strict=False)
def test_optional_field_alias_model_by_name(): def test_optional_field_alias_model_by_name():
client = TestClient(app) client = TestClient(app)
resp = client.post("/optional-field-alias-model", files={"file": b"content"}) resp = client.post("/optional-field-alias-model", files={"file": b"content"})
assert resp.status_code == 200 assert resp.status_code == 200
assert resp.json() == {"file_size": None} assert resp.json() == {"file_size": None}
# AssertionError: assert {'file_size': 7} == {'file_size': None}
@pytest.mark.xfail(raises=AssertionError, strict=False)
def test_optional_field_alias_model_by_alias(): def test_optional_field_alias_model_by_alias():
client = TestClient(app) client = TestClient(app)
resp = client.post("/optional-field-alias-model", files={"file_alias": b"content"}) resp = client.post("/optional-field-alias-model", files={"file_alias": b"content"})
assert resp.status_code == 200, resp.text assert resp.status_code == 200, resp.text
assert resp.json() == {"file_size": 7} assert resp.json() == {"file_size": 7}
# AssertionError: assert {'file_size': None} == {'file_size': 7}
@pytest.mark.xfail(raises=AssertionError, strict=False)
def test_optional_field_alias_model_schema(): def test_optional_field_alias_model_schema():
openapi = app.openapi() openapi = app.openapi()
body_schema = openapi["components"]["schemas"]["OptionalFieldAliasModel"] body_schema = openapi["components"]["schemas"]["OptionalFieldAliasModel"]
@ -89,6 +108,9 @@ def test_optional_field_alias_model_schema():
"title": "File Alias", "title": "File Alias",
}, },
} }
# AssertionError: assert
# {'file': {'anyOf': [{'type': 'string', 'format': 'binary'}, {'type': 'null'}], 'title': 'File'}} ==
# {'file_alias': {'anyOf': [{'type': 'string', 'format': 'binary'}, {'type': 'null'}], 'title': 'File Alias'}}
# ------------------------------ # ------------------------------
@ -96,14 +118,15 @@ def test_optional_field_alias_model_schema():
class ListFieldAliasModel(BaseModel): class ListFieldAliasModel(BaseModel):
files: List[UploadFile] = Field(alias="files_alias") files: List[UploadFile] = File(alias="files_alias")
@app.post("/list-field-alias-model") @app.post("/list-field-alias-model")
async def list_field_alias_model(data: ListFieldAliasModel = File(...)): async def list_field_alias_model(data: ListFieldAliasModel = Form()):
return {"file_sizes": [file.size for file in data.files]} return {"file_sizes": [file.size for file in data.files]}
@pytest.mark.xfail(raises=AssertionError, strict=False)
def test_list_field_alias_model_by_name(): def test_list_field_alias_model_by_name():
client = TestClient(app) client = TestClient(app)
resp = client.post( resp = client.post(
@ -111,11 +134,14 @@ def test_list_field_alias_model_by_name():
files=[("files", b"content1"), ("files", b"content2")], files=[("files", b"content1"), ("files", b"content2")],
) )
assert resp.status_code == 422 assert resp.status_code == 422
detail = resp.json()["detail"]
assert detail[0]["msg"] == "Field required" # Uncomment when the assertion above passes:
assert "files_alias" in detail[0]["loc"] # detail = resp.json()["detail"]
# assert detail[0]["msg"] == "Field required"
# assert "files_alias" in detail[0]["loc"]
@pytest.mark.xfail(raises=AssertionError, strict=False)
def test_list_field_alias_model_by_alias(): def test_list_field_alias_model_by_alias():
client = TestClient(app) client = TestClient(app)
resp = client.post( resp = client.post(
@ -123,9 +149,12 @@ def test_list_field_alias_model_by_alias():
files=[("files_alias", b"content1"), ("files_alias", b"content2")], files=[("files_alias", b"content1"), ("files_alias", b"content2")],
) )
assert resp.status_code == 200, resp.text assert resp.status_code == 200, resp.text
assert resp.json() == {"file_sizes": [8, 8]}
# Uncomment when the assertion above passes:
# assert resp.json() == {"file_sizes": [8, 8]}
@pytest.mark.xfail(raises=AssertionError, strict=False)
def test_list_field_alias_model_schema(): def test_list_field_alias_model_schema():
openapi = app.openapi() openapi = app.openapi()
body_schema = openapi["components"]["schemas"]["ListFieldAliasModel"] body_schema = openapi["components"]["schemas"]["ListFieldAliasModel"]
@ -143,16 +172,17 @@ def test_list_field_alias_model_schema():
class OptionalListFieldAliasModel(BaseModel): class OptionalListFieldAliasModel(BaseModel):
files: Optional[List[UploadFile]] = Field(None, alias="files_alias") files: Optional[List[UploadFile]] = File(None, alias="files_alias")
@app.post("/optional-list-field-alias-model") @app.post("/optional-list-field-alias-model")
async def optional_list_field_alias_model(data: OptionalListFieldAliasModel = File()): async def optional_list_field_alias_model(data: OptionalListFieldAliasModel = Form()):
if data.files is None: if data.files is None:
return {"file_sizes": None} return {"file_sizes": None}
return {"file_sizes": [file.size for file in data.files]} return {"file_sizes": [file.size for file in data.files]}
@pytest.mark.xfail(raises=AssertionError, strict=False)
def test_optional_list_field_alias_model_by_name(): def test_optional_list_field_alias_model_by_name():
client = TestClient(app) client = TestClient(app)
resp = client.post( resp = client.post(
@ -163,6 +193,7 @@ def test_optional_list_field_alias_model_by_name():
assert resp.json() == {"file_sizes": None} assert resp.json() == {"file_sizes": None}
@pytest.mark.xfail(raises=AssertionError, strict=False)
def test_optional_list_field_alias_model_by_alias(): def test_optional_list_field_alias_model_by_alias():
client = TestClient(app) client = TestClient(app)
resp = client.post( resp = client.post(
@ -173,6 +204,7 @@ def test_optional_list_field_alias_model_by_alias():
assert resp.json() == {"file_sizes": [8, 8]} assert resp.json() == {"file_sizes": [8, 8]}
@pytest.mark.xfail(raises=AssertionError, strict=False)
def test_optional_list_field_alias_model_schema(): def test_optional_list_field_alias_model_schema():
openapi = app.openapi() openapi = app.openapi()
body_schema = openapi["components"]["schemas"]["OptionalListFieldAliasModel"] body_schema = openapi["components"]["schemas"]["OptionalListFieldAliasModel"]
@ -197,7 +229,7 @@ def test_optional_list_field_alias_model_schema():
class RequiredFieldValidationAliasModel(BaseModel): class RequiredFieldValidationAliasModel(BaseModel):
file: UploadFile = Field(validation_alias="file_val_alias") file: UploadFile = File(validation_alias="file_val_alias")
@app.post( @app.post(
@ -205,7 +237,7 @@ class RequiredFieldValidationAliasModel(BaseModel):
operation_id="required_field_validation_alias_model", operation_id="required_field_validation_alias_model",
) )
async def required_field_validation_alias_model( async def required_field_validation_alias_model(
data: RequiredFieldValidationAliasModel = File(...), data: RequiredFieldValidationAliasModel = Form(),
): ):
return {"file_size": data.file.size} return {"file_size": data.file.size}
@ -247,7 +279,7 @@ def test_required_field_validation_alias_model_schema():
class OptionalFieldValidationAliasModel(BaseModel): class OptionalFieldValidationAliasModel(BaseModel):
file: Optional[UploadFile] = Field(None, validation_alias="file_val_alias") file: Optional[UploadFile] = File(None, validation_alias="file_val_alias")
@app.post( @app.post(
@ -255,7 +287,7 @@ class OptionalFieldValidationAliasModel(BaseModel):
operation_id="optional_field_validation_alias_model", operation_id="optional_field_validation_alias_model",
) )
async def optional_field_validation_alias_model( async def optional_field_validation_alias_model(
data: OptionalFieldValidationAliasModel = File(...), data: OptionalFieldValidationAliasModel = Form(),
): ):
if data.file is None: if data.file is None:
return {"file_size": None} return {"file_size": None}
@ -296,7 +328,7 @@ def test_optional_field_validation_alias_model_schema():
class ListFieldValidationAliasModel(BaseModel): class ListFieldValidationAliasModel(BaseModel):
files: List[UploadFile] = Field(validation_alias="files_val_alias") files: List[UploadFile] = File(validation_alias="files_val_alias")
@app.post( @app.post(
@ -304,7 +336,7 @@ class ListFieldValidationAliasModel(BaseModel):
operation_id="list_field_validation_alias_model", operation_id="list_field_validation_alias_model",
) )
async def list_field_validation_alias_model( async def list_field_validation_alias_model(
data: ListFieldValidationAliasModel = File(...), data: ListFieldValidationAliasModel = Form(),
): ):
return {"file_sizes": [file.size for file in data.files]} return {"file_sizes": [file.size for file in data.files]}
@ -350,7 +382,7 @@ def test_list_field_validation_alias_model_schema():
class OptionalListFieldValidationAliasModel(BaseModel): class OptionalListFieldValidationAliasModel(BaseModel):
files: Optional[List[UploadFile]] = Field(None, validation_alias="files_val_alias") files: Optional[List[UploadFile]] = File(None, validation_alias="files_val_alias")
@app.post( @app.post(
@ -358,7 +390,7 @@ class OptionalListFieldValidationAliasModel(BaseModel):
operation_id="optional_list_field_validation_alias_model", operation_id="optional_list_field_validation_alias_model",
) )
async def optional_list_field_validation_alias_model( async def optional_list_field_validation_alias_model(
data: OptionalListFieldValidationAliasModel = File(...), data: OptionalListFieldValidationAliasModel = Form(),
): ):
if data.files is None: if data.files is None:
return {"file_sizes": None} return {"file_sizes": None}
@ -414,7 +446,7 @@ def test_optional_list_field_validation_alias_model_schema():
class RequiredFieldAliasAndValidationAliasModel(BaseModel): class RequiredFieldAliasAndValidationAliasModel(BaseModel):
file: UploadFile = Field(alias="file_alias", validation_alias="file_val_alias") file: UploadFile = File(alias="file_alias", validation_alias="file_val_alias")
@app.post( @app.post(
@ -422,7 +454,7 @@ class RequiredFieldAliasAndValidationAliasModel(BaseModel):
operation_id="required_field_alias_and_validation_alias_model", operation_id="required_field_alias_and_validation_alias_model",
) )
async def required_field_alias_and_validation_alias_model( async def required_field_alias_and_validation_alias_model(
data: RequiredFieldAliasAndValidationAliasModel = File(...), data: RequiredFieldAliasAndValidationAliasModel = Form(),
): ):
return {"file_size": data.file.size} return {"file_size": data.file.size}
@ -479,7 +511,7 @@ def test_required_field_alias_and_validation_alias_model_schema():
class OptionalFieldAliasAndValidationAliasModel(BaseModel): class OptionalFieldAliasAndValidationAliasModel(BaseModel):
file: Optional[UploadFile] = Field( file: Optional[UploadFile] = File(
None, alias="file_alias", validation_alias="file_val_alias" None, alias="file_alias", validation_alias="file_val_alias"
) )
@ -489,7 +521,7 @@ class OptionalFieldAliasAndValidationAliasModel(BaseModel):
operation_id="optional_field_alias_and_validation_alias_model", operation_id="optional_field_alias_and_validation_alias_model",
) )
async def optional_field_alias_and_validation_alias_model( async def optional_field_alias_and_validation_alias_model(
data: OptionalFieldAliasAndValidationAliasModel = File(...), data: OptionalFieldAliasAndValidationAliasModel = Form(),
): ):
if data.file is None: if data.file is None:
return {"file_size": None} return {"file_size": None}
@ -543,7 +575,7 @@ def test_optional_field_alias_and_validation_alias_model_schema():
class ListFieldAliasAndValidationAliasModel(BaseModel): class ListFieldAliasAndValidationAliasModel(BaseModel):
files: List[UploadFile] = Field( files: List[UploadFile] = File(
alias="files_alias", validation_alias="files_val_alias" alias="files_alias", validation_alias="files_val_alias"
) )
@ -553,7 +585,7 @@ class ListFieldAliasAndValidationAliasModel(BaseModel):
operation_id="list_field_alias_and_validation_alias_model", operation_id="list_field_alias_and_validation_alias_model",
) )
async def list_field_alias_and_validation_alias_model( async def list_field_alias_and_validation_alias_model(
data: ListFieldAliasAndValidationAliasModel = File(...), data: ListFieldAliasAndValidationAliasModel = Form(),
): ):
return {"file_sizes": [file.size for file in data.files]} return {"file_sizes": [file.size for file in data.files]}
@ -613,7 +645,7 @@ def test_list_field_alias_and_validation_alias_model_schema():
class OptionalListFieldAliasAndValidationAliasModel(BaseModel): class OptionalListFieldAliasAndValidationAliasModel(BaseModel):
files: Optional[List[UploadFile]] = Field( files: Optional[List[UploadFile]] = File(
None, alias="files_alias", validation_alias="files_val_alias" None, alias="files_alias", validation_alias="files_val_alias"
) )
@ -623,7 +655,7 @@ class OptionalListFieldAliasAndValidationAliasModel(BaseModel):
operation_id="optional_list_field_alias_and_validation_alias_model", operation_id="optional_list_field_alias_and_validation_alias_model",
) )
async def optional_list_field_alias_and_validation_alias_model( async def optional_list_field_alias_and_validation_alias_model(
data: OptionalListFieldAliasAndValidationAliasModel = File(...), data: OptionalListFieldAliasAndValidationAliasModel = Form(),
): ):
if data.files is None: if data.files is None:
return {"file_sizes": None} return {"file_sizes": None}

Loading…
Cancel
Save