OverviewFilesCommitsIssuesDocsReleasesPeers

P2P-Hiverelay / test / unit / api-route-mounts.test.js

112 lines · javascript ref:
import test from 'brittle'
import {
  HYPER_GATEWAY_ROUTE_PREFIX,
  INDEX_PROXY_ROOM_ROUTE,
  INDEX_PROXY_ROUTE_PREFIX,
  MANAGEMENT_API_ROUTE_PREFIX,
  OUTBOXLOG_HTTP_EXACT_ROUTES,
  OUTBOXLOG_HTTP_ROUTE_PREFIXES,
  POKER_HTTP_ROUTE,
  POKER_HTTP_ROUTE_PREFIX,
  POKER_TABLE_CREATE_AUTH_MESSAGE,
  POKER_TABLE_CREATE_ROUTE,
  isHyperGatewayRoute,
  isIndexProxyRoute,
  isManagementApiRoute,
  isOutboxLogHttpReadRequest,
  isOutboxLogHttpRoute,
  isPokerHttpRoute,
  resolvePokerHttpRoutePolicy
} from '../../packages/core/core/relay-node/api-route-mounts.js'

test('api route mounts: hyper gateway predicate stays bound to RelayKernel route prefix', (t) => {
  t.is(HYPER_GATEWAY_ROUTE_PREFIX, '/v1/hyper/')
  t.is(isHyperGatewayRoute('/v1/hyper/drive-key/index.html'), true)
  t.is(isHyperGatewayRoute('/v1/hyper'), false)
  t.is(isHyperGatewayRoute('/v1/hyperion/drive-key'), false)
  t.is(isHyperGatewayRoute(null), false)
})

test('api route mounts: index sidecar proxy remains read-only', (t) => {
  t.is(INDEX_PROXY_ROOM_ROUTE, '/api/index/room')
  t.is(INDEX_PROXY_ROUTE_PREFIX, '/index/')
  t.is(isIndexProxyRoute('GET', '/api/index/room'), true)
  t.is(isIndexProxyRoute('GET', '/index/rooms/demo'), true)
  t.is(isIndexProxyRoute('POST', '/api/index/room'), false)
  t.is(isIndexProxyRoute('GET', '/index'), false)
  t.is(isIndexProxyRoute('GET', '/api/index/room/extra'), false)
})

test('api route mounts: poker substrate keeps exact root plus subtree', (t) => {
  t.is(POKER_HTTP_ROUTE, '/api/poker')
  t.is(POKER_HTTP_ROUTE_PREFIX, '/api/poker/')
  t.is(isPokerHttpRoute('/api/poker'), true)
  t.is(isPokerHttpRoute('/api/poker/tables'), true)
  t.is(isPokerHttpRoute('/api/poker/usage'), true)
  t.is(isPokerHttpRoute('/api/pokerish'), false)
  t.is(isPokerHttpRoute('/api/poker-tables'), false)
})

test('api route mounts: outboxlog bridge owns exact Peerit /api routes only', (t) => {
  t.alike(OUTBOXLOG_HTTP_EXACT_ROUTES, ['/api/token', '/api/bridge/status', '/api/directory', '/api/identity', '/api/admin/takedown', '/api/admin/restore', '/api/admin/takedowns'])
  t.alike(OUTBOXLOG_HTTP_ROUTE_PREFIXES, ['/api/identity/', '/api/sync/', '/api/swarm/'])
  t.is(isOutboxLogHttpRoute('/api/token'), true)
  t.is(isOutboxLogHttpRoute('/api/bridge/status'), true)
  t.is(isOutboxLogHttpRoute('/api/directory'), true)
  t.is(isOutboxLogHttpRoute('/api/identity'), true)
  t.is(isOutboxLogHttpRoute('/api/identity/sign'), true)
  t.is(isOutboxLogHttpRoute('/api/sync/create'), true)
  t.is(isOutboxLogHttpRoute('/api/swarm/events'), true)
  // Takedown admin surface routes into the outboxlog dispatch (adapter gates it)
  // as EXACTLY these three routes — outboxlog does not reserve the whole
  // /api/admin/* namespace.
  t.is(isOutboxLogHttpRoute('/api/admin/takedown'), true)
  t.is(isOutboxLogHttpRoute('/api/admin/restore'), true)
  t.is(isOutboxLogHttpRoute('/api/admin/takedowns'), true)
  t.is(isOutboxLogHttpRoute('/api/tokens'), false)
  t.is(isOutboxLogHttpRoute('/api/identityish'), false)
  t.is(isOutboxLogHttpRoute('/api/identity-sign'), false)
  t.is(isOutboxLogHttpRoute('/api/synchronize'), false)
  t.is(isOutboxLogHttpRoute('/api/admin'), false)
  // Namespace exclusivity: a future non-outboxlog /api/admin/<x> route must NOT
  // be swallowed by the outboxlog dispatch (it falls through to generic 404).
  t.is(isOutboxLogHttpRoute('/api/admin/other'), false)
  t.is(isOutboxLogHttpRoute('/api/admin/takedown/extra'), false)
  t.is(isOutboxLogHttpRoute('/api/poker/tables'), false)
})

test('api route mounts: poker create-table mutation is the only auth policy override', (t) => {
  t.is(POKER_TABLE_CREATE_ROUTE, '/api/poker/tables')
  t.is(POKER_TABLE_CREATE_AUTH_MESSAGE, 'Unauthorized — API key required to create a poker table')
  t.alike(resolvePokerHttpRoutePolicy('POST', '/api/poker/tables'), {
    kind: 'poker-table-create',
    authRequired: true,
    authMessage: POKER_TABLE_CREATE_AUTH_MESSAGE
  })
  t.is(resolvePokerHttpRoutePolicy('GET', '/api/poker/tables'), null)
  t.is(resolvePokerHttpRoutePolicy('POST', '/api/poker/tables/'), null)
  t.is(resolvePokerHttpRoutePolicy('POST', '/api/poker/aa/move'), null)
  t.is(resolvePokerHttpRoutePolicy('POST', '/api/pokerish/tables'), null)
})

test('api route mounts: management API predicate covers control-plane subtree only', (t) => {
  t.is(MANAGEMENT_API_ROUTE_PREFIX, '/api/manage/')
  t.is(isManagementApiRoute('/api/manage/config'), true)
  t.is(isManagementApiRoute('/api/manage/services/config'), true)
  t.is(isManagementApiRoute('/api/manage'), false)
  t.is(isManagementApiRoute('/api/management/config'), false)
  t.is(isManagementApiRoute(undefined), false)
})

test('api route mounts: outboxlog read-request classifier (global rate-limit exemption)', (t) => {
  t.ok(isOutboxLogHttpReadRequest('GET', '/api/directory'))
  t.ok(isOutboxLogHttpReadRequest('GET', '/api/sync/range'))
  t.ok(isOutboxLogHttpReadRequest('GET', '/api/sync/events'))
  t.ok(isOutboxLogHttpReadRequest('POST', '/api/sync/heads'), 'heads is POST-shaped but a read')
  t.absent(isOutboxLogHttpReadRequest('POST', '/api/token'), 'token issuance is not a read')
  t.absent(isOutboxLogHttpReadRequest('POST', '/api/sync/append'), 'writes stay under the global budget')
  t.absent(isOutboxLogHttpReadRequest('POST', '/api/swarm/join'))
  t.absent(isOutboxLogHttpReadRequest('GET', '/api/admin/takedowns'), 'admin surface is never exempt')
  t.absent(isOutboxLogHttpReadRequest('POST', '/api/directory'), 'wrong method is not exempt')
})