mirror of
https://github.com/Sendouc/sendou.ink.git
synced 2026-05-26 05:24:03 -05:00
1308 lines
32 KiB
TypeScript
1308 lines
32 KiB
TypeScript
import { describe, expect, it } from "vitest";
|
|
import type { TournamentRoundMaps } from "~/db/tables";
|
|
import type { ModeShort, StageId } from "~/modules/in-game-lists/types";
|
|
import {
|
|
CUSTOM_FLOW_VALIDATION_ERRORS,
|
|
currentTurnSessionStartedAt,
|
|
isModeLegal,
|
|
mapsListWithLegality,
|
|
type PickBanEvent,
|
|
type PickBanTeam,
|
|
resolveCurrentStep,
|
|
resolveTeamFromSide,
|
|
teamOfEvent,
|
|
turnOf,
|
|
validateCustomFlowSection,
|
|
} from "./PickBan";
|
|
|
|
describe("validateCustomFlowSection", () => {
|
|
it("returns no errors for valid preSet steps", () => {
|
|
const steps = [
|
|
{ action: "BAN" as const, side: "HIGHER_SEED" as const },
|
|
{ action: "BAN" as const, side: "LOWER_SEED" as const },
|
|
{ action: "PICK" as const, side: "HIGHER_SEED" as const },
|
|
];
|
|
|
|
expect(validateCustomFlowSection(steps, "preSet")).toEqual([]);
|
|
});
|
|
|
|
it("returns no errors for valid postGame steps", () => {
|
|
const steps = [
|
|
{ action: "BAN" as const, side: "WINNER" as const },
|
|
{ action: "PICK" as const, side: "LOSER" as const },
|
|
];
|
|
|
|
expect(validateCustomFlowSection(steps, "postGame")).toEqual([]);
|
|
});
|
|
|
|
it("returns STEP_MISSING_ACTION when a step has no action", () => {
|
|
const steps = [
|
|
{ side: "ALPHA" as const },
|
|
{ action: "PICK" as const, side: "ALPHA" as const },
|
|
];
|
|
|
|
expect(validateCustomFlowSection(steps, "preSet")).toContain(
|
|
CUSTOM_FLOW_VALIDATION_ERRORS.STEP_MISSING_ACTION,
|
|
);
|
|
});
|
|
|
|
it("returns STEP_MISSING_WHO when a non-ROLL step has no side", () => {
|
|
const steps = [{ action: "BAN" as const }];
|
|
|
|
expect(validateCustomFlowSection(steps, "preSet")).toContain(
|
|
CUSTOM_FLOW_VALIDATION_ERRORS.STEP_MISSING_WHO,
|
|
);
|
|
});
|
|
|
|
it("does not require side for ROLL steps", () => {
|
|
const steps = [{ action: "ROLL" as const }];
|
|
|
|
expect(validateCustomFlowSection(steps, "preSet")).toEqual([]);
|
|
});
|
|
|
|
it("returns LAST_STEP_MUST_BE_PICK_OR_ROLL when last step is BAN", () => {
|
|
const steps = [
|
|
{ action: "PICK" as const, side: "ALPHA" as const },
|
|
{ action: "BAN" as const, side: "BRAVO" as const },
|
|
];
|
|
|
|
expect(validateCustomFlowSection(steps, "preSet")).toContain(
|
|
CUSTOM_FLOW_VALIDATION_ERRORS.LAST_STEP_MUST_BE_PICK_OR_ROLL,
|
|
);
|
|
});
|
|
|
|
it("returns LAST_STEP_MUST_BE_PICK_OR_ROLL when last step is MODE_BAN", () => {
|
|
const steps = [{ action: "MODE_BAN" as const, side: "ALPHA" as const }];
|
|
|
|
expect(validateCustomFlowSection(steps, "preSet")).toContain(
|
|
CUSTOM_FLOW_VALIDATION_ERRORS.LAST_STEP_MUST_BE_PICK_OR_ROLL,
|
|
);
|
|
});
|
|
|
|
it("allows PICK as last step", () => {
|
|
const steps = [{ action: "PICK" as const, side: "ALPHA" as const }];
|
|
|
|
const errors = validateCustomFlowSection(steps, "preSet");
|
|
|
|
expect(errors).not.toContain(
|
|
CUSTOM_FLOW_VALIDATION_ERRORS.LAST_STEP_MUST_BE_PICK_OR_ROLL,
|
|
);
|
|
});
|
|
|
|
it("allows ROLL as last step", () => {
|
|
const steps = [{ action: "ROLL" as const }];
|
|
|
|
const errors = validateCustomFlowSection(steps, "postGame");
|
|
|
|
expect(errors).not.toContain(
|
|
CUSTOM_FLOW_VALIDATION_ERRORS.LAST_STEP_MUST_BE_PICK_OR_ROLL,
|
|
);
|
|
});
|
|
|
|
it("returns WINNER_LOSER_IN_PRE_SET when WINNER is used in preSet", () => {
|
|
const steps = [{ action: "PICK" as const, side: "WINNER" as const }];
|
|
|
|
expect(validateCustomFlowSection(steps, "preSet")).toContain(
|
|
CUSTOM_FLOW_VALIDATION_ERRORS.WINNER_LOSER_IN_PRE_SET,
|
|
);
|
|
});
|
|
|
|
it("returns WINNER_LOSER_IN_PRE_SET when LOSER is used in preSet", () => {
|
|
const steps = [{ action: "PICK" as const, side: "LOSER" as const }];
|
|
|
|
expect(validateCustomFlowSection(steps, "preSet")).toContain(
|
|
CUSTOM_FLOW_VALIDATION_ERRORS.WINNER_LOSER_IN_PRE_SET,
|
|
);
|
|
});
|
|
|
|
it("allows WINNER/LOSER in postGame", () => {
|
|
const steps = [
|
|
{ action: "BAN" as const, side: "WINNER" as const },
|
|
{ action: "PICK" as const, side: "LOSER" as const },
|
|
];
|
|
|
|
expect(validateCustomFlowSection(steps, "postGame")).toEqual([]);
|
|
});
|
|
|
|
it("returns TOO_MANY_MODE_PICKS when more than one MODE_PICK", () => {
|
|
const steps = [
|
|
{ action: "MODE_PICK" as const, side: "ALPHA" as const },
|
|
{ action: "MODE_PICK" as const, side: "BRAVO" as const },
|
|
{ action: "PICK" as const, side: "ALPHA" as const },
|
|
];
|
|
|
|
expect(validateCustomFlowSection(steps, "preSet")).toContain(
|
|
CUSTOM_FLOW_VALIDATION_ERRORS.TOO_MANY_MODE_PICKS,
|
|
);
|
|
});
|
|
|
|
it("returns TOO_MANY_MAP_PICKS when section has PICK and ROLL", () => {
|
|
const steps = [
|
|
{ action: "BAN" as const, side: "ALPHA" as const },
|
|
{ action: "PICK" as const, side: "BRAVO" as const },
|
|
{ action: "ROLL" as const },
|
|
];
|
|
|
|
expect(validateCustomFlowSection(steps, "preSet")).toContain(
|
|
CUSTOM_FLOW_VALIDATION_ERRORS.TOO_MANY_MAP_PICKS,
|
|
);
|
|
});
|
|
|
|
it("returns TOO_MANY_MAP_PICKS when section has two ROLLs", () => {
|
|
const steps = [{ action: "ROLL" as const }, { action: "ROLL" as const }];
|
|
|
|
expect(validateCustomFlowSection(steps, "preSet")).toContain(
|
|
CUSTOM_FLOW_VALIDATION_ERRORS.TOO_MANY_MAP_PICKS,
|
|
);
|
|
});
|
|
|
|
it("returns TOO_MANY_MAP_PICKS when section has two PICKs", () => {
|
|
const steps = [
|
|
{ action: "PICK" as const, side: "ALPHA" as const },
|
|
{ action: "MODE_BAN" as const, side: "BRAVO" as const },
|
|
{ action: "PICK" as const, side: "BRAVO" as const },
|
|
];
|
|
|
|
expect(validateCustomFlowSection(steps, "preSet")).toContain(
|
|
CUSTOM_FLOW_VALIDATION_ERRORS.TOO_MANY_MAP_PICKS,
|
|
);
|
|
});
|
|
|
|
it("allows exactly one PICK or ROLL", () => {
|
|
const stepsWithPick = [
|
|
{ action: "BAN" as const, side: "ALPHA" as const },
|
|
{ action: "PICK" as const, side: "BRAVO" as const },
|
|
];
|
|
const stepsWithRoll = [
|
|
{ action: "BAN" as const, side: "ALPHA" as const },
|
|
{ action: "ROLL" as const },
|
|
];
|
|
|
|
expect(validateCustomFlowSection(stepsWithPick, "preSet")).not.toContain(
|
|
CUSTOM_FLOW_VALIDATION_ERRORS.TOO_MANY_MAP_PICKS,
|
|
);
|
|
expect(validateCustomFlowSection(stepsWithRoll, "preSet")).not.toContain(
|
|
CUSTOM_FLOW_VALIDATION_ERRORS.TOO_MANY_MAP_PICKS,
|
|
);
|
|
});
|
|
|
|
it("allows exactly one MODE_PICK", () => {
|
|
const steps = [
|
|
{ action: "MODE_PICK" as const, side: "ALPHA" as const },
|
|
{ action: "PICK" as const, side: "BRAVO" as const },
|
|
];
|
|
|
|
const errors = validateCustomFlowSection(steps, "preSet");
|
|
|
|
expect(errors).not.toContain(
|
|
CUSTOM_FLOW_VALIDATION_ERRORS.TOO_MANY_MODE_PICKS,
|
|
);
|
|
});
|
|
|
|
it("returns LAST_STEP_MUST_BE_PICK_OR_ROLL for empty steps array", () => {
|
|
expect(validateCustomFlowSection([], "preSet")).toContain(
|
|
CUSTOM_FLOW_VALIDATION_ERRORS.LAST_STEP_MUST_BE_PICK_OR_ROLL,
|
|
);
|
|
});
|
|
|
|
it("returns SAME_TEAM_MODE_AND_MAP_PICK when same side does MODE_PICK and PICK", () => {
|
|
const steps = [
|
|
{ action: "MODE_PICK" as const, side: "ALPHA" as const },
|
|
{ action: "PICK" as const, side: "ALPHA" as const },
|
|
];
|
|
|
|
expect(validateCustomFlowSection(steps, "preSet")).toContain(
|
|
CUSTOM_FLOW_VALIDATION_ERRORS.SAME_TEAM_MODE_AND_MAP_PICK,
|
|
);
|
|
});
|
|
|
|
it("returns SAME_TEAM_MODE_AND_MAP_PICK even with bans between", () => {
|
|
const steps = [
|
|
{ action: "MODE_PICK" as const, side: "HIGHER_SEED" as const },
|
|
{ action: "BAN" as const, side: "LOWER_SEED" as const },
|
|
{ action: "PICK" as const, side: "HIGHER_SEED" as const },
|
|
];
|
|
|
|
expect(validateCustomFlowSection(steps, "preSet")).toContain(
|
|
CUSTOM_FLOW_VALIDATION_ERRORS.SAME_TEAM_MODE_AND_MAP_PICK,
|
|
);
|
|
});
|
|
|
|
it("does not return SAME_TEAM_MODE_AND_MAP_PICK when different sides", () => {
|
|
const steps = [
|
|
{ action: "MODE_PICK" as const, side: "ALPHA" as const },
|
|
{ action: "PICK" as const, side: "BRAVO" as const },
|
|
];
|
|
|
|
expect(validateCustomFlowSection(steps, "preSet")).not.toContain(
|
|
CUSTOM_FLOW_VALIDATION_ERRORS.SAME_TEAM_MODE_AND_MAP_PICK,
|
|
);
|
|
});
|
|
|
|
it("does not return SAME_TEAM_MODE_AND_MAP_PICK for MODE_PICK followed by ROLL", () => {
|
|
const steps = [
|
|
{ action: "MODE_PICK" as const, side: "ALPHA" as const },
|
|
{ action: "ROLL" as const },
|
|
];
|
|
|
|
expect(validateCustomFlowSection(steps, "preSet")).not.toContain(
|
|
CUSTOM_FLOW_VALIDATION_ERRORS.SAME_TEAM_MODE_AND_MAP_PICK,
|
|
);
|
|
});
|
|
|
|
it("can return multiple errors at once", () => {
|
|
const steps = [
|
|
{ action: "MODE_PICK" as const, side: "WINNER" as const },
|
|
{ action: "MODE_PICK" as const, side: "LOSER" as const },
|
|
{ action: "MODE_BAN" as const, side: "ALPHA" as const },
|
|
];
|
|
|
|
const errors = validateCustomFlowSection(steps, "preSet");
|
|
|
|
expect(errors).toContain(
|
|
CUSTOM_FLOW_VALIDATION_ERRORS.WINNER_LOSER_IN_PRE_SET,
|
|
);
|
|
expect(errors).toContain(CUSTOM_FLOW_VALIDATION_ERRORS.TOO_MANY_MODE_PICKS);
|
|
expect(errors).toContain(
|
|
CUSTOM_FLOW_VALIDATION_ERRORS.LAST_STEP_MUST_BE_PICK_OR_ROLL,
|
|
);
|
|
});
|
|
});
|
|
|
|
describe("resolveCurrentStep", () => {
|
|
const preSet = [
|
|
{ action: "BAN" as const, side: "HIGHER_SEED" as const },
|
|
{ action: "BAN" as const, side: "LOWER_SEED" as const },
|
|
{ action: "PICK" as const, side: "HIGHER_SEED" as const },
|
|
];
|
|
const postGame = [
|
|
{ action: "BAN" as const, side: "WINNER" as const },
|
|
{ action: "PICK" as const, side: "LOSER" as const },
|
|
];
|
|
|
|
it("returns preSet steps when eventCount < preSet.length", () => {
|
|
expect(
|
|
resolveCurrentStep({ eventCount: 0, preSet, postGame, resultsCount: 0 }),
|
|
).toEqual(preSet[0]);
|
|
expect(
|
|
resolveCurrentStep({ eventCount: 1, preSet, postGame, resultsCount: 0 }),
|
|
).toEqual(preSet[1]);
|
|
expect(
|
|
resolveCurrentStep({ eventCount: 2, preSet, postGame, resultsCount: 0 }),
|
|
).toEqual(preSet[2]);
|
|
});
|
|
|
|
it("returns null when waiting for game result after preSet", () => {
|
|
expect(
|
|
resolveCurrentStep({ eventCount: 3, preSet, postGame, resultsCount: 0 }),
|
|
).toBeNull();
|
|
});
|
|
|
|
it("throws when postGame is empty", () => {
|
|
expect(() =>
|
|
resolveCurrentStep({
|
|
eventCount: 3,
|
|
preSet,
|
|
postGame: [],
|
|
resultsCount: 1,
|
|
}),
|
|
).toThrow();
|
|
});
|
|
|
|
it("returns postGame steps after first game result", () => {
|
|
expect(
|
|
resolveCurrentStep({ eventCount: 3, preSet, postGame, resultsCount: 1 }),
|
|
).toEqual(postGame[0]);
|
|
expect(
|
|
resolveCurrentStep({ eventCount: 4, preSet, postGame, resultsCount: 1 }),
|
|
).toEqual(postGame[1]);
|
|
});
|
|
|
|
it("returns null when waiting for next game result after postGame cycle", () => {
|
|
expect(
|
|
resolveCurrentStep({ eventCount: 5, preSet, postGame, resultsCount: 1 }),
|
|
).toBeNull();
|
|
});
|
|
|
|
it("cycles postGame steps after subsequent results", () => {
|
|
expect(
|
|
resolveCurrentStep({ eventCount: 5, preSet, postGame, resultsCount: 2 }),
|
|
).toEqual(postGame[0]);
|
|
expect(
|
|
resolveCurrentStep({ eventCount: 6, preSet, postGame, resultsCount: 2 }),
|
|
).toEqual(postGame[1]);
|
|
});
|
|
});
|
|
|
|
describe("resolveTeamFromSide", () => {
|
|
const teams: [PickBanTeam, PickBanTeam] = [
|
|
{ id: 100, seed: 2 },
|
|
{ id: 200, seed: 1 },
|
|
];
|
|
|
|
it("resolves ALPHA to teams[0]", () => {
|
|
expect(resolveTeamFromSide({ side: "ALPHA", teams, results: [] })).toBe(
|
|
100,
|
|
);
|
|
});
|
|
|
|
it("resolves BRAVO to teams[1]", () => {
|
|
expect(resolveTeamFromSide({ side: "BRAVO", teams, results: [] })).toBe(
|
|
200,
|
|
);
|
|
});
|
|
|
|
it("resolves HIGHER_SEED to teams[1]", () => {
|
|
expect(
|
|
resolveTeamFromSide({ side: "HIGHER_SEED", teams, results: [] }),
|
|
).toBe(200);
|
|
});
|
|
|
|
it("resolves LOWER_SEED to teams[0]", () => {
|
|
expect(
|
|
resolveTeamFromSide({ side: "LOWER_SEED", teams, results: [] }),
|
|
).toBe(100);
|
|
});
|
|
|
|
it("resolves HIGHER_SEED by seed, not array position", () => {
|
|
const swappedTeams: [PickBanTeam, PickBanTeam] = [
|
|
{ id: 200, seed: 1 },
|
|
{ id: 100, seed: 2 },
|
|
];
|
|
|
|
expect(
|
|
resolveTeamFromSide({
|
|
side: "HIGHER_SEED",
|
|
teams: swappedTeams,
|
|
results: [],
|
|
}),
|
|
).toBe(200);
|
|
});
|
|
|
|
it("resolves LOWER_SEED by seed, not array position", () => {
|
|
const swappedTeams: [PickBanTeam, PickBanTeam] = [
|
|
{ id: 200, seed: 1 },
|
|
{ id: 100, seed: 2 },
|
|
];
|
|
|
|
expect(
|
|
resolveTeamFromSide({
|
|
side: "LOWER_SEED",
|
|
teams: swappedTeams,
|
|
results: [],
|
|
}),
|
|
).toBe(100);
|
|
});
|
|
|
|
it("resolves WINNER to last game winner", () => {
|
|
expect(
|
|
resolveTeamFromSide({
|
|
side: "WINNER",
|
|
teams,
|
|
results: [{ winnerTeamId: 200 }],
|
|
}),
|
|
).toBe(200);
|
|
});
|
|
|
|
it("resolves LOSER to last game loser", () => {
|
|
expect(
|
|
resolveTeamFromSide({
|
|
side: "LOSER",
|
|
teams,
|
|
results: [{ winnerTeamId: 200 }],
|
|
}),
|
|
).toBe(100);
|
|
});
|
|
});
|
|
|
|
describe("turnOf — CUSTOM flow", () => {
|
|
const customMaps: TournamentRoundMaps = {
|
|
count: 5,
|
|
type: "BEST_OF",
|
|
pickBan: "CUSTOM",
|
|
customFlow: {
|
|
preSet: [
|
|
{ action: "BAN", side: "HIGHER_SEED" },
|
|
{ action: "BAN", side: "LOWER_SEED" },
|
|
{ action: "PICK", side: "HIGHER_SEED" },
|
|
],
|
|
postGame: [
|
|
{ action: "BAN", side: "WINNER" },
|
|
{ action: "PICK", side: "LOSER" },
|
|
],
|
|
},
|
|
};
|
|
const teams: [PickBanTeam, PickBanTeam] = [
|
|
{ id: 100, seed: 2 },
|
|
{ id: 200, seed: 1 },
|
|
];
|
|
|
|
it("returns first preSet step", () => {
|
|
const result = turnOf({
|
|
results: [],
|
|
maps: customMaps,
|
|
teams,
|
|
pickBanEventCount: 0,
|
|
});
|
|
|
|
expect(result).toEqual({
|
|
teamId: 200,
|
|
action: "BAN",
|
|
stepCurrent: 1,
|
|
stepTotal: 1,
|
|
});
|
|
});
|
|
|
|
it("returns second preSet step", () => {
|
|
const result = turnOf({
|
|
results: [],
|
|
maps: customMaps,
|
|
teams,
|
|
pickBanEventCount: 1,
|
|
});
|
|
|
|
expect(result).toEqual({
|
|
teamId: 100,
|
|
action: "BAN",
|
|
stepCurrent: 1,
|
|
stepTotal: 1,
|
|
});
|
|
});
|
|
|
|
it("returns null when waiting for game result", () => {
|
|
const result = turnOf({
|
|
results: [],
|
|
maps: customMaps,
|
|
teams,
|
|
pickBanEventCount: 3,
|
|
});
|
|
|
|
expect(result).toBeNull();
|
|
});
|
|
|
|
it("returns postGame step after result", () => {
|
|
const result = turnOf({
|
|
results: [{ winnerTeamId: 200 }],
|
|
maps: customMaps,
|
|
teams,
|
|
pickBanEventCount: 3,
|
|
});
|
|
|
|
expect(result).toEqual({
|
|
teamId: 200,
|
|
action: "BAN",
|
|
stepCurrent: 1,
|
|
stepTotal: 1,
|
|
});
|
|
});
|
|
|
|
it("returns null for ROLL steps", () => {
|
|
const rollMaps: TournamentRoundMaps = {
|
|
count: 3,
|
|
type: "BEST_OF",
|
|
pickBan: "CUSTOM",
|
|
customFlow: {
|
|
preSet: [{ action: "ROLL" }],
|
|
postGame: [],
|
|
},
|
|
};
|
|
|
|
const result = turnOf({
|
|
results: [],
|
|
maps: rollMaps,
|
|
teams,
|
|
pickBanEventCount: 0,
|
|
});
|
|
|
|
expect(result).toBeNull();
|
|
});
|
|
|
|
it("returns null when set is over", () => {
|
|
const result = turnOf({
|
|
results: [
|
|
{ winnerTeamId: 200 },
|
|
{ winnerTeamId: 200 },
|
|
{ winnerTeamId: 200 },
|
|
],
|
|
maps: customMaps,
|
|
teams,
|
|
pickBanEventCount: 7,
|
|
});
|
|
|
|
expect(result).toBeNull();
|
|
});
|
|
|
|
it("returns null when no customFlow defined", () => {
|
|
const result = turnOf({
|
|
results: [],
|
|
maps: { count: 3, type: "BEST_OF", pickBan: "CUSTOM" },
|
|
teams,
|
|
pickBanEventCount: 0,
|
|
});
|
|
|
|
expect(result).toBeNull();
|
|
});
|
|
});
|
|
|
|
describe("turnOf — CUSTOM flow stepCurrent/stepTotal", () => {
|
|
const teams: [PickBanTeam, PickBanTeam] = [
|
|
{ id: 100, seed: 2 },
|
|
{ id: 200, seed: 1 },
|
|
];
|
|
|
|
it("counts consecutive bans by same side in preSet", () => {
|
|
const maps: TournamentRoundMaps = {
|
|
count: 5,
|
|
type: "BEST_OF",
|
|
pickBan: "CUSTOM",
|
|
customFlow: {
|
|
preSet: [
|
|
{ action: "BAN", side: "HIGHER_SEED" },
|
|
{ action: "BAN", side: "HIGHER_SEED" },
|
|
{ action: "BAN", side: "LOWER_SEED" },
|
|
{ action: "PICK", side: "LOWER_SEED" },
|
|
],
|
|
postGame: [{ action: "PICK", side: "LOSER" }],
|
|
},
|
|
};
|
|
|
|
expect(
|
|
turnOf({ results: [], maps, teams, pickBanEventCount: 0 }),
|
|
).toMatchObject({ stepCurrent: 1, stepTotal: 2 });
|
|
|
|
expect(
|
|
turnOf({ results: [], maps, teams, pickBanEventCount: 1 }),
|
|
).toMatchObject({ stepCurrent: 2, stepTotal: 2 });
|
|
|
|
expect(
|
|
turnOf({ results: [], maps, teams, pickBanEventCount: 2 }),
|
|
).toMatchObject({ stepCurrent: 1, stepTotal: 1 });
|
|
});
|
|
|
|
it("counts consecutive bans by same side in postGame", () => {
|
|
const maps: TournamentRoundMaps = {
|
|
count: 5,
|
|
type: "BEST_OF",
|
|
pickBan: "CUSTOM",
|
|
customFlow: {
|
|
preSet: [{ action: "PICK", side: "HIGHER_SEED" }],
|
|
postGame: [
|
|
{ action: "BAN", side: "WINNER" },
|
|
{ action: "BAN", side: "WINNER" },
|
|
{ action: "PICK", side: "LOSER" },
|
|
],
|
|
},
|
|
};
|
|
|
|
expect(
|
|
turnOf({
|
|
results: [{ winnerTeamId: 200 }],
|
|
maps,
|
|
teams,
|
|
pickBanEventCount: 1,
|
|
}),
|
|
).toMatchObject({ stepCurrent: 1, stepTotal: 2 });
|
|
|
|
expect(
|
|
turnOf({
|
|
results: [{ winnerTeamId: 200 }],
|
|
maps,
|
|
teams,
|
|
pickBanEventCount: 2,
|
|
}),
|
|
).toMatchObject({ stepCurrent: 2, stepTotal: 2 });
|
|
|
|
expect(
|
|
turnOf({
|
|
results: [{ winnerTeamId: 200 }],
|
|
maps,
|
|
teams,
|
|
pickBanEventCount: 3,
|
|
}),
|
|
).toMatchObject({ stepCurrent: 1, stepTotal: 1 });
|
|
});
|
|
|
|
it("does not group consecutive steps with different sides", () => {
|
|
const maps: TournamentRoundMaps = {
|
|
count: 5,
|
|
type: "BEST_OF",
|
|
pickBan: "CUSTOM",
|
|
customFlow: {
|
|
preSet: [
|
|
{ action: "BAN", side: "HIGHER_SEED" },
|
|
{ action: "BAN", side: "LOWER_SEED" },
|
|
{ action: "PICK", side: "HIGHER_SEED" },
|
|
],
|
|
postGame: [{ action: "PICK", side: "LOSER" }],
|
|
},
|
|
};
|
|
|
|
expect(
|
|
turnOf({ results: [], maps, teams, pickBanEventCount: 0 }),
|
|
).toMatchObject({ stepCurrent: 1, stepTotal: 1 });
|
|
|
|
expect(
|
|
turnOf({ results: [], maps, teams, pickBanEventCount: 1 }),
|
|
).toMatchObject({ stepCurrent: 1, stepTotal: 1 });
|
|
});
|
|
|
|
it("does not group consecutive steps with different actions", () => {
|
|
const maps: TournamentRoundMaps = {
|
|
count: 5,
|
|
type: "BEST_OF",
|
|
pickBan: "CUSTOM",
|
|
customFlow: {
|
|
preSet: [
|
|
{ action: "MODE_BAN", side: "HIGHER_SEED" },
|
|
{ action: "BAN", side: "HIGHER_SEED" },
|
|
{ action: "PICK", side: "HIGHER_SEED" },
|
|
],
|
|
postGame: [{ action: "PICK", side: "LOSER" }],
|
|
},
|
|
};
|
|
|
|
expect(
|
|
turnOf({ results: [], maps, teams, pickBanEventCount: 0 }),
|
|
).toMatchObject({ stepCurrent: 1, stepTotal: 1 });
|
|
|
|
expect(
|
|
turnOf({ results: [], maps, teams, pickBanEventCount: 1 }),
|
|
).toMatchObject({ stepCurrent: 1, stepTotal: 1 });
|
|
});
|
|
});
|
|
|
|
describe("turnOf — BAN_2 flow", () => {
|
|
const ban2Maps: TournamentRoundMaps = {
|
|
count: 5,
|
|
type: "BEST_OF",
|
|
pickBan: "BAN_2",
|
|
};
|
|
const teams: [PickBanTeam, PickBanTeam] = [
|
|
{ id: 100, seed: 2 },
|
|
{ id: 200, seed: 1 },
|
|
];
|
|
|
|
it("returns action BAN for first picker", () => {
|
|
const result = turnOf({
|
|
results: [],
|
|
maps: ban2Maps,
|
|
teams,
|
|
mapList: [
|
|
{
|
|
mode: "SZ",
|
|
stageId: 1,
|
|
source: "TO",
|
|
bannedByTournamentTeamId: undefined,
|
|
},
|
|
{
|
|
mode: "SZ",
|
|
stageId: 2,
|
|
source: "TO",
|
|
bannedByTournamentTeamId: undefined,
|
|
},
|
|
],
|
|
});
|
|
|
|
expect(result).toEqual({ teamId: 200, action: "BAN" });
|
|
});
|
|
|
|
it("returns null when both teams have banned", () => {
|
|
const result = turnOf({
|
|
results: [],
|
|
maps: ban2Maps,
|
|
teams,
|
|
mapList: [
|
|
{ mode: "SZ", stageId: 1, source: "TO", bannedByTournamentTeamId: 200 },
|
|
{ mode: "SZ", stageId: 2, source: "TO", bannedByTournamentTeamId: 100 },
|
|
],
|
|
});
|
|
|
|
expect(result).toBeNull();
|
|
});
|
|
});
|
|
|
|
describe("mapsListWithLegality — MODE_PICK restriction survives intervening events", () => {
|
|
const SZ = "SZ" as ModeShort;
|
|
const TC = "TC" as ModeShort;
|
|
const RM = "RM" as ModeShort;
|
|
|
|
const toSetMapPool = [
|
|
{ mode: SZ, stageId: 1 as StageId },
|
|
{ mode: SZ, stageId: 2 as StageId },
|
|
{ mode: TC, stageId: 3 as StageId },
|
|
{ mode: TC, stageId: 4 as StageId },
|
|
{ mode: RM, stageId: 5 as StageId },
|
|
];
|
|
|
|
const teams = [{ mapPool: [] }, { mapPool: [] }] as unknown as Parameters<
|
|
typeof mapsListWithLegality
|
|
>[0]["teams"];
|
|
|
|
const customMaps: TournamentRoundMaps = {
|
|
count: 5,
|
|
type: "BEST_OF",
|
|
pickBan: "CUSTOM",
|
|
customFlow: {
|
|
preSet: [
|
|
{ action: "MODE_PICK", side: "HIGHER_SEED" },
|
|
{ action: "BAN", side: "LOWER_SEED" },
|
|
{ action: "BAN", side: "LOWER_SEED" },
|
|
{ action: "PICK", side: "LOWER_SEED" },
|
|
],
|
|
postGame: [{ action: "PICK", side: "LOSER" }],
|
|
},
|
|
};
|
|
|
|
it("restricts to picked mode even when bans happen after MODE_PICK", () => {
|
|
const pickBanEvents: PickBanEvent[] = [
|
|
{ type: "MODE_PICK", stageId: null, mode: SZ },
|
|
{ type: "BAN", stageId: 3 as StageId, mode: TC },
|
|
{ type: "BAN", stageId: 5 as StageId, mode: RM },
|
|
];
|
|
|
|
const result = mapsListWithLegality({
|
|
results: [],
|
|
maps: customMaps,
|
|
mapList: null,
|
|
teams,
|
|
pickerTeamId: 100,
|
|
tieBreakerMapPool: [],
|
|
toSetMapPool,
|
|
pickBanEvents,
|
|
});
|
|
|
|
const legalModes = new Set(
|
|
result.filter((m) => m.isLegal).map((m) => m.mode),
|
|
);
|
|
|
|
// MODE_PICK chose SZ, so only SZ maps should be legal
|
|
expect(legalModes).toEqual(new Set([SZ]));
|
|
// TC and RM should not be legal
|
|
expect(legalModes.has(TC)).toBe(false);
|
|
expect(legalModes.has(RM)).toBe(false);
|
|
});
|
|
|
|
it("does not carry MODE_PICK restriction from a previous game section", () => {
|
|
const mapsWithPostGameModePick: TournamentRoundMaps = {
|
|
count: 5,
|
|
type: "BEST_OF",
|
|
pickBan: "CUSTOM",
|
|
customFlow: {
|
|
preSet: [
|
|
{ action: "MODE_PICK", side: "HIGHER_SEED" },
|
|
{ action: "PICK", side: "LOWER_SEED" },
|
|
],
|
|
postGame: [
|
|
{ action: "MODE_BAN", side: "WINNER" },
|
|
{ action: "PICK", side: "LOSER" },
|
|
],
|
|
},
|
|
};
|
|
|
|
// preSet: MODE_PICK(SZ), PICK — then game 1 played
|
|
// postGame cycle 1: MODE_BAN(TC), now at PICK step with no MODE_PICK in this section
|
|
const pickBanEvents: PickBanEvent[] = [
|
|
{ type: "MODE_PICK", stageId: null, mode: SZ },
|
|
{ type: "PICK", stageId: 1 as StageId, mode: SZ },
|
|
{ type: "MODE_BAN", stageId: null, mode: TC },
|
|
];
|
|
|
|
const result = mapsListWithLegality({
|
|
results: [{ mode: SZ, stageId: 1 as StageId, winnerTeamId: 200 }],
|
|
maps: mapsWithPostGameModePick,
|
|
mapList: null,
|
|
teams,
|
|
pickerTeamId: 100,
|
|
tieBreakerMapPool: [],
|
|
toSetMapPool,
|
|
pickBanEvents,
|
|
});
|
|
|
|
const legalModes = new Set(
|
|
result.filter((m) => m.isLegal).map((m) => m.mode),
|
|
);
|
|
|
|
// No MODE_PICK in the current postGame section, so SZ restriction should not apply
|
|
// Only TC is mode-banned, SZ and RM should be legal
|
|
expect(legalModes).toEqual(new Set([SZ, RM]));
|
|
expect(legalModes.has(TC)).toBe(false);
|
|
});
|
|
});
|
|
|
|
describe("mapsListWithLegality — pre-set MODE_BAN persists into postGame", () => {
|
|
const SZ = "SZ" as ModeShort;
|
|
const TC = "TC" as ModeShort;
|
|
const RM = "RM" as ModeShort;
|
|
|
|
const toSetMapPool = [
|
|
{ mode: SZ, stageId: 1 as StageId },
|
|
{ mode: SZ, stageId: 2 as StageId },
|
|
{ mode: TC, stageId: 3 as StageId },
|
|
{ mode: TC, stageId: 4 as StageId },
|
|
{ mode: RM, stageId: 5 as StageId },
|
|
];
|
|
|
|
const teams = [{ mapPool: [] }, { mapPool: [] }] as unknown as Parameters<
|
|
typeof mapsListWithLegality
|
|
>[0]["teams"];
|
|
|
|
const customMaps: TournamentRoundMaps = {
|
|
count: 5,
|
|
type: "BEST_OF",
|
|
pickBan: "CUSTOM",
|
|
customFlow: {
|
|
preSet: [{ action: "MODE_BAN", side: "HIGHER_SEED" }, { action: "ROLL" }],
|
|
postGame: [
|
|
{ action: "BAN", side: "WINNER" },
|
|
{ action: "PICK", side: "LOSER" },
|
|
],
|
|
},
|
|
};
|
|
|
|
it("keeps a mode banned in pre-set unavailable for picks in later postGame cycles", () => {
|
|
// preSet: HIGHER_SEED bans mode SZ, ROLL lands on TC stage 3
|
|
// game 1: TC stage 3 played, team 200 wins
|
|
// postGame cycle 1: WINNER (200) bans stage 4 (TC); LOSER (100) is now at PICK
|
|
const pickBanEvents: PickBanEvent[] = [
|
|
{ type: "MODE_BAN", stageId: null, mode: SZ },
|
|
{ type: "ROLL", stageId: 3 as StageId, mode: TC },
|
|
{ type: "BAN", stageId: 4 as StageId, mode: TC },
|
|
];
|
|
|
|
const result = mapsListWithLegality({
|
|
results: [{ mode: TC, stageId: 3 as StageId, winnerTeamId: 200 }],
|
|
maps: customMaps,
|
|
mapList: null,
|
|
teams,
|
|
pickerTeamId: 100,
|
|
tieBreakerMapPool: [],
|
|
toSetMapPool,
|
|
pickBanEvents,
|
|
});
|
|
|
|
const legalModes = new Set(
|
|
result.filter((m) => m.isLegal).map((m) => m.mode),
|
|
);
|
|
|
|
expect(legalModes.has(SZ)).toBe(false);
|
|
expect(legalModes).toEqual(new Set([RM]));
|
|
});
|
|
});
|
|
|
|
describe("isModeLegal", () => {
|
|
const SZ = "SZ" as ModeShort;
|
|
const TC = "TC" as ModeShort;
|
|
const RM = "RM" as ModeShort;
|
|
const CB = "CB" as ModeShort;
|
|
|
|
const toSetMapPool = [
|
|
{ mode: SZ, stageId: 1 as StageId },
|
|
{ mode: SZ, stageId: 2 as StageId },
|
|
{ mode: TC, stageId: 3 as StageId },
|
|
{ mode: RM, stageId: 5 as StageId },
|
|
];
|
|
|
|
const teams = [{ mapPool: [] }, { mapPool: [] }] as unknown as Parameters<
|
|
typeof mapsListWithLegality
|
|
>[0]["teams"];
|
|
|
|
const customMaps: TournamentRoundMaps = {
|
|
count: 5,
|
|
type: "BEST_OF",
|
|
pickBan: "CUSTOM",
|
|
customFlow: {
|
|
preSet: [
|
|
{ action: "MODE_BAN", side: "HIGHER_SEED" },
|
|
{ action: "MODE_PICK", side: "LOWER_SEED" },
|
|
{ action: "PICK", side: "LOWER_SEED" },
|
|
],
|
|
postGame: [{ action: "PICK", side: "LOSER" }],
|
|
},
|
|
};
|
|
|
|
const baseArgs = {
|
|
results: [],
|
|
maps: customMaps,
|
|
mapList: null,
|
|
teams,
|
|
pickerTeamId: 100,
|
|
tieBreakerMapPool: [],
|
|
toSetMapPool,
|
|
};
|
|
|
|
it("returns true for a mode present in the pool with no bans", () => {
|
|
expect(
|
|
isModeLegal({
|
|
mode: TC,
|
|
...baseArgs,
|
|
pickBanEvents: [],
|
|
}),
|
|
).toBe(true);
|
|
});
|
|
|
|
it("returns false for a mode that has been banned", () => {
|
|
const pickBanEvents: PickBanEvent[] = [
|
|
{ type: "MODE_BAN", stageId: null, mode: TC },
|
|
];
|
|
|
|
expect(
|
|
isModeLegal({
|
|
mode: TC,
|
|
...baseArgs,
|
|
pickBanEvents,
|
|
}),
|
|
).toBe(false);
|
|
});
|
|
|
|
it("returns false for a mode not in the map pool", () => {
|
|
expect(
|
|
isModeLegal({
|
|
mode: CB,
|
|
...baseArgs,
|
|
pickBanEvents: [],
|
|
}),
|
|
).toBe(false);
|
|
});
|
|
});
|
|
|
|
describe("turnOf — COUNTERPICK flow", () => {
|
|
const cpMaps: TournamentRoundMaps = {
|
|
count: 3,
|
|
type: "BEST_OF",
|
|
pickBan: "COUNTERPICK",
|
|
};
|
|
const teams: [PickBanTeam, PickBanTeam] = [
|
|
{ id: 100, seed: 2 },
|
|
{ id: 200, seed: 1 },
|
|
];
|
|
|
|
it("returns action PICK for loser of last game", () => {
|
|
const result = turnOf({
|
|
results: [{ winnerTeamId: 200 }],
|
|
maps: cpMaps,
|
|
teams,
|
|
mapList: [
|
|
{
|
|
mode: "SZ",
|
|
stageId: 1,
|
|
source: "TO",
|
|
bannedByTournamentTeamId: undefined,
|
|
},
|
|
],
|
|
});
|
|
|
|
expect(result).toEqual({ teamId: 100, action: "PICK" });
|
|
});
|
|
|
|
it("returns null when match was completed without per-game results (drop-out)", () => {
|
|
const result = turnOf({
|
|
results: [],
|
|
maps: cpMaps,
|
|
teams,
|
|
mapList: [],
|
|
});
|
|
|
|
expect(result).toBeNull();
|
|
});
|
|
});
|
|
|
|
describe("teamOfEvent", () => {
|
|
const teams: [PickBanTeam, PickBanTeam] = [
|
|
{ id: 100, seed: 2 },
|
|
{ id: 200, seed: 1 },
|
|
];
|
|
|
|
it("returns null when setup is not pick/ban", () => {
|
|
const result = teamOfEvent({
|
|
eventIndex: 0,
|
|
maps: { count: 3, type: "BEST_OF" },
|
|
teams,
|
|
results: [],
|
|
});
|
|
|
|
expect(result).toBeNull();
|
|
});
|
|
|
|
describe("BAN_2", () => {
|
|
const ban2Maps: TournamentRoundMaps = {
|
|
count: 3,
|
|
type: "BEST_OF",
|
|
pickBan: "BAN_2",
|
|
};
|
|
|
|
it("assigns event 0 to teams[1] (first picker)", () => {
|
|
expect(
|
|
teamOfEvent({ eventIndex: 0, maps: ban2Maps, teams, results: [] }),
|
|
).toBe(200);
|
|
});
|
|
|
|
it("assigns event 1 to teams[0] (second picker)", () => {
|
|
expect(
|
|
teamOfEvent({ eventIndex: 1, maps: ban2Maps, teams, results: [] }),
|
|
).toBe(100);
|
|
});
|
|
|
|
it("returns null for further indices", () => {
|
|
expect(
|
|
teamOfEvent({ eventIndex: 2, maps: ban2Maps, teams, results: [] }),
|
|
).toBeNull();
|
|
});
|
|
});
|
|
|
|
describe("COUNTERPICK", () => {
|
|
const cpMaps: TournamentRoundMaps = {
|
|
count: 5,
|
|
type: "BEST_OF",
|
|
pickBan: "COUNTERPICK",
|
|
};
|
|
|
|
it("attributes the counterpick to the loser of the preceding result", () => {
|
|
const result = teamOfEvent({
|
|
eventIndex: 0,
|
|
maps: cpMaps,
|
|
teams,
|
|
results: [{ winnerTeamId: 100 }],
|
|
});
|
|
|
|
expect(result).toBe(200);
|
|
});
|
|
|
|
it("also works for COUNTERPICK_MODE_REPEAT_OK", () => {
|
|
const result = teamOfEvent({
|
|
eventIndex: 1,
|
|
maps: { ...cpMaps, pickBan: "COUNTERPICK_MODE_REPEAT_OK" },
|
|
teams,
|
|
results: [{ winnerTeamId: 100 }, { winnerTeamId: 200 }],
|
|
});
|
|
|
|
expect(result).toBe(100);
|
|
});
|
|
|
|
it("returns null when no corresponding result exists", () => {
|
|
const result = teamOfEvent({
|
|
eventIndex: 0,
|
|
maps: cpMaps,
|
|
teams,
|
|
results: [],
|
|
});
|
|
|
|
expect(result).toBeNull();
|
|
});
|
|
});
|
|
|
|
describe("CUSTOM", () => {
|
|
const customMaps: TournamentRoundMaps = {
|
|
count: 5,
|
|
type: "BEST_OF",
|
|
pickBan: "CUSTOM",
|
|
customFlow: {
|
|
preSet: [
|
|
{ action: "BAN", side: "HIGHER_SEED" },
|
|
{ action: "BAN", side: "LOWER_SEED" },
|
|
],
|
|
postGame: [
|
|
{ action: "BAN", side: "WINNER" },
|
|
{ action: "PICK", side: "LOSER" },
|
|
],
|
|
},
|
|
};
|
|
|
|
it("resolves preSet steps via side (HIGHER_SEED → teams[1])", () => {
|
|
expect(
|
|
teamOfEvent({ eventIndex: 0, maps: customMaps, teams, results: [] }),
|
|
).toBe(200);
|
|
});
|
|
|
|
it("resolves preSet steps via side (LOWER_SEED → teams[0])", () => {
|
|
expect(
|
|
teamOfEvent({ eventIndex: 1, maps: customMaps, teams, results: [] }),
|
|
).toBe(100);
|
|
});
|
|
|
|
it("resolves postGame WINNER using the result of that cycle", () => {
|
|
const result = teamOfEvent({
|
|
eventIndex: 2,
|
|
maps: customMaps,
|
|
teams,
|
|
results: [{ winnerTeamId: 100 }],
|
|
});
|
|
|
|
expect(result).toBe(100);
|
|
});
|
|
|
|
it("resolves postGame LOSER using the result of that cycle", () => {
|
|
const result = teamOfEvent({
|
|
eventIndex: 3,
|
|
maps: customMaps,
|
|
teams,
|
|
results: [{ winnerTeamId: 100 }],
|
|
});
|
|
|
|
expect(result).toBe(200);
|
|
});
|
|
|
|
it("uses the correct cycle's result across multiple post-game cycles", () => {
|
|
const result = teamOfEvent({
|
|
eventIndex: 4,
|
|
maps: customMaps,
|
|
teams,
|
|
results: [{ winnerTeamId: 100 }, { winnerTeamId: 200 }],
|
|
});
|
|
|
|
expect(result).toBe(200);
|
|
});
|
|
|
|
it("returns null when customFlow is missing", () => {
|
|
const result = teamOfEvent({
|
|
eventIndex: 0,
|
|
maps: { count: 3, type: "BEST_OF", pickBan: "CUSTOM" },
|
|
teams,
|
|
results: [],
|
|
});
|
|
|
|
expect(result).toBeNull();
|
|
});
|
|
|
|
it("returns null for ROLL steps (no side)", () => {
|
|
const rollMaps: TournamentRoundMaps = {
|
|
count: 3,
|
|
type: "BEST_OF",
|
|
pickBan: "CUSTOM",
|
|
customFlow: {
|
|
preSet: [{ action: "ROLL" }],
|
|
postGame: [{ action: "PICK", side: "LOSER" }],
|
|
},
|
|
};
|
|
|
|
expect(
|
|
teamOfEvent({ eventIndex: 0, maps: rollMaps, teams, results: [] }),
|
|
).toBeNull();
|
|
});
|
|
});
|
|
});
|
|
|
|
describe("currentTurnSessionStartedAt", () => {
|
|
const teams: [PickBanTeam, PickBanTeam] = [
|
|
{ id: 100, seed: 2 },
|
|
{ id: 200, seed: 1 },
|
|
];
|
|
|
|
it("returns null when there is no current turn", () => {
|
|
const result = currentTurnSessionStartedAt({
|
|
currentTurn: null,
|
|
events: [],
|
|
results: [],
|
|
matchStartedAt: 1000,
|
|
maps: { count: 3, type: "BEST_OF", pickBan: "BAN_2" },
|
|
teams,
|
|
});
|
|
|
|
expect(result).toBeNull();
|
|
});
|
|
|
|
it("returns null when matchStartedAt is null", () => {
|
|
const result = currentTurnSessionStartedAt({
|
|
currentTurn: { teamId: 200, action: "BAN" },
|
|
events: [],
|
|
results: [],
|
|
matchStartedAt: null,
|
|
maps: { count: 3, type: "BEST_OF", pickBan: "BAN_2" },
|
|
teams,
|
|
});
|
|
|
|
expect(result).toBeNull();
|
|
});
|
|
|
|
it("falls back to matchStartedAt when no events or results exist", () => {
|
|
const result = currentTurnSessionStartedAt({
|
|
currentTurn: { teamId: 200, action: "BAN" },
|
|
events: [],
|
|
results: [],
|
|
matchStartedAt: 1000,
|
|
maps: { count: 3, type: "BEST_OF", pickBan: "BAN_2" },
|
|
teams,
|
|
});
|
|
|
|
expect(result).toBe(1000);
|
|
});
|
|
|
|
it("BAN_2: second banner's session starts at the first ban's timestamp", () => {
|
|
const result = currentTurnSessionStartedAt({
|
|
currentTurn: { teamId: 100, action: "BAN" },
|
|
events: [{ createdAt: 1500 }],
|
|
results: [],
|
|
matchStartedAt: 1000,
|
|
maps: { count: 3, type: "BEST_OF", pickBan: "BAN_2" },
|
|
teams,
|
|
});
|
|
|
|
expect(result).toBe(1500);
|
|
});
|
|
|
|
it("COUNTERPICK: loser's session starts when the result is reported", () => {
|
|
const result = currentTurnSessionStartedAt({
|
|
currentTurn: { teamId: 200, action: "PICK" },
|
|
events: [],
|
|
results: [{ createdAt: 2000, winnerTeamId: 100 }],
|
|
matchStartedAt: 1000,
|
|
maps: { count: 5, type: "BEST_OF", pickBan: "COUNTERPICK" },
|
|
teams,
|
|
});
|
|
|
|
expect(result).toBe(2000);
|
|
});
|
|
|
|
it("CUSTOM: consecutive same-team events share the session start", () => {
|
|
const customMaps: TournamentRoundMaps = {
|
|
count: 5,
|
|
type: "BEST_OF",
|
|
pickBan: "CUSTOM",
|
|
customFlow: {
|
|
preSet: [
|
|
{ action: "BAN", side: "HIGHER_SEED" },
|
|
{ action: "BAN", side: "HIGHER_SEED" },
|
|
{ action: "BAN", side: "LOWER_SEED" },
|
|
],
|
|
postGame: [{ action: "PICK", side: "LOSER" }],
|
|
},
|
|
};
|
|
|
|
const result = currentTurnSessionStartedAt({
|
|
currentTurn: { teamId: 200, action: "BAN" },
|
|
events: [{ createdAt: 1500 }],
|
|
results: [],
|
|
matchStartedAt: 1000,
|
|
maps: customMaps,
|
|
teams,
|
|
});
|
|
|
|
expect(result).toBe(1000);
|
|
});
|
|
|
|
it("CUSTOM: a result restarts the session even when the same team is responsible again", () => {
|
|
const customMaps: TournamentRoundMaps = {
|
|
count: 5,
|
|
type: "BEST_OF",
|
|
pickBan: "CUSTOM",
|
|
customFlow: {
|
|
preSet: [{ action: "BAN", side: "HIGHER_SEED" }],
|
|
postGame: [
|
|
{ action: "PICK", side: "LOSER" },
|
|
{ action: "BAN", side: "LOSER" },
|
|
],
|
|
},
|
|
};
|
|
|
|
const result = currentTurnSessionStartedAt({
|
|
currentTurn: { teamId: 100, action: "BAN" },
|
|
events: [{ createdAt: 1100 }, { createdAt: 2500 }],
|
|
results: [{ createdAt: 2000, winnerTeamId: 200 }],
|
|
matchStartedAt: 1000,
|
|
maps: customMaps,
|
|
teams,
|
|
});
|
|
|
|
expect(result).toBe(2000);
|
|
});
|
|
});
|