cloud-functions.router.test.ts 3.0 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162
  1. import { createServer } from 'node:http';
  2. import express from 'express';
  3. import { test } from 'node:test';
  4. import assert from 'node:assert/strict';
  5. import { createCloudFunctionRouter } from '../src/cloud-functions/router.js';
  6. async function withRouter(dispatch: Parameters<typeof createCloudFunctionRouter>[0]['dispatch'], actionBody: unknown): Promise<{ status: number; body: any }> {
  7. const app = express();
  8. app.use(express.json());
  9. app.use('/api/functions', createCloudFunctionRouter({ defaultWorkspaceId: 'demashi', dispatch }));
  10. const server = createServer(app);
  11. await new Promise<void>((resolve) => server.listen(0, '127.0.0.1', resolve));
  12. const address = server.address();
  13. const port = typeof address === 'object' && address ? address.port : 0;
  14. try {
  15. const response = await fetch(`http://127.0.0.1:${port}/api/functions`, { method: 'POST', headers: { 'content-type': 'application/json' }, body: JSON.stringify(actionBody) });
  16. return { status: response.status, body: await response.json() };
  17. } finally {
  18. await new Promise<void>((resolve, reject) => server.close((error) => error ? reject(error) : resolve()));
  19. }
  20. }
  21. test('cloud function router rejects actions outside the allowlist', async () => {
  22. const result = await withRouter(async () => undefined, { action: 'parse.query', workspaceId: 'demashi', payload: {} });
  23. assert.equal(result.status, 400);
  24. assert.equal(result.body.code, 'cloud_action_not_allowed');
  25. assert.match(result.body.requestId, /.+/);
  26. });
  27. test('cloud function router normalizes action payloads and forwards a workspace-scoped route', async () => {
  28. let captured: { path: string; method: string; body: Record<string, unknown> } | undefined;
  29. const result = await withRouter(async (_request, response, path, method, body, requestId) => {
  30. captured = { path, method, body };
  31. response.json({ success: true, data: { ok: true }, requestId });
  32. }, { action: 'domestic.products.list', workspaceId: 'workspace/alpha', platform: 'jd', payload: { limit: 20, cursor: 'next' } });
  33. assert.equal(result.status, 200);
  34. assert.deepEqual(captured, {
  35. path: '/domestic-voc/products?workspaceId=workspace%2Falpha&platform=jd&limit=20&cursor=next',
  36. method: 'GET',
  37. body: { limit: 20, cursor: 'next', platform: 'jd', workspaceId: 'workspace/alpha' },
  38. });
  39. });
  40. test('cloud function router accepts the managed Function browser envelope', async () => {
  41. let captured: { path: string; method: string; body: Record<string, unknown> } | undefined;
  42. const result = await withRouter(async (_request, response, path, method, body, requestId) => {
  43. captured = { path, method, body };
  44. response.json({ success: true, data: { ok: true }, requestId });
  45. }, {
  46. path: '/saas-voc-gateway',
  47. params: { action: 'context.get', workspaceId: 'demashi' },
  48. _ApplicationId: 'saas-voc-local',
  49. _InstallationId: 'test-installation',
  50. });
  51. assert.equal(result.status, 200);
  52. assert.deepEqual(captured, {
  53. path: '/saas/context',
  54. method: 'GET',
  55. body: { workspaceId: 'demashi' },
  56. });
  57. });