/**
 * The adapter that was never once imported by a test.
 *
 * `src/adapters/http.ts` is the module whose entire job is to be correct when
 * everything else has failed — no server, a 404, a static host answering with
 * its own index.html, a body about another city, a feed that has gone away
 * mid-session — and until this file it had **zero coverage**, because a pair of
 * TypeScript parameter properties on `HttpFlights` made Node's type stripping
 * refuse the whole module with `ERR_UNSUPPORTED_TYPESCRIPT_SYNTAX`.
 *
 * `engine/flights.ts` already records what that costs: the module with the
 * worst bug this project has shipped was, by construction, the one module that
 * could not be tested. So the first assertion here is that the module imports
 * at all, and the rest are the degrade paths that were being taken on trust:
 *
 *   - every feed falls back rather than failing, and **says** it fell back;
 *   - a body about somewhere else is refused, which is the San Francisco fog
 *     over Long Beach failure the whole location parameter exists to prevent;
 *   - the back-off ladder is climbed rather than retried at the frame rate,
 *     which is the specific bug a malformed `ttlSeconds` used to cause;
 *   - a device command travels in a POST of its own and never in a read.
 */

import assert from "node:assert/strict";
import { describe, it } from "node:test";
import { createTeraClient, describeLiveness } from "../../adapters/http.ts";
import { SAMPLE_MARKERS } from "../../adapters/sample.ts";
import type { SkyRegion } from "../../engine/flights.ts";
import type { DevicesBody, FiresBody, FlightsBody, WeatherBody } from "../../server/wire.ts";

/** The Bay Area, roughly, and big enough that the fixtures below are inside it. */
const SF: SkyRegion = { center: { lat: 37.77, lng: -122.42 }, radiusNm: 60 };

/** Long Beach: five hundred and ninety kilometres away, and the whole point. */
const ELSEWHERE = { lat: 33.77, lng: -118.19 };

interface Call {
  url: string;
  method: string;
  body: unknown;
}

/**
 * A fetch that answers from a table, records what it was asked, and can be told
 * to fail.
 *
 * Deliberately not a mock of `Response` — `new Response` is in Node and the
 * adapter reads `ok`, `headers.get("content-type")` and `json()`, all of which
 * a real one does correctly. A hand-rolled stub would be a second opinion about
 * what a `Response` is.
 */
function stubFetch(routes: Record<string, unknown | (() => unknown)>) {
  const calls: Call[] = [];
  const fetcher = (async (input: RequestInfo | URL, init?: RequestInit) => {
    const url = String(input);
    const path = url.split("?")[0] ?? url;
    calls.push({ url, method: init?.method ?? "GET", body: readBody(init) });
    const key = Object.keys(routes).find((candidate) => path.endsWith(candidate));
    const answer = key === undefined ? undefined : routes[key];
    const value = typeof answer === "function" ? (answer as () => unknown)() : answer;
    if (value === undefined) return new Response("no", { status: 404 });
    if (value === "html") {
      // A static host serving the SPA shell for an unknown path: a 200, with
      // HTML in it. The content-type check is the only thing between that and
      // `res.json()` throwing somewhere further in.
      return new Response("<!doctype html>", {
        status: 200,
        headers: { "content-type": "text/html" },
      });
    }
    return new Response(JSON.stringify(value), {
      status: 200,
      headers: { "content-type": "application/json" },
    });
  }) as unknown as typeof fetch;
  return { fetcher, calls };
}

function readBody(init?: RequestInit): unknown {
  if (typeof init?.body !== "string") return null;
  try {
    return JSON.parse(init.body) as unknown;
  } catch {
    return init.body;
  }
}

/** Let everything in flight settle. Two turns, because a `then` chains a `finally`. */
async function settle(): Promise<void> {
  await new Promise((resolve) => setTimeout(resolve, 0));
  await new Promise((resolve) => setTimeout(resolve, 0));
}

describe("the module imports at all", () => {
  it("has no TypeScript parameter properties left in it", async () => {
    // The assertion is the import itself: a parameter property anywhere in this
    // file makes the next line throw ERR_UNSUPPORTED_TYPESCRIPT_SYNTAX and
    // takes every other test in this file with it.
    const module = await import("../../adapters/http.ts");
    assert.equal(typeof module.createTeraClient, "function");
  });
});

describe("markers degrade to the bundled set", () => {
  it("serves the sample set, and says so, when there is no API", async () => {
    const { fetcher } = stubFetch({});
    const feed = await createTeraClient({ fetch: fetcher }).markers();
    assert.equal(feed.live, false);
    assert.equal(feed.value, SAMPLE_MARKERS);
    assert.equal(feed.generatedAt, null);
  });

  it("serves the sample set when a static host answers with its own HTML", async () => {
    const { fetcher } = stubFetch({ "/markers": "html" });
    const feed = await createTeraClient({ fetch: fetcher }).markers();
    assert.equal(feed.live, false);
  });

  it("falls back rather than showing an empty map wearing a live badge", async () => {
    const { fetcher } = stubFetch({ "/markers": { markers: [], generatedAt: "2026-01-01" } });
    const feed = await createTeraClient({ fetch: fetcher }).markers();
    assert.equal(feed.live, false);
    assert.equal(feed.value, SAMPLE_MARKERS);
  });

  it("passes a real feed through with the caller's palette", async () => {
    const markers = [{ id: "m1", name: "One", lat: 37.77, lng: -122.42, colorKey: "a" }];
    const { fetcher } = stubFetch({
      "/markers": { markers, generatedAt: "2026-08-01", refused: [{ reason: "provenance", count: 2 }] },
    });
    const feed = await createTeraClient({ fetch: fetcher, palette: { a: 0x00ff00 } }).markers();
    assert.equal(feed.live, true);
    assert.equal(feed.value.length, 1);
    assert.deepEqual(feed.palette, { a: 0x00ff00 });
    // Passed through rather than swallowed: a gate that drops rows silently is
    // indistinguishable from an empty database.
    assert.deepEqual(feed.refused, [{ reason: "provenance", count: 2 }]);
  });
});

describe("the weather is refused unless it is this city's", () => {
  const observation = (over: { lat: number; lng: number }, extra: Partial<WeatherBody> = {}) => ({
    observedAt: new Date().toISOString(),
    source: "nws",
    synthetic: false,
    location: over,
    temperatureC: 14,
    windKph: 10,
    windDirDeg: 270,
    cloudCover: 0.8,
    precipitation: 0,
    visibilityKm: 12,
    condition: "cloudy",
    ...extra,
  });

  it("adopts an observation of the place that was asked about", async () => {
    const { fetcher, calls } = stubFetch({ "/weather": observation(SF.center) });
    const feed = await createTeraClient({ fetch: fetcher }).weather(SF.center);
    assert.equal(feed.live, true);
    assert.equal(feed.value?.cloudCover, 0.8);
    // Rounded to two places for the shared cache: two viewers of one city have
    // to produce byte-identical URLs or the cache is a per-viewer cache.
    assert.ok(calls[0]?.url.includes("lat=37.77"));
  });

  it("refuses an observation of somewhere else, however good it is", async () => {
    const { fetcher } = stubFetch({ "/weather": observation(ELSEWHERE) });
    const feed = await createTeraClient({ fetch: fetcher }).weather(SF.center);
    // Nobody-was-asked, so the local climatology runs. Rendering a real
    // observation of a city the viewer is not looking at is worse than
    // rendering none: it is wrong and it is convincing.
    assert.equal(feed.live, false);
    assert.equal(feed.value, null);
  });

  it("refuses a body the server admits it invented", async () => {
    const { fetcher } = stubFetch({ "/weather": observation(SF.center, { synthetic: true }) });
    const feed = await createTeraClient({ fetch: fetcher }).weather(SF.center);
    assert.equal(feed.live, false);
    assert.equal(feed.value, null);
  });

  it("refuses a body it cannot place", async () => {
    const { fetcher } = stubFetch({
      "/weather": { ...observation(SF.center), location: undefined },
    });
    const feed = await createTeraClient({ fetch: fetcher }).weather(SF.center);
    assert.equal(feed.live, false);
  });

  it("is nobody-was-asked rather than a clear day when nothing answers", async () => {
    const { fetcher } = stubFetch({});
    const feed = await createTeraClient({ fetch: fetcher }).weather(SF.center);
    // The distinction this whole fallback exists for: a reported clear sky is
    // authoritative in `atmosphere.ts` and would delete San Francisco's marine
    // layer permanently on a zero-config box.
    assert.equal(feed.value, null);
    assert.equal(feed.observedAt, null);
  });
});

describe("traffic", () => {
  const live = (aircraft: unknown[], extra: Partial<FlightsBody> = {}) => ({
    mode: "live",
    source: "adsb",
    observedAt: Date.now(),
    aircraft,
    ttlSeconds: 5,
    redistributable: true,
    attribution: ["Data from adsb.lol"],
    ...extra,
  });

  const overSf = {
    id: "a1b2c3",
    icao24: "a1b2c3",
    callsign: "UAL221",
    lat: 37.8,
    lng: -122.4,
    altitude: 3048,
    heading: 95,
  };

  it("flies the simulator until an answer lands, and does not claim it is live", async () => {
    const { fetcher } = stubFetch({});
    const source = createTeraClient({ fetch: fetcher }).flights(SF);
    const first = source.poll();
    assert.ok(first.length > 0);
    assert.equal(source.live(), false);
    // Nobody is credited for this repo's own arithmetic.
    assert.deepEqual(source.attribution(), []);
    source.dispose();
  });

  it("adopts real positions inside the region and credits the feed", async () => {
    const { fetcher } = stubFetch({ "/flights": live([overSf]) });
    const source = createTeraClient({ fetch: fetcher }).flights(SF);
    source.poll();
    await settle();
    const aircraft = source.poll();
    assert.equal(source.live(), true);
    assert.deepEqual(aircraft.map((a) => a.id), ["a1b2c3"]);
    assert.deepEqual(source.attribution(), ["Data from adsb.lol"]);
    source.dispose();
  });

  it("refuses a feed pointed at another city rather than drawing an empty board", async () => {
    const elsewhere = { ...overSf, lat: ELSEWHERE.lat, lng: ELSEWHERE.lng };
    const { fetcher } = stubFetch({ "/flights": live([elsewhere]) });
    const source = createTeraClient({ fetch: fetcher }).flights(SF);
    source.poll();
    await settle();
    // Fifty aircraft, none within a hundred miles of the board, is a server
    // pointed at another city — and there the simulator is the honest picture.
    assert.equal(source.live(), false);
    assert.ok(source.poll().length > 0);
    assert.deepEqual(source.attribution(), []);
    source.dispose();
  });

  it("keeps an empty sky when the feed really is empty", async () => {
    const { fetcher } = stubFetch({ "/flights": live([]) });
    const source = createTeraClient({ fetch: fetcher }).flights(SF);
    source.poll();
    await settle();
    // Three in the morning over a small city is an empty sky and is live. Only
    // an empty *filter result over a non-empty body* means somewhere else.
    assert.equal(source.live(), true);
    assert.deepEqual(source.poll(), []);
    source.dispose();
  });

  it("evaluates a plan locally and keeps only the legs that are here", async () => {
    const plan = {
      mode: "plan",
      source: "sim",
      t0: Date.now() - 60_000,
      seed: 4711,
      ttlSeconds: 300,
      routes: [
        { callsign: "SFO1", from: [37.6, -122.4], to: [37.9, -122.3], fromAlt: 0, toAlt: 3000, duration: 600 },
        { callsign: "LAX1", from: [33.9, -118.4], to: [33.7, -118.1], fromAlt: 0, toAlt: 3000, duration: 600 },
      ],
    };
    const { fetcher, calls } = stubFetch({ "/flights": plan });
    const source = createTeraClient({ fetch: fetcher }).flights(SF);
    source.poll();
    await settle();
    const aircraft = source.poll();
    assert.deepEqual(aircraft.map((a) => a.callsign), ["SFO1"]);
    // A plan is not live traffic and does not say it is, even though every
    // viewer agrees about where its aircraft are.
    assert.equal(source.live(), false);
    // One request, not one per poll: a plan is arithmetic.
    source.poll();
    source.poll();
    assert.equal(calls.length, 1);
    source.dispose();
  });

  it("does not poll at the frame rate when a body forgets its ttl", async () => {
    // `Math.max(1, undefined)` is `NaN`, `now < NaN` is false forever, and the
    // poll interval quietly became the frame rate. This is that bug's test.
    const { fetcher, calls } = stubFetch({ "/flights": live([overSf], { ttlSeconds: undefined }) });
    const source = createTeraClient({ fetch: fetcher }).flights(SF);
    source.poll();
    await settle();
    for (let i = 0; i < 30; i += 1) source.poll();
    await settle();
    assert.equal(calls.length, 1);
    source.dispose();
  });

  it("survives a body it cannot read without starting a request per frame", async () => {
    const { fetcher, calls } = stubFetch({ "/flights": { mode: "live", source: "adsb", ttlSeconds: 5 } });
    const source = createTeraClient({ fetch: fetcher }).flights(SF);
    source.poll();
    await settle();
    for (let i = 0; i < 30; i += 1) source.poll();
    await settle();
    assert.equal(calls.length, 1);
    assert.equal(source.live(), false);
    source.dispose();
  });

  it("stops fetching once disposed", async () => {
    const { fetcher, calls } = stubFetch({ "/flights": live([overSf]) });
    const source = createTeraClient({ fetch: fetcher }).flights(SF);
    source.poll();
    await settle();
    source.dispose();
    const seen = calls.length;
    for (let i = 0; i < 10; i += 1) source.poll();
    await settle();
    assert.equal(calls.length, seen);
  });
});

describe("one aircraft, as a card an anonymous visitor can open", () => {
  const overSf = {
    id: "a1b2c3",
    icao24: "a1b2c3",
    callsign: " UAL221 ",
    lat: 37.8,
    lng: -122.4,
    altitude: 3048,
    heading: 95,
  };

  it("carries callsign, address, altitude, heading and position", async () => {
    const { fetcher } = stubFetch({
      "/flights": {
        mode: "live",
        source: "adsb",
        observedAt: Date.now(),
        aircraft: [overSf],
        ttlSeconds: 5,
        redistributable: true,
        attribution: ["Data from adsb.lol"],
      },
    });
    const source = createTeraClient({ fetch: fetcher }).flights(SF);
    source.poll();
    await settle();
    source.poll();

    const detail = source.detail("a1b2c3");
    assert.ok(detail !== null);
    assert.equal(detail.callsign, "UAL221");
    assert.equal(detail.icao24, "a1b2c3");
    assert.equal(detail.altitudeM, 3048);
    assert.equal(detail.altitudeFt, 10_000);
    assert.equal(detail.headingDeg, 95);
    assert.equal(detail.headingCompass, "E");
    assert.equal(detail.lat, 37.8);
    assert.equal(detail.observed, true);
    // The credit travels with the card, because a card is where the data is
    // displayed and that is what an ODbL notice is about.
    assert.deepEqual(detail.attribution, ["Data from adsb.lol"]);
    assert.ok(detail.distanceNm !== null && detail.distanceNm < 10);
    source.dispose();
  });

  it("says nothing about an aircraft that has left the feed", async () => {
    const { fetcher } = stubFetch({});
    const source = createTeraClient({ fetch: fetcher }).flights(SF);
    source.poll();
    assert.equal(source.detail("a1b2c3"), null);
    source.dispose();
  });

  it("never presents a simulated aircraft as observed, or its id as an address", async () => {
    const { fetcher } = stubFetch({});
    const source = createTeraClient({ fetch: fetcher }).flights(SF);
    const first = source.poll()[0];
    assert.ok(first !== undefined);
    const detail = source.detail(first.id);
    assert.ok(detail !== null);
    assert.equal(detail.observed, false);
    // `sim-BA286` is a route name, not a transponder address, and somebody
    // pastes this field into a registry lookup.
    assert.equal(detail.icao24, null);
    assert.deepEqual(detail.attribution, []);
    source.dispose();
  });
});

describe("the corner label only claims what is true", () => {
  it("says nothing when nothing is live", () => {
    assert.equal(describeLiveness({ markers: false, weather: false, flights: false }), "");
  });

  it("names the parts rather than claiming the whole map", () => {
    assert.equal(describeLiveness({ markers: false, weather: true, flights: true }), "live weather + traffic");
  });

  it("earns the unqualified claim only when all three are live", () => {
    assert.equal(describeLiveness({ markers: true, weather: true, flights: true }), "live data");
  });
});

describe("devices", () => {
  const body: DevicesBody = {
    officeId: "hq",
    devices: [
      {
        id: "mic-1",
        kind: "mic",
        powered: true,
        muted: false,
        gainDb: 12,
        levelDb: -21.5,
        observedAt: 1,
        synthetic: true,
      },
    ],
    observedAt: 1,
    source: "sim",
    synthetic: true,
    ttlSeconds: 5,
  };

  it("is empty and not live when there is no API", async () => {
    const { fetcher } = stubFetch({});
    const feed = await createTeraClient({ fetch: fetcher }).devices("hq");
    assert.deepEqual(feed.value, []);
    assert.equal(feed.live, false);
    assert.equal(feed.source, "none");
    // Not a claim that anybody observed these zero readings.
    assert.equal(feed.synthetic, true);
  });

  it("is empty and not live when the deployment refuses an anonymous read", async () => {
    const { fetcher } = stubFetch({ "/devices": undefined });
    const feed = await createTeraClient({ fetch: fetcher }).devices("hq");
    assert.equal(feed.live, false);
    assert.deepEqual(feed.value, []);
  });

  it("adopts a body and keeps its provenance", async () => {
    const { fetcher, calls } = stubFetch({ "/devices": body });
    const feed = await createTeraClient({ fetch: fetcher }).devices("hq");
    assert.equal(feed.live, true);
    assert.equal(feed.synthetic, true);
    assert.equal(feed.source, "sim");
    assert.equal(feed.value[0]?.levelDb, -21.5);
    assert.equal(calls[0]?.url, "/api/v1/offices/hq/devices");
  });

  it("treats an office id as an id and not as a path", async () => {
    const { fetcher, calls } = stubFetch({ "/devices": body });
    await createTeraClient({ fetch: fetcher }).devices("../secrets");
    assert.equal(calls[0]?.url, "/api/v1/offices/..%2Fsecrets/devices");
  });

  it("is empty when the body is the wrong shape", async () => {
    const { fetcher } = stubFetch({ "/devices": { officeId: "hq", devices: "lots" } });
    const feed = await createTeraClient({ fetch: fetcher }).devices("hq");
    assert.equal(feed.live, false);
  });

  it("sends a command as a POST of its own, never on the read", async () => {
    const { fetcher, calls } = stubFetch({
      "/devices/command": { officeId: "hq", device: { ...body.devices[0], powered: false }, observedAt: 2 },
    });
    const client = createTeraClient({ fetch: fetcher });
    const state = await client.commandDevice("hq", { deviceId: "mic-1", op: "power", value: false });

    assert.equal(state?.powered, false);
    const call = calls[0];
    assert.equal(call?.method, "POST");
    assert.equal(call?.url, "/api/v1/offices/hq/devices/command");
    assert.deepEqual(call?.body, { command: { deviceId: "mic-1", op: "power", value: false } });
    // The read route was never touched. A command that could ride on a GET is a
    // command a shared cache can replay.
    assert.equal(calls.filter((c) => c.method === "GET").length, 0);
  });

  it("reports a refused command rather than pretending it worked", async () => {
    const { fetcher } = stubFetch({});
    const client = createTeraClient({ fetch: fetcher });
    const state = await client.commandDevice("hq", { deviceId: "mic-1", op: "power", value: true });
    assert.equal(state, null);
  });

  it("reports a 200 whose body is not a result", async () => {
    const { fetcher } = stubFetch({ "/devices/command": { officeId: "hq" } });
    const client = createTeraClient({ fetch: fetcher });
    const state = await client.commandDevice("hq", { deviceId: "mic-1", op: "power", value: true });
    assert.equal(state, null);
  });

  it("publishes the first answer, then only when a reading changes", async () => {
    let served: DevicesBody = body;
    const { fetcher } = stubFetch({ "/devices": () => served });
    const feeds: number[] = [];
    const watch = createTeraClient({ fetch: fetcher }).watchDevices("hq", (feed) => {
      feeds.push(feed.value[0]?.levelDb ?? 0);
    });
    await settle();
    assert.equal(feeds.length, 1);

    // The same reading again, with only `observedAt` moved: not news, and
    // publishing it would rebuild the panel on every poll forever.
    served = { ...body, observedAt: 99, devices: [{ ...body.devices[0]!, observedAt: 99 }] };
    watch.refresh();
    await settle();
    assert.equal(feeds.length, 1);

    served = { ...body, devices: [{ ...body.devices[0]!, levelDb: -12.5 }] };
    watch.refresh();
    await settle();
    assert.deepEqual(feeds, [-21.5, -12.5]);
    watch.stop();
  });

  it("holds the latest feed so a panel opening late does not wait for a poll", async () => {
    const { fetcher } = stubFetch({ "/devices": body });
    const watch = createTeraClient({ fetch: fetcher }).watchDevices("hq", () => {});
    await settle();
    assert.equal(watch.current().value[0]?.id, "mic-1");
    watch.stop();
  });

  it("stops dead, and drops an answer that lands after it was stopped", async () => {
    const { fetcher, calls } = stubFetch({ "/devices": body });
    const feeds: unknown[] = [];
    const watch = createTeraClient({ fetch: fetcher }).watchDevices("hq", (feed) => feeds.push(feed));
    watch.stop();
    await settle();
    const seen = calls.length;
    watch.refresh();
    await settle();
    assert.equal(calls.length, seen);
    assert.equal(feeds.length, 0);
  });
});

/**
 * The fire feed, from the browser's side.
 *
 * Two properties, and both of them are about a board with nothing on it — which
 * is the commonest correct answer this feed will ever give, and the one that is
 * indistinguishable from a broken feed unless the client is careful.
 */
const FIRES: FiresBody = {
  source: "cloud1",
  fetchedAt: "2026-08-22T22:22:35.806Z",
  latestSeen: "2026-08-22T22:20:06Z",
  incidents: [],
  detections: [],
  detectionWindowHours: 24,
  ttlSeconds: 600,
};

describe("fires", () => {
  it("asks one unparameterised route, because the answer is the same for everyone", async () => {
    const { fetcher, calls } = stubFetch({ "/fires": FIRES });
    const body = await createTeraClient({ fetch: fetcher }).fires();
    assert.equal(body?.source, "cloud1");
    assert.equal(calls.length, 1);
    // No `?city=`. The whole state's live set is small, the boards are
    // rectangles inside it, and `promote()` has to clip anyway.
    assert.ok(!String(calls[0]?.url).includes("?"));
  });

  it("is null when nobody answered, and never an empty board", async () => {
    const { fetcher } = stubFetch({});
    assert.equal(await createTeraClient({ fetch: fetcher }).fires(), null);
  });

  it("refuses a 200 that is the SPA shell rather than a body", async () => {
    const { fetcher } = stubFetch({ "/fires": "html" });
    assert.equal(await createTeraClient({ fetch: fetcher }).fires(), null);
  });

  it("republishes an unchanged body, so the age on screen keeps moving", async (t) => {
    // Deliberately unlike `watchDevices`, which publishes only on change. An
    // unchanged fire body still carries a NEWER `fetchedAt`, and that is the
    // field a quiet board is captioned with. Suppressing the republish would
    // freeze "4 minutes ago" on screen at the moment of the last change, so a
    // feed that died an hour ago would go on claiming to be fresh — the precise
    // failure a stated fetch age exists to prevent.
    let served = 0;
    const { fetcher } = stubFetch({
      "/fires": () => {
        served += 1;
        return { ...FIRES, ttlSeconds: 60 };
      },
    });
    const bodies: (FiresBody | null)[] = [];
    const watch = createTeraClient({ fetch: fetcher }).watchFires((body) => bodies.push(body));
    t.after(() => watch.stop());

    await settle();
    assert.equal(bodies.length, 1);
    assert.equal(watch.current()?.source, "cloud1");

    watch.refresh();
    await settle();
    await settle();
    assert.equal(served, 2);
    assert.equal(bodies.length, 2);
  });

  it("reports a refusal rather than freezing on the last good board", async (t) => {
    let up = true;
    const { fetcher } = stubFetch({ "/fires": () => (up ? FIRES : undefined) });
    const bodies: (FiresBody | null)[] = [];
    const watch = createTeraClient({ fetch: fetcher }).watchFires((body) => bodies.push(body));
    t.after(() => watch.stop());

    await settle();
    assert.equal(bodies[0]?.source, "cloud1");

    up = false;
    watch.refresh();
    await settle();
    await settle();
    // `null`, not the previous board. "California stopped burning" and "I have
    // stopped hearing about California" are different facts and only one of
    // them may be drawn.
    assert.equal(bodies.at(-1), null);
    assert.equal(watch.current(), null);
  });

  it("refuses a body with no incident array, whatever its status was", async (t) => {
    const { fetcher } = stubFetch({ "/fires": { source: "cloud1", ttlSeconds: 600 } });
    const bodies: (FiresBody | null)[] = [];
    const watch = createTeraClient({ fetch: fetcher }).watchFires((body) => bodies.push(body));
    t.after(() => watch.stop());
    await settle();
    // The shape a server one version behind this one sends. Adopting it would
    // reach `promote()` as an empty board — a silent all-clear.
    assert.equal(bodies.at(-1), null);
  });

  it("does no work at all once stopped", async () => {
    const { fetcher, calls } = stubFetch({ "/fires": FIRES });
    const bodies: (FiresBody | null)[] = [];
    const watch = createTeraClient({ fetch: fetcher }).watchFires((body) => bodies.push(body));
    await settle();
    const seen = calls.length;
    watch.stop();
    watch.refresh();
    await settle();
    assert.equal(calls.length, seen);
  });
});
