From f53e514a1909525733d9a9cb45004c05f5c0f182 Mon Sep 17 00:00:00 2001 From: Lawrence Forooghian Date: Tue, 4 Jun 2024 10:29:40 -0300 Subject: [PATCH] Record usages of private APIs in the test suite MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This adds annotations to each place in the test suite where a private API is called. When the tests are run, these annotations are used to emit a JSON file containing private API usage information. (In ECO-4834, we’ll process this JSON file to generate some useful reports.) This JSON file also contains a list of all of the tests which get run, which will be used as part of the aforementioned reporting to allow us to generate a report of the tests that do not use any private APIs. The motivation for this gathering this data is that we intend to try and reuse the ably-js test suite as a unified test suite for all of our client libraries. In order to do this, we’ll need to address the usage of ably-js private APIs in the test suite, and to do that we need to first gather data about the current usage. I originally intended to put the changes into the current commit into a separate unified test suite branch, instead of merging them into `main`. But I don’t really want to maintain a long-running feature branch, and also I want to make sure that we add private API annotations to any new tests that we add to ably-js, hence I decided to put this commit into `main`. But maybe, once we’re done with the unified test suite, we’ll decide to remove these annotations. Given the context in which we’re gathering this data, in the interests of saving time I skipped adding some annotations to some files that I’m pretty sure will not form part of the unified test suite: - test/realtime/transports.test.js - test/browser/simple.test.js - test/browser/http.test.js - test/browser/connection.test.js - test/browser/modular.test.js - test/browser/push.test.js - test/rest/bufferutils.test.js If we decide at some point that we’d like to for whatever reason gather data on these tests, we can do that later. (Note I haven't been very consistent with how many times per test I record the usage of a given private API; it’s not a stat I’m particularly interested in here. But for the most part I’m doing it at the point of each invocation of the private API.) Resolves ECO-4821. --- .github/workflows/test-browser.yml | 5 + .github/workflows/test-node.yml | 5 + .gitignore | 1 + CONTRIBUTING.md | 24 +++ test/common/globals/named_dependencies.js | 4 + test/common/modules/client_module.js | 15 +- test/common/modules/private_api_recorder.js | 196 ++++++++++++++++++ test/common/modules/shared_helper.js | 207 ++++++++++++++++++-- test/common/modules/testapp_manager.js | 15 +- test/common/modules/testapp_module.js | 8 +- test/realtime/auth.test.js | 40 +++- test/realtime/channel.test.js | 37 ++++ test/realtime/connection.test.js | 18 ++ test/realtime/connectivity.test.js | 72 +++++-- test/realtime/crypto.test.js | 31 +++ test/realtime/delta.test.js | 1 + test/realtime/encoding.test.js | 5 + test/realtime/event_emitter.test.js | 21 ++ test/realtime/failure.test.js | 33 +++- test/realtime/init.test.js | 34 +++- test/realtime/message.test.js | 20 ++ test/realtime/presence.test.js | 26 +++ test/realtime/resume.test.js | 29 ++- test/realtime/sync.test.js | 33 ++++ test/realtime/utils.test.js | 1 + test/rest/auth.test.js | 9 + test/rest/defaults.test.js | 55 +++++- test/rest/fallbacks.test.js | 8 + test/rest/history.test.js | 5 + test/rest/http.test.js | 6 + test/rest/init.test.js | 5 + test/rest/message.test.js | 13 ++ test/rest/presence.test.js | 7 +- test/rest/push.test.js | 39 +++- test/rest/request.test.js | 5 +- test/support/junit_directory_path.js | 3 - test/support/mocha_reporter.js | 4 +- test/support/output_directory_paths.js | 6 + test/support/playwrightSetup.js | 9 +- test/support/root_hooks.js | 2 + test/support/runPlaywrightTests.js | 23 ++- 41 files changed, 995 insertions(+), 85 deletions(-) create mode 100644 test/common/modules/private_api_recorder.js delete mode 100644 test/support/junit_directory_path.js create mode 100644 test/support/output_directory_paths.js diff --git a/.github/workflows/test-browser.yml b/.github/workflows/test-browser.yml index c434c2e582..5cb471fd16 100644 --- a/.github/workflows/test-browser.yml +++ b/.github/workflows/test-browser.yml @@ -30,6 +30,11 @@ jobs: - env: PLAYWRIGHT_BROWSER: ${{ matrix.browser }} run: npm run test:playwright + - name: Save private API usage data + uses: actions/upload-artifact@v4 + with: + name: private-api-usage-${{ matrix.browser }} + path: private-api-usage - name: Upload test results if: always() uses: ably/test-observability-action@v1 diff --git a/.github/workflows/test-node.yml b/.github/workflows/test-node.yml index 39ccf4d0e6..db3ba7b7d4 100644 --- a/.github/workflows/test-node.yml +++ b/.github/workflows/test-node.yml @@ -28,6 +28,11 @@ jobs: - run: npm run test:node env: CI: true + - name: Save private API usage data + uses: actions/upload-artifact@v4 + with: + name: private-api-usage-${{ matrix.node-version }} + path: private-api-usage - name: Upload test results if: always() uses: ably/test-observability-action@v1 diff --git a/.gitignore b/.gitignore index 072dde41f2..3a3066ac2a 100644 --- a/.gitignore +++ b/.gitignore @@ -8,4 +8,5 @@ build/ react/ typedoc/generated/ junit/ +private-api-usage/ test/support/mocha_junit_reporter/build/ diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index dcf5cc5802..9aba64ebce 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -158,6 +158,30 @@ When using the test webserver `npm run test:webserver` the following test variab - `tls` - true or false to enable/disable use of TLS respectively - `log_level` - Log level for the client libraries, defaults to 2, 4 is `MICRO` +### Adding private API annotations to tests + +We have an ongoing project which aims to reuse the ably-js test suite as a unified test suite for all of our client libraries. To enable this work, we want to be able to monitor the test suite’s usage of APIs that are private to ably-js. + +When you use a private API in a test, record its usage using the `recordPrivateApi` method on the test’s helper object. For example: + +```javascript +/* Sabotage the reattach attempt, then simulate a server-sent detach */ +helper.recordPrivateApi('replace.channel.sendMessage'); +channel.sendMessage = function () {}; +``` + +The current list of private API usage identifiers can be found in [`test/common/modules/private_api_recorder.js`](test/common/modules/private_api_recorder.js); add new ones there as necessary. + +The following test files do not utilise private API annotations, and you don’t need to add them: + +- [`test/realtime/transports.test.js`](test/realtime/transports.test.js) +- [`test/browser/simple.test.js`](test/browser/simple.test.js) +- [`test/browser/http.test.js`](test/browser/http.test.js) +- [`test/browser/connection.test.js`](test/browser/connection.test.js) +- [`test/browser/modular.test.js`](test/browser/modular.test.js) +- [`test/browser/push.test.js`](test/browser/push.test.js) +- [`test/rest/bufferutils.test.js`](test/rest/bufferutils.test.js) + ## React hooks The react sample application is configured to execute using Vite - which will load a sample web app that acts as a simple test harness for the hooks. diff --git a/test/common/globals/named_dependencies.js b/test/common/globals/named_dependencies.js index d89cdbe7ad..e06d925987 100644 --- a/test/common/globals/named_dependencies.js +++ b/test/common/globals/named_dependencies.js @@ -18,5 +18,9 @@ define(function () { async: { browser: 'node_modules/async/lib/async' }, chai: { browser: 'node_modules/chai/chai', node: 'node_modules/chai/chai' }, ulid: { browser: 'node_modules/ulid/dist/index.umd', node: 'node_modules/ulid/dist/index.umd' }, + private_api_recorder: { + browser: 'test/common/modules/private_api_recorder', + node: 'test/common/modules/private_api_recorder', + }, }); }); diff --git a/test/common/modules/client_module.js b/test/common/modules/client_module.js index 5ea3c37ba6..786a0cd225 100644 --- a/test/common/modules/client_module.js +++ b/test/common/modules/client_module.js @@ -5,8 +5,11 @@ define(['ably', 'globals', 'test/common/modules/testapp_module'], function (Ably, ablyGlobals, testAppHelper) { var utils = Ably.Realtime.Utils; - function ablyClientOptions(options) { + function ablyClientOptions(helper, options) { + helper = helper.addingHelperFunction('ablyClientOptions'); + helper.recordPrivateApi('call.Utils.copy'); var clientOptions = utils.copy(ablyGlobals); + helper.recordPrivateApi('call.Utils.mixin'); utils.mixin(clientOptions, options); var authMethods = ['authUrl', 'authCallback', 'token', 'tokenDetails', 'key']; @@ -22,12 +25,14 @@ define(['ably', 'globals', 'test/common/modules/testapp_module'], function (Ably return clientOptions; } - function ablyRest(options) { - return new Ably.Rest(ablyClientOptions(options)); + function ablyRest(helper, options) { + helper = helper.addingHelperFunction('ablyRest'); + return new Ably.Rest(ablyClientOptions(helper, options)); } - function ablyRealtime(options) { - return new Ably.Realtime(ablyClientOptions(options)); + function ablyRealtime(helper, options) { + helper = helper.addingHelperFunction('ablyRealtime'); + return new Ably.Realtime(ablyClientOptions(helper, options)); } return (module.exports = { diff --git a/test/common/modules/private_api_recorder.js b/test/common/modules/private_api_recorder.js new file mode 100644 index 0000000000..ad65faf6ae --- /dev/null +++ b/test/common/modules/private_api_recorder.js @@ -0,0 +1,196 @@ +'use strict'; + +define(['test/support/output_directory_paths'], function (outputDirectoryPaths) { + const privateAPIIdentifiers = [ + 'call.BufferUtils.areBuffersEqual', + 'call.BufferUtils.base64Decode', + 'call.BufferUtils.base64Encode', + 'call.BufferUtils.hexEncode', + 'call.BufferUtils.isBuffer', + 'call.BufferUtils.toArrayBuffer', + 'call.BufferUtils.utf8Encode', + 'call.ConnectionManager.supportedTransports', + 'call.Defaults.getHost', + 'call.Defaults.getHost', + 'call.Defaults.getHosts', + 'call.Defaults.getPort', + 'call.Defaults.normaliseOptions', + 'call.EventEmitter.emit', + 'call.Message.decode', + 'call.Message.encode', + 'call.Platform.Config.push.storage.clear', + 'call.Platform.nextTick', + 'call.PresenceMessage.fromValues', + 'call.ProtocolMessage.setFlag', + 'call.Utils.copy', + 'call.Utils.getRetryTime', + 'call.Utils.inspectError', + 'call.Utils.keysArray', + 'call.Utils.mixin', + 'call.Utils.toQueryString', + 'call.auth.getAuthHeaders', + 'call.channel.checkPendingState', + 'call.channel.processMessage', + 'call.channel.requestState', + 'call.channel.sendPresence', + 'call.channel.sync', + 'call.connectionManager.activeProtocol.getTransport', + 'call.connectionManager.disconnectAllTransports', + 'call.connectionManager.notifyState', + 'call.connectionManager.onChannelMessage', + 'call.connectionManager.requestState', + 'call.connectionManager.send', + 'call.filteredSubscriptions.has', + 'call.http._getHosts', + 'call.http.checkConnectivity', + 'call.http.doUri', + 'call.msgpack.decode', + 'call.msgpack.encode', + 'call.presence._myMembers.put', + 'call.presence.waitSync', + 'call.protocolMessageFromDeserialized', + 'call.realtime.baseUri', + 'call.rest.baseUri', + 'call.rest.http.do', + 'call.restChannel._publish', + 'call.transport.onProtocolMessage', + 'call.transport.send', + 'delete.auth.authOptions.requestHeaders', + 'deserialize.recoveryKey', + 'listen.channel._allChannelChanges.attached', + 'listen.channel._allChannelChanges.update', + 'listen.connectionManager.connectiondetails', + 'listen.connectionManager.transport.active', + 'listen.connectionManager.transport.pending', + 'listen.transport.disposed', + 'new.Crypto.CipherParams', // This is in a bit of a grey area re whether it’s private API. The CipherParams class is indeed part of the public API in the spec, but it’s not clear whether you’re meant to construct one directly, and furthermore the spec doesn’t describe it as being exposed as a property on Crypto. + 'pass.clientOption.connectivityCheckUrl', // actually ably-js public API (i.e. it’s in the TypeScript typings) but no other SDK has it and it doesn’t enable ably-js-specific functionality + 'pass.clientOption.disableConnectivityCheck', // actually ably-js public API (i.e. it’s in the TypeScript typings) but no other SDK has it and it doesn’t enable ably-js-specific functionality + 'pass.clientOption.pushRecipientChannel', + 'pass.clientOption.webSocketConnectTimeout', + 'read.Defaults.protocolVersion', + 'read.Defaults.version', + 'read.EventEmitter.events', + 'read.Platform.Config.push', + 'read.Realtime._transports', + 'read.auth.authOptions.authUrl', + 'read.auth.key', + 'read.auth.method', + 'read.auth.tokenParams.version', + 'read.channel.channelOptions', + 'read.channel.channelOptions.cipher', + 'read.connectionManager.activeProtocol', + 'read.connectionManager.activeProtocol.transport', + 'read.connectionManager.baseTransport', + 'read.connectionManager.connectionId', + 'read.connectionManager.connectionId', + 'read.connectionManager.connectionStateTtl', + 'read.connectionManager.httpHosts', + 'read.connectionManager.msgSerial', + 'read.connectionManager.options', + 'read.connectionManager.options.timeouts.httpMaxRetryDuration', + 'read.connectionManager.options.timeouts.httpRequestTimeout', + 'read.connectionManager.pendingTransport', + 'read.connectionManager.queuedMessages.messages', + 'read.connectionManager.states.disconnected.retryDelay', + 'read.connectionManager.states.suspended.retryDelay', + 'read.connectionManager.webSocketTransportAvailable', + 'read.realtime.options', + 'read.realtime.options.key', + 'read.realtime.options.maxMessageSize', + 'read.realtime.options.token', + 'read.rest._currentFallback', + 'read.rest._currentFallback.host', + 'read.rest._currentFallback.validUntil', + 'read.rest.options.key', + 'read.rest.options.realtimeHost', + 'read.rest.options.token', + 'read.rest.serverTimeOffset', + 'read.transport.params.mode', + 'read.transport.recvRequest.recvUri', + 'read.transport.uri', + 'replace.channel.attachImpl', + 'replace.channel.processMessage', + 'replace.channel.sendMessage', + 'replace.channel.sendPresence', + 'replace.connectionManager.onChannelMessage', + 'replace.connectionManager.send', + 'replace.connectionManager.tryATransport', + 'replace.http.doUri', + 'replace.rest.http.do', + 'replace.rest.time', + 'replace.restChannel._publish', + 'replace.transport.onProtocolMessage', + 'replace.transport.send', + 'serialize.recoveryKey', + 'write.Defaults.ENVIRONMENT', + 'write.Platform.Config.push', // This implies using a mock implementation of the internal IPlatformPushConfig interface. Our mock (in push_channel_transport.js) then interacts with internal objects and private APIs of public objects to implement this interface; I haven’t added annotations for that private API usage, since there wasn’t an easy way to pass test context information into the mock. I think that for now we can just say that if we wanted to get rid of this private API usage, then we’d need to remove this mock entirely. + 'write.auth.authOptions.requestHeaders', + 'write.auth.key', + 'write.auth.tokenDetails.token', + 'write.channel._lastPayload', + 'write.channel.state', + 'write.connectionManager.connectionDetails.maxMessageSize', + 'write.connectionManager.connectionId', + 'write.connectionManager.connectionKey', + 'write.connectionManager.lastActivity', + 'write.connectionManager.msgSerial', + 'write.realtime.options.timeouts.realtimeRequestTimeout', + 'write.rest._currentFallback.validUntil', + ]; + + class PrivateApiRecorder { + privateAPIUsages = []; + + /** + * Creates a recording context for the current Mocha test case. + * + * @param context A description of the context for which the calls will be recorded. + */ + createContext(context) { + if (!context) { + throw new Error('No description passed to createContext'); + } + + const loggingDescription = JSON.stringify(context); + + return { + record: (privateAPIIdentifier) => { + if (privateAPIIdentifiers.indexOf(privateAPIIdentifier) == -1) { + throw new Error(`(${loggingDescription}) Recorded unknown private API: ${privateAPIIdentifier}`); + } + this.privateAPIUsages.push({ context, privateAPIIdentifier }); + }, + recordTestStart: () => { + this.privateAPIUsages.push({ context, privateAPIIdentifier: null }); + }, + }; + } + + dump() { + if (isBrowser) { + window.dispatchEvent(new CustomEvent('privateApiUsageData', { detail: this.privateAPIUsages })); + } else { + try { + const fs = require('fs'); + const path = require('path'); + + if (!fs.existsSync(outputDirectoryPaths.privateApiUsage)) { + fs.mkdirSync(outputDirectoryPaths.privateApiUsage); + } + const filename = `node-${process.version.split('.')[0]}.json`; + fs.writeFileSync( + path.join(outputDirectoryPaths.privateApiUsage, filename), + JSON.stringify(this.privateAPIUsages), + { encoding: 'utf-8' }, + ); + } catch (err) { + console.log('Failed to write private API usage data: ', err); + throw err; + } + } + } + } + + return (module.exports = new PrivateApiRecorder()); +}); diff --git a/test/common/modules/shared_helper.js b/test/common/modules/shared_helper.js index 7f50db8bba..e13ec6bcdb 100644 --- a/test/common/modules/shared_helper.js +++ b/test/common/modules/shared_helper.js @@ -10,7 +10,8 @@ define([ 'globals', 'async', 'chai', -], function (testAppModule, clientModule, testAppManager, globals, async, chai) { + 'private_api_recorder', +], function (testAppModule, clientModule, testAppManager, globals, async, chai, privateApiRecorder) { var utils = clientModule.Ably.Realtime.Utils; var platform = clientModule.Ably.Realtime.Platform; var BufferUtils = platform.BufferUtils; @@ -20,14 +21,9 @@ define([ var unroutableAddress = 'http://' + unroutableHost + '/'; class SharedHelper { - setupApp = testAppModule.setup; - tearDownApp = testAppModule.tearDown; - createStats = testAppModule.createStatsFixtureData; getTestApp = testAppModule.getTestApp; Ably = clientModule.Ably; - AblyRest = clientModule.AblyRest; - ablyClientOptions = clientModule.ablyClientOptions; Utils = utils; loadTestData = testAppManager.loadJsonData; @@ -42,30 +38,123 @@ define([ throw new Error('SharedHelper created without context'); } this.context = context; + this.privateApiContext = privateApiRecorder.createContext(context); + } + + addingHelperFunction(helperFunctionName) { + return new SharedHelper({ ...this.context, helperStack: [helperFunctionName, ...this.context.helperStack] }); + } + + withParameterisedTestTitle(title) { + return new SharedHelper({ ...this.context, parameterisedTestTitle: title }); + } + + static createContext(data) { + return { + ...data, + helperStack: [], + }; + } + + static extractSuiteHierarchy(suite) { + const hierarchy = []; + while (suite.title) { + hierarchy.unshift(suite.title); + suite = suite.parent; + } + return hierarchy; } static forTest(thisInBeforeEach) { - return new this(thisInBeforeEach.currentTest.fullTitle()); + const context = { + type: 'test', + file: thisInBeforeEach.currentTest.file, + suite: this.extractSuiteHierarchy(thisInBeforeEach.currentTest.parent), + title: thisInBeforeEach.currentTest.title, + parameterisedTestTitle: null, + }; + + return new this(this.createContext(context)); } static forHook(thisInHook) { - return new this(thisInHook.test.fullTitle()); + /** + * Based on what I’ve observed (haven’t found good documentation about it), the value of `this` in a hook varies: + * + * - `before` hook in a test file: Context, with properties: + * - test: Hook + * + * - `beforeEach` hook in a test file: Suite, which has a `ctx`: Context property with properties: + * - test: Hook + * + * - global `after` hook: Context, with properties: + * - test: Hook + * + * For the test file hooks, the Hook has a `file` property, but for some reason the root hook doesn’t (i.e. doesn’t indicate which file the hook lives in) + */ + const mochaContext = 'ctx' in thisInHook ? thisInHook.ctx : thisInHook; + const mochaHook = mochaContext.test; + + const context = { + type: 'hook', + title: mochaHook.originalTitle, + }; + + if (!mochaHook.parent.root) { + // For some reason the root hook doesn’t contain file information + context.file = mochaHook.file; + context.suite = this.extractSuiteHierarchy(mochaHook.parent); + } else { + context.file = null; + context.suite = null; + } + + return new this(this.createContext(context)); } static forTestDefinition(thisInDescribe, label) { if (!label) { throw new Error('SharedHelper.forTestDefinition called without label'); } - return new this(`${thisInDescribe.title} (defining ${label})`); + + const context = { + type: 'definition', + file: thisInDescribe.file, + suite: this.extractSuiteHierarchy(thisInDescribe), + label, + }; + + return new this(this.createContext(context)); + } + + recordTestStart() { + this.privateApiContext.recordTestStart(); + } + + recordPrivateApi(identifier) { + this.privateApiContext.record(identifier); } get availableTransports() { + const helper = this.addingHelperFunction('availableTransports'); + return helper._availableTransports; + } + + get _availableTransports() { + this.recordPrivateApi('call.Utils.keysArray'); + this.recordPrivateApi('call.ConnectionManager.supportedTransports'); + this.recordPrivateApi('read.Realtime._transports'); return utils.keysArray( clientModule.Ably.Realtime.ConnectionManager.supportedTransports(clientModule.Ably.Realtime._transports), ); } get bestTransport() { + const helper = this.addingHelperFunction('bestTransport'); + return helper._bestTransport; + } + + get _bestTransport() { return this.availableTransports[0]; } @@ -99,6 +188,11 @@ define([ } closeAndFinish(done, realtime, err) { + const helper = this.addingHelperFunction('closeAndFinish'); + helper._closeAndFinish(done, realtime, err); + } + + _closeAndFinish(done, realtime, err) { if (typeof realtime === 'undefined') { // Likely called in a catch block for an exception // that occured before realtime was initialized @@ -118,6 +212,11 @@ define([ } async closeAndFinishAsync(realtime, err) { + const helper = this.addingHelperFunction('closeAndFinishAsync'); + return helper._closeAndFinishAsync(realtime, err); + } + + async _closeAndFinishAsync(realtime, err) { return new Promise((resolve, reject) => { this.closeAndFinish((err) => (err ? reject(err) : resolve()), realtime, err); }); @@ -137,18 +236,34 @@ define([ } simulateDroppedConnection(realtime) { + const helper = this.addingHelperFunction('simulateDroppedConnection'); + helper._simulateDroppedConnection(realtime); + } + + _simulateDroppedConnection(realtime) { + const self = this; // Go into the 'disconnected' state before actually disconnecting the transports // to avoid the instantaneous reconnect attempt that would be triggered in // notifyState by the active transport getting disconnected from a connected state realtime.connection.once('disconnected', function () { + self.recordPrivateApi('call.connectionManager.disconnectAllTransports'); realtime.connection.connectionManager.disconnectAllTransports(); }); + this.recordPrivateApi('call.connectionManager.requestState'); realtime.connection.connectionManager.requestState({ state: 'disconnected' }); } becomeSuspended(realtime, cb) { + const helper = this.addingHelperFunction('becomeSuspended'); + helper._becomeSuspended(realtime, cb); + } + + _becomeSuspended(realtime, cb) { + this.recordPrivateApi('call.connectionManager.disconnectAllTransports'); realtime.connection.connectionManager.disconnectAllTransports(); + const self = this; realtime.connection.once('disconnected', function () { + self.recordPrivateApi('call.connectionManager.notifyState'); realtime.connection.connectionManager.notifyState({ state: 'suspended' }); }); if (cb) @@ -158,25 +273,40 @@ define([ } callbackOnClose(realtime, callback) { + const helper = this.addingHelperFunction('callbackOnClose'); + helper._callbackOnClose(realtime, callback); + } + + _callbackOnClose(realtime, callback) { + this.recordPrivateApi('read.connectionManager.activeProtocol'); if (!realtime.connection.connectionManager.activeProtocol) { + this.recordPrivateApi('call.Platform.nextTick'); platform.Config.nextTick(function () { realtime.close(); callback(); }); return; } + this.recordPrivateApi('read.connectionManager.activeProtocol.transport'); + this.recordPrivateApi('listen.transport.disposed'); realtime.connection.connectionManager.activeProtocol.transport.on('disposed', function () { callback(); }); /* wait a tick before closing in order to avoid the final close * happening synchronously in a publish/attach callback, which * complicates channelattach_publish_invalid etc. */ + this.recordPrivateApi('call.Platform.nextTick'); platform.Config.nextTick(function () { realtime.close(); }); } closeAndFinishSeveral(done, realtimeArray, e) { + const helper = this.addingHelperFunction('closeAndFinishSeveral'); + helper._closeAndFinishSeveral(done, realtimeArray, e); + } + + _closeAndFinishSeveral(done, realtimeArray, e) { async.map( realtimeArray, (realtime, mapCb) => { @@ -201,34 +331,46 @@ define([ /* testFn is assumed to be a function of realtimeOptions that returns a mocha test */ static testOnAllTransports(thisInDescribe, name, testFn, skip) { - const helper = this.forTestDefinition(thisInDescribe, name); + const helper = this.forTestDefinition(thisInDescribe, name).addingHelperFunction('testOnAllTransports'); var itFn = skip ? it.skip : it; + + function createTest(options) { + return function (done) { + this.test.helper = this.test.helper.withParameterisedTestTitle(name); + return testFn(options).apply(this, [done]); + }; + } + let transports = helper.availableTransports; transports.forEach(function (transport) { itFn( name + '_with_' + transport + '_binary_transport', - testFn({ transports: [transport], useBinaryProtocol: true }), + createTest({ transports: [transport], useBinaryProtocol: true }), ); itFn( name + '_with_' + transport + '_text_transport', - testFn({ transports: [transport], useBinaryProtocol: false }), + createTest({ transports: [transport], useBinaryProtocol: false }), ); }); /* Plus one for no transport specified (ie use websocket/base mechanism if * present). (we explicitly specify all transports since node only does * websocket+nodecomet if comet is explicitly requested) * */ - itFn(name + '_with_binary_transport', testFn({ transports, useBinaryProtocol: true })); - itFn(name + '_with_text_transport', testFn({ transports, useBinaryProtocol: false })); + itFn(name + '_with_binary_transport', createTest({ transports, useBinaryProtocol: true })); + itFn(name + '_with_text_transport', createTest({ transports, useBinaryProtocol: false })); } static restTestOnJsonMsgpack(name, testFn, skip) { var itFn = skip ? it.skip : it; itFn(name + ' with binary protocol', async function () { - await testFn(new clientModule.AblyRest({ useBinaryProtocol: true }), name + '_binary', this.test.helper); + const helper = this.test.helper.withParameterisedTestTitle(name); + + await testFn(new clientModule.AblyRest(helper, { useBinaryProtocol: true }), name + '_binary', helper); }); itFn(name + ' with text protocol', async function () { - await testFn(new clientModule.AblyRest({ useBinaryProtocol: false }), name + '_text', this.test.helper); + const helper = this.test.helper.withParameterisedTestTitle(name); + + await testFn(new clientModule.AblyRest(helper, { useBinaryProtocol: false }), name + '_text', helper); }); } @@ -251,6 +393,11 @@ define([ } testMessageEquality(one, two) { + const helper = this.addingHelperFunction('testMessageEquality'); + return helper._testMessageEquality(one, two); + } + + _testMessageEquality(one, two) { // treat `null` same as `undefined` (using ==, rather than ===) expect(one.encoding == two.encoding, "Encoding mismatch ('" + one.encoding + "' != '" + two.encoding + "').").to .be.ok; @@ -260,7 +407,9 @@ define([ return; } + this.recordPrivateApi('call.BufferUtils.isBuffer'); if (BufferUtils.isBuffer(one.data) && BufferUtils.isBuffer(two.data)) { + this.recordPrivateApi('call.BufferUtils.areBuffersEqual'); expect(BufferUtils.areBuffersEqual(one.data, two.data), 'Buffer data contents mismatch.').to.equal(true); return; } @@ -274,10 +423,18 @@ define([ expect(json1 === json2, 'JSON data contents mismatch.').to.be.ok; } + ablyClientOptions(options) { + return clientModule.ablyClientOptions(this, options); + } + + AblyRest(options) { + return clientModule.AblyRest(this, options); + } + static activeClients = []; AblyRealtime(options) { - const client = clientModule.AblyRealtime(options); + const client = clientModule.AblyRealtime(this, options); SharedHelper.activeClients.push(client); return client; } @@ -305,6 +462,22 @@ define([ } } } + + createStats(app, statsData, callback) { + testAppModule.createStatsFixtureData(this, app, statsData, callback); + } + + setupApp(forceSetup, done) { + return testAppModule.setup(this, forceSetup, done); + } + + tearDownApp(app, callback) { + testAppModule.tearDown(this, app, callback); + } + + dumpPrivateApiUsage() { + privateApiRecorder.dump(); + } } SharedHelper.testOnAllTransports.skip = function (thisInDescribe, name, testFn) { diff --git a/test/common/modules/testapp_manager.js b/test/common/modules/testapp_manager.js index d69fcb9043..0f4b7fa9dc 100644 --- a/test/common/modules/testapp_manager.js +++ b/test/common/modules/testapp_manager.js @@ -31,9 +31,12 @@ define(['globals', 'ably'], function (ablyGlobals, ably) { } } - function toBase64(str) { + function toBase64(helper, str) { + helper = helper.addingHelperFunction('toBase64'); var bufferUtils = ably.Realtime.Platform.BufferUtils; + helper.recordPrivateApi('call.BufferUtils.utf8Encode'); var buffer = bufferUtils.utf8Encode(str); + helper.recordPrivateApi('call.BufferUtils.base64Encode'); return bufferUtils.base64Encode(buffer); } @@ -164,11 +167,12 @@ define(['globals', 'ably'], function (ablyGlobals, ably) { }); } - function createStatsFixtureData(app, statsData, callback) { + function createStatsFixtureData(helper, app, statsData, callback) { + helper = helper.addingHelperFunction('createStatsFixtureData'); var postData = JSON.stringify(statsData); var authKey = app.keys[0].keyStr; - var authHeader = toBase64(authKey); + var authHeader = toBase64(helper, authKey); var postOptions = { host: restHost, @@ -195,9 +199,10 @@ define(['globals', 'ably'], function (ablyGlobals, ably) { }); } - function deleteApp(app, callback) { + function deleteApp(helper, app, callback) { + helper = helper.addingHelperFunction('deleteApp'); var authKey = app.keys[0].keyStr, - authHeader = toBase64(authKey); + authHeader = toBase64(helper, authKey); var delOptions = { host: restHost, diff --git a/test/common/modules/testapp_module.js b/test/common/modules/testapp_module.js index 3e2e7db1b0..70b7d36d08 100644 --- a/test/common/modules/testapp_module.js +++ b/test/common/modules/testapp_module.js @@ -20,7 +20,7 @@ define(['test/common/modules/testapp_manager', 'globals'], function (testAppMana then no need to recreate a test app, unless forceSetup is true. If forceSetup is true and existing app exists, then the app will be torn down (deleted) first */ - function setup(forceSetup, done) { + function setup(helper, forceSetup, done) { if (typeof forceSetup === 'function') { done = forceSetup; forceSetup = false; @@ -48,7 +48,7 @@ define(['test/common/modules/testapp_manager', 'globals'], function (testAppMana }; if (configuredTestApp() && forceSetup) { - tearDown(function (err) { + tearDown(helper, function (err) { if (err) { done(err); } else { @@ -63,13 +63,13 @@ define(['test/common/modules/testapp_manager', 'globals'], function (testAppMana /* tearDown is typically called by an afterAll test suite block. If tearDown is called more than once i.e. across multiple suites, and other test suites are still to run, then we must not yet delete the test app */ - function tearDown(done) { + function tearDown(helper, done) { if (!configuredTestApp()) { done(); return; } - testAppManager.tearDown(configuredTestApp(), function (err) { + testAppManager.tearDown(helper, configuredTestApp(), function (err) { if (err) { done(new Error('Could not tear down Test App: ' + JSON.stringify(err))); } else { diff --git a/test/realtime/auth.test.js b/test/realtime/auth.test.js index 61b3390fc2..3e46b3f149 100644 --- a/test/realtime/auth.test.js +++ b/test/realtime/auth.test.js @@ -13,8 +13,10 @@ define(['ably', 'shared_helper', 'async', 'chai'], function (Ably, Helper, async /* * Helper function to fetch JWT tokens from the echo server */ - function getJWT(params, callback) { + function getJWT(params, helper, callback) { + helper = helper.addingHelperFunction('getJWT'); var authUrl = echoServer + '/createJWT'; + helper.recordPrivateApi('call.http.doUri'); Helper.whenPromiseSettles(http.doUri('get', authUrl, null, null, params), function (err, result) { if (result.error) { callback(result.error, null); @@ -200,6 +202,7 @@ define(['ably', 'shared_helper', 'async', 'chai'], function (Ably, Helper, async realtime.connection.on('connected', function () { try { + helper.recordPrivateApi('read.auth.method'); expect(realtime.auth.method).to.equal('token'); helper.closeAndFinish(done, realtime); } catch (err) { @@ -244,6 +247,7 @@ define(['ably', 'shared_helper', 'async', 'chai'], function (Ably, Helper, async realtime.connection.on('connected', function () { try { + helper.recordPrivateApi('read.auth.method'); expect(realtime.auth.method).to.equal('token'); helper.closeAndFinish(done, realtime); } catch (err) { @@ -285,6 +289,7 @@ define(['ably', 'shared_helper', 'async', 'chai'], function (Ably, Helper, async realtime.connection.on('connected', function () { try { + helper.recordPrivateApi('read.auth.method'); expect(realtime.auth.method).to.equal('token'); helper.closeAndFinish(done, realtime); } catch (err) { @@ -325,6 +330,7 @@ define(['ably', 'shared_helper', 'async', 'chai'], function (Ably, Helper, async keyName: tokenRequest.keyName, mac: tokenRequest.mac, }; + helper.recordPrivateApi('call.Utils.toQueryString'); var authPath = echoServer + '/qs_to_body' + helper.Utils.toQueryString(lowerPrecedenceTokenRequestParts); realtime = helper.AblyRealtime({ authUrl: authPath, authParams: higherPrecedenceTokenRequestParts }); @@ -779,6 +785,7 @@ define(['ably', 'shared_helper', 'async', 'chai'], function (Ably, Helper, async done(err); return; } + helper.recordPrivateApi('call.Utils.mixin'); clientRealtime = helper.AblyRealtime( helper.Utils.mixin(realtimeOpts, { tokenDetails: tokenDetails, queryTime: true }), ); @@ -817,12 +824,14 @@ define(['ably', 'shared_helper', 'async', 'chai'], function (Ably, Helper, async originalTime = rest.time; /* stub time */ + helper.recordPrivateApi('replace.rest.time'); rest.time = async function () { timeRequestCount += 1; return originalTime.call(rest); }; try { + helper.recordPrivateApi('read.rest.serverTimeOffset'); expect( isNaN(parseInt(rest.serverTimeOffset)) && !rest.serverTimeOffset, 'Server time offset is empty and falsey until a time request has been made', @@ -839,6 +848,7 @@ define(['ably', 'shared_helper', 'async', 'chai'], function (Ably, Helper, async if (err) { return callback(err); } + helper.recordPrivateApi('read.rest.serverTimeOffset'); expect( !isNaN(parseInt(rest.serverTimeOffset)), 'Server time offset is configured when time is requested', @@ -888,6 +898,7 @@ define(['ably', 'shared_helper', 'async', 'chai'], function (Ably, Helper, async }); }; + helper.recordPrivateApi('call.Utils.mixin'); realtime = helper.AblyRealtime( helper.Utils.mixin(realtimeOpts, { authCallback: authCallback, clientId: clientId }), ); @@ -936,6 +947,7 @@ define(['ably', 'shared_helper', 'async', 'chai'], function (Ably, Helper, async }); }; + helper.recordPrivateApi('call.Utils.mixin'); realtime = helper.AblyRealtime( helper.Utils.mixin(realtimeOpts, { authCallback: authCallback, clientId: clientId }), ); @@ -978,6 +990,7 @@ define(['ably', 'shared_helper', 'async', 'chai'], function (Ably, Helper, async helper.closeAndFinish(done, realtime, err); return; } + helper.recordPrivateApi('call.Utils.mixin'); realtime = helper.AblyRealtime( helper.Utils.mixin(realtimeOpts, { token: tokenDetails.token, clientId: clientId }), ); @@ -1026,6 +1039,7 @@ define(['ably', 'shared_helper', 'async', 'chai'], function (Ably, Helper, async return; } setTimeout(function () { + helper.recordPrivateApi('call.Utils.mixin'); realtime = helper.AblyRealtime( helper.Utils.mixin(realtimeOpts, { token: tokenDetails.token, clientId: clientId }), ); @@ -1080,6 +1094,7 @@ define(['ably', 'shared_helper', 'async', 'chai'], function (Ably, Helper, async }); }; + helper.recordPrivateApi('call.Utils.mixin'); realtime = helper.AblyRealtime(helper.Utils.mixin(realtimeOpts, { authCallback: authCallback })); realtime.connection.once('connected', function () { var channel = realtime.channels.get('right'); @@ -1126,6 +1141,9 @@ define(['ably', 'shared_helper', 'async', 'chai'], function (Ably, Helper, async }); try { + helper.recordPrivateApi('read.auth.tokenParams.version'); + helper.recordPrivateApi('read.auth.authOptions.authUrl'); + expect(realtime.auth.tokenParams.version).to.equal(1, 'Check initial defaultTokenParams stored'); expect(realtime.auth.tokenDetails.token).to.equal('1', 'Check initial token stored'); expect(realtime.auth.authOptions.authUrl).to.equal('1', 'Check initial authUrl stored'); @@ -1166,8 +1184,10 @@ define(['ably', 'shared_helper', 'async', 'chai'], function (Ably, Helper, async realtime = helper.AblyRealtime({ authCallback: authCallback, transports: [helper.bestTransport] }); realtime.connection.once('connected', function () { + helper.recordPrivateApi('read.connectionManager.activeProtocol.transport'); var transport = realtime.connection.connectionManager.activeProtocol.transport, originalSend = transport.send; + helper.recordPrivateApi('replace.transport.send'); /* Spy on transport.send to detect the outgoing AUTH */ transport.send = function (message) { if (message.action === 17) { @@ -1178,10 +1198,12 @@ define(['ably', 'shared_helper', 'async', 'chai'], function (Ably, Helper, async helper.closeAndFinish(done, realtime, err); } } else { + helper.recordPrivateApi('call.transport.send'); originalSend.call(this, message); } }; /* Inject a fake AUTH from realtime */ + helper.recordPrivateApi('call.transport.onProtocolMessage'); transport.onProtocolMessage({ action: 17 }); }); }); @@ -1198,9 +1220,10 @@ define(['ably', 'shared_helper', 'async', 'chai'], function (Ably, Helper, async var currentKey = helper.getTestApp().keys[0]; var keys = { keyName: currentKey.keyName, keySecret: currentKey.keySecret }; var clientId = 'testJWTClientId'; + helper.recordPrivateApi('call.Utils.mixin'); var params = helper.Utils.mixin(keys, { clientId: clientId }); var authCallback = function (tokenParams, callback) { - getJWT(params, callback); + getJWT(params, helper, callback); }; var realtime = helper.AblyRealtime({ authCallback: authCallback }); @@ -1237,9 +1260,10 @@ define(['ably', 'shared_helper', 'async', 'chai'], function (Ably, Helper, async var currentKey = helper.getTestApp().keys[0]; var keys = { keyName: currentKey.keyName, keySecret: currentKey.keySecret, returnType: 'jwt' }; var clientId = 'testJWTClientId'; + helper.recordPrivateApi('call.Utils.mixin'); var params = helper.Utils.mixin(keys, { clientId: clientId }); var authCallback = function (tokenParams, callback) { - getJWT(params, callback); + getJWT(params, helper, callback); }; var realtime = helper.AblyRealtime({ authCallback: authCallback }); @@ -1273,7 +1297,7 @@ define(['ably', 'shared_helper', 'async', 'chai'], function (Ably, Helper, async var currentKey = helper.getTestApp().keys[3]; // get subscribe-only keys { "*":["subscribe"] } var params = { keyName: currentKey.keyName, keySecret: currentKey.keySecret }; var authCallback = function (tokenParams, callback) { - getJWT(params, callback); + getJWT(params, helper, callback); }; var realtime = helper.AblyRealtime({ authCallback: authCallback }); @@ -1303,7 +1327,7 @@ define(['ably', 'shared_helper', 'async', 'chai'], function (Ably, Helper, async var currentKey = helper.getTestApp().keys[0]; var params = { keyName: currentKey.keyName, keySecret: currentKey.keySecret }; var authCallback = function (tokenParams, callback) { - getJWT(params, callback); + getJWT(params, helper, callback); }; var publishEvent = 'publishEvent', @@ -1335,7 +1359,7 @@ define(['ably', 'shared_helper', 'async', 'chai'], function (Ably, Helper, async var currentKey = helper.getTestApp().keys[0]; var params = { keyName: currentKey.keyName, keySecret: currentKey.keySecret, expiresIn: 5 }; var authCallback = function (tokenParams, callback) { - getJWT(params, callback); + getJWT(params, helper, callback); }; var realtime = helper.AblyRealtime({ authCallback: authCallback }); @@ -1366,7 +1390,7 @@ define(['ably', 'shared_helper', 'async', 'chai'], function (Ably, Helper, async // We create a token that lasts 35 so there's room to receive the update event message. var params = { keyName: currentKey.keyName, keySecret: currentKey.keySecret, expiresIn: 35 }; var authCallback = function (tokenParams, callback) { - getJWT(params, callback); + getJWT(params, helper, callback); }; var realtime = helper.AblyRealtime({ authCallback: authCallback }); @@ -1394,7 +1418,7 @@ define(['ably', 'shared_helper', 'async', 'chai'], function (Ably, Helper, async const helper = this.test.helper; var currentKey = helper.getTestApp().keys[0]; var params = { keyName: currentKey.keyName, keySecret: currentKey.keySecret }; - getJWT(params, function (err, token) { + getJWT(params, helper, function (err, token) { if (err) { done(err); return; diff --git a/test/realtime/channel.test.js b/test/realtime/channel.test.js index bfa7534eef..e028b59e32 100644 --- a/test/realtime/channel.test.js +++ b/test/realtime/channel.test.js @@ -177,6 +177,7 @@ define(['ably', 'shared_helper', 'async', 'chai'], function (Ably, Helper, async var realtime = helper.AblyRealtime(realtimeOpts); realtime.connection.on('connected', function () { try { + helper.recordPrivateApi('read.channel.channelOptions'); /* set options on init */ var channel0 = realtime.channels.get('channelinit0', { fakeOption: true }); expect(channel0.channelOptions.fakeOption).to.equal(true); @@ -558,6 +559,7 @@ define(['ably', 'shared_helper', 'async', 'chai'], function (Ably, Helper, async return; } try { + helper.recordPrivateApi('read.channel.channelOptions'); expect(channel.channelOptions).to.deep.equal(channelOptions, 'Check requested channel options'); expect(channel.params).to.deep.equal(params, 'Check result params'); expect(channel.modes).to.deep.equal(['subscribe'], 'Check result modes'); @@ -620,6 +622,7 @@ define(['ably', 'shared_helper', 'async', 'chai'], function (Ably, Helper, async helper.closeAndFinish(done, realtime, err); return; } + helper.recordPrivateApi('read.channel.channelOptions'); expect(channel.channelOptions).to.deep.equal(channelOptions, 'Check requested channel options'); expect(channel.params).to.deep.equal(params, 'Check result params'); expect(channel.modes).to.deep.equal(['subscribe'], 'Check result modes'); @@ -750,6 +753,7 @@ define(['ably', 'shared_helper', 'async', 'chai'], function (Ably, Helper, async }, function (cb) { var channelUpdated = false; + helper.recordPrivateApi('listen.channel._allChannelChanges.update'); channel._allChannelChanges.on(['update'], function () { channelUpdated = true; }); @@ -763,6 +767,7 @@ define(['ably', 'shared_helper', 'async', 'chai'], function (Ably, Helper, async function () { /* Wait a tick so we don' depend on whether the update event runs the * channelUpdated listener or the setOptions listener first */ + helper.recordPrivateApi('call.Platform.nextTick'); Ably.Realtime.Platform.Config.nextTick(function () { expect( channelUpdated, @@ -775,6 +780,8 @@ define(['ably', 'shared_helper', 'async', 'chai'], function (Ably, Helper, async }, function (cb) { var channelUpdated = false; + helper.recordPrivateApi('listen.channel._allChannelChanges.update'); + helper.recordPrivateApi('listen.channel._allChannelChanges.attached'); channel._allChannelChanges.on(['attached', 'update'], function () { channelUpdated = true; }); @@ -784,6 +791,7 @@ define(['ably', 'shared_helper', 'async', 'chai'], function (Ably, Helper, async modes: ['subscribe'], }), function () { + helper.recordPrivateApi('call.Platform.nextTick'); Ably.Realtime.Platform.Config.nextTick(function () { expect(channelUpdated, 'Check channel went to the server to update the channel mode').to.be.ok; cb(); @@ -830,6 +838,7 @@ define(['ably', 'shared_helper', 'async', 'chai'], function (Ably, Helper, async return; } try { + helper.recordPrivateApi('read.channel.channelOptions'); expect(channel.channelOptions).to.deep.equal(channelOptions, 'Check requested channel options'); expect(channel.params).to.deep.equal(params, 'Check result params'); expect(channel.modes).to.deep.equal(paramsModes, 'Check result modes'); @@ -888,6 +897,7 @@ define(['ably', 'shared_helper', 'async', 'chai'], function (Ably, Helper, async return; } try { + helper.recordPrivateApi('read.channel.channelOptions'); expect(channel.channelOptions).to.deep.equal(channelOptions, 'Check requested channel options'); expect(channel.modes).to.deep.equal(modes, 'Check result modes'); } catch (err) { @@ -948,6 +958,7 @@ define(['ably', 'shared_helper', 'async', 'chai'], function (Ably, Helper, async return; } try { + helper.recordPrivateApi('read.channel.channelOptions'); expect(channel.channelOptions).to.deep.equal(channelOptions, 'Check requested channel options'); expect(channel.params).to.deep.equal({ delta: 'vcdiff' }, 'Check result params'); expect(channel.modes).to.deep.equal(modes, 'Check result modes'); @@ -1239,14 +1250,19 @@ define(['ably', 'shared_helper', 'async', 'chai'], function (Ably, Helper, async }, function (cb) { /* Sabotage the reattach attempt, then simulate a server-sent detach */ + helper.recordPrivateApi('replace.channel.sendMessage'); channel.sendMessage = function () {}; + helper.recordPrivateApi('write.realtime.options.timeouts.realtimeRequestTimeout'); realtime.options.timeouts.realtimeRequestTimeout = 100; channel.once(function (stateChange) { expect(stateChange.current).to.equal('attaching', 'Channel reattach attempt happens immediately'); expect(stateChange.reason.code).to.equal(50000, 'check error is propogated in the reason'); cb(); }); + helper.recordPrivateApi('call.connectionManager.activeProtocol.getTransport'); var transport = realtime.connection.connectionManager.activeProtocol.getTransport(); + helper.recordPrivateApi('call.protocolMessageFromDeserialized'); + helper.recordPrivateApi('call.transport.onProtocolMessage'); transport.onProtocolMessage( createPM({ action: 13, @@ -1282,8 +1298,10 @@ define(['ably', 'shared_helper', 'async', 'chai'], function (Ably, Helper, async channel = realtime.channels.get(channelName); realtime.connection.once('connected', function () { + helper.recordPrivateApi('call.connectionManager.activeProtocol.getTransport'); var transport = realtime.connection.connectionManager.activeProtocol.getTransport(); /* Mock sendMessage to respond to attaches with a DETACHED */ + helper.recordPrivateApi('replace.channel.sendMessage'); channel.sendMessage = function (msg) { try { expect(msg.action).to.equal(10, 'check attach action'); @@ -1291,7 +1309,10 @@ define(['ably', 'shared_helper', 'async', 'chai'], function (Ably, Helper, async helper.closeAndFinish(done, realtime, err); return; } + helper.recordPrivateApi('call.Platform.nextTick'); Ably.Realtime.Platform.Config.nextTick(function () { + helper.recordPrivateApi('call.protocolMessageFromDeserialized'); + helper.recordPrivateApi('call.transport.onProtocolMessage'); transport.onProtocolMessage( createPM({ action: 13, @@ -1339,7 +1360,10 @@ define(['ably', 'shared_helper', 'async', 'chai'], function (Ably, Helper, async helper.closeAndFinish(done, realtime, err); } }); + helper.recordPrivateApi('call.connectionManager.activeProtocol.getTransport'); var transport = realtime.connection.connectionManager.activeProtocol.getTransport(); + helper.recordPrivateApi('call.protocolMessageFromDeserialized'); + helper.recordPrivateApi('call.transport.onProtocolMessage'); transport.onProtocolMessage( createPM({ action: 9, @@ -1384,7 +1408,10 @@ define(['ably', 'shared_helper', 'async', 'chai'], function (Ably, Helper, async expect(channel.state).to.equal('attached', 'check channel still attached'); cb(); }); + helper.recordPrivateApi('call.connectionManager.activeProtocol.getTransport'); var transport = realtime.connection.connectionManager.activeProtocol.getTransport(); + helper.recordPrivateApi('call.protocolMessageFromDeserialized'); + helper.recordPrivateApi('call.transport.onProtocolMessage'); transport.onProtocolMessage( createPM({ action: 11, @@ -1439,6 +1466,7 @@ define(['ably', 'shared_helper', 'async', 'chai'], function (Ably, Helper, async channel = realtime.channels.get(channelName); /* Stub out the channel's ability to communicate */ + helper.recordPrivateApi('replace.channel.sendMessage'); channel.sendMessage = function () {}; async.series( @@ -1458,6 +1486,7 @@ define(['ably', 'shared_helper', 'async', 'chai'], function (Ably, Helper, async }, function (cb) { /* nexttick so that it doesn't pick up the suspended event */ + helper.recordPrivateApi('call.Platform.nextTick'); Ably.Realtime.Platform.Config.nextTick(function () { channel.once(function (stateChange) { expect(stateChange.current).to.equal('attaching', 'Check channel tries again after a bit'); @@ -1504,13 +1533,16 @@ define(['ably', 'shared_helper', 'async', 'chai'], function (Ably, Helper, async /* Have the connection go into the suspended state, and check that the * channel goes into the suspended state and doesn't try to reattach * until the connection reconnects */ + helper.recordPrivateApi('replace.channel.sendMessage'); channel.sendMessage = function (msg) { expect(false, 'Channel tried to send a message ' + JSON.stringify(msg)).to.be.ok; }; + helper.recordPrivateApi('write.realtime.options.timeouts.realtimeRequestTimeout'); realtime.options.timeouts.realtimeRequestTimeout = 2000; helper.becomeSuspended(realtime, function () { /* nextTick as connection event is emitted before channel state is changed */ + helper.recordPrivateApi('call.Platform.nextTick'); Ably.Realtime.Platform.Config.nextTick(function () { expect(channel.state).to.equal('suspended', 'check channel state is suspended'); cb(); @@ -1521,6 +1553,7 @@ define(['ably', 'shared_helper', 'async', 'chai'], function (Ably, Helper, async realtime.connection.once(function (stateChange) { expect(stateChange.current).to.equal('connecting', 'Check we try to connect again'); /* We no longer want to fail the test for an attach, but still want to sabotage it */ + helper.recordPrivateApi('replace.channel.sendMessage'); channel.sendMessage = function () {}; cb(); }); @@ -1569,6 +1602,7 @@ define(['ably', 'shared_helper', 'async', 'chai'], function (Ably, Helper, async /* Sabotage the detach attempt, detach, then simulate a server-sent attached while * the detach is ongoing. Expect to see the library reassert the detach */ let detachCount = 0; + helper.recordPrivateApi('replace.channel.sendMessage'); channel.sendMessage = function (msg) { expect(msg.action).to.equal(12, 'Check we only see a detach. No attaches!'); expect(channel.state).to.equal('detaching', 'Check still in detaching state after both detaches'); @@ -1580,7 +1614,10 @@ define(['ably', 'shared_helper', 'async', 'chai'], function (Ably, Helper, async }; channel.detach(); setTimeout(function () { + helper.recordPrivateApi('call.connectionManager.activeProtocol.getTransport'); var transport = realtime.connection.connectionManager.activeProtocol.getTransport(); + helper.recordPrivateApi('call.protocolMessageFromDeserialized'); + helper.recordPrivateApi('call.transport.onProtocolMessage'); transport.onProtocolMessage(createPM({ action: 11, channel: channelName })); }, 0); }, diff --git a/test/realtime/connection.test.js b/test/realtime/connection.test.js index eae128a33d..5a6b7952c3 100644 --- a/test/realtime/connection.test.js +++ b/test/realtime/connection.test.js @@ -75,8 +75,10 @@ define(['ably', 'shared_helper', 'async', 'chai'], function (Ably, Helper, async realtime = helper.AblyRealtime(); realtime.connection.on('connected', function () { try { + helper.recordPrivateApi('deserialize.recoveryKey'); const recoveryContext = JSON.parse(realtime.connection.recoveryKey); expect(recoveryContext.connectionKey).to.equal(realtime.connection.key); + helper.recordPrivateApi('read.connectionManager.msgSerial'); expect(recoveryContext.msgSerial).to.equal(realtime.connection.connectionManager.msgSerial); } catch (err) { helper.closeAndFinish(done, realtime, err); @@ -94,8 +96,10 @@ define(['ably', 'shared_helper', 'async', 'chai'], function (Ably, Helper, async function (cb) { channel.subscribe(function () { setTimeout(function () { + helper.recordPrivateApi('deserialize.recoveryKey'); const recoveryContext = JSON.parse(realtime.connection.recoveryKey); expect(recoveryContext.connectionKey).to.equal(realtime.connection.key); + helper.recordPrivateApi('read.connectionManager.msgSerial'); expect(recoveryContext.msgSerial).to.equal(realtime.connection.connectionManager.msgSerial); cb(); }, 0); @@ -133,6 +137,7 @@ define(['ably', 'shared_helper', 'async', 'chai'], function (Ably, Helper, async it('unrecoverableConnection', function (done) { const helper = this.test.helper; var realtime; + helper.recordPrivateApi('serialize.recoveryKey'); const fakeRecoveryKey = JSON.stringify({ connectionKey: '_____!ablyjs_test_fake-key____', msgSerial: 3, @@ -150,6 +155,7 @@ define(['ably', 'shared_helper', 'async', 'chai'], function (Ably, Helper, async 80018, 'verify unrecoverable-connection error set in connection.errorReason', ); + helper.recordPrivateApi('read.connectionManager.msgSerial'); expect(realtime.connection.connectionManager.msgSerial).to.equal( 0, 'verify msgSerial is 0 (new connection), not 3', @@ -185,6 +191,7 @@ define(['ably', 'shared_helper', 'async', 'chai'], function (Ably, Helper, async connectionManager = realtime.connection.connectionManager; realtime.connection.once('connected', function () { + helper.recordPrivateApi('read.connectionManager.activeProtocol.transport'); var transport = connectionManager.activeProtocol.transport; Helper.whenPromiseSettles(channel.attach(), function (err) { if (err) { @@ -194,6 +201,7 @@ define(['ably', 'shared_helper', 'async', 'chai'], function (Ably, Helper, async let transportSendCallback; + helper.recordPrivateApi('replace.transport.send'); /* Sabotage sending the message */ transport.send = function (msg) { if (msg.action == 15) { @@ -243,9 +251,11 @@ define(['ably', 'shared_helper', 'async', 'chai'], function (Ably, Helper, async }, function (cb) { /* After the disconnect, on reconnect, spy on transport.send again */ + helper.recordPrivateApi('listen.connectionManager.transport.pending'); connectionManager.once('transport.pending', function (transport) { var oldSend = transport.send; + helper.recordPrivateApi('replace.transport.send'); transport.send = function (msg, msgCb) { if (msg.action === 15) { if (msg.messages[0].name === 'first') { @@ -269,12 +279,14 @@ define(['ably', 'shared_helper', 'async', 'chai'], function (Ably, Helper, async cb(); } } + helper.recordPrivateApi('call.transport.send'); oldSend.call(transport, msg, msgCb); }; channel.publish('second', null); }); /* Disconnect the transport (will automatically reconnect and resume) () */ + helper.recordPrivateApi('call.connectionManager.disconnectAllTransports'); connectionManager.disconnectAllTransports(); }, ], @@ -303,15 +315,18 @@ define(['ably', 'shared_helper', 'async', 'chai'], function (Ably, Helper, async realtime = helper.AblyRealtime(), connectionManager = realtime.connection.connectionManager; realtime.connection.once('connected', function () { + helper.recordPrivateApi('listen.connectionManager.connectiondetails'); connectionManager.once('connectiondetails', function (details) { try { expect(details.connectionStateTtl).to.equal(12345, 'Check connectionStateTtl in event'); + helper.recordPrivateApi('read.connectionManager.connectionStateTtl'); expect(connectionManager.connectionStateTtl).to.equal( 12345, 'Check connectionStateTtl set in connectionManager', ); expect(details.clientId).to.equal('foo', 'Check clientId in event'); expect(realtime.auth.clientId).to.equal('foo', 'Check clientId set in auth'); + helper.recordPrivateApi('read.realtime.options.maxMessageSize'); expect(realtime.options.maxMessageSize).to.equal(98765, 'Check maxMessageSize set'); } catch (err) { helper.closeAndFinish(done, realtime, err); @@ -319,6 +334,9 @@ define(['ably', 'shared_helper', 'async', 'chai'], function (Ably, Helper, async } helper.closeAndFinish(done, realtime); }); + helper.recordPrivateApi('call.connectionManager.activeProtocol.getTransport'); + helper.recordPrivateApi('replace.transport.onProtocolMessage'); + helper.recordPrivateApi('call.protocolMessageFromDeserialized'); connectionManager.activeProtocol.getTransport().onProtocolMessage( createPM({ action: 4, diff --git a/test/realtime/connectivity.test.js b/test/realtime/connectivity.test.js index abf44cd572..2fa1db0706 100644 --- a/test/realtime/connectivity.test.js +++ b/test/realtime/connectivity.test.js @@ -23,9 +23,14 @@ define(['ably', 'shared_helper', 'chai'], function (Ably, Helper, chai) { */ it('http_connectivity_check', function (done) { const helper = this.test.helper; + helper.recordPrivateApi('call.http.checkConnectivity'); Helper.whenPromiseSettles(new Ably.Realtime._Http().checkConnectivity(), function (err, res) { try { - expect(res && !err, 'Connectivity check completed ' + (err && helper.Utils.inspectError(err))).to.be.ok; + expect( + res && !err, + 'Connectivity check completed ' + + (err && (helper.recordPrivateApi('call.Utils.inspectError'), helper.Utils.inspectError(err))), + ).to.be.ok; } catch (err) { done(err); return; @@ -34,7 +39,9 @@ define(['ably', 'shared_helper', 'chai'], function (Ably, Helper, chai) { }); }); - function options(connectivityCheckUrl, disableConnectivityCheck) { + function options(helper, connectivityCheckUrl, disableConnectivityCheck) { + helper.recordPrivateApi('pass.clientOption.connectivityCheckUrl'); + helper.recordPrivateApi('pass.clientOption.disableConnectivityCheck'); return { connectivityCheckUrl, disableConnectivityCheck, @@ -51,11 +58,16 @@ define(['ably', 'shared_helper', 'chai'], function (Ably, Helper, chai) { /** @nospec */ it('succeeds with scheme', function (done) { const helper = this.test.helper; + helper.recordPrivateApi('call.http.checkConnectivity'); Helper.whenPromiseSettles( - helper.AblyRealtime(options(urlScheme + successUrl)).http.checkConnectivity(), + helper.AblyRealtime(options(helper, urlScheme + successUrl)).http.checkConnectivity(), function (err, res) { try { - expect(res && !err, 'Connectivity check completed ' + (err && helper.Utils.inspectError(err))).to.be.ok; + expect( + res && !err, + 'Connectivity check completed ' + + (err && (helper.recordPrivateApi('call.Utils.inspectError'), helper.Utils.inspectError(err))), + ).to.be.ok; } catch (err) { done(err); return; @@ -68,8 +80,9 @@ define(['ably', 'shared_helper', 'chai'], function (Ably, Helper, chai) { /** @nospec */ it('fails with scheme', function (done) { const helper = this.test.helper; + helper.recordPrivateApi('call.http.checkConnectivity'); Helper.whenPromiseSettles( - helper.AblyRealtime(options(urlScheme + failUrl)).http.checkConnectivity(), + helper.AblyRealtime(options(helper, urlScheme + failUrl)).http.checkConnectivity(), function (err, res) { try { expect(!res, 'Connectivity check expected to return false').to.be.ok; @@ -84,11 +97,16 @@ define(['ably', 'shared_helper', 'chai'], function (Ably, Helper, chai) { /** @nospec */ it('succeeds with querystring', function (done) { const helper = this.test.helper; + helper.recordPrivateApi('call.http.checkConnectivity'); Helper.whenPromiseSettles( - helper.AblyRealtime(options(successUrl)).http.checkConnectivity(), + helper.AblyRealtime(options(helper, successUrl)).http.checkConnectivity(), function (err, res) { try { - expect(res && !err, 'Connectivity check completed ' + (err && helper.Utils.inspectError(err))).to.be.ok; + expect( + res && !err, + 'Connectivity check completed ' + + (err && (helper.recordPrivateApi('call.Utils.inspectError'), helper.Utils.inspectError(err))), + ).to.be.ok; done(); } catch (err) { done(err); @@ -100,24 +118,32 @@ define(['ably', 'shared_helper', 'chai'], function (Ably, Helper, chai) { /** @nospec */ it('fails with querystring', function (done) { const helper = this.test.helper; - Helper.whenPromiseSettles(helper.AblyRealtime(options(failUrl)).http.checkConnectivity(), function (err, res) { - try { - expect(!res, 'Connectivity check expected to return false').to.be.ok; - done(); - } catch (err) { - done(err); - } - }); + helper.recordPrivateApi('call.http.checkConnectivity'); + Helper.whenPromiseSettles( + helper.AblyRealtime(options(helper, failUrl)).http.checkConnectivity(), + function (err, res) { + try { + expect(!res, 'Connectivity check expected to return false').to.be.ok; + done(); + } catch (err) { + done(err); + } + }, + ); }); /** @nospec */ it('succeeds with plain url', function (done) { const helper = this.test.helper; Helper.whenPromiseSettles( - helper.AblyRealtime(options('sandbox-rest.ably.io/time')).http.checkConnectivity(), + helper.AblyRealtime(options(helper, 'sandbox-rest.ably.io/time')).http.checkConnectivity(), function (err, res) { try { - expect(res && !err, 'Connectivity check completed ' + (err && helper.Utils.inspectError(err))).to.be.ok; + expect( + res && !err, + 'Connectivity check completed ' + + (err && (helper.recordPrivateApi('call.Utils.inspectError'), helper.Utils.inspectError(err))), + ).to.be.ok; done(); } catch (err) { done(err); @@ -129,8 +155,9 @@ define(['ably', 'shared_helper', 'chai'], function (Ably, Helper, chai) { /** @nospec */ it('fails with plain url', function (done) { const helper = this.test.helper; + helper.recordPrivateApi('call.http.checkConnectivity'); Helper.whenPromiseSettles( - helper.AblyRealtime(options('echo.ably.io')).http.checkConnectivity(), + helper.AblyRealtime(options(helper, 'echo.ably.io')).http.checkConnectivity(), function (err, res) { try { expect(!res, 'Connectivity check expected to return false').to.be.ok; @@ -146,11 +173,16 @@ define(['ably', 'shared_helper', 'chai'], function (Ably, Helper, chai) { /** @nospec */ it('disable_connectivity_check', function (done) { const helper = this.test.helper; + helper.recordPrivateApi('call.http.checkConnectivity'); Helper.whenPromiseSettles( - helper.AblyRealtime(options('notarealhost', true)).http.checkConnectivity(), + helper.AblyRealtime(options(helper, 'notarealhost', true)).http.checkConnectivity(), function (err, res) { try { - expect(res && !err, 'Connectivity check completed ' + (err && helper.Utils.inspectError(err))).to.be.ok; + expect( + res && !err, + 'Connectivity check completed ' + + (err && (helper.recordPrivateApi('call.Utils.inspectError'), helper.Utils.inspectError(err))), + ).to.be.ok; done(); } catch (err) { done(err); diff --git a/test/realtime/crypto.test.js b/test/realtime/crypto.test.js index 2431b1c0ec..14cb65330b 100644 --- a/test/realtime/crypto.test.js +++ b/test/realtime/crypto.test.js @@ -37,6 +37,7 @@ define(['ably', 'shared_helper', 'async', 'chai'], function (Ably, Helper, async return; } var realtime = helper.AblyRealtime(); + helper.recordPrivateApi('call.BufferUtils.base64Decode'); var key = BufferUtils.base64Decode(testData.key); var iv = BufferUtils.base64Decode(testData.iv); var channel = realtime.channels.get(channelName, { cipher: { key: key, iv: iv } }); @@ -63,9 +64,11 @@ define(['ably', 'shared_helper', 'async', 'chai'], function (Ably, Helper, async if (testPlaintextVariants) { var testMessage = await createTestMessage(); + helper.recordPrivateApi('call.BufferUtils.isBuffer'); if (BufferUtils.isBuffer(testMessage.data) && !(testMessage.data instanceof ArrayBuffer)) { // Now, check that we can also handle an ArrayBuffer plaintext. var testMessageWithArrayBufferData = await createTestMessage(); + helper.recordPrivateApi('call.BufferUtils.toArrayBuffer'); testMessageWithArrayBufferData.data = BufferUtils.toArrayBuffer(testMessageWithArrayBufferData.data); runTest(testMessageWithArrayBufferData); } @@ -161,13 +164,16 @@ define(['ably', 'shared_helper', 'async', 'chai'], function (Ably, Helper, async /** @specpartial RSE1c - can accept binary key */ it('getDefaultParams_ArrayBuffer_key', function (done) { + const helper = this.test.helper; Helper.whenPromiseSettles(Crypto.generateRandomKey(), function (err, key) { if (err) { done(err); } + helper.recordPrivateApi('call.BufferUtils.toArrayBuffer'); var arrayBufferKey = Ably.Realtime.Platform.BufferUtils.toArrayBuffer(key); var params = Crypto.getDefaultParams({ key: arrayBufferKey }); try { + helper.recordPrivateApi('call.BufferUtils.areBuffersEqual'); expect(BufferUtils.areBuffersEqual(params.key, key)).to.equal(true); done(); } catch (err) { @@ -178,14 +184,17 @@ define(['ably', 'shared_helper', 'async', 'chai'], function (Ably, Helper, async /** @specpartial RSE1c - can accept base64 string key */ it('getDefaultParams_base64_key', function (done) { + const helper = this.test.helper; Helper.whenPromiseSettles(Crypto.generateRandomKey(), function (err, key) { if (err) { done(err); return; } + helper.recordPrivateApi('call.BufferUtils.base64Encode'); var b64key = Ably.Realtime.Platform.BufferUtils.base64Encode(key); var params = Crypto.getDefaultParams({ key: b64key }); try { + helper.recordPrivateApi('call.BufferUtils.areBuffersEqual'); expect(BufferUtils.areBuffersEqual(params.key, key)).to.equal(true); done(); } catch (err) { @@ -254,6 +263,7 @@ define(['ably', 'shared_helper', 'async', 'chai'], function (Ably, Helper, async true, function (channelOpts, testMessage, encryptedMessage) { /* encrypt plaintext message; encode() also to handle data that is not already string or buffer */ + helper.recordPrivateApi('call.Message.encode'); Helper.whenPromiseSettles(Message.encode(testMessage, channelOpts), function () { /* compare */ testMessageEquality(done, helper, testMessage, encryptedMessage); @@ -277,6 +287,7 @@ define(['ably', 'shared_helper', 'async', 'chai'], function (Ably, Helper, async true, function (channelOpts, testMessage, encryptedMessage) { /* encrypt plaintext message; encode() also to handle data that is not already string or buffer */ + helper.recordPrivateApi('call.Message.encode'); Helper.whenPromiseSettles(Message.encode(testMessage, channelOpts), function () { /* compare */ testMessageEquality(done, helper, testMessage, encryptedMessage); @@ -300,6 +311,7 @@ define(['ably', 'shared_helper', 'async', 'chai'], function (Ably, Helper, async false, async function (channelOpts, testMessage, encryptedMessage) { /* decrypt encrypted message; decode() also to handle data that is not string or buffer */ + helper.recordPrivateApi('call.Message.decode'); await Message.decode(encryptedMessage, channelOpts); /* compare */ testMessageEquality(done, helper, testMessage, encryptedMessage); @@ -322,6 +334,7 @@ define(['ably', 'shared_helper', 'async', 'chai'], function (Ably, Helper, async false, async function (channelOpts, testMessage, encryptedMessage) { /* decrypt encrypted message; decode() also to handle data that is not string or buffer */ + helper.recordPrivateApi('call.Message.decode'); await Message.decode(encryptedMessage, channelOpts); /* compare */ testMessageEquality(done, helper, testMessage, encryptedMessage); @@ -343,6 +356,7 @@ define(['ably', 'shared_helper', 'async', 'chai'], function (Ably, Helper, async done(err); return; } + helper.recordPrivateApi('call.BufferUtils.base64Decode'); var key = BufferUtils.base64Decode(testData.key); var iv = BufferUtils.base64Decode(testData.iv); @@ -372,9 +386,13 @@ define(['ably', 'shared_helper', 'async', 'chai'], function (Ably, Helper, async 2, false, function (channelOpts, testMessage, encryptedMessage, msgpackEncodedMessage) { + helper.recordPrivateApi('call.Message.encode'); Helper.whenPromiseSettles(Message.encode(testMessage, channelOpts), function () { + helper.recordPrivateApi('call.msgpack.encode'); var msgpackFromEncoded = msgpack.encode(testMessage); var msgpackFromEncrypted = msgpack.encode(encryptedMessage); + helper.recordPrivateApi('call.BufferUtils.base64Decode'); + helper.recordPrivateApi('call.msgpack.decode'); var messageFromMsgpack = Message.fromValues( msgpack.decode(BufferUtils.base64Decode(msgpackEncodedMessage)), ); @@ -382,6 +400,7 @@ define(['ably', 'shared_helper', 'async', 'chai'], function (Ably, Helper, async try { /* Mainly testing that we're correctly encoding the direct output from * the platform's ICipher implementation into the msgpack binary type */ + helper.recordPrivateApi('call.BufferUtils.areBuffersEqual'); expect(BufferUtils.areBuffersEqual(msgpackFromEncoded, msgpackFromEncrypted)).to.equal( true, 'verify msgpack encodings of newly-encrypted and preencrypted messages identical using areBuffersEqual', @@ -413,8 +432,11 @@ define(['ably', 'shared_helper', 'async', 'chai'], function (Ably, Helper, async false, function (channelOpts, testMessage, encryptedMessage, msgpackEncodedMessage) { Helper.whenPromiseSettles(Message.encode(testMessage, channelOpts), function () { + helper.recordPrivateApi('call.msgpack.encode'); var msgpackFromEncoded = msgpack.encode(testMessage); var msgpackFromEncrypted = msgpack.encode(encryptedMessage); + helper.recordPrivateApi('call.BufferUtils.base64Decode'); + helper.recordPrivateApi('call.msgpack.decode'); var messageFromMsgpack = Message.fromValues( msgpack.decode(BufferUtils.base64Decode(msgpackEncodedMessage)), ); @@ -422,6 +444,7 @@ define(['ably', 'shared_helper', 'async', 'chai'], function (Ably, Helper, async try { /* Mainly testing that we're correctly encoding the direct output from * the platform's ICipher implementation into the msgpack binary type */ + helper.recordPrivateApi('call.BufferUtils.areBuffersEqual'); expect(BufferUtils.areBuffersEqual(msgpackFromEncoded, msgpackFromEncrypted)).to.equal( true, 'verify msgpack encodings of newly-encrypted and preencrypted messages identical using areBuffersEqual', @@ -440,6 +463,9 @@ define(['ably', 'shared_helper', 'async', 'chai'], function (Ably, Helper, async } function single_send(done, helper, realtimeOpts, keyLength) { + // the _128 and _256 variants both call this so it makes more sense for this to be the parameterisedTestTitle instead of that set by testOnAllTransports + helper = helper.withParameterisedTestTitle('single_send'); + if (!Crypto) { done(new Error('Encryption not supported')); return; @@ -463,6 +489,7 @@ define(['ably', 'shared_helper', 'async', 'chai'], function (Ably, Helper, async return; } try { + helper.recordPrivateApi('read.channel.channelOptions.cipher'); expect(channel.channelOptions.cipher.algorithm).to.equal('aes'); expect(channel.channelOptions.cipher.keyLength).to.equal(keyLength); } catch (err) { @@ -498,6 +525,8 @@ define(['ably', 'shared_helper', 'async', 'chai'], function (Ably, Helper, async }); function _multiple_send(done, helper, text, iterations, delay) { + helper = helper.withParameterisedTestTitle('multiple_send'); + if (!Crypto) { done(new Error('Encryption not supported')); return; @@ -511,6 +540,7 @@ define(['ably', 'shared_helper', 'async', 'chai'], function (Ably, Helper, async Helper.whenPromiseSettles(Crypto.generateRandomKey(128), function (err, key) { channel.setOptions({ cipher: { key: key } }); try { + helper.recordPrivateApi('read.channel.channelOptions.cipher'); expect(channel.channelOptions.cipher.algorithm).to.equal('aes'); expect(channel.channelOptions.cipher.keyLength).to.equal(128); } catch (err) { @@ -642,6 +672,7 @@ define(['ably', 'shared_helper', 'async', 'chai'], function (Ably, Helper, async return; } try { + helper.recordPrivateApi('read.channel.channelOptions.cipher'); expect(txChannel.channelOptions.cipher.algorithm).to.equal('aes'); expect(rxChannel.channelOptions.cipher.algorithm).to.equal('aes'); } catch (err) { diff --git a/test/realtime/delta.test.js b/test/realtime/delta.test.js index e0045abd8d..1e0e12aa39 100644 --- a/test/realtime/delta.test.js +++ b/test/realtime/delta.test.js @@ -166,6 +166,7 @@ define(['shared_helper', 'vcdiff-decoder', 'async', 'chai'], function (Helper, v if (index === 1) { /* Simulate issue */ + helper.recordPrivateApi('write.channel._lastPayload'); channel._lastPayload.messageId = null; channel.once('attaching', function (stateChange) { try { diff --git a/test/realtime/encoding.test.js b/test/realtime/encoding.test.js index 5511ae5e9a..fddc3f9374 100644 --- a/test/realtime/encoding.test.js +++ b/test/realtime/encoding.test.js @@ -68,6 +68,7 @@ define(['ably', 'shared_helper', 'async', 'chai'], function (Ably, Helper, async channel.subscribe(name, function (msg) { try { if (encodingSpec.expectedHexValue) { + helper.recordPrivateApi('call.BufferUtils.hexEncode'); expect(BufferUtils.hexEncode(msg.data)).to.equal( encodingSpec.expectedHexValue, 'Check data matches', @@ -86,6 +87,7 @@ define(['ably', 'shared_helper', 'async', 'chai'], function (Ably, Helper, async binarychannel.subscribe(name, function (msg) { try { if (encodingSpec.expectedHexValue) { + helper.recordPrivateApi('call.BufferUtils.hexEncode'); expect(BufferUtils.hexEncode(msg.data)).to.equal( encodingSpec.expectedHexValue, 'Check data matches', @@ -101,6 +103,7 @@ define(['ably', 'shared_helper', 'async', 'chai'], function (Ably, Helper, async }); }, function (parallelCb) { + helper.recordPrivateApi('read.Defaults.protocolVersion'); Helper.whenPromiseSettles( realtime.request( 'post', @@ -169,6 +172,7 @@ define(['ably', 'shared_helper', 'async', 'chai'], function (Ably, Helper, async var data, name = index.toString(); if (encodingSpec.expectedHexValue) { + helper.recordPrivateApi('call.BufferUtils.base64Decode'); data = BufferUtils.base64Decode(encodingSpec.data); } else { data = encodingSpec.expectedValue; @@ -187,6 +191,7 @@ define(['ably', 'shared_helper', 'async', 'chai'], function (Ably, Helper, async eachOfCb(err); return; } + helper.recordPrivateApi('read.Defaults.protocolVersion'); Helper.whenPromiseSettles( realtime.request('get', channelPath, 3, null, null, null), function (err, resultPage) { diff --git a/test/realtime/event_emitter.test.js b/test/realtime/event_emitter.test.js index 4e56b55af5..c584cf6704 100644 --- a/test/realtime/event_emitter.test.js +++ b/test/realtime/event_emitter.test.js @@ -93,6 +93,7 @@ define(['shared_helper', 'chai'], function (Helper, chai) { callbackCalled = true; }); + helper.recordPrivateApi('call.EventEmitter.emit'); eventEmitter.emit('custom'); try { expect(callbackCalled, 'Last callback should have been called').to.be.ok; @@ -118,6 +119,7 @@ define(['shared_helper', 'chai'], function (Helper, chai) { onCallbackCalled += 1; }); + helper.recordPrivateApi('call.EventEmitter.emit'); eventEmitter.emit('custom'); eventEmitter.emit('custom'); eventEmitter.emit('custom'); @@ -152,6 +154,7 @@ define(['shared_helper', 'chai'], function (Helper, chai) { eventEmitter.once('custom', callback); eventEmitter.once('custom', callback); + helper.recordPrivateApi('call.EventEmitter.emit'); eventEmitter.emit('custom'); eventEmitter.emit('custom'); @@ -181,11 +184,13 @@ define(['shared_helper', 'chai'], function (Helper, chai) { eventEmitter.on('custom', callback); eventEmitter.on('custom', callback); + helper.recordPrivateApi('call.EventEmitter.emit'); eventEmitter.emit('custom'); expect(callbackCalled).to.equal(3, 'The same callback should have been called for every registration'); callbackCalled = 0; eventEmitter.off(callback); + helper.recordPrivateApi('call.EventEmitter.emit'); eventEmitter.emit('custom'); expect(callbackCalled).to.equal(0, 'All callbacks should have been removed'); } catch (err) { @@ -212,11 +217,13 @@ define(['shared_helper', 'chai'], function (Helper, chai) { eventEmitter.on('custom', callback); eventEmitter.on('custom', callback); + helper.recordPrivateApi('call.EventEmitter.emit'); eventEmitter.emit('custom'); expect(callbackCalled).to.equal(3, 'The same callback should have been called for every registration'); callbackCalled = 0; eventEmitter.off(); + helper.recordPrivateApi('call.EventEmitter.emit'); eventEmitter.emit('custom'); expect(callbackCalled).to.equal(0, 'All callbacks should have been removed'); } catch (err) { @@ -243,11 +250,13 @@ define(['shared_helper', 'chai'], function (Helper, chai) { eventEmitter.on('custom', callback); eventEmitter.on('custom', callback); + helper.recordPrivateApi('call.EventEmitter.emit'); eventEmitter.emit('custom'); expect(callbackCalled).to.equal(3, 'The same callback should have been called for every registration'); callbackCalled = 0; eventEmitter.off('custom', callback); + helper.recordPrivateApi('call.EventEmitter.emit'); eventEmitter.emit('custom'); expect(callbackCalled).to.equal(0, 'All callbacks should have been removed'); } catch (err) { @@ -278,11 +287,13 @@ define(['shared_helper', 'chai'], function (Helper, chai) { eventEmitter.on('custom', callback); eventEmitter.on('custom', callback); + helper.recordPrivateApi('call.EventEmitter.emit'); eventEmitter.emit('custom'); expect(callbackCalled).to.equal(3, 'The same callback should have been called for every registration'); callbackCalled = 0; eventEmitter.off('custom'); + helper.recordPrivateApi('call.EventEmitter.emit'); eventEmitter.emit('custom'); expect(callbackCalled).to.equal(0, 'All callbacks should have been removed'); } catch (err) { @@ -313,16 +324,20 @@ define(['shared_helper', 'chai'], function (Helper, chai) { try { eventEmitter.once('custom', callback); eventEmitter.on('custom', callback); + helper.recordPrivateApi('read.EventEmitter.events'); expect('custom' in eventEmitter.events, 'custom event array exists').to.be.ok; eventEmitter.off('custom', callback); + helper.recordPrivateApi('read.EventEmitter.events'); expect(!('custom' in eventEmitter.events), 'custom event listener array is removed from object').to.be.ok; eventEmitter.once('custom', callback); eventEmitter.on('custom', callback); + helper.recordPrivateApi('read.EventEmitter.events'); expect('custom' in eventEmitter.events, 'custom event array exists').to.be.ok; eventEmitter.off(callback); + helper.recordPrivateApi('read.EventEmitter.events'); expect(!('custom' in eventEmitter.events), 'event listener array is removed from object').to.be.ok; } catch (err) { helper.closeAndFinish(done, realtime, err); @@ -348,6 +363,7 @@ define(['shared_helper', 'chai'], function (Helper, chai) { }; try { + helper.recordPrivateApi('call.EventEmitter.emit'); callbackCalled = 0; eventEmitter.on(['a', 'b', 'c'], callback); eventEmitter.emit('a'); @@ -396,6 +412,7 @@ define(['shared_helper', 'chai'], function (Helper, chai) { try { callbackCalled = 0; eventEmitter.once(['a', 'b', 'c'], callback); + helper.recordPrivateApi('call.EventEmitter.emit'); eventEmitter.emit('a', 'expected'); eventEmitter.emit('b', 'wrong'); eventEmitter.emit('c', 'wrong'); @@ -427,6 +444,7 @@ define(['shared_helper', 'chai'], function (Helper, chai) { secondCbCalled = true; }); }); + helper.recordPrivateApi('call.EventEmitter.emit'); eventEmitter.emit('a'); try { @@ -475,6 +493,7 @@ define(['shared_helper', 'chai'], function (Helper, chai) { eventEmitter.off(); }); + helper.recordPrivateApi('call.EventEmitter.emit'); eventEmitter.emit('a'); try { @@ -533,6 +552,7 @@ define(['shared_helper', 'chai'], function (Helper, chai) { eventEmitter = realtime.connection; const p = eventEmitter.once(); + helper.recordPrivateApi('call.EventEmitter.emit'); eventEmitter.emit('b'); p.then(function () { helper.closeAndFinish(done, realtime); @@ -551,6 +571,7 @@ define(['shared_helper', 'chai'], function (Helper, chai) { eventEmitter = realtime.connection; const p = eventEmitter.once(['a', 'b', 'c']); + helper.recordPrivateApi('call.EventEmitter.emit'); eventEmitter.emit('b'); p.then(function () { helper.closeAndFinish(done, realtime); diff --git a/test/realtime/failure.test.js b/test/realtime/failure.test.js index 8044abf019..970e7b90e4 100644 --- a/test/realtime/failure.test.js +++ b/test/realtime/failure.test.js @@ -125,6 +125,8 @@ define(['ably', 'shared_helper', 'async', 'chai'], function (Ably, Helper, async var lifecycleTest = function (transports) { return function (cb) { var connectionEvents = []; + + helper.recordPrivateApi('pass.clientOption.webSocketConnectTimeout'); var realtime = helper.AblyRealtime({ transports: transports, realtimeHost: 'invalid', @@ -372,10 +374,12 @@ define(['ably', 'shared_helper', 'async', 'chai'], function (Ably, Helper, async channel = realtime.channels.get('failed_attach'), originalProcessMessage = channel.processMessage.bind(channel); + helper.recordPrivateApi('replace.channel.processMessage'); channel.processMessage = async function (message) { if (message.action === 11) { return; } + helper.recordPrivateApi('call.channel.processMessage'); await originalProcessMessage(message); }; @@ -422,17 +426,20 @@ define(['ably', 'shared_helper', 'async', 'chai'], function (Ably, Helper, async var performance = isBrowser ? window.performance : require('perf_hooks').performance; + helper.recordPrivateApi('replace.channel.processMessage'); channel.processMessage = async function (message) { // Ignore ATTACHED messages if (message.action === 11) { return; } + helper.recordPrivateApi('call.channel.processMessage'); await originalProcessMessage(message); }; var retryTimeouts = []; realtime.connection.on('connected', function () { + helper.recordPrivateApi('write.realtime.options.timeouts.realtimeRequestTimeout'); realtime.options.timeouts.realtimeRequestTimeout = 1; Helper.whenPromiseSettles(channel.attach(), function (err) { if (err) { @@ -479,7 +486,7 @@ define(['ably', 'shared_helper', 'async', 'chai'], function (Ably, Helper, async function nack_on_connection_failure(failureFn, expectedRealtimeState, expectedNackCode) { return function (done) { /* Use one transport because stubbing out transport#onProtocolMesage */ - var helper = this.test.helper, + var helper = this.test.helper.withParameterisedTestTitle('nack_on_connection_failure'), realtime = helper.AblyRealtime({ transports: [helper.bestTransport] }), channel = realtime.channels.get('nack_on_connection_failure'); @@ -494,12 +501,15 @@ define(['ably', 'shared_helper', 'async', 'chai'], function (Ably, Helper, async Helper.whenPromiseSettles(channel.attach(), cb); }, function (cb) { + helper.recordPrivateApi('read.connectionManager.activeProtocol.transport'); var transport = realtime.connection.connectionManager.activeProtocol.transport, originalOnProtocolMessage = transport.onProtocolMessage; + helper.recordPrivateApi('replace.transport.onProtocolMessage'); transport.onProtocolMessage = function (message) { /* make sure we don't get an ack! */ if (message.action !== 1) { + helper.recordPrivateApi('call.transport.onProtocolMessage'); originalOnProtocolMessage.apply(this, arguments); } }; @@ -516,8 +526,9 @@ define(['ably', 'shared_helper', 'async', 'chai'], function (Ably, Helper, async cb(err); } }); + helper.recordPrivateApi('call.Platform.nextTick'); Ably.Realtime.Platform.Config.nextTick(function () { - failureFn(realtime, helper); + failureFn(realtime, helper.withParameterisedTestTitle(null)); }); }, ], @@ -544,7 +555,9 @@ define(['ably', 'shared_helper', 'async', 'chai'], function (Ably, Helper, async it( 'nack_on_connection_failed', nack_on_connection_failure( - function (realtime) { + function (realtime, helper) { + helper.recordPrivateApi('read.connectionManager.activeProtocol.transport'); + helper.recordPrivateApi('call.transport.onProtocolMessage'); realtime.connection.connectionManager.activeProtocol.transport.onProtocolMessage({ action: 9, error: { statusCode: 401, code: 40100, message: 'connection failed because reasons' }, @@ -573,12 +586,15 @@ define(['ably', 'shared_helper', 'async', 'chai'], function (Ably, Helper, async realtime = helper.AblyRealtime({ realtimeRequestTimeout: 2000 }), originalOnProtocolMessage; + helper.recordPrivateApi('listen.connectionManager.transport.pending'); realtime.connection.connectionManager.on('transport.pending', function (transport) { originalOnProtocolMessage = transport.onProtocolMessage; + helper.recordPrivateApi('replace.transport.onProtocolMessage'); transport.onProtocolMessage = function (message) { if (message.action === 4) { message.connectionDetails.maxIdleInterval = 100; } + helper.recordPrivateApi('call.transport.onProtocolMessage'); originalOnProtocolMessage.call(this, message); }; }); @@ -605,10 +621,11 @@ define(['ably', 'shared_helper', 'async', 'chai'], function (Ably, Helper, async /** @specpartial RTN14d - last sentence: check that if we received a 5xx disconnected, when we try again we use a fallback host */ Helper.testOnAllTransports(this, 'try_fallback_hosts_on_placement_constraint', function (realtimeOpts) { return function (done) { + const helper = this.test.helper; /* Use the echoserver as a fallback host because it doesn't support * websockets, so it'll fail to connect, which we can detect */ - var helper = this.test.helper, - realtime = helper.AblyRealtime(helper.Utils.mixin({ fallbackHosts: ['echo.ably.io'] }, realtimeOpts)), + helper.recordPrivateApi('call.Utils.mixin'); + var realtime = helper.AblyRealtime(helper.Utils.mixin({ fallbackHosts: ['echo.ably.io'] }, realtimeOpts)), connection = realtime.connection, connectionManager = connection.connectionManager; @@ -627,6 +644,9 @@ define(['ably', 'shared_helper', 'async', 'chai'], function (Ably, Helper, async helper.closeAndFinish(done, realtime); }); }); + helper.recordPrivateApi('call.protocolMessageFromDeserialized'); + helper.recordPrivateApi('call.connectionManager.activeProtocol.getTransport'); + helper.recordPrivateApi('call.transport.onProtocolMessage'); connectionManager.activeProtocol.getTransport().onProtocolMessage( createPM({ action: 6, @@ -664,10 +684,13 @@ define(['ably', 'shared_helper', 'async', 'chai'], function (Ably, Helper, async var connectionManager = sender_realtime.connection.connectionManager; var onChannelMsgOrig = connectionManager.onChannelMessage; + helper.recordPrivateApi('replace.connectionManager.onChannelMessage'); connectionManager.onChannelMessage = function (msg, transport) { if (msg.action === 15) { + helper.recordPrivateApi('call.channel.requestState'); sender_channel.requestState('attaching'); } + helper.recordPrivateApi('call.connectionManager.onChannelMessage'); onChannelMsgOrig.call(connectionManager, msg, transport); }; diff --git a/test/realtime/init.test.js b/test/realtime/init.test.js index cdf83ed74e..af15253c0a 100644 --- a/test/realtime/init.test.js +++ b/test/realtime/init.test.js @@ -31,8 +31,17 @@ define(['ably', 'shared_helper', 'chai'], function (Ably, Helper, chai) { realtime = helper.AblyRealtime({ transports: ['web_socket', 'xhr_polling'] }); realtime.connection.on('connected', function () { /* check api version */ + helper.recordPrivateApi('read.connectionManager.activeProtocol.transport'); var transport = realtime.connection.connectionManager.activeProtocol.transport; - var connectUri = helper.isWebsocket(transport) ? transport.uri : transport.recvRequest.recvUri; + var connectUri = helper.isWebsocket(transport) + ? (() => { + helper.recordPrivateApi('read.transport.uri'); + return transport.uri; + })() + : (() => { + helper.recordPrivateApi('read.transport.recvRequest.recvUri'); + return transport.recvRequest.recvUri; + })(); try { expect(connectUri.indexOf('v=3') > -1, 'Check uri includes v=3').to.be.ok; } catch (err) { @@ -63,7 +72,10 @@ define(['ably', 'shared_helper', 'chai'], function (Ably, Helper, chai) { realtime = new helper.Ably.Realtime(keyStr); try { + helper.recordPrivateApi('read.realtime.options.key'); expect(realtime.options.key).to.equal(keyStr); + helper.recordPrivateApi('read.realtime.options'); + helper.recordPrivateApi('read.connectionManager.options'); expect(realtime.options).to.deep.equal(realtime.connection.connectionManager.options); } catch (err) { helper.closeAndFinish(done, realtime, err); @@ -98,7 +110,10 @@ define(['ably', 'shared_helper', 'chai'], function (Ably, Helper, chai) { realtime = new helper.Ably.Realtime(tokenStr); try { + helper.recordPrivateApi('read.realtime.options.token'); expect(realtime.options.token).to.equal(tokenStr); + helper.recordPrivateApi('read.realtime.options'); + helper.recordPrivateApi('read.connectionManager.options'); expect(realtime.options).to.deep.equal(realtime.connection.connectionManager.options); helper.closeAndFinish(done, realtime); } catch (err) { @@ -120,7 +135,9 @@ define(['ably', 'shared_helper', 'chai'], function (Ably, Helper, chai) { try { var keyStr = helper.getTestApp().keys[0].keyStr; realtime = helper.AblyRealtime({ key: keyStr, useTokenAuth: true }); + helper.recordPrivateApi('read.realtime.options.key'); expect(realtime.options.key).to.equal(keyStr); + helper.recordPrivateApi('read.auth.method'); expect(realtime.auth.method).to.equal('token'); expect(realtime.auth.clientId).to.equal(undefined); /* Check that useTokenAuth by default results in an anonymous (and not wildcard) token */ @@ -260,6 +277,7 @@ define(['ably', 'shared_helper', 'chai'], function (Ably, Helper, chai) { * host set, so not using helpers.realtime this time, which will use a * test env */ var realtime = new Ably.Realtime({ key: 'not_a.real:key', autoConnect: false }); + helper.recordPrivateApi('read.connectionManager.httpHosts'); var defaultHost = realtime.connection.connectionManager.httpHosts[0]; expect(defaultHost).to.equal('rest.ably.io', 'Verify correct default rest host chosen'); realtime.close(); @@ -289,18 +307,22 @@ define(['ably', 'shared_helper', 'chai'], function (Ably, Helper, chai) { }); /* Note: uses internal knowledge of connectionManager */ try { + helper.recordPrivateApi('read.connectionManager.states.disconnected.retryDelay'); expect(realtime.connection.connectionManager.states.disconnected.retryDelay).to.equal( 123, 'Verify disconnected retry frequency is settable', ); + helper.recordPrivateApi('read.connectionManager.states.suspended.retryDelay'); expect(realtime.connection.connectionManager.states.suspended.retryDelay).to.equal( 456, 'Verify suspended retry frequency is settable', ); + helper.recordPrivateApi('read.connectionManager.options.timeouts.httpRequestTimeout'); expect(realtime.connection.connectionManager.options.timeouts.httpRequestTimeout).to.equal( 789, 'Verify http request timeout is settable', ); + helper.recordPrivateApi('read.connectionManager.options.timeouts.httpMaxRetryDuration'); expect(realtime.connection.connectionManager.options.timeouts.httpMaxRetryDuration).to.equal( 321, 'Verify http max retry duration is settable', @@ -336,6 +358,7 @@ define(['ably', 'shared_helper', 'chai'], function (Ably, Helper, chai) { fallbackHosts: ['b', 'c', 'd', 'e'], }); /* Note: uses internal knowledge of connectionManager */ + helper.recordPrivateApi('read.connectionManager.httpHosts'); expect(realtime.connection.connectionManager.httpHosts.length).to.equal( 3, 'Verify hosts list is the expected length', @@ -343,6 +366,7 @@ define(['ably', 'shared_helper', 'chai'], function (Ably, Helper, chai) { expect(realtime.connection.connectionManager.httpHosts[0]).to.equal('a', 'Verify given restHost is first'); /* Replace chooseTransportForHost with a spy, then try calling * chooseHttpTransport to see what host is picked */ + helper.recordPrivateApi('replace.connectionManager.tryATransport'); realtime.connection.connectionManager.tryATransport = function (transportParams, transport, cb) { switch (transportParams.host) { case 'a': @@ -382,7 +406,9 @@ define(['ably', 'shared_helper', 'chai'], function (Ably, Helper, chai) { var realtime; try { realtime = helper.AblyRealtime({ transports: helper.availableTransports }); + helper.recordPrivateApi('read.connectionManager.baseTransport'); expect(realtime.connection.connectionManager.baseTransport).to.equal('comet'); + helper.recordPrivateApi('read.connectionManager.webSocketTransportAvailable'); expect(realtime.connection.connectionManager.webSocketTransportAvailable).to.be.ok; helper.closeAndFinish(done, realtime); } catch (err) { @@ -406,9 +432,12 @@ define(['ably', 'shared_helper', 'chai'], function (Ably, Helper, chai) { try { var keyStr = helper.getTestApp().keys[0].keyStr; var realtime = helper.AblyRealtime({ key: keyStr, useTokenAuth: true }); + helper.recordPrivateApi('listen.connectionManager.transport.pending'); realtime.connection.connectionManager.once('transport.pending', function (state) { + helper.recordPrivateApi('read.connectionManager.pendingTransport'); var transport = realtime.connection.connectionManager.pendingTransport, originalOnProtocolMessage = transport.onProtocolMessage; + helper.recordPrivateApi('replace.transport.onProtocolMessage'); realtime.connection.connectionManager.pendingTransport.onProtocolMessage = function (message) { try { if (message.action === 4) { @@ -416,6 +445,7 @@ define(['ably', 'shared_helper', 'chai'], function (Ably, Helper, chai) { message.connectionDetails.connectionKey = 'importantConnectionKey'; message.connectionDetails.clientId = 'customClientId'; } + helper.recordPrivateApi('call.transport.onProtocolMessage'); originalOnProtocolMessage.call(transport, message); } catch (err) { helper.closeAndFinish(done, realtime, err); @@ -453,6 +483,7 @@ define(['ably', 'shared_helper', 'chai'], function (Ably, Helper, chai) { }); realtime.connection.once('connected', function () { try { + helper.recordPrivateApi('call.http._getHosts'); var hosts = new Ably.Rest._Http()._getHosts(realtime); /* restHost rather than realtimeHost as that's what connectionManager * knows about; converted to realtimeHost by the websocketTransport */ @@ -476,6 +507,7 @@ define(['ably', 'shared_helper', 'chai'], function (Ably, Helper, chai) { fallbackHosts: [goodHost, 'b', 'c'], }); realtime.connection.once('connected', function () { + helper.recordPrivateApi('call.http._getHosts'); var hosts = new Ably.Realtime._Http()._getHosts(realtime); /* restHost rather than realtimeHost as that's what connectionManager * knows about; converted to realtimeHost by the websocketTransport */ diff --git a/test/realtime/message.test.js b/test/realtime/message.test.js index c50d81b09a..6d0fe8f7c5 100644 --- a/test/realtime/message.test.js +++ b/test/realtime/message.test.js @@ -151,6 +151,7 @@ define(['ably', 'shared_helper', 'async', 'chai'], function (Ably, Helper, async txRealtime, rxRealtime; try { + helper.recordPrivateApi('call.Utils.mixin'); txRealtime = helper.AblyRealtime(helper.Utils.mixin(realtimeOpts, { autoConnect: false })); rxRealtime = helper.AblyRealtime(); var txChannel = txRealtime.channels.get('publishQueued_' + String(Math.random()).substr(2)); @@ -671,9 +672,11 @@ define(['ably', 'shared_helper', 'async', 'chai'], function (Ably, Helper, async realtime = helper.AblyRealtime({ clientId: clientId }); realtime.connection.once('connected', function () { + helper.recordPrivateApi('read.connectionManager.activeProtocol.transport'); var transport = realtime.connection.connectionManager.activeProtocol.transport, originalSend = transport.send; + helper.recordPrivateApi('replace.transport.send'); transport.send = function (message) { try { if (message.action === 15) { @@ -681,6 +684,7 @@ define(['ably', 'shared_helper', 'async', 'chai'], function (Ably, Helper, async expect(!message.messages[0].clientId, 'client ID is not added by the client library as it is implicit').to .be.ok; } + helper.recordPrivateApi('call.transport.send'); originalSend.apply(transport, arguments); } catch (err) { helper.closeAndFinish(done, realtime, err); @@ -715,15 +719,18 @@ define(['ably', 'shared_helper', 'async', 'chai'], function (Ably, Helper, async realtime = helper.AblyRealtime({ clientId: clientId, transports: [helper.bestTransport] }); realtime.connection.once('connected', function () { + helper.recordPrivateApi('read.connectionManager.activeProtocol.transport'); var transport = realtime.connection.connectionManager.activeProtocol.transport, originalSend = transport.send; + helper.recordPrivateApi('replace.transport.send'); transport.send = function (message) { try { if (message.action === 15) { expect(message.messages[0].name === 'event0', 'Outgoing message interecepted').to.be.ok; expect(message.messages[0].clientId === clientId, 'client ID is present when published to Ably').to.be.ok; } + helper.recordPrivateApi('call.transport.send'); originalSend.apply(transport, arguments); } catch (err) { helper.closeAndFinish(done, realtime, err); @@ -809,9 +816,11 @@ define(['ably', 'shared_helper', 'async', 'chai'], function (Ably, Helper, async }, 500); // ensure that the message is not published }); + helper.recordPrivateApi('listen.connectionManager.transport.pending'); realtime.connection.connectionManager.on('transport.pending', function (transport) { var originalSend = transport.send; + helper.recordPrivateApi('replace.transport.send'); transport.send = function (message) { try { if (message.action === 15) { @@ -819,6 +828,7 @@ define(['ably', 'shared_helper', 'async', 'chai'], function (Ably, Helper, async expect(message.messages[0].clientId === invalidClientId, 'client ID is present when published to Ably') .to.be.ok; } + helper.recordPrivateApi('call.transport.send'); originalSend.apply(transport, arguments); } catch (err) { helper.closeAndFinish(done, realtime, err); @@ -869,6 +879,7 @@ define(['ably', 'shared_helper', 'async', 'chai'], function (Ably, Helper, async ++received; if (received === 2) { /* wait a tick to make sure no more messages come in */ + helper.recordPrivateApi('call.Platform.nextTick'); config.nextTick(function () { innercb(); }); @@ -960,6 +971,7 @@ define(['ably', 'shared_helper', 'async', 'chai'], function (Ably, Helper, async return cb(e); } // Wait for any errant messages to arrive before continuing + helper.recordPrivateApi('call.Platform.nextTick'); config.nextTick(cb); }, ); @@ -1023,12 +1035,14 @@ define(['ably', 'shared_helper', 'async', 'chai'], function (Ably, Helper, async return expect.fail('Listener should not fire'); }; channel.subscribe({ refType: 'com.ably.test', refTimeserial: '0123456789' }, listener); + helper.recordPrivateApi('call.filteredSubscriptions.has'); expect(channel.filteredSubscriptions.has(listener), 'Listener should initially be present').to.be.true; channel.unsubscribe(listener); expect( channel.filteredSubscriptions.has(listener), 'Listener should no longer be present after unsubscribing', ).to.be.false; + helper.recordPrivateApi('call.Platform.nextTick'); config.nextTick(cb); } catch (e) { cb(e); @@ -1112,6 +1126,7 @@ define(['ably', 'shared_helper', 'async', 'chai'], function (Ably, Helper, async channel = realtime.channels.get('maxMessageSize'); realtime.connection.once('connected', function () { + helper.recordPrivateApi('listen.connectionManager.connectiondetails'); connectionManager.once('connectiondetails', function (details) { Helper.whenPromiseSettles( channel.publish('foo', 'aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa'), @@ -1128,7 +1143,11 @@ define(['ably', 'shared_helper', 'async', 'chai'], function (Ably, Helper, async ); }); var connectionDetails = connectionManager.connectionDetails; + helper.recordPrivateApi('write.connectionManager.connectionDetails.maxMessageSize'); connectionDetails.maxMessageSize = 64; + helper.recordPrivateApi('call.connectionManager.activeProtocol.getTransport'); + helper.recordPrivateApi('call.protocolMessageFromDeserialized'); + helper.recordPrivateApi('call.transport.onProtocolMessage'); connectionManager.activeProtocol.getTransport().onProtocolMessage( createPM({ action: 4, @@ -1183,6 +1202,7 @@ define(['ably', 'shared_helper', 'async', 'chai'], function (Ably, Helper, async channelOne.publish({ name: 'm', id: 'bundle_m', data: { expectedBundle: 9 } }); channelOne.publish('z_last', { expectedBundle: 10 }); + helper.recordPrivateApi('call.transport.onProtocolMessage'); var queue = realtime.connection.connectionManager.queuedMessages; var messages; try { diff --git a/test/realtime/presence.test.js b/test/realtime/presence.test.js index f54fe76440..636c259541 100644 --- a/test/realtime/presence.test.js +++ b/test/realtime/presence.test.js @@ -438,6 +438,8 @@ define(['ably', 'shared_helper', 'async', 'chai'], function (Ably, Helper, async } cb(); }); + + helper.recordPrivateApi('call.PresenceMessage.fromValues'); presence.enter( PresenceMessage.fromValues({ extras: { headers: { key: 'value' } }, @@ -458,6 +460,7 @@ define(['ably', 'shared_helper', 'async', 'chai'], function (Ably, Helper, async } cb(); }); + helper.recordPrivateApi('call.PresenceMessage.fromValues'); presence.leave( PresenceMessage.fromValues({ extras: { headers: { otherKey: 'otherValue' } }, @@ -1250,6 +1253,7 @@ define(['ably', 'shared_helper', 'async', 'chai'], function (Ably, Helper, async var channel = client.channels.get('presenceClientIdIsImplicit'), presence = channel.presence; + helper.recordPrivateApi('replace.channel.sendPresence'); var originalSendPresence = channel.sendPresence; channel.sendPresence = function (presence, callback) { try { @@ -1258,6 +1262,7 @@ define(['ably', 'shared_helper', 'async', 'chai'], function (Ably, Helper, async helper.closeAndFinish(done, client, err); return; } + helper.recordPrivateApi('call.channel.sendPresence'); originalSendPresence.apply(channel, arguments); }; @@ -1299,13 +1304,16 @@ define(['ably', 'shared_helper', 'async', 'chai'], function (Ably, Helper, async transports: [helper.bestTransport], }; + helper.recordPrivateApi('call.Utils.mixin'); var realtimeBin = helper.AblyRealtime(helper.Utils.mixin(options, { useBinaryProtocol: true })); var realtimeJson = helper.AblyRealtime(helper.Utils.mixin(options, { useBinaryProtocol: false })); var runTest = function (realtime, callback) { + helper.recordPrivateApi('listen.connectionManager.transport.active'); realtime.connection.connectionManager.once('transport.active', function (transport) { var originalSend = transport.send; + helper.recordPrivateApi('replace.transport.send'); transport.send = function (message) { if (message.action === 14) { /* Message is formatted for Ably by the toJSON method, so need to @@ -1319,9 +1327,11 @@ define(['ably', 'shared_helper', 'async', 'chai'], function (Ably, Helper, async callback(err); return; } + helper.recordPrivateApi('replace.transport.send'); transport.send = originalSend; callback(); } + helper.recordPrivateApi('call.transport.send'); originalSend.apply(transport, arguments); }; @@ -1635,6 +1645,7 @@ define(['ably', 'shared_helper', 'async', 'chai'], function (Ably, Helper, async }, function (cb) { if (!channel.presence.syncComplete) { + helper.recordPrivateApi('call.presence.waitSync'); channel.presence.members.waitSync(cb); } else { cb(); @@ -1649,7 +1660,9 @@ define(['ably', 'shared_helper', 'async', 'chai'], function (Ably, Helper, async }, function (cb) { /* inject an additional member into the myMember set, then force a suspended state */ + helper.recordPrivateApi('read.connectionManager.connectionId'); var connId = realtime.connection.connectionManager.connectionId; + helper.recordPrivateApi('call.presence._myMembers.put'); channel.presence._myMembers.put({ action: 'enter', clientId: 'two', @@ -1677,8 +1690,10 @@ define(['ably', 'shared_helper', 'async', 'chai'], function (Ably, Helper, async * that realtime will feel it necessary to do a sync - if it doesn't, * we request one */ if (channel.presence.syncComplete) { + helper.recordPrivateApi('call.channel.sync'); channel.sync(); } + helper.recordPrivateApi('call.presence.waitSync'); channel.presence.members.waitSync(cb); }, function (cb) { @@ -1761,6 +1776,7 @@ define(['ably', 'shared_helper', 'async', 'chai'], function (Ably, Helper, async }, function (cb) { if (!channel.presence.syncComplete) { + helper.recordPrivateApi('call.presence.waitSync'); channel.presence.members.waitSync(cb); } else { cb(); @@ -1779,7 +1795,9 @@ define(['ably', 'shared_helper', 'async', 'chai'], function (Ably, Helper, async }, function (cb) { /* inject an additional member into the myMember set, then force a suspended state */ + helper.recordPrivateApi('read.connectionManager.connectionId'); var connId = realtime.connection.connectionManager.connectionId; + helper.recordPrivateApi('call.presence._myMembers.put'); channel.presence._myMembers.put({ action: 'enter', clientId: 'me', @@ -1847,6 +1865,7 @@ define(['ably', 'shared_helper', 'async', 'chai'], function (Ably, Helper, async }, function (cb) { /* stub out attachimpl */ + helper.recordPrivateApi('replace.channel.attachImpl'); channel.attachImpl = function () {}; channel.attach(); @@ -1854,13 +1873,17 @@ define(['ably', 'shared_helper', 'async', 'chai'], function (Ably, Helper, async channel.presence.enterClient('client_' + i.toString(), i.toString()); } + helper.recordPrivateApi('replace.channel.attachImpl'); channel.attachImpl = originalAttachImpl; + + helper.recordPrivateApi('call.channel.checkPendingState'); channel.checkPendingState(); /* Now just wait for an enter. One enter implies all, they'll all be * sent in one protocol message */ channel.presence.subscribe('enter', function () { channel.presence.unsubscribe('enter'); + helper.recordPrivateApi('call.Platform.nextTick'); Ably.Realtime.Platform.Config.nextTick(cb); }); }, @@ -1934,6 +1957,7 @@ define(['ably', 'shared_helper', 'async', 'chai'], function (Ably, Helper, async }, function (cb) { /* Inject an additional member locally */ + helper.recordPrivateApi('call.channel.processMessage'); channel .processMessage({ action: 14, @@ -1977,6 +2001,7 @@ define(['ably', 'shared_helper', 'async', 'chai'], function (Ably, Helper, async } cb(); }); + helper.recordPrivateApi('call.channel.sync'); channel.sync(); }, function (cb) { @@ -2070,6 +2095,7 @@ define(['ably', 'shared_helper', 'async', 'chai'], function (Ably, Helper, async cb(); }); /* Inject an ATTACHED with RESUMED and HAS_PRESENCE both false */ + helper.recordPrivateApi('call.protocolMessageFromDeserialized'); channel.processMessage( createPM({ action: 11, diff --git a/test/realtime/resume.test.js b/test/realtime/resume.test.js index b49ebecc86..b792f1dc9f 100644 --- a/test/realtime/resume.test.js +++ b/test/realtime/resume.test.js @@ -75,8 +75,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; + helper.recordPrivateApi('listen.connectionManager.transport.active'); connectionManager.once('transport.active', function (transport) { try { + helper.recordPrivateApi('read.transport.params.mode'); expect(transport.params.mode).to.equal('resume', 'Verify reconnect is resume mode'); } catch (err) { callback(err); @@ -212,8 +214,10 @@ define(['shared_helper', 'async', 'chai'], function (Helper, async, chai) { rxCount = 0; rxRealtime.connection.connect(); var connectionManager = rxRealtime.connection.connectionManager; + helper.recordPrivateApi('listen.connectionManager.transport.active'); connectionManager.on('transport.active', function (transport) { try { + helper.recordPrivateApi('read.transport.params.mode'); expect(transport.params.mode).to.equal('resume', 'Verify reconnect is resume mode'); } catch (err) { callback(err); @@ -293,17 +297,23 @@ define(['shared_helper', 'async', 'chai'], function (Helper, async, chai) { }); }, function (cb) { + helper.recordPrivateApi('write.channel.state'); suspendedChannel.state = 'suspended'; Helper.whenPromiseSettles(attachedChannel.attach(), cb); }, function (cb) { /* Sabotage the resume */ - (connection.connectionManager.connectionKey = '_____!ablyjs_test_fake-key____'), + helper.recordPrivateApi('write.connectionManager.connectionKey'); + helper.recordPrivateApi('write.connectionManager.connectionId'); + helper.recordPrivateApi('write.connectionManager.msgSerial')( + (connection.connectionManager.connectionKey = '_____!ablyjs_test_fake-key____'), + ), (connection.connectionManager.connectionId = 'ablyjs_tes'); connection.connectionManager.msgSerial = 15; connection.once('disconnected', function () { cb(); }); + helper.recordPrivateApi('call.connectionManager.disconnectAllTransports'); connection.connectionManager.disconnectAllTransports(); }, function (cb) { @@ -315,7 +325,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'); + helper.recordPrivateApi('read.connectionManager.msgSerial'); expect(connection.connectionManager.msgSerial).to.equal(0, 'Check msgSerial is reset to 0'); + helper.recordPrivateApi('read.connectionManager.connectionId'); expect( connection.connectionManager.connectionId !== 'ablyjs_tes', 'Check connectionId is set by the new CONNECTED', @@ -366,6 +378,7 @@ define(['shared_helper', 'async', 'chai'], function (Helper, async, chai) { }, function (cb) { /* Sabotage the resume - use a valid but now-expired token */ + helper.recordPrivateApi('write.auth.tokenDetails.token'); realtime.auth.tokenDetails.token = badtoken.token; connection.once(function (stateChange) { try { @@ -376,6 +389,7 @@ define(['shared_helper', 'async', 'chai'], function (Helper, async, chai) { } cb(); }); + helper.recordPrivateApi('call.connectionManager.disconnectAllTransports'); connection.connectionManager.disconnectAllTransports(); }, function (cb) { @@ -414,7 +428,9 @@ define(['shared_helper', 'async', 'chai'], function (Helper, async, chai) { }); }, function (cb) { + helper.recordPrivateApi('read.auth.key'); var keyName = realtime.auth.key.split(':')[0]; + helper.recordPrivateApi('write.auth.key'); realtime.auth.key = keyName + ':wrong'; connection.once(function (stateChange) { try { @@ -425,6 +441,8 @@ define(['shared_helper', 'async', 'chai'], function (Helper, async, chai) { } cb(); }); + helper.recordPrivateApi('call.connectionManager.disconnectAllTransports'); + connection.connectionManager.disconnectAllTransports(); }, function (cb) { @@ -539,6 +557,7 @@ define(['shared_helper', 'async', 'chai'], function (Helper, async, chai) { helper.becomeSuspended(realtime, cb); }, function (cb) { + helper.recordPrivateApi('replace.connectionManager.tryATransport'); realtime.connection.connectionManager.tryATransport = function (transportParams) { try { expect(transportParams.mode).to.equal('clean', 'Check library didn’t try to resume'); @@ -570,10 +589,14 @@ define(['shared_helper', 'async', 'chai'], function (Helper, async, chai) { connectionManager = connection.connectionManager; connection.once('connected', function () { + helper.recordPrivateApi('write.connectionManager.lastActivity'); connectionManager.lastActivity = Date.now() - 10000000; /* noop-out onProtocolMessage so that a DISCONNECTED message doesn't * reset the last activity timer */ + helper.recordPrivateApi('call.connectionManager.activeProtocol.getTransport'); + helper.recordPrivateApi('replace.transport.onProtocolMessage'); connectionManager.activeProtocol.getTransport().onProtocolMessage = function () {}; + helper.recordPrivateApi('replace.connectionManager.tryATransport'); connectionManager.tryATransport = function (transportParams) { try { expect(transportParams.mode).to.equal('clean', 'Check library didn’t try to resume'); @@ -583,6 +606,7 @@ define(['shared_helper', 'async', 'chai'], function (Helper, async, chai) { } helper.closeAndFinish(done, realtime); }; + helper.recordPrivateApi('call.connectionManager.disconnectAllTransports'); connectionManager.disconnectAllTransports(); }); }); @@ -614,9 +638,12 @@ define(['shared_helper', 'async', 'chai'], function (Helper, async, chai) { var resumed_receiver_realtime = helper.AblyRealtime(); var connectionManager = resumed_receiver_realtime.connection.connectionManager; + helper.recordPrivateApi('replace.connectionManager.send'); var sendOrig = connectionManager.send; connectionManager.send = function (msg, queueEvent, callback) { + helper.recordPrivateApi('call.ProtocolMessage.setFlag'); msg.setFlag('ATTACH_RESUME'); + helper.recordPrivateApi('call.connectionManager.send'); sendOrig.call(connectionManager, msg, queueEvent, callback); }; diff --git a/test/realtime/sync.test.js b/test/realtime/sync.test.js index b4520f3b7a..dccbdeff52 100644 --- a/test/realtime/sync.test.js +++ b/test/realtime/sync.test.js @@ -50,6 +50,8 @@ define(['ably', 'shared_helper', 'async', 'chai'], function (Ably, Helper, async channelName = 'syncexistingset', channel = realtime.channels.get(channelName); + helper.recordPrivateApi('call.protocolMessageFromDeserialized'); + helper.recordPrivateApi('call.channel.processMessage'); await channel.processMessage( createPM({ action: 11, @@ -66,6 +68,7 @@ define(['ably', 'shared_helper', 'async', 'chai'], function (Ably, Helper, async async.series( [ function (cb) { + helper.recordPrivateApi('call.channel.processMessage'); channel .processMessage({ action: 16, @@ -109,6 +112,7 @@ define(['ably', 'shared_helper', 'async', 'chai'], function (Ably, Helper, async }, function (cb) { /* Trigger another sync. Two has gone without so much as a `leave` message! */ + helper.recordPrivateApi('call.channel.processMessage'); channel .processMessage({ action: 16, @@ -175,6 +179,8 @@ define(['ably', 'shared_helper', 'async', 'chai'], function (Ably, Helper, async channelName = 'sync_member_arrives_in_middle', channel = realtime.channels.get(channelName); + helper.recordPrivateApi('call.protocolMessageFromDeserialized'); + helper.recordPrivateApi('call.channel.processMessage'); await channel.processMessage( createPM({ action: 11, @@ -184,6 +190,7 @@ define(['ably', 'shared_helper', 'async', 'chai'], function (Ably, Helper, async ); /* First sync */ + helper.recordPrivateApi('call.channel.processMessage'); await channel.processMessage({ action: 16, channel: channelName, @@ -199,6 +206,7 @@ define(['ably', 'shared_helper', 'async', 'chai'], function (Ably, Helper, async }); /* A second sync, this time in multiple parts, with a presence message in the middle */ + helper.recordPrivateApi('call.channel.processMessage'); await channel.processMessage({ action: 16, channel: channelName, @@ -214,6 +222,7 @@ define(['ably', 'shared_helper', 'async', 'chai'], function (Ably, Helper, async ], }); + helper.recordPrivateApi('call.channel.processMessage'); await channel.processMessage({ action: 14, channel: channelName, @@ -228,6 +237,7 @@ define(['ably', 'shared_helper', 'async', 'chai'], function (Ably, Helper, async ], }); + helper.recordPrivateApi('call.channel.processMessage'); await channel.processMessage({ action: 16, channel: channelName, @@ -281,6 +291,8 @@ define(['ably', 'shared_helper', 'async', 'chai'], function (Ably, Helper, async channelName = 'sync_member_arrives_normally_after_came_in_sync', channel = realtime.channels.get(channelName); + helper.recordPrivateApi('call.protocolMessageFromDeserialized'); + helper.recordPrivateApi('call.channel.processMessage'); await channel.processMessage( createPM({ action: 11, @@ -289,6 +301,7 @@ define(['ably', 'shared_helper', 'async', 'chai'], function (Ably, Helper, async }), ); + helper.recordPrivateApi('call.channel.processMessage'); await channel.processMessage({ action: 16, channel: channelName, @@ -304,6 +317,7 @@ define(['ably', 'shared_helper', 'async', 'chai'], function (Ably, Helper, async ], }); + helper.recordPrivateApi('call.channel.processMessage'); await channel.processMessage({ action: 14, channel: channelName, @@ -318,6 +332,7 @@ define(['ably', 'shared_helper', 'async', 'chai'], function (Ably, Helper, async ], }); + helper.recordPrivateApi('call.channel.processMessage'); await channel.processMessage({ action: 16, channel: channelName, @@ -368,6 +383,8 @@ define(['ably', 'shared_helper', 'async', 'chai'], function (Ably, Helper, async channelName = 'sync_member_arrives_normally_before_comes_in_sync', channel = realtime.channels.get(channelName); + helper.recordPrivateApi('call.protocolMessageFromDeserialized'); + helper.recordPrivateApi('call.channel.processMessage'); await channel.processMessage( createPM({ action: 11, @@ -376,6 +393,7 @@ define(['ably', 'shared_helper', 'async', 'chai'], function (Ably, Helper, async }), ); + helper.recordPrivateApi('call.channel.processMessage'); await channel.processMessage({ action: 16, channel: channelName, @@ -391,6 +409,7 @@ define(['ably', 'shared_helper', 'async', 'chai'], function (Ably, Helper, async ], }); + helper.recordPrivateApi('call.channel.processMessage'); await channel.processMessage({ action: 14, channel: channelName, @@ -405,6 +424,7 @@ define(['ably', 'shared_helper', 'async', 'chai'], function (Ably, Helper, async ], }); + helper.recordPrivateApi('call.channel.processMessage'); await channel.processMessage({ action: 16, channel: channelName, @@ -459,6 +479,8 @@ define(['ably', 'shared_helper', 'async', 'chai'], function (Ably, Helper, async channelName = 'sync_ordering', channel = realtime.channels.get(channelName); + helper.recordPrivateApi('call.protocolMessageFromDeserialized'); + helper.recordPrivateApi('call.channel.processMessage'); await channel.processMessage( createPM({ action: 11, @@ -467,6 +489,7 @@ define(['ably', 'shared_helper', 'async', 'chai'], function (Ably, Helper, async ); /* One enters */ + helper.recordPrivateApi('call.channel.processMessage'); await channel.processMessage({ action: 14, channel: channelName, @@ -482,6 +505,7 @@ define(['ably', 'shared_helper', 'async', 'chai'], function (Ably, Helper, async }); /* An earlier leave from one (should be ignored) */ + helper.recordPrivateApi('call.channel.processMessage'); await channel.processMessage({ action: 14, channel: channelName, @@ -497,6 +521,7 @@ define(['ably', 'shared_helper', 'async', 'chai'], function (Ably, Helper, async }); /* One adds some data in a newer msgSerial */ + helper.recordPrivateApi('call.channel.processMessage'); await channel.processMessage({ action: 14, channel: channelName, @@ -513,6 +538,7 @@ define(['ably', 'shared_helper', 'async', 'chai'], function (Ably, Helper, async }); /* Two enters */ + helper.recordPrivateApi('call.channel.processMessage'); await channel.processMessage({ action: 14, channel: channelName, @@ -528,6 +554,7 @@ define(['ably', 'shared_helper', 'async', 'chai'], function (Ably, Helper, async }); /* Two updates twice in the same message */ + helper.recordPrivateApi('call.channel.processMessage'); await channel.processMessage({ action: 14, channel: channelName, @@ -549,6 +576,7 @@ define(['ably', 'shared_helper', 'async', 'chai'], function (Ably, Helper, async }); /* Three enters */ + helper.recordPrivateApi('call.channel.processMessage'); await channel.processMessage({ action: 14, channel: channelName, @@ -565,6 +593,7 @@ define(['ably', 'shared_helper', 'async', 'chai'], function (Ably, Helper, async /* Synthesized leave for three (with earlier msgSerial, incompatible id, * and later timestamp) */ + helper.recordPrivateApi('call.channel.processMessage'); await channel.processMessage({ action: 14, channel: channelName, @@ -652,11 +681,15 @@ define(['ably', 'shared_helper', 'async', 'chai'], function (Ably, Helper, async }, function (cb) { var originalProcessMessage = syncerChannel.processMessage; + helper.recordPrivateApi('replace.channel.processMessage'); syncerChannel.processMessage = async function (message) { + helper.recordPrivateApi('call.channel.processMessage'); await originalProcessMessage.apply(this, arguments); /* Inject an additional presence message after the first sync */ if (message.action === 16) { + helper.recordPrivateApi('replace.channel.processMessage'); syncerChannel.processMessage = originalProcessMessage; + helper.recordPrivateApi('call.channel.processMessage'); await syncerChannel.processMessage({ action: 14, id: 'messageid:0', diff --git a/test/realtime/utils.test.js b/test/realtime/utils.test.js index 63800371d3..05142d31ce 100644 --- a/test/realtime/utils.test.js +++ b/test/realtime/utils.test.js @@ -10,6 +10,7 @@ define(['shared_helper', 'chai'], function (Helper, chai) { var retryAttempts = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15]; var initialTimeout = 15; + helper.recordPrivateApi('call.Utils.getRetryTime'); var retryTimeouts = retryAttempts.map((attempt) => helper.Utils.getRetryTime(initialTimeout, attempt)); expect(retryTimeouts.filter((timeout) => timeout >= 30).length).to.equal(0); diff --git a/test/rest/auth.test.js b/test/rest/auth.test.js index 1e123a5e2a..90c5c07a5a 100644 --- a/test/rest/auth.test.js +++ b/test/rest/auth.test.js @@ -176,9 +176,12 @@ define(['chai', 'shared_helper', 'async', 'globals'], function (chai, Helper, as /** @nospec */ it('Token generation with explicit auth', async function () { const helper = this.test.helper; + helper.recordPrivateApi('call.auth.getAuthHeaders'); const authHeaders = await rest.auth.getAuthHeaders(); + helper.recordPrivateApi('write.auth.authOptions.requestHeaders'); rest.auth.authOptions.requestHeaders = authHeaders; var tokenDetails = await rest.auth.requestToken(); + helper.recordPrivateApi('delete.auth.authOptions.requestHeaders'); delete rest.auth.authOptions.requestHeaders; expect(tokenDetails.token, 'Verify token value').to.be.ok; expect(tokenDetails.issued && tokenDetails.issued >= currentTime, 'Verify token issued').to.be.ok; @@ -192,6 +195,7 @@ define(['chai', 'shared_helper', 'async', 'globals'], function (chai, Helper, as */ it('Token generation with explicit auth, different key', async function () { const helper = this.test.helper; + helper.recordPrivateApi('call.auth.getAuthHeaders'); const authHeaders = await rest.auth.getAuthHeaders(); var testKeyOpts = { key: helper.getTestApp().keys[1].keyStr }; var testCapability = JSON.parse(helper.getTestApp().keys[1].capability); @@ -379,7 +383,9 @@ define(['chai', 'shared_helper', 'async', 'globals'], function (chai, Helper, as const helper = this.test.helper; var currentKey = helper.getTestApp().keys[0]; var keys = { keyName: currentKey.keyName, keySecret: currentKey.keySecret }; + helper.recordPrivateApi('call.Utils.mixin'); var authParams = helper.Utils.mixin(keys, params); + helper.recordPrivateApi('call.Utils.toQueryString'); var authUrl = echoServer + '/createJWT' + helper.Utils.toQueryString(authParams); var restJWTRequester = helper.AblyRest({ authUrl: authUrl }); @@ -410,6 +416,7 @@ define(['chai', 'shared_helper', 'async', 'globals'], function (chai, Helper, as it('JWT request with invalid key', async function () { const helper = this.test.helper; var keys = { keyName: 'invalid.invalid', keySecret: 'invalidinvalid' }; + helper.recordPrivateApi('call.Utils.toQueryString'); var authUrl = echoServer + '/createJWT' + helper.Utils.toQueryString(keys); var restJWTRequester = helper.AblyRest({ authUrl: authUrl }); @@ -430,6 +437,7 @@ define(['chai', 'shared_helper', 'async', 'globals'], function (chai, Helper, as const helper = this.test.helper; var currentKey = helper.getTestApp().keys[0]; var keys = { keyName: currentKey.keyName, keySecret: currentKey.keySecret }; + helper.recordPrivateApi('call.Utils.toQueryString'); var authUrl = echoServer + '/createJWT' + helper.Utils.toQueryString(keys); var restJWTRequester = helper.AblyRest({ authUrl: authUrl }); @@ -450,6 +458,7 @@ define(['chai', 'shared_helper', 'async', 'globals'], function (chai, Helper, as it('Rest JWT with authCallback and invalid keys', async function () { const helper = this.test.helper; var keys = { keyName: 'invalid.invalid', keySecret: 'invalidinvalid' }; + helper.recordPrivateApi('call.Utils.toQueryString'); var authUrl = echoServer + '/createJWT' + helper.Utils.toQueryString(keys); var restJWTRequester = helper.AblyRest({ authUrl: authUrl }); diff --git a/test/rest/defaults.test.js b/test/rest/defaults.test.js index 0c3f3f1b89..c74f895613 100644 --- a/test/rest/defaults.test.js +++ b/test/rest/defaults.test.js @@ -19,6 +19,9 @@ define(['ably', 'chai'], function (Ably, chai) { * @specpartial RTN17a - primary host for realtime is realtimeHost */ it('Init with no endpoint-related options', function () { + const helper = this.test.helper; + + helper.recordPrivateApi('call.Defaults.normaliseOptions'); var normalisedOptions = Defaults.normaliseOptions({}, null, null); expect(normalisedOptions.restHost).to.equal('rest.ably.io'); @@ -28,11 +31,14 @@ define(['ably', 'chai'], function (Ably, chai) { expect(normalisedOptions.fallbackHosts.sort()).to.deep.equal(Defaults.FALLBACK_HOSTS.sort()); expect(normalisedOptions.tls).to.equal(true); + helper.recordPrivateApi('call.Defaults.getHosts'); expect(Defaults.getHosts(normalisedOptions).length).to.equal(4); expect(Defaults.getHosts(normalisedOptions)[0]).to.deep.equal(normalisedOptions.restHost); + helper.recordPrivateApi('call.Defaults.getHost'); expect(Defaults.getHost(normalisedOptions, 'rest.ably.io', false)).to.deep.equal('rest.ably.io'); expect(Defaults.getHost(normalisedOptions, 'rest.ably.io', true)).to.equal('realtime.ably.io'); + helper.recordPrivateApi('call.Defaults.getPort'); expect(Defaults.getPort(normalisedOptions)).to.equal(443); }); @@ -54,6 +60,9 @@ define(['ably', 'chai'], function (Ably, chai) { * @specpartial RTC1e - test with environment set to 'production' */ it('Init with production environment', function () { + const helper = this.test.helper; + + helper.recordPrivateApi('call.Defaults.normaliseOptions'); var normalisedOptions = Defaults.normaliseOptions({ environment: 'production' }, null, null); expect(normalisedOptions.restHost).to.equal('rest.ably.io'); @@ -63,11 +72,14 @@ define(['ably', 'chai'], function (Ably, chai) { expect(normalisedOptions.fallbackHosts.sort()).to.deep.equal(Defaults.FALLBACK_HOSTS.sort()); expect(normalisedOptions.tls).to.equal(true); + helper.recordPrivateApi('call.Defaults.getHosts'); expect(Defaults.getHosts(normalisedOptions).length).to.deep.equal(4); expect(Defaults.getHosts(normalisedOptions)[0]).to.deep.equal(normalisedOptions.restHost); + helper.recordPrivateApi('call.Defaults.getHost'); expect(Defaults.getHost(normalisedOptions, 'rest.ably.io', false)).to.deep.equal('rest.ably.io'); expect(Defaults.getHost(normalisedOptions, 'rest.ably.io', true)).to.deep.equal('realtime.ably.io'); + helper.recordPrivateApi('call.Defaults.getPort'); expect(Defaults.getPort(normalisedOptions)).to.equal(443); }); @@ -86,6 +98,9 @@ define(['ably', 'chai'], function (Ably, chai) { * @specpartial RTC1e - test with environment set other than 'production' */ it('Init with given environment', function () { + const helper = this.test.helper; + + helper.recordPrivateApi('call.Defaults.normaliseOptions'); var normalisedOptions = Defaults.normaliseOptions({ environment: 'sandbox' }, null, null); expect(normalisedOptions.restHost).to.equal('sandbox-rest.ably.io'); @@ -95,13 +110,16 @@ define(['ably', 'chai'], function (Ably, chai) { expect(normalisedOptions.fallbackHosts.sort()).to.deep.equal(Defaults.environmentFallbackHosts('sandbox').sort()); expect(normalisedOptions.tls).to.equal(true); + helper.recordPrivateApi('call.Defaults.getHosts'); expect(Defaults.getHosts(normalisedOptions).length).to.deep.equal(4); expect(Defaults.getHosts(normalisedOptions)[0]).to.deep.equal(normalisedOptions.restHost); + helper.recordPrivateApi('call.Defaults.getHost'); expect(Defaults.getHost(normalisedOptions, 'sandbox-rest.ably.io', false)).to.deep.equal('sandbox-rest.ably.io'); expect(Defaults.getHost(normalisedOptions, 'sandbox-rest.ably.io', true)).to.deep.equal( 'sandbox-realtime.ably.io', ); + helper.recordPrivateApi('call.Defaults.getPort'); expect(Defaults.getPort(normalisedOptions)).to.equal(443); }); @@ -120,6 +138,9 @@ define(['ably', 'chai'], function (Ably, chai) { * @specpartial RTC1e - test with environment set other than 'production' */ it('Init with local environment and non-default ports', function () { + const helper = this.test.helper; + + helper.recordPrivateApi('call.Defaults.normaliseOptions'); var normalisedOptions = Defaults.normaliseOptions( { environment: 'local', port: 8080, tlsPort: 8081 }, null, @@ -133,10 +154,13 @@ define(['ably', 'chai'], function (Ably, chai) { expect(normalisedOptions.fallbackHosts).to.equal(undefined); expect(normalisedOptions.tls).to.equal(true); + helper.recordPrivateApi('call.Defaults.getHosts'); expect(Defaults.getHosts(normalisedOptions)).to.deep.equal([normalisedOptions.restHost]); + helper.recordPrivateApi('call.Defaults.getHost'); expect(Defaults.getHost(normalisedOptions, 'local-rest.ably.io', false)).to.deep.equal('local-rest.ably.io'); expect(Defaults.getHost(normalisedOptions, 'local-rest.ably.io', true)).to.deep.equal('local-realtime.ably.io'); + helper.recordPrivateApi('call.Defaults.getPort'); expect(Defaults.getPort(normalisedOptions)).to.equal(8081); }); @@ -154,6 +178,9 @@ define(['ably', 'chai'], function (Ably, chai) { * @specpartial RSC11 - test restHost is overridden by custom value */ it('Init with given host', function () { + const helper = this.test.helper; + + helper.recordPrivateApi('call.Defaults.normaliseOptions'); var normalisedOptions = Defaults.normaliseOptions({ restHost: 'test.org' }, null, null); expect(normalisedOptions.restHost).to.equal('test.org'); @@ -163,10 +190,13 @@ define(['ably', 'chai'], function (Ably, chai) { expect(normalisedOptions.fallbackHosts).to.equal(undefined); expect(normalisedOptions.tls).to.equal(true); + helper.recordPrivateApi('call.Defaults.getHosts'); expect(Defaults.getHosts(normalisedOptions)).to.deep.equal([normalisedOptions.restHost]); + helper.recordPrivateApi('call.Defaults.getHost'); expect(Defaults.getHost(normalisedOptions, 'test.org', false)).to.deep.equal('test.org'); expect(Defaults.getHost(normalisedOptions, 'test.org', true)).to.deep.equal('test.org'); + helper.recordPrivateApi('call.Defaults.getPort'); expect(Defaults.getPort(normalisedOptions)).to.equal(443); }); @@ -183,6 +213,9 @@ define(['ably', 'chai'], function (Ably, chai) { * @specpartial RTN17a - primary host for realtime can be overridden by realtimeHost */ it('Init with given restHost and realtimeHost', function () { + const helper = this.test.helper; + + helper.recordPrivateApi('call.Defaults.normaliseOptions'); var normalisedOptions = Defaults.normaliseOptions( { restHost: 'test.org', realtimeHost: 'ws.test.org' }, null, @@ -196,10 +229,13 @@ define(['ably', 'chai'], function (Ably, chai) { expect(normalisedOptions.fallbackHosts).to.equal(undefined); expect(normalisedOptions.tls).to.equal(true); + helper.recordPrivateApi('call.Defaults.getHosts'); expect(Defaults.getHosts(normalisedOptions)).to.deep.equal([normalisedOptions.restHost]); + helper.recordPrivateApi('call.Defaults.getHost'); expect(Defaults.getHost(normalisedOptions, 'test.org', false)).to.deep.equal('test.org'); expect(Defaults.getHost(normalisedOptions, 'test.org', true)).to.deep.equal('ws.test.org'); + helper.recordPrivateApi('call.Defaults.getPort'); expect(Defaults.getPort(normalisedOptions)).to.equal(443); }); @@ -216,7 +252,11 @@ define(['ably', 'chai'], function (Ably, chai) { * @specpartial RSC15g2 - test with environment set other than 'production' */ it('Init with no endpoint-related options and given default environment', function () { + const helper = this.test.helper; + + helper.recordPrivateApi('write.Defaults.ENVIRONMENT'); Defaults.ENVIRONMENT = 'sandbox'; + helper.recordPrivateApi('call.Defaults.normaliseOptions'); var normalisedOptions = Defaults.normaliseOptions({}, null, null); expect(normalisedOptions.restHost).to.equal('sandbox-rest.ably.io'); @@ -226,14 +266,18 @@ define(['ably', 'chai'], function (Ably, chai) { expect(normalisedOptions.fallbackHosts.sort()).to.deep.equal(Defaults.environmentFallbackHosts('sandbox').sort()); expect(normalisedOptions.tls).to.equal(true); + helper.recordPrivateApi('call.Defaults.getHosts'); expect(Defaults.getHosts(normalisedOptions).length).to.equal(4); expect(Defaults.getHosts(normalisedOptions)[0]).to.deep.equal(normalisedOptions.restHost); + helper.recordPrivateApi('call.Defaults.getHost'); expect(Defaults.getHost(normalisedOptions, 'sandbox-rest.ably.io', false)).to.deep.equal('sandbox-rest.ably.io'); expect(Defaults.getHost(normalisedOptions, 'sandbox-rest.ably.io', true)).to.deep.equal( 'sandbox-realtime.ably.io', ); + helper.recordPrivateApi('call.Defaults.getPort'); expect(Defaults.getPort(normalisedOptions)).to.equal(443); + helper.recordPrivateApi('write.Defaults.ENVIRONMENT'); Defaults.ENVIRONMENT = ''; }); @@ -242,8 +286,10 @@ define(['ably', 'chai'], function (Ably, chai) { if (Ably.Realtime.Platform.Config.supportsBinary) { describe('given MsgPack implementation', () => { /** @spec TO3f */ - it('maintains useBinaryProtocol as true', () => { + it('maintains useBinaryProtocol as true', function () { + const helper = this.test.helper; const StubMsgPack = {}; + helper.recordPrivateApi('call.Defaults.normaliseOptions'); var normalisedOptions = Defaults.normaliseOptions({ useBinaryProtocol: true }, StubMsgPack, null); expect(normalisedOptions.useBinaryProtocol).to.be.true; @@ -253,7 +299,9 @@ define(['ably', 'chai'], function (Ably, chai) { describe('given no MsgPack implementation', () => { /** @spec TO3f */ - it('changes useBinaryProtocol to false', () => { + it('changes useBinaryProtocol to false', function () { + const helper = this.test.helper; + helper.recordPrivateApi('call.Defaults.normaliseOptions'); var normalisedOptions = Defaults.normaliseOptions({ useBinaryProtocol: true }, null, null); expect(normalisedOptions.useBinaryProtocol).to.be.false; @@ -266,8 +314,11 @@ define(['ably', 'chai'], function (Ably, chai) { * @nospec */ it('closeOnUnload', function () { + const helper = this.test.helper; var options; + helper.recordPrivateApi('call.Defaults.normaliseOptions'); + /* Default to true */ options = Defaults.normaliseOptions({}, null, null); expect(options.closeOnUnload).to.equal(true); diff --git a/test/rest/fallbacks.test.js b/test/rest/fallbacks.test.js index b817e8111a..b6a77507b6 100644 --- a/test/rest/fallbacks.test.js +++ b/test/rest/fallbacks.test.js @@ -30,26 +30,34 @@ define(['shared_helper', 'async', 'chai'], function (Helper, async, chai) { var validUntil; var serverTime = await rest.time(); expect(serverTime, 'Check serverTime returned').to.be.ok; + helper.recordPrivateApi('read.rest._currentFallback'); var currentFallback = rest._currentFallback; expect(currentFallback, 'Check current fallback stored').to.be.ok; + helper.recordPrivateApi('read.rest._currentFallback.host'); expect(currentFallback && currentFallback.host).to.equal(goodHost, 'Check good host set'); + helper.recordPrivateApi('read.rest._currentFallback.validUntil'); validUntil = currentFallback.validUntil; /* now try again, check that this time it uses the remembered good endpoint straight away */ var serverTime = await rest.time(); expect(serverTime, 'Check serverTime returned').to.be.ok; var currentFallback = rest._currentFallback; + helper.recordPrivateApi('read.rest._currentFallback.validUntil'); expect(currentFallback.validUntil).to.equal( validUntil, 'Check validUntil is the same (implying currentFallback has not been re-set)', ); /* set the validUntil to the past and check that the stored fallback is forgotten */ var now = Date.now(); + helper.recordPrivateApi('write.rest._currentFallback.validUntil'); rest._currentFallback.validUntil = now - 1000; var serverTime = await rest.time(); expect(serverTime, 'Check serverTime returned').to.be.ok; + helper.recordPrivateApi('read.rest._currentFallback'); var currentFallback = rest._currentFallback; expect(currentFallback, 'Check current fallback re-stored').to.be.ok; + helper.recordPrivateApi('read.rest._currentFallback.host'); expect(currentFallback && currentFallback.host).to.equal(goodHost, 'Check good host set again'); + helper.recordPrivateApi('read.rest._currentFallback.validUntil'); expect(currentFallback.validUntil > now, 'Check validUntil has been re-set').to.be.ok; }); diff --git a/test/rest/history.test.js b/test/rest/history.test.js index 105f5cba4b..bc26ebbc0f 100644 --- a/test/rest/history.test.js +++ b/test/rest/history.test.js @@ -51,6 +51,7 @@ define(['shared_helper', 'async', 'chai'], function (Helper, async, chai) { messages.forEach(function (msg) { ids[msg.id] = msg; }); + helper.recordPrivateApi('call.Utils.keysArray'); expect(helper.Utils.keysArray(ids).length).to.equal( testMessages.length, 'Verify correct number of distinct message ids found', @@ -79,6 +80,7 @@ define(['shared_helper', 'async', 'chai'], function (Helper, async, chai) { messages.forEach(function (msg) { ids[msg.id] = msg; }); + helper.recordPrivateApi('call.Utils.keysArray'); expect(helper.Utils.keysArray(ids).length).to.equal( testMessages.length, 'Verify correct number of distinct message ids found', @@ -122,6 +124,7 @@ define(['shared_helper', 'async', 'chai'], function (Helper, async, chai) { } } /* verify message ids are unique */ + helper.recordPrivateApi('call.Utils.keysArray'); expect(helper.Utils.keysArray(ids).length).to.equal( testMessages.length, 'Verify correct number of distinct message ids found', @@ -166,6 +169,7 @@ define(['shared_helper', 'async', 'chai'], function (Helper, async, chai) { } /* verify message ids are unique */ + helper.recordPrivateApi('call.Utils.keysArray'); expect(helper.Utils.keysArray(ids).length).to.equal( testMessages.length, 'Verify correct number of distinct message ids found', @@ -247,6 +251,7 @@ define(['shared_helper', 'async', 'chai'], function (Helper, async, chai) { } /* verify message ids are unique */ + helper.recordPrivateApi('call.Utils.keysArray'); expect(helper.Utils.keysArray(ids).length).to.equal( testMessages.length, 'Verify correct number of distinct message ids found', diff --git a/test/rest/http.test.js b/test/rest/http.test.js index 2ffd375f01..054972525a 100644 --- a/test/rest/http.test.js +++ b/test/rest/http.test.js @@ -24,6 +24,7 @@ define(['ably', 'shared_helper', 'chai'], function (Ably, Helper, chai) { * @specpartial RSC7d2 - tests Ably-Agent only for http */ it('Should send X-Ably-Version and Ably-Agent headers in get/post requests', async function () { + const helper = this.test.helper; var originalDo = rest.http.do; // Intercept Http.do with test @@ -34,6 +35,7 @@ define(['ably', 'shared_helper', 'chai'], function (Ably, Helper, chai) { // This test should not directly validate version against Defaults.version, as // ultimately the version header has been derived from that value. expect(headers['X-Ably-Version']).to.equal('3', 'Verify current version number'); + helper.recordPrivateApi('read.Defaults.version'); expect(headers['Ably-Agent'].indexOf('ably-js/' + Defaults.version) > -1, 'Verify agent').to.be.ok; expect(headers['Ably-Agent'].indexOf('custom-agent/0.1.2') > -1, 'Verify custom agent').to.be.ok; @@ -49,9 +51,11 @@ define(['ably', 'shared_helper', 'chai'], function (Ably, Helper, chai) { expect(headers['Ably-Agent'].indexOf('nodejs') > -1, 'Verify agent').to.be.ok; } + helper.recordPrivateApi('call.rest.http.do'); return originalDo.call(rest.http, method, path, headers, body, params); } + helper.recordPrivateApi('replace.rest.http.do'); rest.http.do = testRequestHandler; // Call all methods that use rest http calls @@ -65,12 +69,14 @@ define(['ably', 'shared_helper', 'chai'], function (Ably, Helper, chai) { /** @nospec */ it('Should handle no content responses', async function () { + const helper = this.test.helper; //Intercept Http.do with test async function testRequestHandler() { return { error: null, body: null, headers: { 'X-Ably-Foo': 'headerValue' }, unpacked: false, statusCode: 204 }; } + helper.recordPrivateApi('replace.rest.http.do'); rest.http.do = testRequestHandler; const response = await rest.request('GET', '/foo', {}, null, {}); diff --git a/test/rest/init.test.js b/test/rest/init.test.js index 00cb1a44a3..bc91b030ea 100644 --- a/test/rest/init.test.js +++ b/test/rest/init.test.js @@ -26,6 +26,7 @@ define(['ably', 'shared_helper', 'chai'], function (Ably, Helper, chai) { var keyStr = helper.getTestApp().keys[0].keyStr; var rest = new helper.Ably.Rest(keyStr); + helper.recordPrivateApi('read.rest.options.key'); expect(rest.options.key).to.equal(keyStr); }); @@ -43,6 +44,7 @@ define(['ably', 'shared_helper', 'chai'], function (Ably, Helper, chai) { var tokenStr = tokenDetails.token, rest = new helper.Ably.Rest(tokenStr); + helper.recordPrivateApi('read.rest.options.token'); expect(rest.options.token).to.equal(tokenStr); }); @@ -55,6 +57,7 @@ define(['ably', 'shared_helper', 'chai'], function (Ably, Helper, chai) { it('Init with tls: false', function () { const helper = this.test.helper; var rest = helper.AblyRest({ tls: false, port: 123, tlsPort: 456 }); + helper.recordPrivateApi('call.rest.baseUri'); expect(rest.baseUri('example.com')).to.equal('http://example.com:123'); }); @@ -66,6 +69,7 @@ define(['ably', 'shared_helper', 'chai'], function (Ably, Helper, chai) { it('Init with tls: true', function () { const helper = this.test.helper; var rest = helper.AblyRest({ tls: true, port: 123, tlsPort: 456 }); + helper.recordPrivateApi('call.rest.baseUri'); expect(rest.baseUri('example.com')).to.equal('https://example.com:456'); }); @@ -80,6 +84,7 @@ define(['ably', 'shared_helper', 'chai'], function (Ably, Helper, chai) { it('Init without any tls key should enable tls', function () { const helper = this.test.helper; var rest = helper.AblyRest({ port: 123, tlsPort: 456 }); + helper.recordPrivateApi('call.rest.baseUri'); expect(rest.baseUri('example.com')).to.equal('https://example.com:456'); }); diff --git a/test/rest/message.test.js b/test/rest/message.test.js index 4eac3391e4..495ed26038 100644 --- a/test/rest/message.test.js +++ b/test/rest/message.test.js @@ -30,10 +30,12 @@ define(['ably', 'shared_helper', 'async', 'chai'], function (Ably, Helper, async channel = rest.channels.get('rest_implicit_client_id_0'); var originalPublish = channel._publish; + helper.recordPrivateApi('replace.restChannel._publish'); channel._publish = async function (requestBody) { var message = JSON.parse(requestBody)[0]; expect(message.name === 'event0', 'Outgoing message interecepted').to.be.ok; expect(!message.clientId, 'client ID is not added by the client library as it is implicit').to.be.ok; + helper.recordPrivateApi('call.restChannel._publish'); return originalPublish.apply(channel, arguments); }; @@ -57,6 +59,7 @@ define(['ably', 'shared_helper', 'async', 'chai'], function (Ably, Helper, async channel = rest.channels.get('rest_explicit_client_id_0'); var originalPublish = channel._publish; + helper.recordPrivateApi('replace.restChannel._publish'); channel._publish = async function (requestBody) { var message = JSON.parse(requestBody)[0]; expect(message.name === 'event0', 'Outgoing message interecepted').to.be.ok; @@ -64,6 +67,7 @@ define(['ably', 'shared_helper', 'async', 'chai'], function (Ably, Helper, async message.clientId == clientId, 'client ID is added by the client library as it is explicit in the publish', ).to.be.ok; + helper.recordPrivateApi('call.restChannel._publish'); return originalPublish.apply(channel, arguments); }; @@ -92,6 +96,7 @@ define(['ably', 'shared_helper', 'async', 'chai'], function (Ably, Helper, async channel = rest.channels.get('rest_explicit_client_id_1'); var originalPublish = channel._publish; + helper.recordPrivateApi('replace.restChannel._publish'); channel._publish = async function (requestBody) { var message = JSON.parse(requestBody)[0]; expect(message.name === 'event0', 'Outgoing message interecepted').to.be.ok; @@ -99,6 +104,7 @@ define(['ably', 'shared_helper', 'async', 'chai'], function (Ably, Helper, async message.clientId == invalidClientId, 'invalid client ID is added by the client library as it is explicit in the publish', ).to.be.ok; + helper.recordPrivateApi('call.restChannel._publish'); return originalPublish.apply(channel, arguments); }; @@ -180,6 +186,7 @@ define(['ably', 'shared_helper', 'async', 'chai'], function (Ably, Helper, async originalPublish = channel._publish, originalDoUri = Ably.Realtime._Http.doUri; + helper.recordPrivateApi('replace.restChannel._publish'); channel._publish = async function (requestBody) { var messageOne = JSON.parse(requestBody)[0]; var messageTwo = JSON.parse(requestBody)[1]; @@ -191,11 +198,15 @@ define(['ably', 'shared_helper', 'async', 'chai'], function (Ably, Helper, async expect(idTwo, 'id set on message 2').to.be.ok; expect(idOne && idOne.split(':')[1]).to.equal('0', 'check zero-based index'); expect(idTwo && idTwo.split(':')[1]).to.equal('1', 'check zero-based index'); + helper.recordPrivateApi('call.restChannel._publish'); return originalPublish.apply(channel, arguments); }; + helper.recordPrivateApi('replace.http.doUri'); Ably.Rest._Http.doUri = async function (method, uri, headers, body, params) { + helper.recordPrivateApi('call.http.doUri'); const resultPromise = originalDoUri(method, uri, headers, body, params); + helper.recordPrivateApi('replace.http.doUri'); Ably.Rest._Http.doUri = originalDoUri; const result = await resultPromise; if (result.error) { @@ -232,8 +243,10 @@ define(['ably', 'shared_helper', 'async', 'chai'], function (Ably, Helper, async var originalPublish = channel._publish; /* Stub out _publish to check params */ + helper.recordPrivateApi('replace.restChannel._publish'); channel._publish = async function (requestBody, headers, params) { expect(params && params.testParam).to.equal('testParamValue'); + helper.recordPrivateApi('call.restChannel._publish'); return originalPublish.apply(channel, arguments); }; diff --git a/test/rest/presence.test.js b/test/rest/presence.test.js index 21ebbe0dfd..4f2bfae461 100644 --- a/test/rest/presence.test.js +++ b/test/rest/presence.test.js @@ -7,13 +7,15 @@ define(['ably', 'shared_helper', 'async', 'chai'], function (Ably, Helper, async var Crypto = Ably.Realtime.Platform.Crypto; var BufferUtils = Ably.Realtime.Platform.BufferUtils; - function cipherParamsFromConfig(cipherConfig) { + function cipherParamsFromConfig(cipherConfig, helper) { + helper.recordPrivateApi('new.Crypto.CipherParams'); var cipherParams = new Crypto.CipherParams(); for (var prop in cipherConfig) { cipherParams[prop] = cipherConfig[prop]; } cipherParams.keyLength = cipherConfig.keylength; delete cipherParams.keylength; // grr case differences + helper.recordPrivateApi('call.BufferUtils.base64Decode'); cipherParams.key = BufferUtils.base64Decode(cipherParams.key); cipherParams.iv = BufferUtils.base64Decode(cipherParams.iv); return cipherParams; @@ -33,7 +35,8 @@ define(['ably', 'shared_helper', 'async', 'chai'], function (Ably, Helper, async function presence_simple(operation) { return async function () { - var cipherParams = cipherParamsFromConfig(cipherConfig); + const helper = this.test.helper.withParameterisedTestTitle('presence_simple'); + var cipherParams = cipherParamsFromConfig(cipherConfig, helper); var channel = rest.channels.get('persisted:presence_fixtures', { cipher: cipherParams }); var resultPage = await channel.presence[operation](); var presenceMessages = resultPage.items; diff --git a/test/rest/push.test.js b/test/rest/push.test.js index 0b958d5db7..591d7f4bcf 100644 --- a/test/rest/push.test.js +++ b/test/rest/push.test.js @@ -9,7 +9,7 @@ define(['ably', 'shared_helper', 'async', 'chai', 'test/support/push_channel_tra PushPlugin, ) { var expect = chai.expect; - var originalPushConfig = Ably.Realtime.Platform.Config.push; + var originalPushConfig; function PushRealtime(helper, options) { return helper.AblyRealtime({ ...options, plugins: { Push: PushPlugin } }); @@ -49,7 +49,12 @@ define(['ably', 'shared_helper', 'async', 'chai', 'test/support/push_channel_tra before(function (done) { const helper = Helper.forHook(this); + + helper.recordPrivateApi('read.Platform.Config.push'); + originalPushConfig = Ably.Realtime.Platform.Config.push; + helper.recordPrivateApi('write.Platform.Config.push'); Ably.Realtime.Platform.Config.push = pushChannelTransport; + helper.setupApp(function (err) { if (err) { done(err); @@ -60,10 +65,16 @@ define(['ably', 'shared_helper', 'async', 'chai', 'test/support/push_channel_tra }); beforeEach(() => { + const helper = Helper.forHook(this); + + helper.recordPrivateApi('call.Platform.Config.push.storage.clear'); Ably.Realtime.Platform.Config.push.storage.clear(); }); after(() => { + const helper = Helper.forHook(this); + + helper.recordPrivateApi('write.Platform.Config.push'); Ably.Realtime.Platform.Config.push = originalPushConfig; }); @@ -117,6 +128,9 @@ define(['ably', 'shared_helper', 'async', 'chai', 'test/support/push_channel_tra data: { foo: 'bar' }, }; + helper.recordPrivateApi('read.realtime.options'); + helper.recordPrivateApi('call.Defaults.getHost'); + helper.recordPrivateApi('call.realtime.baseUri'); var baseUri = realtime.baseUri(Ably.Rest.Platform.Defaults.getHost(realtime.options)); var pushRecipient = { transportType: 'ablyChannel', @@ -376,7 +390,10 @@ define(['ably', 'shared_helper', 'async', 'chai', 'test/support/push_channel_tra describe('push activation', function () { /** @spec RSH2a */ it('push_activation_succeeds', async function () { - const rest = PushRealtime(this.test.helper, { pushRecipientChannel: 'my_channel' }); + const helper = this.test.helper; + + helper.recordPrivateApi('pass.clientOption.pushRecipientChannel'); + const rest = PushRealtime(helper, { pushRecipientChannel: 'my_channel' }); await rest.push.activate(); expect(rest.device.deviceIdentityToken).to.be.ok; }); @@ -386,6 +403,7 @@ define(['ably', 'shared_helper', 'async', 'chai', 'test/support/push_channel_tra const helper = this.test.helper; const channelName = 'pushenabled:device_push'; + helper.recordPrivateApi('pass.clientOption.pushRecipientChannel'); const realtime = PushRealtime(helper, { pushRecipientChannel: channelName }); const pushPayload = { @@ -395,8 +413,11 @@ define(['ably', 'shared_helper', 'async', 'chai', 'test/support/push_channel_tra const channel = realtime.channels.get(channelName); + helper.recordPrivateApi('call.Defaults.getHost'); + helper.recordPrivateApi('read.realtime.options'); const baseUri = realtime.baseUri(Ably.Rest.Platform.Defaults.getHost(realtime.options)); + helper.recordPrivateApi('read.realtime.options'); const pushRecipient = { transportType: 'ablyChannel', channel: channelName, @@ -431,6 +452,7 @@ define(['ably', 'shared_helper', 'async', 'chai', 'test/support/push_channel_tra const clientId = 'me'; const channelName = 'pushenabled:subscribe_client'; + helper.recordPrivateApi('pass.clientOption.pushRecipientChannel'); const rest = PushRest(helper, { clientId, pushRecipientChannel: channelName }); const channel = rest.channels.get(channelName); @@ -450,6 +472,7 @@ define(['ably', 'shared_helper', 'async', 'chai', 'test/support/push_channel_tra const helper = this.test.helper; const channelName = 'pushenabled:subscribe_client_without_clientId'; + helper.recordPrivateApi('pass.clientOption.pushRecipientChannel'); const rest = PushRest(helper, { pushRecipientChannel: 'hello' }); await rest.push.activate(); const channel = rest.channels.get(channelName); @@ -469,6 +492,7 @@ define(['ably', 'shared_helper', 'async', 'chai', 'test/support/push_channel_tra const clientId = 'me'; const channelName = 'pushenabled:unsubscribe_client'; + helper.recordPrivateApi('pass.clientOption.pushRecipientChannel'); const rest = PushRest(helper, { clientId, pushRecipientChannel: channelName }); const channel = rest.channels.get(channelName); @@ -489,6 +513,7 @@ define(['ably', 'shared_helper', 'async', 'chai', 'test/support/push_channel_tra const clientId = 'me2'; const channelName = 'pushenabled:direct_publish_client_id'; + helper.recordPrivateApi('pass.clientOption.pushRecipientChannel'); const rest = PushRest(helper, { clientId, pushRecipientChannel: channelName }); const realtime = PushRealtime(helper); const rtChannel = realtime.channels.get(channelName); @@ -526,6 +551,7 @@ define(['ably', 'shared_helper', 'async', 'chai', 'test/support/push_channel_tra const helper = this.test.helper; const channelName = 'pushenabled:subscribe_device'; + helper.recordPrivateApi('pass.clientOption.pushRecipientChannel'); const rest = PushRest(helper, { pushRecipientChannel: channelName }); const channel = rest.channels.get(channelName); @@ -545,6 +571,7 @@ define(['ably', 'shared_helper', 'async', 'chai', 'test/support/push_channel_tra const helper = this.test.helper; const channelName = 'pushenabled:unsubscribe_device'; + helper.recordPrivateApi('pass.clientOption.pushRecipientChannel'); const rest = PushRest(helper, { pushRecipientChannel: channelName }); const channel = rest.channels.get(channelName); @@ -564,6 +591,7 @@ define(['ably', 'shared_helper', 'async', 'chai', 'test/support/push_channel_tra const helper = this.test.helper; const channelName = 'direct_publish_device_id'; + helper.recordPrivateApi('pass.clientOption.pushRecipientChannel'); const rest = PushRest(helper, { pushRecipientChannel: channelName }); const realtime = PushRealtime(helper); const rtChannel = realtime.channels.get(channelName); @@ -602,6 +630,7 @@ define(['ably', 'shared_helper', 'async', 'chai', 'test/support/push_channel_tra const pushRecipientChannel = 'push_channel_subscription_device_id'; const channelName = 'pushenabled:push_channel_subscription_device_id'; + helper.recordPrivateApi('pass.clientOption.pushRecipientChannel'); const rest = PushRest(helper, { pushRecipientChannel }); const realtime = PushRealtime(helper); const channel = rest.channels.get(channelName); @@ -645,6 +674,7 @@ define(['ably', 'shared_helper', 'async', 'chai', 'test/support/push_channel_tra const pushRecipientChannel = 'push_channel_subscription_client_id'; const channelName = 'pushenabled:push_channel_subscription_client_id'; + helper.recordPrivateApi('pass.clientOption.pushRecipientChannel'); const rest = PushRest(helper, { clientId: 'me', pushRecipientChannel }); const realtime = PushRealtime(helper); const channel = rest.channels.get(channelName); @@ -697,8 +727,11 @@ define(['ably', 'shared_helper', 'async', 'chai', 'test/support/push_channel_tra /** @spec RSH3b3c */ it('failed_registration', async function () { + const helper = this.test.helper; + const pushRecipientChannel = 'failed_registration'; - const rest = PushRest(this.test.helper, { pushRecipientChannel }); + helper.recordPrivateApi('pass.clientOption.pushRecipientChannel'); + const rest = PushRest(helper, { pushRecipientChannel }); rest.device.platform = 'not_a_real_platform'; try { await rest.push.activate(); diff --git a/test/rest/request.test.js b/test/rest/request.test.js index 541e42386d..7132b0c5ef 100644 --- a/test/rest/request.test.js +++ b/test/rest/request.test.js @@ -28,7 +28,7 @@ define(['ably', 'shared_helper', 'async', 'chai'], function (Ably, Helper, async * @specpartial RSC7e - tests providing a version value in .request parameters * @specpartial CSV2c - tests version is provided in http requests */ - Helper.restTestOnJsonMsgpack('request_version', function (rest) { + Helper.restTestOnJsonMsgpack('request_version', function (rest, _, helper) { const version = 150; // arbitrarily chosen async function testRequestHandler(_, __, headers) { @@ -42,6 +42,7 @@ define(['ably', 'shared_helper', 'async', 'chai'], function (Ably, Helper, async return new Promise(() => {}); } + helper.recordPrivateApi('replace.rest.http.do'); rest.http.do = testRequestHandler; rest.request('get', '/time' /* arbitrarily chosen */, version, null, null, null); @@ -212,7 +213,7 @@ define(['ably', 'shared_helper', 'async', 'chai'], function (Ably, Helper, async ['put', 'patch', 'delete'].forEach(function (method) { /** @specpartial RSC19f - tests put, patch, delete methods are supported */ it('check' + method, async function () { - const helper = this.test.helper; + const helper = this.test.helper.withParameterisedTestTitle('check'); var restEcho = helper.AblyRest({ useBinaryProtocol: false, restHost: echoServerHost, tls: true }); var res = await restEcho.request(method, '/methods', 3, {}, {}, {}); expect(res.items[0] && res.items[0].method).to.equal(method); diff --git a/test/support/junit_directory_path.js b/test/support/junit_directory_path.js deleted file mode 100644 index d89d55cbd4..0000000000 --- a/test/support/junit_directory_path.js +++ /dev/null @@ -1,3 +0,0 @@ -const path = require('path'); - -module.exports = path.join(__dirname, '..', '..', 'junit'); diff --git a/test/support/mocha_reporter.js b/test/support/mocha_reporter.js index feb56fc687..e85e26b0ba 100644 --- a/test/support/mocha_reporter.js +++ b/test/support/mocha_reporter.js @@ -1,7 +1,7 @@ const Mocha = require('mocha'); const MochaJUnitReporter = require('mocha-junit-reporter'); const path = require('path'); -const jUnitDirectoryPath = require('./junit_directory_path'); +const outputDirectoryPaths = require('./output_directory_paths'); /** * Logs test results to the console (by extending the default `Spec` reporter) and also emits a JUnit XML file. @@ -12,7 +12,7 @@ class Reporter extends Mocha.reporters.Spec { constructor(runner, options) { super(runner, options); const jUnitFileName = `node-${process.version.split('.')[0]}.junit`; - const jUnitFilePath = path.join(jUnitDirectoryPath, jUnitFileName); + const jUnitFilePath = path.join(outputDirectoryPaths.jUnit, jUnitFileName); this.jUnitReporter = new MochaJUnitReporter(runner, { reporterOptions: { mochaFile: jUnitFilePath } }); } } diff --git a/test/support/output_directory_paths.js b/test/support/output_directory_paths.js new file mode 100644 index 0000000000..d1072fa036 --- /dev/null +++ b/test/support/output_directory_paths.js @@ -0,0 +1,6 @@ +const path = require('path'); + +module.exports = { + jUnit: path.join(__dirname, '..', '..', 'junit'), + privateApiUsage: path.join(__dirname, '..', '..', 'private-api-usage'), +}; diff --git a/test/support/playwrightSetup.js b/test/support/playwrightSetup.js index 99a0d68b7a..ee6b1bdd82 100644 --- a/test/support/playwrightSetup.js +++ b/test/support/playwrightSetup.js @@ -17,6 +17,7 @@ class CustomEventReporter extends Mocha.reporters.HTML { jUnitReporter; jUnitReport; testResultStats; + privateApiUsageData; constructor(runner) { super(runner); @@ -53,6 +54,11 @@ class CustomEventReporter extends Mocha.reporters.HTML { this.testResultStats = runner.stats; this.gotResults(); }); + + window.addEventListener('privateApiUsageData', ({ detail }) => { + this.privateApiUsageData = detail; + this.gotResults(); + }); } logToNodeConsole(text) { @@ -72,7 +78,7 @@ class CustomEventReporter extends Mocha.reporters.HTML { } gotResults() { - if (!this.testResultStats || !this.jUnitReport) { + if (!this.testResultStats || !this.jUnitReport || !this.privateApiUsageData) { return; } @@ -83,6 +89,7 @@ class CustomEventReporter extends Mocha.reporters.HTML { passes: this.testResultStats.passes, total: this.testResultStats.passes + this.testResultStats.failures, jUnitReport: this.jUnitReport, + privateApiUsageData: this.privateApiUsageData, }, }), ); diff --git a/test/support/root_hooks.js b/test/support/root_hooks.js index a3e999c333..0f1232c397 100644 --- a/test/support/root_hooks.js +++ b/test/support/root_hooks.js @@ -9,6 +9,7 @@ define(['shared_helper'], function (Helper) { } done(); }); + helper.dumpPrivateApiUsage(); }); afterEach(function () { @@ -22,6 +23,7 @@ define(['shared_helper'], function (Helper) { }); beforeEach(function () { this.currentTest.helper = Helper.forTest(this); + this.currentTest.helper.recordTestStart(); }); beforeEach(function () { this.currentTest.helper.clearTransportPreference(); diff --git a/test/support/runPlaywrightTests.js b/test/support/runPlaywrightTests.js index 07f580ae30..be73457d7e 100644 --- a/test/support/runPlaywrightTests.js +++ b/test/support/runPlaywrightTests.js @@ -2,7 +2,7 @@ const playwright = require('playwright'); const path = require('path'); const MochaServer = require('../web_server'); const fs = require('fs'); -const jUnitDirectoryPath = require('./junit_directory_path'); +const outputDirectoryPaths = require('./output_directory_paths'); const port = process.env.PORT || 3000; const host = 'localhost'; @@ -33,16 +33,31 @@ const runTests = async (browserType) => { console.log(`${browserType.name()} tests complete: ${detail.passes}/${detail.total} passed`); try { - if (!fs.existsSync(jUnitDirectoryPath)) { - fs.mkdirSync(jUnitDirectoryPath); + if (!fs.existsSync(outputDirectoryPaths.jUnit)) { + fs.mkdirSync(outputDirectoryPaths.jUnit); } const filename = `playwright-${browserType.name()}.junit`; - fs.writeFileSync(path.join(jUnitDirectoryPath, filename), detail.jUnitReport, { encoding: 'utf-8' }); + fs.writeFileSync(path.join(outputDirectoryPaths.jUnit, filename), detail.jUnitReport, { encoding: 'utf-8' }); } catch (err) { console.log('Failed to write JUnit report, exiting with code 2: ', err); process.exit(2); } + try { + if (!fs.existsSync(outputDirectoryPaths.privateApiUsage)) { + fs.mkdirSync(outputDirectoryPaths.privateApiUsage); + } + const filename = `playwright-${browserType.name()}.json`; + fs.writeFileSync( + path.join(outputDirectoryPaths.privateApiUsage, filename), + JSON.stringify(detail.privateApiUsageData), + { encoding: 'utf-8' }, + ); + } catch (err) { + console.log('Failed to write private API usage data, exiting with code 2: ', err); + process.exit(2); + } + if (detail.pass) { browser.close(); resolve();