Skip to content
Open
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
254 changes: 254 additions & 0 deletions tests/test_validation_alias_support.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,254 @@
"""
Tests for Pydantic v2 validation_alias and serialization_alias support in FastAPI.

This test file verifies that validation_alias and serialization_alias work correctly
across all parameter types: Query, Path, Header, Cookie, Body, Form, and File parameters.
"""

from fastapi import Body, Cookie, FastAPI, File, Form, Header, Path, Query
from fastapi.testclient import TestClient
from typing_extensions import Annotated

from tests.utils import needs_pydanticv2

app = FastAPI()


# =====================================================================================
# Query parameter with validation_alias
# =====================================================================================


@app.get("/query-validation-alias")
def read_query_validation_alias(
item_name: Annotated[str, Query(validation_alias="itemName")]
):
return {"item_name": item_name}


@needs_pydanticv2
def test_query_validation_alias_accepts_validation_alias():
"""Test that Query parameter accepts validation_alias name."""
client = TestClient(app)
response = client.get("/query-validation-alias?itemName=test")
assert response.status_code == 200
assert response.json() == {"item_name": "test"}


@needs_pydanticv2
def test_query_validation_alias_rejects_python_name():
"""Test that Query parameter with validation_alias rejects Python field name."""
client = TestClient(app)
response = client.get("/query-validation-alias?item_name=test")
assert response.status_code == 422


# =====================================================================================
# Path parameter with validation_alias
# =====================================================================================


@app.get("/items/{itemId}")
def read_path_validation_alias(
item_id: Annotated[str, Path(validation_alias="itemId")]
):
return {"item_id": item_id}


@needs_pydanticv2
def test_path_validation_alias_accepts_validation_alias():
"""Test that Path parameter accepts validation_alias name."""
client = TestClient(app)
response = client.get("/items/123")
assert response.status_code == 200
assert response.json() == {"item_id": "123"}


# =====================================================================================
# Header parameter with validation_alias
# =====================================================================================


@app.get("/header-validation-alias")
def read_header_validation_alias(
content_type: Annotated[str, Header(validation_alias="Content-Type")]
):
return {"content_type": content_type}


@needs_pydanticv2
def test_header_validation_alias_accepts_validation_alias():
"""Test that Header parameter accepts validation_alias name."""
client = TestClient(app)
response = client.get("/header-validation-alias", headers={"Content-Type": "application/json"})
assert response.status_code == 200
assert response.json() == {"content_type": "application/json"}


# =====================================================================================
# Cookie parameter with validation_alias
# =====================================================================================


@app.get("/cookie-validation-alias")
def read_cookie_validation_alias(
session_id: Annotated[str, Cookie(validation_alias="sessionId")]
):
return {"session_id": session_id}


@needs_pydanticv2
def test_cookie_validation_alias_accepts_validation_alias():
"""Test that Cookie parameter accepts validation_alias name."""
client = TestClient(app)
client.cookies.set("sessionId", "abc123")
response = client.get("/cookie-validation-alias")
assert response.status_code == 200
assert response.json() == {"session_id": "abc123"}


# =====================================================================================
# Body parameter with validation_alias
# =====================================================================================


@app.post("/body-validation-alias")
def read_body_validation_alias(
item_name: Annotated[str, Body(validation_alias="itemName", embed=True)]
):
return {"item_name": item_name}


@needs_pydanticv2
def test_body_validation_alias_accepts_validation_alias():
"""Test that Body parameter accepts validation_alias name."""
client = TestClient(app)
response = client.post("/body-validation-alias", json={"itemName": "test"})
assert response.status_code == 200
assert response.json() == {"item_name": "test"}


@needs_pydanticv2
def test_body_validation_alias_rejects_python_name():
"""Test that Body parameter with validation_alias rejects Python field name."""
client = TestClient(app)
response = client.post("/body-validation-alias", json={"item_name": "test"})
assert response.status_code == 422


# =====================================================================================
# Form parameter with validation_alias
# =====================================================================================


@app.post("/form-validation-alias")
def read_form_validation_alias(
user_name: Annotated[str, Form(validation_alias="userName")]
):
return {"user_name": user_name}


@needs_pydanticv2
def test_form_validation_alias_accepts_validation_alias():
"""Test that Form parameter accepts validation_alias name."""
client = TestClient(app)
response = client.post("/form-validation-alias", data={"userName": "john"})
assert response.status_code == 200
assert response.json() == {"user_name": "john"}


# =====================================================================================
# File parameter with validation_alias
# =====================================================================================


@app.post("/file-validation-alias")
def read_file_validation_alias(
upload_file: Annotated[bytes, File(validation_alias="uploadFile")]
):
return {"file_size": len(upload_file)}


@needs_pydanticv2
def test_file_validation_alias_accepts_validation_alias():
"""Test that File parameter accepts validation_alias name."""
client = TestClient(app)
response = client.post(
"/file-validation-alias",
files={"uploadFile": ("test.txt", b"test content")}
)
assert response.status_code == 200
assert response.json() == {"file_size": 12}


@needs_pydanticv2
def test_file_validation_alias_rejects_python_name():
"""Test that File parameter with validation_alias rejects Python field name."""
client = TestClient(app)
response = client.post(
"/file-validation-alias",
files={"upload_file": ("test.txt", b"test content")}
)
assert response.status_code == 422


# =====================================================================================
# Both alias and validation_alias (validation_alias takes precedence for input)
# =====================================================================================


@app.get("/query-both-aliases")
def read_query_both_aliases(
item_name: Annotated[str, Query(alias="item-name", validation_alias="itemName")]
):
return {"item_name": item_name}


@needs_pydanticv2
def test_query_both_aliases_accepts_validation_alias():
"""Test that validation_alias is used for input when both are set."""
client = TestClient(app)
response = client.get("/query-both-aliases?itemName=test")
assert response.status_code == 200
assert response.json() == {"item_name": "test"}


@needs_pydanticv2
def test_query_both_aliases_rejects_alias():
"""Test that regular alias is rejected when validation_alias is set."""
client = TestClient(app)
response = client.get("/query-both-aliases?item-name=test")
assert response.status_code == 422


@needs_pydanticv2
def test_query_both_aliases_rejects_python_name():
"""Test that Python name is rejected when both aliases are set."""
client = TestClient(app)
response = client.get("/query-both-aliases?item_name=test")
assert response.status_code == 422


# =====================================================================================
# OpenAPI schema uses validation_alias
# =====================================================================================


@needs_pydanticv2
def test_openapi_schema_query_shows_validation_alias():
"""Test that OpenAPI schema shows validation_alias in query parameter name."""
schema = app.openapi()
query_params = schema["paths"]["/query-validation-alias"]["get"]["parameters"]
assert len(query_params) == 1
assert query_params[0]["name"] == "itemName"
assert query_params[0]["in"] == "query"


@needs_pydanticv2
def test_openapi_schema_path_shows_validation_alias():
"""Test that OpenAPI schema shows validation_alias in path parameter name."""
schema = app.openapi()
path_params = schema["paths"]["/items/{itemId}"]["get"]["parameters"]
assert len(path_params) == 1
assert path_params[0]["name"] == "itemId"
assert path_params[0]["in"] == "path"