package service_test import ( "context" "testing" "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" ) // ============================================================================= // TOTP Service Tests // ============================================================================= func setupTOTPTestEnv(t *testing.T) (*service.TOTPService, *gorm.DB) { t.Helper() db, err := gorm.Open(gormsqlite.New(gormsqlite.Config{ DriverName: "sqlite", DSN: "file:totp_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) totpSvc := service.NewTOTPService(userRepo) return totpSvc, db } func TestTOTPService_SetupTOTP(t *testing.T) { svc, db := setupTOTPTestEnv(t) ctx := context.Background() // Create test user user := &domain.User{ Username: "totpuser", Password: "$2a$10$hash", Status: domain.UserStatusActive, } db.Create(user) t.Run("Setup TOTP for non-existent user", func(t *testing.T) { _, err := svc.SetupTOTP(ctx, 99999) if err == nil { t.Error("Expected error for non-existent user") } }) t.Run("Setup TOTP for existing user", func(t *testing.T) { resp, err := svc.SetupTOTP(ctx, user.ID) if err != nil { t.Fatalf("SetupTOTP failed: %v", err) } if resp.Secret == "" { t.Error("Expected secret to be returned") } if resp.QRCodeBase64 == "" { t.Error("Expected QR code to be returned") } if len(resp.RecoveryCodes) == 0 { t.Error("Expected recovery codes to be returned") } }) t.Run("Setup TOTP for user with TOTP already enabled", func(t *testing.T) { // Enable TOTP for user first db.Model(&domain.User{}).Where("id = ?", user.ID).Update("totp_enabled", true) _, err := svc.SetupTOTP(ctx, user.ID) if err == nil { t.Error("Expected error for user with TOTP already enabled") } }) } func TestTOTPService_GetTOTPStatus(t *testing.T) { svc, db := setupTOTPTestEnv(t) ctx := context.Background() // Create test user user := &domain.User{ Username: "totpstatususer", Password: "$2a$10$hash", Status: domain.UserStatusActive, } db.Create(user) t.Run("Get TOTP status for non-existent user", func(t *testing.T) { _, err := svc.GetTOTPStatus(ctx, 99999) if err == nil { t.Error("Expected error for non-existent user") } }) t.Run("Get TOTP status for existing user", func(t *testing.T) { enabled, err := svc.GetTOTPStatus(ctx, user.ID) if err != nil { t.Fatalf("GetTOTPStatus failed: %v", err) } if enabled { t.Error("Expected TOTP to be disabled by default") } }) } func TestTOTPService_EnableTOTP(t *testing.T) { svc, db := setupTOTPTestEnv(t) ctx := context.Background() // Create test user user := &domain.User{ Username: "enabletotpuser", Password: "$2a$10$hash", Status: domain.UserStatusActive, } db.Create(user) t.Run("Enable TOTP for non-existent user", func(t *testing.T) { err := svc.EnableTOTP(ctx, 99999, "123456") if err == nil { t.Error("Expected error for non-existent user") } }) t.Run("Enable TOTP without setup", func(t *testing.T) { err := svc.EnableTOTP(ctx, user.ID, "123456") if err == nil { t.Error("Expected error when TOTP not set up") } }) t.Run("Enable TOTP with empty code", func(t *testing.T) { // Setup TOTP first user2 := &domain.User{ Username: "enabletotpuser2", Password: "$2a$10$hash", Status: domain.UserStatusActive, TOTPSecret: "JBSWY3DPEHPK3PXP", } db.Create(user2) err := svc.EnableTOTP(ctx, user2.ID, "") if err == nil { t.Error("Expected error for empty code") } }) } func TestTOTPService_DisableTOTP(t *testing.T) { svc, db := setupTOTPTestEnv(t) ctx := context.Background() // Create test user user := &domain.User{ Username: "disabletotpuser", Password: "$2a$10$hash", Status: domain.UserStatusActive, TOTPEnabled: true, TOTPSecret: "testsecret", } db.Create(user) t.Run("Disable TOTP for non-existent user", func(t *testing.T) { err := svc.DisableTOTP(ctx, 99999, "123456") if err == nil { t.Error("Expected error for non-existent user") } }) t.Run("Disable TOTP without setup", func(t *testing.T) { // Create user without TOTP user2 := &domain.User{ Username: "nototpsetup", Password: "$2a$10$hash", Status: domain.UserStatusActive, } db.Create(user2) err := svc.DisableTOTP(ctx, user2.ID, "123456") if err == nil { t.Error("Expected error when TOTP not enabled") } }) t.Run("Disable TOTP with wrong code", func(t *testing.T) { err := svc.DisableTOTP(ctx, user.ID, "wrongcode") if err == nil { t.Error("Expected error for wrong code") } }) t.Run("Disable TOTP with empty code", func(t *testing.T) { err := svc.DisableTOTP(ctx, user.ID, "") if err == nil { t.Error("Expected error for empty code") } }) } func TestTOTPService_VerifyTOTP(t *testing.T) { svc, db := setupTOTPTestEnv(t) ctx := context.Background() // Create test user without TOTP user := &domain.User{ Username: "verifytotpuser", Password: "$2a$10$hash", Status: domain.UserStatusActive, } db.Create(user) t.Run("Verify TOTP for non-existent user", func(t *testing.T) { err := svc.VerifyTOTP(ctx, 99999, "123456") if err == nil { t.Error("Expected error for non-existent user") } }) t.Run("Verify TOTP when disabled", func(t *testing.T) { // When TOTP is disabled, VerifyTOTP should return nil (no error) err := svc.VerifyTOTP(ctx, user.ID, "123456") if err != nil { t.Errorf("Expected no error when TOTP disabled, got: %v", err) } }) }