Files
gods-eye-view/server/providers/inaturalist/queue.test.mjs
T
Yehe LiuandClaude Opus 5.5 14f88b3e08 feat: Wildlife Sightings layer from iNaturalist and Acartia
Recent community wildlife observations, as a new Wildlife group in the
layer panel.

- Server: keyless iNaturalist proxy (v2 observations with only the
  projected fields; undocumented v1 UTFGrid density tiles, decoded on the
  server) sharing one paced queue (burst 4, ~1/s, 60/min, 8,000/day budget,
  up to four in flight), in-memory caches and upstream-only cooldowns;
  Acartia Salish Sea marine-mammal feed rounded to 0.01 degrees with
  comments dropped.
- Layer: grid counts drawn as a soft view-ranked density glow; real sampled
  sightings as taxon badges at their true positions with ambient photo
  thumbnails and labels; obscured records stay privacy cells; a clicked
  sighting stays pinned through camera moves and refreshes.
- Anchored photo card per selection with the single iNaturalist link;
  attribution in the layer row and the Data attribution popover.
- Filters (CONFIRMED, taxa presets) round-trip in share links with token 9.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-11-05 16:17:12 -05:00

112 lines
3.6 KiB
JavaScript

import test from 'node:test';
import assert from 'node:assert/strict';
import { createInatQueue } from './queue.js';
async function until(check) {
for (let i = 0; i < 100; i += 1) {
if (check()) return;
await new Promise(setImmediate);
}
assert.fail('queue did not reach its expected state');
}
test('shared governor paces a four-call burst within the rolling minute ceiling', async () => {
let at = 0;
const waits = [];
const queue = createInatQueue({
now: () => at,
wait: async (ms) => {
waits.push(ms);
at += ms;
},
});
const dispatched = [];
await Promise.all(
Array.from({ length: 65 }, () => queue(() => dispatched.push(at))),
);
assert.equal(dispatched.length, 65);
assert.deepEqual(dispatched.slice(0, 7), [0, 0, 0, 0, 1000, 2000, 3000]);
assert.deepEqual(waits.slice(0, 3), [1000, 1000, 1000]);
for (const time of dispatched)
assert.ok(
dispatched.filter((other) => other > time - 60_000 && other <= time)
.length <= 60,
);
assert.equal(dispatched[60], 60_000);
});
test('an observation arriving during the pacing wait overtakes queued tiles', async () => {
let at = 0;
const waits = [];
const queue = createInatQueue({
burst: 1,
now: () => at,
wait: (ms) => new Promise((resolve) => waits.push({ ms, resolve })),
});
const dispatched = [];
await queue(() => dispatched.push('first tile'));
const tile = queue(() => dispatched.push('waiting tile'));
await until(() => waits.length === 1);
const observation = queue(() => dispatched.push('observation'), {
priority: 0,
});
at += waits[0].ms;
waits[0].resolve();
await observation;
assert.deepEqual(dispatched, ['first tile', 'observation']);
await until(() => waits.length === 2);
at += waits[1].ms;
waits[1].resolve();
await tile;
assert.deepEqual(dispatched, ['first tile', 'observation', 'waiting tile']);
});
test('superseded tasks leave before dispatch, and a rejection cannot poison the tail', async () => {
const queue = createInatQueue({ intervalMs: 0 });
const dispatched = [];
let release;
const first = queue(
() =>
new Promise((resolve) => {
release = resolve;
}),
);
await until(() => release);
const controller = new AbortController();
const superseded = queue(() => assert.fail('superseded tile dispatched'), {
signal: controller.signal,
});
const dropped = assert.rejects(superseded, { name: 'AbortError' });
controller.abort();
await dropped;
const failed = queue(() => {
dispatched.push('failed');
throw new Error('synthetic');
});
const failure = assert.rejects(failed, /synthetic/);
const succeeding = queue(() => dispatched.push('recovered'));
release();
await Promise.all([first, failure, succeeding]);
assert.deepEqual(dispatched, ['failed', 'recovered']);
});
test('a slow call does not hold later calls; at most four run at once', async () => {
const queue = createInatQueue({ intervalMs: 0, windowMs: 0 });
const releases = [];
const started = [];
const slow = (id) => () =>
new Promise((resolve) => {
started.push(id);
releases.push(() => resolve(id));
});
const jobs = [1, 2, 3, 4, 5].map((id) => queue(slow(id)));
await until(() => started.length === 4);
assert.deepEqual(started, [1, 2, 3, 4], 'four start without waiting');
releases[1]();
assert.equal(await jobs[1], 2, 'a later call finishes before the first');
await until(() => started.length === 5);
assert.deepEqual(started, [1, 2, 3, 4, 5], 'a freed slot starts the next');
for (const release of releases) release();
assert.deepEqual(await Promise.all(jobs), [1, 2, 3, 4, 5]);
});