@shipfox/api-server 5.0.0 → 6.0.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/package.json CHANGED
@@ -2,7 +2,7 @@
2
2
  "name": "@shipfox/api-server",
3
3
  "license": "MIT",
4
4
  "private": false,
5
- "version": "5.0.0",
5
+ "version": "6.0.0",
6
6
  "repository": {
7
7
  "type": "git",
8
8
  "url": "git+https://github.com/ShipfoxHQ/shipfox.git",
@@ -25,28 +25,42 @@
25
25
  }
26
26
  },
27
27
  "dependencies": {
28
- "@shipfox/annotations": "5.0.0",
29
- "@shipfox/api-agent": "5.0.0",
30
- "@shipfox/api-auth": "5.0.0",
31
- "@shipfox/api-definitions": "5.0.0",
32
- "@shipfox/api-dispatcher": "5.0.0",
33
- "@shipfox/api-integration-core": "5.0.0",
34
- "@shipfox/api-logs": "5.0.0",
35
- "@shipfox/api-projects": "5.0.0",
36
- "@shipfox/api-runners": "5.0.0",
37
- "@shipfox/api-secrets": "5.0.0",
38
- "@shipfox/api-triggers": "5.0.0",
39
- "@shipfox/api-workflows": "5.0.0",
40
- "@shipfox/api-workspaces": "5.0.0",
28
+ "@shipfox/annotations": "6.0.0",
29
+ "@shipfox/annotations-dto": "6.0.0",
30
+ "@shipfox/api-agent": "6.0.0",
31
+ "@shipfox/api-agent-dto": "6.0.0",
32
+ "@shipfox/api-auth": "6.0.0",
33
+ "@shipfox/api-auth-dto": "6.0.0",
34
+ "@shipfox/api-definitions": "6.0.0",
35
+ "@shipfox/api-definitions-dto": "6.0.0",
36
+ "@shipfox/api-dispatcher": "6.0.0",
37
+ "@shipfox/api-email-challenges": "0.2.0",
38
+ "@shipfox/api-integration-core": "6.0.0",
39
+ "@shipfox/api-integration-core-dto": "6.0.0",
40
+ "@shipfox/api-logs": "6.0.0",
41
+ "@shipfox/api-projects": "6.0.0",
42
+ "@shipfox/api-projects-dto": "6.0.0",
43
+ "@shipfox/api-runners": "6.0.0",
44
+ "@shipfox/api-runners-dto": "6.0.0",
45
+ "@shipfox/api-secrets": "6.0.0",
46
+ "@shipfox/api-secrets-dto": "6.0.0",
47
+ "@shipfox/api-triggers": "6.0.0",
48
+ "@shipfox/api-workflows": "6.0.0",
49
+ "@shipfox/api-workflows-dto": "6.0.0",
50
+ "@shipfox/api-workspaces-dto": "6.0.0",
51
+ "@shipfox/api-workspaces": "6.0.0",
41
52
  "@shipfox/config": "1.2.2",
42
53
  "@shipfox/node-error-monitoring": "0.1.3",
43
- "@shipfox/node-fastify": "0.2.3",
44
- "@shipfox/node-module": "0.3.2",
54
+ "@shipfox/node-fastify": "0.2.4",
55
+ "@shipfox/node-jwt": "0.3.0",
56
+ "@shipfox/node-module": "0.4.0",
45
57
  "@shipfox/node-opentelemetry": "0.5.2",
46
58
  "@shipfox/node-postgres": "0.4.2"
47
59
  },
48
60
  "devDependencies": {
49
61
  "@shipfox/biome": "1.8.2",
62
+ "@shipfox/depcruise": "1.0.2",
63
+ "@shipfox/inter-module": "0.2.0",
50
64
  "@shipfox/swc": "1.2.6",
51
65
  "@shipfox/ts-config": "1.3.8",
52
66
  "@shipfox/typescript": "1.1.7",
@@ -56,6 +70,7 @@
56
70
  "build": "shipfox-swc",
57
71
  "check": "shipfox-biome-check",
58
72
  "check:fix": "shipfox-biome-check --write",
73
+ "depcruise": "shipfox-depcruise",
59
74
  "test": "shipfox-vitest-run",
60
75
  "test:watch": "shipfox-vitest-watch",
61
76
  "type": "shipfox-tsc-check",
package/src/index.ts CHANGED
@@ -1,3 +1,3 @@
1
- export {defaultModules} from './modules.js';
1
+ export {type DefaultModulesOptions, defaultModules} from './modules.js';
2
2
  export type {CreateServerOptions, RunServerOptions, ServerHandle} from './server.js';
3
3
  export {createServer, runServer} from './server.js';
@@ -1,28 +1,74 @@
1
+ import {annotationsInterModuleContract} from '@shipfox/annotations-dto/inter-module';
2
+ import {agentInterModuleContract} from '@shipfox/api-agent-dto/inter-module';
3
+ import {authInterModuleContract} from '@shipfox/api-auth-dto/inter-module';
4
+ import {definitionsInterModuleContract} from '@shipfox/api-definitions-dto/inter-module';
5
+ import {integrationsInterModuleContract} from '@shipfox/api-integration-core-dto';
6
+ import {projectsInterModuleContract} from '@shipfox/api-projects-dto';
7
+ import {runnersInterModuleContract} from '@shipfox/api-runners-dto/inter-module';
8
+ import {
9
+ type SecretsInterModuleClient,
10
+ secretsInterModuleContract,
11
+ } from '@shipfox/api-secrets-dto/inter-module';
12
+ import {workflowsInterModuleContract} from '@shipfox/api-workflows-dto/inter-module';
13
+ import {workspacesInterModuleContract} from '@shipfox/api-workspaces-dto/inter-module';
14
+ import {defineInterModulePresentation} from '@shipfox/inter-module';
1
15
  import {defaultModules} from './modules.js';
2
16
 
3
17
  const mocks = vi.hoisted(() => ({
4
18
  buildAgentToolCatalogs: vi.fn(),
5
19
  buildAgentToolSelectionCatalogs: vi.fn(),
20
+ createAgentModule: vi.fn(),
6
21
  createDefinitionsModule: vi.fn(),
7
22
  createIntegrationsContext: vi.fn(),
23
+ createLogsModule: vi.fn(),
8
24
  createProjectsModule: vi.fn(),
25
+ createRunnersModule: vi.fn(),
26
+ createSecretsModule: vi.fn(),
27
+ createTriggersModule: vi.fn(),
28
+ createWorkflowsModule: vi.fn(),
9
29
  createWorkspaceConnectionSnapshotLoader: vi.fn(),
10
30
  deleteSecrets: vi.fn(),
11
31
  getIntegrationConnectionById: vi.fn(),
12
32
  getSecret: vi.fn(),
13
- loadRunningLeasedStep: vi.fn(),
14
- setAgentToolMaterializationServices: vi.fn(),
15
33
  setSecrets: vi.fn(),
16
- setSourceControl: vi.fn(),
17
34
  }));
18
35
 
19
- vi.mock('@shipfox/api-agent', () => ({agentModule: {name: 'agent'}}));
20
- vi.mock('@shipfox/annotations', () => ({annotationsModule: {name: 'annotations'}}));
21
- vi.mock('@shipfox/api-auth', () => ({authModule: {name: 'auth'}}));
36
+ vi.mock('@shipfox/annotations', () => ({
37
+ annotationsModule: {
38
+ name: 'annotations',
39
+ interModulePresentations: [
40
+ {
41
+ contract: annotationsInterModuleContract,
42
+ handlers: {replaceOrRemoveAnnotation: vi.fn()},
43
+ },
44
+ ],
45
+ },
46
+ }));
47
+ vi.mock('@shipfox/api-auth', () => ({
48
+ createAuthModule: () => ({
49
+ name: 'auth',
50
+ interModulePresentations: [
51
+ {
52
+ contract: authInterModuleContract,
53
+ handlers: {
54
+ mintJobLeaseToken: vi.fn(),
55
+ mintRunnerSessionToken: vi.fn(),
56
+ },
57
+ },
58
+ ],
59
+ }),
60
+ }));
61
+ vi.mock('@shipfox/api-agent', () => ({createAgentModule: mocks.createAgentModule}));
62
+ vi.mock('@shipfox/api-auth/config', () => ({
63
+ config: {AUTH_JOB_LEASE_TOKEN_EXPIRES_IN: '90m'},
64
+ }));
22
65
  vi.mock('@shipfox/api-definitions', () => ({
23
66
  createDefinitionsModule: mocks.createDefinitionsModule,
24
67
  }));
25
68
  vi.mock('@shipfox/api-dispatcher', () => ({dispatcherModule: {name: 'dispatcher'}}));
69
+ vi.mock('@shipfox/api-email-challenges', () => ({
70
+ emailChallengesModule: {name: 'email-challenges'},
71
+ }));
26
72
  vi.mock('@shipfox/api-integration-core', () => ({
27
73
  buildAgentToolCatalogs: mocks.buildAgentToolCatalogs,
28
74
  buildAgentToolSelectionCatalogs: mocks.buildAgentToolSelectionCatalogs,
@@ -30,56 +76,162 @@ vi.mock('@shipfox/api-integration-core', () => ({
30
76
  createWorkspaceConnectionSnapshotLoader: mocks.createWorkspaceConnectionSnapshotLoader,
31
77
  getIntegrationConnectionById: mocks.getIntegrationConnectionById,
32
78
  }));
33
- vi.mock('@shipfox/api-logs', () => ({logsModule: {name: 'logs'}}));
79
+ vi.mock('@shipfox/api-logs', () => ({createLogsModule: mocks.createLogsModule}));
34
80
  vi.mock('@shipfox/api-projects', () => ({createProjectsModule: mocks.createProjectsModule}));
35
- vi.mock('@shipfox/api-runners', () => ({runnersModule: {name: 'runners'}}));
81
+ vi.mock('@shipfox/api-runners', () => ({createRunnersModule: mocks.createRunnersModule}));
36
82
  vi.mock('@shipfox/api-secrets', () => ({
83
+ createSecretsModule: mocks.createSecretsModule,
37
84
  deleteSecrets: mocks.deleteSecrets,
38
85
  getSecret: mocks.getSecret,
39
- secretsModule: {name: 'secrets'},
40
86
  setSecrets: mocks.setSecrets,
41
87
  }));
42
- vi.mock('@shipfox/api-triggers', () => ({triggersModule: {name: 'triggers'}}));
88
+ vi.mock('@shipfox/api-triggers', () => ({createTriggersModule: mocks.createTriggersModule}));
43
89
  vi.mock('@shipfox/api-workflows', () => ({
44
- loadRunningLeasedStep: mocks.loadRunningLeasedStep,
45
- setAgentToolMaterializationServices: mocks.setAgentToolMaterializationServices,
46
- setSourceControl: mocks.setSourceControl,
47
- workflowsModule: {name: 'workflows'},
90
+ createWorkflowsModule: mocks.createWorkflowsModule,
91
+ }));
92
+ vi.mock('@shipfox/api-workspaces', () => ({
93
+ workspacesModule: {
94
+ name: 'workspaces',
95
+ interModulePresentations: [
96
+ {
97
+ contract: workspacesInterModuleContract,
98
+ handlers: {
99
+ listMembershipsForTokenClaims: vi.fn(),
100
+ preflightInvitationAcceptance: vi.fn(),
101
+ acceptInvitation: vi.fn(),
102
+ requireActiveMembership: vi.fn(),
103
+ },
104
+ },
105
+ ],
106
+ },
48
107
  }));
49
- vi.mock('@shipfox/api-workspaces', () => ({workspacesModule: {name: 'workspaces'}}));
50
108
 
51
109
  describe('defaultModules', () => {
52
110
  beforeEach(() => {
53
111
  mocks.buildAgentToolCatalogs.mockReset();
54
112
  mocks.buildAgentToolSelectionCatalogs.mockReset();
113
+ mocks.createAgentModule.mockReset();
55
114
  mocks.createDefinitionsModule.mockReset();
56
115
  mocks.createIntegrationsContext.mockReset();
116
+ mocks.createLogsModule.mockReset();
57
117
  mocks.createProjectsModule.mockReset();
118
+ mocks.createRunnersModule.mockReset();
119
+ mocks.createSecretsModule.mockReset();
120
+ mocks.createTriggersModule.mockReset();
121
+ mocks.createWorkflowsModule.mockReset();
58
122
  mocks.createWorkspaceConnectionSnapshotLoader.mockReset();
59
123
  mocks.deleteSecrets.mockReset();
60
124
  mocks.getIntegrationConnectionById.mockReset();
61
125
  mocks.getSecret.mockReset();
62
- mocks.loadRunningLeasedStep.mockReset();
63
- mocks.setAgentToolMaterializationServices.mockReset();
64
126
  mocks.setSecrets.mockReset();
65
- mocks.setSourceControl.mockReset();
66
127
 
67
128
  mocks.createIntegrationsContext.mockResolvedValue({
68
- module: {name: 'integrations'},
129
+ module: {
130
+ name: 'integrations',
131
+ interModulePresentations: [
132
+ defineInterModulePresentation(integrationsInterModuleContract, {
133
+ createCheckoutSpec: vi.fn(),
134
+ fetchSourceFile: vi.fn(),
135
+ getAgentToolsContext: vi.fn(),
136
+ listSourceFiles: vi.fn(),
137
+ resolveSourceRepository: vi.fn(),
138
+ }),
139
+ ],
140
+ },
69
141
  registry: {},
70
142
  sourceControl: {provider: 'source-control'},
71
143
  });
144
+ mocks.createLogsModule.mockReturnValue({name: 'logs'});
72
145
  mocks.buildAgentToolCatalogs.mockResolvedValue(new Map());
73
146
  mocks.buildAgentToolSelectionCatalogs.mockResolvedValue(new Map());
74
147
  mocks.createWorkspaceConnectionSnapshotLoader.mockReturnValue(vi.fn());
75
- mocks.createProjectsModule.mockReturnValue({name: 'projects'});
76
- mocks.createDefinitionsModule.mockReturnValue({name: 'definitions'});
148
+ mocks.deleteSecrets.mockResolvedValue({deleted: 1});
149
+ mocks.getSecret.mockResolvedValue({value: 'secret'});
150
+ mocks.setSecrets.mockResolvedValue({});
151
+ mocks.createProjectsModule.mockReturnValue({
152
+ name: 'projects',
153
+ interModulePresentations: [
154
+ defineInterModulePresentation(projectsInterModuleContract, {
155
+ getProjectById: () => ({project: null}),
156
+ requireProjectForWorkspace: () => ({
157
+ project: {
158
+ id: crypto.randomUUID(),
159
+ workspaceId: crypto.randomUUID(),
160
+ sourceConnectionId: crypto.randomUUID(),
161
+ sourceExternalRepositoryId: 'repo',
162
+ name: 'Project',
163
+ },
164
+ }),
165
+ }),
166
+ ],
167
+ });
168
+ mocks.createSecretsModule.mockReturnValue({
169
+ name: 'secrets',
170
+ interModulePresentations: [
171
+ defineInterModulePresentation(secretsInterModuleContract, {
172
+ deleteSecrets: mocks.deleteSecrets,
173
+ getSecret: mocks.getSecret,
174
+ getSecretsByNamespace: vi.fn(),
175
+ getVariablesByNamespace: vi.fn(),
176
+ setSecrets: mocks.setSecrets,
177
+ }),
178
+ ],
179
+ });
180
+ mocks.createAgentModule.mockReturnValue({
181
+ name: 'agent',
182
+ interModulePresentations: [
183
+ {
184
+ contract: agentInterModuleContract,
185
+ handlers: {resolveAgentConfig: vi.fn(), resolveRuntimeCredentials: vi.fn()},
186
+ },
187
+ ],
188
+ });
189
+ mocks.createRunnersModule.mockReturnValue({
190
+ name: 'runners',
191
+ interModulePresentations: [
192
+ {
193
+ contract: runnersInterModuleContract,
194
+ handlers: {
195
+ cancelJobs: vi.fn(),
196
+ enqueueJobExecution: vi.fn(),
197
+ getEffectiveRunnerToolCapabilities: vi.fn(),
198
+ getLeaseState: vi.fn(),
199
+ releaseJobExecution: vi.fn(),
200
+ },
201
+ },
202
+ ],
203
+ });
204
+ mocks.createDefinitionsModule.mockReturnValue({
205
+ name: 'definitions',
206
+ interModulePresentations: [
207
+ {
208
+ contract: definitionsInterModuleContract,
209
+ handlers: {getDefinitionForWorkflowRun: vi.fn()},
210
+ },
211
+ ],
212
+ });
213
+ mocks.createWorkflowsModule.mockReturnValue({
214
+ name: 'workflows',
215
+ interModulePresentations: [
216
+ {
217
+ contract: workflowsInterModuleContract,
218
+ handlers: {
219
+ deliverEventToJobListener: vi.fn(),
220
+ getLeasedAgentToolContext: vi.fn(),
221
+ getStepLogContext: vi.fn(),
222
+ startRunFromTrigger: vi.fn(),
223
+ },
224
+ },
225
+ ],
226
+ });
227
+ mocks.createTriggersModule.mockReturnValue({name: 'triggers'});
77
228
  });
78
229
 
79
230
  it('returns the API modules in lifecycle order', async () => {
80
231
  const modules = await defaultModules();
81
232
 
82
233
  expect(modules.map((module) => module.name)).toEqual([
234
+ 'email-challenges',
83
235
  'auth',
84
236
  'workspaces',
85
237
  'secrets',
@@ -96,12 +248,19 @@ describe('defaultModules', () => {
96
248
  ]);
97
249
  });
98
250
 
99
- it('uses the leased-step loader and namespaced provider secrets', async () => {
251
+ it('injects Auth into the Runners module', async () => {
252
+ await defaultModules();
253
+
254
+ expect(mocks.createRunnersModule).toHaveBeenCalledWith({auth: expect.any(Object)});
255
+ });
256
+
257
+ it('injects Workflows into integrations and logs and namespaces provider secrets', async () => {
100
258
  await defaultModules();
101
259
 
102
260
  expect(mocks.createIntegrationsContext).toHaveBeenCalledWith({
261
+ workspaces: expect.anything(),
103
262
  secrets: {
104
- deleteSecrets: mocks.deleteSecrets,
263
+ deleteSecrets: expect.any(Function),
105
264
  linear: {
106
265
  deleteSecrets: expect.any(Function),
107
266
  getSecret: expect.any(Function),
@@ -118,64 +277,114 @@ describe('defaultModules', () => {
118
277
  setSecrets: expect.any(Function),
119
278
  },
120
279
  },
121
- agentTools: {loadLeasedAgentStep: mocks.loadRunningLeasedStep},
280
+ agentTools: {workflows: expect.objectContaining({getStepLogContext: expect.any(Function)})},
281
+ webhookDeliverySource: undefined,
122
282
  });
123
283
 
124
284
  const integrationsOptions = mocks.createIntegrationsContext.mock.calls[0]?.[0] as {
125
285
  secrets: {
126
- linear: {
127
- deleteSecrets: (params: {namespace: string}) => unknown;
128
- getSecret: (params: {namespace: string}) => unknown;
129
- setSecrets: (params: {namespace: string}) => unknown;
130
- };
131
- jira: {
132
- deleteSecrets: (params: {namespace: string}) => unknown;
133
- getSecret: (params: {namespace: string}) => unknown;
134
- setSecrets: (params: {namespace: string}) => unknown;
135
- };
136
- slack: {
137
- deleteSecrets: (params: {namespace: string}) => unknown;
138
- getSecret: (params: {namespace: string}) => unknown;
139
- setSecrets: (params: {namespace: string}) => unknown;
140
- };
286
+ linear: Pick<SecretsInterModuleClient, 'deleteSecrets' | 'getSecret' | 'setSecrets'>;
287
+ jira: Pick<SecretsInterModuleClient, 'deleteSecrets' | 'getSecret' | 'setSecrets'>;
288
+ slack: Pick<SecretsInterModuleClient, 'deleteSecrets' | 'getSecret' | 'setSecrets'>;
141
289
  };
290
+ agentTools: {workflows: unknown};
142
291
  };
143
- integrationsOptions.secrets.linear.getSecret({namespace: 'workspace'});
144
- integrationsOptions.secrets.linear.setSecrets({namespace: 'workspace'});
145
- integrationsOptions.secrets.linear.deleteSecrets({namespace: 'workspace'});
146
- integrationsOptions.secrets.jira.getSecret({namespace: 'workspace'});
147
- integrationsOptions.secrets.jira.setSecrets({namespace: 'workspace'});
148
- integrationsOptions.secrets.jira.deleteSecrets({namespace: 'workspace'});
149
- integrationsOptions.secrets.slack.getSecret({namespace: 'workspace'});
150
- integrationsOptions.secrets.slack.setSecrets({namespace: 'workspace'});
151
- integrationsOptions.secrets.slack.deleteSecrets({namespace: 'workspace'});
152
-
153
- expect(mocks.getSecret).toHaveBeenCalledWith({
292
+ expect(integrationsOptions.agentTools.workflows).toEqual(
293
+ expect.objectContaining({getLeasedAgentToolContext: expect.any(Function)}),
294
+ );
295
+ expect(mocks.createLogsModule).toHaveBeenCalledWith({
296
+ workflows: expect.objectContaining({getStepLogContext: expect.any(Function)}),
297
+ jobLeaseTokenTtlSeconds: 5400,
298
+ });
299
+
300
+ const scope = {workspaceId: crypto.randomUUID(), projectId: null, namespace: 'workspace'};
301
+ await Promise.all([
302
+ integrationsOptions.secrets.linear.getSecret({...scope, key: 'token'}),
303
+ integrationsOptions.secrets.linear.setSecrets({
304
+ ...scope,
305
+ values: {token: 'secret'},
306
+ editedBy: undefined,
307
+ }),
308
+ integrationsOptions.secrets.linear.deleteSecrets({...scope, keys: ['token']}),
309
+ integrationsOptions.secrets.jira.getSecret({...scope, key: 'token'}),
310
+ integrationsOptions.secrets.jira.setSecrets({
311
+ ...scope,
312
+ values: {token: 'secret'},
313
+ editedBy: undefined,
314
+ }),
315
+ integrationsOptions.secrets.jira.deleteSecrets({...scope, keys: ['token']}),
316
+ integrationsOptions.secrets.slack.getSecret({...scope, key: 'token'}),
317
+ integrationsOptions.secrets.slack.setSecrets({
318
+ ...scope,
319
+ values: {token: 'secret'},
320
+ editedBy: undefined,
321
+ }),
322
+ integrationsOptions.secrets.slack.deleteSecrets({...scope, keys: ['token']}),
323
+ ]);
324
+
325
+ expect(mocks.getSecret.mock.calls.map(([params]) => params)).toContainEqual({
326
+ key: 'token',
154
327
  namespace: 'system/integrations/linear/workspace',
328
+ projectId: null,
329
+ workspaceId: scope.workspaceId,
155
330
  });
156
- expect(mocks.setSecrets).toHaveBeenCalledWith({
331
+ expect(mocks.setSecrets.mock.calls.map(([params]) => params)).toContainEqual({
332
+ values: {token: 'secret'},
157
333
  namespace: 'system/integrations/linear/workspace',
334
+ projectId: null,
335
+ workspaceId: scope.workspaceId,
158
336
  });
159
- expect(mocks.deleteSecrets).toHaveBeenCalledWith({
337
+ expect(mocks.deleteSecrets.mock.calls.map(([params]) => params)).toContainEqual({
338
+ keys: ['token'],
160
339
  namespace: 'system/integrations/linear/workspace',
340
+ projectId: null,
341
+ workspaceId: scope.workspaceId,
161
342
  });
162
- expect(mocks.getSecret).toHaveBeenCalledWith({
343
+ expect(mocks.getSecret.mock.calls.map(([params]) => params)).toContainEqual({
344
+ key: 'token',
163
345
  namespace: 'system/integrations/jira/workspace',
346
+ projectId: null,
347
+ workspaceId: scope.workspaceId,
164
348
  });
165
- expect(mocks.setSecrets).toHaveBeenCalledWith({
349
+ expect(mocks.setSecrets.mock.calls.map(([params]) => params)).toContainEqual({
350
+ values: {token: 'secret'},
166
351
  namespace: 'system/integrations/jira/workspace',
352
+ projectId: null,
353
+ workspaceId: scope.workspaceId,
167
354
  });
168
- expect(mocks.deleteSecrets).toHaveBeenCalledWith({
355
+ expect(mocks.deleteSecrets.mock.calls.map(([params]) => params)).toContainEqual({
356
+ keys: ['token'],
169
357
  namespace: 'system/integrations/jira/workspace',
358
+ projectId: null,
359
+ workspaceId: scope.workspaceId,
170
360
  });
171
- expect(mocks.getSecret).toHaveBeenCalledWith({
361
+ expect(mocks.getSecret.mock.calls.map(([params]) => params)).toContainEqual({
362
+ key: 'token',
172
363
  namespace: 'system/integrations/slack/workspace',
364
+ projectId: null,
365
+ workspaceId: scope.workspaceId,
173
366
  });
174
- expect(mocks.setSecrets).toHaveBeenCalledWith({
367
+ expect(mocks.setSecrets.mock.calls.map(([params]) => params)).toContainEqual({
368
+ values: {token: 'secret'},
175
369
  namespace: 'system/integrations/slack/workspace',
370
+ projectId: null,
371
+ workspaceId: scope.workspaceId,
176
372
  });
177
- expect(mocks.deleteSecrets).toHaveBeenCalledWith({
373
+ expect(mocks.deleteSecrets.mock.calls.map(([params]) => params)).toContainEqual({
374
+ keys: ['token'],
178
375
  namespace: 'system/integrations/slack/workspace',
376
+ projectId: null,
377
+ workspaceId: scope.workspaceId,
179
378
  });
180
379
  });
380
+
381
+ it('passes an optional webhook delivery source to integration composition', async () => {
382
+ const webhookDeliverySource = {createService: vi.fn()};
383
+
384
+ await defaultModules({webhookDeliverySource});
385
+
386
+ expect(mocks.createIntegrationsContext).toHaveBeenCalledWith(
387
+ expect.objectContaining({webhookDeliverySource}),
388
+ );
389
+ });
181
390
  });