Luigit
repositories / dotfiles

dotfiles

bugabingas dorkfiles

owned by admin

quickshell/nuguland/sysinfo.test.js

Raw
const assert = require('assert');
const S = require('./sysinfo.js');

// ============================================================================
// Fixtures captured from the host (2012 ThinkPad X230) via /proc, df, systemctl, ps, hwmon.
// ============================================================================

const STAT0 = 'cpu  5430899 3815162 1035595 41251079 269664 239859 55955 0 0 0\ncpu0 1 2 3 4';
// second snapshot: +100 system, +100 idle => totalDelta 200, idleDelta 100 => usage 0.5
const STAT1 = 'cpu  5430899 3815162 1035695 41251179 269664 239859 55955 0 0 0\ncpu0 9 9 9 9';

const MEMINFO = [
    'MemTotal:       16176232 kB',
    'MemFree:         9155632 kB',
    'MemAvailable:   12945364 kB',
    'Buffers:             680 kB',
    'SwapTotal:      33554428 kB',
    'SwapFree:       26795148 kB'
].join('\n');

const NETDEV = [
    'Inter-|   Receive                                                |  Transmit',
    ' face |bytes    packets errs drop fifo frame compressed multicast|bytes    packets errs drop fifo colls carrier compressed',
    '    lo: 54663953   73730    0    0    0     0          0         0 54663953   73730    0    0    0     0       0          0',
    'enp0s25:       0       0    0    0    0     0          0         0        0       0    0    0    0     0       0          0',
    ' wlan0: 2445277803 4752440    0 129580    0     0          0         0 5890720566 5360204    0   82    0     0       0          0'
].join('\n');

const DISKSTATS = [
    '   8       0 sda 9645 2012 90857 763 42944 11803 281531 93778 0 74294 161809 0 0 0 0 39089 67268',
    '   8       1 sda1 234 2012 8145 99 12 1 11 3 0 56 102 0 0 0 0 0 0',
    '   7       0 loop0 10 0 80 1 0 0 0 0 0 0 0',
    ' 259       0 nvme0n1 287018 21005 15602232 437312 519616 172971 20543952 3947826 0 1207756 4536137 13779 0 34278912 73655 19543 77343'
].join('\n');

const LOADAVG = '0.76 0.64 0.56 1/714 710938';

const DF = [
    'Filesystem         1-blocks        Used    Available Capacity Mounted on',
    '/dev/sda3      590718976000 31650119680 556774883328       6% /',
    'tmpfs            8282230784  5876457472   2405773312      71% /tmp',
    '/dev/sda1        1071624192   193921024    877703168      19% /boot',
    '/dev/sda3      590718976000 31650119680 556774883328       6% /home'
].join('\n');

const FAILED_USER = 'app-Handy@autostart.service not-found failed failed app-Handy@autostart.service';
const FAILED_NONE = '';

const PS = [
    '    PID COMMAND         %CPU %MEM',
    ' 710984 rtk             77.7  0.0',
    ' 493586 claude           2.7  2.8',
    ' 619330 firefox          1.7  2.9'
].join('\n');

// ============================================================================
// cpuDelta — first call (no prev), steady-state delta, garbage guard
// ============================================================================
let cd0 = S.cpuDelta(null, STAT0);
assert.strictEqual(cd0.usage, 0, 'first call usage 0');
assert.strictEqual(cd0.next.idle, 41251079 + 269664, 'idle = idle + iowait');
assert.strictEqual(cd0.next.total, 5430899 + 3815162 + 1035595 + 41251079 + 269664 + 239859 + 55955, 'total = sum');

let cd1 = S.cpuDelta(cd0.next, STAT1);
assert.strictEqual(cd1.usage, 0.5, 'delta 200 total / 100 non-idle => 0.5');

let cdg = S.cpuDelta(cd0.next, 'garbage no numbers here');
assert.strictEqual(cdg.usage, 0, 'garbage -> usage 0');
assert.strictEqual(cdg.next, cd0.next, 'garbage keeps prev snapshot');

// ============================================================================
// parseMeminfo — bytes (kB*1024) + usage ratios
// ============================================================================
let mem = S.parseMeminfo(MEMINFO);
assert.strictEqual(mem.memTotal, 16176232 * 1024, 'memTotal bytes');
assert.strictEqual(mem.memAvailable, 12945364 * 1024, 'memAvailable bytes (prefers MemAvailable)');
assert.strictEqual(mem.swapTotal, 33554428 * 1024, 'swapTotal bytes');
assert.strictEqual(mem.swapFree, 26795148 * 1024, 'swapFree bytes');
assert.ok(Math.abs(mem.memoryUsage - (16176232 - 12945364) / 16176232) < 1e-9, 'memoryUsage ratio');
assert.ok(Math.abs(mem.swapUsage - (33554428 - 26795148) / 33554428) < 1e-9, 'swapUsage ratio');
// no-swap machine -> swapUsage 0, no divide-by-zero
assert.strictEqual(S.parseMeminfo('MemTotal: 100 kB\nMemAvailable: 50 kB').swapUsage, 0, 'no swap -> 0');

// ============================================================================
// parseNetDev — sum non-lo
// ============================================================================
let net = S.parseNetDev(NETDEV);
assert.strictEqual(net.rx, 0 + 2445277803, 'rx sums enp0s25 + wlan0, excludes lo');
assert.strictEqual(net.tx, 0 + 5890720566, 'tx sums enp0s25 + wlan0, excludes lo');

// ============================================================================
// parseDiskstats — sectors*512, skip loop/ram AND partitions (avoid double-count)
// ============================================================================
let disk = S.parseDiskstats(DISKSTATS);
// whole-disk only: sda=90857, nvme0n1=15602232 ; sda1 partition + loop0 skipped
assert.strictEqual(disk.readBytes, (90857 + 15602232) * 512, 'readBytes whole-disk sum *512');
assert.strictEqual(disk.writeBytes, (281531 + 20543952) * 512, 'writeBytes whole-disk sum *512');

// ============================================================================
// rate — per-second, guards reset + bad interval
// ============================================================================
assert.strictEqual(S.rate(1000, 3000, 2), 1000, 'rate (3000-1000)/2');
assert.strictEqual(S.rate(5000, 1000, 2), 0, 'counter reset -> 0');
assert.strictEqual(S.rate(0, 100, 0), 0, 'zero interval -> 0');

// ============================================================================
// parseLoadavg
// ============================================================================
let la = S.parseLoadavg(LOADAVG);
assert.strictEqual(la.one, 0.76);
assert.strictEqual(la.five, 0.64);
assert.strictEqual(la.fifteen, 0.56);

// ============================================================================
// parseDf — skip header, parse percent, capture source + target
// ============================================================================
let df = S.parseDf(DF);
assert.strictEqual(df.length, 4, 'four real rows, header skipped');
assert.deepStrictEqual(df[0], { source: '/dev/sda3', target: '/', usedPercent: 6 });
assert.deepStrictEqual(df[1], { source: 'tmpfs', target: '/tmp', usedPercent: 71 });
assert.strictEqual(df[3].target, '/home');

// ============================================================================
// parseFailedUnits
// ============================================================================
assert.deepStrictEqual(S.parseFailedUnits(FAILED_USER), ['app-Handy@autostart.service']);
assert.deepStrictEqual(S.parseFailedUnits(FAILED_NONE), [], 'empty -> no units');

// ============================================================================
// parsePsTop — skip header (non-numeric pid)
// ============================================================================
let ps = S.parsePsTop(PS);
assert.strictEqual(ps.length, 3, 'header dropped');
assert.deepStrictEqual(ps[0], { pid: 710984, name: 'rtk', cpu: 77.7, mem: 0.0 });
assert.strictEqual(ps[1].name, 'claude');

// ============================================================================
// parseTemp / parseFan
// ============================================================================
assert.strictEqual(S.parseTemp('65000'), 65, 'millidegrees -> C');
assert.strictEqual(S.parseTemp(''), null, 'empty temp -> null');
assert.strictEqual(S.parseFan('3816'), 3816, 'fan rpm');
assert.strictEqual(S.parseFan('  '), null, 'empty fan -> null');

// ============================================================================
// formatBytes / formatRate — verbatim thresholds
// ============================================================================
assert.strictEqual(S.formatBytes(512), '512 B');
assert.strictEqual(S.formatBytes(2048), '2.0 KiB');
assert.strictEqual(S.formatBytes(5 * 1048576), '5.0 MiB');
assert.strictEqual(S.formatBytes(3 * 1073741824), '3.0 GiB');
assert.strictEqual(S.formatBytes(-5), '0 B', 'negative clamps');
assert.strictEqual(S.formatRate(2048), '2.0 KiB/s');

// ============================================================================
// pushRing — append + cap, returns new array, original untouched
// ============================================================================
let r = [];
for (let i = 1; i <= 5; i++) r = S.pushRing(r, i, 3);
assert.deepStrictEqual(r, [3, 4, 5], 'capped to last 3');
let orig = [1, 2];
let r2 = S.pushRing(orig, 3, 32);
assert.deepStrictEqual(orig, [1, 2], 'input not mutated');
assert.deepStrictEqual(r2, [1, 2, 3], 'appended copy');

// ============================================================================
// detectIssues — empty all-clear, then each rule: absent / below / above
// ============================================================================
const CORES = 4;
function base() {
    // a fully-healthy state: every check present and below threshold
    return {
        failedUnits: [],
        cpuTemperatureC: 55, tempHighC: 87, degradationReason: null,
        diskUsage: [{ source: '/dev/sda3', target: '/', usedPercent: 6 }],
        memoryUsage: 0.5, swapGrowing: false,
        healthPercentage: 95, isLaptopBattery: true,
        loadOne: 1.0, cores: CORES,
        updateCount: 0
    };
}
function ids(state) { return S.detectIssues(state).map(function (i) { return i.id; }); }
function find(state, id) { return S.detectIssues(state).filter(function (i) { return i.id === id; })[0]; }

// all-clear -> [] (no synthetic ok row; that's a view concern)
assert.deepStrictEqual(S.detectIssues(base()), [], 'healthy -> no issues');
assert.deepStrictEqual(S.detectIssues({}), [], 'empty state -> no issues (all absent)');

// --- Failed services (error) ---
{
    let s = base(); s.failedUnits = ['app-Handy@autostart.service'];
    let f = find(s, 'failed-services');
    assert.strictEqual(f.severity, 'error', 'failed -> error');
    assert.deepStrictEqual(f.action, { kind: 'systemctl-status', units: ['app-Handy@autostart.service'] });
    let absent = base(); delete absent.failedUnits;
    assert.strictEqual(ids(absent).indexOf('failed-services'), -1, 'no failedUnits -> skip');
}

// --- Thermal: absent / below 87 / >=87 warning / degradation error ---
{
    let absent = base(); absent.cpuTemperatureC = null; absent.degradationReason = null;
    assert.strictEqual(ids(absent).indexOf('thermal'), -1, 'no temp + no signal -> skip');
    let below = base(); below.cpuTemperatureC = 86;
    assert.strictEqual(ids(below).indexOf('thermal'), -1, '86 < 87 -> none');
    let at = base(); at.cpuTemperatureC = 87;
    assert.strictEqual(find(at, 'thermal').severity, 'warning', '87 >= 87 -> warning');
    let deg = base(); deg.cpuTemperatureC = 60; deg.degradationReason = 'ThermalLimit';
    assert.strictEqual(find(deg, 'thermal').severity, 'error', 'degradation thermal -> error even when cool');
    let degOther = base(); degOther.degradationReason = 'PowerSupply';
    assert.strictEqual(ids(degOther).indexOf('thermal'), -1, 'non-thermal degradation -> skip');
}

// --- Low disk: absent / 89 none / 90 warn / 94 warn / 95 error ; per-source dedupe ---
{
    let absent = base(); delete absent.diskUsage;
    assert.strictEqual(ids(absent).indexOf('low-disk:/dev/sda3'), -1, 'no diskUsage -> skip');
    function diskAt(pct) { let s = base(); s.diskUsage = [{ source: '/dev/sda3', target: '/', usedPercent: pct }]; return s; }
    assert.strictEqual(S.detectIssues(diskAt(89)).length, 0, '89 -> none');
    assert.strictEqual(find(diskAt(90), 'low-disk:/dev/sda3').severity, 'warning', '90 -> warning');
    assert.strictEqual(find(diskAt(94), 'low-disk:/dev/sda3').severity, 'warning', '94 -> warning');
    assert.strictEqual(find(diskAt(95), 'low-disk:/dev/sda3').severity, 'error', '95 -> error');
    // same source mounted twice over threshold -> one issue (worst)
    let dup = base();
    dup.diskUsage = [
        { source: '/dev/sda3', target: '/', usedPercent: 91 },
        { source: '/dev/sda3', target: '/home', usedPercent: 96 }
    ];
    let dupIssues = S.detectIssues(dup).filter(function (i) { return i.id.indexOf('low-disk') === 0; });
    assert.strictEqual(dupIssues.length, 1, 'same device -> one disk issue');
    assert.strictEqual(dupIssues[0].severity, 'error', 'worst mount wins');
}

// --- Memory pressure: needs >=0.9 AND swapGrowing ---
{
    let high = base(); high.memoryUsage = 0.95; high.swapGrowing = true;
    assert.strictEqual(find(high, 'mem-pressure').severity, 'warning', '0.95 + growing -> warning');
    let noGrow = base(); noGrow.memoryUsage = 0.95; noGrow.swapGrowing = false;
    assert.strictEqual(ids(noGrow).indexOf('mem-pressure'), -1, 'high mem but no swap growth -> none');
    let low = base(); low.memoryUsage = 0.5; low.swapGrowing = true;
    assert.strictEqual(ids(low).indexOf('mem-pressure'), -1, 'low mem -> none');
    let absent = base(); absent.memoryUsage = null;
    assert.strictEqual(ids(absent).indexOf('mem-pressure'), -1, 'no mem reading -> skip');
}

// --- Battery health tiers: 80 none, 79 warn, 60 warn, 59 error, absent/no-battery skip ---
{
    function bat(h) { let s = base(); s.healthPercentage = h; return s; }
    assert.strictEqual(S.detectIssues(bat(80)).filter(i => i.id === 'battery-health').length, 0, '80 -> none');
    assert.strictEqual(find(bat(79), 'battery-health').severity, 'warning', '79 -> warning');
    assert.strictEqual(find(bat(60), 'battery-health').severity, 'warning', '60 -> warning');
    assert.strictEqual(find(bat(59), 'battery-health').severity, 'error', '59 -> error');
    let absent = base(); absent.healthPercentage = null;
    assert.strictEqual(ids(absent).indexOf('battery-health'), -1, 'no health -> skip');
    let desktop = bat(50); desktop.isLaptopBattery = false;
    assert.strictEqual(ids(desktop).indexOf('battery-health'), -1, 'no laptop battery -> skip');
}

// --- High load: loadOne > cores (strict) ---
{
    let eq = base(); eq.loadOne = 4; eq.cores = 4;
    assert.strictEqual(ids(eq).indexOf('high-load'), -1, 'load == cores -> none (strict >)');
    let over = base(); over.loadOne = 4.1; over.cores = 4;
    assert.strictEqual(find(over, 'high-load').severity, 'warning', '4.1 > 4 -> warning');
    let absent = base(); absent.loadOne = null;
    assert.strictEqual(ids(absent).indexOf('high-load'), -1, 'no load -> skip');
}

// --- Updates: 0 none, 1 info, absent (n/a) skip ---
{
    let zero = base(); zero.updateCount = 0;
    assert.strictEqual(ids(zero).indexOf('updates'), -1, '0 updates -> none');
    let one = base(); one.updateCount = 1;
    assert.strictEqual(find(one, 'updates').severity, 'info', '1 update -> info');
    let absent = base(); absent.updateCount = null;
    assert.strictEqual(ids(absent).indexOf('updates'), -1, 'updateCount n/a -> skip');
}

// --- Sort order: error -> warning -> info ---
{
    let s = base();
    s.failedUnits = ['x.service'];                 // error
    s.cpuTemperatureC = 90;                         // warning (thermal)
    s.updateCount = 3;                              // info
    let order = S.detectIssues(s).map(i => i.severity);
    let ranks = order.map(sev => sev === 'error' ? 0 : sev === 'warning' ? 1 : 2);
    for (let i = 1; i < ranks.length; i++) assert.ok(ranks[i] >= ranks[i - 1], 'sorted error->warning->info');
    assert.strictEqual(order[0], 'error');
    assert.strictEqual(order[order.length - 1], 'info');
}

console.log('sysinfo.test.js: ALL PASS');