// 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)