Files
user-system/internal/service/totp_test.go
long-agent 582ad7a069 test: add comprehensive test coverage and improve code quality
- Add new test files for auth, service, and handler modules
- Improve test organization and coverage
- Refactor code for better maintainability
- Add captcha, settings, stats, and theme handler tests
- Add auth module tests (CAS, OAuth, password, SSO, state)
- Add service layer tests for auth, export, permissions, roles
- All Go tests pass (exit code 0)
- All frontend tests pass (325 tests in 59 files)
2026-04-17 20:43:50 +08:00

239 lines
5.8 KiB
Go

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)
}
})
}