/** * 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; let userProfilesService: jest.Mocked; let zulipAccountsService: jest.Mocked; 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); 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); }); }); });