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

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

7 

8app = FastAPI() 1adbc

9 

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) 

20 

21 

22class User(BaseModel): 1adbc

23 username: str 1abc

24 

25 

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

31 

32 

33@app.post("/login") 1adbc

34def login(form_data: OAuth2PasswordRequestFormStrict = Depends()): 1adbc

35 return form_data 1pqrs

36 

37 

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

43 

44 

45client = TestClient(app) 1adbc

46 

47 

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

52 

53 

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

58 

59 

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

64 

65 

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 } 

91 

92 

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 } 

106 

107 

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 } 

133 

134 

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 } 

149 

150 

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 )