import { describe, it, expect, vi, beforeEach } from "vitest"; import { calculateReplacementLevel, calculateVORP } from "~/services/ev-calculator"; import { syncVorpForSeason } from "../participant-expected-value"; const { mockUpdate, mockSet, mockDb, mockSqlFn } = vi.hoisted(() => { const update = vi.fn(); const set = vi.fn(); const sqlFn = Object.assign(vi.fn(() => ({})), { join: vi.fn(() => ({})) }); const db = { update, select: vi.fn() }; return { mockUpdate: update, mockSet: set, mockDb: db, mockSqlFn: sqlFn }; }); vi.mock("~/database/context", () => ({ database: () => mockDb, })); vi.mock("~/database/schema", () => ({ seasonParticipants: { id: "id" }, seasonParticipantExpectedValues: { participantId: "participantId", sportsSeasonId: "sportsSeasonId", }, })); vi.mock("drizzle-orm", () => ({ eq: vi.fn((field, value) => ({ field, value })), and: vi.fn((...args) => ({ and: args })), count: vi.fn(() => ({ count: true })), sql: mockSqlFn, })); describe("syncVorpForSeason", () => { beforeEach(() => { vi.clearAllMocks(); }); it("calculates correct VORP values for 14 participants with EVs 100 down to 35 (step 5)", () => { // 14 participants: EVs = 100, 95, 90, 85, 80, 75, 70, 65, 60, 55, 50, 45, 40, 35 // replacement level = avg of positions 12-14 (0-indexed 11-13) = avg(45, 40, 35) = 40 const evValues = Array.from({ length: 14 }, (_, i) => 100 - i * 5); const replacementLevel = calculateReplacementLevel(evValues); expect(replacementLevel).toBe(40); expect(calculateVORP(100, replacementLevel)).toBe(60); expect(calculateVORP(35, replacementLevel)).toBe(-5); }); it("returns early when no EVs exist for the season", async () => { const mockSelectChain = { from: vi.fn().mockReturnThis(), where: vi.fn().mockResolvedValue([]), }; mockDb.select = vi.fn().mockReturnValue(mockSelectChain); await syncVorpForSeason("season-empty"); expect(mockUpdate).not.toHaveBeenCalled(); }); it("calls db.update with correct vorpValue for each participant", async () => { // 3 participants: EVs 100, 70, 40 // replacement level = avg of positions 12-14, clamped to [40] → 40 // VORP: 100→60, 70→30, 40→0 const mockEvRecords = [ { participantId: "p1", expectedValue: "100", sportsSeasonId: "season-1" }, { participantId: "p2", expectedValue: "70", sportsSeasonId: "season-1" }, { participantId: "p3", expectedValue: "40", sportsSeasonId: "season-1" }, ]; const mockSelectChain = { from: vi.fn().mockReturnThis(), where: vi.fn().mockResolvedValue(mockEvRecords), }; mockDb.select = vi.fn().mockReturnValue(mockSelectChain); const mockWhereResolved = vi.fn().mockResolvedValue([]); mockSet.mockReturnValue({ where: mockWhereResolved }); mockUpdate.mockReturnValue({ set: mockSet }); await syncVorpForSeason("season-1"); expect(mockUpdate).toHaveBeenCalledTimes(1); expect(mockSet).toHaveBeenCalledTimes(1); const setArg = mockSet.mock.calls[0][0]; expect(setArg).toHaveProperty("vorpValue"); expect(setArg).toHaveProperty("updatedAt"); }); });