OverviewFilesCommitsIssuesDocsReleasesPeers

P2P-Hiverelay / test / unit / health-monitor.test.js

125 lines · javascript ref:
import test from 'brittle'
import { HealthMonitor } from 'p2p-hiverelay/core/relay-node/health-monitor.js'

function createMockNode (opts = {}) {
  const connList = opts.connections || []
  const connMap = new Map()
  for (const c of connList) {
    connMap.set(c, { lastActivity: Date.now() })
  }
  return {
    running: opts.running !== undefined ? opts.running : true,
    swarm: opts.swarm || {
      destroyed: false,
      connections: new Set(connList)
    },
    connections: connMap,
    metrics: { _errorCount: opts.errorCount || 0 },
    config: { storage: '/tmp' }
  }
}

test('HealthMonitor - reports healthy when everything is fine', async (t) => {
  const node = createMockNode({ connections: ['a', 'b'] })
  const hm = new HealthMonitor(node, { checkInterval: 999999 })

  await hm._check()
  const status = hm.getStatus()
  t.is(status.healthy, true)
  t.is(status.checks.memory.ok, true)
  t.is(status.checks.swarm.ok, true)
})

test('HealthMonitor - detects destroyed swarm', async (t) => {
  const node = createMockNode()
  node.swarm.destroyed = true

  const hm = new HealthMonitor(node, { checkInterval: 999999 })

  let criticalFired = false
  hm.on('health-critical', () => { criticalFired = true })

  await hm._check()
  const status = hm.getStatus()
  t.is(status.healthy, false)
  t.is(status.checks.swarm.ok, false)
  t.is(criticalFired, true)
})

test('HealthMonitor - detects zero connections after threshold', async (t) => {
  const node = createMockNode({ connections: [] })
  const hm = new HealthMonitor(node, {
    checkInterval: 999999,
    zeroConnectionsThreshold: -1 // trigger immediately
  })

  let warningFired = false
  hm.on('health-warning', (d) => {
    if (d.check === 'connections') warningFired = true
  })

  // First check sets _zeroConnectionsSince
  await hm._check()
  // Second check triggers warning (threshold = 0)
  await hm._check()

  t.is(warningFired, true)
})

test('HealthMonitor - log buffer works', async (t) => {
  const node = createMockNode()
  const hm = new HealthMonitor(node, { checkInterval: 999999 })

  hm.log('info', 'test', 'hello')
  hm.log('error', 'test', 'boom')
  hm.log('info', 'other', 'world')

  const all = hm.getLogs()
  t.is(all.length, 3)

  const errors = hm.getLogs({ level: 'error' })
  t.is(errors.length, 1)
  t.is(errors[0].message, 'boom')

  const testLogs = hm.getLogs({ component: 'test' })
  t.is(testLogs.length, 2)

  const limited = hm.getLogs({ limit: 1 })
  t.is(limited.length, 1)
})

test('HealthMonitor - alert cooldown prevents spam', async (t) => {
  const node = createMockNode()
  node.swarm.destroyed = true

  const hm = new HealthMonitor(node, {
    checkInterval: 999999,
    alertCooldownMs: 60_000
  })

  let alertCount = 0
  hm.on('alert', () => { alertCount++ })

  await hm._check()
  await hm._check()
  await hm._check()

  // Should only fire once due to cooldown
  t.is(alertCount, 1, 'alert fired only once due to cooldown')
})

test('HealthMonitor - disk check with valid path', async (t) => {
  const node = createMockNode()
  const hm = new HealthMonitor(node, {
    checkInterval: 999999,
    diskCheckPath: '/tmp',
    maxDiskUsagePct: 99.9 // Should be ok
  })

  await hm._check()
  const status = hm.getStatus()
  t.is(status.checks.disk.ok, true)
  t.ok(typeof status.checks.disk.usedPct === 'number')
  t.ok(typeof status.checks.disk.freeGB === 'number')
})