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
import pytest
from fastapi import FastAPI, File
from fastapi import FastAPI, File, Form
from fastapi.testclient import TestClient
from pydantic import BaseModel, Field
from pydantic import BaseModel
from ..utils import needs_pydanticv2
@ -13,30 +13,33 @@ app = FastAPI()
# =====================================================================================
# Field(alias=...)
# Current situation: doesn't work due to validation error
# Current situation: doesn't work (neither validation nor schema generation)
# ------------------------------
# required field
class RequiredFieldAliasModel(BaseModel):
file: bytes = Field(alias="file_alias")
file: bytes = File(alias="file_alias")
@app.post("/required-field-alias-model")
async def required_field_alias_model( # pragma: no cover (remove `no cover` when bug fixed)
data: RequiredFieldAliasModel = File(...),
):
async def required_field_alias_model(data: RequiredFieldAliasModel = Form()):
return {"file_size": len(data.file)}
@pytest.mark.xfail(raises=AssertionError, strict=False)
def test_required_field_alias_model_by_name():
client = TestClient(app)
resp = client.post("/required-field-alias-model", files={"file": b"content"})
assert resp.status_code == 422
detail = resp.json()["detail"]
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)
@ -44,20 +47,25 @@ def test_required_field_alias_model_by_alias():
client = TestClient(app)
resp = client.post("/required-field-alias-model", files={"file_alias": b"content"})
assert resp.status_code == 200, resp.text
# Fails with:
# Currently fails due to issue with aliases:
# 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:
# assert resp.json() == {"file_size": 7}
@pytest.mark.xfail(raises=AssertionError, strict=False)
def test_required_field_alias_model_schema():
openapi = app.openapi()
body_schema = openapi["components"]["schemas"]["RequiredFieldAliasModel"]
assert body_schema["properties"] == {
"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):
file: Optional[bytes] = Field(None, alias="file_alias")
file: Optional[bytes] = File(None, alias="file_alias")
@app.post("/optional-field-alias-model")
async def optional_field_alias_model( # pragma: no cover (remove `no cover` when bug fixed)
data: OptionalFieldAliasModel = File(...),
):
async def optional_field_alias_model(data: OptionalFieldAliasModel = Form()):
if data.file is None:
return {"file_size": None}
return {"file_size": len(data.file)}
@pytest.mark.xfail(raises=AssertionError, strict=False)
def test_optional_field_alias_model_by_name():
client = TestClient(app)
resp = client.post("/optional-field-alias-model", files={"file": b"content"})
assert resp.status_code == 200
assert resp.json() == {"file_size": None}
assert resp.status_code == 200, resp.text
# 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)
@ -89,14 +101,12 @@ def test_optional_field_alias_model_by_alias():
client = TestClient(app)
resp = client.post("/optional-field-alias-model", files={"file_alias": b"content"})
assert resp.status_code == 200, resp.text
# Fails with:
# 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}}]}
# Uncomment when the assertion above passes:
# assert resp.json() == {"file_size": 7}
assert resp.json() == {"file_size": 7}
# Currently fails due to issue with aliases:
# AssertionError: assert {'file_size': None} == {'file_size': 7}
@pytest.mark.xfail(raises=AssertionError, strict=False)
def test_optional_field_alias_model_schema():
openapi = app.openapi()
body_schema = openapi["components"]["schemas"]["OptionalFieldAliasModel"]
@ -106,6 +116,10 @@ def test_optional_field_alias_model_schema():
"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):
files: List[bytes] = Field(alias="files_alias")
files: List[bytes] = File(alias="files_alias")
@app.post("/list-field-alias-model")
async def list_field_alias_model( # pragma: no cover (remove `no cover` when bug fixed)
data: ListFieldAliasModel = File(...),
):
async def list_field_alias_model(data: ListFieldAliasModel = Form()):
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():
client = TestClient(app)
resp = client.post(
@ -132,7 +145,11 @@ def test_list_field_alias_model_by_name():
assert resp.status_code == 422
detail = resp.json()["detail"]
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)
@ -143,14 +160,15 @@ def test_list_field_alias_model_by_alias():
files=[("files_alias", b"content1"), ("files_alias", b"content2")],
)
assert resp.status_code == 200, resp.text
# Fails with:
# Currently fails due to issue with aliases:
# 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:
# assert resp.json() == {"file_sizes": [8, 8]}
@pytest.mark.xfail(raises=AssertionError, strict=False)
def test_list_field_alias_model_schema():
openapi = app.openapi()
body_schema = openapi["components"]["schemas"]["ListFieldAliasModel"]
@ -161,6 +179,10 @@ def test_list_field_alias_model_schema():
"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):
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")
async def optional_list_field_alias_model( # pragma: no cover (remove `no cover` when bug fixed)
data: OptionalListFieldAliasModel = File(),
):
async def optional_list_field_alias_model(data: OptionalListFieldAliasModel = Form()):
if data.files is None:
return {"file_sizes": None}
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():
client = TestClient(app)
resp = client.post(
"/optional-list-field-alias-model",
files=[("files", b"content1"), ("files", b"content2")],
)
assert resp.status_code == 200
assert resp.json() == {"file_sizes": None}
assert resp.status_code == 200, resp.text
# 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)
@ -198,14 +222,12 @@ def test_optional_list_field_alias_model_by_alias():
files=[("files_alias", b"content1"), ("files_alias", b"content2")],
)
assert resp.status_code == 200, resp.text
# Fails with:
# 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}}]}
# Uncomment when the assertion above passes:
# assert resp.json() == {"file_sizes": [8, 8]}
assert resp.json() == {"file_sizes": [8, 8]}
# Currently fails due to issue with aliases
# AssertionError: assert {'file_sizes': None} == {'file_sizes': [8, 8]}
@pytest.mark.xfail(raises=AssertionError, strict=False)
def test_optional_list_field_alias_model_schema():
openapi = app.openapi()
body_schema = openapi["components"]["schemas"]["OptionalListFieldAliasModel"]
@ -218,11 +240,15 @@ def test_optional_list_field_alias_model_schema():
"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=...)
# 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):
file: bytes = Field(validation_alias="file_val_alias")
file: bytes = File(validation_alias="file_val_alias")
@app.post(
"/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)
data: RequiredFieldValidationAliasModel = File(...),
async def required_field_validation_alias_model(
data: RequiredFieldValidationAliasModel = Form(),
):
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"}
)
assert resp.status_code == 200, resp.text
# Fails with:
# 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}}]}
@ -286,15 +311,15 @@ def test_required_field_validation_alias_model_schema():
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(
"/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)
data: OptionalFieldValidationAliasModel = File(...),
async def optional_field_validation_alias_model(
data: OptionalFieldValidationAliasModel = Form(),
):
if data.file is 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"}
)
assert resp.status_code == 200, resp.text
# Fails with:
# 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}}]}
@ -341,15 +365,15 @@ def test_optional_field_validation_alias_model_schema():
class ListFieldValidationAliasModel(BaseModel):
files: List[bytes] = Field(validation_alias="files_val_alias")
files: List[bytes] = File(validation_alias="files_val_alias")
@app.post(
"/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)
data: ListFieldValidationAliasModel = File(...),
async def list_field_validation_alias_model(
data: ListFieldValidationAliasModel = Form(),
):
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")],
)
assert resp.status_code == 200, resp.text
# Fails with:
# 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}}]}
@ -399,15 +422,15 @@ def test_list_field_validation_alias_model_schema():
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(
"/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)
data: OptionalListFieldValidationAliasModel = File(...),
async def optional_list_field_validation_alias_model(
data: OptionalListFieldValidationAliasModel = Form(),
):
if data.files is 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")],
)
assert resp.status_code == 200, resp.text
# Fails with:
# 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}}]}
@ -458,22 +480,22 @@ def test_optional_list_field_validation_alias_model_schema():
# =====================================================================================
# 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
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(
"/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)
data: RequiredFieldAliasAndValidationAliasModel = File(...),
async def required_field_alias_and_validation_alias_model(
data: RequiredFieldAliasAndValidationAliasModel = Form(),
):
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"},
)
assert resp.status_code == 200, resp.text
# Fails with:
# 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}}]}
@ -536,7 +557,7 @@ def test_required_field_alias_and_validation_alias_model_schema():
class OptionalFieldAliasAndValidationAliasModel(BaseModel):
file: Optional[bytes] = Field(
file: Optional[bytes] = File(
None, alias="file_alias", validation_alias="file_val_alias"
)
@ -545,8 +566,8 @@ class OptionalFieldAliasAndValidationAliasModel(BaseModel):
"/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)
data: OptionalFieldAliasAndValidationAliasModel = File(...),
async def optional_field_alias_and_validation_alias_model(
data: OptionalFieldAliasAndValidationAliasModel = Form(),
):
if data.file is 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"},
)
assert resp.status_code == 200, resp.text
# Fails with:
# 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}}]}
@ -606,15 +626,15 @@ def test_optional_field_alias_and_validation_alias_model_schema():
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(
"/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)
data: ListFieldAliasAndValidationAliasModel = File(...),
async def list_field_alias_and_validation_alias_model(
data: ListFieldAliasAndValidationAliasModel = Form(),
):
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")],
)
assert resp.status_code == 200, resp.text
# Fails with:
# 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}}]}
@ -678,7 +697,7 @@ def test_list_field_alias_and_validation_alias_model_schema():
class OptionalListFieldAliasAndValidationAliasModel(BaseModel):
files: Optional[List[bytes]] = Field(
files: Optional[List[bytes]] = File(
None, alias="files_alias", validation_alias="files_val_alias"
)
@ -687,8 +706,8 @@ class OptionalListFieldAliasAndValidationAliasModel(BaseModel):
"/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)
data: OptionalListFieldAliasAndValidationAliasModel = File(...),
async def optional_list_field_alias_and_validation_alias_model(
data: OptionalListFieldAliasAndValidationAliasModel = Form(),
):
if data.files is 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")],
)
assert resp.status_code == 200, resp.text
# Fails with:
# 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}}]}

102
tests/test_aliases/test_uploadfile_model_aliases.py

@ -1,8 +1,9 @@
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 pydantic import BaseModel, Field
from pydantic import BaseModel
from ..utils import needs_pydanticv2
@ -12,43 +13,56 @@ app = FastAPI()
# =====================================================================================
# Field(alias=...)
# Current situation: Works
# Current situation: doesn't work (neither validation nor schema generation)
# ------------------------------
# required field
class RequiredFieldAliasModel(BaseModel):
file: UploadFile = Field(alias="file_alias")
file: UploadFile = File(alias="file_alias")
@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}
@pytest.mark.xfail(raises=AssertionError, strict=False)
def test_required_field_alias_model_by_name():
client = TestClient(app)
resp = client.post("/required-field-alias-model", files={"file": b"content"})
assert resp.status_code == 422
detail = resp.json()["detail"]
assert detail[0]["msg"] == "Field required"
assert "file_alias" in detail[0]["loc"]
# assert 200 == 422
# 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():
client = TestClient(app)
resp = client.post("/required-field-alias-model", files={"file_alias": b"content"})
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():
openapi = app.openapi()
body_schema = openapi["components"]["schemas"]["RequiredFieldAliasModel"]
assert body_schema["properties"] == {
"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):
file: Optional[UploadFile] = Field(None, alias="file_alias")
file: Optional[UploadFile] = File(None, alias="file_alias")
@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:
return {"file_size": None}
return {"file_size": data.file.size}
@pytest.mark.xfail(raises=AssertionError, strict=False)
def test_optional_field_alias_model_by_name():
client = TestClient(app)
resp = client.post("/optional-field-alias-model", files={"file": b"content"})
assert resp.status_code == 200
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():
client = TestClient(app)
resp = client.post("/optional-field-alias-model", files={"file_alias": b"content"})
assert resp.status_code == 200, resp.text
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():
openapi = app.openapi()
body_schema = openapi["components"]["schemas"]["OptionalFieldAliasModel"]
@ -89,6 +108,9 @@ def test_optional_field_alias_model_schema():
"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):
files: List[UploadFile] = Field(alias="files_alias")
files: List[UploadFile] = File(alias="files_alias")
@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]}
@pytest.mark.xfail(raises=AssertionError, strict=False)
def test_list_field_alias_model_by_name():
client = TestClient(app)
resp = client.post(
@ -111,11 +134,14 @@ def test_list_field_alias_model_by_name():
files=[("files", b"content1"), ("files", b"content2")],
)
assert resp.status_code == 422
detail = resp.json()["detail"]
assert detail[0]["msg"] == "Field required"
assert "files_alias" in detail[0]["loc"]
# Uncomment when the assertion above passes:
# 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():
client = TestClient(app)
resp = client.post(
@ -123,9 +149,12 @@ def test_list_field_alias_model_by_alias():
files=[("files_alias", b"content1"), ("files_alias", b"content2")],
)
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():
openapi = app.openapi()
body_schema = openapi["components"]["schemas"]["ListFieldAliasModel"]
@ -143,16 +172,17 @@ def test_list_field_alias_model_schema():
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")
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:
return {"file_sizes": None}
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():
client = TestClient(app)
resp = client.post(
@ -163,6 +193,7 @@ def test_optional_list_field_alias_model_by_name():
assert resp.json() == {"file_sizes": None}
@pytest.mark.xfail(raises=AssertionError, strict=False)
def test_optional_list_field_alias_model_by_alias():
client = TestClient(app)
resp = client.post(
@ -173,6 +204,7 @@ def test_optional_list_field_alias_model_by_alias():
assert resp.json() == {"file_sizes": [8, 8]}
@pytest.mark.xfail(raises=AssertionError, strict=False)
def test_optional_list_field_alias_model_schema():
openapi = app.openapi()
body_schema = openapi["components"]["schemas"]["OptionalListFieldAliasModel"]
@ -197,7 +229,7 @@ def test_optional_list_field_alias_model_schema():
class RequiredFieldValidationAliasModel(BaseModel):
file: UploadFile = Field(validation_alias="file_val_alias")
file: UploadFile = File(validation_alias="file_val_alias")
@app.post(
@ -205,7 +237,7 @@ class RequiredFieldValidationAliasModel(BaseModel):
operation_id="required_field_validation_alias_model",
)
async def required_field_validation_alias_model(
data: RequiredFieldValidationAliasModel = File(...),
data: RequiredFieldValidationAliasModel = Form(),
):
return {"file_size": data.file.size}
@ -247,7 +279,7 @@ def test_required_field_validation_alias_model_schema():
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(
@ -255,7 +287,7 @@ class OptionalFieldValidationAliasModel(BaseModel):
operation_id="optional_field_validation_alias_model",
)
async def optional_field_validation_alias_model(
data: OptionalFieldValidationAliasModel = File(...),
data: OptionalFieldValidationAliasModel = Form(),
):
if data.file is None:
return {"file_size": None}
@ -296,7 +328,7 @@ def test_optional_field_validation_alias_model_schema():
class ListFieldValidationAliasModel(BaseModel):
files: List[UploadFile] = Field(validation_alias="files_val_alias")
files: List[UploadFile] = File(validation_alias="files_val_alias")
@app.post(
@ -304,7 +336,7 @@ class ListFieldValidationAliasModel(BaseModel):
operation_id="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]}
@ -350,7 +382,7 @@ def test_list_field_validation_alias_model_schema():
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(
@ -358,7 +390,7 @@ class OptionalListFieldValidationAliasModel(BaseModel):
operation_id="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:
return {"file_sizes": None}
@ -414,7 +446,7 @@ def test_optional_list_field_validation_alias_model_schema():
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(
@ -422,7 +454,7 @@ class RequiredFieldAliasAndValidationAliasModel(BaseModel):
operation_id="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}
@ -479,7 +511,7 @@ def test_required_field_alias_and_validation_alias_model_schema():
class OptionalFieldAliasAndValidationAliasModel(BaseModel):
file: Optional[UploadFile] = Field(
file: Optional[UploadFile] = File(
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",
)
async def optional_field_alias_and_validation_alias_model(
data: OptionalFieldAliasAndValidationAliasModel = File(...),
data: OptionalFieldAliasAndValidationAliasModel = Form(),
):
if data.file is None:
return {"file_size": None}
@ -543,7 +575,7 @@ def test_optional_field_alias_and_validation_alias_model_schema():
class ListFieldAliasAndValidationAliasModel(BaseModel):
files: List[UploadFile] = Field(
files: List[UploadFile] = File(
alias="files_alias", validation_alias="files_val_alias"
)
@ -553,7 +585,7 @@ class ListFieldAliasAndValidationAliasModel(BaseModel):
operation_id="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]}
@ -613,7 +645,7 @@ def test_list_field_alias_and_validation_alias_model_schema():
class OptionalListFieldAliasAndValidationAliasModel(BaseModel):
files: Optional[List[UploadFile]] = Field(
files: Optional[List[UploadFile]] = File(
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",
)
async def optional_list_field_alias_and_validation_alias_model(
data: OptionalListFieldAliasAndValidationAliasModel = File(...),
data: OptionalListFieldAliasAndValidationAliasModel = Form(),
):
if data.files is None:
return {"file_sizes": None}

Loading…
Cancel
Save