mirror of
https://github.com/Sendouc/sendou.ink.git
synced 2026-05-16 07:56:22 -05:00
Cap placements bracket progression placements at 100
This matters as we have arrays where the size is the amount of placements. Previously some users put 100k + placements
This commit is contained in:
parent
2d48d71ccc
commit
f60966e11d
|
|
@ -636,7 +636,10 @@ function ErrorMessage({ error }: { error: Progression.ValidationError }) {
|
|||
{bracketIdxsArr ? (
|
||||
<> (Bracket {bracketIdxsArr.map((idx) => `#${idx + 1}`).join(", ")})</>
|
||||
) : null}
|
||||
: {t(`tournament:progression.error.${error.type}`)}
|
||||
:{" "}
|
||||
{t(`tournament:progression.error.${error.type}`, {
|
||||
max: TOURNAMENT.PLACEMENT_MAX,
|
||||
})}
|
||||
</FormMessage>
|
||||
);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -442,6 +442,39 @@ describe("validatedSources - other rules", () => {
|
|||
expect((error as any).bracketIdx).toEqual(1);
|
||||
});
|
||||
|
||||
it("handles PLACEMENT_TOO_HIGH", () => {
|
||||
const error = getValidatedBrackets([
|
||||
{
|
||||
settings: { teamsPerGroup: 200 },
|
||||
type: "round_robin",
|
||||
},
|
||||
{
|
||||
settings: {},
|
||||
type: "single_elimination",
|
||||
sources: [{ bracketId: "0", placements: "1-101" }],
|
||||
},
|
||||
]) as Progression.ValidationError;
|
||||
|
||||
expect(error.type).toBe("PLACEMENT_TOO_HIGH");
|
||||
expect((error as any).bracketIdx).toEqual(1);
|
||||
});
|
||||
|
||||
it("does not flag PLACEMENT_TOO_HIGH at the max boundary", () => {
|
||||
const result = getValidatedBrackets([
|
||||
{
|
||||
settings: { teamsPerGroup: 200 },
|
||||
type: "round_robin",
|
||||
},
|
||||
{
|
||||
settings: {},
|
||||
type: "single_elimination",
|
||||
sources: [{ bracketId: "0", placements: "1-100" }],
|
||||
},
|
||||
]);
|
||||
|
||||
expect(Array.isArray(result)).toBe(true);
|
||||
});
|
||||
|
||||
it("does not flag TOO_MANY_PLACEMENTS when larger round robin has valid high placements", () => {
|
||||
const result = getValidatedBrackets([
|
||||
{
|
||||
|
|
|
|||
|
|
@ -68,6 +68,11 @@ export type ValidationError =
|
|||
type: "TOO_MANY_PLACEMENTS";
|
||||
bracketIdx: number;
|
||||
}
|
||||
// placements above the hard cap are nonsensical and bloat the settings JSON
|
||||
| {
|
||||
type: "PLACEMENT_TOO_HIGH";
|
||||
bracketIdx: number;
|
||||
}
|
||||
// two brackets can not have the same name
|
||||
| {
|
||||
type: "DUPLICATE_BRACKET_NAME";
|
||||
|
|
@ -254,6 +259,14 @@ export function bracketsToValidationError(
|
|||
};
|
||||
}
|
||||
|
||||
faultyBracketIdx = placementTooHigh(brackets);
|
||||
if (typeof faultyBracketIdx === "number") {
|
||||
return {
|
||||
type: "PLACEMENT_TOO_HIGH",
|
||||
bracketIdx: faultyBracketIdx,
|
||||
};
|
||||
}
|
||||
|
||||
faultyBracketIdx = nameMissing(brackets);
|
||||
if (typeof faultyBracketIdx === "number") {
|
||||
return {
|
||||
|
|
@ -542,6 +555,22 @@ function tooManyPlacements(brackets: ParsedBracket[]) {
|
|||
return null;
|
||||
}
|
||||
|
||||
function placementTooHigh(brackets: ParsedBracket[]) {
|
||||
for (const [bracketIdx, bracket] of brackets.entries()) {
|
||||
for (const source of bracket.sources ?? []) {
|
||||
if (
|
||||
source.placements.some(
|
||||
(placement) => placement > TOURNAMENT.PLACEMENT_MAX,
|
||||
)
|
||||
) {
|
||||
return bracketIdx;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
function nameMissing(brackets: ParsedBracket[]) {
|
||||
for (const [bracketIdx, bracket] of brackets.entries()) {
|
||||
if (!bracket.name) {
|
||||
|
|
|
|||
|
|
@ -11,6 +11,7 @@ export const TOURNAMENT = {
|
|||
MAX_GROUP_SIZE: 6,
|
||||
MAX_BRACKETS_PER_TOURNAMENT: 10,
|
||||
BRACKET_NAME_MAX_LENGTH: 32,
|
||||
PLACEMENT_MAX: 100,
|
||||
// just a fallback, normally this should be set by user explicitly
|
||||
RR_DEFAULT_TEAM_COUNT_PER_GROUP: 4,
|
||||
RR_TEAMS_PER_GROUP_OPTIONS: [3, 4, 5, 6],
|
||||
|
|
|
|||
BIN
db-test.sqlite3
BIN
db-test.sqlite3
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
|
|
@ -170,6 +170,7 @@
|
|||
"progression.error.SAME_PLACEMENT_TO_MULTIPLE_BRACKETS": "",
|
||||
"progression.error.GAP_IN_PLACEMENTS": "",
|
||||
"progression.error.TOO_MANY_PLACEMENTS": "",
|
||||
"progression.error.PLACEMENT_TOO_HIGH": "",
|
||||
"progression.error.DUPLICATE_BRACKET_NAME": "",
|
||||
"progression.error.NAME_MISSING": "",
|
||||
"progression.error.NEGATIVE_PROGRESSION": "",
|
||||
|
|
|
|||
|
|
@ -170,6 +170,7 @@
|
|||
"progression.error.SAME_PLACEMENT_TO_MULTIPLE_BRACKETS": "",
|
||||
"progression.error.GAP_IN_PLACEMENTS": "",
|
||||
"progression.error.TOO_MANY_PLACEMENTS": "",
|
||||
"progression.error.PLACEMENT_TOO_HIGH": "",
|
||||
"progression.error.DUPLICATE_BRACKET_NAME": "",
|
||||
"progression.error.NAME_MISSING": "",
|
||||
"progression.error.NEGATIVE_PROGRESSION": "",
|
||||
|
|
|
|||
|
|
@ -170,6 +170,7 @@
|
|||
"progression.error.SAME_PLACEMENT_TO_MULTIPLE_BRACKETS": "Same placement leads to multiple brackets",
|
||||
"progression.error.GAP_IN_PLACEMENTS": "Gap in placements that advance",
|
||||
"progression.error.TOO_MANY_PLACEMENTS": "Too many placements (more than teams in groups)",
|
||||
"progression.error.PLACEMENT_TOO_HIGH": "Placement is too high (max {{max}})",
|
||||
"progression.error.DUPLICATE_BRACKET_NAME": "Duplicate bracket name",
|
||||
"progression.error.NAME_MISSING": "Bracket name missing",
|
||||
"progression.error.NEGATIVE_PROGRESSION": "Negative progression only possible for double elimination",
|
||||
|
|
|
|||
|
|
@ -172,6 +172,7 @@
|
|||
"progression.error.SAME_PLACEMENT_TO_MULTIPLE_BRACKETS": "La misma clasificación lleva a varios cuadros",
|
||||
"progression.error.GAP_IN_PLACEMENTS": "Hay un hueco en las clasificaciones que avanzan",
|
||||
"progression.error.TOO_MANY_PLACEMENTS": "Demasiadas clasificaciones (más que equipos en grupos)",
|
||||
"progression.error.PLACEMENT_TOO_HIGH": "",
|
||||
"progression.error.DUPLICATE_BRACKET_NAME": "Nombre de cuadro duplicado",
|
||||
"progression.error.NAME_MISSING": "Falta el nombre del cuadro",
|
||||
"progression.error.NEGATIVE_PROGRESSION": "La progresión negativa solo es posible en eliminación doble",
|
||||
|
|
|
|||
|
|
@ -172,6 +172,7 @@
|
|||
"progression.error.SAME_PLACEMENT_TO_MULTIPLE_BRACKETS": "",
|
||||
"progression.error.GAP_IN_PLACEMENTS": "",
|
||||
"progression.error.TOO_MANY_PLACEMENTS": "",
|
||||
"progression.error.PLACEMENT_TOO_HIGH": "",
|
||||
"progression.error.DUPLICATE_BRACKET_NAME": "",
|
||||
"progression.error.NAME_MISSING": "",
|
||||
"progression.error.NEGATIVE_PROGRESSION": "",
|
||||
|
|
|
|||
|
|
@ -172,6 +172,7 @@
|
|||
"progression.error.SAME_PLACEMENT_TO_MULTIPLE_BRACKETS": "",
|
||||
"progression.error.GAP_IN_PLACEMENTS": "",
|
||||
"progression.error.TOO_MANY_PLACEMENTS": "",
|
||||
"progression.error.PLACEMENT_TOO_HIGH": "",
|
||||
"progression.error.DUPLICATE_BRACKET_NAME": "",
|
||||
"progression.error.NAME_MISSING": "",
|
||||
"progression.error.NEGATIVE_PROGRESSION": "",
|
||||
|
|
|
|||
|
|
@ -172,6 +172,7 @@
|
|||
"progression.error.SAME_PLACEMENT_TO_MULTIPLE_BRACKETS": "Same placement leads to multiple brackets",
|
||||
"progression.error.GAP_IN_PLACEMENTS": "Gap in placements that advance",
|
||||
"progression.error.TOO_MANY_PLACEMENTS": "Too many placements (more than teams in groups)",
|
||||
"progression.error.PLACEMENT_TOO_HIGH": "",
|
||||
"progression.error.DUPLICATE_BRACKET_NAME": "Duplicate bracket name",
|
||||
"progression.error.NAME_MISSING": "Bracket name missing",
|
||||
"progression.error.NEGATIVE_PROGRESSION": "Negative progression only possible for double elimination",
|
||||
|
|
|
|||
|
|
@ -172,6 +172,7 @@
|
|||
"progression.error.SAME_PLACEMENT_TO_MULTIPLE_BRACKETS": "",
|
||||
"progression.error.GAP_IN_PLACEMENTS": "",
|
||||
"progression.error.TOO_MANY_PLACEMENTS": "",
|
||||
"progression.error.PLACEMENT_TOO_HIGH": "",
|
||||
"progression.error.DUPLICATE_BRACKET_NAME": "",
|
||||
"progression.error.NAME_MISSING": "",
|
||||
"progression.error.NEGATIVE_PROGRESSION": "",
|
||||
|
|
|
|||
|
|
@ -172,6 +172,7 @@
|
|||
"progression.error.SAME_PLACEMENT_TO_MULTIPLE_BRACKETS": "La stessa posizione porta a più bracket",
|
||||
"progression.error.GAP_IN_PLACEMENTS": "Vuoto tra le posizioni che avanzano",
|
||||
"progression.error.TOO_MANY_PLACEMENTS": "Troppe posizioni (più dei team nella fase a gironi)",
|
||||
"progression.error.PLACEMENT_TOO_HIGH": "",
|
||||
"progression.error.DUPLICATE_BRACKET_NAME": "Nome bracket duplicato",
|
||||
"progression.error.NAME_MISSING": "Nome bracket mancante",
|
||||
"progression.error.NEGATIVE_PROGRESSION": "La progressione negativa è disponibile solo in doppia eliminazione",
|
||||
|
|
|
|||
|
|
@ -166,6 +166,7 @@
|
|||
"progression.error.SAME_PLACEMENT_TO_MULTIPLE_BRACKETS": "同じ順位はブラケットを多数作ります",
|
||||
"progression.error.GAP_IN_PLACEMENTS": "前に進む順位にギャップがあります",
|
||||
"progression.error.TOO_MANY_PLACEMENTS": "順位がありすぎます。(グループに入っているチームより多いです)",
|
||||
"progression.error.PLACEMENT_TOO_HIGH": "",
|
||||
"progression.error.DUPLICATE_BRACKET_NAME": "ブラケットの名前が重複しています",
|
||||
"progression.error.NAME_MISSING": "ブラケットの名前がありません",
|
||||
"progression.error.NEGATIVE_PROGRESSION": "逆の進行はダブルエリ三ネーションの時のみ可能です",
|
||||
|
|
|
|||
|
|
@ -166,6 +166,7 @@
|
|||
"progression.error.SAME_PLACEMENT_TO_MULTIPLE_BRACKETS": "",
|
||||
"progression.error.GAP_IN_PLACEMENTS": "",
|
||||
"progression.error.TOO_MANY_PLACEMENTS": "",
|
||||
"progression.error.PLACEMENT_TOO_HIGH": "",
|
||||
"progression.error.DUPLICATE_BRACKET_NAME": "",
|
||||
"progression.error.NAME_MISSING": "",
|
||||
"progression.error.NEGATIVE_PROGRESSION": "",
|
||||
|
|
|
|||
|
|
@ -170,6 +170,7 @@
|
|||
"progression.error.SAME_PLACEMENT_TO_MULTIPLE_BRACKETS": "",
|
||||
"progression.error.GAP_IN_PLACEMENTS": "",
|
||||
"progression.error.TOO_MANY_PLACEMENTS": "",
|
||||
"progression.error.PLACEMENT_TOO_HIGH": "",
|
||||
"progression.error.DUPLICATE_BRACKET_NAME": "",
|
||||
"progression.error.NAME_MISSING": "",
|
||||
"progression.error.NEGATIVE_PROGRESSION": "",
|
||||
|
|
|
|||
|
|
@ -174,6 +174,7 @@
|
|||
"progression.error.SAME_PLACEMENT_TO_MULTIPLE_BRACKETS": "",
|
||||
"progression.error.GAP_IN_PLACEMENTS": "",
|
||||
"progression.error.TOO_MANY_PLACEMENTS": "",
|
||||
"progression.error.PLACEMENT_TOO_HIGH": "",
|
||||
"progression.error.DUPLICATE_BRACKET_NAME": "",
|
||||
"progression.error.NAME_MISSING": "",
|
||||
"progression.error.NEGATIVE_PROGRESSION": "",
|
||||
|
|
|
|||
|
|
@ -172,6 +172,7 @@
|
|||
"progression.error.SAME_PLACEMENT_TO_MULTIPLE_BRACKETS": "",
|
||||
"progression.error.GAP_IN_PLACEMENTS": "",
|
||||
"progression.error.TOO_MANY_PLACEMENTS": "",
|
||||
"progression.error.PLACEMENT_TOO_HIGH": "",
|
||||
"progression.error.DUPLICATE_BRACKET_NAME": "",
|
||||
"progression.error.NAME_MISSING": "",
|
||||
"progression.error.NEGATIVE_PROGRESSION": "",
|
||||
|
|
|
|||
|
|
@ -174,6 +174,7 @@
|
|||
"progression.error.SAME_PLACEMENT_TO_MULTIPLE_BRACKETS": "Одинаковые места приводят к нескольким сеткам",
|
||||
"progression.error.GAP_IN_PLACEMENTS": "Разрыв между местами, которые проходят на следующий этап",
|
||||
"progression.error.TOO_MANY_PLACEMENTS": "Слишком много мест (больше чем команд в группах)",
|
||||
"progression.error.PLACEMENT_TOO_HIGH": "",
|
||||
"progression.error.DUPLICATE_BRACKET_NAME": "Дубликат имени сетки",
|
||||
"progression.error.NAME_MISSING": "Имя сетки отсутствует",
|
||||
"progression.error.NEGATIVE_PROGRESSION": "Отрицательная прогрессия возможна только в Double Elimination турнирах",
|
||||
|
|
|
|||
|
|
@ -166,6 +166,7 @@
|
|||
"progression.error.SAME_PLACEMENT_TO_MULTIPLE_BRACKETS": "",
|
||||
"progression.error.GAP_IN_PLACEMENTS": "",
|
||||
"progression.error.TOO_MANY_PLACEMENTS": "",
|
||||
"progression.error.PLACEMENT_TOO_HIGH": "",
|
||||
"progression.error.DUPLICATE_BRACKET_NAME": "",
|
||||
"progression.error.NAME_MISSING": "",
|
||||
"progression.error.NEGATIVE_PROGRESSION": "",
|
||||
|
|
|
|||
38
migrations/138-cap-tournament-placements.js
Normal file
38
migrations/138-cap-tournament-placements.js
Normal file
|
|
@ -0,0 +1,38 @@
|
|||
const PLACEMENT_MAX = 100;
|
||||
|
||||
export function up(db) {
|
||||
const rows = db
|
||||
.prepare(/* sql */ `select "id", "settings" from "Tournament"`)
|
||||
.all();
|
||||
|
||||
const updates = [];
|
||||
for (const row of rows) {
|
||||
const settings = JSON.parse(row.settings);
|
||||
const progression = settings.bracketProgression;
|
||||
if (!Array.isArray(progression)) continue;
|
||||
|
||||
let changed = false;
|
||||
for (const bracket of progression) {
|
||||
for (const source of bracket.sources ?? []) {
|
||||
if (!Array.isArray(source.placements)) continue;
|
||||
const filtered = source.placements.filter((p) => p <= PLACEMENT_MAX);
|
||||
if (filtered.length !== source.placements.length) {
|
||||
source.placements = filtered;
|
||||
changed = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (changed) {
|
||||
updates.push({ id: row.id, settings: JSON.stringify(settings) });
|
||||
}
|
||||
}
|
||||
|
||||
const update = db.prepare(
|
||||
/* sql */ `update "Tournament" set "settings" = ? where "id" = ?`,
|
||||
);
|
||||
|
||||
for (const u of updates) {
|
||||
update.run(u.settings, u.id);
|
||||
}
|
||||
}
|
||||
Loading…
Reference in New Issue
Block a user