@hubspot/ui-extensions-dev-server 1.1.5 → 1.1.7

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (133) hide show
  1. package/dist/lib/DevServerState.d.ts +1 -1
  2. package/dist/lib/ExtensionsWebSocket.js +26 -2
  3. package/dist/lib/__tests__/ExtensionsWebSocket.spec.js +42 -8
  4. package/dist/lib/__tests__/app-functions/context.spec.d.ts +1 -0
  5. package/dist/lib/__tests__/app-functions/context.spec.js +101 -0
  6. package/dist/lib/__tests__/app-functions/errorReporter.spec.d.ts +1 -0
  7. package/dist/lib/__tests__/app-functions/errorReporter.spec.js +102 -0
  8. package/dist/lib/__tests__/app-functions/executor_v20231.spec.d.ts +1 -0
  9. package/dist/lib/__tests__/app-functions/executor_v20231.spec.js +168 -0
  10. package/dist/lib/__tests__/app-functions/executor_v20232.spec.d.ts +1 -0
  11. package/dist/lib/__tests__/app-functions/executor_v20232.spec.js +190 -0
  12. package/dist/lib/__tests__/app-functions/fixtures/constants.d.ts +18 -0
  13. package/dist/lib/__tests__/app-functions/fixtures/constants.js +139 -0
  14. package/dist/lib/__tests__/app-functions/fixtures/v2023.1/app.functions/func-async-fails.cjs +8 -0
  15. package/dist/lib/__tests__/app-functions/fixtures/v2023.1/app.functions/func-async-fails.d.cts +1 -0
  16. package/dist/lib/__tests__/app-functions/fixtures/v2023.1/app.functions/func-async-succeeds.cjs +8 -0
  17. package/dist/lib/__tests__/app-functions/fixtures/v2023.1/app.functions/func-async-succeeds.d.cts +1 -0
  18. package/dist/lib/__tests__/app-functions/fixtures/v2023.1/app.functions/func-callback-on-promise-rejected.cjs +8 -0
  19. package/dist/lib/__tests__/app-functions/fixtures/v2023.1/app.functions/func-callback-on-promise-rejected.d.cts +1 -0
  20. package/dist/lib/__tests__/app-functions/fixtures/v2023.1/app.functions/func-callback-on-promise-resolved.cjs +8 -0
  21. package/dist/lib/__tests__/app-functions/fixtures/v2023.1/app.functions/func-callback-on-promise-resolved.d.cts +1 -0
  22. package/dist/lib/__tests__/app-functions/fixtures/v2023.1/app.functions/func-does-not-export-main.cjs +4 -0
  23. package/dist/lib/__tests__/app-functions/fixtures/v2023.1/app.functions/func-does-not-export-main.d.cts +1 -0
  24. package/dist/lib/__tests__/app-functions/fixtures/v2023.1/app.functions/func-echos-input.cjs +8 -0
  25. package/dist/lib/__tests__/app-functions/fixtures/v2023.1/app.functions/func-echos-input.d.cts +1 -0
  26. package/dist/lib/__tests__/app-functions/fixtures/v2023.1/app.functions/func-logs.cjs +10 -0
  27. package/dist/lib/__tests__/app-functions/fixtures/v2023.1/app.functions/func-logs.d.cts +1 -0
  28. package/dist/lib/__tests__/app-functions/fixtures/v2023.1/app.functions/func-returns-function.cjs +4 -0
  29. package/dist/lib/__tests__/app-functions/fixtures/v2023.1/app.functions/func-returns-function.d.cts +1 -0
  30. package/dist/lib/__tests__/app-functions/fixtures/v2023.1/app.functions/func-returns-promise-rejected.cjs +7 -0
  31. package/dist/lib/__tests__/app-functions/fixtures/v2023.1/app.functions/func-returns-promise-rejected.d.cts +1 -0
  32. package/dist/lib/__tests__/app-functions/fixtures/v2023.1/app.functions/func-returns-promise-resolved.cjs +7 -0
  33. package/dist/lib/__tests__/app-functions/fixtures/v2023.1/app.functions/func-returns-promise-resolved.d.cts +1 -0
  34. package/dist/lib/__tests__/app-functions/fixtures/v2023.1/app.functions/func-returns-text.cjs +4 -0
  35. package/dist/lib/__tests__/app-functions/fixtures/v2023.1/app.functions/func-returns-text.d.cts +1 -0
  36. package/dist/lib/__tests__/app-functions/fixtures/v2023.1/app.functions/func-returns-undefined.cjs +4 -0
  37. package/dist/lib/__tests__/app-functions/fixtures/v2023.1/app.functions/func-returns-undefined.d.cts +1 -0
  38. package/dist/lib/__tests__/app-functions/fixtures/v2023.1/app.functions/func-throws-error.cjs +4 -0
  39. package/dist/lib/__tests__/app-functions/fixtures/v2023.1/app.functions/func-throws-error.d.cts +1 -0
  40. package/dist/lib/__tests__/app-functions/fixtures/v2023.1/app.functions/func-times-out.cjs +10 -0
  41. package/dist/lib/__tests__/app-functions/fixtures/v2023.1/app.functions/func-times-out.d.cts +1 -0
  42. package/dist/lib/__tests__/app-functions/fixtures/v2023.1/app.functions/func-undeclared.cjs +4 -0
  43. package/dist/lib/__tests__/app-functions/fixtures/v2023.1/app.functions/func-undeclared.d.cts +1 -0
  44. package/dist/lib/__tests__/app-functions/fixtures/v2023.1/app.functions/func-uses-secret.cjs +14 -0
  45. package/dist/lib/__tests__/app-functions/fixtures/v2023.1/app.functions/func-uses-secret.d.cts +1 -0
  46. package/dist/lib/__tests__/app-functions/fixtures/v2023.2/app.functions/func-async-fails.cjs +5 -0
  47. package/dist/lib/__tests__/app-functions/fixtures/v2023.2/app.functions/func-async-fails.d.cts +1 -0
  48. package/dist/lib/__tests__/app-functions/fixtures/v2023.2/app.functions/func-async-succeeds.cjs +5 -0
  49. package/dist/lib/__tests__/app-functions/fixtures/v2023.2/app.functions/func-async-succeeds.d.cts +3 -0
  50. package/dist/lib/__tests__/app-functions/fixtures/v2023.2/app.functions/func-calls-callback.cjs +4 -0
  51. package/dist/lib/__tests__/app-functions/fixtures/v2023.2/app.functions/func-calls-callback.d.cts +1 -0
  52. package/dist/lib/__tests__/app-functions/fixtures/v2023.2/app.functions/func-does-not-export-main.cjs +4 -0
  53. package/dist/lib/__tests__/app-functions/fixtures/v2023.2/app.functions/func-does-not-export-main.d.cts +1 -0
  54. package/dist/lib/__tests__/app-functions/fixtures/v2023.2/app.functions/func-echos-input.cjs +8 -0
  55. package/dist/lib/__tests__/app-functions/fixtures/v2023.2/app.functions/func-echos-input.d.cts +5 -0
  56. package/dist/lib/__tests__/app-functions/fixtures/v2023.2/app.functions/func-logs.cjs +10 -0
  57. package/dist/lib/__tests__/app-functions/fixtures/v2023.2/app.functions/func-logs.d.cts +3 -0
  58. package/dist/lib/__tests__/app-functions/fixtures/v2023.2/app.functions/func-returns-function.cjs +4 -0
  59. package/dist/lib/__tests__/app-functions/fixtures/v2023.2/app.functions/func-returns-function.d.cts +1 -0
  60. package/dist/lib/__tests__/app-functions/fixtures/v2023.2/app.functions/func-returns-implicitly.cjs +7 -0
  61. package/dist/lib/__tests__/app-functions/fixtures/v2023.2/app.functions/func-returns-implicitly.d.cts +1 -0
  62. package/dist/lib/__tests__/app-functions/fixtures/v2023.2/app.functions/func-returns-promise-rejected.cjs +4 -0
  63. package/dist/lib/__tests__/app-functions/fixtures/v2023.2/app.functions/func-returns-promise-rejected.d.cts +1 -0
  64. package/dist/lib/__tests__/app-functions/fixtures/v2023.2/app.functions/func-returns-promise-resolved.cjs +4 -0
  65. package/dist/lib/__tests__/app-functions/fixtures/v2023.2/app.functions/func-returns-promise-resolved.d.cts +3 -0
  66. package/dist/lib/__tests__/app-functions/fixtures/v2023.2/app.functions/func-returns-text.cjs +4 -0
  67. package/dist/lib/__tests__/app-functions/fixtures/v2023.2/app.functions/func-returns-text.d.cts +1 -0
  68. package/dist/lib/__tests__/app-functions/fixtures/v2023.2/app.functions/func-returns-undefined.cjs +4 -0
  69. package/dist/lib/__tests__/app-functions/fixtures/v2023.2/app.functions/func-returns-undefined.d.cts +1 -0
  70. package/dist/lib/__tests__/app-functions/fixtures/v2023.2/app.functions/func-throws-error.cjs +4 -0
  71. package/dist/lib/__tests__/app-functions/fixtures/v2023.2/app.functions/func-throws-error.d.cts +1 -0
  72. package/dist/lib/__tests__/app-functions/fixtures/v2023.2/app.functions/func-times-out.cjs +12 -0
  73. package/dist/lib/__tests__/app-functions/fixtures/v2023.2/app.functions/func-times-out.d.cts +1 -0
  74. package/dist/lib/__tests__/app-functions/fixtures/v2023.2/app.functions/func-undeclared.cjs +4 -0
  75. package/dist/lib/__tests__/app-functions/fixtures/v2023.2/app.functions/func-undeclared.d.cts +1 -0
  76. package/dist/lib/__tests__/app-functions/fixtures/v2023.2/app.functions/func-uses-secret.cjs +14 -0
  77. package/dist/lib/__tests__/app-functions/fixtures/v2023.2/app.functions/func-uses-secret.d.cts +4 -0
  78. package/dist/lib/__tests__/app-functions/secrets.spec.d.ts +1 -0
  79. package/dist/lib/__tests__/app-functions/secrets.spec.js +278 -0
  80. package/dist/lib/__tests__/app-functions/services/AppProxyService.spec.d.ts +1 -0
  81. package/dist/lib/__tests__/app-functions/services/AppProxyService.spec.js +667 -0
  82. package/dist/lib/__tests__/app-functions/services/PrivateAppUserTokenManager.spec.d.ts +1 -0
  83. package/dist/lib/__tests__/app-functions/services/PrivateAppUserTokenManager.spec.js +243 -0
  84. package/dist/lib/__tests__/app-functions/services/services_v20231.spec.d.ts +1 -0
  85. package/dist/lib/__tests__/app-functions/services/services_v20231.spec.js +319 -0
  86. package/dist/lib/__tests__/app-functions/services/services_v20232.spec.d.ts +1 -0
  87. package/dist/lib/__tests__/app-functions/services/services_v20232.spec.js +302 -0
  88. package/dist/lib/__tests__/app-functions/setup.d.ts +1 -0
  89. package/dist/lib/__tests__/app-functions/setup.js +7 -0
  90. package/dist/lib/__tests__/app-functions/signing.spec.d.ts +1 -0
  91. package/dist/lib/__tests__/app-functions/signing.spec.js +460 -0
  92. package/dist/lib/__tests__/ast.spec.js +1 -1
  93. package/dist/lib/__tests__/server.spec.js +24 -2
  94. package/dist/lib/app-functions/api/privateAppUserToken.d.ts +16 -0
  95. package/dist/lib/app-functions/api/privateAppUserToken.js +28 -0
  96. package/dist/lib/app-functions/config.d.ts +4 -0
  97. package/dist/lib/app-functions/config.js +48 -0
  98. package/dist/lib/app-functions/constants.d.ts +26 -0
  99. package/dist/lib/app-functions/constants.js +63 -0
  100. package/dist/lib/app-functions/context.d.ts +3 -0
  101. package/dist/lib/app-functions/context.js +65 -0
  102. package/dist/lib/app-functions/errorReporter.d.ts +22 -0
  103. package/dist/lib/app-functions/errorReporter.js +42 -0
  104. package/dist/lib/app-functions/errors.d.ts +44 -0
  105. package/dist/lib/app-functions/errors.js +82 -0
  106. package/dist/lib/app-functions/executor.d.ts +3 -0
  107. package/dist/lib/app-functions/executor.js +131 -0
  108. package/dist/lib/app-functions/index.d.ts +4 -0
  109. package/dist/lib/app-functions/index.js +4 -0
  110. package/dist/lib/app-functions/secrets.d.ts +5 -0
  111. package/dist/lib/app-functions/secrets.js +55 -0
  112. package/dist/lib/app-functions/services/AppFunctionExecutionService.d.ts +2 -0
  113. package/dist/lib/app-functions/services/AppFunctionExecutionService.js +55 -0
  114. package/dist/lib/app-functions/services/AppProxyService.d.ts +5 -0
  115. package/dist/lib/app-functions/services/AppProxyService.js +196 -0
  116. package/dist/lib/app-functions/services/PrivateAppUserTokenManager.d.ts +22 -0
  117. package/dist/lib/app-functions/services/PrivateAppUserTokenManager.js +185 -0
  118. package/dist/lib/app-functions/services/constants.d.ts +4 -0
  119. package/dist/lib/app-functions/services/constants.js +4 -0
  120. package/dist/lib/app-functions/services/index.d.ts +3 -0
  121. package/dist/lib/app-functions/services/index.js +3 -0
  122. package/dist/lib/app-functions/services/messages.d.ts +14 -0
  123. package/dist/lib/app-functions/services/messages.js +36 -0
  124. package/dist/lib/app-functions/signing.d.ts +29 -0
  125. package/dist/lib/app-functions/signing.js +51 -0
  126. package/dist/lib/app-functions/types.d.ts +172 -0
  127. package/dist/lib/app-functions/types.js +6 -0
  128. package/dist/lib/app-functions/utils.d.ts +15 -0
  129. package/dist/lib/app-functions/utils.js +28 -0
  130. package/dist/lib/ast.js +2 -1
  131. package/dist/lib/server.js +15 -4
  132. package/dist/lib/types.d.ts +2 -2
  133. package/package.json +9 -6
@@ -0,0 +1,302 @@
1
+ import { describe, it, expect, vi, afterEach, beforeEach, } from 'vitest';
2
+ import httpMocks from 'node-mocks-http';
3
+ import { EventEmitter } from 'node:events';
4
+ import { AppFunctionExecutionService } from "../../../app-functions/services/index.js";
5
+ import { TEST_CONFIG_V20232 as TEST_CONFIG } from "../fixtures/constants.js";
6
+ import * as executor from "../../../app-functions/executor.js";
7
+ import { scopesOnAccessToken as __scopesOnAccessToken } from '@hubspot/local-dev-lib/personalAccessKey';
8
+ import { USER_TOKEN_READ, USER_TOKEN_WRITE, } from "../../../app-functions/services/constants.js";
9
+ import { PrivateAppUserTokenManager } from "../../../app-functions/services/PrivateAppUserTokenManager.js";
10
+ vi.mock('../../../app-functions/services/PrivateAppUserTokenManager.ts');
11
+ vi.mock('@hubspot/local-dev-lib/personalAccessKey');
12
+ const scopesOnAccessToken = __scopesOnAccessToken;
13
+ const callAppFunction = async (functionName, parameters) => {
14
+ const request = httpMocks.createRequest({
15
+ method: 'POST',
16
+ url: '/action/function/100',
17
+ params: {
18
+ id: 100,
19
+ },
20
+ body: {
21
+ serverlessFunction: functionName,
22
+ parameters,
23
+ },
24
+ });
25
+ const response = httpMocks.createResponse({
26
+ req: request,
27
+ eventEmitter: EventEmitter,
28
+ });
29
+ const handler = AppFunctionExecutionService(TEST_CONFIG);
30
+ // Hold response until the handler finishes writing to it. THis must
31
+ // be set up before calling the handler or it may miss the `end` event.
32
+ const responsePromised = new Promise((resolve) => {
33
+ response.on('end', () => {
34
+ resolve(response);
35
+ });
36
+ });
37
+ await handler(request, response);
38
+ return await responsePromised;
39
+ };
40
+ /**
41
+ * Validate the service for executing app functions
42
+ */
43
+ describe('app function dev server', () => {
44
+ const initialEnvJson = JSON.stringify(process.env);
45
+ beforeEach(() => {
46
+ scopesOnAccessToken.mockResolvedValue([USER_TOKEN_READ, USER_TOKEN_WRITE]);
47
+ });
48
+ afterEach(() => {
49
+ // restore process.env
50
+ process.env = JSON.parse(initialEnvJson);
51
+ vi.resetAllMocks();
52
+ vi.restoreAllMocks();
53
+ });
54
+ it('returns "200 OK" response if function execution succeeded', async () => {
55
+ const getTokenspy = vi.spyOn(PrivateAppUserTokenManager.prototype, 'getPrivateAppUserToken');
56
+ const response = await callAppFunction('returns-text');
57
+ // Validate response status and body
58
+ expect(response.statusCode).toEqual(200);
59
+ expect(response._getJSONData()).toEqual({
60
+ logId: 'n/a',
61
+ response: 'result',
62
+ });
63
+ // Validate log output
64
+ expect(TEST_CONFIG.logger.debug).toHaveBeenCalledWith(expect.stringContaining('App function "returns-text" execution succeeded'));
65
+ expect(TEST_CONFIG.logger.warn).toHaveBeenCalledTimes(0);
66
+ expect(TEST_CONFIG.logger.error).toHaveBeenCalledTimes(0);
67
+ // Validate process.env is put back to what it was before the call
68
+ expect(process.env).toEqual(JSON.parse(initialEnvJson));
69
+ // get the token
70
+ expect(getTokenspy).toHaveBeenCalledTimes(1);
71
+ });
72
+ it('returns "400 Bad Request" response if function execution failed', async () => {
73
+ const response = await callAppFunction('throws-error');
74
+ // Validate response status and body
75
+ expect(response.statusCode).toEqual(400);
76
+ const body = response._getJSONData();
77
+ expect(body).toHaveProperty('status', 'error');
78
+ expect(body).toHaveProperty('category', 'VALIDATION_ERROR');
79
+ const exception = body.errors?.[0]?.context?.exception?.[0];
80
+ expect(exception).toEqual('Error: Oops');
81
+ // Validate log output
82
+ expect(TEST_CONFIG.logger.warn).toHaveBeenCalledWith(expect.stringContaining('App function "throws-error" execution failed'));
83
+ expect(TEST_CONFIG.logger.error).toHaveBeenCalledWith('App function encountered an uncaught error.', expect.anything());
84
+ // Validate process.env is put back to what it was before the call
85
+ expect(process.env).toEqual(JSON.parse(initialEnvJson));
86
+ });
87
+ describe('handles asynchronous functions (promise chaining)', () => {
88
+ it('returns "200 OK" response if function returns a promise that resolves', async () => {
89
+ const response = await callAppFunction('returns-promise-resolved');
90
+ // Validate response status and body
91
+ expect(response.statusCode).toEqual(200);
92
+ expect(response._getJSONData()).toEqual({
93
+ logId: 'n/a',
94
+ response: { result: 'simulated' },
95
+ });
96
+ // Validate log output
97
+ expect(TEST_CONFIG.logger.debug).toHaveBeenCalledWith(expect.stringContaining('App function "returns-promise-resolved" execution succeeded'));
98
+ expect(TEST_CONFIG.logger.warn).toHaveBeenCalledTimes(0);
99
+ expect(TEST_CONFIG.logger.error).toHaveBeenCalledTimes(0);
100
+ // Validate process.env is put back to what it was before the call
101
+ expect(process.env).toEqual(JSON.parse(initialEnvJson));
102
+ });
103
+ it('returns "400 Bad Request" response if function returns a promise that rejects', async () => {
104
+ const response = await callAppFunction('returns-promise-rejected');
105
+ // Validate response status and body
106
+ expect(response.statusCode).toEqual(400);
107
+ const body = response._getJSONData();
108
+ expect(body).toHaveProperty('status', 'error');
109
+ expect(body).toHaveProperty('category', 'VALIDATION_ERROR');
110
+ const exception = body.errors?.[0]?.context?.exception?.[0];
111
+ expect(exception).toEqual('Error: fail');
112
+ // Validate log output
113
+ expect(TEST_CONFIG.logger.warn).toHaveBeenCalledWith(expect.stringContaining('App function "returns-promise-rejected" execution failed'));
114
+ expect(TEST_CONFIG.logger.error).toHaveBeenCalledWith('App function encountered an uncaught error.', expect.anything());
115
+ // Validate process.env is put back to what it was before the call
116
+ expect(process.env).toEqual(JSON.parse(initialEnvJson));
117
+ });
118
+ });
119
+ describe('handles asynchronous functions (async/await)', () => {
120
+ it('returns "200 OK" response if an async function execution succeeded', async () => {
121
+ const response = await callAppFunction('async-succeeds');
122
+ // Validate response status and body
123
+ expect(response.statusCode).toEqual(200);
124
+ expect(response._getJSONData()).toEqual({
125
+ logId: 'n/a',
126
+ response: { result: 'simulated' },
127
+ });
128
+ // Validate log output
129
+ expect(TEST_CONFIG.logger.debug).toHaveBeenCalledWith(expect.stringContaining('App function "async-succeeds" execution succeeded'));
130
+ expect(TEST_CONFIG.logger.warn).toHaveBeenCalledTimes(0);
131
+ expect(TEST_CONFIG.logger.error).toHaveBeenCalledTimes(0);
132
+ // Validate process.env is put back to what it was before the call
133
+ expect(process.env).toEqual(JSON.parse(initialEnvJson));
134
+ });
135
+ it('returns "400 Bad Request" response if an async function execution failed', async () => {
136
+ const response = await callAppFunction('async-fails');
137
+ // Validate response status and body
138
+ expect(response.statusCode).toEqual(400);
139
+ const body = response._getJSONData();
140
+ expect(body).toHaveProperty('status', 'error');
141
+ expect(body).toHaveProperty('category', 'VALIDATION_ERROR');
142
+ const exception = body.errors?.[0]?.context?.exception?.[0];
143
+ expect(exception).toEqual('Error: fail');
144
+ // Validate log output
145
+ expect(TEST_CONFIG.logger.warn).toHaveBeenCalledWith(expect.stringContaining('App function "async-fails" execution failed'));
146
+ expect(TEST_CONFIG.logger.error).toHaveBeenCalledWith('App function encountered an uncaught error.', expect.anything());
147
+ // Validate process.env is put back to what it was before the call
148
+ expect(process.env).toEqual(JSON.parse(initialEnvJson));
149
+ });
150
+ });
151
+ it('returns "200 OK" response if function returns implicitly (only 2023.2)', async () => {
152
+ const response = await callAppFunction('returns-implicitly');
153
+ // Validate response status and body
154
+ expect(response.statusCode).toEqual(200);
155
+ expect(response._getJSONData()).toEqual({
156
+ logId: 'n/a',
157
+ response: null,
158
+ });
159
+ // Validate log output
160
+ expect(TEST_CONFIG.logger.debug).toHaveBeenCalledWith(expect.stringContaining('App function "returns-implicitly" execution succeeded'));
161
+ expect(TEST_CONFIG.logger.warn).toHaveBeenCalledTimes(0);
162
+ expect(TEST_CONFIG.logger.error).toHaveBeenCalledTimes(0);
163
+ // Validate process.env is put back to what it was before the call
164
+ expect(process.env).toEqual(JSON.parse(initialEnvJson));
165
+ });
166
+ it('returns "400 Bad Request" response if function not found', async () => {
167
+ const response = await callAppFunction('does-not-exist');
168
+ // Validate response status and body
169
+ expect(response.statusCode).toEqual(400);
170
+ const body = response._getJSONData();
171
+ expect(body).toHaveProperty('status', 'error');
172
+ expect(body).toHaveProperty('category', 'VALIDATION_ERROR');
173
+ const exception = body.errors?.[0]?.context?.exception?.[0];
174
+ expect(exception).toMatch(/doesn't exist in this project/);
175
+ // Validate log output
176
+ expect(TEST_CONFIG.logger.warn).toHaveBeenCalledWith(expect.stringContaining('App function "does-not-exist" execution failed'));
177
+ expect(TEST_CONFIG.logger.error).toHaveBeenCalledWith(expect.stringContaining('Could not find file'));
178
+ // Validate process.env is put back to what it was before the call
179
+ expect(process.env).toEqual(JSON.parse(initialEnvJson));
180
+ });
181
+ it('returns "400 Bad Request" response if function does not export main', async () => {
182
+ // TEST_CONFIG.functionTimeoutMs = 250
183
+ const response = await callAppFunction('does-not-export-main');
184
+ // Validate response status and body
185
+ expect(response.statusCode).toEqual(400);
186
+ const body = response._getJSONData();
187
+ expect(body).toHaveProperty('status', 'error');
188
+ expect(body).toHaveProperty('category', 'VALIDATION_ERROR');
189
+ const exception = body.errors?.[0]?.context?.exception?.[0];
190
+ expect(exception).toMatch(/customerPayload\.main is not a function/);
191
+ // Validate log output
192
+ expect(TEST_CONFIG.logger.warn).toHaveBeenCalledWith(expect.stringContaining('App function "does-not-export-main" execution failed'));
193
+ expect(TEST_CONFIG.logger.error).toHaveBeenCalledWith(expect.stringContaining('Could not find "main" export in'));
194
+ // Validate process.env is put back to what it was before the call
195
+ expect(process.env).toEqual(JSON.parse(initialEnvJson));
196
+ });
197
+ it('returns "400 Bad Request" response if function returns invalid json', async () => {
198
+ // TEST_CONFIG.functionTimeoutMs = 250
199
+ const response = await callAppFunction('returns-function');
200
+ // Validate response status and body
201
+ expect(response.statusCode).toEqual(400);
202
+ const body = response._getJSONData();
203
+ expect(body).toHaveProperty('status', 'error');
204
+ expect(body).toHaveProperty('category', 'VALIDATION_ERROR');
205
+ const exception = body.errors?.[0]?.context?.exception?.[0];
206
+ expect(exception).toMatch(/Wrong arguments/);
207
+ // Validate log output
208
+ expect(TEST_CONFIG.logger.warn).toHaveBeenCalledWith(expect.stringContaining('App function "returns-function" execution failed'));
209
+ expect(TEST_CONFIG.logger.error).toHaveBeenCalledWith('App function reponse is not valid JSON.');
210
+ // Validate process.env is put back to what it was before the call
211
+ expect(process.env).toEqual(JSON.parse(initialEnvJson));
212
+ });
213
+ it('returns "200 OK" response if function returns before timeout', async () => {
214
+ // TEST_CONFIG.functionTimeoutMs = 250
215
+ const response = await callAppFunction('times-out', { delayMs: 200 });
216
+ const variance = 10; // milliseconds
217
+ // Validate response status and body
218
+ expect(response.statusCode).toEqual(200);
219
+ const body = response._getJSONData();
220
+ expect(body).toEqual({
221
+ logId: 'n/a',
222
+ response: { status: 'success', elapsedMs: expect.anything() },
223
+ });
224
+ expect(body.response.elapsedMs).toBeGreaterThanOrEqual(200 - variance);
225
+ expect(body.response.elapsedMs).toBeLessThanOrEqual(200 + variance);
226
+ // Validate log output
227
+ expect(TEST_CONFIG.logger.debug).toHaveBeenCalledWith(expect.stringContaining('App function "times-out" execution succeeded'));
228
+ expect(TEST_CONFIG.logger.warn).toHaveBeenCalledTimes(0);
229
+ expect(TEST_CONFIG.logger.error).toHaveBeenCalledTimes(0);
230
+ // Validate process.env is put back to what it was before the call
231
+ expect(process.env).toEqual(JSON.parse(initialEnvJson));
232
+ });
233
+ it('returns "400 Bad Request" response if function returns after timeout', async () => {
234
+ // TEST_CONFIG.functionTimeoutMs = 250
235
+ const response = await callAppFunction('times-out', { delayMs: 300 });
236
+ // Validate response status and body
237
+ expect(response.statusCode).toEqual(400);
238
+ const body = response._getJSONData();
239
+ expect(body).toHaveProperty('status', 'error');
240
+ expect(body).toHaveProperty('category', 'VALIDATION_ERROR');
241
+ const exception = body.errors?.[0]?.context?.exception?.[0];
242
+ expect(exception).toMatch(/Task timed out after/);
243
+ // Validate log output
244
+ expect(TEST_CONFIG.logger.warn).toHaveBeenCalledWith(expect.stringContaining('App function "times-out" execution failed'));
245
+ expect(TEST_CONFIG.logger.error).toHaveBeenCalledWith('App function failed to callback within 0.25 second.');
246
+ // Validate process.env is put back to what it was before the call
247
+ expect(process.env).toEqual(JSON.parse(initialEnvJson));
248
+ });
249
+ it('returns "500 Internal Server Error" response if something went wrong', async () => {
250
+ const error = new Error('Something went terribly wrong!');
251
+ vi.spyOn(executor, 'executeFunction').mockImplementation(() => {
252
+ throw error;
253
+ });
254
+ // Act
255
+ const response = await callAppFunction('returns-text');
256
+ // Validate response status and body
257
+ expect(response.statusCode).toEqual(500);
258
+ const body = response._getJSONData();
259
+ expect(body).toHaveProperty('status', 'error');
260
+ expect(body).not.toHaveProperty('category', 'INTERNAL_ERROR');
261
+ expect(body).toHaveProperty('message', 'internal error');
262
+ // Validate log output
263
+ expect(TEST_CONFIG.logger.warn).toHaveBeenCalledWith(expect.stringContaining('App function "returns-text" execution failed due to server internal error'));
264
+ expect(TEST_CONFIG.logger.error).toHaveBeenCalledWith(error);
265
+ // Validate process.env is put back to what it was before the call
266
+ expect(process.env).toEqual(JSON.parse(initialEnvJson));
267
+ });
268
+ it('logs to console from inside the function', async () => {
269
+ const log = vi.spyOn(console, 'log').mockImplementation(() => { });
270
+ const debug = vi.spyOn(console, 'debug').mockImplementation(() => { });
271
+ const info = vi.spyOn(console, 'info').mockImplementation(() => { });
272
+ const warn = vi.spyOn(console, 'warn').mockImplementation(() => { });
273
+ const error = vi.spyOn(console, 'error').mockImplementation(() => { });
274
+ // Act
275
+ const response = await callAppFunction('logs');
276
+ // Validate response status and body
277
+ expect(response.statusCode).toEqual(200);
278
+ expect(response._getJSONData()).toEqual({
279
+ logId: 'n/a',
280
+ response: { status: 'success' },
281
+ });
282
+ // Validate function logging
283
+ expect(log).toHaveBeenCalledTimes(1);
284
+ expect(log).toHaveBeenCalledWith('log line');
285
+ expect(debug).toHaveBeenCalledTimes(1);
286
+ expect(debug).toHaveBeenCalledWith('debug line');
287
+ expect(info).toHaveBeenCalledTimes(2);
288
+ expect(info).toHaveBeenNthCalledWith(1, 'info line');
289
+ expect(info).toHaveBeenNthCalledWith(2, 'print data:', [1, 2]);
290
+ expect(warn).toHaveBeenCalledTimes(1);
291
+ expect(warn).toHaveBeenCalledWith('warn line');
292
+ expect(error).toHaveBeenCalledTimes(1);
293
+ expect(error).toHaveBeenCalledWith('error line');
294
+ // Validate service logging, which is separate from function logging
295
+ expect(TEST_CONFIG.logger.debug).toHaveBeenCalledTimes(1);
296
+ expect(TEST_CONFIG.logger.debug).toHaveBeenNthCalledWith(1, expect.stringContaining('App function "logs" execution succeeded'));
297
+ expect(TEST_CONFIG.logger.warn).toHaveBeenCalledTimes(0);
298
+ expect(TEST_CONFIG.logger.error).toHaveBeenCalledTimes(0);
299
+ // Validate process.env is put back to what it was before the call
300
+ expect(process.env).toEqual(JSON.parse(initialEnvJson));
301
+ });
302
+ });
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,7 @@
1
+ // Overwrite console.debug to only log when in DEBUG mode
2
+ console.debug = (...args) => {
3
+ if (process.env.DEBUG) {
4
+ console.log(...args);
5
+ }
6
+ };
7
+ export {};
@@ -0,0 +1 @@
1
+ export {};