Skip to content

Commit 1715ee5

Browse files
author
Chris Miller
committed
[STREAM-151] 100% test coverage
1 parent 808f47e commit 1715ee5

3 files changed

Lines changed: 204 additions & 4 deletions

File tree

jest.config.js

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -34,10 +34,10 @@ module.exports = {
3434
coverageDirectory: './coverage',
3535
coverageThreshold: {
3636
global: {
37-
branches: 95,
38-
functions: 95,
39-
lines: 95,
40-
statements: 95
37+
branches: 100,
38+
functions: 100,
39+
lines: 100,
40+
statements: 100
4141
}
4242
},
4343
reporters: [

test/unit/sessions/softphone-session-handler.test.ts

Lines changed: 53 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -325,6 +325,34 @@ describe('handleSessionInit()', () => {
325325
expect(acceptSessionSpy).toHaveBeenCalled();
326326
expect(oldSession.sessionReplacedByReinvite).toBeTruthy();
327327
});
328+
329+
it('should create a StatsAggregator when reportStatistics is enabled', async () => {
330+
const superInit = jest.spyOn(BaseSessionHandler.prototype, 'handleSessionInit');
331+
const acceptSessionSpy = jest.spyOn(handler, 'acceptSession').mockImplementation();
332+
mockSdk._config.autoConnectSessions = true;
333+
mockSdk._config.reportStatistics = true;
334+
335+
const session: any = new MockSession();
336+
await handler.handleSessionInit(session);
337+
338+
expect(superInit).toHaveBeenCalled();
339+
expect(session.statsAggregator).toBeDefined();
340+
expect(acceptSessionSpy).toHaveBeenCalled();
341+
});
342+
343+
it('should not create a StatsAggregator when reportStatistics is not enabled', async () => {
344+
const superInit = jest.spyOn(BaseSessionHandler.prototype, 'handleSessionInit');
345+
const acceptSessionSpy = jest.spyOn(handler, 'acceptSession').mockImplementation();
346+
mockSdk._config.autoConnectSessions = true;
347+
mockSdk._config.reportStatistics = false;
348+
349+
const session: any = new MockSession();
350+
await handler.handleSessionInit(session);
351+
352+
expect(superInit).toHaveBeenCalled();
353+
expect(session.statsAggregator).toBeUndefined();
354+
expect(acceptSessionSpy).toHaveBeenCalled();
355+
});
328356
});
329357

330358
describe('acceptSession()', () => {
@@ -2847,4 +2875,29 @@ describe('isConversationHeld()', () => {
28472875
expect(handler.boundPersistentConnectionEventHandler).not.toEqual(old);
28482876
});
28492877
});
2878+
2879+
describe('enableHandler', () => {
2880+
it('should listen for persistent connection events when not a guest', async () => {
2881+
(mockSdk as any).isGuest = false;
2882+
const subscribeSpy = mockSdk._streamingConnection.notifications.subscribe;
2883+
2884+
await handler.enableHandler();
2885+
2886+
expect(subscribeSpy).toHaveBeenCalledWith(
2887+
expect.stringContaining('persistentconnection'),
2888+
expect.any(Function),
2889+
true
2890+
);
2891+
expect(handler.boundPersistentConnectionEventHandler).toBeDefined();
2892+
});
2893+
2894+
it('should not listen for persistent connection events when a guest', async () => {
2895+
(mockSdk as any).isGuest = true;
2896+
const subscribeSpy = mockSdk._streamingConnection.notifications.subscribe;
2897+
2898+
await handler.enableHandler();
2899+
2900+
expect(subscribeSpy).not.toHaveBeenCalled();
2901+
});
2902+
});
28502903
});

test/unit/stats-aggregator.test.ts

Lines changed: 147 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,99 @@ describe('StatsAggregator', () => {
1111
expect(statsAggregator).toBeTruthy();
1212
});
1313
});
14+
describe('shouldGatherImmediately / eager persistent connection', () => {
15+
it('should not start gathering stats immediately for eager persistent connections (privAnswerMode === Auto)', () => {
16+
const mockSession = new MockSession() as unknown as IExtendedMediaSession;
17+
(mockSession as any).privAnswerMode = 'Auto';
18+
const sdk = new SimpleMockSdk() as unknown as GenesysCloudWebrtSdk;
19+
const statsAggregator = new StatsAggregator(mockSession, sdk);
20+
21+
// statsGatherer should NOT be created yet for eager persistent connections
22+
expect(statsAggregator['statsGatherer']).toBeFalsy();
23+
});
24+
25+
it('should start gathering stats immediately for non-eager sessions', () => {
26+
const mockSession = new MockSession() as unknown as IExtendedMediaSession;
27+
(mockSession as any).privAnswerMode = 'Manual';
28+
const sdk = new SimpleMockSdk() as unknown as GenesysCloudWebrtSdk;
29+
const statsAggregator = new StatsAggregator(mockSession, sdk);
30+
31+
expect(statsAggregator['statsGatherer']).toBeTruthy();
32+
});
33+
});
34+
35+
describe('onSessionStarted', () => {
36+
it('should start gathering stats and set baseline when the matching session starts', () => {
37+
const mockSession = new MockSession() as unknown as IExtendedMediaSession;
38+
(mockSession as any).privAnswerMode = 'Auto';
39+
const sdk = new SimpleMockSdk() as unknown as GenesysCloudWebrtSdk;
40+
const statsAggregator = new StatsAggregator(mockSession, sdk);
41+
42+
expect(statsAggregator['statsGatherer']).toBeFalsy();
43+
44+
// Emit sessionStarted with the same session
45+
(sdk as any).emit('sessionStarted', mockSession);
46+
47+
expect(statsAggregator['statsGatherer']).toBeTruthy();
48+
expect(statsAggregator['setBaseline']).toBe(true);
49+
});
50+
51+
it('should not start gathering stats for a different session', () => {
52+
const mockSession = new MockSession() as unknown as IExtendedMediaSession;
53+
(mockSession as any).privAnswerMode = 'Auto';
54+
const sdk = new SimpleMockSdk() as unknown as GenesysCloudWebrtSdk;
55+
const statsAggregator = new StatsAggregator(mockSession, sdk);
56+
57+
const otherSession = new MockSession() as unknown as IExtendedMediaSession;
58+
(sdk as any).emit('sessionStarted', otherSession);
59+
60+
expect(statsAggregator['statsGatherer']).toBeFalsy();
61+
});
62+
});
63+
64+
describe('onSessionEnded', () => {
65+
it('should stop gathering stats when the matching session ends', () => {
66+
const mockSession = new MockSession() as unknown as IExtendedMediaSession;
67+
const sdk = new SimpleMockSdk() as unknown as GenesysCloudWebrtSdk;
68+
const statsAggregator = new StatsAggregator(mockSession, sdk);
69+
70+
expect(statsAggregator['statsGatherer']).toBeTruthy();
71+
72+
(sdk as any).emit('sessionEnded', mockSession);
73+
74+
expect(statsAggregator['statsGatherer']).toBeFalsy();
75+
});
76+
77+
it('should not stop gathering stats for a different session', () => {
78+
const mockSession = new MockSession() as unknown as IExtendedMediaSession;
79+
const sdk = new SimpleMockSdk() as unknown as GenesysCloudWebrtSdk;
80+
const statsAggregator = new StatsAggregator(mockSession, sdk);
81+
82+
const otherSession = new MockSession() as unknown as IExtendedMediaSession;
83+
(sdk as any).emit('sessionEnded', otherSession);
84+
85+
expect(statsAggregator['statsGatherer']).toBeTruthy();
86+
});
87+
});
88+
89+
describe('session terminated event', () => {
90+
it('should stop gathering stats and remove SDK listeners on session terminated', () => {
91+
const mockSession = new MockSession() as unknown as IExtendedMediaSession;
92+
const sdk = new SimpleMockSdk() as unknown as GenesysCloudWebrtSdk;
93+
const statsAggregator = new StatsAggregator(mockSession, sdk);
94+
95+
expect(statsAggregator['statsGatherer']).toBeTruthy();
96+
97+
const listenerCountBefore = (sdk as any).listenerCount('sessionStarted');
98+
99+
(mockSession as any).emit('terminated');
100+
101+
expect(statsAggregator['statsGatherer']).toBeFalsy();
102+
// SDK listeners for sessionStarted/sessionEnded should be removed
103+
expect((sdk as any).listenerCount('sessionStarted')).toBe(listenerCountBefore - 1);
104+
});
105+
});
106+
14107
describe('handleStatsUpdate', () => {
15108
it('should only handle a GetStatsEvent', () => {
16109
const mockSession = new MockSession() as unknown as IExtendedMediaSession;
@@ -108,6 +201,60 @@ describe('StatsAggregator', () => {
108201
);
109202
});
110203

204+
it('should not send stats when jitter is undefined', () => {
205+
const mockSession = new MockSession() as unknown as IExtendedMediaSession;
206+
const sdk = new SimpleMockSdk() as unknown as GenesysCloudWebrtSdk;
207+
const statsAggregator = new StatsAggregator(mockSession, sdk);
208+
statsAggregator['sendStats'] = jest.fn();
209+
210+
// Trigger baseline
211+
statsAggregator['setBaseline'] = true;
212+
const baselineEvent = {
213+
name: 'getStats',
214+
tracks: [{ totalRoundTripTime: 0, roundTripTimeMeasurements: 0 }],
215+
remoteTracks: [{ packetsReceived: 0, packetsLost: 0, jitter: 0.001, timestamp: Date.now() }]
216+
};
217+
statsAggregator['handleStatsUpdate'](baselineEvent);
218+
219+
// Event with all fields present except jitter
220+
const event = {
221+
name: 'getStats',
222+
tracks: [{ totalRoundTripTime: 0.1, roundTripTimeMeasurements: 1 }],
223+
remoteTracks: [{ packetsReceived: 10, packetsLost: 0, timestamp: Date.now() }]
224+
};
225+
226+
statsAggregator['handleStatsUpdate'](event);
227+
228+
expect(statsAggregator['sendStats']).not.toHaveBeenCalled();
229+
});
230+
231+
it('should handle packetLoss calculation when packetsReceived is 0', () => {
232+
const mockSession = new MockSession() as unknown as IExtendedMediaSession;
233+
const sdk = new SimpleMockSdk() as unknown as GenesysCloudWebrtSdk;
234+
const statsAggregator = new StatsAggregator(mockSession, sdk);
235+
statsAggregator['sendStats'] = jest.fn();
236+
237+
// Trigger baseline with same values so packetsReceived delta is 0
238+
statsAggregator['setBaseline'] = true;
239+
const baselineEvent = {
240+
name: 'getStats',
241+
tracks: [{ totalRoundTripTime: 0, roundTripTimeMeasurements: 0 }],
242+
remoteTracks: [{ packetsReceived: 10, packetsLost: 0, jitter: 0.001, timestamp: Date.now() }]
243+
};
244+
statsAggregator['handleStatsUpdate'](baselineEvent);
245+
246+
// Same packetsReceived as baseline → delta is 0
247+
const event = {
248+
name: 'getStats',
249+
tracks: [{ totalRoundTripTime: 0.1, roundTripTimeMeasurements: 1 }],
250+
remoteTracks: [{ packetsReceived: 10, packetsLost: 0, jitter: 0.002, timestamp: Date.now() }]
251+
};
252+
253+
statsAggregator['handleStatsUpdate'](event);
254+
255+
expect(statsAggregator['sendStats']).toHaveBeenCalled();
256+
});
257+
111258
it('should send stats when we have everything we want', () => {
112259
const mockSession = new MockSession() as unknown as IExtendedMediaSession;
113260
const sdk = new SimpleMockSdk() as unknown as GenesysCloudWebrtSdk;

0 commit comments

Comments
 (0)