import { settleSelection, calculatePayout, isQuarterHandicapOrTotal, } from './domains/settlement/domain/settlement-calculator'; /** * Agent credit & wallet integration scenarios (A001-A007) * These tests validate business rules without DB dependency. */ describe('Agent Credit Rules', () => { it('A001: deposit increases player balance and reduces agent available credit', () => { const creditLimit = 10000; const usedCredit = 1000; const depositAmount = 500; const newUsed = usedCredit + depositAmount; expect(creditLimit - newUsed).toBe(8500); }); it('A002: bet freeze does not change total balance or agent credit', () => { const available = 1000; const frozen = 0; const stake = 100; const totalBefore = available + frozen; const totalAfter = (available - stake) + (frozen + stake); expect(totalAfter).toBe(totalBefore); }); it('A003: player lose releases agent credit', () => { const usedBefore = 1000; const stake = 100; const usedAfter = usedBefore - stake; expect(usedAfter).toBe(900); }); it('A004: player win increases agent credit usage', () => { const usedBefore = 1000; const payout = 185; const stake = 100; const netGain = payout - stake; const usedAfter = usedBefore + netGain; expect(usedAfter).toBe(1085); }); it('A005: negative credit blocks further deposit', () => { const creditLimit = 1000; const usedCredit = 1200; const available = creditLimit - usedCredit; expect(available).toBeLessThan(0); const canDeposit = available > 0; expect(canDeposit).toBe(false); }); it('A006: level 1 allocating credit to level 2 reduces available', () => { const available = 10000; const allocate = 3000; expect(available - allocate).toBe(7000); }); it('A007: non-direct player deposit should be rejected', () => { const agentId = BigInt(1); const playerParentId = BigInt(2); const canDeposit = agentId === playerParentId; expect(canDeposit).toBe(false); }); it('A008: admin deposit cannot exceed parent agent available credit', () => { const creditLimit = 5000; const usedCredit = 4800; const depositAmount = 300; const available = creditLimit - usedCredit; expect(depositAmount).toBeGreaterThan(available); const allowed = depositAmount <= available; expect(allowed).toBe(false); }); }); describe('Bet Validation Rules (B001-B010)', () => { it('B003: odds version mismatch should reject', () => { const submitted = BigInt(1); const current = BigInt(2); expect(submitted === current).toBe(false); }); it('B007: same match legs allowed in parlay (2–5 legs)', () => { const legs = [ { matchId: '1', selectionId: 'a' }, { matchId: '1', selectionId: 'b' }, ]; expect(legs.length).toBeGreaterThanOrEqual(2); expect(legs.length).toBeLessThanOrEqual(5); }); it('B008: quarter line in parlay rejected', () => { expect(isQuarterHandicapOrTotal(-0.25)).toBe(true); expect(isQuarterHandicapOrTotal(-0.5)).toBe(false); }); it('B009: more than 5 legs rejected', () => { const legs = 6; expect(legs > 5).toBe(true); }); }); describe('Settlement payout accuracy', () => { it('half win payout formula', () => { const payout = calculatePayout(100, 1.85, 'HALF_WIN'); expect(payout.toNumber()).toBe(142.5); }); it('half lose payout formula', () => { const payout = calculatePayout(100, 1.85, 'HALF_LOSE'); expect(payout.toNumber()).toBe(50); }); it('S004: 0-0 odd/even is even', () => { const result = settleSelection({ marketType: 'FT_ODD_EVEN', selectionCode: 'EVEN', score: { htHome: 0, htAway: 0, ftHome: 0, ftAway: 0 }, }); expect(result).toBe('WIN'); }); });