Adds platform infrastructure used by the upcoming refactor of
nodeClass / specificClass across all 12 nodes:
- src/domain/UnitPolicy.js — extracted from rotatingMachine/MGC
- src/domain/ChildRouter.js — declarative event routing on top of childRegistrationUtils
- src/domain/LatestWinsGate.js — extracted from MGC dispatch gate
- src/domain/HealthStatus.js — standardised {level, flags, message, source}
- src/nodered/statusBadge.js — compose / error / idle / byState / text helpers
- src/stats/index.js — mean / stdDev / median / mad / lerp
All additive — no existing exports change shape.
56 unit tests pass under node:test.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
104 lines
3.7 KiB
JavaScript
104 lines
3.7 KiB
JavaScript
'use strict';
|
|
|
|
const test = require('node:test');
|
|
const assert = require('node:assert');
|
|
|
|
const HealthStatus = require('../../src/domain/HealthStatus');
|
|
|
|
test('ok() returns the canonical zero-level shape', () => {
|
|
const h = HealthStatus.ok();
|
|
assert.strictEqual(h.level, 0);
|
|
assert.deepStrictEqual(h.flags, []);
|
|
assert.strictEqual(h.message, 'nominal');
|
|
assert.strictEqual(h.source, null);
|
|
assert.ok(Object.isFrozen(h));
|
|
assert.ok(Object.isFrozen(h.flags));
|
|
});
|
|
|
|
test('ok(message, source) carries through optional args', () => {
|
|
const h = HealthStatus.ok('all good', 'aggregator');
|
|
assert.strictEqual(h.level, 0);
|
|
assert.strictEqual(h.message, 'all good');
|
|
assert.strictEqual(h.source, 'aggregator');
|
|
});
|
|
|
|
test('degraded(2, [...], msg, src) returns the right frozen shape', () => {
|
|
const h = HealthStatus.degraded(2, ['x'], 'msg', 'src');
|
|
assert.strictEqual(h.level, 2);
|
|
assert.deepStrictEqual(h.flags, ['x']);
|
|
assert.strictEqual(h.message, 'msg');
|
|
assert.strictEqual(h.source, 'src');
|
|
assert.ok(Object.isFrozen(h));
|
|
assert.ok(Object.isFrozen(h.flags));
|
|
// Mutation attempts must not change the frozen flags array.
|
|
assert.throws(() => { h.flags.push('y'); }, TypeError);
|
|
});
|
|
|
|
test('degraded clamps out-of-range levels (high)', () => {
|
|
const h = HealthStatus.degraded(7, ['hot'], 'too high');
|
|
assert.strictEqual(h.level, 3);
|
|
});
|
|
|
|
test('degraded clamps out-of-range levels (low / non-numeric)', () => {
|
|
const lo = HealthStatus.degraded(0, ['lo'], 'too low');
|
|
assert.strictEqual(lo.level, 1);
|
|
const nan = HealthStatus.degraded('nope', ['n'], 'bad input');
|
|
assert.strictEqual(nan.level, 1);
|
|
});
|
|
|
|
test('degraded falls back to label-derived message when message is empty', () => {
|
|
const h = HealthStatus.degraded(2, ['x']);
|
|
assert.strictEqual(h.message, 'major');
|
|
});
|
|
|
|
test('compose([]) returns ok()', () => {
|
|
const h = HealthStatus.compose([]);
|
|
assert.strictEqual(h.level, 0);
|
|
assert.deepStrictEqual(h.flags, []);
|
|
assert.strictEqual(h.message, 'nominal');
|
|
assert.strictEqual(h.source, null);
|
|
});
|
|
|
|
test('compose merges, picking worst level + that status\'s message/source', () => {
|
|
const h = HealthStatus.compose([
|
|
HealthStatus.ok(),
|
|
HealthStatus.degraded(1, ['a'], 'a-msg', 'a-src'),
|
|
HealthStatus.degraded(2, ['b'], 'b-msg', 'b-src'),
|
|
]);
|
|
assert.strictEqual(h.level, 2);
|
|
assert.deepStrictEqual(h.flags, ['a', 'b']);
|
|
assert.strictEqual(h.message, 'b-msg');
|
|
assert.strictEqual(h.source, 'b-src');
|
|
});
|
|
|
|
test('compose ties: first worst-level status wins for message/source', () => {
|
|
const h = HealthStatus.compose([
|
|
HealthStatus.degraded(2, ['a'], 'first', 'first-src'),
|
|
HealthStatus.degraded(2, ['b'], 'second', 'second-src'),
|
|
]);
|
|
assert.strictEqual(h.level, 2);
|
|
assert.strictEqual(h.message, 'first');
|
|
assert.strictEqual(h.source, 'first-src');
|
|
});
|
|
|
|
test('compose dedupes flags across statuses', () => {
|
|
const h = HealthStatus.compose([
|
|
HealthStatus.degraded(1, ['x', 'y'], 'one'),
|
|
HealthStatus.degraded(2, ['y', 'z', 'x'], 'two'),
|
|
]);
|
|
assert.deepStrictEqual(h.flags, ['x', 'y', 'z']);
|
|
});
|
|
|
|
test('label maps 0..3 → nominal/minor/major/critical', () => {
|
|
assert.strictEqual(HealthStatus.label(0), 'nominal');
|
|
assert.strictEqual(HealthStatus.label(1), 'minor');
|
|
assert.strictEqual(HealthStatus.label(2), 'major');
|
|
assert.strictEqual(HealthStatus.label(3), 'critical');
|
|
});
|
|
|
|
test('label returns "unknown" for out-of-range levels', () => {
|
|
assert.strictEqual(HealthStatus.label(-1), 'unknown');
|
|
assert.strictEqual(HealthStatus.label(4), 'unknown');
|
|
assert.strictEqual(HealthStatus.label('x'), 'unknown');
|
|
});
|