@shipfox/api-server 4.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": "4.0.0",
5
+ "version": "6.0.0",
6
6
  "repository": {
7
7
  "type": "git",
8
8
  "url": "git+https://github.com/ShipfoxHQ/shipfox.git",
@@ -12,66 +12,65 @@
12
12
  "main": "dist/index.js",
13
13
  "types": "dist/index.d.ts",
14
14
  "imports": {
15
- "#*": {
16
- "workspace-source": "./src/*",
17
- "development": "./src/*",
18
- "default": "./dist/*"
19
- }
15
+ "#*": "./dist/*"
20
16
  },
21
17
  "exports": {
22
18
  ".": {
23
- "development": {
24
- "types": "./src/index.ts",
25
- "default": "./src/index.ts"
26
- },
27
- "default": {
28
- "types": "./dist/index.d.ts",
29
- "default": "./dist/index.js"
30
- }
19
+ "types": "./dist/index.d.ts",
20
+ "default": "./dist/index.js"
31
21
  },
32
22
  "./instrumentation": {
33
- "development": {
34
- "types": "./src/instrumentation.ts",
35
- "default": "./src/instrumentation.ts"
36
- },
37
- "default": {
38
- "types": "./dist/instrumentation.d.ts",
39
- "default": "./dist/instrumentation.js"
40
- }
23
+ "types": "./dist/instrumentation.d.ts",
24
+ "default": "./dist/instrumentation.js"
41
25
  }
42
26
  },
43
27
  "dependencies": {
44
- "@shipfox/annotations": "4.0.0",
45
- "@shipfox/api-agent": "4.0.0",
46
- "@shipfox/api-auth": "4.0.0",
47
- "@shipfox/api-definitions": "4.0.0",
48
- "@shipfox/api-dispatcher": "4.0.0",
49
- "@shipfox/api-integration-core": "4.0.0",
50
- "@shipfox/api-logs": "4.0.0",
51
- "@shipfox/api-projects": "4.0.0",
52
- "@shipfox/api-runners": "4.0.0",
53
- "@shipfox/api-secrets": "4.0.0",
54
- "@shipfox/api-triggers": "4.0.0",
55
- "@shipfox/api-workflows": "4.0.0",
56
- "@shipfox/api-workspaces": "4.0.0",
57
- "@shipfox/config": "1.2.1",
58
- "@shipfox/node-error-monitoring": "0.1.2",
59
- "@shipfox/node-fastify": "0.2.2",
60
- "@shipfox/node-module": "0.3.1",
61
- "@shipfox/node-opentelemetry": "0.5.1",
62
- "@shipfox/node-postgres": "0.4.1"
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",
52
+ "@shipfox/config": "1.2.2",
53
+ "@shipfox/node-error-monitoring": "0.1.3",
54
+ "@shipfox/node-fastify": "0.2.4",
55
+ "@shipfox/node-jwt": "0.3.0",
56
+ "@shipfox/node-module": "0.4.0",
57
+ "@shipfox/node-opentelemetry": "0.5.2",
58
+ "@shipfox/node-postgres": "0.4.2"
63
59
  },
64
60
  "devDependencies": {
65
- "@shipfox/biome": "1.8.1",
66
- "@shipfox/swc": "1.2.5",
61
+ "@shipfox/biome": "1.8.2",
62
+ "@shipfox/depcruise": "1.0.2",
63
+ "@shipfox/inter-module": "0.2.0",
64
+ "@shipfox/swc": "1.2.6",
67
65
  "@shipfox/ts-config": "1.3.8",
68
- "@shipfox/typescript": "1.1.6",
69
- "@shipfox/vitest": "1.2.2"
66
+ "@shipfox/typescript": "1.1.7",
67
+ "@shipfox/vitest": "1.2.3"
70
68
  },
71
69
  "scripts": {
72
70
  "build": "shipfox-swc",
73
71
  "check": "shipfox-biome-check",
74
72
  "check:fix": "shipfox-biome-check --write",
73
+ "depcruise": "shipfox-depcruise",
75
74
  "test": "shipfox-vitest-run",
76
75
  "test:watch": "shipfox-vitest-watch",
77
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,64 +248,143 @@ 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),
108
267
  setSecrets: expect.any(Function),
109
268
  },
269
+ jira: {
270
+ deleteSecrets: expect.any(Function),
271
+ getSecret: expect.any(Function),
272
+ setSecrets: expect.any(Function),
273
+ },
110
274
  slack: {
111
275
  deleteSecrets: expect.any(Function),
112
276
  getSecret: expect.any(Function),
113
277
  setSecrets: expect.any(Function),
114
278
  },
115
279
  },
116
- agentTools: {loadLeasedAgentStep: mocks.loadRunningLeasedStep},
280
+ agentTools: {workflows: expect.objectContaining({getStepLogContext: expect.any(Function)})},
281
+ webhookDeliverySource: undefined,
117
282
  });
118
283
 
119
284
  const integrationsOptions = mocks.createIntegrationsContext.mock.calls[0]?.[0] as {
120
285
  secrets: {
121
- linear: {
122
- deleteSecrets: (params: {namespace: string}) => unknown;
123
- getSecret: (params: {namespace: string}) => unknown;
124
- setSecrets: (params: {namespace: string}) => unknown;
125
- };
126
- slack: {
127
- deleteSecrets: (params: {namespace: string}) => unknown;
128
- getSecret: (params: {namespace: string}) => unknown;
129
- setSecrets: (params: {namespace: string}) => unknown;
130
- };
286
+ linear: Pick<SecretsInterModuleClient, 'deleteSecrets' | 'getSecret' | 'setSecrets'>;
287
+ jira: Pick<SecretsInterModuleClient, 'deleteSecrets' | 'getSecret' | 'setSecrets'>;
288
+ slack: Pick<SecretsInterModuleClient, 'deleteSecrets' | 'getSecret' | 'setSecrets'>;
131
289
  };
290
+ agentTools: {workflows: unknown};
132
291
  };
133
- integrationsOptions.secrets.linear.getSecret({namespace: 'workspace'});
134
- integrationsOptions.secrets.linear.setSecrets({namespace: 'workspace'});
135
- integrationsOptions.secrets.linear.deleteSecrets({namespace: 'workspace'});
136
- integrationsOptions.secrets.slack.getSecret({namespace: 'workspace'});
137
- integrationsOptions.secrets.slack.setSecrets({namespace: 'workspace'});
138
- integrationsOptions.secrets.slack.deleteSecrets({namespace: 'workspace'});
139
-
140
- 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',
141
327
  namespace: 'system/integrations/linear/workspace',
328
+ projectId: null,
329
+ workspaceId: scope.workspaceId,
142
330
  });
143
- expect(mocks.setSecrets).toHaveBeenCalledWith({
331
+ expect(mocks.setSecrets.mock.calls.map(([params]) => params)).toContainEqual({
332
+ values: {token: 'secret'},
144
333
  namespace: 'system/integrations/linear/workspace',
334
+ projectId: null,
335
+ workspaceId: scope.workspaceId,
145
336
  });
146
- expect(mocks.deleteSecrets).toHaveBeenCalledWith({
337
+ expect(mocks.deleteSecrets.mock.calls.map(([params]) => params)).toContainEqual({
338
+ keys: ['token'],
147
339
  namespace: 'system/integrations/linear/workspace',
340
+ projectId: null,
341
+ workspaceId: scope.workspaceId,
342
+ });
343
+ expect(mocks.getSecret.mock.calls.map(([params]) => params)).toContainEqual({
344
+ key: 'token',
345
+ namespace: 'system/integrations/jira/workspace',
346
+ projectId: null,
347
+ workspaceId: scope.workspaceId,
348
+ });
349
+ expect(mocks.setSecrets.mock.calls.map(([params]) => params)).toContainEqual({
350
+ values: {token: 'secret'},
351
+ namespace: 'system/integrations/jira/workspace',
352
+ projectId: null,
353
+ workspaceId: scope.workspaceId,
148
354
  });
149
- expect(mocks.getSecret).toHaveBeenCalledWith({
355
+ expect(mocks.deleteSecrets.mock.calls.map(([params]) => params)).toContainEqual({
356
+ keys: ['token'],
357
+ namespace: 'system/integrations/jira/workspace',
358
+ projectId: null,
359
+ workspaceId: scope.workspaceId,
360
+ });
361
+ expect(mocks.getSecret.mock.calls.map(([params]) => params)).toContainEqual({
362
+ key: 'token',
150
363
  namespace: 'system/integrations/slack/workspace',
364
+ projectId: null,
365
+ workspaceId: scope.workspaceId,
151
366
  });
152
- expect(mocks.setSecrets).toHaveBeenCalledWith({
367
+ expect(mocks.setSecrets.mock.calls.map(([params]) => params)).toContainEqual({
368
+ values: {token: 'secret'},
153
369
  namespace: 'system/integrations/slack/workspace',
370
+ projectId: null,
371
+ workspaceId: scope.workspaceId,
154
372
  });
155
- expect(mocks.deleteSecrets).toHaveBeenCalledWith({
373
+ expect(mocks.deleteSecrets.mock.calls.map(([params]) => params)).toContainEqual({
374
+ keys: ['token'],
156
375
  namespace: 'system/integrations/slack/workspace',
376
+ projectId: null,
377
+ workspaceId: scope.workspaceId,
157
378
  });
158
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
+ });
159
390
  });