Browse Source

Fix tests for aliases with Pydantic V1

pull/14293/head
Yurii Motov 9 months ago
parent
commit
b630ac6cf4
  1. 622
      tests/test_aliases/test_file_field_aliases_pd_v1.py
  2. 570
      tests/test_aliases/test_form_field_aliases_pd_v1.py
  3. 519
      tests/test_aliases/test_form_model_aliases_pd_v1.py

622
tests/test_aliases/test_file_field_aliases_pd_v1.py

@ -1,16 +1,19 @@
from typing import Optional from typing import List, Optional
import pytest import pytest
from dirty_equals import IsOneOf
from fastapi import FastAPI, File from fastapi import FastAPI, File
from fastapi.testclient import TestClient from fastapi.testclient import TestClient
from ..utils import needs_pydanticv1
pytestmark = needs_pydanticv1
app = FastAPI() app = FastAPI()
# ===================================================================================== # =====================================================================================
# File(alias=...) # File(alias=...)
# Current situation: Works, but schema is wrong # Current situation: Works
# Optional[list[bytes]] fails due to another issue (likely not related to aliases) # Schema generation for optional field and optional list fails due to issue likely not related to aliases
# ------------------------------ # ------------------------------
# required field # required field
@ -26,10 +29,7 @@ def test_required_field_alias_by_name():
resp = client.post("/required-field-alias", files={"file": b"content"}) resp = client.post("/required-field-alias", 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"] == IsOneOf( assert detail[0]["msg"] == "field required"
"Field required",
"field required", # TODO: remove when deprecating Pydantic v1
)
assert "file_alias" in detail[0]["loc"] assert "file_alias" in detail[0]["loc"]
@ -40,16 +40,12 @@ def test_required_field_alias_by_alias():
assert resp.json() == {"file_size": 7} assert resp.json() == {"file_size": 7}
@pytest.mark.xfail(raises=AssertionError, strict=False)
def test_required_field_alias_schema(): def test_required_field_alias_schema():
openapi = app.openapi() openapi = app.openapi()
body_schema = openapi["components"]["schemas"]["Body_required_field_alias"] body_schema = openapi["components"]["schemas"]["Body_required_field_alias"]
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'}}
# ------------------------------ # ------------------------------
@ -89,9 +85,9 @@ def test_optional_field_alias_schema():
"title": "File Alias", "title": "File Alias",
} }
} }
# Currently fails due to issue with aliases: # Fails with:
# AssertionError: assert # AssertionError: assert
# {'file': {'anyOf': [{'type': 'string', 'format': 'binary'}, {'type': 'null'}], 'title': 'File'}} == # {'file_alias': {'type': 'string', 'format': 'binary', 'title': 'File Alias'}} ==
# {'file_alias': {'anyOf': [{'type': 'string', 'format': 'binary'}, {'type': 'null'}], 'title': 'File Alias'}} # {'file_alias': {'anyOf': [{'type': 'string', 'format': 'binary'}, {'type': 'null'}], 'title': 'File Alias'}}
@ -100,7 +96,7 @@ def test_optional_field_alias_schema():
@app.post("/list-field-alias", operation_id="list_field_alias") @app.post("/list-field-alias", operation_id="list_field_alias")
async def list_field_alias(files: list[bytes] = File(alias="files_alias")): async def list_field_alias(files: List[bytes] = File(alias="files_alias")):
return {"file_sizes": [len(file) for file in files]} return {"file_sizes": [len(file) for file in files]}
@ -111,10 +107,7 @@ def test_list_field_alias_by_name():
) )
assert resp.status_code == 422 assert resp.status_code == 422
detail = resp.json()["detail"] detail = resp.json()["detail"]
assert detail[0]["msg"] == IsOneOf( assert detail[0]["msg"] == "field required"
"Field required",
"field required", # TODO: remove when deprecating Pydantic v1
)
assert "files_alias" in detail[0]["loc"] assert "files_alias" in detail[0]["loc"]
@ -128,7 +121,6 @@ def test_list_field_alias_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_list_field_alias_schema(): def test_list_field_alias_schema():
openapi = app.openapi() openapi = app.openapi()
body_schema = openapi["components"]["schemas"]["Body_list_field_alias"] body_schema = openapi["components"]["schemas"]["Body_list_field_alias"]
@ -139,10 +131,6 @@ def test_list_field_alias_schema():
"items": {"type": "string", "format": "binary"}, "items": {"type": "string", "format": "binary"},
} }
} }
# Currently fails due to issue with aliases:
# AssertionError: assert
# {'files': {'items': {'type': 'string', 'format': 'binary'}, 'type': 'array', 'title': 'Files'}} ==
# {'files_alias': {'title': 'Files Alias', 'type': 'array', 'items': {'type': 'string', 'format': 'binary'}}}
# ------------------------------ # ------------------------------
@ -151,7 +139,7 @@ def test_list_field_alias_schema():
@app.post("/optional-list-field-alias", operation_id="optional_list_field_alias") @app.post("/optional-list-field-alias", operation_id="optional_list_field_alias")
async def optional_list_field_alias( async def optional_list_field_alias(
files: Optional[list[bytes]] = File(None, alias="files_alias"), files: Optional[List[bytes]] = File(None, alias="files_alias"),
): ):
if files is None: if files is None:
return {"file_sizes": None} return {"file_sizes": None}
@ -168,20 +156,12 @@ def test_optional_list_field_alias_by_name():
assert resp.json() == {"file_sizes": None} assert resp.json() == {"file_sizes": None}
@pytest.mark.xfail(
reason="Optional[list[bytes]] File type causes TypeError in FastAPI",
raises=TypeError,
strict=False,
)
def test_optional_list_field_alias_by_alias(): def test_optional_list_field_alias_by_alias():
client = TestClient(app) client = TestClient(app)
resp = client.post( resp = client.post(
"/optional-list-field-alias", "/optional-list-field-alias",
files=[("files_alias", b"content1"), ("files_alias", b"content2")], files=[("files_alias", b"content1"), ("files_alias", b"content2")],
) )
# Currently fails due to some issue (likely unrelated to aliases) with:
# TypeError: issubclass() arg 1 must be a class
assert resp.json() == {"file_sizes": [8, 8]} assert resp.json() == {"file_sizes": [8, 8]}
@ -198,579 +178,7 @@ def test_optional_list_field_alias_schema():
"title": "Files Alias", "title": "Files Alias",
}, },
} }
# Currently fails due to issue with aliases: # Fails with:
# AssertionError: assert # AssertionError: assert
# {'files': {'anyOf': [{'items': {'type': 'string', 'format': 'binary'}, 'type': 'array'}, {'type': 'null'}], 'title': 'Files'}} == # {'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'}} # {'files_alias': {'anyOf': [{'items': {'type': 'string', 'format': 'binary'}, 'type': 'array'}, {'type': 'null'}], 'title': 'Files Alias'}}
# =====================================================================================
# File(validation_alias=...)
# Current situation: schema is correct, but doesn't work (parameter name is used)
# Optional[list[bytes]] fails due to another issue (likely not related to aliases)
# ------------------------------
# required field
@app.post(
"/required-field-validation-alias", operation_id="required_field_validation_alias"
)
async def required_field_validation_alias(
file: bytes = File(validation_alias="file_val_alias"),
):
return {"file_size": len(file)}
@pytest.mark.xfail(raises=AssertionError, strict=False)
def test_required_field_validation_alias_by_name():
client = TestClient(app)
resp = client.post("/required-field-validation-alias", files={"file": b"content"})
assert resp.status_code == 422
# Currently fails due to issue with aliases:
# AssertionError: assert 200 == 422
detail = resp.json()["detail"]
assert detail[0]["msg"] == IsOneOf(
"Field required",
"field required", # TODO: remove when deprecating Pydantic v1
)
assert "file_val_alias" in detail[0]["loc"]
@pytest.mark.xfail(raises=AssertionError, strict=False)
def test_required_field_validation_alias_by_alias():
client = TestClient(app)
resp = client.post(
"/required-field-validation-alias", files={"file_val_alias": b"content"}
)
assert resp.status_code == 200
# Currently fails due to issue with aliases:
# AssertionError: assert 422 == 200
assert resp.json() == {"file_size": 7}
def test_required_field_validation_alias_schema():
openapi = app.openapi()
body_schema = openapi["components"]["schemas"][
"Body_required_field_validation_alias"
]
assert body_schema["properties"] == {
"file_val_alias": {
"title": "File Val Alias",
"type": "string",
"format": "binary",
},
}
# ------------------------------
# optional field
@app.post(
"/optional-field-validation-alias", operation_id="optional_field_validation_alias"
)
async def optional_field_validation_alias(
file: Optional[bytes] = File(None, validation_alias="file_val_alias"),
):
if file is None:
return {"file_size": None}
return {"file_size": len(file)}
@pytest.mark.xfail(raises=AssertionError, strict=False)
def test_optional_field_validation_alias_by_name():
client = TestClient(app)
resp = client.post("/optional-field-validation-alias", files={"file": b"content"})
assert resp.json() == {"file_size": None}
# Currently fails due to issue with aliases:
# AssertionError: assert {'file_size': 7} == {'file_size': None}
@pytest.mark.xfail(raises=AssertionError, strict=False)
def test_optional_field_validation_alias_by_alias():
client = TestClient(app)
resp = client.post(
"/optional-field-validation-alias", files={"file_val_alias": b"content"}
)
assert resp.json() == {"file_size": 7}
# Currently fails due to issue with aliases:
# AssertionError: assert {'file_size': None} == {'file_size': 7}
def test_optional_field_validation_alias_schema():
openapi = app.openapi()
body_schema = openapi["components"]["schemas"][
"Body_optional_field_validation_alias"
]
assert body_schema["properties"] == {
"file_val_alias": {
"anyOf": [{"type": "string", "format": "binary"}, {"type": "null"}],
"title": "File Val Alias",
},
}
# ------------------------------
# list field
@app.post("/list-field-validation-alias", operation_id="list_field_validation_alias")
async def list_field_validation_alias(
files: list[bytes] = File(validation_alias="files_val_alias"),
):
return {"file_sizes": [len(file) for file in files]}
@pytest.mark.xfail(raises=AssertionError, strict=False)
def test_list_field_validation_alias_by_name():
client = TestClient(app)
resp = client.post(
"/list-field-validation-alias",
files=[("files", b"content1"), ("files", b"content2")],
)
assert resp.status_code == 422
# Currently fails due to issue with aliases:
# AssertionError: assert 200 == 422
detail = resp.json()["detail"]
assert detail[0]["msg"] == IsOneOf(
"Field required",
"field required", # TODO: remove when deprecating Pydantic v1
)
assert "files_val_alias" in detail[0]["loc"]
@pytest.mark.xfail(raises=AssertionError, strict=False)
def test_list_field_validation_alias_by_alias():
client = TestClient(app)
resp = client.post(
"/list-field-validation-alias",
files=[("files_val_alias", b"content1"), ("files_val_alias", b"content2")],
)
assert resp.status_code == 200
# Currently fails due to issue with aliases:
# AssertionError: assert 422 == 200
assert resp.json() == {"file_sizes": [8, 8]}
def test_list_field_validation_alias_schema():
openapi = app.openapi()
body_schema = openapi["components"]["schemas"]["Body_list_field_validation_alias"]
assert body_schema["properties"] == {
"files_val_alias": {
"items": {"type": "string", "format": "binary"},
"title": "Files Val Alias",
"type": "array",
},
}
# ------------------------------
# optional list field
@app.post(
"/optional-list-field-validation-alias",
operation_id="optional_list_field_validation_alias",
)
async def optional_list_field_validation_alias(
files: Optional[list[bytes]] = File(None, validation_alias="files_val_alias"),
):
if files is None:
return {"file_sizes": None}
return {"file_sizes": [len(file) for file in files]}
@pytest.mark.xfail(
reason="Optional[list[bytes]] File type causes TypeError in FastAPI",
raises=TypeError,
strict=False,
)
def test_optional_list_field_validation_alias_by_name():
client = TestClient(app)
resp = client.post(
"/optional-list-field-validation-alias",
files=[("files", b"content1"), ("files", b"content2")],
)
# Currently fails due to some issue (likely unrelated to aliases) with:
# TypeError: issubclass() arg 1 must be a class
assert resp.json() == {"file_sizes": None}
# Will likely fail due to issue with aliases with:
# AssertionError: assert {'file_sizes': [8, 8]} == {'file_sizes': None}
@pytest.mark.xfail(raises=AssertionError, strict=False)
def test_optional_list_field_validation_alias_by_alias():
client = TestClient(app)
resp = client.post(
"/optional-list-field-validation-alias",
files=[("files_val_alias", b"content1"), ("files_val_alias", b"content2")],
)
assert resp.json() == {"file_sizes": [8, 8]}
# Currently fails due to issue with aliases:
# AssertionError: assert {'file_sizes': None} == {'file_sizes': [8, 8]}
def test_optional_list_field_validation_alias_schema():
openapi = app.openapi()
body_schema = openapi["components"]["schemas"][
"Body_optional_list_field_validation_alias"
]
assert body_schema["properties"] == {
"files_val_alias": {
"anyOf": [
{"items": {"type": "string", "format": "binary"}, "type": "array"},
{"type": "null"},
],
"title": "Files Val Alias",
},
}
# =====================================================================================
# File(alias=..., validation_alias=...)
# Current situation: Schema is correct (validation_alias), but doesn't work (alias is used)
# Optional[list[bytes]] fails due to another issue (likely not related to aliases)
# ------------------------------
# required field
@app.post(
"/required-field-alias-and-validation-alias",
operation_id="required_field_alias_and_validation_alias",
)
async def required_field_alias_and_validation_alias(
file: bytes = File(alias="file_alias", validation_alias="file_val_alias"),
):
return {"file_size": len(file)}
@pytest.mark.xfail(raises=AssertionError, strict=False)
def test_required_field_alias_and_validation_alias_by_name():
client = TestClient(app)
resp = client.post(
"/required-field-alias-and-validation-alias", files={"file": b"content"}
)
assert resp.status_code == 422
detail = resp.json()["detail"]
assert detail[0]["msg"] == IsOneOf(
"Field required",
"field required", # TODO: remove when deprecating Pydantic v1
)
assert "file_val_alias" in detail[0]["loc"]
# Currently fails due to issue with aliases:
# AssertionError: assert 'file_val_alias' in ['body', 'file_alias']
@pytest.mark.xfail(raises=AssertionError, strict=False)
def test_required_field_alias_and_validation_alias_by_alias():
client = TestClient(app)
resp = client.post(
"/required-field-alias-and-validation-alias", files={"file_alias": b"content"}
)
assert resp.status_code == 422
# Currently fails due to issue with aliases:
# AssertionError: assert 200 == 422
detail = resp.json()["detail"]
assert detail[0]["msg"] == IsOneOf(
"Field required",
"field required", # TODO: remove when deprecating Pydantic v1
)
assert "file_val_alias" in detail[0]["loc"]
@pytest.mark.xfail(raises=AssertionError, strict=False)
def test_required_field_alias_and_validation_alias_by_validation_alias():
client = TestClient(app)
resp = client.post(
"/required-field-alias-and-validation-alias",
files={"file_val_alias": b"content"},
)
assert resp.status_code == 200
# Currently fails due to issue with aliases:
# AssertionError: assert 422 == 200
assert resp.json() == {"file_size": 7}
def test_required_field_alias_and_validation_alias_schema():
openapi = app.openapi()
body_schema = openapi["components"]["schemas"][
"Body_required_field_alias_and_validation_alias"
]
assert body_schema["properties"] == {
"file_val_alias": {
"title": "File Val Alias",
"type": "string",
"format": "binary",
}
}
# ------------------------------
# optional field
@app.post(
"/optional-field-alias-and-validation-alias",
operation_id="optional_field_alias_and_validation_alias",
)
async def optional_field_alias_and_validation_alias(
file: Optional[bytes] = File(
None, alias="file_alias", validation_alias="file_val_alias"
),
):
if file is None:
return {"file_size": None}
return {"file_size": len(file)}
def test_optional_field_alias_and_validation_alias_by_name():
client = TestClient(app)
resp = client.post(
"/optional-field-alias-and-validation-alias", files={"file": b"content"}
)
assert resp.status_code == 200
assert resp.json() == {"file_size": None}
@pytest.mark.xfail(raises=AssertionError, strict=False)
def test_optional_field_alias_and_validation_alias_by_alias():
client = TestClient(app)
resp = client.post(
"/optional-field-alias-and-validation-alias", files={"file_alias": b"content"}
)
assert resp.status_code == 200
assert resp.json() == {"file_size": None}
# Currently fails due to issue with aliases:
# AssertionError: assert {'file_size': 7} == {'file_size': None}
@pytest.mark.xfail(raises=AssertionError, strict=False)
def test_optional_field_alias_and_validation_alias_by_validation_alias():
client = TestClient(app)
resp = client.post(
"/optional-field-alias-and-validation-alias",
files={"file_val_alias": b"content"},
)
assert resp.status_code == 200
assert resp.json() == {"file_size": 7}
# Currently fails due to issue with aliases:
# AssertionError: assert {'file_size': None} == {'file_size': 7}
def test_optional_field_alias_and_validation_alias_schema():
openapi = app.openapi()
body_schema = openapi["components"]["schemas"][
"Body_optional_field_alias_and_validation_alias"
]
assert body_schema["properties"] == {
"file_val_alias": {
"anyOf": [{"type": "string", "format": "binary"}, {"type": "null"}],
"title": "File Val Alias",
}
}
# ------------------------------
# list field
@app.post(
"/list-field-alias-and-validation-alias",
operation_id="list_field_alias_and_validation_alias",
)
async def list_field_alias_and_validation_alias(
files: list[bytes] = File(alias="files_alias", validation_alias="files_val_alias"),
):
return {"file_sizes": [len(file) for file in files]}
@pytest.mark.xfail(raises=AssertionError, strict=False)
def test_list_field_alias_and_validation_alias_by_name():
client = TestClient(app)
resp = client.post(
"/list-field-alias-and-validation-alias",
files=[("files", b"content1"), ("files", b"content2")],
)
assert resp.status_code == 422
detail = resp.json()["detail"]
assert detail[0]["msg"] == IsOneOf(
"Field required",
"field required", # TODO: remove when deprecating Pydantic v1
)
assert "files_val_alias" in detail[0]["loc"]
# Currently fails due to issue with aliases:
# AssertionError: assert 'files_val_alias' in ['body', 'files_alias']
@pytest.mark.xfail(raises=AssertionError, strict=False)
def test_list_field_alias_and_validation_alias_by_alias():
client = TestClient(app)
resp = client.post(
"/list-field-alias-and-validation-alias",
files=[("files_alias", b"content1"), ("files_alias", b"content2")],
)
assert resp.status_code == 422
# Currently fails due to issue with aliases:
# AssertionError: assert 200 == 422
detail = resp.json()["detail"]
assert detail[0]["msg"] == IsOneOf(
"Field required",
"field required", # TODO: remove when deprecating Pydantic v1
)
assert "files_val_alias" in detail[0]["loc"]
@pytest.mark.xfail(raises=AssertionError, strict=False)
def test_list_field_alias_and_validation_alias_by_validation_alias():
client = TestClient(app)
resp = client.post(
"/list-field-alias-and-validation-alias",
files=[("files_val_alias", b"content1"), ("files_val_alias", b"content2")],
)
assert resp.status_code == 200
# Currently fails due to issue with aliases:
# AssertionError: assert 422 == 200
assert resp.json() == {"file_sizes": [8, 8]}
def test_list_field_alias_and_validation_alias_schema():
openapi = app.openapi()
body_schema = openapi["components"]["schemas"][
"Body_list_field_alias_and_validation_alias"
]
assert body_schema["properties"] == {
"files_val_alias": {
"items": {"type": "string", "format": "binary"},
"title": "Files Val Alias",
"type": "array",
},
}
# ------------------------------
# optional list field
@app.post(
"/optional-list-field-alias-and-validation-alias",
operation_id="optional_list_field_alias_and_validation_alias",
)
async def optional_list_field_alias_and_validation_alias(
files: Optional[list[bytes]] = File(
None, alias="files_alias", validation_alias="files_val_alias"
),
):
if files is None:
return {"file_sizes": None}
return {"file_sizes": [len(file) for file in files]}
def test_optional_list_field_alias_and_validation_alias_by_name():
client = TestClient(app)
resp = client.post(
"/optional-list-field-alias-and-validation-alias",
files=[("files", b"content1"), ("files", b"content2")],
)
assert resp.status_code == 200
assert resp.json() == {"file_sizes": None}
@pytest.mark.xfail(
reason="Optional[list[bytes]] File type causes TypeError in FastAPI",
raises=TypeError,
strict=False,
)
def test_optional_list_field_alias_and_validation_alias_by_alias():
client = TestClient(app)
resp = client.post(
"/optional-list-field-alias-and-validation-alias",
files=[("files_alias", b"content1"), ("files_alias", b"content2")],
)
# Currently fails due to some issue (likely unrelated to aliases) with:
# TypeError: issubclass() arg 1 must be a class
assert resp.status_code == 200
assert resp.json() == {"file_sizes": None}
# Will likely fail due to issue with aliases:
# AssertionError: assert {'file_sizes': [8, 8]} == {'file_sizes': None}
@pytest.mark.xfail(raises=AssertionError, strict=False)
def test_optional_list_field_alias_and_validation_alias_by_validation_alias():
client = TestClient(app)
resp = client.post(
"/optional-list-field-alias-and-validation-alias",
files=[("files_val_alias", b"content1"), ("files_val_alias", b"content2")],
)
assert resp.status_code == 200
assert resp.json() == {"file_sizes": [8, 8]}
# Currently fails due to issue with aliases:
# AssertionError: assert {'file_sizes': None} == {'file_sizes': [8, 8]}
def test_optional_list_field_alias_and_validation_alias_schema():
openapi = app.openapi()
body_schema = openapi["components"]["schemas"][
"Body_optional_list_field_alias_and_validation_alias"
]
assert body_schema["properties"] == {
"files_val_alias": {
"anyOf": [
{"items": {"type": "string", "format": "binary"}, "type": "array"},
{"type": "null"},
],
"title": "Files Val Alias",
},
}
# =====================================================================================
# File(alias=..., validation_alias=...) # alias == validation_alias
# The only working solution (current workaround)
# TODO: remove when issue with File field aliases is fixed
@app.post("/workaround", operation_id="workaround")
async def workaround(
file: bytes = File(alias="file_alias", validation_alias="file_alias"),
):
return {"file_size": len(file)}
def test_workaround_by_name():
client = TestClient(app)
resp = client.post("/workaround", files={"file": b"content"})
assert resp.status_code == 422
detail = resp.json()["detail"]
assert detail[0]["msg"] == IsOneOf(
"Field required",
"field required", # TODO: remove when deprecating Pydantic v1
)
assert "file_alias" in detail[0]["loc"]
def test_workaround_by_alias():
client = TestClient(app)
resp = client.post("/workaround", files={"file_alias": b"content"})
assert resp.status_code == 200
assert resp.json() == {"file_size": 7}
def test_workaround_schema():
openapi = app.openapi()
body_schema = openapi["components"]["schemas"]["Body_workaround"]
assert body_schema["properties"] == {
"file_alias": {"title": "File Alias", "type": "string", "format": "binary"}
}

570
tests/test_aliases/test_form_field_aliases_pd_v1.py

@ -1,15 +1,20 @@
from typing import Optional from typing import List, Optional
import pytest import pytest
from dirty_equals import IsOneOf
from fastapi import FastAPI, Form from fastapi import FastAPI, Form
from fastapi.testclient import TestClient from fastapi.testclient import TestClient
from ..utils import needs_pydanticv1
pytestmark = needs_pydanticv1
app = FastAPI() app = FastAPI()
# ===================================================================================== # =====================================================================================
# Form(alias=...) # Form(alias=...)
# Current situation: Works, but schema is wrong # Current situation: Works
# Schema generation for optional field and optional list fails due to issue likely not related to aliases
# ------------------------------ # ------------------------------
# required field # required field
@ -25,10 +30,7 @@ def test_required_field_alias_by_name():
resp = client.post("/required-field-alias", data={"param": "123"}) resp = client.post("/required-field-alias", data={"param": "123"})
assert resp.status_code == 422 assert resp.status_code == 422
detail = resp.json()["detail"] detail = resp.json()["detail"]
assert detail[0]["msg"] == IsOneOf( assert detail[0]["msg"] == "field required"
"Field required",
"field required", # TODO: remove when deprecating Pydantic v1
)
assert "param_alias" in detail[0]["loc"] assert "param_alias" in detail[0]["loc"]
@ -39,16 +41,12 @@ def test_required_field_alias_by_alias():
assert resp.json() == {"param": "123"} assert resp.json() == {"param": "123"}
@pytest.mark.xfail(raises=AssertionError, strict=False)
def test_required_field_alias_schema(): def test_required_field_alias_schema():
openapi = app.openapi() openapi = app.openapi()
body_schema = openapi["components"]["schemas"]["Body_required_field_alias"] body_schema = openapi["components"]["schemas"]["Body_required_field_alias"]
assert body_schema["properties"] == { assert body_schema["properties"] == {
"param_alias": {"title": "Param Alias", "type": "string"} "param_alias": {"title": "Param Alias", "type": "string"}
} }
# Currently fails due to issue with aliases:
# AssertionError: assert {'param': {'type': 'string', 'title': 'Param'}} ==
# {'param_alias': {'title': 'Param Alias', 'type': 'string'}}
# ------------------------------ # ------------------------------
@ -86,9 +84,9 @@ def test_optional_field_alias_schema():
"title": "Param Alias", "title": "Param Alias",
} }
} }
# Currently fails due to issue with aliases: # Fails with:
# AssertionError: assert # AssertionError: assert
# {'param': {'anyOf': [{'type': 'string'}, {'type': 'null'}], 'title': 'Param'}} == # {'param_alias': {'type': 'string', 'title': 'Param Alias'}} ==
# {'param_alias': {'anyOf': [{'type': 'string'}, {'type': 'null'}], 'title': 'Param Alias'}} # {'param_alias': {'anyOf': [{'type': 'string'}, {'type': 'null'}], 'title': 'Param Alias'}}
@ -97,7 +95,7 @@ def test_optional_field_alias_schema():
@app.post("/list-field-alias", operation_id="list_field_alias") @app.post("/list-field-alias", operation_id="list_field_alias")
async def list_field_alias(param: list[str] = Form(alias="param_alias")): async def list_field_alias(param: List[str] = Form(alias="param_alias")):
return {"param": param} return {"param": param}
@ -106,10 +104,7 @@ def test_list_field_alias_by_name():
resp = client.post("/list-field-alias", data={"param": ["123", "456"]}) resp = client.post("/list-field-alias", data={"param": ["123", "456"]})
assert resp.status_code == 422 assert resp.status_code == 422
detail = resp.json()["detail"] detail = resp.json()["detail"]
assert detail[0]["msg"] == IsOneOf( assert detail[0]["msg"] == "field required"
"Field required",
"field required", # TODO: remove when deprecating Pydantic v1
)
assert "param_alias" in detail[0]["loc"] assert "param_alias" in detail[0]["loc"]
@ -120,7 +115,6 @@ def test_list_field_alias_by_alias():
assert resp.json() == {"param": ["123", "456"]} assert resp.json() == {"param": ["123", "456"]}
@pytest.mark.xfail(raises=AssertionError, strict=False)
def test_list_field_alias_schema(): def test_list_field_alias_schema():
openapi = app.openapi() openapi = app.openapi()
body_schema = openapi["components"]["schemas"]["Body_list_field_alias"] body_schema = openapi["components"]["schemas"]["Body_list_field_alias"]
@ -131,10 +125,6 @@ def test_list_field_alias_schema():
"items": {"type": "string"}, "items": {"type": "string"},
} }
} }
# Currently fails due to issue with aliases:
# AssertionError: assert
# {'param': {'items': {'type': 'string'}, 'type': 'array', 'title': 'Param'}} ==
# {'param_alias': {'title': 'Param Alias', 'type': 'array', 'items': {'type': 'string'}}}
# ------------------------------ # ------------------------------
@ -143,7 +133,7 @@ def test_list_field_alias_schema():
@app.post("/optional-list-field-alias", operation_id="optional_list_field_alias") @app.post("/optional-list-field-alias", operation_id="optional_list_field_alias")
async def optional_list_field_alias( async def optional_list_field_alias(
param: Optional[list[str]] = Form(None, alias="param_alias"), param: Optional[List[str]] = Form(None, alias="param_alias"),
): ):
return {"param": param} return {"param": param}
@ -174,535 +164,7 @@ def test_optional_list_field_alias_schema():
"title": "Param Alias", "title": "Param Alias",
}, },
} }
# Currently fails due to issue with aliases: # Fails with:
# AssertionError: assert # AssertionError: assert
# {'param': {'anyOf': [{'items': {'type': 'string'}, 'type': 'array'}, {'type': 'null'}], 'title': 'Param'}} == # {'param_alias': {'items': {'type': 'string'}, 'type': 'array', 'title': 'Param Alias'}} ==
# {'param_alias': {'anyOf': [{'items': {'type': 'string'}, 'type': 'array'}, {'type': 'null'}], 'title': 'Param Alias'}} # {'param_alias': {'anyOf': [{'items': {'type': 'string'}, 'type': 'array'}, {'type': 'null'}], 'title': 'Param Alias'}}
# =====================================================================================
# Form(validation_alias=...)
# Current situation: schema is correct, but doesn't work (parameter name is used)
# ------------------------------
# required field
@app.post(
"/required-field-validation-alias", operation_id="required_field_validation_alias"
)
async def required_field_validation_alias(
param: str = Form(validation_alias="param_val_alias"),
):
return {"param": param}
@pytest.mark.xfail(raises=AssertionError, strict=False)
def test_required_field_validation_alias_by_name():
client = TestClient(app)
resp = client.post("/required-field-validation-alias", data={"param": "123"})
assert resp.status_code == 422
# Currently fails due to issue with aliases:
# AssertionError: assert 200 == 422
detail = resp.json()["detail"]
assert detail[0]["msg"] == IsOneOf(
"Field required",
"field required", # TODO: remove when deprecating Pydantic v1
)
assert "param_val_alias" in detail[0]["loc"]
@pytest.mark.xfail(raises=AssertionError, strict=False)
def test_required_field_validation_alias_by_alias():
client = TestClient(app)
resp = client.post(
"/required-field-validation-alias", data={"param_val_alias": "123"}
)
assert resp.status_code == 200
# Currently fails due to issue with aliases:
# AssertionError: assert 422 == 200
assert resp.json() == {"param": "123"}
def test_required_field_validation_alias_schema():
openapi = app.openapi()
body_schema = openapi["components"]["schemas"][
"Body_required_field_validation_alias"
]
assert body_schema["properties"] == {
"param_val_alias": {"title": "Param Val Alias", "type": "string"},
}
# ------------------------------
# optional field
@app.post(
"/optional-field-validation-alias", operation_id="optional_field_validation_alias"
)
async def optional_field_validation_alias(
param: Optional[str] = Form(None, validation_alias="param_val_alias"),
):
return {"param": param}
@pytest.mark.xfail(raises=AssertionError, strict=False)
def test_optional_field_validation_alias_by_name():
client = TestClient(app)
resp = client.post("/optional-field-validation-alias", data={"param": "123"})
assert resp.json() == {"param": None}
# Currently fails due to issue with aliases:
# AssertionError: assert {'param': '123'} == {'param': None}
@pytest.mark.xfail(raises=AssertionError, strict=False)
def test_optional_field_validation_alias_by_alias():
client = TestClient(app)
resp = client.post(
"/optional-field-validation-alias", data={"param_val_alias": "123"}
)
assert resp.status_code == 200
assert resp.json() == {"param": "123"}
# Currently fails due to issue with aliases:
# AssertionError: assert {'param': None} == {'param': '123'}
def test_optional_field_validation_alias_schema():
openapi = app.openapi()
body_schema = openapi["components"]["schemas"][
"Body_optional_field_validation_alias"
]
assert body_schema["properties"] == {
"param_val_alias": {
"anyOf": [{"type": "string"}, {"type": "null"}],
"title": "Param Val Alias",
},
}
# ------------------------------
# list field
@app.post("/list-field-validation-alias", operation_id="list_field_validation_alias")
async def list_field_validation_alias(
param: list[str] = Form(validation_alias="param_val_alias"),
):
return {"param": param}
@pytest.mark.xfail(raises=AssertionError, strict=False)
def test_list_field_validation_alias_by_name():
client = TestClient(app)
resp = client.post("/list-field-validation-alias", data={"param": ["123", "456"]})
assert resp.status_code == 422
# Currently fails due to issue with aliases:
# AssertionError: assert 200 == 422
detail = resp.json()["detail"]
assert detail[0]["msg"] == IsOneOf(
"Field required",
"field required", # TODO: remove when deprecating Pydantic v1
)
assert "param_val_alias" in detail[0]["loc"]
@pytest.mark.xfail(raises=AssertionError, strict=False)
def test_list_field_validation_alias_by_alias():
client = TestClient(app)
resp = client.post(
"/list-field-validation-alias", data={"param_val_alias": ["123", "456"]}
)
assert resp.status_code == 200
# Currently fails due to issue with aliases:
# AssertionError: assert 422 == 200
assert resp.json() == {"param": ["123", "456"]}
def test_list_field_validation_alias_schema():
openapi = app.openapi()
body_schema = openapi["components"]["schemas"]["Body_list_field_validation_alias"]
assert body_schema["properties"] == {
"param_val_alias": {
"items": {"type": "string"},
"title": "Param Val Alias",
"type": "array",
},
}
# ------------------------------
# optional list field
@app.post(
"/optional-list-field-validation-alias",
operation_id="optional_list_field_validation_alias",
)
async def optional_list_field_validation_alias(
param: Optional[list[str]] = Form(None, validation_alias="param_val_alias"),
):
return {"param": param}
@pytest.mark.xfail(raises=AssertionError, strict=False)
def test_optional_list_field_validation_alias_by_name():
client = TestClient(app)
resp = client.post(
"/optional-list-field-validation-alias", data={"param": ["123", "456"]}
)
assert resp.status_code == 200
assert resp.json() == {"param": None}
# Currently fails due to issue with aliases:
# AssertionError: assert {'param': ['123', '456']} == {'param': None}
@pytest.mark.xfail(raises=AssertionError, strict=False)
def test_optional_list_field_validation_alias_by_alias():
client = TestClient(app)
resp = client.post(
"/optional-list-field-validation-alias",
data={"param_val_alias": ["123", "456"]},
)
assert resp.status_code == 200
assert resp.json() == {"param": ["123", "456"]}
# Currently fails due to issue with aliases:
# AssertionError: assert {'param': None} == {'param': ['123', '456']}
def test_optional_list_field_validation_alias_schema():
openapi = app.openapi()
body_schema = openapi["components"]["schemas"][
"Body_optional_list_field_validation_alias"
]
assert body_schema["properties"] == {
"param_val_alias": {
"anyOf": [{"items": {"type": "string"}, "type": "array"}, {"type": "null"}],
"title": "Param Val Alias",
},
}
# =====================================================================================
# Form(alias=..., validation_alias=...)
# Current situation: Schema is correct (validation_alias), but doesn't work (alias is used)
# ------------------------------
# required field
@app.post(
"/required-field-alias-and-validation-alias",
operation_id="required_field_alias_and_validation_alias",
)
async def required_field_alias_and_validation_alias(
param: str = Form(alias="param_alias", validation_alias="param_val_alias"),
):
return {"param": param}
@pytest.mark.xfail(raises=AssertionError, strict=False)
def test_required_field_alias_and_validation_alias_by_name():
client = TestClient(app)
resp = client.post(
"/required-field-alias-and-validation-alias", data={"param": "123"}
)
assert resp.status_code == 422
detail = resp.json()["detail"]
assert detail[0]["msg"] == IsOneOf(
"Field required",
"field required", # TODO: remove when deprecating Pydantic v1
)
assert "param_val_alias" in detail[0]["loc"]
# Currently fails due to issue with aliases:
# AssertionError: assert 'param_val_alias' in ['body', 'param_alias']
@pytest.mark.xfail(raises=AssertionError, strict=False)
def test_required_field_alias_and_validation_alias_by_alias():
client = TestClient(app)
resp = client.post(
"/required-field-alias-and-validation-alias", data={"param_alias": "123"}
)
assert resp.status_code == 422
# Currently fails due to issue with aliases:
# AssertionError: assert 200 == 422
detail = resp.json()["detail"]
assert detail[0]["msg"] == IsOneOf(
"Field required",
"field required", # TODO: remove when deprecating Pydantic v1
)
assert "param_val_alias" in detail[0]["loc"]
@pytest.mark.xfail(raises=AssertionError, strict=False)
def test_required_field_alias_and_validation_alias_by_validation_alias():
client = TestClient(app)
resp = client.post(
"/required-field-alias-and-validation-alias", data={"param_val_alias": "123"}
)
assert resp.status_code == 200
# Currently fails due to issue with aliases:
# AssertionError: assert 422 == 200
assert resp.json() == {"param": "123"}
def test_required_field_alias_and_validation_alias_schema():
openapi = app.openapi()
body_schema = openapi["components"]["schemas"][
"Body_required_field_alias_and_validation_alias"
]
assert body_schema["properties"] == {
"param_val_alias": {"title": "Param Val Alias", "type": "string"}
}
# ------------------------------
# optional field
@app.post(
"/optional-field-alias-and-validation-alias",
operation_id="optional_field_alias_and_validation_alias",
)
async def optional_field_alias_and_validation_alias(
param: Optional[str] = Form(
None, alias="param_alias", validation_alias="param_val_alias"
),
):
return {"param": param}
def test_optional_field_alias_and_validation_alias_by_name():
client = TestClient(app)
resp = client.post(
"/optional-field-alias-and-validation-alias", data={"param": "123"}
)
assert resp.status_code == 200
assert resp.json() == {"param": None}
@pytest.mark.xfail(raises=AssertionError, strict=False)
def test_optional_field_alias_and_validation_alias_by_alias():
client = TestClient(app)
resp = client.post(
"/optional-field-alias-and-validation-alias", data={"param_alias": "123"}
)
assert resp.status_code == 200
assert resp.json() == {"param": None}
# Currently fails due to issue with aliases:
# AssertionError: assert {'param': '123'} == {'param': None}
@pytest.mark.xfail(raises=AssertionError, strict=False)
def test_optional_field_alias_and_validation_alias_by_validation_alias():
client = TestClient(app)
resp = client.post(
"/optional-field-alias-and-validation-alias", data={"param_val_alias": "123"}
)
assert resp.status_code == 200
assert resp.json() == {"param": "123"}
# Currently fails due to issue with aliases:
# AssertionError: assert {'param': None} == {'param': '123'}
def test_optional_field_alias_and_validation_alias_schema():
openapi = app.openapi()
body_schema = openapi["components"]["schemas"][
"Body_optional_field_alias_and_validation_alias"
]
assert body_schema["properties"] == {
"param_val_alias": {
"anyOf": [{"type": "string"}, {"type": "null"}],
"title": "Param Val Alias",
}
}
# ------------------------------
# list field
@app.post(
"/list-field-alias-and-validation-alias",
operation_id="list_field_alias_and_validation_alias",
)
async def list_field_alias_and_validation_alias(
param: list[str] = Form(alias="param_alias", validation_alias="param_val_alias"),
):
return {"param": param}
@pytest.mark.xfail(raises=AssertionError, strict=False)
def test_list_field_alias_and_validation_alias_by_name():
client = TestClient(app)
resp = client.post(
"/list-field-alias-and-validation-alias", data={"param": ["123", "456"]}
)
assert resp.status_code == 422
detail = resp.json()["detail"]
assert detail[0]["msg"] == IsOneOf(
"Field required",
"field required", # TODO: remove when deprecating Pydantic v1
)
assert "param_val_alias" in detail[0]["loc"]
# Currently fails due to issue with aliases:
# AssertionError: assert 'param_val_alias' in ['body', 'param_alias']
@pytest.mark.xfail(raises=AssertionError, strict=False)
def test_list_field_alias_and_validation_alias_by_alias():
client = TestClient(app)
resp = client.post(
"/list-field-alias-and-validation-alias", data={"param_alias": ["123", "456"]}
)
assert resp.status_code == 422
# Currently fails due to issue with aliases:
# AssertionError: assert 200 == 422
detail = resp.json()["detail"]
assert detail[0]["msg"] == IsOneOf(
"Field required",
"field required", # TODO: remove when deprecating Pydantic v1
)
assert "param_val_alias" in detail[0]["loc"]
@pytest.mark.xfail(raises=AssertionError, strict=False)
def test_list_field_alias_and_validation_alias_by_validation_alias():
client = TestClient(app)
resp = client.post(
"/list-field-alias-and-validation-alias",
data={"param_val_alias": ["123", "456"]},
)
assert resp.status_code == 200
# Currently fails due to issue with aliases:
# AssertionError: assert 422 == 200
assert resp.json() == {"param": ["123", "456"]}
def test_list_field_alias_and_validation_alias_schema():
openapi = app.openapi()
body_schema = openapi["components"]["schemas"][
"Body_list_field_alias_and_validation_alias"
]
assert body_schema["properties"] == {
"param_val_alias": {
"items": {"type": "string"},
"title": "Param Val Alias",
"type": "array",
},
}
# ------------------------------
# optional list field
@app.post(
"/optional-list-field-alias-and-validation-alias",
operation_id="optional_list_field_alias_and_validation_alias",
)
async def optional_list_field_alias_and_validation_alias(
param: Optional[list[str]] = Form(
None, alias="param_alias", validation_alias="param_val_alias"
),
):
return {"param": param}
def test_optional_list_field_alias_and_validation_alias_by_name():
client = TestClient(app)
resp = client.post(
"/optional-list-field-alias-and-validation-alias",
data={"param": ["123", "456"]},
)
assert resp.status_code == 200
assert resp.json() == {"param": None}
@pytest.mark.xfail(raises=AssertionError, strict=False)
def test_optional_list_field_alias_and_validation_alias_by_alias():
client = TestClient(app)
resp = client.post(
"/optional-list-field-alias-and-validation-alias",
data={"param_alias": ["123", "456"]},
)
assert resp.status_code == 200
assert resp.json() == {"param": None}
# Currently fails due to issue with aliases:
# AssertionError: assert {'param': ['123', '456']} == {'param': None}
@pytest.mark.xfail(raises=AssertionError, strict=False)
def test_optional_list_field_alias_and_validation_alias_by_validation_alias():
client = TestClient(app)
resp = client.post(
"/optional-list-field-alias-and-validation-alias",
data={"param_val_alias": ["123", "456"]},
)
assert resp.status_code == 200
assert resp.json() == {"param": ["123", "456"]}
# Currently fails due to issue with aliases:
# AssertionError: assert {'param': None} == {'param': ['123', '456']}
def test_optional_list_field_alias_and_validation_alias_schema():
openapi = app.openapi()
body_schema = openapi["components"]["schemas"][
"Body_optional_list_field_alias_and_validation_alias"
]
assert body_schema["properties"] == {
"param_val_alias": {
"anyOf": [{"items": {"type": "string"}, "type": "array"}, {"type": "null"}],
"title": "Param Val Alias",
},
}
# =====================================================================================
# Form(alias=..., validation_alias=...) # alias == validation_alias
# The only working solution (current workaround)
# TODO: remove when issue with Form field aliases is fixed
@app.post("/workaround", operation_id="workaround")
async def workaround(
param: str = Form(alias="param_alias", validation_alias="param_alias"),
):
return {"param": param}
def test_workaround_by_name():
client = TestClient(app)
resp = client.post("/workaround", data={"param": "123"})
assert resp.status_code == 422
detail = resp.json()["detail"]
assert detail[0]["msg"] == IsOneOf(
"Field required",
"field required", # TODO: remove when deprecating Pydantic v1
)
assert "param_alias" in detail[0]["loc"]
def test_workaround_by_alias():
client = TestClient(app)
resp = client.post("/workaround", data={"param_alias": "123"})
assert resp.status_code == 200
assert resp.json() == {"param": "123"}
def test_workaround_schema():
openapi = app.openapi()
body_schema = openapi["components"]["schemas"]["Body_workaround"]
assert body_schema["properties"] == {
"param_alias": {"title": "Param Alias", "type": "string"}
}

519
tests/test_aliases/test_form_model_aliases_pd_v1.py

@ -1,16 +1,20 @@
from typing import Annotated, Optional from typing import Annotated, List, Optional
import pytest import pytest
from dirty_equals import IsOneOf
from fastapi import FastAPI, Form from fastapi import FastAPI, Form
from fastapi.testclient import TestClient from fastapi.testclient import TestClient
from pydantic import BaseModel, Field from pydantic import BaseModel, Field
from ..utils import needs_pydanticv1
pytestmark = needs_pydanticv1
app = FastAPI() app = FastAPI()
# ===================================================================================== # =====================================================================================
# Field(alias=...) # Field(alias=...)
# Current situation: fully works # Current situation: works
# Schema generation for optional field and optional list fails due to issue likely not related to aliases
# ------------------------------ # ------------------------------
# required field # required field
@ -30,10 +34,7 @@ def test_required_field_alias_model_by_name():
resp = client.post("/required-field-alias-model", data={"param": "123"}) resp = client.post("/required-field-alias-model", data={"param": "123"})
assert resp.status_code == 422 assert resp.status_code == 422
detail = resp.json()["detail"] detail = resp.json()["detail"]
assert detail[0]["msg"] == IsOneOf( assert detail[0]["msg"] == "field required"
"Field required",
"field required", # TODO: remove when deprecating Pydantic v1
)
assert "param_alias" in detail[0]["loc"] assert "param_alias" in detail[0]["loc"]
@ -79,6 +80,7 @@ def test_optional_field_alias_model_by_alias():
assert resp.json() == {"param": "123"} assert resp.json() == {"param": "123"}
@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"]
@ -88,6 +90,10 @@ def test_optional_field_alias_model_schema():
"title": "Param Alias", "title": "Param Alias",
}, },
} }
# Fails with:
# AssertionError: assert
# {'param_alias': {'type': 'string', 'title': 'Param Alias'}} ==
# {'param_alias': {'anyOf': [{'type': 'string'}, {'type': 'null'}], 'title': 'Param Alias'}}
# ------------------------------ # ------------------------------
@ -95,7 +101,7 @@ def test_optional_field_alias_model_schema():
class ListFieldAliasModel(BaseModel): class ListFieldAliasModel(BaseModel):
param: list[str] = Field(alias="param_alias") param: List[str] = Field(alias="param_alias")
@app.post("/list-field-alias-model") @app.post("/list-field-alias-model")
@ -108,10 +114,7 @@ def test_list_field_alias_model_by_name():
resp = client.post("/list-field-alias-model", data={"param": ["123", "456"]}) resp = client.post("/list-field-alias-model", data={"param": ["123", "456"]})
assert resp.status_code == 422 assert resp.status_code == 422
detail = resp.json()["detail"] detail = resp.json()["detail"]
assert detail[0]["msg"] == IsOneOf( assert detail[0]["msg"] == "field required"
"Field required",
"field required", # TODO: remove when deprecating Pydantic v1
)
assert "param_alias" in detail[0]["loc"] assert "param_alias" in detail[0]["loc"]
@ -139,7 +142,7 @@ def test_list_field_alias_model_schema():
class OptionalListFieldAliasModel(BaseModel): class OptionalListFieldAliasModel(BaseModel):
param: Optional[list[str]] = Field(None, alias="param_alias") param: Optional[List[str]] = Field(None, alias="param_alias")
@app.post("/optional-list-field-alias-model") @app.post("/optional-list-field-alias-model")
@ -167,493 +170,17 @@ def test_optional_list_field_alias_model_by_alias():
assert resp.json() == {"param": ["123", "456"]} assert resp.json() == {"param": ["123", "456"]}
@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"]
assert len(body_schema["properties"]) == 1
assert "param_alias" in body_schema["properties"]
# =====================================================================================
# Field(validation_alias=...)
# Current situation: works except lists
# ------------------------------
# required field
class RequiredFieldValidationAliasModel(BaseModel):
param: str = Field(validation_alias="param_val_alias")
@app.post(
"/required-field-validation-alias-model",
operation_id="required_field_validation_alias_model",
)
async def required_field_validation_alias_model(
data: Annotated[RequiredFieldValidationAliasModel, Form()],
):
return {"param": data.param}
def test_required_field_validation_alias_model_by_name():
client = TestClient(app)
resp = client.post("/required-field-validation-alias-model", data={"param": "123"})
assert resp.status_code == 422
detail = resp.json()["detail"]
assert detail[0]["msg"] == IsOneOf(
"Field required",
"field required", # TODO: remove when deprecating Pydantic v1
)
assert "param_val_alias" in detail[0]["loc"]
def test_required_field_validation_alias_model_by_alias():
client = TestClient(app)
resp = client.post(
"/required-field-validation-alias-model", data={"param_val_alias": "123"}
)
assert resp.status_code == 200
assert resp.json() == {"param": "123"}
def test_required_field_validation_alias_model_schema():
openapi = app.openapi()
body_schema = openapi["components"]["schemas"]["RequiredFieldValidationAliasModel"]
assert body_schema["properties"] == { assert body_schema["properties"] == {
"param_val_alias": {"title": "Param Val Alias", "type": "string"} "param_alias": {
}
# ------------------------------
# optional field
class OptionalFieldValidationAliasModel(BaseModel):
param: Optional[str] = Field(None, validation_alias="param_val_alias")
@app.post(
"/optional-field-validation-alias-model",
operation_id="optional_field_validation_alias_model",
)
async def optional_field_validation_alias_model(
data: Annotated[OptionalFieldValidationAliasModel, Form()],
):
return {"param": data.param}
def test_optional_field_validation_alias_model_by_name():
client = TestClient(app)
resp = client.post("/optional-field-validation-alias-model", data={"param": "123"})
assert resp.status_code == 200
assert resp.json() == {"param": None}
def test_optional_field_validation_alias_model_by_alias():
client = TestClient(app)
resp = client.post(
"/optional-field-validation-alias-model", data={"param_val_alias": "123"}
)
assert resp.status_code == 200
assert resp.json() == {"param": "123"}
def test_optional_field_validation_alias_model_schema():
openapi = app.openapi()
body_schema = openapi["components"]["schemas"]["OptionalFieldValidationAliasModel"]
assert body_schema["properties"] == {
"param_val_alias": {
"anyOf": [{"type": "string"}, {"type": "null"}],
"title": "Param Val Alias",
},
}
# ------------------------------
# list field
class ListFieldValidationAliasModel(BaseModel):
param: list[str] = Field(validation_alias="param_val_alias")
@app.post(
"/list-field-validation-alias-model",
operation_id="list_field_validation_alias_model",
)
async def list_field_validation_alias_model(
data: Annotated[ListFieldValidationAliasModel, Form()],
):
return {"param": data.param}
def test_list_field_validation_alias_model_by_name():
client = TestClient(app)
resp = client.post(
"/list-field-validation-alias-model", data={"param": ["123", "456"]}
)
assert resp.status_code == 422
detail = resp.json()["detail"]
assert detail[0]["msg"] == IsOneOf(
"Field required",
"field required", # TODO: remove when deprecating Pydantic v1
)
assert "param_val_alias" in detail[0]["loc"]
@pytest.mark.xfail(raises=AssertionError, strict=False)
def test_list_field_validation_alias_model_by_alias():
client = TestClient(app)
resp = client.post(
"/list-field-validation-alias-model", data={"param_val_alias": ["123", "456"]}
)
assert resp.status_code == 200, resp.text
# Currently fails due to some issue:
# AssertionError: assert 422 == 200
# {"detail":[{"type":"list_type","loc":["body","param_val_alias"],"msg":"Input should be a valid list","input":"456"}]}
assert resp.json() == {"param": ["123", "456"]}
def test_list_field_validation_alias_model_schema():
openapi = app.openapi()
body_schema = openapi["components"]["schemas"]["ListFieldValidationAliasModel"]
assert body_schema["properties"] == {
"param_val_alias": {
"items": {"type": "string"},
"title": "Param Val Alias",
"type": "array",
}
}
# ------------------------------
# optional list field
class OptionalListFieldValidationAliasModel(BaseModel):
param: Optional[list[str]] = Field(None, validation_alias="param_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(
data: Annotated[OptionalListFieldValidationAliasModel, Form()],
):
return {"param": data.param}
def test_optional_list_field_validation_alias_model_by_name():
client = TestClient(app)
resp = client.post(
"/optional-list-field-validation-alias-model", data={"param": ["123", "456"]}
)
assert resp.status_code == 200
assert resp.json() == {"param": None}
@pytest.mark.xfail(raises=AssertionError, strict=False)
def test_optional_list_field_validation_alias_model_by_alias():
client = TestClient(app)
resp = client.post(
"/optional-list-field-validation-alias-model",
data={"param_val_alias": ["123", "456"]},
)
assert resp.status_code == 200, resp.text
# Currently fails due to some issue:
# AssertionError: assert 422 == 200
# {"detail":[{"type":"list_type","loc":["body","param_val_alias"],"msg":"Input should be a valid list","input":"456"}]}
assert resp.json() == {"param": ["123", "456"]}
def test_optional_list_field_validation_alias_model_schema():
openapi = app.openapi()
body_schema = openapi["components"]["schemas"][
"OptionalListFieldValidationAliasModel"
]
assert body_schema["properties"] == {
"param_val_alias": {
"anyOf": [{"items": {"type": "string"}, "type": "array"}, {"type": "null"}],
"title": "Param Val Alias",
},
}
# =====================================================================================
# Field(alias=..., validation_alias=...)
# Current situation: works except lists
# ------------------------------
# required field
class RequiredFieldAliasAndValidationAliasModel(BaseModel):
param: str = Field(alias="param_alias", validation_alias="param_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(
data: Annotated[RequiredFieldAliasAndValidationAliasModel, Form()],
):
return {"param": data.param}
def test_required_field_alias_and_validation_alias_model_by_name():
client = TestClient(app)
resp = client.post(
"/required-field-alias-and-validation-alias-model", data={"param": "123"}
)
assert resp.status_code == 422
detail = resp.json()["detail"]
assert detail[0]["msg"] == IsOneOf(
"Field required",
"field required", # TODO: remove when deprecating Pydantic v1
)
assert "param_val_alias" in detail[0]["loc"]
def test_required_field_alias_and_validation_alias_model_by_alias():
client = TestClient(app)
resp = client.post(
"/required-field-alias-and-validation-alias-model", data={"param_alias": "123"}
)
assert resp.status_code == 422
detail = resp.json()["detail"]
assert detail[0]["msg"] == IsOneOf(
"Field required",
"field required", # TODO: remove when deprecating Pydantic v1
)
assert "param_val_alias" in detail[0]["loc"]
def test_required_field_alias_and_validation_alias_model_by_validation_alias():
client = TestClient(app)
resp = client.post(
"/required-field-alias-and-validation-alias-model",
data={"param_val_alias": "123"},
)
assert resp.status_code == 200
assert resp.json() == {"param": "123"}
def test_required_field_alias_and_validation_alias_model_schema():
openapi = app.openapi()
body_schema = openapi["components"]["schemas"][
"RequiredFieldAliasAndValidationAliasModel"
]
assert body_schema["properties"] == {
"param_val_alias": {"title": "Param Val Alias", "type": "string"}
}
# ------------------------------
# optional field
class OptionalFieldAliasAndValidationAliasModel(BaseModel):
param: Optional[str] = Field(
None, alias="param_alias", validation_alias="param_val_alias"
)
@app.post(
"/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(
data: Annotated[OptionalFieldAliasAndValidationAliasModel, Form()],
):
return {"param": data.param}
def test_optional_field_alias_and_validation_alias_model_by_name():
client = TestClient(app)
resp = client.post(
"/optional-field-alias-and-validation-alias-model", data={"param": "123"}
)
assert resp.status_code == 200
assert resp.json() == {"param": None}
def test_optional_field_alias_and_validation_alias_model_by_alias():
client = TestClient(app)
resp = client.post(
"/optional-field-alias-and-validation-alias-model", data={"param_alias": "123"}
)
assert resp.status_code == 200
assert resp.json() == {"param": None}
def test_optional_field_alias_and_validation_alias_model_by_validation_alias():
client = TestClient(app)
resp = client.post(
"/optional-field-alias-and-validation-alias-model",
data={"param_val_alias": "123"},
)
assert resp.status_code == 200
assert resp.json() == {"param": "123"}
def test_optional_field_alias_and_validation_alias_model_schema():
openapi = app.openapi()
body_schema = openapi["components"]["schemas"][
"OptionalFieldAliasAndValidationAliasModel"
]
assert body_schema["properties"] == {
"param_val_alias": {
"anyOf": [{"type": "string"}, {"type": "null"}],
"title": "Param Val Alias",
}
}
# ------------------------------
# list field
class ListFieldAliasAndValidationAliasModel(BaseModel):
param: list[str] = Field(alias="param_alias", validation_alias="param_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(
data: Annotated[ListFieldAliasAndValidationAliasModel, Form()],
):
return {"param": data.param}
def test_list_field_alias_and_validation_alias_model_by_name():
client = TestClient(app)
resp = client.post(
"/list-field-alias-and-validation-alias-model", data={"param": ["123", "456"]}
)
assert resp.status_code == 422
detail = resp.json()["detail"]
assert detail[0]["msg"] == IsOneOf(
"Field required",
"field required", # TODO: remove when deprecating Pydantic v1
)
assert "param_val_alias" in detail[0]["loc"]
def test_list_field_alias_and_validation_alias_model_by_alias():
client = TestClient(app)
resp = client.post(
"/list-field-alias-and-validation-alias-model",
data={"param_alias": ["123", "456"]},
)
assert resp.status_code == 422
detail = resp.json()["detail"]
assert detail[0]["msg"] == IsOneOf(
"Field required",
"field required", # TODO: remove when deprecating Pydantic v1
)
assert "param_val_alias" in detail[0]["loc"]
@pytest.mark.xfail(raises=AssertionError, strict=False)
def test_list_field_alias_and_validation_alias_model_by_validation_alias():
client = TestClient(app)
resp = client.post(
"/list-field-alias-and-validation-alias-model",
data={"param_val_alias": ["123", "456"]},
)
assert resp.status_code == 200, resp.text
# Currently fails due to some issue:
# AssertionError: assert 422 == 200
# {"detail":[{"type":"list_type","loc":["body","param_val_alias"],"msg":"Input should be a valid list","input":"456"}]}
assert resp.json() == {"param": ["123", "456"]}
def test_list_field_alias_and_validation_alias_model_schema():
openapi = app.openapi()
body_schema = openapi["components"]["schemas"][
"ListFieldAliasAndValidationAliasModel"
]
assert body_schema["properties"] == {
"param_val_alias": {
"items": {"type": "string"},
"title": "Param Val Alias",
"type": "array",
}
}
# ------------------------------
# optional list field
class OptionalListFieldAliasAndValidationAliasModel(BaseModel):
param: Optional[list[str]] = Field(
None, alias="param_alias", validation_alias="param_val_alias"
)
@app.post(
"/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(
data: Annotated[OptionalListFieldAliasAndValidationAliasModel, Form()],
):
return {"param": data.param}
def test_optional_list_field_alias_and_validation_alias_model_by_name():
client = TestClient(app)
resp = client.post(
"/optional-list-field-alias-and-validation-alias-model",
data={"param": ["123", "456"]},
)
assert resp.status_code == 200, resp.text
assert resp.json() == {"param": None}
def test_optional_list_field_alias_and_validation_alias_model_by_alias():
client = TestClient(app)
resp = client.post(
"/optional-list-field-alias-and-validation-alias-model",
data={"param_alias": ["123", "456"]},
)
assert resp.status_code == 200, resp.text
assert resp.json() == {"param": None}
@pytest.mark.xfail(raises=AssertionError, strict=False)
def test_optional_list_field_alias_and_validation_alias_model_by_validation_alias():
client = TestClient(app)
resp = client.post(
"/optional-list-field-alias-and-validation-alias-model",
data={"param_val_alias": ["123", "456"]},
)
assert resp.status_code == 200, resp.text
# Currently fails due to some issue:
# AssertionError: assert 422 == 200
# {"detail":[{"type":"list_type","loc":["body","param_val_alias"],"msg":"Input should be a valid list","input":"456"}]}
assert resp.json() == {"param": ["123", "456"]}
def test_optional_list_field_alias_and_validation_alias_model_schema():
openapi = app.openapi()
body_schema = openapi["components"]["schemas"][
"OptionalListFieldAliasAndValidationAliasModel"
]
assert body_schema["properties"] == {
"param_val_alias": {
"anyOf": [{"items": {"type": "string"}, "type": "array"}, {"type": "null"}], "anyOf": [{"items": {"type": "string"}, "type": "array"}, {"type": "null"}],
"title": "Param Val Alias", "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'}}

Loading…
Cancel
Save