all repos

onasty @ c5b3657

a one-time notes service

onasty/e2e/apiv1_auth_test.go (view raw)

Smirnov Oleksandr Smirnov Oleksandr
ss2316544@gmail.com
feat: reset password (#110)..., 1 year ago
1
package e2e_test
2
3
import (
4
	"net/http"
5
6
	"github.com/gofrs/uuid/v5"
7
	"github.com/olexsmir/onasty/internal/models"
8
)
9
10
type apiv1AuthSignUpRequest struct {
11
	Username string `json:"username"`
12
	Email    string `json:"email"`
13
	Password string `json:"password"`
14
}
15
16
func (e *AppTestSuite) TestAuthV1_SignUP() {
17
	username := "test" + e.uuid()
18
	email := e.uuid() + "test@test.com"
19
	password := "password"
20
21
	httpResp := e.httpRequest(
22
		http.MethodPost,
23
		"/api/v1/auth/signup",
24
		e.jsonify(apiv1AuthSignUpRequest{
25
			Username: username,
26
			Email:    email,
27
			Password: password,
28
		}),
29
	)
30
31
	dbUser := e.getUserByUsername(username)
32
	hashedPasswd, err := e.hasher.Hash(password)
33
	e.require.NoError(err)
34
35
	e.Equal(http.StatusCreated, httpResp.Code)
36
	e.Equal(dbUser.Email, email)
37
	e.Equal(dbUser.Password, hashedPasswd)
38
}
39
40
func (e *AppTestSuite) TestAuthV1_SignUP_badrequest() {
41
	tests := []struct {
42
		name     string
43
		username string
44
		email    string
45
		password string
46
	}{
47
		{name: "all fields empty", email: "", password: "", username: ""},
48
		{
49
			name:     "non valid email",
50
			username: "testing",
51
			email:    "email",
52
			password: "password",
53
		},
54
		{
55
			name:     "non valid password",
56
			email:    "test@test.com",
57
			password: "12345",
58
			username: "test",
59
		},
60
	}
61
	for _, t := range tests {
62
		httpResp := e.httpRequest(
63
			http.MethodPost,
64
			"/api/v1/auth/signup",
65
			e.jsonify(apiv1AuthSignUpRequest{
66
				Username: t.username,
67
				Email:    t.email,
68
				Password: t.password,
69
			}),
70
		)
71
72
		e.Equal(http.StatusBadRequest, httpResp.Code)
73
	}
74
}
75
76
type (
77
	apiv1AuthSignInRequest struct {
78
		Email    string `json:"email"`
79
		Password string `json:"password"`
80
	}
81
	apiv1AuthSignInResponse struct {
82
		AccessToken  string `json:"access_token"`
83
		RefreshToken string `json:"refresh_token"`
84
	}
85
)
86
87
func (e *AppTestSuite) TestAuthV1_VerifyEmail() {
88
	email := e.uuid() + "email@email.com"
89
	password := "qwerty"
90
91
	httpResp := e.httpRequest(
92
		http.MethodPost,
93
		"/api/v1/auth/signup",
94
		e.jsonify(apiv1AuthSignUpRequest{
95
			Username: e.uuid(),
96
			Email:    email,
97
			Password: password,
98
		}),
99
	)
100
101
	e.Equal(http.StatusCreated, httpResp.Code)
102
103
	user := e.getLastUserByEmail(email)
104
	token := e.getVerificationTokenByUserID(user.ID)
105
	e.Equal(token.Token, mockMailStore[email])
106
107
	httpResp = e.httpRequest(http.MethodGet, "/api/v1/auth/verify/"+token.Token, nil)
108
	e.Equal(http.StatusOK, httpResp.Code)
109
110
	user = e.getLastUserByEmail(email)
111
	e.Equal(user.Activated, true)
112
}
113
114
func (e *AppTestSuite) TestAuthV1_ResendVerificationEmail() {
115
	email, password := e.uuid()+"email@email.com", e.uuid()
116
117
	// create test user
118
	signUpHTTPResp := e.httpRequest(
119
		http.MethodPost,
120
		"/api/v1/auth/signup",
121
		e.jsonify(apiv1AuthSignUpRequest{
122
			Username: e.uuid(),
123
			Email:    email,
124
			Password: password,
125
		}),
126
	)
127
128
	e.Equal(http.StatusCreated, signUpHTTPResp.Code)
129
130
	// handle sending of the email
131
	httpResp := e.httpRequest(
132
		http.MethodPost,
133
		"/api/v1/auth/resend-verification-email",
134
		e.jsonify(apiv1AuthSignInRequest{
135
			Email:    email,
136
			Password: password,
137
		}),
138
	)
139
140
	e.Equal(http.StatusOK, httpResp.Code)
141
	e.NotEmpty(mockMailStore[email])
142
}
143
144
func (e *AppTestSuite) TestAuthV1_ResendVerificationEmail_wrong() {
145
	email, password := e.uuid()+"@"+e.uuid()+".com", "password"
146
	e.insertUser(e.uuid(), email, password, true)
147
148
	tests := []struct {
149
		name         string
150
		email        string
151
		password     string
152
		expectedCode int
153
	}{
154
		{
155
			name:         "activated account",
156
			email:        email,
157
			password:     password,
158
			expectedCode: http.StatusBadRequest,
159
		},
160
		{
161
			name:         "wrong credentials",
162
			email:        email,
163
			password:     e.uuid(),
164
			expectedCode: http.StatusUnauthorized,
165
		},
166
	}
167
168
	for _, t := range tests {
169
		httpResp := e.httpRequest(
170
			http.MethodPost,
171
			"/api/v1/auth/resend-verification-email",
172
			e.jsonify(apiv1AuthSignInRequest{
173
				Email:    t.email,
174
				Password: t.password,
175
			}))
176
177
		e.Equal(httpResp.Code, t.expectedCode)
178
		e.Empty(mockMailStore[t.email])
179
	}
180
}
181
182
func (e *AppTestSuite) TestAuthV1_SignIn() {
183
	email := e.uuid() + "email@email.com"
184
	password := "qwerty"
185
186
	uid := e.insertUser("test", email, password, true)
187
188
	httpResp := e.httpRequest(
189
		http.MethodPost,
190
		"/api/v1/auth/signin",
191
		e.jsonify(apiv1AuthSignInRequest{
192
			Email:    email,
193
			Password: password,
194
		}),
195
	)
196
197
	var body apiv1AuthSignInResponse
198
	e.readBodyAndUnjsonify(httpResp.Body, &body)
199
200
	session := e.getLastSessionByUserID(uid)
201
	parsedToken := e.parseJwtToken(body.AccessToken)
202
203
	e.Equal(http.StatusOK, httpResp.Code)
204
	e.Equal(body.RefreshToken, session.RefreshToken)
205
	e.Equal(parsedToken.UserID, uid.String())
206
}
207
208
func (e *AppTestSuite) TestAuthV1_SignIn_wrong() {
209
	password := "password"
210
	email := e.uuid() + "@test.com"
211
	e.insertUser(e.uuid(), email, "password", true)
212
213
	unactivatedEmail := e.uuid() + "@test.com"
214
	e.insertUser(e.uuid(), unactivatedEmail, password, false)
215
216
	//exhaustruct:ignore
217
	tests := []struct {
218
		name         string
219
		email        string
220
		password     string
221
		expectedCode int
222
223
		expectMsg   bool
224
		expectedMsg string
225
	}{
226
		{
227
			name:         "inactivated user",
228
			email:        unactivatedEmail,
229
			password:     password,
230
			expectedCode: http.StatusBadRequest,
231
			expectMsg:    true,
232
			expectedMsg:  models.ErrUserIsNotActivated.Error(),
233
		},
234
		{
235
			name:         "wrong email",
236
			email:        "wrong@email.com",
237
			password:     password,
238
			expectedCode: http.StatusBadRequest,
239
		},
240
		{
241
			name:         "wrong password",
242
			email:        email,
243
			password:     "wrong-wrong",
244
			expectedCode: http.StatusUnauthorized,
245
		},
246
	}
247
248
	for _, t := range tests {
249
		httpResp := e.httpRequest(
250
			http.MethodPost,
251
			"/api/v1/auth/signin",
252
			e.jsonify(apiv1AuthSignInRequest{
253
				Email:    t.email,
254
				Password: t.password,
255
			}),
256
		)
257
258
		if t.expectMsg {
259
			var body errorResponse
260
			e.readBodyAndUnjsonify(httpResp.Body, &body)
261
262
			e.Equal(body.Message, t.expectedMsg)
263
		}
264
265
		e.Equal(t.expectedCode, httpResp.Code)
266
	}
267
}
268
269
type apiv1AuthRefreshTokensRequest struct {
270
	RefreshToken string `json:"refresh_token"`
271
}
272
273
func (e *AppTestSuite) TestAuthV1_RefreshTokens() {
274
	uid, toks := e.createAndSingIn(e.uuid()+"@test.com", e.uuid(), "password")
275
	httpResp := e.httpRequest(
276
		http.MethodPost,
277
		"/api/v1/auth/refresh-tokens",
278
		e.jsonify(apiv1AuthRefreshTokensRequest{
279
			RefreshToken: toks.RefreshToken,
280
		}),
281
	)
282
283
	var body apiv1AuthSignInResponse
284
	e.readBodyAndUnjsonify(httpResp.Body, &body)
285
286
	sessionDB := e.getLastSessionByUserID(uid)
287
	e.Equal(e.parseJwtToken(body.AccessToken).UserID, uid.String())
288
289
	e.Equal(httpResp.Code, http.StatusOK)
290
	e.NotEqual(toks.RefreshToken, body.RefreshToken)
291
	e.Equal(body.RefreshToken, sessionDB.RefreshToken)
292
}
293
294
func (e *AppTestSuite) TestAuthV1_RefreshTokens_wrong() {
295
	// requests a new token pair with a wrong refresh token
296
297
	httpResp := e.httpRequest(
298
		http.MethodPost,
299
		"/api/v1/auth/refresh-tokens",
300
		e.jsonify(apiv1AuthRefreshTokensRequest{
301
			RefreshToken: e.uuid(),
302
		}),
303
	)
304
305
	e.Equal(httpResp.Code, http.StatusBadRequest)
306
}
307
308
func (e *AppTestSuite) TestAuthV1_Logout() {
309
	uid, toks := e.createAndSingIn(e.uuid()+"@test.com", e.uuid(), "password")
310
311
	sessionDB := e.getLastSessionByUserID(uid)
312
	e.NotEmpty(sessionDB.RefreshToken)
313
314
	httpResp := e.httpRequest(http.MethodPost, "/api/v1/auth/logout", nil, toks.AccessToken)
315
	e.Equal(httpResp.Code, http.StatusNoContent)
316
317
	sessionDB = e.getLastSessionByUserID(uid)
318
	e.Empty(sessionDB.RefreshToken)
319
}
320
321
type apiv1AtuhChangePasswordRequest struct {
322
	CurrentPassword string `json:"current_password"`
323
	NewPassword     string `json:"new_password"`
324
}
325
326
func (e *AppTestSuite) TestAuthV1_ChangePassword() {
327
	password := e.uuid()
328
	newPassword := e.uuid()
329
	username := e.uuid()
330
	_, toks := e.createAndSingIn(e.uuid()+"@test.com", username, password)
331
332
	httpResp := e.httpRequest(
333
		http.MethodPost,
334
		"/api/v1/auth/change-password",
335
		e.jsonify(apiv1AtuhChangePasswordRequest{
336
			CurrentPassword: password,
337
			NewPassword:     newPassword,
338
		}),
339
		toks.AccessToken,
340
	)
341
342
	e.Equal(httpResp.Code, http.StatusOK)
343
344
	userDB := e.getUserByUsername(username)
345
	e.Equal(userDB.Username, username)
346
	e.NoError(e.hasher.Compare(userDB.Password, newPassword))
347
}
348
349
type (
350
	apiV1AuthResetPasswordRequest struct {
351
		Email string `json:"email"`
352
	}
353
	apiV1AuthSetPasswordRequest struct {
354
		Password string `json:"password"`
355
	}
356
)
357
358
func (e *AppTestSuite) TestAuthV1_ResetPassword() {
359
	email := e.uuid() + "@test.com"
360
	uname := e.uuid()
361
	uid, _ := e.createAndSingIn(email, uname, "password")
362
363
	httpResp := e.httpRequest(
364
		http.MethodPost,
365
		"/api/v1/auth/reset-password",
366
		e.jsonify(apiV1AuthResetPasswordRequest{
367
			Email: email,
368
		}),
369
	)
370
371
	e.Equal(httpResp.Code, http.StatusOK)
372
373
	token := e.getResetPasswordTokenByUserID(uid)
374
	e.Empty(token.UsedAt)
375
	e.Equal(mockMailStore[email], token.Token)
376
377
	// set new password
378
	password := e.uuid()
379
	httpResp = e.httpRequest(
380
		http.MethodPost,
381
		"/api/v1/auth/reset-password/"+token.Token,
382
		e.jsonify(apiV1AuthSetPasswordRequest{
383
			Password: password,
384
		}),
385
	)
386
387
	dbUser := e.getUserByUsername(uname)
388
	e.Equal(httpResp.Code, http.StatusOK)
389
	e.NoError(e.hasher.Compare(dbUser.Password, password))
390
391
	token = e.getResetPasswordTokenByUserID(uid)
392
	e.NotEmpty(token.UsedAt)
393
}
394
395
func (e *AppTestSuite) TestAuthV1_ResetPassword_nonExistentUser() {
396
	_, _ = e.createAndSingIn(e.uuid()+"@test.comd", e.uuid(), "password")
397
	httpResp := e.httpRequest(
398
		http.MethodPost,
399
		"/api/v1/auth/reset-password",
400
		e.jsonify(apiV1AuthResetPasswordRequest{
401
			Email: e.uuid() + "@testing.com",
402
		}),
403
	)
404
405
	e.Equal(httpResp.Code, http.StatusBadRequest)
406
}
407
408
// createAndSingIn creates an activated username, logs them in,
409
// and returns their userID along with access and refresh tokens.
410
func (e *AppTestSuite) createAndSingIn(
411
	email, username, password string,
412
) (uuid.UUID, apiv1AuthSignInResponse) {
413
	uid := e.insertUser(username, email, password, true)
414
	httpResp := e.httpRequest(
415
		http.MethodPost,
416
		"/api/v1/auth/signin",
417
		e.jsonify(apiv1AuthSignInRequest{
418
			Email:    email,
419
			Password: password,
420
		}),
421
	)
422
423
	e.Equal(httpResp.Code, http.StatusOK)
424
425
	var body apiv1AuthSignInResponse
426
	e.readBodyAndUnjsonify(httpResp.Body, &body)
427
428
	return uid, body
429
}