chadtmiller.com/pds.js
An atproto PDS in JavaScript, for Cloudflare Workers and Node
0
0
// @pdsjs/core/car tests - CAR file parser
import { describe, expect, it } from 'vitest';
import {
getCarHeader,
iterateCarBlocks,
parseCarFile,
readVarint,
} from '../packages/core/src/car.js';
import {
buildCarFile,
cborEncodeDagCbor,
cidToString,
createCid,
} from '../packages/core/src/repo.js';
describe('readVarint', () => {
it('should decode single-byte varints', () => {
// 0x00 = 0
expect(readVarint(new Uint8Array([0x00]), 0)).toEqual([0, 1]);
// 0x01 = 1
expect(readVarint(new Uint8Array([0x01]), 0)).toEqual([1, 1]);
// 0x7f = 127
expect(readVarint(new Uint8Array([0x7f]), 0)).toEqual([127, 1]);
});
it('should decode multi-byte varints', () => {
// 0x80 0x01 = 128
expect(readVarint(new Uint8Array([0x80, 0x01]), 0)).toEqual([128, 2]);
// 0xff 0x01 = 255
expect(readVarint(new Uint8Array([0xff, 0x01]), 0)).toEqual([255, 2]);
// 0xac 0x02 = 300
expect(readVarint(new Uint8Array([0xac, 0x02]), 0)).toEqual([300, 2]);
});
it('should decode varint at offset', () => {
const bytes = new Uint8Array([0xff, 0x7f, 0xac, 0x02, 0x00]);
expect(readVarint(bytes, 2)).toEqual([300, 4]);
});
it('should throw on unterminated varint', () => {
expect(() => readVarint(new Uint8Array([0x80]), 0)).toThrow(
'Unexpected end of varint',
);
});
});
describe('parseCarFile', () => {
it('should roundtrip with buildCarFile - single block', async () => {
// Create a test block
const record = { $type: 'app.bsky.feed.post', text: 'Hello World' };
const recordBytes = cborEncodeDagCbor(record);
const recordCid = cidToString(await createCid(recordBytes));
// Build CAR file
const car = buildCarFile(recordCid, [
{ cid: recordCid, data: recordBytes },
]);
// Parse it back
const parsed = parseCarFile(car);
expect(parsed.roots).toHaveLength(1);
expect(parsed.roots[0]).toBe(recordCid);
expect(parsed.blocks.size).toBe(1);
expect(parsed.blocks.has(recordCid)).toBe(true);
expect(parsed.blocks.get(recordCid)).toEqual(recordBytes);
});
it('should roundtrip with buildCarFile - multiple blocks', async () => {
// Create multiple test blocks
const block1 = cborEncodeDagCbor({ text: 'Block 1' });
const cid1 = cidToString(await createCid(block1));
const block2 = cborEncodeDagCbor({ text: 'Block 2' });
const cid2 = cidToString(await createCid(block2));
const block3 = cborEncodeDagCbor({ text: 'Block 3' });
const cid3 = cidToString(await createCid(block3));
// Build CAR file with first block as root
const car = buildCarFile(cid1, [
{ cid: cid1, data: block1 },
{ cid: cid2, data: block2 },
{ cid: cid3, data: block3 },
]);
// Parse it back
const parsed = parseCarFile(car);
expect(parsed.roots).toHaveLength(1);
expect(parsed.roots[0]).toBe(cid1);
expect(parsed.blocks.size).toBe(3);
expect(parsed.blocks.get(cid1)).toEqual(block1);
expect(parsed.blocks.get(cid2)).toEqual(block2);
expect(parsed.blocks.get(cid3)).toEqual(block3);
});
it('should throw on invalid CAR version', () => {
// Create a malformed CAR with version 2
const invalidHeader = cborEncodeDagCbor({ version: 2, roots: [] });
const headerLenBytes = new Uint8Array([invalidHeader.length]);
const car = new Uint8Array(headerLenBytes.length + invalidHeader.length);
car.set(headerLenBytes, 0);
car.set(invalidHeader, headerLenBytes.length);
expect(() => parseCarFile(car)).toThrow('Unsupported CAR version: 2');
});
});
describe('iterateCarBlocks', () => {
it('should iterate blocks in order', async () => {
// Create test blocks
const block1 = cborEncodeDagCbor({ index: 1 });
const cid1 = cidToString(await createCid(block1));
const block2 = cborEncodeDagCbor({ index: 2 });
const cid2 = cidToString(await createCid(block2));
// Build CAR file
const car = buildCarFile(cid1, [
{ cid: cid1, data: block1 },
{ cid: cid2, data: block2 },
]);
// Iterate blocks
const blocks = [];
for (const block of iterateCarBlocks(car)) {
blocks.push(block);
}
expect(blocks).toHaveLength(2);
expect(blocks[0].cid).toBe(cid1);
expect(blocks[0].data).toEqual(block1);
expect(blocks[1].cid).toBe(cid2);
expect(blocks[1].data).toEqual(block2);
});
});
describe('getCarHeader', () => {
it('should extract header without parsing blocks', async () => {
// Create a large block (to verify we don't parse it)
const largeData = cborEncodeDagCbor({ data: 'x'.repeat(10000) });
const largeCid = cidToString(await createCid(largeData));
// Build CAR file
const car = buildCarFile(largeCid, [{ cid: largeCid, data: largeData }]);
// Get header only
const header = getCarHeader(car);
expect(header.version).toBe(1);
expect(header.roots).toHaveLength(1);
expect(header.roots[0]).toBe(largeCid);
});
});