brackt/app/models/__tests__/season-participant-copy.test.ts

252 lines
9 KiB
TypeScript
Raw Normal View History

import { describe, it, expect, vi, beforeEach } from "vitest";
vi.mock("~/database/context", () => ({
database: vi.fn(),
}));
vi.mock("~/database/schema", () => ({
seasonParticipants: { id: "sp.id", sportsSeasonId: "sp.sports_season_id", name: "sp.name", shortName: "sp.short_name", externalId: "sp.external_id", participantId: "sp.participant_id" },
seasonParticipantExpectedValues: { sportsSeasonId: "pev.sports_season_id" },
}));
vi.mock("drizzle-orm", () => ({
eq: (col: unknown, val: unknown) => ({ type: "eq", col, val }),
inArray: vi.fn(),
count: vi.fn(),
and: (...args: unknown[]) => ({ type: "and", args }),
sql: (strings: TemplateStringsArray, ...values: unknown[]) => ({ type: "sql", strings, values }),
asc: (col: unknown) => ({ type: "asc", col }),
desc: (col: unknown) => ({ type: "desc", col }),
}));
import * as schema from "~/database/schema";
import { copyParticipantsFromSeason } from "../season-participant";
import { database } from "~/database/context";
const SOURCE_ID = "source-season";
const TARGET_ID = "target-season";
function makeSourceParticipant(overrides: Record<string, unknown> = {}) {
return {
id: "sp-src-1",
sportsSeasonId: SOURCE_ID,
name: "Tiger Woods",
shortName: "T. Woods",
externalId: "tiger",
participantId: "canon-1",
...overrides,
};
}
function makeSourceEv(overrides: Record<string, unknown> = {}) {
return {
id: "ev-src-1",
participantId: "sp-src-1",
sportsSeasonId: SOURCE_ID,
sourceOdds: 800,
sourceElo: null,
source: "futures",
worldRanking: 5,
...overrides,
};
}
function makeMockDb({
targetParticipants = [] as object[],
sourceParticipants = [] as object[],
sourceEvRows = [] as object[],
newParticipantRows = sourceParticipants.map((p, i) => ({
...(p as Record<string, unknown>),
id: `sp-new-${i + 1}`,
sportsSeasonId: TARGET_ID,
})),
}: {
targetParticipants?: object[];
sourceParticipants?: object[];
sourceEvRows?: object[];
newParticipantRows?: object[];
} = {}) {
const insertedRows: Record<string, unknown> = {};
const findManyCalls: { table: string; args: unknown }[] = [];
const db = {
query: {
seasonParticipants: {
findMany: vi.fn().mockImplementation((opts: unknown) => {
findManyCalls.push({ table: "seasonParticipants", args: opts });
const where = (opts as Record<string, unknown>)?.where as { val: string } | undefined;
if (where?.val === SOURCE_ID) return sourceParticipants;
if (where?.val === TARGET_ID) return targetParticipants;
return targetParticipants;
}),
},
seasonParticipantExpectedValues: {
findMany: vi.fn().mockResolvedValue(sourceEvRows),
},
},
insert: vi.fn().mockImplementation((table: object) => {
let key: string;
let returnRows: object[];
if (table === schema.seasonParticipants) {
key = "seasonParticipants";
returnRows = newParticipantRows;
} else {
key = "participantExpectedValues";
returnRows = [];
}
return {
values: vi.fn().mockImplementation((vals: unknown) => {
insertedRows[key] = vals;
if (key === "seasonParticipants") {
return { returning: vi.fn().mockResolvedValue(returnRows) };
}
return Promise.resolve(undefined);
}),
};
}),
transaction: vi.fn().mockImplementation(async (fn: (tx: typeof db) => Promise<unknown>) => fn(db)),
_insertedRows: insertedRows,
_findManyCalls: findManyCalls,
};
return db;
}
beforeEach(() => {
vi.clearAllMocks();
});
describe("copyParticipantsFromSeason", () => {
it("returns empty result when source has no participants", async () => {
const db = makeMockDb({ sourceParticipants: [] });
vi.mocked(database).mockReturnValue(db as never);
const result = await copyParticipantsFromSeason(SOURCE_ID, TARGET_ID);
expect(result).toEqual({ copied: 0, skipped: 0, evStubsCopied: 0 });
expect(db.transaction).not.toHaveBeenCalled();
});
it("skips participants already present in target (case-insensitive)", async () => {
const db = makeMockDb({
targetParticipants: [makeSourceParticipant({ id: "sp-tgt-1", sportsSeasonId: TARGET_ID })],
sourceParticipants: [makeSourceParticipant()],
});
vi.mocked(database).mockReturnValue(db as never);
const result = await copyParticipantsFromSeason(SOURCE_ID, TARGET_ID);
expect(result).toEqual({ copied: 0, skipped: 1, evStubsCopied: 0 });
expect(db.transaction).not.toHaveBeenCalled();
});
it("copies participants and returns counts", async () => {
const srcP1 = makeSourceParticipant({ name: "Tiger Woods", externalId: "tiger", participantId: "canon-1" });
const srcP2 = makeSourceParticipant({ id: "sp-src-2", name: "Rory McIlroy", externalId: "rory", participantId: "canon-2" });
const db = makeMockDb({
sourceParticipants: [srcP1, srcP2],
newParticipantRows: [
{ ...srcP1, id: "sp-new-1", sportsSeasonId: TARGET_ID },
{ ...srcP2, id: "sp-new-2", sportsSeasonId: TARGET_ID },
],
});
vi.mocked(database).mockReturnValue(db as never);
const result = await copyParticipantsFromSeason(SOURCE_ID, TARGET_ID);
expect(result.copied).toBe(2);
expect(result.skipped).toBe(0);
expect(db._insertedRows.seasonParticipants).toHaveLength(2);
const values = db._insertedRows.seasonParticipants as Array<Record<string, unknown>>;
expect(values[0]).toMatchObject({ name: "Tiger Woods", externalId: "tiger", participantId: "canon-1" });
expect(values[1]).toMatchObject({ name: "Rory McIlroy", externalId: "rory", participantId: "canon-2" });
});
it("copies EV stubs matched by externalId", async () => {
const srcP = makeSourceParticipant({ externalId: "tiger" });
const srcEv = makeSourceEv({ participantId: "sp-src-1", sourceOdds: 800 });
const db = makeMockDb({
sourceParticipants: [srcP],
sourceEvRows: [srcEv],
newParticipantRows: [{ ...srcP, id: "sp-new-1", sportsSeasonId: TARGET_ID }],
});
vi.mocked(database).mockReturnValue(db as never);
const result = await copyParticipantsFromSeason(SOURCE_ID, TARGET_ID);
expect(result.evStubsCopied).toBe(1);
const evRows = db._insertedRows.participantExpectedValues as Array<Record<string, unknown>>;
expect(evRows).toHaveLength(1);
expect(evRows[0]).toMatchObject({
participantId: "sp-new-1",
sourceOdds: 800,
source: "futures",
worldRanking: 5,
});
});
it("copies EV stubs matched by name when externalId is null", async () => {
const srcP = makeSourceParticipant({ externalId: null, name: "Tiger Woods" });
const srcEv = makeSourceEv({ participantId: "sp-src-1" });
const db = makeMockDb({
sourceParticipants: [srcP],
sourceEvRows: [srcEv],
newParticipantRows: [{ ...srcP, id: "sp-new-1", sportsSeasonId: TARGET_ID }],
});
vi.mocked(database).mockReturnValue(db as never);
const result = await copyParticipantsFromSeason(SOURCE_ID, TARGET_ID);
expect(result.evStubsCopied).toBe(1);
});
it("skips EV rows with neither sourceOdds nor sourceElo", async () => {
const srcP = makeSourceParticipant();
const srcEv = makeSourceEv({ sourceOdds: null, sourceElo: null });
const db = makeMockDb({
sourceParticipants: [srcP],
sourceEvRows: [srcEv],
newParticipantRows: [{ ...srcP, id: "sp-new-1", sportsSeasonId: TARGET_ID }],
});
vi.mocked(database).mockReturnValue(db as never);
const result = await copyParticipantsFromSeason(SOURCE_ID, TARGET_ID);
expect(result.evStubsCopied).toBe(0);
expect(db._insertedRows.participantExpectedValues).toBeUndefined();
});
it("wraps writes in a transaction", async () => {
const srcP = makeSourceParticipant();
const db = makeMockDb({
sourceParticipants: [srcP],
newParticipantRows: [{ ...srcP, id: "sp-new-1", sportsSeasonId: TARGET_ID }],
});
vi.mocked(database).mockReturnValue(db as never);
await copyParticipantsFromSeason(SOURCE_ID, TARGET_ID);
expect(db.transaction).toHaveBeenCalledOnce();
});
it("handles partial duplicates (some copied, some skipped)", async () => {
const srcP1 = makeSourceParticipant({ name: "Tiger Woods" });
const srcP2 = makeSourceParticipant({ id: "sp-src-2", name: "Rory McIlroy", participantId: "canon-2" });
const db = makeMockDb({
targetParticipants: [makeSourceParticipant({ id: "sp-tgt-1", sportsSeasonId: TARGET_ID, name: "Tiger Woods" })],
sourceParticipants: [srcP1, srcP2],
newParticipantRows: [{ ...srcP2, id: "sp-new-1", sportsSeasonId: TARGET_ID }],
});
vi.mocked(database).mockReturnValue(db as never);
const result = await copyParticipantsFromSeason(SOURCE_ID, TARGET_ID);
expect(result.copied).toBe(1);
expect(result.skipped).toBe(1);
const values = db._insertedRows.seasonParticipants as Array<Record<string, unknown>>;
expect(values).toHaveLength(1);
expect(values[0]).toMatchObject({ name: "Rory McIlroy" });
});
});