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