- hand-derived vectors from the plan's <polyline_vectors> block - external live-OSRM demo geometry (RESEARCH Finding 1) as ground truth - P7 pins the truncated-varint refusal law (T-05-02)
134 lines
5.5 KiB
JavaScript
134 lines
5.5 KiB
JavaScript
#!/usr/bin/env node
|
||
/*
|
||
* polyline.test.mjs — polyline6 codec vectors (P1–P8).
|
||
* -----------------------------------------------------
|
||
* RED first (plan 05-01 task 2): every case fails while
|
||
* routing/src/polyline.ts does not exist — the lazy-import pattern
|
||
* keeps each case reporting its own failure.
|
||
*
|
||
* Vectors are computed ground truth (the plan's <polyline_vectors> block,
|
||
* derived with the reference algorithm in the planning session), NOT
|
||
* guesses:
|
||
* P1 zero vector — decode("??") -> [{lat:0,lng:0}]; encode inverts
|
||
* P2 KL point vector — encode([{3.1578,101.7117}]) === "oqv_Egt~~`E"
|
||
* (note: the string contains a BACKTICK)
|
||
* P3 delta accumulation — the 3-point KL vector (deltas 100/100/100/200)
|
||
* P4 negative zigzag — the V4 vector (the ~ inversion branch)
|
||
* P5 external OSRM vector — live demo-server geometry (KLCC -> KL Sentral,
|
||
* probed 2026-10-24; RESEARCH Finding 1): first
|
||
* point decodes to {3.157846, 101.711716} (OSRM
|
||
* snapped the origin onto the road graph) and
|
||
* encode(decode(s)) === s byte-identically
|
||
* P6 empty input — decode("") -> []; encode([]) === ""
|
||
* P7 malformed refusal — decode("A") / decode("g") THROW (truncated
|
||
* varint — never a partial/NaN point, T-05-02)
|
||
* P8 round-trip law — encode(decode(s)) === s for EVERY vector here
|
||
*
|
||
* Run: npm run routing:test (exit 0 = all green)
|
||
*/
|
||
import { strict as assert } from "node:assert";
|
||
import { test } from "./harness.mjs";
|
||
|
||
// Lazy codec load — missing module => per-case ERR_MODULE_NOT_FOUND (the RED state).
|
||
let codecPromise = null;
|
||
function loadCodec() {
|
||
if (!codecPromise) {
|
||
codecPromise = import("../src/polyline.ts");
|
||
}
|
||
return codecPromise;
|
||
}
|
||
|
||
/* ————— the pinned vectors ————— */
|
||
|
||
const V_ZERO = "??";
|
||
const V_KL_POINT = "oqv_Egt~~`E";
|
||
const V_KL_THREE = "oqv_Egt~~`EgEgEgEoK";
|
||
const V_NEGATIVE = "~tpzA_pihC_pihCnt}oC";
|
||
const V_EXTERNAL =
|
||
"ktv_Egu~~`EsAlFaDnGeF`EmGvEmWbSySjOiTfUiOb`@{n@xZ}FhBsGx@cJ?";
|
||
|
||
const KL_POINT = { lat: 3.1578, lng: 101.7117 };
|
||
const KL_THREE_POINTS = [
|
||
{ lat: 3.1578, lng: 101.7117 },
|
||
{ lat: 3.1579, lng: 101.7118 },
|
||
{ lat: 3.158, lng: 101.712 },
|
||
];
|
||
const NEGATIVE_POINTS = [
|
||
{ lat: -1.5, lng: 2.25 },
|
||
{ lat: 0.75, lng: -0.125 },
|
||
];
|
||
|
||
/* ————— P1–P8 ————— */
|
||
|
||
test("P1 zero vector — decode('??') -> [{lat:0,lng:0}]; encode inverts exactly", async () => {
|
||
const { decodePolyline6, encodePolyline6 } = await loadCodec();
|
||
assert.deepEqual(decodePolyline6(V_ZERO), [{ lat: 0, lng: 0 }]);
|
||
assert.equal(encodePolyline6([{ lat: 0, lng: 0 }]), V_ZERO);
|
||
});
|
||
|
||
test("P2 KL point vector — encode([{3.1578,101.7117}]) === 'oqv_Egt~~`E' and decode inverts", async () => {
|
||
const { decodePolyline6, encodePolyline6 } = await loadCodec();
|
||
assert.equal(encodePolyline6([KL_POINT]), V_KL_POINT);
|
||
assert.deepEqual(decodePolyline6(V_KL_POINT), [KL_POINT]);
|
||
});
|
||
|
||
test("P3 delta accumulation — the 3-point KL vector encodes/decodes exactly", async () => {
|
||
const { decodePolyline6, encodePolyline6 } = await loadCodec();
|
||
assert.equal(encodePolyline6(KL_THREE_POINTS), V_KL_THREE);
|
||
assert.deepEqual(decodePolyline6(V_KL_THREE), KL_THREE_POINTS);
|
||
});
|
||
|
||
test("P4 negative zigzag — the V4 vector exercises the ~ inversion branch", async () => {
|
||
const { decodePolyline6, encodePolyline6 } = await loadCodec();
|
||
assert.equal(encodePolyline6(NEGATIVE_POINTS), V_NEGATIVE);
|
||
assert.deepEqual(decodePolyline6(V_NEGATIVE), NEGATIVE_POINTS);
|
||
});
|
||
|
||
test("P5 external OSRM vector — live demo geometry decodes and re-encodes byte-identically", async () => {
|
||
const { decodePolyline6, encodePolyline6 } = await loadCodec();
|
||
const first = decodePolyline6(V_EXTERNAL)[0];
|
||
assert.deepEqual(
|
||
first,
|
||
{ lat: 3.157846, lng: 101.711716 },
|
||
"OSRM snapped the origin onto the road graph — the small offset from 3.1578/101.7117 is expected",
|
||
);
|
||
assert.equal(
|
||
encodePolyline6(decodePolyline6(V_EXTERNAL)),
|
||
V_EXTERNAL,
|
||
"the external ground truth must round-trip byte-identically (RESEARCH Finding 1)",
|
||
);
|
||
});
|
||
|
||
test("P6 empty input — decode('') -> []; encode([]) === ''", async () => {
|
||
const { decodePolyline6, encodePolyline6 } = await loadCodec();
|
||
assert.deepEqual(decodePolyline6(""), []);
|
||
assert.equal(encodePolyline6([]), "");
|
||
});
|
||
|
||
test("P7 malformed refusal — truncated varints THROW, never return partial/NaN points (T-05-02)", async () => {
|
||
const { decodePolyline6 } = await loadCodec();
|
||
assert.throws(() => decodePolyline6("A"), "a lone 'A' is a truncated coordinate — must throw");
|
||
assert.throws(() => decodePolyline6("g"), "a dangling continuation chunk — must throw");
|
||
for (const bad of ["A", "g", "oqv_E"]) {
|
||
let points = null;
|
||
try {
|
||
points = decodePolyline6(bad);
|
||
} catch {
|
||
continue; // throwing is the contract
|
||
}
|
||
assert.fail(`decode(${JSON.stringify(bad)}) returned ${JSON.stringify(points)} instead of throwing`);
|
||
}
|
||
});
|
||
|
||
test("P8 round-trip law — encode(decode(s)) === s for EVERY vector string in this suite", async () => {
|
||
const { decodePolyline6, encodePolyline6 } = await loadCodec();
|
||
const all = [V_ZERO, V_KL_POINT, V_KL_THREE, V_NEGATIVE, V_EXTERNAL];
|
||
for (const s of all) {
|
||
assert.equal(
|
||
encodePolyline6(decodePolyline6(s)),
|
||
s,
|
||
`round-trip law violated for ${s.slice(0, 12)}… — the corpus gate (plan 05-02) leans on this`,
|
||
);
|
||
}
|
||
});
|