Coverage for tests / test_extra_routes.py: 100%

63 statements  

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

1from fastapi import FastAPI 1adbc

2from fastapi.responses import JSONResponse 1adbc

3from fastapi.testclient import TestClient 1adbc

4from inline_snapshot import snapshot 1adbc

5from pydantic import BaseModel 1adbc

6 

7app = FastAPI() 1adbc

8 

9 

10class Item(BaseModel): 1adbc

11 name: str 1abc

12 price: float | None = None 1adbc

13 

14 

15@app.api_route("/items/{item_id}", methods=["GET"]) 1adbc

16def get_items(item_id: str): 1adbc

17 return {"item_id": item_id} 1efgh

18 

19 

20def get_not_decorated(item_id: str): 1adbc

21 return {"item_id": item_id} 1ijkl

22 

23 

24app.add_api_route("/items-not-decorated/{item_id}", get_not_decorated) 1adbc

25 

26 

27@app.delete("/items/{item_id}") 1adbc

28def delete_item(item_id: str, item: Item): 1adbc

29 return {"item_id": item_id, "item": item} 1mnop

30 

31 

32@app.head("/items/{item_id}") 1adbc

33def head_item(item_id: str): 1adbc

34 return JSONResponse(None, headers={"x-fastapi-item-id": item_id}) 1qrst

35 

36 

37@app.options("/items/{item_id}") 1adbc

38def options_item(item_id: str): 1adbc

39 return JSONResponse(None, headers={"x-fastapi-item-id": item_id}) 1uvwx

40 

41 

42@app.patch("/items/{item_id}") 1adbc

43def patch_item(item_id: str, item: Item): 1adbc

44 return {"item_id": item_id, "item": item} 1yzAB

45 

46 

47@app.trace("/items/{item_id}") 1adbc

48def trace_item(item_id: str): 1adbc

49 return JSONResponse(None, media_type="message/http") 1CDEF

50 

51 

52client = TestClient(app) 1adbc

53 

54 

55def test_get_api_route(): 1adbc

56 response = client.get("/items/foo") 1efgh

57 assert response.status_code == 200, response.text 1efgh

58 assert response.json() == {"item_id": "foo"} 1efgh

59 

60 

61def test_get_api_route_not_decorated(): 1adbc

62 response = client.get("/items-not-decorated/foo") 1ijkl

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

64 assert response.json() == {"item_id": "foo"} 1ijkl

65 

66 

67def test_delete(): 1adbc

68 response = client.request("DELETE", "/items/foo", json={"name": "Foo"}) 1mnop

69 assert response.status_code == 200, response.text 1mnop

70 assert response.json() == {"item_id": "foo", "item": {"name": "Foo", "price": None}} 1mnop

71 

72 

73def test_head(): 1adbc

74 response = client.head("/items/foo") 1qrst

75 assert response.status_code == 200, response.text 1qrst

76 assert response.headers["x-fastapi-item-id"] == "foo" 1qrst

77 

78 

79def test_options(): 1adbc

80 response = client.options("/items/foo") 1uvwx

81 assert response.status_code == 200, response.text 1uvwx

82 assert response.headers["x-fastapi-item-id"] == "foo" 1uvwx

83 

84 

85def test_patch(): 1adbc

86 response = client.patch("/items/foo", json={"name": "Foo"}) 1yzAB

87 assert response.status_code == 200, response.text 1yzAB

88 assert response.json() == {"item_id": "foo", "item": {"name": "Foo", "price": None}} 1yzAB

89 

90 

91def test_trace(): 1adbc

92 response = client.request("trace", "/items/foo") 1CDEF

93 assert response.status_code == 200, response.text 1CDEF

94 assert response.headers["content-type"] == "message/http" 1CDEF

95 

96 

97def test_openapi_schema(): 1adbc

98 response = client.get("/openapi.json") 1GHIJ

99 assert response.status_code == 200, response.text 1GHIJ

100 assert response.json() == snapshot( 1GHIJ

101 { 

102 "openapi": "3.1.0", 

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

104 "paths": { 

105 "/items/{item_id}": { 

106 "get": { 

107 "responses": { 

108 "200": { 

109 "description": "Successful Response", 

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

111 }, 

112 "422": { 

113 "description": "Validation Error", 

114 "content": { 

115 "application/json": { 

116 "schema": { 

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

118 } 

119 } 

120 }, 

121 }, 

122 }, 

123 "summary": "Get Items", 

124 "operationId": "get_items_items__item_id__get", 

125 "parameters": [ 

126 { 

127 "required": True, 

128 "schema": {"title": "Item Id", "type": "string"}, 

129 "name": "item_id", 

130 "in": "path", 

131 } 

132 ], 

133 }, 

134 "delete": { 

135 "responses": { 

136 "200": { 

137 "description": "Successful Response", 

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

139 }, 

140 "422": { 

141 "description": "Validation Error", 

142 "content": { 

143 "application/json": { 

144 "schema": { 

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

146 } 

147 } 

148 }, 

149 }, 

150 }, 

151 "summary": "Delete Item", 

152 "operationId": "delete_item_items__item_id__delete", 

153 "parameters": [ 

154 { 

155 "required": True, 

156 "schema": {"title": "Item Id", "type": "string"}, 

157 "name": "item_id", 

158 "in": "path", 

159 } 

160 ], 

161 "requestBody": { 

162 "content": { 

163 "application/json": { 

164 "schema": {"$ref": "#/components/schemas/Item"} 

165 } 

166 }, 

167 "required": True, 

168 }, 

169 }, 

170 "options": { 

171 "responses": { 

172 "200": { 

173 "description": "Successful Response", 

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

175 }, 

176 "422": { 

177 "description": "Validation Error", 

178 "content": { 

179 "application/json": { 

180 "schema": { 

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

182 } 

183 } 

184 }, 

185 }, 

186 }, 

187 "summary": "Options Item", 

188 "operationId": "options_item_items__item_id__options", 

189 "parameters": [ 

190 { 

191 "required": True, 

192 "schema": {"title": "Item Id", "type": "string"}, 

193 "name": "item_id", 

194 "in": "path", 

195 } 

196 ], 

197 }, 

198 "head": { 

199 "responses": { 

200 "200": { 

201 "description": "Successful Response", 

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

203 }, 

204 "422": { 

205 "description": "Validation Error", 

206 "content": { 

207 "application/json": { 

208 "schema": { 

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

210 } 

211 } 

212 }, 

213 }, 

214 }, 

215 "summary": "Head Item", 

216 "operationId": "head_item_items__item_id__head", 

217 "parameters": [ 

218 { 

219 "required": True, 

220 "schema": {"title": "Item Id", "type": "string"}, 

221 "name": "item_id", 

222 "in": "path", 

223 } 

224 ], 

225 }, 

226 "patch": { 

227 "responses": { 

228 "200": { 

229 "description": "Successful Response", 

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

231 }, 

232 "422": { 

233 "description": "Validation Error", 

234 "content": { 

235 "application/json": { 

236 "schema": { 

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

238 } 

239 } 

240 }, 

241 }, 

242 }, 

243 "summary": "Patch Item", 

244 "operationId": "patch_item_items__item_id__patch", 

245 "parameters": [ 

246 { 

247 "required": True, 

248 "schema": {"title": "Item Id", "type": "string"}, 

249 "name": "item_id", 

250 "in": "path", 

251 } 

252 ], 

253 "requestBody": { 

254 "content": { 

255 "application/json": { 

256 "schema": {"$ref": "#/components/schemas/Item"} 

257 } 

258 }, 

259 "required": True, 

260 }, 

261 }, 

262 "trace": { 

263 "responses": { 

264 "200": { 

265 "description": "Successful Response", 

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

267 }, 

268 "422": { 

269 "description": "Validation Error", 

270 "content": { 

271 "application/json": { 

272 "schema": { 

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

274 } 

275 } 

276 }, 

277 }, 

278 }, 

279 "summary": "Trace Item", 

280 "operationId": "trace_item_items__item_id__trace", 

281 "parameters": [ 

282 { 

283 "required": True, 

284 "schema": {"title": "Item Id", "type": "string"}, 

285 "name": "item_id", 

286 "in": "path", 

287 } 

288 ], 

289 }, 

290 }, 

291 "/items-not-decorated/{item_id}": { 

292 "get": { 

293 "responses": { 

294 "200": { 

295 "description": "Successful Response", 

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

297 }, 

298 "422": { 

299 "description": "Validation Error", 

300 "content": { 

301 "application/json": { 

302 "schema": { 

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

304 } 

305 } 

306 }, 

307 }, 

308 }, 

309 "summary": "Get Not Decorated", 

310 "operationId": "get_not_decorated_items_not_decorated__item_id__get", 

311 "parameters": [ 

312 { 

313 "required": True, 

314 "schema": {"title": "Item Id", "type": "string"}, 

315 "name": "item_id", 

316 "in": "path", 

317 } 

318 ], 

319 } 

320 }, 

321 }, 

322 "components": { 

323 "schemas": { 

324 "Item": { 

325 "title": "Item", 

326 "required": ["name"], 

327 "type": "object", 

328 "properties": { 

329 "name": {"title": "Name", "type": "string"}, 

330 "price": { 

331 "title": "Price", 

332 "anyOf": [{"type": "number"}, {"type": "null"}], 

333 }, 

334 }, 

335 }, 

336 "ValidationError": { 

337 "title": "ValidationError", 

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

339 "type": "object", 

340 "properties": { 

341 "loc": { 

342 "title": "Location", 

343 "type": "array", 

344 "items": { 

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

346 }, 

347 }, 

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

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

350 "input": {"title": "Input"}, 

351 "ctx": {"title": "Context", "type": "object"}, 

352 }, 

353 }, 

354 "HTTPValidationError": { 

355 "title": "HTTPValidationError", 

356 "type": "object", 

357 "properties": { 

358 "detail": { 

359 "title": "Detail", 

360 "type": "array", 

361 "items": { 

362 "$ref": "#/components/schemas/ValidationError" 

363 }, 

364 } 

365 }, 

366 }, 

367 } 

368 }, 

369 } 

370 )