Files
worldmonitor/tests/seed-sovereign-wealth.test.mjs
Elie Habib e9146516a5 fix(swf): restore 8/8 fund coverage + explicit per-country observability (#3352)
* fix(swf): restore 8/8 fund coverage — WB bulk mrv=1 silently dropped Gulf countries

The 2026-04-23 post-#3344 Railway run seeded 4/8 funds (NO, SA, SG) and
silently dropped AE/KW/QA. Root cause: WB's `country/all/indicator/…?mrv=1`
returns the SAME year across every country (the most recent year that any
country publishes). KW/QA/AE report NE.IMP.GNFS.CD a year or two behind
NO/SA/SG, so mrv=1 gave them `value: null` and the seeder skipped them
because the rawMonths denominator was missing.

Fix: bump to `mrv=5` and pick the most recent non-null value per country
via a new pure helper `pickLatestPerCountry(records)`. Verified via
6 back-to-back live dry-runs (all 8/8, byte-identical numbers):
  NO: GPFG          1/1  effMo=93.05   (2024 imports)
  AE: ADIA+Mubadala 2/2  effMo=3.85    (2023 imports)
  SA: PIF           1/1  effMo=1.68    (2024 imports)
  KW: KIA           1/1  effMo=45.43   (2023 imports)
  QA: QIA           1/1  effMo=8.61    (2022 imports)
  SG: GIC+Temasek   2/2  effMo=7.11    (2024 imports; Temasek via infobox)

Second fix (observability): every manifest country is now enumerated in
a `summary` block in the payload + logged with an explicit status and
reason. Prod 14:59Z run had logs for KW/QA ("missing WB imports") but AE
was dropped with no log line — the operator has to cross-reference the
manifest to notice. New `buildCoverageSummary(manifest, imports, countries)`
is exported and always emits one row per manifest country: `complete`,
`partial`, or `missing` with `reason ∈ {'missing WB imports', 'no fund
AUM matched'}`. Summary is also embedded in the published payload so
downstream consumers can detect degraded runs without parsing logs.

Tests (48/48 pass, 9 new):
- `pickLatestPerCountry` — 7 cases including the exact prod scenario
  (AE-2024-null + AE-2023-non-null → resolves to 2023 row). Guards
  against upstream re-order (asserts latest-year wins regardless of
  array order), rejects null-only countries, rejects non-positive
  values, handles both iso3 and iso2 codes.
- `buildCoverageSummary` — 2 cases covering the regression
  (silent-drop of AE) and the reason-string disambiguation (operator
  should know whether to investigate WB or Wikipedia).

Validated: 6 live end-to-end dry-runs (all 8/8), full test suite
569/569 pass, biome + lint:md clean.

* fix(swf): address Greptile P2 — uniform reason field + meaningful null-filter test

Two P2 findings on PR #3352:

1. `complete` and `partial` entries in countryStatuses were pushed
   without a `reason` key, while `missing` always carried one. The log
   path tolerated this (`row.reason ? ... : ''`), but the summary is
   now persisted in Redis — any downstream consumer iterating
   countryStatuses and reading `.reason` on a `partial` would see
   undefined. Added `reason: null` to complete + partial for uniform
   persisted shape. Test now asserts the `reason` key is present on
   every row regardless of status.

2. The null-only pickLatestPerCountry test used `'XYZ'` as the ISO-3
   code, which is filtered at the iso3→iso2 lookup stage BEFORE ever
   reaching the null-value guard — a regression that removed null
   filtering entirely would leave the test green. Swapped to `'NOR'`
   (real ISO-3 with a valid iso2 mapping) so the null-filter is the
   actual gate under test. Verified via sanity probe: `NOR + null`
   still drops, `NOR + value` still lands.

Tests 48/48 pass; live dry-run still 8/8 byte-identical; biome clean.
2026-04-23 21:35:25 +04:00

29 KiB