@webex/plugin-meetings 3.12.0-next.63 → 3.12.0-next.65
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/dist/aiEnableRequest/index.js +1 -1
- package/dist/breakouts/breakout.js +1 -1
- package/dist/breakouts/index.js +1 -1
- package/dist/hashTree/hashTreeParser.js +15 -11
- package/dist/hashTree/hashTreeParser.js.map +1 -1
- package/dist/interpretation/index.js +1 -1
- package/dist/interpretation/siLanguage.js +1 -1
- package/dist/types/hashTree/hashTreeParser.d.ts +3 -1
- package/dist/webinar/index.js +33 -11
- package/dist/webinar/index.js.map +1 -1
- package/package.json +1 -1
- package/src/hashTree/hashTreeParser.ts +18 -13
- package/src/webinar/index.ts +32 -19
- package/test/unit/spec/hashTree/hashTreeParser.ts +128 -24
- package/test/unit/spec/webinar/index.ts +61 -8
|
@@ -117,6 +117,7 @@ function createDataSet(name: string, leafCount: number, version = 1) {
|
|
|
117
117
|
name,
|
|
118
118
|
idleMs: 1000,
|
|
119
119
|
backoff: {maxMs: 1000, exponent: 2},
|
|
120
|
+
heartbeatIntervalMs: 5000,
|
|
120
121
|
};
|
|
121
122
|
}
|
|
122
123
|
|
|
@@ -3042,7 +3043,6 @@ describe('HashTreeParser', () => {
|
|
|
3042
3043
|
],
|
|
3043
3044
|
visibleDataSetsUrl,
|
|
3044
3045
|
locusUrl,
|
|
3045
|
-
heartbeatIntervalMs,
|
|
3046
3046
|
};
|
|
3047
3047
|
|
|
3048
3048
|
parser.handleMessage(heartbeatMessage, 'initial heartbeat');
|
|
@@ -3112,7 +3112,6 @@ describe('HashTreeParser', () => {
|
|
|
3112
3112
|
],
|
|
3113
3113
|
visibleDataSetsUrl,
|
|
3114
3114
|
locusUrl,
|
|
3115
|
-
heartbeatIntervalMs,
|
|
3116
3115
|
};
|
|
3117
3116
|
|
|
3118
3117
|
parser.handleMessage(heartbeatMessage, 'self heartbeat');
|
|
@@ -3148,7 +3147,6 @@ describe('HashTreeParser', () => {
|
|
|
3148
3147
|
|
|
3149
3148
|
it('sets watchdog timers for each data set in the message', async () => {
|
|
3150
3149
|
const parser = createHashTreeParser();
|
|
3151
|
-
const heartbeatIntervalMs = 5000;
|
|
3152
3150
|
|
|
3153
3151
|
// Send heartbeat with multiple datasets
|
|
3154
3152
|
const heartbeatMessage = {
|
|
@@ -3165,7 +3163,6 @@ describe('HashTreeParser', () => {
|
|
|
3165
3163
|
],
|
|
3166
3164
|
visibleDataSetsUrl,
|
|
3167
3165
|
locusUrl,
|
|
3168
|
-
heartbeatIntervalMs,
|
|
3169
3166
|
};
|
|
3170
3167
|
|
|
3171
3168
|
parser.handleMessage(heartbeatMessage, 'multi-dataset heartbeat');
|
|
@@ -3179,7 +3176,6 @@ describe('HashTreeParser', () => {
|
|
|
3179
3176
|
|
|
3180
3177
|
it('resets the watchdog timer for a specific data set when a new heartbeat for it is received', async () => {
|
|
3181
3178
|
const parser = createHashTreeParser();
|
|
3182
|
-
const heartbeatIntervalMs = 5000;
|
|
3183
3179
|
|
|
3184
3180
|
// Send first heartbeat for 'main'
|
|
3185
3181
|
const heartbeat1 = {
|
|
@@ -3191,7 +3187,6 @@ describe('HashTreeParser', () => {
|
|
|
3191
3187
|
],
|
|
3192
3188
|
visibleDataSetsUrl,
|
|
3193
3189
|
locusUrl,
|
|
3194
|
-
heartbeatIntervalMs,
|
|
3195
3190
|
};
|
|
3196
3191
|
|
|
3197
3192
|
parser.handleMessage(heartbeat1, 'first heartbeat');
|
|
@@ -3212,7 +3207,6 @@ describe('HashTreeParser', () => {
|
|
|
3212
3207
|
],
|
|
3213
3208
|
visibleDataSetsUrl,
|
|
3214
3209
|
locusUrl,
|
|
3215
|
-
heartbeatIntervalMs,
|
|
3216
3210
|
};
|
|
3217
3211
|
|
|
3218
3212
|
parser.handleMessage(heartbeat2, 'second heartbeat');
|
|
@@ -3231,7 +3225,6 @@ describe('HashTreeParser', () => {
|
|
|
3231
3225
|
|
|
3232
3226
|
it('resets the watchdog timer when a normal message (with locusStateElements) is received', async () => {
|
|
3233
3227
|
const parser = createHashTreeParser();
|
|
3234
|
-
const heartbeatIntervalMs = 5000;
|
|
3235
3228
|
|
|
3236
3229
|
// Send initial heartbeat to start the watchdog for 'main'
|
|
3237
3230
|
const heartbeat = {
|
|
@@ -3243,7 +3236,6 @@ describe('HashTreeParser', () => {
|
|
|
3243
3236
|
],
|
|
3244
3237
|
visibleDataSetsUrl,
|
|
3245
3238
|
locusUrl,
|
|
3246
|
-
heartbeatIntervalMs,
|
|
3247
3239
|
};
|
|
3248
3240
|
|
|
3249
3241
|
parser.handleMessage(heartbeat, 'initial heartbeat');
|
|
@@ -3271,7 +3263,6 @@ describe('HashTreeParser', () => {
|
|
|
3271
3263
|
data: {someData: 'value'},
|
|
3272
3264
|
},
|
|
3273
3265
|
],
|
|
3274
|
-
heartbeatIntervalMs,
|
|
3275
3266
|
};
|
|
3276
3267
|
|
|
3277
3268
|
parser.handleMessage(normalMessage, 'normal message');
|
|
@@ -3285,12 +3276,17 @@ describe('HashTreeParser', () => {
|
|
|
3285
3276
|
const parser = createHashTreeParser();
|
|
3286
3277
|
|
|
3287
3278
|
// Send a heartbeat message without heartbeatIntervalMs
|
|
3288
|
-
const heartbeatMessage =
|
|
3289
|
-
|
|
3290
|
-
|
|
3291
|
-
|
|
3292
|
-
|
|
3293
|
-
|
|
3279
|
+
const heartbeatMessage = {
|
|
3280
|
+
dataSets: [
|
|
3281
|
+
{
|
|
3282
|
+
...createDataSet('main', 16, 1100),
|
|
3283
|
+
root: parser.dataSets.main.hashTree.getRootHash(),
|
|
3284
|
+
heartbeatIntervalMs: undefined,
|
|
3285
|
+
},
|
|
3286
|
+
],
|
|
3287
|
+
visibleDataSetsUrl,
|
|
3288
|
+
locusUrl,
|
|
3289
|
+
};
|
|
3294
3290
|
|
|
3295
3291
|
parser.handleMessage(heartbeatMessage, 'heartbeat without interval');
|
|
3296
3292
|
|
|
@@ -3299,7 +3295,6 @@ describe('HashTreeParser', () => {
|
|
|
3299
3295
|
|
|
3300
3296
|
it('stops all watchdog timers when meeting ends via sentinel message', async () => {
|
|
3301
3297
|
const parser = createHashTreeParser();
|
|
3302
|
-
const heartbeatIntervalMs = 5000;
|
|
3303
3298
|
|
|
3304
3299
|
// Send heartbeat for multiple datasets
|
|
3305
3300
|
const heartbeat = {
|
|
@@ -3316,7 +3311,6 @@ describe('HashTreeParser', () => {
|
|
|
3316
3311
|
],
|
|
3317
3312
|
visibleDataSetsUrl,
|
|
3318
3313
|
locusUrl,
|
|
3319
|
-
heartbeatIntervalMs,
|
|
3320
3314
|
};
|
|
3321
3315
|
|
|
3322
3316
|
parser.handleMessage(heartbeat, 'initial heartbeat');
|
|
@@ -3367,7 +3361,6 @@ describe('HashTreeParser', () => {
|
|
|
3367
3361
|
};
|
|
3368
3362
|
|
|
3369
3363
|
const parser = createHashTreeParser(initialLocus, metadata);
|
|
3370
|
-
const heartbeatIntervalMs = 5000;
|
|
3371
3364
|
|
|
3372
3365
|
// Set Math.random to return 1 so that backoff = 1^exponent * maxMs = maxMs
|
|
3373
3366
|
mathRandomStub.returns(1);
|
|
@@ -3389,7 +3382,6 @@ describe('HashTreeParser', () => {
|
|
|
3389
3382
|
],
|
|
3390
3383
|
visibleDataSetsUrl,
|
|
3391
3384
|
locusUrl,
|
|
3392
|
-
heartbeatIntervalMs,
|
|
3393
3385
|
};
|
|
3394
3386
|
|
|
3395
3387
|
parser.handleMessage(heartbeat, 'heartbeat');
|
|
@@ -3439,7 +3431,6 @@ describe('HashTreeParser', () => {
|
|
|
3439
3431
|
|
|
3440
3432
|
it('does not set watchdog for data sets without a hash tree', async () => {
|
|
3441
3433
|
const parser = createHashTreeParser();
|
|
3442
|
-
const heartbeatIntervalMs = 5000;
|
|
3443
3434
|
|
|
3444
3435
|
// 'atd-active' is in the initial locus but is not visible (no hash tree)
|
|
3445
3436
|
// Send heartbeat mentioning a non-visible dataset
|
|
@@ -3453,7 +3444,6 @@ describe('HashTreeParser', () => {
|
|
|
3453
3444
|
],
|
|
3454
3445
|
visibleDataSetsUrl,
|
|
3455
3446
|
locusUrl,
|
|
3456
|
-
heartbeatIntervalMs,
|
|
3457
3447
|
};
|
|
3458
3448
|
|
|
3459
3449
|
parser.handleMessage(heartbeatMessage, 'heartbeat with non-visible dataset');
|
|
@@ -3477,7 +3467,6 @@ describe('HashTreeParser', () => {
|
|
|
3477
3467
|
],
|
|
3478
3468
|
visibleDataSetsUrl,
|
|
3479
3469
|
locusUrl,
|
|
3480
|
-
heartbeatIntervalMs,
|
|
3481
3470
|
};
|
|
3482
3471
|
|
|
3483
3472
|
parser.handleMessage(heartbeatMessage, 'initial heartbeat');
|
|
@@ -3531,6 +3520,122 @@ describe('HashTreeParser', () => {
|
|
|
3531
3520
|
// And the watchdog should still be running
|
|
3532
3521
|
expect(parser.dataSets.main.heartbeatWatchdogTimer).to.not.be.undefined;
|
|
3533
3522
|
});
|
|
3523
|
+
|
|
3524
|
+
it('uses dataset-level heartbeatIntervalMs over top-level value', async () => {
|
|
3525
|
+
const parser = createHashTreeParser();
|
|
3526
|
+
const datasetLevelInterval = 3000;
|
|
3527
|
+
const topLevelInterval = 8000;
|
|
3528
|
+
|
|
3529
|
+
// Send heartbeat with both top-level and dataset-level heartbeatIntervalMs
|
|
3530
|
+
const heartbeatMessage = {
|
|
3531
|
+
dataSets: [
|
|
3532
|
+
{
|
|
3533
|
+
...createDataSet('main', 16, 1100),
|
|
3534
|
+
root: parser.dataSets.main.hashTree.getRootHash(),
|
|
3535
|
+
heartbeatIntervalMs: datasetLevelInterval,
|
|
3536
|
+
},
|
|
3537
|
+
],
|
|
3538
|
+
visibleDataSetsUrl,
|
|
3539
|
+
locusUrl,
|
|
3540
|
+
heartbeatIntervalMs: topLevelInterval,
|
|
3541
|
+
};
|
|
3542
|
+
|
|
3543
|
+
parser.handleMessage(heartbeatMessage, 'heartbeat with both levels');
|
|
3544
|
+
|
|
3545
|
+
expect(parser.dataSets.main.heartbeatWatchdogTimer).to.not.be.undefined;
|
|
3546
|
+
|
|
3547
|
+
// Mock sync responses
|
|
3548
|
+
const mainDataSetUrl = parser.dataSets.main.url;
|
|
3549
|
+
mockGetHashesFromLocusResponse(
|
|
3550
|
+
mainDataSetUrl,
|
|
3551
|
+
new Array(16).fill('00000000000000000000000000000000'),
|
|
3552
|
+
createDataSet('main', 16, 1101)
|
|
3553
|
+
);
|
|
3554
|
+
mockSendSyncRequestResponse(mainDataSetUrl, null);
|
|
3555
|
+
|
|
3556
|
+
// Watchdog should NOT fire at the top-level interval (8000ms)
|
|
3557
|
+
// It should fire at the dataset-level interval (3000ms)
|
|
3558
|
+
await clock.tickAsync(datasetLevelInterval - 1);
|
|
3559
|
+
assert.notCalled(webexRequest);
|
|
3560
|
+
|
|
3561
|
+
await clock.tickAsync(1);
|
|
3562
|
+
// Now at datasetLevelInterval, watchdog should have fired
|
|
3563
|
+
assert.calledWith(
|
|
3564
|
+
webexRequest,
|
|
3565
|
+
sinon.match({
|
|
3566
|
+
method: 'GET',
|
|
3567
|
+
uri: `${mainDataSetUrl}/hashtree`,
|
|
3568
|
+
})
|
|
3569
|
+
);
|
|
3570
|
+
});
|
|
3571
|
+
|
|
3572
|
+
it('falls back to top-level heartbeatIntervalMs when dataset-level is missing', async () => {
|
|
3573
|
+
const parser = createHashTreeParser();
|
|
3574
|
+
const topLevelInterval = 7000;
|
|
3575
|
+
|
|
3576
|
+
// Send heartbeat with top-level heartbeatIntervalMs but no dataset-level
|
|
3577
|
+
const heartbeatMessage = {
|
|
3578
|
+
dataSets: [
|
|
3579
|
+
{
|
|
3580
|
+
...createDataSet('main', 16, 1100),
|
|
3581
|
+
root: parser.dataSets.main.hashTree.getRootHash(),
|
|
3582
|
+
heartbeatIntervalMs: undefined,
|
|
3583
|
+
},
|
|
3584
|
+
],
|
|
3585
|
+
visibleDataSetsUrl,
|
|
3586
|
+
locusUrl,
|
|
3587
|
+
heartbeatIntervalMs: topLevelInterval,
|
|
3588
|
+
};
|
|
3589
|
+
|
|
3590
|
+
parser.handleMessage(heartbeatMessage, 'heartbeat with top-level only');
|
|
3591
|
+
|
|
3592
|
+
expect(parser.dataSets.main.heartbeatWatchdogTimer).to.not.be.undefined;
|
|
3593
|
+
|
|
3594
|
+
// Mock sync responses
|
|
3595
|
+
const mainDataSetUrl = parser.dataSets.main.url;
|
|
3596
|
+
mockGetHashesFromLocusResponse(
|
|
3597
|
+
mainDataSetUrl,
|
|
3598
|
+
new Array(16).fill('00000000000000000000000000000000'),
|
|
3599
|
+
createDataSet('main', 16, 1101)
|
|
3600
|
+
);
|
|
3601
|
+
mockSendSyncRequestResponse(mainDataSetUrl, null);
|
|
3602
|
+
|
|
3603
|
+
// Should fire at the top-level interval
|
|
3604
|
+
await clock.tickAsync(topLevelInterval - 1);
|
|
3605
|
+
assert.notCalled(webexRequest);
|
|
3606
|
+
|
|
3607
|
+
await clock.tickAsync(1);
|
|
3608
|
+
assert.calledWith(
|
|
3609
|
+
webexRequest,
|
|
3610
|
+
sinon.match({
|
|
3611
|
+
method: 'GET',
|
|
3612
|
+
uri: `${mainDataSetUrl}/hashtree`,
|
|
3613
|
+
})
|
|
3614
|
+
);
|
|
3615
|
+
});
|
|
3616
|
+
|
|
3617
|
+
it('does not start watchdog when dataset-level heartbeatIntervalMs is 0 even if top-level is set', async () => {
|
|
3618
|
+
const parser = createHashTreeParser();
|
|
3619
|
+
|
|
3620
|
+
// Send heartbeat with dataset-level 0 and a top-level value
|
|
3621
|
+
const heartbeatMessage = {
|
|
3622
|
+
dataSets: [
|
|
3623
|
+
{
|
|
3624
|
+
...createDataSet('main', 16, 1100),
|
|
3625
|
+
root: parser.dataSets.main.hashTree.getRootHash(),
|
|
3626
|
+
heartbeatIntervalMs: 0,
|
|
3627
|
+
},
|
|
3628
|
+
],
|
|
3629
|
+
visibleDataSetsUrl,
|
|
3630
|
+
locusUrl,
|
|
3631
|
+
heartbeatIntervalMs: 5000,
|
|
3632
|
+
};
|
|
3633
|
+
|
|
3634
|
+
parser.handleMessage(heartbeatMessage, 'heartbeat with dataset-level 0');
|
|
3635
|
+
|
|
3636
|
+
// Dataset-level 0 means no watchdog, should NOT fall back to top-level
|
|
3637
|
+
expect(parser.dataSets.main.heartbeatWatchdogTimer).to.be.undefined;
|
|
3638
|
+
});
|
|
3534
3639
|
});
|
|
3535
3640
|
|
|
3536
3641
|
});
|
|
@@ -5196,7 +5301,6 @@ describe('HashTreeParser', () => {
|
|
|
5196
5301
|
],
|
|
5197
5302
|
visibleDataSetsUrl,
|
|
5198
5303
|
locusUrl,
|
|
5199
|
-
heartbeatIntervalMs: 5000,
|
|
5200
5304
|
locusStateElements: [
|
|
5201
5305
|
{
|
|
5202
5306
|
htMeta: {
|
|
@@ -5,7 +5,7 @@ import MockWebex from '@webex/test-helper-mock-webex';
|
|
|
5
5
|
import uuid from 'uuid';
|
|
6
6
|
import sinon from 'sinon';
|
|
7
7
|
import {DataChannelTokenType} from '@webex/internal-plugin-llm';
|
|
8
|
-
import {LLM_PRACTICE_SESSION, SHARE_STATUS} from '@webex/plugin-meetings/src/constants';
|
|
8
|
+
import {LLM_PRACTICE_SESSION, LOCUS_LLM_EVENT, SHARE_STATUS} from '@webex/plugin-meetings/src/constants';
|
|
9
9
|
|
|
10
10
|
describe('plugin-meetings', () => {
|
|
11
11
|
describe('Webinar', () => {
|
|
@@ -227,7 +227,7 @@ describe('plugin-meetings', () => {
|
|
|
227
227
|
|
|
228
228
|
beforeEach(() => {
|
|
229
229
|
relayListener = sinon.stub();
|
|
230
|
-
webinar.
|
|
230
|
+
webinar.llmListeners = {relay: relayListener, locusLLM: null};
|
|
231
231
|
});
|
|
232
232
|
|
|
233
233
|
it('disconnects the practice session channel and removes the tracked relay listener', async () => {
|
|
@@ -238,16 +238,16 @@ describe('plugin-meetings', () => {
|
|
|
238
238
|
{code: 3050, reason: 'done (permanent)'},
|
|
239
239
|
LLM_PRACTICE_SESSION
|
|
240
240
|
);
|
|
241
|
-
assert.
|
|
241
|
+
assert.calledWithExactly(
|
|
242
242
|
webex.internal.llm.off,
|
|
243
243
|
`event:relay.event:${LLM_PRACTICE_SESSION}`,
|
|
244
244
|
relayListener
|
|
245
245
|
);
|
|
246
|
-
assert.isNull(webinar.
|
|
246
|
+
assert.isNull(webinar.llmListeners.relay);
|
|
247
247
|
});
|
|
248
248
|
|
|
249
249
|
it('skips relay listener removal when no listener has been tracked', async () => {
|
|
250
|
-
webinar.
|
|
250
|
+
webinar.llmListeners.relay = null;
|
|
251
251
|
|
|
252
252
|
await webinar.cleanupPSDataChannel();
|
|
253
253
|
|
|
@@ -257,6 +257,31 @@ describe('plugin-meetings', () => {
|
|
|
257
257
|
assert.equal(relayOffCalls.length, 0);
|
|
258
258
|
});
|
|
259
259
|
|
|
260
|
+
it('disconnects and removes the tracked locusLLM listener', async () => {
|
|
261
|
+
const locusLLMListener = sinon.stub();
|
|
262
|
+
webinar.llmListeners.locusLLM = locusLLMListener;
|
|
263
|
+
|
|
264
|
+
await webinar.cleanupPSDataChannel();
|
|
265
|
+
|
|
266
|
+
assert.calledWithExactly(
|
|
267
|
+
webex.internal.llm.off,
|
|
268
|
+
`${LOCUS_LLM_EVENT}:${LLM_PRACTICE_SESSION}`,
|
|
269
|
+
locusLLMListener
|
|
270
|
+
);
|
|
271
|
+
assert.isNull(webinar.llmListeners.locusLLM);
|
|
272
|
+
});
|
|
273
|
+
|
|
274
|
+
it('skips locusLLM listener removal when no listener has been tracked', async () => {
|
|
275
|
+
webinar.llmListeners.locusLLM = null;
|
|
276
|
+
|
|
277
|
+
await webinar.cleanupPSDataChannel();
|
|
278
|
+
|
|
279
|
+
const locusLLMOffCalls = webex.internal.llm.off.args.filter(
|
|
280
|
+
([event]) => event === `${LOCUS_LLM_EVENT}:${LLM_PRACTICE_SESSION}`
|
|
281
|
+
);
|
|
282
|
+
assert.equal(locusLLMOffCalls.length, 0);
|
|
283
|
+
});
|
|
284
|
+
|
|
260
285
|
it('does not consult the meeting collection during cleanup', async () => {
|
|
261
286
|
webex.meetings.getMeetingByType = sinon.stub();
|
|
262
287
|
|
|
@@ -289,13 +314,16 @@ describe('plugin-meetings', () => {
|
|
|
289
314
|
describe('#updatePSDataChannel', () => {
|
|
290
315
|
let meeting;
|
|
291
316
|
let processRelayEvent;
|
|
317
|
+
let processLocusLLMEvent;
|
|
292
318
|
|
|
293
319
|
beforeEach(() => {
|
|
294
320
|
processRelayEvent = sinon.stub();
|
|
321
|
+
processLocusLLMEvent = sinon.stub();
|
|
295
322
|
meeting = {
|
|
296
323
|
locusUrl: 'locusUrl',
|
|
297
324
|
isJoined: sinon.stub().returns(true),
|
|
298
325
|
processRelayEvent,
|
|
326
|
+
processLocusLLMEvent,
|
|
299
327
|
locusInfo: {
|
|
300
328
|
url: 'locus-url',
|
|
301
329
|
info: {practiceSessionDatachannelUrl: 'dc-url'},
|
|
@@ -476,7 +504,7 @@ describe('plugin-meetings', () => {
|
|
|
476
504
|
await webinar.updatePSDataChannel();
|
|
477
505
|
|
|
478
506
|
// Stores the exact listener reference for deterministic cleanup
|
|
479
|
-
assert.equal(webinar.
|
|
507
|
+
assert.equal(webinar.llmListeners.relay, processRelayEvent);
|
|
480
508
|
assert.calledWith(
|
|
481
509
|
webex.internal.llm.on,
|
|
482
510
|
`event:relay.event:${LLM_PRACTICE_SESSION}`,
|
|
@@ -486,7 +514,7 @@ describe('plugin-meetings', () => {
|
|
|
486
514
|
|
|
487
515
|
it('removes a previously tracked relay listener before re-binding on reconnect', async () => {
|
|
488
516
|
const previousListener = sinon.stub();
|
|
489
|
-
webinar.
|
|
517
|
+
webinar.llmListeners = {relay: previousListener, locusLLM: null};
|
|
490
518
|
|
|
491
519
|
await webinar.updatePSDataChannel();
|
|
492
520
|
|
|
@@ -495,7 +523,32 @@ describe('plugin-meetings', () => {
|
|
|
495
523
|
`event:relay.event:${LLM_PRACTICE_SESSION}`,
|
|
496
524
|
previousListener
|
|
497
525
|
);
|
|
498
|
-
assert.equal(webinar.
|
|
526
|
+
assert.equal(webinar.llmListeners.relay, processRelayEvent);
|
|
527
|
+
});
|
|
528
|
+
|
|
529
|
+
it('tracks and binds the locusLLM listener after successful connect', async () => {
|
|
530
|
+
await webinar.updatePSDataChannel();
|
|
531
|
+
|
|
532
|
+
assert.equal(webinar.llmListeners.locusLLM, processLocusLLMEvent);
|
|
533
|
+
assert.calledWith(
|
|
534
|
+
webex.internal.llm.on,
|
|
535
|
+
`${LOCUS_LLM_EVENT}:${LLM_PRACTICE_SESSION}`,
|
|
536
|
+
processLocusLLMEvent
|
|
537
|
+
);
|
|
538
|
+
});
|
|
539
|
+
|
|
540
|
+
it('removes a previously tracked locusLLM listener before re-binding on reconnect', async () => {
|
|
541
|
+
const previousListener = sinon.stub();
|
|
542
|
+
webinar.llmListeners = {relay: null, locusLLM: previousListener};
|
|
543
|
+
|
|
544
|
+
await webinar.updatePSDataChannel();
|
|
545
|
+
|
|
546
|
+
assert.calledWith(
|
|
547
|
+
webex.internal.llm.off,
|
|
548
|
+
`${LOCUS_LLM_EVENT}:${LLM_PRACTICE_SESSION}`,
|
|
549
|
+
previousListener
|
|
550
|
+
);
|
|
551
|
+
assert.equal(webinar.llmListeners.locusLLM, processLocusLLMEvent);
|
|
499
552
|
});
|
|
500
553
|
|
|
501
554
|
it('subscribes to transcription when caption intent is enabled', async () => {
|