/** * The one place the fire wire and the fire renderer meet, checked from both * sides. * * `src/server/fires.ts` owns `promote()` and the shape it returns. * `src/engine/scene.ts` owns `SceneHandle.setFires` and declares the *minimum* * a renderer needs — deliberately its own copy, so the engine never imports a * wire module and neither file has to exist for the other to compile. The cost * of that decision is exactly one thing: nothing stops the two drifting. * * This is that one thing. The assignment below is the whole test — if * `FirePromotion` stops satisfying `FireView`, `tsc` fails here rather than in * a render loop three weeks later — and the runtime assertions state the same * contract for a reader who is not running the compiler. * * The risk it guards is named in the round's own notes: cloud-1's `fires.py` * was modified the day this landed and nothing in this repo's test suite covers * it, so a renamed column has to fail loudly somewhere. This is the somewhere on * the client side. */ import assert from "node:assert/strict"; import { describe, it } from "node:test"; import { emptyPromotion, promote, type FirePromotion } from "../server/fires.ts"; import type { FireView } from "../engine/scene.ts"; /** * The compile-time half. A `FirePromotion` must be usable wherever the scene * asks for a `FireView`, with no mapping step in between — because there is no * mapping step in `main.ts` either. */ const _assignable: (p: FirePromotion) => FireView = (p) => p; const SOCAL = { minLat: 32.5, maxLat: 34.9, minLng: -119.5, maxLng: -117.2 }; describe("the fire seam", () => { it("hands promote() output straight to the scene with no adapter", () => { const view: FireView = _assignable(emptyPromotion()); assert.deepEqual(view.drawn, []); assert.deepEqual(view.detections, []); // Epoch zero rather than "now": nothing has answered, and saying so with a // real timestamp would be the lie this field exists to prevent. assert.equal(view.fetchedAt, new Date(0).toISOString()); assert.equal(view.ageMs, null); }); it("survives a body it did not build, rather than throwing into a render loop", () => { for (const body of [null, undefined, {} as never]) { const view: FireView = _assignable(promote(body, SOCAL, 1_000)); assert.deepEqual(view.drawn, []); assert.deepEqual(view.detections, []); } }); it("keeps the field names the renderer reads", () => { // Named explicitly rather than inferred, so a rename on either side lands // here as a failing assertion with the old name printed in it. const view: FireView = _assignable(emptyPromotion()); for (const key of ["drawn", "detections", "fetchedAt", "ageMs"]) { assert.ok(key in view, `FireView lost ${key}`); } }); });