Fix Team Race duplicate-membership bug and two related team-roster gaps

Reported by the user: creating a team, adding the wrong driver, then
moving that driver to another team by ticking them on the new team's
edit form left the driver on both teams at once. Root cause: there
was never a "move" action - the edit form only ever added a driver to
the team being saved, never removed them from wherever they already
were. Live laps from a driver on two teams were then silently
credited to only the first-matching team, with no clear error.

reassignTeamMembers (event_race_logic.js) now strips a driver/car
being saved onto one team out of every other team in the same event,
and the team checkbox list shows which team a driver is already on
before you move them.

Also fixed two related gaps found while investigating, at the user's
request to fix everything: getTeamDriverPool had regressed to
ignoring the event's class/participant scoping entirely (showing
every driver in the club), and deleting a driver or car left them
behind in team rosters forever. Added a duplicate_team_members Race
Day preflight check as defense in depth for any already-corrupted
saved state.

Verified with new unit tests for reassignTeamMembers, getTeamDriverPool
and the new preflight check, plus a new permanent browser test
reproducing the user's exact reported scenario end to end.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
This commit is contained in:
larssand
2026-09-10 17:07:45 +02:00
parent 71b3451b13
commit 15b574a37d
16 changed files with 261 additions and 15 deletions

View File

@@ -67,6 +67,31 @@ test("open practice allows an unset lap window but still requires the main decod
assert.equal(result.ready, false);
});
test("team_race preflight blocks a driver saved onto two teams at once, naming them instead of a generic transponder error", () => {
const state = createState();
state.sessions.push({ id: "session_team", eventId: "event_1", name: "Team Race", mode: "race", type: "team_race", status: "ready", startMode: "mass" });
state.events[0].raceConfig.teams = [
{ id: "team_a", name: "Team A", driverIds: ["driver_1"], carIds: [] },
{ id: "team_b", name: "Team B", driverIds: ["driver_1", "driver_2"], carIds: [] },
];
const result = buildRaceDayPreflight(state, state.sessions.find((s) => s.id === "session_team"), { backendAvailable: true });
assert.equal(result.ready, false);
const duplicateCheck = result.checks.find((item) => item.id === "duplicate_team_members");
assert.equal(duplicateCheck.level, "error");
assert.deepEqual(duplicateCheck.duplicateTeamMemberNames, ["One"]);
});
test("team_race preflight accepts teams with no members in common", () => {
const state = createState();
state.sessions.push({ id: "session_team", eventId: "event_1", name: "Team Race", mode: "race", type: "team_race", status: "ready", startMode: "mass" });
state.events[0].raceConfig.teams = [
{ id: "team_a", name: "Team A", driverIds: ["driver_1"], carIds: [] },
{ id: "team_b", name: "Team B", driverIds: ["driver_2"], carIds: [] },
];
const result = buildRaceDayPreflight(state, state.sessions.find((s) => s.id === "session_team"), { backendAvailable: true });
assert.equal(result.checks.find((item) => item.id === "duplicate_team_members").level, "ok");
});
test("preflight presents blockers before warnings without changing the original checks", async () => {
const { sortRaceDayChecks } = await import("../src/race_day_view.js");
const checks = [{ id: "audio", level: "warning" }, { id: "session", level: "ok" }, { id: "decoder", level: "error" }];