mirror of
https://github.com/Kpa-clawbot/meshcore-analyzer.git
synced 2026-05-11 17:24:42 +00:00
308b67ed66
## Summary Remove tautological/vacuous frontend tests identified in the test audit (2026-04-30). ## Deleted Files - **`test-anim-perf.js`** — Tests local simulation functions, not actual `live.js` animation code. Behavioral equivalents exist in `test-live-anims.js`. - **`test-channel-add-ux.js`** — Pure HTML/CSS substring grep tests (`src.includes(...)`) with zero behavioral value. Any refactor breaks them without indicating a real bug. ## Edited Files - **`test-aging.js`** — Removed `mockGetStatusInfo` (5 tests) and `getStatusTooltip` (6 tests) blocks. These re-implement SUT logic inside the test file and assert on the local copy, not the real code. The GOLD-rated `getNodeStatus` boundary tests (18 assertions) and the BUG CHECK section are preserved. ## Why These tests are unfailable by construction — they pass regardless of whether the code under test is correct. They inflate test counts without providing regression protection, and they break on harmless refactors (false negatives). ## Validation - `npm run test:unit` passes (all 3 files: 62 + 18 + 601 assertions). - 9 pre-existing failures in `test-frontend-helpers.js` (hop-resolver affinity tests, unrelated to this change). - No harness references (`test-all.sh`, `package.json`) needed updating — deleted files were not listed there. Co-authored-by: you <you@example.com>
79 lines
4.0 KiB
JavaScript
79 lines
4.0 KiB
JavaScript
/* Unit tests for node aging system */
|
|
'use strict';
|
|
const vm = require('vm');
|
|
const fs = require('fs');
|
|
const assert = require('assert');
|
|
|
|
// Load roles.js in a sandboxed context
|
|
const ctx = { window: {}, console, Date, Infinity, document: { readyState: 'complete', createElement: () => ({ id: '' }), head: { appendChild: () => {} }, getElementById: () => null, addEventListener: () => {} }, fetch: () => Promise.resolve({ json: () => Promise.resolve({}) }) };
|
|
vm.createContext(ctx);
|
|
vm.runInContext(fs.readFileSync('public/roles.js', 'utf8'), ctx);
|
|
|
|
// The IIFE assigns to window.*, but the functions reference HEALTH_THRESHOLDS as a bare global
|
|
// In the VM context, window.X doesn't create a global X, so we need to copy them
|
|
for (const k of Object.keys(ctx.window)) {
|
|
ctx[k] = ctx.window[k];
|
|
}
|
|
|
|
const { getNodeStatus, getHealthThresholds, HEALTH_THRESHOLDS } = ctx.window;
|
|
|
|
let passed = 0, failed = 0;
|
|
function test(name, fn) {
|
|
try { fn(); passed++; console.log(` ✅ ${name}`); }
|
|
catch (e) { failed++; console.log(` ❌ ${name}: ${e.message}`); }
|
|
}
|
|
|
|
console.log('\n=== HEALTH_THRESHOLDS ===');
|
|
test('infraSilentMs = 72h (259200000)', () => assert.strictEqual(HEALTH_THRESHOLDS.infraSilentMs, 259200000));
|
|
test('nodeSilentMs = 24h (86400000)', () => assert.strictEqual(HEALTH_THRESHOLDS.nodeSilentMs, 86400000));
|
|
|
|
console.log('\n=== getHealthThresholds ===');
|
|
test('repeater uses infra thresholds', () => {
|
|
const t = getHealthThresholds('repeater');
|
|
assert.strictEqual(t.silentMs, 259200000);
|
|
});
|
|
test('room uses infra thresholds', () => {
|
|
const t = getHealthThresholds('room');
|
|
assert.strictEqual(t.silentMs, 259200000);
|
|
});
|
|
test('companion uses node thresholds', () => {
|
|
const t = getHealthThresholds('companion');
|
|
assert.strictEqual(t.silentMs, 86400000);
|
|
});
|
|
|
|
console.log('\n=== getNodeStatus ===');
|
|
const now = Date.now();
|
|
const h = 3600000;
|
|
|
|
test('repeater seen 1h ago → active', () => assert.strictEqual(getNodeStatus('repeater', now - 1*h), 'active'));
|
|
test('repeater seen 71h ago → active', () => assert.strictEqual(getNodeStatus('repeater', now - 71*h), 'active'));
|
|
test('repeater seen 73h ago → stale', () => assert.strictEqual(getNodeStatus('repeater', now - 73*h), 'stale'));
|
|
test('room seen 73h ago → stale (same as repeater)', () => assert.strictEqual(getNodeStatus('room', now - 73*h), 'stale'));
|
|
test('companion seen 1h ago → active', () => assert.strictEqual(getNodeStatus('companion', now - 1*h), 'active'));
|
|
test('companion seen 23h ago → active', () => assert.strictEqual(getNodeStatus('companion', now - 23*h), 'active'));
|
|
test('companion seen 25h ago → stale', () => assert.strictEqual(getNodeStatus('companion', now - 25*h), 'stale'));
|
|
test('sensor seen 25h ago → stale', () => assert.strictEqual(getNodeStatus('sensor', now - 25*h), 'stale'));
|
|
test('unknown role → uses node (24h) threshold', () => assert.strictEqual(getNodeStatus('unknown', now - 25*h), 'stale'));
|
|
test('unknown role seen 23h ago → active', () => assert.strictEqual(getNodeStatus('unknown', now - 23*h), 'active'));
|
|
test('null lastSeenMs → stale', () => assert.strictEqual(getNodeStatus('repeater', null), 'stale'));
|
|
test('undefined lastSeenMs → stale', () => assert.strictEqual(getNodeStatus('repeater', undefined), 'stale'));
|
|
test('0 lastSeenMs → stale', () => assert.strictEqual(getNodeStatus('repeater', 0), 'stale'));
|
|
|
|
|
|
|
|
// === Bug check: renderRows uses last_seen instead of last_heard || last_seen ===
|
|
console.log('\n=== BUG CHECK ===');
|
|
const nodesJs = fs.readFileSync('public/nodes.js', 'utf8');
|
|
const renderRowsMatch = nodesJs.match(/const status = getNodeStatus\(n\.role[^;]+/);
|
|
if (renderRowsMatch) {
|
|
const line = renderRowsMatch[0];
|
|
console.log(` renderRows status line: ${line}`);
|
|
if (!line.includes('last_heard')) {
|
|
console.log(' 🐛 BUG: renderRows() uses only n.last_seen, ignoring n.last_heard!');
|
|
console.log(' Should be: n.last_heard || n.last_seen');
|
|
}
|
|
}
|
|
|
|
console.log(`\n=== Results: ${passed} passed, ${failed} failed ===\n`);
|
|
process.exit(failed > 0 ? 1 : 0);
|