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

104 lines
3.8 KiB
JavaScript

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');
});
});