Coverage for tests / test_tutorial / test_security / test_tutorial004.py: 100%
102 statements
« prev ^ index » next coverage.py v7.13.3, created at 2026-02-12 18:15 +0000
« prev ^ index » next coverage.py v7.13.3, created at 2026-02-12 18:15 +0000
1import importlib 1abdc
2from types import ModuleType 1abdc
3from unittest.mock import patch 1abdc
5import pytest 1abdc
6from fastapi.testclient import TestClient 1abdc
7from inline_snapshot import snapshot 1abdc
9from ...utils import needs_py310 1abdc
12@pytest.fixture( 1abdc
13 name="mod",
14 params=[
15 pytest.param("tutorial004_py310", marks=needs_py310),
16 pytest.param("tutorial004_an_py310", marks=needs_py310),
17 ],
18)
19def get_mod(request: pytest.FixtureRequest): 1abdc
20 mod = importlib.import_module(f"docs_src.security.{request.param}") 1abc
22 return mod 1abc
25def get_access_token(*, username="johndoe", password="secret", client: TestClient): 1abdc
26 data = {"username": username, "password": password} 1hiejkflmg
27 response = client.post("/token", data=data) 1hiejkflmg
28 content = response.json() 1hiejkflmg
29 access_token = content.get("access_token") 1hiejkflmg
30 return access_token 1hiejkflmg
33def test_login(mod: ModuleType): 1abdc
34 client = TestClient(mod.app) 1nop
35 response = client.post("/token", data={"username": "johndoe", "password": "secret"}) 1nop
36 assert response.status_code == 200, response.text 1nop
37 content = response.json() 1nop
38 assert "access_token" in content 1nop
39 assert content["token_type"] == "bearer" 1nop
42def test_login_incorrect_password(mod: ModuleType): 1abdc
43 client = TestClient(mod.app) 1IJK
44 response = client.post( 1IJK
45 "/token", data={"username": "johndoe", "password": "incorrect"}
46 )
47 assert response.status_code == 401, response.text 1IJK
48 assert response.json() == {"detail": "Incorrect username or password"} 1IJK
51def test_login_incorrect_username(mod: ModuleType): 1abdc
52 client = TestClient(mod.app) 1LMN
53 response = client.post("/token", data={"username": "foo", "password": "secret"}) 1LMN
54 assert response.status_code == 401, response.text 1LMN
55 assert response.json() == {"detail": "Incorrect username or password"} 1LMN
58def test_no_token(mod: ModuleType): 1abdc
59 client = TestClient(mod.app) 1qrs
60 response = client.get("/users/me") 1qrs
61 assert response.status_code == 401, response.text 1qrs
62 assert response.json() == {"detail": "Not authenticated"} 1qrs
63 assert response.headers["WWW-Authenticate"] == "Bearer" 1qrs
66def test_token(mod: ModuleType): 1abdc
67 client = TestClient(mod.app) 1ikm
68 access_token = get_access_token(client=client) 1ikm
69 response = client.get( 1ikm
70 "/users/me", headers={"Authorization": f"Bearer {access_token}"}
71 )
72 assert response.status_code == 200, response.text 1ikm
73 assert response.json() == { 1ikm
74 "username": "johndoe",
75 "full_name": "John Doe",
76 "email": "johndoe@example.com",
77 "disabled": False,
78 }
81def test_incorrect_token(mod: ModuleType): 1abdc
82 client = TestClient(mod.app) 1tuv
83 response = client.get("/users/me", headers={"Authorization": "Bearer nonexistent"}) 1tuv
84 assert response.status_code == 401, response.text 1tuv
85 assert response.json() == {"detail": "Could not validate credentials"} 1tuv
86 assert response.headers["WWW-Authenticate"] == "Bearer" 1tuv
89def test_incorrect_token_type(mod: ModuleType): 1abdc
90 client = TestClient(mod.app) 1wxy
91 response = client.get( 1wxy
92 "/users/me", headers={"Authorization": "Notexistent testtoken"}
93 )
94 assert response.status_code == 401, response.text 1wxy
95 assert response.json() == {"detail": "Not authenticated"} 1wxy
96 assert response.headers["WWW-Authenticate"] == "Bearer" 1wxy
99def test_verify_password(mod: ModuleType): 1abdc
100 assert mod.verify_password( 1UVW
101 "secret", mod.fake_users_db["johndoe"]["hashed_password"]
102 )
105def test_get_password_hash(mod: ModuleType): 1abdc
106 assert mod.get_password_hash("johndoe") 1XYZ
109def test_create_access_token(mod: ModuleType): 1abdc
110 access_token = mod.create_access_token(data={"data": "foo"}) 1RST
111 assert access_token 1RST
114def test_token_no_sub(mod: ModuleType): 1abdc
115 client = TestClient(mod.app) 1zAB
117 response = client.get( 1zAB
118 "/users/me",
119 headers={
120 "Authorization": "Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJkYXRhIjoiZm9vIn0.9ynBhuYb4e6aW3oJr_K_TBgwcMTDpRToQIE25L57rOE"
121 },
122 )
123 assert response.status_code == 401, response.text 1zAB
124 assert response.json() == {"detail": "Could not validate credentials"} 1zAB
125 assert response.headers["WWW-Authenticate"] == "Bearer" 1zAB
128def test_token_no_username(mod: ModuleType): 1abdc
129 client = TestClient(mod.app) 1CDE
131 response = client.get( 1CDE
132 "/users/me",
133 headers={
134 "Authorization": "Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJmb28ifQ.NnExK_dlNAYyzACrXtXDrcWOgGY2JuPbI4eDaHdfK5Y"
135 },
136 )
137 assert response.status_code == 401, response.text 1CDE
138 assert response.json() == {"detail": "Could not validate credentials"} 1CDE
139 assert response.headers["WWW-Authenticate"] == "Bearer" 1CDE
142def test_token_nonexistent_user(mod: ModuleType): 1abdc
143 client = TestClient(mod.app) 1FGH
145 response = client.get( 1FGH
146 "/users/me",
147 headers={
148 "Authorization": "Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ1c2VybmFtZTpib2IifQ.HcfCW67Uda-0gz54ZWTqmtgJnZeNem0Q757eTa9EZuw"
149 },
150 )
151 assert response.status_code == 401, response.text 1FGH
152 assert response.json() == {"detail": "Could not validate credentials"} 1FGH
153 assert response.headers["WWW-Authenticate"] == "Bearer" 1FGH
156def test_token_inactive_user(mod: ModuleType): 1abdc
157 client = TestClient(mod.app) 1efg
158 alice_user_data = { 1efg
159 "username": "alice",
160 "full_name": "Alice Wonderson",
161 "email": "alice@example.com",
162 "hashed_password": mod.get_password_hash("secretalice"),
163 "disabled": True,
164 }
165 with patch.dict(f"{mod.__name__}.fake_users_db", {"alice": alice_user_data}): 1efg
166 access_token = get_access_token( 1efg
167 username="alice", password="secretalice", client=client
168 )
169 response = client.get( 1efg
170 "/users/me", headers={"Authorization": f"Bearer {access_token}"}
171 )
172 assert response.status_code == 400, response.text 1efg
173 assert response.json() == {"detail": "Inactive user"} 1efg
176def test_read_items(mod: ModuleType): 1abdc
177 client = TestClient(mod.app) 1hjl
178 access_token = get_access_token(client=client) 1hjl
179 response = client.get( 1hjl
180 "/users/me/items/", headers={"Authorization": f"Bearer {access_token}"}
181 )
182 assert response.status_code == 200, response.text 1hjl
183 assert response.json() == [{"item_id": "Foo", "owner": "johndoe"}] 1hjl
186def test_openapi_schema(mod: ModuleType): 1abdc
187 client = TestClient(mod.app) 1OPQ
188 response = client.get("/openapi.json") 1OPQ
189 assert response.status_code == 200, response.text 1OPQ
190 assert response.json() == snapshot( 1OPQ
191 {
192 "openapi": "3.1.0",
193 "info": {"title": "FastAPI", "version": "0.1.0"},
194 "paths": {
195 "/token": {
196 "post": {
197 "responses": {
198 "200": {
199 "description": "Successful Response",
200 "content": {
201 "application/json": {
202 "schema": {"$ref": "#/components/schemas/Token"}
203 }
204 },
205 },
206 "422": {
207 "description": "Validation Error",
208 "content": {
209 "application/json": {
210 "schema": {
211 "$ref": "#/components/schemas/HTTPValidationError"
212 }
213 }
214 },
215 },
216 },
217 "summary": "Login For Access Token",
218 "operationId": "login_for_access_token_token_post",
219 "requestBody": {
220 "content": {
221 "application/x-www-form-urlencoded": {
222 "schema": {
223 "$ref": "#/components/schemas/Body_login_for_access_token_token_post"
224 }
225 }
226 },
227 "required": True,
228 },
229 }
230 },
231 "/users/me/": {
232 "get": {
233 "responses": {
234 "200": {
235 "description": "Successful Response",
236 "content": {
237 "application/json": {
238 "schema": {"$ref": "#/components/schemas/User"}
239 }
240 },
241 }
242 },
243 "summary": "Read Users Me",
244 "operationId": "read_users_me_users_me__get",
245 "security": [{"OAuth2PasswordBearer": []}],
246 }
247 },
248 "/users/me/items/": {
249 "get": {
250 "responses": {
251 "200": {
252 "description": "Successful Response",
253 "content": {"application/json": {"schema": {}}},
254 }
255 },
256 "summary": "Read Own Items",
257 "operationId": "read_own_items_users_me_items__get",
258 "security": [{"OAuth2PasswordBearer": []}],
259 }
260 },
261 },
262 "components": {
263 "schemas": {
264 "User": {
265 "title": "User",
266 "required": ["username"],
267 "type": "object",
268 "properties": {
269 "username": {"title": "Username", "type": "string"},
270 "email": {
271 "title": "Email",
272 "anyOf": [{"type": "string"}, {"type": "null"}],
273 },
274 "full_name": {
275 "title": "Full Name",
276 "anyOf": [{"type": "string"}, {"type": "null"}],
277 },
278 "disabled": {
279 "title": "Disabled",
280 "anyOf": [{"type": "boolean"}, {"type": "null"}],
281 },
282 },
283 },
284 "Token": {
285 "title": "Token",
286 "required": ["access_token", "token_type"],
287 "type": "object",
288 "properties": {
289 "access_token": {"title": "Access Token", "type": "string"},
290 "token_type": {"title": "Token Type", "type": "string"},
291 },
292 },
293 "Body_login_for_access_token_token_post": {
294 "title": "Body_login_for_access_token_token_post",
295 "required": ["username", "password"],
296 "type": "object",
297 "properties": {
298 "grant_type": {
299 "title": "Grant Type",
300 "anyOf": [
301 {"pattern": "^password$", "type": "string"},
302 {"type": "null"},
303 ],
304 },
305 "username": {"title": "Username", "type": "string"},
306 "password": {
307 "title": "Password",
308 "type": "string",
309 "format": "password",
310 },
311 "scope": {
312 "title": "Scope",
313 "type": "string",
314 "default": "",
315 },
316 "client_id": {
317 "title": "Client Id",
318 "anyOf": [{"type": "string"}, {"type": "null"}],
319 },
320 "client_secret": {
321 "title": "Client Secret",
322 "anyOf": [{"type": "string"}, {"type": "null"}],
323 "format": "password",
324 },
325 },
326 },
327 "ValidationError": {
328 "title": "ValidationError",
329 "required": ["loc", "msg", "type"],
330 "type": "object",
331 "properties": {
332 "loc": {
333 "title": "Location",
334 "type": "array",
335 "items": {
336 "anyOf": [{"type": "string"}, {"type": "integer"}]
337 },
338 },
339 "msg": {"title": "Message", "type": "string"},
340 "type": {"title": "Error Type", "type": "string"},
341 "input": {"title": "Input"},
342 "ctx": {"title": "Context", "type": "object"},
343 },
344 },
345 "HTTPValidationError": {
346 "title": "HTTPValidationError",
347 "type": "object",
348 "properties": {
349 "detail": {
350 "title": "Detail",
351 "type": "array",
352 "items": {
353 "$ref": "#/components/schemas/ValidationError"
354 },
355 }
356 },
357 },
358 },
359 "securitySchemes": {
360 "OAuth2PasswordBearer": {
361 "type": "oauth2",
362 "flows": {
363 "password": {
364 "scopes": {},
365 "tokenUrl": "token",
366 }
367 },
368 }
369 },
370 },
371 }
372 )