#!/usr/bin/env node /* * destination-preload.test.mjs — DP1..DP11, the ONBD-02 webview gates (D-11, D-07). * ----------------------------------------------------------------------------------- * The webview half of the deep-link hand-off: the destination id arrives from TWO * carriers — the URL's own query string (`?dest=`, the inline web_app-button * path, PRIMARY) and the SIGNED initData's `start_param` field (the documented * direct-link / home-screen path, SECONDARY) — with ONE shared validation, and the * resolved destination lives in MEMORY ONLY (GEO-03/D-07). * * Styles, per the repo's conventions: * - pure-module vector cases over resolve-destination.ts (session.test.mjs shape) * - source-introspection gates with comments stripped (no-storage.test.mjs shape) * * Cases: * DP1 the URL carrier resolves (?dest=suria-klcc → url) * DP2 the start_param carrier resolves (initData → start-param) * DP3 the URL carrier wins when both are present * DP4 the dest_ prefix is stripped on BOTH carriers * DP5 no id yields the honest none * DP6 malformed ids are rejected (traversal, XSS, empty, 200 chars) * DP7 a known id resolves to a valid entry (finite lat/lng, non-empty name) * DP8 an unknown id returns null — never a fabricated coordinate * DP9 the memory-only source gate (no storage surface in code) * DP10 no crypto in the destination path (D-11) * DP11 the id-set parity — every committed gazetteer id resolves */ import { strict as assert } from "node:assert"; import { readFileSync } from "node:fs"; import path from "node:path"; import { fileURLToPath } from "node:url"; import { test } from "./harness.mjs"; const { readDestinationId, resolveDestination, setDestination, getDestination } = await import( "../src/search/resolve-destination.ts" ); const gazetteer = JSON.parse( readFileSync( path.join(path.dirname(fileURLToPath(new URL(import.meta.url))), "..", "..", "search", "src", "data", "kl-gazetteer.json"), "utf8", ), ); /* ————— DP1..DP6 — the two-carrier reader ————— */ test("DP1 the URL carrier resolves (?dest=suria-klcc → url)", () => { assert.deepEqual(readDestinationId("?dest=suria-klcc", ""), { id: "suria-klcc", source: "url" }); }); test("DP2 the start_param carrier resolves (signed initData field → start-param)", () => { assert.deepEqual( readDestinationId("", "user=%7B%7D&auth_date=1&start_param=suria-klcc&hash=h"), { id: "suria-klcc", source: "start-param" }, ); }); test("DP3 the URL carrier wins over start_param when both are present", () => { assert.deepEqual( readDestinationId("?dest=suria-klcc", "user=%7B%7D&auth_date=1&start_param=pavilion-kl&hash=h"), { id: "suria-klcc", source: "url" }, "the inline-button path (URL) is PRIMARY — a stale start_param must not override it", ); }); test("DP4 the dest_ prefix is stripped on BOTH carriers (D-11's literal dest_ form)", () => { assert.deepEqual(readDestinationId("?dest=dest_suria-klcc", ""), { id: "suria-klcc", source: "url" }); assert.deepEqual( readDestinationId("", "user=%7B%7D&auth_date=1&start_param=dest_suria-klcc&hash=h"), { id: "suria-klcc", source: "start-param" }, ); }); test("DP5 no id at all yields the honest none", () => { assert.deepEqual(readDestinationId("", ""), { id: null, source: "none" }); assert.deepEqual(readDestinationId("", null), { id: null, source: "none" }); assert.deepEqual(readDestinationId("", undefined), { id: null, source: "none" }); assert.deepEqual(readDestinationId("?mock=android&scheme=dark", "user=%7B%7D&auth_date=1&hash=h"), { id: null, source: "none", }); }); test("DP6 malformed ids are rejected — traversal, XSS, empty, 200 chars, bad charset", () => { for (const hostile of ["../../etc/passwd", "