@webex/internal-plugin-llm 3.12.0-next.9 → 3.12.0-task-refactor.1

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.
@@ -19,182 +19,47 @@ describe('plugin-llm', () => {
19
19
  },
20
20
  });
21
21
 
22
- webex.internal.feature = {
23
- setFeature: sinon.stub().resolves({value: true}),
24
- getFeature: sinon.stub().resolves(true),
25
- };
26
-
27
22
  llmService = webex.internal.llm;
28
- llmService.webSocketUrl = 'wss://example.com/socket';
23
+ llmService.connect = sinon.stub().callsFake(() => {
24
+ llmService.connected = true;
25
+ });
29
26
  llmService.disconnect = sinon.stub().resolves(true);
30
27
  llmService.request = sinon.stub().resolves({
31
28
  headers: {},
32
29
  body: {
33
30
  binding: 'binding',
34
- webSocketUrl: 'wss://example.com/socket',
31
+ webSocketUrl: 'url',
35
32
  },
36
33
  });
37
- const sockets = new Map();
38
-
39
- llmService.connect = sinon.stub().callsFake((url, sessionId) => {
40
- sockets.set(sessionId, {connected: true});
41
- llmService.getSocket = sinon.stub().callsFake((sid) => sockets.get(sid));
42
- });
43
- llmService.connections.set('llm-default-session',{
44
- webSocketUrl: 'wss://example.com/socket',
45
- })
46
34
  });
47
35
 
48
- afterEach(() => sinon.restore());
49
-
50
36
  describe('#registerAndConnect', () => {
51
37
  it('registers connection', async () => {
52
- llmService.register = sinon.stub().callsFake(async () => {
53
- llmService.binding = 'binding';
54
- llmService.webSocketUrl = 'wss://example.com/socket';
55
- return {
56
- body: {
57
- binding: 'binding',
58
- webSocketUrl: 'wss://example.com/socket',
59
- },
60
- };
61
- });
62
-
63
- assert.equal(llmService.isConnected('llm-default-session'), false);
64
- await llmService.registerAndConnect(locusUrl, datachannelUrl,undefined);
65
- assert.equal(llmService.isConnected('llm-default-session'), true);
66
- });
67
-
68
- it("doesn't register connection for invalid input", async () => {
69
- llmService.register = sinon.stub().callsFake(async () => {
70
- llmService.binding = 'binding';
71
- llmService.webSocketUrl = 'wss://example.com/socket';
72
- return {
73
- body: {
74
- binding: 'binding',
75
- webSocketUrl: 'wss://example.com/socket',
76
- },
77
- };
78
- });
79
-
80
- await llmService.registerAndConnect();
81
- assert.equal(llmService.isConnected(), false);
82
- });
83
-
84
- it('registers connection with token', async () => {
85
- llmService.register = sinon.stub().callsFake(async () => {
86
- llmService.binding = 'binding';
87
- llmService.webSocketUrl = 'wss://example.com/socket';
88
- return {
89
- body: {
90
- binding: 'binding',
91
- webSocketUrl: 'wss://example.com/socket',
92
- },
93
- };
38
+ llmService.register = sinon.stub().resolves({
39
+ body: {
40
+ binding: 'binding',
41
+ webSocketUrl: 'url',
42
+ },
94
43
  });
95
-
96
44
  assert.equal(llmService.isConnected(), false);
97
-
98
- await llmService.registerAndConnect(locusUrl, datachannelUrl,'abc123');
99
-
100
- sinon.assert.calledOnceWithExactly(
101
- llmService.register,
102
- datachannelUrl,
103
- 'abc123',
104
- 'llm-default-session'
105
- );
106
-
45
+ await llmService.registerAndConnect(locusUrl, datachannelUrl);
107
46
  assert.equal(llmService.isConnected(), true);
108
47
  });
109
48
 
110
- it('connects with subscriptionAwareSubchannels when token enabled', async () => {
111
- llmService.isDataChannelTokenEnabled = sinon.stub().returns(true);
112
-
113
- llmService.register = sinon.stub().callsFake(async () => {
114
- llmService.binding = 'binding';
115
- llmService.webSocketUrl = 'wss://example.com/socket';
116
- return {
117
- body: {
118
- binding: 'binding',
119
- webSocketUrl: 'wss://example.com/socket',
120
- },
121
- };
122
- });
123
-
124
- const buildSpy = sinon.spy(LLMService, 'buildUrlWithAwareSubchannels');
125
-
126
- await llmService.registerAndConnect(locusUrl, datachannelUrl,'abc123');
127
-
128
- sinon.assert.calledOnce(buildSpy);
129
- sinon.assert.calledOnce(llmService.connect);
130
-
131
- const calledUrl = llmService.connect.getCall(0).args[0];
132
- assert.include(calledUrl, 'subscriptionAwareSubchannels=');
133
- });
134
-
135
- it('connects without subscriptionAwareSubchannels when token disabled', async () => {
136
- llmService.isDataChannelTokenEnabled = sinon.stub().returns(false);
137
-
138
- llmService.register = sinon.stub().callsFake(async () => {
139
- llmService.binding = 'binding';
140
- llmService.webSocketUrl = 'wss://example.com/socket';
141
- return {
142
- body: {
143
- binding: 'binding',
144
- webSocketUrl: 'wss://example.com/socket',
145
- },
146
- };
49
+ it("doesn't registers connection for invalid input", async () => {
50
+ llmService.register = sinon.stub().resolves({
51
+ body: {
52
+ binding: 'binding',
53
+ webSocketUrl: 'url',
54
+ },
147
55
  });
148
-
149
- const buildSpy = sinon.spy(LLMService, 'buildUrlWithAwareSubchannels');
150
-
151
- await llmService.registerAndConnect(locusUrl, datachannelUrl);
152
-
153
- sinon.assert.notCalled(buildSpy);
154
- sinon.assert.calledOnce(llmService.connect);
155
-
156
- const calledUrl = llmService.connect.getCall(0).args[0];
157
- assert.equal(calledUrl, llmService.webSocketUrl);
158
- });
159
-
160
- it('connects without subscriptionAwareSubchannels when token enabled BUT token missing', async () => {
161
- llmService.isDataChannelTokenEnabled = sinon.stub().resolves(true);
162
-
163
- const buildSpy = sinon.spy(LLMService, 'buildUrlWithAwareSubchannels');
164
-
165
- await llmService.registerAndConnect(locusUrl, datachannelUrl, undefined);
166
-
167
- sinon.assert.calledOnce(buildSpy);
168
- sinon.assert.calledOnce(llmService.connect);
169
-
170
- const calledUrl = llmService.connect.getCall(0).args[0];
171
- assert.include(calledUrl, 'subscriptionAwareSubchannels=');
172
-
173
- buildSpy.restore();
56
+ await llmService.registerAndConnect();
57
+ assert.equal(llmService.isConnected(), false);
174
58
  });
175
59
  });
176
60
 
177
61
  describe('#register', () => {
178
- beforeEach(() => {
179
- llmService.isDataChannelTokenEnabled = sinon.stub();
180
- });
181
-
182
- it('registers connection with token header', async () => {
183
- llmService.isDataChannelTokenEnabled.resolves(true);
184
- await llmService.register(datachannelUrl, 'abc123');
185
-
186
- sinon.assert.calledOnceWithExactly(
187
- llmService.request,
188
- sinon.match({
189
- method: 'POST',
190
- url: `${datachannelUrl}`,
191
- body: {deviceUrl: webex.internal.device.url},
192
- headers: {'Data-Channel-Auth-Token': 'abc123'},
193
- })
194
- );
195
- });
196
-
197
- it('registers connection without token header when none provided', async () => {
62
+ it('registers connection', async () => {
198
63
  await llmService.register(datachannelUrl);
199
64
 
200
65
  sinon.assert.calledOnceWithExactly(
@@ -203,40 +68,21 @@ describe('plugin-llm', () => {
203
68
  method: 'POST',
204
69
  url: `${datachannelUrl}`,
205
70
  body: {deviceUrl: webex.internal.device.url},
206
- headers: {},
207
71
  })
208
72
  );
209
- });
210
73
 
211
- it('registers connection without token header when toggle disabled', async () => {
212
- llmService.isDataChannelTokenEnabled.resolves(false);
213
-
214
- await llmService.register(datachannelUrl,'abc123');
215
- sinon.assert.calledOnceWithExactly(
216
- llmService.request,
217
- sinon.match({
218
- method: 'POST',
219
- url: `${datachannelUrl}`,
220
- body: {deviceUrl: webex.internal.device.url},
221
- headers: {},
222
- })
223
- );
74
+ assert.equal(llmService.getBinding(), 'binding');
224
75
  });
225
76
  });
226
77
 
227
78
  describe('#getLocusUrl', () => {
228
79
  it('gets LocusUrl', async () => {
229
- llmService.register = sinon.stub().callsFake(async () => {
230
- llmService.binding = 'binding';
231
- llmService.webSocketUrl = 'wss://example.com/socket';
232
- return {
233
- body: {
234
- binding: 'binding',
235
- webSocketUrl: 'wss://example.com/socket',
236
- },
237
- };
80
+ llmService.register = sinon.stub().resolves({
81
+ body: {
82
+ binding: 'binding',
83
+ webSocketUrl: 'url',
84
+ },
238
85
  });
239
-
240
86
  await llmService.registerAndConnect(locusUrl, datachannelUrl);
241
87
  assert.equal(llmService.getLocusUrl(), locusUrl);
242
88
  });
@@ -244,15 +90,11 @@ describe('plugin-llm', () => {
244
90
 
245
91
  describe('#getDatachannelUrl', () => {
246
92
  it('gets dataChannel Url', async () => {
247
- llmService.register = sinon.stub().callsFake(async () => {
248
- llmService.binding = 'binding';
249
- llmService.webSocketUrl = 'wss://example.com/socket';
250
- return {
251
- body: {
252
- binding: 'binding',
253
- webSocketUrl: 'wss://example.com/socket',
254
- },
255
- };
93
+ llmService.register = sinon.stub().resolves({
94
+ body: {
95
+ binding: 'binding',
96
+ webSocketUrl: 'url',
97
+ },
256
98
  });
257
99
  await llmService.registerAndConnect(locusUrl, datachannelUrl);
258
100
  assert.equal(llmService.getDatachannelUrl(), datachannelUrl);
@@ -270,181 +112,42 @@ describe('plugin-llm', () => {
270
112
  });
271
113
  });
272
114
 
273
- describe('#disconnectLLM', () => {
115
+ describe('disconnectLLM', () => {
274
116
  let instance;
275
117
 
276
118
  beforeEach(() => {
277
119
  instance = {
278
120
  disconnect: jest.fn(() => Promise.resolve()),
279
- connections: new Map([
280
- ['llm-default-session', { foo: 'bar' }],
281
- ]),
282
- datachannelTokens: {
283
- 'llm-default-session': 'session-token',
284
- },
285
-
286
- disconnectLLM: function (options, sessionId = 'llm-default-session') {
287
- return this.disconnect(options, sessionId).then(() => {
288
- this.connections.delete(sessionId);
289
- this.datachannelTokens[sessionId] = undefined;
121
+ locusUrl: 'someUrl',
122
+ datachannelUrl: 'someUrl',
123
+ binding: {},
124
+ webSocketUrl: 'someUrl',
125
+ disconnectLLM: function (options) {
126
+ return this.disconnect(options).then(() => {
127
+ this.locusUrl = undefined;
128
+ this.datachannelUrl = undefined;
129
+ this.binding = undefined;
130
+ this.webSocketUrl = undefined;
290
131
  });
291
- },
132
+ }
292
133
  };
293
134
  });
294
135
 
295
- it('calls disconnect and clears session connection + token', async () => {
296
- await instance.disconnectLLM({ code: 3000, reason: 'bye' });
297
-
298
- expect(instance.disconnect).toHaveBeenCalledWith(
299
- { code: 3000, reason: 'bye' },
300
- 'llm-default-session'
301
- );
302
-
303
- expect(instance.connections.has('llm-default-session')).toBe(false);
304
-
305
- expect(instance.datachannelTokens['llm-default-session']).toBeUndefined();
306
- });
307
-
308
- it('propagates disconnect errors', async () => {
309
- instance.disconnect.mockRejectedValue(new Error('disconnect failed'));
136
+ it('should call disconnect and clear relevant properties', async () => {
137
+ await instance.disconnectLLM({});
310
138
 
311
- await expect(
312
- instance.disconnectLLM({ code: 3000, reason: 'bye' })
313
- ).rejects.toThrow('disconnect failed');
139
+ expect(instance.disconnect).toHaveBeenCalledWith({});
140
+ expect(instance.locusUrl).toBeUndefined();
141
+ expect(instance.datachannelUrl).toBeUndefined();
142
+ expect(instance.binding).toBeUndefined();
143
+ expect(instance.webSocketUrl).toBeUndefined();
314
144
  });
315
- });
316
-
317
- describe('#setRefreshHandler', () => {
318
- it('stores the provided handler', () => {
319
- const handler = sinon.stub().resolves({ body: { datachannelToken: 'newToken' } });
320
- llmService.setRefreshHandler(handler);
321
-
322
- // @ts-ignore
323
- assert.equal(llmService.refreshHandler, handler);
324
- });
325
- });
326
145
 
327
- describe('#isDataChannelTokenEnabled', () => {
328
- it('works correctly', async () => {
329
- webex.internal.feature.getFeature.returns(true);
330
-
331
- const result = await llmService.isDataChannelTokenEnabled();
332
-
333
- sinon.assert.calledOnceWithExactly(
334
- webex.internal.feature.getFeature,
335
- 'developer',
336
- 'data-channel-with-jwt-token'
337
- );
146
+ it('should handle errors from disconnect gracefully', async () => {
147
+ instance.disconnect.mockRejectedValue(new Error('Disconnect failed'));
338
148
 
339
- assert.equal(result, true);
149
+ await expect(instance.disconnectLLM({})).rejects.toThrow('Disconnect failed');
340
150
  });
341
151
  });
342
-
343
- describe('#refreshDataChannelToken', () => {
344
- it('returns null and logs warn if no handler is set', async () => {
345
- const warnSpy = llmService.logger.warn
346
-
347
- const result = await llmService.refreshDataChannelToken();
348
-
349
- assert.equal(result, null);
350
-
351
- sinon.assert.calledOnce(warnSpy);
352
- sinon.assert.calledWithMatch(
353
- warnSpy,
354
- sinon.match('LLM refreshHandler is not set')
355
- );
356
- });
357
-
358
- it('returns token when handler resolves', async () => {
359
- const mockToken = { body: { datachannelToken: 'newToken', isPracticeSession: false } };
360
- const handler = sinon.stub().resolves(mockToken);
361
-
362
- llmService.setRefreshHandler(handler);
363
-
364
- const token = await llmService.refreshDataChannelToken();
365
-
366
- assert.equal(token, mockToken);
367
- sinon.assert.calledOnce(handler);
368
- });
369
-
370
- it('logs warn and returns null when handler rejects', async () => {
371
- const handler = sinon.stub().rejects(new Error('throw error'));
372
- llmService.setRefreshHandler(handler);
373
-
374
- const warnSpy = llmService.logger.warn
375
-
376
- const result = await llmService.refreshDataChannelToken();
377
-
378
- assert.equal(result, null);
379
-
380
- sinon.assert.calledOnce(warnSpy);
381
- sinon.assert.calledWithMatch(
382
- warnSpy,
383
- sinon.match('DataChannel token refresh failed'),
384
- );
385
- });
386
- });
387
-
388
- describe('#getDatachannelToken / #setDatachannelToken', () => {
389
- it('sets and gets datachannel token', () => {
390
- llmService.setDatachannelToken('abc123','llm-default-session');
391
- assert.equal(llmService.getDatachannelToken('llm-default-session'), 'abc123');
392
- llmService.setDatachannelToken('123abc','llm-practice-session');
393
- assert.equal(llmService.getDatachannelToken('llm-practice-session'), '123abc');
394
- });
395
- });
396
-
397
- describe('multi-connection logic', () => {
398
- const locusUrl2 = 'locusUrl2';
399
- const datachannelUrl2 = 'datachannelUrl2';
400
-
401
- it('tracks multiple sessions independently', async () => {
402
- await llmService.registerAndConnect(locusUrl, datachannelUrl, undefined, 's1');
403
- await llmService.registerAndConnect(locusUrl2, datachannelUrl2, undefined, 's2');
404
-
405
- assert.equal(llmService.isConnected('s1'), true);
406
- assert.equal(llmService.isConnected('s2'), true);
407
- assert.equal(llmService.getLocusUrl('s1'), locusUrl);
408
- assert.equal(llmService.getLocusUrl('s2'), locusUrl2);
409
- assert.equal(llmService.getDatachannelUrl('s1'), datachannelUrl);
410
- assert.equal(llmService.getDatachannelUrl('s2'), datachannelUrl2);
411
-
412
- const all = llmService.getAllConnections();
413
- assert.equal(all.has('s1'), true);
414
- assert.equal(all.has('s2'), true);
415
- });
416
-
417
- it('disconnectLLM clears only the targeted session', async () => {
418
- llmService.disconnect = sinon.stub().resolves(true);
419
-
420
- await llmService.registerAndConnect(locusUrl, datachannelUrl, undefined, 's1');
421
- await llmService.registerAndConnect(locusUrl2, datachannelUrl2, undefined, 's2');
422
-
423
- const options = {code: 1000, reason: 'test'};
424
- await llmService.disconnectLLM(options, 's1');
425
-
426
- sinon.assert.calledOnceWithExactly(llmService.disconnect, options, 's1');
427
-
428
- const all = llmService.getAllConnections();
429
- assert.equal(all.has('s1'), false);
430
- assert.equal(all.has('s2'), true);
431
-
432
- assert.equal(llmService.datachannelTokens['s1'], undefined);
433
- });
434
-
435
- it('disconnectAllLLM clears all sessions', async () => {
436
- llmService.disconnectAll = sinon.stub().resolves(true);
437
- sinon.spy(llmService, 'resetDatachannelTokens');
438
-
439
- await llmService.registerAndConnect(locusUrl, datachannelUrl, undefined, 's1');
440
- await llmService.registerAndConnect(locusUrl2, datachannelUrl2, undefined, 's2');
441
-
442
- await llmService.disconnectAllLLM({code: 1000, reason: 'all'});
443
-
444
- sinon.assert.calledOnce(llmService.disconnectAll);
445
- assert.equal(llmService.getAllConnections().size, 0);
446
- });
447
- });
448
-
449
152
  });
450
153
  });