Update btop
This commit is contained in:
@@ -17,6 +17,10 @@ test('baretop-tui uses alternate screen teardown pattern', async (t) => {
|
||||
(src.match(/\\x1b\[2J\\x1b\[H/g) || []).length >= 1,
|
||||
'expected full clear fallback in finally'
|
||||
)
|
||||
t.ok(
|
||||
src.includes('incrementalFrameDiff'),
|
||||
'expected incremental frame diff gate in tui source'
|
||||
)
|
||||
})
|
||||
|
||||
test('shipped kernel/bin/baretop includes snapshot + TUI', async (t) => {
|
||||
|
||||
@@ -19,6 +19,10 @@ test('fixture metrics_live-shaped JSON parses with expected keys', async (t) =>
|
||||
t.ok(typeof o.delegateInflight === 'object')
|
||||
t.ok(o.replicationLive && typeof o.replicationLive === 'object')
|
||||
t.ok(o.kernelCounters && typeof o.kernelCounters === 'object')
|
||||
t.ok(
|
||||
o.netSummaryNestedExample &&
|
||||
typeof o.netSummaryNestedExample.replicationQueue === 'object'
|
||||
)
|
||||
})
|
||||
|
||||
test('baretop-snapshot preamble lists batch proc entries', async (t) => {
|
||||
@@ -29,6 +33,7 @@ test('baretop-snapshot preamble lists batch proc entries', async (t) => {
|
||||
t.ok(src.includes('bareTopHealthScore'))
|
||||
t.ok(src.includes('BARE_TOP_SNAPSHOT_LITE_ENTRIES'))
|
||||
t.ok(src.includes('bootBudgetSummary'))
|
||||
t.ok(src.includes('security_posture.json'))
|
||||
})
|
||||
|
||||
test('bareTopHealthScore penalizes stall and inflight cardinality', async (t) => {
|
||||
@@ -49,6 +54,44 @@ test('bareTopHealthScore penalizes stall and inflight cardinality', async (t) =>
|
||||
t.is(ctx.bareTopHealthScore(good, 2), 100)
|
||||
})
|
||||
|
||||
test('bareTopHealthScore: no_peers + idle delegates is not stuck at 73', async (t) => {
|
||||
const src = await readFile(snapPath, 'utf8')
|
||||
const ctx = createContext({})
|
||||
runInContext(src, ctx)
|
||||
const idle = {
|
||||
peers: 0,
|
||||
delegateInflight: {},
|
||||
replicationLive: { peerCount: 0, stallHint: 'no_peers' }
|
||||
}
|
||||
t.is(ctx.bareTopHealthScore(idle, 0), 100)
|
||||
const lightInflight = {
|
||||
peers: 0,
|
||||
delegateInflight: { http: 1, dns: 0 },
|
||||
replicationLive: { peerCount: 0, stallHint: 'no_peers' }
|
||||
}
|
||||
const s2 = ctx.bareTopHealthScore(lightInflight, 0)
|
||||
t.ok(s2 >= 90)
|
||||
t.not(s2, 73)
|
||||
})
|
||||
|
||||
test('bareTopHealthScoreDetail: benign stall hints and replicationStall flag', async (t) => {
|
||||
const src = await readFile(snapPath, 'utf8')
|
||||
const ctx = createContext({})
|
||||
runInContext(src, ctx)
|
||||
const lenOk = {
|
||||
peers: 0,
|
||||
replicationLive: { stallHint: 'length_unavailable', peerCount: 0 }
|
||||
}
|
||||
t.is(ctx.bareTopHealthScore(lenOk, 0), 100)
|
||||
const explicit = {
|
||||
peers: 1,
|
||||
replicationLive: { stallHint: 'ok', peerCount: 1, replicationStall: true }
|
||||
}
|
||||
t.is(ctx.bareTopHealthScore(explicit, 1), 80)
|
||||
const d = ctx.bareTopHealthScoreDetail(explicit, 1, null)
|
||||
t.ok(String(d.breakdown).includes('replication_stall_flag'))
|
||||
})
|
||||
|
||||
test('process_table fixture maps to sorted PID rows via helpers', async (t) => {
|
||||
const raw = await readFile(processTableFixture, 'utf8')
|
||||
const pt = JSON.parse(raw)
|
||||
|
||||
@@ -0,0 +1,39 @@
|
||||
import test from 'brittle'
|
||||
|
||||
/**
|
||||
* Mirrors baretop-tui incrementalFrameDiff patch builder (line CUP + EL + line).
|
||||
* @param {string[]} prevLines
|
||||
* @param {string[]} nextLines
|
||||
*/
|
||||
function bareTopLineDiffPatch(prevLines, nextLines) {
|
||||
let patch = '\x1b[?25l'
|
||||
let nch = 0
|
||||
for (let i = 0; i < nextLines.length; i++) {
|
||||
if (prevLines[i] !== nextLines[i]) {
|
||||
nch++
|
||||
const r = Math.max(1, Math.min(Math.floor(i + 1), 9999))
|
||||
const c = 1
|
||||
patch += '\x1b[' + r + ';' + c + 'H' + '\x1b[K' + nextLines[i] + '\r\n'
|
||||
}
|
||||
}
|
||||
patch += '\x1b[?25h'
|
||||
return { patch, nch }
|
||||
}
|
||||
|
||||
test('incremental line-diff patch is smaller than full frame when one line changes', async (t) => {
|
||||
const n = 48
|
||||
const prev = Array.from({ length: n }, (_, i) => 'row-' + i + ' ' + 'x'.repeat(40))
|
||||
const next = prev.slice()
|
||||
next[22] = 'row-22 ' + 'CHANGED'.repeat(6)
|
||||
const full = next.join('\r\n') + '\r\n'
|
||||
const { patch, nch } = bareTopLineDiffPatch(prev, next)
|
||||
t.is(nch, 1)
|
||||
t.ok(
|
||||
patch.length < full.length,
|
||||
'expected patch bytes < full frame (' +
|
||||
patch.length +
|
||||
' vs ' +
|
||||
full.length +
|
||||
')'
|
||||
)
|
||||
})
|
||||
@@ -120,6 +120,91 @@ test('bareTopOverviewLines keeps ASCII section lines within cols', async (t) =>
|
||||
}
|
||||
})
|
||||
|
||||
test('bareTopNetTabLines sorts interfaces by rx+tx', async (t) => {
|
||||
const code = await readFile(helpersPath, 'utf8')
|
||||
const ctx = createContext({})
|
||||
runInContext(code, ctx)
|
||||
const lines = ctx.bareTopNetTabLines(
|
||||
{
|
||||
interfaces: [
|
||||
{ name: 'lo', rxBytes: 1, txBytes: 1 },
|
||||
{ name: 'eth0', rxBytes: 100, txBytes: 50 }
|
||||
]
|
||||
},
|
||||
100
|
||||
)
|
||||
t.ok(lines.some((l) => l.includes('interfaces')))
|
||||
t.ok(lines.some((l) => l.includes('eth0')))
|
||||
})
|
||||
|
||||
test('bareTopNetTabLines expands nested objects (no [object Object])', async (t) => {
|
||||
const code = await readFile(helpersPath, 'utf8')
|
||||
const ctx = createContext({})
|
||||
runInContext(code, ctx)
|
||||
const raw = await readFile(
|
||||
join(__dirname, 'fixtures/baretop-net-summary-nested.json'),
|
||||
'utf8'
|
||||
)
|
||||
const net = JSON.parse(raw)
|
||||
const lines = ctx.bareTopNetTabLines(net, 100, { maxLines: 200 })
|
||||
const blob = lines.join('\n')
|
||||
t.ok(!blob.includes('[object Object]'), 'should not stringify objects blindly')
|
||||
t.ok(blob.includes('replicationQueue'), 'section for queue')
|
||||
t.ok(blob.includes('peerFirewallStats'), 'section for firewall')
|
||||
t.ok(blob.includes('bsdSocketGuestBridge'), 'section for bridge')
|
||||
t.ok(blob.includes('acceptedSessionCount') || blob.includes('10'), 'firewall scalar')
|
||||
})
|
||||
|
||||
test('bareTopNetTabLines respects maxLines cap', async (t) => {
|
||||
const code = await readFile(helpersPath, 'utf8')
|
||||
const ctx = createContext({})
|
||||
runInContext(code, ctx)
|
||||
const big = { a: 1 }
|
||||
for (let i = 0; i < 80; i++) big['k' + i] = i
|
||||
const lines = ctx.bareTopNetTabLines(big, 80, { maxLines: 12 })
|
||||
t.ok(lines.length <= 12)
|
||||
t.ok(lines[lines.length - 1].includes('truncated'))
|
||||
})
|
||||
|
||||
test('bareTopSortProcessRows supports nice and cpu keys', async (t) => {
|
||||
const code = await readFile(helpersPath, 'utf8')
|
||||
const ctx = createContext({})
|
||||
runInContext(code, ctx)
|
||||
const rows = [
|
||||
{ pid: 1, name: 'a', nice: 0 },
|
||||
{ pid: 2, name: 'b', nice: 5 }
|
||||
]
|
||||
const byNice = ctx.bareTopSortProcessRows(rows, 'nice', true)
|
||||
t.is(bareTopProcessPidLocal(byNice[0]), 1)
|
||||
const rows2 = [
|
||||
{ pid: 1, name: 'a', cpuPct: 10 },
|
||||
{ pid: 2, name: 'b', cpuPct: 99 }
|
||||
]
|
||||
const byCpu = ctx.bareTopSortProcessRows(rows2, 'cpu', false)
|
||||
t.is(bareTopProcessPidLocal(byCpu[0]), 2)
|
||||
})
|
||||
|
||||
test('bareTopLimitsMergedLines flattens quotas and rlimits', async (t) => {
|
||||
const code = await readFile(helpersPath, 'utf8')
|
||||
const ctx = createContext({})
|
||||
runInContext(code, ctx)
|
||||
const snap = {
|
||||
extra: { quotas: { pipe: 8 }, rlimits: { nofile: 1024 } }
|
||||
}
|
||||
const lines = ctx.bareTopLimitsMergedLines(snap)
|
||||
t.ok(lines.some((l) => l.includes('pipe=8')))
|
||||
t.ok(lines.some((l) => l.includes('nofile=1024')))
|
||||
})
|
||||
|
||||
test('bareTopDelegateInflightTableLines right-aligns counts', async (t) => {
|
||||
const code = await readFile(helpersPath, 'utf8')
|
||||
const ctx = createContext({})
|
||||
runInContext(code, ctx)
|
||||
const lines = ctx.bareTopDelegateInflightTableLines({ a: 3, b: 12 }, 5, 8)
|
||||
t.ok(lines[0].includes('b') && lines[0].includes('12'))
|
||||
t.ok(lines[1].includes('a') && lines[1].includes('3'))
|
||||
})
|
||||
|
||||
test('bareTopParseMeminfoMetrics derives MemTotal and MemAvailable', async (t) => {
|
||||
const code = await readFile(helpersPath, 'utf8')
|
||||
const ctx = createContext({})
|
||||
|
||||
@@ -0,0 +1,27 @@
|
||||
{
|
||||
"schema": 1,
|
||||
"schemaVersion": 2,
|
||||
"topicHex": "deadbeef",
|
||||
"peerCount": 3,
|
||||
"seedHandshakeError": null,
|
||||
"replicationQueueDepth": 2,
|
||||
"replicationQueue": {
|
||||
"depth": 2,
|
||||
"pending": 1,
|
||||
"items": [{ "id": "a" }]
|
||||
},
|
||||
"peerFirewallStats": {
|
||||
"acceptedSessionCount": 10,
|
||||
"rejectedSessionCount": 1,
|
||||
"inboundSessionCount": 4,
|
||||
"outboundSessionCount": 6,
|
||||
"wave9": { "x": 1 }
|
||||
},
|
||||
"bsdSocketGuestBridge": {
|
||||
"schema": 1,
|
||||
"policyEnv": "BARE_OS_BSD_SOCKET_POLICY",
|
||||
"note": "test bridge",
|
||||
"activeBridgedFds": 0
|
||||
},
|
||||
"atMs": 1700000000000
|
||||
}
|
||||
@@ -28,5 +28,27 @@
|
||||
"swarmLifecycle": { "phase": "steady" },
|
||||
"initdReadiness": { "failed": 0, "starting": 1, "active": 4 },
|
||||
"workerBudgetHints": { "wallMsMax": 60000 },
|
||||
"processTable": { "schema": 7, "rows": [] }
|
||||
"processTable": { "schema": 7, "rows": [] },
|
||||
"netSummaryNestedExample": {
|
||||
"schemaVersion": 2,
|
||||
"topicHex": "deadbeef",
|
||||
"peerCount": 2,
|
||||
"seedHandshakeError": "",
|
||||
"replicationQueueDepth": 4,
|
||||
"peerFirewallBlockedSessions": 0,
|
||||
"peerFirewallAllowedSessions": 12,
|
||||
"replicationQueue": {
|
||||
"depth": 4,
|
||||
"lastError": null,
|
||||
"batches": [{ "id": 1, "state": "pending" }]
|
||||
},
|
||||
"peerFirewallStats": {
|
||||
"sessions": { "blocked": 0, "allowed": 12 }
|
||||
},
|
||||
"bsdSocketGuestBridge": {
|
||||
"schema": 1,
|
||||
"policyEnv": "default",
|
||||
"note": "fixture"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user