OverviewFilesCommitsIssuesDocsReleasesPeers

P2P-Hiverelay / test / integration / drive-boot-real-cli.test.js

130 lines · javascript ref:
// drive-boot M4-lite acceptance: the loader boots the REAL Blindspark Node CLI off a signed
// code drive (not a stand-in), gates on the relay's real /health, and self-updates to a
// version whose bumped packages/core/package.json ONLY the new drive content reports — proving
// the swap actually put fresh code live — while the /data relay identity survives the restart.
//
// Exercises the real dependency wiring: the drive ships pure-JS packages/, and the loader links
// node_modules so external deps resolve to the image's baked copy while the 4 workspace packages
// resolve to the mirror's fresh packages/. Boots two full relays, so it runs longer than a unit
// test (explicit timeout). /health running:true is not DHT-gated, so it stays robust.
import test from 'brittle'
import Corestore from 'corestore'
import Hyperdrive from 'hyperdrive'
import net from 'node:net'
import http from 'node:http'
import { readFileSync, existsSync } from 'node:fs'
import { mkdtemp, rm } from 'node:fs/promises'
import { tmpdir } from 'node:os'
import { join } from 'node:path'
import { fileURLToPath } from 'node:url'
import { randomBytes } from 'node:crypto'
import { collectCodeFiles, writeCodeDrive } from '../../scripts/drive-boot/publish-code-drive.mjs'
import { pubKeyFromSeedHex } from '../../scripts/drive-boot/release-manifest.mjs'
import { DriveBootLoader } from '../../scripts/drive-boot/loader.mjs'

const REPO_ROOT = fileURLToPath(new URL('../../', import.meta.url))

function freePort () {
  return new Promise((resolve, reject) => {
    const s = net.createServer()
    s.on('error', reject)
    s.listen(0, '127.0.0.1', () => { const p = s.address().port; s.close(() => resolve(p)) })
  })
}

function shimHealth (port) {
  return new Promise((resolve) => {
    const req = http.get({ host: '127.0.0.1', port, path: '/health', timeout: 4000 }, (res) => {
      let b = ''
      res.on('data', (d) => { b += d })
      res.on('end', () => { try { resolve(JSON.parse(b)) } catch { resolve(null) } })
    })
    req.on('error', () => resolve(null))
    req.on('timeout', () => { req.destroy(); resolve(null) })
  })
}

test('drive-boot M4-lite: loader boots the REAL Blindspark CLI + self-updates; /data identity survives',
  { timeout: 180000 }, async (t) => {
    const realVersion = JSON.parse(readFileSync(join(REPO_ROOT, 'packages/core/package.json'), 'utf8')).version
    const bumped = realVersion + '-driveboot-e2e'
    const seed = randomBytes(32).toString('hex')
    const { pubHex } = pubKeyFromSeedHex(seed)
    const runtime = { nodeMajor: String(process.versions.node.split('.')[0]), packagesOnly: true }

    // v1 = the real packages/ tree; v2 = same tree with packages/core/package.json version bumped
    // (the relay's /health reports packages/core/package.json version via _relayVersion()).
    const v1 = await collectCodeFiles(REPO_ROOT)
    t.ok(v1.length > 100, 'collected the real app tree (' + v1.length + ' files)')
    const v2 = v1.map((f) => {
      if (f.path !== '/packages/core/package.json') return f
      const pkg = JSON.parse(f.content.toString()); pkg.version = bumped
      return { path: f.path, content: Buffer.from(JSON.stringify(pkg, null, 2)) }
    })

    const storeDirA = await mkdtemp(join(tmpdir(), 'db-cli-storeA-'))
    const storeDirB = await mkdtemp(join(tmpdir(), 'db-cli-storeB-'))
    const mirrorRoot = await mkdtemp(join(tmpdir(), 'db-cli-app-'))
    const dataDir = await mkdtemp(join(tmpdir(), 'db-cli-data-'))
    const storeA = new Corestore(storeDirA)
    const drive = new Hyperdrive(storeA)
    await drive.ready()
    await writeCodeDrive(drive, { files: v1, version: realVersion, seedHex: seed, runtime }); const dv1 = drive.version
    await writeCodeDrive(drive, { files: v2, version: bumped, seedHex: seed, runtime }); const dv2 = drive.version

    const storeB = new Corestore(storeDirB)
    const s1 = storeA.replicate(true)
    const s2 = storeB.replicate(false)
    s1.pipe(s2).pipe(s1)

    const appPort = await freePort()
    const loader = new DriveBootLoader({
      store: storeB,
      driveKey: drive.key.toString('hex'),
      expectedReleaseKey: pubHex,
      mirrorRoot,
      dataDir,
      entry: 'packages/core/cli/index.js',
      appArgs: ['start'],
      portFlag: '--port',
      nodeModules: join(REPO_ROOT, 'node_modules'),
      env: { HOME: dataDir, HIVERELAY_STORAGE: dataDir },
      appPort,
      publicPort: 0,
      healthPath: '/health',
      healthTimeoutMs: 40000
    })
    const publicPort = await loader.startShim()

    try {
      // ── boot the real relay at v1 ──────────────────────────────────────────
      const a = await loader.activate(dv1)
      t.is(a.appVersion, realVersion, 'real CLI activated at ' + realVersion)
      const h1 = await shimHealth(publicPort)
      t.ok(h1 && h1.running === true, 'real relay reports running:true through the shim')
      t.is(String(h1.version), realVersion, 'shim surfaces the real relay version')
      const idPath = join(dataDir, 'relay-identity.json')
      t.ok(existsSync(idPath), 'relay minted its identity on /data')
      const identityV1 = readFileSync(idPath, 'utf8')

      // ── self-update to the bumped v2: the gate on `bumped` only passes if the NEW drive
      //    content (bumped package.json) is what the relay is now running ──────────
      const sw = await loader.swapTo(dv2)
      t.ok(sw.swapped && !sw.rolledBack, 'swapped the real relay to v2')
      const h2 = await shimHealth(publicPort)
      t.ok(h2 && h2.running === true, 'relay running:true after the self-update')
      t.is(String(h2.version), bumped, 'relay now reports the bumped version — fresh drive code is live')

      // ── /data identity survived the real relay restart ─────────────────────
      t.is(readFileSync(idPath, 'utf8'), identityV1, '/data relay identity preserved across the self-update')
    } finally {
      await loader.stop()
      s1.destroy(); s2.destroy()
      await drive.close(); await storeA.close(); await storeB.close()
      await rm(storeDirA, { recursive: true, force: true })
      await rm(storeDirB, { recursive: true, force: true })
      await rm(mirrorRoot, { recursive: true, force: true })
      await rm(dataDir, { recursive: true, force: true })
    }
  })