Adds CRUD modules for the canonical tables created in commit 775b905.
Each module mirrors existing app/models conventions (database() from
~/database/context, schema from ~/database/schema, mock-based tests).
Key implementation notes:
- participant.ts exports use "Canonical" prefix (CanonicalParticipant,
createCanonicalParticipant, etc.) to avoid collision with existing
season-participant.ts exports
- All four models include comprehensive unit tests following the
audit-log.test.ts pattern
- Tests use mocked db responses (no real database access)
- Upsert functions use onConflictDoUpdate for appropriate unique constraints
Part of Phase 1b of canonical tournament layer migration.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
100 lines
2.4 KiB
TypeScript
100 lines
2.4 KiB
TypeScript
import { describe, it, expect, vi, beforeEach } from "vitest";
|
|
|
|
vi.mock("~/database/context", () => ({
|
|
database: vi.fn(),
|
|
}));
|
|
|
|
import {
|
|
upsertCanonicalSurfaceElo,
|
|
getCanonicalSurfaceElo,
|
|
} from "../canonical-surface-elo";
|
|
import { database } from "~/database/context";
|
|
|
|
const PARTICIPANT_ID = "participant-1";
|
|
|
|
const SAMPLE_ELO = {
|
|
id: "elo-1",
|
|
participantId: PARTICIPANT_ID,
|
|
worldRanking: 1,
|
|
eloHard: 2400,
|
|
eloClay: 2350,
|
|
eloGrass: 2300,
|
|
updatedAt: new Date("2026-01-28T00:00:00Z"),
|
|
};
|
|
|
|
function makeUpsertDb(returnValue: object) {
|
|
return {
|
|
insert: vi.fn().mockReturnValue({
|
|
values: vi.fn().mockReturnValue({
|
|
onConflictDoUpdate: vi.fn().mockReturnValue({
|
|
returning: vi.fn().mockResolvedValue([returnValue]),
|
|
}),
|
|
}),
|
|
}),
|
|
};
|
|
}
|
|
|
|
function makeSelectDb(rows: object[]) {
|
|
return {
|
|
select: vi.fn().mockReturnValue({
|
|
from: vi.fn().mockReturnValue({
|
|
where: vi.fn().mockReturnValue({
|
|
limit: vi.fn().mockResolvedValue(rows),
|
|
}),
|
|
}),
|
|
}),
|
|
};
|
|
}
|
|
|
|
beforeEach(() => {
|
|
vi.clearAllMocks();
|
|
});
|
|
|
|
describe("upsertCanonicalSurfaceElo", () => {
|
|
it("inserts or updates surface elo data", async () => {
|
|
vi.mocked(database).mockReturnValue(makeUpsertDb(SAMPLE_ELO) as never);
|
|
|
|
const result = await upsertCanonicalSurfaceElo({
|
|
participantId: PARTICIPANT_ID,
|
|
worldRanking: 1,
|
|
eloHard: 2400,
|
|
eloClay: 2350,
|
|
eloGrass: 2300,
|
|
});
|
|
|
|
expect(result).toEqual(SAMPLE_ELO);
|
|
});
|
|
|
|
it("uses onConflictDoUpdate on participantId", async () => {
|
|
const mockDb = makeUpsertDb(SAMPLE_ELO);
|
|
vi.mocked(database).mockReturnValue(mockDb as never);
|
|
|
|
await upsertCanonicalSurfaceElo({
|
|
participantId: PARTICIPANT_ID,
|
|
worldRanking: 1,
|
|
});
|
|
|
|
const onConflictCall = (mockDb.insert as ReturnType<typeof vi.fn>)
|
|
.mock.results[0].value.values.mock.results[0].value.onConflictDoUpdate;
|
|
|
|
expect(onConflictCall).toHaveBeenCalled();
|
|
});
|
|
});
|
|
|
|
describe("getCanonicalSurfaceElo", () => {
|
|
it("returns elo data when found", async () => {
|
|
vi.mocked(database).mockReturnValue(makeSelectDb([SAMPLE_ELO]) as never);
|
|
|
|
const result = await getCanonicalSurfaceElo(PARTICIPANT_ID);
|
|
|
|
expect(result).toEqual(SAMPLE_ELO);
|
|
});
|
|
|
|
it("returns null when not found", async () => {
|
|
vi.mocked(database).mockReturnValue(makeSelectDb([]) as never);
|
|
|
|
const result = await getCanonicalSurfaceElo("nonexistent");
|
|
|
|
expect(result).toBeNull();
|
|
});
|
|
});
|