import { Test, TestingModule } from '@nestjs/testing'; import { getRepositoryToken } from '@nestjs/typeorm'; import { Repository } from 'typeorm'; import { BlacklistService } from './blacklist.service'; import { Blacklist } from '../../entities/blacklist.entity'; import { User } from '../../entities/user.entity'; import { GroupMember } from '../../entities/group-member.entity'; import { BlacklistStatus } from '../../common/enums'; import { NotFoundException, ForbiddenException } from '@nestjs/common'; describe('BlacklistService', () => { let service: BlacklistService; let blacklistRepository: Repository; let userRepository: Repository; let groupMemberRepository: Repository; const mockBlacklist = { id: 'blacklist-1', reporterId: 'user-1', targetGameId: 'game-123', targetNickname: '违规玩家', reason: '恶意行为', proofImages: ['image1.jpg'], status: BlacklistStatus.PENDING, createdAt: new Date(), }; const mockUser = { id: 'user-1', username: '举报人', isMember: true, }; const mockGroupMember = { id: 'member-1', userId: 'user-1', groupId: 'group-1', }; const mockQueryBuilder = { leftJoinAndSelect: jest.fn().mockReturnThis(), andWhere: jest.fn().mockReturnThis(), orderBy: jest.fn().mockReturnThis(), getMany: jest.fn(), }; beforeEach(async () => { const module: TestingModule = await Test.createTestingModule({ providers: [ BlacklistService, { provide: getRepositoryToken(Blacklist), useValue: { create: jest.fn(), save: jest.fn(), find: jest.fn(), findOne: jest.fn(), remove: jest.fn(), count: jest.fn(), createQueryBuilder: jest.fn(() => mockQueryBuilder), }, }, { provide: getRepositoryToken(User), useValue: { findOne: jest.fn(), }, }, { provide: getRepositoryToken(GroupMember), useValue: { find: jest.fn(), }, }, ], }).compile(); service = module.get(BlacklistService); blacklistRepository = module.get>(getRepositoryToken(Blacklist)); userRepository = module.get>(getRepositoryToken(User)); groupMemberRepository = module.get>(getRepositoryToken(GroupMember)); }); it('should be defined', () => { expect(service).toBeDefined(); }); describe('create', () => { it('应该成功创建黑名单举报', async () => { const createDto = { targetGameId: 'game-123', targetNickname: '违规玩家', reason: '恶意行为', proofImages: ['image1.jpg'], }; jest.spyOn(userRepository, 'findOne').mockResolvedValue(mockUser as any); jest.spyOn(blacklistRepository, 'create').mockReturnValue(mockBlacklist as any); jest.spyOn(blacklistRepository, 'save').mockResolvedValue(mockBlacklist as any); jest.spyOn(blacklistRepository, 'findOne').mockResolvedValue(mockBlacklist as any); const result = await service.create('user-1', createDto); expect(result).toBeDefined(); expect(blacklistRepository.create).toHaveBeenCalledWith({ ...createDto, reporterId: 'user-1', status: BlacklistStatus.PENDING, }); expect(blacklistRepository.save).toHaveBeenCalled(); }); }); describe('findAll', () => { it('应该返回黑名单列表', async () => { const query = { status: BlacklistStatus.APPROVED }; mockQueryBuilder.getMany.mockResolvedValue([mockBlacklist]); const result = await service.findAll(query); expect(result).toHaveLength(1); expect(blacklistRepository.createQueryBuilder).toHaveBeenCalled(); }); it('应该支持按状态筛选', async () => { const query = { status: BlacklistStatus.PENDING }; mockQueryBuilder.getMany.mockResolvedValue([mockBlacklist]); await service.findAll(query); expect(mockQueryBuilder.andWhere).toHaveBeenCalledWith( 'blacklist.status = :status', { status: BlacklistStatus.PENDING } ); }); }); describe('findOne', () => { it('应该返回单个黑名单记录', async () => { jest.spyOn(blacklistRepository, 'findOne').mockResolvedValue(mockBlacklist as any); const result = await service.findOne('blacklist-1'); expect(result).toBeDefined(); expect(result.id).toBe('blacklist-1'); }); it('记录不存在时应该抛出异常', async () => { jest.spyOn(blacklistRepository, 'findOne').mockResolvedValue(null); await expect(service.findOne('non-existent')).rejects.toThrow(NotFoundException); }); }); describe('review', () => { it('应该成功审核黑名单(会员权限)', async () => { const reviewDto = { status: BlacklistStatus.APPROVED, reviewNote: '确认违规', }; const updatedBlacklist = { ...mockBlacklist, ...reviewDto, reviewerId: 'user-1', }; jest.spyOn(userRepository, 'findOne').mockResolvedValue(mockUser as any); jest.spyOn(blacklistRepository, 'findOne') .mockResolvedValueOnce(mockBlacklist as any) // First call in review method .mockResolvedValueOnce(updatedBlacklist as any); // Second call in findOne at the end jest.spyOn(blacklistRepository, 'save').mockResolvedValue(updatedBlacklist as any); const result = await service.review('user-1', 'blacklist-1', reviewDto); expect(result.status).toBe(BlacklistStatus.APPROVED); expect(blacklistRepository.save).toHaveBeenCalled(); }); it('非会员审核时应该抛出异常', async () => { const reviewDto = { status: BlacklistStatus.APPROVED, }; jest.spyOn(userRepository, 'findOne').mockResolvedValue({ ...mockUser, isMember: false, } as any); await expect(service.review('user-1', 'blacklist-1', reviewDto)).rejects.toThrow(ForbiddenException); }); it('用户不存在时应该抛出异常', async () => { const reviewDto = { status: BlacklistStatus.APPROVED, }; jest.spyOn(userRepository, 'findOne').mockResolvedValue(null); await expect(service.review('user-1', 'blacklist-1', reviewDto)).rejects.toThrow(ForbiddenException); }); }); describe('checkBlacklist', () => { it('应该正确检查玩家是否在黑名单', async () => { jest.spyOn(blacklistRepository, 'findOne').mockResolvedValue({ ...mockBlacklist, status: BlacklistStatus.APPROVED, } as any); const result = await service.checkBlacklist('game-123'); expect(result.isBlacklisted).toBe(true); expect(result.blacklist).toBeDefined(); expect(blacklistRepository.findOne).toHaveBeenCalledWith({ where: { targetGameId: 'game-123', status: BlacklistStatus.APPROVED, }, }); }); it('玩家不在黑名单时应该返回false', async () => { jest.spyOn(blacklistRepository, 'findOne').mockResolvedValue(null); const result = await service.checkBlacklist('game-123'); expect(result.isBlacklisted).toBe(false); expect(result.blacklist).toBeNull(); }); }); describe('remove', () => { it('举报人应该可以删除自己的举报', async () => { jest.spyOn(blacklistRepository, 'findOne').mockResolvedValue(mockBlacklist as any); jest.spyOn(userRepository, 'findOne').mockResolvedValue(mockUser as any); jest.spyOn(blacklistRepository, 'remove').mockResolvedValue(mockBlacklist as any); const result = await service.remove('user-1', 'blacklist-1'); expect(result.message).toBe('删除成功'); expect(blacklistRepository.remove).toHaveBeenCalled(); }); it('会员应该可以删除任何举报', async () => { jest.spyOn(blacklistRepository, 'findOne').mockResolvedValue({ ...mockBlacklist, reporterId: 'other-user', } as any); jest.spyOn(userRepository, 'findOne').mockResolvedValue(mockUser as any); jest.spyOn(blacklistRepository, 'remove').mockResolvedValue(mockBlacklist as any); const result = await service.remove('user-1', 'blacklist-1'); expect(result.message).toBe('删除成功'); }); it('非举报人且非会员删除时应该抛出异常', async () => { jest.spyOn(blacklistRepository, 'findOne').mockResolvedValue({ ...mockBlacklist, reporterId: 'other-user', } as any); jest.spyOn(userRepository, 'findOne').mockResolvedValue({ ...mockUser, isMember: false, } as any); await expect(service.remove('user-1', 'blacklist-1')).rejects.toThrow(ForbiddenException); }); }); });