- 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)
239 lines
5.8 KiB
Go
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)
|
|
}
|
|
})
|
|
}
|