// Unit: rosterLogs loader (Session 23). Redis-only; no network. const store = {}; const mockScan = jest.fn(); jest.mock('../../src/utils/redis', () => ({ cacheGet: jest.fn(async (k) => (k in store ? store[k] : null)), getRedisClient: () => ({ scan: mockScan }), isDegraded: () => false, })); const { loadRosterLogs, __internals } = require('../../src/services/rosterLogs'); beforeEach(() => { for (const k of Object.keys(store)) delete store[k]; mockScan.mockReset(); }); describe('rosterLogs', () => { test('fast path: returns a prefetched roster blob without scanning', async () => { store['rosterlogs:nba'] = [{ name: 'Wemby', games: [{ points: 30 }] }]; const roster = await loadRosterLogs('nba'); expect(roster).toHaveLength(1); expect(mockScan).not.toHaveBeenCalled(); }); test('scan path: assembles roster from per-player gamelogs keys', async () => { store['gamelogs:nba:Wembanyama:20'] = [{ points: 30, team: 'SA', playerId: 'W1' }]; store['gamelogs:nba:Brunson:20'] = [{ points: 24, team: 'NYK' }]; mockScan .mockResolvedValueOnce(['0', ['gamelogs:nba:Wembanyama:20', 'gamelogs:nba:Brunson:20']]); const roster = await loadRosterLogs('nba'); expect(roster.map((p) => p.name).sort()).toEqual(['Brunson', 'Wembanyama']); const wemby = roster.find((p) => p.name === 'Wembanyama'); expect(wemby.team).toBe('SA'); expect(wemby.playerId).toBe('W1'); }); test('dedupes by highest game-count variant', async () => { store['gamelogs:nba:Star:10'] = [{ points: 1 }, { points: 2 }]; store['gamelogs:nba:Star:20'] = [{ points: 1 }, { points: 2 }, { points: 3 }]; mockScan.mockResolvedValueOnce(['0', ['gamelogs:nba:Star:10', 'gamelogs:nba:Star:20']]); const roster = await loadRosterLogs('nba'); expect(roster).toHaveLength(1); expect(roster[0].games).toHaveLength(3); // the :20 variant wins }); test('empty cache → empty roster, never throws', async () => { mockScan.mockResolvedValueOnce(['0', []]); expect(await loadRosterLogs('nba')).toEqual([]); }); test('scan failure → returns what it had, no throw', async () => { mockScan.mockRejectedValueOnce(new Error('redis down')); expect(await loadRosterLogs('nba')).toEqual([]); }); test('playerFromKey parses the player name out of the key', () => { expect(__internals.playerFromKey('gamelogs:nba:LeBron James:20', 'nba')).toBe('LeBron James'); }); });