@webex/internal-plugin-mercury 3.11.0 → 3.12.0-mobius-socket.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.
- package/README.md +54 -0
- package/dist/mercury.js +395 -198
- package/dist/mercury.js.map +1 -1
- package/dist/socket/constants.js +16 -0
- package/dist/socket/constants.js.map +1 -0
- package/dist/socket/socket-base.js +36 -3
- package/dist/socket/socket-base.js.map +1 -1
- package/package.json +17 -17
- package/src/mercury.js +398 -171
- package/src/socket/constants.js +6 -0
- package/src/socket/socket-base.js +40 -3
- package/test/unit/spec/mercury-events.js +20 -2
- package/test/unit/spec/mercury.js +307 -139
- package/test/unit/spec/socket.js +61 -0
|
@@ -100,9 +100,32 @@ describe('plugin-mercury', () => {
|
|
|
100
100
|
});
|
|
101
101
|
|
|
102
102
|
mercury = webex.internal.mercury;
|
|
103
|
+
mercury.defaultSessionId = 'mercury-default-session';
|
|
103
104
|
});
|
|
104
105
|
|
|
105
|
-
afterEach(() => {
|
|
106
|
+
afterEach(async () => {
|
|
107
|
+
// Clean up Mercury connections and internal state
|
|
108
|
+
if (mercury) {
|
|
109
|
+
try {
|
|
110
|
+
await mercury.disconnectAll();
|
|
111
|
+
} catch (e) {
|
|
112
|
+
// Ignore cleanup errors
|
|
113
|
+
}
|
|
114
|
+
// Clear any remaining connection promises
|
|
115
|
+
if (mercury._connectPromises) {
|
|
116
|
+
mercury._connectPromises.clear();
|
|
117
|
+
}
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
// Ensure mock socket is properly closed
|
|
121
|
+
if (mockWebSocket && typeof mockWebSocket.close === 'function') {
|
|
122
|
+
try {
|
|
123
|
+
mockWebSocket.close();
|
|
124
|
+
} catch (e) {
|
|
125
|
+
// Ignore cleanup errors
|
|
126
|
+
}
|
|
127
|
+
}
|
|
128
|
+
|
|
106
129
|
if (socketOpenStub) {
|
|
107
130
|
socketOpenStub.restore();
|
|
108
131
|
}
|
|
@@ -110,6 +133,9 @@ describe('plugin-mercury', () => {
|
|
|
110
133
|
if (Socket.getWebSocketConstructor.restore) {
|
|
111
134
|
Socket.getWebSocketConstructor.restore();
|
|
112
135
|
}
|
|
136
|
+
|
|
137
|
+
// Small delay to ensure all async operations complete
|
|
138
|
+
await new Promise(resolve => setTimeout(resolve, 10));
|
|
113
139
|
});
|
|
114
140
|
|
|
115
141
|
describe('#listen()', () => {
|
|
@@ -499,9 +525,13 @@ describe('plugin-mercury', () => {
|
|
|
499
525
|
|
|
500
526
|
// skipping due to apparent bug with lolex in all browsers but Chrome.
|
|
501
527
|
skipInBrowser(it)('does not continue attempting to connect', () => {
|
|
502
|
-
mercury.connect();
|
|
528
|
+
const promise = mercury.connect();
|
|
503
529
|
|
|
504
|
-
|
|
530
|
+
// Wait for the connection to be established before proceeding
|
|
531
|
+
mockWebSocket.open();
|
|
532
|
+
|
|
533
|
+
return promise.then(() =>
|
|
534
|
+
promiseTick(2)
|
|
505
535
|
.then(() => {
|
|
506
536
|
clock.tick(6 * webex.internal.mercury.config.backoffTimeReset);
|
|
507
537
|
|
|
@@ -509,7 +539,8 @@ describe('plugin-mercury', () => {
|
|
|
509
539
|
})
|
|
510
540
|
.then(() => {
|
|
511
541
|
assert.calledOnce(Socket.prototype.open);
|
|
512
|
-
|
|
542
|
+
})
|
|
543
|
+
);
|
|
513
544
|
});
|
|
514
545
|
});
|
|
515
546
|
|
|
@@ -584,11 +615,11 @@ describe('plugin-mercury', () => {
|
|
|
584
615
|
});
|
|
585
616
|
|
|
586
617
|
describe('#logout()', () => {
|
|
587
|
-
it('calls
|
|
618
|
+
it('calls disconnectAll and logs', () => {
|
|
588
619
|
sinon.stub(mercury.logger, 'info');
|
|
589
|
-
sinon.stub(mercury, '
|
|
620
|
+
sinon.stub(mercury, 'disconnectAll');
|
|
590
621
|
mercury.logout();
|
|
591
|
-
assert.called(mercury.
|
|
622
|
+
assert.called(mercury.disconnectAll);
|
|
592
623
|
assert.calledTwice(mercury.logger.info);
|
|
593
624
|
|
|
594
625
|
assert.calledWith(mercury.logger.info.getCall(0), 'Mercury: logout() called');
|
|
@@ -600,24 +631,24 @@ describe('plugin-mercury', () => {
|
|
|
600
631
|
});
|
|
601
632
|
|
|
602
633
|
it('uses the config.beforeLogoutOptionsCloseReason to disconnect and will send code 3050 for logout', () => {
|
|
603
|
-
sinon.stub(mercury, '
|
|
634
|
+
sinon.stub(mercury, 'disconnectAll');
|
|
604
635
|
mercury.config.beforeLogoutOptionsCloseReason = 'done (permanent)';
|
|
605
636
|
mercury.logout();
|
|
606
|
-
assert.calledWith(mercury.
|
|
637
|
+
assert.calledWith(mercury.disconnectAll, {code: 3050, reason: 'done (permanent)'});
|
|
607
638
|
});
|
|
608
639
|
|
|
609
640
|
it('uses the config.beforeLogoutOptionsCloseReason to disconnect and will send code 3050 for logout if the reason is different than standard', () => {
|
|
610
|
-
sinon.stub(mercury, '
|
|
641
|
+
sinon.stub(mercury, 'disconnectAll');
|
|
611
642
|
mercury.config.beforeLogoutOptionsCloseReason = 'test';
|
|
612
643
|
mercury.logout();
|
|
613
|
-
assert.calledWith(mercury.
|
|
644
|
+
assert.calledWith(mercury.disconnectAll, {code: 3050, reason: 'test'});
|
|
614
645
|
});
|
|
615
646
|
|
|
616
647
|
it('uses the config.beforeLogoutOptionsCloseReason to disconnect and will send undefined for logout if the reason is same as standard', () => {
|
|
617
|
-
sinon.stub(mercury, '
|
|
648
|
+
sinon.stub(mercury, 'disconnectAll');
|
|
618
649
|
mercury.config.beforeLogoutOptionsCloseReason = 'done (forced)';
|
|
619
650
|
mercury.logout();
|
|
620
|
-
assert.calledWith(mercury.
|
|
651
|
+
assert.calledWith(mercury.disconnectAll, undefined);
|
|
621
652
|
});
|
|
622
653
|
});
|
|
623
654
|
|
|
@@ -723,12 +754,12 @@ describe('plugin-mercury', () => {
|
|
|
723
754
|
return promiseTick(webex.internal.mercury.config.backoffTimeReset).then(() => {
|
|
724
755
|
// By this time backoffCall and mercury socket should be defined by the
|
|
725
756
|
// 'connect' call
|
|
726
|
-
assert.isDefined(mercury.
|
|
757
|
+
assert.isDefined(mercury.backoffCalls.get('mercury-default-session'), 'Mercury backoffCall is not defined');
|
|
727
758
|
assert.isDefined(mercury.socket, 'Mercury socket is not defined');
|
|
728
759
|
// Calling disconnect will abort the backoffCall, close the socket, and
|
|
729
760
|
// reject the connect
|
|
730
761
|
mercury.disconnect();
|
|
731
|
-
assert.isUndefined(mercury.
|
|
762
|
+
assert.isUndefined(mercury.backoffCalls.get('mercury-default-session'), 'Mercury backoffCall is still defined');
|
|
732
763
|
// The socket will never be unset (which seems bad)
|
|
733
764
|
assert.isDefined(mercury.socket, 'Mercury socket is not defined');
|
|
734
765
|
|
|
@@ -746,16 +777,24 @@ describe('plugin-mercury', () => {
|
|
|
746
777
|
|
|
747
778
|
let reason;
|
|
748
779
|
|
|
749
|
-
mercury.
|
|
750
|
-
|
|
751
|
-
|
|
752
|
-
|
|
780
|
+
mercury.backoffCalls.clear();
|
|
781
|
+
|
|
782
|
+
const promise = mercury._attemptConnection(
|
|
783
|
+
'ws://example.com',
|
|
784
|
+
'mercury-default-session',
|
|
785
|
+
(_reason) => {
|
|
786
|
+
reason = _reason;
|
|
787
|
+
}
|
|
788
|
+
);
|
|
753
789
|
|
|
754
790
|
return promiseTick(webex.internal.mercury.config.backoffTimeReset).then(() => {
|
|
755
791
|
assert.equal(
|
|
756
792
|
reason.message,
|
|
757
|
-
|
|
793
|
+
`Mercury: prevent socket open when backoffCall no longer defined for ${mercury.defaultSessionId}`
|
|
758
794
|
);
|
|
795
|
+
|
|
796
|
+
// Ensure the promise was actually rejected (short-circuited)
|
|
797
|
+
return assert.isRejected(promise);
|
|
759
798
|
});
|
|
760
799
|
});
|
|
761
800
|
|
|
@@ -776,7 +815,7 @@ describe('plugin-mercury', () => {
|
|
|
776
815
|
return assert.isRejected(promise).then((error) => {
|
|
777
816
|
const lastError = mercury.getLastError();
|
|
778
817
|
|
|
779
|
-
assert.equal(error.message,
|
|
818
|
+
assert.equal(error.message, `Mercury Connection Aborted for ${mercury.defaultSessionId}`);
|
|
780
819
|
assert.isDefined(lastError);
|
|
781
820
|
assert.equal(lastError, realError);
|
|
782
821
|
});
|
|
@@ -870,7 +909,7 @@ describe('plugin-mercury', () => {
|
|
|
870
909
|
},
|
|
871
910
|
};
|
|
872
911
|
assert.isUndefined(mercury.mercuryTimeOffset);
|
|
873
|
-
mercury._setTimeOffset(event);
|
|
912
|
+
mercury._setTimeOffset('mercury-default-session', event);
|
|
874
913
|
assert.isDefined(mercury.mercuryTimeOffset);
|
|
875
914
|
assert.isTrue(mercury.mercuryTimeOffset > 0);
|
|
876
915
|
});
|
|
@@ -880,7 +919,7 @@ describe('plugin-mercury', () => {
|
|
|
880
919
|
wsWriteTimestamp: Date.now() + 60000,
|
|
881
920
|
},
|
|
882
921
|
};
|
|
883
|
-
mercury._setTimeOffset(event);
|
|
922
|
+
mercury._setTimeOffset('mercury-default-session', event);
|
|
884
923
|
assert.isTrue(mercury.mercuryTimeOffset < 0);
|
|
885
924
|
});
|
|
886
925
|
it('handles invalid wsWriteTimestamp', () => {
|
|
@@ -891,7 +930,7 @@ describe('plugin-mercury', () => {
|
|
|
891
930
|
wsWriteTimestamp: invalidTimestamp,
|
|
892
931
|
},
|
|
893
932
|
};
|
|
894
|
-
mercury._setTimeOffset(event);
|
|
933
|
+
mercury._setTimeOffset('mercury-default-session', event);
|
|
895
934
|
assert.isUndefined(mercury.mercuryTimeOffset);
|
|
896
935
|
});
|
|
897
936
|
});
|
|
@@ -998,13 +1037,15 @@ describe('plugin-mercury', () => {
|
|
|
998
1037
|
describe('shutdown protocol', () => {
|
|
999
1038
|
describe('#_handleImminentShutdown()', () => {
|
|
1000
1039
|
let connectWithBackoffStub;
|
|
1040
|
+
const sessionId = 'mercury-default-session';
|
|
1001
1041
|
|
|
1002
1042
|
beforeEach(() => {
|
|
1003
1043
|
mercury.connected = true;
|
|
1004
|
-
mercury.
|
|
1044
|
+
mercury.sockets.set(sessionId, {
|
|
1005
1045
|
url: 'ws://old-socket.com',
|
|
1006
1046
|
removeAllListeners: sinon.stub(),
|
|
1007
|
-
};
|
|
1047
|
+
});
|
|
1048
|
+
mercury.socket = mercury.sockets.get(sessionId);
|
|
1008
1049
|
connectWithBackoffStub = sinon.stub(mercury, '_connectWithBackoff');
|
|
1009
1050
|
connectWithBackoffStub.returns(Promise.resolve());
|
|
1010
1051
|
sinon.stub(mercury, '_emit');
|
|
@@ -1013,32 +1054,47 @@ describe('plugin-mercury', () => {
|
|
|
1013
1054
|
afterEach(() => {
|
|
1014
1055
|
connectWithBackoffStub.restore();
|
|
1015
1056
|
mercury._emit.restore();
|
|
1057
|
+
mercury.sockets.clear();
|
|
1016
1058
|
});
|
|
1017
1059
|
|
|
1018
1060
|
it('should be idempotent - no-op if already in progress', () => {
|
|
1019
|
-
|
|
1061
|
+
// Simulate an existing switchover in progress by seeding the backoff map
|
|
1062
|
+
mercury._shutdownSwitchoverBackoffCalls.set(sessionId, {placeholder: true});
|
|
1020
1063
|
|
|
1021
|
-
mercury._handleImminentShutdown();
|
|
1064
|
+
mercury._handleImminentShutdown(sessionId);
|
|
1022
1065
|
|
|
1023
1066
|
assert.notCalled(connectWithBackoffStub);
|
|
1024
1067
|
});
|
|
1025
1068
|
|
|
1026
1069
|
it('should set switchover flags when called', () => {
|
|
1027
|
-
mercury._handleImminentShutdown();
|
|
1070
|
+
mercury._handleImminentShutdown(sessionId);
|
|
1028
1071
|
|
|
1029
|
-
|
|
1072
|
+
// With _connectWithBackoff stubbed, the backoff map entry may not be created here.
|
|
1073
|
+
// Assert that switchover initiation state was set and a shutdown switchover connect was requested.
|
|
1030
1074
|
assert.isDefined(mercury._shutdownSwitchoverId);
|
|
1075
|
+
|
|
1076
|
+
assert.calledOnce(connectWithBackoffStub);
|
|
1077
|
+
const callArgs = connectWithBackoffStub.firstCall.args;
|
|
1078
|
+
assert.isUndefined(callArgs[0]); // webSocketUrl
|
|
1079
|
+
assert.equal(callArgs[1], sessionId); // sessionId
|
|
1080
|
+
assert.isObject(callArgs[2]); // context
|
|
1081
|
+
assert.isTrue(callArgs[2].isShutdownSwitchover);
|
|
1082
|
+
assert.isObject(callArgs[2].attemptOptions);
|
|
1083
|
+
assert.isTrue(callArgs[2].attemptOptions.isShutdownSwitchover);
|
|
1031
1084
|
});
|
|
1032
1085
|
|
|
1033
1086
|
it('should call _connectWithBackoff with correct parameters', (done) => {
|
|
1034
|
-
mercury._handleImminentShutdown();
|
|
1087
|
+
mercury._handleImminentShutdown(sessionId);
|
|
1035
1088
|
|
|
1036
1089
|
process.nextTick(() => {
|
|
1037
1090
|
assert.calledOnce(connectWithBackoffStub);
|
|
1038
1091
|
const callArgs = connectWithBackoffStub.firstCall.args;
|
|
1039
1092
|
assert.isUndefined(callArgs[0]); // webSocketUrl
|
|
1040
|
-
assert.
|
|
1041
|
-
assert.
|
|
1093
|
+
assert.equal(callArgs[1], sessionId); // sessionId
|
|
1094
|
+
assert.isObject(callArgs[2]); // context
|
|
1095
|
+
assert.isTrue(callArgs[2].isShutdownSwitchover);
|
|
1096
|
+
assert.isObject(callArgs[2].attemptOptions);
|
|
1097
|
+
assert.isTrue(callArgs[2].attemptOptions.isShutdownSwitchover);
|
|
1042
1098
|
done();
|
|
1043
1099
|
});
|
|
1044
1100
|
});
|
|
@@ -1047,12 +1103,17 @@ describe('plugin-mercury', () => {
|
|
|
1047
1103
|
connectWithBackoffStub.restore();
|
|
1048
1104
|
sinon.stub(mercury, '_connectWithBackoff').throws(new Error('Connection failed'));
|
|
1049
1105
|
|
|
1050
|
-
mercury._handleImminentShutdown();
|
|
1106
|
+
mercury._handleImminentShutdown(sessionId);
|
|
1051
1107
|
|
|
1052
|
-
|
|
1108
|
+
// When an exception happens synchronously, the placeholder entry
|
|
1109
|
+
// should be removed from the map.
|
|
1110
|
+
const switchoverCall = mercury._shutdownSwitchoverBackoffCalls.get(sessionId);
|
|
1111
|
+
assert.isUndefined(switchoverCall);
|
|
1112
|
+
mercury._connectWithBackoff.restore();
|
|
1053
1113
|
});
|
|
1054
1114
|
});
|
|
1055
1115
|
|
|
1116
|
+
|
|
1056
1117
|
describe('#_onmessage() with shutdown message', () => {
|
|
1057
1118
|
beforeEach(() => {
|
|
1058
1119
|
sinon.stub(mercury, '_handleImminentShutdown');
|
|
@@ -1073,10 +1134,15 @@ describe('plugin-mercury', () => {
|
|
|
1073
1134
|
},
|
|
1074
1135
|
};
|
|
1075
1136
|
|
|
1076
|
-
const result = mercury._onmessage(shutdownEvent);
|
|
1137
|
+
const result = mercury._onmessage(mercury.defaultSessionId, shutdownEvent);
|
|
1077
1138
|
|
|
1078
1139
|
assert.calledOnce(mercury._handleImminentShutdown);
|
|
1079
|
-
assert.calledWith(
|
|
1140
|
+
assert.calledWith(
|
|
1141
|
+
mercury._emit,
|
|
1142
|
+
mercury.defaultSessionId,
|
|
1143
|
+
'event:mercury_shutdown_imminent',
|
|
1144
|
+
shutdownEvent.data
|
|
1145
|
+
);
|
|
1080
1146
|
assert.instanceOf(result, Promise);
|
|
1081
1147
|
});
|
|
1082
1148
|
|
|
@@ -1087,7 +1153,7 @@ describe('plugin-mercury', () => {
|
|
|
1087
1153
|
},
|
|
1088
1154
|
};
|
|
1089
1155
|
|
|
1090
|
-
mercury._onmessage(shutdownEvent);
|
|
1156
|
+
mercury._onmessage(mercury.defaultSessionId, shutdownEvent);
|
|
1091
1157
|
|
|
1092
1158
|
assert.calledOnce(mercury._handleImminentShutdown);
|
|
1093
1159
|
});
|
|
@@ -1102,12 +1168,118 @@ describe('plugin-mercury', () => {
|
|
|
1102
1168
|
},
|
|
1103
1169
|
};
|
|
1104
1170
|
|
|
1105
|
-
mercury._onmessage(regularEvent);
|
|
1171
|
+
mercury._onmessage(mercury.defaultSessionId, regularEvent);
|
|
1106
1172
|
|
|
1107
1173
|
assert.notCalled(mercury._handleImminentShutdown);
|
|
1108
1174
|
});
|
|
1109
1175
|
});
|
|
1110
1176
|
|
|
1177
|
+
describe('#_onmessage() with missing data or eventType', () => {
|
|
1178
|
+
beforeEach(() => {
|
|
1179
|
+
sinon.stub(mercury, '_emit');
|
|
1180
|
+
sinon.stub(mercury, '_setTimeOffset');
|
|
1181
|
+
sinon.stub(mercury, '_applyOverrides');
|
|
1182
|
+
});
|
|
1183
|
+
|
|
1184
|
+
afterEach(() => {
|
|
1185
|
+
mercury._emit.restore();
|
|
1186
|
+
mercury._setTimeOffset.restore();
|
|
1187
|
+
mercury._applyOverrides.restore();
|
|
1188
|
+
});
|
|
1189
|
+
|
|
1190
|
+
it('should not throw when envelope.data is undefined', () => {
|
|
1191
|
+
const event = {
|
|
1192
|
+
data: {
|
|
1193
|
+
type: 'someType',
|
|
1194
|
+
// no nested data property
|
|
1195
|
+
},
|
|
1196
|
+
};
|
|
1197
|
+
|
|
1198
|
+
const result = mercury._onmessage(mercury.defaultSessionId, event);
|
|
1199
|
+
|
|
1200
|
+
assert.instanceOf(result, Promise);
|
|
1201
|
+
assert.calledWith(mercury._emit, mercury.defaultSessionId, 'event', event.data);
|
|
1202
|
+
});
|
|
1203
|
+
|
|
1204
|
+
it('should not throw when data.eventType is undefined', () => {
|
|
1205
|
+
const event = {
|
|
1206
|
+
data: {
|
|
1207
|
+
type: 'someType',
|
|
1208
|
+
data: {
|
|
1209
|
+
// no eventType property
|
|
1210
|
+
someField: 'value',
|
|
1211
|
+
},
|
|
1212
|
+
},
|
|
1213
|
+
};
|
|
1214
|
+
|
|
1215
|
+
const result = mercury._onmessage(mercury.defaultSessionId, event);
|
|
1216
|
+
|
|
1217
|
+
assert.instanceOf(result, Promise);
|
|
1218
|
+
assert.calledWith(mercury._emit, mercury.defaultSessionId, 'event', event.data);
|
|
1219
|
+
});
|
|
1220
|
+
|
|
1221
|
+
it('should emit generic event for messages without eventType (e.g. subscription responses)', () => {
|
|
1222
|
+
const event = {
|
|
1223
|
+
data: {
|
|
1224
|
+
id: 'msg-123',
|
|
1225
|
+
sequenceNumber: 5,
|
|
1226
|
+
data: {
|
|
1227
|
+
statusCode: 200,
|
|
1228
|
+
},
|
|
1229
|
+
},
|
|
1230
|
+
};
|
|
1231
|
+
|
|
1232
|
+
const result = mercury._onmessage(mercury.defaultSessionId, event);
|
|
1233
|
+
|
|
1234
|
+
assert.instanceOf(result, Promise);
|
|
1235
|
+
assert.calledOnce(mercury._emit);
|
|
1236
|
+
assert.calledWith(mercury._emit, mercury.defaultSessionId, 'event', event.data);
|
|
1237
|
+
});
|
|
1238
|
+
|
|
1239
|
+
it('should still process messages with a valid eventType', async () => {
|
|
1240
|
+
const event = {
|
|
1241
|
+
data: {
|
|
1242
|
+
data: {
|
|
1243
|
+
eventType: 'conversation.activity',
|
|
1244
|
+
},
|
|
1245
|
+
},
|
|
1246
|
+
};
|
|
1247
|
+
|
|
1248
|
+
await mercury._onmessage(mercury.defaultSessionId, event);
|
|
1249
|
+
|
|
1250
|
+
// Normal flow emits namespace-specific events after processing handlers.
|
|
1251
|
+
// The early-return guard only emits 'event', so asserting these proves the normal path was taken.
|
|
1252
|
+
assert.calledWith(mercury._emit, mercury.defaultSessionId, 'event:conversation', event.data);
|
|
1253
|
+
assert.calledWith(mercury._emit, mercury.defaultSessionId, 'event:conversation.activity', event.data);
|
|
1254
|
+
});
|
|
1255
|
+
});
|
|
1256
|
+
|
|
1257
|
+
describe('#_getEventHandlers()', () => {
|
|
1258
|
+
it('should return an empty array when eventType is undefined', () => {
|
|
1259
|
+
const result = mercury._getEventHandlers(undefined);
|
|
1260
|
+
|
|
1261
|
+
assert.deepEqual(result, []);
|
|
1262
|
+
});
|
|
1263
|
+
|
|
1264
|
+
it('should return an empty array when eventType is null', () => {
|
|
1265
|
+
const result = mercury._getEventHandlers(null);
|
|
1266
|
+
|
|
1267
|
+
assert.deepEqual(result, []);
|
|
1268
|
+
});
|
|
1269
|
+
|
|
1270
|
+
it('should return an empty array when eventType is an empty string', () => {
|
|
1271
|
+
const result = mercury._getEventHandlers('');
|
|
1272
|
+
|
|
1273
|
+
assert.deepEqual(result, []);
|
|
1274
|
+
});
|
|
1275
|
+
|
|
1276
|
+
it('should return an empty array when namespace is not registered', () => {
|
|
1277
|
+
const result = mercury._getEventHandlers('unknownNamespace.someEvent');
|
|
1278
|
+
|
|
1279
|
+
assert.deepEqual(result, []);
|
|
1280
|
+
});
|
|
1281
|
+
});
|
|
1282
|
+
|
|
1111
1283
|
describe('#_onclose() with code 4001 (shutdown replacement)', () => {
|
|
1112
1284
|
let mockSocket, anotherSocket;
|
|
1113
1285
|
|
|
@@ -1121,6 +1293,7 @@ describe('plugin-mercury', () => {
|
|
|
1121
1293
|
removeAllListeners: sinon.stub(),
|
|
1122
1294
|
};
|
|
1123
1295
|
mercury.socket = mockSocket;
|
|
1296
|
+
mercury.sockets.set(mercury.defaultSessionId, mockSocket);
|
|
1124
1297
|
mercury.connected = true;
|
|
1125
1298
|
sinon.stub(mercury, '_emit');
|
|
1126
1299
|
sinon.stub(mercury, '_reconnect');
|
|
@@ -1139,9 +1312,9 @@ describe('plugin-mercury', () => {
|
|
|
1139
1312
|
reason: 'replaced during shutdown',
|
|
1140
1313
|
};
|
|
1141
1314
|
|
|
1142
|
-
mercury._onclose(closeEvent, mockSocket);
|
|
1315
|
+
mercury._onclose(mercury.defaultSessionId, closeEvent, mockSocket);
|
|
1143
1316
|
|
|
1144
|
-
assert.calledWith(mercury._emit, 'offline.permanent', closeEvent);
|
|
1317
|
+
assert.calledWith(mercury._emit, mercury.defaultSessionId, 'offline.permanent', closeEvent);
|
|
1145
1318
|
assert.notCalled(mercury._reconnect); // No reconnect for 4001 on active socket
|
|
1146
1319
|
assert.isFalse(mercury.connected);
|
|
1147
1320
|
});
|
|
@@ -1152,9 +1325,9 @@ describe('plugin-mercury', () => {
|
|
|
1152
1325
|
reason: 'replaced during shutdown',
|
|
1153
1326
|
};
|
|
1154
1327
|
|
|
1155
|
-
mercury._onclose(closeEvent, anotherSocket);
|
|
1328
|
+
mercury._onclose(mercury.defaultSessionId, closeEvent, anotherSocket);
|
|
1156
1329
|
|
|
1157
|
-
assert.calledWith(mercury._emit, 'offline.replaced', closeEvent);
|
|
1330
|
+
assert.calledWith(mercury._emit, mercury.defaultSessionId, 'offline.replaced', closeEvent);
|
|
1158
1331
|
assert.notCalled(mercury._reconnect);
|
|
1159
1332
|
assert.isTrue(mercury.connected); // Should remain connected
|
|
1160
1333
|
assert.notCalled(mercury.unset);
|
|
@@ -1167,15 +1340,16 @@ describe('plugin-mercury', () => {
|
|
|
1167
1340
|
};
|
|
1168
1341
|
|
|
1169
1342
|
// Test non-active socket
|
|
1170
|
-
mercury._onclose(closeEvent, anotherSocket);
|
|
1171
|
-
assert.calledWith(mercury._emit, 'offline.replaced', closeEvent);
|
|
1343
|
+
mercury._onclose(mercury.defaultSessionId, closeEvent, anotherSocket);
|
|
1344
|
+
assert.calledWith(mercury._emit, mercury.defaultSessionId, 'offline.replaced', closeEvent);
|
|
1172
1345
|
|
|
1173
1346
|
// Reset the spy call history
|
|
1174
1347
|
mercury._emit.resetHistory();
|
|
1175
1348
|
|
|
1176
1349
|
// Test active socket
|
|
1177
|
-
mercury.
|
|
1178
|
-
|
|
1350
|
+
mercury.sockets.set(mercury.defaultSessionId, mockSocket);
|
|
1351
|
+
mercury._onclose(mercury.defaultSessionId, closeEvent, mockSocket);
|
|
1352
|
+
assert.calledWith(mercury._emit, mercury.defaultSessionId, 'offline.permanent', closeEvent);
|
|
1179
1353
|
});
|
|
1180
1354
|
|
|
1181
1355
|
it('should handle missing sourceSocket parameter (treats as non-active)', () => {
|
|
@@ -1184,10 +1358,10 @@ describe('plugin-mercury', () => {
|
|
|
1184
1358
|
reason: 'replaced during shutdown',
|
|
1185
1359
|
};
|
|
1186
1360
|
|
|
1187
|
-
mercury._onclose(closeEvent); // No sourceSocket parameter
|
|
1361
|
+
mercury._onclose(mercury.defaultSessionId, closeEvent); // No sourceSocket parameter
|
|
1188
1362
|
|
|
1189
1363
|
// With simplified logic, undefined !== this.socket, so isActiveSocket = false
|
|
1190
|
-
assert.calledWith(mercury._emit, 'offline.replaced', closeEvent);
|
|
1364
|
+
assert.calledWith(mercury._emit, mercury.defaultSessionId, 'offline.replaced', closeEvent);
|
|
1191
1365
|
assert.notCalled(mercury._reconnect);
|
|
1192
1366
|
});
|
|
1193
1367
|
|
|
@@ -1198,7 +1372,7 @@ describe('plugin-mercury', () => {
|
|
|
1198
1372
|
};
|
|
1199
1373
|
|
|
1200
1374
|
// Close non-active socket (not the active one)
|
|
1201
|
-
mercury._onclose(closeEvent, anotherSocket);
|
|
1375
|
+
mercury._onclose(mercury.defaultSessionId, closeEvent, anotherSocket);
|
|
1202
1376
|
|
|
1203
1377
|
// Verify listeners were removed from the old socket
|
|
1204
1378
|
// The _onclose method checks if sourceSocket !== this.socket (non-active)
|
|
@@ -1213,7 +1387,7 @@ describe('plugin-mercury', () => {
|
|
|
1213
1387
|
};
|
|
1214
1388
|
|
|
1215
1389
|
// Close active socket
|
|
1216
|
-
mercury._onclose(closeEvent, mockSocket);
|
|
1390
|
+
mercury._onclose(mercury.defaultSessionId, closeEvent, mockSocket);
|
|
1217
1391
|
|
|
1218
1392
|
// Verify listeners were removed from active socket
|
|
1219
1393
|
assert.calledOnce(mockSocket.removeAllListeners);
|
|
@@ -1222,13 +1396,15 @@ describe('plugin-mercury', () => {
|
|
|
1222
1396
|
|
|
1223
1397
|
describe('shutdown switchover with retry logic', () => {
|
|
1224
1398
|
let connectWithBackoffStub;
|
|
1399
|
+
const sessionId = 'mercury-default-session';
|
|
1225
1400
|
|
|
1226
1401
|
beforeEach(() => {
|
|
1227
1402
|
mercury.connected = true;
|
|
1228
|
-
mercury.
|
|
1403
|
+
mercury.sockets.set(sessionId, {
|
|
1229
1404
|
url: 'ws://old-socket.com',
|
|
1230
1405
|
removeAllListeners: sinon.stub(),
|
|
1231
|
-
};
|
|
1406
|
+
});
|
|
1407
|
+
mercury.socket = mercury.sockets.get(sessionId);
|
|
1232
1408
|
connectWithBackoffStub = sinon.stub(mercury, '_connectWithBackoff');
|
|
1233
1409
|
sinon.stub(mercury, '_emit');
|
|
1234
1410
|
});
|
|
@@ -1236,39 +1412,46 @@ describe('plugin-mercury', () => {
|
|
|
1236
1412
|
afterEach(() => {
|
|
1237
1413
|
connectWithBackoffStub.restore();
|
|
1238
1414
|
mercury._emit.restore();
|
|
1415
|
+
mercury.sockets.clear();
|
|
1239
1416
|
});
|
|
1240
1417
|
|
|
1241
1418
|
it('should call _connectWithBackoff with shutdown switchover context', (done) => {
|
|
1242
1419
|
connectWithBackoffStub.returns(Promise.resolve());
|
|
1243
1420
|
|
|
1244
|
-
mercury._handleImminentShutdown();
|
|
1421
|
+
mercury._handleImminentShutdown(sessionId);
|
|
1245
1422
|
|
|
1246
|
-
// Give it a tick for the async call to happen
|
|
1247
1423
|
process.nextTick(() => {
|
|
1248
1424
|
assert.calledOnce(connectWithBackoffStub);
|
|
1249
1425
|
const callArgs = connectWithBackoffStub.firstCall.args;
|
|
1250
1426
|
|
|
1251
|
-
assert.isUndefined(callArgs[0]); // webSocketUrl
|
|
1252
|
-
assert.
|
|
1253
|
-
assert.
|
|
1254
|
-
assert.
|
|
1255
|
-
assert.
|
|
1427
|
+
assert.isUndefined(callArgs[0]); // webSocketUrl
|
|
1428
|
+
assert.equal(callArgs[1], sessionId);
|
|
1429
|
+
assert.isObject(callArgs[2]);
|
|
1430
|
+
assert.isTrue(callArgs[2].isShutdownSwitchover);
|
|
1431
|
+
assert.isObject(callArgs[2].attemptOptions);
|
|
1432
|
+
assert.isTrue(callArgs[2].attemptOptions.isShutdownSwitchover);
|
|
1256
1433
|
done();
|
|
1257
1434
|
});
|
|
1258
1435
|
});
|
|
1259
1436
|
|
|
1260
1437
|
it('should set _shutdownSwitchoverInProgress flag during switchover', () => {
|
|
1261
|
-
|
|
1438
|
+
// With the new behavior, "in progress" is represented by the presence
|
|
1439
|
+
// of an entry in _shutdownSwitchoverBackoffCalls.
|
|
1440
|
+
// Since _connectWithBackoff is stubbed in this suite, simulate its side-effect
|
|
1441
|
+
// of seeding the backoff-call map entry.
|
|
1442
|
+
connectWithBackoffStub.callsFake(() => {
|
|
1443
|
+
mercury._shutdownSwitchoverBackoffCalls.set(sessionId, {placeholder: true});
|
|
1444
|
+
return new Promise(() => {}); // Never resolves
|
|
1445
|
+
});
|
|
1262
1446
|
|
|
1263
|
-
mercury._handleImminentShutdown();
|
|
1447
|
+
mercury._handleImminentShutdown(sessionId);
|
|
1264
1448
|
|
|
1265
|
-
|
|
1449
|
+
const switchoverBackoffCall = mercury._shutdownSwitchoverBackoffCalls.get(sessionId);
|
|
1450
|
+
assert.isOk(switchoverBackoffCall);
|
|
1266
1451
|
});
|
|
1267
1452
|
|
|
1268
1453
|
it('should emit success event when switchover completes', async () => {
|
|
1269
|
-
|
|
1270
|
-
connectWithBackoffStub.callsFake((url, context) => {
|
|
1271
|
-
// Simulate successful connection by calling onSuccess
|
|
1454
|
+
connectWithBackoffStub.callsFake((url, sid, context) => {
|
|
1272
1455
|
if (context && context.attemptOptions && context.attemptOptions.onSuccess) {
|
|
1273
1456
|
const mockSocket = {url: 'ws://new-socket.com'};
|
|
1274
1457
|
context.attemptOptions.onSuccess(mockSocket, 'ws://new-socket.com');
|
|
@@ -1276,14 +1459,15 @@ describe('plugin-mercury', () => {
|
|
|
1276
1459
|
return Promise.resolve();
|
|
1277
1460
|
});
|
|
1278
1461
|
|
|
1279
|
-
mercury._handleImminentShutdown();
|
|
1462
|
+
mercury._handleImminentShutdown(sessionId);
|
|
1280
1463
|
|
|
1281
|
-
// Wait for async operations
|
|
1282
1464
|
await promiseTick(50);
|
|
1283
1465
|
|
|
1284
1466
|
const emitCalls = mercury._emit.getCalls();
|
|
1285
1467
|
const hasCompleteEvent = emitCalls.some(
|
|
1286
|
-
(call) =>
|
|
1468
|
+
(call) =>
|
|
1469
|
+
call.args[0] === sessionId &&
|
|
1470
|
+
call.args[1] === 'event:mercury_shutdown_switchover_complete'
|
|
1287
1471
|
);
|
|
1288
1472
|
|
|
1289
1473
|
assert.isTrue(hasCompleteEvent, 'Should emit switchover complete event');
|
|
@@ -1294,16 +1478,16 @@ describe('plugin-mercury', () => {
|
|
|
1294
1478
|
|
|
1295
1479
|
connectWithBackoffStub.returns(Promise.reject(testError));
|
|
1296
1480
|
|
|
1297
|
-
mercury._handleImminentShutdown();
|
|
1481
|
+
mercury._handleImminentShutdown(sessionId);
|
|
1298
1482
|
await promiseTick(50);
|
|
1299
1483
|
|
|
1300
|
-
// Check if failure event was emitted
|
|
1301
1484
|
const emitCalls = mercury._emit.getCalls();
|
|
1302
1485
|
const hasFailureEvent = emitCalls.some(
|
|
1303
1486
|
(call) =>
|
|
1304
|
-
call.args[0] ===
|
|
1305
|
-
call.args[1] &&
|
|
1306
|
-
call.args[
|
|
1487
|
+
call.args[0] === sessionId &&
|
|
1488
|
+
call.args[1] === 'event:mercury_shutdown_switchover_failed' &&
|
|
1489
|
+
call.args[2] &&
|
|
1490
|
+
call.args[2].reason === testError
|
|
1307
1491
|
);
|
|
1308
1492
|
|
|
1309
1493
|
assert.isTrue(hasFailureEvent, 'Should emit switchover failed event');
|
|
@@ -1312,10 +1496,9 @@ describe('plugin-mercury', () => {
|
|
|
1312
1496
|
it('should allow old socket to be closed by server after switchover failure', async () => {
|
|
1313
1497
|
connectWithBackoffStub.returns(Promise.reject(new Error('Failed')));
|
|
1314
1498
|
|
|
1315
|
-
mercury._handleImminentShutdown();
|
|
1499
|
+
mercury._handleImminentShutdown(sessionId);
|
|
1316
1500
|
await promiseTick(50);
|
|
1317
1501
|
|
|
1318
|
-
// Old socket should not be closed immediately - server will close it
|
|
1319
1502
|
assert.equal(mercury.socket.removeAllListeners.callCount, 0);
|
|
1320
1503
|
});
|
|
1321
1504
|
});
|
|
@@ -1412,18 +1595,16 @@ describe('plugin-mercury', () => {
|
|
|
1412
1595
|
});
|
|
1413
1596
|
|
|
1414
1597
|
describe('#_attemptConnection() with shutdown switchover', () => {
|
|
1415
|
-
let
|
|
1598
|
+
let prepareAndOpenSocketStub, callback;
|
|
1599
|
+
const sessionId = 'mercury-default-session';
|
|
1416
1600
|
|
|
1417
1601
|
beforeEach(() => {
|
|
1418
|
-
mockSocket = {
|
|
1419
|
-
url: 'ws://test.com',
|
|
1420
|
-
};
|
|
1421
1602
|
prepareAndOpenSocketStub = sinon
|
|
1422
1603
|
.stub(mercury, '_prepareAndOpenSocket')
|
|
1423
1604
|
.returns(Promise.resolve('ws://new-socket.com'));
|
|
1424
1605
|
callback = sinon.stub();
|
|
1425
|
-
mercury.
|
|
1426
|
-
mercury.socket =
|
|
1606
|
+
mercury._shutdownSwitchoverBackoffCalls.set(sessionId, {abort: sinon.stub()});
|
|
1607
|
+
mercury.socket = {url: 'ws://test.com'};
|
|
1427
1608
|
mercury.connected = true;
|
|
1428
1609
|
sinon.stub(mercury, '_emit');
|
|
1429
1610
|
sinon.stub(mercury, '_attachSocketEventListeners');
|
|
@@ -1433,28 +1614,26 @@ describe('plugin-mercury', () => {
|
|
|
1433
1614
|
prepareAndOpenSocketStub.restore();
|
|
1434
1615
|
mercury._emit.restore();
|
|
1435
1616
|
mercury._attachSocketEventListeners.restore();
|
|
1617
|
+
mercury._shutdownSwitchoverBackoffCalls.clear();
|
|
1436
1618
|
});
|
|
1437
1619
|
|
|
1438
1620
|
it('should not set socket reference before opening for shutdown switchover', async () => {
|
|
1439
1621
|
const originalSocket = mercury.socket;
|
|
1440
1622
|
|
|
1441
|
-
await mercury._attemptConnection('ws://test.com', callback, {
|
|
1623
|
+
await mercury._attemptConnection('ws://test.com', sessionId, callback, {
|
|
1442
1624
|
isShutdownSwitchover: true,
|
|
1443
1625
|
onSuccess: (newSocket, url) => {
|
|
1444
|
-
// During onSuccess, verify original socket is still set
|
|
1445
|
-
// (socket swap happens inside onSuccess callback in _handleImminentShutdown)
|
|
1446
1626
|
assert.equal(mercury.socket, originalSocket);
|
|
1447
1627
|
},
|
|
1448
1628
|
});
|
|
1449
1629
|
|
|
1450
|
-
// After onSuccess, socket should still be original since we only swap in _handleImminentShutdown
|
|
1451
1630
|
assert.equal(mercury.socket, originalSocket);
|
|
1452
1631
|
});
|
|
1453
1632
|
|
|
1454
1633
|
it('should call onSuccess callback with new socket and URL for shutdown', async () => {
|
|
1455
1634
|
const onSuccessStub = sinon.stub();
|
|
1456
1635
|
|
|
1457
|
-
await mercury._attemptConnection('ws://test.com', callback, {
|
|
1636
|
+
await mercury._attemptConnection('ws://test.com', sessionId, callback, {
|
|
1458
1637
|
isShutdownSwitchover: true,
|
|
1459
1638
|
onSuccess: onSuccessStub,
|
|
1460
1639
|
});
|
|
@@ -1464,20 +1643,22 @@ describe('plugin-mercury', () => {
|
|
|
1464
1643
|
});
|
|
1465
1644
|
|
|
1466
1645
|
it('should emit shutdown switchover complete event', async () => {
|
|
1467
|
-
|
|
1468
|
-
|
|
1469
|
-
await mercury._attemptConnection('ws://test.com', callback, {
|
|
1646
|
+
await mercury._attemptConnection('ws://test.com', sessionId, callback, {
|
|
1470
1647
|
isShutdownSwitchover: true,
|
|
1471
1648
|
onSuccess: (newSocket, url) => {
|
|
1472
|
-
// Simulate the onSuccess callback behavior
|
|
1473
1649
|
mercury.socket = newSocket;
|
|
1474
1650
|
mercury.connected = true;
|
|
1475
|
-
mercury._emit(
|
|
1651
|
+
mercury._emit(
|
|
1652
|
+
sessionId,
|
|
1653
|
+
'event:mercury_shutdown_switchover_complete',
|
|
1654
|
+
{url}
|
|
1655
|
+
);
|
|
1476
1656
|
},
|
|
1477
1657
|
});
|
|
1478
1658
|
|
|
1479
1659
|
assert.calledWith(
|
|
1480
1660
|
mercury._emit,
|
|
1661
|
+
sessionId,
|
|
1481
1662
|
'event:mercury_shutdown_switchover_complete',
|
|
1482
1663
|
sinon.match.has('url', 'ws://new-socket.com')
|
|
1483
1664
|
);
|
|
@@ -1486,25 +1667,25 @@ describe('plugin-mercury', () => {
|
|
|
1486
1667
|
it('should use simpler error handling for shutdown switchover failures', async () => {
|
|
1487
1668
|
prepareAndOpenSocketStub.returns(Promise.reject(new Error('Connection failed')));
|
|
1488
1669
|
|
|
1489
|
-
|
|
1490
|
-
|
|
1670
|
+
await mercury
|
|
1671
|
+
._attemptConnection('ws://test.com', sessionId, callback, {
|
|
1491
1672
|
isShutdownSwitchover: true,
|
|
1492
|
-
})
|
|
1493
|
-
|
|
1494
|
-
// Error should be caught and passed to callback
|
|
1495
|
-
}
|
|
1673
|
+
})
|
|
1674
|
+
.catch(() => {});
|
|
1496
1675
|
|
|
1497
|
-
// Should call callback with error for retry
|
|
1498
1676
|
assert.calledOnce(callback);
|
|
1499
1677
|
assert.instanceOf(callback.firstCall.args[0], Error);
|
|
1500
1678
|
});
|
|
1501
1679
|
|
|
1502
1680
|
it('should check _shutdownSwitchoverBackoffCall for shutdown connections', () => {
|
|
1503
|
-
mercury.
|
|
1681
|
+
mercury._shutdownSwitchoverBackoffCalls.clear();
|
|
1504
1682
|
|
|
1505
|
-
const result = mercury._attemptConnection(
|
|
1506
|
-
|
|
1507
|
-
|
|
1683
|
+
const result = mercury._attemptConnection(
|
|
1684
|
+
'ws://test.com',
|
|
1685
|
+
sessionId,
|
|
1686
|
+
callback,
|
|
1687
|
+
{isShutdownSwitchover: true}
|
|
1688
|
+
);
|
|
1508
1689
|
|
|
1509
1690
|
return result.catch((err) => {
|
|
1510
1691
|
assert.instanceOf(err, Error);
|
|
@@ -1514,35 +1695,28 @@ describe('plugin-mercury', () => {
|
|
|
1514
1695
|
});
|
|
1515
1696
|
|
|
1516
1697
|
describe('#_connectWithBackoff() with shutdown switchover', () => {
|
|
1517
|
-
|
|
1518
|
-
// to avoid test hangs. The backoff mechanism itself is tested in other test suites.
|
|
1698
|
+
const sessionId = 'mercury-default-session';
|
|
1519
1699
|
|
|
1520
1700
|
it('should use shutdown-specific parameters when called', () => {
|
|
1521
|
-
// Stub _connectWithBackoff to prevent real execution
|
|
1522
1701
|
const connectWithBackoffStub = sinon
|
|
1523
1702
|
.stub(mercury, '_connectWithBackoff')
|
|
1524
1703
|
.returns(Promise.resolve());
|
|
1525
1704
|
|
|
1526
|
-
mercury._handleImminentShutdown();
|
|
1705
|
+
mercury._handleImminentShutdown(sessionId);
|
|
1527
1706
|
|
|
1528
|
-
// Verify it was called with shutdown context
|
|
1529
1707
|
assert.calledOnce(connectWithBackoffStub);
|
|
1530
1708
|
const callArgs = connectWithBackoffStub.firstCall.args;
|
|
1531
|
-
assert.
|
|
1532
|
-
assert.
|
|
1709
|
+
assert.equal(callArgs[1], sessionId);
|
|
1710
|
+
assert.isObject(callArgs[2]);
|
|
1711
|
+
assert.isTrue(callArgs[2].isShutdownSwitchover);
|
|
1533
1712
|
|
|
1534
1713
|
connectWithBackoffStub.restore();
|
|
1535
1714
|
});
|
|
1536
1715
|
|
|
1537
1716
|
it('should pass shutdown switchover options to _attemptConnection', () => {
|
|
1538
|
-
// Stub _attemptConnection to verify it receives correct options
|
|
1539
1717
|
const attemptStub = sinon.stub(mercury, '_attemptConnection');
|
|
1540
|
-
attemptStub.callsFake((url,
|
|
1541
|
-
// Immediately succeed
|
|
1542
|
-
callback();
|
|
1543
|
-
});
|
|
1718
|
+
attemptStub.callsFake((url, sid, cb) => cb());
|
|
1544
1719
|
|
|
1545
|
-
// Call _connectWithBackoff with shutdown context
|
|
1546
1720
|
const context = {
|
|
1547
1721
|
isShutdownSwitchover: true,
|
|
1548
1722
|
attemptOptions: {
|
|
@@ -1551,34 +1725,30 @@ describe('plugin-mercury', () => {
|
|
|
1551
1725
|
},
|
|
1552
1726
|
};
|
|
1553
1727
|
|
|
1554
|
-
|
|
1555
|
-
const promise = mercury._connectWithBackoff(undefined, context);
|
|
1728
|
+
const promise = mercury._connectWithBackoff(undefined, sessionId, context);
|
|
1556
1729
|
|
|
1557
|
-
// Check that _attemptConnection was called with shutdown options
|
|
1558
1730
|
return promise.then(() => {
|
|
1559
1731
|
assert.calledOnce(attemptStub);
|
|
1560
1732
|
const callArgs = attemptStub.firstCall.args;
|
|
1561
|
-
assert.
|
|
1562
|
-
assert.
|
|
1563
|
-
|
|
1733
|
+
assert.equal(callArgs[1], sessionId);
|
|
1734
|
+
assert.isObject(callArgs[3]);
|
|
1735
|
+
assert.isTrue(callArgs[3].isShutdownSwitchover);
|
|
1564
1736
|
attemptStub.restore();
|
|
1565
1737
|
});
|
|
1566
1738
|
});
|
|
1567
1739
|
|
|
1568
1740
|
it('should set and clear state flags appropriately', () => {
|
|
1569
|
-
|
|
1570
|
-
sinon.stub(mercury, '_attemptConnection').callsFake((url, callback) => callback());
|
|
1741
|
+
sinon.stub(mercury, '_attemptConnection').callsFake((url, sid, cb) => cb());
|
|
1571
1742
|
|
|
1572
|
-
mercury.
|
|
1743
|
+
mercury._shutdownSwitchoverBackoffCalls.set(sessionId, {placeholder: true});
|
|
1573
1744
|
|
|
1574
|
-
const promise = mercury._connectWithBackoff(undefined, {
|
|
1745
|
+
const promise = mercury._connectWithBackoff(undefined, sessionId, {
|
|
1575
1746
|
isShutdownSwitchover: true,
|
|
1576
1747
|
attemptOptions: {isShutdownSwitchover: true, onSuccess: () => {}},
|
|
1577
1748
|
});
|
|
1578
1749
|
|
|
1579
1750
|
return promise.then(() => {
|
|
1580
|
-
|
|
1581
|
-
assert.isFalse(mercury._shutdownSwitchoverInProgress);
|
|
1751
|
+
assert.isUndefined(mercury._shutdownSwitchoverBackoffCalls.get(sessionId));
|
|
1582
1752
|
mercury._attemptConnection.restore();
|
|
1583
1753
|
});
|
|
1584
1754
|
});
|
|
@@ -1586,32 +1756,30 @@ describe('plugin-mercury', () => {
|
|
|
1586
1756
|
|
|
1587
1757
|
describe('#disconnect() with shutdown switchover in progress', () => {
|
|
1588
1758
|
let abortStub;
|
|
1759
|
+
const sessionId = 'mercury-default-session';
|
|
1589
1760
|
|
|
1590
1761
|
beforeEach(() => {
|
|
1591
|
-
mercury.
|
|
1762
|
+
mercury.sockets.clear();
|
|
1763
|
+
mercury.sockets.set(sessionId, {
|
|
1592
1764
|
close: sinon.stub().returns(Promise.resolve()),
|
|
1593
1765
|
removeAllListeners: sinon.stub(),
|
|
1594
|
-
};
|
|
1766
|
+
});
|
|
1595
1767
|
abortStub = sinon.stub();
|
|
1596
|
-
mercury.
|
|
1597
|
-
abort: abortStub,
|
|
1598
|
-
};
|
|
1768
|
+
mercury._shutdownSwitchoverBackoffCalls.set(sessionId, {abort: abortStub});
|
|
1599
1769
|
});
|
|
1600
1770
|
|
|
1601
1771
|
it('should abort shutdown switchover backoff call on disconnect', async () => {
|
|
1602
|
-
await mercury.disconnect();
|
|
1772
|
+
await mercury.disconnect(undefined, sessionId);
|
|
1603
1773
|
|
|
1604
1774
|
assert.calledOnce(abortStub);
|
|
1605
1775
|
});
|
|
1606
1776
|
|
|
1607
1777
|
it('should handle disconnect when no switchover is in progress', async () => {
|
|
1608
|
-
mercury.
|
|
1778
|
+
mercury._shutdownSwitchoverBackoffCalls.clear();
|
|
1609
1779
|
|
|
1610
|
-
|
|
1611
|
-
await mercury.disconnect();
|
|
1780
|
+
await mercury.disconnect(undefined, sessionId);
|
|
1612
1781
|
|
|
1613
|
-
|
|
1614
|
-
assert.calledOnce(mercury.socket.close);
|
|
1782
|
+
assert.calledOnce(mercury.sockets.get(sessionId).close);
|
|
1615
1783
|
});
|
|
1616
1784
|
});
|
|
1617
1785
|
});
|