Browse Source

Fix Pydantic v1 optional schema tests and file model tests

pull/14293/head
Yurii Motov 8 months ago
parent
commit
de7d6dfcb1
  1. 55
      tests/test_aliases/test_file_field_aliases_pd_v1.py
  2. 63
      tests/test_aliases/test_file_model_aliases_pd_v1.py
  3. 43
      tests/test_aliases/test_form_field_aliases_pd_v1.py
  4. 48
      tests/test_aliases/test_form_model_aliases_pd_v1.py
  5. 20
      tests/test_aliases/test_uploadfile_model_aliases_pd_v1.py

55
tests/test_aliases/test_file_field_aliases_pd_v1.py

@ -1,7 +1,7 @@
from typing import List, Optional
import pytest
from fastapi import FastAPI, File
from fastapi._compat import PYDANTIC_V2
from fastapi.testclient import TestClient
from ..utils import needs_pydanticv1
@ -13,7 +13,6 @@ app = FastAPI()
# =====================================================================================
# File(alias=...)
# Current situation: Works
# Schema generation for optional field and optional list fails due to issue likely not related to aliases
# ------------------------------
# required field
@ -51,14 +50,15 @@ def test_required_field_alias_schema():
# ------------------------------
# optional field
if not PYDANTIC_V2:
@app.post("/optional-field-alias", operation_id="optional_field_alias")
async def optional_field_alias(
file: Optional[bytes] = File(None, alias="file_alias"),
):
if file is None:
return {"file_size": None}
return {"file_size": len(file)}
@app.post("/optional-field-alias", operation_id="optional_field_alias")
async def optional_field_alias(
file: Optional[bytes] = File(None, alias="file_alias", nullable=True),
):
if file is None:
return {"file_size": None}
return {"file_size": len(file)}
def test_optional_field_alias_by_name():
@ -75,20 +75,17 @@ def test_optional_field_alias_by_alias():
assert resp.json() == {"file_size": 7}
@pytest.mark.xfail(raises=AssertionError, strict=False)
def test_optional_field_alias_schema():
openapi = app.openapi()
body_schema = openapi["components"]["schemas"]["Body_optional_field_alias"]
assert body_schema["properties"] == {
"file_alias": {
"anyOf": [{"type": "string", "format": "binary"}, {"type": "null"}],
"type": "string",
"nullable": True,
"format": "binary",
"title": "File Alias",
}
}
# Fails with:
# AssertionError: assert
# {'file_alias': {'type': 'string', 'format': 'binary', 'title': 'File Alias'}} ==
# {'file_alias': {'anyOf': [{'type': 'string', 'format': 'binary'}, {'type': 'null'}], 'title': 'File Alias'}}
# ------------------------------
@ -137,13 +134,15 @@ def test_list_field_alias_schema():
# optional list field
@app.post("/optional-list-field-alias", operation_id="optional_list_field_alias")
async def optional_list_field_alias(
files: Optional[List[bytes]] = File(None, alias="files_alias"),
):
if files is None:
return {"file_sizes": None}
return {"file_sizes": [len(file) for file in files]}
if not PYDANTIC_V2:
@app.post("/optional-list-field-alias", operation_id="optional_list_field_alias")
async def optional_list_field_alias(
files: Optional[List[bytes]] = File(None, alias="files_alias", nullable=True),
):
if files is None:
return {"file_sizes": None}
return {"file_sizes": [len(file) for file in files]}
def test_optional_list_field_alias_by_name():
@ -165,20 +164,14 @@ def test_optional_list_field_alias_by_alias():
assert resp.json() == {"file_sizes": [8, 8]}
@pytest.mark.xfail(raises=AssertionError, strict=False)
def test_optional_list_field_alias_schema():
openapi = app.openapi()
body_schema = openapi["components"]["schemas"]["Body_optional_list_field_alias"]
assert body_schema["properties"] == {
"files_alias": {
"anyOf": [
{"items": {"type": "string", "format": "binary"}, "type": "array"},
{"type": "null"},
],
"items": {"format": "binary", "type": "string"},
"nullable": True,
"title": "Files Alias",
"type": "array",
},
}
# Fails with:
# AssertionError: assert
# {'files_alias': {'items': {'type': 'string', 'format': 'binary'}, 'type': 'array', 'title': 'Files Alias'}} ==
# {'files_alias': {'anyOf': [{'items': {'type': 'string', 'format': 'binary'}, 'type': 'array'}, {'type': 'null'}], 'title': 'Files Alias'}}

63
tests/test_aliases/test_file_model_aliases_pd_v1.py

@ -1,10 +1,9 @@
from typing import List, Optional
import pytest
from fastapi import FastAPI, File
from fastapi import FastAPI, File, Form
from fastapi._compat import PYDANTIC_V2
from fastapi.testclient import TestClient
from pydantic import BaseModel, Field
from pydantic import BaseModel
from ..utils import needs_pydanticv1
@ -14,20 +13,18 @@ app = FastAPI()
# =====================================================================================
# Field(alias=...)
# Current situation: doesn't work due to validation error
# Current situation: works
# ------------------------------
# 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)}
@ -40,17 +37,11 @@ def test_required_field_alias_model_by_name():
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
# Fails with:
# AssertionError: assert 422 == 200
# {"detail":[{"loc":["body","file_alias"],"msg":"byte type expected","type":"type_error.bytes"}]}
# Uncomment when the assertion above passes:
# assert resp.json() == {"file_size": 7}
assert resp.json() == {"file_size": 7}
def test_required_field_alias_model_schema():
@ -67,12 +58,10 @@ def test_required_field_alias_model_schema():
if not PYDANTIC_V2:
class OptionalFieldAliasModel(BaseModel):
file: Optional[bytes] = Field(None, alias="file_alias", nullable=True)
file: Optional[bytes] = File(None, alias="file_alias", nullable=True)
@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)}
@ -85,17 +74,11 @@ def test_optional_field_alias_model_by_name():
assert resp.json() == {"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
# Fails with:
# AssertionError: assert 422 == 200
# {"detail":[{"loc":["body","file_alias"],"msg":"byte type expected","type":"type_error.bytes"}]}
# Uncomment when the assertion above passes:
# assert resp.json() == {"file_size": 7}
assert resp.json() == {"file_size": 7}
def test_optional_field_alias_model_schema():
@ -116,13 +99,11 @@ 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]}
@ -138,7 +119,6 @@ def test_list_field_alias_model_by_name():
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(
@ -146,12 +126,7 @@ 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:
# AssertionError: assert 422 == 200
# {"detail":[{"loc":["body","files_alias",0],"msg":"byte type expected","type":"type_error.bytes"},{"loc":["body","files_alias",1],"msg":"byte type expected","type":"type_error.bytes"}]}
# Uncomment when the assertion above passes:
# assert resp.json() == {"file_sizes": [8, 8]}
assert resp.json() == {"file_sizes": [8, 8]}
def test_list_field_alias_model_schema():
@ -173,11 +148,11 @@ def test_list_field_alias_model_schema():
if not PYDANTIC_V2:
class OptionalListFieldAliasModel(BaseModel):
files: Optional[List[bytes]] = Field(None, alias="files_alias", nullable=True)
files: Optional[List[bytes]] = File(None, alias="files_alias", nullable=True)
@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}
@ -194,7 +169,6 @@ 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(
@ -202,12 +176,7 @@ 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":[{"loc":["body","files_alias",0],"msg":"byte type expected","type":"type_error.bytes"},{"loc":["body","files_alias",1],"msg":"byte type expected","type":"type_error.bytes"}]}
# Uncomment when the assertion above passes:
# assert resp.json() == {"file_sizes": [8, 8]}
assert resp.json() == {"file_sizes": [8, 8]}
def test_optional_list_field_alias_model_schema():

43
tests/test_aliases/test_form_field_aliases_pd_v1.py

@ -1,7 +1,7 @@
from typing import List, Optional
import pytest
from fastapi import FastAPI, Form
from fastapi._compat import PYDANTIC_V2
from fastapi.testclient import TestClient
from ..utils import needs_pydanticv1
@ -13,8 +13,6 @@ app = FastAPI()
# =====================================================================================
# Form(alias=...)
# Current situation: Works
# Schema generation for optional field and optional list fails due to issue likely not related to aliases
# ------------------------------
# required field
@ -52,12 +50,13 @@ def test_required_field_alias_schema():
# ------------------------------
# optional field
if not PYDANTIC_V2:
@app.post("/optional-field-alias", operation_id="optional_field_alias")
async def optional_field_alias(
param: Optional[str] = Form(None, alias="param_alias"),
):
return {"param": param}
@app.post("/optional-field-alias", operation_id="optional_field_alias")
async def optional_field_alias(
param: Optional[str] = Form(None, alias="param_alias", nullable=True),
):
return {"param": param}
def test_optional_field_alias_by_name():
@ -74,20 +73,16 @@ def test_optional_field_alias_by_alias():
assert resp.json() == {"param": "123"}
@pytest.mark.xfail(raises=AssertionError, strict=False)
def test_optional_field_alias_schema():
openapi = app.openapi()
body_schema = openapi["components"]["schemas"]["Body_optional_field_alias"]
assert body_schema["properties"] == {
"param_alias": {
"anyOf": [{"type": "string"}, {"type": "null"}],
"type": "string",
"nullable": True,
"title": "Param Alias",
}
}
# Fails with:
# AssertionError: assert
# {'param_alias': {'type': 'string', 'title': 'Param Alias'}} ==
# {'param_alias': {'anyOf': [{'type': 'string'}, {'type': 'null'}], 'title': 'Param Alias'}}
# ------------------------------
@ -130,12 +125,13 @@ def test_list_field_alias_schema():
# ------------------------------
# optional list field
if not PYDANTIC_V2:
@app.post("/optional-list-field-alias", operation_id="optional_list_field_alias")
async def optional_list_field_alias(
param: Optional[List[str]] = Form(None, alias="param_alias"),
):
return {"param": param}
@app.post("/optional-list-field-alias", operation_id="optional_list_field_alias")
async def optional_list_field_alias(
param: Optional[List[str]] = Form(None, alias="param_alias", nullable=True),
):
return {"param": param}
def test_optional_list_field_alias_by_name():
@ -154,17 +150,14 @@ def test_optional_list_field_alias_by_alias():
assert resp.json() == {"param": ["123", "456"]}
@pytest.mark.xfail(raises=AssertionError, strict=False)
def test_optional_list_field_alias_schema():
openapi = app.openapi()
body_schema = openapi["components"]["schemas"]["Body_optional_list_field_alias"]
assert body_schema["properties"] == {
"param_alias": {
"anyOf": [{"items": {"type": "string"}, "type": "array"}, {"type": "null"}],
"items": {"type": "string"},
"type": "array",
"nullable": True,
"title": "Param Alias",
},
}
# Fails with:
# AssertionError: assert
# {'param_alias': {'items': {'type': 'string'}, 'type': 'array', 'title': 'Param Alias'}} ==
# {'param_alias': {'anyOf': [{'items': {'type': 'string'}, 'type': 'array'}, {'type': 'null'}], 'title': 'Param Alias'}}

48
tests/test_aliases/test_form_model_aliases_pd_v1.py

@ -1,7 +1,7 @@
from typing import List, Optional
import pytest
from fastapi import FastAPI, Form
from fastapi._compat import PYDANTIC_V2
from fastapi.testclient import TestClient
from pydantic import BaseModel, Field
@ -14,7 +14,6 @@ app = FastAPI()
# =====================================================================================
# Field(alias=...)
# Current situation: works
# Schema generation for optional field and optional list fails due to issue likely not related to aliases
# ------------------------------
# required field
@ -56,14 +55,14 @@ def test_required_field_alias_model_schema():
# ------------------------------
# optional field
if not PYDANTIC_V2:
class OptionalFieldAliasModel(BaseModel):
param: Optional[str] = Field(None, alias="param_alias")
class OptionalFieldAliasModel(BaseModel):
param: Optional[str] = Field(None, alias="param_alias", nullable=True)
@app.post("/optional-field-alias-model")
async def optional_field_alias_model(data: OptionalFieldAliasModel = Form(...)):
return {"param": data.param}
@app.post("/optional-field-alias-model")
async def optional_field_alias_model(data: OptionalFieldAliasModel = Form(...)):
return {"param": data.param}
def test_optional_field_alias_model_by_name():
@ -80,20 +79,16 @@ def test_optional_field_alias_model_by_alias():
assert resp.json() == {"param": "123"}
@pytest.mark.xfail(raises=AssertionError, strict=False)
def test_optional_field_alias_model_schema():
openapi = app.openapi()
body_schema = openapi["components"]["schemas"]["OptionalFieldAliasModel"]
assert body_schema["properties"] == {
"param_alias": {
"anyOf": [{"type": "string"}, {"type": "null"}],
"type": "string",
"nullable": True,
"title": "Param Alias",
},
}
# Fails with:
# AssertionError: assert
# {'param_alias': {'type': 'string', 'title': 'Param Alias'}} ==
# {'param_alias': {'anyOf': [{'type': 'string'}, {'type': 'null'}], 'title': 'Param Alias'}}
# ------------------------------
@ -140,16 +135,16 @@ def test_list_field_alias_model_schema():
# ------------------------------
# optional list field
if not PYDANTIC_V2:
class OptionalListFieldAliasModel(BaseModel):
param: Optional[List[str]] = Field(None, alias="param_alias")
class OptionalListFieldAliasModel(BaseModel):
param: Optional[List[str]] = Field(None, alias="param_alias", nullable=True)
@app.post("/optional-list-field-alias-model")
async def optional_list_field_alias_model(
data: OptionalListFieldAliasModel = Form(...),
):
return {"param": data.param}
@app.post("/optional-list-field-alias-model")
async def optional_list_field_alias_model(
data: OptionalListFieldAliasModel = Form(...),
):
return {"param": data.param}
def test_optional_list_field_alias_model_by_name():
@ -170,17 +165,14 @@ def test_optional_list_field_alias_model_by_alias():
assert resp.json() == {"param": ["123", "456"]}
@pytest.mark.xfail(raises=AssertionError, strict=False)
def test_optional_list_field_alias_model_schema():
openapi = app.openapi()
body_schema = openapi["components"]["schemas"]["OptionalListFieldAliasModel"]
assert body_schema["properties"] == {
"param_alias": {
"anyOf": [{"items": {"type": "string"}, "type": "array"}, {"type": "null"}],
"items": {"type": "string"},
"type": "array",
"nullable": True,
"title": "Param Alias",
}
}
# Fails with:
# AssertionError: assert
# {'param_alias': {'items': {'type': 'string'}, 'type': 'array', 'title': 'Param Alias'}} ==
# {'param_alias': {'anyOf': [{'items': {'type': 'string'}, 'type': 'array'}, {'type': 'null'}], 'title': 'Param Alias'}}

20
tests/test_aliases/test_uploadfile_model_aliases_pd_v1.py

@ -1,9 +1,9 @@
from typing import List, Optional
from fastapi import FastAPI, File, UploadFile
from fastapi import FastAPI, File, Form, UploadFile
from fastapi._compat import PYDANTIC_V2
from fastapi.testclient import TestClient
from pydantic import BaseModel, Field
from pydantic import BaseModel
from ..utils import needs_pydanticv1
@ -20,11 +20,11 @@ app = FastAPI()
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}
@ -58,10 +58,10 @@ def test_required_field_alias_model_schema():
if not PYDANTIC_V2:
class OptionalFieldAliasModel(BaseModel):
file: Optional[UploadFile] = Field(None, alias="file_alias", nullable=True)
file: Optional[UploadFile] = File(None, alias="file_alias", nullable=True)
@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}
@ -99,11 +99,11 @@ 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]}
@ -147,13 +147,13 @@ def test_list_field_alias_model_schema():
if not PYDANTIC_V2:
class OptionalListFieldAliasModel(BaseModel):
files: Optional[List[UploadFile]] = Field(
files: Optional[List[UploadFile]] = File(
None, alias="files_alias", nullable=True
)
@app.post("/optional-list-field-alias-model")
async def optional_list_field_alias_model(
data: OptionalListFieldAliasModel = File(),
data: OptionalListFieldAliasModel = Form(),
):
if data.files is None:
return {"file_sizes": None}

Loading…
Cancel
Save