import { Decimal } from '@prisma/client/runtime/library'; import { CashbackService } from './cashback.service'; describe('CashbackService previewBatch', () => { const tx = { cashbackBatch: { findMany: jest.fn(), create: jest.fn(), }, cashbackItem: { create: jest.fn(), }, cashbackBet: { create: jest.fn(), }, }; const prisma = { cashbackBatch: { findFirst: jest.fn(), }, cashbackBet: { findMany: jest.fn(), }, bet: { findMany: jest.fn(), }, cashbackRule: { findMany: jest.fn(), }, agentProfile: { findMany: jest.fn(), }, user: { findMany: jest.fn(), }, wallet: { findMany: jest.fn(), }, $transaction: jest.fn(async (fn: (client: typeof tx) => Promise) => fn(tx)), }; const funds = {}; const systemConfig = { getPlatformDirectCashbackSettings: jest.fn(), }; let service: CashbackService; const platformPlayerId = 100n; const adminInvitePlayerId = 101n; const adminSponsorId = 200n; const periodStart = new Date('2026-06-01T00:00:00.000Z'); const periodEnd = new Date('2026-06-01T23:59:59.999Z'); beforeEach(() => { jest.clearAllMocks(); service = new CashbackService(prisma as never, funds as never, systemConfig as never); systemConfig.getPlatformDirectCashbackSettings.mockResolvedValue({ platformDirectRate: 0.02, adminInviteRate: 0.05, }); prisma.cashbackBatch.findFirst.mockResolvedValue(null); prisma.cashbackBet.findMany.mockResolvedValue([]); prisma.cashbackRule.findMany.mockResolvedValue([]); prisma.agentProfile.findMany.mockResolvedValue([]); tx.cashbackBatch.findMany.mockResolvedValue([]); tx.cashbackItem.create.mockResolvedValue({}); tx.cashbackBet.create.mockResolvedValue({}); tx.cashbackBatch.create.mockImplementation(({ data }: { data: Record }) => Promise.resolve({ id: 1n, batchNo: 'CB-TEST', ...data }), ); }); it('uses platformDirectRate for platform-direct players', async () => { prisma.bet.findMany.mockResolvedValue([ { id: 1n, userId: platformPlayerId, stake: new Decimal(1000), status: 'WON', settledAt: new Date('2026-06-01T12:00:00.000Z'), user: { id: platformPlayerId, parentId: null, inviteSponsorId: null }, selections: [{ marketType: 'FT_1X2' }], }, ]); prisma.user.findMany.mockResolvedValue([ { id: platformPlayerId, username: 'pd1', parent: null }, ]); prisma.wallet.findMany.mockResolvedValue([ { userId: platformPlayerId, availableBalance: new Decimal(0) }, ]); const result = await service.previewBatch(periodStart, periodEnd); expect(result.items).toHaveLength(1); expect(result.items[0].amount.toString()).toBe('20'); expect(result.totalAmount.toString()).toBe('20'); }); it('uses adminInviteRate for admin-invited platform-direct players', async () => { prisma.bet.findMany.mockResolvedValue([ { id: 2n, userId: adminInvitePlayerId, stake: new Decimal(1000), status: 'LOST', settledAt: new Date('2026-06-01T12:00:00.000Z'), user: { id: adminInvitePlayerId, parentId: null, inviteSponsorId: adminSponsorId, }, selections: [{ marketType: 'FT_1X2' }], }, ]); prisma.user.findMany .mockResolvedValueOnce([{ id: adminSponsorId, userType: 'ADMIN' }]) .mockResolvedValueOnce([{ id: adminInvitePlayerId, username: 'inv1', parent: null }]); prisma.wallet.findMany.mockResolvedValue([ { userId: adminInvitePlayerId, availableBalance: new Decimal(0) }, ]); const result = await service.previewBatch(periodStart, periodEnd); expect(result.items).toHaveLength(1); expect(result.items[0].amount.toString()).toBe('50'); expect(result.totalAmount.toString()).toBe('50'); }); });