Files
vyndr/tests/unit/opponentStrength.test.js
builtbykev 55157b3288 Close-capture retry (lock-walled) + MLB opp_rank_stat derivation
PHASE 1 — CLOSE-CAPTURE RETRY, test-first. The closing capture gets a
retry the snapshot path deliberately does not: a snapshot re-runs at the
next slot, but a MISSED CLOSE IS PERMANENT, and the feed flaked once on a
dry induce. Three hard rules, each driven by a test written before the
logic:
  - BOUNDED attempts (default 3) with short backoff so every attempt fits
    inside the window. Never infinite.
  - HARD LOCK-WALL: inside lockWallMinutes of first pitch (or past it) it
    stops and records missed_close. A price captured AT or AFTER lock is
    NOT a close; storing one would fabricate the CLV baseline.
  - NO BOUND LOCK TIME -> refuse immediately, never burn retries on a prop
    whose close cannot be timed.
On exhaustion it records missed_close with NO price — never a stale,
mid-day or post-lock line.

PHASE 3 — MLB opp_rank_stat DERIVED, contract-locked. MLB previously had
no opponent metric at all (ESPN's MLB team endpoint carries none), so
engine1's +/-1.0 opponent factor never fired for the sport carrying most
of our volume. Derived from data we already ingest: statsapi team pitching
splits, all 30 teams in ONE free unauthenticated call.

THE SHARED CONTRACT is documented and TESTED, not assumed: 0-1 scale,
HIGH (>=0.70) = WEAK opponent, LOW (<=0.30) = TOUGH — identical to WNBA's
live semantics. Polarity is the highest-risk part: backwards polarity does
not fail loudly, it silently adjusts every MLB grade the wrong way. A test
asserts MLB polarity EQUALS WNBA polarity using engine1's own thresholds.

PROVEN AGAINST THE LIVE FEED:
  Colorado Rockies  BAA .286 -> opp_rank 0.983  (weak, fires weak_opponent)
  LA Dodgers        BAA .215 -> opp_rank 0.017  (tough, fires top_opponent)
  POLARITY HOLDS: true

HONEST NULLS, tested: thin league baseline, thin opponent sample, unmapped
stat, unknown opponent, or a missing field all return NULL with a reason —
we are FIXING a silent null, so it is never replaced by a confident guess
off three games. opponentStrengthHealth pages on an empty source AND on
derived-null-for-a-sport-we-expect-to-derive.

Suite 285/3435 green, build exit 0.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01SmNjJAwEnqHPtXbvSZR8kA
2026-07-20 12:27:47 -04:00

95 lines
4.0 KiB
JavaScript
Raw Permalink 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.
/**
* Session 64 Phase 3 — opp_rank_stat is ONE column with ONE meaning.
*
* The highest-risk item is POLARITY. Backwards polarity does not fail loudly —
* it silently adjusts every MLB grade in the WRONG direction, which is worse
* than having no value at all. So the contract is asserted against WNBA's live
* semantics, not merely "a number appears".
*/
const os = require('../../src/services/opponentStrength');
// 30 teams; BAA against ranges 0.200 (toughest staff) → 0.290 (weakest).
const league = Array.from({ length: 30 }, (_, i) => ({
teamId: i + 1,
stat: { avg: 0.200 + i * 0.003, slg: 0.350 + i * 0.005, gamesPlayed: 90, homeRuns: 10 + i, strikeOuts: 300 + i },
}));
describe('THE SHARED CONTRACT — scale and polarity match WNBA', () => {
test('scale is 01, never a raw count', () => {
const r = os.deriveMlbOppRank(league, 15, 'hits');
expect(r.value).toBeGreaterThanOrEqual(0);
expect(r.value).toBeLessThanOrEqual(1);
expect(r.value).not.toBe(league[14].stat.avg); // not the raw stat
});
test('POLARITY: the WEAKEST staff (highest BAA) scores HIGH = weak opponent', () => {
const weakest = os.deriveMlbOppRank(league, 30, 'hits'); // BAA .287
expect(weakest.value).toBeGreaterThanOrEqual(0.70);
});
test('POLARITY: the TOUGHEST staff (lowest BAA) scores LOW = tough opponent', () => {
const toughest = os.deriveMlbOppRank(league, 1, 'hits'); // BAA .200
expect(toughest.value).toBeLessThanOrEqual(0.30);
});
test('MLB polarity EQUALS WNBA polarity — high fires weak, low fires tough', () => {
// engine1's live thresholds, applied identically to both sports.
const WEAK = 0.70;
const TOUGH = 0.30;
const weak = os.deriveMlbOppRank(league, 30, 'hits').value;
const tough = os.deriveMlbOppRank(league, 1, 'hits').value;
// The same comparison engine1 makes for WNBA must classify MLB the same way.
expect(weak >= WEAK).toBe(true);
expect(tough <= TOUGH).toBe(true);
expect(weak).toBeGreaterThan(tough); // monotone in the right direction
});
test('a mid-pack staff lands mid-scale and fires NEITHER factor', () => {
const mid = os.deriveMlbOppRank(league, 15, 'hits').value;
expect(mid).toBeGreaterThan(0.30);
expect(mid).toBeLessThan(0.70);
});
});
describe('HONEST NULLS — never a confident value off thin data', () => {
test('a thin LEAGUE baseline returns null, not a rank', () => {
const r = os.deriveMlbOppRank(league.slice(0, 5), 1, 'hits');
expect(r.value).toBeNull();
expect(r.reason).toBe('league_baseline_too_thin');
});
test("a thin OPPONENT sample returns null", () => {
const thin = league.map((t, i) => (i === 0 ? { ...t, stat: { ...t.stat, gamesPlayed: 3 } } : t));
const r = os.deriveMlbOppRank(thin, 1, 'hits');
expect(r.value).toBeNull();
expect(r.reason).toBe('opponent_sample_too_thin');
});
test('an unmapped stat returns null rather than a wrong field', () => {
expect(os.deriveMlbOppRank(league, 1, 'stolen_bases').reason).toBe('stat_not_mapped');
});
test('an unknown opponent returns null', () => {
expect(os.deriveMlbOppRank(league, 999, 'hits').reason).toBe('opponent_not_found');
});
test('a missing field returns null, never 0', () => {
const noField = league.map((t) => ({ ...t, stat: { ...t.stat, avg: null } }));
const r = os.deriveMlbOppRank(noField, 1, 'hits');
expect(r.value).toBeNull();
expect(r.value).not.toBe(0);
});
});
describe('HEALTH — we are fixing a silent null, not rebuilding one', () => {
test('empty source pages', () => {
expect(os.opponentStrengthHealth({ sport: 'mlb', teamsLoaded: 0 }).alarm).toBe(true);
});
test('all-null despite a loaded source pages', () => {
expect(os.opponentStrengthHealth({ sport: 'mlb', teamsLoaded: 30, attempted: 25, derived: 0 }).alarm).toBe(true);
});
test('healthy derivation is quiet', () => {
expect(os.opponentStrengthHealth({ sport: 'mlb', teamsLoaded: 30, attempted: 25, derived: 24 }).alarm).toBe(false);
});
});