Sessions 29-30: Content templates + PropLine 3-key adapter + MLB Stats API + ESPN summary (1694 tests)
This commit is contained in:
@@ -0,0 +1,76 @@
|
||||
// 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);
|
||||
});
|
||||
});
|
||||
Reference in New Issue
Block a user