diff --git a/changelog.md b/changelog.md index b45d614909..ce8d0bc45f 100644 --- a/changelog.md +++ b/changelog.md @@ -19,14 +19,18 @@ and maybe more. Bugfixes - Fixed auth in batch not saving the password +- Fixed catch tls handshake failures with newer Node.js versions +- Fixed stream option being copied +- Fixed pub sub activated, listening to a `(p)messageBuffer`, no Buffer returns + and monitoring resulting in a crash. Features - Native promise support - Auto pipelining +- The client is now exported directly and be instantiated directly Breaking Changes -- Dropped support for `UPPER_CASE` commands - Dropped support for `snake_case` - Dropped support for domains - Dropped support for Redis 2.4 @@ -36,9 +40,7 @@ Breaking Changes - Removed `parser` option - Removed `retryMaxDelay` (max_delay) option - Removed `maxAttempts` (max_attempts) option -- Removed `socketNoDelay` (socket_no_delay) option -- Removed `authPass` (auth_pass) option. Please use `password` instead -- Removed `Redis.print` helper function +- Removed `socketNoDelay` (socket\_no_delay) option - Removed backpressure indicator from function return value - Removed the `stream` parameter from the RedisClient constructor. - Please set the stream in the options instead @@ -60,11 +62,12 @@ Breaking Changes - `restore-asking` is now `restore_asking_` - `host:` is now `host` - Changed the `serverInfo` into a nested object and to parse numbers -- Changed the `serverInfo.versions` to `serverInfo.version` +- Changed the `serverInfo.versions` to `serverInfo.server.version` - Changed the `message` and `pmessage` listener to always return a string - If you want to receive a buffer, please listen to the `messageBuffer` or `pmessageBuffer` - Using `.end` without the flush parameter is now going to throw an TypeError - Only emit ready when all commands were truly send to Redis +- `UPPER_CASE` commands are not enumerated anymore ## v.2.7.2 - 14 Mar, 2017 diff --git a/index.js b/index.js index 3218bd7121..fca8a92494 100644 --- a/index.js +++ b/index.js @@ -20,6 +20,7 @@ const offlineCommand = require('./lib/offlineCommand') const utils = require('./lib/utils') const normalizeAndWriteCommand = require('./lib/writeCommands') const noop = function () {} +var connectionId = 0 // Attention: The second parameter might be removed at will and is not officially supported. // Do not rely on this @@ -34,8 +35,8 @@ class RedisClient extends EventEmitter { super() // Copy the options so they are not mutated options = utils.clone(options) + // TODO: Add a more restrictive options validation const cnxOptions = {} - /* istanbul ignore next: travis does not work with stunnel atm. Therefore the tls tests are skipped on travis */ for (const tlsOption in options.tls) { if (options.tls.hasOwnProperty(tlsOption)) { cnxOptions[tlsOption] = options.tls[tlsOption] @@ -140,6 +141,8 @@ class RedisClient extends EventEmitter { // Do not call internalSendCommand directly, if you are not absolutely certain it handles everything properly // e.g. monitor / info does not work with internalSendCommand only // TODO: Move this function out of the client as a private function + // TODO: Check how others can intercept (monkey patch) essential parts (e.g. opbeat) + // after making this private. internalSendCommand (commandObj) { if (this.ready === false || this._stream.writable === false) { // Handle offline commands right away @@ -154,7 +157,6 @@ class RedisClient extends EventEmitter { } // Handle `CLIENT REPLY ON|OFF|SKIP` // This has to be checked after callOnWrite - /* istanbul ignore else: TODO: Remove this as soon as we test Redis 3.2 on travis */ if (this._reply === 'ON') { this.commandQueue.push(commandObj) } else { @@ -229,6 +231,10 @@ class RedisClient extends EventEmitter { } // TODO: promisify this + // This can not be done without removing support to return the client sync. + // This would be another BC and it should be fine to return the client sync. + // Therefore a option could be to accept a resolved promise instead of a callback + // to return a promise. duplicate (options, callback) { if (typeof options === 'function') { callback = options @@ -261,33 +267,31 @@ class RedisClient extends EventEmitter { // Note: this overrides a native function! multi (args) { - return new Multi(this, 'multi', args) + return new Multi(this, args, 'multi') } - // Note: This is not a native function but is still handled as a individual command as it behaves just the same as multi batch (args) { - return new Multi(this, 'batch', args) + return new Multi(this, args, 'batch') } } -RedisClient.connectionId = 0 +RedisClient.debugMode = /\bredis\b/i.test(process.env.NODE_DEBUG) +RedisClient.RedisClient = RedisClient +RedisClient.Multi = Multi +RedisClient.AbortError = Errors.AbortError +RedisClient.ParserError = Errors.ParserError +RedisClient.RedisError = Errors.RedisError +RedisClient.ReplyError = Errors.ReplyError +RedisClient.InterruptError = Errors.InterruptError +RedisClient.print = utils.print +RedisClient.createClient = function () { + return new RedisClient(unifyOptions.apply(null, arguments)) +} Commands.list.forEach((name) => addCommand(RedisClient.prototype, Multi.prototype, name)) -module.exports = { - debugMode: /\bredis\b/i.test(process.env.NODE_DEBUG), - RedisClient, - Multi, - AbortError: Errors.AbortError, - ParserError: Errors.ParserError, - RedisError: Errors.RedisError, - ReplyError: Errors.ReplyError, - InterruptError: Errors.InterruptError, - createClient () { - return new RedisClient(unifyOptions.apply(null, arguments)) - } -} +module.exports = RedisClient // Add all redis commands / nodeRedis api to the client // TODO: Change the way this is included... diff --git a/lib/commands.js b/lib/commands.js index b6ecb68d7b..14566c2ec3 100644 --- a/lib/commands.js +++ b/lib/commands.js @@ -23,6 +23,12 @@ function addCommand (clientProto, multiProto, command) { }) } } + Object.defineProperty(clientProto, commandName.toUpperCase(), { + enumerable: false, + configurable: false, + writable: false, + value: clientProto[commandName] + }) // Do not override existing functions if (!multiProto[command] && command !== 'multi') { @@ -41,6 +47,12 @@ function addCommand (clientProto, multiProto, command) { }) } } + Object.defineProperty(multiProto, commandName.toUpperCase(), { + enumerable: false, + configurable: false, + writable: false, + value: clientProto[commandName] + }) } module.exports = addCommand diff --git a/lib/connect.js b/lib/connect.js index 039bbb4f0c..bd698e7711 100644 --- a/lib/connect.js +++ b/lib/connect.js @@ -10,9 +10,9 @@ const replyHandler = require('./replyHandler') const onResult = replyHandler.onResult const onError = replyHandler.onError -var reconnect = function (client, why, err) { - reconnect = require('./reconnect') - reconnect(client, why, err) +var lazyReconnect = function (client, why, err) { + lazyReconnect = require('./reconnect') + lazyReconnect(client, why, err) } function onStreamError (client, err) { @@ -31,7 +31,7 @@ function onStreamError (client, err) { } // 'error' events get turned into exceptions if they aren't listened for. If the user handled this error // then we should try to reconnect. - reconnect(client, 'error', err) + lazyReconnect(client, 'error', err) } /** @@ -91,9 +91,19 @@ function connect (client) { client._stream.destroy() } - /* istanbul ignore if: travis does not work with stunnel atm. Therefore the tls tests are skipped on travis */ if (client._options.tls) { client._stream = tls.connect(client._connectionOptions) + + // Whenever a handshake times out. + // Older Node.js versions use "clientError", newer versions use tlsClientError. + stream.once('clientError', (err) => { + debug('clientError occurred') + onStreamError(client, err) + }) + stream.once('tlsClientError', (err) => { + debug('clientError occurred') + onStreamError(client, err) + }) } else { client._stream = net.createConnection(client._connectionOptions) } @@ -105,11 +115,10 @@ function connect (client) { // TODO: Investigate why this is not properly triggered stream.setTimeout(client._options.connectTimeout, () => { // Note: This is only tested if a internet connection is established - reconnect(client, 'timeout') + lazyReconnect(client, 'timeout') }) } - /* istanbul ignore next: travis does not work with stunnel atm. Therefore the tls tests are skipped on travis */ const connectEvent = client._options.tls ? 'secureConnect' : 'connect' stream.once(connectEvent, () => { stream.removeAllListeners('timeout') @@ -126,18 +135,12 @@ function connect (client) { onStreamError(client, err) }) - /* istanbul ignore next: difficult to test and not important as long as we keep this listener */ - stream.on('clientError', (err) => { - debug('clientError occurred') - onStreamError(client, err) - }) - stream.once('close', (hadError) => { - reconnect(client, 'close') + lazyReconnect(client, 'close') }) stream.once('end', () => { - reconnect(client, 'end') + lazyReconnect(client, 'end') }) stream.setNoDelay() diff --git a/lib/createClient.js b/lib/createClient.js index ccf1f95781..820dc93a7f 100644 --- a/lib/createClient.js +++ b/lib/createClient.js @@ -3,7 +3,9 @@ const utils = require('./utils') const URL = require('url') -module.exports = function createClient (portArg, hostArg, options) { +// TODO: Improve the unify performance by checking for the arguments length +// before trying to access that argument. +function unifyOptions (portArg, hostArg, options) { if (typeof portArg === 'number' || (typeof portArg === 'string' && /^\d+$/.test(portArg))) { var host if (typeof hostArg === 'string') { @@ -75,3 +77,5 @@ module.exports = function createClient (portArg, hostArg, options) { return options } + +module.exports = unifyOptions diff --git a/lib/individualCommands.js b/lib/individualCommands.js index c1cffc331d..f773fa1353 100644 --- a/lib/individualCommands.js +++ b/lib/individualCommands.js @@ -186,7 +186,7 @@ function authCallback (client, pass) { if (err) { if (noPasswordIsSet.test(err.message)) { utils.warn(client, 'Warning: Redis server does not require a password, but a password was supplied.') - return 'OK' // TODO: Fix this + return 'OK' } return err } @@ -224,7 +224,6 @@ RedisClient.prototype.client = function client () { } var callOnWrite // CLIENT REPLY ON|OFF|SKIP - /* istanbul ignore next: TODO: Remove this as soon as Travis runs Redis 3.2 */ if (arr.length === 2 && arr[0].toString().toUpperCase() === 'REPLY') { const replyOnOff = arr[1].toString().toUpperCase() if (replyOnOff === 'ON' || replyOnOff === 'OFF' || replyOnOff === 'SKIP') { @@ -244,7 +243,6 @@ Multi.prototype.client = function client () { } var callOnWrite // CLIENT REPLY ON|OFF|SKIP - /* istanbul ignore next: TODO: Remove this as soon as Travis runs Redis 3.2 */ if (arr.length === 2 && arr[0].toString().toUpperCase() === 'REPLY') { const replyOnOff = arr[1].toString().toUpperCase() if (replyOnOff === 'ON' || replyOnOff === 'OFF' || replyOnOff === 'SKIP') { diff --git a/lib/multi.js b/lib/multi.js index 016662e003..4d2c3ab714 100644 --- a/lib/multi.js +++ b/lib/multi.js @@ -145,14 +145,14 @@ class Multi { /** * Creates an instance of Multi. * @param {RedisClient} client - * @param {string} type * @param {any[]} [args] + * @param {string} [type] * * @memberof Multi */ - constructor (client, type, args) { + constructor (client, args, type) { this._client = client - this._type = type + this._type = typeof type === 'string' ? type : 'multi' this._queue = new Queue() // Either undefined or an array. Fail hard if it's not an array if (args) { diff --git a/lib/readyHandler.js b/lib/readyHandler.js index a3b3af2dce..fb4c84393c 100644 --- a/lib/readyHandler.js +++ b/lib/readyHandler.js @@ -105,7 +105,6 @@ function readyCheck (client) { // Always fire client info command as first command even if other commands are already queued up client.ready = true client.info().then((res) => { - /* istanbul ignore if: some servers might not respond with any info data. client is just a safety check that is difficult to test */ if (!res) { debug('The info command returned without any data.') readyHandler(client) diff --git a/lib/replyHandler.js b/lib/replyHandler.js index f7bac3fa43..f29b2a8d1a 100644 --- a/lib/replyHandler.js +++ b/lib/replyHandler.js @@ -40,7 +40,7 @@ function onResult (client, reply) { // If in monitor mode, all normal commands are still working and we only want to emit the streamlined commands // As this is not the average use case and monitor is expensive anyway, let's change the code here, to improve // the average performance of all other commands in case of no monitor mode - if (client._monitoring) { + if (client._monitoring === true) { var replyStr if (client.buffers && Buffer.isBuffer(reply)) { replyStr = reply.toString() @@ -63,6 +63,7 @@ function onResult (client, reply) { } else if (client._pubSubMode !== 1) { client._pubSubMode-- normalReply(client, reply) + // TODO: Have another look at this if this could be further improved } else if (!(reply instanceof Array) || reply.length <= 2) { // Only PING and QUIT are allowed in this context besides the pub sub commands // Ping replies with ['pong', null|value] and quit with 'OK' diff --git a/lib/utils.js b/lib/utils.js index 1990ea43b2..f18271778d 100644 --- a/lib/utils.js +++ b/lib/utils.js @@ -46,25 +46,29 @@ function replyToStrings (reply) { * @description Deep clone arbitrary objects with arrays. * Can't handle cyclic structures (results in a range error). * Any attribute with a non primitive value besides object - * and array will be passed by reference (e.g. Buffers, Maps, Functions) + * and array will be passed by reference (e.g. Buffers, Maps, Functions). * * @param {any} obj * @returns any */ function clone (obj) { - var copy if (Array.isArray(obj)) { - copy = new Array(obj.length) + const copy = new Array(obj.length) for (var i = 0; i < obj.length; i++) { copy[i] = clone(obj[i]) } return copy } if (Object.prototype.toString.call(obj) === '[object Object]') { - copy = {} - const elements = Object.keys(obj) - for (var elem = elements.pop(); elem !== undefined; elem = elements.pop()) { - copy[elem] = clone(obj[elem]) + const copy = {} + const keys = Object.keys(obj) + while (keys.length) { + const key = keys.pop() + if (key === 'stream') { + copy[key] = obj[key] + } else { + copy[key] = clone(obj[key]) + } } return copy } @@ -96,12 +100,10 @@ function convenienceClone (obj) { * @param {Denque} queue */ function replyInOrder (client, callback, err, res, queue) { - var commandObj - if (queue) { - commandObj = queue.peekBack() - } else { - commandObj = client.offlineQueue.peekBack() || client.commandQueue.peekBack() - } + const commandObj = queue + ? queue.peekBack() + : (client.offlineQueue.peekBack() || client.commandQueue.peekBack()) + if (!commandObj) { process.nextTick(callback, err, res) } else { @@ -149,6 +151,21 @@ function handleReply (client, reply, command) { return reply } +/** + * @description Callback result print helper + * + * @param {Error|null} err + * @param {any} reply + */ +function print (err, reply) { + if (err) { + // A error always begins with Error: + console.error(err.toString()) + } else { + console.log('Reply: ' + reply) + } +} + /** * @description Set default reconnect variables * @@ -171,5 +188,6 @@ module.exports = { replyInOrder, warn, handleReply, - setReconnectDefaults + setReconnectDefaults, + print } diff --git a/test/auth.spec.js b/test/auth.spec.js index 6c78bafb68..43c40b056a 100644 --- a/test/auth.spec.js +++ b/test/auth.spec.js @@ -14,9 +14,7 @@ if (process.platform !== 'win32') { }) }) - helper.allTests({ - allConnections: true - }, (ip, args) => { + helper.allTests((ip, args) => { describe(`using ${ip}`, () => { const auth = 'porkchopsandwiches' let client = null diff --git a/test/connection.spec.js b/test/connection.spec.js index e61ad89360..6ddc1a621d 100644 --- a/test/connection.spec.js +++ b/test/connection.spec.js @@ -4,7 +4,7 @@ const assert = require('assert') const config = require('./lib/config') const connect = require('../lib/connect') const helper = require('./helper') -const redis = config.redis +const Redis = config.redis const intercept = require('intercept-stdout') const net = require('net') let client @@ -22,7 +22,7 @@ describe('connection tests', () => { // Besides that some functions also have to be monkey patched to be safe from errors in this case. // Therefore this is not officially supported! const socket = new net.Socket() - client = new redis.RedisClient({ + client = new Redis({ prefix: 'test', stream: socket }) @@ -38,7 +38,7 @@ describe('connection tests', () => { describe('quit on lost connections', () => { it('calling quit while the connection is down should not end in reconnecting version a', (done) => { let called = 0 - client = redis.createClient({ + client = Redis.createClient({ connectTimeout: 5, port: 9999, retryStrategy (options) { @@ -59,7 +59,7 @@ describe('connection tests', () => { it('calling quit while the connection is down should not end in reconnecting version b', () => { let called = false - client = redis.createClient(9999) + client = Redis.createClient(9999) client.set('foo', 'bar').catch((err) => { assert.strictEqual(err.message, 'Stream connection ended and command aborted.') called = true @@ -72,7 +72,7 @@ describe('connection tests', () => { it('calling quit while the connection is down without offline queue should end the connection right away', () => { let called = false - client = redis.createClient(9999, { + client = Redis.createClient(9999, { enableOfflineQueue: false }) client.set('foo', 'bar').catch((err) => { @@ -87,7 +87,7 @@ describe('connection tests', () => { it('calling quit while connected without offline queue should end the connection when all commands have finished', (done) => { let called = false - client = redis.createClient({ + client = Redis.createClient({ enableOfflineQueue: false }) client.on('ready', () => { @@ -104,7 +104,7 @@ describe('connection tests', () => { }) it('do not quit before connected or a connection issue is detected', () => { - client = redis.createClient() + client = Redis.createClient() return Promise.all([ client.set('foo', 'bar').then(helper.isString('OK')), client.quit() @@ -112,7 +112,7 @@ describe('connection tests', () => { }) it('quit "succeeds" even if the client connection is closed while doing so', () => { - client = redis.createClient() + client = Redis.createClient() return client.set('foo', 'bar').then((res) => { assert.strictEqual(res, 'OK') const promise = client.quit().then((res) => { @@ -124,7 +124,7 @@ describe('connection tests', () => { }) it('quit right away if connection drops while quit command is on the fly', (done) => { - client = redis.createClient() + client = Redis.createClient() client.once('ready', () => { client.set('foo', 'bar').catch(helper.isError()) client.quit().then(() => done()) @@ -138,7 +138,7 @@ describe('connection tests', () => { describe('on lost connection', () => { it('end connection while retry is still ongoing', (done) => { const connectTimeout = 1000 // in ms - client = redis.createClient({ + client = Redis.createClient({ connectTimeout }) @@ -160,7 +160,7 @@ describe('connection tests', () => { family: ip, retryStrategy () {} } - client = redis.createClient(options) + client = Redis.createClient(options) assert.strictEqual(client._connectionOptions.family, ip === 'IPv6' ? 6 : 4) assert.strictEqual(Object.keys(options).length, 4) const end = helper.callFuncAfter(done, 2) @@ -172,7 +172,7 @@ describe('connection tests', () => { }) it('retryStrategy used to reconnect with individual error', (done) => { - client = redis.createClient({ + client = Redis.createClient({ retryStrategy (options) { if (options.totalRetryTime > 150) { client.set('foo', 'bar').then(assert, (err) => { @@ -191,7 +191,7 @@ describe('connection tests', () => { }) it('retryStrategy used to reconnect', (done) => { - client = redis.createClient({ + client = Redis.createClient({ retryStrategy (options) { if (options.totalRetryTime > 150) { client.set('foo', 'bar').catch((err) => { @@ -213,17 +213,17 @@ describe('connection tests', () => { const unhookIntercept = intercept(() => { return '' }) - redis.debugMode = true - client = redis.createClient({ + Redis.debugMode = true + client = Redis.createClient({ retryStrategy (options) { client.set('foo', 'bar').catch((err) => { assert.strictEqual(err.code, 'NR_CLOSED') assert.strictEqual(err.message, 'Stream connection ended and command aborted.') unhookIntercept() - redis.debugMode = false + Redis.debugMode = false done() }) - assert(redis.debugMode) + assert(Redis.debugMode) return null } }) @@ -237,7 +237,7 @@ describe('connection tests', () => { // TODO: Fix this test it.skip('emit an error after the socket timeout exceeded the connectTimeout time', (done) => { const connectTimeout = 500 // in ms - client = redis.createClient({ + client = Redis.createClient({ // Auto detect ipv4 and use non routeable ip to trigger the timeout host: '10.255.255.1', connectTimeout, @@ -271,7 +271,7 @@ describe('connection tests', () => { }) it('use the system socket timeout if the connectTimeout has not been provided', (done) => { - client = redis.createClient({ + client = Redis.createClient({ host: '2001:db8::ff00:42:8329' // auto detect ip v6 }) assert.strictEqual(client.address, '2001:db8::ff00:42:8329:6379') @@ -284,7 +284,7 @@ describe('connection tests', () => { }) it('clears the socket timeout after a connection has been established', (done) => { - client = redis.createClient({ + client = Redis.createClient({ connectTimeout: 1000 }) process.nextTick(assert.strictEqual, client._stream._idleTimeout, 1000) @@ -296,7 +296,7 @@ describe('connection tests', () => { }) it('connect with host and port provided in the options object', (done) => { - client = redis.createClient({ + client = Redis.createClient({ host: 'localhost', port: '6379', connectTimeout: 1000 @@ -309,7 +309,7 @@ describe('connection tests', () => { if (process.platform === 'win32') { this.skip() } - client = redis.createClient({ + client = Redis.createClient({ path: '/tmp/redis.sock', connectTimeout: 1000 }) @@ -317,7 +317,7 @@ describe('connection tests', () => { }) it('connects correctly with args', (done) => { - client = redis.createClient.apply(null, args) + client = Redis.createClient.apply(null, args) client.on('error', done) client.once('ready', () => { @@ -327,7 +327,7 @@ describe('connection tests', () => { }) it('connects correctly with default values', (done) => { - client = redis.createClient() + client = Redis.createClient() client.on('error', done) client.once('ready', () => { @@ -337,7 +337,7 @@ describe('connection tests', () => { }) it('connects with a port only', (done) => { - client = redis.createClient(6379) + client = Redis.createClient(6379) assert.strictEqual(client._connectionOptions.family, 4) client.on('error', done) @@ -348,7 +348,7 @@ describe('connection tests', () => { }) it('connects correctly to localhost', (done) => { - client = redis.createClient(null, null) + client = Redis.createClient(null, null) client.on('error', done) client.once('ready', () => { @@ -358,7 +358,7 @@ describe('connection tests', () => { }) it('connects correctly to the provided host with the port set to null', (done) => { - client = redis.createClient(null, 'localhost') + client = Redis.createClient(null, 'localhost') client.on('error', done) assert.strictEqual(client.address, 'localhost:6379') @@ -371,7 +371,7 @@ describe('connection tests', () => { }) it('connects correctly to localhost and no ready check', (done) => { - client = redis.createClient(undefined, undefined, { + client = Redis.createClient(undefined, undefined, { noReadyCheck: true }) client.on('error', done) @@ -385,7 +385,7 @@ describe('connection tests', () => { }) it('connects correctly to the provided host with the port set to undefined', (done) => { - client = redis.createClient(undefined, 'localhost', { + client = Redis.createClient(undefined, 'localhost', { noReadyCheck: true }) client.on('error', done) @@ -400,7 +400,7 @@ describe('connection tests', () => { }) it('connects correctly even if the info command is not present on the redis server', (done) => { - client = redis.createClient.apply(null, args) + client = Redis.createClient.apply(null, args) const end = helper.callFuncAfter(done, 2) client.info = function () { // Mock the result @@ -415,13 +415,13 @@ describe('connection tests', () => { if (ip === 'IPv4') { it('allows connecting with the redis url to the default host and port, select db 3 and warn about duplicate db option', (done) => { - client = redis.createClient('redis:///3?db=3') + client = Redis.createClient('redis:///3?db=3') assert.strictEqual(client.selectedDb, '3') client.on('ready', done) }) it('allows connecting with the redis url and the default port and auth provided even though it is not required', (done) => { - client = redis.createClient(`redis://:porkchopsandwiches@${config.HOST[ip]}/`) + client = Redis.createClient(`redis://:porkchopsandwiches@${config.HOST[ip]}/`) const end = helper.callFuncAfter(done, 2) client.on('warning', (msg) => { assert.strictEqual(msg, 'Warning: Redis server does not require a password, but a password was supplied.') @@ -431,7 +431,7 @@ describe('connection tests', () => { }) it('allows connecting with the redis url as first parameter and the options as second parameter', (done) => { - client = redis.createClient('//127.0.0.1', { + client = Redis.createClient('//127.0.0.1', { connectTimeout: 1000 }) assert.strictEqual(client._options.connectTimeout, 1000) @@ -439,7 +439,7 @@ describe('connection tests', () => { }) it('allows connecting with the redis url in the options object and works with protocols other than the redis protocol (e.g. http)', (done) => { - client = redis.createClient({ + client = Redis.createClient({ url: `http://foo:porkchopsandwiches@${config.HOST[ip]}/3` }) assert.strictEqual(client._options.password, 'porkchopsandwiches') @@ -453,13 +453,13 @@ describe('connection tests', () => { const options = { detectBuffers: false } - client = redis.createClient(`redis://${config.HOST[ip]}:${config.PORT}`, options) + client = Redis.createClient(`redis://${config.HOST[ip]}:${config.PORT}`, options) assert.strictEqual(Object.keys(options).length, 1) client.on('ready', done) }) it('allows connecting with the redis url and no auth and options as third parameter', (done) => { - client = redis.createClient(`redis://${config.HOST[ip]}:${config.PORT}`, null, { + client = Redis.createClient(`redis://${config.HOST[ip]}:${config.PORT}`, null, { detectBuffers: false }) client.on('ready', done) @@ -467,7 +467,7 @@ describe('connection tests', () => { } it('redis still loading <= 500', (done) => { - client = redis.createClient.apply(null, args) + client = Redis.createClient.apply(null, args) const tmp = client.info.bind(client) const end = helper.callFuncAfter(done, 3) let delayed = false @@ -496,7 +496,7 @@ describe('connection tests', () => { }) it('redis still loading > 1000ms', (done) => { - client = redis.createClient.apply(null, args) + client = Redis.createClient.apply(null, args) const tmp = client.info.bind(client) const end = helper.callFuncAfter(done, 3) let delayed = false diff --git a/test/helper.js b/test/helper.js index a7ab58cdc2..bc74ba2ef7 100644 --- a/test/helper.js +++ b/test/helper.js @@ -170,7 +170,7 @@ module.exports = { let key for (key in options) { if (options.hasOwnProperty(key)) { - strOptions += `${key }: ${options[key]}; ` + strOptions += `${key}: ${options[key]}; ` } } describe(`using options: ${strOptions}`, () => { diff --git a/test/utils.spec.js b/test/utils.spec.js index eceece0fce..717486cdf9 100644 --- a/test/utils.spec.js +++ b/test/utils.spec.js @@ -2,9 +2,34 @@ const assert = require('assert') const Queue = require('denque') +const intercept = require('intercept-stdout') const utils = require('../lib/utils') describe('utils.js', () => { + describe('print helper', function () { + it('callback with reply', function () { + var text = '' + const unhookIntercept = intercept(function (data) { + text += data + return '' + }) + utils.print(null, 'abc') + unhookIntercept() + assert.strictEqual(text, 'Reply: abc\n') + }) + + it('callback with error', function () { + var text = '' + const unhookIntercept = intercept(function (data) { + text += data + return '' + }) + utils.print(new Error('Wonderful exception')) + unhookIntercept() + assert.strictEqual(text, 'Error: Wonderful exception\n') + }) + }) + describe('clone', () => { it('ignore the object prototype and clone a nested array / object', () => { const obj = {