package auth import ( "errors" "testing" "time" "cardverse/config" "cardverse/internal/modules/user" "cardverse/internal/pkg/utils" ) func setupTestConfig() { config.Cfg = &config.Config{ JWTSecret: "test-secret-key", JWTExpiresHours: 1, JWTRefreshSecret: "test-refresh-secret-key", JWTRefreshExpiresHours: 168, } } func TestService_Register_Success(t *testing.T) { setupTestConfig() userRepo := &mockUserRepository{ findByEmailFunc: func(email string) (*user.User, error) { return nil, errors.New("record not found") }, createFunc: func(u *user.User) error { u.ID = 1 return nil }, } authRepo := &mockAuthRepository{} svc := NewService(userRepo, authRepo) req := RegisterRequest{ Name: "User Test", Email: "register@mail.com", Password: "password123", ConfirmPassword: "password123", } u, token, err := svc.Register(req) if err != nil { t.Fatalf("expected no error, got: %v", err) } if token == "" { t.Error("verification token should not be empty") } if u.Status != user.StatusUnverified { t.Errorf("expected status 'unverified', got: %s", u.Status) } } func TestService_Register_PasswordMismatch(t *testing.T) { setupTestConfig() userRepo := &mockUserRepository{} authRepo := &mockAuthRepository{} svc := NewService(userRepo, authRepo) req := RegisterRequest{ Name: "User Test", Email: "mismatch@mail.com", Password: "password123", ConfirmPassword: "differentpassword", } _, _, err := svc.Register(req) if !errors.Is(err, ErrPasswordMismatch) { t.Fatalf("expected ErrPasswordMismatch, got: %v", err) } } func TestService_Register_EmailAlreadyExists(t *testing.T) { setupTestConfig() userRepo := &mockUserRepository{ findByEmailFunc: func(email string) (*user.User, error) { return &user.User{ID: 1, Email: email}, nil }, } authRepo := &mockAuthRepository{} svc := NewService(userRepo, authRepo) req := RegisterRequest{ Name: "User Test", Email: "existing@mail.com", Password: "password123", ConfirmPassword: "password123", } _, _, err := svc.Register(req) if !errors.Is(err, ErrEmailAlreadyExists) { t.Fatalf("expected ErrEmailAlreadyExists, got: %v", err) } } func TestService_Login_Success(t *testing.T) { setupTestConfig() hashedPassword, _ := utils.HashPassword("rahasia123") userRepo := &mockUserRepository{ findByEmailFunc: func(email string) (*user.User, error) { return &user.User{ID: 1, Name: "Budi", Email: email, Password: hashedPassword, Role: user.RoleUser, Status: user.StatusActive}, nil }, } authRepo := &mockAuthRepository{ createRefreshTokenFunc: func(token *RefreshToken) error { return nil }, } svc := NewService(userRepo, authRepo) result, err := svc.Login(LoginRequest{Email: "budi@mail.com", Password: "rahasia123"}) if err != nil { t.Fatalf("expected no error, got: %v", err) } if result.AccessToken == "" { t.Error("access_token should not be empty") } if result.RefreshToken == "" { t.Error("refresh_token should not be empty") } if result.User.Email != "budi@mail.com" { t.Errorf("expected email budi@mail.com, got %s", result.User.Email) } } func TestService_Login_SuspendedAccount(t *testing.T) { setupTestConfig() hashedPassword, _ := utils.HashPassword("rahasia123") userRepo := &mockUserRepository{ findByEmailFunc: func(email string) (*user.User, error) { return &user.User{ID: 1, Name: "Budi", Email: email, Password: hashedPassword, Status: user.StatusSuspended}, nil }, } authRepo := &mockAuthRepository{} svc := NewService(userRepo, authRepo) _, err := svc.Login(LoginRequest{Email: "budi@mail.com", Password: "rahasia123"}) if !errors.Is(err, ErrAccountSuspended) { t.Fatalf("expected ErrAccountSuspended, got: %v", err) } } func TestService_Login_InvalidPassword(t *testing.T) { setupTestConfig() hashedPassword, _ := utils.HashPassword("rahasia123") userRepo := &mockUserRepository{ findByEmailFunc: func(email string) (*user.User, error) { return &user.User{ID: 1, Email: email, Password: hashedPassword}, nil }, } authRepo := &mockAuthRepository{} svc := NewService(userRepo, authRepo) _, err := svc.Login(LoginRequest{Email: "budi@mail.com", Password: "wrong_password"}) if !errors.Is(err, ErrInvalidCredentials) { t.Fatalf("expected ErrInvalidCredentials, got: %v", err) } } func TestService_RefreshToken_Success(t *testing.T) { setupTestConfig() validRefreshToken, _ := utils.GenerateRefreshToken(42) userRepo := &mockUserRepository{ findByIDFunc: func(id uint) (*user.User, error) { return &user.User{ID: 42, Email: "test@mail.com", Role: user.RoleUser, Status: user.StatusActive}, nil }, } authRepo := &mockAuthRepository{ findByTokenFunc: func(token string) (*RefreshToken, error) { return &RefreshToken{ UserID: 42, Token: token, ExpiresAt: time.Now().Add(1 * time.Hour), }, nil }, } svc := NewService(userRepo, authRepo) res, err := svc.RefreshToken(RefreshTokenRequest{RefreshToken: validRefreshToken}) if err != nil { t.Fatalf("RefreshToken expected no error, got: %v", err) } if res.AccessToken == "" || res.RefreshToken == "" { t.Error("new access_token and refresh_token should not be empty") } } func TestService_Logout_Success(t *testing.T) { setupTestConfig() deleted := false authRepo := &mockAuthRepository{ deleteByTokenFunc: func(token string) error { deleted = true return nil }, } svc := NewService(&mockUserRepository{}, authRepo) err := svc.Logout("token-to-delete") if err != nil { t.Fatalf("Logout expected no error, got: %v", err) } if !deleted { t.Error("deleteByTokenFunc should have been called") } } func TestService_VerifyEmail_Success(t *testing.T) { setupTestConfig() userRepo := &mockUserRepository{ findByVerificationTokenFunc: func(token string) (*user.User, error) { return &user.User{ID: 1, Status: user.StatusUnverified, IsEmailVerified: false, VerificationToken: token}, nil }, updateFunc: func(u *user.User) error { return nil }, } authRepo := &mockAuthRepository{} svc := NewService(userRepo, authRepo) err := svc.VerifyEmail("valid-token") if err != nil { t.Fatalf("expected no error, got: %v", err) } } func TestService_ForgotPassword_AndResetPassword_Success(t *testing.T) { setupTestConfig() var storedToken string userRepo := &mockUserRepository{ findByEmailFunc: func(email string) (*user.User, error) { return &user.User{ID: 1, Email: email}, nil }, findByResetTokenFunc: func(token string) (*user.User, error) { expiry := time.Now().Add(1 * time.Hour) return &user.User{ID: 1, ResetPasswordToken: token, ResetPasswordExpiresAt: &expiry}, nil }, updateFunc: func(u *user.User) error { storedToken = u.ResetPasswordToken return nil }, } authRepo := &mockAuthRepository{} svc := NewService(userRepo, authRepo) resetToken, err := svc.ForgotPassword("user@mail.com") if err != nil { t.Fatalf("ForgotPassword expected no error, got: %v", err) } if resetToken == "" { t.Fatal("resetToken should not be empty") } if storedToken != resetToken { t.Errorf("expected storedToken to be %s, got %s", resetToken, storedToken) } err = svc.ResetPassword(ResetPasswordRequest{ Token: resetToken, NewPassword: "newpassword123", ConfirmNewPassword: "newpassword123", }) if err != nil { t.Fatalf("ResetPassword expected no error, got: %v", err) } }