OverviewFilesCommitsIssuesDocsReleasesPeers

pgh-sidecar / test / name-registry-default-subscription.test.js

109 lines · javascript ref:
/**
 * Default NameRegistry subscription — a fresh machine must resolve bare
 * app names without a manual `pgh names subscribe`.
 *
 * Bug this pins: name resolution is subscription-based (`_resolveNameAcross`
 * = own registry, then each subscribed replica). Nothing seeded either source
 * on a fresh machine — not `pgh init`, not install.sh, not any package.json
 * default — so a new peer booted its OWN empty NameRegistry, had zero
 * replicas, and every documented `pgh app install <name>` failed with
 * "name not found in pgh-sidecar's NameRegistry". Only a pgh:// URL worked.
 *
 * Contract: configuration WINS OUTRIGHT over the default (no merging), so a
 * peer subscribed to its own namespace registries never has ours forced on it.
 *
 * Constructor-only — no boot, no swarm. The subscription decision happens in
 * the Sidecar constructor; opening the replicas is gated behind enableSeed.
 */

import test from 'brittle'
import fs from '#node:fs'
import path from '#node:path'
import os from '#node:os'
import process from '#node:process'

import { DEFAULT_NAME_REGISTRIES } from 'pgh/bootstrap'
import { Sidecar } from '../lib/index.js'

const OTHER_KEY = 'a'.repeat(64)
const THIRD_KEY = 'b'.repeat(64)

// Point subscriptions.json at a scratch file so the test never reads (or
// writes) the real ~/.config/pgh/subscriptions.json.
function withSubscriptions (contents, fn) {
  const dir = fs.mkdtempSync(path.join(os.tmpdir(), 'pgh-subs-'))
  const file = path.join(dir, 'subscriptions.json')
  if (contents !== null) fs.writeFileSync(file, JSON.stringify(contents, null, 2))
  // NB: under Bare `process.env` is a Proxy whose deleteProperty trap returns
  // false — `delete process.env.X` THROWS. Both readers treat '' as unset
  // (plain falsy checks), so clear by assignment instead.
  const prevPath = process.env.PGH_SUBSCRIPTIONS_PATH || ''
  const prevEnv = process.env.PGH_NAME_REGISTRIES || ''
  process.env.PGH_SUBSCRIPTIONS_PATH = file
  process.env.PGH_NAME_REGISTRIES = ''
  try {
    return fn()
  } finally {
    process.env.PGH_SUBSCRIPTIONS_PATH = prevPath
    process.env.PGH_NAME_REGISTRIES = prevEnv
    fs.rmSync(dir, { recursive: true, force: true })
  }
}

// Constructing a Sidecar touches no disk and starts nothing — `_open` does.
function subscriptionsFor (opts = {}) {
  return new Sidecar({ enableSeed: false, ...opts }).nameRegistrySubscriptions
}

test('default is non-empty and well-formed (64-hex keys)', (t) => {
  t.ok(DEFAULT_NAME_REGISTRIES.length > 0, 'ships at least one default registry')
  for (const k of DEFAULT_NAME_REGISTRIES) {
    t.ok(/^[0-9a-f]{64}$/.test(k), k.slice(0, 12) + '… is a 64-char hex key')
  }
})

test('fresh machine (no subscriptions.json) gets the default', (t) => {
  withSubscriptions(null, () => {
    const subs = subscriptionsFor()
    for (const k of DEFAULT_NAME_REGISTRIES) {
      t.ok(subs.includes(k), 'subscribed to default ' + k.slice(0, 12) + '…')
    }
  })
})

test('subscriptions.json without a nameRegistries key gets the default', (t) => {
  // The real-world fresh shape: `pgh app install pgh://…` writes `registries`
  // (release) and never touches `nameRegistries`.
  withSubscriptions({ registries: [OTHER_KEY] }, () => {
    const subs = subscriptionsFor()
    t.alike(subs, [...DEFAULT_NAME_REGISTRIES], 'release subs do not count as name subs')
  })
})

test('configured nameRegistries win outright — default is NOT merged in', (t) => {
  withSubscriptions({ nameRegistries: [OTHER_KEY] }, () => {
    const subs = subscriptionsFor()
    t.alike(subs, [OTHER_KEY], 'exactly what the user configured')
    for (const k of DEFAULT_NAME_REGISTRIES) {
      t.absent(subs.includes(k), 'default not forced on a configured peer')
    }
  })
})

test('PGH_NAME_REGISTRIES wins outright over the default', (t) => {
  withSubscriptions(null, () => {
    process.env.PGH_NAME_REGISTRIES = THIRD_KEY
    const subs = subscriptionsFor()
    t.alike(subs, [THIRD_KEY], 'env-configured set replaces the default')
  })
})

test('explicit opts win over everything', (t) => {
  withSubscriptions(null, () => {
    t.alike(subscriptionsFor({ nameRegistrySubscriptions: [OTHER_KEY] }), [OTHER_KEY])
    // An explicit empty list is a real choice (subscribe to nothing), not
    // "unset" — it must not fall through to the default.
    t.alike(subscriptionsFor({ nameRegistrySubscriptions: [] }), [], 'explicit [] opts out')
  })
})