Files
vyndr/tests/unit/correlationEngine.test.js
T
builtbykev 411cb6f196 feat: Feature 2.1 — Parlay Scan with correlation detection + monetization
POST /api/scan/parlay — authenticated parlay analysis:
- Supabase JWT auth middleware (auth.getUser verification)
- 5 correlation types detected between legs (same_game, same_team,
  same_player_conflicting, positive_correlation, blowout_cascade)
- Overall parlay grading (A/B/C/D) with correlation penalty adjustments
- Free tier: 5 scans/month, atomic scan count increment
- Scan 5: full analysis + personalized upgrade pitch
- Scan 6+: 403 block with upgrade pitch
- Pitch personalization from scan history (top stats, grades, tier rec)
- DB writes: picks + scan_sessions per scan

30 new tests, 158 total (131 Node.js + 27 Python), all passing

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-21 12:45:15 -04:00

103 lines
4.0 KiB
JavaScript

const { detectCorrelations } = require('../../src/services/correlationEngine');
function makeLeg(overrides = {}) {
return {
player: 'Nikola Jokic',
stat_type: 'points',
direction: 'over',
line: 26.5,
grade: 'B',
confidence: 70,
_team: 'DEN',
_gameTime: '2026-03-21T19:00:00Z',
reasoning: { steps: { season_avg: { value: 26.3 } } },
...overrides,
};
}
describe('correlationEngine', () => {
test('returns empty array when no correlations exist', () => {
const legs = [
makeLeg({ player: 'Nikola Jokic', _team: 'DEN', _gameTime: '2026-03-21T19:00:00Z' }),
makeLeg({ player: 'Jayson Tatum', _team: 'BOS', _gameTime: '2026-03-21T20:00:00Z' }),
];
const flags = detectCorrelations(legs, []);
expect(flags).toEqual([]);
});
test('detects same_game_opposing_players', () => {
const legs = [
makeLeg({ player: 'Nikola Jokic', stat_type: 'points', direction: 'over', _team: 'DEN', _gameTime: 'game1' }),
makeLeg({ player: 'LeBron James', stat_type: 'points', direction: 'over', _team: 'LAL', _gameTime: 'game1' }),
];
const flags = detectCorrelations(legs, []);
expect(flags).toHaveLength(1);
expect(flags[0].type).toBe('same_game_opposing_players');
expect(flags[0].impact).toBe('minor_negative');
});
test('detects same_game_same_team', () => {
const legs = [
makeLeg({ player: 'LeBron James', _team: 'LAL', _gameTime: 'game1' }),
makeLeg({ player: 'Anthony Davis', _team: 'LAL', _gameTime: 'game1' }),
];
const flags = detectCorrelations(legs, []);
expect(flags).toHaveLength(1);
expect(flags[0].type).toBe('same_game_same_team');
expect(flags[0].impact).toBe('minor_negative');
});
test('detects same_player_conflicting (opposite directions)', () => {
const legs = [
makeLeg({ player: 'Nikola Jokic', stat_type: 'points', direction: 'over' }),
makeLeg({ player: 'Nikola Jokic', stat_type: 'points', direction: 'under' }),
];
const flags = detectCorrelations(legs, []);
expect(flags).toHaveLength(1);
expect(flags[0].type).toBe('same_player_conflicting');
expect(flags[0].impact).toBe('major_negative');
});
test('detects positive_correlation (same player, complementary same direction)', () => {
const legs = [
makeLeg({ player: 'Nikola Jokic', stat_type: 'points', direction: 'over' }),
makeLeg({ player: 'Nikola Jokic', stat_type: 'rebounds', direction: 'over' }),
];
const flags = detectCorrelations(legs, []);
expect(flags).toHaveLength(1);
expect(flags[0].type).toBe('positive_correlation');
expect(flags[0].impact).toBe('positive');
});
test('detects blowout_cascade (2+ legs, high spread)', () => {
const legs = [
makeLeg({ player: 'Nikola Jokic', _team: 'DEN', _gameTime: 'game1' }),
makeLeg({ player: 'LeBron James', _team: 'LAL', _gameTime: 'game1', stat_type: 'rebounds' }),
];
const spreads = [
{ home_team: 'DEN', away_team: 'LAL', game_time: 'game1', home_spread: -12, book: 'draftkings' },
];
const flags = detectCorrelations(legs, spreads);
const cascade = flags.find((f) => f.type === 'blowout_cascade');
expect(cascade).toBeDefined();
expect(cascade.impact).toBe('major_negative');
});
test('handles single leg (no correlations possible)', () => {
const legs = [makeLeg()];
const flags = detectCorrelations(legs, []);
expect(flags).toEqual([]);
});
test('multiple correlations can fire simultaneously', () => {
const legs = [
makeLeg({ player: 'Nikola Jokic', stat_type: 'points', direction: 'over', _team: 'DEN', _gameTime: 'game1' }),
makeLeg({ player: 'LeBron James', stat_type: 'points', direction: 'over', _team: 'LAL', _gameTime: 'game1' }),
makeLeg({ player: 'Anthony Davis', stat_type: 'rebounds', direction: 'over', _team: 'LAL', _gameTime: 'game1' }),
];
const flags = detectCorrelations(legs, []);
// Jokic vs LeBron = same_game_opposing_players, LeBron vs AD = same_game_same_team
expect(flags.length).toBeGreaterThanOrEqual(2);
});
});