refactor:项目架构重构和命名规范化

- 统一文件命名为snake_case格式(kebab-case  snake_case)
- 重构zulip模块为zulip_core,明确Core层职责
- 重构user-mgmt模块为user_mgmt,统一命名规范
- 调整模块依赖关系,优化架构分层
- 删除过时的文件和目录结构
- 更新相关文档和配置文件

本次重构涉及大量文件重命名和模块重组,
旨在建立更清晰的项目架构和统一的命名规范。
This commit is contained in:
moyin
2026-01-08 00:14:14 +08:00
parent 4fa4bd1a70
commit bb796a2469
178 changed files with 24767 additions and 3484 deletions

View File

@@ -0,0 +1,453 @@
/**
* 用户管理业务服务测试
*
* 功能描述:
* - 测试用户状态管理业务逻辑
* - 测试批量用户操作功能
* - 测试用户状态统计功能
* - 测试状态变更审计功能
*
* 职责分离:
* - 单元测试覆盖所有公共方法
* - 异常情况和边界情况测试
* - 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<AdminService>;
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>(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
})
);
});
});
});