forked from c-fu/arah2
- strip.ts: six-state lamp+word left cluster, ±M / FIX Ns AGO telemetry (U+00B1 glyph), elapsed-seconds for acquiring - cards.ts: boot/denied/lost builders from theme classes, machine-blink gantry (500ms JS duty cycle), GEO/SYS registers from strings.ts, D-06 CTA inversion (chat-fallback=btn-primary, retry=btn-secondary) - recenter.ts: 40x40 plate w/ >=44 hit slop, orthogonal inline-SVG crosshair, three data-attribute notch states, no inline handlers - main.ts: boot order map->session->chain (bootMap before acquireFix), null-map guards on every map consumer, ARAHARAH-LIVE emitted outside the guard, data-map=absent fallback, localeFromInitData, ASK VIA CHAT w/ D-03 revalidation, 1s tick re-running the chain at 45s staleness - map.ts: attribution control moved bottom-left (UI-SPEC flush chip, compact) - shell.css: attrib chip #map-scoped override (wins cascade over maplibre css), strip left cluster, recenter z-index - vite.config.ts: define __ARAHARAH_HARNESS__ keyed on --mode harness (bare ambient global would ReferenceError in plain dev; Rule 3) - session.test.mjs: SESS1-4 memory-token gates
55 lines
2.6 KiB
JavaScript
55 lines
2.6 KiB
JavaScript
#!/usr/bin/env node
|
|
/*
|
|
* session.test.mjs — SESS1..SESS4, pure-function gates over session/store.ts.
|
|
* ----------------------------------------------------------------------------
|
|
* The store is a module singleton: every case calls clearSession() first so
|
|
* case order never leaks token state. SESS4 is the D-03/D-07 source gate —
|
|
* store.ts must reference no persistence surface in CODE (comments stripped,
|
|
* GEO3 gate pattern from no-storage.test.mjs).
|
|
*/
|
|
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 { setSession, getSession, isSessionStale, clearSession, SESSION_TTL_SECONDS } = await import("../src/session/store.ts");
|
|
|
|
test("SESS1 setSession/getSession round-trips and isSessionStale is false inside the 900s window", () => {
|
|
clearSession();
|
|
setSession("token-abc", 1_000);
|
|
assert.equal(getSession(), "token-abc");
|
|
assert.equal(isSessionStale(1_000 + SESSION_TTL_SECONDS - 1), false, "one second inside the window is fresh");
|
|
assert.equal(isSessionStale(1_000 + 1), false, "a fresh issuance is fresh");
|
|
});
|
|
|
|
test("SESS2 isSessionStale is true at or beyond 900s, and true before any token is set", () => {
|
|
clearSession();
|
|
assert.equal(isSessionStale(1_234), true, "no token — stale by definition");
|
|
setSession("token-abc", 1_000);
|
|
assert.equal(isSessionStale(1_000 + SESSION_TTL_SECONDS), true, "exactly 900s is already stale");
|
|
assert.equal(isSessionStale(1_000 + SESSION_TTL_SECONDS + 42), true, "beyond the window is stale");
|
|
});
|
|
|
|
test("SESS3 clearSession nulls the token", () => {
|
|
setSession("to-be-cleared", 42);
|
|
assert.equal(getSession(), "to-be-cleared");
|
|
clearSession();
|
|
assert.equal(getSession(), null);
|
|
assert.equal(isSessionStale(43), true, "a cleared store is a stale store");
|
|
});
|
|
|
|
test("SESS4 store.ts references no storage API (source-introspection, comments stripped)", () => {
|
|
const file = path.join(path.dirname(fileURLToPath(new URL(import.meta.url))), "..", "src", "session", "store.ts");
|
|
const code = readFileSync(file, "utf8")
|
|
.replace(/\/\*[\s\S]*?\*\//g, " ")
|
|
.replace(/(^|[^:\\])\/\/[^\n]*/g, "$1");
|
|
for (const banned of [
|
|
"localStorage", "sessionStorage", "indexedDB", "caches", "document.cookie",
|
|
"setItem", "getItem", "removeItem",
|
|
]) {
|
|
assert.ok(!code.includes(banned), `store.ts code references ${banned} — the memory-only invariant is broken`);
|
|
}
|
|
assert.ok(code.includes("SESSION_TTL_SECONDS = 900"), "the 900s TTL mirror by name stays present (D-03)");
|
|
});
|