Session 9: api-football + FootApi + Tank01 adapters, grace period middleware, cookie consent, /pricing page, OOM fix documented (1240 tests)
This commit is contained in:
@@ -0,0 +1,143 @@
|
||||
const mockAxiosGet = jest.fn();
|
||||
jest.mock('axios', () => ({ get: (...args) => mockAxiosGet(...args) }));
|
||||
|
||||
const mockCacheStore = new Map();
|
||||
const mockCacheTtls = new Map();
|
||||
jest.mock('../../src/utils/redis', () => ({
|
||||
cacheGet: async (k) => (mockCacheStore.has(k) ? mockCacheStore.get(k) : null),
|
||||
cacheSet: async (k, v, ttl) => { mockCacheStore.set(k, v); mockCacheTtls.set(k, ttl); return true; },
|
||||
cacheDel: async (k) => { mockCacheStore.delete(k); return true; },
|
||||
isDegraded: () => false,
|
||||
}));
|
||||
|
||||
const adapter = require('../../src/services/adapters/tank01NbaAdapter');
|
||||
|
||||
beforeEach(() => {
|
||||
mockAxiosGet.mockReset();
|
||||
mockCacheStore.clear();
|
||||
mockCacheTtls.clear();
|
||||
});
|
||||
|
||||
describe('tank01NbaAdapter', () => {
|
||||
describe('graceful degradation (no RAPID_API_KEY)', () => {
|
||||
const original = process.env.RAPID_API_KEY;
|
||||
beforeAll(() => { delete process.env.RAPID_API_KEY; });
|
||||
afterAll(() => { if (original !== undefined) process.env.RAPID_API_KEY = original; });
|
||||
|
||||
test('hasApiKey false', () => {
|
||||
expect(adapter.hasApiKey()).toBe(false);
|
||||
});
|
||||
|
||||
test('all endpoints return null without touching axios', async () => {
|
||||
expect(await adapter.getNBABoxScore('20260611_LAL_BOS')).toBeNull();
|
||||
expect(await adapter.getNBAGamesForDate('20260611')).toBeNull();
|
||||
expect(await adapter.getNBABettingOdds('20260611')).toBeNull();
|
||||
expect(mockAxiosGet).not.toHaveBeenCalled();
|
||||
});
|
||||
});
|
||||
|
||||
describe('with key configured', () => {
|
||||
beforeAll(() => { process.env.RAPID_API_KEY = 'test-rapid-key'; });
|
||||
|
||||
test('RapidAPI auth headers wired correctly', async () => {
|
||||
mockAxiosGet.mockResolvedValueOnce({ data: { body: [] } });
|
||||
await adapter.getNBAGamesForDate('20260611');
|
||||
const [url, opts] = mockAxiosGet.mock.calls[0];
|
||||
expect(url).toMatch(/^https:\/\/tank01-fantasy-stats\.p\.rapidapi\.com/);
|
||||
expect(opts.headers['x-rapidapi-key']).toBe('test-rapid-key');
|
||||
expect(opts.headers['x-rapidapi-host']).toBe('tank01-fantasy-stats.p.rapidapi.com');
|
||||
});
|
||||
|
||||
test('TANK01_NBA_HOST override is honored', async () => {
|
||||
const original = process.env.TANK01_NBA_HOST;
|
||||
process.env.TANK01_NBA_HOST = 'alt-tank01.rapidapi.com';
|
||||
mockAxiosGet.mockResolvedValueOnce({ data: { body: [] } });
|
||||
await adapter.getNBAGamesForDate('20260611');
|
||||
const [url, opts] = mockAxiosGet.mock.calls[0];
|
||||
expect(url).toMatch(/^https:\/\/alt-tank01\.rapidapi\.com/);
|
||||
expect(opts.headers['x-rapidapi-host']).toBe('alt-tank01.rapidapi.com');
|
||||
if (original !== undefined) process.env.TANK01_NBA_HOST = original;
|
||||
else delete process.env.TANK01_NBA_HOST;
|
||||
});
|
||||
|
||||
test('getNBABoxScore projects playerStats map into a flat list', async () => {
|
||||
mockAxiosGet.mockResolvedValueOnce({
|
||||
data: {
|
||||
body: {
|
||||
gameStatus: 'InProgress',
|
||||
playerStats: {
|
||||
'NBA-123': { longName: 'Jaylen Brown', teamAbv: 'BOS', mins: '34', pts: 27, reb: 5, ast: 4 },
|
||||
'NBA-456': { longName: 'Jayson Tatum', teamAbv: 'BOS', mins: '36', pts: 31, reb: 8, ast: 6, tptfgm: 5 },
|
||||
},
|
||||
},
|
||||
},
|
||||
});
|
||||
const stats = await adapter.getNBABoxScore('20260611_LAL_BOS');
|
||||
expect(stats).toHaveLength(2);
|
||||
const tatum = stats.find((p) => p.name === 'Jayson Tatum');
|
||||
expect(tatum.team).toBe('BOS');
|
||||
expect(tatum.pts).toBe(31);
|
||||
expect(tatum.threes).toBe(5);
|
||||
expect(tatum._final).toBe(false);
|
||||
});
|
||||
|
||||
test('Final game upgrades the cache TTL to 24h', async () => {
|
||||
mockAxiosGet.mockResolvedValueOnce({
|
||||
data: { body: { gameStatus: 'Final', playerStats: {} } },
|
||||
});
|
||||
await adapter.getNBABoxScore('GAME-1');
|
||||
const ttl = mockCacheTtls.get('tank01:nba:boxscore:GAME-1');
|
||||
expect(ttl).toBe(adapter.__internals.TTL.boxScoreFinal);
|
||||
});
|
||||
|
||||
test('In-progress game stays on 5-min TTL', async () => {
|
||||
mockAxiosGet.mockResolvedValueOnce({
|
||||
data: { body: { gameStatus: 'InProgress', playerStats: {} } },
|
||||
});
|
||||
await adapter.getNBABoxScore('GAME-2');
|
||||
const ttl = mockCacheTtls.get('tank01:nba:boxscore:GAME-2');
|
||||
expect(ttl).toBe(adapter.__internals.TTL.boxScoreLive);
|
||||
});
|
||||
|
||||
test('getNBAGamesForDate strips dashes from ISO dates', async () => {
|
||||
mockAxiosGet.mockResolvedValueOnce({ data: { body: [] } });
|
||||
await adapter.getNBAGamesForDate('2026-06-11');
|
||||
const [url] = mockAxiosGet.mock.calls[0];
|
||||
expect(url).toMatch(/gameDate=20260611/);
|
||||
});
|
||||
|
||||
test('getNBAGamesForDate projects to stable shape', async () => {
|
||||
mockAxiosGet.mockResolvedValueOnce({
|
||||
data: { body: [
|
||||
{ gameID: '20260611_BOS_LAL', home: 'LAL', away: 'BOS', gameTime: '7:30p ET', gameStatus: 'Final', homePts: 110, awayPts: 105 },
|
||||
] },
|
||||
});
|
||||
const games = await adapter.getNBAGamesForDate('20260611');
|
||||
expect(games[0]).toMatchObject({
|
||||
gameId: '20260611_BOS_LAL', homeTeam: 'LAL', awayTeam: 'BOS', homeScore: 110, awayScore: 105, gameStatus: 'Final',
|
||||
});
|
||||
});
|
||||
|
||||
test('null IDs return null without touching axios', async () => {
|
||||
expect(await adapter.getNBABoxScore(null)).toBeNull();
|
||||
expect(await adapter.getNBAGamesForDate(null)).toBeNull();
|
||||
expect(await adapter.getNBABettingOdds(null)).toBeNull();
|
||||
expect(mockAxiosGet).not.toHaveBeenCalled();
|
||||
});
|
||||
|
||||
test('axios error → null + stale fallback', async () => {
|
||||
mockCacheStore.set('tank01:nba:games:20260611:stale', { body: [{ gameID: 'STALE' }] });
|
||||
mockAxiosGet.mockRejectedValueOnce(new Error('upstream 503'));
|
||||
const games = await adapter.getNBAGamesForDate('20260611');
|
||||
expect(games).toHaveLength(1);
|
||||
expect(games[0].gameId).toBe('STALE');
|
||||
});
|
||||
|
||||
test('cache hit on repeat call (axios not re-invoked)', async () => {
|
||||
mockAxiosGet.mockResolvedValueOnce({ data: { body: [] } });
|
||||
await adapter.getNBAGamesForDate('20260611');
|
||||
await adapter.getNBAGamesForDate('20260611');
|
||||
expect(mockAxiosGet).toHaveBeenCalledTimes(1);
|
||||
});
|
||||
});
|
||||
});
|
||||
Reference in New Issue
Block a user