From 6ef4d39d14d4e10186c5b335d6ef713a3190f108 Mon Sep 17 00:00:00 2001 From: Chris Parsons Date: Wed, 10 Jun 2026 12:47:56 -0700 Subject: [PATCH] Fix MLB division leaders showing under wildcard instead of division sections The MlbStandingsAdapter never populated `divisionRank`, so every team evaluated to `divisionRank ?? 99 > 1` in `buildDivisionSections` and fell through to the wildcard section. Computes `divisionRank` per team using ESPN's existing division entry order (which already applies MLB's official tiebreakers) rather than re-sorting, then passes it through to the upsert. Co-Authored-By: Claude Sonnet 4.6 --- .../standings-sync/__tests__/mlb.test.ts | 17 +++++++++++++++++ app/services/standings-sync/mlb.ts | 16 ++++++++++++++++ 2 files changed, 33 insertions(+) diff --git a/app/services/standings-sync/__tests__/mlb.test.ts b/app/services/standings-sync/__tests__/mlb.test.ts index da3e545..e7242f9 100644 --- a/app/services/standings-sync/__tests__/mlb.test.ts +++ b/app/services/standings-sync/__tests__/mlb.test.ts @@ -232,6 +232,23 @@ describe("MlbStandingsAdapter", () => { expect(ori?.externalTeamId).toBe("2"); }); + it("assigns divisionRank 1 to the division leader and 2 to the next", async () => { + vi.mocked(fetch).mockResolvedValueOnce({ + ok: true, + json: async () => SAMPLE_ESPN_RESPONSE, + } as Response); + + const adapter = new MlbStandingsAdapter(); + const records = await adapter.fetchStandings(); + + const ori = records.find((r) => r.teamName === "Baltimore Orioles"); + const bos = records.find((r) => r.teamName === "Boston Red Sox"); + const mets = records.find((r) => r.teamName === "New York Mets"); + expect(ori?.divisionRank).toBe(1); + expect(bos?.divisionRank).toBe(2); + expect(mets?.divisionRank).toBe(1); + }); + it("never sets otLosses (MLB has no OT losses)", async () => { vi.mocked(fetch).mockResolvedValueOnce({ ok: true, diff --git a/app/services/standings-sync/mlb.ts b/app/services/standings-sync/mlb.ts index b1b6d3f..02f986b 100644 --- a/app/services/standings-sync/mlb.ts +++ b/app/services/standings-sync/mlb.ts @@ -46,6 +46,21 @@ export class MlbStandingsAdapter implements StandingsSyncAdapter { sm: statsMap(entry.stats), })); + // Compute divisionRank: rank each team within its own division by win/loss. + const divisionRankMap = new Map(); + const divisionGroups = new Map(); + for (const item of withSm) { + const key = item.division; + if (!divisionGroups.has(key)) divisionGroups.set(key, []); + divisionGroups.get(key)?.push(item); + } + for (const divEntries of divisionGroups.values()) { + // Use the ESPN entry order within each division — ESPN already applies correct tiebreakers. + divEntries.forEach((e, i) => { + divisionRankMap.set(e.entry.team.id, i + 1); + }); + } + const sorted = [...withSm].toSorted(sortByWinLoss); return sorted.map(({ entry, conference, division, sm }, leagueIdx): FetchedStandingsRecord => { @@ -76,6 +91,7 @@ export class MlbStandingsAdapter implements StandingsSyncAdapter { conference: abbreviateLeague(conference), division: abbreviatedDiv || undefined, conferenceRank: parseConferenceRank(sm), + divisionRank: divisionRankMap.get(entry.team.id), leagueRank: leagueIdx + 1, streak, lastTen,