'use strict'; /** * THE WINDOW GUARD — a fixed N must never stand in for a season. * * This class has now cost six paths across two sports: * * MLB getStatRows res.last10 -> a 10-game "season rate" * MLB mlbGameLogFeatures res.last10 -> l20_avg was 10 games * NBA espnStatsAdapter rows.slice(0, 20) -> every downstream rate capped at 20 * NBA nbaGameLogFeatures vals.slice(0, 20) -> l20_avg was 20 games * NBA getStatRows (python) getGameLogs(.., 20) -> a 20-game base rate * NBA gameLogFeatures getGameLogs(.., 20) -> same * * Every one looked correct in isolation. `slice(0, 20)` is unremarkable code; * what made it a defect was the QUESTION it was answering — "what is this * player's season rate?" — and no test could see that mismatch, because the * value it produced was always a plausible number. * * So this asserts the property directly on the source: the base-rate and * season-reference paths must not narrow to a fixed window. Basketball is * OFFLINE, which is exactly why it is guarded now — a regression there would * otherwise surface only when it had already served grades. */ const fs = require('fs'); const path = require('path'); const ROOT = path.join(__dirname, '..', '..'); const read = (rel) => fs.readFileSync(path.join(ROOT, rel), 'utf8'); /** Body of a named function, for targeted assertions. */ function fnBody(src, name) { const i = src.indexOf(`function ${name}(`); if (i < 0) return null; let depth = 0; let started = false; for (let j = i; j < src.length; j += 1) { if (src[j] === '{') { depth += 1; started = true; } else if (src[j] === '}') { depth -= 1; if (started && depth === 0) return src.slice(i, j + 1); } } return null; } describe('no fixed window may stand in for a season', () => { const featureCache = read('src/services/intelligence/featureCache.js'); it('the season log depth is a named constant past a full season', () => { // A literal 20 buried in a call is how every instance of this bug looked. expect(featureCache).toMatch(/const SEASON_LOG_DEPTH = (\d+);/); const depth = Number(/const SEASON_LOG_DEPTH = (\d+);/.exec(featureCache)[1]); expect(depth).toBeGreaterThanOrEqual(82); // a basketball season }); it('no game-log request asks for a hardcoded small count', () => { const calls = [...featureCache.matchAll(/getGameLogs\([^)]*\)/g)].map((m) => m[0]); expect(calls.length).toBeGreaterThan(0); for (const c of calls) { const literal = /,\s*(\d+)\s*\)/.exec(c); if (literal) expect(Number(literal[1])).toBeGreaterThanOrEqual(82); } }); it('the BASKETBALL season reference (l20_avg) is not a 20-game slice', () => { const body = fnBody(featureCache, 'nbaGameLogFeatures'); expect(body).not.toBeNull(); // The exact pre-fix line. It read as ordinary code and was the defect. expect(body).not.toMatch(/m20\s*=\s*avg\(vals\.slice\(0,\s*20\)\)/); expect(body).toMatch(/m20\s*=\s*avg\(vals\)/); }); it('the MLB season reference is a real season aggregate, not a slice', () => { const body = fnBody(featureCache, 'mlbGameLogFeatures'); expect(body).not.toBeNull(); expect(body).toMatch(/l20_avg\s*=\s*seasonTotal\s*\/\s*games/); }); it('the MLB base-rate path reads the full log, not last10', () => { const body = fnBody(featureCache, 'getStatRows'); expect(body).toMatch(/res\.fullLog/); }); it('the ESPN adapter does not cap the parsed game log', () => { const adapter = read('src/services/adapters/espnStatsAdapter.js'); const body = fnBody(adapter, 'parseGameLog'); expect(body).not.toBeNull(); // `return rows.slice(0, 20)` capped every downstream basketball rate. expect(body).not.toMatch(/return\s+rows\.slice\(/); }); }); describe('the guard would have caught the real defects', () => { it('flags the exact pre-fix basketball slice', () => { const preFix = 'const m20 = avg(vals.slice(0, 20));'; expect(/m20\s*=\s*avg\(vals\.slice\(0,\s*20\)\)/.test(preFix)).toBe(true); }); it('flags the exact pre-fix ESPN cap', () => { expect(/return\s+rows\.slice\(/.test(' return rows.slice(0, 20);')).toBe(true); }); it('flags a hardcoded small game-log request', () => { const preFix = 'await gameLogs.getGameLogs(playerName, sp, 20);'; const literal = /,\s*(\d+)\s*\)/.exec(/getGameLogs\([^)]*\)/.exec(preFix)[0]); expect(Number(literal[1])).toBeLessThan(82); // i.e. would fail the guard }); });