Files
Live_RC/tests/judging_logic.test.js
2026-08-24 18:58:40 +02:00

164 lines
6.2 KiB
JavaScript

import test from "node:test";
import assert from "node:assert/strict";
import {
applyCompetitorCorrection,
findPassingByUndoMarker,
getJudgeFilteredLog,
getJudgeFilteredRows,
getManualCorrectionSummary,
invalidateCompetitorLastLap,
recalculateCompetitorFromPassings,
restoreCompetitorLastInvalidLap,
undoJudgingAdjustment,
} from "../src/judging_logic.js";
function createHarness() {
const session = { id: "session_1", startedAt: 1000 };
const result = {
passings: [
{ competitorKey: "driver_1", timestamp: 17000, lapMs: 16000, validLap: true },
{ competitorKey: "driver_1", timestamp: 34000, lapMs: 17000, validLap: true },
{ competitorKey: "driver_1", timestamp: 50500, lapMs: 16500, validLap: true },
],
competitors: {
driver_1: {
key: "driver_1",
driverName: "Driver One",
startTimestamp: 1000,
laps: 3,
lastLapMs: 16500,
lastTimestamp: 50500,
bestLapMs: 16000,
manualLapAdjustment: 0,
manualTimeAdjustmentMs: 0,
},
},
adjustments: [],
};
let id = 0;
let saveCount = 0;
const ensureSessionResult = () => result;
const isCountedPassing = (passing) => passing.validLap !== false;
const getCompetitorPassings = (_session, row, options = {}) => result.passings
.filter((passing) => passing.competitorKey === row.key)
.filter((passing) => options.includeInvalid || isCountedPassing(passing))
.sort((left, right) => left.timestamp - right.timestamp);
const recalculate = (targetSession, rowKey) => recalculateCompetitorFromPassings(targetSession, rowKey, {
ensureSessionResult,
getCompetitorPassings,
isCountedPassing,
});
const findPassing = (targetSession, rowKey, timestamp) => findPassingByUndoMarker(targetSession, rowKey, timestamp, {
ensureSessionResult,
});
const deps = {
ensureSessionResult,
getCompetitorPassings,
isCountedPassing,
recalculateCompetitorFromPassings: recalculate,
findPassingByUndoMarker: findPassing,
uid: (prefix) => `${prefix}_${++id}`,
t: (key) => key,
formatLap: (lapMs) => `${lapMs}ms`,
saveState: () => { saveCount += 1; },
};
return {
session,
result,
row: result.competitors.driver_1,
deps,
getSaveCount: () => saveCount,
};
}
test("manual corrections accumulate, reset, and undo to the prior state", () => {
const harness = createHarness();
applyCompetitorCorrection(
harness.session,
harness.row,
{ lapDelta: -1, timeMsDelta: 5000 },
harness.deps
);
assert.equal(harness.row.manualLapAdjustment, -1);
assert.equal(harness.row.manualTimeAdjustmentMs, 5000);
assert.equal(getManualCorrectionSummary(harness.row, harness.deps), "-1L • +5000ms");
assert.equal(harness.result.adjustments[0].detail, "-1L • +5000ms");
applyCompetitorCorrection(harness.session, harness.row, { reset: true }, harness.deps);
assert.equal(harness.row.manualLapAdjustment, 0);
assert.equal(harness.row.manualTimeAdjustmentMs, 0);
const resetAdjustment = harness.result.adjustments[1];
assert.equal(undoJudgingAdjustment(harness.session, resetAdjustment.id, harness.deps), true);
assert.equal(harness.row.manualLapAdjustment, -1);
assert.equal(harness.row.manualTimeAdjustmentMs, 5000);
assert.equal(resetAdjustment.undoneAt > 0, true);
assert.equal(harness.result.adjustments.at(-1).category, "undo");
assert.equal(harness.getSaveCount(), 3);
});
test("invalidating and restoring the latest lap recalculates the competitor", () => {
const harness = createHarness();
assert.equal(invalidateCompetitorLastLap(harness.session, harness.row, harness.deps), true);
assert.equal(harness.result.passings[2].validLap, false);
assert.equal(harness.result.passings[2].invalidReason, "manual_invalid");
assert.equal(harness.row.laps, 2);
assert.equal(harness.row.lastLapMs, 17000);
assert.equal(harness.row.lastTimestamp, 34000);
assert.equal(harness.row.bestLapMs, 16000);
assert.equal(restoreCompetitorLastInvalidLap(harness.session, harness.row, harness.deps), true);
assert.equal(harness.result.passings[2].validLap, true);
assert.equal(harness.row.laps, 3);
assert.equal(harness.row.lastLapMs, 16500);
assert.equal(harness.row.lastTimestamp, 50500);
assert.equal(harness.getSaveCount(), 2);
});
test("undo restores passing validity and cannot be applied twice", () => {
const harness = createHarness();
invalidateCompetitorLastLap(harness.session, harness.row, harness.deps);
const invalidation = harness.result.adjustments[0];
assert.equal(undoJudgingAdjustment(harness.session, invalidation.id, harness.deps), true);
assert.equal(harness.result.passings[2].validLap, true);
assert.equal(harness.result.passings[2].invalidReason, "");
assert.equal(harness.row.laps, 3);
assert.equal(undoJudgingAdjustment(harness.session, invalidation.id, harness.deps), false);
});
test("lap invalidation safely rejects competitors without counted laps", () => {
const harness = createHarness();
harness.result.passings.forEach((passing) => {
passing.validLap = false;
});
assert.equal(invalidateCompetitorLastLap(harness.session, harness.row, harness.deps), false);
assert.equal(harness.result.adjustments.length, 0);
assert.equal(harness.getSaveCount(), 0);
});
test("judging row and action-log filters select only matching entries", () => {
const rows = [
{ key: "invalid", invalidPending: true },
{ key: "corrected", manualLapAdjustment: -1 },
{ key: "team", teamId: "team_1" },
{ key: "plain" },
];
const log = [
{ id: "correction", category: "correction" },
{ id: "invalid", category: "invalid" },
{ id: "undo", category: "undo" },
];
assert.deepEqual(getJudgeFilteredRows(rows, "invalid").map((row) => row.key), ["invalid"]);
assert.deepEqual(getJudgeFilteredRows(rows, "corrected").map((row) => row.key), ["corrected"]);
assert.deepEqual(getJudgeFilteredRows(rows, "team").map((row) => row.key), ["team"]);
assert.equal(getJudgeFilteredRows(rows, "all").length, 4);
assert.deepEqual(getJudgeFilteredLog(log, "corrections").map((entry) => entry.id), ["correction"]);
assert.deepEqual(getJudgeFilteredLog(log, "invalid").map((entry) => entry.id), ["invalid"]);
assert.deepEqual(getJudgeFilteredLog(log, "undo").map((entry) => entry.id), ["undo"]);
});