OverviewFilesCommitsIssuesDocsReleasesPeers

keet-daemon / test / probe-router-room-live.js

120 lines · javascript ref:
// Probe (room-state P3 — LIVE router wiring): verify router._stateBaseProviders
// against a REAL keet daemon with a REAL keet room. Single daemon = creator
// path. Confirms the integration the in-process tests (injected providers)
// couldn't: getMembers from a real room, selfKey from getIdentity, and attest
// via real keet-core, all wired through router._getStateBase → StateBase.
//
// Run: bare test/probe-router-room-live.js   (slow — keet-core boot + room create)
// PASS = providers resolve correctly AND state.set mints a writable gamestate
//        base whose write reads back.

import fs from 'bare-fs'
import path from 'bare-path'
import os from 'bare-os'
import { startDaemon } from '../index.js'

const baseDir = path.join(os.tmpdir(), 'probe-router-room-' + Date.now())
fs.mkdirSync(baseDir, { recursive: true })
const socketPath = path.join(baseDir, 'daemon.sock')

let exitCode = 1
let daemon = null

const sleep = (ms) => new Promise(r => setTimeout(r, ms))

try {
  console.log('[live] startDaemon + vault.setup…')
  daemon = await startDaemon({ storage: baseDir, socketPath })
  const router = daemon.router
  await router._vaultSetup({ password: 'probe-router-pw' })

  console.log('[live] create keet room…')
  const room = await router._roomCreate({ title: 'gamestate-e2e-' + Math.random().toString(36).slice(2, 8) })
  const roomId = typeof room === 'string' ? room : (room && (room.roomId || room.id || room.key))
  console.log('[live] roomId:', roomId)
  if (!roomId) throw new Error('no roomId from createRoom: ' + JSON.stringify(room))

  // members can take a beat to materialize after create
  let members = []
  for (let i = 0; i < 20; i++) {
    members = await router._members({ roomId }).catch(() => [])
    if (members.length) break
    await sleep(1000)
  }
  console.log('[live] members:', JSON.stringify(members))

  // confirm the room's shared secret is reachable (drives deterministic v2 key)
  try {
    const rk = await router.core.backendAPI.api.core.getRoomKeys(roomId)
    console.log('[live] getRoomKeys fields:', rk ? Object.keys(rk).join(',') : null)
  } catch (e) { console.log('[live] getRoomKeys err:', e.message) }
  // verify the members-only encryptionKey accessor (for base encryption, like keet)
  try {
    const idEnc = (await import('hypercore-id-encoding')).default
    const rmKey = idEnc.decode(roomId)
    const rm = router.core.internalOpenExistingRoom(rmKey)
    const ek = rm && rm.encryptionKey
    console.log('[live] room.encryptionKey:', ek ? ('len=' + ek.length + ' ' + Buffer.from(ek).toString('hex').slice(0, 12) + '…') : ek)
    try { router.core.internalReleaseRoom(rm) } catch {}
  } catch (e) { console.log('[live] room.encryptionKey err:', e.message) }

  console.log('[live] build router providers for the room…')
  const p = await router._stateBaseProviders(roomId)
  console.log('[live] providers.roomSeed present (→ deterministic v2):', !!p.roomSeed)
  const selfHex = p.selfKey ? Buffer.from(p.selfKey).toString('hex') : null
  console.log('[live] providers.selfKey:', selfHex ? selfHex.slice(0, 16) + '…' : null)
  const pm = await p.getMembers()
  console.log('[live] providers.getMembers():', JSON.stringify(pm.map(m => ({ key: m.key.slice(0, 12) + '…', role: m.role }))))

  // self must be a member, as admin (creator), keyed by identity pubkey
  const selfMember = pm.find(m => m.key === selfHex)
  console.log('[live] self in members as admin?', !!selfMember, selfMember && ('role=' + selfMember.role))

  // attest/verifyAttest round-trip via the REAL providers
  const testData = Buffer.concat([Buffer.alloc(32, 3), Buffer.alloc(32, 4)])
  const proof = await p.attest(testData)
  const verOk = proof ? p.verifyAttest(proof, testData, p.selfKey) : false
  console.log('[live] providers.attest→verify round-trip:', verOk, ' proofBytes=', proof && proof.length)

  // Drive state.set through the real router path → creator mints, write lands.
  console.log('[live] state.set via router (creator path)…')
  await router._stateSet({ roomId, key: 'world:e2e', value: 'hello-e2e' })
  let info = await router._stateInfo({ roomId })
  console.log('[live] state.info:', JSON.stringify(info))
  const got = await router._stateGet({ roomId, key: 'world:e2e' }).catch(() => null)
  console.log('[live] state.get world:e2e:', JSON.stringify(got))

  // _stateGet wraps StateBase.get (itself {value,ts,_by}) → got.value.value is the leaf
  const leaf = got && got.value && (got.value.value !== undefined ? got.value.value : got.value)

  // D1c: when _getStateBase opened this room's StateBase it should have SEEDED
  // the local membership copy (__members__) from the real keet room, so apply
  // can gate optimistic admission (D1a/D1b, spec game-state.md §3.0/§4). The
  // creator is writable, so the seed runs; read the projected self role back.
  const sb = await router._getStateBase(roomId)
  const projectedSelfRole = await sb.getProjectedRole(selfHex)
  console.log('[live] D1c projected self role:', projectedSelfRole, '(expect', selfMember && selfMember.role, ')')

  const providersOk = !!selfHex && !!selfMember && selfMember.role === 1 && verOk
  const statePathOk = info && info.writable === true && !!info.stateBaseKey && leaf === 'hello-e2e'
  const projectionOk = projectedSelfRole === (selfMember && selfMember.role)
  if (providersOk && statePathOk && projectionOk) {
    console.log('\n✓ LIVE router wiring works: real getMembers/selfKey/attest + state.set minted a writable base, the write reads back, AND D1c seeded projected membership (__members__)')
    exitCode = 0
  } else {
    console.error('\n✗ providersOk=' + providersOk + ' statePathOk=' + statePathOk + ' projectionOk=' + projectionOk)
  }
} catch (err) {
  console.error('\n✗ FAIL:', err.message)
  if (err.stack) console.error(err.stack)
} finally {
  await Promise.race([
    (async () => { try { await daemon?.core?.close() } catch {} })(),
    new Promise(r => setTimeout(r, 5000))
  ])
  try { fs.rmSync(baseDir, { recursive: true, force: true }) } catch {}
}

console.log(exitCode === 0 ? '\nPASS' : '\nFAIL')
Bare.exit(exitCode)