Compare commits

...

6 Commits

Author SHA1 Message Date
Sebastián Ramírez
b1d9769f97 🔖 Release version 0.124.4 2025-12-12 15:59:12 +01:00
github-actions[bot]
89157a803c 📝 Update release notes
[skip ci]
2025-12-12 14:57:20 +00:00
Motov Yurii
d86c47477e 🐛 Fix parameter aliases (#14371)
Co-authored-by: Sebastián Ramírez <tiangolo@gmail.com>
2025-12-12 15:56:57 +01:00
Sebastián Ramírez
3fe6522aae 🔖 Release version 0.124.3 2025-12-12 15:32:58 +01:00
github-actions[bot]
80d1f732e5 📝 Update release notes
[skip ci]
2025-12-12 14:31:45 +00:00
Sebastián Ramírez
c0556ac3a5 🐛 Fix support for tagged union with discriminator inside of Annotated with Body() (#14512) 2025-12-12 15:31:21 +01:00
30 changed files with 623 additions and 1094 deletions

View File

@@ -7,6 +7,18 @@ hide:
## Latest Changes
## 0.124.4
### Fixes
* 🐛 Fix parameter aliases. PR [#14371](https://github.com/fastapi/fastapi/pull/14371) by [@YuriiMotov](https://github.com/YuriiMotov).
## 0.124.3
### Fixes
* 🐛 Fix support for tagged union with discriminator inside of `Annotated` with `Body()`. PR [#14512](https://github.com/fastapi/fastapi/pull/14512) by [@tiangolo](https://github.com/tiangolo).
### Refactors
* ✅ Add set of tests for request parameters and alias. PR [#14358](https://github.com/fastapi/fastapi/pull/14358) by [@YuriiMotov](https://github.com/YuriiMotov).

View File

@@ -1,6 +1,6 @@
"""FastAPI framework, high performance, easy to learn, fast to code, ready for production"""
__version__ = "0.124.2"
__version__ = "0.124.4"
from starlette import status as status

View File

@@ -18,7 +18,7 @@ from typing import (
from fastapi._compat import may_v1, shared
from fastapi.openapi.constants import REF_TEMPLATE
from fastapi.types import IncEx, ModelNameMap, UnionType
from pydantic import BaseModel, ConfigDict, TypeAdapter, create_model
from pydantic import BaseModel, ConfigDict, Field, TypeAdapter, create_model
from pydantic import PydanticSchemaGenerationError as PydanticSchemaGenerationError
from pydantic import PydanticUndefinedAnnotation as PydanticUndefinedAnnotation
from pydantic import ValidationError as ValidationError
@@ -50,6 +50,45 @@ UndefinedType = PydanticUndefinedType
evaluate_forwardref = eval_type_lenient
Validator = Any
# TODO: remove when dropping support for Pydantic < v2.12.3
_Attrs = {
"default": ...,
"default_factory": None,
"alias": None,
"alias_priority": None,
"validation_alias": None,
"serialization_alias": None,
"title": None,
"field_title_generator": None,
"description": None,
"examples": None,
"exclude": None,
"exclude_if": None,
"discriminator": None,
"deprecated": None,
"json_schema_extra": None,
"frozen": None,
"validate_default": None,
"repr": True,
"init": None,
"init_var": None,
"kw_only": None,
}
# TODO: remove when dropping support for Pydantic < v2.12.3
def asdict(field_info: FieldInfo) -> Dict[str, Any]:
attributes = {}
for attr in _Attrs:
value = getattr(field_info, attr, Undefined)
if value is not Undefined:
attributes[attr] = value
return {
"annotation": field_info.annotation,
"metadata": field_info.metadata,
"attributes": attributes,
}
class BaseConfig:
pass
@@ -71,6 +110,18 @@ class ModelField:
a = self.field_info.alias
return a if a is not None else self.name
@property
def validation_alias(self) -> Union[str, None]:
va = self.field_info.validation_alias
if isinstance(va, str) and va:
return va
return None
@property
def serialization_alias(self) -> Union[str, None]:
sa = self.field_info.serialization_alias
return sa or None
@property
def required(self) -> bool:
return self.field_info.is_required()
@@ -95,10 +146,15 @@ class ModelField:
warnings.simplefilter(
"ignore", category=UnsupportedFieldAttributeWarning
)
# TODO: remove after dropping support for Python 3.8 and
# setting the min Pydantic to v2.12.3 that adds asdict()
field_dict = asdict(self.field_info)
annotated_args = (
self.field_info.annotation,
*self.field_info.metadata,
self.field_info,
field_dict["annotation"],
*field_dict["metadata"],
# this FieldInfo needs to be created again so that it doesn't include
# the old field info metadata and only the rest of the attributes
Field(**field_dict["attributes"]),
)
self._type_adapter: TypeAdapter[Any] = TypeAdapter(
Annotated[annotated_args],
@@ -199,12 +255,18 @@ def get_schema_from_model_field(
if (separate_input_output_schemas or _has_computed_fields(field))
else "validation"
)
field_alias = (
(field.validation_alias or field.alias)
if field.mode == "validation"
else (field.serialization_alias or field.alias)
)
# This expects that GenerateJsonSchema was already used to generate the definitions
json_schema = field_mapping[(field, override_mode or field.mode)]
if "$ref" not in json_schema:
# TODO remove when deprecating Pydantic v1
# Ref: https://github.com/pydantic/pydantic/blob/d61792cc42c80b13b23e3ffa74bc37ec7c77f7d1/pydantic/schema.py#L207
json_schema["title"] = field.field_info.title or field.alias.title().replace(
json_schema["title"] = field.field_info.title or field_alias.title().replace(
"_", " "
)
return json_schema

View File

@@ -752,7 +752,7 @@ def _validate_value_with_model_field(
def _get_multidict_value(
field: ModelField, values: Mapping[str, Any], alias: Union[str, None] = None
) -> Any:
alias = alias or field.alias
alias = alias or get_validation_alias(field)
if is_sequence_field(field) and isinstance(values, (ImmutableMultiDict, Headers)):
value = values.getlist(alias)
else:
@@ -809,15 +809,13 @@ def request_params_to_args(
field.field_info, "convert_underscores", default_convert_underscores
)
if convert_underscores:
alias = (
field.alias
if field.alias != field.name
else field.name.replace("_", "-")
)
alias = get_validation_alias(field)
if alias == field.name:
alias = alias.replace("_", "-")
value = _get_multidict_value(field, received_params, alias=alias)
if value is not None:
params_to_process[field.alias] = value
processed_keys.add(alias or field.alias)
params_to_process[get_validation_alias(field)] = value
processed_keys.add(alias or get_validation_alias(field))
for key in received_params.keys():
if key not in processed_keys:
@@ -847,7 +845,7 @@ def request_params_to_args(
assert isinstance(field_info, (params.Param, temp_pydantic_v1_params.Param)), (
"Params must be subclasses of Param"
)
loc = (field_info.in_.value, field.alias)
loc = (field_info.in_.value, get_validation_alias(field))
v_, errors_ = _validate_value_with_model_field(
field=field, value=value, values=values, loc=loc
)
@@ -936,8 +934,8 @@ async def _extract_form_body(
tg.start_soon(process_fn, sub_value.read)
value = serialize_sequence_value(field=field, value=results)
if value is not None:
values[field.alias] = value
field_aliases = {field.alias for field in body_fields}
values[get_validation_alias(field)] = value
field_aliases = {get_validation_alias(field) for field in body_fields}
for key in received_body.keys():
if key not in field_aliases:
param_values = received_body.getlist(key)
@@ -979,11 +977,11 @@ async def request_body_to_args(
)
return {first_field.name: v_}, errors_
for field in body_fields:
loc = ("body", field.alias)
loc = ("body", get_validation_alias(field))
value: Optional[Any] = None
if body_to_process is not None:
try:
value = body_to_process.get(field.alias)
value = body_to_process.get(get_validation_alias(field))
# If the received body is a list, not a dict
except AttributeError:
errors.append(get_missing_field_error(loc))
@@ -1062,3 +1060,8 @@ def get_body_field(
field_info=BodyFieldInfo(**BodyFieldInfo_kwargs),
)
return final_field
def get_validation_alias(field: ModelField) -> str:
va = getattr(field, "validation_alias", None)
return va or field.alias

View File

@@ -19,6 +19,7 @@ from fastapi.dependencies.utils import (
_get_flat_fields_from_params,
get_flat_dependant,
get_flat_params,
get_validation_alias,
)
from fastapi.encoders import jsonable_encoder
from fastapi.openapi.constants import METHODS_WITH_BODY, REF_PREFIX
@@ -141,7 +142,7 @@ def _get_openapi_operation_parameters(
field_mapping=field_mapping,
separate_input_output_schemas=separate_input_output_schemas,
)
name = param.alias
name = get_validation_alias(param)
convert_underscores = getattr(
param.field_info,
"convert_underscores",
@@ -149,7 +150,7 @@ def _get_openapi_operation_parameters(
)
if (
param_type == ParamTypes.header
and param.alias == param.name
and name == param.name
and convert_underscores
):
name = param.name.replace("_", "-")

View File

@@ -115,6 +115,10 @@ class Param(FieldInfo): # type: ignore[misc]
else:
kwargs["deprecated"] = deprecated
if PYDANTIC_V2:
if serialization_alias in (_Unset, None) and isinstance(alias, str):
serialization_alias = alias
if validation_alias in (_Unset, None):
validation_alias = alias
kwargs.update(
{
"annotation": annotation,
@@ -571,6 +575,10 @@ class Body(FieldInfo): # type: ignore[misc]
else:
kwargs["deprecated"] = deprecated
if PYDANTIC_V2:
if serialization_alias in (_Unset, None) and isinstance(alias, str):
serialization_alias = alias
if validation_alias in (_Unset, None):
validation_alias = alias
kwargs.update(
{
"annotation": annotation,

View File

@@ -3,7 +3,6 @@ from typing import List, Union
import pytest
from dirty_equals import IsDict, IsOneOf, IsPartialDict
from fastapi import Body, FastAPI
from fastapi._compat import PYDANTIC_V2
from fastapi.testclient import TestClient
from pydantic import BaseModel, Field
from typing_extensions import Annotated
@@ -115,21 +114,13 @@ class BodyModelRequiredListAlias(BaseModel):
@app.post("/model-required-list-alias", operation_id="model_required_list_alias")
async def read_model_required_list_alias(p: BodyModelRequiredListAlias):
return {"p": p.p} # pragma: no cover
return {"p": p.p}
@pytest.mark.parametrize(
"path",
[
pytest.param(
"/required-list-alias",
marks=pytest.mark.xfail(
raises=AssertionError,
condition=PYDANTIC_V2,
reason="Fails only with PDv2 models",
strict=False,
),
),
"/required-list-alias",
"/model-required-list-alias",
],
)
@@ -253,7 +244,7 @@ class BodyModelRequiredListValidationAlias(BaseModel):
async def read_model_required_list_validation_alias(
p: BodyModelRequiredListValidationAlias,
):
return {"p": p.p} # pragma: no cover
return {"p": p.p}
@needs_pydanticv2
@@ -284,10 +275,7 @@ def test_required_list_validation_alias_schema(path: str):
@pytest.mark.parametrize(
"path",
[
pytest.param(
"/required-list-validation-alias",
marks=pytest.mark.xfail(raises=AssertionError, strict=False),
),
"/required-list-validation-alias",
"/model-required-list-validation-alias",
],
)
@@ -299,9 +287,7 @@ def test_required_list_validation_alias_missing(path: str, json: Union[dict, Non
"detail": [
{
"type": "missing",
"loc": IsOneOf( # /required-validation-alias fails here
["body"], ["body", "p_val_alias"]
),
"loc": IsOneOf(["body"], ["body", "p_val_alias"]),
"msg": "Field required",
"input": IsOneOf(None, {}),
}
@@ -313,19 +299,14 @@ def test_required_list_validation_alias_missing(path: str, json: Union[dict, Non
@pytest.mark.parametrize(
"path",
[
pytest.param(
"/required-list-validation-alias",
marks=pytest.mark.xfail(raises=AssertionError, strict=False),
),
"/required-list-validation-alias",
"/model-required-list-validation-alias",
],
)
def test_required_list_validation_alias_by_name(path: str):
client = TestClient(app)
response = client.post(path, json={"p": ["hello", "world"]})
assert response.status_code == 422, (
response.text # /required-list-validation-alias fails here
)
assert response.status_code == 422, response.text
assert response.json() == {
"detail": [
@@ -343,19 +324,14 @@ def test_required_list_validation_alias_by_name(path: str):
@pytest.mark.parametrize(
"path",
[
pytest.param(
"/required-list-validation-alias",
marks=pytest.mark.xfail(raises=AssertionError, strict=False),
),
"/required-list-validation-alias",
"/model-required-list-validation-alias",
],
)
def test_required_list_validation_alias_by_validation_alias(path: str):
client = TestClient(app)
response = client.post(path, json={"p_val_alias": ["hello", "world"]})
assert response.status_code == 200, (
response.text # /required-list-validation-alias fails here
)
assert response.status_code == 200, response.text
assert response.json() == {"p": ["hello", "world"]}
@@ -386,7 +362,7 @@ class BodyModelRequiredListAliasAndValidationAlias(BaseModel):
def read_model_required_list_alias_and_validation_alias(
p: BodyModelRequiredListAliasAndValidationAlias,
):
return {"p": p.p} # pragma: no cover
return {"p": p.p}
@needs_pydanticv2
@@ -420,10 +396,7 @@ def test_required_list_alias_and_validation_alias_schema(path: str):
@pytest.mark.parametrize(
"path",
[
pytest.param(
"/required-list-alias-and-validation-alias",
marks=pytest.mark.xfail(raises=AssertionError, strict=False),
),
"/required-list-alias-and-validation-alias",
"/model-required-list-alias-and-validation-alias",
],
)
@@ -435,9 +408,7 @@ def test_required_list_alias_and_validation_alias_missing(path: str, json):
"detail": [
{
"type": "missing",
"loc": IsOneOf( # /required-list-alias-and-validation-alias fails here
["body"], ["body", "p_val_alias"]
),
"loc": IsOneOf(["body"], ["body", "p_val_alias"]),
"msg": "Field required",
"input": IsOneOf(None, {}),
}
@@ -449,10 +420,7 @@ def test_required_list_alias_and_validation_alias_missing(path: str, json):
@pytest.mark.parametrize(
"path",
[
pytest.param(
"/required-list-alias-and-validation-alias",
marks=pytest.mark.xfail(raises=AssertionError, strict=False),
),
"/required-list-alias-and-validation-alias",
"/model-required-list-alias-and-validation-alias",
],
)
@@ -464,7 +432,7 @@ def test_required_list_alias_and_validation_alias_by_name(path: str):
"detail": [
{
"type": "missing",
"loc": [ # /required-list-alias-and-validation-alias fails here
"loc": [
"body",
"p_val_alias",
],
@@ -479,10 +447,7 @@ def test_required_list_alias_and_validation_alias_by_name(path: str):
@pytest.mark.parametrize(
"path",
[
pytest.param(
"/required-list-alias-and-validation-alias",
marks=pytest.mark.xfail(raises=AssertionError, strict=False),
),
"/required-list-alias-and-validation-alias",
"/model-required-list-alias-and-validation-alias",
],
)
@@ -507,17 +472,12 @@ def test_required_list_alias_and_validation_alias_by_alias(path: str):
@pytest.mark.parametrize(
"path",
[
pytest.param(
"/required-list-alias-and-validation-alias",
marks=pytest.mark.xfail(raises=AssertionError, strict=False),
),
"/required-list-alias-and-validation-alias",
"/model-required-list-alias-and-validation-alias",
],
)
def test_required_list_alias_and_validation_alias_by_validation_alias(path: str):
client = TestClient(app)
response = client.post(path, json={"p_val_alias": ["hello", "world"]})
assert response.status_code == 200, (
response.text # /required-list-alias-and-validation-alias fails here
)
assert response.status_code == 200, response.text
assert response.json() == {"p": ["hello", "world"]}

View File

@@ -3,7 +3,6 @@ from typing import List, Optional
import pytest
from dirty_equals import IsDict
from fastapi import Body, FastAPI
from fastapi._compat import PYDANTIC_V2
from fastapi.testclient import TestClient
from pydantic import BaseModel, Field
from typing_extensions import Annotated
@@ -149,15 +148,7 @@ async def read_model_optional_list_alias(p: BodyModelOptionalListAlias):
@pytest.mark.parametrize(
"path",
[
pytest.param(
"/optional-list-alias",
marks=pytest.mark.xfail(
raises=AssertionError,
strict=False,
condition=PYDANTIC_V2,
reason="Fails only with PDv2",
),
),
"/optional-list-alias",
"/model-optional-list-alias",
],
)
@@ -383,10 +374,7 @@ def test_optional_list_validation_alias_missing_empty_dict(path: str):
@pytest.mark.parametrize(
"path",
[
pytest.param(
"/optional-list-validation-alias",
marks=pytest.mark.xfail(raises=AssertionError, strict=False),
),
"/optional-list-validation-alias",
"/model-optional-list-validation-alias",
],
)
@@ -394,17 +382,14 @@ def test_optional_list_validation_alias_by_name(path: str):
client = TestClient(app)
response = client.post(path, json={"p": ["hello", "world"]})
assert response.status_code == 200
assert response.json() == {"p": None} # /optional-list-validation-alias fails here
assert response.json() == {"p": None}
@needs_pydanticv2
@pytest.mark.parametrize(
"path",
[
pytest.param(
"/optional-list-validation-alias",
marks=pytest.mark.xfail(raises=AssertionError, strict=False),
),
"/optional-list-validation-alias",
"/model-optional-list-validation-alias",
],
)
@@ -412,9 +397,7 @@ def test_optional_list_validation_alias_by_validation_alias(path: str):
client = TestClient(app)
response = client.post(path, json={"p_val_alias": ["hello", "world"]})
assert response.status_code == 200, response.text
assert response.json() == { # /optional-list-validation-alias fails here
"p": ["hello", "world"]
}
assert response.json() == {"p": ["hello", "world"]}
# =====================================================================================
@@ -561,10 +544,7 @@ def test_optional_list_alias_and_validation_alias_by_name(path: str):
@pytest.mark.parametrize(
"path",
[
pytest.param(
"/optional-list-alias-and-validation-alias",
marks=pytest.mark.xfail(raises=AssertionError, strict=False),
),
"/optional-list-alias-and-validation-alias",
"/model-optional-list-alias-and-validation-alias",
],
)
@@ -572,19 +552,14 @@ def test_optional_list_alias_and_validation_alias_by_alias(path: str):
client = TestClient(app)
response = client.post(path, json={"p_alias": ["hello", "world"]})
assert response.status_code == 200
assert response.json() == {
"p": None # /optional-list-alias-and-validation-alias fails here
}
assert response.json() == {"p": None}
@needs_pydanticv2
@pytest.mark.parametrize(
"path",
[
pytest.param(
"/optional-list-alias-and-validation-alias",
marks=pytest.mark.xfail(raises=AssertionError, strict=False),
),
"/optional-list-alias-and-validation-alias",
"/model-optional-list-alias-and-validation-alias",
],
)
@@ -593,7 +568,7 @@ def test_optional_list_alias_and_validation_alias_by_validation_alias(path: str)
response = client.post(path, json={"p_val_alias": ["hello", "world"]})
assert response.status_code == 200, response.text
assert response.json() == {
"p": [ # /optional-list-alias-and-validation-alias fails here
"p": [
"hello",
"world",
]

View File

@@ -3,7 +3,6 @@ from typing import Optional
import pytest
from dirty_equals import IsDict
from fastapi import Body, FastAPI
from fastapi._compat import PYDANTIC_V2
from fastapi.testclient import TestClient
from pydantic import BaseModel, Field
from typing_extensions import Annotated
@@ -144,15 +143,7 @@ async def read_model_optional_alias(p: BodyModelOptionalAlias):
@pytest.mark.parametrize(
"path",
[
pytest.param(
"/optional-alias",
marks=pytest.mark.xfail(
raises=AssertionError,
strict=False,
condition=PYDANTIC_V2,
reason="Fails only with PDv2",
),
),
"/optional-alias",
"/model-optional-alias",
],
)
@@ -364,10 +355,7 @@ def test_model_optional_validation_alias_missing_empty_dict(path: str):
@pytest.mark.parametrize(
"path",
[
pytest.param(
"/optional-validation-alias",
marks=pytest.mark.xfail(raises=AssertionError, strict=False),
),
"/optional-validation-alias",
"/model-optional-validation-alias",
],
)
@@ -375,17 +363,14 @@ def test_optional_validation_alias_by_name(path: str):
client = TestClient(app)
response = client.post(path, json={"p": "hello"})
assert response.status_code == 200
assert response.json() == {"p": None} # /optional-validation-alias fails here
assert response.json() == {"p": None}
@needs_pydanticv2
@pytest.mark.parametrize(
"path",
[
pytest.param(
"/optional-validation-alias",
marks=pytest.mark.xfail(raises=AssertionError, strict=False),
),
"/optional-validation-alias",
"/model-optional-validation-alias",
],
)
@@ -393,7 +378,7 @@ def test_optional_validation_alias_by_validation_alias(path: str):
client = TestClient(app)
response = client.post(path, json={"p_val_alias": "hello"})
assert response.status_code == 200
assert response.json() == {"p": "hello"} # /optional-validation-alias fails here
assert response.json() == {"p": "hello"}
# =====================================================================================
@@ -533,10 +518,7 @@ def test_optional_alias_and_validation_alias_by_name(path: str):
@pytest.mark.parametrize(
"path",
[
pytest.param(
"/optional-alias-and-validation-alias",
marks=pytest.mark.xfail(raises=AssertionError, strict=False),
),
"/optional-alias-and-validation-alias",
"/model-optional-alias-and-validation-alias",
],
)
@@ -544,19 +526,14 @@ def test_optional_alias_and_validation_alias_by_alias(path: str):
client = TestClient(app)
response = client.post(path, json={"p_alias": "hello"})
assert response.status_code == 200
assert response.json() == {
"p": None # /optional-alias-and-validation-alias fails here
}
assert response.json() == {"p": None}
@needs_pydanticv2
@pytest.mark.parametrize(
"path",
[
pytest.param(
"/optional-alias-and-validation-alias",
marks=pytest.mark.xfail(raises=AssertionError, strict=False),
),
"/optional-alias-and-validation-alias",
"/model-optional-alias-and-validation-alias",
],
)
@@ -564,6 +541,4 @@ def test_optional_alias_and_validation_alias_by_validation_alias(path: str):
client = TestClient(app)
response = client.post(path, json={"p_val_alias": "hello"})
assert response.status_code == 200
assert response.json() == {
"p": "hello" # /optional-alias-and-validation-alias fails here
}
assert response.json() == {"p": "hello"}

View File

@@ -3,7 +3,6 @@ from typing import Any, Dict, Union
import pytest
from dirty_equals import IsDict, IsOneOf
from fastapi import Body, FastAPI
from fastapi._compat import PYDANTIC_V2
from fastapi.testclient import TestClient
from pydantic import BaseModel, Field
from typing_extensions import Annotated
@@ -118,15 +117,7 @@ async def read_model_required_alias(p: BodyModelRequiredAlias):
@pytest.mark.parametrize(
"path",
[
pytest.param(
"/required-alias",
marks=pytest.mark.xfail(
raises=AssertionError,
condition=PYDANTIC_V2,
reason="Fails only with PDv2",
strict=False,
),
),
"/required-alias",
"/model-required-alias",
],
)
@@ -270,10 +261,7 @@ def test_required_validation_alias_schema(path: str):
@pytest.mark.parametrize(
"path",
[
pytest.param(
"/required-validation-alias",
marks=pytest.mark.xfail(raises=AssertionError, strict=False),
),
"/required-validation-alias",
"/model-required-validation-alias",
],
)
@@ -287,9 +275,7 @@ def test_required_validation_alias_missing(
"detail": [
{
"type": "missing",
"loc": IsOneOf( # /required-validation-alias fails here
["body", "p_val_alias"], ["body"]
),
"loc": IsOneOf(["body", "p_val_alias"], ["body"]),
"msg": "Field required",
"input": IsOneOf(None, {}),
}
@@ -301,19 +287,14 @@ def test_required_validation_alias_missing(
@pytest.mark.parametrize(
"path",
[
pytest.param(
"/required-validation-alias",
marks=pytest.mark.xfail(raises=AssertionError, strict=False),
),
"/required-validation-alias",
"/model-required-validation-alias",
],
)
def test_required_validation_alias_by_name(path: str):
client = TestClient(app)
response = client.post(path, json={"p": "hello"})
assert response.status_code == 422, ( # /required-validation-alias fails here
response.text
)
assert response.status_code == 422, response.text
assert response.json() == {
"detail": [
@@ -331,19 +312,14 @@ def test_required_validation_alias_by_name(path: str):
@pytest.mark.parametrize(
"path",
[
pytest.param(
"/required-validation-alias",
marks=pytest.mark.xfail(raises=AssertionError, strict=False),
),
"/required-validation-alias",
"/model-required-validation-alias",
],
)
def test_required_validation_alias_by_validation_alias(path: str):
client = TestClient(app)
response = client.post(path, json={"p_val_alias": "hello"})
assert response.status_code == 200, ( # /required-validation-alias fails here
response.text
)
assert response.status_code == 200, response.text
assert response.json() == {"p": "hello"}
@@ -405,10 +381,7 @@ def test_required_alias_and_validation_alias_schema(path: str):
@pytest.mark.parametrize(
"path",
[
pytest.param(
"/required-alias-and-validation-alias",
marks=pytest.mark.xfail(raises=AssertionError, strict=False),
),
"/required-alias-and-validation-alias",
"/model-required-alias-and-validation-alias",
],
)
@@ -422,9 +395,7 @@ def test_required_alias_and_validation_alias_missing(
"detail": [
{
"type": "missing",
"loc": IsOneOf( # /required-alias-and-validation-alias fails here
["body"], ["body", "p_val_alias"]
),
"loc": IsOneOf(["body"], ["body", "p_val_alias"]),
"msg": "Field required",
"input": IsOneOf(None, {}),
}
@@ -436,10 +407,7 @@ def test_required_alias_and_validation_alias_missing(
@pytest.mark.parametrize(
"path",
[
pytest.param(
"/required-alias-and-validation-alias",
marks=pytest.mark.xfail(raises=AssertionError, strict=False),
),
"/required-alias-and-validation-alias",
"/model-required-alias-and-validation-alias",
],
)
@@ -454,7 +422,7 @@ def test_required_alias_and_validation_alias_by_name(path: str):
"type": "missing",
"loc": [
"body",
"p_val_alias", # /required-alias-and-validation-alias fails here
"p_val_alias",
],
"msg": "Field required",
"input": IsOneOf(None, {"p": "hello"}),
@@ -467,19 +435,14 @@ def test_required_alias_and_validation_alias_by_name(path: str):
@pytest.mark.parametrize(
"path",
[
pytest.param(
"/required-alias-and-validation-alias",
marks=pytest.mark.xfail(raises=AssertionError, strict=False),
),
"/required-alias-and-validation-alias",
"/model-required-alias-and-validation-alias",
],
)
def test_required_alias_and_validation_alias_by_alias(path: str):
client = TestClient(app)
response = client.post(path, json={"p_alias": "hello"})
assert response.status_code == 422, (
response.text # /required-alias-and-validation-alias fails here
)
assert response.status_code == 422, response.text
assert response.json() == {
"detail": [
@@ -497,18 +460,13 @@ def test_required_alias_and_validation_alias_by_alias(path: str):
@pytest.mark.parametrize(
"path",
[
pytest.param(
"/required-alias-and-validation-alias",
marks=pytest.mark.xfail(raises=AssertionError, strict=False),
),
"/required-alias-and-validation-alias",
"/model-required-alias-and-validation-alias",
],
)
def test_required_alias_and_validation_alias_by_validation_alias(path: str):
client = TestClient(app)
response = client.post(path, json={"p_val_alias": "hello"})
assert response.status_code == 200, (
response.text # /required-alias-and-validation-alias fails here
)
assert response.status_code == 200, response.text
assert response.json() == {"p": "hello"}

View File

@@ -157,10 +157,7 @@ def test_optional_alias_by_name(path: str):
"path",
[
"/optional-alias",
pytest.param(
"/model-optional-alias",
marks=pytest.mark.xfail(raises=AssertionError, strict=False),
),
"/model-optional-alias",
],
)
def test_optional_alias_by_alias(path: str):
@@ -168,7 +165,7 @@ def test_optional_alias_by_alias(path: str):
client.cookies.set("p_alias", "hello")
response = client.get(path)
assert response.status_code == 200
assert response.json() == {"p": "hello"} # /model-optional-alias fails here
assert response.json() == {"p": "hello"}
# =====================================================================================
@@ -194,7 +191,6 @@ def read_model_optional_validation_alias(
@needs_pydanticv2
@pytest.mark.xfail(raises=AssertionError, strict=False)
@pytest.mark.parametrize(
"path",
["/optional-validation-alias", "/model-optional-validation-alias"],
@@ -229,10 +225,7 @@ def test_optional_validation_alias_missing(path: str):
@pytest.mark.parametrize(
"path",
[
pytest.param(
"/optional-validation-alias",
marks=pytest.mark.xfail(raises=AssertionError, strict=False),
),
"/optional-validation-alias",
"/model-optional-validation-alias",
],
)
@@ -248,10 +241,7 @@ def test_optional_validation_alias_by_name(path: str):
@pytest.mark.parametrize(
"path",
[
pytest.param(
"/optional-validation-alias",
marks=pytest.mark.xfail(raises=AssertionError, strict=False),
),
"/optional-validation-alias",
"/model-optional-validation-alias",
],
)
@@ -260,7 +250,7 @@ def test_optional_validation_alias_by_validation_alias(path: str):
client.cookies.set("p_val_alias", "hello")
response = client.get(path)
assert response.status_code == 200
assert response.json() == {"p": "hello"} # /optional-validation-alias fails here
assert response.json() == {"p": "hello"}
# =====================================================================================
@@ -288,7 +278,6 @@ def read_model_optional_alias_and_validation_alias(
@needs_pydanticv2
@pytest.mark.xfail(raises=AssertionError, strict=False)
@pytest.mark.parametrize(
"path",
[
@@ -345,10 +334,7 @@ def test_optional_alias_and_validation_alias_by_name(path: str):
@pytest.mark.parametrize(
"path",
[
pytest.param(
"/optional-alias-and-validation-alias",
marks=pytest.mark.xfail(raises=AssertionError, strict=False),
),
"/optional-alias-and-validation-alias",
"/model-optional-alias-and-validation-alias",
],
)
@@ -357,19 +343,14 @@ def test_optional_alias_and_validation_alias_by_alias(path: str):
client.cookies.set("p_alias", "hello")
response = client.get(path)
assert response.status_code == 200
assert response.json() == {
"p": None # /optional-alias-and-validation-alias fails here
}
assert response.json() == {"p": None}
@needs_pydanticv2
@pytest.mark.parametrize(
"path",
[
pytest.param(
"/optional-alias-and-validation-alias",
marks=pytest.mark.xfail(raises=AssertionError, strict=False),
),
"/optional-alias-and-validation-alias",
"/model-optional-alias-and-validation-alias",
],
)
@@ -378,6 +359,4 @@ def test_optional_alias_and_validation_alias_by_validation_alias(path: str):
client.cookies.set("p_val_alias", "hello")
response = client.get(path)
assert response.status_code == 200
assert response.json() == {
"p": "hello" # /optional-alias-and-validation-alias fails here
}
assert response.json() == {"p": "hello"}

View File

@@ -1,7 +1,6 @@
import pytest
from dirty_equals import IsDict, IsOneOf
from fastapi import Cookie, FastAPI
from fastapi._compat import PYDANTIC_V2
from fastapi.testclient import TestClient
from pydantic import BaseModel, Field
from typing_extensions import Annotated
@@ -103,7 +102,7 @@ class CookieModelRequiredAlias(BaseModel):
@app.get("/model-required-alias")
async def read_model_required_alias(p: Annotated[CookieModelRequiredAlias, Cookie()]):
return {"p": p.p} # pragma: no cover
return {"p": p.p}
@pytest.mark.parametrize(
@@ -158,15 +157,7 @@ def test_required_alias_missing(path: str):
"path",
[
"/required-alias",
pytest.param(
"/model-required-alias",
marks=pytest.mark.xfail(
raises=AssertionError,
condition=PYDANTIC_V2,
reason="Fails only with PDv2 models",
strict=False,
),
),
"/model-required-alias",
],
)
def test_required_alias_by_name(path: str):
@@ -183,7 +174,7 @@ def test_required_alias_by_name(path: str):
"msg": "Field required",
"input": IsOneOf(
None,
{"p": "hello"}, # /model-required-alias PDv2 fails here
{"p": "hello"},
),
}
]
@@ -206,19 +197,14 @@ def test_required_alias_by_name(path: str):
"path",
[
"/required-alias",
pytest.param(
"/model-required-alias",
marks=pytest.mark.xfail(raises=AssertionError, strict=False),
),
"/model-required-alias",
],
)
def test_required_alias_by_alias(path: str):
client = TestClient(app)
client.cookies.set("p_alias", "hello")
response = client.get(path)
assert response.status_code == 200, ( # /model-required-alias fails here
response.text
)
assert response.status_code == 200, response.text
assert response.json() == {"p": "hello"}
@@ -245,7 +231,6 @@ def read_model_required_validation_alias(
@needs_pydanticv2
@pytest.mark.xfail(raises=AssertionError, strict=False)
@pytest.mark.parametrize(
"path",
["/required-validation-alias", "/model-required-validation-alias"],
@@ -265,10 +250,7 @@ def test_required_validation_alias_schema(path: str):
@pytest.mark.parametrize(
"path",
[
pytest.param(
"/required-validation-alias",
marks=pytest.mark.xfail(raises=AssertionError, strict=False),
),
"/required-validation-alias",
"/model-required-validation-alias",
],
)
@@ -282,7 +264,7 @@ def test_required_validation_alias_missing(path: str):
"type": "missing",
"loc": [
"cookie",
"p_val_alias", # /required-validation-alias fails here
"p_val_alias",
],
"msg": "Field required",
"input": IsOneOf(None, {}),
@@ -295,10 +277,7 @@ def test_required_validation_alias_missing(path: str):
@pytest.mark.parametrize(
"path",
[
pytest.param(
"/required-validation-alias",
marks=pytest.mark.xfail(raises=AssertionError, strict=False),
),
"/required-validation-alias",
"/model-required-validation-alias",
],
)
@@ -306,9 +285,7 @@ def test_required_validation_alias_by_name(path: str):
client = TestClient(app)
client.cookies.set("p", "hello")
response = client.get(path)
assert response.status_code == 422, ( # /required-validation-alias fails here
response.text
)
assert response.status_code == 422, response.text
assert response.json() == {
"detail": [
@@ -326,10 +303,7 @@ def test_required_validation_alias_by_name(path: str):
@pytest.mark.parametrize(
"path",
[
pytest.param(
"/required-validation-alias",
marks=pytest.mark.xfail(raises=AssertionError, strict=False),
),
"/required-validation-alias",
"/model-required-validation-alias",
],
)
@@ -337,9 +311,7 @@ def test_required_validation_alias_by_validation_alias(path: str):
client = TestClient(app)
client.cookies.set("p_val_alias", "hello")
response = client.get(path)
assert response.status_code == 200, ( # /required-validation-alias fails here
response.text
)
assert response.status_code == 200, response.text
assert response.json() == {"p": "hello"}
@@ -367,7 +339,6 @@ def read_model_required_alias_and_validation_alias(
@needs_pydanticv2
@pytest.mark.xfail(raises=AssertionError, strict=False)
@pytest.mark.parametrize(
"path",
[
@@ -390,10 +361,7 @@ def test_required_alias_and_validation_alias_schema(path: str):
@pytest.mark.parametrize(
"path",
[
pytest.param(
"/required-alias-and-validation-alias",
marks=pytest.mark.xfail(raises=AssertionError, strict=False),
),
"/required-alias-and-validation-alias",
"/model-required-alias-and-validation-alias",
],
)
@@ -407,7 +375,7 @@ def test_required_alias_and_validation_alias_missing(path: str):
"type": "missing",
"loc": [
"cookie",
"p_val_alias", # /required-alias-and-validation-alias fails here
"p_val_alias",
],
"msg": "Field required",
"input": IsOneOf(None, {}),
@@ -417,7 +385,6 @@ def test_required_alias_and_validation_alias_missing(path: str):
@needs_pydanticv2
@pytest.mark.xfail(raises=AssertionError, strict=False)
@pytest.mark.parametrize(
"path",
[
@@ -437,10 +404,10 @@ def test_required_alias_and_validation_alias_by_name(path: str):
"type": "missing",
"loc": [
"cookie",
"p_val_alias", # /required-alias-and-validation-alias fails here
"p_val_alias",
],
"msg": "Field required",
"input": IsOneOf( # /model-alias-and-validation-alias fails here
"input": IsOneOf(
None,
{"p": "hello"},
),
@@ -450,7 +417,6 @@ def test_required_alias_and_validation_alias_by_name(path: str):
@needs_pydanticv2
@pytest.mark.xfail(raises=AssertionError, strict=False)
@pytest.mark.parametrize(
"path",
[
@@ -462,9 +428,7 @@ def test_required_alias_and_validation_alias_by_alias(path: str):
client = TestClient(app)
client.cookies.set("p_alias", "hello")
response = client.get(path)
assert (
response.status_code == 422 # /required-alias-and-validation-alias fails here
)
assert response.status_code == 422
assert response.json() == {
"detail": [
@@ -472,7 +436,7 @@ def test_required_alias_and_validation_alias_by_alias(path: str):
"type": "missing",
"loc": ["cookie", "p_val_alias"],
"msg": "Field required",
"input": IsOneOf( # /model-alias-and-validation-alias fails here
"input": IsOneOf(
None,
{"p_alias": "hello"},
),
@@ -485,10 +449,7 @@ def test_required_alias_and_validation_alias_by_alias(path: str):
@pytest.mark.parametrize(
"path",
[
pytest.param(
"/required-alias-and-validation-alias",
marks=pytest.mark.xfail(raises=AssertionError, strict=False),
),
"/required-alias-and-validation-alias",
"/model-required-alias-and-validation-alias",
],
)
@@ -496,8 +457,6 @@ def test_required_alias_and_validation_alias_by_validation_alias(path: str):
client = TestClient(app)
client.cookies.set("p_val_alias", "hello")
response = client.get(path)
assert response.status_code == 200, (
response.text # /required-alias-and-validation-alias fails here
)
assert response.status_code == 200, response.text
assert response.json() == {"p": "hello"}

View File

@@ -3,7 +3,6 @@ from typing import List
import pytest
from dirty_equals import IsDict
from fastapi import FastAPI, File, UploadFile
from fastapi._compat import PYDANTIC_V2
from fastapi.testclient import TestClient
from typing_extensions import Annotated
@@ -134,12 +133,6 @@ async def read_list_uploadfile_alias(
return {"file_size": [file.size for file in p]}
@pytest.mark.xfail(
raises=AssertionError,
condition=PYDANTIC_V2,
reason="Fails only with PDv2",
strict=False,
)
@pytest.mark.parametrize(
"path",
[
@@ -334,14 +327,8 @@ def test_list_validation_alias_schema(path: str):
@pytest.mark.parametrize(
"path",
[
pytest.param(
"/list-bytes-validation-alias",
marks=pytest.mark.xfail(raises=AssertionError, strict=False),
),
pytest.param(
"/list-uploadfile-validation-alias",
marks=pytest.mark.xfail(raises=AssertionError, strict=False),
),
"/list-bytes-validation-alias",
"/list-uploadfile-validation-alias",
],
)
def test_list_validation_alias_missing(path: str):
@@ -352,7 +339,7 @@ def test_list_validation_alias_missing(path: str):
"detail": [
{
"type": "missing",
"loc": [ # /list-*-validation-alias fail here
"loc": [
"body",
"p_val_alias",
],
@@ -367,24 +354,16 @@ def test_list_validation_alias_missing(path: str):
@pytest.mark.parametrize(
"path",
[
pytest.param(
"/list-bytes-validation-alias",
marks=pytest.mark.xfail(raises=AssertionError, strict=False),
),
pytest.param(
"/list-uploadfile-validation-alias",
marks=pytest.mark.xfail(raises=AssertionError, strict=False),
),
"/list-bytes-validation-alias",
"/list-uploadfile-validation-alias",
],
)
def test_list_validation_alias_by_name(path: str):
client = TestClient(app)
response = client.post(path, files=[("p", b"hello"), ("p", b"world")])
assert response.status_code == 422, ( # /list-*-validation-alias fail here
response.text
)
assert response.status_code == 422, response.text
assert response.json() == { # pragma: no cover
assert response.json() == {
"detail": [
{
"type": "missing",
@@ -396,7 +375,6 @@ def test_list_validation_alias_by_name(path: str):
}
@pytest.mark.xfail(raises=AssertionError, strict=False)
@needs_pydanticv2
@pytest.mark.parametrize(
"path",
@@ -410,8 +388,8 @@ def test_list_validation_alias_by_validation_alias(path: str):
response = client.post(
path, files=[("p_val_alias", b"hello"), ("p_val_alias", b"world")]
)
assert response.status_code == 200, response.text # all 2 fail here
assert response.json() == {"file_size": [5, 5]} # pragma: no cover
assert response.status_code == 200, response.text
assert response.json() == {"file_size": [5, 5]}
# =====================================================================================
@@ -486,14 +464,8 @@ def test_list_alias_and_validation_alias_schema(path: str):
@pytest.mark.parametrize(
"path",
[
pytest.param(
"/list-bytes-alias-and-validation-alias",
marks=pytest.mark.xfail(raises=AssertionError, strict=False),
),
pytest.param(
"/list-uploadfile-alias-and-validation-alias",
marks=pytest.mark.xfail(raises=AssertionError, strict=False),
),
"/list-bytes-alias-and-validation-alias",
"/list-uploadfile-alias-and-validation-alias",
],
)
def test_list_alias_and_validation_alias_missing(path: str):
@@ -506,7 +478,7 @@ def test_list_alias_and_validation_alias_missing(path: str):
"type": "missing",
"loc": [
"body",
"p_val_alias", # /list-*-alias-and-validation-alias fail here
"p_val_alias",
],
"msg": "Field required",
"input": None,
@@ -515,7 +487,6 @@ def test_list_alias_and_validation_alias_missing(path: str):
}
@pytest.mark.xfail(raises=AssertionError, strict=False)
@needs_pydanticv2
@pytest.mark.parametrize(
"path",
@@ -535,7 +506,7 @@ def test_list_alias_and_validation_alias_by_name(path: str):
"type": "missing",
"loc": [
"body",
"p_val_alias", # /list-*-alias-and-validation-alias fail here
"p_val_alias",
],
"msg": "Field required",
"input": None,
@@ -548,24 +519,16 @@ def test_list_alias_and_validation_alias_by_name(path: str):
@pytest.mark.parametrize(
"path",
[
pytest.param(
"/list-bytes-alias-and-validation-alias",
marks=pytest.mark.xfail(raises=AssertionError, strict=False),
),
pytest.param(
"/list-uploadfile-alias-and-validation-alias",
marks=pytest.mark.xfail(raises=AssertionError, strict=False),
),
"/list-bytes-alias-and-validation-alias",
"/list-uploadfile-alias-and-validation-alias",
],
)
def test_list_alias_and_validation_alias_by_alias(path: str):
client = TestClient(app)
response = client.post(path, files=[("p_alias", b"hello"), ("p_alias", b"world")])
assert response.status_code == 422, (
response.text # /list-*-alias-and-validation-alias fails here
)
assert response.status_code == 422, response.text
assert response.json() == { # pragma: no cover
assert response.json() == {
"detail": [
{
"type": "missing",
@@ -577,7 +540,6 @@ def test_list_alias_and_validation_alias_by_alias(path: str):
}
@pytest.mark.xfail(raises=AssertionError, strict=False)
@needs_pydanticv2
@pytest.mark.parametrize(
"path",
@@ -591,7 +553,5 @@ def test_list_alias_and_validation_alias_by_validation_alias(path: str):
response = client.post(
path, files=[("p_val_alias", b"hello"), ("p_val_alias", b"world")]
)
assert response.status_code == 200, ( # all 2 fail here
response.text
)
assert response.json() == {"file_size": [5, 5]} # pragma: no cover
assert response.status_code == 200, response.text
assert response.json() == {"file_size": [5, 5]}

View File

@@ -3,7 +3,6 @@ from typing import Optional
import pytest
from dirty_equals import IsDict
from fastapi import FastAPI, File, UploadFile
from fastapi._compat import PYDANTIC_V2
from fastapi.testclient import TestClient
from typing_extensions import Annotated
@@ -107,12 +106,6 @@ async def read_optional_uploadfile_alias(
return {"file_size": p.size if p else None}
@pytest.mark.xfail(
raises=AssertionError,
condition=PYDANTIC_V2,
reason="Fails only with PDv2",
strict=False,
)
@pytest.mark.parametrize(
"path",
[
@@ -266,44 +259,30 @@ def test_optional_validation_alias_missing(path: str):
@pytest.mark.parametrize(
"path",
[
pytest.param(
"/optional-bytes-validation-alias",
marks=pytest.mark.xfail(raises=AssertionError, strict=False),
),
pytest.param(
"/optional-uploadfile-validation-alias",
marks=pytest.mark.xfail(raises=AssertionError, strict=False),
),
"/optional-bytes-validation-alias",
"/optional-uploadfile-validation-alias",
],
)
def test_optional_validation_alias_by_name(path: str):
client = TestClient(app)
response = client.post(path, files=[("p", b"hello")])
assert response.status_code == 200, response.text
assert response.json() == { # /optional-*-validation-alias fail here
"file_size": None
}
assert response.json() == {"file_size": None}
@needs_pydanticv2
@pytest.mark.parametrize(
"path",
[
pytest.param(
"/optional-bytes-validation-alias",
marks=pytest.mark.xfail(raises=AssertionError, strict=False),
),
pytest.param(
"/optional-uploadfile-validation-alias",
marks=pytest.mark.xfail(raises=AssertionError, strict=False),
),
"/optional-bytes-validation-alias",
"/optional-uploadfile-validation-alias",
],
)
def test_optional_validation_alias_by_validation_alias(path: str):
client = TestClient(app)
response = client.post(path, files=[("p_val_alias", b"hello")])
assert response.status_code == 200, response.text
assert response.json() == {"file_size": 5} # /optional-*-validation-alias fail here
assert response.json() == {"file_size": 5}
# =====================================================================================
@@ -403,14 +382,8 @@ def test_optional_alias_and_validation_alias_by_name(path: str):
@pytest.mark.parametrize(
"path",
[
pytest.param(
"/optional-bytes-alias-and-validation-alias",
marks=pytest.mark.xfail(raises=AssertionError, strict=False),
),
pytest.param(
"/optional-uploadfile-alias-and-validation-alias",
marks=pytest.mark.xfail(raises=AssertionError, strict=False),
),
"/optional-bytes-alias-and-validation-alias",
"/optional-uploadfile-alias-and-validation-alias",
],
)
def test_optional_alias_and_validation_alias_by_alias(path: str):
@@ -424,20 +397,12 @@ def test_optional_alias_and_validation_alias_by_alias(path: str):
@pytest.mark.parametrize(
"path",
[
pytest.param(
"/optional-bytes-alias-and-validation-alias",
marks=pytest.mark.xfail(raises=AssertionError, strict=False),
),
pytest.param(
"/optional-uploadfile-alias-and-validation-alias",
marks=pytest.mark.xfail(raises=AssertionError, strict=False),
),
"/optional-bytes-alias-and-validation-alias",
"/optional-uploadfile-alias-and-validation-alias",
],
)
def test_optional_alias_and_validation_alias_by_validation_alias(path: str):
client = TestClient(app)
response = client.post(path, files=[("p_val_alias", b"hello")])
assert response.status_code == 200, response.text
assert response.json() == {
"file_size": 5
} # /optional-*-alias-and-validation-alias fail here
assert response.json() == {"file_size": 5}

View File

@@ -3,7 +3,6 @@ from typing import List, Optional
import pytest
from dirty_equals import IsDict
from fastapi import FastAPI, File, UploadFile
from fastapi._compat import PYDANTIC_V2
from fastapi.testclient import TestClient
from typing_extensions import Annotated
@@ -87,15 +86,7 @@ def test_optional_list_missing(path: str):
@pytest.mark.parametrize(
"path",
[
pytest.param(
"/optional-list-bytes",
marks=pytest.mark.xfail(
raises=(TypeError, AssertionError),
condition=PYDANTIC_V2,
reason="Fails only with PDv2 due to #14297",
strict=False,
),
),
"/optional-list-bytes",
"/optional-list-uploadfile",
],
)
@@ -124,12 +115,6 @@ async def read_optional_list_uploadfile_alias(
return {"file_size": [file.size for file in p] if p else None}
@pytest.mark.xfail(
raises=AssertionError,
condition=PYDANTIC_V2,
reason="Fails only with PDv2",
strict=False,
)
@pytest.mark.parametrize(
"path",
[
@@ -202,15 +187,7 @@ def test_optional_list_alias_by_name(path: str):
@pytest.mark.parametrize(
"path",
[
pytest.param(
"/optional-list-bytes-alias",
marks=pytest.mark.xfail(
raises=(TypeError, AssertionError),
strict=False,
condition=PYDANTIC_V2,
reason="Fails only with PDv2 model due to #14297",
),
),
"/optional-list-bytes-alias",
"/optional-list-uploadfile-alias",
],
)
@@ -302,30 +279,17 @@ def test_optional_validation_alias_missing(path: str):
@pytest.mark.parametrize(
"path",
[
pytest.param(
"/optional-list-bytes-validation-alias",
marks=pytest.mark.xfail(
raises=(TypeError, AssertionError),
strict=False,
reason="Fails due to #14297",
),
),
pytest.param(
"/optional-list-uploadfile-validation-alias",
marks=pytest.mark.xfail(raises=AssertionError, strict=False),
),
"/optional-list-bytes-validation-alias",
"/optional-list-uploadfile-validation-alias",
],
)
def test_optional_validation_alias_by_name(path: str):
client = TestClient(app)
response = client.post(path, files=[("p", b"hello"), ("p", b"world")])
assert response.status_code == 200, response.text
assert response.json() == { # /optional-list-uploadfile-validation-alias fails here
"file_size": None
}
assert response.json() == {"file_size": None}
@pytest.mark.xfail(raises=AssertionError, strict=False)
@needs_pydanticv2
@pytest.mark.parametrize(
"path",
@@ -340,9 +304,7 @@ def test_optional_validation_alias_by_validation_alias(path: str):
path, files=[("p_val_alias", b"hello"), ("p_val_alias", b"world")]
)
assert response.status_code == 200, response.text
assert response.json() == {
"file_size": [5, 5] # /optional-list-*-validation-alias fail here
}
assert response.json() == {"file_size": [5, 5]}
# =====================================================================================
@@ -444,30 +406,17 @@ def test_optional_list_alias_and_validation_alias_by_name(path: str):
@pytest.mark.parametrize(
"path",
[
pytest.param(
"/optional-list-bytes-alias-and-validation-alias",
marks=pytest.mark.xfail(
raises=(TypeError, AssertionError),
strict=False,
reason="Fails due to #14297",
),
),
pytest.param(
"/optional-list-uploadfile-alias-and-validation-alias",
marks=pytest.mark.xfail(raises=AssertionError, strict=False),
),
"/optional-list-bytes-alias-and-validation-alias",
"/optional-list-uploadfile-alias-and-validation-alias",
],
)
def test_optional_list_alias_and_validation_alias_by_alias(path: str):
client = TestClient(app)
response = client.post(path, files=[("p_alias", b"hello"), ("p_alias", b"world")])
assert response.status_code == 200, response.text
assert ( # /optional-list-uploadfile-alias-and-validation-alias fails here
response.json() == {"file_size": None}
)
assert response.json() == {"file_size": None}
@pytest.mark.xfail(raises=AssertionError, strict=False)
@needs_pydanticv2
@pytest.mark.parametrize(
"path",
@@ -482,6 +431,4 @@ def test_optional_list_alias_and_validation_alias_by_validation_alias(path: str)
path, files=[("p_val_alias", b"hello"), ("p_val_alias", b"world")]
)
assert response.status_code == 200, response.text
assert response.json() == {
"file_size": [5, 5] # /optional-list-*-alias-and-validation-alias fail here
}
assert response.json() == {"file_size": [5, 5]}

View File

@@ -1,7 +1,6 @@
import pytest
from dirty_equals import IsDict
from fastapi import FastAPI, File, UploadFile
from fastapi._compat import PYDANTIC_V2
from fastapi.testclient import TestClient
from typing_extensions import Annotated
@@ -112,12 +111,6 @@ async def read_required_uploadfile_alias(
return {"file_size": p.size}
@pytest.mark.xfail(
raises=AssertionError,
condition=PYDANTIC_V2,
reason="Fails only with PDv2",
strict=False,
)
@pytest.mark.parametrize(
"path",
[
@@ -278,14 +271,8 @@ def test_required_validation_alias_schema(path: str):
@pytest.mark.parametrize(
"path",
[
pytest.param(
"/required-bytes-validation-alias",
marks=pytest.mark.xfail(raises=AssertionError, strict=False),
),
pytest.param(
"/required-uploadfile-validation-alias",
marks=pytest.mark.xfail(raises=AssertionError, strict=False),
),
"/required-bytes-validation-alias",
"/required-uploadfile-validation-alias",
],
)
def test_required_validation_alias_missing(path: str):
@@ -296,7 +283,7 @@ def test_required_validation_alias_missing(path: str):
"detail": [
{
"type": "missing",
"loc": [ # /required-*-validation-alias fail here
"loc": [
"body",
"p_val_alias",
],
@@ -311,24 +298,16 @@ def test_required_validation_alias_missing(path: str):
@pytest.mark.parametrize(
"path",
[
pytest.param(
"/required-bytes-validation-alias",
marks=pytest.mark.xfail(raises=AssertionError, strict=False),
),
pytest.param(
"/required-uploadfile-validation-alias",
marks=pytest.mark.xfail(raises=AssertionError, strict=False),
),
"/required-bytes-validation-alias",
"/required-uploadfile-validation-alias",
],
)
def test_required_validation_alias_by_name(path: str):
client = TestClient(app)
response = client.post(path, files=[("p", b"hello")])
assert response.status_code == 422, ( # /required-*-validation-alias fail here
response.text
)
assert response.status_code == 422, response.text
assert response.json() == { # pragma: no cover
assert response.json() == {
"detail": [
{
"type": "missing",
@@ -344,23 +323,15 @@ def test_required_validation_alias_by_name(path: str):
@pytest.mark.parametrize(
"path",
[
pytest.param(
"/required-bytes-validation-alias",
marks=pytest.mark.xfail(raises=AssertionError, strict=False),
),
pytest.param(
"/required-uploadfile-validation-alias",
marks=pytest.mark.xfail(raises=AssertionError, strict=False),
),
"/required-bytes-validation-alias",
"/required-uploadfile-validation-alias",
],
)
def test_required_validation_alias_by_validation_alias(path: str):
client = TestClient(app)
response = client.post(path, files=[("p_val_alias", b"hello")])
assert response.status_code == 200, ( # all 2 fail here
response.text
)
assert response.json() == {"file_size": 5} # pragma: no cover
assert response.status_code == 200, response.text
assert response.json() == {"file_size": 5}
# =====================================================================================
@@ -417,14 +388,8 @@ def test_required_alias_and_validation_alias_schema(path: str):
@pytest.mark.parametrize(
"path",
[
pytest.param(
"/required-bytes-alias-and-validation-alias",
marks=pytest.mark.xfail(raises=AssertionError, strict=False),
),
pytest.param(
"/required-uploadfile-alias-and-validation-alias",
marks=pytest.mark.xfail(raises=AssertionError, strict=False),
),
"/required-bytes-alias-and-validation-alias",
"/required-uploadfile-alias-and-validation-alias",
],
)
def test_required_alias_and_validation_alias_missing(path: str):
@@ -437,7 +402,7 @@ def test_required_alias_and_validation_alias_missing(path: str):
"type": "missing",
"loc": [
"body",
"p_val_alias", # /required-*-alias-and-validation-alias fail here
"p_val_alias",
],
"msg": "Field required",
"input": None,
@@ -450,14 +415,8 @@ def test_required_alias_and_validation_alias_missing(path: str):
@pytest.mark.parametrize(
"path",
[
pytest.param(
"/required-bytes-alias-and-validation-alias",
marks=pytest.mark.xfail(raises=AssertionError, strict=False),
),
pytest.param(
"/required-uploadfile-alias-and-validation-alias",
marks=pytest.mark.xfail(raises=AssertionError, strict=False),
),
"/required-bytes-alias-and-validation-alias",
"/required-uploadfile-alias-and-validation-alias",
],
)
def test_required_alias_and_validation_alias_by_name(path: str):
@@ -471,7 +430,7 @@ def test_required_alias_and_validation_alias_by_name(path: str):
"type": "missing",
"loc": [
"body",
"p_val_alias", # /required-*-alias-and-validation-alias fail here
"p_val_alias",
],
"msg": "Field required",
"input": None,
@@ -484,24 +443,16 @@ def test_required_alias_and_validation_alias_by_name(path: str):
@pytest.mark.parametrize(
"path",
[
pytest.param(
"/required-bytes-alias-and-validation-alias",
marks=pytest.mark.xfail(raises=AssertionError, strict=False),
),
pytest.param(
"/required-uploadfile-alias-and-validation-alias",
marks=pytest.mark.xfail(raises=AssertionError, strict=False),
),
"/required-bytes-alias-and-validation-alias",
"/required-uploadfile-alias-and-validation-alias",
],
)
def test_required_alias_and_validation_alias_by_alias(path: str):
client = TestClient(app)
response = client.post(path, files=[("p_alias", b"hello")])
assert response.status_code == 422, (
response.text # /required-*-alias-and-validation-alias fails here
)
assert response.status_code == 422, response.text
assert response.json() == { # pragma: no cover
assert response.json() == {
"detail": [
{
"type": "missing",
@@ -517,20 +468,12 @@ def test_required_alias_and_validation_alias_by_alias(path: str):
@pytest.mark.parametrize(
"path",
[
pytest.param(
"/required-bytes-alias-and-validation-alias",
marks=pytest.mark.xfail(raises=AssertionError, strict=False),
),
pytest.param(
"/required-uploadfile-alias-and-validation-alias",
marks=pytest.mark.xfail(raises=AssertionError, strict=False),
),
"/required-bytes-alias-and-validation-alias",
"/required-uploadfile-alias-and-validation-alias",
],
)
def test_required_alias_and_validation_alias_by_validation_alias(path: str):
client = TestClient(app)
response = client.post(path, files=[("p_val_alias", b"hello")])
assert response.status_code == 200, ( # all 2 fail here
response.text
)
assert response.json() == {"file_size": 5} # pragma: no cover
assert response.status_code == 200, response.text
assert response.json() == {"file_size": 5}

View File

@@ -3,7 +3,6 @@ from typing import List
import pytest
from dirty_equals import IsDict, IsOneOf, IsPartialDict
from fastapi import FastAPI, Form
from fastapi._compat import PYDANTIC_V2
from fastapi.testclient import TestClient
from pydantic import BaseModel, Field
from typing_extensions import Annotated
@@ -114,21 +113,13 @@ class FormModelRequiredListAlias(BaseModel):
async def read_model_required_list_alias(
p: Annotated[FormModelRequiredListAlias, Form()],
):
return {"p": p.p} # pragma: no cover
return {"p": p.p}
@pytest.mark.parametrize(
"path",
[
pytest.param(
"/required-list-alias",
marks=pytest.mark.xfail(
raises=AssertionError,
condition=PYDANTIC_V2,
reason="Fails only with PDv2 models",
strict=False,
),
),
"/required-list-alias",
"/model-required-list-alias",
],
)
@@ -187,15 +178,7 @@ def test_required_list_alias_missing(path: str):
"path",
[
"/required-list-alias",
pytest.param(
"/model-required-list-alias",
marks=pytest.mark.xfail(
raises=AssertionError,
condition=PYDANTIC_V2,
reason="Fails only with PDv2 models",
strict=False,
),
),
"/model-required-list-alias",
],
)
def test_required_list_alias_by_name(path: str):
@@ -209,9 +192,7 @@ def test_required_list_alias_by_name(path: str):
"type": "missing",
"loc": ["body", "p_alias"],
"msg": "Field required",
"input": IsOneOf( # /model-required-list-alias with PDv2 fails here
None, {"p": ["hello", "world"]}
),
"input": IsOneOf(None, {"p": ["hello", "world"]}),
}
]
}
@@ -264,7 +245,7 @@ class FormModelRequiredListValidationAlias(BaseModel):
async def read_model_required_list_validation_alias(
p: Annotated[FormModelRequiredListValidationAlias, Form()],
):
return {"p": p.p} # pragma: no cover
return {"p": p.p}
@needs_pydanticv2
@@ -294,10 +275,7 @@ def test_required_list_validation_alias_schema(path: str):
@pytest.mark.parametrize(
"path",
[
pytest.param(
"/required-list-validation-alias",
marks=pytest.mark.xfail(raises=AssertionError, strict=False),
),
"/required-list-validation-alias",
"/model-required-list-validation-alias",
],
)
@@ -311,7 +289,7 @@ def test_required_list_validation_alias_missing(path: str):
"type": "missing",
"loc": [
"body",
"p_val_alias", # /required-list-validation-alias fails here
"p_val_alias",
],
"msg": "Field required",
"input": IsOneOf(None, {}),
@@ -324,19 +302,14 @@ def test_required_list_validation_alias_missing(path: str):
@pytest.mark.parametrize(
"path",
[
pytest.param(
"/required-list-validation-alias",
marks=pytest.mark.xfail(raises=AssertionError, strict=False),
),
"/required-list-validation-alias",
"/model-required-list-validation-alias",
],
)
def test_required_list_validation_alias_by_name(path: str):
client = TestClient(app)
response = client.post(path, data={"p": ["hello", "world"]})
assert response.status_code == 422, (
response.text # /required-list-validation-alias fails here
)
assert response.status_code == 422, response.text
assert response.json() == {
"detail": [
@@ -351,7 +324,6 @@ def test_required_list_validation_alias_by_name(path: str):
@needs_pydanticv2
@pytest.mark.xfail(raises=AssertionError, strict=False)
@pytest.mark.parametrize(
"path",
["/required-list-validation-alias", "/model-required-list-validation-alias"],
@@ -359,9 +331,9 @@ def test_required_list_validation_alias_by_name(path: str):
def test_required_list_validation_alias_by_validation_alias(path: str):
client = TestClient(app)
response = client.post(path, data={"p_val_alias": ["hello", "world"]})
assert response.status_code == 200, response.text # both fail here
assert response.status_code == 200, response.text
assert response.json() == {"p": ["hello", "world"]} # pragma: no cover
assert response.json() == {"p": ["hello", "world"]}
# =====================================================================================
@@ -389,7 +361,7 @@ class FormModelRequiredListAliasAndValidationAlias(BaseModel):
def read_model_required_list_alias_and_validation_alias(
p: Annotated[FormModelRequiredListAliasAndValidationAlias, Form()],
):
return {"p": p.p} # pragma: no cover
return {"p": p.p}
@needs_pydanticv2
@@ -422,10 +394,7 @@ def test_required_list_alias_and_validation_alias_schema(path: str):
@pytest.mark.parametrize(
"path",
[
pytest.param(
"/required-list-alias-and-validation-alias",
marks=pytest.mark.xfail(raises=AssertionError, strict=False),
),
"/required-list-alias-and-validation-alias",
"/model-required-list-alias-and-validation-alias",
],
)
@@ -439,7 +408,6 @@ def test_required_list_alias_and_validation_alias_missing(path: str):
"type": "missing",
"loc": [
"body",
# /required-list-alias-and-validation-alias fails here
"p_val_alias",
],
"msg": "Field required",
@@ -450,7 +418,6 @@ def test_required_list_alias_and_validation_alias_missing(path: str):
@needs_pydanticv2
@pytest.mark.xfail(raises=AssertionError, strict=False)
@pytest.mark.parametrize(
"path",
[
@@ -468,13 +435,11 @@ def test_required_list_alias_and_validation_alias_by_name(path: str):
"type": "missing",
"loc": [
"body",
# /required-list-alias-and-validation-alias fails here
"p_val_alias",
],
"msg": "Field required",
"input": IsOneOf(
None,
# /model-required-list-alias-and-validation-alias fails here
{"p": ["hello", "world"]},
),
}
@@ -486,19 +451,14 @@ def test_required_list_alias_and_validation_alias_by_name(path: str):
@pytest.mark.parametrize(
"path",
[
pytest.param(
"/required-list-alias-and-validation-alias",
marks=pytest.mark.xfail(raises=AssertionError, strict=False),
),
"/required-list-alias-and-validation-alias",
"/model-required-list-alias-and-validation-alias",
],
)
def test_required_list_alias_and_validation_alias_by_alias(path: str):
client = TestClient(app)
response = client.post(path, data={"p_alias": ["hello", "world"]})
assert ( # /required-list-alias-and-validation-alias fails here
response.status_code == 422
)
assert response.status_code == 422
assert response.json() == {
"detail": [
{
@@ -512,7 +472,6 @@ def test_required_list_alias_and_validation_alias_by_alias(path: str):
@needs_pydanticv2
@pytest.mark.xfail(raises=AssertionError, strict=False)
@pytest.mark.parametrize(
"path",
[
@@ -523,5 +482,5 @@ def test_required_list_alias_and_validation_alias_by_alias(path: str):
def test_required_list_alias_and_validation_alias_by_validation_alias(path: str):
client = TestClient(app)
response = client.post(path, data={"p_val_alias": ["hello", "world"]})
assert response.status_code == 200, response.text # both fail here
assert response.json() == {"p": ["hello", "world"]} # pragma: no cover
assert response.status_code == 200, response.text
assert response.json() == {"p": ["hello", "world"]}

View File

@@ -3,7 +3,6 @@ from typing import List, Optional
import pytest
from dirty_equals import IsDict
from fastapi import FastAPI, Form
from fastapi._compat import PYDANTIC_V2
from fastapi.testclient import TestClient
from pydantic import BaseModel, Field
from typing_extensions import Annotated
@@ -115,15 +114,7 @@ async def read_model_optional_list_alias(
@pytest.mark.parametrize(
"path",
[
pytest.param(
"/optional-list-alias",
marks=pytest.mark.xfail(
raises=AssertionError,
strict=False,
condition=PYDANTIC_V2,
reason="Fails only with PDv2",
),
),
"/optional-list-alias",
"/model-optional-list-alias",
],
)
@@ -276,10 +267,7 @@ def test_optional_list_validation_alias_missing(path: str):
@pytest.mark.parametrize(
"path",
[
pytest.param(
"/optional-list-validation-alias",
marks=pytest.mark.xfail(raises=AssertionError, strict=False),
),
"/optional-list-validation-alias",
"/model-optional-list-validation-alias",
],
)
@@ -287,11 +275,10 @@ def test_optional_list_validation_alias_by_name(path: str):
client = TestClient(app)
response = client.post(path, data={"p": ["hello", "world"]})
assert response.status_code == 200
assert response.json() == {"p": None} # /optional-list-validation-alias fails here
assert response.json() == {"p": None}
@needs_pydanticv2
@pytest.mark.xfail(raises=AssertionError, strict=False)
@pytest.mark.parametrize(
"path",
["/optional-list-validation-alias", "/model-optional-list-validation-alias"],
@@ -299,12 +286,8 @@ def test_optional_list_validation_alias_by_name(path: str):
def test_optional_list_validation_alias_by_validation_alias(path: str):
client = TestClient(app)
response = client.post(path, data={"p_val_alias": ["hello", "world"]})
assert response.status_code == 200, (
response.text # /model-optional-list-validation-alias fails here
)
assert response.json() == { # /optional-list-validation-alias fails here
"p": ["hello", "world"]
}
assert response.status_code == 200, response.text
assert response.json() == {"p": ["hello", "world"]}
# =====================================================================================
@@ -415,10 +398,7 @@ def test_optional_list_alias_and_validation_alias_by_name(path: str):
@pytest.mark.parametrize(
"path",
[
pytest.param(
"/optional-list-alias-and-validation-alias",
marks=pytest.mark.xfail(raises=AssertionError, strict=False),
),
"/optional-list-alias-and-validation-alias",
"/model-optional-list-alias-and-validation-alias",
],
)
@@ -426,13 +406,10 @@ def test_optional_list_alias_and_validation_alias_by_alias(path: str):
client = TestClient(app)
response = client.post(path, data={"p_alias": ["hello", "world"]})
assert response.status_code == 200
assert response.json() == {
"p": None # /optional-list-alias-and-validation-alias fails here
}
assert response.json() == {"p": None}
@needs_pydanticv2
@pytest.mark.xfail(raises=AssertionError, strict=False)
@pytest.mark.parametrize(
"path",
[
@@ -443,11 +420,9 @@ def test_optional_list_alias_and_validation_alias_by_alias(path: str):
def test_optional_list_alias_and_validation_alias_by_validation_alias(path: str):
client = TestClient(app)
response = client.post(path, data={"p_val_alias": ["hello", "world"]})
assert response.status_code == 200, (
response.text # /model-optional-list-alias-and-validation-alias fails here
)
assert response.status_code == 200, response.text
assert response.json() == {
"p": [ # /optional-list-alias-and-validation-alias fails here
"p": [
"hello",
"world",
]

View File

@@ -3,7 +3,6 @@ from typing import Optional
import pytest
from dirty_equals import IsDict
from fastapi import FastAPI, Form
from fastapi._compat import PYDANTIC_V2
from fastapi.testclient import TestClient
from pydantic import BaseModel, Field
from typing_extensions import Annotated
@@ -108,15 +107,7 @@ async def read_model_optional_alias(p: Annotated[FormModelOptionalAlias, Form()]
@pytest.mark.parametrize(
"path",
[
pytest.param(
"/optional-alias",
marks=pytest.mark.xfail(
raises=AssertionError,
strict=False,
condition=PYDANTIC_V2,
reason="Fails only with PDv2",
),
),
"/optional-alias",
"/model-optional-alias",
],
)
@@ -252,10 +243,7 @@ def test_optional_validation_alias_missing(path: str):
@pytest.mark.parametrize(
"path",
[
pytest.param(
"/optional-validation-alias",
marks=pytest.mark.xfail(raises=AssertionError, strict=False),
),
"/optional-validation-alias",
"/model-optional-validation-alias",
],
)
@@ -263,17 +251,14 @@ def test_optional_validation_alias_by_name(path: str):
client = TestClient(app)
response = client.post(path, data={"p": "hello"})
assert response.status_code == 200
assert response.json() == {"p": None} # /optional-validation-alias fails here
assert response.json() == {"p": None}
@needs_pydanticv2
@pytest.mark.parametrize(
"path",
[
pytest.param(
"/optional-validation-alias",
marks=pytest.mark.xfail(raises=AssertionError, strict=False),
),
"/optional-validation-alias",
"/model-optional-validation-alias",
],
)
@@ -281,7 +266,7 @@ def test_optional_validation_alias_by_validation_alias(path: str):
client = TestClient(app)
response = client.post(path, data={"p_val_alias": "hello"})
assert response.status_code == 200
assert response.json() == {"p": "hello"} # /optional-validation-alias fails here
assert response.json() == {"p": "hello"}
# =====================================================================================
@@ -383,10 +368,7 @@ def test_optional_alias_and_validation_alias_by_name(path: str):
@pytest.mark.parametrize(
"path",
[
pytest.param(
"/optional-alias-and-validation-alias",
marks=pytest.mark.xfail(raises=AssertionError, strict=False),
),
"/optional-alias-and-validation-alias",
"/model-optional-alias-and-validation-alias",
],
)
@@ -394,19 +376,14 @@ def test_optional_alias_and_validation_alias_by_alias(path: str):
client = TestClient(app)
response = client.post(path, data={"p_alias": "hello"})
assert response.status_code == 200
assert response.json() == {
"p": None # /optional-alias-and-validation-alias fails here
}
assert response.json() == {"p": None}
@needs_pydanticv2
@pytest.mark.parametrize(
"path",
[
pytest.param(
"/optional-alias-and-validation-alias",
marks=pytest.mark.xfail(raises=AssertionError, strict=False),
),
"/optional-alias-and-validation-alias",
"/model-optional-alias-and-validation-alias",
],
)
@@ -414,6 +391,4 @@ def test_optional_alias_and_validation_alias_by_validation_alias(path: str):
client = TestClient(app)
response = client.post(path, data={"p_val_alias": "hello"})
assert response.status_code == 200
assert response.json() == {
"p": "hello" # /optional-alias-and-validation-alias fails here
}
assert response.json() == {"p": "hello"}

View File

@@ -1,7 +1,6 @@
import pytest
from dirty_equals import IsDict, IsOneOf
from fastapi import FastAPI, Form
from fastapi._compat import PYDANTIC_V2
from fastapi.testclient import TestClient
from pydantic import BaseModel, Field
from typing_extensions import Annotated
@@ -113,15 +112,7 @@ async def read_model_required_alias(p: Annotated[FormModelRequiredAlias, Form()]
@pytest.mark.parametrize(
"path",
[
pytest.param(
"/required-alias",
marks=pytest.mark.xfail(
raises=AssertionError,
condition=PYDANTIC_V2,
reason="Fails only with PDv2",
strict=False,
),
),
"/required-alias",
"/model-required-alias",
],
)
@@ -263,10 +254,7 @@ def test_required_validation_alias_schema(path: str):
@pytest.mark.parametrize(
"path",
[
pytest.param(
"/required-validation-alias",
marks=pytest.mark.xfail(raises=AssertionError, strict=False),
),
"/required-validation-alias",
"/model-required-validation-alias",
],
)
@@ -280,7 +268,7 @@ def test_required_validation_alias_missing(path: str):
"type": "missing",
"loc": [
"body",
"p_val_alias", # /required-validation-alias fails here
"p_val_alias",
],
"msg": "Field required",
"input": IsOneOf(None, {}),
@@ -293,19 +281,14 @@ def test_required_validation_alias_missing(path: str):
@pytest.mark.parametrize(
"path",
[
pytest.param(
"/required-validation-alias",
marks=pytest.mark.xfail(raises=AssertionError, strict=False),
),
"/required-validation-alias",
"/model-required-validation-alias",
],
)
def test_required_validation_alias_by_name(path: str):
client = TestClient(app)
response = client.post(path, data={"p": "hello"})
assert response.status_code == 422, ( # /required-validation-alias fails here
response.text
)
assert response.status_code == 422, response.text
assert response.json() == {
"detail": [
@@ -323,19 +306,14 @@ def test_required_validation_alias_by_name(path: str):
@pytest.mark.parametrize(
"path",
[
pytest.param(
"/required-validation-alias",
marks=pytest.mark.xfail(raises=AssertionError, strict=False),
),
"/required-validation-alias",
"/model-required-validation-alias",
],
)
def test_required_validation_alias_by_validation_alias(path: str):
client = TestClient(app)
response = client.post(path, data={"p_val_alias": "hello"})
assert response.status_code == 200, ( # /required-validation-alias fails here
response.text
)
assert response.status_code == 200, response.text
assert response.json() == {"p": "hello"}
@@ -394,10 +372,7 @@ def test_required_alias_and_validation_alias_schema(path: str):
@pytest.mark.parametrize(
"path",
[
pytest.param(
"/required-alias-and-validation-alias",
marks=pytest.mark.xfail(raises=AssertionError, strict=False),
),
"/required-alias-and-validation-alias",
"/model-required-alias-and-validation-alias",
],
)
@@ -411,7 +386,7 @@ def test_required_alias_and_validation_alias_missing(path: str):
"type": "missing",
"loc": [
"body",
"p_val_alias", # /required-alias-and-validation-alias fails here
"p_val_alias",
],
"msg": "Field required",
"input": IsOneOf(None, {}),
@@ -424,10 +399,7 @@ def test_required_alias_and_validation_alias_missing(path: str):
@pytest.mark.parametrize(
"path",
[
pytest.param(
"/required-alias-and-validation-alias",
marks=pytest.mark.xfail(raises=AssertionError, strict=False),
),
"/required-alias-and-validation-alias",
"/model-required-alias-and-validation-alias",
],
)
@@ -442,7 +414,7 @@ def test_required_alias_and_validation_alias_by_name(path: str):
"type": "missing",
"loc": [
"body",
"p_val_alias", # /required-alias-and-validation-alias fails here
"p_val_alias",
],
"msg": "Field required",
"input": IsOneOf(None, {"p": "hello"}),
@@ -455,19 +427,14 @@ def test_required_alias_and_validation_alias_by_name(path: str):
@pytest.mark.parametrize(
"path",
[
pytest.param(
"/required-alias-and-validation-alias",
marks=pytest.mark.xfail(raises=AssertionError, strict=False),
),
"/required-alias-and-validation-alias",
"/model-required-alias-and-validation-alias",
],
)
def test_required_alias_and_validation_alias_by_alias(path: str):
client = TestClient(app)
response = client.post(path, data={"p_alias": "hello"})
assert response.status_code == 422, (
response.text # /required-alias-and-validation-alias fails here
)
assert response.status_code == 422, response.text
assert response.json() == {
"detail": [
@@ -485,18 +452,13 @@ def test_required_alias_and_validation_alias_by_alias(path: str):
@pytest.mark.parametrize(
"path",
[
pytest.param(
"/required-alias-and-validation-alias",
marks=pytest.mark.xfail(raises=AssertionError, strict=False),
),
"/required-alias-and-validation-alias",
"/model-required-alias-and-validation-alias",
],
)
def test_required_alias_and_validation_alias_by_validation_alias(path: str):
client = TestClient(app)
response = client.post(path, data={"p_val_alias": "hello"})
assert response.status_code == 200, (
response.text # /required-alias-and-validation-alias fails here
)
assert response.status_code == 200, response.text
assert response.json() == {"p": "hello"}

View File

@@ -3,7 +3,6 @@ from typing import List
import pytest
from dirty_equals import AnyThing, IsDict, IsOneOf, IsPartialDict
from fastapi import FastAPI, Header
from fastapi._compat import PYDANTIC_V2
from fastapi.testclient import TestClient
from pydantic import BaseModel, Field
from typing_extensions import Annotated
@@ -109,7 +108,7 @@ class HeaderModelRequiredListAlias(BaseModel):
async def read_model_required_list_alias(
p: Annotated[HeaderModelRequiredListAlias, Header()],
):
return {"p": p.p} # pragma: no cover
return {"p": p.p}
@pytest.mark.parametrize(
@@ -168,15 +167,7 @@ def test_required_list_alias_missing(path: str):
"path",
[
"/required-list-alias",
pytest.param(
"/model-required-list-alias",
marks=pytest.mark.xfail(
raises=AssertionError,
condition=PYDANTIC_V2,
reason="Fails only with PDv2 models",
strict=False,
),
),
"/model-required-list-alias",
],
)
def test_required_list_alias_by_name(path: str):
@@ -190,9 +181,7 @@ def test_required_list_alias_by_name(path: str):
"type": "missing",
"loc": ["header", "p_alias"],
"msg": "Field required",
"input": IsOneOf( # /model-required-list-alias with PDv2 fails here
None, IsPartialDict({"p": ["hello", "world"]})
),
"input": IsOneOf(None, IsPartialDict({"p": ["hello", "world"]})),
}
]
}
@@ -214,18 +203,13 @@ def test_required_list_alias_by_name(path: str):
"path",
[
"/required-list-alias",
pytest.param(
"/model-required-list-alias",
marks=pytest.mark.xfail(raises=AssertionError, strict=False),
),
"/model-required-list-alias",
],
)
def test_required_list_alias_by_alias(path: str):
client = TestClient(app)
response = client.get(path, headers=[("p_alias", "hello"), ("p_alias", "world")])
assert response.status_code == 200, ( # /model-required-list-alias fails here
response.text
)
assert response.status_code == 200, response.text
assert response.json() == {"p": ["hello", "world"]}
@@ -248,11 +232,10 @@ class HeaderModelRequiredListValidationAlias(BaseModel):
async def read_model_required_list_validation_alias(
p: Annotated[HeaderModelRequiredListValidationAlias, Header()],
):
return {"p": p.p} # pragma: no cover
return {"p": p.p}
@needs_pydanticv2
@pytest.mark.xfail(raises=AssertionError, strict=False)
@pytest.mark.parametrize(
"path",
["/required-list-validation-alias", "/model-required-list-validation-alias"],
@@ -276,10 +259,7 @@ def test_required_list_validation_alias_schema(path: str):
@pytest.mark.parametrize(
"path",
[
pytest.param(
"/required-list-validation-alias",
marks=pytest.mark.xfail(raises=AssertionError, strict=False),
),
"/required-list-validation-alias",
"/model-required-list-validation-alias",
],
)
@@ -293,7 +273,7 @@ def test_required_list_validation_alias_missing(path: str):
"type": "missing",
"loc": [
"header",
"p_val_alias", # /required-list-validation-alias fails here
"p_val_alias",
],
"msg": "Field required",
"input": AnyThing,
@@ -306,17 +286,14 @@ def test_required_list_validation_alias_missing(path: str):
@pytest.mark.parametrize(
"path",
[
pytest.param(
"/required-list-validation-alias",
marks=pytest.mark.xfail(raises=AssertionError, strict=False),
),
"/required-list-validation-alias",
"/model-required-list-validation-alias",
],
)
def test_required_list_validation_alias_by_name(path: str):
client = TestClient(app)
response = client.get(path, headers=[("p", "hello"), ("p", "world")])
assert response.status_code == 422 # /required-list-validation-alias fails here
assert response.status_code == 422
assert response.json() == {
"detail": [
@@ -331,7 +308,6 @@ def test_required_list_validation_alias_by_name(path: str):
@needs_pydanticv2
@pytest.mark.xfail(raises=AssertionError, strict=False)
@pytest.mark.parametrize(
"path",
["/required-list-validation-alias", "/model-required-list-validation-alias"],
@@ -341,9 +317,9 @@ def test_required_list_validation_alias_by_validation_alias(path: str):
response = client.get(
path, headers=[("p_val_alias", "hello"), ("p_val_alias", "world")]
)
assert response.status_code == 200, response.text # both fail here
assert response.status_code == 200, response.text
assert response.json() == {"p": ["hello", "world"]} # pragma: no cover
assert response.json() == {"p": ["hello", "world"]}
# =====================================================================================
@@ -365,11 +341,10 @@ class HeaderModelRequiredListAliasAndValidationAlias(BaseModel):
def read_model_required_list_alias_and_validation_alias(
p: Annotated[HeaderModelRequiredListAliasAndValidationAlias, Header()],
):
return {"p": p.p} # pragma: no cover
return {"p": p.p}
@needs_pydanticv2
@pytest.mark.xfail(raises=AssertionError, strict=False)
@pytest.mark.parametrize(
"path",
[
@@ -396,10 +371,7 @@ def test_required_list_alias_and_validation_alias_schema(path: str):
@pytest.mark.parametrize(
"path",
[
pytest.param(
"/required-list-alias-and-validation-alias",
marks=pytest.mark.xfail(raises=AssertionError, strict=False),
),
"/required-list-alias-and-validation-alias",
"/model-required-list-alias-and-validation-alias",
],
)
@@ -413,7 +385,6 @@ def test_required_list_alias_and_validation_alias_missing(path: str):
"type": "missing",
"loc": [
"header",
# /required-list-alias-and-validation-alias fails here
"p_val_alias",
],
"msg": "Field required",
@@ -424,7 +395,6 @@ def test_required_list_alias_and_validation_alias_missing(path: str):
@needs_pydanticv2
@pytest.mark.xfail(raises=AssertionError, strict=False)
@pytest.mark.parametrize(
"path",
[
@@ -442,13 +412,11 @@ def test_required_list_alias_and_validation_alias_by_name(path: str):
"type": "missing",
"loc": [
"header",
# /required-list-alias-and-validation-alias fails here
"p_val_alias",
],
"msg": "Field required",
"input": IsOneOf(
None,
# /model-required-list-alias-and-validation-alias fails here
IsPartialDict({"p": ["hello", "world"]}),
),
}
@@ -457,7 +425,6 @@ def test_required_list_alias_and_validation_alias_by_name(path: str):
@needs_pydanticv2
@pytest.mark.xfail(raises=AssertionError, strict=False)
@pytest.mark.parametrize(
"path",
[
@@ -468,9 +435,7 @@ def test_required_list_alias_and_validation_alias_by_name(path: str):
def test_required_list_alias_and_validation_alias_by_alias(path: str):
client = TestClient(app)
response = client.get(path, headers=[("p_alias", "hello"), ("p_alias", "world")])
assert ( # /required-list-alias-and-validation-alias fails here
response.status_code == 422
)
assert response.status_code == 422
assert response.json() == {
"detail": [
{
@@ -479,7 +444,6 @@ def test_required_list_alias_and_validation_alias_by_alias(path: str):
"msg": "Field required",
"input": IsOneOf(
None,
# /model-required-list-alias-and-validation-alias fails here
IsPartialDict({"p_alias": ["hello", "world"]}),
),
}
@@ -488,7 +452,6 @@ def test_required_list_alias_and_validation_alias_by_alias(path: str):
@needs_pydanticv2
@pytest.mark.xfail(raises=AssertionError, strict=False)
@pytest.mark.parametrize(
"path",
[
@@ -501,5 +464,5 @@ def test_required_list_alias_and_validation_alias_by_validation_alias(path: str)
response = client.get(
path, headers=[("p_val_alias", "hello"), ("p_val_alias", "world")]
)
assert response.status_code == 200, response.text # both fail here
assert response.json() == {"p": ["hello", "world"]} # pragma: no cover
assert response.status_code == 200, response.text
assert response.json() == {"p": ["hello", "world"]}

View File

@@ -171,19 +171,14 @@ def test_optional_list_alias_by_name(path: str):
"path",
[
"/optional-list-alias",
pytest.param(
"/model-optional-list-alias",
marks=pytest.mark.xfail(raises=AssertionError, strict=False),
),
"/model-optional-list-alias",
],
)
def test_optional_list_alias_by_alias(path: str):
client = TestClient(app)
response = client.get(path, headers=[("p_alias", "hello"), ("p_alias", "world")])
assert response.status_code == 200
assert response.json() == {
"p": ["hello", "world"] # /model-optional-list-alias fails here
}
assert response.json() == {"p": ["hello", "world"]}
# =====================================================================================
@@ -209,7 +204,6 @@ def read_model_optional_list_validation_alias(
@needs_pydanticv2
@pytest.mark.xfail(raises=AssertionError, strict=False)
@pytest.mark.parametrize(
"path",
["/optional-list-validation-alias", "/model-optional-list-validation-alias"],
@@ -247,10 +241,7 @@ def test_optional_list_validation_alias_missing(path: str):
@pytest.mark.parametrize(
"path",
[
pytest.param(
"/optional-list-validation-alias",
marks=pytest.mark.xfail(raises=AssertionError, strict=False),
),
"/optional-list-validation-alias",
"/model-optional-list-validation-alias",
],
)
@@ -258,11 +249,10 @@ def test_optional_list_validation_alias_by_name(path: str):
client = TestClient(app)
response = client.get(path, headers=[("p", "hello"), ("p", "world")])
assert response.status_code == 200
assert response.json() == {"p": None} # /optional-list-validation-alias fails here
assert response.json() == {"p": None}
@needs_pydanticv2
@pytest.mark.xfail(raises=AssertionError, strict=False)
@pytest.mark.parametrize(
"path",
["/optional-list-validation-alias", "/model-optional-list-validation-alias"],
@@ -272,12 +262,8 @@ def test_optional_list_validation_alias_by_validation_alias(path: str):
response = client.get(
path, headers=[("p_val_alias", "hello"), ("p_val_alias", "world")]
)
assert response.status_code == 200, (
response.text # /model-optional-list-validation-alias fails here
)
assert response.json() == { # /optional-list-validation-alias fails here
"p": ["hello", "world"]
}
assert response.status_code == 200, response.text
assert response.json() == {"p": ["hello", "world"]}
# =====================================================================================
@@ -307,7 +293,6 @@ def read_model_optional_list_alias_and_validation_alias(
@needs_pydanticv2
@pytest.mark.xfail(raises=AssertionError, strict=False)
@pytest.mark.parametrize(
"path",
[
@@ -366,10 +351,7 @@ def test_optional_list_alias_and_validation_alias_by_name(path: str):
@pytest.mark.parametrize(
"path",
[
pytest.param(
"/optional-list-alias-and-validation-alias",
marks=pytest.mark.xfail(raises=AssertionError, strict=False),
),
"/optional-list-alias-and-validation-alias",
"/model-optional-list-alias-and-validation-alias",
],
)
@@ -377,13 +359,10 @@ def test_optional_list_alias_and_validation_alias_by_alias(path: str):
client = TestClient(app)
response = client.get(path, headers=[("p_alias", "hello"), ("p_alias", "world")])
assert response.status_code == 200
assert response.json() == {
"p": None # /optional-list-alias-and-validation-alias fails here
}
assert response.json() == {"p": None}
@needs_pydanticv2
@pytest.mark.xfail(raises=AssertionError, strict=False)
@pytest.mark.parametrize(
"path",
[
@@ -396,11 +375,9 @@ def test_optional_list_alias_and_validation_alias_by_validation_alias(path: str)
response = client.get(
path, headers=[("p_val_alias", "hello"), ("p_val_alias", "world")]
)
assert response.status_code == 200, (
response.text # /model-optional-list-alias-and-validation-alias fails here
)
assert response.status_code == 200, response.text
assert response.json() == {
"p": [ # /optional-list-alias-and-validation-alias fails here
"p": [
"hello",
"world",
]

View File

@@ -155,17 +155,14 @@ def test_optional_alias_by_name(path: str):
"path",
[
"/optional-alias",
pytest.param(
"/model-optional-alias",
marks=pytest.mark.xfail(raises=AssertionError, strict=False),
),
"/model-optional-alias",
],
)
def test_optional_alias_by_alias(path: str):
client = TestClient(app)
response = client.get(path, headers={"p_alias": "hello"})
assert response.status_code == 200
assert response.json() == {"p": "hello"} # /model-optional-alias fails here
assert response.json() == {"p": "hello"}
# =====================================================================================
@@ -191,7 +188,6 @@ def read_model_optional_validation_alias(
@needs_pydanticv2
@pytest.mark.xfail(raises=AssertionError, strict=False)
@pytest.mark.parametrize(
"path",
["/optional-validation-alias", "/model-optional-validation-alias"],
@@ -226,10 +222,7 @@ def test_optional_validation_alias_missing(path: str):
@pytest.mark.parametrize(
"path",
[
pytest.param(
"/optional-validation-alias",
marks=pytest.mark.xfail(raises=AssertionError, strict=False),
),
"/optional-validation-alias",
"/model-optional-validation-alias",
],
)
@@ -237,17 +230,14 @@ def test_optional_validation_alias_by_name(path: str):
client = TestClient(app)
response = client.get(path, headers={"p": "hello"})
assert response.status_code == 200
assert response.json() == {"p": None} # /optional-validation-alias fails here
assert response.json() == {"p": None}
@needs_pydanticv2
@pytest.mark.parametrize(
"path",
[
pytest.param(
"/optional-validation-alias",
marks=pytest.mark.xfail(raises=AssertionError, strict=False),
),
"/optional-validation-alias",
"/model-optional-validation-alias",
],
)
@@ -255,7 +245,7 @@ def test_optional_validation_alias_by_validation_alias(path: str):
client = TestClient(app)
response = client.get(path, headers={"p_val_alias": "hello"})
assert response.status_code == 200
assert response.json() == {"p": "hello"} # /optional-validation-alias fails here
assert response.json() == {"p": "hello"}
# =====================================================================================
@@ -283,7 +273,6 @@ def read_model_optional_alias_and_validation_alias(
@needs_pydanticv2
@pytest.mark.xfail(raises=AssertionError, strict=False)
@pytest.mark.parametrize(
"path",
[
@@ -339,10 +328,7 @@ def test_optional_alias_and_validation_alias_by_name(path: str):
@pytest.mark.parametrize(
"path",
[
pytest.param(
"/optional-alias-and-validation-alias",
marks=pytest.mark.xfail(raises=AssertionError, strict=False),
),
"/optional-alias-and-validation-alias",
"/model-optional-alias-and-validation-alias",
],
)
@@ -350,19 +336,14 @@ def test_optional_alias_and_validation_alias_by_alias(path: str):
client = TestClient(app)
response = client.get(path, headers={"p_alias": "hello"})
assert response.status_code == 200
assert response.json() == {
"p": None # /optional-alias-and-validation-alias fails here
}
assert response.json() == {"p": None}
@needs_pydanticv2
@pytest.mark.parametrize(
"path",
[
pytest.param(
"/optional-alias-and-validation-alias",
marks=pytest.mark.xfail(raises=AssertionError, strict=False),
),
"/optional-alias-and-validation-alias",
"/model-optional-alias-and-validation-alias",
],
)
@@ -370,6 +351,4 @@ def test_optional_alias_and_validation_alias_by_validation_alias(path: str):
client = TestClient(app)
response = client.get(path, headers={"p_val_alias": "hello"})
assert response.status_code == 200
assert response.json() == {
"p": "hello" # /optional-alias-and-validation-alias fails here
}
assert response.json() == {"p": "hello"}

View File

@@ -1,7 +1,6 @@
import pytest
from dirty_equals import AnyThing, IsDict, IsOneOf, IsPartialDict
from fastapi import FastAPI, Header
from fastapi._compat import PYDANTIC_V2
from fastapi.testclient import TestClient
from pydantic import BaseModel, Field
from typing_extensions import Annotated
@@ -102,7 +101,7 @@ class HeaderModelRequiredAlias(BaseModel):
@app.get("/model-required-alias")
async def read_model_required_alias(p: Annotated[HeaderModelRequiredAlias, Header()]):
return {"p": p.p} # pragma: no cover
return {"p": p.p}
@pytest.mark.parametrize(
@@ -157,15 +156,7 @@ def test_required_alias_missing(path: str):
"path",
[
"/required-alias",
pytest.param(
"/model-required-alias",
marks=pytest.mark.xfail(
raises=AssertionError,
condition=PYDANTIC_V2,
reason="Fails only with PDv2 models",
strict=False,
),
),
"/model-required-alias",
],
)
def test_required_alias_by_name(path: str):
@@ -201,18 +192,13 @@ def test_required_alias_by_name(path: str):
"path",
[
"/required-alias",
pytest.param(
"/model-required-alias",
marks=pytest.mark.xfail(raises=AssertionError, strict=False),
),
"/model-required-alias",
],
)
def test_required_alias_by_alias(path: str):
client = TestClient(app)
response = client.get(path, headers={"p_alias": "hello"})
assert response.status_code == 200, ( # /model-required-alias fails here
response.text
)
assert response.status_code == 200, response.text
assert response.json() == {"p": "hello"}
@@ -239,7 +225,6 @@ def read_model_required_validation_alias(
@needs_pydanticv2
@pytest.mark.xfail(raises=AssertionError, strict=False)
@pytest.mark.parametrize(
"path",
["/required-validation-alias", "/model-required-validation-alias"],
@@ -259,10 +244,7 @@ def test_required_validation_alias_schema(path: str):
@pytest.mark.parametrize(
"path",
[
pytest.param(
"/required-validation-alias",
marks=pytest.mark.xfail(raises=AssertionError, strict=False),
),
"/required-validation-alias",
"/model-required-validation-alias",
],
)
@@ -276,7 +258,7 @@ def test_required_validation_alias_missing(path: str):
"type": "missing",
"loc": [
"header",
"p_val_alias", # /required-validation-alias fails here
"p_val_alias",
],
"msg": "Field required",
"input": AnyThing,
@@ -289,19 +271,14 @@ def test_required_validation_alias_missing(path: str):
@pytest.mark.parametrize(
"path",
[
pytest.param(
"/required-validation-alias",
marks=pytest.mark.xfail(raises=AssertionError, strict=False),
),
"/required-validation-alias",
"/model-required-validation-alias",
],
)
def test_required_validation_alias_by_name(path: str):
client = TestClient(app)
response = client.get(path, headers={"p": "hello"})
assert response.status_code == 422, ( # /required-validation-alias fails here
response.text
)
assert response.status_code == 422, response.text
assert response.json() == {
"detail": [
@@ -319,19 +296,14 @@ def test_required_validation_alias_by_name(path: str):
@pytest.mark.parametrize(
"path",
[
pytest.param(
"/required-validation-alias",
marks=pytest.mark.xfail(raises=AssertionError, strict=False),
),
"/required-validation-alias",
"/model-required-validation-alias",
],
)
def test_required_validation_alias_by_validation_alias(path: str):
client = TestClient(app)
response = client.get(path, headers={"p_val_alias": "hello"})
assert response.status_code == 200, ( # /required-validation-alias fails here
response.text
)
assert response.status_code == 200, response.text
assert response.json() == {"p": "hello"}
@@ -359,7 +331,6 @@ def read_model_required_alias_and_validation_alias(
@needs_pydanticv2
@pytest.mark.xfail(raises=AssertionError, strict=False)
@pytest.mark.parametrize(
"path",
[
@@ -382,10 +353,7 @@ def test_required_alias_and_validation_alias_schema(path: str):
@pytest.mark.parametrize(
"path",
[
pytest.param(
"/required-alias-and-validation-alias",
marks=pytest.mark.xfail(raises=AssertionError, strict=False),
),
"/required-alias-and-validation-alias",
"/model-required-alias-and-validation-alias",
],
)
@@ -399,7 +367,7 @@ def test_required_alias_and_validation_alias_missing(path: str):
"type": "missing",
"loc": [
"header",
"p_val_alias", # /required-alias-and-validation-alias fails here
"p_val_alias",
],
"msg": "Field required",
"input": AnyThing,
@@ -409,7 +377,6 @@ def test_required_alias_and_validation_alias_missing(path: str):
@needs_pydanticv2
@pytest.mark.xfail(raises=AssertionError, strict=False)
@pytest.mark.parametrize(
"path",
[
@@ -428,10 +395,10 @@ def test_required_alias_and_validation_alias_by_name(path: str):
"type": "missing",
"loc": [
"header",
"p_val_alias", # /required-alias-and-validation-alias fails here
"p_val_alias",
],
"msg": "Field required",
"input": IsOneOf( # /model-alias-and-validation-alias fails here
"input": IsOneOf(
None,
IsPartialDict({"p": "hello"}),
),
@@ -441,7 +408,6 @@ def test_required_alias_and_validation_alias_by_name(path: str):
@needs_pydanticv2
@pytest.mark.xfail(raises=AssertionError, strict=False)
@pytest.mark.parametrize(
"path",
[
@@ -452,9 +418,7 @@ def test_required_alias_and_validation_alias_by_name(path: str):
def test_required_alias_and_validation_alias_by_alias(path: str):
client = TestClient(app)
response = client.get(path, headers={"p_alias": "hello"})
assert (
response.status_code == 422 # /required-alias-and-validation-alias fails here
)
assert response.status_code == 422
assert response.json() == {
"detail": [
@@ -462,7 +426,7 @@ def test_required_alias_and_validation_alias_by_alias(path: str):
"type": "missing",
"loc": ["header", "p_val_alias"],
"msg": "Field required",
"input": IsOneOf( # /model-alias-and-validation-alias fails here
"input": IsOneOf(
None,
IsPartialDict({"p_alias": "hello"}),
),
@@ -475,18 +439,13 @@ def test_required_alias_and_validation_alias_by_alias(path: str):
@pytest.mark.parametrize(
"path",
[
pytest.param(
"/required-alias-and-validation-alias",
marks=pytest.mark.xfail(raises=AssertionError, strict=False),
),
"/required-alias-and-validation-alias",
"/model-required-alias-and-validation-alias",
],
)
def test_required_alias_and_validation_alias_by_validation_alias(path: str):
client = TestClient(app)
response = client.get(path, headers={"p_val_alias": "hello"})
assert response.status_code == 200, (
response.text # /required-alias-and-validation-alias fails here
)
assert response.status_code == 200, response.text
assert response.json() == {"p": "hello"}

View File

@@ -22,14 +22,14 @@ async def read_required_alias(p: Annotated[str, Path(alias="p_alias")]):
def read_required_validation_alias(
p: Annotated[str, Path(validation_alias="p_val_alias")],
):
return {"p": p} # pragma: no cover
return {"p": p}
@app.get("/required-alias-and-validation-alias/{p_val_alias}")
def read_required_alias_and_validation_alias(
p: Annotated[str, Path(alias="p_alias", validation_alias="p_val_alias")],
):
return {"p": p} # pragma: no cover
return {"p": p}
@pytest.mark.parametrize(
@@ -44,20 +44,14 @@ def read_required_alias_and_validation_alias(
"p_val_alias",
"P Val Alias",
id="required-validation-alias",
marks=(
needs_pydanticv2,
pytest.mark.xfail(raises=AssertionError, strict=False),
),
marks=needs_pydanticv2,
),
pytest.param(
"/required-alias-and-validation-alias/{p_val_alias}",
"p_val_alias",
"P Val Alias",
id="required-alias-and-validation-alias",
marks=(
needs_pydanticv2,
pytest.mark.xfail(raises=AssertionError, strict=False),
),
marks=needs_pydanticv2,
),
],
)
@@ -80,18 +74,12 @@ def test_schema(path: str, expected_name: str, expected_title: str):
pytest.param(
"/required-validation-alias",
id="required-validation-alias",
marks=(
needs_pydanticv2,
pytest.mark.xfail(raises=AssertionError, strict=False),
),
marks=needs_pydanticv2,
),
pytest.param(
"/required-alias-and-validation-alias",
id="required-alias-and-validation-alias",
marks=(
needs_pydanticv2,
pytest.mark.xfail(raises=AssertionError, strict=False),
),
marks=needs_pydanticv2,
),
],
)

View File

@@ -3,7 +3,6 @@ from typing import List
import pytest
from dirty_equals import IsDict, IsOneOf
from fastapi import FastAPI, Query
from fastapi._compat import PYDANTIC_V2
from fastapi.testclient import TestClient
from pydantic import BaseModel, Field
from typing_extensions import Annotated
@@ -109,7 +108,7 @@ class QueryModelRequiredListAlias(BaseModel):
async def read_model_required_list_alias(
p: Annotated[QueryModelRequiredListAlias, Query()],
):
return {"p": p.p} # pragma: no cover
return {"p": p.p}
@pytest.mark.parametrize(
@@ -168,15 +167,7 @@ def test_required_list_alias_missing(path: str):
"path",
[
"/required-list-alias",
pytest.param(
"/model-required-list-alias",
marks=pytest.mark.xfail(
raises=AssertionError,
condition=PYDANTIC_V2,
reason="Fails only with PDv2 models",
strict=False,
),
),
"/model-required-list-alias",
],
)
def test_required_list_alias_by_name(path: str):
@@ -190,9 +181,7 @@ def test_required_list_alias_by_name(path: str):
"type": "missing",
"loc": ["query", "p_alias"],
"msg": "Field required",
"input": IsOneOf( # /model-required-list-alias with PDv2 fails here
None, {"p": ["hello", "world"]}
),
"input": IsOneOf(None, {"p": ["hello", "world"]}),
}
]
}
@@ -214,18 +203,13 @@ def test_required_list_alias_by_name(path: str):
"path",
[
"/required-list-alias",
pytest.param(
"/model-required-list-alias",
marks=pytest.mark.xfail(raises=AssertionError, strict=False),
),
"/model-required-list-alias",
],
)
def test_required_list_alias_by_alias(path: str):
client = TestClient(app)
response = client.get(f"{path}?p_alias=hello&p_alias=world")
assert response.status_code == 200, ( # /model-required-list-alias fails here
response.text
)
assert response.status_code == 200, response.text
assert response.json() == {"p": ["hello", "world"]}
@@ -248,11 +232,10 @@ class QueryModelRequiredListValidationAlias(BaseModel):
async def read_model_required_list_validation_alias(
p: Annotated[QueryModelRequiredListValidationAlias, Query()],
):
return {"p": p.p} # pragma: no cover
return {"p": p.p}
@needs_pydanticv2
@pytest.mark.xfail(raises=AssertionError, strict=False)
@pytest.mark.parametrize(
"path",
["/required-list-validation-alias", "/model-required-list-validation-alias"],
@@ -276,10 +259,7 @@ def test_required_list_validation_alias_schema(path: str):
@pytest.mark.parametrize(
"path",
[
pytest.param(
"/required-list-validation-alias",
marks=pytest.mark.xfail(raises=AssertionError, strict=False),
),
"/required-list-validation-alias",
"/model-required-list-validation-alias",
],
)
@@ -293,7 +273,7 @@ def test_required_list_validation_alias_missing(path: str):
"type": "missing",
"loc": [
"query",
"p_val_alias", # /required-list-validation-alias fails here
"p_val_alias",
],
"msg": "Field required",
"input": IsOneOf(None, {}),
@@ -306,17 +286,14 @@ def test_required_list_validation_alias_missing(path: str):
@pytest.mark.parametrize(
"path",
[
pytest.param(
"/required-list-validation-alias",
marks=pytest.mark.xfail(raises=AssertionError, strict=False),
),
"/required-list-validation-alias",
"/model-required-list-validation-alias",
],
)
def test_required_list_validation_alias_by_name(path: str):
client = TestClient(app)
response = client.get(f"{path}?p=hello&p=world")
assert response.status_code == 422 # /required-list-validation-alias fails here
assert response.status_code == 422
assert response.json() == {
"detail": [
@@ -331,7 +308,6 @@ def test_required_list_validation_alias_by_name(path: str):
@needs_pydanticv2
@pytest.mark.xfail(raises=AssertionError, strict=False)
@pytest.mark.parametrize(
"path",
["/required-list-validation-alias", "/model-required-list-validation-alias"],
@@ -339,9 +315,9 @@ def test_required_list_validation_alias_by_name(path: str):
def test_required_list_validation_alias_by_validation_alias(path: str):
client = TestClient(app)
response = client.get(f"{path}?p_val_alias=hello&p_val_alias=world")
assert response.status_code == 200, response.text # both fail here
assert response.status_code == 200, response.text
assert response.json() == {"p": ["hello", "world"]} # pragma: no cover
assert response.json() == {"p": ["hello", "world"]}
# =====================================================================================
@@ -363,11 +339,10 @@ class QueryModelRequiredListAliasAndValidationAlias(BaseModel):
def read_model_required_list_alias_and_validation_alias(
p: Annotated[QueryModelRequiredListAliasAndValidationAlias, Query()],
):
return {"p": p.p} # pragma: no cover
return {"p": p.p}
@needs_pydanticv2
@pytest.mark.xfail(raises=AssertionError, strict=False)
@pytest.mark.parametrize(
"path",
[
@@ -394,10 +369,7 @@ def test_required_list_alias_and_validation_alias_schema(path: str):
@pytest.mark.parametrize(
"path",
[
pytest.param(
"/required-list-alias-and-validation-alias",
marks=pytest.mark.xfail(raises=AssertionError, strict=False),
),
"/required-list-alias-and-validation-alias",
"/model-required-list-alias-and-validation-alias",
],
)
@@ -411,7 +383,6 @@ def test_required_list_alias_and_validation_alias_missing(path: str):
"type": "missing",
"loc": [
"query",
# /required-list-alias-and-validation-alias fails here
"p_val_alias",
],
"msg": "Field required",
@@ -422,7 +393,6 @@ def test_required_list_alias_and_validation_alias_missing(path: str):
@needs_pydanticv2
@pytest.mark.xfail(raises=AssertionError, strict=False)
@pytest.mark.parametrize(
"path",
[
@@ -440,13 +410,11 @@ def test_required_list_alias_and_validation_alias_by_name(path: str):
"type": "missing",
"loc": [
"query",
# /required-list-alias-and-validation-alias fails here
"p_val_alias",
],
"msg": "Field required",
"input": IsOneOf(
None,
# /model-required-list-alias-and-validation-alias fails here
{
"p": [
"hello",
@@ -460,7 +428,6 @@ def test_required_list_alias_and_validation_alias_by_name(path: str):
@needs_pydanticv2
@pytest.mark.xfail(raises=AssertionError, strict=False)
@pytest.mark.parametrize(
"path",
[
@@ -471,9 +438,7 @@ def test_required_list_alias_and_validation_alias_by_name(path: str):
def test_required_list_alias_and_validation_alias_by_alias(path: str):
client = TestClient(app)
response = client.get(f"{path}?p_alias=hello&p_alias=world")
assert ( # /required-list-alias-and-validation-alias fails here
response.status_code == 422
)
assert response.status_code == 422
assert response.json() == {
"detail": [
{
@@ -482,7 +447,6 @@ def test_required_list_alias_and_validation_alias_by_alias(path: str):
"msg": "Field required",
"input": IsOneOf(
None,
# /model-required-list-alias-and-validation-alias fails here
{"p_alias": ["hello", "world"]},
),
}
@@ -491,7 +455,6 @@ def test_required_list_alias_and_validation_alias_by_alias(path: str):
@needs_pydanticv2
@pytest.mark.xfail(raises=AssertionError, strict=False)
@pytest.mark.parametrize(
"path",
[
@@ -502,5 +465,5 @@ def test_required_list_alias_and_validation_alias_by_alias(path: str):
def test_required_list_alias_and_validation_alias_by_validation_alias(path: str):
client = TestClient(app)
response = client.get(f"{path}?p_val_alias=hello&p_val_alias=world")
assert response.status_code == 200, response.text # both fail here
assert response.json() == {"p": ["hello", "world"]} # pragma: no cover
assert response.status_code == 200, response.text
assert response.json() == {"p": ["hello", "world"]}

View File

@@ -171,19 +171,14 @@ def test_optional_list_alias_by_name(path: str):
"path",
[
"/optional-list-alias",
pytest.param(
"/model-optional-list-alias",
marks=pytest.mark.xfail(raises=AssertionError, strict=False),
),
"/model-optional-list-alias",
],
)
def test_optional_list_alias_by_alias(path: str):
client = TestClient(app)
response = client.get(f"{path}?p_alias=hello&p_alias=world")
assert response.status_code == 200
assert response.json() == {
"p": ["hello", "world"] # /model-optional-list-alias fails here
}
assert response.json() == {"p": ["hello", "world"]}
# =====================================================================================
@@ -209,7 +204,6 @@ def read_model_optional_list_validation_alias(
@needs_pydanticv2
@pytest.mark.xfail(raises=AssertionError, strict=False)
@pytest.mark.parametrize(
"path",
["/optional-list-validation-alias", "/model-optional-list-validation-alias"],
@@ -247,10 +241,7 @@ def test_optional_list_validation_alias_missing(path: str):
@pytest.mark.parametrize(
"path",
[
pytest.param(
"/optional-list-validation-alias",
marks=pytest.mark.xfail(raises=AssertionError, strict=False),
),
"/optional-list-validation-alias",
"/model-optional-list-validation-alias",
],
)
@@ -258,11 +249,10 @@ def test_optional_list_validation_alias_by_name(path: str):
client = TestClient(app)
response = client.get(f"{path}?p=hello&p=world")
assert response.status_code == 200
assert response.json() == {"p": None} # /optional-list-validation-alias fails here
assert response.json() == {"p": None}
@needs_pydanticv2
@pytest.mark.xfail(raises=AssertionError, strict=False)
@pytest.mark.parametrize(
"path",
["/optional-list-validation-alias", "/model-optional-list-validation-alias"],
@@ -270,12 +260,8 @@ def test_optional_list_validation_alias_by_name(path: str):
def test_optional_list_validation_alias_by_validation_alias(path: str):
client = TestClient(app)
response = client.get(f"{path}?p_val_alias=hello&p_val_alias=world")
assert response.status_code == 200, (
response.text # /model-optional-list-validation-alias fails here
)
assert response.json() == { # /optional-list-validation-alias fails here
"p": ["hello", "world"]
}
assert response.status_code == 200, response.text
assert response.json() == {"p": ["hello", "world"]}
# =====================================================================================
@@ -305,7 +291,6 @@ def read_model_optional_list_alias_and_validation_alias(
@needs_pydanticv2
@pytest.mark.xfail(raises=AssertionError, strict=False)
@pytest.mark.parametrize(
"path",
[
@@ -364,10 +349,7 @@ def test_optional_list_alias_and_validation_alias_by_name(path: str):
@pytest.mark.parametrize(
"path",
[
pytest.param(
"/optional-list-alias-and-validation-alias",
marks=pytest.mark.xfail(raises=AssertionError, strict=False),
),
"/optional-list-alias-and-validation-alias",
"/model-optional-list-alias-and-validation-alias",
],
)
@@ -375,13 +357,10 @@ def test_optional_list_alias_and_validation_alias_by_alias(path: str):
client = TestClient(app)
response = client.get(f"{path}?p_alias=hello&p_alias=world")
assert response.status_code == 200
assert response.json() == {
"p": None # /optional-list-alias-and-validation-alias fails here
}
assert response.json() == {"p": None}
@needs_pydanticv2
@pytest.mark.xfail(raises=AssertionError, strict=False)
@pytest.mark.parametrize(
"path",
[
@@ -392,11 +371,9 @@ def test_optional_list_alias_and_validation_alias_by_alias(path: str):
def test_optional_list_alias_and_validation_alias_by_validation_alias(path: str):
client = TestClient(app)
response = client.get(f"{path}?p_val_alias=hello&p_val_alias=world")
assert response.status_code == 200, (
response.text # /model-optional-list-alias-and-validation-alias fails here
)
assert response.status_code == 200, response.text
assert response.json() == {
"p": [ # /optional-list-alias-and-validation-alias fails here
"p": [
"hello",
"world",
]

View File

@@ -155,17 +155,14 @@ def test_optional_alias_by_name(path: str):
"path",
[
"/optional-alias",
pytest.param(
"/model-optional-alias",
marks=pytest.mark.xfail(raises=AssertionError, strict=False),
),
"/model-optional-alias",
],
)
def test_optional_alias_by_alias(path: str):
client = TestClient(app)
response = client.get(f"{path}?p_alias=hello")
assert response.status_code == 200
assert response.json() == {"p": "hello"} # /model-optional-alias fails here
assert response.json() == {"p": "hello"}
# =====================================================================================
@@ -191,7 +188,6 @@ def read_model_optional_validation_alias(
@needs_pydanticv2
@pytest.mark.xfail(raises=AssertionError, strict=False)
@pytest.mark.parametrize(
"path",
["/optional-validation-alias", "/model-optional-validation-alias"],
@@ -226,10 +222,7 @@ def test_optional_validation_alias_missing(path: str):
@pytest.mark.parametrize(
"path",
[
pytest.param(
"/optional-validation-alias",
marks=pytest.mark.xfail(raises=AssertionError, strict=False),
),
"/optional-validation-alias",
"/model-optional-validation-alias",
],
)
@@ -244,10 +237,7 @@ def test_optional_validation_alias_by_name(path: str):
@pytest.mark.parametrize(
"path",
[
pytest.param(
"/optional-validation-alias",
marks=pytest.mark.xfail(raises=AssertionError, strict=False),
),
"/optional-validation-alias",
"/model-optional-validation-alias",
],
)
@@ -255,7 +245,7 @@ def test_optional_validation_alias_by_validation_alias(path: str):
client = TestClient(app)
response = client.get(f"{path}?p_val_alias=hello")
assert response.status_code == 200
assert response.json() == {"p": "hello"} # /optional-validation-alias fails here
assert response.json() == {"p": "hello"}
# =====================================================================================
@@ -283,7 +273,6 @@ def read_model_optional_alias_and_validation_alias(
@needs_pydanticv2
@pytest.mark.xfail(raises=AssertionError, strict=False)
@pytest.mark.parametrize(
"path",
[
@@ -339,10 +328,7 @@ def test_optional_alias_and_validation_alias_by_name(path: str):
@pytest.mark.parametrize(
"path",
[
pytest.param(
"/optional-alias-and-validation-alias",
marks=pytest.mark.xfail(raises=AssertionError, strict=False),
),
"/optional-alias-and-validation-alias",
"/model-optional-alias-and-validation-alias",
],
)
@@ -350,19 +336,14 @@ def test_optional_alias_and_validation_alias_by_alias(path: str):
client = TestClient(app)
response = client.get(f"{path}?p_alias=hello")
assert response.status_code == 200
assert response.json() == {
"p": None # /optional-alias-and-validation-alias fails here
}
assert response.json() == {"p": None}
@needs_pydanticv2
@pytest.mark.parametrize(
"path",
[
pytest.param(
"/optional-alias-and-validation-alias",
marks=pytest.mark.xfail(raises=AssertionError, strict=False),
),
"/optional-alias-and-validation-alias",
"/model-optional-alias-and-validation-alias",
],
)
@@ -370,6 +351,4 @@ def test_optional_alias_and_validation_alias_by_validation_alias(path: str):
client = TestClient(app)
response = client.get(f"{path}?p_val_alias=hello")
assert response.status_code == 200
assert response.json() == {
"p": "hello" # /optional-alias-and-validation-alias fails here
}
assert response.json() == {"p": "hello"}

View File

@@ -1,7 +1,6 @@
import pytest
from dirty_equals import IsDict, IsOneOf
from fastapi import FastAPI, Query
from fastapi._compat import PYDANTIC_V2
from fastapi.testclient import TestClient
from pydantic import BaseModel, Field
from typing_extensions import Annotated
@@ -102,7 +101,7 @@ class QueryModelRequiredAlias(BaseModel):
@app.get("/model-required-alias")
async def read_model_required_alias(p: Annotated[QueryModelRequiredAlias, Query()]):
return {"p": p.p} # pragma: no cover
return {"p": p.p}
@pytest.mark.parametrize(
@@ -157,15 +156,7 @@ def test_required_alias_missing(path: str):
"path",
[
"/required-alias",
pytest.param(
"/model-required-alias",
marks=pytest.mark.xfail(
raises=AssertionError,
condition=PYDANTIC_V2,
reason="Fails only with PDv2 models",
strict=False,
),
),
"/model-required-alias",
],
)
def test_required_alias_by_name(path: str):
@@ -181,7 +172,7 @@ def test_required_alias_by_name(path: str):
"msg": "Field required",
"input": IsOneOf(
None,
{"p": "hello"}, # /model-required-alias PDv2 fails here
{"p": "hello"},
),
}
]
@@ -204,18 +195,13 @@ def test_required_alias_by_name(path: str):
"path",
[
"/required-alias",
pytest.param(
"/model-required-alias",
marks=pytest.mark.xfail(raises=AssertionError, strict=False),
),
"/model-required-alias",
],
)
def test_required_alias_by_alias(path: str):
client = TestClient(app)
response = client.get(f"{path}?p_alias=hello")
assert response.status_code == 200, ( # /model-required-alias fails here
response.text
)
assert response.status_code == 200, response.text
assert response.json() == {"p": "hello"}
@@ -242,7 +228,6 @@ def read_model_required_validation_alias(
@needs_pydanticv2
@pytest.mark.xfail(raises=AssertionError, strict=False)
@pytest.mark.parametrize(
"path",
["/required-validation-alias", "/model-required-validation-alias"],
@@ -262,10 +247,7 @@ def test_required_validation_alias_schema(path: str):
@pytest.mark.parametrize(
"path",
[
pytest.param(
"/required-validation-alias",
marks=pytest.mark.xfail(raises=AssertionError, strict=False),
),
"/required-validation-alias",
"/model-required-validation-alias",
],
)
@@ -279,7 +261,7 @@ def test_required_validation_alias_missing(path: str):
"type": "missing",
"loc": [
"query",
"p_val_alias", # /required-validation-alias fails here
"p_val_alias",
],
"msg": "Field required",
"input": IsOneOf(None, {}),
@@ -292,19 +274,14 @@ def test_required_validation_alias_missing(path: str):
@pytest.mark.parametrize(
"path",
[
pytest.param(
"/required-validation-alias",
marks=pytest.mark.xfail(raises=AssertionError, strict=False),
),
"/required-validation-alias",
"/model-required-validation-alias",
],
)
def test_required_validation_alias_by_name(path: str):
client = TestClient(app)
response = client.get(f"{path}?p=hello")
assert response.status_code == 422, ( # /required-validation-alias fails here
response.text
)
assert response.status_code == 422, response.text
assert response.json() == {
"detail": [
@@ -322,19 +299,14 @@ def test_required_validation_alias_by_name(path: str):
@pytest.mark.parametrize(
"path",
[
pytest.param(
"/required-validation-alias",
marks=pytest.mark.xfail(raises=AssertionError, strict=False),
),
"/required-validation-alias",
"/model-required-validation-alias",
],
)
def test_required_validation_alias_by_validation_alias(path: str):
client = TestClient(app)
response = client.get(f"{path}?p_val_alias=hello")
assert response.status_code == 200, ( # /required-validation-alias fails here
response.text
)
assert response.status_code == 200, response.text
assert response.json() == {"p": "hello"}
@@ -362,7 +334,6 @@ def read_model_required_alias_and_validation_alias(
@needs_pydanticv2
@pytest.mark.xfail(raises=AssertionError, strict=False)
@pytest.mark.parametrize(
"path",
[
@@ -385,10 +356,7 @@ def test_required_alias_and_validation_alias_schema(path: str):
@pytest.mark.parametrize(
"path",
[
pytest.param(
"/required-alias-and-validation-alias",
marks=pytest.mark.xfail(raises=AssertionError, strict=False),
),
"/required-alias-and-validation-alias",
"/model-required-alias-and-validation-alias",
],
)
@@ -402,7 +370,7 @@ def test_required_alias_and_validation_alias_missing(path: str):
"type": "missing",
"loc": [
"query",
"p_val_alias", # /required-alias-and-validation-alias fails here
"p_val_alias",
],
"msg": "Field required",
"input": IsOneOf(None, {}),
@@ -412,7 +380,6 @@ def test_required_alias_and_validation_alias_missing(path: str):
@needs_pydanticv2
@pytest.mark.xfail(raises=AssertionError, strict=False)
@pytest.mark.parametrize(
"path",
[
@@ -431,10 +398,10 @@ def test_required_alias_and_validation_alias_by_name(path: str):
"type": "missing",
"loc": [
"query",
"p_val_alias", # /required-alias-and-validation-alias fails here
"p_val_alias",
],
"msg": "Field required",
"input": IsOneOf( # /model-alias-and-validation-alias fails here
"input": IsOneOf(
None,
{"p": "hello"},
),
@@ -444,7 +411,6 @@ def test_required_alias_and_validation_alias_by_name(path: str):
@needs_pydanticv2
@pytest.mark.xfail(raises=AssertionError, strict=False)
@pytest.mark.parametrize(
"path",
[
@@ -455,9 +421,7 @@ def test_required_alias_and_validation_alias_by_name(path: str):
def test_required_alias_and_validation_alias_by_alias(path: str):
client = TestClient(app)
response = client.get(f"{path}?p_alias=hello")
assert (
response.status_code == 422 # /required-alias-and-validation-alias fails here
)
assert response.status_code == 422
assert response.json() == {
"detail": [
@@ -465,7 +429,7 @@ def test_required_alias_and_validation_alias_by_alias(path: str):
"type": "missing",
"loc": ["query", "p_val_alias"],
"msg": "Field required",
"input": IsOneOf( # /model-alias-and-validation-alias fails here
"input": IsOneOf(
None,
{"p_alias": "hello"},
),
@@ -478,18 +442,13 @@ def test_required_alias_and_validation_alias_by_alias(path: str):
@pytest.mark.parametrize(
"path",
[
pytest.param(
"/required-alias-and-validation-alias",
marks=pytest.mark.xfail(raises=AssertionError, strict=False),
),
"/required-alias-and-validation-alias",
"/model-required-alias-and-validation-alias",
],
)
def test_required_alias_and_validation_alias_by_validation_alias(path: str):
client = TestClient(app)
response = client.get(f"{path}?p_val_alias=hello")
assert response.status_code == 200, (
response.text # /required-alias-and-validation-alias fails here
)
assert response.status_code == 200, response.text
assert response.json() == {"p": "hello"}

View File

@@ -0,0 +1,207 @@
# Ref: https://github.com/fastapi/fastapi/discussions/14495
from typing import Union
import pytest
from fastapi import FastAPI
from fastapi.testclient import TestClient
from inline_snapshot import snapshot
from pydantic import BaseModel
from typing_extensions import Annotated
from .utils import needs_pydanticv2
@pytest.fixture(name="client")
def client_fixture() -> TestClient:
from fastapi import Body
from pydantic import Discriminator, Tag
class Cat(BaseModel):
pet_type: str = "cat"
meows: int
class Dog(BaseModel):
pet_type: str = "dog"
barks: float
def get_pet_type(v):
assert isinstance(v, dict)
return v.get("pet_type", "")
Pet = Annotated[
Union[Annotated[Cat, Tag("cat")], Annotated[Dog, Tag("dog")]],
Discriminator(get_pet_type),
]
app = FastAPI()
@app.post("/pet/assignment")
async def create_pet_assignment(pet: Pet = Body()):
return pet
@app.post("/pet/annotated")
async def create_pet_annotated(pet: Annotated[Pet, Body()]):
return pet
client = TestClient(app)
return client
@needs_pydanticv2
def test_union_body_discriminator_assignment(client: TestClient) -> None:
response = client.post("/pet/assignment", json={"pet_type": "cat", "meows": 5})
assert response.status_code == 200, response.text
assert response.json() == {"pet_type": "cat", "meows": 5}
@needs_pydanticv2
def test_union_body_discriminator_annotated(client: TestClient) -> None:
response = client.post("/pet/annotated", json={"pet_type": "dog", "barks": 3.5})
assert response.status_code == 200, response.text
assert response.json() == {"pet_type": "dog", "barks": 3.5}
@needs_pydanticv2
def test_openapi_schema(client: TestClient) -> None:
response = client.get("/openapi.json")
assert response.status_code == 200, response.text
assert response.json() == snapshot(
{
"openapi": "3.1.0",
"info": {"title": "FastAPI", "version": "0.1.0"},
"paths": {
"/pet/assignment": {
"post": {
"summary": "Create Pet Assignment",
"operationId": "create_pet_assignment_pet_assignment_post",
"requestBody": {
"content": {
"application/json": {
"schema": {
"anyOf": [
{"$ref": "#/components/schemas/Cat"},
{"$ref": "#/components/schemas/Dog"},
],
"title": "Pet",
}
}
},
"required": True,
},
"responses": {
"200": {
"description": "Successful Response",
"content": {"application/json": {"schema": {}}},
},
"422": {
"description": "Validation Error",
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/HTTPValidationError"
}
}
},
},
},
}
},
"/pet/annotated": {
"post": {
"summary": "Create Pet Annotated",
"operationId": "create_pet_annotated_pet_annotated_post",
"requestBody": {
"content": {
"application/json": {
"schema": {
"oneOf": [
{"$ref": "#/components/schemas/Cat"},
{"$ref": "#/components/schemas/Dog"},
],
"title": "Pet",
}
}
},
"required": True,
},
"responses": {
"200": {
"description": "Successful Response",
"content": {"application/json": {"schema": {}}},
},
"422": {
"description": "Validation Error",
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/HTTPValidationError"
}
}
},
},
},
}
},
},
"components": {
"schemas": {
"Cat": {
"properties": {
"pet_type": {
"type": "string",
"title": "Pet Type",
"default": "cat",
},
"meows": {"type": "integer", "title": "Meows"},
},
"type": "object",
"required": ["meows"],
"title": "Cat",
},
"Dog": {
"properties": {
"pet_type": {
"type": "string",
"title": "Pet Type",
"default": "dog",
},
"barks": {"type": "number", "title": "Barks"},
},
"type": "object",
"required": ["barks"],
"title": "Dog",
},
"HTTPValidationError": {
"properties": {
"detail": {
"items": {
"$ref": "#/components/schemas/ValidationError"
},
"type": "array",
"title": "Detail",
}
},
"type": "object",
"title": "HTTPValidationError",
},
"ValidationError": {
"properties": {
"loc": {
"items": {
"anyOf": [{"type": "string"}, {"type": "integer"}]
},
"type": "array",
"title": "Location",
},
"msg": {"type": "string", "title": "Message"},
"type": {"type": "string", "title": "Error Type"},
},
"type": "object",
"required": ["loc", "msg", "type"],
"title": "ValidationError",
},
}
},
}
)