mirror of https://github.com/tiangolo/fastapi.git
135 lines
4.2 KiB
Python
135 lines
4.2 KiB
Python
from typing import Union
|
|
|
|
from fastapi import FastAPI, UploadFile
|
|
from fastapi._compat import (
|
|
Undefined,
|
|
is_uploadfile_sequence_annotation,
|
|
)
|
|
from fastapi._compat.shared import is_bytes_sequence_annotation
|
|
from fastapi.testclient import TestClient
|
|
from pydantic import BaseModel, ConfigDict
|
|
from pydantic.fields import FieldInfo
|
|
|
|
from .utils import needs_py310
|
|
|
|
|
|
def test_model_field_default_required():
|
|
from fastapi._compat import v2
|
|
|
|
# For coverage
|
|
field_info = FieldInfo(annotation=str)
|
|
field = v2.ModelField(name="foo", field_info=field_info)
|
|
assert field.default is Undefined
|
|
|
|
|
|
def test_complex():
|
|
app = FastAPI()
|
|
|
|
@app.post("/")
|
|
def foo(foo: Union[str, list[int]]):
|
|
return foo
|
|
|
|
client = TestClient(app)
|
|
|
|
response = client.post("/", json="bar")
|
|
assert response.status_code == 200, response.text
|
|
assert response.json() == "bar"
|
|
|
|
response2 = client.post("/", json=[1, 2])
|
|
assert response2.status_code == 200, response2.text
|
|
assert response2.json() == [1, 2]
|
|
|
|
|
|
def test_propagates_pydantic2_model_config():
|
|
app = FastAPI()
|
|
|
|
class Missing:
|
|
def __bool__(self):
|
|
return False
|
|
|
|
class EmbeddedModel(BaseModel):
|
|
model_config = ConfigDict(arbitrary_types_allowed=True)
|
|
value: Union[str, Missing] = Missing()
|
|
|
|
class Model(BaseModel):
|
|
model_config = ConfigDict(
|
|
arbitrary_types_allowed=True,
|
|
)
|
|
value: Union[str, Missing] = Missing()
|
|
embedded_model: EmbeddedModel = EmbeddedModel()
|
|
|
|
@app.post("/")
|
|
def foo(req: Model) -> dict[str, Union[str, None]]:
|
|
return {
|
|
"value": req.value or None,
|
|
"embedded_value": req.embedded_model.value or None,
|
|
}
|
|
|
|
client = TestClient(app)
|
|
|
|
response = client.post("/", json={})
|
|
assert response.status_code == 200, response.text
|
|
assert response.json() == {
|
|
"value": None,
|
|
"embedded_value": None,
|
|
}
|
|
|
|
response2 = client.post(
|
|
"/", json={"value": "foo", "embedded_model": {"value": "bar"}}
|
|
)
|
|
assert response2.status_code == 200, response2.text
|
|
assert response2.json() == {
|
|
"value": "foo",
|
|
"embedded_value": "bar",
|
|
}
|
|
|
|
|
|
def test_is_bytes_sequence_annotation_union():
|
|
# For coverage
|
|
# TODO: in theory this would allow declaring types that could be lists of bytes
|
|
# to be read from files and other types, but I'm not even sure it's a good idea
|
|
# to support it as a first class "feature"
|
|
assert is_bytes_sequence_annotation(Union[list[str], list[bytes]])
|
|
|
|
|
|
def test_is_uploadfile_sequence_annotation():
|
|
# For coverage
|
|
# TODO: in theory this would allow declaring types that could be lists of UploadFile
|
|
# and other types, but I'm not even sure it's a good idea to support it as a first
|
|
# class "feature"
|
|
assert is_uploadfile_sequence_annotation(Union[list[str], list[UploadFile]])
|
|
|
|
|
|
def test_serialize_sequence_value_with_optional_list():
|
|
"""Test that serialize_sequence_value handles optional lists correctly."""
|
|
from fastapi._compat import v2
|
|
|
|
field_info = FieldInfo(annotation=Union[list[str], None])
|
|
field = v2.ModelField(name="items", field_info=field_info)
|
|
result = v2.serialize_sequence_value(field=field, value=["a", "b", "c"])
|
|
assert result == ["a", "b", "c"]
|
|
assert isinstance(result, list)
|
|
|
|
|
|
@needs_py310
|
|
def test_serialize_sequence_value_with_optional_list_pipe_union():
|
|
"""Test that serialize_sequence_value handles optional lists correctly (with new syntax)."""
|
|
from fastapi._compat import v2
|
|
|
|
field_info = FieldInfo(annotation=list[str] | None)
|
|
field = v2.ModelField(name="items", field_info=field_info)
|
|
result = v2.serialize_sequence_value(field=field, value=["a", "b", "c"])
|
|
assert result == ["a", "b", "c"]
|
|
assert isinstance(result, list)
|
|
|
|
|
|
def test_serialize_sequence_value_with_none_first_in_union():
|
|
"""Test that serialize_sequence_value handles Union[None, List[...]] correctly."""
|
|
from fastapi._compat import v2
|
|
|
|
field_info = FieldInfo(annotation=Union[None, list[str]])
|
|
field = v2.ModelField(name="items", field_info=field_info)
|
|
result = v2.serialize_sequence_value(field=field, value=["x", "y"])
|
|
assert result == ["x", "y"]
|
|
assert isinstance(result, list)
|