Coverage for tests/test_tutorial/test_security/test_tutorial003.py: 100%
49 statements
« prev ^ index » next coverage.py v7.6.1, created at 2025-09-09 09:16 +0000
« prev ^ index » next coverage.py v7.6.1, created at 2025-09-09 09:16 +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": {
167 "title": "Password",
168 "type": "string",
169 "format": "password",
170 },
171 "scope": {"title": "Scope", "type": "string", "default": ""},
172 "client_id": IsDict(
173 {
174 "title": "Client Id",
175 "anyOf": [{"type": "string"}, {"type": "null"}],
176 }
177 )
178 | IsDict(
179 # TODO: remove when deprecating Pydantic v1
180 {"title": "Client Id", "type": "string"}
181 ),
182 "client_secret": IsDict(
183 {
184 "title": "Client Secret",
185 "anyOf": [{"type": "string"}, {"type": "null"}],
186 "format": "password",
187 }
188 )
189 | IsDict(
190 # TODO: remove when deprecating Pydantic v1
191 {
192 "title": "Client Secret",
193 "type": "string",
194 "format": "password",
195 }
196 ),
197 },
198 },
199 "ValidationError": {
200 "title": "ValidationError",
201 "required": ["loc", "msg", "type"],
202 "type": "object",
203 "properties": {
204 "loc": {
205 "title": "Location",
206 "type": "array",
207 "items": {
208 "anyOf": [{"type": "string"}, {"type": "integer"}]
209 },
210 },
211 "msg": {"title": "Message", "type": "string"},
212 "type": {"title": "Error Type", "type": "string"},
213 },
214 },
215 "HTTPValidationError": {
216 "title": "HTTPValidationError",
217 "type": "object",
218 "properties": {
219 "detail": {
220 "title": "Detail",
221 "type": "array",
222 "items": {"$ref": "#/components/schemas/ValidationError"},
223 }
224 },
225 },
226 },
227 "securitySchemes": {
228 "OAuth2PasswordBearer": {
229 "type": "oauth2",
230 "flows": {"password": {"scopes": {}, "tokenUrl": "token"}},
231 }
232 },
233 },
234 }