1
0

test: enforce simulation and render budgets

This commit is contained in:
2026-08-11 22:02:29 -07:00
parent 19f022f71a
commit 655c383061
9 changed files with 822 additions and 6 deletions
+13 -2
View File
@@ -47,8 +47,8 @@ art; every shipped dependency, route, font, and asset has recorded provenance.
## M1 — California roads and passive Model X traffic
Status: **implemented and rendered on desktop and mobile; performance-budget
instrumentation remains**.
Status: **implemented and rendered on desktop and mobile; repeatable California
and office browser performance-budget instrumentation is implemented**.
- Coarse California board plus detailed Bay Area and SoCal boards.
- Serializable US-101 and I-5/I-580/I-80 route graphs.
@@ -137,6 +137,17 @@ validation, and no regression to ambient live ADS-B rendering.
## Performance gates
Run `npm run build && npm run performance`. The harness serves the production
bundle locally, uses system Chrome (real GPU when available, SwiftShader as the
headless fallback), warms each scene, and writes `/tmp/tera-performance-budget.json`.
Budgets live in `scripts/performance-budgets.json`; a threshold, console error,
insufficient sample, or anonymous private endpoint request makes the command
non-zero. Use `--output`, `--budgets`, `--warmup-ms`, `--sample-ms`, or
`--software` for explicit CI/diagnostic runs. The named matrix is California and
Office at 1440×900 desktop and 390×844 mobile viewports. Frame comparisons allow
only 0.1 ms for Chrome's rAF timestamp quantization; the declared and reported
budgets are not raised.
- Named benchmark scenes: p95 frame at or below 16.7 ms desktop and 33.3 ms on
the selected supported mobile tier.
- Hard budgets per scale for resident cells, triangles, draw calls, dynamic
+2 -1
View File
@@ -15,7 +15,8 @@
"sbom": "npm sbom --package-lock-only --sbom-format=spdx",
"test": "node --test \"src/test/*.test.ts\"",
"typecheck": "tsc --noEmit",
"preview": "vite preview"
"preview": "vite preview",
"performance": "node scripts/performance-budget.mjs"
},
"dependencies": {
"satellite.js": "^7.1.0",
+291
View File
@@ -0,0 +1,291 @@
#!/usr/bin/env node
/** Repeatable anonymous WebGL performance gate over a built Tera bundle. */
import { chromium } from "playwright";
import { createServer } from "node:http";
import { access, readFile, writeFile } from "node:fs/promises";
import { extname, join, normalize, resolve } from "node:path";
import { fileURLToPath } from "node:url";
const ROOT = fileURLToPath(new URL("..", import.meta.url));
const DIST = join(ROOT, "dist");
const DEFAULT_BUDGETS = join(ROOT, "scripts", "performance-budgets.json");
const PRIVATE_PATH = /^\/api\/v1\/(?:media|realtime|presence|offices)(?:\/|$)|^\/assets\/(?:remoteMedia|presenceIndicator|scenePeers)-/;
const MIME = { ".html": "text/html; charset=utf-8", ".js": "text/javascript", ".css": "text/css", ".json": "application/json", ".png": "image/png", ".svg": "image/svg+xml", ".webp": "image/webp", ".webmanifest": "application/manifest+json" };
const VIEWPORTS = {
desktop: { width: 1440, height: 900, deviceScaleFactor: 1 },
mobile: { width: 390, height: 844, deviceScaleFactor: 2, isMobile: true, hasTouch: true },
};
const SCENES = {
california: { host: "tera.lumbridgecorp.com", ready: () => document.getElementById("boot")?.hidden === true && document.querySelectorAll("#chapters .chapter").length > 0 },
office: { host: "office.lumbridgecorp.com", ready: () => document.getElementById("boot")?.hidden === true && document.getElementById("enter")?.textContent?.includes("Back to the city") === true },
};
const args = process.argv.slice(2);
function option(name, fallback) {
const at = args.indexOf(`--${name}`);
return at < 0 ? fallback : args[at + 1];
}
function positive(name, fallback) {
const value = Number(option(name, fallback));
if (!Number.isFinite(value) || value <= 0) throw new Error(`--${name} must be a positive number`);
return value;
}
function sceneBudget(value, label) {
if (!value || typeof value !== "object") throw new Error(`missing budget for ${label}`);
for (const key of ["p95FrameIntervalMs", "maxDrawCalls", "maxTriangles"]) {
if (typeof value[key] !== "number" || !Number.isFinite(value[key]) || value[key] <= 0) {
throw new Error(`${label}.${key} must be a positive number`);
}
}
return value;
}
const outputPath = resolve(option("output", "/tmp/tera-performance-budget.json"));
const budgetPath = resolve(option("budgets", DEFAULT_BUDGETS));
const warmupMs = positive("warmup-ms", 3_000);
const sampleMs = positive("sample-ms", 8_000);
const readyTimeoutMs = positive("ready-timeout-ms", 180_000);
const softwareOnly = args.includes("--software");
// Chrome exposes rAF timestamps at 0.1 ms precision, so an ideal 60 Hz cadence
// can quantize to 16.8 ms. This tolerance is measurement resolution, not budget
// headroom; reported values and declared budgets remain unchanged.
const FRAME_COMPARISON_EPSILON_MS = 0.1;
function percentile(values, fraction) {
if (values.length === 0) return null;
const ordered = [...values].sort((a, b) => a - b);
return ordered[Math.max(0, Math.ceil(ordered.length * fraction) - 1)];
}
const rounded = (value) => value === null ? null : Math.round(value * 1_000) / 1_000;
async function serve() {
const requests = [];
const server = createServer(async (req, res) => {
const url = new URL(req.url ?? "/", "http://local.invalid");
requests.push({ method: req.method ?? "GET", path: url.pathname });
if (url.pathname === "/api/v1/health") {
res.writeHead(200, { "content-type": "application/json", "cache-control": "no-store" });
res.end(JSON.stringify({ auth: { mode: "jwt", entryUrl: "/login.html" }, sources: { weather: "none", flights: "none", satellites: "none", markers: "none" } }));
return;
}
if (url.pathname === "/api/v1/session") {
res.writeHead(200, { "content-type": "application/json", "cache-control": "no-store" });
res.end(JSON.stringify({ authenticated: false, subject: null, passwordLogin: false, admin: false }));
return;
}
if (url.pathname.startsWith("/api/v1/")) {
res.writeHead(404, { "content-type": "application/json", "cache-control": "no-store" });
res.end(JSON.stringify({ error: "not_found" }));
return;
}
const requested = normalize(decodeURIComponent(url.pathname)).replace(/^(?:\.\.[/\\])+/, "");
for (const relative of [requested === "/" ? "/index.html" : requested, "/index.html"]) {
const target = resolve(DIST, `.${relative}`);
if (!target.startsWith(`${resolve(DIST)}/`)) continue;
try {
const body = await readFile(target);
res.writeHead(200, { "content-type": MIME[extname(target)] ?? "application/octet-stream" });
res.end(body);
return;
} catch { /* SPA fallback */ }
}
res.writeHead(404).end("not found");
});
await new Promise((ok, fail) => { server.once("error", fail); server.listen(0, "127.0.0.1", ok); });
const address = server.address();
if (!address || typeof address === "string") throw new Error("performance server did not expose a TCP port");
return { server, requests, port: address.port };
}
const COMMON_ARGS = ["--no-sandbox", "--disable-dev-shm-usage"];
async function rendererOf(browser) {
const page = await browser.newPage();
try {
await page.goto("about:blank");
return await page.evaluate(() => {
const gl = document.createElement("canvas").getContext("webgl2");
const extension = gl?.getExtension("WEBGL_debug_renderer_info");
return extension ? String(gl.getParameter(extension.UNMASKED_RENDERER_WEBGL)) : null;
});
} finally { await page.close(); }
}
async function launchBrowser(port) {
const resolver = `--host-resolver-rules=MAP tera.lumbridgecorp.com 127.0.0.1, MAP office.lumbridgecorp.com 127.0.0.1`;
const attempts = softwareOnly
? [["swiftshader", ["--use-gl=angle", "--use-angle=swiftshader"]]]
: [["vulkan", ["--use-gl=angle", "--use-angle=vulkan"]], ["swiftshader", ["--use-gl=angle", "--use-angle=swiftshader"]]];
let last;
for (const [backend, flags] of attempts) {
try {
const browser = await chromium.launch({ channel: "chrome", args: [...COMMON_ARGS, resolver, ...flags] });
const renderer = await rendererOf(browser);
const softwareRenderer = renderer !== null && /SwiftShader|llvmpipe/i.test(renderer);
if (renderer && (backend === "swiftshader" || !softwareRenderer)) return { browser, backend, renderer, port };
await browser.close();
} catch (error) { last = error; }
}
for (const executablePath of ["/usr/bin/google-chrome", "/usr/bin/google-chrome-stable", "/usr/bin/chromium", "/usr/bin/chromium-browser"]) {
try {
await access(executablePath);
const browser = await chromium.launch({ executablePath, args: [...COMMON_ARGS, resolver, "--use-gl=angle", "--use-angle=swiftshader"] });
return { browser, backend: "system-chrome-swiftshader", renderer: await rendererOf(browser), port };
} catch (error) { last = error; }
}
throw new Error(`Chrome launch failed: ${last instanceof Error ? last.message : String(last)}`);
}
function instrumentation() {
// Custom production hostnames mapped to loopback are not secure contexts over
// HTTP, so Chrome withholds randomUUID even though getRandomValues remains.
// Production is HTTPS; this shim only restores that API in the local harness.
if (typeof crypto.randomUUID !== "function") {
crypto.randomUUID = () => {
const bytes = crypto.getRandomValues(new Uint8Array(16));
bytes[6] = (bytes[6] & 0x0f) | 0x40;
bytes[8] = (bytes[8] & 0x3f) | 0x80;
const hex = [...bytes].map((value) => value.toString(16).padStart(2, "0")).join("");
return `${hex.slice(0, 8)}-${hex.slice(8, 12)}-${hex.slice(12, 16)}-${hex.slice(16, 20)}-${hex.slice(20)}`;
};
}
const state = { frames: [], longTasks: [], drawCalls: [], triangles: [], currentCalls: 0, currentTriangles: 0, last: 0, longTaskSupported: false };
Object.defineProperty(globalThis, "__teraPerformanceBudget", { value: state });
const triangleCount = (mode, count) => mode === 4 ? count / 3 : (mode === 5 || mode === 6) ? Math.max(0, count - 2) : 0;
const patch = (prototype, method, countAt, instancesAt = null) => {
if (!prototype || typeof prototype[method] !== "function") return;
const original = prototype[method];
if (original.__teraPerformancePatched) return;
const wrapped = function (...values) {
state.currentCalls += 1;
const instances = instancesAt === null ? 1 : Number(values[instancesAt]) || 0;
state.currentTriangles += triangleCount(Number(values[0]), Number(values[countAt]) || 0) * instances;
return original.apply(this, values);
};
Object.defineProperty(wrapped, "__teraPerformancePatched", { value: true });
prototype[method] = wrapped;
};
for (const prototype of [globalThis.WebGLRenderingContext?.prototype, globalThis.WebGL2RenderingContext?.prototype]) {
patch(prototype, "drawArrays", 2);
patch(prototype, "drawElements", 1);
patch(prototype, "drawArraysInstanced", 2, 3);
patch(prototype, "drawElementsInstanced", 1, 4);
}
try {
new PerformanceObserver((list) => state.longTasks.push(...list.getEntries().map((entry) => ({ startTime: entry.startTime, duration: entry.duration })))).observe({ type: "longtask", buffered: true });
state.longTaskSupported = true;
} catch { /* Long Tasks API is optional. */ }
requestAnimationFrame(function sample(now) {
if (state.last > 0) {
state.frames.push(now - state.last);
state.drawCalls.push(state.currentCalls);
state.triangles.push(state.currentTriangles);
}
state.last = now;
state.currentCalls = 0;
state.currentTriangles = 0;
requestAnimationFrame(sample);
});
}
async function measure(browser, port, sceneName, viewportName, budget, requestLog) {
const viewport = VIEWPORTS[viewportName];
const context = await browser.newContext({ viewport: { width: viewport.width, height: viewport.height }, deviceScaleFactor: viewport.deviceScaleFactor, isMobile: viewport.isMobile, hasTouch: viewport.hasTouch });
const page = await context.newPage();
const consoleErrors = [];
page.on("pageerror", (error) => consoleErrors.push(String(error)));
page.on("console", (message) => { if (message.type() === "error") consoleErrors.push(message.text()); });
const before = requestLog.length;
await page.addInitScript(instrumentation);
const scene = SCENES[sceneName];
const url = `http://${scene.host}:${port}/`;
try {
await page.goto(url, { waitUntil: "networkidle", timeout: readyTimeoutMs });
try {
await page.waitForFunction(scene.ready, null, { timeout: readyTimeoutMs });
} catch (error) {
const state = await page.evaluate(() => ({
title: document.title,
bootHidden: document.getElementById("boot")?.hidden ?? null,
bootText: document.getElementById("boot")?.textContent?.trim().slice(0, 240) ?? null,
chapters: document.querySelectorAll("#chapters .chapter").length,
enter: document.getElementById("enter")?.textContent?.trim() ?? null,
})).catch(() => null);
throw new Error(`scene readiness timed out: ${JSON.stringify({ state, consoleErrors, requests: requestLog.slice(before) })}`, { cause: error });
}
await page.waitForTimeout(warmupMs);
await page.evaluate(() => {
const state = globalThis.__teraPerformanceBudget;
state.frames.length = state.drawCalls.length = state.triangles.length = state.longTasks.length = 0;
state.last = performance.now(); state.currentCalls = 0; state.currentTriangles = 0;
});
await page.waitForTimeout(sampleMs);
const raw = await page.evaluate(() => {
const state = globalThis.__teraPerformanceBudget;
const canvas = document.querySelector("canvas");
const gl = canvas?.getContext("webgl2") ?? canvas?.getContext("webgl");
const extension = gl?.getExtension("WEBGL_debug_renderer_info");
return {
frames: [...state.frames], drawCalls: [...state.drawCalls], triangles: [...state.triangles],
longTasks: [...state.longTasks], longTaskSupported: state.longTaskSupported,
renderer: extension ? String(gl.getParameter(extension.UNMASKED_RENDERER_WEBGL)) : null,
};
});
const relevantRequests = requestLog.slice(before);
const privateRequests = relevantRequests.filter((request) => PRIVATE_PATH.test(request.path));
const metrics = {
frameSamples: raw.frames.length,
p50FrameIntervalMs: rounded(percentile(raw.frames, 0.5)),
p95FrameIntervalMs: rounded(percentile(raw.frames, 0.95)),
p99FrameIntervalMs: rounded(percentile(raw.frames, 0.99)),
maxFrameIntervalMs: rounded(raw.frames.length ? Math.max(...raw.frames) : null),
p95DrawCalls: rounded(percentile(raw.drawCalls, 0.95)),
maxDrawCalls: raw.drawCalls.length ? Math.max(...raw.drawCalls) : null,
p95Triangles: rounded(percentile(raw.triangles, 0.95)),
maxTriangles: raw.triangles.length ? Math.max(...raw.triangles) : null,
longTasksSupported: raw.longTaskSupported,
longTaskCount: raw.longTasks.length,
longTaskTotalMs: raw.longTasks.reduce((sum, task) => sum + task.duration, 0),
};
const checks = {
p95FrameIntervalMs: metrics.p95FrameIntervalMs !== null &&
Math.round(metrics.p95FrameIntervalMs * 10) <=
Math.round((budget.p95FrameIntervalMs + FRAME_COMPARISON_EPSILON_MS) * 10),
maxDrawCalls: metrics.maxDrawCalls !== null && metrics.maxDrawCalls <= budget.maxDrawCalls,
maxTriangles: metrics.maxTriangles !== null && metrics.maxTriangles <= budget.maxTriangles,
anonymousPrivateRequests: privateRequests.length === 0,
consoleErrors: consoleErrors.length === 0,
enoughFrameSamples: metrics.frameSamples >= Math.max(30, Math.floor(sampleMs / 100)),
};
return { scene: sceneName, viewport: viewportName, url, viewportPixels: viewport, renderer: raw.renderer, budget, metrics, checks, passed: Object.values(checks).every(Boolean), privateRequests, consoleErrors };
} finally { await context.close(); }
}
let server;
let browser;
try {
await access(join(DIST, "index.html"));
const budgets = JSON.parse(await readFile(budgetPath, "utf8"));
const hosted = await serve(); server = hosted.server;
const launched = await launchBrowser(hosted.port); browser = launched.browser;
const results = [];
for (const scene of Object.keys(SCENES)) for (const viewport of Object.keys(VIEWPORTS)) {
process.stderr.write(`performance-budget: ${scene}/${viewport}\n`);
const budget = sceneBudget(budgets?.scenes?.[scene]?.[viewport], `${scene}.${viewport}`);
results.push(await measure(browser, hosted.port, scene, viewport, budget, hosted.requests));
}
const report = {
schemaVersion: 1, generatedAt: new Date().toISOString(), browserPlugin: "not available; Playwright system Chrome fallback used",
browser: { backend: launched.backend, renderer: launched.renderer }, warmupMs, sampleMs,
frameComparisonEpsilonMs: FRAME_COMPARISON_EPSILON_MS,
budgets: budgetPath, passed: results.every((result) => result.passed), results,
};
await writeFile(outputPath, `${JSON.stringify(report, null, 2)}\n`);
process.stdout.write(`${JSON.stringify(report, null, 2)}\n`);
process.stderr.write(`performance-budget: ${report.passed ? "PASS" : "FAIL"}${outputPath}\n`);
if (!report.passed) process.exitCode = 1;
} catch (error) {
console.error(`performance-budget: ERROR — ${error instanceof Error ? error.stack : String(error)}`);
process.exitCode = 2;
} finally {
await browser?.close().catch(() => undefined);
if (server) await new Promise((resolveClose) => server.close(resolveClose));
}
+13
View File
@@ -0,0 +1,13 @@
{
"version": 1,
"scenes": {
"california": {
"desktop": { "p95FrameIntervalMs": 16.7, "maxDrawCalls": 650, "maxTriangles": 750000 },
"mobile": { "p95FrameIntervalMs": 33.3, "maxDrawCalls": 650, "maxTriangles": 750000 }
},
"office": {
"desktop": { "p95FrameIntervalMs": 16.7, "maxDrawCalls": 550, "maxTriangles": 550000 },
"mobile": { "p95FrameIntervalMs": 33.3, "maxDrawCalls": 550, "maxTriangles": 550000 }
}
}
}
+4 -2
View File
@@ -28,7 +28,8 @@ const PROVIDER_CREDENTIAL: ScreenShareCredential = {
sessionId: "opaque-session", participantId: "opaque-participant", role: "presenter", grantToken: "opaque-grant-token-value",
};
const iceRequest = (credential: ScreenShareCredential = PROVIDER_CREDENTIAL, binding: ScreenShareBinding = BINDING) => ({
type: "ice-config-request", protocolVersion: 1, requestId: "ice-request-1", binding, credential,
type: "ice-config-request", protocolVersion: 1, requestId: "ice-request-1",
binding: { ...binding }, credential: { ...credential },
} as const);
const request = iceRequest();
const rateKeys = (subject = "auth-subject", trustedIp = "203.0.113.8") => ({ subject, trustedIp });
@@ -221,7 +222,8 @@ describe("authenticated ICE route", () => {
const missing = { ...iceRequest(presenter) } as Record<string, unknown>;
delete missing.credential;
assert.equal((await app.inject({ method: "POST", url: "/api/v1/media/ice", headers: bearer("presenter"), payload: missing })).statusCode, 400);
assert.equal((await app.inject({ method: "POST", url: "/api/v1/media/ice", headers: bearer("thief"), payload: iceRequest(presenter) })).statusCode, 401);
const stolen = await app.inject({ method: "POST", url: "/api/v1/media/ice", headers: bearer("thief"), payload: iceRequest(presenter) });
assert.equal(stolen.statusCode, 401, stolen.body);
assert.equal((await app.inject({ method: "POST", url: "/api/v1/media/ice", headers: bearer("presenter"), payload: iceRequest(presenter, SECOND_BINDING) })).statusCode, 401);
const joined = await app.inject({ method: "POST", url: "/api/v1/media/join", headers: bearer("viewer"), payload: {
+262
View File
@@ -0,0 +1,262 @@
/**
* Fast deterministic lifecycle soak for the in-memory realtime/media services.
* It intentionally uses fake clocks and direct service APIs: no sockets, sleeps,
* filesystem writes, or environment-specific infrastructure.
*/
import assert from "node:assert/strict";
import { describe, it } from "node:test";
import type { ActorPoseSnapshot, ResumeRequest } from "../../../src/realtime/types.ts";
import type {
ScreenShareBinding,
ScreenShareCredential,
ScreenShareCreateRequest,
ScreenShareJoinRequest,
} from "../../../src/media/signalingTypes.ts";
import type { IceConfigRequest } from "../../../src/media/iceTypes.ts";
import type { IceConfig } from "../config.ts";
import { createIceCredentialProvider, createMediaSignalService } from "../media/index.ts";
import { createRealtimeService } from "../realtime/index.ts";
const MEDIA_SESSIONS = 32;
const VIEWERS_PER_SHARE = 3;
const REALTIME_SESSIONS = 128;
describe("bounded in-memory service soak", () => {
it("rotates media grants, releases listeners/queues, and drains every session", () => {
let now = 1_000_000;
const service = createMediaSignalService({
now: () => now,
maximumSessions: MEDIA_SESSIONS,
maximumParticipantsPerSession: VIEWERS_PER_SHARE + 1,
maximumQueuedMessagesPerParticipant: 8,
maximumSignalsPerWindow: 16,
grantTtlMs: 90_000,
});
const credentials: Array<{ credential: ScreenShareCredential; subject: string }> = [];
const unsubscribedDeliveries: number[] = [];
const presenters: Array<{
binding: ScreenShareBinding;
subject: string;
credential: ScreenShareCredential<"presenter">;
}> = [];
for (let sessionIndex = 0; sessionIndex < MEDIA_SESSIONS; sessionIndex += 1) {
const binding = mediaBinding(sessionIndex);
const presenterSubject = `presenter-subject-${sessionIndex}`;
const created = service.join(mediaCreate(binding, sessionIndex), presenterSubject);
assert.ok(created.ok && created.value.type === "screen-share-create-grant");
if (!created.ok || created.value.type !== "screen-share-create-grant") continue;
let presenterCredential = created.value.grant.credential;
const viewers: Array<{ subject: string; credential: ScreenShareCredential; sequence: number }> = [];
for (let viewerIndex = 0; viewerIndex < VIEWERS_PER_SHARE; viewerIndex += 1) {
const subject = `viewer-subject-${sessionIndex}-${viewerIndex}`;
const joined = service.join(mediaJoin(binding, sessionIndex, viewerIndex), subject);
assert.ok(joined.ok && joined.value.type === "screen-share-join-grant");
if (!joined.ok || joined.value.type !== "screen-share-join-grant") continue;
viewers.push({ subject, credential: joined.value.grant.credential, sequence: joined.value.sequence });
}
assert.equal(viewers.length, VIEWERS_PER_SHARE);
const presenterResume = service.resume({
type: "screen-share-resume-request", protocolVersion: 1,
requestId: `presenter-resume-${sessionIndex}`, sequence: 2, timestampMs: now,
binding, credential: presenterCredential, lastReceivedSequence: created.value.sequence,
}, presenterSubject);
assert.equal(presenterResume.ok, true);
if (!presenterResume.ok) continue;
assert.notEqual(presenterResume.value.grant.credential.grantToken, presenterCredential.grantToken);
assert.equal(presenterResume.value.grant.credential.role, "presenter");
if (presenterResume.value.grant.credential.role !== "presenter") continue;
presenterCredential = presenterResume.value.grant.credential as ScreenShareCredential<"presenter">;
credentials.push({ credential: presenterCredential, subject: presenterSubject });
for (const [viewerIndex, viewer] of viewers.entries()) {
const resumed = service.resume({
type: "screen-share-resume-request", protocolVersion: 1,
requestId: `viewer-resume-${sessionIndex}-${viewerIndex}`, sequence: 2, timestampMs: now,
binding, credential: viewer.credential, lastReceivedSequence: viewer.sequence,
}, viewer.subject);
assert.equal(resumed.ok, true);
if (!resumed.ok) continue;
const rotated = resumed.value.grant.credential;
credentials.push({ credential: rotated, subject: viewer.subject });
let deliveries = 0;
const subscription = service.subscribe(rotated, viewer.subject, () => { deliveries += 1; });
assert.equal(subscription.ok, true);
if (!subscription.ok) continue;
subscription.value();
const before = deliveries;
const offered = service.signal({
type: "screen-share-signal-request", protocolVersion: 1, sequence: 3 + viewerIndex, timestampMs: now,
binding, credential: presenterCredential, targetParticipantId: rotated.participantId,
signal: { kind: "sdp", descriptionType: "offer", sdp: "v=0" },
}, presenterSubject);
assert.equal(offered.ok, true);
unsubscribedDeliveries.push(deliveries - before);
}
presenters.push({ binding, subject: presenterSubject, credential: presenterCredential });
now += 1;
}
assert.equal(service.sessionCount(), MEDIA_SESSIONS);
assert.equal(credentials.length, MEDIA_SESSIONS * (VIEWERS_PER_SHARE + 1));
for (const [index, presenter] of presenters.entries()) {
const stopped = service.leave({
type: "screen-share-stop-request", protocolVersion: 1,
requestId: `stop-${index}`, sequence: 3 + VIEWERS_PER_SHARE,
timestampMs: now, binding: presenter.binding,
credential: presenter.credential, reason: "presenter-stopped",
}, presenter.subject, false);
assert.equal(stopped.ok, true);
}
assert.ok(unsubscribedDeliveries.every((count) => count === 0), "unsubscribed listeners receive no later offers");
assert.equal(service.sessionCount(), 0);
assert.ok(credentials.every(({ credential, subject }) =>
!service.revalidate(credential, subject).ok
), "destroyed sessions retain no usable grant for its original owner");
});
it("rotates realtime tokens, disconnects listeners, rate-limits, and expires all sessions", () => {
let now = 2_000_000;
const service = createRealtimeService({
now: () => now,
maximumSessions: REALTIME_SESSIONS,
maximumSessionsPerCell: REALTIME_SESSIONS,
maximumPoseUpdatesPerWindow: 4,
rateWindowMs: 10_000,
disconnectGraceMs: 500,
sessionTtlMs: 30_000,
});
const sessions: Array<{ sessionId: string; token: string; actorId: string; subject: string }> = [];
let disconnectedDeliveries = 0;
for (let index = 0; index < REALTIME_SESSIONS; index += 1) {
const actorId = `soak-actor-${index}`;
const subject = `soak-subject-${index}`;
const joined = service.join({
requestId: `soak-join-${index}`, actorId, subject, role: "member",
interests: [{ kind: "floor", officeId: "lumbridge-hq", floorId: "level-1" }],
});
assert.equal(joined.ok, true);
if (!joined.ok) continue;
let token = joined.value.token;
const connected = service.connect(joined.value.sessionId, token, () => { disconnectedDeliveries += 1; });
assert.equal(connected.ok, true);
if (connected.ok) connected.value.disconnect();
for (let rotation = 0; rotation < 3; rotation += 1) {
const request: ResumeRequest = {
type: "resume-request", protocolVersion: 1,
requestId: `soak-resume-${index}-${rotation}`,
sessionId: joined.value.sessionId, resumeToken: token,
serverEpoch: joined.value.grant.serverEpoch, lastReceivedSequence: 0,
interests: joined.value.grant.interests,
};
const resumed = service.resume(request, subject);
assert.equal(resumed.ok, true);
if (!resumed.ok) break;
assert.notEqual(resumed.value.resumeToken, token);
token = resumed.value.resumeToken;
}
sessions.push({ sessionId: joined.value.sessionId, token, actorId, subject });
}
assert.equal(sessions.length, REALTIME_SESSIONS);
assert.equal(service.sessionCount(), REALTIME_SESSIONS);
const publisher = sessions[0] as (typeof sessions)[number];
for (let sequence = 1; sequence <= 5; sequence += 1) {
now += 100;
const result = service.submitPose(
publisher.sessionId,
publisher.token,
actorPose(publisher.actorId, sequence, now, sequence / 100),
);
assert.equal(result.ok, sequence <= 4);
if (sequence === 5 && !result.ok) assert.equal(result.code, "rate-limited");
}
assert.equal(disconnectedDeliveries, 0, "disconnected realtime listeners receive no broadcast");
for (let index = 0; index < sessions.length / 2; index += 1) {
const session = sessions[index] as (typeof sessions)[number];
assert.equal(service.leave(session.sessionId, session.token).ok, true);
}
assert.equal(service.sessionCount(), REALTIME_SESSIONS / 2);
now += 501;
assert.equal(service.cleanup(), REALTIME_SESSIONS / 2);
assert.equal(service.sessionCount(), 0);
assert.ok(sessions.every((session) => !service.authenticate(session.sessionId, session.token).ok));
});
it("bounds ICE issuance across subject and address churn without retaining credentials", () => {
let now = 3_000_000;
const provider = createIceCredentialProvider(iceConfig(), { now: () => now });
const credential: ScreenShareCredential = {
sessionId: "soak-media-session", participantId: "soak-participant",
role: "presenter", grantToken: "soak-private-grant-token",
};
const binding = mediaBinding(999);
const request: IceConfigRequest = {
type: "ice-config-request", protocolVersion: 1, requestId: "soak-ice",
binding, credential,
};
for (let attempt = 1; attempt <= 5; attempt += 1) {
const result = provider.issue(request, {
subject: "same-subject",
trustedIp: `203.0.113.${attempt}`,
}, now + 90_000);
assert.equal(result.ok, attempt <= 4, "subject limit survives address rotation");
}
now += 10_000;
for (let attempt = 1; attempt <= 5; attempt += 1) {
const result = provider.issue(request, {
subject: `rotating-subject-${attempt}`,
trustedIp: "198.51.100.10",
}, now + 90_000);
assert.equal(result.ok, attempt <= 4, "address limit survives subject rotation");
}
});
});
function mediaBinding(index: number): ScreenShareBinding {
return { officeId: "soak-office", levelId: "level-1", roomId: "room", screenId: `screen-${index}` };
}
function mediaCreate(binding: ScreenShareBinding, index: number): ScreenShareCreateRequest {
return {
type: "screen-share-create-request", protocolVersion: 1, requestId: `create-${index}`,
sequence: 1, timestampMs: 1_000_000, binding, role: "presenter",
};
}
function mediaJoin(binding: ScreenShareBinding, session: number, viewer: number): ScreenShareJoinRequest {
return {
type: "screen-share-join-request", protocolVersion: 1, requestId: `join-${session}-${viewer}`,
sequence: 1, timestampMs: 1_000_000, binding, role: "viewer", viewerOptIn: true,
};
}
function actorPose(actorId: string, sequence: number, timestampMs: number, xM: number): ActorPoseSnapshot {
return {
entity: "actor", actorId, kind: "humanoid", sequence, timestampMs,
pose: {
space: "local", cell: { kind: "floor", officeId: "lumbridge-hq", floorId: "level-1" },
xM, yM: 0, zM: 0, headingDeg: 0, pitchDeg: 0,
},
velocity: { xMps: 0.1, yMps: 0, zMps: 0, yawDegPerSec: 0 },
};
}
function iceConfig(): IceConfig {
return {
configured: true,
urls: ["turn:relay.example.test:3478"],
sharedSecret: "soak-turn-shared-secret-with-at-least-thirty-two-bytes",
credentialTtlSeconds: 60,
rateAttempts: 4,
rateWindowSeconds: 10,
};
}
+6 -1
View File
@@ -50,12 +50,17 @@ function screenBinding(value: unknown): boolean {
function screenCredential(value: unknown): boolean {
return exact(value, ["sessionId", "participantId", "role", "grantToken"]) &&
identifier(value.sessionId) && identifier(value.participantId) &&
opaqueIdentifier(value.sessionId) && opaqueIdentifier(value.participantId) &&
(value.role === "presenter" || value.role === "viewer") &&
typeof value.grantToken === "string" && value.grantToken.length >= 16 &&
value.grantToken.length <= 512;
}
/** Server credentials use unpadded base64url and may begin with `_` or `-`. */
function opaqueIdentifier(value: unknown): value is string {
return typeof value === "string" && /^[a-zA-Z0-9_-]{1,256}$/.test(value);
}
export function parseIceConfigResponse(value: unknown): IceConfigValidationResult<IceConfigResponse> {
if (!plain(value) || value.protocolVersion !== ICE_CONFIG_PROTOCOL_VERSION || !identifier(value.requestId)) {
return failure("invalid response envelope");
+4
View File
@@ -38,6 +38,10 @@ const grant = (): IceConfigGrant => ({
describe("ICE configuration wire contract", () => {
it("accepts exact requests and bounded ephemeral grants", () => {
assert.equal(parseIceConfigRequest(request()).ok, true);
assert.equal(parseIceConfigRequest({
...request(),
credential: { ...request().credential, sessionId: "_opaque-session", participantId: "-opaque-participant" },
}).ok, true, "accepts every server-generated base64url prefix");
assert.equal(parseIceConfigResponse(JSON.parse(JSON.stringify(grant()))).ok, true);
assert.equal(isIceConfigGrantActive(grant(), 300_000), true);
assert.equal(isIceConfigGrantActive(grant(), 601_000), false);
+227
View File
@@ -0,0 +1,227 @@
import assert from "node:assert/strict";
import { describe, it } from "node:test";
import { ActorController, type ActorControllerOptions } from "../actors/controller.ts";
import { AircraftController, type AircraftWaypoint } from "../aircraft/controller.ts";
import { Plan } from "../interiors/plan.ts";
import type { Level, Office, Room, Wall } from "../interiors/types.ts";
import { createWalker } from "../interiors/walker.ts";
import CALIFORNIA_TRANSPORT from "../transport/california.ts";
import { VehicleController } from "../transport/vehicleController.ts";
import { buildRoutePath, VehicleSimulation } from "../transport/vehicleSim.ts";
const SOAK_TIMEOUT_MS = 5_000;
const HOUR_AT_TEN_HZ = 36_000;
function finiteNumbers(value: unknown, path = "state"): void {
if (typeof value === "number") {
assert.ok(Number.isFinite(value), `${path} must remain finite`);
return;
}
if (typeof value !== "object" || value === null) return;
for (const [key, nested] of Object.entries(value)) finiteNumbers(nested, `${path}.${key}`);
}
function inside(value: number, minimum: number, maximum: number, label: string): void {
assert.ok(value >= minimum && value <= maximum, `${label} ${value} outside [${minimum}, ${maximum}]`);
}
function countForwardWrap(previous: number, next: number): number {
return previous > 0.9 && next < 0.1 ? 1 : 0;
}
describe("accelerated long-duration deterministic simulation", () => {
it("runs both California routes and ambient traffic through repeated completions", { timeout: SOAK_TIMEOUT_MS }, () => {
for (const routeId of ["la-sf-us-101", "la-sf-i-5"] as const) {
const path = buildRoutePath(CALIFORNIA_TRANSPORT, routeId);
const first = new VehicleSimulation(CALIFORNIA_TRANSPORT, { routeId, count: 24, seed: 115, timeScale: 900 });
const replay = new VehicleSimulation(CALIFORNIA_TRANSPORT, { routeId, count: 24, seed: 115, timeScale: 900 });
let wraps = 0;
let prior = first.poses()[0]?.progress ?? 0;
// 15 rendered minutes at the production 900x traffic scale is more than
// nine simulated days, advanced in bounded 20 Hz fixed steps.
for (let frame = 0; frame < 3_600; frame += 1) {
first.tick(0.25);
replay.tick(0.25);
const hero = first.poses()[0];
assert.ok(hero);
wraps += countForwardWrap(prior, hero.progress);
prior = hero.progress;
if (frame % 60 !== 0) continue;
for (const pose of first.poses()) {
finiteNumbers(pose, `${routeId}.${pose.id}`);
inside(pose.progress, 0, 1, `${pose.id}.progress`);
inside(pose.distanceM, 0, path.lengthM, `${pose.id}.distanceM`);
inside(pose.lat, 32, 43, `${pose.id}.lat`);
inside(pose.lng, -125, -113, `${pose.id}.lng`);
}
}
assert.ok(wraps >= 10, `${routeId} hero completed only ${wraps} circuits`);
assert.deepEqual(first.poses(), replay.poses(), `${routeId} seeded traffic drifted on replay`);
}
});
it("drives the playable vehicle for an accelerated hour without drift or escape", { timeout: SOAK_TIMEOUT_MS }, () => {
const options = {
routeId: "la-sf-us-101",
fixedStepSeconds: 0.1,
initialSpeedMps: 20,
travelScale: 900,
guardrailOffsetM: 5,
} as const;
const first = new VehicleController(CALIFORNIA_TRANSPORT, options);
const replay = new VehicleController(CALIFORNIA_TRANSPORT, options);
const path = buildRoutePath(CALIFORNIA_TRANSPORT, options.routeId);
let prior = first.state().progress;
let completions = 0;
for (let step = 0; step < HOUR_AT_TEN_HZ; step += 1) {
const actions = step % 1_200 < 60
? { throttle: 0.75, steering: Math.sin(step / 20) * 0.35 }
: step % 1_200 === 60 ? { modeRequest: "assisted" as const } : undefined;
first.stepFixed(actions);
replay.stepFixed(actions);
const state = first.state();
completions += countForwardWrap(prior, state.progress);
prior = state.progress;
if (step % 300 !== 0) continue;
finiteNumbers(state);
inside(state.progress, 0, 1, "vehicle.progress");
inside(state.distanceM, 0, path.lengthM, "vehicle.distanceM");
inside(state.lateralOffsetM, -5, 5, "vehicle.lateralOffsetM");
inside(state.speedMps, 0, 58, "vehicle.speedMps");
}
assert.ok(completions >= 20, `playable vehicle completed only ${completions} route circuits`);
assert.ok(first.state().elapsedSteps === HOUR_AT_TEN_HZ);
assert.deepEqual(first.snapshot(), replay.snapshot());
});
it("flies a crow for an accelerated hour inside strict 3D bounds", { timeout: SOAK_TIMEOUT_MS }, () => {
const options: ActorControllerOptions = {
kind: "crow",
mode: "flight",
identity: { id: "soak-crow", displayName: "Soak Crow", authenticated: false, profile: {} },
position: { x: 0, y: 25, z: 0 },
groundY: 0,
minFlightAltitude: 5,
maxFlightAltitude: 50,
horizontalBounds: { minX: -200, maxX: 200, minZ: -200, maxZ: 200 },
fixedStepSeconds: 0.1,
};
const first = new ActorController(options);
const replay = new ActorController(options);
const visited = new Set<string>();
for (let step = 0; step < HOUR_AT_TEN_HZ; step += 1) {
const phase = step % 1_800;
const actions = {
forward: 0.65 + 0.3 * Math.sin(step / 173),
turn: phase < 900 ? 0.23 : -0.31,
pitch: Math.sin(step / 257) * 0.7,
climb: Math.sin(step / 401) * 0.8,
glide: phase >= 1_500,
};
first.stepFixed(actions);
replay.stepFixed(actions);
const state = first.state();
if (step % 120 !== 0) continue;
finiteNumbers(state);
inside(state.x, -200, 200, "crow.x");
inside(state.z, -200, 200, "crow.z");
inside(state.y, 5, 50, "crow.y");
inside(state.posePhase, 0, Math.PI * 2, "crow.posePhase");
visited.add(`${Math.round(state.x / 20)}:${Math.round(state.z / 20)}:${Math.round(state.y / 5)}`);
}
assert.ok(first.state().distanceM > 20_000,
`crow accumulated only ${first.state().distanceM.toFixed(1)} m of flight distance`);
assert.ok(visited.size > 25, `crow explored only ${visited.size} coarse cells`);
assert.deepEqual(first.snapshot(), replay.snapshot());
});
it("walks against office walls for an accelerated hour without tunnelling or sticking", { timeout: SOAK_TIMEOUT_MS }, () => {
const room: Room = {
id: "soak-room", name: "Soak Room", floor: "floor" as never,
outline: [{ x: 0, z: 0 }, { x: 30, z: 0 }, { x: 30, z: 20 }, { x: 0, z: 20 }],
};
const walls: Wall[] = [
{ id: "vertical", from: { x: 15, z: 0 }, to: { x: 15, z: 20 },
openings: [{ kind: "door", start: 8.5, width: 3, sill: 0, head: 2.2 }] },
{ id: "horizontal", from: { x: 0, z: 10 }, to: { x: 12, z: 10 } },
];
const level: Level = {
id: "ground", name: "Ground", elevation: 0, wallHeight: 3, wallThickness: 0.12,
floorplan: { rooms: [room], walls },
};
const plan = new Plan({ id: "soak-office", name: "Soak Office", levels: [level], viewpoints: [] } as Office,
{ warn: false });
const create = () => createWalker(plan, {
levelId: "ground", position: { x: 4, z: 4 }, radius: 0.3,
speed: 2.4, fixedStep: 0.1, maxCatchUpSteps: 1,
});
const first = create();
const replay = create();
const directions = [
{ x: 1, z: 0.23 }, { x: 0.18, z: 1 }, { x: -1, z: -0.17 }, { x: -0.11, z: -1 },
] as const;
const visited = new Set<string>();
for (let step = 0; step < HOUR_AT_TEN_HZ; step += 1) {
const action = directions[Math.floor(step / 450) % directions.length]!;
const a = first.tick(0.1, action);
replay.tick(0.1, action);
if (step % 30 === 0) visited.add(`${Math.round(a.position.x)}:${Math.round(a.position.z)}`);
if (step % 120 !== 0) continue;
finiteNumbers(a);
inside(a.position.x, 0.3, 29.7, "walker.x");
inside(a.position.z, 0.3, 19.7, "walker.z");
assert.equal(plan.blocked("ground", a.position, a.position, 0.3), false, "walker entered collision geometry");
}
assert.ok(first.state().distance > 200,
`walker accumulated only ${first.state().distance.toFixed(1)} m of distance`);
assert.ok(visited.size > 20, `walker visited only ${visited.size} cells`);
assert.deepEqual(first.state(), replay.state());
});
it("circuits an assisted aircraft route for an accelerated hour within its envelope", { timeout: SOAK_TIMEOUT_MS }, () => {
const route: readonly AircraftWaypoint[] = [
{ id: "west", lat: 34.05, lng: -118.28, altitudeM: 1_200 },
{ id: "north", lat: 34.13, lng: -118.20, altitudeM: 1_650 },
{ id: "east", lat: 34.05, lng: -118.12, altitudeM: 1_350 },
];
const options = {
route,
initialPosition: route[0],
initialAltitudeM: 1_200,
initialHeadingDeg: 45,
initialSpeedMps: 55,
fixedStepSeconds: 0.1,
} as const;
const first = new AircraftController(options);
const replay = new AircraftController(options);
let priorIndex = first.state().routeWaypointIndex;
let waypointTransitions = 0;
let routeCompletions = 0;
for (let step = 0; step < HOUR_AT_TEN_HZ; step += 1) {
first.stepFixed();
replay.stepFixed();
const state = first.state();
if (state.routeWaypointIndex !== priorIndex) {
waypointTransitions += 1;
if (priorIndex === route.length - 1 && state.routeWaypointIndex === 0) routeCompletions += 1;
priorIndex = state.routeWaypointIndex;
}
if (step % 120 !== 0) continue;
finiteNumbers(state);
inside(state.lat, 32.4, 42.1, "aircraft.lat");
inside(state.lng, -124.6, -114, "aircraft.lng");
inside(state.altitudeM, 75, 6_000, "aircraft.altitudeM");
inside(state.speedMps, 20, 95, "aircraft.speedMps");
inside(state.headingDeg, 0, 360, "aircraft.headingDeg");
}
assert.ok(waypointTransitions >= 6, `aircraft made only ${waypointTransitions} waypoint transitions`);
assert.ok(routeCompletions >= 2, `aircraft completed only ${routeCompletions} circuits`);
assert.equal(first.state().elapsedSteps, HOUR_AT_TEN_HZ);
assert.deepEqual(first.snapshot(), replay.snapshot());
});
});