- 统一文件命名为snake_case格式(kebab-case snake_case) - 重构zulip模块为zulip_core,明确Core层职责 - 重构user-mgmt模块为user_mgmt,统一命名规范 - 调整模块依赖关系,优化架构分层 - 删除过时的文件和目录结构 - 更新相关文档和配置文件 本次重构涉及大量文件重命名和模块重组, 旨在建立更清晰的项目架构和统一的命名规范。
436 lines
15 KiB
TypeScript
436 lines
15 KiB
TypeScript
/**
|
|
* 用户管理模块集成测试
|
|
*
|
|
* 功能描述:
|
|
* - 测试用户管理模块的完整业务流程
|
|
* - 测试控制器与服务的集成
|
|
* - 测试真实的HTTP请求处理
|
|
* - 测试端到端的业务场景
|
|
*
|
|
* 职责分离:
|
|
* - 集成测试覆盖完整的业务流程
|
|
* - 测试模块间的协作和数据流
|
|
* - 验证真实环境下的功能表现
|
|
*
|
|
* 最近修改:
|
|
* - 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 { INestApplication } from '@nestjs/common';
|
|
import { UserStatusController } from './user_status.controller';
|
|
import { UserManagementService } from './user_management.service';
|
|
import { AdminService } from '../admin/admin.service';
|
|
import { AdminGuard } from '../admin/guards/admin.guard';
|
|
import { UserStatusDto, BatchUserStatusDto } from './user_status.dto';
|
|
import { UserStatus } from './user_status.enum';
|
|
import { BATCH_OPERATION, ERROR_CODES, MESSAGES } from './user_mgmt.constants';
|
|
|
|
describe('UserManagement Integration', () => {
|
|
let app: INestApplication;
|
|
let controller: UserStatusController;
|
|
let userManagementService: UserManagementService;
|
|
let mockAdminService: jest.Mocked<AdminService>;
|
|
|
|
beforeAll(async () => {
|
|
// Create mock AdminService
|
|
const mockAdminServiceProvider = {
|
|
updateUserStatus: jest.fn(),
|
|
batchUpdateUserStatus: jest.fn(),
|
|
getUserStatusStats: jest.fn(),
|
|
};
|
|
|
|
const moduleFixture: TestingModule = await Test.createTestingModule({
|
|
controllers: [UserStatusController],
|
|
providers: [
|
|
UserManagementService,
|
|
{
|
|
provide: AdminService,
|
|
useValue: mockAdminServiceProvider,
|
|
},
|
|
],
|
|
})
|
|
.overrideGuard(AdminGuard)
|
|
.useValue({ canActivate: jest.fn(() => true) })
|
|
.compile();
|
|
|
|
app = moduleFixture.createNestApplication();
|
|
await app.init();
|
|
|
|
controller = moduleFixture.get<UserStatusController>(UserStatusController);
|
|
userManagementService = moduleFixture.get<UserManagementService>(UserManagementService);
|
|
mockAdminService = moduleFixture.get(AdminService);
|
|
});
|
|
|
|
afterAll(async () => {
|
|
await app.close();
|
|
});
|
|
|
|
beforeEach(() => {
|
|
jest.clearAllMocks();
|
|
});
|
|
|
|
describe('Complete User Status Management Flow', () => {
|
|
it('should handle complete user status update workflow', async () => {
|
|
// Arrange
|
|
const userId = '123';
|
|
const userStatusDto: UserStatusDto = {
|
|
status: UserStatus.LOCKED,
|
|
reason: '用户违反社区规定'
|
|
};
|
|
|
|
const mockUpdateResult = {
|
|
success: true,
|
|
data: {
|
|
user: {
|
|
id: '123',
|
|
username: 'testuser',
|
|
nickname: '测试用户',
|
|
status: UserStatus.LOCKED,
|
|
status_description: '已锁定',
|
|
updated_at: new Date('2026-01-07T10:00:00.000Z')
|
|
},
|
|
reason: '用户违反社区规定'
|
|
},
|
|
message: '用户状态修改成功'
|
|
};
|
|
|
|
mockAdminService.updateUserStatus.mockResolvedValue(mockUpdateResult);
|
|
|
|
// Act - Controller calls Service, Service calls AdminService
|
|
const result = await controller.updateUserStatus(userId, userStatusDto);
|
|
|
|
// Assert - Verify complete integration
|
|
expect(result).toEqual(mockUpdateResult);
|
|
expect(mockAdminService.updateUserStatus).toHaveBeenCalledWith(BigInt(123), userStatusDto);
|
|
expect(result.data.user.status).toBe(UserStatus.LOCKED);
|
|
expect(result.data.reason).toBe('用户违反社区规定');
|
|
});
|
|
|
|
it('should handle complete batch update workflow', async () => {
|
|
// Arrange
|
|
const batchUserStatusDto: BatchUserStatusDto = {
|
|
userIds: ['1', '2', '3'],
|
|
status: UserStatus.BANNED,
|
|
reason: '批量处理违规用户'
|
|
};
|
|
|
|
const mockBatchResult = {
|
|
success: true,
|
|
data: {
|
|
result: {
|
|
success_users: [
|
|
{ id: '1', username: 'user1', nickname: '用户1', status: UserStatus.BANNED, status_description: '已封禁', updated_at: new Date() },
|
|
{ id: '2', username: 'user2', nickname: '用户2', status: UserStatus.BANNED, status_description: '已封禁', updated_at: new Date() }
|
|
],
|
|
failed_users: [
|
|
{ user_id: '3', error: '用户不存在' }
|
|
],
|
|
success_count: 2,
|
|
failed_count: 1,
|
|
total_count: 3
|
|
},
|
|
reason: '批量处理违规用户'
|
|
},
|
|
message: '批量用户状态修改完成'
|
|
};
|
|
|
|
mockAdminService.batchUpdateUserStatus.mockResolvedValue(mockBatchResult);
|
|
|
|
// Act - Complete batch workflow
|
|
const result = await controller.batchUpdateUserStatus(batchUserStatusDto);
|
|
|
|
// Assert - Verify batch integration
|
|
expect(result).toEqual(mockBatchResult);
|
|
expect(result.data.result.success_count).toBe(2);
|
|
expect(result.data.result.failed_count).toBe(1);
|
|
expect(mockAdminService.batchUpdateUserStatus).toHaveBeenCalledWith(batchUserStatusDto);
|
|
});
|
|
|
|
it('should handle complete statistics workflow', async () => {
|
|
// Arrange
|
|
const mockStatsResult = {
|
|
success: true,
|
|
data: {
|
|
stats: {
|
|
active: 1000,
|
|
inactive: 200,
|
|
locked: 50,
|
|
banned: 25,
|
|
deleted: 10,
|
|
pending: 30,
|
|
total: 1315
|
|
},
|
|
timestamp: '2026-01-07T10:00:00.000Z'
|
|
},
|
|
message: '用户状态统计获取成功'
|
|
};
|
|
|
|
mockAdminService.getUserStatusStats.mockResolvedValue(mockStatsResult);
|
|
|
|
// Act - Complete statistics workflow
|
|
const result = await controller.getUserStatusStats();
|
|
|
|
// Assert - Verify statistics integration
|
|
expect(result).toEqual(mockStatsResult);
|
|
expect(result.data.stats.total).toBe(1315);
|
|
expect(mockAdminService.getUserStatusStats).toHaveBeenCalledTimes(1);
|
|
});
|
|
});
|
|
|
|
describe('Business Logic Integration', () => {
|
|
it('should enforce batch operation limits through service layer', async () => {
|
|
// Arrange - Create request exceeding limits
|
|
const userIds = Array.from({ length: BATCH_OPERATION.MAX_USER_COUNT + 1 }, (_, i) => i.toString());
|
|
const batchUserStatusDto: BatchUserStatusDto = {
|
|
userIds,
|
|
status: UserStatus.LOCKED,
|
|
reason: '超限测试'
|
|
};
|
|
|
|
// Act - Service should reject before calling AdminService
|
|
const result = await userManagementService.batchUpdateUserStatus(batchUserStatusDto);
|
|
|
|
// Assert - Verify business rule enforcement
|
|
expect(result.success).toBe(false);
|
|
expect(result.message).toBe(MESSAGES.BATCH_OPERATION_LIMIT_ERROR);
|
|
expect(result.error_code).toBe(ERROR_CODES.BATCH_OPERATION_LIMIT_EXCEEDED);
|
|
expect(mockAdminService.batchUpdateUserStatus).not.toHaveBeenCalled();
|
|
});
|
|
|
|
it('should handle user status history integration', async () => {
|
|
// Arrange
|
|
const userId = BigInt(456);
|
|
const limit = 10;
|
|
|
|
// Act - Test history functionality (currently mock implementation)
|
|
const result = await userManagementService.getUserStatusHistory(userId, limit);
|
|
|
|
// Assert - Verify history integration
|
|
expect(result.success).toBe(true);
|
|
expect(result.data.user_id).toBe('456');
|
|
expect(result.data.history).toEqual([]);
|
|
expect(result.data.total_count).toBe(0);
|
|
expect(result.message).toContain('状态变更历史获取成功');
|
|
});
|
|
});
|
|
|
|
describe('Error Handling Integration', () => {
|
|
it('should handle service errors through complete stack', async () => {
|
|
// Arrange
|
|
const userId = '999';
|
|
const userStatusDto: UserStatusDto = {
|
|
status: UserStatus.ACTIVE,
|
|
reason: '测试错误处理'
|
|
};
|
|
|
|
const mockErrorResult = {
|
|
success: false,
|
|
message: '用户不存在',
|
|
error_code: 'USER_NOT_FOUND'
|
|
};
|
|
|
|
mockAdminService.updateUserStatus.mockResolvedValue(mockErrorResult);
|
|
|
|
// Act - Error propagation through layers
|
|
const result = await controller.updateUserStatus(userId, userStatusDto);
|
|
|
|
// Assert - Verify error handling integration
|
|
expect(result.success).toBe(false);
|
|
expect(result.message).toBe('用户不存在');
|
|
expect(result.error_code).toBe('USER_NOT_FOUND');
|
|
});
|
|
|
|
it('should handle batch operation partial failures', async () => {
|
|
// Arrange
|
|
const batchUserStatusDto: BatchUserStatusDto = {
|
|
userIds: ['1', '2', '999', '888'],
|
|
status: UserStatus.ACTIVE,
|
|
reason: '批量激活测试'
|
|
};
|
|
|
|
const mockPartialFailureResult = {
|
|
success: true,
|
|
data: {
|
|
result: {
|
|
success_users: [
|
|
{ id: '1', username: 'user1', nickname: '用户1', status: UserStatus.ACTIVE, status_description: '正常', updated_at: new Date() },
|
|
{ id: '2', username: 'user2', nickname: '用户2', status: UserStatus.ACTIVE, status_description: '正常', updated_at: new Date() }
|
|
],
|
|
failed_users: [
|
|
{ user_id: '999', error: '用户不存在' },
|
|
{ user_id: '888', error: '用户状态无法修改' }
|
|
],
|
|
success_count: 2,
|
|
failed_count: 2,
|
|
total_count: 4
|
|
},
|
|
reason: '批量激活测试'
|
|
},
|
|
message: '批量用户状态修改完成'
|
|
};
|
|
|
|
mockAdminService.batchUpdateUserStatus.mockResolvedValue(mockPartialFailureResult);
|
|
|
|
// Act - Handle partial failures
|
|
const result = await controller.batchUpdateUserStatus(batchUserStatusDto);
|
|
|
|
// Assert - Verify partial failure handling
|
|
expect(result.success).toBe(true);
|
|
expect(result.data.result.success_count).toBe(2);
|
|
expect(result.data.result.failed_count).toBe(2);
|
|
expect(result.data.result.failed_users).toHaveLength(2);
|
|
});
|
|
|
|
it('should handle statistics service failures', async () => {
|
|
// Arrange
|
|
const mockStatsError = {
|
|
success: false,
|
|
message: '数据库连接失败',
|
|
error_code: 'DATABASE_CONNECTION_ERROR'
|
|
};
|
|
|
|
mockAdminService.getUserStatusStats.mockResolvedValue(mockStatsError);
|
|
|
|
// Act - Handle statistics errors
|
|
const result = await controller.getUserStatusStats();
|
|
|
|
// Assert - Verify error propagation
|
|
expect(result.success).toBe(false);
|
|
expect(result.message).toBe('数据库连接失败');
|
|
expect(result.error_code).toBe('DATABASE_CONNECTION_ERROR');
|
|
});
|
|
});
|
|
|
|
describe('Data Flow Integration', () => {
|
|
it('should maintain data consistency through all layers', async () => {
|
|
// Arrange
|
|
const userId = '789';
|
|
const userStatusDto: UserStatusDto = {
|
|
status: UserStatus.INACTIVE,
|
|
reason: '长期未活跃'
|
|
};
|
|
|
|
const mockResult = {
|
|
success: true,
|
|
data: {
|
|
user: {
|
|
id: '789',
|
|
username: 'inactive_user',
|
|
nickname: '非活跃用户',
|
|
status: UserStatus.INACTIVE,
|
|
status_description: '非活跃',
|
|
updated_at: new Date('2026-01-07T10:00:00.000Z')
|
|
},
|
|
reason: '长期未活跃'
|
|
},
|
|
message: '用户状态修改成功'
|
|
};
|
|
|
|
mockAdminService.updateUserStatus.mockResolvedValue(mockResult);
|
|
|
|
// Act - Data flows through Controller -> Service -> AdminService
|
|
const result = await controller.updateUserStatus(userId, userStatusDto);
|
|
|
|
// Assert - Verify data consistency
|
|
expect(result.data.user.id).toBe(userId);
|
|
expect(result.data.user.status).toBe(userStatusDto.status);
|
|
expect(result.data.reason).toBe(userStatusDto.reason);
|
|
expect(mockAdminService.updateUserStatus).toHaveBeenCalledWith(
|
|
BigInt(789),
|
|
expect.objectContaining({
|
|
status: UserStatus.INACTIVE,
|
|
reason: '长期未活跃'
|
|
})
|
|
);
|
|
});
|
|
|
|
it('should handle BigInt conversion correctly in data flow', async () => {
|
|
// Arrange - Test large number handling
|
|
const largeUserId = '9007199254740991';
|
|
const userStatusDto: UserStatusDto = {
|
|
status: UserStatus.PENDING,
|
|
reason: '大数字ID测试'
|
|
};
|
|
|
|
const mockResult = {
|
|
success: true,
|
|
data: {
|
|
user: {
|
|
id: largeUserId,
|
|
username: 'large_id_user',
|
|
nickname: '大ID用户',
|
|
status: UserStatus.PENDING,
|
|
status_description: '待处理',
|
|
updated_at: new Date()
|
|
},
|
|
reason: '大数字ID测试'
|
|
},
|
|
message: '用户状态修改成功'
|
|
};
|
|
|
|
mockAdminService.updateUserStatus.mockResolvedValue(mockResult);
|
|
|
|
// Act - Test BigInt conversion in data flow
|
|
const result = await controller.updateUserStatus(largeUserId, userStatusDto);
|
|
|
|
// Assert - Verify BigInt handling
|
|
expect(result.data.user.id).toBe(largeUserId);
|
|
expect(mockAdminService.updateUserStatus).toHaveBeenCalledWith(
|
|
BigInt('9007199254740991'),
|
|
userStatusDto
|
|
);
|
|
});
|
|
});
|
|
|
|
describe('Performance Integration', () => {
|
|
it('should handle maximum allowed batch size efficiently', async () => {
|
|
// Arrange - Test with maximum allowed batch size
|
|
const userIds = Array.from({ length: BATCH_OPERATION.MAX_USER_COUNT }, (_, i) => `user_${i}`);
|
|
const batchUserStatusDto: BatchUserStatusDto = {
|
|
userIds,
|
|
status: UserStatus.ACTIVE,
|
|
reason: '性能测试'
|
|
};
|
|
|
|
const mockResult = {
|
|
success: true,
|
|
data: {
|
|
result: {
|
|
success_users: userIds.map(id => ({
|
|
id,
|
|
username: `user_${id}`,
|
|
nickname: `用户_${id}`,
|
|
status: UserStatus.ACTIVE,
|
|
status_description: '正常',
|
|
updated_at: new Date()
|
|
})),
|
|
failed_users: [],
|
|
success_count: userIds.length,
|
|
failed_count: 0,
|
|
total_count: userIds.length
|
|
},
|
|
reason: '性能测试'
|
|
},
|
|
message: '批量用户状态修改完成'
|
|
};
|
|
|
|
mockAdminService.batchUpdateUserStatus.mockResolvedValue(mockResult);
|
|
|
|
// Act - Process maximum batch size
|
|
const startTime = Date.now();
|
|
const result = await controller.batchUpdateUserStatus(batchUserStatusDto);
|
|
const endTime = Date.now();
|
|
|
|
// Assert - Verify performance and correctness
|
|
expect(result.success).toBe(true);
|
|
expect(result.data.result.total_count).toBe(BATCH_OPERATION.MAX_USER_COUNT);
|
|
expect(endTime - startTime).toBeLessThan(1000); // Should complete within 1 second
|
|
});
|
|
});
|
|
}); |