Coverage for tests/test_tutorial/test_security/test_tutorial003.py: 100%
49 statements
« prev ^ index » next coverage.py v7.6.1, created at 2025-05-05 00:03 +0000
« prev ^ index » next coverage.py v7.6.1, created at 2025-05-05 00:03 +0000
1import importlib 1abcdef
3import pytest 1abcdef
4from dirty_equals import IsDict 1abcdef
5from fastapi.testclient import TestClient 1abcdef
7from ...utils import needs_py39, needs_py310 1abcdef
10@pytest.fixture( 1abcdef
11 name="client",
12 params=[
13 "tutorial003",
14 pytest.param("tutorial003_py310", marks=needs_py310),
15 "tutorial003_an",
16 pytest.param("tutorial003_an_py39", marks=needs_py39),
17 pytest.param("tutorial003_an_py310", marks=needs_py310),
18 ],
19)
20def get_client(request: pytest.FixtureRequest): 1abcdef
21 mod = importlib.import_module(f"docs_src.security.{request.param}") 1abcdef
23 client = TestClient(mod.app) 1abcdef
24 return client 1abcdef
27def test_login(client: TestClient): 1abcdef
28 response = client.post("/token", data={"username": "johndoe", "password": "secret"}) 1yzABCD
29 assert response.status_code == 200, response.text 1yzABCD
30 assert response.json() == {"access_token": "johndoe", "token_type": "bearer"} 1yzABCD
33def test_login_incorrect_password(client: TestClient): 1abcdef
34 response = client.post( 1EFGHIJ
35 "/token", data={"username": "johndoe", "password": "incorrect"}
36 )
37 assert response.status_code == 400, response.text 1EFGHIJ
38 assert response.json() == {"detail": "Incorrect username or password"} 1EFGHIJ
41def test_login_incorrect_username(client: TestClient): 1abcdef
42 response = client.post("/token", data={"username": "foo", "password": "secret"}) 1KLMNOP
43 assert response.status_code == 400, response.text 1KLMNOP
44 assert response.json() == {"detail": "Incorrect username or password"} 1KLMNOP
47def test_no_token(client: TestClient): 1abcdef
48 response = client.get("/users/me") 1ghijkl
49 assert response.status_code == 401, response.text 1ghijkl
50 assert response.json() == {"detail": "Not authenticated"} 1ghijkl
51 assert response.headers["WWW-Authenticate"] == "Bearer" 1ghijkl
54def test_token(client: TestClient): 1abcdef
55 response = client.get("/users/me", headers={"Authorization": "Bearer johndoe"}) 1QRSTUV
56 assert response.status_code == 200, response.text 1QRSTUV
57 assert response.json() == { 1QRSTUV
58 "username": "johndoe",
59 "full_name": "John Doe",
60 "email": "johndoe@example.com",
61 "hashed_password": "fakehashedsecret",
62 "disabled": False,
63 }
66def test_incorrect_token(client: TestClient): 1abcdef
67 response = client.get("/users/me", headers={"Authorization": "Bearer nonexistent"}) 1mnopqr
68 assert response.status_code == 401, response.text 1mnopqr
69 assert response.json() == {"detail": "Invalid authentication credentials"} 1mnopqr
70 assert response.headers["WWW-Authenticate"] == "Bearer" 1mnopqr
73def test_incorrect_token_type(client: TestClient): 1abcdef
74 response = client.get( 1stuvwx
75 "/users/me", headers={"Authorization": "Notexistent testtoken"}
76 )
77 assert response.status_code == 401, response.text 1stuvwx
78 assert response.json() == {"detail": "Not authenticated"} 1stuvwx
79 assert response.headers["WWW-Authenticate"] == "Bearer" 1stuvwx
82def test_inactive_user(client: TestClient): 1abcdef
83 response = client.get("/users/me", headers={"Authorization": "Bearer alice"}) 1WXYZ01
84 assert response.status_code == 400, response.text 1WXYZ01
85 assert response.json() == {"detail": "Inactive user"} 1WXYZ01
88def test_openapi_schema(client: TestClient): 1abcdef
89 response = client.get("/openapi.json") 1234567
90 assert response.status_code == 200, response.text 1234567
91 assert response.json() == { 1234567
92 "openapi": "3.1.0",
93 "info": {"title": "FastAPI", "version": "0.1.0"},
94 "paths": {
95 "/token": {
96 "post": {
97 "responses": {
98 "200": {
99 "description": "Successful Response",
100 "content": {"application/json": {"schema": {}}},
101 },
102 "422": {
103 "description": "Validation Error",
104 "content": {
105 "application/json": {
106 "schema": {
107 "$ref": "#/components/schemas/HTTPValidationError"
108 }
109 }
110 },
111 },
112 },
113 "summary": "Login",
114 "operationId": "login_token_post",
115 "requestBody": {
116 "content": {
117 "application/x-www-form-urlencoded": {
118 "schema": {
119 "$ref": "#/components/schemas/Body_login_token_post"
120 }
121 }
122 },
123 "required": True,
124 },
125 }
126 },
127 "/users/me": {
128 "get": {
129 "responses": {
130 "200": {
131 "description": "Successful Response",
132 "content": {"application/json": {"schema": {}}},
133 }
134 },
135 "summary": "Read Users Me",
136 "operationId": "read_users_me_users_me_get",
137 "security": [{"OAuth2PasswordBearer": []}],
138 }
139 },
140 },
141 "components": {
142 "schemas": {
143 "Body_login_token_post": {
144 "title": "Body_login_token_post",
145 "required": ["username", "password"],
146 "type": "object",
147 "properties": {
148 "grant_type": IsDict(
149 {
150 "title": "Grant Type",
151 "anyOf": [
152 {"pattern": "^password$", "type": "string"},
153 {"type": "null"},
154 ],
155 }
156 )
157 | IsDict(
158 # TODO: remove when deprecating Pydantic v1
159 {
160 "title": "Grant Type",
161 "pattern": "^password$",
162 "type": "string",
163 }
164 ),
165 "username": {"title": "Username", "type": "string"},
166 "password": {"title": "Password", "type": "string"},
167 "scope": {"title": "Scope", "type": "string", "default": ""},
168 "client_id": IsDict(
169 {
170 "title": "Client Id",
171 "anyOf": [{"type": "string"}, {"type": "null"}],
172 }
173 )
174 | IsDict(
175 # TODO: remove when deprecating Pydantic v1
176 {"title": "Client Id", "type": "string"}
177 ),
178 "client_secret": IsDict(
179 {
180 "title": "Client Secret",
181 "anyOf": [{"type": "string"}, {"type": "null"}],
182 }
183 )
184 | IsDict(
185 # TODO: remove when deprecating Pydantic v1
186 {"title": "Client Secret", "type": "string"}
187 ),
188 },
189 },
190 "ValidationError": {
191 "title": "ValidationError",
192 "required": ["loc", "msg", "type"],
193 "type": "object",
194 "properties": {
195 "loc": {
196 "title": "Location",
197 "type": "array",
198 "items": {
199 "anyOf": [{"type": "string"}, {"type": "integer"}]
200 },
201 },
202 "msg": {"title": "Message", "type": "string"},
203 "type": {"title": "Error Type", "type": "string"},
204 },
205 },
206 "HTTPValidationError": {
207 "title": "HTTPValidationError",
208 "type": "object",
209 "properties": {
210 "detail": {
211 "title": "Detail",
212 "type": "array",
213 "items": {"$ref": "#/components/schemas/ValidationError"},
214 }
215 },
216 },
217 },
218 "securitySchemes": {
219 "OAuth2PasswordBearer": {
220 "type": "oauth2",
221 "flows": {"password": {"scopes": {}, "tokenUrl": "token"}},
222 }
223 },
224 },
225 }