1
0

Real weather, real aircraft, a heightfield off the main thread, and instruments

Three things that were built and never connected, connected.

**The weather was already there.** `observe()` has always taken a
`WeatherObservation` and `main.ts` has always passed null, so the cloud,
precipitation, visibility and marine-layer paths in atmosphere.ts had never run
outside a test. The server already shipped NWS, met.no and Open-Meteo, all
configured off. What was actually missing was that a single TERA_ORIGIN_LAT/LNG
served one metro and lied to the other — so weather and traffic are per-region
now, derived from the city's own bounds, and the Bay Area gets its fog while
Long Beach gets its own sky. The route takes ?city= or a validated ?lat=&lng=
and refuses to become an open geocoding proxy for the planet.

**The heightfield moved to a Worker.** 2.3 s of blocked main thread at boot, and
another ~950 ms of point-in-polygon on top of it: the park mask is filled in the
worker now, and block placement samples four corners and only runs the exact
test on a cell that straddles an edge — 8 buildings differ out of 185,036.
createScene is async and takes a Stage as a consequence, and there is a
main-thread fallback because "clone it and it works" has no exception clause.

**Spaces is a chunk you fetch when you reach for the door**, not one everybody
downloads. Same for the godmode tools. The entry chunk is 722 kB rather than
772; three.js is most of what is left and splitting it is a different job.

**Godmode is an instrument panel now** rather than one slider: the date and the
season, not just the hour, so the Meeus moon and the sun's seasonal arc become
visible instead of merely correct; a weather override that says on screen when
it is lying; a frame-time and draw-call readout; and a pose editor that emits a
paste-ready Chapter block, which is the thing that makes adding New York cheap.

Two blockers the review caught:

  - Every city switch leaked 8 GPU textures — one of them a 2048x2048 shadow map
    — and ~10.5 shader programs, and deleteTexture had never been called once in
    the app's lifetime. The renderer was being built per scene; it belongs to the
    canvas, for the life of the page.
  - An upstream fetch that threw rather than returning null skipped the cache
    stamp, so the TTL — the only rate limit on outbound calls — collapsed to one
    upstream request per inbound request, and the caller got a 500.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
This commit is contained in:
2026-08-06 03:25:31 -07:00
parent a6f6a91813
commit e41c90fe8d
39 changed files with 8482 additions and 503 deletions
+257
View File
@@ -0,0 +1,257 @@
/**
* Live traffic, and the budget that keeps it welcome.
*
* adsb.lol is a volunteer-fed community feed that asks for no more than one
* request per second and will rate-limit a caller who ignores it. Most of this
* file is therefore about **how often this server is capable of calling out**,
* not about what comes back: one poll per region per TTL, a TTL with a floor
* under it, and a refused query that costs the upstream nothing. The arithmetic
* those tests pin down is written out in `flights/index.ts`.
*
* The one that is not about rate is the URL assertion. A caller's coordinate
* must select a configured region and must never itself be fetched.
*/
import assert from "node:assert/strict";
import { mkdtemp, rm, writeFile } from "node:fs/promises";
import { tmpdir } from "node:os";
import { join } from "node:path";
import { after, before, beforeEach, describe, it } from "node:test";
import { buildApp } from "../app.ts";
import { loadConfig } from "../config.ts";
import type { FlightsBody } from "../../../src/server/wire.ts";
const realFetch = globalThis.fetch;
let calls: string[] = [];
let feedIsUp = true;
/** A body to serve instead of the usual one, for the wrong-shape tests. */
let feedOverride: unknown = undefined;
/** One aircraft, whose id encodes the point that was asked about. */
function feed(url: string): unknown | undefined {
if (!feedIsUp) return undefined;
const point = /\/v2\/point\/(-?[\d.]+)\/(-?[\d.]+)\/(\d+)$/.exec(url);
if (point === null) return undefined;
if (feedOverride !== undefined) return feedOverride;
return {
now: 1_770_000_000_000,
ac: [{ hex: `a${point[1]}`, flight: "LMB1 ", lat: 37.5, lon: -122.3, alt_baro: 10_000, track: 90 }],
};
}
globalThis.fetch = (async (input: unknown) => {
const url = String(input);
calls.push(url);
const body = feed(url);
if (body === undefined) return new Response("nope", { status: 503 });
return new Response(JSON.stringify(body), {
status: 200,
headers: { "content-type": "application/json" },
});
}) as unknown as typeof globalThis.fetch;
after(() => {
globalThis.fetch = realFetch;
});
beforeEach(() => {
calls = [];
feedIsUp = true;
feedOverride = undefined;
});
function appWith(env: Record<string, string>) {
const config = loadConfig(env);
config.logLevel = "silent";
return buildApp(config);
}
const adsbEnv = { TERA_FLIGHTS_SOURCE: "adsb" };
describe("the ADS-B source", () => {
it("polls each city's own centre, and never the caller's coordinate", async () => {
const app = appWith(adsbEnv);
after(() => app.close());
// Pasadena, which is inside the Southland region and is not a point this
// deployment serves.
await app.inject({ method: "GET", url: "/api/v1/flights?lat=34.1478&lng=-118.1445" });
await app.inject({ method: "GET", url: "/api/v1/flights?city=sf" });
assert.deepEqual(calls, [
"https://api.adsb.lol/v2/point/33.8200/-118.0500/40",
"https://api.adsb.lol/v2/point/37.7749/-122.4194/40",
]);
});
it("clamps a radius the feeds would reject, and says so", async () => {
const config = loadConfig({ ...adsbEnv, TERA_ADSB_RADIUS_NM: "2500" });
config.logLevel = "silent";
const app = buildApp(config);
after(() => app.close());
assert.equal(config.flights.radiusNm, 250);
assert.match(config.degraded[0] ?? "", /TERA_ADSB_RADIUS_NM/);
await app.inject({ method: "GET", url: "/api/v1/flights" });
assert.equal(calls[0], "https://api.adsb.lol/v2/point/37.7749/-122.4194/250");
});
it("polls once per region per TTL, whatever the request rate is", async () => {
const app = appWith(adsbEnv);
after(() => app.close());
const ask = (city: string) =>
app.inject({ method: "GET", url: `/api/v1/flights?city=${city}` });
await Promise.all([ask("sf"), ask("sf"), ask("socal"), ask("sf"), ask("socal")]);
for (let i = 0; i < 6; i++) await ask("sf");
// Two regions, eleven requests, two upstream calls.
assert.equal(calls.length, 2);
});
it("floors the poll interval so TERA_FLIGHTS_TTL=0 is not a flood", async () => {
// This is the bug the floor exists for: a zero TTL used to mean one
// outbound request per inbound request, straight through the rate limit,
// from a setting that reads like "as fresh as possible".
const app = appWith({ ...adsbEnv, TERA_FLIGHTS_TTL: "0" });
after(() => app.close());
let body: FlightsBody | null = null;
for (let i = 0; i < 8; i++) {
body = (await app.inject({ method: "GET", url: "/api/v1/flights" })).json<FlightsBody>();
}
assert.equal(calls.length, 1);
assert.ok(body !== null && body.mode === "live" && body.ttlSeconds === 5);
});
it("caps the poll interval too, because aircraft move", async () => {
const app = appWith({ ...adsbEnv, TERA_FLIGHTS_TTL: "3600" });
after(() => app.close());
const body = (await app.inject({ method: "GET", url: "/api/v1/flights" })).json<FlightsBody>();
assert.ok(body.mode === "live" && body.ttlSeconds === 15);
});
it("costs the upstream nothing when the query is refused", async () => {
const app = appWith(adsbEnv);
after(() => app.close());
const res = await app.inject({ method: "GET", url: "/api/v1/flights?city=atlantis" });
assert.equal(res.statusCode, 400);
assert.deepEqual(calls, []);
});
it("falls back to the simulated plan rather than to an empty sky", async () => {
feedIsUp = false;
const app = appWith(adsbEnv);
after(() => app.close());
const body = (await app.inject({ method: "GET", url: "/api/v1/flights" })).json<FlightsBody>();
assert.equal(body.mode, "plan");
assert.ok(body.mode === "plan" && body.routes.length > 0);
});
it("credits the feed it took the positions from", async () => {
const app = appWith(adsbEnv);
after(() => app.close());
const body = (await app.inject({ method: "GET", url: "/api/v1/flights" })).json<FlightsBody>();
assert.ok(body.mode === "live");
assert.match(body.attribution?.[0] ?? "", /adsb\.lol/);
});
});
/**
* The failure that turns a rate limit inside out.
*
* A feed that goes *down* is handled everywhere. A feed that stays up and
* answers 200 with a field of the wrong type used to throw out of `normalise`,
* past `upstream.ts` — which then never stamped its clock — and out to the
* route as a 500. The TTL is the only rate limit on outbound calls, so losing it
* meant one request to adsb.lol per inbound request, from the operator's
* address, for as long as the feed stayed broken.
*/
describe("a feed that changed shape", () => {
it("answers with the plan instead of a 500", async () => {
// Valid JSON, wrong shape: `ac` is a number where an array belongs.
feedOverride = { ac: 5, now: 1 };
const app = appWith(adsbEnv);
after(() => app.close());
const res = await app.inject({ method: "GET", url: "/api/v1/flights" });
assert.equal(res.statusCode, 200);
assert.equal(res.json<FlightsBody>().mode, "plan");
});
it("still polls once per TTL, which is the whole of the rate limit", async () => {
feedOverride = { ac: 5, now: 1 };
const app = appWith(adsbEnv);
after(() => app.close());
for (let i = 0; i < 5; i++) await app.inject({ method: "GET", url: "/api/v1/flights" });
assert.equal(calls.length, 1);
});
it("survives rows that are not objects", async () => {
feedOverride = { ac: [null, 7, "LMB1", { hex: "ok", lat: 37.5, lon: -122.3 }], now: 1 };
const app = appWith(adsbEnv);
after(() => app.close());
const body = (await app.inject({ method: "GET", url: "/api/v1/flights" })).json<FlightsBody>();
assert.ok(body.mode === "live" && body.aircraft.length === 1);
});
it("caps how much of somebody else's sky it will hold and serve", async () => {
feedOverride = {
now: 1,
ac: Array.from({ length: 6000 }, (_, i) => ({
hex: `x${i}`,
lat: 37.5,
lon: -122.3,
alt_baro: 1000,
})),
};
const app = appWith(adsbEnv);
after(() => app.close());
const body = (await app.inject({ method: "GET", url: "/api/v1/flights" })).json<FlightsBody>();
assert.ok(body.mode === "live" && body.aircraft.length === 5000);
});
});
describe("a local receiver", () => {
let path = "";
before(async () => {
const dir = await mkdtemp(join(tmpdir(), "tera-flights-"));
path = join(dir, "aircraft.json");
await writeFile(
path,
JSON.stringify({
now: 1_770_000_000,
aircraft: [{ hex: "abc123", flight: "LMB9 ", lat: 37.6, lon: -122.4, alt_baro: 3000 }],
}),
);
});
it("has one antenna, so every region reads the same snapshot once", async () => {
const app = appWith({ TERA_FLIGHTS_SOURCE: "dump1090", TERA_DUMP1090_PATH: path });
after(() => app.close());
const sf = (
await app.inject({ method: "GET", url: "/api/v1/flights?city=sf" })
).json<FlightsBody>();
assert.ok(sf.mode === "live" && sf.aircraft.length === 1);
// Take the file away, then ask for the other city inside the TTL. A live
// answer proves the two regions share one cache entry — a per-region key
// would have gone back to disk here and found nothing.
await rm(path);
const socal = (
await app.inject({ method: "GET", url: "/api/v1/flights?city=socal" })
).json<FlightsBody>();
assert.ok(socal.mode === "live");
assert.deepEqual(socal.aircraft, sf.aircraft);
});
});
+263
View File
@@ -0,0 +1,263 @@
/**
* The marker feed: the refusal that makes the `member` tier real, and a file an
* operator can actually use.
*
* The first half is one assertion said several ways. `src/access.ts` has three
* tiers and the middle one gated nothing — `member` was a word in a type union
* with no server behaviour behind it. A tier that never refuses anybody is not a
* tier, so a configured feed takes a session, and the test that matters is the
* negative: an anonymous caller gets 401 and no rows, with no query parameter,
* header or cleared cookie that changes it.
*
* The second half is the file itself — what a malformed row does to the rest of
* the snapshot, and the reload path, which has to work without a restart because
* the sync oneshot runs on a timer and nothing signals this process.
*
* Follows `offices.test.ts`: a temp directory, `buildApp` over a fake
* environment, `inject()` rather than a socket, and HS256 by hand.
*/
import assert from "node:assert/strict";
import { createHmac } from "node:crypto";
import { mkdtemp, rm, writeFile } from "node:fs/promises";
import { tmpdir } from "node:os";
import { join } from "node:path";
import { after, before, beforeEach, describe, it } from "node:test";
import { setTimeout as sleep } from "node:timers/promises";
import { buildApp } from "../app.ts";
import { loadConfig } from "../config.ts";
import type { ErrorBody, MarkersBody } from "../../../src/server/wire.ts";
const SECRET = "not-a-real-secret-and-never-was";
const FERRY = {
id: "ferry-building",
label: "Ferry Building",
colorKey: "sector.civic",
lat: 37.7955,
lng: -122.3937,
provenance: "hand-placed",
};
let file = "";
before(async () => {
const dir = await mkdtemp(join(tmpdir(), "tera-markers-"));
file = join(dir, "markers.json");
});
beforeEach(async () => {
await write({ generatedAt: "2026-08-05T09:00:00Z", markers: [FERRY] });
});
async function write(snapshot: unknown): Promise<void> {
await writeFile(file, JSON.stringify(snapshot));
}
/** A feed that is configured, with an issuer that can produce a member. */
const feedEnv = {
TERA_MARKERS_SOURCE: "file",
TERA_AUTH_MODE: "jwt",
TERA_AUTH_JWT_SECRET: SECRET,
};
function appWith(env: Record<string, string>) {
const config = loadConfig({ TERA_MARKERS_FILE: file, ...env });
config.logLevel = "silent";
return buildApp(config);
}
function member(): string {
const encode = (value: unknown): string =>
Buffer.from(JSON.stringify(value)).toString("base64url");
const claims = { sub: "someone", exp: Math.floor(Date.now() / 1000) + 600 };
const signed = `${encode({ alg: "HS256", typ: "JWT" })}.${encode(claims)}`;
return `${signed}.${createHmac("sha256", SECRET).update(signed).digest("base64url")}`;
}
function asMember(app: ReturnType<typeof buildApp>) {
return app.inject({
method: "GET",
url: "/api/v1/markers",
headers: { authorization: `Bearer ${member()}` },
});
}
describe("a configured marker feed", () => {
it("refuses an anonymous caller", async () => {
const app = appWith(feedEnv);
after(() => app.close());
const res = await app.inject({ method: "GET", url: "/api/v1/markers" });
assert.equal(res.statusCode, 401);
assert.equal(res.json<ErrorBody>().error, "unauthorized");
assert.equal(res.headers["www-authenticate"], "Bearer");
// No rows anywhere in the refusal, and nothing shared may keep it.
assert.ok(!res.body.includes("Ferry"));
assert.equal(res.headers["cache-control"], "private, no-store");
});
it("refuses every shape of not-being-signed-in", async () => {
const app = appWith(feedEnv);
after(() => app.close());
const attempts = [
{},
{ authorization: "Bearer not-a-jwt" },
{ authorization: `Bearer ${member()}tampered` },
{ authorization: "Basic Zm9vOmJhcg==" },
{ cookie: "tera_session=" },
{ cookie: "tera_session=%zz" },
];
for (const headers of attempts) {
const res = await app.inject({ method: "GET", url: "/api/v1/markers", headers });
assert.equal(res.statusCode, 401, `${JSON.stringify(headers)} must not get the feed`);
}
});
it("serves the snapshot to a member", async () => {
const app = appWith(feedEnv);
after(() => app.close());
const res = await asMember(app);
assert.equal(res.statusCode, 200);
const body = res.json<MarkersBody>();
assert.equal(body.markers.length, 1);
assert.equal(body.markers[0]?.id, "ferry-building");
assert.equal(body.generatedAt, "2026-08-05T09:00:00Z");
});
it("never lets a shared cache keep a member's copy", async () => {
const app = appWith(feedEnv);
after(() => app.close());
assert.equal((await asMember(app)).headers["cache-control"], "private, no-store");
});
it("is unreachable, loudly, on a box where nobody can sign in", async () => {
const config = loadConfig({ TERA_MARKERS_FILE: file, TERA_MARKERS_SOURCE: "file" });
config.logLevel = "silent";
const app = buildApp(config);
after(() => app.close());
// Fail-closed, and it says so rather than leaving an operator to work it out
// from an empty map.
assert.equal(config.auth.mode, "none");
assert.ok(config.degraded.some((line) => line.includes("TERA_MARKERS_SOURCE=file")));
const anonymous = await app.inject({ method: "GET", url: "/api/v1/markers" });
assert.equal(anonymous.statusCode, 401);
// Even a token that would be valid elsewhere: mode=none verifies nothing.
assert.equal((await asMember(app)).statusCode, 401);
});
it("still answers the public empty body on a box with no feed", async () => {
// The acceptance test's box. There is nothing here to protect, and making a
// stranger sign in to be told "no markers" would gain nobody anything.
const app = appWith({ TERA_MARKERS_SOURCE: "none" });
after(() => app.close());
const res = await app.inject({ method: "GET", url: "/api/v1/markers" });
assert.equal(res.statusCode, 200);
assert.deepEqual(res.json<MarkersBody>().markers, []);
assert.match(String(res.headers["cache-control"]), /^public, max-age=/);
});
});
describe("the marker file itself", () => {
it("accepts a bare array, because that is what a person writes first", async () => {
await write([FERRY]);
const app = appWith(feedEnv);
after(() => app.close());
assert.equal((await asMember(app)).json<MarkersBody>().markers.length, 1);
});
it("refuses a bad row without losing the good ones, and reports the count", async () => {
await write({
markers: [
FERRY,
{ ...FERRY, id: "osm-row", provenance: "nominatim" },
{ ...FERRY, id: "leaky", ownerEmail: "someone@example.com" },
{ ...FERRY, id: "broken", lat: 200 },
],
});
const app = appWith(feedEnv);
after(() => app.close());
const body = (await asMember(app)).json<MarkersBody>();
assert.deepEqual(
body.markers.map((marker) => marker.id),
["ferry-building"],
);
assert.equal(body.refused.length, 3);
assert.ok(body.refused.some((entry) => entry.reason.includes("allowlist")));
assert.ok(body.refused.some((entry) => entry.reason.includes("ownerEmail")));
});
it("picks up a rewritten file without a restart", async () => {
// TERA_MARKERS_TTL is the reload interval. Zero means every request checks,
// which is what makes this assertable without sleeping for five minutes.
const app = appWith({ ...feedEnv, TERA_MARKERS_TTL: "0" });
after(() => app.close());
assert.equal((await asMember(app)).json<MarkersBody>().markers.length, 1);
await write({ markers: [FERRY, { ...FERRY, id: "coit-tower", label: "Coit Tower" }] });
// A zero TTL means "check on the next millisecond", not "check twice inside
// the same one" — `inject()` is fast enough that both requests can land on
// the same `Date.now()`, which is a property of the test and not of the
// reload.
await sleep(5);
const reloaded = (await asMember(app)).json<MarkersBody>();
assert.deepEqual(
reloaded.markers.map((marker) => marker.id),
["ferry-building", "coit-tower"],
);
});
it("keeps the last good snapshot when a read fails", async () => {
const app = appWith({ ...feedEnv, TERA_MARKERS_TTL: "0" });
after(() => app.close());
assert.equal((await asMember(app)).json<MarkersBody>().markers.length, 1);
// The file goes away mid-flight — a rewrite by something less careful than
// the sync oneshot, a permissions change, a full disk. Emptying the map for
// a whole TTL is the expensive mistake; serving what was there is the cheap
// one.
await rm(file);
await sleep(5);
const survived = (await asMember(app)).json<MarkersBody>();
assert.equal(survived.markers.length, 1);
// And it recovers on its own once the file comes back.
await write({ markers: [{ ...FERRY, id: "coit-tower" }] });
await sleep(5);
const recovered = (await asMember(app)).json<MarkersBody>();
assert.deepEqual(
recovered.markers.map((marker) => marker.id),
["coit-tower"],
);
});
it("says it cannot read the file rather than pretending there are no markers", async () => {
await rm(file);
const app = appWith(feedEnv);
after(() => app.close());
const body = (await asMember(app)).json<MarkersBody>();
assert.deepEqual(body.markers, []);
assert.equal(body.refused.length, 1);
assert.match(body.refused[0]?.reason ?? "", /could not be read/);
});
it("survives a file that is not JSON at all", async () => {
await writeFile(file, "{ this is not json");
const app = appWith(feedEnv);
after(() => app.close());
const res = await asMember(app);
assert.equal(res.statusCode, 200);
assert.deepEqual(res.json<MarkersBody>().markers, []);
});
});
+280
View File
@@ -0,0 +1,280 @@
/**
* The served-region allowlist, and everything it refuses.
*
* Two halves. The first is that a box answers for the *places it was configured
* for* rather than for one origin, because the map has two cities six hundred
* kilometres apart and the Bay Area's fog is not Los Angeles's weather.
*
* The second is the one with teeth: **a caller's coordinate must never reach an
* upstream.** An endpoint that fetches any point on demand is an amplifier
* aimed at somebody else's public-good API, with this deployment's contact
* string on every request. So `?lat=&lng=` selects among configured points and
* nothing else, and everything it cannot select is a 400. See the header of
* `regions.ts`; the assertion that this refusal actually holds at the HTTP layer
* is `weather.test.ts`.
*/
import assert from "node:assert/strict";
import { after, describe, it } from "node:test";
import { buildApp } from "../app.ts";
import { loadConfig } from "../config.ts";
import { loadRegions, resolveRegion, type RegionSet } from "../regions.ts";
import type { ErrorBody, FlightsBody, WeatherBody } from "../../../src/server/wire.ts";
function appWith(env: Record<string, string>) {
const config = loadConfig(env);
config.logLevel = "silent";
return buildApp(config);
}
/** The two shipped cities, which is what an empty environment resolves to. */
function shipped(): RegionSet {
return loadConfig({}).regions;
}
describe("the region set a box ends up with", () => {
it("serves both shipped cities when handed nothing, San Francisco first", () => {
const config = loadConfig({});
assert.deepEqual(
config.regions.map((region) => region.id),
["sf", "socal"],
);
assert.deepEqual(config.degraded, []);
// The default region is still the origin a pre-existing env file named, so
// a bare GET answers exactly as it did before regions existed.
assert.equal(config.regions[0].lat, config.origin.lat);
});
it("promotes the shipped city an operator's origin falls inside", () => {
const config = loadConfig({ TERA_ORIGIN_LAT: "34.05", TERA_ORIGIN_LNG: "-118.24" });
assert.deepEqual(
config.regions.map((region) => region.id),
["socal", "sf"],
);
assert.deepEqual(config.degraded, []);
});
it("adds an origin that is neither city, and makes it the default", () => {
const config = loadConfig({ TERA_ORIGIN_LAT: "39.7392", TERA_ORIGIN_LNG: "-104.9903" });
assert.deepEqual(
config.regions.map((region) => region.id),
["origin", "sf", "socal"],
);
assert.equal(config.regions[0].lat, 39.7392);
});
it("takes TERA_REGIONS literally, in order, with an optional radius", () => {
const config = loadConfig({
TERA_REGIONS: "pdx:45.5152,-122.6784:80; sea:47.6062,-122.3321",
});
assert.deepEqual(config.regions, [
{ id: "pdx", lat: 45.5152, lng: -122.6784, radiusKm: 80 },
{ id: "sea", lat: 47.6062, lng: -122.3321, radiusKm: 120 },
]);
assert.deepEqual(config.degraded, []);
});
it("drops a malformed entry with a sentence and keeps the good ones", () => {
const config = loadConfig({ TERA_REGIONS: "pdx:45.5152,-122.6784; nowhere; sea:200,0" });
assert.deepEqual(
config.regions.map((region) => region.id),
["pdx"],
);
assert.equal(config.degraded.length, 2);
assert.match(config.degraded[0] ?? "", /nowhere/);
assert.match(config.degraded[1] ?? "", /not a place on Earth/);
});
it("falls back to the shipped cities when nothing in TERA_REGIONS parses", () => {
const config = loadConfig({ TERA_REGIONS: "?????" });
assert.deepEqual(
config.regions.map((region) => region.id),
["sf", "socal"],
);
assert.ok(config.degraded.some((line) => line.includes("nothing in it parsed")));
});
it("keeps the first of two entries sharing an id", () => {
const config = loadConfig({ TERA_REGIONS: "sf:37.7749,-122.4194; sf:0,0" });
assert.equal(config.regions.length, 1);
assert.ok(config.degraded.some((line) => line.includes("twice")));
});
it("never ends up with an empty set, whatever it was handed", () => {
for (const spec of ["", " ", ";;;", "sf:", "@:1,2", "x".repeat(200)]) {
const regions = loadRegions({
spec,
origin: { lat: 37.7749, lng: -122.4194 },
originConfigured: false,
degraded: [],
});
assert.ok(regions.length > 0, `"${spec}" produced no regions`);
}
});
});
describe("resolving a request to a region", () => {
const regions = shipped();
it("answers for the default when asked for nothing", () => {
const resolved = resolveRegion(regions, {});
assert.ok(resolved.ok && resolved.region.id === "sf");
});
it("answers for a city by id", () => {
const resolved = resolveRegion(regions, { city: "socal" });
assert.ok(resolved.ok && resolved.region.id === "socal");
});
it("snaps a nearby coordinate to the region that claims it", () => {
// Berkeley, Pasadena: neither is a configured point, and both resolve to the
// configured point that will actually be fetched.
const berkeley = resolveRegion(regions, { lat: "37.8715", lng: "-122.2730" });
assert.ok(berkeley.ok && berkeley.region.id === "sf");
const pasadena = resolveRegion(regions, { lat: "34.1478", lng: "-118.1445" });
assert.ok(pasadena.ok && pasadena.region.id === "socal");
});
it("refuses a coordinate this deployment has nothing to say about", () => {
for (const point of [
{ lat: "36.7378", lng: "-119.7871" }, // Fresno, between the two boards.
{ lat: "40.7128", lng: "-74.0060" }, // New York.
{ lat: "0", lng: "0" }, // Null Island, the classic probe.
]) {
const resolved = resolveRegion(regions, point);
assert.ok(!resolved.ok, `${point.lat},${point.lng} must be refused`);
assert.match(resolved.message, /serves: sf, socal/);
}
});
it("refuses a coordinate that is not a coordinate", () => {
for (const lat of [
"banana",
"NaN",
"Infinity",
"1e5",
"0x2f",
"37.7749deg",
"91",
"37.77490001",
"",
" ",
]) {
const resolved = resolveRegion(regions, { lat, lng: "-122.4194" });
assert.ok(!resolved.ok, `lat=${lat} must be refused`);
}
assert.ok(!resolveRegion(regions, { lat: "37.7", lng: "181" }).ok);
});
it("refuses a repeated parameter rather than picking one", () => {
// `?lat=1&lat=2` arrives as an array, and quietly taking either half is how
// a parser disagreement becomes a security bug somewhere downstream.
assert.ok(!resolveRegion(regions, { lat: ["37.7", "0"], lng: "-122.4" }).ok);
assert.ok(!resolveRegion(regions, { city: ["sf", "socal"] }).ok);
});
it("refuses half a coordinate", () => {
assert.ok(!resolveRegion(regions, { lat: "37.7749" }).ok);
assert.ok(!resolveRegion(regions, { lng: "-122.4194" }).ok);
});
it("refuses a request that asks two ways at once", () => {
const resolved = resolveRegion(regions, { city: "sf", lat: "37.7", lng: "-122.4" });
assert.ok(!resolved.ok);
assert.match(resolved.message, /not both/);
});
it("refuses an unknown city without pretending it might exist elsewhere", () => {
const resolved = resolveRegion(regions, { city: "atlantis" });
assert.ok(!resolved.ok);
assert.match(resolved.message, /sf, socal/);
});
});
describe("the routes that take a region", () => {
for (const route of ["weather", "flights"]) {
it(`serves ${route} for either city, from that city's own point`, async () => {
const app = appWith({});
after(() => app.close());
const sf = await app.inject({ method: "GET", url: `/api/v1/${route}?city=sf` });
const socal = await app.inject({ method: "GET", url: `/api/v1/${route}?city=socal` });
assert.equal(sf.statusCode, 200);
assert.equal(socal.statusCode, 200);
assert.notDeepEqual(sf.json(), socal.json());
});
it(`refuses a nonsense ${route} query with a 400 and no cached copy of it`, async () => {
const app = appWith({});
after(() => app.close());
const res = await app.inject({ method: "GET", url: `/api/v1/${route}?lat=banana&lng=0` });
assert.equal(res.statusCode, 400);
assert.equal(res.json<ErrorBody>().error, "bad_request");
// The fail-closed default still applies: nothing shared may keep a refusal.
assert.equal(res.headers["cache-control"], "private, no-store");
});
it(`refuses a ${route} request for somewhere this box does not serve`, async () => {
const app = appWith({});
after(() => app.close());
const res = await app.inject({
method: "GET",
url: `/api/v1/${route}?lat=51.5072&lng=-0.1276`,
});
assert.equal(res.statusCode, 400);
assert.match(res.json<ErrorBody>().message, /sf, socal/);
});
}
it("puts the weather where it was asked for, not where the box is", async () => {
const app = appWith({});
after(() => app.close());
const res = await app.inject({ method: "GET", url: "/api/v1/weather?city=socal" });
const body = res.json<WeatherBody>();
assert.deepEqual(body.location, { lat: 33.82, lng: -118.05 });
// Still the synthetic clear day, because a zero-config box has no source.
assert.equal(body.synthetic, true);
});
it("flies the Southland's own airports over the Southland", async () => {
const app = appWith({});
after(() => app.close());
const body = (
await app.inject({ method: "GET", url: "/api/v1/flights?city=socal" })
).json<FlightsBody>();
assert.ok(body.mode === "plan");
// LAX's published reference point, as a departure or an arrival. The Bay
// Area plan cannot produce it, which is the whole point of the assertion.
const lax = body.routes.some(
(leg) =>
(leg.from[0] === 33.9425 && leg.from[1] === -118.4081) ||
(leg.to[0] === 33.9425 && leg.to[1] === -118.4081),
);
assert.ok(lax, "the SoCal plan should fly out of LAX");
});
it("lays out spokes for a city it has never heard of", async () => {
const app = appWith({ TERA_REGIONS: "pdx:45.5152,-122.6784" });
after(() => app.close());
const body = (await app.inject({ method: "GET", url: "/api/v1/flights" })).json<FlightsBody>();
assert.ok(body.mode === "plan" && body.routes.length === 8);
});
it("publishes the allowlist on health so a client can stop guessing", async () => {
const app = appWith({});
after(() => app.close());
const body = (await app.inject({ method: "GET", url: "/api/v1/health" })).json<{
regions: { id: string }[];
}>();
assert.deepEqual(
body.regions.map((region) => region.id),
["sf", "socal"],
);
});
});
+294
View File
@@ -0,0 +1,294 @@
/**
* Weather, once a source is actually turned on.
*
* `api.weather.gov` is stood up as a stub here rather than called, for the
* obvious reason and for a less obvious one: the assertions that matter are
* about **which URL this server constructs**, and a real upstream would answer
* the right way for the wrong reason. The load-bearing one is that a caller
* asking for Berkeley causes a fetch of San Francisco's point and never of
* Berkeley's — the difference between a map and an open proxy pointed at a
* public-good API with this deployment's contact address on it.
*
* Global `fetch` is replaced for the file. `node --test` runs each test file in
* its own process, so nothing here leaks into another one, and `after` puts the
* real one back anyway.
*/
import assert from "node:assert/strict";
import { after, beforeEach, describe, it } from "node:test";
import { buildApp } from "../app.ts";
import { loadConfig } from "../config.ts";
import type { WeatherBody } from "../../../src/server/wire.ts";
const CONTACT = "ops@example.com";
const nwsEnv = { TERA_WEATHER_SOURCE: "nws", TERA_WEATHER_CONTACT: CONTACT };
interface Call {
url: string;
userAgent: string;
}
const realFetch = globalThis.fetch;
let calls: Call[] = [];
/** Flipped by a test that wants to watch the upstream go away mid-flight. */
let upstreamIsUp = true;
/**
* Flipped by a test that wants the upstream to stay *up* and answer 200 with
* something the parser cannot walk. `cloudLayers` becomes a number, which is
* the shape that used to throw straight through the cache.
*/
let upstreamIsGarbled = false;
/**
* The three hops NWS makes you take, and nothing else: an unrecognised URL is a
* 404, so a request this server should not be making shows up as a failure
* rather than as a plausible answer.
*/
function nws(url: string): unknown | undefined {
if (!upstreamIsUp) return undefined;
const point = /\/points\/(-?[\d.]+),(-?[\d.]+)$/.exec(url);
if (point !== null) {
return { properties: { observationStations: `https://api.weather.gov/zones/${point[1]}` } };
}
const zone = /\/zones\/(-?[\d.]+)$/.exec(url);
if (zone !== null) {
// One station id per latitude, so a body can be traced back to the point
// that was asked about.
return { features: [{ properties: { stationIdentifier: `K${zone[1]}` } }] };
}
const station = /\/stations\/K(-?[\d.]+)\/observations\/latest$/.exec(url);
if (station !== null) {
return {
properties: {
timestamp: "2026-08-05T09:00:00+00:00",
temperature: { value: Number(station[1]), unitCode: "wmoUnit:degC" },
windSpeed: { value: 9, unitCode: "wmoUnit:km_h-1" },
cloudLayers: upstreamIsGarbled ? 7 : [{ amount: "BKN" }],
presentWeather: [],
visibility: { value: 16_000, unitCode: "wmoUnit:m" },
},
};
}
return undefined;
}
globalThis.fetch = (async (input: unknown, init?: { headers?: Record<string, string> }) => {
const url = String(input);
// `http.ts` always passes a plain object, so this needs no `Headers` dance.
calls.push({ url, userAgent: init?.headers?.["user-agent"] ?? "" });
const body = nws(url);
if (body === undefined) return new Response("nope", { status: 503 });
return new Response(JSON.stringify(body), {
status: 200,
headers: { "content-type": "application/json" },
});
}) as unknown as typeof globalThis.fetch;
after(() => {
globalThis.fetch = realFetch;
});
beforeEach(() => {
calls = [];
upstreamIsUp = true;
upstreamIsGarbled = false;
});
function appWith(env: Record<string, string>) {
const config = loadConfig(env);
config.logLevel = "silent";
return buildApp(config);
}
function observations(): string[] {
return calls.filter((call) => call.url.includes("/observations/")).map((call) => call.url);
}
describe("a configured weather source", () => {
it("fetches the region centre and never the coordinate the caller sent", async () => {
const app = appWith(nwsEnv);
after(() => app.close());
// Berkeley. Inside the Bay Area region, and not a point this deployment
// serves — so it selects San Francisco and San Francisco is what gets asked.
const res = await app.inject({
method: "GET",
url: "/api/v1/weather?lat=37.8715&lng=-122.2730",
});
assert.equal(res.statusCode, 200);
assert.ok(
calls.every((call) => !call.url.includes("37.8715")),
`the caller's coordinate reached the upstream: ${calls.map((c) => c.url).join(" ")}`,
);
assert.ok(calls.some((call) => call.url.endsWith("/points/37.7749,-122.4194")));
assert.deepEqual(res.json<WeatherBody>().location, { lat: 37.7749, lng: -122.4194 });
});
it("holds one observation per city rather than one per box", async () => {
const app = appWith(nwsEnv);
after(() => app.close());
const sf = (
await app.inject({ method: "GET", url: "/api/v1/weather?city=sf" })
).json<WeatherBody>();
const socal = (
await app.inject({ method: "GET", url: "/api/v1/weather?city=socal" })
).json<WeatherBody>();
// The stub encodes the latitude in the temperature, so two different
// numbers here means two different stations were actually consulted.
assert.equal(sf.temperatureC, 37.7749);
assert.equal(socal.temperatureC, 33.82);
assert.equal(observations().length, 2);
assert.equal(sf.source, "nws");
assert.equal(sf.synthetic, false);
});
it("asks once per region per TTL however many callers turn up", async () => {
const app = appWith(nwsEnv);
after(() => app.close());
const ask = () => app.inject({ method: "GET", url: "/api/v1/weather?city=sf" });
// Concurrent, then sequential: the first is the single-flight collapse, the
// second is the TTL. Both used to be one fetch each and only one of them was.
await Promise.all([ask(), ask(), ask(), ask()]);
await ask();
await ask();
assert.equal(observations().length, 1);
});
it("keeps the cities apart under load, not merely on the first request", async () => {
const app = appWith(nwsEnv);
after(() => app.close());
const bodies = await Promise.all(
["sf", "socal", "sf", "socal", "sf"].map(async (city) =>
(await app.inject({ method: "GET", url: `/api/v1/weather?city=${city}` })).json<WeatherBody>(),
),
);
assert.deepEqual(
bodies.map((body) => body.temperatureC),
[37.7749, 33.82, 37.7749, 33.82, 37.7749],
);
assert.equal(observations().length, 2);
});
/**
* A source that is *up* and answering in a shape this build cannot read is a
* different failure from one that is down, and it used to be a much worse
* one: the parser threw, `upstream.ts` never reached its clock stamp, and the
* TTL — the only thing standing between a public-good API and one outbound
* request per inbound request — stopped existing. `current()` also stopped
* being the thing its own header calls it, which is a function that never
* throws.
*/
it("treats a body it cannot parse as a source that did not answer", async () => {
upstreamIsGarbled = true;
const app = appWith(nwsEnv);
after(() => app.close());
const res = await app.inject({ method: "GET", url: "/api/v1/weather?city=sf" });
assert.equal(res.statusCode, 200);
// The clear day, which is what "nobody answered" has always meant here.
assert.equal(res.json<WeatherBody>().synthetic, true);
});
it("keeps the TTL when the body is garbled, not just when the socket dies", async () => {
upstreamIsGarbled = true;
const app = appWith(nwsEnv);
after(() => app.close());
for (let i = 0; i < 5; i++) {
await app.inject({ method: "GET", url: "/api/v1/weather?city=sf" });
}
assert.equal(observations().length, 1);
});
it("identifies the operator to a source that requires it", async () => {
const app = appWith(nwsEnv);
after(() => app.close());
await app.inject({ method: "GET", url: "/api/v1/weather" });
assert.ok(calls.length > 0);
for (const call of calls) assert.match(call.userAgent, new RegExp(CONTACT));
});
it("serves the last good observation when the upstream goes away", async () => {
// TTL 0 makes every request a refetch, which is what makes the failure
// reachable in a test without waiting ten minutes for one.
const app = appWith({ ...nwsEnv, TERA_WEATHER_TTL: "0" });
after(() => app.close());
const first = (
await app.inject({ method: "GET", url: "/api/v1/weather?city=sf" })
).json<WeatherBody>();
assert.equal(first.temperatureC, 37.7749);
upstreamIsUp = false;
const second = await app.inject({ method: "GET", url: "/api/v1/weather?city=sf" });
assert.equal(second.statusCode, 200);
const body = second.json<WeatherBody>();
// Not a 503, not a clear day: the observation from a minute ago, which is
// the only answer that keeps the sky looking like the sky.
assert.equal(body.temperatureC, 37.7749);
assert.equal(body.synthetic, false);
});
it("falls back to a clear day for a city that has never answered", async () => {
upstreamIsUp = false;
const app = appWith(nwsEnv);
after(() => app.close());
const res = await app.inject({ method: "GET", url: "/api/v1/weather?city=socal" });
assert.equal(res.statusCode, 200);
const body = res.json<WeatherBody>();
assert.equal(body.synthetic, true);
assert.equal(body.condition, "clear");
assert.deepEqual(body.location, { lat: 33.82, lng: -118.05 });
});
it("retries a dead source on the TTL, not on every request", async () => {
upstreamIsUp = false;
const app = appWith(nwsEnv);
after(() => app.close());
for (let i = 0; i < 5; i++) await app.inject({ method: "GET", url: "/api/v1/weather" });
// One attempt, one failure, one clock stamp. Somebody else's outage must not
// turn into this box's outbound flood.
assert.equal(calls.length, 1);
});
it("makes no outbound request at all until somebody asks", async () => {
const app = appWith(nwsEnv);
after(() => app.close());
await app.inject({ method: "GET", url: "/api/v1/health" });
assert.deepEqual(calls, []);
});
it("never calls anybody when the source is off", async () => {
const app = appWith({});
after(() => app.close());
await app.inject({ method: "GET", url: "/api/v1/weather?city=sf" });
await app.inject({ method: "GET", url: "/api/v1/weather?city=socal" });
assert.deepEqual(calls, []);
});
it("refuses the request before it would have fetched anything", async () => {
const app = appWith(nwsEnv);
after(() => app.close());
const res = await app.inject({
method: "GET",
url: "/api/v1/weather?lat=51.5072&lng=-0.1276",
});
assert.equal(res.statusCode, 400);
// The point of the allowlist: a refused request costs the upstream nothing.
assert.deepEqual(calls, []);
});
});