mirror of
https://github.com/bilawalsidhu/gods-eye-view.git
synced 2026-11-05 02:44:02 +00:00
feat(fires): MODIS in Active Fires and a Fire Perimeters layer (#737)
Adds MODIS NRT to Active Fires and a keyless Fire Perimeters layer (NIFC WFIGS) with InciWeb incident links, served through a capped, cached /api/fire-perimeters proxy. Co-authored-by: Gustavo Beneduzi <[email protected]> Co-authored-by: James Cooke <[email protected]>
This commit is contained in:
40 files changed
+3201
-26
No files matched your search
@@ -0,0 +1,117 @@
|
||||
import test from 'node:test';
|
||||
import assert from 'node:assert/strict';
|
||||
import { createWfigsPerimeterSource } from './source.js';
|
||||
import { normalizeFirePerimeterSnapshot } from './records.js';
|
||||
import { firePerimetersProxy } from '../../../server/providers/firePerimeters.js';
|
||||
|
||||
const ring = [
|
||||
[-108.1, 35.2],
|
||||
[-108.0, 35.2],
|
||||
[-108.0, 35.3],
|
||||
[-108.1, 35.2],
|
||||
];
|
||||
const validPayload = {
|
||||
features: [
|
||||
{
|
||||
id: 1,
|
||||
geometry: { type: 'Polygon', coordinates: [ring] },
|
||||
properties: { attr_UniqueFireIdentifier: '2026-NMGNF-000123' },
|
||||
},
|
||||
],
|
||||
};
|
||||
|
||||
test('a successful response yields normalized perimeter rows', async () => {
|
||||
let requested;
|
||||
const source = createWfigsPerimeterSource({
|
||||
fetchImpl: async (url) => {
|
||||
requested = String(url);
|
||||
return Response.json({
|
||||
rows: normalizeFirePerimeterSnapshot(validPayload),
|
||||
});
|
||||
},
|
||||
});
|
||||
const rows = await source.getSnapshot();
|
||||
assert.equal(rows.length, 1);
|
||||
assert.equal(rows[0].stableId, '2026-NMGNF-000123');
|
||||
assert.equal(requested, '/api/fire-perimeters');
|
||||
});
|
||||
|
||||
test('a truncated response pages until the feed is complete', async () => {
|
||||
const pageRing = (id) => ({
|
||||
id,
|
||||
geometry: { type: 'Polygon', coordinates: [ring] },
|
||||
properties: { attr_UniqueFireIdentifier: `fire-${id}` },
|
||||
});
|
||||
const requests = [];
|
||||
let handler;
|
||||
const plugin = firePerimetersProxy({
|
||||
fetchImpl: async (url) => {
|
||||
requests.push(new URL(String(url)).searchParams.get('resultOffset'));
|
||||
const page = requests.length;
|
||||
return Response.json({
|
||||
features: [pageRing(page)],
|
||||
properties: { exceededTransferLimit: page < 3 },
|
||||
});
|
||||
},
|
||||
});
|
||||
plugin.configureServer({
|
||||
middlewares: {
|
||||
use: (_path, callback) => {
|
||||
handler = callback;
|
||||
},
|
||||
},
|
||||
});
|
||||
let payload;
|
||||
await handler(
|
||||
{ url: '/', method: 'GET' },
|
||||
{
|
||||
writeHead(status) {
|
||||
assert.equal(status, 200);
|
||||
},
|
||||
end(body) {
|
||||
payload = JSON.parse(body);
|
||||
},
|
||||
},
|
||||
);
|
||||
const rows = payload.rows;
|
||||
assert.deepEqual(
|
||||
rows.map((row) => row.stableId),
|
||||
['fire-1', 'fire-2', 'fire-3'],
|
||||
);
|
||||
assert.deepEqual(requests, [null, '1', '2']);
|
||||
});
|
||||
|
||||
test('an upstream failure surfaces its HTTP status', async () => {
|
||||
const source = createWfigsPerimeterSource({
|
||||
fetchImpl: async () => ({ ok: false, status: 503 }),
|
||||
});
|
||||
await assert.rejects(source.getSnapshot(), /WFIGS HTTP 503/);
|
||||
});
|
||||
|
||||
test('a malformed successful response is never accepted as an empty snapshot', async () => {
|
||||
for (const payload of [{}, { rows: null }, { rows: {} }]) {
|
||||
const source = createWfigsPerimeterSource({
|
||||
fetchImpl: async () => Response.json(payload),
|
||||
});
|
||||
await assert.rejects(source.getSnapshot(), /Malformed perimeter snapshot/);
|
||||
}
|
||||
});
|
||||
|
||||
test('response-body completion honors cancellation without replacing records', async () => {
|
||||
const abort = new AbortController();
|
||||
const source = createWfigsPerimeterSource({
|
||||
fetchImpl: async () => ({
|
||||
ok: true,
|
||||
headers: new Headers(),
|
||||
text: async () => {
|
||||
abort.abort();
|
||||
return JSON.stringify({
|
||||
rows: normalizeFirePerimeterSnapshot(validPayload),
|
||||
});
|
||||
},
|
||||
}),
|
||||
});
|
||||
await assert.rejects(source.getSnapshot({ signal: abort.signal }), {
|
||||
name: 'AbortError',
|
||||
});
|
||||
});
|
||||
Reference in new issue
Block a user