// Session 60 (night2/B) — the VYNDR Lens + form heat. THE RULE: no raw // streak renders alone; every row carries built-vs, tonight's matchup, // difficulty, and a one-line read — all real or absent, never invented. const { buildLens, applyLens, tonightFor, opposingPitcherFor, __internals } = require('../../src/services/streakLens'); const { computeFormHeat } = require('../../src/services/streaksService'); const SCHEDULE = [ { homeTeam: { name: 'Detroit Tigers', abbreviation: 'DET' }, awayTeam: { name: 'Tampa Bay Rays', abbreviation: 'TB' }, gameTime: '2026-07-11T23:10:00Z', }, ]; const PITCHERS = [ { home: { team: 'Detroit Tigers', pitcher: 'Tarik Skubal', era: 2.41 }, away: { team: 'Tampa Bay Rays', pitcher: 'Ryan Pepiot', era: 3.9 }, }, ]; describe('streakLens — tonight resolution', () => { test('finds the opponent + home/away for a team by mascot token', () => { const t = tonightFor('Tampa Bay Rays', SCHEDULE); expect(t.opponent).toBe('Detroit Tigers'); expect(t.isHome).toBe(false); }); test('resolves the OPPOSING probable SP (not the player-team SP)', () => { const sp = opposingPitcherFor('Tampa Bay Rays', PITCHERS); expect(sp.pitcher).toBe('Tarik Skubal'); expect(sp.era).toBe(2.41); }); }); describe('streakLens — the one-line read', () => { const ROW = { sport: 'mlb', player: 'Brandon Lowe', team: 'Tampa Bay Rays', type: 'hit_streak', category: 'hits', currentStreak: 7, description: '7-game hit streak', opponents: ['Oakland Athletics', 'Los Angeles Angels'], }; test('composes streak + built-vs + tonight + difficulty', () => { const lens = buildLens(ROW, { scheduleGames: SCHEDULE, pitcherGames: PITCHERS }); expect(lens.builtVs).toEqual(['Oakland Athletics', 'Los Angeles Angels']); expect(lens.matchup).toBe('@ Detroit Tigers · SP Tarik Skubal (2.41 ERA)'); expect(lens.difficulty).toBe('step up'); // 2.41 ERA ≤ 3.4 expect(lens.read).toContain('7-game hit streak'); expect(lens.read).toContain('built vs Oakland Athletics'); expect(lens.read).toContain('a step up'); }); test('says LESS when context is missing — never invents', () => { const lens = buildLens({ ...ROW, opponents: [] }, {}); expect(lens.builtVs).toBeNull(); expect(lens.matchup).toBeNull(); expect(lens.difficulty).toBeNull(); expect(lens.read).toBe('7-game hit streak'); }); test('pitcher streaks do not get an opposing-SP difficulty read', () => { const lens = buildLens( { ...ROW, type: 'k_streak', description: '3-game 7+ K streak' }, { scheduleGames: SCHEDULE, pitcherGames: PITCHERS }, ); expect(lens.matchup).toBe('@ Detroit Tigers'); // no SP line for pitchers expect(lens.difficulty).toBeNull(); }); test('ERA difficulty bands', () => { expect(__internals.eraDifficulty(2.9)).toBe('step up'); expect(__internals.eraDifficulty(4.0)).toBe('neutral'); expect(__internals.eraDifficulty(5.2)).toBe('step down'); expect(__internals.eraDifficulty(null)).toBeNull(); }); test('applyLens attaches a lens to every row', () => { const rows = applyLens([ROW, { ...ROW, player: 'X' }], { scheduleGames: SCHEDULE, pitcherGames: PITCHERS }); expect(rows).toHaveLength(2); expect(rows.every((r) => r.lens && r.lens.read)).toBe(true); }); }); describe('computeFormHeat — hot hitters / sluggers (correct rate math)', () => { const day = (n) => new Date(Date.now() - n * 86_400_000).toISOString().slice(0, 10); // 7-day window: 12-for-24 (.500); season .270 → hot hitter. const hotGames = [ { date: day(1), opponent: 'Boston Red Sox', hits: 3, atBats: 4, totalBases: 7 }, { date: day(2), opponent: 'Boston Red Sox', hits: 2, atBats: 4, totalBases: 5 }, { date: day(3), opponent: 'New York Yankees', hits: 2, atBats: 4, totalBases: 2 }, { date: day(4), opponent: 'New York Yankees', hits: 2, atBats: 4, totalBases: 6 }, { date: day(5), opponent: 'New York Yankees', hits: 1, atBats: 4, totalBases: 1 }, { date: day(6), opponent: 'Baltimore Orioles', hits: 2, atBats: 4, totalBases: 4 }, ]; test('flags a hot hitter vs his SEASON average with the honest line', () => { const rows = computeFormHeat( [{ name: 'Hot Guy', team: 'Tampa Bay Rays', games: hotGames, seasonRaw: { avg: '0.270', slg: '0.420' } }], 'mlb', ); const hitter = rows.find((r) => r.type === 'hot_hitter'); expect(hitter).toBeTruthy(); expect(hitter.rate).toBe(0.5); // 12/24, Σ/Σ not mean-of-rates expect(hitter.description).toContain('.500 over the last 7 days'); expect(hitter.description).toContain('season .270'); const slugger = rows.find((r) => r.type === 'hot_slugger'); expect(slugger).toBeTruthy(); // 25 TB / 24 AB ≈ 1.042 SLG vs .420 expect(slugger.opponents).toContain('Boston Red Sox'); }); test('small samples are refused (minAtt), not extrapolated', () => { const rows = computeFormHeat( [{ name: 'Two AB Guy', games: [{ date: day(1), hits: 2, atBats: 2 }], seasonRaw: { avg: '0.250' } }], 'mlb', ); expect(rows).toEqual([]); }); test('no season baseline + no prior games → no claim', () => { const rows = computeFormHeat( [{ name: 'No Baseline', games: hotGames, seasonRaw: null }], 'mlb', ); expect(rows).toEqual([]); // all games in-window, nothing to trend against }); });