OverviewFilesCommitsIssuesDocsReleasesPeers

pgh / test / resolve-release-cold-replica.test.js

90 lines · javascript ref:
/**
 * resolveRelease's direct-replica path must not depend on a settle TIMER.
 *
 * 2026-10-11, stortron: the sidecar was wedged (UDS backlog full, EAGAIN), so
 * `pgh app update pear-git-v3` fell back to replicating the registry itself,
 * slept a fixed 500 ms, and read `latest` from a half-synced view: 0.1.46 (April)
 * instead of 0.1.254. Correctness has to come from the replication protocol —
 * findingPeers + update({ wait: true }) — not from how long we waited.
 * settleMs: 0 makes the old timer-dependence deterministic.
 */

import test from 'brittle'
import fs from '#node:fs'
import path from '#node:path'
import os from '#node:os'
import Corestore from 'corestore'
import Hyperswarm from 'hyperswarm'
import createTestnet from 'hyperdht/testnet'

import { ReleaseRegistry } from '../lib/release-registry.js'
import { resolveRelease } from '../lib/resolve-release.js'

const tmp = (p) => fs.mkdtempSync(path.join(os.tmpdir(), p + '-'))

async function publisher (t, bootstrap, versions) {
  const store = new Corestore(tmp('pub-store'))
  const registry = new ReleaseRegistry(store)
  await registry.ready()
  for (const [version, publishedAt] of versions) {
    await registry.add({ name: 'app', version, publishedAt, treeHash: 'f'.repeat(64) })
  }
  const swarm = new Hyperswarm({ bootstrap })
  swarm.on('connection', (c) => store.replicate(c))
  swarm.join(registry._base.discoveryKey, { server: true, client: false })
  await swarm.flush()
  t.teardown(async () => { await swarm.destroy(); await registry.close(); await store.close() })
  return registry
}

test('cold direct replica resolves the NEWEST release with no settle time', async (t) => {
  const testnet = await createTestnet(3)
  t.teardown(() => testnet.destroy())
  const versions = []
  for (let i = 1; i <= 40; i++) versions.push(['0.1.' + i, new Date(Date.UTC(2026, 0, i)).toISOString()])
  const reg = await publisher(t, testnet.bootstrap, versions)

  const { entry, via, cleanup } = await resolveRelease({
    registryKeyHex: reg.keyHex,
    name: 'app',
    preferSidecar: false,
    settleMs: 0,
    storeDir: tmp('cold-replica'),
    bootstrap: testnet.bootstrap
  })
  await cleanup()
  t.is(via, 'replica')
  t.is(entry && entry.version, '0.1.40', 'latest is the newest published, not whatever had arrived')
})

test('no peer at all: resolves null once discovery ends (no hang, no timer)', async (t) => {
  const testnet = await createTestnet(3)
  t.teardown(() => testnet.destroy())
  const lonely = new Corestore(tmp('lonely'))
  const reg = new ReleaseRegistry(lonely)
  await reg.ready()
  const key = reg.keyHex
  await reg.close(); await lonely.close()

  const { entry, cleanup } = await resolveRelease({
    registryKeyHex: key,
    name: 'app',
    preferSidecar: false,
    storeDir: tmp('cold-replica-2'),
    bootstrap: testnet.bootstrap
  })
  await cleanup()
  t.is(entry, null)
})

test('isOlderVersion — numeric dotted compare; null when not comparable', async (t) => {
  const { isOlderVersion } = await import('../lib/resolve-release.js')
  t.is(isOlderVersion('0.1.46', '0.1.254'), true, 'the stortron case')
  t.is(isOlderVersion('0.1.254', '0.1.251'), false)
  t.is(isOlderVersion('0.1.254', '0.1.254'), false, 'same is not older')
  t.is(isOlderVersion('0.1431.0', '0.1429.0'), false)
  t.is(isOlderVersion('1.0', '0.9.9'), false, 'missing segment = 0')
  t.is(isOlderVersion('master-ed31de1', '0.1.1'), null, 'non-numeric: no opinion')
  t.is(isOlderVersion('0.1.5', undefined), null)
})