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

96 statements  

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

1from dirty_equals import IsDict 1idefgh

2from fastapi.testclient import TestClient 1idefgh

3from sqlmodel import Session, create_engine 1idefgh

4from sqlmodel.pool import StaticPool 1idefgh

5 

6from ....conftest import needs_py310 1idefgh

7 

8 

9@needs_py310 1idefgh

10def test_tutorial(clear_sqlmodel): 1defgh

11 from docs_src.tutorial.fastapi.update import tutorial002_py310 as mod 1abc

12 

13 mod.sqlite_url = "sqlite://" 1abc

14 mod.engine = create_engine( 1abc

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

16 ) 

17 

18 with TestClient(mod.app) as client: 1abc

19 hero1_data = { 1abc

20 "name": "Deadpond", 

21 "secret_name": "Dive Wilson", 

22 "password": "chimichanga", 

23 } 

24 hero2_data = { 1abc

25 "name": "Spider-Boy", 

26 "secret_name": "Pedro Parqueador", 

27 "id": 9000, 

28 "password": "auntmay", 

29 } 

30 hero3_data = { 1abc

31 "name": "Rusty-Man", 

32 "secret_name": "Tommy Sharp", 

33 "age": 48, 

34 "password": "bestpreventer", 

35 } 

36 response = client.post("/heroes/", json=hero1_data) 1abc

37 assert response.status_code == 200, response.text 1abc

38 hero1 = response.json() 1abc

39 assert "password" not in hero1 1abc

40 assert "hashed_password" not in hero1 1abc

41 hero1_id = hero1["id"] 1abc

42 response = client.post("/heroes/", json=hero2_data) 1abc

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

44 hero2 = response.json() 1abc

45 hero2_id = hero2["id"] 1abc

46 response = client.post("/heroes/", json=hero3_data) 1abc

47 assert response.status_code == 200, response.text 1abc

48 hero3 = response.json() 1abc

49 hero3_id = hero3["id"] 1abc

50 response = client.get(f"/heroes/{hero2_id}") 1abc

51 assert response.status_code == 200, response.text 1abc

52 fetched_hero2 = response.json() 1abc

53 assert "password" not in fetched_hero2 1abc

54 assert "hashed_password" not in fetched_hero2 1abc

55 response = client.get("/heroes/9000") 1abc

56 assert response.status_code == 404, response.text 1abc

57 response = client.get("/heroes/") 1abc

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

59 data = response.json() 1abc

60 assert len(data) == 3 1abc

61 for response_hero in data: 1abc

62 assert "password" not in response_hero 1abc

63 assert "hashed_password" not in response_hero 1abc

64 

65 # Test hashed passwords 

66 with Session(mod.engine) as session: 1abc

67 hero1_db = session.get(mod.Hero, hero1_id) 1abc

68 assert hero1_db 1abc

69 assert not hasattr(hero1_db, "password") 1abc

70 assert hero1_db.hashed_password == "not really hashed chimichanga hehehe" 1abc

71 hero2_db = session.get(mod.Hero, hero2_id) 1abc

72 assert hero2_db 1abc

73 assert not hasattr(hero2_db, "password") 1abc

74 assert hero2_db.hashed_password == "not really hashed auntmay hehehe" 1abc

75 hero3_db = session.get(mod.Hero, hero3_id) 1abc

76 assert hero3_db 1abc

77 assert not hasattr(hero3_db, "password") 1abc

78 assert hero3_db.hashed_password == "not really hashed bestpreventer hehehe" 1abc

79 

80 response = client.patch( 1abc

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

82 ) 

83 data = response.json() 1abc

84 assert response.status_code == 200, response.text 1abc

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

86 assert ( 1abc

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

88 ), "The secret name should be updated" 

89 assert "password" not in data 1abc

90 assert "hashed_password" not in data 1abc

91 with Session(mod.engine) as session: 1abc

92 hero2b_db = session.get(mod.Hero, hero2_id) 1abc

93 assert hero2b_db 1abc

94 assert not hasattr(hero2b_db, "password") 1abc

95 assert hero2b_db.hashed_password == "not really hashed auntmay hehehe" 1abc

96 

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

98 data = response.json() 1abc

99 assert response.status_code == 200, response.text 1abc

100 assert data["name"] == hero3_data["name"] 1abc

101 assert ( 1abc

102 data["age"] is None 

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

104 assert "password" not in data 1abc

105 assert "hashed_password" not in data 1abc

106 with Session(mod.engine) as session: 1abc

107 hero3b_db = session.get(mod.Hero, hero3_id) 1abc

108 assert hero3b_db 1abc

109 assert not hasattr(hero3b_db, "password") 1abc

110 assert hero3b_db.hashed_password == "not really hashed bestpreventer hehehe" 1abc

111 

112 # Test update dict, hashed_password 

113 response = client.patch( 1abc

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

115 ) 

116 data = response.json() 1abc

117 assert response.status_code == 200, response.text 1abc

118 assert data["name"] == hero3_data["name"] 1abc

119 assert data["age"] is None 1abc

120 assert "password" not in data 1abc

121 assert "hashed_password" not in data 1abc

122 with Session(mod.engine) as session: 1abc

123 hero3b_db = session.get(mod.Hero, hero3_id) 1abc

124 assert hero3b_db 1abc

125 assert not hasattr(hero3b_db, "password") 1abc

126 assert ( 1abc

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

128 ) 

129 

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

131 assert response.status_code == 404, response.text 1abc

132 

133 response = client.get("/openapi.json") 1abc

134 assert response.status_code == 200, response.text 1abc

135 assert response.json() == { 1abc

136 "openapi": "3.1.0", 

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

138 "paths": { 

139 "/heroes/": { 

140 "get": { 

141 "summary": "Read Heroes", 

142 "operationId": "read_heroes_heroes__get", 

143 "parameters": [ 

144 { 

145 "required": False, 

146 "schema": { 

147 "title": "Offset", 

148 "type": "integer", 

149 "default": 0, 

150 }, 

151 "name": "offset", 

152 "in": "query", 

153 }, 

154 { 

155 "required": False, 

156 "schema": { 

157 "title": "Limit", 

158 "maximum": 100, 

159 "type": "integer", 

160 "default": 100, 

161 }, 

162 "name": "limit", 

163 "in": "query", 

164 }, 

165 ], 

166 "responses": { 

167 "200": { 

168 "description": "Successful Response", 

169 "content": { 

170 "application/json": { 

171 "schema": { 

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

173 "type": "array", 

174 "items": { 

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

176 }, 

177 } 

178 } 

179 }, 

180 }, 

181 "422": { 

182 "description": "Validation Error", 

183 "content": { 

184 "application/json": { 

185 "schema": { 

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

187 } 

188 } 

189 }, 

190 }, 

191 }, 

192 }, 

193 "post": { 

194 "summary": "Create Hero", 

195 "operationId": "create_hero_heroes__post", 

196 "requestBody": { 

197 "content": { 

198 "application/json": { 

199 "schema": { 

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

201 } 

202 } 

203 }, 

204 "required": True, 

205 }, 

206 "responses": { 

207 "200": { 

208 "description": "Successful Response", 

209 "content": { 

210 "application/json": { 

211 "schema": { 

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

213 } 

214 } 

215 }, 

216 }, 

217 "422": { 

218 "description": "Validation Error", 

219 "content": { 

220 "application/json": { 

221 "schema": { 

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

223 } 

224 } 

225 }, 

226 }, 

227 }, 

228 }, 

229 }, 

230 "/heroes/{hero_id}": { 

231 "get": { 

232 "summary": "Read Hero", 

233 "operationId": "read_hero_heroes__hero_id__get", 

234 "parameters": [ 

235 { 

236 "required": True, 

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

238 "name": "hero_id", 

239 "in": "path", 

240 } 

241 ], 

242 "responses": { 

243 "200": { 

244 "description": "Successful Response", 

245 "content": { 

246 "application/json": { 

247 "schema": { 

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

249 } 

250 } 

251 }, 

252 }, 

253 "422": { 

254 "description": "Validation Error", 

255 "content": { 

256 "application/json": { 

257 "schema": { 

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

259 } 

260 } 

261 }, 

262 }, 

263 }, 

264 }, 

265 "patch": { 

266 "summary": "Update Hero", 

267 "operationId": "update_hero_heroes__hero_id__patch", 

268 "parameters": [ 

269 { 

270 "required": True, 

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

272 "name": "hero_id", 

273 "in": "path", 

274 } 

275 ], 

276 "requestBody": { 

277 "content": { 

278 "application/json": { 

279 "schema": { 

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

281 } 

282 } 

283 }, 

284 "required": True, 

285 }, 

286 "responses": { 

287 "200": { 

288 "description": "Successful Response", 

289 "content": { 

290 "application/json": { 

291 "schema": { 

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

293 } 

294 } 

295 }, 

296 }, 

297 "422": { 

298 "description": "Validation Error", 

299 "content": { 

300 "application/json": { 

301 "schema": { 

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

303 } 

304 } 

305 }, 

306 }, 

307 }, 

308 }, 

309 }, 

310 }, 

311 "components": { 

312 "schemas": { 

313 "HTTPValidationError": { 

314 "title": "HTTPValidationError", 

315 "type": "object", 

316 "properties": { 

317 "detail": { 

318 "title": "Detail", 

319 "type": "array", 

320 "items": { 

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

322 }, 

323 } 

324 }, 

325 }, 

326 "HeroCreate": { 

327 "title": "HeroCreate", 

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

329 "type": "object", 

330 "properties": { 

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

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

333 "age": IsDict( 

334 { 

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

336 "title": "Age", 

337 } 

338 ) 

339 | IsDict( 

340 # TODO: Remove when deprecating Pydantic v1 

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

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": IsDict( 

354 { 

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

356 "title": "Age", 

357 } 

358 ) 

359 | IsDict( 

360 # TODO: Remove when deprecating Pydantic v1 

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

362 ), 

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

364 }, 

365 }, 

366 "HeroUpdate": { 

367 "title": "HeroUpdate", 

368 "type": "object", 

369 "properties": { 

370 "name": IsDict( 

371 { 

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

373 "title": "Name", 

374 } 

375 ) 

376 | IsDict( 

377 # TODO: Remove when deprecating Pydantic v1 

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

379 ), 

380 "secret_name": IsDict( 

381 { 

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

383 "title": "Secret Name", 

384 } 

385 ) 

386 | IsDict( 

387 # TODO: Remove when deprecating Pydantic v1 

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

389 ), 

390 "age": IsDict( 

391 { 

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

393 "title": "Age", 

394 } 

395 ) 

396 | IsDict( 

397 # TODO: Remove when deprecating Pydantic v1 

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

399 ), 

400 "password": IsDict( 

401 { 

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

403 "title": "Password", 

404 } 

405 ) 

406 | IsDict( 

407 # TODO: Remove when deprecating Pydantic v1 

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

409 ), 

410 }, 

411 }, 

412 "ValidationError": { 

413 "title": "ValidationError", 

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

415 "type": "object", 

416 "properties": { 

417 "loc": { 

418 "title": "Location", 

419 "type": "array", 

420 "items": { 

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

422 }, 

423 }, 

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

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

426 }, 

427 }, 

428 } 

429 }, 

430 }