mirror of
https://github.com/Sendouc/sendou.ink.git
synced 2026-08-22 19:16:09 -05:00
Optimize tournament series leaderboard, tiebreak by placements
This commit is contained in:
@@ -401,6 +401,31 @@ export async function findResultsByEventId(eventId: number) {
|
||||
.execute();
|
||||
}
|
||||
|
||||
/**
|
||||
* Players of the podium teams of the given events, one row per player. Players reported as plain
|
||||
* text rather than linked to an account have a `null` id.
|
||||
*/
|
||||
export async function findTopThreeResultsByEventIds(eventIds: number[]) {
|
||||
if (eventIds.length === 0) return [];
|
||||
|
||||
return db
|
||||
.selectFrom("CalendarEventResultTeam")
|
||||
.innerJoin(
|
||||
"CalendarEventResultPlayer",
|
||||
"CalendarEventResultPlayer.teamId",
|
||||
"CalendarEventResultTeam.id",
|
||||
)
|
||||
.leftJoin("User", "User.id", "CalendarEventResultPlayer.userId")
|
||||
.select((eb) => [
|
||||
"CalendarEventResultTeam.id as teamId",
|
||||
"CalendarEventResultTeam.placement",
|
||||
...commonUserSelect(eb),
|
||||
])
|
||||
.where("CalendarEventResultTeam.eventId", "in", eventIds)
|
||||
.where("CalendarEventResultTeam.placement", "<=", 3)
|
||||
.execute();
|
||||
}
|
||||
|
||||
type CreateArgs = Pick<
|
||||
Tables["CalendarEvent"],
|
||||
| "name"
|
||||
|
||||
@@ -333,7 +333,6 @@ const mapEvent = <
|
||||
T extends {
|
||||
tournamentId: number | null;
|
||||
logoUrl: string;
|
||||
name: string;
|
||||
},
|
||||
>(
|
||||
event: T,
|
||||
@@ -420,6 +419,11 @@ export async function findPaginatedEventsBySeries({
|
||||
return events.map(mapEvent);
|
||||
}
|
||||
|
||||
/**
|
||||
* Every event of the series, newest first. Selects only what the leaderboard and the series header
|
||||
* need - the winners of {@link findPaginatedEventsBySeries} are far too costly across a whole
|
||||
* series.
|
||||
*/
|
||||
export async function findAllEventsBySeries({
|
||||
organizationId,
|
||||
substringMatches,
|
||||
@@ -427,10 +431,31 @@ export async function findAllEventsBySeries({
|
||||
organizationId: number;
|
||||
substringMatches: string[];
|
||||
}) {
|
||||
const events = await findSeriesEventsBaseQuery({
|
||||
organizationId,
|
||||
substringMatches,
|
||||
}).execute();
|
||||
const events = await db
|
||||
.selectFrom("CalendarEvent")
|
||||
.innerJoin(
|
||||
"CalendarEventDate",
|
||||
"CalendarEventDate.eventId",
|
||||
"CalendarEvent.id",
|
||||
)
|
||||
.select(({ eb }) => [
|
||||
"CalendarEvent.id as eventId",
|
||||
"CalendarEvent.tournamentId",
|
||||
eb.fn.min("CalendarEventDate.startsAt").as("startsAt"),
|
||||
tournamentLogoWithDefault(eb).as("logoUrl"),
|
||||
])
|
||||
.where("CalendarEvent.organizationId", "=", organizationId)
|
||||
.where("CalendarEvent.hidden", "=", 0)
|
||||
.where((eb) =>
|
||||
eb.or(
|
||||
substringMatches.map((match) =>
|
||||
eb("CalendarEvent.name", "like", `%${match}%`),
|
||||
),
|
||||
),
|
||||
)
|
||||
.groupBy("CalendarEvent.id")
|
||||
.orderBy("CalendarEventDate.startsAt", "desc")
|
||||
.execute();
|
||||
|
||||
return events.map(mapEvent);
|
||||
}
|
||||
|
||||
@@ -0,0 +1,191 @@
|
||||
import { beforeEach, describe, expect, test } from "vitest";
|
||||
import * as CalendarEventFactory from "~/db/seed/factories/CalendarEventFactory";
|
||||
import * as CalendarEventResultFactory from "~/db/seed/factories/CalendarEventResultFactory";
|
||||
import * as TournamentFactory from "~/db/seed/factories/TournamentFactory";
|
||||
import * as TournamentOrganizationFactory from "~/db/seed/factories/TournamentOrganizationFactory";
|
||||
import * as UserFactory from "~/db/seed/factories/UserFactory";
|
||||
import * as TournamentOrganizationRepository from "../TournamentOrganizationRepository.server";
|
||||
import { eventLeaderboards } from "./leaderboards.server";
|
||||
|
||||
const SERIES_SUBSTRING = "series";
|
||||
|
||||
const users = UserFactory.pool();
|
||||
const authorId = () => users.id(1);
|
||||
|
||||
describe("eventLeaderboards", () => {
|
||||
let organizationId: number;
|
||||
|
||||
beforeEach(async () => {
|
||||
await users.create(18);
|
||||
organizationId = (
|
||||
await TournamentOrganizationFactory.create({ ownerId: authorId() })
|
||||
).id;
|
||||
});
|
||||
|
||||
const seedEvent = async (
|
||||
results: Array<{ placement: number; playerIds: Array<number | null> }>,
|
||||
) => {
|
||||
const { id: eventId } = await CalendarEventFactory.create({
|
||||
authorId: authorId(),
|
||||
organizationId,
|
||||
name: `${SERIES_SUBSTRING} event`,
|
||||
});
|
||||
|
||||
await CalendarEventResultFactory.create({
|
||||
eventId,
|
||||
results: results.map((result, i) => ({
|
||||
teamName: `Team ${i + 1}`,
|
||||
placement: result.placement,
|
||||
players: result.playerIds.map((userId) => ({
|
||||
userId,
|
||||
name: userId ? null : "reported as text",
|
||||
})),
|
||||
})),
|
||||
});
|
||||
};
|
||||
|
||||
const seedTournament = async (teamRosters: Array<Array<number>>) => {
|
||||
await TournamentFactory.createPlayed(
|
||||
{
|
||||
authorId: authorId(),
|
||||
organizationId,
|
||||
name: `${SERIES_SUBSTRING} tournament`,
|
||||
minMembersPerTeam: 1,
|
||||
},
|
||||
{ teamRosters, playedOut: "all" },
|
||||
);
|
||||
};
|
||||
|
||||
const leaderboard = async () =>
|
||||
eventLeaderboards(
|
||||
await TournamentOrganizationRepository.findAllEventsBySeries({
|
||||
organizationId,
|
||||
substringMatches: [SERIES_SUBSTRING],
|
||||
}),
|
||||
);
|
||||
|
||||
test("awards 4, 2 and 1 points for the podium places only", async () => {
|
||||
await seedEvent([
|
||||
{ placement: 1, playerIds: [users.id(1)] },
|
||||
{ placement: 2, playerIds: [users.id(2)] },
|
||||
{ placement: 3, playerIds: [users.id(3)] },
|
||||
{ placement: 4, playerIds: [users.id(4)] },
|
||||
]);
|
||||
|
||||
const result = await leaderboard();
|
||||
|
||||
expect(result.map((entry) => entry.user.id)).toEqual([
|
||||
users.id(1),
|
||||
users.id(2),
|
||||
users.id(3),
|
||||
]);
|
||||
expect(result.map((entry) => entry.points)).toEqual([
|
||||
"4.00",
|
||||
"2.00",
|
||||
"1.00",
|
||||
]);
|
||||
});
|
||||
|
||||
test("splits the points of teams larger than four", async () => {
|
||||
await seedEvent([
|
||||
{ placement: 1, playerIds: users.ids(6) },
|
||||
{ placement: 2, playerIds: [users.id(1)] },
|
||||
]);
|
||||
|
||||
const result = await leaderboard();
|
||||
|
||||
expect(result[0].points).toBe("4.67");
|
||||
expect(result[1].points).toBe("2.67");
|
||||
});
|
||||
|
||||
test("ignores players reported as plain text", async () => {
|
||||
await seedEvent([{ placement: 1, playerIds: [users.id(1), null, null] }]);
|
||||
|
||||
const result = await leaderboard();
|
||||
|
||||
expect(result).toHaveLength(1);
|
||||
expect(result[0].user.id).toBe(users.id(1));
|
||||
});
|
||||
|
||||
test("sums points and placements across the events of the series", async () => {
|
||||
await seedEvent([{ placement: 1, playerIds: [users.id(1)] }]);
|
||||
await seedEvent([{ placement: 3, playerIds: [users.id(1)] }]);
|
||||
|
||||
const result = await leaderboard();
|
||||
|
||||
expect(result[0].points).toBe("5.00");
|
||||
expect(result[0].placements).toEqual({ first: 1, second: 0, third: 1 });
|
||||
});
|
||||
|
||||
test("breaks a points tie by first places", async () => {
|
||||
await seedEvent([{ placement: 2, playerIds: [users.id(2)] }]);
|
||||
await seedEvent([{ placement: 2, playerIds: [users.id(2)] }]);
|
||||
await seedEvent([{ placement: 1, playerIds: [users.id(1)] }]);
|
||||
|
||||
const result = await leaderboard();
|
||||
|
||||
expect(result.map((entry) => entry.points)).toEqual(["4.00", "4.00"]);
|
||||
expect(result.map((entry) => entry.user.id)).toEqual([
|
||||
users.id(1),
|
||||
users.id(2),
|
||||
]);
|
||||
});
|
||||
|
||||
test("breaks a points and first places tie by second places", async () => {
|
||||
await seedEvent([{ placement: 1, playerIds: [users.id(1)] }]);
|
||||
await seedEvent([{ placement: 3, playerIds: [users.id(1)] }]);
|
||||
await seedEvent([{ placement: 3, playerIds: [users.id(1)] }]);
|
||||
await seedEvent([{ placement: 1, playerIds: [users.id(2)] }]);
|
||||
await seedEvent([{ placement: 2, playerIds: [users.id(2)] }]);
|
||||
|
||||
const result = await leaderboard();
|
||||
|
||||
expect(result.map((entry) => entry.points)).toEqual(["6.00", "6.00"]);
|
||||
expect(result.map((entry) => entry.user.id)).toEqual([
|
||||
users.id(2),
|
||||
users.id(1),
|
||||
]);
|
||||
});
|
||||
|
||||
test("breaks a points, first and second places tie by third places", async () => {
|
||||
const onceThirdIds = users.ids(18).slice(0, 6);
|
||||
const twiceThirdIds = users.ids(18).slice(6);
|
||||
|
||||
await seedEvent([{ placement: 3, playerIds: onceThirdIds }]);
|
||||
await seedEvent([{ placement: 3, playerIds: twiceThirdIds }]);
|
||||
await seedEvent([{ placement: 3, playerIds: twiceThirdIds }]);
|
||||
|
||||
const result = await leaderboard();
|
||||
|
||||
expect(result.every((entry) => entry.points === "0.67")).toBe(true);
|
||||
expect(
|
||||
result
|
||||
.slice(0, twiceThirdIds.length)
|
||||
.map((entry) => entry.user.id)
|
||||
.sort(),
|
||||
).toEqual([...twiceThirdIds].sort());
|
||||
});
|
||||
|
||||
test("scores the podium of a played tournament of the series", async () => {
|
||||
await seedTournament([[users.id(1)], [users.id(2)]]);
|
||||
|
||||
const result = await leaderboard();
|
||||
|
||||
expect(result.map((entry) => entry.user.id)).toEqual([
|
||||
users.id(1),
|
||||
users.id(2),
|
||||
]);
|
||||
expect(result.map((entry) => entry.points)).toEqual(["4.00", "2.00"]);
|
||||
});
|
||||
|
||||
test("sums points and placements across tournaments and reported events", async () => {
|
||||
await seedTournament([[users.id(1)], [users.id(2)]]);
|
||||
await seedEvent([{ placement: 3, playerIds: [users.id(1)] }]);
|
||||
|
||||
const result = await leaderboard();
|
||||
|
||||
expect(result[0].user.id).toBe(users.id(1));
|
||||
expect(result[0].points).toBe("5.00");
|
||||
expect(result[0].placements).toEqual({ first: 1, second: 0, third: 1 });
|
||||
});
|
||||
});
|
||||
@@ -1,3 +1,4 @@
|
||||
import * as R from "remeda";
|
||||
import * as CalendarRepository from "~/features/calendar/CalendarRepository.server";
|
||||
import * as TournamentRepository from "~/features/tournament/TournamentRepository.server";
|
||||
import type { CommonUser } from "~/utils/kysely.server";
|
||||
@@ -8,6 +9,17 @@ const THIRD_PLACE_POINTS = 1;
|
||||
const SECOND_PLACE_POINTS = THIRD_PLACE_POINTS * 2;
|
||||
const FIRST_PLACE_POINTS = SECOND_PLACE_POINTS * 2;
|
||||
|
||||
const POINTS_TIE_EPSILON = 0.0001;
|
||||
|
||||
const USER_KEYS = [
|
||||
"id",
|
||||
"username",
|
||||
"discordId",
|
||||
"discordAvatar",
|
||||
"customUrl",
|
||||
"customAvatarUrl",
|
||||
] as const;
|
||||
|
||||
type EventLeaderboardEvent = Unpacked<
|
||||
Awaited<
|
||||
ReturnType<typeof TournamentOrganizationRepository.findAllEventsBySeries>
|
||||
@@ -24,114 +36,139 @@ interface LeaderboardInfo {
|
||||
};
|
||||
}
|
||||
|
||||
export async function eventLeaderboards(
|
||||
events: Awaited<
|
||||
ReturnType<typeof TournamentOrganizationRepository.findAllEventsBySeries>
|
||||
>,
|
||||
) {
|
||||
const points = new Map<number, LeaderboardInfo>();
|
||||
export async function eventLeaderboards(events: EventLeaderboardEvent[]) {
|
||||
const tournamentIds: number[] = [];
|
||||
const calendarEventIds: number[] = [];
|
||||
|
||||
for (const event of events) {
|
||||
const eventsPoints = event.tournamentId
|
||||
? await tournamentPoints(event as typeof event & { tournamentId: number })
|
||||
: await calendarEventPoints(event);
|
||||
|
||||
mergeMaps(points, eventsPoints);
|
||||
}
|
||||
|
||||
return Array.from(points.values())
|
||||
.sort((a, b) => b.points - a.points)
|
||||
.map((info) => ({ ...info, points: info.points.toFixed(2) }));
|
||||
}
|
||||
|
||||
async function tournamentPoints(
|
||||
event: EventLeaderboardEvent & { tournamentId: number },
|
||||
): Promise<Map<number, LeaderboardInfo>> {
|
||||
const results = await TournamentRepository.findTopThreeResultsByTournamentId(
|
||||
event.tournamentId,
|
||||
);
|
||||
|
||||
const leaderboardInfo = new Map<number, LeaderboardInfo>();
|
||||
|
||||
if (results.length === 0) return leaderboardInfo;
|
||||
|
||||
for (const result of results) {
|
||||
const teamSize = results.filter(
|
||||
(result2) => result.tournamentTeamId === result2.tournamentTeamId,
|
||||
).length;
|
||||
|
||||
leaderboardInfo.set(result.user.id, {
|
||||
user: result.user,
|
||||
points: pointsAdjustedToTeamSize({
|
||||
basePoints:
|
||||
result.placement === 1
|
||||
? FIRST_PLACE_POINTS
|
||||
: result.placement === 2
|
||||
? SECOND_PLACE_POINTS
|
||||
: THIRD_PLACE_POINTS,
|
||||
teamSize: teamSize,
|
||||
}),
|
||||
placements: {
|
||||
first: result.placement === 1 ? 1 : 0,
|
||||
second: result.placement === 2 ? 1 : 0,
|
||||
third: result.placement === 3 ? 1 : 0,
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
return leaderboardInfo;
|
||||
}
|
||||
|
||||
async function calendarEventPoints(
|
||||
event: EventLeaderboardEvent,
|
||||
): Promise<Map<number, LeaderboardInfo>> {
|
||||
const results = await CalendarRepository.findResultsByEventId(event.eventId);
|
||||
|
||||
const leaderboardInfo = new Map<number, LeaderboardInfo>();
|
||||
|
||||
if (results.length === 0) return leaderboardInfo;
|
||||
|
||||
for (const placement of [1, 2, 3]) {
|
||||
const placementResults = results.filter(
|
||||
(result) => result.placement === placement,
|
||||
);
|
||||
|
||||
for (const team of placementResults) {
|
||||
const teamSize = team.players.filter((player) => player.id).length;
|
||||
|
||||
for (const player of team.players) {
|
||||
// not a connected user, reported as simple text
|
||||
if (!player.id) continue;
|
||||
|
||||
leaderboardInfo.set(player.id, {
|
||||
user: {
|
||||
customUrl: player.customUrl,
|
||||
customAvatarUrl: player.customAvatarUrl,
|
||||
discordAvatar: player.discordAvatar,
|
||||
discordId: player.discordId!,
|
||||
id: player.id,
|
||||
username: player.username!,
|
||||
},
|
||||
points: pointsAdjustedToTeamSize({
|
||||
basePoints:
|
||||
placement === 1
|
||||
? FIRST_PLACE_POINTS
|
||||
: placement === 2
|
||||
? SECOND_PLACE_POINTS
|
||||
: THIRD_PLACE_POINTS,
|
||||
teamSize: teamSize,
|
||||
}),
|
||||
placements: {
|
||||
first: placement === 1 ? 1 : 0,
|
||||
second: placement === 2 ? 1 : 0,
|
||||
third: placement === 3 ? 1 : 0,
|
||||
},
|
||||
});
|
||||
}
|
||||
if (event.tournamentId) {
|
||||
tournamentIds.push(event.tournamentId);
|
||||
} else {
|
||||
calendarEventIds.push(event.eventId);
|
||||
}
|
||||
}
|
||||
|
||||
return leaderboardInfo;
|
||||
const [tournamentResults, calendarEventResults] = await Promise.all([
|
||||
TournamentRepository.findTopThreeResultsByTournamentIds(tournamentIds),
|
||||
CalendarRepository.findTopThreeResultsByEventIds(calendarEventIds),
|
||||
]);
|
||||
|
||||
const points = new Map<number, LeaderboardInfo>();
|
||||
|
||||
addTournamentPoints(points, tournamentResults);
|
||||
addCalendarEventPoints(points, calendarEventResults);
|
||||
|
||||
return Array.from(points.values())
|
||||
.sort(byPointsThenPlacements)
|
||||
.map((info) => ({ ...info, points: info.points.toFixed(2) }));
|
||||
}
|
||||
|
||||
/** Most points first, ties broken by most first places, then seconds, then thirds. */
|
||||
function byPointsThenPlacements(a: LeaderboardInfo, b: LeaderboardInfo) {
|
||||
const pointsDifference = b.points - a.points;
|
||||
|
||||
// team size adjusted points are fractional, so equal totals can differ in the last bits
|
||||
if (Math.abs(pointsDifference) > POINTS_TIE_EPSILON) return pointsDifference;
|
||||
|
||||
return (
|
||||
b.placements.first - a.placements.first ||
|
||||
b.placements.second - a.placements.second ||
|
||||
b.placements.third - a.placements.third
|
||||
);
|
||||
}
|
||||
|
||||
type TournamentResult = Unpacked<
|
||||
Awaited<
|
||||
ReturnType<typeof TournamentRepository.findTopThreeResultsByTournamentIds>
|
||||
>
|
||||
>;
|
||||
|
||||
function addTournamentPoints(
|
||||
acc: Map<number, LeaderboardInfo>,
|
||||
results: TournamentResult[],
|
||||
) {
|
||||
const teamSizes = R.countBy(results, (result) => result.tournamentTeamId);
|
||||
|
||||
for (const result of results) {
|
||||
addPlacement(acc, {
|
||||
user: R.pick(result, USER_KEYS),
|
||||
placement: result.placement,
|
||||
teamSize: teamSizes[result.tournamentTeamId],
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
type CalendarEventResult = Unpacked<
|
||||
Awaited<ReturnType<typeof CalendarRepository.findTopThreeResultsByEventIds>>
|
||||
>;
|
||||
|
||||
function addCalendarEventPoints(
|
||||
acc: Map<number, LeaderboardInfo>,
|
||||
results: CalendarEventResult[],
|
||||
) {
|
||||
// players reported as simple text don't score
|
||||
const teamSizes = R.countBy(
|
||||
results.filter((result) => result.id),
|
||||
(result) => result.teamId,
|
||||
);
|
||||
|
||||
for (const result of results) {
|
||||
if (!result.id) continue;
|
||||
|
||||
addPlacement(acc, {
|
||||
user: {
|
||||
...R.pick(result, USER_KEYS),
|
||||
id: result.id,
|
||||
username: result.username!,
|
||||
discordId: result.discordId!,
|
||||
},
|
||||
placement: result.placement,
|
||||
teamSize: teamSizes[result.teamId],
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
function addPlacement(
|
||||
acc: Map<number, LeaderboardInfo>,
|
||||
{
|
||||
user,
|
||||
placement,
|
||||
teamSize,
|
||||
}: {
|
||||
user: CommonUser;
|
||||
placement: number;
|
||||
teamSize: number;
|
||||
},
|
||||
) {
|
||||
const points = pointsAdjustedToTeamSize({
|
||||
basePoints:
|
||||
placement === 1
|
||||
? FIRST_PLACE_POINTS
|
||||
: placement === 2
|
||||
? SECOND_PLACE_POINTS
|
||||
: THIRD_PLACE_POINTS,
|
||||
teamSize,
|
||||
});
|
||||
|
||||
const existing = acc.get(user.id);
|
||||
|
||||
if (!existing) {
|
||||
acc.set(user.id, {
|
||||
user,
|
||||
points,
|
||||
placements: {
|
||||
first: placement === 1 ? 1 : 0,
|
||||
second: placement === 2 ? 1 : 0,
|
||||
third: placement === 3 ? 1 : 0,
|
||||
},
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
existing.points += points;
|
||||
if (placement === 1) existing.placements.first += 1;
|
||||
if (placement === 2) existing.placements.second += 1;
|
||||
if (placement === 3) existing.placements.third += 1;
|
||||
}
|
||||
|
||||
function pointsAdjustedToTeamSize({
|
||||
@@ -145,22 +182,3 @@ function pointsAdjustedToTeamSize({
|
||||
|
||||
return (basePoints * 4) / teamSize;
|
||||
}
|
||||
|
||||
function mergeMaps(
|
||||
accMap: Map<number, LeaderboardInfo>,
|
||||
newMap: Map<number, LeaderboardInfo>,
|
||||
) {
|
||||
for (const [userId, info] of newMap) {
|
||||
const accPoints = accMap.get(userId);
|
||||
|
||||
if (!accPoints) {
|
||||
accMap.set(userId, info);
|
||||
continue;
|
||||
}
|
||||
|
||||
accPoints.points += info.points;
|
||||
accPoints.placements.first += info.placements.first;
|
||||
accPoints.placements.second += info.placements.second;
|
||||
accPoints.placements.third += info.placements.third;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -982,22 +982,22 @@ export function findAllBetweenTwoTimestamps({
|
||||
.execute();
|
||||
}
|
||||
|
||||
export function findTopThreeResultsByTournamentId(tournamentId: number) {
|
||||
/** Podium placements of the given tournaments, one row per placed player. */
|
||||
export async function findTopThreeResultsByTournamentIds(
|
||||
tournamentIds: number[],
|
||||
) {
|
||||
if (tournamentIds.length === 0) return [];
|
||||
|
||||
return db
|
||||
.selectFrom("TournamentResult")
|
||||
.select(({ eb }) => [
|
||||
.innerJoin("User", "User.id", "TournamentResult.userId")
|
||||
.select((eb) => [
|
||||
"TournamentResult.placement",
|
||||
"TournamentResult.tournamentTeamId",
|
||||
jsonObjectFrom(
|
||||
eb
|
||||
.selectFrom("User")
|
||||
.select((eb) => commonUserSelect(eb))
|
||||
.whereRef("User.id", "=", "TournamentResult.userId"),
|
||||
).as("user"),
|
||||
...commonUserSelect(eb),
|
||||
])
|
||||
.where("tournamentId", "=", tournamentId)
|
||||
.where("TournamentResult.tournamentId", "in", tournamentIds)
|
||||
.where("TournamentResult.placement", "<=", 3)
|
||||
.$narrowType<{ user: NotNull }>()
|
||||
.execute();
|
||||
}
|
||||
|
||||
|
||||
@@ -226,6 +226,11 @@ export function buildCases(fx: Fixtures): {
|
||||
add("CalendarRepository.findResultsByEventId", fx.resultsEventId, (eventId) =>
|
||||
CalendarRepository.findResultsByEventId(eventId),
|
||||
);
|
||||
add(
|
||||
"CalendarRepository.findTopThreeResultsByEventIds",
|
||||
fx.resultsEventId,
|
||||
(eventId) => CalendarRepository.findTopThreeResultsByEventIds([eventId]),
|
||||
);
|
||||
|
||||
// FriendRepository
|
||||
add("FriendRepository.findByUserIdWithActivity", fx.heavyFriendPair, (pair) =>
|
||||
@@ -1091,10 +1096,10 @@ export function buildCases(fx: Fixtures): {
|
||||
(window) => TournamentRepository.findAllBetweenTwoTimestamps(window),
|
||||
);
|
||||
add(
|
||||
"TournamentRepository.findTopThreeResultsByTournamentId",
|
||||
"TournamentRepository.findTopThreeResultsByTournamentIds",
|
||||
fx.heavyTournamentId,
|
||||
(tournamentId) =>
|
||||
TournamentRepository.findTopThreeResultsByTournamentId(tournamentId),
|
||||
TournamentRepository.findTopThreeResultsByTournamentIds([tournamentId]),
|
||||
);
|
||||
add(
|
||||
"TournamentRepository.findFriendCodesByTournamentId",
|
||||
|
||||
Reference in New Issue
Block a user