重构 API 为 8 领域 + 应用层架构
将后端模块拆分为 domains、applications、shared 三层,结算计算器移入 domain 纯函数目录,API 路径与测试保持不变。 Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
@@ -0,0 +1,187 @@
|
||||
import {
|
||||
settleSelection,
|
||||
calculatePayout,
|
||||
calculateParlayPayout,
|
||||
isQuarterHandicapOrTotal,
|
||||
ScoreInput,
|
||||
} from './settlement-calculator';
|
||||
|
||||
describe('SettlementCalculator', () => {
|
||||
const score: ScoreInput = { htHome: 1, htAway: 0, ftHome: 2, ftAway: 1 };
|
||||
|
||||
describe('FT_1X2', () => {
|
||||
it('S001: home win', () => {
|
||||
expect(
|
||||
settleSelection({ marketType: 'FT_1X2', selectionCode: 'HOME', score }),
|
||||
).toBe('WIN');
|
||||
expect(
|
||||
settleSelection({ marketType: 'FT_1X2', selectionCode: 'DRAW', score }),
|
||||
).toBe('LOSE');
|
||||
});
|
||||
|
||||
it('S002: draw', () => {
|
||||
const draw = { htHome: 0, htAway: 0, ftHome: 1, ftAway: 1 };
|
||||
expect(
|
||||
settleSelection({ marketType: 'FT_1X2', selectionCode: 'DRAW', score: draw }),
|
||||
).toBe('WIN');
|
||||
});
|
||||
});
|
||||
|
||||
describe('HT_1X2', () => {
|
||||
it('S003: half time result', () => {
|
||||
expect(
|
||||
settleSelection({ marketType: 'HT_1X2', selectionCode: 'HOME', score }),
|
||||
).toBe('WIN');
|
||||
});
|
||||
});
|
||||
|
||||
describe('FT_ODD_EVEN', () => {
|
||||
it('S004: 0-0 is even', () => {
|
||||
const s = { htHome: 0, htAway: 0, ftHome: 0, ftAway: 0 };
|
||||
expect(
|
||||
settleSelection({ marketType: 'FT_ODD_EVEN', selectionCode: 'EVEN', score: s }),
|
||||
).toBe('WIN');
|
||||
});
|
||||
});
|
||||
|
||||
describe('Correct Score', () => {
|
||||
it('S005: exact score 2-1', () => {
|
||||
expect(
|
||||
settleSelection({
|
||||
marketType: 'FT_CORRECT_SCORE',
|
||||
selectionCode: 'SCORE_2_1',
|
||||
score,
|
||||
}),
|
||||
).toBe('WIN');
|
||||
});
|
||||
|
||||
it('S006: other home win', () => {
|
||||
const s = { htHome: 2, htAway: 0, ftHome: 5, ftAway: 0 };
|
||||
expect(
|
||||
settleSelection({
|
||||
marketType: 'FT_CORRECT_SCORE',
|
||||
selectionCode: 'OTHER_HOME',
|
||||
score: s,
|
||||
templateScores: ['SCORE_1_0', 'SCORE_2_0'],
|
||||
}),
|
||||
).toBe('WIN');
|
||||
});
|
||||
|
||||
it('S008: second half correct score', () => {
|
||||
expect(
|
||||
settleSelection({
|
||||
marketType: 'SH_CORRECT_SCORE',
|
||||
selectionCode: 'SCORE_1_1',
|
||||
score,
|
||||
}),
|
||||
).toBe('WIN');
|
||||
});
|
||||
});
|
||||
|
||||
describe('Handicap', () => {
|
||||
it('S009: full win', () => {
|
||||
const r = settleSelection({
|
||||
marketType: 'FT_HANDICAP',
|
||||
selectionCode: 'HOME',
|
||||
handicapLine: -1,
|
||||
score: { htHome: 0, htAway: 0, ftHome: 2, ftAway: 0 },
|
||||
});
|
||||
expect(r).toBe('WIN');
|
||||
expect(calculatePayout(100, 1.85, r).toNumber()).toBe(185);
|
||||
});
|
||||
|
||||
it('S010: push', () => {
|
||||
const r = settleSelection({
|
||||
marketType: 'FT_HANDICAP',
|
||||
selectionCode: 'HOME',
|
||||
handicapLine: -1,
|
||||
score: { htHome: 0, htAway: 0, ftHome: 1, ftAway: 0 },
|
||||
});
|
||||
expect(r).toBe('PUSH');
|
||||
expect(calculatePayout(100, 1.85, r).toNumber()).toBe(100);
|
||||
});
|
||||
|
||||
it('S011: half win -0.25', () => {
|
||||
const r = settleSelection({
|
||||
marketType: 'FT_HANDICAP',
|
||||
selectionCode: 'HOME',
|
||||
handicapLine: -0.25,
|
||||
score: { htHome: 0, htAway: 0, ftHome: 0, ftAway: 0 },
|
||||
});
|
||||
expect(r).toBe('HALF_LOSE');
|
||||
});
|
||||
});
|
||||
|
||||
describe('Over/Under', () => {
|
||||
it('S013: over 2.5 wins with 3 goals', () => {
|
||||
const s = { htHome: 1, htAway: 1, ftHome: 2, ftAway: 1 };
|
||||
expect(
|
||||
settleSelection({
|
||||
marketType: 'FT_OVER_UNDER',
|
||||
selectionCode: 'OVER',
|
||||
totalLine: 2.5,
|
||||
score: s,
|
||||
}),
|
||||
).toBe('WIN');
|
||||
});
|
||||
|
||||
it('S014: push on integer line', () => {
|
||||
const s = { htHome: 1, htAway: 0, ftHome: 1, ftAway: 1 };
|
||||
expect(
|
||||
settleSelection({
|
||||
marketType: 'FT_OVER_UNDER',
|
||||
selectionCode: 'OVER',
|
||||
totalLine: 2,
|
||||
score: s,
|
||||
}),
|
||||
).toBe('PUSH');
|
||||
});
|
||||
});
|
||||
|
||||
describe('Parlay', () => {
|
||||
it('S016: all win', () => {
|
||||
const result = calculateParlayPayout(100, [
|
||||
{ odds: 1.8, result: 'WIN' },
|
||||
{ odds: 2.0, result: 'WIN' },
|
||||
]);
|
||||
expect(result.betResult).toBe('WON');
|
||||
expect(result.payout.toNumber()).toBe(360);
|
||||
});
|
||||
|
||||
it('S017: one lose', () => {
|
||||
const result = calculateParlayPayout(100, [
|
||||
{ odds: 1.8, result: 'WIN' },
|
||||
{ odds: 2.0, result: 'LOSE' },
|
||||
]);
|
||||
expect(result.betResult).toBe('LOST');
|
||||
expect(result.payout.toNumber()).toBe(0);
|
||||
});
|
||||
|
||||
it('S018: one push', () => {
|
||||
const result = calculateParlayPayout(100, [
|
||||
{ odds: 1.8, result: 'WIN' },
|
||||
{ odds: 2.0, result: 'PUSH' },
|
||||
{ odds: 1.9, result: 'WIN' },
|
||||
]);
|
||||
expect(result.betResult).toBe('WON');
|
||||
expect(result.payout.toNumber()).toBe(342);
|
||||
});
|
||||
|
||||
it('S019: all push', () => {
|
||||
const result = calculateParlayPayout(100, [
|
||||
{ odds: 1.8, result: 'PUSH' },
|
||||
{ odds: 2.0, result: 'VOID' },
|
||||
]);
|
||||
expect(result.betResult).toBe('PUSH');
|
||||
expect(result.payout.toNumber()).toBe(100);
|
||||
});
|
||||
});
|
||||
|
||||
describe('Quarter line detection', () => {
|
||||
it('detects quarter lines', () => {
|
||||
expect(isQuarterHandicapOrTotal(-0.25)).toBe(true);
|
||||
expect(isQuarterHandicapOrTotal(2.5)).toBe(false);
|
||||
expect(isQuarterHandicapOrTotal(-1)).toBe(false);
|
||||
});
|
||||
});
|
||||
});
|
||||
Reference in New Issue
Block a user