Coverage for tests/test_forms_single_model.py: 100%

51 statements  

« prev     ^ index     » next       coverage.py v7.6.1, created at 2025-12-04 08:29 +0000

1from typing import List, Optional 1abcdefg

2 

3from dirty_equals import IsDict 1abcdefg

4from fastapi import FastAPI, Form 1abcdefg

5from fastapi._compat import PYDANTIC_V2 1abcdefg

6from fastapi.testclient import TestClient 1abcdefg

7from pydantic import BaseModel, Field 1abcdefg

8from typing_extensions import Annotated 1abcdefg

9 

10app = FastAPI() 1abcdefg

11 

12 

13class FormModel(BaseModel): 1abcdefg

14 username: str 1abcdefg

15 lastname: str 1abcdefg

16 age: Optional[int] = None 1abcdefg

17 tags: List[str] = ["foo", "bar"] 1abcdefg

18 alias_with: str = Field(alias="with", default="nothing") 1abcdefg

19 

20 

21class FormModelExtraAllow(BaseModel): 1abcdefg

22 param: str 1abcdefg

23 

24 if PYDANTIC_V2: 1abcdefg

25 model_config = {"extra": "allow"} 1abcdefg

26 else: 

27 

28 class Config: 1abcdef

29 extra = "allow" 1abcdef

30 

31 

32@app.post("/form/") 1abcdefg

33def post_form(user: Annotated[FormModel, Form()]): 1abcdefg

34 return user 1hijklmnopqrstu

35 

36 

37@app.post("/form-extra-allow/") 1abcdefg

38def post_form_extra_allow(params: Annotated[FormModelExtraAllow, Form()]): 1abcdefg

39 return params 1vwxyzABCDEFGHI

40 

41 

42client = TestClient(app) 1abcdefg

43 

44 

45def test_send_all_data(): 1abcdefg

46 response = client.post( 1ikmoqsu

47 "/form/", 

48 data={ 

49 "username": "Rick", 

50 "lastname": "Sanchez", 

51 "age": "70", 

52 "tags": ["plumbus", "citadel"], 

53 "with": "something", 

54 }, 

55 ) 

56 assert response.status_code == 200, response.text 1ikmoqsu

57 assert response.json() == { 1ikmoqsu

58 "username": "Rick", 

59 "lastname": "Sanchez", 

60 "age": 70, 

61 "tags": ["plumbus", "citadel"], 

62 "with": "something", 

63 } 

64 

65 

66def test_defaults(): 1abcdefg

67 response = client.post("/form/", data={"username": "Rick", "lastname": "Sanchez"}) 1hjlnprt

68 assert response.status_code == 200, response.text 1hjlnprt

69 assert response.json() == { 1hjlnprt

70 "username": "Rick", 

71 "lastname": "Sanchez", 

72 "age": None, 

73 "tags": ["foo", "bar"], 

74 "with": "nothing", 

75 } 

76 

77 

78def test_invalid_data(): 1abcdefg

79 response = client.post( 1JKLMNOP

80 "/form/", 

81 data={ 

82 "username": "Rick", 

83 "lastname": "Sanchez", 

84 "age": "seventy", 

85 "tags": ["plumbus", "citadel"], 

86 }, 

87 ) 

88 assert response.status_code == 422, response.text 1JKLMNOP

89 assert response.json() == IsDict( 1JKLMNOP

90 { 

91 "detail": [ 

92 { 

93 "type": "int_parsing", 

94 "loc": ["body", "age"], 

95 "msg": "Input should be a valid integer, unable to parse string as an integer", 

96 "input": "seventy", 

97 } 

98 ] 

99 } 

100 ) | IsDict( 

101 # TODO: remove when deprecating Pydantic v1 

102 { 

103 "detail": [ 

104 { 

105 "loc": ["body", "age"], 

106 "msg": "value is not a valid integer", 

107 "type": "type_error.integer", 

108 } 

109 ] 

110 } 

111 ) 

112 

113 

114def test_no_data(): 1abcdefg

115 response = client.post("/form/") 1QRSTUVW

116 assert response.status_code == 422, response.text 1QRSTUVW

117 assert response.json() == IsDict( 1QRSTUVW

118 { 

119 "detail": [ 

120 { 

121 "type": "missing", 

122 "loc": ["body", "username"], 

123 "msg": "Field required", 

124 "input": {"tags": ["foo", "bar"], "with": "nothing"}, 

125 }, 

126 { 

127 "type": "missing", 

128 "loc": ["body", "lastname"], 

129 "msg": "Field required", 

130 "input": {"tags": ["foo", "bar"], "with": "nothing"}, 

131 }, 

132 ] 

133 } 

134 ) | IsDict( 

135 # TODO: remove when deprecating Pydantic v1 

136 { 

137 "detail": [ 

138 { 

139 "loc": ["body", "username"], 

140 "msg": "field required", 

141 "type": "value_error.missing", 

142 }, 

143 { 

144 "loc": ["body", "lastname"], 

145 "msg": "field required", 

146 "type": "value_error.missing", 

147 }, 

148 ] 

149 } 

150 ) 

151 

152 

153def test_extra_param_single(): 1abcdefg

154 response = client.post( 1wyACEGI

155 "/form-extra-allow/", 

156 data={ 

157 "param": "123", 

158 "extra_param": "456", 

159 }, 

160 ) 

161 assert response.status_code == 200, response.text 1wyACEGI

162 assert response.json() == { 1wyACEGI

163 "param": "123", 

164 "extra_param": "456", 

165 } 

166 

167 

168def test_extra_param_list(): 1abcdefg

169 response = client.post( 1vxzBDFH

170 "/form-extra-allow/", 

171 data={ 

172 "param": "123", 

173 "extra_params": ["456", "789"], 

174 }, 

175 ) 

176 assert response.status_code == 200, response.text 1vxzBDFH

177 assert response.json() == { 1vxzBDFH

178 "param": "123", 

179 "extra_params": ["456", "789"], 

180 }