Session 32: Grades pipeline + NFL/NHL wiring + rate limiting + audit cleanup (1718 tests)

- gradeSlateService writes grades:{sport} cache (closes content pipeline →
  dataLevel full); fire-and-forget from oddsService.recordDownstream, gated
  by shouldGradeSlate (off in test, GRADE_SLATE_ON_FETCH override)
- NFL/NHL wired: oddsService SPORT_KEYS/SPORT_MARKETS (correct the-odds-api
  keys americanfootball_nfl/icehockey_nhl), proplineAdapter MARKETS, NHL
  MARKET_MAP keys to avoid silent-zero
- rate limiting mounted on 8 public cached routers (odds/parlay 30/min,
  rest 60/min)
- jsonlLogger writes to temp under test (no more dirtied tracked artifact);
  5MB pipeline test given 20s timeout

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
Kev
2026-06-15 18:21:32 -04:00
parent 2ba3958c7a
commit f0c8b4f29b
20 changed files with 667 additions and 9 deletions
+89
View File
@@ -0,0 +1,89 @@
// Unit: NFL + NHL sport-key wiring (Session 32). Closes the silent-zero
// trap before NFL season — sport keys, per-sport markets, PropLine markets,
// and end-to-end MARKET_MAP normalization.
const oddsService = require('../../src/services/oddsService');
const propline = require('../../src/services/adapters/proplineAdapter');
const { normalizeProps } = require('../../src/utils/oddsNormalizer');
describe('oddsService sport keys', () => {
test('nfl/nhl map to the correct the-odds-api keys', () => {
// the-odds-api uses full-name prefixes (basketball_nba, baseball_mlb);
// NFL/NHL follow the same convention — NOT football_nfl/hockey_nhl.
expect(oddsService.SPORT_KEYS.nfl).toBe('americanfootball_nfl');
expect(oddsService.SPORT_KEYS.nhl).toBe('icehockey_nhl');
});
test('getMarketsForSport(nfl) requests NFL markets + spreads (not the NBA fallback)', () => {
const markets = oddsService.getMarketsForSport('nfl');
expect(markets).toContain('player_pass_yds');
expect(markets).toContain('player_anytime_td');
expect(markets).toContain('spreads');
expect(markets).not.toContain('player_points'); // would mean nba fallback
});
test('getMarketsForSport(nhl) requests NHL markets', () => {
const markets = oddsService.getMarketsForSport('nhl');
expect(markets).toContain('player_shots_on_goal');
expect(markets).toContain('goalie_saves');
expect(markets).not.toContain('player_points');
});
});
describe('proplineAdapter NFL/NHL markets', () => {
test('nfl/nhl markets are populated and sport keys resolve', () => {
const { MARKETS, SPORT_KEYS } = propline.__internals;
expect(MARKETS.nfl.length).toBeGreaterThan(0);
expect(MARKETS.nhl.length).toBeGreaterThan(0);
expect(MARKETS.nfl).toContain('player_pass_yds');
expect(MARKETS.nhl).toContain('player_shots_on_goal');
expect(SPORT_KEYS.nfl).toBe('football_nfl');
expect(SPORT_KEYS.nhl).toBe('hockey_nhl');
});
});
describe('MARKET_MAP end-to-end normalization', () => {
function eventWith(marketKey, player, point) {
return [{
home_team: 'Kansas City Chiefs',
away_team: 'Buffalo Bills',
commence_time: '2026-09-10T00:00:00Z',
bookmakers: [{
key: 'draftkings',
markets: [{
key: marketKey,
last_update: '2026-09-09T20:00:00Z',
outcomes: [
{ description: player, point, name: 'Over', price: -110 },
{ description: player, point, name: 'Under', price: -110 },
],
}],
}],
}];
}
test('NFL passing-yards prop normalizes to passing_yards', () => {
const props = normalizeProps(eventWith('player_pass_yds', 'Patrick Mahomes', 274.5));
expect(props).toHaveLength(1);
expect(props[0].stat_type).toBe('passing_yards');
expect(props[0].player).toBe('Patrick Mahomes');
expect(props[0].line).toBe(274.5);
});
test('NFL anytime-TD prop normalizes (does not drop to zero)', () => {
const props = normalizeProps(eventWith('player_anytime_td', 'Travis Kelce', 0.5));
expect(props).toHaveLength(1);
expect(props[0].stat_type).toBe('anytime_td');
});
test('NHL shots-on-goal + goalie-saves normalize', () => {
expect(normalizeProps(eventWith('player_shots_on_goal', 'Connor McDavid', 3.5))[0].stat_type).toBe('shots_on_goal');
expect(normalizeProps(eventWith('goalie_saves', 'Igor Shesterkin', 28.5))[0].stat_type).toBe('saves');
});
test('off-season / empty response normalizes gracefully (no crash)', () => {
expect(normalizeProps([])).toEqual([]);
// Unknown market key is skipped, not crashed on.
expect(normalizeProps(eventWith('player_unknown_stat', 'Nobody', 1.5))).toEqual([]);
});
});