Files
vyndr/tests/unit/slateJoinInvariant.test.js
builtbykev 9fc17a4689 Fix the team resolve properly: backfill the name BEFORE confirmation
My first attempt did not work in prod -- team stayed 0/323 after deploy.
I resolved the team name AFTER the hint-confirmation check, but the check
itself reads hit.currentTeam.name, which is undefined because
/sports/1/players returns { id, link }. With a FULL-NAME hint (what
snapshotService passes) neither branch of teamRecordMatchesHint could
match: the name branch had no name, and the abbr branch cannot resolve a
full name to an abbr. Confirmation failed, the team was nulled, and my
later backfill ran on an already-null value.

withTeamName() now backfills the name from the cached /teams list BEFORE
any comparison, and is used at all three confirmation sites plus the
return. Verified against the live API on all four cases: no hint, FULL-NAME
hint, abbr hint -> "Philadelphia Phillies"; WRONG hint -> null.

That last case matters most: a wrong hint must still REFUSE. The
confirmation exists so a namesake collision cannot tag a player to a team
he is not on, which would fabricate opponents downstream. Making the match
succeed must not make it succeed wrongly, and a test locks it.

Gates: 4,087 tests / 327 suites green; next build exit 0.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01QJs13VsyiSKYQP6rj3NNmc
2026-08-01 23:34:16 -04:00

103 lines
4.7 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.
'use strict';
/**
* S59 SLATE JOIN INVARIANT — re-armed 2026-08-01.
*
* The invariant drops a graded prop whose player's REAL team is not a
* participant in that game. It was written correctly and was silently INERT:
* `mlbStatsAdapter.searchPlayer` returned `team: null` for every player
* (`/sports/1/players` gives `currentTeam: { id, link }` with no `name`), so
* `team` was null on 416/416 stored grades and the guard could never fire.
*
* Two properties are locked here:
* 1. FAIL-SAFE — it drops ONLY on a positive not-in-game. Unknown team, or
* unknown game participants, ABSTAIN. Dropping a real prop on absent data
* would be a new fabrication, worse than the bad row it guards against.
* 2. It must key off the PLAYER'S team, never the prop's own game teams —
* comparing a game to itself always matches and would re-disarm it while
* looking armed.
*/
const { buildPlayerStripsFromProps } = require('../../web/src/lib/slateAdapter');
const prop = (player, team) => ({ player, stat_type: 'hits', line: 1.5, side: 'O', team });
const GAME = { home: 'Philadelphia Phillies', away: 'New York Mets' };
const players = (strips) => strips.map((s) => s.player);
describe('join invariant — fail-safe by construction', () => {
it('DROPS a prop whose player is positively not in this game', () => {
const out = buildPlayerStripsFromProps(
[prop('In Game', 'Philadelphia Phillies'), prop('Wrong Game', 'Toronto Blue Jays')],
{}, {}, Date.now(), GAME,
);
expect(players(out)).toEqual(['In Game']);
});
it('ABSTAINS when the player team is unknown — never drops on absent data', () => {
const out = buildPlayerStripsFromProps(
[prop('No Team', null), prop('Empty Team', '')],
{}, {}, Date.now(), GAME,
);
expect(players(out)).toEqual(['No Team', 'Empty Team']);
});
it('ABSTAINS when the GAME participants are unknown', () => {
const rows = [prop('Anyone', 'Toronto Blue Jays')];
expect(players(buildPlayerStripsFromProps(rows, {}, {}, Date.now(), null))).toEqual(['Anyone']);
expect(players(buildPlayerStripsFromProps(rows, {}, {}, Date.now(), {}))).toEqual(['Anyone']);
});
it('matches on either participant, and on abbreviation or full name', () => {
const out = buildPlayerStripsFromProps(
[prop('Home Guy', 'Philadelphia Phillies'), prop('Away Guy', 'New York Mets')],
{}, {}, Date.now(), GAME,
);
expect(players(out)).toEqual(['Home Guy', 'Away Guy']);
});
});
describe('the invariants input is the PLAYER team, not the props own game', () => {
it('a grade carrying only home_team/away_team does NOT satisfy the guard', () => {
// home_team describes the game the prop came from, so using it as the
// player's team compares the game to itself: always a match, guard dead.
const row = { player: 'X', stat_type: 'hits', line: 1.5, side: 'O',
home_team: 'Toronto Blue Jays', away_team: 'Boston Red Sox' };
const out = buildPlayerStripsFromProps([row], {}, {}, Date.now(), GAME);
expect(players(out)).toEqual(['X']); // abstained — no player team known
});
});
describe('mlbStatsAdapter — the team the invariant depends on', () => {
const { __internals } = require('../../src/services/adapters/mlbStatsAdapter');
const { withTeamName, teamRecordMatchesHint } = __internals || {};
const TEAMS = [{ id: 143, abbr: 'PHI', name: 'Philadelphia Phillies' },
{ id: 141, abbr: 'TOR', name: 'Toronto Blue Jays' }];
it('backfills the team NAME from the id — the list returns { id, link } only', () => {
expect(withTeamName({ id: 143, link: '/api/v1/teams/143' }, TEAMS))
.toEqual({ id: 143, name: 'Philadelphia Phillies' });
});
it('returns null for an absent team rather than an empty shell', () => {
expect(withTeamName(null, TEAMS)).toBeNull();
expect(withTeamName({ link: '/x' }, TEAMS)).toBeNull();
});
it('leaves the name null when the id is unknown — never guessed', () => {
expect(withTeamName({ id: 999 }, TEAMS)).toEqual({ id: 999, name: null });
});
it('a FULL-NAME hint now matches — this is what was silently failing', () => {
// Before the backfill, name was undefined, so a full-name hint could match
// neither branch: confirmation failed and the team was nulled.
const t = withTeamName({ id: 143 }, TEAMS);
expect(teamRecordMatchesHint(t, ['Philadelphia Phillies', 'New York Mets'], TEAMS)).toBe(true);
expect(teamRecordMatchesHint(t, ['PHI'], TEAMS)).toBe(true);
});
it('a WRONG hint still REFUSES — the anti-fabrication guard is preserved', () => {
const t = withTeamName({ id: 143 }, TEAMS);
expect(teamRecordMatchesHint(t, ['Toronto Blue Jays'], TEAMS)).toBe(false);
});
});