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

100 statements  

« prev     ^ index     » next       coverage.py v7.13.0, created at 2026-06-10 09:40 +0000

1import importlib 1ijklmnop

2from types import ModuleType 1ijklmnop

3 

4import pytest 1ijklmnop

5from dirty_equals import IsOneOf 1ijklmnop

6from fastapi.testclient import TestClient 1ijklmnop

7from sqlmodel import Session, create_engine 1ijklmnop

8from sqlmodel.pool import StaticPool 1ijklmnop

9 

10 

11@pytest.fixture( 1ijklmnop

12 name="module", 

13 params=[ 

14 pytest.param("tutorial002_py310"), 

15 ], 

16) 

17def get_module(request: pytest.FixtureRequest) -> ModuleType: 1ijklmnop

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

19 mod.sqlite_url = "sqlite://" 1ijklmnop

20 mod.engine = create_engine( 1ijklmnop

21 mod.sqlite_url, connect_args=mod.connect_args, poolclass=StaticPool 

22 ) 

23 return mod 1ijklmnop

24 

25 

26def test_tutorial(module: ModuleType): 1ijklmnop

27 with TestClient(module.app) as client: 1abcdefgh

28 hero1_data = { 1abcdefgh

29 "name": "Deadpond", 

30 "secret_name": "Dive Wilson", 

31 "password": "chimichanga", 

32 } 

33 hero2_data = { 1abcdefgh

34 "name": "Spider-Boy", 

35 "secret_name": "Pedro Parqueador", 

36 "id": 9000, 

37 "password": "auntmay", 

38 } 

39 hero3_data = { 1abcdefgh

40 "name": "Rusty-Man", 

41 "secret_name": "Tommy Sharp", 

42 "age": 48, 

43 "password": "bestpreventer", 

44 } 

45 response = client.post("/heroes/", json=hero1_data) 1abcdefgh

46 assert response.status_code == 200, response.text 1abcdefgh

47 hero1 = response.json() 1abcdefgh

48 assert "password" not in hero1 1abcdefgh

49 assert "hashed_password" not in hero1 1abcdefgh

50 hero1_id = hero1["id"] 1abcdefgh

51 response = client.post("/heroes/", json=hero2_data) 1abcdefgh

52 assert response.status_code == 200, response.text 1abcdefgh

53 hero2 = response.json() 1abcdefgh

54 hero2_id = hero2["id"] 1abcdefgh

55 response = client.post("/heroes/", json=hero3_data) 1abcdefgh

56 assert response.status_code == 200, response.text 1abcdefgh

57 hero3 = response.json() 1abcdefgh

58 hero3_id = hero3["id"] 1abcdefgh

59 response = client.get(f"/heroes/{hero2_id}") 1abcdefgh

60 assert response.status_code == 200, response.text 1abcdefgh

61 fetched_hero2 = response.json() 1abcdefgh

62 assert "password" not in fetched_hero2 1abcdefgh

63 assert "hashed_password" not in fetched_hero2 1abcdefgh

64 response = client.get("/heroes/9000") 1abcdefgh

65 assert response.status_code == 404, response.text 1abcdefgh

66 response = client.get("/heroes/") 1abcdefgh

67 assert response.status_code == 200, response.text 1abcdefgh

68 data = response.json() 1abcdefgh

69 assert len(data) == 3 1abcdefgh

70 for response_hero in data: 1abcdefgh

71 assert "password" not in response_hero 1abcdefgh

72 assert "hashed_password" not in response_hero 1abcdefgh

73 

74 # Test hashed passwords 

75 with Session(module.engine) as session: 1abcdefgh

76 hero1_db = session.get(module.Hero, hero1_id) 1abcdefgh

77 assert hero1_db 1abcdefgh

78 assert not hasattr(hero1_db, "password") 1abcdefgh

79 assert hero1_db.hashed_password == "not really hashed chimichanga hehehe" 1abcdefgh

80 hero2_db = session.get(module.Hero, hero2_id) 1abcdefgh

81 assert hero2_db 1abcdefgh

82 assert not hasattr(hero2_db, "password") 1abcdefgh

83 assert hero2_db.hashed_password == "not really hashed auntmay hehehe" 1abcdefgh

84 hero3_db = session.get(module.Hero, hero3_id) 1abcdefgh

85 assert hero3_db 1abcdefgh

86 assert not hasattr(hero3_db, "password") 1abcdefgh

87 assert hero3_db.hashed_password == "not really hashed bestpreventer hehehe" 1abcdefgh

88 

89 response = client.patch( 1abcdefgh

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

91 ) 

92 data = response.json() 1abcdefgh

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

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

95 assert data["secret_name"] == "Spider-Youngster", ( 1abcdefgh

96 "The secret name should be updated" 

97 ) 

98 assert "password" not in data 1abcdefgh

99 assert "hashed_password" not in data 1abcdefgh

100 with Session(module.engine) as session: 1abcdefgh

101 hero2b_db = session.get(module.Hero, hero2_id) 1abcdefgh

102 assert hero2b_db 1abcdefgh

103 assert not hasattr(hero2b_db, "password") 1abcdefgh

104 assert hero2b_db.hashed_password == "not really hashed auntmay hehehe" 1abcdefgh

105 

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

107 data = response.json() 1abcdefgh

108 assert response.status_code == 200, response.text 1abcdefgh

109 assert data["name"] == hero3_data["name"] 1abcdefgh

110 assert data["age"] is None, ( 1abcdefgh

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

112 ) 

113 assert "password" not in data 1abcdefgh

114 assert "hashed_password" not in data 1abcdefgh

115 with Session(module.engine) as session: 1abcdefgh

116 hero3b_db = session.get(module.Hero, hero3_id) 1abcdefgh

117 assert hero3b_db 1abcdefgh

118 assert not hasattr(hero3b_db, "password") 1abcdefgh

119 assert hero3b_db.hashed_password == "not really hashed bestpreventer hehehe" 1abcdefgh

120 

121 # Test update dict, hashed_password 

122 response = client.patch( 1abcdefgh

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

124 ) 

125 data = response.json() 1abcdefgh

126 assert response.status_code == 200, response.text 1abcdefgh

127 assert data["name"] == hero3_data["name"] 1abcdefgh

128 assert data["age"] is None 1abcdefgh

129 assert "password" not in data 1abcdefgh

130 assert "hashed_password" not in data 1abcdefgh

131 with Session(module.engine) as session: 1abcdefgh

132 hero3b_db = session.get(module.Hero, hero3_id) 1abcdefgh

133 assert hero3b_db 1abcdefgh

134 assert not hasattr(hero3b_db, "password") 1abcdefgh

135 assert ( 1abcdefgh

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

137 ) 

138 

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

140 assert response.status_code == 404, response.text 1abcdefgh

141 

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

143 assert response.status_code == 200, response.text 1abcdefgh

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

145 "openapi": "3.1.0", 

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

147 "paths": { 

148 "/heroes/": { 

149 "get": { 

150 "summary": "Read Heroes", 

151 "operationId": "read_heroes_heroes__get", 

152 "parameters": [ 

153 { 

154 "required": False, 

155 "schema": { 

156 "title": "Offset", 

157 "type": "integer", 

158 "default": 0, 

159 }, 

160 "name": "offset", 

161 "in": "query", 

162 }, 

163 { 

164 "required": False, 

165 "schema": { 

166 "title": "Limit", 

167 "maximum": 100, 

168 "type": "integer", 

169 "default": 100, 

170 }, 

171 "name": "limit", 

172 "in": "query", 

173 }, 

174 ], 

175 "responses": { 

176 "200": { 

177 "description": "Successful Response", 

178 "content": { 

179 "application/json": { 

180 "schema": { 

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

182 "type": "array", 

183 "items": { 

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

185 }, 

186 } 

187 } 

188 }, 

189 }, 

190 "422": { 

191 "description": "Validation Error", 

192 "content": { 

193 "application/json": { 

194 "schema": { 

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

196 } 

197 } 

198 }, 

199 }, 

200 }, 

201 }, 

202 "post": { 

203 "summary": "Create Hero", 

204 "operationId": "create_hero_heroes__post", 

205 "requestBody": { 

206 "content": { 

207 "application/json": { 

208 "schema": { 

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

210 } 

211 } 

212 }, 

213 "required": True, 

214 }, 

215 "responses": { 

216 "200": { 

217 "description": "Successful Response", 

218 "content": { 

219 "application/json": { 

220 "schema": { 

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

222 } 

223 } 

224 }, 

225 }, 

226 "422": { 

227 "description": "Validation Error", 

228 "content": { 

229 "application/json": { 

230 "schema": { 

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

232 } 

233 } 

234 }, 

235 }, 

236 }, 

237 }, 

238 }, 

239 "/heroes/{hero_id}": { 

240 "get": { 

241 "summary": "Read Hero", 

242 "operationId": "read_hero_heroes__hero_id__get", 

243 "parameters": [ 

244 { 

245 "required": True, 

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

247 "name": "hero_id", 

248 "in": "path", 

249 } 

250 ], 

251 "responses": { 

252 "200": { 

253 "description": "Successful Response", 

254 "content": { 

255 "application/json": { 

256 "schema": { 

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

258 } 

259 } 

260 }, 

261 }, 

262 "422": { 

263 "description": "Validation Error", 

264 "content": { 

265 "application/json": { 

266 "schema": { 

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

268 } 

269 } 

270 }, 

271 }, 

272 }, 

273 }, 

274 "patch": { 

275 "summary": "Update Hero", 

276 "operationId": "update_hero_heroes__hero_id__patch", 

277 "parameters": [ 

278 { 

279 "required": True, 

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

281 "name": "hero_id", 

282 "in": "path", 

283 } 

284 ], 

285 "requestBody": { 

286 "content": { 

287 "application/json": { 

288 "schema": { 

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

290 } 

291 } 

292 }, 

293 "required": True, 

294 }, 

295 "responses": { 

296 "200": { 

297 "description": "Successful Response", 

298 "content": { 

299 "application/json": { 

300 "schema": { 

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

302 } 

303 } 

304 }, 

305 }, 

306 "422": { 

307 "description": "Validation Error", 

308 "content": { 

309 "application/json": { 

310 "schema": { 

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

312 } 

313 } 

314 }, 

315 }, 

316 }, 

317 }, 

318 }, 

319 }, 

320 "components": { 

321 "schemas": { 

322 "HTTPValidationError": { 

323 "title": "HTTPValidationError", 

324 "type": "object", 

325 "properties": { 

326 "detail": { 

327 "title": "Detail", 

328 "type": "array", 

329 "items": { 

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

331 }, 

332 } 

333 }, 

334 }, 

335 "HeroCreate": { 

336 "title": "HeroCreate", 

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

338 "type": "object", 

339 "properties": { 

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

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

342 "age": { 

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

344 "title": "Age", 

345 }, 

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

347 }, 

348 }, 

349 "HeroPublic": { 

350 "title": "HeroPublic", 

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

352 "type": "object", 

353 "properties": { 

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

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

356 "age": { 

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

358 "title": "Age", 

359 }, 

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

361 }, 

362 }, 

363 "HeroUpdate": { 

364 "title": "HeroUpdate", 

365 "type": "object", 

366 "properties": { 

367 "name": { 

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

369 "title": "Name", 

370 }, 

371 "secret_name": { 

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

373 "title": "Secret Name", 

374 }, 

375 "age": { 

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

377 "title": "Age", 

378 }, 

379 "password": { 

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

381 "title": "Password", 

382 }, 

383 }, 

384 }, 

385 "ValidationError": { 

386 "title": "ValidationError", 

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

388 "type": "object", 

389 "properties": IsOneOf( 

390 { 

391 "loc": { 

392 "title": "Location", 

393 "type": "array", 

394 "items": { 

395 "anyOf": [ 

396 {"type": "string"}, 

397 {"type": "integer"}, 

398 ] 

399 }, 

400 }, 

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

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

403 }, 

404 { 

405 "loc": { 

406 "title": "Location", 

407 "type": "array", 

408 "items": { 

409 "anyOf": [ 

410 {"type": "string"}, 

411 {"type": "integer"}, 

412 ] 

413 }, 

414 }, 

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

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

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

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

419 }, 

420 ), 

421 }, 

422 } 

423 }, 

424 }