const mockAxiosGet = jest.fn(); jest.mock('axios', () => ({ get: (...args) => mockAxiosGet(...args) })); const mockCache = { current: new Map() }; jest.mock('../../src/utils/redis', () => ({ cacheGet: async (k) => mockCache.current.get(k) ?? null, cacheSet: async (k, v) => { mockCache.current.set(k, v); return true; }, cacheDel: async (k) => { mockCache.current.delete(k); return true; }, })); jest.mock('../../src/utils/rateLimiter', () => ({ createLimiter: () => ({ waitForToken: async () => true, snapshot: () => ({}) }), createCircuitBreaker: () => ({ call: async (fn) => fn(), snapshot: () => ({}) }), })); const injuries = require('../../src/services/intelligence/injuryParser'); beforeEach(() => { mockAxiosGet.mockReset(); mockCache.current.clear(); }); describe('injuryParser.getTeamInjuries', () => { test('normalizes ESPN injury payload', async () => { mockAxiosGet.mockResolvedValue({ status: 200, data: { injuries: [ { athlete: { id: 3934672, displayName: 'Jalen Brunson' }, status: 'Doubtful', details: { detail: 'Right ankle' } }, { athlete: { id: 9999, displayName: 'OG Anunoby' }, status: 'Out' }, ], }, }); const out = await injuries.getTeamInjuries('nba', 18); expect(out).toHaveLength(2); expect(out[0]).toMatchObject({ playerId: '3934672', playerName: 'Jalen Brunson', status: 'DOUBTFUL', detail: 'Right ankle' }); expect(out[1].status).toBe('OUT'); }); test('404 is treated as no current injuries (empty list)', async () => { mockAxiosGet.mockResolvedValue({ status: 404, data: {} }); const out = await injuries.getTeamInjuries('nba', 99); expect(out).toEqual([]); }); test('cache hit avoids second request', async () => { mockAxiosGet.mockResolvedValue({ status: 200, data: { injuries: [] } }); await injuries.getTeamInjuries('nba', 5); await injuries.getTeamInjuries('nba', 5); expect(mockAxiosGet).toHaveBeenCalledTimes(1); }); test('returns [] on unsupported sport (no path mapping)', async () => { const out = await injuries.getTeamInjuries('curling', 1); expect(out).toEqual([]); }); }); describe('injuryParser.isPlayerOut + getMissingStarters', () => { beforeEach(() => { mockAxiosGet.mockResolvedValue({ status: 200, data: { injuries: [ { athlete: { id: 1, displayName: 'A' }, status: 'OUT' }, { athlete: { id: 2, displayName: 'B' }, status: 'PROBABLE' }, { athlete: { id: 3, displayName: 'C' }, status: 'DOUBTFUL' }, ], }, }); }); test('isPlayerOut treats OUT and DOUBTFUL as out', async () => { expect(await injuries.isPlayerOut('nba', 1, '1')).toBe(true); expect(await injuries.isPlayerOut('nba', 1, '3')).toBe(true); expect(await injuries.isPlayerOut('nba', 1, '2')).toBe(false); expect(await injuries.isPlayerOut('nba', 1, '99')).toBe(false); }); test('getMissingStarters intersects starter set with injured list', async () => { const missing = await injuries.getMissingStarters('nba', 1, ['1', '2', '3', '4']); expect(missing.map((m) => m.playerId).sort()).toEqual(['1', '3']); }); }); describe('injuryParser.extractGameInjuries (from summary JSON)', () => { test('reads injuries from competitions[0].competitors[]', async () => { const summary = { header: { competitions: [{ competitors: [ { homeAway: 'home', injuries: [{ athlete: { id: 1, displayName: 'H1' }, status: 'Out' }] }, { homeAway: 'away', injuries: [{ athlete: { id: 2, displayName: 'A1' }, status: 'Probable' }] }, ], }], }, }; const out = await injuries.getGameInjuries('nba', 'g1', summary); expect(out.home).toHaveLength(1); expect(out.home[0].status).toBe('OUT'); expect(out.away[0].status).toBe('PROBABLE'); }); });