// readOldOrgs (#51): reads org:* records from a legacy single-orgs
// autobase view. The fixture builds a REAL autobase (mirroring hyper-rooms'
// open/apply) on a fresh tmp store — no prod data, no copy, so no
// hypercore-storage device-file guard.
import test from 'brittle'
import Corestore from 'corestore'
import Autobase from 'autobase'
import Hyperbee from 'hyperbee'
import { mkdtempSync } from '#node:fs'
import { join } from '#node:path'
import { tmpdir } from '#node:os'
import { readOldOrgs } from '../lib/services/migrate/read-old-orgs.js'
// A minimal single-`orgs` autobase: append create-org ops, the apply puts
// `org:{name}` into the Hyperbee view — the same view shape the old
// HyperOrgs (single-autobase) produced.
async function makeOldOrgsBase (t) {
const store = new Corestore(mkdtempSync(join(tmpdir(), 'old-orgs-')))
const base = new Autobase(store, null, {
valueEncoding: 'json',
open (viewStore) {
return new Hyperbee(viewStore.get('view', { valueEncoding: 'json' }), {
keyEncoding: 'utf-8', valueEncoding: 'json'
})
},
async apply (nodes, view) {
for (const node of nodes) {
const op = node.value
if (op && op.type === 'create-org') await view.put('org:' + op.org.name, op.org)
else if (op && op.type === 'put-raw') await view.put(op.key, op.value) // non-org view entries (writer:/grant:)
}
}
})
await base.ready()
t.teardown(async () => {
try { await base.close() } catch {}
try { await store.close() } catch {}
})
return base
}
async function collect (gen) {
const out = []
for await (const x of gen) out.push(x)
return out
}
test('readOldOrgs streams every org record from the single-orgs view', async (t) => {
const base = await makeOldOrgsBase(t)
const ORGS = [
{ name: 'alpha', displayName: 'Alpha', type: 'group', members: [{ userId: 'a', role: 'owner' }, { userId: 'b', role: 'contributor' }], resources: ['repo-1', 'repo-2'], visibility: 'public' },
{ name: 'beta', type: 'group', members: [{ userId: 'c', role: 'owner' }], resources: [] },
{ name: 'gamma', type: 'group', members: [], resources: ['repo-3'] }
]
for (const org of ORGS) await base.append({ type: 'create-org', org })
await base.update()
const read = await collect(readOldOrgs(base))
t.is(read.length, 3, 'all three orgs read back')
t.alike(read.map(o => o.name).sort(), ['alpha', 'beta', 'gamma'])
const alpha = read.find(o => o.name === 'alpha')
t.alike(alpha.resources, ['repo-1', 'repo-2'], 'resources preserved')
t.is(alpha.members.length, 2, 'members preserved')
})
test('readOldOrgs only yields org:* keys (ignores other view entries)', async (t) => {
const base = await makeOldOrgsBase(t)
await base.append({ type: 'create-org', org: { name: 'alpha', members: [], resources: [] } })
// writer:/grant: entries live in the same view in the real old format —
// written through apply (the view isn't directly writable).
await base.append({ type: 'put-raw', key: 'writer:someuser', value: { role: 'owner' } })
await base.append({ type: 'put-raw', key: 'grant:xyz', value: { permission: 'admin' } })
await base.update()
const read = await collect(readOldOrgs(base))
t.alike(read.map(o => o.name), ['alpha'], 'only the org record, not writer:/grant:')
})
test('readOldOrgs on an empty orgs autobase yields nothing', async (t) => {
const base = await makeOldOrgsBase(t)
const read = await collect(readOldOrgs(base))
t.alike(read, [], 'no orgs → empty')
})
test('readOldOrgs feeds migrateOrgs end-to-end (reader → transform)', async (t) => {
const { migrateOrgs } = await import('../lib/services/migrate/migrate-orgs.js')
const base = await makeOldOrgsBase(t)
for (const org of [
{ name: 'alpha', type: 'group', members: [{ userId: 'a', role: 'owner' }], resources: ['r1'] },
{ name: 'beta', type: 'group', members: [], resources: [] }
]) await base.append({ type: 'create-org', org })
await base.update()
const created = []
const summary = await migrateOrgs({
oldOrgs: readOldOrgs(base), // the reader as the async-iterable source
create: async (o) => created.push(o.name),
addMember: async () => {},
addResource: async () => {}
})
t.is(summary.orgs, 2, 'reader → migrateOrgs migrated both orgs')
t.alike(created.sort(), ['alpha', 'beta'])
})