brackt/app/models/__tests__/populate-bracket-from-draw.test.ts

114 lines
3.6 KiB
TypeScript
Raw Normal View History

import { describe, it, expect, vi, beforeEach } from "vitest";
// populateBracketFromDraw upserts a full draw into playoff_matches and reports
// which losers' matches *transitioned to complete on this run* — the signal used
// to announce knockouts once, idempotently across re-syncs.
const existingRows: Array<Record<string, unknown>> = [];
const mockDb = {
query: {
playoffMatches: {
findMany: vi.fn(async () => existingRows),
},
},
update: vi.fn(() => ({
set: vi.fn(() => ({ where: vi.fn().mockResolvedValue(undefined) })),
})),
insert: vi.fn(() => ({
values: vi.fn(() => ({ returning: vi.fn().mockResolvedValue([]) })),
})),
};
vi.mock("~/database/context", () => ({ database: () => mockDb }));
import { populateBracketFromDraw, type ResolvedDrawMatch } from "../playoff-match";
const EVENT_ID = "ev-1";
function match(overrides: Partial<ResolvedDrawMatch> = {}): ResolvedDrawMatch {
return {
externalMatchId: "m-1",
round: "Round of 64",
matchNumber: 1,
participant1Id: "winner",
participant2Id: "loser",
winnerId: "winner",
loserId: "loser",
isScoring: false,
...overrides,
};
}
beforeEach(() => {
vi.clearAllMocks();
existingRows.length = 0;
});
describe("populateBracketFromDraw newlyDecidedLoserIds", () => {
it("reports the loser of a brand-new completed match", async () => {
const { newlyDecidedLoserIds } = await populateBracketFromDraw(EVENT_ID, [
match({ externalMatchId: "m-1", loserId: "mensik" }),
]);
expect(newlyDecidedLoserIds).toEqual(["mensik"]);
});
it("reports the loser of an existing match that just reached completion", async () => {
existingRows.push({
id: "row-1",
externalMatchId: "m-1",
isComplete: false,
loserId: null,
winnerId: null,
});
const { newlyDecidedLoserIds } = await populateBracketFromDraw(EVENT_ID, [
match({ externalMatchId: "m-1", loserId: "mensik" }),
]);
expect(newlyDecidedLoserIds).toEqual(["mensik"]);
});
it("reports nothing on an idempotent re-sync of an already-complete match", async () => {
existingRows.push({
id: "row-1",
externalMatchId: "m-1",
isComplete: true,
loserId: "mensik",
winnerId: "winner",
});
const { newlyDecidedLoserIds } = await populateBracketFromDraw(EVENT_ID, [
match({ externalMatchId: "m-1", loserId: "mensik" }),
]);
expect(newlyDecidedLoserIds).toEqual([]);
});
it("ignores incomplete matches (no winner/loser yet)", async () => {
const { newlyDecidedLoserIds, completed } = await populateBracketFromDraw(EVENT_ID, [
match({ externalMatchId: "m-2", winnerId: null, loserId: null }),
]);
expect(newlyDecidedLoserIds).toEqual([]);
expect(completed).toBe(0);
});
it("collects only the newly-decided losers in a mixed batch", async () => {
existingRows.push(
{ id: "row-1", externalMatchId: "m-1", isComplete: true, loserId: "old", winnerId: "w1" },
{ id: "row-2", externalMatchId: "m-2", isComplete: false, loserId: null, winnerId: null },
);
const { newlyDecidedLoserIds } = await populateBracketFromDraw(EVENT_ID, [
match({ externalMatchId: "m-1", loserId: "old" }), // already complete → skip
match({ externalMatchId: "m-2", loserId: "freshly-out" }), // transitioned → include
match({ externalMatchId: "m-3", loserId: "brand-new-out" }), // new complete → include
match({ externalMatchId: "m-4", winnerId: null, loserId: null }), // incomplete → skip
]);
expect(newlyDecidedLoserIds.toSorted()).toEqual(["brand-new-out", "freshly-out"]);
});
});