From f1a7bcd7353f58e2f9094184864de13de768206b Mon Sep 17 00:00:00 2001 From: Ruben Bridgewater Date: Sat, 6 May 2017 07:06:52 +0200 Subject: [PATCH] chore: use standard --- benchmarks/diff_multi_bench_output.js | 138 +- benchmarks/multi_bench.js | 553 +++---- examples/auth.js | 10 +- examples/eval.js | 18 +- examples/extend.js | 37 +- examples/file.js | 46 +- examples/mget.js | 9 +- examples/monitor.js | 15 +- examples/multi.js | 52 +- examples/multi2.js | 31 +- examples/psubscribe.js | 50 +- examples/pub_sub.js | 56 +- examples/scan.js | 80 +- examples/simple.js | 37 +- examples/sort.js | 26 +- examples/subqueries.js | 24 +- examples/subquery.js | 26 +- examples/unix_socket.js | 39 +- examples/web_server.js | 49 +- index.js | 1501 +++++++++---------- lib/command.js | 22 +- lib/commands.js | 181 ++- lib/createClient.js | 141 +- lib/customErrors.js | 88 +- lib/debug.js | 12 +- lib/extendedApi.js | 186 +-- lib/individualCommands.js | 1036 +++++++------- lib/multi.js | 318 ++--- lib/utils.js | 153 +- package.json | 10 +- test/auth.spec.js | 626 ++++---- test/batch.spec.js | 617 ++++---- test/commands/blpop.spec.js | 136 +- test/commands/client.spec.js | 263 ++-- test/commands/dbsize.spec.js | 158 +- test/commands/del.spec.js | 94 +- test/commands/eval.spec.js | 354 +++-- test/commands/exists.spec.js | 64 +- test/commands/expire.spec.js | 70 +- test/commands/flushdb.spec.js | 173 ++- test/commands/geoadd.spec.js | 58 +- test/commands/get.spec.js | 160 +-- test/commands/getset.spec.js | 180 ++- test/commands/hgetall.spec.js | 146 +- test/commands/hincrby.spec.js | 62 +- test/commands/hlen.spec.js | 63 +- test/commands/hmget.spec.js | 122 +- test/commands/hmset.spec.js | 204 ++- test/commands/hset.spec.js | 154 +- test/commands/incr.spec.js | 116 +- test/commands/info.spec.js | 141 +- test/commands/keys.spec.js | 116 +- test/commands/mget.spec.js | 122 +- test/commands/monitor.spec.js | 402 +++--- test/commands/mset.spec.js | 189 ++- test/commands/msetnx.spec.js | 62 +- test/commands/randomkey.test.js | 58 +- test/commands/rename.spec.js | 62 +- test/commands/renamenx.spec.js | 68 +- test/commands/rpush.spec.js | 60 +- test/commands/sadd.spec.js | 106 +- test/commands/scard.spec.js | 50 +- test/commands/script.spec.js | 81 +- test/commands/sdiff.spec.js | 76 +- test/commands/sdiffstore.spec.js | 74 +- test/commands/select.spec.js | 220 ++- test/commands/set.spec.js | 292 ++-- test/commands/setex.spec.js | 63 +- test/commands/setnx.spec.js | 60 +- test/commands/sinter.spec.js | 102 +- test/commands/sinterstore.spec.js | 76 +- test/commands/sismember.spec.js | 56 +- test/commands/slowlog.spec.js | 70 +- test/commands/smembers.spec.js | 64 +- test/commands/smove.spec.js | 66 +- test/commands/sort.spec.js | 215 ++- test/commands/spop.spec.js | 62 +- test/commands/srem.spec.js | 118 +- test/commands/sunion.spec.js | 74 +- test/commands/sunionstore.spec.js | 78 +- test/commands/ttl.spec.js | 62 +- test/commands/type.spec.js | 89 +- test/commands/watch.spec.js | 84 +- test/commands/zadd.spec.js | 82 +- test/commands/zscan.spec.js | 87 +- test/commands/zscore.spec.js | 54 +- test/conect.slave.spec.js | 164 ++- test/connection.spec.js | 1074 +++++++------- test/custom_errors.spec.js | 161 ++- test/detect_buffers.spec.js | 488 +++---- test/good_traces.spec.js | 99 +- test/helper.js | 448 +++--- test/lib/config.js | 56 +- test/lib/good-traces.js | 30 +- test/lib/redis-process.js | 152 +- test/lib/stunnel-process.js | 126 +- test/lib/unref.js | 22 +- test/multi.spec.js | 1402 +++++++++--------- test/node_redis.spec.js | 1904 ++++++++++++------------- test/prefix.spec.js | 196 ++- test/pubsub.spec.js | 1191 ++++++++-------- test/rename.spec.js | 232 ++- test/return_buffers.spec.js | 533 ++++--- test/tls.spec.js | 227 ++- test/unify_options.spec.js | 431 +++--- test/utils.spec.js | 243 ++-- 106 files changed, 10706 insertions(+), 10978 deletions(-) diff --git a/benchmarks/diff_multi_bench_output.js b/benchmarks/diff_multi_bench_output.js index f8dca3ee77..7432d14407 100755 --- a/benchmarks/diff_multi_bench_output.js +++ b/benchmarks/diff_multi_bench_output.js @@ -1,95 +1,91 @@ -'use strict'; +'use strict' -var fs = require('fs'); -var metrics = require('metrics'); - // `node diffMultiBenchOutput.js beforeBench.txt afterBench.txt` -var file1 = process.argv[2]; -var file2 = process.argv[3]; +// `node diffMultiBenchOutput.js beforeBench.txt afterBench.txt` + +var fs = require('fs') +var file1 = process.argv[2] +var file2 = process.argv[3] if (!file1 || !file2) { - console.log('Please supply two file arguments:'); - var n = __filename; - n = n.substring(n.lastIndexOf('/', n.length)); - console.log(' node .' + n + ' benchBefore.txt benchAfter.txt\n'); - console.log('To generate the benchmark files, run'); - console.log(' npm run benchmark > benchBefore.txt\n'); - console.log('Thank you for benchmarking responsibly.'); - return; + console.log('Please supply two file arguments:') + var n = __filename + n = n.substring(n.lastIndexOf('/', n.length)) + console.log(' node .' + n + ' benchBefore.txt benchAfter.txt\n') + console.log('To generate the benchmark files, run') + console.log(' npm run benchmark > benchBefore.txt\n') + console.log('Thank you for benchmarking responsibly.') + process.exit(1) } -var beforeLines = fs.readFileSync(file1, 'utf8').split('\n'); -var afterLines = fs.readFileSync(file2, 'utf8').split('\n'); -var totalOps = new metrics.Histogram.createUniformHistogram(); +var beforeLines = fs.readFileSync(file1, 'utf8').split('\n') +var 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) { - return !!s.trim(); + return !!s.trim() } function pad (input, len, chr, right) { - var str = input.toString(); - chr = chr || ' '; + var str = input.toString() + chr = chr || ' ' - if (right) { - while (str.length < len) { - str += chr; - } - } else { - while (str.length < len) { - str = chr + str; - } + if (right) { + while (str.length < len) { + str += chr } - return str; + } else { + while (str.length < len) { + str = chr + str + } + } + return str } // green if greater than 0, red otherwise function humanizeDiff (num, unit, toFixed) { - unit = unit || ''; - if (num > 0) { - return ' +' + pad(num.toFixed(toFixed || 0) + unit, 7); - } - return ' -' + pad(Math.abs(num).toFixed(toFixed || 0) + unit, 7); + unit = unit || '' + if (num > 0) { + return ' +' + pad(num.toFixed(toFixed || 0) + unit, 7) + } + return ' -' + pad(Math.abs(num).toFixed(toFixed || 0) + unit, 7) } function commandName (words) { - var line = words.join(' '); - return line.substr(0, line.indexOf(',')); + var line = words.join(' ') + return line.substr(0, line.indexOf(',')) } beforeLines.forEach(function (b, i) { - var a = afterLines[i]; - if (!a || !b || !b.trim() || !a.trim()) { - // console.log('#ignored#', '>'+a+'<', '>'+b+'<'); - return; - } - var bWords = b.split(' ').filter(isWhitespace); - var aWords = a.split(' ').filter(isWhitespace); + var a = afterLines[i] + if (!a || !b || !b.trim() || !a.trim()) { + // console.log('#ignored#', '>'+a+'<', '>'+b+'<'); + return + } + var bWords = b.split(' ').filter(isWhitespace) + var aWords = a.split(' ').filter(isWhitespace) - var ops = [bWords, aWords].map(function (words) { - // console.log(words); - return words.slice(-2, -1) | 0; - }).filter(function (num) { - var isNaN = !num && num !== 0; - return !isNaN; - }); - if (ops.length !== 2) { - return; - } - var delta = ops[1] - ops[0]; - var pct = +((delta / ops[0]) * 100); - ops[0] = pad(ops[0], 6); - ops[1] = pad(ops[1], 6); - totalOps.update(delta); - delta = humanizeDiff(delta); - var smallDelta = pct < 3 && pct > -3; - // Let's mark differences above 20% bold - var bigDelta = pct > 20 || pct < -20 ? ';1' : ''; - pct = humanizeDiff(pct, '', 2) + '%'; - var str = pad((commandName(aWords) === commandName(bWords) ? commandName(aWords) + ':' : '404:'), 14, false, true) + - (pad(ops.join(' -> '), 15) + ' ops/sec (∆' + delta + pct + ')'); - str = (smallDelta ? '' : (/-[^>]/.test(str) ? '\x1b[31' : '\x1b[32') + bigDelta + 'm') + str + '\x1b[0m'; - console.log(str); -}); - -console.log('Mean difference in ops/sec:', humanizeDiff(totalOps.mean(), '', 1)); + var ops = [bWords, aWords].map(function (words) { + // console.log(words); + return words.slice(-2, -1) | 0 + }).filter(function (num) { + var isNaN = !num && num !== 0 + return !isNaN + }) + if (ops.length !== 2) { + return + } + var delta = ops[1] - ops[0] + var pct = +((delta / ops[0]) * 100) + ops[0] = pad(ops[0], 6) + ops[1] = pad(ops[1], 6) + delta = humanizeDiff(delta) + var smallDelta = pct < 3 && pct > -3 + // Let's mark differences above 20% bold + var bigDelta = pct > 20 || pct < -20 ? ';1' : '' + pct = humanizeDiff(pct, '', 2) + '%' + var str = pad((commandName(aWords) === commandName(bWords) ? commandName(aWords) + ':' : '404:'), 14, false, true) + + (pad(ops.join(' -> '), 15) + ' ops/sec (∆' + delta + pct + ')') + str = (smallDelta ? '' : (/-[^>]/.test(str) ? '\x1b[31' : '\x1b[32') + bigDelta + 'm') + str + '\x1b[0m' + console.log(str) +}) diff --git a/benchmarks/multi_bench.js b/benchmarks/multi_bench.js index 029adb170a..5a872b34e6 100644 --- a/benchmarks/multi_bench.js +++ b/benchmarks/multi_bench.js @@ -1,295 +1,298 @@ -'use strict'; +'use strict' -var path = require('path'); -var RedisProcess = require('../test/lib/redis-process'); -var rp; -var clientNr = 0; -var redis = require('../index'); -var totalTime = 0; -var metrics = require('metrics'); -var tests = []; +var Buffer = require('safe-buffer').Buffer +var path = require('path') +var RedisProcess = require('../test/lib/redis-process') +var rp +var clientNr = 0 +var redis = require('../index') +var totalTime = 0 +var metrics = require('metrics') +var tests = [] // var bluebird = require('bluebird'); // bluebird.promisifyAll(redis.RedisClient.prototype); // bluebird.promisifyAll(redis.Multi.prototype); function returnArg (name, def) { - var matches = process.argv.filter(function (entry) { - return entry.indexOf(name + '=') === 0; - }); - if (matches.length) { - return matches[0].substr(name.length + 1); - } - return def; + var matches = process.argv.filter(function (entry) { + return entry.indexOf(name + '=') === 0 + }) + if (matches.length) { + return matches[0].substr(name.length + 1) + } + return def } -var numClients = returnArg('clients', 1); -var runTime = returnArg('time', 2500); // ms -var pipeline = returnArg('pipeline', 1); // number of concurrent commands -var versionsLogged = false; +var numClients = returnArg('clients', 1) +var runTime = returnArg('time', 2500) // ms +var pipeline = returnArg('pipeline', 1) // number of concurrent commands +var versionsLogged = false var clientOptions = { - path: returnArg('socket') // '/tmp/redis.sock' -}; -var smallStr, largeStr, smallBuf, largeBuf, veryLargeStr, veryLargeBuf, mgetArray; + path: returnArg('socket') // '/tmp/redis.sock' +} +var smallStr, largeStr, smallBuf, largeBuf, veryLargeStr, veryLargeBuf, mgetArray function lpad (input, len, chr) { - var str = input.toString(); - chr = chr || ' '; - while (str.length < len) { - str = chr + str; - } - return str; + var str = input.toString() + chr = chr || ' ' + while (str.length < len) { + str = chr + str + } + return str } metrics.Histogram.prototype.printLine = function () { - var obj = this.printObj(); - return lpad((obj.mean / 1e6).toFixed(2), 6) + '/' + lpad((obj.max / 1e6).toFixed(2), 6); -}; + var obj = this.printObj() + return lpad((obj.mean / 1e6).toFixed(2), 6) + '/' + lpad((obj.max / 1e6).toFixed(2), 6) +} function Test (args) { - this.args = args; - this.args.pipeline = +pipeline; - this.callback = null; - this.clients = []; - this.clientsReady = 0; - this.commandsSent = 0; - this.commandsCompleted = 0; - this.maxPipeline = +pipeline; - this.batchPipeline = this.args.batch || 0; - this.clientOptions = args.clientOptions || {}; - this.clientOptions.connectTimeout = 1000; - if (clientOptions.path) { - this.clientOptions.path = clientOptions.path; - } - this.connectLatency = new metrics.Histogram(); - this.readyLatency = new metrics.Histogram(); - this.commandLatency = new metrics.Histogram(); + this.args = args + this.args.pipeline = +pipeline + this.callback = null + this.clients = [] + this.clientsReady = 0 + this.commandsSent = 0 + this.commandsCompleted = 0 + this.maxPipeline = +pipeline + this.batchPipeline = this.args.batch || 0 + this.clientOptions = args.clientOptions || {} + this.clientOptions.connectTimeout = 1000 + if (clientOptions.path) { + this.clientOptions.path = clientOptions.path + } + this.connectLatency = new metrics.Histogram() + this.readyLatency = new metrics.Histogram() + this.commandLatency = new metrics.Histogram() } Test.prototype.run = function (callback) { - var i; - this.callback = callback; - for (i = 0; i < numClients ; i++) { - this.newClient(i); - } -}; - -Test.prototype.newClient = function (id) { - var self = this, newClient; - - newClient = redis.createClient(this.clientOptions); - newClient.createTime = Date.now(); - - newClient.on('connect', function () { - self.connectLatency.update(Date.now() - newClient.createTime); - }); - - newClient.on('ready', function () { - if (!versionsLogged) { - console.log( - 'clients: ' + numClients + - ', NodeJS: ' + process.versions.node + - ', Redis: ' + newClient.serverInfo.redis_version + - ', connected by: ' + (clientOptions.path ? 'socket' : 'tcp') - ); - versionsLogged = true; - } - self.readyLatency.update(Date.now() - newClient.createTime); - self.clientsReady++; - if (self.clientsReady === self.clients.length) { - self.onClientsReady(); - } - }); - - // If no redis server is running, start one - newClient.on('error', function (err) { - if (err.code === 'CONNECTION_BROKEN') { - throw err; - } - if (rp) { - return; - } - rp = true; - var conf = '../test/conf/redis.conf'; - RedisProcess.start(function (err, Rp) { - if (err) { - throw err; - } - rp = Rp; - }, path.resolve(__dirname, conf)); - }); - - self.clients[id] = newClient; -}; - -Test.prototype.onClientsReady = function () { - process.stdout.write(lpad(this.args.descr, 13) + ', ' + (this.args.batch ? lpad('batch ' + this.args.batch, 9) : lpad(this.args.pipeline, 9)) + '/' + this.clientsReady + ' '); - this.testStart = Date.now(); - this.fillPipeline(); -}; - -Test.prototype.fillPipeline = function () { - var pipeline = this.commandsSent - this.commandsCompleted; - - if (this.testStart < Date.now() - runTime) { - if (this.ended) { - return; - } - this.ended = true; - this.printStats(); - this.stopClients(); - return; - } - - if (this.batchPipeline) { - this.batch(); - } else { - while (pipeline < this.maxPipeline) { - this.commandsSent++; - pipeline++; - this.sendNext(); - } - } -}; - -Test.prototype.batch = function () { - var self = this, - curClient = clientNr++ % this.clients.length, - start = process.hrtime(), - i = 0, - batch = this.clients[curClient].batch(); - - while (i++ < this.batchPipeline) { - this.commandsSent++; - batch[this.args.command](this.args.args); - } - - batch.exec(function (err, res) { - if (err) { - throw err; - } - self.commandsCompleted += res.length; - self.commandLatency.update(process.hrtime(start)[1]); - self.fillPipeline(); - }); -}; - -Test.prototype.stopClients = function () { - var self = this; - - this.clients.forEach(function (client, pos) { - if (pos === self.clients.length - 1) { - client.quit(function (err, res) { - self.callback(); - }); - } else { - client.quit(); - } - }); -}; - -Test.prototype.sendNext = function () { - var self = this, - curClient = this.commandsSent % this.clients.length, - start = process.hrtime(); - - this.clients[curClient][this.args.command](this.args.args, function (err, res) { - if (err) { - throw err; - } - self.commandsCompleted++; - self.commandLatency.update(process.hrtime(start)[1]); - self.fillPipeline(); - }); -}; - -Test.prototype.printStats = function () { - var duration = Date.now() - this.testStart; - totalTime += duration; - - console.log('avg/max: ' + this.commandLatency.printLine() + lpad(duration, 5) + 'ms total, ' + - lpad(Math.round(this.commandsCompleted / (duration / 1000)), 7) + ' ops/sec'); -}; - -smallStr = '1234'; -smallBuf = new Buffer(smallStr); -largeStr = (new Array(4096 + 1).join('-')); -largeBuf = new Buffer(largeStr); -veryLargeStr = (new Array((4 * 1024 * 1024) + 1).join('-')); -veryLargeBuf = new Buffer(veryLargeStr); -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: [], 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], 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], 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'], batch: 50})); - -tests.push(new Test({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], 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], 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'], batch: 50})); - -tests.push(new Test({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'], batch: 50})); - -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: '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 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: '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 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: '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 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, batch: 20})); - -tests.push(new Test({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 () { - var test = tests.shift(); - if (test) { - test.run(function () { - next(); - }); - } else if (rp) { - // Stop the redis process if started by the benchmark - rp.stop(function () { - rp = undefined; - next(); - }); - } else { - console.log('End of tests. Total time elapsed:', totalTime, 'ms'); - process.exit(0); - } + var i + this.callback = callback + for (i = 0; i < numClients; i++) { + this.newClient(i) + } } -next(); +Test.prototype.newClient = function (id) { + var self = this + var newClient + + newClient = redis.createClient(this.clientOptions) + newClient.createTime = Date.now() + + newClient.on('connect', function () { + self.connectLatency.update(Date.now() - newClient.createTime) + }) + + newClient.on('ready', function () { + if (!versionsLogged) { + console.log( + 'clients: ' + numClients + + ', NodeJS: ' + process.versions.node + + ', Redis: ' + newClient.serverInfo.redis_version + + ', connected by: ' + (clientOptions.path ? 'socket' : 'tcp') + ) + versionsLogged = true + } + self.readyLatency.update(Date.now() - newClient.createTime) + self.clientsReady++ + if (self.clientsReady === self.clients.length) { + self.onClientsReady() + } + }) + + // If no redis server is running, start one + newClient.on('error', function (err) { + if (err.code === 'CONNECTION_BROKEN') { + throw err + } + if (rp) { + return + } + rp = true + var conf = '../test/conf/redis.conf' + RedisProcess.start(function (err, Rp) { + if (err) { + throw err + } + rp = Rp + }, path.resolve(__dirname, conf)) + }) + + self.clients[id] = newClient +} + +Test.prototype.onClientsReady = function () { + process.stdout.write(lpad(this.args.descr, 13) + ', ' + (this.args.batch ? lpad('batch ' + this.args.batch, 9) : lpad(this.args.pipeline, 9)) + '/' + this.clientsReady + ' ') + this.testStart = Date.now() + this.fillPipeline() +} + +Test.prototype.fillPipeline = function () { + var pipeline = this.commandsSent - this.commandsCompleted + + if (this.testStart < Date.now() - runTime) { + if (this.ended) { + return + } + this.ended = true + this.printStats() + this.stopClients() + return + } + + if (this.batchPipeline) { + this.batch() + } else { + while (pipeline < this.maxPipeline) { + this.commandsSent++ + pipeline++ + this.sendNext() + } + } +} + +Test.prototype.batch = function () { + var self = this + var curClient = clientNr++ % this.clients.length + var start = process.hrtime() + var i = 0 + var batch = this.clients[curClient].batch() + + while (i++ < this.batchPipeline) { + this.commandsSent++ + batch[this.args.command](this.args.args) + } + + batch.exec(function (err, res) { + if (err) { + throw err + } + self.commandsCompleted += res.length + self.commandLatency.update(process.hrtime(start)[1]) + self.fillPipeline() + }) +} + +Test.prototype.stopClients = function () { + var self = this + + this.clients.forEach(function (client, pos) { + if (pos === self.clients.length - 1) { + client.quit(function (err, res) { + if (err) throw err + self.callback() + }) + } else { + client.quit() + } + }) +} + +Test.prototype.sendNext = function () { + var self = this + var curClient = this.commandsSent % this.clients.length + var start = process.hrtime() + + this.clients[curClient][this.args.command](this.args.args, function (err, res) { + if (err) { + throw err + } + self.commandsCompleted++ + self.commandLatency.update(process.hrtime(start)[1]) + self.fillPipeline() + }) +} + +Test.prototype.printStats = function () { + var duration = Date.now() - this.testStart + totalTime += duration + + console.log('avg/max: ' + this.commandLatency.printLine() + lpad(duration, 5) + 'ms total, ' + + lpad(Math.round(this.commandsCompleted / (duration / 1000)), 7) + ' ops/sec') +} + +smallStr = '1234' +smallBuf = Buffer.from(smallStr) +largeStr = (new Array(4096 + 1).join('-')) +largeBuf = Buffer.from(largeStr) +veryLargeStr = (new Array((4 * 1024 * 1024) + 1).join('-')) +veryLargeBuf = Buffer.from(veryLargeStr) +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: [], 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], 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], 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'], batch: 50})) + +tests.push(new Test({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], 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], 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'], batch: 50})) + +tests.push(new Test({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'], batch: 50})) + +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: '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 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: '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 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: '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 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, batch: 20})) + +tests.push(new Test({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 () { + var test = tests.shift() + if (test) { + test.run(function () { + next() + }) + } else if (rp) { + // Stop the redis process if started by the benchmark + rp.stop(function () { + rp = undefined + next() + }) + } else { + console.log('End of tests. Total time elapsed:', totalTime, 'ms') + process.exit(0) + } +} + +next() diff --git a/examples/auth.js b/examples/auth.js index e36b700656..9141993294 100644 --- a/examples/auth.js +++ b/examples/auth.js @@ -1,7 +1,7 @@ -'use strict'; +'use strict' -var redis = require('redis'); -// The client stashes the password and will reauthenticate on every connect. +var redis = require('redis') +// The client stashes the password and will re-authenticate on every connect. redis.createClient({ - password: 'somepass' -}); + password: 'some pass' +}) diff --git a/examples/eval.js b/examples/eval.js index cffdb548a1..f1af334d2b 100644 --- a/examples/eval.js +++ b/examples/eval.js @@ -1,14 +1,14 @@ -'use strict'; +'use strict' -var redis = require('../index'); -var client = redis.createClient(); +var redis = require('../index') +var client = redis.createClient() client.eval('return 100.5', 0, function (err, res) { - console.dir(err); - console.dir(res); -}); + console.dir(err) + console.dir(res) +}) client.eval([ 'return 100.5', 0 ], function (err, res) { - console.dir(err); - console.dir(res); -}); + console.dir(err) + console.dir(res) +}) diff --git a/examples/extend.js b/examples/extend.js index 1d51c50abf..2d0a4c764d 100644 --- a/examples/extend.js +++ b/examples/extend.js @@ -1,26 +1,27 @@ -'use strict'; +'use strict' -var redis = require('redis'); -var client = redis.createClient(); +var redis = require('redis') +var client = redis.createClient() // Extend the RedisClient prototype to add a custom method // This one converts the results from 'INFO' into a JavaScript Object redis.RedisClient.prototype.parseInfo = function (callback) { - this.info(function (err, res) { - var lines = res.toString().split('\r\n').sort(); - var obj = {}; - lines.forEach(function (line) { - var parts = line.split(':'); - if (parts[1]) { - obj[parts[0]] = parts[1]; - } - }); - callback(obj); - }); -}; + this.info(function (err, res) { + if (err) throw err + var lines = res.toString().split('\r\n').sort() + var obj = {} + lines.forEach(function (line) { + var parts = line.split(':') + if (parts[1]) { + obj[parts[0]] = parts[1] + } + }) + callback(obj) + }) +} client.parseInfo(function (info) { - console.dir(info); - client.quit(); -}); + console.dir(info) + client.quit() +}) diff --git a/examples/file.js b/examples/file.js index 9aaaac4e17..15c50eb92d 100644 --- a/examples/file.js +++ b/examples/file.js @@ -1,14 +1,14 @@ -'use strict'; +'use strict' // Read a file from disk, store it in Redis, then read it back from Redis. -var redis = require('redis'); +var redis = require('redis') var client = redis.createClient({ - returnBuffers: true -}); -var fs = require('fs'); -var assert = require('assert'); -var filename = 'grumpyCat.jpg'; + returnBuffers: true +}) +var fs = require('fs') +var assert = require('assert') +var filename = 'grumpyCat.jpg' // Get the file I use for testing like this: // curl http://media4.popsugar-assets.com/files/2014/08/08/878/n/1922507/caef16ec354ca23b_thumb_temp_cover_file32304521407524949.xxxlarge/i/Funny-Cat-GIFs.jpg -o grumpyCat.jpg @@ -16,23 +16,23 @@ var filename = 'grumpyCat.jpg'; // Read a file from fs, store it in Redis, get it back from Redis, write it back to fs. fs.readFile(filename, function (err, data) { - if (err) throw err; - console.log('Read ' + data.length + ' bytes from filesystem.'); + if (err) throw err + console.log('Read ' + data.length + ' bytes from filesystem.') - client.set(filename, data, console.log); // set entire file - client.get(filename, function (err, reply) { // get entire file + client.set(filename, data, console.log) // set entire file + client.get(filename, function (err, reply) { // get entire file + if (err) { + console.log('Get error: ' + err) + } else { + assert.strictEqual(data.inspect(), reply.inspect()) + fs.writeFile('duplicate_' + filename, reply, function (err) { if (err) { - console.log('Get error: ' + err); + console.log('Error on write: ' + err) } else { - assert.strictEqual(data.inspect(), reply.inspect()); - fs.writeFile('duplicate_' + filename, reply, function (err) { - if (err) { - console.log('Error on write: ' + err); - } else { - console.log('File written.'); - } - client.end(); - }); + console.log('File written.') } - }); -}); + client.end() + }) + } + }) +}) diff --git a/examples/mget.js b/examples/mget.js index e774ac44e0..d1d8575222 100644 --- a/examples/mget.js +++ b/examples/mget.js @@ -1,7 +1,8 @@ -'use strict'; +'use strict' -var client = require('redis').createClient(); +var client = require('redis').createClient() client.mget(['sessions started', 'sessions started', 'foo'], function (err, res) { - console.dir(res); -}); + if (err) throw err + console.dir(res) +}) diff --git a/examples/monitor.js b/examples/monitor.js index 93d5d100f5..d42de32872 100644 --- a/examples/monitor.js +++ b/examples/monitor.js @@ -1,12 +1,13 @@ -'use strict'; +'use strict' -var client = require('../index').createClient(); -var util = require('util'); +var client = require('../index').createClient() +var util = require('util') client.monitor(function (err, res) { - console.log('Entering monitoring mode.'); -}); + if (err) throw err + console.log('Entering monitoring mode.') +}) client.on('monitor', function (time, args) { - console.log(time + ': ' + util.inspect(args)); -}); + console.log(time + ': ' + util.inspect(args)) +}) diff --git a/examples/multi.js b/examples/multi.js index 36410a15ae..4e7626210b 100644 --- a/examples/multi.js +++ b/examples/multi.js @@ -1,15 +1,15 @@ -'use strict'; +'use strict' -var redis = require('redis'); -var client = redis.createClient(); -var setSize = 20; +var redis = require('redis') +var client = redis.createClient() +var setSize = 20 -client.sadd('bigset', 'a member'); -client.sadd('bigset', 'another member'); +client.sadd('bigset', 'a member') +client.sadd('bigset', 'another member') while (setSize > 0) { - client.sadd('bigset', 'member ' + setSize); - setSize -= 1; + client.sadd('bigset', 'member ' + setSize) + setSize -= 1 } // multi chain with an individual callback @@ -17,33 +17,37 @@ client.multi() .scard('bigset') .smembers('bigset') .keys('*', function (err, replies) { - client.mget(replies, console.log); + if (err) throw err + client.mget(replies, console.log) }) .dbsize() .exec(function (err, replies) { - console.log('MULTI got ' + replies.length + ' replies'); - replies.forEach(function (reply, index) { - console.log('Reply ' + index + ': ' + reply.toString()); - }); - }); + if (err) throw err + console.log('MULTI got ' + replies.length + ' replies') + replies.forEach(function (reply, index) { + console.log('Reply ' + index + ': ' + reply.toString()) + }) + }) -client.mset('incr thing', 100, 'incr other thing', 1, console.log); +client.mset('incr thing', 100, 'incr other thing', 1, console.log) // start a separate multi command queue -var multi = client.multi(); -multi.incr('incr thing', console.log); -multi.incr('incr other thing', console.log); +var multi = client.multi() +multi.incr('incr thing', console.log) +multi.incr('incr other thing', console.log) // runs immediately -client.get('incr thing', console.log); // 100 +client.get('incr thing', console.log) // 100 // drains multi queue and runs atomically multi.exec(function (err, replies) { - console.log(replies); // 101, 2 -}); + if (err) throw err + console.log(replies) // 101, 2 +}) // you can re-run the same transaction if you like multi.exec(function (err, replies) { - console.log(replies); // 102, 3 - client.quit(); -}); + if (err) throw err + console.log(replies) // 102, 3 + client.quit() +}) diff --git a/examples/multi2.js b/examples/multi2.js index 71bde7b9ad..040c3b5859 100644 --- a/examples/multi2.js +++ b/examples/multi2.js @@ -1,31 +1,34 @@ -'use strict'; +'use strict' -var redis = require('redis'); -var client = redis.createClient(); +var redis = require('redis') +var client = redis.createClient() // start a separate command queue for multi -var multi = client.multi(); -multi.incr('incr thing', console.log); -multi.incr('incr other thing', console.log); +var multi = client.multi() +multi.incr('incr thing', console.log) +multi.incr('incr other thing', console.log) // runs immediately -client.mset('incr thing', 100, 'incr other thing', 1, console.log); +client.mset('incr thing', 100, 'incr other thing', 1, console.log) // drains multi queue and runs atomically multi.exec(function (err, replies) { - console.log(replies); // 101, 2 -}); + if (err) throw err + console.log(replies) // 101, 2 +}) // you can re-run the same transaction if you like multi.exec(function (err, replies) { - console.log(replies); // 102, 3 - client.quit(); -}); + if (err) throw err + console.log(replies) // 102, 3 + client.quit() +}) client.multi([ ['mget', 'multifoo', 'multibar', console.log], ['incr', 'multifoo'], ['incr', 'multibar'] ]).exec(function (err, replies) { - console.log(replies.toString()); -}); + if (err) throw err + console.log(replies.toString()) +}) diff --git a/examples/psubscribe.js b/examples/psubscribe.js index 39bf3e9e71..1e89582f58 100644 --- a/examples/psubscribe.js +++ b/examples/psubscribe.js @@ -1,33 +1,33 @@ -'use strict'; +'use strict' -var redis = require('redis'); -var client1 = redis.createClient(); -var client2 = redis.createClient(); -var client3 = redis.createClient(); -var client4 = redis.createClient(); -var msgCount = 0; +var redis = require('redis') +var client1 = redis.createClient() +var client2 = redis.createClient() +var client3 = redis.createClient() +var client4 = redis.createClient() +var msgCount = 0 client1.on('psubscribe', function (pattern, count) { - console.log('client1 psubscribed to ' + pattern + ', ' + count + ' total subscriptions'); - client2.publish('channeltwo', 'Me!'); - client3.publish('channelthree', 'Me too!'); - client4.publish('channelfour', 'And me too!'); -}); + console.log('client1 psubscribed to ' + pattern + ', ' + count + ' total subscriptions') + client2.publish('channeltwo', 'Me!') + client3.publish('channelthree', 'Me too!') + client4.publish('channelfour', 'And me too!') +}) client1.on('punsubscribe', function (pattern, count) { - console.log('client1 punsubscribed from ' + pattern + ', ' + count + ' total subscriptions'); - client4.end(); - client3.end(); - client2.end(); - client1.end(); -}); + console.log('client1 punsubscribed from ' + pattern + ', ' + count + ' total subscriptions') + client4.end() + client3.end() + client2.end() + client1.end() +}) client1.on('pmessage', function (pattern, channel, message) { - console.log('(' + pattern + ') client1 received message on ' + channel + ': ' + message); - msgCount += 1; - if (msgCount === 3) { - client1.punsubscribe(); - } -}); + console.log('(' + pattern + ') client1 received message on ' + channel + ': ' + message) + msgCount += 1 + if (msgCount === 3) { + client1.punsubscribe() + } +}) -client1.psubscribe('channel*'); +client1.psubscribe('channel*') diff --git a/examples/pub_sub.js b/examples/pub_sub.js index daa653f71e..9376f9a945 100644 --- a/examples/pub_sub.js +++ b/examples/pub_sub.js @@ -1,42 +1,42 @@ -'use strict'; +'use strict' -var redis = require('redis'); -var client1 = redis.createClient(); -var msgCount = 0; -var client2 = redis.createClient(); +var redis = require('redis') +var client1 = redis.createClient() +var msgCount = 0 +var client2 = redis.createClient() // Most clients probably don't do much on 'subscribe'. This example uses it to coordinate things within one program. client1.on('subscribe', function (channel, count) { - console.log('client1 subscribed to ' + channel + ', ' + count + ' total subscriptions'); - if (count === 2) { - client2.publish('a nice channel', 'I am sending a message.'); - client2.publish('another one', 'I am sending a second message.'); - client2.publish('a nice channel', 'I am sending my last message.'); - } -}); + console.log('client1 subscribed to ' + channel + ', ' + count + ' total subscriptions') + if (count === 2) { + client2.publish('a nice channel', 'I am sending a message.') + client2.publish('another one', 'I am sending a second message.') + client2.publish('a nice channel', 'I am sending my last message.') + } +}) client1.on('unsubscribe', function (channel, count) { - console.log('client1 unsubscribed from ' + channel + ', ' + count + ' total subscriptions'); - if (count === 0) { - client2.end(); - client1.end(); - } -}); + console.log('client1 unsubscribed from ' + channel + ', ' + count + ' total subscriptions') + if (count === 0) { + client2.end() + client1.end() + } +}) client1.on('message', function (channel, message) { - console.log('client1 channel ' + channel + ': ' + message); - msgCount += 1; - if (msgCount === 3) { - client1.unsubscribe(); - } -}); + console.log('client1 channel ' + channel + ': ' + message) + msgCount += 1 + if (msgCount === 3) { + client1.unsubscribe() + } +}) client1.on('ready', function () { // if you need auth, do it here - client1.incr('did a thing'); - client1.subscribe('a nice channel', 'another one'); -}); + client1.incr('did a thing') + client1.subscribe('a nice channel', 'another one') +}) client2.on('ready', function () { // if you need auth, do it here -}); +}) diff --git a/examples/scan.js b/examples/scan.js index e6b67ea6ae..56995dfee0 100644 --- a/examples/scan.js +++ b/examples/scan.js @@ -1,51 +1,51 @@ -'use strict'; +'use strict' -var redis = require('redis'); -var client = redis.createClient(); +var redis = require('redis') +var client = redis.createClient() -var cursor = '0'; +var cursor = '0' function scan () { - client.scan( - cursor, - 'MATCH', 'q:job:*', - 'COUNT', '10', - function (err, res) { - if (err) throw err; + client.scan( + cursor, + 'MATCH', 'q:job:*', + 'COUNT', '10', + function (err, res) { + if (err) throw err - // Update the cursor position for the next scan - cursor = res[0]; - // get the SCAN result for this iteration - var keys = res[1]; + // Update the cursor position for the next scan + cursor = res[0] + // get the SCAN result for this iteration + var keys = res[1] - // Remember: more or less than COUNT or no keys may be returned - // See http://redis.io/commands/scan#the-count-option - // Also, SCAN may return the same key multiple times - // See http://redis.io/commands/scan#scan-guarantees - // Additionally, you should always have the code that uses the keys - // before the code checking the cursor. - if (keys.length > 0) { - console.log('Array of matching keys', keys); - } + // Remember: more or less than COUNT or no keys may be returned + // See http://redis.io/commands/scan#the-count-option + // Also, SCAN may return the same key multiple times + // See http://redis.io/commands/scan#scan-guarantees + // Additionally, you should always have the code that uses the keys + // before the code checking the cursor. + if (keys.length > 0) { + console.log('Array of matching keys', keys) + } - // It's important to note that the cursor and returned keys - // vary independently. The scan is never complete until redis - // returns a non-zero cursor. However, with MATCH and large - // collections, most iterations will return an empty keys array. + // It's important to note that the cursor and returned keys + // vary independently. The scan is never complete until redis + // returns a non-zero cursor. However, with MATCH and large + // collections, most iterations will return an empty keys array. - // Still, a cursor of zero DOES NOT mean that there are no keys. - // A zero cursor just means that the SCAN is complete, but there - // might be one last batch of results to process. + // Still, a cursor of zero DOES NOT mean that there are no keys. + // A zero cursor just means that the SCAN is complete, but there + // might be one last batch of results to process. - // From : - // 'An iteration starts when the cursor is set to 0, - // and terminates when the cursor returned by the server is 0.' - if (cursor === '0') { - return console.log('Iteration complete'); - } + // From : + // 'An iteration starts when the cursor is set to 0, + // and terminates when the cursor returned by the server is 0.' + if (cursor === '0') { + return console.log('Iteration complete') + } - return scan(); - } - ); + return scan() + } + ) } -scan(); +scan() diff --git a/examples/simple.js b/examples/simple.js index 8df8fcbc0a..d77709f0d9 100644 --- a/examples/simple.js +++ b/examples/simple.js @@ -1,26 +1,27 @@ -'use strict'; +'use strict' -var redis = require('redis'); -var client = redis.createClient(); +var redis = require('redis') +var client = redis.createClient() client.on('error', function (err) { - console.log('error event - ' + client.host + ':' + client.port + ' - ' + err); -}); + console.log('error event - ' + client.host + ':' + client.port + ' - ' + err) +}) -client.set('string key', 'string val', console.log); -client.hset('hash key', 'hashtest 1', 'some value', console.log); -client.hset(['hash key', 'hashtest 2', 'some other value'], console.log); +client.set('string key', 'string val', console.log) +client.hset('hash key', 'hashtest 1', 'some value', console.log) +client.hset(['hash key', 'hashtest 2', 'some other value'], console.log) client.hkeys('hash key', function (err, replies) { - if (err) { - return console.error('error response - ' + err); - } + if (err) { + return console.error('error response - ' + err) + } - console.log(replies.length + ' replies:'); - replies.forEach(function (reply, i) { - console.log(' ' + i + ': ' + reply); - }); -}); + console.log(replies.length + ' replies:') + replies.forEach(function (reply, i) { + console.log(' ' + i + ': ' + reply) + }) +}) client.quit(function (err, res) { - console.log('Exiting from quit command.'); -}); + if (err) throw err + console.log('Exiting from quit command.') +}) diff --git a/examples/sort.js b/examples/sort.js index eb6d287345..b5efdb78dd 100644 --- a/examples/sort.js +++ b/examples/sort.js @@ -1,19 +1,19 @@ -'use strict'; +'use strict' -var redis = require('redis'); -var client = redis.createClient(); +var redis = require('redis') +var client = redis.createClient() -client.sadd('mylist', 1); -client.sadd('mylist', 2); -client.sadd('mylist', 3); +client.sadd('mylist', 1) +client.sadd('mylist', 2) +client.sadd('mylist', 3) -client.set('weight_1', 5); -client.set('weight_2', 500); -client.set('weight_3', 1); +client.set('weight_1', 5) +client.set('weight_2', 500) +client.set('weight_3', 1) -client.set('object_1', 'foo'); -client.set('object_2', 'bar'); -client.set('object_3', 'qux'); +client.set('object_1', 'foo') +client.set('object_2', 'bar') +client.set('object_3', 'qux') -client.sort('mylist', 'by', 'weight_*', 'get', 'object_*', console.log); +client.sort('mylist', 'by', 'weight_*', 'get', 'object_*', console.log) // Prints Reply: qux,foo,bar diff --git a/examples/subqueries.js b/examples/subqueries.js index 5677c12919..94c9922fba 100644 --- a/examples/subqueries.js +++ b/examples/subqueries.js @@ -1,17 +1,19 @@ -'use strict'; +'use strict' // Sending commands in response to other commands. // This example runs 'type' against every key in the database // -var client = require('redis').createClient(); +var client = require('redis').createClient() client.keys('*', function (err, keys) { - keys.forEach(function (key, pos) { - client.type(key, function (err, keytype) { - console.log(key + ' is ' + keytype); - if (pos === (keys.length - 1)) { - client.quit(); - } - }); - }); -}); + if (err) throw err + keys.forEach(function (key, pos) { + client.type(key, function (err, keytype) { + if (err) throw err + console.log(key + ' is ' + keytype) + if (pos === (keys.length - 1)) { + client.quit() + } + }) + }) +}) diff --git a/examples/subquery.js b/examples/subquery.js index 06add78af5..40f26fad23 100644 --- a/examples/subquery.js +++ b/examples/subquery.js @@ -1,17 +1,19 @@ -'use strict'; +'use strict' -var client = require('redis').createClient(); +var client = require('redis').createClient() // build a map of all keys and their types client.keys('*', function (err, allKeys) { - var keyTypes = {}; + if (err) throw err + var keyTypes = {} - allKeys.forEach(function (key, pos) { // use second arg of forEach to get pos - client.type(key, function (err, type) { - keyTypes[key] = type; - if (pos === allKeys.length - 1) { // callbacks all run in order - console.dir(keyTypes); - } - }); - }); -}); + allKeys.forEach(function (key, pos) { // use second arg of forEach to get pos + client.type(key, function (err, type) { + if (err) throw err + keyTypes[key] = type + if (pos === allKeys.length - 1) { // callbacks all run in order + console.dir(keyTypes) + } + }) + }) +}) diff --git a/examples/unix_socket.js b/examples/unix_socket.js index b51aef2d11..31e5ebfbb5 100644 --- a/examples/unix_socket.js +++ b/examples/unix_socket.js @@ -1,32 +1,33 @@ -'use strict'; +'use strict' -var redis = require('redis'); -var client = redis.createClient('/tmp/redis.sock'); -var profiler = require('v8-profiler'); +var redis = require('redis') +var client = redis.createClient('/tmp/redis.sock') +var profiler = require('v8-profiler') client.on('connect', function () { - console.log('Got Unix socket connection.'); -}); + console.log('Got Unix socket connection.') +}) client.on('error', function (err) { - console.log(err.message); -}); + console.log(err.message) +}) -client.set('space chars', 'space value'); +client.set('space chars', 'space value') setInterval(function () { - client.get('space chars'); -}, 100); + client.get('space chars') +}, 100) function done () { - client.info(function (err, reply) { - console.log(reply.toString()); - client.quit(); - }); + client.info(function (err, reply) { + if (err) throw err + console.log(reply.toString()) + client.quit() + }) } setTimeout(function () { - console.log('Taking snapshot.'); - profiler.takeSnapshot(); - done(); -}, 5000); + console.log('Taking snapshot.') + profiler.takeSnapshot() + done() +}, 5000) diff --git a/examples/web_server.js b/examples/web_server.js index 376294322d..8814a2764e 100644 --- a/examples/web_server.js +++ b/examples/web_server.js @@ -1,33 +1,36 @@ -'use strict'; +'use strict' // A simple web server that generates dyanmic content based on responses from Redis -var http = require('http'); -var redisClient = require('redis').createClient(); +var http = require('http') +var redisClient = require('redis').createClient() http.createServer(function (request, response) { // The server - response.writeHead(200, { - 'Content-Type': 'text/plain' - }); + response.writeHead(200, { + 'Content-Type': 'text/plain' + }) - var redisInfo, totalRequests; + var redisInfo, totalRequests - redisClient.info(function (err, reply) { - redisInfo = reply; // stash response in outer scope - }); - redisClient.incr('requests', function (err, reply) { - totalRequests = reply; // stash response in outer scope - }); - redisClient.hincrby('ip', request.connection.remoteAddress, 1); - redisClient.hgetall('ip', function (err, reply) { + redisClient.info(function (err, reply) { + if (err) throw err + redisInfo = reply // stash response in outer scope + }) + redisClient.incr('requests', function (err, reply) { + if (err) throw err + totalRequests = reply // stash response in outer scope + }) + redisClient.hincrby('ip', request.connection.remoteAddress, 1) + redisClient.hgetall('ip', function (err, reply) { + if (err) throw err // This is the last reply, so all of the previous replies must have completed already - response.write('This page was generated after talking to redis.\n\n' + + response.write('This page was generated after talking to redis.\n\n' + 'Redis info:\n' + redisInfo + '\n' + 'Total requests: ' + totalRequests + '\n\n' + - 'IP count: \n'); - Object.keys(reply).forEach(function (ip) { - response.write(' ' + ip + ': ' + reply[ip] + '\n'); - }); - response.end(); - }); -}).listen(80); + 'IP count: \n') + Object.keys(reply).forEach(function (ip) { + response.write(' ' + ip + ': ' + reply[ip] + '\n') + }) + response.end() + }) +}).listen(80) diff --git a/index.js b/index.js index 79bc77d2ca..3a6f4be828 100644 --- a/index.js +++ b/index.js @@ -1,160 +1,161 @@ -'use strict'; +'use strict' -var net = require('net'); -var tls = require('tls'); -var util = require('util'); -var utils = require('./lib/utils'); -var Command = require('./lib/command'); -var Queue = require('double-ended-queue'); -var errorClasses = require('./lib/customErrors'); -var EventEmitter = require('events'); -var Parser = require('redis-parser'); -var commands = require('redis-commands'); -var debug = require('./lib/debug'); -var unifyOptions = require('./lib/createClient'); +var Buffer = require('safe-buffer').Buffer +var net = require('net') +var tls = require('tls') +var util = require('util') +var utils = require('./lib/utils') +var Command = require('./lib/command') +var Queue = require('double-ended-queue') +var errorClasses = require('./lib/customErrors') +var EventEmitter = require('events') +var Parser = require('redis-parser') +var commands = require('redis-commands') +var debug = require('./lib/debug') +var unifyOptions = require('./lib/createClient') var SUBSCRIBE_COMMANDS = { - subscribe: true, - unsubscribe: true, - psubscribe: true, - punsubscribe: true -}; + subscribe: true, + unsubscribe: true, + psubscribe: true, + punsubscribe: true +} function noop () {} function handleDetectBuffersReply (reply, command, bufferArgs) { - if (bufferArgs === false || this.messageBuffers) { - // If detectBuffers option was specified, then the reply from the parser will be a buffer. - // If this command did not use Buffer arguments, then convert the reply to Strings here. - reply = utils.replyToStrings(reply); - } + if (bufferArgs === false || this.messageBuffers) { + // If detectBuffers option was specified, then the reply from the parser will be a buffer. + // If this command did not use Buffer arguments, then convert the reply to Strings here. + reply = utils.replyToStrings(reply) + } - if (command === 'hgetall') { - reply = utils.replyToObject(reply); - } - return reply; + if (command === 'hgetall') { + reply = utils.replyToObject(reply) + } + return reply } -exports.debugMode = /\bredis\b/i.test(process.env.NODE_DEBUG); +exports.debugMode = /\bredis\b/i.test(process.env.NODE_DEBUG) // Attention: The second parameter might be removed at will and is not officially supported. // Do not rely on this function RedisClient (options, stream) { - // Copy the options so they are not mutated - options = utils.clone(options); - EventEmitter.call(this); - var cnxOptions = {}; - var self = this; - /* istanbul ignore next: travis does not work with stunnel atm. Therefore the tls tests are skipped on travis */ - for (var tlsOption in options.tls) { - cnxOptions[tlsOption] = options.tls[tlsOption]; - // 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') { - options[tlsOption] = options.tls[tlsOption]; - } - } - if (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 - // Reconnect etc won't work with this. This requires monkey patching to work, so it is not officially supported - options.stream = stream; - this.address = '"Private stream"'; - } else if (options.path) { - cnxOptions.path = options.path; - this.address = options.path; - } else { - cnxOptions.port = +options.port || 6379; - cnxOptions.host = options.host || '127.0.0.1'; - cnxOptions.family = (!options.family && net.isIP(cnxOptions.host)) || (options.family === 'IPv6' ? 6 : 4); - this.address = cnxOptions.host + ':' + cnxOptions.port; + // Copy the options so they are not mutated + options = utils.clone(options) + EventEmitter.call(this) + var cnxOptions = {} + var self = this + /* istanbul ignore next: travis does not work with stunnel atm. Therefore the tls tests are skipped on travis */ + for (var tlsOption in options.tls) { + cnxOptions[tlsOption] = options.tls[tlsOption] + // 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') { + options[tlsOption] = options.tls[tlsOption] } + } + if (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 + // Reconnect etc won't work with this. This requires monkey patching to work, so it is not officially supported + options.stream = stream + this.address = '"Private stream"' + } else if (options.path) { + cnxOptions.path = options.path + this.address = options.path + } else { + cnxOptions.port = +options.port || 6379 + cnxOptions.host = options.host || '127.0.0.1' + cnxOptions.family = (!options.family && net.isIP(cnxOptions.host)) || (options.family === 'IPv6' ? 6 : 4) + this.address = cnxOptions.host + ':' + cnxOptions.port + } - this.connectionOptions = cnxOptions; - this.connectionId = RedisClient.connectionId++; - this.connected = false; - this.ready = false; - if (options.socketKeepalive === undefined) { - options.socketKeepalive = true; + this.connectionOptions = cnxOptions + this.connectionId = RedisClient.connectionId++ + this.connected = false + this.ready = false + if (options.socketKeepalive === undefined) { + options.socketKeepalive = true + } + for (var command in options.renameCommands) { + options.renameCommands[command.toLowerCase()] = options.renameCommands[command] + } + options.returnBuffers = !!options.returnBuffers + options.detectBuffers = !!options.detectBuffers + // Override the detectBuffers setting if returnBuffers is active and print a warning + if (options.returnBuffers && options.detectBuffers) { + self.warn('WARNING: You activated returnBuffers and detectBuffers at the same time. The return value is always going to be a buffer.') + options.detectBuffers = false + } + if (options.detectBuffers) { + // We only need to look at the arguments if we do not know what we have to return + this.handleReply = handleDetectBuffersReply + } + this.shouldBuffer = false + this.commandQueue = new Queue() // Holds sent commands to de-pipeline them + this.offlineQueue = new Queue() // Holds commands issued but not able to be sent + this.pipelineQueue = new Queue() // Holds all pipelined commands + // Only used as timeout until redis has to be connected to redis until throwing an connection error + this.connectTimeout = +options.connectTimeout || 60000 // 60 * 1000 ms + this.enableOfflineQueue = options.enableOfflineQueue !== false + this.initializeRetryVars() + this.pubSubMode = 0 + this.subscriptionSet = {} + this.monitoring = false + this.messageBuffers = false + this.closing = false + this.serverInfo = {} + this.authPass = options.authPass || options.password + this.selectedDb = options.db // Save the selected db here, used when reconnecting + this.oldState = null + this.fireStrings = true // Determine if strings or buffers should be written to the stream + this.pipeline = false + this.subCommandsLeft = 0 + this.timesConnected = 0 + this.buffers = options.returnBuffers || options.detectBuffers + this.options = options + this.reply = 'ON' // Returning replies is the default + this.subscribeChannels = [] + // Init parser + this.replyParser = createParser(this) + this.createStream() + this.on('newListener', function (event) { + if ((event === 'messageBuffer' || event === 'pmessageBuffer') && !this.buffers && !this.messageBuffers) { + this.messageBuffers = true + this.handleReply = handleDetectBuffersReply + this.replyParser.setReturnBuffers(true) } - for (var command in options.renameCommands) { - options.renameCommands[command.toLowerCase()] = options.renameCommands[command]; - } - options.returnBuffers = !!options.returnBuffers; - options.detectBuffers = !!options.detectBuffers; - // Override the detectBuffers setting if returnBuffers is active and print a warning - if (options.returnBuffers && options.detectBuffers) { - self.warn('WARNING: You activated returnBuffers and detectBuffers at the same time. The return value is always going to be a buffer.'); - options.detectBuffers = false; - } - if (options.detectBuffers) { - // We only need to look at the arguments if we do not know what we have to return - this.handleReply = handleDetectBuffersReply; - } - this.shouldBuffer = false; - this.commandQueue = new Queue(); // Holds sent commands to de-pipeline them - this.offlineQueue = new Queue(); // Holds commands issued but not able to be sent - this.pipelineQueue = new Queue(); // Holds all pipelined commands - // Only used as timeout until redis has to be connected to redis until throwing an connection error - this.connectTimeout = +options.connectTimeout || 60000; // 60 * 1000 ms - this.enableOfflineQueue = options.enableOfflineQueue === false ? false : true; - this.initializeRetryVars(); - this.pubSubMode = 0; - this.subscriptionSet = {}; - this.monitoring = false; - this.messageBuffers = false; - this.closing = false; - this.serverInfo = {}; - this.authPass = options.authPass || options.password; - this.selectedDb = options.db; // Save the selected db here, used when reconnecting - this.oldState = null; - this.fireStrings = true; // Determine if strings or buffers should be written to the stream - this.pipeline = false; - this.subCommandsLeft = 0; - this.timesConnected = 0; - this.buffers = options.returnBuffers || options.detectBuffers; - this.options = options; - this.reply = 'ON'; // Returning replies is the default - this.subscribeChannels = []; - // Init parser - this.replyParser = createParser(this); - this.createStream(); - this.on('newListener', function (event) { - if ((event === 'messageBuffer' || event === 'pmessageBuffer') && !this.buffers && !this.messageBuffers) { - this.messageBuffers = true; - this.handleReply = handleDetectBuffersReply; - this.replyParser.setReturnBuffers(true); - } - }); + }) } -util.inherits(RedisClient, EventEmitter); +util.inherits(RedisClient, EventEmitter) -RedisClient.connectionId = 0; +RedisClient.connectionId = 0 function createParser (self) { - return new Parser({ - returnReply: function (data) { - self.returnReply(data); - }, - returnError: function (err) { - // Return a ReplyError to indicate Redis returned an error - self.returnError(err); - }, - returnFatalError: function (err) { - // Error out all fired commands. Otherwise they might rely on faulty data. 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.'; - self.ready = false; - self.flushAndError({ - message: 'Fatal error encountered. Command aborted.', - code: 'NR_FATAL' - }, { - error: err, - queues: ['commandQueue'] - }); - self.emit('error', err); - self.createStream(); - }, - returnBuffers: self.buffers || self.messageBuffers, - stringNumbers: self.options.stringNumbers || false - }); + return new Parser({ + returnReply: function (data) { + self.returnReply(data) + }, + returnError: function (err) { + // Return a ReplyError to indicate Redis returned an error + self.returnError(err) + }, + returnFatalError: function (err) { + // Error out all fired commands. Otherwise they might rely on faulty data. 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.' + self.ready = false + self.flushAndError({ + message: 'Fatal error encountered. Command aborted.', + code: 'NR_FATAL' + }, { + error: err, + queues: ['commandQueue'] + }) + self.emit('error', err) + self.createStream() + }, + returnBuffers: self.buffers || self.messageBuffers, + stringNumbers: self.options.stringNumbers || false + }) } /****************************************************************************** @@ -167,731 +168,731 @@ function createParser (self) { // Attention: the function name "createStream" should not be changed, as other libraries need this to mock the stream (e.g. fakeredis) RedisClient.prototype.createStream = function () { - var self = this; + var self = this - // Init parser - this.replyParser = createParser(this); + // Init parser + this.replyParser = createParser(this) - if (this.options.stream) { - // Only add the listeners once in case of a reconnect try (that won't work) - if (this.stream) { - return; - } - this.stream = this.options.stream; + if (this.options.stream) { + // Only add the listeners once in case of a reconnect try (that won't work) + if (this.stream) { + return + } + this.stream = this.options.stream + } else { + // On a reconnect destroy the former stream and retry + if (this.stream) { + this.stream.removeAllListeners() + this.stream.destroy() + } + + /* istanbul ignore if: travis does not work with stunnel atm. Therefore the tls tests are skipped on travis */ + if (this.options.tls) { + this.stream = tls.connect(this.connectionOptions) } else { - // On a reconnect destroy the former stream and retry - if (this.stream) { - this.stream.removeAllListeners(); - this.stream.destroy(); - } - - /* istanbul ignore if: travis does not work with stunnel atm. Therefore the tls tests are skipped on travis */ - if (this.options.tls) { - this.stream = tls.connect(this.connectionOptions); - } else { - this.stream = net.createConnection(this.connectionOptions); - } + this.stream = net.createConnection(this.connectionOptions) } + } - if (this.options.connectTimeout) { - this.stream.setTimeout(this.connectTimeout, function () { - // Note: This is only tested if a internet connection is established - self.retryTotaltime = self.connectTimeout; - self.connectionGone('timeout'); - }); - } + if (this.options.connectTimeout) { + this.stream.setTimeout(this.connectTimeout, function () { + // Note: This is only tested if a internet connection is established + self.retryTotaltime = self.connectTimeout + self.connectionGone('timeout') + }) + } - /* istanbul ignore next: travis does not work with stunnel atm. Therefore the tls tests are skipped on travis */ - var connectEvent = this.options.tls ? 'secureConnect' : 'connect'; - this.stream.once(connectEvent, function () { - this.removeAllListeners('timeout'); - self.timesConnected++; - self.onConnect(); - }); + /* istanbul ignore next: travis does not work with stunnel atm. Therefore the tls tests are skipped on travis */ + var connectEvent = this.options.tls ? 'secureConnect' : 'connect' + this.stream.once(connectEvent, function () { + this.removeAllListeners('timeout') + self.timesConnected++ + self.onConnect() + }) - this.stream.on('data', function (bufferFromSocket) { - // The bufferFromSocket.toString() has a significant impact on big chunks and therefore this should only be used if necessary - debug('Net read ' + self.address + ' id ' + self.connectionId); // + ': ' + bufferFromSocket.toString()); - self.replyParser.execute(bufferFromSocket); - }); + this.stream.on('data', function (bufferFromSocket) { + // The bufferFromSocket.toString() has a significant impact on big chunks and therefore this should only be used if necessary + debug('Net read ' + self.address + ' id ' + self.connectionId) // + ': ' + bufferFromSocket.toString()); + self.replyParser.execute(bufferFromSocket) + }) - this.stream.on('error', function (err) { - self.onError(err); - }); + this.stream.on('error', function (err) { + self.onError(err) + }) - /* istanbul ignore next: difficult to test and not important as long as we keep this listener */ - this.stream.on('clientError', function (err) { - debug('clientError occurred'); - self.onError(err); - }); + /* istanbul ignore next: difficult to test and not important as long as we keep this listener */ + this.stream.on('clientError', function (err) { + debug('clientError occurred') + self.onError(err) + }) - this.stream.once('close', function (hadError) { - self.connectionGone('close'); - }); + this.stream.once('close', function (hadError) { + self.connectionGone('close') + }) - this.stream.once('end', function () { - self.connectionGone('end'); - }); + this.stream.once('end', function () { + self.connectionGone('end') + }) - // Fire the command before redis is connected to be sure it's the first fired command - if (this.authPass !== undefined) { - this.ready = true; - this.auth(this.authPass); - this.ready = false; - } -}; + // Fire the command before redis is connected to be sure it's the first fired command + if (this.authPass !== undefined) { + this.ready = true + this.auth(this.authPass) + this.ready = false + } +} RedisClient.prototype.handleReply = function (reply, command) { - if (command === 'hgetall') { - reply = utils.replyToObject(reply); - } - return reply; -}; + if (command === 'hgetall') { + reply = utils.replyToObject(reply) + } + return reply +} -RedisClient.prototype.cork = noop; -RedisClient.prototype.uncork = noop; +RedisClient.prototype.cork = noop +RedisClient.prototype.uncork = noop RedisClient.prototype.initializeRetryVars = function () { - this.retryTimer = null; - this.retryTotaltime = 0; - this.retryDelay = 200; - this.retryBackoff = 1.7; - this.attempts = 1; -}; + this.retryTimer = null + this.retryTotaltime = 0 + this.retryDelay = 200 + this.retryBackoff = 1.7 + this.attempts = 1 +} RedisClient.prototype.warn = function (msg) { - var self = this; - // Warn on the next tick. Otherwise no event listener can be added - // for warnings that are emitted in the redis client constructor - process.nextTick(function () { - if (self.listeners('warning').length !== 0) { - self.emit('warning', msg); - } else { - console.warn('nodeRedis:', msg); - } - }); -}; + var self = this + // Warn on the next tick. Otherwise no event listener can be added + // for warnings that are emitted in the redis client constructor + process.nextTick(function () { + if (self.listeners('warning').length !== 0) { + self.emit('warning', msg) + } else { + console.warn('nodeRedis:', msg) + } + }) +} // Flush provided queues, erroring any items with a callback first RedisClient.prototype.flushAndError = function (errorAttributes, options) { - options = options || {}; - var aggregatedErrors = []; - var queueNames = options.queues || ['commandQueue', 'offlineQueue']; // Flush the commandQueue first to keep the order intact - for (var i = 0; i < queueNames.length; i++) { - // If the command was fired it might have been processed so far - if (queueNames[i] === 'commandQueue') { - errorAttributes.message += ' It might have been processed.'; - } else { // As the commandQueue is flushed first, remove this for the offline queue - errorAttributes.message = errorAttributes.message.replace(' It might have been processed.', ''); - } - // Don't flush everything from the queue - for (var commandObj = this[queueNames[i]].shift(); commandObj; commandObj = this[queueNames[i]].shift()) { - var err = new errorClasses.AbortError(errorAttributes); - if (commandObj.error) { - err.stack = err.stack + commandObj.error.stack.replace(/^Error.*?\n/, '\n'); - } - err.command = commandObj.command.toUpperCase(); - if (commandObj.args && commandObj.args.length) { - err.args = commandObj.args; - } - if (options.error) { - err.origin = options.error; - } - if (typeof commandObj.callback === 'function') { - commandObj.callback(err); - } else { - aggregatedErrors.push(err); - } - } + options = options || {} + var aggregatedErrors = [] + var queueNames = options.queues || ['commandQueue', 'offlineQueue'] // Flush the commandQueue first to keep the order intact + for (var i = 0; i < queueNames.length; i++) { + // If the command was fired it might have been processed so far + if (queueNames[i] === 'commandQueue') { + errorAttributes.message += ' It might have been processed.' + } else { // As the commandQueue is flushed first, remove this for the offline queue + errorAttributes.message = errorAttributes.message.replace(' It might have been processed.', '') } - // Currently this would be a breaking change, therefore it's only emitted in debugMode - if (exports.debugMode && aggregatedErrors.length) { - var error; - if (aggregatedErrors.length === 1) { - error = aggregatedErrors[0]; - } else { - errorAttributes.message = errorAttributes.message.replace('It', 'They').replace(/command/i, '$&s'); - error = new errorClasses.AggregateError(errorAttributes); - error.errors = aggregatedErrors; - } - this.emit('error', error); + // Don't flush everything from the queue + for (var commandObj = this[queueNames[i]].shift(); commandObj; commandObj = this[queueNames[i]].shift()) { + var err = new errorClasses.AbortError(errorAttributes) + if (commandObj.error) { + err.stack = err.stack + commandObj.error.stack.replace(/^Error.*?\n/, '\n') + } + err.command = commandObj.command.toUpperCase() + if (commandObj.args && commandObj.args.length) { + err.args = commandObj.args + } + if (options.error) { + err.origin = options.error + } + if (typeof commandObj.callback === 'function') { + commandObj.callback(err) + } else { + aggregatedErrors.push(err) + } } -}; + } + // Currently this would be a breaking change, therefore it's only emitted in debugMode + if (exports.debugMode && aggregatedErrors.length) { + var error + if (aggregatedErrors.length === 1) { + error = aggregatedErrors[0] + } else { + errorAttributes.message = errorAttributes.message.replace('It', 'They').replace(/command/i, '$&s') + error = new errorClasses.AggregateError(errorAttributes) + error.errors = aggregatedErrors + } + this.emit('error', error) + } +} RedisClient.prototype.onError = function (err) { - if (this.closing) { - return; - } + if (this.closing) { + return + } - err.message = 'Redis connection to ' + this.address + ' failed - ' + err.message; - debug(err.message); - this.connected = false; - this.ready = false; + err.message = 'Redis connection to ' + this.address + ' failed - ' + err.message + debug(err.message) + this.connected = false + this.ready = false - // Only emit the error if the retryStrategy option is not set - if (!this.options.retryStrategy) { - this.emit('error', err); - } - // 'error' events get turned into exceptions if they aren't listened for. If the user handled this error - // then we should try to reconnect. - this.connectionGone('error', err); -}; + // Only emit the error if the retryStrategy option is not set + if (!this.options.retryStrategy) { + this.emit('error', err) + } + // 'error' events get turned into exceptions if they aren't listened for. If the user handled this error + // then we should try to reconnect. + this.connectionGone('error', err) +} RedisClient.prototype.onConnect = function () { - debug('Stream connected ' + this.address + ' id ' + this.connectionId); + debug('Stream connected ' + this.address + ' id ' + this.connectionId) - this.connected = true; - this.ready = false; - this.emittedEnd = false; - this.stream.setKeepAlive(this.options.socketKeepalive); - this.stream.setTimeout(0); + this.connected = true + this.ready = false + this.emittedEnd = false + this.stream.setKeepAlive(this.options.socketKeepalive) + this.stream.setTimeout(0) - this.emit('connect'); - this.initializeRetryVars(); + this.emit('connect') + this.initializeRetryVars() - if (this.options.noReadyCheck) { - this.onReady(); - } else { - this.readyCheck(); - } -}; + if (this.options.noReadyCheck) { + this.onReady() + } else { + this.readyCheck() + } +} RedisClient.prototype.onReady = function () { - var self = this; + var self = this - debug('onReady called ' + this.address + ' id ' + this.connectionId); - this.ready = true; + debug('onReady called ' + this.address + ' id ' + this.connectionId) + this.ready = true - this.cork = function () { - self.pipeline = true; - self.stream.cork(); - }; - this.uncork = function () { - if (self.fireStrings) { - self.writeStrings(); - } else { - self.writeBuffers(); - } - self.pipeline = false; - self.fireStrings = true; - // TODO: Consider using next tick here. See https://github.com/NodeRedis/nodeRedis/issues/1033 - self.stream.uncork(); - }; + this.cork = function () { + self.pipeline = true + self.stream.cork() + } + this.uncork = function () { + if (self.fireStrings) { + self.writeStrings() + } else { + self.writeBuffers() + } + self.pipeline = false + self.fireStrings = true + // TODO: Consider using next tick here. See https://github.com/NodeRedis/nodeRedis/issues/1033 + self.stream.uncork() + } - // Restore modal commands from previous connection. The order of the commands is important - if (this.selectedDb !== undefined) { - this.internalSendCommand(new Command('select', [this.selectedDb])); + // Restore modal commands from previous connection. The order of the commands is important + if (this.selectedDb !== undefined) { + this.internalSendCommand(new Command('select', [this.selectedDb])) + } + if (this.monitoring) { // Monitor has to be fired before pub sub commands + this.internalSendCommand(new Command('monitor', [])) + } + var callbackCount = Object.keys(this.subscriptionSet).length + if (!this.options.disableResubscribing && callbackCount) { + debug('Sending pub/sub onReady commands') + for (var key in this.subscriptionSet) { + var command = key.slice(0, key.indexOf('_')) + var args = this.subscriptionSet[key] + this[command]([args]) } - if (this.monitoring) { // Monitor has to be fired before pub sub commands - this.internalSendCommand(new Command('monitor', [])); - } - var callbackCount = Object.keys(this.subscriptionSet).length; - if (!this.options.disableResubscribing && callbackCount) { - debug('Sending pub/sub onReady commands'); - for (var key in this.subscriptionSet) { - var command = key.slice(0, key.indexOf('_')); - var args = this.subscriptionSet[key]; - this[command]([args]); - } - } - this.sendOfflineQueue(); - this.emit('ready'); -}; + } + this.sendOfflineQueue() + this.emit('ready') +} RedisClient.prototype.onInfoCmd = function (err, res) { - if (err) { - if (err.message === "ERR unknown command 'info'") { - this.onReady(); - return; - } - err.message = 'Ready check failed: ' + err.message; - this.emit('error', err); - return; + if (err) { + if (err.message === "ERR unknown command 'info'") { + this.onReady() + return } + err.message = 'Ready check failed: ' + err.message + this.emit('error', err) + return + } - /* istanbul ignore if: some servers might not respond with any info data. This is just a safety check that is difficult to test */ - if (!res) { - debug('The info command returned without any data.'); - this.onReady(); - return; - } + /* istanbul ignore if: some servers might not respond with any info data. This is just a safety check that is difficult to test */ + if (!res) { + debug('The info command returned without any data.') + this.onReady() + return + } - if (!this.serverInfo.loading || this.serverInfo.loading === '0') { - // If the master_link_status exists but the link is not up, try again after 50 ms - if (this.serverInfo.master_link_status && this.serverInfo.master_link_status !== 'up') { - this.serverInfo.loading_eta_seconds = 0.05; - } else { - // Eta loading should change - debug('Redis server ready.'); - this.onReady(); - return; - } + if (!this.serverInfo.loading || this.serverInfo.loading === '0') { + // If the master_link_status exists but the link is not up, try again after 50 ms + if (this.serverInfo.master_link_status && this.serverInfo.master_link_status !== 'up') { + this.serverInfo.loading_eta_seconds = 0.05 + } else { + // Eta loading should change + debug('Redis server ready.') + this.onReady() + return } + } - var retryTime = +this.serverInfo.loading_eta_seconds * 1000; - if (retryTime > 1000) { - retryTime = 1000; - } - debug('Redis server still loading, trying again in ' + retryTime); - setTimeout(function (self) { - self.readyCheck(); - }, retryTime, this); -}; + var retryTime = +this.serverInfo.loading_eta_seconds * 1000 + if (retryTime > 1000) { + retryTime = 1000 + } + debug('Redis server still loading, trying again in ' + retryTime) + setTimeout(function (self) { + self.readyCheck() + }, retryTime, this) +} RedisClient.prototype.readyCheck = function () { - var self = this; - debug('Checking server ready state...'); - // Always fire this info command as first command even if other commands are already queued up - this.ready = true; - this.info(function (err, res) { - self.onInfoCmd(err, res); - }); - this.ready = false; -}; + var self = this + debug('Checking server ready state...') + // Always fire this info command as first command even if other commands are already queued up + this.ready = true + this.info(function (err, res) { + self.onInfoCmd(err, res) + }) + this.ready = false +} RedisClient.prototype.sendOfflineQueue = function () { - for (var commandObj = this.offlineQueue.shift(); commandObj; commandObj = this.offlineQueue.shift()) { - debug('Sending offline command: ' + commandObj.command); - this.internalSendCommand(commandObj); - } -}; + for (var commandObj = this.offlineQueue.shift(); commandObj; commandObj = this.offlineQueue.shift()) { + debug('Sending offline command: ' + commandObj.command) + this.internalSendCommand(commandObj) + } +} var retryConnection = function (self, error) { - debug('Retrying connection...'); + debug('Retrying connection...') - var reconnectParams = { - delay: self.retryDelay, - attempt: self.attempts, - error: error, - totalRetryTime: self.retryTotaltime, - timesConnected: self.timesConnected - }; - self.emit('reconnecting', reconnectParams); + var reconnectParams = { + delay: self.retryDelay, + attempt: self.attempts, + error: error, + totalRetryTime: self.retryTotaltime, + timesConnected: self.timesConnected + } + self.emit('reconnecting', reconnectParams) - self.retryTotaltime += self.retryDelay; - self.attempts += 1; - self.retryDelay = Math.round(self.retryDelay * self.retryBackoff); - self.createStream(); - self.retryTimer = null; -}; + self.retryTotaltime += self.retryDelay + self.attempts += 1 + self.retryDelay = Math.round(self.retryDelay * self.retryBackoff) + self.createStream() + self.retryTimer = null +} RedisClient.prototype.connectionGone = function (why, error) { - // If a retry is already in progress, just let that happen - if (this.retryTimer) { - return; + // If a retry is already in progress, just let that happen + if (this.retryTimer) { + return + } + error = error || null + + debug('Redis connection is gone from ' + why + ' event.') + this.connected = false + this.ready = false + // Deactivate cork to work with the offline queue + this.cork = noop + this.uncork = noop + this.pipeline = false + this.pubSubMode = 0 + + // since we are collapsing end and close, users don't expect to be called twice + if (!this.emittedEnd) { + this.emit('end') + this.emittedEnd = true + } + + // If this is a requested shutdown, then don't retry + if (this.closing) { + debug('Connection ended by quit / end command, not retrying.') + this.flushAndError({ + message: 'Stream connection ended and command aborted.', + code: 'NR_CLOSED' + }, { + error: error + }) + return + } + + if (typeof this.options.retryStrategy === 'function') { + var retryParams = { + attempt: this.attempts, + error: error, + totalRetryTime: this.retryTotaltime, + timesConnected: this.timesConnected } - error = error || null; - - debug('Redis connection is gone from ' + why + ' event.'); - this.connected = false; - this.ready = false; - // Deactivate cork to work with the offline queue - this.cork = noop; - this.uncork = noop; - this.pipeline = false; - this.pubSubMode = 0; - - // since we are collapsing end and close, users don't expect to be called twice - if (!this.emittedEnd) { - this.emit('end'); - this.emittedEnd = true; + this.retryDelay = this.options.retryStrategy(retryParams) + if (typeof this.retryDelay !== 'number') { + // Pass individual error through + if (this.retryDelay instanceof Error) { + error = this.retryDelay + } + this.flushAndError({ + message: 'Stream connection ended and command aborted.', + code: 'NR_CLOSED' + }, { + error: error + }) + this.end(false) + return } + } - // If this is a requested shutdown, then don't retry - if (this.closing) { - debug('Connection ended by quit / end command, not retrying.'); - this.flushAndError({ - message: 'Stream connection ended and command aborted.', - code: 'NR_CLOSED' - }, { - error: error - }); - return; - } + // Retry commands after a reconnect instead of throwing an error. Use this with caution + if (this.options.retryUnfulfilledCommands) { + this.offlineQueue.unshift.apply(this.offlineQueue, this.commandQueue.toArray()) + this.commandQueue.clear() + } else if (this.commandQueue.length !== 0) { + this.flushAndError({ + message: 'Redis connection lost and command aborted.', + code: 'UNCERTAIN_STATE' + }, { + error: error, + queues: ['commandQueue'] + }) + } - if (typeof this.options.retryStrategy === 'function') { - var retryParams = { - attempt: this.attempts, - error: error, - totalRetryTime: this.retryTotaltime, - timesConnected: this.timesConnected - }; - this.retryDelay = this.options.retryStrategy(retryParams); - if (typeof this.retryDelay !== 'number') { - // Pass individual error through - if (this.retryDelay instanceof Error) { - error = this.retryDelay; - } - this.flushAndError({ - message: 'Stream connection ended and command aborted.', - code: 'NR_CLOSED' - }, { - error: error - }); - this.end(false); - return; - } - } + debug('Retry connection in ' + this.retryDelay + ' ms') - // Retry commands after a reconnect instead of throwing an error. Use this with caution - if (this.options.retryUnfulfilledCommands) { - this.offlineQueue.unshift.apply(this.offlineQueue, this.commandQueue.toArray()); - this.commandQueue.clear(); - } else if (this.commandQueue.length !== 0) { - this.flushAndError({ - message: 'Redis connection lost and command aborted.', - code: 'UNCERTAIN_STATE' - }, { - error: error, - queues: ['commandQueue'] - }); - } - - debug('Retry connection in ' + this.retryDelay + ' ms'); - - this.retryTimer = setTimeout(retryConnection, this.retryDelay, this, error); -}; + this.retryTimer = setTimeout(retryConnection, this.retryDelay, this, error) +} RedisClient.prototype.returnError = function (err) { - var commandObj = this.commandQueue.shift(); - if (commandObj.error) { - err.stack = commandObj.error.stack.replace(/^Error.*?\n/, 'ReplyError: ' + err.message + '\n'); - } - err.command = commandObj.command.toUpperCase(); - if (commandObj.args && commandObj.args.length) { - err.args = commandObj.args; - } + var commandObj = this.commandQueue.shift() + if (commandObj.error) { + err.stack = commandObj.error.stack.replace(/^Error.*?\n/, 'ReplyError: ' + err.message + '\n') + } + err.command = commandObj.command.toUpperCase() + if (commandObj.args && commandObj.args.length) { + err.args = commandObj.args + } - // Count down pub sub mode if in entering modus - if (this.pubSubMode > 1) { - this.pubSubMode--; - } + // Count down pub sub mode if in entering modus + if (this.pubSubMode > 1) { + this.pubSubMode-- + } - var match = err.message.match(utils.errCode); - // LUA script could return user errors that don't behave like all other errors! - if (match) { - err.code = match[1]; - } + var match = err.message.match(utils.errCode) + // LUA script could return user errors that don't behave like all other errors! + if (match) { + err.code = match[1] + } - utils.callbackOrEmit(this, commandObj.callback, err); -}; + utils.callbackOrEmit(this, commandObj.callback, err) +} function normalReply (self, reply) { - var commandObj = self.commandQueue.shift(); - if (typeof commandObj.callback === 'function') { - if (commandObj.command !== 'exec') { - reply = self.handleReply(reply, commandObj.command, commandObj.bufferArgs); - } - commandObj.callback(null, reply); - } else { - debug('No callback for reply'); + var commandObj = self.commandQueue.shift() + if (typeof commandObj.callback === 'function') { + if (commandObj.command !== 'exec') { + reply = self.handleReply(reply, commandObj.command, commandObj.bufferArgs) } + commandObj.callback(null, reply) + } else { + debug('No callback for reply') + } } function subscribeUnsubscribe (self, reply, type) { // Subscribe commands take an optional callback and also emit an event, but 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 - var commandObj = self.commandQueue.get(0); - var buffer = self.options.returnBuffers || self.options.detectBuffers && commandObj.bufferArgs; - var channel = (buffer || reply[1] === null) ? reply[1] : reply[1].toString(); - var count = +reply[2]; // Return the channel counter as number no matter if `stringNumbers` is activated or not - debug(type, channel); + var commandObj = self.commandQueue.get(0) + var buffer = self.options.returnBuffers || self.options.detectBuffers && commandObj.bufferArgs + var channel = (buffer || reply[1] === null) ? reply[1] : reply[1].toString() + var count = +reply[2] // Return the channel counter as number no matter if `stringNumbers` is activated or not + debug(type, channel) - // Emit first, then return the callback - if (channel !== null) { // Do not emit or "unsubscribe" something if there was no channel to unsubscribe from - self.emit(type, channel, count); - if (type === 'subscribe' || type === 'psubscribe') { - self.subscriptionSet[type + '_' + channel] = channel; - } else { - type = type === 'unsubscribe' ? 'subscribe' : 'psubscribe'; // Make types consistent - delete self.subscriptionSet[type + '_' + channel]; - } - self.subscribeChannels.push(channel); - } - - if (commandObj.args.length === 1 || self.subCommandsLeft === 1 || commandObj.args.length === 0 && (count === 0 || channel === null)) { - if (count === 0) { // unsubscribed from all channels - var runningCommand; - var i = 1; - self.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 - while (runningCommand = self.commandQueue.get(i)) { - if (SUBSCRIBE_COMMANDS[runningCommand.command]) { - self.pubSubMode = i; // Entering pub sub mode again - break; - } - i++; - } - } - self.commandQueue.shift(); - if (typeof commandObj.callback === 'function') { - commandObj.callback(null, [count, self.subscribeChannels]); - } - self.subscribeChannels = []; - self.subCommandsLeft = 0; + // Emit first, then return the callback + if (channel !== null) { // Do not emit or "unsubscribe" something if there was no channel to unsubscribe from + self.emit(type, channel, count) + if (type === 'subscribe' || type === 'psubscribe') { + self.subscriptionSet[type + '_' + channel] = channel } else { - if (self.subCommandsLeft !== 0) { - self.subCommandsLeft--; - } else { - self.subCommandsLeft = commandObj.args.length ? commandObj.args.length - 1 : count; - } + type = type === 'unsubscribe' ? 'subscribe' : 'psubscribe' // Make types consistent + delete self.subscriptionSet[type + '_' + channel] } + self.subscribeChannels.push(channel) + } + + if (commandObj.args.length === 1 || self.subCommandsLeft === 1 || commandObj.args.length === 0 && (count === 0 || channel === null)) { + if (count === 0) { // unsubscribed from all channels + var runningCommand + var i = 1 + self.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 + for (runningCommand = self.commandQueue.get(i); runningCommand !== undefined; runningCommand = self.commandQueue.get(i)) { + if (SUBSCRIBE_COMMANDS[runningCommand.command]) { + self.pubSubMode = i // Entering pub sub mode again + break + } + i++ + } + } + self.commandQueue.shift() + if (typeof commandObj.callback === 'function') { + commandObj.callback(null, [count, self.subscribeChannels]) + } + self.subscribeChannels = [] + self.subCommandsLeft = 0 + } else { + if (self.subCommandsLeft !== 0) { + self.subCommandsLeft-- + } else { + self.subCommandsLeft = commandObj.args.length ? commandObj.args.length - 1 : count + } + } } function returnPubSub (self, reply) { - var type = reply[0].toString(); - if (type === 'message') { // channel, message - if (!self.options.returnBuffers || self.messageBuffers) { // backwards compatible. Refactor this in v.3 to always return a string on the normal emitter - self.emit('message', reply[1].toString(), reply[2].toString()); - self.emit('messageBuffer', reply[1], reply[2]); - } else { - self.emit('message', reply[1], reply[2]); - } - } else if (type === 'pmessage') { // pattern, channel, message - if (!self.options.returnBuffers || self.messageBuffers) { // backwards compatible. Refactor this in v.3 to always return a string on the normal emitter - self.emit('pmessage', reply[1].toString(), reply[2].toString(), reply[3].toString()); - self.emit('pmessageBuffer', reply[1], reply[2], reply[3]); - } else { - self.emit('pmessage', reply[1], reply[2], reply[3]); - } + var type = reply[0].toString() + if (type === 'message') { // channel, message + if (!self.options.returnBuffers || self.messageBuffers) { // backwards compatible. Refactor this in v.3 to always return a string on the normal emitter + self.emit('message', reply[1].toString(), reply[2].toString()) + self.emit('messageBuffer', reply[1], reply[2]) } else { - subscribeUnsubscribe(self, reply, type); + self.emit('message', reply[1], reply[2]) } + } else if (type === 'pmessage') { // pattern, channel, message + if (!self.options.returnBuffers || self.messageBuffers) { // backwards compatible. Refactor this in v.3 to always return a string on the normal emitter + self.emit('pmessage', reply[1].toString(), reply[2].toString(), reply[3].toString()) + self.emit('pmessageBuffer', reply[1], reply[2], reply[3]) + } else { + self.emit('pmessage', reply[1], reply[2], reply[3]) + } + } else { + subscribeUnsubscribe(self, reply, type) + } } RedisClient.prototype.returnReply = function (reply) { - // If in monitor mode, all normal commands are still working and we only want to emit the streamlined commands - // As this is not the average use case and monitor is expensive anyway, let's change the code here, to improve - // the average performance of all other commands in case of no monitor mode - if (this.monitoring) { - var replyStr; - if (this.buffers && Buffer.isBuffer(reply)) { - replyStr = reply.toString(); - } else { - replyStr = reply; - } - // While reconnecting the redis server does not recognize the client as in monitor mode anymore - // Therefore the monitor command has to finish before it catches further commands - if (typeof replyStr === 'string' && utils.monitorRegex.test(replyStr)) { - var timestamp = replyStr.slice(0, replyStr.indexOf(' ')); - var args = replyStr.slice(replyStr.indexOf('"') + 1, -1).split('" "').map(function (elem) { - return elem.replace(/\\"/g, '"'); - }); - this.emit('monitor', timestamp, args, replyStr); - return; - } - } - if (this.pubSubMode === 0) { - normalReply(this, reply); - } else if (this.pubSubMode !== 1) { - this.pubSubMode--; - normalReply(this, reply); - } else if (!(reply instanceof Array) || reply.length <= 2) { - // Only PING and QUIT are allowed in this context besides the pub sub commands - // Ping replies with ['pong', null|value] and quit with 'OK' - normalReply(this, reply); + // If in monitor mode, all normal commands are still working and we only want to emit the streamlined commands + // As this is not the average use case and monitor is expensive anyway, let's change the code here, to improve + // the average performance of all other commands in case of no monitor mode + if (this.monitoring) { + var replyStr + if (this.buffers && Buffer.isBuffer(reply)) { + replyStr = reply.toString() } else { - returnPubSub(this, reply); + replyStr = reply } -}; + // While reconnecting the redis server does not recognize the client as in monitor mode anymore + // Therefore the monitor command has to finish before it catches further commands + if (typeof replyStr === 'string' && utils.monitorRegex.test(replyStr)) { + var timestamp = replyStr.slice(0, replyStr.indexOf(' ')) + var args = replyStr.slice(replyStr.indexOf('"') + 1, -1).split('" "').map(function (elem) { + return elem.replace(/\\"/g, '"') + }) + this.emit('monitor', timestamp, args, replyStr) + return + } + } + if (this.pubSubMode === 0) { + normalReply(this, reply) + } else if (this.pubSubMode !== 1) { + this.pubSubMode-- + normalReply(this, reply) + } else if (!(reply instanceof Array) || reply.length <= 2) { + // Only PING and QUIT are allowed in this context besides the pub sub commands + // Ping replies with ['pong', null|value] and quit with 'OK' + normalReply(this, reply) + } else { + returnPubSub(this, reply) + } +} function handleOfflineCommand (self, commandObj) { - var command = commandObj.command; - var err, msg; - if (self.closing || !self.enableOfflineQueue) { - command = command.toUpperCase(); - if (!self.closing) { - if (self.stream.writable) { - msg = 'The connection is not yet established and the offline queue is deactivated.'; - } else { - msg = 'Stream not writeable.'; - } - } else { - msg = 'The connection is already closed.'; - } - err = new errorClasses.AbortError({ - message: command + " can't be processed. " + msg, - code: 'NR_CLOSED', - command: command - }); - if (commandObj.args.length) { - err.args = commandObj.args; - } - utils.replyInOrder(self, commandObj.callback, err); + var command = commandObj.command + var err, msg + if (self.closing || !self.enableOfflineQueue) { + command = command.toUpperCase() + if (!self.closing) { + if (self.stream.writable) { + msg = 'The connection is not yet established and the offline queue is deactivated.' + } else { + msg = 'Stream not writeable.' + } } else { - debug('Queueing ' + command + ' for next server connection.'); - self.offlineQueue.push(commandObj); + msg = 'The connection is already closed.' } - self.shouldBuffer = true; + err = new errorClasses.AbortError({ + message: command + " can't be processed. " + msg, + code: 'NR_CLOSED', + command: command + }) + if (commandObj.args.length) { + err.args = commandObj.args + } + utils.replyInOrder(self, commandObj.callback, err) + } else { + debug('Queueing ' + command + ' for next server connection.') + self.offlineQueue.push(commandObj) + } + self.shouldBuffer = true } // Do not call internalSendCommand directly, if you are not absolutely certain it handles everything properly // e.g. monitor / info does not work with internalSendCommand only RedisClient.prototype.internalSendCommand = function (commandObj) { - var arg, prefixKeys; - var i = 0; - var commandStr = ''; - var args = commandObj.args; - var command = commandObj.command; - var len = args.length; - var bigData = false; - var argsCopy = new Array(len); + var arg, prefixKeys + var i = 0 + var commandStr = '' + var args = commandObj.args + var command = commandObj.command + var len = args.length + var bigData = false + var argsCopy = new Array(len) - if (process.domain && commandObj.callback) { - commandObj.callback = process.domain.bind(commandObj.callback); - } + if (process.domain && commandObj.callback) { + commandObj.callback = process.domain.bind(commandObj.callback) + } - if (this.ready === false || this.stream.writable === false) { - // Handle offline commands right away - handleOfflineCommand(this, commandObj); - return false; // Indicate buffering + if (this.ready === false || this.stream.writable === false) { + // Handle offline commands right away + handleOfflineCommand(this, commandObj) + return false // Indicate buffering + } + + for (i = 0; i < len; i += 1) { + if (typeof args[i] === 'string') { + // 30000 seemed to be a good value to switch to buffers after testing and checking the pros and cons + if (args[i].length > 30000) { + bigData = true + argsCopy[i] = Buffer.from(args[i]) + } else { + argsCopy[i] = args[i] + } + } else if (typeof args[i] === 'object') { // Checking for object instead of Buffer.isBuffer helps us finding data types that we can't handle properly + if (args[i] instanceof Date) { // Accept dates as valid input + argsCopy[i] = args[i].toString() + } else if (args[i] === null) { + this.warn( + 'Deprecated: The ' + command.toUpperCase() + ' command contains a "null" argument.\n' + + 'This is converted to a "null" string now and will return an error from v.3.0 on.\n' + + 'Please handle this in your code to make sure everything works as you intended it to.' + ) + argsCopy[i] = 'null' // Backwards compatible :/ + } else if (Buffer.isBuffer(args[i])) { + argsCopy[i] = args[i] + commandObj.bufferArgs = true + bigData = true + } else { + this.warn( + 'Deprecated: The ' + command.toUpperCase() + ' command contains a argument of type ' + args[i].constructor.name + '.\n' + + 'This is converted to "' + args[i].toString() + '" by using .toString() now and will return an error from v.3.0 on.\n' + + 'Please handle this in your code to make sure everything works as you intended it to.' + ) + argsCopy[i] = args[i].toString() // Backwards compatible :/ + } + } else if (typeof args[i] === 'undefined') { + this.warn( + 'Deprecated: The ' + command.toUpperCase() + ' command contains a "undefined" argument.\n' + + 'This is converted to a "undefined" string now and will return an error from v.3.0 on.\n' + + 'Please handle this in your code to make sure everything works as you intended it to.' + ) + argsCopy[i] = 'undefined' // Backwards compatible :/ + } else { + // Seems like numbers are converted fast using string concatenation + argsCopy[i] = '' + args[i] } + } + + if (this.options.prefix) { + prefixKeys = commands.getKeyIndexes(command, argsCopy) + for (i = prefixKeys.pop(); i !== undefined; i = prefixKeys.pop()) { + argsCopy[i] = this.options.prefix + argsCopy[i] + } + } + if (this.options.renameCommands !== undefined && this.options.renameCommands[command]) { + command = this.options.renameCommands[command] + } + // Always use 'Multi bulk commands', but if passed any Buffer args, then do 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. + commandStr = '*' + (len + 1) + '\r\n$' + command.length + '\r\n' + command + '\r\n' + + if (bigData === false) { // Build up a string and send entire command in one write + for (i = 0; i < len; i += 1) { + arg = argsCopy[i] + commandStr += '$' + Buffer.byteLength(arg) + '\r\n' + arg + '\r\n' + } + debug('Send ' + this.address + ' id ' + this.connectionId + ': ' + commandStr) + this.write(commandStr) + } else { + debug('Send command (' + commandStr + ') has Buffer arguments') + this.fireStrings = false + this.write(commandStr) for (i = 0; i < len; i += 1) { - if (typeof args[i] === 'string') { - // 30000 seemed to be a good value to switch to buffers after testing and checking the pros and cons - if (args[i].length > 30000) { - bigData = true; - argsCopy[i] = new Buffer(args[i], 'utf8'); - } else { - argsCopy[i] = args[i]; - } - } else if (typeof args[i] === 'object') { // Checking for object instead of Buffer.isBuffer helps us finding data types that we can't handle properly - if (args[i] instanceof Date) { // Accept dates as valid input - argsCopy[i] = args[i].toString(); - } else if (args[i] === null) { - this.warn( - 'Deprecated: The ' + command.toUpperCase() + ' command contains a "null" argument.\n' + - 'This is converted to a "null" string now and will return an error from v.3.0 on.\n' + - 'Please handle this in your code to make sure everything works as you intended it to.' - ); - argsCopy[i] = 'null'; // Backwards compatible :/ - } else if (Buffer.isBuffer(args[i])) { - argsCopy[i] = args[i]; - commandObj.bufferArgs = true; - bigData = true; - } else { - this.warn( - 'Deprecated: The ' + command.toUpperCase() + ' command contains a argument of type ' + args[i].constructor.name + '.\n' + - 'This is converted to "' + args[i].toString() + '" by using .toString() now and will return an error from v.3.0 on.\n' + - 'Please handle this in your code to make sure everything works as you intended it to.' - ); - argsCopy[i] = args[i].toString(); // Backwards compatible :/ - } - } else if (typeof args[i] === 'undefined') { - this.warn( - 'Deprecated: The ' + command.toUpperCase() + ' command contains a "undefined" argument.\n' + - 'This is converted to a "undefined" string now and will return an error from v.3.0 on.\n' + - 'Please handle this in your code to make sure everything works as you intended it to.' - ); - argsCopy[i] = 'undefined'; // Backwards compatible :/ - } else { - // Seems like numbers are converted fast using string concatenation - argsCopy[i] = '' + args[i]; - } + arg = argsCopy[i] + if (typeof arg === 'string') { + this.write('$' + Buffer.byteLength(arg) + '\r\n' + arg + '\r\n') + } else { // buffer + this.write('$' + arg.length + '\r\n') + this.write(arg) + this.write('\r\n') + } + debug('sendCommand: buffer send ' + arg.length + ' bytes') } - - if (this.options.prefix) { - prefixKeys = commands.getKeyIndexes(command, argsCopy); - for (i = prefixKeys.pop(); i !== undefined; i = prefixKeys.pop()) { - argsCopy[i] = this.options.prefix + argsCopy[i]; - } + } + if (commandObj.callOnWrite) { + commandObj.callOnWrite() + } + // Handle `CLIENT REPLY ON|OFF|SKIP` + // This has to be checked after callOnWrite + /* istanbul ignore else: TODO: Remove this as soon as we test Redis 3.2 on travis */ + if (this.reply === 'ON') { + this.commandQueue.push(commandObj) + } else { + // Do not expect a reply + // Does this work in combination with the pub sub mode? + if (commandObj.callback) { + utils.replyInOrder(this, commandObj.callback, null, undefined, this.commandQueue) } - if (this.options.renameCommands !== undefined && this.options.renameCommands[command]) { - command = this.options.renameCommands[command]; + if (this.reply === 'SKIP') { + this.reply = 'SKIP_ONE_MORE' + } else if (this.reply === 'SKIP_ONE_MORE') { + this.reply = 'ON' } - // Always use 'Multi bulk commands', but if passed any Buffer args, then do 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. - commandStr = '*' + (len + 1) + '\r\n$' + command.length + '\r\n' + command + '\r\n'; - - if (bigData === false) { // Build up a string and send entire command in one write - for (i = 0; i < len; i += 1) { - arg = argsCopy[i]; - commandStr += '$' + Buffer.byteLength(arg) + '\r\n' + arg + '\r\n'; - } - debug('Send ' + this.address + ' id ' + this.connectionId + ': ' + commandStr); - this.write(commandStr); - } else { - debug('Send command (' + commandStr + ') has Buffer arguments'); - this.fireStrings = false; - this.write(commandStr); - - for (i = 0; i < len; i += 1) { - arg = argsCopy[i]; - if (typeof arg === 'string') { - this.write('$' + Buffer.byteLength(arg) + '\r\n' + arg + '\r\n'); - } else { // buffer - this.write('$' + arg.length + '\r\n'); - this.write(arg); - this.write('\r\n'); - } - debug('sendCommand: buffer send ' + arg.length + ' bytes'); - } - } - if (commandObj.callOnWrite) { - commandObj.callOnWrite(); - } - // Handle `CLIENT REPLY ON|OFF|SKIP` - // This has to be checked after callOnWrite - /* istanbul ignore else: TODO: Remove this as soon as we test Redis 3.2 on travis */ - if (this.reply === 'ON') { - this.commandQueue.push(commandObj); - } else { - // Do not expect a reply - // Does this work in combination with the pub sub mode? - if (commandObj.callback) { - utils.replyInOrder(this, commandObj.callback, null, undefined, this.commandQueue); - } - if (this.reply === 'SKIP') { - this.reply = 'SKIP_ONE_MORE'; - } else if (this.reply === 'SKIP_ONE_MORE') { - this.reply = 'ON'; - } - } - return !this.shouldBuffer; -}; + } + return !this.shouldBuffer +} RedisClient.prototype.writeStrings = function () { - var str = ''; - for (var command = this.pipelineQueue.shift(); command; command = this.pipelineQueue.shift()) { - // Write to stream if the string is bigger than 4mb. The biggest string may be Math.pow(2, 28) - 15 chars long - if (str.length + command.length > 4 * 1024 * 1024) { - this.shouldBuffer = !this.stream.write(str); - str = ''; - } - str += command; + var str = '' + for (var command = this.pipelineQueue.shift(); command; command = this.pipelineQueue.shift()) { + // Write to stream if the string is bigger than 4mb. The biggest string may be Math.pow(2, 28) - 15 chars long + if (str.length + command.length > 4 * 1024 * 1024) { + this.shouldBuffer = !this.stream.write(str) + str = '' } - if (str !== '') { - this.shouldBuffer = !this.stream.write(str); - } -}; + str += command + } + if (str !== '') { + this.shouldBuffer = !this.stream.write(str) + } +} RedisClient.prototype.writeBuffers = function () { - for (var command = this.pipelineQueue.shift(); command; command = this.pipelineQueue.shift()) { - this.shouldBuffer = !this.stream.write(command); - } -}; + for (var command = this.pipelineQueue.shift(); command; command = this.pipelineQueue.shift()) { + this.shouldBuffer = !this.stream.write(command) + } +} RedisClient.prototype.write = function (data) { - if (this.pipeline === false) { - this.shouldBuffer = !this.stream.write(data); - return; - } - this.pipelineQueue.push(data); -}; + if (this.pipeline === false) { + this.shouldBuffer = !this.stream.write(data) + return + } + this.pipelineQueue.push(data) +} exports.createClient = function () { - return new RedisClient(unifyOptions.apply(null, arguments)); -}; -exports.RedisClient = RedisClient; -exports.Multi = require('./lib/multi'); -exports.AbortError = errorClasses.AbortError; -exports.RedisError = Parser.RedisError; -exports.ParserError = Parser.ParserError; -exports.ReplyError = Parser.ReplyError; -exports.AggregateError = errorClasses.AggregateError; + return new RedisClient(unifyOptions.apply(null, arguments)) +} +exports.RedisClient = RedisClient +exports.Multi = require('./lib/multi') +exports.AbortError = errorClasses.AbortError +exports.RedisError = Parser.RedisError +exports.ParserError = Parser.ParserError +exports.ReplyError = Parser.ReplyError +exports.AggregateError = errorClasses.AggregateError // Add all redis commands / nodeRedis api to the client -require('./lib/individualCommands'); -require('./lib/extendedApi'); -require('./lib/commands'); +require('./lib/individualCommands') +require('./lib/extendedApi') +require('./lib/commands') diff --git a/lib/command.js b/lib/command.js index 4746c9a4ff..fa06678c20 100644 --- a/lib/command.js +++ b/lib/command.js @@ -1,16 +1,16 @@ -'use strict'; +'use strict' -var betterStackTraces = /development/i.test(process.env.NODE_ENV) || /\bredis\b/i.test(process.env.NODE_DEBUG); +var betterStackTraces = /development/i.test(process.env.NODE_ENV) || /\bredis\b/i.test(process.env.NODE_DEBUG) function Command (command, args, callback, callOnWrite) { - this.command = command; - this.args = args; - this.bufferArgs = false; - this.callback = callback; - this.callOnWrite = callOnWrite; - if (betterStackTraces) { - this.error = new Error(); - } + this.command = command + this.args = args + this.bufferArgs = false + this.callback = callback + this.callOnWrite = callOnWrite + if (betterStackTraces) { + this.error = new Error() + } } -module.exports = Command; +module.exports = Command diff --git a/lib/commands.js b/lib/commands.js index a4172d8b8a..9fb927d041 100644 --- a/lib/commands.js +++ b/lib/commands.js @@ -1,100 +1,99 @@ -'use strict'; +'use strict' -var commands = require('redis-commands'); -var Multi = require('./multi'); -var RedisClient = require('../').RedisClient; -var Command = require('./command'); +var commands = require('redis-commands') +var Multi = require('./multi') +var RedisClient = require('../').RedisClient +var Command = require('./command') // TODO: Rewrite this including the individual commands into a Commands class // that provided a functionality to add new commands to the client commands.list.forEach(function (command) { + // Some rare Redis commands use special characters in their command name + // Convert those to a underscore to prevent using invalid function names + var commandName = command.replace(/(?:^([0-9])|[^a-zA-Z0-9_$])/g, '_$1') - // Some rare Redis commands use special characters in their command name - // Convert those to a underscore to prevent using invalid function names - var commandName = command.replace(/(?:^([0-9])|[^a-zA-Z0-9_$])/g, '_$1'); - - // Do not override existing functions - if (!RedisClient.prototype[command]) { - RedisClient.prototype[command] = function () { - var arr; - var len = arguments.length; - var callback; - var i = 0; - if (Array.isArray(arguments[0])) { - arr = arguments[0]; - if (len === 2) { - callback = arguments[1]; - } - } else if (len > 1 && Array.isArray(arguments[1])) { - if (len === 3) { - callback = arguments[2]; - } - len = arguments[1].length; - arr = new Array(len + 1); - arr[0] = arguments[0]; - for (; i < len; i += 1) { - arr[i + 1] = arguments[1][i]; - } - } else { - // The later should not be the average use case - if (len !== 0 && (typeof arguments[len - 1] === 'function' || typeof arguments[len - 1] === 'undefined')) { - len--; - callback = arguments[len]; - } - arr = new Array(len); - for (; i < len; i += 1) { - arr[i] = arguments[i]; - } - } - return this.internalSendCommand(new Command(command, arr, callback)); - }; - if (RedisClient.prototype[command] !== commandName) { - Object.defineProperty(RedisClient.prototype[command], 'name', { - value: commandName - }); + // Do not override existing functions + if (!RedisClient.prototype[command]) { + RedisClient.prototype[command] = function () { + var arr + var len = arguments.length + var callback + var i = 0 + if (Array.isArray(arguments[0])) { + arr = arguments[0] + if (len === 2) { + callback = arguments[1] } - } - - // Do not override existing functions - if (!Multi.prototype[command]) { - Multi.prototype[command] = function () { - var arr; - var len = arguments.length; - var callback; - var i = 0; - if (Array.isArray(arguments[0])) { - arr = arguments[0]; - if (len === 2) { - callback = arguments[1]; - } - } else if (len > 1 && Array.isArray(arguments[1])) { - if (len === 3) { - callback = arguments[2]; - } - len = arguments[1].length; - arr = new Array(len + 1); - arr[0] = arguments[0]; - for (; i < len; i += 1) { - arr[i + 1] = arguments[1][i]; - } - } else { - // The later should not be the average use case - if (len !== 0 && (typeof arguments[len - 1] === 'function' || typeof arguments[len - 1] === 'undefined')) { - len--; - callback = arguments[len]; - } - arr = new Array(len); - for (; i < len; i += 1) { - arr[i] = arguments[i]; - } - } - this.queue.push(new Command(command, arr, callback)); - return this; - }; - if (Multi.prototype[command] !== commandName) { - Object.defineProperty(Multi.prototype[command], 'name', { - value: commandName - }); + } else if (len > 1 && Array.isArray(arguments[1])) { + if (len === 3) { + callback = arguments[2] } + len = arguments[1].length + arr = new Array(len + 1) + arr[0] = arguments[0] + for (; i < len; i += 1) { + arr[i + 1] = arguments[1][i] + } + } else { + // The later should not be the average use case + if (len !== 0 && (typeof arguments[len - 1] === 'function' || typeof arguments[len - 1] === 'undefined')) { + len-- + callback = arguments[len] + } + arr = new Array(len) + for (; i < len; i += 1) { + arr[i] = arguments[i] + } + } + return this.internalSendCommand(new Command(command, arr, callback)) } -}); + if (RedisClient.prototype[command] !== commandName) { + Object.defineProperty(RedisClient.prototype[command], 'name', { + value: commandName + }) + } + } + + // Do not override existing functions + if (!Multi.prototype[command]) { + Multi.prototype[command] = function () { + var arr + var len = arguments.length + var callback + var i = 0 + if (Array.isArray(arguments[0])) { + arr = arguments[0] + if (len === 2) { + callback = arguments[1] + } + } else if (len > 1 && Array.isArray(arguments[1])) { + if (len === 3) { + callback = arguments[2] + } + len = arguments[1].length + arr = new Array(len + 1) + arr[0] = arguments[0] + for (; i < len; i += 1) { + arr[i + 1] = arguments[1][i] + } + } else { + // The later should not be the average use case + if (len !== 0 && (typeof arguments[len - 1] === 'function' || typeof arguments[len - 1] === 'undefined')) { + len-- + callback = arguments[len] + } + arr = new Array(len) + for (; i < len; i += 1) { + arr[i] = arguments[i] + } + } + this.queue.push(new Command(command, arr, callback)) + return this + } + if (Multi.prototype[command] !== commandName) { + Object.defineProperty(Multi.prototype[command], 'name', { + value: commandName + }) + } + } +}) diff --git a/lib/createClient.js b/lib/createClient.js index 5e583180aa..d92abc7193 100644 --- a/lib/createClient.js +++ b/lib/createClient.js @@ -1,80 +1,75 @@ -'use strict'; +'use strict' -var utils = require('./utils'); -var URL = require('url'); +var utils = require('./utils') +var URL = require('url') module.exports = function createClient (portArg, hostArg, options) { - - if (typeof portArg === 'number' || typeof portArg === 'string' && /^\d+$/.test(portArg)) { - - var host; - if (typeof hostArg === 'string') { - host = hostArg; - } else { - if (options && hostArg) { - throw new TypeError('Unknown type of connection in createClient()'); - } - options = options || hostArg; - } - options = utils.clone(options); - options.host = host || options.host; - options.port = portArg; - - } else if (typeof portArg === 'string' || portArg && portArg.url) { - - options = utils.clone(portArg.url ? portArg : hostArg || options); - - var parsed = URL.parse(portArg.url || portArg, true, true); - - // [redis:]//[[user][:password]@][host][:port][/db-number][?db=db-number[&password=bar[&option=value]]] - if (parsed.slashes) { // We require slashes - if (parsed.auth) { - options.password = parsed.auth.split(':')[1]; - } - if (parsed.protocol && parsed.protocol !== 'redis:') { - console.warn('nodeRedis: WARNING: You passed "' + parsed.protocol.substring(0, parsed.protocol.length - 1) + '" as protocol instead of the "redis" protocol!'); - } - if (parsed.pathname && parsed.pathname !== '/') { - options.db = parsed.pathname.substr(1); - } - if (parsed.hostname) { - options.host = parsed.hostname; - } - if (parsed.port) { - options.port = parsed.port; - } - if (parsed.search !== '') { - var elem; - for (elem in parsed.query) { - // If options are passed twice, only the parsed options will be used - if (elem in options) { - if (options[elem] === parsed.query[elem]) { - console.warn('nodeRedis: WARNING: You passed the ' + elem + ' option twice!'); - } else { - throw new RangeError('The ' + elem + ' option is added twice and does not match'); - } - } - options[elem] = parsed.query[elem]; - } - } - } else if (parsed.hostname) { - throw new RangeError('The redis url must begin with slashes "//" or contain slashes after the redis protocol'); - } else { - options.path = portArg; - } - - } else if (typeof portArg === 'object' || portArg === undefined) { - options = utils.clone(portArg || options); - options.host = options.host || hostArg; - - if (portArg && arguments.length !== 1) { - throw new TypeError('To many arguments passed to createClient. Please only pass the options object'); - } + if (typeof portArg === 'number' || (typeof portArg === 'string' && /^\d+$/.test(portArg))) { + var host + if (typeof hostArg === 'string') { + host = hostArg + } else { + if (options && hostArg) { + throw new TypeError('Unknown type of connection in createClient()') + } + options = options || hostArg } + options = utils.clone(options) + options.host = host || options.host + options.port = portArg + } else if (typeof portArg === 'string' || (portArg && portArg.url)) { + options = utils.clone(portArg.url ? portArg : (hostArg || options)) - if (!options) { - throw new TypeError('Unknown type of connection in createClient()'); + var parsed = URL.parse(portArg.url || portArg, true, true) + + // [redis:]//[[user][:password]@][host][:port][/db-number][?db=db-number[&password=bar[&option=value]]] + if (parsed.slashes) { // We require slashes + if (parsed.auth) { + options.password = parsed.auth.split(':')[1] + } + if (parsed.protocol && parsed.protocol !== 'redis:') { + console.warn('nodeRedis: WARNING: You passed "' + parsed.protocol.substring(0, parsed.protocol.length - 1) + '" as protocol instead of the "redis" protocol!') + } + if (parsed.pathname && parsed.pathname !== '/') { + options.db = parsed.pathname.substr(1) + } + if (parsed.hostname) { + options.host = parsed.hostname + } + if (parsed.port) { + options.port = parsed.port + } + if (parsed.search !== '') { + var elem + for (elem in parsed.query) { + // If options are passed twice, only the parsed options will be used + if (elem in options) { + if (options[elem] === parsed.query[elem]) { + console.warn('nodeRedis: WARNING: You passed the ' + elem + ' option twice!') + } else { + throw new RangeError('The ' + elem + ' option is added twice and does not match') + } + } + options[elem] = parsed.query[elem] + } + } + } else if (parsed.hostname) { + throw new RangeError('The redis url must begin with slashes "//" or contain slashes after the redis protocol') + } else { + options.path = portArg } + } else if (typeof portArg === 'object' || portArg === undefined) { + options = utils.clone(portArg || options) + options.host = options.host || hostArg - return options; -}; + if (portArg && arguments.length !== 1) { + throw new TypeError('To many arguments passed to createClient. Please only pass the options object') + } + } + + if (!options) { + throw new TypeError('Unknown type of connection in createClient()') + } + + return options +} diff --git a/lib/customErrors.js b/lib/customErrors.js index d9b34421a7..caa6eff6fb 100644 --- a/lib/customErrors.js +++ b/lib/customErrors.js @@ -1,59 +1,59 @@ -'use strict'; +'use strict' -var util = require('util'); -var assert = require('assert'); -var RedisError = require('redis-parser').RedisError; -var ADD_STACKTRACE = false; +var util = require('util') +var assert = require('assert') +var RedisError = require('redis-parser').RedisError +var ADD_STACKTRACE = false function AbortError (obj, stack) { - assert(obj, 'The options argument is required'); - assert.strictEqual(typeof obj, 'object', 'The options argument has to be of type object'); + assert(obj, 'The options argument is required') + assert.strictEqual(typeof obj, 'object', 'The options argument has to be of type object') - RedisError.call(this, obj.message, ADD_STACKTRACE); - Object.defineProperty(this, 'message', { - value: obj.message || '', - configurable: true, - writable: true - }); - if (stack || stack === undefined) { - Error.captureStackTrace(this, AbortError); - } - for (var keys = Object.keys(obj), key = keys.pop(); key; key = keys.pop()) { - this[key] = obj[key]; - } + RedisError.call(this, obj.message, ADD_STACKTRACE) + Object.defineProperty(this, 'message', { + value: obj.message || '', + configurable: true, + writable: true + }) + if (stack || stack === undefined) { + Error.captureStackTrace(this, AbortError) + } + for (var keys = Object.keys(obj), key = keys.pop(); key; key = keys.pop()) { + this[key] = obj[key] + } } function AggregateError (obj) { - assert(obj, 'The options argument is required'); - assert.strictEqual(typeof obj, 'object', 'The options argument has to be of type object'); + assert(obj, 'The options argument is required') + assert.strictEqual(typeof obj, 'object', 'The options argument has to be of type object') - AbortError.call(this, obj, ADD_STACKTRACE); - Object.defineProperty(this, 'message', { - value: obj.message || '', - configurable: true, - writable: true - }); - Error.captureStackTrace(this, AggregateError); - for (var keys = Object.keys(obj), key = keys.pop(); key; key = keys.pop()) { - this[key] = obj[key]; - } + AbortError.call(this, obj, ADD_STACKTRACE) + Object.defineProperty(this, 'message', { + value: obj.message || '', + configurable: true, + writable: true + }) + Error.captureStackTrace(this, AggregateError) + for (var keys = Object.keys(obj), key = keys.pop(); key; key = keys.pop()) { + this[key] = obj[key] + } } -util.inherits(AbortError, RedisError); -util.inherits(AggregateError, AbortError); +util.inherits(AbortError, RedisError) +util.inherits(AggregateError, AbortError) Object.defineProperty(AbortError.prototype, 'name', { - value: 'AbortError', - configurable: true, - writable: true -}); + value: 'AbortError', + configurable: true, + writable: true +}) Object.defineProperty(AggregateError.prototype, 'name', { - value: 'AggregateError', - configurable: true, - writable: true -}); + value: 'AggregateError', + configurable: true, + writable: true +}) module.exports = { - AbortError: AbortError, - AggregateError: AggregateError -}; + AbortError: AbortError, + AggregateError: AggregateError +} diff --git a/lib/debug.js b/lib/debug.js index 9fe2ac16ec..2e823725f7 100644 --- a/lib/debug.js +++ b/lib/debug.js @@ -1,11 +1,11 @@ -'use strict'; +'use strict' -var index = require('../'); +var index = require('../') function debug () { - if (index.debugMode) { - console.error.apply(null, arguments); - } + if (index.debugMode) { + console.error.apply(null, arguments) + } } -module.exports = debug; +module.exports = debug diff --git a/lib/extendedApi.js b/lib/extendedApi.js index bcd7125da7..deee22095d 100644 --- a/lib/extendedApi.js +++ b/lib/extendedApi.js @@ -1,10 +1,10 @@ -'use strict'; +'use strict' -var utils = require('./utils'); -var debug = require('./debug'); -var RedisClient = require('../').RedisClient; -var Command = require('./command'); -var noop = function () {}; +var utils = require('./utils') +var debug = require('./debug') +var RedisClient = require('../').RedisClient +var Command = require('./command') +var noop = function () {} /********************************************** All documented and exposed API belongs in here @@ -12,101 +12,101 @@ All documented and exposed API belongs in here // Redirect calls to the appropriate function and use to send arbitrary / not supported commands RedisClient.prototype.sendCommand = function (command, args, callback) { - // Throw to fail early instead of relying in order in this case - if (typeof command !== 'string') { - throw new TypeError('Wrong input type "' + (command !== null && command !== undefined ? command.constructor.name : command) + '" for command name'); - } - if (!Array.isArray(args)) { - if (args === undefined || args === null) { - args = []; - } else if (typeof args === 'function' && callback === undefined) { - callback = args; - args = []; - } else { - throw new TypeError('Wrong input type "' + args.constructor.name + '" for args'); - } - } - if (typeof callback !== 'function' && callback !== undefined) { - throw new TypeError('Wrong input type "' + (callback !== null ? callback.constructor.name : 'null') + '" for callback function'); + // Throw to fail early instead of relying in order in this case + if (typeof command !== 'string') { + throw new TypeError('Wrong input type "' + (command !== null && command !== undefined ? command.constructor.name : command) + '" for command name') + } + if (!Array.isArray(args)) { + if (args === undefined || args === null) { + args = [] + } else if (typeof args === 'function' && callback === undefined) { + callback = args + args = [] + } else { + throw new TypeError('Wrong input type "' + args.constructor.name + '" for args') } + } + if (typeof callback !== 'function' && callback !== undefined) { + throw new TypeError('Wrong input type "' + (callback !== null ? callback.constructor.name : 'null') + '" for callback function') + } - // Using the raw multi command is only possible with this function - // If the command is not yet added to the client, the internal function should be called right away - // Otherwise we need to redirect the calls to make sure the internal functions don't get skipped - // The internal functions could 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 - if (command === 'multi' || typeof this[command] !== 'function') { - return this.internalSendCommand(new Command(command, args, callback)); - } - if (typeof callback === 'function') { - args = args.concat([callback]); // Prevent manipulating the input array - } - return this[command].apply(this, args); -}; + // Using the raw multi command is only possible with this function + // If the command is not yet added to the client, the internal function should be called right away + // Otherwise we need to redirect the calls to make sure the internal functions don't get skipped + // The internal functions could 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 + if (command === 'multi' || typeof this[command] !== 'function') { + return this.internalSendCommand(new Command(command, args, callback)) + } + if (typeof callback === 'function') { + args = args.concat([callback]) // Prevent manipulating the input array + } + return this[command].apply(this, args) +} RedisClient.prototype.end = function (flush) { - // Flush queue if wanted - if (flush) { - this.flushAndError({ - message: 'Connection forcefully ended and command aborted.', - code: 'NR_CLOSED' - }); - } else if (arguments.length === 0) { - this.warn( - 'Using .end() without the flush parameter is deprecated and throws from v.3.0.0 on.\n' + + // Flush queue if wanted + if (flush) { + this.flushAndError({ + message: 'Connection forcefully ended and command aborted.', + code: 'NR_CLOSED' + }) + } else if (arguments.length === 0) { + this.warn( + 'Using .end() without the flush parameter is deprecated and throws from v.3.0.0 on.\n' + 'Please check the documentation (https://github.com/NodeRedis/nodeRedis) and explicitly use flush.' - ); - } - // Clear retryTimer - if (this.retryTimer) { - clearTimeout(this.retryTimer); - this.retryTimer = null; - } - this.stream.removeAllListeners(); - this.stream.on('error', noop); - this.connected = false; - this.ready = false; - this.closing = true; - return this.stream.destroySoon(); -}; + ) + } + // Clear retryTimer + if (this.retryTimer) { + clearTimeout(this.retryTimer) + this.retryTimer = null + } + this.stream.removeAllListeners() + this.stream.on('error', noop) + this.connected = false + this.ready = false + this.closing = true + return this.stream.destroySoon() +} RedisClient.prototype.unref = function () { - if (this.connected) { - debug("Unref'ing the socket connection"); - this.stream.unref(); - } else { - debug('Not connected yet, will unref later'); - this.once('connect', function () { - this.unref(); - }); - } -}; + if (this.connected) { + debug('Unref\'ing the socket connection') + this.stream.unref() + } else { + debug('Not connected yet, will unref later') + this.once('connect', function () { + this.unref() + }) + } +} RedisClient.prototype.duplicate = function (options, callback) { - if (typeof options === 'function') { - callback = options; - options = null; + if (typeof options === 'function') { + callback = options + options = null + } + var existingOptions = utils.clone(this.options) + options = utils.clone(options) + for (var elem in options) { + existingOptions[elem] = options[elem] + } + var client = new RedisClient(existingOptions) + client.selectedDb = this.selectedDb + if (typeof callback === 'function') { + var readyListener = function () { + callback(null, client) + client.removeAllListeners(errorListener) } - var existingOptions = utils.clone(this.options); - options = utils.clone(options); - for (var elem in options) { - existingOptions[elem] = options[elem]; + var errorListener = function (err) { + callback(err) + client.end(true) } - var client = new RedisClient(existingOptions); - client.selectedDb = this.selectedDb; - if (typeof callback === 'function') { - var readyListener = function () { - callback(null, client); - client.removeAllListeners(errorListener); - }; - var errorListener = function (err) { - callback(err); - client.end(true); - }; - client.once('ready', readyListener); - client.once('error', errorListener); - return; - } - return client; -}; + client.once('ready', readyListener) + client.once('error', errorListener) + return + } + return client +} diff --git a/lib/individualCommands.js b/lib/individualCommands.js index a3f19d7b82..fd3cc42a35 100644 --- a/lib/individualCommands.js +++ b/lib/individualCommands.js @@ -1,12 +1,12 @@ -'use strict'; +'use strict' -var utils = require('./utils'); -var debug = require('./debug'); -var Multi = require('./multi'); -var Command = require('./command'); -var noPasswordIsSet = /no password is set/; -var loading = /LOADING/; -var RedisClient = require('../').RedisClient; +var utils = require('./utils') +var debug = require('./debug') +var Multi = require('./multi') +var Command = require('./command') +var noPasswordIsSet = /no password is set/ +var loading = /LOADING/ +var RedisClient = require('../').RedisClient /******************************************************************************************** Replace built-in redis functions @@ -22,596 +22,596 @@ var RedisClient = require('../').RedisClient; ********************************************************************************************/ RedisClient.prototype.multi = function multi (args) { - var multi = new Multi(this, args); - multi.exec = multi.EXEC = multi.execTransaction; - return multi; -}; + var multi = new Multi(this, args) + multi.exec = multi.EXEC = multi.execTransaction + return multi +} // ATTENTION: This is not a native function but is still handled as a individual command as it behaves just the same as multi RedisClient.prototype.batch = function batch (args) { - return new Multi(this, args); -}; + return new Multi(this, args) +} function selectCallback (self, db, callback) { - return function (err, res) { - if (err === null) { - // Store db in this.selectDb to restore it on reconnect - self.selectedDb = db; - } - utils.callbackOrEmit(self, callback, err, res); - }; + return function (err, res) { + if (err === null) { + // Store db in this.selectDb to restore it on reconnect + self.selectedDb = db + } + utils.callbackOrEmit(self, callback, err, res) + } } RedisClient.prototype.select = function select (db, callback) { - return this.internalSendCommand(new Command('select', [db], selectCallback(this, db, callback))); -}; + return this.internalSendCommand(new Command('select', [db], selectCallback(this, db, callback))) +} Multi.prototype.select = function select (db, callback) { - this.queue.push(new Command('select', [db], selectCallback(this._client, db, callback))); - return this; -}; + this.queue.push(new Command('select', [db], selectCallback(this._client, db, callback))) + return this +} RedisClient.prototype.monitor = RedisClient.prototype.MONITOR = function monitor (callback) { - // Use a individual command, as this is a special case that does not has to be checked for any other command - var self = this; - var callOnWrite = function () { - // Activating monitor mode has to happen before Redis returned the callback. 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. - self.monitoring = true; - }; - return this.internalSendCommand(new Command('monitor', [], callback, callOnWrite)); -}; + // Use a individual command, as this is a special case that does not has to be checked for any other command + var self = this + var callOnWrite = function () { + // Activating monitor mode has to happen before Redis returned the callback. 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. + self.monitoring = true + } + return this.internalSendCommand(new Command('monitor', [], callback, callOnWrite)) +} // Only works with batch, not in a transaction Multi.prototype.monitor = function monitor (callback) { - // Use a individual command, as this is a special case that does not has to be checked for any other command - if (this.exec !== this.execTransaction) { - var self = this; - var callOnWrite = function () { - self._client.monitoring = true; - }; - this.queue.push(new Command('monitor', [], callback, callOnWrite)); - return this; + // Use a individual command, as this is a special case that does not has to be checked for any other command + if (this.exec !== this.execTransaction) { + var self = this + var callOnWrite = function () { + self._client.monitoring = true } - // Set multi monitoring to indicate the exec that it should abort - // Remove this "hack" as soon as Redis might fix this - this.monitoring = true; - return this; -}; + this.queue.push(new Command('monitor', [], callback, callOnWrite)) + return this + } + // Set multi monitoring to indicate the exec that it should abort + // Remove this "hack" as soon as Redis might fix this + this.monitoring = true + return this +} function quitCallback (self, callback) { - return function (err, res) { - if (err && err.code === 'NR_CLOSED') { - // Pretend the quit command worked properly in this case. - // Either the quit landed in the offline queue and was flushed at the reconnect - // or the offline queue is deactivated and the command was rejected right away - // or the stream is not writable - // or while sending the quit, the connection ended / closed - err = null; - res = 'OK'; - } - utils.callbackOrEmit(self, callback, err, res); - if (self.stream.writable) { - // If the socket is still alive, kill it. This could happen if quit got a NR_CLOSED error code - self.stream.destroy(); - } - }; + return function (err, res) { + if (err && err.code === 'NR_CLOSED') { + // Pretend the quit command worked properly in this case. + // Either the quit landed in the offline queue and was flushed at the reconnect + // or the offline queue is deactivated and the command was rejected right away + // or the stream is not writable + // or while sending the quit, the connection ended / closed + err = null + res = 'OK' + } + utils.callbackOrEmit(self, callback, err, res) + if (self.stream.writable) { + // If the socket is still alive, kill it. This could happen if quit got a NR_CLOSED error code + self.stream.destroy() + } + } } RedisClient.prototype.quit = function quit (callback) { - // 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; - var backpressureIndicator = this.internalSendCommand(new Command('quit', [], quitCallback(this, callback))); - // Calling quit should always end the connection, no matter if there's a connection or not - this.closing = true; - this.ready = false; - return backpressureIndicator; -}; + // 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; + var backpressureIndicator = this.internalSendCommand(new Command('quit', [], quitCallback(this, callback))) + // Calling quit should always end the connection, no matter if there's a connection or not + this.closing = true + this.ready = false + return backpressureIndicator +} // Only works with batch, not in a transaction Multi.prototype.quit = function quit (callback) { - var self = this._client; - var callOnWrite = function () { - // If called in a multi context, we expect redis is available - self.closing = true; - self.ready = false; - }; - this.queue.push(new Command('quit', [], quitCallback(self, callback), callOnWrite)); - return this; -}; + var self = this._client + var callOnWrite = function () { + // If called in a multi context, we expect redis is available + self.closing = true + self.ready = false + } + this.queue.push(new Command('quit', [], quitCallback(self, callback), callOnWrite)) + return this +} function infoCallback (self, callback) { - return function (err, res) { - if (res) { - var obj = {}; - var lines = res.toString().split('\r\n'); - var line, parts, subParts; + return function (err, res) { + if (res) { + var obj = {} + var lines = res.toString().split('\r\n') + var line, parts, subParts - for (var i = 0; i < lines.length; i++) { - parts = lines[i].split(':'); - if (parts[1]) { - if (parts[0].indexOf('db') === 0) { - subParts = parts[1].split(','); - obj[parts[0]] = {}; - while (line = subParts.pop()) { - line = line.split('='); - obj[parts[0]][line[0]] = +line[1]; - } - } else { - obj[parts[0]] = parts[1]; - } - } + for (var i = 0; i < lines.length; i++) { + parts = lines[i].split(':') + if (parts[1]) { + if (parts[0].indexOf('db') === 0) { + subParts = parts[1].split(',') + obj[parts[0]] = {} + for (line = subParts.pop(); line !== undefined; line = subParts.pop()) { + line = line.split('=') + obj[parts[0]][line[0]] = +line[1] } - obj.versions = []; - if (obj.redis_version) { - obj.redis_version.split('.').forEach(function (num) { - obj.versions.push(+num); - }); - } - // Expose info key/values to users - self.serverInfo = obj; - } else { - self.serverInfo = {}; + } else { + obj[parts[0]] = parts[1] + } } - utils.callbackOrEmit(self, callback, err, res); - }; + } + obj.versions = [] + if (obj.redis_version) { + obj.redis_version.split('.').forEach(function (num) { + obj.versions.push(+num) + }) + } + // Expose info key/values to users + self.serverInfo = obj + } else { + self.serverInfo = {} + } + utils.callbackOrEmit(self, callback, err, res) + } } // Store info in this.serverInfo after each call RedisClient.prototype.info = function info (section, callback) { - var args = []; - if (typeof section === 'function') { - callback = section; - } else if (section !== undefined) { - args = Array.isArray(section) ? section : [section]; - } - return this.internalSendCommand(new Command('info', args, infoCallback(this, callback))); -}; + var args = [] + if (typeof section === 'function') { + callback = section + } else if (section !== undefined) { + args = Array.isArray(section) ? section : [section] + } + return this.internalSendCommand(new Command('info', args, infoCallback(this, callback))) +} Multi.prototype.info = function info (section, callback) { - var args = []; - if (typeof section === 'function') { - callback = section; - } else if (section !== undefined) { - args = Array.isArray(section) ? section : [section]; - } - this.queue.push(new Command('info', args, infoCallback(this._client, callback))); - return this; -}; + var args = [] + if (typeof section === 'function') { + callback = section + } else if (section !== undefined) { + args = Array.isArray(section) ? section : [section] + } + this.queue.push(new Command('info', args, infoCallback(this._client, callback))) + return this +} function authCallback (self, pass, callback) { - return function (err, res) { - if (err) { - if (noPasswordIsSet.test(err.message)) { - self.warn('Warning: Redis server does not require a password, but a password was supplied.'); - err = null; - res = 'OK'; - } else if (loading.test(err.message)) { - // If redis is still loading the db, it will not authenticate and everything else will fail - debug('Redis still loading, trying to authenticate later'); - setTimeout(function () { - self.auth(pass, callback); - }, 100); - return; - } - } - utils.callbackOrEmit(self, callback, err, res); - }; + return function (err, res) { + if (err) { + if (noPasswordIsSet.test(err.message)) { + self.warn('Warning: Redis server does not require a password, but a password was supplied.') + err = null + res = 'OK' + } else if (loading.test(err.message)) { + // If redis is still loading the db, it will not authenticate and everything else will fail + debug('Redis still loading, trying to authenticate later') + setTimeout(function () { + self.auth(pass, callback) + }, 100) + return + } + } + utils.callbackOrEmit(self, callback, err, res) + } } RedisClient.prototype.auth = function auth (pass, callback) { - debug('Sending auth to ' + this.address + ' id ' + this.connectionId); + debug('Sending auth to ' + this.address + ' id ' + this.connectionId) - // Stash auth for connect and reconnect. - this.authPass = pass; - var ready = this.ready; - this.ready = ready || this.offlineQueue.length === 0; - var tmp = this.internalSendCommand(new Command('auth', [pass], authCallback(this, pass, callback))); - this.ready = ready; - return tmp; -}; + // Stash auth for connect and reconnect. + this.authPass = pass + var ready = this.ready + this.ready = ready || this.offlineQueue.length === 0 + var tmp = this.internalSendCommand(new Command('auth', [pass], authCallback(this, pass, callback))) + this.ready = ready + return tmp +} // Only works with batch, not in a transaction Multi.prototype.auth = function auth (pass, callback) { - debug('Sending auth to ' + this.address + ' id ' + this.connectionId); + debug('Sending auth to ' + this.address + ' id ' + this.connectionId) - // Stash auth for connect and reconnect. - this.authPass = pass; - this.queue.push(new Command('auth', [pass], authCallback(this._client, callback))); - return this; -}; + // Stash auth for connect and reconnect. + this.authPass = pass + this.queue.push(new Command('auth', [pass], authCallback(this._client, callback))) + return this +} RedisClient.prototype.client = function client () { - var arr, - len = arguments.length, - callback, - i = 0; - if (Array.isArray(arguments[0])) { - arr = arguments[0]; - callback = arguments[1]; - } else if (Array.isArray(arguments[1])) { - if (len === 3) { - callback = arguments[2]; - } - len = arguments[1].length; - arr = new Array(len + 1); - arr[0] = arguments[0]; - for (; i < len; i += 1) { - arr[i + 1] = arguments[1][i]; - } - } else { - len = arguments.length; - // The later should not be the average use case - if (len !== 0 && (typeof arguments[len - 1] === 'function' || typeof arguments[len - 1] === 'undefined')) { - len--; - callback = arguments[len]; - } - arr = new Array(len); - for (; i < len; i += 1) { - arr[i] = arguments[i]; - } + var arr + var len = arguments.length + var callback + var i = 0 + if (Array.isArray(arguments[0])) { + arr = arguments[0] + callback = arguments[1] + } else if (Array.isArray(arguments[1])) { + if (len === 3) { + callback = arguments[2] } - var self = this; - var callOnWrite = undefined; - // CLIENT REPLY ON|OFF|SKIP - /* istanbul ignore next: TODO: Remove this as soon as Travis runs Redis 3.2 */ - if (arr.length === 2 && arr[0].toString().toUpperCase() === 'REPLY') { - var replyOnOff = arr[1].toString().toUpperCase(); - if (replyOnOff === 'ON' || replyOnOff === 'OFF' || replyOnOff === 'SKIP') { - callOnWrite = function () { - self.reply = replyOnOff; - }; - } + len = arguments[1].length + arr = new Array(len + 1) + arr[0] = arguments[0] + for (; i < len; i += 1) { + arr[i + 1] = arguments[1][i] } - return this.internalSendCommand(new Command('client', arr, callback, callOnWrite)); -}; + } else { + len = arguments.length + // The later should not be the average use case + if (len !== 0 && (typeof arguments[len - 1] === 'function' || typeof arguments[len - 1] === 'undefined')) { + len-- + callback = arguments[len] + } + arr = new Array(len) + for (; i < len; i += 1) { + arr[i] = arguments[i] + } + } + var self = this + var callOnWrite + // CLIENT REPLY ON|OFF|SKIP + /* istanbul ignore next: TODO: Remove this as soon as Travis runs Redis 3.2 */ + if (arr.length === 2 && arr[0].toString().toUpperCase() === 'REPLY') { + var replyOnOff = arr[1].toString().toUpperCase() + if (replyOnOff === 'ON' || replyOnOff === 'OFF' || replyOnOff === 'SKIP') { + callOnWrite = function () { + self.reply = replyOnOff + } + } + } + return this.internalSendCommand(new Command('client', arr, callback, callOnWrite)) +} Multi.prototype.client = function client () { - var arr, - len = arguments.length, - callback, - i = 0; - if (Array.isArray(arguments[0])) { - arr = arguments[0]; - callback = arguments[1]; - } else if (Array.isArray(arguments[1])) { - if (len === 3) { - callback = arguments[2]; - } - len = arguments[1].length; - arr = new Array(len + 1); - arr[0] = arguments[0]; - for (; i < len; i += 1) { - arr[i + 1] = arguments[1][i]; - } - } else { - len = arguments.length; - // The later should not be the average use case - if (len !== 0 && (typeof arguments[len - 1] === 'function' || typeof arguments[len - 1] === 'undefined')) { - len--; - callback = arguments[len]; - } - arr = new Array(len); - for (; i < len; i += 1) { - arr[i] = arguments[i]; - } + var arr + var len = arguments.length + var callback + var i = 0 + if (Array.isArray(arguments[0])) { + arr = arguments[0] + callback = arguments[1] + } else if (Array.isArray(arguments[1])) { + if (len === 3) { + callback = arguments[2] } - var self = this._client; - var callOnWrite = undefined; - // CLIENT REPLY ON|OFF|SKIP - /* istanbul ignore next: TODO: Remove this as soon as Travis runs Redis 3.2 */ - if (arr.length === 2 && arr[0].toString().toUpperCase() === 'REPLY') { - var replyOnOff = arr[1].toString().toUpperCase(); - if (replyOnOff === 'ON' || replyOnOff === 'OFF' || replyOnOff === 'SKIP') { - callOnWrite = function () { - self.reply = replyOnOff; - }; - } + len = arguments[1].length + arr = new Array(len + 1) + arr[0] = arguments[0] + for (; i < len; i += 1) { + arr[i + 1] = arguments[1][i] } - this.queue.push(new Command('client', arr, callback, callOnWrite)); - return this; -}; + } else { + len = arguments.length + // The later should not be the average use case + if (len !== 0 && (typeof arguments[len - 1] === 'function' || typeof arguments[len - 1] === 'undefined')) { + len-- + callback = arguments[len] + } + arr = new Array(len) + for (; i < len; i += 1) { + arr[i] = arguments[i] + } + } + var self = this._client + var callOnWrite + // CLIENT REPLY ON|OFF|SKIP + /* istanbul ignore next: TODO: Remove this as soon as Travis runs Redis 3.2 */ + if (arr.length === 2 && arr[0].toString().toUpperCase() === 'REPLY') { + var replyOnOff = arr[1].toString().toUpperCase() + if (replyOnOff === 'ON' || replyOnOff === 'OFF' || replyOnOff === 'SKIP') { + callOnWrite = function () { + self.reply = replyOnOff + } + } + } + this.queue.push(new Command('client', arr, callback, callOnWrite)) + return this +} RedisClient.prototype.hmset = function hmset () { - var arr, - len = arguments.length, - callback, - i = 0; - if (Array.isArray(arguments[0])) { - arr = arguments[0]; - callback = arguments[1]; - } else if (Array.isArray(arguments[1])) { - if (len === 3) { - callback = arguments[2]; - } - len = arguments[1].length; - arr = new Array(len + 1); - arr[0] = arguments[0]; - for (; i < len; i += 1) { - arr[i + 1] = arguments[1][i]; - } - } else if (typeof arguments[1] === 'object' && (arguments.length === 2 || arguments.length === 3 && (typeof arguments[2] === 'function' || typeof arguments[2] === 'undefined'))) { - arr = [arguments[0]]; - for (var field in arguments[1]) { - arr.push(field, arguments[1][field]); - } - callback = arguments[2]; - } else { - len = arguments.length; - // The later should not be the average use case - if (len !== 0 && (typeof arguments[len - 1] === 'function' || typeof arguments[len - 1] === 'undefined')) { - len--; - callback = arguments[len]; - } - arr = new Array(len); - for (; i < len; i += 1) { - arr[i] = arguments[i]; - } + var arr + var len = arguments.length + var callback + var i = 0 + if (Array.isArray(arguments[0])) { + arr = arguments[0] + callback = arguments[1] + } else if (Array.isArray(arguments[1])) { + if (len === 3) { + callback = arguments[2] } - return this.internalSendCommand(new Command('hmset', arr, callback)); -}; + len = arguments[1].length + arr = new Array(len + 1) + arr[0] = arguments[0] + for (; i < len; i += 1) { + arr[i + 1] = arguments[1][i] + } + } else if (typeof arguments[1] === 'object' && (arguments.length === 2 || (arguments.length === 3 && (typeof arguments[2] === 'function' || typeof arguments[2] === 'undefined')))) { + arr = [arguments[0]] + for (var field in arguments[1]) { + arr.push(field, arguments[1][field]) + } + callback = arguments[2] + } else { + len = arguments.length + // The later should not be the average use case + if (len !== 0 && (typeof arguments[len - 1] === 'function' || typeof arguments[len - 1] === 'undefined')) { + len-- + callback = arguments[len] + } + arr = new Array(len) + for (; i < len; i += 1) { + arr[i] = arguments[i] + } + } + return this.internalSendCommand(new Command('hmset', arr, callback)) +} Multi.prototype.hmset = function hmset () { - var arr, - len = arguments.length, - callback, - i = 0; - if (Array.isArray(arguments[0])) { - arr = arguments[0]; - callback = arguments[1]; - } else if (Array.isArray(arguments[1])) { - if (len === 3) { - callback = arguments[2]; - } - len = arguments[1].length; - arr = new Array(len + 1); - arr[0] = arguments[0]; - for (; i < len; i += 1) { - arr[i + 1] = arguments[1][i]; - } - } else if (typeof arguments[1] === 'object' && (arguments.length === 2 || arguments.length === 3 && (typeof arguments[2] === 'function' || typeof arguments[2] === 'undefined'))) { - arr = [arguments[0]]; - for (var field in arguments[1]) { - arr.push(field, arguments[1][field]); - } - callback = arguments[2]; - } else { - len = arguments.length; - // The later should not be the average use case - if (len !== 0 && (typeof arguments[len - 1] === 'function' || typeof arguments[len - 1] === 'undefined')) { - len--; - callback = arguments[len]; - } - arr = new Array(len); - for (; i < len; i += 1) { - arr[i] = arguments[i]; - } + var arr + var len = arguments.length + var callback + var i = 0 + if (Array.isArray(arguments[0])) { + arr = arguments[0] + callback = arguments[1] + } else if (Array.isArray(arguments[1])) { + if (len === 3) { + callback = arguments[2] } - this.queue.push(new Command('hmset', arr, callback)); - return this; -}; + len = arguments[1].length + arr = new Array(len + 1) + arr[0] = arguments[0] + for (; i < len; i += 1) { + arr[i + 1] = arguments[1][i] + } + } else if (typeof arguments[1] === 'object' && (arguments.length === 2 || (arguments.length === 3 && (typeof arguments[2] === 'function' || typeof arguments[2] === 'undefined')))) { + arr = [arguments[0]] + for (var field in arguments[1]) { + arr.push(field, arguments[1][field]) + } + callback = arguments[2] + } else { + len = arguments.length + // The later should not be the average use case + if (len !== 0 && (typeof arguments[len - 1] === 'function' || typeof arguments[len - 1] === 'undefined')) { + len-- + callback = arguments[len] + } + arr = new Array(len) + for (; i < len; i += 1) { + arr[i] = arguments[i] + } + } + this.queue.push(new Command('hmset', arr, callback)) + return this +} RedisClient.prototype.subscribe = function subscribe () { - var arr, - len = arguments.length, - callback, - i = 0; - if (Array.isArray(arguments[0])) { - arr = arguments[0].slice(0); - callback = arguments[1]; - } else { - len = arguments.length; - // The later should not be the average use case - if (len !== 0 && (typeof arguments[len - 1] === 'function' || typeof arguments[len - 1] === 'undefined')) { - len--; - callback = arguments[len]; - } - arr = new Array(len); - for (; i < len; i += 1) { - arr[i] = arguments[i]; - } + var arr + var len = arguments.length + var callback + var i = 0 + if (Array.isArray(arguments[0])) { + arr = arguments[0].slice(0) + callback = arguments[1] + } else { + len = arguments.length + // The later should not be the average use case + if (len !== 0 && (typeof arguments[len - 1] === 'function' || typeof arguments[len - 1] === 'undefined')) { + len-- + callback = arguments[len] } - var self = this; - var callOnWrite = function () { - self.pubSubMode = self.pubSubMode || self.commandQueue.length + 1; - }; - return this.internalSendCommand(new Command('subscribe', arr, callback, callOnWrite)); -}; + arr = new Array(len) + for (; i < len; i += 1) { + arr[i] = arguments[i] + } + } + var self = this + var callOnWrite = function () { + self.pubSubMode = self.pubSubMode || self.commandQueue.length + 1 + } + return this.internalSendCommand(new Command('subscribe', arr, callback, callOnWrite)) +} Multi.prototype.subscribe = function subscribe () { - var arr, - len = arguments.length, - callback, - i = 0; - if (Array.isArray(arguments[0])) { - arr = arguments[0].slice(0); - callback = arguments[1]; - } else { - len = arguments.length; - // The later should not be the average use case - if (len !== 0 && (typeof arguments[len - 1] === 'function' || typeof arguments[len - 1] === 'undefined')) { - len--; - callback = arguments[len]; - } - arr = new Array(len); - for (; i < len; i += 1) { - arr[i] = arguments[i]; - } + var arr + var len = arguments.length + var callback + var i = 0 + if (Array.isArray(arguments[0])) { + arr = arguments[0].slice(0) + callback = arguments[1] + } else { + len = arguments.length + // The later should not be the average use case + if (len !== 0 && (typeof arguments[len - 1] === 'function' || typeof arguments[len - 1] === 'undefined')) { + len-- + callback = arguments[len] } - var self = this._client; - var callOnWrite = function () { - self.pubSubMode = self.pubSubMode || self.commandQueue.length + 1; - }; - this.queue.push(new Command('subscribe', arr, callback, callOnWrite)); - return this; -}; + arr = new Array(len) + for (; i < len; i += 1) { + arr[i] = arguments[i] + } + } + var self = this._client + var callOnWrite = function () { + self.pubSubMode = self.pubSubMode || self.commandQueue.length + 1 + } + this.queue.push(new Command('subscribe', arr, callback, callOnWrite)) + return this +} RedisClient.prototype.unsubscribe = function unsubscribe () { - var arr, - len = arguments.length, - callback, - i = 0; - if (Array.isArray(arguments[0])) { - arr = arguments[0].slice(0); - callback = arguments[1]; - } else { - len = arguments.length; - // The later should not be the average use case - if (len !== 0 && (typeof arguments[len - 1] === 'function' || typeof arguments[len - 1] === 'undefined')) { - len--; - callback = arguments[len]; - } - arr = new Array(len); - for (; i < len; i += 1) { - arr[i] = arguments[i]; - } + var arr + var len = arguments.length + var callback + var i = 0 + if (Array.isArray(arguments[0])) { + arr = arguments[0].slice(0) + callback = arguments[1] + } else { + len = arguments.length + // The later should not be the average use case + if (len !== 0 && (typeof arguments[len - 1] === 'function' || typeof arguments[len - 1] === 'undefined')) { + len-- + callback = arguments[len] } - var self = this; - var callOnWrite = function () { - // Pub sub has to be activated even if not in pub sub mode, as the return value is manipulated in the callback - self.pubSubMode = self.pubSubMode || self.commandQueue.length + 1; - }; - return this.internalSendCommand(new Command('unsubscribe', arr, callback, callOnWrite)); -}; + arr = new Array(len) + for (; i < len; i += 1) { + arr[i] = arguments[i] + } + } + var self = this + var callOnWrite = function () { + // Pub sub has to be activated even if not in pub sub mode, as the return value is manipulated in the callback + self.pubSubMode = self.pubSubMode || self.commandQueue.length + 1 + } + return this.internalSendCommand(new Command('unsubscribe', arr, callback, callOnWrite)) +} Multi.prototype.unsubscribe = function unsubscribe () { - var arr, - len = arguments.length, - callback, - i = 0; - if (Array.isArray(arguments[0])) { - arr = arguments[0].slice(0); - callback = arguments[1]; - } else { - len = arguments.length; - // The later should not be the average use case - if (len !== 0 && (typeof arguments[len - 1] === 'function' || typeof arguments[len - 1] === 'undefined')) { - len--; - callback = arguments[len]; - } - arr = new Array(len); - for (; i < len; i += 1) { - arr[i] = arguments[i]; - } + var arr + var len = arguments.length + var callback + var i = 0 + if (Array.isArray(arguments[0])) { + arr = arguments[0].slice(0) + callback = arguments[1] + } else { + len = arguments.length + // The later should not be the average use case + if (len !== 0 && (typeof arguments[len - 1] === 'function' || typeof arguments[len - 1] === 'undefined')) { + len-- + callback = arguments[len] } - var self = this._client; - var callOnWrite = function () { - // Pub sub has to be activated even if not in pub sub mode, as the return value is manipulated in the callback - self.pubSubMode = self.pubSubMode || self.commandQueue.length + 1; - }; - this.queue.push(new Command('unsubscribe', arr, callback, callOnWrite)); - return this; -}; + arr = new Array(len) + for (; i < len; i += 1) { + arr[i] = arguments[i] + } + } + var self = this._client + var callOnWrite = function () { + // Pub sub has to be activated even if not in pub sub mode, as the return value is manipulated in the callback + self.pubSubMode = self.pubSubMode || self.commandQueue.length + 1 + } + this.queue.push(new Command('unsubscribe', arr, callback, callOnWrite)) + return this +} RedisClient.prototype.psubscribe = function psubscribe () { - var arr, - len = arguments.length, - callback, - i = 0; - if (Array.isArray(arguments[0])) { - arr = arguments[0].slice(0); - callback = arguments[1]; - } else { - len = arguments.length; - // The later should not be the average use case - if (len !== 0 && (typeof arguments[len - 1] === 'function' || typeof arguments[len - 1] === 'undefined')) { - len--; - callback = arguments[len]; - } - arr = new Array(len); - for (; i < len; i += 1) { - arr[i] = arguments[i]; - } + var arr + var len = arguments.length + var callback + var i = 0 + if (Array.isArray(arguments[0])) { + arr = arguments[0].slice(0) + callback = arguments[1] + } else { + len = arguments.length + // The later should not be the average use case + if (len !== 0 && (typeof arguments[len - 1] === 'function' || typeof arguments[len - 1] === 'undefined')) { + len-- + callback = arguments[len] } - var self = this; - var callOnWrite = function () { - self.pubSubMode = self.pubSubMode || self.commandQueue.length + 1; - }; - return this.internalSendCommand(new Command('psubscribe', arr, callback, callOnWrite)); -}; + arr = new Array(len) + for (; i < len; i += 1) { + arr[i] = arguments[i] + } + } + var self = this + var callOnWrite = function () { + self.pubSubMode = self.pubSubMode || self.commandQueue.length + 1 + } + return this.internalSendCommand(new Command('psubscribe', arr, callback, callOnWrite)) +} Multi.prototype.psubscribe = function psubscribe () { - var arr, - len = arguments.length, - callback, - i = 0; - if (Array.isArray(arguments[0])) { - arr = arguments[0].slice(0); - callback = arguments[1]; - } else { - len = arguments.length; - // The later should not be the average use case - if (len !== 0 && (typeof arguments[len - 1] === 'function' || typeof arguments[len - 1] === 'undefined')) { - len--; - callback = arguments[len]; - } - arr = new Array(len); - for (; i < len; i += 1) { - arr[i] = arguments[i]; - } + var arr + var len = arguments.length + var callback + var i = 0 + if (Array.isArray(arguments[0])) { + arr = arguments[0].slice(0) + callback = arguments[1] + } else { + len = arguments.length + // The later should not be the average use case + if (len !== 0 && (typeof arguments[len - 1] === 'function' || typeof arguments[len - 1] === 'undefined')) { + len-- + callback = arguments[len] } - var self = this._client; - var callOnWrite = function () { - self.pubSubMode = self.pubSubMode || self.commandQueue.length + 1; - }; - this.queue.push(new Command('psubscribe', arr, callback, callOnWrite)); - return this; -}; + arr = new Array(len) + for (; i < len; i += 1) { + arr[i] = arguments[i] + } + } + var self = this._client + var callOnWrite = function () { + self.pubSubMode = self.pubSubMode || self.commandQueue.length + 1 + } + this.queue.push(new Command('psubscribe', arr, callback, callOnWrite)) + return this +} RedisClient.prototype.punsubscribe = function punsubscribe () { - var arr, - len = arguments.length, - callback, - i = 0; - if (Array.isArray(arguments[0])) { - arr = arguments[0].slice(0); - callback = arguments[1]; - } else { - len = arguments.length; - // The later should not be the average use case - if (len !== 0 && (typeof arguments[len - 1] === 'function' || typeof arguments[len - 1] === 'undefined')) { - len--; - callback = arguments[len]; - } - arr = new Array(len); - for (; i < len; i += 1) { - arr[i] = arguments[i]; - } + var arr + var len = arguments.length + var callback + var i = 0 + if (Array.isArray(arguments[0])) { + arr = arguments[0].slice(0) + callback = arguments[1] + } else { + len = arguments.length + // The later should not be the average use case + if (len !== 0 && (typeof arguments[len - 1] === 'function' || typeof arguments[len - 1] === 'undefined')) { + len-- + callback = arguments[len] } - var self = this; - var callOnWrite = function () { - // Pub sub has to be activated even if not in pub sub mode, as the return value is manipulated in the callback - self.pubSubMode = self.pubSubMode || self.commandQueue.length + 1; - }; - return this.internalSendCommand(new Command('punsubscribe', arr, callback, callOnWrite)); -}; + arr = new Array(len) + for (; i < len; i += 1) { + arr[i] = arguments[i] + } + } + var self = this + var callOnWrite = function () { + // Pub sub has to be activated even if not in pub sub mode, as the return value is manipulated in the callback + self.pubSubMode = self.pubSubMode || self.commandQueue.length + 1 + } + return this.internalSendCommand(new Command('punsubscribe', arr, callback, callOnWrite)) +} Multi.prototype.punsubscribe = function punsubscribe () { - var arr, - len = arguments.length, - callback, - i = 0; - if (Array.isArray(arguments[0])) { - arr = arguments[0].slice(0); - callback = arguments[1]; - } else { - len = arguments.length; - // The later should not be the average use case - if (len !== 0 && (typeof arguments[len - 1] === 'function' || typeof arguments[len - 1] === 'undefined')) { - len--; - callback = arguments[len]; - } - arr = new Array(len); - for (; i < len; i += 1) { - arr[i] = arguments[i]; - } + var arr + var len = arguments.length + var callback + var i = 0 + if (Array.isArray(arguments[0])) { + arr = arguments[0].slice(0) + callback = arguments[1] + } else { + len = arguments.length + // The later should not be the average use case + if (len !== 0 && (typeof arguments[len - 1] === 'function' || typeof arguments[len - 1] === 'undefined')) { + len-- + callback = arguments[len] } - var self = this._client; - var callOnWrite = function () { - // Pub sub has to be activated even if not in pub sub mode, as the return value is manipulated in the callback - self.pubSubMode = self.pubSubMode || self.commandQueue.length + 1; - }; - this.queue.push(new Command('punsubscribe', arr, callback, callOnWrite)); - return this; -}; + arr = new Array(len) + for (; i < len; i += 1) { + arr[i] = arguments[i] + } + } + var self = this._client + var callOnWrite = function () { + // Pub sub has to be activated even if not in pub sub mode, as the return value is manipulated in the callback + self.pubSubMode = self.pubSubMode || self.commandQueue.length + 1 + } + this.queue.push(new Command('punsubscribe', arr, callback, callOnWrite)) + return this +} diff --git a/lib/multi.js b/lib/multi.js index 8ca621f6ee..14b3415f2a 100644 --- a/lib/multi.js +++ b/lib/multi.js @@ -1,187 +1,187 @@ -'use strict'; +'use strict' -var Queue = require('double-ended-queue'); -var utils = require('./utils'); -var Command = require('./command'); +var Queue = require('double-ended-queue') +var utils = require('./utils') +var Command = require('./command') function Multi (client, args) { - this._client = client; - this.queue = new Queue(); - var command, tmpArgs; - if (args) { // Either undefined or an array. Fail hard if it's not an array - for (var i = 0; i < args.length; i++) { - command = args[i][0]; - tmpArgs = args[i].slice(1); - if (Array.isArray(command)) { - this[command[0]].apply(this, command.slice(1).concat(tmpArgs)); - } else { - this[command].apply(this, tmpArgs); - } - } + this._client = client + this.queue = new Queue() + var command, tmpArgs + if (args) { // Either undefined or an array. Fail hard if it's not an array + for (var i = 0; i < args.length; i++) { + command = args[i][0] + tmpArgs = args[i].slice(1) + if (Array.isArray(command)) { + this[command[0]].apply(this, command.slice(1).concat(tmpArgs)) + } else { + this[command].apply(this, tmpArgs) + } } + } } function pipelineTransactionCommand (self, commandObj, index) { - // Queueing is done first, then the commands are executed - var tmp = commandObj.callback; - commandObj.callback = function (err, reply) { - // Ignore the multi command. This is applied by nodeRedis and the user does not benefit by it - if (err && index !== -1) { - if (tmp) { - tmp(err); - } - err.position = index; - self.errors.push(err); - } - // Keep track of who wants buffer responses: - // By the time the callback is called the commandObj got the bufferArgs attribute attached - self.wantsBuffers[index] = commandObj.bufferArgs; - commandObj.callback = tmp; - }; - self._client.internalSendCommand(commandObj); + // Queueing is done first, then the commands are executed + var tmp = commandObj.callback + commandObj.callback = function (err, reply) { + // Ignore the multi command. This is applied by nodeRedis and the user does not benefit by it + if (err && index !== -1) { + if (tmp) { + tmp(err) + } + err.position = index + self.errors.push(err) + } + // Keep track of who wants buffer responses: + // By the time the callback is called the commandObj got the bufferArgs attribute attached + self.wantsBuffers[index] = commandObj.bufferArgs + commandObj.callback = tmp + } + self._client.internalSendCommand(commandObj) } Multi.prototype.execAtomic = function execAtomic (callback) { - if (this.queue.length < 2) { - return this.execBatch(callback); - } - return this.exec(callback); -}; + if (this.queue.length < 2) { + return this.execBatch(callback) + } + return this.exec(callback) +} function multiCallback (self, err, replies) { - var i = 0, commandObj; - - if (err) { - err.errors = self.errors; - if (self.callback) { - self.callback(err); - // Exclude connection errors so that those errors won't be emitted twice - } else if (err.code !== 'CONNECTION_BROKEN') { - self._client.emit('error', err); - } - return; - } - - if (replies) { - while (commandObj = self.queue.shift()) { - if (replies[i] instanceof Error) { - var match = replies[i].message.match(utils.errCode); - // LUA script could return user errors that don't behave like all other errors! - if (match) { - replies[i].code = match[1]; - } - replies[i].command = commandObj.command.toUpperCase(); - if (typeof commandObj.callback === 'function') { - commandObj.callback(replies[i]); - } - } else { - // If we asked for strings, even in detectBuffers mode, then return strings: - replies[i] = self._client.handleReply(replies[i], commandObj.command, self.wantsBuffers[i]); - if (typeof commandObj.callback === 'function') { - commandObj.callback(null, replies[i]); - } - } - i++; - } - } + var i = 0 + if (err) { + err.errors = self.errors if (self.callback) { - self.callback(null, replies); + self.callback(err) + // Exclude connection errors so that those errors won't be emitted twice + } else if (err.code !== 'CONNECTION_BROKEN') { + self._client.emit('error', err) } + return + } + + if (replies) { + for (var commandObj = self.queue.shift(); commandObj !== undefined; commandObj = self.queue.shift()) { + if (replies[i] instanceof Error) { + var match = replies[i].message.match(utils.errCode) + // LUA script could return user errors that don't behave like all other errors! + if (match) { + replies[i].code = match[1] + } + replies[i].command = commandObj.command.toUpperCase() + if (typeof commandObj.callback === 'function') { + commandObj.callback(replies[i]) + } + } else { + // If we asked for strings, even in detectBuffers mode, then return strings: + replies[i] = self._client.handleReply(replies[i], commandObj.command, self.wantsBuffers[i]) + if (typeof commandObj.callback === 'function') { + commandObj.callback(null, replies[i]) + } + } + i++ + } + } + + if (self.callback) { + self.callback(null, replies) + } } Multi.prototype.execTransaction = function execTransaction (callback) { - if (this.monitoring || this._client.monitoring) { - var err = new RangeError( - 'Using transaction with a client that is in monitor mode does not work due to faulty return values of Redis.' - ); - err.command = 'EXEC'; - err.code = 'EXECABORT'; - return utils.replyInOrder(this._client, callback, err); - } - var self = this; - var len = self.queue.length; - self.errors = []; - self.callback = callback; - self._client.cork(); - self.wantsBuffers = new Array(len); - pipelineTransactionCommand(self, new Command('multi', []), -1); - // Drain queue, callback will catch 'QUEUED' or error - for (var index = 0; index < len; index++) { - // The commands may not be shifted off, since they are needed in the result handler - pipelineTransactionCommand(self, self.queue.get(index), index); - } + if (this.monitoring || this._client.monitoring) { + var err = new RangeError( + 'Using transaction with a client that is in monitor mode does not work due to faulty return values of Redis.' + ) + err.command = 'EXEC' + err.code = 'EXECABORT' + return utils.replyInOrder(this._client, callback, err) + } + var self = this + var len = self.queue.length + self.errors = [] + self.callback = callback + self._client.cork() + self.wantsBuffers = new Array(len) + pipelineTransactionCommand(self, new Command('multi', []), -1) + // Drain queue, callback will catch 'QUEUED' or error + for (var index = 0; index < len; index++) { + // The commands may not be shifted off, since they are needed in the result handler + pipelineTransactionCommand(self, self.queue.get(index), index) + } - self._client.internalSendCommand(new Command('exec', [], function (err, replies) { - multiCallback(self, err, replies); - })); - self._client.uncork(); - return !self._client.shouldBuffer; -}; + self._client.internalSendCommand(new Command('exec', [], function (err, replies) { + multiCallback(self, err, replies) + })) + self._client.uncork() + return !self._client.shouldBuffer +} function batchCallback (self, cb, i) { - return function batchCallback (err, res) { - if (err) { - self.results[i] = err; - // Add the position to the error - self.results[i].position = i; - } else { - self.results[i] = res; - } - cb(err, res); - }; + return function batchCallback (err, res) { + if (err) { + self.results[i] = err + // Add the position to the error + self.results[i].position = i + } else { + self.results[i] = res + } + cb(err, res) + } } Multi.prototype.exec = Multi.prototype.execBatch = function execBatch (callback) { - var self = this; - var len = self.queue.length; - var index = 0; - var commandObj; - if (len === 0) { - utils.replyInOrder(self._client, callback, null, []); - return !self._client.shouldBuffer; + var self = this + var len = self.queue.length + var index = 0 + var commandObj + if (len === 0) { + utils.replyInOrder(self._client, callback, null, []) + return !self._client.shouldBuffer + } + self._client.cork() + if (!callback) { + for (commandObj = self.queue.shift(); commandObj !== undefined; commandObj = self.queue.shift()) { + self._client.internalSendCommand(commandObj) } - self._client.cork(); - if (!callback) { - while (commandObj = self.queue.shift()) { - self._client.internalSendCommand(commandObj); - } - self._client.uncork(); - return !self._client.shouldBuffer; + self._client.uncork() + return !self._client.shouldBuffer + } + var callbackWithoutOwnCb = function (err, res) { + if (err) { + self.results.push(err) + // Add the position to the error + var i = self.results.length - 1 + self.results[i].position = i + } else { + self.results.push(res) } - var callbackWithoutOwnCb = function (err, res) { - if (err) { - self.results.push(err); - // Add the position to the error - var i = self.results.length - 1; - self.results[i].position = i; - } else { - self.results.push(res); - } - // Do not emit an error here. Otherwise each error would result in one emit. - // The errors will be returned in the result anyway - }; - var lastCallback = function (cb) { - return function (err, res) { - cb(err, res); - callback(null, self.results); - }; - }; - self.results = []; - while (commandObj = self.queue.shift()) { - if (typeof commandObj.callback === 'function') { - commandObj.callback = batchCallback(self, commandObj.callback, index); - } else { - commandObj.callback = callbackWithoutOwnCb; - } - if (typeof callback === 'function' && index === len - 1) { - commandObj.callback = lastCallback(commandObj.callback); - } - this._client.internalSendCommand(commandObj); - index++; + // Do not emit an error here. Otherwise each error would result in one emit. + // The errors will be returned in the result anyway + } + var lastCallback = function (cb) { + return function (err, res) { + cb(err, res) + callback(null, self.results) } - self._client.uncork(); - return !self._client.shouldBuffer; -}; + } + self.results = [] + for (commandObj = self.queue.shift(); commandObj !== undefined; commandObj = self.queue.shift()) { + if (typeof commandObj.callback === 'function') { + commandObj.callback = batchCallback(self, commandObj.callback, index) + } else { + commandObj.callback = callbackWithoutOwnCb + } + if (typeof callback === 'function' && index === len - 1) { + commandObj.callback = lastCallback(commandObj.callback) + } + this._client.internalSendCommand(commandObj) + index++ + } + self._client.uncork() + return !self._client.shouldBuffer +} -module.exports = Multi; +module.exports = Multi diff --git a/lib/utils.js b/lib/utils.js index 090e9474ac..1b7b914a9f 100644 --- a/lib/utils.js +++ b/lib/utils.js @@ -1,105 +1,104 @@ -'use strict'; +'use strict' // hgetall converts its replies to an Object. If the reply is empty, null is returned. // These function are only called with internal data and have therefore always the same instanceof X function replyToObject (reply) { - // The reply might be a string or a buffer if this is called in a transaction (multi) - if (reply.length === 0 || !(reply instanceof Array)) { - return null; - } - var obj = {}; - for (var i = 0; i < reply.length; i += 2) { - obj[reply[i].toString('binary')] = reply[i + 1]; - } - return obj; + // The reply might be a string or a buffer if this is called in a transaction (multi) + if (reply.length === 0 || !(reply instanceof Array)) { + return null + } + var obj = {} + for (var i = 0; i < reply.length; i += 2) { + obj[reply[i].toString('binary')] = reply[i + 1] + } + return obj } function replyToStrings (reply) { - if (reply instanceof Buffer) { - return reply.toString(); - } - if (reply instanceof Array) { - var res = new Array(reply.length); - for (var i = 0; i < reply.length; i++) { - // Recursively call the function as slowlog returns deep nested replies - res[i] = replyToStrings(reply[i]); - } - return res; + if (reply instanceof Buffer) { + return reply.toString() + } + if (reply instanceof Array) { + var res = new Array(reply.length) + for (var i = 0; i < reply.length; i++) { + // Recursively call the function as slowlog returns deep nested replies + res[i] = replyToStrings(reply[i]) } + return res + } - return reply; + return reply } // Deep clone arbitrary objects with arrays. Can't handle cyclic structures (results in a range error) // Any attribute with a non primitive value besides object and array will be passed by reference (e.g. Buffers, Maps, Functions) function clone (obj) { - var copy; - if (Array.isArray(obj)) { - copy = new Array(obj.length); - for (var i = 0; i < obj.length; i++) { - copy[i] = clone(obj[i]); - } - return copy; + var copy + if (Array.isArray(obj)) { + copy = new Array(obj.length) + for (var i = 0; i < obj.length; i++) { + copy[i] = clone(obj[i]) } - if (Object.prototype.toString.call(obj) === '[object Object]') { - copy = {}; - var elements = Object.keys(obj); - var elem; - while (elem = elements.pop()) { - copy[elem] = clone(obj[elem]); - } - return copy; + return copy + } + if (Object.prototype.toString.call(obj) === '[object Object]') { + copy = {} + var elements = Object.keys(obj) + for (var elem = elements.pop(); elem !== undefined; elem = elements.pop()) { + copy[elem] = clone(obj[elem]) } - return obj; + return copy + } + return obj } function convenienceClone (obj) { - return clone(obj) || {}; + return clone(obj) || {} } function callbackOrEmit (self, callback, err, res) { - if (callback) { - callback(err, res); - } else if (err) { - self.emit('error', err); - } + if (callback) { + callback(err, res) + } else if (err) { + self.emit('error', err) + } } function replyInOrder (self, callback, err, res, queue) { - // If the queue is explicitly passed, use that, otherwise fall back to the offline queue first, - // as there might be commands in both queues at the same time - var commandObj; - if (queue) { - commandObj = queue.peekBack(); - } else { - commandObj = self.offlineQueue.peekBack() || self.commandQueue.peekBack(); - } - if (!commandObj) { - process.nextTick(function () { - callbackOrEmit(self, callback, err, res); - }); - } else { - var tmp = commandObj.callback; - commandObj.callback = tmp ? - function (e, r) { - tmp(e, r); - callbackOrEmit(self, callback, err, res); - } : - function (e, r) { - if (e) { - self.emit('error', e); - } - callbackOrEmit(self, callback, err, res); - }; - } + // If the queue is explicitly passed, use that, otherwise fall back to the offline queue first, + // as there might be commands in both queues at the same time + var commandObj + if (queue) { + commandObj = queue.peekBack() + } else { + commandObj = self.offlineQueue.peekBack() || self.commandQueue.peekBack() + } + if (!commandObj) { + process.nextTick(function () { + callbackOrEmit(self, callback, err, res) + }) + } else { + var tmp = commandObj.callback + commandObj.callback = tmp + ? function (e, r) { + tmp(e, r) + callbackOrEmit(self, callback, err, res) + } + : function (e) { + if (e) { + self.emit('error', e) + } + callbackOrEmit(self, callback, err, res) + } + } } module.exports = { - replyToStrings: replyToStrings, - replyToObject: replyToObject, - errCode: /^([A-Z]+)\s+(.+)$/, - monitorRegex: /^[0-9]{10,11}\.[0-9]+ \[[0-9]+ .+\]( ".+?")+$/, - clone: convenienceClone, - callbackOrEmit: callbackOrEmit, - replyInOrder: replyInOrder -}; + replyToStrings: replyToStrings, + replyToObject: replyToObject, + errCode: /^([A-Z]+)\s+(.+)$/, + monitorRegex: /^[0-9]{10,11}\.[0-9]+ \[[0-9]+ .+]( ".+?")+$/, + clone: convenienceClone, + callbackOrEmit: callbackOrEmit, + replyInOrder: replyInOrder +} diff --git a/package.json b/package.json index 7a70f4ffd3..c3266d5a7c 100644 --- a/package.json +++ b/package.json @@ -23,12 +23,13 @@ "test": "nyc --cache mocha ./test/*.js ./test/commands/*.js --timeout=8000", "posttest": "npm run coverage", "compare": "node benchmarks/diff_multi_bench_output.js beforeBench.txt afterBench.txt", - "lint": "standard . --fix" + "lint": "standard --fix" }, "dependencies": { "double-ended-queue": "^2.1.0-0", "redis-commands": "^1.2.0", - "redis-parser": "^2.6.0" + "redis-parser": "^2.6.0", + "safe-buffer": "^5.0.1" }, "engines": { "node": ">=6" @@ -49,6 +50,11 @@ "type": "git", "url": "git://github.com/NodeRedis/node_redis.git" }, + "standard": { + "envs": [ + "mocha" + ] + }, "bugs": { "url": "https://github.com/NodeRedis/node_redis/issues" }, diff --git a/test/auth.spec.js b/test/auth.spec.js index 5205d6f508..6b3b32b7ec 100644 --- a/test/auth.spec.js +++ b/test/auth.spec.js @@ -1,347 +1,351 @@ -'use strict'; +'use strict' -var assert = require('assert'); -var config = require('./lib/config'); -var helper = require('./helper'); -var redis = config.redis; +var assert = require('assert') +var config = require('./lib/config') +var helper = require('./helper') +var redis = config.redis -if (process.platform === 'win32') { - // TODO: Fix redis process spawn on windows - return; -} - -describe('client authentication', function () { + // TODO: Fix redis process spawn on windows +if (process.platform !== 'win32') { + describe('client authentication', function () { before(function (done) { - helper.stopRedis(function () { - helper.startRedis('./conf/password.conf', done); - }); - }); + helper.stopRedis(function () { + helper.startRedis('./conf/password.conf', done) + }) + }) helper.allTests({ - allConnections: true + allConnections: true }, function (ip, args) { + describe('using ' + ip, function () { + var auth = 'porkchopsandwiches' + var client = null - describe('using ' + ip, function () { - var auth = 'porkchopsandwiches'; - var client = null; + beforeEach(function () { + client = null + }) + afterEach(function () { + // Explicitly ignore still running commands + // The ready command could still be running + client.end(false) + }) - beforeEach(function () { - client = null; - }); - afterEach(function () { - // Explicitly ignore still running commands - // The ready command could still be running - client.end(false); - }); + it('allows auth to be provided with \'auth\' method', function (done) { + if (helper.redisProcess().spawnFailed()) this.skip() - it("allows auth to be provided with 'auth' method", function (done) { - if (helper.redisProcess().spawnFailed()) this.skip(); + client = redis.createClient.apply(null, args) + client.auth(auth, function (err, res) { + assert.strictEqual(null, err) + assert.strictEqual('OK', res.toString()) + return done(err) + }) + }) - client = redis.createClient.apply(null, args); - client.auth(auth, function (err, res) { - assert.strictEqual(null, err); - assert.strictEqual('OK', res.toString()); - return done(err); - }); - }); + it('support redis 2.4 with retrying auth commands if still loading', function (done) { + if (helper.redisProcess().spawnFailed()) this.skip() - it('support redis 2.4 with retrying auth commands if still loading', function (done) { - if (helper.redisProcess().spawnFailed()) this.skip(); - - client = redis.createClient.apply(null, args); - var time = Date.now(); - client.auth(auth, function (err, res) { - assert.strictEqual('retry worked', res); - var now = Date.now(); - // Hint: setTimeout sometimes triggers early and therefore the value can be like one or two ms to early - assert(now - time >= 98, 'Time should be above 100 ms (the reconnect time) and is ' + (now - time)); - assert(now - time < 225, 'Time should be below 255 ms (the reconnect should only take a bit above 100 ms) and is ' + (now - time)); - done(); - }); - var tmp = client.commandQueue.get(0).callback; - client.commandQueue.get(0).callback = function (err, res) { - client.auth = function (pass, callback) { - callback(null, 'retry worked'); - }; - tmp(new Error('ERR redis is still LOADING')); - }; - }); - - it('emits error when auth is bad without callback', function (done) { - if (helper.redisProcess().spawnFailed()) this.skip(); - - client = redis.createClient.apply(null, args); - - client.once('error', function (err) { - assert.strictEqual(err.command, 'AUTH'); - assert.ok(/ERR invalid password/.test(err.message)); - return done(); - }); - - client.auth(auth + 'bad'); - }); - - it('returns an error when auth is bad (empty string) with a callback', function (done) { - if (helper.redisProcess().spawnFailed()) this.skip(); - - client = redis.createClient.apply(null, args); - - client.auth('', function (err, res) { - assert.strictEqual(err.command, 'AUTH'); - assert.ok(/ERR invalid password/.test(err.message)); - done(); - }); - }); - - if (ip === 'IPv4') { - it('allows auth to be provided as part of redis url and do not fire commands before auth is done', function (done) { - if (helper.redisProcess().spawnFailed()) this.skip(); - - var end = helper.callFuncAfter(done, 2); - client = redis.createClient('redis://:' + auth + '@' + config.HOST[ip] + ':' + config.PORT); - client.on('ready', function () { - end(); - }); - // The info command may be used while loading but not if not yet authenticated - client.info(function (err, res) { - assert(!err); - end(); - }); - }); - - it('allows auth and database to be provided as part of redis url query parameter', function (done) { - if (helper.redisProcess().spawnFailed()) this.skip(); - - client = redis.createClient('redis://' + config.HOST[ip] + ':' + config.PORT + '?db=2&password=' + auth); - assert.strictEqual(client.options.db, '2'); - assert.strictEqual(client.options.password, auth); - assert.strictEqual(client.authPass, auth); - client.on('ready', function () { - // Set a key so the used database is returned in the info command - client.set('foo', 'bar'); - client.get('foo'); - assert.strictEqual(client.serverInfo.db2, undefined); - // Using the info command should update the serverInfo - client.info(function (err, res) { - assert(typeof client.serverInfo.db2 === 'object'); - }); - client.flushdb(done); - }); - }); + client = redis.createClient.apply(null, args) + var time = Date.now() + client.auth(auth, function (err, res) { + assert.strictEqual(err, null) + assert.strictEqual('retry worked', res) + var now = Date.now() + // Hint: setTimeout sometimes triggers early and therefore the value can be like one or two ms to early + assert(now - time >= 98, 'Time should be above 100 ms (the reconnect time) and is ' + (now - time)) + assert(now - time < 225, 'Time should be below 255 ms (the reconnect should only take a bit above 100 ms) and is ' + (now - time)) + done() + }) + var tmp = client.commandQueue.get(0).callback + client.commandQueue.get(0).callback = function (err) { + assert.strictEqual(err, null) + client.auth = function (pass, callback) { + callback(null, 'retry worked') } + tmp(new Error('ERR redis is still LOADING')) + } + }) - it('allows auth to be provided as config option for client', function (done) { - if (helper.redisProcess().spawnFailed()) this.skip(); + it('emits error when auth is bad without callback', function (done) { + if (helper.redisProcess().spawnFailed()) this.skip() - var args = config.configureClient(ip, { - authPass: auth - }); - client = redis.createClient.apply(null, args); - client.on('ready', done); - }); + client = redis.createClient.apply(null, args) - it('allows auth and noReadyCheck to be provided as config option for client', function (done) { - if (helper.redisProcess().spawnFailed()) this.skip(); + client.once('error', function (err) { + assert.strictEqual(err.command, 'AUTH') + assert.ok(/ERR invalid password/.test(err.message)) + return done() + }) - var args = config.configureClient(ip, { - password: auth, - noReadyCheck: true - }); - client = redis.createClient.apply(null, args); - client.on('ready', done); - }); + client.auth(auth + 'bad') + }) - it('allows auth to be provided post-hoc with auth method', function (done) { - if (helper.redisProcess().spawnFailed()) this.skip(); + it('returns an error when auth is bad (empty string) with a callback', function (done) { + if (helper.redisProcess().spawnFailed()) this.skip() - var args = config.configureClient(ip); - client = redis.createClient.apply(null, args); - client.auth(auth); - client.on('ready', done); - }); + client = redis.createClient.apply(null, args) - it('reconnects with appropriate authentication while offline commands are present', function (done) { - if (helper.redisProcess().spawnFailed()) this.skip(); + client.auth('', function (err) { + assert.strictEqual(err.command, 'AUTH') + assert.ok(/ERR invalid password/.test(err.message)) + done() + }) + }) - client = redis.createClient.apply(null, args); - client.auth(auth); - client.on('ready', function () { - if (this.timesConnected < 3) { - var interval = setInterval(function () { - if (client.commandQueue.length !== 0) { - return; - } - clearInterval(interval); - interval = null; - client.stream.destroy(); - client.set('foo', 'bar'); - client.get('foo'); // Errors would bubble - assert.strictEqual(client.offlineQueue.length, 2); - }, 1); - } else { - done(); - } - }); - client.on('reconnecting', function (params) { - assert.strictEqual(params.error, null); - }); - }); + if (ip === 'IPv4') { + it('allows auth to be provided as part of redis url and do not fire commands before auth is done', function (done) { + if (helper.redisProcess().spawnFailed()) this.skip() - it('should return an error if the password is not correct and a callback has been provided', function (done) { - if (helper.redisProcess().spawnFailed()) this.skip(); + var end = helper.callFuncAfter(done, 2) + client = redis.createClient('redis://:' + auth + '@' + config.HOST[ip] + ':' + config.PORT) + client.on('ready', function () { + end() + }) + // The info command may be used while loading but not if not yet authenticated + client.info(function (err) { + assert.strictEqual(err, null) + end(err) + }) + }) - client = redis.createClient.apply(null, args); - var async = true; - client.auth(undefined, function (err, res) { - assert.strictEqual(err.message, 'ERR invalid password'); - assert.strictEqual(err.command, 'AUTH'); - assert.strictEqual(res, undefined); - async = false; - done(); - }); - assert(async); - }); + it('allows auth and database to be provided as part of redis url query parameter', function (done) { + if (helper.redisProcess().spawnFailed()) this.skip() - it('should emit an error if the password is not correct and no callback has been provided', function (done) { - if (helper.redisProcess().spawnFailed()) this.skip(); + client = redis.createClient('redis://' + config.HOST[ip] + ':' + config.PORT + '?db=2&password=' + auth) + assert.strictEqual(client.options.db, '2') + assert.strictEqual(client.options.password, auth) + assert.strictEqual(client.authPass, auth) + client.on('ready', function () { + // Set a key so the used database is returned in the info command + client.set('foo', 'bar') + client.get('foo') + assert.strictEqual(client.serverInfo.db2, undefined) + // Using the info command should update the serverInfo + client.info(function (err) { + assert.strictEqual(err, null) + assert(typeof client.serverInfo.db2 === 'object') + }) + client.flushdb(done) + }) + }) + } - client = redis.createClient.apply(null, args); - client.on('error', function (err) { - assert.strictEqual(err.message, 'ERR invalid password'); - assert.strictEqual(err.command, 'AUTH'); - done(); - }); - client.auth(234567); - }); + it('allows auth to be provided as config option for client', function (done) { + if (helper.redisProcess().spawnFailed()) this.skip() - it('allows auth to be provided post-hoc with auth method again', function (done) { - if (helper.redisProcess().spawnFailed()) this.skip(); + var args = config.configureClient(ip, { + authPass: auth + }) + client = redis.createClient.apply(null, args) + client.on('ready', done) + }) - var args = config.configureClient(ip, { - authPass: auth - }); - client = redis.createClient.apply(null, args); - client.on('ready', function () { - client.auth(auth, helper.isString('OK', done)); - }); - }); + it('allows auth and noReadyCheck to be provided as config option for client', function (done) { + if (helper.redisProcess().spawnFailed()) this.skip() - it('does not allow any commands to be processed if not authenticated using noReadyCheck true', function (done) { - if (helper.redisProcess().spawnFailed()) this.skip(); + var args = config.configureClient(ip, { + password: auth, + noReadyCheck: true + }) + client = redis.createClient.apply(null, args) + client.on('ready', done) + }) - var args = config.configureClient(ip, { - noReadyCheck: true - }); - client = redis.createClient.apply(null, args); - client.on('ready', function () { - client.set('foo', 'bar', function (err, res) { - assert.equal(err.message, 'NOAUTH Authentication required.'); - assert.equal(err.code, 'NOAUTH'); - assert.equal(err.command, 'SET'); - done(); - }); - }); - }); + it('allows auth to be provided post-hoc with auth method', function (done) { + if (helper.redisProcess().spawnFailed()) this.skip() - it('does not allow auth to be provided post-hoc with auth method if not authenticated before', function (done) { - if (helper.redisProcess().spawnFailed()) this.skip(); - client = redis.createClient.apply(null, args); - client.on('error', function (err) { - assert.equal(err.code, 'NOAUTH'); - assert.equal(err.message, 'Ready check failed: NOAUTH Authentication required.'); - assert.equal(err.command, 'INFO'); - done(); - }); - }); + var args = config.configureClient(ip) + client = redis.createClient.apply(null, args) + client.auth(auth) + client.on('ready', done) + }) - it('should emit an error if the provided password is faulty', function (done) { - if (helper.redisProcess().spawnFailed()) this.skip(); - client = redis.createClient({ - password: 'wrongPassword' - }); - client.once('error', function (err) { - assert.strictEqual(err.message, 'ERR invalid password'); - done(); - }); - }); + it('reconnects with appropriate authentication while offline commands are present', function (done) { + if (helper.redisProcess().spawnFailed()) this.skip() - it('pubsub working with auth', function (done) { - if (helper.redisProcess().spawnFailed()) this.skip(); + client = redis.createClient.apply(null, args) + client.auth(auth) + client.on('ready', function () { + if (this.timesConnected < 3) { + var interval = setInterval(function () { + if (client.commandQueue.length !== 0) { + return + } + clearInterval(interval) + interval = null + client.stream.destroy() + client.set('foo', 'bar') + client.get('foo') // Errors would bubble + assert.strictEqual(client.offlineQueue.length, 2) + }, 1) + } else { + done() + } + }) + client.on('reconnecting', function (params) { + assert.strictEqual(params.error, null) + }) + }) - var args = config.configureClient(ip, { - password: auth - }); - client = redis.createClient.apply(null, args); - client.set('foo', 'bar'); - client.subscribe('somechannel', 'another channel', function (err, res) { - client.once('ready', function () { - assert.strictEqual(client.pubSubMode, 1); - client.get('foo', function (err, res) { - assert(/ERR only \(P\)SUBSCRIBE \/ \(P\)UNSUBSCRIBE/.test(err.message)); - done(); - }); - }); - }); - client.once('ready', function () { - // Coherent behavior with all other offline commands fires commands before emitting but does not wait till they return - assert.strictEqual(client.pubSubMode, 2); - client.ping(function () { // Make sure all commands were properly processed already - client.stream.destroy(); - }); - }); - }); + it('should return an error if the password is not correct and a callback has been provided', function (done) { + if (helper.redisProcess().spawnFailed()) this.skip() - it('individual commands work properly with batch', function (done) { - // quit => might return an error instead of "OK" in the exec callback... (if not connected) - // auth => might return an error instead of "OK" in the exec callback... (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 + client = redis.createClient.apply(null, args) + var async = true + client.auth(undefined, function (err, res) { + assert.strictEqual(err.message, 'ERR invalid password') + assert.strictEqual(err.command, 'AUTH') + assert.strictEqual(res, undefined) + async = false + done() + }) + assert(async) + }) - var args = config.configureClient('localhost', { - noReadyCheck: true - }); - client = redis.createClient.apply(null, args); - assert.strictEqual(client.selectedDb, undefined); - var end = helper.callFuncAfter(done, 8); - client.on('monitor', function () { - end(); // Should be called for each command after monitor - }); - client.batch() - .auth(auth) - .select(5, function (err, res) { - assert.strictEqual(client.selectedDb, 5); - assert.strictEqual(res, 'OK'); - assert.notDeepEqual(client.serverInfo.db5, { avgTtl: 0, expires: 0, keys: 1 }); - }) - .monitor() - .set('foo', 'bar', helper.isString('OK')) - .info('stats', function (err, res) { - assert.strictEqual(res.indexOf('# Stats\r\n'), 0); - assert.strictEqual(client.serverInfo.sync_full, '0'); - }) - .get('foo', helper.isString('bar')) - .subscribe(['foo', 'bar', 'foo'], helper.isUnSubscribe(2, ['foo', 'bar', 'foo'])) - .unsubscribe('foo') - .subscribe('/foo', helper.isUnSubscribe(2, '/foo')) - .psubscribe('*') - .quit(helper.isString('OK')) - .exec(function (err, res) { - res[4] = res[4].substr(0, 9); - assert.deepStrictEqual( - res, - ['OK', 'OK', 'OK', 'OK', '# Stats\r\n', 'bar', [2, ['foo', 'bar', 'foo']], [1, ['foo']], [2, ['/foo']], [3, ['*']], 'OK'] - ); - end(); - }); - }); - }); - }); + it('should emit an error if the password is not correct and no callback has been provided', function (done) { + if (helper.redisProcess().spawnFailed()) this.skip() + + client = redis.createClient.apply(null, args) + client.on('error', function (err) { + assert.strictEqual(err.message, 'ERR invalid password') + assert.strictEqual(err.command, 'AUTH') + done() + }) + client.auth(234567) + }) + + it('allows auth to be provided post-hoc with auth method again', function (done) { + if (helper.redisProcess().spawnFailed()) this.skip() + + var args = config.configureClient(ip, { + authPass: auth + }) + client = redis.createClient.apply(null, args) + client.on('ready', function () { + client.auth(auth, helper.isString('OK', done)) + }) + }) + + it('does not allow any commands to be processed if not authenticated using noReadyCheck true', function (done) { + if (helper.redisProcess().spawnFailed()) this.skip() + + var args = config.configureClient(ip, { + noReadyCheck: true + }) + client = redis.createClient.apply(null, args) + client.on('ready', function () { + client.set('foo', 'bar', function (err) { + assert.strictEqual(err.message, 'NOAUTH Authentication required.') + assert.strictEqual(err.code, 'NOAUTH') + assert.strictEqual(err.command, 'SET') + done() + }) + }) + }) + + it('does not allow auth to be provided post-hoc with auth method if not authenticated before', function (done) { + if (helper.redisProcess().spawnFailed()) this.skip() + client = redis.createClient.apply(null, args) + client.on('error', function (err) { + assert.strictEqual(err.code, 'NOAUTH') + assert.strictEqual(err.message, 'Ready check failed: NOAUTH Authentication required.') + assert.strictEqual(err.command, 'INFO') + done() + }) + }) + + it('should emit an error if the provided password is faulty', function (done) { + if (helper.redisProcess().spawnFailed()) this.skip() + client = redis.createClient({ + password: 'wrongPassword' + }) + client.once('error', function (err) { + assert.strictEqual(err.message, 'ERR invalid password') + done() + }) + }) + + it('pubsub working with auth', function (done) { + if (helper.redisProcess().spawnFailed()) this.skip() + + var args = config.configureClient(ip, { + password: auth + }) + client = redis.createClient.apply(null, args) + client.set('foo', 'bar') + client.subscribe('somechannel', 'another channel', function (err) { + assert.strictEqual(err, null) + client.once('ready', function () { + assert.strictEqual(client.pubSubMode, 1) + client.get('foo', function (err) { + assert(/ERR only \(P\)SUBSCRIBE \/ \(P\)UNSUBSCRIBE/.test(err.message)) + done() + }) + }) + }) + client.once('ready', function () { + // Coherent behavior with all other offline commands fires commands before emitting but does not wait till they return + assert.strictEqual(client.pubSubMode, 2) + client.ping(function () { // Make sure all commands were properly processed already + client.stream.destroy() + }) + }) + }) + + it('individual commands work properly with batch', function (done) { + // quit => might return an error instead of "OK" in the exec callback... (if not connected) + // auth => might return an error instead of "OK" in the exec callback... (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 + + var args = config.configureClient('localhost', { + noReadyCheck: true + }) + client = redis.createClient.apply(null, args) + assert.strictEqual(client.selectedDb, undefined) + var end = helper.callFuncAfter(done, 8) + client.on('monitor', function () { + end() // Should be called for each command after monitor + }) + client.batch() + .auth(auth) + .select(5, function (err, res) { + assert.strictEqual(err, null) + assert.strictEqual(client.selectedDb, 5) + assert.strictEqual(res, 'OK') + assert.notDeepEqual(client.serverInfo.db5, { avgTtl: 0, expires: 0, keys: 1 }) + }) + .monitor() + .set('foo', 'bar', helper.isString('OK')) + .info('stats', function (err, res) { + assert.strictEqual(err, null) + assert.strictEqual(res.indexOf('# Stats\r\n'), 0) + assert.strictEqual(client.serverInfo.sync_full, '0') + }) + .get('foo', helper.isString('bar')) + .subscribe(['foo', 'bar', 'foo'], helper.isDeepEqual([2, ['foo', 'bar', 'foo']])) + .unsubscribe('foo') + .subscribe('/foo', helper.isDeepEqual([2, ['/foo']])) + .psubscribe('*') + .quit(helper.isString('OK')) + .exec(function (err, res) { + assert.strictEqual(err, null) + res[4] = res[4].substr(0, 9) + assert.deepStrictEqual( + res, + ['OK', 'OK', 'OK', 'OK', '# Stats\r\n', 'bar', [2, ['foo', 'bar', 'foo']], [1, ['foo']], [2, ['/foo']], [3, ['*']], 'OK'] + ) + end() + }) + }) + }) + }) after(function (done) { - if (helper.redisProcess().spawnFailed()) return done(); - helper.stopRedis(function () { - helper.startRedis('./conf/redis.conf', done); - }); - }); -}); + if (helper.redisProcess().spawnFailed()) return done() + helper.stopRedis(function () { + helper.startRedis('./conf/redis.conf', done) + }) + }) + }) +} diff --git a/test/batch.spec.js b/test/batch.spec.js index ff26e60f95..fdc8a25377 100644 --- a/test/batch.spec.js +++ b/test/batch.spec.js @@ -1,350 +1,331 @@ -'use strict'; +'use strict' -var assert = require('assert'); -var config = require('./lib/config'); -var helper = require('./helper'); -var redis = config.redis; +var assert = require('assert') +var config = require('./lib/config') +var helper = require('./helper') +var redis = config.redis -describe("The 'batch' method", function () { +describe('The \'batch\' method', function () { + helper.allTests(function (ip, args) { + describe('using ' + ip, function () { + describe('when not connected', function () { + var client - helper.allTests(function (ip, args) { + beforeEach(function (done) { + client = redis.createClient.apply(null, args) + client.once('connect', function () { + client.quit() + }) + client.on('end', done) + }) - describe('using ' + ip, function () { + it('returns an empty array for missing commands', function (done) { + var batch = client.batch() + batch.exec(function (err, res) { + assert.strictEqual(err, null) + assert.strictEqual(res.length, 0) + done() + }) + }) - describe('when not connected', function () { - var client; + it('returns an error for batch with commands', function (done) { + var batch = client.batch() + batch.set('foo', 'bar') + batch.exec(function (err, res) { + assert.strictEqual(err, null) + assert.strictEqual(res[0].code, 'NR_CLOSED') + done() + }) + }) - beforeEach(function (done) { - client = redis.createClient.apply(null, args); - client.once('connect', function () { - client.quit(); - }); - client.on('end', done); - }); + it('returns an empty array for missing commands if promisified', function () { + return client.batch().execAsync().then(function (res) { + assert.strictEqual(res.length, 0) + }) + }) + }) - it('returns an empty array for missing commands', function (done) { - var batch = client.batch(); - batch.exec(function (err, res) { - assert.strictEqual(err, null); - assert.strictEqual(res.length, 0); - done(); - }); - }); + describe('when connected', function () { + var client - it('returns an error for batch with commands', function (done) { - var batch = client.batch(); - batch.set('foo', 'bar'); - batch.exec(function (err, res) { - assert.strictEqual(err, null); - assert.strictEqual(res[0].code, 'NR_CLOSED'); - done(); - }); - }); + beforeEach(function (done) { + client = redis.createClient.apply(null, args) + client.once('ready', function () { + client.flushdb(function (err) { + return done(err) + }) + }) + }) - it('returns an empty array for missing commands if promisified', function () { - return client.batch().execAsync().then(function (res) { - assert.strictEqual(res.length, 0); - }); - }); - }); + afterEach(function () { + client.end(true) + }) - describe('when connected', function () { - var client; + it('returns an empty array and keep the execution order in tact', function (done) { + var called = false + client.set('foo', 'bar', function () { + called = true + }) + var batch = client.batch() + batch.exec(function (err, res) { + assert.strictEqual(err, null) + assert.strictEqual(res.length, 0) + assert(called) + done() + }) + }) - beforeEach(function (done) { - client = redis.createClient.apply(null, args); - client.once('ready', function () { - client.flushdb(function (err) { - return done(err); - }); - }); - }); + it('runs normal calls in-between batch', function (done) { + var batch = client.batch() + batch.set('m1', '123') + client.set('m2', '456', done) + }) - afterEach(function () { - client.end(true); - }); + it('returns an empty array if promisified', function () { + return client.batch().execAsync().then(function (res) { + assert.strictEqual(res.length, 0) + }) + }) - it('returns an empty array and keep the execution order in tact', function (done) { - var called = false; - client.set('foo', 'bar', function (err, res) { - called = true; - }); - var batch = client.batch(); - batch.exec(function (err, res) { - assert.strictEqual(err, null); - assert.strictEqual(res.length, 0); - assert(called); - done(); - }); - }); + it('returns an empty result array', function (done) { + var batch = client.batch() + var async = true + var notBuffering = batch.exec(function (err, res) { + assert.strictEqual(err, null) + assert.strictEqual(res.length, 0) + async = false + done() + }) + assert(async) + assert.strictEqual(notBuffering, true) + }) - it('runs normal calls in-between batch', function (done) { - var batch = client.batch(); - batch.set('m1', '123'); - client.set('m2', '456', done); - }); + it('fail individually when one command fails using chaining notation', function (done) { + var batch1, batch2 + batch1 = client.batch() + batch1.mset('batchfoo', '10', 'batchbar', '20', helper.isString('OK')) - it('returns an empty array if promisified', function () { - return client.batch().execAsync().then(function (res) { - assert.strictEqual(res.length, 0); - }); - }); + // Provoke an error at queue time + batch1.set('foo2', helper.isError()) + batch1.incr('batchfoo') + batch1.incr('batchbar') + batch1.exec(function () { + // Confirm that the previous command, while containing an error, still worked. + batch2 = client.batch() + batch2.get('foo2', helper.isNull()) + batch2.incr('batchbar', helper.isNumber(22)) + batch2.incr('batchfoo', helper.isNumber(12)) + batch2.exec(function (err, replies) { + assert.strictEqual(err, null) + assert.strictEqual(null, replies[0]) + assert.strictEqual(22, replies[1]) + assert.strictEqual(12, replies[2]) + return done() + }) + }) + }) - it('returns an empty result array', function (done) { - var batch = client.batch(); - var async = true; - var notBuffering = batch.exec(function (err, res) { - assert.strictEqual(err, null); - assert.strictEqual(res.length, 0); - async = false; - done(); - }); - assert(async); - assert.strictEqual(notBuffering, true); - }); + it('fail individually when one command fails and emit the error if no callback has been provided', function (done) { + var batch1 + client.on('error', function (err) { + done(err) + }) + batch1 = client.batch() + batch1.mset('batchfoo', '10', 'batchbar', '20', helper.isString('OK')) - it('fail individually when one command fails using chaining notation', function (done) { - var batch1, batch2; - batch1 = client.batch(); - batch1.mset('batchfoo', '10', 'batchbar', '20', helper.isString('OK')); + // Provoke an error at queue time + batch1.set('foo2') + batch1.incr('batchfoo') + batch1.incr('batchbar') + batch1.exec(function (err, res) { + // TODO: This should actually return an error! + assert.strictEqual(err, null) + assert.strictEqual(res[1].command, 'SET') + assert.strictEqual(res[1].code, 'ERR') + done() + }) + }) - // Provoke an error at queue time - batch1.set('foo2', helper.isError()); - batch1.incr('batchfoo'); - batch1.incr('batchbar'); - batch1.exec(function () { - // Confirm that the previous command, while containing an error, still worked. - batch2 = client.batch(); - batch2.get('foo2', helper.isNull()); - batch2.incr('batchbar', helper.isNumber(22)); - batch2.incr('batchfoo', helper.isNumber(12)); - batch2.exec(function (err, replies) { - assert.strictEqual(null, replies[0]); - assert.strictEqual(22, replies[1]); - assert.strictEqual(12, replies[2]); - return done(); - }); - }); - }); + it('fail individually when one command in an array of commands fails', function (done) { + // test nested batch-bulk replies + client.batch([ + ['mget', 'batchfoo', 'batchbar', helper.isDeepEqual([null, null])], + ['set', 'foo2', helper.isError()], + ['incr', 'batchfoo'], + ['incr', 'batchbar'] + ]).exec(function (err, replies) { + // TODO: This should actually return an error! + assert.strictEqual(err, null) + assert.strictEqual(2, replies[0].length) + assert.strictEqual(null, replies[0][0]) + assert.strictEqual(null, replies[0][1]) + assert.strictEqual('SET', replies[1].command) + assert.strictEqual('1', replies[2].toString()) + assert.strictEqual('1', replies[3].toString()) + return done() + }) + }) - it('fail individually when one command fails and emit the error if no callback has been provided', function (done) { - var batch1; - client.on('error', function (err) { - done(err); - }); - batch1 = client.batch(); - batch1.mset('batchfoo', '10', 'batchbar', '20', helper.isString('OK')); + it('handles multiple operations being applied to a set', function (done) { + client.sadd('some set', 'mem 1') + client.sadd(['some set', 'mem 2']) + client.sadd('some set', 'mem 3') + client.sadd('some set', 'mem 4') - // Provoke an error at queue time - batch1.set('foo2'); - batch1.incr('batchfoo'); - batch1.incr('batchbar'); - batch1.exec(function (err, res) { - assert.strictEqual(res[1].command, 'SET'); - assert.strictEqual(res[1].code, 'ERR'); - done(); - }); - }); + // make sure empty mb reply works + client.del('some missing set') + client.smembers('some missing set', function (err, reply) { + assert.strictEqual(err, null) + // make sure empty mb reply works + assert.strictEqual(0, reply.length) + }) - it('fail individually when one command in an array of commands fails', function (done) { - // test nested batch-bulk replies - client.batch([ - ['mget', 'batchfoo', 'batchbar', function (err, res) { - assert.strictEqual(2, res.length); - assert.strictEqual(0, +res[0]); - assert.strictEqual(0, +res[1]); - }], - ['set', 'foo2', helper.isError()], - ['incr', 'batchfoo'], - ['incr', 'batchbar'] - ]).exec(function (err, replies) { - assert.strictEqual(2, replies[0].length); - assert.strictEqual(null, replies[0][0]); - assert.strictEqual(null, replies[0][1]); - assert.strictEqual('SET', replies[1].command); - assert.strictEqual('1', replies[2].toString()); - assert.strictEqual('1', replies[3].toString()); - return done(); - }); - }); + // test nested batch-bulk replies with empty mb elements. + client.batch([ + ['smembers', ['some set']], + ['del', 'some set'], + ['smembers', 'some set', undefined] // The explicit undefined is handled as a callback that is undefined + ]) + .scard('some set') + .exec(function (err, replies) { + assert.strictEqual(err, null) + assert.strictEqual(4, replies[0].length) + assert.strictEqual(0, replies[2].length) + return done() + }) + }) - it('handles multiple operations being applied to a set', function (done) { - client.sadd('some set', 'mem 1'); - client.sadd(['some set', 'mem 2']); - client.sadd('some set', 'mem 3'); - client.sadd('some set', 'mem 4'); + it('allows multiple operations to be performed using constructor with all kinds of syntax', function (done) { + var now = Date.now() + var arr = ['batchhmset', 'batchbar', 'batchbaz'] + var arr2 = ['some manner of key', 'otherTypes'] + var arr3 = [5768, 'batchbarx', 'batchfoox'] + var arr4 = ['mset', [578, 'batchbar'], helper.isString('OK')] + client.batch([ + arr4, + [['mset', 'batchfoo2', 'batchbar2', 'batchfoo3', 'batchbar3'], helper.isString('OK')], + ['hmset', arr], + [['hmset', 'batchhmset2', 'batchbar2', 'batchfoo3', 'batchbar3', 'test'], helper.isString('OK')], + ['hmset', ['batchhmset', 'batchbar', 'batchfoo'], helper.isString('OK')], + ['hmset', arr3, helper.isString('OK')], + ['hmset', now, {123456789: 'abcdefghij', 'some manner of key': 'a type of value', 'otherTypes': 555}], + ['hmset', 'key2', {'0123456789': 'abcdefghij', 'some manner of key': 'a type of value', 'otherTypes': 999}, helper.isString('OK')], + ['hmset', 'batchhmset', ['batchbar', 'batchbaz']], + ['hmset', 'batchhmset', ['batchbar', 'batchbaz'], helper.isString('OK')] + ]) + .hmget(now, 123456789, 'otherTypes') + .hmget('key2', arr2, function noop () {}) + .hmget(['batchhmset2', 'some manner of key', 'batchbar3']) + .mget('batchfoo2', ['batchfoo3', 'batchfoo'], function (err, res) { + assert.strictEqual(err, null) + assert.strictEqual(res[0], 'batchbar2') + assert.strictEqual(res[1], 'batchbar3') + assert.strictEqual(res[2], null) + }) + .exec(function (err, replies) { + assert.strictEqual(arr.length, 3) + assert.strictEqual(arr2.length, 2) + assert.strictEqual(arr3.length, 3) + assert.strictEqual(arr4.length, 3) + assert.strictEqual(null, err) + assert.strictEqual(replies[10][1], '555') + assert.strictEqual(replies[11][0], 'a type of value') + assert.strictEqual(replies[12][0], null) + assert.strictEqual(replies[12][1], 'test') + assert.strictEqual(replies[13][0], 'batchbar2') + assert.strictEqual(replies[13].length, 3) + assert.strictEqual(replies.length, 14) + return done() + }) + }) - // make sure empty mb reply works - client.del('some missing set'); - client.smembers('some missing set', function (err, reply) { - // make sure empty mb reply works - assert.strictEqual(0, reply.length); - }); + it('converts a non string key to a string', function (done) { + // TODO: Converting the key might change soon again. + client.batch().hmset(true, { + test: 123, + bar: 'baz' + }).exec(done) + }) - // test nested batch-bulk replies with empty mb elements. - client.batch([ - ['smembers', ['some set']], - ['del', 'some set'], - ['smembers', 'some set', undefined] // The explicit undefined is handled as a callback that is undefined - ]) - .scard('some set') - .exec(function (err, replies) { - assert.strictEqual(4, replies[0].length); - assert.strictEqual(0, replies[2].length); - return done(); - }); - }); + it('runs a batch without any further commands', function (done) { + var buffering = client.batch().exec(function (err, res) { + assert.strictEqual(err, null) + assert.strictEqual(res.length, 0) + done() + }) + assert(typeof buffering === 'boolean') + }) - it('allows multiple operations to be performed using constructor with all kinds of syntax', function (done) { - var now = Date.now(); - var arr = ['batchhmset', 'batchbar', 'batchbaz']; - var arr2 = ['some manner of key', 'otherTypes']; - var arr3 = [5768, 'batchbarx', 'batchfoox']; - var arr4 = ['mset', [578, 'batchbar'], helper.isString('OK')]; - client.batch([ - arr4, - [['mset', 'batchfoo2', 'batchbar2', 'batchfoo3', 'batchbar3'], helper.isString('OK')], - ['hmset', arr], - [['hmset', 'batchhmset2', 'batchbar2', 'batchfoo3', 'batchbar3', 'test'], helper.isString('OK')], - ['hmset', ['batchhmset', 'batchbar', 'batchfoo'], helper.isString('OK')], - ['hmset', arr3, helper.isString('OK')], - ['hmset', now, {123456789: 'abcdefghij', 'some manner of key': 'a type of value', 'otherTypes': 555}], - ['hmset', 'key2', {'0123456789': 'abcdefghij', 'some manner of key': 'a type of value', 'otherTypes': 999}, helper.isString('OK')], - ['hmset', 'batchhmset', ['batchbar', 'batchbaz']], - ['hmset', 'batchhmset', ['batchbar', 'batchbaz'], helper.isString('OK')], - ]) - .hmget(now, 123456789, 'otherTypes') - .hmget('key2', arr2, function noop () {}) - .hmget(['batchhmset2', 'some manner of key', 'batchbar3']) - .mget('batchfoo2', ['batchfoo3', 'batchfoo'], function (err, res) { - assert.strictEqual(res[0], 'batchbar2'); - assert.strictEqual(res[1], 'batchbar3'); - assert.strictEqual(res[2], null); - }) - .exec(function (err, replies) { - assert.equal(arr.length, 3); - assert.equal(arr2.length, 2); - assert.equal(arr3.length, 3); - assert.equal(arr4.length, 3); - assert.strictEqual(null, err); - assert.equal(replies[10][1], '555'); - assert.equal(replies[11][0], 'a type of value'); - assert.strictEqual(replies[12][0], null); - assert.equal(replies[12][1], 'test'); - assert.equal(replies[13][0], 'batchbar2'); - assert.equal(replies[13].length, 3); - assert.equal(replies.length, 14); - return done(); - }); - }); + it('runs a batch without any further commands and without callback', function () { + var buffering = client.batch().exec() + assert.strictEqual(buffering, true) + }) - it('converts a non string key to a string', function (done) { - // TODO: Converting the key might change soon again. - client.batch().hmset(true, { - test: 123, - bar: 'baz' - }).exec(done); - }); + it('allows multiple operations to be performed using a chaining API', function (done) { + client.batch() + .mset('some', '10', 'keys', '20') + .incr('some') + .incr('keys') + .mget('some', 'keys') + .exec(helper.isDeepEqual(['OK', 11, 21, ['11', '21']], done)) + }) - it('runs a batch without any further commands', function (done) { - var buffering = client.batch().exec(function (err, res) { - assert.strictEqual(err, null); - assert.strictEqual(res.length, 0); - done(); - }); - assert(typeof buffering === 'boolean'); - }); + it('allows multiple commands to work the same as normal to be performed using a chaining API', function (done) { + client.batch() + .mset(['some', '10', 'keys', '20']) + .incr('some', helper.isNumber(11)) + .incr(['keys'], helper.isNumber(21)) + .mget('some', 'keys') + .exec(helper.isDeepEqual(['OK', 11, 21, ['11', '21']], done)) + }) - it('runs a batch without any further commands and without callback', function () { - var buffering = client.batch().exec(); - assert.strictEqual(buffering, true); - }); + it('allows multiple commands to work the same as normal to be performed using a chaining API promisified', function () { + return client.batch() + .mset(['some', '10', 'keys', '20']) + .incr('some', helper.isNumber(11)) + .incr(['keys'], helper.isNumber(21)) + .mget('some', 'keys') + .execAsync() + .then(function (res) { + helper.isDeepEqual(['OK', 11, 21, ['11', '21']])(null, res) + }) + }) - it('allows multiple operations to be performed using a chaining API', function (done) { - client.batch() - .mset('some', '10', 'keys', '20') - .incr('some') - .incr('keys') - .mget('some', 'keys') - .exec(function (err, replies) { - assert.strictEqual(null, err); - assert.equal('OK', replies[0]); - assert.equal(11, replies[1]); - assert.equal(21, replies[2]); - assert.equal(11, replies[3][0].toString()); - assert.equal(21, replies[3][1].toString()); - return done(); - }); - }); + it('allows an array to be provided indicating multiple operations to perform', function (done) { + // test nested batch-bulk replies with nulls. + client.batch([ + ['mget', ['batchfoo', 'some', 'random value', 'keys']], + ['incr', 'batchfoo'] + ]) + .exec(function (err, replies) { + assert.strictEqual(err, null) + assert.strictEqual(replies.length, 2) + assert.strictEqual(replies[0].length, 4) + return done() + }) + }) - it('allows multiple commands to work the same as normal to be performed using a chaining API', function (done) { - client.batch() - .mset(['some', '10', 'keys', '20']) - .incr('some', helper.isNumber(11)) - .incr(['keys'], helper.isNumber(21)) - .mget('some', 'keys') - .exec(function (err, replies) { - assert.strictEqual(null, err); - assert.equal('OK', replies[0]); - assert.equal(11, replies[1]); - assert.equal(21, replies[2]); - assert.equal(11, replies[3][0].toString()); - assert.equal(21, replies[3][1].toString()); - return done(); - }); - }); + it('allows multiple operations to be performed on a hash', function (done) { + client.batch() + .hmset('batchhash', 'a', 'foo', 'b', 1) + .hmset('batchhash', { + extra: 'fancy', + things: 'here' + }) + .hgetall('batchhash') + .exec(done) + }) - it('allows multiple commands to work the same as normal to be performed using a chaining API promisified', function () { - return client.batch() - .mset(['some', '10', 'keys', '20']) - .incr('some', helper.isNumber(11)) - .incr(['keys'], helper.isNumber(21)) - .mget('some', 'keys') - .execAsync() - .then(function (replies) { - assert.equal('OK', replies[0]); - assert.equal(11, replies[1]); - assert.equal(21, replies[2]); - assert.equal(11, replies[3][0].toString()); - assert.equal(21, replies[3][1].toString()); - }); - }); + it('should work without any callback or arguments', function (done) { + var batch = client.batch() + batch.set('baz', 'binary') + batch.set('foo', 'bar') + batch.ping() + batch.exec() - it('allows an array to be provided indicating multiple operations to perform', function (done) { - // test nested batch-bulk replies with nulls. - client.batch([ - ['mget', ['batchfoo', 'some', 'random value', 'keys']], - ['incr', 'batchfoo'] - ]) - .exec(function (err, replies) { - assert.strictEqual(replies.length, 2); - assert.strictEqual(replies[0].length, 4); - return done(); - }); - }); - - it('allows multiple operations to be performed on a hash', function (done) { - client.batch() - .hmset('batchhash', 'a', 'foo', 'b', 1) - .hmset('batchhash', { - extra: 'fancy', - things: 'here' - }) - .hgetall('batchhash') - .exec(done); - }); - - it('should work without any callback or arguments', function (done) { - var batch = client.batch(); - batch.set('baz', 'binary'); - batch.set('foo', 'bar'); - batch.ping(); - batch.exec(); - - client.get('foo', helper.isString('bar', done)); - }); - - }); - }); - }); -}); + client.get('foo', helper.isString('bar', done)) + }) + }) + }) + }) +}) diff --git a/test/commands/blpop.spec.js b/test/commands/blpop.spec.js index 77b3d35d67..7f6a922ec5 100644 --- a/test/commands/blpop.spec.js +++ b/test/commands/blpop.spec.js @@ -1,77 +1,75 @@ -'use strict'; +'use strict' -var assert = require('assert'); -var config = require('../lib/config'); -var helper = require('../helper'); -var redis = config.redis; -var intercept = require('intercept-stdout'); +var assert = require('assert') +var config = require('../lib/config') +var helper = require('../helper') +var redis = config.redis +var intercept = require('intercept-stdout') -describe("The 'blpop' method", function () { +describe('The \'blpop\' method', function () { + helper.allTests(function (ip, args) { + describe('using ' + ip, function () { + var client + var bclient - helper.allTests(function (ip, args) { + beforeEach(function (done) { + client = redis.createClient.apply(null, args) + client.once('ready', function () { + client.flushdb(done) + }) + }) - describe('using ' + ip, function () { - var client; - var bclient; + it('pops value immediately if list contains values', function (done) { + bclient = redis.createClient.apply(null, args) + redis.debugMode = true + var text = '' + var unhookIntercept = intercept(function (data) { + text += data + return '' + }) + client.rpush('blocking list', 'initial value', helper.isNumber(1)) + unhookIntercept() + assert(/^Send 127\.0\.0\.1:6379 id [0-9]+: \*3\r\n\$5\r\nrpush\r\n\$13\r\nblocking list\r\n\$13\r\ninitial value\r\n\n$/.test(text)) + redis.debugMode = false + bclient.blpop('blocking list', 0, function (err, value) { + assert.strictEqual(value[0], 'blocking list') + assert.strictEqual(value[1], 'initial value') + return done(err) + }) + }) - beforeEach(function (done) { - client = redis.createClient.apply(null, args); - client.once('ready', function () { - client.flushdb(done); - }); - }); + it('pops value immediately if list contains values using array notation', function (done) { + bclient = redis.createClient.apply(null, args) + client.rpush(['blocking list', 'initial value'], helper.isNumber(1)) + bclient.blpop(['blocking list', 0], function (err, value) { + assert.strictEqual(value[0], 'blocking list') + assert.strictEqual(value[1], 'initial value') + return done(err) + }) + }) - it('pops value immediately if list contains values', function (done) { - bclient = redis.createClient.apply(null, args); - redis.debugMode = true; - var text = ''; - var unhookIntercept = intercept(function (data) { - text += data; - return ''; - }); - client.rpush('blocking list', 'initial value', helper.isNumber(1)); - unhookIntercept(); - assert(/^Send 127\.0\.0\.1:6379 id [0-9]+: \*3\r\n\$5\r\nrpush\r\n\$13\r\nblocking list\r\n\$13\r\ninitial value\r\n\n$/.test(text)); - redis.debugMode = false; - bclient.blpop('blocking list', 0, function (err, value) { - assert.strictEqual(value[0], 'blocking list'); - assert.strictEqual(value[1], 'initial value'); - return done(err); - }); - }); + it('waits for value if list is not yet populated', function (done) { + bclient = redis.createClient.apply(null, args) + bclient.blpop('blocking list 2', 5, function (err, value) { + assert.strictEqual(value[0], 'blocking list 2') + assert.strictEqual(value[1], 'initial value') + return done(err) + }) + client.rpush('blocking list 2', 'initial value', helper.isNumber(1)) + }) - it('pops value immediately if list contains values using array notation', function (done) { - bclient = redis.createClient.apply(null, args); - client.rpush(['blocking list', 'initial value'], helper.isNumber(1)); - bclient.blpop(['blocking list', 0], function (err, value) { - assert.strictEqual(value[0], 'blocking list'); - assert.strictEqual(value[1], 'initial value'); - return done(err); - }); - }); + it('times out after specified time', function (done) { + bclient = redis.createClient.apply(null, args) + bclient.blpop('blocking list', 1, function (err, res) { + assert.strictEqual(res, null) + return done(err) + }) + }) - it('waits for value if list is not yet populated', function (done) { - bclient = redis.createClient.apply(null, args); - bclient.blpop('blocking list 2', 5, function (err, value) { - assert.strictEqual(value[0], 'blocking list 2'); - assert.strictEqual(value[1], 'initial value'); - return done(err); - }); - client.rpush('blocking list 2', 'initial value', helper.isNumber(1)); - }); - - it('times out after specified time', function (done) { - bclient = redis.createClient.apply(null, args); - bclient.blpop('blocking list', 1, function (err, res) { - assert.strictEqual(res, null); - return done(err); - }); - }); - - afterEach(function () { - client.end(true); - bclient.end(true); - }); - }); - }); -}); + afterEach(function () { + client.end(true) + bclient.end(true) + }) + }) + }) +}) diff --git a/test/commands/client.spec.js b/test/commands/client.spec.js index 51a55e3f16..9ced0f4e4f 100644 --- a/test/commands/client.spec.js +++ b/test/commands/client.spec.js @@ -1,155 +1,148 @@ -'use strict'; +'use strict' -var assert = require('assert'); -var config = require('../lib/config'); -var helper = require('../helper'); -var redis = config.redis; +var Buffer = require('safe-buffer').Buffer +var assert = require('assert') +var config = require('../lib/config') +var helper = require('../helper') +var redis = config.redis -describe("The 'client' method", function () { +describe('The \'client\' method', function () { + helper.allTests(function (ip, args) { + var pattern = /addr=/ - helper.allTests(function (ip, args) { - var pattern = /addr=/; + describe('using ' + ip, function () { + var client - describe('using ' + ip, function () { - var client; + beforeEach(function (done) { + client = redis.createClient.apply(null, args) + client.once('ready', function () { + client.flushdb(done) + }) + }) - beforeEach(function (done) { - client = redis.createClient.apply(null, args); - client.once('ready', function () { - client.flushdb(done); - }); - }); + afterEach(function () { + client.end(true) + }) - afterEach(function () { - client.end(true); - }); + describe('list', function () { + it('lists connected clients', function (done) { + client.client('LIST', helper.match(pattern, done)) + }) - describe('list', function () { - it('lists connected clients', function (done) { - client.client('LIST', helper.match(pattern, done)); - }); + it('lists connected clients when invoked with multi\'s chaining syntax', function (done) { + client.multi().client('list', helper.isType.string()).exec(helper.match(pattern, done)) + }) - it("lists connected clients when invoked with multi's chaining syntax", function (done) { - client.multi().client('list', helper.isType.string()).exec(helper.match(pattern, done)); - }); + it('lists connected clients when invoked with array syntax on client', function (done) { + client.multi().client(['list']).exec(helper.match(pattern, done)) + }) - it('lists connected clients when invoked with array syntax on client', function (done) { - client.multi().client(['list']).exec(helper.match(pattern, done)); - }); + it('lists connected clients when invoked with multi\'s array syntax', function (done) { + client.multi([ + ['client', 'list'] + ]).exec(helper.match(pattern, done)) + }) + }) - it("lists connected clients when invoked with multi's array syntax", function (done) { - client.multi([ - ['client', 'list'] - ]).exec(helper.match(pattern, done)); - }); - }); + describe('reply', function () { + describe('as normal command', function () { + it('on', function (done) { + helper.serverVersionAtLeast.call(this, client, [3, 2, 0]) + assert.strictEqual(client.reply, 'ON') + client.client('reply', 'on', helper.isString('OK')) + assert.strictEqual(client.reply, 'ON') + client.set('foo', 'bar', done) + }) - describe('reply', function () { - describe('as normal command', function () { - it('on', function (done) { - helper.serverVersionAtLeast.call(this, client, [3, 2, 0]); - assert.strictEqual(client.reply, 'ON'); - client.client('reply', 'on', helper.isString('OK')); - assert.strictEqual(client.reply, 'ON'); - client.set('foo', 'bar', done); - }); + it('off', function (done) { + helper.serverVersionAtLeast.call(this, client, [3, 2, 0]) + assert.strictEqual(client.reply, 'ON') + client.client(Buffer.from('REPLY'), 'OFF', helper.isUndefined()) + assert.strictEqual(client.reply, 'OFF') + client.set('foo', 'bar', helper.isUndefined(done)) + }) - it('off', function (done) { - helper.serverVersionAtLeast.call(this, client, [3, 2, 0]); - assert.strictEqual(client.reply, 'ON'); - client.client(new Buffer('REPLY'), 'OFF', helper.isUndefined()); - assert.strictEqual(client.reply, 'OFF'); - client.set('foo', 'bar', helper.isUndefined(done)); - }); + it('skip', function (done) { + helper.serverVersionAtLeast.call(this, client, [3, 2, 0]) + assert.strictEqual(client.reply, 'ON') + client.client('REPLY', Buffer.from('SKIP'), helper.isUndefined()) + assert.strictEqual(client.reply, 'SKIP_ONE_MORE') + client.set('foo', 'bar', helper.isUndefined()) + client.get('foo', helper.isString('bar', done)) + }) + }) - it('skip', function (done) { - helper.serverVersionAtLeast.call(this, client, [3, 2, 0]); - assert.strictEqual(client.reply, 'ON'); - client.client('REPLY', new Buffer('SKIP'), helper.isUndefined()); - assert.strictEqual(client.reply, 'SKIP_ONE_MORE'); - client.set('foo', 'bar', helper.isUndefined()); - client.get('foo', helper.isString('bar', done)); - }); - }); + describe('in a batch context', function () { + it('on', function (done) { + helper.serverVersionAtLeast.call(this, client, [3, 2, 0]) + var batch = client.batch() + assert.strictEqual(client.reply, 'ON') + batch.client('reply', 'on', helper.isString('OK')) + assert.strictEqual(client.reply, 'ON') + batch.set('foo', 'bar') + batch.exec(function (err, res) { + assert.deepEqual(res, ['OK', 'OK']) + done(err) + }) + }) - describe('in a batch context', function () { - it('on', function (done) { - helper.serverVersionAtLeast.call(this, client, [3, 2, 0]); - var batch = client.batch(); - assert.strictEqual(client.reply, 'ON'); - batch.client('reply', 'on', helper.isString('OK')); - assert.strictEqual(client.reply, 'ON'); - batch.set('foo', 'bar'); - batch.exec(function (err, res) { - assert.deepEqual(res, ['OK', 'OK']); - done(err); - }); - }); + it('off', function (done) { + helper.serverVersionAtLeast.call(this, client, [3, 2, 0]) + var batch = client.batch() + assert.strictEqual(client.reply, 'ON') + batch.set('hello', 'world') + batch.client(Buffer.from('REPLY'), Buffer.from('OFF'), helper.isUndefined()) + batch.set('foo', 'bar', helper.isUndefined()) + batch.exec(function (err, res) { + assert.strictEqual(client.reply, 'OFF') + assert.deepEqual(res, ['OK', undefined, undefined]) + done(err) + }) + }) - it('off', function (done) { - helper.serverVersionAtLeast.call(this, client, [3, 2, 0]); - var batch = client.batch(); - assert.strictEqual(client.reply, 'ON'); - batch.set('hello', 'world'); - batch.client(new Buffer('REPLY'), new Buffer('OFF'), helper.isUndefined()); - batch.set('foo', 'bar', helper.isUndefined()); - batch.exec(function (err, res) { - assert.strictEqual(client.reply, 'OFF'); - assert.deepEqual(res, ['OK', undefined, undefined]); - done(err); - }); - }); + it('skip', function (done) { + helper.serverVersionAtLeast.call(this, client, [3, 2, 0]) + assert.strictEqual(client.reply, 'ON') + client.batch() + .set('hello', 'world') + .client('REPLY', 'SKIP', helper.isUndefined()) + .set('foo', 'bar', helper.isUndefined()) + .get('foo') + .exec(function (err, res) { + assert.strictEqual(client.reply, 'ON') + assert.deepEqual(res, ['OK', undefined, undefined, 'bar']) + done(err) + }) + }) + }) + }) - it('skip', function (done) { - helper.serverVersionAtLeast.call(this, client, [3, 2, 0]); - assert.strictEqual(client.reply, 'ON'); - client.batch() - .set('hello', 'world') - .client('REPLY', 'SKIP', helper.isUndefined()) - .set('foo', 'bar', helper.isUndefined()) - .get('foo') - .exec(function (err, res) { - assert.strictEqual(client.reply, 'ON'); - assert.deepEqual(res, ['OK', undefined, undefined, 'bar']); - done(err); - }); - }); - }); - }); + describe('setname / getname', function () { + var client2 - describe('setname / getname', function () { - var client2; + beforeEach(function (done) { + client2 = redis.createClient.apply(null, args) + client2.once('ready', function () { + done() + }) + }) - beforeEach(function (done) { - client2 = redis.createClient.apply(null, args); - client2.once('ready', function () { - done(); - }); - }); + afterEach(function () { + client2.end(true) + }) - afterEach(function () { - client2.end(true); - }); + it('sets the name', function (done) { + // The querys are auto pipelined and the response is a response to all querys of one client + // per chunk. So the execution order is only guaranteed on each client + var end = helper.callFuncAfter(done, 2) - it('sets the name', function (done) { - // The querys are auto pipelined and the response is a response to all querys of one client - // per chunk. So the execution order is only garanteed on each client - var end = helper.callFuncAfter(done, 2); - - client.client('setname', 'RUTH'); - client2.client('setname', ['RENEE'], helper.isString('OK')); - client2.client(['setname', 'MARTIN'], helper.isString('OK')); - client2.client('getname', function (err, res) { - assert.equal(res, 'MARTIN'); - end(); - }); - client.client('getname', function (err, res) { - assert.equal(res, 'RUTH'); - end(); - }); - }); - - }); - }); - }); -}); + client.client('setname', 'RUTH') + client2.client('setname', ['RENEE'], helper.isString('OK')) + client2.client(['setname', 'MARTIN'], helper.isString('OK')) + client2.client('getname', helper.isString('MARTIN', end)) + client.client('getname', helper.isString('RUTH', end)) + }) + }) + }) + }) +}) diff --git a/test/commands/dbsize.spec.js b/test/commands/dbsize.spec.js index 8cedef3c73..bc343ee5f2 100644 --- a/test/commands/dbsize.spec.js +++ b/test/commands/dbsize.spec.js @@ -1,95 +1,93 @@ -'use strict'; +'use strict' -var assert = require('assert'); -var config = require('../lib/config'); -var helper = require('../helper'); -var redis = config.redis; -var uuid = require('uuid'); +var assert = require('assert') +var config = require('../lib/config') +var helper = require('../helper') +var redis = config.redis +var uuid = require('uuid') -describe("The 'dbsize' method", function () { +describe('The \'dbsize\' method', function () { + helper.allTests(function (ip, args) { + describe('using ' + ip, function () { + var key, value - helper.allTests(function (ip, args) { + beforeEach(function () { + key = uuid.v4() + value = uuid.v4() + }) - describe('using ' + ip, function () { - var key, value; + describe('when not connected', function () { + var client - beforeEach(function () { - key = uuid.v4(); - value = uuid.v4(); - }); + beforeEach(function (done) { + client = redis.createClient.apply(null, args) + client.once('ready', function () { + client.quit() + }) + client.on('end', done) + }) - describe('when not connected', function () { - var client; + it('reports an error', function (done) { + client.dbsize([], function (err, res) { + assert(err.message.match(/The connection is already closed/)) + done() + }) + }) + }) - beforeEach(function (done) { - client = redis.createClient.apply(null, args); - client.once('ready', function () { - client.quit(); - }); - client.on('end', done); - }); + describe('when connected', function () { + var client - it('reports an error', function (done) { - client.dbsize([], function (err, res) { - assert(err.message.match(/The connection is already closed/)); - done(); - }); - }); - }); + beforeEach(function (done) { + client = redis.createClient.apply(null, args) + client.once('ready', function () { + client.flushdb(function (err, res) { + helper.isString('OK')(err, res) + done() + }) + }) + }) - describe('when connected', function () { - var client; + afterEach(function () { + client.end(true) + }) - beforeEach(function (done) { - client = redis.createClient.apply(null, args); - client.once('ready', function () { - client.flushdb(function (err, res) { - helper.isString('OK')(err, res); - done(); - }); - }); - }); + it('returns a zero db size', function (done) { + client.dbsize([], function (err, res) { + helper.isNotError()(err, res) + helper.isType.number()(err, res) + assert.strictEqual(res, 0, 'Initial db size should be 0') + done() + }) + }) - afterEach(function () { - client.end(true); - }); + describe('when more data is added to Redis', function () { + var oldSize - it('returns a zero db size', function (done) { - client.dbsize([], function (err, res) { - helper.isNotError()(err, res); - helper.isType.number()(err, res); - assert.strictEqual(res, 0, 'Initial db size should be 0'); - done(); - }); - }); + beforeEach(function (done) { + client.dbsize(function (err, res) { + helper.isType.number()(err, res) + assert.strictEqual(res, 0, 'Initial db size should be 0') - describe('when more data is added to Redis', function () { - var oldSize; + oldSize = res - beforeEach(function (done) { - client.dbsize(function (err, res) { - helper.isType.number()(err, res); - assert.strictEqual(res, 0, 'Initial db size should be 0'); + client.set(key, value, function (err, res) { + helper.isNotError()(err, res) + done() + }) + }) + }) - oldSize = res; - - client.set(key, value, function (err, res) { - helper.isNotError()(err, res); - done(); - }); - }); - }); - - it('returns a larger db size', function (done) { - client.dbsize([], function (err, res) { - helper.isNotError()(err, res); - helper.isType.positiveNumber()(err, res); - assert.strictEqual(true, (oldSize < res), 'Adding data should increase db size.'); - done(); - }); - }); - }); - }); - }); - }); -}); + it('returns a larger db size', function (done) { + client.dbsize([], function (err, res) { + helper.isNotError()(err, res) + helper.isType.positiveNumber()(err, res) + assert.strictEqual(true, (oldSize < res), 'Adding data should increase db size.') + done() + }) + }) + }) + }) + }) + }) +}) diff --git a/test/commands/del.spec.js b/test/commands/del.spec.js index 3f170cc279..41a0f06d1f 100644 --- a/test/commands/del.spec.js +++ b/test/commands/del.spec.js @@ -1,57 +1,55 @@ -'use strict'; +'use strict' -var config = require('../lib/config'); -var helper = require('../helper'); -var redis = config.redis; +var config = require('../lib/config') +var helper = require('../helper') +var redis = config.redis -describe("The 'del' method", function () { +describe('The \'del\' method', function () { + helper.allTests(function (ip, args) { + describe('using ' + ip, function () { + var client - helper.allTests(function (ip, args) { + beforeEach(function (done) { + client = redis.createClient.apply(null, args) + client.once('ready', function () { + client.flushdb(done) + }) + }) - describe('using ' + ip, function () { - var client; + it('allows a single key to be deleted', function (done) { + client.set('foo', 'bar') + client.del('foo', helper.isNumber(1)) + client.get('foo', helper.isNull(done)) + }) - beforeEach(function (done) { - client = redis.createClient.apply(null, args); - client.once('ready', function () { - client.flushdb(done); - }); - }); + it('allows del to be called on a key that does not exist', function (done) { + client.del('foo', helper.isNumber(0, done)) + }) - it('allows a single key to be deleted', function (done) { - client.set('foo', 'bar'); - client.del('foo', helper.isNumber(1)); - client.get('foo', helper.isNull(done)); - }); + it('allows multiple keys to be deleted', function (done) { + client.mset('foo', 'bar', 'apple', 'banana') + client.del('foo', 'apple', helper.isNumber(2)) + client.get('foo', helper.isNull()) + client.get('apple', helper.isNull(done)) + }) - it('allows del to be called on a key that does not exist', function (done) { - client.del('foo', helper.isNumber(0, done)); - }); + it('allows multiple keys to be deleted with the array syntax', function (done) { + client.mset('foo', 'bar', 'apple', 'banana') + client.del(['foo', 'apple'], helper.isNumber(2)) + client.get('foo', helper.isNull()) + client.get('apple', helper.isNull(done)) + }) - it('allows multiple keys to be deleted', function (done) { - client.mset('foo', 'bar', 'apple', 'banana'); - client.del('foo', 'apple', helper.isNumber(2)); - client.get('foo', helper.isNull()); - client.get('apple', helper.isNull(done)); - }); + it('allows multiple keys to be deleted with the array syntax and no callback', function (done) { + client.mset('foo', 'bar', 'apple', 'banana') + client.del(['foo', 'apple']) + client.get('foo', helper.isNull()) + client.get('apple', helper.isNull(done)) + }) - it('allows multiple keys to be deleted with the array syntax', function (done) { - client.mset('foo', 'bar', 'apple', 'banana'); - client.del(['foo', 'apple'], helper.isNumber(2)); - client.get('foo', helper.isNull()); - client.get('apple', helper.isNull(done)); - }); - - it('allows multiple keys to be deleted with the array syntax and no callback', function (done) { - client.mset('foo', 'bar', 'apple', 'banana'); - client.del(['foo', 'apple']); - client.get('foo', helper.isNull()); - client.get('apple', helper.isNull(done)); - }); - - afterEach(function () { - client.end(true); - }); - }); - }); -}); + afterEach(function () { + client.end(true) + }) + }) + }) +}) diff --git a/test/commands/eval.spec.js b/test/commands/eval.spec.js index db74372db4..2ee0e7f833 100644 --- a/test/commands/eval.spec.js +++ b/test/commands/eval.spec.js @@ -1,210 +1,196 @@ -'use strict'; +'use strict' -var assert = require('assert'); -var config = require('../lib/config'); -var crypto = require('crypto'); -var helper = require('../helper'); -var redis = config.redis; +var assert = require('assert') +var config = require('../lib/config') +var crypto = require('crypto') +var helper = require('../helper') +var redis = config.redis -describe("The 'eval' method", function () { +describe('The \'eval\' method', function () { + helper.allTests(function (ip, args) { + describe('using ' + ip, function () { + var client + var source = 'return redis.call(\'set\', \'sha\', \'test\')' - helper.allTests(function (ip, args) { + beforeEach(function (done) { + client = redis.createClient.apply(null, args) + client.once('ready', function () { + client.flushdb(done) + }) + }) - describe('using ' + ip, function () { - var client; - var source = "return redis.call('set', 'sha', 'test')"; + afterEach(function () { + client.end(true) + }) - beforeEach(function (done) { - client = redis.createClient.apply(null, args); - client.once('ready', function () { - client.flushdb(done); - }); - }); + it('converts a float to an integer when evaluated', function (done) { + client.eval('return 100.5', 0, helper.isNumber(100, done)) + }) - afterEach(function () { - client.end(true); - }); + it('returns a string', function (done) { + client.eval('return \'hello world\'', 0, helper.isString('hello world', done)) + }) - it('converts a float to an integer when evaluated', function (done) { - client.eval('return 100.5', 0, helper.isNumber(100, done)); - }); + it('converts boolean true to integer 1', function (done) { + client.eval('return true', 0, helper.isNumber(1, done)) + }) - it('returns a string', function (done) { - client.eval("return 'hello world'", 0, helper.isString('hello world', done)); - }); + it('converts boolean false to null', function (done) { + client.eval('return false', 0, helper.isNull(done)) + }) - it('converts boolean true to integer 1', function (done) { - client.eval('return true', 0, helper.isNumber(1, done)); - }); + it('converts lua status code to string representation', function (done) { + client.eval('return {ok=\'fine\'}', 0, helper.isString('fine', done)) + }) - it('converts boolean false to null', function (done) { - client.eval('return false', 0, helper.isNull(done)); - }); + it('converts lua error to an error response', function (done) { + client.eval('return {err=\'this is an error\'}', 0, function (err) { + assert(err.code === undefined) + helper.isError()(err) + done() + }) + }) - it('converts lua status code to string representation', function (done) { - client.eval("return {ok='fine'}", 0, helper.isString('fine', done)); - }); + it('represents a lua table appropritely', function (done) { + client.eval('return {1,2,3,\'ciao\',{1,2}}', 0, function (err, res) { + assert.strictEqual(err, null) + assert.strictEqual(5, res.length) + assert.strictEqual(1, res[0]) + assert.strictEqual(2, res[1]) + assert.strictEqual(3, res[2]) + assert.strictEqual('ciao', res[3]) + assert.strictEqual(2, res[4].length) + assert.strictEqual(1, res[4][0]) + assert.strictEqual(2, res[4][1]) + return done() + }) + }) - it('converts lua error to an error response', function (done) { - client.eval("return {err='this is an error'}", 0, function (err) { - assert(err.code === undefined); - helper.isError()(err); - done(); - }); - }); + it('populates keys and argv correctly', function (done) { + client.eval('return {KEYS[1],KEYS[2],ARGV[1],ARGV[2]}', 2, 'a', 'b', 'c', 'd', helper.isDeepEqual(['a', 'b', 'c', 'd'], done)) + }) - it('represents a lua table appropritely', function (done) { - client.eval("return {1,2,3,'ciao',{1,2}}", 0, function (err, res) { - assert.strictEqual(5, res.length); - assert.strictEqual(1, res[0]); - assert.strictEqual(2, res[1]); - assert.strictEqual(3, res[2]); - assert.strictEqual('ciao', res[3]); - assert.strictEqual(2, res[4].length); - assert.strictEqual(1, res[4][0]); - assert.strictEqual(2, res[4][1]); - return done(); - }); - }); + it('allows arguments to be provided in array rather than as multiple parameters', function (done) { + client.eval(['return {KEYS[1],KEYS[2],ARGV[1],ARGV[2]}', 2, 'a', 'b', 'c', 'd'], helper.isDeepEqual(['a', 'b', 'c', 'd'], done)) + }) - it('populates keys and argv correctly', function (done) { - client.eval('return {KEYS[1],KEYS[2],ARGV[1],ARGV[2]}', 2, 'a', 'b', 'c', 'd', function (err, res) { - assert.strictEqual(4, res.length); - assert.strictEqual('a', res[0]); - assert.strictEqual('b', res[1]); - assert.strictEqual('c', res[2]); - assert.strictEqual('d', res[3]); - return done(); - }); - }); + it('allows a script to be executed that accesses the redis API without callback', function (done) { + client.eval(source, 0) + client.get('sha', helper.isString('test', done)) + }) - it('allows arguments to be provided in array rather than as multiple parameters', function (done) { - client.eval(['return {KEYS[1],KEYS[2],ARGV[1],ARGV[2]}', 2, 'a', 'b', 'c', 'd'], function (err, res) { - assert.strictEqual(4, res.length); - assert.strictEqual('a', res[0]); - assert.strictEqual('b', res[1]); - assert.strictEqual('c', res[2]); - assert.strictEqual('d', res[3]); - return done(); - }); - }); + describe('evalsha', function () { + var sha = crypto.createHash('sha1').update(source).digest('hex') - it('allows a script to be executed that accesses the redis API without callback', function (done) { - client.eval(source, 0); - client.get('sha', helper.isString('test', done)); - }); + it('allows a script to be executed that accesses the redis API', function (done) { + client.eval(source, 0, helper.isString('OK')) + client.get('sha', helper.isString('test', done)) + }) - describe('evalsha', function () { - var sha = crypto.createHash('sha1').update(source).digest('hex'); + it('can execute a script if the SHA exists', function (done) { + client.evalsha(sha, 0, helper.isString('OK')) + client.get('sha', helper.isString('test', done)) + }) - it('allows a script to be executed that accesses the redis API', function (done) { - client.eval(source, 0, helper.isString('OK')); - client.get('sha', helper.isString('test', done)); - }); + it('returns an error if SHA does not exist', function (done) { + client.evalsha('ffffffffffffffffffffffffffffffffffffffff', 0, helper.isError(done)) + }) - it('can execute a script if the SHA exists', function (done) { - client.evalsha(sha, 0, helper.isString('OK')); - client.get('sha', helper.isString('test', done)); - }); + it('emit an error if SHA does not exist without any callback', function (done) { + client.evalsha('ffffffffffffffffffffffffffffffffffffffff', 0) + client.on('error', function (err) { + assert.strictEqual(err.code, 'NOSCRIPT') + assert(/NOSCRIPT No matching script. Please use EVAL./.test(err.message)) + done() + }) + }) - it('returns an error if SHA does not exist', function (done) { - client.evalsha('ffffffffffffffffffffffffffffffffffffffff', 0, helper.isError(done)); - }); + it('emits an error if SHA does not exist and no callback has been provided', function (done) { + client.on('error', function (err) { + assert.strictEqual(err.message, 'NOSCRIPT No matching script. Please use EVAL.') + done() + }) + client.evalsha('ffffffffffffffffffffffffffffffffffffffff', 0) + }) + }) - it('emit an error if SHA does not exist without any callback', function (done) { - client.evalsha('ffffffffffffffffffffffffffffffffffffffff', 0); - client.on('error', function (err) { - assert.equal(err.code, 'NOSCRIPT'); - assert(/NOSCRIPT No matching script. Please use EVAL./.test(err.message)); - done(); - }); - }); + it('allows a key to be incremented, and performs appropriate conversion from LUA type', function (done) { + client.set('incr key', 0, function (err, reply) { + if (err) return done(err) + client.eval('local foo = redis.call(\'incr\',\'incr key\')\nreturn {type(foo),foo}', 0, function (err, res) { + assert.strictEqual(2, res.length) + assert.strictEqual('number', res[0]) + assert.strictEqual(1, res[1]) + return done(err) + }) + }) + }) - it('emits an error if SHA does not exist and no callback has been provided', function (done) { - client.on('error', function (err) { - assert.equal(err.message, 'NOSCRIPT No matching script. Please use EVAL.'); - done(); - }); - client.evalsha('ffffffffffffffffffffffffffffffffffffffff', 0); - }); - }); + it('allows a bulk operation to be performed, and performs appropriate conversion from LUA type', function (done) { + client.set('bulk reply key', 'bulk reply value', function (err, res) { + assert.strictEqual(err, null) + client.eval('local foo = redis.call(\'get\',\'bulk reply key\'); return {type(foo),foo}', 0, function (err, res) { + assert.strictEqual(2, res.length) + assert.strictEqual('string', res[0]) + assert.strictEqual('bulk reply value', res[1]) + return done(err) + }) + }) + }) - it('allows a key to be incremented, and performs appropriate conversion from LUA type', function (done) { - client.set('incr key', 0, function (err, reply) { - if (err) return done(err); - client.eval("local foo = redis.call('incr','incr key')\nreturn {type(foo),foo}", 0, function (err, res) { - assert.strictEqual(2, res.length); - assert.strictEqual('number', res[0]); - assert.strictEqual(1, res[1]); - return done(err); - }); - }); - }); + it('allows a multi mulk operation to be performed, with the appropriate type conversion', function (done) { + client.multi() + .del('mylist') + .rpush('mylist', 'a') + .rpush('mylist', 'b') + .rpush('mylist', 'c') + .exec(function (err, replies) { + if (err) return done(err) + client.eval('local foo = redis.call(\'lrange\',\'mylist\',0,-1); return {type(foo),foo[1],foo[2],foo[3],# foo}', 0, function (err, res) { + assert.strictEqual(5, res.length) + assert.strictEqual('table', res[0]) + assert.strictEqual('a', res[1]) + assert.strictEqual('b', res[2]) + assert.strictEqual('c', res[3]) + assert.strictEqual(3, res[4]) + return done(err) + }) + }) + }) - it('allows a bulk operation to be performed, and performs appropriate conversion from LUA type', function (done) { - client.set('bulk reply key', 'bulk reply value', function (err, res) { - client.eval("local foo = redis.call('get','bulk reply key'); return {type(foo),foo}", 0, function (err, res) { - assert.strictEqual(2, res.length); - assert.strictEqual('string', res[0]); - assert.strictEqual('bulk reply value', res[1]); - return done(err); - }); - }); - }); + it('returns an appropriate representation of Lua status reply', function (done) { + client.eval('local foo = redis.call(\'set\',\'mykey\',\'myval\'); return {type(foo),foo[\'ok\']}', 0, function (err, res) { + assert.strictEqual(2, res.length) + assert.strictEqual('table', res[0]) + assert.strictEqual('OK', res[1]) + return done(err) + }) + }) - it('allows a multi mulk operation to be performed, with the appropriate type conversion', function (done) { - client.multi() - .del('mylist') - .rpush('mylist', 'a') - .rpush('mylist', 'b') - .rpush('mylist', 'c') - .exec(function (err, replies) { - if (err) return done(err); - client.eval("local foo = redis.call('lrange','mylist',0,-1); return {type(foo),foo[1],foo[2],foo[3],# foo}", 0, function (err, res) { - assert.strictEqual(5, res.length); - assert.strictEqual('table', res[0]); - assert.strictEqual('a', res[1]); - assert.strictEqual('b', res[2]); - assert.strictEqual('c', res[3]); - assert.strictEqual(3, res[4]); - return done(err); - }); - }); - }); + it('returns an appropriate representation of a Lua error reply', function (done) { + client.set('error reply key', 'error reply value', function (err, res) { + if (err) return done(err) + client.eval('local foo = redis.pcall(\'incr\',\'error reply key\'); return {type(foo),foo[\'err\']}', 0, function (err, res) { + assert.strictEqual(2, res.length) + assert.strictEqual('table', res[0]) + assert.strictEqual('ERR value is not an integer or out of range', res[1]) + return done(err) + }) + }) + }) - it('returns an appropriate representation of Lua status reply', function (done) { - client.eval("local foo = redis.call('set','mykey','myval'); return {type(foo),foo['ok']}", 0, function (err, res) { - assert.strictEqual(2, res.length); - assert.strictEqual('table', res[0]); - assert.strictEqual('OK', res[1]); - return done(err); - }); - }); - - it('returns an appropriate representation of a Lua error reply', function (done) { - client.set('error reply key', 'error reply value', function (err, res) { - if (err) return done(err); - client.eval("local foo = redis.pcall('incr','error reply key'); return {type(foo),foo['err']}", 0, function (err, res) { - assert.strictEqual(2, res.length); - assert.strictEqual('table', res[0]); - assert.strictEqual('ERR value is not an integer or out of range', res[1]); - return done(err); - }); - }); - }); - - it('returns an appropriate representation of a Lua nil reply', function (done) { - client.del('nil reply key', function (err, res) { - if (err) return done(err); - client.eval("local foo = redis.call('get','nil reply key'); return {type(foo),foo == false}", 0, function (err, res) { - if (err) throw err; - assert.strictEqual(2, res.length); - assert.strictEqual('boolean', res[0]); - assert.strictEqual(1, res[1]); - return done(err); - }); - }); - }); - }); - }); -}); + it('returns an appropriate representation of a Lua nil reply', function (done) { + client.del('nil reply key', function (err, res) { + if (err) return done(err) + client.eval('local foo = redis.call(\'get\',\'nil reply key\'); return {type(foo),foo == false}', 0, function (err, res) { + if (err) throw err + assert.strictEqual(2, res.length) + assert.strictEqual('boolean', res[0]) + assert.strictEqual(1, res[1]) + return done(err) + }) + }) + }) + }) + }) +}) diff --git a/test/commands/exists.spec.js b/test/commands/exists.spec.js index a77405d398..d9a6221bac 100644 --- a/test/commands/exists.spec.js +++ b/test/commands/exists.spec.js @@ -1,40 +1,38 @@ -'use strict'; +'use strict' -var config = require('../lib/config'); -var helper = require('../helper'); -var redis = config.redis; +var config = require('../lib/config') +var helper = require('../helper') +var redis = config.redis -describe("The 'exists' method", function () { +describe('The \'exists\' method', function () { + helper.allTests(function (ip, args) { + describe('using ' + ip, function () { + var client - helper.allTests(function (ip, args) { + beforeEach(function (done) { + client = redis.createClient.apply(null, args) + client.once('ready', function () { + client.flushdb(done) + }) + }) - describe('using ' + ip, function () { - var client; + it('returns 1 if the key exists', function (done) { + client.set('foo', 'bar') + client.exists('foo', helper.isNumber(1, done)) + }) - beforeEach(function (done) { - client = redis.createClient.apply(null, args); - client.once('ready', function () { - client.flushdb(done); - }); - }); + it('returns 1 if the key exists with array syntax', function (done) { + client.set('foo', 'bar') + client.exists(['foo'], helper.isNumber(1, done)) + }) - it('returns 1 if the key exists', function (done) { - client.set('foo', 'bar'); - client.exists('foo', helper.isNumber(1, done)); - }); + it('returns 0 if the key does not exist', function (done) { + client.exists('bar', helper.isNumber(0, done)) + }) - it('returns 1 if the key exists with array syntax', function (done) { - client.set('foo', 'bar'); - client.exists(['foo'], helper.isNumber(1, done)); - }); - - it('returns 0 if the key does not exist', function (done) { - client.exists('bar', helper.isNumber(0, done)); - }); - - afterEach(function () { - client.end(true); - }); - }); - }); -}); + afterEach(function () { + client.end(true) + }) + }) + }) +}) diff --git a/test/commands/expire.spec.js b/test/commands/expire.spec.js index 9723226fc8..e9688ab4b7 100644 --- a/test/commands/expire.spec.js +++ b/test/commands/expire.spec.js @@ -1,42 +1,40 @@ -'use strict'; +'use strict' -var config = require('../lib/config'); -var helper = require('../helper'); -var redis = config.redis; +var config = require('../lib/config') +var helper = require('../helper') +var redis = config.redis -describe("The 'expire' method", function () { +describe('The \'expire\' method', function () { + helper.allTests(function (ip, args) { + describe('using ' + ip, function () { + var client - helper.allTests(function (ip, args) { + beforeEach(function (done) { + client = redis.createClient.apply(null, args) + client.once('ready', function () { + client.flushdb(done) + }) + }) - describe('using ' + ip, function () { - var client; + it('expires key after timeout', function (done) { + client.set(['expiry key', 'bar'], helper.isString('OK')) + client.expire('expiry key', '1', helper.isNumber(1)) + setTimeout(function () { + client.exists(['expiry key'], helper.isNumber(0, done)) + }, 1050) + }) - beforeEach(function (done) { - client = redis.createClient.apply(null, args); - client.once('ready', function () { - client.flushdb(done); - }); - }); + it('expires key after timeout with array syntax', function (done) { + client.set(['expiry key', 'bar'], helper.isString('OK')) + client.expire(['expiry key', '1'], helper.isNumber(1)) + setTimeout(function () { + client.exists(['expiry key'], helper.isNumber(0, done)) + }, 1050) + }) - it('expires key after timeout', function (done) { - client.set(['expiry key', 'bar'], helper.isString('OK')); - client.expire('expiry key', '1', helper.isNumber(1)); - setTimeout(function () { - client.exists(['expiry key'], helper.isNumber(0, done)); - }, 1050); - }); - - it('expires key after timeout with array syntax', function (done) { - client.set(['expiry key', 'bar'], helper.isString('OK')); - client.expire(['expiry key', '1'], helper.isNumber(1)); - setTimeout(function () { - client.exists(['expiry key'], helper.isNumber(0, done)); - }, 1050); - }); - - afterEach(function () { - client.end(true); - }); - }); - }); -}); + afterEach(function () { + client.end(true) + }) + }) + }) +}) diff --git a/test/commands/flushdb.spec.js b/test/commands/flushdb.spec.js index a4f761d375..ffd8c7e4d6 100644 --- a/test/commands/flushdb.spec.js +++ b/test/commands/flushdb.spec.js @@ -1,105 +1,94 @@ -'use strict'; +'use strict' -var assert = require('assert'); -var config = require('../lib/config'); -var helper = require('../helper'); -var redis = config.redis; -var uuid = require('uuid'); +var assert = require('assert') +var config = require('../lib/config') +var helper = require('../helper') +var redis = config.redis +var uuid = require('uuid') -describe("The 'flushdb' method", function () { +describe('The \'flushdb\' method', function () { + helper.allTests(function (ip, args) { + describe('using ' + ip, function () { + var key, key2 - helper.allTests(function (ip, args) { + beforeEach(function () { + key = uuid.v4() + key2 = uuid.v4() + }) - describe('using ' + ip, function () { - var key, key2; + describe('when not connected', function () { + var client - beforeEach(function () { - key = uuid.v4(); - key2 = uuid.v4(); - }); + beforeEach(function (done) { + client = redis.createClient.apply(null, args) + client.once('ready', function () { + client.quit() + }) + client.on('end', done) + }) - describe('when not connected', function () { - var client; + it('reports an error', function (done) { + client.flushdb(function (err, res) { + assert(err.message.match(/The connection is already closed/)) + done() + }) + }) + }) - beforeEach(function (done) { - client = redis.createClient.apply(null, args); - client.once('ready', function () { - client.quit(); - }); - client.on('end', done); - }); + describe('when connected', function () { + var client - it('reports an error', function (done) { - client.flushdb(function (err, res) { - assert(err.message.match(/The connection is already closed/)); - done(); - }); - }); - }); + beforeEach(function (done) { + client = redis.createClient.apply(null, args) + client.once('ready', function () { + done() + }) + }) - describe('when connected', function () { - var client; + afterEach(function () { + client.end(true) + }) - beforeEach(function (done) { - client = redis.createClient.apply(null, args); - client.once('ready', function () { - done(); - }); - }); + describe('when there is data in Redis', function () { + beforeEach(function (done) { + client.mset(key, uuid.v4(), key2, uuid.v4(), helper.isNotError()) + client.dbsize([], function (err, res) { + helper.isType.positiveNumber()(err, res) + assert.strictEqual(res, 2, 'Two keys should have been inserted') + done() + }) + }) - afterEach(function () { - client.end(true); - }); + it('deletes all the keys', function (done) { + client.flushdb(function (err, res) { + assert.strictEqual(err, null) + assert.strictEqual(res, 'OK') + client.mget(key, key2, function (err, res) { + assert.strictEqual(null, err, 'Unexpected error returned') + assert.strictEqual(true, Array.isArray(res), 'Results object should be an array.') + assert.strictEqual(2, res.length, 'Results array should have length 2.') + assert.strictEqual(null, res[0], 'Redis key should have been flushed.') + assert.strictEqual(null, res[1], 'Redis key should have been flushed.') + done(err) + }) + }) + }) - describe('when there is data in Redis', function () { + it('results in a db size of zero', function (done) { + client.flushdb(function (err, res) { + assert.strictEqual(err, null) + client.dbsize([], helper.isNumber(0, done)) + }) + }) - beforeEach(function (done) { - client.mset(key, uuid.v4(), key2, uuid.v4(), helper.isNotError()); - client.dbsize([], function (err, res) { - helper.isType.positiveNumber()(err, res); - assert.equal(res, 2, 'Two keys should have been inserted'); - done(); - }); - }); - - it('deletes all the keys', function (done) { - client.flushdb(function (err, res) { - assert.equal(res, 'OK'); - client.mget(key, key2, function (err, res) { - assert.strictEqual(null, err, 'Unexpected error returned'); - assert.strictEqual(true, Array.isArray(res), 'Results object should be an array.'); - assert.strictEqual(2, res.length, 'Results array should have length 2.'); - assert.strictEqual(null, res[0], 'Redis key should have been flushed.'); - assert.strictEqual(null, res[1], 'Redis key should have been flushed.'); - done(err); - }); - }); - }); - - it('results in a db size of zero', function (done) { - client.flushdb(function (err, res) { - client.dbsize([], function (err, res) { - helper.isNotError()(err, res); - helper.isType.number()(err, res); - assert.strictEqual(0, res, 'Flushing db should result in db size 0'); - done(); - }); - }); - }); - - it('results in a db size of zero without a callback', function (done) { - client.flushdb(); - setTimeout(function (err, res) { - client.dbsize(function (err, res) { - helper.isNotError()(err, res); - helper.isType.number()(err, res); - assert.strictEqual(0, res, 'Flushing db should result in db size 0'); - done(); - }); - }, 25); - }); - }); - }); - }); - }); -}); + it('results in a db size of zero without a callback', function (done) { + client.flushdb() + setTimeout(function () { + client.dbsize(helper.isNumber(0, done)) + }, 25) + }) + }) + }) + }) + }) +}) diff --git a/test/commands/geoadd.spec.js b/test/commands/geoadd.spec.js index b45df7c83a..343676cecb 100644 --- a/test/commands/geoadd.spec.js +++ b/test/commands/geoadd.spec.js @@ -1,35 +1,33 @@ -'use strict'; +'use strict' -var config = require('../lib/config'); -var helper = require('../helper'); -var redis = config.redis; +var config = require('../lib/config') +var helper = require('../helper') +var redis = config.redis -describe("The 'geoadd' method", function () { +describe('The \'geoadd\' method', function () { + helper.allTests(function (ip, args) { + describe('using ' + ip, function () { + var client - helper.allTests(function (ip, args) { + beforeEach(function (done) { + client = redis.createClient.apply(null, args) + client.once('ready', function () { + client.flushdb(done) + }) + }) - describe('using ' + ip, function () { - var client; + it('returns 1 if the key exists', function (done) { + helper.serverVersionAtLeast.call(this, client, [3, 2, 0]) + client.geoadd('mycity:21:0:location', '13.361389', '38.115556', 'COR', function (err, res) { + console.log(err, res) + // geoadd is still in the unstable branch. As soon as it reaches the stable one, activate this test + done() + }) + }) - beforeEach(function (done) { - client = redis.createClient.apply(null, args); - client.once('ready', function () { - client.flushdb(done); - }); - }); - - it('returns 1 if the key exists', function (done) { - helper.serverVersionAtLeast.call(this, client, [3, 2, 0]); - client.geoadd('mycity:21:0:location', '13.361389', '38.115556', 'COR', function (err, res) { - console.log(err, res); - // geoadd is still in the unstable branch. As soon as it reaches the stable one, activate this test - done(); - }); - }); - - afterEach(function () { - client.end(true); - }); - }); - }); -}); + afterEach(function () { + client.end(true) + }) + }) + }) +}) diff --git a/test/commands/get.spec.js b/test/commands/get.spec.js index 7d454fc5c2..d0e5189112 100644 --- a/test/commands/get.spec.js +++ b/test/commands/get.spec.js @@ -1,95 +1,93 @@ -'use strict'; +'use strict' -var assert = require('assert'); -var config = require('../lib/config'); -var helper = require('../helper'); -var redis = config.redis; -var uuid = require('uuid'); +var assert = require('assert') +var config = require('../lib/config') +var helper = require('../helper') +var redis = config.redis +var uuid = require('uuid') -describe("The 'get' method", function () { +describe('The \'get\' method', function () { + helper.allTests(function (ip, args) { + describe('using ' + ip, function () { + var key, value - helper.allTests(function (ip, args) { + beforeEach(function () { + key = uuid.v4() + value = uuid.v4() + }) - describe('using ' + ip, function () { - var key, value; + describe('when not connected', function () { + var client - beforeEach(function () { - key = uuid.v4(); - value = uuid.v4(); - }); + beforeEach(function (done) { + client = redis.createClient.apply(null, args) + client.once('ready', function () { + client.quit() + }) + client.on('end', done) + }) - describe('when not connected', function () { - var client; + it('reports an error', function (done) { + client.get(key, function (err, res) { + assert(err.message.match(/The connection is already closed/)) + done() + }) + }) - beforeEach(function (done) { - client = redis.createClient.apply(null, args); - client.once('ready', function () { - client.quit(); - }); - client.on('end', done); - }); + it('reports an error promisified', function () { + return client.getAsync(key).then(assert, function (err) { + assert(err.message.match(/The connection is already closed/)) + }) + }) + }) - it('reports an error', function (done) { - client.get(key, function (err, res) { - assert(err.message.match(/The connection is already closed/)); - done(); - }); - }); + describe('when connected', function () { + var client - it('reports an error promisified', function () { - return client.getAsync(key).then(assert, function (err) { - assert(err.message.match(/The connection is already closed/)); - }); - }); - }); + beforeEach(function (done) { + client = redis.createClient.apply(null, args) + client.once('ready', function () { + done() + }) + }) - describe('when connected', function () { - var client; + afterEach(function () { + client.end(true) + }) - beforeEach(function (done) { - client = redis.createClient.apply(null, args); - client.once('ready', function () { - done(); - }); - }); + describe('when the key exists in Redis', function () { + beforeEach(function (done) { + client.set(key, value, function (err, res) { + helper.isNotError()(err, res) + done() + }) + }) - afterEach(function () { - client.end(true); - }); + it('gets the value correctly', function (done) { + client.get(key, function (err, res) { + helper.isString(value)(err, res) + done(err) + }) + }) - describe('when the key exists in Redis', function () { - beforeEach(function (done) { - client.set(key, value, function (err, res) { - helper.isNotError()(err, res); - done(); - }); - }); + it('should not throw on a get without callback (even if it\'s not useful)', function (done) { + client.get(key) + client.on('error', function (err) { + throw err + }) + setTimeout(done, 25) + }) + }) - it('gets the value correctly', function (done) { - client.get(key, function (err, res) { - helper.isString(value)(err, res); - done(err); - }); - }); - - it("should not throw on a get without callback (even if it's not useful)", function (done) { - client.get(key); - client.on('error', function (err) { - throw err; - }); - setTimeout(done, 25); - }); - }); - - describe('when the key does not exist in Redis', function () { - it('gets a null value', function (done) { - client.get(key, function (err, res) { - helper.isNull()(err, res); - done(err); - }); - }); - }); - }); - }); - }); -}); + describe('when the key does not exist in Redis', function () { + it('gets a null value', function (done) { + client.get(key, function (err, res) { + helper.isNull()(err, res) + done(err) + }) + }) + }) + }) + }) + }) +}) diff --git a/test/commands/getset.spec.js b/test/commands/getset.spec.js index bda89e1864..ac7eb2ec60 100644 --- a/test/commands/getset.spec.js +++ b/test/commands/getset.spec.js @@ -1,105 +1,103 @@ -'use strict'; +'use strict' -var assert = require('assert'); -var config = require('../lib/config'); -var helper = require('../helper'); -var redis = config.redis; -var uuid = require('uuid'); +var assert = require('assert') +var config = require('../lib/config') +var helper = require('../helper') +var redis = config.redis +var uuid = require('uuid') -describe("The 'getset' method", function () { +describe('The \'getset\' method', function () { + helper.allTests(function (ip, args) { + describe('using ' + ip, function () { + var key, value, value2 - helper.allTests(function (ip, args) { + beforeEach(function () { + key = uuid.v4() + value = uuid.v4() + value2 = uuid.v4() + }) - describe('using ' + ip, function () { - var key, value, value2; + describe('when not connected', function () { + var client - beforeEach(function () { - key = uuid.v4(); - value = uuid.v4(); - value2 = uuid.v4(); - }); + beforeEach(function (done) { + client = redis.createClient.apply(null, args) + client.once('ready', function () { + client.quit() + }) + client.on('end', done) + }) - describe('when not connected', function () { - var client; + it('reports an error', function (done) { + client.get(key, function (err, res) { + assert(err.message.match(/The connection is already closed/)) + done() + }) + }) + }) - beforeEach(function (done) { - client = redis.createClient.apply(null, args); - client.once('ready', function () { - client.quit(); - }); - client.on('end', done); - }); + describe('when connected', function () { + var client - it('reports an error', function (done) { - client.get(key, function (err, res) { - assert(err.message.match(/The connection is already closed/)); - done(); - }); - }); - }); + beforeEach(function (done) { + client = redis.createClient.apply(null, args) + client.once('ready', function () { + done() + }) + }) - describe('when connected', function () { - var client; + afterEach(function () { + client.end(true) + }) - beforeEach(function (done) { - client = redis.createClient.apply(null, args); - client.once('ready', function () { - done(); - }); - }); + describe('when the key exists in Redis', function () { + beforeEach(function (done) { + client.set(key, value, function (err, res) { + helper.isNotError()(err, res) + done() + }) + }) - afterEach(function () { - client.end(true); - }); + it('gets the value correctly', function (done) { + client.getset(key, value2, function (err, res) { + helper.isString(value)(err, res) + client.get(key, function (err, res) { + helper.isString(value2)(err, res) + done(err) + }) + }) + }) - describe('when the key exists in Redis', function () { - beforeEach(function (done) { - client.set(key, value, function (err, res) { - helper.isNotError()(err, res); - done(); - }); - }); + it('gets the value correctly with array syntax', function (done) { + client.getset([key, value2], function (err, res) { + helper.isString(value)(err, res) + client.get(key, function (err, res) { + helper.isString(value2)(err, res) + done(err) + }) + }) + }) - it('gets the value correctly', function (done) { - client.getset(key, value2, function (err, res) { - helper.isString(value)(err, res); - client.get(key, function (err, res) { - helper.isString(value2)(err, res); - done(err); - }); - }); - }); + it('gets the value correctly with array syntax style 2', function (done) { + client.getset(key, [value2], function (err, res) { + helper.isString(value)(err, res) + client.get(key, function (err, res) { + helper.isString(value2)(err, res) + done(err) + }) + }) + }) + }) - it('gets the value correctly with array syntax', function (done) { - client.getset([key, value2], function (err, res) { - helper.isString(value)(err, res); - client.get(key, function (err, res) { - helper.isString(value2)(err, res); - done(err); - }); - }); - }); - - it('gets the value correctly with array syntax style 2', function (done) { - client.getset(key, [value2], function (err, res) { - helper.isString(value)(err, res); - client.get(key, function (err, res) { - helper.isString(value2)(err, res); - done(err); - }); - }); - }); - }); - - describe('when the key does not exist in Redis', function () { - it('gets a null value', function (done) { - client.getset(key, value, function (err, res) { - helper.isNull()(err, res); - done(err); - }); - }); - }); - }); - }); - }); -}); + describe('when the key does not exist in Redis', function () { + it('gets a null value', function (done) { + client.getset(key, value, function (err, res) { + helper.isNull()(err, res) + done(err) + }) + }) + }) + }) + }) + }) +}) diff --git a/test/commands/hgetall.spec.js b/test/commands/hgetall.spec.js index 6e9eb84ed6..b937948b36 100644 --- a/test/commands/hgetall.spec.js +++ b/test/commands/hgetall.spec.js @@ -1,84 +1,82 @@ -'use strict'; +'use strict' -var assert = require('assert'); -var config = require('../lib/config'); -var helper = require('../helper'); -var redis = config.redis; +var Buffer = require('safe-buffer').Buffer +var assert = require('assert') +var config = require('../lib/config') +var helper = require('../helper') +var redis = config.redis -describe("The 'hgetall' method", function () { +describe('The \'hgetall\' method', function () { + helper.allTests(function (ip, args) { + describe('using ' + ip, function () { + var client - helper.allTests(function (ip, args) { + describe('regular client', function () { + beforeEach(function (done) { + client = redis.createClient.apply(null, args) + client.once('ready', function () { + client.flushdb(done) + }) + }) - describe('using ' + ip, function () { - var client; + it('handles simple keys and values', function (done) { + client.hmset(['hosts', 'hasOwnProperty', '1', 'another', '23', 'home', '1234'], helper.isString('OK')) + client.hgetall(['hosts'], function (err, obj) { + assert.strictEqual(3, Object.keys(obj).length) + assert.strictEqual('1', obj.hasOwnProperty.toString()) + assert.strictEqual('23', obj.another.toString()) + assert.strictEqual('1234', obj.home.toString()) + done(err) + }) + }) - describe('regular client', function () { + it('handles fetching keys set using an object', function (done) { + client.batch().hmset('msgTest', { message: 'hello' }, undefined).exec() + client.hgetall('msgTest', function (err, obj) { + assert.strictEqual(1, Object.keys(obj).length) + assert.strictEqual(obj.message, 'hello') + done(err) + }) + }) - beforeEach(function (done) { - client = redis.createClient.apply(null, args); - client.once('ready', function () { - client.flushdb(done); - }); - }); + it('handles fetching a messing key', function (done) { + client.hgetall('missing', function (err, obj) { + assert.strictEqual(null, obj) + done(err) + }) + }) + }) - it('handles simple keys and values', function (done) { - client.hmset(['hosts', 'hasOwnProperty', '1', 'another', '23', 'home', '1234'], helper.isString('OK')); - client.hgetall(['hosts'], function (err, obj) { - assert.strictEqual(3, Object.keys(obj).length); - assert.strictEqual('1', obj.hasOwnProperty.toString()); - assert.strictEqual('23', obj.another.toString()); - assert.strictEqual('1234', obj.home.toString()); - done(err); - }); - }); + describe('binary client', function () { + var client + var args = config.configureClient(ip, { + returnBuffers: true + }) - it('handles fetching keys set using an object', function (done) { - client.batch().hmset('msgTest', { message: 'hello' }, undefined).exec(); - client.hgetall('msgTest', function (err, obj) { - assert.strictEqual(1, Object.keys(obj).length); - assert.strictEqual(obj.message, 'hello'); - done(err); - }); - }); + beforeEach(function (done) { + client = redis.createClient.apply(null, args) + client.once('ready', function () { + client.flushdb(done) + }) + }) - it('handles fetching a messing key', function (done) { - client.hgetall('missing', function (err, obj) { - assert.strictEqual(null, obj); - done(err); - }); - }); - }); + it('returns binary results', function (done) { + client.hmset(['bhosts', 'mjr', '1', 'another', '23', 'home', '1234', Buffer.from([0xAA, 0xBB, 0x00, 0xF0]), Buffer.from([0xCC, 0xDD, 0x00, 0xF0])], helper.isString('OK')) + client.hgetall('bhosts', function (err, obj) { + assert.strictEqual(4, Object.keys(obj).length) + assert.strictEqual('1', obj.mjr.toString()) + assert.strictEqual('23', obj.another.toString()) + assert.strictEqual('1234', obj.home.toString()) + assert.strictEqual((Buffer.from([0xAA, 0xBB, 0x00, 0xF0])).toString('binary'), Object.keys(obj)[3]) + assert.strictEqual((Buffer.from([0xCC, 0xDD, 0x00, 0xF0])).toString('binary'), obj[(Buffer.from([0xAA, 0xBB, 0x00, 0xF0])).toString('binary')].toString('binary')) + return done(err) + }) + }) + }) - describe('binary client', function () { - var client; - var args = config.configureClient(ip, { - returnBuffers: true - }); - - beforeEach(function (done) { - client = redis.createClient.apply(null, args); - client.once('ready', function () { - client.flushdb(done); - }); - }); - - it('returns binary results', function (done) { - client.hmset(['bhosts', 'mjr', '1', 'another', '23', 'home', '1234', new Buffer([0xAA, 0xBB, 0x00, 0xF0]), new Buffer([0xCC, 0xDD, 0x00, 0xF0])], helper.isString('OK')); - client.hgetall('bhosts', function (err, obj) { - assert.strictEqual(4, Object.keys(obj).length); - assert.strictEqual('1', obj.mjr.toString()); - assert.strictEqual('23', obj.another.toString()); - assert.strictEqual('1234', obj.home.toString()); - assert.strictEqual((new Buffer([0xAA, 0xBB, 0x00, 0xF0])).toString('binary'), Object.keys(obj)[3]); - assert.strictEqual((new Buffer([0xCC, 0xDD, 0x00, 0xF0])).toString('binary'), obj[(new Buffer([0xAA, 0xBB, 0x00, 0xF0])).toString('binary')].toString('binary')); - return done(err); - }); - }); - }); - - afterEach(function () { - client.end(true); - }); - }); - }); -}); + afterEach(function () { + client.end(true) + }) + }) + }) +}) diff --git a/test/commands/hincrby.spec.js b/test/commands/hincrby.spec.js index 226ad0bb1e..218f7f3e43 100644 --- a/test/commands/hincrby.spec.js +++ b/test/commands/hincrby.spec.js @@ -1,40 +1,38 @@ -'use strict'; +'use strict' -var config = require('../lib/config'); -var helper = require('../helper'); -var redis = config.redis; +var config = require('../lib/config') +var helper = require('../helper') +var redis = config.redis -describe("The 'hincrby' method", function () { +describe('The \'hincrby\' method', function () { + helper.allTests(function (ip, args) { + describe('using ' + ip, function () { + var client + var hash = 'test hash' - helper.allTests(function (ip, args) { + beforeEach(function (done) { + client = redis.createClient.apply(null, args) + client.once('ready', function () { + client.flushdb(done) + }) + }) - describe('using ' + ip, function () { - var client; - var hash = 'test hash'; + it('increments a key that has already been set', function (done) { + var field = 'field 1' - beforeEach(function (done) { - client = redis.createClient.apply(null, args); - client.once('ready', function () { - client.flushdb(done); - }); - }); + client.hset(hash, field, 33) + client.hincrby(hash, field, 10, helper.isNumber(43, done)) + }) - it('increments a key that has already been set', function (done) { - var field = 'field 1'; + it('increments a key that has not been set', function (done) { + var field = 'field 2' - client.hset(hash, field, 33); - client.hincrby(hash, field, 10, helper.isNumber(43, done)); - }); + client.hincrby(hash, field, 10, helper.isNumber(10, done)) + }) - it('increments a key that has not been set', function (done) { - var field = 'field 2'; - - client.hincrby(hash, field, 10, helper.isNumber(10, done)); - }); - - afterEach(function () { - client.end(true); - }); - }); - }); -}); + afterEach(function () { + client.end(true) + }) + }) + }) +}) diff --git a/test/commands/hlen.spec.js b/test/commands/hlen.spec.js index 89446a67fa..0bbe2f2a5a 100644 --- a/test/commands/hlen.spec.js +++ b/test/commands/hlen.spec.js @@ -1,38 +1,37 @@ -'use strict'; +'use strict' -var config = require('../lib/config'); -var helper = require('../helper'); -var redis = config.redis; +var Buffer = require('safe-buffer').Buffer +var config = require('../lib/config') +var helper = require('../helper') +var redis = config.redis -describe("The 'hlen' method", function () { +describe('The \'hlen\' method', function () { + helper.allTests(function (ip, args) { + describe('using ' + ip, function () { + var client - helper.allTests(function (ip, args) { + beforeEach(function (done) { + client = redis.createClient.apply(null, args) + client.once('ready', function () { + client.flushdb(done) + }) + }) - describe('using ' + ip, function () { - var client; + it('reports the count of keys', function (done) { + var hash = 'test hash' + var field1 = Buffer.from('0123456789') + var value1 = Buffer.from('abcdefghij') + var field2 = Buffer.from('') + var value2 = Buffer.from('') - beforeEach(function (done) { - client = redis.createClient.apply(null, args); - client.once('ready', function () { - client.flushdb(done); - }); - }); + client.hset(hash, field1, value1, helper.isNumber(1)) + client.hset(hash, field2, value2, helper.isNumber(1)) + client.hlen(hash, helper.isNumber(2, done)) + }) - it('reports the count of keys', function (done) { - var hash = 'test hash'; - var field1 = new Buffer('0123456789'); - var value1 = new Buffer('abcdefghij'); - var field2 = new Buffer(0); - var value2 = new Buffer(0); - - client.hset(hash, field1, value1, helper.isNumber(1)); - client.hset(hash, field2, value2, helper.isNumber(1)); - client.hlen(hash, helper.isNumber(2, done)); - }); - - afterEach(function () { - client.end(true); - }); - }); - }); -}); + afterEach(function () { + client.end(true) + }) + }) + }) +}) diff --git a/test/commands/hmget.spec.js b/test/commands/hmget.spec.js index c1944a478f..25c0d351e5 100644 --- a/test/commands/hmget.spec.js +++ b/test/commands/hmget.spec.js @@ -1,71 +1,69 @@ -'use strict'; +'use strict' -var assert = require('assert'); -var config = require('../lib/config'); -var helper = require('../helper'); -var redis = config.redis; +var assert = require('assert') +var config = require('../lib/config') +var helper = require('../helper') +var redis = config.redis -describe("The 'hmget' method", function () { +describe('The \'hmget\' method', function () { + helper.allTests(function (ip, args) { + describe('using ' + ip, function () { + var client + var hash = 'test hash' - helper.allTests(function (ip, args) { + beforeEach(function (done) { + client = redis.createClient.apply(null, args) + client.once('error', done) + client.once('ready', function () { + client.flushdb() + client.hmset(hash, {'0123456789': 'abcdefghij', 'some manner of key': 'a type of value'}, helper.isString('OK', done)) + }) + }) - describe('using ' + ip, function () { - var client; - var hash = 'test hash'; + it('allows keys to be specified using multiple arguments', function (done) { + client.hmget(hash, '0123456789', 'some manner of key', function (err, reply) { + assert.strictEqual('abcdefghij', reply[0].toString()) + assert.strictEqual('a type of value', reply[1].toString()) + return done(err) + }) + }) - beforeEach(function (done) { - client = redis.createClient.apply(null, args); - client.once('error', done); - client.once('ready', function () { - client.flushdb(); - client.hmset(hash, {'0123456789': 'abcdefghij', 'some manner of key': 'a type of value'}, helper.isString('OK', done)); - }); - }); + it('allows keys to be specified by passing an array without manipulating the array', function (done) { + var data = ['0123456789', 'some manner of key'] + client.hmget(hash, data, function (err, reply) { + assert.strictEqual(data.length, 2) + assert.strictEqual('abcdefghij', reply[0].toString()) + assert.strictEqual('a type of value', reply[1].toString()) + return done(err) + }) + }) - it('allows keys to be specified using multiple arguments', function (done) { - client.hmget(hash, '0123456789', 'some manner of key', function (err, reply) { - assert.strictEqual('abcdefghij', reply[0].toString()); - assert.strictEqual('a type of value', reply[1].toString()); - return done(err); - }); - }); + it('allows keys to be specified by passing an array as first argument', function (done) { + client.hmget([hash, '0123456789', 'some manner of key'], function (err, reply) { + assert.strictEqual('abcdefghij', reply[0].toString()) + assert.strictEqual('a type of value', reply[1].toString()) + return done(err) + }) + }) - it('allows keys to be specified by passing an array without manipulating the array', function (done) { - var data = ['0123456789', 'some manner of key']; - client.hmget(hash, data, function (err, reply) { - assert.strictEqual(data.length, 2); - assert.strictEqual('abcdefghij', reply[0].toString()); - assert.strictEqual('a type of value', reply[1].toString()); - return done(err); - }); - }); + it('allows a single key to be specified in an array', function (done) { + client.hmget(hash, ['0123456789'], function (err, reply) { + assert.strictEqual('abcdefghij', reply[0].toString()) + return done(err) + }) + }) - it('allows keys to be specified by passing an array as first argument', function (done) { - client.hmget([hash, '0123456789', 'some manner of key'], function (err, reply) { - assert.strictEqual('abcdefghij', reply[0].toString()); - assert.strictEqual('a type of value', reply[1].toString()); - return done(err); - }); - }); + it('allows keys to be specified that have not yet been set', function (done) { + client.hmget(hash, 'missing thing', 'another missing thing', function (err, reply) { + assert.strictEqual(null, reply[0]) + assert.strictEqual(null, reply[1]) + return done(err) + }) + }) - it('allows a single key to be specified in an array', function (done) { - client.hmget(hash, ['0123456789'], function (err, reply) { - assert.strictEqual('abcdefghij', reply[0].toString()); - return done(err); - }); - }); - - it('allows keys to be specified that have not yet been set', function (done) { - client.hmget(hash, 'missing thing', 'another missing thing', function (err, reply) { - assert.strictEqual(null, reply[0]); - assert.strictEqual(null, reply[1]); - return done(err); - }); - }); - - afterEach(function () { - client.end(true); - }); - }); - }); -}); + afterEach(function () { + client.end(true) + }) + }) + }) +}) diff --git a/test/commands/hmset.spec.js b/test/commands/hmset.spec.js index 8b6b1ab58d..6f771cb5ae 100644 --- a/test/commands/hmset.spec.js +++ b/test/commands/hmset.spec.js @@ -1,117 +1,115 @@ -'use strict'; +'use strict' -var assert = require('assert'); -var config = require('../lib/config'); -var helper = require('../helper'); -var redis = config.redis; +var assert = require('assert') +var config = require('../lib/config') +var helper = require('../helper') +var redis = config.redis -describe("The 'hmset' method", function () { +describe('The \'hmset\' method', function () { + helper.allTests(function (ip, args) { + describe('using ' + ip, function () { + var client + var hash = 'test hash' - helper.allTests(function (ip, args) { + beforeEach(function (done) { + client = redis.createClient.apply(null, args) + client.once('ready', function () { + client.flushdb(done) + }) + }) - describe('using ' + ip, function () { - var client; - var hash = 'test hash'; + it('handles redis-style syntax', function (done) { + client.hmset(hash, '0123456789', 'abcdefghij', 'some manner of key', 'a type of value', 'otherTypes', 555, helper.isString('OK')) + client.hgetall(hash, function (err, obj) { + assert.strictEqual(obj['0123456789'], 'abcdefghij') + assert.strictEqual(obj['some manner of key'], 'a type of value') + return done(err) + }) + }) - beforeEach(function (done) { - client = redis.createClient.apply(null, args); - client.once('ready', function () { - client.flushdb(done); - }); - }); + it('handles object-style syntax', function (done) { + client.hmset(hash, {'0123456789': 'abcdefghij', 'some manner of key': 'a type of value', 'otherTypes': 555}, helper.isString('OK')) + client.hgetall(hash, function (err, obj) { + assert.strictEqual(obj['0123456789'], 'abcdefghij') + assert.strictEqual(obj['some manner of key'], 'a type of value') + return done(err) + }) + }) - it('handles redis-style syntax', function (done) { - client.hmset(hash, '0123456789', 'abcdefghij', 'some manner of key', 'a type of value', 'otherTypes', 555, helper.isString('OK')); - client.hgetall(hash, function (err, obj) { - assert.equal(obj['0123456789'], 'abcdefghij'); - assert.equal(obj['some manner of key'], 'a type of value'); - return done(err); - }); - }); + it('handles object-style syntax and the key being a number', function (done) { + client.hmset(231232, {'0123456789': 'abcdefghij', 'some manner of key': 'a type of value', 'otherTypes': 555}, undefined) + client.hgetall(231232, function (err, obj) { + assert.strictEqual(obj['0123456789'], 'abcdefghij') + assert.strictEqual(obj['some manner of key'], 'a type of value') + return done(err) + }) + }) - it('handles object-style syntax', function (done) { - client.hmset(hash, {'0123456789': 'abcdefghij', 'some manner of key': 'a type of value', 'otherTypes': 555}, helper.isString('OK')); - client.hgetall(hash, function (err, obj) { - assert.equal(obj['0123456789'], 'abcdefghij'); - assert.equal(obj['some manner of key'], 'a type of value'); - return done(err); - }); - }); + it('allows a numeric key', function (done) { + client.hmset(hash, 99, 'banana', helper.isString('OK')) + client.hgetall(hash, function (err, obj) { + assert.strictEqual(obj['99'], 'banana') + return done(err) + }) + }) - it('handles object-style syntax and the key being a number', function (done) { - client.hmset(231232, {'0123456789': 'abcdefghij', 'some manner of key': 'a type of value', 'otherTypes': 555}, undefined); - client.hgetall(231232, function (err, obj) { - assert.equal(obj['0123456789'], 'abcdefghij'); - assert.equal(obj['some manner of key'], 'a type of value'); - return done(err); - }); - }); + it('allows a numeric key without callback', function (done) { + client.hmset(hash, 99, 'banana', 'test', 25) + client.hgetall(hash, function (err, obj) { + assert.strictEqual(obj['99'], 'banana') + assert.strictEqual(obj.test, '25') + return done(err) + }) + }) - it('allows a numeric key', function (done) { - client.hmset(hash, 99, 'banana', helper.isString('OK')); - client.hgetall(hash, function (err, obj) { - assert.equal(obj['99'], 'banana'); - return done(err); - }); - }); + it('allows an array without callback', function (done) { + client.hmset([hash, 99, 'banana', 'test', 25]) + client.hgetall(hash, function (err, obj) { + assert.strictEqual(obj['99'], 'banana') + assert.strictEqual(obj.test, '25') + return done(err) + }) + }) - it('allows a numeric key without callback', function (done) { - client.hmset(hash, 99, 'banana', 'test', 25); - client.hgetall(hash, function (err, obj) { - assert.equal(obj['99'], 'banana'); - assert.equal(obj.test, '25'); - return done(err); - }); - }); + it('allows an array and a callback', function (done) { + client.hmset([hash, 99, 'banana', 'test', 25], helper.isString('OK')) + client.hgetall(hash, function (err, obj) { + assert.strictEqual(obj['99'], 'banana') + assert.strictEqual(obj.test, '25') + return done(err) + }) + }) - it('allows an array without callback', function (done) { - client.hmset([hash, 99, 'banana', 'test', 25]); - client.hgetall(hash, function (err, obj) { - assert.equal(obj['99'], 'banana'); - assert.equal(obj.test, '25'); - return done(err); - }); - }); + it('allows a key plus array without callback', function (done) { + client.hmset(hash, [99, 'banana', 'test', 25]) + client.hgetall(hash, function (err, obj) { + assert.strictEqual(obj['99'], 'banana') + assert.strictEqual(obj.test, '25') + return done(err) + }) + }) - it('allows an array and a callback', function (done) { - client.hmset([hash, 99, 'banana', 'test', 25], helper.isString('OK')); - client.hgetall(hash, function (err, obj) { - assert.equal(obj['99'], 'banana'); - assert.equal(obj.test, '25'); - return done(err); - }); - }); + it('allows a key plus array and a callback', function (done) { + client.hmset(hash, [99, 'banana', 'test', 25], helper.isString('OK')) + client.hgetall(hash, function (err, obj) { + assert.strictEqual(obj['99'], 'banana') + assert.strictEqual(obj.test, '25') + return done(err) + }) + }) - it('allows a key plus array without callback', function (done) { - client.hmset(hash, [99, 'banana', 'test', 25]); - client.hgetall(hash, function (err, obj) { - assert.equal(obj['99'], 'banana'); - assert.equal(obj.test, '25'); - return done(err); - }); - }); + it('handles object-style syntax without callback', function (done) { + client.hmset(hash, {'0123456789': 'abcdefghij', 'some manner of key': 'a type of value'}) + client.hgetall(hash, function (err, obj) { + assert.strictEqual(obj['0123456789'], 'abcdefghij') + assert.strictEqual(obj['some manner of key'], 'a type of value') + return done(err) + }) + }) - it('allows a key plus array and a callback', function (done) { - client.hmset(hash, [99, 'banana', 'test', 25], helper.isString('OK')); - client.hgetall(hash, function (err, obj) { - assert.equal(obj['99'], 'banana'); - assert.equal(obj.test, '25'); - return done(err); - }); - }); - - it('handles object-style syntax without callback', function (done) { - client.hmset(hash, {'0123456789': 'abcdefghij', 'some manner of key': 'a type of value'}); - client.hgetall(hash, function (err, obj) { - assert.equal(obj['0123456789'], 'abcdefghij'); - assert.equal(obj['some manner of key'], 'a type of value'); - return done(err); - }); - }); - - afterEach(function () { - client.end(true); - }); - }); - }); -}); + afterEach(function () { + client.end(true) + }) + }) + }) +}) diff --git a/test/commands/hset.spec.js b/test/commands/hset.spec.js index 716029de1c..1494126037 100644 --- a/test/commands/hset.spec.js +++ b/test/commands/hset.spec.js @@ -1,91 +1,91 @@ -'use strict'; +'use strict' -var assert = require('assert'); -var config = require('../lib/config'); -var helper = require('../helper'); -var redis = config.redis; +var Buffer = require('safe-buffer').Buffer +var assert = require('assert') +var config = require('../lib/config') +var helper = require('../helper') +var redis = config.redis -describe("The 'hset' method", function () { +describe('The \'hset\' method', function () { + helper.allTests(function (ip, args) { + describe('using ' + ip, function () { + var client + var hash = 'test hash' - helper.allTests(function (ip, args) { + beforeEach(function (done) { + client = redis.createClient.apply(null, args) + client.once('ready', function () { + client.flushdb(done) + }) + }) - describe('using ' + ip, function () { - var client; - var hash = 'test hash'; + it('allows a value to be set in a hash', function (done) { + var field = Buffer.from('0123456789') + var value = Buffer.from('abcdefghij') - beforeEach(function (done) { - client = redis.createClient.apply(null, args); - client.once('ready', function () { - client.flushdb(done); - }); - }); + client.hset(hash, field, value, helper.isNumber(1)) + client.hget(hash, field, helper.isString(value.toString(), done)) + }) - it('allows a value to be set in a hash', function (done) { - var field = new Buffer('0123456789'); - var value = new Buffer('abcdefghij'); + it('handles an empty value', function (done) { + var field = Buffer.from('0123456789') + var value = Buffer.from('') - client.hset(hash, field, value, helper.isNumber(1)); - client.hget(hash, field, helper.isString(value.toString(), done)); - }); + client.hset(hash, field, value, helper.isNumber(1)) + client.hget([hash, field], helper.isString('', done)) + }) - it('handles an empty value', function (done) { - var field = new Buffer('0123456789'); - var value = new Buffer(0); + it('handles empty key and value', function (done) { + var field = Buffer.from('') + var value = Buffer.from('') + client.hset([hash, field, value], function (err, res) { + assert.strictEqual(err, null) + assert.strictEqual(res, 1) + client.hset(hash, field, value, helper.isNumber(0, done)) + }) + }) - client.hset(hash, field, value, helper.isNumber(1)); - client.hget([hash, field], helper.isString('', done)); - }); + it('warns if someone passed a array either as field or as value', function (done) { + var hash = 'test hash' + var field = 'array' + // This would be converted to "array contents" but if you use more than one entry, + // it'll result in e.g. "array contents,second content" and this is not supported and considered harmful + var value = ['array contents'] + client.on('warning', function (msg) { + assert.strictEqual( + msg, + 'Deprecated: The HMSET command contains a argument of type Array.\n' + + 'This is converted to "array contents" by using .toString() now and will return an error from v.3.0 on.\n' + + 'Please handle this in your code to make sure everything works as you intended it to.' + ) + done() + }) + client.hmset(hash, field, value) + }) - it('handles empty key and value', function (done) { - var field = new Buffer(0); - var value = new Buffer(0); - client.hset([hash, field, value], function (err, res) { - assert.strictEqual(res, 1); - client.hset(hash, field, value, helper.isNumber(0, done)); - }); - }); + it('does not error when a buffer and date are set as values on the same hash', function (done) { + var hash = 'test hash' + var field1 = 'buffer' + var value1 = Buffer.from('abcdefghij') + var field2 = 'date' + var value2 = new Date() - it('warns if someone passed a array either as field or as value', function (done) { - var hash = 'test hash'; - var field = 'array'; - // This would be converted to "array contents" but if you use more than one entry, - // it'll result in e.g. "array contents,second content" and this is not supported and considered harmful - var value = ['array contents']; - client.on('warning', function (msg) { - assert.strictEqual( - msg, - 'Deprecated: The HMSET command contains a argument of type Array.\n' + - 'This is converted to "array contents" by using .toString() now and will return an error from v.3.0 on.\n' + - 'Please handle this in your code to make sure everything works as you intended it to.' - ); - done(); - }); - client.hmset(hash, field, value); - }); + client.hmset(hash, field1, value1, field2, value2, helper.isString('OK', done)) + }) - it('does not error when a buffer and date are set as values on the same hash', function (done) { - var hash = 'test hash'; - var field1 = 'buffer'; - var value1 = new Buffer('abcdefghij'); - var field2 = 'date'; - var value2 = new Date(); + it('does not error when a buffer and date are set as fields on the same hash', function (done) { + var hash = 'test hash' + var value1 = 'buffer' + var field1 = Buffer.from('abcdefghij') + var value2 = 'date' + var field2 = new Date() - client.hmset(hash, field1, value1, field2, value2, helper.isString('OK', done)); - }); + client.hmset(hash, field1, value1, field2, value2, helper.isString('OK', done)) + }) - it('does not error when a buffer and date are set as fields on the same hash', function (done) { - var hash = 'test hash'; - var value1 = 'buffer'; - var field1 = new Buffer('abcdefghij'); - var value2 = 'date'; - var field2 = new Date(); - - client.hmset(hash, field1, value1, field2, value2, helper.isString('OK', done)); - }); - - afterEach(function () { - client.end(true); - }); - }); - }); -}); + afterEach(function () { + client.end(true) + }) + }) + }) +}) diff --git a/test/commands/incr.spec.js b/test/commands/incr.spec.js index 41bb751aa5..18747bb22f 100644 --- a/test/commands/incr.spec.js +++ b/test/commands/incr.spec.js @@ -1,26 +1,22 @@ -'use strict'; +'use strict' -var assert = require('assert'); -var config = require('../lib/config'); -var helper = require('../helper'); -var redis = config.redis; +var assert = require('assert') +var config = require('../lib/config') +var helper = require('../helper') +var redis = config.redis -describe("The 'incr' method", function () { +describe('The \'incr\' method', function () { + helper.allTests(function (ip, args) { + describe('using ' + ip, function () { + describe('when connected and a value in Redis', function () { + var client + var key = 'ABOVE_SAFE_JAVASCRIPT_INTEGER' - helper.allTests(function (ip, args) { + afterEach(function () { + client.end(true) + }) - describe('using ' + ip, function () { - - describe('when connected and a value in Redis', function () { - - var client; - var key = 'ABOVE_SAFE_JAVASCRIPT_INTEGER'; - - afterEach(function () { - client.end(true); - }); - - /* + /* Number.MAX_SAFE_INTEGER === Math.pow(2, 53) - 1 === 9007199254740991 9007199254740992 -> 9007199254740992 @@ -31,45 +27,45 @@ describe("The 'incr' method", function () { 9007199254740997 -> 9007199254740996 ... */ - it('count above the safe integers as numbers', function (done) { - client = redis.createClient.apply(null, args); - // Set a value to the maximum safe allowed javascript number (2^53) - 1 - client.set(key, Number.MAX_SAFE_INTEGER, helper.isNotError()); - client.incr(key, helper.isNumber(Number.MAX_SAFE_INTEGER + 1)); - client.incr(key, helper.isNumber(Number.MAX_SAFE_INTEGER + 2)); - client.incr(key, helper.isNumber(Number.MAX_SAFE_INTEGER + 3)); - client.incr(key, helper.isNumber(Number.MAX_SAFE_INTEGER + 4)); - client.incr(key, helper.isNumber(Number.MAX_SAFE_INTEGER + 5)); - client.incr(key, function (err, res) { - helper.isNumber(Number.MAX_SAFE_INTEGER + 6)(err, res); - assert.strictEqual(typeof res, 'number'); - }); - client.incr(key, helper.isNumber(Number.MAX_SAFE_INTEGER + 7)); - client.incr(key, helper.isNumber(Number.MAX_SAFE_INTEGER + 8)); - client.incr(key, helper.isNumber(Number.MAX_SAFE_INTEGER + 9)); - client.incr(key, helper.isNumber(Number.MAX_SAFE_INTEGER + 10, done)); - }); + it('count above the safe integers as numbers', function (done) { + client = redis.createClient.apply(null, args) + // Set a value to the maximum safe allowed javascript number (2^53) - 1 + client.set(key, Number.MAX_SAFE_INTEGER, helper.isNotError()) + client.incr(key, helper.isNumber(Number.MAX_SAFE_INTEGER + 1)) + client.incr(key, helper.isNumber(Number.MAX_SAFE_INTEGER + 2)) + client.incr(key, helper.isNumber(Number.MAX_SAFE_INTEGER + 3)) + client.incr(key, helper.isNumber(Number.MAX_SAFE_INTEGER + 4)) + client.incr(key, helper.isNumber(Number.MAX_SAFE_INTEGER + 5)) + client.incr(key, function (err, res) { + helper.isNumber(Number.MAX_SAFE_INTEGER + 6)(err, res) + assert.strictEqual(typeof res, 'number') + }) + client.incr(key, helper.isNumber(Number.MAX_SAFE_INTEGER + 7)) + client.incr(key, helper.isNumber(Number.MAX_SAFE_INTEGER + 8)) + client.incr(key, helper.isNumber(Number.MAX_SAFE_INTEGER + 9)) + client.incr(key, helper.isNumber(Number.MAX_SAFE_INTEGER + 10, done)) + }) - it('count above the safe integers as strings', function (done) { - args[2].stringNumbers = true; - client = redis.createClient.apply(null, args); - // Set a value to the maximum safe allowed javascript number (2^53) - client.set(key, Number.MAX_SAFE_INTEGER, helper.isNotError()); - client.incr(key, helper.isString('9007199254740992')); - client.incr(key, helper.isString('9007199254740993')); - client.incr(key, helper.isString('9007199254740994')); - client.incr(key, helper.isString('9007199254740995')); - client.incr(key, helper.isString('9007199254740996')); - client.incr(key, function (err, res) { - helper.isString('9007199254740997')(err, res); - assert.strictEqual(typeof res, 'string'); - }); - client.incr(key, helper.isString('9007199254740998')); - client.incr(key, helper.isString('9007199254740999')); - client.incr(key, helper.isString('9007199254741000')); - client.incr(key, helper.isString('9007199254741001', done)); - }); - }); - }); - }); -}); + it('count above the safe integers as strings', function (done) { + args[2].stringNumbers = true + client = redis.createClient.apply(null, args) + // Set a value to the maximum safe allowed javascript number (2^53) + client.set(key, Number.MAX_SAFE_INTEGER, helper.isNotError()) + client.incr(key, helper.isString('9007199254740992')) + client.incr(key, helper.isString('9007199254740993')) + client.incr(key, helper.isString('9007199254740994')) + client.incr(key, helper.isString('9007199254740995')) + client.incr(key, helper.isString('9007199254740996')) + client.incr(key, function (err, res) { + helper.isString('9007199254740997')(err, res) + assert.strictEqual(typeof res, 'string') + }) + client.incr(key, helper.isString('9007199254740998')) + client.incr(key, helper.isString('9007199254740999')) + client.incr(key, helper.isString('9007199254741000')) + client.incr(key, helper.isString('9007199254741001', done)) + }) + }) + }) + }) +}) diff --git a/test/commands/info.spec.js b/test/commands/info.spec.js index fda50c0a05..f3c17c709a 100644 --- a/test/commands/info.spec.js +++ b/test/commands/info.spec.js @@ -1,79 +1,78 @@ -'use strict'; +'use strict' -var assert = require('assert'); -var config = require('../lib/config'); -var helper = require('../helper'); -var redis = config.redis; +var assert = require('assert') +var config = require('../lib/config') +var helper = require('../helper') +var redis = config.redis -describe("The 'info' method", function () { +describe('The \'info\' method', function () { + helper.allTests(function (ip, args) { + describe('using ' + ip, function () { + var client - helper.allTests(function (ip, args) { + beforeEach(function (done) { + client = redis.createClient.apply(null, args) + client.once('ready', function () { + client.flushall(done) + }) + }) - describe('using ' + ip, function () { - var client; + afterEach(function () { + client.end(true) + }) - beforeEach(function (done) { - client = redis.createClient.apply(null, args); - client.once('ready', function () { - client.flushall(done); - }); - }); + it('update serverInfo after a info command', function (done) { + client.set('foo', 'bar') + client.info() + client.select(2, function () { + assert.strictEqual(client.serverInfo.db2, undefined) + }) + client.set('foo', 'bar') + client.info() + setTimeout(function () { + assert.strictEqual(typeof client.serverInfo.db2, 'object') + done() + }, 30) + }) - afterEach(function () { - client.end(true); - }); + it('works with optional section provided with and without callback', function (done) { + client.set('foo', 'bar') + client.info('keyspace') + client.select(2, function () { + assert.strictEqual(Object.keys(client.serverInfo).length, 2, 'Key length should be three') + assert.strictEqual(typeof client.serverInfo.db0, 'object', 'db0 keyspace should be an object') + }) + client.info(['keyspace']) + client.set('foo', 'bar') + client.info('all', function (err, res) { + assert.strictEqual(err, null) + assert(Object.keys(client.serverInfo).length > 3, 'Key length should be way above three') + assert.strictEqual(typeof client.serverInfo.redis_version, 'string') + assert.strictEqual(typeof client.serverInfo.db2, 'object') + done() + }) + }) - it('update serverInfo after a info command', function (done) { - client.set('foo', 'bar'); - client.info(); - client.select(2, function () { - assert.strictEqual(client.serverInfo.db2, undefined); - }); - client.set('foo', 'bar'); - client.info(); - setTimeout(function () { - assert.strictEqual(typeof client.serverInfo.db2, 'object'); - done(); - }, 30); - }); + it('check redis v.2.4 support', function (done) { + var end = helper.callFuncAfter(done, 2) + client.internalSendCommand = function (commandObj) { + assert.strictEqual(commandObj.args.length, 0) + assert.strictEqual(commandObj.command, 'info') + end() + } + client.info() + client.info(function () {}) + }) - it('works with optional section provided with and without callback', function (done) { - client.set('foo', 'bar'); - client.info('keyspace'); - client.select(2, function () { - assert.strictEqual(Object.keys(client.serverInfo).length, 2, 'Key length should be three'); - assert.strictEqual(typeof client.serverInfo.db0, 'object', 'db0 keyspace should be an object'); - }); - client.info(['keyspace']); - client.set('foo', 'bar'); - client.info('all', function (err, res) { - assert(Object.keys(client.serverInfo).length > 3, 'Key length should be way above three'); - assert.strictEqual(typeof client.serverInfo.redis_version, 'string'); - assert.strictEqual(typeof client.serverInfo.db2, 'object'); - done(); - }); - }); - - it('check redis v.2.4 support', function (done) { - var end = helper.callFuncAfter(done, 2); - client.internalSendCommand = function (commandObj) { - assert.strictEqual(commandObj.args.length, 0); - assert.strictEqual(commandObj.command, 'info'); - end(); - }; - client.info(); - client.info(function () {}); - }); - - it('emit error after a failure', function (done) { - client.info(); - client.once('error', function (err) { - assert.strictEqual(err.code, 'UNCERTAIN_STATE'); - assert.strictEqual(err.command, 'INFO'); - done(); - }); - client.stream.destroy(); - }); - }); - }); -}); + it('emit error after a failure', function (done) { + client.info() + client.once('error', function (err) { + assert.strictEqual(err.code, 'UNCERTAIN_STATE') + assert.strictEqual(err.command, 'INFO') + done() + }) + client.stream.destroy() + }) + }) + }) +}) diff --git a/test/commands/keys.spec.js b/test/commands/keys.spec.js index 9bb9e2345c..94582d2072 100644 --- a/test/commands/keys.spec.js +++ b/test/commands/keys.spec.js @@ -1,69 +1,67 @@ -'use strict'; +'use strict' -var assert = require('assert'); -var config = require('../lib/config'); -var crypto = require('crypto'); -var helper = require('../helper'); -var redis = config.redis; +var assert = require('assert') +var config = require('../lib/config') +var crypto = require('crypto') +var helper = require('../helper') +var redis = config.redis -describe("The 'keys' method", function () { +describe('The \'keys\' method', function () { + helper.allTests(function (ip, args) { + describe('using ' + ip, function () { + var client - helper.allTests(function (ip, args) { + beforeEach(function (done) { + client = redis.createClient.apply(null, args) + client.once('ready', function () { + client.flushall(done) + }) + }) - describe('using ' + ip, function () { - var client; + it('returns matching keys', function (done) { + client.mset(['test keys 1', 'test val 1', 'test keys 2', 'test val 2'], helper.isString('OK')) + client.keys('test keys*', function (err, results) { + assert.strictEqual(2, results.length) + assert.ok(~results.indexOf('test keys 1')) + assert.ok(~results.indexOf('test keys 2')) + return done(err) + }) + }) - beforeEach(function (done) { - client = redis.createClient.apply(null, args); - client.once('ready', function () { - client.flushall(done); - }); - }); + it('handles a large packet size', function (done) { + var keysValues = [] - it('returns matching keys', function (done) { - client.mset(['test keys 1', 'test val 1', 'test keys 2', 'test val 2'], helper.isString('OK')); - client.keys('test keys*', function (err, results) { - assert.strictEqual(2, results.length); - assert.ok(~results.indexOf('test keys 1')); - assert.ok(~results.indexOf('test keys 2')); - return done(err); - }); - }); + for (var i = 0; i < 200; i++) { + var keyValue = [ + 'multibulk:' + crypto.randomBytes(256).toString('hex'), // use long strings as keys to ensure generation of large packet + 'test val ' + i + ] + keysValues.push(keyValue) + } - it('handles a large packet size', function (done) { - var keysValues = []; + client.mset(keysValues.reduce(function (a, b) { + return a.concat(b) + }), helper.isString('OK')) - for (var i = 0; i < 200; i++) { - var keyValue = [ - 'multibulk:' + crypto.randomBytes(256).toString('hex'), // use long strings as keys to ensure generation of large packet - 'test val ' + i - ]; - keysValues.push(keyValue); - } + client.keys('multibulk:*', function (err, results) { + assert.deepEqual(keysValues.map(function (val) { + return val[0] + }).sort(), results.sort()) + return done(err) + }) + }) - client.mset(keysValues.reduce(function (a, b) { - return a.concat(b); - }), helper.isString('OK')); + it('handles an empty response', function (done) { + client.keys(['users:*'], function (err, results) { + assert.strictEqual(results.length, 0) + assert.ok(Array.isArray(results)) + return done(err) + }) + }) - client.keys('multibulk:*', function (err, results) { - assert.deepEqual(keysValues.map(function (val) { - return val[0]; - }).sort(), results.sort()); - return done(err); - }); - }); - - it('handles an empty response', function (done) { - client.keys(['users:*'], function (err, results) { - assert.strictEqual(results.length, 0); - assert.ok(Array.isArray(results)); - return done(err); - }); - }); - - afterEach(function () { - client.end(true); - }); - }); - }); -}); + afterEach(function () { + client.end(true) + }) + }) + }) +}) diff --git a/test/commands/mget.spec.js b/test/commands/mget.spec.js index b4846ff007..05ce93e5bb 100644 --- a/test/commands/mget.spec.js +++ b/test/commands/mget.spec.js @@ -1,70 +1,68 @@ -'use strict'; +'use strict' -var assert = require('assert'); -var config = require('../lib/config'); -var helper = require('../helper'); -var redis = config.redis; +var assert = require('assert') +var config = require('../lib/config') +var helper = require('../helper') +var redis = config.redis -describe("The 'mget' method", function () { +describe('The \'mget\' method', function () { + helper.allTests(function (ip, args) { + describe('using ' + ip, function () { + var client - helper.allTests(function (ip, args) { + beforeEach(function (done) { + client = redis.createClient.apply(null, args) + client.once('error', done) + client.once('ready', function () { + client.flushdb() + client.mset(['mget keys 1', 'mget val 1', 'mget keys 2', 'mget val 2', 'mget keys 3', 'mget val 3'], done) + }) + }) - describe('using ' + ip, function () { - var client; + it('handles fetching multiple keys in argument form', function (done) { + client.mset(['mget keys 1', 'mget val 1', 'mget keys 2', 'mget val 2', 'mget keys 3', 'mget val 3'], helper.isString('OK')) + client.mget('mget keys 1', 'mget keys 2', 'mget keys 3', function (err, results) { + assert.strictEqual(3, results.length) + assert.strictEqual('mget val 1', results[0].toString()) + assert.strictEqual('mget val 2', results[1].toString()) + assert.strictEqual('mget val 3', results[2].toString()) + return done(err) + }) + }) - beforeEach(function (done) { - client = redis.createClient.apply(null, args); - client.once('error', done); - client.once('ready', function () { - client.flushdb(); - client.mset(['mget keys 1', 'mget val 1', 'mget keys 2', 'mget val 2', 'mget keys 3', 'mget val 3'], done); - }); - }); + it('handles fetching multiple keys via an array', function (done) { + client.mget(['mget keys 1', 'mget keys 2', 'mget keys 3'], function (err, results) { + assert.strictEqual('mget val 1', results[0].toString()) + assert.strictEqual('mget val 2', results[1].toString()) + assert.strictEqual('mget val 3', results[2].toString()) + return done(err) + }) + }) - it('handles fetching multiple keys in argument form', function (done) { - client.mset(['mget keys 1', 'mget val 1', 'mget keys 2', 'mget val 2', 'mget keys 3', 'mget val 3'], helper.isString('OK')); - client.mget('mget keys 1', 'mget keys 2', 'mget keys 3', function (err, results) { - assert.strictEqual(3, results.length); - assert.strictEqual('mget val 1', results[0].toString()); - assert.strictEqual('mget val 2', results[1].toString()); - assert.strictEqual('mget val 3', results[2].toString()); - return done(err); - }); - }); + it('handles fetching multiple keys, when some keys do not exist', function (done) { + client.mget('mget keys 1', ['some random shit', 'mget keys 2', 'mget keys 3'], function (err, results) { + assert.strictEqual(4, results.length) + assert.strictEqual('mget val 1', results[0].toString()) + assert.strictEqual(null, results[1]) + assert.strictEqual('mget val 2', results[2].toString()) + assert.strictEqual('mget val 3', results[3].toString()) + return done(err) + }) + }) - it('handles fetching multiple keys via an array', function (done) { - client.mget(['mget keys 1', 'mget keys 2', 'mget keys 3'], function (err, results) { - assert.strictEqual('mget val 1', results[0].toString()); - assert.strictEqual('mget val 2', results[1].toString()); - assert.strictEqual('mget val 3', results[2].toString()); - return done(err); - }); - }); + it('handles fetching multiple keys, when some keys do not exist promisified', function () { + return client.mgetAsync('mget keys 1', ['some random shit', 'mget keys 2', 'mget keys 3']).then(function (results) { + assert.strictEqual(4, results.length) + assert.strictEqual('mget val 1', results[0].toString()) + assert.strictEqual(null, results[1]) + assert.strictEqual('mget val 2', results[2].toString()) + assert.strictEqual('mget val 3', results[3].toString()) + }) + }) - it('handles fetching multiple keys, when some keys do not exist', function (done) { - client.mget('mget keys 1', ['some random shit', 'mget keys 2', 'mget keys 3'], function (err, results) { - assert.strictEqual(4, results.length); - assert.strictEqual('mget val 1', results[0].toString()); - assert.strictEqual(null, results[1]); - assert.strictEqual('mget val 2', results[2].toString()); - assert.strictEqual('mget val 3', results[3].toString()); - return done(err); - }); - }); - - it('handles fetching multiple keys, when some keys do not exist promisified', function () { - return client.mgetAsync('mget keys 1', ['some random shit', 'mget keys 2', 'mget keys 3']).then(function (results) { - assert.strictEqual(4, results.length); - assert.strictEqual('mget val 1', results[0].toString()); - assert.strictEqual(null, results[1]); - assert.strictEqual('mget val 2', results[2].toString()); - assert.strictEqual('mget val 3', results[3].toString()); - }); - }); - - afterEach(function () { - client.end(true); - }); - }); - }); -}); + afterEach(function () { + client.end(true) + }) + }) + }) +}) diff --git a/test/commands/monitor.spec.js b/test/commands/monitor.spec.js index ffbb290788..a55f13a745 100644 --- a/test/commands/monitor.spec.js +++ b/test/commands/monitor.spec.js @@ -1,215 +1,215 @@ -'use strict'; +'use strict' -var assert = require('assert'); -var config = require('../lib/config'); -var helper = require('../helper'); -var utils = require('../../lib/utils'); -var redis = config.redis; +var Buffer = require('safe-buffer').Buffer +var assert = require('assert') +var config = require('../lib/config') +var helper = require('../helper') +var utils = require('../../lib/utils') +var redis = config.redis -describe("The 'monitor' method", function () { +describe('The \'monitor\' method', function () { + helper.allTests(function (parser, ip, args) { + var client - helper.allTests(function (parser, ip, args) { + afterEach(function () { + client.end(true) + }) - var client; + beforeEach(function (done) { + client = redis.createClient.apply(null, args) + client.once('connect', function () { + client.flushdb(done) + }) + }) - afterEach(function () { - client.end(true); - }); + it('monitors commands on all redis clients and works in the correct order', function (done) { + var monitorClient = redis.createClient.apply(null, args) + var responses = [ + ['mget', 'some', 'keys', 'foo', 'bar'], + ['set', 'json', '{"foo":"123","bar":"sdflkdfsjk","another":false}'], + ['eval', 'return redis.call(\'set\', \'sha\', \'test\')', '0'], + ['set', 'sha', 'test'], + ['get', 'baz'], + ['set', 'foo', 'bar" "s are " " good!"'], + ['mget', 'foo', 'baz'], + ['subscribe', 'foo', 'baz'] + ] + var end = helper.callFuncAfter(done, 5) - beforeEach(function (done) { - client = redis.createClient.apply(null, args); - client.once('connect', function () { - client.flushdb(done); - }); - }); + monitorClient.set('foo', 'bar') + monitorClient.flushdb() + monitorClient.monitor(function (err, res) { + assert.strictEqual(err, null) + assert.strictEqual(res, 'OK') + client.mget('some', 'keys', 'foo', 'bar') + client.set('json', JSON.stringify({ + foo: '123', + bar: 'sdflkdfsjk', + another: false + })) + client.eval('return redis.call(\'set\', \'sha\', \'test\')', 0) + monitorClient.get('baz', function (err, res) { + assert.strictEqual(res, null) + end(err) + }) + monitorClient.set('foo', 'bar" "s are " " good!"', function (err, res) { + assert.strictEqual(res, 'OK') + end(err) + }) + monitorClient.mget('foo', 'baz', function (err, res) { + assert.strictEqual(res[0], 'bar" "s are " " good!"') + assert.strictEqual(res[1], null) + end(err) + }) + monitorClient.subscribe('foo', 'baz', function (err, res) { + // The return value might change in v.3 + // assert.strictEqual(res, 'baz'); + // TODO: Fix the return value of subscribe calls + end(err) + }) + }) - it('monitors commands on all redis clients and works in the correct order', function (done) { - var monitorClient = redis.createClient.apply(null, args); - var responses = [ - ['mget', 'some', 'keys', 'foo', 'bar'], - ['set', 'json', '{"foo":"123","bar":"sdflkdfsjk","another":false}'], - ['eval', "return redis.call('set', 'sha', 'test')", '0'], - ['set', 'sha', 'test'], - ['get', 'baz'], - ['set', 'foo', 'bar" "s are " " good!"'], - ['mget', 'foo', 'baz'], - ['subscribe', 'foo', 'baz'] - ]; - var end = helper.callFuncAfter(done, 5); + monitorClient.on('monitor', function (time, args, rawOutput) { + assert.strictEqual(monitorClient.monitoring, true) + assert.deepEqual(args, responses.shift()) + assert(utils.monitorRegex.test(rawOutput), rawOutput) + if (responses.length === 0) { + monitorClient.quit(end) + } + }) + }) - monitorClient.set('foo', 'bar'); - monitorClient.flushdb(); - monitorClient.monitor(function (err, res) { - assert.strictEqual(res, 'OK'); - client.mget('some', 'keys', 'foo', 'bar'); - client.set('json', JSON.stringify({ - foo: '123', - bar: 'sdflkdfsjk', - another: false - })); - client.eval("return redis.call('set', 'sha', 'test')", 0); - monitorClient.get('baz', function (err, res) { - assert.strictEqual(res, null); - end(err); - }); - monitorClient.set('foo', 'bar" "s are " " good!"', function (err, res) { - assert.strictEqual(res, 'OK'); - end(err); - }); - monitorClient.mget('foo', 'baz', function (err, res) { - assert.strictEqual(res[0], 'bar" "s are " " good!"'); - assert.strictEqual(res[1], null); - end(err); - }); - monitorClient.subscribe('foo', 'baz', function (err, res) { - // The return value might change in v.3 - // assert.strictEqual(res, 'baz'); - // TODO: Fix the return value of subscribe calls - end(err); - }); - }); + it('monitors returns strings in the rawOutput even with returnBuffers activated', function (done) { + if (process.platform === 'win32') { + this.skip() + } + var monitorClient = redis.createClient({ + returnBuffers: true, + path: '/tmp/redis.sock' + }) - monitorClient.on('monitor', function (time, args, rawOutput) { - assert.strictEqual(monitorClient.monitoring, true); - assert.deepEqual(args, responses.shift()); - assert(utils.monitorRegex.test(rawOutput), rawOutput); - if (responses.length === 0) { - monitorClient.quit(end); - } - }); - }); + monitorClient.monitor(function (err, res) { + assert.strictEqual(err, null) + assert.strictEqual(monitorClient.monitoring, true) + assert.strictEqual(res.inspect(), Buffer.from('OK').inspect()) + monitorClient.mget('hello', Buffer.from('world')) + }) - it('monitors returns strings in the rawOutput even with returnBuffers activated', function (done) { - if (process.platform === 'win32') { - this.skip(); - } - var monitorClient = redis.createClient({ - returnBuffers: true, - path: '/tmp/redis.sock' - }); + monitorClient.on('monitor', function (time, args, rawOutput) { + assert.strictEqual(typeof rawOutput, 'string') + assert(utils.monitorRegex.test(rawOutput), rawOutput) + assert.deepEqual(args, ['mget', 'hello', 'world']) + // Quit immediately ends monitoring mode and therefore does not stream back the quit command + monitorClient.quit(done) + }) + }) - monitorClient.monitor(function (err, res) { - assert.strictEqual(monitorClient.monitoring, true); - assert.strictEqual(res.inspect(), new Buffer('OK').inspect()); - monitorClient.mget('hello', new Buffer('world')); - }); + it('monitors reconnects properly and works with the offline queue', function (done) { + var called = false + client.monitor(helper.isString('OK')) + client.mget('hello', 'world') + client.on('monitor', function (time, args, rawOutput) { + assert.strictEqual(client.monitoring, true) + assert(utils.monitorRegex.test(rawOutput), rawOutput) + assert.deepEqual(args, ['mget', 'hello', 'world']) + if (called) { + // End after a reconnect + return done() + } + client.stream.destroy() + client.mget('hello', 'world') + called = true + }) + }) - monitorClient.on('monitor', function (time, args, rawOutput) { - assert.strictEqual(typeof rawOutput, 'string'); - assert(utils.monitorRegex.test(rawOutput), rawOutput); - assert.deepEqual(args, ['mget', 'hello', 'world']); - // Quit immediatly ends monitoring mode and therefore does not stream back the quit command - monitorClient.quit(done); - }); - }); + it('monitors reconnects properly and works with the offline queue in a batch statement', function (done) { + var called = false + var multi = client.batch() + multi.monitor(helper.isString('OK')) + multi.mget('hello', 'world') + multi.exec(helper.isDeepEqual(['OK', [null, null]])) + client.on('monitor', function (time, args, rawOutput) { + assert.strictEqual(client.monitoring, true) + assert(utils.monitorRegex.test(rawOutput), rawOutput) + assert.deepEqual(args, ['mget', 'hello', 'world']) + if (called) { + // End after a reconnect + return done() + } + client.stream.destroy() + client.mget('hello', 'world') + called = true + }) + }) - it('monitors reconnects properly and works with the offline queue', function (done) { - var called = false; - client.monitor(helper.isString('OK')); - client.mget('hello', 'world'); - client.on('monitor', function (time, args, rawOutput) { - assert.strictEqual(client.monitoring, true); - assert(utils.monitorRegex.test(rawOutput), rawOutput); - assert.deepEqual(args, ['mget', 'hello', 'world']); - if (called) { - // End after a reconnect - return done(); - } - client.stream.destroy(); - client.mget('hello', 'world'); - called = true; - }); - }); + it('monitor activates even if the command could not be processed properly after a reconnect', function (done) { + client.monitor(function (err, res) { + assert.strictEqual(err.code, 'UNCERTAIN_STATE') + }) + client.on('error', function () {}) // Ignore error here + client.stream.destroy() + var end = helper.callFuncAfter(done, 2) + client.on('monitor', function (time, args, rawOutput) { + assert.strictEqual(client.monitoring, true) + end() + }) + client.on('reconnecting', function () { + client.get('foo', function (err, res) { + assert(!err) + assert.strictEqual(client.monitoring, true) + end() + }) + }) + }) - it('monitors reconnects properly and works with the offline queue in a batch statement', function (done) { - var called = false; - var multi = client.batch(); - multi.monitor(helper.isString('OK')); - multi.mget('hello', 'world'); - multi.exec(function (err, res) { - assert.deepEqual(res, ['OK', [null, null]]); - }); - client.on('monitor', function (time, args, rawOutput) { - assert.strictEqual(client.monitoring, true); - assert(utils.monitorRegex.test(rawOutput), rawOutput); - assert.deepEqual(args, ['mget', 'hello', 'world']); - if (called) { - // End after a reconnect - return done(); - } - client.stream.destroy(); - client.mget('hello', 'world'); - called = true; - }); - }); - - it('monitor activates even if the command could not be processed properly after a reconnect', function (done) { - client.monitor(function (err, res) { - assert.strictEqual(err.code, 'UNCERTAIN_STATE'); - }); - client.on('error', function (err) {}); // Ignore error here - client.stream.destroy(); - var end = helper.callFuncAfter(done, 2); - client.on('monitor', function (time, args, rawOutput) { - assert.strictEqual(client.monitoring, true); - end(); - }); - client.on('reconnecting', function () { - client.get('foo', function (err, res) { - assert(!err); - assert.strictEqual(client.monitoring, true); - end(); - }); - }); - }); - - it('monitors works in combination with the pub sub mode and the offline queue', function (done) { - var responses = [ - ['subscribe', '/foo', '/bar'], - ['unsubscribe', '/bar'], - ['get', 'foo'], - ['subscribe', '/foo'], - ['subscribe', 'baz'], - ['unsubscribe', 'baz'], - ['publish', '/foo', 'hello world'] - ]; - var pub = redis.createClient(); - pub.on('ready', function () { - client.monitor(function (err, res) { - assert.strictEqual(res, 'OK'); - pub.get('foo', helper.isNull()); - }); - client.subscribe('/foo', '/bar'); - client.unsubscribe('/bar'); - setTimeout(function () { - client.stream.destroy(); - client.once('ready', function () { - pub.publish('/foo', 'hello world'); - }); - client.set('foo', 'bar', helper.isError()); - client.subscribe('baz'); - client.unsubscribe('baz'); - }, 150); - var called = false; - client.on('monitor', function (time, args, rawOutput) { - assert.deepEqual(args, responses.shift()); - assert(utils.monitorRegex.test(rawOutput), rawOutput); - if (responses.length === 0) { - // The publish is called right after the reconnect and the monitor is called before the message is emitted. - // Therefore we have to wait till the next tick - process.nextTick(function () { - assert(called); - client.quit(done); - pub.end(false); - }); - } - }); - client.on('message', function (channel, msg) { - assert.strictEqual(channel, '/foo'); - assert.strictEqual(msg, 'hello world'); - called = true; - }); - }); - }); - }); -}); + it('monitors works in combination with the pub sub mode and the offline queue', function (done) { + var responses = [ + ['subscribe', '/foo', '/bar'], + ['unsubscribe', '/bar'], + ['get', 'foo'], + ['subscribe', '/foo'], + ['subscribe', 'baz'], + ['unsubscribe', 'baz'], + ['publish', '/foo', 'hello world'] + ] + var pub = redis.createClient() + pub.on('ready', function () { + client.monitor(function (err, res) { + assert.strictEqual(err, null) + assert.strictEqual(res, 'OK') + pub.get('foo', helper.isNull()) + }) + client.subscribe('/foo', '/bar') + client.unsubscribe('/bar') + setTimeout(function () { + client.stream.destroy() + client.once('ready', function () { + pub.publish('/foo', 'hello world') + }) + client.set('foo', 'bar', helper.isError()) + client.subscribe('baz') + client.unsubscribe('baz') + }, 150) + var called = false + client.on('monitor', function (time, args, rawOutput) { + assert.deepEqual(args, responses.shift()) + assert(utils.monitorRegex.test(rawOutput), rawOutput) + if (responses.length === 0) { + // The publish is called right after the reconnect and the monitor is called before the message is emitted. + // Therefore we have to wait till the next tick + process.nextTick(function () { + assert(called) + client.quit(done) + pub.end(false) + }) + } + }) + client.on('message', function (channel, msg) { + assert.strictEqual(channel, '/foo') + assert.strictEqual(msg, 'hello world') + called = true + }) + }) + }) + }) +}) diff --git a/test/commands/mset.spec.js b/test/commands/mset.spec.js index b60f383134..c44837e6ec 100644 --- a/test/commands/mset.spec.js +++ b/test/commands/mset.spec.js @@ -1,111 +1,108 @@ -'use strict'; +'use strict' -var assert = require('assert'); -var config = require('../lib/config'); -var helper = require('../helper'); -var redis = config.redis; -var uuid = require('uuid'); +var assert = require('assert') +var config = require('../lib/config') +var helper = require('../helper') +var redis = config.redis +var uuid = require('uuid') -describe("The 'mset' method", function () { +describe('The \'mset\' method', function () { + helper.allTests(function (ip, args) { + describe('using ' + ip, function () { + var key, value, key2, value2 - helper.allTests(function (ip, args) { + beforeEach(function () { + key = uuid.v4() + value = uuid.v4() + key2 = uuid.v4() + value2 = uuid.v4() + }) - describe('using ' + ip, function () { - var key, value, key2, value2; + describe('when not connected', function () { + var client - beforeEach(function () { - key = uuid.v4(); - value = uuid.v4(); - key2 = uuid.v4(); - value2 = uuid.v4(); - }); + beforeEach(function (done) { + client = redis.createClient.apply(null, args) + client.once('ready', function () { + client.quit() + }) + client.on('end', done) + }) - describe('when not connected', function () { - var client; + it('reports an error', function (done) { + client.mset(key, value, key2, value2, function (err, res) { + assert(err.message.match(/The connection is already closed/)) + done() + }) + }) + }) - beforeEach(function (done) { - client = redis.createClient.apply(null, args); - client.once('ready', function () { - client.quit(); - }); - client.on('end', done); - }); + describe('when connected', function () { + var client - it('reports an error', function (done) { - client.mset(key, value, key2, value2, function (err, res) { - assert(err.message.match(/The connection is already closed/)); - done(); - }); - }); - }); + beforeEach(function (done) { + client = redis.createClient.apply(null, args) + client.once('ready', function () { + done() + }) + }) - describe('when connected', function () { - var client; + afterEach(function () { + client.end(true) + }) - beforeEach(function (done) { - client = redis.createClient.apply(null, args); - client.once('ready', function () { - done(); - }); - }); + describe('and a callback is specified', function () { + describe('with valid parameters', function () { + it('sets the value correctly', function (done) { + client.mset(key, value, key2, value2, function (err) { + if (err) { + return done(err) + } + client.get(key, helper.isString(value)) + client.get(key2, helper.isString(value2, done)) + }) + }) + }) - afterEach(function () { - client.end(true); - }); + describe('with undefined \'key\' parameter and missing \'value\' parameter', function () { + it('reports an error', function (done) { + client.mset(undefined, function (err, res) { + helper.isError()(err, null) + done() + }) + }) + }) + }) - describe('and a callback is specified', function () { - describe('with valid parameters', function () { - it('sets the value correctly', function (done) { - client.mset(key, value, key2, value2, function (err) { - if (err) { - return done(err); - } - client.get(key, helper.isString(value)); - client.get(key2, helper.isString(value2, done)); - }); - }); - }); + describe('and no callback is specified', function () { + describe('with valid parameters', function () { + it('sets the value correctly', function (done) { + client.mset(key, value2, key2, value) + client.get(key, helper.isString(value2)) + client.get(key2, helper.isString(value, done)) + }) - describe("with undefined 'key' parameter and missing 'value' parameter", function () { - it('reports an error', function (done) { - client.mset(undefined, function (err, res) { - helper.isError()(err, null); - done(); - }); - }); - }); + it('sets the value correctly with array syntax', function (done) { + client.mset([key, value2, key2, value]) + client.get(key, helper.isString(value2)) + client.get(key2, helper.isString(value, done)) + }) + }) - }); + describe('with undefined \'key\' and missing \'value\' parameter', function () { + // this behavior is different from the 'set' behavior. + it('emits an error', function (done) { + client.on('error', function (err) { + assert.strictEqual(err.message, 'ERR wrong number of arguments for \'mset\' command') + assert.strictEqual(err.name, 'ReplyError') + done() + }) - describe('and no callback is specified', function () { - describe('with valid parameters', function () { - it('sets the value correctly', function (done) { - client.mset(key, value2, key2, value); - client.get(key, helper.isString(value2)); - client.get(key2, helper.isString(value, done)); - }); - - it('sets the value correctly with array syntax', function (done) { - client.mset([key, value2, key2, value]); - client.get(key, helper.isString(value2)); - client.get(key2, helper.isString(value, done)); - }); - }); - - describe("with undefined 'key' and missing 'value' parameter", function () { - // this behavior is different from the 'set' behavior. - it('emits an error', function (done) { - client.on('error', function (err) { - assert.strictEqual(err.message, "ERR wrong number of arguments for 'mset' command"); - assert.strictEqual(err.name, 'ReplyError'); - done(); - }); - - client.mset(); - }); - }); - }); - }); - }); - }); -}); + client.mset() + }) + }) + }) + }) + }) + }) +}) diff --git a/test/commands/msetnx.spec.js b/test/commands/msetnx.spec.js index 40450257ff..fd3682d0e3 100644 --- a/test/commands/msetnx.spec.js +++ b/test/commands/msetnx.spec.js @@ -1,38 +1,36 @@ -'use strict'; +'use strict' -var config = require('../lib/config'); -var helper = require('../helper'); -var redis = config.redis; +var config = require('../lib/config') +var helper = require('../helper') +var redis = config.redis -describe("The 'msetnx' method", function () { +describe('The \'msetnx\' method', function () { + helper.allTests(function (ip, args) { + describe('using ' + ip, function () { + var client - helper.allTests(function (ip, args) { + beforeEach(function (done) { + client = redis.createClient.apply(null, args) + client.once('ready', function () { + client.flushdb(done) + }) + }) - describe('using ' + ip, function () { - var client; + it('if any keys exist entire operation fails', function (done) { + client.mset(['mset1', 'val1', 'mset2', 'val2', 'mset3', 'val3'], helper.isString('OK')) + client.msetnx(['mset3', 'val3', 'mset4', 'val4'], helper.isNumber(0)) + client.exists(['mset4'], helper.isNumber(0, done)) + }) - beforeEach(function (done) { - client = redis.createClient.apply(null, args); - client.once('ready', function () { - client.flushdb(done); - }); - }); + it('sets multiple keys if all keys are not set', function (done) { + client.msetnx(['mset3', 'val3', 'mset4', 'val4'], helper.isNumber(1)) + client.exists(['mset3'], helper.isNumber(1)) + client.exists(['mset3'], helper.isNumber(1, done)) + }) - it('if any keys exist entire operation fails', function (done) { - client.mset(['mset1', 'val1', 'mset2', 'val2', 'mset3', 'val3'], helper.isString('OK')); - client.msetnx(['mset3', 'val3', 'mset4', 'val4'], helper.isNumber(0)); - client.exists(['mset4'], helper.isNumber(0, done)); - }); - - it('sets multiple keys if all keys are not set', function (done) { - client.msetnx(['mset3', 'val3', 'mset4', 'val4'], helper.isNumber(1)); - client.exists(['mset3'], helper.isNumber(1)); - client.exists(['mset3'], helper.isNumber(1, done)); - }); - - afterEach(function () { - client.end(true); - }); - }); - }); -}); + afterEach(function () { + client.end(true) + }) + }) + }) +}) diff --git a/test/commands/randomkey.test.js b/test/commands/randomkey.test.js index 2f475f6eaf..0797f4451c 100644 --- a/test/commands/randomkey.test.js +++ b/test/commands/randomkey.test.js @@ -1,35 +1,33 @@ -'use strict'; +'use strict' -var assert = require('assert'); -var config = require('../lib/config'); -var helper = require('../helper'); -var redis = config.redis; +var assert = require('assert') +var config = require('../lib/config') +var helper = require('../helper') +var redis = config.redis -describe("The 'randomkey' method", function () { +describe('The \'randomkey\' method', function () { + helper.allTests(function (ip, args) { + describe('using ' + ip, function () { + var client - helper.allTests(function (ip, args) { + beforeEach(function (done) { + client = redis.createClient.apply(null, args) + client.once('ready', function () { + client.flushdb(done) + }) + }) - describe('using ' + ip, function () { - var client; + it('returns a random key', function (done) { + client.mset(['test keys 1', 'test val 1', 'test keys 2', 'test val 2'], helper.isString('OK')) + client.randomkey([], function (err, results) { + assert.strictEqual(true, /test keys.+/.test(results)) + return done(err) + }) + }) - beforeEach(function (done) { - client = redis.createClient.apply(null, args); - client.once('ready', function () { - client.flushdb(done); - }); - }); - - it('returns a random key', function (done) { - client.mset(['test keys 1', 'test val 1', 'test keys 2', 'test val 2'], helper.isString('OK')); - client.randomkey([], function (err, results) { - assert.strictEqual(true, /test keys.+/.test(results)); - return done(err); - }); - }); - - afterEach(function () { - client.end(true); - }); - }); - }); -}); + afterEach(function () { + client.end(true) + }) + }) + }) +}) diff --git a/test/commands/rename.spec.js b/test/commands/rename.spec.js index 6af4f18b5f..35919a9a3b 100644 --- a/test/commands/rename.spec.js +++ b/test/commands/rename.spec.js @@ -1,38 +1,36 @@ -'use strict'; +'use strict' -var config = require('../lib/config'); -var helper = require('../helper'); -var redis = config.redis; +var config = require('../lib/config') +var helper = require('../helper') +var redis = config.redis -describe("The 'rename' method", function () { +describe('The \'rename\' method', function () { + helper.allTests(function (ip, args) { + describe('using ' + ip, function () { + var client - helper.allTests(function (ip, args) { + beforeEach(function (done) { + client = redis.createClient.apply(null, args) + client.once('ready', function () { + client.flushdb(done) + }) + }) - describe('using ' + ip, function () { - var client; + it('populates the new key', function (done) { + client.set(['foo', 'bar'], helper.isString('OK')) + client.rename(['foo', 'new foo'], helper.isString('OK')) + client.exists(['new foo'], helper.isNumber(1, done)) + }) - beforeEach(function (done) { - client = redis.createClient.apply(null, args); - client.once('ready', function () { - client.flushdb(done); - }); - }); + it('removes the old key', function (done) { + client.set(['foo', 'bar'], helper.isString('OK')) + client.rename(['foo', 'new foo'], helper.isString('OK')) + client.exists(['foo'], helper.isNumber(0, done)) + }) - it('populates the new key', function (done) { - client.set(['foo', 'bar'], helper.isString('OK')); - client.rename(['foo', 'new foo'], helper.isString('OK')); - client.exists(['new foo'], helper.isNumber(1, done)); - }); - - it('removes the old key', function (done) { - client.set(['foo', 'bar'], helper.isString('OK')); - client.rename(['foo', 'new foo'], helper.isString('OK')); - client.exists(['foo'], helper.isNumber(0, done)); - }); - - afterEach(function () { - client.end(true); - }); - }); - }); -}); + afterEach(function () { + client.end(true) + }) + }) + }) +}) diff --git a/test/commands/renamenx.spec.js b/test/commands/renamenx.spec.js index 3ed644c2d8..d7ecddd57a 100644 --- a/test/commands/renamenx.spec.js +++ b/test/commands/renamenx.spec.js @@ -1,41 +1,39 @@ -'use strict'; +'use strict' -var config = require('../lib/config'); -var helper = require('../helper'); -var redis = config.redis; +var config = require('../lib/config') +var helper = require('../helper') +var redis = config.redis -describe("The 'renamenx' method", function () { +describe('The \'renamenx\' method', function () { + helper.allTests(function (ip, args) { + describe('using ' + ip, function () { + var client - helper.allTests(function (ip, args) { + beforeEach(function (done) { + client = redis.createClient.apply(null, args) + client.once('ready', function () { + client.flushdb(done) + }) + }) - describe('using ' + ip, function () { - var client; + it('renames the key if target does not yet exist', function (done) { + client.set('foo', 'bar', helper.isString('OK')) + client.renamenx('foo', 'foo2', helper.isNumber(1)) + client.exists('foo', helper.isNumber(0)) + client.exists(['foo2'], helper.isNumber(1, done)) + }) - beforeEach(function (done) { - client = redis.createClient.apply(null, args); - client.once('ready', function () { - client.flushdb(done); - }); - }); + it('does not rename the key if the target exists', function (done) { + client.set('foo', 'bar', helper.isString('OK')) + client.set('foo2', 'apple', helper.isString('OK')) + client.renamenx('foo', 'foo2', helper.isNumber(0)) + client.exists('foo', helper.isNumber(1)) + client.exists(['foo2'], helper.isNumber(1, done)) + }) - it('renames the key if target does not yet exist', function (done) { - client.set('foo', 'bar', helper.isString('OK')); - client.renamenx('foo', 'foo2', helper.isNumber(1)); - client.exists('foo', helper.isNumber(0)); - client.exists(['foo2'], helper.isNumber(1, done)); - }); - - it('does not rename the key if the target exists', function (done) { - client.set('foo', 'bar', helper.isString('OK')); - client.set('foo2', 'apple', helper.isString('OK')); - client.renamenx('foo', 'foo2', helper.isNumber(0)); - client.exists('foo', helper.isNumber(1)); - client.exists(['foo2'], helper.isNumber(1, done)); - }); - - afterEach(function () { - client.end(true); - }); - }); - }); -}); + afterEach(function () { + client.end(true) + }) + }) + }) +}) diff --git a/test/commands/rpush.spec.js b/test/commands/rpush.spec.js index 793d5d2d80..c045b2878c 100644 --- a/test/commands/rpush.spec.js +++ b/test/commands/rpush.spec.js @@ -1,36 +1,34 @@ -'use strict'; +'use strict' -var config = require('../lib/config'); -var helper = require('../helper'); -var redis = config.redis; -var assert = require('assert'); +var config = require('../lib/config') +var helper = require('../helper') +var redis = config.redis +var assert = require('assert') -describe("The 'rpush' command", function () { +describe('The \'rpush\' command', function () { + helper.allTests(function (ip, args) { + describe('using ' + ip, function () { + var client - helper.allTests(function (ip, args) { + beforeEach(function (done) { + client = redis.createClient.apply(null, args) + client.once('ready', function () { + client.flushdb(done) + }) + }) - describe('using ' + ip, function () { - var client; + it('inserts multiple values at a time into a list', function (done) { + client.rpush('test', ['list key', 'should be a list']) + client.lrange('test', 0, -1, function (err, res) { + assert.strictEqual(res[0], 'list key') + assert.strictEqual(res[1], 'should be a list') + done(err) + }) + }) - beforeEach(function (done) { - client = redis.createClient.apply(null, args); - client.once('ready', function () { - client.flushdb(done); - }); - }); - - it('inserts multiple values at a time into a list', function (done) { - client.rpush('test', ['list key', 'should be a list']); - client.lrange('test', 0, -1, function (err, res) { - assert.equal(res[0], 'list key'); - assert.equal(res[1], 'should be a list'); - done(err); - }); - }); - - afterEach(function () { - client.end(true); - }); - }); - }); -}); + afterEach(function () { + client.end(true) + }) + }) + }) +}) diff --git a/test/commands/sadd.spec.js b/test/commands/sadd.spec.js index 65efa19731..3460748982 100644 --- a/test/commands/sadd.spec.js +++ b/test/commands/sadd.spec.js @@ -1,62 +1,60 @@ -'use strict'; +'use strict' -var assert = require('assert'); -var config = require('../lib/config'); -var helper = require('../helper'); -var redis = config.redis; +var assert = require('assert') +var config = require('../lib/config') +var helper = require('../helper') +var redis = config.redis -describe("The 'sadd' method", function () { +describe('The \'sadd\' method', function () { + helper.allTests(function (ip, args) { + describe('using ' + ip, function () { + var client - helper.allTests(function (ip, args) { + beforeEach(function (done) { + client = redis.createClient.apply(null, args) + client.once('ready', function () { + client.flushdb(done) + }) + }) - describe('using ' + ip, function () { - var client; + it('allows a single value to be added to the set', function (done) { + client.sadd('set0', 'member0', helper.isNumber(1)) + client.smembers('set0', function (err, res) { + assert.ok(~res.indexOf('member0')) + return done(err) + }) + }) - beforeEach(function (done) { - client = redis.createClient.apply(null, args); - client.once('ready', function () { - client.flushdb(done); - }); - }); + it('does not add the same value to the set twice', function (done) { + client.sadd('set0', 'member0', helper.isNumber(1)) + client.sadd('set0', 'member0', helper.isNumber(0, done)) + }) - it('allows a single value to be added to the set', function (done) { - client.sadd('set0', 'member0', helper.isNumber(1)); - client.smembers('set0', function (err, res) { - assert.ok(~res.indexOf('member0')); - return done(err); - }); - }); + it('allows multiple values to be added to the set', function (done) { + client.sadd('set0', ['member0', 'member1', 'member2'], helper.isNumber(3)) + client.smembers('set0', function (err, res) { + assert.strictEqual(res.length, 3) + assert.ok(~res.indexOf('member0')) + assert.ok(~res.indexOf('member1')) + assert.ok(~res.indexOf('member2')) + return done(err) + }) + }) - it('does not add the same value to the set twice', function (done) { - client.sadd('set0', 'member0', helper.isNumber(1)); - client.sadd('set0', 'member0', helper.isNumber(0, done)); - }); + it('allows multiple values to be added to the set with a different syntax', function (done) { + client.sadd(['set0', 'member0', 'member1', 'member2'], helper.isNumber(3)) + client.smembers('set0', function (err, res) { + assert.strictEqual(res.length, 3) + assert.ok(~res.indexOf('member0')) + assert.ok(~res.indexOf('member1')) + assert.ok(~res.indexOf('member2')) + return done(err) + }) + }) - it('allows multiple values to be added to the set', function (done) { - client.sadd('set0', ['member0', 'member1', 'member2'], helper.isNumber(3)); - client.smembers('set0', function (err, res) { - assert.strictEqual(res.length, 3); - assert.ok(~res.indexOf('member0')); - assert.ok(~res.indexOf('member1')); - assert.ok(~res.indexOf('member2')); - return done(err); - }); - }); - - it('allows multiple values to be added to the set with a different syntax', function (done) { - client.sadd(['set0', 'member0', 'member1', 'member2'], helper.isNumber(3)); - client.smembers('set0', function (err, res) { - assert.strictEqual(res.length, 3); - assert.ok(~res.indexOf('member0')); - assert.ok(~res.indexOf('member1')); - assert.ok(~res.indexOf('member2')); - return done(err); - }); - }); - - afterEach(function () { - client.end(true); - }); - }); - }); -}); + afterEach(function () { + client.end(true) + }) + }) + }) +}) diff --git a/test/commands/scard.spec.js b/test/commands/scard.spec.js index e327eb282a..36f205b98e 100644 --- a/test/commands/scard.spec.js +++ b/test/commands/scard.spec.js @@ -1,31 +1,29 @@ -'use strict'; +'use strict' -var config = require('../lib/config'); -var helper = require('../helper'); -var redis = config.redis; +var config = require('../lib/config') +var helper = require('../helper') +var redis = config.redis -describe("The 'scard' method", function () { +describe('The \'scard\' method', function () { + helper.allTests(function (ip, args) { + describe('using ' + ip, function () { + var client - helper.allTests(function (ip, args) { + beforeEach(function (done) { + client = redis.createClient.apply(null, args) + client.once('ready', function () { + client.flushdb(done) + }) + }) - describe('using ' + ip, function () { - var client; + it('returns the number of values in a set', function (done) { + client.sadd('foo', [1, 2, 3], helper.isNumber(3)) + client.scard('foo', helper.isNumber(3, done)) + }) - beforeEach(function (done) { - client = redis.createClient.apply(null, args); - client.once('ready', function () { - client.flushdb(done); - }); - }); - - it('returns the number of values in a set', function (done) { - client.sadd('foo', [1, 2, 3], helper.isNumber(3)); - client.scard('foo', helper.isNumber(3, done)); - }); - - afterEach(function () { - client.end(true); - }); - }); - }); -}); + afterEach(function () { + client.end(true) + }) + }) + }) +}) diff --git a/test/commands/script.spec.js b/test/commands/script.spec.js index c374f5b5e1..aec5e7da17 100644 --- a/test/commands/script.spec.js +++ b/test/commands/script.spec.js @@ -1,55 +1,44 @@ -'use strict'; +'use strict' -var assert = require('assert'); -var config = require('../lib/config'); -var crypto = require('crypto'); -var helper = require('../helper'); -var redis = config.redis; +var config = require('../lib/config') +var crypto = require('crypto') +var helper = require('../helper') +var redis = config.redis -describe("The 'script' method", function () { +describe('The \'script\' method', function () { + helper.allTests(function (ip, args) { + var command = 'return 99' + var commandSha = crypto.createHash('sha1').update(command).digest('hex') - helper.allTests(function (ip, args) { - var command = 'return 99'; - var commandSha = crypto.createHash('sha1').update(command).digest('hex'); + describe('using ' + ip, function () { + var client - describe('using ' + ip, function () { - var client; + beforeEach(function (done) { + client = redis.createClient.apply(null, args) + client.once('ready', function () { + client.flushdb(done) + }) + }) - beforeEach(function (done) { - client = redis.createClient.apply(null, args); - client.once('ready', function () { - client.flushdb(done); - }); - }); + afterEach(function () { + client.end(true) + }) - afterEach(function () { - client.end(true); - }); + it('loads script with client.script(\'load\')', function (done) { + client.script('load', command, helper.isString(commandSha, done)) + }) - it("loads script with client.script('load')", function (done) { - client.script('load', command, function (err, result) { - assert.strictEqual(result, commandSha); - return done(); - }); - }); + it('allows a loaded script to be evaluated', function (done) { + client.evalsha(commandSha, 0, helper.isNumber(99, done)) + }) - it('allows a loaded script to be evaluated', function (done) { - client.evalsha(commandSha, 0, helper.isNumber(99, done)); - }); + it('allows a script to be loaded as part of a chained transaction', function (done) { + client.multi().script('load', command).exec(helper.isDeepEqual([commandSha], done)) + }) - it('allows a script to be loaded as part of a chained transaction', function (done) { - client.multi().script('load', command).exec(function (err, result) { - assert.strictEqual(result[0], commandSha); - return done(); - }); - }); - - it("allows a script to be loaded using a transaction's array syntax", function (done) { - client.multi([['script', 'load', command]]).exec(function (err, result) { - assert.strictEqual(result[0], commandSha); - return done(); - }); - }); - }); - }); -}); + it('allows a script to be loaded using a transaction\'s array syntax', function (done) { + client.multi([['script', 'load', command]]).exec(helper.isDeepEqual([commandSha], done)) + }) + }) + }) +}) diff --git a/test/commands/sdiff.spec.js b/test/commands/sdiff.spec.js index 95f81f09bd..91b42be83d 100644 --- a/test/commands/sdiff.spec.js +++ b/test/commands/sdiff.spec.js @@ -1,47 +1,45 @@ -'use strict'; +'use strict' -var assert = require('assert'); -var config = require('../lib/config'); -var helper = require('../helper'); -var redis = config.redis; +var assert = require('assert') +var config = require('../lib/config') +var helper = require('../helper') +var redis = config.redis -describe("The 'sdiff' method", function () { +describe('The \'sdiff\' method', function () { + helper.allTests(function (ip, args) { + describe('using ' + ip, function () { + var client - helper.allTests(function (ip, args) { + beforeEach(function (done) { + client = redis.createClient.apply(null, args) + client.once('ready', function () { + client.flushdb(done) + }) + }) - describe('using ' + ip, function () { - var client; + it('returns set difference', function (done) { + client.sadd('foo', 'x', helper.isNumber(1)) + client.sadd('foo', ['a'], helper.isNumber(1)) + client.sadd('foo', 'b', helper.isNumber(1)) + client.sadd(['foo', 'c'], helper.isNumber(1)) - beforeEach(function (done) { - client = redis.createClient.apply(null, args); - client.once('ready', function () { - client.flushdb(done); - }); - }); + client.sadd(['bar', 'c', helper.isNumber(1)]) - it('returns set difference', function (done) { - client.sadd('foo', 'x', helper.isNumber(1)); - client.sadd('foo', ['a'], helper.isNumber(1)); - client.sadd('foo', 'b', helper.isNumber(1)); - client.sadd(['foo', 'c'], helper.isNumber(1)); + client.sadd('baz', 'a', helper.isNumber(1)) + client.sadd('baz', 'd', helper.isNumber(1)) - client.sadd(['bar', 'c', helper.isNumber(1)]); + client.sdiff('foo', 'bar', 'baz', function (err, values) { + values.sort() + assert.strictEqual(values.length, 2) + assert.strictEqual(values[0], 'b') + assert.strictEqual(values[1], 'x') + return done(err) + }) + }) - client.sadd('baz', 'a', helper.isNumber(1)); - client.sadd('baz', 'd', helper.isNumber(1)); - - client.sdiff('foo', 'bar', 'baz', function (err, values) { - values.sort(); - assert.equal(values.length, 2); - assert.equal(values[0], 'b'); - assert.equal(values[1], 'x'); - return done(err); - }); - }); - - afterEach(function () { - client.end(true); - }); - }); - }); -}); + afterEach(function () { + client.end(true) + }) + }) + }) +}) diff --git a/test/commands/sdiffstore.spec.js b/test/commands/sdiffstore.spec.js index fe822b561b..a5247f5edd 100644 --- a/test/commands/sdiffstore.spec.js +++ b/test/commands/sdiffstore.spec.js @@ -1,47 +1,45 @@ -'use strict'; +'use strict' -var assert = require('assert'); -var config = require('../lib/config'); -var helper = require('../helper'); -var redis = config.redis; +var assert = require('assert') +var config = require('../lib/config') +var helper = require('../helper') +var redis = config.redis -describe("The 'sdiffstore' method", function () { +describe('The \'sdiffstore\' method', function () { + helper.allTests(function (ip, args) { + describe('using ' + ip, function () { + var client - helper.allTests(function (ip, args) { + beforeEach(function (done) { + client = redis.createClient.apply(null, args) + client.once('ready', function () { + client.flushdb(done) + }) + }) - describe('using ' + ip, function () { - var client; + it('calculates set difference ands stores it in a key', function (done) { + client.sadd('foo', 'x', helper.isNumber(1)) + client.sadd('foo', 'a', helper.isNumber(1)) + client.sadd('foo', 'b', helper.isNumber(1)) + client.sadd('foo', 'c', helper.isNumber(1)) - beforeEach(function (done) { - client = redis.createClient.apply(null, args); - client.once('ready', function () { - client.flushdb(done); - }); - }); + client.sadd('bar', 'c', helper.isNumber(1)) - it('calculates set difference ands stores it in a key', function (done) { - client.sadd('foo', 'x', helper.isNumber(1)); - client.sadd('foo', 'a', helper.isNumber(1)); - client.sadd('foo', 'b', helper.isNumber(1)); - client.sadd('foo', 'c', helper.isNumber(1)); + client.sadd('baz', 'a', helper.isNumber(1)) + client.sadd('baz', 'd', helper.isNumber(1)) - client.sadd('bar', 'c', helper.isNumber(1)); + client.sdiffstore('quux', 'foo', 'bar', 'baz', helper.isNumber(2)) - client.sadd('baz', 'a', helper.isNumber(1)); - client.sadd('baz', 'd', helper.isNumber(1)); + client.smembers('quux', function (err, values) { + var members = values.sort() + assert.deepEqual(members, [ 'b', 'x' ]) + return done(err) + }) + }) - client.sdiffstore('quux', 'foo', 'bar', 'baz', helper.isNumber(2)); - - client.smembers('quux', function (err, values) { - var members = values.sort(); - assert.deepEqual(members, [ 'b', 'x' ]); - return done(err); - }); - }); - - afterEach(function () { - client.end(true); - }); - }); - }); -}); + afterEach(function () { + client.end(true) + }) + }) + }) +}) diff --git a/test/commands/select.spec.js b/test/commands/select.spec.js index 41a4d3fea9..3425af1506 100644 --- a/test/commands/select.spec.js +++ b/test/commands/select.spec.js @@ -1,126 +1,124 @@ -'use strict'; +'use strict' -var assert = require('assert'); -var config = require('../lib/config'); -var helper = require('../helper'); -var redis = config.redis; +var assert = require('assert') +var config = require('../lib/config') +var helper = require('../helper') +var redis = config.redis -describe("The 'select' method", function () { +describe('The \'select\' method', function () { + helper.allTests(function (ip, args) { + describe('using ' + ip, function () { + describe('when not connected', function () { + var client - helper.allTests(function (ip, args) { + beforeEach(function (done) { + client = redis.createClient.apply(null, args) + client.once('ready', function () { + client.quit() + }) + client.on('end', done) + }) - describe('using ' + ip, function () { - describe('when not connected', function () { - var client; + it('returns an error if redis is not connected', function (done) { + var buffering = client.select(1, function (err, res) { + assert(err.message.match(/The connection is already closed/)) + done() + }) + assert(typeof buffering === 'boolean') + }) + }) - beforeEach(function (done) { - client = redis.createClient.apply(null, args); - client.once('ready', function () { - client.quit(); - }); - client.on('end', done); - }); + describe('when connected', function () { + var client - it('returns an error if redis is not connected', function (done) { - var buffering = client.select(1, function (err, res) { - assert(err.message.match(/The connection is already closed/)); - done(); - }); - assert(typeof buffering === 'boolean'); - }); - }); + beforeEach(function (done) { + client = redis.createClient.apply(null, args) + client.once('ready', function () { + client.flushdb(done) + }) + }) - describe('when connected', function () { - var client; + afterEach(function () { + client.end(true) + }) - beforeEach(function (done) { - client = redis.createClient.apply(null, args); - client.once('ready', function () { - client.flushdb(done); - }); - }); + it('changes the database and calls the callback', function (done) { + // default value of null means database 0 will be used. + assert.strictEqual(client.selectedDb, undefined, 'default db should be undefined') + var buffering = client.select(1, function (err, res) { + helper.isNotError()(err, res) + assert.strictEqual(client.selectedDb, 1, 'db should be 1 after select') + done() + }) + assert(typeof buffering === 'boolean') + }) - afterEach(function () { - client.end(true); - }); + describe('and a callback is specified', function () { + describe('with a valid db index', function () { + it('selects the appropriate database', function (done) { + assert.strictEqual(client.selectedDb, undefined, 'default db should be undefined') + client.select(1, function (err) { + assert.strictEqual(err, null) + assert.strictEqual(client.selectedDb, 1, 'we should have selected the new valid DB') + done() + }) + }) + }) - it('changes the database and calls the callback', function (done) { - // default value of null means database 0 will be used. - assert.strictEqual(client.selectedDb, undefined, 'default db should be undefined'); - var buffering = client.select(1, function (err, res) { - helper.isNotError()(err, res); - assert.strictEqual(client.selectedDb, 1, 'db should be 1 after select'); - done(); - }); - assert(typeof buffering === 'boolean'); - }); + describe('with an invalid db index', function () { + it('returns an error', function (done) { + assert.strictEqual(client.selectedDb, undefined, 'default db should be undefined') + client.select(9999, function (err) { + assert.strictEqual(err.code, 'ERR') + assert.strictEqual(err.message, 'ERR invalid DB index') + done() + }) + }) + }) + }) - describe('and a callback is specified', function () { - describe('with a valid db index', function () { - it('selects the appropriate database', function (done) { - assert.strictEqual(client.selectedDb, undefined, 'default db should be undefined'); - client.select(1, function (err) { - assert.equal(err, null); - assert.equal(client.selectedDb, 1, 'we should have selected the new valid DB'); - done(); - }); - }); - }); + describe('and no callback is specified', function () { + describe('with a valid db index', function () { + it('selects the appropriate database', function (done) { + assert.strictEqual(client.selectedDb, undefined, 'default db should be undefined') + client.select(1) + setTimeout(function () { + assert.strictEqual(client.selectedDb, 1, 'we should have selected the new valid DB') + done() + }, 25) + }) + }) - describe('with an invalid db index', function () { - it('returns an error', function (done) { - assert.strictEqual(client.selectedDb, undefined, 'default db should be undefined'); - client.select(9999, function (err) { - assert.equal(err.code, 'ERR'); - assert.equal(err.message, 'ERR invalid DB index'); - done(); - }); - }); - }); - }); + describe('with an invalid db index', function () { + it('emits an error when callback not provided', function (done) { + assert.strictEqual(client.selectedDb, undefined, 'default db should be undefined') - describe('and no callback is specified', function () { - describe('with a valid db index', function () { - it('selects the appropriate database', function (done) { - assert.strictEqual(client.selectedDb, undefined, 'default db should be undefined'); - client.select(1); - setTimeout(function () { - assert.equal(client.selectedDb, 1, 'we should have selected the new valid DB'); - done(); - }, 25); - }); - }); + client.on('error', function (err) { + assert.strictEqual(err.command, 'SELECT') + assert.strictEqual(err.message, 'ERR invalid DB index') + done() + }) - describe('with an invalid db index', function () { - it('emits an error when callback not provided', function (done) { - assert.strictEqual(client.selectedDb, undefined, 'default db should be undefined'); + client.select(9999) + }) + }) + }) - client.on('error', function (err) { - assert.strictEqual(err.command, 'SELECT'); - assert.equal(err.message, 'ERR invalid DB index'); - done(); - }); - - client.select(9999); - }); - }); - }); - - describe('reconnection occurs', function () { - it('selects the appropriate database after a reconnect', function (done) { - assert.strictEqual(client.selectedDb, undefined, 'default db should be undefined'); - client.select(3); - client.set('foo', 'bar', function () { - client.stream.destroy(); - }); - client.once('ready', function () { - assert.strictEqual(client.selectedDb, 3); - assert(typeof client.serverInfo.db3 === 'object'); - done(); - }); - }); - }); - }); - }); - }); -}); + describe('reconnection occurs', function () { + it('selects the appropriate database after a reconnect', function (done) { + assert.strictEqual(client.selectedDb, undefined, 'default db should be undefined') + client.select(3) + client.set('foo', 'bar', function () { + client.stream.destroy() + }) + client.once('ready', function () { + assert.strictEqual(client.selectedDb, 3) + assert(typeof client.serverInfo.db3 === 'object') + done() + }) + }) + }) + }) + }) + }) +}) diff --git a/test/commands/set.spec.js b/test/commands/set.spec.js index 86d66178f4..d5951c861a 100644 --- a/test/commands/set.spec.js +++ b/test/commands/set.spec.js @@ -1,170 +1,168 @@ -'use strict'; +'use strict' -var assert = require('assert'); -var config = require('../lib/config'); -var helper = require('../helper'); -var redis = config.redis; -var uuid = require('uuid'); +var assert = require('assert') +var config = require('../lib/config') +var helper = require('../helper') +var redis = config.redis +var uuid = require('uuid') -describe("The 'set' method", function () { +describe('The \'set\' method', function () { + helper.allTests(function (ip, args) { + describe('using ' + ip, function () { + var key, value - helper.allTests(function (ip, args) { + beforeEach(function () { + key = uuid.v4() + value = uuid.v4() + }) - describe('using ' + ip, function () { - var key, value; + describe('when not connected', function () { + var client - beforeEach(function () { - key = uuid.v4(); - value = uuid.v4(); - }); + beforeEach(function (done) { + client = redis.createClient.apply(null, args) + client.once('ready', function () { + client.quit() + }) + client.on('end', done) + }) - describe('when not connected', function () { - var client; + it('reports an error', function (done) { + client.set(key, value, function (err, res) { + assert(err.message.match(/The connection is already closed/)) + done() + }) + }) + }) - beforeEach(function (done) { - client = redis.createClient.apply(null, args); - client.once('ready', function () { - client.quit(); - }); - client.on('end', done); - }); + describe('when connected', function () { + var client - it('reports an error', function (done) { - client.set(key, value, function (err, res) { - assert(err.message.match(/The connection is already closed/)); - done(); - }); - }); - }); + beforeEach(function (done) { + client = redis.createClient.apply(null, args) + client.once('ready', function () { + client.flushdb(done) + }) + }) - describe('when connected', function () { - var client; + afterEach(function () { + client.end(true) + }) - beforeEach(function (done) { - client = redis.createClient.apply(null, args); - client.once('ready', function () { - client.flushdb(done); - }); - }); + describe('and a callback is specified', function () { + describe('with valid parameters', function () { + it('sets the value correctly', function (done) { + client.set(key, value, function (err, res) { + helper.isNotError()(err, res) + client.get(key, function (err, res) { + helper.isString(value)(err, res) + done() + }) + }) + }) - afterEach(function () { - client.end(true); - }); + it('set expire date in seconds', function (done) { + client.set('foo', 'bar', 'ex', 10, helper.isString('OK')) + client.pttl('foo', function (err, res) { + assert(res >= 10000 - 50) // Max 50 ms should have passed + assert(res <= 10000) // Max possible should be 10.000 + done(err) + }) + }) - describe('and a callback is specified', function () { - describe('with valid parameters', function () { - it('sets the value correctly', function (done) { - client.set(key, value, function (err, res) { - helper.isNotError()(err, res); - client.get(key, function (err, res) { - helper.isString(value)(err, res); - done(); - }); - }); - }); + it('set expire date in milliseconds', function (done) { + client.set('foo', 'bar', 'px', 100, helper.isString('OK')) + client.pttl('foo', function (err, res) { + assert(res >= 50) // Max 50 ms should have passed + assert(res <= 100) // Max possible should be 100 + done(err) + }) + }) - it('set expire date in seconds', function (done) { - client.set('foo', 'bar', 'ex', 10, helper.isString('OK')); - client.pttl('foo', function (err, res) { - assert(res >= 10000 - 50); // Max 50 ms should have passed - assert(res <= 10000); // Max possible should be 10.000 - done(err); - }); - }); + it('only set the key if (not) already set', function (done) { + client.set('foo', 'bar', 'NX', helper.isString('OK')) + client.set('foo', 'bar', 'nx', helper.isNull()) + client.set('foo', 'bar', 'EX', '10', 'XX', helper.isString('OK')) + client.ttl('foo', function (err, res) { + assert(res >= 9) // Min 9s should be left + assert(res <= 10) // Max 10s should be left + done(err) + }) + }) + }) - it('set expire date in milliseconds', function (done) { - client.set('foo', 'bar', 'px', 100, helper.isString('OK')); - client.pttl('foo', function (err, res) { - assert(res >= 50); // Max 50 ms should have passed - assert(res <= 100); // Max possible should be 100 - done(err); - }); - }); + describe('reports an error with invalid parameters', function () { + it('undefined \'key\' and missing \'value\' parameter', function (done) { + client.set(undefined, function (err, res) { + helper.isError()(err, null) + assert.strictEqual(err.command, 'SET') + done() + }) + }) - it('only set the key if (not) already set', function (done) { - client.set('foo', 'bar', 'NX', helper.isString('OK')); - client.set('foo', 'bar', 'nx', helper.isNull()); - client.set('foo', 'bar', 'EX', '10', 'XX', helper.isString('OK')); - client.ttl('foo', function (err, res) { - assert(res >= 9); // Min 9s should be left - assert(res <= 10); // Max 10s should be left - done(err); - }); - }); - }); + it('empty array as second parameter', function (done) { + client.set('foo', [], function (err, res) { + assert.strictEqual(err.message, 'ERR wrong number of arguments for \'set\' command') + done() + }) + }) + }) + }) - describe('reports an error with invalid parameters', function () { - it("undefined 'key' and missing 'value' parameter", function (done) { - client.set(undefined, function (err, res) { - helper.isError()(err, null); - assert.equal(err.command, 'SET'); - done(); - }); - }); + describe('and no callback is specified', function () { + describe('with valid parameters', function () { + it('sets the value correctly', function (done) { + client.set(key, value) + client.get(key, helper.isString(value, done)) + }) - it('empty array as second parameter', function (done) { - client.set('foo', [], function (err, res) { - assert.strictEqual(err.message, "ERR wrong number of arguments for 'set' command"); - done(); - }); - }); - }); - }); + it('sets the value correctly even if the callback is explicitly set to undefined', function (done) { + client.set(key, value, undefined) + client.get(key, helper.isString(value, done)) + }) - describe('and no callback is specified', function () { - describe('with valid parameters', function () { - it('sets the value correctly', function (done) { - client.set(key, value); - client.get(key, helper.isString(value, done)); - }); + it('sets the value correctly with the array syntax', function (done) { + client.set([key, value]) + client.get(key, helper.isString(value, done)) + }) + }) - it('sets the value correctly even if the callback is explicitly set to undefined', function (done) { - client.set(key, value, undefined); - client.get(key, helper.isString(value, done)); - }); + describe('with undefined \'key\' and missing \'value\' parameter', function () { + it('emits an error without callback', function (done) { + client.on('error', function (err) { + assert.strictEqual(err.message, 'ERR wrong number of arguments for \'set\' command') + assert.strictEqual(err.command, 'SET') + done() + }) + client.set(undefined) + }) + }) - it('sets the value correctly with the array syntax', function (done) { - client.set([key, value]); - client.get(key, helper.isString(value, done)); - }); - }); + // TODO: This test has to be refactored from v.3.0 on to expect an error instead + it('converts null to \'null\'', function (done) { + client.set('foo', null) + client.get('foo', helper.isString('null', done)) + }) - describe("with undefined 'key' and missing 'value' parameter", function () { - it('emits an error without callback', function (done) { - client.on('error', function (err) { - assert.equal(err.message, "ERR wrong number of arguments for 'set' command"); - assert.equal(err.command, 'SET'); - done(); - }); - client.set(undefined); - }); - }); + it('emit an error with only the key set', function (done) { + client.on('error', function (err) { + assert.strictEqual(err.message, 'ERR wrong number of arguments for \'set\' command') + done() + }) - // TODO: This test has to be refactored from v.3.0 on to expect an error instead - it("converts null to 'null'", function (done) { - client.set('foo', null); - client.get('foo', helper.isString('null', done)); - }); + client.set('foo') + }) - it('emit an error with only the key set', function (done) { - client.on('error', function (err) { - assert.equal(err.message, "ERR wrong number of arguments for 'set' command"); - done(); - }); - - client.set('foo'); - }); - - it('emit an error without any parameters', function (done) { - client.once('error', function (err) { - assert.equal(err.message, "ERR wrong number of arguments for 'set' command"); - assert.equal(err.command, 'SET'); - done(); - }); - client.set(); - }); - }); - }); - }); - }); -}); + it('emit an error without any parameters', function (done) { + client.once('error', function (err) { + assert.strictEqual(err.message, 'ERR wrong number of arguments for \'set\' command') + assert.strictEqual(err.command, 'SET') + done() + }) + client.set() + }) + }) + }) + }) + }) +}) diff --git a/test/commands/setex.spec.js b/test/commands/setex.spec.js index a2126e6dbb..317269ef6f 100644 --- a/test/commands/setex.spec.js +++ b/test/commands/setex.spec.js @@ -1,37 +1,36 @@ -'use strict'; +'use strict' -var assert = require('assert'); -var config = require('../lib/config'); -var helper = require('../helper'); -var redis = config.redis; +var assert = require('assert') +var config = require('../lib/config') +var helper = require('../helper') +var redis = config.redis -describe("The 'setex' method", function () { +describe('The \'setex\' method', function () { + helper.allTests(function (ip, args) { + describe('using ' + ip, function () { + var client - helper.allTests(function (ip, args) { + beforeEach(function (done) { + client = redis.createClient.apply(null, args) + client.once('ready', function () { + client.flushdb(done) + }) + }) - describe('using ' + ip, function () { - var client; + it('sets a key with an expiry', function (done) { + client.setex(['setex key', '100', 'setex val'], helper.isString('OK')) + var buffering = client.exists(['setex key'], helper.isNumber(1)) + assert(typeof buffering === 'boolean') + client.ttl(['setex key'], function (err, ttl) { + assert.strictEqual(err, null) + assert(ttl > 0) + return done() + }) + }) - beforeEach(function (done) { - client = redis.createClient.apply(null, args); - client.once('ready', function () { - client.flushdb(done); - }); - }); - - it('sets a key with an expiry', function (done) { - client.setex(['setex key', '100', 'setex val'], helper.isString('OK')); - var buffering = client.exists(['setex key'], helper.isNumber(1)); - assert(typeof buffering === 'boolean'); - client.ttl(['setex key'], function (err, ttl) { - assert(ttl > 0); - return done(); - }); - }); - - afterEach(function () { - client.end(true); - }); - }); - }); -}); + afterEach(function () { + client.end(true) + }) + }) + }) +}) diff --git a/test/commands/setnx.spec.js b/test/commands/setnx.spec.js index 25fd4465af..d4c6d85dd9 100644 --- a/test/commands/setnx.spec.js +++ b/test/commands/setnx.spec.js @@ -1,37 +1,35 @@ -'use strict'; +'use strict' -var config = require('../lib/config'); -var helper = require('../helper'); -var redis = config.redis; +var config = require('../lib/config') +var helper = require('../helper') +var redis = config.redis -describe("The 'setnx' method", function () { +describe('The \'setnx\' method', function () { + helper.allTests(function (ip, args) { + describe('using ' + ip, function () { + var client - helper.allTests(function (ip, args) { + beforeEach(function (done) { + client = redis.createClient.apply(null, args) + client.once('ready', function () { + client.flushdb(done) + }) + }) - describe('using ' + ip, function () { - var client; + it('sets key if it does not have a value', function (done) { + client.setnx('foo', 'banana', helper.isNumber(1)) + client.get('foo', helper.isString('banana', done)) + }) - beforeEach(function (done) { - client = redis.createClient.apply(null, args); - client.once('ready', function () { - client.flushdb(done); - }); - }); + it('does not set key if it already has a value', function (done) { + client.set('foo', 'bar', helper.isString('OK')) + client.setnx('foo', 'banana', helper.isNumber(0)) + client.get('foo', helper.isString('bar', done)) + }) - it('sets key if it does not have a value', function (done) { - client.setnx('foo', 'banana', helper.isNumber(1)); - client.get('foo', helper.isString('banana', done)); - }); - - it('does not set key if it already has a value', function (done) { - client.set('foo', 'bar', helper.isString('OK')); - client.setnx('foo', 'banana', helper.isNumber(0)); - client.get('foo', helper.isString('bar', done)); - }); - - afterEach(function () { - client.end(true); - }); - }); - }); -}); + afterEach(function () { + client.end(true) + }) + }) + }) +}) diff --git a/test/commands/sinter.spec.js b/test/commands/sinter.spec.js index c9873beca3..ce01f38b2d 100644 --- a/test/commands/sinter.spec.js +++ b/test/commands/sinter.spec.js @@ -1,63 +1,61 @@ -'use strict'; +'use strict' -var assert = require('assert'); -var config = require('../lib/config'); -var helper = require('../helper'); -var redis = config.redis; +var assert = require('assert') +var config = require('../lib/config') +var helper = require('../helper') +var redis = config.redis -describe("The 'sinter' method", function () { +describe('The \'sinter\' method', function () { + helper.allTests(function (ip, args) { + describe('using ' + ip, function () { + var client - helper.allTests(function (ip, args) { + beforeEach(function (done) { + client = redis.createClient.apply(null, args) + client.once('ready', function () { + client.flushdb(done) + }) + }) - describe('using ' + ip, function () { - var client; + it('handles two sets being intersected', function (done) { + client.sadd('sa', 'a', helper.isNumber(1)) + client.sadd('sa', 'b', helper.isNumber(1)) + client.sadd('sa', 'c', helper.isNumber(1)) - beforeEach(function (done) { - client = redis.createClient.apply(null, args); - client.once('ready', function () { - client.flushdb(done); - }); - }); + client.sadd('sb', 'b', helper.isNumber(1)) + client.sadd('sb', 'c', helper.isNumber(1)) + client.sadd('sb', 'd', helper.isNumber(1)) - it('handles two sets being intersected', function (done) { - client.sadd('sa', 'a', helper.isNumber(1)); - client.sadd('sa', 'b', helper.isNumber(1)); - client.sadd('sa', 'c', helper.isNumber(1)); + client.sinter('sa', 'sb', function (err, intersection) { + assert.strictEqual(intersection.length, 2) + assert.deepEqual(intersection.sort(), [ 'b', 'c' ]) + return done(err) + }) + }) - client.sadd('sb', 'b', helper.isNumber(1)); - client.sadd('sb', 'c', helper.isNumber(1)); - client.sadd('sb', 'd', helper.isNumber(1)); + it('handles three sets being intersected', function (done) { + client.sadd('sa', 'a', helper.isNumber(1)) + client.sadd('sa', 'b', helper.isNumber(1)) + client.sadd('sa', 'c', helper.isNumber(1)) - client.sinter('sa', 'sb', function (err, intersection) { - assert.equal(intersection.length, 2); - assert.deepEqual(intersection.sort(), [ 'b', 'c' ]); - return done(err); - }); - }); + client.sadd('sb', 'b', helper.isNumber(1)) + client.sadd('sb', 'c', helper.isNumber(1)) + client.sadd('sb', 'd', helper.isNumber(1)) - it('handles three sets being intersected', function (done) { - client.sadd('sa', 'a', helper.isNumber(1)); - client.sadd('sa', 'b', helper.isNumber(1)); - client.sadd('sa', 'c', helper.isNumber(1)); + client.sadd('sc', 'c', helper.isNumber(1)) + client.sadd('sc', 'd', helper.isNumber(1)) + client.sadd('sc', 'e', helper.isNumber(1)) - client.sadd('sb', 'b', helper.isNumber(1)); - client.sadd('sb', 'c', helper.isNumber(1)); - client.sadd('sb', 'd', helper.isNumber(1)); + client.sinter('sa', 'sb', 'sc', function (err, intersection) { + assert.strictEqual(intersection.length, 1) + assert.strictEqual(intersection[0], 'c') + return done(err) + }) + }) - client.sadd('sc', 'c', helper.isNumber(1)); - client.sadd('sc', 'd', helper.isNumber(1)); - client.sadd('sc', 'e', helper.isNumber(1)); - - client.sinter('sa', 'sb', 'sc', function (err, intersection) { - assert.equal(intersection.length, 1); - assert.equal(intersection[0], 'c'); - return done(err); - }); - }); - - afterEach(function () { - client.end(true); - }); - }); - }); -}); + afterEach(function () { + client.end(true) + }) + }) + }) +}) diff --git a/test/commands/sinterstore.spec.js b/test/commands/sinterstore.spec.js index 1ea4c4b109..2e2f3c0263 100644 --- a/test/commands/sinterstore.spec.js +++ b/test/commands/sinterstore.spec.js @@ -1,48 +1,46 @@ -'use strict'; +'use strict' -var assert = require('assert'); -var config = require('../lib/config'); -var helper = require('../helper'); -var redis = config.redis; +var assert = require('assert') +var config = require('../lib/config') +var helper = require('../helper') +var redis = config.redis -describe("The 'sinterstore' method", function () { +describe('The \'sinterstore\' method', function () { + helper.allTests(function (ip, args) { + describe('using ' + ip, function () { + var client - helper.allTests(function (ip, args) { + beforeEach(function (done) { + client = redis.createClient.apply(null, args) + client.once('ready', function () { + client.flushdb(done) + }) + }) - describe('using ' + ip, function () { - var client; + it('calculates set intersection and stores it in a key', function (done) { + client.sadd('sa', 'a', helper.isNumber(1)) + client.sadd('sa', 'b', helper.isNumber(1)) + client.sadd('sa', 'c', helper.isNumber(1)) - beforeEach(function (done) { - client = redis.createClient.apply(null, args); - client.once('ready', function () { - client.flushdb(done); - }); - }); + client.sadd('sb', 'b', helper.isNumber(1)) + client.sadd('sb', 'c', helper.isNumber(1)) + client.sadd('sb', 'd', helper.isNumber(1)) - it('calculates set intersection and stores it in a key', function (done) { - client.sadd('sa', 'a', helper.isNumber(1)); - client.sadd('sa', 'b', helper.isNumber(1)); - client.sadd('sa', 'c', helper.isNumber(1)); + client.sadd('sc', 'c', helper.isNumber(1)) + client.sadd('sc', 'd', helper.isNumber(1)) + client.sadd('sc', 'e', helper.isNumber(1)) - client.sadd('sb', 'b', helper.isNumber(1)); - client.sadd('sb', 'c', helper.isNumber(1)); - client.sadd('sb', 'd', helper.isNumber(1)); + client.sinterstore('foo', 'sa', 'sb', 'sc', helper.isNumber(1)) - client.sadd('sc', 'c', helper.isNumber(1)); - client.sadd('sc', 'd', helper.isNumber(1)); - client.sadd('sc', 'e', helper.isNumber(1)); + client.smembers('foo', function (err, members) { + assert.deepEqual(members, [ 'c' ]) + return done(err) + }) + }) - client.sinterstore('foo', 'sa', 'sb', 'sc', helper.isNumber(1)); - - client.smembers('foo', function (err, members) { - assert.deepEqual(members, [ 'c' ]); - return done(err); - }); - }); - - afterEach(function () { - client.end(true); - }); - }); - }); -}); + afterEach(function () { + client.end(true) + }) + }) + }) +}) diff --git a/test/commands/sismember.spec.js b/test/commands/sismember.spec.js index 52194b1d2b..3967723f6f 100644 --- a/test/commands/sismember.spec.js +++ b/test/commands/sismember.spec.js @@ -1,35 +1,33 @@ -'use strict'; +'use strict' -var config = require('../lib/config'); -var helper = require('../helper'); -var redis = config.redis; +var config = require('../lib/config') +var helper = require('../helper') +var redis = config.redis -describe("The 'sismember' method", function () { +describe('The \'sismember\' method', function () { + helper.allTests(function (ip, args) { + describe('using ' + ip, function () { + var client - helper.allTests(function (ip, args) { + beforeEach(function (done) { + client = redis.createClient.apply(null, args) + client.once('ready', function () { + client.flushdb(done) + }) + }) - describe('using ' + ip, function () { - var client; + it('returns 0 if the value is not in the set', function (done) { + client.sismember('foo', 'banana', helper.isNumber(0, done)) + }) - beforeEach(function (done) { - client = redis.createClient.apply(null, args); - client.once('ready', function () { - client.flushdb(done); - }); - }); + it('returns 1 if the value is in the set', function (done) { + client.sadd('foo', 'banana', helper.isNumber(1)) + client.sismember('foo', 'banana', helper.isNumber(1, done)) + }) - it('returns 0 if the value is not in the set', function (done) { - client.sismember('foo', 'banana', helper.isNumber(0, done)); - }); - - it('returns 1 if the value is in the set', function (done) { - client.sadd('foo', 'banana', helper.isNumber(1)); - client.sismember('foo', 'banana', helper.isNumber(1, done)); - }); - - afterEach(function () { - client.end(true); - }); - }); - }); -}); + afterEach(function () { + client.end(true) + }) + }) + }) +}) diff --git a/test/commands/slowlog.spec.js b/test/commands/slowlog.spec.js index 7b64a763d6..a7211bd0ed 100644 --- a/test/commands/slowlog.spec.js +++ b/test/commands/slowlog.spec.js @@ -1,41 +1,39 @@ -'use strict'; +'use strict' -var assert = require('assert'); -var config = require('../lib/config'); -var helper = require('../helper'); -var redis = config.redis; +var assert = require('assert') +var config = require('../lib/config') +var helper = require('../helper') +var redis = config.redis -describe("The 'slowlog' method", function () { +describe('The \'slowlog\' method', function () { + helper.allTests(function (ip, args) { + describe('using ' + ip, function () { + var client - helper.allTests(function (ip, args) { + beforeEach(function (done) { + client = redis.createClient.apply(null, args) + client.once('ready', function () { + client.flushdb(done) + }) + }) - describe('using ' + ip, function () { - var client; + it('logs operations in slowlog', function (done) { + client.config('set', 'slowlog-log-slower-than', 0, helper.isString('OK')) + client.slowlog('reset', helper.isString('OK')) + client.set('foo', 'bar', helper.isString('OK')) + client.get('foo', helper.isString('bar')) + client.slowlog('get', function (err, res) { + assert.strictEqual(res.length, 3) + assert.strictEqual(res[0][3].length, 2) + assert.deepEqual(res[1][3], ['set', 'foo', 'bar']) + assert.deepEqual(res[2][3], ['slowlog', 'reset']) + return done(err) + }) + }) - beforeEach(function (done) { - client = redis.createClient.apply(null, args); - client.once('ready', function () { - client.flushdb(done); - }); - }); - - it('logs operations in slowlog', function (done) { - client.config('set', 'slowlog-log-slower-than', 0, helper.isString('OK')); - client.slowlog('reset', helper.isString('OK')); - client.set('foo', 'bar', helper.isString('OK')); - client.get('foo', helper.isString('bar')); - client.slowlog('get', function (err, res) { - assert.equal(res.length, 3); - assert.equal(res[0][3].length, 2); - assert.deepEqual(res[1][3], ['set', 'foo', 'bar']); - assert.deepEqual(res[2][3], ['slowlog', 'reset']); - return done(err); - }); - }); - - afterEach(function () { - client.end(true); - }); - }); - }); -}); + afterEach(function () { + client.end(true) + }) + }) + }) +}) diff --git a/test/commands/smembers.spec.js b/test/commands/smembers.spec.js index 0bc8143719..6124699490 100644 --- a/test/commands/smembers.spec.js +++ b/test/commands/smembers.spec.js @@ -1,38 +1,36 @@ -'use strict'; +'use strict' -var assert = require('assert'); -var config = require('../lib/config'); -var helper = require('../helper'); -var redis = config.redis; +var assert = require('assert') +var config = require('../lib/config') +var helper = require('../helper') +var redis = config.redis -describe("The 'smembers' method", function () { +describe('The \'smembers\' method', function () { + helper.allTests(function (ip, args) { + describe('using ' + ip, function () { + var client - helper.allTests(function (ip, args) { + beforeEach(function (done) { + client = redis.createClient.apply(null, args) + client.once('ready', function () { + client.flushdb(done) + }) + }) - describe('using ' + ip, function () { - var client; + it('returns all values in a set', function (done) { + client.sadd('foo', 'x', helper.isNumber(1)) + client.sadd('foo', 'y', helper.isNumber(1)) + client.smembers('foo', function (err, values) { + assert.strictEqual(values.length, 2) + var members = values.sort() + assert.deepEqual(members, [ 'x', 'y' ]) + return done(err) + }) + }) - beforeEach(function (done) { - client = redis.createClient.apply(null, args); - client.once('ready', function () { - client.flushdb(done); - }); - }); - - it('returns all values in a set', function (done) { - client.sadd('foo', 'x', helper.isNumber(1)); - client.sadd('foo', 'y', helper.isNumber(1)); - client.smembers('foo', function (err, values) { - assert.equal(values.length, 2); - var members = values.sort(); - assert.deepEqual(members, [ 'x', 'y' ]); - return done(err); - }); - }); - - afterEach(function () { - client.end(true); - }); - }); - }); -}); + afterEach(function () { + client.end(true) + }) + }) + }) +}) diff --git a/test/commands/smove.spec.js b/test/commands/smove.spec.js index 447afa352c..fd2e44adad 100644 --- a/test/commands/smove.spec.js +++ b/test/commands/smove.spec.js @@ -1,40 +1,38 @@ -'use strict'; +'use strict' -var config = require('../lib/config'); -var helper = require('../helper'); -var redis = config.redis; +var config = require('../lib/config') +var helper = require('../helper') +var redis = config.redis -describe("The 'smove' method", function () { +describe('The \'smove\' method', function () { + helper.allTests(function (ip, args) { + describe('using ' + ip, function () { + var client - helper.allTests(function (ip, args) { + beforeEach(function (done) { + client = redis.createClient.apply(null, args) + client.once('ready', function () { + client.flushdb(done) + }) + }) - describe('using ' + ip, function () { - var client; + it('moves a value to a set that does not yet exist', function (done) { + client.sadd('foo', 'x', helper.isNumber(1)) + client.smove('foo', 'bar', 'x', helper.isNumber(1)) + client.sismember('foo', 'x', helper.isNumber(0)) + client.sismember('bar', 'x', helper.isNumber(1, done)) + }) - beforeEach(function (done) { - client = redis.createClient.apply(null, args); - client.once('ready', function () { - client.flushdb(done); - }); - }); + it('does not move a value if it does not exist in the first set', function (done) { + client.sadd('foo', 'x', helper.isNumber(1)) + client.smove('foo', 'bar', 'y', helper.isNumber(0)) + client.sismember('foo', 'y', helper.isNumber(0)) + client.sismember('bar', 'y', helper.isNumber(0, done)) + }) - it('moves a value to a set that does not yet exist', function (done) { - client.sadd('foo', 'x', helper.isNumber(1)); - client.smove('foo', 'bar', 'x', helper.isNumber(1)); - client.sismember('foo', 'x', helper.isNumber(0)); - client.sismember('bar', 'x', helper.isNumber(1, done)); - }); - - it('does not move a value if it does not exist in the first set', function (done) { - client.sadd('foo', 'x', helper.isNumber(1)); - client.smove('foo', 'bar', 'y', helper.isNumber(0)); - client.sismember('foo', 'y', helper.isNumber(0)); - client.sismember('bar', 'y', helper.isNumber(0, done)); - }); - - afterEach(function () { - client.end(true); - }); - }); - }); -}); + afterEach(function () { + client.end(true) + }) + }) + }) +}) diff --git a/test/commands/sort.spec.js b/test/commands/sort.spec.js index 7cf6c0386a..cd342141b6 100644 --- a/test/commands/sort.spec.js +++ b/test/commands/sort.spec.js @@ -1,130 +1,127 @@ -'use strict'; +'use strict' -var assert = require('assert'); -var config = require('../lib/config'); -var helper = require('../helper'); -var redis = config.redis; +var assert = require('assert') +var config = require('../lib/config') +var helper = require('../helper') +var redis = config.redis function setupData (client, done) { - client.rpush('y', 'd'); - client.rpush('y', 'b'); - client.rpush('y', 'a'); - client.rpush('y', 'c'); + client.rpush('y', 'd') + client.rpush('y', 'b') + client.rpush('y', 'a') + client.rpush('y', 'c') - client.rpush('x', '3'); - client.rpush('x', '9'); - client.rpush('x', '2'); - client.rpush('x', '4'); + client.rpush('x', '3') + client.rpush('x', '9') + client.rpush('x', '2') + client.rpush('x', '4') - client.set('w3', '4'); - client.set('w9', '5'); - client.set('w2', '12'); - client.set('w4', '6'); + client.set('w3', '4') + client.set('w9', '5') + client.set('w2', '12') + client.set('w4', '6') - client.set('o2', 'buz'); - client.set('o3', 'foo'); - client.set('o4', 'baz'); - client.set('o9', 'bar'); + client.set('o2', 'buz') + client.set('o3', 'foo') + client.set('o4', 'baz') + client.set('o9', 'bar') - client.set('p2', 'qux'); - client.set('p3', 'bux'); - client.set('p4', 'lux'); - client.set('p9', 'tux', done); + client.set('p2', 'qux') + client.set('p3', 'bux') + client.set('p4', 'lux') + client.set('p9', 'tux', done) } -describe("The 'sort' method", function () { +describe('The \'sort\' method', function () { + helper.allTests(function (ip, args) { + describe('using ' + ip, function () { + var client - helper.allTests(function (ip, args) { + beforeEach(function (done) { + client = redis.createClient.apply(null, args) + client.once('error', done) + client.once('connect', function () { + client.flushdb() + setupData(client, done) + }) + }) - describe('using ' + ip, function () { - var client; + describe('alphabetical', function () { + it('sorts in ascending alphabetical order', function (done) { + client.sort('y', 'asc', 'alpha', function (err, sorted) { + assert.deepEqual(sorted, ['a', 'b', 'c', 'd']) + return done(err) + }) + }) - beforeEach(function (done) { - client = redis.createClient.apply(null, args); - client.once('error', done); - client.once('connect', function () { - client.flushdb(); - setupData(client, done); - }); - }); + it('sorts in descending alphabetical order', function (done) { + client.sort('y', 'desc', 'alpha', function (err, sorted) { + assert.deepEqual(sorted, ['d', 'c', 'b', 'a']) + return done(err) + }) + }) + }) - describe('alphabetical', function () { - it('sorts in ascending alphabetical order', function (done) { - client.sort('y', 'asc', 'alpha', function (err, sorted) { - assert.deepEqual(sorted, ['a', 'b', 'c', 'd']); - return done(err); - }); - }); + describe('numeric', function () { + it('sorts in ascending numeric order', function (done) { + client.sort('x', 'asc', function (err, sorted) { + assert.deepEqual(sorted, [2, 3, 4, 9]) + return done(err) + }) + }) - it('sorts in descending alphabetical order', function (done) { - client.sort('y', 'desc', 'alpha', function (err, sorted) { - assert.deepEqual(sorted, ['d', 'c', 'b', 'a']); - return done(err); - }); - }); - }); + it('sorts in descending numeric order', function (done) { + client.sort('x', 'desc', function (err, sorted) { + assert.deepEqual(sorted, [9, 4, 3, 2]) + return done(err) + }) + }) + }) - describe('numeric', function () { - it('sorts in ascending numeric order', function (done) { - client.sort('x', 'asc', function (err, sorted) { - assert.deepEqual(sorted, [2, 3, 4, 9]); - return done(err); - }); - }); + describe('pattern', function () { + it('handles sorting with a pattern', function (done) { + client.sort('x', 'by', 'w*', 'asc', function (err, sorted) { + assert.deepEqual(sorted, [3, 9, 4, 2]) + return done(err) + }) + }) - it('sorts in descending numeric order', function (done) { - client.sort('x', 'desc', function (err, sorted) { - assert.deepEqual(sorted, [9, 4, 3, 2]); - return done(err); - }); - }); - }); + it('handles sorting with a \'by\' pattern and 1 \'get\' pattern', function (done) { + client.sort('x', 'by', 'w*', 'asc', 'get', 'o*', function (err, sorted) { + assert.deepEqual(sorted, ['foo', 'bar', 'baz', 'buz']) + return done(err) + }) + }) - describe('pattern', function () { - it('handles sorting with a pattern', function (done) { - client.sort('x', 'by', 'w*', 'asc', function (err, sorted) { - assert.deepEqual(sorted, [3, 9, 4, 2]); - return done(err); - }); - }); + it('handles sorting with a \'by\' pattern and 2 \'get\' patterns', function (done) { + client.sort('x', 'by', 'w*', 'asc', 'get', 'o*', 'get', 'p*', function (err, sorted) { + assert.deepEqual(sorted, ['foo', 'bux', 'bar', 'tux', 'baz', 'lux', 'buz', 'qux']) + return done(err) + }) + }) - it("handles sorting with a 'by' pattern and 1 'get' pattern", function (done) { - client.sort('x', 'by', 'w*', 'asc', 'get', 'o*', function (err, sorted) { - assert.deepEqual(sorted, ['foo', 'bar', 'baz', 'buz']); - return done(err); - }); - }); + it('handles sorting with a \'by\' pattern and 2 \'get\' patterns with the array syntax', function (done) { + client.sort(['x', 'by', 'w*', 'asc', 'get', 'o*', 'get', 'p*'], function (err, sorted) { + assert.deepEqual(sorted, ['foo', 'bux', 'bar', 'tux', 'baz', 'lux', 'buz', 'qux']) + return done(err) + }) + }) - it("handles sorting with a 'by' pattern and 2 'get' patterns", function (done) { - client.sort('x', 'by', 'w*', 'asc', 'get', 'o*', 'get', 'p*', function (err, sorted) { - assert.deepEqual(sorted, ['foo', 'bux', 'bar', 'tux', 'baz', 'lux', 'buz', 'qux']); - return done(err); - }); - }); + it('sorting with a \'by\' pattern and 2 \'get\' patterns and stores results', function (done) { + client.sort('x', 'by', 'w*', 'asc', 'get', 'o*', 'get', 'p*', 'store', 'bacon', function (err) { + if (err) return done(err) + }) - it("handles sorting with a 'by' pattern and 2 'get' patterns with the array syntax", function (done) { - client.sort(['x', 'by', 'w*', 'asc', 'get', 'o*', 'get', 'p*'], function (err, sorted) { - assert.deepEqual(sorted, ['foo', 'bux', 'bar', 'tux', 'baz', 'lux', 'buz', 'qux']); - return done(err); - }); - }); + client.lrange('bacon', 0, -1, function (err, values) { + assert.deepEqual(values, ['foo', 'bux', 'bar', 'tux', 'baz', 'lux', 'buz', 'qux']) + return done(err) + }) + }) + }) - it("sorting with a 'by' pattern and 2 'get' patterns and stores results", function (done) { - client.sort('x', 'by', 'w*', 'asc', 'get', 'o*', 'get', 'p*', 'store', 'bacon', function (err) { - if (err) return done(err); - }); - - client.lrange('bacon', 0, -1, function (err, values) { - assert.deepEqual(values, ['foo', 'bux', 'bar', 'tux', 'baz', 'lux', 'buz', 'qux']); - return done(err); - }); - }); - }); - - afterEach(function () { - client.end(true); - }); - }); - }); - -}); + afterEach(function () { + client.end(true) + }) + }) + }) +}) diff --git a/test/commands/spop.spec.js b/test/commands/spop.spec.js index ec3e93fda3..64fdf83ba7 100644 --- a/test/commands/spop.spec.js +++ b/test/commands/spop.spec.js @@ -1,38 +1,36 @@ -'use strict'; +'use strict' -var assert = require('assert'); -var config = require('../lib/config'); -var helper = require('../helper'); -var redis = config.redis; +var assert = require('assert') +var config = require('../lib/config') +var helper = require('../helper') +var redis = config.redis -describe("The 'spop' method", function () { +describe('The \'spop\' method', function () { + helper.allTests(function (ip, args) { + describe('using ' + ip, function () { + var client - helper.allTests(function (ip, args) { + beforeEach(function (done) { + client = redis.createClient.apply(null, args) + client.once('ready', function () { + client.flushdb(done) + }) + }) - describe('using ' + ip, function () { - var client; + it('returns a random element from the set', function (done) { + client.sadd('zzz', 'member0', helper.isNumber(1)) + client.scard('zzz', helper.isNumber(1)) - beforeEach(function (done) { - client = redis.createClient.apply(null, args); - client.once('ready', function () { - client.flushdb(done); - }); - }); + client.spop('zzz', function (err, value) { + if (err) return done(err) + assert.strictEqual(value, 'member0') + client.scard('zzz', helper.isNumber(0, done)) + }) + }) - it('returns a random element from the set', function (done) { - client.sadd('zzz', 'member0', helper.isNumber(1)); - client.scard('zzz', helper.isNumber(1)); - - client.spop('zzz', function (err, value) { - if (err) return done(err); - assert.equal(value, 'member0'); - client.scard('zzz', helper.isNumber(0, done)); - }); - }); - - afterEach(function () { - client.end(true); - }); - }); - }); -}); + afterEach(function () { + client.end(true) + }) + }) + }) +}) diff --git a/test/commands/srem.spec.js b/test/commands/srem.spec.js index 7e8d166cc1..c8c6a19f5d 100644 --- a/test/commands/srem.spec.js +++ b/test/commands/srem.spec.js @@ -1,69 +1,67 @@ -'use strict'; +'use strict' -var assert = require('assert'); -var config = require('../lib/config'); -var helper = require('../helper'); -var redis = config.redis; +var assert = require('assert') +var config = require('../lib/config') +var helper = require('../helper') +var redis = config.redis -describe("The 'srem' method", function () { +describe('The \'srem\' method', function () { + helper.allTests(function (ip, args) { + describe('using ' + ip, function () { + var client - helper.allTests(function (ip, args) { + beforeEach(function (done) { + client = redis.createClient.apply(null, args) + client.once('ready', function () { + client.flushdb(done) + }) + }) - describe('using ' + ip, function () { - var client; + it('removes a value', function (done) { + client.sadd('set0', 'member0', helper.isNumber(1)) + client.srem('set0', 'member0', helper.isNumber(1)) + client.scard('set0', helper.isNumber(0, done)) + }) - beforeEach(function (done) { - client = redis.createClient.apply(null, args); - client.once('ready', function () { - client.flushdb(done); - }); - }); + it('handles attempting to remove a missing value', function (done) { + client.srem('set0', 'member0', helper.isNumber(0, done)) + }) - it('removes a value', function (done) { - client.sadd('set0', 'member0', helper.isNumber(1)); - client.srem('set0', 'member0', helper.isNumber(1)); - client.scard('set0', helper.isNumber(0, done)); - }); + it('allows multiple values to be removed', function (done) { + client.sadd('set0', ['member0', 'member1', 'member2'], helper.isNumber(3)) + client.srem('set0', ['member1', 'member2'], helper.isNumber(2)) + client.smembers('set0', function (err, res) { + assert.strictEqual(res.length, 1) + assert.ok(~res.indexOf('member0')) + return done(err) + }) + }) - it('handles attempting to remove a missing value', function (done) { - client.srem('set0', 'member0', helper.isNumber(0, done)); - }); + it('allows multiple values to be removed with sendCommand', function (done) { + client.sendCommand('sadd', ['set0', 'member0', 'member1', 'member2'], helper.isNumber(3)) + client.sendCommand('srem', ['set0', 'member1', 'member2'], helper.isNumber(2)) + client.smembers('set0', function (err, res) { + assert.strictEqual(res.length, 1) + assert.ok(~res.indexOf('member0')) + return done(err) + }) + }) - it('allows multiple values to be removed', function (done) { - client.sadd('set0', ['member0', 'member1', 'member2'], helper.isNumber(3)); - client.srem('set0', ['member1', 'member2'], helper.isNumber(2)); - client.smembers('set0', function (err, res) { - assert.strictEqual(res.length, 1); - assert.ok(~res.indexOf('member0')); - return done(err); - }); - }); + it('handles a value missing from the set of values being removed', function (done) { + client.sadd(['set0', 'member0', 'member1', 'member2'], helper.isNumber(3)) + client.srem(['set0', 'member3', 'member4'], helper.isNumber(0)) + client.smembers('set0', function (err, res) { + assert.strictEqual(res.length, 3) + assert.ok(~res.indexOf('member0')) + assert.ok(~res.indexOf('member1')) + assert.ok(~res.indexOf('member2')) + return done(err) + }) + }) - it('allows multiple values to be removed with sendCommand', function (done) { - client.sendCommand('sadd', ['set0', 'member0', 'member1', 'member2'], helper.isNumber(3)); - client.sendCommand('srem', ['set0', 'member1', 'member2'], helper.isNumber(2)); - client.smembers('set0', function (err, res) { - assert.strictEqual(res.length, 1); - assert.ok(~res.indexOf('member0')); - return done(err); - }); - }); - - it('handles a value missing from the set of values being removed', function (done) { - client.sadd(['set0', 'member0', 'member1', 'member2'], helper.isNumber(3)); - client.srem(['set0', 'member3', 'member4'], helper.isNumber(0)); - client.smembers('set0', function (err, res) { - assert.strictEqual(res.length, 3); - assert.ok(~res.indexOf('member0')); - assert.ok(~res.indexOf('member1')); - assert.ok(~res.indexOf('member2')); - return done(err); - }); - }); - - afterEach(function () { - client.end(true); - }); - }); - }); -}); + afterEach(function () { + client.end(true) + }) + }) + }) +}) diff --git a/test/commands/sunion.spec.js b/test/commands/sunion.spec.js index cc8eb62475..017953e43b 100644 --- a/test/commands/sunion.spec.js +++ b/test/commands/sunion.spec.js @@ -1,46 +1,44 @@ -'use strict'; +'use strict' -var assert = require('assert'); -var config = require('../lib/config'); -var helper = require('../helper'); -var redis = config.redis; +var assert = require('assert') +var config = require('../lib/config') +var helper = require('../helper') +var redis = config.redis -describe("The 'sunion' method", function () { +describe('The \'sunion\' method', function () { + helper.allTests(function (ip, args) { + describe('using ' + ip, function () { + var client - helper.allTests(function (ip, args) { + beforeEach(function (done) { + client = redis.createClient.apply(null, args) + client.once('ready', function () { + client.flushdb(done) + }) + }) - describe('using ' + ip, function () { - var client; + it('returns the union of a group of sets', function (done) { + client.sadd('sa', 'a', helper.isNumber(1)) + client.sadd('sa', 'b', helper.isNumber(1)) + client.sadd('sa', 'c', helper.isNumber(1)) - beforeEach(function (done) { - client = redis.createClient.apply(null, args); - client.once('ready', function () { - client.flushdb(done); - }); - }); + client.sadd('sb', 'b', helper.isNumber(1)) + client.sadd('sb', 'c', helper.isNumber(1)) + client.sadd('sb', 'd', helper.isNumber(1)) - it('returns the union of a group of sets', function (done) { - client.sadd('sa', 'a', helper.isNumber(1)); - client.sadd('sa', 'b', helper.isNumber(1)); - client.sadd('sa', 'c', helper.isNumber(1)); + client.sadd('sc', 'c', helper.isNumber(1)) + client.sadd('sc', 'd', helper.isNumber(1)) + client.sadd('sc', 'e', helper.isNumber(1)) - client.sadd('sb', 'b', helper.isNumber(1)); - client.sadd('sb', 'c', helper.isNumber(1)); - client.sadd('sb', 'd', helper.isNumber(1)); + client.sunion('sa', 'sb', 'sc', function (err, union) { + assert.deepEqual(union.sort(), ['a', 'b', 'c', 'd', 'e']) + return done(err) + }) + }) - client.sadd('sc', 'c', helper.isNumber(1)); - client.sadd('sc', 'd', helper.isNumber(1)); - client.sadd('sc', 'e', helper.isNumber(1)); - - client.sunion('sa', 'sb', 'sc', function (err, union) { - assert.deepEqual(union.sort(), ['a', 'b', 'c', 'd', 'e']); - return done(err); - }); - }); - - afterEach(function () { - client.end(true); - }); - }); - }); -}); + afterEach(function () { + client.end(true) + }) + }) + }) +}) diff --git a/test/commands/sunionstore.spec.js b/test/commands/sunionstore.spec.js index bd64c6f6b7..3de471d9fa 100644 --- a/test/commands/sunionstore.spec.js +++ b/test/commands/sunionstore.spec.js @@ -1,49 +1,47 @@ -'use strict'; +'use strict' -var assert = require('assert'); -var config = require('../lib/config'); -var helper = require('../helper'); -var redis = config.redis; +var assert = require('assert') +var config = require('../lib/config') +var helper = require('../helper') +var redis = config.redis -describe("The 'sunionstore' method", function () { +describe('The \'sunionstore\' method', function () { + helper.allTests(function (ip, args) { + describe('using ' + ip, function () { + var client - helper.allTests(function (ip, args) { + beforeEach(function (done) { + client = redis.createClient.apply(null, args) + client.once('ready', function () { + client.flushdb(done) + }) + }) - describe('using ' + ip, function () { - var client; + it('stores the result of a union', function (done) { + client.sadd('sa', 'a', helper.isNumber(1)) + client.sadd('sa', 'b', helper.isNumber(1)) + client.sadd('sa', 'c', helper.isNumber(1)) - beforeEach(function (done) { - client = redis.createClient.apply(null, args); - client.once('ready', function () { - client.flushdb(done); - }); - }); + client.sadd('sb', 'b', helper.isNumber(1)) + client.sadd('sb', 'c', helper.isNumber(1)) + client.sadd('sb', 'd', helper.isNumber(1)) - it('stores the result of a union', function (done) { - client.sadd('sa', 'a', helper.isNumber(1)); - client.sadd('sa', 'b', helper.isNumber(1)); - client.sadd('sa', 'c', helper.isNumber(1)); + client.sadd('sc', 'c', helper.isNumber(1)) + client.sadd('sc', 'd', helper.isNumber(1)) + client.sadd('sc', 'e', helper.isNumber(1)) - client.sadd('sb', 'b', helper.isNumber(1)); - client.sadd('sb', 'c', helper.isNumber(1)); - client.sadd('sb', 'd', helper.isNumber(1)); + client.sunionstore('foo', 'sa', 'sb', 'sc', helper.isNumber(5)) - client.sadd('sc', 'c', helper.isNumber(1)); - client.sadd('sc', 'd', helper.isNumber(1)); - client.sadd('sc', 'e', helper.isNumber(1)); + client.smembers('foo', function (err, members) { + assert.strictEqual(members.length, 5) + assert.deepEqual(members.sort(), ['a', 'b', 'c', 'd', 'e']) + return done(err) + }) + }) - client.sunionstore('foo', 'sa', 'sb', 'sc', helper.isNumber(5)); - - client.smembers('foo', function (err, members) { - assert.equal(members.length, 5); - assert.deepEqual(members.sort(), ['a', 'b', 'c', 'd', 'e']); - return done(err); - }); - }); - - afterEach(function () { - client.end(true); - }); - }); - }); -}); + afterEach(function () { + client.end(true) + }) + }) + }) +}) diff --git a/test/commands/ttl.spec.js b/test/commands/ttl.spec.js index ee52944969..b4bddf6672 100644 --- a/test/commands/ttl.spec.js +++ b/test/commands/ttl.spec.js @@ -1,37 +1,35 @@ -'use strict'; +'use strict' -var assert = require('assert'); -var config = require('../lib/config'); -var helper = require('../helper'); -var redis = config.redis; +var assert = require('assert') +var config = require('../lib/config') +var helper = require('../helper') +var redis = config.redis -describe("The 'ttl' method", function () { +describe('The \'ttl\' method', function () { + helper.allTests(function (ip, args) { + describe('using ' + ip, function () { + var client - helper.allTests(function (ip, args) { + beforeEach(function (done) { + client = redis.createClient.apply(null, args) + client.once('ready', function () { + client.flushdb(done) + }) + }) - describe('using ' + ip, function () { - var client; + it('returns the current ttl on a key', function (done) { + client.set(['ttl key', 'ttl val'], helper.isString('OK')) + client.expire(['ttl key', '100'], helper.isNumber(1)) + client.ttl(['ttl key'], function (err, ttl) { + assert(ttl >= 99) + assert(ttl <= 100) + done(err) + }) + }) - beforeEach(function (done) { - client = redis.createClient.apply(null, args); - client.once('ready', function () { - client.flushdb(done); - }); - }); - - it('returns the current ttl on a key', function (done) { - client.set(['ttl key', 'ttl val'], helper.isString('OK')); - client.expire(['ttl key', '100'], helper.isNumber(1)); - client.ttl(['ttl key'], function (err, ttl) { - assert(ttl >= 99); - assert(ttl <= 100); - done(err); - }); - }); - - afterEach(function () { - client.end(true); - }); - }); - }); -}); + afterEach(function () { + client.end(true) + }) + }) + }) +}) diff --git a/test/commands/type.spec.js b/test/commands/type.spec.js index 84df05f257..39847e7b71 100644 --- a/test/commands/type.spec.js +++ b/test/commands/type.spec.js @@ -1,56 +1,53 @@ -'use strict'; +'use strict' -var config = require('../lib/config'); -var helper = require('../helper'); -var redis = config.redis; +var config = require('../lib/config') +var helper = require('../helper') +var redis = config.redis -describe("The 'type' method", function () { +describe('The \'type\' method', function () { + helper.allTests(function (ip, args) { + describe('using ' + ip, function () { + var client - helper.allTests(function (ip, args) { + beforeEach(function (done) { + client = redis.createClient.apply(null, args) + client.once('ready', function () { + client.flushdb(done) + }) + }) - describe('using ' + ip, function () { - var client; + it('reports string type', function (done) { + client.set(['string key', 'should be a string'], helper.isString('OK')) + client.type(['string key'], helper.isString('string', done)) + }) - beforeEach(function (done) { - client = redis.createClient.apply(null, args); - client.once('ready', function () { - client.flushdb(done); - }); - }); + it('reports list type', function (done) { + client.rpush(['list key', 'should be a list'], helper.isNumber(1)) + client.type(['list key'], helper.isString('list', done)) + }) - it('reports string type', function (done) { - client.set(['string key', 'should be a string'], helper.isString('OK')); - client.type(['string key'], helper.isString('string', done)); - }); + it('reports set type', function (done) { + client.sadd(['set key', 'should be a set'], helper.isNumber(1)) + client.type(['set key'], helper.isString('set', done)) + }) - it('reports list type', function (done) { - client.rpush(['list key', 'should be a list'], helper.isNumber(1)); - client.type(['list key'], helper.isString('list', done)); - }); + it('reports zset type', function (done) { + client.zadd('zset key', ['10.0', 'should be a zset'], helper.isNumber(1)) + client.type(['zset key'], helper.isString('zset', done)) + }) - it('reports set type', function (done) { - client.sadd(['set key', 'should be a set'], helper.isNumber(1)); - client.type(['set key'], helper.isString('set', done)); - }); + it('reports hash type', function (done) { + client.hset('hash key', 'hashtest', 'should be a hash', helper.isNumber(1)) + client.type(['hash key'], helper.isString('hash', done)) + }) - it('reports zset type', function (done) { - client.zadd('zset key', ['10.0', 'should be a zset'], helper.isNumber(1)); - client.type(['zset key'], helper.isString('zset', done)); - }); + it('reports none for null key', function (done) { + client.type('not here yet', helper.isString('none', done)) + }) - it('reports hash type', function (done) { - client.hset('hash key', 'hashtest', 'should be a hash', helper.isNumber(1)); - client.type(['hash key'], helper.isString('hash', done)); - }); - - it('reports none for null key', function (done) { - client.type('not here yet', helper.isString('none', done)); - }); - - afterEach(function () { - client.end(true); - }); - }); - }); - -}); + afterEach(function () { + client.end(true) + }) + }) + }) +}) diff --git a/test/commands/watch.spec.js b/test/commands/watch.spec.js index dda5616c2a..163f8fbeb5 100644 --- a/test/commands/watch.spec.js +++ b/test/commands/watch.spec.js @@ -1,54 +1,50 @@ -'use strict'; +'use strict' -var assert = require('assert'); -var config = require('../lib/config'); -var helper = require('../helper'); -var redis = config.redis; +var assert = require('assert') +var config = require('../lib/config') +var helper = require('../helper') +var redis = config.redis -describe("The 'watch' method", function () { +describe('The \'watch\' method', function () { + helper.allTests(function (ip, args) { + var watched = 'foobar' - helper.allTests(function (ip, args) { + describe('using ' + ip, function () { + var client - var watched = 'foobar'; + beforeEach(function (done) { + client = redis.createClient.apply(null, args) + client.once('ready', function () { + client.flushdb(done) + }) + }) - describe('using ' + ip, function () { - var client; + afterEach(function () { + client.end(true) + }) - beforeEach(function (done) { - client = redis.createClient.apply(null, args); - client.once('ready', function () { - client.flushdb(done); - }); - }); + it('does not execute transaction if watched key was modified prior to execution', function (done) { + client.watch(watched) + client.incr(watched) + var multi = client.multi() + multi.incr(watched) + multi.exec(helper.isNull(done)) + }) - afterEach(function () { - client.end(true); - }); + it('successfully modifies other keys independently of transaction', function (done) { + client.set('unwatched', 200) - it('does not execute transaction if watched key was modified prior to execution', function (done) { - client.watch(watched); - client.incr(watched); - var multi = client.multi(); - multi.incr(watched); - multi.exec(helper.isNull(done)); - }); + client.set(watched, 0) + client.watch(watched) + client.incr(watched) - it('successfully modifies other keys independently of transaction', function (done) { - client.set('unwatched', 200); + client.multi().incr(watched).exec(function (err, replies) { + assert.strictEqual(err, null) + assert.strictEqual(replies, null, 'Aborted transaction multi-bulk reply should be null.') - client.set(watched, 0); - client.watch(watched); - client.incr(watched); - - client.multi().incr(watched).exec(function (err, replies) { - assert.strictEqual(replies, null, 'Aborted transaction multi-bulk reply should be null.'); - - client.get('unwatched', function (err, reply) { - assert.equal(reply, 200, 'Expected 200, got ' + reply); - return done(err); - }); - }); - }); - }); - }); -}); + client.get('unwatched', helper.isString('200', done)) + }) + }) + }) + }) +}) diff --git a/test/commands/zadd.spec.js b/test/commands/zadd.spec.js index 827630a389..c9e826d801 100644 --- a/test/commands/zadd.spec.js +++ b/test/commands/zadd.spec.js @@ -1,48 +1,46 @@ -'use strict'; +'use strict' -var config = require('../lib/config'); -var helper = require('../helper'); -var assert = require('assert'); -var redis = config.redis; +var config = require('../lib/config') +var helper = require('../helper') +var assert = require('assert') +var redis = config.redis -describe("The 'zadd' method", function () { +describe('The \'zadd\' method', function () { + helper.allTests(function (ip, args) { + describe('using ' + ip, function () { + var client - helper.allTests(function (ip, args) { + beforeEach(function (done) { + client = redis.createClient.apply(null, args) + client.once('ready', function () { + client.flushdb(done) + }) + }) - describe('using ' + ip, function () { - var client; + it('reports an error', function (done) { + if (helper.redisProcess().spawnFailed()) this.skip() + client.zadd('infinity', [+'5t', 'should not be possible'], helper.isError(done)) + }) - beforeEach(function (done) { - client = redis.createClient.apply(null, args); - client.once('ready', function () { - client.flushdb(done); - }); - }); + it('return inf / -inf', function (done) { + if (helper.redisProcess().spawnFailed()) this.skip() + helper.serverVersionAtLeast.call(this, client, [3, 0, 2]) + client.zadd('infinity', [+Infinity, 'should be inf'], helper.isNumber(1)) + client.zadd('infinity', ['inf', 'should be also be inf'], helper.isNumber(1)) + client.zadd('infinity', -Infinity, 'should be negative inf', helper.isNumber(1)) + client.zadd('infinity', [99999999999999999999999, 'should not be inf'], helper.isNumber(1)) + client.zrange('infinity', 0, -1, 'WITHSCORES', function (err, res) { + assert.strictEqual(err, null) + assert.strictEqual(res[5], 'inf') + assert.strictEqual(res[1], '-inf') + assert.strictEqual(res[3], '9.9999999999999992e+22') + done() + }) + }) - it('reports an error', function (done) { - if (helper.redisProcess().spawnFailed()) this.skip(); - client.zadd('infinity', [+'5t', 'should not be possible'], helper.isError(done)); - }); - - it('return inf / -inf', function (done) { - if (helper.redisProcess().spawnFailed()) this.skip(); - helper.serverVersionAtLeast.call(this, client, [3, 0, 2]); - client.zadd('infinity', [+Infinity, 'should be inf'], helper.isNumber(1)); - client.zadd('infinity', ['inf', 'should be also be inf'], helper.isNumber(1)); - client.zadd('infinity', -Infinity, 'should be negative inf', helper.isNumber(1)); - client.zadd('infinity', [99999999999999999999999, 'should not be inf'], helper.isNumber(1)); - client.zrange('infinity', 0, -1, 'WITHSCORES', function (err, res) { - assert.equal(res[5], 'inf'); - assert.equal(res[1], '-inf'); - assert.equal(res[3], '9.9999999999999992e+22'); - done(); - }); - }); - - afterEach(function () { - client.end(true); - }); - }); - }); - -}); + afterEach(function () { + client.end(true) + }) + }) + }) +}) diff --git a/test/commands/zscan.spec.js b/test/commands/zscan.spec.js index efa679a070..5e9e61cc13 100644 --- a/test/commands/zscan.spec.js +++ b/test/commands/zscan.spec.js @@ -1,50 +1,47 @@ -'use strict'; +'use strict' -var config = require('../lib/config'); -var helper = require('../helper'); -var assert = require('assert'); -var redis = config.redis; +var config = require('../lib/config') +var helper = require('../helper') +var assert = require('assert') +var redis = config.redis -describe("The 'zscan' method", function () { +describe('The \'zscan\' method', function () { + helper.allTests(function (ip, args) { + describe('using ' + ip, function () { + var client - helper.allTests(function (ip, args) { + beforeEach(function (done) { + client = redis.createClient.apply(null, args) + client.once('ready', function () { + client.flushdb(done) + }) + }) - describe('using ' + ip, function () { - var client; + it('return values', function (done) { + if (helper.redisProcess().spawnFailed()) this.skip() + helper.serverVersionAtLeast.call(this, client, [2, 8, 0]) + var hash = {} + var set = [] + var zset = ['zset:1'] + for (var i = 0; i < 500; i++) { + hash['key_' + i] = 'value_' + i + set.push('member_' + i) + zset.push(i, 'zMember_' + i) + } + client.hmset('hash:1', hash) + client.sadd('set:1', set) + client.zadd(zset) + client.zscan('zset:1', 0, 'MATCH', '*', 'COUNT', 500, function (err, res) { + assert(!err) + assert.strictEqual(res.length, 2) + assert.strictEqual(res[1].length, 1000) + done() + }) + }) - beforeEach(function (done) { - client = redis.createClient.apply(null, args); - client.once('ready', function () { - client.flushdb(done); - }); - }); - - it('return values', function (done) { - if (helper.redisProcess().spawnFailed()) this.skip(); - helper.serverVersionAtLeast.call(this, client, [2, 8, 0]); - var hash = {}; - var set = []; - var zset = ['zset:1']; - for (var i = 0; i < 500; i++) { - hash['key_' + i] = 'value_' + i; - set.push('member_' + i); - zset.push(i, 'zMember_' + i); - } - client.hmset('hash:1', hash); - client.sadd('set:1', set); - client.zadd(zset); - client.zscan('zset:1', 0, 'MATCH', '*', 'COUNT', 500, function (err, res) { - assert(!err); - assert.strictEqual(res.length, 2); - assert.strictEqual(res[1].length, 1000); - done(); - }); - }); - - afterEach(function () { - client.end(true); - }); - }); - }); - -}); + afterEach(function () { + client.end(true) + }) + }) + }) +}) diff --git a/test/commands/zscore.spec.js b/test/commands/zscore.spec.js index 8b95e52764..ade392e40f 100644 --- a/test/commands/zscore.spec.js +++ b/test/commands/zscore.spec.js @@ -1,35 +1,29 @@ -'use strict'; +'use strict' -var config = require('../lib/config'); -var helper = require('../helper'); -var assert = require('assert'); -var redis = config.redis; +var config = require('../lib/config') +var helper = require('../helper') +var redis = config.redis -describe("The 'zscore' method", function () { +describe('The \'zscore\' method', function () { + helper.allTests(function (ip, args) { + describe('using ' + ip, function () { + var client - helper.allTests(function (ip, args) { + beforeEach(function (done) { + client = redis.createClient.apply(null, args) + client.once('ready', function () { + client.flushdb(done) + }) + }) - describe('using ' + ip, function () { - var client; + it('should return the score of member in the sorted set at key', function (done) { + client.zadd('myzset', 1, 'one') + client.zscore('myzset', 'one', helper.isString('1', done)) + }) - beforeEach(function (done) { - client = redis.createClient.apply(null, args); - client.once('ready', function () { - client.flushdb(done); - }); - }); - - it('should return the score of member in the sorted set at key', function (done) { - client.zadd('myzset', 1, 'one'); - client.zscore('myzset', 'one', function (err, res) { - assert.equal(res, 1); - done(); - }); - }); - - afterEach(function () { - client.end(true); - }); - }); - }); -}); + afterEach(function () { + client.end(true) + }) + }) + }) +}) diff --git a/test/conect.slave.spec.js b/test/conect.slave.spec.js index 4808e8df66..d79502d4b9 100644 --- a/test/conect.slave.spec.js +++ b/test/conect.slave.spec.js @@ -1,99 +1,97 @@ -'use strict'; +'use strict' -var assert = require('assert'); -var config = require('./lib/config'); -var helper = require('./helper'); -var RedisProcess = require('./lib/redis-process'); -var rp; -var path = require('path'); -var redis = config.redis; +var assert = require('assert') +var config = require('./lib/config') +var helper = require('./helper') +var RedisProcess = require('./lib/redis-process') +var rp +var path = require('path') +var redis = config.redis -if (process.platform === 'win32') { - // TODO: Fix redis process spawn on windows - return; -} - -describe('master slave sync', function () { - var master = null; - var slave = null; + // TODO: Fix redis process spawn on windows +if (process.platform !== 'win32') { + describe('master slave sync', function () { + var master = null + var slave = null before(function (done) { - helper.stopRedis(function () { - helper.startRedis('./conf/password.conf', done); - }); - }); + helper.stopRedis(function () { + helper.startRedis('./conf/password.conf', done) + }) + }) before(function (done) { - if (helper.redisProcess().spawnFailed()) return done(); - master = redis.createClient({ - password: 'porkchopsandwiches' - }); - var multi = master.multi(); - var i = 0; - while (i < 1000) { - i++; - // Write some data in the redis instance, so there's something to sync - multi.set('foo' + i, 'bar' + new Array(500).join(Math.random())); - } - multi.exec(done); - }); + if (helper.redisProcess().spawnFailed()) return done() + master = redis.createClient({ + password: 'porkchopsandwiches' + }) + var multi = master.multi() + var i = 0 + while (i < 1000) { + i++ + // Write some data in the redis instance, so there's something to sync + multi.set('foo' + i, 'bar' + new Array(500).join(Math.random())) + } + multi.exec(done) + }) it('sync process and no master should delay ready being emitted for slaves', function (done) { - if (helper.redisProcess().spawnFailed()) this.skip(); + if (helper.redisProcess().spawnFailed()) this.skip() - var port = 6381; - var firstInfo; - slave = redis.createClient({ - port: port, - retryStrategy: function (options) { - // Try to reconnect in very small intervals to catch the master_link_status down before the sync completes - return 10; - } - }); + var port = 6381 + var firstInfo + slave = redis.createClient({ + port: port, + retryStrategy: function (options) { + // Try to reconnect in very small intervals to catch the master_link_status down before the sync completes + return 10 + } + }) - var tmp = slave.info.bind(slave); - var i = 0; - slave.info = function (err, res) { - i++; - tmp(err, res); - if (!firstInfo || Object.keys(firstInfo).length === 0) { - firstInfo = slave.serverInfo; - } - }; + var tmp = slave.info.bind(slave) + var i = 0 + slave.info = function (err, res) { + i++ + tmp(err, res) + if (!firstInfo || Object.keys(firstInfo).length === 0) { + firstInfo = slave.serverInfo + } + } - slave.on('connect', function () { - assert.strictEqual(i, 0); - }); + slave.on('connect', function () { + assert.strictEqual(i, 0) + }) - var end = helper.callFuncAfter(done, 2); + var end = helper.callFuncAfter(done, 2) - slave.on('ready', function () { - assert.strictEqual(this.serverInfo.master_link_status, 'up'); - assert.strictEqual(firstInfo.master_link_status, 'down'); - assert(i > 1); - this.get('foo300', function (err, res) { - assert.strictEqual(res.substr(0, 3), 'bar'); - end(err); - }); - }); + slave.on('ready', function () { + assert.strictEqual(this.serverInfo.master_link_status, 'up') + assert.strictEqual(firstInfo.master_link_status, 'down') + assert(i > 1) + this.get('foo300', function (err, res) { + assert.strictEqual(res.substr(0, 3), 'bar') + end(err) + }) + }) - RedisProcess.start(function (err, _rp) { - rp = _rp; - end(err); - }, path.resolve(__dirname, './conf/slave.conf'), port); - }); + RedisProcess.start(function (err, _rp) { + rp = _rp + end(err) + }, path.resolve(__dirname, './conf/slave.conf'), port) + }) after(function (done) { - if (helper.redisProcess().spawnFailed()) return done(); - var end = helper.callFuncAfter(done, 3); - rp.stop(end); - slave.end(true); - master.flushdb(function (err) { - end(err); - master.end(true); - }); - helper.stopRedis(function () { - helper.startRedis('./conf/redis.conf', end); - }); - }); -}); + if (helper.redisProcess().spawnFailed()) return done() + var end = helper.callFuncAfter(done, 3) + rp.stop(end) + slave.end(true) + master.flushdb(function (err) { + end(err) + master.end(true) + }) + helper.stopRedis(function () { + helper.startRedis('./conf/redis.conf', end) + }) + }) + }) +} diff --git a/test/connection.spec.js b/test/connection.spec.js index 2f34a56f5f..193423ff94 100644 --- a/test/connection.spec.js +++ b/test/connection.spec.js @@ -1,588 +1,580 @@ -'use strict'; +'use strict' -var assert = require('assert'); -var config = require('./lib/config'); -var helper = require('./helper'); -var redis = config.redis; -var intercept = require('intercept-stdout'); -var net = require('net'); -var client; +var assert = require('assert') +var config = require('./lib/config') +var helper = require('./helper') +var redis = config.redis +var intercept = require('intercept-stdout') +var net = require('net') +var client describe('connection tests', function () { + beforeEach(function () { + client = null + }) + afterEach(function () { + client.end(true) + }) - beforeEach(function () { - client = null; - }); - afterEach(function () { - client.end(true); - }); + it('unofficially support for a private stream', function () { + // While using a private stream, reconnection and other features are not going to work properly. + // Besides that some functions also have to be monkey patched to be safe from errors in this case. + // Therefore this is not officially supported! + var socket = new net.Socket() + client = new redis.RedisClient({ + prefix: 'test' + }, socket) + assert.strictEqual(client.stream, socket) + assert.strictEqual(client.stream.listeners('error').length, 1) + assert.strictEqual(client.address, '"Private stream"') + // Pretend a reconnect event + client.createStream() + assert.strictEqual(client.stream, socket) + assert.strictEqual(client.stream.listeners('error').length, 1) + }) - it('unofficially support for a private stream', function () { - // While using a private stream, reconnection and other features are not going to work properly. - // Besides that some functions also have to be monkey patched to be safe from errors in this case. - // Therefore this is not officially supported! - var socket = new net.Socket(); - client = new redis.RedisClient({ - prefix: 'test' - }, socket); - assert.strictEqual(client.stream, socket); - assert.strictEqual(client.stream.listeners('error').length, 1); - assert.strictEqual(client.address, '"Private stream"'); - // Pretent a reconnect event - client.createStream(); - assert.strictEqual(client.stream, socket); - assert.strictEqual(client.stream.listeners('error').length, 1); - }); + describe('quit on lost connections', function () { + it('calling quit while the connection is down should not end in reconnecting version a', function (done) { + var called = 0 + client = redis.createClient({ + port: 9999, + retryStrategy: function (options) { + var bool = client.quit(function (err, res) { + assert.strictEqual(res, 'OK') + assert.strictEqual(err, null) + assert.strictEqual(called++, -1) + setTimeout(done, 25) + }) + assert.strictEqual(bool, false) + assert.strictEqual(called++, 0) + return 5 + } + }) + client.set('foo', 'bar', function (err, res) { + assert.strictEqual(err.message, 'Stream connection ended and command aborted.') + called = -1 + }) + }) - describe('quit on lost connections', function () { + it('calling quit while the connection is down should not end in reconnecting version b', function (done) { + var called = false + client = redis.createClient(9999) + client.set('foo', 'bar', function (err, res) { + assert.strictEqual(err.message, 'Stream connection ended and command aborted.') + called = true + }) + var bool = client.quit(function (err, res) { + assert.strictEqual(res, 'OK') + assert.strictEqual(err, null) + assert(called) + done() + }) + assert.strictEqual(bool, false) + }) - it('calling quit while the connection is down should not end in reconnecting version a', function (done) { - var called = 0; - client = redis.createClient({ - port: 9999, - retryStrategy: function (options) { - var bool = client.quit(function (err, res) { - assert.strictEqual(res, 'OK'); - assert.strictEqual(err, null); - assert.strictEqual(called++, -1); - setTimeout(done, 25); - }); - assert.strictEqual(bool, false); - assert.strictEqual(called++, 0); - return 5; - } - }); - client.set('foo', 'bar', function (err, res) { - assert.strictEqual(err.message, 'Stream connection ended and command aborted.'); - called = -1; - }); - }); + it('calling quit while the connection is down without offline queue should end the connection right away', function (done) { + var called = false + client = redis.createClient(9999, { + enableOfflineQueue: false + }) + client.set('foo', 'bar', function (err, res) { + assert.strictEqual(err.message, 'SET can\'t be processed. The connection is not yet established and the offline queue is deactivated.') + called = true + }) + var bool = client.quit(function (err, res) { + assert.strictEqual(res, 'OK') + assert.strictEqual(err, null) + assert(called) + done() + }) + // TODO: In v.3 the quit command would be fired right away, so bool should be true + assert.strictEqual(bool, false) + }) - it('calling quit while the connection is down should not end in reconnecting version b', function (done) { - var called = false; - client = redis.createClient(9999); - client.set('foo', 'bar', function (err, res) { - assert.strictEqual(err.message, 'Stream connection ended and command aborted.'); - called = true; - }); - var bool = client.quit(function (err, res) { - assert.strictEqual(res, 'OK'); - assert.strictEqual(err, null); - assert(called); - done(); - }); - assert.strictEqual(bool, false); - }); + it('calling quit while connected without offline queue should end the connection when all commands have finished', function (done) { + var called = false + client = redis.createClient({ + enableOfflineQueue: false + }) + client.on('ready', function () { + client.set('foo', 'bar', function (err, res) { + assert.strictEqual(err, null) + assert.strictEqual(res, 'OK') + called = true + }) + var bool = client.quit(function (err, res) { + assert.strictEqual(res, 'OK') + assert.strictEqual(err, null) + assert(called) + done() + }) + // TODO: In v.3 the quit command would be fired right away, so bool should be true + assert.strictEqual(bool, true) + }) + }) - it('calling quit while the connection is down without offline queue should end the connection right away', function (done) { - var called = false; - client = redis.createClient(9999, { - enableOfflineQueue: false - }); - client.set('foo', 'bar', function (err, res) { - assert.strictEqual(err.message, 'SET can\'t be processed. The connection is not yet established and the offline queue is deactivated.'); - called = true; - }); - var bool = client.quit(function (err, res) { - assert.strictEqual(res, 'OK'); - assert.strictEqual(err, null); - assert(called); - done(); - }); - // TODO: In v.3 the quit command would be fired right away, so bool should be true - assert.strictEqual(bool, false); - }); + it('do not quit before connected or a connection issue is detected', function (done) { + client = redis.createClient() + client.set('foo', 'bar', helper.isString('OK')) + var bool = client.quit(done) + assert.strictEqual(bool, false) + }) - it('calling quit while connected without offline queue should end the connection when all commands have finished', function (done) { - var called = false; - client = redis.createClient({ - enableOfflineQueue: false - }); - client.on('ready', function () { + it('quit "succeeds" even if the client connection is closed while doing so', function (done) { + client = redis.createClient() + client.set('foo', 'bar', function (err, res) { + assert.strictEqual(err, null) + assert.strictEqual(res, 'OK') + client.quit(function (err, res) { + assert.strictEqual(res, 'OK') + done(err) + }) + client.end(true) // Flushing the quit command should result in a success + }) + }) + + it('quit right away if connection drops while quit command is on the fly', function (done) { + client = redis.createClient() + client.once('ready', function () { + client.set('foo', 'bar', helper.isError()) + var bool = client.quit(done) + assert.strictEqual(bool, true) + process.nextTick(function () { + client.stream.destroy() + }) + }) + }) + }) + + helper.allTests(function (ip, args) { + describe('using ' + ip, function () { + describe('on lost connection', function () { + it('end connection while retry is still ongoing', function (done) { + var connectTimeout = 1000 // in ms + client = redis.createClient({ + connectTimeout: connectTimeout + }) + + client.once('ready', function () { + helper.killConnection(client) + }) + + client.on('reconnecting', function (params) { + client.end(true) + assert.strictEqual(params.timesConnected, 1) + setTimeout(done, 5) + }) + }) + + it.skip('can not connect with wrong host / port in the options object', function (done) { + var options = { + host: 'somewhere', + port: 6379, + family: ip, + retryStrategy: function () {} + } + client = redis.createClient(options) + assert.strictEqual(client.connectionOptions.family, ip === 'IPv6' ? 6 : 4) + assert.strictEqual(Object.keys(options).length, 4) + var end = helper.callFuncAfter(done, 2) + + client.on('error', function (err) { + assert(/CONNECTION_BROKEN|ENOTFOUND|EAI_AGAIN/.test(err.code)) + end() + }) + }) + + it('emits error once if reconnecting after command has been executed but not yet returned without callback', function (done) { + client = redis.createClient.apply(null, args) + + client.on('ready', function () { + client.set('foo', 'bar', function (err) { + assert.strictEqual(err.code, 'UNCERTAIN_STATE') + done() + }) + // Abort connection before the value returned + client.stream.destroy() + }) + }) + + it('retryStrategy used to reconnect with individual error', function (done) { + client = redis.createClient({ + retryStrategy: function (options) { + if (options.totalRetryTime > 150) { client.set('foo', 'bar', function (err, res) { - assert.strictEqual(res, 'OK'); - called = true; - }); - var bool = client.quit(function (err, res) { - assert.strictEqual(res, 'OK'); - assert.strictEqual(err, null); - assert(called); - done(); - }); - // TODO: In v.3 the quit command would be fired right away, so bool should be true - assert.strictEqual(bool, true); - }); - }); + assert.strictEqual(err.message, 'Stream connection ended and command aborted.') + assert.strictEqual(err.origin.message, 'Connection timeout') + done() + }) + // Pass a individual error message to the error handler + return new Error('Connection timeout') + } + return Math.min(options.attempt * 25, 200) + }, + port: 9999 + }) + }) - it('do not quit before connected or a connection issue is detected', function (done) { - client = redis.createClient(); - client.set('foo', 'bar', helper.isString('OK')); - var bool = client.quit(done); - assert.strictEqual(bool, false); - }); + it('retryStrategy used to reconnect', function (done) { + client = redis.createClient({ + retryStrategy: function (options) { + if (options.totalRetryTime > 150) { + client.set('foo', 'bar', function (err, res) { + assert.strictEqual(err.message, 'Stream connection ended and command aborted.') + assert.strictEqual(err.code, 'NR_CLOSED') + assert.strictEqual(err.origin.code, 'ECONNREFUSED') + done() + }) + return false + } + return Math.min(options.attempt * 25, 200) + }, + port: 9999 + }) + }) - it('quit "succeeds" even if the client connection is closed while doing so', function (done) { - client = redis.createClient(); - client.set('foo', 'bar', function (err, res) { - assert.strictEqual(res, 'OK'); - client.quit(function (err, res) { - assert.strictEqual(res, 'OK'); - done(err); - }); - client.end(true); // Flushing the quit command should result in a success - }); - }); + it('retryStrategy used to reconnect with defaults', function (done) { + var unhookIntercept = intercept(function () { + return '' + }) + redis.debugMode = true + client = redis.createClient({ + retryStrategy: function (options) { + client.set('foo', 'bar') + assert(redis.debugMode) + return null + } + }) + setTimeout(function () { + client.stream.destroy() + }, 50) + client.on('error', function (err) { + assert.strictEqual(err.code, 'NR_CLOSED') + assert.strictEqual(err.message, 'Stream connection ended and command aborted.') + unhookIntercept() + redis.debugMode = false + done() + }) + }) + }) - it('quit right away if connection drops while quit command is on the fly', function (done) { - client = redis.createClient(); - client.once('ready', function () { - client.set('foo', 'bar', helper.isError()); - var bool = client.quit(done); - assert.strictEqual(bool, true); - process.nextTick(function () { - client.stream.destroy(); - }); - }); - }); + describe('when not connected', function () { + // TODO: Fix this test + it.skip('emit an error after the socket timeout exceeded the connectTimeout time', function (done) { + var connectTimeout = 500 // in ms + client = redis.createClient({ + // Auto detect ipv4 and use non routable ip to trigger the timeout + host: '10.255.255.1', + connectTimeout: connectTimeout + }) + process.nextTick(function () { + assert.strictEqual(client.stream.listeners('timeout').length, 1) + }) + assert.strictEqual(client.address, '10.255.255.1:6379') + assert.strictEqual(client.connectionOptions.family, 4) - }); + client.on('reconnecting', function (params) { + throw new Error('No reconnect, since no connection was ever established') + }) - helper.allTests(function (ip, args) { + var time = Date.now() + client.on('error', function (err) { + if (err.code === 'ENETUNREACH') { // The test is run without a internet connection. Pretent it works + return done() + } + assert(/Redis connection in broken state: connection timeout.*?exceeded./.test(err.message), err.message) + // The code execution on windows is very slow at times + var add = process.platform !== 'win32' ? 15 : 200 + var now = Date.now() + assert(now - time < connectTimeout + add, 'The real timeout time should be below ' + (connectTimeout + add) + 'ms but is: ' + (now - time)) + // Timers sometimes trigger early (e.g. 1ms to early) + assert(now - time >= connectTimeout - 5, 'The real timeout time should be above ' + connectTimeout + 'ms, but it is: ' + (now - time)) + done() + }) + }) - describe('using ' + ip, function () { + it('use the system socket timeout if the connectTimeout has not been provided', function (done) { + client = redis.createClient({ + host: '2001:db8::ff00:42:8329' // auto detect ip v6 + }) + assert.strictEqual(client.address, '2001:db8::ff00:42:8329:6379') + assert.strictEqual(client.connectionOptions.family, 6) + process.nextTick(function () { + assert.strictEqual(client.stream.listeners('timeout').length, 0) + done() + }) + client.end(true) + }) - describe('on lost connection', function () { + it('clears the socket timeout after a connection has been established', function (done) { + client = redis.createClient({ + connectTimeout: 1000 + }) + process.nextTick(function () { + assert.strictEqual(client.stream._idleTimeout, 1000) + }) + client.on('connect', function () { + assert.strictEqual(client.stream._idleTimeout, -1) + assert.strictEqual(client.stream.listeners('timeout').length, 0) + client.on('ready', done) + }) + }) - it('end connection while retry is still ongoing', function (done) { - var connectTimeout = 1000; // in ms - client = redis.createClient({ - connectTimeout: connectTimeout - }); + it('connect with host and port provided in the options object', function (done) { + client = redis.createClient({ + host: 'localhost', + port: '6379', + connectTimeout: 1000 + }) - client.once('ready', function () { - helper.killConnection(client); - }); + client.once('ready', done) + }) - client.on('reconnecting', function (params) { - client.end(true); - assert.strictEqual(params.timesConnected, 1); - setTimeout(done, 5); - }); - }); + it('connect with path provided in the options object', function (done) { + if (process.platform === 'win32') { + this.skip() + } + client = redis.createClient({ + path: '/tmp/redis.sock', + connectTimeout: 1000 + }) - it.skip('can not connect with wrong host / port in the options object', function (done) { - var options = { - host: 'somewhere', - port: 6379, - family: ip, - retryStrategy: function () {} - }; - client = redis.createClient(options); - assert.strictEqual(client.connectionOptions.family, ip === 'IPv6' ? 6 : 4); - assert.strictEqual(Object.keys(options).length, 4); - var end = helper.callFuncAfter(done, 2); + var end = helper.callFuncAfter(done, 2) - client.on('error', function (err) { - assert(/CONNECTION_BROKEN|ENOTFOUND|EAI_AGAIN/.test(err.code)); - end(); - }); + client.once('ready', end) + client.set('foo', 'bar', end) + }) - }); + it('connects correctly with args', function (done) { + client = redis.createClient.apply(null, args) + client.on('error', done) - it('emits error once if reconnecting after command has been executed but not yet returned without callback', function (done) { - client = redis.createClient.apply(null, args); + client.once('ready', function () { + client.removeListener('error', done) + client.get('recon 1', done) + }) + }) - client.on('ready', function () { - client.set('foo', 'bar', function (err) { - assert.strictEqual(err.code, 'UNCERTAIN_STATE'); - done(); - }); - // Abort connection before the value returned - client.stream.destroy(); - }); - }); + it('connects correctly with default values', function (done) { + client = redis.createClient() + client.on('error', done) - it('retryStrategy used to reconnect with individual error', function (done) { - client = redis.createClient({ - retryStrategy: function (options) { - if (options.totalRetryTime > 150) { - client.set('foo', 'bar', function (err, res) { - assert.strictEqual(err.message, 'Stream connection ended and command aborted.'); - assert.strictEqual(err.origin.message, 'Connection timeout'); - done(); - }); - // Pass a individual error message to the error handler - return new Error('Connection timeout'); - } - return Math.min(options.attempt * 25, 200); - }, - port: 9999 - }); - }); + client.once('ready', function () { + client.removeListener('error', done) + client.get('recon 1', done) + }) + }) - it('retryStrategy used to reconnect', function (done) { - client = redis.createClient({ - retryStrategy: function (options) { - if (options.totalRetryTime > 150) { - client.set('foo', 'bar', function (err, res) { - assert.strictEqual(err.message, 'Stream connection ended and command aborted.'); - assert.strictEqual(err.code, 'NR_CLOSED'); - assert.strictEqual(err.origin.code, 'ECONNREFUSED'); - done(); - }); - return false; - } - return Math.min(options.attempt * 25, 200); - }, - port: 9999 - }); - }); + it('connects with a port only', function (done) { + client = redis.createClient(6379) + assert.strictEqual(client.connectionOptions.family, 4) + client.on('error', done) - it('retryStrategy used to reconnect with defaults', function (done) { - var unhookIntercept = intercept(function () { - return ''; - }); - redis.debugMode = true; - client = redis.createClient({ - retryStrategy: function (options) { - client.set('foo', 'bar'); - assert(redis.debugMode); - return null; - } - }); - setTimeout(function () { - client.stream.destroy(); - }, 50); - client.on('error', function (err) { - assert.strictEqual(err.code, 'NR_CLOSED'); - assert.strictEqual(err.message, 'Stream connection ended and command aborted.'); - unhookIntercept(); - redis.debugMode = false; - done(); - }); - }); - }); + client.once('ready', function () { + client.removeListener('error', done) + client.get('recon 1', done) + }) + }) - describe('when not connected', function () { + it('connects correctly to localhost', function (done) { + client = redis.createClient(null, null) + client.on('error', done) - // TODO: Fix this test - it.skip('emit an error after the socket timeout exceeded the connectTimeout time', function (done) { - var connectTimeout = 500; // in ms - client = redis.createClient({ - // Auto detect ipv4 and use non routable ip to trigger the timeout - host: '10.255.255.1', - connectTimeout: connectTimeout - }); - process.nextTick(function () { - assert.strictEqual(client.stream.listeners('timeout').length, 1); - }); - assert.strictEqual(client.address, '10.255.255.1:6379'); - assert.strictEqual(client.connectionOptions.family, 4); + client.once('ready', function () { + client.removeListener('error', done) + client.get('recon 1', done) + }) + }) - client.on('reconnecting', function (params) { - throw new Error('No reconnect, since no connection was ever established'); - }); + it('connects correctly to the provided host with the port set to null', function (done) { + client = redis.createClient(null, 'localhost') + client.on('error', done) + assert.strictEqual(client.address, 'localhost:6379') - var time = Date.now(); - client.on('error', function (err) { - if (err.code === 'ENETUNREACH') { // The test is run without a internet connection. Pretent it works - return done(); - } - assert(/Redis connection in broken state: connection timeout.*?exceeded./.test(err.message), err.message); - // The code execution on windows is very slow at times - var add = process.platform !== 'win32' ? 15 : 200; - var now = Date.now(); - assert(now - time < connectTimeout + add, 'The real timeout time should be below ' + (connectTimeout + add) + 'ms but is: ' + (now - time)); - // Timers sometimes trigger early (e.g. 1ms to early) - assert(now - time >= connectTimeout - 5, 'The real timeout time should be above ' + connectTimeout + 'ms, but it is: ' + (now - time)); - done(); - }); - }); + client.once('ready', function () { + client.set('foo', 'bar') + client.get('foo', function (err, res) { + assert.strictEqual(res, 'bar') + done(err) + }) + }) + }) - it('use the system socket timeout if the connectTimeout has not been provided', function (done) { - client = redis.createClient({ - host: '2001:db8::ff00:42:8329' // auto detect ip v6 - }); - assert.strictEqual(client.address, '2001:db8::ff00:42:8329:6379'); - assert.strictEqual(client.connectionOptions.family, 6); - process.nextTick(function () { - assert.strictEqual(client.stream.listeners('timeout').length, 0); - done(); - }); - client.end(true); - }); + it('connects correctly to localhost and no ready check', function (done) { + client = redis.createClient(undefined, undefined, { + noReadyCheck: true + }) + client.on('error', done) - it('clears the socket timeout after a connection has been established', function (done) { - client = redis.createClient({ - connectTimeout: 1000 - }); - process.nextTick(function () { - assert.strictEqual(client.stream._idleTimeout, 1000); - }); - client.on('connect', function () { - assert.strictEqual(client.stream._idleTimeout, -1); - assert.strictEqual(client.stream.listeners('timeout').length, 0); - client.on('ready', done); - }); - }); + client.once('ready', function () { + client.set('foo', 'bar') + client.get('foo', function (err, res) { + assert.strictEqual(res, 'bar') + done(err) + }) + }) + }) - it('connect with host and port provided in the options object', function (done) { - client = redis.createClient({ - host: 'localhost', - port: '6379', - connectTimeout: 1000 - }); + it('connects correctly to the provided host with the port set to undefined', function (done) { + client = redis.createClient(undefined, 'localhost', { + noReadyCheck: true + }) + client.on('error', done) + assert.strictEqual(client.address, 'localhost:6379') - client.once('ready', done); - }); + client.once('ready', function () { + client.set('foo', 'bar') + client.get('foo', function (err, res) { + assert.strictEqual(res, 'bar') + done(err) + }) + }) + }) - it('connect with path provided in the options object', function (done) { - if (process.platform === 'win32') { - this.skip(); - } - client = redis.createClient({ - path: '/tmp/redis.sock', - connectTimeout: 1000 - }); + it('connects correctly even if the info command is not present on the redis server', function (done) { + client = redis.createClient.apply(null, args) + client.info = function (cb) { + // Mock the result + cb(new Error('ERR unknown command \'info\'')) + } + client.once('ready', function () { + assert.strictEqual(Object.keys(client.serverInfo).length, 0) + done() + }) + }) - var end = helper.callFuncAfter(done, 2); + it('fake the stream to mock redis', function () { + // This is needed for libraries that want to mock the stream like fakeredis + var temp = redis.RedisClient.prototype.createStream + var createStreamString = String(temp) + redis.RedisClient.prototype.createStream = function () { + this.connected = true + this.ready = true + } + client = new redis.RedisClient() + assert.strictEqual(client.stream, undefined) + assert.strictEqual(client.ready, true) + assert.strictEqual(client.connected, true) + client.end = function () {} + assert(createStreamString !== String(redis.RedisClient.prototype.createStream)) + redis.RedisClient.prototype.createStream = temp + assert(createStreamString === String(redis.RedisClient.prototype.createStream)) + }) - client.once('ready', end); - client.set('foo', 'bar', end); - }); + if (ip === 'IPv4') { + it('allows connecting with the redis url to the default host and port, select db 3 and warn about duplicate db option', function (done) { + client = redis.createClient('redis:///3?db=3') + assert.strictEqual(client.selectedDb, '3') + client.on('ready', done) + }) - it('connects correctly with args', function (done) { - client = redis.createClient.apply(null, args); - client.on('error', done); + it('allows connecting with the redis url and the default port and auth provided even though it is not required', function (done) { + client = redis.createClient('redis://:porkchopsandwiches@' + config.HOST[ip] + '/') + var end = helper.callFuncAfter(done, 2) + client.on('warning', function (msg) { + assert.strictEqual(msg, 'Warning: Redis server does not require a password, but a password was supplied.') + end() + }) + client.on('ready', end) + }) - client.once('ready', function () { - client.removeListener('error', done); - client.get('recon 1', done); - }); - }); + it('allows connecting with the redis url as first parameter and the options as second parameter', function (done) { + client = redis.createClient('//127.0.0.1', { + connectTimeout: 1000 + }) + assert.strictEqual(client.options.connectTimeout, 1000) + client.on('ready', done) + }) - it('connects correctly with default values', function (done) { - client = redis.createClient(); - client.on('error', done); + it('allows connecting with the redis url in the options object and works with protocols other than the redis protocol (e.g. http)', function (done) { + client = redis.createClient({ + url: 'http://foo:porkchopsandwiches@' + config.HOST[ip] + '/3' + }) + assert.strictEqual(client.authPass, 'porkchopsandwiches') + assert.strictEqual(+client.selectedDb, 3) + assert(!client.options.port) + assert.strictEqual(client.options.host, config.HOST[ip]) + client.on('ready', done) + }) - client.once('ready', function () { - client.removeListener('error', done); - client.get('recon 1', done); - }); - }); + it('allows connecting with the redis url and no auth and options as second parameter', function (done) { + var options = { + detectBuffers: false + } + client = redis.createClient('redis://' + config.HOST[ip] + ':' + config.PORT, options) + assert.strictEqual(Object.keys(options).length, 1) + client.on('ready', done) + }) - it('connects with a port only', function (done) { - client = redis.createClient(6379); - assert.strictEqual(client.connectionOptions.family, 4); - client.on('error', done); + it('allows connecting with the redis url and no auth and options as third parameter', function (done) { + client = redis.createClient('redis://' + config.HOST[ip] + ':' + config.PORT, null, { + detectBuffers: false + }) + client.on('ready', done) + }) + } - client.once('ready', function () { - client.removeListener('error', done); - client.get('recon 1', done); - }); - }); + it('redis still loading <= 500', function (done) { + client = redis.createClient.apply(null, args) + var tmp = client.info.bind(client) + var end = helper.callFuncAfter(done, 3) + var delayed = false + var time + // Mock original function and pretent redis is still loading + client.info = function (cb) { + tmp(function (err, res) { + if (!delayed) { + assert(!err) + client.serverInfo.loading = 1 + client.serverInfo.loading_eta_seconds = 0.5 + delayed = true + time = Date.now() + } + end() + cb(err, res) + }) + } + client.on('ready', function () { + var rest = Date.now() - time + assert(rest >= 495, 'Rest should be equal or above 500 ms but is: ' + rest) // setTimeout might trigger early + // Be on the safe side and accept 200ms above the original value + assert(rest - 250 < 500, 'Rest - 250 should be below 500 ms but is: ' + (rest - 250)) + assert(delayed) + end() + }) + }) - it('connects correctly to localhost', function (done) { - client = redis.createClient(null, null); - client.on('error', done); - - client.once('ready', function () { - client.removeListener('error', done); - client.get('recon 1', done); - }); - }); - - it('connects correctly to the provided host with the port set to null', function (done) { - client = redis.createClient(null, 'localhost'); - client.on('error', done); - assert.strictEqual(client.address, 'localhost:6379'); - - client.once('ready', function () { - client.set('foo', 'bar'); - client.get('foo', function (err, res) { - assert.strictEqual(res, 'bar'); - done(err); - }); - }); - }); - - it('connects correctly to localhost and no ready check', function (done) { - client = redis.createClient(undefined, undefined, { - noReadyCheck: true - }); - client.on('error', done); - - client.once('ready', function () { - client.set('foo', 'bar'); - client.get('foo', function (err, res) { - assert.strictEqual(res, 'bar'); - done(err); - }); - }); - }); - - it('connects correctly to the provided host with the port set to undefined', function (done) { - client = redis.createClient(undefined, 'localhost', { - noReadyCheck: true - }); - client.on('error', done); - assert.strictEqual(client.address, 'localhost:6379'); - - client.once('ready', function () { - client.set('foo', 'bar'); - client.get('foo', function (err, res) { - assert.strictEqual(res, 'bar'); - done(err); - }); - }); - }); - - it('connects correctly even if the info command is not present on the redis server', function (done) { - client = redis.createClient.apply(null, args); - client.info = function (cb) { - // Mock the result - cb(new Error("ERR unknown command 'info'")); - }; - client.once('ready', function () { - assert.strictEqual(Object.keys(client.serverInfo).length, 0); - done(); - }); - }); - - it('fake the stream to mock redis', function () { - // This is needed for libraries that want to mock the stream like fakeredis - var temp = redis.RedisClient.prototype.createStream; - var createStreamString = String(temp); - redis.RedisClient.prototype.createStream = function () { - this.connected = true; - this.ready = true; - }; - client = new redis.RedisClient(); - assert.strictEqual(client.stream, undefined); - assert.strictEqual(client.ready, true); - assert.strictEqual(client.connected, true); - client.end = function () {}; - assert(createStreamString !== String(redis.RedisClient.prototype.createStream)); - redis.RedisClient.prototype.createStream = temp; - assert(createStreamString === String(redis.RedisClient.prototype.createStream)); - }); - - if (ip === 'IPv4') { - it('allows connecting with the redis url to the default host and port, select db 3 and warn about duplicate db option', function (done) { - client = redis.createClient('redis:///3?db=3'); - assert.strictEqual(client.selectedDb, '3'); - client.on('ready', done); - }); - - it('allows connecting with the redis url and the default port and auth provided even though it is not required', function (done) { - client = redis.createClient('redis://:porkchopsandwiches@' + config.HOST[ip] + '/'); - var end = helper.callFuncAfter(done, 2); - client.on('warning', function (msg) { - assert.strictEqual(msg, 'Warning: Redis server does not require a password, but a password was supplied.'); - end(); - }); - client.on('ready', end); - }); - - it('allows connecting with the redis url as first parameter and the options as second parameter', function (done) { - client = redis.createClient('//127.0.0.1', { - connectTimeout: 1000 - }); - assert.strictEqual(client.options.connectTimeout, 1000); - client.on('ready', done); - }); - - it('allows connecting with the redis url in the options object and works with protocols other than the redis protocol (e.g. http)', function (done) { - client = redis.createClient({ - url: 'http://foo:porkchopsandwiches@' + config.HOST[ip] + '/3' - }); - assert.strictEqual(client.authPass, 'porkchopsandwiches'); - assert.strictEqual(+client.selectedDb, 3); - assert(!client.options.port); - assert.strictEqual(client.options.host, config.HOST[ip]); - client.on('ready', done); - }); - - it('allows connecting with the redis url and no auth and options as second parameter', function (done) { - var options = { - detectBuffers: false - }; - client = redis.createClient('redis://' + config.HOST[ip] + ':' + config.PORT, options); - assert.strictEqual(Object.keys(options).length, 1); - client.on('ready', done); - }); - - it('allows connecting with the redis url and no auth and options as third parameter', function (done) { - client = redis.createClient('redis://' + config.HOST[ip] + ':' + config.PORT, null, { - detectBuffers: false - }); - client.on('ready', done); - }); - } - - it('redis still loading <= 500', function (done) { - client = redis.createClient.apply(null, args); - var tmp = client.info.bind(client); - var end = helper.callFuncAfter(done, 3); - var delayed = false; - var time; - // Mock original function and pretent redis is still loading - client.info = function (cb) { - tmp(function (err, res) { - if (!delayed) { - assert(!err); - client.serverInfo.loading = 1; - client.serverInfo.loading_eta_seconds = 0.5; - delayed = true; - time = Date.now(); - } - end(); - cb(err, res); - }); - }; - client.on('ready', function () { - var rest = Date.now() - time; - assert(rest >= 495, 'Rest should be equal or above 500 ms but is: ' + rest); // setTimeout might trigger early - // Be on the safe side and accept 200ms above the original value - assert(rest - 250 < 500, 'Rest - 250 should be below 500 ms but is: ' + (rest - 250)); - assert(delayed); - end(); - }); - }); - - it('redis still loading > 1000ms', function (done) { - client = redis.createClient.apply(null, args); - var tmp = client.info.bind(client); - var end = helper.callFuncAfter(done, 3); - var delayed = false; - var time; - // Mock original function and pretent redis is still loading - client.info = function (cb) { - tmp(function (err, res) { - if (!delayed) { - assert(!err); - // Try reconnecting after one second even if redis tells us the time needed is above one second - client.serverInfo.loading = 1; - client.serverInfo.loading_eta_seconds = 2.5; - delayed = true; - time = Date.now(); - } - end(); - cb(err, res); - }); - }; - client.on('ready', function () { - var rest = Date.now() - time; - assert(rest >= 998, '`rest` should be equal or above 1000 ms but is: ' + rest); // setTimeout might trigger early - // Be on the safe side and accept 200ms above the original value - assert(rest - 250 < 1000, '`rest` - 250 should be below 1000 ms but is: ' + (rest - 250)); - assert(delayed); - end(); - }); - }); - - }); - - }); - }); -}); + it('redis still loading > 1000ms', function (done) { + client = redis.createClient.apply(null, args) + var tmp = client.info.bind(client) + var end = helper.callFuncAfter(done, 3) + var delayed = false + var time + // Mock original function and pretent redis is still loading + client.info = function (cb) { + tmp(function (err, res) { + if (!delayed) { + assert(!err) + // Try reconnecting after one second even if redis tells us the time needed is above one second + client.serverInfo.loading = 1 + client.serverInfo.loading_eta_seconds = 2.5 + delayed = true + time = Date.now() + } + end() + cb(err, res) + }) + } + client.on('ready', function () { + var rest = Date.now() - time + assert(rest >= 998, '`rest` should be equal or above 1000 ms but is: ' + rest) // setTimeout might trigger early + // Be on the safe side and accept 200ms above the original value + assert(rest - 250 < 1000, '`rest` - 250 should be below 1000 ms but is: ' + (rest - 250)) + assert(delayed) + end() + }) + }) + }) + }) + }) +}) diff --git a/test/custom_errors.spec.js b/test/custom_errors.spec.js index 90a5aaeb66..3b217b81a0 100644 --- a/test/custom_errors.spec.js +++ b/test/custom_errors.spec.js @@ -1,89 +1,88 @@ -'use strict'; +'use strict' -var assert = require('assert'); -var errors = require('../lib/customErrors'); +var assert = require('assert') +var errors = require('../lib/customErrors') describe('errors', function () { + describe('AbortError', function () { + it('should inherit from Error', function () { + var e = new errors.AbortError({}) + assert.strictEqual(e.message, '') + assert.strictEqual(e.name, 'AbortError') + assert.strictEqual(Object.keys(e).length, 0) + assert(e instanceof Error) + assert(e instanceof errors.AbortError) + }) - describe('AbortError', function () { - it('should inherit from Error', function () { - var e = new errors.AbortError({}); - assert.strictEqual(e.message, ''); - assert.strictEqual(e.name, 'AbortError'); - assert.strictEqual(Object.keys(e).length, 0); - assert(e instanceof Error); - assert(e instanceof errors.AbortError); - }); + it('should list options properties but not name and message', function () { + var e = new errors.AbortError({ + name: 'weird', + message: 'hello world', + property: true + }) + assert.strictEqual(e.message, 'hello world') + assert.strictEqual(e.name, 'weird') + assert.strictEqual(e.property, true) + assert.strictEqual(Object.keys(e).length, 2) + assert(e instanceof Error) + assert(e instanceof errors.AbortError) + assert(delete e.name) + assert.strictEqual(e.name, 'AbortError') + }) - it('should list options properties but not name and message', function () { - var e = new errors.AbortError({ - name: 'weird', - message: 'hello world', - property: true - }); - assert.strictEqual(e.message, 'hello world'); - assert.strictEqual(e.name, 'weird'); - assert.strictEqual(e.property, true); - assert.strictEqual(Object.keys(e).length, 2); - assert(e instanceof Error); - assert(e instanceof errors.AbortError); - assert(delete e.name); - assert.strictEqual(e.name, 'AbortError'); - }); + it('should change name and message', function () { + var e = new errors.AbortError({ + message: 'hello world', + property: true + }) + assert.strictEqual(e.name, 'AbortError') + assert.strictEqual(e.message, 'hello world') + e.name = 'foo' + e.message = 'foobar' + assert.strictEqual(e.name, 'foo') + assert.strictEqual(e.message, 'foobar') + }) + }) - it('should change name and message', function () { - var e = new errors.AbortError({ - message: 'hello world', - property: true - }); - assert.strictEqual(e.name, 'AbortError'); - assert.strictEqual(e.message, 'hello world'); - e.name = 'foo'; - e.message = 'foobar'; - assert.strictEqual(e.name, 'foo'); - assert.strictEqual(e.message, 'foobar'); - }); - }); + describe('AggregateError', function () { + it('should inherit from Error and AbortError', function () { + var e = new errors.AggregateError({}) + assert.strictEqual(e.message, '') + assert.strictEqual(e.name, 'AggregateError') + assert.strictEqual(Object.keys(e).length, 0) + assert(e instanceof Error) + assert(e instanceof errors.AggregateError) + assert(e instanceof errors.AbortError) + }) - describe('AggregateError', function () { - it('should inherit from Error and AbortError', function () { - var e = new errors.AggregateError({}); - assert.strictEqual(e.message, ''); - assert.strictEqual(e.name, 'AggregateError'); - assert.strictEqual(Object.keys(e).length, 0); - assert(e instanceof Error); - assert(e instanceof errors.AggregateError); - assert(e instanceof errors.AbortError); - }); + it('should list options properties but not name and message', function () { + var e = new errors.AggregateError({ + name: 'weird', + message: 'hello world', + property: true + }) + assert.strictEqual(e.message, 'hello world') + assert.strictEqual(e.name, 'weird') + assert.strictEqual(e.property, true) + assert.strictEqual(Object.keys(e).length, 2) + assert(e instanceof Error) + assert(e instanceof errors.AggregateError) + assert(e instanceof errors.AbortError) + assert(delete e.name) + assert.strictEqual(e.name, 'AggregateError') + }) - it('should list options properties but not name and message', function () { - var e = new errors.AggregateError({ - name: 'weird', - message: 'hello world', - property: true - }); - assert.strictEqual(e.message, 'hello world'); - assert.strictEqual(e.name, 'weird'); - assert.strictEqual(e.property, true); - assert.strictEqual(Object.keys(e).length, 2); - assert(e instanceof Error); - assert(e instanceof errors.AggregateError); - assert(e instanceof errors.AbortError); - assert(delete e.name); - assert.strictEqual(e.name, 'AggregateError'); - }); - - it('should change name and message', function () { - var e = new errors.AggregateError({ - message: 'hello world', - property: true - }); - assert.strictEqual(e.name, 'AggregateError'); - assert.strictEqual(e.message, 'hello world'); - e.name = 'foo'; - e.message = 'foobar'; - assert.strictEqual(e.name, 'foo'); - assert.strictEqual(e.message, 'foobar'); - }); - }); -}); + it('should change name and message', function () { + var e = new errors.AggregateError({ + message: 'hello world', + property: true + }) + assert.strictEqual(e.name, 'AggregateError') + assert.strictEqual(e.message, 'hello world') + e.name = 'foo' + e.message = 'foobar' + assert.strictEqual(e.name, 'foo') + assert.strictEqual(e.message, 'foobar') + }) + }) +}) diff --git a/test/detect_buffers.spec.js b/test/detect_buffers.spec.js index c885ab72ed..568e3c71e2 100644 --- a/test/detect_buffers.spec.js +++ b/test/detect_buffers.spec.js @@ -1,268 +1,268 @@ -'use strict'; +'use strict' -var assert = require('assert'); -var config = require('./lib/config'); -var helper = require('./helper'); -var redis = config.redis; +var Buffer = require('safe-buffer').Buffer +var assert = require('assert') +var config = require('./lib/config') +var helper = require('./helper') +var redis = config.redis describe('detectBuffers', function () { + var client + var args = config.configureClient('localhost', { + detectBuffers: true + }) - var client; - var args = config.configureClient('localhost', { - detectBuffers: true - }); + beforeEach(function (done) { + client = redis.createClient.apply(null, args) + client.once('error', done) + client.once('connect', function () { + client.flushdb(function (err) { + client.hmset('hash key 2', 'key 1', 'val 1', 'key 2', 'val 2') + client.set('string key 1', 'string value') + return done(err) + }) + }) + }) - beforeEach(function (done) { - client = redis.createClient.apply(null, args); - client.once('error', done); - client.once('connect', function () { - client.flushdb(function (err) { - client.hmset('hash key 2', 'key 1', 'val 1', 'key 2', 'val 2'); - client.set('string key 1', 'string value'); - return done(err); - }); - }); - }); + afterEach(function () { + client.end(true) + }) - afterEach(function () { - client.end(true); - }); + describe('get', function () { + describe('first argument is a string', function () { + it('returns a string', function (done) { + client.get('string key 1', helper.isString('string value', done)) + }) - describe('get', function () { - describe('first argument is a string', function () { - it('returns a string', function (done) { - client.get('string key 1', helper.isString('string value', done)); - }); + it('returns a string when executed as part of transaction', function (done) { + client.multi().get('string key 1').exec(function (err, res) { + helper.isString('string value', done)(err, res[0]) + }) + }) + }) - it('returns a string when executed as part of transaction', function (done) { - client.multi().get('string key 1').exec(function (err, res) { - helper.isString('string value', done)(err, res[0]); - }); - }); - }); + describe('first argument is a buffer', function () { + it('returns a buffer', function (done) { + client.get(Buffer.from('string key 1'), function (err, reply) { + assert.strictEqual(true, Buffer.isBuffer(reply)) + assert.strictEqual('', reply.inspect()) + return done(err) + }) + }) - describe('first argument is a buffer', function () { - it('returns a buffer', function (done) { - client.get(new Buffer('string key 1'), function (err, reply) { - assert.strictEqual(true, Buffer.isBuffer(reply)); - assert.strictEqual('', reply.inspect()); - return done(err); - }); - }); + it('returns a bufffer when executed as part of transaction', function (done) { + client.multi().get(Buffer.from('string key 1')).exec(function (err, reply) { + assert.strictEqual(1, reply.length) + assert.strictEqual(true, Buffer.isBuffer(reply[0])) + assert.strictEqual('', reply[0].inspect()) + return done(err) + }) + }) + }) + }) - it('returns a bufffer when executed as part of transaction', function (done) { - client.multi().get(new Buffer('string key 1')).exec(function (err, reply) { - assert.strictEqual(1, reply.length); - assert.strictEqual(true, Buffer.isBuffer(reply[0])); - assert.strictEqual('', reply[0].inspect()); - return done(err); - }); - }); - }); - }); + describe('multi.hget', function () { + it('can interleave string and buffer results', function (done) { + client.multi() + .hget('hash key 2', 'key 1') + .hget(Buffer.from('hash key 2'), 'key 1') + .hget('hash key 2', Buffer.from('key 2')) + .hget('hash key 2', 'key 2') + .exec(function (err, reply) { + assert.strictEqual(true, Array.isArray(reply)) + assert.strictEqual(4, reply.length) + assert.strictEqual('val 1', reply[0]) + assert.strictEqual(true, Buffer.isBuffer(reply[1])) + assert.strictEqual('', reply[1].inspect()) + assert.strictEqual(true, Buffer.isBuffer(reply[2])) + assert.strictEqual('', reply[2].inspect()) + assert.strictEqual('val 2', reply[3]) + return done(err) + }) + }) + }) - describe('multi.hget', function () { - it('can interleave string and buffer results', function (done) { - client.multi() - .hget('hash key 2', 'key 1') - .hget(new Buffer('hash key 2'), 'key 1') - .hget('hash key 2', new Buffer('key 2')) - .hget('hash key 2', 'key 2') - .exec(function (err, reply) { - assert.strictEqual(true, Array.isArray(reply)); - assert.strictEqual(4, reply.length); - assert.strictEqual('val 1', reply[0]); - assert.strictEqual(true, Buffer.isBuffer(reply[1])); - assert.strictEqual('', reply[1].inspect()); - assert.strictEqual(true, Buffer.isBuffer(reply[2])); - assert.strictEqual('', reply[2].inspect()); - assert.strictEqual('val 2', reply[3]); - return done(err); - }); - }); - }); + describe('batch.hget', function () { + it('can interleave string and buffer results', function (done) { + client.batch() + .hget('hash key 2', 'key 1') + .hget(Buffer.from('hash key 2'), 'key 1') + .hget('hash key 2', Buffer.from('key 2')) + .hget('hash key 2', 'key 2') + .exec(function (err, reply) { + assert.strictEqual(true, Array.isArray(reply)) + assert.strictEqual(4, reply.length) + assert.strictEqual('val 1', reply[0]) + assert.strictEqual(true, Buffer.isBuffer(reply[1])) + assert.strictEqual('', reply[1].inspect()) + assert.strictEqual(true, Buffer.isBuffer(reply[2])) + assert.strictEqual('', reply[2].inspect()) + assert.strictEqual('val 2', reply[3]) + return done(err) + }) + }) + }) - describe('batch.hget', function () { - it('can interleave string and buffer results', function (done) { - client.batch() - .hget('hash key 2', 'key 1') - .hget(new Buffer('hash key 2'), 'key 1') - .hget('hash key 2', new Buffer('key 2')) - .hget('hash key 2', 'key 2') - .exec(function (err, reply) { - assert.strictEqual(true, Array.isArray(reply)); - assert.strictEqual(4, reply.length); - assert.strictEqual('val 1', reply[0]); - assert.strictEqual(true, Buffer.isBuffer(reply[1])); - assert.strictEqual('', reply[1].inspect()); - assert.strictEqual(true, Buffer.isBuffer(reply[2])); - assert.strictEqual('', reply[2].inspect()); - assert.strictEqual('val 2', reply[3]); - return done(err); - }); - }); - }); + describe('hmget', function () { + describe('first argument is a string', function () { + it('returns strings for keys requested', function (done) { + client.hmget('hash key 2', 'key 1', 'key 2', function (err, reply) { + assert.strictEqual(true, Array.isArray(reply)) + assert.strictEqual(2, reply.length) + assert.strictEqual('val 1', reply[0]) + assert.strictEqual('val 2', reply[1]) + return done(err) + }) + }) - describe('hmget', function () { - describe('first argument is a string', function () { - it('returns strings for keys requested', function (done) { - client.hmget('hash key 2', 'key 1', 'key 2', function (err, reply) { - assert.strictEqual(true, Array.isArray(reply)); - assert.strictEqual(2, reply.length); - assert.strictEqual('val 1', reply[0]); - assert.strictEqual('val 2', reply[1]); - return done(err); - }); - }); + it('returns strings for keys requested in transaction', function (done) { + client.multi().hmget('hash key 2', 'key 1', 'key 2').exec(function (err, reply) { + assert.strictEqual(true, Array.isArray(reply)) + assert.strictEqual(1, reply.length) + assert.strictEqual(2, reply[0].length) + assert.strictEqual('val 1', reply[0][0]) + assert.strictEqual('val 2', reply[0][1]) + return done(err) + }) + }) - it('returns strings for keys requested in transaction', function (done) { - client.multi().hmget('hash key 2', 'key 1', 'key 2').exec(function (err, reply) { - assert.strictEqual(true, Array.isArray(reply)); - assert.strictEqual(1, reply.length); - assert.strictEqual(2, reply[0].length); - assert.strictEqual('val 1', reply[0][0]); - assert.strictEqual('val 2', reply[0][1]); - return done(err); - }); - }); + it('handles array of strings with undefined values (repro #344)', function (done) { + client.hmget('hash key 2', 'key 3', 'key 4', function (err, reply) { + assert.strictEqual(true, Array.isArray(reply)) + assert.strictEqual(2, reply.length) + assert.strictEqual(null, reply[0]) + assert.strictEqual(null, reply[1]) + return done(err) + }) + }) - it('handles array of strings with undefined values (repro #344)', function (done) { - client.hmget('hash key 2', 'key 3', 'key 4', function (err, reply) { - assert.strictEqual(true, Array.isArray(reply)); - assert.strictEqual(2, reply.length); - assert.equal(null, reply[0]); - assert.equal(null, reply[1]); - return done(err); - }); - }); + it('handles array of strings with undefined values in transaction (repro #344)', function (done) { + client.multi().hmget('hash key 2', 'key 3', 'key 4').exec(function (err, reply) { + assert.strictEqual(true, Array.isArray(reply)) + assert.strictEqual(1, reply.length) + assert.strictEqual(2, reply[0].length) + assert.strictEqual(null, reply[0][0]) + assert.strictEqual(null, reply[0][1]) + return done(err) + }) + }) + }) - it('handles array of strings with undefined values in transaction (repro #344)', function (done) { - client.multi().hmget('hash key 2', 'key 3', 'key 4').exec(function (err, reply) { - assert.strictEqual(true, Array.isArray(reply)); - assert.strictEqual(1, reply.length); - assert.strictEqual(2, reply[0].length); - assert.equal(null, reply[0][0]); - assert.equal(null, reply[0][1]); - return done(err); - }); - }); - }); + describe('first argument is a buffer', function () { + it('returns buffers for keys requested', function (done) { + client.hmget(Buffer.from('hash key 2'), 'key 1', 'key 2', function (err, reply) { + assert.strictEqual(true, Array.isArray(reply)) + assert.strictEqual(2, reply.length) + assert.strictEqual(true, Buffer.isBuffer(reply[0])) + assert.strictEqual(true, Buffer.isBuffer(reply[1])) + assert.strictEqual('', reply[0].inspect()) + assert.strictEqual('', reply[1].inspect()) + return done(err) + }) + }) - describe('first argument is a buffer', function () { - it('returns buffers for keys requested', function (done) { - client.hmget(new Buffer('hash key 2'), 'key 1', 'key 2', function (err, reply) { - assert.strictEqual(true, Array.isArray(reply)); - assert.strictEqual(2, reply.length); - assert.strictEqual(true, Buffer.isBuffer(reply[0])); - assert.strictEqual(true, Buffer.isBuffer(reply[1])); - assert.strictEqual('', reply[0].inspect()); - assert.strictEqual('', reply[1].inspect()); - return done(err); - }); - }); + it('returns buffers for keys requested in transaction', function (done) { + client.multi().hmget(Buffer.from('hash key 2'), 'key 1', 'key 2').exec(function (err, reply) { + assert.strictEqual(true, Array.isArray(reply)) + assert.strictEqual(1, reply.length) + assert.strictEqual(2, reply[0].length) + assert.strictEqual(true, Buffer.isBuffer(reply[0][0])) + assert.strictEqual(true, Buffer.isBuffer(reply[0][1])) + assert.strictEqual('', reply[0][0].inspect()) + assert.strictEqual('', reply[0][1].inspect()) + return done(err) + }) + }) - it('returns buffers for keys requested in transaction', function (done) { - client.multi().hmget(new Buffer('hash key 2'), 'key 1', 'key 2').exec(function (err, reply) { - assert.strictEqual(true, Array.isArray(reply)); - assert.strictEqual(1, reply.length); - assert.strictEqual(2, reply[0].length); - assert.strictEqual(true, Buffer.isBuffer(reply[0][0])); - assert.strictEqual(true, Buffer.isBuffer(reply[0][1])); - assert.strictEqual('', reply[0][0].inspect()); - assert.strictEqual('', reply[0][1].inspect()); - return done(err); - }); - }); + it('returns buffers for keys requested in .batch', function (done) { + client.batch().hmget(Buffer.from('hash key 2'), 'key 1', 'key 2').exec(function (err, reply) { + assert.strictEqual(true, Array.isArray(reply)) + assert.strictEqual(1, reply.length) + assert.strictEqual(2, reply[0].length) + assert.strictEqual(true, Buffer.isBuffer(reply[0][0])) + assert.strictEqual(true, Buffer.isBuffer(reply[0][1])) + assert.strictEqual('', reply[0][0].inspect()) + assert.strictEqual('', reply[0][1].inspect()) + return done(err) + }) + }) + }) + }) - it('returns buffers for keys requested in .batch', function (done) { - client.batch().hmget(new Buffer('hash key 2'), 'key 1', 'key 2').exec(function (err, reply) { - assert.strictEqual(true, Array.isArray(reply)); - assert.strictEqual(1, reply.length); - assert.strictEqual(2, reply[0].length); - assert.strictEqual(true, Buffer.isBuffer(reply[0][0])); - assert.strictEqual(true, Buffer.isBuffer(reply[0][1])); - assert.strictEqual('', reply[0][0].inspect()); - assert.strictEqual('', reply[0][1].inspect()); - return done(err); - }); - }); - }); - }); + describe('hgetall', function (done) { + describe('first argument is a string', function () { + it('returns string values', function (done) { + client.hgetall('hash key 2', function (err, reply) { + assert.strictEqual('object', typeof reply) + assert.strictEqual(2, Object.keys(reply).length) + assert.strictEqual('val 1', reply['key 1']) + assert.strictEqual('val 2', reply['key 2']) + return done(err) + }) + }) - describe('hgetall', function (done) { - describe('first argument is a string', function () { - it('returns string values', function (done) { - client.hgetall('hash key 2', function (err, reply) { - assert.strictEqual('object', typeof reply); - assert.strictEqual(2, Object.keys(reply).length); - assert.strictEqual('val 1', reply['key 1']); - assert.strictEqual('val 2', reply['key 2']); - return done(err); - }); - }); + it('returns string values when executed in transaction', function (done) { + client.multi().hgetall('hash key 2').exec(function (err, reply) { + assert.strictEqual(1, reply.length) + assert.strictEqual('object', typeof reply[0]) + assert.strictEqual(2, Object.keys(reply[0]).length) + assert.strictEqual('val 1', reply[0]['key 1']) + assert.strictEqual('val 2', reply[0]['key 2']) + return done(err) + }) + }) - it('returns string values when executed in transaction', function (done) { - client.multi().hgetall('hash key 2').exec(function (err, reply) { - assert.strictEqual(1, reply.length); - assert.strictEqual('object', typeof reply[0]); - assert.strictEqual(2, Object.keys(reply[0]).length); - assert.strictEqual('val 1', reply[0]['key 1']); - assert.strictEqual('val 2', reply[0]['key 2']); - return done(err); - }); - }); + it('returns string values when executed in .batch', function (done) { + client.batch().hgetall('hash key 2').exec(function (err, reply) { + assert.strictEqual(1, reply.length) + assert.strictEqual('object', typeof reply[0]) + assert.strictEqual(2, Object.keys(reply[0]).length) + assert.strictEqual('val 1', reply[0]['key 1']) + assert.strictEqual('val 2', reply[0]['key 2']) + return done(err) + }) + }) + }) - it('returns string values when executed in .batch', function (done) { - client.batch().hgetall('hash key 2').exec(function (err, reply) { - assert.strictEqual(1, reply.length); - assert.strictEqual('object', typeof reply[0]); - assert.strictEqual(2, Object.keys(reply[0]).length); - assert.strictEqual('val 1', reply[0]['key 1']); - assert.strictEqual('val 2', reply[0]['key 2']); - return done(err); - }); - }); - }); + describe('first argument is a buffer', function () { + it('returns buffer values', function (done) { + client.hgetall(Buffer.from('hash key 2'), function (err, reply) { + assert.strictEqual(null, err) + assert.strictEqual('object', typeof reply) + assert.strictEqual(2, Object.keys(reply).length) + assert.strictEqual(true, Buffer.isBuffer(reply['key 1'])) + assert.strictEqual(true, Buffer.isBuffer(reply['key 2'])) + assert.strictEqual('', reply['key 1'].inspect()) + assert.strictEqual('', reply['key 2'].inspect()) + return done(err) + }) + }) - describe('first argument is a buffer', function () { - it('returns buffer values', function (done) { - client.hgetall(new Buffer('hash key 2'), function (err, reply) { - assert.strictEqual(null, err); - assert.strictEqual('object', typeof reply); - assert.strictEqual(2, Object.keys(reply).length); - assert.strictEqual(true, Buffer.isBuffer(reply['key 1'])); - assert.strictEqual(true, Buffer.isBuffer(reply['key 2'])); - assert.strictEqual('', reply['key 1'].inspect()); - assert.strictEqual('', reply['key 2'].inspect()); - return done(err); - }); - }); + it('returns buffer values when executed in transaction', function (done) { + client.multi().hgetall(Buffer.from('hash key 2')).exec(function (err, reply) { + assert.strictEqual(1, reply.length) + assert.strictEqual('object', typeof reply[0]) + assert.strictEqual(2, Object.keys(reply[0]).length) + assert.strictEqual(true, Buffer.isBuffer(reply[0]['key 1'])) + assert.strictEqual(true, Buffer.isBuffer(reply[0]['key 2'])) + assert.strictEqual('', reply[0]['key 1'].inspect()) + assert.strictEqual('', reply[0]['key 2'].inspect()) + return done(err) + }) + }) - it('returns buffer values when executed in transaction', function (done) { - client.multi().hgetall(new Buffer('hash key 2')).exec(function (err, reply) { - assert.strictEqual(1, reply.length); - assert.strictEqual('object', typeof reply[0]); - assert.strictEqual(2, Object.keys(reply[0]).length); - assert.strictEqual(true, Buffer.isBuffer(reply[0]['key 1'])); - assert.strictEqual(true, Buffer.isBuffer(reply[0]['key 2'])); - assert.strictEqual('', reply[0]['key 1'].inspect()); - assert.strictEqual('', reply[0]['key 2'].inspect()); - return done(err); - }); - }); - - it('returns buffer values when executed in .batch', function (done) { - client.batch().hgetall(new Buffer('hash key 2')).exec(function (err, reply) { - assert.strictEqual(1, reply.length); - assert.strictEqual('object', typeof reply[0]); - assert.strictEqual(2, Object.keys(reply[0]).length); - assert.strictEqual(true, Buffer.isBuffer(reply[0]['key 1'])); - assert.strictEqual(true, Buffer.isBuffer(reply[0]['key 2'])); - assert.strictEqual('', reply[0]['key 1'].inspect()); - assert.strictEqual('', reply[0]['key 2'].inspect()); - return done(err); - }); - }); - }); - }); -}); + it('returns buffer values when executed in .batch', function (done) { + client.batch().hgetall(Buffer.from('hash key 2')).exec(function (err, reply) { + assert.strictEqual(1, reply.length) + assert.strictEqual('object', typeof reply[0]) + assert.strictEqual(2, Object.keys(reply[0]).length) + assert.strictEqual(true, Buffer.isBuffer(reply[0]['key 1'])) + assert.strictEqual(true, Buffer.isBuffer(reply[0]['key 2'])) + assert.strictEqual('', reply[0]['key 1'].inspect()) + assert.strictEqual('', reply[0]['key 2'].inspect()) + return done(err) + }) + }) + }) + }) +}) diff --git a/test/good_traces.spec.js b/test/good_traces.spec.js index b23b5344d9..f701e2de68 100644 --- a/test/good_traces.spec.js +++ b/test/good_traces.spec.js @@ -1,59 +1,58 @@ -'use strict'; +'use strict' -var assert = require('assert'); -var config = require('./lib/config'); -var fork = require('child_process').fork; -var redis = config.redis; +var assert = require('assert') +var config = require('./lib/config') +var fork = require('child_process').fork +var redis = config.redis describe('stack traces', function () { + it('should return good traces with NODE_ENV=development set', function (done) { + var external = fork('./test/lib/good-traces.js', { + env: { + NODE_ENV: 'development' + } + }) - it('should return good traces with NODE_ENV=development set', function (done) { - var external = fork('./test/lib/good-traces.js', { - env: { - NODE_ENV: 'development' - } - }); + var id = setTimeout(function () { + external.kill() + done(new Error('Timeout')) + }, 6000) - var id = setTimeout(function () { - external.kill(); - done(new Error('Timeout')); - }, 6000); + external.on('close', function (code) { + clearTimeout(id) + assert.strictEqual(code, 0) + done() + }) + }) - external.on('close', function (code) { - clearTimeout(id); - assert.strictEqual(code, 0); - done(); - }); - }); + it('should return good traces with NODE_DEBUG=redis env set', function (done) { + var external = fork('./test/lib/good-traces.js', { + env: { + NODE_DEBUG: 'redis' + }, + silent: true + }) - it('should return good traces with NODE_DEBUG=redis env set', function (done) { - var external = fork('./test/lib/good-traces.js', { - env: { - NODE_DEBUG: 'redis' - }, - silent: true - }); + var id = setTimeout(function () { + external.kill() + done(new Error('Timeout')) + }, 6000) - var id = setTimeout(function () { - external.kill(); - done(new Error('Timeout')); - }, 6000); + external.on('close', function (code) { + clearTimeout(id) + assert.strictEqual(code, 0) + done() + }) + }) - external.on('close', function (code) { - clearTimeout(id); - assert.strictEqual(code, 0); - done(); - }); - }); - - // This is always going to return good stack traces - it('should always return good stack traces for rejected offline commands', function (done) { - var client = redis.createClient({ - enableOfflineQueue: false - }); - client.set('foo', function (err, res) { - assert(/good_traces.spec.js/.test(err.stack)); - client.quit(done); - }); - }); -}); + // This is always going to return good stack traces + it('should always return good stack traces for rejected offline commands', function (done) { + var client = redis.createClient({ + enableOfflineQueue: false + }) + client.set('foo', function (err, res) { + assert(/good_traces.spec.js/.test(err.stack)) + client.quit(done) + }) + }) +}) diff --git a/test/helper.js b/test/helper.js index 492b59a2ee..ef75ba56fd 100644 --- a/test/helper.js +++ b/test/helper.js @@ -1,245 +1,237 @@ -'use strict'; +'use strict' -var assert = require('assert'); -var path = require('path'); -var config = require('./lib/config'); -var RedisProcess = require('./lib/redis-process'); -var StunnelProcess = require('./lib/stunnel-process'); -var rp; -var stunnelProcess; +var assert = require('assert') +var path = require('path') +var config = require('./lib/config') +var RedisProcess = require('./lib/redis-process') +var StunnelProcess = require('./lib/stunnel-process') +var rp +var stunnelProcess function startRedis (conf, done, port) { - RedisProcess.start(function (err, _rp) { - rp = _rp; - return done(err); - }, path.resolve(__dirname, conf), port); + RedisProcess.start(function (err, _rp) { + rp = _rp + return done(err) + }, path.resolve(__dirname, conf), port) } // don't start redis every time we // include this helper file! if (!process.env.REDIS_TESTS_STARTED) { - process.env.REDIS_TESTS_STARTED = true; + process.env.REDIS_TESTS_STARTED = true - before(function (done) { - startRedis('./conf/redis.conf', done); - }); + before(function (done) { + startRedis('./conf/redis.conf', done) + }) - after(function (done) { - if (rp) rp.stop(done); - }); + after(function (done) { + if (rp) rp.stop(done) + }) } function arrayHelper (results) { - if (results instanceof Array) { - assert.strictEqual(results.length, 1, 'The array length may only be one element'); - return results[0]; - } - return results; + if (results instanceof Array) { + assert.strictEqual(results.length, 1, 'The array length may only be one element') + return results[0] + } + return results +} + +function toString (res) { + // If options are passed to return either strings or buffers... + if (Buffer.isBuffer(res)) { + return res.toString() + } + if (Array.isArray(res)) { + return res.map(toString) + } + return res } module.exports = { - redisProcess: function () { - return rp; - }, - stopRedis: function (done) { - rp.stop(done); - }, - startRedis: startRedis, - stopStunnel: function (done) { - if (stunnelProcess) { - StunnelProcess.stop(stunnelProcess, done); - } else { - done(); - } - }, - startStunnel: function (done) { - StunnelProcess.start(function (err, _stunnelProcess) { - stunnelProcess = _stunnelProcess; - return done(err); - }, path.resolve(__dirname, './conf')); - }, - isNumber: function (expected, done) { - return function (err, results) { - assert.strictEqual(err, null, 'expected ' + expected + ', got error: ' + err); - results = arrayHelper(results); - assert.strictEqual(results, expected, expected + ' !== ' + results); - assert.strictEqual(typeof results, 'number', 'expected a number, got ' + typeof results); - if (done) done(); - }; - }, - isString: function (str, done) { - str = '' + str; // Make sure it's a string - return function (err, results) { - assert.strictEqual(err, null, "expected string '" + str + "', got error: " + err); - results = arrayHelper(results); - if (Buffer.isBuffer(results)) { // If options are passed to return either strings or buffers... - results = results.toString(); - } - assert.strictEqual(results, str, str + ' does not match ' + results); - if (done) done(); - }; - }, - isNull: function (done) { - return function (err, results) { - assert.strictEqual(err, null, 'expected null, got error: ' + err); - results = arrayHelper(results); - assert.strictEqual(results, null, results + ' is not null'); - if (done) done(); - }; - }, - isUndefined: function (done) { - return function (err, results) { - assert.strictEqual(err, null, 'expected null, got error: ' + err); - results = arrayHelper(results); - assert.strictEqual(results, undefined, results + ' is not undefined'); - if (done) done(); - }; - }, - isError: function (done) { - return function (err, results) { - assert(err instanceof Error, "err is not instance of 'Error', but an error is expected here."); - if (done) done(); - }; - }, - isNotError: function (done) { - return function (err, results) { - assert.strictEqual(err, null, 'expected success, got an error: ' + err); - if (done) done(); - }; - }, - isType: { - number: function (done) { - return function (err, results) { - assert.strictEqual(err, null, 'expected any number, got error: ' + err); - assert.strictEqual(typeof results, 'number', results + ' is not a number'); - if (done) done(); - }; - }, - string: function (done) { - return function (err, results) { - assert.strictEqual(err, null, 'expected any string, got error: ' + err); - assert.strictEqual(typeof results, 'string', results + ' is not a string'); - if (done) done(); - }; - }, - positiveNumber: function (done) { - return function (err, results) { - assert.strictEqual(err, null, 'expected positive number, got error: ' + err); - assert(results > 0, results + ' is not a positive number'); - if (done) done(); - }; - } - }, - isUnSubscribe: function (count, channels, done) { - if (typeof count !== 'number') { - done = channels; - channels = count; - count = undefined; - } - if (typeof channels === 'function') { - done = count; - count = undefined; - } - if (typeof channels === 'string') { - channels = [channels]; - } - var len = channels.length; - return function (err, results) { - assert.strictEqual(err, null, 'expected an array, got: ' + err); - assert.strictEqual(Array.isArray(results), true, results); - assert.strictEqual(Array.isArray(results[1]), true, results); - assert.strictEqual(results[1].length, len, results); - assert.strictEqual(typeof results[0], 'number', results); - if (count) assert.strictEqual(count, results[0], results); - if (done) done(); - }; - - }, - match: function (pattern, done) { - return function (err, results) { - assert.strictEqual(err, null, 'expected ' + pattern.toString() + ', got error: ' + err); - results = arrayHelper(results); - assert(pattern.test(results), "expected string '" + results + "' to match " + pattern.toString()); - if (done) done(); - }; - }, - serverVersionAtLeast: function (connection, desiredVersion) { - // Wait until a connection has established (otherwise a timeout is going to be triggered at some point) - if (Object.keys(connection.serverInfo).length === 0) { - 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 - var version = connection.serverInfo.versions; - for (var i = 0; i < 3; i++) { - if (version[i] > desiredVersion[i]) { - return true; - } - if (version[i] < desiredVersion[i]) { - if (this.skip) this.skip(); - return false; - } - } - return true; - }, - allTests: function (opts, cb) { - if (!cb) { - cb = opts; - opts = {}; - } - var protocols = ['IPv4']; - if (process.platform !== 'win32') { - protocols.push('IPv6', '/tmp/redis.sock'); - } - var options = [{ - detectBuffers: true - }, { - detectBuffers: false - }]; - options.forEach(function (options) { - var strOptions = ''; - var key; - for (key in options) { - if (options.hasOwnProperty(key)) { - strOptions += key + ': ' + options[key] + '; '; - } - } - describe('using options: ' + strOptions, function () { - protocols.forEach(function (ip, i) { - if (i !== 0 && !opts.allConnections) { - return; - } - cb(ip, config.configureClient(ip, options)); - }); - }); - }); - }, - removeMochaListener: function () { - var mochaListener = process.listeners('uncaughtException').pop(); - process.removeListener('uncaughtException', mochaListener); - return mochaListener; - }, - callFuncAfter: function (func, max) { - var i = 0; - return function (err) { - if (err) { - throw err; - } - i++; - if (i >= max) { - func(); - return true; - } - return false; - }; - }, - killConnection: function (client) { - // Change the connection option to a non existing one and destroy the stream - client.connectionOptions = { - port: 65535, - host: '127.0.0.1', - family: 4 - }; - client.address = '127.0.0.1:65535'; - client.stream.destroy(); + redisProcess: function () { + return rp + }, + stopRedis: function (done) { + rp.stop(done) + }, + startRedis: startRedis, + stopStunnel: function (done) { + if (stunnelProcess) { + StunnelProcess.stop(stunnelProcess, done) + } else { + done() } -}; + }, + startStunnel: function (done) { + StunnelProcess.start(function (err, _stunnelProcess) { + stunnelProcess = _stunnelProcess + return done(err) + }, path.resolve(__dirname, './conf')) + }, + isNumber: function (expected, done) { + return function (err, results) { + assert.strictEqual(err, null, 'expected ' + expected + ', got error: ' + err) + results = arrayHelper(results) + assert.strictEqual(results, expected, expected + ' !== ' + results) + assert.strictEqual(typeof results, 'number', 'expected a number, got ' + typeof results) + if (done) done() + } + }, + isString: function (str, done) { + str = '' + str // Make sure it's a string + return function (err, results) { + assert.strictEqual(err, null, 'expected string \'' + str + '\', got error: ' + err) + results = arrayHelper(results) + results = toString(results) + assert.strictEqual(results, str, str + ' does not match ' + results) + if (done) done() + } + }, + isNull: function (done) { + return function (err, results) { + assert.strictEqual(err, null, 'expected null, got error: ' + err) + results = arrayHelper(results) + assert.strictEqual(results, null, results + ' is not null') + if (done) done() + } + }, + isUndefined: function (done) { + return function (err, results) { + assert.strictEqual(err, null, 'expected null, got error: ' + err) + results = arrayHelper(results) + assert.strictEqual(results, undefined, results + ' is not undefined') + if (done) done() + } + }, + isError: function (done) { + return function (err, results) { + assert(err instanceof Error, 'err is not instance of \'Error\', but an error is expected here.') + if (done) done() + } + }, + isNotError: function (done) { + return function (err, results) { + assert.strictEqual(err, null, 'expected success, got an error: ' + err) + if (done) done() + } + }, + isDeepEqual: function (args, done) { + return function (err, res) { + assert.strictEqual(err, null, 'expected null, got error: ' + err) + res = toString(res) + assert.deepStrictEqual(res, args) + if (done) done() + } + }, + isType: { + number: function (done) { + return function (err, results) { + assert.strictEqual(err, null, 'expected any number, got error: ' + err) + assert.strictEqual(typeof results, 'number', results + ' is not a number') + if (done) done() + } + }, + string: function (done) { + return function (err, results) { + assert.strictEqual(err, null, 'expected any string, got error: ' + err) + assert.strictEqual(typeof results, 'string', results + ' is not a string') + if (done) done() + } + }, + positiveNumber: function (done) { + return function (err, results) { + assert.strictEqual(err, null, 'expected positive number, got error: ' + err) + assert(results > 0, results + ' is not a positive number') + if (done) done() + } + } + }, + match: function (pattern, done) { + return function (err, results) { + assert.strictEqual(err, null, 'expected ' + pattern.toString() + ', got error: ' + err) + results = arrayHelper(results) + assert(pattern.test(results), 'expected string \'' + results + '\' to match ' + pattern.toString()) + if (done) done() + } + }, + serverVersionAtLeast: function (connection, desiredVersion) { + // Wait until a connection has established (otherwise a timeout is going to be triggered at some point) + if (Object.keys(connection.serverInfo).length === 0) { + 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 + var version = connection.serverInfo.versions + for (var i = 0; i < 3; i++) { + if (version[i] > desiredVersion[i]) { + return true + } + if (version[i] < desiredVersion[i]) { + if (this.skip) this.skip() + return false + } + } + return true + }, + allTests: function (opts, cb) { + if (!cb) { + cb = opts + opts = {} + } + var protocols = ['IPv4'] + if (process.platform !== 'win32') { + protocols.push('IPv6', '/tmp/redis.sock') + } + var options = [{ + detectBuffers: true + }, { + detectBuffers: false + }] + options.forEach(function (options) { + var strOptions = '' + var key + for (key in options) { + if (options.hasOwnProperty(key)) { + strOptions += key + ': ' + options[key] + '; ' + } + } + describe('using options: ' + strOptions, function () { + protocols.forEach(function (ip, i) { + if (i !== 0 && !opts.allConnections) { + return + } + cb(ip, config.configureClient(ip, options)) + }) + }) + }) + }, + removeMochaListener: function () { + var mochaListener = process.listeners('uncaughtException').pop() + process.removeListener('uncaughtException', mochaListener) + return mochaListener + }, + callFuncAfter: function (func, max) { + var i = 0 + return function (err) { + if (err) { + throw err + } + i++ + if (i >= max) { + func() + return true + } + return false + } + }, + killConnection: function (client) { + // Change the connection option to a non existing one and destroy the stream + client.connectionOptions = { + port: 65535, + host: '127.0.0.1', + family: 4 + } + client.address = '127.0.0.1:65535' + client.stream.destroy() + } +} diff --git a/test/lib/config.js b/test/lib/config.js index 2f2643884c..11d3f0f36f 100644 --- a/test/lib/config.js +++ b/test/lib/config.js @@ -1,38 +1,38 @@ -'use strict'; +'use strict' // helpers for configuring a redis client in // its various modes, ipV6, ipV4, socket. -var redis = require('../../index'); -var bluebird = require('bluebird'); +var redis = require('../../index') +var bluebird = require('bluebird') // Promisify everything -bluebird.promisifyAll(redis.RedisClient.prototype); -bluebird.promisifyAll(redis.Multi.prototype); +bluebird.promisifyAll(redis.RedisClient.prototype) +bluebird.promisifyAll(redis.Multi.prototype) var config = { - redis: redis, - PORT: 6379, - HOST: { - IPv4: '127.0.0.1', - IPv6: '::1' - }, - configureClient: function (ip, opts) { - var args = []; - // Do not manipulate the opts => copy them each time - opts = opts ? JSON.parse(JSON.stringify(opts)) : {}; + redis: redis, + PORT: 6379, + HOST: { + IPv4: '127.0.0.1', + IPv6: '::1' + }, + configureClient: function (ip, opts) { + var args = [] + // Do not manipulate the opts => copy them each time + opts = opts ? JSON.parse(JSON.stringify(opts)) : {} - if (ip.match(/\.sock/)) { - args.push(ip); - } else { - args.push(config.PORT); - args.push(config.HOST[ip]); - opts.family = ip; - } - - args.push(opts); - - return args; + if (ip.match(/\.sock/)) { + args.push(ip) + } else { + args.push(config.PORT) + args.push(config.HOST[ip]) + opts.family = ip } -}; -module.exports = config; + args.push(opts) + + return args + } +} + +module.exports = config diff --git a/test/lib/good-traces.js b/test/lib/good-traces.js index 37838ff7d2..0e2fa01ae8 100644 --- a/test/lib/good-traces.js +++ b/test/lib/good-traces.js @@ -1,20 +1,20 @@ // Spawned by the goodStacks.spec.js tests -'use strict'; +'use strict' -var assert = require('assert'); -var redis = require('../../index'); -var client = redis.createClient(); +var assert = require('assert') +var redis = require('../../index') +var client = redis.createClient() // Both error cases would normally return bad stack traces client.set('foo', function (err, res) { - assert(/good-traces.js:9:8/.test(err.stack)); - client.set('foo', 'bar', function (err, res) { - assert(/good-traces.js:11:12/.test(err.stack)); - client.quit(function () { - process.exit(0); - }); - }); - process.nextTick(function () { - client.stream.destroy(); - }); -}); + assert(/good-traces.js:9:8/.test(err.stack)) + client.set('foo', 'bar', function (err, res) { + assert(/good-traces.js:11:10/.test(err.stack)) + client.quit(function () { + process.exit(0) + }) + }) + process.nextTick(function () { + client.stream.destroy() + }) +}) diff --git a/test/lib/redis-process.js b/test/lib/redis-process.js index e98f7fbffc..d9aee2c1c2 100644 --- a/test/lib/redis-process.js +++ b/test/lib/redis-process.js @@ -1,88 +1,88 @@ -'use strict'; +'use strict' // helper to start and stop the redis process. -var config = require('./config'); -var fs = require('fs'); -var path = require('path'); -var spawn = require('win-spawn'); -var tcpPortUsed = require('tcp-port-used'); -var bluebird = require('bluebird'); +var config = require('./config') +var fs = require('fs') +var path = require('path') +var spawn = require('win-spawn') +var tcpPortUsed = require('tcp-port-used') +var bluebird = require('bluebird') // wait for redis to be listening in // all three modes (ipv4, ipv6, socket). function waitForRedis (available, cb, port) { - if (process.platform === 'win32') return cb(); + if (process.platform === 'win32') return cb() - var time = Date.now(); - var running = false; - var socket = '/tmp/redis.sock'; - if (port) { - // We have to distinguishe the redis sockets if we have more than a single redis instance running - socket = '/tmp/redis' + port + '.sock'; - } - port = port || config.PORT; - var id = setInterval(function () { - if (running) return; - running = true; - bluebird.join( - tcpPortUsed.check(port, '127.0.0.1'), - tcpPortUsed.check(port, '::1'), - function (ipV4, ipV6) { - if (ipV6 === available && ipV4 === available) { - if (fs.existsSync(socket) === available) { - clearInterval(id); - return cb(); - } - // The same message applies for can't stop but we ignore that case - throw new Error('Port ' + port + ' is already in use. Tests can\'t start.\n'); - } - if (Date.now() - time > 6000) { - throw new Error('Redis could not start on port ' + (port || config.PORT) + '\n'); - } - running = false; - }).catch(function (err) { - console.error('\x1b[31m' + err.stack + '\x1b[0m\n'); - process.exit(1); - }); - }, 100); + var time = Date.now() + var running = false + var socket = '/tmp/redis.sock' + if (port) { + // We have to distinguish the redis sockets if we have more than a single redis instance running + socket = '/tmp/redis' + port + '.sock' + } + port = port || config.PORT + var id = setInterval(function () { + if (running) return + running = true + bluebird.join( + tcpPortUsed.check(port, '127.0.0.1'), + tcpPortUsed.check(port, '::1'), + function (ipV4, ipV6) { + if (ipV6 === available && ipV4 === available) { + if (fs.existsSync(socket) === available) { + clearInterval(id) + return cb() + } + // The same message applies for can't stop but we ignore that case + throw new Error('Port ' + port + ' is already in use. Tests can\'t start.\n') + } + if (Date.now() - time > 6000) { + throw new Error('Redis could not start on port ' + (port || config.PORT) + '\n') + } + running = false + }).catch(function (err) { + console.error('\x1b[31m' + err.stack + '\x1b[0m\n') + process.exit(1) + }) + }, 100) } module.exports = { - start: function (done, conf, port) { - var spawnFailed = false; - // spawn redis with our testing configuration. - var confFile = conf || path.resolve(__dirname, '../conf/redis.conf'); - var rp = spawn('redis-server', [confFile], {}); + start: function (done, conf, port) { + var spawnFailed = false + // spawn redis with our testing configuration. + var confFile = conf || path.resolve(__dirname, '../conf/redis.conf') + var rp = spawn('redis-server', [confFile], {}) - // capture a failure booting redis, and give - // the user running the test some directions. - rp.once('exit', function (code) { - if (code !== 0) spawnFailed = true; - }); + // capture a failure booting redis, and give + // the user running the test some directions. + rp.once('exit', function (code) { + if (code !== 0) spawnFailed = true + }) - // wait for redis to become available, by - // checking the port we bind on. - waitForRedis(true, function () { - // return an object that can be used in - // an after() block to shutdown redis. - return done(null, { - spawnFailed: function () { - return spawnFailed; - }, - stop: function (done) { - if (spawnFailed) return done(); - rp.once('exit', function (code) { - var error = null; - if (code !== null && code !== 0) { - error = new Error('Redis shutdown failed with code ' + code); - } - waitForRedis(false, function () { - return done(error); - }, port); - }); - rp.kill('SIGTERM'); - } - }); - }, port); - } -}; + // wait for redis to become available, by + // checking the port we bind on. + waitForRedis(true, function () { + // return an object that can be used in + // an after() block to shutdown redis. + return done(null, { + spawnFailed: function () { + return spawnFailed + }, + stop: function (done) { + if (spawnFailed) return done() + rp.once('exit', function (code) { + var error = null + if (code !== null && code !== 0) { + error = new Error('Redis shutdown failed with code ' + code) + } + waitForRedis(false, function () { + return done(error) + }, port) + }) + rp.kill('SIGTERM') + } + }) + }, port) + } +} diff --git a/test/lib/stunnel-process.js b/test/lib/stunnel-process.js index 16dbba5d88..8dc883be31 100644 --- a/test/lib/stunnel-process.js +++ b/test/lib/stunnel-process.js @@ -1,83 +1,83 @@ -'use strict'; +'use strict' // helper to start and stop the stunnel process. -var spawn = require('child_process').spawn; -var EventEmitter = require('events'); -var fs = require('fs'); -var path = require('path'); -var util = require('util'); +var spawn = require('child_process').spawn +var EventEmitter = require('events') +var fs = require('fs') +var path = require('path') +var util = require('util') function once (cb) { - var called = false; - return function () { - if (called) return; - called = true; - cb.apply(this, arguments); - }; + var called = false + return function () { + if (called) return + called = true + cb.apply(this, arguments) + } } function StunnelProcess (confDir) { - EventEmitter.call(this); + EventEmitter.call(this) - // set up an stunnel to redis; edit the conf file to include required absolute paths - var confFile = path.resolve(confDir, 'stunnel.conf'); - var confText = fs.readFileSync(confFile + '.template').toString().replace(/__dirname,/g, confDir); + // set up an stunnel to redis; edit the conf file to include required absolute paths + var confFile = path.resolve(confDir, 'stunnel.conf') + var confText = fs.readFileSync(confFile + '.template').toString().replace(/__dirname,/g, confDir) - fs.writeFileSync(confFile, confText); - var stunnel = this.stunnel = spawn('stunnel', [confFile]); + fs.writeFileSync(confFile, confText) + var stunnel = this.stunnel = spawn('stunnel', [confFile]) - // handle child process events, and failure to set up tunnel - var self = this; - this.timer = setTimeout(function () { - self.emit('error', new Error('Timeout waiting for stunnel to start')); - }, 8000); + // handle child process events, and failure to set up tunnel + var self = this + this.timer = setTimeout(function () { + self.emit('error', new Error('Timeout waiting for stunnel to start')) + }, 8000) - stunnel.on('error', function (err) { - self.clear(); - self.emit('error', err); - }); + stunnel.on('error', function (err) { + self.clear() + self.emit('error', err) + }) - stunnel.on('exit', function (code) { - self.clear(); - if (code === 0) { - self.emit('stopped'); - } else { - self.emit('error', new Error('Stunnel exited unexpectedly; code = ' + code)); - } - }); + stunnel.on('exit', function (code) { + self.clear() + if (code === 0) { + self.emit('stopped') + } else { + self.emit('error', new Error('Stunnel exited unexpectedly; code = ' + code)) + } + }) - // wait to stunnel to start - stunnel.stderr.on('data', function (data) { - if (data.toString().match(/Service.+redis.+bound/)) { - clearTimeout(this.timer); - self.emit('started'); - } - }); + // wait to stunnel to start + stunnel.stderr.on('data', function (data) { + if (data.toString().match(/Service.+redis.+bound/)) { + clearTimeout(this.timer) + self.emit('started') + } + }) } -util.inherits(StunnelProcess, EventEmitter); +util.inherits(StunnelProcess, EventEmitter) StunnelProcess.prototype.clear = function () { - this.stunnel = null; - clearTimeout(this.timer); -}; + this.stunnel = null + clearTimeout(this.timer) +} StunnelProcess.prototype.stop = function (done) { - if (this.stunnel) { - this.stunnel.kill(); - } -}; + if (this.stunnel) { + this.stunnel.kill() + } +} module.exports = { - start: function (done, confDir) { - done = once(done); - var stunnel = new StunnelProcess(confDir); - stunnel.once('error', done.bind(done)); - stunnel.once('started', done.bind(done, null, stunnel)); - }, - stop: function (stunnel, done) { - stunnel.removeAllListeners(); - stunnel.stop(); - stunnel.once('error', done.bind(done)); - stunnel.once('stopped', done.bind(done, null)); - } -}; + start: function (done, confDir) { + done = once(done) + var stunnel = new StunnelProcess(confDir) + stunnel.once('error', done.bind(done)) + stunnel.once('started', done.bind(done, null, stunnel)) + }, + stop: function (stunnel, done) { + stunnel.removeAllListeners() + stunnel.stop() + stunnel.once('error', done.bind(done)) + stunnel.once('stopped', done.bind(done, null)) + } +} diff --git a/test/lib/unref.js b/test/lib/unref.js index 2cca7c8e98..e125c018c9 100644 --- a/test/lib/unref.js +++ b/test/lib/unref.js @@ -1,17 +1,17 @@ // spawned by the unref tests in nodeRedis.spec.js. // when configured, unref causes the client to exit // as soon as there are no outstanding commands. -'use strict'; +'use strict' -var redis = require('../../index'); -var HOST = process.argv[2] || '127.0.0.1'; -var PORT = process.argv[3]; -var args = PORT ? [PORT, HOST] : [HOST]; +var redis = require('../../index') +var HOST = process.argv[2] || '127.0.0.1' +var PORT = process.argv[3] +var args = PORT ? [PORT, HOST] : [HOST] -var c = redis.createClient.apply(redis, args); +var c = redis.createClient.apply(redis, args) c.info(function (err, reply) { - if (err) process.exit(-1); - if (!reply.length) process.exit(-1); - process.stdout.write(reply.length.toString()); -}); -c.unref(); + if (err) process.exit(-1) + if (!reply.length) process.exit(-1) + process.stdout.write(reply.length.toString()) +}) +c.unref() diff --git a/test/multi.spec.js b/test/multi.spec.js index 4758af600f..6539ed3288 100644 --- a/test/multi.spec.js +++ b/test/multi.spec.js @@ -1,727 +1,703 @@ -'use strict'; +'use strict' -var assert = require('assert'); -var config = require('./lib/config'); -var helper = require('./helper'); -var utils = require('../lib/utils'); -var redis = config.redis; -var zlib = require('zlib'); -var client; +var Buffer = require('safe-buffer').Buffer +var assert = require('assert') +var config = require('./lib/config') +var helper = require('./helper') +var utils = require('../lib/utils') +var redis = config.redis +var zlib = require('zlib') +var client -describe("The 'multi' method", function () { +describe('The \'multi\' method', function () { + afterEach(function () { + client.end(true) + }) - afterEach(function () { - client.end(true); - }); + describe('regression test', function () { + it('saved buffers with charsets different than utf-8 (issue #913)', function (done) { + this.timeout(12000) // Windows tests on 0.10 are slow + client = redis.createClient() - describe('regression test', function () { - it('saved buffers with charsets different than utf-8 (issue #913)', function (done) { - this.timeout(12000); // Windows tests on 0.10 are slow - client = redis.createClient(); + var end = helper.callFuncAfter(done, 100) - var end = helper.callFuncAfter(done, 100); - - // Some random object created from http://beta.json-generator.com/ - var testObj = { - 'Id': '5642c4c33d4667c4a1fefd99', 'index': 0, 'guid': '5baf1f1c-7621-41e7-ae7a-f8c6f3199b0f', 'isActive': true, - 'balance': '$1,028.63', 'picture': 'http://placehold.it/32x32', 'age': 31, 'eyeColor': 'green', 'name': {'first': 'Shana', 'last': 'Long'}, - 'company': 'MANGLO', 'email': 'shana.long@manglo.us', 'phone': '+1 (926) 405-3105', 'address': '747 Dank Court, Norfolk, Ohio, 1112', - 'about': 'Eu pariatur in nisi occaecat enim qui consequat nostrud cupidatat id. ' + + // Some random object created from http://beta.json-generator.com/ + var testObj = { + 'Id': '5642c4c33d4667c4a1fefd99', + 'index': 0, + 'guid': '5baf1f1c-7621-41e7-ae7a-f8c6f3199b0f', + 'isActive': true, + 'balance': '$1,028.63', + 'picture': 'http://placehold.it/32x32', + 'age': 31, + 'eyeColor': 'green', + 'name': {'first': 'Shana', 'last': 'Long'}, + 'company': 'MANGLO', + 'email': 'shana.long@manglo.us', + 'phone': '+1 (926) 405-3105', + 'address': '747 Dank Court, Norfolk, Ohio, 1112', + '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.', - 'registered': 'Friday, April 18, 2014 9:56 AM', 'latitude': '74.566613', 'longitude': '-11.660432', 'tags': [7, 'excepteur'], - 'range': [0, 1, 2, 3, 4, 5, 6, 7, 8, 9], 'friends': [3, {'id': 1, 'name': 'Schultz Dyer'}], - 'greeting': 'Hello, Shana! You have 5 unread messages.', 'favoriteFruit': 'strawberry' - }; + 'registered': 'Friday, April 18, 2014 9:56 AM', + 'latitude': '74.566613', + 'longitude': '-11.660432', + 'tags': [7, 'excepteur'], + 'range': [0, 1, 2, 3, 4, 5, 6, 7, 8, 9], + 'friends': [3, {'id': 1, 'name': 'Schultz Dyer'}], + 'greeting': 'Hello, Shana! You have 5 unread messages.', + 'favoriteFruit': 'strawberry' + } - function run () { - if (end() === true) { - return; - } - // To demonstrate a big payload for hash set field values, let's create a big array - var testArr = []; - var i = 0; - for (; i < 80; i++) { - var newObj = JSON.parse(JSON.stringify(testObj)); - testArr.push(newObj); - } + function run () { + if (end() === true) { + return + } + // To demonstrate a big payload for hash set field values, let's create a big array + var testArr = [] + var i = 0 + for (; i < 80; i++) { + var newObj = JSON.parse(JSON.stringify(testObj)) + testArr.push(newObj) + } - var json = JSON.stringify(testArr); - zlib.deflate(new Buffer(json), function (err, buffer) { - if (err) { - done(err); - return; - } + var json = JSON.stringify(testArr) + zlib.deflate(Buffer.from(json), function (err, buffer) { + if (err) { + done(err) + return + } - var multi = client.multi(); - multi.del('SOME_KEY'); + var multi = client.multi() + multi.del('SOME_KEY') - for (i = 0; i < 100; i++) { - multi.hset('SOME_KEY', 'SOME_FIELD' + i, buffer); - } - multi.exec(function (err, res) { - if (err) { - done(err); - return; - } - run(); - }); - }); + for (i = 0; i < 100; i++) { + multi.hset('SOME_KEY', 'SOME_FIELD' + i, buffer) + } + multi.exec(function (err, res) { + if (err) { + done(err) + return } - run(); - }); - }); + run() + }) + }) + } + run() + }) + }) - describe('pipeline limit', function () { + describe('pipeline limit', function () { + it('do not exceed maximum string size', function (done) { + this.timeout(process.platform !== 'win32' ? 10000 : 35000) // Windows tests are horribly slow + // Triggers a RangeError: Invalid string length if not handled properly + client = redis.createClient() + var multi = client.multi() + var i = Math.pow(2, 28) + while (i > 0) { + i -= 10230 + multi.set('foo' + i, 'bar' + new Array(1024).join('1234567890')) + } + client.on('ready', function () { + multi.exec(function (err, res) { + assert.strictEqual(err, null) + assert.strictEqual(res.length, 26241) + }) + client.flushdb(done) + }) + }) + }) - it('do not exceed maximum string size', function (done) { - this.timeout(process.platform !== 'win32' ? 10000 : 35000); // Windows tests are horribly slow - // Triggers a RangeError: Invalid string length if not handled properly - client = redis.createClient(); - var multi = client.multi(); - var i = Math.pow(2, 28); - while (i > 0) { - i -= 10230; - multi.set('foo' + i, 'bar' + new Array(1024).join('1234567890')); + helper.allTests(function (ip, args) { + describe('using ' + ip, function () { + describe('when not connected', function () { + beforeEach(function (done) { + var end = helper.callFuncAfter(done, 2) + client = redis.createClient.apply(null, args) + client.once('ready', function () { + client.quit(end) + }) + client.once('end', end) + }) + + it('reports an error', function (done) { + var multi = client.multi() + var notBuffering = multi.exec(function (err, res) { + assert(err.message.match(/The connection is already closed/)) + done() + }) + assert.strictEqual(notBuffering, false) + }) + + it('reports an error if promisified', function () { + return client.multi().execAsync().catch(function (err) { + assert(err.message.match(/The connection is already closed/)) + }) + }) + }) + + describe('when connected', function () { + beforeEach(function () { + client = redis.createClient.apply(null, args) + }) + + describe('monitor and transactions do not work together', function () { + it('results in a execabort', function (done) { + // Check that transactions in combination with monitor result in an error + client.monitor(function (e) { + client.on('error', function (err) { + assert.strictEqual(err.code, 'EXECABORT') + client.end(false) + done() + }) + var multi = client.multi() + multi.set('hello', 'world') + multi.exec() + }) + }) + + it('results in a execabort #2', function (done) { + // Check that using monitor with a transactions results in an error + client.multi().set('foo', 'bar').monitor().exec(function (err, res) { + assert.strictEqual(err.code, 'EXECABORT') + client.end(false) + done() + }) + }) + + it('sanity check', function (done) { + // Remove the listener and add it back again after the error + var mochaListener = helper.removeMochaListener() + process.on('uncaughtException', function () { + helper.removeMochaListener() + process.on('uncaughtException', mochaListener) + done() + }) + // Check if Redis still has the error + client.monitor() + client.sendCommand('multi') + client.sendCommand('set', ['foo', 'bar']) + client.sendCommand('get', ['foo']) + client.sendCommand('exec', function (err, res) { + assert.strictEqual(err, null) + // res[0] is going to be the monitor result of set + // res[1] is going to be the result of the set command + assert(utils.monitorRegex.test(res[0])) + assert.strictEqual(res[1], 'OK') + assert.strictEqual(res.length, 2) + client.end(false) + }) + }) + }) + + it('executes a pipelined multi properly in combination with the offline queue', function (done) { + var multi1 = client.multi() + multi1.set('m1', '123') + multi1.get('m1') + multi1.exec(done) + assert.strictEqual(client.offlineQueue.length, 4) + }) + + it('executes a pipelined multi properly after a reconnect in combination with the offline queue', function (done) { + client.once('ready', function () { + client.stream.destroy() + var called = false + var multi1 = client.multi() + multi1.set('m1', '123') + multi1.get('m1') + multi1.exec(function (err, res) { + assert(!err) + called = true + }) + client.once('ready', function () { + var multi1 = client.multi() + multi1.set('m2', '456') + multi1.get('m2') + multi1.exec(function (err, res) { + assert(called) + assert(!err) + assert.strictEqual(res[1], '456') + done() + }) + }) + }) + }) + }) + + describe('when connection is broken', function () { + it.skip('return an error even if connection is in broken mode if callback is present', function (done) { + client = redis.createClient({ + host: 'somewhere', + port: 6379, + retryStrategy: function () {} + }) + + client.on('error', function (err) { + if (/Redis connection in broken state/.test(err.message)) { + done() } - client.on('ready', function () { - multi.exec(function (err, res) { - assert.strictEqual(res.length, 26241); - }); - client.flushdb(done); - }); - }); - - }); - - helper.allTests(function (ip, args) { - - describe('using ' + ip, function () { - - describe('when not connected', function () { - - beforeEach(function (done) { - var end = helper.callFuncAfter(done, 2); - client = redis.createClient.apply(null, args); - client.once('ready', function () { - client.quit(end); - }); - client.once('end', end); - }); - - it('reports an error', function (done) { - var multi = client.multi(); - var notBuffering = multi.exec(function (err, res) { - assert(err.message.match(/The connection is already closed/)); - done(); - }); - assert.strictEqual(notBuffering, false); - }); - - it('reports an error if promisified', function () { - return client.multi().execAsync().catch(function (err) { - assert(err.message.match(/The connection is already closed/)); - }); - }); - }); - - describe('when connected', function () { - - beforeEach(function () { - client = redis.createClient.apply(null, args); - }); - - describe('monitor and transactions do not work together', function () { - - it('results in a execabort', function (done) { - // Check that transactions in combination with monitor result in an error - client.monitor(function (e) { - client.on('error', function (err) { - assert.strictEqual(err.code, 'EXECABORT'); - client.end(false); - done(); - }); - var multi = client.multi(); - multi.set('hello', 'world'); - multi.exec(); - }); - }); - - it('results in a execabort #2', function (done) { - // Check that using monitor with a transactions results in an error - client.multi().set('foo', 'bar').monitor().exec(function (err, res) { - assert.strictEqual(err.code, 'EXECABORT'); - client.end(false); - done(); - }); - }); - - it('sanity check', function (done) { - // Remove the listener and add it back again after the error - var mochaListener = helper.removeMochaListener(); - process.on('uncaughtException', function (err) { - helper.removeMochaListener(); - process.on('uncaughtException', mochaListener); - done(); - }); - // Check if Redis still has the error - client.monitor(); - client.sendCommand('multi'); - client.sendCommand('set', ['foo', 'bar']); - client.sendCommand('get', ['foo']); - client.sendCommand('exec', function (err, res) { - // res[0] is going to be the monitor result of set - // res[1] is going to be the result of the set command - assert(utils.monitorRegex.test(res[0])); - assert.strictEqual(res[1], 'OK'); - assert.strictEqual(res.length, 2); - client.end(false); - }); - }); - }); - - it('executes a pipelined multi properly in combination with the offline queue', function (done) { - var multi1 = client.multi(); - multi1.set('m1', '123'); - multi1.get('m1'); - multi1.exec(done); - assert.strictEqual(client.offlineQueue.length, 4); - }); - - it('executes a pipelined multi properly after a reconnect in combination with the offline queue', function (done) { - client.once('ready', function () { - client.stream.destroy(); - var called = false; - var multi1 = client.multi(); - multi1.set('m1', '123'); - multi1.get('m1'); - multi1.exec(function (err, res) { - assert(!err); - called = true; - }); - client.once('ready', function () { - var multi1 = client.multi(); - multi1.set('m2', '456'); - multi1.get('m2'); - multi1.exec(function (err, res) { - assert(called); - assert(!err); - assert.strictEqual(res[1], '456'); - done(); - }); - }); - }); - }); - }); - - describe('when connection is broken', function () { - - it.skip('return an error even if connection is in broken mode if callback is present', function (done) { - client = redis.createClient({ - host: 'somewhere', - port: 6379, - retryStrategy: function () {} - }); - - client.on('error', function (err) { - if (/Redis connection in broken state/.test(err.message)) { - done(); - } - }); - - client.multi([['set', 'foo', 'bar'], ['get', 'foo']]).exec(function (err, res) { - // assert(/Redis connection in broken state/.test(err.message)); - assert.strictEqual(err.errors.length, 2); - assert.strictEqual(err.errors[0].args.length, 2); - }); - }); - - it.skip('does not emit an error twice if connection is in broken mode with no callback', function (done) { - client = redis.createClient({ - host: 'somewhere', - port: 6379, - retryStrategy: function () {} - }); - - client.on('error', function (err) { - // Results in multiple done calls if test fails - if (/Redis connection in broken state/.test(err.message)) { - done(); - } - }); - - client.multi([['set', 'foo', 'bar'], ['get', 'foo']]).exec(); - }); - }); - - describe('when ready', function () { - - beforeEach(function (done) { - client = redis.createClient.apply(null, args); - client.once('ready', function () { - client.flushdb(function (err) { - return done(err); - }); - }); - }); - - it('returns an empty result array', function (done) { - var multi = client.multi(); - var notBuffering = multi.exec(function (err, res) { - assert.strictEqual(err, null); - assert.strictEqual(res.length, 0); - done(); - }); - assert.strictEqual(notBuffering, true); - }); - - it('runs normal calls in-between multis', function (done) { - var multi1 = client.multi(); - multi1.set('m1', '123'); - client.set('m2', '456', done); - }); - - it('runs simultaneous multis with the same client', function (done) { - var end = helper.callFuncAfter(done, 2); - - var multi1 = client.multi(); - multi1.set('m1', '123'); - multi1.get('m1'); - - var multi2 = client.multi(); - multi2.set('m2', '456'); - multi2.get('m2'); - - multi1.exec(end); - multi2.exec(function (err, res) { - assert.strictEqual(res[1], '456'); - end(); - }); - }); - - it('runs simultaneous multis with the same client version 2', function (done) { - var end = helper.callFuncAfter(done, 2); - var multi2 = client.multi(); - var multi1 = client.multi(); - - multi2.set('m2', '456'); - multi1.set('m1', '123'); - multi1.get('m1'); - multi2.get('m1'); - multi2.ping(); - - multi1.exec(end); - multi2.exec(function (err, res) { - assert.strictEqual(res[1], '123'); - end(); - }); - }); - - it('roles back a transaction when one command in a sequence of commands fails', function (done) { - var multi1, multi2; - // Provoke an error at queue time - multi1 = client.multi(); - multi1.mset('multifoo', '10', 'multibar', '20', helper.isString('OK')); - - multi1.set('foo2', helper.isError()); - multi1.incr('multifoo'); - multi1.incr('multibar'); - multi1.exec(function () { - // Redis 2.6.5+ will abort transactions with errors - // see: http://redis.io/topics/transactions - var multibarExpected = 1; - var multifooExpected = 1; - // Confirm that the previous command, while containing an error, still worked. - multi2 = client.multi(); - multi2.incr('multibar', helper.isNumber(multibarExpected)); - multi2.incr('multifoo', helper.isNumber(multifooExpected)); - multi2.exec(function (err, replies) { - assert.strictEqual(multibarExpected, replies[0]); - assert.strictEqual(multifooExpected, replies[1]); - return done(); - }); - }); - }); - - it('roles back a transaction when one command in an array of commands fails', function (done) { - // test nested multi-bulk replies - client.multi([ - ['mget', 'multifoo', 'multibar', function (err, res) { - assert.strictEqual(2, res.length); - assert.strictEqual(0, +res[0]); - assert.strictEqual(0, +res[1]); - }], - ['set', 'foo2', helper.isError()], - ['incr', 'multifoo'], - ['incr', 'multibar'] - ]).exec(function (err, replies) { - assert.notEqual(err, null); - assert.equal(replies, undefined); - return done(); - }); - }); - - it('handles multiple operations being applied to a set', function (done) { - client.sadd('some set', 'mem 1'); - client.sadd(['some set', 'mem 2']); - client.sadd('some set', 'mem 3'); - client.sadd('some set', 'mem 4'); - - // make sure empty mb reply works - client.del('some missing set'); - client.smembers('some missing set', function (err, reply) { - // make sure empty mb reply works - assert.strictEqual(0, reply.length); - }); - - // test nested multi-bulk replies with empty mb elements. - client.multi([ - ['smembers', ['some set']], - ['del', 'some set'], - ['smembers', 'some set'] - ]) - .scard('some set') - .exec(function (err, replies) { - assert.strictEqual(4, replies[0].length); - assert.strictEqual(0, replies[2].length); - return done(); - }); - }); - - it('allows multiple operations to be performed using constructor with all kinds of syntax', function (done) { - var now = Date.now(); - var arr = ['multihmset', 'multibar', 'multibaz']; - var arr2 = ['some manner of key', 'otherTypes']; - var arr3 = [5768, 'multibarx', 'multifoox']; - var arr4 = ['mset', [578, 'multibar'], helper.isString('OK')]; - var called = false; - client.multi([ - arr4, - [['mset', 'multifoo2', 'multibar2', 'multifoo3', 'multibar3'], helper.isString('OK')], - ['hmset', arr], - [['hmset', 'multihmset2', 'multibar2', 'multifoo3', 'multibar3', 'test'], helper.isString('OK')], - ['hmset', ['multihmset', 'multibar', 'multifoo'], helper.isString('OK')], - ['hmset', arr3, helper.isString('OK')], - ['hmset', now, {123456789: 'abcdefghij', 'some manner of key': 'a type of value', 'otherTypes': 555}], - ['hmset', 'key2', {'0123456789': 'abcdefghij', 'some manner of key': 'a type of value', 'otherTypes': 999}, helper.isString('OK')], - ['hmset', 'multihmset', ['multibar', 'multibaz'], undefined], // undefined is used as a explicit not set callback variable - ['hmset', 'multihmset', ['multibar', 'multibaz'], helper.isString('OK')], - ]) - .hmget(now, 123456789, 'otherTypes') - .hmget('key2', arr2, function noop () {}) - .hmget(['multihmset2', 'some manner of key', 'multibar3']) - .mget('multifoo2', ['multifoo3', 'multifoo'], function (err, res) { - assert(res[0], 'multifoo3'); - assert(res[1], 'multifoo'); - called = true; - }) - .exec(function (err, replies) { - assert(called); - assert.equal(arr.length, 3); - assert.equal(arr2.length, 2); - assert.equal(arr3.length, 3); - assert.equal(arr4.length, 3); - assert.strictEqual(null, err); - assert.equal(replies[10][1], '555'); - assert.equal(replies[11][0], 'a type of value'); - assert.strictEqual(replies[12][0], null); - assert.equal(replies[12][1], 'test'); - assert.equal(replies[13][0], 'multibar2'); - assert.equal(replies[13].length, 3); - assert.equal(replies.length, 14); - return done(); - }); - }); - - it('converts a non string key to a string', function (done) { - // TODO: Converting the key might change soon again. - client.multi().hmset(true, { - test: 123, - bar: 'baz' - }).exec(done); - }); - - it('runs a multi without any further commands', function (done) { - var buffering = client.multi().exec(function (err, res) { - assert.strictEqual(err, null); - assert.strictEqual(res.length, 0); - done(); - }); - assert(typeof buffering === 'boolean'); - }); - - it('allows multiple operations to be performed using a chaining API', function (done) { - client.multi() - .mset('some', '10', 'keys', '20') - .incr('some') - .incr('keys') - .mget('some', ['keys']) - .exec(function (err, replies) { - assert.strictEqual(null, err); - assert.equal('OK', replies[0]); - assert.equal(11, replies[1]); - assert.equal(21, replies[2]); - assert.equal(11, replies[3][0].toString()); - assert.equal(21, replies[3][1].toString()); - return done(); - }); - }); - - it('allows multiple commands to work the same as normal to be performed using a chaining API', function (done) { - client.multi() - .mset(['some', '10', 'keys', '20']) - .incr('some', helper.isNumber(11)) - .incr(['keys'], helper.isNumber(21)) - .mget('some', 'keys') - .exec(function (err, replies) { - assert.strictEqual(null, err); - assert.equal('OK', replies[0]); - assert.equal(11, replies[1]); - assert.equal(21, replies[2]); - assert.equal(11, replies[3][0].toString()); - assert.equal(21, replies[3][1].toString()); - return done(); - }); - }); - - it('allows multiple commands to work the same as normal to be performed using a chaining API promisified', function () { - return client.multi() - .mset(['some', '10', 'keys', '20']) - .incr('some', helper.isNumber(11)) - .incr(['keys'], helper.isNumber(21)) - .mget('some', 'keys') - .execAsync() - .then(function (replies) { - assert.equal('OK', replies[0]); - assert.equal(11, replies[1]); - assert.equal(21, replies[2]); - assert.equal(11, replies[3][0].toString()); - assert.equal(21, replies[3][1].toString()); - }); - }); - - it('allows an array to be provided indicating multiple operations to perform', function (done) { - // test nested multi-bulk replies with nulls. - client.multi([ - ['mget', ['multifoo', 'some', 'random value', 'keys']], - ['incr', 'multifoo'] - ]) - .exec(function (err, replies) { - assert.strictEqual(replies.length, 2); - assert.strictEqual(replies[0].length, 4); - return done(); - }); - }); - - it('allows multiple operations to be performed on a hash', function (done) { - client.multi() - .hmset('multihash', 'a', 'foo', 'b', 1) - .hmset('multihash', { - extra: 'fancy', - things: 'here' - }) - .hgetall('multihash') - .exec(function (err, replies) { - assert.strictEqual(null, err); - assert.equal('OK', replies[0]); - assert.equal(Object.keys(replies[2]).length, 4); - assert.equal('foo', replies[2].a); - assert.equal('1', replies[2].b); - assert.equal('fancy', replies[2].extra); - assert.equal('here', replies[2].things); - return done(); - }); - }); - - it('reports EXECABORT exceptions when they occur (while queueing)', function (done) { - client.multi().config('bar').set('foo').set('bar').exec(function (err, reply) { - assert.equal(err.code, 'EXECABORT'); - assert.equal(reply, undefined, 'The reply should have been discarded'); - assert(err.message.match(/^EXECABORT/), 'Error message should begin with EXECABORT'); - assert.equal(err.errors.length, 2, 'err.errors should have 2 items'); - assert.strictEqual(err.errors[0].command, 'SET'); - assert.strictEqual(err.errors[0].code, 'ERR'); - assert.strictEqual(err.errors[0].position, 1); - assert(/^ERR/.test(err.errors[0].message), 'Actuall error message should begin with ERR'); - return done(); - }); - }); - - it('reports multiple exceptions when they occur (while EXEC is running)', function (done) { - client.multi().config('bar').debug('foo').eval("return {err='this is an error'}", 0).exec(function (err, reply) { - assert.strictEqual(reply.length, 3); - assert.equal(reply[0].code, 'ERR'); - assert.equal(reply[0].command, 'CONFIG'); - assert.equal(reply[2].code, undefined); - assert.equal(reply[2].command, 'EVAL'); - assert(/^this is an error/.test(reply[2].message)); - assert(/^ERR/.test(reply[0].message), 'Error message should begin with ERR'); - assert(/^ERR/.test(reply[1].message), 'Error message should begin with ERR'); - return done(); - }); - }); - - it('reports multiple exceptions when they occur (while EXEC is running) promisified', function () { - return client.multi().config('bar').debug('foo').eval("return {err='this is an error'}", 0).execAsync().then(function (reply) { - assert.strictEqual(reply.length, 3); - assert.equal(reply[0].code, 'ERR'); - assert.equal(reply[0].command, 'CONFIG'); - assert.equal(reply[2].code, undefined); - assert.equal(reply[2].command, 'EVAL'); - assert(/^this is an error/.test(reply[2].message)); - assert(/^ERR/.test(reply[0].message), 'Error message should begin with ERR'); - assert(/^ERR/.test(reply[1].message), 'Error message should begin with ERR'); - }); - }); - - it('reports multiple exceptions when they occur (while EXEC is running) and calls cb', function (done) { - var multi = client.multi(); - multi.config('bar', helper.isError()); - multi.set('foo', 'bar', helper.isString('OK')); - multi.debug('foo').exec(function (err, reply) { - assert.strictEqual(reply.length, 3); - assert.strictEqual(reply[0].code, 'ERR'); - assert(/^ERR/.test(reply[0].message), 'Error message should begin with ERR'); - assert(/^ERR/.test(reply[2].message), 'Error message should begin with ERR'); - assert.strictEqual(reply[1], 'OK'); - client.get('foo', helper.isString('bar', done)); - }); - }); - - it('emits an error if no callback has been provided and execabort error occured', function (done) { - var multi = client.multi(); - multi.config('bar'); - multi.set('foo'); - multi.exec(); - - client.on('error', function (err) { - assert.equal(err.code, 'EXECABORT'); - done(); - }); - }); - - it('should work without any callback', function (done) { - var multi = client.multi(); - multi.set('baz', 'binary'); - multi.set('foo', 'bar'); - multi.exec(); - - client.get('foo', helper.isString('bar', done)); - }); - - it('should not use a transaction with execAtomic if no command is used', function () { - var multi = client.multi(); - var test = false; - multi.execBatch = function () { - test = true; - }; - multi.execAtomic(); - assert(test); - }); - - it('should not use a transaction with execAtomic if only one command is used', function () { - var multi = client.multi(); - var test = false; - multi.execBatch = function () { - test = true; - }; - multi.set('baz', 'binary'); - multi.execAtomic(); - assert(test); - }); - - it('should use transaction with execAtomic and more than one command used', function (done) { - var multi = client.multi(); - var test = false; - multi.execBatch = function () { - test = true; - }; - multi.set('baz', 'binary'); - multi.get('baz'); - multi.execAtomic(done); - assert(!test); - }); - - it('do not mutate arguments in the multi constructor', function (done) { - var input = [['set', 'foo', 'bar'], ['get', 'foo']]; - client.multi(input).exec(function (err, res) { - assert.strictEqual(input.length, 2); - assert.strictEqual(input[0].length, 3); - assert.strictEqual(input[1].length, 2); - done(); - }); - }); - - it('works properly after a reconnect. issue #897', function (done) { - client.stream.destroy(); - client.on('error', function (err) { - assert.strictEqual(err.code, 'ECONNREFUSED'); - }); - client.on('ready', function () { - client.multi([['set', 'foo', 'bar'], ['get', 'foo']]).exec(function (err, res) { - assert(!err); - assert.strictEqual(res[1], 'bar'); - done(); - }); - }); - }); - - it('emits error once if reconnecting after multi has been executed but not yet returned without callback', function (done) { - // NOTE: If uncork is called async by postponing it to the next tick, this behavior is going to change. - // The command won't be processed anymore two errors are returned instead of one - client.on('error', function (err) { - assert.strictEqual(err.code, 'UNCERTAIN_STATE'); - client.get('foo', function (err, res) { - assert.strictEqual(res, 'bar'); - done(); - }); - }); - - // The commands should still be fired, no matter that the socket is destroyed on the same tick - client.multi().set('foo', 'bar').get('foo').exec(); - // Abort connection before the value returned - client.stream.destroy(); - }); - - it('indivdual commands work properly with multi', function (done) { - // Neither of the following work properly in a transactions: - // (This is due to Redis not returning the reply as expected / resulting in undefined behavior) - // (Likely there are more commands that do not work with a transaction) - // - // auth => can't be called after a multi command - // monitor => results in faulty return values e.g. multi().monitor().set('foo', 'bar').get('foo') - // returns ['OK, 'OK', 'monitor reply'] instead of ['OK', 'OK', 'bar'] - // quit => ends the connection before the exec - // client reply skip|off => results in weird return values. Not sure 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 - client.sendCommand = function () { - throw new Error('failed'); - }; - - assert.strictEqual(client.selectedDb, undefined); - var multi = client.multi(); - multi.select(5, function (err, res) { - assert.strictEqual(client.selectedDb, 5); - assert.strictEqual(res, 'OK'); - assert.notDeepEqual(client.serverInfo.db5, { avg_ttl: 0, expires: 0, keys: 1 }); - }); - // multi.client('reply', 'on', helper.isString('OK')); // Redis v.3.2 - multi.set('foo', 'bar', helper.isString('OK')); - multi.info(function (err, res) { - assert.strictEqual(res.indexOf('# Server\r\nredis_version:'), 0); - assert.deepEqual(client.serverInfo.db5, { avg_ttl: 0, expires: 0, keys: 1 }); - }); - multi.get('foo', helper.isString('bar')); - multi.exec(function (err, res) { - res[2] = res[2].substr(0, 10); - assert.deepEqual(res, ['OK', 'OK', '# Server\r\n', 'bar']); - client.flushdb(done); - }); - }); - - }); - }); - }); -}); + }) + + client.multi([['set', 'foo', 'bar'], ['get', 'foo']]).exec(function (err, res) { + // assert(/Redis connection in broken state/.test(err.message)); + assert.strictEqual(err.errors.length, 2) + assert.strictEqual(err.errors[0].args.length, 2) + }) + }) + + it.skip('does not emit an error twice if connection is in broken mode with no callback', function (done) { + client = redis.createClient({ + host: 'somewhere', + port: 6379, + retryStrategy: function () {} + }) + + client.on('error', function (err) { + // Results in multiple done calls if test fails + if (/Redis connection in broken state/.test(err.message)) { + done() + } + }) + + client.multi([['set', 'foo', 'bar'], ['get', 'foo']]).exec() + }) + }) + + describe('when ready', function () { + beforeEach(function (done) { + client = redis.createClient.apply(null, args) + client.once('ready', function () { + client.flushdb(function (err) { + return done(err) + }) + }) + }) + + it('returns an empty result array', function (done) { + var multi = client.multi() + var notBuffering = multi.exec(function (err, res) { + assert.strictEqual(err, null) + assert.strictEqual(res.length, 0) + done() + }) + assert.strictEqual(notBuffering, true) + }) + + it('runs normal calls in-between multis', function (done) { + var multi1 = client.multi() + multi1.set('m1', '123') + client.set('m2', '456', done) + }) + + it('runs simultaneous multis with the same client', function (done) { + var end = helper.callFuncAfter(done, 2) + + var multi1 = client.multi() + multi1.set('m1', '123') + multi1.get('m1') + + var multi2 = client.multi() + multi2.set('m2', '456') + multi2.get('m2') + + multi1.exec(end) + multi2.exec(helper.isDeepEqual(['OK', '456'], end)) + }) + + it('runs simultaneous multis with the same client version 2', function (done) { + var end = helper.callFuncAfter(done, 2) + var multi2 = client.multi() + var multi1 = client.multi() + + multi2.set('m2', '456') + multi1.set('m1', '123') + multi1.get('m1') + multi2.get('m1') + multi2.ping() + + multi1.exec(end) + multi2.exec(helper.isDeepEqual(['OK', '123', 'PONG'], end)) + }) + + it('roles back a transaction when one command in a sequence of commands fails', function (done) { + var multi1, multi2 + // Provoke an error at queue time + multi1 = client.multi() + multi1.mset('multifoo', '10', 'multibar', '20', helper.isString('OK')) + + multi1.set('foo2', helper.isError()) + multi1.incr('multifoo') + multi1.incr('multibar') + multi1.exec(function () { + // Redis 2.6.5+ will abort transactions with errors + // see: http://redis.io/topics/transactions + var multibarExpected = 1 + var multifooExpected = 1 + // Confirm that the previous command, while containing an error, still worked. + multi2 = client.multi() + multi2.incr('multibar', helper.isNumber(multibarExpected)) + multi2.incr('multifoo', helper.isNumber(multifooExpected)) + multi2.exec(helper.isDeepEqual([multibarExpected, multibarExpected], done)) + }) + }) + + it('roles back a transaction when one command in an array of commands fails', function (done) { + // test nested multi-bulk replies + client.multi([ + ['mget', 'multifoo', 'multibar', helper.isDeepEqual([null, null])], + ['set', 'foo2', helper.isError()], + ['incr', 'multifoo'], + ['incr', 'multibar'] + ]).exec(function (err, replies) { + assert.notEqual(err, null) + assert.strictEqual(replies, undefined) + return done() + }) + }) + + it('handles multiple operations being applied to a set', function (done) { + client.sadd('some set', 'mem 1') + client.sadd(['some set', 'mem 2']) + client.sadd('some set', 'mem 3') + client.sadd('some set', 'mem 4') + + // make sure empty mb reply works + client.del('some missing set') + client.smembers('some missing set', helper.isDeepEqual([])) + + // test nested multi-bulk replies with empty mb elements. + client.multi([ + ['smembers', ['some set']], + ['del', 'some set'], + ['smembers', 'some set'] + ]) + .scard('some set') + .exec(function (err, res) { + assert.strictEqual(err, null) + assert.strictEqual(res[0].length, 4) + assert.strictEqual(res[1], 1) + assert.deepStrictEqual(res[2], []) + assert.strictEqual(res[3], 0) + done() + }) + }) + + it('allows multiple operations to be performed using constructor with all kinds of syntax', function (done) { + var now = Date.now() + var arr = ['multihmset', 'multibar', 'multibaz'] + var arr2 = ['some manner of key', 'otherTypes'] + var arr3 = [5768, 'multibarx', 'multifoox'] + var arr4 = ['mset', [578, 'multibar'], helper.isString('OK')] + var called = false + client.multi([ + arr4, + [['mset', 'multifoo2', 'multibar2', 'multifoo3', 'multibar3'], helper.isString('OK')], + ['hmset', arr], + [['hmset', 'multihmset2', 'multibar2', 'multifoo3', 'multibar3', 'test'], helper.isString('OK')], + ['hmset', ['multihmset', 'multibar', 'multifoo'], helper.isString('OK')], + ['hmset', arr3, helper.isString('OK')], + ['hmset', now, {123456789: 'abcdefghij', 'some manner of key': 'a type of value', 'otherTypes': 555}], + ['hmset', 'key2', {'0123456789': 'abcdefghij', 'some manner of key': 'a type of value', 'otherTypes': 999}, helper.isString('OK')], + ['hmset', 'multihmset', ['multibar', 'multibaz'], undefined], // undefined is used as a explicit not set callback variable + ['hmset', 'multihmset', ['multibar', 'multibaz'], helper.isString('OK')] + ]) + .hmget(now, 123456789, 'otherTypes') + .hmget('key2', arr2, function noop () {}) + .hmget(['multihmset2', 'some manner of key', 'multibar3']) + .mget('multifoo2', ['multifoo3', 'multifoo'], function (err, res) { + assert.strictEqual(err, null) + assert(res[0], 'multifoo3') + assert(res[1], 'multifoo') + called = true + }) + .exec(function (err, replies) { + assert(called) + assert.strictEqual(arr.length, 3) + assert.strictEqual(arr2.length, 2) + assert.strictEqual(arr3.length, 3) + assert.strictEqual(arr4.length, 3) + assert.strictEqual(null, err) + assert.strictEqual(replies[10][1], '555') + assert.strictEqual(replies[11][0], 'a type of value') + assert.strictEqual(replies[12][0], null) + assert.strictEqual(replies[12][1], 'test') + assert.strictEqual(replies[13][0], 'multibar2') + assert.strictEqual(replies[13].length, 3) + assert.strictEqual(replies.length, 14) + return done() + }) + }) + + it('converts a non string key to a string', function (done) { + // TODO: Converting the key might change soon again. + client.multi().hmset(true, { + test: 123, + bar: 'baz' + }).exec(done) + }) + + it('runs a multi without any further commands', function (done) { + var buffering = client.multi().exec(function (err, res) { + assert.strictEqual(err, null) + assert.strictEqual(res.length, 0) + done() + }) + assert(typeof buffering === 'boolean') + }) + + it('allows multiple operations to be performed using a chaining API', function (done) { + client.multi() + .mset('some', '10', 'keys', '20') + .incr('some') + .incr('keys') + .mget('some', ['keys']) + .exec(helper.isDeepEqual(['OK', 11, 21, ['11', '21']], done)) + }) + + it('allows multiple commands to work the same as normal to be performed using a chaining API', function (done) { + client.multi() + .mset(['some', '10', 'keys', '20']) + .incr('some', helper.isNumber(11)) + .incr(['keys'], helper.isNumber(21)) + .mget('some', 'keys') + .exec(helper.isDeepEqual(['OK', 11, 21, ['11', '21']], done)) + }) + + it('allows multiple commands to work the same as normal to be performed using a chaining API promisified', function () { + return client.multi() + .mset(['some', '10', 'keys', '20']) + .incr('some', helper.isNumber(11)) + .incr(['keys'], helper.isNumber(21)) + .mget('some', 'keys') + .execAsync() + .then(function (replies) { + assert.strictEqual('OK', replies[0]) + assert.strictEqual(11, replies[1]) + assert.strictEqual(21, replies[2]) + assert.strictEqual('11', replies[3][0].toString()) + assert.strictEqual('21', replies[3][1].toString()) + }) + }) + + it('allows an array to be provided indicating multiple operations to perform', function (done) { + // test nested multi-bulk replies with nulls. + client.multi([ + ['mget', ['multifoo', 'some', 'random value', 'keys']], + ['incr', 'multifoo'] + ]).exec(helper.isDeepEqual([[null, null, null, null], 1], done)) + }) + + it('allows multiple operations to be performed on a hash', function (done) { + client.multi() + .hmset('multihash', 'a', 'foo', 'b', 1) + .hmset('multihash', { + extra: 'fancy', + things: 'here' + }) + .hgetall('multihash') + .exec(function (err, replies) { + assert.strictEqual(null, err) + assert.strictEqual('OK', replies[0]) + assert.strictEqual(Object.keys(replies[2]).length, 4) + assert.strictEqual('foo', replies[2].a) + assert.strictEqual('1', replies[2].b) + assert.strictEqual('fancy', replies[2].extra) + assert.strictEqual('here', replies[2].things) + return done() + }) + }) + + it('reports EXECABORT exceptions when they occur (while queueing)', function (done) { + client.multi().config('bar').set('foo').set('bar').exec(function (err, reply) { + assert.strictEqual(err.code, 'EXECABORT') + assert.strictEqual(reply, undefined, 'The reply should have been discarded') + assert(err.message.match(/^EXECABORT/), 'Error message should begin with EXECABORT') + assert.strictEqual(err.errors.length, 2, 'err.errors should have 2 items') + assert.strictEqual(err.errors[0].command, 'SET') + assert.strictEqual(err.errors[0].code, 'ERR') + assert.strictEqual(err.errors[0].position, 1) + assert(/^ERR/.test(err.errors[0].message), 'Actuall error message should begin with ERR') + return done() + }) + }) + + it('reports multiple exceptions when they occur (while EXEC is running)', function (done) { + client.multi().config('bar').debug('foo').eval('return {err=\'this is an error\'}', 0).exec(function (err, reply) { + assert.strictEqual(err, null) + assert.strictEqual(reply.length, 3) + assert.strictEqual(reply[0].code, 'ERR') + assert.strictEqual(reply[0].command, 'CONFIG') + assert.strictEqual(reply[2].code, undefined) + assert.strictEqual(reply[2].command, 'EVAL') + assert(/^this is an error/.test(reply[2].message)) + assert(/^ERR/.test(reply[0].message), 'Error message should begin with ERR') + assert(/^ERR/.test(reply[1].message), 'Error message should begin with ERR') + return done() + }) + }) + + it('reports multiple exceptions when they occur (while EXEC is running) promisified', function () { + return client.multi().config('bar').debug('foo').eval('return {err=\'this is an error\'}', 0).execAsync().then(function (reply) { + assert.strictEqual(reply.length, 3) + assert.strictEqual(reply[0].code, 'ERR') + assert.strictEqual(reply[0].command, 'CONFIG') + assert.strictEqual(reply[2].code, undefined) + assert.strictEqual(reply[2].command, 'EVAL') + assert(/^this is an error/.test(reply[2].message)) + assert(/^ERR/.test(reply[0].message), 'Error message should begin with ERR') + assert(/^ERR/.test(reply[1].message), 'Error message should begin with ERR') + }) + }) + + it('reports multiple exceptions when they occur (while EXEC is running) and calls cb', function (done) { + var multi = client.multi() + multi.config('bar', helper.isError()) + multi.set('foo', 'bar', helper.isString('OK')) + multi.debug('foo').exec(function (err, reply) { + assert.strictEqual(err, null) + assert.strictEqual(reply.length, 3) + assert.strictEqual(reply[0].code, 'ERR') + assert(/^ERR/.test(reply[0].message), 'Error message should begin with ERR') + assert(/^ERR/.test(reply[2].message), 'Error message should begin with ERR') + assert.strictEqual(reply[1], 'OK') + client.get('foo', helper.isString('bar', done)) + }) + }) + + it('emits an error if no callback has been provided and execabort error occured', function (done) { + var multi = client.multi() + multi.config('bar') + multi.set('foo') + multi.exec() + + client.on('error', function (err) { + assert.strictEqual(err.code, 'EXECABORT') + done() + }) + }) + + it('should work without any callback', function (done) { + var multi = client.multi() + multi.set('baz', 'binary') + multi.set('foo', 'bar') + multi.exec() + + client.get('foo', helper.isString('bar', done)) + }) + + it('should not use a transaction with execAtomic if no command is used', function () { + var multi = client.multi() + var test = false + multi.execBatch = function () { + test = true + } + multi.execAtomic() + assert(test) + }) + + it('should not use a transaction with execAtomic if only one command is used', function () { + var multi = client.multi() + var test = false + multi.execBatch = function () { + test = true + } + multi.set('baz', 'binary') + multi.execAtomic() + assert(test) + }) + + it('should use transaction with execAtomic and more than one command used', function (done) { + var multi = client.multi() + var test = false + multi.execBatch = function () { + test = true + } + multi.set('baz', 'binary') + multi.get('baz') + multi.execAtomic(done) + assert(!test) + }) + + it('do not mutate arguments in the multi constructor', function (done) { + var input = [['set', 'foo', 'bar'], ['get', 'foo']] + client.multi(input).exec(function (err, res) { + assert.strictEqual(err, null) + assert.strictEqual(input.length, 2) + assert.strictEqual(input[0].length, 3) + assert.strictEqual(input[1].length, 2) + done() + }) + }) + + it('works properly after a reconnect. issue #897', function (done) { + client.stream.destroy() + client.on('error', function (err) { + assert.strictEqual(err.code, 'ECONNREFUSED') + }) + client.on('ready', function () { + client.multi([['set', 'foo', 'bar'], ['get', 'foo']]).exec(function (err, res) { + assert(!err) + assert.strictEqual(res[1], 'bar') + done() + }) + }) + }) + + it('emits error once if reconnecting after multi has been executed but not yet returned without callback', function (done) { + // NOTE: If uncork is called async by postponing it to the next tick, this behavior is going to change. + // The command won't be processed anymore two errors are returned instead of one + client.on('error', function (err) { + assert.strictEqual(err.code, 'UNCERTAIN_STATE') + client.get('foo', helper.isString('bar', done)) + }) + + // The commands should still be fired, no matter that the socket is destroyed on the same tick + client.multi().set('foo', 'bar').get('foo').exec() + // Abort connection before the value returned + client.stream.destroy() + }) + + it('indivdual commands work properly with multi', function (done) { + // Neither of the following work properly in a transactions: + // (This is due to Redis not returning the reply as expected / resulting in undefined behavior) + // (Likely there are more commands that do not work with a transaction) + // + // auth => can't be called after a multi command + // monitor => results in faulty return values e.g. multi().monitor().set('foo', 'bar').get('foo') + // returns ['OK, 'OK', 'monitor reply'] instead of ['OK', 'OK', 'bar'] + // quit => ends the connection before the exec + // client reply skip|off => results in weird return values. Not sure 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 + client.sendCommand = function () { + throw new Error('failed') + } + + assert.strictEqual(client.selectedDb, undefined) + var multi = client.multi() + multi.select(5, function (err, res) { + assert.strictEqual(err, null) + assert.strictEqual(client.selectedDb, 5) + assert.strictEqual(res, 'OK') + assert.notDeepEqual(client.serverInfo.db5, { avg_ttl: 0, expires: 0, keys: 1 }) + }) + // multi.client('reply', 'on', helper.isString('OK')); // Redis v.3.2 + multi.set('foo', 'bar', helper.isString('OK')) + multi.info(function (err, res) { + assert.strictEqual(err, null) + assert.strictEqual(res.indexOf('# Server\r\nredis_version:'), 0) + assert.deepEqual(client.serverInfo.db5, { avg_ttl: 0, expires: 0, keys: 1 }) + }) + multi.get('foo', helper.isString('bar')) + multi.exec(function (err, res) { + assert.strictEqual(err, null) + res[2] = res[2].substr(0, 10) + assert.deepEqual(res, ['OK', 'OK', '# Server\r\n', 'bar']) + client.flushdb(done) + }) + }) + }) + }) + }) +}) diff --git a/test/node_redis.spec.js b/test/node_redis.spec.js index f808d62b7a..648616f396 100644 --- a/test/node_redis.spec.js +++ b/test/node_redis.spec.js @@ -1,971 +1,949 @@ -'use strict'; +'use strict' -var assert = require('assert'); -var fs = require('fs'); -var path = require('path'); -var intercept = require('intercept-stdout'); -var config = require('./lib/config'); -var helper = require('./helper'); -var fork = require('child_process').fork; -var redis = config.redis; -var client; +var Buffer = require('safe-buffer').Buffer +var assert = require('assert') +var fs = require('fs') +var path = require('path') +var intercept = require('intercept-stdout') +var config = require('./lib/config') +var helper = require('./helper') +var fork = require('child_process').fork +var redis = config.redis +var client describe('The nodeRedis client', function () { + it('individual commands sanity check', function (done) { + // All commands should work the same in multi context or without + // Therefor individual commands always have to be handled in both cases + fs.readFile(path.resolve(__dirname, '../lib/individualCommands.js'), 'utf8', function (err, data) { + assert.strictEqual(err, null) + var clientPrototype = data.match(/(\n| = )RedisClient\.prototype.[a-zA-Z_]+/g) + var multiPrototype = data.match(/(\n| = )Multi\.prototype\.[a-zA-Z_]+/g) + // Check that every entry RedisClient entry has a correspondent Multi entry + assert.strictEqual(clientPrototype.filter(function (entry) { + return multiPrototype.indexOf(entry.replace('RedisClient', 'Multi')) === -1 + }).length, 3) // multi and batch are included too + assert.strictEqual(clientPrototype.length, multiPrototype.length + 3) + // Check that all entries exist only in lowercase variants + assert.strictEqual(data.match(/(\n| = )RedisClient\.prototype.[a-zA-Z_]+/g).length, clientPrototype.length) + done() + }) + }) - it('individual commands sanity check', function (done) { - // All commands should work the same in multi context or without - // Therefor individual commands always have to be handled in both cases - fs.readFile(path.resolve(__dirname, '../lib/individualCommands.js'), 'utf8', function (err, data) { - var clientPrototype = data.match(/(\n| = )RedisClient\.prototype.[a-zA-Z_]+/g); - var multiPrototype = data.match(/(\n| = )Multi\.prototype\.[a-zA-Z_]+/g); - // Check that every entry RedisClient entry has a correspondent Multi entry - assert.strictEqual(clientPrototype.filter(function (entry) { - return multiPrototype.indexOf(entry.replace('RedisClient', 'Multi')) === -1; - }).length, 3); // multi and batch are included too - assert.strictEqual(clientPrototype.length, multiPrototype.length + 3); - // Check that all entries exist in uppercase and in lowercase variants - assert.strictEqual(data.match(/(\n| = )RedisClient\.prototype.[a-z_]+/g).length * 2, clientPrototype.length); - done(); - }); - }); + it('convert minus to underscore in Redis function names', function (done) { + var names = Object.keys(redis.RedisClient.prototype) + client = redis.createClient() + for (var i = 0; i < names.length; i++) { + assert(/^([a-zA-Z_][a-zA-Z_0-9]*)?$/.test(client[names[i]].name)) + } + client.quit(done) + }) - it('convert minus to underscore in Redis function names', function (done) { - var names = Object.keys(redis.RedisClient.prototype); - client = redis.createClient(); - for (var i = 0; i < names.length; i++) { - assert(/^([a-zA-Z_][a-zA-Z_0-9]*)?$/.test(client[names[i]].name)); - } - client.quit(done); - }); + it('reset the parser while reconnecting (See #1190)', function (done) { + var client = redis.createClient({ + retryStrategy: function () { + return 5 + } + }) + client.once('reconnecting', function () { + process.nextTick(function () { + assert.strictEqual(client.replyParser.buffer, null) + done() + }) + }) + var partialInput = Buffer.from('$100\r\nabcdef') + client.replyParser.execute(partialInput) + assert.strictEqual(client.replyParser.buffer.inspect(), partialInput.inspect()) + client.stream.destroy() + }) - it('reset the parser while reconnecting (See #1190)', function (done) { - var client = redis.createClient({ - retryStrategy: function () { - return 5; + helper.allTests(function (ip, args) { + describe('using ' + ip, function () { + afterEach(function () { + client.end(true) + }) + + describe('when connected', function () { + beforeEach(function (done) { + client = redis.createClient.apply(null, args) + client.once('connect', function () { + client.flushdb(done) + }) + }) + + describe('duplicate', function () { + it('check if all options got copied properly', function (done) { + client.selectedDb = 2 + var client2 = client.duplicate() + assert.strictEqual(client.connectionId + 1, client2.connectionId) + assert.strictEqual(client2.selectedDb, 2) + assert(client.connected) + assert(!client2.connected) + for (var elem in client.options) { + if (client.options.hasOwnProperty(elem)) { + assert.strictEqual(client2.options[elem], client.options[elem]) + } } - }); - client.once('reconnecting', function () { + client2.on('error', function (err) { + assert.strictEqual(err.message, 'Connection forcefully ended and command aborted. It might have been processed.') + assert.strictEqual(err.command, 'SELECT') + assert(err instanceof Error) + assert.strictEqual(err.name, 'AbortError') + }) + client2.on('ready', function () { + client2.end(true) + done() + }) + }) + + it('check if all new options replaced the old ones', function (done) { + var client2 = client.duplicate({ + noReadyCheck: true + }) + assert(client.connected) + assert(!client2.connected) + assert.strictEqual(client.options.noReadyCheck, undefined) + assert.strictEqual(client2.options.noReadyCheck, true) + assert.notDeepEqual(client.options, client2.options) + for (var elem in client.options) { + if (client.options.hasOwnProperty(elem)) { + if (elem !== 'noReadyCheck') { + assert.strictEqual(client2.options[elem], client.options[elem]) + } + } + } + client2.on('ready', function () { + client2.end(true) + done() + }) + }) + + it('works with a callback', function (done) { + client.duplicate(function (err, client) { + assert(!err) + assert.strictEqual(client.ready, true) + client.quit(done) + }) + }) + + it('works with a callback and errors out', function (done) { + client.duplicate({ + port: '9999' + }, function (err, client) { + assert.strictEqual(err.code, 'ECONNREFUSED') + done(client) + }) + }) + + it('works with a promises', function () { + return client.duplicateAsync().then(function (client) { + assert.strictEqual(client.ready, true) + return client.quitAsync() + }) + }) + + it('works with a promises and errors', function () { + return client.duplicateAsync({ + port: 9999 + }).catch(function (err) { + assert.strictEqual(err.code, 'ECONNREFUSED') + }) + }) + }) + + describe('big data', function () { + // Check if the fast mode for big strings is working correct + it('safe strings that are bigger than 30000 characters', function (done) { + var str = 'foo ಠ_ಠ bar ' + while (str.length < 111111) { + str += str + } + client.set('foo', str) + client.get('foo', helper.isString(str, done)) + }) + + it('safe strings that are bigger than 30000 characters with multi', function (done) { + var str = 'foo ಠ_ಠ bar ' + while (str.length < 111111) { + str += str + } + var called = false + var temp = client.writeBuffers.bind(client) + assert(client.fireStrings) + client.writeBuffers = function (data) { + called = true + // To increase write performance for strings the value is converted to a buffer + assert(!client.fireStrings) + temp(data) + } + client.multi().set('foo', str).get('foo', helper.isString(str)).exec(function (err, res) { + assert.strictEqual(err, null) + assert.strictEqual(called, true) + assert.strictEqual(res[1], str) + done() + }) + assert(client.fireStrings) + }) + }) + + describe('sendCommand', function () { + it('omitting args should be fine', function (done) { + client.serverInfo = {} + client.sendCommand('info') + client.sendCommand('ping', function (err, res) { + assert.strictEqual(err, null) + assert.strictEqual(res, 'PONG') + // Check if the previous info command used the internal individual info command + assert.notDeepEqual(client.serverInfo, {}) + client.serverInfo = {} + }) + client.sendCommand('info', null, undefined) + client.sendCommand('ping', null, function (err, res) { + assert.strictEqual(err, null) + assert.strictEqual(res, 'PONG') + // Check if the previous info command used the internal individual info command + assert.notDeepEqual(client.serverInfo, {}) + client.serverInfo = {} + }) + client.sendCommand('info', undefined, undefined) + client.sendCommand('ping', function (err, res) { + assert.strictEqual(err, null) + assert.strictEqual(res, 'PONG') + // Check if the previous info command used the internal individual info command + assert.notDeepEqual(client.serverInfo, {}) + client.serverInfo = {} + }) + client.sendCommand('info', undefined, function (err, res) { + assert.strictEqual(err, null) + assert(/redis_version/.test(res)) + // The individual info command should also be called by using sendCommand + assert.notDeepEqual(client.serverInfo, {}) + done() + }) + }) + + it('using multi with sendCommand should work as individual command instead of using the internal multi', function (done) { + // This is necessary to keep backwards compatibility and it is the only way to handle multis as you want in nodeRedis + client.sendCommand('multi') + client.sendCommand('set', ['foo', 'bar'], helper.isString('QUEUED')) + client.get('foo') + // exec is not manipulated if not fired by the individual multi command + // As the multi command is handled individually by the user he also has to handle the return value + client.exec(helper.isDeepEqual(['OK', 'bar'], done)) + }) + + it('multi should be handled special', function (done) { + client.sendCommand('multi', undefined, helper.isString('OK')) + var args = ['test', 'bla'] + client.sendCommand('set', args, helper.isString('QUEUED')) + assert.deepEqual(args, ['test', 'bla']) // Check args manipulation + client.get('test', helper.isString('QUEUED')) + // As the multi command is handled individually by the user he also has to handle the return value + client.exec(helper.isDeepEqual(['OK', 'bla'], done)) + }) + + it('using another type as cb should throw', function () { + try { + client.sendCommand('set', ['test', 'bla'], [true]) + throw new Error('failed') + } catch (err) { + assert.strictEqual(err.message, 'Wrong input type "Array" for callback function') + } + try { + client.sendCommand('set', ['test', 'bla'], null) + throw new Error('failed') + } catch (err) { + assert.strictEqual(err.message, 'Wrong input type "null" for callback function') + } + }) + + it('command argument has to be of type string', function () { + try { + client.sendCommand(true, ['test', 'bla'], function () {}) + throw new Error('failed') + } catch (err) { + assert.strictEqual(err.message, 'Wrong input type "Boolean" for command name') + } + try { + client.sendCommand(undefined, ['test', 'bla'], function () {}) + throw new Error('failed') + } catch (err) { + assert.strictEqual(err.message, 'Wrong input type "undefined" for command name') + } + try { + client.sendCommand(null, ['test', 'bla'], function () {}) + throw new Error('failed') + } catch (err) { + assert.strictEqual(err.message, 'Wrong input type "null" for command name') + } + }) + + it('args may only be of type Array or undefined', function () { + try { + client.sendCommand('info', 123) + throw new Error('failed') + } catch (err) { + assert.strictEqual(err.message, 'Wrong input type "Number" for args') + } + }) + + it('passing a callback as args and as callback should throw', function () { + try { + client.sendCommand('info', function a () {}, function b () {}) + throw new Error('failed') + } catch (err) { + assert.strictEqual(err.message, 'Wrong input type "Function" for args') + } + }) + + it('multi should be handled special', function (done) { + client.sendCommand('multi', undefined, helper.isString('OK')) + var args = ['test', 'bla'] + client.sendCommand('set', args, helper.isString('QUEUED')) + assert.deepEqual(args, ['test', 'bla']) // Check args manipulation + client.get('test', helper.isString('QUEUED')) + // As the multi command is handled individually by the user he also has to handle the return value + client.exec(helper.isDeepEqual(['OK', 'bla'], done)) + }) + + it('the args array may contain a arbitrary number of arguments', function (done) { + client.sendCommand('mset', ['foo', 1, 'bar', 2, 'baz', 3], helper.isString('OK')) + // As the multi command is handled individually by the user he also has to handle the return value + client.mget(['foo', 'bar', 'baz'], helper.isDeepEqual(['1', '2', '3'], done)) + }) + + it('sendCommand with callback as args', function (done) { + client.sendCommand('abcdef', function (err, res) { + assert.strictEqual(err.message, 'ERR unknown command \'abcdef\'') + done() + }) + }) + }) + + describe('retryUnfulfilledCommands', function () { + it('should retry all commands instead of returning an error if a command did not yet return after a connection loss', function (done) { + var bclient = redis.createClient({ + retryUnfulfilledCommands: true + }) + bclient.blpop('blocking list 2', 5, function (err, value) { + assert.strictEqual(value[0], 'blocking list 2') + assert.strictEqual(value[1], 'initial value') + bclient.end(true) + done(err) + }) + bclient.once('ready', function () { + setTimeout(function () { + bclient.stream.destroy() + client.rpush('blocking list 2', 'initial value', helper.isNumber(1)) + }, 100) + }) + }) + + it('should retry all commands even if the offline queue is disabled', function (done) { + var bclient = redis.createClient({ + enableOfflineQueue: false, + retryUnfulfilledCommands: true + }) + bclient.once('ready', function () { + bclient.blpop('blocking list 2', 5, function (err, value) { + assert.strictEqual(value[0], 'blocking list 2') + assert.strictEqual(value[1], 'initial value') + bclient.end(true) + done(err) + }) + setTimeout(function () { + bclient.stream.destroy() + client.rpush('blocking list 2', 'initial value', helper.isNumber(1)) + }, 100) + }) + }) + }) + + describe('.end', function () { + it('used without flush / flush set to false', function (done) { + var finished = false + var end = helper.callFuncAfter(function () { + if (!finished) { + done(new Error('failed')) + } + }, 20) + var cb = function (err, res) { + assert(/Connection forcefully ended|The connection is already closed./.test(err.message)) + assert.strictEqual(err.code, 'NR_CLOSED') + end() + } + for (var i = 0; i < 20; i++) { + if (i === 10) { + client.end() + } + client.set('foo', 'bar', cb) + } + client.on('warning', function () {}) // Ignore deprecation message + setTimeout(function () { + finished = true + done() + }, 25) + }) + + it('used with flush set to true', function (done) { + var end = helper.callFuncAfter(function () { + done() + }, 20) + var cb = function (err, res) { + assert(/Connection forcefully ended|The connection is already closed./.test(err.message)) + end() + } + for (var i = 0; i < 20; i++) { + if (i === 10) { + client.end(true) + client.stream.write('foo') // Trigger an error on the closed stream that we ignore + } + client.set('foo', 'bar', cb) + } + }) + + it('emits an aggregate error if no callback was present for multiple commands in debugMode', function (done) { + redis.debugMode = true + var unhookIntercept = intercept(function (data) { + return '' // Don't print the debug messages + }) + client.set('foo', 'bar') + client.set('baz', 'hello world') + client.on('error', function (err) { + assert(err instanceof Error) + assert(err instanceof redis.AbortError) + assert(err instanceof redis.AggregateError) + assert.strictEqual(err.name, 'AggregateError') + assert.strictEqual(err.errors.length, 2) + assert.strictEqual(err.message, 'Connection forcefully ended and commands aborted.') + assert.strictEqual(err.code, 'NR_CLOSED') + assert.strictEqual(err.errors[0].message, 'Connection forcefully ended and command aborted. It might have been processed.') + assert.strictEqual(err.errors[0].command, 'SET') + assert.strictEqual(err.errors[0].code, 'NR_CLOSED') + assert.deepEqual(err.errors[0].args, ['foo', 'bar']) + done() + }) + client.end(true) + unhookIntercept() + redis.debugMode = false + }) + + it('emits an abort error if no callback was present for a single commands', function (done) { + redis.debugMode = true + var unhookIntercept = intercept(function (data) { + return '' // Don't print the debug messages + }) + client.set('foo', 'bar') + client.on('error', function (err) { + assert(err instanceof Error) + assert(err instanceof redis.AbortError) + assert(!(err instanceof redis.AggregateError)) + assert.strictEqual(err.message, 'Connection forcefully ended and command aborted. It might have been processed.') + assert.strictEqual(err.command, 'SET') + assert.strictEqual(err.code, 'NR_CLOSED') + assert.deepEqual(err.args, ['foo', 'bar']) + done() + }) + client.end(true) + unhookIntercept() + redis.debugMode = false + }) + + it('does not emit abort errors if no callback was present while not being in debugMode ', function (done) { + client.set('foo', 'bar') + client.end(true) + setTimeout(done, 100) + }) + }) + + describe('commands after using .quit should fail', function () { + it('return an error in the callback', function (done) { + if (helper.redisProcess().spawnFailed()) this.skip() + + // TODO: Investigate why this test is failing hard and killing mocha if using '/tmp/redis.sock'. + // Seems like something is wrong with nyc while passing a socket connection to create client! + client = redis.createClient() + client.quit(function () { + client.get('foo', function (err, res) { + assert.strictEqual(err.message, 'Stream connection ended and command aborted. It might have been processed.') + assert.strictEqual(client.offlineQueue.length, 0) + done() + }) + }) + }) + + it('return an error in the callback version two', function (done) { + if (helper.redisProcess().spawnFailed()) this.skip() + + client.quit() + setTimeout(function () { + client.get('foo', function (err, res) { + assert.strictEqual(err.message, 'GET can\'t be processed. The connection is already closed.') + assert.strictEqual(err.command, 'GET') + assert.strictEqual(client.offlineQueue.length, 0) + done() + }) + }, 50) + }) + + it('emit an error', function (done) { + if (helper.redisProcess().spawnFailed()) this.skip() + client.quit() + client.on('error', function (err) { + assert.strictEqual(err.message, 'SET can\'t be processed. The connection is already closed.') + assert.strictEqual(err.command, 'SET') + assert.strictEqual(client.offlineQueue.length, 0) + done() + }) + setTimeout(function () { + client.set('foo', 'bar') + }, 50) + }) + }) + + describe('when redis closes unexpectedly', function () { + it('reconnects and can retrieve the pre-existing data', function (done) { + client.on('reconnecting', function onRecon (params) { + client.on('connect', function onConnect () { + var end = helper.callFuncAfter(function () { + client.removeListener('connect', onConnect) + client.removeListener('reconnecting', onRecon) + assert.strictEqual(client.serverInfo.db0.keys, 2) + assert.strictEqual(Object.keys(client.serverInfo.db0).length, 3) + done() + }, 4) + client.get('recon 1', helper.isString('one', end)) + client.get('recon 1', helper.isString('one', end)) + client.get('recon 2', helper.isString('two', end)) + client.get('recon 2', helper.isString('two', end)) + }) + }) + + client.set('recon 1', 'one') + client.set('recon 2', 'two', function (err, res) { + assert.strictEqual(err, null) + // Do not do this in normal programs. This is to simulate the server closing on us. + // For orderly shutdown in normal programs, do client.quit() + client.stream.destroy() + }) + }) + + it('reconnects properly when monitoring', function (done) { + client.on('reconnecting', function onRecon (params) { + client.on('ready', function onReady () { + assert.strictEqual(client.monitoring, true, 'monitoring after reconnect') + client.removeListener('ready', onReady) + client.removeListener('reconnecting', onRecon) + done() + }) + }) + + assert.strictEqual(client.monitoring, false, 'monitoring off at start') + client.set('recon 1', 'one') + client.monitor(function (err, res) { + assert.strictEqual(err, null) + assert.strictEqual(client.monitoring, true, 'monitoring on after monitor()') + client.set('recon 2', 'two', function (err, res) { + assert.strictEqual(err, null) + // Do not do this in normal programs. This is to simulate the server closing on us. + // For orderly shutdown in normal programs, do client.quit() + client.stream.destroy() + }) + }) + }) + + describe('and it\'s subscribed to a channel', function () { + // "Connection in subscriber mode, only subscriber commands may be used" + it('reconnects, unsubscribes, and can retrieve the pre-existing data', function (done) { + client.on('ready', function onConnect () { + client.unsubscribe(helper.isNotError()) + + client.on('unsubscribe', function (channel, count) { + // we should now be out of subscriber mode. + assert.strictEqual(channel, 'recon channel') + assert.strictEqual(count, 0) + client.set('foo', 'bar', helper.isString('OK', done)) + }) + }) + + client.set('recon 1', 'one') + client.subscribe('recon channel', function (err, res) { + assert.strictEqual(err, null) + // Do not do this in normal programs. This is to simulate the server closing on us. + // For orderly shutdown in normal programs, do client.quit() + client.stream.destroy() + }) + }) + + it('reconnects, unsubscribes, and can retrieve the pre-existing data of a explicit channel', function (done) { + client.on('ready', function onConnect () { + client.unsubscribe('recon channel', helper.isNotError()) + + client.on('unsubscribe', function (channel, count) { + // we should now be out of subscriber mode. + assert.strictEqual(channel, 'recon channel') + assert.strictEqual(count, 0) + client.set('foo', 'bar', helper.isString('OK', done)) + }) + }) + + client.set('recon 1', 'one') + client.subscribe('recon channel', function (err, res) { + assert.strictEqual(err, null) + // Do not do this in normal programs. This is to simulate the server closing on us. + // For orderly shutdown in normal programs, do client.quit() + client.stream.destroy() + }) + }) + }) + + describe('domain', function () { + it('allows client to be executed from within domain', function (done) { + // eslint-disable-next-line + var domain = require('domain').create() + + domain.run(function () { + client.set('domain', 'value', function (err, res) { + assert.strictEqual(err, null) + assert.ok(process.domain) + throw new Error('ohhhh noooo') + }) + }) + + // this is the expected and desired behavior + domain.on('error', function (err) { + assert.strictEqual(err.message, 'ohhhh noooo') + domain.exit() + done() + }) + }) + + it('keeps the same domain by using the offline queue', function (done) { + client.end(true) + client = redis.createClient() + // eslint-disable-next-line + var testDomain = require('domain').create() + testDomain.run(function () { + client.set('FOOBAR', 'def', function () { + assert.strictEqual(process.domain, testDomain) + done() + }) + }) + // eslint-disable-next-line + require('domain').create() + }) + + it('catches all errors from within the domain', function (done) { + // eslint-disable-next-line + var domain = require('domain').create() + + domain.run(function () { + // Trigger an error within the domain + client.end(true) + client.set('domain', 'value') + }) + + domain.on('error', function (err) { + assert.strictEqual(err.message, 'SET can\'t be processed. The connection is already closed.') + domain.exit() + done() + }) + }) + }) + }) + + describe('utf8', function () { + it('handles utf-8 keys', function (done) { + var utf8Sample = 'ಠ_ಠ' + client.set(['utf8test', utf8Sample], helper.isString('OK')) + client.get(['utf8test'], function (err, obj) { + assert.strictEqual(utf8Sample, obj) + done(err) + }) + }) + }) + }) + + describe('unref', function () { + it('exits subprocess as soon as final command is processed', function (done) { + this.timeout(12000) + var args = config.HOST[ip] ? [config.HOST[ip], config.PORT] : [ip] + var external = fork('./test/lib/unref.js', args) + + var id = setTimeout(function () { + external.kill() + done(new Error('unref subprocess timed out')) + }, 8000) + + external.on('close', function (code) { + clearTimeout(id) + assert.strictEqual(code, 0) + done() + }) + }) + }) + + describe('execution order / fire query while loading', function () { + it('keep execution order for commands that may fire while redis is still loading', function (done) { + client = redis.createClient.apply(null, args) + var fired = false + client.set('foo', 'bar', function (err, res) { + assert.strictEqual(err, null) + assert.strictEqual(fired, false) + done() + }) + client.info(function (err, res) { + assert.strictEqual(err, null) + fired = true + }) + }) + + // TODO: consider allowing loading commands in v.3 + // it('should fire early', function (done) { + // client = redis.createClient.apply(null, args); + // var fired = false; + // client.info(function (err, res) { + // fired = true; + // }); + // client.set('foo', 'bar', function (err, res) { + // assert(fired); + // done(); + // }); + // assert.strictEqual(client.offlineQueue.length, 1); + // assert.strictEqual(client.commandQueue.length, 1); + // client.on('connect', function () { + // assert.strictEqual(client.offlineQueue.length, 1); + // assert.strictEqual(client.commandQueue.length, 1); + // }); + // client.on('ready', function () { + // assert.strictEqual(client.offlineQueue.length, 0); + // }); + // }); + }) + + describe('protocol error', function () { + it('should gracefully recover and only fail on the already send commands', function (done) { + client = redis.createClient.apply(null, args) + var error + client.on('error', function (err) { + assert.strictEqual(err.message, 'Protocol error, got "a" as reply type byte. Please report this.') + assert.strictEqual(err, error) + assert(err instanceof redis.ParserError) + // After the hard failure work properly again. The set should have been processed properly too + client.get('foo', helper.isString('bar', done)) + }) + client.once('ready', function () { + client.set('foo', 'bar', function (err, res) { + assert.strictEqual(err.message, 'Fatal error encountered. Command aborted. It might have been processed.') + assert.strictEqual(err.code, 'NR_FATAL') + assert(err instanceof redis.AbortError) + error = err.origin + }) + // Make sure we call execute out of the reply + // ready is called in a reply process.nextTick(function () { - assert.strictEqual(client.replyParser.buffer, null); - done(); - }); - }); - var partialInput = new Buffer('$100\r\nabcdef'); - client.replyParser.execute(partialInput); - assert.strictEqual(client.replyParser.buffer.inspect(), partialInput.inspect()); - client.stream.destroy(); - }); - - helper.allTests(function (ip, args) { - - describe('using ' + ip, function () { - - afterEach(function () { - client.end(true); - }); - - describe('when connected', function () { - beforeEach(function (done) { - client = redis.createClient.apply(null, args); - client.once('connect', function () { - client.flushdb(done); - }); - }); - - describe('duplicate', function () { - it('check if all options got copied properly', function (done) { - client.selectedDb = 2; - var client2 = client.duplicate(); - assert.strictEqual(client.connectionId + 1, client2.connectionId); - assert.strictEqual(client2.selectedDb, 2); - assert(client.connected); - assert(!client2.connected); - for (var elem in client.options) { - if (client.options.hasOwnProperty(elem)) { - assert.strictEqual(client2.options[elem], client.options[elem]); - } - } - client2.on('error', function (err) { - assert.strictEqual(err.message, 'Connection forcefully ended and command aborted. It might have been processed.'); - assert.strictEqual(err.command, 'SELECT'); - assert(err instanceof Error); - assert.strictEqual(err.name, 'AbortError'); - }); - client2.on('ready', function () { - client2.end(true); - done(); - }); - }); - - it('check if all new options replaced the old ones', function (done) { - var client2 = client.duplicate({ - noReadyCheck: true - }); - assert(client.connected); - assert(!client2.connected); - assert.strictEqual(client.options.noReadyCheck, undefined); - assert.strictEqual(client2.options.noReadyCheck, true); - assert.notDeepEqual(client.options, client2.options); - for (var elem in client.options) { - if (client.options.hasOwnProperty(elem)) { - if (elem !== 'noReadyCheck') { - assert.strictEqual(client2.options[elem], client.options[elem]); - } - } - } - client2.on('ready', function () { - client2.end(true); - done(); - }); - }); - - it('works with a callback', function (done) { - client.duplicate(function (err, client) { - assert(!err); - assert.strictEqual(client.ready, true); - client.quit(done); - }); - }); - - it('works with a callback and errors out', function (done) { - client.duplicate({ - port: '9999' - }, function (err, client) { - assert.strictEqual(err.code, 'ECONNREFUSED'); - done(client); - }); - }); - - it('works with a promises', function () { - return client.duplicateAsync().then(function (client) { - assert.strictEqual(client.ready, true); - return client.quitAsync(); - }); - }); - - it('works with a promises and errors', function () { - return client.duplicateAsync({ - port: 9999 - }).catch(function (err) { - assert.strictEqual(err.code, 'ECONNREFUSED'); - }); - }); - }); - - describe('big data', function () { - - // Check if the fast mode for big strings is working correct - it('safe strings that are bigger than 30000 characters', function (done) { - var str = 'foo ಠ_ಠ bar '; - while (str.length < 111111) { - str += str; - } - client.set('foo', str); - client.get('foo', function (err, res) { - assert.strictEqual(res, str); - done(); - }); - }); - - it('safe strings that are bigger than 30000 characters with multi', function (done) { - var str = 'foo ಠ_ಠ bar '; - while (str.length < 111111) { - str += str; - } - var called = false; - var temp = client.writeBuffers.bind(client); - assert(client.fireStrings); - client.writeBuffers = function (data) { - called = true; - // To increase write performance for strings the value is converted to a buffer - assert(!client.fireStrings); - temp(data); - }; - client.multi().set('foo', str).get('foo', function (err, res) { - assert.strictEqual(res, str); - }).exec(function (err, res) { - assert(called); - assert.strictEqual(res[1], str); - done(); - }); - assert(client.fireStrings); - }); - }); - - describe('sendCommand', function () { - - it('omitting args should be fine', function (done) { - client.serverInfo = {}; - client.sendCommand('info'); - client.sendCommand('ping', function (err, res) { - assert.strictEqual(res, 'PONG'); - // Check if the previous info command used the internal individual info command - assert.notDeepEqual(client.serverInfo, {}); - client.serverInfo = {}; - }); - client.sendCommand('info', null, undefined); - client.sendCommand('ping', null, function (err, res) { - assert.strictEqual(res, 'PONG'); - // Check if the previous info command used the internal individual info command - assert.notDeepEqual(client.serverInfo, {}); - client.serverInfo = {}; - }); - client.sendCommand('info', undefined, undefined); - client.sendCommand('ping', function (err, res) { - assert.strictEqual(res, 'PONG'); - // Check if the previous info command used the internal individual info command - assert.notDeepEqual(client.serverInfo, {}); - client.serverInfo = {}; - }); - client.sendCommand('info', undefined, function (err, res) { - assert(/redis_version/.test(res)); - // The individual info command should also be called by using sendCommand - // console.log(info, client.serverInfo); - assert.notDeepEqual(client.serverInfo, {}); - done(); - }); - }); - - it('using multi with sendCommand should work as individual command instead of using the internal multi', function (done) { - // This is necessary to keep backwards compatibility and it is the only way to handle multis as you want in nodeRedis - client.sendCommand('multi'); - client.sendCommand('set', ['foo', 'bar'], helper.isString('QUEUED')); - client.get('foo'); - client.exec(function (err, res) { // exec is not manipulated if not fired by the individual multi command - // As the multi command is handled individually by the user he also has to handle the return value - assert.strictEqual(res[0].toString(), 'OK'); - assert.strictEqual(res[1].toString(), 'bar'); - done(); - }); - }); - - it('multi should be handled special', function (done) { - client.sendCommand('multi', undefined, helper.isString('OK')); - var args = ['test', 'bla']; - client.sendCommand('set', args, helper.isString('QUEUED')); - assert.deepEqual(args, ['test', 'bla']); // Check args manipulation - client.get('test', helper.isString('QUEUED')); - client.exec(function (err, res) { - // As the multi command is handled individually by the user he also has to handle the return value - assert.strictEqual(res[0].toString(), 'OK'); - assert.strictEqual(res[1].toString(), 'bla'); - done(); - }); - }); - - it('using another type as cb should throw', function () { - try { - client.sendCommand('set', ['test', 'bla'], [true]); - throw new Error('failed'); - } catch (err) { - assert.strictEqual(err.message, 'Wrong input type "Array" for callback function'); - } - try { - client.sendCommand('set', ['test', 'bla'], null); - throw new Error('failed'); - } catch (err) { - assert.strictEqual(err.message, 'Wrong input type "null" for callback function'); - } - }); - - it('command argument has to be of type string', function () { - try { - client.sendCommand(true, ['test', 'bla'], function () {}); - throw new Error('failed'); - } catch (err) { - assert.strictEqual(err.message, 'Wrong input type "Boolean" for command name'); - } - try { - client.sendCommand(undefined, ['test', 'bla'], function () {}); - throw new Error('failed'); - } catch (err) { - assert.strictEqual(err.message, 'Wrong input type "undefined" for command name'); - } - try { - client.sendCommand(null, ['test', 'bla'], function () {}); - throw new Error('failed'); - } catch (err) { - assert.strictEqual(err.message, 'Wrong input type "null" for command name'); - } - }); - - it('args may only be of type Array or undefined', function () { - try { - client.sendCommand('info', 123); - throw new Error('failed'); - } catch (err) { - assert.strictEqual(err.message, 'Wrong input type "Number" for args'); - } - }); - - it('passing a callback as args and as callback should throw', function () { - try { - client.sendCommand('info', function a () {}, function b () {}); - throw new Error('failed'); - } catch (err) { - assert.strictEqual(err.message, 'Wrong input type "Function" for args'); - } - }); - - it('multi should be handled special', function (done) { - client.sendCommand('multi', undefined, helper.isString('OK')); - var args = ['test', 'bla']; - client.sendCommand('set', args, helper.isString('QUEUED')); - assert.deepEqual(args, ['test', 'bla']); // Check args manipulation - client.get('test', helper.isString('QUEUED')); - client.exec(function (err, res) { - // As the multi command is handled individually by the user he also has to handle the return value - assert.strictEqual(res[0].toString(), 'OK'); - assert.strictEqual(res[1].toString(), 'bla'); - done(); - }); - }); - - it('the args array may contain a arbitrary number of arguments', function (done) { - client.sendCommand('mset', ['foo', 1, 'bar', 2, 'baz', 3], helper.isString('OK')); - client.mget(['foo', 'bar', 'baz'], function (err, res) { - // As the multi command is handled individually by the user he also has to handle the return value - assert.strictEqual(res[0].toString(), '1'); - assert.strictEqual(res[1].toString(), '2'); - assert.strictEqual(res[2].toString(), '3'); - done(); - }); - }); - - it('sendCommand with callback as args', function (done) { - client.sendCommand('abcdef', function (err, res) { - assert.strictEqual(err.message, "ERR unknown command 'abcdef'"); - done(); - }); - }); - - }); - - describe('retryUnfulfilledCommands', function () { - - it('should retry all commands instead of returning an error if a command did not yet return after a connection loss', function (done) { - var bclient = redis.createClient({ - retryUnfulfilledCommands: true - }); - bclient.blpop('blocking list 2', 5, function (err, value) { - assert.strictEqual(value[0], 'blocking list 2'); - assert.strictEqual(value[1], 'initial value'); - bclient.end(true); - done(err); - }); - bclient.once('ready', function () { - setTimeout(function () { - bclient.stream.destroy(); - client.rpush('blocking list 2', 'initial value', helper.isNumber(1)); - }, 100); - }); - }); - - it('should retry all commands even if the offline queue is disabled', function (done) { - var bclient = redis.createClient({ - enableOfflineQueue: false, - retryUnfulfilledCommands: true - }); - bclient.once('ready', function () { - bclient.blpop('blocking list 2', 5, function (err, value) { - assert.strictEqual(value[0], 'blocking list 2'); - assert.strictEqual(value[1], 'initial value'); - bclient.end(true); - done(err); - }); - setTimeout(function () { - bclient.stream.destroy(); - client.rpush('blocking list 2', 'initial value', helper.isNumber(1)); - }, 100); - }); - }); - - }); - - describe('.end', function () { - - it('used without flush / flush set to false', function (done) { - var finished = false; - var end = helper.callFuncAfter(function () { - if (!finished) { - done(new Error('failed')); - } - }, 20); - var cb = function (err, res) { - assert(/Connection forcefully ended|The connection is already closed./.test(err.message)); - assert.strictEqual(err.code, 'NR_CLOSED'); - end(); - }; - for (var i = 0; i < 20; i++) { - if (i === 10) { - client.end(); - } - client.set('foo', 'bar', cb); - } - client.on('warning', function () {}); // Ignore deprecation message - setTimeout(function () { - finished = true; - done(); - }, 25); - }); - - it('used with flush set to true', function (done) { - var end = helper.callFuncAfter(function () { - done(); - }, 20); - var cb = function (err, res) { - assert(/Connection forcefully ended|The connection is already closed./.test(err.message)); - end(); - }; - for (var i = 0; i < 20; i++) { - if (i === 10) { - client.end(true); - client.stream.write('foo'); // Trigger an error on the closed stream that we ignore - } - client.set('foo', 'bar', cb); - } - }); - - it('emits an aggregate error if no callback was present for multiple commands in debugMode', function (done) { - redis.debugMode = true; - var unhookIntercept = intercept(function (data) { - return ''; // Don't print the debug messages - }); - client.set('foo', 'bar'); - client.set('baz', 'hello world'); - client.on('error', function (err) { - assert(err instanceof Error); - assert(err instanceof redis.AbortError); - assert(err instanceof redis.AggregateError); - assert.strictEqual(err.name, 'AggregateError'); - assert.strictEqual(err.errors.length, 2); - assert.strictEqual(err.message, 'Connection forcefully ended and commands aborted.'); - assert.strictEqual(err.code, 'NR_CLOSED'); - assert.strictEqual(err.errors[0].message, 'Connection forcefully ended and command aborted. It might have been processed.'); - assert.strictEqual(err.errors[0].command, 'SET'); - assert.strictEqual(err.errors[0].code, 'NR_CLOSED'); - assert.deepEqual(err.errors[0].args, ['foo', 'bar']); - done(); - }); - client.end(true); - unhookIntercept(); - redis.debugMode = false; - }); - - it('emits an abort error if no callback was present for a single commands', function (done) { - redis.debugMode = true; - var unhookIntercept = intercept(function (data) { - return ''; // Don't print the debug messages - }); - client.set('foo', 'bar'); - client.on('error', function (err) { - assert(err instanceof Error); - assert(err instanceof redis.AbortError); - assert(!(err instanceof redis.AggregateError)); - assert.strictEqual(err.message, 'Connection forcefully ended and command aborted. It might have been processed.'); - assert.strictEqual(err.command, 'SET'); - assert.strictEqual(err.code, 'NR_CLOSED'); - assert.deepEqual(err.args, ['foo', 'bar']); - done(); - }); - client.end(true); - unhookIntercept(); - redis.debugMode = false; - }); - - it('does not emit abort errors if no callback was present while not being in debugMode ', function (done) { - client.set('foo', 'bar'); - client.end(true); - setTimeout(done, 100); - }); - - }); - - describe('commands after using .quit should fail', function () { - - it('return an error in the callback', function (done) { - if (helper.redisProcess().spawnFailed()) this.skip(); - - // TODO: Investigate why this test is failing hard and killing mocha if using '/tmp/redis.sock'. - // Seems like something is wrong with nyc while passing a socket connection to create client! - client = redis.createClient(); - client.quit(function () { - client.get('foo', function (err, res) { - assert.strictEqual(err.message, 'Stream connection ended and command aborted. It might have been processed.'); - assert.strictEqual(client.offlineQueue.length, 0); - done(); - }); - }); - }); - - it('return an error in the callback version two', function (done) { - if (helper.redisProcess().spawnFailed()) this.skip(); - - client.quit(); - setTimeout(function () { - client.get('foo', function (err, res) { - assert.strictEqual(err.message, 'GET can\'t be processed. The connection is already closed.'); - assert.strictEqual(err.command, 'GET'); - assert.strictEqual(client.offlineQueue.length, 0); - done(); - }); - }, 50); - }); - - it('emit an error', function (done) { - if (helper.redisProcess().spawnFailed()) this.skip(); - client.quit(); - client.on('error', function (err) { - assert.strictEqual(err.message, 'SET can\'t be processed. The connection is already closed.'); - assert.strictEqual(err.command, 'SET'); - assert.strictEqual(client.offlineQueue.length, 0); - done(); - }); - setTimeout(function () { - client.set('foo', 'bar'); - }, 50); - }); - - }); - - describe('when redis closes unexpectedly', function () { - it('reconnects and can retrieve the pre-existing data', function (done) { - client.on('reconnecting', function onRecon (params) { - client.on('connect', function onConnect () { - var end = helper.callFuncAfter(function () { - client.removeListener('connect', onConnect); - client.removeListener('reconnecting', onRecon); - assert.strictEqual(client.serverInfo.db0.keys, 2); - assert.strictEqual(Object.keys(client.serverInfo.db0).length, 3); - done(); - }, 4); - client.get('recon 1', helper.isString('one', end)); - client.get('recon 1', helper.isString('one', end)); - client.get('recon 2', helper.isString('two', end)); - client.get('recon 2', helper.isString('two', end)); - }); - }); - - client.set('recon 1', 'one'); - client.set('recon 2', 'two', function (err, res) { - // Do not do this in normal programs. This is to simulate the server closing on us. - // For orderly shutdown in normal programs, do client.quit() - client.stream.destroy(); - }); - }); - - it('reconnects properly when monitoring', function (done) { - client.on('reconnecting', function onRecon (params) { - client.on('ready', function onReady () { - assert.strictEqual(client.monitoring, true, 'monitoring after reconnect'); - client.removeListener('ready', onReady); - client.removeListener('reconnecting', onRecon); - done(); - }); - }); - - assert.strictEqual(client.monitoring, false, 'monitoring off at start'); - client.set('recon 1', 'one'); - client.monitor(function (err, res) { - assert.strictEqual(client.monitoring, true, 'monitoring on after monitor()'); - client.set('recon 2', 'two', function (err, res) { - // Do not do this in normal programs. This is to simulate the server closing on us. - // For orderly shutdown in normal programs, do client.quit() - client.stream.destroy(); - }); - }); - }); - - describe("and it's subscribed to a channel", function () { - // "Connection in subscriber mode, only subscriber commands may be used" - it('reconnects, unsubscribes, and can retrieve the pre-existing data', function (done) { - client.on('ready', function onConnect () { - client.unsubscribe(helper.isNotError()); - - client.on('unsubscribe', function (channel, count) { - // we should now be out of subscriber mode. - assert.strictEqual(channel, 'recon channel'); - assert.strictEqual(count, 0); - client.set('foo', 'bar', helper.isString('OK', done)); - }); - }); - - client.set('recon 1', 'one'); - client.subscribe('recon channel', function (err, res) { - // Do not do this in normal programs. This is to simulate the server closing on us. - // For orderly shutdown in normal programs, do client.quit() - client.stream.destroy(); - }); - }); - - it('reconnects, unsubscribes, and can retrieve the pre-existing data of a explicit channel', function (done) { - client.on('ready', function onConnect () { - client.unsubscribe('recon channel', helper.isNotError()); - - client.on('unsubscribe', function (channel, count) { - // we should now be out of subscriber mode. - assert.strictEqual(channel, 'recon channel'); - assert.strictEqual(count, 0); - client.set('foo', 'bar', helper.isString('OK', done)); - }); - }); - - client.set('recon 1', 'one'); - client.subscribe('recon channel', function (err, res) { - // Do not do this in normal programs. This is to simulate the server closing on us. - // For orderly shutdown in normal programs, do client.quit() - client.stream.destroy(); - }); - }); - }); - - describe('domain', function () { - it('allows client to be executed from within domain', function (done) { - var domain = require('domain').create(); - - domain.run(function () { - client.set('domain', 'value', function (err, res) { - assert.ok(process.domain); - throw new Error('ohhhh noooo'); - }); - }); - - // this is the expected and desired behavior - domain.on('error', function (err) { - assert.strictEqual(err.message, 'ohhhh noooo'); - domain.exit(); - done(); - }); - }); - - it('keeps the same domain by using the offline queue', function (done) { - client.end(true); - client = redis.createClient(); - var testDomain = require('domain').create(); - testDomain.run(function () { - client.set('FOOBAR', 'def', function () { - assert.strictEqual(process.domain, testDomain); - done(); - }); - }); - require('domain').create(); - }); - - it('catches all errors from within the domain', function (done) { - var domain = require('domain').create(); - - domain.run(function () { - // Trigger an error within the domain - client.end(true); - client.set('domain', 'value'); - }); - - domain.on('error', function (err) { - assert.strictEqual(err.message, 'SET can\'t be processed. The connection is already closed.'); - domain.exit(); - done(); - }); - }); - }); - }); - - describe('utf8', function () { - it('handles utf-8 keys', function (done) { - var utf8Sample = 'ಠ_ಠ'; - client.set(['utf8test', utf8Sample], helper.isString('OK')); - client.get(['utf8test'], function (err, obj) { - assert.strictEqual(utf8Sample, obj); - done(err); - }); - }); - }); - }); - - describe('unref', function () { - it('exits subprocess as soon as final command is processed', function (done) { - this.timeout(12000); - var args = config.HOST[ip] ? [config.HOST[ip], config.PORT] : [ip]; - var external = fork('./test/lib/unref.js', args); - - var id = setTimeout(function () { - external.kill(); - done(new Error('unref subprocess timed out')); - }, 8000); - - external.on('close', function (code) { - clearTimeout(id); - assert.strictEqual(code, 0); - done(); - }); - }); - }); - - describe('execution order / fire query while loading', function () { - it('keep execution order for commands that may fire while redis is still loading', function (done) { - client = redis.createClient.apply(null, args); - var fired = false; - client.set('foo', 'bar', function (err, res) { - assert(fired === false); - done(); - }); - client.info(function (err, res) { - fired = true; - }); - }); - - // TODO: consider allowing loading commands in v.3 - // it('should fire early', function (done) { - // client = redis.createClient.apply(null, args); - // var fired = false; - // client.info(function (err, res) { - // fired = true; - // }); - // client.set('foo', 'bar', function (err, res) { - // assert(fired); - // done(); - // }); - // assert.strictEqual(client.offlineQueue.length, 1); - // assert.strictEqual(client.commandQueue.length, 1); - // client.on('connect', function () { - // assert.strictEqual(client.offlineQueue.length, 1); - // assert.strictEqual(client.commandQueue.length, 1); - // }); - // client.on('ready', function () { - // assert.strictEqual(client.offlineQueue.length, 0); - // }); - // }); - }); - - describe('protocol error', function () { - - it('should gracefully recover and only fail on the already send commands', function (done) { - client = redis.createClient.apply(null, args); - var error; - client.on('error', function (err) { - assert.strictEqual(err.message, 'Protocol error, got "a" as reply type byte. Please report this.'); - assert.strictEqual(err, error); - assert(err instanceof redis.ParserError); - // After the hard failure work properly again. The set should have been processed properly too - client.get('foo', function (err, res) { - assert.strictEqual(res, 'bar'); - done(); - }); - }); - client.once('ready', function () { - client.set('foo', 'bar', function (err, res) { - assert.strictEqual(err.message, 'Fatal error encountered. Command aborted. It might have been processed.'); - assert.strictEqual(err.code, 'NR_FATAL'); - assert(err instanceof redis.AbortError); - error = err.origin; - }); - // Make sure we call execute out of the reply - // ready is called in a reply - process.nextTick(function () { - // Fail the set answer. Has no corresponding command obj and will therefore land in the error handler and set - client.replyParser.execute(new Buffer('a*1\r*1\r$1`zasd\r\na')); - }); - }); - }); - }); - - describe('enableOfflineQueue', function () { - describe('true', function () { - it('does not return an error and enqueues operation', function (done) { - client = redis.createClient(9999); - var finished = false; - client.on('error', function (e) { - // ignore, b/c expecting a "can't connect" error - }); - - setTimeout(function () { - client.set('foo', 'bar', function (err, result) { - if (!finished) done(err); - assert.strictEqual(err.message, 'Connection forcefully ended and command aborted.'); - }); - - setTimeout(function () { - assert.strictEqual(client.offlineQueue.length, 1); - finished = true; - done(); - }, 25); - }, 50); - }); - - it.skip('enqueues operation and keep the queue while trying to reconnect', function (done) { - client = redis.createClient(9999, null, { - retryStrategy: function (options) { - console.log(options) - if (options.attempt < 4) { - return 200; - } - } - }); - var i = 0; - - client.on('error', function (err) { - if (err.code === 'CONNECTION_BROKEN') { - assert(i, 3); - assert.strictEqual(client.offlineQueue.length, 0); - assert.strictEqual(err.origin.code, 'ECONNREFUSED'); - if (!(err instanceof redis.AbortError)) { - done(); - } else { - assert.strictEqual(err.command, 'SET'); - } - } else { - assert.equal(err.code, 'ECONNREFUSED'); - assert.equal(err.errno, 'ECONNREFUSED'); - assert.equal(err.syscall, 'connect'); - } - }); - - client.on('reconnecting', function (params) { - i++; - assert.equal(params.attempt, i); - assert.strictEqual(params.timesConnected, 0); - assert(params.error instanceof Error); - assert(typeof params.totalRetryTime === 'number'); - assert.strictEqual(client.offlineQueue.length, 2); - }); - - // Should work with either a callback or without - client.set('baz', 13); - client.set('foo', 'bar', function (err, result) { - assert(i, 3); - assert(err); - assert.strictEqual(client.offlineQueue.length, 0); - }); - }); - - it('flushes the command queue if connection is lost', function (done) { - client = redis.createClient(); - - client.once('ready', function () { - var multi = client.multi(); - multi.config('bar'); - var cb = function (err, reply) { - assert.equal(err.code, 'UNCERTAIN_STATE'); - }; - for (var i = 0; i < 12; i += 3) { - client.set('foo' + i, 'bar' + i); - multi.set('foo' + (i + 1), 'bar' + (i + 1), cb); - multi.set('foo' + (i + 2), 'bar' + (i + 2)); - } - multi.exec(); - assert.equal(client.commandQueue.length, 15); - helper.killConnection(client); - }); - - var end = helper.callFuncAfter(done, 3); - client.on('error', function (err) { - if (err.command === 'EXEC') { - assert.strictEqual(client.commandQueue.length, 0); - assert.strictEqual(err.errors.length, 9); - assert.strictEqual(err.errors[1].command, 'SET'); - assert.deepEqual(err.errors[1].args, ['foo1', 'bar1']); - end(); - } else if (err.code === 'UNCERTAIN_STATE') { - assert.strictEqual(client.commandQueue.length, 0); - assert.strictEqual(err.errors.length, 4); - assert.strictEqual(err.errors[0].command, 'SET'); - assert.deepEqual(err.errors[0].args, ['foo0', 'bar0']); - end(); - } else { - assert.equal(err.code, 'ECONNREFUSED'); - assert.equal(err.errno, 'ECONNREFUSED'); - assert.equal(err.syscall, 'connect'); - end(); - } - }); - }); - }); - - describe('false', function () { - - it('stream not writable', function (done) { - client = redis.createClient({ - enableOfflineQueue: false - }); - client.on('ready', function () { - client.stream.destroy(); - client.set('foo', 'bar', function (err, res) { - assert.strictEqual(err.message, "SET can't be processed. Stream not writeable."); - done(); - }); - }); - }); - - it('emit an error and does not enqueues operation', function (done) { - client = redis.createClient(9999, null, { - enableOfflineQueue: false - }); - var end = helper.callFuncAfter(done, 3); - - client.on('error', function (err) { - assert(/offline queue is deactivated|ECONNREFUSED/.test(err.message)); - assert.equal(client.commandQueue.length, 0); - end(); - }); - - client.set('foo', 'bar'); - - assert.doesNotThrow(function () { - client.set('foo', 'bar', function (err) { - // should callback with an error - assert.ok(err); - setTimeout(end, 50); - }); - }); - }); - - it('flushes the command queue if connection is lost', function (done) { - client = redis.createClient({ - enableOfflineQueue: false - }); - - redis.debugMode = true; - var unhookIntercept = intercept(function () { - return ''; - }); - client.once('ready', function () { - var multi = client.multi(); - multi.config('bar'); - var cb = function (err, reply) { - assert.equal(err.code, 'UNCERTAIN_STATE'); - }; - for (var i = 0; i < 12; i += 3) { - client.set('foo' + i, 'bar' + i); - multi.set('foo' + (i + 1), 'bar' + (i + 1), cb); - multi.set('foo' + (i + 2), 'bar' + (i + 2)); - } - multi.exec(); - assert.equal(client.commandQueue.length, 15); - helper.killConnection(client); - }); - - var end = helper.callFuncAfter(done, 3); - client.on('error', function (err) { - assert.equal(client.commandQueue.length, 0); - if (err.command === 'EXEC') { - assert.equal(err.errors.length, 9); - end(); - } else if (err.code === 'UNCERTAIN_STATE') { - assert.equal(err.errors.length, 4); - end(); - } else { - assert.equal(err.code, 'ECONNREFUSED'); - assert.equal(err.errno, 'ECONNREFUSED'); - assert.equal(err.syscall, 'connect'); - redis.debugMode = false; - client.end(true); - unhookIntercept(); - end(); - } - }); - }); - }); - }); - - }); - }); -}); + // Fail the set answer. Has no corresponding command obj and will therefore land in the error handler and set + client.replyParser.execute(Buffer.from('a*1\r*1\r$1`zasd\r\na')) + }) + }) + }) + }) + + describe('enableOfflineQueue', function () { + describe('true', function () { + it('does not return an error and enqueues operation', function (done) { + client = redis.createClient(9999) + var finished = false + client.on('error', function (e) { + // ignore, b/c expecting a "can't connect" error + }) + + setTimeout(function () { + client.set('foo', 'bar', function (err, result) { + if (!finished) done(err) + assert.strictEqual(err.message, 'Connection forcefully ended and command aborted.') + }) + + setTimeout(function () { + assert.strictEqual(client.offlineQueue.length, 1) + finished = true + done() + }, 25) + }, 50) + }) + + it.skip('enqueues operation and keep the queue while trying to reconnect', function (done) { + client = redis.createClient(9999, null, { + retryStrategy: function (options) { + if (options.attempt < 4) { + return 200 + } + } + }) + var i = 0 + + client.on('error', function (err) { + if (err.code === 'CONNECTION_BROKEN') { + assert(i, 3) + assert.strictEqual(client.offlineQueue.length, 0) + assert.strictEqual(err.origin.code, 'ECONNREFUSED') + if (!(err instanceof redis.AbortError)) { + done() + } else { + assert.strictEqual(err.command, 'SET') + } + } else { + assert.strictEqual(err.code, 'ECONNREFUSED') + assert.strictEqual(err.errno, 'ECONNREFUSED') + assert.strictEqual(err.syscall, 'connect') + } + }) + + client.on('reconnecting', function (params) { + i++ + assert.strictEqual(params.attempt, i) + assert.strictEqual(params.timesConnected, 0) + assert(params.error instanceof Error) + assert(typeof params.totalRetryTime === 'number') + assert.strictEqual(client.offlineQueue.length, 2) + }) + + // Should work with either a callback or without + client.set('baz', 13) + client.set('foo', 'bar', function (err, result) { + assert(i, 3) + assert(err) + assert.strictEqual(client.offlineQueue.length, 0) + }) + }) + + it('flushes the command queue if connection is lost', function (done) { + client = redis.createClient() + + client.once('ready', function () { + var multi = client.multi() + multi.config('bar') + var cb = function (err, reply) { + assert.strictEqual(err.code, 'UNCERTAIN_STATE') + } + for (var i = 0; i < 12; i += 3) { + client.set('foo' + i, 'bar' + i) + multi.set('foo' + (i + 1), 'bar' + (i + 1), cb) + multi.set('foo' + (i + 2), 'bar' + (i + 2)) + } + multi.exec() + assert.strictEqual(client.commandQueue.length, 15) + helper.killConnection(client) + }) + + var end = helper.callFuncAfter(done, 3) + client.on('error', function (err) { + if (err.command === 'EXEC') { + assert.strictEqual(client.commandQueue.length, 0) + assert.strictEqual(err.errors.length, 9) + assert.strictEqual(err.errors[1].command, 'SET') + assert.deepEqual(err.errors[1].args, ['foo1', 'bar1']) + end() + } else if (err.code === 'UNCERTAIN_STATE') { + assert.strictEqual(client.commandQueue.length, 0) + assert.strictEqual(err.errors.length, 4) + assert.strictEqual(err.errors[0].command, 'SET') + assert.deepEqual(err.errors[0].args, ['foo0', 'bar0']) + end() + } else { + assert.strictEqual(err.code, 'ECONNREFUSED') + assert.strictEqual(err.errno, 'ECONNREFUSED') + assert.strictEqual(err.syscall, 'connect') + end() + } + }) + }) + }) + + describe('false', function () { + it('stream not writable', function (done) { + client = redis.createClient({ + enableOfflineQueue: false + }) + client.on('ready', function () { + client.stream.destroy() + client.set('foo', 'bar', function (err, res) { + assert.strictEqual(err.message, 'SET can\'t be processed. Stream not writeable.') + done() + }) + }) + }) + + it('emit an error and does not enqueues operation', function (done) { + client = redis.createClient(9999, null, { + enableOfflineQueue: false + }) + var end = helper.callFuncAfter(done, 3) + + client.on('error', function (err) { + assert(/offline queue is deactivated|ECONNREFUSED/.test(err.message)) + assert.strictEqual(client.commandQueue.length, 0) + end() + }) + + client.set('foo', 'bar') + + assert.doesNotThrow(function () { + client.set('foo', 'bar', function (err) { + // should callback with an error + assert.ok(err) + setTimeout(end, 50) + }) + }) + }) + + it('flushes the command queue if connection is lost', function (done) { + client = redis.createClient({ + enableOfflineQueue: false + }) + + redis.debugMode = true + var unhookIntercept = intercept(function () { + return '' + }) + client.once('ready', function () { + var multi = client.multi() + multi.config('bar') + var cb = function (err, reply) { + assert.strictEqual(err.code, 'UNCERTAIN_STATE') + } + for (var i = 0; i < 12; i += 3) { + client.set('foo' + i, 'bar' + i) + multi.set('foo' + (i + 1), 'bar' + (i + 1), cb) + multi.set('foo' + (i + 2), 'bar' + (i + 2)) + } + multi.exec() + assert.strictEqual(client.commandQueue.length, 15) + helper.killConnection(client) + }) + + var end = helper.callFuncAfter(done, 3) + client.on('error', function (err) { + assert.strictEqual(client.commandQueue.length, 0) + if (err.command === 'EXEC') { + assert.strictEqual(err.errors.length, 9) + end() + } else if (err.code === 'UNCERTAIN_STATE') { + assert.strictEqual(err.errors.length, 4) + end() + } else { + assert.strictEqual(err.code, 'ECONNREFUSED') + assert.strictEqual(err.errno, 'ECONNREFUSED') + assert.strictEqual(err.syscall, 'connect') + redis.debugMode = false + client.end(true) + unhookIntercept() + end() + } + }) + }) + }) + }) + }) + }) +}) diff --git a/test/prefix.spec.js b/test/prefix.spec.js index 52fd39c1cc..dda4b40e66 100644 --- a/test/prefix.spec.js +++ b/test/prefix.spec.js @@ -1,118 +1,94 @@ -'use strict'; +'use strict' -var assert = require('assert'); -var config = require('./lib/config'); -var helper = require('./helper'); -var redis = config.redis; +var assert = require('assert') +var config = require('./lib/config') +var helper = require('./helper') +var redis = config.redis describe('prefix key names', function () { + helper.allTests(function (ip, args) { + describe('using ' + ip, function () { + var client = null - helper.allTests(function (ip, args) { + beforeEach(function (done) { + client = redis.createClient({ + prefix: 'test:prefix:' + }) + client.on('ready', function () { + client.flushdb(function (err) { + done(err) + }) + }) + }) - describe('using ' + ip, function () { - var client = null; + afterEach(function () { + client.end(true) + }) - beforeEach(function (done) { - client = redis.createClient({ - prefix: 'test:prefix:' - }); - client.on('ready', function () { - client.flushdb(function (err) { - done(err); - }); - }); - }); + it('auto prefix set / get', function (done) { + client.set('key', 'value', helper.isString('OK')) + client.get('key', helper.isString('value')) + client.getrange('key', 1, -1, function (err, reply) { + assert.strictEqual(reply, 'alue') + assert.strictEqual(err, null) + }) + client.exists('key', helper.isNumber(1)) + // The key will be prefixed itself + client.exists('test:prefix:key', helper.isNumber(0)) + client.mset('key2', 'value2', 'key3', 'value3') + client.keys('*', function (err, res) { + assert.strictEqual(err, null) + assert.strictEqual(res.length, 3) + assert(res.indexOf('test:prefix:key') !== -1) + assert(res.indexOf('test:prefix:key2') !== -1) + assert(res.indexOf('test:prefix:key3') !== -1) + done() + }) + }) - afterEach(function () { - client.end(true); - }); + it('auto prefix set / get with .batch', function (done) { + var batch = client.batch() + batch.set('key', 'value', helper.isString('OK')) + batch.get('key', helper.isString('value')) + batch.getrange('key', 1, -1, function (err, reply) { + assert.strictEqual(reply, 'alue') + assert.strictEqual(err, null) + }) + batch.exists('key', helper.isNumber(1)) + // The key will be prefixed itself + batch.exists('test:prefix:key', helper.isNumber(0)) + batch.mset('key2', 'value2', 'key3', 'value3') + batch.keys('*', function (err, res) { + assert.strictEqual(err, null) + assert.strictEqual(res.length, 3) + assert(res.indexOf('test:prefix:key') !== -1) + assert(res.indexOf('test:prefix:key2') !== -1) + assert(res.indexOf('test:prefix:key3') !== -1) + }) + batch.exec(done) + }) - it('auto prefix set / get', function (done) { - client.set('key', 'value', function (err, reply) { - assert.strictEqual(reply, 'OK'); - }); - client.get('key', function (err, reply) { - assert.strictEqual(reply, 'value'); - }); - client.getrange('key', 1, -1, function (err, reply) { - assert.strictEqual(reply, 'alue'); - assert.strictEqual(err, null); - }); - client.exists('key', function (err, res) { - assert.strictEqual(res, 1); - }); - client.exists('test:prefix:key', function (err, res) { - // The key will be prefixed itself - assert.strictEqual(res, 0); - }); - client.mset('key2', 'value2', 'key3', 'value3'); - client.keys('*', function (err, res) { - assert.strictEqual(res.length, 3); - assert(res.indexOf('test:prefix:key') !== -1); - assert(res.indexOf('test:prefix:key2') !== -1); - assert(res.indexOf('test:prefix:key3') !== -1); - done(); - }); - }); - - it('auto prefix set / get with .batch', function (done) { - var batch = client.batch(); - batch.set('key', 'value', function (err, reply) { - assert.strictEqual(reply, 'OK'); - }); - batch.get('key', function (err, reply) { - assert.strictEqual(reply, 'value'); - }); - batch.getrange('key', 1, -1, function (err, reply) { - assert.strictEqual(reply, 'alue'); - assert.strictEqual(err, null); - }); - batch.exists('key', function (err, res) { - assert.strictEqual(res, 1); - }); - batch.exists('test:prefix:key', function (err, res) { - // The key will be prefixed itself - assert.strictEqual(res, 0); - }); - batch.mset('key2', 'value2', 'key3', 'value3'); - batch.keys('*', function (err, res) { - assert.strictEqual(res.length, 3); - assert(res.indexOf('test:prefix:key') !== -1); - assert(res.indexOf('test:prefix:key2') !== -1); - assert(res.indexOf('test:prefix:key3') !== -1); - }); - batch.exec(done); - }); - - it('auto prefix set / get with .multi', function (done) { - var multi = client.multi(); - multi.set('key', 'value', function (err, reply) { - assert.strictEqual(reply, 'OK'); - }); - multi.get('key', function (err, reply) { - assert.strictEqual(reply, 'value'); - }); - multi.getrange('key', 1, -1, function (err, reply) { - assert.strictEqual(reply, 'alue'); - assert.strictEqual(err, null); - }); - multi.exists('key', function (err, res) { - assert.strictEqual(res, 1); - }); - multi.exists('test:prefix:key', function (err, res) { - // The key will be prefixed itself - assert.strictEqual(res, 0); - }); - multi.mset('key2', 'value2', 'key3', 'value3'); - multi.keys('*', function (err, res) { - assert.strictEqual(res.length, 3); - assert(res.indexOf('test:prefix:key') !== -1); - assert(res.indexOf('test:prefix:key2') !== -1); - assert(res.indexOf('test:prefix:key3') !== -1); - }); - multi.exec(done); - }); - - }); - }); -}); + it('auto prefix set / get with .multi', function (done) { + var multi = client.multi() + multi.set('key', 'value', helper.isString('OK')) + multi.get('key', helper.isString('value')) + multi.getrange('key', 1, -1, function (err, reply) { + assert.strictEqual(reply, 'alue') + assert.strictEqual(err, null) + }) + multi.exists('key', helper.isNumber(1)) + // The key will be prefixed itself + multi.exists('test:prefix:key', helper.isNumber(0)) + multi.mset('key2', 'value2', 'key3', 'value3') + multi.keys('*', function (err, res) { + assert.strictEqual(err, null) + assert.strictEqual(res.length, 3) + assert(res.indexOf('test:prefix:key') !== -1) + assert(res.indexOf('test:prefix:key2') !== -1) + assert(res.indexOf('test:prefix:key3') !== -1) + }) + multi.exec(done) + }) + }) + }) +}) diff --git a/test/pubsub.spec.js b/test/pubsub.spec.js index b5e912b52f..40a1745a71 100644 --- a/test/pubsub.spec.js +++ b/test/pubsub.spec.js @@ -1,669 +1,650 @@ -'use strict'; +'use strict' -var assert = require('assert'); -var config = require('./lib/config'); -var helper = require('./helper'); -var redis = config.redis; +var Buffer = require('safe-buffer').Buffer +var assert = require('assert') +var config = require('./lib/config') +var helper = require('./helper') +var redis = config.redis describe('publish/subscribe', function () { + helper.allTests(function (ip, args) { + describe('using ' + ip, function () { + var pub = null + var sub = null + var channel = 'test channel' + var channel2 = 'test channel 2' + var message = 'test message' - helper.allTests(function (ip, args) { + beforeEach(function (done) { + var end = helper.callFuncAfter(done, 2) - describe('using ' + ip, function () { - var pub = null; - var sub = null; - var channel = 'test channel'; - var channel2 = 'test channel 2'; - var message = 'test message'; + pub = redis.createClient.apply(null, args) + sub = redis.createClient.apply(null, args) + pub.once('connect', function () { + pub.flushdb(function () { + end() + }) + }) + sub.once('connect', function () { + end() + }) + }) - beforeEach(function (done) { - var end = helper.callFuncAfter(done, 2); + describe('disable resubscribe', function () { + beforeEach(function (done) { + sub.end(false) + sub = redis.createClient({ + disableResubscribing: true + }) + sub.once('connect', function () { + done() + }) + }) - pub = redis.createClient.apply(null, args); - sub = redis.createClient.apply(null, args); - pub.once('connect', function () { - pub.flushdb(function () { - end(); - }); - }); - sub.once('connect', function () { - end(); - }); - }); + it('does not fire subscribe events after reconnecting', function (done) { + var a = false + sub.on('subscribe', function (chnl, count) { + if (chnl === channel2) { + if (a) { + return done(new Error('Test failed')) + } + assert.strictEqual(2, count) + sub.stream.destroy() + } + }) - describe('disable resubscribe', function () { - beforeEach(function (done) { - sub.end(false); - sub = redis.createClient({ - disableResubscribing: true - }); - sub.once('connect', function () { - done(); - }); - }); + sub.on('reconnecting', function () { + a = true + sub.on('ready', function () { + assert.strictEqual(sub.commandQueue.length, 0) + done() + }) + }) - it('does not fire subscribe events after reconnecting', function (done) { - var a = false; - sub.on('subscribe', function (chnl, count) { - if (chnl === channel2) { - if (a) { - return done(new Error('Test failed')); - } - assert.equal(2, count); - sub.stream.destroy(); - } - }); + sub.subscribe(channel, channel2) + }) + }) - sub.on('reconnecting', function () { - a = true; - sub.on('ready', function () { - assert.strictEqual(sub.commandQueue.length, 0); - done(); - }); - }); + describe('stringNumbers and pub sub', function () { + beforeEach(function (done) { + sub.end(false) + sub = redis.createClient({ + stringNumbers: true + }) + sub.once('connect', function () { + done() + }) + }) - sub.subscribe(channel, channel2); - }); - }); + it('does not fire subscribe events after reconnecting', function (done) { + var i = 0 + var end = helper.callFuncAfter(done, 2) + sub.on('subscribe', function (chnl, count) { + assert.strictEqual(typeof count, 'number') + assert.strictEqual(++i, count) + }) + sub.on('unsubscribe', function (chnl, count) { + assert.strictEqual(typeof count, 'number') + assert.strictEqual(--i, count) + }) + sub.subscribe(channel, channel2) + sub.unsubscribe(function (err, res) { // Do not pass a channel here! + if (err) throw err + assert.strictEqual(sub.pubSubMode, 2) + assert.deepEqual(sub.subscriptionSet, {}) + end() + }) + sub.set('foo', 'bar', helper.isString('OK')) + sub.subscribe(channel2, end) + }) + }) - describe('stringNumbers and pub sub', function () { - beforeEach(function (done) { - sub.end(false); - sub = redis.createClient({ - stringNumbers: true - }); - sub.once('connect', function () { - done(); - }); - }); + describe('subscribe', function () { + it('fires a subscribe event for each channel subscribed to even after reconnecting', function (done) { + var a = false + sub.on('subscribe', function (chnl, count) { + if (chnl === channel2) { + assert.strictEqual(2, count) + if (a) return done() + sub.stream.destroy() + } + }) - it('does not fire subscribe events after reconnecting', function (done) { - var i = 0; - var end = helper.callFuncAfter(done, 2); - sub.on('subscribe', function (chnl, count) { - assert.strictEqual(typeof count, 'number'); - assert.strictEqual(++i, count); - }); - sub.on('unsubscribe', function (chnl, count) { - assert.strictEqual(typeof count, 'number'); - assert.strictEqual(--i, count); - }); - sub.subscribe(channel, channel2); - sub.unsubscribe(function (err, res) { // Do not pass a channel here! - assert.strictEqual(sub.pubSubMode, 2); - assert.deepEqual(sub.subscriptionSet, {}); - end(); - }); - sub.set('foo', 'bar', helper.isString('OK')); - sub.subscribe(channel2, end); - }); - }); + sub.on('reconnecting', function () { + a = true + }) - describe('subscribe', function () { - it('fires a subscribe event for each channel subscribed to even after reconnecting', function (done) { - var a = false; - sub.on('subscribe', function (chnl, count) { - if (chnl === channel2) { - assert.equal(2, count); - if (a) return done(); - sub.stream.destroy(); - } - }); + sub.subscribe(channel, channel2) + }) - sub.on('reconnecting', function () { - a = true; - }); + it('fires a subscribe event for each channel as buffer subscribed to even after reconnecting', function (done) { + var a = false + sub.end(true) + sub = redis.createClient({ + detectBuffers: true + }) + sub.on('subscribe', function (chnl, count) { + if (chnl.inspect() === Buffer.from([0xAA, 0xBB, 0x00, 0xF0]).inspect()) { + assert.strictEqual(1, count) + if (a) { + return done() + } + sub.stream.destroy() + } + }) - sub.subscribe(channel, channel2); - }); + sub.on('reconnecting', function () { + a = true + }) - it('fires a subscribe event for each channel as buffer subscribed to even after reconnecting', function (done) { - var a = false; - sub.end(true); - sub = redis.createClient({ - detectBuffers: true - }); - sub.on('subscribe', function (chnl, count) { - if (chnl.inspect() === new Buffer([0xAA, 0xBB, 0x00, 0xF0]).inspect()) { - assert.equal(1, count); - if (a) { - return done(); - } - sub.stream.destroy(); - } - }); + sub.subscribe(Buffer.from([0xAA, 0xBB, 0x00, 0xF0]), channel2) + }) - sub.on('reconnecting', function () { - a = true; - }); + it('receives messages on subscribed channel', function (done) { + var end = helper.callFuncAfter(done, 2) + sub.on('subscribe', function (chnl, count) { + pub.publish(channel, message, function (err, res) { + helper.isNumber(1)(err, res) + end() + }) + }) - sub.subscribe(new Buffer([0xAA, 0xBB, 0x00, 0xF0]), channel2); - }); + sub.on('message', function (chnl, msg) { + assert.strictEqual(chnl, channel) + assert.strictEqual(msg, message) + end() + }) - it('receives messages on subscribed channel', function (done) { - var end = helper.callFuncAfter(done, 2); - sub.on('subscribe', function (chnl, count) { - pub.publish(channel, message, function (err, res) { - helper.isNumber(1)(err, res); - end(); - }); - }); + sub.subscribe(channel) + }) - sub.on('message', function (chnl, msg) { - assert.equal(chnl, channel); - assert.equal(msg, message); - end(); - }); + it('receives messages if subscribe is called after unsubscribe', function (done) { + var end = helper.callFuncAfter(done, 2) + sub.once('subscribe', function (chnl, count) { + pub.publish(channel, message, function (err, res) { + helper.isNumber(1)(err, res) + end() + }) + }) - sub.subscribe(channel); - }); + sub.on('message', function (chnl, msg) { + assert.strictEqual(chnl, channel) + assert.strictEqual(msg, message) + end() + }) - it('receives messages if subscribe is called after unsubscribe', function (done) { - var end = helper.callFuncAfter(done, 2); - sub.once('subscribe', function (chnl, count) { - pub.publish(channel, message, function (err, res) { - helper.isNumber(1)(err, res); - end(); - }); - }); + sub.subscribe(channel) + sub.unsubscribe(channel) + sub.subscribe(channel) + }) - sub.on('message', function (chnl, msg) { - assert.equal(chnl, channel); - assert.equal(msg, message); - end(); - }); + it('handles SUB UNSUB MSG SUB', function (done) { + sub.subscribe('chan8') + sub.subscribe('chan9') + sub.unsubscribe('chan9') + pub.publish('chan8', 'something') + sub.subscribe('chan9', done) + }) - sub.subscribe(channel); - sub.unsubscribe(channel); - sub.subscribe(channel); - }); + it('handles SUB UNSUB MSG SUB 2', function (done) { + sub.psubscribe('abc*', helper.isDeepEqual([1, ['abc*']])) + sub.subscribe('xyz') + sub.unsubscribe('xyz') + pub.publish('abcd', 'something') + sub.subscribe('xyz', done) + }) - it('handles SUB UNSUB MSG SUB', function (done) { - sub.subscribe('chan8'); - sub.subscribe('chan9'); - sub.unsubscribe('chan9'); - pub.publish('chan8', 'something'); - sub.subscribe('chan9', done); - }); + it('emits end event if quit is called from within subscribe', function (done) { + sub.on('end', done) + sub.on('subscribe', function (chnl, count) { + sub.quit() + }) + sub.subscribe(channel) + }) - it('handles SUB UNSUB MSG SUB 2', function (done) { - sub.psubscribe('abc*', helper.isUnSubscribe(1, 'abc*')); - sub.subscribe('xyz'); - sub.unsubscribe('xyz'); - pub.publish('abcd', 'something'); - sub.subscribe('xyz', done); - }); + it('subscribe; close; resubscribe with prototype inherited property names', function (done) { + var count = 0 + var channels = ['channel 1', 'channel 2'] + var msg = ['hi from channel 1', 'hi from channel 2'] - it('emits end event if quit is called from within subscribe', function (done) { - sub.on('end', done); - sub.on('subscribe', function (chnl, count) { - sub.quit(); - }); - sub.subscribe(channel); - }); + sub.on('message', function (channel, message) { + var n = Math.max(count - 1, 0) + assert.strictEqual(channel, channels[n]) + assert.strictEqual(message, msg[n]) + if (count === 2) return done() + sub.stream.end() + }) - it('subscribe; close; resubscribe with prototype inherited property names', function (done) { - var count = 0; - var channels = ['channel 1', 'channel 2']; - var msg = ['hi from channel 1', 'hi from channel 2']; + sub.select(3) + sub.subscribe(channels) - sub.on('message', function (channel, message) { - var n = Math.max(count - 1, 0); - assert.strictEqual(channel, channels[n]); - assert.strictEqual(message, msg[n]); - if (count === 2) return done(); - sub.stream.end(); - }); + sub.on('ready', function (err, results) { + if (err) throw err + pub.publish(channels[count], msg[count]) + count++ + }) - sub.select(3); - sub.subscribe(channels); + pub.publish(channels[count], msg[count]) + }) + }) - sub.on('ready', function (err, results) { - pub.publish(channels[count], msg[count]); - count++; - }); + describe('multiple subscribe / unsubscribe commands', function () { + it('reconnects properly with pub sub and select command', function (done) { + var end = helper.callFuncAfter(done, 2) + sub.select(3) + sub.set('foo', 'bar') + sub.set('failure', helper.isError()) // Triggering a warning while subscribing should work + sub.mget('foo', 'bar', 'baz', 'hello', 'world', helper.isDeepEqual(['bar', null, null, null, null])) + sub.subscribe('somechannel', 'another channel', function (err, res) { + if (err) throw err + end() + sub.stream.destroy() + }) + assert(sub.ready) + sub.on('ready', function () { + sub.unsubscribe() + sub.del('foo') + sub.info(end) + }) + }) - pub.publish(channels[count], msg[count]); - }); - }); + it('should not go into pubsub mode with unsubscribe commands', function (done) { + sub.on('unsubscribe', function (msg) { + // The unsubscribe should not be triggered, as there was no corresponding channel + throw new Error('Test failed') + }) + sub.set('foo', 'bar') + sub.unsubscribe(helper.isDeepEqual([0, []])) + sub.del('foo', done) + }) - describe('multiple subscribe / unsubscribe commands', function () { + it('handles multiple channels with the same channel name properly, even with buffers', function (done) { + var channels = ['a', 'b', 'a', Buffer.from('a'), 'c', 'b'] + var subscribedChannels = [1, 2, 2, 2, 3, 3] + var i = 0 + sub.subscribe(channels) + sub.on('subscribe', function (channel, count) { + if (Buffer.isBuffer(channel)) { + assert.strictEqual(channel.inspect(), Buffer.from(channels[i]).inspect()) + } else { + assert.strictEqual(channel, channels[i].toString()) + } + assert.strictEqual(count, subscribedChannels[i]) + i++ + }) + sub.unsubscribe('a', 'c', 'b') + sub.get('foo', done) + }) - it('reconnects properly with pub sub and select command', function (done) { - var end = helper.callFuncAfter(done, 2); - sub.select(3); - sub.set('foo', 'bar'); - sub.set('failure', helper.isError()); // Triggering a warning while subscribing should work - sub.mget('foo', 'bar', 'baz', 'hello', 'world', function (err, res) { - assert.deepEqual(res, ['bar', null, null, null, null]); - }); - sub.subscribe('somechannel', 'another channel', function (err, res) { - end(); - sub.stream.destroy(); - }); - assert(sub.ready); - sub.on('ready', function () { - sub.unsubscribe(); - sub.del('foo'); - sub.info(end); - }); - }); + it('should only resubscribe to channels not unsubscribed earlier on a reconnect', function (done) { + sub.subscribe('/foo', '/bar') + sub.batch().unsubscribe(['/bar'], function () { + pub.pubsub('channels', helper.isDeepEqual(['/foo'], function () { + sub.stream.destroy() + sub.once('ready', function () { + pub.pubsub('channels', helper.isDeepEqual(['/foo'], function () { + sub.unsubscribe('/foo', done) + })) + }) + })) + }).exec() + }) - it('should not go into pubsub mode with unsubscribe commands', function (done) { - sub.on('unsubscribe', function (msg) { - // The unsubscribe should not be triggered, as there was no corresponding channel - throw new Error('Test failed'); - }); - sub.set('foo', 'bar'); - sub.unsubscribe(function (err, res) { - assert.deepStrictEqual(res, [0, []]); - }); - sub.del('foo', done); - }); + it('unsubscribes, subscribes, unsubscribes... single and multiple entries mixed. Withouth callbacks', function (done) { + function subscribe (channels) { + sub.unsubscribe(helper.isNull) + sub.subscribe(channels, helper.isNull) + } + var all = false + var subscribeMsg = ['1', '3', '2', '5', 'test', 'bla'] + sub.on('subscribe', function (msg, count) { + subscribeMsg.splice(subscribeMsg.indexOf(msg), 1) + if (subscribeMsg.length === 0 && all) { + assert.strictEqual(count, 3) + done() + } + }) + var unsubscribeMsg = ['1', '3', '2'] + sub.on('unsubscribe', function (msg, count) { + unsubscribeMsg.splice(unsubscribeMsg.indexOf(msg), 1) + if (unsubscribeMsg.length === 0) { + assert.strictEqual(count, 0) + all = true + } + }) - it('handles multiple channels with the same channel name properly, even with buffers', function (done) { - var channels = ['a', 'b', 'a', new Buffer('a'), 'c', 'b']; - var subscribedChannels = [1, 2, 2, 2, 3, 3]; - var i = 0; - sub.subscribe(channels); - sub.on('subscribe', function (channel, count) { - if (Buffer.isBuffer(channel)) { - assert.strictEqual(channel.inspect(), new Buffer(channels[i]).inspect()); - } else { - assert.strictEqual(channel, channels[i].toString()); - } - assert.strictEqual(count, subscribedChannels[i]); - i++; - }); - sub.unsubscribe('a', 'c', 'b'); - sub.get('foo', done); - }); + subscribe(['1', '3']) + subscribe(['2']) + subscribe(['5', 'test', 'bla']) + }) - it('should only resubscribe to channels not unsubscribed earlier on a reconnect', function (done) { - sub.subscribe('/foo', '/bar'); - sub.batch().unsubscribe(['/bar'], function () { - pub.pubsub('channels', function (err, res) { - assert.deepEqual(res, ['/foo']); - sub.stream.destroy(); - sub.once('ready', function () { - pub.pubsub('channels', function (err, res) { - assert.deepEqual(res, ['/foo']); - sub.unsubscribe('/foo', done); - }); - }); - }); - }).exec(); - }); + it('unsubscribes, subscribes, unsubscribes... single and multiple entries mixed. Without callbacks', function (done) { + function subscribe (channels) { + sub.unsubscribe() + sub.subscribe(channels) + } + var all = false + var subscribeMsg = ['1', '3', '2', '5', 'test', 'bla'] + sub.on('subscribe', function (msg, count) { + subscribeMsg.splice(subscribeMsg.indexOf(msg), 1) + if (subscribeMsg.length === 0 && all) { + assert.strictEqual(count, 3) + done() + } + }) + var unsubscribeMsg = ['1', '3', '2'] + sub.on('unsubscribe', function (msg, count) { + unsubscribeMsg.splice(unsubscribeMsg.indexOf(msg), 1) + if (unsubscribeMsg.length === 0) { + assert.strictEqual(count, 0) + all = true + } + }) - it('unsubscribes, subscribes, unsubscribes... single and multiple entries mixed. Withouth callbacks', function (done) { - function subscribe (channels) { - sub.unsubscribe(helper.isNull); - sub.subscribe(channels, helper.isNull); - } - var all = false; - var subscribeMsg = ['1', '3', '2', '5', 'test', 'bla']; - sub.on('subscribe', function (msg, count) { - subscribeMsg.splice(subscribeMsg.indexOf(msg), 1); - if (subscribeMsg.length === 0 && all) { - assert.strictEqual(count, 3); - done(); - } - }); - var unsubscribeMsg = ['1', '3', '2']; - sub.on('unsubscribe', function (msg, count) { - unsubscribeMsg.splice(unsubscribeMsg.indexOf(msg), 1); - if (unsubscribeMsg.length === 0) { - assert.strictEqual(count, 0); - all = true; - } - }); + subscribe(['1', '3']) + subscribe(['2']) + subscribe(['5', 'test', 'bla']) + }) - subscribe(['1', '3']); - subscribe(['2']); - subscribe(['5', 'test', 'bla']); - }); + it('unsubscribes, subscribes, unsubscribes... single and multiple entries mixed. Without callback and concret channels', function (done) { + function subscribe (channels) { + sub.unsubscribe(channels) + sub.unsubscribe(channels) + sub.subscribe(channels) + } + var all = false + var subscribeMsg = ['1', '3', '2', '5', 'test', 'bla'] + sub.on('subscribe', function (msg, count) { + subscribeMsg.splice(subscribeMsg.indexOf(msg), 1) + if (subscribeMsg.length === 0 && all) { + assert.strictEqual(count, 6) + done() + } + }) + var unsubscribeMsg = ['1', '3', '2', '5', 'test', 'bla'] + sub.on('unsubscribe', function (msg, count) { + var pos = unsubscribeMsg.indexOf(msg) + if (pos !== -1) { unsubscribeMsg.splice(pos, 1) } + if (unsubscribeMsg.length === 0) { + all = true + } + }) - it('unsubscribes, subscribes, unsubscribes... single and multiple entries mixed. Without callbacks', function (done) { - function subscribe (channels) { - sub.unsubscribe(); - sub.subscribe(channels); - } - var all = false; - var subscribeMsg = ['1', '3', '2', '5', 'test', 'bla']; - sub.on('subscribe', function (msg, count) { - subscribeMsg.splice(subscribeMsg.indexOf(msg), 1); - if (subscribeMsg.length === 0 && all) { - assert.strictEqual(count, 3); - done(); - } - }); - var unsubscribeMsg = ['1', '3', '2']; - sub.on('unsubscribe', function (msg, count) { - unsubscribeMsg.splice(unsubscribeMsg.indexOf(msg), 1); - if (unsubscribeMsg.length === 0) { - assert.strictEqual(count, 0); - all = true; - } - }); + subscribe(['1', '3']) + subscribe(['2']) + subscribe(['5', 'test', 'bla']) + }) - subscribe(['1', '3']); - subscribe(['2']); - subscribe(['5', 'test', 'bla']); - }); + it('unsubscribes, subscribes, unsubscribes... with pattern matching', function (done) { + function subscribe (channels, callback) { + sub.punsubscribe('prefix:*', helper.isNull) + sub.psubscribe(channels, function (err, res) { + helper.isNull(err) + if (callback) callback(err, res) + }) + } + var all = false + var end = helper.callFuncAfter(done, 8) + var subscribeMsg = ['prefix:*', 'prefix:3', 'prefix:2', '5', 'test:a', 'bla'] + sub.on('psubscribe', function (msg, count) { + subscribeMsg.splice(subscribeMsg.indexOf(msg), 1) + if (subscribeMsg.length === 0) { + assert.strictEqual(count, 5) + all = true + } + }) + var rest = 1 + var unsubscribeMsg = ['prefix:*', 'prefix:*', 'prefix:*', '*'] + sub.on('punsubscribe', function (msg, count) { + unsubscribeMsg.splice(unsubscribeMsg.indexOf(msg), 1) + if (all) { + assert.strictEqual(unsubscribeMsg.length, 0) + assert.strictEqual(count, rest--) // Print the remaining channels + end() + } else { + assert.strictEqual(msg, 'prefix:*') + assert.strictEqual(count, rest++ - 1) + } + }) + sub.on('pmessage', function (pattern, channel, msg) { + assert.strictEqual(msg, 'test') + assert.strictEqual(pattern, 'prefix:*') + assert.strictEqual(channel, 'prefix:1') + end() + }) - it('unsubscribes, subscribes, unsubscribes... single and multiple entries mixed. Without callback and concret channels', function (done) { - function subscribe (channels) { - sub.unsubscribe(channels); - sub.unsubscribe(channels); - sub.subscribe(channels); - } - var all = false; - var subscribeMsg = ['1', '3', '2', '5', 'test', 'bla']; - sub.on('subscribe', function (msg, count) { - subscribeMsg.splice(subscribeMsg.indexOf(msg), 1); - if (subscribeMsg.length === 0 && all) { - assert.strictEqual(count, 6); - done(); - } - }); - var unsubscribeMsg = ['1', '3', '2', '5', 'test', 'bla']; - sub.on('unsubscribe', function (msg, count) { - var pos = unsubscribeMsg.indexOf(msg); - if (pos !== -1) - unsubscribeMsg.splice(pos, 1); - if (unsubscribeMsg.length === 0) { - all = true; - } - }); + subscribe(['prefix:*', 'prefix:3'], function () { + pub.publish('prefix:1', Buffer.from('test'), function () { + subscribe(['prefix:2']) + subscribe(['5', 'test:a', 'bla'], function () { + assert(all) + }) + sub.punsubscribe(function (err, res) { + assert(!err) + assert.deepStrictEqual(res, [0, ['prefix:3', 'prefix:2', '5', 'test:a', 'bla']]) + assert(all) + all = false // Make sure the callback is actually after the emit + end() + }) + sub.pubsub('channels', helper.isDeepEqual([], end)) + }) + }) + }) + }) - subscribe(['1', '3']); - subscribe(['2']); - subscribe(['5', 'test', 'bla']); - }); + describe('unsubscribe', function () { + it('fires an unsubscribe event', function (done) { + sub.on('subscribe', function (chnl, count) { + sub.unsubscribe(channel) + }) - it('unsubscribes, subscribes, unsubscribes... with pattern matching', function (done) { - function subscribe (channels, callback) { - sub.punsubscribe('prefix:*', helper.isNull); - sub.psubscribe(channels, function (err, res) { - helper.isNull(err); - if (callback) callback(err, res); - }); - } - var all = false; - var end = helper.callFuncAfter(done, 8); - var subscribeMsg = ['prefix:*', 'prefix:3', 'prefix:2', '5', 'test:a', 'bla']; - sub.on('psubscribe', function (msg, count) { - subscribeMsg.splice(subscribeMsg.indexOf(msg), 1); - if (subscribeMsg.length === 0) { - assert.strictEqual(count, 5); - all = true; - } - }); - var rest = 1; - var unsubscribeMsg = ['prefix:*', 'prefix:*', 'prefix:*', '*']; - sub.on('punsubscribe', function (msg, count) { - unsubscribeMsg.splice(unsubscribeMsg.indexOf(msg), 1); - if (all) { - assert.strictEqual(unsubscribeMsg.length, 0); - assert.strictEqual(count, rest--); // Print the remaining channels - end(); - } else { - assert.strictEqual(msg, 'prefix:*'); - assert.strictEqual(count, rest++ - 1); - } - }); - sub.on('pmessage', function (pattern, channel, msg) { - assert.strictEqual(msg, 'test'); - assert.strictEqual(pattern, 'prefix:*'); - assert.strictEqual(channel, 'prefix:1'); - end(); - }); + sub.subscribe(channel) - subscribe(['prefix:*', 'prefix:3'], function () { - pub.publish('prefix:1', new Buffer('test'), function () { - subscribe(['prefix:2']); - subscribe(['5', 'test:a', 'bla'], function () { - assert(all); - }); - sub.punsubscribe(function (err, res) { - assert(!err); - assert.deepStrictEqual(res, [0, ['prefix:3', 'prefix:2', '5', 'test:a', 'bla']]); - assert(all); - all = false; // Make sure the callback is actually after the emit - end(); - }); - sub.pubsub('channels', function (err, res) { - assert.strictEqual(res.length, 0); - end(); - }); - }); - }); - }); - }); + sub.on('unsubscribe', function (chnl, count) { + assert.strictEqual(chnl, channel) + assert.strictEqual(count, 0) + return done() + }) + }) - describe('unsubscribe', function () { - it('fires an unsubscribe event', function (done) { - sub.on('subscribe', function (chnl, count) { - sub.unsubscribe(channel); - }); + it('puts client back into write mode', function (done) { + sub.on('subscribe', function (chnl, count) { + sub.unsubscribe(channel) + }) - sub.subscribe(channel); + sub.subscribe(channel) - sub.on('unsubscribe', function (chnl, count) { - assert.equal(chnl, channel); - assert.strictEqual(count, 0); - return done(); - }); - }); + sub.on('unsubscribe', function (chnl, count) { + pub.incr('foo', helper.isNumber(1, done)) + }) + }) - it('puts client back into write mode', function (done) { - sub.on('subscribe', function (chnl, count) { - sub.unsubscribe(channel); - }); + it('sub executes callback when unsubscribe is called and there are no subscriptions', function (done) { + sub.unsubscribe(helper.isDeepEqual([0, []], done)) + }) - sub.subscribe(channel); + it('pub executes callback when unsubscribe is called and there are no subscriptions', function (done) { + pub.unsubscribe(helper.isDeepEqual([0, []])) + pub.get('foo', done) + }) + }) - sub.on('unsubscribe', function (chnl, count) { - pub.incr('foo', helper.isNumber(1, done)); - }); - }); + describe('psubscribe', function () { + it('allows all channels to be subscribed to using a * pattern', function (done) { + var sub2 = redis.createClient({ + returnBuffers: true + }) + sub.subscribe('/foo', function () { + sub2.on('ready', function () { + sub2.batch().psubscribe('*', helper.isDeepEqual([1, ['*']])).exec() + sub2.subscribe('/foo', function () { + pub.pubsub('numsub', '/foo', helper.isDeepEqual(['/foo', 2])) + // sub2 is counted twice as it subscribed with psubscribe and subscribe + pub.publish('/foo', 'hello world', helper.isNumber(3)) + }) + sub2.on('pmessage', function (pattern, channel, message) { + assert.strictEqual(pattern.inspect(), Buffer.from('*').inspect()) + assert.strictEqual(channel.inspect(), Buffer.from('/foo').inspect()) + assert.strictEqual(message.inspect(), Buffer.from('hello world').inspect()) + sub2.quit(done) + }) + }) + }) + }) - it('sub executes callback when unsubscribe is called and there are no subscriptions', function (done) { - sub.unsubscribe(helper.isUnSubscribe(0, [], done)); - }); + it('allows to listen to pmessageBuffer and pmessage', function (done) { + var end = helper.callFuncAfter(done, 6) + var data = Array(10000).join('äüs^öéÉÉ`e') + sub.set('foo', data, function () { + sub.get('foo') + sub.stream.once('data', function () { + assert.strictEqual(sub.messageBuffers, false) + assert.strictEqual(sub.shouldBuffer, false) + sub.on('pmessageBuffer', function (pattern, channel, message) { + assert.strictEqual(pattern.inspect(), Buffer.from('*').inspect()) + assert.strictEqual(channel.inspect(), Buffer.from('/foo').inspect()) + sub.quit(end) + }) + assert.notStrictEqual(sub.messageBuffers, sub.buffers) + }) + var batch = sub.batch() + batch.psubscribe('*') + batch.subscribe('/foo') + batch.unsubscribe('/foo') + batch.unsubscribe(helper.isDeepEqual([1, []])) + batch.subscribe(['/foo'], helper.isDeepEqual([2, ['/foo']])) + batch.exec(function () { + // There's one subscriber to this channel + pub.pubsub('numsub', '/foo', helper.isDeepEqual(['/foo', 1], end)) + // There's exactly one channel that is listened too + pub.pubsub('channels', helper.isDeepEqual(['/foo'], end)) + // One pattern is active + pub.pubsub('numpat', helper.isNumber(1, end)) + pub.publish('/foo', 'hello world', helper.isNumber(2)) + }) + // Either messageBuffers or buffers has to be true, but not both at the same time + sub.on('pmessage', function (pattern, channel, message) { + assert.strictEqual(pattern, '*') + assert.strictEqual(channel, '/foo') + assert.strictEqual(message, 'hello world') + end() + }) + sub.on('message', function (channel, message) { + assert.strictEqual(channel, '/foo') + assert.strictEqual(message, 'hello world') + end() + }) + }) + }) + }) - it('pub executes callback when unsubscribe is called and there are no subscriptions', function (done) { - pub.unsubscribe(helper.isUnSubscribe(0, [])); - pub.get('foo', done); - }); - }); + describe('punsubscribe', function () { + it('does not complain when punsubscribe is called and there are no subscriptions', function () { + sub.punsubscribe() + }) - describe('psubscribe', function () { - it('allows all channels to be subscribed to using a * pattern', function (done) { - var sub2 = redis.createClient({ - returnBuffers: true - }); - sub.subscribe('/foo', function () { - sub2.on('ready', function () { - sub2.batch().psubscribe('*', helper.isUnSubscribe(1, '*')).exec(); - sub2.subscribe('/foo', function () { - pub.pubsub('numsub', '/foo', function (err, res) { - assert.deepStrictEqual(res, ['/foo', 2]); - }); - // sub2 is counted twice as it subscribed with psubscribe and subscribe - pub.publish('/foo', 'hello world', helper.isNumber(3)); - }); - sub2.on('pmessage', function (pattern, channel, message) { - assert.strictEqual(pattern.inspect(), new Buffer('*').inspect()); - assert.strictEqual(channel.inspect(), new Buffer('/foo').inspect()); - assert.strictEqual(message.inspect(), new Buffer('hello world').inspect()); - sub2.quit(done); - }); - }); - }); - }); + it('executes callback when punsubscribe is called and there are no subscriptions', function (done) { + pub.batch().punsubscribe(helper.isDeepEqual([0, []])).exec(done) + }) + }) - it('allows to listen to pmessageBuffer and pmessage', function (done) { - var end = helper.callFuncAfter(done, 6); - var data = Array(10000).join('äüs^öéÉÉ`e'); - sub.set('foo', data, function () { - sub.get('foo'); - sub.stream.once('data', function () { - assert.strictEqual(sub.messageBuffers, false); - assert.strictEqual(sub.shouldBuffer, false); - sub.on('pmessageBuffer', function (pattern, channel, message) { - assert.strictEqual(pattern.inspect(), new Buffer('*').inspect()); - assert.strictEqual(channel.inspect(), new Buffer('/foo').inspect()); - sub.quit(end); - }); - assert.notStrictEqual(sub.messageBuffers, sub.buffers); - }); - var batch = sub.batch(); - batch.psubscribe('*'); - batch.subscribe('/foo'); - batch.unsubscribe('/foo'); - batch.unsubscribe(helper.isUnSubscribe(1, [])); - batch.subscribe(['/foo'], helper.isUnSubscribe(2, '/foo')); - batch.exec(function () { - pub.pubsub('numsub', '/foo', function (err, res) { - // There's one subscriber to this channel - assert.deepEqual(res, ['/foo', 1]); - end(); - }); - pub.pubsub('channels', function (err, res) { - // There's exactly one channel that is listened too - assert.deepEqual(res, ['/foo']); - end(); - }); - pub.pubsub('numpat', function (err, res) { - // One pattern is active - assert.strictEqual(res, 1); - end(); - }); - pub.publish('/foo', 'hello world', helper.isNumber(2)); - }); - // Either messageBuffers or buffers has to be true, but not both at the same time - sub.on('pmessage', function (pattern, channel, message) { - assert.strictEqual(pattern, '*'); - assert.strictEqual(channel, '/foo'); - assert.strictEqual(message, 'hello world'); - end(); - }); - sub.on('message', function (channel, message) { - assert.strictEqual(channel, '/foo'); - assert.strictEqual(message, 'hello world'); - end(); - }); - }); - }); - }); + describe('fail for other commands while in pub sub mode', function () { + it('return error if only pub sub commands are allowed', function (done) { + sub.subscribe('channel') + // Ping is allowed even if not listed as such! + sub.ping(function (err, res) { + assert.strictEqual(err, null) + assert.strictEqual(res[0], 'pong') + }) + // Get is forbidden + sub.get('foo', function (err, res) { + assert(/^ERR only \(P\)SUBSCRIBE \/ \(P\)UNSUBSCRIBE/.test(err.message)) + assert.strictEqual(err.command, 'GET') + }) + // Quit is allowed + sub.quit(done) + }) - describe('punsubscribe', function () { - it('does not complain when punsubscribe is called and there are no subscriptions', function () { - sub.punsubscribe(); - }); + it('emit error if only pub sub commands are allowed without callback', function (done) { + sub.subscribe('channel') + sub.on('error', function (err) { + assert(/^ERR only \(P\)SUBSCRIBE \/ \(P\)UNSUBSCRIBE/.test(err.message)) + assert.strictEqual(err.command, 'GET') + done() + }) + sub.get('foo') + }) + }) - it('executes callback when punsubscribe is called and there are no subscriptions', function (done) { - pub.batch().punsubscribe(helper.isUnSubscribe(0, [])).exec(done); - }); - }); + it('should not publish a message multiple times per command', function (done) { + var published = {} - describe('fail for other commands while in pub sub mode', function () { - it('return error if only pub sub commands are allowed', function (done) { - sub.subscribe('channel'); - // Ping is allowed even if not listed as such! - sub.ping(function (err, res) { - assert.strictEqual(err, null); - assert.strictEqual(res[0], 'pong'); - }); - // Get is forbidden - sub.get('foo', function (err, res) { - assert(/^ERR only \(P\)SUBSCRIBE \/ \(P\)UNSUBSCRIBE/.test(err.message)); - assert.strictEqual(err.command, 'GET'); - }); - // Quit is allowed - sub.quit(done); - }); + function subscribe (message) { + sub.removeAllListeners('subscribe') + sub.removeAllListeners('message') + sub.removeAllListeners('unsubscribe') + sub.on('subscribe', function () { + pub.publish('/foo', message) + }) + sub.on('message', function (channel, message) { + if (published[message]) { + done(new Error('Message published more than once.')) + } + published[message] = true + }) + sub.on('unsubscribe', function (channel, count) { + assert.strictEqual(count, 0) + }) + sub.subscribe('/foo') + } - it('emit error if only pub sub commands are allowed without callback', function (done) { - sub.subscribe('channel'); - sub.on('error', function (err) { - assert(/^ERR only \(P\)SUBSCRIBE \/ \(P\)UNSUBSCRIBE/.test(err.message)); - assert.strictEqual(err.command, 'GET'); - done(); - }); - sub.get('foo'); - }); - }); + subscribe('hello') - it('should not publish a message multiple times per command', function (done) { - var published = {}; + setTimeout(function () { + sub.unsubscribe() + setTimeout(function () { + subscribe('world') + setTimeout(done, 50) + }, 40) + }, 40) + }) - function subscribe (message) { - sub.removeAllListeners('subscribe'); - sub.removeAllListeners('message'); - sub.removeAllListeners('unsubscribe'); - sub.on('subscribe', function () { - pub.publish('/foo', message); - }); - sub.on('message', function (channel, message) { - if (published[message]) { - done(new Error('Message published more than once.')); - } - published[message] = true; - }); - sub.on('unsubscribe', function (channel, count) { - assert.strictEqual(count, 0); - }); - sub.subscribe('/foo'); - } + it('should not publish a message without any publish command', function (done) { + pub.set('foo', 'message') + pub.set('bar', 'hello') + pub.mget('foo', 'bar') + pub.subscribe('channel', function () { + setTimeout(done, 50) + }) + pub.on('message', function (msg) { + done(new Error('This message should not have been published: ' + msg)) + }) + }) - subscribe('hello'); + it('arguments variants', function (done) { + sub.batch() + .info(['stats']) + .info() + .client('KILL', ['type', 'pubsub']) + .client('KILL', ['type', 'pubsub'], function () {}) + .unsubscribe() + .psubscribe(['pattern:*']) + .punsubscribe('unknown*') + .punsubscribe(['pattern:*']) + .exec(function (err, res) { + if (err) throw err + sub.client('kill', ['type', 'pubsub']) + sub.psubscribe('*') + sub.punsubscribe('pa*') + sub.punsubscribe(['a', '*'], done) + }) + }) - setTimeout(function () { - sub.unsubscribe(); - setTimeout(function () { - subscribe('world'); - setTimeout(done, 50); - }, 40); - }, 40); - }); - - it('should not publish a message without any publish command', function (done) { - pub.set('foo', 'message'); - pub.set('bar', 'hello'); - pub.mget('foo', 'bar'); - pub.subscribe('channel', function () { - setTimeout(done, 50); - }); - pub.on('message', function (msg) { - done(new Error('This message should not have been published: ' + msg)); - }); - }); - - it('arguments variants', function (done) { - sub.batch() - .info(['stats']) - .info() - .client('KILL', ['type', 'pubsub']) - .client('KILL', ['type', 'pubsub'], function () {}) - .unsubscribe() - .psubscribe(['pattern:*']) - .punsubscribe('unknown*') - .punsubscribe(['pattern:*']) - .exec(function (err, res) { - sub.client('kill', ['type', 'pubsub']); - sub.psubscribe('*'); - sub.punsubscribe('pa*'); - sub.punsubscribe(['a', '*'], done); - }); - }); - - afterEach(function () { - // Explicitly ignore still running commands - pub.end(false); - sub.end(false); - }); - }); - }); -}); + afterEach(function () { + // Explicitly ignore still running commands + pub.end(false) + sub.end(false) + }) + }) + }) +}) diff --git a/test/rename.spec.js b/test/rename.spec.js index e7be8b542c..3897291557 100644 --- a/test/rename.spec.js +++ b/test/rename.spec.js @@ -1,145 +1,135 @@ -'use strict'; +'use strict' -var assert = require('assert'); -var config = require('./lib/config'); -var helper = require('./helper'); -var redis = config.redis; +var assert = require('assert') +var config = require('./lib/config') +var helper = require('./helper') +var redis = config.redis -if (process.platform === 'win32') { - // TODO: Fix redis process spawn on windows - return; -} - -describe('rename commands', function () { + // TODO: Fix redis process spawn on windows +if (process.platform !== 'win32') { + describe('rename commands', function () { before(function (done) { - helper.stopRedis(function () { - helper.startRedis('./conf/rename.conf', done); - }); - }); + helper.stopRedis(function () { + helper.startRedis('./conf/rename.conf', done) + }) + }) helper.allTests(function (ip, args) { + describe('using ' + ip, function () { + var client = null - describe('using ' + ip, function () { - var client = null; + beforeEach(function (done) { + if (helper.redisProcess().spawnFailed()) return done() + client = redis.createClient({ + renameCommands: { + set: '807081f5afa96845a02816a28b7258c3', + GETRANGE: '9e3102b15cf231c4e9e940f284744fe0' + } + }) - beforeEach(function (done) { - if (helper.redisProcess().spawnFailed()) return done(); - client = redis.createClient({ - renameCommands: { - set: '807081f5afa96845a02816a28b7258c3', - GETRANGE: '9e3102b15cf231c4e9e940f284744fe0' - } - }); + client.on('ready', function () { + client.flushdb(done) + }) + }) - client.on('ready', function () { - client.flushdb(done); - }); - }); + afterEach(function () { + if (helper.redisProcess().spawnFailed()) return + client.end(true) + }) - afterEach(function () { - if (helper.redisProcess().spawnFailed()) return; - client.end(true); - }); + it('allows to use renamed functions', function (done) { + if (helper.redisProcess().spawnFailed()) this.skip() - it('allows to use renamed functions', function (done) { - if (helper.redisProcess().spawnFailed()) this.skip(); + client.set('key', 'value', helper.isString('OK')) - client.set('key', 'value', function (err, reply) { - assert.strictEqual(reply, 'OK'); - }); + client.get('key', function (err, reply) { + assert.strictEqual(err.message, 'ERR unknown command \'get\'') + assert.strictEqual(err.command, 'GET') + assert.strictEqual(reply, undefined) + }) - client.get('key', function (err, reply) { - assert.strictEqual(err.message, "ERR unknown command 'get'"); - assert.strictEqual(err.command, 'GET'); - assert.strictEqual(reply, undefined); - }); + client.getrange('key', 1, -1, function (err, reply) { + assert.strictEqual(reply, 'alue') + assert.strictEqual(err, null) + done() + }) + }) - client.getrange('key', 1, -1, function (err, reply) { - assert.strictEqual(reply, 'alue'); - assert.strictEqual(err, null); - done(); - }); - }); + it('should also work with batch', function (done) { + if (helper.redisProcess().spawnFailed()) this.skip() - it('should also work with batch', function (done) { - if (helper.redisProcess().spawnFailed()) this.skip(); + client.batch([['set', 'key', 'value']]).exec(helper.isDeepEqual(['OK'])) - client.batch([['set', 'key', 'value']]).exec(function (err, res) { - assert.strictEqual(res[0], 'OK'); - }); + var batch = client.batch() + batch.getrange('key', 1, -1) + batch.exec(function (err, res) { + assert(!err) + assert.strictEqual(res.length, 1) + assert.strictEqual(res[0], 'alue') + done() + }) + }) - var batch = client.batch(); - batch.getrange('key', 1, -1); - batch.exec(function (err, res) { - assert(!err); - assert.strictEqual(res.length, 1); - assert.strictEqual(res[0], 'alue'); - done(); - }); - }); + it('should also work with multi', function (done) { + if (helper.redisProcess().spawnFailed()) this.skip() - it('should also work with multi', function (done) { - if (helper.redisProcess().spawnFailed()) this.skip(); + client.multi([['set', 'key', 'value']]).exec(helper.isDeepEqual(['OK'])) - client.multi([['set', 'key', 'value']]).exec(function (err, res) { - assert.strictEqual(res[0], 'OK'); - }); + var multi = client.multi() + multi.getrange('key', 1, -1) + multi.exec(function (err, res) { + assert(!err) + assert.strictEqual(res.length, 1) + assert.strictEqual(res[0], 'alue') + done() + }) + }) - var multi = client.multi(); - multi.getrange('key', 1, -1); - multi.exec(function (err, res) { - assert(!err); - assert.strictEqual(res.length, 1); - assert.strictEqual(res[0], 'alue'); - done(); - }); - }); + it('should also work with multi and abort transaction', function (done) { + if (helper.redisProcess().spawnFailed()) this.skip() - it('should also work with multi and abort transaction', function (done) { - if (helper.redisProcess().spawnFailed()) this.skip(); + var multi = client.multi() + multi.get('key') + multi.getrange('key', 1, -1, function (err, reply) { + assert.strictEqual(reply, 'alue') + assert.strictEqual(err, null) + }) + multi.exec(function (err, res) { + assert(err) + assert.strictEqual(err.message, 'EXECABORT Transaction discarded because of previous errors.') + assert.strictEqual(err.errors[0].message, 'ERR unknown command \'get\'') + assert.strictEqual(err.errors[0].command, 'GET') + assert.strictEqual(err.code, 'EXECABORT') + assert.strictEqual(err.errors[0].code, 'ERR') + done() + }) + }) - var multi = client.multi(); - multi.get('key'); - multi.getrange('key', 1, -1, function (err, reply) { - assert.strictEqual(reply, 'alue'); - assert.strictEqual(err, null); - }); - multi.exec(function (err, res) { - assert(err); - assert.strictEqual(err.message, 'EXECABORT Transaction discarded because of previous errors.'); - assert.strictEqual(err.errors[0].message, "ERR unknown command 'get'"); - assert.strictEqual(err.errors[0].command, 'GET'); - assert.strictEqual(err.code, 'EXECABORT'); - assert.strictEqual(err.errors[0].code, 'ERR'); - done(); - }); - }); + it('should also work prefixed commands', function (done) { + if (helper.redisProcess().spawnFailed()) this.skip() - it('should also work prefixed commands', function (done) { - if (helper.redisProcess().spawnFailed()) this.skip(); - - client.end(true); - client = redis.createClient({ - renameCommands: { - set: '807081f5afa96845a02816a28b7258c3' - }, - prefix: 'baz' - }); - client.set('foo', 'bar'); - client.keys('*', function (err, reply) { - assert.strictEqual(reply[0], 'bazfoo'); - assert.strictEqual(err, null); - done(); - }); - }); - - }); - }); + client.end(true) + client = redis.createClient({ + renameCommands: { + set: '807081f5afa96845a02816a28b7258c3' + }, + prefix: 'baz' + }) + client.set('foo', 'bar') + client.keys('*', function (err, reply) { + assert.strictEqual(reply[0], 'bazfoo') + assert.strictEqual(err, null) + done() + }) + }) + }) + }) after(function (done) { - if (helper.redisProcess().spawnFailed()) return done(); - helper.stopRedis(function () { - helper.startRedis('./conf/redis.conf', done); - }); - }); -}); + if (helper.redisProcess().spawnFailed()) return done() + helper.stopRedis(function () { + helper.startRedis('./conf/redis.conf', done) + }) + }) + }) +} diff --git a/test/return_buffers.spec.js b/test/return_buffers.spec.js index e0296ba9ae..ce2a420805 100644 --- a/test/return_buffers.spec.js +++ b/test/return_buffers.spec.js @@ -1,293 +1,292 @@ -'use strict'; +'use strict' -var assert = require('assert'); -var config = require('./lib/config'); -var helper = require('./helper'); -var redis = config.redis; +var Buffer = require('safe-buffer').Buffer +var assert = require('assert') +var config = require('./lib/config') +var helper = require('./helper') +var redis = config.redis describe('returnBuffers', function () { + helper.allTests(function (ip, basicArgs) { + describe('using ' + ip, function () { + var client + var args = config.configureClient(ip, { + returnBuffers: true, + detectBuffers: true + }) - helper.allTests(function (ip, basicArgs) { + beforeEach(function (done) { + client = redis.createClient.apply(null, args) + var i = 1 + if (args[2].detectBuffers) { + // Test if detectBuffer option was deactivated + assert.strictEqual(client.options.detectBuffers, false) + args[2].detectBuffers = false + i++ + } + var end = helper.callFuncAfter(done, i) + client.on('warning', function (msg) { + assert.strictEqual(msg, 'WARNING: You activated returnBuffers and detectBuffers at the same time. The return value is always going to be a buffer.') + end() + }) + client.once('error', done) + client.once('connect', function () { + client.flushdb(function (err) { + client.hmset('hash key 2', 'key 1', 'val 1', 'key 2', 'val 2') + client.set('string key 1', 'string value') + end(err) + }) + }) + }) - describe('using ' + ip, function () { - var client; - var args = config.configureClient(ip, { - returnBuffers: true, - detectBuffers: true - }); + describe('get', function () { + describe('first argument is a string', function () { + it('returns a buffer', function (done) { + client.get('string key 1', function (err, reply) { + assert.strictEqual(true, Buffer.isBuffer(reply)) + assert.strictEqual('', reply.inspect()) + return done(err) + }) + }) - beforeEach(function (done) { - client = redis.createClient.apply(null, args); - var i = 1; - if (args[2].detectBuffers) { - // Test if detectBuffer option was deactivated - assert.strictEqual(client.options.detectBuffers, false); - args[2].detectBuffers = false; - i++; - } - var end = helper.callFuncAfter(done, i); - client.on('warning', function (msg) { - assert.strictEqual(msg, 'WARNING: You activated returnBuffers and detectBuffers at the same time. The return value is always going to be a buffer.'); - end(); - }); - client.once('error', done); - client.once('connect', function () { - client.flushdb(function (err) { - client.hmset('hash key 2', 'key 1', 'val 1', 'key 2', 'val 2'); - client.set('string key 1', 'string value'); - end(err); - }); - }); - }); + it('returns a bufffer when executed as part of transaction', function (done) { + client.multi().get('string key 1').exec(function (err, reply) { + assert.strictEqual(1, reply.length) + assert.strictEqual(true, Buffer.isBuffer(reply[0])) + assert.strictEqual('', reply[0].inspect()) + return done(err) + }) + }) + }) + }) - describe('get', function () { - describe('first argument is a string', function () { - it('returns a buffer', function (done) { - client.get('string key 1', function (err, reply) { - assert.strictEqual(true, Buffer.isBuffer(reply)); - assert.strictEqual('', reply.inspect()); - return done(err); - }); - }); + describe('multi.hget', function () { + it('returns buffers', function (done) { + client.multi() + .hget('hash key 2', 'key 1') + .hget(Buffer.from('hash key 2'), 'key 1') + .hget('hash key 2', Buffer.from('key 2')) + .hget('hash key 2', 'key 2') + .exec(function (err, reply) { + assert.strictEqual(true, Array.isArray(reply)) + assert.strictEqual(4, reply.length) + assert.strictEqual('', reply[0].inspect()) + assert.strictEqual(true, Buffer.isBuffer(reply[1])) + assert.strictEqual('', reply[1].inspect()) + assert.strictEqual(true, Buffer.isBuffer(reply[2])) + assert.strictEqual('', reply[2].inspect()) + assert.strictEqual(true, Buffer.isBuffer(reply[3])) + assert.strictEqual('', reply[3].inspect()) + return done(err) + }) + }) + }) - it('returns a bufffer when executed as part of transaction', function (done) { - client.multi().get('string key 1').exec(function (err, reply) { - assert.strictEqual(1, reply.length); - assert.strictEqual(true, Buffer.isBuffer(reply[0])); - assert.strictEqual('', reply[0].inspect()); - return done(err); - }); - }); - }); - }); + describe('batch.hget', function () { + it('returns buffers', function (done) { + client.batch() + .hget('hash key 2', 'key 1') + .hget(Buffer.from('hash key 2'), 'key 1') + .hget('hash key 2', Buffer.from('key 2')) + .hget('hash key 2', 'key 2') + .exec(function (err, reply) { + assert.strictEqual(true, Array.isArray(reply)) + assert.strictEqual(4, reply.length) + assert.strictEqual('', reply[0].inspect()) + assert.strictEqual(true, Buffer.isBuffer(reply[1])) + assert.strictEqual('', reply[1].inspect()) + assert.strictEqual(true, Buffer.isBuffer(reply[2])) + assert.strictEqual('', reply[2].inspect()) + assert.strictEqual(true, Buffer.isBuffer(reply[3])) + assert.strictEqual('', reply[3].inspect()) + return done(err) + }) + }) + }) - describe('multi.hget', function () { - it('returns buffers', function (done) { - client.multi() - .hget('hash key 2', 'key 1') - .hget(new Buffer('hash key 2'), 'key 1') - .hget('hash key 2', new Buffer('key 2')) - .hget('hash key 2', 'key 2') - .exec(function (err, reply) { - assert.strictEqual(true, Array.isArray(reply)); - assert.strictEqual(4, reply.length); - assert.strictEqual('', reply[0].inspect()); - assert.strictEqual(true, Buffer.isBuffer(reply[1])); - assert.strictEqual('', reply[1].inspect()); - assert.strictEqual(true, Buffer.isBuffer(reply[2])); - assert.strictEqual('', reply[2].inspect()); - assert.strictEqual(true, Buffer.isBuffer(reply[3])); - assert.strictEqual('', reply[3].inspect()); - return done(err); - }); - }); - }); + describe('hmget', function () { + describe('first argument is a string', function () { + it('handles array of strings with undefined values in transaction (repro #344)', function (done) { + client.multi().hmget('hash key 2', 'key 3', 'key 4').exec(function (err, reply) { + assert.strictEqual(true, Array.isArray(reply)) + assert.strictEqual(1, reply.length) + assert.strictEqual(2, reply[0].length) + assert.strictEqual(null, reply[0][0]) + assert.strictEqual(null, reply[0][1]) + return done(err) + }) + }) + }) - describe('batch.hget', function () { - it('returns buffers', function (done) { - client.batch() - .hget('hash key 2', 'key 1') - .hget(new Buffer('hash key 2'), 'key 1') - .hget('hash key 2', new Buffer('key 2')) - .hget('hash key 2', 'key 2') - .exec(function (err, reply) { - assert.strictEqual(true, Array.isArray(reply)); - assert.strictEqual(4, reply.length); - assert.strictEqual('', reply[0].inspect()); - assert.strictEqual(true, Buffer.isBuffer(reply[1])); - assert.strictEqual('', reply[1].inspect()); - assert.strictEqual(true, Buffer.isBuffer(reply[2])); - assert.strictEqual('', reply[2].inspect()); - assert.strictEqual(true, Buffer.isBuffer(reply[3])); - assert.strictEqual('', reply[3].inspect()); - return done(err); - }); - }); - }); + describe('first argument is a buffer', function () { + it('returns buffers for keys requested', function (done) { + client.hmget(Buffer.from('hash key 2'), 'key 1', 'key 2', function (err, reply) { + assert.strictEqual(true, Array.isArray(reply)) + assert.strictEqual(2, reply.length) + assert.strictEqual(true, Buffer.isBuffer(reply[0])) + assert.strictEqual(true, Buffer.isBuffer(reply[1])) + assert.strictEqual('', reply[0].inspect()) + assert.strictEqual('', reply[1].inspect()) + return done(err) + }) + }) - describe('hmget', function () { - describe('first argument is a string', function () { - it('handles array of strings with undefined values in transaction (repro #344)', function (done) { - client.multi().hmget('hash key 2', 'key 3', 'key 4').exec(function (err, reply) { - assert.strictEqual(true, Array.isArray(reply)); - assert.strictEqual(1, reply.length); - assert.strictEqual(2, reply[0].length); - assert.equal(null, reply[0][0]); - assert.equal(null, reply[0][1]); - return done(err); - }); - }); - }); + it('returns buffers for keys requested in transaction', function (done) { + client.multi().hmget(Buffer.from('hash key 2'), 'key 1', 'key 2').exec(function (err, reply) { + assert.strictEqual(true, Array.isArray(reply)) + assert.strictEqual(1, reply.length) + assert.strictEqual(2, reply[0].length) + assert.strictEqual(true, Buffer.isBuffer(reply[0][0])) + assert.strictEqual(true, Buffer.isBuffer(reply[0][1])) + assert.strictEqual('', reply[0][0].inspect()) + assert.strictEqual('', reply[0][1].inspect()) + return done(err) + }) + }) - describe('first argument is a buffer', function () { - it('returns buffers for keys requested', function (done) { - client.hmget(new Buffer('hash key 2'), 'key 1', 'key 2', function (err, reply) { - assert.strictEqual(true, Array.isArray(reply)); - assert.strictEqual(2, reply.length); - assert.strictEqual(true, Buffer.isBuffer(reply[0])); - assert.strictEqual(true, Buffer.isBuffer(reply[1])); - assert.strictEqual('', reply[0].inspect()); - assert.strictEqual('', reply[1].inspect()); - return done(err); - }); - }); + it('returns buffers for keys requested in .batch', function (done) { + client.batch().hmget(Buffer.from('hash key 2'), 'key 1', 'key 2').exec(function (err, reply) { + assert.strictEqual(true, Array.isArray(reply)) + assert.strictEqual(1, reply.length) + assert.strictEqual(2, reply[0].length) + assert.strictEqual(true, Buffer.isBuffer(reply[0][0])) + assert.strictEqual(true, Buffer.isBuffer(reply[0][1])) + assert.strictEqual('', reply[0][0].inspect()) + assert.strictEqual('', reply[0][1].inspect()) + return done(err) + }) + }) + }) + }) - it('returns buffers for keys requested in transaction', function (done) { - client.multi().hmget(new Buffer('hash key 2'), 'key 1', 'key 2').exec(function (err, reply) { - assert.strictEqual(true, Array.isArray(reply)); - assert.strictEqual(1, reply.length); - assert.strictEqual(2, reply[0].length); - assert.strictEqual(true, Buffer.isBuffer(reply[0][0])); - assert.strictEqual(true, Buffer.isBuffer(reply[0][1])); - assert.strictEqual('', reply[0][0].inspect()); - assert.strictEqual('', reply[0][1].inspect()); - return done(err); - }); - }); + describe('hgetall', function (done) { + describe('first argument is a string', function () { + it('returns buffer values', function (done) { + client.hgetall('hash key 2', function (err, reply) { + assert.strictEqual('object', typeof reply) + assert.strictEqual(2, Object.keys(reply).length) + assert.strictEqual('', reply['key 1'].inspect()) + assert.strictEqual('', reply['key 2'].inspect()) + return done(err) + }) + }) - it('returns buffers for keys requested in .batch', function (done) { - client.batch().hmget(new Buffer('hash key 2'), 'key 1', 'key 2').exec(function (err, reply) { - assert.strictEqual(true, Array.isArray(reply)); - assert.strictEqual(1, reply.length); - assert.strictEqual(2, reply[0].length); - assert.strictEqual(true, Buffer.isBuffer(reply[0][0])); - assert.strictEqual(true, Buffer.isBuffer(reply[0][1])); - assert.strictEqual('', reply[0][0].inspect()); - assert.strictEqual('', reply[0][1].inspect()); - return done(err); - }); - }); - }); - }); + it('returns buffer values when executed in transaction', function (done) { + client.multi().hgetall('hash key 2').exec(function (err, reply) { + assert.strictEqual(1, reply.length) + assert.strictEqual('object', typeof reply[0]) + assert.strictEqual(2, Object.keys(reply[0]).length) + assert.strictEqual('', reply[0]['key 1'].inspect()) + assert.strictEqual('', reply[0]['key 2'].inspect()) + return done(err) + }) + }) - describe('hgetall', function (done) { - describe('first argument is a string', function () { - it('returns buffer values', function (done) { - client.hgetall('hash key 2', function (err, reply) { - assert.strictEqual('object', typeof reply); - assert.strictEqual(2, Object.keys(reply).length); - assert.strictEqual('', reply['key 1'].inspect()); - assert.strictEqual('', reply['key 2'].inspect()); - return done(err); - }); - }); + it('returns buffer values when executed in .batch', function (done) { + client.batch().hgetall('hash key 2').exec(function (err, reply) { + assert.strictEqual(1, reply.length) + assert.strictEqual('object', typeof reply[0]) + assert.strictEqual(2, Object.keys(reply[0]).length) + assert.strictEqual('', reply[0]['key 1'].inspect()) + assert.strictEqual('', reply[0]['key 2'].inspect()) + return done(err) + }) + }) + }) - it('returns buffer values when executed in transaction', function (done) { - client.multi().hgetall('hash key 2').exec(function (err, reply) { - assert.strictEqual(1, reply.length); - assert.strictEqual('object', typeof reply[0]); - assert.strictEqual(2, Object.keys(reply[0]).length); - assert.strictEqual('', reply[0]['key 1'].inspect()); - assert.strictEqual('', reply[0]['key 2'].inspect()); - return done(err); - }); - }); + describe('first argument is a buffer', function () { + it('returns buffer values', function (done) { + client.hgetall(Buffer.from('hash key 2'), function (err, reply) { + assert.strictEqual(null, err) + assert.strictEqual('object', typeof reply) + assert.strictEqual(2, Object.keys(reply).length) + assert.strictEqual(true, Buffer.isBuffer(reply['key 1'])) + assert.strictEqual(true, Buffer.isBuffer(reply['key 2'])) + assert.strictEqual('', reply['key 1'].inspect()) + assert.strictEqual('', reply['key 2'].inspect()) + return done(err) + }) + }) - it('returns buffer values when executed in .batch', function (done) { - client.batch().hgetall('hash key 2').exec(function (err, reply) { - assert.strictEqual(1, reply.length); - assert.strictEqual('object', typeof reply[0]); - assert.strictEqual(2, Object.keys(reply[0]).length); - assert.strictEqual('', reply[0]['key 1'].inspect()); - assert.strictEqual('', reply[0]['key 2'].inspect()); - return done(err); - }); - }); - }); + it('returns buffer values when executed in transaction', function (done) { + client.multi().hgetall(Buffer.from('hash key 2')).exec(function (err, reply) { + assert.strictEqual(1, reply.length) + assert.strictEqual('object', typeof reply[0]) + assert.strictEqual(2, Object.keys(reply[0]).length) + assert.strictEqual(true, Buffer.isBuffer(reply[0]['key 1'])) + assert.strictEqual(true, Buffer.isBuffer(reply[0]['key 2'])) + assert.strictEqual('', reply[0]['key 1'].inspect()) + assert.strictEqual('', reply[0]['key 2'].inspect()) + return done(err) + }) + }) - describe('first argument is a buffer', function () { - it('returns buffer values', function (done) { - client.hgetall(new Buffer('hash key 2'), function (err, reply) { - assert.strictEqual(null, err); - assert.strictEqual('object', typeof reply); - assert.strictEqual(2, Object.keys(reply).length); - assert.strictEqual(true, Buffer.isBuffer(reply['key 1'])); - assert.strictEqual(true, Buffer.isBuffer(reply['key 2'])); - assert.strictEqual('', reply['key 1'].inspect()); - assert.strictEqual('', reply['key 2'].inspect()); - return done(err); - }); - }); + it('returns buffer values when executed in .batch', function (done) { + client.batch().hgetall(Buffer.from('hash key 2')).exec(function (err, reply) { + assert.strictEqual(1, reply.length) + assert.strictEqual('object', typeof reply[0]) + assert.strictEqual(2, Object.keys(reply[0]).length) + assert.strictEqual(true, Buffer.isBuffer(reply[0]['key 1'])) + assert.strictEqual(true, Buffer.isBuffer(reply[0]['key 2'])) + assert.strictEqual('', reply[0]['key 1'].inspect()) + assert.strictEqual('', reply[0]['key 2'].inspect()) + return done(err) + }) + }) + }) + }) - it('returns buffer values when executed in transaction', function (done) { - client.multi().hgetall(new Buffer('hash key 2')).exec(function (err, reply) { - assert.strictEqual(1, reply.length); - assert.strictEqual('object', typeof reply[0]); - assert.strictEqual(2, Object.keys(reply[0]).length); - assert.strictEqual(true, Buffer.isBuffer(reply[0]['key 1'])); - assert.strictEqual(true, Buffer.isBuffer(reply[0]['key 2'])); - assert.strictEqual('', reply[0]['key 1'].inspect()); - assert.strictEqual('', reply[0]['key 2'].inspect()); - return done(err); - }); - }); + describe('publish/subscribe', function (done) { + var pub + var sub + var channel = 'test channel' + var message = Buffer.from('test message') - it('returns buffer values when executed in .batch', function (done) { - client.batch().hgetall(new Buffer('hash key 2')).exec(function (err, reply) { - assert.strictEqual(1, reply.length); - assert.strictEqual('object', typeof reply[0]); - assert.strictEqual(2, Object.keys(reply[0]).length); - assert.strictEqual(true, Buffer.isBuffer(reply[0]['key 1'])); - assert.strictEqual(true, Buffer.isBuffer(reply[0]['key 2'])); - assert.strictEqual('', reply[0]['key 1'].inspect()); - assert.strictEqual('', reply[0]['key 2'].inspect()); - return done(err); - }); - }); - }); - }); + var args = config.configureClient(ip, { + returnBuffers: true + }) - describe('publish/subscribe', function (done) { - var pub; - var sub; - var channel = 'test channel'; - var message = new Buffer('test message'); + beforeEach(function (done) { + var pubConnected + var subConnected - var args = config.configureClient(ip, { - returnBuffers: true - }); + pub = redis.createClient.apply(redis.createClient, basicArgs) + sub = redis.createClient.apply(null, args) + pub.once('connect', function () { + pub.flushdb(function () { + pubConnected = true + if (subConnected) { + done() + } + }) + }) + sub.once('connect', function () { + subConnected = true + if (pubConnected) { + done() + } + }) + }) - beforeEach(function (done) { - var pubConnected; - var subConnected; + it('receives buffer messages', function (done) { + sub.on('subscribe', function (chnl, count) { + pub.publish(channel, message) + }) - pub = redis.createClient.apply(redis.createClient, basicArgs); - sub = redis.createClient.apply(null, args); - pub.once('connect', function () { - pub.flushdb(function () { - pubConnected = true; - if (subConnected) { - done(); - } - }); - }); - sub.once('connect', function () { - subConnected = true; - if (pubConnected) { - done(); - } - }); - }); + sub.on('message', function (chnl, msg) { + assert.strictEqual(true, Buffer.isBuffer(msg)) + assert.strictEqual('', msg.inspect()) + return done() + }) - it('receives buffer messages', function (done) { - sub.on('subscribe', function (chnl, count) { - pub.publish(channel, message); - }); + sub.subscribe(channel) + }) - sub.on('message', function (chnl, msg) { - assert.strictEqual(true, Buffer.isBuffer(msg)); - assert.strictEqual('', msg.inspect()); - return done(); - }); - - sub.subscribe(channel); - }); - - afterEach(function () { - sub.end(true); - pub.end(true); - }); - }); - }); - }); -}); + afterEach(function () { + sub.end(true) + pub.end(true) + }) + }) + }) + }) +}) diff --git a/test/tls.spec.js b/test/tls.spec.js index d3f3beb541..7c8e3d0d8f 100644 --- a/test/tls.spec.js +++ b/test/tls.spec.js @@ -1,132 +1,127 @@ -'use strict'; +'use strict' -var assert = require('assert'); -var config = require('./lib/config'); -var fs = require('fs'); -var helper = require('./helper'); -var path = require('path'); -var redis = config.redis; -var utils = require('../lib/utils'); +var assert = require('assert') +var config = require('./lib/config') +var fs = require('fs') +var helper = require('./helper') +var path = require('path') +var redis = config.redis +var utils = require('../lib/utils') var tlsOptions = { - servername: 'redis.js.org', - rejectUnauthorized: true, - ca: [ String(fs.readFileSync(path.resolve(__dirname, './conf/redis.js.org.cert'))) ] -}; + servername: 'redis.js.org', + rejectUnauthorized: true, + ca: [ String(fs.readFileSync(path.resolve(__dirname, './conf/redis.js.org.cert'))) ] +} -var tlsPort = 6380; +var tlsPort = 6380 // Use skip instead of returning to indicate what tests really got skipped -var skip = false; +var skip = false // Wait until stunnel4 is in the travis whitelist // Check: https://github.com/travis-ci/apt-package-whitelist/issues/403 // If this is merged, remove the travis env checks describe.skip('TLS connection tests', function () { + before(function (done) { + // Print the warning when the tests run instead of while starting mocha + if (process.platform === 'win32') { + skip = true + console.warn('\nStunnel tests do not work on windows atm. If you think you can fix that, it would be warmly welcome.\n') + } else if (process.env.TRAVIS === 'true') { + skip = true + console.warn('\nTravis does not support stunnel right now. Skipping tests.\nCheck: https://github.com/travis-ci/apt-package-whitelist/issues/403\n') + } + if (skip) return done() + helper.stopStunnel(function () { + helper.startStunnel(done) + }) + }) - before(function (done) { - // Print the warning when the tests run instead of while starting mocha - if (process.platform === 'win32') { - skip = true; - console.warn('\nStunnel tests do not work on windows atm. If you think you can fix that, it would be warmly welcome.\n'); - } else if (process.env.TRAVIS === 'true') { - skip = true; - console.warn('\nTravis does not support stunnel right now. Skipping tests.\nCheck: https://github.com/travis-ci/apt-package-whitelist/issues/403\n'); + after(function (done) { + if (skip) return done() + helper.stopStunnel(done) + }) + + var client + + afterEach(function () { + if (skip) return + client.end(true) + }) + + describe('on lost connection', function () { + it.skip('emit an error after max retry timeout and do not try to reconnect afterwards', function (done) { + if (skip) this.skip() + var connectTimeout = 500 // in ms + client = redis.createClient({ + connectTimeout: connectTimeout, + port: tlsPort, + tls: tlsOptions + }) + var time = 0 + assert.strictEqual(client.address, '127.0.0.1:' + tlsPort) + + client.once('ready', function () { + helper.killConnection(client) + }) + + client.on('reconnecting', function (params) { + time += params.delay + }) + + client.on('error', function (err) { + if (/Redis connection in broken state: connection timeout.*?exceeded./.test(err.message)) { + process.nextTick(function () { + assert.strictEqual(time, connectTimeout) + assert.strictEqual(client.emittedEnd, true) + assert.strictEqual(client.connected, false) + assert.strictEqual(client.ready, false) + assert.strictEqual(client.closing, true) + assert.strictEqual(time, connectTimeout) + done() + }) } - if (skip) return done(); - helper.stopStunnel(function () { - helper.startStunnel(done); - }); - }); + }) + }) + }) - after(function (done) { - if (skip) return done(); - helper.stopStunnel(done); - }); + describe('when not connected', function () { + it('connect with host and port provided in the tls object', function (done) { + if (skip) this.skip() + var tls = utils.clone(tlsOptions) + tls.port = tlsPort + tls.host = 'localhost' + client = redis.createClient({ + connectTimeout: 1000, + tls: tls + }) - var client; + // verify connection is using TCP, not UNIX socket + assert.strictEqual(client.connectionOptions.host, 'localhost') + assert.strictEqual(client.connectionOptions.port, tlsPort) + assert.strictEqual(client.address, 'localhost:' + tlsPort) + assert(client.stream.encrypted) - afterEach(function () { - if (skip) return; - client.end(true); - }); + client.set('foo', 'bar') + client.get('foo', helper.isString('bar', done)) + }) - describe('on lost connection', function () { - it.skip('emit an error after max retry timeout and do not try to reconnect afterwards', function (done) { - if (skip) this.skip(); - var connectTimeout = 500; // in ms - client = redis.createClient({ - connectTimeout: connectTimeout, - port: tlsPort, - tls: tlsOptions - }); - var time = 0; - assert.strictEqual(client.address, '127.0.0.1:' + tlsPort); - - client.once('ready', function () { - helper.killConnection(client); - }); - - client.on('reconnecting', function (params) { - time += params.delay; - }); - - client.on('error', function (err) { - if (/Redis connection in broken state: connection timeout.*?exceeded./.test(err.message)) { - process.nextTick(function () { - assert.strictEqual(time, connectTimeout); - assert.strictEqual(client.emittedEnd, true); - assert.strictEqual(client.connected, false); - assert.strictEqual(client.ready, false); - assert.strictEqual(client.closing, true); - assert.strictEqual(time, connectTimeout); - done(); - }); - } - }); - }); - }); - - describe('when not connected', function () { - - it('connect with host and port provided in the tls object', function (done) { - if (skip) this.skip(); - var tls = utils.clone(tlsOptions); - tls.port = tlsPort; - tls.host = 'localhost'; - client = redis.createClient({ - connectTimeout: 1000, - tls: tls - }); - - // verify connection is using TCP, not UNIX socket - assert.strictEqual(client.connectionOptions.host, 'localhost'); - assert.strictEqual(client.connectionOptions.port, tlsPort); - assert.strictEqual(client.address, 'localhost:' + tlsPort); - assert(client.stream.encrypted); - - client.set('foo', 'bar'); - client.get('foo', helper.isString('bar', done)); - }); - - it('fails to connect because the cert is not correct', function (done) { - if (skip) this.skip(); - var faultyCert = utils.clone(tlsOptions); - faultyCert.ca = [ String(fs.readFileSync(path.resolve(__dirname, './conf/faulty.cert'))) ]; - client = redis.createClient({ - host: 'localhost', - connectTimeout: 1000, - port: tlsPort, - tls: faultyCert - }); - assert.strictEqual(client.address, 'localhost:' + tlsPort); - client.on('error', function (err) { - assert(/DEPTH_ZERO_SELF_SIGNED_CERT/.test(err.code || err.message), err); - client.end(true); - }); - client.set('foo', 'bar', function (err, res) { - done(res); - }); - }); - - }); -}); + it('fails to connect because the cert is not correct', function (done) { + if (skip) this.skip() + var faultyCert = utils.clone(tlsOptions) + faultyCert.ca = [ String(fs.readFileSync(path.resolve(__dirname, './conf/faulty.cert'))) ] + client = redis.createClient({ + host: 'localhost', + connectTimeout: 1000, + port: tlsPort, + tls: faultyCert + }) + assert.strictEqual(client.address, 'localhost:' + tlsPort) + client.on('error', function (err) { + assert(/DEPTH_ZERO_SELF_SIGNED_CERT/.test(err.code || err.message), err) + client.end(true) + }) + client.set('foo', 'bar', helper.isError(done)) + }) + }) +}) diff --git a/test/unify_options.spec.js b/test/unify_options.spec.js index aa034b747d..fa5490d2a3 100644 --- a/test/unify_options.spec.js +++ b/test/unify_options.spec.js @@ -1,241 +1,240 @@ -'use strict'; +'use strict' -var assert = require('assert'); -var unifyOptions = require('../lib/createClient'); -var intercept = require('intercept-stdout'); +var assert = require('assert') +var unifyOptions = require('../lib/createClient') +var intercept = require('intercept-stdout') describe('createClient options', function () { + describe('port as first parameter', function () { + it('pass the options in the second parameter after a port', function () { + var options = unifyOptions(1234, { + option1: true, + option2: function () {} + }) + assert.strictEqual(Object.keys(options).length, 4) + assert(options.option1) + assert.strictEqual(options.port, 1234) + assert.strictEqual(options.host, undefined) + assert.strictEqual(typeof options.option2, 'function') + }) - describe('port as first parameter', function () { - it('pass the options in the second parameter after a port', function () { - var options = unifyOptions(1234, { - option1: true, - option2: function () {} - }); - assert.strictEqual(Object.keys(options).length, 4); - assert(options.option1); - assert.strictEqual(options.port, 1234); - assert.strictEqual(options.host, undefined); - assert.strictEqual(typeof options.option2, 'function'); - }); + it('pass the options in the third parameter after a port and host being set to null', function () { + var options = unifyOptions(1234, null, { + option1: true, + option2: function () {} + }) + assert.strictEqual(Object.keys(options).length, 4) + assert(options.option1) + assert.strictEqual(options.port, 1234) + assert.strictEqual(options.host, undefined) + assert.strictEqual(typeof options.option2, 'function') + }) - it('pass the options in the third parameter after a port and host being set to null', function () { - var options = unifyOptions(1234, null, { - option1: true, - option2: function () {} - }); - assert.strictEqual(Object.keys(options).length, 4); - assert(options.option1); - assert.strictEqual(options.port, 1234); - assert.strictEqual(options.host, undefined); - assert.strictEqual(typeof options.option2, 'function'); - }); + it('pass the options in the third parameter after a port and host being set to undefined', function () { + var options = unifyOptions(1234, undefined, { + option1: true, + option2: function () {} + }) + assert.strictEqual(Object.keys(options).length, 4) + assert(options.option1) + assert.strictEqual(options.port, 1234) + assert.strictEqual(options.host, undefined) + assert.strictEqual(typeof options.option2, 'function') + }) - it('pass the options in the third parameter after a port and host being set to undefined', function () { - var options = unifyOptions(1234, undefined, { - option1: true, - option2: function () {} - }); - assert.strictEqual(Object.keys(options).length, 4); - assert(options.option1); - assert.strictEqual(options.port, 1234); - assert.strictEqual(options.host, undefined); - assert.strictEqual(typeof options.option2, 'function'); - }); + it('pass the options in the third parameter after a port and host', function () { + var options = unifyOptions('1234', 'localhost', { + option1: true, + option2: function () {} + }) + assert.strictEqual(Object.keys(options).length, 4) + assert(options.option1) + assert.strictEqual(options.port, '1234') + assert.strictEqual(options.host, 'localhost') + assert.strictEqual(typeof options.option2, 'function') + }) - it('pass the options in the third parameter after a port and host', function () { - var options = unifyOptions('1234', 'localhost', { - option1: true, - option2: function () {} - }); - assert.strictEqual(Object.keys(options).length, 4); - assert(options.option1); - assert.strictEqual(options.port, '1234'); - assert.strictEqual(options.host, 'localhost'); - assert.strictEqual(typeof options.option2, 'function'); - }); + it('should throw with three parameters all set to a truthy value', function () { + try { + unifyOptions(1234, {}, {}) + throw new Error('failed') + } catch (err) { + assert.strictEqual(err.message, 'Unknown type of connection in createClient()') + } + }) + }) - it('should throw with three parameters all set to a truthy value', function () { - try { - unifyOptions(1234, {}, {}); - throw new Error('failed'); - } catch (err) { - assert.strictEqual(err.message, 'Unknown type of connection in createClient()'); - } - }); - }); + describe('unix socket as first parameter', function () { + it('pass the options in the second parameter after a port', function () { + var options = unifyOptions('/tmp/redis.sock', { + option1: true, + option2: function () {}, + option3: [1, 2, 3] + }) + assert.strictEqual(Object.keys(options).length, 4) + assert(options.option1) + assert.strictEqual(options.path, '/tmp/redis.sock') + assert.strictEqual(typeof options.option2, 'function') + assert.strictEqual(options.option3.length, 3) + }) - describe('unix socket as first parameter', function () { - it('pass the options in the second parameter after a port', function () { - var options = unifyOptions('/tmp/redis.sock', { - option1: true, - option2: function () {}, - option3: [1, 2, 3] - }); - assert.strictEqual(Object.keys(options).length, 4); - assert(options.option1); - assert.strictEqual(options.path, '/tmp/redis.sock'); - assert.strictEqual(typeof options.option2, 'function'); - assert.strictEqual(options.option3.length, 3); - }); + it('pass the options in the third parameter after a port and host being set to null', function () { + var options = unifyOptions('/tmp/redis.sock', null, { + option1: true, + option2: function () {} + }) + assert.strictEqual(Object.keys(options).length, 3) + assert(options.option1) + assert.strictEqual(options.path, '/tmp/redis.sock') + assert.strictEqual(typeof options.option2, 'function') + }) + }) - it('pass the options in the third parameter after a port and host being set to null', function () { - var options = unifyOptions('/tmp/redis.sock', null, { - option1: true, - option2: function () {} - }); - assert.strictEqual(Object.keys(options).length, 3); - assert(options.option1); - assert.strictEqual(options.path, '/tmp/redis.sock'); - assert.strictEqual(typeof options.option2, 'function'); - }); - }); + describe('redis url as first parameter', function () { + it('empty redis url including options as second parameter', function () { + var options = unifyOptions('redis://', { + option: [1, 2, 3] + }) + assert.strictEqual(Object.keys(options).length, 1) + assert.strictEqual(options.option.length, 3) + }) - describe('redis url as first parameter', function () { - it('empty redis url including options as second parameter', function () { - var options = unifyOptions('redis://', { - option: [1, 2, 3] - }); - assert.strictEqual(Object.keys(options).length, 1); - assert.strictEqual(options.option.length, 3); - }); + it('begin with two slashes including options as third parameter', function () { + var options = unifyOptions('//:abc@/3?port=123', { + option: [1, 2, 3] + }) + assert.strictEqual(Object.keys(options).length, 4) + assert.strictEqual(options.option.length, 3) + assert.strictEqual(options.port, '123') + assert.strictEqual(options.db, '3') + assert.strictEqual(options.password, 'abc') + }) - it('begin with two slashes including options as third parameter', function () { - var options = unifyOptions('//:abc@/3?port=123', { - option: [1, 2, 3] - }); - assert.strictEqual(Object.keys(options).length, 4); - assert.strictEqual(options.option.length, 3); - assert.strictEqual(options.port, '123'); - assert.strictEqual(options.db, '3'); - assert.strictEqual(options.password, 'abc'); - }); - - it('duplicated, identical query options including options obj', function () { - var text = ''; - var unhookIntercept = intercept(function (data) { - text += data; - return ''; - }); - var options = unifyOptions('//:abc@localhost:123/3?db=3&port=123&password=abc', null, { - option: [1, 2, 3] - }); - unhookIntercept(); - assert.strictEqual(text, - 'nodeRedis: WARNING: You passed the db option twice!\n' + + it('duplicated, identical query options including options obj', function () { + var text = '' + var unhookIntercept = intercept(function (data) { + text += data + return '' + }) + var options = unifyOptions('//:abc@localhost:123/3?db=3&port=123&password=abc', null, { + option: [1, 2, 3] + }) + unhookIntercept() + assert.strictEqual(text, + 'nodeRedis: WARNING: You passed the db option twice!\n' + 'nodeRedis: WARNING: You passed the port option twice!\n' + 'nodeRedis: WARNING: You passed the password option twice!\n' - ); - assert.strictEqual(Object.keys(options).length, 5); - assert.strictEqual(options.option.length, 3); - assert.strictEqual(options.host, 'localhost'); - assert.strictEqual(options.port, '123'); - assert.strictEqual(options.db, '3'); - assert.strictEqual(options.password, 'abc'); - }); + ) + assert.strictEqual(Object.keys(options).length, 5) + assert.strictEqual(options.option.length, 3) + assert.strictEqual(options.host, 'localhost') + assert.strictEqual(options.port, '123') + assert.strictEqual(options.db, '3') + assert.strictEqual(options.password, 'abc') + }) - it('should throw on duplicated, non-identical query options', function () { - try { - unifyOptions('//:abc@localhost:1234/3?port=123&password=abc'); - throw new Error('failed'); - } catch (err) { - assert.equal(err.message, 'The port option is added twice and does not match'); - } - }); + it('should throw on duplicated, non-identical query options', function () { + try { + unifyOptions('//:abc@localhost:1234/3?port=123&password=abc') + throw new Error('failed') + } catch (err) { + assert.strictEqual(err.message, 'The port option is added twice and does not match') + } + }) - it('should throw without protocol slashes', function () { - try { - unifyOptions('redis:abc@localhost:123/3?db=3&port=123&password=abc'); - throw new Error('failed'); - } catch (err) { - assert.equal(err.message, 'The redis url must begin with slashes "//" or contain slashes after the redis protocol'); - } - }); + it('should throw without protocol slashes', function () { + try { + unifyOptions('redis:abc@localhost:123/3?db=3&port=123&password=abc') + throw new Error('failed') + } catch (err) { + assert.strictEqual(err.message, 'The redis url must begin with slashes "//" or contain slashes after the redis protocol') + } + }) - it('warns on protocol other than redis in the redis url', function () { - var text = ''; - var unhookIntercept = intercept(function (data) { - text += data; - return ''; - }); - var options = unifyOptions('http://abc'); - unhookIntercept(); - assert.strictEqual(Object.keys(options).length, 1); - assert.strictEqual(options.host, 'abc'); - assert.strictEqual(text, 'nodeRedis: WARNING: You passed "http" as protocol instead of the "redis" protocol!\n'); - }); - }); + it('warns on protocol other than redis in the redis url', function () { + var text = '' + var unhookIntercept = intercept(function (data) { + text += data + return '' + }) + var options = unifyOptions('http://abc') + unhookIntercept() + assert.strictEqual(Object.keys(options).length, 1) + assert.strictEqual(options.host, 'abc') + assert.strictEqual(text, 'nodeRedis: WARNING: You passed "http" as protocol instead of the "redis" protocol!\n') + }) + }) - describe('no parameters or set to null / undefined', function () { - it('no parameters', function () { - var options = unifyOptions(); - assert.strictEqual(Object.keys(options).length, 1); - assert.strictEqual(options.host, undefined); - }); + describe('no parameters or set to null / undefined', function () { + it('no parameters', function () { + var options = unifyOptions() + assert.strictEqual(Object.keys(options).length, 1) + assert.strictEqual(options.host, undefined) + }) - it('set to null', function () { - var options = unifyOptions(null, null); - assert.strictEqual(Object.keys(options).length, 1); - assert.strictEqual(options.host, null); - }); + it('set to null', function () { + var options = unifyOptions(null, null) + assert.strictEqual(Object.keys(options).length, 1) + assert.strictEqual(options.host, null) + }) - it('set to undefined', function () { - var options = unifyOptions(undefined, undefined); - assert.strictEqual(Object.keys(options).length, 1); - assert.strictEqual(options.host, undefined); - }); - }); + it('set to undefined', function () { + var options = unifyOptions(undefined, undefined) + assert.strictEqual(Object.keys(options).length, 1) + assert.strictEqual(options.host, undefined) + }) + }) - describe('only an options object is passed', function () { - it('with options', function () { - var options = unifyOptions({ - option: true - }); - assert.strictEqual(Object.keys(options).length, 2); - assert.strictEqual(options.host, undefined); - assert.strictEqual(options.option, true); - }); + describe('only an options object is passed', function () { + it('with options', function () { + var options = unifyOptions({ + option: true + }) + assert.strictEqual(Object.keys(options).length, 2) + assert.strictEqual(options.host, undefined) + assert.strictEqual(options.option, true) + }) - it('without options', function () { - var options = unifyOptions({}); - assert.strictEqual(Object.keys(options).length, 1); - assert.strictEqual(options.host, undefined); - }); + it('without options', function () { + var options = unifyOptions({}) + assert.strictEqual(Object.keys(options).length, 1) + assert.strictEqual(options.host, undefined) + }) - it('should throw with more parameters', function () { - try { - unifyOptions({ - option: true - }, undefined); - throw new Error('failed'); - } catch (err) { - assert.strictEqual(err.message, 'To many arguments passed to createClient. Please only pass the options object'); - } - }); + it('should throw with more parameters', function () { + try { + unifyOptions({ + option: true + }, undefined) + throw new Error('failed') + } catch (err) { + assert.strictEqual(err.message, 'To many arguments passed to createClient. Please only pass the options object') + } + }) - it('including url as option', function () { - var options = unifyOptions({ - option: [1, 2, 3], - url: '//hm:abc@localhost:123/3' - }); - assert.strictEqual(Object.keys(options).length, 6); - assert.strictEqual(options.option.length, 3); - assert.strictEqual(options.host, 'localhost'); - assert.strictEqual(options.port, '123'); - assert.strictEqual(options.db, '3'); - assert.strictEqual(options.url, '//hm:abc@localhost:123/3'); - assert.strictEqual(options.password, 'abc'); - }); - }); + it('including url as option', function () { + var options = unifyOptions({ + option: [1, 2, 3], + url: '//hm:abc@localhost:123/3' + }) + assert.strictEqual(Object.keys(options).length, 6) + assert.strictEqual(options.option.length, 3) + assert.strictEqual(options.host, 'localhost') + assert.strictEqual(options.port, '123') + assert.strictEqual(options.db, '3') + assert.strictEqual(options.url, '//hm:abc@localhost:123/3') + assert.strictEqual(options.password, 'abc') + }) + }) - describe('faulty data', function () { - it('throws on strange connection info', function () { - try { - unifyOptions(true); - throw new Error('failed'); - } catch (err) { - assert.equal(err.message, 'Unknown type of connection in createClient()'); - } - }); - }); -}); + describe('faulty data', function () { + it('throws on strange connection info', function () { + try { + unifyOptions(true) + throw new Error('failed') + } catch (err) { + assert.strictEqual(err.message, 'Unknown type of connection in createClient()') + } + }) + }) +}) diff --git a/test/utils.spec.js b/test/utils.spec.js index a09603c9ee..8f8211dc1d 100644 --- a/test/utils.spec.js +++ b/test/utils.spec.js @@ -1,134 +1,133 @@ -'use strict'; +'use strict' -var assert = require('assert'); -var Queue = require('double-ended-queue'); -var utils = require('../lib/utils'); +var assert = require('assert') +var Queue = require('double-ended-queue') +var utils = require('../lib/utils') describe('utils.js', function () { + describe('clone', function () { + it('ignore the object prototype and clone a nested array / object', function () { + var obj = { + a: [null, 'foo', ['bar'], { + 'i\'m special': true + }], + number: 5, + fn: function noop () {} + } + var clone = utils.clone(obj) + assert.deepEqual(clone, obj) + assert.strictEqual(obj.fn, clone.fn) + assert(typeof clone.fn === 'function') + }) - describe('clone', function () { - it('ignore the object prototype and clone a nested array / object', function () { - var obj = { - a: [null, 'foo', ['bar'], { - "i'm special": true - }], - number: 5, - fn: function noop () {} - }; - var clone = utils.clone(obj); - assert.deepEqual(clone, obj); - assert.strictEqual(obj.fn, clone.fn); - assert(typeof clone.fn === 'function'); - }); + it('replace falsy values with an empty object as return value', function () { + var a = utils.clone() + var b = utils.clone(null) + assert.strictEqual(Object.keys(a).length, 0) + assert.strictEqual(Object.keys(b).length, 0) + }) - it('replace falsy values with an empty object as return value', function () { - var a = utils.clone(); - var b = utils.clone(null); - assert.strictEqual(Object.keys(a).length, 0); - assert.strictEqual(Object.keys(b).length, 0); - }); + it('throws on circular data', function () { + try { + var a = {} + a.b = a + utils.clone(a) + throw new Error('failed') + } catch (e) { + assert(e.message !== 'failed') + } + }) + }) - it('throws on circular data', function () { - try { - var a = {}; - a.b = a; - utils.clone(a); - throw new Error('failed'); - } catch (e) { - assert(e.message !== 'failed'); - } - }); - }); + describe('replyInOrder', function () { + var errCount = 0 + var resCount = 0 + var emitted = false + var clientMock = { + emit: function () { emitted = true }, + offlineQueue: new Queue(), + commandQueue: new Queue() + } + var createCommandObj = function () { + return { + callback: function (err, res) { + if (err) errCount++ + else resCount++ + } + } + } - describe('replyInOrder', function () { + beforeEach(function () { + clientMock.offlineQueue.clear() + clientMock.commandQueue.clear() + errCount = 0 + resCount = 0 + emitted = false + }) - var errCount = 0; - var resCount = 0; - var emitted = false; - var clientMock = { - emit: function () { emitted = true; }, - offlineQueue: new Queue(), - commandQueue: new Queue() - }; - var createCommandObj = function () { - return { - callback: function (err, res) { - if (err) errCount++; - else resCount++; - } - }; - }; + it('no elements in either queue. Reply in the next tick with callback', function (done) { + var called = false + utils.replyInOrder(clientMock, function () { + called = true + done() + }, null, null) + assert(!called) + }) - beforeEach(function () { - clientMock.offlineQueue.clear(); - clientMock.commandQueue.clear(); - errCount = 0; - resCount = 0; - emitted = false; - }); + it('no elements in either queue. Reply in the next tick without callback', function (done) { + assert(!emitted) + utils.replyInOrder(clientMock, null, new Error('tada')) + assert(!emitted) + setTimeout(function () { + assert(emitted) + done() + }, 1) + }) - it('no elements in either queue. Reply in the next tick with callback', function (done) { - var called = false; - utils.replyInOrder(clientMock, function () { - called = true; - done(); - }, null, null); - assert(!called); - }); + it('elements in the offline queue. Reply after the offline queue is empty and respect the commandObj callback', function (done) { + clientMock.offlineQueue.push(createCommandObj(), createCommandObj()) + utils.replyInOrder(clientMock, function () { + assert.strictEqual(clientMock.offlineQueue.length, 0) + assert.strictEqual(resCount, 2) + done() + }, null, null) + while (clientMock.offlineQueue.length) clientMock.offlineQueue.shift().callback(null, 'foo') + }) - it('no elements in either queue. Reply in the next tick without callback', function (done) { - assert(!emitted); - utils.replyInOrder(clientMock, null, new Error('tada')); - assert(!emitted); - setTimeout(function () { - assert(emitted); - done(); - }, 1); - }); + it('elements in the offline queue. Reply after the offline queue is empty and respect the commandObj error emit', function (done) { + clientMock.commandQueue.push({}, createCommandObj(), {}) + utils.replyInOrder(clientMock, function () { + assert.strictEqual(clientMock.commandQueue.length, 0) + assert(emitted) + assert.strictEqual(errCount, 1) + assert.strictEqual(resCount, 0) + done() + }, null, null) + while (clientMock.commandQueue.length) { + var commandObj = clientMock.commandQueue.shift() + if (commandObj.callback) { + commandObj.callback(new Error('tada')) + } + } + }) - it('elements in the offline queue. Reply after the offline queue is empty and respect the commandObj callback', function (done) { - clientMock.offlineQueue.push(createCommandObj(), createCommandObj()); - utils.replyInOrder(clientMock, function () { - assert.strictEqual(clientMock.offlineQueue.length, 0); - assert.strictEqual(resCount, 2); - done(); - }, null, null); - while (clientMock.offlineQueue.length) clientMock.offlineQueue.shift().callback(null, 'foo'); - }); - - it('elements in the offline queue. Reply after the offline queue is empty and respect the commandObj error emit', function (done) { - clientMock.commandQueue.push({}, createCommandObj(), {}); - utils.replyInOrder(clientMock, function () { - assert.strictEqual(clientMock.commandQueue.length, 0); - assert(emitted); - assert.strictEqual(errCount, 1); - assert.strictEqual(resCount, 0); - done(); - }, null, null); - while (clientMock.commandQueue.length) { - var commandObj = clientMock.commandQueue.shift(); - if (commandObj.callback) { - commandObj.callback(new Error('tada')); - } - } - }); - - it('elements in the offline queue and the commandQueue. Reply all other commands got handled respect the commandObj', function (done) { - clientMock.commandQueue.push(createCommandObj(), createCommandObj()); - clientMock.offlineQueue.push(createCommandObj(), {}); - utils.replyInOrder(clientMock, function (err, res) { - assert.strictEqual(clientMock.commandQueue.length, 0); - assert.strictEqual(clientMock.offlineQueue.length, 0); - assert(!emitted); - assert.strictEqual(resCount, 3); - done(); - }, null, null); - while (clientMock.offlineQueue.length) { - clientMock.commandQueue.push(clientMock.offlineQueue.shift()); - } - while (clientMock.commandQueue.length) { - clientMock.commandQueue.shift().callback(null, 'hello world'); - } - }); - }); -}); + it('elements in the offline queue and the commandQueue. Reply all other commands got handled respect the commandObj', function (done) { + clientMock.commandQueue.push(createCommandObj(), createCommandObj()) + clientMock.offlineQueue.push(createCommandObj(), {}) + utils.replyInOrder(clientMock, function (err, res) { + if (err) throw err + assert.strictEqual(clientMock.commandQueue.length, 0) + assert.strictEqual(clientMock.offlineQueue.length, 0) + assert(!emitted) + assert.strictEqual(resCount, 3) + done() + }, null, null) + while (clientMock.offlineQueue.length) { + clientMock.commandQueue.push(clientMock.offlineQueue.shift()) + } + while (clientMock.commandQueue.length) { + clientMock.commandQueue.shift().callback(null, 'hello world') + } + }) + }) +})