OverviewFilesCommitsIssuesDocsReleasesPeers

P2P-Hiverelay / test / unit / runtime-timers.test.js

123 lines · javascript ref:
import test from 'brittle'
import b4a from 'b4a'
import { mkdtemp, rm } from 'fs/promises'
import { tmpdir } from 'os'
import { join } from 'path'
import { PubSub } from 'p2p-hiverelay/core/router/pubsub.js'
import { CircuitRelay } from 'p2p-hiverelay/core/protocol/relay-circuit.js'
import { TokenBucketRateLimiter } from 'p2p-hiverelay/core/protocol/rate-limiter.js'
import { SeedProtocol } from 'p2p-hiverelay/core/protocol/seed-request.js'
import { Seeder } from 'p2p-hiverelay/core/relay-node/seeder.js'

test('runtime cleanup timers do not keep short-lived processes alive', async (t) => {
  const pubsub = new PubSub()
  const circuit = new CircuitRelay(null, null)
  const limiter = new TokenBucketRateLimiter()
  const seed = new SeedProtocol(null)

  t.absent(pubsub._cleanupInterval.hasRef(), 'pubsub cleanup interval is unrefed')
  t.absent(circuit._cleanupInterval.hasRef(), 'circuit cleanup interval is unrefed')
  t.absent(limiter._cleanupInterval.hasRef(), 'rate limiter cleanup interval is unrefed')
  t.absent(seed._pendingCleanup.hasRef(), 'seed pending cleanup interval is unrefed')
  t.absent(seed._unseedNonceCleanup.hasRef(), 'seed nonce cleanup interval is unrefed')
  t.absent(seed.rateLimiter._cleanupInterval.hasRef(), 'seed rate limiter cleanup interval is unrefed')

  pubsub.destroy()
  circuit.destroy()
  limiter.destroy()
  seed.destroy()
})

test('seeder reannounce interval does not pin the process', async (t) => {
  const keyHex = 'a'.repeat(64)
  const dir = await mkdtemp(join(tmpdir(), 'runtime-timer-seeder-'))
  t.teardown(() => rm(dir, { recursive: true, force: true }))
  let updateCalls = 0
  let refreshStartedResolve
  let refreshResolve
  const refreshStarted = new Promise(resolve => { refreshStartedResolve = resolve })
  const refreshGate = new Promise(resolve => { refreshResolve = resolve })
  const snapshots = []
  const core = {
    discoveryKey: b4a.alloc(32, 0x11),
    fork: 0,
    length: 0,
    byteLength: 0,
    async ready () {},
    async update () {
      updateCalls++
      if (updateCalls === 1) return true
      refreshStartedResolve()
      await refreshGate
      return true
    },
    snapshot () {
      const range = {
        destroyed: false,
        async done () {},
        destroy () { this.destroyed = true }
      }
      const snap = {
        fork: core.fork,
        length: core.length,
        byteLength: core.byteLength,
        closed: false,
        async ready () {},
        download () { return range },
        async close () { this.closed = true },
        range
      }
      snapshots.push(snap)
      return snap
    },
    download () { return { done: async () => {}, destroy () {} } },
    on () {},
    removeListener () {},
    async close () {}
  }
  const store = {
    get () { return core }
  }
  const swarm = {
    join () {},
    async leave () {}
  }
  const admission = {
    reserve: (key, bytes) => ({ allowed: true, key, bytes }),
    owns: () => true,
    commit: () => true,
    rollback: () => true,
    release: () => true,
    markRecoveryReady () {},
    admission: () => ({ allowed: true }),
    failClosed () {}
  }
  const seeder = new Seeder(store, swarm, {
    storagePath: join(dir, 'seeded-cores.json'),
    storageAdmission: admission
  })
  await seeder.start()
  const entry = await seeder.seedCore(keyHex, { maxStorageBytes: 1024 * 1024 })

  t.absent(entry.interval.hasRef(), 'seeder reannounce interval is unrefed')

  const previousSnapshot = entry.rangeSnapshot
  const previousRange = entry.range
  const refreshing = seeder._refreshBoundedDownload(entry)
  await refreshStarted
  t.is(entry.rangeSnapshot, previousSnapshot, 'old snapshot stays live while the replacement proof is pending')
  t.is(entry.range, previousRange, 'old finite range stays live while the replacement proof is pending')
  t.absent(previousSnapshot.closed)
  t.absent(previousRange.destroyed)

  refreshResolve()
  t.ok(await refreshing, 'replacement proof settles')
  t.not(entry.rangeSnapshot, previousSnapshot, 'settled proof installs a fresh pinned snapshot')
  t.not(entry.range, previousRange, 'settled proof installs a fresh finite range')
  t.ok(previousSnapshot.closed, 'previous snapshot closes after replacement')
  t.ok(previousRange.destroyed, 'previous range destroys after replacement')
  t.is(snapshots.length, 2, 'one initial and one replacement snapshot were created')

  await seeder.stop()
})