refactor:项目架构重构和命名规范化
- 统一文件命名为snake_case格式(kebab-case snake_case) - 重构zulip模块为zulip_core,明确Core层职责 - 重构user-mgmt模块为user_mgmt,统一命名规范 - 调整模块依赖关系,优化架构分层 - 删除过时的文件和目录结构 - 更新相关文档和配置文件 本次重构涉及大量文件重命名和模块重组, 旨在建立更清晰的项目架构和统一的命名规范。
This commit is contained in:
436
src/business/user_mgmt/user_mgmt.integration.spec.ts
Normal file
436
src/business/user_mgmt/user_mgmt.integration.spec.ts
Normal file
@@ -0,0 +1,436 @@
|
||||
/**
|
||||
* 用户管理模块集成测试
|
||||
*
|
||||
* 功能描述:
|
||||
* - 测试用户管理模块的完整业务流程
|
||||
* - 测试控制器与服务的集成
|
||||
* - 测试真实的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
|
||||
});
|
||||
});
|
||||
});
|
||||
Reference in New Issue
Block a user