OverviewFilesCommitsIssuesDocsReleasesPeers

P2P-Hiverelay / test / unit / api-poker-usage.test.js

109 lines · javascript ref:
import test from 'brittle'
import http from 'http'
import { RelayAPI } from 'p2p-hiverelay/core/relay-node/api.js'

const API_KEY = 'poker-usage-test-key'

// Regression guard for the release-image smoke gate: GET /api/poker/usage on a
// relay with NO poker service must answer 200 { enabled: false, ... } — "poker
// isn't enabled" is a valid telemetry answer, not an error. The 503 this used
// to return kept every Release surfaces run red from v0.24.0 through v0.24.3.

function mockNode (opts = {}) {
  return {
    running: true,
    config: { storage: null, plugins: opts.plugins || [], trustProxy: true },
    metrics: { getSummary () { return { uptime: 1 } } },
    seededApps: new Map(),
    appRegistry: { apps: new Map(), catalog () { return [] }, catalogForBroadcast () { return [] } },
    getStats () { return { running: true } },
    getHealthStatus () { return { healthy: true } },
    serviceRegistry: opts.registry || null,
    async stop () {},
    async start () {},
    on () {},
    emit () {}
  }
}

async function serverWithApi (t, node, opts = {}) {
  const api = new RelayAPI(node, { apiPort: 0, apiHost: '127.0.0.1', apiKey: API_KEY, ...opts })
  await api.start()
  const port = api.server.address().port
  t.teardown(async () => {
    if (api._rateLimitCleanup) clearInterval(api._rateLimitCleanup)
    if (api._dashboardFeed) { try { api._dashboardFeed.stop() } catch {} }
    if (api._pokerFeed) { try { api._pokerFeed.stop() } catch {} }
    await new Promise((resolve) => api.server.close(resolve))
  })
  return { api, port }
}

function request (port, method, path, body, headers = {}) {
  return new Promise((resolve, reject) => {
    const req = http.request({
      hostname: '127.0.0.1',
      port,
      method,
      path,
      headers: { 'Content-Type': 'application/json', ...headers }
    }, (res) => {
      let data = ''
      res.on('data', (chunk) => { data += chunk })
      res.on('end', () => {
        let parsed
        try { parsed = JSON.parse(data) } catch { parsed = data }
        resolve({ statusCode: res.statusCode, body: parsed, headers: res.headers })
      })
    })
    req.on('error', reject)
    if (body != null) req.write(JSON.stringify(body))
    req.end()
  })
}

test('/api/poker/usage answers enabled:false (200) when poker is not running', async (t) => {
  const { port } = await serverWithApi(t, mockNode({ registry: null }))
  const res = await request(port, 'GET', '/api/poker/usage', null, {
    Authorization: `Bearer ${API_KEY}`
  })

  t.is(res.statusCode, 200, 'a stock (no-services) relay answers 200, not 503')
  t.is(res.body.enabled, false)
  t.is(res.body.service, 'poker')
  t.is(res.body.tables, 0)
  t.is(res.body.appends, 0)
  t.is(res.body.seats, 0)
  t.alike(res.body.perTable, [])
})

test('/api/poker/usage still requires auth', async (t) => {
  const { port } = await serverWithApi(t, mockNode({ registry: null }))
  const res = await request(port, 'GET', '/api/poker/usage')
  t.is(res.statusCode, 401)
})

test('/api/poker/usage reports live counts when a poker provider is running', async (t) => {
  const pokerProvider = {
    listTables () {
      return [
        { tableKey: 'a'.repeat(64), length: 5, writers: ['w1', 'w2'], lastTs: 1751900000000 }
      ]
    }
  }
  const registry = {
    get (name) { return name === 'poker' ? { provider: pokerProvider, status: 'running' } : null },
    services: new Map([['poker', { provider: pokerProvider, status: 'running' }]])
  }
  const { port } = await serverWithApi(t, mockNode({ registry }))
  const res = await request(port, 'GET', '/api/poker/usage', null, {
    Authorization: `Bearer ${API_KEY}`
  })

  t.is(res.statusCode, 200)
  t.is(res.body.enabled, true)
  t.is(res.body.tables, 1)
  t.is(res.body.appends, 5)
  t.is(res.body.seats, 2)
})