process.env.PROPODDS_KEY = 'test-key'; process.env.PROPODDS_BASE_URL = 'https://api.propodds.test/v1'; 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; }, })); const adapter = require('../../src/services/adapters/propOddsAdapter'); beforeEach(() => { mockAxiosGet.mockReset(); mockCache.current.clear(); }); describe('propOddsAdapter.configured', () => { test('reflects PROPODDS_KEY presence', () => { expect(adapter.configured()).toBe(true); delete process.env.PROPODDS_KEY; expect(adapter.configured()).toBe(false); process.env.PROPODDS_KEY = 'test-key'; }); }); describe('propOddsAdapter.getPlayerProps', () => { test('normalizes player props with devig', async () => { mockAxiosGet.mockResolvedValue({ status: 200, data: { props: [ { book: 'dk', player: 'LeBron', market: 'points', line: 25.5, over_odds: -110, under_odds: -110 }, ], }, }); const result = await adapter.getPlayerProps('nba', 'g1', 'LeBron', 'points'); expect(result[0]).toMatchObject({ book: 'dk', player: 'LeBron', statType: 'points', line: 25.5 }); expect(result[0].fairOver).toBeCloseTo(0.5, 5); }); test('api_key embeds in query, never logged', async () => { mockAxiosGet.mockResolvedValue({ status: 200, data: { props: [] } }); await adapter.getPlayerProps('nba', 'gx', null, null); expect(mockAxiosGet).toHaveBeenCalledWith( expect.any(String), expect.objectContaining({ params: expect.objectContaining({ api_key: 'test-key' }) }), ); }); test('429 falls back to stale cache', async () => { mockCache.current.set('propodds:nba:g-stale:all:all', { props: [{ book: 'dk', player: 'Old', market: 'points', line: 20, over_odds: -110, under_odds: -110 }], stale: true, }); mockAxiosGet.mockResolvedValue({ status: 429, data: {} }); const result = await adapter.getPlayerProps('nba', 'g-stale', null, null); expect(result.stale).toBe(true); }); test('returns [] when unconfigured', async () => { delete process.env.PROPODDS_KEY; expect(await adapter.getPlayerProps('nba', 'g1', null, null)).toEqual([]); process.env.PROPODDS_KEY = 'test-key'; }); });