import { createServer } from 'node:http'; import express from 'express'; import { test } from 'node:test'; import assert from 'node:assert/strict'; import { createCloudFunctionRouter } from '../src/cloud-functions/router.js'; async function withRouter(dispatch: Parameters[0]['dispatch'], actionBody: unknown): Promise<{ status: number; body: any }> { const app = express(); app.use(express.json()); app.use('/api/functions', createCloudFunctionRouter({ defaultWorkspaceId: 'demashi', dispatch })); const server = createServer(app); await new Promise((resolve) => server.listen(0, '127.0.0.1', resolve)); const address = server.address(); const port = typeof address === 'object' && address ? address.port : 0; try { const response = await fetch(`http://127.0.0.1:${port}/api/functions`, { method: 'POST', headers: { 'content-type': 'application/json' }, body: JSON.stringify(actionBody) }); return { status: response.status, body: await response.json() }; } finally { await new Promise((resolve, reject) => server.close((error) => error ? reject(error) : resolve())); } } test('cloud function router rejects actions outside the allowlist', async () => { const result = await withRouter(async () => undefined, { action: 'parse.query', workspaceId: 'demashi', payload: {} }); assert.equal(result.status, 400); assert.equal(result.body.code, 'cloud_action_not_allowed'); assert.match(result.body.requestId, /.+/); }); test('cloud function router normalizes action payloads and forwards a workspace-scoped route', async () => { let captured: { path: string; method: string; body: Record } | undefined; const result = await withRouter(async (_request, response, path, method, body, requestId) => { captured = { path, method, body }; response.json({ success: true, data: { ok: true }, requestId }); }, { action: 'domestic.products.list', workspaceId: 'workspace/alpha', platform: 'jd', payload: { limit: 20, cursor: 'next' } }); assert.equal(result.status, 200); assert.deepEqual(captured, { path: '/domestic-voc/products?workspaceId=workspace%2Falpha&platform=jd&limit=20&cursor=next', method: 'GET', body: { limit: 20, cursor: 'next', platform: 'jd', workspaceId: 'workspace/alpha' }, }); }); test('cloud function router accepts the managed Function browser envelope', async () => { let captured: { path: string; method: string; body: Record } | undefined; const result = await withRouter(async (_request, response, path, method, body, requestId) => { captured = { path, method, body }; response.json({ success: true, data: { ok: true }, requestId }); }, { path: '/saas-voc-gateway', params: { action: 'context.get', workspaceId: 'demashi' }, _ApplicationId: 'saas-voc-local', _InstallationId: 'test-installation', }); assert.equal(result.status, 200); assert.deepEqual(captured, { path: '/saas/context', method: 'GET', body: { workspaceId: 'demashi' }, }); });