'use strict'; /** * refusalDiagnostics + the grade-slate cap (2026-08-01). * * Locks the two things the Part-1 diagnosis established: * 1. POLICY-SUPPRESSION is not a data gap and must never be counted as one. * 2. A no-projection refusal is only a GENUINE-ABSENCE when the player truly * has no history — otherwise it is a wiring gap with something to fix. */ const { diagnose, __internals } = require('../../src/services/refusalDiagnostics'); const { uniqueGradeable } = __internals; const prop = (player, stat, line, book = 'draftkings') => ({ player, stat_type: stat, line, book, over_odds: -110, under_odds: -110, }); const isModelBook = (b) => ['draftkings', 'fanduel', 'betmgm', 'betrivers'].includes(b); describe('uniqueGradeable — mirrors gradeSlateService.dedupeProps', () => { it('keeps model books only, first row wins, one row per player+stat+line', () => { const out = uniqueGradeable([ prop('A', 'hits', 1.5, 'prizepicks'), // DFS — never reaches the model prop('A', 'hits', 1.5, 'betmgm'), prop('A', 'hits', 1.5, 'draftkings'), // duplicate prop, different book prop('B', 'rbi', 0.5, 'novig'), // exchange — display only ], isModelBook); expect(out).toHaveLength(1); expect(out[0]).toMatchObject({ player: 'A', book: 'betmgm' }); }); it('drops rows missing a player, stat or line rather than guessing', () => { const out = uniqueGradeable([ { player: null, stat_type: 'hits', line: 1.5, book: 'draftkings' }, { player: 'A', stat_type: null, line: 1.5, book: 'draftkings' }, { player: 'A', stat_type: 'hits', line: null, book: 'draftkings' }, ], isModelBook); expect(out).toEqual([]); }); }); describe('diagnose — bucket separation', () => { const baseDeps = (analyze, getStatRows) => ({ sport: 'mlb', sample: 10, concurrency: 2, isModelBook, getStatRows, analyze, getOdds: async () => ({ props: [ prop('Graded One', 'hits', 1.5), prop('Suppressed One', 'doubles', 0.5), prop('NoProj Has History', 'stolen_bases', 0.5), prop('NoProj No History', 'triples', 0.5), ], }), }); const analyze = async (p) => { if (p.stat_type === 'hits') return { grade: 'B', insufficient_data: false }; if (p.stat_type === 'doubles') { return { grade: null, insufficient_data: true, suppressed: true, suppressed_reason: 'rare_event_over_below_line', reasoning: { summary: 'no read — juiced rare-event market' }, }; } return { grade: null, insufficient_data: true, reasoning: { summary: 'INSUFFICIENT DATA — no read.' }, }; }; // Only the player WITH history should count as a fixable gap. const getStatRows = async (player, _sport, stat) => ( player === 'NoProj Has History' ? [{ date: '2026-07-30', [stat]: 1 }] : [] ); it('counts a deliberate suppression as POLICY, never as a data gap', async () => { const r = await diagnose(baseDeps(analyze, getStatRows)); expect(r.outcome.e_POLICY_SUPPRESSION).toBe(1); expect(r.policy_suppression_reasons).toEqual({ rare_event_over_below_line: 1 }); // A suppression must NOT be counted in the no-projection split at all — // treating it as missing data would send us hunting for data that exists. expect(r.no_projection_split.probed).toBe(2); }); it('splits no-projection into FETCHABLE vs GENUINE by real history, not by assumption', async () => { const r = await diagnose(baseDeps(analyze, getStatRows)); expect(r.no_projection_split.b_fetchable_gap).toBe(1); expect(r.no_projection_split.d_genuine_absence).toBe(1); expect(r.no_projection_split.fetchable_examples[0]).toMatchObject({ player: 'NoProj Has History', rows_with_stat: 1, }); }); it('reports what the cap discards, which is the point of the exercise', async () => { const r = await diagnose(baseDeps(analyze, getStatRows)); expect(r.slate.unique_gradeable_props).toBe(4); expect(r.slate).toHaveProperty('capped_out'); expect(r.read_only).toBe(true); }); it('a thrown grade is bucketed, never silently counted as graded', async () => { const boom = async () => { throw new Error('kaboom'); }; const r = await diagnose(baseDeps(boom, getStatRows)); expect(r.outcome.graded).toBe(0); expect(r.outcome.x_THREW).toBe(4); }); }); describe('gradeSlateService cap', () => { it('is raised well past the live slate size and is env-tunable', () => { const { DEFAULT_LIMIT } = require('../../src/services/gradeSlateService').__internals; // The measured live MLB slate carries ~585 unique gradeable props; a cap of // 25 discarded 95.7% of the product. expect(DEFAULT_LIMIT).toBeGreaterThanOrEqual(500); }); });