Coverage for tests / test_extra_routes.py: 100%

64 statements  

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

1from typing import Optional 1abcd

2 

3from fastapi import FastAPI 1abcd

4from fastapi.responses import JSONResponse 1abcd

5from fastapi.testclient import TestClient 1abcd

6from inline_snapshot import snapshot 1abcd

7from pydantic import BaseModel 1abcd

8 

9app = FastAPI() 1abcd

10 

11 

12class Item(BaseModel): 1abcd

13 name: str 1abcd

14 price: Optional[float] = None 1abcd

15 

16 

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

18def get_items(item_id: str): 1abcd

19 return {"item_id": item_id} 1efg

20 

21 

22def get_not_decorated(item_id: str): 1abcd

23 return {"item_id": item_id} 1hij

24 

25 

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

27 

28 

29@app.delete("/items/{item_id}") 1abcd

30def delete_item(item_id: str, item: Item): 1abcd

31 return {"item_id": item_id, "item": item} 1klm

32 

33 

34@app.head("/items/{item_id}") 1abcd

35def head_item(item_id: str): 1abcd

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

37 

38 

39@app.options("/items/{item_id}") 1abcd

40def options_item(item_id: str): 1abcd

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

42 

43 

44@app.patch("/items/{item_id}") 1abcd

45def patch_item(item_id: str, item: Item): 1abcd

46 return {"item_id": item_id, "item": item} 1tuv

47 

48 

49@app.trace("/items/{item_id}") 1abcd

50def trace_item(item_id: str): 1abcd

51 return JSONResponse(None, media_type="message/http") 1wxy

52 

53 

54client = TestClient(app) 1abcd

55 

56 

57def test_get_api_route(): 1abcd

58 response = client.get("/items/foo") 1efg

59 assert response.status_code == 200, response.text 1efg

60 assert response.json() == {"item_id": "foo"} 1efg

61 

62 

63def test_get_api_route_not_decorated(): 1abcd

64 response = client.get("/items-not-decorated/foo") 1hij

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

66 assert response.json() == {"item_id": "foo"} 1hij

67 

68 

69def test_delete(): 1abcd

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

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

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

73 

74 

75def test_head(): 1abcd

76 response = client.head("/items/foo") 1nop

77 assert response.status_code == 200, response.text 1nop

78 assert response.headers["x-fastapi-item-id"] == "foo" 1nop

79 

80 

81def test_options(): 1abcd

82 response = client.options("/items/foo") 1qrs

83 assert response.status_code == 200, response.text 1qrs

84 assert response.headers["x-fastapi-item-id"] == "foo" 1qrs

85 

86 

87def test_patch(): 1abcd

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

89 assert response.status_code == 200, response.text 1tuv

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

91 

92 

93def test_trace(): 1abcd

94 response = client.request("trace", "/items/foo") 1wxy

95 assert response.status_code == 200, response.text 1wxy

96 assert response.headers["content-type"] == "message/http" 1wxy

97 

98 

99def test_openapi_schema(): 1abcd

100 response = client.get("/openapi.json") 1zAB

101 assert response.status_code == 200, response.text 1zAB

102 assert response.json() == snapshot( 1zAB

103 { 

104 "openapi": "3.1.0", 

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

106 "paths": { 

107 "/items/{item_id}": { 

108 "get": { 

109 "responses": { 

110 "200": { 

111 "description": "Successful Response", 

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

113 }, 

114 "422": { 

115 "description": "Validation Error", 

116 "content": { 

117 "application/json": { 

118 "schema": { 

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

120 } 

121 } 

122 }, 

123 }, 

124 }, 

125 "summary": "Get Items", 

126 "operationId": "get_items_items__item_id__get", 

127 "parameters": [ 

128 { 

129 "required": True, 

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

131 "name": "item_id", 

132 "in": "path", 

133 } 

134 ], 

135 }, 

136 "delete": { 

137 "responses": { 

138 "200": { 

139 "description": "Successful Response", 

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

141 }, 

142 "422": { 

143 "description": "Validation Error", 

144 "content": { 

145 "application/json": { 

146 "schema": { 

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

148 } 

149 } 

150 }, 

151 }, 

152 }, 

153 "summary": "Delete Item", 

154 "operationId": "delete_item_items__item_id__delete", 

155 "parameters": [ 

156 { 

157 "required": True, 

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

159 "name": "item_id", 

160 "in": "path", 

161 } 

162 ], 

163 "requestBody": { 

164 "content": { 

165 "application/json": { 

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

167 } 

168 }, 

169 "required": True, 

170 }, 

171 }, 

172 "options": { 

173 "responses": { 

174 "200": { 

175 "description": "Successful Response", 

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

177 }, 

178 "422": { 

179 "description": "Validation Error", 

180 "content": { 

181 "application/json": { 

182 "schema": { 

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

184 } 

185 } 

186 }, 

187 }, 

188 }, 

189 "summary": "Options Item", 

190 "operationId": "options_item_items__item_id__options", 

191 "parameters": [ 

192 { 

193 "required": True, 

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

195 "name": "item_id", 

196 "in": "path", 

197 } 

198 ], 

199 }, 

200 "head": { 

201 "responses": { 

202 "200": { 

203 "description": "Successful Response", 

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

205 }, 

206 "422": { 

207 "description": "Validation Error", 

208 "content": { 

209 "application/json": { 

210 "schema": { 

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

212 } 

213 } 

214 }, 

215 }, 

216 }, 

217 "summary": "Head Item", 

218 "operationId": "head_item_items__item_id__head", 

219 "parameters": [ 

220 { 

221 "required": True, 

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

223 "name": "item_id", 

224 "in": "path", 

225 } 

226 ], 

227 }, 

228 "patch": { 

229 "responses": { 

230 "200": { 

231 "description": "Successful Response", 

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

233 }, 

234 "422": { 

235 "description": "Validation Error", 

236 "content": { 

237 "application/json": { 

238 "schema": { 

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

240 } 

241 } 

242 }, 

243 }, 

244 }, 

245 "summary": "Patch Item", 

246 "operationId": "patch_item_items__item_id__patch", 

247 "parameters": [ 

248 { 

249 "required": True, 

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

251 "name": "item_id", 

252 "in": "path", 

253 } 

254 ], 

255 "requestBody": { 

256 "content": { 

257 "application/json": { 

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

259 } 

260 }, 

261 "required": True, 

262 }, 

263 }, 

264 "trace": { 

265 "responses": { 

266 "200": { 

267 "description": "Successful Response", 

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

269 }, 

270 "422": { 

271 "description": "Validation Error", 

272 "content": { 

273 "application/json": { 

274 "schema": { 

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

276 } 

277 } 

278 }, 

279 }, 

280 }, 

281 "summary": "Trace Item", 

282 "operationId": "trace_item_items__item_id__trace", 

283 "parameters": [ 

284 { 

285 "required": True, 

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

287 "name": "item_id", 

288 "in": "path", 

289 } 

290 ], 

291 }, 

292 }, 

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

294 "get": { 

295 "responses": { 

296 "200": { 

297 "description": "Successful Response", 

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

299 }, 

300 "422": { 

301 "description": "Validation Error", 

302 "content": { 

303 "application/json": { 

304 "schema": { 

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

306 } 

307 } 

308 }, 

309 }, 

310 }, 

311 "summary": "Get Not Decorated", 

312 "operationId": "get_not_decorated_items_not_decorated__item_id__get", 

313 "parameters": [ 

314 { 

315 "required": True, 

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

317 "name": "item_id", 

318 "in": "path", 

319 } 

320 ], 

321 } 

322 }, 

323 }, 

324 "components": { 

325 "schemas": { 

326 "Item": { 

327 "title": "Item", 

328 "required": ["name"], 

329 "type": "object", 

330 "properties": { 

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

332 "price": { 

333 "title": "Price", 

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

335 }, 

336 }, 

337 }, 

338 "ValidationError": { 

339 "title": "ValidationError", 

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

341 "type": "object", 

342 "properties": { 

343 "loc": { 

344 "title": "Location", 

345 "type": "array", 

346 "items": { 

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

348 }, 

349 }, 

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

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

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

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

354 }, 

355 }, 

356 "HTTPValidationError": { 

357 "title": "HTTPValidationError", 

358 "type": "object", 

359 "properties": { 

360 "detail": { 

361 "title": "Detail", 

362 "type": "array", 

363 "items": { 

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

365 }, 

366 } 

367 }, 

368 }, 

369 } 

370 }, 

371 } 

372 )