/** * BUILD 1 — the settled/live gate (specs/tier-redesign-spec.md). * The load-bearing monetization boundary: unresolved = paid, resolved = free. */ const request = require('supertest'); const g = require('../../src/utils/snapshotGating'); const settled = { player: 'Settled Sam', stat_type: 'points', line: 12.5, book_odds: -110, fair_odds: -104, season_avg: 14, grade: 'A', confidence: 75, reasoning: { summary: 'real why' }, kill_conditions_triggered: [{ code: 'X', reason: 'r' }], projection: 15, edge_pct: 20, outcome: { result: 'hit', actual: 14 } }; const live = { player: 'Live Larry', stat_type: 'assists', line: 5.5, book_odds: -120, fair_odds: -112, season_avg: 6, archetype: { primary: { name: 'CONDUCTOR' } }, gradedAt: { line: 5.5, odds: -120 }, grade: 'A', confidence: 75, confidence_basis: 'grade_band', reasoning: { summary: 'secret why' }, kill_conditions_triggered: [{ code: 'Y', reason: 'secret' }], projection: 7.1, edge_pct: 29, matchup_grade: 'B', form: 'hot', alt_lines: [{}], kelly: { units: 1 } }; describe('resolution is the flip point — and only a WRITTEN outcome counts', () => { test('a real outcome resolves; void/unrecoverable are terminal ⇒ resolved', () => { expect(g.isResolved(settled)).toBe(true); expect(g.isResolved({ outcome: { result: 'void' } })).toBe(true); expect(g.isResolved({ outcome: 'unrecoverable' })).toBe(true); }); test('FAILS CLOSED — anything unproven is LIVE (paid), never assumed settled', () => { expect(g.isResolved(live)).toBe(false); expect(g.isResolved({ outcome: null })).toBe(false); expect(g.isResolved({ outcome: {} })).toBe(false); // no result field expect(g.isResolved({ outcome: { result: '' } })).toBe(false); expect(g.isResolved({})).toBe(false); expect(g.isResolved(null)).toBe(false); }); test('resolution is NEVER inferred from time or game status', () => { // a grade locked hours ago with no outcome is still LIVE expect(g.isResolved({ gradedAt: { timestamp: '2020-01-01T00:00:00Z' } })).toBe(false); }); }); describe('free tier — settled is FULL, live is a shell', () => { const out = g.gateLiveGrades([settled, live], 'free'); test('a SETTLED grade passes through in full, reasoning included (the proof product)', () => { expect(out[0].grade).toBe('A'); expect(out[0].reasoning.summary).toBe('real why'); expect(out[0].kill_conditions_triggered).toHaveLength(1); expect(out[0].edge_pct).toBe(20); expect(out[0].projection).toBe(15); }); test('a LIVE grade loses every piece of model JUDGMENT', () => { for (const f of ['grade', 'confidence', 'confidence_basis', 'reasoning', 'kill_conditions_triggered', 'projection', 'edge_pct', 'matchup_grade', 'form', 'alt_lines', 'kelly']) { expect(out[1][f]).toBeUndefined(); } expect(out[1].locked).toBe(true); }); test('a LIVE grade KEEPS the free-side data (the tease is real, not empty)', () => { expect(out[1].player).toBe('Live Larry'); expect(out[1].stat_type).toBe('assists'); expect(out[1].line).toBe(5.5); expect(out[1].book_odds).toBe(-120); expect(out[1].fair_odds).toBe(-112); // the fair leg is NEVER the paywall expect(out[1].season_avg).toBe(6); expect(out[1].archetype).toBeTruthy(); }); test('the serialized free payload contains no trace of live judgment', () => { const json = JSON.stringify(out[1]); expect(json).not.toMatch(/secret why|secret|grade_band|hot/); }); }); describe('the tease is AGGREGATE ONLY — never which prop is which tier', () => { test('reports count + tier distribution', () => { const s = g.liveLockedSummary([settled, live, { grade: 'B' }, { grade: 'C' }]); expect(s.count).toBe(3); // settled excluded from the live count expect(s.tiers).toEqual({ A: 1, B: 1, C: 1 }); }); test('no gated ROW carries a tier, so the aggregate cannot be joined back', () => { const out = g.gateLiveGrades([live, { player: 'P2', grade: 'B' }], 'free'); for (const row of out) expect(row.grade).toBeUndefined(); }); }); describe('entitled tiers are untouched', () => { test('analyst and desk get the array back by reference — zero cost, zero change', () => { const rows = [settled, live]; expect(g.gateLiveGrades(rows, 'analyst')).toBe(rows); expect(g.gateLiveGrades(rows, 'desk')).toBe(rows); expect(g.entitledToLiveGrades('free')).toBe(false); expect(g.entitledToLiveGrades(undefined)).toBe(false); // anonymous }); }); // NOTE: the route-level assertion is deliberately NOT here. It needs a live Redis, // and a single-suite run with redis down hangs on ioredis' reconnect timer (a known // local behaviour, see CLAUDE.md). The gate's CONTRACT is fully covered by the pure // tests above; the wire is verified against PROD anonymously in the fingerprint.