- IsAdminBootstrapRequired: userRepo.GetByID 循环 → GetByIDs 批量 - AssignRoles: roleRepo.GetByID 循环 → GetByIDs 批量 - 在 userRepositoryInterface 补充 GetByIDs 方法签名
480 lines
12 KiB
Go
480 lines
12 KiB
Go
package service_test
|
|
|
|
import (
|
|
"context"
|
|
"testing"
|
|
|
|
"github.com/user-management-system/internal/auth"
|
|
"github.com/user-management-system/internal/domain"
|
|
"github.com/user-management-system/internal/service"
|
|
)
|
|
|
|
// =============================================================================
|
|
// UserService CRUD Tests - Phase 1 (Simplified)
|
|
// =============================================================================
|
|
|
|
func TestUserService_List(t *testing.T) {
|
|
env := setupAuthTestEnv(t)
|
|
if env == nil {
|
|
return
|
|
}
|
|
ctx := context.Background()
|
|
|
|
t.Run("List users", func(t *testing.T) {
|
|
// Create multiple users
|
|
for i := 0; i < 3; i++ {
|
|
user := &domain.User{
|
|
Username: "listuser_" + string(rune('a'+i)),
|
|
Password: "$2a$10$dummyhash",
|
|
Status: domain.UserStatusActive,
|
|
}
|
|
env.userSvc.Create(ctx, user)
|
|
}
|
|
|
|
// List
|
|
users, total, err := env.userSvc.List(ctx, 0, 10)
|
|
if err != nil {
|
|
t.Fatalf("List failed: %v", err)
|
|
}
|
|
if len(users) == 0 {
|
|
t.Error("Expected users to be returned")
|
|
}
|
|
if total < 3 {
|
|
t.Errorf("Expected total >= 3, got %d", total)
|
|
}
|
|
})
|
|
|
|
t.Run("List users with pagination", func(t *testing.T) {
|
|
users, total, err := env.userSvc.List(ctx, 0, 2)
|
|
if err != nil {
|
|
t.Fatalf("List with pagination failed: %v", err)
|
|
}
|
|
if len(users) > 2 {
|
|
t.Errorf("Expected max 2 users, got %d", len(users))
|
|
}
|
|
if total == 0 {
|
|
t.Error("Expected total > 0")
|
|
}
|
|
})
|
|
|
|
t.Run("List users with invalid pagination", func(t *testing.T) {
|
|
// Test with negative offset
|
|
_, _, err := env.userSvc.List(ctx, -1, 10)
|
|
if err != nil {
|
|
t.Errorf("List with negative offset should handle it gracefully: %v", err)
|
|
}
|
|
|
|
// Test with zero limit
|
|
_, _, err = env.userSvc.List(ctx, 0, 0)
|
|
if err != nil {
|
|
t.Errorf("List with zero limit should handle it gracefully: %v", err)
|
|
}
|
|
})
|
|
}
|
|
|
|
func TestUserService_GetByID(t *testing.T) {
|
|
env := setupAuthTestEnv(t)
|
|
if env == nil {
|
|
return
|
|
}
|
|
ctx := context.Background()
|
|
|
|
t.Run("Get user by ID success", func(t *testing.T) {
|
|
// Create user
|
|
user := &domain.User{
|
|
Username: "getbyid",
|
|
Password: "$2a$10$dummyhash",
|
|
Status: domain.UserStatusActive,
|
|
}
|
|
err := env.userSvc.Create(ctx, user)
|
|
if err != nil {
|
|
t.Fatalf("Create failed: %v", err)
|
|
}
|
|
|
|
// Get by ID
|
|
found, err := env.userSvc.GetByID(ctx, user.ID)
|
|
if err != nil {
|
|
t.Fatalf("GetByID failed: %v", err)
|
|
}
|
|
if found.Username != "getbyid" {
|
|
t.Errorf("Expected username 'getbyid', got %s", found.Username)
|
|
}
|
|
})
|
|
|
|
t.Run("Get user by ID not found", func(t *testing.T) {
|
|
_, err := env.userSvc.GetByID(ctx, 99999)
|
|
if err == nil {
|
|
t.Error("Expected error for non-existent user")
|
|
}
|
|
})
|
|
|
|
t.Run("Get user by ID with zero ID", func(t *testing.T) {
|
|
_, err := env.userSvc.GetByID(ctx, 0)
|
|
if err == nil {
|
|
t.Error("Expected error for zero ID")
|
|
}
|
|
})
|
|
}
|
|
|
|
func TestUserService_Update(t *testing.T) {
|
|
env := setupAuthTestEnv(t)
|
|
if env == nil {
|
|
return
|
|
}
|
|
ctx := context.Background()
|
|
|
|
t.Run("Update user success", func(t *testing.T) {
|
|
// Create user
|
|
user := &domain.User{
|
|
Username: "updateuser",
|
|
Password: "$2a$10$dummyhash",
|
|
Status: domain.UserStatusActive,
|
|
Nickname: "Old Nickname",
|
|
}
|
|
err := env.userSvc.Create(ctx, user)
|
|
if err != nil {
|
|
t.Fatalf("Create failed: %v", err)
|
|
}
|
|
|
|
// Update
|
|
user.Nickname = "New Nickname"
|
|
err = env.userSvc.Update(ctx, user)
|
|
if err != nil {
|
|
t.Fatalf("Update failed: %v", err)
|
|
}
|
|
|
|
// Verify
|
|
updated, _ := env.userSvc.GetByID(ctx, user.ID)
|
|
if updated.Nickname != "New Nickname" {
|
|
t.Errorf("Expected nickname 'New Nickname', got %s", updated.Nickname)
|
|
}
|
|
})
|
|
|
|
t.Run("Update non-existent user", func(t *testing.T) {
|
|
user := &domain.User{
|
|
ID: 99999,
|
|
Username: "nonexistent",
|
|
Password: "$2a$10$dummyhash",
|
|
Status: domain.UserStatusActive,
|
|
}
|
|
err := env.userSvc.Update(ctx, user)
|
|
// 实际实现可能静默处理
|
|
_ = err
|
|
t.Logf("Update non-existent user returned: %v", err)
|
|
})
|
|
}
|
|
|
|
func TestUserService_UpdateStatus(t *testing.T) {
|
|
env := setupAuthTestEnv(t)
|
|
if env == nil {
|
|
return
|
|
}
|
|
ctx := context.Background()
|
|
|
|
t.Run("Update status success", func(t *testing.T) {
|
|
// Create user
|
|
user := &domain.User{
|
|
Username: "statususer",
|
|
Password: "$2a$10$dummyhash",
|
|
Status: domain.UserStatusActive,
|
|
}
|
|
err := env.userSvc.Create(ctx, user)
|
|
if err != nil {
|
|
t.Fatalf("Create failed: %v", err)
|
|
}
|
|
|
|
// Update status to locked
|
|
err = env.userSvc.UpdateStatus(ctx, user.ID, domain.UserStatusLocked)
|
|
if err != nil {
|
|
t.Fatalf("UpdateStatus failed: %v", err)
|
|
}
|
|
|
|
// Verify
|
|
updated, _ := env.userSvc.GetByID(ctx, user.ID)
|
|
if updated.Status != domain.UserStatusLocked {
|
|
t.Errorf("Expected status %d, got %d", domain.UserStatusLocked, updated.Status)
|
|
}
|
|
})
|
|
|
|
t.Run("Update status for non-existent user", func(t *testing.T) {
|
|
err := env.userSvc.UpdateStatus(ctx, 99999, domain.UserStatusLocked)
|
|
// 实际实现可能静默处理
|
|
_ = err
|
|
t.Logf("UpdateStatus non-existent user returned: %v", err)
|
|
})
|
|
}
|
|
|
|
func TestUserService_Delete(t *testing.T) {
|
|
env := setupAuthTestEnv(t)
|
|
if env == nil {
|
|
return
|
|
}
|
|
ctx := context.Background()
|
|
|
|
t.Run("Delete user success", func(t *testing.T) {
|
|
// Create user
|
|
user := &domain.User{
|
|
Username: "deleteuser",
|
|
Password: "$2a$10$dummyhash",
|
|
Status: domain.UserStatusActive,
|
|
}
|
|
err := env.userSvc.Create(ctx, user)
|
|
if err != nil {
|
|
t.Fatalf("Create failed: %v", err)
|
|
}
|
|
|
|
// Delete
|
|
err = env.userSvc.Delete(ctx, user.ID)
|
|
if err != nil {
|
|
t.Fatalf("Delete failed: %v", err)
|
|
}
|
|
|
|
// Verify deletion
|
|
_, err = env.userSvc.GetByID(ctx, user.ID)
|
|
if err == nil {
|
|
t.Error("Expected error for deleted user")
|
|
}
|
|
})
|
|
|
|
t.Run("Delete non-existent user", func(t *testing.T) {
|
|
err := env.userSvc.Delete(ctx, 99999)
|
|
// 实际实现可能静默处理
|
|
_ = err
|
|
t.Logf("Delete non-existent user returned: %v", err)
|
|
})
|
|
}
|
|
|
|
func TestUserService_ChangePassword(t *testing.T) {
|
|
env := setupAuthTestEnv(t)
|
|
if env == nil {
|
|
return
|
|
}
|
|
ctx := context.Background()
|
|
|
|
t.Run("Change password success", func(t *testing.T) {
|
|
// Create user with known password
|
|
hashedPassword, _ := auth.HashPassword("OldPassword123!")
|
|
user := &domain.User{
|
|
Username: "changepwd",
|
|
Password: hashedPassword,
|
|
Status: domain.UserStatusActive,
|
|
}
|
|
env.userSvc.Create(ctx, user)
|
|
|
|
// Change password
|
|
err := env.userSvc.ChangePassword(ctx, user.ID, "OldPassword123!", "NewPassword456!")
|
|
if err != nil {
|
|
t.Fatalf("ChangePassword failed: %v", err)
|
|
}
|
|
|
|
// Verify new password works
|
|
updated, _ := env.userSvc.GetByID(ctx, user.ID)
|
|
if !auth.VerifyPassword(updated.Password, "NewPassword456!") {
|
|
t.Error("New password verification failed")
|
|
}
|
|
})
|
|
|
|
t.Run("Change password with wrong old password", func(t *testing.T) {
|
|
hashedPassword, _ := auth.HashPassword("CorrectPassword123!")
|
|
user := &domain.User{
|
|
Username: "wrongpwd",
|
|
Password: hashedPassword,
|
|
Status: domain.UserStatusActive,
|
|
}
|
|
env.userSvc.Create(ctx, user)
|
|
|
|
err := env.userSvc.ChangePassword(ctx, user.ID, "WrongPassword!", "NewPassword456!")
|
|
if err == nil {
|
|
t.Error("Expected error for wrong old password")
|
|
}
|
|
})
|
|
|
|
t.Run("Change password for non-existent user", func(t *testing.T) {
|
|
err := env.userSvc.ChangePassword(ctx, 99999, "old", "NewPassword123!")
|
|
if err == nil {
|
|
t.Error("Expected error for non-existent user")
|
|
}
|
|
})
|
|
|
|
t.Run("Change password with empty old password", func(t *testing.T) {
|
|
user := &domain.User{
|
|
Username: "emptypwd",
|
|
Password: "$2a$10$dummyhash",
|
|
Status: domain.UserStatusActive,
|
|
}
|
|
env.userSvc.Create(ctx, user)
|
|
|
|
err := env.userSvc.ChangePassword(ctx, user.ID, "", "NewPassword123!")
|
|
if err == nil {
|
|
t.Error("Expected error for empty old password")
|
|
}
|
|
})
|
|
|
|
t.Run("Change password with empty new password", func(t *testing.T) {
|
|
hashedPassword, _ := auth.HashPassword("OldPassword123!")
|
|
user := &domain.User{
|
|
Username: "emptynewpwd",
|
|
Password: hashedPassword,
|
|
Status: domain.UserStatusActive,
|
|
}
|
|
env.userSvc.Create(ctx, user)
|
|
|
|
err := env.userSvc.ChangePassword(ctx, user.ID, "OldPassword123!", "")
|
|
if err == nil {
|
|
t.Error("Expected error for empty new password")
|
|
}
|
|
})
|
|
|
|
t.Run("Change password with weak new password", func(t *testing.T) {
|
|
hashedPassword, _ := auth.HashPassword("OldPassword123!")
|
|
user := &domain.User{
|
|
Username: "weakpwd",
|
|
Password: hashedPassword,
|
|
Status: domain.UserStatusActive,
|
|
}
|
|
env.userSvc.Create(ctx, user)
|
|
|
|
err := env.userSvc.ChangePassword(ctx, user.ID, "OldPassword123!", "123")
|
|
if err == nil {
|
|
t.Error("Expected error for weak new password")
|
|
}
|
|
})
|
|
}
|
|
|
|
func TestUserService_AdminResetPassword(t *testing.T) {
|
|
env := setupAuthTestEnv(t)
|
|
if env == nil {
|
|
return
|
|
}
|
|
ctx := context.Background()
|
|
|
|
t.Run("Admin reset password success", func(t *testing.T) {
|
|
hashedPassword, _ := auth.HashPassword("OldPassword123!")
|
|
user := &domain.User{
|
|
Username: "adminresetpwd",
|
|
Password: hashedPassword,
|
|
Status: domain.UserStatusActive,
|
|
}
|
|
env.userSvc.Create(ctx, user)
|
|
|
|
err := env.userSvc.AdminResetPassword(ctx, user.ID, "ResetPassword456!")
|
|
if err != nil {
|
|
t.Fatalf("AdminResetPassword failed: %v", err)
|
|
}
|
|
|
|
updated, _ := env.userSvc.GetByID(ctx, user.ID)
|
|
if !auth.VerifyPassword(updated.Password, "ResetPassword456!") {
|
|
t.Error("reset password verification failed")
|
|
}
|
|
if auth.VerifyPassword(updated.Password, "OldPassword123!") {
|
|
t.Error("old password should no longer work after admin reset")
|
|
}
|
|
})
|
|
|
|
t.Run("Admin reset password for non-existent user", func(t *testing.T) {
|
|
err := env.userSvc.AdminResetPassword(ctx, 99999, "ResetPassword456!")
|
|
if err == nil {
|
|
t.Error("Expected error for non-existent user")
|
|
}
|
|
})
|
|
}
|
|
|
|
func TestUserService_BatchUpdateStatus(t *testing.T) {
|
|
env := setupAuthTestEnv(t)
|
|
if env == nil {
|
|
return
|
|
}
|
|
ctx := context.Background()
|
|
|
|
// Create test users
|
|
user1 := &domain.User{Username: "batch1", Password: "$2a$10$hash", Status: domain.UserStatusActive}
|
|
user2 := &domain.User{Username: "batch2", Password: "$2a$10$hash", Status: domain.UserStatusActive}
|
|
env.userSvc.Create(ctx, user1)
|
|
env.userSvc.Create(ctx, user2)
|
|
|
|
t.Run("Batch update status", func(t *testing.T) {
|
|
_, err := env.userSvc.BatchUpdateStatus(ctx, &service.BatchUpdateStatusRequest{
|
|
IDs: []int64{user1.ID, user2.ID},
|
|
Status: domain.UserStatusLocked,
|
|
})
|
|
if err != nil {
|
|
t.Fatalf("BatchUpdateStatus failed: %v", err)
|
|
}
|
|
|
|
updated1, _ := env.userSvc.GetByID(ctx, user1.ID)
|
|
if updated1.Status != domain.UserStatusLocked {
|
|
t.Error("Expected user1 status to be locked")
|
|
}
|
|
})
|
|
}
|
|
|
|
func TestUserService_Create(t *testing.T) {
|
|
env := setupAuthTestEnv(t)
|
|
if env == nil {
|
|
return
|
|
}
|
|
ctx := context.Background()
|
|
|
|
t.Run("Create user success", func(t *testing.T) {
|
|
email := "create@test.com"
|
|
user := &domain.User{
|
|
Username: "createuser",
|
|
Password: "$2a$10$hash",
|
|
Email: &email,
|
|
Status: domain.UserStatusActive,
|
|
}
|
|
err := env.userSvc.Create(ctx, user)
|
|
if err != nil {
|
|
t.Fatalf("Create failed: %v", err)
|
|
}
|
|
if user.ID == 0 {
|
|
t.Error("Expected user ID to be set")
|
|
}
|
|
})
|
|
|
|
t.Run("Create user with duplicate username", func(t *testing.T) {
|
|
user1 := &domain.User{Username: "dupcreate", Password: "$2a$10$hash", Status: domain.UserStatusActive}
|
|
env.userSvc.Create(ctx, user1)
|
|
|
|
user2 := &domain.User{Username: "dupcreate", Password: "$2a$10$hash", Status: domain.UserStatusActive}
|
|
err := env.userSvc.Create(ctx, user2)
|
|
if err == nil {
|
|
t.Error("Expected error for duplicate username")
|
|
}
|
|
})
|
|
}
|
|
|
|
func TestUserService_GetByEmail(t *testing.T) {
|
|
env := setupAuthTestEnv(t)
|
|
if env == nil {
|
|
return
|
|
}
|
|
ctx := context.Background()
|
|
|
|
t.Run("Get user by email", func(t *testing.T) {
|
|
email := "getbyemail@test.com"
|
|
user := &domain.User{
|
|
Username: "emailuser",
|
|
Password: "$2a$10$hash",
|
|
Email: &email,
|
|
Status: domain.UserStatusActive,
|
|
}
|
|
env.userSvc.Create(ctx, user)
|
|
|
|
found, err := env.userSvc.GetByEmail(ctx, "getbyemail@test.com")
|
|
if err != nil {
|
|
t.Fatalf("GetByEmail failed: %v", err)
|
|
}
|
|
if found.Username != "emailuser" {
|
|
t.Errorf("Expected username 'emailuser', got %s", found.Username)
|
|
}
|
|
})
|
|
|
|
t.Run("Get user by non-existent email", func(t *testing.T) {
|
|
_, err := env.userSvc.GetByEmail(ctx, "nonexistent@test.com")
|
|
if err == nil {
|
|
t.Error("Expected error for non-existent email")
|
|
}
|
|
})
|
|
}
|