OverviewFilesCommitsIssuesDocsReleasesPeers

favela-web / test / repair-bytes.test.mjs

147 lines · javascript ref:
// repair-bytes — filling the byte gap the 0.3.36 create-gate left behind.
//
// The engine is fully injected (refs, store, fetch, verify), so every branch here runs with no network,
// no corestore and no daemon. What is under test is the POLICY, which is the part that can hurt:
//
//   - only ABSENCES are filled — a record with bytes is never rewritten (this is what makes the repair
//     safe to run against hpp's own canonical files)
//   - a dry run touches NOTHING
//   - unverifiable bytes are never written, however plausible they look
//   - bytes that do not survive decode->encode are never written — that round trip is the 2026-07-30
//     outage, and a repair that reintroduced it would be worse than the gap it closes
//
// Run: node test/repair-bytes.test.mjs
import { repairBytes } from '../lib/repair-bytes.js'
import * as dagCbor from '@ipld/dag-cbor'

const assert = (cond, msg) => { if (!cond) throw new Error('FAIL: ' + msg); console.log('  ok -', msg) }
const DID = 'did:plc:wj62ebbusln6gxo5azo6ogzo'
const COLL = 'app.bsky.feed.post'

// A canonical record: dag-cbor round-trips it byte-identically, which is what the real bytes do.
const canonical = { $type: COLL, text: 'from the peer', createdAt: '2026-08-03T10:00:00.000Z', langs: ['en'] }
const canonicalBytes = dagCbor.encode(canonical)

// A store where `have` already has bytes and everything else is a hole.
function fakeStore (have = {}) {
  const written = {}
  return {
    written,
    async getRaw (collection, rkey) {
      const k = collection + '/' + rkey
      if (written[k]) return { bytes: dagCbor.encode(written[k]) }
      return have[k] ? { bytes: have[k] } : null
    },
    async put (collection, rkey, value) { written[collection + '/' + rkey] = value }
  }
}
const refsFor = (rkeys) => async function * () { for (const rkey of rkeys) yield { did: DID, rkey, collection: COLL } }
const okFetch = async (p) => ({ bytes: canonicalBytes, commit: {}, identity: {}, pointer: p })
const okVerify = async () => true

console.log('# a DRY RUN finds the gap and touches nothing')
{
  const store = fakeStore({ [COLL + '/has-bytes']: canonicalBytes })
  const r = await repairBytes({
    dids: [DID], listRefs: refsFor(['has-bytes', 'gap1', 'gap2']), openStore: async () => store,
    fetchRecord: okFetch, verifyRecord: okVerify, write: false
  })
  assert(r.scanned === 3, 'scanned every indexed ref (3)')
  assert(r.present === 1, 'one record already had bytes')
  assert(r.missing === 2, 'two are indexed with NO bytes — the defect footprint')
  assert(r.written === 0, 'a dry run writes nothing')
  assert(Object.keys(store.written).length === 0, 'and the store was not touched at all')
  assert(r.samples.length === 2 && r.samples[0].rkey === 'gap1', 'the gaps are named, so a dry run is actionable')
}

console.log('\n# --write fills ONLY the holes')
{
  const store = fakeStore({ [COLL + '/has-bytes']: canonicalBytes })
  const r = await repairBytes({
    dids: [DID], listRefs: refsFor(['has-bytes', 'gap1', 'gap2']), openStore: async () => store,
    fetchRecord: okFetch, verifyRecord: okVerify, write: true
  })
  assert(r.written === 2, 'both gaps were fetched, verified and written')
  assert(store.written[COLL + '/gap1'].text === 'from the peer', 'the canonical record landed')
  assert(store.written[COLL + '/has-bytes'] === undefined, 'the record that ALREADY had bytes was never rewritten')
}

console.log('\n# a record no peer can produce is counted, not invented')
{
  const store = fakeStore()
  const r = await repairBytes({
    dids: [DID], listRefs: refsFor(['lost']), openStore: async () => store,
    fetchRecord: async () => null, verifyRecord: okVerify, write: true
  })
  assert(r.unfetchable === 1 && r.written === 0, 'unfetchable — the 2026-07-03 blob case, reported not faked')
  assert(Object.keys(store.written).length === 0, 'nothing written')
}

console.log('\n# ⛔ bytes that FAIL verification are never written')
{
  const store = fakeStore()
  const r = await repairBytes({
    dids: [DID], listRefs: refsFor(['forged']), openStore: async () => store,
    fetchRecord: okFetch, verifyRecord: async () => false, write: true
  })
  assert(r.unverified === 1 && r.written === 0, 'a failed proof is refused — the key comes from the DID doc, not the server')
  assert(Object.keys(store.written).length === 0, 'and nothing reached the store')
}

console.log('\n# ⛔ bytes that do not survive decode->encode are refused (the 2026-07-30 shape)')
{
  // Non-canonical CBOR for the same value: a map encoded with a longer length header than necessary.
  // It decodes fine and re-encodes to something SHORTER — exactly the class of drift that made a
  // converged pointer unresolvable. The engine must notice and refuse.
  const nonCanonical = new Uint8Array([0xb9, 0x00, 0x01, 0x61, 0x61, 0x01]) // {"a":1} with a 2-byte map header
  const store = fakeStore()
  const r = await repairBytes({
    dids: [DID], listRefs: refsFor(['noncanon']), openStore: async () => store,
    fetchRecord: async () => ({ bytes: nonCanonical, commit: {}, identity: {} }), verifyRecord: okVerify, write: true
  })
  assert(r.reencodeRefused === 1 && r.written === 0, 'a re-encode that differs from the verified bytes is refused')
  assert(Object.keys(store.written).length === 0, 'the store never sees bytes that would not match their CID')
}

console.log('\n# --limit caps the work')
{
  const store = fakeStore()
  const r = await repairBytes({
    dids: [DID], listRefs: refsFor(['a', 'b', 'c', 'd']), openStore: async () => store,
    fetchRecord: okFetch, verifyRecord: okVerify, write: true, limit: 2
  })
  assert(r.written === 2, 'stopped after 2 attempts — a big backlog can be drained in bounded passes')
}

console.log('\n# a ref with no collection cannot be located or asked for — counted, never guessed')
{
  const store = fakeStore()
  const listRefs = async function * () { yield { did: DID, rkey: 'old', collection: null } }
  const r = await repairBytes({ dids: [DID], listRefs, openStore: async () => store, fetchRecord: okFetch, verifyRecord: okVerify, write: true })
  assert(r.scanned === 1 && r.storeErrors === 1 && r.written === 0, 'an unlocatable ref is an error, not a silent skip')
}

console.log('\n# a store that will not open does not abort the whole run')
{
  const good = fakeStore()
  const r = await repairBytes({
    dids: ['did:plc:broken', DID],
    listRefs: refsFor(['gap1']),
    openStore: async (did) => { if (did === 'did:plc:broken') throw new Error('locked'); return good },
    fetchRecord: okFetch, verifyRecord: okVerify, write: true
  })
  assert(r.storeErrors === 1, 'the broken DID is reported')
  assert(r.written === 1, 'and the healthy one is still repaired')
}

console.log('\n# --write demands the fetch/verify pair — a half-wired caller fails loudly')
{
  let threw = false
  try {
    await repairBytes({ dids: [DID], listRefs: refsFor(['x']), openStore: async () => fakeStore(), write: true })
  } catch { threw = true }
  assert(threw, 'writing without a verifier is refused at the door')
}

console.log('\nall repair-bytes assertions passed')