import test from 'node:test'; import assert from 'node:assert/strict'; import { createOperations } from '../operations.js'; const file = { name: 'fixture.png', base64: 'YWJj' }; function sample(schema) { if (Array.isArray(schema.type)) return sample({ ...schema, type: schema.type[0] }); if (schema.const !== undefined) return schema.const; if (schema.enum) return schema.enum[0]; if (schema.anyOf) return sample(schema.anyOf[0]); if (schema.pattern?.includes('d{4}')) return '2026-09-22'; if (schema.type === 'object') return Object.fromEntries((schema.required ?? []).map(k => [k, sample(schema.properties[k])])); if (schema.type === 'array') return [sample(schema.items)]; if (schema.type === 'number' || schema.type === 'integer') return 1; if (schema.type === 'boolean') return false; if (schema.type === 'string') return 'fixture'; return {}; } function setup() { const requests = [], uploads = []; const client = { resolveFrameId() { throw new Error('Writes must not auto-discover frames'); }, async request(method, path, options) { requests.push({ method, path, options }); if (path.endsWith('cloud_upload_credentials')) return { data: { credentials: { access_key_id: 'test', secret_access_key: 'test', session_token: 'test' }, region: 'us-east-1', bucket: 'test-bucket', key_prefix: 'test/' } }; if (method === 'GET' && path.endsWith('/list_items')) return { data: [{ id: 'fixture', type: 'item', attributes: {} }] }; return { data: { id: 'fixture', type: 'fixture', attributes: {}, message_ids: ['fixture'] } }; }, }; return { dispatch: createOperations(client, {}, async params => { uploads.push(params); return 'test-etag'; }), requests, uploads }; } function argsFor(tool) { const args = sample(tool.inputSchema); if (tool.inputSchema.properties.frameId) args.frameId = 'frame-test'; if (tool.inputSchema.properties.file) args.file = file; if (tool.name === 'skylight_link_apple_calendar') args.app_specific_password = '{{secret:appleAppPassword}}'; return args; } test('All 114 upstream operations, with 77 writes, are discoverable with schemas', async () => { const { dispatch } = setup(); const tools = await dispatch({ operation: 'tools' }); assert.equal(tools.length, 114); assert.equal(tools.filter(t => !t.readOnly).length, 77); assert.equal(new Set(tools.map(t => t.name)).size, 114); assert.ok(tools.every(t => t.inputSchema.type === 'object')); }); test('Every write previews without any API request or upload, including inner confirm flags', async () => { const { dispatch, requests, uploads } = setup(); for (const tool of await dispatch({ operation: 'tools', mode: 'write' })) { const args = argsFor(tool); if (tool.inputSchema.properties.confirm) args.confirm = true; const preview = await dispatch({ operation: tool.name, args }); assert.equal(preview.dryRun, true, tool.name); assert.match(preview.previewToken, /^[0-9a-f]{64}$/); assert.equal(requests.length, 0, tool.name); assert.equal(uploads.length, 0, tool.name); } }); test('Confirmed writes exercise every upstream handler against the mock API', async () => { for (const tool of await setup().dispatch({ operation: 'tools', mode: 'write' })) { const { dispatch, requests, uploads } = setup(); const args = argsFor(tool); const preview = await dispatch({ operation: tool.name, args }); try { await dispatch({ operation: tool.name, args, confirm: true, previewToken: preview.previewToken }); } catch (error) { throw new Error(tool.name + ': ' + error.message); } assert.ok(requests.some(r => r.method !== 'GET') || uploads.length > 0, tool.name); } }); test('Wrong/missing preview token, changed args, implicit frames and unknown fields cannot write', async () => { const { dispatch, requests } = setup(); const operation = 'skylight_delete_chore'; const args = { id: 'chore-test', frameId: 'frame-test', apply_to: 'all' }; const preview = await dispatch({ operation, args }); await assert.rejects(dispatch({ operation, args, confirm: true }), /previewToken/); await assert.rejects(dispatch({ operation, args: { ...args, id: 'other' }, confirm: true, previewToken: preview.previewToken }), /previewToken/); await assert.rejects(dispatch({ operation: 'skylight_create_chore', args: { summary: 'Test', category_id: 'member' } }), /frameId/); await assert.rejects(dispatch({ operation, args: { ...args, surprise: true } })); const dry = await dispatch({ operation, args, confirm: true, previewToken: preview.previewToken, dryRun: true }); assert.equal(dry.dryRun, true); assert.equal(dry.bulk, true); assert.equal(requests.length, 0); }); test('Representative edits preserve exact method, endpoint and body through upstream code', async () => { const examples = [ ['skylight_update_category', { id: 'member', color: '#123456' }, 'PUT', '/frames/frame-test/categories/member', { color: '#123456' }], ['skylight_create_chore', { summary: 'Make bed', category_id: 'member' }, 'POST', '/frames/frame-test/chores', { summary: 'Make bed', category_id: 'member' }], ['skylight_update_event', { id: 'event', category_ids: ['member'] }, 'PUT', '/frames/frame-test/calendar_events/event', { category_ids: ['member'] }], ['skylight_delete_event', { id: 'event' }, 'DELETE', '/frames/frame-test/calendar_events/event', undefined], ]; for (const [operation, partial, method, path, body] of examples) { const { dispatch, requests } = setup(); const args = { ...partial, frameId: 'frame-test' }; const p = await dispatch({ operation, args }); await dispatch({ operation, args, confirm: true, previewToken: p.previewToken }); assert.equal(requests[0].method, method); assert.equal(requests[0].path, path); assert.deepEqual(requests[0].options?.body, body); } }); test('File uploads use bytes, redact preview payloads, and keep temporary credentials out of results', async () => { const { dispatch, requests, uploads } = setup(); const args = { file, frameId: 'frame-test' }, operation = 'skylight_upload_photo'; const p = await dispatch({ operation, args }); assert.equal(p.args.file.bytes, 3); assert.equal(p.args.file.base64, undefined); const r = await dispatch({ operation, args, confirm: true, previewToken: p.previewToken }); assert.equal(uploads[0].body.toString(), 'abc'); assert.equal(requests.at(-1).path, '/messages/uploads'); assert.ok(!JSON.stringify(r).includes('secret_access_key')); await assert.rejects(dispatch({ operation, args: { ...args, file: { name: '../secrets.txt', base64: 'YWJj' } } })); }); test('Apple password requires a stored secret and previews never echo a resolvable placeholder', async () => { const { dispatch } = setup(); const operation = 'skylight_link_apple_calendar'; await assert.rejects(dispatch({ operation, args: { frameId: 'frame-test', email: 'test@example.com', app_specific_password: 'plaintext' } }), /Kody secret/); const p = await dispatch({ operation, args: { frameId: 'frame-test', email: 'test@example.com', app_specific_password: '{{secret:appleAppPassword}}' } }); assert.equal(p.args.app_specific_password, '[stored credential]'); }); test('Clearing a list aborts if its reviewed membership changed', async () => { const { dispatch, requests } = setup(); const operation = 'skylight_clear_list', args = { frameId: 'frame-test', listId: 'list-test', item_ids: ['old-item'] }; const p = await dispatch({ operation, args }); await assert.rejects(dispatch({ operation, args, confirm: true, previewToken: p.previewToken }), /contents changed/); assert.ok(requests.every(r => r.method === 'GET')); }); test('Avatar transport sends supplied bytes in multipart form without reading local files', async () => { const { dispatch, requests } = setup(); const operation = 'skylight_set_member_avatar', args = { frameId: 'frame-test', id: 'member', file }; const p = await dispatch({ operation, args }); await dispatch({ operation, args, confirm: true, previewToken: p.previewToken }); assert.equal(requests[0].method, 'PUT'); assert.equal(await requests[0].options.formData.get('profile_picture').text(), 'abc'); });