Coverage for tests / test_compat.py: 100%

73 statements  

« prev     ^ index     » next       coverage.py v7.13.3, created at 2026-02-12 18:15 +0000

1from typing import Union 1defg

2 

3from fastapi import FastAPI, UploadFile 1defg

4from fastapi._compat import ( 1defg

5 Undefined, 

6 is_uploadfile_sequence_annotation, 

7) 

8from fastapi._compat.shared import is_bytes_sequence_annotation 1defg

9from fastapi.testclient import TestClient 1defg

10from pydantic import BaseModel, ConfigDict 1defg

11from pydantic.fields import FieldInfo 1defg

12 

13from .utils import needs_py310 1defg

14 

15 

16def test_model_field_default_required(): 1defg

17 from fastapi._compat import v2 1tuv

18 

19 # For coverage 

20 field_info = FieldInfo(annotation=str) 1tuv

21 field = v2.ModelField(name="foo", field_info=field_info) 1tuv

22 assert field.default is Undefined 1tuv

23 

24 

25def test_complex(): 1defg

26 app = FastAPI() 1hij

27 

28 @app.post("/") 1hij

29 def foo(foo: Union[str, list[int]]): 1hij

30 return foo 1hij

31 

32 client = TestClient(app) 1hij

33 

34 response = client.post("/", json="bar") 1hij

35 assert response.status_code == 200, response.text 1hij

36 assert response.json() == "bar" 1hij

37 

38 response2 = client.post("/", json=[1, 2]) 1hij

39 assert response2.status_code == 200, response2.text 1hij

40 assert response2.json() == [1, 2] 1hij

41 

42 

43def test_propagates_pydantic2_model_config(): 1defg

44 app = FastAPI() 1abc

45 

46 class Missing: 1abc

47 def __bool__(self): 1abc

48 return False 1abc

49 

50 class EmbeddedModel(BaseModel): 1abc

51 model_config = ConfigDict(arbitrary_types_allowed=True) 1abc

52 value: Union[str, Missing] = Missing() 1abc

53 

54 class Model(BaseModel): 1abc

55 model_config = ConfigDict( 1abc

56 arbitrary_types_allowed=True, 

57 ) 

58 value: Union[str, Missing] = Missing() 1abc

59 embedded_model: EmbeddedModel = EmbeddedModel() 1abc

60 

61 @app.post("/") 1abc

62 def foo(req: Model) -> dict[str, Union[str, None]]: 1abc

63 return { 1abc

64 "value": req.value or None, 

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

66 } 

67 

68 client = TestClient(app) 1abc

69 

70 response = client.post("/", json={}) 1abc

71 assert response.status_code == 200, response.text 1abc

72 assert response.json() == { 1abc

73 "value": None, 

74 "embedded_value": None, 

75 } 

76 

77 response2 = client.post( 1abc

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

79 ) 

80 assert response2.status_code == 200, response2.text 1abc

81 assert response2.json() == { 1abc

82 "value": "foo", 

83 "embedded_value": "bar", 

84 } 

85 

86 

87def test_is_bytes_sequence_annotation_union(): 1defg

88 # For coverage 

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

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

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

92 assert is_bytes_sequence_annotation(Union[list[str], list[bytes]]) 1wxy

93 

94 

95def test_is_uploadfile_sequence_annotation(): 1defg

96 # For coverage 

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

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

99 # class "feature" 

100 assert is_uploadfile_sequence_annotation(Union[list[str], list[UploadFile]]) 1zAB

101 

102 

103def test_serialize_sequence_value_with_optional_list(): 1defg

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

105 from fastapi._compat import v2 1klm

106 

107 field_info = FieldInfo(annotation=Union[list[str], None]) 1klm

108 field = v2.ModelField(name="items", field_info=field_info) 1klm

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

110 assert result == ["a", "b", "c"] 1klm

111 assert isinstance(result, list) 1klm

112 

113 

114@needs_py310 1defg

115def test_serialize_sequence_value_with_optional_list_pipe_union(): 1defg

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

117 from fastapi._compat import v2 1nop

118 

119 field_info = FieldInfo(annotation=list[str] | None) 1nop

120 field = v2.ModelField(name="items", field_info=field_info) 1nop

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

122 assert result == ["a", "b", "c"] 1nop

123 assert isinstance(result, list) 1nop

124 

125 

126def test_serialize_sequence_value_with_none_first_in_union(): 1defg

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

128 from fastapi._compat import v2 1qrs

129 

130 field_info = FieldInfo(annotation=Union[None, list[str]]) 1qrs

131 field = v2.ModelField(name="items", field_info=field_info) 1qrs

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

133 assert result == ["x", "y"] 1qrs

134 assert isinstance(result, list) 1qrs