You've already forked node-redis
mirror of
https://github.com/redis/node-redis.git
synced 2025-08-06 02:15:48 +03:00
chore: refactor codebase to promises
This commit is contained in:
@@ -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 {
|
||||
return this.batch()
|
||||
}
|
||||
const promises = []
|
||||
while (pipeline < this.maxPipeline) {
|
||||
this.commandsSent++
|
||||
pipeline++
|
||||
this.sendNext()
|
||||
}
|
||||
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()
|
||||
})
|
||||
}
|
||||
|
||||
|
18
changelog.md
18
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
|
||||
|
163
index.js
163
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)
|
||||
}
|
||||
}
|
||||
}
|
||||
// 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,26 +380,52 @@ 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) {
|
||||
RedisClient.prototype.onInfoFail = function (err) {
|
||||
if (this.closing) {
|
||||
return
|
||||
}
|
||||
|
||||
if (err.message === "ERR unknown command 'info'") {
|
||||
this.onReady()
|
||||
return
|
||||
@@ -408,8 +433,9 @@ RedisClient.prototype.onInfoCmd = function (err, res) {
|
||||
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,14 +548,12 @@ RedisClient.prototype.connectionGone = function (why, error) {
|
||||
return
|
||||
}
|
||||
|
||||
if (typeof this.options.retryStrategy === 'function') {
|
||||
const retryParams = {
|
||||
this.retryDelay = this.retryStrategy({
|
||||
attempt: this.attempts,
|
||||
error,
|
||||
totalRetryTime: this.retryTotaltime,
|
||||
timesConnected: this.timesConnected
|
||||
}
|
||||
this.retryDelay = this.options.retryStrategy(retryParams)
|
||||
})
|
||||
if (typeof this.retryDelay !== 'number') {
|
||||
// Pass individual error through
|
||||
if (this.retryDelay instanceof Error) {
|
||||
@@ -529,10 +565,13 @@ RedisClient.prototype.connectionGone = function (why, error) {
|
||||
}, {
|
||||
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
|
||||
if (this.options.retryUnfulfilledCommands) {
|
||||
@@ -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')
|
||||
}
|
||||
}
|
||||
|
||||
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])
|
||||
}
|
||||
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)
|
||||
}
|
||||
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')
|
||||
|
@@ -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) {
|
||||
|
@@ -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) {
|
||||
|
@@ -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) {
|
||||
|
@@ -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
|
||||
}
|
||||
|
@@ -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
|
||||
|
@@ -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,23 +107,27 @@ 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) {
|
||||
if (err) {
|
||||
self.serverInfo = {}
|
||||
return err
|
||||
}
|
||||
|
||||
const obj = {}
|
||||
const lines = res.toString().split('\r\n')
|
||||
let line, parts, subParts
|
||||
var line, parts, subParts
|
||||
|
||||
for (let i = 0; i < lines.length; i++) {
|
||||
parts = lines[i].split(':')
|
||||
@@ -151,89 +152,70 @@ function infoCallback (self, callback) {
|
||||
}
|
||||
// Expose info key/values to users
|
||||
self.serverInfo = obj
|
||||
} else {
|
||||
self.serverInfo = {}
|
||||
}
|
||||
utils.callbackOrEmit(self, callback, err, res)
|
||||
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
|
||||
}
|
||||
|
169
lib/multi.js
169
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) {
|
||||
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])
|
||||
}
|
||||
} 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])
|
||||
}
|
||||
}
|
||||
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)
|
||||
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
|
||||
}))
|
||||
}
|
||||
self._client.uncork()
|
||||
return
|
||||
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)
|
||||
}
|
||||
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)
|
||||
}
|
||||
// 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
|
||||
|
34
lib/utils.js
34
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,20 +70,14 @@ 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) {
|
||||
commandObj.callback = function (e, r) {
|
||||
tmp(e, r)
|
||||
callbackOrEmit(self, callback, err, res)
|
||||
}
|
||||
: function (e) {
|
||||
if (e) {
|
||||
self.emit('error', e)
|
||||
}
|
||||
callbackOrEmit(self, callback, err, res)
|
||||
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
|
||||
}
|
||||
|
@@ -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",
|
||||
|
@@ -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)
|
||||
})
|
||||
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', () => {
|
||||
const promises = []
|
||||
// Set a key so the used database is returned in the info command
|
||||
client.set('foo', 'bar')
|
||||
client.get('foo')
|
||||
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)
|
||||
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,16 +207,13 @@ 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', () => {
|
||||
client.subscribe('somechannel', 'another channel').then(() => {
|
||||
assert.strictEqual(client.pubSubMode, 1)
|
||||
client.get('foo', (err) => {
|
||||
client.get('foo').catch((err) => {
|
||||
assert(/ERR only \(P\)SUBSCRIBE \/ \(P\)UNSUBSCRIBE/.test(err.message))
|
||||
done()
|
||||
})
|
||||
})
|
||||
})
|
||||
client.once('ready', () => {
|
||||
// Coherent behavior with all other offline commands fires commands before emitting but does not wait till they return
|
||||
assert.strictEqual(client.pubSubMode, 2)
|
||||
@@ -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,
|
||||
|
@@ -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()
|
||||
})
|
||||
})
|
||||
})
|
||||
|
@@ -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(() => {
|
||||
|
@@ -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'))
|
||||
])
|
||||
})
|
||||
})
|
||||
})
|
||||
|
@@ -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()
|
||||
})
|
||||
})
|
||||
})
|
||||
|
@@ -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(() => {
|
||||
|
@@ -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('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('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('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)
|
||||
})
|
||||
})
|
||||
])
|
||||
})
|
||||
})
|
||||
})
|
||||
|
@@ -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(() => {
|
||||
|
@@ -9,28 +9,34 @@ 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))
|
||||
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(() => {
|
||||
client.exists(['expiry key'], helper.isNumber(0, done))
|
||||
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))
|
||||
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(() => {
|
||||
client.exists(['expiry key'], helper.isNumber(0, done))
|
||||
resolve(client.exists(['expiry key']).then(helper.isNumber(0)))
|
||||
}, 1050)
|
||||
})
|
||||
])
|
||||
})
|
||||
|
||||
afterEach(() => {
|
||||
client.end(true)
|
||||
|
@@ -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))
|
||||
])
|
||||
})
|
||||
})
|
||||
})
|
||||
|
@@ -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(() => {
|
||||
|
@@ -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())
|
||||
})
|
||||
})
|
||||
})
|
||||
|
@@ -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())
|
||||
})
|
||||
})
|
||||
})
|
||||
|
@@ -8,69 +8,62 @@ const redis = config.redis
|
||||
|
||||
describe('The \'hgetall\' method', () => {
|
||||
helper.allTests((ip, args) => {
|
||||
describe(`using ${ip}`, () => {
|
||||
let client
|
||||
|
||||
describe(`using ${ip}`, () => {
|
||||
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) => {
|
||||
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((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)
|
||||
assert.strictEqual(weirdKey.toString('binary'), Object.keys(obj)[3])
|
||||
assert.strictEqual(weirdValue.toString('binary'), obj[weirdKey.toString('binary')].toString('binary'))
|
||||
})
|
||||
])
|
||||
})
|
||||
})
|
||||
|
||||
|
@@ -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(() => {
|
||||
|
@@ -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(() => {
|
||||
|
@@ -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))
|
||||
})
|
||||
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(() => {
|
||||
|
@@ -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(() => {
|
||||
|
@@ -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(() => {
|
||||
|
@@ -1,6 +1,5 @@
|
||||
'use strict'
|
||||
|
||||
const assert = require('assert')
|
||||
const config = require('../lib/config')
|
||||
const helper = require('../helper')
|
||||
const redis = config.redis
|
||||
@@ -27,43 +26,37 @@ describe('The \'incr\' method', () => {
|
||||
9007199254740997 -> 9007199254740996
|
||||
...
|
||||
*/
|
||||
it('count above the safe integers as numbers', (done) => {
|
||||
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'))
|
||||
})
|
||||
})
|
||||
})
|
||||
|
@@ -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')
|
||||
client.info()
|
||||
setTimeout(() => {
|
||||
return client.info().then(() => {
|
||||
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)
|
||||
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')
|
||||
done()
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
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
|
||||
})
|
||||
})
|
||||
})
|
||||
|
@@ -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(() => {
|
||||
|
@@ -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)
|
||||
})
|
||||
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(() => {
|
||||
|
@@ -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())
|
||||
})
|
||||
}
|
||||
})
|
||||
|
@@ -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()
|
||||
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))
|
||||
})
|
||||
})
|
||||
})
|
||||
|
@@ -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(() => {
|
||||
|
@@ -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)
|
||||
})
|
||||
})
|
||||
|
||||
|
@@ -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(() => {
|
||||
|
@@ -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(() => {
|
||||
|
@@ -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(() => {
|
||||
|
@@ -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)
|
||||
})
|
||||
})
|
||||
|
||||
|
@@ -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(() => {
|
||||
|
@@ -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]))
|
||||
})
|
||||
})
|
||||
})
|
||||
|
@@ -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)
|
||||
})
|
||||
})
|
||||
|
||||
|
@@ -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' ])
|
||||
})
|
||||
})
|
||||
|
||||
|
@@ -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) => {
|
||||
it('selects the appropriate database', () => {
|
||||
assert.strictEqual(client.selectedDb, undefined, 'default db should be undefined')
|
||||
client.select(1, (err) => {
|
||||
assert.strictEqual(err, null)
|
||||
return client.select(1).then(() => {
|
||||
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) => {
|
||||
it('returns an error', () => {
|
||||
assert.strictEqual(client.selectedDb, undefined, 'default db should be undefined')
|
||||
client.select(9999, (err) => {
|
||||
return client.select(9999).then(assert, (err) => {
|
||||
assert.strictEqual(err.code, 'ERR')
|
||||
assert.strictEqual(err.message, 'ERR invalid DB index')
|
||||
done()
|
||||
})
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
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('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')
|
||||
|
@@ -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()
|
||||
})
|
||||
})
|
||||
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) => {
|
||||
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
|
||||
done(err)
|
||||
})
|
||||
})
|
||||
|
||||
it('set expire date in milliseconds', (done) => {
|
||||
client.set('foo', 'bar', 'px', 100, helper.isString('OK'))
|
||||
client.pttl('foo', (err, res) => {
|
||||
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
|
||||
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) => {
|
||||
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
|
||||
done(err)
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
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('empty array as second parameter', (done) => {
|
||||
client.set('foo', [], (err, res) => {
|
||||
assert.strictEqual(err.message, 'ERR wrong number of arguments for \'set\' command')
|
||||
done()
|
||||
})
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
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()
|
||||
it('empty array as second parameter', () => {
|
||||
return client.set('foo', [])
|
||||
.then(assert, helper.isError(/ERR wrong number of arguments for 'set' command/))
|
||||
})
|
||||
})
|
||||
})
|
||||
|
@@ -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(() => {
|
||||
|
@@ -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(() => {
|
||||
|
@@ -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)
|
||||
})
|
||||
})
|
||||
|
||||
|
@@ -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(() => {
|
||||
|
@@ -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(() => {
|
||||
|
@@ -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'])
|
||||
})
|
||||
})
|
||||
|
||||
|
@@ -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' ])
|
||||
})
|
||||
})
|
||||
|
||||
|
@@ -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(() => {
|
||||
|
@@ -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)
|
||||
})
|
||||
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']))
|
||||
})
|
||||
})
|
||||
|
||||
|
@@ -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(() => {
|
||||
|
@@ -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)
|
||||
})
|
||||
})
|
||||
|
||||
|
@@ -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'])
|
||||
})
|
||||
})
|
||||
|
||||
|
@@ -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'])
|
||||
})
|
||||
})
|
||||
|
||||
|
@@ -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)
|
||||
})
|
||||
})
|
||||
|
||||
|
@@ -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(() => {
|
||||
|
@@ -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'))
|
||||
})
|
||||
})
|
||||
})
|
||||
|
@@ -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()
|
||||
})
|
||||
})
|
||||
|
||||
|
@@ -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()
|
||||
})
|
||||
})
|
||||
|
||||
|
@@ -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(() => {
|
||||
|
@@ -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)
|
||||
})
|
||||
|
@@ -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', () => {
|
||||
|
@@ -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')
|
||||
})
|
||||
})
|
||||
})
|
||||
|
@@ -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')
|
||||
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')
|
||||
return done(err)
|
||||
})
|
||||
})
|
||||
])
|
||||
})
|
||||
|
||||
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('<Buffer 73 74 72 69 6e 67 20 76 61 6c 75 65>', 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('<Buffer 73 74 72 69 6e 67 20 76 61 6c 75 65>', 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('<Buffer 76 61 6c 20 32>', 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('<Buffer 76 61 6c 20 32>', 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('<Buffer 76 61 6c 20 31>', reply[0].inspect())
|
||||
assert.strictEqual('<Buffer 76 61 6c 20 32>', 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('<Buffer 76 61 6c 20 31>', reply[0][0].inspect())
|
||||
assert.strictEqual('<Buffer 76 61 6c 20 32>', 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('<Buffer 76 61 6c 20 31>', reply[0][0].inspect())
|
||||
assert.strictEqual('<Buffer 76 61 6c 20 32>', 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('<Buffer 76 61 6c 20 31>', reply['key 1'].inspect())
|
||||
assert.strictEqual('<Buffer 76 61 6c 20 32>', 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('<Buffer 76 61 6c 20 31>', reply[0]['key 1'].inspect())
|
||||
assert.strictEqual('<Buffer 76 61 6c 20 32>', 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('<Buffer 76 61 6c 20 31>', reply[0]['key 1'].inspect())
|
||||
assert.strictEqual('<Buffer 76 61 6c 20 32>', reply[0]['key 2'].inspect())
|
||||
return done(err)
|
||||
})
|
||||
})
|
||||
})
|
||||
|
@@ -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()
|
||||
})
|
||||
})
|
||||
})
|
||||
|
107
test/helper.js
107
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()
|
||||
|
@@ -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,
|
||||
|
@@ -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)
|
||||
|
@@ -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,10 +23,12 @@ 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) => {
|
||||
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)
|
||||
|
@@ -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)
|
||||
multi.exec().then((res) => {
|
||||
assert.strictEqual(res.length, 26241)
|
||||
})
|
||||
client.flushdb(done)
|
||||
})
|
||||
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()
|
||||
})
|
||||
})
|
||||
})
|
||||
|
@@ -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)
|
||||
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, {})
|
||||
done()
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
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) => {
|
||||
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()
|
||||
})
|
||||
}, 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')
|
||||
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()
|
||||
}
|
||||
})
|
||||
})
|
||||
})
|
||||
@@ -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()
|
||||
}
|
||||
})
|
||||
})
|
||||
})
|
||||
})
|
||||
})
|
||||
|
@@ -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) => {
|
||||
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')
|
||||
assert.strictEqual(err, null)
|
||||
})
|
||||
client.exists('key', helper.isNumber(1))
|
||||
}),
|
||||
client.exists('key').then(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)
|
||||
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.indexOf('test:prefix:key') !== -1)
|
||||
assert(res.indexOf('test:prefix:key2') !== -1)
|
||||
assert(res.indexOf('test:prefix:key3') !== -1)
|
||||
done()
|
||||
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)
|
||||
})
|
||||
})
|
||||
})
|
||||
|
@@ -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')
|
||||
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')
|
||||
sub.unsubscribe('chan9')
|
||||
pub.publish('chan8', 'something')
|
||||
sub.subscribe('chan9', done)
|
||||
])
|
||||
})
|
||||
|
||||
it('handles SUB UNSUB MSG SUB 2', (done) => {
|
||||
sub.psubscribe('abc*', helper.isDeepEqual([1, ['abc*']]))
|
||||
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')
|
||||
sub.unsubscribe('xyz')
|
||||
pub.publish('abcd', 'something')
|
||||
sub.subscribe('xyz', done)
|
||||
])
|
||||
})
|
||||
|
||||
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')
|
||||
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((err, res) => {
|
||||
assert.strictEqual(err, null)
|
||||
assert.strictEqual(res[0], 'pong')
|
||||
})
|
||||
sub.ping().then(helper.isDeepEqual(['pong', ''])),
|
||||
// Get is forbidden
|
||||
sub.get('foo', (err, res) => {
|
||||
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(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')
|
||||
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(() => {
|
||||
|
@@ -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']))
|
||||
})
|
||||
})
|
||||
})
|
||||
|
@@ -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.flushdb()
|
||||
client.hmset('hash key 2', 'key 1', 'val 1', 'key 2', 'val 2')
|
||||
client.set('string key 1', 'string value')
|
||||
end(err)
|
||||
})
|
||||
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('<Buffer 73 74 72 69 6e 67 20 76 61 6c 75 65>', 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('<Buffer 73 74 72 69 6e 67 20 76 61 6c 75 65>', 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('<Buffer 76 61 6c 20 31>', reply[0].inspect())
|
||||
assert.strictEqual(true, Buffer.isBuffer(reply[1]))
|
||||
@@ -77,20 +73,18 @@ describe('returnBuffers', () => {
|
||||
assert.strictEqual('<Buffer 76 61 6c 20 32>', reply[2].inspect())
|
||||
assert.strictEqual(true, Buffer.isBuffer(reply[3]))
|
||||
assert.strictEqual('<Buffer 76 61 6c 20 32>', 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('<Buffer 76 61 6c 20 31>', reply[0].inspect())
|
||||
assert.strictEqual(true, Buffer.isBuffer(reply[1]))
|
||||
@@ -99,117 +93,103 @@ describe('returnBuffers', () => {
|
||||
assert.strictEqual('<Buffer 76 61 6c 20 32>', reply[2].inspect())
|
||||
assert.strictEqual(true, Buffer.isBuffer(reply[3]))
|
||||
assert.strictEqual('<Buffer 76 61 6c 20 32>', 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('<Buffer 76 61 6c 20 31>', reply[0].inspect())
|
||||
assert.strictEqual('<Buffer 76 61 6c 20 32>', 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('<Buffer 76 61 6c 20 31>', reply[0][0].inspect())
|
||||
assert.strictEqual('<Buffer 76 61 6c 20 32>', 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('<Buffer 76 61 6c 20 31>', reply[0][0].inspect())
|
||||
assert.strictEqual('<Buffer 76 61 6c 20 32>', 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('<Buffer 76 61 6c 20 31>', reply['key 1'].inspect())
|
||||
assert.strictEqual('<Buffer 76 61 6c 20 32>', 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('<Buffer 76 61 6c 20 31>', reply[0]['key 1'].inspect())
|
||||
assert.strictEqual('<Buffer 76 61 6c 20 32>', 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('<Buffer 76 61 6c 20 31>', reply[0]['key 1'].inspect())
|
||||
assert.strictEqual('<Buffer 76 61 6c 20 32>', 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('<Buffer 76 61 6c 20 31>', reply['key 1'].inspect())
|
||||
assert.strictEqual('<Buffer 76 61 6c 20 32>', 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('<Buffer 76 61 6c 20 31>', reply[0]['key 1'].inspect())
|
||||
assert.strictEqual('<Buffer 76 61 6c 20 32>', 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('<Buffer 76 61 6c 20 31>', reply[0]['key 1'].inspect())
|
||||
assert.strictEqual('<Buffer 76 61 6c 20 32>', 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('<Buffer 74 65 73 74 20 6d 65 73 73 61 67 65>', msg.inspect())
|
||||
return done()
|
||||
done()
|
||||
})
|
||||
|
||||
sub.subscribe(channel)
|
||||
|
@@ -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())
|
||||
})
|
||||
})
|
||||
})
|
||||
|
@@ -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) {
|
||||
|
Reference in New Issue
Block a user