OverviewFilesCommitsIssuesDocsReleasesPeers

cthuloid / webui / test / unit.test.js

119 lines · javascript ref:
import test from 'brittle'
import { Unit } from '../lib/unit.js'
import { FakeUnit, waitFor, sleep } from './helpers.js'

const DEAD = 'cthuloid-test-nonexistent.local'

function unitFor (fake, opts = {}) {
  return new Unit({ id: fake.name, host: '127.0.0.1', port: fake.port, backoffStartMs: 100, ...opts })
}

test('open connects, reads the greeting, delivers the desired state', async (t) => {
  const fake = await new FakeUnit('alpha').listen()
  const u = unitFor(fake)
  u.set('pwm 1\n')
  u.open()
  t.ok(await waitFor(() => fake.lines.includes('pwm 1')), 'payload delivered')
  t.is(u.status, 'connected')
  t.is(u.greetingName, 'alpha')
  u.close()
  await fake.close()
})

test('a burst of sets coalesces to the latest, and repeats are skipped', async (t) => {
  const fake = await new FakeUnit().listen()
  const u = unitFor(fake, { minIntervalMs: 40 })
  u.open()
  await waitFor(() => u.status === 'connected')
  for (let i = 0; i < 50; i++) u.set('pwm ' + i + '\n')
  t.ok(await waitFor(() => fake.lines.includes('pwm 49')), 'latest arrives')
  const pwm = fake.lines.filter((l) => l.startsWith('pwm'))
  t.ok(pwm.length <= 3, 'intermediate frames dropped, sent ' + pwm.length)
  u.set('pwm 49\n')
  await sleep(150)
  t.is(fake.lines.filter((l) => l === 'pwm 49').length, 1, 'identical frame not resent')
  u.close()
  await fake.close()
})

test('a dead unit never delays a live one', async (t) => {
  const fake = await new FakeUnit().listen()
  const dead = new Unit({ id: 'dead', host: DEAD, port: 1337 })
  const live = unitFor(fake)
  dead.set('pwm 0\n')
  dead.open()
  live.open()
  await waitFor(() => live.status === 'connected')
  const t0 = Date.now()
  live.set('pwm 7\n')
  t.ok(await waitFor(() => fake.lines.includes('pwm 7'), 500), 'live unit served while dead one resolves')
  t.ok(Date.now() - t0 < 500, 'within ' + (Date.now() - t0) + ' ms')
  dead.close()
  live.close()
  await fake.close()
})

test('a dropped connection reconnects and re-asserts the look', async (t) => {
  const fake = await new FakeUnit().listen()
  const u = unitFor(fake)
  u.set('pwm 3\n')
  u.open()
  await waitFor(() => fake.lines.includes('pwm 3'))
  fake.lines.length = 0
  fake.dropClients()
  t.ok(await waitFor(() => fake.lines.includes('pwm 3'), 3000), 're-asserted after reconnect')
  t.is(fake.accepted, 2)
  u.close()
  await fake.close()
})

test('close() disconnects and stops retrying', async (t) => {
  const fake = await new FakeUnit().listen()
  const u = unitFor(fake)
  u.open()
  await waitFor(() => fake.conns.size === 1)
  u.close()
  t.ok(await waitFor(() => fake.conns.size === 0), 'socket closed')
  await sleep(400)
  t.is(fake.accepted, 1, 'no reconnect after close')
  t.is(u.status, 'off')
  await fake.close()
})

test('an unreachable unit reports it and keeps retrying', async (t) => {
  const fake = await new FakeUnit().listen()
  const port = fake.port
  await fake.close()
  const u = new Unit({ id: 'gone', host: '127.0.0.1', port, backoffStartMs: 100 })
  u.open()
  t.ok(await waitFor(() => u.status === 'unreachable'), 'reported unreachable')
  u.close()
})

test('sendOnce delivers without staying connected', async (t) => {
  const fake = await new FakeUnit().listen()
  const u = unitFor(fake)
  u.sendOnce('stopfade\npwm 0\n')
  t.ok(await waitFor(() => fake.lines.includes('pwm 0')), 'delivered')
  t.ok(await waitFor(() => fake.conns.size === 0), 'connection released')
  await sleep(300)
  t.is(fake.accepted, 1, 'never reconnects')
  await fake.close()
})

test('state readback is parsed and emitted', async (t) => {
  const fake = await new FakeUnit().listen()
  fake.values[2] = 30000
  const u = unitFor(fake)
  let got = null
  u.on('state', (s) => { got = s })
  u.open()
  await waitFor(() => u.status === 'connected')
  u.requestState()
  t.ok(await waitFor(() => got !== null), 'state emitted')
  t.is(got.values[2], 30000)
  t.alike(u.actual.values, got.values, 'kept as unit.actual')
  u.close()
  await fake.close()
})