Coverage for tests/test_tuples.py: 100%

58 statements  

« prev     ^ index     » next       coverage.py v7.6.1, created at 2024-08-08 03:53 +0000

1from typing import List, Tuple 1abcde

2 

3from dirty_equals import IsDict 1abcde

4from fastapi import FastAPI, Form 1abcde

5from fastapi.testclient import TestClient 1abcde

6from pydantic import BaseModel 1abcde

7 

8app = FastAPI() 1abcde

9 

10 

11class ItemGroup(BaseModel): 1abcde

12 items: List[Tuple[str, str]] 1abcde

13 

14 

15class Coordinate(BaseModel): 1abcde

16 x: float 1abcde

17 y: float 1abcde

18 

19 

20@app.post("/model-with-tuple/") 1abcde

21def post_model_with_tuple(item_group: ItemGroup): 1abcde

22 return item_group 1abcde

23 

24 

25@app.post("/tuple-of-models/") 1abcde

26def post_tuple_of_models(square: Tuple[Coordinate, Coordinate]): 1abcde

27 return square 1abcde

28 

29 

30@app.post("/tuple-form/") 1abcde

31def hello(values: Tuple[int, int] = Form()): 1abcde

32 return values 1abcde

33 

34 

35client = TestClient(app) 1abcde

36 

37 

38def test_model_with_tuple_valid(): 1abcde

39 data = {"items": [["foo", "bar"], ["baz", "whatelse"]]} 1abcde

40 response = client.post("/model-with-tuple/", json=data) 1abcde

41 assert response.status_code == 200, response.text 1abcde

42 assert response.json() == data 1abcde

43 

44 

45def test_model_with_tuple_invalid(): 1abcde

46 data = {"items": [["foo", "bar"], ["baz", "whatelse", "too", "much"]]} 1abcde

47 response = client.post("/model-with-tuple/", json=data) 1abcde

48 assert response.status_code == 422, response.text 1abcde

49 

50 data = {"items": [["foo", "bar"], ["baz"]]} 1abcde

51 response = client.post("/model-with-tuple/", json=data) 1abcde

52 assert response.status_code == 422, response.text 1abcde

53 

54 

55def test_tuple_with_model_valid(): 1abcde

56 data = [{"x": 1, "y": 2}, {"x": 3, "y": 4}] 1abcde

57 response = client.post("/tuple-of-models/", json=data) 1abcde

58 assert response.status_code == 200, response.text 1abcde

59 assert response.json() == data 1abcde

60 

61 

62def test_tuple_with_model_invalid(): 1abcde

63 data = [{"x": 1, "y": 2}, {"x": 3, "y": 4}, {"x": 5, "y": 6}] 1abcde

64 response = client.post("/tuple-of-models/", json=data) 1abcde

65 assert response.status_code == 422, response.text 1abcde

66 

67 data = [{"x": 1, "y": 2}] 1abcde

68 response = client.post("/tuple-of-models/", json=data) 1abcde

69 assert response.status_code == 422, response.text 1abcde

70 

71 

72def test_tuple_form_valid(): 1abcde

73 response = client.post("/tuple-form/", data={"values": ("1", "2")}) 1abcde

74 assert response.status_code == 200, response.text 1abcde

75 assert response.json() == [1, 2] 1abcde

76 

77 

78def test_tuple_form_invalid(): 1abcde

79 response = client.post("/tuple-form/", data={"values": ("1", "2", "3")}) 1abcde

80 assert response.status_code == 422, response.text 1abcde

81 

82 response = client.post("/tuple-form/", data={"values": ("1")}) 1abcde

83 assert response.status_code == 422, response.text 1abcde

84 

85 

86def test_openapi_schema(): 1abcde

87 response = client.get("/openapi.json") 1abcde

88 assert response.status_code == 200, response.text 1abcde

89 assert response.json() == { 1abcde

90 "openapi": "3.1.0", 

91 "info": {"title": "FastAPI", "version": "0.1.0"}, 

92 "paths": { 

93 "/model-with-tuple/": { 

94 "post": { 

95 "summary": "Post Model With Tuple", 

96 "operationId": "post_model_with_tuple_model_with_tuple__post", 

97 "requestBody": { 

98 "content": { 

99 "application/json": { 

100 "schema": {"$ref": "#/components/schemas/ItemGroup"} 

101 } 

102 }, 

103 "required": True, 

104 }, 

105 "responses": { 

106 "200": { 

107 "description": "Successful Response", 

108 "content": {"application/json": {"schema": {}}}, 

109 }, 

110 "422": { 

111 "description": "Validation Error", 

112 "content": { 

113 "application/json": { 

114 "schema": { 

115 "$ref": "#/components/schemas/HTTPValidationError" 

116 } 

117 } 

118 }, 

119 }, 

120 }, 

121 } 

122 }, 

123 "/tuple-of-models/": { 

124 "post": { 

125 "summary": "Post Tuple Of Models", 

126 "operationId": "post_tuple_of_models_tuple_of_models__post", 

127 "requestBody": { 

128 "content": { 

129 "application/json": { 

130 "schema": IsDict( 

131 { 

132 "title": "Square", 

133 "maxItems": 2, 

134 "minItems": 2, 

135 "type": "array", 

136 "prefixItems": [ 

137 {"$ref": "#/components/schemas/Coordinate"}, 

138 {"$ref": "#/components/schemas/Coordinate"}, 

139 ], 

140 } 

141 ) 

142 | IsDict( 

143 # TODO: remove when deprecating Pydantic v1 

144 { 

145 "title": "Square", 

146 "maxItems": 2, 

147 "minItems": 2, 

148 "type": "array", 

149 "items": [ 

150 {"$ref": "#/components/schemas/Coordinate"}, 

151 {"$ref": "#/components/schemas/Coordinate"}, 

152 ], 

153 } 

154 ) 

155 } 

156 }, 

157 "required": True, 

158 }, 

159 "responses": { 

160 "200": { 

161 "description": "Successful Response", 

162 "content": {"application/json": {"schema": {}}}, 

163 }, 

164 "422": { 

165 "description": "Validation Error", 

166 "content": { 

167 "application/json": { 

168 "schema": { 

169 "$ref": "#/components/schemas/HTTPValidationError" 

170 } 

171 } 

172 }, 

173 }, 

174 }, 

175 } 

176 }, 

177 "/tuple-form/": { 

178 "post": { 

179 "summary": "Hello", 

180 "operationId": "hello_tuple_form__post", 

181 "requestBody": { 

182 "content": { 

183 "application/x-www-form-urlencoded": { 

184 "schema": { 

185 "$ref": "#/components/schemas/Body_hello_tuple_form__post" 

186 } 

187 } 

188 }, 

189 "required": True, 

190 }, 

191 "responses": { 

192 "200": { 

193 "description": "Successful Response", 

194 "content": {"application/json": {"schema": {}}}, 

195 }, 

196 "422": { 

197 "description": "Validation Error", 

198 "content": { 

199 "application/json": { 

200 "schema": { 

201 "$ref": "#/components/schemas/HTTPValidationError" 

202 } 

203 } 

204 }, 

205 }, 

206 }, 

207 } 

208 }, 

209 }, 

210 "components": { 

211 "schemas": { 

212 "Body_hello_tuple_form__post": { 

213 "title": "Body_hello_tuple_form__post", 

214 "required": ["values"], 

215 "type": "object", 

216 "properties": { 

217 "values": IsDict( 

218 { 

219 "title": "Values", 

220 "maxItems": 2, 

221 "minItems": 2, 

222 "type": "array", 

223 "prefixItems": [ 

224 {"type": "integer"}, 

225 {"type": "integer"}, 

226 ], 

227 } 

228 ) 

229 | IsDict( 

230 # TODO: remove when deprecating Pydantic v1 

231 { 

232 "title": "Values", 

233 "maxItems": 2, 

234 "minItems": 2, 

235 "type": "array", 

236 "items": [{"type": "integer"}, {"type": "integer"}], 

237 } 

238 ) 

239 }, 

240 }, 

241 "Coordinate": { 

242 "title": "Coordinate", 

243 "required": ["x", "y"], 

244 "type": "object", 

245 "properties": { 

246 "x": {"title": "X", "type": "number"}, 

247 "y": {"title": "Y", "type": "number"}, 

248 }, 

249 }, 

250 "HTTPValidationError": { 

251 "title": "HTTPValidationError", 

252 "type": "object", 

253 "properties": { 

254 "detail": { 

255 "title": "Detail", 

256 "type": "array", 

257 "items": {"$ref": "#/components/schemas/ValidationError"}, 

258 } 

259 }, 

260 }, 

261 "ItemGroup": { 

262 "title": "ItemGroup", 

263 "required": ["items"], 

264 "type": "object", 

265 "properties": { 

266 "items": { 

267 "title": "Items", 

268 "type": "array", 

269 "items": IsDict( 

270 { 

271 "maxItems": 2, 

272 "minItems": 2, 

273 "type": "array", 

274 "prefixItems": [ 

275 {"type": "string"}, 

276 {"type": "string"}, 

277 ], 

278 } 

279 ) 

280 | IsDict( 

281 # TODO: remove when deprecating Pydantic v1 

282 { 

283 "maxItems": 2, 

284 "minItems": 2, 

285 "type": "array", 

286 "items": [{"type": "string"}, {"type": "string"}], 

287 } 

288 ), 

289 } 

290 }, 

291 }, 

292 "ValidationError": { 

293 "title": "ValidationError", 

294 "required": ["loc", "msg", "type"], 

295 "type": "object", 

296 "properties": { 

297 "loc": { 

298 "title": "Location", 

299 "type": "array", 

300 "items": { 

301 "anyOf": [{"type": "string"}, {"type": "integer"}] 

302 }, 

303 }, 

304 "msg": {"title": "Message", "type": "string"}, 

305 "type": {"title": "Error Type", "type": "string"}, 

306 }, 

307 }, 

308 } 

309 }, 

310 }