Files
vyndr/tests/unit/tierGating.test.js
T

109 lines
4.0 KiB
JavaScript

const { applyTierGating, __internals } = require('../../src/utils/tierGating');
function sampleResult(overrides = {}) {
return {
player: 'Jalen Brunson',
stat_type: 'points',
line: 26.5,
direction: 'over',
book: 'draftkings',
grade: 'A',
confidence: 78,
edge_pct: 6.2,
kill_conditions_triggered: [
{ code: 'TRAP', reason: 'Multiple trap signals firing.' },
{ code: 'COLD_L5', reason: 'Last-5 average below the line.' },
],
reasoning: {
summary: 'Brunson averaging 28.4 last 5; weak NYK defense; rested.',
steps: { season_avg: { value: 26.1 }, final_grade: 'A' },
},
...overrides,
};
}
describe('applyTierGating — free tier locks the explanation surface', () => {
test('keeps grade + confidence + edge_pct (the hook)', () => {
const gated = applyTierGating(sampleResult(), 'free');
expect(gated.grade).toBe('A');
expect(gated.confidence).toBe(78);
expect(gated.edge_pct).toBe(6.2);
expect(gated.player).toBe('Jalen Brunson');
expect(gated.stat_type).toBe('points');
expect(gated.line).toBe(26.5);
expect(gated.direction).toBe('over');
});
test('redacts reasoning.summary + steps + marks locked', () => {
const gated = applyTierGating(sampleResult(), 'free');
expect(gated.reasoning.summary).toBe(__internals.LOCKED_REASONING_SUMMARY);
expect(gated.reasoning.steps).toBeNull();
expect(gated.reasoning.locked).toBe(true);
});
test('redacts kill condition reasons but keeps codes for badge rendering', () => {
const gated = applyTierGating(sampleResult(), 'free');
expect(gated.kill_conditions_triggered).toHaveLength(2);
for (const kc of gated.kill_conditions_triggered) {
expect(kc.code).toBeDefined();
expect(kc.reason).toBe(__internals.LOCKED_KILL_REASON);
expect(kc.locked).toBe(true);
}
});
test('tags response with tier_gated + upgrade_hint', () => {
const gated = applyTierGating(sampleResult(), 'free');
expect(gated.tier_gated).toBe(true);
expect(typeof gated.upgrade_hint).toBe('string');
});
test('does not mutate the input result', () => {
const original = sampleResult();
applyTierGating(original, 'free');
expect(original.reasoning.summary).toContain('Brunson averaging');
expect(original.kill_conditions_triggered[0].reason).toContain('Multiple trap');
});
});
describe('applyTierGating — paid tiers pass through unchanged', () => {
test('analyst sees full reasoning', () => {
const r = sampleResult();
const out = applyTierGating(r, 'analyst');
expect(out.reasoning.summary).toContain('Brunson averaging');
expect(out.reasoning.locked).toBeUndefined();
expect(out.tier_gated).toBeUndefined();
expect(out.kill_conditions_triggered[0].reason).toContain('Multiple trap');
});
test('desk sees full reasoning', () => {
const out = applyTierGating(sampleResult(), 'desk');
expect(out.reasoning.summary).toContain('Brunson averaging');
expect(out.tier_gated).toBeUndefined();
});
});
describe('applyTierGating — edge cases', () => {
test('null result returns null', () => {
expect(applyTierGating(null, 'free')).toBeNull();
expect(applyTierGating(undefined, 'free')).toBeUndefined();
});
test('unknown tier name → treated as free', () => {
const out = applyTierGating(sampleResult(), 'pirate');
expect(out.reasoning.locked).toBe(true);
});
test('result with no kill_conditions_triggered still produces a clean response', () => {
const noKills = { ...sampleResult(), kill_conditions_triggered: [] };
const out = applyTierGating(noKills, 'free');
expect(out.kill_conditions_triggered).toEqual([]);
expect(out.reasoning.locked).toBe(true);
});
test('result with malformed reasoning still produces a locked placeholder', () => {
const out = applyTierGating({ ...sampleResult(), reasoning: null }, 'free');
expect(out.reasoning.summary).toBe(__internals.LOCKED_REASONING_SUMMARY);
expect(out.reasoning.locked).toBe(true);
});
});