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

94 statements  

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

1from dirty_equals import IsDict 1ghijkl

2from fastapi.testclient import TestClient 1ghijkl

3from sqlmodel import Session, create_engine 1ghijkl

4from sqlmodel.pool import StaticPool 1ghijkl

5 

6 

7def test_tutorial(clear_sqlmodel): 1ghijkl

8 from docs_src.tutorial.fastapi.update import tutorial002 as mod 1fabcde

9 

10 mod.sqlite_url = "sqlite://" 1fabcde

11 mod.engine = create_engine( 1fabcde

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

13 ) 

14 

15 with TestClient(mod.app) as client: 1fabcde

16 hero1_data = { 1abcde

17 "name": "Deadpond", 

18 "secret_name": "Dive Wilson", 

19 "password": "chimichanga", 

20 } 

21 hero2_data = { 1abcde

22 "name": "Spider-Boy", 

23 "secret_name": "Pedro Parqueador", 

24 "id": 9000, 

25 "password": "auntmay", 

26 } 

27 hero3_data = { 1abcde

28 "name": "Rusty-Man", 

29 "secret_name": "Tommy Sharp", 

30 "age": 48, 

31 "password": "bestpreventer", 

32 } 

33 response = client.post("/heroes/", json=hero1_data) 1fabcde

34 assert response.status_code == 200, response.text 1fabcde

35 hero1 = response.json() 1fabcde

36 assert "password" not in hero1 1fabcde

37 assert "hashed_password" not in hero1 1fabcde

38 hero1_id = hero1["id"] 1fabcde

39 response = client.post("/heroes/", json=hero2_data) 1fabcde

40 assert response.status_code == 200, response.text 1fabcde

41 hero2 = response.json() 1fabcde

42 hero2_id = hero2["id"] 1fabcde

43 response = client.post("/heroes/", json=hero3_data) 1fabcde

44 assert response.status_code == 200, response.text 1fabcde

45 hero3 = response.json() 1fabcde

46 hero3_id = hero3["id"] 1fabcde

47 response = client.get(f"/heroes/{hero2_id}") 1fabcde

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

49 fetched_hero2 = response.json() 1fabcde

50 assert "password" not in fetched_hero2 1fabcde

51 assert "hashed_password" not in fetched_hero2 1fabcde

52 response = client.get("/heroes/9000") 1fabcde

53 assert response.status_code == 404, response.text 1fabcde

54 response = client.get("/heroes/") 1fabcde

55 assert response.status_code == 200, response.text 1fabcde

56 data = response.json() 1fabcde

57 assert len(data) == 3 1fabcde

58 for response_hero in data: 1fabcde

59 assert "password" not in response_hero 1fabcde

60 assert "hashed_password" not in response_hero 1fabcde

61 

62 # Test hashed passwords 

63 with Session(mod.engine) as session: 1fabcde

64 hero1_db = session.get(mod.Hero, hero1_id) 1fabcde

65 assert hero1_db 1fabcde

66 assert not hasattr(hero1_db, "password") 1fabcde

67 assert hero1_db.hashed_password == "not really hashed chimichanga hehehe" 1fabcde

68 hero2_db = session.get(mod.Hero, hero2_id) 1fabcde

69 assert hero2_db 1fabcde

70 assert not hasattr(hero2_db, "password") 1fabcde

71 assert hero2_db.hashed_password == "not really hashed auntmay hehehe" 1fabcde

72 hero3_db = session.get(mod.Hero, hero3_id) 1fabcde

73 assert hero3_db 1fabcde

74 assert not hasattr(hero3_db, "password") 1fabcde

75 assert hero3_db.hashed_password == "not really hashed bestpreventer hehehe" 1fabcde

76 

77 response = client.patch( 1fabcde

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

79 ) 

80 data = response.json() 1fabcde

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

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

83 assert ( 1fabcde

84 data["secret_name"] == "Spider-Youngster" 

85 ), "The secret name should be updated" 

86 assert "password" not in data 1fabcde

87 assert "hashed_password" not in data 1fabcde

88 with Session(mod.engine) as session: 1fabcde

89 hero2b_db = session.get(mod.Hero, hero2_id) 1fabcde

90 assert hero2b_db 1fabcde

91 assert not hasattr(hero2b_db, "password") 1fabcde

92 assert hero2b_db.hashed_password == "not really hashed auntmay hehehe" 1fabcde

93 

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

95 data = response.json() 1fabcde

96 assert response.status_code == 200, response.text 1fabcde

97 assert data["name"] == hero3_data["name"] 1fabcde

98 assert ( 1fabcde

99 data["age"] is None 

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

101 assert "password" not in data 1fabcde

102 assert "hashed_password" not in data 1fabcde

103 with Session(mod.engine) as session: 1fabcde

104 hero3b_db = session.get(mod.Hero, hero3_id) 1fabcde

105 assert hero3b_db 1fabcde

106 assert not hasattr(hero3b_db, "password") 1fabcde

107 assert hero3b_db.hashed_password == "not really hashed bestpreventer hehehe" 1fabcde

108 

109 # Test update dict, hashed_password 

110 response = client.patch( 1fabcde

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

112 ) 

113 data = response.json() 1fabcde

114 assert response.status_code == 200, response.text 1fabcde

115 assert data["name"] == hero3_data["name"] 1fabcde

116 assert data["age"] is None 1fabcde

117 assert "password" not in data 1fabcde

118 assert "hashed_password" not in data 1fabcde

119 with Session(mod.engine) as session: 1fabcde

120 hero3b_db = session.get(mod.Hero, hero3_id) 1fabcde

121 assert hero3b_db 1fabcde

122 assert not hasattr(hero3b_db, "password") 1fabcde

123 assert ( 1fabcde

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

125 ) 

126 

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

128 assert response.status_code == 404, response.text 1fabcde

129 

130 response = client.get("/openapi.json") 1fabcde

131 assert response.status_code == 200, response.text 1fabcde

132 assert response.json() == { 1fabcde

133 "openapi": "3.1.0", 

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

135 "paths": { 

136 "/heroes/": { 

137 "get": { 

138 "summary": "Read Heroes", 

139 "operationId": "read_heroes_heroes__get", 

140 "parameters": [ 

141 { 

142 "required": False, 

143 "schema": { 

144 "title": "Offset", 

145 "type": "integer", 

146 "default": 0, 

147 }, 

148 "name": "offset", 

149 "in": "query", 

150 }, 

151 { 

152 "required": False, 

153 "schema": { 

154 "title": "Limit", 

155 "maximum": 100, 

156 "type": "integer", 

157 "default": 100, 

158 }, 

159 "name": "limit", 

160 "in": "query", 

161 }, 

162 ], 

163 "responses": { 

164 "200": { 

165 "description": "Successful Response", 

166 "content": { 

167 "application/json": { 

168 "schema": { 

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

170 "type": "array", 

171 "items": { 

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

173 }, 

174 } 

175 } 

176 }, 

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 }, 

190 "post": { 

191 "summary": "Create Hero", 

192 "operationId": "create_hero_heroes__post", 

193 "requestBody": { 

194 "content": { 

195 "application/json": { 

196 "schema": { 

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

198 } 

199 } 

200 }, 

201 "required": True, 

202 }, 

203 "responses": { 

204 "200": { 

205 "description": "Successful Response", 

206 "content": { 

207 "application/json": { 

208 "schema": { 

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

210 } 

211 } 

212 }, 

213 }, 

214 "422": { 

215 "description": "Validation Error", 

216 "content": { 

217 "application/json": { 

218 "schema": { 

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

220 } 

221 } 

222 }, 

223 }, 

224 }, 

225 }, 

226 }, 

227 "/heroes/{hero_id}": { 

228 "get": { 

229 "summary": "Read Hero", 

230 "operationId": "read_hero_heroes__hero_id__get", 

231 "parameters": [ 

232 { 

233 "required": True, 

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

235 "name": "hero_id", 

236 "in": "path", 

237 } 

238 ], 

239 "responses": { 

240 "200": { 

241 "description": "Successful Response", 

242 "content": { 

243 "application/json": { 

244 "schema": { 

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

246 } 

247 } 

248 }, 

249 }, 

250 "422": { 

251 "description": "Validation Error", 

252 "content": { 

253 "application/json": { 

254 "schema": { 

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

256 } 

257 } 

258 }, 

259 }, 

260 }, 

261 }, 

262 "patch": { 

263 "summary": "Update Hero", 

264 "operationId": "update_hero_heroes__hero_id__patch", 

265 "parameters": [ 

266 { 

267 "required": True, 

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

269 "name": "hero_id", 

270 "in": "path", 

271 } 

272 ], 

273 "requestBody": { 

274 "content": { 

275 "application/json": { 

276 "schema": { 

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

278 } 

279 } 

280 }, 

281 "required": True, 

282 }, 

283 "responses": { 

284 "200": { 

285 "description": "Successful Response", 

286 "content": { 

287 "application/json": { 

288 "schema": { 

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

290 } 

291 } 

292 }, 

293 }, 

294 "422": { 

295 "description": "Validation Error", 

296 "content": { 

297 "application/json": { 

298 "schema": { 

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

300 } 

301 } 

302 }, 

303 }, 

304 }, 

305 }, 

306 }, 

307 }, 

308 "components": { 

309 "schemas": { 

310 "HTTPValidationError": { 

311 "title": "HTTPValidationError", 

312 "type": "object", 

313 "properties": { 

314 "detail": { 

315 "title": "Detail", 

316 "type": "array", 

317 "items": { 

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

319 }, 

320 } 

321 }, 

322 }, 

323 "HeroCreate": { 

324 "title": "HeroCreate", 

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

326 "type": "object", 

327 "properties": { 

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

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

330 "age": IsDict( 

331 { 

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

333 "title": "Age", 

334 } 

335 ) 

336 | IsDict( 

337 # TODO: Remove when deprecating Pydantic v1 

338 {"title": "Age", "type": "integer"} 

339 ), 

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

341 }, 

342 }, 

343 "HeroPublic": { 

344 "title": "HeroPublic", 

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

346 "type": "object", 

347 "properties": { 

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

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

350 "age": IsDict( 

351 { 

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

353 "title": "Age", 

354 } 

355 ) 

356 | IsDict( 

357 # TODO: Remove when deprecating Pydantic v1 

358 {"title": "Age", "type": "integer"} 

359 ), 

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

361 }, 

362 }, 

363 "HeroUpdate": { 

364 "title": "HeroUpdate", 

365 "type": "object", 

366 "properties": { 

367 "name": IsDict( 

368 { 

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

370 "title": "Name", 

371 } 

372 ) 

373 | IsDict( 

374 # TODO: Remove when deprecating Pydantic v1 

375 {"title": "Name", "type": "string"} 

376 ), 

377 "secret_name": IsDict( 

378 { 

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

380 "title": "Secret Name", 

381 } 

382 ) 

383 | IsDict( 

384 # TODO: Remove when deprecating Pydantic v1 

385 {"title": "Secret Name", "type": "string"} 

386 ), 

387 "age": IsDict( 

388 { 

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

390 "title": "Age", 

391 } 

392 ) 

393 | IsDict( 

394 # TODO: Remove when deprecating Pydantic v1 

395 {"title": "Age", "type": "integer"} 

396 ), 

397 "password": IsDict( 

398 { 

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

400 "title": "Password", 

401 } 

402 ) 

403 | IsDict( 

404 # TODO: Remove when deprecating Pydantic v1 

405 {"title": "Password", "type": "string"} 

406 ), 

407 }, 

408 }, 

409 "ValidationError": { 

410 "title": "ValidationError", 

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

412 "type": "object", 

413 "properties": { 

414 "loc": { 

415 "title": "Location", 

416 "type": "array", 

417 "items": { 

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

419 }, 

420 }, 

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

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

423 }, 

424 }, 

425 } 

426 }, 

427 }