diff --git a/benchmarks/multi_bench.js b/benchmarks/multi_bench.js index a495c40e2f..4bee7789dc 100644 --- a/benchmarks/multi_bench.js +++ b/benchmarks/multi_bench.js @@ -119,7 +119,7 @@ Test.prototype.newClient = function (id) { Test.prototype.onClientsReady = function () { process.stdout.write(`${lpad(this.args.descr, 13) }, ${this.args.batch ? lpad(`batch ${this.args.batch}`, 9) : lpad(this.args.pipeline, 9) }/${this.clientsReady} `) this.testStart = Date.now() - this.fillPipeline() + return this.fillPipeline() } Test.prototype.fillPipeline = function () { @@ -131,19 +131,19 @@ Test.prototype.fillPipeline = function () { } this.ended = true this.printStats() - this.stopClients() - return + return this.stopClients() } if (this.batchPipeline) { - this.batch() - } else { - while (pipeline < this.maxPipeline) { - this.commandsSent++ - pipeline++ - this.sendNext() - } + return this.batch() } + const promises = [] + while (pipeline < this.maxPipeline) { + this.commandsSent++ + pipeline++ + promises.push(this.sendNext()) + } + return Promise.all(promises) } Test.prototype.batch = function () { @@ -158,29 +158,24 @@ Test.prototype.batch = function () { batch[this.args.command](this.args.args) } - batch.exec((err, res) => { - if (err) { - throw err - } + batch.exec().then((res) => { self.commandsCompleted += res.length self.commandLatency.update(process.hrtime(start)[1]) - self.fillPipeline() + return self.fillPipeline() }) } Test.prototype.stopClients = function () { const self = this - this.clients.forEach((client, pos) => { + return Promise.all(this.clients.map((client, pos) => { if (pos === self.clients.length - 1) { - client.quit((err, res) => { - if (err) throw err + return client.quit().then((res) => { self.callback() }) - } else { - client.quit() } - }) + return client.quit() + })) } Test.prototype.sendNext = function () { @@ -188,13 +183,10 @@ Test.prototype.sendNext = function () { const curClient = this.commandsSent % this.clients.length const start = process.hrtime() - this.clients[curClient][this.args.command](this.args.args, (err, res) => { - if (err) { - throw err - } + this.clients[curClient][this.args.command](this.args.args).then((res) => { self.commandsCompleted++ self.commandLatency.update(process.hrtime(start)[1]) - self.fillPipeline() + return self.fillPipeline() }) } @@ -280,7 +272,7 @@ function next () { next() }) } else if (rp) { - // Stop the redis process if started by the benchmark + // Stop the redis process if started by the benchmark rp.stop(() => { rp = undefined next() diff --git a/changelog.md b/changelog.md index 8f58b3f9ba..4829b294c4 100644 --- a/changelog.md +++ b/changelog.md @@ -2,10 +2,24 @@ ## v.3.0.0-alpha1 - XX XXX, 2017 +Version three is mainly a release to remove a lot of old cruft and open the doors for new features again. +It became a hassle to implement new things while supporting all the old and deprecated features. + +Therefore this is going to a big breaking change. For most of these there's likely not much to change, +but some of the changes are significant like dropping support for callbacks! To mitigate this breakage +there's a migration library you can include. It restores e.g. support for callbacks and some of the dropped +options and it will warn you in case you use any removed feature or option. + +It will not restore the support for old Node.js versions, the return value of (p)(un)subscribe calls, +the backpressure indicator and the changed connectTimeout behavior. It will also only partially restore +snake_case support and maybe more. + Breaking Changes - Dropped support for `UPPER_CASE` commands - Dropped support for `snake_case` +- Dropped support for domains +- Dropped support for Redis 2.4 - Dropped support for Node.js < 4 - Removed `drain` event - Removed `idle` event @@ -13,6 +27,7 @@ Breaking Changes - 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 backpressure indicator from function return value - Changed return value of `(p)(un)subscribe` @@ -20,6 +35,9 @@ Breaking Changes - Changed `connectTimeout` (connect_timeout) option - This timeout does not limit the total retry time anymore - From now on this will only set the stream timeout to connect to the host +- Changed return value for `multi` and `batch` in case of an error + - If an error occurs for one of the calls, the result is from now on always going to be an error + - The result of all queries can be inspected on the error through the `result` attribute - Only emit ready when all commands were truly send to Redis ## v.2.7.2 - 14 Mar, 2017 diff --git a/index.js b/index.js index d3accf2d3d..bd646e710f 100644 --- a/index.js +++ b/index.js @@ -1,5 +1,6 @@ 'use strict' +// TODO: Replace all for in loops! const Buffer = require('safe-buffer').Buffer const net = require('net') const tls = require('tls') @@ -113,6 +114,14 @@ function RedisClient (options, stream) { this.buffers = options.returnBuffers || options.detectBuffers this.options = options this.reply = 'ON' // Returning replies is the default + this.retryStrategy = options.retryStrategy || function (options) { + if (options.attempt > 100) { + return + } + // reconnect after + return Math.min(options.attempt * 100, 3000) + } + this.retryStrategyProvided = !!options.retryStrategy this.subscribeChannels = [] // Init parser this.replyParser = createParser(this) @@ -150,8 +159,8 @@ function createParser (self) { error: err, queues: ['commandQueue'] }) - self.emit('error', err) self.createStream() + setImmediate(() => self.emit('error', err)) }, returnBuffers: self.buffers || self.messageBuffers, stringNumbers: self.options.stringNumbers || false @@ -195,17 +204,17 @@ RedisClient.prototype.createStream = function () { } if (this.options.connectTimeout) { + // TODO: Investigate why this is not properly triggered this.stream.setTimeout(this.connectTimeout, () => { // Note: This is only tested if a internet connection is established - self.retryTotaltime = self.connectTimeout self.connectionGone('timeout') }) } /* istanbul ignore next: travis does not work with stunnel atm. Therefore the tls tests are skipped on travis */ const connectEvent = this.options.tls ? 'secureConnect' : 'connect' - this.stream.once(connectEvent, function () { - this.removeAllListeners('timeout') + this.stream.once(connectEvent, () => { + this.stream.removeAllListeners('timeout') self.timesConnected++ self.onConnect() }) @@ -234,10 +243,18 @@ RedisClient.prototype.createStream = function () { self.connectionGone('end') }) + this.stream.setNoDelay() + // Fire the command before redis is connected to be sure it's the first fired command if (this.authPass !== undefined) { this.ready = true - this.auth(this.authPass) + this.auth(this.authPass).catch((err) => { + this.closing = true + process.nextTick(() => { + this.emit('error', err) + this.end(true) + }) + }) this.ready = false } } @@ -255,8 +272,7 @@ RedisClient.prototype.uncork = noop RedisClient.prototype.initializeRetryVars = function () { this.retryTimer = null this.retryTotaltime = 0 - this.retryDelay = 200 - this.retryBackoff = 1.7 + this.retryDelay = 100 this.attempts = 1 } @@ -276,7 +292,6 @@ RedisClient.prototype.warn = function (msg) { // Flush provided queues, erroring any items with a callback first RedisClient.prototype.flushAndError = function (errorAttributes, options) { options = options || {} - const aggregatedErrors = [] const queueNames = options.queues || ['commandQueue', 'offlineQueue'] // Flush the commandQueue first to keep the order intact for (let i = 0; i < queueNames.length; i++) { // If the command was fired it might have been processed so far @@ -298,25 +313,9 @@ RedisClient.prototype.flushAndError = function (errorAttributes, options) { if (options.error) { err.origin = options.error } - if (typeof commandObj.callback === 'function') { - commandObj.callback(err) - } else { - aggregatedErrors.push(err) - } + commandObj.callback(err) } } - // Currently this would be a breaking change, therefore it's only emitted in debugMode - if (exports.debugMode && aggregatedErrors.length) { - let error - if (aggregatedErrors.length === 1) { - error = aggregatedErrors[0] - } else { - errorAttributes.message = errorAttributes.message.replace('It', 'They').replace(/command/i, '$&s') - error = new errorClasses.AggregateError(errorAttributes) - error.errors = aggregatedErrors - } - this.emit('error', error) - } } RedisClient.prototype.onError = function (err) { @@ -330,7 +329,7 @@ RedisClient.prototype.onError = function (err) { this.ready = false // Only emit the error if the retryStrategy option is not set - if (!this.options.retryStrategy) { + if (this.retryStrategyProvided === false) { this.emit('error', err) } // 'error' events get turned into exceptions if they aren't listened for. If the user handled this error @@ -381,35 +380,62 @@ RedisClient.prototype.onReady = function () { // Restore modal commands from previous connection. The order of the commands is important if (this.selectedDb !== undefined) { - this.internalSendCommand(new Command('select', [this.selectedDb])) + this.internalSendCommand(new Command('select', [this.selectedDb])).catch((err) => { + if (!this.closing) { + // TODO: These internal things should be wrapped in a + // special error that describes what is happening + process.nextTick(() => this.emit('error', err)) + } + }) } if (this.monitoring) { // Monitor has to be fired before pub sub commands - this.internalSendCommand(new Command('monitor', [])) + this.internalSendCommand(new Command('monitor', [])).catch((err) => { + if (!this.closing) { + process.nextTick(() => this.emit('error', err)) + } + }) } const callbackCount = Object.keys(this.subscriptionSet).length + // TODO: Replace the disableResubscribing by a individual function that may be called + // Add HOOKS!!! + // Replace the disableResubscribing by: + // resubmit: { + // select: true, + // monitor: true, + // subscriptions: true, + // // individual: function noop () {} + // } if (!this.options.disableResubscribing && callbackCount) { debug('Sending pub/sub onReady commands') for (const key in this.subscriptionSet) { const command = key.slice(0, key.indexOf('_')) const args = this.subscriptionSet[key] - this[command]([args]) + this[command]([args]).catch((err) => { + if (!this.closing) { + process.nextTick(() => this.emit('error', err)) + } + }) } } this.sendOfflineQueue() this.emit('ready') } -RedisClient.prototype.onInfoCmd = function (err, res) { - if (err) { - if (err.message === "ERR unknown command 'info'") { - this.onReady() - return - } - err.message = `Ready check failed: ${err.message}` - this.emit('error', err) +RedisClient.prototype.onInfoFail = function (err) { + if (this.closing) { return } + if (err.message === "ERR unknown command 'info'") { + this.onReady() + return + } + err.message = `Ready check failed: ${err.message}` + this.emit('error', err) + return +} + +RedisClient.prototype.onInfoCmd = function (res) { /* istanbul ignore if: some servers might not respond with any info data. This is just a safety check that is difficult to test */ if (!res) { debug('The info command returned without any data.') @@ -434,19 +460,18 @@ RedisClient.prototype.onInfoCmd = function (err, res) { retryTime = 1000 } debug(`Redis server still loading, trying again in ${retryTime}`) - setTimeout((self) => { - self.readyCheck() - }, retryTime, this) + return new Promise((resolve) => { + setTimeout((self) => resolve(self.readyCheck()), retryTime, this) + }) } RedisClient.prototype.readyCheck = function () { - const self = this debug('Checking server ready state...') // Always fire this info command as first command even if other commands are already queued up this.ready = true - this.info((err, res) => { - self.onInfoCmd(err, res) - }) + this.info() + .then((res) => this.onInfoCmd(res)) + .catch((err) => this.onInfoFail(err)) this.ready = false } @@ -471,7 +496,6 @@ const retryConnection = function (self, error) { self.retryTotaltime += self.retryDelay self.attempts += 1 - self.retryDelay = Math.round(self.retryDelay * self.retryBackoff) self.createStream() self.retryTimer = null } @@ -498,6 +522,20 @@ RedisClient.prototype.connectionGone = function (why, error) { this.emittedEnd = true } + if (why === 'timeout') { + var message = 'Redis connection in broken state: connection timeout exceeded.' + const err = new Error(message) + // TODO: Find better error codes... + err.code = 'CONNECTION_BROKEN' + this.flushAndError({ + message: message, + code: 'CONNECTION_BROKEN' + }) + this.emit('error', err) + this.end(false) + return + } + // If this is a requested shutdown, then don't retry if (this.closing) { debug('Connection ended by quit / end command, not retrying.') @@ -510,28 +548,29 @@ RedisClient.prototype.connectionGone = function (why, error) { return } - if (typeof this.options.retryStrategy === 'function') { - const retryParams = { - attempt: this.attempts, - error, - totalRetryTime: this.retryTotaltime, - timesConnected: this.timesConnected + this.retryDelay = this.retryStrategy({ + attempt: this.attempts, + error, + totalRetryTime: this.retryTotaltime, + timesConnected: this.timesConnected + }) + if (typeof this.retryDelay !== 'number') { + // Pass individual error through + if (this.retryDelay instanceof Error) { + error = this.retryDelay } - this.retryDelay = this.options.retryStrategy(retryParams) - if (typeof this.retryDelay !== 'number') { - // Pass individual error through - if (this.retryDelay instanceof Error) { - error = this.retryDelay - } - this.flushAndError({ - message: 'Stream connection ended and command aborted.', - code: 'NR_CLOSED' - }, { - error - }) - this.end(false) - return + this.flushAndError({ + message: 'Stream connection ended and command aborted.', + code: 'NR_CLOSED' + }, { + error + }) + // TODO: Check if this is so smart + if (error) { + this.emit('error', error) } + this.end(false) + return } // Retry commands after a reconnect instead of throwing an error. Use this with caution @@ -574,19 +613,15 @@ RedisClient.prototype.returnError = function (err) { err.code = match[1] } - utils.callbackOrEmit(this, commandObj.callback, err) + commandObj.callback(err) } function normalReply (self, reply) { const commandObj = self.commandQueue.shift() - if (typeof commandObj.callback === 'function') { - if (commandObj.command !== 'exec') { - reply = self.handleReply(reply, commandObj.command, commandObj.bufferArgs) - } - commandObj.callback(null, reply) - } else { - debug('No callback for reply') + if (commandObj.command !== 'exec') { + reply = self.handleReply(reply, commandObj.command, commandObj.bufferArgs) } + commandObj.callback(null, reply) } function subscribeUnsubscribe (self, reply, type) { @@ -600,13 +635,13 @@ function subscribeUnsubscribe (self, reply, type) { // Emit first, then return the callback if (channel !== null) { // Do not emit or "unsubscribe" something if there was no channel to unsubscribe from - self.emit(type, channel, count) if (type === 'subscribe' || type === 'psubscribe') { self.subscriptionSet[`${type}_${channel}`] = channel } else { - type = type === 'unsubscribe' ? 'subscribe' : 'psubscribe' // Make types consistent - delete self.subscriptionSet[`${type}_${channel}`] + const innerType = type === 'unsubscribe' ? 'subscribe' : 'psubscribe' // Make types consistent + delete self.subscriptionSet[`${innerType}_${channel}`] } + self.emit(type, channel, count) self.subscribeChannels.push(channel) } @@ -625,9 +660,7 @@ function subscribeUnsubscribe (self, reply, type) { } } self.commandQueue.shift() - if (typeof commandObj.callback === 'function') { - commandObj.callback(null, [count, self.subscribeChannels]) - } + commandObj.callback(null, [count, self.subscribeChannels]) self.subscribeChannels = [] self.subCommandsLeft = 0 } else { @@ -738,16 +771,14 @@ RedisClient.prototype.internalSendCommand = function (commandObj) { let bigData = false const argsCopy = new Array(len) - if (process.domain && commandObj.callback) { - commandObj.callback = process.domain.bind(commandObj.callback) - } - if (this.ready === false || this.stream.writable === false) { // Handle offline commands right away handleOfflineCommand(this, commandObj) - return + return commandObj.promise } + // TODO: Refactor this to also accept SET, MAP and ArrayBuffer + // TODO: Add a utility function to create errors with all necessary params for (i = 0; i < len; i += 1) { if (typeof args[i] === 'string') { // 30000 seemed to be a good value to switch to buffers after testing and checking the pros and cons @@ -764,7 +795,8 @@ RedisClient.prototype.internalSendCommand = function (commandObj) { const err = new TypeError('The command contains a "null" argument but NodeRedis can only handle strings, numbers and buffers.') err.command = command.toUpperCase() err.args = args - return utils.replyInOrder(this, commandObj.callback, err, undefined, this.commandQueue) + utils.replyInOrder(this, commandObj.callback, err, undefined, this.commandQueue) + return commandObj.promise } else if (Buffer.isBuffer(args[i])) { argsCopy[i] = args[i] commandObj.bufferArgs = true @@ -773,13 +805,15 @@ RedisClient.prototype.internalSendCommand = function (commandObj) { const err = new TypeError('The command contains a argument of type "' + args[i].constructor.name + '" but NodeRedis can only handle strings, numbers, and buffers.') err.command = command.toUpperCase() err.args = args - return utils.replyInOrder(this, commandObj.callback, err, undefined, this.commandQueue) + utils.replyInOrder(this, commandObj.callback, err, undefined, this.commandQueue) + return commandObj.promise } } else if (typeof args[i] === 'undefined') { const err = new TypeError('The command contains a "undefined" argument but NodeRedis can only handle strings, numbers and buffers.') err.command = command.toUpperCase() err.args = args - return utils.replyInOrder(this, commandObj.callback, err, undefined, this.commandQueue) + utils.replyInOrder(this, commandObj.callback, err, undefined, this.commandQueue) + return commandObj.promise } else { // Seems like numbers are converted fast using string concatenation argsCopy[i] = `${args[i]}` @@ -834,16 +868,14 @@ RedisClient.prototype.internalSendCommand = function (commandObj) { } else { // Do not expect a reply // Does this work in combination with the pub sub mode? - if (commandObj.callback) { - utils.replyInOrder(this, commandObj.callback, null, undefined, this.commandQueue) - } + utils.replyInOrder(this, commandObj.callback, null, undefined, this.commandQueue) if (this.reply === 'SKIP') { this.reply = 'SKIP_ONE_MORE' } else if (this.reply === 'SKIP_ONE_MORE') { this.reply = 'ON' } } - return + return commandObj.promise } RedisClient.prototype.writeStrings = function () { @@ -884,7 +916,6 @@ exports.AbortError = errorClasses.AbortError exports.RedisError = Parser.RedisError exports.ParserError = Parser.ParserError exports.ReplyError = Parser.ReplyError -exports.AggregateError = errorClasses.AggregateError // Add all redis commands / nodeRedis api to the client require('./lib/individualCommands') diff --git a/lib/command.js b/lib/command.js index 92bb9634cd..d183250d06 100644 --- a/lib/command.js +++ b/lib/command.js @@ -2,10 +2,30 @@ const betterStackTraces = /development/i.test(process.env.NODE_ENV) || /\bredis\b/i.test(process.env.NODE_DEBUG) -function Command (command, args, callback, callOnWrite) { +// TODO: Change the arguments to an object +// callOnWrite could be two things now +function Command (command, args, callOnWrite, transformer) { this.command = command this.args = args this.bufferArgs = false + var callback + transformer = transformer || function (err, res) { + return err || res + } + this.promise = new Promise((resolve, reject) => { + callback = (err, res) => { + if (err) { + const transformed = transformer(err) + if (transformed.stack) { // instanceof Error + reject(transformed) + } else { + resolve(transformed) + } + } else { + resolve(transformer(null, res)) + } + } + }) this.callback = callback this.callOnWrite = callOnWrite if (betterStackTraces) { diff --git a/lib/commands.js b/lib/commands.js index 6aeec6048f..194b90fce1 100644 --- a/lib/commands.js +++ b/lib/commands.js @@ -5,6 +5,8 @@ const Multi = require('./multi') const RedisClient = require('../').RedisClient const Command = require('./command') +const EMPTY_ARRAY = [] + // TODO: Rewrite this including the individual commands into a Commands class // that provided a functionality to add new commands to the client commands.list.forEach((command) => { @@ -15,37 +17,26 @@ commands.list.forEach((command) => { // Do not override existing functions if (!RedisClient.prototype[command]) { RedisClient.prototype[command] = function () { - let arr - let len = arguments.length - let callback - let i = 0 - if (Array.isArray(arguments[0])) { + const len = arguments.length + var arr, i + if (len === 0) { + arr = EMPTY_ARRAY + } else if (arguments[0].shift) { arr = arguments[0] - if (len === 2) { - callback = arguments[1] - } - } else if (len > 1 && Array.isArray(arguments[1])) { - if (len === 3) { - callback = arguments[2] - } - len = arguments[1].length - arr = new Array(len + 1) + } else if (len > 1 && arguments[1].shift) { + const innerLen = arguments[1].length + arr = new Array(innerLen + 1) arr[0] = arguments[0] - for (; i < len; i += 1) { + for (i = 0; i < innerLen; i += 1) { arr[i + 1] = arguments[1][i] } } else { - // The later should not be the average use case - if (len !== 0 && (typeof arguments[len - 1] === 'function' || typeof arguments[len - 1] === 'undefined')) { - len-- - callback = arguments[len] - } arr = new Array(len) - for (; i < len; i += 1) { + for (i = 0; i < len; i += 1) { arr[i] = arguments[i] } } - return this.internalSendCommand(new Command(command, arr, callback)) + return this.internalSendCommand(new Command(command, arr)) } if (RedisClient.prototype[command] !== commandName) { Object.defineProperty(RedisClient.prototype[command], 'name', { @@ -57,37 +48,26 @@ commands.list.forEach((command) => { // Do not override existing functions if (!Multi.prototype[command]) { Multi.prototype[command] = function () { - let arr - let len = arguments.length - let callback - let i = 0 - if (Array.isArray(arguments[0])) { + const len = arguments.length + var arr, i + if (len === 0) { + arr = EMPTY_ARRAY + } else if (arguments[0].shift) { arr = arguments[0] - if (len === 2) { - callback = arguments[1] - } - } else if (len > 1 && Array.isArray(arguments[1])) { - if (len === 3) { - callback = arguments[2] - } - len = arguments[1].length - arr = new Array(len + 1) + } else if (len > 1 && arguments[1].shift) { + const innerLen = arguments[1].length + arr = new Array(innerLen + 1) arr[0] = arguments[0] - for (; i < len; i += 1) { + for (i = 0; i < innerLen; i += 1) { arr[i + 1] = arguments[1][i] } } else { - // The later should not be the average use case - if (len !== 0 && (typeof arguments[len - 1] === 'function' || typeof arguments[len - 1] === 'undefined')) { - len-- - callback = arguments[len] - } arr = new Array(len) - for (; i < len; i += 1) { + for (i = 0; i < len; i += 1) { arr[i] = arguments[i] } } - this.queue.push(new Command(command, arr, callback)) + this.queue.push(new Command(command, arr)) return this } if (Multi.prototype[command] !== commandName) { diff --git a/lib/createClient.js b/lib/createClient.js index 34744b1ca8..b6e8ca165d 100644 --- a/lib/createClient.js +++ b/lib/createClient.js @@ -5,7 +5,7 @@ const URL = require('url') module.exports = function createClient (portArg, hostArg, options) { if (typeof portArg === 'number' || (typeof portArg === 'string' && /^\d+$/.test(portArg))) { - let host + var host if (typeof hostArg === 'string') { host = hostArg } else { @@ -40,7 +40,7 @@ module.exports = function createClient (portArg, hostArg, options) { options.port = parsed.port } if (parsed.search !== '') { - let elem + var elem for (elem in parsed.query) { // If options are passed twice, only the parsed options will be used if (elem in options) { diff --git a/lib/customErrors.js b/lib/customErrors.js index 48cf2e10fa..29ec813255 100644 --- a/lib/customErrors.js +++ b/lib/customErrors.js @@ -23,37 +23,14 @@ function AbortError (obj, stack) { } } -function AggregateError (obj) { - assert(obj, 'The options argument is required') - assert.strictEqual(typeof obj, 'object', 'The options argument has to be of type object') - - AbortError.call(this, obj, ADD_STACKTRACE) - Object.defineProperty(this, 'message', { - value: obj.message || '', - configurable: true, - writable: true - }) - Error.captureStackTrace(this, AggregateError) - for (let keys = Object.keys(obj), key = keys.pop(); key; key = keys.pop()) { - this[key] = obj[key] - } -} - util.inherits(AbortError, RedisError) -util.inherits(AggregateError, AbortError) Object.defineProperty(AbortError.prototype, 'name', { value: 'AbortError', configurable: true, writable: true }) -Object.defineProperty(AggregateError.prototype, 'name', { - value: 'AggregateError', - configurable: true, - writable: true -}) module.exports = { - AbortError, - AggregateError + AbortError } diff --git a/lib/extendedApi.js b/lib/extendedApi.js index b81626acc7..e52a920537 100644 --- a/lib/extendedApi.js +++ b/lib/extendedApi.js @@ -11,7 +11,9 @@ All documented and exposed API belongs in here **********************************************/ // Redirect calls to the appropriate function and use to send arbitrary / not supported commands -RedisClient.prototype.sendCommand = function (command, args, callback) { +// TODO: REMOVE sendCommand and replace it by a function to add new commands +// TODO: Add a library to add the sendCommand back in place for legacy reasons +RedisClient.prototype.sendCommand = function (command, args) { // Throw to fail early instead of relying in order in this case if (typeof command !== 'string') { throw new TypeError(`Wrong input type "${command !== null && command !== undefined ? command.constructor.name : command}" for command name`) @@ -19,16 +21,10 @@ RedisClient.prototype.sendCommand = function (command, args, callback) { if (!Array.isArray(args)) { if (args === undefined || args === null) { args = [] - } else if (typeof args === 'function' && callback === undefined) { - callback = args - args = [] } else { throw new TypeError(`Wrong input type "${args.constructor.name}" for args`) } } - if (typeof callback !== 'function' && callback !== undefined) { - throw new TypeError(`Wrong input type "${callback !== null ? callback.constructor.name : 'null' }" for callback function`) - } // Using the raw multi command is only possible with this function // If the command is not yet added to the client, the internal function should be called right away @@ -37,10 +33,7 @@ RedisClient.prototype.sendCommand = function (command, args, callback) { // but this might change from time to time and at the moment there's no good way to distinguish them // from each other, so let's just do it do it this way for the time being if (command === 'multi' || typeof this[command] !== 'function') { - return this.internalSendCommand(new Command(command, args, callback)) - } - if (typeof callback === 'function') { - args = args.concat([callback]) // Prevent manipulating the input array + return this.internalSendCommand(new Command(command, args)) } return this[command].apply(this, args) } @@ -83,6 +76,8 @@ RedisClient.prototype.unref = function () { } } +// TODO: promisify this +// TODO: the sendCommand legacy module should also make duplicate handle callbacks again RedisClient.prototype.duplicate = function (options, callback) { if (typeof options === 'function') { callback = options diff --git a/lib/individualCommands.js b/lib/individualCommands.js index eb86ca0a85..c9c44af528 100644 --- a/lib/individualCommands.js +++ b/lib/individualCommands.js @@ -1,11 +1,9 @@ 'use strict' -const utils = require('./utils') const debug = require('./debug') const Multi = require('./multi') const Command = require('./command') const noPasswordIsSet = /no password is set/ -const loading = /LOADING/ const RedisClient = require('../').RedisClient /******************************************************************************************** @@ -32,26 +30,26 @@ RedisClient.prototype.batch = function batch (args) { return new Multi(this, args) } -function selectCallback (self, db, callback) { +function selectCallback (self, db) { return function (err, res) { if (err === null) { // Store db in this.selectDb to restore it on reconnect self.selectedDb = db } - utils.callbackOrEmit(self, callback, err, res) + return err || res } } -RedisClient.prototype.select = function select (db, callback) { - return this.internalSendCommand(new Command('select', [db], selectCallback(this, db, callback))) +RedisClient.prototype.select = function select (db) { + return this.internalSendCommand(new Command('select', [db], null, selectCallback(this, db))) } -Multi.prototype.select = function select (db, callback) { - this.queue.push(new Command('select', [db], selectCallback(this._client, db, callback))) +Multi.prototype.select = function select (db) { + this.queue.push(new Command('select', [db], null, selectCallback(this._client, db))) return this } -RedisClient.prototype.monitor = RedisClient.prototype.MONITOR = function monitor (callback) { +RedisClient.prototype.monitor = RedisClient.prototype.MONITOR = function monitor () { // Use a individual command, as this is a special case that does not has to be checked for any other command const self = this const callOnWrite = function () { @@ -59,18 +57,18 @@ RedisClient.prototype.monitor = RedisClient.prototype.MONITOR = function monitor // Therefore we expect the command to be properly processed. If this is not the case, it's not an issue either. self.monitoring = true } - return this.internalSendCommand(new Command('monitor', [], callback, callOnWrite)) + return this.internalSendCommand(new Command('monitor', [], callOnWrite)) } // Only works with batch, not in a transaction -Multi.prototype.monitor = function monitor (callback) { +Multi.prototype.monitor = function monitor () { // Use a individual command, as this is a special case that does not has to be checked for any other command if (this.exec !== this.execTransaction) { const self = this const callOnWrite = function () { self._client.monitoring = true } - this.queue.push(new Command('monitor', [], callback, callOnWrite)) + this.queue.push(new Command('monitor', [], callOnWrite)) return this } // Set multi monitoring to indicate the exec that it should abort @@ -79,30 +77,29 @@ Multi.prototype.monitor = function monitor (callback) { return this } -function quitCallback (self, callback) { +function quitCallback (self) { return function (err, res) { + if (self.stream.writable) { + // If the socket is still alive, kill it. This could happen if quit got a NR_CLOSED error code + self.stream.destroy() + } if (err && err.code === 'NR_CLOSED') { // Pretend the quit command worked properly in this case. // Either the quit landed in the offline queue and was flushed at the reconnect // or the offline queue is deactivated and the command was rejected right away // or the stream is not writable // or while sending the quit, the connection ended / closed - err = null - res = 'OK' - } - utils.callbackOrEmit(self, callback, err, res) - if (self.stream.writable) { - // If the socket is still alive, kill it. This could happen if quit got a NR_CLOSED error code - self.stream.destroy() + return 'OK' } + return err || res } } -RedisClient.prototype.quit = function quit (callback) { +RedisClient.prototype.quit = function quit () { // TODO: Consider this for v.3 // Allow the quit command to be fired as soon as possible to prevent it landing in the offline queue. // this.ready = this.offlineQueue.length === 0; - const backpressureIndicator = this.internalSendCommand(new Command('quit', [], quitCallback(this, callback))) + const backpressureIndicator = this.internalSendCommand(new Command('quit', [], null, quitCallback(this))) // Calling quit should always end the connection, no matter if there's a connection or not this.closing = true this.ready = false @@ -110,130 +107,115 @@ RedisClient.prototype.quit = function quit (callback) { } // Only works with batch, not in a transaction -Multi.prototype.quit = function quit (callback) { +Multi.prototype.quit = function quit () { const self = this._client const callOnWrite = function () { // If called in a multi context, we expect redis is available self.closing = true self.ready = false } - this.queue.push(new Command('quit', [], quitCallback(self, callback), callOnWrite)) + this.queue.push(new Command('quit', [], null, quitCallback(self), callOnWrite)) return this } -function infoCallback (self, callback) { +function infoCallback (self) { return function (err, res) { - if (res) { - const obj = {} - const lines = res.toString().split('\r\n') - let line, parts, subParts + if (err) { + self.serverInfo = {} + return err + } - for (let i = 0; i < lines.length; i++) { - parts = lines[i].split(':') - if (parts[1]) { - if (parts[0].indexOf('db') === 0) { - subParts = parts[1].split(',') - obj[parts[0]] = {} - for (line = subParts.pop(); line !== undefined; line = subParts.pop()) { - line = line.split('=') - obj[parts[0]][line[0]] = +line[1] - } - } else { - obj[parts[0]] = parts[1] + const obj = {} + const lines = res.toString().split('\r\n') + var line, parts, subParts + + for (let i = 0; i < lines.length; i++) { + parts = lines[i].split(':') + if (parts[1]) { + if (parts[0].indexOf('db') === 0) { + subParts = parts[1].split(',') + obj[parts[0]] = {} + for (line = subParts.pop(); line !== undefined; line = subParts.pop()) { + line = line.split('=') + obj[parts[0]][line[0]] = +line[1] } + } else { + obj[parts[0]] = parts[1] } } - obj.versions = [] - if (obj.redis_version) { - obj.redis_version.split('.').forEach((num) => { - obj.versions.push(+num) - }) - } - // Expose info key/values to users - self.serverInfo = obj - } else { - self.serverInfo = {} } - utils.callbackOrEmit(self, callback, err, res) + obj.versions = [] + if (obj.redis_version) { + obj.redis_version.split('.').forEach((num) => { + obj.versions.push(+num) + }) + } + // Expose info key/values to users + self.serverInfo = obj + return res } } // Store info in this.serverInfo after each call -RedisClient.prototype.info = function info (section, callback) { - let args = [] - if (typeof section === 'function') { - callback = section - } else if (section !== undefined) { +RedisClient.prototype.info = function info (section) { + var args = [] + if (section !== undefined) { args = Array.isArray(section) ? section : [section] } - return this.internalSendCommand(new Command('info', args, infoCallback(this, callback))) + return this.internalSendCommand(new Command('info', args, null, infoCallback(this))) } -Multi.prototype.info = function info (section, callback) { - let args = [] - if (typeof section === 'function') { - callback = section - } else if (section !== undefined) { +Multi.prototype.info = function info (section) { + var args = [] + if (section !== undefined) { args = Array.isArray(section) ? section : [section] } - this.queue.push(new Command('info', args, infoCallback(this._client, callback))) + this.queue.push(new Command('info', args, null, infoCallback(this._client))) return this } -function authCallback (self, pass, callback) { +function authCallback (self, pass) { return function (err, res) { if (err) { if (noPasswordIsSet.test(err.message)) { self.warn('Warning: Redis server does not require a password, but a password was supplied.') - err = null - res = 'OK' - } else if (loading.test(err.message)) { - // If redis is still loading the db, it will not authenticate and everything else will fail - debug('Redis still loading, trying to authenticate later') - setTimeout(() => { - self.auth(pass, callback) - }, 100) - return + return 'OK' // TODO: Fix this } + return err } - utils.callbackOrEmit(self, callback, err, res) + return res } } -RedisClient.prototype.auth = function auth (pass, callback) { +RedisClient.prototype.auth = function auth (pass) { debug(`Sending auth to ${this.address} id ${this.connectionId}`) // Stash auth for connect and reconnect. this.authPass = pass const ready = this.ready this.ready = ready || this.offlineQueue.length === 0 - const tmp = this.internalSendCommand(new Command('auth', [pass], authCallback(this, pass, callback))) + const tmp = this.internalSendCommand(new Command('auth', [pass], null, authCallback(this, pass))) this.ready = ready return tmp } // Only works with batch, not in a transaction -Multi.prototype.auth = function auth (pass, callback) { +Multi.prototype.auth = function auth (pass) { debug(`Sending auth to ${this.address} id ${this.connectionId}`) // Stash auth for connect and reconnect. this.authPass = pass - this.queue.push(new Command('auth', [pass], authCallback(this._client, callback))) + this.queue.push(new Command('auth', [pass], null, authCallback(this._client))) return this } RedisClient.prototype.client = function client () { - let arr - let len = arguments.length - let callback - let i = 0 + var arr + var len = arguments.length + var i = 0 if (Array.isArray(arguments[0])) { arr = arguments[0] - callback = arguments[1] } else if (Array.isArray(arguments[1])) { - if (len === 3) { - callback = arguments[2] - } len = arguments[1].length arr = new Array(len + 1) arr[0] = arguments[0] @@ -242,18 +224,13 @@ RedisClient.prototype.client = function client () { } } else { len = arguments.length - // The later should not be the average use case - if (len !== 0 && (typeof arguments[len - 1] === 'function' || typeof arguments[len - 1] === 'undefined')) { - len-- - callback = arguments[len] - } arr = new Array(len) for (; i < len; i += 1) { arr[i] = arguments[i] } } const self = this - let callOnWrite + 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') { @@ -264,21 +241,16 @@ RedisClient.prototype.client = function client () { } } } - return this.internalSendCommand(new Command('client', arr, callback, callOnWrite)) + return this.internalSendCommand(new Command('client', arr, callOnWrite)) } Multi.prototype.client = function client () { - let arr - let len = arguments.length - let callback - let i = 0 + var arr + var len = arguments.length + var i = 0 if (Array.isArray(arguments[0])) { arr = arguments[0] - callback = arguments[1] } else if (Array.isArray(arguments[1])) { - if (len === 3) { - callback = arguments[2] - } len = arguments[1].length arr = new Array(len + 1) arr[0] = arguments[0] @@ -287,18 +259,13 @@ Multi.prototype.client = function client () { } } else { len = arguments.length - // The later should not be the average use case - if (len !== 0 && (typeof arguments[len - 1] === 'function' || typeof arguments[len - 1] === 'undefined')) { - len-- - callback = arguments[len] - } arr = new Array(len) for (; i < len; i += 1) { arr[i] = arguments[i] } } const self = this._client - let callOnWrite + 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') { @@ -309,104 +276,75 @@ Multi.prototype.client = function client () { } } } - this.queue.push(new Command('client', arr, callback, callOnWrite)) + this.queue.push(new Command('client', arr, callOnWrite)) return this } RedisClient.prototype.hmset = function hmset () { - let arr - let len = arguments.length - let callback - let i = 0 + var arr + var len = arguments.length + var i = 0 if (Array.isArray(arguments[0])) { arr = arguments[0] - callback = arguments[1] } else if (Array.isArray(arguments[1])) { - if (len === 3) { - callback = arguments[2] - } len = arguments[1].length arr = new Array(len + 1) arr[0] = arguments[0] for (; i < len; i += 1) { arr[i + 1] = arguments[1][i] } - } else if (typeof arguments[1] === 'object' && (arguments.length === 2 || (arguments.length === 3 && (typeof arguments[2] === 'function' || typeof arguments[2] === 'undefined')))) { + } else if (typeof arguments[1] === 'object' && (arguments.length === 2)) { arr = [arguments[0]] for (const field in arguments[1]) { arr.push(field, arguments[1][field]) } - callback = arguments[2] } else { len = arguments.length - // The later should not be the average use case - if (len !== 0 && (typeof arguments[len - 1] === 'function' || typeof arguments[len - 1] === 'undefined')) { - len-- - callback = arguments[len] - } arr = new Array(len) for (; i < len; i += 1) { arr[i] = arguments[i] } } - return this.internalSendCommand(new Command('hmset', arr, callback)) + return this.internalSendCommand(new Command('hmset', arr)) } Multi.prototype.hmset = function hmset () { - let arr - let len = arguments.length - let callback - let i = 0 + var arr + var len = arguments.length + var i = 0 if (Array.isArray(arguments[0])) { arr = arguments[0] - callback = arguments[1] } else if (Array.isArray(arguments[1])) { - if (len === 3) { - callback = arguments[2] - } len = arguments[1].length arr = new Array(len + 1) arr[0] = arguments[0] for (; i < len; i += 1) { arr[i + 1] = arguments[1][i] } - } else if (typeof arguments[1] === 'object' && (arguments.length === 2 || (arguments.length === 3 && (typeof arguments[2] === 'function' || typeof arguments[2] === 'undefined')))) { + } else if (typeof arguments[1] === 'object' && (arguments.length === 2)) { arr = [arguments[0]] for (const field in arguments[1]) { arr.push(field, arguments[1][field]) } - callback = arguments[2] } else { len = arguments.length - // The later should not be the average use case - if (len !== 0 && (typeof arguments[len - 1] === 'function' || typeof arguments[len - 1] === 'undefined')) { - len-- - callback = arguments[len] - } arr = new Array(len) for (; i < len; i += 1) { arr[i] = arguments[i] } } - this.queue.push(new Command('hmset', arr, callback)) + this.queue.push(new Command('hmset', arr)) return this } RedisClient.prototype.subscribe = function subscribe () { - let arr - let len = arguments.length - let callback - let i = 0 + var arr + var len = arguments.length + var i = 0 if (Array.isArray(arguments[0])) { arr = arguments[0].slice(0) - callback = arguments[1] } else { len = arguments.length - // The later should not be the average use case - if (len !== 0 && (typeof arguments[len - 1] === 'function' || typeof arguments[len - 1] === 'undefined')) { - len-- - callback = arguments[len] - } arr = new Array(len) for (; i < len; i += 1) { arr[i] = arguments[i] @@ -416,24 +354,17 @@ RedisClient.prototype.subscribe = function subscribe () { const callOnWrite = function () { self.pubSubMode = self.pubSubMode || self.commandQueue.length + 1 } - return this.internalSendCommand(new Command('subscribe', arr, callback, callOnWrite)) + return this.internalSendCommand(new Command('subscribe', arr, callOnWrite)) } Multi.prototype.subscribe = function subscribe () { - let arr - let len = arguments.length - let callback - let i = 0 + var arr + var len = arguments.length + var i = 0 if (Array.isArray(arguments[0])) { arr = arguments[0].slice(0) - callback = arguments[1] } else { len = arguments.length - // The later should not be the average use case - if (len !== 0 && (typeof arguments[len - 1] === 'function' || typeof arguments[len - 1] === 'undefined')) { - len-- - callback = arguments[len] - } arr = new Array(len) for (; i < len; i += 1) { arr[i] = arguments[i] @@ -443,25 +374,18 @@ Multi.prototype.subscribe = function subscribe () { const callOnWrite = function () { self.pubSubMode = self.pubSubMode || self.commandQueue.length + 1 } - this.queue.push(new Command('subscribe', arr, callback, callOnWrite)) + this.queue.push(new Command('subscribe', arr, callOnWrite)) return this } RedisClient.prototype.unsubscribe = function unsubscribe () { - let arr - let len = arguments.length - let callback - let i = 0 + var arr + var len = arguments.length + var i = 0 if (Array.isArray(arguments[0])) { arr = arguments[0].slice(0) - callback = arguments[1] } else { len = arguments.length - // The later should not be the average use case - if (len !== 0 && (typeof arguments[len - 1] === 'function' || typeof arguments[len - 1] === 'undefined')) { - len-- - callback = arguments[len] - } arr = new Array(len) for (; i < len; i += 1) { arr[i] = arguments[i] @@ -472,24 +396,17 @@ RedisClient.prototype.unsubscribe = function unsubscribe () { // Pub sub has to be activated even if not in pub sub mode, as the return value is manipulated in the callback self.pubSubMode = self.pubSubMode || self.commandQueue.length + 1 } - return this.internalSendCommand(new Command('unsubscribe', arr, callback, callOnWrite)) + return this.internalSendCommand(new Command('unsubscribe', arr, callOnWrite)) } Multi.prototype.unsubscribe = function unsubscribe () { - let arr - let len = arguments.length - let callback - let i = 0 + var arr + var len = arguments.length + var i = 0 if (Array.isArray(arguments[0])) { arr = arguments[0].slice(0) - callback = arguments[1] } else { len = arguments.length - // The later should not be the average use case - if (len !== 0 && (typeof arguments[len - 1] === 'function' || typeof arguments[len - 1] === 'undefined')) { - len-- - callback = arguments[len] - } arr = new Array(len) for (; i < len; i += 1) { arr[i] = arguments[i] @@ -500,25 +417,18 @@ Multi.prototype.unsubscribe = function unsubscribe () { // Pub sub has to be activated even if not in pub sub mode, as the return value is manipulated in the callback self.pubSubMode = self.pubSubMode || self.commandQueue.length + 1 } - this.queue.push(new Command('unsubscribe', arr, callback, callOnWrite)) + this.queue.push(new Command('unsubscribe', arr, callOnWrite)) return this } RedisClient.prototype.psubscribe = function psubscribe () { - let arr - let len = arguments.length - let callback - let i = 0 + var arr + var len = arguments.length + var i = 0 if (Array.isArray(arguments[0])) { arr = arguments[0].slice(0) - callback = arguments[1] } else { len = arguments.length - // The later should not be the average use case - if (len !== 0 && (typeof arguments[len - 1] === 'function' || typeof arguments[len - 1] === 'undefined')) { - len-- - callback = arguments[len] - } arr = new Array(len) for (; i < len; i += 1) { arr[i] = arguments[i] @@ -528,24 +438,17 @@ RedisClient.prototype.psubscribe = function psubscribe () { const callOnWrite = function () { self.pubSubMode = self.pubSubMode || self.commandQueue.length + 1 } - return this.internalSendCommand(new Command('psubscribe', arr, callback, callOnWrite)) + return this.internalSendCommand(new Command('psubscribe', arr, callOnWrite)) } Multi.prototype.psubscribe = function psubscribe () { - let arr - let len = arguments.length - let callback - let i = 0 + var arr + var len = arguments.length + var i = 0 if (Array.isArray(arguments[0])) { arr = arguments[0].slice(0) - callback = arguments[1] } else { len = arguments.length - // The later should not be the average use case - if (len !== 0 && (typeof arguments[len - 1] === 'function' || typeof arguments[len - 1] === 'undefined')) { - len-- - callback = arguments[len] - } arr = new Array(len) for (; i < len; i += 1) { arr[i] = arguments[i] @@ -555,25 +458,18 @@ Multi.prototype.psubscribe = function psubscribe () { const callOnWrite = function () { self.pubSubMode = self.pubSubMode || self.commandQueue.length + 1 } - this.queue.push(new Command('psubscribe', arr, callback, callOnWrite)) + this.queue.push(new Command('psubscribe', arr, callOnWrite)) return this } RedisClient.prototype.punsubscribe = function punsubscribe () { - let arr - let len = arguments.length - let callback - let i = 0 + var arr + var len = arguments.length + var i = 0 if (Array.isArray(arguments[0])) { arr = arguments[0].slice(0) - callback = arguments[1] } else { len = arguments.length - // The later should not be the average use case - if (len !== 0 && (typeof arguments[len - 1] === 'function' || typeof arguments[len - 1] === 'undefined')) { - len-- - callback = arguments[len] - } arr = new Array(len) for (; i < len; i += 1) { arr[i] = arguments[i] @@ -584,24 +480,17 @@ RedisClient.prototype.punsubscribe = function punsubscribe () { // Pub sub has to be activated even if not in pub sub mode, as the return value is manipulated in the callback self.pubSubMode = self.pubSubMode || self.commandQueue.length + 1 } - return this.internalSendCommand(new Command('punsubscribe', arr, callback, callOnWrite)) + return this.internalSendCommand(new Command('punsubscribe', arr, callOnWrite)) } Multi.prototype.punsubscribe = function punsubscribe () { - let arr - let len = arguments.length - let callback - let i = 0 + var arr + var len = arguments.length + var i = 0 if (Array.isArray(arguments[0])) { arr = arguments[0].slice(0) - callback = arguments[1] } else { len = arguments.length - // The later should not be the average use case - if (len !== 0 && (typeof arguments[len - 1] === 'function' || typeof arguments[len - 1] === 'undefined')) { - len-- - callback = arguments[len] - } arr = new Array(len) for (; i < len; i += 1) { arr[i] = arguments[i] @@ -612,6 +501,6 @@ Multi.prototype.punsubscribe = function punsubscribe () { // Pub sub has to be activated even if not in pub sub mode, as the return value is manipulated in the callback self.pubSubMode = self.pubSubMode || self.commandQueue.length + 1 } - this.queue.push(new Command('punsubscribe', arr, callback, callOnWrite)) + this.queue.push(new Command('punsubscribe', arr, callOnWrite)) return this } diff --git a/lib/multi.js b/lib/multi.js index 0af1c0b970..11f2e1f84a 100644 --- a/lib/multi.js +++ b/lib/multi.js @@ -4,10 +4,12 @@ const Queue = require('double-ended-queue') const utils = require('./utils') const Command = require('./command') +// TODO: Remove support for the non chaining way of using this +// It's confusing and has no benefit function Multi (client, args) { this._client = client this.queue = new Queue() - let command, tmpArgs + var command, tmpArgs if (args) { // Either undefined or an array. Fail hard if it's not an array for (let i = 0; i < args.length; i++) { command = args[i][0] @@ -25,163 +27,116 @@ function pipelineTransactionCommand (self, commandObj, index) { // Queueing is done first, then the commands are executed const tmp = commandObj.callback commandObj.callback = function (err, reply) { - // Ignore the multi command. This is applied by nodeRedis and the user does not benefit by it - if (err && index !== -1) { - if (tmp) { - tmp(err) - } + if (err) { + tmp(err) err.position = index self.errors.push(err) + return } // Keep track of who wants buffer responses: // By the time the callback is called the commandObj got the bufferArgs attribute attached self.wantsBuffers[index] = commandObj.bufferArgs - commandObj.callback = tmp + tmp(null, reply) } - self._client.internalSendCommand(commandObj) + return self._client.internalSendCommand(commandObj) } -Multi.prototype.execAtomic = function execAtomic (callback) { +Multi.prototype.execAtomic = function execAtomic () { if (this.queue.length < 2) { - return this.execBatch(callback) + return this.execBatch() } - return this.exec(callback) + return this.exec() } -function multiCallback (self, err, replies) { - let i = 0 - - if (err) { - err.errors = self.errors - if (self.callback) { - self.callback(err) - // Exclude connection errors so that those errors won't be emitted twice - } else if (err.code !== 'CONNECTION_BROKEN') { - self._client.emit('error', err) - } - return - } +function multiCallback (self, replies) { + var i = 0 if (replies) { for (let commandObj = self.queue.shift(); commandObj !== undefined; commandObj = self.queue.shift()) { - if (replies[i] instanceof Error) { + if (replies[i].message) { // instanceof Error const match = replies[i].message.match(utils.errCode) // LUA script could return user errors that don't behave like all other errors! if (match) { replies[i].code = match[1] } replies[i].command = commandObj.command.toUpperCase() - if (typeof commandObj.callback === 'function') { - commandObj.callback(replies[i]) - } + commandObj.callback(replies[i]) } else { // If we asked for strings, even in detectBuffers mode, then return strings: replies[i] = self._client.handleReply(replies[i], commandObj.command, self.wantsBuffers[i]) - if (typeof commandObj.callback === 'function') { - commandObj.callback(null, replies[i]) - } + commandObj.callback(null, replies[i]) } i++ } } - if (self.callback) { - self.callback(null, replies) - } + return replies } -Multi.prototype.execTransaction = function execTransaction (callback) { +Multi.prototype.execTransaction = function execTransaction () { if (this.monitoring || this._client.monitoring) { const err = new RangeError( 'Using transaction with a client that is in monitor mode does not work due to faulty return values of Redis.' ) err.command = 'EXEC' err.code = 'EXECABORT' - return utils.replyInOrder(this._client, callback, err) + return new Promise((resolve, reject) => { + utils.replyInOrder(this._client, (err, res) => { + if (err) return reject(err) + resolve(res) + }, null, []) + }) } - const self = this - const len = self.queue.length - self.errors = [] - self.callback = callback - self._client.cork() - self.wantsBuffers = new Array(len) - pipelineTransactionCommand(self, new Command('multi', []), -1) + const len = this.queue.length + this.errors = [] + this._client.cork() + this.wantsBuffers = new Array(len) + // Silently ignore this error. We'll receive the error for the exec as well + const promises = [this._client.internalSendCommand(new Command('multi', [])).catch(() => {})] // Drain queue, callback will catch 'QUEUED' or error for (let index = 0; index < len; index++) { // The commands may not be shifted off, since they are needed in the result handler - pipelineTransactionCommand(self, self.queue.get(index), index) + promises.push(pipelineTransactionCommand(this, this.queue.get(index), index).catch((e) => e)) } - self._client.internalSendCommand(new Command('exec', [], (err, replies) => { - multiCallback(self, err, replies) - })) - self._client.uncork() - return -} - -function batchCallback (self, cb, i) { - return function batchCallback (err, res) { - if (err) { - self.results[i] = err - // Add the position to the error - self.results[i].position = i - } else { - self.results[i] = res - } - cb(err, res) - } -} - -Multi.prototype.exec = Multi.prototype.execBatch = function execBatch (callback) { + const main = this._client.internalSendCommand(new Command('exec', [])) + this._client.uncork() const self = this - const len = self.queue.length - let index = 0 - let commandObj - if (len === 0) { - utils.replyInOrder(self._client, callback, null, []) - return + return Promise.all(promises).then(() => main.then((replies) => multiCallback(self, replies)).catch((err) => { + err.errors = self.errors + return Promise.reject(err) + })) +} + +Multi.prototype.exec = Multi.prototype.execBatch = function execBatch () { + if (this.queue.length === 0) { + // TODO: return an error if not "ready" + return new Promise((resolve) => { + utils.replyInOrder(this._client, (e, res) => { + resolve(res) + }, null, []) + }) } - self._client.cork() - if (!callback) { - for (commandObj = self.queue.shift(); commandObj !== undefined; commandObj = self.queue.shift()) { - self._client.internalSendCommand(commandObj) - } - self._client.uncork() - return + var error = false + this._client.cork() + const promises = [] + while (this.queue.length) { + const commandObj = this.queue.shift() + promises.push(this._client.internalSendCommand(commandObj).catch((e) => { + error = true + return e + })) } - const callbackWithoutOwnCb = function (err, res) { - if (err) { - self.results.push(err) - // Add the position to the error - const i = self.results.length - 1 - self.results[i].position = i - } else { - self.results.push(res) + this._client.uncork() + return Promise.all(promises).then((res) => { + if (error) { + const err = new Error('bla failed') + err.code = 'foo' + err.replies = res + return Promise.reject(err) } - // Do not emit an error here. Otherwise each error would result in one emit. - // The errors will be returned in the result anyway - } - const lastCallback = function (cb) { - return function (err, res) { - cb(err, res) - callback(null, self.results) - } - } - self.results = [] - for (commandObj = self.queue.shift(); commandObj !== undefined; commandObj = self.queue.shift()) { - if (typeof commandObj.callback === 'function') { - commandObj.callback = batchCallback(self, commandObj.callback, index) - } else { - commandObj.callback = callbackWithoutOwnCb - } - if (typeof callback === 'function' && index === len - 1) { - commandObj.callback = lastCallback(commandObj.callback) - } - this._client.internalSendCommand(commandObj) - index++ - } - self._client.uncork() - return + return res + }) } module.exports = Multi diff --git a/lib/utils.js b/lib/utils.js index c1fcbe669e..4f00586fc8 100644 --- a/lib/utils.js +++ b/lib/utils.js @@ -15,10 +15,13 @@ function replyToObject (reply) { } function replyToStrings (reply) { - if (reply instanceof Buffer) { + if (reply === null) { + return null + } + if (typeof reply.inspect === 'function') { // instanceof Buffer return reply.toString() } - if (reply instanceof Array) { + if (typeof reply.map === 'function') { // instanceof Array const res = new Array(reply.length) for (let i = 0; i < reply.length; i++) { // Recursively call the function as slowlog returns deep nested replies @@ -33,7 +36,7 @@ function replyToStrings (reply) { // 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) function clone (obj) { - let copy + var copy if (Array.isArray(obj)) { copy = new Array(obj.length) for (let i = 0; i < obj.length; i++) { @@ -56,18 +59,10 @@ function convenienceClone (obj) { return clone(obj) || {} } -function callbackOrEmit (self, callback, err, res) { - if (callback) { - callback(err, res) - } else if (err) { - self.emit('error', err) - } -} - function replyInOrder (self, callback, err, res, queue) { // If the queue is explicitly passed, use that, otherwise fall back to the offline queue first, // as there might be commands in both queues at the same time - let commandObj + var commandObj if (queue) { commandObj = queue.peekBack() } else { @@ -75,21 +70,15 @@ function replyInOrder (self, callback, err, res, queue) { } if (!commandObj) { process.nextTick(() => { - callbackOrEmit(self, callback, err, res) + callback(err, res) }) } else { + // TODO: Change this to chain promises instead const tmp = commandObj.callback - commandObj.callback = tmp - ? function (e, r) { - tmp(e, r) - callbackOrEmit(self, callback, err, res) - } - : function (e) { - if (e) { - self.emit('error', e) - } - callbackOrEmit(self, callback, err, res) - } + commandObj.callback = function (e, r) { + tmp(e, r) + callback(err, res) + } } } @@ -99,6 +88,5 @@ module.exports = { errCode: /^([A-Z]+)\s+(.+)$/, monitorRegex: /^[0-9]{10,11}\.[0-9]+ \[[0-9]+ .+]( ".+?")+$/, clone: convenienceClone, - callbackOrEmit, replyInOrder } diff --git a/package.json b/package.json index c3266d5a7c..369d41cfa7 100644 --- a/package.json +++ b/package.json @@ -23,7 +23,7 @@ "test": "nyc --cache mocha ./test/*.js ./test/commands/*.js --timeout=8000", "posttest": "npm run coverage", "compare": "node benchmarks/diff_multi_bench_output.js beforeBench.txt afterBench.txt", - "lint": "standard --fix" + "lint": "eslint . --fix" }, "dependencies": { "double-ended-queue": "^2.1.0-0", @@ -35,7 +35,6 @@ "node": ">=6" }, "devDependencies": { - "bluebird": "^3.0.2", "coveralls": "^2.11.2", "intercept-stdout": "~0.1.2", "metrics": "^0.1.9", diff --git a/test/auth.spec.js b/test/auth.spec.js index 8e2d0a2e80..75a53f0281 100644 --- a/test/auth.spec.js +++ b/test/auth.spec.js @@ -30,81 +30,42 @@ if (process.platform !== 'win32') { client.end(false) }) - it('allows auth to be provided with \'auth\' method', function (done) { + it('allows auth to be provided with \'auth\' method', function () { if (helper.redisProcess().spawnFailed()) this.skip() client = redis.createClient.apply(null, args) - client.auth(auth, (err, res) => { - assert.strictEqual(null, err) - assert.strictEqual('OK', res.toString()) - return done(err) - }) + return client.auth(auth).then(helper.isString('OK')) }) - it('support redis 2.4 with retrying auth commands if still loading', function (done) { + it('returns error when auth is bad', function () { if (helper.redisProcess().spawnFailed()) this.skip() client = redis.createClient.apply(null, args) - const time = Date.now() - client.auth(auth, (err, res) => { - assert.strictEqual(err, null) - assert.strictEqual('retry worked', res) - const now = Date.now() - // Hint: setTimeout sometimes triggers early and therefore the value can be like one or two ms to early - assert(now - time >= 98, `Time should be above 100 ms (the reconnect time) and is ${now - time}`) - assert(now - time < 225, `Time should be below 255 ms (the reconnect should only take a bit above 100 ms) and is ${now - time}`) - done() - }) - const tmp = client.commandQueue.get(0).callback - client.commandQueue.get(0).callback = function (err) { - assert.strictEqual(err, null) - client.auth = function (pass, callback) { - callback(null, 'retry worked') - } - tmp(new Error('ERR redis is still LOADING')) - } - }) - - it('emits error when auth is bad without callback', function (done) { - if (helper.redisProcess().spawnFailed()) this.skip() - - client = redis.createClient.apply(null, args) - - client.once('error', (err) => { + client.on('error', helper.isError(/Ready check failed: NOAUTH Authentication required./)) + return client.auth(`${auth}bad`).then(assert, (err) => { assert.strictEqual(err.command, 'AUTH') assert.ok(/ERR invalid password/.test(err.message)) - return done() }) - - client.auth(`${auth}bad`) }) - it('returns an error when auth is bad (empty string) with a callback', function (done) { + it('returns an error when auth is bad (empty string)', function () { if (helper.redisProcess().spawnFailed()) this.skip() client = redis.createClient.apply(null, args) - - client.auth('', (err) => { + client.on('error', helper.isError(/Ready check failed: NOAUTH Authentication required./)) + return client.auth('').then(helper.fail).catch((err) => { assert.strictEqual(err.command, 'AUTH') assert.ok(/ERR invalid password/.test(err.message)) - done() }) }) if (ip === 'IPv4') { - it('allows auth to be provided as part of redis url and do not fire commands before auth is done', function (done) { + it('allows auth to be provided as part of redis url and do not fire commands before auth is done', function () { if (helper.redisProcess().spawnFailed()) this.skip() - const end = helper.callFuncAfter(done, 2) client = redis.createClient(`redis://:${auth}@${config.HOST[ip]}:${config.PORT}`) - client.on('ready', () => { - end() - }) - // The info command may be used while loading but not if not yet authenticated - client.info((err) => { - assert.strictEqual(err, null) - end(err) - }) + // The info command may be used while loading but not if not yet authenticated + return client.info() }) it('allows auth and database to be provided as part of redis url query parameter', function (done) { @@ -115,16 +76,17 @@ if (process.platform !== 'win32') { assert.strictEqual(client.options.password, auth) assert.strictEqual(client.authPass, auth) client.on('ready', () => { - // Set a key so the used database is returned in the info command - client.set('foo', 'bar') - client.get('foo') + const promises = [] + // Set a key so the used database is returned in the info command + promises.push(client.set('foo', 'bar')) + promises.push(client.get('foo')) assert.strictEqual(client.serverInfo.db2, undefined) - // Using the info command should update the serverInfo - client.info((err) => { - assert.strictEqual(err, null) + // Using the info command should update the serverInfo + promises.push(client.info().then(() => { assert(typeof client.serverInfo.db2 === 'object') - }) - client.flushdb(done) + })) + promises.push(client.flushdb()) + return Promise.all(promises).then(() => done()) }) }) } @@ -155,7 +117,7 @@ if (process.platform !== 'win32') { const args = config.configureClient(ip) client = redis.createClient.apply(null, args) - client.auth(auth) + client.auth(auth).catch(done) client.on('ready', done) }) @@ -163,7 +125,7 @@ if (process.platform !== 'win32') { if (helper.redisProcess().spawnFailed()) this.skip() client = redis.createClient.apply(null, args) - client.auth(auth) + client.auth(auth).catch(done) client.on('ready', function () { if (this.timesConnected < 3) { let interval = setInterval(() => { @@ -173,8 +135,8 @@ if (process.platform !== 'win32') { clearInterval(interval) interval = null client.stream.destroy() - client.set('foo', 'bar') - client.get('foo') // Errors would bubble + client.set('foo', 'bar').catch(done) + client.get('foo').catch(done) assert.strictEqual(client.offlineQueue.length, 2) }, 1) } else { @@ -190,11 +152,11 @@ if (process.platform !== 'win32') { if (helper.redisProcess().spawnFailed()) this.skip() const args = config.configureClient(ip, { - authPass: auth + password: auth }) client = redis.createClient.apply(null, args) client.on('ready', () => { - client.auth(auth, helper.isString('OK', done)) + client.auth(auth).then(helper.isString('OK')).then(done).catch(done) }) }) @@ -206,7 +168,7 @@ if (process.platform !== 'win32') { }) client = redis.createClient.apply(null, args) client.on('ready', () => { - client.set('foo', 'bar', (err) => { + client.set('foo', 'bar').catch((err) => { assert.strictEqual(err.message, 'NOAUTH Authentication required.') assert.strictEqual(err.code, 'NOAUTH') assert.strictEqual(err.command, 'SET') @@ -245,14 +207,11 @@ if (process.platform !== 'win32') { }) client = redis.createClient.apply(null, args) client.set('foo', 'bar') - client.subscribe('somechannel', 'another channel', (err) => { - assert.strictEqual(err, null) - client.once('ready', () => { - assert.strictEqual(client.pubSubMode, 1) - client.get('foo', (err) => { - assert(/ERR only \(P\)SUBSCRIBE \/ \(P\)UNSUBSCRIBE/.test(err.message)) - done() - }) + client.subscribe('somechannel', 'another channel').then(() => { + assert.strictEqual(client.pubSubMode, 1) + client.get('foo').catch((err) => { + assert(/ERR only \(P\)SUBSCRIBE \/ \(P\)UNSUBSCRIBE/.test(err.message)) + done() }) }) client.once('ready', () => { @@ -282,27 +241,17 @@ if (process.platform !== 'win32') { }) client.batch() .auth(auth) - .select(5, (err, res) => { - assert.strictEqual(err, null) - assert.strictEqual(client.selectedDb, 5) - assert.strictEqual(res, 'OK') - assert.notDeepEqual(client.serverInfo.db5, { avgTtl: 0, expires: 0, keys: 1 }) - }) + .select(5) .monitor() - .set('foo', 'bar', helper.isString('OK')) - .info('stats', (err, res) => { - assert.strictEqual(err, null) - assert.strictEqual(res.indexOf('# Stats\r\n'), 0) - assert.strictEqual(client.serverInfo.sync_full, '0') - }) - .get('foo', helper.isString('bar')) - .subscribe(['foo', 'bar', 'foo'], helper.isDeepEqual([2, ['foo', 'bar', 'foo']])) + .set('foo', 'bar') + .info('stats') + .get('foo') + .subscribe(['foo', 'bar', 'foo']) .unsubscribe('foo') - .subscribe('/foo', helper.isDeepEqual([2, ['/foo']])) + .subscribe('/foo') .psubscribe('*') - .quit(helper.isString('OK')) - .exec((err, res) => { - assert.strictEqual(err, null) + .quit() + .exec().then((res) => { res[4] = res[4].substr(0, 9) assert.deepStrictEqual( res, diff --git a/test/batch.spec.js b/test/batch.spec.js index 1bd832e366..5af6ef9871 100644 --- a/test/batch.spec.js +++ b/test/batch.spec.js @@ -11,36 +11,21 @@ describe('The \'batch\' method', () => { describe('when not connected', () => { let client - beforeEach((done) => { + beforeEach(() => { client = redis.createClient.apply(null, args) - client.once('connect', () => { - client.quit() - }) - client.on('end', done) + return client.quit() }) - it('returns an empty array for missing commands', (done) => { + it('returns an empty array for missing commands', () => { const batch = client.batch() - batch.exec((err, res) => { - assert.strictEqual(err, null) - assert.strictEqual(res.length, 0) - done() - }) + return batch.exec().then(helper.isDeepEqual([])) }) - it('returns an error for batch with commands', (done) => { + it('returns an error for batch with commands', () => { const batch = client.batch() batch.set('foo', 'bar') - batch.exec((err, res) => { - assert.strictEqual(err, null) - assert.strictEqual(res[0].code, 'NR_CLOSED') - done() - }) - }) - - it('returns an empty array for missing commands if promisified', () => { - return client.batch().execAsync().then((res) => { - assert.strictEqual(res.length, 0) + return batch.exec().then(helper.fail).catch((err) => { + assert.strictEqual(err.replies[0].code, 'NR_CLOSED') }) }) }) @@ -48,122 +33,85 @@ describe('The \'batch\' method', () => { describe('when connected', () => { let client - beforeEach((done) => { + beforeEach(() => { client = redis.createClient.apply(null, args) - client.once('ready', () => { - client.flushdb((err) => { - return done(err) - }) - }) + return client.flushdb() }) afterEach(() => { client.end(true) }) - it('returns an empty array and keep the execution order in tact', (done) => { + it('returns an empty array and keep the execution order in tact', () => { let called = false - client.set('foo', 'bar', () => { + client.set('foo', 'bar').then(() => { called = true }) const batch = client.batch() - batch.exec((err, res) => { - assert.strictEqual(err, null) + return batch.exec().then((res) => { assert.strictEqual(res.length, 0) assert(called) - done() }) }) - it('runs normal calls in-between batch', (done) => { + it('runs normal calls in-between batch', () => { const batch = client.batch() batch.set('m1', '123') - client.set('m2', '456', done) + return client.set('m2', '456') }) - it('returns an empty array if promisified', () => { - return client.batch().execAsync().then((res) => { - assert.strictEqual(res.length, 0) - }) - }) - - it('returns an empty result array', (done) => { + it('returns an empty result array', () => { const batch = client.batch() let async = true - batch.exec((err, res) => { - assert.strictEqual(err, null) + const promise = batch.exec().then((res) => { assert.strictEqual(res.length, 0) async = false - done() }) assert(async) + return promise }) - it('fail individually when one command fails using chaining notation', (done) => { + it('fail individually when one command fails using chaining notation', () => { const batch1 = client.batch() - batch1.mset('batchfoo', '10', 'batchbar', '20', helper.isString('OK')) - - // Provoke an error at queue time - batch1.set('foo2', helper.isError()) - batch1.incr('batchfoo') - batch1.incr('batchbar') - batch1.exec(() => { - // Confirm that the previous command, while containing an error, still worked. - const batch2 = client.batch() - batch2.get('foo2', helper.isNull()) - batch2.incr('batchbar', helper.isNumber(22)) - batch2.incr('batchfoo', helper.isNumber(12)) - batch2.exec((err, replies) => { - assert.strictEqual(err, null) - assert.strictEqual(null, replies[0]) - assert.strictEqual(22, replies[1]) - assert.strictEqual(12, replies[2]) - return done() - }) - }) - }) - - it('fail individually when one command fails and emit the error if no callback has been provided', (done) => { - client.on('error', (err) => { - done(err) - }) - const batch1 = client.batch() - batch1.mset('batchfoo', '10', 'batchbar', '20', helper.isString('OK')) + batch1.mset('batchfoo', '10', 'batchbar', '20') // Provoke an error at queue time batch1.set('foo2') batch1.incr('batchfoo') batch1.incr('batchbar') - batch1.exec((err, res) => { - // TODO: This should actually return an error! - assert.strictEqual(err, null) - assert.strictEqual(res[1].command, 'SET') - assert.strictEqual(res[1].code, 'ERR') - done() + return batch1.exec().then(helper.fail).catch(() => { + // Confirm that the previous command, while containing an error, still worked. + const batch2 = client.batch() + batch2.get('foo2') + batch2.incr('batchbar') + batch2.incr('batchfoo') + return batch2.exec().then((replies) => { + assert.strictEqual(null, replies[0]) + assert.strictEqual(22, replies[1]) + assert.strictEqual(12, replies[2]) + }) }) }) - it('fail individually when one command in an array of commands fails', (done) => { + it('fail individually when one command in an array of commands fails', () => { // test nested batch-bulk replies - client.batch([ - ['mget', 'batchfoo', 'batchbar', helper.isDeepEqual([null, null])], - ['set', 'foo2', helper.isError()], + return client.batch([ + ['mget', 'batchfoo', 'batchbar'], + ['set', 'foo2'], ['incr', 'batchfoo'], ['incr', 'batchbar'] - ]).exec((err, replies) => { - // TODO: This should actually return an error! - assert.strictEqual(err, null) + ]).exec().then(helper.fail).catch((err) => { + const replies = err.replies assert.strictEqual(2, replies[0].length) assert.strictEqual(null, replies[0][0]) assert.strictEqual(null, replies[0][1]) assert.strictEqual('SET', replies[1].command) assert.strictEqual('1', replies[2].toString()) assert.strictEqual('1', replies[3].toString()) - return done() }) }) - it('handles multiple operations being applied to a set', (done) => { + it('handles multiple operations being applied to a set', () => { client.sadd('some set', 'mem 1') client.sadd(['some set', 'mem 2']) client.sadd('some set', 'mem 3') @@ -171,60 +119,51 @@ describe('The \'batch\' method', () => { // make sure empty mb reply works client.del('some missing set') - client.smembers('some missing set', (err, reply) => { - assert.strictEqual(err, null) + client.smembers('some missing set').then((reply) => { // make sure empty mb reply works assert.strictEqual(0, reply.length) }) // test nested batch-bulk replies with empty mb elements. - client.batch([ + return client.batch([ ['smembers', ['some set']], ['del', 'some set'], - ['smembers', 'some set', undefined] // The explicit undefined is handled as a callback that is undefined + ['smembers', 'some set'] ]) .scard('some set') - .exec((err, replies) => { - assert.strictEqual(err, null) + .exec().then((replies) => { assert.strictEqual(4, replies[0].length) assert.strictEqual(0, replies[2].length) - return done() }) }) - it('allows multiple operations to be performed using constructor with all kinds of syntax', (done) => { + it('allows multiple operations to be performed using constructor with all kinds of syntax', () => { const now = Date.now() const arr = ['batchhmset', 'batchbar', 'batchbaz'] const arr2 = ['some manner of key', 'otherTypes'] const arr3 = [5768, 'batchbarx', 'batchfoox'] - const arr4 = ['mset', [578, 'batchbar'], helper.isString('OK')] - client.batch([ + const arr4 = ['mset', [578, 'batchbar']] + return client.batch([ arr4, - [['mset', 'batchfoo2', 'batchbar2', 'batchfoo3', 'batchbar3'], helper.isString('OK')], + [['mset', 'batchfoo2', 'batchbar2', 'batchfoo3', 'batchbar3']], ['hmset', arr], - [['hmset', 'batchhmset2', 'batchbar2', 'batchfoo3', 'batchbar3', 'test'], helper.isString('OK')], - ['hmset', ['batchhmset', 'batchbar', 'batchfoo'], helper.isString('OK')], - ['hmset', arr3, helper.isString('OK')], + [['hmset', 'batchhmset2', 'batchbar2', 'batchfoo3', 'batchbar3', 'test']], + ['hmset', ['batchhmset', 'batchbar', 'batchfoo']], + ['hmset', arr3], ['hmset', now, {123456789: 'abcdefghij', 'some manner of key': 'a type of value', 'otherTypes': 555}], - ['hmset', 'key2', {'0123456789': 'abcdefghij', 'some manner of key': 'a type of value', 'otherTypes': 999}, helper.isString('OK')], + ['hmset', 'key2', {'0123456789': 'abcdefghij', 'some manner of key': 'a type of value', 'otherTypes': 999}], ['hmset', 'batchhmset', ['batchbar', 'batchbaz']], - ['hmset', 'batchhmset', ['batchbar', 'batchbaz'], helper.isString('OK')] + ['hmset', 'batchhmset', ['batchbar', 'batchbaz']] ]) .hmget(now, 123456789, 'otherTypes') - .hmget('key2', arr2, () => {}) + .hmget('key2', arr2) .hmget(['batchhmset2', 'some manner of key', 'batchbar3']) - .mget('batchfoo2', ['batchfoo3', 'batchfoo'], (err, res) => { - assert.strictEqual(err, null) - assert.strictEqual(res[0], 'batchbar2') - assert.strictEqual(res[1], 'batchbar3') - assert.strictEqual(res[2], null) - }) - .exec((err, replies) => { + .mget('batchfoo2', ['batchfoo3', 'batchfoo']) + .exec().then((replies) => { assert.strictEqual(arr.length, 3) assert.strictEqual(arr2.length, 2) assert.strictEqual(arr3.length, 3) - assert.strictEqual(arr4.length, 3) - assert.strictEqual(null, err) + assert.strictEqual(arr4.length, 2) assert.strictEqual(replies[10][1], '555') assert.strictEqual(replies[11][0], 'a type of value') assert.strictEqual(replies[12][0], null) @@ -232,93 +171,62 @@ describe('The \'batch\' method', () => { assert.strictEqual(replies[13][0], 'batchbar2') assert.strictEqual(replies[13].length, 3) assert.strictEqual(replies.length, 14) - return done() }) }) - it('converts a non string key to a string', (done) => { + it('converts a non string key to a string', () => { // TODO: Converting the key might change soon again. - client.batch().hmset(true, { + return client.batch().hmset(true, { test: 123, bar: 'baz' - }).exec(done) + }).exec() }) - it('runs a batch without any further commands', (done) => { - client.batch().exec((err, res) => { - assert.strictEqual(err, null) + it('runs a batch without any further commands', () => { + return client.batch().exec().then((res) => { assert.strictEqual(res.length, 0) - done() }) }) - it('runs a batch without any further commands and without callback', () => { - client.batch().exec() - }) - - it('allows multiple operations to be performed using a chaining API', (done) => { - client.batch() + it('allows multiple operations to be performed using a chaining API', () => { + return client.batch() .mset('some', '10', 'keys', '20') .incr('some') .incr('keys') .mget('some', 'keys') - .exec(helper.isDeepEqual(['OK', 11, 21, ['11', '21']], done)) + .exec().then(helper.isDeepEqual(['OK', 11, 21, ['11', '21']])) }) - it('allows multiple commands to work the same as normal to be performed using a chaining API', (done) => { - client.batch() - .mset(['some', '10', 'keys', '20']) - .incr('some', helper.isNumber(11)) - .incr(['keys'], helper.isNumber(21)) - .mget('some', 'keys') - .exec(helper.isDeepEqual(['OK', 11, 21, ['11', '21']], done)) - }) - - it('allows multiple commands to work the same as normal to be performed using a chaining API promisified', () => { + it('allows multiple commands to work the same as normal to be performed using a chaining API', () => { return client.batch() .mset(['some', '10', 'keys', '20']) - .incr('some', helper.isNumber(11)) - .incr(['keys'], helper.isNumber(21)) + .incr('some') + .incr(['keys']) .mget('some', 'keys') - .execAsync() - .then((res) => { - helper.isDeepEqual(['OK', 11, 21, ['11', '21']])(null, res) - }) + .exec().then(helper.isDeepEqual(['OK', 11, 21, ['11', '21']])) }) - it('allows an array to be provided indicating multiple operations to perform', (done) => { + it('allows an array to be provided indicating multiple operations to perform', () => { // test nested batch-bulk replies with nulls. - client.batch([ + return client.batch([ ['mget', ['batchfoo', 'some', 'random value', 'keys']], ['incr', 'batchfoo'] ]) - .exec((err, replies) => { - assert.strictEqual(err, null) + .exec().then((replies) => { assert.strictEqual(replies.length, 2) assert.strictEqual(replies[0].length, 4) - return done() }) }) - it('allows multiple operations to be performed on a hash', (done) => { - client.batch() + it('allows multiple operations to be performed on a hash', () => { + return client.batch() .hmset('batchhash', 'a', 'foo', 'b', 1) .hmset('batchhash', { extra: 'fancy', things: 'here' }) .hgetall('batchhash') - .exec(done) - }) - - it('should work without any callback or arguments', (done) => { - const batch = client.batch() - batch.set('baz', 'binary') - batch.set('foo', 'bar') - batch.ping() - batch.exec() - - client.get('foo', helper.isString('bar', done)) + .exec() }) }) }) diff --git a/test/commands/blpop.spec.js b/test/commands/blpop.spec.js index 8df695ff23..542818911e 100644 --- a/test/commands/blpop.spec.js +++ b/test/commands/blpop.spec.js @@ -12,14 +12,12 @@ describe('The \'blpop\' method', () => { let client let bclient - beforeEach((done) => { + beforeEach(() => { client = redis.createClient.apply(null, args) - client.once('ready', () => { - client.flushdb(done) - }) + return client.flushdb() }) - it('pops value immediately if list contains values', (done) => { + it('pops value immediately if list contains values', () => { bclient = redis.createClient.apply(null, args) redis.debugMode = true let text = '' @@ -27,43 +25,42 @@ describe('The \'blpop\' method', () => { text += data return '' }) - client.rpush('blocking list', 'initial value', helper.isNumber(1)) + const values = ['blocking list', 'initial value'] + const promise = client.rpush(values).then(helper.isNumber(1)) unhookIntercept() - assert(/^Send 127\.0\.0\.1:6379 id [0-9]+: \*3\r\n\$5\r\nrpush\r\n\$13\r\nblocking list\r\n\$13\r\ninitial value\r\n\n$/.test(text)) + assert(/Send 127\.0\.0\.1:6379 id [0-9]+: \*3\r\n\$5\r\nrpush\r\n\$13\r\nblocking list\r\n\$13\r\ninitial value\r\n\n/.test(text), text) redis.debugMode = false - bclient.blpop('blocking list', 0, (err, value) => { - assert.strictEqual(value[0], 'blocking list') - assert.strictEqual(value[1], 'initial value') - return done(err) - }) + return promise + .then(() => bclient.blpop(values[0], 0)) + .then(helper.isDeepEqual(values)) }) - it('pops value immediately if list contains values using array notation', (done) => { + it('pops value immediately if list contains values using array notation', () => { bclient = redis.createClient.apply(null, args) - client.rpush(['blocking list', 'initial value'], helper.isNumber(1)) - bclient.blpop(['blocking list', 0], (err, value) => { - assert.strictEqual(value[0], 'blocking list') - assert.strictEqual(value[1], 'initial value') - return done(err) - }) + return client.rpush(['blocking list', 'initial value']) + .then(helper.isNumber(1)) + .then(() => bclient.blpop(['blocking list', 0])) + .then(helper.isDeepEqual(['blocking list', 'initial value'])) }) - it('waits for value if list is not yet populated', (done) => { + it('waits for value if list is not yet populated', () => { bclient = redis.createClient.apply(null, args) - bclient.blpop('blocking list 2', 5, (err, value) => { - assert.strictEqual(value[0], 'blocking list 2') - assert.strictEqual(value[1], 'initial value') - return done(err) - }) - client.rpush('blocking list 2', 'initial value', helper.isNumber(1)) + const promises = [ + bclient.blpop('blocking list 2', 5).then(helper.isDeepEqual(['blocking list 2', 'initial value'])) + ] + promises.push(new Promise((resolve, reject) => { + setTimeout(() => { + resolve(client.rpush('blocking list 2', 'initial value').then(helper.isNumber(1))) + }, 100) + })) + return Promise.all(promises) }) - it('times out after specified time', (done) => { + it('times out after specified time', () => { bclient = redis.createClient.apply(null, args) - bclient.blpop('blocking list', 1, (err, res) => { - assert.strictEqual(res, null) - return done(err) - }) + return bclient.blpop('blocking list', 1) + .then(helper.fail) + .catch(helper.isError()) }) afterEach(() => { diff --git a/test/commands/client.spec.js b/test/commands/client.spec.js index 0af4bd062a..995209d71b 100644 --- a/test/commands/client.spec.js +++ b/test/commands/client.spec.js @@ -13,11 +13,9 @@ describe('The \'client\' method', () => { describe(`using ${ip}`, () => { let client - beforeEach((done) => { + beforeEach(() => { client = redis.createClient.apply(null, args) - client.once('ready', () => { - client.flushdb(done) - }) + return client.flushdb() }) afterEach(() => { @@ -25,93 +23,93 @@ describe('The \'client\' method', () => { }) describe('list', () => { - it('lists connected clients', (done) => { - client.client('LIST', helper.match(pattern, done)) + it('lists connected clients', () => { + return client.client('LIST').then(helper.match(pattern)) }) - it('lists connected clients when invoked with multi\'s chaining syntax', (done) => { - client.multi().client('list', helper.isType.string()).exec(helper.match(pattern, done)) + it('lists connected clients when invoked with multi\'s chaining syntax', () => { + return client.multi().client('list').exec().then(helper.match(pattern)) }) - it('lists connected clients when invoked with array syntax on client', (done) => { - client.multi().client(['list']).exec(helper.match(pattern, done)) + it('lists connected clients when invoked with array syntax on client', () => { + return client.multi().client(['list']).exec().then(helper.match(pattern)) }) - it('lists connected clients when invoked with multi\'s array syntax', (done) => { - client.multi([ + it('lists connected clients when invoked with multi\'s array syntax', () => { + return client.multi([ ['client', 'list'] - ]).exec(helper.match(pattern, done)) + ]).exec().then(helper.match(pattern)) }) }) describe('reply', () => { describe('as normal command', () => { - it('on', function (done) { + it('on', function () { helper.serverVersionAtLeast.call(this, client, [3, 2, 0]) assert.strictEqual(client.reply, 'ON') - client.client('reply', 'on', helper.isString('OK')) + const promises = [client.client('reply', 'on').then(helper.isString('OK'))] assert.strictEqual(client.reply, 'ON') - client.set('foo', 'bar', done) + promises.push(client.set('foo', 'bar')) + return Promise.all(promises) }) - it('off', function (done) { + it('off', function () { helper.serverVersionAtLeast.call(this, client, [3, 2, 0]) assert.strictEqual(client.reply, 'ON') - client.client(Buffer.from('REPLY'), 'OFF', helper.isUndefined()) + const promises = [client.client(Buffer.from('REPLY'), 'OFF').then(helper.isUndefined())] assert.strictEqual(client.reply, 'OFF') - client.set('foo', 'bar', helper.isUndefined(done)) + promises.push(client.set('foo', 'bar').then(helper.isUndefined())) + return Promise.all(promises) }) - it('skip', function (done) { + it('skip', function () { helper.serverVersionAtLeast.call(this, client, [3, 2, 0]) assert.strictEqual(client.reply, 'ON') - client.client('REPLY', Buffer.from('SKIP'), helper.isUndefined()) + const promises = [client.client('REPLY', Buffer.from('SKIP')).then(helper.isUndefined())] assert.strictEqual(client.reply, 'SKIP_ONE_MORE') - client.set('foo', 'bar', helper.isUndefined()) - client.get('foo', helper.isString('bar', done)) + promises.push(client.set('foo', 'bar').then(helper.isUndefined())) + promises.push(client.get('foo').then(helper.isString('bar'))) + return Promise.all(promises) }) }) describe('in a batch context', () => { - it('on', function (done) { + it('on', function () { helper.serverVersionAtLeast.call(this, client, [3, 2, 0]) const batch = client.batch() assert.strictEqual(client.reply, 'ON') - batch.client('reply', 'on', helper.isString('OK')) + batch.client('reply', 'on') assert.strictEqual(client.reply, 'ON') batch.set('foo', 'bar') - batch.exec((err, res) => { - assert.deepEqual(res, ['OK', 'OK']) - done(err) - }) + return batch.exec().then(helper.isDeepEqual(['OK', 'OK'])) }) - it('off', function (done) { + it('off', function () { helper.serverVersionAtLeast.call(this, client, [3, 2, 0]) const batch = client.batch() assert.strictEqual(client.reply, 'ON') batch.set('hello', 'world') - batch.client(Buffer.from('REPLY'), Buffer.from('OFF'), helper.isUndefined()) - batch.set('foo', 'bar', helper.isUndefined()) - batch.exec((err, res) => { + batch.client(Buffer.from('REPLY'), Buffer.from('OFF')) + batch.get('hello') + batch.get('hello') + return batch.exec().then((res) => { assert.strictEqual(client.reply, 'OFF') - assert.deepEqual(res, ['OK', undefined, undefined]) - done(err) + assert.deepStrictEqual(res, ['OK', undefined, undefined, undefined]) }) }) - it('skip', function (done) { + it('skip', function () { helper.serverVersionAtLeast.call(this, client, [3, 2, 0]) assert.strictEqual(client.reply, 'ON') - client.batch() + return client.batch() .set('hello', 'world') - .client('REPLY', 'SKIP', helper.isUndefined()) - .set('foo', 'bar', helper.isUndefined()) + .client('REPLY', 'SKIP') + .set('foo', 'bar') .get('foo') - .exec((err, res) => { + .exec() + .then((res) => { assert.strictEqual(client.reply, 'ON') - assert.deepEqual(res, ['OK', undefined, undefined, 'bar']) - done(err) + assert.deepStrictEqual(res, ['OK', undefined, undefined, 'bar']) }) }) }) @@ -122,25 +120,23 @@ describe('The \'client\' method', () => { beforeEach((done) => { client2 = redis.createClient.apply(null, args) - client2.once('ready', () => { - done() - }) + client2.once('ready', done) }) afterEach(() => { client2.end(true) }) - it('sets the name', (done) => { + it('sets the name', () => { // The querys are auto pipelined and the response is a response to all querys of one client // per chunk. So the execution order is only guaranteed on each client - const end = helper.callFuncAfter(done, 2) - - client.client('setname', 'RUTH') - client2.client('setname', ['RENEE'], helper.isString('OK')) - client2.client(['setname', 'MARTIN'], helper.isString('OK')) - client2.client('getname', helper.isString('MARTIN', end)) - client.client('getname', helper.isString('RUTH', end)) + return Promise.all([ + client.client('setname', 'RUTH'), + client2.client('setname', ['RENEE']).then(helper.isString('OK')), + client2.client(['setname', 'MARTIN']).then(helper.isString('OK')), + client2.client('getname').then(helper.isString('MARTIN')), + client.client('getname').then(helper.isString('RUTH')) + ]) }) }) }) diff --git a/test/commands/dbsize.spec.js b/test/commands/dbsize.spec.js index d8fab90d2e..238b530f62 100644 --- a/test/commands/dbsize.spec.js +++ b/test/commands/dbsize.spec.js @@ -19,18 +19,14 @@ describe('The \'dbsize\' method', () => { describe('when not connected', () => { let client - beforeEach((done) => { + beforeEach(() => { client = redis.createClient.apply(null, args) - client.once('ready', () => { - client.quit() - }) - client.on('end', done) + return client.quit() }) - it('reports an error', (done) => { - client.dbsize([], (err, res) => { + it('reports an error', () => { + return client.dbsize([]).then(helper.fail).catch((err) => { assert(err.message.match(/The connection is already closed/)) - done() }) }) }) @@ -38,52 +34,34 @@ describe('The \'dbsize\' method', () => { describe('when connected', () => { let client - beforeEach((done) => { + beforeEach(() => { client = redis.createClient.apply(null, args) - client.once('ready', () => { - client.flushdb((err, res) => { - helper.isString('OK')(err, res) - done() - }) - }) + return client.flushdb().then(helper.isString('OK')) }) afterEach(() => { client.end(true) }) - it('returns a zero db size', (done) => { - client.dbsize([], (err, res) => { - helper.isNotError()(err, res) - helper.isType.number()(err, res) - assert.strictEqual(res, 0, 'Initial db size should be 0') - done() - }) + it('returns a zero db size', () => { + return client.dbsize([]).then(helper.isNumber(0)) }) describe('when more data is added to Redis', () => { let oldSize - beforeEach((done) => { - client.dbsize((err, res) => { - helper.isType.number()(err, res) - assert.strictEqual(res, 0, 'Initial db size should be 0') - + beforeEach(() => { + return client.dbsize().then((res) => { + helper.isNumber(0)(res) oldSize = res - - client.set(key, value, (err, res) => { - helper.isNotError()(err, res) - done() - }) + return client.set(key, value).then(helper.isString('OK')) }) }) - it('returns a larger db size', (done) => { - client.dbsize([], (err, res) => { - helper.isNotError()(err, res) - helper.isType.positiveNumber()(err, res) + it('returns a larger db size', () => { + return client.dbsize([]).then((res) => { + assert.strictEqual(typeof res, 'number') assert.strictEqual(true, (oldSize < res), 'Adding data should increase db size.') - done() }) }) }) diff --git a/test/commands/del.spec.js b/test/commands/del.spec.js index 34f3d46946..68606922eb 100644 --- a/test/commands/del.spec.js +++ b/test/commands/del.spec.js @@ -9,42 +9,39 @@ describe('The \'del\' method', () => { describe(`using ${ip}`, () => { let client - beforeEach((done) => { + beforeEach(() => { client = redis.createClient.apply(null, args) - client.once('ready', () => { - client.flushdb(done) - }) + return client.flushdb() }) - it('allows a single key to be deleted', (done) => { - client.set('foo', 'bar') - client.del('foo', helper.isNumber(1)) - client.get('foo', helper.isNull(done)) + it('allows a single key to be deleted', () => { + return Promise.all([ + client.set('foo', 'bar'), + client.del('foo').then(helper.isNumber(1)), + client.get('foo').then(helper.isNull()) + ]) }) - it('allows del to be called on a key that does not exist', (done) => { - client.del('foo', helper.isNumber(0, done)) + it('allows del to be called on a key that does not exist', () => { + return client.del('foo').then(helper.isNumber(0)) }) - it('allows multiple keys to be deleted', (done) => { - client.mset('foo', 'bar', 'apple', 'banana') - client.del('foo', 'apple', helper.isNumber(2)) - client.get('foo', helper.isNull()) - client.get('apple', helper.isNull(done)) + it('allows multiple keys to be deleted', () => { + return Promise.all([ + client.mset('foo', 'bar', 'apple', 'banana'), + client.del('foo', 'apple').then(helper.isNumber(2)), + client.get('foo').then(helper.isNull()), + client.get('apple').then(helper.isNull()) + ]) }) - it('allows multiple keys to be deleted with the array syntax', (done) => { - client.mset('foo', 'bar', 'apple', 'banana') - client.del(['foo', 'apple'], helper.isNumber(2)) - client.get('foo', helper.isNull()) - client.get('apple', helper.isNull(done)) - }) - - it('allows multiple keys to be deleted with the array syntax and no callback', (done) => { - client.mset('foo', 'bar', 'apple', 'banana') - client.del(['foo', 'apple']) - client.get('foo', helper.isNull()) - client.get('apple', helper.isNull(done)) + it('allows multiple keys to be deleted with the array syntax', () => { + return Promise.all([ + client.mset('foo', 'bar', 'apple', 'banana'), + client.del(['foo', 'apple']).then(helper.isNumber(2)), + client.get('foo').then(helper.isNull()), + client.get('apple').then(helper.isNull()) + ]) }) afterEach(() => { diff --git a/test/commands/eval.spec.js b/test/commands/eval.spec.js index 157bcdb169..c72e2a2b02 100644 --- a/test/commands/eval.spec.js +++ b/test/commands/eval.spec.js @@ -12,48 +12,44 @@ describe('The \'eval\' method', () => { let client const source = 'return redis.call(\'set\', \'sha\', \'test\')' - beforeEach((done) => { + beforeEach(() => { client = redis.createClient.apply(null, args) - client.once('ready', () => { - client.flushdb(done) - }) + return client.flushdb() }) afterEach(() => { client.end(true) }) - it('converts a float to an integer when evaluated', (done) => { - client.eval('return 100.5', 0, helper.isNumber(100, done)) + it('converts a float to an integer when evaluated', () => { + return client.eval('return 100.5', 0).then(helper.isNumber(100)) }) - it('returns a string', (done) => { - client.eval('return \'hello world\'', 0, helper.isString('hello world', done)) + it('returns a string', () => { + return client.eval('return \'hello world\'', 0).then(helper.isString('hello world')) }) - it('converts boolean true to integer 1', (done) => { - client.eval('return true', 0, helper.isNumber(1, done)) + it('converts boolean true to integer 1', () => { + return client.eval('return true', 0).then(helper.isNumber(1)) }) - it('converts boolean false to null', (done) => { - client.eval('return false', 0, helper.isNull(done)) + it('converts boolean false to null', () => { + return client.eval('return false', 0).then(helper.isNull()) }) - it('converts lua status code to string representation', (done) => { - client.eval('return {ok=\'fine\'}', 0, helper.isString('fine', done)) + it('converts lua status code to string representation', () => { + return client.eval('return {ok=\'fine\'}', 0).then(helper.isString('fine')) }) - it('converts lua error to an error response', (done) => { - client.eval('return {err=\'this is an error\'}', 0, (err) => { + it('converts lua error to an error response', () => { + return client.eval('return {err=\'this is an error\'}', 0).then(helper.fail).catch((err) => { assert(err.code === undefined) helper.isError()(err) - done() }) }) - it('represents a lua table appropritely', (done) => { - client.eval('return {1,2,3,\'ciao\',{1,2}}', 0, (err, res) => { - assert.strictEqual(err, null) + it('represents a lua table appropriately', () => { + return client.eval('return {1,2,3,\'ciao\',{1,2}}', 0).then((res) => { assert.strictEqual(5, res.length) assert.strictEqual(1, res[0]) assert.strictEqual(2, res[1]) @@ -62,134 +58,109 @@ describe('The \'eval\' method', () => { assert.strictEqual(2, res[4].length) assert.strictEqual(1, res[4][0]) assert.strictEqual(2, res[4][1]) - return done() }) }) - it('populates keys and argv correctly', (done) => { - client.eval('return {KEYS[1],KEYS[2],ARGV[1],ARGV[2]}', 2, 'a', 'b', 'c', 'd', helper.isDeepEqual(['a', 'b', 'c', 'd'], done)) + it('populates keys and argv correctly', () => { + return client.eval('return {KEYS[1],KEYS[2],ARGV[1],ARGV[2]}', 2, 'a', 'b', 'c', 'd').then(helper.isDeepEqual(['a', 'b', 'c', 'd'])) }) - it('allows arguments to be provided in array rather than as multiple parameters', (done) => { - client.eval(['return {KEYS[1],KEYS[2],ARGV[1],ARGV[2]}', 2, 'a', 'b', 'c', 'd'], helper.isDeepEqual(['a', 'b', 'c', 'd'], done)) - }) - - it('allows a script to be executed that accesses the redis API without callback', (done) => { - client.eval(source, 0) - client.get('sha', helper.isString('test', done)) + it('allows arguments to be provided in array rather than as multiple parameters', () => { + return client.eval(['return {KEYS[1],KEYS[2],ARGV[1],ARGV[2]}', 2, 'a', 'b', 'c', 'd']).then(helper.isDeepEqual(['a', 'b', 'c', 'd'])) }) describe('evalsha', () => { const sha = crypto.createHash('sha1').update(source).digest('hex') - it('allows a script to be executed that accesses the redis API', (done) => { - client.eval(source, 0, helper.isString('OK')) - client.get('sha', helper.isString('test', done)) + it('allows a script to be executed that accesses the redis API', () => { + return Promise.all([ + client.eval(source, 0).then(helper.isString('OK')), + client.get('sha').then(helper.isString('test')) + ]) }) - it('can execute a script if the SHA exists', (done) => { - client.evalsha(sha, 0, helper.isString('OK')) - client.get('sha', helper.isString('test', done)) + it('can execute a script if the SHA exists', () => { + return Promise.all([ + client.evalsha(sha, 0).then(helper.isString('OK')), + client.get('sha').then(helper.isString('test')) + ]) }) - it('returns an error if SHA does not exist', (done) => { - client.evalsha('ffffffffffffffffffffffffffffffffffffffff', 0, helper.isError(done)) - }) - - it('emit an error if SHA does not exist without any callback', (done) => { - client.evalsha('ffffffffffffffffffffffffffffffffffffffff', 0) - client.on('error', (err) => { - assert.strictEqual(err.code, 'NOSCRIPT') - assert(/NOSCRIPT No matching script. Please use EVAL./.test(err.message)) - done() - }) - }) - - it('emits an error if SHA does not exist and no callback has been provided', (done) => { - client.on('error', (err) => { - assert.strictEqual(err.message, 'NOSCRIPT No matching script. Please use EVAL.') - done() - }) - client.evalsha('ffffffffffffffffffffffffffffffffffffffff', 0) + it('returns an error if SHA does not exist', () => { + return client.evalsha('ffffffffffffffffffffffffffffffffffffffff', 0) + .then(helper.fail) + .catch(helper.isError(/NOSCRIPT No matching script\. Please use EVAL/)) }) }) - it('allows a key to be incremented, and performs appropriate conversion from LUA type', (done) => { - client.set('incr key', 0, (err, reply) => { - if (err) return done(err) - client.eval('local foo = redis.call(\'incr\',\'incr key\')\nreturn {type(foo),foo}', 0, (err, res) => { + it('allows a key to be incremented, and performs appropriate conversion from LUA type', () => { + return Promise.all([ + client.set('incr key', 0), + client.eval('local foo = redis.call(\'incr\',\'incr key\')\nreturn {type(foo),foo}', 0).then((res) => { assert.strictEqual(2, res.length) assert.strictEqual('number', res[0]) assert.strictEqual(1, res[1]) - return done(err) }) - }) + ]) }) - it('allows a bulk operation to be performed, and performs appropriate conversion from LUA type', (done) => { - client.set('bulk reply key', 'bulk reply value', (err, res) => { - assert.strictEqual(err, null) - client.eval('local foo = redis.call(\'get\',\'bulk reply key\'); return {type(foo),foo}', 0, (err, res) => { + it('allows a bulk operation to be performed, and performs appropriate conversion from LUA type', () => { + return Promise.all([ + client.set('bulk reply key', 'bulk reply value'), + client.eval('local foo = redis.call(\'get\',\'bulk reply key\'); return {type(foo),foo}', 0).then((res) => { assert.strictEqual(2, res.length) assert.strictEqual('string', res[0]) assert.strictEqual('bulk reply value', res[1]) - return done(err) }) - }) + ]) }) - it('allows a multi mulk operation to be performed, with the appropriate type conversion', (done) => { - client.multi() + it('allows a multi mulk operation to be performed, with the appropriate type conversion', () => { + return client.multi() .del('mylist') .rpush('mylist', 'a') .rpush('mylist', 'b') .rpush('mylist', 'c') - .exec((err, replies) => { - if (err) return done(err) - client.eval('local foo = redis.call(\'lrange\',\'mylist\',0,-1); return {type(foo),foo[1],foo[2],foo[3],# foo}', 0, (err, res) => { + .exec().then((replies) => { + return client.eval('local foo = redis.call(\'lrange\',\'mylist\',0,-1); return {type(foo),foo[1],foo[2],foo[3],# foo}', 0).then((res) => { assert.strictEqual(5, res.length) assert.strictEqual('table', res[0]) assert.strictEqual('a', res[1]) assert.strictEqual('b', res[2]) assert.strictEqual('c', res[3]) assert.strictEqual(3, res[4]) - return done(err) }) }) }) - it('returns an appropriate representation of Lua status reply', (done) => { - client.eval('local foo = redis.call(\'set\',\'mykey\',\'myval\'); return {type(foo),foo[\'ok\']}', 0, (err, res) => { + it('returns an appropriate representation of Lua status reply', () => { + return client.eval('local foo = redis.call(\'set\',\'mykey\',\'myval\'); return {type(foo),foo[\'ok\']}', 0).then((res) => { assert.strictEqual(2, res.length) assert.strictEqual('table', res[0]) assert.strictEqual('OK', res[1]) - return done(err) }) }) - it('returns an appropriate representation of a Lua error reply', (done) => { - client.set('error reply key', 'error reply value', (err, res) => { - if (err) return done(err) - client.eval('local foo = redis.pcall(\'incr\',\'error reply key\'); return {type(foo),foo[\'err\']}', 0, (err, res) => { + it('returns an appropriate representation of a Lua error reply', () => { + return Promise.all([ + client.set('error reply key', 'error reply value'), + client.eval('local foo = redis.pcall(\'incr\',\'error reply key\'); return {type(foo),foo[\'err\']}', 0).then((res) => { assert.strictEqual(2, res.length) assert.strictEqual('table', res[0]) assert.strictEqual('ERR value is not an integer or out of range', res[1]) - return done(err) }) - }) + ]) }) - it('returns an appropriate representation of a Lua nil reply', (done) => { - client.del('nil reply key', (err, res) => { - if (err) return done(err) - client.eval('local foo = redis.call(\'get\',\'nil reply key\'); return {type(foo),foo == false}', 0, (err, res) => { - if (err) throw err + it('returns an appropriate representation of a Lua nil reply', () => { + return Promise.all([ + client.del('nil reply key'), + client.eval('local foo = redis.call(\'get\',\'nil reply key\'); return {type(foo),foo == false}', 0).then((res) => { assert.strictEqual(2, res.length) assert.strictEqual('boolean', res[0]) assert.strictEqual(1, res[1]) - return done(err) }) - }) + ]) }) }) }) diff --git a/test/commands/exists.spec.js b/test/commands/exists.spec.js index 3a53b853a1..ee2b523863 100644 --- a/test/commands/exists.spec.js +++ b/test/commands/exists.spec.js @@ -9,25 +9,27 @@ describe('The \'exists\' method', () => { describe(`using ${ip}`, () => { let client - beforeEach((done) => { + beforeEach(() => { client = redis.createClient.apply(null, args) - client.once('ready', () => { - client.flushdb(done) - }) + return client.flushdb() }) - it('returns 1 if the key exists', (done) => { - client.set('foo', 'bar') - client.exists('foo', helper.isNumber(1, done)) + it('returns 1 if the key exists', () => { + return Promise.all([ + client.set('foo', 'bar'), + client.exists('foo').then(helper.isNumber(1)) + ]) }) - it('returns 1 if the key exists with array syntax', (done) => { - client.set('foo', 'bar') - client.exists(['foo'], helper.isNumber(1, done)) + it('returns 1 if the key exists with array syntax', () => { + return Promise.all([ + client.set('foo', 'bar'), + client.exists(['foo']).then(helper.isNumber(1)) + ]) }) - it('returns 0 if the key does not exist', (done) => { - client.exists('bar', helper.isNumber(0, done)) + it('returns 0 if the key does not exist', () => { + return client.exists('bar').then(helper.isNumber(0)) }) afterEach(() => { diff --git a/test/commands/expire.spec.js b/test/commands/expire.spec.js index ee98db7128..94496c5cb8 100644 --- a/test/commands/expire.spec.js +++ b/test/commands/expire.spec.js @@ -9,27 +9,33 @@ describe('The \'expire\' method', () => { describe(`using ${ip}`, () => { let client - beforeEach((done) => { + beforeEach(() => { client = redis.createClient.apply(null, args) - client.once('ready', () => { - client.flushdb(done) - }) + return client.flushdb() }) - it('expires key after timeout', (done) => { - client.set(['expiry key', 'bar'], helper.isString('OK')) - client.expire('expiry key', '1', helper.isNumber(1)) - setTimeout(() => { - client.exists(['expiry key'], helper.isNumber(0, done)) - }, 1050) + it('expires key after timeout', () => { + return Promise.all([ + client.set(['expiry key', 'bar']).then(helper.isString('OK')), + client.expire('expiry key', '1').then(helper.isNumber(1)), + new Promise((resolve, reject) => { + setTimeout(() => { + resolve(client.exists(['expiry key']).then(helper.isNumber(0))) + }, 1050) + }) + ]) }) - it('expires key after timeout with array syntax', (done) => { - client.set(['expiry key', 'bar'], helper.isString('OK')) - client.expire(['expiry key', '1'], helper.isNumber(1)) - setTimeout(() => { - client.exists(['expiry key'], helper.isNumber(0, done)) - }, 1050) + it('expires key after timeout with array syntax', () => { + return Promise.all([ + client.set(['expiry key', 'bar']).then(helper.isString('OK')), + client.expire(['expiry key', '1']).then(helper.isNumber(1)), + new Promise((resolve, reject) => { + setTimeout(() => { + resolve(client.exists(['expiry key']).then(helper.isNumber(0))) + }, 1050) + }) + ]) }) afterEach(() => { diff --git a/test/commands/flushdb.spec.js b/test/commands/flushdb.spec.js index 2f99683756..fed9132d38 100644 --- a/test/commands/flushdb.spec.js +++ b/test/commands/flushdb.spec.js @@ -1,6 +1,5 @@ 'use strict' -const assert = require('assert') const config = require('../lib/config') const helper = require('../helper') const redis = config.redis @@ -19,19 +18,15 @@ describe('The \'flushdb\' method', () => { describe('when not connected', () => { let client - beforeEach((done) => { + beforeEach(() => { client = redis.createClient.apply(null, args) - client.once('ready', () => { - client.quit() - }) - client.on('end', done) + return client.quit() }) - it('reports an error', (done) => { - client.flushdb((err, res) => { - assert(err.message.match(/The connection is already closed/)) - done() - }) + it('reports an error', () => { + return client.flushdb() + .then(helper.fail) + .catch(helper.isError(/The connection is already closed/)) }) }) @@ -40,9 +35,7 @@ describe('The \'flushdb\' method', () => { beforeEach((done) => { client = redis.createClient.apply(null, args) - client.once('ready', () => { - done() - }) + client.once('ready', done) }) afterEach(() => { @@ -50,42 +43,25 @@ describe('The \'flushdb\' method', () => { }) describe('when there is data in Redis', () => { - beforeEach((done) => { - client.mset(key, uuid.v4(), key2, uuid.v4(), helper.isNotError()) - client.dbsize([], (err, res) => { - helper.isType.positiveNumber()(err, res) - assert.strictEqual(res, 2, 'Two keys should have been inserted') - done() - }) + beforeEach(() => { + return Promise.all([ + client.mset(key, uuid.v4(), key2, uuid.v4()).then(helper.isString('OK')), + client.dbsize([]).then(helper.isNumber(2)) + ]) }) - it('deletes all the keys', (done) => { - client.flushdb((err, res) => { - assert.strictEqual(err, null) - assert.strictEqual(res, 'OK') - client.mget(key, key2, (err, res) => { - assert.strictEqual(null, err, 'Unexpected error returned') - assert.strictEqual(true, Array.isArray(res), 'Results object should be an array.') - assert.strictEqual(2, res.length, 'Results array should have length 2.') - assert.strictEqual(null, res[0], 'Redis key should have been flushed.') - assert.strictEqual(null, res[1], 'Redis key should have been flushed.') - done(err) - }) - }) + it('deletes all the keys', () => { + return Promise.all([ + client.flushdb().then(helper.isString('OK')), + client.mget(key, key2).then(helper.isDeepEqual([null, null])) + ]) }) - it('results in a db size of zero', (done) => { - client.flushdb((err, res) => { - assert.strictEqual(err, null) - client.dbsize([], helper.isNumber(0, done)) - }) - }) - - it('results in a db size of zero without a callback', (done) => { - client.flushdb() - setTimeout(() => { - client.dbsize(helper.isNumber(0, done)) - }, 25) + it('results in a db size of zero', () => { + return Promise.all([ + client.flushdb(), + client.dbsize([]).then(helper.isNumber(0)) + ]) }) }) }) diff --git a/test/commands/geoadd.spec.js b/test/commands/geoadd.spec.js index 7acc83ba42..f066234103 100644 --- a/test/commands/geoadd.spec.js +++ b/test/commands/geoadd.spec.js @@ -9,20 +9,14 @@ describe('The \'geoadd\' method', () => { describe(`using ${ip}`, () => { let client - beforeEach((done) => { + beforeEach(() => { client = redis.createClient.apply(null, args) - client.once('ready', () => { - client.flushdb(done) - }) + return client.flushdb() }) - it('returns 1 if the key exists', function (done) { + it('returns 1 if the key exists', function () { helper.serverVersionAtLeast.call(this, client, [3, 2, 0]) - client.geoadd('mycity:21:0:location', '13.361389', '38.115556', 'COR', (err, res) => { - console.log(err, res) - // geoadd is still in the unstable branch. As soon as it reaches the stable one, activate this test - done() - }) + return client.geoadd('mycity:21:0:location', '13.361389', '38.115556', 'COR') }) afterEach(() => { diff --git a/test/commands/get.spec.js b/test/commands/get.spec.js index 10ed0bb208..5c49c224fa 100644 --- a/test/commands/get.spec.js +++ b/test/commands/get.spec.js @@ -1,6 +1,5 @@ 'use strict' -const assert = require('assert') const config = require('../lib/config') const helper = require('../helper') const redis = config.redis @@ -19,25 +18,15 @@ describe('The \'get\' method', () => { describe('when not connected', () => { let client - beforeEach((done) => { + beforeEach(() => { client = redis.createClient.apply(null, args) - client.once('ready', () => { - client.quit() - }) - client.on('end', done) + return client.quit() }) - it('reports an error', (done) => { - client.get(key, (err, res) => { - assert(err.message.match(/The connection is already closed/)) - done() - }) - }) - - it('reports an error promisified', () => { - return client.getAsync(key).then(assert, (err) => { - assert(err.message.match(/The connection is already closed/)) - }) + it('reports an error', () => { + return client.get(key) + .then(helper.fail) + .catch(helper.isError(/The connection is already closed/)) }) }) @@ -46,9 +35,7 @@ describe('The \'get\' method', () => { beforeEach((done) => { client = redis.createClient.apply(null, args) - client.once('ready', () => { - done() - }) + client.once('ready', done) }) afterEach(() => { @@ -56,35 +43,18 @@ describe('The \'get\' method', () => { }) describe('when the key exists in Redis', () => { - beforeEach((done) => { - client.set(key, value, (err, res) => { - helper.isNotError()(err, res) - done() - }) + beforeEach(() => { + return client.set(key, value).then(helper.isString('OK')) }) - it('gets the value correctly', (done) => { - client.get(key, (err, res) => { - helper.isString(value)(err, res) - done(err) - }) - }) - - it('should not throw on a get without callback (even if it\'s not useful)', (done) => { - client.get(key) - client.on('error', (err) => { - throw err - }) - setTimeout(done, 25) + it('gets the value correctly', () => { + return client.get(key).then(helper.isString(value)) }) }) describe('when the key does not exist in Redis', () => { - it('gets a null value', (done) => { - client.get(key, (err, res) => { - helper.isNull()(err, res) - done(err) - }) + it('gets a null value', () => { + return client.get(key).then(helper.isNull()) }) }) }) diff --git a/test/commands/getset.spec.js b/test/commands/getset.spec.js index f0aa90f8cd..55ccf04271 100644 --- a/test/commands/getset.spec.js +++ b/test/commands/getset.spec.js @@ -1,6 +1,5 @@ 'use strict' -const assert = require('assert') const config = require('../lib/config') const helper = require('../helper') const redis = config.redis @@ -20,19 +19,15 @@ describe('The \'getset\' method', () => { describe('when not connected', () => { let client - beforeEach((done) => { + beforeEach(() => { client = redis.createClient.apply(null, args) - client.once('ready', () => { - client.quit() - }) - client.on('end', done) + return client.quit() }) - it('reports an error', (done) => { - client.get(key, (err, res) => { - assert(err.message.match(/The connection is already closed/)) - done() - }) + it('reports an error', () => { + return client.get(key) + .then(helper.fail) + .catch(helper.isError(/The connection is already closed/)) }) }) @@ -41,9 +36,7 @@ describe('The \'getset\' method', () => { beforeEach((done) => { client = redis.createClient.apply(null, args) - client.once('ready', () => { - done() - }) + client.once('ready', done) }) afterEach(() => { @@ -51,50 +44,35 @@ describe('The \'getset\' method', () => { }) describe('when the key exists in Redis', () => { - beforeEach((done) => { - client.set(key, value, (err, res) => { - helper.isNotError()(err, res) - done() - }) + beforeEach(() => { + return client.set(key, value).then(helper.isString('OK')) }) - it('gets the value correctly', (done) => { - client.getset(key, value2, (err, res) => { - helper.isString(value)(err, res) - client.get(key, (err, res) => { - helper.isString(value2)(err, res) - done(err) - }) - }) + it('gets the value correctly', () => { + return Promise.all([ + client.getset(key, value2).then(helper.isString(value)), + client.get(key).then(helper.isString(value2)) + ]) }) - it('gets the value correctly with array syntax', (done) => { - client.getset([key, value2], (err, res) => { - helper.isString(value)(err, res) - client.get(key, (err, res) => { - helper.isString(value2)(err, res) - done(err) - }) - }) + it('gets the value correctly with array syntax', () => { + return Promise.all([ + client.getset([key, value2]).then(helper.isString(value)), + client.get(key).then(helper.isString(value2)) + ]) }) - it('gets the value correctly with array syntax style 2', (done) => { - client.getset(key, [value2], (err, res) => { - helper.isString(value)(err, res) - client.get(key, (err, res) => { - helper.isString(value2)(err, res) - done(err) - }) - }) + it('gets the value correctly with array syntax style 2', () => { + return Promise.all([ + client.getset(key, [value2]).then(helper.isString(value)), + client.get(key).then(helper.isString(value2)) + ]) }) }) describe('when the key does not exist in Redis', () => { - it('gets a null value', (done) => { - client.getset(key, value, (err, res) => { - helper.isNull()(err, res) - done(err) - }) + it('gets a null value', () => { + return client.getset(key, value).then(helper.isNull()) }) }) }) diff --git a/test/commands/hgetall.spec.js b/test/commands/hgetall.spec.js index aa76e41872..2ae1af4212 100644 --- a/test/commands/hgetall.spec.js +++ b/test/commands/hgetall.spec.js @@ -8,69 +8,62 @@ const redis = config.redis describe('The \'hgetall\' method', () => { helper.allTests((ip, args) => { + let client + describe(`using ${ip}`, () => { - let client - describe('regular client', () => { - beforeEach((done) => { + beforeEach(() => { client = redis.createClient.apply(null, args) - client.once('ready', () => { - client.flushdb(done) - }) + return client.flushdb() }) - it('handles simple keys and values', (done) => { - client.hmset(['hosts', 'hasOwnProperty', '1', 'another', '23', 'home', '1234'], helper.isString('OK')) - client.hgetall(['hosts'], (err, obj) => { - assert.strictEqual(3, Object.keys(obj).length) - assert.strictEqual('1', obj.hasOwnProperty.toString()) - assert.strictEqual('23', obj.another.toString()) - assert.strictEqual('1234', obj.home.toString()) - done(err) - }) + it('handles simple keys and values', () => { + return Promise.all([ + client.hmset(['hosts', 'hasOwnProperty', '1', 'another', '23', 'home', '1234']).then(helper.isString('OK')), + client.hgetall(['hosts']).then(helper.isDeepEqual({ + hasOwnProperty: '1', + another: '23', + home: '1234' + })) + ]) }) - it('handles fetching keys set using an object', (done) => { - client.batch().hmset('msgTest', { message: 'hello' }, undefined).exec() - client.hgetall('msgTest', (err, obj) => { - assert.strictEqual(1, Object.keys(obj).length) - assert.strictEqual(obj.message, 'hello') - done(err) - }) + it('handles fetching keys set using an object', () => { + return Promise.all([ + client.batch().hmset('msgTest', { message: 'hello' }).exec(), + client.hgetall('msgTest').then(helper.isDeepEqual({ message: 'hello' })) + ]) }) - it('handles fetching a messing key', (done) => { - client.hgetall('missing', (err, obj) => { - assert.strictEqual(null, obj) - done(err) - }) + it('handles fetching a messing key', () => { + return client.hgetall('missing').then(helper.isNull()) }) }) describe('binary client', () => { - let client const args = config.configureClient(ip, { returnBuffers: true }) - beforeEach((done) => { + beforeEach(() => { client = redis.createClient.apply(null, args) - client.once('ready', () => { - client.flushdb(done) - }) + return client.flushdb() }) - it('returns binary results', (done) => { - client.hmset(['bhosts', 'mjr', '1', 'another', '23', 'home', '1234', Buffer.from([0xAA, 0xBB, 0x00, 0xF0]), Buffer.from([0xCC, 0xDD, 0x00, 0xF0])], helper.isString('OK')) - client.hgetall('bhosts', (err, obj) => { - assert.strictEqual(4, Object.keys(obj).length) - assert.strictEqual('1', obj.mjr.toString()) - assert.strictEqual('23', obj.another.toString()) - assert.strictEqual('1234', obj.home.toString()) - assert.strictEqual((Buffer.from([0xAA, 0xBB, 0x00, 0xF0])).toString('binary'), Object.keys(obj)[3]) - assert.strictEqual((Buffer.from([0xCC, 0xDD, 0x00, 0xF0])).toString('binary'), obj[(Buffer.from([0xAA, 0xBB, 0x00, 0xF0])).toString('binary')].toString('binary')) - return done(err) - }) + it('returns binary results', () => { + const weirdKey = Buffer.from([0xAA, 0xBB, 0x00, 0xF0]) + const weirdValue = Buffer.from([0xCC, 0xDD, 0x00, 0xF0]) + return Promise.all([ + client.hmset(['bhosts', 'mjr', '1', 'another', '23', 'home', '1234', weirdKey, weirdValue]).then(helper.isString('OK')), + client.hgetall('bhosts').then((obj) => { + assert.strictEqual(4, Object.keys(obj).length) + assert.strictEqual('1', obj.mjr.toString()) + assert.strictEqual('23', obj.another.toString()) + assert.strictEqual('1234', obj.home.toString()) + assert.strictEqual(weirdKey.toString('binary'), Object.keys(obj)[3]) + assert.strictEqual(weirdValue.toString('binary'), obj[weirdKey.toString('binary')].toString('binary')) + }) + ]) }) }) diff --git a/test/commands/hincrby.spec.js b/test/commands/hincrby.spec.js index a3df0130a3..21fbc4264c 100644 --- a/test/commands/hincrby.spec.js +++ b/test/commands/hincrby.spec.js @@ -10,24 +10,19 @@ describe('The \'hincrby\' method', () => { let client const hash = 'test hash' - beforeEach((done) => { + beforeEach(() => { client = redis.createClient.apply(null, args) - client.once('ready', () => { - client.flushdb(done) - }) + return client.flushdb() }) - it('increments a key that has already been set', (done) => { + it('increments a key that has already been set', () => { const field = 'field 1' - client.hset(hash, field, 33) - client.hincrby(hash, field, 10, helper.isNumber(43, done)) + return client.hincrby(hash, field, 10).then(helper.isNumber(43)) }) - it('increments a key that has not been set', (done) => { - const field = 'field 2' - - client.hincrby(hash, field, 10, helper.isNumber(10, done)) + it('increments a key that has not been set', () => { + return client.hincrby(hash, 'field 2', 10).then(helper.isNumber(10)) }) afterEach(() => { diff --git a/test/commands/hlen.spec.js b/test/commands/hlen.spec.js index 0012f53359..c5c7432427 100644 --- a/test/commands/hlen.spec.js +++ b/test/commands/hlen.spec.js @@ -10,23 +10,21 @@ describe('The \'hlen\' method', () => { describe(`using ${ip}`, () => { let client - beforeEach((done) => { + beforeEach(() => { client = redis.createClient.apply(null, args) - client.once('ready', () => { - client.flushdb(done) - }) + return client.flushdb() }) - it('reports the count of keys', (done) => { + it('reports the count of keys', () => { const hash = 'test hash' const field1 = Buffer.from('0123456789') const value1 = Buffer.from('abcdefghij') const field2 = Buffer.from('') const value2 = Buffer.from('') - client.hset(hash, field1, value1, helper.isNumber(1)) - client.hset(hash, field2, value2, helper.isNumber(1)) - client.hlen(hash, helper.isNumber(2, done)) + client.hset(hash, field1, value1).then(helper.isNumber(1)) + client.hset(hash, field2, value2).then(helper.isNumber(1)) + return client.hlen(hash).then(helper.isNumber(2)) }) afterEach(() => { diff --git a/test/commands/hmget.spec.js b/test/commands/hmget.spec.js index f383d8c14f..9b2bd9e50f 100644 --- a/test/commands/hmget.spec.js +++ b/test/commands/hmget.spec.js @@ -1,6 +1,5 @@ 'use strict' -const assert = require('assert') const config = require('../lib/config') const helper = require('../helper') const redis = config.redis @@ -11,54 +10,36 @@ describe('The \'hmget\' method', () => { let client const hash = 'test hash' - beforeEach((done) => { + beforeEach(() => { client = redis.createClient.apply(null, args) - client.once('error', done) - client.once('ready', () => { - client.flushdb() - client.hmset(hash, {'0123456789': 'abcdefghij', 'some manner of key': 'a type of value'}, helper.isString('OK', done)) - }) + client.flushdb() + return client.hmset(hash, {'0123456789': 'abcdefghij', 'some manner of key': 'a type of value'}) + .then(helper.isString('OK')) }) - it('allows keys to be specified using multiple arguments', (done) => { - client.hmget(hash, '0123456789', 'some manner of key', (err, reply) => { - assert.strictEqual('abcdefghij', reply[0].toString()) - assert.strictEqual('a type of value', reply[1].toString()) - return done(err) - }) + it('allows keys to be specified using multiple arguments', () => { + return client.hmget(hash, '0123456789', 'some manner of key') + .then(helper.isDeepEqual(['abcdefghij', 'a type of value'])) }) - it('allows keys to be specified by passing an array without manipulating the array', (done) => { + it('allows keys to be specified by passing an array without manipulating the array', () => { const data = ['0123456789', 'some manner of key'] - client.hmget(hash, data, (err, reply) => { - assert.strictEqual(data.length, 2) - assert.strictEqual('abcdefghij', reply[0].toString()) - assert.strictEqual('a type of value', reply[1].toString()) - return done(err) - }) + return client.hmget(hash, data) + .then(helper.isDeepEqual(['abcdefghij', 'a type of value'])) }) - it('allows keys to be specified by passing an array as first argument', (done) => { - client.hmget([hash, '0123456789', 'some manner of key'], (err, reply) => { - assert.strictEqual('abcdefghij', reply[0].toString()) - assert.strictEqual('a type of value', reply[1].toString()) - return done(err) - }) + it('allows keys to be specified by passing an array as first argument', () => { + return client.hmget([hash, '0123456789', 'some manner of key']) + .then(helper.isDeepEqual(['abcdefghij', 'a type of value'])) }) - it('allows a single key to be specified in an array', (done) => { - client.hmget(hash, ['0123456789'], (err, reply) => { - assert.strictEqual('abcdefghij', reply[0].toString()) - return done(err) - }) + it('allows a single key to be specified in an array', () => { + return client.hmget(hash, ['0123456789']).then(helper.isDeepEqual(['abcdefghij'])) }) - it('allows keys to be specified that have not yet been set', (done) => { - client.hmget(hash, 'missing thing', 'another missing thing', (err, reply) => { - assert.strictEqual(null, reply[0]) - assert.strictEqual(null, reply[1]) - return done(err) - }) + it('allows keys to be specified that have not yet been set', () => { + return client.hmget(hash, 'missing thing', 'another missing thing') + .then(helper.isDeepEqual([null, null])) }) afterEach(() => { diff --git a/test/commands/hmset.spec.js b/test/commands/hmset.spec.js index 81107830eb..1b199f987b 100644 --- a/test/commands/hmset.spec.js +++ b/test/commands/hmset.spec.js @@ -1,6 +1,5 @@ 'use strict' -const assert = require('assert') const config = require('../lib/config') const helper = require('../helper') const redis = config.redis @@ -11,100 +10,57 @@ describe('The \'hmset\' method', () => { let client const hash = 'test hash' - beforeEach((done) => { + beforeEach(() => { client = redis.createClient.apply(null, args) - client.once('ready', () => { - client.flushdb(done) - }) + return client.flushdb() }) - it('handles redis-style syntax', (done) => { - client.hmset(hash, '0123456789', 'abcdefghij', 'some manner of key', 'a type of value', 'otherTypes', 555, helper.isString('OK')) - client.hgetall(hash, (err, obj) => { - assert.strictEqual(obj['0123456789'], 'abcdefghij') - assert.strictEqual(obj['some manner of key'], 'a type of value') - return done(err) - }) + it('handles redis-style syntax', () => { + client.hmset(hash, '0123456789', 'abcdefghij', 'some manner of key', 'a type of value', 'otherTypes', 555).then(helper.isString('OK')) + return client.hgetall(hash).then(helper.isDeepEqual({ + '0123456789': 'abcdefghij', + 'some manner of key': 'a type of value', + 'otherTypes': '555' + })) }) - it('handles object-style syntax', (done) => { - client.hmset(hash, {'0123456789': 'abcdefghij', 'some manner of key': 'a type of value', 'otherTypes': 555}, helper.isString('OK')) - client.hgetall(hash, (err, obj) => { - assert.strictEqual(obj['0123456789'], 'abcdefghij') - assert.strictEqual(obj['some manner of key'], 'a type of value') - return done(err) - }) + it('handles object-style syntax', () => { + client.hmset(hash, {'0123456789': 'abcdefghij', 'some manner of key': 'a type of value', 'otherTypes': 555}).then(helper.isString('OK')) + return client.hgetall(hash).then(helper.isDeepEqual({ + '0123456789': 'abcdefghij', + 'some manner of key': 'a type of value', + 'otherTypes': '555' + })) }) - it('handles object-style syntax and the key being a number', (done) => { - client.hmset(231232, {'0123456789': 'abcdefghij', 'some manner of key': 'a type of value', 'otherTypes': 555}, undefined) - client.hgetall(231232, (err, obj) => { - assert.strictEqual(obj['0123456789'], 'abcdefghij') - assert.strictEqual(obj['some manner of key'], 'a type of value') - return done(err) - }) + it('handles object-style syntax and the key being a number', () => { + client.hmset(231232, {'0123456789': 'abcdefghij', 'some manner of key': 'a type of value', 'otherTypes': 555}) + return client.hgetall(231232).then(helper.isDeepEqual({ + '0123456789': 'abcdefghij', + 'some manner of key': 'a type of value', + 'otherTypes': '555' + })) }) - it('allows a numeric key', (done) => { - client.hmset(hash, 99, 'banana', helper.isString('OK')) - client.hgetall(hash, (err, obj) => { - assert.strictEqual(obj['99'], 'banana') - return done(err) - }) + it('allows a numeric key', () => { + client.hmset(hash, 99, 'banana').then(helper.isString('OK')) + return client.hgetall(hash).then(helper.isDeepEqual({ 99: 'banana' })) }) - it('allows a numeric key without callback', (done) => { - client.hmset(hash, 99, 'banana', 'test', 25) - client.hgetall(hash, (err, obj) => { - assert.strictEqual(obj['99'], 'banana') - assert.strictEqual(obj.test, '25') - return done(err) - }) + it('allows an array', () => { + client.hmset([hash, 99, 'banana', 'test', 25]).then(helper.isString('OK')) + return client.hgetall(hash).then(helper.isDeepEqual({ + 99: 'banana', + test: '25' + })) }) - it('allows an array without callback', (done) => { - client.hmset([hash, 99, 'banana', 'test', 25]) - client.hgetall(hash, (err, obj) => { - assert.strictEqual(obj['99'], 'banana') - assert.strictEqual(obj.test, '25') - return done(err) - }) - }) - - it('allows an array and a callback', (done) => { - client.hmset([hash, 99, 'banana', 'test', 25], helper.isString('OK')) - client.hgetall(hash, (err, obj) => { - assert.strictEqual(obj['99'], 'banana') - assert.strictEqual(obj.test, '25') - return done(err) - }) - }) - - it('allows a key plus array without callback', (done) => { - client.hmset(hash, [99, 'banana', 'test', 25]) - client.hgetall(hash, (err, obj) => { - assert.strictEqual(obj['99'], 'banana') - assert.strictEqual(obj.test, '25') - return done(err) - }) - }) - - it('allows a key plus array and a callback', (done) => { - client.hmset(hash, [99, 'banana', 'test', 25], helper.isString('OK')) - client.hgetall(hash, (err, obj) => { - assert.strictEqual(obj['99'], 'banana') - assert.strictEqual(obj.test, '25') - return done(err) - }) - }) - - it('handles object-style syntax without callback', (done) => { - client.hmset(hash, {'0123456789': 'abcdefghij', 'some manner of key': 'a type of value'}) - client.hgetall(hash, (err, obj) => { - assert.strictEqual(obj['0123456789'], 'abcdefghij') - assert.strictEqual(obj['some manner of key'], 'a type of value') - return done(err) - }) + it('allows a key plus array', () => { + client.hmset(hash, [99, 'banana', 'test', 25]).then(helper.isString('OK')) + return client.hgetall(hash).then(helper.isDeepEqual({ + 99: 'banana', + test: '25' + })) }) afterEach(() => { diff --git a/test/commands/hset.spec.js b/test/commands/hset.spec.js index 48c0c84709..2927981e13 100644 --- a/test/commands/hset.spec.js +++ b/test/commands/hset.spec.js @@ -12,66 +12,51 @@ describe('The \'hset\' method', () => { let client const hash = 'test hash' - beforeEach((done) => { + beforeEach(() => { client = redis.createClient.apply(null, args) - client.once('ready', () => { - client.flushdb(done) - }) + return client.flushdb() }) - it('allows a value to be set in a hash', (done) => { + it('allows a value to be set in a hash', () => { const field = Buffer.from('0123456789') const value = Buffer.from('abcdefghij') - client.hset(hash, field, value, helper.isNumber(1)) - client.hget(hash, field, helper.isString(value.toString(), done)) + client.hset(hash, field, value).then(helper.isNumber(1)) + return client.hget(hash, field).then(helper.isString(value.toString())) }) - it('handles an empty value', (done) => { + it('handles an empty value', () => { const field = Buffer.from('0123456789') const value = Buffer.from('') - client.hset(hash, field, value, helper.isNumber(1)) - client.hget([hash, field], helper.isString('', done)) + client.hset(hash, field, value).then(helper.isNumber(1)) + return client.hget([hash, field]).then(helper.isString('')) }) - it('handles empty key and value', (done) => { + it('handles empty key and value', () => { const field = Buffer.from('') const value = Buffer.from('') - client.hset([hash, field, value], (err, res) => { - assert.strictEqual(err, null) - assert.strictEqual(res, 1) - client.hset(hash, field, value, helper.isNumber(0, done)) - }) + client.hset([hash, field, value]).then(helper.isNumber(1)) + return client.hset(hash, field, value).then(helper.isNumber(0)) }) - it('warns if someone passed a array either as field or as value', (done) => { + it('warns if someone passed a array either as field or as value', () => { const hash = 'test hash' const field = 'array' // This would be converted to "array contents" but if you use more than one entry, // it'll result in e.g. "array contents,second content" and this is not supported and considered harmful const value = ['array contents'] - client.hmset(hash, field, value, helper.isError(done)) + return client.hmset(hash, field, value).then(assert, helper.isError()) }) - it('does not error when a buffer and date are set as values on the same hash', (done) => { - const hash = 'test hash' - const field1 = 'buffer' - const value1 = Buffer.from('abcdefghij') - const field2 = 'date' - const value2 = new Date() - - client.hmset(hash, field1, value1, field2, value2, helper.isString('OK', done)) + it('does not error when a buffer and date are set as values on the same hash', () => { + return client.hmset('test hash', 'buffer', Buffer.from('abcdefghij'), 'data', new Date()) + .then(helper.isString('OK')) }) - it('does not error when a buffer and date are set as fields on the same hash', (done) => { - const hash = 'test hash' - const value1 = 'buffer' - const field1 = Buffer.from('abcdefghij') - const value2 = 'date' - const field2 = new Date() - - client.hmset(hash, field1, value1, field2, value2, helper.isString('OK', done)) + it('does not error when a buffer and date are set as fields on the same hash', () => { + return client.hmset('test hash', Buffer.from('abcdefghij'), 'buffer', new Date(), 'date') + .then(helper.isString('OK')) }) afterEach(() => { diff --git a/test/commands/incr.spec.js b/test/commands/incr.spec.js index 63b90dcff5..97e936124c 100644 --- a/test/commands/incr.spec.js +++ b/test/commands/incr.spec.js @@ -1,6 +1,5 @@ 'use strict' -const assert = require('assert') const config = require('../lib/config') const helper = require('../helper') const redis = config.redis @@ -17,53 +16,47 @@ describe('The \'incr\' method', () => { }) /* - Number.MAX_SAFE_INTEGER === Math.pow(2, 53) - 1 === 9007199254740991 + Number.MAX_SAFE_INTEGER === Math.pow(2, 53) - 1 === 9007199254740991 - 9007199254740992 -> 9007199254740992 - 9007199254740993 -> 9007199254740992 - 9007199254740994 -> 9007199254740994 - 9007199254740995 -> 9007199254740996 - 9007199254740996 -> 9007199254740996 - 9007199254740997 -> 9007199254740996 - ... - */ - it('count above the safe integers as numbers', (done) => { + 9007199254740992 -> 9007199254740992 + 9007199254740993 -> 9007199254740992 + 9007199254740994 -> 9007199254740994 + 9007199254740995 -> 9007199254740996 + 9007199254740996 -> 9007199254740996 + 9007199254740997 -> 9007199254740996 + ... + */ + it('count above the safe integers as numbers', () => { client = redis.createClient.apply(null, args) // Set a value to the maximum safe allowed javascript number (2^53) - 1 - client.set(key, Number.MAX_SAFE_INTEGER, helper.isNotError()) - client.incr(key, helper.isNumber(Number.MAX_SAFE_INTEGER + 1)) - client.incr(key, helper.isNumber(Number.MAX_SAFE_INTEGER + 2)) - client.incr(key, helper.isNumber(Number.MAX_SAFE_INTEGER + 3)) - client.incr(key, helper.isNumber(Number.MAX_SAFE_INTEGER + 4)) - client.incr(key, helper.isNumber(Number.MAX_SAFE_INTEGER + 5)) - client.incr(key, (err, res) => { - helper.isNumber(Number.MAX_SAFE_INTEGER + 6)(err, res) - assert.strictEqual(typeof res, 'number') - }) - client.incr(key, helper.isNumber(Number.MAX_SAFE_INTEGER + 7)) - client.incr(key, helper.isNumber(Number.MAX_SAFE_INTEGER + 8)) - client.incr(key, helper.isNumber(Number.MAX_SAFE_INTEGER + 9)) - client.incr(key, helper.isNumber(Number.MAX_SAFE_INTEGER + 10, done)) + client.set(key, Number.MAX_SAFE_INTEGER).then(helper.isString('OK')) + client.incr(key).then(helper.isNumber(Number.MAX_SAFE_INTEGER + 1)) + client.incr(key).then(helper.isNumber(Number.MAX_SAFE_INTEGER + 2)) + client.incr(key).then(helper.isNumber(Number.MAX_SAFE_INTEGER + 3)) + client.incr(key).then(helper.isNumber(Number.MAX_SAFE_INTEGER + 4)) + client.incr(key).then(helper.isNumber(Number.MAX_SAFE_INTEGER + 5)) + client.incr(key).then(helper.isNumber(Number.MAX_SAFE_INTEGER + 6)) + client.incr(key).then(helper.isNumber(Number.MAX_SAFE_INTEGER + 7)) + client.incr(key).then(helper.isNumber(Number.MAX_SAFE_INTEGER + 8)) + client.incr(key).then(helper.isNumber(Number.MAX_SAFE_INTEGER + 9)) + return client.incr(key).then(helper.isNumber(Number.MAX_SAFE_INTEGER + 10)) }) - it('count above the safe integers as strings', (done) => { + it('count above the safe integers as strings', () => { args[2].stringNumbers = true client = redis.createClient.apply(null, args) // Set a value to the maximum safe allowed javascript number (2^53) - client.set(key, Number.MAX_SAFE_INTEGER, helper.isNotError()) - client.incr(key, helper.isString('9007199254740992')) - client.incr(key, helper.isString('9007199254740993')) - client.incr(key, helper.isString('9007199254740994')) - client.incr(key, helper.isString('9007199254740995')) - client.incr(key, helper.isString('9007199254740996')) - client.incr(key, (err, res) => { - helper.isString('9007199254740997')(err, res) - assert.strictEqual(typeof res, 'string') - }) - client.incr(key, helper.isString('9007199254740998')) - client.incr(key, helper.isString('9007199254740999')) - client.incr(key, helper.isString('9007199254741000')) - client.incr(key, helper.isString('9007199254741001', done)) + client.set(key, Number.MAX_SAFE_INTEGER).then(helper.isString('OK')) + client.incr(key).then(helper.isString('9007199254740992')) + client.incr(key).then(helper.isString('9007199254740993')) + client.incr(key).then(helper.isString('9007199254740994')) + client.incr(key).then(helper.isString('9007199254740995')) + client.incr(key).then(helper.isString('9007199254740996')) + client.incr(key).then(helper.isString('9007199254740997')) + client.incr(key).then(helper.isString('9007199254740998')) + client.incr(key).then(helper.isString('9007199254740999')) + client.incr(key).then(helper.isString('9007199254741000')) + return client.incr(key).then(helper.isString('9007199254741001')) }) }) }) diff --git a/test/commands/info.spec.js b/test/commands/info.spec.js index 78bafb7c3e..ac0740a71a 100644 --- a/test/commands/info.spec.js +++ b/test/commands/info.spec.js @@ -10,68 +10,50 @@ describe('The \'info\' method', () => { describe(`using ${ip}`, () => { let client - beforeEach((done) => { + beforeEach(() => { client = redis.createClient.apply(null, args) - client.once('ready', () => { - client.flushall(done) - }) + return client.flushall() }) afterEach(() => { client.end(true) }) - it('update serverInfo after a info command', (done) => { + it('update serverInfo after a info command', () => { client.set('foo', 'bar') - client.info() - client.select(2, () => { + return client.info().then(() => { assert.strictEqual(client.serverInfo.db2, undefined) + client.select(2) + client.set('foo', 'bar') + return client.info().then(() => { + assert.strictEqual(typeof client.serverInfo.db2, 'object') + }) }) - client.set('foo', 'bar') - client.info() - setTimeout(() => { - assert.strictEqual(typeof client.serverInfo.db2, 'object') - done() - }, 30) }) - it('works with optional section provided with and without callback', (done) => { + it('works with optional section provided', () => { client.set('foo', 'bar') client.info('keyspace') - client.select(2, () => { + return client.select(2).then(() => { assert.strictEqual(Object.keys(client.serverInfo).length, 2, 'Key length should be three') assert.strictEqual(typeof client.serverInfo.db0, 'object', 'db0 keyspace should be an object') - }) - client.info(['keyspace']) - client.set('foo', 'bar') - client.info('all', (err, res) => { - assert.strictEqual(err, null) - assert(Object.keys(client.serverInfo).length > 3, 'Key length should be way above three') - assert.strictEqual(typeof client.serverInfo.redis_version, 'string') - assert.strictEqual(typeof client.serverInfo.db2, 'object') - done() + client.info(['keyspace']) + client.set('foo', 'bar') + return client.info('all').then((res) => { + assert(Object.keys(client.serverInfo).length > 3, 'Key length should be way above three') + assert.strictEqual(typeof client.serverInfo.redis_version, 'string') + assert.strictEqual(typeof client.serverInfo.db2, 'object') + }) }) }) - it('check redis v.2.4 support', (done) => { - const end = helper.callFuncAfter(done, 2) - client.internalSendCommand = function (commandObj) { - assert.strictEqual(commandObj.args.length, 0) - assert.strictEqual(commandObj.command, 'info') - end() - } - client.info() - client.info(() => {}) - }) - - it('emit error after a failure', (done) => { - client.info() - client.once('error', (err) => { + it('return error after a failure', () => { + const promise = client.info().then(helper.fail).catch((err) => { assert.strictEqual(err.code, 'UNCERTAIN_STATE') assert.strictEqual(err.command, 'INFO') - done() }) client.stream.destroy() + return promise }) }) }) diff --git a/test/commands/keys.spec.js b/test/commands/keys.spec.js index b6a3077ee4..f1095ab464 100644 --- a/test/commands/keys.spec.js +++ b/test/commands/keys.spec.js @@ -11,24 +11,21 @@ describe('The \'keys\' method', () => { describe(`using ${ip}`, () => { let client - beforeEach((done) => { + beforeEach(() => { client = redis.createClient.apply(null, args) - client.once('ready', () => { - client.flushall(done) - }) + return client.flushall() }) - it('returns matching keys', (done) => { - client.mset(['test keys 1', 'test val 1', 'test keys 2', 'test val 2'], helper.isString('OK')) - client.keys('test keys*', (err, results) => { + it('returns matching keys', () => { + client.mset(['test keys 1', 'test val 1', 'test keys 2', 'test val 2']).then(helper.isString('OK')) + return client.keys('test keys*').then((results) => { assert.strictEqual(2, results.length) assert.ok(~results.indexOf('test keys 1')) assert.ok(~results.indexOf('test keys 2')) - return done(err) }) }) - it('handles a large packet size', (done) => { + it('handles a large packet size', () => { const keysValues = [] for (let i = 0; i < 200; i++) { @@ -39,24 +36,15 @@ describe('The \'keys\' method', () => { keysValues.push(keyValue) } - client.mset(keysValues.reduce((a, b) => { - return a.concat(b) - }), helper.isString('OK')) + client.mset(keysValues.reduce((a, b) => a.concat(b))).then(helper.isString('OK')) - client.keys('multibulk:*', (err, results) => { - assert.deepEqual(keysValues.map((val) => { - return val[0] - }).sort(), results.sort()) - return done(err) + return client.keys('multibulk:*').then((results) => { + assert.deepStrictEqual(keysValues.map((val) => val[0]).sort(), results.sort()) }) }) - it('handles an empty response', (done) => { - client.keys(['users:*'], (err, results) => { - assert.strictEqual(results.length, 0) - assert.ok(Array.isArray(results)) - return done(err) - }) + it('handles an empty response', () => { + return client.keys(['users:*']).then(helper.isDeepEqual([])) }) afterEach(() => { diff --git a/test/commands/mget.spec.js b/test/commands/mget.spec.js index 2cdb62c984..51298e4d8d 100644 --- a/test/commands/mget.spec.js +++ b/test/commands/mget.spec.js @@ -1,6 +1,5 @@ 'use strict' -const assert = require('assert') const config = require('../lib/config') const helper = require('../helper') const redis = config.redis @@ -10,54 +9,29 @@ describe('The \'mget\' method', () => { describe(`using ${ip}`, () => { let client - beforeEach((done) => { + beforeEach(() => { client = redis.createClient.apply(null, args) - client.once('error', done) - client.once('ready', () => { - client.flushdb() - client.mset(['mget keys 1', 'mget val 1', 'mget keys 2', 'mget val 2', 'mget keys 3', 'mget val 3'], done) - }) + client.flushdb() + return client.mset(['mget keys 1', 'mget val 1', 'mget keys 2', 'mget val 2', 'mget keys 3', 'mget val 3']) }) - it('handles fetching multiple keys in argument form', (done) => { - client.mset(['mget keys 1', 'mget val 1', 'mget keys 2', 'mget val 2', 'mget keys 3', 'mget val 3'], helper.isString('OK')) - client.mget('mget keys 1', 'mget keys 2', 'mget keys 3', (err, results) => { - assert.strictEqual(3, results.length) - assert.strictEqual('mget val 1', results[0].toString()) - assert.strictEqual('mget val 2', results[1].toString()) - assert.strictEqual('mget val 3', results[2].toString()) - return done(err) - }) + it('handles fetching multiple keys in argument form', () => { + client.mset(['mget keys 1', 'mget val 1', 'mget keys 2', 'mget val 2', 'mget keys 3', 'mget val 3']).then(helper.isString('OK')) + return client.mget('mget keys 1', 'mget keys 2', 'mget keys 3').then(helper.isDeepEqual([ + 'mget val 1', 'mget val 2', 'mget val 3' + ])) }) - it('handles fetching multiple keys via an array', (done) => { - client.mget(['mget keys 1', 'mget keys 2', 'mget keys 3'], (err, results) => { - assert.strictEqual('mget val 1', results[0].toString()) - assert.strictEqual('mget val 2', results[1].toString()) - assert.strictEqual('mget val 3', results[2].toString()) - return done(err) - }) + it('handles fetching multiple keys via an array', () => { + return client.mget(['mget keys 1', 'mget keys 2', 'mget keys 3']).then(helper.isDeepEqual([ + 'mget val 1', 'mget val 2', 'mget val 3' + ])) }) - it('handles fetching multiple keys, when some keys do not exist', (done) => { - client.mget('mget keys 1', ['some random shit', 'mget keys 2', 'mget keys 3'], (err, results) => { - assert.strictEqual(4, results.length) - assert.strictEqual('mget val 1', results[0].toString()) - assert.strictEqual(null, results[1]) - assert.strictEqual('mget val 2', results[2].toString()) - assert.strictEqual('mget val 3', results[3].toString()) - return done(err) - }) - }) - - it('handles fetching multiple keys, when some keys do not exist promisified', () => { - return client.mgetAsync('mget keys 1', ['some random shit', 'mget keys 2', 'mget keys 3']).then((results) => { - assert.strictEqual(4, results.length) - assert.strictEqual('mget val 1', results[0].toString()) - assert.strictEqual(null, results[1]) - assert.strictEqual('mget val 2', results[2].toString()) - assert.strictEqual('mget val 3', results[3].toString()) - }) + it('handles fetching multiple keys, when some keys do not exist', () => { + return client.mget('mget keys 1', ['some random shit', 'mget keys 2', 'mget keys 3']).then(helper.isDeepEqual([ + 'mget val 1', null, 'mget val 2', 'mget val 3' + ])) }) afterEach(() => { diff --git a/test/commands/monitor.spec.js b/test/commands/monitor.spec.js index bb6f5fb49e..f550623915 100644 --- a/test/commands/monitor.spec.js +++ b/test/commands/monitor.spec.js @@ -15,11 +15,9 @@ describe('The \'monitor\' method', () => { client.end(true) }) - beforeEach((done) => { + beforeEach(() => { client = redis.createClient.apply(null, args) - client.once('connect', () => { - client.flushdb(done) - }) + return client.flushdb() }) it('monitors commands on all redis clients and works in the correct order', (done) => { @@ -38,8 +36,7 @@ describe('The \'monitor\' method', () => { monitorClient.set('foo', 'bar') monitorClient.flushdb() - monitorClient.monitor((err, res) => { - assert.strictEqual(err, null) + monitorClient.monitor().then((res) => { assert.strictEqual(res, 'OK') client.mget('some', 'keys', 'foo', 'bar') client.set('json', JSON.stringify({ @@ -48,33 +45,28 @@ describe('The \'monitor\' method', () => { another: false })) client.eval('return redis.call(\'set\', \'sha\', \'test\')', 0) - monitorClient.get('baz', (err, res) => { + monitorClient.get('baz').then((res) => { assert.strictEqual(res, null) - end(err) + end() }) - monitorClient.set('foo', 'bar" "s are " " good!"', (err, res) => { + monitorClient.set('foo', 'bar" "s are " " good!"').then((res) => { assert.strictEqual(res, 'OK') - end(err) + end() }) - monitorClient.mget('foo', 'baz', (err, res) => { + monitorClient.mget('foo', 'baz').then((res) => { assert.strictEqual(res[0], 'bar" "s are " " good!"') assert.strictEqual(res[1], null) - end(err) - }) - monitorClient.subscribe('foo', 'baz', (err, res) => { - // The return value might change in v.3 - // assert.strictEqual(res, 'baz'); - // TODO: Fix the return value of subscribe calls - end(err) + end() }) + monitorClient.subscribe('foo', 'baz').then(() => end()) }) monitorClient.on('monitor', (time, args, rawOutput) => { assert.strictEqual(monitorClient.monitoring, true) - assert.deepEqual(args, responses.shift()) + assert.deepStrictEqual(args, responses.shift()) assert(utils.monitorRegex.test(rawOutput), rawOutput) if (responses.length === 0) { - monitorClient.quit(end) + monitorClient.quit().then(() => end()) } }) }) @@ -88,8 +80,7 @@ describe('The \'monitor\' method', () => { path: '/tmp/redis.sock' }) - monitorClient.monitor((err, res) => { - assert.strictEqual(err, null) + monitorClient.monitor().then((res) => { assert.strictEqual(monitorClient.monitoring, true) assert.strictEqual(res.inspect(), Buffer.from('OK').inspect()) monitorClient.mget('hello', Buffer.from('world')) @@ -98,20 +89,20 @@ describe('The \'monitor\' method', () => { monitorClient.on('monitor', (time, args, rawOutput) => { assert.strictEqual(typeof rawOutput, 'string') assert(utils.monitorRegex.test(rawOutput), rawOutput) - assert.deepEqual(args, ['mget', 'hello', 'world']) + assert.deepStrictEqual(args, ['mget', 'hello', 'world']) // Quit immediately ends monitoring mode and therefore does not stream back the quit command - monitorClient.quit(done) + monitorClient.quit().then(() => done()) }) }) it('monitors reconnects properly and works with the offline queue', (done) => { let called = false - client.monitor(helper.isString('OK')) + client.monitor().then(helper.isString('OK')) client.mget('hello', 'world') client.on('monitor', (time, args, rawOutput) => { assert.strictEqual(client.monitoring, true) assert(utils.monitorRegex.test(rawOutput), rawOutput) - assert.deepEqual(args, ['mget', 'hello', 'world']) + assert.deepStrictEqual(args, ['mget', 'hello', 'world']) if (called) { // End after a reconnect return done() @@ -125,13 +116,13 @@ describe('The \'monitor\' method', () => { it('monitors reconnects properly and works with the offline queue in a batch statement', (done) => { let called = false const multi = client.batch() - multi.monitor(helper.isString('OK')) + multi.monitor() multi.mget('hello', 'world') - multi.exec(helper.isDeepEqual(['OK', [null, null]])) + multi.exec().then(helper.isDeepEqual(['OK', [null, null]])) client.on('monitor', (time, args, rawOutput) => { assert.strictEqual(client.monitoring, true) assert(utils.monitorRegex.test(rawOutput), rawOutput) - assert.deepEqual(args, ['mget', 'hello', 'world']) + assert.deepStrictEqual(args, ['mget', 'hello', 'world']) if (called) { // End after a reconnect return done() @@ -143,7 +134,7 @@ describe('The \'monitor\' method', () => { }) it('monitor activates even if the command could not be processed properly after a reconnect', (done) => { - client.monitor((err, res) => { + client.monitor().then(assert, (err) => { assert.strictEqual(err.code, 'UNCERTAIN_STATE') }) client.on('error', () => {}) // Ignore error here @@ -154,8 +145,7 @@ describe('The \'monitor\' method', () => { end() }) client.on('reconnecting', () => { - client.get('foo', (err, res) => { - assert(!err) + client.get('foo').then((res) => { assert.strictEqual(client.monitoring, true) end() }) @@ -174,10 +164,9 @@ describe('The \'monitor\' method', () => { ] const pub = redis.createClient() pub.on('ready', () => { - client.monitor((err, res) => { - assert.strictEqual(err, null) + client.monitor().then((res) => { assert.strictEqual(res, 'OK') - pub.get('foo', helper.isNull()) + pub.get('foo').then(helper.isNull()) }) client.subscribe('/foo', '/bar') client.unsubscribe('/bar') @@ -186,21 +175,22 @@ describe('The \'monitor\' method', () => { client.once('ready', () => { pub.publish('/foo', 'hello world') }) - client.set('foo', 'bar', helper.isError()) + client.set('foo', 'bar') + .then(assert, helper.isError(/ERR only \(P\)SUBSCRIBE \/ \(P\)UNSUBSCRIBE/)) client.subscribe('baz') client.unsubscribe('baz') }, 150) let called = false client.on('monitor', (time, args, rawOutput) => { - assert.deepEqual(args, responses.shift()) + assert.deepStrictEqual(args, responses.shift()) assert(utils.monitorRegex.test(rawOutput), rawOutput) if (responses.length === 0) { // The publish is called right after the reconnect and the monitor is called before the message is emitted. // Therefore we have to wait till the next tick process.nextTick(() => { assert(called) - client.quit(done) pub.end(false) + client.quit().then(() => done()) }) } }) diff --git a/test/commands/mset.spec.js b/test/commands/mset.spec.js index e1a7d3bc6b..3b463d1945 100644 --- a/test/commands/mset.spec.js +++ b/test/commands/mset.spec.js @@ -21,19 +21,14 @@ describe('The \'mset\' method', () => { describe('when not connected', () => { let client - beforeEach((done) => { + beforeEach(() => { client = redis.createClient.apply(null, args) - client.once('ready', () => { - client.quit() - }) - client.on('end', done) + return client.quit() }) - it('reports an error', (done) => { - client.mset(key, value, key2, value2, (err, res) => { - assert(err.message.match(/The connection is already closed/)) - done() - }) + it('reports an error', () => { + return client.mset(key, value, key2, value2) + .then(assert, helper.isError(/The connection is already closed/)) }) }) @@ -42,63 +37,18 @@ describe('The \'mset\' method', () => { beforeEach((done) => { client = redis.createClient.apply(null, args) - client.once('ready', () => { - done() - }) + client.once('ready', done) }) afterEach(() => { client.end(true) }) - describe('and a callback is specified', () => { - describe('with valid parameters', () => { - it('sets the value correctly', (done) => { - client.mset(key, value, key2, value2, (err) => { - if (err) { - return done(err) - } - client.get(key, helper.isString(value)) - client.get(key2, helper.isString(value2, done)) - }) - }) - }) - - describe('with undefined \'key\' parameter and missing \'value\' parameter', () => { - it('reports an error', (done) => { - client.mset(undefined, (err, res) => { - helper.isError()(err, null) - done() - }) - }) - }) - }) - - describe('and no callback is specified', () => { - describe('with valid parameters', () => { - it('sets the value correctly', (done) => { - client.mset(key, value2, key2, value) - client.get(key, helper.isString(value2)) - client.get(key2, helper.isString(value, done)) - }) - - it('sets the value correctly with array syntax', (done) => { - client.mset([key, value2, key2, value]) - client.get(key, helper.isString(value2)) - client.get(key2, helper.isString(value, done)) - }) - }) - - describe('with undefined \'key\' and missing \'value\' parameter', () => { - // this behavior is different from the 'set' behavior. - it('emits an error', (done) => { - client.on('error', (err) => { - assert.strictEqual(err.message, 'ERR wrong number of arguments for \'mset\' command') - assert.strictEqual(err.name, 'ReplyError') - done() - }) - - client.mset() + describe('with valid parameters', () => { + it('sets the value correctly', () => { + return client.mset(key, value, key2, value2).then(() => { + client.get(key).then(helper.isString(value)) + return client.get(key2).then(helper.isString(value2)) }) }) }) diff --git a/test/commands/msetnx.spec.js b/test/commands/msetnx.spec.js index f032aa4c44..2cfb23b93a 100644 --- a/test/commands/msetnx.spec.js +++ b/test/commands/msetnx.spec.js @@ -9,23 +9,21 @@ describe('The \'msetnx\' method', () => { describe(`using ${ip}`, () => { let client - beforeEach((done) => { + beforeEach(() => { client = redis.createClient.apply(null, args) - client.once('ready', () => { - client.flushdb(done) - }) + return client.flushdb() }) - it('if any keys exist entire operation fails', (done) => { - client.mset(['mset1', 'val1', 'mset2', 'val2', 'mset3', 'val3'], helper.isString('OK')) - client.msetnx(['mset3', 'val3', 'mset4', 'val4'], helper.isNumber(0)) - client.exists(['mset4'], helper.isNumber(0, done)) + it('if any keys exist entire operation fails', () => { + client.mset(['mset1', 'val1', 'mset2', 'val2', 'mset3', 'val3']).then(helper.isString('OK')) + client.msetnx(['mset3', 'val3', 'mset4', 'val4']).then(helper.isNumber(0)) + return client.exists(['mset4']).then(helper.isNumber(0)) }) - it('sets multiple keys if all keys are not set', (done) => { - client.msetnx(['mset3', 'val3', 'mset4', 'val4'], helper.isNumber(1)) - client.exists(['mset3'], helper.isNumber(1)) - client.exists(['mset3'], helper.isNumber(1, done)) + it('sets multiple keys if all keys are not set', () => { + client.msetnx(['mset3', 'val3', 'mset4', 'val4']).then(helper.isNumber(1)) + client.exists(['mset3']).then(helper.isNumber(1)) + return client.exists(['mset3']).then(helper.isNumber(1)) }) afterEach(() => { diff --git a/test/commands/randomkey.test.js b/test/commands/randomkey.test.js index bfbb6389e5..e7be46e925 100644 --- a/test/commands/randomkey.test.js +++ b/test/commands/randomkey.test.js @@ -10,18 +10,15 @@ describe('The \'randomkey\' method', () => { describe(`using ${ip}`, () => { let client - beforeEach((done) => { + beforeEach(() => { client = redis.createClient.apply(null, args) - client.once('ready', () => { - client.flushdb(done) - }) + return client.flushdb() }) - it('returns a random key', (done) => { - client.mset(['test keys 1', 'test val 1', 'test keys 2', 'test val 2'], helper.isString('OK')) - client.randomkey([], (err, results) => { + it('returns a random key', () => { + client.mset(['test keys 1', 'test val 1', 'test keys 2', 'test val 2']).then(helper.isString('OK')) + return client.randomkey([]).then((results) => { assert.strictEqual(true, /test keys.+/.test(results)) - return done(err) }) }) diff --git a/test/commands/rename.spec.js b/test/commands/rename.spec.js index 434183e5fc..34893e6948 100644 --- a/test/commands/rename.spec.js +++ b/test/commands/rename.spec.js @@ -9,23 +9,21 @@ describe('The \'rename\' method', () => { describe(`using ${ip}`, () => { let client - beforeEach((done) => { + beforeEach(() => { client = redis.createClient.apply(null, args) - client.once('ready', () => { - client.flushdb(done) - }) + return client.flushdb() }) - it('populates the new key', (done) => { - client.set(['foo', 'bar'], helper.isString('OK')) - client.rename(['foo', 'new foo'], helper.isString('OK')) - client.exists(['new foo'], helper.isNumber(1, done)) + it('populates the new key', () => { + client.set(['foo', 'bar']).then(helper.isString('OK')) + client.rename(['foo', 'new foo']).then(helper.isString('OK')) + return client.exists(['new foo']).then(helper.isNumber(1)) }) - it('removes the old key', (done) => { - client.set(['foo', 'bar'], helper.isString('OK')) - client.rename(['foo', 'new foo'], helper.isString('OK')) - client.exists(['foo'], helper.isNumber(0, done)) + it('removes the old key', () => { + client.set(['foo', 'bar']).then(helper.isString('OK')) + client.rename(['foo', 'new foo']).then(helper.isString('OK')) + return client.exists(['foo']).then(helper.isNumber(0)) }) afterEach(() => { diff --git a/test/commands/renamenx.spec.js b/test/commands/renamenx.spec.js index 23ef44c025..04e655666a 100644 --- a/test/commands/renamenx.spec.js +++ b/test/commands/renamenx.spec.js @@ -9,26 +9,24 @@ describe('The \'renamenx\' method', () => { describe(`using ${ip}`, () => { let client - beforeEach((done) => { + beforeEach(() => { client = redis.createClient.apply(null, args) - client.once('ready', () => { - client.flushdb(done) - }) + return client.flushdb() }) - it('renames the key if target does not yet exist', (done) => { - client.set('foo', 'bar', helper.isString('OK')) - client.renamenx('foo', 'foo2', helper.isNumber(1)) - client.exists('foo', helper.isNumber(0)) - client.exists(['foo2'], helper.isNumber(1, done)) + it('renames the key if target does not yet exist', () => { + client.set('foo', 'bar').then(helper.isString('OK')) + client.renamenx('foo', 'foo2').then(helper.isNumber(1)) + client.exists('foo').then(helper.isNumber(0)) + return client.exists(['foo2']).then(helper.isNumber(1)) }) - it('does not rename the key if the target exists', (done) => { - client.set('foo', 'bar', helper.isString('OK')) - client.set('foo2', 'apple', helper.isString('OK')) - client.renamenx('foo', 'foo2', helper.isNumber(0)) - client.exists('foo', helper.isNumber(1)) - client.exists(['foo2'], helper.isNumber(1, done)) + it('does not rename the key if the target exists', () => { + client.set('foo', 'bar').then(helper.isString('OK')) + client.set('foo2', 'apple').then(helper.isString('OK')) + client.renamenx('foo', 'foo2').then(helper.isNumber(0)) + client.exists('foo').then(helper.isNumber(1)) + return client.exists(['foo2']).then(helper.isNumber(1)) }) afterEach(() => { diff --git a/test/commands/rpush.spec.js b/test/commands/rpush.spec.js index 03c5cf8c8d..8430ccff20 100644 --- a/test/commands/rpush.spec.js +++ b/test/commands/rpush.spec.js @@ -3,27 +3,21 @@ const config = require('../lib/config') const helper = require('../helper') const redis = config.redis -const assert = require('assert') describe('The \'rpush\' command', () => { helper.allTests((ip, args) => { describe(`using ${ip}`, () => { let client - beforeEach((done) => { + beforeEach(() => { client = redis.createClient.apply(null, args) - client.once('ready', () => { - client.flushdb(done) - }) + return client.flushdb() }) - it('inserts multiple values at a time into a list', (done) => { - client.rpush('test', ['list key', 'should be a list']) - client.lrange('test', 0, -1, (err, res) => { - assert.strictEqual(res[0], 'list key') - assert.strictEqual(res[1], 'should be a list') - done(err) - }) + it('inserts multiple values at a time into a list', () => { + const list = ['list key', 'should be a list'] + client.rpush('test', list) + return client.lrange('test', 0, -1).then(helper.isDeepEqual(list)) }) afterEach(() => { diff --git a/test/commands/sadd.spec.js b/test/commands/sadd.spec.js index 11f11a8138..08ba12501f 100644 --- a/test/commands/sadd.spec.js +++ b/test/commands/sadd.spec.js @@ -10,45 +10,40 @@ describe('The \'sadd\' method', () => { describe(`using ${ip}`, () => { let client - beforeEach((done) => { + beforeEach(() => { client = redis.createClient.apply(null, args) - client.once('ready', () => { - client.flushdb(done) - }) + return client.flushdb() }) - it('allows a single value to be added to the set', (done) => { - client.sadd('set0', 'member0', helper.isNumber(1)) - client.smembers('set0', (err, res) => { + it('allows a single value to be added to the set', () => { + client.sadd('set0', 'member0').then(helper.isNumber(1)) + return client.smembers('set0').then((res) => { assert.ok(~res.indexOf('member0')) - return done(err) }) }) - it('does not add the same value to the set twice', (done) => { - client.sadd('set0', 'member0', helper.isNumber(1)) - client.sadd('set0', 'member0', helper.isNumber(0, done)) + it('does not add the same value to the set twice', () => { + client.sadd('set0', 'member0').then(helper.isNumber(1)) + return client.sadd('set0', 'member0').then(helper.isNumber(0)) }) - it('allows multiple values to be added to the set', (done) => { - client.sadd('set0', ['member0', 'member1', 'member2'], helper.isNumber(3)) - client.smembers('set0', (err, res) => { + it('allows multiple values to be added to the set', () => { + client.sadd('set0', ['member0', 'member1', 'member2']).then(helper.isNumber(3)) + return client.smembers('set0').then((res) => { assert.strictEqual(res.length, 3) assert.ok(~res.indexOf('member0')) assert.ok(~res.indexOf('member1')) assert.ok(~res.indexOf('member2')) - return done(err) }) }) - it('allows multiple values to be added to the set with a different syntax', (done) => { - client.sadd(['set0', 'member0', 'member1', 'member2'], helper.isNumber(3)) - client.smembers('set0', (err, res) => { + it('allows multiple values to be added to the set with a different syntax', () => { + client.sadd(['set0', 'member0', 'member1', 'member2']).then(helper.isNumber(3)) + return client.smembers('set0').then((res) => { assert.strictEqual(res.length, 3) assert.ok(~res.indexOf('member0')) assert.ok(~res.indexOf('member1')) assert.ok(~res.indexOf('member2')) - return done(err) }) }) diff --git a/test/commands/scard.spec.js b/test/commands/scard.spec.js index fd21edab96..26cca9b815 100644 --- a/test/commands/scard.spec.js +++ b/test/commands/scard.spec.js @@ -9,16 +9,14 @@ describe('The \'scard\' method', () => { describe(`using ${ip}`, () => { let client - beforeEach((done) => { + beforeEach(() => { client = redis.createClient.apply(null, args) - client.once('ready', () => { - client.flushdb(done) - }) + return client.flushdb() }) - it('returns the number of values in a set', (done) => { - client.sadd('foo', [1, 2, 3], helper.isNumber(3)) - client.scard('foo', helper.isNumber(3, done)) + it('returns the number of values in a set', () => { + client.sadd('foo', [1, 2, 3]).then(helper.isNumber(3)) + return client.scard('foo').then(helper.isNumber(3)) }) afterEach(() => { diff --git a/test/commands/script.spec.js b/test/commands/script.spec.js index d9bdac0b0d..e8f9855bf4 100644 --- a/test/commands/script.spec.js +++ b/test/commands/script.spec.js @@ -13,31 +13,29 @@ describe('The \'script\' method', () => { describe(`using ${ip}`, () => { let client - beforeEach((done) => { + beforeEach(() => { client = redis.createClient.apply(null, args) - client.once('ready', () => { - client.flushdb(done) - }) + return client.flushdb() }) afterEach(() => { client.end(true) }) - it('loads script with client.script(\'load\')', (done) => { - client.script('load', command, helper.isString(commandSha, done)) + it('loads script with client.script(\'load\')', () => { + return client.script('load', command).then(helper.isString(commandSha)) }) - it('allows a loaded script to be evaluated', (done) => { - client.evalsha(commandSha, 0, helper.isNumber(99, done)) + it('allows a loaded script to be evaluated', () => { + return client.evalsha(commandSha, 0).then(helper.isNumber(99)) }) - it('allows a script to be loaded as part of a chained transaction', (done) => { - client.multi().script('load', command).exec(helper.isDeepEqual([commandSha], done)) + it('allows a script to be loaded as part of a chained transaction', () => { + return client.multi().script('load', command).exec(helper.isDeepEqual([commandSha])) }) - it('allows a script to be loaded using a transaction\'s array syntax', (done) => { - client.multi([['script', 'load', command]]).exec(helper.isDeepEqual([commandSha], done)) + it('allows a script to be loaded using a transaction\'s array syntax', () => { + return client.multi([['script', 'load', command]]).exec(helper.isDeepEqual([commandSha])) }) }) }) diff --git a/test/commands/sdiff.spec.js b/test/commands/sdiff.spec.js index f8bc9448d9..989c1f87d4 100644 --- a/test/commands/sdiff.spec.js +++ b/test/commands/sdiff.spec.js @@ -10,30 +10,25 @@ describe('The \'sdiff\' method', () => { describe(`using ${ip}`, () => { let client - beforeEach((done) => { + beforeEach(() => { client = redis.createClient.apply(null, args) - client.once('ready', () => { - client.flushdb(done) - }) + return client.flushdb() }) - it('returns set difference', (done) => { - client.sadd('foo', 'x', helper.isNumber(1)) - client.sadd('foo', ['a'], helper.isNumber(1)) - client.sadd('foo', 'b', helper.isNumber(1)) - client.sadd(['foo', 'c'], helper.isNumber(1)) + it('returns set difference', () => { + client.sadd('foo', 'x').then(helper.isNumber(1)) + client.sadd('foo', ['a']).then(helper.isNumber(1)) + client.sadd('foo', 'b').then(helper.isNumber(1)) + client.sadd(['foo', 'c']).then(helper.isNumber(1)) + client.sadd(['bar', 'c']).then(helper.isNumber(1)) + client.sadd('baz', 'a').then(helper.isNumber(1)) + client.sadd('baz', 'd').then(helper.isNumber(1)) - client.sadd(['bar', 'c', helper.isNumber(1)]) - - client.sadd('baz', 'a', helper.isNumber(1)) - client.sadd('baz', 'd', helper.isNumber(1)) - - client.sdiff('foo', 'bar', 'baz', (err, values) => { + return client.sdiff('foo', 'bar', 'baz').then((values) => { values.sort() assert.strictEqual(values.length, 2) assert.strictEqual(values[0], 'b') assert.strictEqual(values[1], 'x') - return done(err) }) }) diff --git a/test/commands/sdiffstore.spec.js b/test/commands/sdiffstore.spec.js index 5e459e85a9..26a0c50ff0 100644 --- a/test/commands/sdiffstore.spec.js +++ b/test/commands/sdiffstore.spec.js @@ -10,30 +10,25 @@ describe('The \'sdiffstore\' method', () => { describe(`using ${ip}`, () => { let client - beforeEach((done) => { + beforeEach(() => { client = redis.createClient.apply(null, args) - client.once('ready', () => { - client.flushdb(done) - }) + return client.flushdb() }) - it('calculates set difference ands stores it in a key', (done) => { - client.sadd('foo', 'x', helper.isNumber(1)) - client.sadd('foo', 'a', helper.isNumber(1)) - client.sadd('foo', 'b', helper.isNumber(1)) - client.sadd('foo', 'c', helper.isNumber(1)) + it('calculates set difference ands stores it in a key', () => { + client.sadd('foo', 'x').then(helper.isNumber(1)) + client.sadd('foo', 'a').then(helper.isNumber(1)) + client.sadd('foo', 'b').then(helper.isNumber(1)) + client.sadd('foo', 'c').then(helper.isNumber(1)) + client.sadd('bar', 'c').then(helper.isNumber(1)) + client.sadd('baz', 'a').then(helper.isNumber(1)) + client.sadd('baz', 'd').then(helper.isNumber(1)) - client.sadd('bar', 'c', helper.isNumber(1)) + client.sdiffstore('quux', 'foo', 'bar', 'baz').then(helper.isNumber(2)) - client.sadd('baz', 'a', helper.isNumber(1)) - client.sadd('baz', 'd', helper.isNumber(1)) - - client.sdiffstore('quux', 'foo', 'bar', 'baz', helper.isNumber(2)) - - client.smembers('quux', (err, values) => { + return client.smembers('quux').then((values) => { const members = values.sort() - assert.deepEqual(members, [ 'b', 'x' ]) - return done(err) + assert.deepStrictEqual(members, [ 'b', 'x' ]) }) }) diff --git a/test/commands/select.spec.js b/test/commands/select.spec.js index bcabb8aca0..9ee1f95425 100644 --- a/test/commands/select.spec.js +++ b/test/commands/select.spec.js @@ -11,104 +11,60 @@ describe('The \'select\' method', () => { describe('when not connected', () => { let client - beforeEach((done) => { + beforeEach(() => { client = redis.createClient.apply(null, args) - client.once('ready', () => { - client.quit() - }) - client.on('end', done) + return client.quit() }) - it('returns an error if redis is not connected', (done) => { - client.select(1, (err, res) => { - assert(err.message.match(/The connection is already closed/)) - done() - }) + it('returns an error if redis is not connected', () => { + return client.select(1).then(assert, helper.isError(/The connection is already closed/)) }) }) describe('when connected', () => { let client - beforeEach((done) => { + beforeEach(() => { client = redis.createClient.apply(null, args) - client.once('ready', () => { - client.flushdb(done) - }) + return client.flushdb() }) afterEach(() => { client.end(true) }) - it('changes the database and calls the callback', (done) => { + it('changes the database', () => { // default value of null means database 0 will be used. assert.strictEqual(client.selectedDb, undefined, 'default db should be undefined') - client.select(1, (err, res) => { - helper.isNotError()(err, res) + return client.select(1).then((res) => { assert.strictEqual(client.selectedDb, 1, 'db should be 1 after select') - done() }) }) - describe('and a callback is specified', () => { - describe('with a valid db index', () => { - it('selects the appropriate database', (done) => { - assert.strictEqual(client.selectedDb, undefined, 'default db should be undefined') - client.select(1, (err) => { - assert.strictEqual(err, null) - assert.strictEqual(client.selectedDb, 1, 'we should have selected the new valid DB') - done() - }) - }) - }) - - describe('with an invalid db index', () => { - it('returns an error', (done) => { - assert.strictEqual(client.selectedDb, undefined, 'default db should be undefined') - client.select(9999, (err) => { - assert.strictEqual(err.code, 'ERR') - assert.strictEqual(err.message, 'ERR invalid DB index') - done() - }) + describe('with a valid db index', () => { + it('selects the appropriate database', () => { + assert.strictEqual(client.selectedDb, undefined, 'default db should be undefined') + return client.select(1).then(() => { + assert.strictEqual(client.selectedDb, 1, 'we should have selected the new valid DB') }) }) }) - describe('and no callback is specified', () => { - describe('with a valid db index', () => { - it('selects the appropriate database', (done) => { - assert.strictEqual(client.selectedDb, undefined, 'default db should be undefined') - client.select(1) - setTimeout(() => { - assert.strictEqual(client.selectedDb, 1, 'we should have selected the new valid DB') - done() - }, 25) - }) - }) - - describe('with an invalid db index', () => { - it('emits an error when callback not provided', (done) => { - assert.strictEqual(client.selectedDb, undefined, 'default db should be undefined') - - client.on('error', (err) => { - assert.strictEqual(err.command, 'SELECT') - assert.strictEqual(err.message, 'ERR invalid DB index') - done() - }) - - client.select(9999) + describe('with an invalid db index', () => { + it('returns an error', () => { + assert.strictEqual(client.selectedDb, undefined, 'default db should be undefined') + return client.select(9999).then(assert, (err) => { + assert.strictEqual(err.code, 'ERR') + assert.strictEqual(err.message, 'ERR invalid DB index') }) }) }) - describe('reconnection occurs', () => { + describe('reconnecting', () => { it('selects the appropriate database after a reconnect', (done) => { assert.strictEqual(client.selectedDb, undefined, 'default db should be undefined') client.select(3) - client.set('foo', 'bar', () => { - client.stream.destroy() - }) + client.set('foo', 'bar').then(() => client.stream.destroy()) client.once('ready', () => { assert.strictEqual(client.selectedDb, 3) assert(typeof client.serverInfo.db3 === 'object') diff --git a/test/commands/set.spec.js b/test/commands/set.spec.js index 1f5796f810..49b949280e 100644 --- a/test/commands/set.spec.js +++ b/test/commands/set.spec.js @@ -19,145 +19,65 @@ describe('The \'set\' method', () => { describe('when not connected', () => { let client - beforeEach((done) => { + beforeEach(() => { client = redis.createClient.apply(null, args) - client.once('ready', () => { - client.quit() - }) - client.on('end', done) + return client.quit() }) - it('reports an error', (done) => { - client.set(key, value, (err, res) => { - assert(err.message.match(/The connection is already closed/)) - done() - }) + it('reports an error', () => { + return client.set(key, value).then(assert, helper.isError(/The connection is already closed/)) }) }) describe('when connected', () => { let client - beforeEach((done) => { + beforeEach(() => { client = redis.createClient.apply(null, args) - client.once('ready', () => { - client.flushdb(done) - }) + return client.flushdb() }) afterEach(() => { client.end(true) }) - describe('and a callback is specified', () => { - describe('with valid parameters', () => { - it('sets the value correctly', (done) => { - client.set(key, value, (err, res) => { - helper.isNotError()(err, res) - client.get(key, (err, res) => { - helper.isString(value)(err, res) - done() - }) - }) - }) + describe('with valid parameters', () => { + it('sets the value correctly', () => { + client.set(key, value) + return client.get(key).then(helper.isString(value)) + }) - it('set expire date in seconds', (done) => { - client.set('foo', 'bar', 'ex', 10, helper.isString('OK')) - client.pttl('foo', (err, res) => { - assert(res >= 10000 - 50) // Max 50 ms should have passed - assert(res <= 10000) // Max possible should be 10.000 - done(err) - }) - }) - - it('set expire date in milliseconds', (done) => { - client.set('foo', 'bar', 'px', 100, helper.isString('OK')) - client.pttl('foo', (err, res) => { - assert(res >= 50) // Max 50 ms should have passed - assert(res <= 100) // Max possible should be 100 - done(err) - }) - }) - - it('only set the key if (not) already set', (done) => { - client.set('foo', 'bar', 'NX', helper.isString('OK')) - client.set('foo', 'bar', 'nx', helper.isNull()) - client.set('foo', 'bar', 'EX', '10', 'XX', helper.isString('OK')) - client.ttl('foo', (err, res) => { - assert(res >= 9) // Min 9s should be left - assert(res <= 10) // Max 10s should be left - done(err) - }) + it('set expire date in seconds', () => { + client.set('foo', 'bar', 'ex', 10).then(helper.isString('OK')) + return client.pttl('foo').then((res) => { + assert(res >= 10000 - 50) // Max 50 ms should have passed + assert(res <= 10000) // Max possible should be 10.000 }) }) - describe('reports an error with invalid parameters', () => { - it('undefined \'key\' and missing \'value\' parameter', (done) => { - client.set(undefined, (err, res) => { - helper.isError()(err, null) - assert.strictEqual(err.command, 'SET') - done() - }) + it('set expire date in milliseconds', () => { + client.set('foo', 'bar', 'px', 100).then(helper.isString('OK')) + return client.pttl('foo').then((res) => { + assert(res >= 50) // Max 50 ms should have passed + assert(res <= 100) // Max possible should be 100 }) + }) - it('empty array as second parameter', (done) => { - client.set('foo', [], (err, res) => { - assert.strictEqual(err.message, 'ERR wrong number of arguments for \'set\' command') - done() - }) + it('only set the key if (not) already set', () => { + client.set('foo', 'bar', 'NX').then(helper.isString('OK')) + client.set('foo', 'bar', 'nx').then(helper.isNull()) + client.set('foo', 'bar', 'EX', '10', 'XX').then(helper.isString('OK')) + return client.ttl('foo').then((res) => { + assert(res >= 9) // Min 9s should be left + assert(res <= 10) // Max 10s should be left }) }) }) - describe('and no callback is specified', () => { - describe('with valid parameters', () => { - it('sets the value correctly', (done) => { - client.set(key, value) - client.get(key, helper.isString(value, done)) - }) - - it('sets the value correctly even if the callback is explicitly set to undefined', (done) => { - client.set(key, value, undefined) - client.get(key, helper.isString(value, done)) - }) - - it('sets the value correctly with the array syntax', (done) => { - client.set([key, value]) - client.get(key, helper.isString(value, done)) - }) - }) - - describe('with undefined \'key\' and missing \'value\' parameter', () => { - it('emits an error without callback', (done) => { - client.on('error', (err) => { - assert.strictEqual(err.message, 'ERR wrong number of arguments for \'set\' command') - assert.strictEqual(err.command, 'SET') - done() - }) - client.set(undefined) - }) - }) - - it('returns an error on \'null\'', (done) => { - client.set('foo', null, helper.isError(done)) - }) - - it('emit an error with only the key set', (done) => { - client.on('error', (err) => { - assert.strictEqual(err.message, 'ERR wrong number of arguments for \'set\' command') - done() - }) - - client.set('foo') - }) - - it('emit an error without any parameters', (done) => { - client.once('error', (err) => { - assert.strictEqual(err.message, 'ERR wrong number of arguments for \'set\' command') - assert.strictEqual(err.command, 'SET') - done() - }) - client.set() + describe('reports an error with invalid parameters', () => { + it('empty array as second parameter', () => { + return client.set('foo', []) + .then(assert, helper.isError(/ERR wrong number of arguments for 'set' command/)) }) }) }) diff --git a/test/commands/setex.spec.js b/test/commands/setex.spec.js index eae91259b4..42ea9e1d10 100644 --- a/test/commands/setex.spec.js +++ b/test/commands/setex.spec.js @@ -10,21 +10,15 @@ describe('The \'setex\' method', () => { describe(`using ${ip}`, () => { let client - beforeEach((done) => { + beforeEach(() => { client = redis.createClient.apply(null, args) - client.once('ready', () => { - client.flushdb(done) - }) + return client.flushdb() }) - it('sets a key with an expiry', (done) => { - client.setex(['setex key', '100', 'setex val'], helper.isString('OK')) - client.exists(['setex key'], helper.isNumber(1)) - client.ttl(['setex key'], (err, ttl) => { - assert.strictEqual(err, null) - assert(ttl > 0) - return done() - }) + it('sets a key with an expiry', () => { + client.setex(['setex key', '100', 'setex val']).then(helper.isString('OK')) + client.exists(['setex key']).then(helper.isNumber(1)) + return client.ttl(['setex key']).then(assert) }) afterEach(() => { diff --git a/test/commands/setnx.spec.js b/test/commands/setnx.spec.js index a5c49162c5..0265726715 100644 --- a/test/commands/setnx.spec.js +++ b/test/commands/setnx.spec.js @@ -9,22 +9,20 @@ describe('The \'setnx\' method', () => { describe(`using ${ip}`, () => { let client - beforeEach((done) => { + beforeEach(() => { client = redis.createClient.apply(null, args) - client.once('ready', () => { - client.flushdb(done) - }) + return client.flushdb() }) - it('sets key if it does not have a value', (done) => { - client.setnx('foo', 'banana', helper.isNumber(1)) - client.get('foo', helper.isString('banana', done)) + it('sets key if it does not have a value', () => { + client.setnx('foo', 'banana').then(helper.isNumber(1)) + return client.get('foo').then(helper.isString('banana')) }) - it('does not set key if it already has a value', (done) => { - client.set('foo', 'bar', helper.isString('OK')) - client.setnx('foo', 'banana', helper.isNumber(0)) - client.get('foo', helper.isString('bar', done)) + it('does not set key if it already has a value', () => { + client.set('foo', 'bar').then(helper.isString('OK')) + client.setnx('foo', 'banana').then(helper.isNumber(0)) + return client.get('foo').then(helper.isString('bar')) }) afterEach(() => { diff --git a/test/commands/sinter.spec.js b/test/commands/sinter.spec.js index 30e93b16e0..37a5742c1f 100644 --- a/test/commands/sinter.spec.js +++ b/test/commands/sinter.spec.js @@ -10,46 +10,42 @@ describe('The \'sinter\' method', () => { describe(`using ${ip}`, () => { let client - beforeEach((done) => { + beforeEach(() => { client = redis.createClient.apply(null, args) - client.once('ready', () => { - client.flushdb(done) - }) + return client.flushdb() }) - it('handles two sets being intersected', (done) => { - client.sadd('sa', 'a', helper.isNumber(1)) - client.sadd('sa', 'b', helper.isNumber(1)) - client.sadd('sa', 'c', helper.isNumber(1)) + it('handles two sets being intersected', () => { + client.sadd('sa', 'a').then(helper.isNumber(1)) + client.sadd('sa', 'b').then(helper.isNumber(1)) + client.sadd('sa', 'c').then(helper.isNumber(1)) - client.sadd('sb', 'b', helper.isNumber(1)) - client.sadd('sb', 'c', helper.isNumber(1)) - client.sadd('sb', 'd', helper.isNumber(1)) + client.sadd('sb', 'b').then(helper.isNumber(1)) + client.sadd('sb', 'c').then(helper.isNumber(1)) + client.sadd('sb', 'd').then(helper.isNumber(1)) - client.sinter('sa', 'sb', (err, intersection) => { + return client.sinter('sa', 'sb').then((intersection) => { assert.strictEqual(intersection.length, 2) - assert.deepEqual(intersection.sort(), [ 'b', 'c' ]) - return done(err) + assert.deepStrictEqual(intersection.sort(), [ 'b', 'c' ]) }) }) - it('handles three sets being intersected', (done) => { - client.sadd('sa', 'a', helper.isNumber(1)) - client.sadd('sa', 'b', helper.isNumber(1)) - client.sadd('sa', 'c', helper.isNumber(1)) + it('handles three sets being intersected', () => { + client.sadd('sa', 'a').then(helper.isNumber(1)) + client.sadd('sa', 'b').then(helper.isNumber(1)) + client.sadd('sa', 'c').then(helper.isNumber(1)) - client.sadd('sb', 'b', helper.isNumber(1)) - client.sadd('sb', 'c', helper.isNumber(1)) - client.sadd('sb', 'd', helper.isNumber(1)) + client.sadd('sb', 'b').then(helper.isNumber(1)) + client.sadd('sb', 'c').then(helper.isNumber(1)) + client.sadd('sb', 'd').then(helper.isNumber(1)) - client.sadd('sc', 'c', helper.isNumber(1)) - client.sadd('sc', 'd', helper.isNumber(1)) - client.sadd('sc', 'e', helper.isNumber(1)) + client.sadd('sc', 'c').then(helper.isNumber(1)) + client.sadd('sc', 'd').then(helper.isNumber(1)) + client.sadd('sc', 'e').then(helper.isNumber(1)) - client.sinter('sa', 'sb', 'sc', (err, intersection) => { + return client.sinter('sa', 'sb', 'sc').then((intersection) => { assert.strictEqual(intersection.length, 1) assert.strictEqual(intersection[0], 'c') - return done(err) }) }) diff --git a/test/commands/sinterstore.spec.js b/test/commands/sinterstore.spec.js index 629fc84a51..012ee845eb 100644 --- a/test/commands/sinterstore.spec.js +++ b/test/commands/sinterstore.spec.js @@ -1,6 +1,5 @@ 'use strict' -const assert = require('assert') const config = require('../lib/config') const helper = require('../helper') const redis = config.redis @@ -10,32 +9,27 @@ describe('The \'sinterstore\' method', () => { describe(`using ${ip}`, () => { let client - beforeEach((done) => { + beforeEach(() => { client = redis.createClient.apply(null, args) - client.once('ready', () => { - client.flushdb(done) - }) + return client.flushdb() }) - it('calculates set intersection and stores it in a key', (done) => { - client.sadd('sa', 'a', helper.isNumber(1)) - client.sadd('sa', 'b', helper.isNumber(1)) - client.sadd('sa', 'c', helper.isNumber(1)) + it('calculates set intersection and stores it in a key', () => { + client.sadd('sa', 'a').then(helper.isNumber(1)) + client.sadd('sa', 'b').then(helper.isNumber(1)) + client.sadd('sa', 'c').then(helper.isNumber(1)) - client.sadd('sb', 'b', helper.isNumber(1)) - client.sadd('sb', 'c', helper.isNumber(1)) - client.sadd('sb', 'd', helper.isNumber(1)) + client.sadd('sb', 'b').then(helper.isNumber(1)) + client.sadd('sb', 'c').then(helper.isNumber(1)) + client.sadd('sb', 'd').then(helper.isNumber(1)) - client.sadd('sc', 'c', helper.isNumber(1)) - client.sadd('sc', 'd', helper.isNumber(1)) - client.sadd('sc', 'e', helper.isNumber(1)) + client.sadd('sc', 'c').then(helper.isNumber(1)) + client.sadd('sc', 'd').then(helper.isNumber(1)) + client.sadd('sc', 'e').then(helper.isNumber(1)) - client.sinterstore('foo', 'sa', 'sb', 'sc', helper.isNumber(1)) + client.sinterstore('foo', 'sa', 'sb', 'sc').then(helper.isNumber(1)) - client.smembers('foo', (err, members) => { - assert.deepEqual(members, [ 'c' ]) - return done(err) - }) + return client.smembers('foo').then(helper.isDeepEqual(['c'])) }) afterEach(() => { diff --git a/test/commands/sismember.spec.js b/test/commands/sismember.spec.js index eec500c6fe..2277800afc 100644 --- a/test/commands/sismember.spec.js +++ b/test/commands/sismember.spec.js @@ -9,20 +9,18 @@ describe('The \'sismember\' method', () => { describe(`using ${ip}`, () => { let client - beforeEach((done) => { + beforeEach(() => { client = redis.createClient.apply(null, args) - client.once('ready', () => { - client.flushdb(done) - }) + return client.flushdb() }) - it('returns 0 if the value is not in the set', (done) => { - client.sismember('foo', 'banana', helper.isNumber(0, done)) + it('returns 0 if the value is not in the set', () => { + return client.sismember('foo', 'banana').then(helper.isNumber(0)) }) - it('returns 1 if the value is in the set', (done) => { - client.sadd('foo', 'banana', helper.isNumber(1)) - client.sismember('foo', 'banana', helper.isNumber(1, done)) + it('returns 1 if the value is in the set', () => { + client.sadd('foo', 'banana').then(helper.isNumber(1)) + return client.sismember('foo', 'banana').then(helper.isNumber(1)) }) afterEach(() => { diff --git a/test/commands/slowlog.spec.js b/test/commands/slowlog.spec.js index ce5fe07b93..93ca29043e 100644 --- a/test/commands/slowlog.spec.js +++ b/test/commands/slowlog.spec.js @@ -10,24 +10,21 @@ describe('The \'slowlog\' method', () => { describe(`using ${ip}`, () => { let client - beforeEach((done) => { + beforeEach(() => { client = redis.createClient.apply(null, args) - client.once('ready', () => { - client.flushdb(done) - }) + return client.flushdb() }) - it('logs operations in slowlog', (done) => { - client.config('set', 'slowlog-log-slower-than', 0, helper.isString('OK')) - client.slowlog('reset', helper.isString('OK')) - client.set('foo', 'bar', helper.isString('OK')) - client.get('foo', helper.isString('bar')) - client.slowlog('get', (err, res) => { + it('logs operations in slowlog', () => { + client.config('set', 'slowlog-log-slower-than', 0).then(helper.isString('OK')) + client.slowlog('reset').then(helper.isString('OK')) + client.set('foo', 'bar').then(helper.isString('OK')) + client.get('foo').then(helper.isString('bar')) + return client.slowlog('get').then((res) => { assert.strictEqual(res.length, 3) assert.strictEqual(res[0][3].length, 2) - assert.deepEqual(res[1][3], ['set', 'foo', 'bar']) - assert.deepEqual(res[2][3], ['slowlog', 'reset']) - return done(err) + assert.deepStrictEqual(res[1][3], ['set', 'foo', 'bar']) + assert.deepStrictEqual(res[2][3], ['slowlog', 'reset']) }) }) diff --git a/test/commands/smembers.spec.js b/test/commands/smembers.spec.js index c096fc35f5..22e5c9e5b7 100644 --- a/test/commands/smembers.spec.js +++ b/test/commands/smembers.spec.js @@ -10,21 +10,18 @@ describe('The \'smembers\' method', () => { describe(`using ${ip}`, () => { let client - beforeEach((done) => { + beforeEach(() => { client = redis.createClient.apply(null, args) - client.once('ready', () => { - client.flushdb(done) - }) + return client.flushdb() }) - it('returns all values in a set', (done) => { - client.sadd('foo', 'x', helper.isNumber(1)) - client.sadd('foo', 'y', helper.isNumber(1)) - client.smembers('foo', (err, values) => { + it('returns all values in a set', () => { + client.sadd('foo', 'x').then(helper.isNumber(1)) + client.sadd('foo', 'y').then(helper.isNumber(1)) + return client.smembers('foo').then((values) => { assert.strictEqual(values.length, 2) const members = values.sort() - assert.deepEqual(members, [ 'x', 'y' ]) - return done(err) + assert.deepStrictEqual(members, [ 'x', 'y' ]) }) }) diff --git a/test/commands/smove.spec.js b/test/commands/smove.spec.js index 4b6b0a1d9e..d078de95ec 100644 --- a/test/commands/smove.spec.js +++ b/test/commands/smove.spec.js @@ -9,25 +9,23 @@ describe('The \'smove\' method', () => { describe(`using ${ip}`, () => { let client - beforeEach((done) => { + beforeEach(() => { client = redis.createClient.apply(null, args) - client.once('ready', () => { - client.flushdb(done) - }) + return client.flushdb() }) - it('moves a value to a set that does not yet exist', (done) => { - client.sadd('foo', 'x', helper.isNumber(1)) - client.smove('foo', 'bar', 'x', helper.isNumber(1)) - client.sismember('foo', 'x', helper.isNumber(0)) - client.sismember('bar', 'x', helper.isNumber(1, done)) + it('moves a value to a set that does not yet exist', () => { + client.sadd('foo', 'x').then(helper.isNumber(1)) + client.smove('foo', 'bar', 'x').then(helper.isNumber(1)) + client.sismember('foo', 'x').then(helper.isNumber(0)) + return client.sismember('bar', 'x').then(helper.isNumber(1)) }) - it('does not move a value if it does not exist in the first set', (done) => { - client.sadd('foo', 'x', helper.isNumber(1)) - client.smove('foo', 'bar', 'y', helper.isNumber(0)) - client.sismember('foo', 'y', helper.isNumber(0)) - client.sismember('bar', 'y', helper.isNumber(0, done)) + it('does not move a value if it does not exist in the first set', () => { + client.sadd('foo', 'x').then(helper.isNumber(1)) + client.smove('foo', 'bar', 'y').then(helper.isNumber(0)) + client.sismember('foo', 'y').then(helper.isNumber(0)) + return client.sismember('bar', 'y').then(helper.isNumber(0)) }) afterEach(() => { diff --git a/test/commands/sort.spec.js b/test/commands/sort.spec.js index 8122ecbbd3..5a8f764945 100644 --- a/test/commands/sort.spec.js +++ b/test/commands/sort.spec.js @@ -1,11 +1,10 @@ 'use strict' -const assert = require('assert') const config = require('../lib/config') const helper = require('../helper') const redis = config.redis -function setupData (client, done) { +function setupData (client) { client.rpush('y', 'd') client.rpush('y', 'b') client.rpush('y', 'a') @@ -29,7 +28,7 @@ function setupData (client, done) { client.set('p2', 'qux') client.set('p3', 'bux') client.set('p4', 'lux') - client.set('p9', 'tux', done) + return client.set('p9', 'tux') } describe('The \'sort\' method', () => { @@ -37,85 +36,57 @@ describe('The \'sort\' method', () => { describe(`using ${ip}`, () => { let client - beforeEach((done) => { + beforeEach(() => { client = redis.createClient.apply(null, args) - client.once('error', done) - client.once('connect', () => { - client.flushdb() - setupData(client, done) - }) + client.flushdb() + return setupData(client) }) describe('alphabetical', () => { - it('sorts in ascending alphabetical order', (done) => { - client.sort('y', 'asc', 'alpha', (err, sorted) => { - assert.deepEqual(sorted, ['a', 'b', 'c', 'd']) - return done(err) - }) + it('sorts in ascending alphabetical order', () => { + return client.sort('y', 'asc', 'alpha').then(helper.isDeepEqual(['a', 'b', 'c', 'd'])) }) - it('sorts in descending alphabetical order', (done) => { - client.sort('y', 'desc', 'alpha', (err, sorted) => { - assert.deepEqual(sorted, ['d', 'c', 'b', 'a']) - return done(err) - }) + it('sorts in descending alphabetical order', () => { + return client.sort('y', 'desc', 'alpha').then(helper.isDeepEqual(['d', 'c', 'b', 'a'])) }) }) describe('numeric', () => { - it('sorts in ascending numeric order', (done) => { - client.sort('x', 'asc', (err, sorted) => { - assert.deepEqual(sorted, [2, 3, 4, 9]) - return done(err) - }) + it('sorts in ascending numeric order', () => { + return client.sort('x', 'asc').then(helper.isDeepEqual(['2', '3', '4', '9'])) }) - it('sorts in descending numeric order', (done) => { - client.sort('x', 'desc', (err, sorted) => { - assert.deepEqual(sorted, [9, 4, 3, 2]) - return done(err) - }) + it('sorts in descending numeric order', () => { + return client.sort('x', 'desc').then(helper.isDeepEqual(['9', '4', '3', '2'])) }) }) describe('pattern', () => { - it('handles sorting with a pattern', (done) => { - client.sort('x', 'by', 'w*', 'asc', (err, sorted) => { - assert.deepEqual(sorted, [3, 9, 4, 2]) - return done(err) - }) + it('handles sorting with a pattern', () => { + return client.sort('x', 'by', 'w*', 'asc').then(helper.isDeepEqual(['3', '9', '4', '2'])) }) - it('handles sorting with a \'by\' pattern and 1 \'get\' pattern', (done) => { - client.sort('x', 'by', 'w*', 'asc', 'get', 'o*', (err, sorted) => { - assert.deepEqual(sorted, ['foo', 'bar', 'baz', 'buz']) - return done(err) - }) + it('handles sorting with a \'by\' pattern and 1 \'get\' pattern', () => { + return client.sort('x', 'by', 'w*', 'asc', 'get', 'o*') + .then(helper.isDeepEqual(['foo', 'bar', 'baz', 'buz'])) }) - it('handles sorting with a \'by\' pattern and 2 \'get\' patterns', (done) => { - client.sort('x', 'by', 'w*', 'asc', 'get', 'o*', 'get', 'p*', (err, sorted) => { - assert.deepEqual(sorted, ['foo', 'bux', 'bar', 'tux', 'baz', 'lux', 'buz', 'qux']) - return done(err) - }) + it('handles sorting with a \'by\' pattern and 2 \'get\' patterns', () => { + return client.sort('x', 'by', 'w*', 'asc', 'get', 'o*', 'get', 'p*') + .then(helper.isDeepEqual(['foo', 'bux', 'bar', 'tux', 'baz', 'lux', 'buz', 'qux'])) }) - it('handles sorting with a \'by\' pattern and 2 \'get\' patterns with the array syntax', (done) => { - client.sort(['x', 'by', 'w*', 'asc', 'get', 'o*', 'get', 'p*'], (err, sorted) => { - assert.deepEqual(sorted, ['foo', 'bux', 'bar', 'tux', 'baz', 'lux', 'buz', 'qux']) - return done(err) - }) + it('handles sorting with a \'by\' pattern and 2 \'get\' patterns with the array syntax', () => { + return client.sort(['x', 'by', 'w*', 'asc', 'get', 'o*', 'get', 'p*']) + .then(helper.isDeepEqual(['foo', 'bux', 'bar', 'tux', 'baz', 'lux', 'buz', 'qux'])) }) - it('sorting with a \'by\' pattern and 2 \'get\' patterns and stores results', (done) => { - client.sort('x', 'by', 'w*', 'asc', 'get', 'o*', 'get', 'p*', 'store', 'bacon', (err) => { - if (err) return done(err) - }) + it('sorting with a \'by\' pattern and 2 \'get\' patterns and stores results', () => { + client.sort('x', 'by', 'w*', 'asc', 'get', 'o*', 'get', 'p*', 'store', 'bacon') - client.lrange('bacon', 0, -1, (err, values) => { - assert.deepEqual(values, ['foo', 'bux', 'bar', 'tux', 'baz', 'lux', 'buz', 'qux']) - return done(err) - }) + return client.lrange('bacon', 0, -1) + .then(helper.isDeepEqual(['foo', 'bux', 'bar', 'tux', 'baz', 'lux', 'buz', 'qux'])) }) }) diff --git a/test/commands/spop.spec.js b/test/commands/spop.spec.js index ffa0d690ba..fe7afb3bf4 100644 --- a/test/commands/spop.spec.js +++ b/test/commands/spop.spec.js @@ -1,6 +1,5 @@ 'use strict' -const assert = require('assert') const config = require('../lib/config') const helper = require('../helper') const redis = config.redis @@ -10,22 +9,16 @@ describe('The \'spop\' method', () => { describe(`using ${ip}`, () => { let client - beforeEach((done) => { + beforeEach(() => { client = redis.createClient.apply(null, args) - client.once('ready', () => { - client.flushdb(done) - }) + return client.flushdb() }) - it('returns a random element from the set', (done) => { - client.sadd('zzz', 'member0', helper.isNumber(1)) - client.scard('zzz', helper.isNumber(1)) - - client.spop('zzz', (err, value) => { - if (err) return done(err) - assert.strictEqual(value, 'member0') - client.scard('zzz', helper.isNumber(0, done)) - }) + it('returns a random element from the set', () => { + client.sadd('zzz', 'member0').then(helper.isNumber(1)) + client.scard('zzz').then(helper.isNumber(1)) + client.spop('zzz').then(helper.isString('member0')) + return client.scard('zzz').then(helper.isNumber(0)) }) afterEach(() => { diff --git a/test/commands/srem.spec.js b/test/commands/srem.spec.js index a2887e5451..7fa7707d2c 100644 --- a/test/commands/srem.spec.js +++ b/test/commands/srem.spec.js @@ -10,52 +10,47 @@ describe('The \'srem\' method', () => { describe(`using ${ip}`, () => { let client - beforeEach((done) => { + beforeEach(() => { client = redis.createClient.apply(null, args) - client.once('ready', () => { - client.flushdb(done) - }) + return client.flushdb() }) - it('removes a value', (done) => { - client.sadd('set0', 'member0', helper.isNumber(1)) - client.srem('set0', 'member0', helper.isNumber(1)) - client.scard('set0', helper.isNumber(0, done)) + it('removes a value', () => { + client.sadd('set0', 'member0').then(helper.isNumber(1)) + client.srem('set0', 'member0').then(helper.isNumber(1)) + return client.scard('set0').then(helper.isNumber(0)) }) - it('handles attempting to remove a missing value', (done) => { - client.srem('set0', 'member0', helper.isNumber(0, done)) + it('handles attempting to remove a missing value', () => { + return client.srem('set0', 'member0').then(helper.isNumber(0)) }) - it('allows multiple values to be removed', (done) => { - client.sadd('set0', ['member0', 'member1', 'member2'], helper.isNumber(3)) - client.srem('set0', ['member1', 'member2'], helper.isNumber(2)) - client.smembers('set0', (err, res) => { + it('allows multiple values to be removed', () => { + client.sadd('set0', ['member0', 'member1', 'member2']).then(helper.isNumber(3)) + client.srem('set0', ['member1', 'member2']).then(helper.isNumber(2)) + return client.smembers('set0').then((res) => { assert.strictEqual(res.length, 1) assert.ok(~res.indexOf('member0')) - return done(err) }) }) - it('allows multiple values to be removed with sendCommand', (done) => { - client.sendCommand('sadd', ['set0', 'member0', 'member1', 'member2'], helper.isNumber(3)) - client.sendCommand('srem', ['set0', 'member1', 'member2'], helper.isNumber(2)) - client.smembers('set0', (err, res) => { + it('allows multiple values to be removed with sendCommand', () => { + client.sendCommand('sadd', ['set0', 'member0', 'member1', 'member2']).then(helper.isNumber(3)) + client.sendCommand('srem', ['set0', 'member1', 'member2']).then(helper.isNumber(2)) + return client.smembers('set0').then((res) => { assert.strictEqual(res.length, 1) assert.ok(~res.indexOf('member0')) - return done(err) }) }) - it('handles a value missing from the set of values being removed', (done) => { - client.sadd(['set0', 'member0', 'member1', 'member2'], helper.isNumber(3)) - client.srem(['set0', 'member3', 'member4'], helper.isNumber(0)) - client.smembers('set0', (err, res) => { + it('handles a value missing from the set of values being removed', () => { + client.sadd(['set0', 'member0', 'member1', 'member2']).then(helper.isNumber(3)) + client.srem(['set0', 'member3', 'member4']).then(helper.isNumber(0)) + return client.smembers('set0').then((res) => { assert.strictEqual(res.length, 3) assert.ok(~res.indexOf('member0')) assert.ok(~res.indexOf('member1')) assert.ok(~res.indexOf('member2')) - return done(err) }) }) diff --git a/test/commands/sunion.spec.js b/test/commands/sunion.spec.js index 583b157349..4cedd1d48e 100644 --- a/test/commands/sunion.spec.js +++ b/test/commands/sunion.spec.js @@ -10,29 +10,26 @@ describe('The \'sunion\' method', () => { describe(`using ${ip}`, () => { let client - beforeEach((done) => { + beforeEach(() => { client = redis.createClient.apply(null, args) - client.once('ready', () => { - client.flushdb(done) - }) + return client.flushdb() }) - it('returns the union of a group of sets', (done) => { - client.sadd('sa', 'a', helper.isNumber(1)) - client.sadd('sa', 'b', helper.isNumber(1)) - client.sadd('sa', 'c', helper.isNumber(1)) + it('returns the union of a group of sets', () => { + client.sadd('sa', 'a').then(helper.isNumber(1)) + client.sadd('sa', 'b').then(helper.isNumber(1)) + client.sadd('sa', 'c').then(helper.isNumber(1)) - client.sadd('sb', 'b', helper.isNumber(1)) - client.sadd('sb', 'c', helper.isNumber(1)) - client.sadd('sb', 'd', helper.isNumber(1)) + client.sadd('sb', 'b').then(helper.isNumber(1)) + client.sadd('sb', 'c').then(helper.isNumber(1)) + client.sadd('sb', 'd').then(helper.isNumber(1)) - client.sadd('sc', 'c', helper.isNumber(1)) - client.sadd('sc', 'd', helper.isNumber(1)) - client.sadd('sc', 'e', helper.isNumber(1)) + client.sadd('sc', 'c').then(helper.isNumber(1)) + client.sadd('sc', 'd').then(helper.isNumber(1)) + client.sadd('sc', 'e').then(helper.isNumber(1)) - client.sunion('sa', 'sb', 'sc', (err, union) => { - assert.deepEqual(union.sort(), ['a', 'b', 'c', 'd', 'e']) - return done(err) + return client.sunion('sa', 'sb', 'sc').then((union) => { + assert.deepStrictEqual(union.sort(), ['a', 'b', 'c', 'd', 'e']) }) }) diff --git a/test/commands/sunionstore.spec.js b/test/commands/sunionstore.spec.js index d7c910e564..f78f363bb9 100644 --- a/test/commands/sunionstore.spec.js +++ b/test/commands/sunionstore.spec.js @@ -10,32 +10,29 @@ describe('The \'sunionstore\' method', () => { describe(`using ${ip}`, () => { let client - beforeEach((done) => { + beforeEach(() => { client = redis.createClient.apply(null, args) - client.once('ready', () => { - client.flushdb(done) - }) + return client.flushdb() }) - it('stores the result of a union', (done) => { - client.sadd('sa', 'a', helper.isNumber(1)) - client.sadd('sa', 'b', helper.isNumber(1)) - client.sadd('sa', 'c', helper.isNumber(1)) + it('stores the result of a union', () => { + client.sadd('sa', 'a').then(helper.isNumber(1)) + client.sadd('sa', 'b').then(helper.isNumber(1)) + client.sadd('sa', 'c').then(helper.isNumber(1)) - client.sadd('sb', 'b', helper.isNumber(1)) - client.sadd('sb', 'c', helper.isNumber(1)) - client.sadd('sb', 'd', helper.isNumber(1)) + client.sadd('sb', 'b').then(helper.isNumber(1)) + client.sadd('sb', 'c').then(helper.isNumber(1)) + client.sadd('sb', 'd').then(helper.isNumber(1)) - client.sadd('sc', 'c', helper.isNumber(1)) - client.sadd('sc', 'd', helper.isNumber(1)) - client.sadd('sc', 'e', helper.isNumber(1)) + client.sadd('sc', 'c').then(helper.isNumber(1)) + client.sadd('sc', 'd').then(helper.isNumber(1)) + client.sadd('sc', 'e').then(helper.isNumber(1)) - client.sunionstore('foo', 'sa', 'sb', 'sc', helper.isNumber(5)) + client.sunionstore('foo', 'sa', 'sb', 'sc').then(helper.isNumber(5)) - client.smembers('foo', (err, members) => { + return client.smembers('foo').then((members) => { assert.strictEqual(members.length, 5) - assert.deepEqual(members.sort(), ['a', 'b', 'c', 'd', 'e']) - return done(err) + assert.deepStrictEqual(members.sort(), ['a', 'b', 'c', 'd', 'e']) }) }) diff --git a/test/commands/ttl.spec.js b/test/commands/ttl.spec.js index 559228a1c9..3d0a54ba94 100644 --- a/test/commands/ttl.spec.js +++ b/test/commands/ttl.spec.js @@ -10,20 +10,17 @@ describe('The \'ttl\' method', () => { describe(`using ${ip}`, () => { let client - beforeEach((done) => { + beforeEach(() => { client = redis.createClient.apply(null, args) - client.once('ready', () => { - client.flushdb(done) - }) + return client.flushdb() }) - it('returns the current ttl on a key', (done) => { - client.set(['ttl key', 'ttl val'], helper.isString('OK')) - client.expire(['ttl key', '100'], helper.isNumber(1)) - client.ttl(['ttl key'], (err, ttl) => { + it('returns the current ttl on a key', () => { + client.set(['ttl key', 'ttl val']).then(helper.isString('OK')) + client.expire(['ttl key', '100']).then(helper.isNumber(1)) + return client.ttl(['ttl key']).then((ttl) => { assert(ttl >= 99) assert(ttl <= 100) - done(err) }) }) diff --git a/test/commands/type.spec.js b/test/commands/type.spec.js index 3967f70d31..e8fb675e2a 100644 --- a/test/commands/type.spec.js +++ b/test/commands/type.spec.js @@ -9,40 +9,38 @@ describe('The \'type\' method', () => { describe(`using ${ip}`, () => { let client - beforeEach((done) => { + beforeEach(() => { client = redis.createClient.apply(null, args) - client.once('ready', () => { - client.flushdb(done) - }) + return client.flushdb() }) - it('reports string type', (done) => { - client.set(['string key', 'should be a string'], helper.isString('OK')) - client.type(['string key'], helper.isString('string', done)) + it('reports string type', () => { + client.set(['string key', 'should be a string']).then(helper.isString('OK')) + return client.type(['string key']).then(helper.isString('string')) }) - it('reports list type', (done) => { - client.rpush(['list key', 'should be a list'], helper.isNumber(1)) - client.type(['list key'], helper.isString('list', done)) + it('reports list type', () => { + client.rpush(['list key', 'should be a list']).then(helper.isNumber(1)) + return client.type(['list key']).then(helper.isString('list')) }) - it('reports set type', (done) => { - client.sadd(['set key', 'should be a set'], helper.isNumber(1)) - client.type(['set key'], helper.isString('set', done)) + it('reports set type', () => { + client.sadd(['set key', 'should be a set']).then(helper.isNumber(1)) + return client.type(['set key']).then(helper.isString('set')) }) - it('reports zset type', (done) => { - client.zadd('zset key', ['10.0', 'should be a zset'], helper.isNumber(1)) - client.type(['zset key'], helper.isString('zset', done)) + it('reports zset type', () => { + client.zadd('zset key', ['10.0', 'should be a zset']).then(helper.isNumber(1)) + return client.type(['zset key']).then(helper.isString('zset')) }) - it('reports hash type', (done) => { - client.hset('hash key', 'hashtest', 'should be a hash', helper.isNumber(1)) - client.type(['hash key'], helper.isString('hash', done)) + it('reports hash type', () => { + client.hset('hash key', 'hashtest', 'should be a hash').then(helper.isNumber(1)) + return client.type(['hash key']).then(helper.isString('hash')) }) - it('reports none for null key', (done) => { - client.type('not here yet', helper.isString('none', done)) + it('reports none for null key', () => { + return client.type('not here yet').then(helper.isString('none')) }) afterEach(() => { diff --git a/test/commands/watch.spec.js b/test/commands/watch.spec.js index 9bfcc13d29..498e5e0688 100644 --- a/test/commands/watch.spec.js +++ b/test/commands/watch.spec.js @@ -12,37 +12,34 @@ describe('The \'watch\' method', () => { describe(`using ${ip}`, () => { let client - beforeEach((done) => { + beforeEach(() => { client = redis.createClient.apply(null, args) - client.once('ready', () => { - client.flushdb(done) - }) + return client.flushdb() }) afterEach(() => { client.end(true) }) - it('does not execute transaction if watched key was modified prior to execution', (done) => { + it('does not execute transaction if watched key was modified prior to execution', () => { client.watch(watched) client.incr(watched) const multi = client.multi() multi.incr(watched) - multi.exec(helper.isNull(done)) + return multi.exec().then(helper.isNull()) }) - it('successfully modifies other keys independently of transaction', (done) => { + it('successfully modifies other keys independently of transaction', () => { client.set('unwatched', 200) client.set(watched, 0) client.watch(watched) client.incr(watched) - client.multi().incr(watched).exec((err, replies) => { - assert.strictEqual(err, null) + return client.multi().incr(watched).exec().then((replies) => { assert.strictEqual(replies, null, 'Aborted transaction multi-bulk reply should be null.') - client.get('unwatched', helper.isString('200', done)) + return client.get('unwatched').then(helper.isString('200')) }) }) }) diff --git a/test/commands/zadd.spec.js b/test/commands/zadd.spec.js index 045a55a496..227330e03d 100644 --- a/test/commands/zadd.spec.js +++ b/test/commands/zadd.spec.js @@ -10,31 +10,27 @@ describe('The \'zadd\' method', () => { describe(`using ${ip}`, () => { let client - beforeEach((done) => { + beforeEach(() => { client = redis.createClient.apply(null, args) - client.once('ready', () => { - client.flushdb(done) - }) + return client.flushdb() }) - it('reports an error', function (done) { + it('reports an error', function () { if (helper.redisProcess().spawnFailed()) this.skip() - client.zadd('infinity', [+'5t', 'should not be possible'], helper.isError(done)) + return client.zadd('infinity', [+'5t', 'should not be possible']).then(assert, helper.isError()) }) - it('return inf / -inf', function (done) { + it('return inf / -inf', function () { if (helper.redisProcess().spawnFailed()) this.skip() helper.serverVersionAtLeast.call(this, client, [3, 0, 2]) - client.zadd('infinity', [+Infinity, 'should be inf'], helper.isNumber(1)) - client.zadd('infinity', ['inf', 'should be also be inf'], helper.isNumber(1)) - client.zadd('infinity', -Infinity, 'should be negative inf', helper.isNumber(1)) - client.zadd('infinity', [99999999999999999999999, 'should not be inf'], helper.isNumber(1)) - client.zrange('infinity', 0, -1, 'WITHSCORES', (err, res) => { - assert.strictEqual(err, null) + client.zadd('infinity', [+Infinity, 'should be inf']).then(helper.isNumber(1)) + client.zadd('infinity', ['inf', 'should be also be inf']).then(helper.isNumber(1)) + client.zadd('infinity', -Infinity, 'should be negative inf').then(helper.isNumber(1)) + client.zadd('infinity', [99999999999999999999999, 'should not be inf']).then(helper.isNumber(1)) + return client.zrange('infinity', 0, -1, 'WITHSCORES').then((res) => { assert.strictEqual(res[5], 'inf') assert.strictEqual(res[1], '-inf') assert.strictEqual(res[3], '9.9999999999999992e+22') - done() }) }) diff --git a/test/commands/zscan.spec.js b/test/commands/zscan.spec.js index 9799034813..e349f45295 100644 --- a/test/commands/zscan.spec.js +++ b/test/commands/zscan.spec.js @@ -10,14 +10,12 @@ describe('The \'zscan\' method', () => { describe(`using ${ip}`, () => { let client - beforeEach((done) => { + beforeEach(() => { client = redis.createClient.apply(null, args) - client.once('ready', () => { - client.flushdb(done) - }) + return client.flushdb() }) - it('return values', function (done) { + it('return values', function () { if (helper.redisProcess().spawnFailed()) this.skip() helper.serverVersionAtLeast.call(this, client, [2, 8, 0]) const hash = {} @@ -31,11 +29,9 @@ describe('The \'zscan\' method', () => { client.hmset('hash:1', hash) client.sadd('set:1', set) client.zadd(zset) - client.zscan('zset:1', 0, 'MATCH', '*', 'COUNT', 500, (err, res) => { - assert(!err) + return client.zscan('zset:1', 0, 'MATCH', '*', 'COUNT', 500).then((res) => { assert.strictEqual(res.length, 2) assert.strictEqual(res[1].length, 1000) - done() }) }) diff --git a/test/commands/zscore.spec.js b/test/commands/zscore.spec.js index 08abb27739..e2aa95a276 100644 --- a/test/commands/zscore.spec.js +++ b/test/commands/zscore.spec.js @@ -9,16 +9,14 @@ describe('The \'zscore\' method', () => { describe(`using ${ip}`, () => { let client - beforeEach((done) => { + beforeEach(() => { client = redis.createClient.apply(null, args) - client.once('ready', () => { - client.flushdb(done) - }) + return client.flushdb() }) - it('should return the score of member in the sorted set at key', (done) => { + it('should return the score of member in the sorted set at key', () => { client.zadd('myzset', 1, 'one') - client.zscore('myzset', 'one', helper.isString('1', done)) + return client.zscore('myzset', 'one').then(helper.isString('1')) }) afterEach(() => { diff --git a/test/conect.slave.spec.js b/test/conect.slave.spec.js index 2f59d65271..63be8219b0 100644 --- a/test/conect.slave.spec.js +++ b/test/conect.slave.spec.js @@ -20,8 +20,8 @@ if (process.platform !== 'win32') { }) }) - before((done) => { - if (helper.redisProcess().spawnFailed()) return done() + before(() => { + if (helper.redisProcess().spawnFailed()) return master = redis.createClient({ password: 'porkchopsandwiches' }) @@ -32,7 +32,7 @@ if (process.platform !== 'win32') { // Write some data in the redis instance, so there's something to sync multi.set(`foo${i}`, `bar${new Array(500).join(Math.random())}`) } - multi.exec(done) + return multi.exec() }) it('sync process and no master should delay ready being emitted for slaves', function (done) { @@ -50,12 +50,13 @@ if (process.platform !== 'win32') { const tmp = slave.info.bind(slave) let i = 0 - slave.info = function (err, res) { + slave.info = function () { i++ - tmp(err, res) + const promise = tmp() if (!firstInfo || Object.keys(firstInfo).length === 0) { firstInfo = slave.serverInfo } + return promise } slave.on('connect', () => { @@ -68,9 +69,9 @@ if (process.platform !== 'win32') { assert.strictEqual(this.serverInfo.master_link_status, 'up') assert.strictEqual(firstInfo.master_link_status, 'down') assert(i > 1) - this.get('foo300', (err, res) => { + this.get('foo300').then((res) => { assert.strictEqual(res.substr(0, 3), 'bar') - end(err) + end() }) }) @@ -85,10 +86,10 @@ if (process.platform !== 'win32') { const end = helper.callFuncAfter(done, 3) rp.stop(end) slave.end(true) - master.flushdb((err) => { - end(err) + master.flushdb().then(() => { + end() master.end(true) - }) + }).catch(done) helper.stopRedis(() => { helper.startRedis('./conf/redis.conf', end) }) diff --git a/test/connection.spec.js b/test/connection.spec.js index 35c5eeaecd..9b21ebc38e 100644 --- a/test/connection.spec.js +++ b/test/connection.spec.js @@ -17,7 +17,7 @@ describe('connection tests', () => { }) it('unofficially support for a private stream', () => { - // While using a private stream, reconnection and other features are not going to work properly. + // While using a private stream, reconnecting and other features are not going to work properly. // 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() @@ -37,53 +37,49 @@ describe('connection tests', () => { it('calling quit while the connection is down should not end in reconnecting version a', (done) => { let called = 0 client = redis.createClient({ + connectTimeout: 5, port: 9999, retryStrategy (options) { - client.quit((err, res) => { + client.quit().then((res) => { assert.strictEqual(res, 'OK') - assert.strictEqual(err, null) assert.strictEqual(called++, -1) setTimeout(done, 25) - }) + }).catch(helper.fail) assert.strictEqual(called++, 0) return 5 } }) - client.set('foo', 'bar', (err, res) => { + client.set('foo', 'bar').catch((err) => { assert.strictEqual(err.message, 'Stream connection ended and command aborted.') called = -1 }) }) - it('calling quit while the connection is down should not end in reconnecting version b', (done) => { + it('calling quit while the connection is down should not end in reconnecting version b', () => { let called = false client = redis.createClient(9999) - client.set('foo', 'bar', (err, res) => { + client.set('foo', 'bar').catch((err) => { assert.strictEqual(err.message, 'Stream connection ended and command aborted.') called = true }) - client.quit((err, res) => { + return client.quit().then((res) => { assert.strictEqual(res, 'OK') - assert.strictEqual(err, null) assert(called) - done() }) }) - it('calling quit while the connection is down without offline queue should end the connection right away', (done) => { + it('calling quit while the connection is down without offline queue should end the connection right away', () => { let called = false client = redis.createClient(9999, { enableOfflineQueue: false }) - client.set('foo', 'bar', (err, res) => { + client.set('foo', 'bar').catch((err) => { assert.strictEqual(err.message, 'SET can\'t be processed. The connection is not yet established and the offline queue is deactivated.') called = true }) - client.quit((err, res) => { + return client.quit().then((res) => { assert.strictEqual(res, 'OK') - assert.strictEqual(err, null) assert(called) - done() }) }) @@ -93,44 +89,43 @@ describe('connection tests', () => { enableOfflineQueue: false }) client.on('ready', () => { - client.set('foo', 'bar', (err, res) => { - assert.strictEqual(err, null) + client.set('foo', 'bar').then((res) => { assert.strictEqual(res, 'OK') called = true }) - client.quit((err, res) => { + client.quit().then((res) => { assert.strictEqual(res, 'OK') - assert.strictEqual(err, null) assert(called) done() - }) + }).catch(done) }) }) - it('do not quit before connected or a connection issue is detected', (done) => { + it('do not quit before connected or a connection issue is detected', () => { client = redis.createClient() - client.set('foo', 'bar', helper.isString('OK')) - client.quit(done) + return Promise.all([ + client.set('foo', 'bar').then(helper.isString('OK')), + client.quit() + ]) }) - it('quit "succeeds" even if the client connection is closed while doing so', (done) => { + it('quit "succeeds" even if the client connection is closed while doing so', () => { client = redis.createClient() - client.set('foo', 'bar', (err, res) => { - assert.strictEqual(err, null) + return client.set('foo', 'bar').then((res) => { assert.strictEqual(res, 'OK') - client.quit((err, res) => { + const promise = client.quit().then((res) => { assert.strictEqual(res, 'OK') - done(err) }) client.end(true) // Flushing the quit command should result in a success + return promise }) }) it('quit right away if connection drops while quit command is on the fly', (done) => { client = redis.createClient() client.once('ready', () => { - client.set('foo', 'bar', helper.isError()) - client.quit(done) + client.set('foo', 'bar').catch(helper.isError()) + client.quit().then(() => done()) process.nextTick(() => { client.stream.destroy() }) @@ -176,24 +171,11 @@ describe('connection tests', () => { }) }) - it('emits error once if reconnecting after command has been executed but not yet returned without callback', (done) => { - client = redis.createClient.apply(null, args) - - client.on('ready', () => { - client.set('foo', 'bar', (err) => { - assert.strictEqual(err.code, 'UNCERTAIN_STATE') - done() - }) - // Abort connection before the value returned - client.stream.destroy() - }) - }) - it('retryStrategy used to reconnect with individual error', (done) => { client = redis.createClient({ retryStrategy (options) { if (options.totalRetryTime > 150) { - client.set('foo', 'bar', (err, res) => { + client.set('foo', 'bar').then(assert, (err) => { assert.strictEqual(err.message, 'Stream connection ended and command aborted.') assert.strictEqual(err.origin.message, 'Connection timeout') done() @@ -205,13 +187,14 @@ describe('connection tests', () => { }, port: 9999 }) + client.on('error', helper.isError(/Connection timeout/)) }) it('retryStrategy used to reconnect', (done) => { client = redis.createClient({ retryStrategy (options) { if (options.totalRetryTime > 150) { - client.set('foo', 'bar', (err, res) => { + client.set('foo', 'bar').catch((err) => { assert.strictEqual(err.message, 'Stream connection ended and command aborted.') assert.strictEqual(err.code, 'NR_CLOSED') assert.strictEqual(err.origin.code, 'ECONNREFUSED') @@ -223,6 +206,7 @@ describe('connection tests', () => { }, port: 9999 }) + client.on('error', helper.isError(/Redis connection to 127\.0\.0\.1:9999 failed/)) }) it('retryStrategy used to reconnect with defaults', (done) => { @@ -232,7 +216,13 @@ describe('connection tests', () => { redis.debugMode = true client = redis.createClient({ retryStrategy (options) { - client.set('foo', 'bar') + 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 + done() + }) assert(redis.debugMode) return null } @@ -240,13 +230,6 @@ describe('connection tests', () => { setTimeout(() => { client.stream.destroy() }, 50) - client.on('error', (err) => { - assert.strictEqual(err.code, 'NR_CLOSED') - assert.strictEqual(err.message, 'Stream connection ended and command aborted.') - unhookIntercept() - redis.debugMode = false - done() - }) }) }) @@ -255,23 +238,25 @@ describe('connection tests', () => { it.skip('emit an error after the socket timeout exceeded the connectTimeout time', (done) => { const connectTimeout = 500 // in ms client = redis.createClient({ - // Auto detect ipv4 and use non routable ip to trigger the timeout + // Auto detect ipv4 and use non routeable ip to trigger the timeout host: '10.255.255.1', - connectTimeout - }) - process.nextTick(() => { - assert.strictEqual(client.stream.listeners('timeout').length, 1) + connectTimeout, + retryStrategy () { + return 5000 + } }) + process.nextTick(() => assert.strictEqual(client.stream.listeners('timeout').length, 1)) assert.strictEqual(client.address, '10.255.255.1:6379') assert.strictEqual(client.connectionOptions.family, 4) - client.on('reconnecting', (params) => { + client.on('reconnecting', () => { throw new Error('No reconnect, since no connection was ever established') }) const time = Date.now() client.on('error', (err) => { - if (err.code === 'ENETUNREACH') { // The test is run without a internet connection. Pretent it works + console.log('errrrrr', err) + if (err.code === 'ENETUNREACH') { // The test is run without a internet connection. Pretend it works return done() } assert(/Redis connection in broken state: connection timeout.*?exceeded./.test(err.message), err.message) @@ -322,7 +307,7 @@ describe('connection tests', () => { client.once('ready', done) }) - it('connect with path provided in the options object', function (done) { + it('connect with path provided in the options object', function () { if (process.platform === 'win32') { this.skip() } @@ -330,11 +315,7 @@ describe('connection tests', () => { path: '/tmp/redis.sock', connectTimeout: 1000 }) - - const end = helper.callFuncAfter(done, 2) - - client.once('ready', end) - client.set('foo', 'bar', end) + return client.set('foo', 'bar') }) it('connects correctly with args', (done) => { @@ -343,7 +324,7 @@ describe('connection tests', () => { client.once('ready', () => { client.removeListener('error', done) - client.get('recon 1', done) + client.get('recon 1').then(() => done()) }) }) @@ -353,7 +334,7 @@ describe('connection tests', () => { client.once('ready', () => { client.removeListener('error', done) - client.get('recon 1', done) + client.get('recon 1').then(() => done()) }) }) @@ -364,7 +345,7 @@ describe('connection tests', () => { client.once('ready', () => { client.removeListener('error', done) - client.get('recon 1', done) + client.get('recon 1').then(() => done()) }) }) @@ -374,7 +355,7 @@ describe('connection tests', () => { client.once('ready', () => { client.removeListener('error', done) - client.get('recon 1', done) + client.get('recon 1').then(() => done()) }) }) @@ -385,10 +366,9 @@ describe('connection tests', () => { client.once('ready', () => { client.set('foo', 'bar') - client.get('foo', (err, res) => { - assert.strictEqual(res, 'bar') - done(err) - }) + client.get('foo') + .then(helper.isString('bar')) + .then(done) }) }) @@ -400,10 +380,9 @@ describe('connection tests', () => { client.once('ready', () => { client.set('foo', 'bar') - client.get('foo', (err, res) => { - assert.strictEqual(res, 'bar') - done(err) - }) + client.get('foo') + .then(helper.isString('bar')) + .then(done) }) }) @@ -416,22 +395,23 @@ describe('connection tests', () => { client.once('ready', () => { client.set('foo', 'bar') - client.get('foo', (err, res) => { - assert.strictEqual(res, 'bar') - done(err) - }) + client.get('foo') + .then(helper.isString('bar')) + .then(done) }) }) it('connects correctly even if the info command is not present on the redis server', (done) => { client = redis.createClient.apply(null, args) - client.info = function (cb) { + const end = helper.callFuncAfter(done, 2) + client.info = function () { // Mock the result - cb(new Error('ERR unknown command \'info\'')) + end() + return Promise.reject(new Error('ERR unknown command \'info\'')) } client.once('ready', () => { assert.strictEqual(Object.keys(client.serverInfo).length, 0) - done() + end() }) }) @@ -512,18 +492,17 @@ describe('connection tests', () => { const end = helper.callFuncAfter(done, 3) let delayed = false let time - // Mock original function and pretent redis is still loading - client.info = function (cb) { - tmp((err, res) => { + // Mock original function and pretend redis is still loading + client.info = function () { + return tmp().then((res) => { if (!delayed) { - assert(!err) client.serverInfo.loading = 1 client.serverInfo.loading_eta_seconds = 0.5 delayed = true time = Date.now() } end() - cb(err, res) + return res }) } client.on('ready', () => { @@ -542,11 +521,10 @@ describe('connection tests', () => { const end = helper.callFuncAfter(done, 3) let delayed = false let time - // Mock original function and pretent redis is still loading - client.info = function (cb) { - tmp((err, res) => { + // Mock original function and pretend redis is still loading + client.info = function () { + return tmp().then((res) => { if (!delayed) { - assert(!err) // Try reconnecting after one second even if redis tells us the time needed is above one second client.serverInfo.loading = 1 client.serverInfo.loading_eta_seconds = 2.5 @@ -554,7 +532,7 @@ describe('connection tests', () => { time = Date.now() } end() - cb(err, res) + return res }) } client.on('ready', () => { diff --git a/test/custom_errors.spec.js b/test/custom_errors.spec.js index b00817ec2a..c86d66f96c 100644 --- a/test/custom_errors.spec.js +++ b/test/custom_errors.spec.js @@ -43,46 +43,4 @@ describe('errors', () => { assert.strictEqual(e.message, 'foobar') }) }) - - describe('AggregateError', () => { - it('should inherit from Error and AbortError', () => { - const e = new errors.AggregateError({}) - assert.strictEqual(e.message, '') - assert.strictEqual(e.name, 'AggregateError') - assert.strictEqual(Object.keys(e).length, 0) - assert(e instanceof Error) - assert(e instanceof errors.AggregateError) - assert(e instanceof errors.AbortError) - }) - - it('should list options properties but not name and message', () => { - const e = new errors.AggregateError({ - name: 'weird', - message: 'hello world', - property: true - }) - assert.strictEqual(e.message, 'hello world') - assert.strictEqual(e.name, 'weird') - assert.strictEqual(e.property, true) - assert.strictEqual(Object.keys(e).length, 2) - assert(e instanceof Error) - assert(e instanceof errors.AggregateError) - assert(e instanceof errors.AbortError) - assert(delete e.name) - assert.strictEqual(e.name, 'AggregateError') - }) - - it('should change name and message', () => { - const e = new errors.AggregateError({ - message: 'hello world', - property: true - }) - assert.strictEqual(e.name, 'AggregateError') - assert.strictEqual(e.message, 'hello world') - e.name = 'foo' - e.message = 'foobar' - assert.strictEqual(e.name, 'foo') - assert.strictEqual(e.message, 'foobar') - }) - }) }) diff --git a/test/detect_buffers.spec.js b/test/detect_buffers.spec.js index c8a425725e..425c5e0e58 100644 --- a/test/detect_buffers.spec.js +++ b/test/detect_buffers.spec.js @@ -12,16 +12,13 @@ describe('detectBuffers', () => { detectBuffers: true }) - beforeEach((done) => { + beforeEach(() => { client = redis.createClient.apply(null, args) - client.once('error', done) - client.once('connect', () => { - client.flushdb((err) => { - client.hmset('hash key 2', 'key 1', 'val 1', 'key 2', 'val 2') - client.set('string key 1', 'string value') - return done(err) - }) - }) + return Promise.all([ + client.flushdb(), + client.hmset('hash key 2', 'key 1', 'val 1', 'key 2', 'val 2'), + client.set('string key 1', 'string value') + ]) }) afterEach(() => { @@ -30,45 +27,41 @@ describe('detectBuffers', () => { describe('get', () => { describe('first argument is a string', () => { - it('returns a string', (done) => { - client.get('string key 1', helper.isString('string value', done)) + it('returns a string', () => { + return client.get('string key 1').then(helper.isString('string value')) }) - it('returns a string when executed as part of transaction', (done) => { - client.multi().get('string key 1').exec((err, res) => { - helper.isString('string value', done)(err, res[0]) - }) + it('returns a string when executed as part of transaction', () => { + return client.multi().get('string key 1').exec().then(helper.isString('string value')) }) }) describe('first argument is a buffer', () => { - it('returns a buffer', (done) => { - client.get(Buffer.from('string key 1'), (err, reply) => { + it('returns a buffer', () => { + return client.get(Buffer.from('string key 1')).then((reply) => { assert.strictEqual(true, Buffer.isBuffer(reply)) assert.strictEqual('', reply.inspect()) - return done(err) }) }) - it('returns a bufffer when executed as part of transaction', (done) => { - client.multi().get(Buffer.from('string key 1')).exec((err, reply) => { + it('returns a buffer when executed as part of transaction', () => { + return client.multi().get(Buffer.from('string key 1')).exec().then((reply) => { assert.strictEqual(1, reply.length) assert.strictEqual(true, Buffer.isBuffer(reply[0])) assert.strictEqual('', reply[0].inspect()) - return done(err) }) }) }) }) describe('multi.hget', () => { - it('can interleave string and buffer results', (done) => { - client.multi() + it('can interleave string and buffer results', () => { + return client.multi() .hget('hash key 2', 'key 1') .hget(Buffer.from('hash key 2'), 'key 1') .hget('hash key 2', Buffer.from('key 2')) .hget('hash key 2', 'key 2') - .exec((err, reply) => { + .exec().then((reply) => { assert.strictEqual(true, Array.isArray(reply)) assert.strictEqual(4, reply.length) assert.strictEqual('val 1', reply[0]) @@ -77,19 +70,18 @@ describe('detectBuffers', () => { assert.strictEqual(true, Buffer.isBuffer(reply[2])) assert.strictEqual('', reply[2].inspect()) assert.strictEqual('val 2', reply[3]) - return done(err) }) }) }) describe('batch.hget', () => { - it('can interleave string and buffer results', (done) => { - client.batch() + it('can interleave string and buffer results', () => { + return client.batch() .hget('hash key 2', 'key 1') .hget(Buffer.from('hash key 2'), 'key 1') .hget('hash key 2', Buffer.from('key 2')) .hget('hash key 2', 'key 2') - .exec((err, reply) => { + .exec().then((reply) => { assert.strictEqual(true, Array.isArray(reply)) assert.strictEqual(4, reply.length) assert.strictEqual('val 1', reply[0]) @@ -98,71 +90,56 @@ describe('detectBuffers', () => { assert.strictEqual(true, Buffer.isBuffer(reply[2])) assert.strictEqual('', reply[2].inspect()) assert.strictEqual('val 2', reply[3]) - return done(err) }) }) }) describe('hmget', () => { describe('first argument is a string', () => { - it('returns strings for keys requested', (done) => { - client.hmget('hash key 2', 'key 1', 'key 2', (err, reply) => { + it('returns strings for keys requested', () => { + return client.hmget('hash key 2', 'key 1', 'key 2').then((reply) => { assert.strictEqual(true, Array.isArray(reply)) assert.strictEqual(2, reply.length) assert.strictEqual('val 1', reply[0]) assert.strictEqual('val 2', reply[1]) - return done(err) }) }) - it('returns strings for keys requested in transaction', (done) => { - client.multi().hmget('hash key 2', 'key 1', 'key 2').exec((err, reply) => { + it('returns strings for keys requested in transaction', () => { + return client.multi().hmget('hash key 2', 'key 1', 'key 2').exec().then((reply) => { assert.strictEqual(true, Array.isArray(reply)) assert.strictEqual(1, reply.length) assert.strictEqual(2, reply[0].length) assert.strictEqual('val 1', reply[0][0]) assert.strictEqual('val 2', reply[0][1]) - return done(err) }) }) - it('handles array of strings with undefined values (repro #344)', (done) => { - client.hmget('hash key 2', 'key 3', 'key 4', (err, reply) => { - assert.strictEqual(true, Array.isArray(reply)) - assert.strictEqual(2, reply.length) - assert.strictEqual(null, reply[0]) - assert.strictEqual(null, reply[1]) - return done(err) - }) + it('handles array of strings with undefined values (repro #344)', () => { + return client.hmget('hash key 2', 'key 3', 'key 4') + .then(helper.isDeepEqual([null, null])) }) - it('handles array of strings with undefined values in transaction (repro #344)', (done) => { - client.multi().hmget('hash key 2', 'key 3', 'key 4').exec((err, reply) => { - assert.strictEqual(true, Array.isArray(reply)) - assert.strictEqual(1, reply.length) - assert.strictEqual(2, reply[0].length) - assert.strictEqual(null, reply[0][0]) - assert.strictEqual(null, reply[0][1]) - return done(err) - }) + it('handles array of strings with undefined values in transaction (repro #344)', () => { + return client.multi().hmget('hash key 2', 'key 3', 'key 4').exec() + .then(helper.isDeepEqual([[null, null]])) }) }) describe('first argument is a buffer', () => { - it('returns buffers for keys requested', (done) => { - client.hmget(Buffer.from('hash key 2'), 'key 1', 'key 2', (err, reply) => { + it('returns buffers for keys requested', () => { + return client.hmget(Buffer.from('hash key 2'), 'key 1', 'key 2').then((reply) => { assert.strictEqual(true, Array.isArray(reply)) assert.strictEqual(2, reply.length) assert.strictEqual(true, Buffer.isBuffer(reply[0])) assert.strictEqual(true, Buffer.isBuffer(reply[1])) assert.strictEqual('', reply[0].inspect()) assert.strictEqual('', reply[1].inspect()) - return done(err) }) }) - it('returns buffers for keys requested in transaction', (done) => { - client.multi().hmget(Buffer.from('hash key 2'), 'key 1', 'key 2').exec((err, reply) => { + it('returns buffers for keys requested in transaction', () => { + return client.multi().hmget(Buffer.from('hash key 2'), 'key 1', 'key 2').exec().then((reply) => { assert.strictEqual(true, Array.isArray(reply)) assert.strictEqual(1, reply.length) assert.strictEqual(2, reply[0].length) @@ -170,12 +147,11 @@ describe('detectBuffers', () => { assert.strictEqual(true, Buffer.isBuffer(reply[0][1])) assert.strictEqual('', reply[0][0].inspect()) assert.strictEqual('', reply[0][1].inspect()) - return done(err) }) }) - it('returns buffers for keys requested in .batch', (done) => { - client.batch().hmget(Buffer.from('hash key 2'), 'key 1', 'key 2').exec((err, reply) => { + it('returns buffers for keys requested in .batch', () => { + return client.batch().hmget(Buffer.from('hash key 2'), 'key 1', 'key 2').exec().then((reply) => { assert.strictEqual(true, Array.isArray(reply)) assert.strictEqual(1, reply.length) assert.strictEqual(2, reply[0].length) @@ -183,63 +159,49 @@ describe('detectBuffers', () => { assert.strictEqual(true, Buffer.isBuffer(reply[0][1])) assert.strictEqual('', reply[0][0].inspect()) assert.strictEqual('', reply[0][1].inspect()) - return done(err) }) }) }) }) - describe('hgetall', (done) => { + describe('hgetall', () => { describe('first argument is a string', () => { - it('returns string values', (done) => { - client.hgetall('hash key 2', (err, reply) => { - assert.strictEqual('object', typeof reply) - assert.strictEqual(2, Object.keys(reply).length) - assert.strictEqual('val 1', reply['key 1']) - assert.strictEqual('val 2', reply['key 2']) - return done(err) - }) + it('returns string values', () => { + return client.hgetall('hash key 2').then(helper.isDeepEqual({ + 'key 1': 'val 1', + 'key 2': 'val 2' + })) }) - it('returns string values when executed in transaction', (done) => { - client.multi().hgetall('hash key 2').exec((err, reply) => { - assert.strictEqual(1, reply.length) - assert.strictEqual('object', typeof reply[0]) - assert.strictEqual(2, Object.keys(reply[0]).length) - assert.strictEqual('val 1', reply[0]['key 1']) - assert.strictEqual('val 2', reply[0]['key 2']) - return done(err) - }) + it('returns string values when executed in transaction', () => { + return client.multi().hgetall('hash key 2').exec().then(helper.isDeepEqual([{ + 'key 1': 'val 1', + 'key 2': 'val 2' + }])) }) - it('returns string values when executed in .batch', (done) => { - client.batch().hgetall('hash key 2').exec((err, reply) => { - assert.strictEqual(1, reply.length) - assert.strictEqual('object', typeof reply[0]) - assert.strictEqual(2, Object.keys(reply[0]).length) - assert.strictEqual('val 1', reply[0]['key 1']) - assert.strictEqual('val 2', reply[0]['key 2']) - return done(err) - }) + it('returns string values when executed in .batch', () => { + return client.batch().hgetall('hash key 2').exec().then(helper.isDeepEqual([{ + 'key 1': 'val 1', + 'key 2': 'val 2' + }])) }) }) describe('first argument is a buffer', () => { - it('returns buffer values', (done) => { - client.hgetall(Buffer.from('hash key 2'), (err, reply) => { - assert.strictEqual(null, err) + it('returns buffer values', () => { + return client.hgetall(Buffer.from('hash key 2')).then((reply) => { assert.strictEqual('object', typeof reply) assert.strictEqual(2, Object.keys(reply).length) assert.strictEqual(true, Buffer.isBuffer(reply['key 1'])) assert.strictEqual(true, Buffer.isBuffer(reply['key 2'])) assert.strictEqual('', reply['key 1'].inspect()) assert.strictEqual('', reply['key 2'].inspect()) - return done(err) }) }) - it('returns buffer values when executed in transaction', (done) => { - client.multi().hgetall(Buffer.from('hash key 2')).exec((err, reply) => { + it('returns buffer values when executed in transaction', () => { + return client.multi().hgetall(Buffer.from('hash key 2')).exec().then((reply) => { assert.strictEqual(1, reply.length) assert.strictEqual('object', typeof reply[0]) assert.strictEqual(2, Object.keys(reply[0]).length) @@ -247,12 +209,11 @@ describe('detectBuffers', () => { assert.strictEqual(true, Buffer.isBuffer(reply[0]['key 2'])) assert.strictEqual('', reply[0]['key 1'].inspect()) assert.strictEqual('', reply[0]['key 2'].inspect()) - return done(err) }) }) - it('returns buffer values when executed in .batch', (done) => { - client.batch().hgetall(Buffer.from('hash key 2')).exec((err, reply) => { + it('returns buffer values when executed in .batch', () => { + return client.batch().hgetall(Buffer.from('hash key 2')).exec().then((reply) => { assert.strictEqual(1, reply.length) assert.strictEqual('object', typeof reply[0]) assert.strictEqual(2, Object.keys(reply[0]).length) @@ -260,7 +221,6 @@ describe('detectBuffers', () => { assert.strictEqual(true, Buffer.isBuffer(reply[0]['key 2'])) assert.strictEqual('', reply[0]['key 1'].inspect()) assert.strictEqual('', reply[0]['key 2'].inspect()) - return done(err) }) }) }) diff --git a/test/good_traces.spec.js b/test/good_traces.spec.js index 97c110bb5b..be65f041b2 100644 --- a/test/good_traces.spec.js +++ b/test/good_traces.spec.js @@ -2,6 +2,7 @@ const assert = require('assert') const config = require('./lib/config') +const helper = require('./helper') const fork = require('child_process').fork const redis = config.redis @@ -46,13 +47,13 @@ describe('stack traces', () => { }) // This is always going to return good stack traces - it('should always return good stack traces for rejected offline commands', (done) => { + it('should always return good stack traces for rejected offline commands', () => { const client = redis.createClient({ enableOfflineQueue: false }) - client.set('foo', (err, res) => { + return client.set('foo').then(helper.fail).catch((err) => { assert(/good_traces.spec.js/.test(err.stack)) - client.quit(done) + return client.quit() }) }) }) diff --git a/test/helper.js b/test/helper.js index edfba90299..3165e96bf5 100644 --- a/test/helper.js +++ b/test/helper.js @@ -8,6 +8,14 @@ const StunnelProcess = require('./lib/stunnel-process') let rp let stunnelProcess +process.on('unhandledRejection', (err, promise) => { + console.log(err) + rp.stop(() => { + console.log('PROCESS ENDING DUE TO AN UNHANDLED REJECTION') + process.exit(1) + }) +}) + function startRedis (conf, done, port) { RedisProcess.start((err, _rp) => { rp = _rp @@ -45,6 +53,10 @@ function toString (res) { if (Array.isArray(res)) { return res.map(toString) } + // Stringify all values as well + if (typeof res === 'object' && res !== null) { + Object.keys(res).map((key) => (res[key] = toString(res[key]))) + } return res } @@ -69,92 +81,58 @@ module.exports = { return done(err) }, path.resolve(__dirname, './conf')) }, - isNumber (expected, done) { - return function (err, results) { - assert.strictEqual(err, null, `expected ${expected}, got error: ${err}`) + isNumber (expected) { + return function (results) { results = arrayHelper(results) - assert.strictEqual(results, expected, `${expected } !== ${results}`) + assert.strictEqual(results, expected, `${expected} !== ${results}`) assert.strictEqual(typeof results, 'number', `expected a number, got ${typeof results}`) - if (done) done() } }, - isString (str, done) { + isString (str) { str = `${str}` // Make sure it's a string - return function (err, results) { - assert.strictEqual(err, null, `expected string '${str}', got error: ${err}`) + return function (results) { results = arrayHelper(results) results = toString(results) - assert.strictEqual(results, str, `${str } does not match ${results}`) - if (done) done() + assert.strictEqual(results, str, `${str} does not match ${results}`) } }, - isNull (done) { - return function (err, results) { - assert.strictEqual(err, null, `expected null, got error: ${err}`) + isNull () { + return function (results) { results = arrayHelper(results) - assert.strictEqual(results, null, `${results } is not null`) - if (done) done() + assert.strictEqual(results, null, `${results} is not null`) } }, - isUndefined (done) { - return function (err, results) { - assert.strictEqual(err, null, `expected null, got error: ${err}`) + isUndefined () { + return function (results) { results = arrayHelper(results) - assert.strictEqual(results, undefined, `${results } is not undefined`) - if (done) done() + assert.strictEqual(results, undefined, `${results} is not undefined`) } }, - isError (done) { - return function (err, results) { - assert(err instanceof Error, 'err is not instance of \'Error\', but an error is expected here.') - if (done) done() - } - }, - isNotError (done) { - return function (err, results) { - assert.strictEqual(err, null, `expected success, got an error: ${err}`) - if (done) done() - } - }, - isDeepEqual (args, done) { + isError (regex) { return function (err, res) { - assert.strictEqual(err, null, `expected null, got error: ${err}`) + assert.strictEqual(res, undefined, 'There should be an error, no result!') + assert(err instanceof Error, 'err is not instance of \'Error\', but an error is expected here.') + if (regex) assert(regex.test(err.message)) + } + }, + isDeepEqual (args) { + return function (res) { res = toString(res) assert.deepStrictEqual(res, args) - if (done) done() } }, - isType: { - number (done) { - return function (err, results) { - assert.strictEqual(err, null, `expected any number, got error: ${err}`) - assert.strictEqual(typeof results, 'number', `${results } is not a number`) - if (done) done() - } - }, - string (done) { - return function (err, results) { - assert.strictEqual(err, null, `expected any string, got error: ${err}`) - assert.strictEqual(typeof results, 'string', `${results } is not a string`) - if (done) done() - } - }, - positiveNumber (done) { - return function (err, results) { - assert.strictEqual(err, null, `expected positive number, got error: ${err}`) - assert(results > 0, `${results } is not a positive number`) - if (done) done() - } - } - }, - match (pattern, done) { - return function (err, results) { - assert.strictEqual(err, null, `expected ${pattern.toString()}, got error: ${err}`) + match (pattern) { + return function (results) { results = arrayHelper(results) assert(pattern.test(results), `expected string '${results}' to match ${pattern.toString()}`) - if (done) done() } }, + fail (err) { + err = err instanceof Error + ? err + : new Error('This should not be reachable') + throw err + }, serverVersionAtLeast (connection, desiredVersion) { // Wait until a connection has established (otherwise a timeout is going to be triggered at some point) if (Object.keys(connection.serverInfo).length === 0) { @@ -212,10 +190,7 @@ module.exports = { }, callFuncAfter (func, max) { let i = 0 - return function (err) { - if (err) { - throw err - } + return function () { i++ if (i >= max) { func() diff --git a/test/lib/config.js b/test/lib/config.js index cc02a80aee..94c861b47d 100644 --- a/test/lib/config.js +++ b/test/lib/config.js @@ -3,11 +3,6 @@ // helpers for configuring a redis client in // its various modes, ipV6, ipV4, socket. const redis = require('../../index') -const bluebird = require('bluebird') - -// Promisify everything -bluebird.promisifyAll(redis.RedisClient.prototype) -bluebird.promisifyAll(redis.Multi.prototype) const config = { redis, diff --git a/test/lib/good-traces.js b/test/lib/good-traces.js index 5177fbc20e..348b83c23d 100644 --- a/test/lib/good-traces.js +++ b/test/lib/good-traces.js @@ -6,9 +6,9 @@ const redis = require('../../index') const client = redis.createClient() // Both error cases would normally return bad stack traces -client.set('foo', (err, res) => { +client.set('foo').catch((err) => { assert(/good-traces.js:9:8/.test(err.stack)) - client.set('foo', 'bar', (err, res) => { + client.set('foo', 'bar').catch((err) => { assert(/good-traces.js:11:10/.test(err.stack)) client.quit(() => { process.exit(0) diff --git a/test/lib/redis-process.js b/test/lib/redis-process.js index edaaecb8e6..6bd5c6c6d8 100644 --- a/test/lib/redis-process.js +++ b/test/lib/redis-process.js @@ -6,7 +6,6 @@ const fs = require('fs') const path = require('path') const spawn = require('win-spawn') const tcpPortUsed = require('tcp-port-used') -const bluebird = require('bluebird') // wait for redis to be listening in // all three modes (ipv4, ipv6, socket). @@ -24,26 +23,28 @@ function waitForRedis (available, cb, port) { const id = setInterval(() => { if (running) return running = true - bluebird.join( + Promise.all([ tcpPortUsed.check(port, '127.0.0.1'), - tcpPortUsed.check(port, '::1'), - (ipV4, ipV6) => { - if (ipV6 === available && ipV4 === available) { - if (fs.existsSync(socket) === available) { - clearInterval(id) - return cb() - } - // The same message applies for can't stop but we ignore that case - throw new Error(`Port ${port} is already in use. Tests can't start.\n`) + tcpPortUsed.check(port, '::1') + ]).then((ip) => { + const ipV4 = ip[0] + const ipV6 = ip[1] + if (ipV6 === available && ipV4 === available) { + if (fs.existsSync(socket) === available) { + clearInterval(id) + return cb() } - if (Date.now() - time > 6000) { - throw new Error(`Redis could not start on port ${port || config.PORT}\n`) - } - running = false - }).catch((err) => { - console.error(`\x1b[31m${err.stack}\x1b[0m\n`) - process.exit(1) - }) + // The same message applies for can't stop but we ignore that case + throw new Error(`Port ${port} is already in use. Tests can't start.\n`) + } + if (Date.now() - time > 6000) { + throw new Error(`Redis could not start on port ${port || config.PORT}\n`) + } + running = false + }).catch((err) => { + console.error(`\x1b[31m${err.stack}\x1b[0m\n`) + process.exit(1) + }) }, 100) } diff --git a/test/multi.spec.js b/test/multi.spec.js index ee165c62c4..cbeaebf1d3 100644 --- a/test/multi.spec.js +++ b/test/multi.spec.js @@ -73,13 +73,7 @@ describe('The \'multi\' method', () => { for (i = 0; i < 100; i++) { multi.hset('SOME_KEY', `SOME_FIELD${i}`, buffer) } - multi.exec((err, res) => { - if (err) { - done(err) - return - } - run() - }) + multi.exec().then(run) }) } run() @@ -87,7 +81,7 @@ describe('The \'multi\' method', () => { }) describe('pipeline limit', () => { - it('do not exceed maximum string size', function (done) { + it('do not exceed maximum string size', function () { this.timeout(process.platform !== 'win32' ? 10000 : 35000) // Windows tests are horribly slow // Triggers a RangeError: Invalid string length if not handled properly client = redis.createClient() @@ -97,40 +91,24 @@ describe('The \'multi\' method', () => { i -= 10230 multi.set(`foo${i}`, `bar${new Array(1024).join('1234567890')}`) } - client.on('ready', () => { - multi.exec((err, res) => { - assert.strictEqual(err, null) - assert.strictEqual(res.length, 26241) - }) - client.flushdb(done) + multi.exec().then((res) => { + assert.strictEqual(res.length, 26241) }) + return client.flushdb() }) }) helper.allTests((ip, args) => { describe(`using ${ip}`, () => { describe('when not connected', () => { - beforeEach((done) => { - const end = helper.callFuncAfter(done, 2) + beforeEach(() => { client = redis.createClient.apply(null, args) - client.once('ready', () => { - client.quit(end) - }) - client.once('end', end) + return client.quit() }) - it('reports an error', (done) => { + it('reports an error', () => { const multi = client.multi() - multi.exec((err, res) => { - assert(err.message.match(/The connection is already closed/)) - done() - }) - }) - - it('reports an error if promisified', () => { - return client.multi().execAsync().catch((err) => { - assert(err.message.match(/The connection is already closed/)) - }) + return multi.exec().then(helper.fail, helper.isError(/The connection is already closed/)) }) }) @@ -140,26 +118,23 @@ describe('The \'multi\' method', () => { }) describe('monitor and transactions do not work together', () => { - it('results in a execabort', (done) => { + it('results in a execabort', () => { // Check that transactions in combination with monitor result in an error - client.monitor((e) => { - client.on('error', (err) => { - assert.strictEqual(err.code, 'EXECABORT') - client.end(false) - done() - }) + return client.monitor().then(() => { const multi = client.multi() multi.set('hello', 'world') - multi.exec() + return multi.exec().then(assert, (err) => { + assert.strictEqual(err.code, 'EXECABORT') + client.end(false) + }) }) }) - it('results in a execabort #2', (done) => { + it('results in a execabort #2', () => { // Check that using monitor with a transactions results in an error - client.multi().set('foo', 'bar').monitor().exec((err, res) => { + return client.multi().set('foo', 'bar').monitor().exec().then(assert, (err) => { assert.strictEqual(err.code, 'EXECABORT') client.end(false) - done() }) }) @@ -176,24 +151,24 @@ describe('The \'multi\' method', () => { client.sendCommand('multi') client.sendCommand('set', ['foo', 'bar']) client.sendCommand('get', ['foo']) - client.sendCommand('exec', (err, res) => { - assert.strictEqual(err, null) + client.sendCommand('exec').then((res) => { // res[0] is going to be the monitor result of set // res[1] is going to be the result of the set command assert(utils.monitorRegex.test(res[0])) - assert.strictEqual(res[1], 'OK') + assert.strictEqual(res[1].toString(), 'OK') assert.strictEqual(res.length, 2) client.end(false) }) }) }) - it('executes a pipelined multi properly in combination with the offline queue', (done) => { + it('executes a pipelined multi properly in combination with the offline queue', () => { const multi1 = client.multi() multi1.set('m1', '123') multi1.get('m1') - multi1.exec(done) + const promise = multi1.exec() assert.strictEqual(client.offlineQueue.length, 4) + return promise }) it('executes a pipelined multi properly after a reconnect in combination with the offline queue', (done) => { @@ -203,17 +178,13 @@ describe('The \'multi\' method', () => { const multi1 = client.multi() multi1.set('m1', '123') multi1.get('m1') - multi1.exec((err, res) => { - assert(!err) - called = true - }) + multi1.exec().then(() => (called = true)) client.once('ready', () => { const multi1 = client.multi() multi1.set('m2', '456') multi1.get('m2') - multi1.exec((err, res) => { + multi1.exec().then((res) => { assert(called) - assert(!err) assert.strictEqual(res[1], '456') done() }) @@ -223,7 +194,7 @@ describe('The \'multi\' method', () => { }) describe('when connection is broken', () => { - it.skip('return an error even if connection is in broken mode if callback is present', (done) => { + it.skip('return an error even if connection is in broken mode', (done) => { client = redis.createClient({ host: 'somewhere', port: 6379, @@ -236,59 +207,32 @@ describe('The \'multi\' method', () => { } }) - client.multi([['set', 'foo', 'bar'], ['get', 'foo']]).exec((err, res) => { + client.multi([['set', 'foo', 'bar'], ['get', 'foo']]).exec().catch((err) => { // assert(/Redis connection in broken state/.test(err.message)); assert.strictEqual(err.errors.length, 2) assert.strictEqual(err.errors[0].args.length, 2) }) }) - - it.skip('does not emit an error twice if connection is in broken mode with no callback', (done) => { - client = redis.createClient({ - host: 'somewhere', - port: 6379, - retryStrategy () {} - }) - - client.on('error', (err) => { - // Results in multiple done calls if test fails - if (/Redis connection in broken state/.test(err.message)) { - done() - } - }) - - client.multi([['set', 'foo', 'bar'], ['get', 'foo']]).exec() - }) }) describe('when ready', () => { - beforeEach((done) => { + beforeEach(() => { client = redis.createClient.apply(null, args) - client.once('ready', () => { - client.flushdb((err) => { - return done(err) - }) - }) + return client.flushdb() }) - it('returns an empty result array', (done) => { + it('returns an empty result array', () => { const multi = client.multi() - multi.exec((err, res) => { - assert.strictEqual(err, null) - assert.strictEqual(res.length, 0) - done() - }) + return multi.exec().then(helper.isDeepEqual([])) }) - it('runs normal calls in-between multis', (done) => { + it('runs normal calls in-between multis', () => { const multi1 = client.multi() multi1.set('m1', '123') - client.set('m2', '456', done) + return client.set('m2', '456') }) - it('runs simultaneous multis with the same client', (done) => { - const end = helper.callFuncAfter(done, 2) - + it('runs simultaneous multis with the same client', () => { const multi1 = client.multi() multi1.set('m1', '123') multi1.get('m1') @@ -297,12 +241,13 @@ describe('The \'multi\' method', () => { multi2.set('m2', '456') multi2.get('m2') - multi1.exec(end) - multi2.exec(helper.isDeepEqual(['OK', '456'], end)) + return Promise.all([ + multi1.exec(), + multi2.exec().then(helper.isDeepEqual(['OK', '456'])) + ]) }) - it('runs simultaneous multis with the same client version 2', (done) => { - const end = helper.callFuncAfter(done, 2) + it('runs simultaneous multis with the same client version 2', () => { const multi2 = client.multi() const multi1 = client.multi() @@ -312,46 +257,44 @@ describe('The \'multi\' method', () => { multi2.get('m1') multi2.ping() - multi1.exec(end) - multi2.exec(helper.isDeepEqual(['OK', '123', 'PONG'], end)) + return Promise.all([ + multi1.exec(), + multi2.exec().then(helper.isDeepEqual(['OK', '123', 'PONG'])) + ]) }) - it('roles back a transaction when one command in a sequence of commands fails', (done) => { + it('roles back a transaction when one command in a sequence of commands fails', () => { // Provoke an error at queue time const multi1 = client.multi() - multi1.mset('multifoo', '10', 'multibar', '20', helper.isString('OK')) + multi1.mset('multifoo', '10', 'multibar', '20') - multi1.set('foo2', helper.isError()) + multi1.set('foo2') multi1.incr('multifoo') multi1.incr('multibar') - multi1.exec(() => { + return multi1.exec().then(helper.fail, () => { // Redis 2.6.5+ will abort transactions with errors // see: http://redis.io/topics/transactions - const multibarExpected = 1 - const multifooExpected = 1 // Confirm that the previous command, while containing an error, still worked. const multi2 = client.multi() - multi2.incr('multibar', helper.isNumber(multibarExpected)) - multi2.incr('multifoo', helper.isNumber(multifooExpected)) - multi2.exec(helper.isDeepEqual([multibarExpected, multibarExpected], done)) + multi2.incr('multibar') + multi2.incr('multifoo') + return multi2.exec().then(helper.isDeepEqual([1, 1])) }) }) - it('roles back a transaction when one command in an array of commands fails', (done) => { + it('roles back a transaction when one command in an array of commands fails', () => { // test nested multi-bulk replies - client.multi([ - ['mget', 'multifoo', 'multibar', helper.isDeepEqual([null, null])], - ['set', 'foo2', helper.isError()], + return client.multi([ + ['mget', 'multifoo', 'multibar'], + ['set', 'foo2'], ['incr', 'multifoo'], ['incr', 'multibar'] - ]).exec((err, replies) => { + ]).exec().then(assert, (err) => { assert.notEqual(err, null) - assert.strictEqual(replies, undefined) - return done() }) }) - it('handles multiple operations being applied to a set', (done) => { + it('handles multiple operations being applied to a set', () => { client.sadd('some set', 'mem 1') client.sadd(['some set', 'mem 2']) client.sadd('some set', 'mem 3') @@ -359,60 +302,50 @@ describe('The \'multi\' method', () => { // make sure empty mb reply works client.del('some missing set') - client.smembers('some missing set', helper.isDeepEqual([])) + client.smembers('some missing set').then(helper.isDeepEqual([])) // test nested multi-bulk replies with empty mb elements. - client.multi([ + return client.multi([ ['smembers', ['some set']], ['del', 'some set'], ['smembers', 'some set'] ]) .scard('some set') - .exec((err, res) => { - assert.strictEqual(err, null) + .exec().then((res) => { assert.strictEqual(res[0].length, 4) assert.strictEqual(res[1], 1) assert.deepStrictEqual(res[2], []) assert.strictEqual(res[3], 0) - done() }) }) - it('allows multiple operations to be performed using constructor with all kinds of syntax', (done) => { + it('allows multiple operations to be performed using constructor with all kinds of syntax', () => { const now = Date.now() const arr = ['multihmset', 'multibar', 'multibaz'] const arr2 = ['some manner of key', 'otherTypes'] const arr3 = [5768, 'multibarx', 'multifoox'] - const arr4 = ['mset', [578, 'multibar'], helper.isString('OK')] - let called = false - client.multi([ + const arr4 = ['mset', [578, 'multibar']] + return client.multi([ arr4, - [['mset', 'multifoo2', 'multibar2', 'multifoo3', 'multibar3'], helper.isString('OK')], + [['mset', 'multifoo2', 'multibar2', 'multifoo3', 'multibar3']], ['hmset', arr], - [['hmset', 'multihmset2', 'multibar2', 'multifoo3', 'multibar3', 'test'], helper.isString('OK')], - ['hmset', ['multihmset', 'multibar', 'multifoo'], helper.isString('OK')], - ['hmset', arr3, helper.isString('OK')], + [['hmset', 'multihmset2', 'multibar2', 'multifoo3', 'multibar3', 'test']], + ['hmset', ['multihmset', 'multibar', 'multifoo']], + ['hmset', arr3], ['hmset', now, {123456789: 'abcdefghij', 'some manner of key': 'a type of value', 'otherTypes': 555}], - ['hmset', 'key2', {'0123456789': 'abcdefghij', 'some manner of key': 'a type of value', 'otherTypes': 999}, helper.isString('OK')], - ['hmset', 'multihmset', ['multibar', 'multibaz'], undefined], // undefined is used as a explicit not set callback variable - ['hmset', 'multihmset', ['multibar', 'multibaz'], helper.isString('OK')] + ['hmset', 'key2', {'0123456789': 'abcdefghij', 'some manner of key': 'a type of value', 'otherTypes': 999}], + ['hmset', 'multihmset', ['multibar', 'multibaz']], + ['hmset', 'multihmset', ['multibar', 'multibaz']] ]) .hmget(now, 123456789, 'otherTypes') - .hmget('key2', arr2, () => {}) + .hmget('key2', arr2) .hmget(['multihmset2', 'some manner of key', 'multibar3']) - .mget('multifoo2', ['multifoo3', 'multifoo'], (err, res) => { - assert.strictEqual(err, null) - assert(res[0], 'multifoo3') - assert(res[1], 'multifoo') - called = true - }) - .exec((err, replies) => { - assert(called) + .mget('multifoo2', ['multifoo3', 'multifoo']) + .exec().then((replies) => { assert.strictEqual(arr.length, 3) assert.strictEqual(arr2.length, 2) assert.strictEqual(arr3.length, 3) - assert.strictEqual(arr4.length, 3) - assert.strictEqual(null, err) + assert.strictEqual(arr4.length, 2) assert.strictEqual(replies[10][1], '555') assert.strictEqual(replies[11][0], 'a type of value') assert.strictEqual(replies[12][0], null) @@ -420,166 +353,81 @@ describe('The \'multi\' method', () => { assert.strictEqual(replies[13][0], 'multibar2') assert.strictEqual(replies[13].length, 3) assert.strictEqual(replies.length, 14) - return done() }) }) - it('converts a non string key to a string', (done) => { + it('converts a non string key to a string', () => { // TODO: Converting the key might change soon again. - client.multi().hmset(true, { + return client.multi().hmset(true, { test: 123, bar: 'baz' - }).exec(done) + }).exec() }) - it('runs a multi without any further commands', (done) => { - client.multi().exec((err, res) => { - assert.strictEqual(err, null) - assert.strictEqual(res.length, 0) - done() - }) + it('runs a multi without any further commands', () => { + return client.multi().exec().then(helper.isDeepEqual([])) }) - it('allows multiple operations to be performed using a chaining API', (done) => { - client.multi() + it('allows multiple operations to be performed using a chaining API', () => { + return client.multi() .mset('some', '10', 'keys', '20') .incr('some') .incr('keys') .mget('some', ['keys']) - .exec(helper.isDeepEqual(['OK', 11, 21, ['11', '21']], done)) + .exec().then(helper.isDeepEqual(['OK', 11, 21, ['11', '21']])) }) - it('allows multiple commands to work the same as normal to be performed using a chaining API', (done) => { - client.multi() - .mset(['some', '10', 'keys', '20']) - .incr('some', helper.isNumber(11)) - .incr(['keys'], helper.isNumber(21)) - .mget('some', 'keys') - .exec(helper.isDeepEqual(['OK', 11, 21, ['11', '21']], done)) - }) - - it('allows multiple commands to work the same as normal to be performed using a chaining API promisified', () => { - return client.multi() - .mset(['some', '10', 'keys', '20']) - .incr('some', helper.isNumber(11)) - .incr(['keys'], helper.isNumber(21)) - .mget('some', 'keys') - .execAsync() - .then((replies) => { - assert.strictEqual('OK', replies[0]) - assert.strictEqual(11, replies[1]) - assert.strictEqual(21, replies[2]) - assert.strictEqual('11', replies[3][0].toString()) - assert.strictEqual('21', replies[3][1].toString()) - }) - }) - - it('allows an array to be provided indicating multiple operations to perform', (done) => { + it('allows an array to be provided indicating multiple operations to perform', () => { // test nested multi-bulk replies with nulls. - client.multi([ + return client.multi([ ['mget', ['multifoo', 'some', 'random value', 'keys']], ['incr', 'multifoo'] - ]).exec(helper.isDeepEqual([[null, null, null, null], 1], done)) + ]).exec().then(helper.isDeepEqual([[null, null, null, null], 1])) }) - it('allows multiple operations to be performed on a hash', (done) => { - client.multi() + it('allows multiple operations to be performed on a hash', () => { + return client.multi() .hmset('multihash', 'a', 'foo', 'b', 1) .hmset('multihash', { extra: 'fancy', things: 'here' }) .hgetall('multihash') - .exec((err, replies) => { - assert.strictEqual(null, err) + .exec().then((replies) => { assert.strictEqual('OK', replies[0]) assert.strictEqual(Object.keys(replies[2]).length, 4) assert.strictEqual('foo', replies[2].a) assert.strictEqual('1', replies[2].b) assert.strictEqual('fancy', replies[2].extra) assert.strictEqual('here', replies[2].things) - return done() }) }) - it('reports EXECABORT exceptions when they occur (while queueing)', (done) => { - client.multi().config('bar').set('foo').set('bar').exec((err, reply) => { + it('reports EXECABORT exceptions when they occur (while queueing)', () => { + return client.multi().config('bar').set('foo').set('bar').exec().then(assert, (err) => { assert.strictEqual(err.code, 'EXECABORT') - assert.strictEqual(reply, undefined, 'The reply should have been discarded') assert(err.message.match(/^EXECABORT/), 'Error message should begin with EXECABORT') assert.strictEqual(err.errors.length, 2, 'err.errors should have 2 items') assert.strictEqual(err.errors[0].command, 'SET') assert.strictEqual(err.errors[0].code, 'ERR') assert.strictEqual(err.errors[0].position, 1) - assert(/^ERR/.test(err.errors[0].message), 'Actuall error message should begin with ERR') - return done() + assert(/^ERR/.test(err.errors[0].message), 'Actual error message should begin with ERR') }) }) - it('reports multiple exceptions when they occur (while EXEC is running)', (done) => { - client.multi().config('bar').debug('foo').eval('return {err=\'this is an error\'}', 0).exec((err, reply) => { - assert.strictEqual(err, null) - assert.strictEqual(reply.length, 3) - assert.strictEqual(reply[0].code, 'ERR') - assert.strictEqual(reply[0].command, 'CONFIG') - assert.strictEqual(reply[2].code, undefined) - assert.strictEqual(reply[2].command, 'EVAL') - assert(/^this is an error/.test(reply[2].message)) - assert(/^ERR/.test(reply[0].message), 'Error message should begin with ERR') - assert(/^ERR/.test(reply[1].message), 'Error message should begin with ERR') - return done() + it('reports multiple exceptions when they occur (while EXEC is running)', () => { + return client.multi().config('bar').debug('foo').eval('return {err=\'this is an error\'}', 0).exec().then(assert, (err) => { + assert.strictEqual(err.replies.length, 3) + assert.strictEqual(err.replies[0].code, 'ERR') + assert.strictEqual(err.replies[0].command, 'CONFIG') + assert.strictEqual(err.replies[2].code, undefined) + assert.strictEqual(err.replies[2].command, 'EVAL') + assert(/^this is an error/.test(err.replies[2].message)) + assert(/^ERR/.test(err.replies[0].message), 'Error message should begin with ERR') + assert(/^ERR/.test(err.replies[1].message), 'Error message should begin with ERR') }) }) - it('reports multiple exceptions when they occur (while EXEC is running) promisified', () => { - return client.multi().config('bar').debug('foo').eval('return {err=\'this is an error\'}', 0).execAsync().then((reply) => { - assert.strictEqual(reply.length, 3) - assert.strictEqual(reply[0].code, 'ERR') - assert.strictEqual(reply[0].command, 'CONFIG') - assert.strictEqual(reply[2].code, undefined) - assert.strictEqual(reply[2].command, 'EVAL') - assert(/^this is an error/.test(reply[2].message)) - assert(/^ERR/.test(reply[0].message), 'Error message should begin with ERR') - assert(/^ERR/.test(reply[1].message), 'Error message should begin with ERR') - }) - }) - - it('reports multiple exceptions when they occur (while EXEC is running) and calls cb', (done) => { - const multi = client.multi() - multi.config('bar', helper.isError()) - multi.set('foo', 'bar', helper.isString('OK')) - multi.debug('foo').exec((err, reply) => { - assert.strictEqual(err, null) - assert.strictEqual(reply.length, 3) - assert.strictEqual(reply[0].code, 'ERR') - assert(/^ERR/.test(reply[0].message), 'Error message should begin with ERR') - assert(/^ERR/.test(reply[2].message), 'Error message should begin with ERR') - assert.strictEqual(reply[1], 'OK') - client.get('foo', helper.isString('bar', done)) - }) - }) - - it('emits an error if no callback has been provided and execabort error occured', (done) => { - const multi = client.multi() - multi.config('bar') - multi.set('foo') - multi.exec() - - client.on('error', (err) => { - assert.strictEqual(err.code, 'EXECABORT') - done() - }) - }) - - it('should work without any callback', (done) => { - const multi = client.multi() - multi.set('baz', 'binary') - multi.set('foo', 'bar') - multi.exec() - - client.get('foo', helper.isString('bar', done)) - }) - it('should not use a transaction with execAtomic if no command is used', () => { const multi = client.multi() let test = false @@ -601,7 +449,7 @@ describe('The \'multi\' method', () => { assert(test) }) - it('should use transaction with execAtomic and more than one command used', (done) => { + it('should use transaction with execAtomic and more than one command used', () => { const multi = client.multi() let test = false multi.execBatch = function () { @@ -609,18 +457,17 @@ describe('The \'multi\' method', () => { } multi.set('baz', 'binary') multi.get('baz') - multi.execAtomic(done) + const promise = multi.execAtomic() assert(!test) + return promise }) - it('do not mutate arguments in the multi constructor', (done) => { + it('do not mutate arguments in the multi constructor', () => { const input = [['set', 'foo', 'bar'], ['get', 'foo']] - client.multi(input).exec((err, res) => { - assert.strictEqual(err, null) + return client.multi(input).exec().then((res) => { assert.strictEqual(input.length, 2) assert.strictEqual(input[0].length, 3) assert.strictEqual(input[1].length, 2) - done() }) }) @@ -630,29 +477,14 @@ describe('The \'multi\' method', () => { assert.strictEqual(err.code, 'ECONNREFUSED') }) client.on('ready', () => { - client.multi([['set', 'foo', 'bar'], ['get', 'foo']]).exec((err, res) => { - assert(!err) + client.multi([['set', 'foo', 'bar'], ['get', 'foo']]).exec().then((res) => { assert.strictEqual(res[1], 'bar') done() }) }) }) - it('emits error once if reconnecting after multi has been executed but not yet returned without callback', (done) => { - // NOTE: If uncork is called async by postponing it to the next tick, this behavior is going to change. - // The command won't be processed anymore two errors are returned instead of one - client.on('error', (err) => { - assert.strictEqual(err.code, 'UNCERTAIN_STATE') - client.get('foo', helper.isString('bar', done)) - }) - - // The commands should still be fired, no matter that the socket is destroyed on the same tick - client.multi().set('foo', 'bar').get('foo').exec() - // Abort connection before the value returned - client.stream.destroy() - }) - - it('indivdual commands work properly with multi', (done) => { + it('indivdual commands work properly with multi', () => { // Neither of the following work properly in a transactions: // (This is due to Redis not returning the reply as expected / resulting in undefined behavior) // (Likely there are more commands that do not work with a transaction) @@ -666,31 +498,23 @@ describe('The \'multi\' method', () => { // // Make sure sendCommand is not called - client.sendCommand = function () { + client.sendCommand = () => { throw new Error('failed') } assert.strictEqual(client.selectedDb, undefined) const multi = client.multi() - multi.select(5, (err, res) => { - assert.strictEqual(err, null) - assert.strictEqual(client.selectedDb, 5) - assert.strictEqual(res, 'OK') - assert.notDeepEqual(client.serverInfo.db5, { avg_ttl: 0, expires: 0, keys: 1 }) - }) - // multi.client('reply', 'on', helper.isString('OK')); // Redis v.3.2 - multi.set('foo', 'bar', helper.isString('OK')) - multi.info((err, res) => { - assert.strictEqual(err, null) - assert.strictEqual(res.indexOf('# Server\r\nredis_version:'), 0) - assert.deepEqual(client.serverInfo.db5, { avg_ttl: 0, expires: 0, keys: 1 }) - }) - multi.get('foo', helper.isString('bar')) - multi.exec((err, res) => { - assert.strictEqual(err, null) + multi.select(5) + // multi.client('reply', 'on') // Redis v.3.2 + multi.set('foo', 'bar') + multi.info() + multi.get('foo') + return multi.exec().then((res) => { res[2] = res[2].substr(0, 10) - assert.deepEqual(res, ['OK', 'OK', '# Server\r\n', 'bar']) - client.flushdb(done) + assert.strictEqual(client.selectedDb, 5) + assert.deepStrictEqual(client.serverInfo.db5, { avg_ttl: 0, expires: 0, keys: 1 }) + assert.deepStrictEqual(res, ['OK', 'OK', '# Server\r\n', 'bar']) + return client.flushdb() }) }) }) diff --git a/test/node_redis.spec.js b/test/node_redis.spec.js index a7f57941a1..058c9cf354 100644 --- a/test/node_redis.spec.js +++ b/test/node_redis.spec.js @@ -30,13 +30,13 @@ describe('The nodeRedis client', () => { }) }) - it('convert minus to underscore in Redis function names', (done) => { + it('convert minus to underscore in Redis function names', () => { const names = Object.keys(redis.RedisClient.prototype) client = redis.createClient() for (let i = 0; i < names.length; i++) { assert(/^([a-zA-Z_][a-zA-Z_0-9]*)?$/.test(client[names[i]].name)) } - client.quit(done) + return client.quit() }) it('reset the parser while reconnecting (See #1190)', (done) => { @@ -64,11 +64,9 @@ describe('The nodeRedis client', () => { }) describe('when connected', () => { - beforeEach((done) => { + beforeEach(() => { client = redis.createClient.apply(null, args) - client.once('connect', () => { - client.flushdb(done) - }) + return client.flushdb() }) describe('duplicate', () => { @@ -122,7 +120,7 @@ describe('The nodeRedis client', () => { client.duplicate((err, client) => { assert(!err) assert.strictEqual(client.ready, true) - client.quit(done) + client.quit().then(() => done()) }) }) @@ -134,35 +132,20 @@ describe('The nodeRedis client', () => { done(client) }) }) - - it('works with a promises', () => { - return client.duplicateAsync().then((client) => { - assert.strictEqual(client.ready, true) - return client.quitAsync() - }) - }) - - it('works with a promises and errors', () => { - return client.duplicateAsync({ - port: 9999 - }).catch((err) => { - assert.strictEqual(err.code, 'ECONNREFUSED') - }) - }) }) describe('big data', () => { // Check if the fast mode for big strings is working correct - it('safe strings that are bigger than 30000 characters', (done) => { + it('safe strings that are bigger than 30000 characters', () => { let str = 'foo ಠ_ಠ bar ' while (str.length < 111111) { str += str } client.set('foo', str) - client.get('foo', helper.isString(str, done)) + return client.get('foo').then(helper.isString(str)) }) - it('safe strings that are bigger than 30000 characters with multi', (done) => { + it('safe strings that are bigger than 30000 characters with multi', () => { let str = 'foo ಠ_ಠ bar ' while (str.length < 111111) { str += str @@ -176,102 +159,68 @@ describe('The nodeRedis client', () => { assert(!client.fireStrings) temp(data) } - client.multi().set('foo', str).get('foo', helper.isString(str)).exec((err, res) => { - assert.strictEqual(err, null) + const promise = client.multi().set('foo', str).get('foo').exec().then((res) => { assert.strictEqual(called, true) assert.strictEqual(res[1], str) - done() }) assert(client.fireStrings) + return promise }) }) describe('sendCommand', () => { - it('omitting args should be fine', (done) => { + it('omitting args should be fine', () => { client.serverInfo = {} client.sendCommand('info') - client.sendCommand('ping', (err, res) => { - assert.strictEqual(err, null) + return client.sendCommand('ping').then((res) => { assert.strictEqual(res, 'PONG') // Check if the previous info command used the internal individual info command assert.notDeepEqual(client.serverInfo, {}) client.serverInfo = {} - }) - client.sendCommand('info', null, undefined) - client.sendCommand('ping', null, (err, res) => { - assert.strictEqual(err, null) - assert.strictEqual(res, 'PONG') - // Check if the previous info command used the internal individual info command - assert.notDeepEqual(client.serverInfo, {}) - client.serverInfo = {} - }) - client.sendCommand('info', undefined, undefined) - client.sendCommand('ping', (err, res) => { - assert.strictEqual(err, null) - assert.strictEqual(res, 'PONG') - // Check if the previous info command used the internal individual info command - assert.notDeepEqual(client.serverInfo, {}) - client.serverInfo = {} - }) - client.sendCommand('info', undefined, (err, res) => { - assert.strictEqual(err, null) - assert(/redis_version/.test(res)) - // The individual info command should also be called by using sendCommand - assert.notDeepEqual(client.serverInfo, {}) - done() + client.sendCommand('ping', null).then(helper.isString('PONG')) + return client.sendCommand('info').then((res) => { + assert(/redis_version/.test(res)) + // The individual info command should also be called by using sendCommand + assert.notDeepEqual(client.serverInfo, {}) + }) }) }) - it('using multi with sendCommand should work as individual command instead of using the internal multi', (done) => { + it('using multi with sendCommand should work as individual command instead of using the internal multi', () => { // This is necessary to keep backwards compatibility and it is the only way to handle multis as you want in nodeRedis client.sendCommand('multi') - client.sendCommand('set', ['foo', 'bar'], helper.isString('QUEUED')) + client.sendCommand('set', ['foo', 'bar']).then(helper.isString('QUEUED')) client.get('foo') // exec is not manipulated if not fired by the individual multi command // As the multi command is handled individually by the user he also has to handle the return value - client.exec(helper.isDeepEqual(['OK', 'bar'], done)) + return client.exec().then(helper.isDeepEqual(['OK', 'bar'])) }) - it('multi should be handled special', (done) => { - client.sendCommand('multi', undefined, helper.isString('OK')) + it('multi should be handled special', () => { + client.sendCommand('multi', undefined).then(helper.isString('OK')) const args = ['test', 'bla'] - client.sendCommand('set', args, helper.isString('QUEUED')) - assert.deepEqual(args, ['test', 'bla']) // Check args manipulation - client.get('test', helper.isString('QUEUED')) + client.sendCommand('set', args).then(helper.isString('QUEUED')) + assert.deepStrictEqual(args, ['test', 'bla']) // Check args manipulation + client.get('test').then(helper.isString('QUEUED')) // As the multi command is handled individually by the user he also has to handle the return value - client.exec(helper.isDeepEqual(['OK', 'bla'], done)) - }) - - it('using another type as cb should throw', () => { - try { - client.sendCommand('set', ['test', 'bla'], [true]) - throw new Error('failed') - } catch (err) { - assert.strictEqual(err.message, 'Wrong input type "Array" for callback function') - } - try { - client.sendCommand('set', ['test', 'bla'], null) - throw new Error('failed') - } catch (err) { - assert.strictEqual(err.message, 'Wrong input type "null" for callback function') - } + return client.exec().then(helper.isDeepEqual(['OK', 'bla'])) }) it('command argument has to be of type string', () => { try { - client.sendCommand(true, ['test', 'bla'], () => {}) + client.sendCommand(true, ['test', 'bla']) throw new Error('failed') } catch (err) { assert.strictEqual(err.message, 'Wrong input type "Boolean" for command name') } try { - client.sendCommand(undefined, ['test', 'bla'], () => {}) + client.sendCommand(undefined, ['test', 'bla']) throw new Error('failed') } catch (err) { assert.strictEqual(err.message, 'Wrong input type "undefined" for command name') } try { - client.sendCommand(null, ['test', 'bla'], () => {}) + client.sendCommand(null, ['test', 'bla']) throw new Error('failed') } catch (err) { assert.strictEqual(err.message, 'Wrong input type "null" for command name') @@ -287,56 +236,44 @@ describe('The nodeRedis client', () => { } }) - it('passing a callback as args and as callback should throw', () => { - try { - client.sendCommand('info', () => {}, () => {}) - throw new Error('failed') - } catch (err) { - assert.strictEqual(err.message, 'Wrong input type "Function" for args') - } - }) - - it('multi should be handled special', (done) => { - client.sendCommand('multi', undefined, helper.isString('OK')) + it('multi should be handled special', () => { + client.sendCommand('multi', undefined).then(helper.isString('OK')) const args = ['test', 'bla'] - client.sendCommand('set', args, helper.isString('QUEUED')) - assert.deepEqual(args, ['test', 'bla']) // Check args manipulation - client.get('test', helper.isString('QUEUED')) + client.sendCommand('set', args).then(helper.isString('QUEUED')) + assert.deepStrictEqual(args, ['test', 'bla']) // Check args manipulation + client.get('test').then(helper.isString('QUEUED')) // As the multi command is handled individually by the user he also has to handle the return value - client.exec(helper.isDeepEqual(['OK', 'bla'], done)) + return client.exec().then(helper.isDeepEqual(['OK', 'bla'])) }) - it('the args array may contain a arbitrary number of arguments', (done) => { - client.sendCommand('mset', ['foo', 1, 'bar', 2, 'baz', 3], helper.isString('OK')) + it('the args array may contain a arbitrary number of arguments', () => { + client.sendCommand('mset', ['foo', 1, 'bar', 2, 'baz', 3]).then(helper.isString('OK')) // As the multi command is handled individually by the user he also has to handle the return value - client.mget(['foo', 'bar', 'baz'], helper.isDeepEqual(['1', '2', '3'], done)) + return client.mget(['foo', 'bar', 'baz']).then(helper.isDeepEqual(['1', '2', '3'])) }) - it('sendCommand with callback as args', (done) => { - client.sendCommand('abcdef', (err, res) => { - assert.strictEqual(err.message, 'ERR unknown command \'abcdef\'') - done() - }) + it('sendCommand with callback as args', () => { + return client.sendCommand('abcdef').then(assert, helper.isError(/ERR unknown command 'abcdef'/)) }) }) describe('retryUnfulfilledCommands', () => { - it('should retry all commands instead of returning an error if a command did not yet return after a connection loss', (done) => { + it('should retry all commands instead of returning an error if a command did not yet return after a connection loss', () => { const bclient = redis.createClient({ retryUnfulfilledCommands: true }) - bclient.blpop('blocking list 2', 5, (err, value) => { + const promise = bclient.blpop('blocking list 2', 5).then((value) => { assert.strictEqual(value[0], 'blocking list 2') assert.strictEqual(value[1], 'initial value') bclient.end(true) - done(err) }) bclient.once('ready', () => { setTimeout(() => { bclient.stream.destroy() - client.rpush('blocking list 2', 'initial value', helper.isNumber(1)) + client.rpush('blocking list 2', 'initial value').then(helper.isNumber(1)) }, 100) }) + return promise }) it('should retry all commands even if the offline queue is disabled', (done) => { @@ -345,15 +282,15 @@ describe('The nodeRedis client', () => { retryUnfulfilledCommands: true }) bclient.once('ready', () => { - bclient.blpop('blocking list 2', 5, (err, value) => { + bclient.blpop('blocking list 2', 5).then((value) => { assert.strictEqual(value[0], 'blocking list 2') assert.strictEqual(value[1], 'initial value') bclient.end(true) - done(err) + done() }) setTimeout(() => { bclient.stream.destroy() - client.rpush('blocking list 2', 'initial value', helper.isNumber(1)) + client.rpush('blocking list 2', 'initial value').then(helper.isNumber(1)) }, 100) }) }) @@ -367,7 +304,7 @@ describe('The nodeRedis client', () => { done(new Error('failed')) } }, 20) - const cb = function (err, res) { + const cb = function (err) { assert(/Connection forcefully ended|The connection is already closed./.test(err.message)) assert.strictEqual(err.code, 'NR_CLOSED') end() @@ -376,7 +313,7 @@ describe('The nodeRedis client', () => { if (i === 10) { client.end() } - client.set('foo', 'bar', cb) + client.set('foo', 'bar').then(assert, cb) } client.on('warning', () => {}) // Ignore deprecation message setTimeout(() => { @@ -386,10 +323,8 @@ describe('The nodeRedis client', () => { }) it('used with flush set to true', (done) => { - const end = helper.callFuncAfter(() => { - done() - }, 20) - const cb = function (err, res) { + const end = helper.callFuncAfter(done, 20) + const cb = function (err) { assert(/Connection forcefully ended|The connection is already closed./.test(err.message)) end() } @@ -398,106 +333,21 @@ describe('The nodeRedis client', () => { client.end(true) client.stream.write('foo') // Trigger an error on the closed stream that we ignore } - client.set('foo', 'bar', cb) + client.set('foo', 'bar').then(assert, cb) } }) - - it('emits an aggregate error if no callback was present for multiple commands in debugMode', (done) => { - redis.debugMode = true - const unhookIntercept = intercept((data) => { - return '' // Don't print the debug messages - }) - client.set('foo', 'bar') - client.set('baz', 'hello world') - client.on('error', (err) => { - assert(err instanceof Error) - assert(err instanceof redis.AbortError) - assert(err instanceof redis.AggregateError) - assert.strictEqual(err.name, 'AggregateError') - assert.strictEqual(err.errors.length, 2) - assert.strictEqual(err.message, 'Connection forcefully ended and commands aborted.') - assert.strictEqual(err.code, 'NR_CLOSED') - assert.strictEqual(err.errors[0].message, 'Connection forcefully ended and command aborted. It might have been processed.') - assert.strictEqual(err.errors[0].command, 'SET') - assert.strictEqual(err.errors[0].code, 'NR_CLOSED') - assert.deepEqual(err.errors[0].args, ['foo', 'bar']) - done() - }) - client.end(true) - unhookIntercept() - redis.debugMode = false - }) - - it('emits an abort error if no callback was present for a single commands', (done) => { - redis.debugMode = true - const unhookIntercept = intercept((data) => { - return '' // Don't print the debug messages - }) - client.set('foo', 'bar') - client.on('error', (err) => { - assert(err instanceof Error) - assert(err instanceof redis.AbortError) - assert(!(err instanceof redis.AggregateError)) - assert.strictEqual(err.message, 'Connection forcefully ended and command aborted. It might have been processed.') - assert.strictEqual(err.command, 'SET') - assert.strictEqual(err.code, 'NR_CLOSED') - assert.deepEqual(err.args, ['foo', 'bar']) - done() - }) - client.end(true) - unhookIntercept() - redis.debugMode = false - }) - - it('does not emit abort errors if no callback was present while not being in debugMode ', (done) => { - client.set('foo', 'bar') - client.end(true) - setTimeout(done, 100) - }) }) describe('commands after using .quit should fail', () => { - it('return an error in the callback', function (done) { - if (helper.redisProcess().spawnFailed()) this.skip() - - // TODO: Investigate why this test is failing hard and killing mocha if using '/tmp/redis.sock'. - // Seems like something is wrong with nyc while passing a socket connection to create client! - client = redis.createClient() - client.quit(() => { - client.get('foo', (err, res) => { - assert.strictEqual(err.message, 'Stream connection ended and command aborted. It might have been processed.') - assert.strictEqual(client.offlineQueue.length, 0) - done() - }) - }) - }) - - it('return an error in the callback version two', function (done) { + it('return an error in the callback version two', function () { if (helper.redisProcess().spawnFailed()) this.skip() client.quit() - setTimeout(() => { - client.get('foo', (err, res) => { - assert.strictEqual(err.message, 'GET can\'t be processed. The connection is already closed.') - assert.strictEqual(err.command, 'GET') - assert.strictEqual(client.offlineQueue.length, 0) - done() - }) - }, 50) - }) - - it('emit an error', function (done) { - if (helper.redisProcess().spawnFailed()) this.skip() - client.quit() - client.on('error', (err) => { - assert.strictEqual(err.message, 'SET can\'t be processed. The connection is already closed.') - assert.strictEqual(err.command, 'SET') + return client.get('foo').then(assert, (err) => { + assert.strictEqual(err.message, 'GET can\'t be processed. The connection is already closed.') + assert.strictEqual(err.command, 'GET') assert.strictEqual(client.offlineQueue.length, 0) - done() }) - setTimeout(() => { - client.set('foo', 'bar') - }, 50) }) }) @@ -512,16 +362,15 @@ describe('The nodeRedis client', () => { assert.strictEqual(Object.keys(client.serverInfo.db0).length, 3) done() }, 4) - client.get('recon 1', helper.isString('one', end)) - client.get('recon 1', helper.isString('one', end)) - client.get('recon 2', helper.isString('two', end)) - client.get('recon 2', helper.isString('two', end)) + client.get('recon 1').then(helper.isString('one')).then(end) + client.get('recon 1').then(helper.isString('one')).then(end) + client.get('recon 2').then(helper.isString('two')).then(end) + client.get('recon 2').then(helper.isString('two')).then(end) }) }) client.set('recon 1', 'one') - client.set('recon 2', 'two', (err, res) => { - assert.strictEqual(err, null) + client.set('recon 2', 'two').then((res) => { // Do not do this in normal programs. This is to simulate the server closing on us. // For orderly shutdown in normal programs, do client.quit() client.stream.destroy() @@ -540,11 +389,9 @@ describe('The nodeRedis client', () => { assert.strictEqual(client.monitoring, false, 'monitoring off at start') client.set('recon 1', 'one') - client.monitor((err, res) => { - assert.strictEqual(err, null) + client.monitor().then((res) => { assert.strictEqual(client.monitoring, true, 'monitoring on after monitor()') - client.set('recon 2', 'two', (err, res) => { - assert.strictEqual(err, null) + client.set('recon 2', 'two').then((res) => { // Do not do this in normal programs. This is to simulate the server closing on us. // For orderly shutdown in normal programs, do client.quit() client.stream.destroy() @@ -556,19 +403,18 @@ describe('The nodeRedis client', () => { // "Connection in subscriber mode, only subscriber commands may be used" it('reconnects, unsubscribes, and can retrieve the pre-existing data', (done) => { client.on('ready', () => { - client.unsubscribe(helper.isNotError()) + client.unsubscribe() client.on('unsubscribe', (channel, count) => { // we should now be out of subscriber mode. assert.strictEqual(channel, 'recon channel') assert.strictEqual(count, 0) - client.set('foo', 'bar', helper.isString('OK', done)) + client.set('foo', 'bar').then(helper.isString('OK')).then(done) }) }) client.set('recon 1', 'one') - client.subscribe('recon channel', (err, res) => { - assert.strictEqual(err, null) + client.subscribe('recon channel').then((res) => { // Do not do this in normal programs. This is to simulate the server closing on us. // For orderly shutdown in normal programs, do client.quit() client.stream.destroy() @@ -577,89 +423,31 @@ describe('The nodeRedis client', () => { it('reconnects, unsubscribes, and can retrieve the pre-existing data of a explicit channel', (done) => { client.on('ready', () => { - client.unsubscribe('recon channel', helper.isNotError()) + client.unsubscribe('recon channel').then(helper.isDeepEqual([0, ['recon channel']])) client.on('unsubscribe', (channel, count) => { // we should now be out of subscriber mode. assert.strictEqual(channel, 'recon channel') assert.strictEqual(count, 0) - client.set('foo', 'bar', helper.isString('OK', done)) + client.set('foo', 'bar').then(helper.isString('OK')).then(done) }) }) client.set('recon 1', 'one') - client.subscribe('recon channel', (err, res) => { - assert.strictEqual(err, null) + client.subscribe('recon channel').then((res) => { // Do not do this in normal programs. This is to simulate the server closing on us. // For orderly shutdown in normal programs, do client.quit() client.stream.destroy() }) }) }) - - describe('domain', () => { - it('allows client to be executed from within domain', (done) => { - // eslint-disable-next-line - var domain = require('domain').create() - - domain.run(() => { - client.set('domain', 'value', (err, res) => { - assert.strictEqual(err, null) - assert.ok(process.domain) - throw new Error('ohhhh noooo') - }) - }) - - // this is the expected and desired behavior - domain.on('error', (err) => { - assert.strictEqual(err.message, 'ohhhh noooo') - domain.exit() - done() - }) - }) - - it('keeps the same domain by using the offline queue', (done) => { - client.end(true) - client = redis.createClient() - // eslint-disable-next-line - var testDomain = require('domain').create() - testDomain.run(() => { - client.set('FOOBAR', 'def', () => { - assert.strictEqual(process.domain, testDomain) - done() - }) - }) - // eslint-disable-next-line - require('domain').create() - }) - - it('catches all errors from within the domain', (done) => { - // eslint-disable-next-line - var domain = require('domain').create() - - domain.run(() => { - // Trigger an error within the domain - client.end(true) - client.set('domain', 'value') - }) - - domain.on('error', (err) => { - assert.strictEqual(err.message, 'SET can\'t be processed. The connection is already closed.') - domain.exit() - done() - }) - }) - }) }) describe('utf8', () => { - it('handles utf-8 keys', (done) => { + it('handles utf-8 keys', () => { const utf8Sample = 'ಠ_ಠ' - client.set(['utf8test', utf8Sample], helper.isString('OK')) - client.get(['utf8test'], (err, obj) => { - assert.strictEqual(utf8Sample, obj) - done(err) - }) + client.set(['utf8test', utf8Sample]).then(helper.isString('OK')) + return client.get(['utf8test']).then(helper.isString(utf8Sample)) }) }) }) @@ -687,13 +475,11 @@ describe('The nodeRedis client', () => { it('keep execution order for commands that may fire while redis is still loading', (done) => { client = redis.createClient.apply(null, args) let fired = false - client.set('foo', 'bar', (err, res) => { - assert.strictEqual(err, null) + client.set('foo', 'bar').then((res) => { assert.strictEqual(fired, false) done() }) - client.info((err, res) => { - assert.strictEqual(err, null) + client.info().then(() => { fired = true }) }) @@ -730,10 +516,10 @@ describe('The nodeRedis client', () => { assert.strictEqual(err, error) assert(err instanceof redis.ParserError) // After the hard failure work properly again. The set should have been processed properly too - client.get('foo', helper.isString('bar', done)) + client.get('foo').then(helper.isString('bar')).then(done) }) client.once('ready', () => { - client.set('foo', 'bar', (err, res) => { + client.set('foo', 'bar').then(assert, (err) => { assert.strictEqual(err.message, 'Fatal error encountered. Command aborted. It might have been processed.') assert.strictEqual(err.code, 'NR_FATAL') assert(err instanceof redis.AbortError) @@ -759,7 +545,7 @@ describe('The nodeRedis client', () => { }) setTimeout(() => { - client.set('foo', 'bar', (err, result) => { + client.set('foo', 'bar').then(helper.fail, (err) => { if (!finished) done(err) assert.strictEqual(err.message, 'Connection forcefully ended and command aborted.') }) @@ -772,17 +558,19 @@ describe('The nodeRedis client', () => { }, 50) }) + // TODO: Fix this by adding the CONNECTION_BROKEN back in it.skip('enqueues operation and keep the queue while trying to reconnect', (done) => { client = redis.createClient(9999, null, { retryStrategy (options) { if (options.attempt < 4) { - return 200 + return 50 } } }) let i = 0 client.on('error', (err) => { + console.log(err) if (err.code === 'CONNECTION_BROKEN') { assert(i, 3) assert.strictEqual(client.offlineQueue.length, 0) @@ -805,12 +593,10 @@ describe('The nodeRedis client', () => { assert.strictEqual(params.timesConnected, 0) assert(params.error instanceof Error) assert(typeof params.totalRetryTime === 'number') - assert.strictEqual(client.offlineQueue.length, 2) + assert.strictEqual(client.offlineQueue.length, 1) }) - // Should work with either a callback or without - client.set('baz', 13) - client.set('foo', 'bar', (err, result) => { + client.set('foo', 'bar').then(assert, (err) => { assert(i, 3) assert(err) assert.strictEqual(client.offlineQueue.length, 0) @@ -823,39 +609,28 @@ describe('The nodeRedis client', () => { client.once('ready', () => { const multi = client.multi() multi.config('bar') - const cb = function (err, reply) { - assert.strictEqual(err.code, 'UNCERTAIN_STATE') - } for (let i = 0; i < 12; i += 3) { - client.set(`foo${i}`, `bar${i}`) - multi.set(`foo${i + 1}`, `bar${i + 1}`, cb) + client.set(`foo${i}`, `bar${i}`).then(helper.fail, helper.isError) + multi.set(`foo${i + 1}`, `bar${i + 1}`) multi.set(`foo${i + 2}`, `bar${i + 2}`) } - multi.exec() + multi.exec().then(helper.fail, (err) => { + assert.strictEqual(client.commandQueue.length, 0) + assert.strictEqual(err.errors.length, 9) + assert.strictEqual(err.errors[1].command, 'SET') + assert.deepStrictEqual(err.errors[1].args, ['foo1', 'bar1']) + end() + }) assert.strictEqual(client.commandQueue.length, 15) helper.killConnection(client) }) - const end = helper.callFuncAfter(done, 3) + const end = helper.callFuncAfter(done, 2) client.on('error', (err) => { - if (err.command === 'EXEC') { - assert.strictEqual(client.commandQueue.length, 0) - assert.strictEqual(err.errors.length, 9) - assert.strictEqual(err.errors[1].command, 'SET') - assert.deepEqual(err.errors[1].args, ['foo1', 'bar1']) - end() - } else if (err.code === 'UNCERTAIN_STATE') { - assert.strictEqual(client.commandQueue.length, 0) - assert.strictEqual(err.errors.length, 4) - assert.strictEqual(err.errors[0].command, 'SET') - assert.deepEqual(err.errors[0].args, ['foo0', 'bar0']) - end() - } else { - assert.strictEqual(err.code, 'ECONNREFUSED') - assert.strictEqual(err.errno, 'ECONNREFUSED') - assert.strictEqual(err.syscall, 'connect') - end() - } + assert.strictEqual(err.code, 'ECONNREFUSED') + assert.strictEqual(err.errno, 'ECONNREFUSED') + assert.strictEqual(err.syscall, 'connect') + end() }) }) }) @@ -867,7 +642,7 @@ describe('The nodeRedis client', () => { }) client.on('ready', () => { client.stream.destroy() - client.set('foo', 'bar', (err, res) => { + client.set('foo', 'bar').then(assert, (err) => { assert.strictEqual(err.message, 'SET can\'t be processed. Stream not writeable.') done() }) @@ -881,67 +656,25 @@ describe('The nodeRedis client', () => { const end = helper.callFuncAfter(done, 3) client.on('error', (err) => { - assert(/offline queue is deactivated|ECONNREFUSED/.test(err.message)) + assert(/ECONNREFUSED/.test(err.message)) assert.strictEqual(client.commandQueue.length, 0) end() }) - client.set('foo', 'bar') + client.set('foo', 'bar').then(helper.fail, (err) => { + assert(/offline queue is deactivated/.test(err.message)) + assert.strictEqual(client.commandQueue.length, 0) + end() + }) assert.doesNotThrow(() => { - client.set('foo', 'bar', (err) => { + client.set('foo', 'bar').then(assert, (err) => { // should callback with an error assert.ok(err) setTimeout(end, 50) }) }) }) - - it('flushes the command queue if connection is lost', (done) => { - client = redis.createClient({ - enableOfflineQueue: false - }) - - redis.debugMode = true - const unhookIntercept = intercept(() => { - return '' - }) - client.once('ready', () => { - const multi = client.multi() - multi.config('bar') - const cb = function (err, reply) { - assert.strictEqual(err.code, 'UNCERTAIN_STATE') - } - for (let i = 0; i < 12; i += 3) { - client.set(`foo${i}`, `bar${i}`) - multi.set(`foo${i + 1}`, `bar${i + 1}`, cb) - multi.set(`foo${i + 2}`, `bar${i + 2}`) - } - multi.exec() - assert.strictEqual(client.commandQueue.length, 15) - helper.killConnection(client) - }) - - const end = helper.callFuncAfter(done, 3) - client.on('error', (err) => { - assert.strictEqual(client.commandQueue.length, 0) - if (err.command === 'EXEC') { - assert.strictEqual(err.errors.length, 9) - end() - } else if (err.code === 'UNCERTAIN_STATE') { - assert.strictEqual(err.errors.length, 4) - end() - } else { - assert.strictEqual(err.code, 'ECONNREFUSED') - assert.strictEqual(err.errno, 'ECONNREFUSED') - assert.strictEqual(err.syscall, 'connect') - redis.debugMode = false - client.end(true) - unhookIntercept() - end() - } - }) - }) }) }) }) diff --git a/test/prefix.spec.js b/test/prefix.spec.js index 547f260e56..09648946a4 100644 --- a/test/prefix.spec.js +++ b/test/prefix.spec.js @@ -10,84 +10,75 @@ describe('prefix key names', () => { describe(`using ${ip}`, () => { let client = null - beforeEach((done) => { + beforeEach(() => { client = redis.createClient({ prefix: 'test:prefix:' }) - client.on('ready', () => { - client.flushdb((err) => { - done(err) - }) - }) + return client.flushdb() }) afterEach(() => { client.end(true) }) - it('auto prefix set / get', (done) => { - client.set('key', 'value', helper.isString('OK')) - client.get('key', helper.isString('value')) - client.getrange('key', 1, -1, (err, reply) => { - assert.strictEqual(reply, 'alue') - assert.strictEqual(err, null) - }) - client.exists('key', helper.isNumber(1)) - // The key will be prefixed itself - client.exists('test:prefix:key', helper.isNumber(0)) - client.mset('key2', 'value2', 'key3', 'value3') - client.keys('*', (err, res) => { - assert.strictEqual(err, null) - assert.strictEqual(res.length, 3) - assert(res.indexOf('test:prefix:key') !== -1) - assert(res.indexOf('test:prefix:key2') !== -1) - assert(res.indexOf('test:prefix:key3') !== -1) - done() - }) + it('auto prefix set / get', () => { + return Promise.all([ + client.set('key', 'value').then(helper.isString('OK')), + client.get('key').then(helper.isString('value')), + client.getrange('key', 1, -1).then((reply) => { + assert.strictEqual(reply, 'alue') + }), + client.exists('key').then(helper.isNumber(1)), + // The key will be prefixed itself + client.exists('test:prefix:key').then(helper.isNumber(0)), + client.mset('key2', 'value2', 'key3', 'value3'), + client.keys('*').then((res) => { + assert.strictEqual(res.length, 3) + assert(res.includes('test:prefix:key')) + assert(res.includes('test:prefix:key2')) + assert(res.includes('test:prefix:key3')) + }) + ]) }) - it('auto prefix set / get with .batch', (done) => { + it('auto prefix set / get with .batch', () => { const batch = client.batch() - batch.set('key', 'value', helper.isString('OK')) - batch.get('key', helper.isString('value')) - batch.getrange('key', 1, -1, (err, reply) => { - assert.strictEqual(reply, 'alue') - assert.strictEqual(err, null) - }) - batch.exists('key', helper.isNumber(1)) + batch.set('key', 'value') + batch.get('key') + batch.getrange('key', 1, -1) + batch.exists('key') // The key will be prefixed itself - batch.exists('test:prefix:key', helper.isNumber(0)) + batch.exists('test:prefix:key') batch.mset('key2', 'value2', 'key3', 'value3') - batch.keys('*', (err, res) => { - assert.strictEqual(err, null) - assert.strictEqual(res.length, 3) - assert(res.indexOf('test:prefix:key') !== -1) - assert(res.indexOf('test:prefix:key2') !== -1) - assert(res.indexOf('test:prefix:key3') !== -1) + batch.keys('*') + return batch.exec().then((res) => { + const prefixes = res.pop() + assert.deepStrictEqual(res, ['OK', 'value', 'alue', 1, 0, 'OK']) + assert.strictEqual(prefixes.length, 3) + assert(prefixes.includes('test:prefix:key')) + assert(prefixes.includes('test:prefix:key2')) + assert(prefixes.includes('test:prefix:key3')) }) - batch.exec(done) }) - it('auto prefix set / get with .multi', (done) => { + it('auto prefix set / get with .multi', () => { const multi = client.multi() - multi.set('key', 'value', helper.isString('OK')) - multi.get('key', helper.isString('value')) - multi.getrange('key', 1, -1, (err, reply) => { - assert.strictEqual(reply, 'alue') - assert.strictEqual(err, null) - }) - multi.exists('key', helper.isNumber(1)) + multi.set('key', 'value') + multi.get('key') + multi.getrange('key', 1, -1) + multi.exists('key') // The key will be prefixed itself - multi.exists('test:prefix:key', helper.isNumber(0)) + multi.exists('test:prefix:key') multi.mset('key2', 'value2', 'key3', 'value3') - multi.keys('*', (err, res) => { - assert.strictEqual(err, null) - assert.strictEqual(res.length, 3) - assert(res.indexOf('test:prefix:key') !== -1) - assert(res.indexOf('test:prefix:key2') !== -1) - assert(res.indexOf('test:prefix:key3') !== -1) + multi.keys('*') + return multi.exec().then((res) => { + const prefixes = res.pop() + assert.deepStrictEqual(res, ['OK', 'value', 'alue', 1, 0, 'OK']) + assert.strictEqual(prefixes.length, 3) + assert(prefixes.includes('test:prefix:key')) + assert(prefixes.includes('test:prefix:key2')) + assert(prefixes.includes('test:prefix:key3')) }) - multi.exec(done) }) }) }) diff --git a/test/pubsub.spec.js b/test/pubsub.spec.js index d25cea6898..3a09f8a77b 100644 --- a/test/pubsub.spec.js +++ b/test/pubsub.spec.js @@ -20,14 +20,8 @@ describe('publish/subscribe', () => { pub = redis.createClient.apply(null, args) sub = redis.createClient.apply(null, args) - pub.once('connect', () => { - pub.flushdb(() => { - end() - }) - }) - sub.once('connect', () => { - end() - }) + pub.flushdb().then(() => end()) + sub.once('connect', end) }) describe('disable resubscribe', () => { @@ -36,9 +30,7 @@ describe('publish/subscribe', () => { sub = redis.createClient({ disableResubscribing: true }) - sub.once('connect', () => { - done() - }) + sub.once('connect', done) }) it('does not fire subscribe events after reconnecting', (done) => { @@ -71,9 +63,7 @@ describe('publish/subscribe', () => { sub = redis.createClient({ stringNumbers: true }) - sub.once('connect', () => { - done() - }) + sub.once('connect', done) }) it('does not fire subscribe events after reconnecting', (done) => { @@ -86,16 +76,15 @@ describe('publish/subscribe', () => { sub.on('unsubscribe', (chnl, count) => { assert.strictEqual(typeof count, 'number') assert.strictEqual(--i, count) + if (count === 0) { + assert.deepStrictEqual(sub.subscriptionSet, {}) + end() + } }) sub.subscribe(channel, channel2) - sub.unsubscribe((err, res) => { // Do not pass a channel here! - if (err) throw err - assert.strictEqual(sub.pubSubMode, 2) - assert.deepEqual(sub.subscriptionSet, {}) - end() - }) - sub.set('foo', 'bar', helper.isString('OK')) - sub.subscribe(channel2, end) + sub.unsubscribe() + sub.set('foo', 'bar').then(helper.isString('OK')) + sub.subscribe(channel2).then(end) }) }) @@ -143,8 +132,8 @@ describe('publish/subscribe', () => { it('receives messages on subscribed channel', (done) => { const end = helper.callFuncAfter(done, 2) sub.on('subscribe', (chnl, count) => { - pub.publish(channel, message, (err, res) => { - helper.isNumber(1)(err, res) + pub.publish(channel, message).then((res) => { + helper.isNumber(1)(res) end() }) }) @@ -161,8 +150,8 @@ describe('publish/subscribe', () => { it('receives messages if subscribe is called after unsubscribe', (done) => { const end = helper.callFuncAfter(done, 2) sub.once('subscribe', (chnl, count) => { - pub.publish(channel, message, (err, res) => { - helper.isNumber(1)(err, res) + pub.publish(channel, message).then((res) => { + helper.isNumber(1)(res) end() }) }) @@ -178,20 +167,24 @@ describe('publish/subscribe', () => { sub.subscribe(channel) }) - it('handles SUB UNSUB MSG SUB', (done) => { - sub.subscribe('chan8') - sub.subscribe('chan9') - sub.unsubscribe('chan9') - pub.publish('chan8', 'something') - sub.subscribe('chan9', done) + it('handles SUB UNSUB MSG SUB', () => { + return Promise.all([ + sub.subscribe('chan8'), + sub.subscribe('chan9'), + sub.unsubscribe('chan9'), + pub.publish('chan8', 'something'), + sub.subscribe('chan9') + ]) }) - it('handles SUB UNSUB MSG SUB 2', (done) => { - sub.psubscribe('abc*', helper.isDeepEqual([1, ['abc*']])) - sub.subscribe('xyz') - sub.unsubscribe('xyz') - pub.publish('abcd', 'something') - sub.subscribe('xyz', done) + it('handles SUB UNSUB MSG SUB 2', () => { + return Promise.all([ + sub.psubscribe('abc*').then(helper.isDeepEqual([1, ['abc*']])), + sub.subscribe('xyz'), + sub.unsubscribe('xyz'), + pub.publish('abcd', 'something'), + sub.subscribe('xyz') + ]) }) it('emits end event if quit is called from within subscribe', (done) => { @@ -218,8 +211,7 @@ describe('publish/subscribe', () => { sub.select(3) sub.subscribe(channels) - sub.on('ready', (err, results) => { - if (err) throw err + sub.on('ready', () => { pub.publish(channels[count], msg[count]) count++ }) @@ -233,10 +225,9 @@ describe('publish/subscribe', () => { const end = helper.callFuncAfter(done, 2) sub.select(3) sub.set('foo', 'bar') - sub.set('failure', helper.isError()) // Triggering a warning while subscribing should work - sub.mget('foo', 'bar', 'baz', 'hello', 'world', helper.isDeepEqual(['bar', null, null, null, null])) - sub.subscribe('somechannel', 'another channel', (err, res) => { - if (err) throw err + sub.set('failure').then(helper.fail, helper.isError()) // Triggering a warning while subscribing should work + sub.mget('foo', 'bar', 'baz', 'hello', 'world').then(helper.isDeepEqual(['bar', null, null, null, null])) + sub.subscribe('somechannel', 'another channel').then((res) => { end() sub.stream.destroy() }) @@ -244,56 +235,59 @@ describe('publish/subscribe', () => { sub.on('ready', () => { sub.unsubscribe() sub.del('foo') - sub.info(end) + sub.info().then(end) }) }) - it('should not go into pubsub mode with unsubscribe commands', (done) => { + it('should not go into pubsub mode with unsubscribe commands', () => { sub.on('unsubscribe', (msg) => { // The unsubscribe should not be triggered, as there was no corresponding channel throw new Error('Test failed') }) - sub.set('foo', 'bar') - sub.unsubscribe(helper.isDeepEqual([0, []])) - sub.del('foo', done) + return Promise.all([ + sub.set('foo', 'bar'), + sub.unsubscribe().then(helper.isDeepEqual([0, []])), + sub.del('foo') + ]) }) - it('handles multiple channels with the same channel name properly, even with buffers', (done) => { + it('handles multiple channels with the same channel name properly, even with buffers', () => { const channels = ['a', 'b', 'a', Buffer.from('a'), 'c', 'b'] const subscribedChannels = [1, 2, 2, 2, 3, 3] - let i = 0 sub.subscribe(channels) sub.on('subscribe', (channel, count) => { + const compareChannel = channels.shift() if (Buffer.isBuffer(channel)) { - assert.strictEqual(channel.inspect(), Buffer.from(channels[i]).inspect()) + assert.strictEqual(channel.inspect(), Buffer.from(compareChannel).inspect()) } else { - assert.strictEqual(channel, channels[i].toString()) + assert.strictEqual(channel, compareChannel.toString()) } - assert.strictEqual(count, subscribedChannels[i]) - i++ + assert.strictEqual(count, subscribedChannels.shift()) }) sub.unsubscribe('a', 'c', 'b') - sub.get('foo', done) + return sub.get('foo') }) it('should only resubscribe to channels not unsubscribed earlier on a reconnect', (done) => { sub.subscribe('/foo', '/bar') - sub.batch().unsubscribe(['/bar'], () => { - pub.pubsub('channels', helper.isDeepEqual(['/foo'], () => { + sub.batch().unsubscribe(['/bar']).exec().then(() => { + pub.pubsub('channels').then((res) => { + helper.isDeepEqual(['/foo'])(res) sub.stream.destroy() sub.once('ready', () => { - pub.pubsub('channels', helper.isDeepEqual(['/foo'], () => { - sub.unsubscribe('/foo', done) - })) + pub.pubsub('channels').then((res) => { + helper.isDeepEqual(['/foo'])(res) + sub.unsubscribe('/foo').then(() => done()) + }) }) - })) - }).exec() + }) + }) }) - it('unsubscribes, subscribes, unsubscribes... single and multiple entries mixed. Withouth callbacks', (done) => { + it('unsubscribes, subscribes, unsubscribes... single and multiple entries mixed', (done) => { function subscribe (channels) { - sub.unsubscribe(helper.isNull) - sub.subscribe(channels, helper.isNull) + sub.unsubscribe().then(helper.isNull) + sub.subscribe(channels).then(helper.isNull) } let all = false const subscribeMsg = ['1', '3', '2', '5', 'test', 'bla'] @@ -318,35 +312,7 @@ describe('publish/subscribe', () => { subscribe(['5', 'test', 'bla']) }) - it('unsubscribes, subscribes, unsubscribes... single and multiple entries mixed. Without callbacks', (done) => { - function subscribe (channels) { - sub.unsubscribe() - sub.subscribe(channels) - } - let all = false - const subscribeMsg = ['1', '3', '2', '5', 'test', 'bla'] - sub.on('subscribe', (msg, count) => { - subscribeMsg.splice(subscribeMsg.indexOf(msg), 1) - if (subscribeMsg.length === 0 && all) { - assert.strictEqual(count, 3) - done() - } - }) - const unsubscribeMsg = ['1', '3', '2'] - sub.on('unsubscribe', (msg, count) => { - unsubscribeMsg.splice(unsubscribeMsg.indexOf(msg), 1) - if (unsubscribeMsg.length === 0) { - assert.strictEqual(count, 0) - all = true - } - }) - - subscribe(['1', '3']) - subscribe(['2']) - subscribe(['5', 'test', 'bla']) - }) - - it('unsubscribes, subscribes, unsubscribes... single and multiple entries mixed. Without callback and concret channels', (done) => { + it('unsubscribes, subscribes, unsubscribes... single and multiple entries mixed. Without concrete channels', (done) => { function subscribe (channels) { sub.unsubscribe(channels) sub.unsubscribe(channels) @@ -377,11 +343,8 @@ describe('publish/subscribe', () => { it('unsubscribes, subscribes, unsubscribes... with pattern matching', (done) => { function subscribe (channels, callback) { - sub.punsubscribe('prefix:*', helper.isNull) - sub.psubscribe(channels, (err, res) => { - helper.isNull(err) - if (callback) callback(err, res) - }) + sub.punsubscribe('prefix:*').then(helper.isNull) + sub.psubscribe(channels).then(callback) } let all = false const end = helper.callFuncAfter(done, 8) @@ -414,19 +377,16 @@ describe('publish/subscribe', () => { }) subscribe(['prefix:*', 'prefix:3'], () => { - pub.publish('prefix:1', Buffer.from('test'), () => { + pub.publish('prefix:1', Buffer.from('test')).then(() => { subscribe(['prefix:2']) - subscribe(['5', 'test:a', 'bla'], () => { - assert(all) - }) - sub.punsubscribe((err, res) => { - assert(!err) + subscribe(['5', 'test:a', 'bla'], () => assert(all)) + sub.punsubscribe().then((res) => { assert.deepStrictEqual(res, [0, ['prefix:3', 'prefix:2', '5', 'test:a', 'bla']]) assert(all) all = false // Make sure the callback is actually after the emit end() }) - sub.pubsub('channels', helper.isDeepEqual([], end)) + sub.pubsub('channels').then(helper.isDeepEqual([])).then(end) }) }) }) @@ -443,7 +403,7 @@ describe('publish/subscribe', () => { sub.on('unsubscribe', (chnl, count) => { assert.strictEqual(chnl, channel) assert.strictEqual(count, 0) - return done() + done() }) }) @@ -455,17 +415,19 @@ describe('publish/subscribe', () => { sub.subscribe(channel) sub.on('unsubscribe', (chnl, count) => { - pub.incr('foo', helper.isNumber(1, done)) + pub.incr('foo').then(helper.isNumber(1)).then(done) }) }) - it('sub executes callback when unsubscribe is called and there are no subscriptions', (done) => { - sub.unsubscribe(helper.isDeepEqual([0, []], done)) + it('sub executes when unsubscribe is called and there are no subscriptions', () => { + return sub.unsubscribe().then(helper.isDeepEqual([0, []])) }) - it('pub executes callback when unsubscribe is called and there are no subscriptions', (done) => { - pub.unsubscribe(helper.isDeepEqual([0, []])) - pub.get('foo', done) + it('pub executes when unsubscribe is called and there are no subscriptions', () => { + return Promise.all([ + pub.unsubscribe().then(helper.isDeepEqual([0, []])), + pub.get('foo') + ]) }) }) @@ -474,28 +436,28 @@ describe('publish/subscribe', () => { const sub2 = redis.createClient({ returnBuffers: true }) - sub.subscribe('/foo', () => { + sub.subscribe('/foo').then(() => { sub2.on('ready', () => { - sub2.batch().psubscribe('*', helper.isDeepEqual([1, ['*']])).exec() - sub2.subscribe('/foo', () => { - pub.pubsub('numsub', '/foo', helper.isDeepEqual(['/foo', 2])) + sub2.batch().psubscribe('*').exec().then(helper.isDeepEqual([[1, ['*']]])) + sub2.subscribe('/foo').then(() => { + pub.pubsub('numsub', '/foo').then(helper.isDeepEqual(['/foo', 2])) // sub2 is counted twice as it subscribed with psubscribe and subscribe - pub.publish('/foo', 'hello world', helper.isNumber(3)) + pub.publish('/foo', 'hello world').then(helper.isNumber(3)) }) sub2.on('pmessage', (pattern, channel, message) => { assert.strictEqual(pattern.inspect(), Buffer.from('*').inspect()) assert.strictEqual(channel.inspect(), Buffer.from('/foo').inspect()) assert.strictEqual(message.inspect(), Buffer.from('hello world').inspect()) - sub2.quit(done) + sub2.quit().then(() => done()) }) }) }) }) it('allows to listen to pmessageBuffer and pmessage', (done) => { - const end = helper.callFuncAfter(done, 6) + const end = helper.callFuncAfter(done, 3) const data = Array(10000).join('äüs^öéÉÉ`e') - sub.set('foo', data, () => { + sub.set('foo', data).then(() => { sub.get('foo') sub.stream.once('data', () => { assert.strictEqual(sub.messageBuffers, false) @@ -503,7 +465,7 @@ describe('publish/subscribe', () => { sub.on('pmessageBuffer', (pattern, channel, message) => { assert.strictEqual(pattern.inspect(), Buffer.from('*').inspect()) assert.strictEqual(channel.inspect(), Buffer.from('/foo').inspect()) - sub.quit(end) + sub.quit().then(end) }) assert.notStrictEqual(sub.messageBuffers, sub.buffers) }) @@ -511,16 +473,16 @@ describe('publish/subscribe', () => { batch.psubscribe('*') batch.subscribe('/foo') batch.unsubscribe('/foo') - batch.unsubscribe(helper.isDeepEqual([1, []])) - batch.subscribe(['/foo'], helper.isDeepEqual([2, ['/foo']])) - batch.exec(() => { + batch.unsubscribe() + batch.subscribe(['/foo']) + batch.exec().then(() => { // There's one subscriber to this channel - pub.pubsub('numsub', '/foo', helper.isDeepEqual(['/foo', 1], end)) + pub.pubsub('numsub', '/foo').then(helper.isDeepEqual(['/foo', 1])) // There's exactly one channel that is listened too - pub.pubsub('channels', helper.isDeepEqual(['/foo'], end)) + pub.pubsub('channels').then(helper.isDeepEqual(['/foo'])) // One pattern is active - pub.pubsub('numpat', helper.isNumber(1, end)) - pub.publish('/foo', 'hello world', helper.isNumber(2)) + pub.pubsub('numpat').then(helper.isNumber(1)) + pub.publish('/foo', 'hello world').then(helper.isNumber(2)) }) // Either messageBuffers or buffers has to be true, but not both at the same time sub.on('pmessage', (pattern, channel, message) => { @@ -540,39 +502,28 @@ describe('publish/subscribe', () => { describe('punsubscribe', () => { it('does not complain when punsubscribe is called and there are no subscriptions', () => { - sub.punsubscribe() + return sub.punsubscribe() }) - it('executes callback when punsubscribe is called and there are no subscriptions', (done) => { - pub.batch().punsubscribe(helper.isDeepEqual([0, []])).exec(done) + it('executes when punsubscribe is called and there are no subscriptions', () => { + return pub.batch().punsubscribe(helper.isDeepEqual([0, []])).exec() }) }) describe('fail for other commands while in pub sub mode', () => { - it('return error if only pub sub commands are allowed', (done) => { - sub.subscribe('channel') - // Ping is allowed even if not listed as such! - sub.ping((err, res) => { - assert.strictEqual(err, null) - assert.strictEqual(res[0], 'pong') - }) - // Get is forbidden - sub.get('foo', (err, res) => { - assert(/^ERR only \(P\)SUBSCRIBE \/ \(P\)UNSUBSCRIBE/.test(err.message)) - assert.strictEqual(err.command, 'GET') - }) - // Quit is allowed - sub.quit(done) - }) - - it('emit error if only pub sub commands are allowed without callback', (done) => { - sub.subscribe('channel') - sub.on('error', (err) => { - assert(/^ERR only \(P\)SUBSCRIBE \/ \(P\)UNSUBSCRIBE/.test(err.message)) - assert.strictEqual(err.command, 'GET') - done() - }) - sub.get('foo') + it('return error if only pub sub commands are allowed', () => { + return Promise.all([ + sub.subscribe('channel'), + // Ping is allowed even if not listed as such! + sub.ping().then(helper.isDeepEqual(['pong', ''])), + // Get is forbidden + sub.get('foo').then(helper.fail).catch((err) => { + assert(/^ERR only \(P\)SUBSCRIBE \/ \(P\)UNSUBSCRIBE/.test(err.message)) + assert.strictEqual(err.command, 'GET') + }), + // Quit is allowed + sub.quit() + ]) }) }) @@ -613,31 +564,28 @@ describe('publish/subscribe', () => { pub.set('foo', 'message') pub.set('bar', 'hello') pub.mget('foo', 'bar') - pub.subscribe('channel', () => { - setTimeout(done, 50) - }) + pub.subscribe('channel').then(() => setTimeout(done, 50)) pub.on('message', (msg) => { done(new Error(`This message should not have been published: ${msg}`)) }) }) - it('arguments variants', (done) => { - sub.batch() + it('arguments variants', () => { + return sub.batch() .info(['stats']) .info() .client('KILL', ['type', 'pubsub']) - .client('KILL', ['type', 'pubsub'], () => {}) + .client('KILL', ['type', 'pubsub']) .unsubscribe() .psubscribe(['pattern:*']) .punsubscribe('unknown*') .punsubscribe(['pattern:*']) - .exec((err, res) => { - if (err) throw err - sub.client('kill', ['type', 'pubsub']) - sub.psubscribe('*') - sub.punsubscribe('pa*') - sub.punsubscribe(['a', '*'], done) - }) + .exec().then(() => Promise.all([ + sub.client('kill', ['type', 'pubsub']), + sub.psubscribe('*'), + sub.punsubscribe('pa*'), + sub.punsubscribe(['a', '*']) + ])) }) afterEach(() => { diff --git a/test/rename.spec.js b/test/rename.spec.js index 4dc8af073b..8fd065d4d6 100644 --- a/test/rename.spec.js +++ b/test/rename.spec.js @@ -18,8 +18,8 @@ if (process.platform !== 'win32') { describe(`using ${ip}`, () => { let client = null - beforeEach((done) => { - if (helper.redisProcess().spawnFailed()) return done() + beforeEach(() => { + if (helper.redisProcess().spawnFailed()) return client = redis.createClient({ renameCommands: { set: '807081f5afa96845a02816a28b7258c3', @@ -27,9 +27,7 @@ if (process.platform !== 'win32') { } }) - client.on('ready', () => { - client.flushdb(done) - }) + return client.flushdb() }) afterEach(() => { @@ -37,75 +35,56 @@ if (process.platform !== 'win32') { client.end(true) }) - it('allows to use renamed functions', function (done) { + it('allows to use renamed functions', function () { if (helper.redisProcess().spawnFailed()) this.skip() - client.set('key', 'value', helper.isString('OK')) + client.set('key', 'value').then(helper.isString('OK')) - client.get('key', (err, reply) => { + client.get('key').then(helper.fail).catch((err) => { assert.strictEqual(err.message, 'ERR unknown command \'get\'') assert.strictEqual(err.command, 'GET') - assert.strictEqual(reply, undefined) }) - client.getrange('key', 1, -1, (err, reply) => { - assert.strictEqual(reply, 'alue') - assert.strictEqual(err, null) - done() - }) + return client.getrange('key', 1, -1).then(helper.isString('alue')) }) - it('should also work with batch', function (done) { + it('should also work with batch', function () { if (helper.redisProcess().spawnFailed()) this.skip() client.batch([['set', 'key', 'value']]).exec(helper.isDeepEqual(['OK'])) const batch = client.batch() batch.getrange('key', 1, -1) - batch.exec((err, res) => { - assert(!err) - assert.strictEqual(res.length, 1) - assert.strictEqual(res[0], 'alue') - done() - }) + return batch.exec().then(helper.isDeepEqual(['alue'])) }) - it('should also work with multi', function (done) { + it('should also work with multi', function () { if (helper.redisProcess().spawnFailed()) this.skip() client.multi([['set', 'key', 'value']]).exec(helper.isDeepEqual(['OK'])) const multi = client.multi() multi.getrange('key', 1, -1) - multi.exec((err, res) => { - assert(!err) - assert.strictEqual(res.length, 1) - assert.strictEqual(res[0], 'alue') - done() - }) + return multi.exec().then(helper.isDeepEqual(['alue'])) }) - it('should also work with multi and abort transaction', function (done) { + it('should also work with multi and abort transaction', function () { if (helper.redisProcess().spawnFailed()) this.skip() const multi = client.multi() multi.get('key') - multi.getrange('key', 1, -1, (err, reply) => { - assert.strictEqual(reply, 'alue') - assert.strictEqual(err, null) - }) - multi.exec((err, res) => { + multi.getrange('key', 1, -1) + return multi.exec().then(helper.fail).catch((err) => { assert(err) assert.strictEqual(err.message, 'EXECABORT Transaction discarded because of previous errors.') assert.strictEqual(err.errors[0].message, 'ERR unknown command \'get\'') assert.strictEqual(err.errors[0].command, 'GET') assert.strictEqual(err.code, 'EXECABORT') assert.strictEqual(err.errors[0].code, 'ERR') - done() }) }) - it('should also work prefixed commands', function (done) { + it('should also work prefixed commands', function () { if (helper.redisProcess().spawnFailed()) this.skip() client.end(true) @@ -116,11 +95,7 @@ if (process.platform !== 'win32') { prefix: 'baz' }) client.set('foo', 'bar') - client.keys('*', (err, reply) => { - assert.strictEqual(reply[0], 'bazfoo') - assert.strictEqual(err, null) - done() - }) + return client.keys('*').then(helper.isDeepEqual(['bazfoo'])) }) }) }) diff --git a/test/return_buffers.spec.js b/test/return_buffers.spec.js index 53b6189c2b..8b0d18f5b3 100644 --- a/test/return_buffers.spec.js +++ b/test/return_buffers.spec.js @@ -29,46 +29,42 @@ describe('returnBuffers', () => { assert.strictEqual(msg, 'WARNING: You activated returnBuffers and detectBuffers at the same time. The return value is always going to be a buffer.') end() }) - client.once('error', done) - client.once('connect', () => { - client.flushdb((err) => { - client.hmset('hash key 2', 'key 1', 'val 1', 'key 2', 'val 2') - client.set('string key 1', 'string value') - end(err) - }) - }) + client.flushdb() + client.hmset('hash key 2', 'key 1', 'val 1', 'key 2', 'val 2') + client.set('string key 1', 'string value').then(end) + }) + + afterEach(() => { + client.end(true) }) describe('get', () => { describe('first argument is a string', () => { - it('returns a buffer', (done) => { - client.get('string key 1', (err, reply) => { + it('returns a buffer', () => { + return client.get('string key 1').then((reply) => { assert.strictEqual(true, Buffer.isBuffer(reply)) assert.strictEqual('', reply.inspect()) - return done(err) }) }) - it('returns a bufffer when executed as part of transaction', (done) => { - client.multi().get('string key 1').exec((err, reply) => { + it('returns a buffer when executed as part of transaction', () => { + return client.multi().get('string key 1').exec().then((reply) => { assert.strictEqual(1, reply.length) assert.strictEqual(true, Buffer.isBuffer(reply[0])) assert.strictEqual('', reply[0].inspect()) - return done(err) }) }) }) }) describe('multi.hget', () => { - it('returns buffers', (done) => { - client.multi() + it('returns buffers', () => { + return client.multi() .hget('hash key 2', 'key 1') .hget(Buffer.from('hash key 2'), 'key 1') .hget('hash key 2', Buffer.from('key 2')) .hget('hash key 2', 'key 2') - .exec((err, reply) => { - assert.strictEqual(true, Array.isArray(reply)) + .exec().then((reply) => { assert.strictEqual(4, reply.length) assert.strictEqual('', reply[0].inspect()) assert.strictEqual(true, Buffer.isBuffer(reply[1])) @@ -77,20 +73,18 @@ describe('returnBuffers', () => { assert.strictEqual('', reply[2].inspect()) assert.strictEqual(true, Buffer.isBuffer(reply[3])) assert.strictEqual('', reply[3].inspect()) - return done(err) }) }) }) describe('batch.hget', () => { - it('returns buffers', (done) => { - client.batch() + it('returns buffers', () => { + return client.batch() .hget('hash key 2', 'key 1') .hget(Buffer.from('hash key 2'), 'key 1') .hget('hash key 2', Buffer.from('key 2')) .hget('hash key 2', 'key 2') - .exec((err, reply) => { - assert.strictEqual(true, Array.isArray(reply)) + .exec().then((reply) => { assert.strictEqual(4, reply.length) assert.strictEqual('', reply[0].inspect()) assert.strictEqual(true, Buffer.isBuffer(reply[1])) @@ -99,117 +93,103 @@ describe('returnBuffers', () => { assert.strictEqual('', reply[2].inspect()) assert.strictEqual(true, Buffer.isBuffer(reply[3])) assert.strictEqual('', reply[3].inspect()) - return done(err) }) }) }) describe('hmget', () => { describe('first argument is a string', () => { - it('handles array of strings with undefined values in transaction (repro #344)', (done) => { - client.multi().hmget('hash key 2', 'key 3', 'key 4').exec((err, reply) => { - assert.strictEqual(true, Array.isArray(reply)) + it('handles array of strings with undefined values in transaction (repro #344)', () => { + return client.multi().hmget('hash key 2', 'key 3', 'key 4').exec().then((reply) => { assert.strictEqual(1, reply.length) assert.strictEqual(2, reply[0].length) assert.strictEqual(null, reply[0][0]) assert.strictEqual(null, reply[0][1]) - return done(err) }) }) }) describe('first argument is a buffer', () => { - it('returns buffers for keys requested', (done) => { - client.hmget(Buffer.from('hash key 2'), 'key 1', 'key 2', (err, reply) => { - assert.strictEqual(true, Array.isArray(reply)) + it('returns buffers for keys requested', () => { + return client.hmget(Buffer.from('hash key 2'), 'key 1', 'key 2').then((reply) => { assert.strictEqual(2, reply.length) assert.strictEqual(true, Buffer.isBuffer(reply[0])) assert.strictEqual(true, Buffer.isBuffer(reply[1])) assert.strictEqual('', reply[0].inspect()) assert.strictEqual('', reply[1].inspect()) - return done(err) }) }) - it('returns buffers for keys requested in transaction', (done) => { - client.multi().hmget(Buffer.from('hash key 2'), 'key 1', 'key 2').exec((err, reply) => { - assert.strictEqual(true, Array.isArray(reply)) + it('returns buffers for keys requested in transaction', () => { + return client.multi().hmget(Buffer.from('hash key 2'), 'key 1', 'key 2').exec().then((reply) => { assert.strictEqual(1, reply.length) assert.strictEqual(2, reply[0].length) assert.strictEqual(true, Buffer.isBuffer(reply[0][0])) assert.strictEqual(true, Buffer.isBuffer(reply[0][1])) assert.strictEqual('', reply[0][0].inspect()) assert.strictEqual('', reply[0][1].inspect()) - return done(err) }) }) - it('returns buffers for keys requested in .batch', (done) => { - client.batch().hmget(Buffer.from('hash key 2'), 'key 1', 'key 2').exec((err, reply) => { - assert.strictEqual(true, Array.isArray(reply)) + it('returns buffers for keys requested in .batch', () => { + return client.batch().hmget(Buffer.from('hash key 2'), 'key 1', 'key 2').exec().then((reply) => { assert.strictEqual(1, reply.length) assert.strictEqual(2, reply[0].length) assert.strictEqual(true, Buffer.isBuffer(reply[0][0])) assert.strictEqual(true, Buffer.isBuffer(reply[0][1])) assert.strictEqual('', reply[0][0].inspect()) assert.strictEqual('', reply[0][1].inspect()) - return done(err) }) }) }) }) - describe('hgetall', (done) => { + describe('hgetall', () => { describe('first argument is a string', () => { - it('returns buffer values', (done) => { - client.hgetall('hash key 2', (err, reply) => { + it('returns buffer values', () => { + return client.hgetall('hash key 2').then((reply) => { assert.strictEqual('object', typeof reply) assert.strictEqual(2, Object.keys(reply).length) assert.strictEqual('', reply['key 1'].inspect()) assert.strictEqual('', reply['key 2'].inspect()) - return done(err) }) }) - it('returns buffer values when executed in transaction', (done) => { - client.multi().hgetall('hash key 2').exec((err, reply) => { + it('returns buffer values when executed in transaction', () => { + return client.multi().hgetall('hash key 2').exec().then((reply) => { assert.strictEqual(1, reply.length) assert.strictEqual('object', typeof reply[0]) assert.strictEqual(2, Object.keys(reply[0]).length) assert.strictEqual('', reply[0]['key 1'].inspect()) assert.strictEqual('', reply[0]['key 2'].inspect()) - return done(err) }) }) - it('returns buffer values when executed in .batch', (done) => { - client.batch().hgetall('hash key 2').exec((err, reply) => { + it('returns buffer values when executed in .batch', () => { + return client.batch().hgetall('hash key 2').exec().then((reply) => { assert.strictEqual(1, reply.length) assert.strictEqual('object', typeof reply[0]) assert.strictEqual(2, Object.keys(reply[0]).length) assert.strictEqual('', reply[0]['key 1'].inspect()) assert.strictEqual('', reply[0]['key 2'].inspect()) - return done(err) }) }) }) describe('first argument is a buffer', () => { - it('returns buffer values', (done) => { - client.hgetall(Buffer.from('hash key 2'), (err, reply) => { - assert.strictEqual(null, err) + it('returns buffer values', () => { + return client.hgetall(Buffer.from('hash key 2')).then((reply) => { assert.strictEqual('object', typeof reply) assert.strictEqual(2, Object.keys(reply).length) assert.strictEqual(true, Buffer.isBuffer(reply['key 1'])) assert.strictEqual(true, Buffer.isBuffer(reply['key 2'])) assert.strictEqual('', reply['key 1'].inspect()) assert.strictEqual('', reply['key 2'].inspect()) - return done(err) }) }) - it('returns buffer values when executed in transaction', (done) => { - client.multi().hgetall(Buffer.from('hash key 2')).exec((err, reply) => { + it('returns buffer values when executed in transaction', () => { + return client.multi().hgetall(Buffer.from('hash key 2')).exec().then((reply) => { assert.strictEqual(1, reply.length) assert.strictEqual('object', typeof reply[0]) assert.strictEqual(2, Object.keys(reply[0]).length) @@ -217,12 +197,11 @@ describe('returnBuffers', () => { assert.strictEqual(true, Buffer.isBuffer(reply[0]['key 2'])) assert.strictEqual('', reply[0]['key 1'].inspect()) assert.strictEqual('', reply[0]['key 2'].inspect()) - return done(err) }) }) - it('returns buffer values when executed in .batch', (done) => { - client.batch().hgetall(Buffer.from('hash key 2')).exec((err, reply) => { + it('returns buffer values when executed in .batch', () => { + return client.batch().hgetall(Buffer.from('hash key 2')).exec().then((reply) => { assert.strictEqual(1, reply.length) assert.strictEqual('object', typeof reply[0]) assert.strictEqual(2, Object.keys(reply[0]).length) @@ -230,15 +209,15 @@ describe('returnBuffers', () => { assert.strictEqual(true, Buffer.isBuffer(reply[0]['key 2'])) assert.strictEqual('', reply[0]['key 1'].inspect()) assert.strictEqual('', reply[0]['key 2'].inspect()) - return done(err) }) }) }) }) - describe('publish/subscribe', (done) => { + describe('publish/subscribe', () => { let pub let sub + const channel = 'test channel' const message = Buffer.from('test message') @@ -247,25 +226,12 @@ describe('returnBuffers', () => { }) beforeEach((done) => { - let pubConnected - let subConnected + const end = helper.callFuncAfter(done, 2) pub = redis.createClient.apply(redis.createClient, basicArgs) sub = redis.createClient.apply(null, args) - pub.once('connect', () => { - pub.flushdb(() => { - pubConnected = true - if (subConnected) { - done() - } - }) - }) - sub.once('connect', () => { - subConnected = true - if (pubConnected) { - done() - } - }) + pub.flushdb().then(end) + sub.once('connect', end) }) it('receives buffer messages', (done) => { @@ -276,7 +242,7 @@ describe('returnBuffers', () => { sub.on('message', (chnl, msg) => { assert.strictEqual(true, Buffer.isBuffer(msg)) assert.strictEqual('', msg.inspect()) - return done() + done() }) sub.subscribe(channel) diff --git a/test/tls.spec.js b/test/tls.spec.js index 4c2f49d67f..04c1706b6b 100644 --- a/test/tls.spec.js +++ b/test/tls.spec.js @@ -86,7 +86,7 @@ describe.skip('TLS connection tests', () => { }) describe('when not connected', () => { - it('connect with host and port provided in the tls object', function (done) { + it('connect with host and port provided in the tls object', function () { if (skip) this.skip() const tls = utils.clone(tlsOptions) tls.port = tlsPort @@ -103,10 +103,10 @@ describe.skip('TLS connection tests', () => { assert(client.stream.encrypted) client.set('foo', 'bar') - client.get('foo', helper.isString('bar', done)) + return client.get('foo').then(helper.isString('bar')) }) - it('fails to connect because the cert is not correct', function (done) { + it('fails to connect because the cert is not correct', function () { if (skip) this.skip() const faultyCert = utils.clone(tlsOptions) faultyCert.ca = [ String(fs.readFileSync(path.resolve(__dirname, './conf/faulty.cert'))) ] @@ -121,7 +121,7 @@ describe.skip('TLS connection tests', () => { assert(/DEPTH_ZERO_SELF_SIGNED_CERT/.test(err.code || err.message), err) client.end(true) }) - client.set('foo', 'bar', helper.isError(done)) + return client.set('foo', 'bar').then(helper.isError()) }) }) }) diff --git a/test/utils.spec.js b/test/utils.spec.js index f01ea9d59a..a0e15f435d 100644 --- a/test/utils.spec.js +++ b/test/utils.spec.js @@ -15,7 +15,7 @@ describe('utils.js', () => { fn: function noop () {} } const clone = utils.clone(obj) - assert.deepEqual(clone, obj) + assert.deepStrictEqual(clone, obj) assert.strictEqual(obj.fn, clone.fn) assert(typeof clone.fn === 'function') }) @@ -42,9 +42,7 @@ describe('utils.js', () => { describe('replyInOrder', () => { let errCount = 0 let resCount = 0 - let emitted = false const clientMock = { - emit () { emitted = true }, offlineQueue: new Queue(), commandQueue: new Queue() } @@ -62,7 +60,6 @@ describe('utils.js', () => { clientMock.commandQueue.clear() errCount = 0 resCount = 0 - emitted = false }) it('no elements in either queue. Reply in the next tick with callback', (done) => { @@ -74,16 +71,6 @@ describe('utils.js', () => { assert(!called) }) - it('no elements in either queue. Reply in the next tick without callback', (done) => { - assert(!emitted) - utils.replyInOrder(clientMock, null, new Error('tada')) - assert(!emitted) - setTimeout(() => { - assert(emitted) - done() - }, 1) - }) - it('elements in the offline queue. Reply after the offline queue is empty and respect the commandObj callback', (done) => { clientMock.offlineQueue.push(createCommandObj(), createCommandObj()) utils.replyInOrder(clientMock, () => { @@ -95,11 +82,10 @@ describe('utils.js', () => { }) it('elements in the offline queue. Reply after the offline queue is empty and respect the commandObj error emit', (done) => { - clientMock.commandQueue.push({}, createCommandObj(), {}) + clientMock.commandQueue.push(createCommandObj(), createCommandObj(), createCommandObj()) utils.replyInOrder(clientMock, () => { assert.strictEqual(clientMock.commandQueue.length, 0) - assert(emitted) - assert.strictEqual(errCount, 1) + assert.strictEqual(errCount, 3) assert.strictEqual(resCount, 0) done() }, null, null) @@ -113,13 +99,12 @@ describe('utils.js', () => { it('elements in the offline queue and the commandQueue. Reply all other commands got handled respect the commandObj', (done) => { clientMock.commandQueue.push(createCommandObj(), createCommandObj()) - clientMock.offlineQueue.push(createCommandObj(), {}) + clientMock.offlineQueue.push(createCommandObj(), createCommandObj()) utils.replyInOrder(clientMock, (err, res) => { if (err) throw err assert.strictEqual(clientMock.commandQueue.length, 0) assert.strictEqual(clientMock.offlineQueue.length, 0) - assert(!emitted) - assert.strictEqual(resCount, 3) + assert.strictEqual(resCount, 4) done() }, null, null) while (clientMock.offlineQueue.length) {