package service_test import ( "context" "fmt" "testing" "time" "github.com/user-management-system/internal/auth" "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" ) // ============================================================================= // Auth Core Methods Tests - Phase 1: Coverage to 35% // ============================================================================= type authTestEnv struct { db *gorm.DB authSvc *service.AuthService userSvc *service.UserService } func setupAuthTestEnv(t *testing.T) *authTestEnv { t.Helper() dsn := fmt.Sprintf("file:authtest_%s_%d?mode=memory&cache=shared", sanitizeTestName(t.Name()), time.Now().UnixNano()) db, err := gorm.Open(gormsqlite.New(gormsqlite.Config{ DriverName: "sqlite", DSN: dsn, }), &gorm.Config{ Logger: logger.Default.LogMode(logger.Silent), }) if err != nil { t.Skipf("skipping test (SQLite unavailable): %v", err) return nil } db.Exec("PRAGMA journal_mode=WAL") if err := db.AutoMigrate( &domain.User{}, &domain.Role{}, &domain.UserRole{}, &domain.LoginLog{}, &domain.PasswordHistory{}, ); err != nil { t.Fatalf("db migration failed: %v", err) } // Seed roles for _, role := range domain.PredefinedRoles { if err := db.Create(&role).Error; err != nil { t.Fatalf("seed role %s failed: %v", role.Code, err) } } jwtManager, _ := auth.NewJWTWithOptions(auth.JWTOptions{ HS256Secret: fmt.Sprintf("test-secret-%d", time.Now().UnixNano()), AccessTokenExpire: 15 * time.Minute, RefreshTokenExpire: 7 * 24 * time.Hour, }) l1Cache := cache.NewL1Cache() l2Cache := cache.NewRedisCache(false) cacheManager := cache.NewCacheManager(l1Cache, l2Cache) userRepo := repository.NewUserRepository(db) roleRepo := repository.NewRoleRepository(db) userRoleRepo := repository.NewUserRoleRepository(db) passwordHistoryRepo := repository.NewPasswordHistoryRepository(db) authSvc := service.NewAuthService(userRepo, nil, jwtManager, cacheManager, 8, 5, 15*time.Minute) authSvc.SetRoleRepositories(userRoleRepo, roleRepo) userSvc := service.NewUserService(userRepo, userRoleRepo, roleRepo, passwordHistoryRepo) t.Cleanup(func() { if sqlDB, err := db.DB(); err == nil { sqlDB.Close() } }) return &authTestEnv{ db: db, authSvc: authSvc, userSvc: userSvc, } } // Test RefreshToken method func TestAuthService_RefreshToken(t *testing.T) { env := setupAuthTestEnv(t) if env == nil { return } ctx := context.Background() // First register a user req := &service.RegisterRequest{ Username: "refreshuser", Password: "Test123!", Email: "refresh@test.com", } authResp, err := env.authSvc.Register(ctx, req) if err != nil { t.Fatalf("Register failed: %v", err) } userID := authResp.ID // Login to get refresh token loginResp, err := env.authSvc.Login(ctx, &service.LoginRequest{ Username: "refreshuser", Password: "Test123!", }, "127.0.0.1") if err != nil { t.Fatalf("Login failed: %v", err) } refreshToken := loginResp.RefreshToken t.Run("Refresh token success", func(t *testing.T) { resp, err := env.authSvc.RefreshToken(ctx, refreshToken) if err != nil { t.Fatalf("RefreshToken failed: %v", err) } if resp.AccessToken == "" { t.Error("Expected access token to be returned") } if resp.RefreshToken == "" { t.Error("Expected refresh token to be returned") } }) t.Run("Refresh token with invalid token", func(t *testing.T) { _, err := env.authSvc.RefreshToken(ctx, "invalid-token") if err == nil { t.Error("Expected error for invalid token") } }) t.Run("Refresh token with empty token", func(t *testing.T) { _, err := env.authSvc.RefreshToken(ctx, "") if err == nil { t.Error("Expected error for empty token") } }) t.Run("Refresh token for locked user", func(t *testing.T) { // Lock the user env.userSvc.UpdateStatus(ctx, userID, domain.UserStatusLocked) // Try to refresh token - should fail _, err := env.authSvc.RefreshToken(ctx, refreshToken) if err == nil { t.Error("Expected error for locked user") } // Unlock user for cleanup env.userSvc.UpdateStatus(ctx, userID, domain.UserStatusActive) }) t.Run("Refresh token with nil service", func(t *testing.T) { var nilSvc *service.AuthService _, err := nilSvc.RefreshToken(ctx, refreshToken) if err == nil { t.Error("Expected error for nil service") } }) } // Test GetUserInfo method func TestAuthService_GetUserInfo(t *testing.T) { env := setupAuthTestEnv(t) if env == nil { return } ctx := context.Background() // Register a user req := &service.RegisterRequest{ Username: "infouser", Password: "Test123!", Email: "info@test.com", Nickname: "Info User", } authResp, err := env.authSvc.Register(ctx, req) if err != nil { t.Fatalf("Register failed: %v", err) } userID := authResp.ID t.Run("Get user info success", func(t *testing.T) { info, err := env.authSvc.GetUserInfo(ctx, userID) if err != nil { t.Fatalf("GetUserInfo failed: %v", err) } if info.ID != userID { t.Errorf("Expected user ID %d, got %d", userID, info.ID) } if info.Username != "infouser" { t.Errorf("Expected username 'infouser', got %s", info.Username) } if info.Nickname != "Info User" { t.Errorf("Expected nickname 'Info User', got %s", info.Nickname) } if info.Email != "info@test.com" { t.Errorf("Expected email 'info@test.com', got %s", info.Email) } }) t.Run("Get user info from cache", func(t *testing.T) { // Second call should hit cache info, err := env.authSvc.GetUserInfo(ctx, userID) if err != nil { t.Fatalf("GetUserInfo from cache failed: %v", err) } if info.ID != userID { t.Errorf("Expected user ID %d, got %d", userID, info.ID) } }) t.Run("Get user info for non-existent user", func(t *testing.T) { _, err := env.authSvc.GetUserInfo(ctx, 99999) if err == nil { t.Error("Expected error for non-existent user") } }) t.Run("Get user info with nil service", func(t *testing.T) { var nilSvc *service.AuthService _, err := nilSvc.GetUserInfo(ctx, userID) if err == nil { t.Error("Expected error for nil service") } }) t.Run("Get user info with zero ID", func(t *testing.T) { _, err := env.authSvc.GetUserInfo(ctx, 0) if err == nil { t.Error("Expected error for zero user ID") } }) } // Test Logout method func TestAuthService_Logout(t *testing.T) { env := setupAuthTestEnv(t) if env == nil { return } ctx := context.Background() // Register and login a user req := &service.RegisterRequest{ Username: "logoutuser", Password: "Test123!", } _, err := env.authSvc.Register(ctx, req) if err != nil { t.Fatalf("Register failed: %v", err) } loginResp, err := env.authSvc.Login(ctx, &service.LoginRequest{ Username: "logoutuser", Password: "Test123!", }, "127.0.0.1") if err != nil { t.Fatalf("Login failed: %v", err) } t.Run("Logout success", func(t *testing.T) { err := env.authSvc.Logout(ctx, "logoutuser", &service.LogoutRequest{ AccessToken: loginResp.AccessToken, RefreshToken: loginResp.RefreshToken, }) if err != nil { t.Errorf("Logout failed: %v", err) } }) t.Run("Logout with nil request", func(t *testing.T) { err := env.authSvc.Logout(ctx, "logoutuser", nil) if err != nil { t.Errorf("Logout with nil request should not error: %v", err) } }) t.Run("Logout with nil service", func(t *testing.T) { var nilSvc *service.AuthService err := nilSvc.Logout(ctx, "logoutuser", &service.LogoutRequest{ AccessToken: loginResp.AccessToken, RefreshToken: loginResp.RefreshToken, }) if err != nil { t.Errorf("Logout with nil service should not error: %v", err) } }) }