Files
vyndr/tests/unit/mlbContext.test.js
T

90 lines
3.1 KiB
JavaScript

// 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();
});
});