@friggframework/admin-scripts 2.0.0--canary.517.300ded3.0 → 2.0.0--canary.522.cbd3d5a.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 +8 -6
- package/src/application/__tests__/admin-frigg-commands.test.js +18 -18
- 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__/script-runner.test.js +14 -144
- package/src/application/admin-frigg-commands.js +7 -7
- package/src/application/admin-script-base.js +4 -2
- package/src/application/dry-run-http-interceptor.js +296 -0
- package/src/application/dry-run-repository-wrapper.js +261 -0
- package/src/application/script-runner.js +127 -121
- package/src/infrastructure/__tests__/admin-auth-middleware.test.js +95 -32
- package/src/infrastructure/__tests__/admin-script-router.test.js +25 -24
- package/src/infrastructure/admin-auth-middleware.js +43 -5
- package/src/infrastructure/admin-script-router.js +16 -14
- package/src/infrastructure/script-executor-handler.js +2 -2
package/index.js
CHANGED
|
@@ -12,7 +12,7 @@ const { AdminFriggCommands, createAdminFriggCommands } = require('./src/applicat
|
|
|
12
12
|
const { ScriptRunner, createScriptRunner } = require('./src/application/script-runner');
|
|
13
13
|
|
|
14
14
|
// Infrastructure
|
|
15
|
-
const {
|
|
15
|
+
const { adminAuthMiddleware } = require('./src/infrastructure/admin-auth-middleware');
|
|
16
16
|
const { router, app, handler: routerHandler } = require('./src/infrastructure/admin-script-router');
|
|
17
17
|
const { handler: executorHandler } = require('./src/infrastructure/script-executor-handler');
|
|
18
18
|
|
|
@@ -45,7 +45,7 @@ module.exports = {
|
|
|
45
45
|
createScriptRunner,
|
|
46
46
|
|
|
47
47
|
// Infrastructure layer
|
|
48
|
-
|
|
48
|
+
adminAuthMiddleware,
|
|
49
49
|
router,
|
|
50
50
|
app,
|
|
51
51
|
routerHandler,
|
package/package.json
CHANGED
|
@@ -1,21 +1,23 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@friggframework/admin-scripts",
|
|
3
3
|
"prettier": "@friggframework/prettier-config",
|
|
4
|
-
"version": "2.0.0--canary.
|
|
4
|
+
"version": "2.0.0--canary.522.cbd3d5a.0",
|
|
5
5
|
"description": "Admin Script Runner for Frigg - Execute maintenance and operational scripts in hosted environments",
|
|
6
6
|
"dependencies": {
|
|
7
7
|
"@aws-sdk/client-scheduler": "^3.588.0",
|
|
8
|
-
"@friggframework/core": "2.0.0--canary.
|
|
8
|
+
"@friggframework/core": "2.0.0--canary.522.cbd3d5a.0",
|
|
9
9
|
"bcryptjs": "^2.4.3",
|
|
10
10
|
"express": "^4.18.2",
|
|
11
11
|
"lodash": "4.17.21",
|
|
12
|
+
"mongoose": "6.11.6",
|
|
12
13
|
"serverless-http": "^3.2.0",
|
|
13
14
|
"uuid": "^9.0.1"
|
|
14
15
|
},
|
|
15
16
|
"devDependencies": {
|
|
16
|
-
"@friggframework/eslint-config": "2.0.0--canary.
|
|
17
|
-
"@friggframework/prettier-config": "2.0.0--canary.
|
|
18
|
-
"@friggframework/test": "2.0.0--canary.
|
|
17
|
+
"@friggframework/eslint-config": "2.0.0--canary.522.cbd3d5a.0",
|
|
18
|
+
"@friggframework/prettier-config": "2.0.0--canary.522.cbd3d5a.0",
|
|
19
|
+
"@friggframework/test": "2.0.0--canary.522.cbd3d5a.0",
|
|
20
|
+
"chai": "^4.3.6",
|
|
19
21
|
"eslint": "^8.22.0",
|
|
20
22
|
"jest": "^29.7.0",
|
|
21
23
|
"prettier": "^2.7.1",
|
|
@@ -47,5 +49,5 @@
|
|
|
47
49
|
"maintenance",
|
|
48
50
|
"operations"
|
|
49
51
|
],
|
|
50
|
-
"gitHead": "
|
|
52
|
+
"gitHead": "cbd3d5a81315519842f308fc0bd786a3f8786b79"
|
|
51
53
|
}
|
|
@@ -5,7 +5,7 @@ jest.mock('@friggframework/core/integrations/repositories/integration-repository
|
|
|
5
5
|
jest.mock('@friggframework/core/user/repositories/user-repository-factory');
|
|
6
6
|
jest.mock('@friggframework/core/modules/repositories/module-repository-factory');
|
|
7
7
|
jest.mock('@friggframework/core/credential/repositories/credential-repository-factory');
|
|
8
|
-
jest.mock('@friggframework/core/admin-scripts/repositories/
|
|
8
|
+
jest.mock('@friggframework/core/admin-scripts/repositories/script-execution-repository-factory');
|
|
9
9
|
jest.mock('@friggframework/core/queues');
|
|
10
10
|
|
|
11
11
|
describe('AdminFriggCommands', () => {
|
|
@@ -13,7 +13,7 @@ describe('AdminFriggCommands', () => {
|
|
|
13
13
|
let mockUserRepo;
|
|
14
14
|
let mockModuleRepo;
|
|
15
15
|
let mockCredentialRepo;
|
|
16
|
-
let
|
|
16
|
+
let mockScriptExecutionRepo;
|
|
17
17
|
let mockQueuerUtil;
|
|
18
18
|
|
|
19
19
|
beforeEach(() => {
|
|
@@ -46,8 +46,8 @@ describe('AdminFriggCommands', () => {
|
|
|
46
46
|
updateCredential: jest.fn(),
|
|
47
47
|
};
|
|
48
48
|
|
|
49
|
-
|
|
50
|
-
|
|
49
|
+
mockScriptExecutionRepo = {
|
|
50
|
+
appendExecutionLog: jest.fn().mockResolvedValue(undefined),
|
|
51
51
|
};
|
|
52
52
|
|
|
53
53
|
mockQueuerUtil = {
|
|
@@ -60,14 +60,14 @@ describe('AdminFriggCommands', () => {
|
|
|
60
60
|
const { createUserRepository } = require('@friggframework/core/user/repositories/user-repository-factory');
|
|
61
61
|
const { createModuleRepository } = require('@friggframework/core/modules/repositories/module-repository-factory');
|
|
62
62
|
const { createCredentialRepository } = require('@friggframework/core/credential/repositories/credential-repository-factory');
|
|
63
|
-
const {
|
|
63
|
+
const { createScriptExecutionRepository } = require('@friggframework/core/admin-scripts/repositories/script-execution-repository-factory');
|
|
64
64
|
const { QueuerUtil } = require('@friggframework/core/queues');
|
|
65
65
|
|
|
66
66
|
createIntegrationRepository.mockReturnValue(mockIntegrationRepo);
|
|
67
67
|
createUserRepository.mockReturnValue(mockUserRepo);
|
|
68
68
|
createModuleRepository.mockReturnValue(mockModuleRepo);
|
|
69
69
|
createCredentialRepository.mockReturnValue(mockCredentialRepo);
|
|
70
|
-
|
|
70
|
+
createScriptExecutionRepository.mockReturnValue(mockScriptExecutionRepo);
|
|
71
71
|
|
|
72
72
|
// Mock QueuerUtil methods
|
|
73
73
|
QueuerUtil.send = mockQueuerUtil.send;
|
|
@@ -158,16 +158,16 @@ describe('AdminFriggCommands', () => {
|
|
|
158
158
|
expect(repo).toBe(mockCredentialRepo);
|
|
159
159
|
});
|
|
160
160
|
|
|
161
|
-
it('creates
|
|
161
|
+
it('creates scriptExecutionRepository on first access', () => {
|
|
162
162
|
const commands = new AdminFriggCommands();
|
|
163
|
-
const {
|
|
163
|
+
const { createScriptExecutionRepository } = require('@friggframework/core/admin-scripts/repositories/script-execution-repository-factory');
|
|
164
164
|
|
|
165
|
-
expect(
|
|
165
|
+
expect(createScriptExecutionRepository).not.toHaveBeenCalled();
|
|
166
166
|
|
|
167
|
-
const repo = commands.
|
|
167
|
+
const repo = commands.scriptExecutionRepository;
|
|
168
168
|
|
|
169
|
-
expect(
|
|
170
|
-
expect(repo).toBe(
|
|
169
|
+
expect(createScriptExecutionRepository).toHaveBeenCalledTimes(1);
|
|
170
|
+
expect(repo).toBe(mockScriptExecutionRepo);
|
|
171
171
|
});
|
|
172
172
|
});
|
|
173
173
|
|
|
@@ -551,15 +551,15 @@ describe('AdminFriggCommands', () => {
|
|
|
551
551
|
it('log() persists if executionId set', async () => {
|
|
552
552
|
const commands = new AdminFriggCommands({ executionId: 'exec_123' });
|
|
553
553
|
// Force repository creation
|
|
554
|
-
commands.
|
|
554
|
+
commands.scriptExecutionRepository;
|
|
555
555
|
|
|
556
556
|
commands.log('warn', 'Warning message', { detail: 'xyz' });
|
|
557
557
|
|
|
558
558
|
// Give async operation a chance to execute
|
|
559
559
|
await new Promise(resolve => setImmediate(resolve));
|
|
560
560
|
|
|
561
|
-
expect(
|
|
562
|
-
const callArgs =
|
|
561
|
+
expect(mockScriptExecutionRepo.appendExecutionLog).toHaveBeenCalled();
|
|
562
|
+
const callArgs = mockScriptExecutionRepo.appendExecutionLog.mock.calls[0];
|
|
563
563
|
expect(callArgs[0]).toBe('exec_123');
|
|
564
564
|
expect(callArgs[1].level).toBe('warn');
|
|
565
565
|
expect(callArgs[1].message).toBe('Warning message');
|
|
@@ -572,14 +572,14 @@ describe('AdminFriggCommands', () => {
|
|
|
572
572
|
|
|
573
573
|
await new Promise(resolve => setImmediate(resolve));
|
|
574
574
|
|
|
575
|
-
expect(
|
|
575
|
+
expect(mockScriptExecutionRepo.appendExecutionLog).not.toHaveBeenCalled();
|
|
576
576
|
});
|
|
577
577
|
|
|
578
578
|
it('log() handles persistence failure gracefully', async () => {
|
|
579
579
|
const commands = new AdminFriggCommands({ executionId: 'exec_123' });
|
|
580
580
|
// Force repository creation
|
|
581
|
-
commands.
|
|
582
|
-
|
|
581
|
+
commands.scriptExecutionRepository;
|
|
582
|
+
mockScriptExecutionRepo.appendExecutionLog.mockRejectedValue(new Error('DB Error'));
|
|
583
583
|
|
|
584
584
|
// Should not throw
|
|
585
585
|
expect(() => commands.log('error', 'Test error')).not.toThrow();
|
|
@@ -0,0 +1,313 @@
|
|
|
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
|
+
});
|