mirror of
https://github.com/koala73/worldmonitor.git
synced 2026-04-25 17:14:57 +02:00
* fix(economy): GSCPI shape mismatch with ais-relay payload
`seed-economy.mjs` was reporting `[StressIndex] GSCPI not in Redis yet
(ais-relay lag or first run) — excluding` even when GSCPI was current
in Redis. The Stress Index then computed on 5/6 components instead
of 6/6 every run.
Root cause: shape mismatch.
- ais-relay.cjs (`seedGscpi()`) writes the FRED-compatible payload
`{ series: { series_id, title, units, frequency, observations: [...] } }`
- seed-economy.mjs `fetchGscpiFromRedis()` was reading the legacy
flat shape `{ observations: [...] }` (top-level), so
`Array.isArray(parsed.observations)` was always false → null returned
→ "not in Redis yet" log, even though 343 monthly observations were
sitting in `economic:fred:v1:GSCPI:0`
Fix: extract the parsing into `extractGscpiObservations()` which checks
both shapes (`parsed.series.observations` first, then top-level
`parsed.observations` for back-compat). The "not in Redis yet" message
will now correctly fire only when the relay is genuinely behind.
Verified against live Redis: returns `{ observations: 343 entries,
latest 2026-03-01 = 0.68 }` instead of null.
Tests added in tests/gscpi-shape-extraction.test.mjs (3 cases:
ais-relay shape, legacy flat shape, malformed payload).
* style(economy): single @type cast in extractGscpiObservations
PR #3072 review (P2): cast `parsed` to `any` once into a local instead
of repeating the inline `/** @type {any} */` annotation on every access.
Same behavior, less visual noise.
48 lines
1.8 KiB
JavaScript
48 lines
1.8 KiB
JavaScript
import { describe, it } from 'node:test';
|
|
import assert from 'node:assert/strict';
|
|
|
|
import { extractGscpiObservations } from '../scripts/seed-economy.mjs';
|
|
|
|
describe('extractGscpiObservations', () => {
|
|
it('reads the ais-relay FRED-compatible shape (observations under .series)', () => {
|
|
// This is the actual shape ais-relay.cjs writes — see seedGscpi() in that file.
|
|
const parsed = {
|
|
series: {
|
|
series_id: 'GSCPI',
|
|
title: 'Global Supply Chain Pressure Index',
|
|
units: 'Standard Deviations',
|
|
frequency: 'Monthly',
|
|
observations: [
|
|
{ date: '2026-02-01', value: 0.42 },
|
|
{ date: '2026-03-01', value: 0.68 },
|
|
],
|
|
},
|
|
};
|
|
const result = extractGscpiObservations(parsed);
|
|
assert.ok(result, 'should return non-null');
|
|
assert.equal(result.observations.length, 2);
|
|
assert.equal(result.observations[1].value, 0.68);
|
|
});
|
|
|
|
it('reads the legacy flat shape (top-level observations) for back-compat', () => {
|
|
// Earlier ais-relay versions stored this shape — keep working if any
|
|
// long-lived Redis key still has it.
|
|
const parsed = {
|
|
observations: [
|
|
{ date: '2026-03-01', value: 0.68 },
|
|
],
|
|
};
|
|
const result = extractGscpiObservations(parsed);
|
|
assert.ok(result, 'should return non-null');
|
|
assert.equal(result.observations.length, 1);
|
|
});
|
|
|
|
it('returns null when neither shape is present', () => {
|
|
assert.equal(extractGscpiObservations(null), null);
|
|
assert.equal(extractGscpiObservations({}), null);
|
|
assert.equal(extractGscpiObservations({ series: {} }), null);
|
|
assert.equal(extractGscpiObservations({ observations: 'not-an-array' }), null);
|
|
assert.equal(extractGscpiObservations({ series: { observations: 'nope' } }), null);
|
|
});
|
|
});
|