Files
thebet365/apps/api/src/domains/agent/agents.service.spec.ts
Mars ce84226219 feat(admin+api): 代理停用默认、结算加固与冒烟配置探针
- 代理层级默认授信比例与停用冻结/禁登全局默认

- 结算预览去重、比分校验、串关当场判负与市场类型校验

- 站内信 Banner/公告自动通知开关;开发环境动态 API 端口

- 扩充 RBAC/结算/认证/返现单元测试与 agent skills
2026-06-23 11:08:41 +08:00

157 lines
4.5 KiB
TypeScript

import { Decimal } from '@prisma/client/runtime/library';
import { AgentsService } from './agents.service';
import { createPrismaMock } from '../../testing/prisma-mock';
describe('AgentsService', () => {
const parentAgentId = 1n;
const createdAgentId = 2n;
const tx = {
user: {
create: jest.fn(),
findUnique: jest.fn(),
update: jest.fn(),
},
userAuth: {
create: jest.fn(),
},
userPreference: {
create: jest.fn(),
},
userInvite: {
create: jest.fn(),
findUnique: jest.fn(),
},
agentProfile: {
create: jest.fn(),
findUnique: jest.fn(),
},
agentClosure: {
create: jest.fn(),
findMany: jest.fn(),
},
};
const prismaBase = {
...tx,
agentProfile: {
...tx.agentProfile,
update: jest.fn(),
},
user: {
...tx.user,
updateMany: jest.fn(),
},
};
const prisma = createPrismaMock(prismaBase) as typeof prismaBase & { $transaction: jest.Mock };
prisma.$transaction.mockImplementation(async (arg: unknown) => {
if (Array.isArray(arg)) {
return Promise.all(arg);
}
return (arg as (client: typeof tx) => Promise<unknown>)(tx);
});
const auth = {
hashPassword: jest.fn(),
};
const systemConfig = {
getAgentHierarchySettings: jest.fn(),
getAgentSuspendSettings: jest.fn(),
};
const network = {};
const credit = {
recalculateUsedCredit: jest.fn(),
};
let service: AgentsService;
beforeEach(() => {
jest.clearAllMocks();
service = new AgentsService(
prisma as never,
auth as never,
systemConfig as never,
network as never,
credit as never,
);
systemConfig.getAgentHierarchySettings.mockResolvedValue({
maxAgentLevel: 3,
defaultSubAgentCreditRatio: 50,
});
systemConfig.getAgentSuspendSettings.mockResolvedValue({
suspendFreezeDirectPlayers: true,
suspendBlockPlayerLogin: true,
});
auth.hashPassword.mockResolvedValue('hashed-password');
prisma.agentProfile.findUnique.mockResolvedValue({
userId: parentAgentId,
level: 1,
creditLimit: new Decimal(10000),
usedCredit: new Decimal(2000),
cashbackRate: new Decimal(10),
maxSingleDeposit: null,
maxDailyDeposit: null,
});
tx.user.create.mockResolvedValue({
id: createdAgentId,
username: 'agent-child',
userType: 'AGENT',
});
tx.user.findUnique.mockResolvedValue(null);
tx.user.update.mockResolvedValue({});
tx.userInvite.findUnique.mockResolvedValue(null);
tx.userInvite.create.mockResolvedValue({});
tx.userAuth.create.mockResolvedValue({});
tx.userPreference.create.mockResolvedValue({});
tx.agentProfile.create.mockResolvedValue({});
tx.agentClosure.create.mockResolvedValue({});
tx.agentClosure.findMany.mockResolvedValue([
{ ancestorId: parentAgentId, depth: 0 },
]);
});
it('recalculates parent credit exposure after creating a child agent', async () => {
await service.createAgent(99n, {
username: 'agent-child',
password: 'secret',
level: 2,
parentAgentId,
});
expect(tx.agentProfile.create).toHaveBeenCalledWith(
expect.objectContaining({
data: expect.objectContaining({ creditLimit: 4000 }),
}),
);
expect(credit.recalculateUsedCredit).toHaveBeenCalledWith(parentAgentId);
expect(credit.recalculateUsedCredit).not.toHaveBeenCalledWith(createdAgentId);
});
it('applies global suspend defaults when suspending without explicit flags', async () => {
jest.spyOn(service, 'getAgentAdminDetail').mockResolvedValue({ userId: '10' } as never);
prisma.agentProfile.findUnique.mockResolvedValue({
userId: 10n,
user: { username: 'agent-a', locale: 'zh-CN' },
parentAgentId: null,
});
prisma.user.updateMany.mockResolvedValue({ count: 2 });
prisma.agentProfile.update.mockResolvedValue({});
prisma.user.update.mockResolvedValue({});
await service.updateAgentAdmin(10n, { status: 'SUSPENDED' });
expect(systemConfig.getAgentSuspendSettings).toHaveBeenCalled();
expect(prisma.user.updateMany).toHaveBeenCalledWith(
expect.objectContaining({
where: expect.objectContaining({ parentId: 10n, userType: 'PLAYER' }),
data: { status: 'SUSPENDED' },
}),
);
expect(prisma.agentProfile.update).toHaveBeenCalledWith(
expect.objectContaining({
data: expect.objectContaining({ blockDirectPlayerLogin: true }),
}),
);
});
});