1
from dirty_equals import IsDict
2
from fastapi import Depends, FastAPI, Security
3
from fastapi.security import OAuth2, OAuth2PasswordRequestFormStrict
4
from fastapi.testclient import TestClient
5
from fastapi.utils import match_pydantic_error_url
6
from pydantic import BaseModel
10
reusable_oauth2 = OAuth2(
14
"scopes": {"read:users": "Read the users", "write:users": "Create users"},
25
def get_current_user(oauth_header: "str" = Security(reusable_oauth2)):
26
user = User(username=oauth_header)
32
def login(form_data: "OAuth2PasswordRequestFormStrict" = Depends()):
38
def read_current_user(current_user: "User" = Depends(get_current_user)):
42
client = TestClient(app)
45
def test_security_oauth2():
46
response = client.get("/users/me", headers={"Authorization": "Bearer footokenbar"})
47
assert response.status_code == 200, response.text
48
assert response.json() == {"username": "Bearer footokenbar"}
51
def test_security_oauth2_password_other_header():
52
response = client.get("/users/me", headers={"Authorization": "Other footokenbar"})
53
assert response.status_code == 200, response.text
54
assert response.json() == {"username": "Other footokenbar"}
57
def test_security_oauth2_password_bearer_no_header():
58
response = client.get("/users/me")
59
assert response.status_code == 403, response.text
60
assert response.json() == {"detail": "Not authenticated"}
63
def test_strict_login_no_data():
64
response = client.post("/login")
65
assert response.status_code == 422
66
assert response.json() == IsDict(
71
"loc": ["body", "grant_type"],
72
"msg": "Field required",
74
"url": match_pydantic_error_url("missing"),
78
"loc": ["body", "username"],
79
"msg": "Field required",
81
"url": match_pydantic_error_url("missing"),
85
"loc": ["body", "password"],
86
"msg": "Field required",
88
"url": match_pydantic_error_url("missing"),
97
"loc": ["body", "grant_type"],
98
"msg": "field required",
99
"type": "value_error.missing",
102
"loc": ["body", "username"],
103
"msg": "field required",
104
"type": "value_error.missing",
107
"loc": ["body", "password"],
108
"msg": "field required",
109
"type": "value_error.missing",
116
def test_strict_login_no_grant_type():
117
response = client.post("/login", data={"username": "johndoe", "password": "secret"})
118
assert response.status_code == 422
119
assert response.json() == IsDict(
124
"loc": ["body", "grant_type"],
125
"msg": "Field required",
127
"url": match_pydantic_error_url("missing"),
136
"loc": ["body", "grant_type"],
137
"msg": "field required",
138
"type": "value_error.missing",
145
def test_strict_login_incorrect_grant_type():
146
response = client.post(
148
data={"username": "johndoe", "password": "secret", "grant_type": "incorrect"},
150
assert response.status_code == 422
151
assert response.json() == IsDict(
155
"type": "string_pattern_mismatch",
156
"loc": ["body", "grant_type"],
157
"msg": "String should match pattern 'password'",
158
"input": "incorrect",
159
"ctx": {"pattern": "password"},
160
"url": match_pydantic_error_url("string_pattern_mismatch"),
169
"loc": ["body", "grant_type"],
170
"msg": 'string does not match regex "password"',
171
"type": "value_error.str.regex",
172
"ctx": {"pattern": "password"},
179
def test_strict_login_correct_grant_type():
180
response = client.post(
182
data={"username": "johndoe", "password": "secret", "grant_type": "password"},
184
assert response.status_code == 200
185
assert response.json() == {
186
"grant_type": "password",
187
"username": "johndoe",
188
"password": "secret",
191
"client_secret": None,
195
def test_openapi_schema():
196
response = client.get("/openapi.json")
197
assert response.status_code == 200, response.text
198
assert response.json() == {
200
"info": {"title": "FastAPI", "version": "0.1.0"},
206
"description": "Successful Response",
207
"content": {"application/json": {"schema": {}}},
210
"description": "Validation Error",
212
"application/json": {
214
"$ref": "#/components/schemas/HTTPValidationError"
221
"operationId": "login_login_post",
224
"application/x-www-form-urlencoded": {
226
"$ref": "#/components/schemas/Body_login_login_post"
238
"description": "Successful Response",
239
"content": {"application/json": {"schema": {}}},
242
"summary": "Read Current User",
243
"operationId": "read_current_user_users_me_get",
244
"security": [{"OAuth2": []}],
250
"Body_login_login_post": {
251
"title": "Body_login_login_post",
252
"required": ["grant_type", "username", "password"],
256
"title": "Grant Type",
257
"pattern": "password",
260
"username": {"title": "Username", "type": "string"},
261
"password": {"title": "Password", "type": "string"},
262
"scope": {"title": "Scope", "type": "string", "default": ""},
265
"title": "Client Id",
266
"anyOf": [{"type": "string"}, {"type": "null"}],
271
{"title": "Client Id", "type": "string"}
273
"client_secret": IsDict(
275
"title": "Client Secret",
276
"anyOf": [{"type": "string"}, {"type": "null"}],
281
{"title": "Client Secret", "type": "string"}
286
"title": "ValidationError",
287
"required": ["loc", "msg", "type"],
294
"anyOf": [{"type": "string"}, {"type": "integer"}]
297
"msg": {"title": "Message", "type": "string"},
298
"type": {"title": "Error Type", "type": "string"},
301
"HTTPValidationError": {
302
"title": "HTTPValidationError",
308
"items": {"$ref": "#/components/schemas/ValidationError"},
319
"read:users": "Read the users",
320
"write:users": "Create users",