32069863dc
- Backend: whitelist MLB stat_types in analyze.js + scan.js gates (mirrors python validation.py); fixes MLB scans 400ing. - Routes: /settings -> /profile, /report -> /blog redirect pages. - Profile: read tier from useAuth().tier (nav's source) to kill the Free-vs-DESK mismatch. - Fonts: self-host Inter/JetBrains Mono/IBM Plex Mono via next/font, drop the 503ing fonts.googleapis.com <link>; rewire literal font-family refs to vars. - Kept /settings/security (real MFA page) intact — NOT clobbered to a redirect. - +33 tests (1907 -> 1940), 149 suites; web build clean (exit 0). Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
71 lines
2.7 KiB
JavaScript
71 lines
2.7 KiB
JavaScript
// Session 41 — MLB stat_type acceptance. The Scan frontend sends MLB IDs
|
|
// (hits, strikeouts, total_bases, rbi, home_runs, earned_runs, hits_allowed,
|
|
// innings_pitched) but the /api/analyze validation gate only whitelisted the
|
|
// NBA/soccer set, so every MLB scan 400'd. This file lives apart from
|
|
// analyze.test.js because that suite sits exactly at the 10/min IP rate-limit
|
|
// boundary; a separate file gets a fresh limiter (jest isolates module state
|
|
// per file) so we can exercise all eight MLB stats under the cap.
|
|
|
|
const request = require('supertest');
|
|
|
|
const mockRedis = { get: jest.fn(), set: jest.fn(), hset: jest.fn(), hgetall: jest.fn(), expire: jest.fn() };
|
|
jest.mock('../../src/utils/redis', () => ({
|
|
getRedisClient: () => mockRedis,
|
|
cacheGet: async () => null,
|
|
cacheSet: async () => true,
|
|
cacheDel: async () => true,
|
|
isDegraded: () => false,
|
|
}));
|
|
|
|
const mockAnalyzeViaEngine1 = jest.fn();
|
|
jest.mock('../../src/services/intelligence/analyzeViaEngine1', () => ({
|
|
analyzeViaEngine1: (...args) => mockAnalyzeViaEngine1(...args),
|
|
}));
|
|
|
|
jest.mock('../../src/utils/tierGating', () => ({ applyTierGating: (result) => result }));
|
|
|
|
const { __internals: scanLimitInternals } = require('../../src/middleware/scanLimit');
|
|
const app = require('../../src/app');
|
|
|
|
const MLB_STATS = ['hits', 'strikeouts', 'total_bases', 'rbi', 'home_runs', 'earned_runs', 'hits_allowed', 'innings_pitched'];
|
|
|
|
beforeEach(() => {
|
|
jest.clearAllMocks();
|
|
mockRedis.get.mockResolvedValue(null);
|
|
mockRedis.set.mockResolvedValue('OK');
|
|
mockAnalyzeViaEngine1.mockReset();
|
|
if (scanLimitInternals && scanLimitInternals.resetForTests) scanLimitInternals.resetForTests();
|
|
});
|
|
|
|
describe('POST /api/analyze/prop — MLB stat_types', () => {
|
|
it.each(MLB_STATS)('accepts %s (no longer 400s at the validation gate)', async (stat) => {
|
|
mockAnalyzeViaEngine1.mockResolvedValueOnce({
|
|
player: 'Aaron Judge',
|
|
stat_type: stat,
|
|
line: 1.5,
|
|
direction: 'over',
|
|
grade: 'B',
|
|
confidence: 70,
|
|
kill_conditions_triggered: [],
|
|
reasoning: { summary: 'ok', steps: [] },
|
|
});
|
|
|
|
const res = await request(app)
|
|
.post('/api/analyze/prop')
|
|
.send({ player: 'Aaron Judge', sport: 'mlb', stat_type: stat, line: 1.5, direction: 'over' });
|
|
|
|
expect(res.status).toBe(200);
|
|
expect(res.body.error).toBeUndefined();
|
|
expect(mockAnalyzeViaEngine1).toHaveBeenCalled();
|
|
});
|
|
|
|
it('still rejects a genuinely invalid stat_type', async () => {
|
|
const res = await request(app)
|
|
.post('/api/analyze/prop')
|
|
.send({ player: 'Aaron Judge', sport: 'mlb', stat_type: 'vibes', line: 1.5, direction: 'over' });
|
|
|
|
expect(res.status).toBe(400);
|
|
expect(res.body.error).toContain('Invalid stat_type');
|
|
});
|
|
});
|