Files
Arah2/web/test/session.test.mjs
T
araharah-executor d573acc583 feat(03-03): status strip, state cards, recenter control, boot choreography — map-first, D-09 staged states
- 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
2026-10-23 00:08:34 +08:00

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)");
});