mirror of
https://github.com/Sendouc/sendou.ink.git
synced 2026-11-13 07:12:15 -06:00
Only count saved tournaments that haven't started towards the saved limit
This commit is contained in:
@@ -2,6 +2,7 @@ import { db } from "~/db/sql";
|
||||
import { actorId } from "~/features/auth/core/user.server";
|
||||
import type { ShowcaseCalendarEvent } from "~/features/calendar/calendar-types";
|
||||
import * as ShowcaseTournaments from "~/features/front-page/core/ShowcaseTournaments.server";
|
||||
import { databaseTimestampNow } from "~/utils/dates";
|
||||
|
||||
export function saveOwn(tournamentId: number) {
|
||||
return db
|
||||
@@ -58,11 +59,18 @@ export async function isSaved({
|
||||
return Boolean(row);
|
||||
}
|
||||
|
||||
export async function countByUserId(userId: number): Promise<number> {
|
||||
/** Counts the saved tournaments of the user that haven't started yet. */
|
||||
export async function countUpcomingByUserId(userId: number): Promise<number> {
|
||||
const result = await db
|
||||
.selectFrom("SavedCalendarEvent")
|
||||
.select((eb) => eb.fn.countAll<number>().as("count"))
|
||||
.where("userId", "=", userId)
|
||||
.where("SavedCalendarEvent.userId", "=", userId)
|
||||
.where("SavedCalendarEvent.calendarEventId", "in", (eb) =>
|
||||
eb
|
||||
.selectFrom("CalendarEventDate")
|
||||
.select("CalendarEventDate.eventId")
|
||||
.where("CalendarEventDate.startsAt", ">", databaseTimestampNow()),
|
||||
)
|
||||
.executeTakeFirstOrThrow();
|
||||
|
||||
return result.count;
|
||||
|
||||
83
app/features/tournament/actions/to.$id.info.server.test.ts
Normal file
83
app/features/tournament/actions/to.$id.info.server.test.ts
Normal file
@@ -0,0 +1,83 @@
|
||||
import { addDays, subDays } from "date-fns";
|
||||
import { beforeEach, describe, expect, test } from "vitest";
|
||||
import * as SavedCalendarEventFactory from "~/db/seed/factories/SavedCalendarEventFactory";
|
||||
import * as TournamentFactory from "~/db/seed/factories/TournamentFactory";
|
||||
import * as UserFactory from "~/db/seed/factories/UserFactory";
|
||||
import { dateToDatabaseTimestamp } from "~/utils/dates";
|
||||
import { wrappedAction } from "~/utils/Test";
|
||||
import * as SavedCalendarEventRepository from "../SavedCalendarEventRepository.server";
|
||||
import { TOURNAMENT } from "../tournament-constants";
|
||||
import type { saveTournamentSchema } from "../tournament-schemas";
|
||||
import { action as infoAction } from "./to.$id.info.server";
|
||||
|
||||
const info = wrappedAction<typeof saveTournamentSchema>({ action: infoAction });
|
||||
|
||||
const users = UserFactory.pool();
|
||||
const saverId = () => users.id(1);
|
||||
|
||||
describe("SAVE_TOURNAMENT", () => {
|
||||
beforeEach(async () => {
|
||||
await users.create(1);
|
||||
});
|
||||
|
||||
test("tournaments saved in the past don't count towards the saved limit", async () => {
|
||||
for (let i = 0; i < TOURNAMENT.MAX_SAVED_COUNT; i++) {
|
||||
await saveTournamentStartingAt(subDays(new Date(), 30 + i));
|
||||
}
|
||||
const upcoming = await createTournamentStartingAt(addDays(new Date(), 7));
|
||||
|
||||
const response = await save(upcoming.id);
|
||||
|
||||
expect(errorToastOf(response)).toBeNull();
|
||||
expect(await isSaved(upcoming.id)).toBe(true);
|
||||
});
|
||||
|
||||
test("rejects saving once the limit of upcoming saved tournaments is reached", async () => {
|
||||
for (let i = 0; i < TOURNAMENT.MAX_SAVED_COUNT; i++) {
|
||||
await saveTournamentStartingAt(addDays(new Date(), 1 + i));
|
||||
}
|
||||
const upcoming = await createTournamentStartingAt(addDays(new Date(), 30));
|
||||
|
||||
const response = await save(upcoming.id);
|
||||
|
||||
expect(errorToastOf(response)).toBe("Maximum saved tournaments reached");
|
||||
expect(await isSaved(upcoming.id)).toBe(false);
|
||||
});
|
||||
});
|
||||
|
||||
function createTournamentStartingAt(startTime: Date) {
|
||||
return TournamentFactory.create({
|
||||
authorId: saverId(),
|
||||
startTimes: [dateToDatabaseTimestamp(startTime)],
|
||||
});
|
||||
}
|
||||
|
||||
async function saveTournamentStartingAt(startTime: Date) {
|
||||
const tournament = await createTournamentStartingAt(startTime);
|
||||
await SavedCalendarEventFactory.create({
|
||||
userId: saverId(),
|
||||
tournamentId: tournament.id,
|
||||
});
|
||||
}
|
||||
|
||||
function save(tournamentId: number) {
|
||||
return info(
|
||||
{ _action: "SAVE_TOURNAMENT" },
|
||||
{ user: saverId(), params: { id: String(tournamentId) } },
|
||||
);
|
||||
}
|
||||
|
||||
function isSaved(tournamentId: number) {
|
||||
return SavedCalendarEventRepository.isSaved({
|
||||
userId: saverId(),
|
||||
tournamentId,
|
||||
});
|
||||
}
|
||||
|
||||
function errorToastOf(response: Response | null | undefined) {
|
||||
if (!response) return null;
|
||||
|
||||
return new URLSearchParams(
|
||||
response.headers.get("location")?.split("?")[1],
|
||||
).get("__error");
|
||||
}
|
||||
@@ -14,9 +14,10 @@ export const action = defineAction(
|
||||
|
||||
switch (body._action) {
|
||||
case "SAVE_TOURNAMENT": {
|
||||
const count = await SavedCalendarEventRepository.countByUserId(user.id);
|
||||
const upcomingCount =
|
||||
await SavedCalendarEventRepository.countUpcomingByUserId(user.id);
|
||||
errorToastIfFalsy(
|
||||
count < TOURNAMENT.MAX_SAVED_COUNT,
|
||||
upcomingCount < TOURNAMENT.MAX_SAVED_COUNT,
|
||||
"Maximum saved tournaments reached",
|
||||
);
|
||||
|
||||
|
||||
5
changelog/2026-11-08-saved-tournaments-limit.md
Normal file
5
changelog/2026-11-08-saved-tournaments-limit.md
Normal file
@@ -0,0 +1,5 @@
|
||||
---
|
||||
navItem: medal
|
||||
type: bug
|
||||
---
|
||||
Fixed saving tournaments failing with "Maximum saved tournaments reached" once 20 of your saved tournaments had already happened
|
||||
@@ -2013,8 +2013,10 @@ export function buildCases(fx: Fixtures): {
|
||||
tournamentId,
|
||||
}),
|
||||
);
|
||||
add("SavedCalendarEventRepository.countByUserId", fx.heavyUser, (user) =>
|
||||
SavedCalendarEventRepository.countByUserId(user.id),
|
||||
add(
|
||||
"SavedCalendarEventRepository.countUpcomingByUserId",
|
||||
fx.heavyUser,
|
||||
(user) => SavedCalendarEventRepository.countUpcomingByUserId(user.id),
|
||||
);
|
||||
add(
|
||||
"SavedCalendarEventRepository.findAllUpcomingByUserId",
|
||||
|
||||
Reference in New Issue
Block a user