A layout switch during a running open practice kept every driver's old-layout best lap, blocking personal-best calls and skewing the practice leaderboard. Best laps now restart on a new layout and each lap records its layout. The track record baseline now includes the current session so the same record isn't called twice. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
131 lines
6.5 KiB
JavaScript
131 lines
6.5 KiB
JavaScript
import test from "node:test";
|
|
import assert from "node:assert/strict";
|
|
import { normalizeTrackLayout, normalizeTrackLayouts, getTrackLayoutIdAt, getTrackLayoutLabel, nextTrackLayoutLabel, isPracticeSessionType, findRaceSessionBlockingLayoutChange, isTrackLayoutInUse } from "../src/track_layouts_logic.js";
|
|
import { getTrackRecordMs } from "../src/timing_logic.js";
|
|
|
|
test("normalizeTrackLayout fills in an id via uid() when missing and coerces startedAt", () => {
|
|
const withoutId = normalizeTrackLayout({ label: "Layout 1", startedAt: 1000 }, { uid: (prefix) => `${prefix}_generated` });
|
|
assert.equal(withoutId.id, "layout_generated");
|
|
assert.equal(withoutId.startedAt, 1000);
|
|
|
|
const malformed = normalizeTrackLayout({ id: "x", label: " Layout 2 ", startedAt: "not-a-number" });
|
|
assert.equal(malformed.label, "Layout 2");
|
|
assert.ok(Number.isFinite(malformed.startedAt), "falls back to Date.now() rather than NaN");
|
|
});
|
|
|
|
test("normalizeTrackLayouts sorts chronologically and drops entries with no id", () => {
|
|
const layouts = normalizeTrackLayouts([
|
|
{ id: "b", label: "Later", startedAt: 2000 },
|
|
{ id: "a", label: "Earlier", startedAt: 1000 },
|
|
{ label: "" },
|
|
], { uid: () => "" });
|
|
assert.deepEqual(layouts.map((layout) => layout.id), ["a", "b"]);
|
|
});
|
|
|
|
test("getTrackLayoutIdAt returns the latest layout that has already started, treating layouts as contiguous", () => {
|
|
const layouts = [
|
|
{ id: "layout_1", label: "Layout 1", startedAt: 1000 },
|
|
{ id: "layout_2", label: "Layout 2", startedAt: 2000 },
|
|
{ id: "layout_3", label: "Layout 3", startedAt: 3000 },
|
|
];
|
|
assert.equal(getTrackLayoutIdAt(layouts, 500), "", "before any layout existed");
|
|
assert.equal(getTrackLayoutIdAt(layouts, 1000), "layout_1", "exactly at a layout's start");
|
|
assert.equal(getTrackLayoutIdAt(layouts, 2500), "layout_2", "between two layouts belongs to the earlier one");
|
|
assert.equal(getTrackLayoutIdAt(layouts, 9999), "layout_3", "after the last change, still the latest layout");
|
|
});
|
|
|
|
test("getTrackLayoutIdAt and getTrackLayoutLabel are inert with no layouts configured", () => {
|
|
assert.equal(getTrackLayoutIdAt([], Date.now()), "");
|
|
assert.equal(getTrackLayoutLabel([], ""), "");
|
|
assert.equal(getTrackLayoutLabel([{ id: "layout_1", label: "Layout 1" }], ""), "", "an empty id never matches a real layout");
|
|
});
|
|
|
|
test("getTrackLayoutLabel resolves a known id and nextTrackLayoutLabel numbers sequentially", () => {
|
|
const layouts = [{ id: "layout_1", label: "Layout 1", startedAt: 1000 }];
|
|
assert.equal(getTrackLayoutLabel(layouts, "layout_1"), "Layout 1");
|
|
assert.equal(getTrackLayoutLabel(layouts, "missing"), "");
|
|
assert.equal(nextTrackLayoutLabel([]), "Layout 1");
|
|
assert.equal(nextTrackLayoutLabel(layouts), "Layout 2");
|
|
});
|
|
|
|
function createTrackRecordState({ sessions, results }) {
|
|
return {
|
|
events: [{ id: "event_1", classId: "class_1" }],
|
|
sessions,
|
|
resultsBySession: results,
|
|
};
|
|
}
|
|
|
|
test("getTrackRecordMs finds the fastest lap among other sessions in the same class when no layout is set", () => {
|
|
const state = createTrackRecordState({
|
|
sessions: [
|
|
{ id: "session_1", eventId: "event_1" },
|
|
{ id: "session_2", eventId: "event_1" },
|
|
],
|
|
results: {
|
|
session_2: { competitors: { a: { bestLapMs: 12345 } } },
|
|
},
|
|
});
|
|
assert.equal(getTrackRecordMs(state.sessions[0], { state }), 12345);
|
|
});
|
|
|
|
test("getTrackRecordMs only compares sessions run on the same track layout once layouts are in use", () => {
|
|
const state = createTrackRecordState({
|
|
sessions: [
|
|
{ id: "current", eventId: "event_1", trackLayoutId: "layout_2" },
|
|
{ id: "same_layout", eventId: "event_1", trackLayoutId: "layout_2" },
|
|
{ id: "old_layout", eventId: "event_1", trackLayoutId: "layout_1" },
|
|
],
|
|
results: {
|
|
same_layout: { competitors: { a: { bestLapMs: 15000 } } },
|
|
old_layout: { competitors: { a: { bestLapMs: 9000 } } },
|
|
},
|
|
});
|
|
// The 9000ms lap belongs to a different, earlier layout - it must not count as the record
|
|
// for a lap set on the current layout, even though it is numerically faster.
|
|
assert.equal(getTrackRecordMs(state.sessions[0], { state }), 15000);
|
|
});
|
|
|
|
test("getTrackRecordMs includes the current session, so a record beaten earlier in it is the new bar", () => {
|
|
const state = createTrackRecordState({
|
|
sessions: [
|
|
{ id: "current", eventId: "event_1", trackLayoutId: "layout_1" },
|
|
{ id: "older", eventId: "event_1", trackLayoutId: "layout_1" },
|
|
],
|
|
results: {
|
|
older: { competitors: { a: { bestLapMs: 16000 } } },
|
|
current: { competitors: { b: { bestLapMs: 15000 } } },
|
|
},
|
|
});
|
|
// Driver B already set 15.0 here; a 15.5 must not be called a track record again.
|
|
assert.equal(getTrackRecordMs(state.sessions[0], { state }), 15000);
|
|
});
|
|
|
|
test("getTrackRecordMs does not conflate a layout-tagged session with an untagged legacy one", () => {
|
|
const state = createTrackRecordState({
|
|
sessions: [
|
|
{ id: "current", eventId: "event_1", trackLayoutId: "layout_1" },
|
|
{ id: "legacy", eventId: "event_1" },
|
|
],
|
|
results: {
|
|
legacy: { competitors: { a: { bestLapMs: 5000 } } },
|
|
},
|
|
});
|
|
assert.equal(getTrackRecordMs(state.sessions[0], { state }), null, "the legacy session (no trackLayoutId) is a different bucket from layout_1");
|
|
});
|
|
|
|
test("layouts change only around practice: a running or paused race session blocks it", () => {
|
|
assert.equal(isPracticeSessionType("open_practice"), true);
|
|
assert.equal(isPracticeSessionType("free_practice"), true);
|
|
assert.equal(isPracticeSessionType("qualification"), false);
|
|
assert.equal(findRaceSessionBlockingLayoutChange([{ type: "open_practice", status: "running" }, { type: "final", status: "ready" }]), null, "running practice and a not-yet-started final don't block");
|
|
assert.equal(findRaceSessionBlockingLayoutChange([{ name: "Q1", type: "qualification", status: "paused" }])?.name, "Q1");
|
|
assert.equal(findRaceSessionBlockingLayoutChange([{ name: "Heat", type: "heat", status: "running" }])?.name, "Heat");
|
|
});
|
|
|
|
test("a layout is in use when a session started on it or a practice lap was driven on it", () => {
|
|
assert.equal(isTrackLayoutInUse("layout_2", { sessions: [{ trackLayoutId: "layout_1" }], ledger: [] }), false);
|
|
assert.equal(isTrackLayoutInUse("layout_2", { sessions: [{ trackLayoutId: "layout_2" }] }), true);
|
|
assert.equal(isTrackLayoutInUse("layout_2", { sessions: [{ trackLayoutId: "layout_1" }], ledger: [{ trackLayoutId: "layout_2" }] }), true, "a mid-session switch leaves laps on layout_2 even though no session started on it");
|
|
});
|