GenerAI / worldmonitor /tests /acled-resolution-feed-seed.test.mjs
amogaddy's picture
Fix build: ripristinate blog-site/tests/e2e/pro-test/convex (referenziate dagli script di build) (part 3)
ec8acdf verified
Raw
History Blame Contribute Delete
5.62 kB
import { strict as assert } from 'node:assert';
import { describe, it } from 'node:test';
import { readFileSync } from 'node:fs';
import { dirname, resolve } from 'node:path';
import { fileURLToPath } from 'node:url';
const here = dirname(fileURLToPath(import.meta.url));
const root = resolve(here, '..');
function source(path) {
return readFileSync(resolve(root, path), 'utf8');
}
function assertObjectProperty(sourceText, objectName, propertyPattern) {
assert.match(sourceText, new RegExp(`${objectName}:\\s*\\{[^}]*${propertyPattern}`, 's'));
}
describe('ACLED resolution-feed seed contract (#5076)', () => {
const conflictSeed = source('scripts/seed-conflict-intel.mjs');
const unrestSeed = source('scripts/seed-unrest-events.mjs');
const resolutionSpec = source('scripts/_forecast-resolution.mjs');
const healthApi = source('api/health.js');
const seedHealthApi = source('api/seed-health.js');
it('routes conflict hard counts to a long-window resolution key, not the map display key', () => {
assert.match(resolutionSpec, /CONFLICT_COUNT_SOURCE_FEED\s*=\s*'conflict:acled-resolution:v1:all:0:0'/);
assert.doesNotMatch(resolutionSpec, /CONFLICT_COUNT_SOURCE_FEED\s*=\s*'conflict:acled:v1:all:0:0'/);
});
it('routes unrest hard counts to a long-window resolution key, not the canonical display feed', () => {
assert.match(resolutionSpec, /UNREST_COUNT_SOURCE_FEED\s*=\s*'unrest:events-resolution:v1'/);
assert.doesNotMatch(resolutionSpec, /UNREST_COUNT_SOURCE_FEED\s*=\s*'unrest:events:v1'/);
});
it('conflict seeder keeps the capped display payload but also publishes a paginated 60d resolution feed', () => {
assert.match(conflictSeed, /ACLED_CACHE_KEY\s*=\s*'conflict:acled:v1:all:0:0'/);
assert.match(conflictSeed, /ACLED_DISPLAY_LOOKBACK_DAYS\s*=\s*30/);
assert.match(conflictSeed, /ACLED_DISPLAY_LIMIT\s*=\s*500/);
assert.match(conflictSeed, /ACLED_RESOLUTION_CACHE_KEY\s*=\s*'conflict:acled-resolution:v1:all:0:0'/);
assert.match(conflictSeed, /ACLED_RESOLUTION_LOOKBACK_DAYS\s*=\s*60/);
assert.match(conflictSeed, /ACLED_RESOLUTION_PAGE_LIMIT\s*=\s*5000/);
assert.match(conflictSeed, /ACLED_RESOLUTION_MAX_PAGES\s*=\s*(?:[1-9]\d+)/);
assert.match(conflictSeed, /writeExtraKeyWithMeta\(\s*ACLED_RESOLUTION_CACHE_KEY/);
assert.match(conflictSeed, /ACLED_RESOLUTION_CACHE_KEY,[\s\S]*clusters:\s*\[\],[\s\S]*acResolution\.pagination/);
});
it('conflict seeder skips ACLED gracefully when no credentials are configured (auxiliary-only mode, #1651/#2288)', () => {
// Regression guard for #5106: a *missing* ACLED credential must NOT crash the
// seed every cron tick. When creds are absent the seed runs in its long-standing
// auxiliary-only mode — publish an empty ACLED payload and exit 0 rather than throw.
//
// #5256: returning a BARE `{ events: [], pagination: undefined }` was not enough to
// deliver the exit 0 this guard promises. It laundered an upstream outage into a
// "0 records" result, which runSeed reads as contract RETRY — and once the last-good
// keys expired, #5258's guard exited 1 and the seeder crash-looped forever. The
// payload must carry `sourceUnavailable: true` so runSeed skips the publish (an empty
// envelope would overwrite last-good) and exits 0. Keep the flag in the pattern:
// dropping it silently reinstates the crash loop.
assert.match(conflictSeed, /missingCredentials\s*=\s*acled\.status\s*===\s*'fulfilled'/);
assert.match(
conflictSeed,
/if\s*\(\s*missingCredentials\s*\)\s*\{[\s\S]*?return\s*\{\s*events:\s*\[\],\s*pagination:\s*undefined,\s*sourceUnavailable:\s*true\s*\}/,
);
});
it('conflict seeder still fails when a CONFIGURED ACLED primary feed is unavailable (no silent masking, #5106)', () => {
// #5106's genuine value is preserved: when creds ARE present but the display
// fetch fails, refuse to let auxiliary feeds silently mask the broken primary feed.
assert.match(conflictSeed, /const err = new Error\([\s\S]*ACLED display fetch failed for \$\{ACLED_CACHE_KEY\}[\s\S]*auxiliary conflict\/intel feeds mask the primary feed/);
assert.match(conflictSeed, /if\s*\(\s*acled\.reason\?\.nonRetryable\s*\)\s*err\.nonRetryable\s*=\s*true/);
assert.match(conflictSeed, /throw err/);
});
it('health surfaces the ACLED display cache and seeder heartbeat (#5099)', () => {
assert.match(healthApi, /acledIntel:\s*'conflict:acled:v1:all:0:0'/);
assertObjectProperty(healthApi, 'acledIntel', "key:\\s*'seed-meta:conflict:acled-intel'");
assertObjectProperty(healthApi, 'acledIntel', 'maxStaleMin:\\s*38');
assertObjectProperty(seedHealthApi, "'conflict:acled-intel'", "key:\\s*'seed-meta:conflict:acled-intel'");
assertObjectProperty(seedHealthApi, "'conflict:acled-intel'", 'intervalMin:\\s*19');
});
it('unrest seeder keeps the canonical display feed but also publishes a paginated 60d ACLED resolution feed', () => {
assert.match(unrestSeed, /CANONICAL_KEY\s*=\s*'unrest:events:v1'/);
assert.match(unrestSeed, /ACLED_DISPLAY_LOOKBACK_DAYS\s*=\s*30/);
assert.match(unrestSeed, /ACLED_DISPLAY_LIMIT\s*=\s*500/);
assert.match(unrestSeed, /UNREST_RESOLUTION_CACHE_KEY\s*=\s*'unrest:events-resolution:v1'/);
assert.match(unrestSeed, /UNREST_RESOLUTION_LOOKBACK_DAYS\s*=\s*60/);
assert.match(unrestSeed, /UNREST_RESOLUTION_PAGE_LIMIT\s*=\s*5000/);
assert.match(unrestSeed, /UNREST_RESOLUTION_MAX_PAGES\s*=\s*(?:[1-9]\d+)/);
assert.match(unrestSeed, /writeExtraKeyWithMeta\(\s*UNREST_RESOLUTION_CACHE_KEY/);
});
});