Fix tournament check-in notification time boundary
Some checks are pending
E2E Tests / e2e (push) Waiting to run
Tests and checks on push / run-checks-and-tests (push) Waiting to run
Updates translation progress / update-translation-progress-issue (push) Waiting to run

Change query to exclude tournaments starting exactly at the current time, preventing duplicate notifications for events that have
already started.
This commit is contained in:
Kalle 2025-10-18 21:06:43 +03:00
parent 64427df511
commit 90fd7a22a7
2 changed files with 177 additions and 1 deletions

View File

@ -553,7 +553,7 @@ export function findAllBetweenTwoTimestamps({
.select(["Tournament.id as tournamentId"])
.where(
"CalendarEventDate.startTime",
">=",
">",
dateToDatabaseTimestamp(startTime),
)
.where(

View File

@ -0,0 +1,176 @@
import { add } from "date-fns";
import { afterEach, beforeEach, describe, expect, test, vi } from "vitest";
import * as CalendarRepository from "~/features/calendar/CalendarRepository.server";
import { clearAllTournamentDataCache } from "~/features/tournament-bracket/core/Tournament.server";
import { dateToDatabaseTimestamp } from "~/utils/dates";
import { dbInsertUsers, dbReset } from "~/utils/Test";
import { NotifyCheckInStartRoutine } from "./notifyCheckInStart";
const { mockNotify } = vi.hoisted(() => ({
mockNotify: vi.fn(),
}));
vi.mock("~/features/notifications/core/notify.server", () => ({
notify: mockNotify,
}));
async function createTestTournament({
name,
startTime,
authorId = 1,
discordInviteCode = "test-discord",
}: {
name: string;
startTime: Date;
authorId?: number;
discordInviteCode?: string;
}) {
return CalendarRepository.create({
isFullTournament: true,
authorId,
badges: [],
bracketUrl: "https://example.com/bracket",
description: null,
discordInviteCode,
deadlines: "DEFAULT",
name,
organizationId: null,
rules: null,
startTimes: [dateToDatabaseTimestamp(startTime)],
tags: null,
bracketProgression: [
{
name: "Bracket",
type: "single_elimination",
requiresCheckIn: false,
settings: {
thirdPlaceMatch: false,
},
},
],
mapPickingStyle: "TO",
mapPoolMaps: ([1, 2, 3, 4, 5] as const).map((id) => ({
mode: "SZ",
stageId: id,
})),
});
}
describe("NotifyCheckInStartRoutine", () => {
beforeEach(async () => {
vi.useFakeTimers();
vi.setSystemTime(new Date("2025-01-15T12:00:00Z"));
dbReset();
clearAllTournamentDataCache();
await dbInsertUsers(5);
mockNotify.mockClear();
});
afterEach(() => {
vi.useRealTimers();
});
test("sends notification for tournament starting exactly 1 hour from now", async () => {
const now = new Date();
const oneHourFromNow = add(now, { hours: 1 });
await createTestTournament({
name: "Tournament 1 Hour Away",
startTime: oneHourFromNow,
});
await NotifyCheckInStartRoutine.run();
expect(mockNotify).toHaveBeenCalledTimes(1);
expect(mockNotify).toHaveBeenCalledWith(
expect.objectContaining({
notification: expect.objectContaining({
type: "TO_CHECK_IN_OPENED",
meta: expect.objectContaining({
tournamentName: "Tournament 1 Hour Away",
}),
}),
}),
);
});
test("does NOT send notification for tournament starting exactly now", async () => {
const now = new Date();
await createTestTournament({
name: "Tournament Starting Now",
startTime: now,
});
await NotifyCheckInStartRoutine.run();
expect(mockNotify).not.toHaveBeenCalled();
});
test("sends notification for tournament starting 30 minutes from now", async () => {
const now = new Date();
const thirtyMinutesFromNow = add(now, { minutes: 30 });
await createTestTournament({
name: "Tournament 30 Minutes Away",
startTime: thirtyMinutesFromNow,
});
await NotifyCheckInStartRoutine.run();
expect(mockNotify).toHaveBeenCalledTimes(1);
expect(mockNotify).toHaveBeenCalledWith(
expect.objectContaining({
notification: expect.objectContaining({
type: "TO_CHECK_IN_OPENED",
meta: expect.objectContaining({
tournamentName: "Tournament 30 Minutes Away",
}),
}),
}),
);
});
test("does NOT send notification for tournament starting more than 1 hour from now", async () => {
const now = new Date();
const oneAndHalfHoursFromNow = add(now, { hours: 1, minutes: 30 });
await createTestTournament({
name: "Tournament 1.5 Hours Away",
startTime: oneAndHalfHoursFromNow,
});
await NotifyCheckInStartRoutine.run();
expect(mockNotify).not.toHaveBeenCalled();
});
test("sends notifications for multiple tournaments in the time window", async () => {
const now = new Date();
const thirtyMinutesFromNow = add(now, { minutes: 30 });
const fortyFiveMinutesFromNow = add(now, { minutes: 45 });
await createTestTournament({
name: "Tournament A",
startTime: thirtyMinutesFromNow,
discordInviteCode: "test-discord-1",
});
await createTestTournament({
name: "Tournament B",
startTime: fortyFiveMinutesFromNow,
authorId: 2,
discordInviteCode: "test-discord-2",
});
await NotifyCheckInStartRoutine.run();
expect(mockNotify).toHaveBeenCalledTimes(2);
const tournamentNames = mockNotify.mock.calls.map(
(call) => call[0].notification.meta.tournamentName,
);
expect(tournamentNames).toContain("Tournament A");
expect(tournamentNames).toContain("Tournament B");
});
});