// Proof: the REAL RepoBase (atproto-room, dependency-free) drives an OverlayRepoStore through the
// substrate adapter identically to a StateBase KV — and the records it writes derive the same repo
// as the Hyperbee RepoStore. This is the record seam that moves the multi-DID path off the autobase.
import test from 'brittle'
import { mkdtempSync, rmSync } from 'node:fs'
import { join } from 'node:path'
import { tmpdir } from 'node:os'
import b4a from 'b4a'
import { OverlayRepoStore } from '../lib/overlay-repo-store.js'
import { OverlayRepoSubstrate } from '../lib/overlay-repo-substrate.js'
import { RepoStore } from '../lib/repo-store.js'
import { getRepo } from '../lib/read-view.js'
import { RepoBase } from '../../atproto-room/lib/repo-base.js' // dependency-free (pure KV convention)
const DID = 'did:plc:test'
const KEY = (() => { const k = b4a.alloc(32); k[31] = 7; return k })()
const REV = '3ktestrev00000'
const META = { did: DID, rev: REV, prev: null, privateKey: KEY }
const tmp = (t) => { const d = mkdtempSync(join(tmpdir(), 'ors-sub-')); t.teardown(() => rmSync(d, { recursive: true, force: true })); return d }
test('RepoBase over the adapter: put/get/list/snapshot/delete work over files', async (t) => {
const store = await new OverlayRepoStore(tmp(t)).ready()
const repo = new RepoBase(new OverlayRepoSubstrate(store), { did: DID })
const post = (text, at) => ({ $type: 'app.bsky.feed.post', text, createdAt: at })
await repo.putRecord('app.bsky.feed.post', '3kaaa', post('hello', '2026-01-01T00:00:00.000Z'))
await repo.putRecord('app.bsky.feed.post', '3kbbb', post('world', '2026-01-02T00:00:00.000Z'))
await repo.putRecord('app.bsky.actor.profile', 'self', { $type: 'app.bsky.actor.profile', displayName: 'T' })
t.alike(await repo.getRecord('app.bsky.feed.post', '3kaaa'), post('hello', '2026-01-01T00:00:00.000Z'), 'getRecord round-trips through files')
t.is(await repo.getRecord('app.bsky.feed.post', 'nope'), null, 'missing record → null')
const posts = await repo.listRecords('app.bsky.feed.post')
t.alike(posts.map((r) => r.rkey).sort(), ['3kaaa', '3kbbb'], 'listRecords scoped to a collection')
t.is((await repo.listRecords()).length, 3, 'listRecords all')
const snap = await repo.snapshot()
t.is(Object.keys(snap).length, 2, 'two collections in snapshot')
t.ok(snap['app.bsky.feed.post']['3kaaa'], 'snapshot nests collection/rkey/record')
await repo.deleteRecord('app.bsky.feed.post', '3kbbb')
t.is((await repo.listRecords('app.bsky.feed.post')).length, 1, 'delete removed the record file')
await store.close()
})
test('records written via RepoBase→adapter derive the same repo as a Hyperbee RepoStore', async (t) => {
const RECS = [
['app.bsky.actor.profile', 'self', { $type: 'app.bsky.actor.profile', displayName: 'T' }],
['app.bsky.feed.post', '3kaaa', { $type: 'app.bsky.feed.post', text: 'hello', createdAt: '2026-01-01T00:00:00.000Z' }],
['app.bsky.feed.post', '3kbbb', { $type: 'app.bsky.feed.post', text: 'world', createdAt: '2026-01-02T00:00:00.000Z' }]
]
const ov = await new OverlayRepoStore(tmp(t)).ready()
const rb = new RepoBase(new OverlayRepoSubstrate(ov), { did: DID })
for (const [c, r, rec] of RECS) await rb.putRecord(c, r, rec)
const hb = await new RepoStore(tmp(t)).ready()
for (const [c, r, rec] of RECS) await hb.put(c, r, rec)
const a = await getRepo(hb, META); const b = await getRepo(ov, META)
t.is(b.commitCid.toString(), a.commitCid.toString(), 'commit CID identical')
t.ok(b4a.equals(b.carBytes, a.carBytes), 'CAR byte-identical')
await ov.close(); await hb.close()
})
test('substrate is records-only — a reserved / no-separator key is rejected', async (t) => {
const store = await new OverlayRepoStore(tmp(t)).ready()
const sub = new OverlayRepoSubstrate(store)
await t.exception(() => sub.set('__lease__', { x: 1 }), /collection\/rkey/, 'reserved control key rejected')
await store.close()
})