Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
60 changes: 60 additions & 0 deletions packages/backend/src/fantasy/fantasy.service.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1702,6 +1702,66 @@ describe('FantasyService', () => {
}
});

it('waits for delayed cold-cache FantasyCalc responses before falling back', async () => {
vi.useFakeTimers();
vi.setSystemTime(new Date('2026-09-11T12:00:00.000Z'));
const internals = service as unknown as FantasyServiceInternals;
const league: SleeperLeague = {
league_id: '998',
name: 'Redraft League',
season: '2026',
status: 'in_season',
avatar: null,
total_rosters: 12,
roster_positions: ['QB'],
};
(Axios.get as Mock).mockImplementationOnce(
() =>
new Promise<{ data: FantasyCalcResponseEntry[] }>((resolve) => {
setTimeout(
() =>
resolve({
data: [
{
player: { sleeperId: 'p2' },
value: 1234,
redraftValue: 1234,
overallRank: 50,
positionRank: 20,
trend30Day: 1,
},
],
}),
300,
);
}),
);

try {
const snapshotPromise = internals.getFantasyCalcValuesForOverview(league);
await vi.advanceTimersByTimeAsync(300);

await expect(snapshotPromise).resolves.toEqual({
values: new Map([
[
'p2',
{
sleeperId: 'p2',
value: 1234,
overallRank: 50,
positionRank: 20,
trend30Day: 1,
tradeFrequency: null,
},
],
]),
version: 1,
});
} finally {
vi.useRealTimers();
}
});

it('keeps the overview FantasyCalc version aligned with the fallback values snapshot', async () => {
const internals = service as unknown as FantasyServiceInternals;
const serviceState = service as unknown as {
Expand Down
5 changes: 4 additions & 1 deletion packages/backend/src/fantasy/fantasy.service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -48,7 +48,10 @@ const WAIVER_MARKET_CACHE_MS = 6 * 60 * 60 * 1000;
const FANTASYCALC_CACHE_MS = 6 * 60 * 60 * 1000;
const FANTASYCALC_FAILURE_CACHE_MS = 5 * 60 * 1000;
const FANTASYCALC_TIMEOUT_MS = 2000;
const FANTASYCALC_OVERVIEW_WAIT_MS = 150;
// Must be long enough for the FantasyCalc request (bounded by FANTASYCALC_TIMEOUT_MS) to actually
// finish on a cold cache; a short wait here mostly guarantees the fallback (all-null marketValues)
// on every first request after a deploy, since the in-memory cache resets on restart.
const FANTASYCALC_OVERVIEW_WAIT_MS = FANTASYCALC_TIMEOUT_MS;
const WAIVER_HISTORY_SEASONS = 3;
const NFL_REGULAR_SEASON_WEEKS = 18;
const SLEEPER_ID_PATTERN = /^\d{1,32}$/;
Expand Down
Loading