brackt/app/models/__tests__/team-score-events.test.ts
Chris Parsons b5b60a6093
fix(tests): update mock query keys after participants table rename
Change mock db.query.participants to db.query.seasonParticipants in test
files to match the schema rename from commit 66145a9. This fixes
"Cannot read properties of undefined (reading 'findFirst'/'findMany')"
errors that occurred when production code queries db.query.seasonParticipants
but test mocks only defined the old participants key.

Files updated:
- app/services/simulations/__tests__/world-cup-simulator.test.ts
- app/routes/api/__tests__/draft.force-manual-pick.test.ts
- app/routes/api/__tests__/draft.force-manual-pick.timer-mode.test.ts
- app/routes/api/__tests__/draft.make-pick.timer-mode.test.ts
- server/__tests__/timer-autodraft.test.ts
- app/models/__tests__/team-score-events.test.ts

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-05-01 19:09:28 +00:00

360 lines
11 KiB
TypeScript

import { describe, it, expect, vi, beforeEach } from "vitest";
vi.mock("~/lib/logger", () => ({
logger: { error: vi.fn() },
}));
// ── DB mock helpers ────────────────────────────────────────────────────────
function makeInsertChain() {
const onConflictDoUpdate = vi.fn().mockResolvedValue(undefined);
const values = vi.fn().mockReturnValue({ onConflictDoUpdate });
const insert = vi.fn().mockReturnValue({ values });
return { insert, values, onConflictDoUpdate };
}
interface MakeDbOpts {
sportsSeason?: { sport: { name: string } } | null;
seasonSports?: { seasonId: string }[];
picks?: { teamId: string; seasonId: string }[];
seasons?: {
id: string;
pointsFor1st: number; pointsFor2nd: number; pointsFor3rd: number;
pointsFor4th: number; pointsFor5th: number; pointsFor6th: number;
pointsFor7th: number; pointsFor8th: number;
}[];
scoreEventRows?: {
id: string; teamId: string; teamName?: string;
scoringEventId: string | null; scoringEventName: string | null;
sportName: string | null; pointsDelta: string; occurredAt: Date;
participantIds: string[];
team: { id: string; name: string };
}[];
participantRows?: { id: string; name: string }[];
}
function makeDb(opts: MakeDbOpts = {}) {
const {
sportsSeason = null,
seasonSports = [],
picks = [],
seasons = [],
scoreEventRows = [],
participantRows = [],
} = opts;
const chain = makeInsertChain();
return {
db: {
insert: chain.insert,
query: {
sportsSeasons: {
findFirst: vi.fn().mockResolvedValue(sportsSeason),
},
seasonSports: {
findMany: vi.fn().mockResolvedValue(seasonSports),
},
draftPicks: {
findMany: vi.fn().mockResolvedValue(picks),
},
seasons: {
findMany: vi.fn().mockResolvedValue(seasons),
},
teamScoreEvents: {
findMany: vi.fn().mockResolvedValue(scoreEventRows),
},
seasonParticipants: {
findMany: vi.fn().mockResolvedValue(participantRows),
},
},
} as any,
chain,
};
}
import { recordTeamScoreEvent, recordMatchScoreEvents, getRecentTeamScoreEvents } from "../team-score-events";
const BASE_PARAMS = {
teamId: "team-1",
seasonId: "season-1",
scoringEventId: "event-1",
scoringEventName: "Round of 16",
sportName: "Darts",
participantIds: ["p-1"],
pointsDelta: 10,
};
describe("recordTeamScoreEvent", () => {
beforeEach(() => vi.clearAllMocks());
it("uses matchId-based conflict target when matchId provided", async () => {
const { db, chain } = makeDb();
await recordTeamScoreEvent({ ...BASE_PARAMS, matchId: "match-1" }, db);
expect(chain.insert).toHaveBeenCalled();
expect(chain.values).toHaveBeenCalledWith(
expect.objectContaining({ matchId: "match-1", pointsDelta: "10" })
);
const conflictArg = chain.onConflictDoUpdate.mock.calls[0][0];
expect(conflictArg.target).toHaveLength(3);
// targetWhere should reference matchId IS NOT NULL
expect(conflictArg.targetWhere).toBeDefined();
expect(conflictArg.set).toMatchObject({ pointsDelta: "10", participantIds: ["p-1"] });
});
it("uses scoringEventId-based conflict target when no matchId", async () => {
const { db, chain } = makeDb();
await recordTeamScoreEvent(BASE_PARAMS, db);
expect(chain.insert).toHaveBeenCalled();
expect(chain.values).toHaveBeenCalledWith(
expect.objectContaining({ matchId: null })
);
const conflictArg = chain.onConflictDoUpdate.mock.calls[0][0];
expect(conflictArg.target).toHaveLength(3);
expect(conflictArg.targetWhere).toBeDefined();
});
it("stores pointsDelta as string", async () => {
const { db, chain } = makeDb();
await recordTeamScoreEvent({ ...BASE_PARAMS, pointsDelta: 42 }, db);
expect(chain.values).toHaveBeenCalledWith(expect.objectContaining({ pointsDelta: "42" }));
});
it("defaults matchId to null when not provided", async () => {
const { db, chain } = makeDb();
await recordTeamScoreEvent(BASE_PARAMS, db);
expect(chain.values).toHaveBeenCalledWith(expect.objectContaining({ matchId: null }));
});
});
describe("recordMatchScoreEvents", () => {
beforeEach(() => vi.clearAllMocks());
const BASE_EVENT = {
participantId: "p-1",
sportsSeasonId: "ss-1",
oldFloor: 0,
newFloor: 2,
bracketTemplateId: null as string | null,
matchId: "match-1",
eventId: "event-1",
eventName: "Semifinals",
};
const SEASON_ROW = {
id: "season-1",
pointsFor1st: 100, pointsFor2nd: 75, pointsFor3rd: 50,
pointsFor4th: 40, pointsFor5th: 30, pointsFor6th: 20,
pointsFor7th: 10, pointsFor8th: 5,
};
it("returns early when no fantasy seasons use this sports season", async () => {
const { db, chain } = makeDb({
sportsSeason: { sport: { name: "Darts" } },
seasonSports: [],
});
await recordMatchScoreEvents(BASE_EVENT, db);
expect(chain.insert).not.toHaveBeenCalled();
});
it("returns early when no draft picks found for participant", async () => {
const { db, chain } = makeDb({
sportsSeason: { sport: { name: "Darts" } },
seasonSports: [{ seasonId: "season-1" }],
picks: [],
seasons: [SEASON_ROW],
});
await recordMatchScoreEvents(BASE_EVENT, db);
expect(chain.insert).not.toHaveBeenCalled();
});
it("skips seasons where team did not draft the participant", async () => {
const { db, chain } = makeDb({
sportsSeason: { sport: { name: "Darts" } },
seasonSports: [{ seasonId: "season-1" }, { seasonId: "season-2" }],
picks: [{ teamId: "team-1", seasonId: "season-1" }], // only season-1 has pick
seasons: [SEASON_ROW, { ...SEASON_ROW, id: "season-2" }],
});
await recordMatchScoreEvents(BASE_EVENT, db);
expect(chain.insert).toHaveBeenCalledTimes(1);
expect(chain.values).toHaveBeenCalledWith(expect.objectContaining({ seasonId: "season-1" }));
});
it("skips seasons where point delta is zero or negative", async () => {
const { db, chain } = makeDb({
sportsSeason: { sport: { name: "Darts" } },
seasonSports: [{ seasonId: "season-1" }],
picks: [{ teamId: "team-1", seasonId: "season-1" }],
seasons: [{ ...SEASON_ROW, pointsFor1st: 0, pointsFor2nd: 0 }], // all zeros → delta=0
});
// With all-zero scoring rules, positions map to 0 points → delta = 0 → skip
await recordMatchScoreEvents({ ...BASE_EVENT, oldFloor: 1, newFloor: 2 }, db);
expect(chain.insert).not.toHaveBeenCalled();
});
it("records score event with correct teamId, seasonId, and matchId", async () => {
const { db, chain } = makeDb({
sportsSeason: { sport: { name: "Darts" } },
seasonSports: [{ seasonId: "season-1" }],
picks: [{ teamId: "team-1", seasonId: "season-1" }],
seasons: [SEASON_ROW],
});
await recordMatchScoreEvents(BASE_EVENT, db);
expect(chain.values).toHaveBeenCalledWith(
expect.objectContaining({
teamId: "team-1",
seasonId: "season-1",
matchId: "match-1",
sportName: "Darts",
participantIds: ["p-1"],
})
);
});
it("uses sport name from sportsSeasons query", async () => {
const { db, chain } = makeDb({
sportsSeason: { sport: { name: "Snooker" } },
seasonSports: [{ seasonId: "season-1" }],
picks: [{ teamId: "team-1", seasonId: "season-1" }],
seasons: [SEASON_ROW],
});
await recordMatchScoreEvents(BASE_EVENT, db);
expect(chain.values).toHaveBeenCalledWith(
expect.objectContaining({ sportName: "Snooker" })
);
});
it("handles null sportsSeason gracefully (sportName = null)", async () => {
const { db, chain } = makeDb({
sportsSeason: null,
seasonSports: [{ seasonId: "season-1" }],
picks: [{ teamId: "team-1", seasonId: "season-1" }],
seasons: [SEASON_ROW],
});
await recordMatchScoreEvents(BASE_EVENT, db);
expect(chain.values).toHaveBeenCalledWith(
expect.objectContaining({ sportName: null })
);
});
});
describe("getRecentTeamScoreEvents", () => {
beforeEach(() => vi.clearAllMocks());
it("returns empty array when no rows found", async () => {
const { db } = makeDb({ scoreEventRows: [] });
const result = await getRecentTeamScoreEvents("season-1", 10, db);
expect(result).toEqual([]);
});
it("does not query participants when rows array is empty", async () => {
const { db } = makeDb({ scoreEventRows: [] });
await getRecentTeamScoreEvents("season-1", 10, db);
expect(db.query.seasonParticipants.findMany).not.toHaveBeenCalled();
});
it("returns mapped entries with resolved participant names", async () => {
const occurredAt = new Date("2024-01-15");
const { db } = makeDb({
scoreEventRows: [
{
id: "tse-1",
teamId: "team-1",
scoringEventId: "event-1",
scoringEventName: "Semifinals",
sportName: "Darts",
pointsDelta: "75",
occurredAt,
participantIds: ["p-1", "p-2"],
team: { id: "team-1", name: "Team Alpha" },
},
],
participantRows: [
{ id: "p-1", name: "Luke Littler" },
{ id: "p-2", name: "Michael van Gerwen" },
],
});
const result = await getRecentTeamScoreEvents("season-1", 10, db);
expect(result).toHaveLength(1);
expect(result[0]).toMatchObject({
id: "tse-1",
teamId: "team-1",
teamName: "Team Alpha",
scoringEventId: "event-1",
scoringEventName: "Semifinals",
sportName: "Darts",
pointsDelta: "75",
occurredAt,
});
expect(result[0].participants).toEqual([
{ id: "p-1", name: "Luke Littler" },
{ id: "p-2", name: "Michael van Gerwen" },
]);
});
it("omits participants whose names cannot be resolved", async () => {
const { db } = makeDb({
scoreEventRows: [
{
id: "tse-1",
teamId: "team-1",
scoringEventId: "event-1",
scoringEventName: null,
sportName: null,
pointsDelta: "50",
occurredAt: new Date(),
participantIds: ["p-1", "p-missing"],
team: { id: "team-1", name: "Team Beta" },
},
],
participantRows: [{ id: "p-1", name: "Known Player" }],
});
const result = await getRecentTeamScoreEvents("season-1", 10, db);
expect(result[0].participants).toEqual([{ id: "p-1", name: "Known Player" }]);
});
it("handles null participantIds gracefully", async () => {
const { db } = makeDb({
scoreEventRows: [
{
id: "tse-1",
teamId: "team-1",
scoringEventId: "event-1",
scoringEventName: null,
sportName: null,
pointsDelta: "10",
occurredAt: new Date(),
participantIds: null as any,
team: { id: "team-1", name: "Team Gamma" },
},
],
participantRows: [],
});
const result = await getRecentTeamScoreEvents("season-1", 10, db);
expect(result[0].participants).toEqual([]);
});
});