/** * 用户管理业务服务测试 * * 功能描述: * - 测试用户状态管理业务逻辑 * - 测试批量用户操作功能 * - 测试用户状态统计功能 * - 测试状态变更审计功能 * * 职责分离: * - 单元测试覆盖所有公共方法 * - 异常情况和边界情况测试 * - Mock依赖服务的行为验证 * * 最近修改: * - 2026-01-07: 代码规范优化 - 创建完整的测试覆盖 (修改者: moyin) * * @author moyin * @version 1.0.1 * @since 2026-01-07 * @lastModified 2026-01-07 */ import { Test, TestingModule } from '@nestjs/testing'; import { Logger } from '@nestjs/common'; import { UserManagementService } from './user_management.service'; import { AdminService } from '../admin/admin.service'; import { UserStatusDto, BatchUserStatusDto } from './user_status.dto'; import { UserStatus } from './user_status.enum'; import { BATCH_OPERATION, ERROR_CODES, MESSAGES } from './user_mgmt.constants'; describe('UserManagementService', () => { let service: UserManagementService; let mockAdminService: jest.Mocked; beforeEach(async () => { const mockAdminServiceProvider = { updateUserStatus: jest.fn(), batchUpdateUserStatus: jest.fn(), getUserStatusStats: jest.fn(), }; const module: TestingModule = await Test.createTestingModule({ providers: [ UserManagementService, { provide: AdminService, useValue: mockAdminServiceProvider, }, ], }).compile(); service = module.get(UserManagementService); mockAdminService = module.get(AdminService); // Mock Logger to avoid console output during tests jest.spyOn(Logger.prototype, 'log').mockImplementation(); jest.spyOn(Logger.prototype, 'warn').mockImplementation(); }); afterEach(() => { jest.clearAllMocks(); }); describe('updateUserStatus', () => { it('should update user status successfully', async () => { // Arrange const userId = BigInt(123); const userStatusDto: UserStatusDto = { status: UserStatus.ACTIVE, reason: '用户申诉通过' }; const expectedResult = { success: true, data: { user: { id: '123', username: 'testuser', nickname: '测试用户', status: UserStatus.ACTIVE, status_description: '正常', updated_at: new Date() }, reason: '用户申诉通过' }, message: '用户状态修改成功' }; mockAdminService.updateUserStatus.mockResolvedValue(expectedResult); // Act const result = await service.updateUserStatus(userId, userStatusDto); // Assert expect(result).toEqual(expectedResult); expect(mockAdminService.updateUserStatus).toHaveBeenCalledWith(userId, userStatusDto); expect(mockAdminService.updateUserStatus).toHaveBeenCalledTimes(1); }); it('should handle update failure', async () => { // Arrange const userId = BigInt(999); const userStatusDto: UserStatusDto = { status: UserStatus.LOCKED, reason: '违规操作' }; const expectedResult = { success: false, message: '用户不存在', error_code: 'USER_NOT_FOUND' }; mockAdminService.updateUserStatus.mockResolvedValue(expectedResult); // Act const result = await service.updateUserStatus(userId, userStatusDto); // Assert expect(result).toEqual(expectedResult); expect(mockAdminService.updateUserStatus).toHaveBeenCalledWith(userId, userStatusDto); }); it('should log success when update succeeds', async () => { // Arrange const userId = BigInt(123); const userStatusDto: UserStatusDto = { status: UserStatus.ACTIVE, reason: '测试' }; const successResult = { success: true, data: { user: { id: '123', username: 'testuser', nickname: '测试用户', status: UserStatus.ACTIVE, status_description: '正常', updated_at: new Date() }, reason: '测试' }, message: '成功' }; mockAdminService.updateUserStatus.mockResolvedValue(successResult); const logSpy = jest.spyOn(Logger.prototype, 'log'); // Act await service.updateUserStatus(userId, userStatusDto); // Assert expect(logSpy).toHaveBeenCalledWith( '用户管理:用户状态修改成功', expect.objectContaining({ operation: 'user_mgmt_update_status_success', userId: '123', newStatus: UserStatus.ACTIVE }) ); }); }); describe('batchUpdateUserStatus', () => { it('should batch update user status successfully', async () => { // Arrange const batchUserStatusDto: BatchUserStatusDto = { userIds: ['1', '2', '3'], status: UserStatus.LOCKED, reason: '批量锁定违规用户' }; const expectedResult = { success: true, data: { result: { success_users: [], failed_users: [], success_count: 3, failed_count: 0, total_count: 3 }, reason: '批量锁定违规用户' }, message: '批量用户状态修改完成' }; mockAdminService.batchUpdateUserStatus.mockResolvedValue(expectedResult); // Act const result = await service.batchUpdateUserStatus(batchUserStatusDto); // Assert expect(result).toEqual(expectedResult); expect(mockAdminService.batchUpdateUserStatus).toHaveBeenCalledWith(batchUserStatusDto); }); it('should reject batch operation when user count exceeds limit', async () => { // Arrange const userIds = Array.from({ length: BATCH_OPERATION.MAX_USER_COUNT + 1 }, (_, i) => i.toString()); const batchUserStatusDto: BatchUserStatusDto = { userIds, status: UserStatus.LOCKED, reason: '超限测试' }; // Act const result = await service.batchUpdateUserStatus(batchUserStatusDto); // Assert expect(result).toEqual({ success: false, message: MESSAGES.BATCH_OPERATION_LIMIT_ERROR, error_code: ERROR_CODES.BATCH_OPERATION_LIMIT_EXCEEDED }); expect(mockAdminService.batchUpdateUserStatus).not.toHaveBeenCalled(); }); it('should handle empty user list', async () => { // Arrange const batchUserStatusDto: BatchUserStatusDto = { userIds: [], status: UserStatus.ACTIVE, reason: '空列表测试' }; const expectedResult = { success: true, data: { result: { success_users: [], failed_users: [], success_count: 0, failed_count: 0, total_count: 0 } }, message: '批量操作完成' }; mockAdminService.batchUpdateUserStatus.mockResolvedValue(expectedResult); // Act const result = await service.batchUpdateUserStatus(batchUserStatusDto); // Assert expect(result).toEqual(expectedResult); }); it('should log warning when batch operation exceeds limit', async () => { // Arrange const userIds = Array.from({ length: BATCH_OPERATION.MAX_USER_COUNT + 1 }, (_, i) => i.toString()); const batchUserStatusDto: BatchUserStatusDto = { userIds, status: UserStatus.LOCKED, reason: '超限测试' }; const warnSpy = jest.spyOn(Logger.prototype, 'warn'); // Act await service.batchUpdateUserStatus(batchUserStatusDto); // Assert expect(warnSpy).toHaveBeenCalledWith( '用户管理:批量操作数量超限', expect.objectContaining({ operation: 'user_mgmt_batch_update_limit_exceeded', requestCount: BATCH_OPERATION.MAX_USER_COUNT + 1, maxAllowed: BATCH_OPERATION.MAX_USER_COUNT }) ); }); }); describe('getUserStatusStats', () => { it('should get user status statistics successfully', async () => { // Arrange const expectedResult = { success: true, data: { stats: { active: 1250, inactive: 45, locked: 12, banned: 8, deleted: 3, pending: 15, total: 1333 }, timestamp: '2026-01-07T10:00:00.000Z' }, message: '用户状态统计获取成功' }; mockAdminService.getUserStatusStats.mockResolvedValue(expectedResult); // Act const result = await service.getUserStatusStats(); // Assert expect(result).toEqual(expectedResult); expect(mockAdminService.getUserStatusStats).toHaveBeenCalledTimes(1); }); it('should handle statistics retrieval failure', async () => { // Arrange const expectedResult = { success: false, message: '统计数据获取失败', error_code: 'STATS_RETRIEVAL_FAILED' }; mockAdminService.getUserStatusStats.mockResolvedValue(expectedResult); // Act const result = await service.getUserStatusStats(); // Assert expect(result).toEqual(expectedResult); }); it('should calculate business metrics when stats are available', async () => { // Arrange const statsResult = { success: true, data: { stats: { active: 80, inactive: 10, locked: 5, banned: 3, deleted: 2, pending: 0, total: 100 }, timestamp: '2026-01-07T10:00:00.000Z' }, message: '成功' }; mockAdminService.getUserStatusStats.mockResolvedValue(statsResult); const logSpy = jest.spyOn(Logger.prototype, 'log'); // Act await service.getUserStatusStats(); // Assert expect(logSpy).toHaveBeenCalledWith( '用户管理:用户状态统计分析', expect.objectContaining({ operation: 'user_mgmt_status_analysis', totalUsers: 100, activeUsers: 80, activeRate: '80.00%', problemUsers: 10 // locked + banned + deleted }) ); }); it('should handle zero total users in statistics', async () => { // Arrange const statsResult = { success: true, data: { stats: { active: 0, inactive: 0, locked: 0, banned: 0, deleted: 0, pending: 0, total: 0 }, timestamp: '2026-01-07T10:00:00.000Z' }, message: '成功' }; mockAdminService.getUserStatusStats.mockResolvedValue(statsResult); const logSpy = jest.spyOn(Logger.prototype, 'log'); // Act await service.getUserStatusStats(); // Assert expect(logSpy).toHaveBeenCalledWith( '用户管理:用户状态统计分析', expect.objectContaining({ activeRate: '0%' }) ); }); }); describe('getUserStatusHistory', () => { it('should return mock history data with default limit', async () => { // Arrange const userId = BigInt(123); // Act const result = await service.getUserStatusHistory(userId); // Assert expect(result).toEqual({ success: true, data: { user_id: '123', history: [], total_count: 0 }, message: '状态变更历史获取成功(当前返回空数据,待实现完整功能)' }); }); it('should return mock history data with custom limit', async () => { // Arrange const userId = BigInt(456); const customLimit = 20; // Act const result = await service.getUserStatusHistory(userId, customLimit); // Assert expect(result).toEqual({ success: true, data: { user_id: '456', history: [], total_count: 0 }, message: '状态变更历史获取成功(当前返回空数据,待实现完整功能)' }); }); it('should log history query operation', async () => { // Arrange const userId = BigInt(789); const limit = 15; const logSpy = jest.spyOn(Logger.prototype, 'log'); // Act await service.getUserStatusHistory(userId, limit); // Assert expect(logSpy).toHaveBeenCalledWith( '用户管理:获取用户状态变更历史', expect.objectContaining({ operation: 'user_mgmt_get_status_history', userId: '789', limit: 15 }) ); }); }); });