Files
Live_RC/tests/session_control.test.js
larssand f0838bc5db Fix qualifying/finals/bump-up/judging/pause-timing bugs
A thorough audit of practice/qualifying/heats/finals, bump-up,
judging and pause/timer code (requested by the user) found several
real, concrete bugs, fixed together:

- Qualifying reseed sorted heats as text ("H10" before "H2"),
  scrambling grids at 10+ heats; now sorts numerically and reports a
  clear warning instead of silently dropping/ghosting heats when the
  participant count changes.
- A driver with zero laps in a finished heat/final (DNS) vanished
  entirely - not just from that round, but silently from every future
  reseeded round and the final grid. Now backfilled as a real,
  last-place "DNS" row.
- Bump-up did nothing at all with "reserve bump slots" off (mains
  were already seeded full, so the old capacity check always
  blocked it), and a promoted driver stayed double-entered in their
  old main's upcoming leg. An oversized bump count could also blow
  the main-letter naming past "Z". All three fixed.
- Judging undo restored an absolute snapshot instead of reversing its
  own delta - undoing two penalties out of order could leave a live
  penalty behind two "undone" log entries. Now always nets correctly
  regardless of undo order.
- A competitor's elapsed time didn't exclude paused time (only the
  session clock did), making a paused heat's results incomparable to
  an unpaused one in qualifying ranking. Finishing while paused also
  dropped the final pause segment, so the displayed elapsed time kept
  growing after finish. Both fixed.

Verified with new unit tests for every fix (including a new
session_control.test.js - that file had zero prior coverage) and a
real headless-browser run confirming the judging-undo and bump-up
fixes end to end through the actual UI.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-09-10 17:41:10 +02:00

62 lines
2.7 KiB
JavaScript

import test from "node:test";
import assert from "node:assert/strict";
import { beginSession, finishSession, pauseSession, resumeSession } from "../src/session_control.js";
test("beginSession resets pause/timing bookkeeping for a fresh start", () => {
const session = { status: "ready", pausedAt: 999, totalPausedMs: 5000, followUpPausedMs: 2000 };
beginSession(session, 1000);
assert.equal(session.status, "running");
assert.equal(session.startedAt, 1000);
assert.equal(session.pausedAt, null);
assert.equal(session.totalPausedMs, 0);
assert.equal(session.followUpPausedMs, 0);
});
test("pauseSession only takes effect on a running session", () => {
const session = { status: "running" };
assert.equal(pauseSession(session, 1000), true);
assert.equal(session.status, "paused");
assert.equal(session.pausedAt, 1000);
assert.equal(pauseSession({ status: "paused" }, 1000), false, "already paused");
assert.equal(pauseSession({ status: "ready" }, 1000), false, "not started yet");
});
test("resumeSession accumulates paused duration across multiple pause/resume cycles", () => {
const session = { status: "running", startedAt: 0, totalPausedMs: 0 };
pauseSession(session, 1000);
resumeSession(session, 3000); // paused 2000ms
assert.equal(session.totalPausedMs, 2000);
assert.equal(session.pausedAt, null);
assert.equal(session.status, "running");
pauseSession(session, 5000);
resumeSession(session, 5750); // paused another 750ms
assert.equal(session.totalPausedMs, 2750, "second pause segment adds to the running total, not replaces it");
});
test("resumeSession also folds the pause into an active follow-up period", () => {
const session = { status: "running", followUpStartedAt: 4000, followUpPausedMs: 0, totalPausedMs: 0 };
pauseSession(session, 4500);
resumeSession(session, 5200); // paused 700ms during follow-up
assert.equal(session.totalPausedMs, 700);
assert.equal(session.followUpPausedMs, 700);
});
test("finishSession while paused folds the still-open pause segment into totalPausedMs instead of dropping it", () => {
const session = { status: "paused", startedAt: 0, pausedAt: 10000, totalPausedMs: 2000 };
finishSession(session, 13000); // was paused for 3000ms when finished
assert.equal(session.status, "finished");
assert.equal(session.endedAt, 13000);
assert.equal(session.pausedAt, null);
assert.equal(session.totalPausedMs, 5000, "the 3000ms final pause segment is added to the 2000ms already accumulated");
});
test("finishSession on a running (not paused) session leaves totalPausedMs untouched", () => {
const session = { status: "running", startedAt: 0, totalPausedMs: 1500 };
finishSession(session, 20000);
assert.equal(session.totalPausedMs, 1500);
assert.equal(session.endedAt, 20000);
});