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
« 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
7def test_tutorial(clear_sqlmodel): 1ghijkl
8 from docs_src.tutorial.fastapi.update import tutorial002 as mod 1fabcde
10 mod.sqlite_url = "sqlite://" 1fabcde
11 mod.engine = create_engine( 1fabcde
12 mod.sqlite_url, connect_args=mod.connect_args, poolclass=StaticPool
13 )
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
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
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
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
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 )
127 response = client.patch("/heroes/9001", json={"name": "Dragon Cube X"}) 1fabcde
128 assert response.status_code == 404, response.text 1fabcde
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 }