Coverage for tests / test_security_oauth2_optional_description.py: 100%
57 statements
« prev ^ index » next coverage.py v7.13.3, created at 2026-04-06 01:24 +0000
« prev ^ index » next coverage.py v7.13.3, created at 2026-04-06 01:24 +0000
1import pytest 1adbc
2from fastapi import Depends, FastAPI, Security 1adbc
3from fastapi.security import OAuth2, OAuth2PasswordRequestFormStrict 1adbc
4from fastapi.testclient import TestClient 1adbc
5from inline_snapshot import snapshot 1adbc
6from pydantic import BaseModel 1adbc
8app = FastAPI() 1adbc
10reusable_oauth2 = OAuth2( 1adbc
11 flows={
12 "password": {
13 "tokenUrl": "token",
14 "scopes": {"read:users": "Read the users", "write:users": "Create users"},
15 }
16 },
17 description="OAuth2 security scheme",
18 auto_error=False,
19)
22class User(BaseModel): 1adbc
23 username: str 1abc
26def get_current_user(oauth_header: str | None = Security(reusable_oauth2)): 1adbc
27 if oauth_header is None: 1lefgmhinjk
28 return None 1lomn
29 user = User(username=oauth_header) 1efghijk
30 return user 1efghijk
33@app.post("/login") 1adbc
34def login(form_data: OAuth2PasswordRequestFormStrict = Depends()): 1adbc
35 return form_data 1pqrs
38@app.get("/users/me") 1adbc
39def read_users_me(current_user: User | None = Depends(get_current_user)): 1adbc
40 if current_user is None: 1lefgmhinjk
41 return {"msg": "Create an account first"} 1lomn
42 return current_user 1efghijk
45client = TestClient(app) 1adbc
48def test_security_oauth2(): 1adbc
49 response = client.get("/users/me", headers={"Authorization": "Bearer footokenbar"}) 1fgik
50 assert response.status_code == 200, response.text 1fgik
51 assert response.json() == {"username": "Bearer footokenbar"} 1fgik
54def test_security_oauth2_password_other_header(): 1adbc
55 response = client.get("/users/me", headers={"Authorization": "Other footokenbar"}) 1ethj
56 assert response.status_code == 200, response.text 1ethj
57 assert response.json() == {"username": "Other footokenbar"} 1ethj
60def test_security_oauth2_password_bearer_no_header(): 1adbc
61 response = client.get("/users/me") 1lomn
62 assert response.status_code == 200, response.text 1lomn
63 assert response.json() == {"msg": "Create an account first"} 1lomn
66def test_strict_login_None(): 1adbc
67 response = client.post("/login", data=None) 1uvwx
68 assert response.status_code == 422 1uvwx
69 assert response.json() == { 1uvwx
70 "detail": [
71 {
72 "type": "missing",
73 "loc": ["body", "grant_type"],
74 "msg": "Field required",
75 "input": None,
76 },
77 {
78 "type": "missing",
79 "loc": ["body", "username"],
80 "msg": "Field required",
81 "input": None,
82 },
83 {
84 "type": "missing",
85 "loc": ["body", "password"],
86 "msg": "Field required",
87 "input": None,
88 },
89 ]
90 }
93def test_strict_login_no_grant_type(): 1adbc
94 response = client.post("/login", data={"username": "johndoe", "password": "secret"}) 1yzAB
95 assert response.status_code == 422 1yzAB
96 assert response.json() == { 1yzAB
97 "detail": [
98 {
99 "type": "missing",
100 "loc": ["body", "grant_type"],
101 "msg": "Field required",
102 "input": None,
103 }
104 ]
105 }
108@pytest.mark.parametrize( 1adbc
109 argnames=["grant_type"],
110 argvalues=[
111 pytest.param("incorrect", id="incorrect value"),
112 pytest.param("passwordblah", id="password with suffix"),
113 pytest.param("blahpassword", id="password with prefix"),
114 ],
115)
116def test_strict_login_incorrect_grant_type(grant_type: str): 1adbc
117 response = client.post( 1CDEFGHIJKL
118 "/login",
119 data={"username": "johndoe", "password": "secret", "grant_type": grant_type},
120 )
121 assert response.status_code == 422 1CDEFGHIJKL
122 assert response.json() == { 1CDEFGHIJKL
123 "detail": [
124 {
125 "type": "string_pattern_mismatch",
126 "loc": ["body", "grant_type"],
127 "msg": "String should match pattern '^password$'",
128 "input": grant_type,
129 "ctx": {"pattern": "^password$"},
130 }
131 ]
132 }
135def test_strict_login_correct_correct_grant_type(): 1adbc
136 response = client.post( 1pqrs
137 "/login",
138 data={"username": "johndoe", "password": "secret", "grant_type": "password"},
139 )
140 assert response.status_code == 200, response.text 1pqrs
141 assert response.json() == { 1pqrs
142 "grant_type": "password",
143 "username": "johndoe",
144 "password": "secret",
145 "scopes": [],
146 "client_id": None,
147 "client_secret": None,
148 }
151def test_openapi_schema(): 1adbc
152 response = client.get("/openapi.json") 1MNOP
153 assert response.status_code == 200, response.text 1MNOP
154 assert response.json() == snapshot( 1MNOP
155 {
156 "openapi": "3.1.0",
157 "info": {"title": "FastAPI", "version": "0.1.0"},
158 "paths": {
159 "/login": {
160 "post": {
161 "responses": {
162 "200": {
163 "description": "Successful Response",
164 "content": {"application/json": {"schema": {}}},
165 },
166 "422": {
167 "description": "Validation Error",
168 "content": {
169 "application/json": {
170 "schema": {
171 "$ref": "#/components/schemas/HTTPValidationError"
172 }
173 }
174 },
175 },
176 },
177 "summary": "Login",
178 "operationId": "login_login_post",
179 "requestBody": {
180 "content": {
181 "application/x-www-form-urlencoded": {
182 "schema": {
183 "$ref": "#/components/schemas/Body_login_login_post"
184 }
185 }
186 },
187 "required": True,
188 },
189 }
190 },
191 "/users/me": {
192 "get": {
193 "responses": {
194 "200": {
195 "description": "Successful Response",
196 "content": {"application/json": {"schema": {}}},
197 }
198 },
199 "summary": "Read Users Me",
200 "operationId": "read_users_me_users_me_get",
201 "security": [{"OAuth2": []}],
202 }
203 },
204 },
205 "components": {
206 "schemas": {
207 "Body_login_login_post": {
208 "title": "Body_login_login_post",
209 "required": ["grant_type", "username", "password"],
210 "type": "object",
211 "properties": {
212 "grant_type": {
213 "title": "Grant Type",
214 "pattern": "^password$",
215 "type": "string",
216 },
217 "username": {"title": "Username", "type": "string"},
218 "password": {"title": "Password", "type": "string"},
219 "scope": {
220 "title": "Scope",
221 "type": "string",
222 "default": "",
223 },
224 "client_id": {
225 "title": "Client Id",
226 "anyOf": [{"type": "string"}, {"type": "null"}],
227 },
228 "client_secret": {
229 "title": "Client Secret",
230 "anyOf": [{"type": "string"}, {"type": "null"}],
231 },
232 },
233 },
234 "ValidationError": {
235 "title": "ValidationError",
236 "required": ["loc", "msg", "type"],
237 "type": "object",
238 "properties": {
239 "loc": {
240 "title": "Location",
241 "type": "array",
242 "items": {
243 "anyOf": [{"type": "string"}, {"type": "integer"}]
244 },
245 },
246 "msg": {"title": "Message", "type": "string"},
247 "type": {"title": "Error Type", "type": "string"},
248 "input": {"title": "Input"},
249 "ctx": {"title": "Context", "type": "object"},
250 },
251 },
252 "HTTPValidationError": {
253 "title": "HTTPValidationError",
254 "type": "object",
255 "properties": {
256 "detail": {
257 "title": "Detail",
258 "type": "array",
259 "items": {
260 "$ref": "#/components/schemas/ValidationError"
261 },
262 }
263 },
264 },
265 },
266 "securitySchemes": {
267 "OAuth2": {
268 "type": "oauth2",
269 "flows": {
270 "password": {
271 "scopes": {
272 "read:users": "Read the users",
273 "write:users": "Create users",
274 },
275 "tokenUrl": "token",
276 }
277 },
278 "description": "OAuth2 security scheme",
279 }
280 },
281 },
282 }
283 )