brackt/app/models/__tests__/season-match.test.ts
Claude 0dca36dd5c
feat: add generic season_matches + match_sub_games tables with CS2 admin UI (Phase 1)
Adds sport-agnostic match data infrastructure:
- matchStatusEnum + season_matches + match_sub_games tables in schema
- externalSeasonId column on sports_seasons for API linkage
- Drizzle migration 0120_tidy_invaders.sql
- season-match model (upsert w/ onConflictDoUpdate, queries by event/season/stage)
- CS2 admin: Swiss rounds read-only section in cs2-setup + new manual swiss-matches route
- 7 unit tests for model (upsert conflict, null stage/round for non-CS2 sports, bulk, sub-games)
- Plan doc committed at docs/plans/match-sync-display.md for cross-session reference

https://claude.ai/code/session_01WUUM7uWzFoSkGcZRhnEKG6
2026-06-12 20:13:27 +00:00

234 lines
7.1 KiB
TypeScript

import { describe, it, expect, vi, beforeEach } from "vitest";
vi.mock("~/database/context", () => ({
database: vi.fn(),
}));
import {
upsertSeasonMatch,
upsertSeasonMatchBulk,
upsertMatchSubGame,
} from "../season-match";
import { database } from "~/database/context";
const SPORTS_SEASON_ID = "ss-1";
const SCORING_EVENT_ID = "evt-1";
const P1_ID = "p1";
const P2_ID = "p2";
const WINNER_ID = "p1";
const EXTERNAL_ID = "panda_123";
function makeUpsertDb(returnValue: object) {
const returningFn = vi.fn().mockResolvedValue([returnValue]);
const onConflictFn = vi.fn().mockReturnValue({ returning: returningFn });
const valuesFn = vi.fn().mockReturnValue({ onConflictDoUpdate: onConflictFn });
return {
insert: vi.fn().mockReturnValue({ values: valuesFn }),
_returning: returningFn,
_onConflict: onConflictFn,
_values: valuesFn,
};
}
function makeBulkUpsertDb(returnValues: object[]) {
const returningFn = vi.fn().mockResolvedValue(returnValues);
const onConflictFn = vi.fn().mockReturnValue({ returning: returningFn });
const valuesFn = vi.fn().mockReturnValue({ onConflictDoUpdate: onConflictFn });
return {
insert: vi.fn().mockReturnValue({ values: valuesFn }),
_returning: returningFn,
};
}
const SAMPLE_MATCH = {
id: "match-1",
sportsSeasonId: SPORTS_SEASON_ID,
scoringEventId: SCORING_EVENT_ID,
participant1Id: P1_ID,
participant2Id: P2_ID,
winnerId: WINNER_ID,
participant1Score: 2,
participant2Score: 1,
matchStage: 1,
matchRound: 3,
matchday: null,
isSeries: true,
status: "complete" as const,
scheduledAt: null,
startedAt: null,
completedAt: new Date("2025-01-15T14:00:00Z"),
externalMatchId: EXTERNAL_ID,
createdAt: new Date(),
updatedAt: new Date(),
};
describe("upsertSeasonMatch", () => {
beforeEach(() => {
vi.clearAllMocks();
});
it("inserts a match and returns it", async () => {
const mockDb = makeUpsertDb(SAMPLE_MATCH);
vi.mocked(database).mockReturnValue(mockDb as unknown as ReturnType<typeof database>);
const result = await upsertSeasonMatch({
sportsSeasonId: SPORTS_SEASON_ID,
scoringEventId: SCORING_EVENT_ID,
participant1Id: P1_ID,
participant2Id: P2_ID,
winnerId: WINNER_ID,
participant1Score: 2,
participant2Score: 1,
matchStage: 1,
matchRound: 3,
isSeries: true,
status: "complete",
completedAt: new Date("2025-01-15T14:00:00Z"),
externalMatchId: EXTERNAL_ID,
});
expect(result.id).toBe("match-1");
expect(result.matchStage).toBe(1);
expect(result.matchRound).toBe(3);
expect(result.status).toBe("complete");
expect(mockDb.insert).toHaveBeenCalledOnce();
expect(mockDb._values).toHaveBeenCalledOnce();
expect(mockDb._onConflict).toHaveBeenCalledOnce();
});
it("defaults isSeries to false when not provided", async () => {
const expectedMatch = { ...SAMPLE_MATCH, isSeries: false };
const mockDb = makeUpsertDb(expectedMatch);
vi.mocked(database).mockReturnValue(mockDb as unknown as ReturnType<typeof database>);
const result = await upsertSeasonMatch({
sportsSeasonId: SPORTS_SEASON_ID,
externalMatchId: "ext_1",
status: "scheduled",
});
const valuesCall = mockDb._values.mock.calls[0][0];
expect(valuesCall.isSeries).toBe(false);
expect(result.isSeries).toBe(false);
});
it("allows null matchStage and matchRound for non-CS2 sports", async () => {
const mlbMatch = { ...SAMPLE_MATCH, matchStage: null, matchRound: null, matchday: null, isSeries: false };
const mockDb = makeUpsertDb(mlbMatch);
vi.mocked(database).mockReturnValue(mockDb as unknown as ReturnType<typeof database>);
const result = await upsertSeasonMatch({
sportsSeasonId: SPORTS_SEASON_ID,
externalMatchId: "mlb_game_456",
status: "complete",
participant1Score: 5,
participant2Score: 3,
});
const valuesCall = mockDb._values.mock.calls[0][0];
expect(valuesCall.matchStage).toBeUndefined();
expect(valuesCall.matchRound).toBeUndefined();
expect(result.matchStage).toBeNull();
expect(result.matchRound).toBeNull();
});
});
describe("upsertSeasonMatchBulk", () => {
beforeEach(() => {
vi.clearAllMocks();
});
it("returns empty array for empty input", async () => {
vi.mocked(database).mockReturnValue({} as ReturnType<typeof database>);
const result = await upsertSeasonMatchBulk([]);
expect(result).toEqual([]);
});
it("bulk upserts multiple matches", async () => {
const match2 = { ...SAMPLE_MATCH, id: "match-2", matchRound: 4, externalMatchId: "panda_456" };
const mockDb = makeBulkUpsertDb([SAMPLE_MATCH, match2]);
vi.mocked(database).mockReturnValue(mockDb as unknown as ReturnType<typeof database>);
const result = await upsertSeasonMatchBulk([
{ sportsSeasonId: SPORTS_SEASON_ID, externalMatchId: EXTERNAL_ID, status: "complete", matchStage: 1, matchRound: 3 },
{ sportsSeasonId: SPORTS_SEASON_ID, externalMatchId: "panda_456", status: "complete", matchStage: 1, matchRound: 4 },
]);
expect(result).toHaveLength(2);
expect(mockDb.insert).toHaveBeenCalledOnce();
});
});
describe("upsertMatchSubGame", () => {
beforeEach(() => {
vi.clearAllMocks();
});
it("upserts a map sub-game", async () => {
const subGame = {
id: "sg-1",
seasonMatchId: "match-1",
gameNumber: 1,
gameLabel: "Mirage",
participant1Score: 16,
participant2Score: 14,
winnerId: P1_ID,
status: "complete" as const,
startedAt: null,
completedAt: new Date(),
externalGameId: "map_1",
createdAt: new Date(),
updatedAt: new Date(),
};
const mockDb = makeUpsertDb(subGame);
vi.mocked(database).mockReturnValue(mockDb as unknown as ReturnType<typeof database>);
const result = await upsertMatchSubGame({
seasonMatchId: "match-1",
gameNumber: 1,
gameLabel: "Mirage",
participant1Score: 16,
participant2Score: 14,
winnerId: P1_ID,
status: "complete",
externalGameId: "map_1",
});
expect(result.gameLabel).toBe("Mirage");
expect(result.participant1Score).toBe(16);
expect(result.gameNumber).toBe(1);
expect(mockDb.insert).toHaveBeenCalledOnce();
expect(mockDb._onConflict).toHaveBeenCalledOnce();
});
it("allows null gameLabel for non-named sub-games", async () => {
const subGame = {
id: "sg-2",
seasonMatchId: "match-1",
gameNumber: 2,
gameLabel: null,
participant1Score: 3,
participant2Score: 1,
winnerId: P1_ID,
status: "complete" as const,
startedAt: null,
completedAt: new Date(),
externalGameId: null,
createdAt: new Date(),
updatedAt: new Date(),
};
const mockDb = makeUpsertDb(subGame);
vi.mocked(database).mockReturnValue(mockDb as unknown as ReturnType<typeof database>);
const result = await upsertMatchSubGame({
seasonMatchId: "match-1",
gameNumber: 2,
participant1Score: 3,
participant2Score: 1,
status: "complete",
});
expect(result.gameLabel).toBeNull();
expect(result.gameNumber).toBe(2);
});
});