OverviewFilesCommitsIssuesDocsReleasesPeers

ap-bridge / test / admin-serve.test.js

71 lines · javascript ref:
// The admin surface with a real port bound — the half admin.test.js deliberately cannot reach.
//
// ⛔ admin.js is pure and returns `{status, headers, body}`, which makes the ROUTING testable with no
// socket. That leaves the wiring — bare-http1, the form parser, the loopback bind — as the part that
// only fails in production. It is exactly the shape of thing that passes every unit test and then does
// not start, so it gets a bound port here.

import test from 'brittle'
import http from 'bare-http1'
import { adminRoutes } from '../lib/admin.js'
import { parseForm } from '../lib/admin-serve.js'
import { MemoryRegistrations } from '../lib/registrations.js'

const ROOT = 'a'.repeat(64)

async function serve (registrations, config) {
  const server = http.createServer((req, res) => {
    parseForm(req)
      .then((form) => adminRoutes({ method: req.method, path: (req.url || '/').split('?')[0], form },
        { registrations, inbound: null, config }))
      .then((out) => { res.writeHead(out.status, out.headers); res.end(out.body) })
      .catch((err) => { res.writeHead(500, {}); res.end(String(err && err.message)) })
  })
  await new Promise((resolve) => server.listen(0, '127.0.0.1', resolve))
  return { server, port: server.address().port }
}

const fetchText = (port, path, opts = {}) => new Promise((resolve, reject) => {
  const req = http.request({ host: '127.0.0.1', port, path, method: opts.method || 'GET', headers: opts.headers || {} },
    (res) => {
      let body = ''
      res.on('data', (c) => { body += c })
      res.on('end', () => resolve({ status: res.statusCode, headers: res.headers, body }))
    })
  req.on('error', reject)
  if (opts.body) req.write(opts.body)
  req.end()
})

test('★★★ it binds loopback and serves the overview', async (t) => {
  const registrations = new MemoryRegistrations({ policy: 'open' })
  await registrations.claim({ username: 'fleeky', root: ROOT, feedKey: 'd'.repeat(64) })

  const { server, port } = await serve(registrations, { host: 'ap.7rnx.net', registration: 'open', inboundCap: 500 })
  t.teardown(() => new Promise((r) => server.close(r)))

  const res = await fetchText(port, '/admin')
  t.is(res.status, 200)
  t.ok(/fleeky@ap\.7rnx\.net/.test(res.body), 'the operator can see who holds a name')
})

test('★★★ a posted form reaches the handler and takes effect', async (t) => {
  // ⛔ THE PARSER IS THE POINT. A release that silently parsed no username would 303 back to a page
  // that still lists the name, which reads as "the button does nothing" — the failure mode this repo
  // has now hit twice on other surfaces.
  const registrations = new MemoryRegistrations({ policy: 'open' })
  await registrations.claim({ username: 'squatted', root: ROOT })

  const { server, port } = await serve(registrations, { host: 'ap.7rnx.net', registration: 'open', inboundCap: 500 })
  t.teardown(() => new Promise((r) => server.close(r)))

  const body = 'username=squatted'
  const res = await fetchText(port, '/admin/release', {
    method: 'POST',
    headers: { 'content-type': 'application/x-www-form-urlencoded', 'content-length': String(body.length) },
    body
  })
  t.is(res.status, 303, 'redirected back')
  t.absent(await registrations.get('squatted'), 'and the name really was released')
})