Files
gods-eye-view/src/data/trafficFlowStyle.test.mjs
T

107 lines
4.2 KiB
JavaScript

// src/data/trafficFlowStyle.test.mjs
import { test } from 'node:test';
import assert from 'node:assert/strict';
import {
flowBucket,
flowColor,
flowSpeedScale,
flowDensityMult,
FLOW_BUCKET_RGBA,
} from './trafficFlowStyle.js';
// ── bucket thresholds ───────────────────────────────────────
test('flowBucket: >= 0.85 is free-flowing', () => {
assert.equal(flowBucket(1), 'free');
assert.equal(flowBucket(0.85), 'free');
assert.equal(flowBucket(0.9), 'free');
});
test('flowBucket: [0.55, 0.85) is slow', () => {
assert.equal(flowBucket(0.849999), 'slow');
assert.equal(flowBucket(0.7), 'slow');
assert.equal(flowBucket(0.55), 'slow');
});
test('flowBucket: < 0.55 is jammed', () => {
assert.equal(flowBucket(0.549999), 'jam');
assert.equal(flowBucket(0.2), 'jam');
assert.equal(flowBucket(0), 'jam');
});
test('flowBucket: non-finite input degrades to free (no false alarms)', () => {
assert.equal(flowBucket(NaN), 'free');
assert.equal(flowBucket(undefined), 'free');
});
// ── colors ──────────────────────────────────────────────────
test('flowColor returns the rgba tuple for the level bucket', () => {
assert.deepEqual(flowColor(1), FLOW_BUCKET_RGBA.free);
assert.deepEqual(flowColor(0.7), FLOW_BUCKET_RGBA.slow);
assert.deepEqual(flowColor(0.1), FLOW_BUCKET_RGBA.jam);
});
test('bucket palette: green #2ecc71 / amber #f0b23e / red #e05252 @ alpha 0.9', () => {
assert.deepEqual(FLOW_BUCKET_RGBA.free, [46, 204, 113, 0.9]);
assert.deepEqual(FLOW_BUCKET_RGBA.slow, [240, 178, 62, 0.9]);
assert.deepEqual(FLOW_BUCKET_RGBA.jam, [224, 82, 82, 0.9]);
});
// ── speed scale ─────────────────────────────────────────────
test('flowSpeedScale: clamps level into [0.15, 1]', () => {
assert.equal(flowSpeedScale(1), 1);
assert.equal(flowSpeedScale(0.6), 0.6);
assert.equal(flowSpeedScale(0.15), 0.15);
assert.equal(flowSpeedScale(0.05), 0.15); // jam crawls, never freezes
assert.equal(flowSpeedScale(0), 0.15);
assert.equal(flowSpeedScale(1.5), 1);
});
test('flowSpeedScale: non-finite input keeps full speed (sim parity)', () => {
assert.equal(flowSpeedScale(NaN), 1);
assert.equal(flowSpeedScale(undefined), 1);
});
// ── density multiplier ──────────────────────────────────────
test('flowDensityMult: congestion packs more dots, capped at 2.5', () => {
assert.equal(flowDensityMult(1), 1);
assert.ok(Math.abs(flowDensityMult(0.5) - 2) < 1e-12);
assert.equal(flowDensityMult(0.4), 2.5);
assert.equal(flowDensityMult(0.1), 2.5); // 1/max(level, 0.4) caps the blowup
assert.equal(flowDensityMult(0), 2.5);
});
test('flowDensityMult: non-finite input keeps neutral density (sim parity)', () => {
assert.equal(flowDensityMult(NaN), 1);
assert.equal(flowDensityMult(undefined), 1);
});
// ── jam-boost density curve (jamViz density prototype) ──────
test('flowDensityMult jamBoost: identical to base curve down to level 0.4', () => {
assert.equal(flowDensityMult(1, { jamBoost: true }), 1);
assert.ok(Math.abs(flowDensityMult(0.5, { jamBoost: true }) - 2) < 1e-12);
assert.equal(flowDensityMult(0.4, { jamBoost: true }), 2.5);
});
test('flowDensityMult jamBoost: deep jams keep climbing to a 4.0 cap', () => {
assert.ok(Math.abs(flowDensityMult(0.3, { jamBoost: true }) - (1 / 0.3)) < 1e-12);
assert.equal(flowDensityMult(0.25, { jamBoost: true }), 4);
assert.equal(flowDensityMult(0.1, { jamBoost: true }), 4);
assert.equal(flowDensityMult(0, { jamBoost: true }), 4);
});
test('flowDensityMult jamBoost: non-finite input still neutral (sim parity)', () => {
assert.equal(flowDensityMult(NaN, { jamBoost: true }), 1);
assert.equal(flowDensityMult(undefined, { jamBoost: true }), 1);
});
test('flowDensityMult: opts omitted or jamBoost false keeps the shipped 2.5 cap', () => {
assert.equal(flowDensityMult(0.1), 2.5);
assert.equal(flowDensityMult(0.1, {}), 2.5);
assert.equal(flowDensityMult(0.1, { jamBoost: false }), 2.5);
});