@app-connect/core 1.7.8 → 1.7.10
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/handlers/auth.js +10 -4
- package/handlers/contact.js +42 -9
- package/handlers/log.js +4 -4
- package/index.js +101 -79
- package/lib/oauth.js +0 -2
- package/models/accountDataModel.js +34 -0
- package/package.json +70 -69
- package/releaseNotes.json +24 -0
- package/test/connector/registry.test.js +145 -0
- package/test/handlers/admin.test.js +583 -0
- package/test/handlers/auth.test.js +355 -0
- package/test/handlers/contact.test.js +852 -0
- package/test/handlers/log.test.js +868 -0
- package/test/lib/callLogComposer.test.js +1231 -0
- package/test/lib/debugTracer.test.js +328 -0
- package/test/lib/oauth.test.js +359 -0
- package/test/lib/ringcentral.test.js +473 -0
- package/test/lib/util.test.js +282 -0
- package/test/models/accountDataModel.test.js +98 -0
- package/test/models/dynamo/connectorSchema.test.js +189 -0
- package/test/models/models.test.js +539 -0
- package/test/setup.js +176 -176
|
@@ -0,0 +1,583 @@
|
|
|
1
|
+
// Use in-memory SQLite for isolated model tests
|
|
2
|
+
jest.mock('../../models/sequelize', () => {
|
|
3
|
+
const { Sequelize } = require('sequelize');
|
|
4
|
+
return {
|
|
5
|
+
sequelize: new Sequelize({
|
|
6
|
+
dialect: 'sqlite',
|
|
7
|
+
storage: ':memory:',
|
|
8
|
+
logging: false,
|
|
9
|
+
}),
|
|
10
|
+
};
|
|
11
|
+
});
|
|
12
|
+
|
|
13
|
+
jest.mock('axios');
|
|
14
|
+
jest.mock('../../connector/registry');
|
|
15
|
+
jest.mock('../../lib/oauth');
|
|
16
|
+
jest.mock('../../lib/ringcentral');
|
|
17
|
+
jest.mock('../../models/dynamo/connectorSchema', () => ({
|
|
18
|
+
Connector: {
|
|
19
|
+
getProxyConfig: jest.fn()
|
|
20
|
+
}
|
|
21
|
+
}));
|
|
22
|
+
|
|
23
|
+
const axios = require('axios');
|
|
24
|
+
const adminHandler = require('../../handlers/admin');
|
|
25
|
+
const { AdminConfigModel } = require('../../models/adminConfigModel');
|
|
26
|
+
const { UserModel } = require('../../models/userModel');
|
|
27
|
+
const connectorRegistry = require('../../connector/registry');
|
|
28
|
+
const oauth = require('../../lib/oauth');
|
|
29
|
+
const { RingCentral } = require('../../lib/ringcentral');
|
|
30
|
+
const { Connector } = require('../../models/dynamo/connectorSchema');
|
|
31
|
+
const { sequelize } = require('../../models/sequelize');
|
|
32
|
+
|
|
33
|
+
describe('Admin Handler', () => {
|
|
34
|
+
beforeAll(async () => {
|
|
35
|
+
await AdminConfigModel.sync({ force: true });
|
|
36
|
+
await UserModel.sync({ force: true });
|
|
37
|
+
});
|
|
38
|
+
|
|
39
|
+
afterEach(async () => {
|
|
40
|
+
await AdminConfigModel.destroy({ where: {} });
|
|
41
|
+
await UserModel.destroy({ where: {} });
|
|
42
|
+
jest.clearAllMocks();
|
|
43
|
+
});
|
|
44
|
+
|
|
45
|
+
afterAll(async () => {
|
|
46
|
+
await sequelize.close();
|
|
47
|
+
});
|
|
48
|
+
|
|
49
|
+
describe('validateAdminRole', () => {
|
|
50
|
+
test('should return validated true when user has admin permissions', async () => {
|
|
51
|
+
// Arrange
|
|
52
|
+
axios.get.mockResolvedValue({
|
|
53
|
+
data: {
|
|
54
|
+
permissions: {
|
|
55
|
+
admin: { enabled: true }
|
|
56
|
+
},
|
|
57
|
+
account: { id: 'rc-account-123' },
|
|
58
|
+
id: 'extension-123'
|
|
59
|
+
}
|
|
60
|
+
});
|
|
61
|
+
|
|
62
|
+
// Act
|
|
63
|
+
const result = await adminHandler.validateAdminRole({
|
|
64
|
+
rcAccessToken: 'valid-token'
|
|
65
|
+
});
|
|
66
|
+
|
|
67
|
+
// Assert
|
|
68
|
+
expect(result.isValidated).toBe(true);
|
|
69
|
+
expect(result.rcAccountId).toBe('rc-account-123');
|
|
70
|
+
expect(axios.get).toHaveBeenCalledWith(
|
|
71
|
+
'https://platform.ringcentral.com/restapi/v1.0/account/~/extension/~',
|
|
72
|
+
{ headers: { Authorization: 'Bearer valid-token' } }
|
|
73
|
+
);
|
|
74
|
+
});
|
|
75
|
+
|
|
76
|
+
test('should return validated false when user lacks admin permissions', async () => {
|
|
77
|
+
// Arrange
|
|
78
|
+
axios.get.mockResolvedValue({
|
|
79
|
+
data: {
|
|
80
|
+
permissions: {
|
|
81
|
+
admin: { enabled: false }
|
|
82
|
+
},
|
|
83
|
+
account: { id: 'rc-account-456' },
|
|
84
|
+
id: 'extension-456'
|
|
85
|
+
}
|
|
86
|
+
});
|
|
87
|
+
|
|
88
|
+
// Act
|
|
89
|
+
const result = await adminHandler.validateAdminRole({
|
|
90
|
+
rcAccessToken: 'non-admin-token'
|
|
91
|
+
});
|
|
92
|
+
|
|
93
|
+
// Assert
|
|
94
|
+
expect(result.isValidated).toBe(false);
|
|
95
|
+
expect(result.rcAccountId).toBe('rc-account-456');
|
|
96
|
+
});
|
|
97
|
+
|
|
98
|
+
test('should return validated true for dev pass list extension', async () => {
|
|
99
|
+
// Arrange
|
|
100
|
+
const originalEnv = process.env.ADMIN_EXTENSION_ID_DEV_PASS_LIST;
|
|
101
|
+
process.env.ADMIN_EXTENSION_ID_DEV_PASS_LIST = '999,1000,1001';
|
|
102
|
+
|
|
103
|
+
axios.get.mockResolvedValue({
|
|
104
|
+
data: {
|
|
105
|
+
permissions: {
|
|
106
|
+
admin: { enabled: false }
|
|
107
|
+
},
|
|
108
|
+
account: { id: 'rc-account-dev' },
|
|
109
|
+
id: 1000
|
|
110
|
+
}
|
|
111
|
+
});
|
|
112
|
+
|
|
113
|
+
// Act
|
|
114
|
+
const result = await adminHandler.validateAdminRole({
|
|
115
|
+
rcAccessToken: 'dev-token'
|
|
116
|
+
});
|
|
117
|
+
|
|
118
|
+
// Assert
|
|
119
|
+
expect(result.isValidated).toBe(true);
|
|
120
|
+
|
|
121
|
+
// Cleanup
|
|
122
|
+
process.env.ADMIN_EXTENSION_ID_DEV_PASS_LIST = originalEnv;
|
|
123
|
+
});
|
|
124
|
+
});
|
|
125
|
+
|
|
126
|
+
describe('upsertAdminSettings', () => {
|
|
127
|
+
test('should create new admin config when none exists', async () => {
|
|
128
|
+
// Act
|
|
129
|
+
await adminHandler.upsertAdminSettings({
|
|
130
|
+
hashedRcAccountId: 'hashed-123',
|
|
131
|
+
adminSettings: {
|
|
132
|
+
userSettings: { autoLogCalls: true, autoLogMessages: false }
|
|
133
|
+
}
|
|
134
|
+
});
|
|
135
|
+
|
|
136
|
+
// Assert
|
|
137
|
+
const config = await AdminConfigModel.findByPk('hashed-123');
|
|
138
|
+
expect(config).not.toBeNull();
|
|
139
|
+
expect(config.userSettings).toEqual({ autoLogCalls: true, autoLogMessages: false });
|
|
140
|
+
});
|
|
141
|
+
|
|
142
|
+
test('should update existing admin config', async () => {
|
|
143
|
+
// Arrange
|
|
144
|
+
await AdminConfigModel.create({
|
|
145
|
+
id: 'hashed-existing',
|
|
146
|
+
userSettings: { autoLogCalls: false, autoLogMessages: true }
|
|
147
|
+
});
|
|
148
|
+
|
|
149
|
+
// Act
|
|
150
|
+
await adminHandler.upsertAdminSettings({
|
|
151
|
+
hashedRcAccountId: 'hashed-existing',
|
|
152
|
+
adminSettings: {
|
|
153
|
+
userSettings: { autoLogCalls: true, autoLogMessages: false }
|
|
154
|
+
}
|
|
155
|
+
});
|
|
156
|
+
|
|
157
|
+
// Assert
|
|
158
|
+
const config = await AdminConfigModel.findByPk('hashed-existing');
|
|
159
|
+
expect(config.userSettings).toEqual({ autoLogCalls: true, autoLogMessages: false });
|
|
160
|
+
});
|
|
161
|
+
});
|
|
162
|
+
|
|
163
|
+
describe('getAdminSettings', () => {
|
|
164
|
+
test('should return admin settings when they exist', async () => {
|
|
165
|
+
// Arrange
|
|
166
|
+
await AdminConfigModel.create({
|
|
167
|
+
id: 'hashed-get-test',
|
|
168
|
+
userSettings: { autoLogCalls: true, autoLogMessages: true }
|
|
169
|
+
});
|
|
170
|
+
|
|
171
|
+
// Act
|
|
172
|
+
const result = await adminHandler.getAdminSettings({
|
|
173
|
+
hashedRcAccountId: 'hashed-get-test'
|
|
174
|
+
});
|
|
175
|
+
|
|
176
|
+
// Assert
|
|
177
|
+
expect(result).not.toBeNull();
|
|
178
|
+
expect(result.userSettings).toEqual({ autoLogCalls: true, autoLogMessages: true });
|
|
179
|
+
});
|
|
180
|
+
|
|
181
|
+
test('should return null when settings do not exist', async () => {
|
|
182
|
+
// Act
|
|
183
|
+
const result = await adminHandler.getAdminSettings({
|
|
184
|
+
hashedRcAccountId: 'non-existent'
|
|
185
|
+
});
|
|
186
|
+
|
|
187
|
+
// Assert
|
|
188
|
+
expect(result).toBeNull();
|
|
189
|
+
});
|
|
190
|
+
});
|
|
191
|
+
|
|
192
|
+
describe('updateAdminRcTokens', () => {
|
|
193
|
+
test('should update tokens for existing config', async () => {
|
|
194
|
+
// Arrange
|
|
195
|
+
await AdminConfigModel.create({
|
|
196
|
+
id: 'hashed-token-test',
|
|
197
|
+
adminAccessToken: 'old-access',
|
|
198
|
+
adminRefreshToken: 'old-refresh',
|
|
199
|
+
adminTokenExpiry: new Date('2024-01-01')
|
|
200
|
+
});
|
|
201
|
+
|
|
202
|
+
const newExpiry = new Date('2024-12-31');
|
|
203
|
+
|
|
204
|
+
// Act
|
|
205
|
+
await adminHandler.updateAdminRcTokens({
|
|
206
|
+
hashedRcAccountId: 'hashed-token-test',
|
|
207
|
+
adminAccessToken: 'new-access',
|
|
208
|
+
adminRefreshToken: 'new-refresh',
|
|
209
|
+
adminTokenExpiry: newExpiry
|
|
210
|
+
});
|
|
211
|
+
|
|
212
|
+
// Assert
|
|
213
|
+
const config = await AdminConfigModel.findByPk('hashed-token-test');
|
|
214
|
+
expect(config.adminAccessToken).toBe('new-access');
|
|
215
|
+
expect(config.adminRefreshToken).toBe('new-refresh');
|
|
216
|
+
});
|
|
217
|
+
|
|
218
|
+
test('should create new config with tokens when none exists', async () => {
|
|
219
|
+
// Arrange
|
|
220
|
+
const expiry = new Date('2024-12-31');
|
|
221
|
+
|
|
222
|
+
// Act
|
|
223
|
+
await adminHandler.updateAdminRcTokens({
|
|
224
|
+
hashedRcAccountId: 'hashed-new-token',
|
|
225
|
+
adminAccessToken: 'new-access-token',
|
|
226
|
+
adminRefreshToken: 'new-refresh-token',
|
|
227
|
+
adminTokenExpiry: expiry
|
|
228
|
+
});
|
|
229
|
+
|
|
230
|
+
// Assert
|
|
231
|
+
const config = await AdminConfigModel.findByPk('hashed-new-token');
|
|
232
|
+
expect(config).not.toBeNull();
|
|
233
|
+
expect(config.adminAccessToken).toBe('new-access-token');
|
|
234
|
+
expect(config.adminRefreshToken).toBe('new-refresh-token');
|
|
235
|
+
});
|
|
236
|
+
});
|
|
237
|
+
|
|
238
|
+
describe('getServerLoggingSettings', () => {
|
|
239
|
+
test('should return settings from platform module when available', async () => {
|
|
240
|
+
// Arrange
|
|
241
|
+
const mockUser = { platform: 'testCRM', accessToken: 'token' };
|
|
242
|
+
const mockSettings = { enableAutoLog: true, logLevel: 'debug' };
|
|
243
|
+
|
|
244
|
+
const mockConnector = {
|
|
245
|
+
getServerLoggingSettings: jest.fn().mockResolvedValue(mockSettings)
|
|
246
|
+
};
|
|
247
|
+
connectorRegistry.getConnector.mockReturnValue(mockConnector);
|
|
248
|
+
|
|
249
|
+
// Act
|
|
250
|
+
const result = await adminHandler.getServerLoggingSettings({ user: mockUser });
|
|
251
|
+
|
|
252
|
+
// Assert
|
|
253
|
+
expect(result).toEqual(mockSettings);
|
|
254
|
+
expect(mockConnector.getServerLoggingSettings).toHaveBeenCalledWith({ user: mockUser });
|
|
255
|
+
});
|
|
256
|
+
|
|
257
|
+
test('should return empty object when platform module lacks getServerLoggingSettings', async () => {
|
|
258
|
+
// Arrange
|
|
259
|
+
const mockUser = { platform: 'testCRM', accessToken: 'token' };
|
|
260
|
+
|
|
261
|
+
const mockConnector = {};
|
|
262
|
+
connectorRegistry.getConnector.mockReturnValue(mockConnector);
|
|
263
|
+
|
|
264
|
+
// Act
|
|
265
|
+
const result = await adminHandler.getServerLoggingSettings({ user: mockUser });
|
|
266
|
+
|
|
267
|
+
// Assert
|
|
268
|
+
expect(result).toEqual({});
|
|
269
|
+
});
|
|
270
|
+
});
|
|
271
|
+
|
|
272
|
+
describe('updateServerLoggingSettings', () => {
|
|
273
|
+
test('should update settings via platform module when available', async () => {
|
|
274
|
+
// Arrange
|
|
275
|
+
const mockUser = { platform: 'testCRM', accessToken: 'token' };
|
|
276
|
+
const additionalFieldValues = { field1: 'value1' };
|
|
277
|
+
|
|
278
|
+
const mockConnector = {
|
|
279
|
+
getOauthInfo: jest.fn().mockResolvedValue({
|
|
280
|
+
clientId: 'id',
|
|
281
|
+
clientSecret: 'secret',
|
|
282
|
+
accessTokenUri: 'https://token.url'
|
|
283
|
+
}),
|
|
284
|
+
updateServerLoggingSettings: jest.fn().mockResolvedValue({
|
|
285
|
+
successful: true,
|
|
286
|
+
returnMessage: { message: 'Settings updated' }
|
|
287
|
+
})
|
|
288
|
+
};
|
|
289
|
+
connectorRegistry.getConnector.mockReturnValue(mockConnector);
|
|
290
|
+
oauth.getOAuthApp.mockReturnValue({});
|
|
291
|
+
|
|
292
|
+
// Act
|
|
293
|
+
const result = await adminHandler.updateServerLoggingSettings({
|
|
294
|
+
user: mockUser,
|
|
295
|
+
additionalFieldValues
|
|
296
|
+
});
|
|
297
|
+
|
|
298
|
+
// Assert
|
|
299
|
+
expect(result.successful).toBe(true);
|
|
300
|
+
expect(mockConnector.updateServerLoggingSettings).toHaveBeenCalled();
|
|
301
|
+
});
|
|
302
|
+
|
|
303
|
+
test('should return empty object when platform module lacks updateServerLoggingSettings', async () => {
|
|
304
|
+
// Arrange
|
|
305
|
+
const mockUser = { platform: 'testCRM', accessToken: 'token' };
|
|
306
|
+
|
|
307
|
+
const mockConnector = {
|
|
308
|
+
getOauthInfo: jest.fn().mockResolvedValue({})
|
|
309
|
+
};
|
|
310
|
+
connectorRegistry.getConnector.mockReturnValue(mockConnector);
|
|
311
|
+
oauth.getOAuthApp.mockReturnValue({});
|
|
312
|
+
|
|
313
|
+
// Act
|
|
314
|
+
const result = await adminHandler.updateServerLoggingSettings({
|
|
315
|
+
user: mockUser,
|
|
316
|
+
additionalFieldValues: {}
|
|
317
|
+
});
|
|
318
|
+
|
|
319
|
+
// Assert
|
|
320
|
+
expect(result).toEqual({});
|
|
321
|
+
});
|
|
322
|
+
});
|
|
323
|
+
|
|
324
|
+
describe('getAdminReport', () => {
|
|
325
|
+
test('should return empty stats when RC credentials are not configured', async () => {
|
|
326
|
+
// Arrange
|
|
327
|
+
const originalServer = process.env.RINGCENTRAL_SERVER;
|
|
328
|
+
delete process.env.RINGCENTRAL_SERVER;
|
|
329
|
+
|
|
330
|
+
// Act
|
|
331
|
+
const result = await adminHandler.getAdminReport({
|
|
332
|
+
rcAccountId: 'account-123',
|
|
333
|
+
timezone: 'America/Los_Angeles',
|
|
334
|
+
timeFrom: '2024-01-01',
|
|
335
|
+
timeTo: '2024-01-31',
|
|
336
|
+
groupBy: 'Users'
|
|
337
|
+
});
|
|
338
|
+
|
|
339
|
+
// Assert
|
|
340
|
+
expect(result).toEqual({ callLogStats: {} });
|
|
341
|
+
|
|
342
|
+
// Cleanup
|
|
343
|
+
if (originalServer) {
|
|
344
|
+
process.env.RINGCENTRAL_SERVER = originalServer;
|
|
345
|
+
}
|
|
346
|
+
});
|
|
347
|
+
|
|
348
|
+
test('should handle errors and return empty stats', async () => {
|
|
349
|
+
// Arrange
|
|
350
|
+
const originalServer = process.env.RINGCENTRAL_SERVER;
|
|
351
|
+
const originalClientId = process.env.RINGCENTRAL_CLIENT_ID;
|
|
352
|
+
const originalClientSecret = process.env.RINGCENTRAL_CLIENT_SECRET;
|
|
353
|
+
|
|
354
|
+
process.env.RINGCENTRAL_SERVER = 'https://platform.ringcentral.com';
|
|
355
|
+
process.env.RINGCENTRAL_CLIENT_ID = 'test-client-id';
|
|
356
|
+
process.env.RINGCENTRAL_CLIENT_SECRET = 'test-client-secret';
|
|
357
|
+
|
|
358
|
+
// Mock AdminConfigModel.findByPk to throw error
|
|
359
|
+
jest.spyOn(AdminConfigModel, 'findByPk').mockRejectedValueOnce(new Error('Database error'));
|
|
360
|
+
|
|
361
|
+
// Act
|
|
362
|
+
const result = await adminHandler.getAdminReport({
|
|
363
|
+
rcAccountId: 'account-123',
|
|
364
|
+
timezone: 'America/Los_Angeles',
|
|
365
|
+
timeFrom: '2024-01-01',
|
|
366
|
+
timeTo: '2024-01-31',
|
|
367
|
+
groupBy: 'Users'
|
|
368
|
+
});
|
|
369
|
+
|
|
370
|
+
// Assert
|
|
371
|
+
expect(result).toEqual({ callLogStats: {} });
|
|
372
|
+
|
|
373
|
+
// Cleanup
|
|
374
|
+
process.env.RINGCENTRAL_SERVER = originalServer;
|
|
375
|
+
process.env.RINGCENTRAL_CLIENT_ID = originalClientId;
|
|
376
|
+
process.env.RINGCENTRAL_CLIENT_SECRET = originalClientSecret;
|
|
377
|
+
});
|
|
378
|
+
});
|
|
379
|
+
|
|
380
|
+
describe('getUserReport', () => {
|
|
381
|
+
test('should return empty stats when RC credentials are not configured', async () => {
|
|
382
|
+
// Arrange
|
|
383
|
+
const originalServer = process.env.RINGCENTRAL_SERVER;
|
|
384
|
+
delete process.env.RINGCENTRAL_SERVER;
|
|
385
|
+
|
|
386
|
+
// Act
|
|
387
|
+
const result = await adminHandler.getUserReport({
|
|
388
|
+
rcAccountId: 'account-123',
|
|
389
|
+
rcExtensionId: 'extension-123',
|
|
390
|
+
timezone: 'America/Los_Angeles',
|
|
391
|
+
timeFrom: '2024-01-01',
|
|
392
|
+
timeTo: '2024-01-31'
|
|
393
|
+
});
|
|
394
|
+
|
|
395
|
+
// Assert
|
|
396
|
+
expect(result).toEqual({ callLogStats: {} });
|
|
397
|
+
|
|
398
|
+
// Cleanup
|
|
399
|
+
if (originalServer) {
|
|
400
|
+
process.env.RINGCENTRAL_SERVER = originalServer;
|
|
401
|
+
}
|
|
402
|
+
});
|
|
403
|
+
|
|
404
|
+
test('should handle errors and return null', async () => {
|
|
405
|
+
// Arrange
|
|
406
|
+
const originalServer = process.env.RINGCENTRAL_SERVER;
|
|
407
|
+
const originalClientId = process.env.RINGCENTRAL_CLIENT_ID;
|
|
408
|
+
const originalClientSecret = process.env.RINGCENTRAL_CLIENT_SECRET;
|
|
409
|
+
|
|
410
|
+
process.env.RINGCENTRAL_SERVER = 'https://platform.ringcentral.com';
|
|
411
|
+
process.env.RINGCENTRAL_CLIENT_ID = 'test-client-id';
|
|
412
|
+
process.env.RINGCENTRAL_CLIENT_SECRET = 'test-client-secret';
|
|
413
|
+
|
|
414
|
+
// Mock AdminConfigModel.findByPk to throw error
|
|
415
|
+
jest.spyOn(AdminConfigModel, 'findByPk').mockRejectedValueOnce(new Error('Database error'));
|
|
416
|
+
|
|
417
|
+
// Act
|
|
418
|
+
const result = await adminHandler.getUserReport({
|
|
419
|
+
rcAccountId: 'account-123',
|
|
420
|
+
rcExtensionId: 'extension-123',
|
|
421
|
+
timezone: 'America/Los_Angeles',
|
|
422
|
+
timeFrom: '2024-01-01',
|
|
423
|
+
timeTo: '2024-01-31'
|
|
424
|
+
});
|
|
425
|
+
|
|
426
|
+
// Assert
|
|
427
|
+
expect(result).toBeNull();
|
|
428
|
+
|
|
429
|
+
// Cleanup
|
|
430
|
+
process.env.RINGCENTRAL_SERVER = originalServer;
|
|
431
|
+
process.env.RINGCENTRAL_CLIENT_ID = originalClientId;
|
|
432
|
+
process.env.RINGCENTRAL_CLIENT_SECRET = originalClientSecret;
|
|
433
|
+
});
|
|
434
|
+
});
|
|
435
|
+
|
|
436
|
+
describe('getUserMapping', () => {
|
|
437
|
+
test('should return empty array when platform module lacks getUserList', async () => {
|
|
438
|
+
// Arrange
|
|
439
|
+
await UserModel.create({
|
|
440
|
+
id: 'test-user-id',
|
|
441
|
+
platform: 'testCRM',
|
|
442
|
+
accessToken: 'token',
|
|
443
|
+
platformAdditionalInfo: {}
|
|
444
|
+
});
|
|
445
|
+
|
|
446
|
+
const mockConnector = {};
|
|
447
|
+
connectorRegistry.getConnector.mockReturnValue(mockConnector);
|
|
448
|
+
|
|
449
|
+
// Act
|
|
450
|
+
const result = await adminHandler.getUserMapping({
|
|
451
|
+
user: { id: 'test-user-id', platform: 'testCRM', platformAdditionalInfo: {} },
|
|
452
|
+
hashedRcAccountId: 'hashed-123',
|
|
453
|
+
rcExtensionList: []
|
|
454
|
+
});
|
|
455
|
+
|
|
456
|
+
// Assert
|
|
457
|
+
expect(result).toEqual([]);
|
|
458
|
+
});
|
|
459
|
+
|
|
460
|
+
test('should return empty array when proxy config lacks getUserList operation', async () => {
|
|
461
|
+
// Arrange
|
|
462
|
+
const user = {
|
|
463
|
+
id: 'test-user-id',
|
|
464
|
+
platform: 'testCRM',
|
|
465
|
+
accessToken: 'token',
|
|
466
|
+
platformAdditionalInfo: { proxyId: 'proxy-123' }
|
|
467
|
+
};
|
|
468
|
+
|
|
469
|
+
Connector.getProxyConfig.mockResolvedValue({
|
|
470
|
+
operations: {}
|
|
471
|
+
});
|
|
472
|
+
|
|
473
|
+
const mockConnector = {
|
|
474
|
+
getUserList: jest.fn()
|
|
475
|
+
};
|
|
476
|
+
connectorRegistry.getConnector.mockReturnValue(mockConnector);
|
|
477
|
+
|
|
478
|
+
// Act
|
|
479
|
+
const result = await adminHandler.getUserMapping({
|
|
480
|
+
user,
|
|
481
|
+
hashedRcAccountId: 'hashed-123',
|
|
482
|
+
rcExtensionList: []
|
|
483
|
+
});
|
|
484
|
+
|
|
485
|
+
// Assert
|
|
486
|
+
expect(result).toEqual([]);
|
|
487
|
+
});
|
|
488
|
+
|
|
489
|
+
test('should map CRM users to RC extensions', async () => {
|
|
490
|
+
// Arrange
|
|
491
|
+
await AdminConfigModel.create({
|
|
492
|
+
id: 'hashed-mapping',
|
|
493
|
+
userMappings: []
|
|
494
|
+
});
|
|
495
|
+
|
|
496
|
+
const user = {
|
|
497
|
+
id: 'test-user-id',
|
|
498
|
+
platform: 'testCRM',
|
|
499
|
+
accessToken: 'token',
|
|
500
|
+
platformAdditionalInfo: {}
|
|
501
|
+
};
|
|
502
|
+
|
|
503
|
+
const crmUsers = [
|
|
504
|
+
{ id: 'crm-user-1', name: 'John Doe', email: 'john@example.com' },
|
|
505
|
+
{ id: 'crm-user-2', name: 'Jane Smith', email: 'jane@example.com' }
|
|
506
|
+
];
|
|
507
|
+
|
|
508
|
+
const rcExtensions = [
|
|
509
|
+
{ id: 'ext-1', firstName: 'John', lastName: 'Doe', email: 'john@example.com', extensionNumber: '101' },
|
|
510
|
+
{ id: 'ext-2', firstName: 'Bob', lastName: 'Wilson', email: 'bob@example.com', extensionNumber: '102' }
|
|
511
|
+
];
|
|
512
|
+
|
|
513
|
+
const mockConnector = {
|
|
514
|
+
getAuthType: jest.fn().mockResolvedValue('apiKey'),
|
|
515
|
+
getBasicAuth: jest.fn().mockReturnValue('base64'),
|
|
516
|
+
getUserList: jest.fn().mockResolvedValue(crmUsers)
|
|
517
|
+
};
|
|
518
|
+
connectorRegistry.getConnector.mockReturnValue(mockConnector);
|
|
519
|
+
|
|
520
|
+
// Act
|
|
521
|
+
const result = await adminHandler.getUserMapping({
|
|
522
|
+
user,
|
|
523
|
+
hashedRcAccountId: 'hashed-mapping',
|
|
524
|
+
rcExtensionList: rcExtensions
|
|
525
|
+
});
|
|
526
|
+
|
|
527
|
+
// Assert
|
|
528
|
+
expect(result).toHaveLength(2);
|
|
529
|
+
// John should be matched by email
|
|
530
|
+
const johnMapping = result.find(m => m.crmUser.id === 'crm-user-1');
|
|
531
|
+
expect(johnMapping.rcUser).toHaveLength(1);
|
|
532
|
+
expect(johnMapping.rcUser[0].extensionId).toBe('ext-1');
|
|
533
|
+
|
|
534
|
+
// Jane should not be matched
|
|
535
|
+
const janeMapping = result.find(m => m.crmUser.id === 'crm-user-2');
|
|
536
|
+
expect(janeMapping.rcUser).toHaveLength(0);
|
|
537
|
+
});
|
|
538
|
+
|
|
539
|
+
test('should preserve existing mappings', async () => {
|
|
540
|
+
// Arrange
|
|
541
|
+
await AdminConfigModel.create({
|
|
542
|
+
id: 'hashed-existing-mapping',
|
|
543
|
+
userMappings: [
|
|
544
|
+
{ crmUserId: 'crm-user-1', rcExtensionId: ['ext-existing'] }
|
|
545
|
+
]
|
|
546
|
+
});
|
|
547
|
+
|
|
548
|
+
const user = {
|
|
549
|
+
id: 'test-user-id',
|
|
550
|
+
platform: 'testCRM',
|
|
551
|
+
accessToken: 'token',
|
|
552
|
+
platformAdditionalInfo: {}
|
|
553
|
+
};
|
|
554
|
+
|
|
555
|
+
const crmUsers = [
|
|
556
|
+
{ id: 'crm-user-1', name: 'John Doe', email: 'john@example.com' }
|
|
557
|
+
];
|
|
558
|
+
|
|
559
|
+
const rcExtensions = [
|
|
560
|
+
{ id: 'ext-existing', firstName: 'John', lastName: 'Doe', email: 'john@example.com', extensionNumber: '101' }
|
|
561
|
+
];
|
|
562
|
+
|
|
563
|
+
const mockConnector = {
|
|
564
|
+
getAuthType: jest.fn().mockResolvedValue('apiKey'),
|
|
565
|
+
getBasicAuth: jest.fn().mockReturnValue('base64'),
|
|
566
|
+
getUserList: jest.fn().mockResolvedValue(crmUsers)
|
|
567
|
+
};
|
|
568
|
+
connectorRegistry.getConnector.mockReturnValue(mockConnector);
|
|
569
|
+
|
|
570
|
+
// Act
|
|
571
|
+
const result = await adminHandler.getUserMapping({
|
|
572
|
+
user,
|
|
573
|
+
hashedRcAccountId: 'hashed-existing-mapping',
|
|
574
|
+
rcExtensionList: rcExtensions
|
|
575
|
+
});
|
|
576
|
+
|
|
577
|
+
// Assert
|
|
578
|
+
expect(result).toHaveLength(1);
|
|
579
|
+
expect(result[0].rcUser[0].extensionId).toBe('ext-existing');
|
|
580
|
+
});
|
|
581
|
+
});
|
|
582
|
+
});
|
|
583
|
+
|