devin ivy 331a356ce2
Lexicon resolver package (#4069)
* lexicon: doc validation compatibility with published lexicons

* lexicon-resolver: setup new package

* lexicon-resolver: implement record resolution

* lexicon-resolver: implement lexicon resolution

* lexicon-resolver: test record resolution

* repo: add option to verify CIDs found in CARs. tidy.

* lexicon-resolution: verify CIDs in proof CAR

* lexicon-resolution: tests and fixes

* tidy

* lexicon-resolution: add entrypoint

* lexicon-resolver: tidy errors

* lexicon-resolver: readme

* lexicon-resolver: changeset

* prettier

* eslint

* tidy

* tidy

* tidy

* enable CID-to-content verification within CARs by default

* lexicon-resolver: tidy types, application of defaults, gitattributes

* lexicon-resolver: add interface and builder fn for lexicon and record resolvers

* lexicon-resolver: update readme

* tidy

* lexicon-resolver: cover error cases in record resolution

---------

Co-authored-by: Matthieu Sieben <matthieu.sieben@gmail.com>
2025-08-17 22:45:51 -04:00

106 lines
3.5 KiB
TypeScript

import { CID } from 'multiformats/cid'
import * as ui8 from 'uint8arrays'
import { dataToCborBlock, streamToBytes, wait } from '@atproto/common'
import { CarBlock, readCarStream, writeCarStream } from '../src'
import fixtures from './car-file-fixtures.json'
describe('car', () => {
for (const fixture of fixtures) {
it('correctly writes car files', async () => {
const root = CID.parse(fixture.root)
async function* blockIter() {
for (const block of fixture.blocks) {
const cid = CID.parse(block.cid)
const bytes = ui8.fromString(block.bytes, 'base64')
yield { cid, bytes }
}
}
const carStream = writeCarStream(root, blockIter())
const car = await streamToBytes(carStream)
const carB64 = ui8.toString(car, 'base64')
expect(carB64).toEqual(fixture.car)
})
it('correctly reads carfiles', async () => {
const carStream = [ui8.fromString(fixture.car, 'base64')]
const { roots, blocks } = await readCarStream(carStream)
expect(roots.length).toBe(1)
expect(roots[0].toString()).toEqual(fixture.root)
const carBlocks: CarBlock[] = []
for await (const block of blocks) {
carBlocks.push(block)
}
expect(carBlocks.length).toEqual(fixture.blocks.length)
for (let i = 0; i < carBlocks.length; i++) {
expect(carBlocks[i].cid.toString()).toEqual(fixture.blocks[i].cid)
expect(ui8.toString(carBlocks[i].bytes, 'base64')).toEqual(
fixture.blocks[i].bytes,
)
}
})
}
it('writeCar propagates errors', async () => {
const iterate = async () => {
async function* blockIterator() {
await wait(1)
const block = await dataToCborBlock({ test: 1 })
yield block
throw new Error('Oops!')
}
const iter = writeCarStream(null, blockIterator())
for await (const _bytes of iter) {
// no-op
}
}
await expect(iterate).rejects.toThrow('Oops!')
})
it('verifies CIDs', async () => {
const block0 = await dataToCborBlock({ block: 0 })
const block1 = await dataToCborBlock({ block: 1 })
const block2 = await dataToCborBlock({ block: 2 })
const block3 = await dataToCborBlock({ block: 3 })
const badBlock = await dataToCborBlock({ block: 'bad' })
const blockIter = async function* () {
yield block0
yield block1
yield block2
yield { cid: block3.cid, bytes: badBlock.bytes }
}
const flush = async function (iter: AsyncIterable<unknown>) {
for await (const _ of iter) {
// no-op
}
}
const badCar = await readCarStream(writeCarStream(block0.cid, blockIter()))
await expect(flush(badCar.blocks)).rejects.toThrow(
'Not a valid CID for bytes',
)
})
it('skips CID verification', async () => {
const block0 = await dataToCborBlock({ block: 0 })
const block1 = await dataToCborBlock({ block: 1 })
const block2 = await dataToCborBlock({ block: 2 })
const block3 = await dataToCborBlock({ block: 3 })
const badBlock = await dataToCborBlock({ block: 'bad' })
const blockIter = async function* () {
yield block0
yield block1
yield block2
yield { cid: block3.cid, bytes: badBlock.bytes }
}
const flush = async function (iter: AsyncIterable<unknown>) {
for await (const _ of iter) {
// no-op
}
}
const badCar = await readCarStream(
writeCarStream(block0.cid, blockIter()),
{ skipCidVerification: true },
)
await expect(flush(badCar.blocks)).resolves.toBeUndefined()
})
})