OverviewFilesCommitsIssuesDocsReleasesPeers

hyper-vdrive / test / canary.mjs

118 lines · javascript ref:
// Canary — the seam is not a supported contract. This asserts every assumption
// the adapter makes about hypercore-storage and hypercore, so a version bump
// fails here loudly instead of somewhere subtle later.
//   node test/canary.mjs <workDir>
import { createRequire } from 'module'
const require = createRequire(import.meta.url)

const crypto = require('hypercore-crypto')
const Corestore = require('corestore')
const Hyperdrive = require('hyperdrive')
const CorestoreStorage = require('hypercore-storage')
const { ContentStore, build } = require('../lib/storage.cjs')

const ROOT = process.argv[2] || './work/canary'
const failures = []
const advisories = []
const checks = []

// required: the adapter cannot work if this is false.
// advisory: only matters for work not yet done (encryption re-derivation).
function check (name, ok, detail, severity = 'required') {
  checks.push({ name, ok: !!ok, severity, detail })
  if (ok) return
  const line = name + (detail ? ' — ' + detail : '')
  if (severity === 'required') failures.push(line)
  else advisories.push(line)
}

const versions = {}
for (const m of ['hypercore', 'hypercore-storage', 'corestore', 'hyperdrive', 'hyperblobs']) {
  try {
    versions[m] = require(m + '/package.json').version
  } catch {
    versions[m] = 'missing'
  }
}

// 1. the two factories Core calls to obtain per-core storage
for (const m of ['resumeCore', 'createCore']) {
  check('CorestoreStorage.' + m, typeof CorestoreStorage.prototype[m] === 'function')
}

// 2. duck-type used by Hypercore.defaultStorage to accept a storage instance
check(
  'isCoreStorage duck-type (setDefaultDiscoveryKey)',
  typeof CorestoreStorage.prototype.setDefaultDiscoveryKey === 'function'
)

// 3. drive a real publish through the adapter and observe the seam
const payload = crypto.randomBytes(512 * 1024)
const content = new ContentStore()
// deliberately register nothing: every block should fall through as literal,
// which still exercises read()/write()/getBlock/putBlock
const Base = build(content)

const seen = { resumeCore: 0, createCore: 0, read: 0, write: 0 }
class Observed extends Base {
  async resumeCore (dk) {
    seen.resumeCore++
    return super.resumeCore(dk)
  }

  async createCore (data) {
    seen.createCore++
    return super.createCore(data)
  }
}

const store = new Corestore(new Observed(ROOT), { primaryKey: Buffer.alloc(32, 5), unsafe: true })
const drive = new Hyperdrive(store)
await drive.ready()
await drive.getBlobs()

const coreStorage = drive.blobs.core.state.storage
for (const m of ['read', 'write', 'snapshot', 'atomize', 'createBlockStream']) {
  check('HypercoreStorage.' + m, typeof coreStorage[m] === 'function')
}

const rx = coreStorage.read()
check('CoreRX.getBlock', typeof rx.getBlock === 'function')
check(
  'CoreRX carries fork (only needed if encryption re-derivation is built)',
  'fork' in rx.__unwrapped,
  null,
  'advisory'
)
rx.destroy()

const tx = coreStorage.write()
check('CoreTX.putBlock', typeof tx.putBlock === 'function')
check('CoreTX.deleteBlockRange', typeof tx.deleteBlockRange === 'function')

await drive.put('/probe.bin', payload)
const got = await drive.get('/probe.bin')
check('readback through the adapter', got !== null && got.equals(payload))

check('Core called resumeCore or createCore', seen.resumeCore + seen.createCore > 0,
  JSON.stringify(seen))

await drive.close()
await store.close()

console.log(
  JSON.stringify(
    {
      versions,
      checks,
      failed: failures,
      advisories,
      verdict: failures.length === 0 ? 'PASS' : 'FAIL'
    },
    null,
    2
  )
)

if (failures.length) process.exit(1)