2026-03-18 22:40:19 -07:00
|
|
|
import { describe, it, expect, vi, beforeEach } from "vitest";
|
|
|
|
|
|
|
|
|
|
vi.mock("~/database/context", () => ({
|
|
|
|
|
database: vi.fn(),
|
|
|
|
|
}));
|
|
|
|
|
|
2026-04-05 19:09:52 -07:00
|
|
|
// Mock the schema so the schema module doesn't execute drizzle builder calls,
|
|
|
|
|
// following the same pattern as scoring-event-dashboard.test.ts.
|
|
|
|
|
vi.mock("~/database/schema", () => ({
|
|
|
|
|
sportsSeasons: {
|
|
|
|
|
id: "ss.id",
|
|
|
|
|
sportId: "ss.sport_id",
|
|
|
|
|
name: "ss.name",
|
|
|
|
|
year: "ss.year",
|
|
|
|
|
draftOn: "ss.draft_on",
|
|
|
|
|
draftOff: "ss.draft_off",
|
|
|
|
|
},
|
|
|
|
|
}));
|
|
|
|
|
|
|
|
|
|
vi.mock("drizzle-orm", () => ({
|
|
|
|
|
eq: (col: unknown, val: unknown) => ({ type: "eq", col, val }),
|
|
|
|
|
sql: (strings: TemplateStringsArray, ...values: unknown[]) => ({ type: "sql", strings, values }),
|
|
|
|
|
lte: (col: unknown, val: unknown) => ({ type: "lte", col, val }),
|
|
|
|
|
gte: (col: unknown, val: unknown) => ({ type: "gte", col, val }),
|
|
|
|
|
and: (...args: unknown[]) => ({ type: "and", args }),
|
|
|
|
|
desc: (col: unknown) => ({ type: "desc", col }),
|
|
|
|
|
asc: (col: unknown) => ({ type: "asc", col }),
|
|
|
|
|
}));
|
|
|
|
|
|
2026-07-02 00:44:23 +00:00
|
|
|
import {
|
|
|
|
|
findAllSportsSeasons,
|
|
|
|
|
findDraftableSportsSeasons,
|
|
|
|
|
findDraftScheduleForHorizon,
|
|
|
|
|
} from "../sports-season";
|
2026-03-18 22:40:19 -07:00
|
|
|
import { database } from "~/database/context";
|
|
|
|
|
|
2026-04-05 19:09:52 -07:00
|
|
|
const today = new Date().toISOString().slice(0, 10);
|
|
|
|
|
const future = "2099-12-31";
|
|
|
|
|
const pastStart = "2020-01-01";
|
|
|
|
|
const pastEnd = "2020-12-31";
|
|
|
|
|
|
2026-03-18 22:40:19 -07:00
|
|
|
const mockSeasons = [
|
2026-04-28 22:24:13 -07:00
|
|
|
{ id: "season-1", name: "2025 NBA Playoffs", draftOn: today, draftOff: future, year: 2025, status: "upcoming", sport: { name: "NBA" }, participants: [] },
|
|
|
|
|
{ id: "season-2", name: "2025 F1 Season", draftOn: today, draftOff: future, year: 2025, status: "upcoming", sport: { name: "F1" }, participants: [] },
|
|
|
|
|
{ id: "season-3", name: "2024 Old Golf", draftOn: pastStart, draftOff: pastEnd, year: 2024, status: "completed", sport: { name: "Golf" }, participants: [] },
|
2026-03-18 22:40:19 -07:00
|
|
|
];
|
|
|
|
|
|
2026-04-28 22:24:13 -07:00
|
|
|
function makeMockDb(seasons: Record<string, unknown>[]) {
|
2026-03-18 22:40:19 -07:00
|
|
|
return {
|
|
|
|
|
query: {
|
|
|
|
|
sportsSeasons: {
|
|
|
|
|
findMany: vi.fn().mockResolvedValue(seasons),
|
|
|
|
|
},
|
|
|
|
|
},
|
|
|
|
|
};
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
beforeEach(() => {
|
|
|
|
|
vi.clearAllMocks();
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
describe("findAllSportsSeasons", () => {
|
2026-04-05 19:09:52 -07:00
|
|
|
it("returns all sport seasons regardless of draft window", async () => {
|
2026-03-18 22:40:19 -07:00
|
|
|
vi.mocked(database).mockReturnValue(makeMockDb(mockSeasons) as never);
|
|
|
|
|
const result = await findAllSportsSeasons();
|
|
|
|
|
expect(result).toHaveLength(3);
|
|
|
|
|
});
|
2026-04-09 10:07:18 -04:00
|
|
|
|
|
|
|
|
it("sorts active before upcoming before completed", async () => {
|
|
|
|
|
const seasons = [
|
|
|
|
|
{ id: "s1", name: "Completed", draftOn: pastStart, draftOff: pastEnd, year: 2024, status: "completed", sport: { name: "Sport" } },
|
|
|
|
|
{ id: "s2", name: "Active", draftOn: today, draftOff: future, year: 2025, status: "active", sport: { name: "Sport" } },
|
|
|
|
|
{ id: "s3", name: "Upcoming", draftOn: today, draftOff: future, year: 2026, status: "upcoming", sport: { name: "Sport" } },
|
|
|
|
|
];
|
|
|
|
|
vi.mocked(database).mockReturnValue(makeMockDb(seasons) as never);
|
|
|
|
|
const result = await findAllSportsSeasons();
|
|
|
|
|
expect(result.map((s) => s.status)).toEqual(["active", "upcoming", "completed"]);
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
it("sorts by sport name alphabetically within the same status", async () => {
|
|
|
|
|
const seasons = [
|
|
|
|
|
{ id: "s1", name: "Zebra Season", draftOn: today, draftOff: future, year: 2025, status: "active", sport: { name: "Zebra" } },
|
|
|
|
|
{ id: "s2", name: "Apple Season", draftOn: today, draftOff: future, year: 2025, status: "active", sport: { name: "Apple" } },
|
|
|
|
|
{ id: "s3", name: "Mango Season", draftOn: today, draftOff: future, year: 2025, status: "active", sport: { name: "Mango" } },
|
|
|
|
|
];
|
|
|
|
|
vi.mocked(database).mockReturnValue(makeMockDb(seasons) as never);
|
|
|
|
|
const result = await findAllSportsSeasons();
|
|
|
|
|
expect(result.map((s) => s.sport.name)).toEqual(["Apple", "Mango", "Zebra"]);
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
it("sorts by year ascending within the same status and sport name", async () => {
|
|
|
|
|
const seasons = [
|
|
|
|
|
{ id: "s1", name: "NFL 2026", draftOn: today, draftOff: future, year: 2026, status: "upcoming", sport: { name: "NFL" } },
|
|
|
|
|
{ id: "s2", name: "NFL 2024", draftOn: today, draftOff: future, year: 2024, status: "upcoming", sport: { name: "NFL" } },
|
|
|
|
|
{ id: "s3", name: "NFL 2025", draftOn: today, draftOff: future, year: 2025, status: "upcoming", sport: { name: "NFL" } },
|
|
|
|
|
];
|
|
|
|
|
vi.mocked(database).mockReturnValue(makeMockDb(seasons) as never);
|
|
|
|
|
const result = await findAllSportsSeasons();
|
|
|
|
|
expect(result.map((s) => s.year)).toEqual([2024, 2025, 2026]);
|
|
|
|
|
});
|
2026-03-18 22:40:19 -07:00
|
|
|
});
|
|
|
|
|
|
|
|
|
|
describe("findDraftableSportsSeasons", () => {
|
2026-04-05 19:09:52 -07:00
|
|
|
it("returns only currently-draftable seasons", async () => {
|
|
|
|
|
const draftableOnly = mockSeasons.filter((s) => s.draftOn <= today && today <= s.draftOff);
|
2026-03-18 22:40:19 -07:00
|
|
|
const mockDb = makeMockDb(draftableOnly);
|
|
|
|
|
vi.mocked(database).mockReturnValue(mockDb as never);
|
|
|
|
|
|
|
|
|
|
const result = await findDraftableSportsSeasons();
|
|
|
|
|
expect(result).toHaveLength(2);
|
2026-04-05 19:09:52 -07:00
|
|
|
expect(result.every((s) => s.draftOn <= today && today <= s.draftOff)).toBe(true);
|
2026-03-18 22:40:19 -07:00
|
|
|
});
|
|
|
|
|
|
2026-04-05 19:09:52 -07:00
|
|
|
it("returns empty array when no seasons fall within a draft window", async () => {
|
2026-03-18 22:40:19 -07:00
|
|
|
vi.mocked(database).mockReturnValue(makeMockDb([]) as never);
|
|
|
|
|
const result = await findDraftableSportsSeasons();
|
|
|
|
|
expect(result).toHaveLength(0);
|
|
|
|
|
});
|
|
|
|
|
|
2026-04-05 19:09:52 -07:00
|
|
|
it("calls findMany with a where clause", async () => {
|
2026-03-18 22:40:19 -07:00
|
|
|
const mockDb = makeMockDb([]);
|
|
|
|
|
vi.mocked(database).mockReturnValue(mockDb as never);
|
|
|
|
|
|
|
|
|
|
await findDraftableSportsSeasons();
|
|
|
|
|
expect(mockDb.query.sportsSeasons.findMany).toHaveBeenCalledWith(
|
|
|
|
|
expect.objectContaining({
|
|
|
|
|
where: expect.anything(),
|
|
|
|
|
})
|
|
|
|
|
);
|
|
|
|
|
});
|
|
|
|
|
});
|
2026-07-02 00:44:23 +00:00
|
|
|
|
|
|
|
|
describe("findDraftScheduleForHorizon", () => {
|
|
|
|
|
const horizonWindows = [
|
|
|
|
|
{
|
|
|
|
|
id: "w1",
|
|
|
|
|
name: "2026 NBA Playoffs",
|
|
|
|
|
year: 2026,
|
|
|
|
|
status: "upcoming",
|
|
|
|
|
draftOn: today,
|
|
|
|
|
draftOff: future,
|
|
|
|
|
sport: { id: "nba", name: "NBA", slug: "nba", iconUrl: null },
|
|
|
|
|
},
|
|
|
|
|
{
|
|
|
|
|
id: "w2",
|
|
|
|
|
name: "2026 F1 Season",
|
|
|
|
|
year: 2026,
|
|
|
|
|
status: "active",
|
|
|
|
|
draftOn: today,
|
|
|
|
|
draftOff: future,
|
|
|
|
|
sport: { id: "f1", name: "F1", slug: "f1", iconUrl: null },
|
|
|
|
|
},
|
|
|
|
|
];
|
|
|
|
|
|
|
|
|
|
it("maps rows to windows carrying their sport info", async () => {
|
|
|
|
|
vi.mocked(database).mockReturnValue(makeMockDb(horizonWindows) as never);
|
|
|
|
|
const result = await findDraftScheduleForHorizon(6);
|
|
|
|
|
expect(result).toHaveLength(2);
|
|
|
|
|
expect(result[0]).toMatchObject({
|
|
|
|
|
id: "w1",
|
|
|
|
|
draftOn: today,
|
|
|
|
|
draftOff: future,
|
|
|
|
|
sport: { id: "nba", name: "NBA" },
|
|
|
|
|
});
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
it("returns an empty array when no windows overlap the horizon", async () => {
|
|
|
|
|
vi.mocked(database).mockReturnValue(makeMockDb([]) as never);
|
|
|
|
|
const result = await findDraftScheduleForHorizon(12);
|
|
|
|
|
expect(result).toHaveLength(0);
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
it("queries with a where clause, ordering, and the sport relation", async () => {
|
|
|
|
|
const mockDb = makeMockDb([]);
|
|
|
|
|
vi.mocked(database).mockReturnValue(mockDb as never);
|
|
|
|
|
|
|
|
|
|
await findDraftScheduleForHorizon(6);
|
|
|
|
|
expect(mockDb.query.sportsSeasons.findMany).toHaveBeenCalledWith(
|
|
|
|
|
expect.objectContaining({
|
|
|
|
|
where: expect.anything(),
|
|
|
|
|
orderBy: expect.anything(),
|
|
|
|
|
with: expect.anything(),
|
|
|
|
|
})
|
|
|
|
|
);
|
|
|
|
|
});
|
|
|
|
|
});
|