const fs = require('fs'); const path = require('path'); // ───────────────────────────────────────────────────────────── // VYNDR — Supplement Intelligence Systems // Pure logic tests: all constants and formulas inlined // ───────────────────────────────────────────────────────────── describe('Supplement Intelligence Systems', () => { // ═══════════════════════════════════════════════════════════ // System 1 — Coaching Tendencies // ═══════════════════════════════════════════════════════════ describe('Coaching Tendencies', () => { const NBA_COACHING_FIELDS = [ 'pace_preference', 'rotation_depth', 'closing_lineup_consistency', 'garbage_time_threshold', 'challenge_frequency', 'blitz_frequency', 'zone_pct', 'rest_pattern', 'matchup_hunting_rate', 'dnp_volatility', 'second_unit_usage', 'timeout_tendency', ]; const MLB_COACHING_FIELDS = [ 'starter_hook_tendency', 'bullpen_deployment', 'platoon_aggressiveness', 'sacrifice_bunt_rate', 'hit_and_run_rate', 'steal_aggressiveness', 'defensive_shift_rate', 'lineup_consistency', 'rest_day_pattern', 'challenge_aggressiveness', ]; test('NBA coaching fields include all 12 expected keys', () => { expect(NBA_COACHING_FIELDS).toHaveLength(12); expect(NBA_COACHING_FIELDS).toContain('pace_preference'); expect(NBA_COACHING_FIELDS).toContain('timeout_tendency'); }); test('MLB coaching fields include all 10 expected keys', () => { expect(MLB_COACHING_FIELDS).toHaveLength(10); expect(MLB_COACHING_FIELDS).toContain('starter_hook_tendency'); expect(MLB_COACHING_FIELDS).toContain('challenge_aggressiveness'); }); test('parse_nba_coaching_decisions extracts rotation_depth from players with 10+ min', () => { const players = [ { minutes: 32 }, { minutes: 28 }, { minutes: 24 }, { minutes: 20 }, { minutes: 18 }, { minutes: 14 }, { minutes: 12 }, { minutes: 11 }, { minutes: 8 }, { minutes: 5 }, { minutes: 3 }, ]; const rotation_depth = players.filter(p => p.minutes >= 10).length; expect(rotation_depth).toBe(8); }); test('rotation_depth of 8 from 8 players with 10+ minutes', () => { const playerMinutes = [35, 30, 28, 22, 18, 15, 12, 10, 7, 4]; const rotation_depth = playerMinutes.filter(m => m >= 10).length; expect(rotation_depth).toBe(8); }); test('parse_mlb_coaching_decisions extracts starter_hook_tendency', () => { // Hook tendency = avg innings before pull across recent starts const starterInnings = [5.2, 6.0, 5.1, 4.2, 6.1]; const avgHook = starterInnings.reduce((s, v) => s + v, 0) / starterInnings.length; expect(avgHook).toBeCloseTo(5.32, 1); }); test('shift detection: 15% threshold for flagging', () => { const SHIFT_THRESHOLD = 0.15; expect(SHIFT_THRESHOLD).toBe(0.15); }); test('shift detection: 10% change does NOT flag', () => { const SHIFT_THRESHOLD = 0.15; const baseline = 0.50; const current = 0.55; const change = Math.abs(current - baseline) / baseline; expect(change).toBeCloseTo(0.10, 4); expect(change < SHIFT_THRESHOLD).toBe(true); }); test('shift detection: 20% change DOES flag', () => { const SHIFT_THRESHOLD = 0.15; const baseline = 0.50; const current = 0.60; const change = Math.abs(current - baseline) / baseline; expect(change).toBeCloseTo(0.20, 4); expect(change >= SHIFT_THRESHOLD).toBe(true); }); test('shift detection returns direction increased or decreased', () => { const baseline = 0.50; const currentUp = 0.65; const currentDown = 0.35; const dirUp = currentUp > baseline ? 'increased' : 'decreased'; const dirDown = currentDown > baseline ? 'increased' : 'decreased'; expect(dirUp).toBe('increased'); expect(dirDown).toBe('decreased'); }); test('season baseline includes all numeric fields', () => { const seasonBaseline = { pace_preference: 98.5, rotation_depth: 9, closing_lineup_consistency: 0.72, zone_pct: 0.15, rest_pattern: 3.2, }; for (const val of Object.values(seasonBaseline)) { expect(typeof val).toBe('number'); } }); test('recent tendencies calculated from last 15 games', () => { const RECENT_WINDOW = 15; const allGames = Array.from({ length: 40 }, (_, i) => ({ pace: 95 + (i % 10) })); const recentGames = allGames.slice(-RECENT_WINDOW); expect(recentGames).toHaveLength(15); }); test('coaching fields are sport-specific (NBA != MLB)', () => { const overlap = NBA_COACHING_FIELDS.filter(f => MLB_COACHING_FIELDS.includes(f)); expect(overlap).toHaveLength(0); }); }); // ═══════════════════════════════════════════════════════════ // System 2 — Redistribution Engine // ═══════════════════════════════════════════════════════════ describe('Redistribution Engine', () => { function classify_absorption_tier(boost, confidence) { if (boost >= 0.20 && confidence >= 0.75) return 'primary'; if (boost >= 0.10 && confidence >= 0.60) return 'secondary'; if (boost >= 0.05) return 'tertiary'; return 'minimal'; } test('classify_absorption_tier: primary when boost>=0.20 AND confidence>=0.75', () => { expect(classify_absorption_tier(0.25, 0.80)).toBe('primary'); expect(classify_absorption_tier(0.20, 0.75)).toBe('primary'); }); test('classify_absorption_tier: secondary when boost>=0.10 AND confidence>=0.60', () => { expect(classify_absorption_tier(0.15, 0.65)).toBe('secondary'); expect(classify_absorption_tier(0.10, 0.60)).toBe('secondary'); }); test('classify_absorption_tier: tertiary when boost>=0.05', () => { expect(classify_absorption_tier(0.07, 0.40)).toBe('tertiary'); expect(classify_absorption_tier(0.05, 0.10)).toBe('tertiary'); }); test('classify_absorption_tier: minimal when boost<0.05', () => { expect(classify_absorption_tier(0.04, 0.90)).toBe('minimal'); expect(classify_absorption_tier(0.01, 0.50)).toBe('minimal'); }); test('concentrated coach (7-man): backup gets 70% of freed minutes', () => { const rotation_depth = 7; const minutes_freed = 30; const backup_share = rotation_depth <= 7 ? minutes_freed * 0.70 : minutes_freed / 4; expect(backup_share).toBe(21); }); test('distributed coach (10-man): minutes spread across 3-4 players', () => { const rotation_depth = 10; const minutes_freed = 30; const per_player_share = rotation_depth <= 7 ? minutes_freed * 0.70 : minutes_freed / 4; expect(per_player_share).toBe(7.5); // 4 players share equally expect(minutes_freed / 4).toBe(7.5); }); test('usage-efficiency tradeoff: -1.5% TS per +5% usage applied correctly', () => { // Formula: penalty = raw_boost * (-0.015 / 0.05) const raw_boost = 0.10; const penalty = raw_boost * (-0.015 / 0.05); expect(penalty).toBeCloseTo(-0.03, 4); }); test('net boost = raw_boost + efficiency_penalty', () => { const raw_boost = 0.10; const penalty = raw_boost * (-0.015 / 0.05); const net_boost = raw_boost + penalty; expect(net_boost).toBeCloseTo(0.07, 4); }); test('system change: primary_scorer out -> secondary_creator gets +0.08', () => { const SYSTEM_SHIFTS = { primary_scorer: { secondary_creator: 0.08, tertiary_scorer: 0.05 }, primary_playmaker: { secondary_creator: 0.06 }, interior_big: { stretch_big: 0.07 }, }; expect(SYSTEM_SHIFTS.primary_scorer.secondary_creator).toBe(0.08); }); test('system change: primary_playmaker out -> secondary_creator gets +0.06', () => { const SYSTEM_SHIFTS = { primary_scorer: { secondary_creator: 0.08 }, primary_playmaker: { secondary_creator: 0.06 }, interior_big: { stretch_big: 0.07 }, }; expect(SYSTEM_SHIFTS.primary_playmaker.secondary_creator).toBe(0.06); }); test('system change: interior_big out -> stretch_big gets +0.07', () => { const SYSTEM_SHIFTS = { primary_scorer: { secondary_creator: 0.08 }, primary_playmaker: { secondary_creator: 0.06 }, interior_big: { stretch_big: 0.07 }, }; expect(SYSTEM_SHIFTS.interior_big.stretch_big).toBe(0.07); }); test('auto-grade threshold: 15%+ boost AND 0.65+ confidence', () => { const AUTO_BOOST_THRESHOLD = 0.15; const AUTO_CONFIDENCE_THRESHOLD = 0.65; const should_auto_grade = (boost, conf) => boost >= AUTO_BOOST_THRESHOLD && conf >= AUTO_CONFIDENCE_THRESHOLD; expect(should_auto_grade(0.18, 0.70)).toBe(true); expect(should_auto_grade(0.15, 0.65)).toBe(true); }); test('auto-grade: 14% boost does NOT trigger auto-grade', () => { const AUTO_BOOST_THRESHOLD = 0.15; const AUTO_CONFIDENCE_THRESHOLD = 0.65; const should_auto_grade = (boost, conf) => boost >= AUTO_BOOST_THRESHOLD && conf >= AUTO_CONFIDENCE_THRESHOLD; expect(should_auto_grade(0.14, 0.90)).toBe(false); }); test('absorption alert format includes is OUT and is underpriced', () => { const playerOut = 'LeBron James'; const beneficiary = 'Anthony Davis'; const alert = `${playerOut} is OUT — ${beneficiary} is underpriced at current line`; expect(alert).toContain('is OUT'); expect(alert).toContain('is underpriced'); }); test('coach-specific redistribution_profile overrides generic system shifts', () => { const generic_boost = 0.08; const coach_profile = { secondary_creator_boost: 0.12 }; const effective_boost = coach_profile.secondary_creator_boost || generic_boost; expect(effective_boost).toBe(0.12); expect(effective_boost).not.toBe(generic_boost); }); }); // ═══════════════════════════════════════════════════════════ // System 3 — Alt Line Scanner // ═══════════════════════════════════════════════════════════ describe('Alt Line Scanner', () => { const A_GRADES = ['A+', 'A', 'A-']; function isEligibleForAltScan(grade) { return A_GRADES.includes(grade); } test('only runs on A-grade props (A+, A, A-)', () => { expect(isEligibleForAltScan('A+')).toBe(true); expect(isEligibleForAltScan('A')).toBe(true); expect(isEligibleForAltScan('A-')).toBe(true); }); test('returns eligible=false for B+ grade', () => { expect(isEligibleForAltScan('B+')).toBe(false); expect(isEligibleForAltScan('B')).toBe(false); expect(isEligibleForAltScan('C')).toBe(false); }); test('edge improvement threshold is 3% (0.03)', () => { const EDGE_IMPROVEMENT_THRESHOLD = 0.03; expect(EDGE_IMPROVEMENT_THRESHOLD).toBe(0.03); }); test('recommends alt when edge_vs_standard >= 0.03', () => { const EDGE_IMPROVEMENT_THRESHOLD = 0.03; const edge_vs_standard = 0.05; const recommend = edge_vs_standard >= EDGE_IMPROVEMENT_THRESHOLD; expect(recommend).toBe(true); }); test('does NOT recommend when edge_vs_standard < 0.03', () => { const EDGE_IMPROVEMENT_THRESHOLD = 0.03; const edge_vs_standard = 0.02; const recommend = edge_vs_standard >= EDGE_IMPROVEMENT_THRESHOLD; expect(recommend).toBe(false); }); test('alt lines sorted by ev_per_dollar descending', () => { const altLines = [ { line: 22.5, ev_per_dollar: 0.04 }, { line: 24.5, ev_per_dollar: 0.12 }, { line: 27.5, ev_per_dollar: 0.08 }, ]; const sorted = [...altLines].sort((a, b) => b.ev_per_dollar - a.ev_per_dollar); expect(sorted[0].ev_per_dollar).toBe(0.12); expect(sorted[1].ev_per_dollar).toBe(0.08); expect(sorted[2].ev_per_dollar).toBe(0.04); }); test('returns top 5 positive EV alts only', () => { const altLines = [ { line: 20.5, ev_per_dollar: 0.15 }, { line: 21.5, ev_per_dollar: 0.12 }, { line: 22.5, ev_per_dollar: 0.09 }, { line: 23.5, ev_per_dollar: 0.06 }, { line: 24.5, ev_per_dollar: 0.03 }, { line: 25.5, ev_per_dollar: 0.01 }, { line: 26.5, ev_per_dollar: -0.02 }, ]; const positiveEV = altLines .filter(a => a.ev_per_dollar > 0) .sort((a, b) => b.ev_per_dollar - a.ev_per_dollar) .slice(0, 5); expect(positiveEV).toHaveLength(5); expect(positiveEV.every(a => a.ev_per_dollar > 0)).toBe(true); }); test('model probability calculation: over = 1 - CDF, under = CDF', () => { // Inline normalCDF approximation function normalCDF(x, mean, stddev) { const z = (x - mean) / stddev; const t = 1 / (1 + 0.2316419 * Math.abs(z)); const d = 0.3989422804014327; const p = d * Math.exp(-z * z / 2) * (t * (0.3193815 + t * (-0.3565638 + t * (1.781478 + t * (-1.821256 + t * 1.330274))))); return z > 0 ? 1 - p : p; } const mean = 25, stddev = 5, line = 25; const prob_over = 1 - normalCDF(line, mean, stddev); const prob_under = normalCDF(line, mean, stddev); expect(prob_over).toBeCloseTo(0.5, 1); expect(prob_under).toBeCloseTo(0.5, 1); }); test('optimal alt is first element after sorting', () => { const sorted = [ { line: 24.5, ev_per_dollar: 0.12 }, { line: 22.5, ev_per_dollar: 0.08 }, { line: 27.5, ev_per_dollar: 0.04 }, ]; const optimal = sorted[0]; expect(optimal.line).toBe(24.5); expect(optimal.ev_per_dollar).toBe(0.12); }); test('alt line includes bookmaker field', () => { const altLine = { line: 22.5, odds: -130, book: 'draftkings', ev_per_dollar: 0.08 }; expect(altLine).toHaveProperty('book'); expect(altLine.book).toBe('draftkings'); }); }); // ═══════════════════════════════════════════════════════════ // System 4 — Unconventional Data Pipeline // ═══════════════════════════════════════════════════════════ describe('Unconventional Data Pipeline', () => { const MIN_INSTANCES = 500; const MIN_PEARSON_R = 0.15; const BASE_ALPHA = 0.05; function validateFactor(instances, r, p_value, num_tests) { if (instances < MIN_INSTANCES) return { validated: false, reason: 'insufficient_instances' }; if (Math.abs(r) < MIN_PEARSON_R) return { validated: false, reason: 'weak_correlation' }; const corrected_alpha = BASE_ALPHA / num_tests; if (p_value >= corrected_alpha) return { validated: false, reason: 'not_significant' }; return { validated: true, corrected_alpha }; } const UNCONVENTIONAL_FACTORS = [ { name: 'travel_distance', validated: true }, { name: 'altitude_adjustment', validated: false }, { name: 'circadian_rhythm', validated: false }, { name: 'back_to_back_fatigue', validated: true }, { name: 'timezone_crossing', validated: false }, ]; test('validation requires minimum 500 instances', () => { expect(MIN_INSTANCES).toBe(500); }); test('fails with 499 instances', () => { const result = validateFactor(499, 0.20, 0.001, 4); expect(result.validated).toBe(false); expect(result.reason).toBe('insufficient_instances'); }); test('passes with 500+ instances (if r and p pass)', () => { const result = validateFactor(600, 0.25, 0.001, 4); expect(result.validated).toBe(true); }); test('minimum Pearson r is 0.15', () => { expect(MIN_PEARSON_R).toBe(0.15); }); test('fails when r < 0.15', () => { const result = validateFactor(600, 0.10, 0.001, 4); expect(result.validated).toBe(false); expect(result.reason).toBe('weak_correlation'); }); test('Bonferroni correction: alpha = 0.05 / number_of_active_tests', () => { const num_tests = 4; const corrected = BASE_ALPHA / num_tests; expect(corrected).toBe(0.0125); }); test('with 4 unvalidated factors, corrected alpha = 0.0125', () => { const unvalidated = UNCONVENTIONAL_FACTORS.filter(f => !f.validated); expect(unvalidated).toHaveLength(3); // When testing 4 factors simultaneously const corrected = BASE_ALPHA / 4; expect(corrected).toBe(0.0125); }); test('with 1 unvalidated factor, corrected alpha = 0.05', () => { const corrected = BASE_ALPHA / 1; expect(corrected).toBe(0.05); }); test('travel_distance starts as validated=True', () => { const travel = UNCONVENTIONAL_FACTORS.find(f => f.name === 'travel_distance'); expect(travel.validated).toBe(true); }); test('altitude_adjustment starts as validated=False', () => { const altitude = UNCONVENTIONAL_FACTORS.find(f => f.name === 'altitude_adjustment'); expect(altitude.validated).toBe(false); }); test('factor only enters grading engine AFTER validation (validated=True check)', () => { const activeFactors = UNCONVENTIONAL_FACTORS.filter(f => f.validated); expect(activeFactors.every(f => f.validated === true)).toBe(true); expect(activeFactors.map(f => f.name)).toContain('travel_distance'); expect(activeFactors.map(f => f.name)).not.toContain('altitude_adjustment'); }); test('status endpoint returns all 5 factor names', () => { expect(UNCONVENTIONAL_FACTORS).toHaveLength(5); const names = UNCONVENTIONAL_FACTORS.map(f => f.name); expect(names).toContain('travel_distance'); expect(names).toContain('altitude_adjustment'); expect(names).toContain('circadian_rhythm'); expect(names).toContain('back_to_back_fatigue'); expect(names).toContain('timezone_crossing'); }); }); // ═══════════════════════════════════════════════════════════ // System 5 — Evolution Alerting // ═══════════════════════════════════════════════════════════ describe('Evolution Alerting', () => { const MIN_GAMES = 15; const CHANGE_THRESHOLD = 0.10; const MIN_INFLECTIONS = 2; const NBA_METRICS = ['usage_rate', 'assist_rate', 'three_pa_rate', 'fg_pct', 'minutes']; const MLB_METRICS = ['k_rate', 'bb_rate', 'exit_velocity', 'hard_hit_pct', 'fb_velo']; function detectEvolution(gameCount, inflections) { if (gameCount < MIN_GAMES) return { evolution_detected: false, reason: 'insufficient_games' }; if (inflections.length < MIN_INFLECTIONS) return { evolution_detected: false, reason: 'insufficient_inflections' }; return { evolution_detected: true, detection_date: new Date().toISOString().split('T')[0], metrics: inflections, }; } function isInflection(baseline, current) { const change = Math.abs(current - baseline) / baseline; return change >= CHANGE_THRESHOLD; } test('evolution detected when 2+ metrics show concurrent inflection', () => { const inflections = ['usage_rate', 'assist_rate']; const result = detectEvolution(20, inflections); expect(result.evolution_detected).toBe(true); }); test('evolution NOT detected with only 1 inflection', () => { const inflections = ['usage_rate']; const result = detectEvolution(20, inflections); expect(result.evolution_detected).toBe(false); expect(result.reason).toBe('insufficient_inflections'); }); test('minimum 15 games required', () => { expect(MIN_GAMES).toBe(15); }); test('returns evolution_detected=false with 14 games', () => { const result = detectEvolution(14, ['usage_rate', 'assist_rate']); expect(result.evolution_detected).toBe(false); expect(result.reason).toBe('insufficient_games'); }); test('change threshold is 10% (0.10)', () => { expect(CHANGE_THRESHOLD).toBe(0.10); }); test('9% change does NOT qualify as inflection', () => { const baseline = 0.20; const current = 0.218; // 9% change expect(isInflection(baseline, current)).toBe(false); }); test('11% change DOES qualify as inflection', () => { const baseline = 0.20; const current = 0.222; // 11% change expect(isInflection(baseline, current)).toBe(true); }); test('NBA metrics: usage_rate, assist_rate, three_pa_rate, fg_pct, minutes', () => { expect(NBA_METRICS).toEqual(['usage_rate', 'assist_rate', 'three_pa_rate', 'fg_pct', 'minutes']); expect(NBA_METRICS).toHaveLength(5); }); test('MLB metrics: k_rate, bb_rate, exit_velocity, hard_hit_pct, fb_velo', () => { expect(MLB_METRICS).toEqual(['k_rate', 'bb_rate', 'exit_velocity', 'hard_hit_pct', 'fb_velo']); expect(MLB_METRICS).toHaveLength(5); }); test('evolution record includes detection_date and metrics', () => { const inflections = ['usage_rate', 'three_pa_rate']; const result = detectEvolution(20, inflections); expect(result).toHaveProperty('detection_date'); expect(result).toHaveProperty('metrics'); expect(result.metrics).toEqual(inflections); }); test('Evolution Watch post format includes Evolution Watch', () => { const playerName = 'Jalen Brunson'; const metrics = ['usage_rate', 'assist_rate']; const post = `Evolution Watch: ${playerName} — ${metrics.join(', ')} trending. The market hasn't priced it yet.`; expect(post).toContain('Evolution Watch'); }); test('Evolution Watch post includes market hasn\'t priced it yet', () => { const post = `Evolution Watch: Player X — usage_rate trending. The market hasn't priced it yet.`; expect(post).toContain("market hasn't priced it yet"); }); }); // ═══════════════════════════════════════════════════════════ // Migration 008 — Table Definitions // ═══════════════════════════════════════════════════════════ describe('Migration 008', () => { const sql = fs.readFileSync( path.join(__dirname, '..', '..', 'supabase', 'migrations', '008_supplement_tables.sql'), 'utf-8' ); test('creates coaching_tendencies table with UNIQUE constraint', () => { expect(sql).toContain('CREATE TABLE IF NOT EXISTS coaching_tendencies'); expect(sql).toContain('UNIQUE(coach_id, team_id, sport, season)'); }); test('creates player_out_history table', () => { expect(sql).toContain('CREATE TABLE IF NOT EXISTS player_out_history'); expect(sql).toContain('player_out_id TEXT NOT NULL'); expect(sql).toContain('beneficiary_stats JSONB NOT NULL'); }); test('creates evolution_detections table', () => { expect(sql).toContain('CREATE TABLE IF NOT EXISTS evolution_detections'); expect(sql).toContain('detection_date DATE NOT NULL'); expect(sql).toContain('metrics JSONB NOT NULL'); }); test('creates unconventional_validations with RLS', () => { expect(sql).toContain('CREATE TABLE IF NOT EXISTS unconventional_validations'); expect(sql).toContain('ALTER TABLE unconventional_validations ENABLE ROW LEVEL SECURITY'); expect(sql).toContain('factor_name TEXT NOT NULL'); }); }); });