/** * The boundary between this bundle and a deployment, from the browser's side. * * Three things meet here and all three are easy to get wrong quietly: * * 1. **The wire bodies are JSON and stay JSON.** `src/server/wire.ts` compiles * to nothing, so nothing in it can be checked by running it — but the shapes * it declares are the shapes both sides build, and a body that does not * survive `JSON.parse(JSON.stringify(x))` unchanged is a body the two sides * will disagree about. The device bodies are new and carry the first * optional-reading type on the wire, which is exactly where a `undefined` * versus `null` mistake hides. * 2. **`/health` is read defensively.** `sources.devices` and `degraded` are * newer than servers this client will meet, and a missing field has to fall * the safe way rather than throw or be assumed. * 3. **The seam chooses a strategy and says which.** An anonymous visitor and a * zero-config clone both get the local simulator, alive and labelled; a * signed-in viewer on a configured box gets the deployment's readings. What * must never happen is a studio that looks live and is not, or a control * that appears to work and changes nothing anybody else can see. */ import assert from "node:assert/strict"; import { describe, it } from "node:test"; import { resolveAccess } from "../../access.ts"; import { createTeraClient } from "../../adapters/http.ts"; import { createDeviceSource, createNullDeviceSource } from "../../devices/adapter.ts"; import { initialDeviceState, type DeviceDeclaration, type DeviceState } from "../../devices/types.ts"; import type { DeviceCommandBody, DeviceCommandResultBody, DevicesBody, HealthBody, } from "../../server/wire.ts"; Object.defineProperty(globalThis, "window", { configurable: true, value: { location: { origin: "https://office.example.test" } }, }); const DECLARATION: DeviceDeclaration = { id: "mic-1", kind: "mic", label: "Desk mic", assetId: "tera:device.mic.desk", anchor: { levelId: "l1", propId: "mic-prop", seatId: "desk-01" }, capabilities: ["power", "mute", "gain", "level"], provenance: "simulated", disclosure: "Simulated studio hardware. Demonstration data, never presence data.", }; function json(body: unknown, status = 200): Response { return new Response(JSON.stringify(body), { status, headers: { "content-type": "application/json" }, }); } function deployment(routes: Record Response>): typeof fetch { return (async (input: RequestInfo | URL) => { const url = String(input); for (const [path, answer] of Object.entries(routes)) { if (url.includes(path)) return answer(); } throw new TypeError("Failed to fetch"); }) as typeof fetch; } const health = (over: Partial = {}): HealthBody => ({ ok: true, service: "tera-api", version: "0.1.0", uptimeSeconds: 1, sources: { weather: "nws", flights: "adsb", satellites: "none", markers: "none", devices: "sim" }, auth: { mode: "none", entryUrl: null }, regions: [{ id: "sf", lat: 37.77, lng: -122.42, radiusKm: 120 }], degraded: [], ...over, }); describe("the bodies are JSON, and stay JSON", () => { it("round-trips a devices body unchanged", () => { const body: DevicesBody = { officeId: "hq", devices: [ { id: "mic-1", kind: "mic", powered: true, muted: false, gainDb: 12, levelDb: -22.4, observedAt: 17, synthetic: true }, { id: "spk-1", kind: "speaker", powered: false, volume: 0.35, playing: false, observedAt: 17, synthetic: true }, ], observedAt: 17, source: "sim", synthetic: true, ttlSeconds: 5, }; assert.deepEqual(JSON.parse(JSON.stringify(body)), body); }); it("round-trips a command and its result unchanged", () => { const command: DeviceCommandBody = { command: { deviceId: "mic-1", op: "gain", value: 18 } }; assert.deepEqual(JSON.parse(JSON.stringify(command)), command); const result: DeviceCommandResultBody = { officeId: "hq", device: initialDeviceState(DECLARATION, 17), observedAt: 17, }; assert.deepEqual(JSON.parse(JSON.stringify(result)), result); }); it("omits a reading a device does not have, rather than nulling it", () => { // The difference matters on the wire as well as in the panel: `null` would // survive JSON as a *reading of zero information* that a consumer has to // special-case, while an absent key is already the thing every consumer // checks for. `initialDeviceState` is where the rule is implemented. const state = initialDeviceState({ ...DECLARATION, capabilities: ["power"] }, 1); const round = JSON.parse(JSON.stringify(state)) as DeviceState; assert.equal("levelDb" in round, false); assert.equal("muted" in round, false); assert.equal(round.powered, false); assert.equal(round.synthetic, true); }); it("declares regions and a device source on the health body", () => { // A compile-time assertion made at runtime: `health()` above is typed as a // `HealthBody`, so this file would not build if either field left the type. const body = health(); assert.equal(body.sources.devices, "sim"); assert.equal(body.regions[0]?.id, "sf"); }); }); describe("what the browser learns from /health", () => { it("reports the device source and the demotions to the interface", async () => { const access = await resolveAccess( deployment({ "/health": () => json( health({ degraded: ["TERA_WEATHER_SOURCE=nws needs TERA_WEATHER_CONTACT.", "and another"], }), ), }), ); assert.equal(access.feeds?.devices, true); // Built, served, and — until now — read by nobody. This is the whole point // of the field: "why is the weather always clear" answers itself. assert.deepEqual(access.degraded, [ "TERA_WEATHER_SOURCE=nws needs TERA_WEATHER_CONTACT.", "and another", ]); }); it("falls the safe way when the server is older than this client", async () => { const older = health(); delete (older.sources as Partial).devices; delete (older as Partial).degraded; const access = await resolveAccess(deployment({ "/health": () => json(older) })); // No field means no feed and no request. A poll against a box that never // heard of the route is a 404 per TTL per tab, forever. assert.equal(access.feeds?.devices, false); assert.deepEqual(access.degraded, []); }); it("drops anything in degraded that is not a sentence", async () => { const access = await resolveAccess( deployment({ "/health": () => json(health({ degraded: [1, null, { a: 1 }, "real"] as never })) }), ); // `[object Object]` in front of an operator who is already looking at this // list because something is wrong. assert.deepEqual(access.degraded, ["real"]); }); it("has nothing to report about a deployment that does not exist", async () => { const access = await resolveAccess(deployment({})); assert.equal(access.tier, "member"); assert.equal(access.feeds, null); assert.deepEqual(access.degraded, []); }); }); describe("a zero-config clone", () => { it("gets an empty device feed that does not claim to be live", async () => { const client = createTeraClient({ fetch: deployment({}) }); const feed = await client.devices("hq"); assert.deepEqual(feed.value, []); assert.equal(feed.live, false); assert.equal(feed.source, "none"); }); it("gets a studio that is alive anyway, from the simulator in this tab", () => { const source = createDeviceSource({ declarations: [DECLARATION], client: null }); source.command({ deviceId: "mic-1", op: "power", value: true }); let peak = -60; for (let i = 0; i < 400; i += 1) { source.tick(0.1); peak = Math.max(peak, source.current().states[0]?.levelDb ?? -60); } assert.ok(peak > -58, `peak ${peak}`); // Alive, and honest about it: `live` is "a deployment answered" and // `synthetic` is "nobody observed this", and both are what the panel shows. assert.equal(source.current().live, false); assert.equal(source.current().synthetic, true); assert.equal(source.current().source, "sim"); source.stop(); }); }); describe("the seam", () => { it("is nothing at all for an office that declares no devices", () => { const source = createDeviceSource({ declarations: [] }); assert.deepEqual(source.current().states, []); assert.equal(source.current().source, "none"); source.tick(1); assert.deepEqual(source.current().states, []); }); it("reads the API when the deployment has a source and the viewer may read it", async (t) => { const body: DevicesBody = { officeId: "hq", devices: [{ id: "mic-1", kind: "mic", powered: true, observedAt: 1, synthetic: true }], observedAt: 1, source: "sim", synthetic: true, ttlSeconds: 5, }; const client = createTeraClient({ fetch: deployment({ "/devices": () => json(body) }) }); const readings: unknown[] = []; const source = createDeviceSource({ declarations: [DECLARATION], client, officeId: "hq", serverHasDevices: true, onReading: (reading) => readings.push(reading), }); t.after(() => source.stop()); // Before anything lands, the panel has instruments to draw rather than an // empty box that would flicker into existence a poll later. assert.equal(source.current().states.length, 1); assert.equal(source.current().live, false); await new Promise((resolve) => setTimeout(resolve, 0)); await new Promise((resolve) => setTimeout(resolve, 0)); assert.equal(source.current().live, true); assert.equal(source.current().states[0]?.powered, true); assert.equal(readings.length, 1); // The server's clock, not ours: ticking must not advance a second set of // numbers over the top of a real feed. const before = JSON.stringify(source.current().states); source.tick(5); assert.equal(JSON.stringify(source.current().states), before); }); it("skips the API entirely when /health said this box has no devices", async (t) => { let asked = 0; const client = createTeraClient({ fetch: deployment({ "/devices": () => { asked += 1; return json({}); }, }), }); const source = createDeviceSource({ declarations: [DECLARATION], client, officeId: "hq", serverHasDevices: false, }); t.after(() => source.stop()); await new Promise((resolve) => setTimeout(resolve, 0)); assert.equal(asked, 0); assert.equal(source.current().source, "sim"); }); it("reports a refused command rather than quietly applying it locally", async (t) => { // The one asymmetry between the two strategies, and it is deliberate: on a // real deployment a control that appears to work and changes nothing // anybody else can see is worse than one that says no. const client = createTeraClient({ fetch: deployment({}) }); const source = createDeviceSource({ declarations: [DECLARATION], client, officeId: "hq", serverHasDevices: true, }); // Registered before the assertions, not after them: a watch left running by // a failed assertion keeps its back-off timer alive and hangs the runner // long after the failure it is hiding. t.after(() => source.stop()); const result = await source.command({ deviceId: "mic-1", op: "power", value: true }); assert.equal(result, null); // The instruments are still there, at rest and not live — a deployment that // has stopped answering is not an office that has no hardware in it. assert.equal(source.current().states.length, 1); assert.equal(source.current().states[0]?.powered, false); assert.equal(source.current().live, false); }); it("refuses a command the declaration does not allow, before spending a request", async (t) => { let asked = 0; const client = createTeraClient({ fetch: deployment({ "/command": () => { asked += 1; return json({}); }, }), }); const source = createDeviceSource({ declarations: [DECLARATION], client, officeId: "hq", serverHasDevices: true, }); t.after(() => source.stop()); assert.equal(await source.command({ deviceId: "mic-1", op: "volume", value: 0.5 }), null); assert.equal(await source.command({ deviceId: "somebody-elses", op: "power", value: true }), null); assert.equal(asked, 0); }); it("applies a command locally, and openly, on the simulated strategy", async () => { const source = createDeviceSource({ declarations: [DECLARATION], client: null }); const state = await source.command({ deviceId: "mic-1", op: "power", value: true }); assert.equal(state?.powered, true); assert.equal(state?.synthetic, true); source.stop(); }); it("publishes only when a reading a viewer could see has changed", () => { const readings: unknown[] = []; const source = createDeviceSource({ declarations: [DECLARATION], client: null, onReading: (reading) => readings.push(reading), }); // Powered off, so every step produces the same floor reading and only // `observedAt` moves — which is deliberately not in the signature. for (let i = 0; i < 50; i += 1) source.tick(0.1); assert.equal(readings.length, 0); void source.command({ deviceId: "mic-1", op: "power", value: true }); source.tick(0.1); assert.ok(readings.length > 0); source.stop(); }); it("does no work at all once stopped", async () => { const source = createDeviceSource({ declarations: [DECLARATION], client: null }); void source.command({ deviceId: "mic-1", op: "power", value: true }); for (let i = 0; i < 20; i += 1) source.tick(0.1); source.stop(); const frozen = JSON.stringify(source.current().states); for (let i = 0; i < 20; i += 1) source.tick(0.1); assert.equal(JSON.stringify(source.current().states), frozen); assert.equal(await source.command({ deviceId: "mic-1", op: "mute", value: true }), null); }); it("survives a tab that was backgrounded for ten minutes", () => { const source = createDeviceSource({ declarations: [DECLARATION], client: null }); void source.command({ deviceId: "mic-1", op: "power", value: true }); // Six hundred seconds of `dt` would be six thousand steps in one frame. source.tick(600); // Still a valid reading, and it arrived without a hitch. const level = source.current().states[0]?.levelDb ?? 0; assert.ok(level >= -60 && level <= 0, `${level}`); }); it("has a null source for anything that genuinely has nothing to say", () => { const source = createNullDeviceSource(); assert.deepEqual(source.current().states, []); assert.equal(source.current().source, "none"); assert.equal(source.current().synthetic, true); source.tick(1); source.refresh(); source.setOccupancy(["desk-01"]); source.stop(); }); });