import assert from 'node:assert/strict'; import test from 'node:test'; import { ParseRestClient, ParseRestError } from '../src/db/parse-rest.client.js'; test('Parse REST client keeps the master key server-side and encodes structured queries', async () => { const calls: Array<{ url: string; init: RequestInit }> = []; const fetchMock: typeof fetch = async (input, init = {}) => { calls.push({ url: String(input), init }); return new Response(JSON.stringify({ results: [] }), { status: 200, headers: { 'Content-Type': 'application/json' }, }); }; const client = new ParseRestClient({ serverUrl: 'https://parse.example.test/parse/', appId: 'app-id', masterKey: 'master-secret', }, fetchMock); await client.find('VocProduct', { where: { workspaceId: 'demashi', platform: 'jd' }, order: 'productKey', limit: 25, }); await client.request('/health', { master: false }); const queryUrl = new URL(calls[0]!.url); assert.equal(queryUrl.pathname, '/parse/classes/VocProduct'); assert.deepEqual(JSON.parse(queryUrl.searchParams.get('where')!), { workspaceId: 'demashi', platform: 'jd' }); assert.equal(queryUrl.searchParams.get('order'), 'productKey'); assert.equal(queryUrl.searchParams.get('limit'), '25'); assert.equal((calls[0]!.init.headers as Record)['X-Parse-Master-Key'], 'master-secret'); assert.equal((calls[0]!.init.headers as Record)['Cache-Control'], 'no-cache, no-store, max-age=0'); assert.equal((calls[0]!.init.headers as Record).Pragma, 'no-cache'); assert.equal('X-Parse-Master-Key' in (calls[1]!.init.headers as Record), false); }); test('Parse REST batch requests stay under the upstream limit and preserve the mount path', async () => { let requestBody: { requests: Array<{ path: string }> } | null = null; const fetchMock: typeof fetch = async (_input, init = {}) => { requestBody = JSON.parse(String(init.body)) as { requests: Array<{ path: string }> }; return new Response(JSON.stringify([{ success: { objectId: 'one' } }]), { status: 200, headers: { 'Content-Type': 'application/json' }, }); }; const client = new ParseRestClient({ serverUrl: 'https://parse.example.test/custom/parse', appId: 'app-id', masterKey: 'master-secret', }, fetchMock); await client.batch([{ method: 'POST', path: '/classes/VocWorkspace', body: { publicId: 'demashi' } }]); assert.equal(requestBody!.requests[0]!.path, '/custom/parse/classes/VocWorkspace'); await assert.rejects( () => client.batch(Array.from({ length: 51 }, () => ({ method: 'DELETE' as const, path: '/classes/VocProduct/id' }))), /limited to 50/, ); }); test('Parse REST errors expose status and Parse code without returning raw response text', async () => { const client = new ParseRestClient({ serverUrl: 'https://parse.example.test/parse', appId: 'app-id', masterKey: 'master-secret', }, async () => new Response(JSON.stringify({ code: 119, error: 'Permission denied' }), { status: 400, headers: { 'Content-Type': 'application/json' }, })); await assert.rejects( () => client.find('VocWorkspace'), (error: unknown) => error instanceof ParseRestError && error.status === 400 && error.code === 119 && error.message === 'Permission denied', ); }); test('Parse REST schema writes retry without required flags for older managed servers', async () => { const bodies: Array<{ fields: Record; indexes?: Record>; }> = []; const client = new ParseRestClient({ serverUrl: 'https://parse.example.test/parse', appId: 'app-id', masterKey: 'master-secret', }, async (_input, init = {}) => { bodies.push(JSON.parse(String(init.body)) as { fields: Record; indexes?: Record>; }); if (bodies.length === 1) { return new Response(JSON.stringify({ error: 'unauthorized' }), { status: 403, headers: { 'Content-Type': 'application/json' }, }); } return new Response(JSON.stringify({ className: 'VocWorkspace' }), { status: 200, headers: { 'Content-Type': 'application/json' }, }); }); await client.createSchema({ className: 'VocWorkspace', fields: { publicId: { type: 'String', required: true }, name: { type: 'String', required: false }, }, indexes: { voc_workspace_publicid_idx: { publicId: 1 } }, }); assert.equal(bodies.length, 2); assert.equal(bodies[0]!.fields.publicId!.required, true); assert.equal('required' in bodies[1]!.fields.publicId!, false); assert.equal('required' in bodies[1]!.fields.name!, false); assert.deepEqual(bodies[1]!.indexes, { voc_workspace_publicid_idx: { publicId: 1 } }); }); test('Parse REST schema index updates use the managed schema endpoint', async () => { let request: { url: string; method: string | undefined; body: string | undefined } | null = null; const client = new ParseRestClient({ serverUrl: 'https://parse.example.test/parse', appId: 'app-id', masterKey: 'master-secret', }, async (input, init = {}) => { request = { url: String(input), method: init.method, body: String(init.body) }; return new Response(JSON.stringify({ className: 'VocProduct' }), { status: 200, headers: { 'Content-Type': 'application/json' }, }); }); await client.addSchemaIndexes('VocProduct', { voc_product_naturalkey_idx: { naturalKey: 1 }, }); assert.equal(request!.url, 'https://parse.example.test/parse/schemas/VocProduct'); assert.equal(request!.method, 'PUT'); assert.deepEqual(JSON.parse(request!.body!), { indexes: { voc_product_naturalkey_idx: { naturalKey: 1 } }, }); }); test('Parse REST class requests retry transient master authorization drift', async () => { let attempts = 0; const client = new ParseRestClient({ serverUrl: 'https://parse.example.test/parse', appId: 'app-id', masterKey: 'master-secret', }, async () => { attempts += 1; if (attempts < 3) { return new Response(JSON.stringify({ error: 'unauthorized' }), { status: 403, headers: { 'Content-Type': 'application/json' }, }); } return new Response(JSON.stringify({ results: [], count: 0 }), { status: 200, headers: { 'Content-Type': 'application/json' }, }); }); const result = await client.find('VocWorkspace', { count: true }); assert.equal(attempts, 3); assert.equal(result.count, 0); });