package service_test import ( "context" "errors" "testing" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/mock" "github.com/user-management-system/internal/auth" "github.com/user-management-system/internal/domain" "github.com/user-management-system/internal/pagination" "github.com/user-management-system/internal/repository" "github.com/user-management-system/internal/service" "gorm.io/gorm" ) // ============================================================================= // Mock Implementations // ============================================================================= type mockUserRepository struct { mock.Mock } func (m *mockUserRepository) GetByID(ctx context.Context, id int64) (*domain.User, error) { args := m.Called(ctx, id) if args.Get(0) == nil { return nil, args.Error(1) } return args.Get(0).(*domain.User), args.Error(1) } func (m *mockUserRepository) GetByUsername(ctx context.Context, username string) (*domain.User, error) { args := m.Called(ctx, username) if args.Get(0) == nil { return nil, args.Error(1) } return args.Get(0).(*domain.User), args.Error(1) } func (m *mockUserRepository) GetByEmail(ctx context.Context, email string) (*domain.User, error) { args := m.Called(ctx, email) if args.Get(0) == nil { return nil, args.Error(1) } return args.Get(0).(*domain.User), args.Error(1) } func (m *mockUserRepository) Create(ctx context.Context, user *domain.User) error { args := m.Called(ctx, user) return args.Error(0) } func (m *mockUserRepository) Update(ctx context.Context, user *domain.User) error { args := m.Called(ctx, user) return args.Error(0) } func (m *mockUserRepository) Delete(ctx context.Context, id int64) error { args := m.Called(ctx, id) return args.Error(0) } func (m *mockUserRepository) List(ctx context.Context, offset, limit int) ([]*domain.User, int64, error) { args := m.Called(ctx, offset, limit) return args.Get(0).([]*domain.User), args.Get(1).(int64), args.Error(2) } func (m *mockUserRepository) ListCursor(ctx context.Context, filter *repository.AdvancedFilter, limit int, cursor *pagination.Cursor) ([]*domain.User, bool, error) { args := m.Called(ctx, filter, limit, cursor) return args.Get(0).([]*domain.User), args.Get(1).(bool), args.Error(2) } func (m *mockUserRepository) GetByIDs(ctx context.Context, ids []int64) ([]*domain.User, error) { args := m.Called(ctx, ids) return args.Get(0).([]*domain.User), args.Error(1) } func (m *mockUserRepository) UpdateStatus(ctx context.Context, id int64, status domain.UserStatus) error { args := m.Called(ctx, id, status) return args.Error(0) } func (m *mockUserRepository) BatchUpdateStatus(ctx context.Context, ids []int64, status domain.UserStatus) error { args := m.Called(ctx, ids, status) return args.Error(0) } func (m *mockUserRepository) BatchDelete(ctx context.Context, ids []int64) error { args := m.Called(ctx, ids) return args.Error(0) } func (m *mockUserRepository) DB() *gorm.DB { args := m.Called() if args.Get(0) == nil { return nil } return args.Get(0).(*gorm.DB) } type mockUserRoleRepository struct { mock.Mock } func (m *mockUserRoleRepository) GetByUserID(ctx context.Context, userID int64) ([]*domain.UserRole, error) { args := m.Called(ctx, userID) return args.Get(0).([]*domain.UserRole), args.Error(1) } func (m *mockUserRoleRepository) DeleteByUserID(ctx context.Context, userID int64) error { args := m.Called(ctx, userID) return args.Error(0) } func (m *mockUserRoleRepository) DeleteByUserAndRole(ctx context.Context, userID, roleID int64) error { args := m.Called(ctx, userID, roleID) return args.Error(0) } func (m *mockUserRoleRepository) GetByRoleID(ctx context.Context, roleID int64) ([]*domain.UserRole, error) { args := m.Called(ctx, roleID) return args.Get(0).([]*domain.UserRole), args.Error(1) } func (m *mockUserRoleRepository) GetUserIDByRoleID(ctx context.Context, roleID int64) ([]int64, error) { args := m.Called(ctx, roleID) return args.Get(0).([]int64), args.Error(1) } func (m *mockUserRoleRepository) BatchCreate(ctx context.Context, userRoles []*domain.UserRole) error { args := m.Called(ctx, userRoles) return args.Error(0) } func (m *mockUserRoleRepository) ReplaceUserRoles(ctx context.Context, userID int64, roleIDs []int64) error { args := m.Called(ctx, userID, roleIDs) return args.Error(0) } func (m *mockUserRoleRepository) DB() *gorm.DB { args := m.Called() if args.Get(0) == nil { return nil } return args.Get(0).(*gorm.DB) } type mockRoleRepositoryForUser struct { mock.Mock } func (m *mockRoleRepositoryForUser) GetByCode(ctx context.Context, code string) (*domain.Role, error) { args := m.Called(ctx, code) if args.Get(0) == nil { return nil, args.Error(1) } return args.Get(0).(*domain.Role), args.Error(1) } func (m *mockRoleRepositoryForUser) GetByID(ctx context.Context, id int64) (*domain.Role, error) { args := m.Called(ctx, id) if args.Get(0) == nil { return nil, args.Error(1) } return args.Get(0).(*domain.Role), args.Error(1) } func (m *mockRoleRepositoryForUser) GetByIDs(ctx context.Context, ids []int64) ([]*domain.Role, error) { args := m.Called(ctx, ids) return args.Get(0).([]*domain.Role), args.Error(1) } type mockPasswordHistoryRepository struct { mock.Mock } func (m *mockPasswordHistoryRepository) GetByUserID(ctx context.Context, userID int64, limit int) ([]*domain.PasswordHistory, error) { args := m.Called(ctx, userID, limit) return args.Get(0).([]*domain.PasswordHistory), args.Error(1) } func (m *mockPasswordHistoryRepository) Create(ctx context.Context, history *domain.PasswordHistory) error { args := m.Called(ctx, history) return args.Error(0) } func (m *mockPasswordHistoryRepository) DeleteOldRecords(ctx context.Context, userID int64, keep int) error { args := m.Called(ctx, userID, keep) return args.Error(0) } // ============================================================================= // Test Setup Helper // ============================================================================= func setupUserServiceTest() (*service.UserService, *mockUserRepository, *mockUserRoleRepository, *mockRoleRepositoryForUser, *mockPasswordHistoryRepository) { userRepo := &mockUserRepository{} userRoleRepo := &mockUserRoleRepository{} roleRepo := &mockRoleRepositoryForUser{} passwordHistoryRepo := &mockPasswordHistoryRepository{} svc := service.NewUserService(userRepo, userRoleRepo, roleRepo, passwordHistoryRepo) return svc, userRepo, userRoleRepo, roleRepo, passwordHistoryRepo } // ============================================================================= // GetByID Tests // ============================================================================= func TestUserService_GetByID_Success(t *testing.T) { svc, userRepo, _, _, _ := setupUserServiceTest() expectedUser := &domain.User{ ID: 1, Username: "testuser", } userRepo.On("GetByID", mock.Anything, int64(1)).Return(expectedUser, nil) result, err := svc.GetByID(context.Background(), 1) assert.NoError(t, err) assert.Equal(t, expectedUser, result) userRepo.AssertExpectations(t) } func TestUserService_GetByID_NotFound(t *testing.T) { svc, userRepo, _, _, _ := setupUserServiceTest() userRepo.On("GetByID", mock.Anything, int64(999)).Return(nil, errors.New("user not found")) result, err := svc.GetByID(context.Background(), 999) assert.Error(t, err) assert.Nil(t, result) userRepo.AssertExpectations(t) } // ============================================================================= // GetByEmail Tests // ============================================================================= func TestUserService_GetByEmail_Success(t *testing.T) { svc, userRepo, _, _, _ := setupUserServiceTest() expectedUser := &domain.User{ ID: 1, Username: "testuser", Email: strPtr("test@example.com"), } userRepo.On("GetByEmail", mock.Anything, "test@example.com").Return(expectedUser, nil) result, err := svc.GetByEmail(context.Background(), "test@example.com") assert.NoError(t, err) assert.Equal(t, expectedUser, result) userRepo.AssertExpectations(t) } // ============================================================================= // Create Tests // ============================================================================= func TestUserService_Create_Success(t *testing.T) { svc, userRepo, _, _, _ := setupUserServiceTest() email := "test@example.com" user := &domain.User{ Username: "testuser", Email: &email, Nickname: "Test User", } userRepo.On("Create", mock.Anything, user).Return(nil) err := svc.Create(context.Background(), user) assert.NoError(t, err) userRepo.AssertExpectations(t) } func TestUserService_Create_EmptyUsername(t *testing.T) { svc, _, _, _, _ := setupUserServiceTest() user := &domain.User{ Username: "", Email: strPtr("test@example.com"), } err := svc.Create(context.Background(), user) assert.EqualError(t, err, "用户名不能为空") } func TestUserService_Create_WhitespaceUsername(t *testing.T) { svc, _, _, _, _ := setupUserServiceTest() user := &domain.User{ Username: " ", Email: strPtr("test@example.com"), } err := svc.Create(context.Background(), user) assert.EqualError(t, err, "用户名不能为空") } func TestUserService_Create_UsernameTooLong(t *testing.T) { svc, _, _, _, _ := setupUserServiceTest() longName := make([]byte, 51) for i := range longName { longName[i] = 'a' } user := &domain.User{ Username: string(longName), Email: strPtr("test@example.com"), } err := svc.Create(context.Background(), user) assert.EqualError(t, err, "用户名长度超过限制") } func TestUserService_Create_InvalidEmail(t *testing.T) { svc, _, _, _, _ := setupUserServiceTest() invalidEmail := "invalid-email" user := &domain.User{ Username: "testuser", Email: &invalidEmail, } err := svc.Create(context.Background(), user) assert.EqualError(t, err, "邮箱格式不正确") } func TestUserService_Create_EmailWithNoAtSign(t *testing.T) { svc, _, _, _, _ := setupUserServiceTest() email := "testexample.com" user := &domain.User{ Username: "testuser", Email: &email, } err := svc.Create(context.Background(), user) assert.EqualError(t, err, "邮箱格式不正确") } func TestUserService_Create_EmailWithMultipleAtSigns(t *testing.T) { svc, _, _, _, _ := setupUserServiceTest() email := "test@@example.com" user := &domain.User{ Username: "testuser", Email: &email, } err := svc.Create(context.Background(), user) assert.EqualError(t, err, "邮箱格式不正确") } func TestUserService_Create_EmailWithSpace(t *testing.T) { svc, _, _, _, _ := setupUserServiceTest() email := "test user@example.com" user := &domain.User{ Username: "testuser", Email: &email, } err := svc.Create(context.Background(), user) assert.EqualError(t, err, "邮箱格式不正确") } func TestUserService_Create_EmailTooLong(t *testing.T) { svc, _, _, _, _ := setupUserServiceTest() longLocal := make([]byte, 97) for i := range longLocal { longLocal[i] = 'a' } email := string(longLocal) + "@com" user := &domain.User{ Username: "testuser", Email: &email, } err := svc.Create(context.Background(), user) assert.EqualError(t, err, "邮箱长度超过限制") } func TestUserService_Create_NicknameTooLong(t *testing.T) { svc, _, _, _, _ := setupUserServiceTest() longNickname := make([]rune, 51) for i := range longNickname { longNickname[i] = '中' } user := &domain.User{ Username: "testuser", Nickname: string(longNickname), } err := svc.Create(context.Background(), user) assert.EqualError(t, err, "昵称长度超过限制") } func TestUserService_Create_BioTooLong(t *testing.T) { svc, _, _, _, _ := setupUserServiceTest() longBio := make([]rune, 501) for i := range longBio { longBio[i] = 'a' } user := &domain.User{ Username: "testuser", Bio: string(longBio), } err := svc.Create(context.Background(), user) assert.EqualError(t, err, "简介长度超过限制") } func TestUserService_Create_NilEmail(t *testing.T) { svc, userRepo, _, _, _ := setupUserServiceTest() user := &domain.User{ Username: "testuser", Email: nil, } userRepo.On("Create", mock.Anything, user).Return(nil) err := svc.Create(context.Background(), user) assert.NoError(t, err) userRepo.AssertExpectations(t) } func TestUserService_Create_EmptyStringEmail(t *testing.T) { svc, userRepo, _, _, _ := setupUserServiceTest() emptyEmail := "" user := &domain.User{ Username: "testuser", Email: &emptyEmail, } userRepo.On("Create", mock.Anything, user).Return(nil) err := svc.Create(context.Background(), user) assert.NoError(t, err) userRepo.AssertExpectations(t) } // ============================================================================= // Update & Delete Tests // ============================================================================= func TestUserService_Update_Success(t *testing.T) { svc, userRepo, _, _, _ := setupUserServiceTest() user := &domain.User{ID: 1, Username: "updated"} userRepo.On("Update", mock.Anything, user).Return(nil) err := svc.Update(context.Background(), user) assert.NoError(t, err) userRepo.AssertExpectations(t) } func TestUserService_Delete_Success(t *testing.T) { svc, userRepo, _, _, _ := setupUserServiceTest() userRepo.On("Delete", mock.Anything, int64(1)).Return(nil) err := svc.Delete(context.Background(), 1) assert.NoError(t, err) userRepo.AssertExpectations(t) } // ============================================================================= // List Tests // ============================================================================= func TestUserService_List_Success(t *testing.T) { svc, userRepo, _, _, _ := setupUserServiceTest() users := []*domain.User{ {ID: 1, Username: "user1"}, {ID: 2, Username: "user2"}, } userRepo.On("List", mock.Anything, 0, 10).Return(users, int64(2), nil) result, total, err := svc.List(context.Background(), 0, 10) assert.NoError(t, err) assert.Equal(t, users, result) assert.Equal(t, int64(2), total) userRepo.AssertExpectations(t) } func TestUserService_List_DefaultLimit(t *testing.T) { svc, userRepo, _, _, _ := setupUserServiceTest() users := []*domain.User{} userRepo.On("List", mock.Anything, 0, 10).Return(users, int64(0), nil) result, total, err := svc.List(context.Background(), 0, 0) assert.NoError(t, err) assert.NotNil(t, result) assert.Equal(t, int64(0), total) userRepo.AssertExpectations(t) } func TestUserService_List_InvalidOffset(t *testing.T) { svc, userRepo, _, _, _ := setupUserServiceTest() users := []*domain.User{} userRepo.On("List", mock.Anything, 0, 10).Return(users, int64(0), nil) result, _, err := svc.List(context.Background(), -1, 10) assert.NoError(t, err) assert.NotNil(t, result) userRepo.AssertExpectations(t) } // ============================================================================= // BatchUpdateStatus Tests // ============================================================================= func TestUserService_BatchUpdateStatus_Success(t *testing.T) { svc, userRepo, _, _, _ := setupUserServiceTest() ids := []int64{1, 2, 3} userRepo.On("BatchUpdateStatus", mock.Anything, ids, domain.UserStatusActive).Return(nil).Once() req := &service.BatchUpdateStatusRequest{ IDs: ids, Status: domain.UserStatusActive, } count, err := svc.BatchUpdateStatus(context.Background(), req) assert.NoError(t, err) assert.Equal(t, int64(3), count) userRepo.AssertExpectations(t) } // ============================================================================= // BatchDelete Tests // ============================================================================= func TestUserService_BatchDelete_Success(t *testing.T) { svc, userRepo, _, _, _ := setupUserServiceTest() ids := []int64{1, 2, 3} userRepo.On("BatchDelete", mock.Anything, ids).Return(nil).Once() req := &service.BatchDeleteRequest{ IDs: ids, } count, err := svc.BatchDelete(context.Background(), req) assert.NoError(t, err) assert.Equal(t, int64(3), count) userRepo.AssertExpectations(t) } // ============================================================================= // GetUserRoles Tests // ============================================================================= func TestUserService_GetUserRoles_Success(t *testing.T) { svc, userRepo, userRoleRepo, roleRepo, _ := setupUserServiceTest() user := &domain.User{ID: 1, Username: "testuser"} userRepo.On("GetByID", mock.Anything, int64(1)).Return(user, nil).Once() userRoles := []*domain.UserRole{ {UserID: 1, RoleID: 1}, {UserID: 1, RoleID: 2}, } userRoleRepo.On("GetByUserID", mock.Anything, int64(1)).Return(userRoles, nil).Once() roles := []*domain.Role{ {ID: 1, Name: "Admin"}, {ID: 2, Name: "User"}, } roleRepo.On("GetByIDs", mock.Anything, []int64{1, 2}).Return(roles, nil).Once() result, err := svc.GetUserRoles(context.Background(), 1) assert.NoError(t, err) assert.Equal(t, 2, len(result)) userRepo.AssertExpectations(t) userRoleRepo.AssertExpectations(t) roleRepo.AssertExpectations(t) } func TestUserService_GetUserRoles_UserNotFound(t *testing.T) { svc, userRepo, _, _, _ := setupUserServiceTest() userRepo.On("GetByID", mock.Anything, int64(999)).Return(nil, errors.New("user not found")).Once() result, err := svc.GetUserRoles(context.Background(), 999) assert.Error(t, err) assert.Nil(t, result) userRepo.AssertExpectations(t) } func TestUserService_GetUserRoles_NoRoles(t *testing.T) { svc, userRepo, userRoleRepo, _, _ := setupUserServiceTest() user := &domain.User{ID: 1, Username: "testuser"} userRepo.On("GetByID", mock.Anything, int64(1)).Return(user, nil).Once() userRoleRepo.On("GetByUserID", mock.Anything, int64(1)).Return([]*domain.UserRole{}, nil).Once() result, err := svc.GetUserRoles(context.Background(), 1) assert.NoError(t, err) assert.Equal(t, 0, len(result)) userRepo.AssertExpectations(t) userRoleRepo.AssertExpectations(t) } // ============================================================================= // AssignRoles Tests // ============================================================================= func TestUserService_AssignRoles_Success(t *testing.T) { svc, userRepo, userRoleRepo, roleRepo, _ := setupUserServiceTest() user := &domain.User{ID: 1, Username: "testuser"} userRepo.On("GetByID", mock.Anything, int64(1)).Return(user, nil).Once() roleRepo.On("GetByID", mock.Anything, int64(1)).Return(&domain.Role{ID: 1}, nil).Once() roleRepo.On("GetByID", mock.Anything, int64(2)).Return(&domain.Role{ID: 2}, nil).Once() userRoleRepo.On("ReplaceUserRoles", mock.Anything, int64(1), []int64{1, 2}).Return(nil).Once() err := svc.AssignRoles(context.Background(), 1, []int64{1, 2}) assert.NoError(t, err) userRepo.AssertExpectations(t) userRoleRepo.AssertExpectations(t) roleRepo.AssertExpectations(t) } func TestUserService_AssignRoles_UserNotFound(t *testing.T) { svc, userRepo, _, _, _ := setupUserServiceTest() userRepo.On("GetByID", mock.Anything, int64(999)).Return(nil, errors.New("user not found")).Once() err := svc.AssignRoles(context.Background(), 999, []int64{1}) assert.Error(t, err) userRepo.AssertExpectations(t) } // ============================================================================= // ListAdmins Tests // ============================================================================= func TestUserService_ListAdmins_Success(t *testing.T) { svc, userRepo, userRoleRepo, roleRepo, _ := setupUserServiceTest() adminRole := &domain.Role{ID: 1, Code: "admin", Name: "Admin"} adminUserIDs := []int64{1, 2} admins := []*domain.User{ {ID: 1, Username: "admin1"}, {ID: 2, Username: "admin2"}, } roleRepo.On("GetByCode", mock.Anything, "admin").Return(adminRole, nil).Once() userRoleRepo.On("GetUserIDByRoleID", mock.Anything, int64(1)).Return(adminUserIDs, nil).Once() userRepo.On("GetByIDs", mock.Anything, adminUserIDs).Return(admins, nil).Once() result, err := svc.ListAdmins(context.Background()) assert.NoError(t, err) assert.Equal(t, 2, len(result)) userRepo.AssertExpectations(t) userRoleRepo.AssertExpectations(t) roleRepo.AssertExpectations(t) } func TestUserService_ListAdmins_NoAdmins(t *testing.T) { svc, _, userRoleRepo, roleRepo, _ := setupUserServiceTest() adminRole := &domain.Role{ID: 1, Code: "admin", Name: "Admin"} roleRepo.On("GetByCode", mock.Anything, "admin").Return(adminRole, nil).Once() userRoleRepo.On("GetUserIDByRoleID", mock.Anything, int64(1)).Return([]int64{}, nil).Once() result, err := svc.ListAdmins(context.Background()) assert.NoError(t, err) assert.Equal(t, 0, len(result)) userRoleRepo.AssertExpectations(t) roleRepo.AssertExpectations(t) } // ============================================================================= // DeleteAdmin Tests // ============================================================================= func TestUserService_DeleteAdmin_Success(t *testing.T) { svc, userRepo, userRoleRepo, roleRepo, _ := setupUserServiceTest() user := &domain.User{ID: 2, Username: "admin2"} userRepo.On("GetByID", mock.Anything, int64(2)).Return(user, nil).Once() adminRole := &domain.Role{ID: 1, Code: "admin", Name: "Admin"} roleRepo.On("GetByCode", mock.Anything, "admin").Return(adminRole, nil).Once() adminUserRoles := []*domain.UserRole{ {UserID: 1, RoleID: 1}, {UserID: 2, RoleID: 1}, } userRoleRepo.On("GetByRoleID", mock.Anything, int64(1)).Return(adminUserRoles, nil).Once() userRoleRepo.On("DeleteByUserAndRole", mock.Anything, int64(2), int64(1)).Return(nil).Once() err := svc.DeleteAdmin(context.Background(), 2, 1) assert.NoError(t, err) userRepo.AssertExpectations(t) userRoleRepo.AssertExpectations(t) roleRepo.AssertExpectations(t) } func TestUserService_DeleteAdmin_CannotDeleteSelf(t *testing.T) { svc, userRepo, _, _, _ := setupUserServiceTest() user := &domain.User{ID: 1, Username: "admin1"} userRepo.On("GetByID", mock.Anything, int64(1)).Return(user, nil).Once() err := svc.DeleteAdmin(context.Background(), 1, 1) assert.Error(t, err) assert.EqualError(t, err, "不能删除自己") userRepo.AssertExpectations(t) } func TestUserService_DeleteAdmin_LastAdmin(t *testing.T) { svc, userRepo, userRoleRepo, roleRepo, _ := setupUserServiceTest() user := &domain.User{ID: 1, Username: "admin1"} userRepo.On("GetByID", mock.Anything, int64(1)).Return(user, nil).Once() adminRole := &domain.Role{ID: 1, Code: "admin", Name: "Admin"} roleRepo.On("GetByCode", mock.Anything, "admin").Return(adminRole, nil).Once() adminUserRoles := []*domain.UserRole{ {UserID: 1, RoleID: 1}, } userRoleRepo.On("GetByRoleID", mock.Anything, int64(1)).Return(adminUserRoles, nil).Once() err := svc.DeleteAdmin(context.Background(), 1, 999) assert.Error(t, err) assert.EqualError(t, err, "不能删除最后一个管理员") userRepo.AssertExpectations(t) userRoleRepo.AssertExpectations(t) roleRepo.AssertExpectations(t) } // ============================================================================= // ChangePassword Tests // ============================================================================= func TestUserService_ChangePassword_NilRepo(t *testing.T) { svc := service.NewUserService(nil, nil, nil, nil) err := svc.ChangePassword(context.Background(), 1, "old", "new") assert.Error(t, err) assert.EqualError(t, err, "user repository is not configured") } func TestUserService_ChangePassword_UserNotFound(t *testing.T) { svc, userRepo, _, _, _ := setupUserServiceTest() userRepo.On("GetByID", mock.Anything, int64(999)).Return(nil, errors.New("user not found")).Once() err := svc.ChangePassword(context.Background(), 999, "old", "new") assert.Error(t, err) assert.EqualError(t, err, "用户不存在") userRepo.AssertExpectations(t) } func TestUserService_ChangePassword_EmptyOldPassword(t *testing.T) { svc, userRepo, _, _, _ := setupUserServiceTest() hashedPassword, _ := auth.HashPassword("password123") user := &domain.User{ ID: 1, Username: "testuser", Password: hashedPassword, } userRepo.On("GetByID", mock.Anything, int64(1)).Return(user, nil).Once() err := svc.ChangePassword(context.Background(), 1, "", "newpass123") assert.Error(t, err) assert.EqualError(t, err, "请输入当前密码") userRepo.AssertExpectations(t) } func TestUserService_ChangePassword_EmptyNewPassword(t *testing.T) { svc, userRepo, _, _, _ := setupUserServiceTest() hashedPassword, _ := auth.HashPassword("password123") user := &domain.User{ ID: 1, Username: "testuser", Password: hashedPassword, } userRepo.On("GetByID", mock.Anything, int64(1)).Return(user, nil).Once() err := svc.ChangePassword(context.Background(), 1, "password123", "") assert.Error(t, err) assert.EqualError(t, err, "新密码不能为空") userRepo.AssertExpectations(t) } func TestUserService_ChangePassword_WeakNewPassword(t *testing.T) { svc, userRepo, _, _, _ := setupUserServiceTest() hashedPassword, _ := auth.HashPassword("password123") user := &domain.User{ ID: 1, Username: "testuser", Password: hashedPassword, } userRepo.On("GetByID", mock.Anything, int64(1)).Return(user, nil).Once() err := svc.ChangePassword(context.Background(), 1, "password123", "123") assert.Error(t, err) userRepo.AssertExpectations(t) } func TestUserService_ChangePassword_IncorrectOldPassword(t *testing.T) { svc, userRepo, _, _, _ := setupUserServiceTest() hashedPassword, _ := auth.HashPassword("password123") user := &domain.User{ ID: 1, Username: "testuser", Password: hashedPassword, } userRepo.On("GetByID", mock.Anything, int64(1)).Return(user, nil).Once() err := svc.ChangePassword(context.Background(), 1, "wrongpassword", "Newpass123!") assert.Error(t, err) assert.EqualError(t, err, "当前密码不正确") userRepo.AssertExpectations(t) } func TestUserService_ChangePassword_WhitespaceOldPassword(t *testing.T) { svc, userRepo, _, _, _ := setupUserServiceTest() hashedPassword, _ := auth.HashPassword("password123") user := &domain.User{ ID: 1, Username: "testuser", Password: hashedPassword, } userRepo.On("GetByID", mock.Anything, int64(1)).Return(user, nil).Once() err := svc.ChangePassword(context.Background(), 1, " ", "Newpass123!") assert.Error(t, err) assert.EqualError(t, err, "请输入当前密码") userRepo.AssertExpectations(t) }