Coverage for tests/test_annotated.py: 100%

60 statements  

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

1import pytest 1abcde

2from dirty_equals import IsDict 1abcde

3from fastapi import APIRouter, FastAPI, Query 1abcde

4from fastapi.testclient import TestClient 1abcde

5from typing_extensions import Annotated 1abcde

6 

7app = FastAPI() 1abcde

8 

9 

10@app.get("/default") 1abcde

11async def default(foo: Annotated[str, Query()] = "foo"): 1abcde

12 return {"foo": foo} 1abcde

13 

14 

15@app.get("/required") 1abcde

16async def required(foo: Annotated[str, Query(min_length=1)]): 1abcde

17 return {"foo": foo} 1abcde

18 

19 

20@app.get("/multiple") 1abcde

21async def multiple(foo: Annotated[str, object(), Query(min_length=1)]): 1abcde

22 return {"foo": foo} 1abcde

23 

24 

25@app.get("/unrelated") 1abcde

26async def unrelated(foo: Annotated[str, object()]): 1abcde

27 return {"foo": foo} 1abcde

28 

29 

30client = TestClient(app) 1abcde

31 

32foo_is_missing = { 1abcde

33 "detail": [ 

34 IsDict( 

35 { 

36 "loc": ["query", "foo"], 

37 "msg": "Field required", 

38 "type": "missing", 

39 "input": None, 

40 } 

41 ) 

42 # TODO: remove when deprecating Pydantic v1 

43 | IsDict( 

44 { 

45 "loc": ["query", "foo"], 

46 "msg": "field required", 

47 "type": "value_error.missing", 

48 } 

49 ) 

50 ] 

51} 

52foo_is_short = { 1abcde

53 "detail": [ 

54 IsDict( 

55 { 

56 "ctx": {"min_length": 1}, 

57 "loc": ["query", "foo"], 

58 "msg": "String should have at least 1 character", 

59 "type": "string_too_short", 

60 "input": "", 

61 } 

62 ) 

63 # TODO: remove when deprecating Pydantic v1 

64 | IsDict( 

65 { 

66 "ctx": {"limit_value": 1}, 

67 "loc": ["query", "foo"], 

68 "msg": "ensure this value has at least 1 characters", 

69 "type": "value_error.any_str.min_length", 

70 } 

71 ) 

72 ] 

73} 

74 

75 

76@pytest.mark.parametrize( 1abcde

77 "path,expected_status,expected_response", 

78 [ 

79 ("/default", 200, {"foo": "foo"}), 

80 ("/default?foo=bar", 200, {"foo": "bar"}), 

81 ("/required?foo=bar", 200, {"foo": "bar"}), 

82 ("/required", 422, foo_is_missing), 

83 ("/required?foo=", 422, foo_is_short), 

84 ("/multiple?foo=bar", 200, {"foo": "bar"}), 

85 ("/multiple", 422, foo_is_missing), 

86 ("/multiple?foo=", 422, foo_is_short), 

87 ("/unrelated?foo=bar", 200, {"foo": "bar"}), 

88 ("/unrelated", 422, foo_is_missing), 

89 ], 

90) 

91def test_get(path, expected_status, expected_response): 1abcde

92 response = client.get(path) 1abcde

93 assert response.status_code == expected_status 1abcde

94 assert response.json() == expected_response 1abcde

95 

96 

97def test_multiple_path(): 1abcde

98 app = FastAPI() 1abcde

99 

100 @app.get("/test1") 1abcde

101 @app.get("/test2") 1abcde

102 async def test(var: Annotated[str, Query()] = "bar"): 1abcde

103 return {"foo": var} 1abcde

104 

105 client = TestClient(app) 1abcde

106 response = client.get("/test1") 1abcde

107 assert response.status_code == 200 1abcde

108 assert response.json() == {"foo": "bar"} 1abcde

109 

110 response = client.get("/test1", params={"var": "baz"}) 1abcde

111 assert response.status_code == 200 1abcde

112 assert response.json() == {"foo": "baz"} 1abcde

113 

114 response = client.get("/test2") 1abcde

115 assert response.status_code == 200 1abcde

116 assert response.json() == {"foo": "bar"} 1abcde

117 

118 response = client.get("/test2", params={"var": "baz"}) 1abcde

119 assert response.status_code == 200 1abcde

120 assert response.json() == {"foo": "baz"} 1abcde

121 

122 

123def test_nested_router(): 1abcde

124 app = FastAPI() 1abcde

125 

126 router = APIRouter(prefix="/nested") 1abcde

127 

128 @router.get("/test") 1abcde

129 async def test(var: Annotated[str, Query()] = "bar"): 1abcde

130 return {"foo": var} 1abcde

131 

132 app.include_router(router) 1abcde

133 

134 client = TestClient(app) 1abcde

135 

136 response = client.get("/nested/test") 1abcde

137 assert response.status_code == 200 1abcde

138 assert response.json() == {"foo": "bar"} 1abcde

139 

140 

141def test_openapi_schema(): 1abcde

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

143 assert response.status_code == 200 1abcde

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

145 "openapi": "3.1.0", 

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

147 "paths": { 

148 "/default": { 

149 "get": { 

150 "summary": "Default", 

151 "operationId": "default_default_get", 

152 "parameters": [ 

153 { 

154 "required": False, 

155 "schema": { 

156 "title": "Foo", 

157 "type": "string", 

158 "default": "foo", 

159 }, 

160 "name": "foo", 

161 "in": "query", 

162 } 

163 ], 

164 "responses": { 

165 "200": { 

166 "description": "Successful Response", 

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

168 }, 

169 "422": { 

170 "description": "Validation Error", 

171 "content": { 

172 "application/json": { 

173 "schema": { 

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

175 } 

176 } 

177 }, 

178 }, 

179 }, 

180 } 

181 }, 

182 "/required": { 

183 "get": { 

184 "summary": "Required", 

185 "operationId": "required_required_get", 

186 "parameters": [ 

187 { 

188 "required": True, 

189 "schema": { 

190 "title": "Foo", 

191 "minLength": 1, 

192 "type": "string", 

193 }, 

194 "name": "foo", 

195 "in": "query", 

196 } 

197 ], 

198 "responses": { 

199 "200": { 

200 "description": "Successful Response", 

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

202 }, 

203 "422": { 

204 "description": "Validation Error", 

205 "content": { 

206 "application/json": { 

207 "schema": { 

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

209 } 

210 } 

211 }, 

212 }, 

213 }, 

214 } 

215 }, 

216 "/multiple": { 

217 "get": { 

218 "summary": "Multiple", 

219 "operationId": "multiple_multiple_get", 

220 "parameters": [ 

221 { 

222 "required": True, 

223 "schema": { 

224 "title": "Foo", 

225 "minLength": 1, 

226 "type": "string", 

227 }, 

228 "name": "foo", 

229 "in": "query", 

230 } 

231 ], 

232 "responses": { 

233 "200": { 

234 "description": "Successful Response", 

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

236 }, 

237 "422": { 

238 "description": "Validation Error", 

239 "content": { 

240 "application/json": { 

241 "schema": { 

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

243 } 

244 } 

245 }, 

246 }, 

247 }, 

248 } 

249 }, 

250 "/unrelated": { 

251 "get": { 

252 "summary": "Unrelated", 

253 "operationId": "unrelated_unrelated_get", 

254 "parameters": [ 

255 { 

256 "required": True, 

257 "schema": {"title": "Foo", "type": "string"}, 

258 "name": "foo", 

259 "in": "query", 

260 } 

261 ], 

262 "responses": { 

263 "200": { 

264 "description": "Successful Response", 

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

266 }, 

267 "422": { 

268 "description": "Validation Error", 

269 "content": { 

270 "application/json": { 

271 "schema": { 

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

273 } 

274 } 

275 }, 

276 }, 

277 }, 

278 } 

279 }, 

280 }, 

281 "components": { 

282 "schemas": { 

283 "HTTPValidationError": { 

284 "title": "HTTPValidationError", 

285 "type": "object", 

286 "properties": { 

287 "detail": { 

288 "title": "Detail", 

289 "type": "array", 

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

291 } 

292 }, 

293 }, 

294 "ValidationError": { 

295 "title": "ValidationError", 

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

297 "type": "object", 

298 "properties": { 

299 "loc": { 

300 "title": "Location", 

301 "type": "array", 

302 "items": { 

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

304 }, 

305 }, 

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

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

308 }, 

309 }, 

310 } 

311 }, 

312 }