feat: Feature 1.5 — Bet Submission with 3 methods + performance tracking
Three submission methods: - POST /api/bets/quickslip — structured bet entry - POST /api/bets/screenshot — stub OCR with confirm flow - POST /api/bets/sync — coming soon stub Full bet lifecycle: - PATCH /api/bets/:id/settle — settle with outcome, recalculates performance - GET /api/bets — list with status/book/pagination filters - GET /api/bets/performance — ROI, win rate, profit (weekly/monthly/all_time) Payout calculator handles straight bets (American odds) and parlays (multiplied leg payouts). Performance service recalculates on each settlement and upserts into performance table. 33 new tests, 221 total (194 Node.js + 27 Python), all passing. All backend features for Phase 1 + Phase 2 now complete. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -0,0 +1,63 @@
|
||||
const { calculatePayout, calculateStraightPayout, calculateParlayPayout } = require('../../src/services/payoutCalculator');
|
||||
|
||||
describe('payoutCalculator', () => {
|
||||
describe('straight bets', () => {
|
||||
test('negative odds: -110, $20 → $38.18', () => {
|
||||
const payout = calculatePayout(20, 'straight', [-110]);
|
||||
expect(payout).toBeCloseTo(38.18, 1);
|
||||
});
|
||||
|
||||
test('positive odds: +150, $20 → $50', () => {
|
||||
const payout = calculatePayout(20, 'straight', [150]);
|
||||
expect(payout).toBe(50);
|
||||
});
|
||||
|
||||
test('even odds: -100, $20 → $40', () => {
|
||||
const payout = calculatePayout(20, 'straight', [-100]);
|
||||
expect(payout).toBe(40);
|
||||
});
|
||||
|
||||
test('heavy favorite: -200, $20 → $30', () => {
|
||||
const payout = calculatePayout(20, 'straight', [-200]);
|
||||
expect(payout).toBe(30);
|
||||
});
|
||||
|
||||
test('big underdog: +300, $10 → $40', () => {
|
||||
const payout = calculatePayout(10, 'straight', [300]);
|
||||
expect(payout).toBe(40);
|
||||
});
|
||||
});
|
||||
|
||||
describe('parlay bets', () => {
|
||||
test('2-leg parlay: -110 and -110, $20', () => {
|
||||
const payout = calculatePayout(20, 'parlay', [-110, -110]);
|
||||
// Each leg: 1 + 100/110 = 1.909..., product = 3.647, payout = 20 * 3.647 = 72.93
|
||||
expect(payout).toBeCloseTo(72.93, 0);
|
||||
});
|
||||
|
||||
test('3-leg parlay: -110, -110, +150, $10', () => {
|
||||
const payout = calculatePayout(10, 'parlay', [-110, -110, 150]);
|
||||
// 1.909 * 1.909 * 2.5 = 9.118, payout = 91.18
|
||||
expect(payout).toBeCloseTo(91.18, 0);
|
||||
});
|
||||
|
||||
test('2-leg parlay: +200 and +200, $10', () => {
|
||||
const payout = calculatePayout(10, 'parlay', [200, 200]);
|
||||
// 3 * 3 = 9, payout = 90
|
||||
expect(payout).toBe(90);
|
||||
});
|
||||
});
|
||||
|
||||
describe('edge cases', () => {
|
||||
test('empty odds returns amount', () => {
|
||||
expect(calculatePayout(20, 'straight', [])).toBe(20);
|
||||
});
|
||||
|
||||
test('single leg in parlay treated as straight', () => {
|
||||
const straight = calculatePayout(20, 'straight', [-110]);
|
||||
const parlaySingle = calculatePayout(20, 'parlay', [-110]);
|
||||
// Single leg parlay should use parlay calc but result is same as straight
|
||||
expect(parlaySingle).toBeCloseTo(straight, 1);
|
||||
});
|
||||
});
|
||||
});
|
||||
Reference in New Issue
Block a user