all repos

onasty @ 2e4e6ff

a one-time notes service

onasty/e2e/apiv1_auth_test.go (view raw)

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