mirror of
https://github.com/fastapi/fastapi.git
synced 2025-12-28 08:40:21 -05:00
Compare commits
1 Commits
0.124.4
...
dependabot
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
0c4822b40e |
2
.github/workflows/build-docs.yml
vendored
2
.github/workflows/build-docs.yml
vendored
@@ -98,7 +98,7 @@ jobs:
|
||||
run: uv pip install -r requirements-docs.txt
|
||||
- name: Update Languages
|
||||
run: python ./scripts/docs.py update-languages
|
||||
- uses: actions/cache@v4
|
||||
- uses: actions/cache@v5
|
||||
with:
|
||||
key: mkdocs-cards-${{ matrix.lang }}-${{ github.ref }}
|
||||
path: docs/${{ matrix.lang }}/.cache
|
||||
|
||||
@@ -7,18 +7,6 @@ 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).
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
"""FastAPI framework, high performance, easy to learn, fast to code, ready for production"""
|
||||
|
||||
__version__ = "0.124.4"
|
||||
__version__ = "0.124.2"
|
||||
|
||||
from starlette import status as status
|
||||
|
||||
|
||||
@@ -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, Field, TypeAdapter, create_model
|
||||
from pydantic import BaseModel, ConfigDict, TypeAdapter, create_model
|
||||
from pydantic import PydanticSchemaGenerationError as PydanticSchemaGenerationError
|
||||
from pydantic import PydanticUndefinedAnnotation as PydanticUndefinedAnnotation
|
||||
from pydantic import ValidationError as ValidationError
|
||||
@@ -50,45 +50,6 @@ 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
|
||||
@@ -110,18 +71,6 @@ 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()
|
||||
@@ -146,15 +95,10 @@ 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 = (
|
||||
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.field_info.annotation,
|
||||
*self.field_info.metadata,
|
||||
self.field_info,
|
||||
)
|
||||
self._type_adapter: TypeAdapter[Any] = TypeAdapter(
|
||||
Annotated[annotated_args],
|
||||
@@ -255,18 +199,12 @@ 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
|
||||
|
||||
@@ -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 get_validation_alias(field)
|
||||
alias = alias or field.alias
|
||||
if is_sequence_field(field) and isinstance(values, (ImmutableMultiDict, Headers)):
|
||||
value = values.getlist(alias)
|
||||
else:
|
||||
@@ -809,13 +809,15 @@ def request_params_to_args(
|
||||
field.field_info, "convert_underscores", default_convert_underscores
|
||||
)
|
||||
if convert_underscores:
|
||||
alias = get_validation_alias(field)
|
||||
if alias == field.name:
|
||||
alias = alias.replace("_", "-")
|
||||
alias = (
|
||||
field.alias
|
||||
if field.alias != field.name
|
||||
else field.name.replace("_", "-")
|
||||
)
|
||||
value = _get_multidict_value(field, received_params, alias=alias)
|
||||
if value is not None:
|
||||
params_to_process[get_validation_alias(field)] = value
|
||||
processed_keys.add(alias or get_validation_alias(field))
|
||||
params_to_process[field.alias] = value
|
||||
processed_keys.add(alias or field.alias)
|
||||
|
||||
for key in received_params.keys():
|
||||
if key not in processed_keys:
|
||||
@@ -845,7 +847,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, get_validation_alias(field))
|
||||
loc = (field_info.in_.value, field.alias)
|
||||
v_, errors_ = _validate_value_with_model_field(
|
||||
field=field, value=value, values=values, loc=loc
|
||||
)
|
||||
@@ -934,8 +936,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[get_validation_alias(field)] = value
|
||||
field_aliases = {get_validation_alias(field) for field in body_fields}
|
||||
values[field.alias] = value
|
||||
field_aliases = {field.alias for field in body_fields}
|
||||
for key in received_body.keys():
|
||||
if key not in field_aliases:
|
||||
param_values = received_body.getlist(key)
|
||||
@@ -977,11 +979,11 @@ async def request_body_to_args(
|
||||
)
|
||||
return {first_field.name: v_}, errors_
|
||||
for field in body_fields:
|
||||
loc = ("body", get_validation_alias(field))
|
||||
loc = ("body", field.alias)
|
||||
value: Optional[Any] = None
|
||||
if body_to_process is not None:
|
||||
try:
|
||||
value = body_to_process.get(get_validation_alias(field))
|
||||
value = body_to_process.get(field.alias)
|
||||
# If the received body is a list, not a dict
|
||||
except AttributeError:
|
||||
errors.append(get_missing_field_error(loc))
|
||||
@@ -1060,8 +1062,3 @@ 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
|
||||
|
||||
@@ -19,7 +19,6 @@ 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
|
||||
@@ -142,7 +141,7 @@ def _get_openapi_operation_parameters(
|
||||
field_mapping=field_mapping,
|
||||
separate_input_output_schemas=separate_input_output_schemas,
|
||||
)
|
||||
name = get_validation_alias(param)
|
||||
name = param.alias
|
||||
convert_underscores = getattr(
|
||||
param.field_info,
|
||||
"convert_underscores",
|
||||
@@ -150,7 +149,7 @@ def _get_openapi_operation_parameters(
|
||||
)
|
||||
if (
|
||||
param_type == ParamTypes.header
|
||||
and name == param.name
|
||||
and param.alias == param.name
|
||||
and convert_underscores
|
||||
):
|
||||
name = param.name.replace("_", "-")
|
||||
|
||||
@@ -115,10 +115,6 @@ 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,
|
||||
@@ -575,10 +571,6 @@ 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,
|
||||
|
||||
@@ -3,6 +3,7 @@ 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
|
||||
@@ -114,13 +115,21 @@ 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}
|
||||
return {"p": p.p} # pragma: no cover
|
||||
|
||||
|
||||
@pytest.mark.parametrize(
|
||||
"path",
|
||||
[
|
||||
"/required-list-alias",
|
||||
pytest.param(
|
||||
"/required-list-alias",
|
||||
marks=pytest.mark.xfail(
|
||||
raises=AssertionError,
|
||||
condition=PYDANTIC_V2,
|
||||
reason="Fails only with PDv2 models",
|
||||
strict=False,
|
||||
),
|
||||
),
|
||||
"/model-required-list-alias",
|
||||
],
|
||||
)
|
||||
@@ -244,7 +253,7 @@ class BodyModelRequiredListValidationAlias(BaseModel):
|
||||
async def read_model_required_list_validation_alias(
|
||||
p: BodyModelRequiredListValidationAlias,
|
||||
):
|
||||
return {"p": p.p}
|
||||
return {"p": p.p} # pragma: no cover
|
||||
|
||||
|
||||
@needs_pydanticv2
|
||||
@@ -275,7 +284,10 @@ def test_required_list_validation_alias_schema(path: str):
|
||||
@pytest.mark.parametrize(
|
||||
"path",
|
||||
[
|
||||
"/required-list-validation-alias",
|
||||
pytest.param(
|
||||
"/required-list-validation-alias",
|
||||
marks=pytest.mark.xfail(raises=AssertionError, strict=False),
|
||||
),
|
||||
"/model-required-list-validation-alias",
|
||||
],
|
||||
)
|
||||
@@ -287,7 +299,9 @@ def test_required_list_validation_alias_missing(path: str, json: Union[dict, Non
|
||||
"detail": [
|
||||
{
|
||||
"type": "missing",
|
||||
"loc": IsOneOf(["body"], ["body", "p_val_alias"]),
|
||||
"loc": IsOneOf( # /required-validation-alias fails here
|
||||
["body"], ["body", "p_val_alias"]
|
||||
),
|
||||
"msg": "Field required",
|
||||
"input": IsOneOf(None, {}),
|
||||
}
|
||||
@@ -299,14 +313,19 @@ def test_required_list_validation_alias_missing(path: str, json: Union[dict, Non
|
||||
@pytest.mark.parametrize(
|
||||
"path",
|
||||
[
|
||||
"/required-list-validation-alias",
|
||||
pytest.param(
|
||||
"/required-list-validation-alias",
|
||||
marks=pytest.mark.xfail(raises=AssertionError, strict=False),
|
||||
),
|
||||
"/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
|
||||
assert response.status_code == 422, (
|
||||
response.text # /required-list-validation-alias fails here
|
||||
)
|
||||
|
||||
assert response.json() == {
|
||||
"detail": [
|
||||
@@ -324,14 +343,19 @@ def test_required_list_validation_alias_by_name(path: str):
|
||||
@pytest.mark.parametrize(
|
||||
"path",
|
||||
[
|
||||
"/required-list-validation-alias",
|
||||
pytest.param(
|
||||
"/required-list-validation-alias",
|
||||
marks=pytest.mark.xfail(raises=AssertionError, strict=False),
|
||||
),
|
||||
"/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
|
||||
assert response.status_code == 200, (
|
||||
response.text # /required-list-validation-alias fails here
|
||||
)
|
||||
assert response.json() == {"p": ["hello", "world"]}
|
||||
|
||||
|
||||
@@ -362,7 +386,7 @@ class BodyModelRequiredListAliasAndValidationAlias(BaseModel):
|
||||
def read_model_required_list_alias_and_validation_alias(
|
||||
p: BodyModelRequiredListAliasAndValidationAlias,
|
||||
):
|
||||
return {"p": p.p}
|
||||
return {"p": p.p} # pragma: no cover
|
||||
|
||||
|
||||
@needs_pydanticv2
|
||||
@@ -396,7 +420,10 @@ def test_required_list_alias_and_validation_alias_schema(path: str):
|
||||
@pytest.mark.parametrize(
|
||||
"path",
|
||||
[
|
||||
"/required-list-alias-and-validation-alias",
|
||||
pytest.param(
|
||||
"/required-list-alias-and-validation-alias",
|
||||
marks=pytest.mark.xfail(raises=AssertionError, strict=False),
|
||||
),
|
||||
"/model-required-list-alias-and-validation-alias",
|
||||
],
|
||||
)
|
||||
@@ -408,7 +435,9 @@ def test_required_list_alias_and_validation_alias_missing(path: str, json):
|
||||
"detail": [
|
||||
{
|
||||
"type": "missing",
|
||||
"loc": IsOneOf(["body"], ["body", "p_val_alias"]),
|
||||
"loc": IsOneOf( # /required-list-alias-and-validation-alias fails here
|
||||
["body"], ["body", "p_val_alias"]
|
||||
),
|
||||
"msg": "Field required",
|
||||
"input": IsOneOf(None, {}),
|
||||
}
|
||||
@@ -420,7 +449,10 @@ def test_required_list_alias_and_validation_alias_missing(path: str, json):
|
||||
@pytest.mark.parametrize(
|
||||
"path",
|
||||
[
|
||||
"/required-list-alias-and-validation-alias",
|
||||
pytest.param(
|
||||
"/required-list-alias-and-validation-alias",
|
||||
marks=pytest.mark.xfail(raises=AssertionError, strict=False),
|
||||
),
|
||||
"/model-required-list-alias-and-validation-alias",
|
||||
],
|
||||
)
|
||||
@@ -432,7 +464,7 @@ def test_required_list_alias_and_validation_alias_by_name(path: str):
|
||||
"detail": [
|
||||
{
|
||||
"type": "missing",
|
||||
"loc": [
|
||||
"loc": [ # /required-list-alias-and-validation-alias fails here
|
||||
"body",
|
||||
"p_val_alias",
|
||||
],
|
||||
@@ -447,7 +479,10 @@ def test_required_list_alias_and_validation_alias_by_name(path: str):
|
||||
@pytest.mark.parametrize(
|
||||
"path",
|
||||
[
|
||||
"/required-list-alias-and-validation-alias",
|
||||
pytest.param(
|
||||
"/required-list-alias-and-validation-alias",
|
||||
marks=pytest.mark.xfail(raises=AssertionError, strict=False),
|
||||
),
|
||||
"/model-required-list-alias-and-validation-alias",
|
||||
],
|
||||
)
|
||||
@@ -472,12 +507,17 @@ def test_required_list_alias_and_validation_alias_by_alias(path: str):
|
||||
@pytest.mark.parametrize(
|
||||
"path",
|
||||
[
|
||||
"/required-list-alias-and-validation-alias",
|
||||
pytest.param(
|
||||
"/required-list-alias-and-validation-alias",
|
||||
marks=pytest.mark.xfail(raises=AssertionError, strict=False),
|
||||
),
|
||||
"/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
|
||||
assert response.status_code == 200, (
|
||||
response.text # /required-list-alias-and-validation-alias fails here
|
||||
)
|
||||
assert response.json() == {"p": ["hello", "world"]}
|
||||
|
||||
@@ -3,6 +3,7 @@ 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
|
||||
@@ -148,7 +149,15 @@ async def read_model_optional_list_alias(p: BodyModelOptionalListAlias):
|
||||
@pytest.mark.parametrize(
|
||||
"path",
|
||||
[
|
||||
"/optional-list-alias",
|
||||
pytest.param(
|
||||
"/optional-list-alias",
|
||||
marks=pytest.mark.xfail(
|
||||
raises=AssertionError,
|
||||
strict=False,
|
||||
condition=PYDANTIC_V2,
|
||||
reason="Fails only with PDv2",
|
||||
),
|
||||
),
|
||||
"/model-optional-list-alias",
|
||||
],
|
||||
)
|
||||
@@ -374,7 +383,10 @@ def test_optional_list_validation_alias_missing_empty_dict(path: str):
|
||||
@pytest.mark.parametrize(
|
||||
"path",
|
||||
[
|
||||
"/optional-list-validation-alias",
|
||||
pytest.param(
|
||||
"/optional-list-validation-alias",
|
||||
marks=pytest.mark.xfail(raises=AssertionError, strict=False),
|
||||
),
|
||||
"/model-optional-list-validation-alias",
|
||||
],
|
||||
)
|
||||
@@ -382,14 +394,17 @@ 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}
|
||||
assert response.json() == {"p": None} # /optional-list-validation-alias fails here
|
||||
|
||||
|
||||
@needs_pydanticv2
|
||||
@pytest.mark.parametrize(
|
||||
"path",
|
||||
[
|
||||
"/optional-list-validation-alias",
|
||||
pytest.param(
|
||||
"/optional-list-validation-alias",
|
||||
marks=pytest.mark.xfail(raises=AssertionError, strict=False),
|
||||
),
|
||||
"/model-optional-list-validation-alias",
|
||||
],
|
||||
)
|
||||
@@ -397,7 +412,9 @@ 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() == {"p": ["hello", "world"]}
|
||||
assert response.json() == { # /optional-list-validation-alias fails here
|
||||
"p": ["hello", "world"]
|
||||
}
|
||||
|
||||
|
||||
# =====================================================================================
|
||||
@@ -544,7 +561,10 @@ def test_optional_list_alias_and_validation_alias_by_name(path: str):
|
||||
@pytest.mark.parametrize(
|
||||
"path",
|
||||
[
|
||||
"/optional-list-alias-and-validation-alias",
|
||||
pytest.param(
|
||||
"/optional-list-alias-and-validation-alias",
|
||||
marks=pytest.mark.xfail(raises=AssertionError, strict=False),
|
||||
),
|
||||
"/model-optional-list-alias-and-validation-alias",
|
||||
],
|
||||
)
|
||||
@@ -552,14 +572,19 @@ 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}
|
||||
assert response.json() == {
|
||||
"p": None # /optional-list-alias-and-validation-alias fails here
|
||||
}
|
||||
|
||||
|
||||
@needs_pydanticv2
|
||||
@pytest.mark.parametrize(
|
||||
"path",
|
||||
[
|
||||
"/optional-list-alias-and-validation-alias",
|
||||
pytest.param(
|
||||
"/optional-list-alias-and-validation-alias",
|
||||
marks=pytest.mark.xfail(raises=AssertionError, strict=False),
|
||||
),
|
||||
"/model-optional-list-alias-and-validation-alias",
|
||||
],
|
||||
)
|
||||
@@ -568,7 +593,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": [
|
||||
"p": [ # /optional-list-alias-and-validation-alias fails here
|
||||
"hello",
|
||||
"world",
|
||||
]
|
||||
|
||||
@@ -3,6 +3,7 @@ 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
|
||||
@@ -143,7 +144,15 @@ async def read_model_optional_alias(p: BodyModelOptionalAlias):
|
||||
@pytest.mark.parametrize(
|
||||
"path",
|
||||
[
|
||||
"/optional-alias",
|
||||
pytest.param(
|
||||
"/optional-alias",
|
||||
marks=pytest.mark.xfail(
|
||||
raises=AssertionError,
|
||||
strict=False,
|
||||
condition=PYDANTIC_V2,
|
||||
reason="Fails only with PDv2",
|
||||
),
|
||||
),
|
||||
"/model-optional-alias",
|
||||
],
|
||||
)
|
||||
@@ -355,7 +364,10 @@ def test_model_optional_validation_alias_missing_empty_dict(path: str):
|
||||
@pytest.mark.parametrize(
|
||||
"path",
|
||||
[
|
||||
"/optional-validation-alias",
|
||||
pytest.param(
|
||||
"/optional-validation-alias",
|
||||
marks=pytest.mark.xfail(raises=AssertionError, strict=False),
|
||||
),
|
||||
"/model-optional-validation-alias",
|
||||
],
|
||||
)
|
||||
@@ -363,14 +375,17 @@ 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}
|
||||
assert response.json() == {"p": None} # /optional-validation-alias fails here
|
||||
|
||||
|
||||
@needs_pydanticv2
|
||||
@pytest.mark.parametrize(
|
||||
"path",
|
||||
[
|
||||
"/optional-validation-alias",
|
||||
pytest.param(
|
||||
"/optional-validation-alias",
|
||||
marks=pytest.mark.xfail(raises=AssertionError, strict=False),
|
||||
),
|
||||
"/model-optional-validation-alias",
|
||||
],
|
||||
)
|
||||
@@ -378,7 +393,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"}
|
||||
assert response.json() == {"p": "hello"} # /optional-validation-alias fails here
|
||||
|
||||
|
||||
# =====================================================================================
|
||||
@@ -518,7 +533,10 @@ def test_optional_alias_and_validation_alias_by_name(path: str):
|
||||
@pytest.mark.parametrize(
|
||||
"path",
|
||||
[
|
||||
"/optional-alias-and-validation-alias",
|
||||
pytest.param(
|
||||
"/optional-alias-and-validation-alias",
|
||||
marks=pytest.mark.xfail(raises=AssertionError, strict=False),
|
||||
),
|
||||
"/model-optional-alias-and-validation-alias",
|
||||
],
|
||||
)
|
||||
@@ -526,14 +544,19 @@ 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}
|
||||
assert response.json() == {
|
||||
"p": None # /optional-alias-and-validation-alias fails here
|
||||
}
|
||||
|
||||
|
||||
@needs_pydanticv2
|
||||
@pytest.mark.parametrize(
|
||||
"path",
|
||||
[
|
||||
"/optional-alias-and-validation-alias",
|
||||
pytest.param(
|
||||
"/optional-alias-and-validation-alias",
|
||||
marks=pytest.mark.xfail(raises=AssertionError, strict=False),
|
||||
),
|
||||
"/model-optional-alias-and-validation-alias",
|
||||
],
|
||||
)
|
||||
@@ -541,4 +564,6 @@ 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"}
|
||||
assert response.json() == {
|
||||
"p": "hello" # /optional-alias-and-validation-alias fails here
|
||||
}
|
||||
|
||||
@@ -3,6 +3,7 @@ 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
|
||||
@@ -117,7 +118,15 @@ async def read_model_required_alias(p: BodyModelRequiredAlias):
|
||||
@pytest.mark.parametrize(
|
||||
"path",
|
||||
[
|
||||
"/required-alias",
|
||||
pytest.param(
|
||||
"/required-alias",
|
||||
marks=pytest.mark.xfail(
|
||||
raises=AssertionError,
|
||||
condition=PYDANTIC_V2,
|
||||
reason="Fails only with PDv2",
|
||||
strict=False,
|
||||
),
|
||||
),
|
||||
"/model-required-alias",
|
||||
],
|
||||
)
|
||||
@@ -261,7 +270,10 @@ def test_required_validation_alias_schema(path: str):
|
||||
@pytest.mark.parametrize(
|
||||
"path",
|
||||
[
|
||||
"/required-validation-alias",
|
||||
pytest.param(
|
||||
"/required-validation-alias",
|
||||
marks=pytest.mark.xfail(raises=AssertionError, strict=False),
|
||||
),
|
||||
"/model-required-validation-alias",
|
||||
],
|
||||
)
|
||||
@@ -275,7 +287,9 @@ def test_required_validation_alias_missing(
|
||||
"detail": [
|
||||
{
|
||||
"type": "missing",
|
||||
"loc": IsOneOf(["body", "p_val_alias"], ["body"]),
|
||||
"loc": IsOneOf( # /required-validation-alias fails here
|
||||
["body", "p_val_alias"], ["body"]
|
||||
),
|
||||
"msg": "Field required",
|
||||
"input": IsOneOf(None, {}),
|
||||
}
|
||||
@@ -287,14 +301,19 @@ def test_required_validation_alias_missing(
|
||||
@pytest.mark.parametrize(
|
||||
"path",
|
||||
[
|
||||
"/required-validation-alias",
|
||||
pytest.param(
|
||||
"/required-validation-alias",
|
||||
marks=pytest.mark.xfail(raises=AssertionError, strict=False),
|
||||
),
|
||||
"/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, response.text
|
||||
assert response.status_code == 422, ( # /required-validation-alias fails here
|
||||
response.text
|
||||
)
|
||||
|
||||
assert response.json() == {
|
||||
"detail": [
|
||||
@@ -312,14 +331,19 @@ def test_required_validation_alias_by_name(path: str):
|
||||
@pytest.mark.parametrize(
|
||||
"path",
|
||||
[
|
||||
"/required-validation-alias",
|
||||
pytest.param(
|
||||
"/required-validation-alias",
|
||||
marks=pytest.mark.xfail(raises=AssertionError, strict=False),
|
||||
),
|
||||
"/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, response.text
|
||||
assert response.status_code == 200, ( # /required-validation-alias fails here
|
||||
response.text
|
||||
)
|
||||
|
||||
assert response.json() == {"p": "hello"}
|
||||
|
||||
@@ -381,7 +405,10 @@ def test_required_alias_and_validation_alias_schema(path: str):
|
||||
@pytest.mark.parametrize(
|
||||
"path",
|
||||
[
|
||||
"/required-alias-and-validation-alias",
|
||||
pytest.param(
|
||||
"/required-alias-and-validation-alias",
|
||||
marks=pytest.mark.xfail(raises=AssertionError, strict=False),
|
||||
),
|
||||
"/model-required-alias-and-validation-alias",
|
||||
],
|
||||
)
|
||||
@@ -395,7 +422,9 @@ def test_required_alias_and_validation_alias_missing(
|
||||
"detail": [
|
||||
{
|
||||
"type": "missing",
|
||||
"loc": IsOneOf(["body"], ["body", "p_val_alias"]),
|
||||
"loc": IsOneOf( # /required-alias-and-validation-alias fails here
|
||||
["body"], ["body", "p_val_alias"]
|
||||
),
|
||||
"msg": "Field required",
|
||||
"input": IsOneOf(None, {}),
|
||||
}
|
||||
@@ -407,7 +436,10 @@ def test_required_alias_and_validation_alias_missing(
|
||||
@pytest.mark.parametrize(
|
||||
"path",
|
||||
[
|
||||
"/required-alias-and-validation-alias",
|
||||
pytest.param(
|
||||
"/required-alias-and-validation-alias",
|
||||
marks=pytest.mark.xfail(raises=AssertionError, strict=False),
|
||||
),
|
||||
"/model-required-alias-and-validation-alias",
|
||||
],
|
||||
)
|
||||
@@ -422,7 +454,7 @@ def test_required_alias_and_validation_alias_by_name(path: str):
|
||||
"type": "missing",
|
||||
"loc": [
|
||||
"body",
|
||||
"p_val_alias",
|
||||
"p_val_alias", # /required-alias-and-validation-alias fails here
|
||||
],
|
||||
"msg": "Field required",
|
||||
"input": IsOneOf(None, {"p": "hello"}),
|
||||
@@ -435,14 +467,19 @@ def test_required_alias_and_validation_alias_by_name(path: str):
|
||||
@pytest.mark.parametrize(
|
||||
"path",
|
||||
[
|
||||
"/required-alias-and-validation-alias",
|
||||
pytest.param(
|
||||
"/required-alias-and-validation-alias",
|
||||
marks=pytest.mark.xfail(raises=AssertionError, strict=False),
|
||||
),
|
||||
"/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
|
||||
assert response.status_code == 422, (
|
||||
response.text # /required-alias-and-validation-alias fails here
|
||||
)
|
||||
|
||||
assert response.json() == {
|
||||
"detail": [
|
||||
@@ -460,13 +497,18 @@ def test_required_alias_and_validation_alias_by_alias(path: str):
|
||||
@pytest.mark.parametrize(
|
||||
"path",
|
||||
[
|
||||
"/required-alias-and-validation-alias",
|
||||
pytest.param(
|
||||
"/required-alias-and-validation-alias",
|
||||
marks=pytest.mark.xfail(raises=AssertionError, strict=False),
|
||||
),
|
||||
"/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
|
||||
assert response.status_code == 200, (
|
||||
response.text # /required-alias-and-validation-alias fails here
|
||||
)
|
||||
|
||||
assert response.json() == {"p": "hello"}
|
||||
|
||||
@@ -157,7 +157,10 @@ def test_optional_alias_by_name(path: str):
|
||||
"path",
|
||||
[
|
||||
"/optional-alias",
|
||||
"/model-optional-alias",
|
||||
pytest.param(
|
||||
"/model-optional-alias",
|
||||
marks=pytest.mark.xfail(raises=AssertionError, strict=False),
|
||||
),
|
||||
],
|
||||
)
|
||||
def test_optional_alias_by_alias(path: str):
|
||||
@@ -165,7 +168,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"}
|
||||
assert response.json() == {"p": "hello"} # /model-optional-alias fails here
|
||||
|
||||
|
||||
# =====================================================================================
|
||||
@@ -191,6 +194,7 @@ 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"],
|
||||
@@ -225,7 +229,10 @@ def test_optional_validation_alias_missing(path: str):
|
||||
@pytest.mark.parametrize(
|
||||
"path",
|
||||
[
|
||||
"/optional-validation-alias",
|
||||
pytest.param(
|
||||
"/optional-validation-alias",
|
||||
marks=pytest.mark.xfail(raises=AssertionError, strict=False),
|
||||
),
|
||||
"/model-optional-validation-alias",
|
||||
],
|
||||
)
|
||||
@@ -241,7 +248,10 @@ def test_optional_validation_alias_by_name(path: str):
|
||||
@pytest.mark.parametrize(
|
||||
"path",
|
||||
[
|
||||
"/optional-validation-alias",
|
||||
pytest.param(
|
||||
"/optional-validation-alias",
|
||||
marks=pytest.mark.xfail(raises=AssertionError, strict=False),
|
||||
),
|
||||
"/model-optional-validation-alias",
|
||||
],
|
||||
)
|
||||
@@ -250,7 +260,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"}
|
||||
assert response.json() == {"p": "hello"} # /optional-validation-alias fails here
|
||||
|
||||
|
||||
# =====================================================================================
|
||||
@@ -278,6 +288,7 @@ def read_model_optional_alias_and_validation_alias(
|
||||
|
||||
|
||||
@needs_pydanticv2
|
||||
@pytest.mark.xfail(raises=AssertionError, strict=False)
|
||||
@pytest.mark.parametrize(
|
||||
"path",
|
||||
[
|
||||
@@ -334,7 +345,10 @@ def test_optional_alias_and_validation_alias_by_name(path: str):
|
||||
@pytest.mark.parametrize(
|
||||
"path",
|
||||
[
|
||||
"/optional-alias-and-validation-alias",
|
||||
pytest.param(
|
||||
"/optional-alias-and-validation-alias",
|
||||
marks=pytest.mark.xfail(raises=AssertionError, strict=False),
|
||||
),
|
||||
"/model-optional-alias-and-validation-alias",
|
||||
],
|
||||
)
|
||||
@@ -343,14 +357,19 @@ 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}
|
||||
assert response.json() == {
|
||||
"p": None # /optional-alias-and-validation-alias fails here
|
||||
}
|
||||
|
||||
|
||||
@needs_pydanticv2
|
||||
@pytest.mark.parametrize(
|
||||
"path",
|
||||
[
|
||||
"/optional-alias-and-validation-alias",
|
||||
pytest.param(
|
||||
"/optional-alias-and-validation-alias",
|
||||
marks=pytest.mark.xfail(raises=AssertionError, strict=False),
|
||||
),
|
||||
"/model-optional-alias-and-validation-alias",
|
||||
],
|
||||
)
|
||||
@@ -359,4 +378,6 @@ 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"}
|
||||
assert response.json() == {
|
||||
"p": "hello" # /optional-alias-and-validation-alias fails here
|
||||
}
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
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
|
||||
@@ -102,7 +103,7 @@ class CookieModelRequiredAlias(BaseModel):
|
||||
|
||||
@app.get("/model-required-alias")
|
||||
async def read_model_required_alias(p: Annotated[CookieModelRequiredAlias, Cookie()]):
|
||||
return {"p": p.p}
|
||||
return {"p": p.p} # pragma: no cover
|
||||
|
||||
|
||||
@pytest.mark.parametrize(
|
||||
@@ -157,7 +158,15 @@ def test_required_alias_missing(path: str):
|
||||
"path",
|
||||
[
|
||||
"/required-alias",
|
||||
"/model-required-alias",
|
||||
pytest.param(
|
||||
"/model-required-alias",
|
||||
marks=pytest.mark.xfail(
|
||||
raises=AssertionError,
|
||||
condition=PYDANTIC_V2,
|
||||
reason="Fails only with PDv2 models",
|
||||
strict=False,
|
||||
),
|
||||
),
|
||||
],
|
||||
)
|
||||
def test_required_alias_by_name(path: str):
|
||||
@@ -174,7 +183,7 @@ def test_required_alias_by_name(path: str):
|
||||
"msg": "Field required",
|
||||
"input": IsOneOf(
|
||||
None,
|
||||
{"p": "hello"},
|
||||
{"p": "hello"}, # /model-required-alias PDv2 fails here
|
||||
),
|
||||
}
|
||||
]
|
||||
@@ -197,14 +206,19 @@ def test_required_alias_by_name(path: str):
|
||||
"path",
|
||||
[
|
||||
"/required-alias",
|
||||
"/model-required-alias",
|
||||
pytest.param(
|
||||
"/model-required-alias",
|
||||
marks=pytest.mark.xfail(raises=AssertionError, strict=False),
|
||||
),
|
||||
],
|
||||
)
|
||||
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, response.text
|
||||
assert response.status_code == 200, ( # /model-required-alias fails here
|
||||
response.text
|
||||
)
|
||||
assert response.json() == {"p": "hello"}
|
||||
|
||||
|
||||
@@ -231,6 +245,7 @@ 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"],
|
||||
@@ -250,7 +265,10 @@ def test_required_validation_alias_schema(path: str):
|
||||
@pytest.mark.parametrize(
|
||||
"path",
|
||||
[
|
||||
"/required-validation-alias",
|
||||
pytest.param(
|
||||
"/required-validation-alias",
|
||||
marks=pytest.mark.xfail(raises=AssertionError, strict=False),
|
||||
),
|
||||
"/model-required-validation-alias",
|
||||
],
|
||||
)
|
||||
@@ -264,7 +282,7 @@ def test_required_validation_alias_missing(path: str):
|
||||
"type": "missing",
|
||||
"loc": [
|
||||
"cookie",
|
||||
"p_val_alias",
|
||||
"p_val_alias", # /required-validation-alias fails here
|
||||
],
|
||||
"msg": "Field required",
|
||||
"input": IsOneOf(None, {}),
|
||||
@@ -277,7 +295,10 @@ def test_required_validation_alias_missing(path: str):
|
||||
@pytest.mark.parametrize(
|
||||
"path",
|
||||
[
|
||||
"/required-validation-alias",
|
||||
pytest.param(
|
||||
"/required-validation-alias",
|
||||
marks=pytest.mark.xfail(raises=AssertionError, strict=False),
|
||||
),
|
||||
"/model-required-validation-alias",
|
||||
],
|
||||
)
|
||||
@@ -285,7 +306,9 @@ 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, response.text
|
||||
assert response.status_code == 422, ( # /required-validation-alias fails here
|
||||
response.text
|
||||
)
|
||||
|
||||
assert response.json() == {
|
||||
"detail": [
|
||||
@@ -303,7 +326,10 @@ def test_required_validation_alias_by_name(path: str):
|
||||
@pytest.mark.parametrize(
|
||||
"path",
|
||||
[
|
||||
"/required-validation-alias",
|
||||
pytest.param(
|
||||
"/required-validation-alias",
|
||||
marks=pytest.mark.xfail(raises=AssertionError, strict=False),
|
||||
),
|
||||
"/model-required-validation-alias",
|
||||
],
|
||||
)
|
||||
@@ -311,7 +337,9 @@ 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, response.text
|
||||
assert response.status_code == 200, ( # /required-validation-alias fails here
|
||||
response.text
|
||||
)
|
||||
|
||||
assert response.json() == {"p": "hello"}
|
||||
|
||||
@@ -339,6 +367,7 @@ def read_model_required_alias_and_validation_alias(
|
||||
|
||||
|
||||
@needs_pydanticv2
|
||||
@pytest.mark.xfail(raises=AssertionError, strict=False)
|
||||
@pytest.mark.parametrize(
|
||||
"path",
|
||||
[
|
||||
@@ -361,7 +390,10 @@ def test_required_alias_and_validation_alias_schema(path: str):
|
||||
@pytest.mark.parametrize(
|
||||
"path",
|
||||
[
|
||||
"/required-alias-and-validation-alias",
|
||||
pytest.param(
|
||||
"/required-alias-and-validation-alias",
|
||||
marks=pytest.mark.xfail(raises=AssertionError, strict=False),
|
||||
),
|
||||
"/model-required-alias-and-validation-alias",
|
||||
],
|
||||
)
|
||||
@@ -375,7 +407,7 @@ def test_required_alias_and_validation_alias_missing(path: str):
|
||||
"type": "missing",
|
||||
"loc": [
|
||||
"cookie",
|
||||
"p_val_alias",
|
||||
"p_val_alias", # /required-alias-and-validation-alias fails here
|
||||
],
|
||||
"msg": "Field required",
|
||||
"input": IsOneOf(None, {}),
|
||||
@@ -385,6 +417,7 @@ def test_required_alias_and_validation_alias_missing(path: str):
|
||||
|
||||
|
||||
@needs_pydanticv2
|
||||
@pytest.mark.xfail(raises=AssertionError, strict=False)
|
||||
@pytest.mark.parametrize(
|
||||
"path",
|
||||
[
|
||||
@@ -404,10 +437,10 @@ def test_required_alias_and_validation_alias_by_name(path: str):
|
||||
"type": "missing",
|
||||
"loc": [
|
||||
"cookie",
|
||||
"p_val_alias",
|
||||
"p_val_alias", # /required-alias-and-validation-alias fails here
|
||||
],
|
||||
"msg": "Field required",
|
||||
"input": IsOneOf(
|
||||
"input": IsOneOf( # /model-alias-and-validation-alias fails here
|
||||
None,
|
||||
{"p": "hello"},
|
||||
),
|
||||
@@ -417,6 +450,7 @@ def test_required_alias_and_validation_alias_by_name(path: str):
|
||||
|
||||
|
||||
@needs_pydanticv2
|
||||
@pytest.mark.xfail(raises=AssertionError, strict=False)
|
||||
@pytest.mark.parametrize(
|
||||
"path",
|
||||
[
|
||||
@@ -428,7 +462,9 @@ 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
|
||||
assert (
|
||||
response.status_code == 422 # /required-alias-and-validation-alias fails here
|
||||
)
|
||||
|
||||
assert response.json() == {
|
||||
"detail": [
|
||||
@@ -436,7 +472,7 @@ def test_required_alias_and_validation_alias_by_alias(path: str):
|
||||
"type": "missing",
|
||||
"loc": ["cookie", "p_val_alias"],
|
||||
"msg": "Field required",
|
||||
"input": IsOneOf(
|
||||
"input": IsOneOf( # /model-alias-and-validation-alias fails here
|
||||
None,
|
||||
{"p_alias": "hello"},
|
||||
),
|
||||
@@ -449,7 +485,10 @@ def test_required_alias_and_validation_alias_by_alias(path: str):
|
||||
@pytest.mark.parametrize(
|
||||
"path",
|
||||
[
|
||||
"/required-alias-and-validation-alias",
|
||||
pytest.param(
|
||||
"/required-alias-and-validation-alias",
|
||||
marks=pytest.mark.xfail(raises=AssertionError, strict=False),
|
||||
),
|
||||
"/model-required-alias-and-validation-alias",
|
||||
],
|
||||
)
|
||||
@@ -457,6 +496,8 @@ 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
|
||||
assert response.status_code == 200, (
|
||||
response.text # /required-alias-and-validation-alias fails here
|
||||
)
|
||||
|
||||
assert response.json() == {"p": "hello"}
|
||||
|
||||
@@ -3,6 +3,7 @@ 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
|
||||
|
||||
@@ -133,6 +134,12 @@ 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",
|
||||
[
|
||||
@@ -327,8 +334,14 @@ def test_list_validation_alias_schema(path: str):
|
||||
@pytest.mark.parametrize(
|
||||
"path",
|
||||
[
|
||||
"/list-bytes-validation-alias",
|
||||
"/list-uploadfile-validation-alias",
|
||||
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),
|
||||
),
|
||||
],
|
||||
)
|
||||
def test_list_validation_alias_missing(path: str):
|
||||
@@ -339,7 +352,7 @@ def test_list_validation_alias_missing(path: str):
|
||||
"detail": [
|
||||
{
|
||||
"type": "missing",
|
||||
"loc": [
|
||||
"loc": [ # /list-*-validation-alias fail here
|
||||
"body",
|
||||
"p_val_alias",
|
||||
],
|
||||
@@ -354,16 +367,24 @@ def test_list_validation_alias_missing(path: str):
|
||||
@pytest.mark.parametrize(
|
||||
"path",
|
||||
[
|
||||
"/list-bytes-validation-alias",
|
||||
"/list-uploadfile-validation-alias",
|
||||
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),
|
||||
),
|
||||
],
|
||||
)
|
||||
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, response.text
|
||||
assert response.status_code == 422, ( # /list-*-validation-alias fail here
|
||||
response.text
|
||||
)
|
||||
|
||||
assert response.json() == {
|
||||
assert response.json() == { # pragma: no cover
|
||||
"detail": [
|
||||
{
|
||||
"type": "missing",
|
||||
@@ -375,6 +396,7 @@ def test_list_validation_alias_by_name(path: str):
|
||||
}
|
||||
|
||||
|
||||
@pytest.mark.xfail(raises=AssertionError, strict=False)
|
||||
@needs_pydanticv2
|
||||
@pytest.mark.parametrize(
|
||||
"path",
|
||||
@@ -388,8 +410,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
|
||||
assert response.json() == {"file_size": [5, 5]}
|
||||
assert response.status_code == 200, response.text # all 2 fail here
|
||||
assert response.json() == {"file_size": [5, 5]} # pragma: no cover
|
||||
|
||||
|
||||
# =====================================================================================
|
||||
@@ -464,8 +486,14 @@ def test_list_alias_and_validation_alias_schema(path: str):
|
||||
@pytest.mark.parametrize(
|
||||
"path",
|
||||
[
|
||||
"/list-bytes-alias-and-validation-alias",
|
||||
"/list-uploadfile-alias-and-validation-alias",
|
||||
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),
|
||||
),
|
||||
],
|
||||
)
|
||||
def test_list_alias_and_validation_alias_missing(path: str):
|
||||
@@ -478,7 +506,7 @@ def test_list_alias_and_validation_alias_missing(path: str):
|
||||
"type": "missing",
|
||||
"loc": [
|
||||
"body",
|
||||
"p_val_alias",
|
||||
"p_val_alias", # /list-*-alias-and-validation-alias fail here
|
||||
],
|
||||
"msg": "Field required",
|
||||
"input": None,
|
||||
@@ -487,6 +515,7 @@ def test_list_alias_and_validation_alias_missing(path: str):
|
||||
}
|
||||
|
||||
|
||||
@pytest.mark.xfail(raises=AssertionError, strict=False)
|
||||
@needs_pydanticv2
|
||||
@pytest.mark.parametrize(
|
||||
"path",
|
||||
@@ -506,7 +535,7 @@ def test_list_alias_and_validation_alias_by_name(path: str):
|
||||
"type": "missing",
|
||||
"loc": [
|
||||
"body",
|
||||
"p_val_alias",
|
||||
"p_val_alias", # /list-*-alias-and-validation-alias fail here
|
||||
],
|
||||
"msg": "Field required",
|
||||
"input": None,
|
||||
@@ -519,16 +548,24 @@ def test_list_alias_and_validation_alias_by_name(path: str):
|
||||
@pytest.mark.parametrize(
|
||||
"path",
|
||||
[
|
||||
"/list-bytes-alias-and-validation-alias",
|
||||
"/list-uploadfile-alias-and-validation-alias",
|
||||
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),
|
||||
),
|
||||
],
|
||||
)
|
||||
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
|
||||
assert response.status_code == 422, (
|
||||
response.text # /list-*-alias-and-validation-alias fails here
|
||||
)
|
||||
|
||||
assert response.json() == {
|
||||
assert response.json() == { # pragma: no cover
|
||||
"detail": [
|
||||
{
|
||||
"type": "missing",
|
||||
@@ -540,6 +577,7 @@ def test_list_alias_and_validation_alias_by_alias(path: str):
|
||||
}
|
||||
|
||||
|
||||
@pytest.mark.xfail(raises=AssertionError, strict=False)
|
||||
@needs_pydanticv2
|
||||
@pytest.mark.parametrize(
|
||||
"path",
|
||||
@@ -553,5 +591,7 @@ 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, response.text
|
||||
assert response.json() == {"file_size": [5, 5]}
|
||||
assert response.status_code == 200, ( # all 2 fail here
|
||||
response.text
|
||||
)
|
||||
assert response.json() == {"file_size": [5, 5]} # pragma: no cover
|
||||
|
||||
@@ -3,6 +3,7 @@ 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
|
||||
|
||||
@@ -106,6 +107,12 @@ 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",
|
||||
[
|
||||
@@ -259,30 +266,44 @@ def test_optional_validation_alias_missing(path: str):
|
||||
@pytest.mark.parametrize(
|
||||
"path",
|
||||
[
|
||||
"/optional-bytes-validation-alias",
|
||||
"/optional-uploadfile-validation-alias",
|
||||
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),
|
||||
),
|
||||
],
|
||||
)
|
||||
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() == {"file_size": None}
|
||||
assert response.json() == { # /optional-*-validation-alias fail here
|
||||
"file_size": None
|
||||
}
|
||||
|
||||
|
||||
@needs_pydanticv2
|
||||
@pytest.mark.parametrize(
|
||||
"path",
|
||||
[
|
||||
"/optional-bytes-validation-alias",
|
||||
"/optional-uploadfile-validation-alias",
|
||||
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),
|
||||
),
|
||||
],
|
||||
)
|
||||
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}
|
||||
assert response.json() == {"file_size": 5} # /optional-*-validation-alias fail here
|
||||
|
||||
|
||||
# =====================================================================================
|
||||
@@ -382,8 +403,14 @@ def test_optional_alias_and_validation_alias_by_name(path: str):
|
||||
@pytest.mark.parametrize(
|
||||
"path",
|
||||
[
|
||||
"/optional-bytes-alias-and-validation-alias",
|
||||
"/optional-uploadfile-alias-and-validation-alias",
|
||||
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),
|
||||
),
|
||||
],
|
||||
)
|
||||
def test_optional_alias_and_validation_alias_by_alias(path: str):
|
||||
@@ -397,12 +424,20 @@ def test_optional_alias_and_validation_alias_by_alias(path: str):
|
||||
@pytest.mark.parametrize(
|
||||
"path",
|
||||
[
|
||||
"/optional-bytes-alias-and-validation-alias",
|
||||
"/optional-uploadfile-alias-and-validation-alias",
|
||||
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),
|
||||
),
|
||||
],
|
||||
)
|
||||
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}
|
||||
assert response.json() == {
|
||||
"file_size": 5
|
||||
} # /optional-*-alias-and-validation-alias fail here
|
||||
|
||||
@@ -3,6 +3,7 @@ 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
|
||||
|
||||
@@ -86,7 +87,15 @@ def test_optional_list_missing(path: str):
|
||||
@pytest.mark.parametrize(
|
||||
"path",
|
||||
[
|
||||
"/optional-list-bytes",
|
||||
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-uploadfile",
|
||||
],
|
||||
)
|
||||
@@ -115,6 +124,12 @@ 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",
|
||||
[
|
||||
@@ -187,7 +202,15 @@ def test_optional_list_alias_by_name(path: str):
|
||||
@pytest.mark.parametrize(
|
||||
"path",
|
||||
[
|
||||
"/optional-list-bytes-alias",
|
||||
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-uploadfile-alias",
|
||||
],
|
||||
)
|
||||
@@ -279,17 +302,30 @@ def test_optional_validation_alias_missing(path: str):
|
||||
@pytest.mark.parametrize(
|
||||
"path",
|
||||
[
|
||||
"/optional-list-bytes-validation-alias",
|
||||
"/optional-list-uploadfile-validation-alias",
|
||||
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),
|
||||
),
|
||||
],
|
||||
)
|
||||
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() == {"file_size": None}
|
||||
assert response.json() == { # /optional-list-uploadfile-validation-alias fails here
|
||||
"file_size": None
|
||||
}
|
||||
|
||||
|
||||
@pytest.mark.xfail(raises=AssertionError, strict=False)
|
||||
@needs_pydanticv2
|
||||
@pytest.mark.parametrize(
|
||||
"path",
|
||||
@@ -304,7 +340,9 @@ 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]}
|
||||
assert response.json() == {
|
||||
"file_size": [5, 5] # /optional-list-*-validation-alias fail here
|
||||
}
|
||||
|
||||
|
||||
# =====================================================================================
|
||||
@@ -406,17 +444,30 @@ def test_optional_list_alias_and_validation_alias_by_name(path: str):
|
||||
@pytest.mark.parametrize(
|
||||
"path",
|
||||
[
|
||||
"/optional-list-bytes-alias-and-validation-alias",
|
||||
"/optional-list-uploadfile-alias-and-validation-alias",
|
||||
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),
|
||||
),
|
||||
],
|
||||
)
|
||||
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 response.json() == {"file_size": None}
|
||||
assert ( # /optional-list-uploadfile-alias-and-validation-alias fails here
|
||||
response.json() == {"file_size": None}
|
||||
)
|
||||
|
||||
|
||||
@pytest.mark.xfail(raises=AssertionError, strict=False)
|
||||
@needs_pydanticv2
|
||||
@pytest.mark.parametrize(
|
||||
"path",
|
||||
@@ -431,4 +482,6 @@ 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]}
|
||||
assert response.json() == {
|
||||
"file_size": [5, 5] # /optional-list-*-alias-and-validation-alias fail here
|
||||
}
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
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
|
||||
|
||||
@@ -111,6 +112,12 @@ 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",
|
||||
[
|
||||
@@ -271,8 +278,14 @@ def test_required_validation_alias_schema(path: str):
|
||||
@pytest.mark.parametrize(
|
||||
"path",
|
||||
[
|
||||
"/required-bytes-validation-alias",
|
||||
"/required-uploadfile-validation-alias",
|
||||
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),
|
||||
),
|
||||
],
|
||||
)
|
||||
def test_required_validation_alias_missing(path: str):
|
||||
@@ -283,7 +296,7 @@ def test_required_validation_alias_missing(path: str):
|
||||
"detail": [
|
||||
{
|
||||
"type": "missing",
|
||||
"loc": [
|
||||
"loc": [ # /required-*-validation-alias fail here
|
||||
"body",
|
||||
"p_val_alias",
|
||||
],
|
||||
@@ -298,16 +311,24 @@ def test_required_validation_alias_missing(path: str):
|
||||
@pytest.mark.parametrize(
|
||||
"path",
|
||||
[
|
||||
"/required-bytes-validation-alias",
|
||||
"/required-uploadfile-validation-alias",
|
||||
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),
|
||||
),
|
||||
],
|
||||
)
|
||||
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, response.text
|
||||
assert response.status_code == 422, ( # /required-*-validation-alias fail here
|
||||
response.text
|
||||
)
|
||||
|
||||
assert response.json() == {
|
||||
assert response.json() == { # pragma: no cover
|
||||
"detail": [
|
||||
{
|
||||
"type": "missing",
|
||||
@@ -323,15 +344,23 @@ def test_required_validation_alias_by_name(path: str):
|
||||
@pytest.mark.parametrize(
|
||||
"path",
|
||||
[
|
||||
"/required-bytes-validation-alias",
|
||||
"/required-uploadfile-validation-alias",
|
||||
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),
|
||||
),
|
||||
],
|
||||
)
|
||||
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, response.text
|
||||
assert response.json() == {"file_size": 5}
|
||||
assert response.status_code == 200, ( # all 2 fail here
|
||||
response.text
|
||||
)
|
||||
assert response.json() == {"file_size": 5} # pragma: no cover
|
||||
|
||||
|
||||
# =====================================================================================
|
||||
@@ -388,8 +417,14 @@ def test_required_alias_and_validation_alias_schema(path: str):
|
||||
@pytest.mark.parametrize(
|
||||
"path",
|
||||
[
|
||||
"/required-bytes-alias-and-validation-alias",
|
||||
"/required-uploadfile-alias-and-validation-alias",
|
||||
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),
|
||||
),
|
||||
],
|
||||
)
|
||||
def test_required_alias_and_validation_alias_missing(path: str):
|
||||
@@ -402,7 +437,7 @@ def test_required_alias_and_validation_alias_missing(path: str):
|
||||
"type": "missing",
|
||||
"loc": [
|
||||
"body",
|
||||
"p_val_alias",
|
||||
"p_val_alias", # /required-*-alias-and-validation-alias fail here
|
||||
],
|
||||
"msg": "Field required",
|
||||
"input": None,
|
||||
@@ -415,8 +450,14 @@ def test_required_alias_and_validation_alias_missing(path: str):
|
||||
@pytest.mark.parametrize(
|
||||
"path",
|
||||
[
|
||||
"/required-bytes-alias-and-validation-alias",
|
||||
"/required-uploadfile-alias-and-validation-alias",
|
||||
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),
|
||||
),
|
||||
],
|
||||
)
|
||||
def test_required_alias_and_validation_alias_by_name(path: str):
|
||||
@@ -430,7 +471,7 @@ def test_required_alias_and_validation_alias_by_name(path: str):
|
||||
"type": "missing",
|
||||
"loc": [
|
||||
"body",
|
||||
"p_val_alias",
|
||||
"p_val_alias", # /required-*-alias-and-validation-alias fail here
|
||||
],
|
||||
"msg": "Field required",
|
||||
"input": None,
|
||||
@@ -443,16 +484,24 @@ def test_required_alias_and_validation_alias_by_name(path: str):
|
||||
@pytest.mark.parametrize(
|
||||
"path",
|
||||
[
|
||||
"/required-bytes-alias-and-validation-alias",
|
||||
"/required-uploadfile-alias-and-validation-alias",
|
||||
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),
|
||||
),
|
||||
],
|
||||
)
|
||||
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
|
||||
assert response.status_code == 422, (
|
||||
response.text # /required-*-alias-and-validation-alias fails here
|
||||
)
|
||||
|
||||
assert response.json() == {
|
||||
assert response.json() == { # pragma: no cover
|
||||
"detail": [
|
||||
{
|
||||
"type": "missing",
|
||||
@@ -468,12 +517,20 @@ def test_required_alias_and_validation_alias_by_alias(path: str):
|
||||
@pytest.mark.parametrize(
|
||||
"path",
|
||||
[
|
||||
"/required-bytes-alias-and-validation-alias",
|
||||
"/required-uploadfile-alias-and-validation-alias",
|
||||
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),
|
||||
),
|
||||
],
|
||||
)
|
||||
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, response.text
|
||||
assert response.json() == {"file_size": 5}
|
||||
assert response.status_code == 200, ( # all 2 fail here
|
||||
response.text
|
||||
)
|
||||
assert response.json() == {"file_size": 5} # pragma: no cover
|
||||
|
||||
@@ -3,6 +3,7 @@ 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
|
||||
@@ -113,13 +114,21 @@ class FormModelRequiredListAlias(BaseModel):
|
||||
async def read_model_required_list_alias(
|
||||
p: Annotated[FormModelRequiredListAlias, Form()],
|
||||
):
|
||||
return {"p": p.p}
|
||||
return {"p": p.p} # pragma: no cover
|
||||
|
||||
|
||||
@pytest.mark.parametrize(
|
||||
"path",
|
||||
[
|
||||
"/required-list-alias",
|
||||
pytest.param(
|
||||
"/required-list-alias",
|
||||
marks=pytest.mark.xfail(
|
||||
raises=AssertionError,
|
||||
condition=PYDANTIC_V2,
|
||||
reason="Fails only with PDv2 models",
|
||||
strict=False,
|
||||
),
|
||||
),
|
||||
"/model-required-list-alias",
|
||||
],
|
||||
)
|
||||
@@ -178,7 +187,15 @@ def test_required_list_alias_missing(path: str):
|
||||
"path",
|
||||
[
|
||||
"/required-list-alias",
|
||||
"/model-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,
|
||||
),
|
||||
),
|
||||
],
|
||||
)
|
||||
def test_required_list_alias_by_name(path: str):
|
||||
@@ -192,7 +209,9 @@ def test_required_list_alias_by_name(path: str):
|
||||
"type": "missing",
|
||||
"loc": ["body", "p_alias"],
|
||||
"msg": "Field required",
|
||||
"input": IsOneOf(None, {"p": ["hello", "world"]}),
|
||||
"input": IsOneOf( # /model-required-list-alias with PDv2 fails here
|
||||
None, {"p": ["hello", "world"]}
|
||||
),
|
||||
}
|
||||
]
|
||||
}
|
||||
@@ -245,7 +264,7 @@ class FormModelRequiredListValidationAlias(BaseModel):
|
||||
async def read_model_required_list_validation_alias(
|
||||
p: Annotated[FormModelRequiredListValidationAlias, Form()],
|
||||
):
|
||||
return {"p": p.p}
|
||||
return {"p": p.p} # pragma: no cover
|
||||
|
||||
|
||||
@needs_pydanticv2
|
||||
@@ -275,7 +294,10 @@ def test_required_list_validation_alias_schema(path: str):
|
||||
@pytest.mark.parametrize(
|
||||
"path",
|
||||
[
|
||||
"/required-list-validation-alias",
|
||||
pytest.param(
|
||||
"/required-list-validation-alias",
|
||||
marks=pytest.mark.xfail(raises=AssertionError, strict=False),
|
||||
),
|
||||
"/model-required-list-validation-alias",
|
||||
],
|
||||
)
|
||||
@@ -289,7 +311,7 @@ def test_required_list_validation_alias_missing(path: str):
|
||||
"type": "missing",
|
||||
"loc": [
|
||||
"body",
|
||||
"p_val_alias",
|
||||
"p_val_alias", # /required-list-validation-alias fails here
|
||||
],
|
||||
"msg": "Field required",
|
||||
"input": IsOneOf(None, {}),
|
||||
@@ -302,14 +324,19 @@ def test_required_list_validation_alias_missing(path: str):
|
||||
@pytest.mark.parametrize(
|
||||
"path",
|
||||
[
|
||||
"/required-list-validation-alias",
|
||||
pytest.param(
|
||||
"/required-list-validation-alias",
|
||||
marks=pytest.mark.xfail(raises=AssertionError, strict=False),
|
||||
),
|
||||
"/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
|
||||
assert response.status_code == 422, (
|
||||
response.text # /required-list-validation-alias fails here
|
||||
)
|
||||
|
||||
assert response.json() == {
|
||||
"detail": [
|
||||
@@ -324,6 +351,7 @@ 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"],
|
||||
@@ -331,9 +359,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
|
||||
assert response.status_code == 200, response.text # both fail here
|
||||
|
||||
assert response.json() == {"p": ["hello", "world"]}
|
||||
assert response.json() == {"p": ["hello", "world"]} # pragma: no cover
|
||||
|
||||
|
||||
# =====================================================================================
|
||||
@@ -361,7 +389,7 @@ class FormModelRequiredListAliasAndValidationAlias(BaseModel):
|
||||
def read_model_required_list_alias_and_validation_alias(
|
||||
p: Annotated[FormModelRequiredListAliasAndValidationAlias, Form()],
|
||||
):
|
||||
return {"p": p.p}
|
||||
return {"p": p.p} # pragma: no cover
|
||||
|
||||
|
||||
@needs_pydanticv2
|
||||
@@ -394,7 +422,10 @@ def test_required_list_alias_and_validation_alias_schema(path: str):
|
||||
@pytest.mark.parametrize(
|
||||
"path",
|
||||
[
|
||||
"/required-list-alias-and-validation-alias",
|
||||
pytest.param(
|
||||
"/required-list-alias-and-validation-alias",
|
||||
marks=pytest.mark.xfail(raises=AssertionError, strict=False),
|
||||
),
|
||||
"/model-required-list-alias-and-validation-alias",
|
||||
],
|
||||
)
|
||||
@@ -408,6 +439,7 @@ 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",
|
||||
@@ -418,6 +450,7 @@ def test_required_list_alias_and_validation_alias_missing(path: str):
|
||||
|
||||
|
||||
@needs_pydanticv2
|
||||
@pytest.mark.xfail(raises=AssertionError, strict=False)
|
||||
@pytest.mark.parametrize(
|
||||
"path",
|
||||
[
|
||||
@@ -435,11 +468,13 @@ 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"]},
|
||||
),
|
||||
}
|
||||
@@ -451,14 +486,19 @@ def test_required_list_alias_and_validation_alias_by_name(path: str):
|
||||
@pytest.mark.parametrize(
|
||||
"path",
|
||||
[
|
||||
"/required-list-alias-and-validation-alias",
|
||||
pytest.param(
|
||||
"/required-list-alias-and-validation-alias",
|
||||
marks=pytest.mark.xfail(raises=AssertionError, strict=False),
|
||||
),
|
||||
"/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 response.status_code == 422
|
||||
assert ( # /required-list-alias-and-validation-alias fails here
|
||||
response.status_code == 422
|
||||
)
|
||||
assert response.json() == {
|
||||
"detail": [
|
||||
{
|
||||
@@ -472,6 +512,7 @@ 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",
|
||||
[
|
||||
@@ -482,5 +523,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
|
||||
assert response.json() == {"p": ["hello", "world"]}
|
||||
assert response.status_code == 200, response.text # both fail here
|
||||
assert response.json() == {"p": ["hello", "world"]} # pragma: no cover
|
||||
|
||||
@@ -3,6 +3,7 @@ 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
|
||||
@@ -114,7 +115,15 @@ async def read_model_optional_list_alias(
|
||||
@pytest.mark.parametrize(
|
||||
"path",
|
||||
[
|
||||
"/optional-list-alias",
|
||||
pytest.param(
|
||||
"/optional-list-alias",
|
||||
marks=pytest.mark.xfail(
|
||||
raises=AssertionError,
|
||||
strict=False,
|
||||
condition=PYDANTIC_V2,
|
||||
reason="Fails only with PDv2",
|
||||
),
|
||||
),
|
||||
"/model-optional-list-alias",
|
||||
],
|
||||
)
|
||||
@@ -267,7 +276,10 @@ def test_optional_list_validation_alias_missing(path: str):
|
||||
@pytest.mark.parametrize(
|
||||
"path",
|
||||
[
|
||||
"/optional-list-validation-alias",
|
||||
pytest.param(
|
||||
"/optional-list-validation-alias",
|
||||
marks=pytest.mark.xfail(raises=AssertionError, strict=False),
|
||||
),
|
||||
"/model-optional-list-validation-alias",
|
||||
],
|
||||
)
|
||||
@@ -275,10 +287,11 @@ 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}
|
||||
assert response.json() == {"p": None} # /optional-list-validation-alias fails here
|
||||
|
||||
|
||||
@needs_pydanticv2
|
||||
@pytest.mark.xfail(raises=AssertionError, strict=False)
|
||||
@pytest.mark.parametrize(
|
||||
"path",
|
||||
["/optional-list-validation-alias", "/model-optional-list-validation-alias"],
|
||||
@@ -286,8 +299,12 @@ 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
|
||||
assert response.json() == {"p": ["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"]
|
||||
}
|
||||
|
||||
|
||||
# =====================================================================================
|
||||
@@ -398,7 +415,10 @@ def test_optional_list_alias_and_validation_alias_by_name(path: str):
|
||||
@pytest.mark.parametrize(
|
||||
"path",
|
||||
[
|
||||
"/optional-list-alias-and-validation-alias",
|
||||
pytest.param(
|
||||
"/optional-list-alias-and-validation-alias",
|
||||
marks=pytest.mark.xfail(raises=AssertionError, strict=False),
|
||||
),
|
||||
"/model-optional-list-alias-and-validation-alias",
|
||||
],
|
||||
)
|
||||
@@ -406,10 +426,13 @@ 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}
|
||||
assert response.json() == {
|
||||
"p": None # /optional-list-alias-and-validation-alias fails here
|
||||
}
|
||||
|
||||
|
||||
@needs_pydanticv2
|
||||
@pytest.mark.xfail(raises=AssertionError, strict=False)
|
||||
@pytest.mark.parametrize(
|
||||
"path",
|
||||
[
|
||||
@@ -420,9 +443,11 @@ 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
|
||||
assert response.status_code == 200, (
|
||||
response.text # /model-optional-list-alias-and-validation-alias fails here
|
||||
)
|
||||
assert response.json() == {
|
||||
"p": [
|
||||
"p": [ # /optional-list-alias-and-validation-alias fails here
|
||||
"hello",
|
||||
"world",
|
||||
]
|
||||
|
||||
@@ -3,6 +3,7 @@ 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
|
||||
@@ -107,7 +108,15 @@ async def read_model_optional_alias(p: Annotated[FormModelOptionalAlias, Form()]
|
||||
@pytest.mark.parametrize(
|
||||
"path",
|
||||
[
|
||||
"/optional-alias",
|
||||
pytest.param(
|
||||
"/optional-alias",
|
||||
marks=pytest.mark.xfail(
|
||||
raises=AssertionError,
|
||||
strict=False,
|
||||
condition=PYDANTIC_V2,
|
||||
reason="Fails only with PDv2",
|
||||
),
|
||||
),
|
||||
"/model-optional-alias",
|
||||
],
|
||||
)
|
||||
@@ -243,7 +252,10 @@ def test_optional_validation_alias_missing(path: str):
|
||||
@pytest.mark.parametrize(
|
||||
"path",
|
||||
[
|
||||
"/optional-validation-alias",
|
||||
pytest.param(
|
||||
"/optional-validation-alias",
|
||||
marks=pytest.mark.xfail(raises=AssertionError, strict=False),
|
||||
),
|
||||
"/model-optional-validation-alias",
|
||||
],
|
||||
)
|
||||
@@ -251,14 +263,17 @@ 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}
|
||||
assert response.json() == {"p": None} # /optional-validation-alias fails here
|
||||
|
||||
|
||||
@needs_pydanticv2
|
||||
@pytest.mark.parametrize(
|
||||
"path",
|
||||
[
|
||||
"/optional-validation-alias",
|
||||
pytest.param(
|
||||
"/optional-validation-alias",
|
||||
marks=pytest.mark.xfail(raises=AssertionError, strict=False),
|
||||
),
|
||||
"/model-optional-validation-alias",
|
||||
],
|
||||
)
|
||||
@@ -266,7 +281,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"}
|
||||
assert response.json() == {"p": "hello"} # /optional-validation-alias fails here
|
||||
|
||||
|
||||
# =====================================================================================
|
||||
@@ -368,7 +383,10 @@ def test_optional_alias_and_validation_alias_by_name(path: str):
|
||||
@pytest.mark.parametrize(
|
||||
"path",
|
||||
[
|
||||
"/optional-alias-and-validation-alias",
|
||||
pytest.param(
|
||||
"/optional-alias-and-validation-alias",
|
||||
marks=pytest.mark.xfail(raises=AssertionError, strict=False),
|
||||
),
|
||||
"/model-optional-alias-and-validation-alias",
|
||||
],
|
||||
)
|
||||
@@ -376,14 +394,19 @@ 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}
|
||||
assert response.json() == {
|
||||
"p": None # /optional-alias-and-validation-alias fails here
|
||||
}
|
||||
|
||||
|
||||
@needs_pydanticv2
|
||||
@pytest.mark.parametrize(
|
||||
"path",
|
||||
[
|
||||
"/optional-alias-and-validation-alias",
|
||||
pytest.param(
|
||||
"/optional-alias-and-validation-alias",
|
||||
marks=pytest.mark.xfail(raises=AssertionError, strict=False),
|
||||
),
|
||||
"/model-optional-alias-and-validation-alias",
|
||||
],
|
||||
)
|
||||
@@ -391,4 +414,6 @@ 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"}
|
||||
assert response.json() == {
|
||||
"p": "hello" # /optional-alias-and-validation-alias fails here
|
||||
}
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
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
|
||||
@@ -112,7 +113,15 @@ async def read_model_required_alias(p: Annotated[FormModelRequiredAlias, Form()]
|
||||
@pytest.mark.parametrize(
|
||||
"path",
|
||||
[
|
||||
"/required-alias",
|
||||
pytest.param(
|
||||
"/required-alias",
|
||||
marks=pytest.mark.xfail(
|
||||
raises=AssertionError,
|
||||
condition=PYDANTIC_V2,
|
||||
reason="Fails only with PDv2",
|
||||
strict=False,
|
||||
),
|
||||
),
|
||||
"/model-required-alias",
|
||||
],
|
||||
)
|
||||
@@ -254,7 +263,10 @@ def test_required_validation_alias_schema(path: str):
|
||||
@pytest.mark.parametrize(
|
||||
"path",
|
||||
[
|
||||
"/required-validation-alias",
|
||||
pytest.param(
|
||||
"/required-validation-alias",
|
||||
marks=pytest.mark.xfail(raises=AssertionError, strict=False),
|
||||
),
|
||||
"/model-required-validation-alias",
|
||||
],
|
||||
)
|
||||
@@ -268,7 +280,7 @@ def test_required_validation_alias_missing(path: str):
|
||||
"type": "missing",
|
||||
"loc": [
|
||||
"body",
|
||||
"p_val_alias",
|
||||
"p_val_alias", # /required-validation-alias fails here
|
||||
],
|
||||
"msg": "Field required",
|
||||
"input": IsOneOf(None, {}),
|
||||
@@ -281,14 +293,19 @@ def test_required_validation_alias_missing(path: str):
|
||||
@pytest.mark.parametrize(
|
||||
"path",
|
||||
[
|
||||
"/required-validation-alias",
|
||||
pytest.param(
|
||||
"/required-validation-alias",
|
||||
marks=pytest.mark.xfail(raises=AssertionError, strict=False),
|
||||
),
|
||||
"/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, response.text
|
||||
assert response.status_code == 422, ( # /required-validation-alias fails here
|
||||
response.text
|
||||
)
|
||||
|
||||
assert response.json() == {
|
||||
"detail": [
|
||||
@@ -306,14 +323,19 @@ def test_required_validation_alias_by_name(path: str):
|
||||
@pytest.mark.parametrize(
|
||||
"path",
|
||||
[
|
||||
"/required-validation-alias",
|
||||
pytest.param(
|
||||
"/required-validation-alias",
|
||||
marks=pytest.mark.xfail(raises=AssertionError, strict=False),
|
||||
),
|
||||
"/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, response.text
|
||||
assert response.status_code == 200, ( # /required-validation-alias fails here
|
||||
response.text
|
||||
)
|
||||
|
||||
assert response.json() == {"p": "hello"}
|
||||
|
||||
@@ -372,7 +394,10 @@ def test_required_alias_and_validation_alias_schema(path: str):
|
||||
@pytest.mark.parametrize(
|
||||
"path",
|
||||
[
|
||||
"/required-alias-and-validation-alias",
|
||||
pytest.param(
|
||||
"/required-alias-and-validation-alias",
|
||||
marks=pytest.mark.xfail(raises=AssertionError, strict=False),
|
||||
),
|
||||
"/model-required-alias-and-validation-alias",
|
||||
],
|
||||
)
|
||||
@@ -386,7 +411,7 @@ def test_required_alias_and_validation_alias_missing(path: str):
|
||||
"type": "missing",
|
||||
"loc": [
|
||||
"body",
|
||||
"p_val_alias",
|
||||
"p_val_alias", # /required-alias-and-validation-alias fails here
|
||||
],
|
||||
"msg": "Field required",
|
||||
"input": IsOneOf(None, {}),
|
||||
@@ -399,7 +424,10 @@ def test_required_alias_and_validation_alias_missing(path: str):
|
||||
@pytest.mark.parametrize(
|
||||
"path",
|
||||
[
|
||||
"/required-alias-and-validation-alias",
|
||||
pytest.param(
|
||||
"/required-alias-and-validation-alias",
|
||||
marks=pytest.mark.xfail(raises=AssertionError, strict=False),
|
||||
),
|
||||
"/model-required-alias-and-validation-alias",
|
||||
],
|
||||
)
|
||||
@@ -414,7 +442,7 @@ def test_required_alias_and_validation_alias_by_name(path: str):
|
||||
"type": "missing",
|
||||
"loc": [
|
||||
"body",
|
||||
"p_val_alias",
|
||||
"p_val_alias", # /required-alias-and-validation-alias fails here
|
||||
],
|
||||
"msg": "Field required",
|
||||
"input": IsOneOf(None, {"p": "hello"}),
|
||||
@@ -427,14 +455,19 @@ def test_required_alias_and_validation_alias_by_name(path: str):
|
||||
@pytest.mark.parametrize(
|
||||
"path",
|
||||
[
|
||||
"/required-alias-and-validation-alias",
|
||||
pytest.param(
|
||||
"/required-alias-and-validation-alias",
|
||||
marks=pytest.mark.xfail(raises=AssertionError, strict=False),
|
||||
),
|
||||
"/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
|
||||
assert response.status_code == 422, (
|
||||
response.text # /required-alias-and-validation-alias fails here
|
||||
)
|
||||
|
||||
assert response.json() == {
|
||||
"detail": [
|
||||
@@ -452,13 +485,18 @@ def test_required_alias_and_validation_alias_by_alias(path: str):
|
||||
@pytest.mark.parametrize(
|
||||
"path",
|
||||
[
|
||||
"/required-alias-and-validation-alias",
|
||||
pytest.param(
|
||||
"/required-alias-and-validation-alias",
|
||||
marks=pytest.mark.xfail(raises=AssertionError, strict=False),
|
||||
),
|
||||
"/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
|
||||
assert response.status_code == 200, (
|
||||
response.text # /required-alias-and-validation-alias fails here
|
||||
)
|
||||
|
||||
assert response.json() == {"p": "hello"}
|
||||
|
||||
@@ -3,6 +3,7 @@ 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
|
||||
@@ -108,7 +109,7 @@ class HeaderModelRequiredListAlias(BaseModel):
|
||||
async def read_model_required_list_alias(
|
||||
p: Annotated[HeaderModelRequiredListAlias, Header()],
|
||||
):
|
||||
return {"p": p.p}
|
||||
return {"p": p.p} # pragma: no cover
|
||||
|
||||
|
||||
@pytest.mark.parametrize(
|
||||
@@ -167,7 +168,15 @@ def test_required_list_alias_missing(path: str):
|
||||
"path",
|
||||
[
|
||||
"/required-list-alias",
|
||||
"/model-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,
|
||||
),
|
||||
),
|
||||
],
|
||||
)
|
||||
def test_required_list_alias_by_name(path: str):
|
||||
@@ -181,7 +190,9 @@ def test_required_list_alias_by_name(path: str):
|
||||
"type": "missing",
|
||||
"loc": ["header", "p_alias"],
|
||||
"msg": "Field required",
|
||||
"input": IsOneOf(None, IsPartialDict({"p": ["hello", "world"]})),
|
||||
"input": IsOneOf( # /model-required-list-alias with PDv2 fails here
|
||||
None, IsPartialDict({"p": ["hello", "world"]})
|
||||
),
|
||||
}
|
||||
]
|
||||
}
|
||||
@@ -203,13 +214,18 @@ def test_required_list_alias_by_name(path: str):
|
||||
"path",
|
||||
[
|
||||
"/required-list-alias",
|
||||
"/model-required-list-alias",
|
||||
pytest.param(
|
||||
"/model-required-list-alias",
|
||||
marks=pytest.mark.xfail(raises=AssertionError, strict=False),
|
||||
),
|
||||
],
|
||||
)
|
||||
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, response.text
|
||||
assert response.status_code == 200, ( # /model-required-list-alias fails here
|
||||
response.text
|
||||
)
|
||||
assert response.json() == {"p": ["hello", "world"]}
|
||||
|
||||
|
||||
@@ -232,10 +248,11 @@ class HeaderModelRequiredListValidationAlias(BaseModel):
|
||||
async def read_model_required_list_validation_alias(
|
||||
p: Annotated[HeaderModelRequiredListValidationAlias, Header()],
|
||||
):
|
||||
return {"p": p.p}
|
||||
return {"p": p.p} # pragma: no cover
|
||||
|
||||
|
||||
@needs_pydanticv2
|
||||
@pytest.mark.xfail(raises=AssertionError, strict=False)
|
||||
@pytest.mark.parametrize(
|
||||
"path",
|
||||
["/required-list-validation-alias", "/model-required-list-validation-alias"],
|
||||
@@ -259,7 +276,10 @@ def test_required_list_validation_alias_schema(path: str):
|
||||
@pytest.mark.parametrize(
|
||||
"path",
|
||||
[
|
||||
"/required-list-validation-alias",
|
||||
pytest.param(
|
||||
"/required-list-validation-alias",
|
||||
marks=pytest.mark.xfail(raises=AssertionError, strict=False),
|
||||
),
|
||||
"/model-required-list-validation-alias",
|
||||
],
|
||||
)
|
||||
@@ -273,7 +293,7 @@ def test_required_list_validation_alias_missing(path: str):
|
||||
"type": "missing",
|
||||
"loc": [
|
||||
"header",
|
||||
"p_val_alias",
|
||||
"p_val_alias", # /required-list-validation-alias fails here
|
||||
],
|
||||
"msg": "Field required",
|
||||
"input": AnyThing,
|
||||
@@ -286,14 +306,17 @@ def test_required_list_validation_alias_missing(path: str):
|
||||
@pytest.mark.parametrize(
|
||||
"path",
|
||||
[
|
||||
"/required-list-validation-alias",
|
||||
pytest.param(
|
||||
"/required-list-validation-alias",
|
||||
marks=pytest.mark.xfail(raises=AssertionError, strict=False),
|
||||
),
|
||||
"/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
|
||||
assert response.status_code == 422 # /required-list-validation-alias fails here
|
||||
|
||||
assert response.json() == {
|
||||
"detail": [
|
||||
@@ -308,6 +331,7 @@ 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"],
|
||||
@@ -317,9 +341,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
|
||||
assert response.status_code == 200, response.text # both fail here
|
||||
|
||||
assert response.json() == {"p": ["hello", "world"]}
|
||||
assert response.json() == {"p": ["hello", "world"]} # pragma: no cover
|
||||
|
||||
|
||||
# =====================================================================================
|
||||
@@ -341,10 +365,11 @@ class HeaderModelRequiredListAliasAndValidationAlias(BaseModel):
|
||||
def read_model_required_list_alias_and_validation_alias(
|
||||
p: Annotated[HeaderModelRequiredListAliasAndValidationAlias, Header()],
|
||||
):
|
||||
return {"p": p.p}
|
||||
return {"p": p.p} # pragma: no cover
|
||||
|
||||
|
||||
@needs_pydanticv2
|
||||
@pytest.mark.xfail(raises=AssertionError, strict=False)
|
||||
@pytest.mark.parametrize(
|
||||
"path",
|
||||
[
|
||||
@@ -371,7 +396,10 @@ def test_required_list_alias_and_validation_alias_schema(path: str):
|
||||
@pytest.mark.parametrize(
|
||||
"path",
|
||||
[
|
||||
"/required-list-alias-and-validation-alias",
|
||||
pytest.param(
|
||||
"/required-list-alias-and-validation-alias",
|
||||
marks=pytest.mark.xfail(raises=AssertionError, strict=False),
|
||||
),
|
||||
"/model-required-list-alias-and-validation-alias",
|
||||
],
|
||||
)
|
||||
@@ -385,6 +413,7 @@ 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",
|
||||
@@ -395,6 +424,7 @@ def test_required_list_alias_and_validation_alias_missing(path: str):
|
||||
|
||||
|
||||
@needs_pydanticv2
|
||||
@pytest.mark.xfail(raises=AssertionError, strict=False)
|
||||
@pytest.mark.parametrize(
|
||||
"path",
|
||||
[
|
||||
@@ -412,11 +442,13 @@ 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"]}),
|
||||
),
|
||||
}
|
||||
@@ -425,6 +457,7 @@ 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",
|
||||
[
|
||||
@@ -435,7 +468,9 @@ 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 response.status_code == 422
|
||||
assert ( # /required-list-alias-and-validation-alias fails here
|
||||
response.status_code == 422
|
||||
)
|
||||
assert response.json() == {
|
||||
"detail": [
|
||||
{
|
||||
@@ -444,6 +479,7 @@ 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"]}),
|
||||
),
|
||||
}
|
||||
@@ -452,6 +488,7 @@ 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",
|
||||
[
|
||||
@@ -464,5 +501,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
|
||||
assert response.json() == {"p": ["hello", "world"]}
|
||||
assert response.status_code == 200, response.text # both fail here
|
||||
assert response.json() == {"p": ["hello", "world"]} # pragma: no cover
|
||||
|
||||
@@ -171,14 +171,19 @@ def test_optional_list_alias_by_name(path: str):
|
||||
"path",
|
||||
[
|
||||
"/optional-list-alias",
|
||||
"/model-optional-list-alias",
|
||||
pytest.param(
|
||||
"/model-optional-list-alias",
|
||||
marks=pytest.mark.xfail(raises=AssertionError, strict=False),
|
||||
),
|
||||
],
|
||||
)
|
||||
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"]}
|
||||
assert response.json() == {
|
||||
"p": ["hello", "world"] # /model-optional-list-alias fails here
|
||||
}
|
||||
|
||||
|
||||
# =====================================================================================
|
||||
@@ -204,6 +209,7 @@ 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"],
|
||||
@@ -241,7 +247,10 @@ def test_optional_list_validation_alias_missing(path: str):
|
||||
@pytest.mark.parametrize(
|
||||
"path",
|
||||
[
|
||||
"/optional-list-validation-alias",
|
||||
pytest.param(
|
||||
"/optional-list-validation-alias",
|
||||
marks=pytest.mark.xfail(raises=AssertionError, strict=False),
|
||||
),
|
||||
"/model-optional-list-validation-alias",
|
||||
],
|
||||
)
|
||||
@@ -249,10 +258,11 @@ 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}
|
||||
assert response.json() == {"p": None} # /optional-list-validation-alias fails here
|
||||
|
||||
|
||||
@needs_pydanticv2
|
||||
@pytest.mark.xfail(raises=AssertionError, strict=False)
|
||||
@pytest.mark.parametrize(
|
||||
"path",
|
||||
["/optional-list-validation-alias", "/model-optional-list-validation-alias"],
|
||||
@@ -262,8 +272,12 @@ 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
|
||||
assert response.json() == {"p": ["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"]
|
||||
}
|
||||
|
||||
|
||||
# =====================================================================================
|
||||
@@ -293,6 +307,7 @@ def read_model_optional_list_alias_and_validation_alias(
|
||||
|
||||
|
||||
@needs_pydanticv2
|
||||
@pytest.mark.xfail(raises=AssertionError, strict=False)
|
||||
@pytest.mark.parametrize(
|
||||
"path",
|
||||
[
|
||||
@@ -351,7 +366,10 @@ def test_optional_list_alias_and_validation_alias_by_name(path: str):
|
||||
@pytest.mark.parametrize(
|
||||
"path",
|
||||
[
|
||||
"/optional-list-alias-and-validation-alias",
|
||||
pytest.param(
|
||||
"/optional-list-alias-and-validation-alias",
|
||||
marks=pytest.mark.xfail(raises=AssertionError, strict=False),
|
||||
),
|
||||
"/model-optional-list-alias-and-validation-alias",
|
||||
],
|
||||
)
|
||||
@@ -359,10 +377,13 @@ 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}
|
||||
assert response.json() == {
|
||||
"p": None # /optional-list-alias-and-validation-alias fails here
|
||||
}
|
||||
|
||||
|
||||
@needs_pydanticv2
|
||||
@pytest.mark.xfail(raises=AssertionError, strict=False)
|
||||
@pytest.mark.parametrize(
|
||||
"path",
|
||||
[
|
||||
@@ -375,9 +396,11 @@ 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
|
||||
assert response.status_code == 200, (
|
||||
response.text # /model-optional-list-alias-and-validation-alias fails here
|
||||
)
|
||||
assert response.json() == {
|
||||
"p": [
|
||||
"p": [ # /optional-list-alias-and-validation-alias fails here
|
||||
"hello",
|
||||
"world",
|
||||
]
|
||||
|
||||
@@ -155,14 +155,17 @@ def test_optional_alias_by_name(path: str):
|
||||
"path",
|
||||
[
|
||||
"/optional-alias",
|
||||
"/model-optional-alias",
|
||||
pytest.param(
|
||||
"/model-optional-alias",
|
||||
marks=pytest.mark.xfail(raises=AssertionError, strict=False),
|
||||
),
|
||||
],
|
||||
)
|
||||
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"}
|
||||
assert response.json() == {"p": "hello"} # /model-optional-alias fails here
|
||||
|
||||
|
||||
# =====================================================================================
|
||||
@@ -188,6 +191,7 @@ 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"],
|
||||
@@ -222,7 +226,10 @@ def test_optional_validation_alias_missing(path: str):
|
||||
@pytest.mark.parametrize(
|
||||
"path",
|
||||
[
|
||||
"/optional-validation-alias",
|
||||
pytest.param(
|
||||
"/optional-validation-alias",
|
||||
marks=pytest.mark.xfail(raises=AssertionError, strict=False),
|
||||
),
|
||||
"/model-optional-validation-alias",
|
||||
],
|
||||
)
|
||||
@@ -230,14 +237,17 @@ 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}
|
||||
assert response.json() == {"p": None} # /optional-validation-alias fails here
|
||||
|
||||
|
||||
@needs_pydanticv2
|
||||
@pytest.mark.parametrize(
|
||||
"path",
|
||||
[
|
||||
"/optional-validation-alias",
|
||||
pytest.param(
|
||||
"/optional-validation-alias",
|
||||
marks=pytest.mark.xfail(raises=AssertionError, strict=False),
|
||||
),
|
||||
"/model-optional-validation-alias",
|
||||
],
|
||||
)
|
||||
@@ -245,7 +255,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"}
|
||||
assert response.json() == {"p": "hello"} # /optional-validation-alias fails here
|
||||
|
||||
|
||||
# =====================================================================================
|
||||
@@ -273,6 +283,7 @@ def read_model_optional_alias_and_validation_alias(
|
||||
|
||||
|
||||
@needs_pydanticv2
|
||||
@pytest.mark.xfail(raises=AssertionError, strict=False)
|
||||
@pytest.mark.parametrize(
|
||||
"path",
|
||||
[
|
||||
@@ -328,7 +339,10 @@ def test_optional_alias_and_validation_alias_by_name(path: str):
|
||||
@pytest.mark.parametrize(
|
||||
"path",
|
||||
[
|
||||
"/optional-alias-and-validation-alias",
|
||||
pytest.param(
|
||||
"/optional-alias-and-validation-alias",
|
||||
marks=pytest.mark.xfail(raises=AssertionError, strict=False),
|
||||
),
|
||||
"/model-optional-alias-and-validation-alias",
|
||||
],
|
||||
)
|
||||
@@ -336,14 +350,19 @@ 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}
|
||||
assert response.json() == {
|
||||
"p": None # /optional-alias-and-validation-alias fails here
|
||||
}
|
||||
|
||||
|
||||
@needs_pydanticv2
|
||||
@pytest.mark.parametrize(
|
||||
"path",
|
||||
[
|
||||
"/optional-alias-and-validation-alias",
|
||||
pytest.param(
|
||||
"/optional-alias-and-validation-alias",
|
||||
marks=pytest.mark.xfail(raises=AssertionError, strict=False),
|
||||
),
|
||||
"/model-optional-alias-and-validation-alias",
|
||||
],
|
||||
)
|
||||
@@ -351,4 +370,6 @@ 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"}
|
||||
assert response.json() == {
|
||||
"p": "hello" # /optional-alias-and-validation-alias fails here
|
||||
}
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
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
|
||||
@@ -101,7 +102,7 @@ class HeaderModelRequiredAlias(BaseModel):
|
||||
|
||||
@app.get("/model-required-alias")
|
||||
async def read_model_required_alias(p: Annotated[HeaderModelRequiredAlias, Header()]):
|
||||
return {"p": p.p}
|
||||
return {"p": p.p} # pragma: no cover
|
||||
|
||||
|
||||
@pytest.mark.parametrize(
|
||||
@@ -156,7 +157,15 @@ def test_required_alias_missing(path: str):
|
||||
"path",
|
||||
[
|
||||
"/required-alias",
|
||||
"/model-required-alias",
|
||||
pytest.param(
|
||||
"/model-required-alias",
|
||||
marks=pytest.mark.xfail(
|
||||
raises=AssertionError,
|
||||
condition=PYDANTIC_V2,
|
||||
reason="Fails only with PDv2 models",
|
||||
strict=False,
|
||||
),
|
||||
),
|
||||
],
|
||||
)
|
||||
def test_required_alias_by_name(path: str):
|
||||
@@ -192,13 +201,18 @@ def test_required_alias_by_name(path: str):
|
||||
"path",
|
||||
[
|
||||
"/required-alias",
|
||||
"/model-required-alias",
|
||||
pytest.param(
|
||||
"/model-required-alias",
|
||||
marks=pytest.mark.xfail(raises=AssertionError, strict=False),
|
||||
),
|
||||
],
|
||||
)
|
||||
def test_required_alias_by_alias(path: str):
|
||||
client = TestClient(app)
|
||||
response = client.get(path, headers={"p_alias": "hello"})
|
||||
assert response.status_code == 200, response.text
|
||||
assert response.status_code == 200, ( # /model-required-alias fails here
|
||||
response.text
|
||||
)
|
||||
assert response.json() == {"p": "hello"}
|
||||
|
||||
|
||||
@@ -225,6 +239,7 @@ 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"],
|
||||
@@ -244,7 +259,10 @@ def test_required_validation_alias_schema(path: str):
|
||||
@pytest.mark.parametrize(
|
||||
"path",
|
||||
[
|
||||
"/required-validation-alias",
|
||||
pytest.param(
|
||||
"/required-validation-alias",
|
||||
marks=pytest.mark.xfail(raises=AssertionError, strict=False),
|
||||
),
|
||||
"/model-required-validation-alias",
|
||||
],
|
||||
)
|
||||
@@ -258,7 +276,7 @@ def test_required_validation_alias_missing(path: str):
|
||||
"type": "missing",
|
||||
"loc": [
|
||||
"header",
|
||||
"p_val_alias",
|
||||
"p_val_alias", # /required-validation-alias fails here
|
||||
],
|
||||
"msg": "Field required",
|
||||
"input": AnyThing,
|
||||
@@ -271,14 +289,19 @@ def test_required_validation_alias_missing(path: str):
|
||||
@pytest.mark.parametrize(
|
||||
"path",
|
||||
[
|
||||
"/required-validation-alias",
|
||||
pytest.param(
|
||||
"/required-validation-alias",
|
||||
marks=pytest.mark.xfail(raises=AssertionError, strict=False),
|
||||
),
|
||||
"/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, response.text
|
||||
assert response.status_code == 422, ( # /required-validation-alias fails here
|
||||
response.text
|
||||
)
|
||||
|
||||
assert response.json() == {
|
||||
"detail": [
|
||||
@@ -296,14 +319,19 @@ def test_required_validation_alias_by_name(path: str):
|
||||
@pytest.mark.parametrize(
|
||||
"path",
|
||||
[
|
||||
"/required-validation-alias",
|
||||
pytest.param(
|
||||
"/required-validation-alias",
|
||||
marks=pytest.mark.xfail(raises=AssertionError, strict=False),
|
||||
),
|
||||
"/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, response.text
|
||||
assert response.status_code == 200, ( # /required-validation-alias fails here
|
||||
response.text
|
||||
)
|
||||
|
||||
assert response.json() == {"p": "hello"}
|
||||
|
||||
@@ -331,6 +359,7 @@ def read_model_required_alias_and_validation_alias(
|
||||
|
||||
|
||||
@needs_pydanticv2
|
||||
@pytest.mark.xfail(raises=AssertionError, strict=False)
|
||||
@pytest.mark.parametrize(
|
||||
"path",
|
||||
[
|
||||
@@ -353,7 +382,10 @@ def test_required_alias_and_validation_alias_schema(path: str):
|
||||
@pytest.mark.parametrize(
|
||||
"path",
|
||||
[
|
||||
"/required-alias-and-validation-alias",
|
||||
pytest.param(
|
||||
"/required-alias-and-validation-alias",
|
||||
marks=pytest.mark.xfail(raises=AssertionError, strict=False),
|
||||
),
|
||||
"/model-required-alias-and-validation-alias",
|
||||
],
|
||||
)
|
||||
@@ -367,7 +399,7 @@ def test_required_alias_and_validation_alias_missing(path: str):
|
||||
"type": "missing",
|
||||
"loc": [
|
||||
"header",
|
||||
"p_val_alias",
|
||||
"p_val_alias", # /required-alias-and-validation-alias fails here
|
||||
],
|
||||
"msg": "Field required",
|
||||
"input": AnyThing,
|
||||
@@ -377,6 +409,7 @@ def test_required_alias_and_validation_alias_missing(path: str):
|
||||
|
||||
|
||||
@needs_pydanticv2
|
||||
@pytest.mark.xfail(raises=AssertionError, strict=False)
|
||||
@pytest.mark.parametrize(
|
||||
"path",
|
||||
[
|
||||
@@ -395,10 +428,10 @@ def test_required_alias_and_validation_alias_by_name(path: str):
|
||||
"type": "missing",
|
||||
"loc": [
|
||||
"header",
|
||||
"p_val_alias",
|
||||
"p_val_alias", # /required-alias-and-validation-alias fails here
|
||||
],
|
||||
"msg": "Field required",
|
||||
"input": IsOneOf(
|
||||
"input": IsOneOf( # /model-alias-and-validation-alias fails here
|
||||
None,
|
||||
IsPartialDict({"p": "hello"}),
|
||||
),
|
||||
@@ -408,6 +441,7 @@ def test_required_alias_and_validation_alias_by_name(path: str):
|
||||
|
||||
|
||||
@needs_pydanticv2
|
||||
@pytest.mark.xfail(raises=AssertionError, strict=False)
|
||||
@pytest.mark.parametrize(
|
||||
"path",
|
||||
[
|
||||
@@ -418,7 +452,9 @@ 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
|
||||
assert (
|
||||
response.status_code == 422 # /required-alias-and-validation-alias fails here
|
||||
)
|
||||
|
||||
assert response.json() == {
|
||||
"detail": [
|
||||
@@ -426,7 +462,7 @@ def test_required_alias_and_validation_alias_by_alias(path: str):
|
||||
"type": "missing",
|
||||
"loc": ["header", "p_val_alias"],
|
||||
"msg": "Field required",
|
||||
"input": IsOneOf(
|
||||
"input": IsOneOf( # /model-alias-and-validation-alias fails here
|
||||
None,
|
||||
IsPartialDict({"p_alias": "hello"}),
|
||||
),
|
||||
@@ -439,13 +475,18 @@ def test_required_alias_and_validation_alias_by_alias(path: str):
|
||||
@pytest.mark.parametrize(
|
||||
"path",
|
||||
[
|
||||
"/required-alias-and-validation-alias",
|
||||
pytest.param(
|
||||
"/required-alias-and-validation-alias",
|
||||
marks=pytest.mark.xfail(raises=AssertionError, strict=False),
|
||||
),
|
||||
"/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
|
||||
assert response.status_code == 200, (
|
||||
response.text # /required-alias-and-validation-alias fails here
|
||||
)
|
||||
|
||||
assert response.json() == {"p": "hello"}
|
||||
|
||||
@@ -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}
|
||||
return {"p": p} # pragma: no cover
|
||||
|
||||
|
||||
@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}
|
||||
return {"p": p} # pragma: no cover
|
||||
|
||||
|
||||
@pytest.mark.parametrize(
|
||||
@@ -44,14 +44,20 @@ def read_required_alias_and_validation_alias(
|
||||
"p_val_alias",
|
||||
"P Val Alias",
|
||||
id="required-validation-alias",
|
||||
marks=needs_pydanticv2,
|
||||
marks=(
|
||||
needs_pydanticv2,
|
||||
pytest.mark.xfail(raises=AssertionError, strict=False),
|
||||
),
|
||||
),
|
||||
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,
|
||||
marks=(
|
||||
needs_pydanticv2,
|
||||
pytest.mark.xfail(raises=AssertionError, strict=False),
|
||||
),
|
||||
),
|
||||
],
|
||||
)
|
||||
@@ -74,12 +80,18 @@ def test_schema(path: str, expected_name: str, expected_title: str):
|
||||
pytest.param(
|
||||
"/required-validation-alias",
|
||||
id="required-validation-alias",
|
||||
marks=needs_pydanticv2,
|
||||
marks=(
|
||||
needs_pydanticv2,
|
||||
pytest.mark.xfail(raises=AssertionError, strict=False),
|
||||
),
|
||||
),
|
||||
pytest.param(
|
||||
"/required-alias-and-validation-alias",
|
||||
id="required-alias-and-validation-alias",
|
||||
marks=needs_pydanticv2,
|
||||
marks=(
|
||||
needs_pydanticv2,
|
||||
pytest.mark.xfail(raises=AssertionError, strict=False),
|
||||
),
|
||||
),
|
||||
],
|
||||
)
|
||||
|
||||
@@ -3,6 +3,7 @@ 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
|
||||
@@ -108,7 +109,7 @@ class QueryModelRequiredListAlias(BaseModel):
|
||||
async def read_model_required_list_alias(
|
||||
p: Annotated[QueryModelRequiredListAlias, Query()],
|
||||
):
|
||||
return {"p": p.p}
|
||||
return {"p": p.p} # pragma: no cover
|
||||
|
||||
|
||||
@pytest.mark.parametrize(
|
||||
@@ -167,7 +168,15 @@ def test_required_list_alias_missing(path: str):
|
||||
"path",
|
||||
[
|
||||
"/required-list-alias",
|
||||
"/model-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,
|
||||
),
|
||||
),
|
||||
],
|
||||
)
|
||||
def test_required_list_alias_by_name(path: str):
|
||||
@@ -181,7 +190,9 @@ def test_required_list_alias_by_name(path: str):
|
||||
"type": "missing",
|
||||
"loc": ["query", "p_alias"],
|
||||
"msg": "Field required",
|
||||
"input": IsOneOf(None, {"p": ["hello", "world"]}),
|
||||
"input": IsOneOf( # /model-required-list-alias with PDv2 fails here
|
||||
None, {"p": ["hello", "world"]}
|
||||
),
|
||||
}
|
||||
]
|
||||
}
|
||||
@@ -203,13 +214,18 @@ def test_required_list_alias_by_name(path: str):
|
||||
"path",
|
||||
[
|
||||
"/required-list-alias",
|
||||
"/model-required-list-alias",
|
||||
pytest.param(
|
||||
"/model-required-list-alias",
|
||||
marks=pytest.mark.xfail(raises=AssertionError, strict=False),
|
||||
),
|
||||
],
|
||||
)
|
||||
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, response.text
|
||||
assert response.status_code == 200, ( # /model-required-list-alias fails here
|
||||
response.text
|
||||
)
|
||||
assert response.json() == {"p": ["hello", "world"]}
|
||||
|
||||
|
||||
@@ -232,10 +248,11 @@ class QueryModelRequiredListValidationAlias(BaseModel):
|
||||
async def read_model_required_list_validation_alias(
|
||||
p: Annotated[QueryModelRequiredListValidationAlias, Query()],
|
||||
):
|
||||
return {"p": p.p}
|
||||
return {"p": p.p} # pragma: no cover
|
||||
|
||||
|
||||
@needs_pydanticv2
|
||||
@pytest.mark.xfail(raises=AssertionError, strict=False)
|
||||
@pytest.mark.parametrize(
|
||||
"path",
|
||||
["/required-list-validation-alias", "/model-required-list-validation-alias"],
|
||||
@@ -259,7 +276,10 @@ def test_required_list_validation_alias_schema(path: str):
|
||||
@pytest.mark.parametrize(
|
||||
"path",
|
||||
[
|
||||
"/required-list-validation-alias",
|
||||
pytest.param(
|
||||
"/required-list-validation-alias",
|
||||
marks=pytest.mark.xfail(raises=AssertionError, strict=False),
|
||||
),
|
||||
"/model-required-list-validation-alias",
|
||||
],
|
||||
)
|
||||
@@ -273,7 +293,7 @@ def test_required_list_validation_alias_missing(path: str):
|
||||
"type": "missing",
|
||||
"loc": [
|
||||
"query",
|
||||
"p_val_alias",
|
||||
"p_val_alias", # /required-list-validation-alias fails here
|
||||
],
|
||||
"msg": "Field required",
|
||||
"input": IsOneOf(None, {}),
|
||||
@@ -286,14 +306,17 @@ def test_required_list_validation_alias_missing(path: str):
|
||||
@pytest.mark.parametrize(
|
||||
"path",
|
||||
[
|
||||
"/required-list-validation-alias",
|
||||
pytest.param(
|
||||
"/required-list-validation-alias",
|
||||
marks=pytest.mark.xfail(raises=AssertionError, strict=False),
|
||||
),
|
||||
"/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
|
||||
assert response.status_code == 422 # /required-list-validation-alias fails here
|
||||
|
||||
assert response.json() == {
|
||||
"detail": [
|
||||
@@ -308,6 +331,7 @@ 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"],
|
||||
@@ -315,9 +339,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
|
||||
assert response.status_code == 200, response.text # both fail here
|
||||
|
||||
assert response.json() == {"p": ["hello", "world"]}
|
||||
assert response.json() == {"p": ["hello", "world"]} # pragma: no cover
|
||||
|
||||
|
||||
# =====================================================================================
|
||||
@@ -339,10 +363,11 @@ class QueryModelRequiredListAliasAndValidationAlias(BaseModel):
|
||||
def read_model_required_list_alias_and_validation_alias(
|
||||
p: Annotated[QueryModelRequiredListAliasAndValidationAlias, Query()],
|
||||
):
|
||||
return {"p": p.p}
|
||||
return {"p": p.p} # pragma: no cover
|
||||
|
||||
|
||||
@needs_pydanticv2
|
||||
@pytest.mark.xfail(raises=AssertionError, strict=False)
|
||||
@pytest.mark.parametrize(
|
||||
"path",
|
||||
[
|
||||
@@ -369,7 +394,10 @@ def test_required_list_alias_and_validation_alias_schema(path: str):
|
||||
@pytest.mark.parametrize(
|
||||
"path",
|
||||
[
|
||||
"/required-list-alias-and-validation-alias",
|
||||
pytest.param(
|
||||
"/required-list-alias-and-validation-alias",
|
||||
marks=pytest.mark.xfail(raises=AssertionError, strict=False),
|
||||
),
|
||||
"/model-required-list-alias-and-validation-alias",
|
||||
],
|
||||
)
|
||||
@@ -383,6 +411,7 @@ 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",
|
||||
@@ -393,6 +422,7 @@ def test_required_list_alias_and_validation_alias_missing(path: str):
|
||||
|
||||
|
||||
@needs_pydanticv2
|
||||
@pytest.mark.xfail(raises=AssertionError, strict=False)
|
||||
@pytest.mark.parametrize(
|
||||
"path",
|
||||
[
|
||||
@@ -410,11 +440,13 @@ 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",
|
||||
@@ -428,6 +460,7 @@ 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",
|
||||
[
|
||||
@@ -438,7 +471,9 @@ 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 response.status_code == 422
|
||||
assert ( # /required-list-alias-and-validation-alias fails here
|
||||
response.status_code == 422
|
||||
)
|
||||
assert response.json() == {
|
||||
"detail": [
|
||||
{
|
||||
@@ -447,6 +482,7 @@ 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"]},
|
||||
),
|
||||
}
|
||||
@@ -455,6 +491,7 @@ 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",
|
||||
[
|
||||
@@ -465,5 +502,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
|
||||
assert response.json() == {"p": ["hello", "world"]}
|
||||
assert response.status_code == 200, response.text # both fail here
|
||||
assert response.json() == {"p": ["hello", "world"]} # pragma: no cover
|
||||
|
||||
@@ -171,14 +171,19 @@ def test_optional_list_alias_by_name(path: str):
|
||||
"path",
|
||||
[
|
||||
"/optional-list-alias",
|
||||
"/model-optional-list-alias",
|
||||
pytest.param(
|
||||
"/model-optional-list-alias",
|
||||
marks=pytest.mark.xfail(raises=AssertionError, strict=False),
|
||||
),
|
||||
],
|
||||
)
|
||||
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"]}
|
||||
assert response.json() == {
|
||||
"p": ["hello", "world"] # /model-optional-list-alias fails here
|
||||
}
|
||||
|
||||
|
||||
# =====================================================================================
|
||||
@@ -204,6 +209,7 @@ 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"],
|
||||
@@ -241,7 +247,10 @@ def test_optional_list_validation_alias_missing(path: str):
|
||||
@pytest.mark.parametrize(
|
||||
"path",
|
||||
[
|
||||
"/optional-list-validation-alias",
|
||||
pytest.param(
|
||||
"/optional-list-validation-alias",
|
||||
marks=pytest.mark.xfail(raises=AssertionError, strict=False),
|
||||
),
|
||||
"/model-optional-list-validation-alias",
|
||||
],
|
||||
)
|
||||
@@ -249,10 +258,11 @@ 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}
|
||||
assert response.json() == {"p": None} # /optional-list-validation-alias fails here
|
||||
|
||||
|
||||
@needs_pydanticv2
|
||||
@pytest.mark.xfail(raises=AssertionError, strict=False)
|
||||
@pytest.mark.parametrize(
|
||||
"path",
|
||||
["/optional-list-validation-alias", "/model-optional-list-validation-alias"],
|
||||
@@ -260,8 +270,12 @@ 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
|
||||
assert response.json() == {"p": ["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"]
|
||||
}
|
||||
|
||||
|
||||
# =====================================================================================
|
||||
@@ -291,6 +305,7 @@ def read_model_optional_list_alias_and_validation_alias(
|
||||
|
||||
|
||||
@needs_pydanticv2
|
||||
@pytest.mark.xfail(raises=AssertionError, strict=False)
|
||||
@pytest.mark.parametrize(
|
||||
"path",
|
||||
[
|
||||
@@ -349,7 +364,10 @@ def test_optional_list_alias_and_validation_alias_by_name(path: str):
|
||||
@pytest.mark.parametrize(
|
||||
"path",
|
||||
[
|
||||
"/optional-list-alias-and-validation-alias",
|
||||
pytest.param(
|
||||
"/optional-list-alias-and-validation-alias",
|
||||
marks=pytest.mark.xfail(raises=AssertionError, strict=False),
|
||||
),
|
||||
"/model-optional-list-alias-and-validation-alias",
|
||||
],
|
||||
)
|
||||
@@ -357,10 +375,13 @@ 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}
|
||||
assert response.json() == {
|
||||
"p": None # /optional-list-alias-and-validation-alias fails here
|
||||
}
|
||||
|
||||
|
||||
@needs_pydanticv2
|
||||
@pytest.mark.xfail(raises=AssertionError, strict=False)
|
||||
@pytest.mark.parametrize(
|
||||
"path",
|
||||
[
|
||||
@@ -371,9 +392,11 @@ 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
|
||||
assert response.status_code == 200, (
|
||||
response.text # /model-optional-list-alias-and-validation-alias fails here
|
||||
)
|
||||
assert response.json() == {
|
||||
"p": [
|
||||
"p": [ # /optional-list-alias-and-validation-alias fails here
|
||||
"hello",
|
||||
"world",
|
||||
]
|
||||
|
||||
@@ -155,14 +155,17 @@ def test_optional_alias_by_name(path: str):
|
||||
"path",
|
||||
[
|
||||
"/optional-alias",
|
||||
"/model-optional-alias",
|
||||
pytest.param(
|
||||
"/model-optional-alias",
|
||||
marks=pytest.mark.xfail(raises=AssertionError, strict=False),
|
||||
),
|
||||
],
|
||||
)
|
||||
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"}
|
||||
assert response.json() == {"p": "hello"} # /model-optional-alias fails here
|
||||
|
||||
|
||||
# =====================================================================================
|
||||
@@ -188,6 +191,7 @@ 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"],
|
||||
@@ -222,7 +226,10 @@ def test_optional_validation_alias_missing(path: str):
|
||||
@pytest.mark.parametrize(
|
||||
"path",
|
||||
[
|
||||
"/optional-validation-alias",
|
||||
pytest.param(
|
||||
"/optional-validation-alias",
|
||||
marks=pytest.mark.xfail(raises=AssertionError, strict=False),
|
||||
),
|
||||
"/model-optional-validation-alias",
|
||||
],
|
||||
)
|
||||
@@ -237,7 +244,10 @@ def test_optional_validation_alias_by_name(path: str):
|
||||
@pytest.mark.parametrize(
|
||||
"path",
|
||||
[
|
||||
"/optional-validation-alias",
|
||||
pytest.param(
|
||||
"/optional-validation-alias",
|
||||
marks=pytest.mark.xfail(raises=AssertionError, strict=False),
|
||||
),
|
||||
"/model-optional-validation-alias",
|
||||
],
|
||||
)
|
||||
@@ -245,7 +255,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"}
|
||||
assert response.json() == {"p": "hello"} # /optional-validation-alias fails here
|
||||
|
||||
|
||||
# =====================================================================================
|
||||
@@ -273,6 +283,7 @@ def read_model_optional_alias_and_validation_alias(
|
||||
|
||||
|
||||
@needs_pydanticv2
|
||||
@pytest.mark.xfail(raises=AssertionError, strict=False)
|
||||
@pytest.mark.parametrize(
|
||||
"path",
|
||||
[
|
||||
@@ -328,7 +339,10 @@ def test_optional_alias_and_validation_alias_by_name(path: str):
|
||||
@pytest.mark.parametrize(
|
||||
"path",
|
||||
[
|
||||
"/optional-alias-and-validation-alias",
|
||||
pytest.param(
|
||||
"/optional-alias-and-validation-alias",
|
||||
marks=pytest.mark.xfail(raises=AssertionError, strict=False),
|
||||
),
|
||||
"/model-optional-alias-and-validation-alias",
|
||||
],
|
||||
)
|
||||
@@ -336,14 +350,19 @@ 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}
|
||||
assert response.json() == {
|
||||
"p": None # /optional-alias-and-validation-alias fails here
|
||||
}
|
||||
|
||||
|
||||
@needs_pydanticv2
|
||||
@pytest.mark.parametrize(
|
||||
"path",
|
||||
[
|
||||
"/optional-alias-and-validation-alias",
|
||||
pytest.param(
|
||||
"/optional-alias-and-validation-alias",
|
||||
marks=pytest.mark.xfail(raises=AssertionError, strict=False),
|
||||
),
|
||||
"/model-optional-alias-and-validation-alias",
|
||||
],
|
||||
)
|
||||
@@ -351,4 +370,6 @@ 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"}
|
||||
assert response.json() == {
|
||||
"p": "hello" # /optional-alias-and-validation-alias fails here
|
||||
}
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
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
|
||||
@@ -101,7 +102,7 @@ class QueryModelRequiredAlias(BaseModel):
|
||||
|
||||
@app.get("/model-required-alias")
|
||||
async def read_model_required_alias(p: Annotated[QueryModelRequiredAlias, Query()]):
|
||||
return {"p": p.p}
|
||||
return {"p": p.p} # pragma: no cover
|
||||
|
||||
|
||||
@pytest.mark.parametrize(
|
||||
@@ -156,7 +157,15 @@ def test_required_alias_missing(path: str):
|
||||
"path",
|
||||
[
|
||||
"/required-alias",
|
||||
"/model-required-alias",
|
||||
pytest.param(
|
||||
"/model-required-alias",
|
||||
marks=pytest.mark.xfail(
|
||||
raises=AssertionError,
|
||||
condition=PYDANTIC_V2,
|
||||
reason="Fails only with PDv2 models",
|
||||
strict=False,
|
||||
),
|
||||
),
|
||||
],
|
||||
)
|
||||
def test_required_alias_by_name(path: str):
|
||||
@@ -172,7 +181,7 @@ def test_required_alias_by_name(path: str):
|
||||
"msg": "Field required",
|
||||
"input": IsOneOf(
|
||||
None,
|
||||
{"p": "hello"},
|
||||
{"p": "hello"}, # /model-required-alias PDv2 fails here
|
||||
),
|
||||
}
|
||||
]
|
||||
@@ -195,13 +204,18 @@ def test_required_alias_by_name(path: str):
|
||||
"path",
|
||||
[
|
||||
"/required-alias",
|
||||
"/model-required-alias",
|
||||
pytest.param(
|
||||
"/model-required-alias",
|
||||
marks=pytest.mark.xfail(raises=AssertionError, strict=False),
|
||||
),
|
||||
],
|
||||
)
|
||||
def test_required_alias_by_alias(path: str):
|
||||
client = TestClient(app)
|
||||
response = client.get(f"{path}?p_alias=hello")
|
||||
assert response.status_code == 200, response.text
|
||||
assert response.status_code == 200, ( # /model-required-alias fails here
|
||||
response.text
|
||||
)
|
||||
assert response.json() == {"p": "hello"}
|
||||
|
||||
|
||||
@@ -228,6 +242,7 @@ 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"],
|
||||
@@ -247,7 +262,10 @@ def test_required_validation_alias_schema(path: str):
|
||||
@pytest.mark.parametrize(
|
||||
"path",
|
||||
[
|
||||
"/required-validation-alias",
|
||||
pytest.param(
|
||||
"/required-validation-alias",
|
||||
marks=pytest.mark.xfail(raises=AssertionError, strict=False),
|
||||
),
|
||||
"/model-required-validation-alias",
|
||||
],
|
||||
)
|
||||
@@ -261,7 +279,7 @@ def test_required_validation_alias_missing(path: str):
|
||||
"type": "missing",
|
||||
"loc": [
|
||||
"query",
|
||||
"p_val_alias",
|
||||
"p_val_alias", # /required-validation-alias fails here
|
||||
],
|
||||
"msg": "Field required",
|
||||
"input": IsOneOf(None, {}),
|
||||
@@ -274,14 +292,19 @@ def test_required_validation_alias_missing(path: str):
|
||||
@pytest.mark.parametrize(
|
||||
"path",
|
||||
[
|
||||
"/required-validation-alias",
|
||||
pytest.param(
|
||||
"/required-validation-alias",
|
||||
marks=pytest.mark.xfail(raises=AssertionError, strict=False),
|
||||
),
|
||||
"/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, response.text
|
||||
assert response.status_code == 422, ( # /required-validation-alias fails here
|
||||
response.text
|
||||
)
|
||||
|
||||
assert response.json() == {
|
||||
"detail": [
|
||||
@@ -299,14 +322,19 @@ def test_required_validation_alias_by_name(path: str):
|
||||
@pytest.mark.parametrize(
|
||||
"path",
|
||||
[
|
||||
"/required-validation-alias",
|
||||
pytest.param(
|
||||
"/required-validation-alias",
|
||||
marks=pytest.mark.xfail(raises=AssertionError, strict=False),
|
||||
),
|
||||
"/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, response.text
|
||||
assert response.status_code == 200, ( # /required-validation-alias fails here
|
||||
response.text
|
||||
)
|
||||
|
||||
assert response.json() == {"p": "hello"}
|
||||
|
||||
@@ -334,6 +362,7 @@ def read_model_required_alias_and_validation_alias(
|
||||
|
||||
|
||||
@needs_pydanticv2
|
||||
@pytest.mark.xfail(raises=AssertionError, strict=False)
|
||||
@pytest.mark.parametrize(
|
||||
"path",
|
||||
[
|
||||
@@ -356,7 +385,10 @@ def test_required_alias_and_validation_alias_schema(path: str):
|
||||
@pytest.mark.parametrize(
|
||||
"path",
|
||||
[
|
||||
"/required-alias-and-validation-alias",
|
||||
pytest.param(
|
||||
"/required-alias-and-validation-alias",
|
||||
marks=pytest.mark.xfail(raises=AssertionError, strict=False),
|
||||
),
|
||||
"/model-required-alias-and-validation-alias",
|
||||
],
|
||||
)
|
||||
@@ -370,7 +402,7 @@ def test_required_alias_and_validation_alias_missing(path: str):
|
||||
"type": "missing",
|
||||
"loc": [
|
||||
"query",
|
||||
"p_val_alias",
|
||||
"p_val_alias", # /required-alias-and-validation-alias fails here
|
||||
],
|
||||
"msg": "Field required",
|
||||
"input": IsOneOf(None, {}),
|
||||
@@ -380,6 +412,7 @@ def test_required_alias_and_validation_alias_missing(path: str):
|
||||
|
||||
|
||||
@needs_pydanticv2
|
||||
@pytest.mark.xfail(raises=AssertionError, strict=False)
|
||||
@pytest.mark.parametrize(
|
||||
"path",
|
||||
[
|
||||
@@ -398,10 +431,10 @@ def test_required_alias_and_validation_alias_by_name(path: str):
|
||||
"type": "missing",
|
||||
"loc": [
|
||||
"query",
|
||||
"p_val_alias",
|
||||
"p_val_alias", # /required-alias-and-validation-alias fails here
|
||||
],
|
||||
"msg": "Field required",
|
||||
"input": IsOneOf(
|
||||
"input": IsOneOf( # /model-alias-and-validation-alias fails here
|
||||
None,
|
||||
{"p": "hello"},
|
||||
),
|
||||
@@ -411,6 +444,7 @@ def test_required_alias_and_validation_alias_by_name(path: str):
|
||||
|
||||
|
||||
@needs_pydanticv2
|
||||
@pytest.mark.xfail(raises=AssertionError, strict=False)
|
||||
@pytest.mark.parametrize(
|
||||
"path",
|
||||
[
|
||||
@@ -421,7 +455,9 @@ 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
|
||||
assert (
|
||||
response.status_code == 422 # /required-alias-and-validation-alias fails here
|
||||
)
|
||||
|
||||
assert response.json() == {
|
||||
"detail": [
|
||||
@@ -429,7 +465,7 @@ def test_required_alias_and_validation_alias_by_alias(path: str):
|
||||
"type": "missing",
|
||||
"loc": ["query", "p_val_alias"],
|
||||
"msg": "Field required",
|
||||
"input": IsOneOf(
|
||||
"input": IsOneOf( # /model-alias-and-validation-alias fails here
|
||||
None,
|
||||
{"p_alias": "hello"},
|
||||
),
|
||||
@@ -442,13 +478,18 @@ def test_required_alias_and_validation_alias_by_alias(path: str):
|
||||
@pytest.mark.parametrize(
|
||||
"path",
|
||||
[
|
||||
"/required-alias-and-validation-alias",
|
||||
pytest.param(
|
||||
"/required-alias-and-validation-alias",
|
||||
marks=pytest.mark.xfail(raises=AssertionError, strict=False),
|
||||
),
|
||||
"/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
|
||||
assert response.status_code == 200, (
|
||||
response.text # /required-alias-and-validation-alias fails here
|
||||
)
|
||||
|
||||
assert response.json() == {"p": "hello"}
|
||||
|
||||
@@ -1,207 +0,0 @@
|
||||
# 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",
|
||||
},
|
||||
}
|
||||
},
|
||||
}
|
||||
)
|
||||
Reference in New Issue
Block a user