Updates
CI / test (push) Successful in 1m1s
Release rolling / release (push) Successful in 6m59s

This commit is contained in:
Raven Scott
2026-07-18 20:32:05 -04:00
parent 41f5476a60
commit a7648dc090
28 changed files with 2539 additions and 333 deletions
+28
View File
@@ -40,3 +40,31 @@ test('evaluate includes continuous score in message', (t) => {
t.ok(fired[0].score >= 0.6)
t.ok(String(fired[0].message).includes('score'))
})
test('getWeights ranks active anomaly charts', (t) => {
const eng = new AnomalyEngine({ cpuCount: 2 })
eng.setConfig({
id: 'load_high',
chart: 'system.load',
dimension: 'load1',
warn: 1,
crit: 2,
comparator: '>',
enabled: true,
info: 'Load',
})
eng.evaluate([
{
chart: 'system.load',
context: 'system.load',
ts: Date.now(),
values: { load1: 5 },
},
])
const w = eng.getWeights({ limit: 20 })
t.ok(w.results?.length >= 1)
const row = w.results.find((r) => r.id === 'system.load')
t.ok(row)
t.ok(row.weight >= 0.65)
t.is(row.severity, 'critical')
})
+25 -23
View File
@@ -7,33 +7,35 @@ import {
upsertBookmark,
removeBookmark,
setBookmarkAlias,
bookmarksPath,
} from '../client/bookmarks.js'
const tmp = path.join(os.tmpdir(), `peardata-bm-${Date.now()}`)
import { getPeersCachePath } from '../client/peerCache.js'
test('bookmarks upsert alias remove', (t) => {
fs.mkdirSync(tmp, { recursive: true })
const prev = process.env.PEARDATA_HOME
const tmp = fs.mkdtempSync(path.join(os.tmpdir(), 'peardata-bm-'))
process.env.PEARDATA_HOME = tmp
t.ok(bookmarksPath().includes('bookmarks.json'))
const pk = 'a'.repeat(64)
upsertBookmark({ publicKeyHex: pk, alias: 'box-1' })
let list = loadBookmarks()
t.is(list.length, 1)
t.is(list[0].alias, 'box-1')
setBookmarkAlias(pk, 'box-2')
list = loadBookmarks()
t.is(list[0].alias, 'box-2')
removeBookmark(pk)
t.is(loadBookmarks().length, 0)
try {
fs.rmSync(tmp, { recursive: true, force: true })
} catch {
// ignore
t.ok(getPeersCachePath().includes(path.join('cache', 'peers.json')))
const pk = 'a'.repeat(64)
upsertBookmark({ publicKeyHex: pk, alias: 'box-1' })
let list = loadBookmarks()
t.is(list.length, 1)
t.is(list[0].alias, 'box-1')
setBookmarkAlias(pk, 'box-2')
list = loadBookmarks()
t.is(list[0].alias, 'box-2')
removeBookmark(pk)
t.is(loadBookmarks().length, 0)
} finally {
if (prev === undefined) delete process.env.PEARDATA_HOME
else process.env.PEARDATA_HOME = prev
try {
fs.rmSync(tmp, { recursive: true, force: true })
} catch {
// ignore
}
}
delete process.env.PEARDATA_HOME
})
+82
View File
@@ -0,0 +1,82 @@
import test from 'brittle'
import fs from 'fs'
import os from 'os'
import path from 'path'
import {
normalizePeerEntry,
parsePeersPayload,
loadPeers,
savePeers,
upsertPeer,
removePeer,
getLastActivePeerId,
setLastActivePeerId,
getPeersCachePath,
} from '../client/peerCache.js'
function withHome(fn) {
const prev = process.env.PEARDATA_HOME
const tmp = fs.mkdtempSync(path.join(os.tmpdir(), 'peardata-peers-'))
process.env.PEARDATA_HOME = tmp
try {
return fn(tmp)
} finally {
if (prev === undefined) delete process.env.PEARDATA_HOME
else process.env.PEARDATA_HOME = prev
try {
fs.rmSync(tmp, { recursive: true, force: true })
} catch {
// ignore
}
}
}
test('normalizePeerEntry accepts hex keys', (t) => {
const hex = 'a'.repeat(64)
const e = normalizePeerEntry({ publicKeyHex: hex, alias: 'prod' })
t.is(e.publicKeyHex, hex)
t.is(e.alias, 'prod')
t.absent(normalizePeerEntry({ publicKeyHex: 'nope' }))
})
test('parsePeersPayload reads envelope + bookmarks legacy', (t) => {
const hex = 'b'.repeat(64)
const fromEnv = parsePeersPayload({
version: 1,
peers: { [hex]: { publicKeyHex: hex, alias: 'x' } },
})
t.ok(fromEnv[hex])
const fromBm = parsePeersPayload({
bookmarks: [{ publicKeyHex: hex, alias: 'y' }],
})
t.is(fromBm[hex].alias, 'y')
})
test('upsertPeer + active id round-trip', (t) => {
withHome(() => {
const hex = 'c'.repeat(64)
upsertPeer({ publicKeyHex: hex, alias: 'lab' }, { makeActive: true })
const peers = loadPeers()
t.is(peers[hex].alias, 'lab')
t.is(getLastActivePeerId(), hex)
t.ok(fs.existsSync(getPeersCachePath()))
setLastActivePeerId(null)
t.absent(getLastActivePeerId())
removePeer(hex)
t.absent(loadPeers()[hex])
})
})
test('savePeers refuses accidental wipe', (t) => {
withHome(() => {
const hex = 'd'.repeat(64)
upsertPeer({ publicKeyHex: hex }, { makeActive: true })
const ok = savePeers({})
t.is(ok, false)
t.ok(loadPeers()[hex], 'peer still present after refused wipe')
savePeers({}, { force: true, activePeerId: null })
t.is(Object.keys(loadPeers()).length, 0)
})
})
+1
View File
@@ -39,6 +39,7 @@ test('method roles cover monitoring surface', (t) => {
'unlinkPeer',
'getFleetHealth',
'listChildPeers',
'getWeights',
]) {
t.ok(MethodRoles[m], m)
t.is(Methods[m], m)
+40
View File
@@ -0,0 +1,40 @@
import test from 'brittle'
import { pearson, rankRelatedCharts } from '../shared/related-metrics.js'
test('pearson correlates aligned series', (t) => {
const x = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
const y = x.map((v) => v * 2)
t.ok(pearson(x, y) > 0.99)
const z = x.map((v) => -v)
t.ok(pearson(x, z) < -0.99)
})
test('rankRelatedCharts prefers same context/family', (t) => {
const catalog = {
'system.cpu': { context: 'system.cpu', family: 'cpu', plugin: 'proc', units: '%' },
'cpu.cpu0': { context: 'cpu.cpu', family: 'cpu', plugin: 'proc', units: '%' },
'system.ram': { context: 'system.ram', family: 'ram', plugin: 'proc', units: 'MiB' },
'nginx.connections': { context: 'nginx.connections', family: 'nginx', plugin: 'nginx', units: 'connections' },
}
const ranked = rankRelatedCharts('system.cpu', catalog, null, { limit: 5 })
t.ok(ranked.length >= 1)
t.is(ranked[0].id, 'cpu.cpu0')
t.ok(!ranked.some((r) => r.id === 'nginx.connections') || ranked.at(-1)?.id === 'nginx.connections')
})
test('rankRelatedCharts boosts loaded correlation', (t) => {
const series = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12]
const catalog = {
a: { context: 'x.a', family: 'f1', plugin: 'p' },
b: { context: 'y.b', family: 'f2', plugin: 'q' },
c: { context: 'z.c', family: 'f3', plugin: 'r' },
}
const loaded = new Map([
['a', { dims: new Map([['v', series]]) }],
['b', { dims: new Map([['v', series.map((n) => n * 3)]]) }],
['c', { dims: new Map([['v', series.map((_, i) => (i % 2 ? 10 : 0))]]) }],
])
const ranked = rankRelatedCharts('a', catalog, loaded, { limit: 5 })
t.ok(ranked[0].id === 'b')
t.ok(ranked[0].reason.includes('corr'))
})
+36
View File
@@ -0,0 +1,36 @@
import test from 'brittle'
import { getAllChartDefs } from '../shared/metrics.js'
import { sectionForChart, SECTIONS } from '../shared/taxonomy.js'
test('static catalog charts map into taxonomy sections', (t) => {
const defs = getAllChartDefs()
t.ok(defs.length > 20)
/** @type {Record<string, number>} */
const counts = Object.fromEntries(SECTIONS.map((s) => [s.id, 0]))
let other = 0
for (const def of defs) {
const sec = sectionForChart(def.id, def)
counts[sec.id] = (counts[sec.id] || 0) + 1
if (sec.id === 'other') other++
}
t.ok(counts.system > 0, 'system section should have charts')
// Most built-in charts should not dump into Other
const ratio = other / defs.length
t.ok(ratio < 0.35, `other ratio ${ratio.toFixed(2)} should stay under 35%`)
})
test('common plugin prefixes are classified', (t) => {
const samples = [
['system.cpu', 'system'],
['mem.available', 'system'],
['docker.cpu.x', 'containers'],
['zfs.arc', 'storage'],
['sensors.temp.cpu', 'hardware'],
['nginx.connections', 'applications'],
['ebpf.cachestat', 'observability'],
['fleet.nodes', 'fleet'],
]
for (const [id, want] of samples) {
t.is(sectionForChart(id, { context: id }).id, want, id)
}
})