import { describe, it, expect, vi, beforeEach } from "vitest"; // Mock the database context before importing any model vi.mock("~/database/context", () => ({ database: vi.fn(), })); import { database } from "~/database/context"; import { getSurfaceEloMap, batchUpsertSurfaceElos, getSurfaceElosForSeason } from "../surface-elo"; const mockDb = { select: vi.fn().mockReturnThis(), from: vi.fn().mockReturnThis(), where: vi.fn().mockReturnThis(), innerJoin: vi.fn().mockReturnThis(), orderBy: vi.fn().mockReturnThis(), insert: vi.fn().mockReturnThis(), values: vi.fn().mockReturnThis(), onConflictDoUpdate: vi.fn().mockReturnThis(), }; beforeEach(() => { vi.clearAllMocks(); (database as ReturnType).mockReturnValue(mockDb); // Reset chain to return mock itself (supports fluent chaining) mockDb.select.mockReturnValue(mockDb); mockDb.from.mockReturnValue(mockDb); mockDb.where.mockReturnValue(mockDb); mockDb.innerJoin.mockReturnValue(mockDb); mockDb.orderBy.mockReturnValue(mockDb); mockDb.insert.mockReturnValue(mockDb); mockDb.values.mockReturnValue(mockDb); mockDb.onConflictDoUpdate.mockResolvedValue(undefined); }); describe("getSurfaceEloMap", () => { it("returns an empty Map when no records exist", async () => { mockDb.where.mockResolvedValue([]); const result = await getSurfaceEloMap("season-1"); expect(result).toBeInstanceOf(Map); expect(result.size).toBe(0); }); it("returns a Map keyed by seasonParticipantId", async () => { mockDb.where.mockResolvedValue([ { seasonParticipantId: "p1", worldRanking: 1, eloHard: 1800, eloClay: 1700, eloGrass: 1600 }, { seasonParticipantId: "p2", worldRanking: null, eloHard: null, eloClay: 1750, eloGrass: null }, ]); const result = await getSurfaceEloMap("season-1"); expect(result.size).toBe(2); expect(result.get("p1")).toEqual({ worldRanking: 1, eloHard: 1800, eloClay: 1700, eloGrass: 1600 }); expect(result.get("p2")).toEqual({ worldRanking: null, eloHard: null, eloClay: 1750, eloGrass: null }); }); it("preserves null surface Elos (does not coerce to 0)", async () => { mockDb.where.mockResolvedValue([ { seasonParticipantId: "p1", worldRanking: null, eloHard: null, eloClay: null, eloGrass: null }, ]); const result = await getSurfaceEloMap("season-1"); const entry = result.get("p1"); expect(entry?.worldRanking).toBeNull(); expect(entry?.eloHard).toBeNull(); expect(entry?.eloClay).toBeNull(); expect(entry?.eloGrass).toBeNull(); }); }); describe("batchUpsertSurfaceElos", () => { it("does nothing when given an empty array", async () => { await batchUpsertSurfaceElos([]); expect(mockDb.insert).not.toHaveBeenCalled(); }); it("writes canonical rows using resolved canonical participant ids", async () => { // season_participants lookup returns canonical links for p1 and p2. mockDb.where.mockResolvedValue([ { id: "p1", canonicalId: "canon-1" }, { id: "p2", canonicalId: "canon-2" }, ]); await batchUpsertSurfaceElos([ { participantId: "p1", sportsSeasonId: "s1", worldRanking: 1, eloHard: 1800, eloClay: 1700, eloGrass: 1600 }, { participantId: "p2", sportsSeasonId: "s1", worldRanking: null, eloHard: null, eloClay: 1750, eloGrass: undefined }, ]); expect(mockDb.insert).toHaveBeenCalledOnce(); expect(mockDb.values).toHaveBeenCalledOnce(); expect(mockDb.onConflictDoUpdate).toHaveBeenCalledOnce(); const passedValues = mockDb.values.mock.calls[0][0] as Array<{ participantId: string; eloHard: number | null; eloClay: number | null; eloGrass: number | null; }>; expect(passedValues).toHaveLength(2); expect(passedValues[0].participantId).toBe("canon-1"); expect(passedValues[0].eloHard).toBe(1800); expect(passedValues[1].participantId).toBe("canon-2"); expect(passedValues[1].eloHard).toBeNull(); expect(passedValues[1].eloGrass).toBeNull(); // undefined → null }); it("skips season_participants that have no canonical link", async () => { mockDb.where.mockResolvedValue([]); // no canonical links; short-circuits. await batchUpsertSurfaceElos([ { participantId: "p1", sportsSeasonId: "s1", worldRanking: 5, eloHard: 2000 }, ]); expect(mockDb.insert).not.toHaveBeenCalled(); }); }); describe("getSurfaceElosForSeason", () => { it("returns records joined with participant names", async () => { mockDb.orderBy.mockResolvedValue([ { id: "rec-1", participantId: "p1", sportsSeasonId: "s1", worldRanking: 1, eloHard: 1800, eloClay: 1700, eloGrass: 1600, updatedAt: new Date("2025-01-01"), participantName: "Carlos Alcaraz", }, ]); const result = await getSurfaceElosForSeason("s1"); expect(result).toHaveLength(1); expect(result[0].participantName).toBe("Carlos Alcaraz"); expect(result[0].eloHard).toBe(1800); }); it("returns empty array when no Elo records exist", async () => { mockDb.orderBy.mockResolvedValue([]); const result = await getSurfaceElosForSeason("s1"); expect(result).toEqual([]); }); });