// The query gate in createServer: experimental.sites.queries composes the engine's route // and commit tap in; absent, the endpoint does not exist. Driven through // pds.fetch and the real write path, so the tap itself is covered too. import { mkdtempSync, rmSync } from 'node:fs'; import { tmpdir } from 'node:os'; import { join } from 'node:path'; import { afterEach, describe, expect, it } from 'vitest'; import { createServer } from '../src/index.js'; const DID = 'did:plc:querywiringtestaccountaa'; const PASSWORD = 'wiring-test-password'; const BASE = 'https://pds.example.com'; const RUN = `${BASE}/xrpc/dev.pdsjs.query.run`; /** @type {Array<{close: () => Promise}>} */ const servers = []; /** @type {string[]} */ const dirs = []; async function makeServer(opts = {}) { const dir = mkdtempSync(join(tmpdir(), 'pdsjs-query-wiring-')); dirs.push(dir); const server = await createServer({ dbPath: join(dir, 'pds.db'), blobsDir: join(dir, 'blobs'), jwtSecret: 'wiring-test-secret', hostname: 'pds.example.com', password: PASSWORD, ...opts, }); servers.push(server); return server; } /** @param {import('@pdsjs/core').PersonalDataServer} pds */ async function initAccount(pds) { const init = await pds.fetch( new Request(`${BASE}/init?did=${DID}`, { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ did: DID, privateKey: Buffer.from( crypto.getRandomValues(new Uint8Array(32)), ).toString('hex'), handle: 'query.test', password: PASSWORD, }), }), ); expect(init.ok).toBe(true); const session = await pds.fetch( new Request(`${BASE}/xrpc/com.atproto.server.createSession`, { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ identifier: DID, password: PASSWORD }), }), ); const { accessJwt } = await session.json(); return { Authorization: `Bearer ${accessJwt}` }; } /** * @param {import('@pdsjs/core').PersonalDataServer} pds * @param {Record} auth * @param {string} collection * @param {string} rkey * @param {Record} record */ async function put(pds, auth, collection, rkey, record) { const response = await pds.fetch( new Request(`${BASE}/xrpc/com.atproto.repo.putRecord`, { method: 'POST', headers: { ...auth, 'Content-Type': 'application/json' }, body: JSON.stringify({ repo: DID, collection, rkey, record: { $type: collection, ...record }, }), }), ); expect(response.ok).toBe(true); } afterEach(async () => { for (const server of servers.splice(0)) { await server.close().catch(() => {}); } for (const dir of dirs.splice(0)) rmSync(dir, { recursive: true, force: true }); }); describe('node query wiring', () => { it('serves no query endpoint without experimental.sites.queries', async () => { const { pds } = await makeServer(); const response = await pds.fetch(new Request(`${RUN}?query=x`)); expect(response.status).toBe(501); }); it('answers queries and sees writes through the commit tap', async () => { const { pds } = await makeServer({ experimental: { sites: { queries: true } }, }); const auth = await initAccount(pds); await put(pds, auth, 'dev.pdsjs.query.def', 'notes', { collection: 'com.example.note', sort: { field: 'title', direction: 'asc' }, createdAt: new Date().toISOString(), }); await put(pds, auth, 'com.example.note', '3aaa', { title: 'beta' }); const first = await pds.fetch(new Request(`${RUN}?query=notes`)); expect(first.status).toBe(200); expect(first.headers.get('access-control-allow-origin')).toBe('*'); const body = await first.json(); expect(body.records.map((/** @type {any} */ r) => r.value.title)).toEqual([ 'beta', ]); // A write after the collection is cached arrives via the tap await put(pds, auth, 'com.example.note', '3bbb', { title: 'alpha' }); const second = await pds.fetch(new Request(`${RUN}?query=notes`)); const updated = await second.json(); expect( updated.records.map((/** @type {any} */ r) => r.value.title), ).toEqual(['alpha', 'beta']); }); });