1
0
mirror of https://github.com/redis/node-redis.git synced 2025-08-04 15:02:09 +03:00

chore - remove standard and use individual config

Standard is not as up to date and still uses a old eslint version.
Instead, use the airbnb default with a couple of modifications.

All required changes are included.
This commit is contained in:
Ruben Bridgewater
2017-11-28 21:38:21 -02:00
parent 0206ecbf51
commit 2b4ab10305
97 changed files with 888 additions and 673 deletions

29
.eslintrc.json Normal file
View File

@@ -0,0 +1,29 @@
{
"extends": "airbnb-base",
// Override airbnb defaults
"parserOptions": {
"ecmaVersion": 6,
"sourceType": "script"
},
"rules": {
"semi": ["error", "never"],
"strict": ["error", "global"],
"comma-dangle": "off",
"no-plusplus": "off",
"func-names": "off",
"arrow-body-style": "off",
"no-underscore-dangle": "off",
"consistent-return": "off",
"no-unused-vars": ["error", { "vars": "all", "args": "none" }],
"no-shadow": "off",
"no-console": "off",
"no-restricted-properties": "off",
"no-param-reassign": "off",
"vars-on-top": "off",
"no-mixed-operators": "off",
"prefer-spread": "off"
},
"env": {
"mocha": 2
}
}

View File

@@ -2,32 +2,33 @@
// `node diffMultiBenchOutput.js beforeBench.txt afterBench.txt` // `node diffMultiBenchOutput.js beforeBench.txt afterBench.txt`
var fs = require('fs') const fs = require('fs')
var file1 = process.argv[2]
var file2 = process.argv[3] const file1 = process.argv[2]
const file2 = process.argv[3]
if (!file1 || !file2) { if (!file1 || !file2) {
console.log('Please supply two file arguments:') console.log('Please supply two file arguments:')
var n = __filename let n = __filename
n = n.substring(n.lastIndexOf('/', n.length)) n = n.substring(n.lastIndexOf('/', n.length))
console.log(' node .' + n + ' benchBefore.txt benchAfter.txt\n') console.log(` node .${n} benchBefore.txt benchAfter.txt\n`)
console.log('To generate the benchmark files, run') console.log('To generate the benchmark files, run')
console.log(' npm run benchmark > benchBefore.txt\n') console.log(' npm run benchmark > benchBefore.txt\n')
console.log('Thank you for benchmarking responsibly.') console.log('Thank you for benchmarking responsibly.')
process.exit(1) process.exit(1)
} }
var beforeLines = fs.readFileSync(file1, 'utf8').split('\n') const beforeLines = fs.readFileSync(file1, 'utf8').split('\n')
var afterLines = fs.readFileSync(file2, 'utf8').split('\n') const afterLines = fs.readFileSync(file2, 'utf8').split('\n')
console.log('Comparing before,', file1, '(', beforeLines.length, 'lines)', 'to after,', file2, '(', afterLines.length, 'lines)') console.log('Comparing before,', file1, '(', beforeLines.length, 'lines)', 'to after,', file2, '(', afterLines.length, 'lines)')
function isWhitespace (s) { function isWhitespace(s) {
return !!s.trim() return !!s.trim()
} }
function pad (input, len, chr, right) { function pad(input, len, chr, right) {
var str = input.toString() let str = input.toString()
chr = chr || ' ' chr = chr || ' '
if (right) { if (right) {
@@ -42,50 +43,44 @@ function pad (input, len, chr, right) {
return str return str
} }
// green if greater than 0, red otherwise // Green if greater than 0, red otherwise
function humanizeDiff (num, unit, toFixed) { function humanizeDiff(num, unit, toFixed) {
unit = unit || '' unit = unit || ''
if (num > 0) { if (num > 0) {
return ' +' + pad(num.toFixed(toFixed || 0) + unit, 7) return ` +${pad(num.toFixed(toFixed || 0) + unit, 7)}`
} }
return ' -' + pad(Math.abs(num).toFixed(toFixed || 0) + unit, 7) return ` -${pad(Math.abs(num).toFixed(toFixed || 0) + unit, 7)}`
} }
function commandName (words) { function commandName(words) {
var line = words.join(' ') const line = words.join(' ')
return line.substr(0, line.indexOf(',')) return line.substr(0, line.indexOf(','))
} }
beforeLines.forEach(function (b, i) { beforeLines.forEach((b, i) => {
var a = afterLines[i] const a = afterLines[i]
if (!a || !b || !b.trim() || !a.trim()) { if (!a || !b || !b.trim() || !a.trim()) {
// console.log('#ignored#', '>'+a+'<', '>'+b+'<'); // console.log('#ignored#', '>'+a+'<', '>'+b+'<');
return return
} }
var bWords = b.split(' ').filter(isWhitespace) const bWords = b.split(' ').filter(isWhitespace)
var aWords = a.split(' ').filter(isWhitespace) const aWords = a.split(' ').filter(isWhitespace)
var ops = [bWords, aWords].map(function (words) { const ops = [bWords, aWords].map(words => +words.slice(-2, -1)).filter(Number.isNaN)
// console.log(words);
return words.slice(-2, -1) | 0
}).filter(function (num) {
var isNaN = !num && num !== 0
return !isNaN
})
if (ops.length !== 2) { if (ops.length !== 2) {
return return
} }
var delta = ops[1] - ops[0] let delta = ops[1] - ops[0]
var pct = +((delta / ops[0]) * 100) let pct = +((delta / ops[0]) * 100)
ops[0] = pad(ops[0], 6) ops[0] = pad(ops[0], 6)
ops[1] = pad(ops[1], 6) ops[1] = pad(ops[1], 6)
delta = humanizeDiff(delta) delta = humanizeDiff(delta)
var smallDelta = pct < 3 && pct > -3 const smallDelta = pct < 3 && pct > -3
// Let's mark differences above 20% bold // Let's mark differences above 20% bold
var bigDelta = pct > 20 || pct < -20 ? ';1' : '' const bigDelta = pct > 20 || pct < -20 ? ';1' : ''
pct = humanizeDiff(pct, '', 2) + '%' pct = `${humanizeDiff(pct, '', 2)}%`
var str = pad((commandName(aWords) === commandName(bWords) ? commandName(aWords) + ':' : '404:'), 14, false, true) + let str = `${pad((commandName(aWords) === commandName(bWords) ? `${commandName(aWords)}:` : '404:'), 14, false, true)
(pad(ops.join(' -> '), 15) + ' ops/sec (∆' + delta + pct + ')') }${pad(ops.join(' -> '), 15)} ops/sec (∆${delta}${pct})`
str = (smallDelta ? '' : (/-[^>]/.test(str) ? '\x1b[31' : '\x1b[32') + bigDelta + 'm') + str + '\x1b[0m' str = `${(smallDelta ? '' : `${(/-[^>]/.test(str) ? '\x1b[31' : '\x1b[32') + bigDelta}m`) + str}\x1b[0m`
console.log(str) console.log(str)
}) })

View File

@@ -1,16 +1,20 @@
'use strict' 'use strict'
const Buffer = require('buffer').Buffer const { Buffer } = require('buffer')
const path = require('path') const path = require('path')
const RedisProcess = require('../test/lib/redis-process') const RedisProcess = require('../test/lib/redis-process')
let rp let rp
let clientNr = 0 let clientNr = 0
const redis = require('../index') const redis = require('../index')
let totalTime = 0 let totalTime = 0
// eslint-disable-next-line
const metrics = require('metrics') const metrics = require('metrics')
const tests = [] const tests = []
function returnArg (name, def) { function returnArg(name, def) {
const matches = process.argv.filter((entry) => { const matches = process.argv.filter((entry) => {
return entry.indexOf(`${name}=`) === 0 return entry.indexOf(`${name}=`) === 0
}) })
@@ -27,7 +31,7 @@ const clientOptions = {
path: returnArg('socket') // '/tmp/redis.sock' path: returnArg('socket') // '/tmp/redis.sock'
} }
function lpad (input, len, chr) { function lpad(input, len, chr) {
let str = input.toString() let str = input.toString()
chr = chr || ' ' chr = chr || ' '
while (str.length < len) { while (str.length < len) {
@@ -41,7 +45,7 @@ metrics.Histogram.prototype.printLine = function () {
return `${lpad((obj.mean / 1e6).toFixed(2), 6)}/${lpad((obj.max / 1e6).toFixed(2), 6)}` return `${lpad((obj.mean / 1e6).toFixed(2), 6)}/${lpad((obj.max / 1e6).toFixed(2), 6)}`
} }
function Test (args) { function Test(args) {
this.args = args this.args = args
this.args.pipeline = +pipeline this.args.pipeline = +pipeline
this.callback = null this.callback = null
@@ -116,7 +120,7 @@ Test.prototype.newClient = function (id) {
} }
Test.prototype.onClientsReady = function () { 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} `) 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.testStart = Date.now()
return this.fillPipeline() return this.fillPipeline()
} }
@@ -203,64 +207,110 @@ const veryLargeStr = (new Array((4 * 1024 * 1024) + 1).join('-'))
const veryLargeBuf = Buffer.from(veryLargeStr) const veryLargeBuf = Buffer.from(veryLargeStr)
const mgetArray = (new Array(1025)).join('fooRand000000000001;').split(';') const mgetArray = (new Array(1025)).join('fooRand000000000001;').split(';')
tests.push(new Test({descr: 'PING', command: 'ping', args: []})) tests.push(new Test({ descr: 'PING', command: 'ping', args: [] }))
tests.push(new Test({descr: 'PING', command: 'ping', args: [], batch: 50})) tests.push(new Test({
descr: 'PING', command: 'ping', args: [], batch: 50
}))
tests.push(new Test({descr: 'SET 4B str', command: 'set', args: ['fooRand000000000000', smallStr]})) tests.push(new Test({ descr: 'SET 4B str', command: 'set', args: ['fooRand000000000000', smallStr] }))
tests.push(new Test({descr: 'SET 4B str', command: 'set', args: ['fooRand000000000000', smallStr], batch: 50})) tests.push(new Test({
descr: 'SET 4B str', command: 'set', args: ['fooRand000000000000', smallStr], batch: 50
}))
tests.push(new Test({descr: 'SET 4B buf', command: 'set', args: ['fooRand000000000000', smallBuf]})) tests.push(new Test({ descr: 'SET 4B buf', command: 'set', args: ['fooRand000000000000', smallBuf] }))
tests.push(new Test({descr: 'SET 4B buf', command: 'set', args: ['fooRand000000000000', smallBuf], batch: 50})) tests.push(new Test({
descr: 'SET 4B buf', command: 'set', args: ['fooRand000000000000', smallBuf], batch: 50
}))
tests.push(new Test({descr: 'GET 4B str', command: 'get', args: ['fooRand000000000000']})) tests.push(new Test({ descr: 'GET 4B str', command: 'get', args: ['fooRand000000000000'] }))
tests.push(new Test({descr: 'GET 4B str', command: 'get', args: ['fooRand000000000000'], batch: 50})) tests.push(new Test({
descr: 'GET 4B str', command: 'get', args: ['fooRand000000000000'], batch: 50
}))
tests.push(new Test({descr: 'GET 4B buf', command: 'get', args: ['fooRand000000000000'], clientOptions: {returnBuffers: true}})) tests.push(new Test({
tests.push(new Test({descr: 'GET 4B buf', command: 'get', args: ['fooRand000000000000'], batch: 50, clientOptions: {returnBuffers: true}})) descr: 'GET 4B buf', command: 'get', args: ['fooRand000000000000'], clientOptions: { returnBuffers: true }
}))
tests.push(new Test({
descr: 'GET 4B buf', command: 'get', args: ['fooRand000000000000'], batch: 50, clientOptions: { returnBuffers: true }
}))
tests.push(new Test({descr: 'SET 4KiB str', command: 'set', args: ['fooRand000000000001', largeStr]})) tests.push(new Test({ descr: 'SET 4KiB str', command: 'set', args: ['fooRand000000000001', largeStr] }))
tests.push(new Test({descr: 'SET 4KiB str', command: 'set', args: ['fooRand000000000001', largeStr], batch: 50})) tests.push(new Test({
descr: 'SET 4KiB str', command: 'set', args: ['fooRand000000000001', largeStr], batch: 50
}))
tests.push(new Test({descr: 'SET 4KiB buf', command: 'set', args: ['fooRand000000000001', largeBuf]})) tests.push(new Test({ descr: 'SET 4KiB buf', command: 'set', args: ['fooRand000000000001', largeBuf] }))
tests.push(new Test({descr: 'SET 4KiB buf', command: 'set', args: ['fooRand000000000001', largeBuf], batch: 50})) tests.push(new Test({
descr: 'SET 4KiB buf', command: 'set', args: ['fooRand000000000001', largeBuf], batch: 50
}))
tests.push(new Test({descr: 'GET 4KiB str', command: 'get', args: ['fooRand000000000001']})) tests.push(new Test({ descr: 'GET 4KiB str', command: 'get', args: ['fooRand000000000001'] }))
tests.push(new Test({descr: 'GET 4KiB str', command: 'get', args: ['fooRand000000000001'], batch: 50})) tests.push(new Test({
descr: 'GET 4KiB str', command: 'get', args: ['fooRand000000000001'], batch: 50
}))
tests.push(new Test({descr: 'GET 4KiB buf', command: 'get', args: ['fooRand000000000001'], clientOptions: {returnBuffers: true}})) tests.push(new Test({
tests.push(new Test({descr: 'GET 4KiB buf', command: 'get', args: ['fooRand000000000001'], batch: 50, clientOptions: {returnBuffers: true}})) descr: 'GET 4KiB buf', command: 'get', args: ['fooRand000000000001'], clientOptions: { returnBuffers: true }
}))
tests.push(new Test({
descr: 'GET 4KiB buf', command: 'get', args: ['fooRand000000000001'], batch: 50, clientOptions: { returnBuffers: true }
}))
tests.push(new Test({descr: 'INCR', command: 'incr', args: ['counterRand000000000000']})) tests.push(new Test({ descr: 'INCR', command: 'incr', args: ['counterRand000000000000'] }))
tests.push(new Test({descr: 'INCR', command: 'incr', args: ['counterRand000000000000'], batch: 50})) tests.push(new Test({
descr: 'INCR', command: 'incr', args: ['counterRand000000000000'], batch: 50
}))
tests.push(new Test({descr: 'LPUSH', command: 'lpush', args: ['mylist', smallStr]})) tests.push(new Test({ descr: 'LPUSH', command: 'lpush', args: ['mylist', smallStr] }))
tests.push(new Test({descr: 'LPUSH', command: 'lpush', args: ['mylist', smallStr], batch: 50})) tests.push(new Test({
descr: 'LPUSH', command: 'lpush', args: ['mylist', smallStr], batch: 50
}))
tests.push(new Test({descr: 'LRANGE 10', command: 'lrange', args: ['mylist', '0', '9']})) tests.push(new Test({ descr: 'LRANGE 10', command: 'lrange', args: ['mylist', '0', '9'] }))
tests.push(new Test({descr: 'LRANGE 10', command: 'lrange', args: ['mylist', '0', '9'], batch: 50})) tests.push(new Test({
descr: 'LRANGE 10', command: 'lrange', args: ['mylist', '0', '9'], batch: 50
}))
tests.push(new Test({descr: 'LRANGE 100', command: 'lrange', args: ['mylist', '0', '99']})) tests.push(new Test({ descr: 'LRANGE 100', command: 'lrange', args: ['mylist', '0', '99'] }))
tests.push(new Test({descr: 'LRANGE 100', command: 'lrange', args: ['mylist', '0', '99'], batch: 50})) tests.push(new Test({
descr: 'LRANGE 100', command: 'lrange', args: ['mylist', '0', '99'], batch: 50
}))
tests.push(new Test({descr: 'SET 4MiB str', command: 'set', args: ['fooRand000000000002', veryLargeStr]})) tests.push(new Test({ descr: 'SET 4MiB str', command: 'set', args: ['fooRand000000000002', veryLargeStr] }))
tests.push(new Test({descr: 'SET 4MiB str', command: 'set', args: ['fooRand000000000002', veryLargeStr], batch: 20})) tests.push(new Test({
descr: 'SET 4MiB str', command: 'set', args: ['fooRand000000000002', veryLargeStr], batch: 20
}))
tests.push(new Test({descr: 'SET 4MiB buf', command: 'set', args: ['fooRand000000000002', veryLargeBuf]})) tests.push(new Test({ descr: 'SET 4MiB buf', command: 'set', args: ['fooRand000000000002', veryLargeBuf] }))
tests.push(new Test({descr: 'SET 4MiB buf', command: 'set', args: ['fooRand000000000002', veryLargeBuf], batch: 20})) tests.push(new Test({
descr: 'SET 4MiB buf', command: 'set', args: ['fooRand000000000002', veryLargeBuf], batch: 20
}))
tests.push(new Test({descr: 'GET 4MiB str', command: 'get', args: ['fooRand000000000002']})) tests.push(new Test({ descr: 'GET 4MiB str', command: 'get', args: ['fooRand000000000002'] }))
tests.push(new Test({descr: 'GET 4MiB str', command: 'get', args: ['fooRand000000000002'], batch: 20})) tests.push(new Test({
descr: 'GET 4MiB str', command: 'get', args: ['fooRand000000000002'], batch: 20
}))
tests.push(new Test({descr: 'GET 4MiB buf', command: 'get', args: ['fooRand000000000002'], clientOptions: {returnBuffers: true}})) tests.push(new Test({
tests.push(new Test({descr: 'GET 4MiB buf', command: 'get', args: ['fooRand000000000002'], batch: 20, clientOptions: {returnBuffers: true}})) descr: 'GET 4MiB buf', command: 'get', args: ['fooRand000000000002'], clientOptions: { returnBuffers: true }
}))
tests.push(new Test({
descr: 'GET 4MiB buf', command: 'get', args: ['fooRand000000000002'], batch: 20, clientOptions: { returnBuffers: true }
}))
tests.push(new Test({descr: 'MGET 4MiB str', command: 'mget', args: mgetArray})) tests.push(new Test({ descr: 'MGET 4MiB str', command: 'mget', args: mgetArray }))
tests.push(new Test({descr: 'MGET 4MiB str', command: 'mget', args: mgetArray, batch: 20})) tests.push(new Test({
descr: 'MGET 4MiB str', command: 'mget', args: mgetArray, batch: 20
}))
tests.push(new Test({descr: 'MGET 4MiB buf', command: 'mget', args: mgetArray, clientOptions: {returnBuffers: true}})) tests.push(new Test({
tests.push(new Test({descr: 'MGET 4MiB buf', command: 'mget', args: mgetArray, batch: 20, clientOptions: {returnBuffers: true}})) descr: 'MGET 4MiB buf', command: 'mget', args: mgetArray, clientOptions: { returnBuffers: true }
}))
tests.push(new Test({
descr: 'MGET 4MiB buf', command: 'mget', args: mgetArray, batch: 20, clientOptions: { returnBuffers: true }
}))
function next () { function next() {
const test = tests.shift() const test = tests.shift()
if (test) { if (test) {
test.run(() => { test.run(() => {

View File

@@ -14,12 +14,12 @@ Object.assign(RedisClient, Errors, {
RedisClient, RedisClient,
Multi, Multi,
print: utils.print, print: utils.print,
createClient() { createClient(...args) {
return new RedisClient(unifyOptions.apply(null, arguments)) return new RedisClient(unifyOptions.apply(null, args))
}, },
debugMode = /\bredis\b/i.test(process.env.NODE_DEBUG) debugMode: /\bredis\b/i.test(process.env.NODE_DEBUG)
}) })
Commands.list.forEach((name) => addCommand(RedisClient.prototype, Multi.prototype, name)) Commands.list.forEach(name => addCommand(RedisClient.prototype, Multi.prototype, name))
module.exports = RedisClient module.exports = RedisClient

34
lib/.eslintrc.json Normal file
View File

@@ -0,0 +1,34 @@
{
"extends": "airbnb-base",
// Override airbnb defaults
"parserOptions": {
"ecmaVersion": 6,
"sourceType": "script"
},
"rules": {
"semi": ["error", "never"],
"strict": ["error", "global"],
"comma-dangle": "off",
"no-plusplus": "off",
"func-names": "off",
"arrow-body-style": "off",
"no-underscore-dangle": "off",
"consistent-return": "off",
"no-unused-vars": ["error", { "vars": "all", "args": "none" }],
"no-shadow": "off",
"no-console": "off",
"no-restricted-properties": "off",
"no-param-reassign": "off",
"no-var": "off",
"vars-on-top": "off",
"prefer-destructuring": "off",
"no-mixed-operators": "off",
"prefer-spread": "off",
"no-use-before-define": "off",
"global-require": "off",
"no-nested-ternary": "off"
},
"env": {
"mocha": 2
}
}

View File

@@ -2,20 +2,15 @@
const Command = require('./command') const Command = require('./command')
function addCommand (clientProto, multiProto, command) { function addCommand(clientProto, multiProto, command) {
// Some rare Redis commands use special characters in their command name // Some rare Redis commands use special characters in their command name
// Convert those to a underscore to prevent using invalid function names // Convert those to a underscore to prevent using invalid function names
const commandName = command.replace(/(?:^([0-9])|[^a-zA-Z0-9_$])/g, '_$1') const commandName = command.replace(/(?:^([0-9])|[^a-zA-Z0-9_$])/g, '_$1')
// Do not override existing functions // Do not override existing functions
if (!clientProto[command]) { if (!clientProto[command]) {
clientProto[commandName] = function () { clientProto[commandName] = function (...args) {
const len = arguments.length return this.internalSendCommand(new Command(command, args))
const arr = new Array(len)
for (var i = 0; i < len; i += 1) {
arr[i] = arguments[i]
}
return this.internalSendCommand(new Command(command, arr))
} }
if (!clientProto[commandName].name) { if (!clientProto[commandName].name) {
Object.defineProperty(clientProto[commandName], 'name', { Object.defineProperty(clientProto[commandName], 'name', {
@@ -32,13 +27,8 @@ function addCommand (clientProto, multiProto, command) {
// Do not override existing functions // Do not override existing functions
if (!multiProto[command] && command !== 'multi') { if (!multiProto[command] && command !== 'multi') {
multiProto[commandName] = function () { multiProto[commandName] = function (...args) {
const len = arguments.length this._queue.push(new Command(command, args))
const arr = new Array(len)
for (var i = 0; i < len; i += 1) {
arr[i] = arguments[i]
}
this._queue.push(new Command(command, arr))
return this return this
} }
if (!multiProto[commandName].name) { if (!multiProto[commandName].name) {

View File

@@ -11,8 +11,9 @@ const Multi = require('./multi')
const offlineCommand = require('./offlineCommand') const offlineCommand = require('./offlineCommand')
const utils = require('./utils') const utils = require('./utils')
const normalizeAndWriteCommand = require('./writeCommand') const normalizeAndWriteCommand = require('./writeCommand')
const noop = function () {} const noop = function () {}
var connectionId = 0 let connectionId = 0
// Attention: The second parameter might be removed at will and is not officially supported. // Attention: The second parameter might be removed at will and is not officially supported.
// Do not rely on this // Do not rely on this
@@ -23,15 +24,17 @@ class RedisClient extends EventEmitter {
* *
* @memberof RedisClient * @memberof RedisClient
*/ */
constructor (options) { constructor(options) {
var i
super() super()
// Copy the options so they are not mutated // Copy the options so they are not mutated
options = utils.clone(options) options = utils.clone(options)
// TODO: Add a more restrictive options validation // TODO: Add a more restrictive options validation
const cnxOptions = {} const cnxOptions = {}
for (const tlsOption in options.tls) { if (options.tls) {
/* istanbul ignore else */ const tlsKeys = Object.keys(options.tls)
if (options.tls.hasOwnProperty(tlsOption)) { for (i = 0; i < tlsKeys.length; i++) {
const tlsOption = tlsKeys[i]
cnxOptions[tlsOption] = options.tls[tlsOption] cnxOptions[tlsOption] = options.tls[tlsOption]
// Copy the tls options into the general options to make sure the address is set right // Copy the tls options into the general options to make sure the address is set right
if (tlsOption === 'port' || tlsOption === 'host' || tlsOption === 'path' || tlsOption === 'family') { if (tlsOption === 'port' || tlsOption === 'host' || tlsOption === 'path' || tlsOption === 'family') {
@@ -40,8 +43,10 @@ class RedisClient extends EventEmitter {
} }
} }
if (options.stream) { if (options.stream) {
// The stream from the outside is used so no connection from this side is triggered but from the server this client should talk to // The stream from the outside is used so no connection from this side is
// Reconnect etc won't work with this. This requires monkey patching to work, so it is not officially supported // triggered but from the server this client should talk to Reconnect etc
// won't work with this. This requires monkey patching to work, so it is
// not officially supported
this.address = '"Private stream"' this.address = '"Private stream"'
} else if (options.path) { } else if (options.path) {
cnxOptions.path = options.path cnxOptions.path = options.path
@@ -56,9 +61,10 @@ class RedisClient extends EventEmitter {
if (options.socketKeepalive === undefined) { if (options.socketKeepalive === undefined) {
options.socketKeepalive = true options.socketKeepalive = true
} }
for (const command in options.renameCommands) { if (options.renameCommands) {
/* istanbul ignore else */ const renameKeys = Object.keys(options.renameCommands)
if (options.renameCommands.hasOwnProperty(command)) { for (i = 0; i < renameKeys.length; i++) {
const command = renameKeys[i]
options.renameCommands[command.toLowerCase()] = options.renameCommands[command] options.renameCommands[command.toLowerCase()] = options.renameCommands[command]
} }
} }
@@ -116,7 +122,8 @@ class RedisClient extends EventEmitter {
this._closing = false this._closing = false
this._timesConnected = 0 this._timesConnected = 0
this._connectionOptions = cnxOptions this._connectionOptions = cnxOptions
// Only used as timeout until redis has to be connected to redis until throwing an connection error // Only used as timeout until redis has to be connected to redis until
// throwing an connection error
this._connectTimeout = +options.connectTimeout || 60 * 1000 // ms this._connectTimeout = +options.connectTimeout || 60 * 1000 // ms
this._retryStrategy = options.retryStrategy || function (options) { this._retryStrategy = options.retryStrategy || function (options) {
// TODO: Find better defaults // TODO: Find better defaults
@@ -140,12 +147,15 @@ class RedisClient extends EventEmitter {
}) })
} }
// Do not call internalSendCommand directly, if you are not absolutely certain it handles everything properly // Do not call internalSendCommand directly, if you are not absolutely certain
// e.g. monitor / info does not work with internalSendCommand only // it handles everything properly e.g. monitor / info does not work with
// internalSendCommand only
//
// TODO: Move this function out of the client as a private function // TODO: Move this function out of the client as a private function
// TODO: Check how others can intercept (monkey patch) essential parts (e.g. opbeat) //
// after making this private. // TODO: Check how others can intercept (monkey patch) essential parts (e.g.
internalSendCommand (commandObj) { // opbeat) after making this private.
internalSendCommand(commandObj) {
if (this.ready === false || this._stream.writable === false) { if (this.ready === false || this._stream.writable === false) {
// Handle offline commands right away // Handle offline commands right away
offlineCommand(this, commandObj) offlineCommand(this, commandObj)
@@ -175,7 +185,7 @@ class RedisClient extends EventEmitter {
} }
// Redirect calls to the appropriate function and use to send arbitrary / not supported commands // Redirect calls to the appropriate function and use to send arbitrary / not supported commands
sendCommand (command, args) { sendCommand(command, args) {
// Throw to fail early instead of relying in order in this case // Throw to fail early instead of relying in order in this case
if (typeof command !== 'string') { if (typeof command !== 'string') {
throw new TypeError(`Wrong input type "${command !== null && command !== undefined ? command.constructor.name : command}" for command name`) throw new TypeError(`Wrong input type "${command !== null && command !== undefined ? command.constructor.name : command}" for command name`)
@@ -188,11 +198,12 @@ class RedisClient extends EventEmitter {
} }
} }
// Using the raw multi command is only possible with this function // Using the raw multi command is only possible with this function If the
// If the command is not yet added to the client, the internal function should be called right away // command is not yet added to the client, the internal function should be
// Otherwise we need to redirect the calls to make sure the internal functions don't get skipped // called right away Otherwise we need to redirect the calls to make sure
// The internal functions could actually be used for any non hooked function // the internal functions don't get skipped The internal functions could
// but this might change from time to time and at the moment there's no good way to distinguish them // actually be used for any non hooked function 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 // from each other, so let's just do it do it this way for the time being
if (command === 'multi' || typeof this[command] !== 'function') { if (command === 'multi' || typeof this[command] !== 'function') {
return this.internalSendCommand(new Command(command, args)) return this.internalSendCommand(new Command(command, args))
@@ -200,7 +211,7 @@ class RedisClient extends EventEmitter {
return this[command].apply(this, args) return this[command].apply(this, args)
} }
end (flush) { end(flush) {
if (typeof flush !== 'boolean') { if (typeof flush !== 'boolean') {
throw new TypeError('You must call "end" with the flush argument.') throw new TypeError('You must call "end" with the flush argument.')
} }
@@ -222,7 +233,7 @@ class RedisClient extends EventEmitter {
return this._stream.destroySoon() return this._stream.destroySoon()
} }
unref () { unref() {
if (this.connected) { if (this.connected) {
debug('Unref\'ing the socket connection') debug('Unref\'ing the socket connection')
this._stream.unref() this._stream.unref()
@@ -237,18 +248,16 @@ class RedisClient extends EventEmitter {
// This would be another BC and it should be fine to return the client sync. // This would be another BC and it should be fine to return the client sync.
// Therefore a option could be to accept a resolved promise instead of a callback // Therefore a option could be to accept a resolved promise instead of a callback
// to return a promise. // to return a promise.
duplicate (options, callback) { duplicate(options, callback) {
if (typeof options === 'function') { if (typeof options === 'function') {
callback = options callback = options
options = null options = null
} }
const existingOptions = utils.clone(this._options) const existingOptions = utils.clone(this._options)
options = utils.clone(options) options = utils.clone(options)
for (const elem in options) { const keys = Object.keys(options)
/* istanbul ignore else */ for (var i = 0; i < keys.length; i++) {
if (options.hasOwnProperty(elem)) { existingOptions[keys[i]] = options[keys[i]]
existingOptions[elem] = options[elem]
}
} }
const client = new RedisClient(existingOptions) const client = new RedisClient(existingOptions)
// Return to the same state as the other client // Return to the same state as the other client
@@ -274,14 +283,13 @@ class RedisClient extends EventEmitter {
} }
// Note: this overrides a native function! // Note: this overrides a native function!
multi (args) { multi(args) {
return new Multi(this, 'multi', args) return new Multi(this, 'multi', args)
} }
batch (args) { batch(args) {
return new Multi(this, 'batch', args) return new Multi(this, 'batch', args)
} }
} }
module.exports = RedisClient module.exports = RedisClient

View File

@@ -4,7 +4,7 @@ const betterStackTraces = /development/i.test(process.env.NODE_ENV) || /\bredis\
// TODO: Change the arguments to an object // TODO: Change the arguments to an object
// callOnWrite could be two things now // callOnWrite could be two things now
function Command (name, args, callOnWrite, transformer) { function Command(name, args, callOnWrite, transformer) {
this.command = name this.command = name
this.args = args this.args = args
this.argsLength = 0 this.argsLength = 0

View File

@@ -7,15 +7,13 @@ const debug = require('./debug')
const flushAndError = require('./flushAndError') const flushAndError = require('./flushAndError')
const onConnect = require('./readyHandler') const onConnect = require('./readyHandler')
const replyHandler = require('./replyHandler') const replyHandler = require('./replyHandler')
var reconnect
const onResult = replyHandler.onResult const onResult = replyHandler.onResult
const onError = replyHandler.onError const onError = replyHandler.onError
var lazyReconnect = function (client, why, err) { function onStreamError(client, err) {
lazyReconnect = require('./reconnect')
lazyReconnect(client, why, err)
}
function onStreamError (client, err) {
if (client._closing) { if (client._closing) {
return return
} }
@@ -29,9 +27,10 @@ function onStreamError (client, err) {
if (client._retryStrategyProvided === false) { if (client._retryStrategyProvided === false) {
client.emit('error', err) client.emit('error', err)
} }
// 'error' events get turned into exceptions if they aren't listened for. If the user handled this error // 'error' events get turned into exceptions if they aren't listened for. If
// then we should try to reconnect. // the user handled this error then we should try to reconnect.
lazyReconnect(client, 'error', err) if (reconnect === undefined) reconnect = require('./reconnect')
reconnect(client, 'error', err)
} }
/** /**
@@ -40,17 +39,19 @@ function onStreamError (client, err) {
* @param {RedisClient} client * @param {RedisClient} client
* @returns JavascriptRedisParser * @returns JavascriptRedisParser
*/ */
function createParser (client) { function createParser(client) {
return new Parser({ return new Parser({
returnReply (data) { returnReply(data) {
onResult(client, data) onResult(client, data)
}, },
returnError (err) { returnError(err) {
onError(client, err) onError(client, err)
}, },
returnFatalError (err) { returnFatalError(err) {
// Error out all fired commands. Otherwise they might rely on faulty data. We have to reconnect to get in a working state again // Error out all fired commands. Otherwise they might rely on faulty data.
// Note: the execution order is important. First flush and emit, then create the stream // We have to reconnect to get in a working state again Note: the
// execution order is important. First flush and emit, then create the
// stream
err.message += '. Please report this.' err.message += '. Please report this.'
client.ready = false client.ready = false
flushAndError(client, 'Fatal error encountered. Command aborted.', 'NR_FATAL', { flushAndError(client, 'Fatal error encountered. Command aborted.', 'NR_FATAL', {
@@ -73,7 +74,7 @@ function createParser (client) {
* *
* @param {RedisClient} client * @param {RedisClient} client
*/ */
function connect (client) { function connect(client) {
// Init parser // Init parser
const parser = createParser(client) const parser = createParser(client)
const options = client._options const options = client._options
@@ -106,7 +107,8 @@ function connect (client) {
// TODO: Check if this works with tls. // TODO: Check if this works with tls.
stream.setTimeout(client._connectTimeout, () => { stream.setTimeout(client._connectTimeout, () => {
// Note: This is only tested if a internet connection is established // Note: This is only tested if a internet connection is established
lazyReconnect(client, 'timeout') if (reconnect === undefined) reconnect = require('./reconnect')
reconnect(client, 'timeout')
}) })
} }
@@ -127,11 +129,13 @@ function connect (client) {
}) })
stream.once('close', (hadError) => { stream.once('close', (hadError) => {
lazyReconnect(client, 'close') if (reconnect === undefined) reconnect = require('./reconnect')
reconnect(client, 'close')
}) })
stream.once('end', () => { stream.once('end', () => {
lazyReconnect(client, 'end') if (reconnect === undefined) reconnect = require('./reconnect')
reconnect(client, 'end')
}) })
if (options.tls) { if (options.tls) {

View File

@@ -1,16 +1,17 @@
'use strict' 'use strict'
var index = { let index = {
debugMode: /\bredis\b/i.test(process.env.NODE_DEBUG) debugMode: /\bredis\b/i.test(process.env.NODE_DEBUG)
} }
// Lazy load the main file // Lazy load the main file
process.nextTick(() => (index = require('../'))) process.nextTick(() => { index = require('../') })
/** /**
* @description Print a debug statement if in debug mode * @description Print a debug statement if in debug mode
*/ */
function debug () { function debug() {
if (index.debugMode) { if (index.debugMode) {
// eslint-disable-next-line
console.error.apply(null, arguments) console.error.apply(null, arguments)
} }
} }

View File

@@ -3,7 +3,7 @@
const Errors = require('redis-errors') const Errors = require('redis-errors')
// Flush provided queues, erroring out all items // Flush provided queues, erroring out all items
function flushAndError (client, message, code, options) { function flushAndError(client, message, code, options) {
options = options || {} options = options || {}
// Flush the commandQueue first to keep the order intact // Flush the commandQueue first to keep the order intact
const queueNames = options.queues || ['commandQueue', 'offlineQueue'] const queueNames = options.queues || ['commandQueue', 'offlineQueue']
@@ -20,7 +20,7 @@ function flushAndError (client, message, code, options) {
err.command = command.command.toUpperCase() err.command = command.command.toUpperCase()
err.args = command.args err.args = command.args
if (command.error) { if (command.error) {
err.stack = err.stack + command.error.stack.replace(/^Error.*?\n/, '\n') err.stack += command.error.stack.replace(/^Error.*?\n/, '\n')
} }
if (options.error) { if (options.error) {
err.origin = options.error err.origin = options.error

View File

@@ -4,10 +4,11 @@ const Command = require('./command')
const debug = require('./debug') const debug = require('./debug')
const Multi = require('./multi') const Multi = require('./multi')
const utils = require('./utils') const utils = require('./utils')
const noPasswordIsSet = /no password is set/ const noPasswordIsSet = /no password is set/
const RedisClient = require('./client') const RedisClient = require('./client')
/******************************************************************************************** /** ******************************************************************************************
Replace built-in redis functions Replace built-in redis functions
The callback may be hooked as needed. The same does not apply to the rest of the function. The callback may be hooked as needed. The same does not apply to the rest of the function.
@@ -20,9 +21,9 @@ const RedisClient = require('./client')
on single and multi calls! on single and multi calls!
TODO: Implement hooks to replace this. Most of these things are perfect for hooks TODO: Implement hooks to replace this. Most of these things are perfect for hooks
********************************************************************************************/ ******************************************************************************************* */
function selectCallback (client, db) { function selectCallback(client, db) {
return function (err, res) { return function (err, res) {
if (err === null) { if (err === null) {
// Store db in this.selectDb to restore it on reconnect // Store db in this.selectDb to restore it on reconnect
@@ -32,28 +33,31 @@ function selectCallback (client, db) {
} }
} }
RedisClient.prototype.select = function select (db) { RedisClient.prototype.select = function select(db) {
return this.internalSendCommand(new Command('select', [db], null, selectCallback(this, db))) return this.internalSendCommand(new Command('select', [db], null, selectCallback(this, db)))
} }
Multi.prototype.select = function select (db) { Multi.prototype.select = function select(db) {
this._queue.push(new Command('select', [db], null, selectCallback(this._client, db))) this._queue.push(new Command('select', [db], null, selectCallback(this._client, db)))
return this return this
} }
RedisClient.prototype.monitor = function 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 // Use a individual command, as this is a special case that does not has to be
// checked for any other command
const callOnWrite = () => { const callOnWrite = () => {
// Activating monitor mode has to happen before Redis returned the callback. The monitor result is returned first. // Activating monitor mode has to happen before Redis returned the callback.
// Therefore we expect the command to be properly processed. If this is not the case, it's not an issue either. // The monitor result is returned first. Therefore we expect the command to
// be properly processed. If this is not the case, it's not an issue either.
this._monitoring = true this._monitoring = true
} }
return this.internalSendCommand(new Command('monitor', [], callOnWrite)) return this.internalSendCommand(new Command('monitor', [], callOnWrite))
} }
// Only works with batch, not in a transaction // Only works with batch, not in a transaction
Multi.prototype.monitor = function monitor () { 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 // Use a individual command, as this is a special case that does not has to be
// checked for any other command
if (this._type !== 'multi') { if (this._type !== 'multi') {
const callOnWrite = () => { const callOnWrite = () => {
this._client._monitoring = true this._client._monitoring = true
@@ -67,10 +71,11 @@ Multi.prototype.monitor = function monitor () {
return this return this
} }
function quitCallback (client) { function quitCallback(client) {
return function (err, res) { return function (err, res) {
if (client._stream.writable) { if (client._stream.writable) {
// If the socket is still alive, destroy it. This could happen if quit got a NR_CLOSED error code // If the socket is still alive, destroy it. This could happen if quit got
// a NR_CLOSED error code
client._stream.destroy() client._stream.destroy()
} }
if (err && err.code === 'NR_CLOSED') { if (err && err.code === 'NR_CLOSED') {
@@ -85,10 +90,11 @@ function quitCallback (client) {
} }
} }
RedisClient.prototype.quit = function quit () { RedisClient.prototype.quit = function quit() {
// TODO: Consider this for v.3 // 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; // 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', [], null, quitCallback(this))) 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 // Calling quit should always end the connection, no matter if there's a connection or not
this._closing = true this._closing = true
@@ -97,7 +103,7 @@ RedisClient.prototype.quit = function quit () {
} }
// Only works with batch, not in a transaction // Only works with batch, not in a transaction
Multi.prototype.quit = function quit () { Multi.prototype.quit = function quit() {
const callOnWrite = () => { const callOnWrite = () => {
// If called in a multi context, we expect redis is available // If called in a multi context, we expect redis is available
this._client._closing = true this._client._closing = true
@@ -113,7 +119,7 @@ Multi.prototype.quit = function quit () {
* @param {RedisClient} client * @param {RedisClient} client
* @returns {function} * @returns {function}
*/ */
function infoCallback (client) { function infoCallback(client) {
return function (err, res) { return function (err, res) {
if (err) { if (err) {
return err return err
@@ -125,7 +131,7 @@ function infoCallback (client) {
const obj = {} const obj = {}
const lines = res.split('\r\n') const lines = res.split('\r\n')
var topic = '' let topic = ''
while (lines.length) { while (lines.length) {
const parts = lines.shift().split(':') const parts = lines.shift().split(':')
@@ -170,18 +176,18 @@ function infoCallback (client) {
} }
// Store info in this.serverInfo after each call // Store info in this.serverInfo after each call
RedisClient.prototype.info = function info (section) { RedisClient.prototype.info = function info(section) {
const args = section ? [section] : [] const args = section ? [section] : []
return this.internalSendCommand(new Command('info', args, null, infoCallback(this))) return this.internalSendCommand(new Command('info', args, null, infoCallback(this)))
} }
Multi.prototype.info = function info (section) { Multi.prototype.info = function info(section) {
const args = section ? [section] : [] const args = section ? [section] : []
this._queue.push(new Command('info', args, null, infoCallback(this._client))) this._queue.push(new Command('info', args, null, infoCallback(this._client)))
return this return this
} }
function authCallback (client, pass) { function authCallback(client, pass) {
return function (err, res) { return function (err, res) {
if (err) { if (err) {
if (noPasswordIsSet.test(err.message)) { if (noPasswordIsSet.test(err.message)) {
@@ -194,7 +200,7 @@ function authCallback (client, pass) {
} }
} }
RedisClient.prototype.auth = function auth (pass) { RedisClient.prototype.auth = function auth(pass) {
debug('Sending auth to %s id %s', this.address, this.connectionId) debug('Sending auth to %s id %s', this.address, this.connectionId)
// Stash auth for connect and reconnect. // Stash auth for connect and reconnect.
@@ -207,7 +213,7 @@ RedisClient.prototype.auth = function auth (pass) {
} }
// Only works with batch, not in a transaction // Only works with batch, not in a transaction
Multi.prototype.auth = function auth (pass) { Multi.prototype.auth = function auth(pass) {
debug('Sending auth to %s id %s', this.address, this.connectionId) debug('Sending auth to %s id %s', this.address, this.connectionId)
// Stash auth for connect and reconnect. // Stash auth for connect and reconnect.
@@ -216,12 +222,7 @@ Multi.prototype.auth = function auth (pass) {
return this return this
} }
RedisClient.prototype.client = function client () { RedisClient.prototype.client = function client(...arr) {
const len = arguments.length
const arr = new Array(len)
for (var i = 0; i < len; i += 1) {
arr[i] = arguments[i]
}
var callOnWrite var callOnWrite
// CLIENT REPLY ON|OFF|SKIP // CLIENT REPLY ON|OFF|SKIP
if (arr.length === 2 && arr[0].toString().toUpperCase() === 'REPLY') { if (arr.length === 2 && arr[0].toString().toUpperCase() === 'REPLY') {
@@ -235,12 +236,7 @@ RedisClient.prototype.client = function client () {
return this.internalSendCommand(new Command('client', arr, callOnWrite)) return this.internalSendCommand(new Command('client', arr, callOnWrite))
} }
Multi.prototype.client = function client () { Multi.prototype.client = function client(...arr) {
const len = arguments.length
const arr = new Array(len)
for (var i = 0; i < len; i += 1) {
arr[i] = arguments[i]
}
var callOnWrite var callOnWrite
// CLIENT REPLY ON|OFF|SKIP // CLIENT REPLY ON|OFF|SKIP
if (arr.length === 2 && arr[0].toString().toUpperCase() === 'REPLY') { if (arr.length === 2 && arr[0].toString().toUpperCase() === 'REPLY') {
@@ -255,24 +251,14 @@ Multi.prototype.client = function client () {
return this return this
} }
RedisClient.prototype.subscribe = function subscribe () { RedisClient.prototype.subscribe = function subscribe(...arr) {
const len = arguments.length
const arr = new Array(len)
for (var i = 0; i < len; i += 1) {
arr[i] = arguments[i]
}
const callOnWrite = () => { const callOnWrite = () => {
this._pubSubMode = this._pubSubMode || this.commandQueue.length + 1 this._pubSubMode = this._pubSubMode || this.commandQueue.length + 1
} }
return this.internalSendCommand(new Command('subscribe', arr, callOnWrite)) return this.internalSendCommand(new Command('subscribe', arr, callOnWrite))
} }
Multi.prototype.subscribe = function subscribe () { Multi.prototype.subscribe = function subscribe(...arr) {
const len = arguments.length
const arr = new Array(len)
for (var i = 0; i < len; i += 1) {
arr[i] = arguments[i]
}
const callOnWrite = () => { const callOnWrite = () => {
this._client._pubSubMode = this._client._pubSubMode || this._client.commandQueue.length + 1 this._client._pubSubMode = this._client._pubSubMode || this._client.commandQueue.length + 1
} }
@@ -280,51 +266,33 @@ Multi.prototype.subscribe = function subscribe () {
return this return this
} }
RedisClient.prototype.unsubscribe = function unsubscribe () { RedisClient.prototype.unsubscribe = function unsubscribe(...arr) {
const len = arguments.length
const arr = new Array(len)
for (var i = 0; i < len; i += 1) {
arr[i] = arguments[i]
}
const callOnWrite = () => { const callOnWrite = () => {
// Pub sub has to be activated even if not in pub sub mode, as the return value is manipulated in the callback // Pub sub has to be activated even if not in pub sub mode, as the return
// value is manipulated in the callback
this._pubSubMode = this._pubSubMode || this.commandQueue.length + 1 this._pubSubMode = this._pubSubMode || this.commandQueue.length + 1
} }
return this.internalSendCommand(new Command('unsubscribe', arr, callOnWrite)) return this.internalSendCommand(new Command('unsubscribe', arr, callOnWrite))
} }
Multi.prototype.unsubscribe = function unsubscribe () { Multi.prototype.unsubscribe = function unsubscribe(...arr) {
const len = arguments.length
const arr = new Array(len)
for (var i = 0; i < len; i += 1) {
arr[i] = arguments[i]
}
const callOnWrite = () => { const callOnWrite = () => {
// Pub sub has to be activated even if not in pub sub mode, as the return value is manipulated in the callback // Pub sub has to be activated even if not in pub sub mode, as the return
// value is manipulated in the callback
this._client._pubSubMode = this._client._pubSubMode || this._client.commandQueue.length + 1 this._client._pubSubMode = this._client._pubSubMode || this._client.commandQueue.length + 1
} }
this._queue.push(new Command('unsubscribe', arr, callOnWrite)) this._queue.push(new Command('unsubscribe', arr, callOnWrite))
return this return this
} }
RedisClient.prototype.psubscribe = function psubscribe () { RedisClient.prototype.psubscribe = function psubscribe(...arr) {
const len = arguments.length
const arr = new Array(len)
for (var i = 0; i < len; i += 1) {
arr[i] = arguments[i]
}
const callOnWrite = () => { const callOnWrite = () => {
this._pubSubMode = this._pubSubMode || this.commandQueue.length + 1 this._pubSubMode = this._pubSubMode || this.commandQueue.length + 1
} }
return this.internalSendCommand(new Command('psubscribe', arr, callOnWrite)) return this.internalSendCommand(new Command('psubscribe', arr, callOnWrite))
} }
Multi.prototype.psubscribe = function psubscribe () { Multi.prototype.psubscribe = function psubscribe(...arr) {
const len = arguments.length
const arr = new Array(len)
for (var i = 0; i < len; i += 1) {
arr[i] = arguments[i]
}
const callOnWrite = () => { const callOnWrite = () => {
this._client._pubSubMode = this._client._pubSubMode || this._client.commandQueue.length + 1 this._client._pubSubMode = this._client._pubSubMode || this._client.commandQueue.length + 1
} }
@@ -332,27 +300,19 @@ Multi.prototype.psubscribe = function psubscribe () {
return this return this
} }
RedisClient.prototype.punsubscribe = function punsubscribe () { RedisClient.prototype.punsubscribe = function punsubscribe(...arr) {
const len = arguments.length
const arr = new Array(len)
for (var i = 0; i < len; i += 1) {
arr[i] = arguments[i]
}
const callOnWrite = () => { const callOnWrite = () => {
// Pub sub has to be activated even if not in pub sub mode, as the return value is manipulated in the callback // Pub sub has to be activated even if not in pub sub mode, as the return
// value is manipulated in the callback
this._pubSubMode = this._pubSubMode || this.commandQueue.length + 1 this._pubSubMode = this._pubSubMode || this.commandQueue.length + 1
} }
return this.internalSendCommand(new Command('punsubscribe', arr, callOnWrite)) return this.internalSendCommand(new Command('punsubscribe', arr, callOnWrite))
} }
Multi.prototype.punsubscribe = function punsubscribe () { Multi.prototype.punsubscribe = function punsubscribe(...arr) {
const len = arguments.length
const arr = new Array(len)
for (var i = 0; i < len; i += 1) {
arr[i] = arguments[i]
}
const callOnWrite = () => { const callOnWrite = () => {
// Pub sub has to be activated even if not in pub sub mode, as the return value is manipulated in the callback // Pub sub has to be activated even if not in pub sub mode, as the return
// value is manipulated in the callback
this._client._pubSubMode = this._client._pubSubMode || this._client.commandQueue.length + 1 this._client._pubSubMode = this._client._pubSubMode || this._client.commandQueue.length + 1
} }
this._queue.push(new Command('punsubscribe', arr, callOnWrite)) this._queue.push(new Command('punsubscribe', arr, callOnWrite))

View File

@@ -4,6 +4,7 @@ const Queue = require('denque')
const Errors = require('redis-errors') const Errors = require('redis-errors')
const Command = require('./command') const Command = require('./command')
const utils = require('./utils') const utils = require('./utils')
const handleReply = utils.handleReply const handleReply = utils.handleReply
/** /**
@@ -15,7 +16,7 @@ const handleReply = utils.handleReply
* @param {number} index Command index in the Multi queue * @param {number} index Command index in the Multi queue
* @returns * * @returns *
*/ */
function pipelineTransactionCommand (multi, command, index) { function pipelineTransactionCommand(multi, command, index) {
// Queueing is done first, then the commands are executed // Queueing is done first, then the commands are executed
const tmp = command.callback const tmp = command.callback
command.callback = function (err, reply) { command.callback = function (err, reply) {
@@ -37,7 +38,7 @@ function pipelineTransactionCommand (multi, command, index) {
* @param {any[]} replies * @param {any[]} replies
* @returns any[] * @returns any[]
*/ */
function multiCallback (multi, replies) { function multiCallback(multi, replies) {
if (replies) { if (replies) {
var i = 0 var i = 0
const queue = multi._queue const queue = multi._queue
@@ -70,13 +71,11 @@ function multiCallback (multi, replies) {
* @param {Multi} multi * @param {Multi} multi
* @returns Promise<any[]> * @returns Promise<any[]>
*/ */
function execTransaction (multi) { function execTransaction(multi) {
const client = multi._client const client = multi._client
const queue = multi._queue const queue = multi._queue
if (multi._monitoring || client._monitoring) { if (multi._monitoring || client._monitoring) {
const err = new RangeError( const err = new RangeError('Using transaction with a client that is in monitor mode does not work due to faulty return values of Redis.')
'Using transaction with a client that is in monitor mode does not work due to faulty return values of Redis.'
)
err.command = 'EXEC' err.command = 'EXEC'
err.code = 'EXECABORT' err.code = 'EXECABORT'
return new Promise((resolve, reject) => { return new Promise((resolve, reject) => {
@@ -89,16 +88,19 @@ function execTransaction (multi) {
// Silently ignore this error. We'll receive the error for the exec as well // Silently ignore this error. We'll receive the error for the exec as well
const promises = [client.internalSendCommand(new Command('multi', [])).catch(() => {})] const promises = [client.internalSendCommand(new Command('multi', [])).catch(() => {})]
// Drain queue, callback will catch 'QUEUED' or error // Drain queue, callback will catch 'QUEUED' or error
for (var index = 0; index < len; index++) { for (let index = 0; index < len; index++) {
// The commands may not be shifted off, since they are needed in the result handler // The commands may not be shifted off, since they are needed in the result handler
promises.push(pipelineTransactionCommand(multi, queue.peekAt(index), index).catch((e) => e)) promises.push(pipelineTransactionCommand(multi, queue.peekAt(index), index).catch(e => e))
} }
const main = client.internalSendCommand(new Command('exec', [])) const main = client.internalSendCommand(new Command('exec', []))
return Promise.all(promises).then(() => main.then((replies) => multiCallback(multi, replies)).catch((err) => { return Promise.all(promises)
err.errors = multi._errors .then(() => main
return Promise.reject(err) .then(replies => multiCallback(multi, replies))
})) .catch((err) => {
err.errors = multi._errors
return Promise.reject(err)
}))
} }
/** /**
@@ -107,7 +109,7 @@ function execTransaction (multi) {
* @param {Multi} multi * @param {Multi} multi
* @returns Promise<any[]> * @returns Promise<any[]>
*/ */
function execBatch (multi) { function execBatch(multi) {
const client = multi._client const client = multi._client
const queue = multi._queue const queue = multi._queue
if (queue.length === 0) { if (queue.length === 0) {
@@ -119,13 +121,14 @@ function execBatch (multi) {
}) })
} }
var error = false var error = false
function setError(err) {
error = true
return err
}
const promises = [] const promises = []
while (queue.length) { while (queue.length) {
const command = queue.shift() const command = queue.shift()
promises.push(client.internalSendCommand(command).catch((e) => { promises.push(client.internalSendCommand(command).catch(setError))
error = true
return e
}))
} }
return Promise.all(promises).then((res) => { return Promise.all(promises).then((res) => {
if (error) { if (error) {
@@ -147,14 +150,14 @@ class Multi {
* *
* @memberof Multi * @memberof Multi
*/ */
constructor (client, type, args) { constructor(client, type, args) {
this._client = client this._client = client
this._type = type this._type = type
this._queue = new Queue() this._queue = new Queue()
// Either undefined or an array. Fail hard if it's not an array // Either undefined or an array. Fail hard if it's not an array
if (args) { if (args) {
// Legacy support for passing in an array of arguments // Legacy support for passing in an array of arguments
for (var i = 0; i < args.length; i++) { for (let i = 0; i < args.length; i++) {
const command = args[i][0] const command = args[i][0]
const tmpArgs = args[i].slice(1) const tmpArgs = args[i].slice(1)
if (Array.isArray(command)) { if (Array.isArray(command)) {
@@ -173,7 +176,7 @@ class Multi {
* *
* @memberof Multi * @memberof Multi
*/ */
execAtomic () { execAtomic() {
if (this._queue.length < 2) { if (this._queue.length < 2) {
return this.execBatch() return this.execBatch()
} }
@@ -187,7 +190,7 @@ class Multi {
* *
* @memberof Multi * @memberof Multi
*/ */
exec () { exec() {
if (this._type === 'batch') { if (this._type === 'batch') {
return execBatch(this) return execBatch(this)
} }

View File

@@ -4,14 +4,14 @@ const Errors = require('redis-errors')
const debug = require('./debug') const debug = require('./debug')
const utils = require('./utils') const utils = require('./utils')
function offlineCommand (client, command) { function offlineCommand(client, command) {
const commandName = command.command.toUpperCase() const commandName = command.command.toUpperCase()
if (client._closing || client._options.enableOfflineQueue === false) { if (client._closing || client._options.enableOfflineQueue === false) {
const msg = client._closing === true const msg = client._closing === true
? 'The connection is already closed.' ? 'The connection is already closed.'
: client._stream.writable === true : client._stream.writable === true
? 'The connection is not yet established and the offline queue is deactivated.' ? 'The connection is not yet established and the offline queue is deactivated.'
: 'Stream not writeable.' : 'Stream not writeable.'
const err = new Errors.AbortError(`${commandName} can't be processed. ${msg}`) const err = new Errors.AbortError(`${commandName} can't be processed. ${msg}`)
err.code = 'NR_CLOSED' err.code = 'NR_CLOSED'
err.command = commandName err.command = commandName

View File

@@ -1,6 +1,7 @@
'use strict' 'use strict'
const debug = require('./debug') const debug = require('./debug')
const SUBSCRIBE_COMMANDS = { const SUBSCRIBE_COMMANDS = {
subscribe: true, subscribe: true,
unsubscribe: true, unsubscribe: true,
@@ -8,17 +9,22 @@ const SUBSCRIBE_COMMANDS = {
punsubscribe: true punsubscribe: true
} }
function subscribeUnsubscribe (client, reply, type) { function subscribeUnsubscribe(client, reply, type) {
// Subscribe commands take an optional callback and also emit an event, but only the Last_ response is included in the callback // Subscribe commands take an optional callback and also emit an event, but
// The pub sub commands return each argument in a separate return value and have to be handled that way // only the Last_ response is included in the callback The pub sub commands
// return each argument in a separate return value and have to be handled that
// way
const commandObj = client.commandQueue.peekAt(0) const commandObj = client.commandQueue.peekAt(0)
const buffer = client._options.returnBuffers || client._options.detectBuffers && commandObj.bufferArgs const buffer = client._options.returnBuffers ||
client._options.detectBuffers && commandObj.bufferArgs
const channel = (buffer || reply[1] === null) ? reply[1] : reply[1].toString() const channel = (buffer || reply[1] === null) ? reply[1] : reply[1].toString()
const count = +reply[2] // Return the channel counter as number no matter if `stringNumbers` is activated or not // Return the channel counter as number no matter if `stringNumbers` is activated or not
const count = +reply[2]
debug(type, channel) debug(type, channel)
// Emit first, then return the callback // Emit first, then return the callback
if (channel !== null) { // Do not emit or "unsubscribe" something if there was no channel to unsubscribe from // Do not emit or "unsubscribe" something if there was no channel to unsubscribe from
if (channel !== null) {
if (type === 'subscribe' || type === 'psubscribe') { if (type === 'subscribe' || type === 'psubscribe') {
client._subscriptionSet[`${type}_${channel}`] = channel client._subscriptionSet[`${type}_${channel}`] = channel
} else { } else {
@@ -29,10 +35,13 @@ function subscribeUnsubscribe (client, reply, type) {
client._subscribeChannels.push(channel) client._subscribeChannels.push(channel)
} }
if (commandObj.argsLength === 1 || client._subCommandsLeft === 1 || commandObj.argsLength === 0 && (count === 0 || channel === null)) { if (commandObj.argsLength === 1 ||
client._subCommandsLeft === 1 ||
commandObj.argsLength === 0 && (count === 0 || channel === null)) {
if (count === 0) { // Unsubscribed from all channels if (count === 0) { // Unsubscribed from all channels
client._pubSubMode = 0 // Deactivating pub sub mode client._pubSubMode = 0 // Deactivating pub sub mode
// This should be a rare case and therefore handling it this way should be good performance wise for the general case // This should be a rare case and therefore handling it this way should be
// good performance wise for the general case
for (var i = 1; i < client.commandQueue.length; i++) { for (var i = 1; i < client.commandQueue.length; i++) {
const runningCommand = client.commandQueue.peekAt(i) const runningCommand = client.commandQueue.peekAt(i)
if (SUBSCRIBE_COMMANDS[runningCommand.command]) { if (SUBSCRIBE_COMMANDS[runningCommand.command]) {
@@ -45,16 +54,14 @@ function subscribeUnsubscribe (client, reply, type) {
commandObj.callback(null, [count, client._subscribeChannels]) commandObj.callback(null, [count, client._subscribeChannels])
client._subscribeChannels = [] client._subscribeChannels = []
client._subCommandsLeft = 0 client._subCommandsLeft = 0
} else if (client._subCommandsLeft !== 0) {
client._subCommandsLeft--
} else { } else {
if (client._subCommandsLeft !== 0) { client._subCommandsLeft = commandObj.argsLength ? commandObj.argsLength - 1 : count
client._subCommandsLeft--
} else {
client._subCommandsLeft = commandObj.argsLength ? commandObj.argsLength - 1 : count
}
} }
} }
function returnPubSub (client, reply) { function returnPubSub(client, reply) {
const type = reply[0].toString() const type = reply[0].toString()
if (type === 'message') { // Channel, message if (type === 'message') { // Channel, message
if (typeof reply[1] !== 'string') { if (typeof reply[1] !== 'string') {

View File

@@ -4,7 +4,7 @@ const Command = require('./command')
const debug = require('./debug') const debug = require('./debug')
const utils = require('./utils') const utils = require('./utils')
function onConnect (client) { function onConnect(client) {
debug('Stream connected %s id %s', client.address, client.connectionId) debug('Stream connected %s id %s', client.address, client.connectionId)
// TODO: Check if the clients prototype and the clients instance have // TODO: Check if the clients prototype and the clients instance have
@@ -30,7 +30,7 @@ function onConnect (client) {
* *
* @param {RedisClient} client * @param {RedisClient} client
*/ */
function sendOfflineQueue (client) { function sendOfflineQueue(client) {
const queue = client.offlineQueue const queue = client.offlineQueue
while (queue.length) { while (queue.length) {
const command = queue.shift() const command = queue.shift()
@@ -47,7 +47,7 @@ function sendOfflineQueue (client) {
* *
* @param {RedisClient} client * @param {RedisClient} client
*/ */
function readyHandler (client) { function readyHandler(client) {
debug('readyHandler called %s id %s', client.address, client.connectionId) debug('readyHandler called %s id %s', client.address, client.connectionId)
client.ready = true client.ready = true
@@ -79,16 +79,16 @@ function readyHandler (client) {
// } // }
if (!client._options.disableResubscribing && callbackCount) { if (!client._options.disableResubscribing && callbackCount) {
debug('Sending pub/sub commands') debug('Sending pub/sub commands')
for (const key in client._subscriptionSet) { const keys = Object.keys(client._subscriptionSet)
if (client._subscriptionSet.hasOwnProperty(key)) { for (var i = 0; i < keys.length; i++) {
const command = key.slice(0, key.indexOf('_')) const key = keys[i]
const args = client._subscriptionSet[key] const command = key.slice(0, key.indexOf('_'))
client[command]([args]).catch((err) => { const args = client._subscriptionSet[key]
if (!client._closing) { client[command]([args]).catch((err) => {
process.nextTick(client.emit, 'error', err) if (!client._closing) {
} process.nextTick(client.emit, 'error', err)
}) }
} })
} }
} }
sendOfflineQueue(client) sendOfflineQueue(client)
@@ -100,7 +100,7 @@ function readyHandler (client) {
* *
* @param {RedisClient} client * @param {RedisClient} client
*/ */
function readyCheck (client) { function readyCheck(client) {
debug('Checking server ready state...') debug('Checking server ready state...')
// Always fire client info command as first command even if other commands are already queued up // Always fire client info command as first command even if other commands are already queued up
client.ready = true client.ready = true
@@ -112,7 +112,9 @@ function readyCheck (client) {
} }
const persistence = client.serverInfo.persistence const persistence = client.serverInfo.persistence
if (persistence === undefined || persistence.loading === undefined || persistence.loading === 0) { if (persistence === undefined ||
persistence.loading === undefined ||
persistence.loading === 0) {
// If the master_link_status exists but the link is not up, try again after 50 ms // If the master_link_status exists but the link is not up, try again after 50 ms
const replication = client.serverInfo.replication const replication = client.serverInfo.replication
if (replication && typeof replication.master_link_status === 'string' && replication.master_link_status !== 'up') { if (replication && typeof replication.master_link_status === 'string' && replication.master_link_status !== 'up') {
@@ -125,12 +127,12 @@ function readyCheck (client) {
} }
} }
var retryTime = +persistence.loading_eta_seconds * 1000 let retryTime = +persistence.loading_eta_seconds * 1000
if (retryTime > 1000) { if (retryTime > 1000) {
retryTime = 1000 retryTime = 1000
} }
debug('Redis server still loading, trying again in %s', retryTime) debug('Redis server still loading, trying again in %s', retryTime)
setTimeout((client) => readyCheck(client), retryTime, client) setTimeout(client => readyCheck(client), retryTime, client)
}).catch((err) => { }).catch((err) => {
if (client._closing) { if (client._closing) {
return return
@@ -142,7 +144,6 @@ function readyCheck (client) {
} }
err.message = `Ready check failed: ${err.message}` err.message = `Ready check failed: ${err.message}`
client.emit('error', err) client.emit('error', err)
return
}) })
client.ready = false client.ready = false
} }

View File

@@ -11,7 +11,7 @@ const flushAndError = require('./flushAndError')
* @param {RedisClient} client * @param {RedisClient} client
* @param {Error} [error] * @param {Error} [error]
*/ */
function retryConnection (client, error) { function retryConnection(client, error) {
debug('Retrying connection...') debug('Retrying connection...')
const reconnectParams = { const reconnectParams = {
@@ -36,7 +36,7 @@ function retryConnection (client, error) {
* @param {string} why * @param {string} why
* @param {Error} [error] * @param {Error} [error]
*/ */
function reconnect (client, why, error) { function reconnect(client, why, error) {
// If a retry is already in progress, just let that happen // If a retry is already in progress, just let that happen
if (client.retryTimer) { if (client.retryTimer) {
return return
@@ -112,7 +112,12 @@ function reconnect (client, why, error) {
debug('Retry connection in %s ms', client.retryDelay) debug('Retry connection in %s ms', client.retryDelay)
client.retryTimer = setTimeout((client, error) => retryConnection(client, error), client.retryDelay, client, error) client.retryTimer = setTimeout(
(client, error) => retryConnection(client, error),
client.retryDelay,
client,
error
)
} }
module.exports = reconnect module.exports = reconnect

View File

@@ -1,10 +1,10 @@
'use strict' 'use strict'
const Buffer = require('buffer').Buffer const { Buffer } = require('buffer')
const pubsub = require('./pubsub') const pubsub = require('./pubsub')
const utils = require('./utils') const utils = require('./utils')
function onError (client, err) { function onError(client, err) {
const commandObj = client.commandQueue.shift() const commandObj = client.commandQueue.shift()
if (commandObj.error) { if (commandObj.error) {
err.stack = commandObj.error.stack.replace(/^Error.*?\n/, `ReplyError: ${err.message}\n`) err.stack = commandObj.error.stack.replace(/^Error.*?\n/, `ReplyError: ${err.message}\n`)
@@ -28,7 +28,7 @@ function onError (client, err) {
commandObj.callback(err) commandObj.callback(err)
} }
function normalReply (client, reply) { function normalReply(client, reply) {
const command = client.commandQueue.shift() const command = client.commandQueue.shift()
if (client._multi === false) { if (client._multi === false) {
reply = utils.handleReply(client, reply, command) reply = utils.handleReply(client, reply, command)
@@ -36,10 +36,11 @@ function normalReply (client, reply) {
command.callback(null, reply) command.callback(null, reply)
} }
function onResult (client, reply) { function onResult(client, reply) {
// If in monitor mode, all normal commands are still working and we only want to emit the streamlined commands // If in monitor mode, all normal commands are still working and we only want
// As this is not the average use case and monitor is expensive anyway, let's change the code here, to improve // to emit the streamlined commands As this is not the average use case and
// the average performance of all other commands in case of no monitor mode // monitor is expensive anyway, let's change the code here, to improve the
// average performance of all other commands in case of no monitor mode
if (client._monitoring === true) { if (client._monitoring === true) {
var replyStr var replyStr
// TODO: This could be further improved performance wise // TODO: This could be further improved performance wise

View File

@@ -5,9 +5,9 @@ const URL = require('url')
// TODO: Improve the unify performance by checking for the arguments length // TODO: Improve the unify performance by checking for the arguments length
// before trying to access that argument. // before trying to access that argument.
function unifyOptions (portArg, hostArg, options) { function unifyOptions(portArg, hostArg, options) {
if (typeof portArg === 'number' || (typeof portArg === 'string' && /^\d+$/.test(portArg))) { if (typeof portArg === 'number' || (typeof portArg === 'string' && /^\d+$/.test(portArg))) {
var host let host
if (typeof hostArg === 'string') { if (typeof hostArg === 'string') {
host = hostArg host = hostArg
} else { } else {
@@ -24,6 +24,7 @@ function unifyOptions (portArg, hostArg, options) {
const parsed = URL.parse(portArg.url || portArg, true, true) const parsed = URL.parse(portArg.url || portArg, true, true)
// eslint-disable-next-line
// [redis:]//[[user][:password]@][host][:port][/db-number][?db=db-number[&password=bar[&option=value]]] // [redis:]//[[user][:password]@][host][:port][/db-number][?db=db-number[&password=bar[&option=value]]]
if (parsed.slashes) { // We require slashes if (parsed.slashes) { // We require slashes
if (parsed.auth) { if (parsed.auth) {
@@ -42,11 +43,9 @@ function unifyOptions (portArg, hostArg, options) {
options.port = parsed.port options.port = parsed.port
} }
if (parsed.search !== '') { if (parsed.search !== '') {
for (var elem in parsed.query) { const keys = Object.keys(parsed.query)
/* istanbul ignore if */ for (var i = 0; i < keys.length; i++) {
if (!Object.prototype.hasOwnProperty.call(parsed.query, elem)) { const elem = keys[i]
continue
}
// If options are passed twice, only the parsed options will be used // If options are passed twice, only the parsed options will be used
if (elem in options) { if (elem in options) {
if (options[elem] === parsed.query[elem]) { if (options[elem] === parsed.query[elem]) {

View File

@@ -6,12 +6,12 @@
* @param {any[]} reply * @param {any[]} reply
* @returns object * @returns object
*/ */
function replyToObject (reply) { function replyToObject(reply) {
if (reply.length === 0) { if (reply.length === 0) {
return null return null
} }
const obj = {} const obj = {}
for (let i = 0; i < reply.length; i += 2) { for (var i = 0; i < reply.length; i += 2) {
obj[reply[i].toString('binary')] = reply[i + 1] obj[reply[i].toString('binary')] = reply[i + 1]
} }
return obj return obj
@@ -23,7 +23,7 @@ function replyToObject (reply) {
* @param {any[]} reply * @param {any[]} reply
* @returns any[]|string * @returns any[]|string
*/ */
function replyToStrings (reply) { function replyToStrings(reply) {
if (reply === null) { if (reply === null) {
return null return null
} }
@@ -32,7 +32,7 @@ function replyToStrings (reply) {
} }
if (typeof reply.map === 'function') { // instanceof Array if (typeof reply.map === 'function') { // instanceof Array
const res = new Array(reply.length) const res = new Array(reply.length)
for (let i = 0; i < reply.length; i++) { for (var i = 0; i < reply.length; i++) {
// Recursively call the function as slowlog returns deep nested replies // Recursively call the function as slowlog returns deep nested replies
res[i] = replyToStrings(reply[i]) res[i] = replyToStrings(reply[i])
} }
@@ -51,7 +51,7 @@ function replyToStrings (reply) {
* @param {any} obj * @param {any} obj
* @returns any * @returns any
*/ */
function clone (obj) { function clone(obj) {
if (Array.isArray(obj)) { if (Array.isArray(obj)) {
const copy = new Array(obj.length) const copy = new Array(obj.length)
for (var i = 0; i < obj.length; i++) { for (var i = 0; i < obj.length; i++) {
@@ -81,7 +81,7 @@ function clone (obj) {
* @param {undefined|object} obj * @param {undefined|object} obj
* @returns object * @returns object
*/ */
function convenienceClone (obj) { function convenienceClone(obj) {
return clone(obj) || {} return clone(obj) || {}
} }
@@ -99,7 +99,7 @@ function convenienceClone (obj) {
* @param {any} res * @param {any} res
* @param {Denque} queue * @param {Denque} queue
*/ */
function replyInOrder (client, callback, err, res, queue) { function replyInOrder(client, callback, err, res, queue) {
const commandObj = queue const commandObj = queue
? queue.peekBack() ? queue.peekBack()
: (client.offlineQueue.peekBack() || client.commandQueue.peekBack()) : (client.offlineQueue.peekBack() || client.commandQueue.peekBack())
@@ -121,7 +121,7 @@ function replyInOrder (client, callback, err, res, queue) {
* @param {RedisClient} client * @param {RedisClient} client
* @param {string} msg * @param {string} msg
*/ */
function warn (client, msg) { function warn(client, msg) {
if (client.listeners('warning').length !== 0) { if (client.listeners('warning').length !== 0) {
client.emit('warning', msg) client.emit('warning', msg)
} else { } else {
@@ -140,8 +140,9 @@ function warn (client, msg) {
* @param {Command} command * @param {Command} command
* @returns {string|number|null|Buffer|any[]|object} * @returns {string|number|null|Buffer|any[]|object}
*/ */
function handleReply (client, reply, command) { function handleReply(client, reply, command) {
if (client._options.detectBuffers === true && command.bufferArgs === false || client._messageBuffers === true) { if (client._options.detectBuffers === true && command.bufferArgs === false ||
client._messageBuffers === true) {
reply = replyToStrings(reply) reply = replyToStrings(reply)
} }
@@ -157,12 +158,12 @@ function handleReply (client, reply, command) {
* @param {Error|null} err * @param {Error|null} err
* @param {any} reply * @param {any} reply
*/ */
function print (err, reply) { function print(err, reply) {
if (err) { if (err) {
// A error always begins with Error: // A error always begins with Error:
console.error(err.toString()) console.error(err.toString())
} else { } else {
console.log('Reply: ' + reply) console.log(`Reply: ${reply}`)
} }
} }
@@ -171,7 +172,7 @@ function print (err, reply) {
* *
* @param {RedisClient} client * @param {RedisClient} client
*/ */
function setReconnectDefaults (client) { function setReconnectDefaults(client) {
client.retryTimer = null client.retryTimer = null
client.retryTotaltime = 0 client.retryTotaltime = 0
client.retryDelay = 100 client.retryDelay = 100

View File

@@ -21,10 +21,11 @@ var errors = null
/** /**
* @description Pipeline and write all commands to the stream * @description Pipeline and write all commands to the stream
* *
* If the pipelined string exceeds X mb, write it directly to the stream and pipeline the rest again. * If the pipelined string exceeds X mb, write it directly to the stream and
* pipeline the rest again.
* @param {RedisClient} client * @param {RedisClient} client
*/ */
function writeToStream (client) { function writeToStream(client) {
const stream = client._stream const stream = client._stream
const queue = client._pipelineQueue const queue = client._pipelineQueue
const cache = client._strCache const cache = client._strCache
@@ -41,7 +42,7 @@ function writeToStream (client) {
client._pipeline = false client._pipeline = false
} }
function write (client) { function write(client) {
if (client._pipeline === false) { if (client._pipeline === false) {
client._stream.cork() client._stream.cork()
client._pipeline = true client._pipeline = true
@@ -49,11 +50,11 @@ function write (client) {
} }
} }
function pipelineBuffers (client, commandStr) { function pipelineBuffers(client, commandStr) {
const queue = client._pipelineQueue const queue = client._pipelineQueue
client._strCache += commandStr client._strCache += commandStr
while (copy.length) { while (copy.length) {
var arg = copy.shift() const arg = copy.shift()
if (typeof arg === 'string') { if (typeof arg === 'string') {
client._strCache += `$${Buffer.byteLength(arg)}\r\n${arg}\r\n` client._strCache += `$${Buffer.byteLength(arg)}\r\n${arg}\r\n`
} else { } else {
@@ -69,20 +70,20 @@ function pipelineBuffers (client, commandStr) {
client._strCache = '' client._strCache = ''
} }
function toString (arg) { function toString(arg) {
if (typeof arg === 'string') { if (typeof arg === 'string') {
copy.push(arg) copy.push(arg)
} else if (typeof arg === 'number') { } else if (typeof arg === 'number') {
copy.push('' + arg) copy.push(`${arg}`)
} else if (arg instanceof Array) { } else if (arg instanceof Array) {
for (var i = 0; i < arg.length; i += 1) { for (let i = 0; i < arg.length; i += 1) {
toString(arg[i]) toString(arg[i])
} }
} else if (arg && arg.constructor.name === 'Buffer') { // TODO: check performance } else if (arg && arg.constructor.name === 'Buffer') { // TODO: check performance
copy.push(arg) copy.push(arg)
bufferCount++ bufferCount++
} else if (typeof arg === 'boolean') { // TODO: Remove this support and use hooks instead } else if (typeof arg === 'boolean') { // TODO: Remove this support and use hooks instead
copy.push('' + arg) copy.push(`${arg}`)
} else if (arg && arg.constructor.name === 'Object') { // Check if this is actually a good check or not } else if (arg && arg.constructor.name === 'Object') { // Check if this is actually a good check or not
// TODO: As soon as we add support for JSON // TODO: As soon as we add support for JSON
// We could simple stringify this right here. // We could simple stringify this right here.
@@ -99,7 +100,7 @@ function toString (arg) {
toString(val) toString(val)
}) })
} else if (arg instanceof Set) { } else if (arg instanceof Set) {
arg.forEach((val) => toString(val)) arg.forEach(val => toString(val))
} else if (arg && arg.constructor.name === 'Date') { // Check if this is actually a good check or not } else if (arg && arg.constructor.name === 'Date') { // Check if this is actually a good check or not
copy.push(arg.toString()) copy.push(arg.toString())
} else { } else {
@@ -110,7 +111,7 @@ function toString (arg) {
} }
} }
function returnErr (client, command) { function returnErr(client, command) {
const err = new TypeError('NodeRedis can not handle the provided arguments (see "error.issues" property).\n\nFurther information https://github.com/asd') const err = new TypeError('NodeRedis can not handle the provided arguments (see "error.issues" property).\n\nFurther information https://github.com/asd')
err.command = command.command.toUpperCase() err.command = command.command.toUpperCase()
err.args = command.args err.args = command.args
@@ -119,13 +120,14 @@ function returnErr (client, command) {
utils.replyInOrder(client, command.callback, err, undefined, client.commandQueue) utils.replyInOrder(client, command.callback, err, undefined, client.commandQueue)
} }
// Always use 'Multi bulk commands', but if passed any Buffer args, then do multiple writes, one for each arg. // Always use 'Multi bulk commands', but if passed any Buffer args, then do
// This means that using Buffers in commands is going to be slower, so use Strings if you don't already have a Buffer. // multiple writes, one for each arg. This means that using Buffers in commands
// is going to be slower, so use Strings if you don't already have a Buffer.
// TODO: It is faster to move this part somewhere else // TODO: It is faster to move this part somewhere else
// We could move this to the function creation as well // We could move this to the function creation as well
// if we use hooks for our individual commands! // if we use hooks for our individual commands!
function normalizeAndWrite (client, command) { function normalizeAndWrite(client, command) {
const args = command.args const args = command.args
const origName = command.command const origName = command.command
const renameCommands = client._options.renameCommands const renameCommands = client._options.renameCommands
@@ -134,7 +136,7 @@ function normalizeAndWrite (client, command) {
: origName : origName
bufferCount = 0 bufferCount = 0
for (var i = 0; i < args.length; i++) { for (let i = 0; i < args.length; i++) {
toString(args[i]) toString(args[i])
} }
@@ -153,7 +155,7 @@ function normalizeAndWrite (client, command) {
const bufferArgs = bufferCount !== 0 const bufferArgs = bufferCount !== 0
const len = copy.length const len = copy.length
var commandStr = `*${len + 1}\r\n$${name.length}\r\n${name}\r\n` let commandStr = `*${len + 1}\r\n$${name.length}\r\n${name}\r\n`
command.bufferArgs = bufferArgs command.bufferArgs = bufferArgs
command.argsLength = len command.argsLength = len

View File

@@ -23,7 +23,7 @@
"test": "nyc --cache mocha ./test/*.js ./test/commands/*.js --timeout=8000", "test": "nyc --cache mocha ./test/*.js ./test/commands/*.js --timeout=8000",
"posttest": "npm run coverage", "posttest": "npm run coverage",
"compare": "node benchmarks/diff_multi_bench_output.js beforeBench.txt afterBench.txt", "compare": "node benchmarks/diff_multi_bench_output.js beforeBench.txt afterBench.txt",
"lint": "standard . --fix" "lint": "eslint . --fix"
}, },
"dependencies": { "dependencies": {
"denque": "^1.2.2", "denque": "^1.2.2",
@@ -37,11 +37,13 @@
"devDependencies": { "devDependencies": {
"coveralls": "^2.13.3", "coveralls": "^2.13.3",
"cross-spawn": "^5.1.0", "cross-spawn": "^5.1.0",
"eslint": "^4.12.0",
"eslint-config-airbnb-base": "^12.1.0",
"eslint-plugin-import": "^2.8.0",
"intercept-stdout": "~0.1.2", "intercept-stdout": "~0.1.2",
"metrics": "^0.1.14", "metrics": "^0.1.14",
"mocha": "^3.5.3", "mocha": "^3.5.3",
"nyc": "^8.3.0", "nyc": "^8.3.0",
"standard": "^10.0.2",
"tcp-port-used": "^0.1.2", "tcp-port-used": "^0.1.2",
"uuid": "^2.0.1" "uuid": "^2.0.1"
}, },

View File

@@ -3,7 +3,8 @@
const assert = require('assert') const assert = require('assert')
const config = require('./lib/config') const config = require('./lib/config')
const helper = require('./helper') const helper = require('./helper')
const redis = config.redis
const { redis } = config
// TODO: Fix redis process spawn on windows // TODO: Fix redis process spawn on windows
if (process.platform !== 'win32') { if (process.platform !== 'win32') {
@@ -215,7 +216,8 @@ if (process.platform !== 'win32') {
}) })
}) })
client.once('ready', () => { client.once('ready', () => {
// Coherent behavior with all other offline commands fires commands before emitting but does not wait till they return // Coherent behavior with all other offline commands fires commands
// before emitting but does not wait till they return
assert.strictEqual(client._pubSubMode, 2) assert.strictEqual(client._pubSubMode, 2)
client.ping().then(() => { // Make sure all commands were properly processed already client.ping().then(() => { // Make sure all commands were properly processed already
client._stream.destroy() client._stream.destroy()
@@ -224,11 +226,15 @@ if (process.platform !== 'win32') {
}) })
it('individual commands work properly with batch', (done) => { it('individual commands work properly with batch', (done) => {
// quit => might return an error instead of "OK" in the exec callback... (if not connected) // quit => might return an error instead of "OK" in the exec callback...
// auth => might return an error instead of "OK" in the exec callback... (if no password is required / still loading on Redis <= 2.4) // (if not connected)
// This could be fixed by checking the return value of the callback in the exec callback and //
// returning the manipulated [error, result] from the callback. // auth => might return an error instead of "OK" in the exec callback...
// There should be a better solution though // (if no password is required / still loading on Redis <= 2.4)
//
// This could be fixed by checking the return value of the callback in
// the exec callback and returning the manipulated [error, result] from
// the callback. There should be a better solution though
const args = config.configureClient('localhost', { const args = config.configureClient('localhost', {
noReadyCheck: true noReadyCheck: true
@@ -240,25 +246,26 @@ if (process.platform !== 'win32') {
end() // Should be called for each command after monitor end() // Should be called for each command after monitor
}) })
client.batch() client.batch()
.auth(auth) .auth(auth)
.select(5) .select(5)
.monitor() .monitor()
.set('foo', 'bar') .set('foo', 'bar')
.info('stats') .info('stats')
.get('foo') .get('foo')
.subscribe(['foo', 'bar', 'foo']) .subscribe(['foo', 'bar', 'foo'])
.unsubscribe('foo') .unsubscribe('foo')
.subscribe('/foo') .subscribe('/foo')
.psubscribe('*') .psubscribe('*')
.quit() .quit()
.exec().then((res) => { .exec()
res[4] = res[4].substr(0, 9) .then((res) => {
assert.deepStrictEqual( res[4] = res[4].substr(0, 9)
res, assert.deepStrictEqual(
['OK', 'OK', 'OK', 'OK', '# Stats\r\n', 'bar', [2, ['foo', 'bar', 'foo']], [1, ['foo']], [2, ['/foo']], [3, ['*']], 'OK'] res,
) ['OK', 'OK', 'OK', 'OK', '# Stats\r\n', 'bar', [2, ['foo', 'bar', 'foo']], [1, ['foo']], [2, ['/foo']], [3, ['*']], 'OK']
end() )
}) end()
})
}) })
}) })
}) })

View File

@@ -3,7 +3,8 @@
const assert = require('assert') const assert = require('assert')
const config = require('./lib/config') const config = require('./lib/config')
const helper = require('./helper') const helper = require('./helper')
const redis = config.redis
const { redis } = config
describe('The \'batch\' method', () => { describe('The \'batch\' method', () => {
helper.allTests((ip, args) => { helper.allTests((ip, args) => {
@@ -101,7 +102,7 @@ describe('The \'batch\' method', () => {
['incr', 'batchfoo'], ['incr', 'batchfoo'],
['incr', 'batchbar'] ['incr', 'batchbar']
]).exec().then(helper.fail).catch((err) => { ]).exec().then(helper.fail).catch((err) => {
const replies = err.replies const { replies } = err
assert.strictEqual(2, replies[0].length) assert.strictEqual(2, replies[0].length)
assert.strictEqual(null, replies[0][0]) assert.strictEqual(null, replies[0][0])
assert.strictEqual(null, replies[0][1]) assert.strictEqual(null, replies[0][1])
@@ -150,8 +151,8 @@ describe('The \'batch\' method', () => {
[['hmset', 'batchhmset2', 'batchbar2', 'batchfoo3', 'batchbar3', 'test']], [['hmset', 'batchhmset2', 'batchbar2', 'batchfoo3', 'batchbar3', 'test']],
['hmset', ['batchhmset', 'batchbar', 'batchfoo']], ['hmset', ['batchhmset', 'batchbar', 'batchfoo']],
['hmset', arr3], ['hmset', arr3],
['hmset', now, {123456789: 'abcdefghij', 'some manner of key': 'a type of value', 'otherTypes': 555}], ['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}], ['hmset', 'key2', { '0123456789': 'abcdefghij', 'some manner of key': 'a type of value', otherTypes: 999 }],
['hmset', new Set(['batchhmset', ['batchbar', 'batchbaz']])], ['hmset', new Set(['batchhmset', ['batchbar', 'batchbaz']])],
['hmset', ['batchhmset'], new Map([['batchbar', 'batchbaz']])] ['hmset', ['batchhmset'], new Map([['batchbar', 'batchbaz']])]
]) ])
@@ -159,7 +160,8 @@ describe('The \'batch\' method', () => {
.hmget('key2', arr2) .hmget('key2', arr2)
.hmget(['batchhmset2', ['some manner of key', 'batchbar3']]) .hmget(['batchhmset2', ['some manner of key', 'batchbar3']])
.mget('batchfoo2', ['batchfoo3', 'batchfoo']) .mget('batchfoo2', ['batchfoo3', 'batchfoo'])
.exec().then((replies) => { .exec()
.then((replies) => {
assert.strictEqual(arr.length, 3) assert.strictEqual(arr.length, 3)
assert.strictEqual(arr2.length, 2) assert.strictEqual(arr2.length, 2)
assert.strictEqual(arr3.length, 3) assert.strictEqual(arr3.length, 3)
@@ -194,7 +196,8 @@ describe('The \'batch\' method', () => {
.incr('some') .incr('some')
.incr('keys') .incr('keys')
.mget('some', 'keys') .mget('some', 'keys')
.exec().then(helper.isDeepEqual(['OK', 11, 21, ['11', '21']])) .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', () => { it('allows multiple commands to work the same as normal to be performed using a chaining API', () => {
@@ -203,7 +206,8 @@ describe('The \'batch\' method', () => {
.incr('some') .incr('some')
.incr(['keys']) .incr(['keys'])
.mget('some', 'keys') .mget('some', 'keys')
.exec().then(helper.isDeepEqual(['OK', 11, 21, ['11', '21']])) .exec()
.then(helper.isDeepEqual(['OK', 11, 21, ['11', '21']]))
}) })
it('allows an array to be provided indicating multiple operations to perform', () => { it('allows an array to be provided indicating multiple operations to perform', () => {

View File

@@ -3,7 +3,8 @@
const assert = require('assert') const assert = require('assert')
const config = require('../lib/config') const config = require('../lib/config')
const helper = require('../helper') const helper = require('../helper')
const redis = config.redis
const { redis } = config
const intercept = require('intercept-stdout') const intercept = require('intercept-stdout')
describe('The \'blpop\' method', () => { describe('The \'blpop\' method', () => {

View File

@@ -1,10 +1,11 @@
'use strict' 'use strict'
const Buffer = require('buffer').Buffer const { Buffer } = require('buffer')
const assert = require('assert') const assert = require('assert')
const config = require('../lib/config') const config = require('../lib/config')
const helper = require('../helper') const helper = require('../helper')
const redis = config.redis
const { redis } = config
describe('The \'client\' method', () => { describe('The \'client\' method', () => {
helper.allTests((ip, args) => { helper.allTests((ip, args) => {
@@ -144,8 +145,9 @@ describe('The \'client\' method', () => {
}) })
it('sets the name', () => { it('sets the name', () => {
// The querys are auto pipelined and the response is a response to all querys of one client // The querys are auto pipelined and the response is a response to all
// per chunk. So the execution order is only guaranteed on each client // querys of one client per chunk. So the execution order is only
// guaranteed on each client
return Promise.all([ return Promise.all([
client.client('setname', 'RUTH'), client.client('setname', 'RUTH'),
client2.client('setname', ['RENEE']).then(helper.isString('OK')), client2.client('setname', ['RENEE']).then(helper.isString('OK')),

View File

@@ -3,13 +3,15 @@
const assert = require('assert') const assert = require('assert')
const config = require('../lib/config') const config = require('../lib/config')
const helper = require('../helper') const helper = require('../helper')
const redis = config.redis
const { redis } = config
const uuid = require('uuid') const uuid = require('uuid')
describe('The \'dbsize\' method', () => { describe('The \'dbsize\' method', () => {
helper.allTests((ip, args) => { helper.allTests((ip, args) => {
describe(`using ${ip}`, () => { describe(`using ${ip}`, () => {
let key, value let key
let value
beforeEach(() => { beforeEach(() => {
key = uuid.v4() key = uuid.v4()

View File

@@ -2,7 +2,8 @@
const config = require('../lib/config') const config = require('../lib/config')
const helper = require('../helper') const helper = require('../helper')
const redis = config.redis
const { redis } = config
describe('The \'del\' method', () => { describe('The \'del\' method', () => {
helper.allTests((ip, args) => { helper.allTests((ip, args) => {

View File

@@ -4,7 +4,8 @@ const assert = require('assert')
const config = require('../lib/config') const config = require('../lib/config')
const crypto = require('crypto') const crypto = require('crypto')
const helper = require('../helper') const helper = require('../helper')
const redis = config.redis
const { redis } = config
describe('The \'eval\' method', () => { describe('The \'eval\' method', () => {
helper.allTests((ip, args) => { helper.allTests((ip, args) => {
@@ -121,7 +122,8 @@ describe('The \'eval\' method', () => {
.rpush('mylist', 'a') .rpush('mylist', 'a')
.rpush('mylist', 'b') .rpush('mylist', 'b')
.rpush('mylist', 'c') .rpush('mylist', 'c')
.exec().then((replies) => { .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) => { 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(5, res.length)
assert.strictEqual('table', res[0]) assert.strictEqual('table', res[0])

View File

@@ -2,7 +2,8 @@
const config = require('../lib/config') const config = require('../lib/config')
const helper = require('../helper') const helper = require('../helper')
const redis = config.redis
const { redis } = config
describe('The \'exists\' method', () => { describe('The \'exists\' method', () => {
helper.allTests((ip, args) => { helper.allTests((ip, args) => {

View File

@@ -2,7 +2,8 @@
const config = require('../lib/config') const config = require('../lib/config')
const helper = require('../helper') const helper = require('../helper')
const redis = config.redis
const { redis } = config
describe('The \'expire\' method', () => { describe('The \'expire\' method', () => {
helper.allTests((ip, args) => { helper.allTests((ip, args) => {

View File

@@ -2,13 +2,15 @@
const config = require('../lib/config') const config = require('../lib/config')
const helper = require('../helper') const helper = require('../helper')
const redis = config.redis
const { redis } = config
const uuid = require('uuid') const uuid = require('uuid')
describe('The \'flushdb\' method', () => { describe('The \'flushdb\' method', () => {
helper.allTests((ip, args) => { helper.allTests((ip, args) => {
describe(`using ${ip}`, () => { describe(`using ${ip}`, () => {
let key, key2 let key
let key2
beforeEach(() => { beforeEach(() => {
key = uuid.v4() key = uuid.v4()

View File

@@ -2,7 +2,8 @@
const config = require('../lib/config') const config = require('../lib/config')
const helper = require('../helper') const helper = require('../helper')
const redis = config.redis
const { redis } = config
describe('The \'geoadd\' method', () => { describe('The \'geoadd\' method', () => {
helper.allTests((ip, args) => { helper.allTests((ip, args) => {

View File

@@ -2,13 +2,15 @@
const config = require('../lib/config') const config = require('../lib/config')
const helper = require('../helper') const helper = require('../helper')
const redis = config.redis
const { redis } = config
const uuid = require('uuid') const uuid = require('uuid')
describe('The \'get\' method', () => { describe('The \'get\' method', () => {
helper.allTests((ip, args) => { helper.allTests((ip, args) => {
describe(`using ${ip}`, () => { describe(`using ${ip}`, () => {
let key, value let key
let value
beforeEach(() => { beforeEach(() => {
key = uuid.v4() key = uuid.v4()

View File

@@ -2,13 +2,16 @@
const config = require('../lib/config') const config = require('../lib/config')
const helper = require('../helper') const helper = require('../helper')
const redis = config.redis
const { redis } = config
const uuid = require('uuid') const uuid = require('uuid')
describe('The \'getset\' method', () => { describe('The \'getset\' method', () => {
helper.allTests((ip, args) => { helper.allTests((ip, args) => {
describe(`using ${ip}`, () => { describe(`using ${ip}`, () => {
let key, value, value2 let key
let value
let value2
beforeEach(() => { beforeEach(() => {
key = uuid.v4() key = uuid.v4()

View File

@@ -1,10 +1,11 @@
'use strict' 'use strict'
const Buffer = require('buffer').Buffer const { Buffer } = require('buffer')
const assert = require('assert') const assert = require('assert')
const config = require('../lib/config') const config = require('../lib/config')
const helper = require('../helper') const helper = require('../helper')
const redis = config.redis
const { redis } = config
describe('The \'hgetall\' method', () => { describe('The \'hgetall\' method', () => {
helper.allTests((ip, args) => { helper.allTests((ip, args) => {

View File

@@ -2,7 +2,8 @@
const config = require('../lib/config') const config = require('../lib/config')
const helper = require('../helper') const helper = require('../helper')
const redis = config.redis
const { redis } = config
describe('The \'hincrby\' method', () => { describe('The \'hincrby\' method', () => {
helper.allTests((ip, args) => { helper.allTests((ip, args) => {

View File

@@ -1,9 +1,10 @@
'use strict' 'use strict'
const Buffer = require('buffer').Buffer const { Buffer } = require('buffer')
const config = require('../lib/config') const config = require('../lib/config')
const helper = require('../helper') const helper = require('../helper')
const redis = config.redis
const { redis } = config
describe('The \'hlen\' method', () => { describe('The \'hlen\' method', () => {
helper.allTests((ip, args) => { helper.allTests((ip, args) => {

View File

@@ -2,7 +2,8 @@
const config = require('../lib/config') const config = require('../lib/config')
const helper = require('../helper') const helper = require('../helper')
const redis = config.redis
const { redis } = config
describe('The \'hmget\' method', () => { describe('The \'hmget\' method', () => {
helper.allTests((ip, args) => { helper.allTests((ip, args) => {
@@ -13,7 +14,7 @@ describe('The \'hmget\' method', () => {
beforeEach(() => { beforeEach(() => {
client = redis.createClient.apply(null, args) client = redis.createClient.apply(null, args)
client.flushdb() client.flushdb()
return client.hmset(hash, {'0123456789': 'abcdefghij', 'some manner of key': 'a type of value'}) return client.hmset(hash, { '0123456789': 'abcdefghij', 'some manner of key': 'a type of value' })
.then(helper.isString('OK')) .then(helper.isString('OK'))
}) })

View File

@@ -2,7 +2,8 @@
const config = require('../lib/config') const config = require('../lib/config')
const helper = require('../helper') const helper = require('../helper')
const redis = config.redis
const { redis } = config
describe('The \'hmset\' method', () => { describe('The \'hmset\' method', () => {
helper.allTests((ip, args) => { helper.allTests((ip, args) => {
@@ -20,25 +21,25 @@ describe('The \'hmset\' method', () => {
return client.hgetall(hash).then(helper.isDeepEqual({ return client.hgetall(hash).then(helper.isDeepEqual({
'0123456789': 'abcdefghij', '0123456789': 'abcdefghij',
'some manner of key': 'a type of value', 'some manner of key': 'a type of value',
'otherTypes': '555' otherTypes: '555'
})) }))
}) })
it('handles object-style syntax', () => { it('handles object-style syntax', () => {
client.hmset(hash, {'0123456789': 'abcdefghij', 'some manner of key': 'a type of value', 'otherTypes': 555}).then(helper.isString('OK')) 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({ return client.hgetall(hash).then(helper.isDeepEqual({
'0123456789': 'abcdefghij', '0123456789': 'abcdefghij',
'some manner of key': 'a type of value', 'some manner of key': 'a type of value',
'otherTypes': '555' otherTypes: '555'
})) }))
}) })
it('handles object-style syntax and the key being a number', () => { 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}) client.hmset(231232, { '0123456789': 'abcdefghij', 'some manner of key': 'a type of value', otherTypes: 555 })
return client.hgetall(231232).then(helper.isDeepEqual({ return client.hgetall(231232).then(helper.isDeepEqual({
'0123456789': 'abcdefghij', '0123456789': 'abcdefghij',
'some manner of key': 'a type of value', 'some manner of key': 'a type of value',
'otherTypes': '555' otherTypes: '555'
})) }))
}) })

View File

@@ -1,10 +1,11 @@
'use strict' 'use strict'
const Buffer = require('buffer').Buffer const { Buffer } = require('buffer')
const assert = require('assert') const assert = require('assert')
const config = require('../lib/config') const config = require('../lib/config')
const helper = require('../helper') const helper = require('../helper')
const redis = config.redis
const { redis } = config
describe('The \'hset\' method', () => { describe('The \'hset\' method', () => {
helper.allTests((ip, args) => { helper.allTests((ip, args) => {
@@ -43,8 +44,9 @@ describe('The \'hset\' method', () => {
it('warns if someone passed a array either as field or as value', () => { it('warns if someone passed a array either as field or as value', () => {
const hash = 'test hash' const hash = 'test hash'
const field = 'array' const field = 'array'
// This would be converted to "array contents" but if you use more than one entry, // This would be converted to "array contents" but if you use more than
// it'll result in e.g. "array contents,second content" and this is not supported and considered harmful // one entry, it'll result in e.g. "array contents,second content" and
// this is not supported and considered harmful
const value = ['array contents'] const value = ['array contents']
return client.hmset(hash, field, value).then(assert, helper.isError()) return client.hmset(hash, field, value).then(assert, helper.isError())
}) })

View File

@@ -2,7 +2,8 @@
const config = require('../lib/config') const config = require('../lib/config')
const helper = require('../helper') const helper = require('../helper')
const redis = config.redis
const { redis } = config
describe('The \'incr\' method', () => { describe('The \'incr\' method', () => {
helper.allTests((ip, args) => { helper.allTests((ip, args) => {

View File

@@ -3,7 +3,8 @@
const assert = require('assert') const assert = require('assert')
const config = require('../lib/config') const config = require('../lib/config')
const helper = require('../helper') const helper = require('../helper')
const redis = config.redis
const { redis } = config
describe('The \'info\' method', () => { describe('The \'info\' method', () => {
helper.allTests((ip, args) => { helper.allTests((ip, args) => {

View File

@@ -4,7 +4,8 @@ const assert = require('assert')
const config = require('../lib/config') const config = require('../lib/config')
const crypto = require('crypto') const crypto = require('crypto')
const helper = require('../helper') const helper = require('../helper')
const redis = config.redis
const { redis } = config
describe('The \'keys\' method', () => { describe('The \'keys\' method', () => {
helper.allTests((ip, args) => { helper.allTests((ip, args) => {
@@ -20,8 +21,8 @@ describe('The \'keys\' method', () => {
client.mset(['test keys 1', 'test val 1', 'test keys 2', 'test val 2']).then(helper.isString('OK')) 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) => { return client.keys('test keys*').then((results) => {
assert.strictEqual(2, results.length) assert.strictEqual(2, results.length)
assert.ok(~results.indexOf('test keys 1')) assert.notStrictEqual(results.indexOf('test keys 1'), -1)
assert.ok(~results.indexOf('test keys 2')) assert.notStrictEqual(results.indexOf('test keys 2'), -1)
}) })
}) })
@@ -39,7 +40,7 @@ describe('The \'keys\' method', () => {
client.mset(keysValues.reduce((a, b) => a.concat(b))).then(helper.isString('OK')) client.mset(keysValues.reduce((a, b) => a.concat(b))).then(helper.isString('OK'))
return client.keys('multibulk:*').then((results) => { return client.keys('multibulk:*').then((results) => {
assert.deepStrictEqual(keysValues.map((val) => val[0]).sort(), results.sort()) assert.deepStrictEqual(keysValues.map(val => val[0]).sort(), results.sort())
}) })
}) })

View File

@@ -2,7 +2,8 @@
const config = require('../lib/config') const config = require('../lib/config')
const helper = require('../helper') const helper = require('../helper')
const redis = config.redis
const { redis } = config
describe('The \'mget\' method', () => { describe('The \'mget\' method', () => {
helper.allTests((ip, args) => { helper.allTests((ip, args) => {

View File

@@ -1,11 +1,12 @@
'use strict' 'use strict'
const Buffer = require('buffer').Buffer const { Buffer } = require('buffer')
const assert = require('assert') const assert = require('assert')
const config = require('../lib/config') const config = require('../lib/config')
const helper = require('../helper') const helper = require('../helper')
const utils = require('../../lib/utils') const utils = require('../../lib/utils')
const redis = config.redis
const { redis } = config
describe('The \'monitor\' method', () => { describe('The \'monitor\' method', () => {
helper.allTests((parser, ip, args) => { helper.allTests((parser, ip, args) => {
@@ -185,8 +186,9 @@ describe('The \'monitor\' method', () => {
assert.deepStrictEqual(args, responses.shift()) assert.deepStrictEqual(args, responses.shift())
assert(utils.monitorRegex.test(rawOutput), rawOutput) assert(utils.monitorRegex.test(rawOutput), rawOutput)
if (responses.length === 0) { if (responses.length === 0) {
// The publish is called right after the reconnect and the monitor is called before the message is emitted. // The publish is called right after the reconnect and the monitor
// Therefore we have to wait till the next tick // is called before the message is emitted. Therefore we have to
// wait till the next tick
process.nextTick(() => { process.nextTick(() => {
assert(called) assert(called)
pub.end(false) pub.end(false)

View File

@@ -3,13 +3,17 @@
const assert = require('assert') const assert = require('assert')
const config = require('../lib/config') const config = require('../lib/config')
const helper = require('../helper') const helper = require('../helper')
const redis = config.redis
const { redis } = config
const uuid = require('uuid') const uuid = require('uuid')
describe('The \'mset\' method', () => { describe('The \'mset\' method', () => {
helper.allTests((ip, args) => { helper.allTests((ip, args) => {
describe(`using ${ip}`, () => { describe(`using ${ip}`, () => {
let key, value, key2, value2 let key
let value
let key2
let value2
beforeEach(() => { beforeEach(() => {
key = uuid.v4() key = uuid.v4()

View File

@@ -2,7 +2,8 @@
const config = require('../lib/config') const config = require('../lib/config')
const helper = require('../helper') const helper = require('../helper')
const redis = config.redis
const { redis } = config
describe('The \'msetnx\' method', () => { describe('The \'msetnx\' method', () => {
helper.allTests((ip, args) => { helper.allTests((ip, args) => {

View File

@@ -3,7 +3,8 @@
const assert = require('assert') const assert = require('assert')
const config = require('../lib/config') const config = require('../lib/config')
const helper = require('../helper') const helper = require('../helper')
const redis = config.redis
const { redis } = config
describe('The \'randomkey\' method', () => { describe('The \'randomkey\' method', () => {
helper.allTests((ip, args) => { helper.allTests((ip, args) => {

View File

@@ -2,7 +2,8 @@
const config = require('../lib/config') const config = require('../lib/config')
const helper = require('../helper') const helper = require('../helper')
const redis = config.redis
const { redis } = config
describe('The \'rename\' method', () => { describe('The \'rename\' method', () => {
helper.allTests((ip, args) => { helper.allTests((ip, args) => {

View File

@@ -2,7 +2,8 @@
const config = require('../lib/config') const config = require('../lib/config')
const helper = require('../helper') const helper = require('../helper')
const redis = config.redis
const { redis } = config
describe('The \'renamenx\' method', () => { describe('The \'renamenx\' method', () => {
helper.allTests((ip, args) => { helper.allTests((ip, args) => {

View File

@@ -2,7 +2,8 @@
const config = require('../lib/config') const config = require('../lib/config')
const helper = require('../helper') const helper = require('../helper')
const redis = config.redis
const { redis } = config
describe('The \'rpush\' command', () => { describe('The \'rpush\' command', () => {
helper.allTests((ip, args) => { helper.allTests((ip, args) => {

View File

@@ -3,7 +3,8 @@
const assert = require('assert') const assert = require('assert')
const config = require('../lib/config') const config = require('../lib/config')
const helper = require('../helper') const helper = require('../helper')
const redis = config.redis
const { redis } = config
describe('The \'sadd\' method', () => { describe('The \'sadd\' method', () => {
helper.allTests((ip, args) => { helper.allTests((ip, args) => {
@@ -18,7 +19,7 @@ describe('The \'sadd\' method', () => {
it('allows a single value to be added to the set', () => { it('allows a single value to be added to the set', () => {
client.sadd('set0', 'member0').then(helper.isNumber(1)) client.sadd('set0', 'member0').then(helper.isNumber(1))
return client.smembers('set0').then((res) => { return client.smembers('set0').then((res) => {
assert.ok(~res.indexOf('member0')) assert.notStrictEqual(res.indexOf('member0'), -1)
}) })
}) })
@@ -31,9 +32,9 @@ describe('The \'sadd\' method', () => {
client.sadd('set0', ['member0', 'member1', 'member2']).then(helper.isNumber(3)) client.sadd('set0', ['member0', 'member1', 'member2']).then(helper.isNumber(3))
return client.smembers('set0').then((res) => { return client.smembers('set0').then((res) => {
assert.strictEqual(res.length, 3) assert.strictEqual(res.length, 3)
assert.ok(~res.indexOf('member0')) assert.notStrictEqual(res.indexOf('member0'), -1)
assert.ok(~res.indexOf('member1')) assert.notStrictEqual(res.indexOf('member1'), -1)
assert.ok(~res.indexOf('member2')) assert.notStrictEqual(res.indexOf('member2'), -1)
}) })
}) })
@@ -41,9 +42,9 @@ describe('The \'sadd\' method', () => {
client.sadd(['set0', 'member0', 'member1', 'member2']).then(helper.isNumber(3)) client.sadd(['set0', 'member0', 'member1', 'member2']).then(helper.isNumber(3))
return client.smembers('set0').then((res) => { return client.smembers('set0').then((res) => {
assert.strictEqual(res.length, 3) assert.strictEqual(res.length, 3)
assert.ok(~res.indexOf('member0')) assert.notStrictEqual(res.indexOf('member0'), -1)
assert.ok(~res.indexOf('member1')) assert.notStrictEqual(res.indexOf('member1'), -1)
assert.ok(~res.indexOf('member2')) assert.notStrictEqual(res.indexOf('member2'), -1)
}) })
}) })

View File

@@ -2,7 +2,8 @@
const config = require('../lib/config') const config = require('../lib/config')
const helper = require('../helper') const helper = require('../helper')
const redis = config.redis
const { redis } = config
describe('The \'scard\' method', () => { describe('The \'scard\' method', () => {
helper.allTests((ip, args) => { helper.allTests((ip, args) => {

View File

@@ -3,7 +3,8 @@
const config = require('../lib/config') const config = require('../lib/config')
const crypto = require('crypto') const crypto = require('crypto')
const helper = require('../helper') const helper = require('../helper')
const redis = config.redis
const { redis } = config
describe('The \'script\' method', () => { describe('The \'script\' method', () => {
helper.allTests((ip, args) => { helper.allTests((ip, args) => {

View File

@@ -3,7 +3,8 @@
const assert = require('assert') const assert = require('assert')
const config = require('../lib/config') const config = require('../lib/config')
const helper = require('../helper') const helper = require('../helper')
const redis = config.redis
const { redis } = config
describe('The \'sdiff\' method', () => { describe('The \'sdiff\' method', () => {
helper.allTests((ip, args) => { helper.allTests((ip, args) => {

View File

@@ -3,7 +3,8 @@
const assert = require('assert') const assert = require('assert')
const config = require('../lib/config') const config = require('../lib/config')
const helper = require('../helper') const helper = require('../helper')
const redis = config.redis
const { redis } = config
describe('The \'sdiffstore\' method', () => { describe('The \'sdiffstore\' method', () => {
helper.allTests((ip, args) => { helper.allTests((ip, args) => {
@@ -28,7 +29,7 @@ describe('The \'sdiffstore\' method', () => {
return client.smembers('quux').then((values) => { return client.smembers('quux').then((values) => {
const members = values.sort() const members = values.sort()
assert.deepStrictEqual(members, [ 'b', 'x' ]) assert.deepStrictEqual(members, ['b', 'x'])
}) })
}) })

View File

@@ -3,7 +3,8 @@
const assert = require('assert') const assert = require('assert')
const config = require('../lib/config') const config = require('../lib/config')
const helper = require('../helper') const helper = require('../helper')
const redis = config.redis
const { redis } = config
describe('The \'select\' method', () => { describe('The \'select\' method', () => {
helper.allTests((ip, args) => { helper.allTests((ip, args) => {

View File

@@ -3,13 +3,15 @@
const assert = require('assert') const assert = require('assert')
const config = require('../lib/config') const config = require('../lib/config')
const helper = require('../helper') const helper = require('../helper')
const redis = config.redis
const { redis } = config
const uuid = require('uuid') const uuid = require('uuid')
describe('The \'set\' method', () => { describe('The \'set\' method', () => {
helper.allTests((ip, args) => { helper.allTests((ip, args) => {
describe(`using ${ip}`, () => { describe(`using ${ip}`, () => {
let key, value let key
let value
beforeEach(() => { beforeEach(() => {
key = uuid.v4() key = uuid.v4()

View File

@@ -3,7 +3,8 @@
const assert = require('assert') const assert = require('assert')
const config = require('../lib/config') const config = require('../lib/config')
const helper = require('../helper') const helper = require('../helper')
const redis = config.redis
const { redis } = config
describe('The \'setex\' method', () => { describe('The \'setex\' method', () => {
helper.allTests((ip, args) => { helper.allTests((ip, args) => {

View File

@@ -2,7 +2,8 @@
const config = require('../lib/config') const config = require('../lib/config')
const helper = require('../helper') const helper = require('../helper')
const redis = config.redis
const { redis } = config
describe('The \'setnx\' method', () => { describe('The \'setnx\' method', () => {
helper.allTests((ip, args) => { helper.allTests((ip, args) => {

View File

@@ -3,7 +3,8 @@
const assert = require('assert') const assert = require('assert')
const config = require('../lib/config') const config = require('../lib/config')
const helper = require('../helper') const helper = require('../helper')
const redis = config.redis
const { redis } = config
describe('The \'sinter\' method', () => { describe('The \'sinter\' method', () => {
helper.allTests((ip, args) => { helper.allTests((ip, args) => {
@@ -26,7 +27,7 @@ describe('The \'sinter\' method', () => {
return client.sinter('sa', 'sb').then((intersection) => { return client.sinter('sa', 'sb').then((intersection) => {
assert.strictEqual(intersection.length, 2) assert.strictEqual(intersection.length, 2)
assert.deepStrictEqual(intersection.sort(), [ 'b', 'c' ]) assert.deepStrictEqual(intersection.sort(), ['b', 'c'])
}) })
}) })

View File

@@ -2,7 +2,8 @@
const config = require('../lib/config') const config = require('../lib/config')
const helper = require('../helper') const helper = require('../helper')
const redis = config.redis
const { redis } = config
describe('The \'sinterstore\' method', () => { describe('The \'sinterstore\' method', () => {
helper.allTests((ip, args) => { helper.allTests((ip, args) => {

View File

@@ -2,7 +2,8 @@
const config = require('../lib/config') const config = require('../lib/config')
const helper = require('../helper') const helper = require('../helper')
const redis = config.redis
const { redis } = config
describe('The \'sismember\' method', () => { describe('The \'sismember\' method', () => {
helper.allTests((ip, args) => { helper.allTests((ip, args) => {

View File

@@ -3,7 +3,8 @@
const assert = require('assert') const assert = require('assert')
const config = require('../lib/config') const config = require('../lib/config')
const helper = require('../helper') const helper = require('../helper')
const redis = config.redis
const { redis } = config
describe('The \'slowlog\' method', () => { describe('The \'slowlog\' method', () => {
helper.allTests((ip, args) => { helper.allTests((ip, args) => {

View File

@@ -3,7 +3,8 @@
const assert = require('assert') const assert = require('assert')
const config = require('../lib/config') const config = require('../lib/config')
const helper = require('../helper') const helper = require('../helper')
const redis = config.redis
const { redis } = config
describe('The \'smembers\' method', () => { describe('The \'smembers\' method', () => {
helper.allTests((ip, args) => { helper.allTests((ip, args) => {
@@ -21,7 +22,7 @@ describe('The \'smembers\' method', () => {
return client.smembers('foo').then((values) => { return client.smembers('foo').then((values) => {
assert.strictEqual(values.length, 2) assert.strictEqual(values.length, 2)
const members = values.sort() const members = values.sort()
assert.deepStrictEqual(members, [ 'x', 'y' ]) assert.deepStrictEqual(members, ['x', 'y'])
}) })
}) })

View File

@@ -2,7 +2,8 @@
const config = require('../lib/config') const config = require('../lib/config')
const helper = require('../helper') const helper = require('../helper')
const redis = config.redis
const { redis } = config
describe('The \'smove\' method', () => { describe('The \'smove\' method', () => {
helper.allTests((ip, args) => { helper.allTests((ip, args) => {

View File

@@ -2,9 +2,10 @@
const config = require('../lib/config') const config = require('../lib/config')
const helper = require('../helper') const helper = require('../helper')
const redis = config.redis
function setupData (client) { const { redis } = config
function setupData(client) {
client.rpush('y', 'd') client.rpush('y', 'd')
client.rpush('y', 'b') client.rpush('y', 'b')
client.rpush('y', 'a') client.rpush('y', 'a')

View File

@@ -2,7 +2,8 @@
const config = require('../lib/config') const config = require('../lib/config')
const helper = require('../helper') const helper = require('../helper')
const redis = config.redis
const { redis } = config
describe('The \'spop\' method', () => { describe('The \'spop\' method', () => {
helper.allTests((ip, args) => { helper.allTests((ip, args) => {

View File

@@ -3,7 +3,8 @@
const assert = require('assert') const assert = require('assert')
const config = require('../lib/config') const config = require('../lib/config')
const helper = require('../helper') const helper = require('../helper')
const redis = config.redis
const { redis } = config
describe('The \'srem\' method', () => { describe('The \'srem\' method', () => {
helper.allTests((ip, args) => { helper.allTests((ip, args) => {
@@ -30,7 +31,7 @@ describe('The \'srem\' method', () => {
client.srem('set0', ['member1', 'member2']).then(helper.isNumber(2)) client.srem('set0', ['member1', 'member2']).then(helper.isNumber(2))
return client.smembers('set0').then((res) => { return client.smembers('set0').then((res) => {
assert.strictEqual(res.length, 1) assert.strictEqual(res.length, 1)
assert.ok(~res.indexOf('member0')) assert.notStrictEqual(res.indexOf('member0'), -1)
}) })
}) })
@@ -39,7 +40,7 @@ describe('The \'srem\' method', () => {
client.sendCommand('srem', ['set0', 'member1', 'member2']).then(helper.isNumber(2)) client.sendCommand('srem', ['set0', 'member1', 'member2']).then(helper.isNumber(2))
return client.smembers('set0').then((res) => { return client.smembers('set0').then((res) => {
assert.strictEqual(res.length, 1) assert.strictEqual(res.length, 1)
assert.ok(~res.indexOf('member0')) assert.notStrictEqual(res.indexOf('member0'), -1)
}) })
}) })
@@ -48,9 +49,9 @@ describe('The \'srem\' method', () => {
client.srem(['set0', 'member3', 'member4']).then(helper.isNumber(0)) client.srem(['set0', 'member3', 'member4']).then(helper.isNumber(0))
return client.smembers('set0').then((res) => { return client.smembers('set0').then((res) => {
assert.strictEqual(res.length, 3) assert.strictEqual(res.length, 3)
assert.ok(~res.indexOf('member0')) assert.notStrictEqual(res.indexOf('member0'), -1)
assert.ok(~res.indexOf('member1')) assert.notStrictEqual(res.indexOf('member1'), -1)
assert.ok(~res.indexOf('member2')) assert.notStrictEqual(res.indexOf('member2'), -1)
}) })
}) })

View File

@@ -3,7 +3,8 @@
const assert = require('assert') const assert = require('assert')
const config = require('../lib/config') const config = require('../lib/config')
const helper = require('../helper') const helper = require('../helper')
const redis = config.redis
const { redis } = config
describe('The \'sunion\' method', () => { describe('The \'sunion\' method', () => {
helper.allTests((ip, args) => { helper.allTests((ip, args) => {

View File

@@ -3,7 +3,8 @@
const assert = require('assert') const assert = require('assert')
const config = require('../lib/config') const config = require('../lib/config')
const helper = require('../helper') const helper = require('../helper')
const redis = config.redis
const { redis } = config
describe('The \'sunionstore\' method', () => { describe('The \'sunionstore\' method', () => {
helper.allTests((ip, args) => { helper.allTests((ip, args) => {

View File

@@ -3,7 +3,8 @@
const assert = require('assert') const assert = require('assert')
const config = require('../lib/config') const config = require('../lib/config')
const helper = require('../helper') const helper = require('../helper')
const redis = config.redis
const { redis } = config
describe('The \'ttl\' method', () => { describe('The \'ttl\' method', () => {
helper.allTests((ip, args) => { helper.allTests((ip, args) => {

View File

@@ -2,7 +2,8 @@
const config = require('../lib/config') const config = require('../lib/config')
const helper = require('../helper') const helper = require('../helper')
const redis = config.redis
const { redis } = config
describe('The \'type\' method', () => { describe('The \'type\' method', () => {
helper.allTests((ip, args) => { helper.allTests((ip, args) => {

View File

@@ -3,7 +3,8 @@
const assert = require('assert') const assert = require('assert')
const config = require('../lib/config') const config = require('../lib/config')
const helper = require('../helper') const helper = require('../helper')
const redis = config.redis
const { redis } = config
describe('The \'watch\' method', () => { describe('The \'watch\' method', () => {
helper.allTests((ip, args) => { helper.allTests((ip, args) => {

View File

@@ -3,7 +3,8 @@
const config = require('../lib/config') const config = require('../lib/config')
const helper = require('../helper') const helper = require('../helper')
const assert = require('assert') const assert = require('assert')
const redis = config.redis
const { redis } = config
describe('The \'zadd\' method', () => { describe('The \'zadd\' method', () => {
helper.allTests((ip, args) => { helper.allTests((ip, args) => {

View File

@@ -3,7 +3,8 @@
const config = require('../lib/config') const config = require('../lib/config')
const helper = require('../helper') const helper = require('../helper')
const assert = require('assert') const assert = require('assert')
const redis = config.redis
const { redis } = config
describe('The \'zscan\' method', () => { describe('The \'zscan\' method', () => {
helper.allTests((ip, args) => { helper.allTests((ip, args) => {

View File

@@ -2,7 +2,8 @@
const config = require('../lib/config') const config = require('../lib/config')
const helper = require('../helper') const helper = require('../helper')
const redis = config.redis
const { redis } = config
describe('The \'zscore\' method', () => { describe('The \'zscore\' method', () => {
helper.allTests((ip, args) => { helper.allTests((ip, args) => {

View File

@@ -4,11 +4,13 @@ const assert = require('assert')
const config = require('./lib/config') const config = require('./lib/config')
const helper = require('./helper') const helper = require('./helper')
const RedisProcess = require('./lib/redis-process') const RedisProcess = require('./lib/redis-process')
let rp let rp
const path = require('path') const path = require('path')
const redis = config.redis
// TODO: Fix redis process spawn on windows const { redis } = config
// TODO: Fix redis process spawn on windows
if (process.platform !== 'win32') { if (process.platform !== 'win32') {
describe('master slave sync', () => { describe('master slave sync', () => {
let master = null let master = null
@@ -29,7 +31,7 @@ if (process.platform !== 'win32') {
let i = 0 let i = 0
while (i < 1000) { while (i < 1000) {
i++ i++
// Write some data in the redis instance, so there's something to sync // 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.set(`foo${i}`, `bar${new Array(500).join(Math.random())}`)
} }
return multi.exec() return multi.exec()
@@ -42,8 +44,9 @@ if (process.platform !== 'win32') {
let firstInfo let firstInfo
slave = redis.createClient({ slave = redis.createClient({
port, port,
retryStrategy (options) { retryStrategy(options) {
// Try to reconnect in very small intervals to catch the master_link_status down before the sync completes // Try to reconnect in very small intervals to catch the
// master_link_status down before the sync completes
return 10 return 10
} }
}) })

View File

@@ -4,9 +4,11 @@ const assert = require('assert')
const config = require('./lib/config') const config = require('./lib/config')
const connect = require('../lib/connect') const connect = require('../lib/connect')
const helper = require('./helper') const helper = require('./helper')
const Redis = config.redis
const { Redis } = config
const intercept = require('intercept-stdout') const intercept = require('intercept-stdout')
const net = require('net') const net = require('net')
let client let client
describe('connection tests', () => { describe('connection tests', () => {
@@ -18,9 +20,10 @@ describe('connection tests', () => {
}) })
it('support for a private stream', () => { it('support for a private stream', () => {
// While using a private stream, reconnecting and other features are not going to work properly. // While using a private stream, reconnecting and other features are not
// Besides that some functions also have to be monkey patched to be safe from errors in this case. // going to work properly. Besides that some functions also have to be
// Therefore this is not officially supported! // monkey patched to be safe from errors in this case. Therefore this is not
// officially supported!
const socket = new net.Socket() const socket = new net.Socket()
client = new Redis({ client = new Redis({
prefix: 'test', prefix: 'test',
@@ -41,7 +44,7 @@ describe('connection tests', () => {
client = Redis.createClient({ client = Redis.createClient({
connectTimeout: 5, connectTimeout: 5,
port: 9999, port: 9999,
retryStrategy (options) { retryStrategy(options) {
client.quit().then((res) => { client.quit().then((res) => {
assert.strictEqual(res, 'OK') assert.strictEqual(res, 'OK')
assert.strictEqual(called++, -1) assert.strictEqual(called++, -1)
@@ -158,7 +161,7 @@ describe('connection tests', () => {
host: 'somewhere', host: 'somewhere',
port: 6379, port: 6379,
family: ip, family: ip,
retryStrategy () {} retryStrategy() {}
} }
client = Redis.createClient(options) client = Redis.createClient(options)
assert.strictEqual(client._connectionOptions.family, ip === 'IPv6' ? 6 : 4) assert.strictEqual(client._connectionOptions.family, ip === 'IPv6' ? 6 : 4)
@@ -172,7 +175,7 @@ describe('connection tests', () => {
it('retryStrategy used to reconnect with individual error', (done) => { it('retryStrategy used to reconnect with individual error', (done) => {
client = Redis.createClient({ client = Redis.createClient({
retryStrategy (options) { retryStrategy(options) {
if (options.totalRetryTime > 150) { if (options.totalRetryTime > 150) {
client.set('foo', 'bar').then(assert, (err) => { client.set('foo', 'bar').then(assert, (err) => {
assert.strictEqual(err.message, 'Stream connection ended and command aborted.') assert.strictEqual(err.message, 'Stream connection ended and command aborted.')
@@ -191,7 +194,7 @@ describe('connection tests', () => {
it('retryStrategy used to reconnect', (done) => { it('retryStrategy used to reconnect', (done) => {
client = Redis.createClient({ client = Redis.createClient({
retryStrategy (options) { retryStrategy(options) {
if (options.totalRetryTime > 150) { if (options.totalRetryTime > 150) {
client.set('foo', 'bar').catch((err) => { client.set('foo', 'bar').catch((err) => {
assert.strictEqual(err.message, 'Stream connection ended and command aborted.') assert.strictEqual(err.message, 'Stream connection ended and command aborted.')
@@ -214,7 +217,7 @@ describe('connection tests', () => {
}) })
Redis.debugMode = true Redis.debugMode = true
client = Redis.createClient({ client = Redis.createClient({
retryStrategy (options) { retryStrategy(options) {
client.set('foo', 'bar').catch((err) => { client.set('foo', 'bar').catch((err) => {
assert.strictEqual(err.code, 'NR_CLOSED') assert.strictEqual(err.code, 'NR_CLOSED')
assert.strictEqual(err.message, 'Stream connection ended and command aborted.') assert.strictEqual(err.message, 'Stream connection ended and command aborted.')
@@ -241,7 +244,7 @@ describe('connection tests', () => {
// Auto detect ipv4 and use non routeable ip to trigger the timeout // Auto detect ipv4 and use non routeable ip to trigger the timeout
host: '10.255.255.1', host: '10.255.255.1',
connectTimeout, connectTimeout,
retryStrategy () { retryStrategy() {
return 5000 return 5000
} }
}) })
@@ -510,7 +513,7 @@ describe('connection tests', () => {
// Cover info parts with no value // Cover info parts with no value
setImmediate(() => { setImmediate(() => {
const command = client.commandQueue.peekAt(0) const command = client.commandQueue.peekAt(0)
const callback = command.callback const { callback } = command
command.callback = (err, res) => { command.callback = (err, res) => {
res += 'added:\r\n' res += 'added:\r\n'
callback(err, res) callback(err, res)
@@ -528,7 +531,8 @@ describe('connection tests', () => {
client.info = function () { client.info = function () {
return tmp().then((res) => { return tmp().then((res) => {
if (!delayed) { if (!delayed) {
// Try reconnecting after one second even if redis tells us the time needed is above one second // Try reconnecting after one second even if redis tells us the
// time needed is above one second
client.serverInfo.persistence.loading = 1 client.serverInfo.persistence.loading = 1
client.serverInfo.persistence.loading_eta_seconds = 2.5 client.serverInfo.persistence.loading_eta_seconds = 2.5
delayed = true delayed = true

View File

@@ -1,10 +1,11 @@
'use strict' 'use strict'
const Buffer = require('buffer').Buffer const { Buffer } = require('buffer')
const assert = require('assert') const assert = require('assert')
const config = require('./lib/config') const config = require('./lib/config')
const helper = require('./helper') const helper = require('./helper')
const redis = config.redis
const { redis } = config
describe('detectBuffers', () => { describe('detectBuffers', () => {
let client let client
@@ -61,7 +62,8 @@ describe('detectBuffers', () => {
.hget(Buffer.from('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', Buffer.from('key 2'))
.hget('hash key 2', 'key 2') .hget('hash key 2', 'key 2')
.exec().then((reply) => { .exec()
.then((reply) => {
assert.strictEqual(true, Array.isArray(reply)) assert.strictEqual(true, Array.isArray(reply))
assert.strictEqual(4, reply.length) assert.strictEqual(4, reply.length)
assert.strictEqual('val 1', reply[0]) assert.strictEqual('val 1', reply[0])
@@ -81,7 +83,8 @@ describe('detectBuffers', () => {
.hget(Buffer.from('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', Buffer.from('key 2'))
.hget('hash key 2', 'key 2') .hget('hash key 2', 'key 2')
.exec().then((reply) => { .exec()
.then((reply) => {
assert.strictEqual(true, Array.isArray(reply)) assert.strictEqual(true, Array.isArray(reply))
assert.strictEqual(4, reply.length) assert.strictEqual(4, reply.length)
assert.strictEqual('val 1', reply[0]) assert.strictEqual('val 1', reply[0])

View File

@@ -3,8 +3,9 @@
const assert = require('assert') const assert = require('assert')
const config = require('./lib/config') const config = require('./lib/config')
const helper = require('./helper') const helper = require('./helper')
const fork = require('child_process').fork const { fork } = require('child_process')
const redis = config.redis
const { redis } = config
describe('stack traces', () => { describe('stack traces', () => {
it('should return good traces with NODE_ENV=development set', (done) => { it('should return good traces with NODE_ENV=development set', (done) => {

View File

@@ -5,6 +5,7 @@ const path = require('path')
const config = require('./lib/config') const config = require('./lib/config')
const RedisProcess = require('./lib/redis-process') const RedisProcess = require('./lib/redis-process')
const StunnelProcess = require('./lib/stunnel-process') const StunnelProcess = require('./lib/stunnel-process')
let rp let rp
let stunnelProcess let stunnelProcess
@@ -16,7 +17,7 @@ process.on('unhandledRejection', (err, promise) => {
}) })
}) })
function startRedis (conf, done, port) { function startRedis(conf, done, port) {
RedisProcess.start((err, _rp) => { RedisProcess.start((err, _rp) => {
rp = _rp rp = _rp
return done(err) return done(err)
@@ -37,7 +38,7 @@ if (!process.env.REDIS_TESTS_STARTED) {
}) })
} }
function arrayHelper (results) { function arrayHelper(results) {
if (results instanceof Array) { if (results instanceof Array) {
assert.strictEqual(results.length, 1, 'The array length may only be one element') assert.strictEqual(results.length, 1, 'The array length may only be one element')
return results[0] return results[0]
@@ -45,7 +46,7 @@ function arrayHelper (results) {
return results return results
} }
function toString (res) { function toString(res) {
// If options are passed to return either strings or buffers... // If options are passed to return either strings or buffers...
if (Buffer.isBuffer(res)) { if (Buffer.isBuffer(res)) {
return res.toString() return res.toString()
@@ -55,40 +56,40 @@ function toString (res) {
} }
// Stringify all values as well // Stringify all values as well
if (typeof res === 'object' && res !== null) { if (typeof res === 'object' && res !== null) {
Object.keys(res).map((key) => (res[key] = toString(res[key]))) Object.keys(res).forEach((key) => { res[key] = toString(res[key]) })
} }
return res return res
} }
module.exports = { module.exports = {
redisProcess () { redisProcess() {
return rp return rp
}, },
stopRedis (done) { stopRedis(done) {
rp.stop(done) rp.stop(done)
}, },
startRedis, startRedis,
stopStunnel (done) { stopStunnel(done) {
if (stunnelProcess) { if (stunnelProcess) {
StunnelProcess.stop(stunnelProcess, done) StunnelProcess.stop(stunnelProcess, done)
} else { } else {
done() done()
} }
}, },
startStunnel (done) { startStunnel(done) {
StunnelProcess.start((err, _stunnelProcess) => { StunnelProcess.start((err, _stunnelProcess) => {
stunnelProcess = _stunnelProcess stunnelProcess = _stunnelProcess
return done(err) return done(err)
}, path.resolve(__dirname, './conf')) }, path.resolve(__dirname, './conf'))
}, },
isNumber (expected) { isNumber(expected) {
return function (results) { return function (results) {
results = arrayHelper(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}`) assert.strictEqual(typeof results, 'number', `expected a number, got ${typeof results}`)
} }
}, },
isString (str) { isString(str) {
str = `${str}` // Make sure it's a string str = `${str}` // Make sure it's a string
return function (results) { return function (results) {
results = arrayHelper(results) results = arrayHelper(results)
@@ -96,50 +97,51 @@ module.exports = {
assert.strictEqual(results, str, `${str} does not match ${results}`) assert.strictEqual(results, str, `${str} does not match ${results}`)
} }
}, },
isNull () { isNull() {
return function (results) { return function (results) {
results = arrayHelper(results) results = arrayHelper(results)
assert.strictEqual(results, null, `${results} is not null`) assert.strictEqual(results, null, `${results} is not null`)
} }
}, },
isUndefined () { isUndefined() {
return function (results) { return function (results) {
results = arrayHelper(results) results = arrayHelper(results)
assert.strictEqual(results, undefined, `${results} is not undefined`) assert.strictEqual(results, undefined, `${results} is not undefined`)
} }
}, },
isError (regex) { isError(regex) {
return function (err, res) { return function (err, res) {
assert.strictEqual(res, undefined, 'There should be an error, no result!') 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.') assert(err instanceof Error, 'err is not instance of \'Error\', but an error is expected here.')
if (regex) assert(regex.test(err.message)) if (regex) assert(regex.test(err.message))
} }
}, },
isDeepEqual (args) { isDeepEqual(args) {
return function (res) { return function (res) {
res = toString(res) res = toString(res)
assert.deepStrictEqual(res, args) assert.deepStrictEqual(res, args)
} }
}, },
match (pattern) { match(pattern) {
return function (results) { return function (results) {
results = arrayHelper(results) results = arrayHelper(results)
assert(pattern.test(results), `expected string '${results}' to match ${pattern.toString()}`) assert(pattern.test(results), `expected string '${results}' to match ${pattern.toString()}`)
} }
}, },
fail (err) { fail(err) {
err = err instanceof Error err = err instanceof Error
? err ? err
: new Error('This should not be reachable') : new Error('This should not be reachable')
throw err throw err
}, },
serverVersionAtLeast (connection, desiredVersion) { serverVersionAtLeast(connection, desiredVersion) {
// Wait until a connection has established (otherwise a timeout is going to be triggered at some point) // Wait until a connection has established (otherwise a timeout is going to
// be triggered at some point)
if (Object.keys(connection.serverInfo).length === 0) { if (Object.keys(connection.serverInfo).length === 0) {
throw new Error('Version check not possible as the client is not yet ready or did not expose the version') throw new Error('Version check not possible as the client is not yet ready or did not expose the version')
} }
// Return true if the server version >= desiredVersion // Return true if the server version >= desiredVersion
const version = connection.serverInfo.server.version const { version } = connection.serverInfo.server
for (let i = 0; i < 3; i++) { for (let i = 0; i < 3; i++) {
if (version[i] > desiredVersion[i]) { if (version[i] > desiredVersion[i]) {
return true return true
@@ -151,7 +153,7 @@ module.exports = {
} }
return true return true
}, },
allTests (opts, cb) { allTests(opts, cb) {
if (!cb) { if (!cb) {
cb = opts cb = opts
opts = {} opts = {}
@@ -167,11 +169,9 @@ module.exports = {
}] }]
options.forEach((options) => { options.forEach((options) => {
let strOptions = '' let strOptions = ''
let key const keys = Object.keys(options)
for (key in options) { for (let i = 0; i < keys.length; i++) {
if (options.hasOwnProperty(key)) { strOptions += `${keys[i]}: ${options[keys[i]]}; `
strOptions += `${key}: ${options[key]}; `
}
} }
describe(`using options: ${strOptions}`, () => { describe(`using options: ${strOptions}`, () => {
protocols.forEach((ip, i) => { protocols.forEach((ip, i) => {
@@ -183,12 +183,12 @@ module.exports = {
}) })
}) })
}, },
removeMochaListener () { removeMochaListener() {
const mochaListener = process.listeners('uncaughtException').pop() const mochaListener = process.listeners('uncaughtException').pop()
process.removeListener('uncaughtException', mochaListener) process.removeListener('uncaughtException', mochaListener)
return mochaListener return mochaListener
}, },
callFuncAfter (func, max) { callFuncAfter(func, max) {
let i = 0 let i = 0
return function () { return function () {
i++ i++
@@ -199,7 +199,7 @@ module.exports = {
return false return false
} }
}, },
killConnection (client) { killConnection(client) {
// Change the connection option to a non existing one and destroy the stream // Change the connection option to a non existing one and destroy the stream
client._connectionOptions = { client._connectionOptions = {
port: 65535, port: 65535,

View File

@@ -11,7 +11,7 @@ const config = {
IPv4: '127.0.0.1', IPv4: '127.0.0.1',
IPv6: '::1' IPv6: '::1'
}, },
configureClient (ip, opts) { configureClient(ip, opts) {
const args = [] const args = []
// Do not manipulate the opts => copy them each time // Do not manipulate the opts => copy them each time
opts = opts ? JSON.parse(JSON.stringify(opts)) : {} opts = opts ? JSON.parse(JSON.stringify(opts)) : {}

View File

@@ -1,8 +1,10 @@
// Spawned by the goodStacks.spec.js tests // Spawned by the goodStacks.spec.js tests
'use strict' 'use strict'
const assert = require('assert') const assert = require('assert')
const redis = require('../../index') const redis = require('../../index')
const client = redis.createClient() const client = redis.createClient()
// Both error cases would normally return bad stack traces // Both error cases would normally return bad stack traces

View File

@@ -9,7 +9,7 @@ const tcpPortUsed = require('tcp-port-used')
// wait for redis to be listening in // wait for redis to be listening in
// all three modes (ipv4, ipv6, socket). // all three modes (ipv4, ipv6, socket).
function waitForRedis (available, cb, port) { function waitForRedis(available, cb, port) {
if (process.platform === 'win32') return cb() if (process.platform === 'win32') return cb()
const time = Date.now() const time = Date.now()
@@ -49,7 +49,7 @@ function waitForRedis (available, cb, port) {
} }
module.exports = { module.exports = {
start (done, conf, port) { start(done, conf, port) {
let spawnFailed = false let spawnFailed = false
// spawn redis with our testing configuration. // spawn redis with our testing configuration.
const confFile = conf || path.resolve(__dirname, '../conf/redis.conf') const confFile = conf || path.resolve(__dirname, '../conf/redis.conf')
@@ -67,10 +67,10 @@ module.exports = {
// return an object that can be used in // return an object that can be used in
// an after() block to shutdown redis. // an after() block to shutdown redis.
return done(null, { return done(null, {
spawnFailed () { spawnFailed() {
return spawnFailed return spawnFailed
}, },
stop (done) { stop(done) {
if (spawnFailed) return done() if (spawnFailed) return done()
rp.once('exit', (code) => { rp.once('exit', (code) => {
let error = null let error = null

View File

@@ -1,22 +1,22 @@
'use strict' 'use strict'
// Helper to start and stop the stunnel process. // Helper to start and stop the stunnel process.
const spawn = require('child_process').spawn const { spawn } = require('child_process')
const EventEmitter = require('events') const EventEmitter = require('events')
const fs = require('fs') const fs = require('fs')
const path = require('path') const path = require('path')
const util = require('util') const util = require('util')
function once (cb) { function once(cb) {
let called = false let called = false
return function () { return function (...args) {
if (called) return if (called) return
called = true called = true
cb.apply(this, arguments) cb.apply(this, args)
} }
} }
function StunnelProcess (confDir) { function StunnelProcess(confDir) {
EventEmitter.call(this) EventEmitter.call(this)
// Set up an stunnel to redis; edit the conf file to include required absolute paths // Set up an stunnel to redis; edit the conf file to include required absolute paths
@@ -24,7 +24,8 @@ function StunnelProcess (confDir) {
const confText = fs.readFileSync(`${confFile}.template`).toString().replace(/__dirname/g, confDir) const confText = fs.readFileSync(`${confFile}.template`).toString().replace(/__dirname/g, confDir)
fs.writeFileSync(confFile, confText) fs.writeFileSync(confFile, confText)
const stunnel = this.stunnel = spawn('stunnel', [confFile]) this.stunnel = spawn('stunnel', [confFile])
const { stunnel } = this
// Handle child process events, and failure to set up tunnel // Handle child process events, and failure to set up tunnel
this.timer = setTimeout(() => { this.timer = setTimeout(() => {
@@ -67,13 +68,13 @@ StunnelProcess.prototype.stop = function (done) {
} }
module.exports = { module.exports = {
start (done, confDir) { start(doneOrig, confDir) {
done = once(done) const done = once(doneOrig)
const stunnel = new StunnelProcess(confDir) const stunnel = new StunnelProcess(confDir)
stunnel.once('error', done.bind(done)) stunnel.once('error', done.bind(done))
stunnel.once('started', done.bind(done, null, stunnel)) stunnel.once('started', done.bind(done, null, stunnel))
}, },
stop (stunnel, done) { stop(stunnel, done) {
stunnel.removeAllListeners() stunnel.removeAllListeners()
stunnel.stop() stunnel.stop()
stunnel.once('error', done.bind(done)) stunnel.once('error', done.bind(done))

View File

@@ -1,14 +1,16 @@
// spawned by the unref tests in nodeRedis.spec.js. // spawned by the unref tests in nodeRedis.spec.js.
// when configured, unref causes the client to exit // when configured, unref causes the client to exit
// as soon as there are no outstanding commands. // as soon as there are no outstanding commands.
'use strict' 'use strict'
const redis = require('../../index') const redis = require('../../index')
const HOST = process.argv[2] || '127.0.0.1' const HOST = process.argv[2] || '127.0.0.1'
const PORT = process.argv[3] const PORT = process.argv[3]
const args = PORT ? [PORT, HOST] : [HOST] const args = PORT ? [PORT, HOST] : [HOST]
const c = redis.createClient.apply(redis, args) const c = redis.createClient(...args)
c.info((err, reply) => { c.info((err, reply) => {
if (err) process.exit(-1) if (err) process.exit(-1)
if (!reply.length) process.exit(-1) if (!reply.length) process.exit(-1)

View File

@@ -1,12 +1,14 @@
'use strict' 'use strict'
const Buffer = require('buffer').Buffer const { Buffer } = require('buffer')
const assert = require('assert') const assert = require('assert')
const config = require('./lib/config') const config = require('./lib/config')
const helper = require('./helper') const helper = require('./helper')
const utils = require('../lib/utils') const utils = require('../lib/utils')
const redis = config.redis
const { redis } = config
const zlib = require('zlib') const zlib = require('zlib')
let client let client
describe('The \'multi\' method', () => { describe('The \'multi\' method', () => {
@@ -23,32 +25,32 @@ describe('The \'multi\' method', () => {
// Some random object created from http://beta.json-generator.com/ // Some random object created from http://beta.json-generator.com/
const testObj = { const testObj = {
'Id': '5642c4c33d4667c4a1fefd99', Id: '5642c4c33d4667c4a1fefd99',
'index': 0, index: 0,
'guid': '5baf1f1c-7621-41e7-ae7a-f8c6f3199b0f', guid: '5baf1f1c-7621-41e7-ae7a-f8c6f3199b0f',
'isActive': true, isActive: true,
'balance': '$1,028.63', balance: '$1,028.63',
'picture': 'http://placehold.it/32x32', picture: 'http://placehold.it/32x32',
'age': 31, age: 31,
'eyeColor': 'green', eyeColor: 'green',
'name': {'first': 'Shana', 'last': 'Long'}, name: { first: 'Shana', last: 'Long' },
'company': 'MANGLO', company: 'MANGLO',
'email': 'shana.long@manglo.us', email: 'shana.long@manglo.us',
'phone': '+1 (926) 405-3105', phone: '+1 (926) 405-3105',
'address': '747 Dank Court, Norfolk, Ohio, 1112', address: '747 Dank Court, Norfolk, Ohio, 1112',
'about': 'Eu pariatur in nisi occaecat enim qui consequat nostrud cupidatat id. ' + about: 'Eu pariatur in nisi occaecat enim qui consequat nostrud cupidatat id. ' +
'Commodo commodo dolore esse irure minim quis deserunt anim laborum aute deserunt et est. Quis nisi laborum deserunt nisi quis.', 'Commodo commodo dolore esse irure minim quis deserunt anim laborum aute deserunt et est. Quis nisi laborum deserunt nisi quis.',
'registered': 'Friday, April 18, 2014 9:56 AM', registered: 'Friday, April 18, 2014 9:56 AM',
'latitude': '74.566613', latitude: '74.566613',
'longitude': '-11.660432', longitude: '-11.660432',
'tags': [7, 'excepteur'], tags: [7, 'excepteur'],
'range': [0, 1, 2, 3, 4, 5, 6, 7, 8, 9], range: [0, 1, 2, 3, 4, 5, 6, 7, 8, 9],
'friends': [3, {'id': 1, 'name': 'Schultz Dyer'}], friends: [3, { id: 1, name: 'Schultz Dyer' }],
'greeting': 'Hello, Shana! You have 5 unread messages.', greeting: 'Hello, Shana! You have 5 unread messages.',
'favoriteFruit': 'strawberry' favoriteFruit: 'strawberry'
} }
function run () { function run() {
if (end() === true) { if (end() === true) {
return return
} }
@@ -132,10 +134,11 @@ describe('The \'multi\' method', () => {
it('results in a execabort #2', () => { it('results in a execabort #2', () => {
// Check that using monitor with a transactions results in an error // Check that using monitor with a transactions results in an error
return client.multi().set('foo', 'bar').monitor().exec().then(assert, (err) => { return client.multi().set('foo', 'bar').monitor().exec()
assert.strictEqual(err.code, 'EXECABORT') .then(assert, (err) => {
client.end(false) assert.strictEqual(err.code, 'EXECABORT')
}) client.end(false)
})
}) })
it('sanity check', (done) => { it('sanity check', (done) => {
@@ -178,7 +181,7 @@ describe('The \'multi\' method', () => {
const multi1 = client.multi() const multi1 = client.multi()
multi1.set('m1', '123') multi1.set('m1', '123')
multi1.get('m1') multi1.get('m1')
multi1.exec().then(() => (called = true)) multi1.exec().then(() => { called = true })
client.once('ready', () => { client.once('ready', () => {
const multi1 = client.multi() const multi1 = client.multi()
multi1.set('m2', '456') multi1.set('m2', '456')
@@ -199,7 +202,7 @@ describe('The \'multi\' method', () => {
host: 'somewhere', host: 'somewhere',
port: 6379, port: 6379,
family: 'IPv6', family: 'IPv6',
retryStrategy () {} retryStrategy() {}
}) })
assert.strictEqual(client._connectionOptions.family, 6) assert.strictEqual(client._connectionOptions.family, 6)
@@ -334,8 +337,8 @@ describe('The \'multi\' method', () => {
[['hmset', 'multihmset2', 'multibar2', 'multifoo3', 'multibar3', 'test']], [['hmset', 'multihmset2', 'multibar2', 'multifoo3', 'multibar3', 'test']],
['hmset', ['multihmset', 'multibar', 'multifoo']], ['hmset', ['multihmset', 'multibar', 'multifoo']],
['hmset', arr3], ['hmset', arr3],
['hmset', now, {123456789: 'abcdefghij', 'some manner of key': 'a type of value', 'otherTypes': 555}], ['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}], ['hmset', 'key2', { '0123456789': 'abcdefghij', 'some manner of key': 'a type of value', otherTypes: 999 }],
['hmset', 'multihmset', ['multibar', 'multibaz']], ['hmset', 'multihmset', ['multibar', 'multibaz']],
['hmset', 'multihmset', ['multibar', 'multibaz']] ['hmset', 'multihmset', ['multibar', 'multibaz']]
]) ])
@@ -343,7 +346,8 @@ describe('The \'multi\' method', () => {
.hmget('key2', arr2) .hmget('key2', arr2)
.hmget(['multihmset2', 'some manner of key', 'multibar3']) .hmget(['multihmset2', 'some manner of key', 'multibar3'])
.mget('multifoo2', ['multifoo3', 'multifoo']) .mget('multifoo2', ['multifoo3', 'multifoo'])
.exec().then((replies) => { .exec()
.then((replies) => {
assert.strictEqual(arr.length, 3) assert.strictEqual(arr.length, 3)
assert.strictEqual(arr2.length, 2) assert.strictEqual(arr2.length, 2)
assert.strictEqual(arr3.length, 3) assert.strictEqual(arr3.length, 3)
@@ -376,7 +380,8 @@ describe('The \'multi\' method', () => {
.incr('some') .incr('some')
.incr('keys') .incr('keys')
.mget('some', ['keys']) .mget('some', ['keys'])
.exec().then(helper.isDeepEqual(['OK', 11, 21, ['11', '21']])) .exec()
.then(helper.isDeepEqual(['OK', 11, 21, ['11', '21']]))
}) })
it('allows an array to be provided indicating multiple operations to perform', () => { it('allows an array to be provided indicating multiple operations to perform', () => {
@@ -395,7 +400,8 @@ describe('The \'multi\' method', () => {
things: 'here' things: 'here'
}) })
.hgetall('multihash') .hgetall('multihash')
.exec().then((replies) => { .exec()
.then((replies) => {
assert.strictEqual('OK', replies[0]) assert.strictEqual('OK', replies[0])
assert.strictEqual(Object.keys(replies[2]).length, 4) assert.strictEqual(Object.keys(replies[2]).length, 4)
assert.strictEqual('foo', replies[2].a) assert.strictEqual('foo', replies[2].a)
@@ -406,28 +412,32 @@ describe('The \'multi\' method', () => {
}) })
it('reports EXECABORT exceptions when they occur (while queueing)', () => { it('reports EXECABORT exceptions when they occur (while queueing)', () => {
return client.multi().config('bar').set('foo').set('bar').exec().then(assert, (err) => { return client.multi().config('bar').set('foo').set('bar')
assert.strictEqual(err.code, 'EXECABORT') .exec()
assert(err.message.match(/^EXECABORT/), 'Error message should begin with EXECABORT') .then(assert, (err) => {
assert.strictEqual(err.errors.length, 2, 'err.errors should have 2 items') assert.strictEqual(err.code, 'EXECABORT')
assert.strictEqual(err.errors[0].command, 'SET') assert(err.message.match(/^EXECABORT/), 'Error message should begin with EXECABORT')
assert.strictEqual(err.errors[0].code, 'ERR') assert.strictEqual(err.errors.length, 2, 'err.errors should have 2 items')
assert.strictEqual(err.errors[0].position, 1) assert.strictEqual(err.errors[0].command, 'SET')
assert(/^ERR/.test(err.errors[0].message), 'Actual error message should begin with ERR') assert.strictEqual(err.errors[0].code, 'ERR')
}) assert.strictEqual(err.errors[0].position, 1)
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)', () => { 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) => { return client.multi().config('bar').debug('foo').eval('return {err=\'this is an error\'}', 0)
assert.strictEqual(err.replies.length, 3) .exec()
assert.strictEqual(err.replies[0].code, 'ERR') .then(assert, (err) => {
assert.strictEqual(err.replies[0].command, 'CONFIG') assert.strictEqual(err.replies.length, 3)
assert.strictEqual(err.replies[2].code, undefined) assert.strictEqual(err.replies[0].code, 'ERR')
assert.strictEqual(err.replies[2].command, 'EVAL') assert.strictEqual(err.replies[0].command, 'CONFIG')
assert(/^this is an error/.test(err.replies[2].message)) assert.strictEqual(err.replies[2].code, undefined)
assert(/^ERR/.test(err.replies[0].message), 'Error message should begin with ERR') assert.strictEqual(err.replies[2].command, 'EVAL')
assert(/^ERR/.test(err.replies[1].message), 'Error message should begin with ERR') 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('should not use a transaction with execAtomic if no command is used', () => { it('should not use a transaction with execAtomic if no command is used', () => {
@@ -487,16 +497,19 @@ describe('The \'multi\' method', () => {
}) })
it('indivdual commands work properly with multi', () => { it('indivdual commands work properly with multi', () => {
// Neither of the following work properly in a transactions: // Neither of the following work properly in a transactions: (This is
// (This is due to Redis not returning the reply as expected / resulting in undefined behavior) // due to Redis not returning the reply as expected / resulting in
// (Likely there are more commands that do not work with a transaction) // undefined behavior) (Likely there are more commands that do not
// work with a transaction)
// //
// auth => can't be called after a multi command // auth => can't be called after a multi command monitor => results in
// monitor => results in faulty return values e.g. multi().monitor().set('foo', 'bar').get('foo') // faulty return values e.g. multi().monitor().set('foo',
// returns ['OK, 'OK', 'monitor reply'] instead of ['OK', 'OK', 'bar'] // 'bar').get('foo') returns ['OK, 'OK', 'monitor reply'] instead of
// quit => ends the connection before the exec // ['OK', 'OK', 'bar'] quit => ends the connection before the exec
// client reply skip|off => results in weird return values. Not sure what exactly happens // client reply skip|off => results in weird return values. Not sure
// subscribe => enters subscribe mode and this does not work in combination with exec (the same for psubscribe, unsubscribe...) // what exactly happens subscribe => enters subscribe mode and this
// does not work in combination with exec (the same for psubscribe,
// unsubscribe...)
// //
// Make sure sendCommand is not called // Make sure sendCommand is not called
@@ -514,7 +527,10 @@ describe('The \'multi\' method', () => {
return multi.exec().then((res) => { return multi.exec().then((res) => {
res[2] = res[2].substr(0, 10) res[2] = res[2].substr(0, 10)
assert.strictEqual(client.selectedDb, 5) assert.strictEqual(client.selectedDb, 5)
assert.deepStrictEqual(client.serverInfo.keyspace.db5, { avg_ttl: 0, expires: 0, keys: 1 }) assert.deepStrictEqual(
client.serverInfo.keyspace.db5,
{ avg_ttl: 0, expires: 0, keys: 1 }
)
assert.deepStrictEqual(res, ['OK', 'OK', '# Server\r\n', 'bar']) assert.deepStrictEqual(res, ['OK', 'OK', '# Server\r\n', 'bar'])
return client.flushdb() return client.flushdb()
}) })

View File

@@ -1,14 +1,15 @@
'use strict' 'use strict'
const Buffer = require('buffer').Buffer const { Buffer } = require('buffer')
const assert = require('assert') const assert = require('assert')
const fs = require('fs') const fs = require('fs')
const path = require('path') const path = require('path')
const config = require('./lib/config') const config = require('./lib/config')
const helper = require('./helper') const helper = require('./helper')
const fork = require('child_process').fork const { fork } = require('child_process')
const Errors = require('redis-errors') const Errors = require('redis-errors')
const redis = config.redis
const { redis } = config
let client let client
describe('The nodeRedis client', () => { describe('The nodeRedis client', () => {
@@ -41,7 +42,7 @@ describe('The nodeRedis client', () => {
it('reset the parser while reconnecting (See #1190)', (done) => { it('reset the parser while reconnecting (See #1190)', (done) => {
const client = redis.createClient({ const client = redis.createClient({
retryStrategy () { retryStrategy() {
return 5 return 5
} }
}) })
@@ -113,10 +114,9 @@ describe('The nodeRedis client', () => {
assert.strictEqual(client2.selectedDb, 2) assert.strictEqual(client2.selectedDb, 2)
assert(client.connected) assert(client.connected)
assert(!client2.connected) assert(!client2.connected)
for (const elem in client._options) { const keys = Object.keys(client._options)
if (client._options.hasOwnProperty(elem)) { for (let i = 0; i < keys.length; i++) {
assert.strictEqual(client2._options[elem], client._options[elem]) assert.strictEqual(client2._options[keys[i]], client._options[keys[i]])
}
} }
client2.on('error', (err) => { client2.on('error', (err) => {
assert.strictEqual(err.message, 'Connection forcefully ended and command aborted.') assert.strictEqual(err.message, 'Connection forcefully ended and command aborted.')
@@ -140,11 +140,10 @@ describe('The nodeRedis client', () => {
assert.strictEqual(client._options.noReadyCheck, undefined) assert.strictEqual(client._options.noReadyCheck, undefined)
assert.strictEqual(client2._options.noReadyCheck, true) assert.strictEqual(client2._options.noReadyCheck, true)
assert.notDeepEqual(client._options, client2._options) assert.notDeepEqual(client._options, client2._options)
for (const elem in client._options) { const keys = Object.keys(client._options)
if (client._options.hasOwnProperty(elem)) { for (let i = 0; i < keys.length; i++) {
if (elem !== 'noReadyCheck') { if (keys[i] !== 'noReadyCheck') {
assert.strictEqual(client2._options[elem], client._options[elem]) assert.strictEqual(client2._options[keys[i]], client._options[keys[i]])
}
} }
} }
client2.on('ready', () => { client2.on('ready', () => {
@@ -190,12 +189,14 @@ describe('The nodeRedis client', () => {
}) })
it('using multi with sendCommand should work as individual command instead of using the internal multi', () => { 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 multi as you want in nodeRedis // This is necessary to keep backwards compatibility and it is the
// only way to handle multi as you want in nodeRedis
client.sendCommand('multi') client.sendCommand('multi')
client.sendCommand('set', ['foo', 'bar']).then(helper.isString('QUEUED')) client.sendCommand('set', ['foo', 'bar']).then(helper.isString('QUEUED'))
client.get('foo') client.get('foo')
// exec is not manipulated if not fired by the individual multi command // exec is not manipulated if not fired by the individual multi
// As the multi command is handled individually by the user he also has to handle the return value // command As the multi command is handled individually by the user
// he also has to handle the return value
return client.exec().then(helper.isDeepEqual(['OK', 'bar'])) return client.exec().then(helper.isDeepEqual(['OK', 'bar']))
}) })
@@ -205,7 +206,8 @@ describe('The nodeRedis client', () => {
client.sendCommand('set', args).then(helper.isString('QUEUED')) client.sendCommand('set', args).then(helper.isString('QUEUED'))
assert.deepStrictEqual(args, ['test', 'bla']) // Check args manipulation assert.deepStrictEqual(args, ['test', 'bla']) // Check args manipulation
client.get('test').then(helper.isString('QUEUED')) 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 // As the multi command is handled individually by the user he also
// has to handle the return value
return client.exec().then(helper.isDeepEqual(['OK', 'bla'])) return client.exec().then(helper.isDeepEqual(['OK', 'bla']))
}) })
@@ -245,13 +247,15 @@ describe('The nodeRedis client', () => {
client.sendCommand('set', args).then(helper.isString('QUEUED')) client.sendCommand('set', args).then(helper.isString('QUEUED'))
assert.deepStrictEqual(args, ['test', 'bla']) // Check args manipulation assert.deepStrictEqual(args, ['test', 'bla']) // Check args manipulation
client.get('test').then(helper.isString('QUEUED')) 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 // As the multi command is handled individually by the user he also
// has to handle the return value
return client.exec().then(helper.isDeepEqual(['OK', 'bla'])) return client.exec().then(helper.isDeepEqual(['OK', 'bla']))
}) })
it('the args array may contain a arbitrary number of arguments', () => { it('the args array may contain a arbitrary number of arguments', () => {
client.sendCommand('mset', ['foo', 1, 'bar', 2, 'baz', 3]).then(helper.isString('OK')) 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 // As the multi command is handled individually by the user he also
// has to handle the return value
return client.mget(['foo', 'bar', 'baz']).then(helper.isDeepEqual(['1', '2', '3'])) return client.mget(['foo', 'bar', 'baz']).then(helper.isDeepEqual(['1', '2', '3']))
}) })
@@ -340,8 +344,8 @@ describe('The nodeRedis client', () => {
describe('when redis closes unexpectedly', () => { describe('when redis closes unexpectedly', () => {
it('reconnects and can retrieve the pre-existing data', (done) => { it('reconnects and can retrieve the pre-existing data', (done) => {
client.on('reconnecting', function onRecon (params) { client.on('reconnecting', function onRecon(params) {
client.on('connect', function onConnect () { client.on('connect', function onConnect() {
const end = helper.callFuncAfter(() => { const end = helper.callFuncAfter(() => {
client.removeListener('connect', onConnect) client.removeListener('connect', onConnect)
client.removeListener('reconnecting', onRecon) client.removeListener('reconnecting', onRecon)
@@ -365,8 +369,8 @@ describe('The nodeRedis client', () => {
}) })
it('reconnects properly when monitoring', (done) => { it('reconnects properly when monitoring', (done) => {
client.on('reconnecting', function onRecon (params) { client.on('reconnecting', function onRecon(params) {
client.on('ready', function onReady () { client.on('ready', function onReady() {
assert.strictEqual(client._monitoring, true, 'monitoring after reconnect') assert.strictEqual(client._monitoring, true, 'monitoring after reconnect')
client.removeListener('ready', onReady) client.removeListener('ready', onReady)
client.removeListener('reconnecting', onRecon) client.removeListener('reconnecting', onRecon)
@@ -502,7 +506,8 @@ describe('The nodeRedis client', () => {
assert.strictEqual(err.message, 'Protocol error, got "a" as reply type byte. Please report this.') assert.strictEqual(err.message, 'Protocol error, got "a" as reply type byte. Please report this.')
assert.strictEqual(err, error) assert.strictEqual(err, error)
assert(err instanceof redis.ParserError) assert(err instanceof redis.ParserError)
// After the hard failure work properly again. The set should have been processed properly too // After the hard failure work properly again. The set should have
// been processed properly too
client.get('foo').then(helper.isString('bar')).then(done) client.get('foo').then(helper.isString('bar')).then(done)
}) })
client.once('ready', () => { client.once('ready', () => {
@@ -512,9 +517,9 @@ describe('The nodeRedis client', () => {
assert(err instanceof redis.InterruptError) assert(err instanceof redis.InterruptError)
error = err.origin error = err.origin
}) })
// Make sure we call execute out of the reply. // Make sure we call execute out of the reply. Ready is called in a
// Ready is called in a reply. // reply. Fail the set answer. Has no corresponding command obj and
// Fail the set answer. Has no corresponding command obj and will therefore land in the error handler and set // will therefore land in the error handler and set
process.nextTick(() => client._replyParser.execute(Buffer.from('a*1\r*1\r$1`zasd\r\na'))) process.nextTick(() => client._replyParser.execute(Buffer.from('a*1\r*1\r$1`zasd\r\na')))
}) })
}) })
@@ -545,7 +550,7 @@ describe('The nodeRedis client', () => {
it('enqueues operation and keep the queue while trying to reconnect', (done) => { it('enqueues operation and keep the queue while trying to reconnect', (done) => {
client = redis.createClient(9999, null, { client = redis.createClient(9999, null, {
retryStrategy (options) { retryStrategy(options) {
if (options.attempt < 4) { if (options.attempt < 4) {
return 50 return 50
} }
@@ -587,6 +592,7 @@ describe('The nodeRedis client', () => {
}) })
it('flushes the command queue if connection is lost', (done) => { it('flushes the command queue if connection is lost', (done) => {
const end = helper.callFuncAfter(done, 2)
client = redis.createClient() client = redis.createClient()
client.once('ready', () => { client.once('ready', () => {
@@ -607,8 +613,6 @@ describe('The nodeRedis client', () => {
assert.strictEqual(client.commandQueue.length, 15) assert.strictEqual(client.commandQueue.length, 15)
helper.killConnection(client) helper.killConnection(client)
}) })
const end = helper.callFuncAfter(done, 2)
client.on('error', (err) => { client.on('error', (err) => {
assert.strictEqual(err.code, 'ECONNREFUSED') assert.strictEqual(err.code, 'ECONNREFUSED')
assert.strictEqual(err.errno, 'ECONNREFUSED') assert.strictEqual(err.errno, 'ECONNREFUSED')

View File

@@ -3,7 +3,8 @@
const assert = require('assert') const assert = require('assert')
const config = require('./lib/config') const config = require('./lib/config')
const helper = require('./helper') const helper = require('./helper')
const redis = config.redis
const { redis } = config
describe('prefix key names', () => { describe('prefix key names', () => {
helper.allTests((ip, args) => { helper.allTests((ip, args) => {

View File

@@ -1,10 +1,11 @@
'use strict' 'use strict'
const Buffer = require('buffer').Buffer const { Buffer } = require('buffer')
const assert = require('assert') const assert = require('assert')
const config = require('./lib/config') const config = require('./lib/config')
const helper = require('./helper') const helper = require('./helper')
const redis = config.redis
const { redis } = config
describe('publish/subscribe', () => { describe('publish/subscribe', () => {
helper.allTests((ip, args) => { helper.allTests((ip, args) => {
@@ -285,7 +286,7 @@ describe('publish/subscribe', () => {
}) })
it('unsubscribes, subscribes, unsubscribes... single and multiple entries mixed', (done) => { it('unsubscribes, subscribes, unsubscribes... single and multiple entries mixed', (done) => {
function subscribe (channels) { function subscribe(channels) {
sub.unsubscribe().then(helper.isNull) sub.unsubscribe().then(helper.isNull)
sub.subscribe(channels).then(helper.isNull) sub.subscribe(channels).then(helper.isNull)
} }
@@ -313,7 +314,7 @@ describe('publish/subscribe', () => {
}) })
it('unsubscribes, subscribes, unsubscribes... single and multiple entries mixed. Without concrete channels', (done) => { it('unsubscribes, subscribes, unsubscribes... single and multiple entries mixed. Without concrete channels', (done) => {
function subscribe (channels) { function subscribe(channels) {
sub.unsubscribe(channels) sub.unsubscribe(channels)
sub.unsubscribe(channels) sub.unsubscribe(channels)
sub.subscribe(channels) sub.subscribe(channels)
@@ -342,7 +343,7 @@ describe('publish/subscribe', () => {
}) })
it('unsubscribes, subscribes, unsubscribes... with pattern matching', (done) => { it('unsubscribes, subscribes, unsubscribes... with pattern matching', (done) => {
function subscribe (channels, callback) { function subscribe(channels, callback) {
sub.punsubscribe('prefix:*').then(helper.isNull) sub.punsubscribe('prefix:*').then(helper.isNull)
sub.psubscribe(channels).then(callback) sub.psubscribe(channels).then(callback)
} }
@@ -461,7 +462,7 @@ describe('publish/subscribe', () => {
const end = helper.callFuncAfter(done, 5) const end = helper.callFuncAfter(done, 5)
const data = Array(10000).join('äüs^öéÉÉ`e') const data = Array(10000).join('äüs^öéÉÉ`e')
sub.set('foo', data).then(() => { sub.set('foo', data).then(() => {
sub.get('foo').then((res) => assert.strictEqual(typeof res, 'string')) sub.get('foo').then(res => assert.strictEqual(typeof res, 'string'))
sub._stream.once('data', () => { sub._stream.once('data', () => {
assert.strictEqual(sub._messageBuffers, false) assert.strictEqual(sub._messageBuffers, false)
assert.strictEqual(sub.shouldBuffer, false) assert.strictEqual(sub.shouldBuffer, false)
@@ -536,7 +537,7 @@ describe('publish/subscribe', () => {
it('should not publish a message multiple times per command', (done) => { it('should not publish a message multiple times per command', (done) => {
const published = {} const published = {}
function subscribe (message) { function subscribe(message) {
sub.removeAllListeners('subscribe') sub.removeAllListeners('subscribe')
sub.removeAllListeners('message') sub.removeAllListeners('message')
sub.removeAllListeners('unsubscribe') sub.removeAllListeners('unsubscribe')
@@ -586,7 +587,8 @@ describe('publish/subscribe', () => {
.psubscribe(['pattern:*']) .psubscribe(['pattern:*'])
.punsubscribe('unknown*') .punsubscribe('unknown*')
.punsubscribe(['pattern:*']) .punsubscribe(['pattern:*'])
.exec().then(() => Promise.all([ .exec()
.then(() => Promise.all([
sub.client('kill', ['type', 'pubsub']), sub.client('kill', ['type', 'pubsub']),
sub.psubscribe('*'), sub.psubscribe('*'),
sub.punsubscribe('pa*'), sub.punsubscribe('pa*'),

View File

@@ -3,9 +3,10 @@
const assert = require('assert') const assert = require('assert')
const config = require('./lib/config') const config = require('./lib/config')
const helper = require('./helper') const helper = require('./helper')
const redis = config.redis
// TODO: Fix redis process spawn on windows const { redis } = config
// TODO: Fix redis process spawn on windows
if (process.platform !== 'win32') { if (process.platform !== 'win32') {
describe('rename commands', () => { describe('rename commands', () => {
before((done) => { before((done) => {

View File

@@ -1,10 +1,11 @@
'use strict' 'use strict'
const Buffer = require('buffer').Buffer const { Buffer } = require('buffer')
const assert = require('assert') const assert = require('assert')
const config = require('./lib/config') const config = require('./lib/config')
const helper = require('./helper') const helper = require('./helper')
const redis = config.redis
const { redis } = config
describe('returnBuffers', () => { describe('returnBuffers', () => {
helper.allTests((ip, basicArgs) => { helper.allTests((ip, basicArgs) => {
@@ -64,7 +65,8 @@ describe('returnBuffers', () => {
.hget(Buffer.from('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', Buffer.from('key 2'))
.hget('hash key 2', 'key 2') .hget('hash key 2', 'key 2')
.exec().then((reply) => { .exec()
.then((reply) => {
assert.strictEqual(4, reply.length) assert.strictEqual(4, reply.length)
assert.strictEqual('<Buffer 76 61 6c 20 31>', reply[0].inspect()) assert.strictEqual('<Buffer 76 61 6c 20 31>', reply[0].inspect())
assert.strictEqual(true, Buffer.isBuffer(reply[1])) assert.strictEqual(true, Buffer.isBuffer(reply[1]))
@@ -84,7 +86,8 @@ describe('returnBuffers', () => {
.hget(Buffer.from('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', Buffer.from('key 2'))
.hget('hash key 2', 'key 2') .hget('hash key 2', 'key 2')
.exec().then((reply) => { .exec()
.then((reply) => {
assert.strictEqual(4, reply.length) assert.strictEqual(4, reply.length)
assert.strictEqual('<Buffer 76 61 6c 20 31>', reply[0].inspect()) assert.strictEqual('<Buffer 76 61 6c 20 31>', reply[0].inspect())
assert.strictEqual(true, Buffer.isBuffer(reply[1])) assert.strictEqual(true, Buffer.isBuffer(reply[1]))

View File

@@ -5,13 +5,14 @@ const config = require('./lib/config')
const fs = require('fs') const fs = require('fs')
const helper = require('./helper') const helper = require('./helper')
const path = require('path') const path = require('path')
const redis = config.redis
const { redis } = config
const utils = require('../lib/utils') const utils = require('../lib/utils')
const tlsOptions = { const tlsOptions = {
servername: 'redis.js.org', servername: 'redis.js.org',
rejectUnauthorized: true, rejectUnauthorized: true,
ca: [ String(fs.readFileSync(path.resolve(__dirname, './conf/redis.js.org.cert'))) ] ca: [String(fs.readFileSync(path.resolve(__dirname, './conf/redis.js.org.cert')))]
} }
const tlsPort = 6380 const tlsPort = 6380
@@ -109,7 +110,7 @@ describe('TLS connection tests', () => {
it('fails to connect because the cert is not correct', function () { it('fails to connect because the cert is not correct', function () {
if (skip) this.skip() if (skip) this.skip()
const faultyCert = utils.clone(tlsOptions) const faultyCert = utils.clone(tlsOptions)
faultyCert.ca = [ String(fs.readFileSync(path.resolve(__dirname, './conf/faulty.cert'))) ] faultyCert.ca = [String(fs.readFileSync(path.resolve(__dirname, './conf/faulty.cert')))]
client = redis.createClient({ client = redis.createClient({
host: 'localhost', host: 'localhost',
connectTimeout: 1000, connectTimeout: 1000,

View File

@@ -9,7 +9,7 @@ describe('createClient options', () => {
it('pass the options in the second parameter after a port', () => { it('pass the options in the second parameter after a port', () => {
const options = unifyOptions(1234, { const options = unifyOptions(1234, {
option1: true, option1: true,
option2 () {} option2() {}
}) })
assert.strictEqual(Object.keys(options).length, 4) assert.strictEqual(Object.keys(options).length, 4)
assert(options.option1) assert(options.option1)
@@ -21,7 +21,7 @@ describe('createClient options', () => {
it('pass the options in the third parameter after a port and host being set to null', () => { it('pass the options in the third parameter after a port and host being set to null', () => {
const options = unifyOptions(1234, null, { const options = unifyOptions(1234, null, {
option1: true, option1: true,
option2 () {} option2() {}
}) })
assert.strictEqual(Object.keys(options).length, 4) assert.strictEqual(Object.keys(options).length, 4)
assert(options.option1) assert(options.option1)
@@ -33,7 +33,7 @@ describe('createClient options', () => {
it('pass the options in the third parameter after a port and host being set to undefined', () => { it('pass the options in the third parameter after a port and host being set to undefined', () => {
const options = unifyOptions(1234, undefined, { const options = unifyOptions(1234, undefined, {
option1: true, option1: true,
option2 () {} option2() {}
}) })
assert.strictEqual(Object.keys(options).length, 4) assert.strictEqual(Object.keys(options).length, 4)
assert(options.option1) assert(options.option1)
@@ -45,7 +45,7 @@ describe('createClient options', () => {
it('pass the options in the third parameter after a port and host', () => { it('pass the options in the third parameter after a port and host', () => {
const options = unifyOptions('1234', 'localhost', { const options = unifyOptions('1234', 'localhost', {
option1: true, option1: true,
option2 () {} option2() {}
}) })
assert.strictEqual(Object.keys(options).length, 4) assert.strictEqual(Object.keys(options).length, 4)
assert(options.option1) assert(options.option1)
@@ -68,7 +68,7 @@ describe('createClient options', () => {
it('pass the options in the second parameter after a port', () => { it('pass the options in the second parameter after a port', () => {
const options = unifyOptions('/tmp/redis.sock', { const options = unifyOptions('/tmp/redis.sock', {
option1: true, option1: true,
option2 () {}, option2() {},
option3: [1, 2, 3] option3: [1, 2, 3]
}) })
assert.strictEqual(Object.keys(options).length, 4) assert.strictEqual(Object.keys(options).length, 4)
@@ -81,7 +81,7 @@ describe('createClient options', () => {
it('pass the options in the third parameter after a port and host being set to null', () => { it('pass the options in the third parameter after a port and host being set to null', () => {
const options = unifyOptions('/tmp/redis.sock', null, { const options = unifyOptions('/tmp/redis.sock', null, {
option1: true, option1: true,
option2 () {} option2() {}
}) })
assert.strictEqual(Object.keys(options).length, 3) assert.strictEqual(Object.keys(options).length, 3)
assert(options.option1) assert(options.option1)
@@ -120,7 +120,8 @@ describe('createClient options', () => {
option: [1, 2, 3] option: [1, 2, 3]
}) })
unhookIntercept() unhookIntercept()
assert.strictEqual(text, assert.strictEqual(
text,
'nodeRedis: WARNING: You passed the db option twice!\n' + 'nodeRedis: WARNING: You passed the db option twice!\n' +
'nodeRedis: WARNING: You passed the port option twice!\n' + 'nodeRedis: WARNING: You passed the port option twice!\n' +
'nodeRedis: WARNING: You passed the password option twice!\n' 'nodeRedis: WARNING: You passed the password option twice!\n'

View File

@@ -6,10 +6,10 @@ const intercept = require('intercept-stdout')
const utils = require('../lib/utils') const utils = require('../lib/utils')
describe('utils.js', () => { describe('utils.js', () => {
describe('print helper', function () { describe('print helper', () => {
it('callback with reply', function () { it('callback with reply', () => {
var text = '' let text = ''
const unhookIntercept = intercept(function (data) { const unhookIntercept = intercept((data) => {
text += data text += data
return '' return ''
}) })
@@ -18,9 +18,9 @@ describe('utils.js', () => {
assert.strictEqual(text, 'Reply: abc\n') assert.strictEqual(text, 'Reply: abc\n')
}) })
it('callback with error', function () { it('callback with error', () => {
var text = '' let text = ''
const unhookIntercept = intercept(function (data) { const unhookIntercept = intercept((data) => {
text += data text += data
return '' return ''
}) })
@@ -37,7 +37,7 @@ describe('utils.js', () => {
'i\'m special': true 'i\'m special': true
}], }],
number: 5, number: 5,
fn: function noop () {} fn: function noop() {}
} }
const clone = utils.clone(obj) const clone = utils.clone(obj)
assert.deepStrictEqual(clone, obj) assert.deepStrictEqual(clone, obj)
@@ -73,7 +73,7 @@ describe('utils.js', () => {
} }
const createCommandObj = function () { const createCommandObj = function () {
return { return {
callback (err, res) { callback(err, res) {
if (err) errCount++ if (err) errCount++
else resCount++ else resCount++
} }