Files
gamegroup/src/modules/blacklist/blacklist.service.spec.ts
UGREEN USER 575a29ac8f chore: 代码风格统一和项目文档添加
主要变更:

1. 代码风格统一
   - 统一使用双引号替代单引号
   - 保持项目代码风格一致性
   - 涵盖所有模块、配置、实体和服务文件

2. 项目文档
   - 新增 SECURITY_FIXES_SUMMARY.md - 安全修复总结文档
   - 新增 项目问题评估报告.md - 项目问题评估文档

3. 包含修改的文件类别
   - 配置文件:app, database, jwt, redis, cache, performance
   - 实体文件:所有 TypeORM 实体
   - 模块文件:所有业务模块
   - 公共模块:guards, decorators, interceptors, filters, utils
   - 测试文件:单元测试和 E2E 测试

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
2026-01-28 13:03:28 +08:00

302 lines
9.3 KiB
TypeScript

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<Blacklist>;
let userRepository: Repository<User>;
let groupMemberRepository: Repository<GroupMember>;
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>(BlacklistService);
blacklistRepository = module.get<Repository<Blacklist>>(
getRepositoryToken(Blacklist),
);
userRepository = module.get<Repository<User>>(getRepositoryToken(User));
groupMemberRepository = module.get<Repository<GroupMember>>(
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,
);
});
});
});