Files
sendou.ink/app/features/tournament-bracket/BracketRepository.server.test.ts
2026-09-06 16:57:29 +03:00

143 lines
4.3 KiB
TypeScript

import { afterEach, beforeEach, describe, expect, test, vi } from "vitest";
import * as TournamentFactory from "~/db/seed/factories/TournamentFactory";
import * as TournamentTeamFactory from "~/db/seed/factories/TournamentTeamFactory";
import * as UserFactory from "~/db/seed/factories/UserFactory";
import { db } from "~/db/sql";
import { userChannel } from "~/features/events/events-types";
import {
abortSubscriptions,
subscribeTo,
} from "~/features/events/tests/fixtures";
import { resolveMatchMapList } from "~/features/tournament-match/core/mapList.server";
import { reportScore } from "~/features/tournament-match/core/reportScore.server";
import * as TournamentMatchRepository from "~/features/tournament-match/TournamentMatchRepository.server";
import { invariant } from "~/utils/invariant";
import * as Engine from "./core/engine";
import { executeBracketOperation } from "./core/executeBracketOperation.server";
import {
clearTournamentDataCache,
tournamentFromDB,
} from "./core/Tournament.server";
const users = UserFactory.pool();
beforeEach(async () => {
await users.create(9);
});
afterEach(() => {
abortSubscriptions();
});
const setupStartedMatch = async () => {
const authorId = users.id(1);
const teamAlphaUserIds = [users.id(2), users.id(3), users.id(4), users.id(5)];
const teamBravoUserIds = [users.id(6), users.id(7), users.id(8), users.id(9)];
const tournament = await TournamentFactory.create({ authorId });
for (const memberUserIds of [teamAlphaUserIds, teamBravoUserIds]) {
await TournamentTeamFactory.create(
{ tournamentId: tournament.id, memberUserIds },
{ isCheckedIn: true },
);
}
await TournamentFactory.startBracket(tournament.id);
const match = await db
.selectFrom("TournamentMatch")
.innerJoin(
"TournamentStage",
"TournamentStage.id",
"TournamentMatch.stageId",
)
.select(["TournamentMatch.id", "TournamentMatch.chatRoomId"])
.where("TournamentStage.tournamentId", "=", tournament.id)
.where("TournamentMatch.chatRoomId", "is not", null)
.executeTakeFirstOrThrow();
return {
tournamentId: tournament.id,
authorId,
matchId: match.id,
chatRoomId: match.chatRoomId!,
};
};
/** Reports every game of the match through `reportScore` until the set is over. */
const playOutMatch = async (
setup: Awaited<ReturnType<typeof setupStartedMatch>>,
) => {
let position = 0;
let setOver = false;
while (!setOver) {
const tournament = await tournamentFromDB(setup.tournamentId);
const matchRow = await TournamentMatchRepository.findMatchById(
setup.matchId,
);
invariant(matchRow, "Match not found");
invariant(matchRow.opponentOne?.id, "Match has no first opponent");
const reported = await reportScore({
match: matchRow,
tournament,
mapList: await resolveMatchMapList({ match: matchRow, tournament }),
user: { id: setup.authorId },
position,
winnerTeamId: matchRow.opponentOne.id,
ko: false,
});
invariant(reported, `Game ${position} was already reported`);
setOver = reported.setOver;
position++;
}
clearTournamentDataCache(setup.tournamentId);
};
describe("BracketRepository.applyMatchChanges", () => {
test("completing a match marks its chat room inactive", async () => {
const setup = await setupStartedMatch();
expect((await roomById(setup.chatRoomId)).inactive).toBe(0);
await playOutMatch(setup);
expect((await roomById(setup.chatRoomId)).inactive).toBe(1);
});
test("tells the match's participants their room list changed", async () => {
const setup = await setupStartedMatch();
const received = subscribeTo(userChannel(users.id(2)));
await playOutMatch(setup);
await vi.waitFor(() =>
expect(received).toEqual([{ kind: "roomsChanged" }]),
);
});
test("reopening a completed match marks its chat room back active", async () => {
const setup = await setupStartedMatch();
await playOutMatch(setup);
const tournament = await tournamentFromDB(setup.tournamentId);
await executeBracketOperation({
tournamentId: setup.tournamentId,
tournament,
operation: (bracketData) =>
Engine.reopenMatch(bracketData, setup.matchId),
endDroppedTeams: false,
});
expect((await roomById(setup.chatRoomId)).inactive).toBe(0);
});
});
const roomById = (id: number) =>
db
.selectFrom("ChatRoom")
.selectAll()
.where("id", "=", id)
.executeTakeFirstOrThrow();