import test from 'brittle' import { MetricStore } from '../server/services/store.js' import { AnomalyEngine } from '../server/services/anomaly.js' import { stats, zScore, thresholdsFromStats, seriesFromStore } from '../server/services/zscore.js' test('stats and zScore', (t) => { const s = stats([10, 12, 11, 13, 10]) t.ok(s.n === 5) t.ok(Math.abs(s.mean - 11.2) < 0.01) t.ok(s.stdev > 0) t.ok(Math.abs(zScore(11.2, s.mean, s.stdev)) < 0.01) t.ok(zScore(20, s.mean, s.stdev) > 2) }) test('thresholdsFromStats greater-than', (t) => { const thr = thresholdsFromStats({ mean: 10, stdev: 2 }, '>', { warnZ: 2, critZ: 3 }) t.is(thr.warn, 14) t.is(thr.crit, 16) }) test('retrainAnomaly updates thresholds from store', (t) => { const store = new MetricStore() const now = Date.now() for (let i = 0; i < 40; i++) { store.ingest([ { chart: 'system.cpu', context: 'system.cpu', ts: now + i * 1000, values: { user: 20 + (i % 3), system: 5, nice: 0, iowait: 0, irq: 0, softirq: 0, idle: 70, steal: 0, }, }, ]) } const eng = new AnomalyEngine({ cpuCount: 2 }) const res = eng.retrain(store, { minPoints: 20, warnZ: 2, critZ: 3 }) t.ok(res.ok) t.ok(res.updated.length >= 1) const cpu = eng.configs.get('cpu_user_high') t.ok(cpu.warn > 20) t.ok(cpu.crit >= cpu.warn) t.ok(seriesFromStore(store, 'system.cpu', 'user').length >= 20) })