Coverage for tests / test_tutorial / test_fastapi / test_update / test_tutorial002.py: 100%
99 statements
« prev ^ index » next coverage.py v7.13.0, created at 2026-11-09 00:02 +0000
« prev ^ index » next coverage.py v7.13.0, created at 2026-11-09 00:02 +0000
1import importlib 1jklmnopqr
2from types import ModuleType 1jklmnopqr
4import pytest 1jklmnopqr
5from dirty_equals import IsOneOf 1jklmnopqr
6from fastapi.testclient import TestClient 1jklmnopqr
7from sqlalchemy import Engine 1jklmnopqr
8from sqlmodel import Session 1jklmnopqr
11@pytest.fixture( 1jklmnopqr
12 name="module",
13 params=[
14 pytest.param("tutorial002_py310"),
15 ],
16)
17def get_module(request: pytest.FixtureRequest, database_engine: Engine) -> ModuleType: 1jklmnopqr
18 mod = importlib.import_module(f"docs_src.tutorial.fastapi.update.{request.param}") 1jklmnopqr
19 mod.engine = database_engine 1jklmnopqr
20 return mod 1jklmnopqr
23def test_tutorial(module: ModuleType): 1jklmnopqr
24 with TestClient(module.app) as client: 1abcdefghi
25 hero1_data = { 1abcdefghi
26 "name": "Deadpond",
27 "secret_name": "Dive Wilson",
28 "password": "chimichanga",
29 }
30 hero2_data = { 1abcdefghi
31 "name": "Spider-Boy",
32 "secret_name": "Pedro Parqueador",
33 "id": 9000,
34 "password": "auntmay",
35 }
36 hero3_data = { 1abcdefghi
37 "name": "Rusty-Man",
38 "secret_name": "Tommy Sharp",
39 "age": 48,
40 "password": "bestpreventer",
41 }
42 response = client.post("/heroes/", json=hero1_data) 1abcdefghi
43 assert response.status_code == 200, response.text 1abcdefghi
44 hero1 = response.json() 1abcdefghi
45 assert "password" not in hero1 1abcdefghi
46 assert "hashed_password" not in hero1 1abcdefghi
47 hero1_id = hero1["id"] 1abcdefghi
48 response = client.post("/heroes/", json=hero2_data) 1abcdefghi
49 assert response.status_code == 200, response.text 1abcdefghi
50 hero2 = response.json() 1abcdefghi
51 hero2_id = hero2["id"] 1abcdefghi
52 response = client.post("/heroes/", json=hero3_data) 1abcdefghi
53 assert response.status_code == 200, response.text 1abcdefghi
54 hero3 = response.json() 1abcdefghi
55 hero3_id = hero3["id"] 1abcdefghi
56 response = client.get(f"/heroes/{hero2_id}") 1abcdefghi
57 assert response.status_code == 200, response.text 1abcdefghi
58 fetched_hero2 = response.json() 1abcdefghi
59 assert "password" not in fetched_hero2 1abcdefghi
60 assert "hashed_password" not in fetched_hero2 1abcdefghi
61 response = client.get("/heroes/9000") 1abcdefghi
62 assert response.status_code == 404, response.text 1abcdefghi
63 response = client.get("/heroes/") 1abcdefghi
64 assert response.status_code == 200, response.text 1abcdefghi
65 data = response.json() 1abcdefghi
66 assert len(data) == 3 1abcdefghi
67 for response_hero in data: 1abcdefghi
68 assert "password" not in response_hero 1abcdefghi
69 assert "hashed_password" not in response_hero 1abcdefghi
71 # Test hashed passwords
72 with Session(module.engine) as session: 1abcdefghi
73 hero1_db = session.get(module.Hero, hero1_id) 1abcdefghi
74 assert hero1_db 1abcdefghi
75 assert not hasattr(hero1_db, "password") 1abcdefghi
76 assert hero1_db.hashed_password == "not really hashed chimichanga hehehe" 1abcdefghi
77 hero2_db = session.get(module.Hero, hero2_id) 1abcdefghi
78 assert hero2_db 1abcdefghi
79 assert not hasattr(hero2_db, "password") 1abcdefghi
80 assert hero2_db.hashed_password == "not really hashed auntmay hehehe" 1abcdefghi
81 hero3_db = session.get(module.Hero, hero3_id) 1abcdefghi
82 assert hero3_db 1abcdefghi
83 assert not hasattr(hero3_db, "password") 1abcdefghi
84 assert hero3_db.hashed_password == "not really hashed bestpreventer hehehe" 1abcdefghi
86 response = client.patch( 1abcdefghi
87 f"/heroes/{hero2_id}", json={"secret_name": "Spider-Youngster"}
88 )
89 data = response.json() 1abcdefghi
90 assert response.status_code == 200, response.text 1abcdefghi
91 assert data["name"] == hero2_data["name"], "The name should not be set to none" 1abcdefghi
92 assert data["secret_name"] == "Spider-Youngster", ( 1abcdefghi
93 "The secret name should be updated"
94 )
95 assert "password" not in data 1abcdefghi
96 assert "hashed_password" not in data 1abcdefghi
97 with Session(module.engine) as session: 1abcdefghi
98 hero2b_db = session.get(module.Hero, hero2_id) 1abcdefghi
99 assert hero2b_db 1abcdefghi
100 assert not hasattr(hero2b_db, "password") 1abcdefghi
101 assert hero2b_db.hashed_password == "not really hashed auntmay hehehe" 1abcdefghi
103 response = client.patch(f"/heroes/{hero3_id}", json={"age": None}) 1abcdefghi
104 data = response.json() 1abcdefghi
105 assert response.status_code == 200, response.text 1abcdefghi
106 assert data["name"] == hero3_data["name"] 1abcdefghi
107 assert data["age"] is None, ( 1abcdefghi
108 "A field should be updatable to None, even if that's the default"
109 )
110 assert "password" not in data 1abcdefghi
111 assert "hashed_password" not in data 1abcdefghi
112 with Session(module.engine) as session: 1abcdefghi
113 hero3b_db = session.get(module.Hero, hero3_id) 1abcdefghi
114 assert hero3b_db 1abcdefghi
115 assert not hasattr(hero3b_db, "password") 1abcdefghi
116 assert hero3b_db.hashed_password == "not really hashed bestpreventer hehehe" 1abcdefghi
118 # Test update dict, hashed_password
119 response = client.patch( 1abcdefghi
120 f"/heroes/{hero3_id}", json={"password": "philantroplayboy"}
121 )
122 data = response.json() 1abcdefghi
123 assert response.status_code == 200, response.text 1abcdefghi
124 assert data["name"] == hero3_data["name"] 1abcdefghi
125 assert data["age"] is None 1abcdefghi
126 assert "password" not in data 1abcdefghi
127 assert "hashed_password" not in data 1abcdefghi
128 with Session(module.engine) as session: 1abcdefghi
129 hero3b_db = session.get(module.Hero, hero3_id) 1abcdefghi
130 assert hero3b_db 1abcdefghi
131 assert not hasattr(hero3b_db, "password") 1abcdefghi
132 assert ( 1abcdefghi
133 hero3b_db.hashed_password == "not really hashed philantroplayboy hehehe"
134 )
136 response = client.patch("/heroes/9001", json={"name": "Dragon Cube X"}) 1abcdefghi
137 assert response.status_code == 404, response.text 1abcdefghi
139 response = client.get("/openapi.json") 1abcdefghi
140 assert response.status_code == 200, response.text 1abcdefghi
141 assert response.json() == { 1abcdefghi
142 "openapi": "3.1.0",
143 "info": {"title": "FastAPI", "version": "0.1.0"},
144 "paths": {
145 "/heroes/": {
146 "get": {
147 "summary": "Read Heroes",
148 "operationId": "read_heroes_heroes__get",
149 "parameters": [
150 {
151 "required": False,
152 "schema": {
153 "title": "Offset",
154 "type": "integer",
155 "default": 0,
156 },
157 "name": "offset",
158 "in": "query",
159 },
160 {
161 "required": False,
162 "schema": {
163 "title": "Limit",
164 "maximum": 100,
165 "type": "integer",
166 "default": 100,
167 },
168 "name": "limit",
169 "in": "query",
170 },
171 ],
172 "responses": {
173 "200": {
174 "description": "Successful Response",
175 "content": {
176 "application/json": {
177 "schema": {
178 "title": "Response Read Heroes Heroes Get",
179 "type": "array",
180 "items": {
181 "$ref": "#/components/schemas/HeroPublic"
182 },
183 }
184 }
185 },
186 },
187 "422": {
188 "description": "Validation Error",
189 "content": {
190 "application/json": {
191 "schema": {
192 "$ref": "#/components/schemas/HTTPValidationError"
193 }
194 }
195 },
196 },
197 },
198 },
199 "post": {
200 "summary": "Create Hero",
201 "operationId": "create_hero_heroes__post",
202 "requestBody": {
203 "content": {
204 "application/json": {
205 "schema": {
206 "$ref": "#/components/schemas/HeroCreate"
207 }
208 }
209 },
210 "required": True,
211 },
212 "responses": {
213 "200": {
214 "description": "Successful Response",
215 "content": {
216 "application/json": {
217 "schema": {
218 "$ref": "#/components/schemas/HeroPublic"
219 }
220 }
221 },
222 },
223 "422": {
224 "description": "Validation Error",
225 "content": {
226 "application/json": {
227 "schema": {
228 "$ref": "#/components/schemas/HTTPValidationError"
229 }
230 }
231 },
232 },
233 },
234 },
235 },
236 "/heroes/{hero_id}": {
237 "get": {
238 "summary": "Read Hero",
239 "operationId": "read_hero_heroes__hero_id__get",
240 "parameters": [
241 {
242 "required": True,
243 "schema": {"title": "Hero Id", "type": "integer"},
244 "name": "hero_id",
245 "in": "path",
246 }
247 ],
248 "responses": {
249 "200": {
250 "description": "Successful Response",
251 "content": {
252 "application/json": {
253 "schema": {
254 "$ref": "#/components/schemas/HeroPublic"
255 }
256 }
257 },
258 },
259 "422": {
260 "description": "Validation Error",
261 "content": {
262 "application/json": {
263 "schema": {
264 "$ref": "#/components/schemas/HTTPValidationError"
265 }
266 }
267 },
268 },
269 },
270 },
271 "patch": {
272 "summary": "Update Hero",
273 "operationId": "update_hero_heroes__hero_id__patch",
274 "parameters": [
275 {
276 "required": True,
277 "schema": {"title": "Hero Id", "type": "integer"},
278 "name": "hero_id",
279 "in": "path",
280 }
281 ],
282 "requestBody": {
283 "content": {
284 "application/json": {
285 "schema": {
286 "$ref": "#/components/schemas/HeroUpdate"
287 }
288 }
289 },
290 "required": True,
291 },
292 "responses": {
293 "200": {
294 "description": "Successful Response",
295 "content": {
296 "application/json": {
297 "schema": {
298 "$ref": "#/components/schemas/HeroPublic"
299 }
300 }
301 },
302 },
303 "422": {
304 "description": "Validation Error",
305 "content": {
306 "application/json": {
307 "schema": {
308 "$ref": "#/components/schemas/HTTPValidationError"
309 }
310 }
311 },
312 },
313 },
314 },
315 },
316 },
317 "components": {
318 "schemas": {
319 "HTTPValidationError": {
320 "title": "HTTPValidationError",
321 "type": "object",
322 "properties": {
323 "detail": {
324 "title": "Detail",
325 "type": "array",
326 "items": {
327 "$ref": "#/components/schemas/ValidationError"
328 },
329 }
330 },
331 },
332 "HeroCreate": {
333 "title": "HeroCreate",
334 "required": ["name", "secret_name", "password"],
335 "type": "object",
336 "properties": {
337 "name": {"title": "Name", "type": "string"},
338 "secret_name": {"title": "Secret Name", "type": "string"},
339 "age": {
340 "anyOf": [{"type": "integer"}, {"type": "null"}],
341 "title": "Age",
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": {
354 "anyOf": [{"type": "integer"}, {"type": "null"}],
355 "title": "Age",
356 },
357 "id": {"title": "Id", "type": "integer"},
358 },
359 },
360 "HeroUpdate": {
361 "title": "HeroUpdate",
362 "type": "object",
363 "properties": {
364 "name": {
365 "anyOf": [{"type": "string"}, {"type": "null"}],
366 "title": "Name",
367 },
368 "secret_name": {
369 "anyOf": [{"type": "string"}, {"type": "null"}],
370 "title": "Secret Name",
371 },
372 "age": {
373 "anyOf": [{"type": "integer"}, {"type": "null"}],
374 "title": "Age",
375 },
376 "password": {
377 "anyOf": [{"type": "string"}, {"type": "null"}],
378 "title": "Password",
379 },
380 },
381 },
382 "ValidationError": {
383 "title": "ValidationError",
384 "required": ["loc", "msg", "type"],
385 "type": "object",
386 "properties": IsOneOf(
387 {
388 "loc": {
389 "title": "Location",
390 "type": "array",
391 "items": {
392 "anyOf": [
393 {"type": "string"},
394 {"type": "integer"},
395 ]
396 },
397 },
398 "msg": {"title": "Message", "type": "string"},
399 "type": {"title": "Error Type", "type": "string"},
400 },
401 {
402 "loc": {
403 "title": "Location",
404 "type": "array",
405 "items": {
406 "anyOf": [
407 {"type": "string"},
408 {"type": "integer"},
409 ]
410 },
411 },
412 "msg": {"title": "Message", "type": "string"},
413 "type": {"title": "Error Type", "type": "string"},
414 "ctx": {"title": "Context", "type": "object"},
415 "input": {"title": "Input"},
416 },
417 ),
418 },
419 }
420 },
421 }