import test from "node:test"; import assert from "node:assert/strict"; import { isSuspectLap, getSuspectReferenceLaps, countsForBestLap, listPendingSuspectLaps, SUSPECT_LAP_RATIO, MIN_REFERENCE_LAPS } from "../src/suspect_laps_logic.js"; import { syncTrainingLedger } from "../src/training_logic.js"; const lap = (lapMs, extra = {}) => ({ competitorKey: "d1", lapMs, timestamp: lapMs, validLap: true, ...extra }); test("a lap well under 85 % of the driver's own median is suspect - only once there are 5 normal laps", () => { assert.equal(SUSPECT_LAP_RATIO, 0.85); assert.equal(MIN_REFERENCE_LAPS, 5); const reference = [20000, 20500, 19800, 20200, 21000]; // median 20 200 -> limit 17 170 assert.equal(isSuspectLap(17000, reference), true, "a shortcut"); assert.equal(isSuspectLap(17500, reference), false, "a genuinely good lap"); assert.equal(isSuspectLap(12000, reference.slice(0, 4)), false, "too little to compare with yet"); }); test("the reference is the driver's own recent normal laps on the same layout", () => { const passings = [ lap(20000), lap(20000), lap(30000, { stintBoundary: true }), lap(9000, { validLap: false }), lap(15000, { suspect: true }), lap(25000, { competitorKey: "other" }), lap(40000, { trackLayoutId: "old" }), lap(21000), ]; assert.deepEqual(getSuspectReferenceLaps(passings, "d1", ""), [20000, 20000, 21000]); }); test("suspect and removed laps never count for best lap; pending ones are listed newest first", () => { assert.equal(countsForBestLap(lap(15000, { suspect: true })), false); assert.equal(countsForBestLap(lap(15000, { validLap: false })), false); assert.equal(countsForBestLap(lap(15000, { suspectApproved: true })), true); const result = { passings: [lap(15000, { suspect: true, timestamp: 1 }), lap(16000, { suspect: true, validLap: false, timestamp: 2 }), lap(14000, { suspect: true, timestamp: 3 })] }; assert.deepEqual(listPendingSuspectLaps(result).map((passing) => passing.lapMs), [14000, 15000], "a removed suspect lap is no longer pending"); }); test("a suspect lap stays out of the training stats until it is approved", () => { let n = 0; const uid = (prefix) => `${prefix}_${++n}`; const state = { sessions: [{ id: "op", name: "Open Practice", eventId: "e" }], events: [{ id: "e", name: "Club" }], resultsBySession: { op: { passings: [{ ...lap(20000), driverId: "d1", timestamp: 1000 }, { ...lap(15000, { suspect: true }), driverId: "d1", timestamp: 2000 }] } }, }; assert.deepEqual(syncTrainingLedger({}, state, { uid }).ledger.map((entry) => entry.lapMs), [20000]); Object.assign(state.resultsBySession.op.passings[1], { suspect: false, suspectApproved: true }); assert.deepEqual(syncTrainingLedger({}, state, { uid }).ledger.map((entry) => entry.lapMs), [20000, 15000]); });