From 83d9bd6ea84f8d77dc7453b37dde6de4f1a9aeac Mon Sep 17 00:00:00 2001 From: Kalle <38327916+Sendouc@users.noreply.github.com> Date: Sun, 2 Aug 2026 20:24:55 +0300 Subject: [PATCH] Fix crash when registering a tournament team as an organizer (#3285) --- .../TournamentTeamRepository.server.test.ts | 98 +++++++++++++++++++ .../TournamentTeamRepository.server.ts | 3 +- 2 files changed, 100 insertions(+), 1 deletion(-) create mode 100644 app/features/tournament/TournamentTeamRepository.server.test.ts diff --git a/app/features/tournament/TournamentTeamRepository.server.test.ts b/app/features/tournament/TournamentTeamRepository.server.test.ts new file mode 100644 index 000000000..a661df569 --- /dev/null +++ b/app/features/tournament/TournamentTeamRepository.server.test.ts @@ -0,0 +1,98 @@ +import { beforeEach, describe, expect, test } 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 { withUserId } from "~/utils/Test"; +import * as TournamentTeamRepository from "./TournamentTeamRepository.server"; + +let organizer: { id: number }; +let owner: { id: number }; +let member: { id: number }; +let anotherMember: { id: number }; + +const membersByTeamId = (tournamentTeamId: number) => + db + .selectFrom("TournamentTeamMember") + .select(["TournamentTeamMember.userId", "TournamentTeamMember.role"]) + .where("TournamentTeamMember.tournamentTeamId", "=", tournamentTeamId) + .execute(); + +const roleOf = ( + members: Array<{ userId: number; role: string }>, + userId: number, +) => members.find((teamMember) => teamMember.userId === userId)?.role; + +describe("TournamentTeamRepository", () => { + beforeEach(async () => { + [organizer, owner, member, anotherMember] = await UserFactory.createMany(4); + }); + + describe("upsertRegistration", () => { + test("gives a new team's members their roles", async () => { + const tournament = await TournamentFactory.create({ + authorId: organizer.id, + }); + + await withUserId(organizer.id, () => + TournamentTeamRepository.upsertRegistration({ + tournamentId: tournament.id, + name: "Team Olive", + teamId: null, + avatarImgId: null, + ownerUserId: owner.id, + ownerChange: null, + membersToAdd: [owner.id, member.id, anotherMember.id], + membersToRemove: [], + inGameNameUpdates: [], + }), + ); + + const team = await db + .selectFrom("TournamentTeam") + .select("TournamentTeam.id") + .where("TournamentTeam.tournamentId", "=", tournament.id) + .executeTakeFirstOrThrow(); + + const members = await membersByTeamId(team.id); + + expect(members).toHaveLength(3); + expect(roleOf(members, owner.id)).toBe("OWNER"); + expect(roleOf(members, member.id)).toBe("REGULAR"); + expect(roleOf(members, anotherMember.id)).toBe("REGULAR"); + }); + + test("added members of an existing team don't take the owner role", async () => { + const tournament = await TournamentFactory.create({ + authorId: organizer.id, + }); + const team = await TournamentTeamFactory.create({ + tournamentId: tournament.id, + memberUserIds: [owner.id], + team: { name: "Team Olive", prefersNotToHost: 0, teamId: null }, + }); + + await withUserId(organizer.id, () => + TournamentTeamRepository.upsertRegistration({ + tournamentTeamId: team.id, + tournamentId: tournament.id, + name: "Team Olive", + teamId: null, + avatarImgId: null, + ownerUserId: owner.id, + ownerChange: null, + membersToAdd: [member.id, anotherMember.id], + membersToRemove: [], + inGameNameUpdates: [], + }), + ); + + const members = await membersByTeamId(team.id); + + expect(members).toHaveLength(3); + expect(roleOf(members, owner.id)).toBe("OWNER"); + expect(roleOf(members, member.id)).toBe("REGULAR"); + expect(roleOf(members, anotherMember.id)).toBe("REGULAR"); + }); + }); +}); diff --git a/app/features/tournament/TournamentTeamRepository.server.ts b/app/features/tournament/TournamentTeamRepository.server.ts index 41bfb8c0f..45eba2aea 100644 --- a/app/features/tournament/TournamentTeamRepository.server.ts +++ b/app/features/tournament/TournamentTeamRepository.server.ts @@ -321,7 +321,8 @@ export function upsertRegistration({ ?.inGameName ?? (await resolveInGameName({ tournamentId, userId }, trx)), isSub, - ...(isOwner ? { role: "OWNER" as const } : {}), + // every row needs the same keys, otherwise Kysely inserts null for the missing ones + role: isOwner ? "OWNER" : "REGULAR", }); }