Files
worldmonitor/tests/resilience-intervals-handler.test.mts
Elie Habib 0a1b74a9b2 feat(resilience): add score confidence intervals via batch Monte Carlo (#2877)
* feat(resilience): add score confidence intervals via batch Monte Carlo

Weekly cron perturbs domain weights ±10% across 100 draws per country,
stores p05/p95 in Redis. Score handler reads intervals and includes
them in the API response as ScoreInterval { p05, p95 }.

Proto field 14 (score_interval) added to GetResilienceScoreResponse.

* chore: regenerate proto types and OpenAPI docs for ScoreInterval

* fix(resilience): add seed-meta + lock + fix interval cache + percentile formula

1. Write seed-meta:resilience:intervals for health monitoring
2. Add distributed lock to prevent concurrent cron overlap
3. Move scoreInterval read outside 6h score cache boundary
4. Fix percentile index from floor to ceil-1 (nearest-rank)

* fix(health): add resilience:intervals to health + seed-health registries

* fix(seed): skip seed-meta on no-op runs + register intervals in health check
2026-04-09 22:06:54 +04:00

68 lines
2.5 KiB
TypeScript

import assert from 'node:assert/strict';
import { afterEach, describe, it } from 'node:test';
import { getResilienceScore } from '../server/worldmonitor/resilience/v1/get-resilience-score.ts';
import { createRedisFetch } from './helpers/fake-upstash-redis.mts';
import { RESILIENCE_FIXTURES } from './helpers/resilience-fixtures.mts';
const originalFetch = globalThis.fetch;
const originalRedisUrl = process.env.UPSTASH_REDIS_REST_URL;
const originalRedisToken = process.env.UPSTASH_REDIS_REST_TOKEN;
const originalVercelEnv = process.env.VERCEL_ENV;
afterEach(() => {
globalThis.fetch = originalFetch;
if (originalRedisUrl == null) delete process.env.UPSTASH_REDIS_REST_URL;
else process.env.UPSTASH_REDIS_REST_URL = originalRedisUrl;
if (originalRedisToken == null) delete process.env.UPSTASH_REDIS_REST_TOKEN;
else process.env.UPSTASH_REDIS_REST_TOKEN = originalRedisToken;
if (originalVercelEnv == null) delete process.env.VERCEL_ENV;
else process.env.VERCEL_ENV = originalVercelEnv;
});
describe('resilience score interval integration', () => {
it('includes scoreInterval when Redis has interval data', async () => {
process.env.UPSTASH_REDIS_REST_URL = 'https://redis.example';
process.env.UPSTASH_REDIS_REST_TOKEN = 'token';
delete process.env.VERCEL_ENV;
const fixtures = {
...RESILIENCE_FIXTURES,
'resilience:intervals:v1:US': {
p05: 65.2,
p95: 72.8,
draws: 100,
computedAt: '2026-04-06T00:00:00.000Z',
},
};
const { fetchImpl } = createRedisFetch(fixtures);
globalThis.fetch = fetchImpl;
const response = await getResilienceScore(
{ request: new Request('https://example.com') } as never,
{ countryCode: 'US' },
);
assert.ok(response.scoreInterval, 'scoreInterval should be present');
assert.equal(response.scoreInterval.p05, 65.2);
assert.equal(response.scoreInterval.p95, 72.8);
});
it('omits scoreInterval when Redis has no interval data', async () => {
process.env.UPSTASH_REDIS_REST_URL = 'https://redis.example';
process.env.UPSTASH_REDIS_REST_TOKEN = 'token';
delete process.env.VERCEL_ENV;
const { fetchImpl } = createRedisFetch(RESILIENCE_FIXTURES);
globalThis.fetch = fetchImpl;
const response = await getResilienceScore(
{ request: new Request('https://example.com') } as never,
{ countryCode: 'US' },
);
assert.equal(response.scoreInterval, undefined, 'scoreInterval should be absent when no interval data exists');
});
});