File size: 6,392 Bytes
ec8acdf | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 | import assert from 'node:assert/strict';
import { describe, it } from 'node:test';
import {
buildAlertDigest,
buildWeeklyTrends,
SEVERITY_RANK,
} from '../shared/analysis-alert-digest.ts';
const NOW = Date.parse('2026-07-28T00:00:00.000Z');
const fullInputs = () => ({
cii: [
{ code: 'SD', score: 82, level: 'critical' },
{ code: 'UA', score: 64, level: 'high' },
{ code: 'DE', score: 12, level: 'low' },
],
militarySurges: [
{ theaterId: 'centcom', surgeType: 'fighter', surgeMultiple: 2.4, strikeCapable: true },
{ theaterId: 'eucom', surgeType: 'airlift', surgeMultiple: 2.1, strikeCapable: false },
],
cables: [
{ name: 'SeaMeWe-6', status: 'CABLE_HEALTH_STATUS_FAULT' },
{ name: 'Curie', status: 'CABLE_HEALTH_STATUS_DEGRADED' },
{ name: 'Marea', status: 'CABLE_HEALTH_STATUS_OK' },
],
outages: [
{ country: 'Sudan', severity: 'major', detectedAt: NOW - 3_600_000 },
{ country: 'Nepal', severity: 'minor', detectedAt: NOW - 90_000_000, endedAt: NOW - 86_400_000 },
],
temporalAnomalies: [
{ type: 'news', region: 'Middle East', zScore: 3.4, severity: 'critical' },
],
thermal: [
{ id: 'strait-of-hormuz', level: 'high', score: 71 },
{ id: 'baltic', level: 'low', score: 12 },
],
shippingStress: { index: 61, level: 'high' },
});
describe('buildAlertDigest', () => {
it('trips each domain on its own severity vocabulary', () => {
const digest = buildAlertDigest(fullInputs(), NOW);
const domains = new Set(digest.tripped.map((t) => t.domain));
assert.deepEqual(
[...domains].sort(),
['cable_health', 'cii', 'military_surge', 'outages', 'shipping_stress', 'temporal_anomaly', 'thermal'],
);
// CII: critical + high trip, low does not.
const cii = digest.tripped.filter((t) => t.domain === 'cii');
assert.deepEqual(cii.map((t) => t.id).sort(), ['SD', 'UA']);
// Cables: fault + degraded trip, ok does not.
const cables = digest.tripped.filter((t) => t.domain === 'cable_health');
assert.deepEqual(cables.map((t) => t.id).sort(), ['Curie', 'SeaMeWe-6']);
// Outages: the ended one must not trip.
const outages = digest.tripped.filter((t) => t.domain === 'outages');
assert.equal(outages.length, 1);
assert.equal(outages[0].id, 'Sudan');
});
it('assigns severities from domain semantics and sorts by rank', () => {
const digest = buildAlertDigest(fullInputs(), NOW);
const bySev = Object.fromEntries(digest.tripped.map((t) => [`${t.domain}:${t.id}`, t.severity]));
assert.equal(bySev['cii:SD'], 'critical');
assert.equal(bySev['cii:UA'], 'high');
assert.equal(bySev['military_surge:fighter-centcom'], 'critical'); // strikeCapable
assert.equal(bySev['military_surge:airlift-eucom'], 'high');
assert.equal(bySev['cable_health:SeaMeWe-6'], 'high'); // fault
assert.equal(bySev['cable_health:Curie'], 'medium'); // degraded
assert.equal(bySev['temporal_anomaly:news:Middle East'], 'critical');
const ranks = digest.tripped.map((t) => SEVERITY_RANK[t.severity]);
assert.deepEqual(ranks, [...ranks].sort((a, b) => b - a), 'sorted most-severe first');
});
it('separates quiet domains from unavailable domains', () => {
const digest = buildAlertDigest(
{
cii: [{ code: 'DE', score: 8, level: 'low' }],
militarySurges: [],
cables: null,
outages: undefined,
temporalAnomalies: [],
thermal: null,
shippingStress: null,
},
NOW,
);
assert.equal(digest.tripped.length, 0);
assert.deepEqual(digest.quiet.sort(), ['cii', 'military_surge', 'temporal_anomaly']);
assert.deepEqual(digest.unavailable.sort(), ['cable_health', 'outages', 'shipping_stress', 'thermal']);
assert.equal(digest.generatedAt, new Date(NOW).toISOString());
});
it('treats the producer endedAt=0 sentinel as an ongoing outage', () => {
const digest = buildAlertDigest({
cii: [],
militarySurges: [],
cables: [],
outages: [{ country: 'Sudan', severity: 'critical', endedAt: 0 }],
temporalAnomalies: [],
thermal: [],
shippingStress: { index: 0, level: 'low' },
}, NOW);
const outage = digest.tripped.find((alert) => alert.domain === 'outages');
assert.ok(outage);
assert.equal(outage.severity, 'critical');
});
it('does not trip shipping stress without an elevated level', () => {
const inputs = { ...fullInputs(), shippingStress: { index: 30, level: 'normal' } };
const digest = buildAlertDigest(inputs, NOW);
assert.ok(!digest.tripped.some((t) => t.domain === 'shipping_stress'));
assert.ok(digest.quiet.includes('shipping_stress'));
});
it('tolerates malformed entries without throwing', () => {
const digest = buildAlertDigest(
{
cii: [{ code: null, score: 'x', level: 42 }, {}],
militarySurges: [{}],
cables: [{ status: 'BOGUS' }],
outages: [{}],
temporalAnomalies: [{ severity: 'nonsense' }],
thermal: [{}],
shippingStress: {},
},
NOW,
);
assert.ok(Array.isArray(digest.tripped));
});
});
describe('buildWeeklyTrends', () => {
const series = (values) => values.map((v, i) => ({ t: NOW - (values.length - 1 - i) * 86_400_000, value: v }));
it('computes direction, volatility, and anomaly flags per domain', () => {
const trends = buildWeeklyTrends(
[
{ domain: 'cii', points: series([10, 10, 11, 10, 30, 45, 60]) },
{ domain: 'outages', points: series([5, 5, 5, 5, 5, 5, 5]) },
{ domain: 'news', points: series([60, 50, 40, 30, 20, 10, 5]) },
],
NOW,
);
const byDomain = Object.fromEntries(trends.map((t) => [t.domain, t]));
assert.equal(byDomain.cii.direction, 'rising');
assert.equal(byDomain.outages.direction, 'flat');
assert.equal(byDomain.news.direction, 'falling');
assert.equal(byDomain.outages.volatility, 0);
assert.ok(byDomain.cii.volatility > byDomain.outages.volatility);
assert.equal(byDomain.cii.latest, 60);
assert.ok(byDomain.cii.anomalous, 'latest 60 is > mean + 2σ of the early-week baseline');
assert.ok(!byDomain.outages.anomalous);
});
it('skips domains with fewer than 3 points', () => {
const trends = buildWeeklyTrends([{ domain: 'thin', points: series([1, 2]) }], NOW);
assert.equal(trends.length, 0);
});
});
|