OverviewFilesCommitsIssuesDocsReleasesPeers

pear-git-v3 / test / pairing-persistence.test.js

73 lines · javascript ref:
// Debt-2 proof: an org invite survives a daemon restart. The daemon persists
// the pairing descriptor (pending-pairings.json) and re-registers the blind-
// pairing member on boot (resumeRoomPairing), so an already-shared invite keeps
// working after the owner restarts.

import test from 'brittle'
import process from '#node:process'
import { mkdtempSync } from '#node:fs'
import { join } from '#node:path'
import { tmpdir } from '#node:os'
import b4a from 'b4a'
import createTestnet from '@hyperswarm/testnet'

import { startPearGit } from '../index.js'

let n = 0
const tmpStorage = () => mkdtempSync(join(tmpdir(), 'pgv3-pp-' + (n++) + '-'))
const tmpSock = () => join(mkdtempSync(join(tmpdir(), 'pgv3-pp-sock-' + (n++) + '-')), 'svc.sock')

async function waitFor (pred, ms = 30000) {
  const t0 = Date.now()
  while (Date.now() - t0 < ms) {
    try { if (await pred()) return true } catch {}
    await new Promise((r) => setTimeout(r, 100))
  }
  return false
}

test('org invite survives a daemon restart (persisted descriptor → resume on boot)', async (t) => {
  process.env.PGV3_USE_RAW_IDENTITY = '1'
  const testnet = await createTestnet(3, t.teardown)
  const storage = tmpStorage()
  const { Client } = await import('hyper-svc')

  // A1: create an org + a contributor invite (persisted), capture it, then CLOSE.
  const A1 = await startPearGit({
    storage, uds: tmpSock(), http: { port: 0 },
    swarm: { swarm: { bootstrap: testnet.bootstrap } }, deploy: false, sync: false
  })
  const topicHex = b4a.toString(A1.svc.swarm.topic, 'hex')
  let invitation
  {
    const ca = await Client.connectUds(A1.svc.uds.path)
    await ca.call('create-org', { name: 'palpha', type: 'group', displayName: 'PA' })
    const r = await ca.call('create-org-invite', { name: 'palpha', role: 'contributor' })
    invitation = r.invitation
    await ca.close()
  }
  await A1.close()
  t.ok(invitation && invitation.startsWith('roompair1:'), 'invite minted + daemon closed')

  // A2: reopen the SAME storage → boot reads pending-pairings.json and
  // resumeRoomPairing re-registers the member.
  const A2 = await startPearGit({
    storage, uds: tmpSock(), http: { port: 0 },
    swarm: { swarm: { bootstrap: testnet.bootstrap } }, deploy: false, sync: false
  })
  t.teardown(async () => { try { await A2.close() } catch {} })
  t.is(b4a.toString(A2.svc.swarm.topic, 'hex'), topicHex, 'A2 reopened the same identity (raw identity persisted)')

  // B: join using the PRE-RESTART invite → still pairs + becomes a writer.
  const B = await startPearGit({
    storage: tmpStorage(), uds: tmpSock(), http: { port: 0 },
    swarm: { swarm: { bootstrap: testnet.bootstrap } }, deploy: false, sync: false
  })
  t.teardown(async () => { try { await B.close() } catch {} })

  const oj = await B.orgs.joinOrg(invitation, { swarm: B.svc.swarm.swarm })
  t.is(oj.name, 'palpha', 'B joined via the pre-restart invite')
  const writable = await waitFor(() => { const r = B.orgs.getOrgRoom('palpha'); return r && r.writable })
  t.ok(writable, 'B became a writer via the RESUMED pairing — invite survived the restart')
})