// Slice 1 (Holepunch-first): the classic-hyperbee RepoStore, verified against the
// MST oracle. The point: records stored + read back through real Hyperbee produce
// the SAME MST root as building directly from the records — so the native store is a
// faithful, byte-transparent home, and the bridge (atproto-repo) can serialize it.
import test from 'brittle'
import { readFileSync, mkdtempSync, rmSync } from 'node:fs'
import { fileURLToPath } from 'node:url'
import { dirname, join } from 'node:path'
import { tmpdir } from 'node:os'
import * as dagCbor from '@ipld/dag-cbor'
import { RepoStore } from '../lib/repo-store.js'
// bridge oracle (cross-module; pure-JS path):
import { buildMst } from 'atproto-repo/lib/mst.js'
import { cidForBytesSync } from 'atproto-repo/lib/cid.js'
import { readCar, loadCommit, listRecords } from 'atproto-repo/lib/repo.js'
const here = dirname(fileURLToPath(import.meta.url))
const goldenCar = readFileSync(join(here, 'vectors', 'bridgetest-repo.car'))
// records [{key, bytes}] -> MST root CID (this is what the bridge will do)
const mstRootOf = (recs) => buildMst(recs.map(r => ({ key: r.key, value: cidForBytesSync(r.bytes) }))).root
const splitKey = (k) => { const i = k.indexOf('/'); return [k.slice(0, i), k.slice(i + 1)] }
const newStore = async (t) => {
const dir = mkdtempSync(join(tmpdir(), 'repostore-'))
const s = await new RepoStore(dir).ready()
t.teardown(async () => { try { await s.close() } catch {} rmSync(dir, { recursive: true, force: true }) })
return s
}
// 1. round-trip
test('put -> get round-trips a record', async (t) => {
const s = await newStore(t)
const rec = { $type: 'app.bsky.feed.post', text: 'hello', createdAt: '2026-07-01T00:00:00.000Z' }
await s.put('app.bsky.feed.post', '3aaa', rec)
const got = await s.get('app.bsky.feed.post', '3aaa')
// compare canonical DAG-CBOR bytes (JSON.stringify is key-order-sensitive; dag-cbor canonicalises)
t.ok(Buffer.from(dagCbor.encode(got)).equals(Buffer.from(dagCbor.encode(rec))),
'put -> get round-trips a record')
})
// 2. delete
test('del removes a record', async (t) => {
const s = await newStore(t)
await s.put('app.bsky.feed.post', '3aaa', { $type: 'x', text: 'a' })
await s.del('app.bsky.feed.post', '3aaa')
const got = await s.get('app.bsky.feed.post', '3aaa')
t.is(got, null, 'del removes a record')
})
// 3. list by collection + records() ordering
test('list scopes to a collection; records() is key-ordered', async (t) => {
const s = await newStore(t)
await s.put('app.bsky.feed.post', '3bbb', { text: 'b' })
await s.put('app.bsky.feed.post', '3aaa', { text: 'a' })
await s.put('app.bsky.actor.profile', 'self', { displayName: 'me' })
const posts = []
for await (const r of s.list('app.bsky.feed.post')) posts.push(r.rkey)
const allKeys = (await s.records()).map(r => r.key)
const listOk = JSON.stringify(posts) === JSON.stringify(['3aaa', '3bbb'])
const orderedOk = JSON.stringify(allKeys) === JSON.stringify([...allKeys].sort())
const scopedOk = !posts.includes('self')
t.ok(listOk && orderedOk && scopedOk, 'list scopes to a collection; records() is key-ordered')
})
// 4. ORACLE A — real golden repo: records through the store -> golden MST root
test('golden repo records via store -> MST root == commit.data', async (t) => {
const car = await readCar(goldenCar)
const commitData = loadCommit(car).value.data
const s = await newStore(t)
for (const r of listRecords(car)) { const [c, rk] = splitKey(r.key); await s.put(c, rk, r.value) }
const root = mstRootOf(await s.records())
t.ok(root.equals(commitData), 'golden repo records via store -> MST root == commit.data')
})
// 5. ORACLE B — store is byte-transparent to MST building (multi-record)
test('store->MST root == direct->MST root (byte-transparent)', async (t) => {
const recs = []
for (let i = 0; i < 40; i++) recs.push({ key: `app.test.record/${String(i).padStart(4, '0')}`, record: { $type: 'app.test.record', n: i, note: `row ${i}` } })
const s = await newStore(t)
for (const r of recs) { const [c, rk] = splitKey(r.key); await s.put(c, rk, r.record) }
const storeRoot = mstRootOf(await s.records())
const directRoot = mstRootOf(recs.map(r => ({ key: r.key, bytes: dagCbor.encode(r.record) })))
t.ok(storeRoot.equals(directRoot), 'store->MST root == direct->MST root (byte-transparent)')
})