new raceday and backup

This commit is contained in:
larssand
2026-09-01 07:07:23 +02:00
parent c2bfa97d30
commit f561dd8feb
21 changed files with 1042 additions and 60 deletions

View File

@@ -0,0 +1,68 @@
import test from "node:test";
import assert from "node:assert/strict";
import { buildRaceDayPreflight, buildRaceDayQueue } from "../src/race_day_logic.js";
function createState() {
return {
activeSessionId: "session_1",
decoder: { connected: true, splitConnected: true, pitEntryConnected: true, pitExitConnected: true },
settings: { splitDecoderEnabled: false, pitDecoderEnabled: false, raceAudio: { enabled: true } },
classes: [{ id: "class_1", name: "2WD" }],
drivers: [
{ id: "driver_1", classId: "class_1", name: "One", transponder: "111" },
{ id: "driver_2", classId: "class_1", name: "Two", transponder: "222" },
],
cars: [],
events: [{
id: "event_1",
mode: "race",
classId: "class_1",
raceConfig: { participantsConfigured: true, driverIds: ["driver_1", "driver_2"], minLapMs: 10000, maxLapMs: 60000 },
}],
sessions: [
{ id: "session_1", eventId: "event_1", name: "Q1", mode: "race", type: "qualification", status: "ready", startMode: "mass", driverIds: ["driver_1", "driver_2"] },
{ id: "session_2", eventId: "event_1", name: "Q2", mode: "race", type: "qualification", status: "ready", startMode: "mass", driverIds: ["driver_1", "driver_2"] },
{ id: "session_3", eventId: "event_1", name: "A final", mode: "race", type: "final", status: "ready", startMode: "position", driverIds: ["driver_1", "driver_2"], manualGridIds: ["driver_1", "driver_2"] },
],
};
}
test("Race Day queue follows the selected event session order", () => {
const queue = buildRaceDayQueue(createState());
assert.equal(queue.current.id, "session_1");
assert.equal(queue.next.id, "session_2");
assert.equal(queue.afterNext.id, "session_3");
});
test("Race Day preflight accepts a complete race session", () => {
const state = createState();
const result = buildRaceDayPreflight(state, state.sessions[0], { backendAvailable: true });
assert.equal(result.ready, true);
assert.ok(result.checks.every((item) => item.level === "ok"));
});
test("Race Day preflight blocks decoder, transponder, duplicate and grid errors", () => {
const state = createState();
state.decoder.connected = false;
state.drivers[1].transponder = "111";
state.sessions[2].manualGridIds = ["driver_1"];
const result = buildRaceDayPreflight(state, state.sessions[2], { backendAvailable: true });
assert.equal(result.ready, false);
const levels = new Map(result.checks.map((item) => [item.id, item.level]));
assert.equal(levels.get("decoder"), "error");
assert.equal(levels.get("duplicate_transponders"), "error");
assert.equal(levels.get("grid"), "error");
});
test("open practice allows an unset lap window but still requires the main decoder", () => {
const state = createState();
state.events[0].raceConfig.minLapMs = 0;
state.events[0].raceConfig.maxLapMs = 0;
const practice = { ...state.sessions[0], type: "open_practice", driverIds: [] };
let result = buildRaceDayPreflight(state, practice, { backendAvailable: true });
assert.equal(result.ready, true);
assert.equal(result.checks.find((item) => item.id === "lap_window").level, "warning");
state.decoder.connected = false;
result = buildRaceDayPreflight(state, practice, { backendAvailable: true });
assert.equal(result.ready, false);
});