forked from datawhale/whale-town-end
435 lines
16 KiB
TypeScript
435 lines
16 KiB
TypeScript
/**
|
||
* 管理员数据库管理集成测试
|
||
*
|
||
* 功能描述:
|
||
* - 测试管理员数据库管理的完整功能
|
||
* - 验证CRUD操作的正确性
|
||
* - 测试权限控制和错误处理
|
||
* - 验证响应格式的一致性
|
||
*
|
||
* 测试覆盖:
|
||
* - 用户管理功能测试
|
||
* - 用户档案管理功能测试
|
||
* - Zulip账号关联管理功能测试
|
||
* - 批量操作功能测试
|
||
* - 错误处理和边界条件测试
|
||
*
|
||
* 最近修改:
|
||
* - 2026-01-08: 注释规范优化 - 修正@author字段,更新版本号和修改记录 (修改者: moyin)
|
||
* - 2026-01-08: 功能新增 - 创建管理员数据库管理集成测试 (修改者: assistant)
|
||
*
|
||
* @author moyin
|
||
* @version 1.0.1
|
||
* @since 2026-01-08
|
||
* @lastModified 2026-01-08
|
||
*/
|
||
|
||
import { Test, TestingModule } from '@nestjs/testing';
|
||
import { INestApplication } from '@nestjs/common';
|
||
import { ConfigModule } from '@nestjs/config';
|
||
import { TypeOrmModule } from '@nestjs/typeorm';
|
||
import { AdminDatabaseController } from '../controllers/admin_database.controller';
|
||
import { DatabaseManagementService } from '../services/database_management.service';
|
||
import { AdminOperationLogService } from '../services/admin_operation_log.service';
|
||
import { AdminOperationLogInterceptor } from '../admin_operation_log.interceptor';
|
||
import { AdminDatabaseExceptionFilter } from '../admin_database_exception.filter';
|
||
import { AdminGuard } from '../admin.guard';
|
||
import { UserStatus } from '../../../core/db/users/user_status.enum';
|
||
|
||
describe('Admin Database Management Integration Tests', () => {
|
||
let app: INestApplication;
|
||
let module: TestingModule;
|
||
let controller: AdminDatabaseController;
|
||
let service: DatabaseManagementService;
|
||
|
||
// 测试数据
|
||
const testUser = {
|
||
username: 'admin-test-user',
|
||
nickname: '管理员测试用户',
|
||
email: 'admin-test@example.com',
|
||
role: 1,
|
||
status: UserStatus.ACTIVE
|
||
};
|
||
|
||
const testProfile = {
|
||
user_id: '1',
|
||
bio: '管理员测试档案',
|
||
current_map: 'test-plaza',
|
||
pos_x: 100.5,
|
||
pos_y: 200.3,
|
||
status: 1
|
||
};
|
||
|
||
const testZulipAccount = {
|
||
gameUserId: '1',
|
||
zulipUserId: 12345,
|
||
zulipEmail: 'test@zulip.com',
|
||
zulipFullName: '测试用户',
|
||
zulipApiKeyEncrypted: 'encrypted_test_key',
|
||
status: 'active'
|
||
};
|
||
|
||
beforeAll(async () => {
|
||
module = await Test.createTestingModule({
|
||
imports: [
|
||
ConfigModule.forRoot({
|
||
isGlobal: true,
|
||
envFilePath: ['.env.test', '.env']
|
||
})
|
||
],
|
||
controllers: [AdminDatabaseController],
|
||
providers: [
|
||
DatabaseManagementService,
|
||
// Mock AdminOperationLogService for testing
|
||
{
|
||
provide: AdminOperationLogService,
|
||
useValue: {
|
||
createLog: jest.fn().mockResolvedValue({}),
|
||
queryLogs: jest.fn().mockResolvedValue({ logs: [], total: 0 }),
|
||
getLogById: jest.fn().mockResolvedValue(null),
|
||
getStatistics: jest.fn().mockResolvedValue({}),
|
||
cleanupExpiredLogs: jest.fn().mockResolvedValue(0),
|
||
getAdminOperationHistory: jest.fn().mockResolvedValue([]),
|
||
getSensitiveOperations: jest.fn().mockResolvedValue({ logs: [], total: 0 })
|
||
}
|
||
},
|
||
// Mock AdminOperationLogInterceptor
|
||
{
|
||
provide: AdminOperationLogInterceptor,
|
||
useValue: {
|
||
intercept: jest.fn().mockImplementation((context, next) => next.handle())
|
||
}
|
||
},
|
||
{
|
||
provide: 'UsersService',
|
||
useValue: {
|
||
findAll: jest.fn().mockResolvedValue([]),
|
||
findOne: jest.fn().mockResolvedValue({ ...testUser, id: BigInt(1) }),
|
||
create: jest.fn().mockResolvedValue({ ...testUser, id: BigInt(1) }),
|
||
update: jest.fn().mockResolvedValue({ ...testUser, id: BigInt(1) }),
|
||
remove: jest.fn().mockResolvedValue(undefined),
|
||
search: jest.fn().mockResolvedValue([]),
|
||
count: jest.fn().mockResolvedValue(0)
|
||
}
|
||
},
|
||
{
|
||
provide: 'IUserProfilesService',
|
||
useValue: {
|
||
findAll: jest.fn().mockResolvedValue([]),
|
||
findOne: jest.fn().mockResolvedValue({ ...testProfile, id: BigInt(1) }),
|
||
create: jest.fn().mockResolvedValue({ ...testProfile, id: BigInt(1) }),
|
||
update: jest.fn().mockResolvedValue({ ...testProfile, id: BigInt(1) }),
|
||
remove: jest.fn().mockResolvedValue(undefined),
|
||
findByMap: jest.fn().mockResolvedValue([]),
|
||
count: jest.fn().mockResolvedValue(0)
|
||
}
|
||
},
|
||
{
|
||
provide: 'ZulipAccountsService',
|
||
useValue: {
|
||
findMany: jest.fn().mockResolvedValue({ accounts: [] }),
|
||
findById: jest.fn().mockResolvedValue(testZulipAccount),
|
||
create: jest.fn().mockResolvedValue({ ...testZulipAccount, id: '1' }),
|
||
update: jest.fn().mockResolvedValue({ ...testZulipAccount, id: '1' }),
|
||
delete: jest.fn().mockResolvedValue(undefined),
|
||
getStatusStatistics: jest.fn().mockResolvedValue({
|
||
active: 0,
|
||
inactive: 0,
|
||
suspended: 0,
|
||
error: 0,
|
||
total: 0
|
||
})
|
||
}
|
||
}
|
||
]
|
||
})
|
||
.overrideGuard(AdminGuard)
|
||
.useValue({ canActivate: () => true })
|
||
.compile();
|
||
|
||
app = module.createNestApplication();
|
||
app.useGlobalFilters(new AdminDatabaseExceptionFilter());
|
||
await app.init();
|
||
|
||
controller = module.get<AdminDatabaseController>(AdminDatabaseController);
|
||
service = module.get<DatabaseManagementService>(DatabaseManagementService);
|
||
});
|
||
|
||
afterAll(async () => {
|
||
await app.close();
|
||
});
|
||
|
||
describe('用户管理功能测试', () => {
|
||
it('应该成功获取用户列表', async () => {
|
||
const result = await controller.getUserList(20, 0);
|
||
|
||
expect(result).toBeDefined();
|
||
expect(result.success).toBe(true);
|
||
expect(result.data).toBeDefined();
|
||
expect(result.data.items).toBeInstanceOf(Array);
|
||
expect(result.data.total).toBeDefined();
|
||
expect(result.data.limit).toBe(20);
|
||
expect(result.data.offset).toBe(0);
|
||
expect(result.message).toBe('用户列表获取成功');
|
||
});
|
||
|
||
it('应该成功获取用户详情', async () => {
|
||
const result = await controller.getUserById('1');
|
||
|
||
expect(result).toBeDefined();
|
||
expect(result.success).toBe(true);
|
||
expect(result.data).toBeDefined();
|
||
expect(result.data.username).toBe(testUser.username);
|
||
expect(result.message).toBe('用户详情获取成功');
|
||
});
|
||
|
||
it('应该成功创建用户', async () => {
|
||
const result = await controller.createUser(testUser);
|
||
|
||
expect(result).toBeDefined();
|
||
expect(result.success).toBe(true);
|
||
expect(result.data).toBeDefined();
|
||
expect(result.data.username).toBe(testUser.username);
|
||
expect(result.message).toBe('用户创建成功');
|
||
});
|
||
|
||
it('应该成功更新用户', async () => {
|
||
const updateData = { nickname: '更新后的昵称' };
|
||
const result = await controller.updateUser('1', updateData);
|
||
|
||
expect(result).toBeDefined();
|
||
expect(result.success).toBe(true);
|
||
expect(result.data).toBeDefined();
|
||
expect(result.message).toBe('用户更新成功');
|
||
});
|
||
|
||
it('应该成功删除用户', async () => {
|
||
const result = await controller.deleteUser('1');
|
||
|
||
expect(result).toBeDefined();
|
||
expect(result.success).toBe(true);
|
||
expect(result.data.deleted).toBe(true);
|
||
expect(result.message).toBe('用户删除成功');
|
||
});
|
||
|
||
it('应该成功搜索用户', async () => {
|
||
const result = await controller.searchUsers('admin', 20);
|
||
|
||
expect(result).toBeDefined();
|
||
expect(result.success).toBe(true);
|
||
expect(result.data).toBeDefined();
|
||
expect(result.data.items).toBeInstanceOf(Array);
|
||
expect(result.message).toBe('用户搜索成功');
|
||
});
|
||
});
|
||
|
||
describe('用户档案管理功能测试', () => {
|
||
it('应该成功获取用户档案列表', async () => {
|
||
const result = await controller.getUserProfileList(20, 0);
|
||
|
||
expect(result).toBeDefined();
|
||
expect(result.success).toBe(true);
|
||
expect(result.data).toBeDefined();
|
||
expect(result.data.items).toBeInstanceOf(Array);
|
||
expect(result.message).toBe('用户档案列表获取成功');
|
||
});
|
||
|
||
it('应该成功获取用户档案详情', async () => {
|
||
const result = await controller.getUserProfileById('1');
|
||
|
||
expect(result).toBeDefined();
|
||
expect(result.success).toBe(true);
|
||
expect(result.data).toBeDefined();
|
||
expect(result.data.user_id).toBe(testProfile.user_id);
|
||
expect(result.message).toBe('用户档案详情获取成功');
|
||
});
|
||
|
||
it('应该成功创建用户档案', async () => {
|
||
const result = await controller.createUserProfile(testProfile);
|
||
|
||
expect(result).toBeDefined();
|
||
expect(result.success).toBe(true);
|
||
expect(result.data).toBeDefined();
|
||
expect(result.data.user_id).toBe(testProfile.user_id);
|
||
expect(result.message).toBe('用户档案创建成功');
|
||
});
|
||
|
||
it('应该成功更新用户档案', async () => {
|
||
const updateData = { bio: '更新后的简介' };
|
||
const result = await controller.updateUserProfile('1', updateData);
|
||
|
||
expect(result).toBeDefined();
|
||
expect(result.success).toBe(true);
|
||
expect(result.data).toBeDefined();
|
||
expect(result.message).toBe('用户档案更新成功');
|
||
});
|
||
|
||
it('应该成功删除用户档案', async () => {
|
||
const result = await controller.deleteUserProfile('1');
|
||
|
||
expect(result).toBeDefined();
|
||
expect(result.success).toBe(true);
|
||
expect(result.data.deleted).toBe(true);
|
||
expect(result.message).toBe('用户档案删除成功');
|
||
});
|
||
|
||
it('应该成功根据地图获取用户档案', async () => {
|
||
const result = await controller.getUserProfilesByMap('plaza', 20, 0);
|
||
|
||
expect(result).toBeDefined();
|
||
expect(result.success).toBe(true);
|
||
expect(result.data).toBeDefined();
|
||
expect(result.data.items).toBeInstanceOf(Array);
|
||
expect(result.message).toBe('地图 plaza 的用户档案获取成功');
|
||
});
|
||
});
|
||
|
||
describe('Zulip账号关联管理功能测试', () => {
|
||
it('应该成功获取Zulip账号关联列表', async () => {
|
||
const result = await controller.getZulipAccountList(20, 0);
|
||
|
||
expect(result).toBeDefined();
|
||
expect(result.success).toBe(true);
|
||
expect(result.data).toBeDefined();
|
||
expect(result.data.items).toBeInstanceOf(Array);
|
||
expect(result.message).toBe('Zulip账号关联列表获取成功');
|
||
});
|
||
|
||
it('应该成功获取Zulip账号关联详情', async () => {
|
||
const result = await controller.getZulipAccountById('1');
|
||
|
||
expect(result).toBeDefined();
|
||
expect(result.success).toBe(true);
|
||
expect(result.data).toBeDefined();
|
||
expect(result.data.gameUserId).toBe(testZulipAccount.gameUserId);
|
||
expect(result.message).toBe('Zulip账号关联详情获取成功');
|
||
});
|
||
|
||
it('应该成功创建Zulip账号关联', async () => {
|
||
const result = await controller.createZulipAccount(testZulipAccount);
|
||
|
||
expect(result).toBeDefined();
|
||
expect(result.success).toBe(true);
|
||
expect(result.data).toBeDefined();
|
||
expect(result.data.gameUserId).toBe(testZulipAccount.gameUserId);
|
||
expect(result.message).toBe('Zulip账号关联创建成功');
|
||
});
|
||
|
||
it('应该成功更新Zulip账号关联', async () => {
|
||
const updateData = { status: 'inactive' };
|
||
const result = await controller.updateZulipAccount('1', updateData);
|
||
|
||
expect(result).toBeDefined();
|
||
expect(result.success).toBe(true);
|
||
expect(result.data).toBeDefined();
|
||
expect(result.message).toBe('Zulip账号关联更新成功');
|
||
});
|
||
|
||
it('应该成功删除Zulip账号关联', async () => {
|
||
const result = await controller.deleteZulipAccount('1');
|
||
|
||
expect(result).toBeDefined();
|
||
expect(result.success).toBe(true);
|
||
expect(result.data.deleted).toBe(true);
|
||
expect(result.message).toBe('Zulip账号关联删除成功');
|
||
});
|
||
|
||
it('应该成功批量更新Zulip账号状态', async () => {
|
||
const batchData = {
|
||
ids: ['1', '2', '3'],
|
||
status: 'active' as 'active' | 'inactive' | 'suspended' | 'error',
|
||
reason: '批量激活测试'
|
||
};
|
||
const result = await controller.batchUpdateZulipAccountStatus(batchData);
|
||
|
||
expect(result).toBeDefined();
|
||
expect(result.success).toBe(true);
|
||
expect(result.data).toBeDefined();
|
||
expect(result.data.total).toBe(3);
|
||
expect(result.message).toContain('批量更新完成');
|
||
});
|
||
|
||
it('应该成功获取Zulip账号关联统计', async () => {
|
||
const result = await controller.getZulipAccountStatistics();
|
||
|
||
expect(result).toBeDefined();
|
||
expect(result.success).toBe(true);
|
||
expect(result.data).toBeDefined();
|
||
expect(result.data.total).toBeDefined();
|
||
expect(result.message).toBe('Zulip账号关联统计获取成功');
|
||
});
|
||
});
|
||
|
||
describe('系统功能测试', () => {
|
||
it('应该成功进行健康检查', async () => {
|
||
const result = await controller.healthCheck();
|
||
|
||
expect(result).toBeDefined();
|
||
expect(result.success).toBe(true);
|
||
expect(result.data).toBeDefined();
|
||
expect(result.data.status).toBe('healthy');
|
||
expect(result.data.services).toBeDefined();
|
||
expect(result.message).toBe('数据库管理系统运行正常');
|
||
});
|
||
});
|
||
|
||
describe('响应格式一致性测试', () => {
|
||
it('所有成功响应应该有统一的格式', async () => {
|
||
const responses = [
|
||
await controller.getUserList(20, 0),
|
||
await controller.getUserById('1'),
|
||
await controller.getUserProfileList(20, 0),
|
||
await controller.getZulipAccountList(20, 0),
|
||
await controller.healthCheck()
|
||
];
|
||
|
||
responses.forEach(response => {
|
||
expect(response).toHaveProperty('success');
|
||
expect(response).toHaveProperty('message');
|
||
expect(response).toHaveProperty('data');
|
||
expect(response).toHaveProperty('timestamp');
|
||
expect(response).toHaveProperty('request_id');
|
||
expect(response.success).toBe(true);
|
||
expect(typeof response.message).toBe('string');
|
||
expect(typeof response.timestamp).toBe('string');
|
||
expect(typeof response.request_id).toBe('string');
|
||
});
|
||
});
|
||
|
||
it('列表响应应该有分页信息', async () => {
|
||
const listResponses = [
|
||
await controller.getUserList(20, 0),
|
||
await controller.getUserProfileList(20, 0),
|
||
await controller.getZulipAccountList(20, 0)
|
||
];
|
||
|
||
listResponses.forEach(response => {
|
||
expect(response.data).toHaveProperty('items');
|
||
expect(response.data).toHaveProperty('total');
|
||
expect(response.data).toHaveProperty('limit');
|
||
expect(response.data).toHaveProperty('offset');
|
||
expect(response.data).toHaveProperty('has_more');
|
||
expect(Array.isArray(response.data.items)).toBe(true);
|
||
expect(typeof response.data.total).toBe('number');
|
||
expect(typeof response.data.limit).toBe('number');
|
||
expect(typeof response.data.offset).toBe('number');
|
||
expect(typeof response.data.has_more).toBe('boolean');
|
||
});
|
||
});
|
||
});
|
||
|
||
describe('参数验证测试', () => {
|
||
it('应该正确处理分页参数限制', async () => {
|
||
// 测试超过最大限制的情况
|
||
const result = await controller.getUserList(200, 0);
|
||
expect(result).toBeDefined();
|
||
expect(result.success).toBe(true);
|
||
});
|
||
|
||
it('应该正确处理搜索参数限制', async () => {
|
||
const result = await controller.searchUsers('test', 100);
|
||
expect(result).toBeDefined();
|
||
expect(result.success).toBe(true);
|
||
});
|
||
});
|
||
}); |