import { cborEncodeDagCbor, cidToString, createCid } from '@pdsjs/core/repo'; import { describe, expect, it } from 'vitest'; import { createQueryEngine, fieldAt } from '../src/query.js'; const DID = 'did:plc:querytest'; const DEFS = 'dev.pdsjs.query.def'; /** * In-memory actorStorage over a mutable record set. Records are stored as * DAG-CBOR bytes, the same shape the real storage returns. * @param {Map} store - uri -> record value */ function storageOver(store) { const encoded = (/** @type {string} */ uri) => { const entry = store.get(uri); if (!entry) return null; const bytes = cborEncodeDagCbor(entry.value); return { bytes, cidOf: createCid(bytes) }; }; return /** @type {any} */ ({ listRecords: async ( /** @type {string} */ collection, /** @type {string|null} */ cursor, /** @type {number} */ limit, ) => { const uris = [...store.keys()] .filter((uri) => uri.includes(`/${collection}/`)) .sort(); const start = cursor ? Number(cursor) : 0; const page = uris.slice(start, start + limit); const records = []; for (const uri of page) { const row = encoded(uri); if (!row) continue; records.push({ uri, cid: cidToString(await row.cidOf), value: row.bytes, }); } const next = start + page.length; return { records, cursor: next < uris.length ? String(next) : null }; }, getRecord: async (/** @type {string} */ uri) => { const row = encoded(uri); if (!row) return null; return { cid: cidToString(await row.cidOf), value: row.bytes }; }, }); } /** @param {Record} defValue */ function withDef(defValue, extra = new Map()) { const store = new Map(extra); store.set(`at://${DID}/${DEFS}/test`, { value: { $type: DEFS, createdAt: '2026-08-10T00:00:00.000Z', ...defValue }, }); return store; } /** @param {Map} store */ function engineOver(store) { return createQueryEngine({ actorStorage: storageOver(store), getDid: async () => DID, }); } /** @param {ReturnType} engine @param {string} qs */ async function run(engine, qs) { const url = new URL(`https://pds.example.com/xrpc/dev.pdsjs.query.run${qs}`); const handler = engine.routes['/xrpc/dev.pdsjs.query.run'].handler; const response = await handler(new Request(url), url); return { status: response.status, body: await response.json() }; } /** Three notes with staggered TIDs and scores. */ function noteRecords() { const store = new Map(); const notes = [ { rkey: '3aaa', text: 'first', score: 3 }, { rkey: '3bbb', text: 'second', score: 1 }, { rkey: '3ccc', text: 'third', score: 2 }, ]; for (const note of notes) { store.set(`at://${DID}/com.example.note/${note.rkey}`, { value: { $type: 'com.example.note', text: note.text, meta: { score: note.score }, }, }); } return store; } describe('fieldAt', () => { it('walks dot paths and returns undefined off the map', () => { expect(fieldAt({ a: { b: 7 } }, 'a.b')).toBe(7); expect(fieldAt({ a: 1 }, 'a.b')).toBeUndefined(); expect(fieldAt(null, 'a')).toBeUndefined(); }); }); describe('dev.pdsjs.query.run', () => { it('requires a query name and 404s unknown ones', async () => { const engine = engineOver(withDef({ collection: 'com.example.note' })); expect((await run(engine, '')).status).toBe(400); expect((await run(engine, '?query=nope')).status).toBe(404); }); it('returns a whole collection newest-first by default', async () => { const engine = engineOver( withDef({ collection: 'com.example.note' }, noteRecords()), ); const { status, body } = await run(engine, '?query=test'); expect(status).toBe(200); expect(body.records.map((/** @type {any} */ r) => r.value.text)).toEqual([ 'third', 'second', 'first', ]); expect(body.cursor).toBeUndefined(); }); it('sorts by a dot-path field in either direction', async () => { const asc = engineOver( withDef( { collection: 'com.example.note', sort: { field: 'meta.score', direction: 'asc' }, }, noteRecords(), ), ); const { body } = await run(asc, '?query=test'); expect( body.records.map((/** @type {any} */ r) => r.value.meta.score), ).toEqual([1, 2, 3]); const desc = engineOver( withDef( { collection: 'com.example.note', sort: { field: 'meta.score', direction: 'desc' }, }, noteRecords(), ), ); const result = await run(desc, '?query=test'); expect( result.body.records.map((/** @type {any} */ r) => r.value.meta.score), ).toEqual([3, 2, 1]); }); it('filters on where equality over dot paths', async () => { const engine = engineOver( withDef( { collection: 'com.example.note', where: { 'meta.score': 2 } }, noteRecords(), ), ); const { body } = await run(engine, '?query=test'); expect(body.records).toHaveLength(1); expect(body.records[0].value.text).toBe('third'); }); it('pages with limit and an offset cursor', async () => { const engine = engineOver( withDef({ collection: 'com.example.note' }, noteRecords()), ); const first = await run(engine, '?query=test&limit=2'); expect(first.body.records).toHaveLength(2); expect(first.body.cursor).toBe('2'); const second = await run(engine, '?query=test&limit=2&cursor=2'); expect(second.body.records).toHaveLength(1); expect(second.body.cursor).toBeUndefined(); }); it('rejects a definition without a collection', async () => { const engine = engineOver(withDef({})); expect((await run(engine, '?query=test')).status).toBe(400); }); }); describe('onCommit maintenance', () => { it('applies creates, updates, and deletes to a loaded collection', async () => { const store = withDef({ collection: 'com.example.note' }, noteRecords()); const engine = engineOver(store); await run(engine, '?query=test'); const uri = `at://${DID}/com.example.note/3ddd`; store.set(uri, { value: { $type: 'com.example.note', text: 'fourth', meta: { score: 9 } }, }); await engine.onCommit({ did: DID, seq: 1, rev: 'rev1', ops: [{ action: 'create', uri, cid: 'bafycid' }], }); let result = await run(engine, '?query=test'); expect(result.body.records[0].value.text).toBe('fourth'); store.set(uri, { value: { $type: 'com.example.note', text: 'fourth-edited', meta: {} }, }); await engine.onCommit({ did: DID, seq: 2, rev: 'rev2', ops: [{ action: 'update', uri, cid: 'bafycid2' }], }); result = await run(engine, '?query=test'); expect(result.body.records[0].value.text).toBe('fourth-edited'); store.delete(uri); await engine.onCommit({ did: DID, seq: 3, rev: 'rev3', ops: [{ action: 'delete', uri, cid: null }], }); result = await run(engine, '?query=test'); expect(result.body.records).toHaveLength(3); }); it('leaves collections nobody queried alone', async () => { const store = withDef({ collection: 'com.example.note' }, noteRecords()); const storage = storageOver(store); let reads = 0; const counted = /** @type {any} */ ({ ...storage, getRecord: (/** @type {string} */ uri) => { reads += 1; return storage.getRecord(uri); }, }); const engine = createQueryEngine({ actorStorage: counted, getDid: async () => DID, }); await engine.onCommit({ did: DID, seq: 1, rev: 'rev1', ops: [ { action: 'create', uri: `at://${DID}/com.example.note/3zzz`, cid: 'bafycid', }, ], }); expect(reads).toBe(0); }); it('makes a def written after startup queryable at once', async () => { const store = withDef({ collection: 'com.example.note' }, noteRecords()); const engine = engineOver(store); await run(engine, '?query=test'); const uri = `at://${DID}/${DEFS}/scores`; store.set(uri, { value: { $type: DEFS, collection: 'com.example.note', sort: { field: 'meta.score', direction: 'asc' }, createdAt: '2026-08-10T00:00:00.000Z', }, }); await engine.onCommit({ did: DID, seq: 4, rev: 'rev4', ops: [{ action: 'create', uri, cid: 'bafydef' }], }); const { status, body } = await run(engine, '?query=scores'); expect(status).toBe(200); expect(body.records[0].value.meta.score).toBe(1); }); });