From b630ac6cf4ed41c60b865dd32e4c6f54d5fb1090 Mon Sep 17 00:00:00 2001 From: Yurii Motov Date: Wed, 5 Nov 2025 00:02:19 +0100 Subject: [PATCH] Fix tests for aliases with Pydantic V1 --- .../test_file_field_aliases_pd_v1.py | 622 +----------------- .../test_form_field_aliases_pd_v1.py | 570 +--------------- .../test_form_model_aliases_pd_v1.py | 519 +-------------- 3 files changed, 54 insertions(+), 1657 deletions(-) diff --git a/tests/test_aliases/test_file_field_aliases_pd_v1.py b/tests/test_aliases/test_file_field_aliases_pd_v1.py index 5a3265e03..f9de67317 100644 --- a/tests/test_aliases/test_file_field_aliases_pd_v1.py +++ b/tests/test_aliases/test_file_field_aliases_pd_v1.py @@ -1,16 +1,19 @@ -from typing import Optional +from typing import List, Optional import pytest -from dirty_equals import IsOneOf from fastapi import FastAPI, File from fastapi.testclient import TestClient +from ..utils import needs_pydanticv1 + +pytestmark = needs_pydanticv1 + app = FastAPI() # ===================================================================================== # File(alias=...) -# Current situation: Works, but schema is wrong -# Optional[list[bytes]] fails due to another issue (likely not related to aliases) +# Current situation: Works +# Schema generation for optional field and optional list fails due to issue likely not related to aliases # ------------------------------ # required field @@ -26,10 +29,7 @@ def test_required_field_alias_by_name(): resp = client.post("/required-field-alias", files={"file": b"content"}) assert resp.status_code == 422 detail = resp.json()["detail"] - assert detail[0]["msg"] == IsOneOf( - "Field required", - "field required", # TODO: remove when deprecating Pydantic v1 - ) + assert detail[0]["msg"] == "field required" assert "file_alias" in detail[0]["loc"] @@ -40,16 +40,12 @@ def test_required_field_alias_by_alias(): assert resp.json() == {"file_size": 7} -@pytest.mark.xfail(raises=AssertionError, strict=False) def test_required_field_alias_schema(): openapi = app.openapi() body_schema = openapi["components"]["schemas"]["Body_required_field_alias"] assert body_schema["properties"] == { "file_alias": {"title": "File Alias", "type": "string", "format": "binary"} } - # Currently fails due to issue with aliases: - # AssertionError: assert {'file': {'type': 'string', 'format': 'binary', 'title': 'File'}} == - # {'file_alias': {'title': 'File Alias', 'type': 'string', 'format': 'binary'}} # ------------------------------ @@ -89,9 +85,9 @@ def test_optional_field_alias_schema(): "title": "File Alias", } } - # Currently fails due to issue with aliases: + # Fails with: # AssertionError: assert - # {'file': {'anyOf': [{'type': 'string', 'format': 'binary'}, {'type': 'null'}], 'title': 'File'}} == + # {'file_alias': {'type': 'string', 'format': 'binary', 'title': 'File Alias'}} == # {'file_alias': {'anyOf': [{'type': 'string', 'format': 'binary'}, {'type': 'null'}], 'title': 'File Alias'}} @@ -100,7 +96,7 @@ def test_optional_field_alias_schema(): @app.post("/list-field-alias", operation_id="list_field_alias") -async def list_field_alias(files: list[bytes] = File(alias="files_alias")): +async def list_field_alias(files: List[bytes] = File(alias="files_alias")): return {"file_sizes": [len(file) for file in files]} @@ -111,10 +107,7 @@ def test_list_field_alias_by_name(): ) assert resp.status_code == 422 detail = resp.json()["detail"] - assert detail[0]["msg"] == IsOneOf( - "Field required", - "field required", # TODO: remove when deprecating Pydantic v1 - ) + assert detail[0]["msg"] == "field required" assert "files_alias" in detail[0]["loc"] @@ -128,7 +121,6 @@ def test_list_field_alias_by_alias(): assert resp.json() == {"file_sizes": [8, 8]} -@pytest.mark.xfail(raises=AssertionError, strict=False) def test_list_field_alias_schema(): openapi = app.openapi() body_schema = openapi["components"]["schemas"]["Body_list_field_alias"] @@ -139,10 +131,6 @@ def test_list_field_alias_schema(): "items": {"type": "string", "format": "binary"}, } } - # Currently fails due to issue with aliases: - # AssertionError: assert - # {'files': {'items': {'type': 'string', 'format': 'binary'}, 'type': 'array', 'title': 'Files'}} == - # {'files_alias': {'title': 'Files Alias', 'type': 'array', 'items': {'type': 'string', 'format': 'binary'}}} # ------------------------------ @@ -151,7 +139,7 @@ def test_list_field_alias_schema(): @app.post("/optional-list-field-alias", operation_id="optional_list_field_alias") async def optional_list_field_alias( - files: Optional[list[bytes]] = File(None, alias="files_alias"), + files: Optional[List[bytes]] = File(None, alias="files_alias"), ): if files is None: return {"file_sizes": None} @@ -168,20 +156,12 @@ def test_optional_list_field_alias_by_name(): assert resp.json() == {"file_sizes": None} -@pytest.mark.xfail( - reason="Optional[list[bytes]] File type causes TypeError in FastAPI", - raises=TypeError, - strict=False, -) def test_optional_list_field_alias_by_alias(): client = TestClient(app) resp = client.post( "/optional-list-field-alias", files=[("files_alias", b"content1"), ("files_alias", b"content2")], ) - # Currently fails due to some issue (likely unrelated to aliases) with: - # TypeError: issubclass() arg 1 must be a class - assert resp.json() == {"file_sizes": [8, 8]} @@ -198,579 +178,7 @@ def test_optional_list_field_alias_schema(): "title": "Files Alias", }, } - # Currently fails due to issue with aliases: + # Fails with: # AssertionError: assert - # {'files': {'anyOf': [{'items': {'type': 'string', 'format': 'binary'}, 'type': 'array'}, {'type': 'null'}], 'title': 'Files'}} == + # {'files_alias': {'items': {'type': 'string', 'format': 'binary'}, 'type': 'array', 'title': 'Files Alias'}} == # {'files_alias': {'anyOf': [{'items': {'type': 'string', 'format': 'binary'}, 'type': 'array'}, {'type': 'null'}], 'title': 'Files Alias'}} - - -# ===================================================================================== -# File(validation_alias=...) -# Current situation: schema is correct, but doesn't work (parameter name is used) -# Optional[list[bytes]] fails due to another issue (likely not related to aliases) - - -# ------------------------------ -# required field - - -@app.post( - "/required-field-validation-alias", operation_id="required_field_validation_alias" -) -async def required_field_validation_alias( - file: bytes = File(validation_alias="file_val_alias"), -): - return {"file_size": len(file)} - - -@pytest.mark.xfail(raises=AssertionError, strict=False) -def test_required_field_validation_alias_by_name(): - client = TestClient(app) - resp = client.post("/required-field-validation-alias", files={"file": b"content"}) - assert resp.status_code == 422 - # Currently fails due to issue with aliases: - # AssertionError: assert 200 == 422 - - detail = resp.json()["detail"] - assert detail[0]["msg"] == IsOneOf( - "Field required", - "field required", # TODO: remove when deprecating Pydantic v1 - ) - assert "file_val_alias" in detail[0]["loc"] - - -@pytest.mark.xfail(raises=AssertionError, strict=False) -def test_required_field_validation_alias_by_alias(): - client = TestClient(app) - resp = client.post( - "/required-field-validation-alias", files={"file_val_alias": b"content"} - ) - assert resp.status_code == 200 - # Currently fails due to issue with aliases: - # AssertionError: assert 422 == 200 - - assert resp.json() == {"file_size": 7} - - -def test_required_field_validation_alias_schema(): - openapi = app.openapi() - body_schema = openapi["components"]["schemas"][ - "Body_required_field_validation_alias" - ] - assert body_schema["properties"] == { - "file_val_alias": { - "title": "File Val Alias", - "type": "string", - "format": "binary", - }, - } - - -# ------------------------------ -# optional field - - -@app.post( - "/optional-field-validation-alias", operation_id="optional_field_validation_alias" -) -async def optional_field_validation_alias( - file: Optional[bytes] = File(None, validation_alias="file_val_alias"), -): - if file is None: - return {"file_size": None} - return {"file_size": len(file)} - - -@pytest.mark.xfail(raises=AssertionError, strict=False) -def test_optional_field_validation_alias_by_name(): - client = TestClient(app) - resp = client.post("/optional-field-validation-alias", files={"file": b"content"}) - assert resp.json() == {"file_size": None} - # Currently fails due to issue with aliases: - # AssertionError: assert {'file_size': 7} == {'file_size': None} - - -@pytest.mark.xfail(raises=AssertionError, strict=False) -def test_optional_field_validation_alias_by_alias(): - client = TestClient(app) - resp = client.post( - "/optional-field-validation-alias", files={"file_val_alias": b"content"} - ) - assert resp.json() == {"file_size": 7} - # Currently fails due to issue with aliases: - # AssertionError: assert {'file_size': None} == {'file_size': 7} - - -def test_optional_field_validation_alias_schema(): - openapi = app.openapi() - body_schema = openapi["components"]["schemas"][ - "Body_optional_field_validation_alias" - ] - assert body_schema["properties"] == { - "file_val_alias": { - "anyOf": [{"type": "string", "format": "binary"}, {"type": "null"}], - "title": "File Val Alias", - }, - } - - -# ------------------------------ -# list field - - -@app.post("/list-field-validation-alias", operation_id="list_field_validation_alias") -async def list_field_validation_alias( - files: list[bytes] = File(validation_alias="files_val_alias"), -): - return {"file_sizes": [len(file) for file in files]} - - -@pytest.mark.xfail(raises=AssertionError, strict=False) -def test_list_field_validation_alias_by_name(): - client = TestClient(app) - resp = client.post( - "/list-field-validation-alias", - files=[("files", b"content1"), ("files", b"content2")], - ) - assert resp.status_code == 422 - # Currently fails due to issue with aliases: - # AssertionError: assert 200 == 422 - - detail = resp.json()["detail"] - assert detail[0]["msg"] == IsOneOf( - "Field required", - "field required", # TODO: remove when deprecating Pydantic v1 - ) - assert "files_val_alias" in detail[0]["loc"] - - -@pytest.mark.xfail(raises=AssertionError, strict=False) -def test_list_field_validation_alias_by_alias(): - client = TestClient(app) - resp = client.post( - "/list-field-validation-alias", - files=[("files_val_alias", b"content1"), ("files_val_alias", b"content2")], - ) - assert resp.status_code == 200 - # Currently fails due to issue with aliases: - # AssertionError: assert 422 == 200 - - assert resp.json() == {"file_sizes": [8, 8]} - - -def test_list_field_validation_alias_schema(): - openapi = app.openapi() - body_schema = openapi["components"]["schemas"]["Body_list_field_validation_alias"] - assert body_schema["properties"] == { - "files_val_alias": { - "items": {"type": "string", "format": "binary"}, - "title": "Files Val Alias", - "type": "array", - }, - } - - -# ------------------------------ -# optional list field - - -@app.post( - "/optional-list-field-validation-alias", - operation_id="optional_list_field_validation_alias", -) -async def optional_list_field_validation_alias( - files: Optional[list[bytes]] = File(None, validation_alias="files_val_alias"), -): - if files is None: - return {"file_sizes": None} - return {"file_sizes": [len(file) for file in files]} - - -@pytest.mark.xfail( - reason="Optional[list[bytes]] File type causes TypeError in FastAPI", - raises=TypeError, - strict=False, -) -def test_optional_list_field_validation_alias_by_name(): - client = TestClient(app) - resp = client.post( - "/optional-list-field-validation-alias", - files=[("files", b"content1"), ("files", b"content2")], - ) - # Currently fails due to some issue (likely unrelated to aliases) with: - # TypeError: issubclass() arg 1 must be a class - - assert resp.json() == {"file_sizes": None} - # Will likely fail due to issue with aliases with: - # AssertionError: assert {'file_sizes': [8, 8]} == {'file_sizes': None} - - -@pytest.mark.xfail(raises=AssertionError, strict=False) -def test_optional_list_field_validation_alias_by_alias(): - client = TestClient(app) - resp = client.post( - "/optional-list-field-validation-alias", - files=[("files_val_alias", b"content1"), ("files_val_alias", b"content2")], - ) - assert resp.json() == {"file_sizes": [8, 8]} - # Currently fails due to issue with aliases: - # AssertionError: assert {'file_sizes': None} == {'file_sizes': [8, 8]} - - -def test_optional_list_field_validation_alias_schema(): - openapi = app.openapi() - body_schema = openapi["components"]["schemas"][ - "Body_optional_list_field_validation_alias" - ] - assert body_schema["properties"] == { - "files_val_alias": { - "anyOf": [ - {"items": {"type": "string", "format": "binary"}, "type": "array"}, - {"type": "null"}, - ], - "title": "Files Val Alias", - }, - } - - -# ===================================================================================== -# File(alias=..., validation_alias=...) -# Current situation: Schema is correct (validation_alias), but doesn't work (alias is used) -# Optional[list[bytes]] fails due to another issue (likely not related to aliases) - -# ------------------------------ -# required field - - -@app.post( - "/required-field-alias-and-validation-alias", - operation_id="required_field_alias_and_validation_alias", -) -async def required_field_alias_and_validation_alias( - file: bytes = File(alias="file_alias", validation_alias="file_val_alias"), -): - return {"file_size": len(file)} - - -@pytest.mark.xfail(raises=AssertionError, strict=False) -def test_required_field_alias_and_validation_alias_by_name(): - client = TestClient(app) - resp = client.post( - "/required-field-alias-and-validation-alias", files={"file": b"content"} - ) - assert resp.status_code == 422 - detail = resp.json()["detail"] - assert detail[0]["msg"] == IsOneOf( - "Field required", - "field required", # TODO: remove when deprecating Pydantic v1 - ) - assert "file_val_alias" in detail[0]["loc"] - # Currently fails due to issue with aliases: - # AssertionError: assert 'file_val_alias' in ['body', 'file_alias'] - - -@pytest.mark.xfail(raises=AssertionError, strict=False) -def test_required_field_alias_and_validation_alias_by_alias(): - client = TestClient(app) - resp = client.post( - "/required-field-alias-and-validation-alias", files={"file_alias": b"content"} - ) - assert resp.status_code == 422 - # Currently fails due to issue with aliases: - # AssertionError: assert 200 == 422 - - detail = resp.json()["detail"] - assert detail[0]["msg"] == IsOneOf( - "Field required", - "field required", # TODO: remove when deprecating Pydantic v1 - ) - assert "file_val_alias" in detail[0]["loc"] - - -@pytest.mark.xfail(raises=AssertionError, strict=False) -def test_required_field_alias_and_validation_alias_by_validation_alias(): - client = TestClient(app) - resp = client.post( - "/required-field-alias-and-validation-alias", - files={"file_val_alias": b"content"}, - ) - assert resp.status_code == 200 - # Currently fails due to issue with aliases: - # AssertionError: assert 422 == 200 - - assert resp.json() == {"file_size": 7} - - -def test_required_field_alias_and_validation_alias_schema(): - openapi = app.openapi() - body_schema = openapi["components"]["schemas"][ - "Body_required_field_alias_and_validation_alias" - ] - assert body_schema["properties"] == { - "file_val_alias": { - "title": "File Val Alias", - "type": "string", - "format": "binary", - } - } - - -# ------------------------------ -# optional field - - -@app.post( - "/optional-field-alias-and-validation-alias", - operation_id="optional_field_alias_and_validation_alias", -) -async def optional_field_alias_and_validation_alias( - file: Optional[bytes] = File( - None, alias="file_alias", validation_alias="file_val_alias" - ), -): - if file is None: - return {"file_size": None} - return {"file_size": len(file)} - - -def test_optional_field_alias_and_validation_alias_by_name(): - client = TestClient(app) - resp = client.post( - "/optional-field-alias-and-validation-alias", files={"file": b"content"} - ) - assert resp.status_code == 200 - assert resp.json() == {"file_size": None} - - -@pytest.mark.xfail(raises=AssertionError, strict=False) -def test_optional_field_alias_and_validation_alias_by_alias(): - client = TestClient(app) - resp = client.post( - "/optional-field-alias-and-validation-alias", files={"file_alias": b"content"} - ) - assert resp.status_code == 200 - assert resp.json() == {"file_size": None} - # Currently fails due to issue with aliases: - # AssertionError: assert {'file_size': 7} == {'file_size': None} - - -@pytest.mark.xfail(raises=AssertionError, strict=False) -def test_optional_field_alias_and_validation_alias_by_validation_alias(): - client = TestClient(app) - resp = client.post( - "/optional-field-alias-and-validation-alias", - files={"file_val_alias": b"content"}, - ) - assert resp.status_code == 200 - assert resp.json() == {"file_size": 7} - # Currently fails due to issue with aliases: - # AssertionError: assert {'file_size': None} == {'file_size': 7} - - -def test_optional_field_alias_and_validation_alias_schema(): - openapi = app.openapi() - body_schema = openapi["components"]["schemas"][ - "Body_optional_field_alias_and_validation_alias" - ] - assert body_schema["properties"] == { - "file_val_alias": { - "anyOf": [{"type": "string", "format": "binary"}, {"type": "null"}], - "title": "File Val Alias", - } - } - - -# ------------------------------ -# list field - - -@app.post( - "/list-field-alias-and-validation-alias", - operation_id="list_field_alias_and_validation_alias", -) -async def list_field_alias_and_validation_alias( - files: list[bytes] = File(alias="files_alias", validation_alias="files_val_alias"), -): - return {"file_sizes": [len(file) for file in files]} - - -@pytest.mark.xfail(raises=AssertionError, strict=False) -def test_list_field_alias_and_validation_alias_by_name(): - client = TestClient(app) - resp = client.post( - "/list-field-alias-and-validation-alias", - files=[("files", b"content1"), ("files", b"content2")], - ) - assert resp.status_code == 422 - detail = resp.json()["detail"] - assert detail[0]["msg"] == IsOneOf( - "Field required", - "field required", # TODO: remove when deprecating Pydantic v1 - ) - assert "files_val_alias" in detail[0]["loc"] - # Currently fails due to issue with aliases: - # AssertionError: assert 'files_val_alias' in ['body', 'files_alias'] - - -@pytest.mark.xfail(raises=AssertionError, strict=False) -def test_list_field_alias_and_validation_alias_by_alias(): - client = TestClient(app) - resp = client.post( - "/list-field-alias-and-validation-alias", - files=[("files_alias", b"content1"), ("files_alias", b"content2")], - ) - assert resp.status_code == 422 - # Currently fails due to issue with aliases: - # AssertionError: assert 200 == 422 - - detail = resp.json()["detail"] - assert detail[0]["msg"] == IsOneOf( - "Field required", - "field required", # TODO: remove when deprecating Pydantic v1 - ) - assert "files_val_alias" in detail[0]["loc"] - - -@pytest.mark.xfail(raises=AssertionError, strict=False) -def test_list_field_alias_and_validation_alias_by_validation_alias(): - client = TestClient(app) - resp = client.post( - "/list-field-alias-and-validation-alias", - files=[("files_val_alias", b"content1"), ("files_val_alias", b"content2")], - ) - assert resp.status_code == 200 - # Currently fails due to issue with aliases: - # AssertionError: assert 422 == 200 - - assert resp.json() == {"file_sizes": [8, 8]} - - -def test_list_field_alias_and_validation_alias_schema(): - openapi = app.openapi() - body_schema = openapi["components"]["schemas"][ - "Body_list_field_alias_and_validation_alias" - ] - assert body_schema["properties"] == { - "files_val_alias": { - "items": {"type": "string", "format": "binary"}, - "title": "Files Val Alias", - "type": "array", - }, - } - - -# ------------------------------ -# optional list field - - -@app.post( - "/optional-list-field-alias-and-validation-alias", - operation_id="optional_list_field_alias_and_validation_alias", -) -async def optional_list_field_alias_and_validation_alias( - files: Optional[list[bytes]] = File( - None, alias="files_alias", validation_alias="files_val_alias" - ), -): - if files is None: - return {"file_sizes": None} - return {"file_sizes": [len(file) for file in files]} - - -def test_optional_list_field_alias_and_validation_alias_by_name(): - client = TestClient(app) - resp = client.post( - "/optional-list-field-alias-and-validation-alias", - files=[("files", b"content1"), ("files", b"content2")], - ) - assert resp.status_code == 200 - assert resp.json() == {"file_sizes": None} - - -@pytest.mark.xfail( - reason="Optional[list[bytes]] File type causes TypeError in FastAPI", - raises=TypeError, - strict=False, -) -def test_optional_list_field_alias_and_validation_alias_by_alias(): - client = TestClient(app) - resp = client.post( - "/optional-list-field-alias-and-validation-alias", - files=[("files_alias", b"content1"), ("files_alias", b"content2")], - ) - # Currently fails due to some issue (likely unrelated to aliases) with: - # TypeError: issubclass() arg 1 must be a class - - assert resp.status_code == 200 - assert resp.json() == {"file_sizes": None} - # Will likely fail due to issue with aliases: - # AssertionError: assert {'file_sizes': [8, 8]} == {'file_sizes': None} - - -@pytest.mark.xfail(raises=AssertionError, strict=False) -def test_optional_list_field_alias_and_validation_alias_by_validation_alias(): - client = TestClient(app) - resp = client.post( - "/optional-list-field-alias-and-validation-alias", - files=[("files_val_alias", b"content1"), ("files_val_alias", b"content2")], - ) - assert resp.status_code == 200 - assert resp.json() == {"file_sizes": [8, 8]} - # Currently fails due to issue with aliases: - # AssertionError: assert {'file_sizes': None} == {'file_sizes': [8, 8]} - - -def test_optional_list_field_alias_and_validation_alias_schema(): - openapi = app.openapi() - body_schema = openapi["components"]["schemas"][ - "Body_optional_list_field_alias_and_validation_alias" - ] - assert body_schema["properties"] == { - "files_val_alias": { - "anyOf": [ - {"items": {"type": "string", "format": "binary"}, "type": "array"}, - {"type": "null"}, - ], - "title": "Files Val Alias", - }, - } - - -# ===================================================================================== -# File(alias=..., validation_alias=...) # alias == validation_alias -# The only working solution (current workaround) -# TODO: remove when issue with File field aliases is fixed - - -@app.post("/workaround", operation_id="workaround") -async def workaround( - file: bytes = File(alias="file_alias", validation_alias="file_alias"), -): - return {"file_size": len(file)} - - -def test_workaround_by_name(): - client = TestClient(app) - resp = client.post("/workaround", files={"file": b"content"}) - assert resp.status_code == 422 - detail = resp.json()["detail"] - assert detail[0]["msg"] == IsOneOf( - "Field required", - "field required", # TODO: remove when deprecating Pydantic v1 - ) - assert "file_alias" in detail[0]["loc"] - - -def test_workaround_by_alias(): - client = TestClient(app) - resp = client.post("/workaround", files={"file_alias": b"content"}) - assert resp.status_code == 200 - assert resp.json() == {"file_size": 7} - - -def test_workaround_schema(): - openapi = app.openapi() - body_schema = openapi["components"]["schemas"]["Body_workaround"] - assert body_schema["properties"] == { - "file_alias": {"title": "File Alias", "type": "string", "format": "binary"} - } diff --git a/tests/test_aliases/test_form_field_aliases_pd_v1.py b/tests/test_aliases/test_form_field_aliases_pd_v1.py index 39920d7f6..d6402d8a0 100644 --- a/tests/test_aliases/test_form_field_aliases_pd_v1.py +++ b/tests/test_aliases/test_form_field_aliases_pd_v1.py @@ -1,15 +1,20 @@ -from typing import Optional +from typing import List, Optional import pytest -from dirty_equals import IsOneOf from fastapi import FastAPI, Form from fastapi.testclient import TestClient +from ..utils import needs_pydanticv1 + +pytestmark = needs_pydanticv1 + app = FastAPI() # ===================================================================================== # Form(alias=...) -# Current situation: Works, but schema is wrong +# Current situation: Works +# Schema generation for optional field and optional list fails due to issue likely not related to aliases + # ------------------------------ # required field @@ -25,10 +30,7 @@ def test_required_field_alias_by_name(): resp = client.post("/required-field-alias", data={"param": "123"}) assert resp.status_code == 422 detail = resp.json()["detail"] - assert detail[0]["msg"] == IsOneOf( - "Field required", - "field required", # TODO: remove when deprecating Pydantic v1 - ) + assert detail[0]["msg"] == "field required" assert "param_alias" in detail[0]["loc"] @@ -39,16 +41,12 @@ def test_required_field_alias_by_alias(): assert resp.json() == {"param": "123"} -@pytest.mark.xfail(raises=AssertionError, strict=False) def test_required_field_alias_schema(): openapi = app.openapi() body_schema = openapi["components"]["schemas"]["Body_required_field_alias"] assert body_schema["properties"] == { "param_alias": {"title": "Param Alias", "type": "string"} } - # Currently fails due to issue with aliases: - # AssertionError: assert {'param': {'type': 'string', 'title': 'Param'}} == - # {'param_alias': {'title': 'Param Alias', 'type': 'string'}} # ------------------------------ @@ -86,9 +84,9 @@ def test_optional_field_alias_schema(): "title": "Param Alias", } } - # Currently fails due to issue with aliases: + # Fails with: # AssertionError: assert - # {'param': {'anyOf': [{'type': 'string'}, {'type': 'null'}], 'title': 'Param'}} == + # {'param_alias': {'type': 'string', 'title': 'Param Alias'}} == # {'param_alias': {'anyOf': [{'type': 'string'}, {'type': 'null'}], 'title': 'Param Alias'}} @@ -97,7 +95,7 @@ def test_optional_field_alias_schema(): @app.post("/list-field-alias", operation_id="list_field_alias") -async def list_field_alias(param: list[str] = Form(alias="param_alias")): +async def list_field_alias(param: List[str] = Form(alias="param_alias")): return {"param": param} @@ -106,10 +104,7 @@ def test_list_field_alias_by_name(): resp = client.post("/list-field-alias", data={"param": ["123", "456"]}) assert resp.status_code == 422 detail = resp.json()["detail"] - assert detail[0]["msg"] == IsOneOf( - "Field required", - "field required", # TODO: remove when deprecating Pydantic v1 - ) + assert detail[0]["msg"] == "field required" assert "param_alias" in detail[0]["loc"] @@ -120,7 +115,6 @@ def test_list_field_alias_by_alias(): assert resp.json() == {"param": ["123", "456"]} -@pytest.mark.xfail(raises=AssertionError, strict=False) def test_list_field_alias_schema(): openapi = app.openapi() body_schema = openapi["components"]["schemas"]["Body_list_field_alias"] @@ -131,10 +125,6 @@ def test_list_field_alias_schema(): "items": {"type": "string"}, } } - # Currently fails due to issue with aliases: - # AssertionError: assert - # {'param': {'items': {'type': 'string'}, 'type': 'array', 'title': 'Param'}} == - # {'param_alias': {'title': 'Param Alias', 'type': 'array', 'items': {'type': 'string'}}} # ------------------------------ @@ -143,7 +133,7 @@ def test_list_field_alias_schema(): @app.post("/optional-list-field-alias", operation_id="optional_list_field_alias") async def optional_list_field_alias( - param: Optional[list[str]] = Form(None, alias="param_alias"), + param: Optional[List[str]] = Form(None, alias="param_alias"), ): return {"param": param} @@ -174,535 +164,7 @@ def test_optional_list_field_alias_schema(): "title": "Param Alias", }, } - # Currently fails due to issue with aliases: + # Fails with: # AssertionError: assert - # {'param': {'anyOf': [{'items': {'type': 'string'}, 'type': 'array'}, {'type': 'null'}], 'title': 'Param'}} == + # {'param_alias': {'items': {'type': 'string'}, 'type': 'array', 'title': 'Param Alias'}} == # {'param_alias': {'anyOf': [{'items': {'type': 'string'}, 'type': 'array'}, {'type': 'null'}], 'title': 'Param Alias'}} - - -# ===================================================================================== -# Form(validation_alias=...) -# Current situation: schema is correct, but doesn't work (parameter name is used) - - -# ------------------------------ -# required field - - -@app.post( - "/required-field-validation-alias", operation_id="required_field_validation_alias" -) -async def required_field_validation_alias( - param: str = Form(validation_alias="param_val_alias"), -): - return {"param": param} - - -@pytest.mark.xfail(raises=AssertionError, strict=False) -def test_required_field_validation_alias_by_name(): - client = TestClient(app) - resp = client.post("/required-field-validation-alias", data={"param": "123"}) - assert resp.status_code == 422 - # Currently fails due to issue with aliases: - # AssertionError: assert 200 == 422 - - detail = resp.json()["detail"] - assert detail[0]["msg"] == IsOneOf( - "Field required", - "field required", # TODO: remove when deprecating Pydantic v1 - ) - assert "param_val_alias" in detail[0]["loc"] - - -@pytest.mark.xfail(raises=AssertionError, strict=False) -def test_required_field_validation_alias_by_alias(): - client = TestClient(app) - resp = client.post( - "/required-field-validation-alias", data={"param_val_alias": "123"} - ) - assert resp.status_code == 200 - # Currently fails due to issue with aliases: - # AssertionError: assert 422 == 200 - - assert resp.json() == {"param": "123"} - - -def test_required_field_validation_alias_schema(): - openapi = app.openapi() - body_schema = openapi["components"]["schemas"][ - "Body_required_field_validation_alias" - ] - assert body_schema["properties"] == { - "param_val_alias": {"title": "Param Val Alias", "type": "string"}, - } - - -# ------------------------------ -# optional field - - -@app.post( - "/optional-field-validation-alias", operation_id="optional_field_validation_alias" -) -async def optional_field_validation_alias( - param: Optional[str] = Form(None, validation_alias="param_val_alias"), -): - return {"param": param} - - -@pytest.mark.xfail(raises=AssertionError, strict=False) -def test_optional_field_validation_alias_by_name(): - client = TestClient(app) - resp = client.post("/optional-field-validation-alias", data={"param": "123"}) - assert resp.json() == {"param": None} - # Currently fails due to issue with aliases: - # AssertionError: assert {'param': '123'} == {'param': None} - - -@pytest.mark.xfail(raises=AssertionError, strict=False) -def test_optional_field_validation_alias_by_alias(): - client = TestClient(app) - resp = client.post( - "/optional-field-validation-alias", data={"param_val_alias": "123"} - ) - assert resp.status_code == 200 - assert resp.json() == {"param": "123"} - # Currently fails due to issue with aliases: - # AssertionError: assert {'param': None} == {'param': '123'} - - -def test_optional_field_validation_alias_schema(): - openapi = app.openapi() - body_schema = openapi["components"]["schemas"][ - "Body_optional_field_validation_alias" - ] - assert body_schema["properties"] == { - "param_val_alias": { - "anyOf": [{"type": "string"}, {"type": "null"}], - "title": "Param Val Alias", - }, - } - - -# ------------------------------ -# list field - - -@app.post("/list-field-validation-alias", operation_id="list_field_validation_alias") -async def list_field_validation_alias( - param: list[str] = Form(validation_alias="param_val_alias"), -): - return {"param": param} - - -@pytest.mark.xfail(raises=AssertionError, strict=False) -def test_list_field_validation_alias_by_name(): - client = TestClient(app) - resp = client.post("/list-field-validation-alias", data={"param": ["123", "456"]}) - assert resp.status_code == 422 - # Currently fails due to issue with aliases: - # AssertionError: assert 200 == 422 - - detail = resp.json()["detail"] - assert detail[0]["msg"] == IsOneOf( - "Field required", - "field required", # TODO: remove when deprecating Pydantic v1 - ) - assert "param_val_alias" in detail[0]["loc"] - - -@pytest.mark.xfail(raises=AssertionError, strict=False) -def test_list_field_validation_alias_by_alias(): - client = TestClient(app) - resp = client.post( - "/list-field-validation-alias", data={"param_val_alias": ["123", "456"]} - ) - assert resp.status_code == 200 - # Currently fails due to issue with aliases: - # AssertionError: assert 422 == 200 - - assert resp.json() == {"param": ["123", "456"]} - - -def test_list_field_validation_alias_schema(): - openapi = app.openapi() - body_schema = openapi["components"]["schemas"]["Body_list_field_validation_alias"] - assert body_schema["properties"] == { - "param_val_alias": { - "items": {"type": "string"}, - "title": "Param Val Alias", - "type": "array", - }, - } - - -# ------------------------------ -# optional list field - - -@app.post( - "/optional-list-field-validation-alias", - operation_id="optional_list_field_validation_alias", -) -async def optional_list_field_validation_alias( - param: Optional[list[str]] = Form(None, validation_alias="param_val_alias"), -): - return {"param": param} - - -@pytest.mark.xfail(raises=AssertionError, strict=False) -def test_optional_list_field_validation_alias_by_name(): - client = TestClient(app) - resp = client.post( - "/optional-list-field-validation-alias", data={"param": ["123", "456"]} - ) - assert resp.status_code == 200 - assert resp.json() == {"param": None} - # Currently fails due to issue with aliases: - # AssertionError: assert {'param': ['123', '456']} == {'param': None} - - -@pytest.mark.xfail(raises=AssertionError, strict=False) -def test_optional_list_field_validation_alias_by_alias(): - client = TestClient(app) - resp = client.post( - "/optional-list-field-validation-alias", - data={"param_val_alias": ["123", "456"]}, - ) - assert resp.status_code == 200 - assert resp.json() == {"param": ["123", "456"]} - # Currently fails due to issue with aliases: - # AssertionError: assert {'param': None} == {'param': ['123', '456']} - - -def test_optional_list_field_validation_alias_schema(): - openapi = app.openapi() - body_schema = openapi["components"]["schemas"][ - "Body_optional_list_field_validation_alias" - ] - assert body_schema["properties"] == { - "param_val_alias": { - "anyOf": [{"items": {"type": "string"}, "type": "array"}, {"type": "null"}], - "title": "Param Val Alias", - }, - } - - -# ===================================================================================== -# Form(alias=..., validation_alias=...) -# Current situation: Schema is correct (validation_alias), but doesn't work (alias is used) - -# ------------------------------ -# required field - - -@app.post( - "/required-field-alias-and-validation-alias", - operation_id="required_field_alias_and_validation_alias", -) -async def required_field_alias_and_validation_alias( - param: str = Form(alias="param_alias", validation_alias="param_val_alias"), -): - return {"param": param} - - -@pytest.mark.xfail(raises=AssertionError, strict=False) -def test_required_field_alias_and_validation_alias_by_name(): - client = TestClient(app) - resp = client.post( - "/required-field-alias-and-validation-alias", data={"param": "123"} - ) - assert resp.status_code == 422 - detail = resp.json()["detail"] - assert detail[0]["msg"] == IsOneOf( - "Field required", - "field required", # TODO: remove when deprecating Pydantic v1 - ) - assert "param_val_alias" in detail[0]["loc"] - # Currently fails due to issue with aliases: - # AssertionError: assert 'param_val_alias' in ['body', 'param_alias'] - - -@pytest.mark.xfail(raises=AssertionError, strict=False) -def test_required_field_alias_and_validation_alias_by_alias(): - client = TestClient(app) - resp = client.post( - "/required-field-alias-and-validation-alias", data={"param_alias": "123"} - ) - assert resp.status_code == 422 - # Currently fails due to issue with aliases: - # AssertionError: assert 200 == 422 - - detail = resp.json()["detail"] - assert detail[0]["msg"] == IsOneOf( - "Field required", - "field required", # TODO: remove when deprecating Pydantic v1 - ) - assert "param_val_alias" in detail[0]["loc"] - - -@pytest.mark.xfail(raises=AssertionError, strict=False) -def test_required_field_alias_and_validation_alias_by_validation_alias(): - client = TestClient(app) - resp = client.post( - "/required-field-alias-and-validation-alias", data={"param_val_alias": "123"} - ) - assert resp.status_code == 200 - # Currently fails due to issue with aliases: - # AssertionError: assert 422 == 200 - - assert resp.json() == {"param": "123"} - - -def test_required_field_alias_and_validation_alias_schema(): - openapi = app.openapi() - body_schema = openapi["components"]["schemas"][ - "Body_required_field_alias_and_validation_alias" - ] - assert body_schema["properties"] == { - "param_val_alias": {"title": "Param Val Alias", "type": "string"} - } - - -# ------------------------------ -# optional field - - -@app.post( - "/optional-field-alias-and-validation-alias", - operation_id="optional_field_alias_and_validation_alias", -) -async def optional_field_alias_and_validation_alias( - param: Optional[str] = Form( - None, alias="param_alias", validation_alias="param_val_alias" - ), -): - return {"param": param} - - -def test_optional_field_alias_and_validation_alias_by_name(): - client = TestClient(app) - resp = client.post( - "/optional-field-alias-and-validation-alias", data={"param": "123"} - ) - assert resp.status_code == 200 - assert resp.json() == {"param": None} - - -@pytest.mark.xfail(raises=AssertionError, strict=False) -def test_optional_field_alias_and_validation_alias_by_alias(): - client = TestClient(app) - resp = client.post( - "/optional-field-alias-and-validation-alias", data={"param_alias": "123"} - ) - assert resp.status_code == 200 - assert resp.json() == {"param": None} - # Currently fails due to issue with aliases: - # AssertionError: assert {'param': '123'} == {'param': None} - - -@pytest.mark.xfail(raises=AssertionError, strict=False) -def test_optional_field_alias_and_validation_alias_by_validation_alias(): - client = TestClient(app) - resp = client.post( - "/optional-field-alias-and-validation-alias", data={"param_val_alias": "123"} - ) - assert resp.status_code == 200 - assert resp.json() == {"param": "123"} - # Currently fails due to issue with aliases: - # AssertionError: assert {'param': None} == {'param': '123'} - - -def test_optional_field_alias_and_validation_alias_schema(): - openapi = app.openapi() - body_schema = openapi["components"]["schemas"][ - "Body_optional_field_alias_and_validation_alias" - ] - assert body_schema["properties"] == { - "param_val_alias": { - "anyOf": [{"type": "string"}, {"type": "null"}], - "title": "Param Val Alias", - } - } - - -# ------------------------------ -# list field - - -@app.post( - "/list-field-alias-and-validation-alias", - operation_id="list_field_alias_and_validation_alias", -) -async def list_field_alias_and_validation_alias( - param: list[str] = Form(alias="param_alias", validation_alias="param_val_alias"), -): - return {"param": param} - - -@pytest.mark.xfail(raises=AssertionError, strict=False) -def test_list_field_alias_and_validation_alias_by_name(): - client = TestClient(app) - resp = client.post( - "/list-field-alias-and-validation-alias", data={"param": ["123", "456"]} - ) - assert resp.status_code == 422 - detail = resp.json()["detail"] - assert detail[0]["msg"] == IsOneOf( - "Field required", - "field required", # TODO: remove when deprecating Pydantic v1 - ) - assert "param_val_alias" in detail[0]["loc"] - # Currently fails due to issue with aliases: - # AssertionError: assert 'param_val_alias' in ['body', 'param_alias'] - - -@pytest.mark.xfail(raises=AssertionError, strict=False) -def test_list_field_alias_and_validation_alias_by_alias(): - client = TestClient(app) - resp = client.post( - "/list-field-alias-and-validation-alias", data={"param_alias": ["123", "456"]} - ) - assert resp.status_code == 422 - # Currently fails due to issue with aliases: - # AssertionError: assert 200 == 422 - - detail = resp.json()["detail"] - assert detail[0]["msg"] == IsOneOf( - "Field required", - "field required", # TODO: remove when deprecating Pydantic v1 - ) - assert "param_val_alias" in detail[0]["loc"] - - -@pytest.mark.xfail(raises=AssertionError, strict=False) -def test_list_field_alias_and_validation_alias_by_validation_alias(): - client = TestClient(app) - resp = client.post( - "/list-field-alias-and-validation-alias", - data={"param_val_alias": ["123", "456"]}, - ) - assert resp.status_code == 200 - # Currently fails due to issue with aliases: - # AssertionError: assert 422 == 200 - - assert resp.json() == {"param": ["123", "456"]} - - -def test_list_field_alias_and_validation_alias_schema(): - openapi = app.openapi() - body_schema = openapi["components"]["schemas"][ - "Body_list_field_alias_and_validation_alias" - ] - assert body_schema["properties"] == { - "param_val_alias": { - "items": {"type": "string"}, - "title": "Param Val Alias", - "type": "array", - }, - } - - -# ------------------------------ -# optional list field - - -@app.post( - "/optional-list-field-alias-and-validation-alias", - operation_id="optional_list_field_alias_and_validation_alias", -) -async def optional_list_field_alias_and_validation_alias( - param: Optional[list[str]] = Form( - None, alias="param_alias", validation_alias="param_val_alias" - ), -): - return {"param": param} - - -def test_optional_list_field_alias_and_validation_alias_by_name(): - client = TestClient(app) - resp = client.post( - "/optional-list-field-alias-and-validation-alias", - data={"param": ["123", "456"]}, - ) - assert resp.status_code == 200 - assert resp.json() == {"param": None} - - -@pytest.mark.xfail(raises=AssertionError, strict=False) -def test_optional_list_field_alias_and_validation_alias_by_alias(): - client = TestClient(app) - resp = client.post( - "/optional-list-field-alias-and-validation-alias", - data={"param_alias": ["123", "456"]}, - ) - assert resp.status_code == 200 - assert resp.json() == {"param": None} - # Currently fails due to issue with aliases: - # AssertionError: assert {'param': ['123', '456']} == {'param': None} - - -@pytest.mark.xfail(raises=AssertionError, strict=False) -def test_optional_list_field_alias_and_validation_alias_by_validation_alias(): - client = TestClient(app) - resp = client.post( - "/optional-list-field-alias-and-validation-alias", - data={"param_val_alias": ["123", "456"]}, - ) - assert resp.status_code == 200 - assert resp.json() == {"param": ["123", "456"]} - # Currently fails due to issue with aliases: - # AssertionError: assert {'param': None} == {'param': ['123', '456']} - - -def test_optional_list_field_alias_and_validation_alias_schema(): - openapi = app.openapi() - body_schema = openapi["components"]["schemas"][ - "Body_optional_list_field_alias_and_validation_alias" - ] - assert body_schema["properties"] == { - "param_val_alias": { - "anyOf": [{"items": {"type": "string"}, "type": "array"}, {"type": "null"}], - "title": "Param Val Alias", - }, - } - - -# ===================================================================================== -# Form(alias=..., validation_alias=...) # alias == validation_alias -# The only working solution (current workaround) -# TODO: remove when issue with Form field aliases is fixed - - -@app.post("/workaround", operation_id="workaround") -async def workaround( - param: str = Form(alias="param_alias", validation_alias="param_alias"), -): - return {"param": param} - - -def test_workaround_by_name(): - client = TestClient(app) - resp = client.post("/workaround", data={"param": "123"}) - assert resp.status_code == 422 - detail = resp.json()["detail"] - assert detail[0]["msg"] == IsOneOf( - "Field required", - "field required", # TODO: remove when deprecating Pydantic v1 - ) - assert "param_alias" in detail[0]["loc"] - - -def test_workaround_by_alias(): - client = TestClient(app) - resp = client.post("/workaround", data={"param_alias": "123"}) - assert resp.status_code == 200 - assert resp.json() == {"param": "123"} - - -def test_workaround_schema(): - openapi = app.openapi() - body_schema = openapi["components"]["schemas"]["Body_workaround"] - assert body_schema["properties"] == { - "param_alias": {"title": "Param Alias", "type": "string"} - } diff --git a/tests/test_aliases/test_form_model_aliases_pd_v1.py b/tests/test_aliases/test_form_model_aliases_pd_v1.py index 48b1d0da4..163939e70 100644 --- a/tests/test_aliases/test_form_model_aliases_pd_v1.py +++ b/tests/test_aliases/test_form_model_aliases_pd_v1.py @@ -1,16 +1,20 @@ -from typing import Annotated, Optional +from typing import Annotated, List, Optional import pytest -from dirty_equals import IsOneOf from fastapi import FastAPI, Form from fastapi.testclient import TestClient from pydantic import BaseModel, Field +from ..utils import needs_pydanticv1 + +pytestmark = needs_pydanticv1 + app = FastAPI() # ===================================================================================== # Field(alias=...) -# Current situation: fully works +# Current situation: works +# Schema generation for optional field and optional list fails due to issue likely not related to aliases # ------------------------------ # required field @@ -30,10 +34,7 @@ def test_required_field_alias_model_by_name(): resp = client.post("/required-field-alias-model", data={"param": "123"}) assert resp.status_code == 422 detail = resp.json()["detail"] - assert detail[0]["msg"] == IsOneOf( - "Field required", - "field required", # TODO: remove when deprecating Pydantic v1 - ) + assert detail[0]["msg"] == "field required" assert "param_alias" in detail[0]["loc"] @@ -79,6 +80,7 @@ def test_optional_field_alias_model_by_alias(): assert resp.json() == {"param": "123"} +@pytest.mark.xfail(raises=AssertionError, strict=False) def test_optional_field_alias_model_schema(): openapi = app.openapi() body_schema = openapi["components"]["schemas"]["OptionalFieldAliasModel"] @@ -88,6 +90,10 @@ def test_optional_field_alias_model_schema(): "title": "Param Alias", }, } + # Fails with: + # AssertionError: assert + # {'param_alias': {'type': 'string', 'title': 'Param Alias'}} == + # {'param_alias': {'anyOf': [{'type': 'string'}, {'type': 'null'}], 'title': 'Param Alias'}} # ------------------------------ @@ -95,7 +101,7 @@ def test_optional_field_alias_model_schema(): class ListFieldAliasModel(BaseModel): - param: list[str] = Field(alias="param_alias") + param: List[str] = Field(alias="param_alias") @app.post("/list-field-alias-model") @@ -108,10 +114,7 @@ def test_list_field_alias_model_by_name(): resp = client.post("/list-field-alias-model", data={"param": ["123", "456"]}) assert resp.status_code == 422 detail = resp.json()["detail"] - assert detail[0]["msg"] == IsOneOf( - "Field required", - "field required", # TODO: remove when deprecating Pydantic v1 - ) + assert detail[0]["msg"] == "field required" assert "param_alias" in detail[0]["loc"] @@ -139,7 +142,7 @@ def test_list_field_alias_model_schema(): class OptionalListFieldAliasModel(BaseModel): - param: Optional[list[str]] = Field(None, alias="param_alias") + param: Optional[List[str]] = Field(None, alias="param_alias") @app.post("/optional-list-field-alias-model") @@ -167,493 +170,17 @@ def test_optional_list_field_alias_model_by_alias(): assert resp.json() == {"param": ["123", "456"]} +@pytest.mark.xfail(raises=AssertionError, strict=False) def test_optional_list_field_alias_model_schema(): openapi = app.openapi() body_schema = openapi["components"]["schemas"]["OptionalListFieldAliasModel"] - assert len(body_schema["properties"]) == 1 - assert "param_alias" in body_schema["properties"] - - -# ===================================================================================== -# Field(validation_alias=...) -# Current situation: works except lists - - -# ------------------------------ -# required field - - -class RequiredFieldValidationAliasModel(BaseModel): - param: str = Field(validation_alias="param_val_alias") - - -@app.post( - "/required-field-validation-alias-model", - operation_id="required_field_validation_alias_model", -) -async def required_field_validation_alias_model( - data: Annotated[RequiredFieldValidationAliasModel, Form()], -): - return {"param": data.param} - - -def test_required_field_validation_alias_model_by_name(): - client = TestClient(app) - resp = client.post("/required-field-validation-alias-model", data={"param": "123"}) - assert resp.status_code == 422 - detail = resp.json()["detail"] - assert detail[0]["msg"] == IsOneOf( - "Field required", - "field required", # TODO: remove when deprecating Pydantic v1 - ) - assert "param_val_alias" in detail[0]["loc"] - - -def test_required_field_validation_alias_model_by_alias(): - client = TestClient(app) - resp = client.post( - "/required-field-validation-alias-model", data={"param_val_alias": "123"} - ) - assert resp.status_code == 200 - assert resp.json() == {"param": "123"} - - -def test_required_field_validation_alias_model_schema(): - openapi = app.openapi() - body_schema = openapi["components"]["schemas"]["RequiredFieldValidationAliasModel"] assert body_schema["properties"] == { - "param_val_alias": {"title": "Param Val Alias", "type": "string"} - } - - -# ------------------------------ -# optional field - - -class OptionalFieldValidationAliasModel(BaseModel): - param: Optional[str] = Field(None, validation_alias="param_val_alias") - - -@app.post( - "/optional-field-validation-alias-model", - operation_id="optional_field_validation_alias_model", -) -async def optional_field_validation_alias_model( - data: Annotated[OptionalFieldValidationAliasModel, Form()], -): - return {"param": data.param} - - -def test_optional_field_validation_alias_model_by_name(): - client = TestClient(app) - resp = client.post("/optional-field-validation-alias-model", data={"param": "123"}) - assert resp.status_code == 200 - assert resp.json() == {"param": None} - - -def test_optional_field_validation_alias_model_by_alias(): - client = TestClient(app) - resp = client.post( - "/optional-field-validation-alias-model", data={"param_val_alias": "123"} - ) - assert resp.status_code == 200 - assert resp.json() == {"param": "123"} - - -def test_optional_field_validation_alias_model_schema(): - openapi = app.openapi() - body_schema = openapi["components"]["schemas"]["OptionalFieldValidationAliasModel"] - assert body_schema["properties"] == { - "param_val_alias": { - "anyOf": [{"type": "string"}, {"type": "null"}], - "title": "Param Val Alias", - }, - } - - -# ------------------------------ -# list field - - -class ListFieldValidationAliasModel(BaseModel): - param: list[str] = Field(validation_alias="param_val_alias") - - -@app.post( - "/list-field-validation-alias-model", - operation_id="list_field_validation_alias_model", -) -async def list_field_validation_alias_model( - data: Annotated[ListFieldValidationAliasModel, Form()], -): - return {"param": data.param} - - -def test_list_field_validation_alias_model_by_name(): - client = TestClient(app) - resp = client.post( - "/list-field-validation-alias-model", data={"param": ["123", "456"]} - ) - assert resp.status_code == 422 - detail = resp.json()["detail"] - assert detail[0]["msg"] == IsOneOf( - "Field required", - "field required", # TODO: remove when deprecating Pydantic v1 - ) - assert "param_val_alias" in detail[0]["loc"] - - -@pytest.mark.xfail(raises=AssertionError, strict=False) -def test_list_field_validation_alias_model_by_alias(): - client = TestClient(app) - resp = client.post( - "/list-field-validation-alias-model", data={"param_val_alias": ["123", "456"]} - ) - assert resp.status_code == 200, resp.text - # Currently fails due to some issue: - # AssertionError: assert 422 == 200 - # {"detail":[{"type":"list_type","loc":["body","param_val_alias"],"msg":"Input should be a valid list","input":"456"}]} - - assert resp.json() == {"param": ["123", "456"]} - - -def test_list_field_validation_alias_model_schema(): - openapi = app.openapi() - body_schema = openapi["components"]["schemas"]["ListFieldValidationAliasModel"] - assert body_schema["properties"] == { - "param_val_alias": { - "items": {"type": "string"}, - "title": "Param Val Alias", - "type": "array", - } - } - - -# ------------------------------ -# optional list field - - -class OptionalListFieldValidationAliasModel(BaseModel): - param: Optional[list[str]] = Field(None, validation_alias="param_val_alias") - - -@app.post( - "/optional-list-field-validation-alias-model", - operation_id="optional_list_field_validation_alias_model", -) -async def optional_list_field_validation_alias_model( - data: Annotated[OptionalListFieldValidationAliasModel, Form()], -): - return {"param": data.param} - - -def test_optional_list_field_validation_alias_model_by_name(): - client = TestClient(app) - resp = client.post( - "/optional-list-field-validation-alias-model", data={"param": ["123", "456"]} - ) - assert resp.status_code == 200 - assert resp.json() == {"param": None} - - -@pytest.mark.xfail(raises=AssertionError, strict=False) -def test_optional_list_field_validation_alias_model_by_alias(): - client = TestClient(app) - resp = client.post( - "/optional-list-field-validation-alias-model", - data={"param_val_alias": ["123", "456"]}, - ) - assert resp.status_code == 200, resp.text - # Currently fails due to some issue: - # AssertionError: assert 422 == 200 - # {"detail":[{"type":"list_type","loc":["body","param_val_alias"],"msg":"Input should be a valid list","input":"456"}]} - - assert resp.json() == {"param": ["123", "456"]} - - -def test_optional_list_field_validation_alias_model_schema(): - openapi = app.openapi() - body_schema = openapi["components"]["schemas"][ - "OptionalListFieldValidationAliasModel" - ] - assert body_schema["properties"] == { - "param_val_alias": { - "anyOf": [{"items": {"type": "string"}, "type": "array"}, {"type": "null"}], - "title": "Param Val Alias", - }, - } - - -# ===================================================================================== -# Field(alias=..., validation_alias=...) -# Current situation: works except lists - -# ------------------------------ -# required field - - -class RequiredFieldAliasAndValidationAliasModel(BaseModel): - param: str = Field(alias="param_alias", validation_alias="param_val_alias") - - -@app.post( - "/required-field-alias-and-validation-alias-model", - operation_id="required_field_alias_and_validation_alias_model", -) -async def required_field_alias_and_validation_alias_model( - data: Annotated[RequiredFieldAliasAndValidationAliasModel, Form()], -): - return {"param": data.param} - - -def test_required_field_alias_and_validation_alias_model_by_name(): - client = TestClient(app) - resp = client.post( - "/required-field-alias-and-validation-alias-model", data={"param": "123"} - ) - assert resp.status_code == 422 - detail = resp.json()["detail"] - assert detail[0]["msg"] == IsOneOf( - "Field required", - "field required", # TODO: remove when deprecating Pydantic v1 - ) - assert "param_val_alias" in detail[0]["loc"] - - -def test_required_field_alias_and_validation_alias_model_by_alias(): - client = TestClient(app) - resp = client.post( - "/required-field-alias-and-validation-alias-model", data={"param_alias": "123"} - ) - assert resp.status_code == 422 - detail = resp.json()["detail"] - assert detail[0]["msg"] == IsOneOf( - "Field required", - "field required", # TODO: remove when deprecating Pydantic v1 - ) - assert "param_val_alias" in detail[0]["loc"] - - -def test_required_field_alias_and_validation_alias_model_by_validation_alias(): - client = TestClient(app) - resp = client.post( - "/required-field-alias-and-validation-alias-model", - data={"param_val_alias": "123"}, - ) - assert resp.status_code == 200 - assert resp.json() == {"param": "123"} - - -def test_required_field_alias_and_validation_alias_model_schema(): - openapi = app.openapi() - body_schema = openapi["components"]["schemas"][ - "RequiredFieldAliasAndValidationAliasModel" - ] - assert body_schema["properties"] == { - "param_val_alias": {"title": "Param Val Alias", "type": "string"} - } - - -# ------------------------------ -# optional field - - -class OptionalFieldAliasAndValidationAliasModel(BaseModel): - param: Optional[str] = Field( - None, alias="param_alias", validation_alias="param_val_alias" - ) - - -@app.post( - "/optional-field-alias-and-validation-alias-model", - operation_id="optional_field_alias_and_validation_alias_model", -) -async def optional_field_alias_and_validation_alias_model( - data: Annotated[OptionalFieldAliasAndValidationAliasModel, Form()], -): - return {"param": data.param} - - -def test_optional_field_alias_and_validation_alias_model_by_name(): - client = TestClient(app) - resp = client.post( - "/optional-field-alias-and-validation-alias-model", data={"param": "123"} - ) - assert resp.status_code == 200 - assert resp.json() == {"param": None} - - -def test_optional_field_alias_and_validation_alias_model_by_alias(): - client = TestClient(app) - resp = client.post( - "/optional-field-alias-and-validation-alias-model", data={"param_alias": "123"} - ) - assert resp.status_code == 200 - assert resp.json() == {"param": None} - - -def test_optional_field_alias_and_validation_alias_model_by_validation_alias(): - client = TestClient(app) - resp = client.post( - "/optional-field-alias-and-validation-alias-model", - data={"param_val_alias": "123"}, - ) - assert resp.status_code == 200 - assert resp.json() == {"param": "123"} - - -def test_optional_field_alias_and_validation_alias_model_schema(): - openapi = app.openapi() - body_schema = openapi["components"]["schemas"][ - "OptionalFieldAliasAndValidationAliasModel" - ] - assert body_schema["properties"] == { - "param_val_alias": { - "anyOf": [{"type": "string"}, {"type": "null"}], - "title": "Param Val Alias", - } - } - - -# ------------------------------ -# list field - - -class ListFieldAliasAndValidationAliasModel(BaseModel): - param: list[str] = Field(alias="param_alias", validation_alias="param_val_alias") - - -@app.post( - "/list-field-alias-and-validation-alias-model", - operation_id="list_field_alias_and_validation_alias_model", -) -async def list_field_alias_and_validation_alias_model( - data: Annotated[ListFieldAliasAndValidationAliasModel, Form()], -): - return {"param": data.param} - - -def test_list_field_alias_and_validation_alias_model_by_name(): - client = TestClient(app) - resp = client.post( - "/list-field-alias-and-validation-alias-model", data={"param": ["123", "456"]} - ) - assert resp.status_code == 422 - detail = resp.json()["detail"] - assert detail[0]["msg"] == IsOneOf( - "Field required", - "field required", # TODO: remove when deprecating Pydantic v1 - ) - assert "param_val_alias" in detail[0]["loc"] - - -def test_list_field_alias_and_validation_alias_model_by_alias(): - client = TestClient(app) - resp = client.post( - "/list-field-alias-and-validation-alias-model", - data={"param_alias": ["123", "456"]}, - ) - assert resp.status_code == 422 - detail = resp.json()["detail"] - assert detail[0]["msg"] == IsOneOf( - "Field required", - "field required", # TODO: remove when deprecating Pydantic v1 - ) - assert "param_val_alias" in detail[0]["loc"] - - -@pytest.mark.xfail(raises=AssertionError, strict=False) -def test_list_field_alias_and_validation_alias_model_by_validation_alias(): - client = TestClient(app) - resp = client.post( - "/list-field-alias-and-validation-alias-model", - data={"param_val_alias": ["123", "456"]}, - ) - assert resp.status_code == 200, resp.text - # Currently fails due to some issue: - # AssertionError: assert 422 == 200 - # {"detail":[{"type":"list_type","loc":["body","param_val_alias"],"msg":"Input should be a valid list","input":"456"}]} - - assert resp.json() == {"param": ["123", "456"]} - - -def test_list_field_alias_and_validation_alias_model_schema(): - openapi = app.openapi() - body_schema = openapi["components"]["schemas"][ - "ListFieldAliasAndValidationAliasModel" - ] - assert body_schema["properties"] == { - "param_val_alias": { - "items": {"type": "string"}, - "title": "Param Val Alias", - "type": "array", - } - } - - -# ------------------------------ -# optional list field - - -class OptionalListFieldAliasAndValidationAliasModel(BaseModel): - param: Optional[list[str]] = Field( - None, alias="param_alias", validation_alias="param_val_alias" - ) - - -@app.post( - "/optional-list-field-alias-and-validation-alias-model", - operation_id="optional_list_field_alias_and_validation_alias_model", -) -async def optional_list_field_alias_and_validation_alias_model( - data: Annotated[OptionalListFieldAliasAndValidationAliasModel, Form()], -): - return {"param": data.param} - - -def test_optional_list_field_alias_and_validation_alias_model_by_name(): - client = TestClient(app) - resp = client.post( - "/optional-list-field-alias-and-validation-alias-model", - data={"param": ["123", "456"]}, - ) - assert resp.status_code == 200, resp.text - assert resp.json() == {"param": None} - - -def test_optional_list_field_alias_and_validation_alias_model_by_alias(): - client = TestClient(app) - resp = client.post( - "/optional-list-field-alias-and-validation-alias-model", - data={"param_alias": ["123", "456"]}, - ) - assert resp.status_code == 200, resp.text - assert resp.json() == {"param": None} - - -@pytest.mark.xfail(raises=AssertionError, strict=False) -def test_optional_list_field_alias_and_validation_alias_model_by_validation_alias(): - client = TestClient(app) - resp = client.post( - "/optional-list-field-alias-and-validation-alias-model", - data={"param_val_alias": ["123", "456"]}, - ) - assert resp.status_code == 200, resp.text - # Currently fails due to some issue: - # AssertionError: assert 422 == 200 - # {"detail":[{"type":"list_type","loc":["body","param_val_alias"],"msg":"Input should be a valid list","input":"456"}]} - - assert resp.json() == {"param": ["123", "456"]} - - -def test_optional_list_field_alias_and_validation_alias_model_schema(): - openapi = app.openapi() - body_schema = openapi["components"]["schemas"][ - "OptionalListFieldAliasAndValidationAliasModel" - ] - assert body_schema["properties"] == { - "param_val_alias": { + "param_alias": { "anyOf": [{"items": {"type": "string"}, "type": "array"}, {"type": "null"}], - "title": "Param Val Alias", + "title": "Param Alias", } } + # Fails with: + # AssertionError: assert + # {'param_alias': {'items': {'type': 'string'}, 'type': 'array', 'title': 'Param Alias'}} == + # {'param_alias': {'anyOf': [{'items': {'type': 'string'}, 'type': 'array'}, {'type': 'null'}], 'title': 'Param Alias'}}