import test from 'brittle'
import Corestore from 'corestore'
import Hyperswarm from 'hyperswarm'
import createTestnet from 'hyperdht/testnet'
import os from 'bare-os'
import path from 'bare-path'
import { randomBytes } from 'hypercore-crypto'
import { SharedRegistry } from '../lib/multiwriter/shared-registry.js'
import { InstanceLinker } from '../lib/multiwriter/instance-linker.js'
function createStore () {
const tmp = path.join(os.tmpdir(), 'hypernote-linker-test-' + randomBytes(16).toString('hex'))
return new Corestore(tmp)
}
test('createInvite returns hex invite', async (t) => {
const testnet = await createTestnet(3)
const bootstrap = testnet.bootstrap
const store = createStore()
const reg = new SharedRegistry(store)
await reg.ready()
const swarm = new Hyperswarm({ bootstrap })
const linker = new InstanceLinker(reg, { swarm })
const result = await linker.createInvite()
t.ok(result.invite, 'invite buffer exists')
t.ok(result.inviteHex, 'inviteHex exists')
t.is(typeof result.inviteHex, 'string', 'inviteHex is a string')
t.ok(result.inviteHex.length > 0, 'inviteHex is non-empty')
t.is(typeof result.waitForPeer, 'function', 'waitForPeer is a function')
await linker.destroy()
await swarm.destroy()
await reg.close()
await store.close()
await testnet.destroy()
})
test('full link flow between two instances', async (t) => {
const testnet = await createTestnet(3)
const bootstrap = testnet.bootstrap
// Create two stores and registries
const store1 = createStore()
const reg1 = new SharedRegistry(store1)
await reg1.ready()
const store2 = createStore()
const reg2 = new SharedRegistry(store2)
await reg2.ready()
// Add a user on registry 1
await reg1.createUser({ userId: 'admin1', name: 'Admin', role: 'admin' })
// Create swarms with testnet bootstrap
const swarm1 = new Hyperswarm({ bootstrap })
const swarm2 = new Hyperswarm({ bootstrap })
const linker1 = new InstanceLinker(reg1, { swarm: swarm1 })
const linker2 = new InstanceLinker(reg2, { swarm: swarm2 })
// Instance 1 creates invite
const inviteResult = await linker1.createInvite()
t.ok(inviteResult.inviteHex, 'invite created')
// Instance 2 joins with invite, while instance 1 waits for peer
const [joinResult, joinerInfo] = await Promise.all([
linker2.joinWithInvite(inviteResult.inviteHex),
inviteResult.waitForPeer()
])
// Verify join result has the source's keys
t.ok(joinResult.bootstrapKey, 'joinResult has bootstrapKey')
t.is(joinResult.bootstrapKey, reg1.bootstrapKey.toString('hex'), 'bootstrapKey matches reg1')
t.ok(joinResult.writerKey, 'joinResult has writerKey')
t.is(joinResult.writerKey, reg1.localWriterKey.toString('hex'), 'writerKey matches reg1 local writer')
// Verify the member (instance 1) got the joiner's info
t.ok(joinerInfo, 'joinerInfo received by member')
t.is(joinerInfo.writerKey, reg2.localWriterKey.toString('hex'), 'joinerInfo writerKey matches reg2')
// Cleanup
await linker1.destroy()
await linker2.destroy()
await swarm1.destroy()
await swarm2.destroy()
await reg1.close()
await reg2.close()
await store1.close()
await store2.close()
await testnet.destroy()
})