chadtmiller.com/pds.js
An atproto PDS in JavaScript, for Cloudflare Workers and Node
0
0
// @pdsjs/readonly/build-car tests
import { describe, expect, it } from 'vitest';
import { parseCarFile } from '../packages/core/src/car.js';
import { generateKeyPair } from '../packages/core/src/crypto.js';
import { loadRepositoryFromCar } from '../packages/core/src/loader.js';
import { cborDecode } from '../packages/core/src/repo.js';
import { buildRepositoryCar } from '../packages/readonly/src/build-car.js';
// Mock storage for loading the CAR
function createMockStorage() {
const blocks = new Map();
const records = new Map();
/** @type {any[]} */
const commits = [];
const metadata = { did: null };
return {
async getBlock(/** @type {any} */ cid) {
return blocks.get(cid) || null;
},
async putBlock(/** @type {any} */ cid, /** @type {any} */ data) {
blocks.set(cid, data);
},
async getRecord(/** @type {any} */ uri) {
const rec = records.get(uri);
return rec || null;
},
async putRecord(
/** @type {any} */ uri,
/** @type {any} */ cid,
/** @type {any} */ _collection,
/** @type {any} */ _rkey,
/** @type {any} */ value,
) {
records.set(uri, { cid, value });
},
async listRecords() {
return { records: [], cursor: null };
},
async listAllRecords() {
return [];
},
async deleteRecord() {},
async getLatestCommit() {
return commits.length > 0 ? commits[commits.length - 1] : null;
},
async putCommit(
/** @type {any} */ seq,
/** @type {any} */ cid,
/** @type {any} */ rev,
/** @type {any} */ prev,
) {
commits.push({ seq, cid, rev, prev });
},
async getEvents() {
return { events: [], cursor: 0 };
},
async putEvent() {},
async getBlob() {
return null;
},
async putBlob() {},
async deleteBlob() {},
async listBlobs() {
return { cids: [], cursor: null };
},
async getOrphanedBlobs() {
return [];
},
async linkBlobToRecord() {},
async unlinkBlobsFromRecord() {},
async getDid() {
return metadata.did;
},
async setDid(/** @type {any} */ did) {
metadata.did = did;
},
async getHandle() {
return null;
},
async setHandle() {},
async getPrivateKey() {
return null;
},
async setPrivateKey() {},
async getKemKey() {
return null;
},
async setKemKey() {},
async getPreferences() {
return [];
},
async setPreferences() {},
_getRecords() {
return records;
},
_getCommits() {
return commits;
},
};
}
describe('buildRepositoryCar', () => {
it('should build a valid CAR with a single record', async () => {
const { privateKey } = await generateKeyPair();
const did = 'did:plc:testbuild123';
const { carBytes, commitCid, rev } = await buildRepositoryCar({
did,
privateKey,
records: [
{
collection: 'app.bsky.feed.post',
rkey: '3abc123',
record: {
$type: 'app.bsky.feed.post',
text: 'Hello World!',
createdAt: '2024-01-01T00:00:00Z',
},
},
],
});
expect(carBytes).toBeInstanceOf(Uint8Array);
expect(carBytes.length).toBeGreaterThan(0);
expect(commitCid).toBeDefined();
expect(rev).toBeDefined();
});
it('should build a valid CAR with multiple records', async () => {
const { privateKey } = await generateKeyPair();
const did = 'did:plc:testmulti456';
const { carBytes, commitCid } = await buildRepositoryCar({
did,
privateKey,
records: [
{
collection: 'app.bsky.feed.post',
rkey: '3post1',
record: {
$type: 'app.bsky.feed.post',
text: 'Post 1',
createdAt: '2024-01-01T00:00:00Z',
},
},
{
collection: 'app.bsky.feed.post',
rkey: '3post2',
record: {
$type: 'app.bsky.feed.post',
text: 'Post 2',
createdAt: '2024-01-02T00:00:00Z',
},
},
{
collection: 'app.bsky.actor.profile',
rkey: 'self',
record: { $type: 'app.bsky.actor.profile', displayName: 'Test User' },
},
],
});
expect(carBytes).toBeInstanceOf(Uint8Array);
expect(commitCid).toBeDefined();
});
it('should produce a CAR that can be parsed', async () => {
const { privateKey } = await generateKeyPair();
const did = 'did:plc:testparse789';
const { carBytes, commitCid } = await buildRepositoryCar({
did,
privateKey,
records: [
{
collection: 'app.bsky.feed.post',
rkey: '3test',
record: {
$type: 'app.bsky.feed.post',
text: 'Test',
createdAt: '2024-01-01T00:00:00Z',
},
},
],
});
// Parse the CAR
const parsed = parseCarFile(carBytes);
expect(parsed.roots).toHaveLength(1);
expect(parsed.roots[0]).toBe(commitCid);
expect(parsed.blocks.size).toBeGreaterThan(0);
// Verify commit structure
const commitBytes = parsed.blocks.get(commitCid);
expect(commitBytes).toBeDefined();
const commit = cborDecode(/** @type {Uint8Array} */ (commitBytes));
expect(commit.did).toBe(did);
expect(commit.version).toBe(3);
expect(commit.sig).toBeDefined();
expect(commit.sig).toBeInstanceOf(Uint8Array);
expect(commit.sig.length).toBe(64); // ECDSA P-256 signature
});
it('should produce a CAR that can be loaded into storage', async () => {
const { privateKey } = await generateKeyPair();
const did = 'did:plc:testload101';
const { carBytes } = await buildRepositoryCar({
did,
privateKey,
records: [
{
collection: 'app.bsky.feed.post',
rkey: '3loadtest',
record: {
$type: 'app.bsky.feed.post',
text: 'Load Test',
createdAt: '2024-01-01T00:00:00Z',
},
},
],
});
// Load into storage
const storage = createMockStorage();
const result = await loadRepositoryFromCar(
carBytes,
/** @type {any} */ (storage),
);
expect(result.did).toBe(did);
expect(result.recordCount).toBe(1);
expect(await storage.getDid()).toBe(did);
// Verify record was stored
const record = await storage.getRecord(
`at://${did}/app.bsky.feed.post/3loadtest`,
);
expect(record).not.toBeNull();
expect(record?.cid).toBeDefined();
// Verify commit was stored
const commit = await storage.getLatestCommit();
expect(commit).not.toBeNull();
expect(commit?.cid).toBe(result.commitCid);
});
it('should handle empty records gracefully', async () => {
const { privateKey } = await generateKeyPair();
const did = 'did:plc:testempty';
// Even with no records, should produce valid CAR with just commit
const { carBytes, commitCid } = await buildRepositoryCar({
did,
privateKey,
records: [],
});
expect(carBytes).toBeInstanceOf(Uint8Array);
expect(commitCid).toBeDefined();
const parsed = parseCarFile(carBytes);
expect(parsed.roots[0]).toBe(commitCid);
});
it('should produce deterministic MST structure', async () => {
const { privateKey } = await generateKeyPair();
const did = 'did:plc:testdeterministic';
// Records in different order should produce same MST
const records = [
{ collection: 'c/b', rkey: 'z', record: { val: 3 } },
{ collection: 'a/a', rkey: 'x', record: { val: 1 } },
{ collection: 'b/a', rkey: 'y', record: { val: 2 } },
];
const { carBytes: car1 } = await buildRepositoryCar({
did,
privateKey,
records: [...records],
});
// Reverse order
const { carBytes: car2 } = await buildRepositoryCar({
did,
privateKey,
records: [...records].reverse(),
});
// Parse both and compare MST structure (not commits - revisions differ)
const parsed1 = parseCarFile(car1);
const parsed2 = parseCarFile(car2);
// Both should have same number of blocks (records + MST nodes + commit)
expect(parsed1.blocks.size).toBe(parsed2.blocks.size);
});
});