Skip to content

Commit

Permalink
further
Browse files Browse the repository at this point in the history
  • Loading branch information
lawrence-forooghian committed Jun 5, 2024
1 parent 423488a commit 95c3c8a
Show file tree
Hide file tree
Showing 5 changed files with 77 additions and 13 deletions.
8 changes: 8 additions & 0 deletions docs/internal/private-api-usage.md
Original file line number Diff line number Diff line change
Expand Up @@ -113,6 +113,8 @@ Marked in code.

### `test/realtime/transports.test.js`

Skipped marking in code.

Transports are a JS-specific concept so might not be worth worrying too much about the contents of this file

- `const Defaults = Ably.Rest.Platform.Defaults;`
Expand All @@ -129,12 +131,18 @@ Transports are a JS-specific concept so might not be worth worrying too much abo

### `test/realtime/utils.test.js`

Marked in code.

Ah, I just realised that some of the properties on `shared_helper` actually refer to properties of the library, e.g. `helper.Utils` is actually `Ably.Realtime.Utils`. So perhaps I missed some usages of internal APIs in earlier files. But can figure that out later.

(I’ve addressed this in the context of marking the code; utils stuff is properly marked.)

- this entire file is a test of the internal `utils.getRetryTime(…)` method

### `test/realtime/resume.test.js`

Marked in code.

- `connectionManager.once('transport.active',` and inside the callback it makes an assertion on `transport.params.mode`
- sets a channel’s state: `suspendedChannel.state = 'suspended';`
- sabotages a resume by setting `connection.connectionManager`’s `conectionKey` and `connectionId` to garbage
Expand Down
17 changes: 17 additions & 0 deletions test/common/modules/private_api_recorder.js
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ define([], function () {
'call.PresenceMessage.fromValues',
'call.Utils.mixin',
'call.Utils.toQueryString',
'call.Utils.getRetryTime',
'call.channel.checkPendingState',
'call.channel.processMessage',
'call.channel.requestState',
Expand Down Expand Up @@ -45,6 +46,7 @@ define([], function () {
'read.connectionManager.connectionId',
'read.realtime.connection.connectionManager.activeProtocol.transport',
'read.rest.serverTimeOffset',
'read.transport.params.mode',
'replace.channel.attachImpl',
'replace.channel.processMessage',
'replace.channel.sendMessage',
Expand All @@ -55,6 +57,21 @@ define([], function () {
'replace.transport.send',
'write.channel._lastPayload',
'write.realtime.options.timeouts.realtimeRequestTimeout',
'write.channel.state',
'write.connectionManager.connectionKey',
'write.connectionManager.connectionId',
'write.connectionManager.msgSerial',
'read.connectionManager.msgSerial',
'read.connectionManager.connectionId',
'call.connectionManager.disconnectAllTransports',
'write.auth.tokenDetails.token',
'read.auth.key',
'write.auth.key',
'replace.connectionManager.tryATransport',
'write.connectionManager.lastActivity',
'replace.connectionManager.send',
'call.connectionManager.send',
'call.ProtocolMessage.setFlag',
];

class PrivateApiRecorder {
Expand Down
5 changes: 4 additions & 1 deletion test/realtime/message.test.js
Original file line number Diff line number Diff line change
@@ -1,11 +1,14 @@
'use strict';

define(['ably', 'shared_helper', 'async', 'chai'], function (Ably, helper, async, chai) {
define(['ably', 'shared_helper', 'async', 'chai', 'private_api_recorder'], function (Ably, helper, async, chai, privateApiRecorder) {
var expect = chai.expect;
var displayError = helper.displayError;
// TODO
var utils = helper.Utils;
// TODO
let config = Ably.Realtime.Platform.Config;
var closeAndFinish = helper.closeAndFinish;
// TODO
var createPM = Ably.protocolMessageFromDeserialized;
var monitorConnection = helper.monitorConnection;
var testOnAllTransports = helper.testOnAllTransports;
Expand Down
55 changes: 44 additions & 11 deletions test/realtime/resume.test.js
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
'use strict';

define(['shared_helper', 'async', 'chai'], function (helper, async, chai) {
define(['shared_helper', 'async', 'chai', 'private_api_recorder'], function (helper, async, chai, privateApiRecorder) {
var expect = chai.expect;
var closeAndFinish = helper.closeAndFinish;
var simulateDroppedConnection = helper.simulateDroppedConnection;
Expand Down Expand Up @@ -41,7 +41,7 @@ define(['shared_helper', 'async', 'chai'], function (helper, async, chai) {
* Empty resume case
* Send 5 messages; disconnect; reconnect; send 5 messages
*/
function resume_inactive(done, channelName, txOpts, rxOpts) {
function resume_inactive(done, channelName, txOpts, rxOpts, privateApiContext) {
var count = 5;

var txRest = helper.AblyRest(mixin(txOpts));
Expand Down Expand Up @@ -79,8 +79,10 @@ define(['shared_helper', 'async', 'chai'], function (helper, async, chai) {
/* re-open the connection, verify resume mode */
rxRealtime.connection.connect();
var connectionManager = rxRealtime.connection.connectionManager;
privateApiContext.record('listen.connectionManager.transport.active');
connectionManager.once('transport.active', function (transport) {
try {
privateApiContext.record('read.transport.params.mode');
expect(transport.params.mode).to.equal('resume', 'Verify reconnect is resume mode');
} catch (err) {
callback(err);
Expand Down Expand Up @@ -139,16 +141,16 @@ define(['shared_helper', 'async', 'chai'], function (helper, async, chai) {
}

testOnAllTransports('resume_inactive', function (realtimeOpts) {
return function (done) {
resume_inactive(done, 'resume_inactive' + String(Math.random()), {}, realtimeOpts);
return function (done, privateApiContext) {
resume_inactive(done, 'resume_inactive' + String(Math.random()), {}, realtimeOpts, privateApiContext);
};
});

/**
* Simple resume case
* Send 5 messages; disconnect; send 5 messages; reconnect
*/
function resume_active(done, channelName, txOpts, rxOpts) {
function resume_active(done, channelName, txOpts, rxOpts, privateApiContext) {
var count = 5;

var txRest = helper.AblyRest(mixin(txOpts));
Expand Down Expand Up @@ -209,8 +211,10 @@ define(['shared_helper', 'async', 'chai'], function (helper, async, chai) {
rxCount = 0;
rxRealtime.connection.connect();
var connectionManager = rxRealtime.connection.connectionManager;
privateApiContext.record('listen.connectionManager.transport.active');
connectionManager.on('transport.active', function (transport) {
try {
privateApiContext.record('read.transport.params.mode');
expect(transport.params.mode).to.equal('resume', 'Verify reconnect is resume mode');
} catch (err) {
callback(err);
Expand Down Expand Up @@ -256,8 +260,8 @@ define(['shared_helper', 'async', 'chai'], function (helper, async, chai) {
}

testOnAllTransports('resume_active', function (realtimeOpts) {
return function (done) {
resume_active(done, 'resume_active' + String(Math.random()), {}, realtimeOpts);
return function (done, privateApiContext) {
resume_active(done, 'resume_active' + String(Math.random()), {}, realtimeOpts, privateApiContext);
};
});

Expand All @@ -266,7 +270,7 @@ define(['shared_helper', 'async', 'chai'], function (helper, async, chai) {
*/
testOnAllTransports(
'resume_lost_continuity',
function (realtimeOpts) {
function (realtimeOpts, privateApiContext) {
return function (done) {
var realtime = helper.AblyRealtime(realtimeOpts),
connection = realtime.connection,
Expand All @@ -283,17 +287,23 @@ define(['shared_helper', 'async', 'chai'], function (helper, async, chai) {
});
},
function (cb) {
privateApiContext.record('write.channel.state');
suspendedChannel.state = 'suspended';
whenPromiseSettles(attachedChannel.attach(), cb);
},
function (cb) {
/* Sabotage the resume */
(connection.connectionManager.connectionKey = '_____!ablyjs_test_fake-key____'),
privateApiContext.record('write.connectionManager.connectionKey');
privateApiContext.record('write.connectionManager.connectionId');
privateApiContext.record('write.connectionManager.msgSerial')(
(connection.connectionManager.connectionKey = '_____!ablyjs_test_fake-key____'),
),
(connection.connectionManager.connectionId = 'ablyjs_tes');
connection.connectionManager.msgSerial = 15;
connection.once('disconnected', function () {
cb();
});
privateApiContext.record('call.connectionManager.disconnectAllTransports');
connection.connectionManager.disconnectAllTransports();
},
function (cb) {
Expand All @@ -305,7 +315,9 @@ define(['shared_helper', 'async', 'chai'], function (helper, async, chai) {
);
expect(attachedChannel.state).to.equal('attaching', 'Attached channel went into attaching');
expect(suspendedChannel.state).to.equal('attaching', 'Suspended channel went into attaching');
privateApiContext.record('read.connectionManager.msgSerial');
expect(connection.connectionManager.msgSerial).to.equal(0, 'Check msgSerial is reset to 0');
privateApiContext.record('read.connectionManager.connectionId');
expect(
connection.connectionManager.connectionId !== 'ablyjs_tes',
'Check connectionId is set by the new CONNECTED',
Expand All @@ -332,7 +344,7 @@ define(['shared_helper', 'async', 'chai'], function (helper, async, chai) {
*/
testOnAllTransports(
'resume_token_error',
function (realtimeOpts) {
function (realtimeOpts, privateApiContext) {
return function (done) {
var realtime = helper.AblyRealtime(mixin(realtimeOpts, { useTokenAuth: true })),
badtoken,
Expand All @@ -353,6 +365,7 @@ define(['shared_helper', 'async', 'chai'], function (helper, async, chai) {
},
function (cb) {
/* Sabotage the resume - use a valid but now-expired token */
privateApiContext.record('write.auth.tokenDetails.token');
realtime.auth.tokenDetails.token = badtoken.token;
connection.once(function (stateChange) {
try {
Expand All @@ -363,6 +376,7 @@ define(['shared_helper', 'async', 'chai'], function (helper, async, chai) {
}
cb();
});
privateApiContext.record('call.connectionManager.disconnectAllTransports');
connection.connectionManager.disconnectAllTransports();
},
function (cb) {
Expand All @@ -385,7 +399,7 @@ define(['shared_helper', 'async', 'chai'], function (helper, async, chai) {
*/
testOnAllTransports(
'resume_fatal_error',
function (realtimeOpts) {
function (realtimeOpts, privateApiContext) {
return function (done) {
var realtime = helper.AblyRealtime(realtimeOpts),
connection = realtime.connection;
Expand All @@ -398,7 +412,9 @@ define(['shared_helper', 'async', 'chai'], function (helper, async, chai) {
});
},
function (cb) {
privateApiContext.record('read.auth.key');
var keyName = realtime.auth.key.split(':')[0];
privateApiContext.record('write.auth.key');
realtime.auth.key = keyName + ':wrong';
connection.once(function (stateChange) {
try {
Expand All @@ -409,6 +425,8 @@ define(['shared_helper', 'async', 'chai'], function (helper, async, chai) {
}
cb();
});
privateApiContext.record('call.connectionManager.disconnectAllTransports');

connection.connectionManager.disconnectAllTransports();
},
function (cb) {
Expand Down Expand Up @@ -501,6 +519,8 @@ define(['shared_helper', 'async', 'chai'], function (helper, async, chai) {
* Check the library doesn't try to resume once the connectionStateTtl has expired
*/
it('no_resume_once_suspended', function (done) {
const privateApiContext = privateApiRecorder.createContext(this);

var realtime = helper.AblyRealtime(),
connection = realtime.connection,
channelName = 'no_resume_once_suspended';
Expand All @@ -516,6 +536,7 @@ define(['shared_helper', 'async', 'chai'], function (helper, async, chai) {
helper.becomeSuspended(realtime, cb);
},
function (cb) {
privateApiContext.record('replace.connectionManager.tryATransport');
realtime.connection.connectionManager.tryATransport = function (transportParams) {
try {
expect(transportParams.mode).to.equal('clean', 'Check library didn’t try to resume');
Expand All @@ -539,15 +560,21 @@ define(['shared_helper', 'async', 'chai'], function (helper, async, chai) {
* connection was > connectionStateTtl ago
*/
it('no_resume_last_activity', function (done) {
const privateApiContext = privateApiRecorder.createContext(this);

var realtime = helper.AblyRealtime(),
connection = realtime.connection,
connectionManager = connection.connectionManager;

connection.once('connected', function () {
privateApiContext.record('write.connectionManager.lastActivity');
connectionManager.lastActivity = Date.now() - 10000000;
/* noop-out onProtocolMessage so that a DISCONNECTED message doesn't
* reset the last activity timer */
privateApiContext.record('call.realtime.connection.connectionManager.activeProtocol.getTransport');
privateApiContext.record('replace.transport.onProtocolMessage');
connectionManager.activeProtocol.getTransport().onProtocolMessage = function () {};
privateApiContext.record('replace.connectionManager.tryATransport');
connectionManager.tryATransport = function (transportParams) {
try {
expect(transportParams.mode).to.equal('clean', 'Check library didn’t try to resume');
Expand All @@ -557,11 +584,14 @@ define(['shared_helper', 'async', 'chai'], function (helper, async, chai) {
}
closeAndFinish(done, realtime);
};
privateApiContext.record('call.connectionManager.disconnectAllTransports');
connectionManager.disconnectAllTransports();
});
});

it('resume_rewind_1', function (done) {
const privateApiContext = privateApiRecorder.createContext(this);

var testName = 'resume_rewind_1';
var testMessage = { foo: 'bar', count: 1, status: 'active' };
try {
Expand All @@ -586,9 +616,12 @@ define(['shared_helper', 'async', 'chai'], function (helper, async, chai) {
var resumed_receiver_realtime = helper.AblyRealtime();
var connectionManager = resumed_receiver_realtime.connection.connectionManager;

privateApiContext.record('replace.connectionManager.send');
var sendOrig = connectionManager.send;
connectionManager.send = function (msg, queueEvent, callback) {
privateApiContext.record('call.ProtocolMessage.setFlag');
msg.setFlag('ATTACH_RESUME');
privateApiContext.record('call.connectionManager.send');
sendOrig.call(connectionManager, msg, queueEvent, callback);
};

Expand Down
5 changes: 4 additions & 1 deletion test/realtime/utils.test.js
Original file line number Diff line number Diff line change
@@ -1,15 +1,18 @@
'use strict';

define(['shared_helper', 'chai'], function (helper, chai) {
define(['shared_helper', 'chai', 'private_api_recorder'], function (helper, chai, privateApiRecorder) {
var utils = helper.Utils;
var expect = chai.expect;

// RTB1
describe('incremental backoff and jitter', function () {
it('should calculate retry timeouts using incremental backoff and jitter', function () {
const privateApiContext = privateApiRecorder.createContext(this);

var retryAttempts = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15];
var initialTimeout = 15;

privateApiContext.record('call.Utils.getRetryTime');
var retryTimeouts = retryAttempts.map((attempt) => utils.getRetryTime(initialTimeout, attempt));
expect(retryTimeouts.filter((timeout) => timeout >= 30).length).to.equal(0);

Expand Down

0 comments on commit 95c3c8a

Please sign in to comment.