/**
* Federation end-to-end tests for Autobase-backed IdentityRegistry
*
* Verifies that two registries can sync users via Autobase replication:
* - Server A creates a registry, Server B joins with bootstrap key
* - Users created on A are visible on B after sync (and vice versa)
* - Identity lookups (by public key, by identity) work across peers
*/
import test from 'brittle'
import fs from 'bare-fs'
import path from 'bare-path'
import os from 'bare-os'
import Corestore from 'corestore'
import { IdentityRegistry } from '../../lib/auth/identity-registry.js'
const tmpDir = path.join(os.tmpdir(), 'pear-git-test-federation-' + Date.now())
let storeIdx = 0
function nextStorePath () {
return path.join(tmpDir, 'store-' + (storeIdx++))
}
test('setup: create temp dir', async (t) => {
await fs.promises.mkdir(tmpDir, { recursive: true })
t.pass('temp dir created')
})
test('two registries sync users via Autobase', async (t) => {
// Server A creates registry (no bootstrapKey = new Autobase)
const storeA = new Corestore(nextStorePath())
const registryA = new IdentityRegistry({ corestore: storeA })
await registryA.ready()
// Server B joins with A's bootstrap key
const storeB = new Corestore(nextStorePath())
const registryB = new IdentityRegistry({
corestore: storeB,
bootstrapKey: registryA.bootstrapKey
})
await registryB.ready()
// Replicate corestores (simulates driveSwarm connection)
const s1 = storeA.replicate(true)
const s2 = storeB.replicate(false)
s1.pipe(s2).pipe(s1)
// Server A adds Server B as a writer (federation handshake)
await registryA.addWriter(registryB.localKey)
// Server A creates a user
const userA = await registryA.createUser({ name: 'Alice' })
t.ok(userA.userId, 'Server A created user')
// Server B should see it after sync
await registryB.sync({ timeout: 3000 })
const foundOnB = await registryB.getUser(userA.userId)
t.ok(foundOnB, 'Server B sees user created on Server A')
t.is(foundOnB.name, 'Alice')
// Server B creates a user (now writable after addWriter)
const userB = await registryB.createUser({ name: 'Bob' })
t.ok(userB.userId, 'Server B created user')
// Server A should see it after sync
await registryA.sync({ timeout: 3000 })
const foundOnA = await registryA.getUser(userB.userId)
t.ok(foundOnA, 'Server A sees user created on Server B')
t.is(foundOnA.name, 'Bob')
// Both see all users
const usersA = await registryA.listAllUsers()
const usersB = await registryB.listAllUsers()
t.is(usersA.length, 2, 'Server A has 2 users')
t.is(usersB.length, 2, 'Server B has 2 users')
// Cleanup
s1.destroy()
s2.destroy()
await registryA.close()
await registryB.close()
})
test('identity created on A is resolvable on B', async (t) => {
const storeA = new Corestore(nextStorePath())
const registryA = new IdentityRegistry({ corestore: storeA })
await registryA.ready()
const storeB = new Corestore(nextStorePath())
const registryB = new IdentityRegistry({
corestore: storeB,
bootstrapKey: registryA.bootstrapKey
})
await registryB.ready()
const s1 = storeA.replicate(true)
const s2 = storeB.replicate(false)
s1.pipe(s2).pipe(s1)
// A creates user + web identity
const { user, identity } = await registryA.autoRegister({
identityId: 'web-pubkey-alice-123',
method: 'web-identity',
name: 'Alice',
credentials: { publicKey: 'web-pubkey-alice-123' }
})
// B should resolve identity by public key
await registryB.sync({ timeout: 3000 })
const found = await registryB.getIdentityByPublicKey('web-pubkey-alice-123')
t.ok(found, 'Server B resolves identity by public key')
t.is(found.identityId, 'web-pubkey-alice-123')
const foundUser = await registryB.getUserByIdentity('web-pubkey-alice-123')
t.ok(foundUser, 'Server B resolves user by identity')
t.is(foundUser.name, 'Alice')
s1.destroy()
s2.destroy()
await registryA.close()
await registryB.close()
})
test('cleanup: remove temp dir', async (t) => {
await fs.promises.rm(tmpDir, { recursive: true, force: true })
t.pass('temp dir removed')
})