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); }); }); });