Sessions 7e-7g: Grading path unified - adapter, computeFeatures, analyzeViaEngine1, all routes migrated, dead code removed
This commit is contained in:
@@ -1,10 +1,13 @@
|
||||
// PERF-1 (Session 7d): /api/analyze caches via Redis. Second identical
|
||||
// call must hit cache (returns _cache:'HIT', does not re-invoke
|
||||
// analyzeProp). Different keys still miss.
|
||||
// call must hit cache (returns _cache:'HIT', does not re-invoke the
|
||||
// analyzer). Different keys still miss.
|
||||
// ARCH-1 (Session 7g): the analyzer target moved from propAnalyzer to
|
||||
// the unified analyzeViaEngine1. Mock target updated to follow; the
|
||||
// caching behavior under test is unchanged.
|
||||
|
||||
const mockAnalyze = jest.fn();
|
||||
jest.mock('../../src/services/propAnalyzer', () => ({
|
||||
analyzeProp: (...args) => mockAnalyze(...args),
|
||||
jest.mock('../../src/services/intelligence/analyzeViaEngine1', () => ({
|
||||
analyzeViaEngine1: (...args) => mockAnalyze(...args),
|
||||
}));
|
||||
|
||||
const mockStore = new Map();
|
||||
|
||||
@@ -1,111 +0,0 @@
|
||||
const { computeGrade } = require('../../src/services/grader');
|
||||
|
||||
function makeStepResults(overrides = {}) {
|
||||
return {
|
||||
seasonDelta: 0,
|
||||
recentDelta: 0,
|
||||
situationalDelta: 0,
|
||||
lineEdge: 0,
|
||||
killConditions: [],
|
||||
gamesPlayed: 65,
|
||||
seasonAndRecentAgree: null,
|
||||
...overrides,
|
||||
};
|
||||
}
|
||||
|
||||
describe('grader', () => {
|
||||
describe('grade assignment', () => {
|
||||
test('composite >= 3.0 returns grade A with confidence 80-95', () => {
|
||||
// composite = (4*1 + 4*1.5 + 4*1.2 + 4*0.8) / 4.5 = 18/4.5 = 4.0
|
||||
const result = computeGrade(makeStepResults({
|
||||
seasonDelta: 4, recentDelta: 4, situationalDelta: 4, lineEdge: 4,
|
||||
}));
|
||||
expect(result.grade).toBe('A');
|
||||
expect(result.confidence).toBeGreaterThanOrEqual(80);
|
||||
expect(result.confidence).toBeLessThanOrEqual(95);
|
||||
});
|
||||
|
||||
test('composite 1.5-2.99 returns grade B with confidence 65-79', () => {
|
||||
// composite = (2*1 + 2*1.5 + 2*1.2 + 2*0.8) / 4.5 = 9/4.5 = 2.0
|
||||
const result = computeGrade(makeStepResults({
|
||||
seasonDelta: 2, recentDelta: 2, situationalDelta: 2, lineEdge: 2,
|
||||
}));
|
||||
expect(result.grade).toBe('B');
|
||||
expect(result.confidence).toBeGreaterThanOrEqual(65);
|
||||
expect(result.confidence).toBeLessThanOrEqual(79);
|
||||
});
|
||||
|
||||
test('composite 0.5-1.49 returns grade C with confidence 50-64', () => {
|
||||
// composite = (1*1 + 1*1.5 + 1*1.2 + 0*0.8) / 4.5 = 3.7/4.5 ≈ 0.82
|
||||
const result = computeGrade(makeStepResults({
|
||||
seasonDelta: 1, recentDelta: 1, situationalDelta: 1, lineEdge: 0,
|
||||
}));
|
||||
expect(result.grade).toBe('C');
|
||||
expect(result.confidence).toBeGreaterThanOrEqual(50);
|
||||
expect(result.confidence).toBeLessThanOrEqual(64);
|
||||
});
|
||||
|
||||
test('composite < 0.5 returns grade D with confidence 30-49', () => {
|
||||
const result = computeGrade(makeStepResults({
|
||||
seasonDelta: -1, recentDelta: -1, situationalDelta: -1, lineEdge: 0,
|
||||
}));
|
||||
expect(result.grade).toBe('D');
|
||||
expect(result.confidence).toBeGreaterThanOrEqual(30);
|
||||
expect(result.confidence).toBeLessThanOrEqual(49);
|
||||
});
|
||||
});
|
||||
|
||||
describe('kill condition penalty', () => {
|
||||
test('caps grade at C and reduces confidence by 15', () => {
|
||||
const result = computeGrade(makeStepResults({
|
||||
seasonDelta: 4, recentDelta: 4, situationalDelta: 4, lineEdge: 4,
|
||||
killConditions: [{ code: 'blowout_risk' }],
|
||||
}));
|
||||
expect(result.grade).toBe('C');
|
||||
// Original would be A (80+), minus 15 = 65+
|
||||
expect(result.confidence).toBeLessThan(85);
|
||||
});
|
||||
|
||||
test('grade B with kill condition becomes C', () => {
|
||||
const result = computeGrade(makeStepResults({
|
||||
seasonDelta: 2, recentDelta: 2, situationalDelta: 2, lineEdge: 2,
|
||||
killConditions: [{ code: 'low_minutes' }],
|
||||
}));
|
||||
expect(result.grade).toBe('C');
|
||||
});
|
||||
});
|
||||
|
||||
describe('bonuses', () => {
|
||||
test('sample bonus +5 for > 50 games', () => {
|
||||
const with50 = computeGrade(makeStepResults({ seasonDelta: 1, recentDelta: 1, gamesPlayed: 55 }));
|
||||
const without = computeGrade(makeStepResults({ seasonDelta: 1, recentDelta: 1, gamesPlayed: 20 }));
|
||||
expect(with50.confidence).toBe(without.confidence + 5);
|
||||
});
|
||||
|
||||
test('sample bonus +3 for > 30 games', () => {
|
||||
const with30 = computeGrade(makeStepResults({ seasonDelta: 1, recentDelta: 1, gamesPlayed: 35 }));
|
||||
const without = computeGrade(makeStepResults({ seasonDelta: 1, recentDelta: 1, gamesPlayed: 20 }));
|
||||
expect(with30.confidence).toBe(without.confidence + 3);
|
||||
});
|
||||
|
||||
test('consistency bonus +5 when season and recent agree', () => {
|
||||
const agree = computeGrade(makeStepResults({
|
||||
seasonDelta: 2, recentDelta: 2, seasonAndRecentAgree: true,
|
||||
}));
|
||||
const noInfo = computeGrade(makeStepResults({
|
||||
seasonDelta: 2, recentDelta: 2, seasonAndRecentAgree: null,
|
||||
}));
|
||||
expect(agree.confidence).toBe(noInfo.confidence + 5);
|
||||
});
|
||||
|
||||
test('consistency penalty -5 when season and recent conflict', () => {
|
||||
const conflict = computeGrade(makeStepResults({
|
||||
seasonDelta: 2, recentDelta: 2, seasonAndRecentAgree: false,
|
||||
}));
|
||||
const noInfo = computeGrade(makeStepResults({
|
||||
seasonDelta: 2, recentDelta: 2, seasonAndRecentAgree: null,
|
||||
}));
|
||||
expect(conflict.confidence).toBe(noInfo.confidence - 5);
|
||||
});
|
||||
});
|
||||
});
|
||||
Reference in New Issue
Block a user