github.com/mmrath/gobase@v0.0.1/client/test/user_handler_test.go (about) 1 package test 2 3 import ( 4 "bytes" 5 "github.com/mmrath/gobase/common/auth" 6 "github.com/mmrath/gobase/common/crypto" 7 "log" 8 "net/http" 9 "regexp" 10 "testing" 11 "time" 12 13 "github.com/dgrijalva/jwt-go" 14 "github.com/gavv/httpexpect" 15 "github.com/mmrath/gobase/model" 16 "github.com/stretchr/testify/require" 17 "github.com/stretchr/testify/suite" 18 ) 19 20 type AccountTestSuite struct { 21 TestSuite 22 } 23 24 func TestAccountSuite(t *testing.T) { 25 suite.Run(t, new(AccountTestSuite)) 26 } 27 28 func (s *AccountTestSuite) TestPing() { 29 resp, err := http.Get(s.server.URL + "/ping") 30 require.NoError(s.T(), err) 31 require.Equal(s.T(), 200, resp.StatusCode) 32 33 buf := new(bytes.Buffer) 34 _, err = buf.ReadFrom(resp.Body) 35 require.NoError(s.T(), err) 36 contents := buf.String() 37 require.Equal(s.T(), "pong", contents) 38 } 39 40 func (s *AccountTestSuite) TestSignUpActivateAndLogin() { 41 he := httpexpect.New(s.T(), s.server.URL) 42 testEmail := "test@example.com" 43 testPassword := "Secret123" 44 registerRequest := map[string]interface{}{ 45 "firstName": "Murali", 46 "lastName": "Rath", 47 "email": testEmail, 48 "password": testPassword, 49 } 50 he.POST("/api/account/register"). 51 WithJSON(registerRequest). 52 Expect(). 53 Status(http.StatusOK) 54 55 // Login should throw an error now 56 msg := s.mailer.PopLastMessage() 57 require.NotNil(s.T(), msg) 58 require.Equal(s.T(), "Activate your account", msg.Subject) 59 require.Equal(s.T(), registerRequest["email"], msg.To[0].Email) 60 61 resp := he.POST("/api/account/login"). 62 WithJSON(model.LoginRequest{Email: testEmail, Password: testPassword}). 63 Expect() 64 resp.Status(http.StatusUnauthorized) 65 resp.JSON().Path("$.details.cause").Equal("user is not activated") 66 67 re := regexp.MustCompile("/account/activate\\?key=([0-9a-f\\-]+)") 68 key := re.FindStringSubmatch(msg.Html)[1] 69 he.GET("/api/account/activate"). 70 WithQuery("key", key). 71 Expect(). 72 Status(http.StatusOK) 73 74 resp = he.POST("/api/account/login"). 75 WithJSON(model.LoginRequest{Email: testEmail, Password: testPassword}). 76 Expect() 77 resp.Status(http.StatusOK) 78 token := resp.Header("Authorization").Match("Bearer (.*)").Raw()[1] 79 80 jwtService := auth.NewJWTService(s.cfg.JWT) 81 jwtToken, err := jwtService.Decode(token) 82 require.NoError(s.T(), err) 83 err = jwtToken.Claims.Valid() 84 require.NoError(s.T(), err) 85 claims := jwtToken.Claims.(jwt.MapClaims) 86 require.NotNil(s.T(), claims["jti"]) 87 require.Equal(s.T(), testEmail, claims["sub"]) 88 } 89 90 func (s *AccountTestSuite) TestSignUpWithInvalidEmail() { 91 he := httpexpect.New(s.T(), s.server.URL) 92 signupRequest := map[string]interface{}{ 93 "firstName": "Murali", 94 "lastName": "Rath", 95 "email": "test234", 96 "password": "Secret123", 97 } 98 resp := he.POST("/api/account/register").WithJSON(signupRequest).Expect() 99 resp.Status(http.StatusBadRequest) 100 resp.JSON().Path("$.details.fieldErrors[0].field").Equal("email") 101 resp.JSON().Path("$.details.fieldErrors[0].message").Equal("must be a valid email address") 102 } 103 104 func (s *AccountTestSuite) TestSignUpWithDuplicateEmail() { 105 he := httpexpect.New(s.T(), s.server.URL) 106 signupRequest := map[string]interface{}{ 107 "firstName": "Murali", 108 "lastName": "Rath", 109 "email": "test@example.com", 110 "password": "Secret123", 111 } 112 resp := he.POST("/api/account/register").WithJSON(signupRequest).Expect() 113 resp.Status(http.StatusOK) 114 115 // 2nd Request 116 resp = he.POST("/api/account/register").WithJSON(signupRequest).Expect() 117 resp.Status(http.StatusBadRequest) 118 119 resp.JSON().Path("$.details.fieldErrors[0].field").Equal("email") 120 resp.JSON().Path("$.details.fieldErrors[0].message").Equal("user already exists") 121 } 122 123 func (s *AccountTestSuite) TestSignUpWithInvalidPassword() { 124 he := httpexpect.New(s.T(), s.server.URL) 125 signupRequest := map[string]interface{}{ 126 "firstName": "Murali", 127 "lastName": "Rath", 128 "email": "test@example.com", 129 "password": "123", /// too short 130 } 131 // 2nd Request 132 resp := he.POST("/api/account/register").WithJSON(signupRequest).Expect() 133 resp.Status(http.StatusBadRequest) 134 135 resp.JSON().Path("$.details.fieldErrors[0].field").Equal("password") 136 resp.JSON().Path("$.details.fieldErrors[0].message").Equal("the length must be between 6 and 32") 137 } 138 139 func (s *AccountTestSuite) TestSignUpWithLongPassword() { 140 he := httpexpect.New(s.T(), s.server.URL) 141 signupRequest := map[string]interface{}{ 142 "firstName": "Murali", 143 "lastName": "Rath", 144 "email": "test@example.com", 145 "password": "12345678901234567890123456789012", 146 } 147 // 2nd Request 148 resp := he.POST("/api/account/register").WithJSON(signupRequest).Expect() 149 resp.Status(http.StatusOK) 150 } 151 152 func (s *AccountTestSuite) TestSignUpWithTooLongPassword() { 153 he := httpexpect.New(s.T(), s.server.URL) 154 signupRequest := map[string]interface{}{ 155 "firstName": "Murali", 156 "lastName": "Rath", 157 "email": "test@example.com", 158 "password": "12345678901234567890123456789012232", 159 } 160 // 2nd Request 161 resp := he.POST("/api/account/register").WithJSON(signupRequest).Expect() 162 resp.Status(http.StatusBadRequest) 163 resp.JSON().Path("$.details.fieldErrors[0].field").Equal("password") 164 resp.JSON().Path("$.details.fieldErrors[0].message").Equal("the length must be between 6 and 32") 165 } 166 167 func (s *AccountTestSuite) TestActivateWithWrongKey() { 168 he := httpexpect.New(s.T(), s.server.URL) 169 resp := he.GET("/api/account/activate"). 170 WithQuery("key", "wrong-key"). 171 Expect(). 172 Status(http.StatusBadRequest) 173 resp.JSON().Path("$.details.cause").Equal("invalid activation token") 174 } 175 176 func (s *AccountTestSuite) TestResetPassword() { 177 testEmail := "testuser@localhost" 178 he := httpexpect.New(s.T(), s.server.URL) 179 initResetRequest := map[string]interface{}{ 180 "email": testEmail, 181 } 182 resp := he.POST("/api/account/reset-password/init"). 183 WithJSON(initResetRequest).Expect() 184 resp.Status(http.StatusOK) 185 186 // Login should throw an error now 187 msg := s.mailer.PopLastMessage() 188 require.NotNil(s.T(), msg) 189 require.Equal(s.T(), "Reset password", msg.Subject) 190 require.Equal(s.T(), initResetRequest["email"], msg.To[0].Email) 191 192 re := regexp.MustCompile("/account/reset-password\\?key=([0-9a-f\\-]+)") 193 key := re.FindStringSubmatch(msg.Html)[1] 194 195 resetRequest := map[string]interface{}{ 196 "resetToken": key, 197 "newPassword": "Secret123", 198 } 199 200 he.POST("/api/account/reset-password/finish"). 201 WithJSON(resetRequest). 202 Expect(). 203 Status(http.StatusOK) 204 205 resp = he.POST("/api/account/login"). 206 WithJSON(model.LoginRequest{Email: testEmail, Password: "Secret123"}). 207 Expect() 208 resp.Status(http.StatusOK) 209 token := resp.Header("Authorization").Match("Bearer (.*)").Raw()[1] 210 211 jwtService := auth.NewJWTService(s.cfg.JWT) 212 jwtToken, err := jwtService.Decode(token) 213 require.NoError(s.T(), err) 214 err = jwtToken.Claims.Valid() 215 require.NoError(s.T(), err) 216 claims := jwtToken.Claims.(jwt.MapClaims) 217 require.NotNil(s.T(), claims["jti"]) 218 require.Equal(s.T(), testEmail, claims["sub"]) 219 220 } 221 222 func (s *AccountTestSuite) TestWithWrongUsername() { 223 testEmail := "none@gobase.mmrath.com" 224 he := httpexpect.New(s.T(), s.server.URL) 225 226 resp := he.POST("/api/account/login"). 227 WithJSON(model.LoginRequest{Email: testEmail, Password: "Secret123"}). 228 Expect() 229 resp.Status(http.StatusUnauthorized) 230 _ = resp.Header("Authorization").NotMatch("Bearer (.*)") 231 resp.JSON().Path("$.details.cause").Equal("invalid email or password") 232 } 233 234 func (s *AccountTestSuite) TestWithWrongPassword() { 235 testEmail := "none@gobase.mmrath.com" 236 237 s.createUser(testEmail, "Secret123") 238 239 he := httpexpect.New(s.T(), s.server.URL) 240 241 resp := he.POST("/api/account/login"). 242 WithJSON(model.LoginRequest{Email: testEmail, Password: "incorrectPassword"}). 243 Expect() 244 resp.Status(http.StatusUnauthorized) 245 _ = resp.Header("Authorization").NotMatch("Bearer (.*)") 246 resp.JSON().Path("$.details.cause").Equal("invalid email or password") 247 } 248 249 func (s *AccountTestSuite) TestChangePassword() { 250 testEmail := "testuser1@localhost" 251 password := "Secret123" 252 newPassword := "NewSecret123" 253 s.createUser(testEmail, "Secret123") 254 he := httpexpect.New(s.T(), s.server.URL) 255 256 resp := he.POST("/api/account/login"). 257 WithJSON(model.LoginRequest{Email: testEmail, Password: password}). 258 Expect() 259 resp.Status(http.StatusOK) 260 token := resp.Header("Authorization").Match("Bearer (.*)").Raw()[1] 261 require.NotNil(s.T(), token) 262 263 resp = he.POST("/api/account/change-password"). 264 WithJSON(model.ChangePasswordRequest{CurrentPassword: password, NewPassword: newPassword}). 265 WithHeader("Authorization", "Bearer "+token). 266 Expect() 267 resp.Status(http.StatusOK) 268 269 // Try with the old password 270 resp = he.POST("/api/account/login"). 271 WithJSON(model.LoginRequest{Email: testEmail, Password: password}). 272 Expect() 273 resp.Status(http.StatusUnauthorized) 274 resp.JSON().Path("$.details.cause").Equal("invalid email or password") 275 276 // Try with the new password 277 resp = he.POST("/api/account/login"). 278 WithJSON(model.LoginRequest{Email: testEmail, Password: newPassword}). 279 Expect() 280 resp.Status(http.StatusOK) 281 token = resp.Header("Authorization").Match("Bearer (.*)").Raw()[1] 282 require.NotNil(s.T(), token) 283 284 } 285 286 func (s *AccountTestSuite) createUser(email string, password string) { 287 stmts := []string{ 288 `INSERT INTO public.user_account( 289 first_name, last_name, email, phone_number, active, created_at, created_by, updated_at, updated_by, version) 290 VALUES ('Test', 'Test', ?, NULL, true, current_timestamp, 'test', current_timestamp, 'test', 1) RETURNING ID`, 291 `INSERT INTO public.user_credential( 292 id, password_hash, expires_at, invalid_attempts, locked, activation_key, activation_key_expires_at, activated, reset_key, reset_key_expires_at, reset_at, updated_at, version) 293 SELECT id, ?, ?, 0, false, null, null, true, NULL, NULL, NULL, current_timestamp, 1 FROM user_account where email = ?`, 294 } 295 296 _, err := s.db.Exec(stmts[0], email) 297 if err != nil { 298 log.Printf("Error executing statement %s", stmts[0]) 299 panic(err) 300 } 301 passwordHash, err := crypto.HashPassword(crypto.SHA256([]byte(password))) 302 if err != nil { 303 log.Printf("Error hasing password") 304 panic(err) 305 } 306 _, err = s.db.Exec(stmts[1], passwordHash, time.Now().Add(time.Second*1200), email) 307 if err != nil { 308 log.Printf("Error creating credentials") 309 panic(err) 310 } 311 }