- 代理层级默认授信比例与停用冻结/禁登全局默认 - 结算预览去重、比分校验、串关当场判负与市场类型校验 - 站内信 Banner/公告自动通知开关;开发环境动态 API 端口 - 扩充 RBAC/结算/认证/返现单元测试与 agent skills
157 lines
4.5 KiB
TypeScript
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 }),
|
|
}),
|
|
);
|
|
});
|
|
});
|