Coverage for tests/test_tutorial/test_security/test_tutorial005_py310.py: 100%
125 statements
« prev ^ index » next coverage.py v7.6.1, created at 2024-08-08 03:53 +0000
« prev ^ index » next coverage.py v7.6.1, created at 2024-08-08 03:53 +0000
1import pytest 1deabc
2from dirty_equals import IsDict, IsOneOf 1deabc
3from fastapi.testclient import TestClient 1deabc
5from ...utils import needs_py310 1deabc
8@pytest.fixture(name="client") 1deabc
9def get_client(): 1deabc
10 from docs_src.security.tutorial005_py310 import app 1abc
12 client = TestClient(app) 1abc
13 return client 1abc
16def get_access_token( 1deabc
17 *, username="johndoe", password="secret", scope=None, client: TestClient
18):
19 data = {"username": username, "password": password} 1abc
20 if scope: 1abc
21 data["scope"] = scope 1abc
22 response = client.post("/token", data=data) 1abc
23 content = response.json() 1abc
24 access_token = content.get("access_token") 1abc
25 return access_token 1abc
28@needs_py310 1deabc
29def test_login(client: TestClient): 1deabc
30 response = client.post("/token", data={"username": "johndoe", "password": "secret"}) 1abc
31 assert response.status_code == 200, response.text 1abc
32 content = response.json() 1abc
33 assert "access_token" in content 1abc
34 assert content["token_type"] == "bearer" 1abc
37@needs_py310 1deabc
38def test_login_incorrect_password(client: TestClient): 1deabc
39 response = client.post( 1abc
40 "/token", data={"username": "johndoe", "password": "incorrect"}
41 )
42 assert response.status_code == 400, response.text 1abc
43 assert response.json() == {"detail": "Incorrect username or password"} 1abc
46@needs_py310 1deabc
47def test_login_incorrect_username(client: TestClient): 1deabc
48 response = client.post("/token", data={"username": "foo", "password": "secret"}) 1abc
49 assert response.status_code == 400, response.text 1abc
50 assert response.json() == {"detail": "Incorrect username or password"} 1abc
53@needs_py310 1deabc
54def test_no_token(client: TestClient): 1deabc
55 response = client.get("/users/me") 1abc
56 assert response.status_code == 401, response.text 1abc
57 assert response.json() == {"detail": "Not authenticated"} 1abc
58 assert response.headers["WWW-Authenticate"] == "Bearer" 1abc
61@needs_py310 1deabc
62def test_token(client: TestClient): 1deabc
63 access_token = get_access_token(scope="me", client=client) 1abc
64 response = client.get( 1abc
65 "/users/me", headers={"Authorization": f"Bearer {access_token}"}
66 )
67 assert response.status_code == 200, response.text 1abc
68 assert response.json() == { 1abc
69 "username": "johndoe",
70 "full_name": "John Doe",
71 "email": "johndoe@example.com",
72 "disabled": False,
73 }
76@needs_py310 1deabc
77def test_incorrect_token(client: TestClient): 1deabc
78 response = client.get("/users/me", headers={"Authorization": "Bearer nonexistent"}) 1abc
79 assert response.status_code == 401, response.text 1abc
80 assert response.json() == {"detail": "Could not validate credentials"} 1abc
81 assert response.headers["WWW-Authenticate"] == 'Bearer scope="me"' 1abc
84@needs_py310 1deabc
85def test_incorrect_token_type(client: TestClient): 1deabc
86 response = client.get( 1abc
87 "/users/me", headers={"Authorization": "Notexistent testtoken"}
88 )
89 assert response.status_code == 401, response.text 1abc
90 assert response.json() == {"detail": "Not authenticated"} 1abc
91 assert response.headers["WWW-Authenticate"] == "Bearer" 1abc
94@needs_py310 1deabc
95def test_verify_password(): 1deabc
96 from docs_src.security.tutorial005_py310 import fake_users_db, verify_password 1abc
98 assert verify_password("secret", fake_users_db["johndoe"]["hashed_password"]) 1abc
101@needs_py310 1deabc
102def test_get_password_hash(): 1deabc
103 from docs_src.security.tutorial005_py310 import get_password_hash 1abc
105 assert get_password_hash("secretalice") 1abc
108@needs_py310 1deabc
109def test_create_access_token(): 1deabc
110 from docs_src.security.tutorial005_py310 import create_access_token 1abc
112 access_token = create_access_token(data={"data": "foo"}) 1abc
113 assert access_token 1abc
116@needs_py310 1deabc
117def test_token_no_sub(client: TestClient): 1deabc
118 response = client.get( 1abc
119 "/users/me",
120 headers={
121 "Authorization": "Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJkYXRhIjoiZm9vIn0.9ynBhuYb4e6aW3oJr_K_TBgwcMTDpRToQIE25L57rOE"
122 },
123 )
124 assert response.status_code == 401, response.text 1abc
125 assert response.json() == {"detail": "Could not validate credentials"} 1abc
126 assert response.headers["WWW-Authenticate"] == 'Bearer scope="me"' 1abc
129@needs_py310 1deabc
130def test_token_no_username(client: TestClient): 1deabc
131 response = client.get( 1abc
132 "/users/me",
133 headers={
134 "Authorization": "Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJmb28ifQ.NnExK_dlNAYyzACrXtXDrcWOgGY2JuPbI4eDaHdfK5Y"
135 },
136 )
137 assert response.status_code == 401, response.text 1abc
138 assert response.json() == {"detail": "Could not validate credentials"} 1abc
139 assert response.headers["WWW-Authenticate"] == 'Bearer scope="me"' 1abc
142@needs_py310 1deabc
143def test_token_no_scope(client: TestClient): 1deabc
144 access_token = get_access_token(client=client) 1abc
145 response = client.get( 1abc
146 "/users/me", headers={"Authorization": f"Bearer {access_token}"}
147 )
148 assert response.status_code == 401, response.text 1abc
149 assert response.json() == {"detail": "Not enough permissions"} 1abc
150 assert response.headers["WWW-Authenticate"] == 'Bearer scope="me"' 1abc
153@needs_py310 1deabc
154def test_token_nonexistent_user(client: TestClient): 1deabc
155 response = client.get( 1abc
156 "/users/me",
157 headers={
158 "Authorization": "Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ1c2VybmFtZTpib2IifQ.HcfCW67Uda-0gz54ZWTqmtgJnZeNem0Q757eTa9EZuw"
159 },
160 )
161 assert response.status_code == 401, response.text 1abc
162 assert response.json() == {"detail": "Could not validate credentials"} 1abc
163 assert response.headers["WWW-Authenticate"] == 'Bearer scope="me"' 1abc
166@needs_py310 1deabc
167def test_token_inactive_user(client: TestClient): 1deabc
168 access_token = get_access_token( 1abc
169 username="alice", password="secretalice", scope="me", client=client
170 )
171 response = client.get( 1abc
172 "/users/me", headers={"Authorization": f"Bearer {access_token}"}
173 )
174 assert response.status_code == 400, response.text 1abc
175 assert response.json() == {"detail": "Inactive user"} 1abc
178@needs_py310 1deabc
179def test_read_items(client: TestClient): 1deabc
180 access_token = get_access_token(scope="me items", client=client) 1abc
181 response = client.get( 1abc
182 "/users/me/items/", headers={"Authorization": f"Bearer {access_token}"}
183 )
184 assert response.status_code == 200, response.text 1abc
185 assert response.json() == [{"item_id": "Foo", "owner": "johndoe"}] 1abc
188@needs_py310 1deabc
189def test_read_system_status(client: TestClient): 1deabc
190 access_token = get_access_token(client=client) 1abc
191 response = client.get( 1abc
192 "/status/", headers={"Authorization": f"Bearer {access_token}"}
193 )
194 assert response.status_code == 200, response.text 1abc
195 assert response.json() == {"status": "ok"} 1abc
198@needs_py310 1deabc
199def test_read_system_status_no_token(client: TestClient): 1deabc
200 response = client.get("/status/") 1abc
201 assert response.status_code == 401, response.text 1abc
202 assert response.json() == {"detail": "Not authenticated"} 1abc
203 assert response.headers["WWW-Authenticate"] == "Bearer" 1abc
206@needs_py310 1deabc
207def test_openapi_schema(client: TestClient): 1deabc
208 response = client.get("/openapi.json") 1abc
209 assert response.status_code == 200, response.text 1abc
210 assert response.json() == { 1abc
211 "openapi": "3.1.0",
212 "info": {"title": "FastAPI", "version": "0.1.0"},
213 "paths": {
214 "/token": {
215 "post": {
216 "responses": {
217 "200": {
218 "description": "Successful Response",
219 "content": {
220 "application/json": {
221 "schema": {"$ref": "#/components/schemas/Token"}
222 }
223 },
224 },
225 "422": {
226 "description": "Validation Error",
227 "content": {
228 "application/json": {
229 "schema": {
230 "$ref": "#/components/schemas/HTTPValidationError"
231 }
232 }
233 },
234 },
235 },
236 "summary": "Login For Access Token",
237 "operationId": "login_for_access_token_token_post",
238 "requestBody": {
239 "content": {
240 "application/x-www-form-urlencoded": {
241 "schema": {
242 "$ref": "#/components/schemas/Body_login_for_access_token_token_post"
243 }
244 }
245 },
246 "required": True,
247 },
248 }
249 },
250 "/users/me/": {
251 "get": {
252 "responses": {
253 "200": {
254 "description": "Successful Response",
255 "content": {
256 "application/json": {
257 "schema": {"$ref": "#/components/schemas/User"}
258 }
259 },
260 }
261 },
262 "summary": "Read Users Me",
263 "operationId": "read_users_me_users_me__get",
264 "security": [{"OAuth2PasswordBearer": ["me"]}],
265 }
266 },
267 "/users/me/items/": {
268 "get": {
269 "responses": {
270 "200": {
271 "description": "Successful Response",
272 "content": {"application/json": {"schema": {}}},
273 }
274 },
275 "summary": "Read Own Items",
276 "operationId": "read_own_items_users_me_items__get",
277 "security": [{"OAuth2PasswordBearer": ["items", "me"]}],
278 }
279 },
280 "/status/": {
281 "get": {
282 "responses": {
283 "200": {
284 "description": "Successful Response",
285 "content": {"application/json": {"schema": {}}},
286 }
287 },
288 "summary": "Read System Status",
289 "operationId": "read_system_status_status__get",
290 "security": [{"OAuth2PasswordBearer": []}],
291 }
292 },
293 },
294 "components": {
295 "schemas": {
296 "User": {
297 "title": "User",
298 "required": IsOneOf(
299 ["username", "email", "full_name", "disabled"],
300 # TODO: remove when deprecating Pydantic v1
301 ["username"],
302 ),
303 "type": "object",
304 "properties": {
305 "username": {"title": "Username", "type": "string"},
306 "email": IsDict(
307 {
308 "title": "Email",
309 "anyOf": [{"type": "string"}, {"type": "null"}],
310 }
311 )
312 | IsDict(
313 # TODO: remove when deprecating Pydantic v1
314 {"title": "Email", "type": "string"}
315 ),
316 "full_name": IsDict(
317 {
318 "title": "Full Name",
319 "anyOf": [{"type": "string"}, {"type": "null"}],
320 }
321 )
322 | IsDict(
323 # TODO: remove when deprecating Pydantic v1
324 {"title": "Full Name", "type": "string"}
325 ),
326 "disabled": IsDict(
327 {
328 "title": "Disabled",
329 "anyOf": [{"type": "boolean"}, {"type": "null"}],
330 }
331 )
332 | IsDict(
333 # TODO: remove when deprecating Pydantic v1
334 {"title": "Disabled", "type": "boolean"}
335 ),
336 },
337 },
338 "Token": {
339 "title": "Token",
340 "required": ["access_token", "token_type"],
341 "type": "object",
342 "properties": {
343 "access_token": {"title": "Access Token", "type": "string"},
344 "token_type": {"title": "Token Type", "type": "string"},
345 },
346 },
347 "Body_login_for_access_token_token_post": {
348 "title": "Body_login_for_access_token_token_post",
349 "required": ["username", "password"],
350 "type": "object",
351 "properties": {
352 "grant_type": IsDict(
353 {
354 "title": "Grant Type",
355 "anyOf": [
356 {"pattern": "password", "type": "string"},
357 {"type": "null"},
358 ],
359 }
360 )
361 | IsDict(
362 # TODO: remove when deprecating Pydantic v1
363 {
364 "title": "Grant Type",
365 "pattern": "password",
366 "type": "string",
367 }
368 ),
369 "username": {"title": "Username", "type": "string"},
370 "password": {"title": "Password", "type": "string"},
371 "scope": {"title": "Scope", "type": "string", "default": ""},
372 "client_id": IsDict(
373 {
374 "title": "Client Id",
375 "anyOf": [{"type": "string"}, {"type": "null"}],
376 }
377 )
378 | IsDict(
379 # TODO: remove when deprecating Pydantic v1
380 {"title": "Client Id", "type": "string"}
381 ),
382 "client_secret": IsDict(
383 {
384 "title": "Client Secret",
385 "anyOf": [{"type": "string"}, {"type": "null"}],
386 }
387 )
388 | IsDict(
389 # TODO: remove when deprecating Pydantic v1
390 {"title": "Client Secret", "type": "string"}
391 ),
392 },
393 },
394 "ValidationError": {
395 "title": "ValidationError",
396 "required": ["loc", "msg", "type"],
397 "type": "object",
398 "properties": {
399 "loc": {
400 "title": "Location",
401 "type": "array",
402 "items": {
403 "anyOf": [{"type": "string"}, {"type": "integer"}]
404 },
405 },
406 "msg": {"title": "Message", "type": "string"},
407 "type": {"title": "Error Type", "type": "string"},
408 },
409 },
410 "HTTPValidationError": {
411 "title": "HTTPValidationError",
412 "type": "object",
413 "properties": {
414 "detail": {
415 "title": "Detail",
416 "type": "array",
417 "items": {"$ref": "#/components/schemas/ValidationError"},
418 }
419 },
420 },
421 },
422 "securitySchemes": {
423 "OAuth2PasswordBearer": {
424 "type": "oauth2",
425 "flows": {
426 "password": {
427 "scopes": {
428 "me": "Read information about the current user.",
429 "items": "Read items.",
430 },
431 "tokenUrl": "token",
432 }
433 },
434 }
435 },
436 },
437 }