More progress

This commit is contained in:
Kalle
2026-03-16 21:13:33 +02:00
parent 5ec0934d9e
commit f65b920d3c
13 changed files with 1214 additions and 93 deletions

View File

@@ -2,22 +2,24 @@ import { beforeEach, describe, expect, it } from "vitest";
import { RunningTournaments } from "./RunningTournaments.server";
import { testTournament, tournamentCtxTeam } from "./tests/test-utils";
const createMember = (userId: number) => ({
userId,
username: `User ${userId}`,
discordId: String(userId),
discordAvatar: null,
customUrl: null,
country: null,
twitch: null,
plusTier: null,
createdAt: 0,
isOwner: 0,
inGameName: null,
streamTwitch: null,
streamViewerCount: null,
streamThumbnailUrl: null,
});
const createMember = (userId: number) =>
({
userId,
username: `User ${userId}`,
discordId: String(userId),
discordAvatar: null,
customUrl: null,
country: null,
twitch: null,
plusTier: null,
createdAt: 0,
isOwner: 0,
inGameName: null,
streamTwitch: null,
streamViewerCount: null,
streamThumbnailUrl: null,
role: "REGULAR",
}) as const;
const createTestTournament = (
tournamentId: number,

View File

@@ -827,6 +827,7 @@ export class Tournament {
/** Can a new sub post be made at this time? */
get canAddNewSubPost() {
if (!this.subsFeatureEnabled) return false;
if (this.ctx.isFinalized) return false;
return (
!this.ctx.settings.regClosesAt ||

View File

@@ -49,6 +49,7 @@ describe("tournamentSummary()", () => {
streamTwitch: null,
streamViewerCount: null,
streamThumbnailUrl: null,
role: "REGULAR",
})),
name: `Team ${teamId}`,
prefersNotToHost: 0,

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@@ -406,6 +406,7 @@ export const ZONES_WEEKLY_38 = (): TournamentData => ({
streamTwitch: null,
streamViewerCount: null,
streamThumbnailUrl: null,
role: "REGULAR",
},
{
userId: 2899,
@@ -422,6 +423,7 @@ export const ZONES_WEEKLY_38 = (): TournamentData => ({
streamTwitch: null,
streamViewerCount: null,
streamThumbnailUrl: null,
role: "REGULAR",
},
{
userId: 6114,
@@ -438,6 +440,7 @@ export const ZONES_WEEKLY_38 = (): TournamentData => ({
streamTwitch: null,
streamViewerCount: null,
streamThumbnailUrl: null,
role: "REGULAR",
},
{
userId: 33963,
@@ -454,6 +457,7 @@ export const ZONES_WEEKLY_38 = (): TournamentData => ({
streamTwitch: null,
streamViewerCount: null,
streamThumbnailUrl: null,
role: "REGULAR",
},
{
userId: 30176,
@@ -470,6 +474,7 @@ export const ZONES_WEEKLY_38 = (): TournamentData => ({
streamTwitch: null,
streamViewerCount: null,
streamThumbnailUrl: null,
role: "REGULAR",
},
],
checkIns: [
@@ -515,6 +520,7 @@ export const ZONES_WEEKLY_38 = (): TournamentData => ({
streamTwitch: null,
streamViewerCount: null,
streamThumbnailUrl: null,
role: "REGULAR",
},
{
userId: 21689,
@@ -531,6 +537,7 @@ export const ZONES_WEEKLY_38 = (): TournamentData => ({
streamTwitch: null,
streamViewerCount: null,
streamThumbnailUrl: null,
role: "REGULAR",
},
{
userId: 3147,
@@ -547,6 +554,7 @@ export const ZONES_WEEKLY_38 = (): TournamentData => ({
streamTwitch: null,
streamViewerCount: null,
streamThumbnailUrl: null,
role: "REGULAR",
},
{
userId: 2072,
@@ -563,6 +571,7 @@ export const ZONES_WEEKLY_38 = (): TournamentData => ({
streamTwitch: null,
streamViewerCount: null,
streamThumbnailUrl: null,
role: "REGULAR",
},
],
checkIns: [
@@ -603,6 +612,7 @@ export const ZONES_WEEKLY_38 = (): TournamentData => ({
streamTwitch: null,
streamViewerCount: null,
streamThumbnailUrl: null,
role: "REGULAR",
},
{
userId: 13370,
@@ -619,6 +629,7 @@ export const ZONES_WEEKLY_38 = (): TournamentData => ({
streamTwitch: null,
streamViewerCount: null,
streamThumbnailUrl: null,
role: "REGULAR",
},
{
userId: 45,
@@ -635,6 +646,7 @@ export const ZONES_WEEKLY_38 = (): TournamentData => ({
streamTwitch: null,
streamViewerCount: null,
streamThumbnailUrl: null,
role: "REGULAR",
},
{
userId: 1843,
@@ -651,6 +663,7 @@ export const ZONES_WEEKLY_38 = (): TournamentData => ({
streamTwitch: null,
streamViewerCount: null,
streamThumbnailUrl: null,
role: "REGULAR",
},
],
checkIns: [
@@ -691,6 +704,7 @@ export const ZONES_WEEKLY_38 = (): TournamentData => ({
streamTwitch: null,
streamViewerCount: null,
streamThumbnailUrl: null,
role: "REGULAR",
},
{
userId: 13590,
@@ -707,6 +721,7 @@ export const ZONES_WEEKLY_38 = (): TournamentData => ({
streamTwitch: null,
streamViewerCount: null,
streamThumbnailUrl: null,
role: "REGULAR",
},
{
userId: 10757,
@@ -723,6 +738,7 @@ export const ZONES_WEEKLY_38 = (): TournamentData => ({
streamTwitch: null,
streamViewerCount: null,
streamThumbnailUrl: null,
role: "REGULAR",
},
{
userId: 33047,
@@ -739,6 +755,7 @@ export const ZONES_WEEKLY_38 = (): TournamentData => ({
streamTwitch: null,
streamViewerCount: null,
streamThumbnailUrl: null,
role: "REGULAR",
},
{
userId: 41024,
@@ -755,6 +772,7 @@ export const ZONES_WEEKLY_38 = (): TournamentData => ({
streamTwitch: null,
streamViewerCount: null,
streamThumbnailUrl: null,
role: "REGULAR",
},
],
checkIns: [
@@ -800,6 +818,7 @@ export const ZONES_WEEKLY_38 = (): TournamentData => ({
streamTwitch: null,
streamViewerCount: null,
streamThumbnailUrl: null,
role: "REGULAR",
},
{
userId: 29665,
@@ -816,6 +835,7 @@ export const ZONES_WEEKLY_38 = (): TournamentData => ({
streamTwitch: null,
streamViewerCount: null,
streamThumbnailUrl: null,
role: "REGULAR",
},
{
userId: 46006,
@@ -832,6 +852,7 @@ export const ZONES_WEEKLY_38 = (): TournamentData => ({
streamTwitch: null,
streamViewerCount: null,
streamThumbnailUrl: null,
role: "REGULAR",
},
{
userId: 33483,
@@ -848,6 +869,7 @@ export const ZONES_WEEKLY_38 = (): TournamentData => ({
streamTwitch: null,
streamViewerCount: null,
streamThumbnailUrl: null,
role: "REGULAR",
},
{
userId: 11780,
@@ -864,6 +886,7 @@ export const ZONES_WEEKLY_38 = (): TournamentData => ({
streamTwitch: null,
streamViewerCount: null,
streamThumbnailUrl: null,
role: "REGULAR",
},
{
userId: 37901,
@@ -880,6 +903,7 @@ export const ZONES_WEEKLY_38 = (): TournamentData => ({
streamTwitch: null,
streamViewerCount: null,
streamThumbnailUrl: null,
role: "REGULAR",
},
],
checkIns: [
@@ -920,6 +944,7 @@ export const ZONES_WEEKLY_38 = (): TournamentData => ({
streamTwitch: null,
streamViewerCount: null,
streamThumbnailUrl: null,
role: "REGULAR",
},
{
userId: 43662,
@@ -936,6 +961,7 @@ export const ZONES_WEEKLY_38 = (): TournamentData => ({
streamTwitch: null,
streamViewerCount: null,
streamThumbnailUrl: null,
role: "REGULAR",
},
{
userId: 33491,
@@ -952,6 +978,7 @@ export const ZONES_WEEKLY_38 = (): TournamentData => ({
streamTwitch: null,
streamViewerCount: null,
streamThumbnailUrl: null,
role: "REGULAR",
},
{
userId: 46467,
@@ -968,6 +995,7 @@ export const ZONES_WEEKLY_38 = (): TournamentData => ({
streamTwitch: null,
streamViewerCount: null,
streamThumbnailUrl: null,
role: "REGULAR",
},
{
userId: 46813,
@@ -984,6 +1012,7 @@ export const ZONES_WEEKLY_38 = (): TournamentData => ({
streamTwitch: null,
streamViewerCount: null,
streamThumbnailUrl: null,
role: "REGULAR",
},
],
checkIns: [
@@ -1029,6 +1058,7 @@ export const ZONES_WEEKLY_38 = (): TournamentData => ({
streamTwitch: null,
streamViewerCount: null,
streamThumbnailUrl: null,
role: "REGULAR",
},
{
userId: 33611,
@@ -1045,6 +1075,7 @@ export const ZONES_WEEKLY_38 = (): TournamentData => ({
streamTwitch: null,
streamViewerCount: null,
streamThumbnailUrl: null,
role: "REGULAR",
},
{
userId: 31148,
@@ -1061,6 +1092,7 @@ export const ZONES_WEEKLY_38 = (): TournamentData => ({
streamTwitch: null,
streamViewerCount: null,
streamThumbnailUrl: null,
role: "REGULAR",
},
{
userId: 33578,
@@ -1077,6 +1109,7 @@ export const ZONES_WEEKLY_38 = (): TournamentData => ({
streamTwitch: null,
streamViewerCount: null,
streamThumbnailUrl: null,
role: "REGULAR",
},
],
checkIns: [

File diff suppressed because it is too large Load Diff

View File

@@ -454,34 +454,6 @@ describe("leaveLfg", () => {
dbReset();
});
test("removes member from placeholder team", async () => {
const tournament = await createTournament();
const team = await createPlaceholder(tournament.id, 1);
await db
.insertInto("TournamentTeamMember")
.values({
tournamentTeamId: team.id,
userId: 2,
isOwner: 0,
role: "REGULAR",
})
.execute();
await TournamentLFGRepository.leaveLfg({
userId: 2,
tournamentId: tournament.id,
});
const groups = await TournamentLFGRepository.findLookingTeamsByTournamentId(
tournament.id,
);
const members = groups[0].members;
expect(members).toHaveLength(1);
expect(members[0].id).toBe(1);
});
test("deletes placeholder team when last member leaves", async () => {
const tournament = await createTournament();
await createPlaceholder(tournament.id, 1);
@@ -498,44 +470,6 @@ describe("leaveLfg", () => {
expect(groups).toHaveLength(0);
});
test("promotes MANAGER to OWNER when owner leaves placeholder", async () => {
const tournament = await createTournament();
const team = await createPlaceholder(tournament.id, 1);
await db
.insertInto("TournamentTeamMember")
.values({
tournamentTeamId: team.id,
userId: 2,
isOwner: 0,
role: "MANAGER",
})
.execute();
await db
.insertInto("TournamentTeamMember")
.values({
tournamentTeamId: team.id,
userId: 3,
isOwner: 0,
role: "REGULAR",
})
.execute();
await TournamentLFGRepository.leaveLfg({
userId: 1,
tournamentId: tournament.id,
});
const groups = await TournamentLFGRepository.findLookingTeamsByTournamentId(
tournament.id,
);
const members = groups[0].members;
const newOwner = members.find((m) => m.id === 2);
expect(newOwner?.role).toBe("OWNER");
});
test("sets isLooking=0 for non-placeholder team", async () => {
const tournament = await createTournament();

View File

@@ -163,7 +163,6 @@ export async function findSubGroups(tournamentId: number) {
return rows;
}
// xxx: ensure that the team size does not exceed the max, also filter out in frontend
export function mergeTeams({
survivingTeamId,
otherTeamId,

View File

@@ -1,5 +1,6 @@
import type { ActionFunctionArgs } from "react-router";
import { requireUser } from "~/features/auth/core/user.server";
import { requireNotBannedByOrganization } from "~/features/tournament/tournament-utils.server";
import {
clearTournamentDataCache,
tournamentFromDBCached,
@@ -15,9 +16,6 @@ import * as TournamentLFGRepository from "../TournamentLFGRepository.server";
import { lookingSchema } from "../tournament-lfg-schemas";
import { survivingTeamId } from "../tournament-lfg-utils";
// xxx: prevent actions in certain cases? like when tournament has started
// xxx: add `requireNotBannedByOrganization` & other checks from deleted to.$id.subs.new.tsx route
export const action = async ({ request, params }: ActionFunctionArgs) => {
const user = requireUser();
const { id: tournamentId } = parseParams({ params, schema: idObject });
@@ -53,14 +51,20 @@ export const action = async ({ request, params }: ActionFunctionArgs) => {
tournamentId,
user,
});
await requireNotBannedByOrganization({ tournament, user });
errorToastIfFalsy(
tournament.canAddNewSubPost,
"Cannot add sub post at this time",
);
const team = tournament.teamMemberOfByUser(user);
if (team) {
// xxx: or isManager, also just show errorToast no return null
const isTeamOwner = team.members.some(
(m) => m.userId === user.id && m.isOwner,
const member = team.members.find((m) => m.userId === user.id);
const canManageTeam = member?.isOwner || member?.role === "MANAGER";
errorToastIfFalsy(
canManageTeam,
"Only team owners and managers can join the queue",
);
if (!isTeamOwner) return null;
errorToastIfFalsy(
team.members.length < tournament.maxMembersPerTeam,
@@ -210,6 +214,8 @@ export const action = async ({ request, params }: ActionFunctionArgs) => {
tournamentId,
user,
});
await requireNotBannedByOrganization({ tournament, user });
errorToastIfFalsy(!tournament.everyBracketOver, "Tournament is over");
errorToastIfFalsy(
tournament.canAddNewSubPost,
"Cannot add sub post at this time",

View File

@@ -152,7 +152,7 @@ async function resolveOwnTeam({
languages: [],
vc: null,
pronouns: null,
role: m.isOwner ? "OWNER" : "REGULAR",
role: m.isOwner ? "OWNER" : m.role === "MANAGER" ? "MANAGER" : "REGULAR",
isStayAsSub: false,
weapons: null,
plusTier: m.plusTier,

View File

@@ -53,8 +53,6 @@ export default function TournamentLFGShell() {
return <TournamentLFGPage />;
}
// xxx: no button if already in the tournament
// xxx: can we reuse the column layout with sendouq?
function TournamentLFGPage() {
const data = useLoaderData<typeof loader>();
if (data.mode === "subs") {

View File

@@ -178,6 +178,7 @@ export async function findById(id: number) {
"SeedingSkill.ordinal",
"PlusTier.tier as plusTier",
"TournamentTeamMember.isOwner",
"TournamentTeamMember.role",
"TournamentTeamMember.createdAt",
sql<string | null> /*sql*/`coalesce(
"TournamentTeamMember"."inGameName",