Files
vyndr/tests/unit/vyndrSystems.test.js
T
builtbykev c8790fde55 Session 57: Phase 0 — Kill the Lies (2309 tests)
Work-order Phase 0 (Jul 10 live audit): every fabricated UI element deleted
or rewired to real data. Deletion sprint — no new product features.

0.1 Fake NBA game: root cause was scheduleService fetching the ESPN
    scoreboard with no ?dates= param or date filter — off-season ESPN
    returns the NEAREST slate (Jun 13 NYK@SA Finals rendered as tonight).
    Now pinned to the requested ET date + defensive filter; undated events
    dropped. Honest month-aware per-sport empty states (lib/emptyState.js).
0.2 Fake header counters: liveTick stripped to a bare 1s pulse (the
    auto-incrementing "247 graded", sin-driven brain-%, aPlus/cascades are
    dead). New GET /api/snapshot/summary (cache-only, before /:sport) +
    Next proxy; HeartbeatBar shows the real graded count and SYNC =
    elapsed since the last pipeline run (amber past 5 min).
0.3 Ticker: hardcoded fallback items deleted (real snapshot exhaust only);
    MOVE kept — computeLineDeltas is real movement. <4 real items → no
    ticker; bar publishes --ticker-h so the fixed header collapses cleanly.
0.4 /terminal retired: route redirects to /dashboard; VVI/injury-wire/
    leaders layouts preserved unrouted as §12 content-engine templates.
    Nav PRIMARY = Slate/Scan/Ledger; BottomTabBar Terminal→Explore; PWA
    shortcut Terminal→Ledger; #terminal alias → /dashboard.
0.5 ›Query nav pill deleted (duplicate /scan link).

Backend 2289 → 2309 tests (199 suites), web build exit 0.
Spec: specs/phase-0-kill-the-lies.md. Next: work-order Phase 1 (ledger
persistence + settlement).

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-10 20:17:49 -04:00

193 lines
8.2 KiB
JavaScript
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
// VYNDR 2.0 — Phase G systems (Session 38): living layer, i18n/odds, a11y
// prefs, paywall/checkout, parlay correlation math. Pure logic runs directly
// via the CommonJS modules; the .tsx glue is asserted against source text.
const fs = require('fs');
const path = require('path');
const WEB = path.join(__dirname, '..', '..', 'web', 'src');
const read = (rel) => fs.readFileSync(path.join(WEB, rel), 'utf8');
const parlay = require('../../web/src/lib/parlayMath');
const odds = require('../../web/src/lib/oddsFormat');
const prefs = require('../../web/src/lib/prefs');
const { liveTick } = require('../../web/src/lib/liveTick');
const { checkoutUrl } = require('../../web/src/lib/checkout');
describe('Phase G.5 — parlay correlation math (§12)', () => {
const jokicPts = { player: 'Jokic', team: 'DEN', sport: 'nba', grade: 'A' };
const jokicReb = { player: 'Jokic', team: 'DEN', sport: 'nba', grade: 'A' };
const murray = { player: 'Murray', team: 'DEN', sport: 'nba', grade: 'A' };
const judge = { player: 'Judge', team: 'NYY', sport: 'mlb', grade: 'A' };
const tatum = { player: 'Tatum', team: 'BOS', sport: 'nba', grade: 'A' };
it('encodes the correlation tiers exactly', () => {
expect(parlay.getCorrelation(jokicPts, jokicReb)).toBe(0.62); // same player
expect(parlay.getCorrelation(jokicPts, murray)).toBe(0.34); // same team+sport
expect(parlay.getCorrelation(jokicPts, tatum)).toBe(0.06); // same league
expect(parlay.getCorrelation(jokicPts, judge)).toBe(0); // cross-sport
});
it('parlayGrade penalizes a correlated stack vs an independent one', () => {
const correlated = parlay.parlayGrade([jokicPts, jokicReb]); // 0.62 → penalty
const independent = parlay.parlayGrade([judge, tatum]); // cross-sport, no penalty
const order = parlay.GRADE_ORDER;
expect(order.indexOf(correlated)).toBeGreaterThan(order.indexOf(independent));
expect(independent).toBe('A');
});
it('converts American↔decimal correctly', () => {
expect(parlay.amToDec(110)).toBeCloseTo(2.1, 3);
expect(parlay.amToDec(-135)).toBeCloseTo(1.741, 2);
expect(parlay.decToAm(2.1)).toBe(110);
});
it('maps grades to representative odds and combines a slip', () => {
expect(parlay.GRADE_ODDS['A+']).toBe(-135);
const dec = parlay.combinedDecimal([jokicPts, judge]); // 110 × 110
expect(dec).toBeCloseTo(2.1 * 2.1, 3);
expect(parlay.combinedAmerican([])).toBe('—');
expect(parlay.combinedAmerican([jokicPts])).toMatch(/^[+-]\d+$/);
});
});
describe('Phase G.2 — odds formatting (§9, the totals-pass-through rule)', () => {
it('converts moneylines across formats', () => {
expect(odds.fmtOdds('+150', 'decimal')).toBe('2.50');
expect(odds.fmtOdds('-110', 'decimal')).toBe('1.91');
expect(odds.fmtOdds('+150', 'fractional')).toBe('3/2');
expect(odds.fmtOdds('-110', 'implied')).toBe('52%');
expect(odds.fmtOdds('+150', 'american')).toBe('+150');
});
it('treats integer NUMBERS (the grade→odds map) as moneylines', () => {
expect(odds.fmtOdds(110, 'american')).toBe('+110');
expect(odds.fmtOdds(-135, 'decimal')).toBe('1.74');
});
it('CRITICAL: totals / lines pass through UNCHANGED', () => {
expect(odds.fmtOdds('228.5', 'decimal')).toBe('228.5'); // half-point total
expect(odds.fmtOdds('229', 'implied')).toBe('229'); // unsigned integer total
expect(odds.fmtOdds('-7.5', 'american')).toBe('-7.5'); // spread
expect(odds.fmtOdds(228.5, 'decimal')).toBe(228.5); // non-integer number
});
it('region presets set odds format + currency', () => {
expect(odds.regionPreset('UK')).toMatchObject({ odds: 'fractional', currency: 'GBP', currencySymbol: '£' });
expect(odds.regionPreset('EU').odds).toBe('decimal');
expect(odds.regionPreset('US').odds).toBe('american');
});
});
describe('Phase G.3 — accessibility prefs (§10)', () => {
function fakeEl() {
const attrs = {};
return {
attrs,
setAttribute: (k, v) => { attrs[k] = v; },
removeAttribute: (k) => { delete attrs[k]; },
};
}
function fakeStore() {
const m = {};
return { getItem: (k) => (k in m ? m[k] : null), setItem: (k, v) => { m[k] = String(v); } };
}
it('applyPrefs sets the data-* attributes the CSS layer keys off', () => {
const el = fakeEl();
prefs.applyPrefs({ contrast: 'high', motion: 'reduced', text: 'xl', cb: 'on', font: 'readable' }, el);
expect(el.attrs['data-contrast']).toBe('high');
expect(el.attrs['data-motion']).toBe('reduced');
expect(el.attrs['data-text']).toBe('xl');
expect(el.attrs['data-cb']).toBe('1');
expect(el.attrs['data-font']).toBe('readable');
});
it('applyPrefs removes attributes for default values', () => {
const el = fakeEl();
el.setAttribute('data-contrast', 'high');
prefs.applyPrefs({ contrast: 'off', text: 'base' }, el);
expect(el.attrs['data-contrast']).toBeUndefined();
expect(el.attrs['data-text']).toBeUndefined();
});
it('prefs round-trip through storage', () => {
const store = fakeStore();
prefs.savePrefs({ contrast: 'high', region: 'UK' }, store);
const loaded = prefs.loadPrefs(store);
expect(loaded.contrast).toBe('high');
expect(loaded.region).toBe('UK');
expect(loaded.lang).toBe('en'); // defaults merged
});
});
describe('Phase G.1 — living-layer tick engine (§8)', () => {
beforeEach(() => liveTick.reset());
afterEach(() => liveTick.reset());
it('fans out one tick to all subscribers', () => {
let calls = 0;
const unsub = liveTick.subscribe(() => { calls += 1; });
liveTick.tick();
liveTick.tick();
expect(calls).toBe(2);
unsub();
liveTick.tick();
expect(calls).toBe(2); // unsubscribed
});
it('advances tick with a fresh state object', () => {
const first = liveTick.state;
for (let i = 0; i < 7; i++) liveTick.tick();
expect(liveTick.state.tick).toBe(7);
expect(liveTick.state).not.toBe(first); // new object → React re-renders
});
// Session 57 (Phase 0) — the fake header counters are dead. The tick engine
// must carry NO display data: no auto-incrementing graded count, no
// sin-driven neural %. Real numbers come from /api/snapshot/summary only.
it('carries no fabricated display counters', () => {
liveTick.tick();
expect(Object.keys(liveTick.state)).toEqual(['tick']);
const src = read('lib/liveTick.js');
expect(src).not.toContain('neural');
expect(src).not.toContain('247');
});
});
describe('Phase G.4 — checkout (§12)', () => {
it('maps a plan tier to the checkout URL', () => {
expect(checkoutUrl('analyst')).toBe('/api/checkout?tier=analyst');
expect(checkoutUrl('desk')).toBe('/api/checkout?tier=desk');
expect(checkoutUrl('garbage')).toBe('/api/checkout?tier=analyst'); // safe default
});
});
describe('Phase G — React glue wired correctly', () => {
it('HeartbeatBar consumes the live tick + renders SIGNAL LIVE', () => {
const src = read('components/vyndr/LiveLayer.tsx');
expect(src).toContain('useLive');
expect(src).toContain('SIGNAL LIVE');
expect(src).toContain('ekg-track');
expect(src).toContain('count-tick'); // LiveNumber pop
});
// Session 57 (Phase 0) — the heartbeat's numbers are REAL: graded count +
// sync age come from /api/snapshot/summary; the fake neural % is gone.
it('HeartbeatBar reads /api/snapshot/summary and shows no neural %', () => {
const src = read('components/vyndr/LiveLayer.tsx');
expect(src).toContain('/api/snapshot/summary');
expect(src).not.toContain('neural');
expect(src).toContain('SYNC'); // elapsed-since-snapshot clock
expect(src).toContain('updated_at');
});
it('GlobalHosts registers the design globals + applies prefs on mount', () => {
const src = read('components/vyndr/GlobalHosts.tsx');
expect(src).toContain('window.__prefs');
expect(src).toContain('window.__goPaywall');
expect(src).toContain('window.__checkout');
expect(src).toContain('applyPrefs');
});
it('Nav mounts the heartbeat, a prefs trigger, and a paywall read-meter', () => {
const src = read('components/Nav.tsx');
expect(src).toContain('<HeartbeatBar');
expect(src).toContain('window.__prefs');
expect(src).toContain('window.__goPaywall');
});
it('layout mounts GlobalHosts', () => {
expect(read('app/layout.tsx')).toContain('<GlobalHosts />');
});
});