Files
whale-town-end/src/business/admin/database_management.service.spec.ts
moyin 5f662ef091 feat: 完善管理员系统和用户管理模块
- 更新管理员控制器和数据库管理功能
- 完善管理员操作日志系统
- 添加全面的属性测试覆盖
- 优化用户管理和用户档案服务
- 更新代码检查规范文档

功能改进:
- 增强管理员权限验证
- 完善操作日志记录
- 优化数据库管理接口
- 提升系统安全性和可维护性
2026-01-09 17:05:08 +08:00

492 lines
15 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
/**
* DatabaseManagementService 单元测试
*
* 功能描述:
* - 测试数据库管理服务的所有方法
* - 验证CRUD操作的正确性
* - 测试异常处理和边界情况
*
* 职责分离:
* - 业务逻辑测试不涉及HTTP层
* - Mock数据库服务专注业务服务逻辑
* - 验证数据处理和格式化的正确性
*
* 最近修改:
* - 2026-01-09: 功能新增 - 创建DatabaseManagementService单元测试 (修改者: moyin)
*
* @author moyin
* @version 1.0.0
* @since 2026-01-09
* @lastModified 2026-01-09
*/
import { Test, TestingModule } from '@nestjs/testing';
import { NotFoundException, ConflictException } from '@nestjs/common';
import { DatabaseManagementService } from './database_management.service';
import { UsersService } from '../../core/db/users/users.service';
import { UserProfilesService } from '../../core/db/user_profiles/user_profiles.service';
import { ZulipAccountsService } from '../../core/db/zulip_accounts/zulip_accounts.service';
import { Users } from '../../core/db/users/users.entity';
import { UserProfiles } from '../../core/db/user_profiles/user_profiles.entity';
describe('DatabaseManagementService', () => {
let service: DatabaseManagementService;
let usersService: jest.Mocked<UsersService>;
let userProfilesService: jest.Mocked<UserProfilesService>;
let zulipAccountsService: jest.Mocked<ZulipAccountsService>;
const mockUsersService = {
findAll: jest.fn(),
findOne: jest.fn(),
search: jest.fn(),
create: jest.fn(),
update: jest.fn(),
remove: jest.fn(),
count: jest.fn(),
};
const mockUserProfilesService = {
findAll: jest.fn(),
findOne: jest.fn(),
findByMap: jest.fn(),
create: jest.fn(),
update: jest.fn(),
remove: jest.fn(),
count: jest.fn(),
};
const mockZulipAccountsService = {
findMany: jest.fn(),
findById: jest.fn(),
getStatusStatistics: jest.fn(),
create: jest.fn(),
update: jest.fn(),
delete: jest.fn(),
batchUpdateStatus: jest.fn(),
};
beforeEach(async () => {
const module: TestingModule = await Test.createTestingModule({
providers: [
DatabaseManagementService,
{
provide: 'UsersService',
useValue: mockUsersService,
},
{
provide: 'IUserProfilesService',
useValue: mockUserProfilesService,
},
{
provide: 'ZulipAccountsService',
useValue: mockZulipAccountsService,
},
],
}).compile();
service = module.get<DatabaseManagementService>(DatabaseManagementService);
usersService = module.get('UsersService');
userProfilesService = module.get('IUserProfilesService');
zulipAccountsService = module.get('ZulipAccountsService');
});
afterEach(() => {
jest.clearAllMocks();
});
describe('getUserList', () => {
it('should return user list successfully', async () => {
const mockUsers = [
{ id: BigInt(1), username: 'user1', email: 'user1@test.com' },
{ id: BigInt(2), username: 'user2', email: 'user2@test.com' }
] as Users[];
usersService.findAll.mockResolvedValue(mockUsers);
usersService.count.mockResolvedValue(2);
const result = await service.getUserList(20, 0);
expect(result.success).toBe(true);
expect(result.data.items).toHaveLength(2);
expect(result.data.total).toBe(2);
expect(result.message).toBe('用户列表获取成功');
});
it('should handle database error', async () => {
usersService.findAll.mockRejectedValue(new Error('Database error'));
const result = await service.getUserList(20, 0);
expect(result.success).toBe(true);
expect(result.data.items).toEqual([]);
expect(result.message).toContain('失败,返回空列表');
});
});
describe('getUserById', () => {
it('should return user by id successfully', async () => {
const mockUser = { id: BigInt(1), username: 'user1', email: 'user1@test.com' } as Users;
usersService.findOne.mockResolvedValue(mockUser);
const result = await service.getUserById(BigInt(1));
expect(result.success).toBe(true);
expect(result.data.id).toBe('1');
expect(result.message).toBe('用户详情获取成功');
});
it('should handle user not found', async () => {
usersService.findOne.mockRejectedValue(new NotFoundException('User not found'));
const result = await service.getUserById(BigInt(999));
expect(result.success).toBe(false);
expect(result.error_code).toBe('RESOURCE_NOT_FOUND');
});
});
describe('searchUsers', () => {
it('should search users successfully', async () => {
const mockUsers = [
{ id: BigInt(1), username: 'admin', email: 'admin@test.com' }
] as Users[];
usersService.search.mockResolvedValue(mockUsers);
const result = await service.searchUsers('admin', 20);
expect(result.success).toBe(true);
expect(result.data.items).toHaveLength(1);
expect(result.message).toBe('用户搜索成功');
});
});
describe('createUser', () => {
it('should create user successfully', async () => {
const userData = { username: 'newuser', email: 'new@test.com', nickname: 'New User' };
const mockUser = { id: BigInt(1), ...userData } as Users;
usersService.create.mockResolvedValue(mockUser);
const result = await service.createUser(userData);
expect(result.success).toBe(true);
expect(result.data.username).toBe('newuser');
expect(result.message).toBe('用户创建成功');
});
it('should handle creation conflict', async () => {
const userData = { username: 'existing', email: 'existing@test.com', nickname: 'Existing' };
usersService.create.mockRejectedValue(new ConflictException('Username already exists'));
const result = await service.createUser(userData);
expect(result.success).toBe(false);
expect(result.error_code).toBe('RESOURCE_CONFLICT');
});
});
describe('updateUser', () => {
it('should update user successfully', async () => {
const updateData = { nickname: 'Updated User' };
const mockUser = { id: BigInt(1), username: 'user1', nickname: 'Updated User' } as Users;
usersService.update.mockResolvedValue(mockUser);
const result = await service.updateUser(BigInt(1), updateData);
expect(result.success).toBe(true);
expect(result.data.nickname).toBe('Updated User');
expect(result.message).toBe('用户更新成功');
});
});
describe('deleteUser', () => {
it('should delete user successfully', async () => {
usersService.remove.mockResolvedValue(undefined);
const result = await service.deleteUser(BigInt(1));
expect(result.success).toBe(true);
expect(result.data.deleted).toBe(true);
expect(result.message).toBe('用户删除成功');
});
});
describe('getUserProfileList', () => {
it('should return user profile list successfully', async () => {
const mockProfiles = [
{ id: BigInt(1), user_id: BigInt(1), bio: 'Test bio' }
] as UserProfiles[];
userProfilesService.findAll.mockResolvedValue(mockProfiles);
userProfilesService.count.mockResolvedValue(1);
const result = await service.getUserProfileList(20, 0);
expect(result.success).toBe(true);
expect(result.data.items).toHaveLength(1);
expect(result.message).toBe('用户档案列表获取成功');
});
});
describe('getUserProfileById', () => {
it('should return user profile by id successfully', async () => {
const mockProfile = { id: BigInt(1), user_id: BigInt(1), bio: 'Test bio' } as UserProfiles;
userProfilesService.findOne.mockResolvedValue(mockProfile);
const result = await service.getUserProfileById(BigInt(1));
expect(result.success).toBe(true);
expect(result.data.id).toBe('1');
expect(result.message).toBe('用户档案详情获取成功');
});
});
describe('getUserProfilesByMap', () => {
it('should return user profiles by map successfully', async () => {
const mockProfiles = [
{ id: BigInt(1), user_id: BigInt(1), current_map: 'plaza' }
] as UserProfiles[];
userProfilesService.findByMap.mockResolvedValue(mockProfiles);
const result = await service.getUserProfilesByMap('plaza', 20, 0);
expect(result.success).toBe(true);
expect(result.data.items).toHaveLength(1);
expect(result.message).toContain('plaza');
});
});
describe('createUserProfile', () => {
it('should create user profile successfully', async () => {
const profileData = {
user_id: '1',
bio: 'Test bio',
resume_content: 'Test resume',
tags: '["tag1"]',
social_links: '{"github":"test"}',
skin_id: '1',
current_map: 'plaza',
pos_x: 100,
pos_y: 200,
status: 1
};
const mockProfile = { id: BigInt(1), user_id: BigInt(1), bio: 'Test bio' } as UserProfiles;
userProfilesService.create.mockResolvedValue(mockProfile);
const result = await service.createUserProfile(profileData);
expect(result.success).toBe(true);
expect(result.message).toBe('用户档案创建成功');
});
});
describe('updateUserProfile', () => {
it('should update user profile successfully', async () => {
const updateData = { bio: 'Updated bio' };
const mockProfile = { id: BigInt(1), user_id: BigInt(1), bio: 'Updated bio' } as UserProfiles;
userProfilesService.update.mockResolvedValue(mockProfile);
const result = await service.updateUserProfile(BigInt(1), updateData);
expect(result.success).toBe(true);
expect(result.message).toBe('用户档案更新成功');
});
});
describe('deleteUserProfile', () => {
it('should delete user profile successfully', async () => {
userProfilesService.remove.mockResolvedValue({ affected: 1, message: 'Deleted successfully' });
const result = await service.deleteUserProfile(BigInt(1));
expect(result.success).toBe(true);
expect(result.data.deleted).toBe(true);
expect(result.message).toBe('用户档案删除成功');
});
});
describe('getZulipAccountList', () => {
it('should return zulip account list successfully', async () => {
const mockAccounts = {
accounts: [{
id: '1',
gameUserId: '1',
zulipUserId: 123,
zulipEmail: 'test@zulip.com',
zulipFullName: 'Test User',
status: 'active' as const,
retryCount: 0,
createdAt: '2026-01-09T00:00:00.000Z',
updatedAt: '2026-01-09T00:00:00.000Z'
}],
total: 1,
count: 1
};
zulipAccountsService.findMany.mockResolvedValue(mockAccounts);
const result = await service.getZulipAccountList(20, 0);
expect(result.success).toBe(true);
expect(result.data.items).toHaveLength(1);
expect(result.message).toBe('Zulip账号关联列表获取成功');
});
});
describe('getZulipAccountById', () => {
it('should return zulip account by id successfully', async () => {
const mockAccount = {
id: '1',
gameUserId: '1',
zulipUserId: 123,
zulipEmail: 'test@zulip.com',
zulipFullName: 'Test User',
status: 'active' as const,
retryCount: 0,
createdAt: '2026-01-09T00:00:00.000Z',
updatedAt: '2026-01-09T00:00:00.000Z'
};
zulipAccountsService.findById.mockResolvedValue(mockAccount);
const result = await service.getZulipAccountById('1');
expect(result.success).toBe(true);
expect(result.data.id).toBe('1');
expect(result.message).toBe('Zulip账号关联详情获取成功');
});
});
describe('getZulipAccountStatistics', () => {
it('should return zulip account statistics successfully', async () => {
const mockStats = {
active: 10,
inactive: 5,
suspended: 2,
error: 1,
total: 18
};
zulipAccountsService.getStatusStatistics.mockResolvedValue(mockStats);
const result = await service.getZulipAccountStatistics();
expect(result.success).toBe(true);
expect(result.data).toEqual(mockStats);
expect(result.message).toBe('Zulip账号关联统计获取成功');
});
});
describe('createZulipAccount', () => {
it('should create zulip account successfully', async () => {
const accountData = {
gameUserId: '1',
zulipUserId: 123,
zulipEmail: 'test@zulip.com',
zulipFullName: 'Test User',
zulipApiKeyEncrypted: 'encrypted_key'
};
const mockAccount = {
id: '1',
gameUserId: '1',
zulipUserId: 123,
zulipEmail: 'test@zulip.com',
zulipFullName: 'Test User',
zulipApiKeyEncrypted: 'encrypted_key',
status: 'active' as const,
retryCount: 0,
createdAt: '2026-01-09T00:00:00.000Z',
updatedAt: '2026-01-09T00:00:00.000Z'
};
zulipAccountsService.create.mockResolvedValue(mockAccount);
const result = await service.createZulipAccount(accountData);
expect(result.success).toBe(true);
expect(result.message).toBe('Zulip账号关联创建成功');
});
});
describe('updateZulipAccount', () => {
it('should update zulip account successfully', async () => {
const updateData = { zulipFullName: 'Updated Name' };
const mockAccount = {
id: '1',
gameUserId: '1',
zulipUserId: 123,
zulipEmail: 'test@zulip.com',
zulipFullName: 'Updated Name',
status: 'active' as const,
retryCount: 0,
createdAt: '2026-01-09T00:00:00.000Z',
updatedAt: '2026-01-09T00:00:00.000Z'
};
zulipAccountsService.update.mockResolvedValue(mockAccount);
const result = await service.updateZulipAccount('1', updateData);
expect(result.success).toBe(true);
expect(result.message).toBe('Zulip账号关联更新成功');
});
});
describe('deleteZulipAccount', () => {
it('should delete zulip account successfully', async () => {
zulipAccountsService.delete.mockResolvedValue(true);
const result = await service.deleteZulipAccount('1');
expect(result.success).toBe(true);
expect(result.data.deleted).toBe(true);
expect(result.message).toBe('Zulip账号关联删除成功');
});
});
describe('batchUpdateZulipAccountStatus', () => {
it('should batch update zulip account status successfully', async () => {
const ids = ['1', '2', '3'];
const status = 'active';
const reason = 'Batch activation';
zulipAccountsService.batchUpdateStatus.mockResolvedValue({
success: true,
updatedCount: 3
});
const result = await service.batchUpdateZulipAccountStatus(ids, status, reason);
expect(result.success).toBe(true);
expect(result.data.success_count).toBe(3);
expect(result.data.failed_count).toBe(0);
expect(result.message).toContain('成功3失败0');
});
it('should handle partial batch update failure', async () => {
const ids = ['1', '2', '3'];
const status = 'active';
zulipAccountsService.batchUpdateStatus.mockResolvedValue({
success: true,
updatedCount: 2
});
const result = await service.batchUpdateZulipAccountStatus(ids, status);
expect(result.success).toBe(true);
expect(result.data.success_count).toBe(2);
expect(result.data.failed_count).toBe(1);
});
});
});