package service_test import ( "context" "testing" "github.com/user-management-system/internal/cache" "github.com/user-management-system/internal/domain" "github.com/user-management-system/internal/repository" "github.com/user-management-system/internal/service" gormsqlite "gorm.io/driver/sqlite" "gorm.io/gorm" "gorm.io/gorm/logger" ) // ============================================================================= // Password Reset Service Tests // ============================================================================= func setupPasswordResetTestEnv(t *testing.T) (*service.PasswordResetService, *gorm.DB) { t.Helper() db, err := gorm.Open(gormsqlite.New(gormsqlite.Config{ DriverName: "sqlite", DSN: "file:pwdreset_test?mode=memory&cache=shared", }), &gorm.Config{ Logger: logger.Default.LogMode(logger.Silent), }) if err != nil { t.Fatalf("failed to connect database: %v", err) } if err := db.AutoMigrate(&domain.User{}); err != nil { t.Fatalf("failed to migrate: %v", err) } userRepo := repository.NewUserRepository(db) l1Cache := cache.NewL1Cache() l2Cache := cache.NewRedisCache(false) cacheManager := cache.NewCacheManager(l1Cache, l2Cache) cfg := service.DefaultPasswordResetConfig() svc := service.NewPasswordResetService(userRepo, cacheManager, cfg) return svc, db } func TestPasswordResetService_ForgotPassword(t *testing.T) { svc, db := setupPasswordResetTestEnv(t) ctx := context.Background() // Create test user with email email := "reset@test.com" user := &domain.User{ Username: "resetuser", Password: "$2a$10$hash", Email: &email, Status: domain.UserStatusActive, } db.Create(user) t.Run("Forgot password for existing email", func(t *testing.T) { err := svc.ForgotPassword(ctx, "reset@test.com") // Should not return error even if email sending fails _ = err t.Logf("ForgotPassword returned: %v", err) }) t.Run("Forgot password for non-existent email", func(t *testing.T) { err := svc.ForgotPassword(ctx, "nonexistent@test.com") // Should return nil to avoid user enumeration if err != nil { t.Errorf("Expected nil for non-existent email, got: %v", err) } }) t.Run("Forgot password with empty email", func(t *testing.T) { err := svc.ForgotPassword(ctx, "") _ = err t.Logf("ForgotPassword with empty email returned: %v", err) }) } func TestPasswordResetService_ResetPassword(t *testing.T) { svc, _ := setupPasswordResetTestEnv(t) ctx := context.Background() t.Run("Reset password with invalid token", func(t *testing.T) { err := svc.ResetPassword(ctx, "invalid_token", "NewPassword123!") if err == nil { t.Error("Expected error for invalid token") } }) t.Run("Reset password with empty token", func(t *testing.T) { err := svc.ResetPassword(ctx, "", "NewPassword123!") if err == nil { t.Error("Expected error for empty token") } }) t.Run("Reset password with empty password", func(t *testing.T) { err := svc.ResetPassword(ctx, "some_token", "") if err == nil { t.Error("Expected error for empty password") } }) } func TestPasswordResetService_ValidateResetToken(t *testing.T) { svc, _ := setupPasswordResetTestEnv(t) ctx := context.Background() t.Run("Validate invalid token", func(t *testing.T) { valid, err := svc.ValidateResetToken(ctx, "invalid_token") if err != nil { t.Fatalf("ValidateResetToken should not return error: %v", err) } if valid { t.Error("Expected token to be invalid") } }) t.Run("Validate empty token", func(t *testing.T) { _, err := svc.ValidateResetToken(ctx, "") if err == nil { t.Error("Expected error for empty token") } }) } func TestPasswordResetService_ForgotPasswordByPhone(t *testing.T) { svc, db := setupPasswordResetTestEnv(t) ctx := context.Background() // Create test user with phone phone := "13800138000" user := &domain.User{ Username: "phoneuser", Password: "$2a$10$hash", Phone: &phone, Status: domain.UserStatusActive, } db.Create(user) t.Run("Forgot password by phone for existing user", func(t *testing.T) { _, err := svc.ForgotPasswordByPhone(ctx, "13800138000") // May fail if SMS service not configured _ = err t.Logf("ForgotPasswordByPhone returned: %v", err) }) t.Run("Forgot password by phone for non-existent user", func(t *testing.T) { _, err := svc.ForgotPasswordByPhone(ctx, "19999999999") // Should return nil to avoid user enumeration _ = err t.Logf("ForgotPasswordByPhone non-existent returned: %v", err) }) } func TestPasswordResetService_ResetPasswordByPhone(t *testing.T) { svc, _ := setupPasswordResetTestEnv(t) ctx := context.Background() t.Run("Reset password by phone with invalid code", func(t *testing.T) { req := &service.ResetPasswordByPhoneRequest{ Phone: "13800138000", Code: "invalid_code", NewPassword: "NewPassword123!", } err := svc.ResetPasswordByPhone(ctx, req) if err == nil { t.Error("Expected error for invalid code") } }) t.Run("Reset password by phone with empty fields", func(t *testing.T) { req := &service.ResetPasswordByPhoneRequest{} err := svc.ResetPasswordByPhone(ctx, req) if err == nil { t.Error("Expected error for empty fields") } }) } func TestPasswordResetService_WithPasswordHistoryRepo(t *testing.T) { svc, _ := setupPasswordResetTestEnv(t) t.Run("WithPasswordHistoryRepo sets repository", func(t *testing.T) { result := svc.WithPasswordHistoryRepo(nil) if result == nil { t.Error("Expected service to be returned") } }) } // ============================================================================= // ResetPassword Extended Tests // ============================================================================= func TestPasswordResetService_ResetPassword_Extended(t *testing.T) { svc, _ := setupPasswordResetTestEnv(t) ctx := context.Background() t.Run("ResetPassword with empty token", func(t *testing.T) { err := svc.ResetPassword(ctx, "", "NewPassword123!") if err == nil { t.Error("Expected error for empty token") } }) t.Run("ResetPassword with empty password", func(t *testing.T) { err := svc.ResetPassword(ctx, "sometoken", "") if err == nil { t.Error("Expected error for empty password") } }) t.Run("ResetPassword with weak password", func(t *testing.T) { err := svc.ResetPassword(ctx, "sometoken", "weak") if err == nil { t.Error("Expected error for weak password") } }) t.Run("ResetPassword with invalid token", func(t *testing.T) { err := svc.ResetPassword(ctx, "invalid_token", "NewPassword123!") if err == nil { t.Error("Expected error for invalid token") } }) } func TestPasswordResetService_ResetPasswordByPhone_Extended(t *testing.T) { svc, _ := setupPasswordResetTestEnv(t) ctx := context.Background() t.Run("ResetPasswordByPhone with empty phone", func(t *testing.T) { req := &service.ResetPasswordByPhoneRequest{ Code: "123456", NewPassword: "NewPassword123!", } err := svc.ResetPasswordByPhone(ctx, req) if err == nil { t.Error("Expected error for empty phone") } }) t.Run("ResetPasswordByPhone with empty code", func(t *testing.T) { req := &service.ResetPasswordByPhoneRequest{ Phone: "13800138000", NewPassword: "NewPassword123!", } err := svc.ResetPasswordByPhone(ctx, req) if err == nil { t.Error("Expected error for empty code") } }) }