fix(firms): stop requesting the retired Suomi NPP source

NASA stopped delivering Suomi NPP data on 2026-11-02 (FIRMS news page).
With a live MAP_KEY on 2026-11-04, VIIRS_SNPP_NRT came back ok with zero
rows while NOAA-20, NOAA-21 and MODIS returned 57k, 49k and 11k, so the
source only spent one transaction per refresh. The proxy now requests
NOAA-20, NOAA-21 and MODIS NRT. The "N" satellite label stays, so cached
or archived Suomi NPP rows still read SNPP.

Tests count sources from the list instead of assuming four, and a
regression test keeps the retired source out of the production list.

Refs #849

Co-Authored-By: Claude Opus 5.5 <[email protected]>
This commit is contained in:
Itallo BrandãoandClaude Opus 5.5 committed 2026-11-04 15:15:34 -03:00
1 parent ac28a11e41
commit 84c1fef8d3
5 files changed
+31 -17

No files matched your search

+14 -4
View File
@@ -21,7 +21,7 @@ assert.notEqual(start, -1, 'FIRMS refresh function exists');
const end = config.indexOf('\n }', start);
assert.notEqual(end, -1, 'FIRMS refresh function closes');
const refreshSource = config.slice(start, end + 4);
const SOURCES = ['VIIRS_NOAA20_NRT', 'VIIRS_NOAA21_NRT', 'VIIRS_SNPP_NRT', 'MODIS_NRT'];
const SOURCES = ['VIIRS_NOAA20_NRT', 'VIIRS_NOAA21_NRT', 'MODIS_NRT'];
const NOW = Date.UTC(2026, 8, 11, 12);
const recent = { acqDate: '2026-09-11', acqTime: '1100' };
@@ -63,7 +63,7 @@ test('FIRMS keeps successful sources when another upstream fails', async () => {
if (source === SOURCES[1]) throw new Error('upstream unavailable');
return [recent];
})('fixture');
assert.equal(result.fires.length, 3);
assert.equal(result.fires.length, SOURCES.length - 1);
assert.deepEqual(result.sources, SOURCES.map((source, index) => ({
source, count: index === 1 ? 0 : 1, ok: index !== 1,
})));
@@ -77,7 +77,7 @@ test('FIRMS reports one failure if consuming a source throws before append', asy
// Fault injection for aggregation; ordinary parsed CSV returns an array.
return { length: 1, [Symbol.iterator]() { throw new Error('aggregation failed'); } };
})('fixture');
assert.equal(result.fires.length, 3);
assert.equal(result.fires.length, SOURCES.length - 1);
assert.deepEqual(result.sources, SOURCES.map((source, index) => ({
source, count: index === 0 ? 0 : 1, ok: index !== 0,
})));
@@ -107,7 +107,7 @@ function wrappedAddressSelectionFailure() {
{ name: 'ConnectTimeoutError', code: 'UND_ERR_CONNECT_TIMEOUT', cause: inner });
return new TypeError('fetch failed', { cause });
}
const URL_WITH_KEY = 'https://firms.modaps.eosdis.nasa.gov/api/area/csv/SECRET/VIIRS_SNPP_NRT/world/2';
const URL_WITH_KEY = 'https://firms.modaps.eosdis.nasa.gov/api/area/csv/SECRET/VIIRS_NOAA20_NRT/world/2';
test('FIRMS failures log the socket cause, never the keyed URL', () => {
const described = describeFetchError(addressSelectionFailure());
@@ -307,3 +307,13 @@ test('every FIRMS upstream request goes through the IPv4 fallback', async () =>
assert.ok(calls[0][2] instanceof AbortSignal);
assert.deepEqual(calls[1], ['read', response, calls[0][2]], 'the body read shares the request deadline');
});
test('FIRMS requests only the satellites NASA still delivers', () => {
const listed = config.match(/const SOURCES = \[([^\]]*)\];/);
assert.ok(listed, 'FIRMS source list exists');
const production = [...listed[1].matchAll(/'([A-Z0-9_]+)'/g)].map((m) => m[1]);
assert.deepEqual(production, SOURCES, 'the test mirrors the production source list');
// NASA stopped delivering Suomi NPP data on 2026-11-02 (#849): requesting it
// returns no rows and still spends a MAP_KEY transaction on every refresh.
assert.ok(!production.includes('VIIRS_SNPP_NRT'));
});
+2 -2
View File
@@ -313,9 +313,9 @@ test('FIRMS retains a large successful source during partial failure and filters
const first = json(await request());
assert.equal(first.count, 130001);
assert.equal(first.sources.filter((s) => s.ok).length, 1);
assert.equal(calls, 4);
assert.equal(calls, first.sources.length);
assert.equal(json(await request()).count, 130001);
assert.equal(calls, 4);
assert.equal(calls, first.sources.length);
assert.deepEqual(json(await request('/status')).transactions, {
used: 3,
limit: 5000,