@friggframework/admin-scripts 2.0.0--canary.517.f04156f.0 → 2.0.0--canary.517.300ded3.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/index.js +2 -2
- package/package.json +6 -8
- package/src/application/__tests__/admin-frigg-commands.test.js +18 -18
- package/src/application/__tests__/script-runner.test.js +144 -14
- package/src/application/admin-frigg-commands.js +7 -7
- package/src/application/admin-script-base.js +2 -4
- package/src/application/script-runner.js +121 -127
- package/src/infrastructure/__tests__/admin-auth-middleware.test.js +32 -95
- package/src/infrastructure/__tests__/admin-script-router.test.js +24 -25
- package/src/infrastructure/admin-auth-middleware.js +5 -43
- package/src/infrastructure/admin-script-router.js +14 -16
- package/src/infrastructure/script-executor-handler.js +2 -2
- package/src/application/__tests__/dry-run-http-interceptor.test.js +0 -313
- package/src/application/__tests__/dry-run-repository-wrapper.test.js +0 -257
- package/src/application/dry-run-http-interceptor.js +0 -296
- package/src/application/dry-run-repository-wrapper.js +0 -261
|
@@ -1,49 +1,11 @@
|
|
|
1
|
-
const { createAdminScriptCommands } = require('@friggframework/core/application/commands/admin-script-commands');
|
|
2
|
-
|
|
3
1
|
/**
|
|
4
2
|
* Admin API Key Authentication Middleware
|
|
5
3
|
*
|
|
6
|
-
*
|
|
7
|
-
*
|
|
4
|
+
* Re-exports shared admin auth middleware from @friggframework/core.
|
|
5
|
+
* Uses simple ENV-based API key validation.
|
|
6
|
+
* Expects: x-frigg-admin-api-key header
|
|
8
7
|
*/
|
|
9
|
-
async function adminAuthMiddleware(req, res, next) {
|
|
10
|
-
try {
|
|
11
|
-
const authHeader = req.headers.authorization;
|
|
12
|
-
|
|
13
|
-
if (!authHeader || !authHeader.startsWith('Bearer ')) {
|
|
14
|
-
return res.status(401).json({
|
|
15
|
-
error: 'Missing or invalid Authorization header',
|
|
16
|
-
code: 'MISSING_AUTH'
|
|
17
|
-
});
|
|
18
|
-
}
|
|
19
|
-
|
|
20
|
-
const apiKey = authHeader.substring(7); // Remove 'Bearer '
|
|
21
|
-
const commands = createAdminScriptCommands();
|
|
22
|
-
const result = await commands.validateAdminApiKey(apiKey);
|
|
23
|
-
|
|
24
|
-
if (result.error) {
|
|
25
|
-
return res.status(result.error).json({
|
|
26
|
-
error: result.reason,
|
|
27
|
-
code: result.code
|
|
28
|
-
});
|
|
29
|
-
}
|
|
30
|
-
|
|
31
|
-
// Attach validated key info to request for audit trail
|
|
32
|
-
req.adminApiKey = result.apiKey;
|
|
33
|
-
req.adminAudit = {
|
|
34
|
-
apiKeyName: result.apiKey.name,
|
|
35
|
-
apiKeyLast4: result.apiKey.keyLast4,
|
|
36
|
-
ipAddress: req.ip || req.connection?.remoteAddress || 'unknown'
|
|
37
|
-
};
|
|
38
8
|
|
|
39
|
-
|
|
40
|
-
} catch (error) {
|
|
41
|
-
console.error('Admin auth middleware error:', error);
|
|
42
|
-
res.status(500).json({
|
|
43
|
-
error: 'Authentication failed',
|
|
44
|
-
code: 'AUTH_ERROR'
|
|
45
|
-
});
|
|
46
|
-
}
|
|
47
|
-
}
|
|
9
|
+
const { validateAdminApiKey } = require('@friggframework/core/handlers/middleware/admin-auth');
|
|
48
10
|
|
|
49
|
-
module.exports = {
|
|
11
|
+
module.exports = { validateAdminApiKey };
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
const express = require('express');
|
|
2
2
|
const serverless = require('serverless-http');
|
|
3
|
-
const {
|
|
3
|
+
const { validateAdminApiKey } = require('./admin-auth-middleware');
|
|
4
4
|
const { getScriptFactory } = require('../application/script-factory');
|
|
5
5
|
const { createScriptRunner } = require('../application/script-runner');
|
|
6
6
|
const { createAdminScriptCommands } = require('@friggframework/core/application/commands/admin-script-commands');
|
|
@@ -11,7 +11,7 @@ const { ScheduleManagementUseCase } = require('../application/schedule-managemen
|
|
|
11
11
|
const router = express.Router();
|
|
12
12
|
|
|
13
13
|
// Apply auth middleware to all admin routes
|
|
14
|
-
router.use(
|
|
14
|
+
router.use(validateAdminApiKey);
|
|
15
15
|
|
|
16
16
|
/**
|
|
17
17
|
* Create ScheduleManagementUseCase instance
|
|
@@ -87,10 +87,10 @@ router.get('/scripts/:scriptName', async (req, res) => {
|
|
|
87
87
|
});
|
|
88
88
|
|
|
89
89
|
/**
|
|
90
|
-
* POST /admin/scripts/:scriptName
|
|
90
|
+
* POST /admin/scripts/:scriptName
|
|
91
91
|
* Execute a script (sync, async, or dry-run)
|
|
92
92
|
*/
|
|
93
|
-
router.post('/scripts/:scriptName
|
|
93
|
+
router.post('/scripts/:scriptName', async (req, res) => {
|
|
94
94
|
try {
|
|
95
95
|
const { scriptName } = req.params;
|
|
96
96
|
const { params = {}, mode = 'async', dryRun = false } = req.body;
|
|
@@ -110,7 +110,6 @@ router.post('/scripts/:scriptName/execute', async (req, res) => {
|
|
|
110
110
|
trigger: 'MANUAL',
|
|
111
111
|
mode: 'sync',
|
|
112
112
|
dryRun: true,
|
|
113
|
-
audit: req.adminAudit,
|
|
114
113
|
});
|
|
115
114
|
return res.json(result);
|
|
116
115
|
}
|
|
@@ -121,20 +120,18 @@ router.post('/scripts/:scriptName/execute', async (req, res) => {
|
|
|
121
120
|
const result = await runner.execute(scriptName, params, {
|
|
122
121
|
trigger: 'MANUAL',
|
|
123
122
|
mode: 'sync',
|
|
124
|
-
audit: req.adminAudit,
|
|
125
123
|
});
|
|
126
124
|
return res.json(result);
|
|
127
125
|
}
|
|
128
126
|
|
|
129
127
|
// Async execution - queue and return immediately
|
|
130
128
|
const commands = createAdminScriptCommands();
|
|
131
|
-
const execution = await commands.
|
|
129
|
+
const execution = await commands.createAdminProcess({
|
|
132
130
|
scriptName,
|
|
133
131
|
scriptVersion: factory.get(scriptName).Definition.version,
|
|
134
132
|
trigger: 'MANUAL',
|
|
135
133
|
mode: 'async',
|
|
136
134
|
input: params,
|
|
137
|
-
audit: req.adminAudit,
|
|
138
135
|
});
|
|
139
136
|
|
|
140
137
|
// Queue the execution
|
|
@@ -161,14 +158,14 @@ router.post('/scripts/:scriptName/execute', async (req, res) => {
|
|
|
161
158
|
});
|
|
162
159
|
|
|
163
160
|
/**
|
|
164
|
-
* GET /admin/executions/:executionId
|
|
165
|
-
* Get execution status
|
|
161
|
+
* GET /admin/scripts/:scriptName/executions/:executionId
|
|
162
|
+
* Get execution status for specific script
|
|
166
163
|
*/
|
|
167
|
-
router.get('/executions/:executionId', async (req, res) => {
|
|
164
|
+
router.get('/scripts/:scriptName/executions/:executionId', async (req, res) => {
|
|
168
165
|
try {
|
|
169
166
|
const { executionId } = req.params;
|
|
170
167
|
const commands = createAdminScriptCommands();
|
|
171
|
-
const execution = await commands.
|
|
168
|
+
const execution = await commands.findAdminProcessById(executionId);
|
|
172
169
|
|
|
173
170
|
if (execution.error) {
|
|
174
171
|
return res.status(execution.error).json({
|
|
@@ -185,12 +182,13 @@ router.get('/executions/:executionId', async (req, res) => {
|
|
|
185
182
|
});
|
|
186
183
|
|
|
187
184
|
/**
|
|
188
|
-
* GET /admin/executions
|
|
189
|
-
* List recent executions
|
|
185
|
+
* GET /admin/scripts/:scriptName/executions
|
|
186
|
+
* List recent executions for specific script
|
|
190
187
|
*/
|
|
191
|
-
router.get('/executions', async (req, res) => {
|
|
188
|
+
router.get('/scripts/:scriptName/executions', async (req, res) => {
|
|
192
189
|
try {
|
|
193
|
-
const { scriptName
|
|
190
|
+
const { scriptName } = req.params;
|
|
191
|
+
const { status, limit = 50 } = req.query;
|
|
194
192
|
const commands = createAdminScriptCommands();
|
|
195
193
|
|
|
196
194
|
const executions = await commands.findRecentExecutions({
|
|
@@ -21,7 +21,7 @@ async function handler(event) {
|
|
|
21
21
|
|
|
22
22
|
// If executionId provided (async from API), update existing record
|
|
23
23
|
if (executionId) {
|
|
24
|
-
await commands.
|
|
24
|
+
await commands.updateAdminProcessState(executionId, 'RUNNING');
|
|
25
25
|
}
|
|
26
26
|
|
|
27
27
|
const result = await runner.execute(scriptName, params, {
|
|
@@ -45,7 +45,7 @@ async function handler(event) {
|
|
|
45
45
|
if (executionId) {
|
|
46
46
|
const commands = createAdminScriptCommands();
|
|
47
47
|
await commands
|
|
48
|
-
.
|
|
48
|
+
.completeAdminProcess(executionId, {
|
|
49
49
|
status: 'FAILED',
|
|
50
50
|
error: {
|
|
51
51
|
name: error.name,
|
|
@@ -1,313 +0,0 @@
|
|
|
1
|
-
const {
|
|
2
|
-
createDryRunHttpClient,
|
|
3
|
-
injectDryRunHttpClient,
|
|
4
|
-
sanitizeHeaders,
|
|
5
|
-
sanitizeData,
|
|
6
|
-
detectService,
|
|
7
|
-
} = require('../dry-run-http-interceptor');
|
|
8
|
-
|
|
9
|
-
describe('Dry-Run HTTP Interceptor', () => {
|
|
10
|
-
describe('sanitizeHeaders', () => {
|
|
11
|
-
test('should redact authorization headers', () => {
|
|
12
|
-
const headers = {
|
|
13
|
-
'Content-Type': 'application/json',
|
|
14
|
-
Authorization: 'Bearer secret-token',
|
|
15
|
-
'X-API-Key': 'api-key-123',
|
|
16
|
-
'User-Agent': 'frigg/1.0',
|
|
17
|
-
};
|
|
18
|
-
|
|
19
|
-
const sanitized = sanitizeHeaders(headers);
|
|
20
|
-
|
|
21
|
-
expect(sanitized['Content-Type']).toBe('application/json');
|
|
22
|
-
expect(sanitized['User-Agent']).toBe('frigg/1.0');
|
|
23
|
-
expect(sanitized.Authorization).toBe('[REDACTED]');
|
|
24
|
-
expect(sanitized['X-API-Key']).toBe('[REDACTED]');
|
|
25
|
-
});
|
|
26
|
-
|
|
27
|
-
test('should handle case variations', () => {
|
|
28
|
-
const headers = {
|
|
29
|
-
authorization: 'Bearer token',
|
|
30
|
-
Authorization: 'Bearer token',
|
|
31
|
-
'x-api-key': 'key1',
|
|
32
|
-
'X-API-Key': 'key2',
|
|
33
|
-
};
|
|
34
|
-
|
|
35
|
-
const sanitized = sanitizeHeaders(headers);
|
|
36
|
-
|
|
37
|
-
expect(sanitized.authorization).toBe('[REDACTED]');
|
|
38
|
-
expect(sanitized.Authorization).toBe('[REDACTED]');
|
|
39
|
-
expect(sanitized['x-api-key']).toBe('[REDACTED]');
|
|
40
|
-
expect(sanitized['X-API-Key']).toBe('[REDACTED]');
|
|
41
|
-
});
|
|
42
|
-
|
|
43
|
-
test('should handle null/undefined', () => {
|
|
44
|
-
expect(sanitizeHeaders(null)).toEqual({});
|
|
45
|
-
expect(sanitizeHeaders(undefined)).toEqual({});
|
|
46
|
-
expect(sanitizeHeaders({})).toEqual({});
|
|
47
|
-
});
|
|
48
|
-
});
|
|
49
|
-
|
|
50
|
-
describe('detectService', () => {
|
|
51
|
-
test('should detect CRM services', () => {
|
|
52
|
-
expect(detectService('https://api.hubapi.com')).toBe('HubSpot');
|
|
53
|
-
expect(detectService('https://login.salesforce.com')).toBe('Salesforce');
|
|
54
|
-
expect(detectService('https://api.pipedrive.com')).toBe('Pipedrive');
|
|
55
|
-
expect(detectService('https://api.attio.com')).toBe('Attio');
|
|
56
|
-
});
|
|
57
|
-
|
|
58
|
-
test('should detect communication services', () => {
|
|
59
|
-
expect(detectService('https://slack.com/api')).toBe('Slack');
|
|
60
|
-
expect(detectService('https://discord.com/api')).toBe('Discord');
|
|
61
|
-
expect(detectService('https://graph.teams.microsoft.com')).toBe('Microsoft Teams');
|
|
62
|
-
});
|
|
63
|
-
|
|
64
|
-
test('should detect project management tools', () => {
|
|
65
|
-
expect(detectService('https://app.asana.com/api')).toBe('Asana');
|
|
66
|
-
expect(detectService('https://api.monday.com')).toBe('Monday.com');
|
|
67
|
-
expect(detectService('https://api.trello.com')).toBe('Trello');
|
|
68
|
-
});
|
|
69
|
-
|
|
70
|
-
test('should return unknown for unrecognized services', () => {
|
|
71
|
-
expect(detectService('https://example.com/api')).toBe('unknown');
|
|
72
|
-
expect(detectService(null)).toBe('unknown');
|
|
73
|
-
expect(detectService(undefined)).toBe('unknown');
|
|
74
|
-
});
|
|
75
|
-
|
|
76
|
-
test('should be case insensitive', () => {
|
|
77
|
-
expect(detectService('HTTPS://API.HUBSPOT.COM')).toBe('HubSpot');
|
|
78
|
-
expect(detectService('https://API.SLACK.COM')).toBe('Slack');
|
|
79
|
-
});
|
|
80
|
-
});
|
|
81
|
-
|
|
82
|
-
describe('sanitizeData', () => {
|
|
83
|
-
test('should redact sensitive fields', () => {
|
|
84
|
-
const data = {
|
|
85
|
-
name: 'Test User',
|
|
86
|
-
email: 'test@example.com',
|
|
87
|
-
password: 'secret123',
|
|
88
|
-
apiToken: 'token-abc',
|
|
89
|
-
authKey: 'key-xyz',
|
|
90
|
-
};
|
|
91
|
-
|
|
92
|
-
const sanitized = sanitizeData(data);
|
|
93
|
-
|
|
94
|
-
expect(sanitized.name).toBe('Test User');
|
|
95
|
-
expect(sanitized.email).toBe('test@example.com');
|
|
96
|
-
expect(sanitized.password).toBe('[REDACTED]');
|
|
97
|
-
expect(sanitized.apiToken).toBe('[REDACTED]');
|
|
98
|
-
expect(sanitized.authKey).toBe('[REDACTED]');
|
|
99
|
-
});
|
|
100
|
-
|
|
101
|
-
test('should handle nested objects', () => {
|
|
102
|
-
const data = {
|
|
103
|
-
user: {
|
|
104
|
-
name: 'Test',
|
|
105
|
-
credentials: {
|
|
106
|
-
password: 'secret',
|
|
107
|
-
token: 'abc123',
|
|
108
|
-
},
|
|
109
|
-
},
|
|
110
|
-
};
|
|
111
|
-
|
|
112
|
-
const sanitized = sanitizeData(data);
|
|
113
|
-
|
|
114
|
-
expect(sanitized.user.name).toBe('Test');
|
|
115
|
-
expect(sanitized.user.credentials.password).toBe('[REDACTED]');
|
|
116
|
-
expect(sanitized.user.credentials.token).toBe('[REDACTED]');
|
|
117
|
-
});
|
|
118
|
-
|
|
119
|
-
test('should handle arrays', () => {
|
|
120
|
-
const data = [
|
|
121
|
-
{ id: '1', password: 'secret1' },
|
|
122
|
-
{ id: '2', apiKey: 'key2' },
|
|
123
|
-
];
|
|
124
|
-
|
|
125
|
-
const sanitized = sanitizeData(data);
|
|
126
|
-
|
|
127
|
-
expect(sanitized[0].id).toBe('1');
|
|
128
|
-
expect(sanitized[0].password).toBe('[REDACTED]');
|
|
129
|
-
expect(sanitized[1].apiKey).toBe('[REDACTED]');
|
|
130
|
-
});
|
|
131
|
-
|
|
132
|
-
test('should preserve primitives', () => {
|
|
133
|
-
expect(sanitizeData('string')).toBe('string');
|
|
134
|
-
expect(sanitizeData(123)).toBe(123);
|
|
135
|
-
expect(sanitizeData(true)).toBe(true);
|
|
136
|
-
expect(sanitizeData(null)).toBe(null);
|
|
137
|
-
expect(sanitizeData(undefined)).toBe(undefined);
|
|
138
|
-
});
|
|
139
|
-
});
|
|
140
|
-
|
|
141
|
-
describe('createDryRunHttpClient', () => {
|
|
142
|
-
let operationLog;
|
|
143
|
-
|
|
144
|
-
beforeEach(() => {
|
|
145
|
-
operationLog = [];
|
|
146
|
-
});
|
|
147
|
-
|
|
148
|
-
test('should log GET requests', async () => {
|
|
149
|
-
const client = createDryRunHttpClient(operationLog);
|
|
150
|
-
|
|
151
|
-
const response = await client.get('/contacts', {
|
|
152
|
-
baseURL: 'https://api.hubapi.com',
|
|
153
|
-
headers: { Authorization: 'Bearer token' },
|
|
154
|
-
});
|
|
155
|
-
|
|
156
|
-
expect(operationLog).toHaveLength(1);
|
|
157
|
-
expect(operationLog[0]).toMatchObject({
|
|
158
|
-
operation: 'HTTP_REQUEST',
|
|
159
|
-
method: 'GET',
|
|
160
|
-
url: 'https://api.hubapi.com/contacts',
|
|
161
|
-
service: 'HubSpot',
|
|
162
|
-
});
|
|
163
|
-
|
|
164
|
-
expect(operationLog[0].headers.Authorization).toBe('[REDACTED]');
|
|
165
|
-
expect(response.data._dryRun).toBe(true);
|
|
166
|
-
});
|
|
167
|
-
|
|
168
|
-
test('should log POST requests with data', async () => {
|
|
169
|
-
const client = createDryRunHttpClient(operationLog);
|
|
170
|
-
|
|
171
|
-
const postData = {
|
|
172
|
-
name: 'John Doe',
|
|
173
|
-
email: 'john@example.com',
|
|
174
|
-
password: 'secret123',
|
|
175
|
-
};
|
|
176
|
-
|
|
177
|
-
await client.post('/users', postData, {
|
|
178
|
-
baseURL: 'https://api.example.com',
|
|
179
|
-
});
|
|
180
|
-
|
|
181
|
-
expect(operationLog).toHaveLength(1);
|
|
182
|
-
expect(operationLog[0].method).toBe('POST');
|
|
183
|
-
expect(operationLog[0].data.name).toBe('John Doe');
|
|
184
|
-
expect(operationLog[0].data.email).toBe('john@example.com');
|
|
185
|
-
expect(operationLog[0].data.password).toBe('[REDACTED]');
|
|
186
|
-
});
|
|
187
|
-
|
|
188
|
-
test('should log PUT requests', async () => {
|
|
189
|
-
const client = createDryRunHttpClient(operationLog);
|
|
190
|
-
|
|
191
|
-
await client.put('/users/123', { status: 'active' }, {
|
|
192
|
-
baseURL: 'https://api.example.com',
|
|
193
|
-
});
|
|
194
|
-
|
|
195
|
-
expect(operationLog).toHaveLength(1);
|
|
196
|
-
expect(operationLog[0].method).toBe('PUT');
|
|
197
|
-
expect(operationLog[0].data.status).toBe('active');
|
|
198
|
-
});
|
|
199
|
-
|
|
200
|
-
test('should log PATCH requests', async () => {
|
|
201
|
-
const client = createDryRunHttpClient(operationLog);
|
|
202
|
-
|
|
203
|
-
await client.patch('/users/123', { name: 'Updated' });
|
|
204
|
-
|
|
205
|
-
expect(operationLog).toHaveLength(1);
|
|
206
|
-
expect(operationLog[0].method).toBe('PATCH');
|
|
207
|
-
});
|
|
208
|
-
|
|
209
|
-
test('should log DELETE requests', async () => {
|
|
210
|
-
const client = createDryRunHttpClient(operationLog);
|
|
211
|
-
|
|
212
|
-
await client.delete('/users/123', {
|
|
213
|
-
baseURL: 'https://api.example.com',
|
|
214
|
-
});
|
|
215
|
-
|
|
216
|
-
expect(operationLog).toHaveLength(1);
|
|
217
|
-
expect(operationLog[0].method).toBe('DELETE');
|
|
218
|
-
});
|
|
219
|
-
|
|
220
|
-
test('should return mock response', async () => {
|
|
221
|
-
const client = createDryRunHttpClient(operationLog);
|
|
222
|
-
|
|
223
|
-
const response = await client.get('/test');
|
|
224
|
-
|
|
225
|
-
expect(response.status).toBe(200);
|
|
226
|
-
expect(response.statusText).toContain('Dry-Run');
|
|
227
|
-
expect(response.data._dryRun).toBe(true);
|
|
228
|
-
expect(response.headers['x-dry-run']).toBe('true');
|
|
229
|
-
});
|
|
230
|
-
|
|
231
|
-
test('should include query params in log', async () => {
|
|
232
|
-
const client = createDryRunHttpClient(operationLog);
|
|
233
|
-
|
|
234
|
-
await client.get('/search', {
|
|
235
|
-
baseURL: 'https://api.example.com',
|
|
236
|
-
params: { q: 'test', limit: 10 },
|
|
237
|
-
});
|
|
238
|
-
|
|
239
|
-
expect(operationLog[0].params).toEqual({ q: 'test', limit: 10 });
|
|
240
|
-
});
|
|
241
|
-
});
|
|
242
|
-
|
|
243
|
-
describe('injectDryRunHttpClient', () => {
|
|
244
|
-
let operationLog;
|
|
245
|
-
let dryRunClient;
|
|
246
|
-
|
|
247
|
-
beforeEach(() => {
|
|
248
|
-
operationLog = [];
|
|
249
|
-
dryRunClient = createDryRunHttpClient(operationLog);
|
|
250
|
-
});
|
|
251
|
-
|
|
252
|
-
test('should inject into primary API module', () => {
|
|
253
|
-
const integrationInstance = {
|
|
254
|
-
primary: {
|
|
255
|
-
api: {
|
|
256
|
-
_httpClient: { get: jest.fn() },
|
|
257
|
-
},
|
|
258
|
-
},
|
|
259
|
-
};
|
|
260
|
-
|
|
261
|
-
injectDryRunHttpClient(integrationInstance, dryRunClient);
|
|
262
|
-
|
|
263
|
-
expect(integrationInstance.primary.api._httpClient).toBe(dryRunClient);
|
|
264
|
-
});
|
|
265
|
-
|
|
266
|
-
test('should inject into target API module', () => {
|
|
267
|
-
const integrationInstance = {
|
|
268
|
-
target: {
|
|
269
|
-
api: {
|
|
270
|
-
_httpClient: { get: jest.fn() },
|
|
271
|
-
},
|
|
272
|
-
},
|
|
273
|
-
};
|
|
274
|
-
|
|
275
|
-
injectDryRunHttpClient(integrationInstance, dryRunClient);
|
|
276
|
-
|
|
277
|
-
expect(integrationInstance.target.api._httpClient).toBe(dryRunClient);
|
|
278
|
-
});
|
|
279
|
-
|
|
280
|
-
test('should inject into both primary and target', () => {
|
|
281
|
-
const integrationInstance = {
|
|
282
|
-
primary: {
|
|
283
|
-
api: { _httpClient: { get: jest.fn() } },
|
|
284
|
-
},
|
|
285
|
-
target: {
|
|
286
|
-
api: { _httpClient: { get: jest.fn() } },
|
|
287
|
-
},
|
|
288
|
-
};
|
|
289
|
-
|
|
290
|
-
injectDryRunHttpClient(integrationInstance, dryRunClient);
|
|
291
|
-
|
|
292
|
-
expect(integrationInstance.primary.api._httpClient).toBe(dryRunClient);
|
|
293
|
-
expect(integrationInstance.target.api._httpClient).toBe(dryRunClient);
|
|
294
|
-
});
|
|
295
|
-
|
|
296
|
-
test('should handle missing api modules gracefully', () => {
|
|
297
|
-
const integrationInstance = {
|
|
298
|
-
primary: {},
|
|
299
|
-
target: null,
|
|
300
|
-
};
|
|
301
|
-
|
|
302
|
-
expect(() => {
|
|
303
|
-
injectDryRunHttpClient(integrationInstance, dryRunClient);
|
|
304
|
-
}).not.toThrow();
|
|
305
|
-
});
|
|
306
|
-
|
|
307
|
-
test('should handle null integration instance', () => {
|
|
308
|
-
expect(() => {
|
|
309
|
-
injectDryRunHttpClient(null, dryRunClient);
|
|
310
|
-
}).not.toThrow();
|
|
311
|
-
});
|
|
312
|
-
});
|
|
313
|
-
});
|