Coverage for tests / test_compat.py: 100%

71 statements  

« prev     ^ index     » next       coverage.py v7.13.3, created at 2026-04-06 01:24 +0000

1from fastapi import FastAPI, UploadFile 1efgh

2from fastapi._compat import ( 1efgh

3 Undefined, 

4 is_uploadfile_sequence_annotation, 

5) 

6from fastapi._compat.shared import is_bytes_sequence_annotation 1efgh

7from fastapi.testclient import TestClient 1efgh

8from pydantic import BaseModel, ConfigDict 1efgh

9from pydantic.fields import FieldInfo 1efgh

10 

11 

12def test_model_field_default_required(): 1efgh

13 from fastapi._compat import v2 1yzAB

14 

15 # For coverage 

16 field_info = FieldInfo(annotation=str) 1yzAB

17 field = v2.ModelField(name="foo", field_info=field_info) 1yzAB

18 assert field.default is Undefined 1yzAB

19 

20 

21def test_complex(): 1efgh

22 app = FastAPI() 1ijkl

23 

24 @app.post("/") 1ijkl

25 def foo(foo: str | list[int]): 1ijkl

26 return foo 1ijkl

27 

28 client = TestClient(app) 1ijkl

29 

30 response = client.post("/", json="bar") 1ijkl

31 assert response.status_code == 200, response.text 1ijkl

32 assert response.json() == "bar" 1ijkl

33 

34 response2 = client.post("/", json=[1, 2]) 1ijkl

35 assert response2.status_code == 200, response2.text 1ijkl

36 assert response2.json() == [1, 2] 1ijkl

37 

38 

39def test_propagates_pydantic2_model_config(): 1efgh

40 app = FastAPI() 1abcd

41 

42 class Missing: 1abcd

43 def __bool__(self): 1abcd

44 return False 1abcd

45 

46 class EmbeddedModel(BaseModel): 1abcd

47 model_config = ConfigDict(arbitrary_types_allowed=True) 1abcd

48 value: str | Missing = Missing() 1abcd

49 

50 class Model(BaseModel): 1abcd

51 model_config = ConfigDict( 1abcd

52 arbitrary_types_allowed=True, 

53 ) 

54 value: str | Missing = Missing() 1abcd

55 embedded_model: EmbeddedModel = EmbeddedModel() 1abcd

56 

57 @app.post("/") 1abcd

58 def foo(req: Model) -> dict[str, str | None]: 1abcd

59 return { 1abcd

60 "value": req.value or None, 

61 "embedded_value": req.embedded_model.value or None, 

62 } 

63 

64 client = TestClient(app) 1abcd

65 

66 response = client.post("/", json={}) 1abcd

67 assert response.status_code == 200, response.text 1abcd

68 assert response.json() == { 1abcd

69 "value": None, 

70 "embedded_value": None, 

71 } 

72 

73 response2 = client.post( 1abcd

74 "/", json={"value": "foo", "embedded_model": {"value": "bar"}} 

75 ) 

76 assert response2.status_code == 200, response2.text 1abcd

77 assert response2.json() == { 1abcd

78 "value": "foo", 

79 "embedded_value": "bar", 

80 } 

81 

82 

83def test_is_bytes_sequence_annotation_union(): 1efgh

84 # For coverage 

85 # TODO: in theory this would allow declaring types that could be lists of bytes 

86 # to be read from files and other types, but I'm not even sure it's a good idea 

87 # to support it as a first class "feature" 

88 assert is_bytes_sequence_annotation(list[str] | list[bytes]) 1CDEF

89 

90 

91def test_is_uploadfile_sequence_annotation(): 1efgh

92 # For coverage 

93 # TODO: in theory this would allow declaring types that could be lists of UploadFile 

94 # and other types, but I'm not even sure it's a good idea to support it as a first 

95 # class "feature" 

96 assert is_uploadfile_sequence_annotation(list[str] | list[UploadFile]) 1GHIJ

97 

98 

99def test_serialize_sequence_value_with_optional_list(): 1efgh

100 """Test that serialize_sequence_value handles optional lists correctly.""" 

101 from fastapi._compat import v2 1qrst

102 

103 field_info = FieldInfo(annotation=list[str] | None) 1qrst

104 field = v2.ModelField(name="items", field_info=field_info) 1qrst

105 result = v2.serialize_sequence_value(field=field, value=["a", "b", "c"]) 1qrst

106 assert result == ["a", "b", "c"] 1qrst

107 assert isinstance(result, list) 1qrst

108 

109 

110def test_serialize_sequence_value_with_optional_list_pipe_union(): 1efgh

111 """Test that serialize_sequence_value handles optional lists correctly (with new syntax).""" 

112 from fastapi._compat import v2 1uvwx

113 

114 field_info = FieldInfo(annotation=list[str] | None) 1uvwx

115 field = v2.ModelField(name="items", field_info=field_info) 1uvwx

116 result = v2.serialize_sequence_value(field=field, value=["a", "b", "c"]) 1uvwx

117 assert result == ["a", "b", "c"] 1uvwx

118 assert isinstance(result, list) 1uvwx

119 

120 

121def test_serialize_sequence_value_with_none_first_in_union(): 1efgh

122 """Test that serialize_sequence_value handles Union[None, List[...]] correctly.""" 

123 from typing import Union 1mnop

124 

125 from fastapi._compat import v2 1mnop

126 

127 # Use Union[None, list[str]] to ensure None comes first in the union args 

128 field_info = FieldInfo(annotation=Union[None, list[str]]) # noqa: UP007 1mnop

129 field = v2.ModelField(name="items", field_info=field_info) 1mnop

130 result = v2.serialize_sequence_value(field=field, value=["x", "y"]) 1mnop

131 assert result == ["x", "y"] 1mnop

132 assert isinstance(result, list) 1mnop