Coverage for tests / test_tutorial / test_fastapi / test_update / test_tutorial002.py: 100%

99 statements  

« prev     ^ index     » next       coverage.py v7.13.0, created at 2026-11-09 00:02 +0000

1import importlib 1jklmnopqr

2from types import ModuleType 1jklmnopqr

3 

4import pytest 1jklmnopqr

5from dirty_equals import IsOneOf 1jklmnopqr

6from fastapi.testclient import TestClient 1jklmnopqr

7from sqlalchemy import Engine 1jklmnopqr

8from sqlmodel import Session 1jklmnopqr

9 

10 

11@pytest.fixture( 1jklmnopqr

12 name="module", 

13 params=[ 

14 pytest.param("tutorial002_py310"), 

15 ], 

16) 

17def get_module(request: pytest.FixtureRequest, database_engine: Engine) -> ModuleType: 1jklmnopqr

18 mod = importlib.import_module(f"docs_src.tutorial.fastapi.update.{request.param}") 1jklmnopqr

19 mod.engine = database_engine 1jklmnopqr

20 return mod 1jklmnopqr

21 

22 

23def test_tutorial(module: ModuleType): 1jklmnopqr

24 with TestClient(module.app) as client: 1abcdefghi

25 hero1_data = { 1abcdefghi

26 "name": "Deadpond", 

27 "secret_name": "Dive Wilson", 

28 "password": "chimichanga", 

29 } 

30 hero2_data = { 1abcdefghi

31 "name": "Spider-Boy", 

32 "secret_name": "Pedro Parqueador", 

33 "id": 9000, 

34 "password": "auntmay", 

35 } 

36 hero3_data = { 1abcdefghi

37 "name": "Rusty-Man", 

38 "secret_name": "Tommy Sharp", 

39 "age": 48, 

40 "password": "bestpreventer", 

41 } 

42 response = client.post("/heroes/", json=hero1_data) 1abcdefghi

43 assert response.status_code == 200, response.text 1abcdefghi

44 hero1 = response.json() 1abcdefghi

45 assert "password" not in hero1 1abcdefghi

46 assert "hashed_password" not in hero1 1abcdefghi

47 hero1_id = hero1["id"] 1abcdefghi

48 response = client.post("/heroes/", json=hero2_data) 1abcdefghi

49 assert response.status_code == 200, response.text 1abcdefghi

50 hero2 = response.json() 1abcdefghi

51 hero2_id = hero2["id"] 1abcdefghi

52 response = client.post("/heroes/", json=hero3_data) 1abcdefghi

53 assert response.status_code == 200, response.text 1abcdefghi

54 hero3 = response.json() 1abcdefghi

55 hero3_id = hero3["id"] 1abcdefghi

56 response = client.get(f"/heroes/{hero2_id}") 1abcdefghi

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

58 fetched_hero2 = response.json() 1abcdefghi

59 assert "password" not in fetched_hero2 1abcdefghi

60 assert "hashed_password" not in fetched_hero2 1abcdefghi

61 response = client.get("/heroes/9000") 1abcdefghi

62 assert response.status_code == 404, response.text 1abcdefghi

63 response = client.get("/heroes/") 1abcdefghi

64 assert response.status_code == 200, response.text 1abcdefghi

65 data = response.json() 1abcdefghi

66 assert len(data) == 3 1abcdefghi

67 for response_hero in data: 1abcdefghi

68 assert "password" not in response_hero 1abcdefghi

69 assert "hashed_password" not in response_hero 1abcdefghi

70 

71 # Test hashed passwords 

72 with Session(module.engine) as session: 1abcdefghi

73 hero1_db = session.get(module.Hero, hero1_id) 1abcdefghi

74 assert hero1_db 1abcdefghi

75 assert not hasattr(hero1_db, "password") 1abcdefghi

76 assert hero1_db.hashed_password == "not really hashed chimichanga hehehe" 1abcdefghi

77 hero2_db = session.get(module.Hero, hero2_id) 1abcdefghi

78 assert hero2_db 1abcdefghi

79 assert not hasattr(hero2_db, "password") 1abcdefghi

80 assert hero2_db.hashed_password == "not really hashed auntmay hehehe" 1abcdefghi

81 hero3_db = session.get(module.Hero, hero3_id) 1abcdefghi

82 assert hero3_db 1abcdefghi

83 assert not hasattr(hero3_db, "password") 1abcdefghi

84 assert hero3_db.hashed_password == "not really hashed bestpreventer hehehe" 1abcdefghi

85 

86 response = client.patch( 1abcdefghi

87 f"/heroes/{hero2_id}", json={"secret_name": "Spider-Youngster"} 

88 ) 

89 data = response.json() 1abcdefghi

90 assert response.status_code == 200, response.text 1abcdefghi

91 assert data["name"] == hero2_data["name"], "The name should not be set to none" 1abcdefghi

92 assert data["secret_name"] == "Spider-Youngster", ( 1abcdefghi

93 "The secret name should be updated" 

94 ) 

95 assert "password" not in data 1abcdefghi

96 assert "hashed_password" not in data 1abcdefghi

97 with Session(module.engine) as session: 1abcdefghi

98 hero2b_db = session.get(module.Hero, hero2_id) 1abcdefghi

99 assert hero2b_db 1abcdefghi

100 assert not hasattr(hero2b_db, "password") 1abcdefghi

101 assert hero2b_db.hashed_password == "not really hashed auntmay hehehe" 1abcdefghi

102 

103 response = client.patch(f"/heroes/{hero3_id}", json={"age": None}) 1abcdefghi

104 data = response.json() 1abcdefghi

105 assert response.status_code == 200, response.text 1abcdefghi

106 assert data["name"] == hero3_data["name"] 1abcdefghi

107 assert data["age"] is None, ( 1abcdefghi

108 "A field should be updatable to None, even if that's the default" 

109 ) 

110 assert "password" not in data 1abcdefghi

111 assert "hashed_password" not in data 1abcdefghi

112 with Session(module.engine) as session: 1abcdefghi

113 hero3b_db = session.get(module.Hero, hero3_id) 1abcdefghi

114 assert hero3b_db 1abcdefghi

115 assert not hasattr(hero3b_db, "password") 1abcdefghi

116 assert hero3b_db.hashed_password == "not really hashed bestpreventer hehehe" 1abcdefghi

117 

118 # Test update dict, hashed_password 

119 response = client.patch( 1abcdefghi

120 f"/heroes/{hero3_id}", json={"password": "philantroplayboy"} 

121 ) 

122 data = response.json() 1abcdefghi

123 assert response.status_code == 200, response.text 1abcdefghi

124 assert data["name"] == hero3_data["name"] 1abcdefghi

125 assert data["age"] is None 1abcdefghi

126 assert "password" not in data 1abcdefghi

127 assert "hashed_password" not in data 1abcdefghi

128 with Session(module.engine) as session: 1abcdefghi

129 hero3b_db = session.get(module.Hero, hero3_id) 1abcdefghi

130 assert hero3b_db 1abcdefghi

131 assert not hasattr(hero3b_db, "password") 1abcdefghi

132 assert ( 1abcdefghi

133 hero3b_db.hashed_password == "not really hashed philantroplayboy hehehe" 

134 ) 

135 

136 response = client.patch("/heroes/9001", json={"name": "Dragon Cube X"}) 1abcdefghi

137 assert response.status_code == 404, response.text 1abcdefghi

138 

139 response = client.get("/openapi.json") 1abcdefghi

140 assert response.status_code == 200, response.text 1abcdefghi

141 assert response.json() == { 1abcdefghi

142 "openapi": "3.1.0", 

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

144 "paths": { 

145 "/heroes/": { 

146 "get": { 

147 "summary": "Read Heroes", 

148 "operationId": "read_heroes_heroes__get", 

149 "parameters": [ 

150 { 

151 "required": False, 

152 "schema": { 

153 "title": "Offset", 

154 "type": "integer", 

155 "default": 0, 

156 }, 

157 "name": "offset", 

158 "in": "query", 

159 }, 

160 { 

161 "required": False, 

162 "schema": { 

163 "title": "Limit", 

164 "maximum": 100, 

165 "type": "integer", 

166 "default": 100, 

167 }, 

168 "name": "limit", 

169 "in": "query", 

170 }, 

171 ], 

172 "responses": { 

173 "200": { 

174 "description": "Successful Response", 

175 "content": { 

176 "application/json": { 

177 "schema": { 

178 "title": "Response Read Heroes Heroes Get", 

179 "type": "array", 

180 "items": { 

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

182 }, 

183 } 

184 } 

185 }, 

186 }, 

187 "422": { 

188 "description": "Validation Error", 

189 "content": { 

190 "application/json": { 

191 "schema": { 

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

193 } 

194 } 

195 }, 

196 }, 

197 }, 

198 }, 

199 "post": { 

200 "summary": "Create Hero", 

201 "operationId": "create_hero_heroes__post", 

202 "requestBody": { 

203 "content": { 

204 "application/json": { 

205 "schema": { 

206 "$ref": "#/components/schemas/HeroCreate" 

207 } 

208 } 

209 }, 

210 "required": True, 

211 }, 

212 "responses": { 

213 "200": { 

214 "description": "Successful Response", 

215 "content": { 

216 "application/json": { 

217 "schema": { 

218 "$ref": "#/components/schemas/HeroPublic" 

219 } 

220 } 

221 }, 

222 }, 

223 "422": { 

224 "description": "Validation Error", 

225 "content": { 

226 "application/json": { 

227 "schema": { 

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

229 } 

230 } 

231 }, 

232 }, 

233 }, 

234 }, 

235 }, 

236 "/heroes/{hero_id}": { 

237 "get": { 

238 "summary": "Read Hero", 

239 "operationId": "read_hero_heroes__hero_id__get", 

240 "parameters": [ 

241 { 

242 "required": True, 

243 "schema": {"title": "Hero Id", "type": "integer"}, 

244 "name": "hero_id", 

245 "in": "path", 

246 } 

247 ], 

248 "responses": { 

249 "200": { 

250 "description": "Successful Response", 

251 "content": { 

252 "application/json": { 

253 "schema": { 

254 "$ref": "#/components/schemas/HeroPublic" 

255 } 

256 } 

257 }, 

258 }, 

259 "422": { 

260 "description": "Validation Error", 

261 "content": { 

262 "application/json": { 

263 "schema": { 

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

265 } 

266 } 

267 }, 

268 }, 

269 }, 

270 }, 

271 "patch": { 

272 "summary": "Update Hero", 

273 "operationId": "update_hero_heroes__hero_id__patch", 

274 "parameters": [ 

275 { 

276 "required": True, 

277 "schema": {"title": "Hero Id", "type": "integer"}, 

278 "name": "hero_id", 

279 "in": "path", 

280 } 

281 ], 

282 "requestBody": { 

283 "content": { 

284 "application/json": { 

285 "schema": { 

286 "$ref": "#/components/schemas/HeroUpdate" 

287 } 

288 } 

289 }, 

290 "required": True, 

291 }, 

292 "responses": { 

293 "200": { 

294 "description": "Successful Response", 

295 "content": { 

296 "application/json": { 

297 "schema": { 

298 "$ref": "#/components/schemas/HeroPublic" 

299 } 

300 } 

301 }, 

302 }, 

303 "422": { 

304 "description": "Validation Error", 

305 "content": { 

306 "application/json": { 

307 "schema": { 

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

309 } 

310 } 

311 }, 

312 }, 

313 }, 

314 }, 

315 }, 

316 }, 

317 "components": { 

318 "schemas": { 

319 "HTTPValidationError": { 

320 "title": "HTTPValidationError", 

321 "type": "object", 

322 "properties": { 

323 "detail": { 

324 "title": "Detail", 

325 "type": "array", 

326 "items": { 

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

328 }, 

329 } 

330 }, 

331 }, 

332 "HeroCreate": { 

333 "title": "HeroCreate", 

334 "required": ["name", "secret_name", "password"], 

335 "type": "object", 

336 "properties": { 

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

338 "secret_name": {"title": "Secret Name", "type": "string"}, 

339 "age": { 

340 "anyOf": [{"type": "integer"}, {"type": "null"}], 

341 "title": "Age", 

342 }, 

343 "password": {"type": "string", "title": "Password"}, 

344 }, 

345 }, 

346 "HeroPublic": { 

347 "title": "HeroPublic", 

348 "required": ["name", "secret_name", "id"], 

349 "type": "object", 

350 "properties": { 

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

352 "secret_name": {"title": "Secret Name", "type": "string"}, 

353 "age": { 

354 "anyOf": [{"type": "integer"}, {"type": "null"}], 

355 "title": "Age", 

356 }, 

357 "id": {"title": "Id", "type": "integer"}, 

358 }, 

359 }, 

360 "HeroUpdate": { 

361 "title": "HeroUpdate", 

362 "type": "object", 

363 "properties": { 

364 "name": { 

365 "anyOf": [{"type": "string"}, {"type": "null"}], 

366 "title": "Name", 

367 }, 

368 "secret_name": { 

369 "anyOf": [{"type": "string"}, {"type": "null"}], 

370 "title": "Secret Name", 

371 }, 

372 "age": { 

373 "anyOf": [{"type": "integer"}, {"type": "null"}], 

374 "title": "Age", 

375 }, 

376 "password": { 

377 "anyOf": [{"type": "string"}, {"type": "null"}], 

378 "title": "Password", 

379 }, 

380 }, 

381 }, 

382 "ValidationError": { 

383 "title": "ValidationError", 

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

385 "type": "object", 

386 "properties": IsOneOf( 

387 { 

388 "loc": { 

389 "title": "Location", 

390 "type": "array", 

391 "items": { 

392 "anyOf": [ 

393 {"type": "string"}, 

394 {"type": "integer"}, 

395 ] 

396 }, 

397 }, 

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

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

400 }, 

401 { 

402 "loc": { 

403 "title": "Location", 

404 "type": "array", 

405 "items": { 

406 "anyOf": [ 

407 {"type": "string"}, 

408 {"type": "integer"}, 

409 ] 

410 }, 

411 }, 

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

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

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

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

416 }, 

417 ), 

418 }, 

419 } 

420 }, 

421 }