// MLB context helpers (Session 15) — pitcher handedness + lineup PA. const { platoonAdvantage, projectedPA, __internals } = require('../../src/services/intelligence/mlbContext'); describe('platoonAdvantage', () => { test('LHP vs RHB → batter has advantage', () => { expect(platoonAdvantage('L', 'R')).toBe(true); }); test('RHP vs LHB → batter has advantage', () => { expect(platoonAdvantage('R', 'L')).toBe(true); }); test('LHP vs LHB → pitcher has advantage', () => { expect(platoonAdvantage('L', 'L')).toBe(false); }); test('RHP vs RHB → pitcher has advantage', () => { expect(platoonAdvantage('R', 'R')).toBe(false); }); test('switch hitter ALWAYS has the edge (vs LHP)', () => { expect(platoonAdvantage('L', 'S')).toBe(true); }); test('switch hitter ALWAYS has the edge (vs RHP)', () => { expect(platoonAdvantage('R', 'S')).toBe(true); }); test('null pitcher → null', () => { expect(platoonAdvantage(null, 'R')).toBeNull(); }); test('null batter → null', () => { expect(platoonAdvantage('L', null)).toBeNull(); }); test('invalid input → null (does not throw)', () => { expect(platoonAdvantage('Z', 'R')).toBeNull(); expect(platoonAdvantage('L', 'X')).toBeNull(); }); test('case-insensitive', () => { expect(platoonAdvantage('l', 'r')).toBe(true); expect(platoonAdvantage('Right', 'Left')).toBe(true); }); test('whitespace tolerant', () => { expect(platoonAdvantage(' L ', ' R ')).toBe(true); }); }); describe('projectedPA', () => { test('leadoff (1) sees the most PAs (~4.7)', () => { expect(projectedPA(1)).toBeGreaterThan(4.6); expect(projectedPA(1)).toBeLessThan(4.8); }); test('9-hole sees the fewest PAs (~3.7)', () => { expect(projectedPA(9)).toBeGreaterThan(3.6); expect(projectedPA(9)).toBeLessThan(3.8); }); test('PA decreases monotonically by slot', () => { let prev = Infinity; for (let i = 1; i <= 9; i += 1) { const pa = projectedPA(i); expect(pa).toBeLessThan(prev); prev = pa; } }); test('numeric string input is accepted', () => { expect(projectedPA('3')).toBeCloseTo(4.43, 2); }); test('out-of-range returns null', () => { expect(projectedPA(0)).toBeNull(); expect(projectedPA(10)).toBeNull(); expect(projectedPA(-1)).toBeNull(); }); test('null / undefined / non-numeric → null', () => { expect(projectedPA(null)).toBeNull(); expect(projectedPA(undefined)).toBeNull(); expect(projectedPA('NaN')).toBeNull(); }); test('non-integer (e.g. 5.5) → null', () => { expect(projectedPA(5.5)).toBeNull(); }); }); describe('normalizeHand', () => { test('accepts L, R, S in upper/lower case', () => { expect(__internals.normalizeHand('l')).toBe('L'); expect(__internals.normalizeHand('R')).toBe('R'); expect(__internals.normalizeHand('Switch')).toBe('S'); }); test('rejects unknown letters', () => { expect(__internals.normalizeHand('X')).toBeNull(); expect(__internals.normalizeHand('')).toBeNull(); expect(__internals.normalizeHand(null)).toBeNull(); }); });