@friggframework/admin-scripts 2.0.0--canary.517.41839c5.0
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.
- package/LICENSE.md +9 -0
- package/index.js +66 -0
- package/package.json +53 -0
- package/src/adapters/__tests__/aws-scheduler-adapter.test.js +322 -0
- package/src/adapters/__tests__/local-scheduler-adapter.test.js +325 -0
- package/src/adapters/__tests__/scheduler-adapter-factory.test.js +257 -0
- package/src/adapters/__tests__/scheduler-adapter.test.js +103 -0
- package/src/adapters/aws-scheduler-adapter.js +138 -0
- package/src/adapters/local-scheduler-adapter.js +103 -0
- package/src/adapters/scheduler-adapter-factory.js +69 -0
- package/src/adapters/scheduler-adapter.js +64 -0
- package/src/application/__tests__/admin-frigg-commands.test.js +643 -0
- package/src/application/__tests__/admin-script-base.test.js +273 -0
- package/src/application/__tests__/dry-run-http-interceptor.test.js +313 -0
- package/src/application/__tests__/dry-run-repository-wrapper.test.js +257 -0
- package/src/application/__tests__/schedule-management-use-case.test.js +276 -0
- package/src/application/__tests__/script-factory.test.js +381 -0
- package/src/application/__tests__/script-runner.test.js +202 -0
- package/src/application/admin-frigg-commands.js +242 -0
- package/src/application/admin-script-base.js +138 -0
- package/src/application/dry-run-http-interceptor.js +296 -0
- package/src/application/dry-run-repository-wrapper.js +261 -0
- package/src/application/schedule-management-use-case.js +230 -0
- package/src/application/script-factory.js +161 -0
- package/src/application/script-runner.js +254 -0
- package/src/builtins/__tests__/integration-health-check.test.js +598 -0
- package/src/builtins/__tests__/oauth-token-refresh.test.js +344 -0
- package/src/builtins/index.js +28 -0
- package/src/builtins/integration-health-check.js +279 -0
- package/src/builtins/oauth-token-refresh.js +221 -0
- package/src/infrastructure/__tests__/admin-auth-middleware.test.js +148 -0
- package/src/infrastructure/__tests__/admin-script-router.test.js +701 -0
- package/src/infrastructure/admin-auth-middleware.js +49 -0
- package/src/infrastructure/admin-script-router.js +311 -0
- package/src/infrastructure/script-executor-handler.js +75 -0
|
@@ -0,0 +1,598 @@
|
|
|
1
|
+
const { IntegrationHealthCheckScript } = require('../integration-health-check');
|
|
2
|
+
|
|
3
|
+
describe('IntegrationHealthCheckScript', () => {
|
|
4
|
+
describe('Definition', () => {
|
|
5
|
+
it('should have correct name and metadata', () => {
|
|
6
|
+
expect(IntegrationHealthCheckScript.Definition.name).toBe('integration-health-check');
|
|
7
|
+
expect(IntegrationHealthCheckScript.Definition.version).toBe('1.0.0');
|
|
8
|
+
expect(IntegrationHealthCheckScript.Definition.source).toBe('BUILTIN');
|
|
9
|
+
expect(IntegrationHealthCheckScript.Definition.config.requiresIntegrationFactory).toBe(true);
|
|
10
|
+
});
|
|
11
|
+
|
|
12
|
+
it('should have valid input schema', () => {
|
|
13
|
+
const schema = IntegrationHealthCheckScript.Definition.inputSchema;
|
|
14
|
+
expect(schema.type).toBe('object');
|
|
15
|
+
expect(schema.properties.integrationIds).toBeDefined();
|
|
16
|
+
expect(schema.properties.checkCredentials).toBeDefined();
|
|
17
|
+
expect(schema.properties.checkConnectivity).toBeDefined();
|
|
18
|
+
expect(schema.properties.updateStatus).toBeDefined();
|
|
19
|
+
});
|
|
20
|
+
|
|
21
|
+
it('should have valid output schema', () => {
|
|
22
|
+
const schema = IntegrationHealthCheckScript.Definition.outputSchema;
|
|
23
|
+
expect(schema.type).toBe('object');
|
|
24
|
+
expect(schema.properties.healthy).toBeDefined();
|
|
25
|
+
expect(schema.properties.unhealthy).toBeDefined();
|
|
26
|
+
expect(schema.properties.unknown).toBeDefined();
|
|
27
|
+
expect(schema.properties.results).toBeDefined();
|
|
28
|
+
});
|
|
29
|
+
|
|
30
|
+
it('should have schedule configuration', () => {
|
|
31
|
+
const schedule = IntegrationHealthCheckScript.Definition.schedule;
|
|
32
|
+
expect(schedule).toBeDefined();
|
|
33
|
+
expect(schedule.enabled).toBe(false);
|
|
34
|
+
expect(schedule.cronExpression).toBe('cron(0 6 * * ? *)');
|
|
35
|
+
});
|
|
36
|
+
|
|
37
|
+
it('should have appropriate timeout configuration', () => {
|
|
38
|
+
expect(IntegrationHealthCheckScript.Definition.config.timeout).toBe(900000); // 15 minutes
|
|
39
|
+
});
|
|
40
|
+
});
|
|
41
|
+
|
|
42
|
+
describe('execute()', () => {
|
|
43
|
+
let script;
|
|
44
|
+
let mockFrigg;
|
|
45
|
+
|
|
46
|
+
beforeEach(() => {
|
|
47
|
+
script = new IntegrationHealthCheckScript();
|
|
48
|
+
mockFrigg = {
|
|
49
|
+
log: jest.fn(),
|
|
50
|
+
listIntegrations: jest.fn(),
|
|
51
|
+
findIntegrationById: jest.fn(),
|
|
52
|
+
instantiate: jest.fn(),
|
|
53
|
+
updateIntegrationStatus: jest.fn(),
|
|
54
|
+
};
|
|
55
|
+
});
|
|
56
|
+
|
|
57
|
+
it('should return empty results when no integrations found', async () => {
|
|
58
|
+
mockFrigg.listIntegrations.mockResolvedValue([]);
|
|
59
|
+
|
|
60
|
+
const result = await script.execute(mockFrigg, {});
|
|
61
|
+
|
|
62
|
+
expect(result.healthy).toBe(0);
|
|
63
|
+
expect(result.unhealthy).toBe(0);
|
|
64
|
+
expect(result.unknown).toBe(0);
|
|
65
|
+
expect(result.results).toEqual([]);
|
|
66
|
+
});
|
|
67
|
+
|
|
68
|
+
it('should return healthy for valid integrations', async () => {
|
|
69
|
+
const integration = {
|
|
70
|
+
id: 'int-1',
|
|
71
|
+
config: {
|
|
72
|
+
type: 'hubspot',
|
|
73
|
+
credentials: {
|
|
74
|
+
access_token: 'token123',
|
|
75
|
+
expires_at: new Date(Date.now() + 24 * 60 * 60 * 1000).toISOString()
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
};
|
|
79
|
+
|
|
80
|
+
const mockInstance = {
|
|
81
|
+
primary: {
|
|
82
|
+
api: {
|
|
83
|
+
getAuthenticationInfo: jest.fn().mockResolvedValue({ user: 'test' })
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
};
|
|
87
|
+
|
|
88
|
+
mockFrigg.listIntegrations.mockResolvedValue([integration]);
|
|
89
|
+
mockFrigg.instantiate.mockResolvedValue(mockInstance);
|
|
90
|
+
|
|
91
|
+
const result = await script.execute(mockFrigg, {
|
|
92
|
+
checkCredentials: true,
|
|
93
|
+
checkConnectivity: true
|
|
94
|
+
});
|
|
95
|
+
|
|
96
|
+
expect(result.healthy).toBe(1);
|
|
97
|
+
expect(result.unhealthy).toBe(0);
|
|
98
|
+
expect(result.results[0]).toMatchObject({
|
|
99
|
+
integrationId: 'int-1',
|
|
100
|
+
status: 'healthy',
|
|
101
|
+
issues: []
|
|
102
|
+
});
|
|
103
|
+
expect(mockInstance.primary.api.getAuthenticationInfo).toHaveBeenCalled();
|
|
104
|
+
});
|
|
105
|
+
|
|
106
|
+
it('should return unhealthy for missing access token', async () => {
|
|
107
|
+
const integration = {
|
|
108
|
+
id: 'int-1',
|
|
109
|
+
config: {
|
|
110
|
+
type: 'hubspot',
|
|
111
|
+
credentials: {} // No access_token
|
|
112
|
+
}
|
|
113
|
+
};
|
|
114
|
+
|
|
115
|
+
mockFrigg.listIntegrations.mockResolvedValue([integration]);
|
|
116
|
+
|
|
117
|
+
const result = await script.execute(mockFrigg, {
|
|
118
|
+
checkCredentials: true,
|
|
119
|
+
checkConnectivity: false
|
|
120
|
+
});
|
|
121
|
+
|
|
122
|
+
expect(result.healthy).toBe(0);
|
|
123
|
+
expect(result.unhealthy).toBe(1);
|
|
124
|
+
expect(result.results[0]).toMatchObject({
|
|
125
|
+
integrationId: 'int-1',
|
|
126
|
+
status: 'unhealthy',
|
|
127
|
+
issues: ['Missing access token']
|
|
128
|
+
});
|
|
129
|
+
});
|
|
130
|
+
|
|
131
|
+
it('should return unhealthy for expired credentials', async () => {
|
|
132
|
+
const pastDate = new Date(Date.now() - 24 * 60 * 60 * 1000); // 24 hours ago
|
|
133
|
+
const integration = {
|
|
134
|
+
id: 'int-1',
|
|
135
|
+
config: {
|
|
136
|
+
type: 'hubspot',
|
|
137
|
+
credentials: {
|
|
138
|
+
access_token: 'token123',
|
|
139
|
+
expires_at: pastDate.toISOString()
|
|
140
|
+
}
|
|
141
|
+
}
|
|
142
|
+
};
|
|
143
|
+
|
|
144
|
+
mockFrigg.listIntegrations.mockResolvedValue([integration]);
|
|
145
|
+
|
|
146
|
+
const result = await script.execute(mockFrigg, {
|
|
147
|
+
checkCredentials: true,
|
|
148
|
+
checkConnectivity: false
|
|
149
|
+
});
|
|
150
|
+
|
|
151
|
+
expect(result.unhealthy).toBe(1);
|
|
152
|
+
expect(result.results[0]).toMatchObject({
|
|
153
|
+
integrationId: 'int-1',
|
|
154
|
+
status: 'unhealthy',
|
|
155
|
+
issues: ['Access token expired']
|
|
156
|
+
});
|
|
157
|
+
});
|
|
158
|
+
|
|
159
|
+
it('should return unhealthy for connectivity failures', async () => {
|
|
160
|
+
const integration = {
|
|
161
|
+
id: 'int-1',
|
|
162
|
+
config: {
|
|
163
|
+
type: 'hubspot',
|
|
164
|
+
credentials: {
|
|
165
|
+
access_token: 'token123',
|
|
166
|
+
expires_at: new Date(Date.now() + 24 * 60 * 60 * 1000).toISOString()
|
|
167
|
+
}
|
|
168
|
+
}
|
|
169
|
+
};
|
|
170
|
+
|
|
171
|
+
const mockInstance = {
|
|
172
|
+
primary: {
|
|
173
|
+
api: {
|
|
174
|
+
getAuthenticationInfo: jest.fn().mockRejectedValue(new Error('Network error'))
|
|
175
|
+
}
|
|
176
|
+
}
|
|
177
|
+
};
|
|
178
|
+
|
|
179
|
+
mockFrigg.listIntegrations.mockResolvedValue([integration]);
|
|
180
|
+
mockFrigg.instantiate.mockResolvedValue(mockInstance);
|
|
181
|
+
|
|
182
|
+
const result = await script.execute(mockFrigg, {
|
|
183
|
+
checkCredentials: true,
|
|
184
|
+
checkConnectivity: true
|
|
185
|
+
});
|
|
186
|
+
|
|
187
|
+
expect(result.unhealthy).toBe(1);
|
|
188
|
+
expect(result.results[0].status).toBe('unhealthy');
|
|
189
|
+
expect(result.results[0].issues).toContainEqual(expect.stringContaining('API connectivity failed'));
|
|
190
|
+
});
|
|
191
|
+
|
|
192
|
+
it('should update integration status when updateStatus is true', async () => {
|
|
193
|
+
const integration = {
|
|
194
|
+
id: 'int-1',
|
|
195
|
+
config: {
|
|
196
|
+
type: 'hubspot',
|
|
197
|
+
credentials: {
|
|
198
|
+
access_token: 'token123',
|
|
199
|
+
expires_at: new Date(Date.now() + 24 * 60 * 60 * 1000).toISOString()
|
|
200
|
+
}
|
|
201
|
+
}
|
|
202
|
+
};
|
|
203
|
+
|
|
204
|
+
const mockInstance = {
|
|
205
|
+
primary: {
|
|
206
|
+
api: {
|
|
207
|
+
getAuthenticationInfo: jest.fn().mockResolvedValue({ user: 'test' })
|
|
208
|
+
}
|
|
209
|
+
}
|
|
210
|
+
};
|
|
211
|
+
|
|
212
|
+
mockFrigg.listIntegrations.mockResolvedValue([integration]);
|
|
213
|
+
mockFrigg.instantiate.mockResolvedValue(mockInstance);
|
|
214
|
+
mockFrigg.updateIntegrationStatus.mockResolvedValue(undefined);
|
|
215
|
+
|
|
216
|
+
const result = await script.execute(mockFrigg, {
|
|
217
|
+
checkCredentials: true,
|
|
218
|
+
checkConnectivity: true,
|
|
219
|
+
updateStatus: true
|
|
220
|
+
});
|
|
221
|
+
|
|
222
|
+
expect(result.healthy).toBe(1);
|
|
223
|
+
expect(mockFrigg.updateIntegrationStatus).toHaveBeenCalledWith('int-1', 'ACTIVE');
|
|
224
|
+
});
|
|
225
|
+
|
|
226
|
+
it('should update integration status to ERROR for unhealthy integrations', async () => {
|
|
227
|
+
const integration = {
|
|
228
|
+
id: 'int-1',
|
|
229
|
+
config: {
|
|
230
|
+
type: 'hubspot',
|
|
231
|
+
credentials: {} // Missing credentials
|
|
232
|
+
}
|
|
233
|
+
};
|
|
234
|
+
|
|
235
|
+
mockFrigg.listIntegrations.mockResolvedValue([integration]);
|
|
236
|
+
mockFrigg.updateIntegrationStatus.mockResolvedValue(undefined);
|
|
237
|
+
|
|
238
|
+
const result = await script.execute(mockFrigg, {
|
|
239
|
+
checkCredentials: true,
|
|
240
|
+
checkConnectivity: false,
|
|
241
|
+
updateStatus: true
|
|
242
|
+
});
|
|
243
|
+
|
|
244
|
+
expect(result.unhealthy).toBe(1);
|
|
245
|
+
expect(mockFrigg.updateIntegrationStatus).toHaveBeenCalledWith('int-1', 'ERROR');
|
|
246
|
+
});
|
|
247
|
+
|
|
248
|
+
it('should not update status when updateStatus is false', async () => {
|
|
249
|
+
const integration = {
|
|
250
|
+
id: 'int-1',
|
|
251
|
+
config: {
|
|
252
|
+
type: 'hubspot',
|
|
253
|
+
credentials: {
|
|
254
|
+
access_token: 'token123',
|
|
255
|
+
expires_at: new Date(Date.now() + 24 * 60 * 60 * 1000).toISOString()
|
|
256
|
+
}
|
|
257
|
+
}
|
|
258
|
+
};
|
|
259
|
+
|
|
260
|
+
const mockInstance = {
|
|
261
|
+
primary: {
|
|
262
|
+
api: {
|
|
263
|
+
getAuthenticationInfo: jest.fn().mockResolvedValue({ user: 'test' })
|
|
264
|
+
}
|
|
265
|
+
}
|
|
266
|
+
};
|
|
267
|
+
|
|
268
|
+
mockFrigg.listIntegrations.mockResolvedValue([integration]);
|
|
269
|
+
mockFrigg.instantiate.mockResolvedValue(mockInstance);
|
|
270
|
+
|
|
271
|
+
await script.execute(mockFrigg, {
|
|
272
|
+
checkCredentials: true,
|
|
273
|
+
checkConnectivity: true,
|
|
274
|
+
updateStatus: false
|
|
275
|
+
});
|
|
276
|
+
|
|
277
|
+
expect(mockFrigg.updateIntegrationStatus).not.toHaveBeenCalled();
|
|
278
|
+
});
|
|
279
|
+
|
|
280
|
+
it('should handle status update failures gracefully', async () => {
|
|
281
|
+
const integration = {
|
|
282
|
+
id: 'int-1',
|
|
283
|
+
config: {
|
|
284
|
+
type: 'hubspot',
|
|
285
|
+
credentials: {
|
|
286
|
+
access_token: 'token123',
|
|
287
|
+
expires_at: new Date(Date.now() + 24 * 60 * 60 * 1000).toISOString()
|
|
288
|
+
}
|
|
289
|
+
}
|
|
290
|
+
};
|
|
291
|
+
|
|
292
|
+
const mockInstance = {
|
|
293
|
+
primary: {
|
|
294
|
+
api: {
|
|
295
|
+
getAuthenticationInfo: jest.fn().mockResolvedValue({ user: 'test' })
|
|
296
|
+
}
|
|
297
|
+
}
|
|
298
|
+
};
|
|
299
|
+
|
|
300
|
+
mockFrigg.listIntegrations.mockResolvedValue([integration]);
|
|
301
|
+
mockFrigg.instantiate.mockResolvedValue(mockInstance);
|
|
302
|
+
mockFrigg.updateIntegrationStatus.mockRejectedValue(new Error('Update failed'));
|
|
303
|
+
|
|
304
|
+
const result = await script.execute(mockFrigg, {
|
|
305
|
+
checkCredentials: true,
|
|
306
|
+
checkConnectivity: true,
|
|
307
|
+
updateStatus: true
|
|
308
|
+
});
|
|
309
|
+
|
|
310
|
+
expect(result.healthy).toBe(1); // Should still report healthy
|
|
311
|
+
expect(mockFrigg.log).toHaveBeenCalledWith(
|
|
312
|
+
'warn',
|
|
313
|
+
expect.stringContaining('Failed to update status'),
|
|
314
|
+
expect.any(Object)
|
|
315
|
+
);
|
|
316
|
+
});
|
|
317
|
+
|
|
318
|
+
it('should filter by specific integration IDs', async () => {
|
|
319
|
+
const integration1 = {
|
|
320
|
+
id: 'int-1',
|
|
321
|
+
config: { type: 'hubspot', credentials: { access_token: 'token1' } }
|
|
322
|
+
};
|
|
323
|
+
const integration2 = {
|
|
324
|
+
id: 'int-2',
|
|
325
|
+
config: { type: 'salesforce', credentials: { access_token: 'token2' } }
|
|
326
|
+
};
|
|
327
|
+
|
|
328
|
+
mockFrigg.findIntegrationById.mockImplementation((id) => {
|
|
329
|
+
if (id === 'int-1') return Promise.resolve(integration1);
|
|
330
|
+
if (id === 'int-2') return Promise.resolve(integration2);
|
|
331
|
+
return Promise.reject(new Error('Not found'));
|
|
332
|
+
});
|
|
333
|
+
|
|
334
|
+
const result = await script.execute(mockFrigg, {
|
|
335
|
+
integrationIds: ['int-1', 'int-2'],
|
|
336
|
+
checkCredentials: true,
|
|
337
|
+
checkConnectivity: false
|
|
338
|
+
});
|
|
339
|
+
|
|
340
|
+
expect(mockFrigg.findIntegrationById).toHaveBeenCalledWith('int-1');
|
|
341
|
+
expect(mockFrigg.findIntegrationById).toHaveBeenCalledWith('int-2');
|
|
342
|
+
expect(mockFrigg.listIntegrations).not.toHaveBeenCalled();
|
|
343
|
+
expect(result.results).toHaveLength(2);
|
|
344
|
+
});
|
|
345
|
+
|
|
346
|
+
it('should handle errors when checking integrations', async () => {
|
|
347
|
+
const integration = {
|
|
348
|
+
id: 'int-1',
|
|
349
|
+
config: {
|
|
350
|
+
type: 'hubspot',
|
|
351
|
+
credentials: {
|
|
352
|
+
access_token: 'token123',
|
|
353
|
+
expires_at: new Date(Date.now() + 24 * 60 * 60 * 1000).toISOString()
|
|
354
|
+
}
|
|
355
|
+
}
|
|
356
|
+
};
|
|
357
|
+
|
|
358
|
+
mockFrigg.listIntegrations.mockResolvedValue([integration]);
|
|
359
|
+
mockFrigg.instantiate.mockRejectedValue(new Error('Instantiation failed'));
|
|
360
|
+
|
|
361
|
+
const result = await script.execute(mockFrigg, {
|
|
362
|
+
checkCredentials: true,
|
|
363
|
+
checkConnectivity: true
|
|
364
|
+
});
|
|
365
|
+
|
|
366
|
+
// Should still complete but mark as unknown or unhealthy
|
|
367
|
+
expect(result.results).toHaveLength(1);
|
|
368
|
+
expect(result.results[0].integrationId).toBe('int-1');
|
|
369
|
+
});
|
|
370
|
+
|
|
371
|
+
it('should skip credential check when checkCredentials is false', async () => {
|
|
372
|
+
const integration = {
|
|
373
|
+
id: 'int-1',
|
|
374
|
+
config: {
|
|
375
|
+
type: 'hubspot',
|
|
376
|
+
credentials: {} // Missing credentials, but check is disabled
|
|
377
|
+
}
|
|
378
|
+
};
|
|
379
|
+
|
|
380
|
+
const mockInstance = {
|
|
381
|
+
primary: {
|
|
382
|
+
api: {
|
|
383
|
+
getAuthenticationInfo: jest.fn().mockResolvedValue({ user: 'test' })
|
|
384
|
+
}
|
|
385
|
+
}
|
|
386
|
+
};
|
|
387
|
+
|
|
388
|
+
mockFrigg.listIntegrations.mockResolvedValue([integration]);
|
|
389
|
+
mockFrigg.instantiate.mockResolvedValue(mockInstance);
|
|
390
|
+
|
|
391
|
+
const result = await script.execute(mockFrigg, {
|
|
392
|
+
checkCredentials: false,
|
|
393
|
+
checkConnectivity: true
|
|
394
|
+
});
|
|
395
|
+
|
|
396
|
+
expect(result.results[0].checks.credentials).toBeUndefined();
|
|
397
|
+
expect(result.results[0].checks.connectivity).toBeDefined();
|
|
398
|
+
});
|
|
399
|
+
|
|
400
|
+
it('should skip connectivity check when checkConnectivity is false', async () => {
|
|
401
|
+
const integration = {
|
|
402
|
+
id: 'int-1',
|
|
403
|
+
config: {
|
|
404
|
+
type: 'hubspot',
|
|
405
|
+
credentials: {
|
|
406
|
+
access_token: 'token123',
|
|
407
|
+
expires_at: new Date(Date.now() + 24 * 60 * 60 * 1000).toISOString()
|
|
408
|
+
}
|
|
409
|
+
}
|
|
410
|
+
};
|
|
411
|
+
|
|
412
|
+
mockFrigg.listIntegrations.mockResolvedValue([integration]);
|
|
413
|
+
|
|
414
|
+
const result = await script.execute(mockFrigg, {
|
|
415
|
+
checkCredentials: true,
|
|
416
|
+
checkConnectivity: false
|
|
417
|
+
});
|
|
418
|
+
|
|
419
|
+
expect(result.results[0].checks.credentials).toBeDefined();
|
|
420
|
+
expect(result.results[0].checks.connectivity).toBeUndefined();
|
|
421
|
+
expect(mockFrigg.instantiate).not.toHaveBeenCalled();
|
|
422
|
+
});
|
|
423
|
+
});
|
|
424
|
+
|
|
425
|
+
describe('checkCredentialValidity()', () => {
|
|
426
|
+
let script;
|
|
427
|
+
|
|
428
|
+
beforeEach(() => {
|
|
429
|
+
script = new IntegrationHealthCheckScript();
|
|
430
|
+
});
|
|
431
|
+
|
|
432
|
+
it('should return valid for integrations with valid credentials', () => {
|
|
433
|
+
const integration = {
|
|
434
|
+
id: 'int-1',
|
|
435
|
+
config: {
|
|
436
|
+
credentials: {
|
|
437
|
+
access_token: 'token123',
|
|
438
|
+
expires_at: new Date(Date.now() + 24 * 60 * 60 * 1000).toISOString()
|
|
439
|
+
}
|
|
440
|
+
}
|
|
441
|
+
};
|
|
442
|
+
|
|
443
|
+
const result = script.checkCredentialValidity(integration);
|
|
444
|
+
|
|
445
|
+
expect(result.valid).toBe(true);
|
|
446
|
+
expect(result.issue).toBeNull();
|
|
447
|
+
});
|
|
448
|
+
|
|
449
|
+
it('should return invalid for missing access token', () => {
|
|
450
|
+
const integration = {
|
|
451
|
+
id: 'int-1',
|
|
452
|
+
config: {
|
|
453
|
+
credentials: {}
|
|
454
|
+
}
|
|
455
|
+
};
|
|
456
|
+
|
|
457
|
+
const result = script.checkCredentialValidity(integration);
|
|
458
|
+
|
|
459
|
+
expect(result.valid).toBe(false);
|
|
460
|
+
expect(result.issue).toBe('Missing access token');
|
|
461
|
+
});
|
|
462
|
+
|
|
463
|
+
it('should return invalid for expired tokens', () => {
|
|
464
|
+
const integration = {
|
|
465
|
+
id: 'int-1',
|
|
466
|
+
config: {
|
|
467
|
+
credentials: {
|
|
468
|
+
access_token: 'token123',
|
|
469
|
+
expires_at: new Date(Date.now() - 1000).toISOString() // Expired
|
|
470
|
+
}
|
|
471
|
+
}
|
|
472
|
+
};
|
|
473
|
+
|
|
474
|
+
const result = script.checkCredentialValidity(integration);
|
|
475
|
+
|
|
476
|
+
expect(result.valid).toBe(false);
|
|
477
|
+
expect(result.issue).toBe('Access token expired');
|
|
478
|
+
});
|
|
479
|
+
|
|
480
|
+
it('should return valid for credentials without expiry', () => {
|
|
481
|
+
const integration = {
|
|
482
|
+
id: 'int-1',
|
|
483
|
+
config: {
|
|
484
|
+
credentials: {
|
|
485
|
+
access_token: 'token123'
|
|
486
|
+
// No expires_at
|
|
487
|
+
}
|
|
488
|
+
}
|
|
489
|
+
};
|
|
490
|
+
|
|
491
|
+
const result = script.checkCredentialValidity(integration);
|
|
492
|
+
|
|
493
|
+
expect(result.valid).toBe(true);
|
|
494
|
+
expect(result.issue).toBeNull();
|
|
495
|
+
});
|
|
496
|
+
});
|
|
497
|
+
|
|
498
|
+
describe('checkApiConnectivity()', () => {
|
|
499
|
+
let script;
|
|
500
|
+
let mockFrigg;
|
|
501
|
+
|
|
502
|
+
beforeEach(() => {
|
|
503
|
+
script = new IntegrationHealthCheckScript();
|
|
504
|
+
mockFrigg = {
|
|
505
|
+
instantiate: jest.fn(),
|
|
506
|
+
};
|
|
507
|
+
});
|
|
508
|
+
|
|
509
|
+
it('should return valid for successful API calls', async () => {
|
|
510
|
+
const integration = {
|
|
511
|
+
id: 'int-1',
|
|
512
|
+
config: { type: 'hubspot' }
|
|
513
|
+
};
|
|
514
|
+
|
|
515
|
+
const mockInstance = {
|
|
516
|
+
primary: {
|
|
517
|
+
api: {
|
|
518
|
+
getAuthenticationInfo: jest.fn().mockResolvedValue({ user: 'test' })
|
|
519
|
+
}
|
|
520
|
+
}
|
|
521
|
+
};
|
|
522
|
+
|
|
523
|
+
mockFrigg.instantiate.mockResolvedValue(mockInstance);
|
|
524
|
+
|
|
525
|
+
const result = await script.checkApiConnectivity(mockFrigg, integration);
|
|
526
|
+
|
|
527
|
+
expect(result.valid).toBe(true);
|
|
528
|
+
expect(result.issue).toBeNull();
|
|
529
|
+
expect(result.responseTime).toBeGreaterThanOrEqual(0);
|
|
530
|
+
});
|
|
531
|
+
|
|
532
|
+
it('should try getCurrentUser if getAuthenticationInfo is not available', async () => {
|
|
533
|
+
const integration = {
|
|
534
|
+
id: 'int-1',
|
|
535
|
+
config: { type: 'hubspot' }
|
|
536
|
+
};
|
|
537
|
+
|
|
538
|
+
const mockInstance = {
|
|
539
|
+
primary: {
|
|
540
|
+
api: {
|
|
541
|
+
getCurrentUser: jest.fn().mockResolvedValue({ user: 'test' })
|
|
542
|
+
}
|
|
543
|
+
}
|
|
544
|
+
};
|
|
545
|
+
|
|
546
|
+
mockFrigg.instantiate.mockResolvedValue(mockInstance);
|
|
547
|
+
|
|
548
|
+
const result = await script.checkApiConnectivity(mockFrigg, integration);
|
|
549
|
+
|
|
550
|
+
expect(result.valid).toBe(true);
|
|
551
|
+
expect(mockInstance.primary.api.getCurrentUser).toHaveBeenCalled();
|
|
552
|
+
});
|
|
553
|
+
|
|
554
|
+
it('should return note when no health check endpoint is available', async () => {
|
|
555
|
+
const integration = {
|
|
556
|
+
id: 'int-1',
|
|
557
|
+
config: { type: 'hubspot' }
|
|
558
|
+
};
|
|
559
|
+
|
|
560
|
+
const mockInstance = {
|
|
561
|
+
primary: {
|
|
562
|
+
api: {} // No health check methods
|
|
563
|
+
}
|
|
564
|
+
};
|
|
565
|
+
|
|
566
|
+
mockFrigg.instantiate.mockResolvedValue(mockInstance);
|
|
567
|
+
|
|
568
|
+
const result = await script.checkApiConnectivity(mockFrigg, integration);
|
|
569
|
+
|
|
570
|
+
expect(result.valid).toBe(true);
|
|
571
|
+
expect(result.issue).toBeNull();
|
|
572
|
+
expect(result.note).toBe('No health check endpoint available');
|
|
573
|
+
});
|
|
574
|
+
|
|
575
|
+
it('should return invalid for API failures', async () => {
|
|
576
|
+
const integration = {
|
|
577
|
+
id: 'int-1',
|
|
578
|
+
config: { type: 'hubspot' }
|
|
579
|
+
};
|
|
580
|
+
|
|
581
|
+
const mockInstance = {
|
|
582
|
+
primary: {
|
|
583
|
+
api: {
|
|
584
|
+
getAuthenticationInfo: jest.fn().mockRejectedValue(new Error('Network error'))
|
|
585
|
+
}
|
|
586
|
+
}
|
|
587
|
+
};
|
|
588
|
+
|
|
589
|
+
mockFrigg.instantiate.mockResolvedValue(mockInstance);
|
|
590
|
+
|
|
591
|
+
const result = await script.checkApiConnectivity(mockFrigg, integration);
|
|
592
|
+
|
|
593
|
+
expect(result.valid).toBe(false);
|
|
594
|
+
expect(result.issue).toContain('API connectivity failed');
|
|
595
|
+
expect(result.issue).toContain('Network error');
|
|
596
|
+
});
|
|
597
|
+
});
|
|
598
|
+
});
|