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, ); }); }); });