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-01-06 21:09 +0000

1import importlib 1jklmnopqr

2from types import ModuleType 1jklmnopqr

3 

4import pytest 1jklmnopqr

5from fastapi.testclient import TestClient 1jklmnopqr

6from sqlmodel import Session, create_engine 1jklmnopqr

7from sqlmodel.pool import StaticPool 1jklmnopqr

8 

9from tests.conftest import needs_py310 1jklmnopqr

10 

11 

12@pytest.fixture( 1jklmnopqr

13 name="module", 

14 params=[ 

15 pytest.param("tutorial002_py39"), 

16 pytest.param("tutorial002_py310", marks=needs_py310), 

17 ], 

18) 

19def get_module(request: pytest.FixtureRequest) -> ModuleType: 1jklmnopqr

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

21 mod.sqlite_url = "sqlite://" 1jklmnopqr

22 mod.engine = create_engine( 1jklmnopqr

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

24 ) 

25 return mod 1jklmnopqr

26 

27 

28def test_tutorial(module: ModuleType): 1jklmnopqr

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

30 hero1_data = { 1abcdefghi

31 "name": "Deadpond", 

32 "secret_name": "Dive Wilson", 

33 "password": "chimichanga", 

34 } 

35 hero2_data = { 1abcdefghi

36 "name": "Spider-Boy", 

37 "secret_name": "Pedro Parqueador", 

38 "id": 9000, 

39 "password": "auntmay", 

40 } 

41 hero3_data = { 1abcdefghi

42 "name": "Rusty-Man", 

43 "secret_name": "Tommy Sharp", 

44 "age": 48, 

45 "password": "bestpreventer", 

46 } 

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

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

49 hero1 = response.json() 1abcdefghi

50 assert "password" not in hero1 1abcdefghi

51 assert "hashed_password" not in hero1 1abcdefghi

52 hero1_id = hero1["id"] 1abcdefghi

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

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

55 hero2 = response.json() 1abcdefghi

56 hero2_id = hero2["id"] 1abcdefghi

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

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

59 hero3 = response.json() 1abcdefghi

60 hero3_id = hero3["id"] 1abcdefghi

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

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

63 fetched_hero2 = response.json() 1abcdefghi

64 assert "password" not in fetched_hero2 1abcdefghi

65 assert "hashed_password" not in fetched_hero2 1abcdefghi

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

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

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

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

70 data = response.json() 1abcdefghi

71 assert len(data) == 3 1abcdefghi

72 for response_hero in data: 1abcdefghi

73 assert "password" not in response_hero 1abcdefghi

74 assert "hashed_password" not in response_hero 1abcdefghi

75 

76 # Test hashed passwords 

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

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

79 assert hero1_db 1abcdefghi

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

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

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

83 assert hero2_db 1abcdefghi

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

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

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

87 assert hero3_db 1abcdefghi

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

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

90 

91 response = client.patch( 1abcdefghi

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

93 ) 

94 data = response.json() 1abcdefghi

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

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

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

98 "The secret name should be updated" 

99 ) 

100 assert "password" not in data 1abcdefghi

101 assert "hashed_password" not in data 1abcdefghi

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

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

104 assert hero2b_db 1abcdefghi

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

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

107 

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

109 data = response.json() 1abcdefghi

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

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

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

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

114 ) 

115 assert "password" not in data 1abcdefghi

116 assert "hashed_password" not in data 1abcdefghi

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

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

119 assert hero3b_db 1abcdefghi

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

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

122 

123 # Test update dict, hashed_password 

124 response = client.patch( 1abcdefghi

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

126 ) 

127 data = response.json() 1abcdefghi

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

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

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

131 assert "password" not in data 1abcdefghi

132 assert "hashed_password" not in data 1abcdefghi

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

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

135 assert hero3b_db 1abcdefghi

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

137 assert ( 1abcdefghi

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

139 ) 

140 

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

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

143 

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

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

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

147 "openapi": "3.1.0", 

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

149 "paths": { 

150 "/heroes/": { 

151 "get": { 

152 "summary": "Read Heroes", 

153 "operationId": "read_heroes_heroes__get", 

154 "parameters": [ 

155 { 

156 "required": False, 

157 "schema": { 

158 "title": "Offset", 

159 "type": "integer", 

160 "default": 0, 

161 }, 

162 "name": "offset", 

163 "in": "query", 

164 }, 

165 { 

166 "required": False, 

167 "schema": { 

168 "title": "Limit", 

169 "maximum": 100, 

170 "type": "integer", 

171 "default": 100, 

172 }, 

173 "name": "limit", 

174 "in": "query", 

175 }, 

176 ], 

177 "responses": { 

178 "200": { 

179 "description": "Successful Response", 

180 "content": { 

181 "application/json": { 

182 "schema": { 

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

184 "type": "array", 

185 "items": { 

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

187 }, 

188 } 

189 } 

190 }, 

191 }, 

192 "422": { 

193 "description": "Validation Error", 

194 "content": { 

195 "application/json": { 

196 "schema": { 

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

198 } 

199 } 

200 }, 

201 }, 

202 }, 

203 }, 

204 "post": { 

205 "summary": "Create Hero", 

206 "operationId": "create_hero_heroes__post", 

207 "requestBody": { 

208 "content": { 

209 "application/json": { 

210 "schema": { 

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

212 } 

213 } 

214 }, 

215 "required": True, 

216 }, 

217 "responses": { 

218 "200": { 

219 "description": "Successful Response", 

220 "content": { 

221 "application/json": { 

222 "schema": { 

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

224 } 

225 } 

226 }, 

227 }, 

228 "422": { 

229 "description": "Validation Error", 

230 "content": { 

231 "application/json": { 

232 "schema": { 

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

234 } 

235 } 

236 }, 

237 }, 

238 }, 

239 }, 

240 }, 

241 "/heroes/{hero_id}": { 

242 "get": { 

243 "summary": "Read Hero", 

244 "operationId": "read_hero_heroes__hero_id__get", 

245 "parameters": [ 

246 { 

247 "required": True, 

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

249 "name": "hero_id", 

250 "in": "path", 

251 } 

252 ], 

253 "responses": { 

254 "200": { 

255 "description": "Successful Response", 

256 "content": { 

257 "application/json": { 

258 "schema": { 

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

260 } 

261 } 

262 }, 

263 }, 

264 "422": { 

265 "description": "Validation Error", 

266 "content": { 

267 "application/json": { 

268 "schema": { 

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

270 } 

271 } 

272 }, 

273 }, 

274 }, 

275 }, 

276 "patch": { 

277 "summary": "Update Hero", 

278 "operationId": "update_hero_heroes__hero_id__patch", 

279 "parameters": [ 

280 { 

281 "required": True, 

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

283 "name": "hero_id", 

284 "in": "path", 

285 } 

286 ], 

287 "requestBody": { 

288 "content": { 

289 "application/json": { 

290 "schema": { 

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

292 } 

293 } 

294 }, 

295 "required": True, 

296 }, 

297 "responses": { 

298 "200": { 

299 "description": "Successful Response", 

300 "content": { 

301 "application/json": { 

302 "schema": { 

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

304 } 

305 } 

306 }, 

307 }, 

308 "422": { 

309 "description": "Validation Error", 

310 "content": { 

311 "application/json": { 

312 "schema": { 

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

314 } 

315 } 

316 }, 

317 }, 

318 }, 

319 }, 

320 }, 

321 }, 

322 "components": { 

323 "schemas": { 

324 "HTTPValidationError": { 

325 "title": "HTTPValidationError", 

326 "type": "object", 

327 "properties": { 

328 "detail": { 

329 "title": "Detail", 

330 "type": "array", 

331 "items": { 

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

333 }, 

334 } 

335 }, 

336 }, 

337 "HeroCreate": { 

338 "title": "HeroCreate", 

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

340 "type": "object", 

341 "properties": { 

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

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

344 "age": { 

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

346 "title": "Age", 

347 }, 

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

349 }, 

350 }, 

351 "HeroPublic": { 

352 "title": "HeroPublic", 

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

354 "type": "object", 

355 "properties": { 

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

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

358 "age": { 

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

360 "title": "Age", 

361 }, 

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

363 }, 

364 }, 

365 "HeroUpdate": { 

366 "title": "HeroUpdate", 

367 "type": "object", 

368 "properties": { 

369 "name": { 

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

371 "title": "Name", 

372 }, 

373 "secret_name": { 

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

375 "title": "Secret Name", 

376 }, 

377 "age": { 

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

379 "title": "Age", 

380 }, 

381 "password": { 

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

383 "title": "Password", 

384 }, 

385 }, 

386 }, 

387 "ValidationError": { 

388 "title": "ValidationError", 

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

390 "type": "object", 

391 "properties": { 

392 "loc": { 

393 "title": "Location", 

394 "type": "array", 

395 "items": { 

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

397 }, 

398 }, 

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

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

401 }, 

402 }, 

403 } 

404 }, 

405 }