// Unit: ESPN summary enrichment (Session 30). const mockAxiosGet = jest.fn(); jest.mock('axios', () => ({ get: (...a) => mockAxiosGet(...a) })); const mockStore = {}; jest.mock('../../src/utils/redis', () => ({ cacheGet: async (k) => (k in mockStore ? mockStore[k] : null), cacheSet: async (k, v) => { mockStore[k] = v; return true; }, })); const { getGameSummary, __internals } = require('../../src/services/scheduleService'); beforeEach(() => { mockAxiosGet.mockReset(); for (const k of Object.keys(mockStore)) delete mockStore[k]; }); describe('getGameSummary', () => { test('extracts enriched fields for a valid sport + eventId', async () => { mockAxiosGet.mockResolvedValue({ data: { injuries: [{ team: 'CIN', injuries: [{ athlete: { displayName: 'Player X' }, status: 'OUT' }] }], odds: [{ provider: { name: 'ESPN BET' }, spread: -1.5, overUnder: 9.5 }], againstTheSpread: [{ team: { abbreviation: 'CIN' }, records: [] }], leaders: [{ name: 'hits' }], boxscore: { teams: [] }, }, }); const out = await getGameSummary('mlb', '401815722'); expect(out.injuries).toHaveLength(1); expect(out.odds[0].overUnder).toBe(9.5); expect(out.ats).not.toBeNull(); expect(out.boxscore).not.toBeNull(); const url = mockAxiosGet.mock.calls[0][0]; expect(url).toBe('https://site.api.espn.com/apis/site/v2/sports/baseball/mlb/summary?event=401815722'); }); test('missing sections → empty defaults (no crash)', async () => { mockAxiosGet.mockResolvedValue({ data: {} }); const out = await getGameSummary('nba', '999'); expect(out).toEqual({ injuries: [], odds: [], ats: null, leaders: [], boxscore: null }); }); test('invalid sport → empty defaults without axios', async () => { const out = await getGameSummary('cricket', '1'); expect(out.injuries).toEqual([]); expect(mockAxiosGet).not.toHaveBeenCalled(); }); test('missing eventId → empty defaults without axios', async () => { await getGameSummary('mlb', null); expect(mockAxiosGet).not.toHaveBeenCalled(); }); test('network error → empty defaults, not a throw', async () => { mockAxiosGet.mockRejectedValue(new Error('espn down')); const out = await getGameSummary('nba', '1'); expect(out.injuries).toEqual([]); }); test('sport path mapping is correct', () => { expect(__internals.ESPN_SPORT_PATHS.nba).toBe('basketball/nba'); expect(__internals.ESPN_SPORT_PATHS.mlb).toBe('baseball/mlb'); expect(__internals.ESPN_SPORT_PATHS.wnba).toBe('basketball/wnba'); expect(__internals.ESPN_SPORT_PATHS.nfl).toBe('football/nfl'); expect(__internals.ESPN_SPORT_PATHS.nhl).toBe('hockey/nhl'); }); test('caches — second call does not re-fetch', async () => { mockAxiosGet.mockResolvedValue({ data: {} }); await getGameSummary('mlb', '5'); await getGameSummary('mlb', '5'); expect(mockAxiosGet).toHaveBeenCalledTimes(1); }); });