// hyper-identity (autobase) must satisfy the shared contract, assertion for assertion.
//
// The contract lives in hyper-identity-core and is imported, never restated. hyper-identity2 runs
// the SAME function against autobee — that is what makes "both engines agree" a fact rather than
// two suites agreeing with themselves.
//
// This arm is also the CONTROL: it exercises the shipping path, so a divergence localises to the
// new binding rather than to the contract.
import test from 'brittle'
import Corestore from 'corestore'
import Hyperbee from 'hyperbee'
import path from '#node:path'
import os from '#node:os'
import crypto from 'hypercore-crypto'
import { contract } from 'hyper-identity-core/test/contract.js'
import { IdentityRegistry } from '../lib/auth/identity-registry.js'
import { adaptHost } from '../lib/auth/adapt-host.js'
async function open () {
const dir = path.join(os.tmpdir(), 'hi-contract-' + crypto.randomBytes(8).toString('hex'))
const store = new Corestore(dir)
await store.ready()
const db = new Hyperbee(store.get({ name: 'legacy-db' }), { keyEncoding: 'utf-8', valueEncoding: 'json' })
await db.ready()
const registry = new IdentityRegistry(store, { db })
await registry.ready()
return { registry, cleanup: async () => { await registry.close(); await db.close(); await store.close() } }
}
test('[autobase] the IdentityRegistry contract', async (t) => {
const { registry, cleanup } = await open()
t.teardown(cleanup)
// ⛔ The package IS the engine. This cannot be an option that lies about what was constructed —
// an earlier version of this suite had exactly that bug and compared autobase to autobase.
t.is(registry.engine, 'autobase', 'the package reports what it is')
await contract(t, registry)
})
// ⛔ The API break that fails SILENTLY, so it gets its own test.
//
// The core speaks `{ isIndexer }`. autobase spells it `{ indexer }`; autobee spells it
// `{ isIndexer }` and DEFAULTS IT TO TRUE. So an option lost in translation makes a plain writer
// here and an INDEXER there — the thing identity-registry warns deadlocks a manifest migration.
//
// An earlier version of this test asked the ENGINE whether a writer had become an indexer. Its own
// positive control disproved it: autobase parks a granted indexer in `pendingIndexers` until its
// core exists and acks, so a fresh key is in neither list and the accessor said "no" to everything,
// including the case it existed to catch. The defect is a translation; test the translation.
test('⛔ [autobase] adaptHost translates the core vocabulary, and pins the default', (t) => {
const seen = []
const host = adaptHost({
addWriter: (key, opts) => { seen.push(['add', key, opts]) },
removeWriter: (key) => { seen.push(['remove', key]) }
})
host.addWriter('k1', { isIndexer: true })
host.addWriter('k2', { isIndexer: false })
host.addWriter('k3', {})
host.removeWriter('k1')
t.alike(seen[0], ['add', 'k1', { indexer: true }], 'isIndexer:true becomes autobase indexer:true')
t.alike(seen[1], ['add', 'k2', { indexer: false }], 'and false stays false')
t.alike(seen[2], ['add', 'k3', { indexer: false }], 'an ABSENT flag is false, never an engine default')
t.alike(seen[3], ['remove', 'k1'], 'removeWriter passes through')
t.absent('isIndexer' in seen[0][2], 'the core spelling never reaches autobase')
})
test('adaptHost leaves a host with no writer controls alone', (t) => {
const bare = { somethingElse: 1 }
t.is(adaptHost(bare), bare, 'nothing to translate, nothing wrapped')
t.is(adaptHost(null), null)
})