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

chore: use standard

This commit is contained in:
Ruben Bridgewater
2017-05-06 07:06:52 +02:00
parent 5d29f541e9
commit f1a7bcd735
106 changed files with 10706 additions and 10978 deletions

View File

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

View File

@@ -1,86 +1,88 @@
'use strict'; 'use strict'
var path = require('path'); var Buffer = require('safe-buffer').Buffer
var RedisProcess = require('../test/lib/redis-process'); var path = require('path')
var rp; var RedisProcess = require('../test/lib/redis-process')
var clientNr = 0; var rp
var redis = require('../index'); var clientNr = 0
var totalTime = 0; var redis = require('../index')
var metrics = require('metrics'); var totalTime = 0
var tests = []; var metrics = require('metrics')
var tests = []
// var bluebird = require('bluebird'); // var bluebird = require('bluebird');
// bluebird.promisifyAll(redis.RedisClient.prototype); // bluebird.promisifyAll(redis.RedisClient.prototype);
// bluebird.promisifyAll(redis.Multi.prototype); // bluebird.promisifyAll(redis.Multi.prototype);
function returnArg (name, def) { function returnArg (name, def) {
var matches = process.argv.filter(function (entry) { var matches = process.argv.filter(function (entry) {
return entry.indexOf(name + '=') === 0; return entry.indexOf(name + '=') === 0
}); })
if (matches.length) { if (matches.length) {
return matches[0].substr(name.length + 1); return matches[0].substr(name.length + 1)
} }
return def; return def
} }
var numClients = returnArg('clients', 1); var numClients = returnArg('clients', 1)
var runTime = returnArg('time', 2500); // ms var runTime = returnArg('time', 2500) // ms
var pipeline = returnArg('pipeline', 1); // number of concurrent commands var pipeline = returnArg('pipeline', 1) // number of concurrent commands
var versionsLogged = false; var versionsLogged = false
var clientOptions = { var clientOptions = {
path: returnArg('socket') // '/tmp/redis.sock' path: returnArg('socket') // '/tmp/redis.sock'
}; }
var smallStr, largeStr, smallBuf, largeBuf, veryLargeStr, veryLargeBuf, mgetArray; var smallStr, largeStr, smallBuf, largeBuf, veryLargeStr, veryLargeBuf, mgetArray
function lpad (input, len, chr) { function lpad (input, len, chr) {
var str = input.toString(); var str = input.toString()
chr = chr || ' '; chr = chr || ' '
while (str.length < len) { while (str.length < len) {
str = chr + str; str = chr + str
} }
return str; return str
} }
metrics.Histogram.prototype.printLine = function () { metrics.Histogram.prototype.printLine = function () {
var obj = this.printObj(); var obj = this.printObj()
return lpad((obj.mean / 1e6).toFixed(2), 6) + '/' + lpad((obj.max / 1e6).toFixed(2), 6); return lpad((obj.mean / 1e6).toFixed(2), 6) + '/' + lpad((obj.max / 1e6).toFixed(2), 6)
}; }
function Test (args) { function Test (args) {
this.args = args; this.args = args
this.args.pipeline = +pipeline; this.args.pipeline = +pipeline
this.callback = null; this.callback = null
this.clients = []; this.clients = []
this.clientsReady = 0; this.clientsReady = 0
this.commandsSent = 0; this.commandsSent = 0
this.commandsCompleted = 0; this.commandsCompleted = 0
this.maxPipeline = +pipeline; this.maxPipeline = +pipeline
this.batchPipeline = this.args.batch || 0; this.batchPipeline = this.args.batch || 0
this.clientOptions = args.clientOptions || {}; this.clientOptions = args.clientOptions || {}
this.clientOptions.connectTimeout = 1000; this.clientOptions.connectTimeout = 1000
if (clientOptions.path) { if (clientOptions.path) {
this.clientOptions.path = clientOptions.path; this.clientOptions.path = clientOptions.path
} }
this.connectLatency = new metrics.Histogram(); this.connectLatency = new metrics.Histogram()
this.readyLatency = new metrics.Histogram(); this.readyLatency = new metrics.Histogram()
this.commandLatency = new metrics.Histogram(); this.commandLatency = new metrics.Histogram()
} }
Test.prototype.run = function (callback) { Test.prototype.run = function (callback) {
var i; var i
this.callback = callback; this.callback = callback
for (i = 0; i < numClients ; i++) { for (i = 0; i < numClients; i++) {
this.newClient(i); this.newClient(i)
} }
}; }
Test.prototype.newClient = function (id) { Test.prototype.newClient = function (id) {
var self = this, newClient; var self = this
var newClient
newClient = redis.createClient(this.clientOptions); newClient = redis.createClient(this.clientOptions)
newClient.createTime = Date.now(); newClient.createTime = Date.now()
newClient.on('connect', function () { newClient.on('connect', function () {
self.connectLatency.update(Date.now() - newClient.createTime); self.connectLatency.update(Date.now() - newClient.createTime)
}); })
newClient.on('ready', function () { newClient.on('ready', function () {
if (!versionsLogged) { if (!versionsLogged) {
@@ -89,207 +91,208 @@ Test.prototype.newClient = function (id) {
', NodeJS: ' + process.versions.node + ', NodeJS: ' + process.versions.node +
', Redis: ' + newClient.serverInfo.redis_version + ', Redis: ' + newClient.serverInfo.redis_version +
', connected by: ' + (clientOptions.path ? 'socket' : 'tcp') ', connected by: ' + (clientOptions.path ? 'socket' : 'tcp')
); )
versionsLogged = true; versionsLogged = true
} }
self.readyLatency.update(Date.now() - newClient.createTime); self.readyLatency.update(Date.now() - newClient.createTime)
self.clientsReady++; self.clientsReady++
if (self.clientsReady === self.clients.length) { if (self.clientsReady === self.clients.length) {
self.onClientsReady(); self.onClientsReady()
} }
}); })
// If no redis server is running, start one // If no redis server is running, start one
newClient.on('error', function (err) { newClient.on('error', function (err) {
if (err.code === 'CONNECTION_BROKEN') { if (err.code === 'CONNECTION_BROKEN') {
throw err; throw err
} }
if (rp) { if (rp) {
return; return
} }
rp = true; rp = true
var conf = '../test/conf/redis.conf'; var conf = '../test/conf/redis.conf'
RedisProcess.start(function (err, Rp) { RedisProcess.start(function (err, Rp) {
if (err) { if (err) {
throw err; throw err
} }
rp = Rp; rp = Rp
}, path.resolve(__dirname, conf)); }, path.resolve(__dirname, conf))
}); })
self.clients[id] = newClient; self.clients[id] = newClient
}; }
Test.prototype.onClientsReady = function () { Test.prototype.onClientsReady = function () {
process.stdout.write(lpad(this.args.descr, 13) + ', ' + (this.args.batch ? lpad('batch ' + this.args.batch, 9) : lpad(this.args.pipeline, 9)) + '/' + this.clientsReady + ' '); process.stdout.write(lpad(this.args.descr, 13) + ', ' + (this.args.batch ? lpad('batch ' + this.args.batch, 9) : lpad(this.args.pipeline, 9)) + '/' + this.clientsReady + ' ')
this.testStart = Date.now(); this.testStart = Date.now()
this.fillPipeline(); this.fillPipeline()
}; }
Test.prototype.fillPipeline = function () { Test.prototype.fillPipeline = function () {
var pipeline = this.commandsSent - this.commandsCompleted; var pipeline = this.commandsSent - this.commandsCompleted
if (this.testStart < Date.now() - runTime) { if (this.testStart < Date.now() - runTime) {
if (this.ended) { if (this.ended) {
return; return
} }
this.ended = true; this.ended = true
this.printStats(); this.printStats()
this.stopClients(); this.stopClients()
return; return
} }
if (this.batchPipeline) { if (this.batchPipeline) {
this.batch(); this.batch()
} else { } else {
while (pipeline < this.maxPipeline) { while (pipeline < this.maxPipeline) {
this.commandsSent++; this.commandsSent++
pipeline++; pipeline++
this.sendNext(); this.sendNext()
} }
} }
}; }
Test.prototype.batch = function () { Test.prototype.batch = function () {
var self = this, var self = this
curClient = clientNr++ % this.clients.length, var curClient = clientNr++ % this.clients.length
start = process.hrtime(), var start = process.hrtime()
i = 0, var i = 0
batch = this.clients[curClient].batch(); var batch = this.clients[curClient].batch()
while (i++ < this.batchPipeline) { while (i++ < this.batchPipeline) {
this.commandsSent++; this.commandsSent++
batch[this.args.command](this.args.args); batch[this.args.command](this.args.args)
} }
batch.exec(function (err, res) { batch.exec(function (err, res) {
if (err) { if (err) {
throw err; throw err
} }
self.commandsCompleted += res.length; self.commandsCompleted += res.length
self.commandLatency.update(process.hrtime(start)[1]); self.commandLatency.update(process.hrtime(start)[1])
self.fillPipeline(); self.fillPipeline()
}); })
}; }
Test.prototype.stopClients = function () { Test.prototype.stopClients = function () {
var self = this; var self = this
this.clients.forEach(function (client, pos) { this.clients.forEach(function (client, pos) {
if (pos === self.clients.length - 1) { if (pos === self.clients.length - 1) {
client.quit(function (err, res) { client.quit(function (err, res) {
self.callback(); if (err) throw err
}); self.callback()
})
} else { } else {
client.quit(); client.quit()
} }
}); })
}; }
Test.prototype.sendNext = function () { Test.prototype.sendNext = function () {
var self = this, var self = this
curClient = this.commandsSent % this.clients.length, var curClient = this.commandsSent % this.clients.length
start = process.hrtime(); var start = process.hrtime()
this.clients[curClient][this.args.command](this.args.args, function (err, res) { this.clients[curClient][this.args.command](this.args.args, function (err, res) {
if (err) { if (err) {
throw err; throw err
} }
self.commandsCompleted++; self.commandsCompleted++
self.commandLatency.update(process.hrtime(start)[1]); self.commandLatency.update(process.hrtime(start)[1])
self.fillPipeline(); self.fillPipeline()
}); })
}; }
Test.prototype.printStats = function () { Test.prototype.printStats = function () {
var duration = Date.now() - this.testStart; var duration = Date.now() - this.testStart
totalTime += duration; totalTime += duration
console.log('avg/max: ' + this.commandLatency.printLine() + lpad(duration, 5) + 'ms total, ' + console.log('avg/max: ' + this.commandLatency.printLine() + lpad(duration, 5) + 'ms total, ' +
lpad(Math.round(this.commandsCompleted / (duration / 1000)), 7) + ' ops/sec'); lpad(Math.round(this.commandsCompleted / (duration / 1000)), 7) + ' ops/sec')
}; }
smallStr = '1234'; smallStr = '1234'
smallBuf = new Buffer(smallStr); smallBuf = Buffer.from(smallStr)
largeStr = (new Array(4096 + 1).join('-')); largeStr = (new Array(4096 + 1).join('-'))
largeBuf = new Buffer(largeStr); largeBuf = Buffer.from(largeStr)
veryLargeStr = (new Array((4 * 1024 * 1024) + 1).join('-')); veryLargeStr = (new Array((4 * 1024 * 1024) + 1).join('-'))
veryLargeBuf = new Buffer(veryLargeStr); veryLargeBuf = Buffer.from(veryLargeStr)
mgetArray = (new Array(1025)).join('fooRand000000000001;').split(';'); mgetArray = (new Array(1025)).join('fooRand000000000001;').split(';')
tests.push(new Test({descr: 'PING', command: 'ping', args: []})); tests.push(new Test({descr: 'PING', command: 'ping', args: []}))
tests.push(new Test({descr: 'PING', command: 'ping', args: [], batch: 50})); tests.push(new Test({descr: 'PING', command: 'ping', args: [], batch: 50}))
tests.push(new Test({descr: 'SET 4B str', command: 'set', args: ['fooRand000000000000', smallStr]})); tests.push(new Test({descr: 'SET 4B str', command: 'set', args: ['fooRand000000000000', smallStr]}))
tests.push(new Test({descr: 'SET 4B str', command: 'set', args: ['fooRand000000000000', smallStr], batch: 50})); tests.push(new Test({descr: 'SET 4B str', command: 'set', args: ['fooRand000000000000', smallStr], batch: 50}))
tests.push(new Test({descr: 'SET 4B buf', command: 'set', args: ['fooRand000000000000', smallBuf]})); tests.push(new Test({descr: 'SET 4B buf', command: 'set', args: ['fooRand000000000000', smallBuf]}))
tests.push(new Test({descr: 'SET 4B buf', command: 'set', args: ['fooRand000000000000', smallBuf], batch: 50})); tests.push(new Test({descr: 'SET 4B buf', command: 'set', args: ['fooRand000000000000', smallBuf], batch: 50}))
tests.push(new Test({descr: 'GET 4B str', command: 'get', args: ['fooRand000000000000']})); tests.push(new Test({descr: 'GET 4B str', command: 'get', args: ['fooRand000000000000']}))
tests.push(new Test({descr: 'GET 4B str', command: 'get', args: ['fooRand000000000000'], batch: 50})); tests.push(new Test({descr: 'GET 4B str', command: 'get', args: ['fooRand000000000000'], batch: 50}))
tests.push(new Test({descr: 'GET 4B buf', command: 'get', args: ['fooRand000000000000'], clientOptions: { returnBuffers: true} })); tests.push(new Test({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: 'GET 4B buf', command: 'get', args: ['fooRand000000000000'], batch: 50, clientOptions: {returnBuffers: true}}))
tests.push(new Test({descr: 'SET 4KiB str', command: 'set', args: ['fooRand000000000001', largeStr]})); tests.push(new Test({descr: 'SET 4KiB str', command: 'set', args: ['fooRand000000000001', largeStr]}))
tests.push(new Test({descr: 'SET 4KiB str', command: 'set', args: ['fooRand000000000001', largeStr], batch: 50})); tests.push(new Test({descr: 'SET 4KiB str', command: 'set', args: ['fooRand000000000001', largeStr], batch: 50}))
tests.push(new Test({descr: 'SET 4KiB buf', command: 'set', args: ['fooRand000000000001', largeBuf]})); tests.push(new Test({descr: 'SET 4KiB buf', command: 'set', args: ['fooRand000000000001', largeBuf]}))
tests.push(new Test({descr: 'SET 4KiB buf', command: 'set', args: ['fooRand000000000001', largeBuf], batch: 50})); tests.push(new Test({descr: 'SET 4KiB buf', command: 'set', args: ['fooRand000000000001', largeBuf], batch: 50}))
tests.push(new Test({descr: 'GET 4KiB str', command: 'get', args: ['fooRand000000000001']})); tests.push(new Test({descr: 'GET 4KiB str', command: 'get', args: ['fooRand000000000001']}))
tests.push(new Test({descr: 'GET 4KiB str', command: 'get', args: ['fooRand000000000001'], batch: 50})); tests.push(new Test({descr: 'GET 4KiB str', command: 'get', args: ['fooRand000000000001'], batch: 50}))
tests.push(new Test({descr: 'GET 4KiB buf', command: 'get', args: ['fooRand000000000001'], clientOptions: { returnBuffers: true} })); tests.push(new Test({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: 'GET 4KiB buf', command: 'get', args: ['fooRand000000000001'], batch: 50, clientOptions: {returnBuffers: true}}))
tests.push(new Test({descr: 'INCR', command: 'incr', args: ['counterRand000000000000']})); tests.push(new Test({descr: 'INCR', command: 'incr', args: ['counterRand000000000000']}))
tests.push(new Test({descr: 'INCR', command: 'incr', args: ['counterRand000000000000'], batch: 50})); tests.push(new Test({descr: 'INCR', command: 'incr', args: ['counterRand000000000000'], batch: 50}))
tests.push(new Test({descr: 'LPUSH', command: 'lpush', args: ['mylist', smallStr]})); tests.push(new Test({descr: 'LPUSH', command: 'lpush', args: ['mylist', smallStr]}))
tests.push(new Test({descr: 'LPUSH', command: 'lpush', args: ['mylist', smallStr], batch: 50})); tests.push(new Test({descr: 'LPUSH', command: 'lpush', args: ['mylist', smallStr], batch: 50}))
tests.push(new Test({descr: 'LRANGE 10', command: 'lrange', args: ['mylist', '0', '9']})); tests.push(new Test({descr: 'LRANGE 10', command: 'lrange', args: ['mylist', '0', '9']}))
tests.push(new Test({descr: 'LRANGE 10', command: 'lrange', args: ['mylist', '0', '9'], batch: 50})); tests.push(new Test({descr: 'LRANGE 10', command: 'lrange', args: ['mylist', '0', '9'], batch: 50}))
tests.push(new Test({descr: 'LRANGE 100', command: 'lrange', args: ['mylist', '0', '99']})); tests.push(new Test({descr: 'LRANGE 100', command: 'lrange', args: ['mylist', '0', '99']}))
tests.push(new Test({descr: 'LRANGE 100', command: 'lrange', args: ['mylist', '0', '99'], batch: 50})); tests.push(new Test({descr: 'LRANGE 100', command: 'lrange', args: ['mylist', '0', '99'], batch: 50}))
tests.push(new Test({descr: 'SET 4MiB str', command: 'set', args: ['fooRand000000000002', veryLargeStr]})); tests.push(new Test({descr: 'SET 4MiB str', command: 'set', args: ['fooRand000000000002', veryLargeStr]}))
tests.push(new Test({descr: 'SET 4MiB str', command: 'set', args: ['fooRand000000000002', veryLargeStr], batch: 20})); tests.push(new Test({descr: 'SET 4MiB str', command: 'set', args: ['fooRand000000000002', veryLargeStr], batch: 20}))
tests.push(new Test({descr: 'SET 4MiB buf', command: 'set', args: ['fooRand000000000002', veryLargeBuf]})); tests.push(new Test({descr: 'SET 4MiB buf', command: 'set', args: ['fooRand000000000002', veryLargeBuf]}))
tests.push(new Test({descr: 'SET 4MiB buf', command: 'set', args: ['fooRand000000000002', veryLargeBuf], batch: 20})); tests.push(new Test({descr: 'SET 4MiB buf', command: 'set', args: ['fooRand000000000002', veryLargeBuf], batch: 20}))
tests.push(new Test({descr: 'GET 4MiB str', command: 'get', args: ['fooRand000000000002']})); tests.push(new Test({descr: 'GET 4MiB str', command: 'get', args: ['fooRand000000000002']}))
tests.push(new Test({descr: 'GET 4MiB str', command: 'get', args: ['fooRand000000000002'], batch: 20})); tests.push(new Test({descr: 'GET 4MiB str', command: 'get', args: ['fooRand000000000002'], batch: 20}))
tests.push(new Test({descr: 'GET 4MiB buf', command: 'get', args: ['fooRand000000000002'], clientOptions: { returnBuffers: true} })); tests.push(new Test({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: 'GET 4MiB buf', command: 'get', args: ['fooRand000000000002'], batch: 20, clientOptions: {returnBuffers: true}}))
tests.push(new Test({descr: 'MGET 4MiB str', command: 'mget', args: mgetArray})); tests.push(new Test({descr: 'MGET 4MiB str', command: 'mget', args: mgetArray}))
tests.push(new Test({descr: 'MGET 4MiB str', command: 'mget', args: mgetArray, batch: 20})); tests.push(new Test({descr: 'MGET 4MiB str', command: 'mget', args: mgetArray, batch: 20}))
tests.push(new Test({descr: 'MGET 4MiB buf', command: 'mget', args: mgetArray, clientOptions: { returnBuffers: true} })); tests.push(new Test({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} })); tests.push(new Test({descr: 'MGET 4MiB buf', command: 'mget', args: mgetArray, batch: 20, clientOptions: {returnBuffers: true}}))
function next () { function next () {
var test = tests.shift(); var test = tests.shift()
if (test) { if (test) {
test.run(function () { test.run(function () {
next(); next()
}); })
} else if (rp) { } else if (rp) {
// Stop the redis process if started by the benchmark // Stop the redis process if started by the benchmark
rp.stop(function () { rp.stop(function () {
rp = undefined; rp = undefined
next(); next()
}); })
} else { } else {
console.log('End of tests. Total time elapsed:', totalTime, 'ms'); console.log('End of tests. Total time elapsed:', totalTime, 'ms')
process.exit(0); process.exit(0)
} }
} }
next(); next()

View File

@@ -1,7 +1,7 @@
'use strict'; 'use strict'
var redis = require('redis'); var redis = require('redis')
// The client stashes the password and will reauthenticate on every connect. // The client stashes the password and will re-authenticate on every connect.
redis.createClient({ redis.createClient({
password: 'somepass' password: 'some pass'
}); })

View File

@@ -1,14 +1,14 @@
'use strict'; 'use strict'
var redis = require('../index'); var redis = require('../index')
var client = redis.createClient(); var client = redis.createClient()
client.eval('return 100.5', 0, function (err, res) { client.eval('return 100.5', 0, function (err, res) {
console.dir(err); console.dir(err)
console.dir(res); console.dir(res)
}); })
client.eval([ 'return 100.5', 0 ], function (err, res) { client.eval([ 'return 100.5', 0 ], function (err, res) {
console.dir(err); console.dir(err)
console.dir(res); console.dir(res)
}); })

View File

@@ -1,26 +1,27 @@
'use strict'; 'use strict'
var redis = require('redis'); var redis = require('redis')
var client = redis.createClient(); var client = redis.createClient()
// Extend the RedisClient prototype to add a custom method // Extend the RedisClient prototype to add a custom method
// This one converts the results from 'INFO' into a JavaScript Object // This one converts the results from 'INFO' into a JavaScript Object
redis.RedisClient.prototype.parseInfo = function (callback) { redis.RedisClient.prototype.parseInfo = function (callback) {
this.info(function (err, res) { this.info(function (err, res) {
var lines = res.toString().split('\r\n').sort(); if (err) throw err
var obj = {}; var lines = res.toString().split('\r\n').sort()
var obj = {}
lines.forEach(function (line) { lines.forEach(function (line) {
var parts = line.split(':'); var parts = line.split(':')
if (parts[1]) { if (parts[1]) {
obj[parts[0]] = parts[1]; obj[parts[0]] = parts[1]
} }
}); })
callback(obj); callback(obj)
}); })
}; }
client.parseInfo(function (info) { client.parseInfo(function (info) {
console.dir(info); console.dir(info)
client.quit(); client.quit()
}); })

View File

@@ -1,14 +1,14 @@
'use strict'; 'use strict'
// Read a file from disk, store it in Redis, then read it back from Redis. // 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({ var client = redis.createClient({
returnBuffers: true returnBuffers: true
}); })
var fs = require('fs'); var fs = require('fs')
var assert = require('assert'); var assert = require('assert')
var filename = 'grumpyCat.jpg'; var filename = 'grumpyCat.jpg'
// Get the file I use for testing like this: // 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 // 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. // 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) { fs.readFile(filename, function (err, data) {
if (err) throw err; if (err) throw err
console.log('Read ' + data.length + ' bytes from filesystem.'); console.log('Read ' + data.length + ' bytes from filesystem.')
client.set(filename, data, console.log); // set entire file client.set(filename, data, console.log) // set entire file
client.get(filename, function (err, reply) { // get entire file client.get(filename, function (err, reply) { // get entire file
if (err) { if (err) {
console.log('Get error: ' + err); console.log('Get error: ' + err)
} else { } else {
assert.strictEqual(data.inspect(), reply.inspect()); assert.strictEqual(data.inspect(), reply.inspect())
fs.writeFile('duplicate_' + filename, reply, function (err) { fs.writeFile('duplicate_' + filename, reply, function (err) {
if (err) { if (err) {
console.log('Error on write: ' + err); console.log('Error on write: ' + err)
} else { } else {
console.log('File written.'); console.log('File written.')
} }
client.end(); client.end()
}); })
} }
}); })
}); })

View File

@@ -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) { client.mget(['sessions started', 'sessions started', 'foo'], function (err, res) {
console.dir(res); if (err) throw err
}); console.dir(res)
})

View File

@@ -1,12 +1,13 @@
'use strict'; 'use strict'
var client = require('../index').createClient(); var client = require('../index').createClient()
var util = require('util'); var util = require('util')
client.monitor(function (err, res) { 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) { client.on('monitor', function (time, args) {
console.log(time + ': ' + util.inspect(args)); console.log(time + ': ' + util.inspect(args))
}); })

View File

@@ -1,15 +1,15 @@
'use strict'; 'use strict'
var redis = require('redis'); var redis = require('redis')
var client = redis.createClient(); var client = redis.createClient()
var setSize = 20; var setSize = 20
client.sadd('bigset', 'a member'); client.sadd('bigset', 'a member')
client.sadd('bigset', 'another member'); client.sadd('bigset', 'another member')
while (setSize > 0) { while (setSize > 0) {
client.sadd('bigset', 'member ' + setSize); client.sadd('bigset', 'member ' + setSize)
setSize -= 1; setSize -= 1
} }
// multi chain with an individual callback // multi chain with an individual callback
@@ -17,33 +17,37 @@ client.multi()
.scard('bigset') .scard('bigset')
.smembers('bigset') .smembers('bigset')
.keys('*', function (err, replies) { .keys('*', function (err, replies) {
client.mget(replies, console.log); if (err) throw err
client.mget(replies, console.log)
}) })
.dbsize() .dbsize()
.exec(function (err, replies) { .exec(function (err, replies) {
console.log('MULTI got ' + replies.length + ' replies'); if (err) throw err
console.log('MULTI got ' + replies.length + ' replies')
replies.forEach(function (reply, index) { replies.forEach(function (reply, index) {
console.log('Reply ' + index + ': ' + reply.toString()); 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 // start a separate multi command queue
var multi = client.multi(); var multi = client.multi()
multi.incr('incr thing', console.log); multi.incr('incr thing', console.log)
multi.incr('incr other thing', console.log); multi.incr('incr other thing', console.log)
// runs immediately // runs immediately
client.get('incr thing', console.log); // 100 client.get('incr thing', console.log) // 100
// drains multi queue and runs atomically // drains multi queue and runs atomically
multi.exec(function (err, replies) { 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 // you can re-run the same transaction if you like
multi.exec(function (err, replies) { multi.exec(function (err, replies) {
console.log(replies); // 102, 3 if (err) throw err
client.quit(); console.log(replies) // 102, 3
}); client.quit()
})

View File

@@ -1,31 +1,34 @@
'use strict'; 'use strict'
var redis = require('redis'); var redis = require('redis')
var client = redis.createClient(); var client = redis.createClient()
// start a separate command queue for multi // start a separate command queue for multi
var multi = client.multi(); var multi = client.multi()
multi.incr('incr thing', console.log); multi.incr('incr thing', console.log)
multi.incr('incr other thing', console.log); multi.incr('incr other thing', console.log)
// runs immediately // 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 // drains multi queue and runs atomically
multi.exec(function (err, replies) { 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 // you can re-run the same transaction if you like
multi.exec(function (err, replies) { multi.exec(function (err, replies) {
console.log(replies); // 102, 3 if (err) throw err
client.quit(); console.log(replies) // 102, 3
}); client.quit()
})
client.multi([ client.multi([
['mget', 'multifoo', 'multibar', console.log], ['mget', 'multifoo', 'multibar', console.log],
['incr', 'multifoo'], ['incr', 'multifoo'],
['incr', 'multibar'] ['incr', 'multibar']
]).exec(function (err, replies) { ]).exec(function (err, replies) {
console.log(replies.toString()); if (err) throw err
}); console.log(replies.toString())
})

View File

@@ -1,33 +1,33 @@
'use strict'; 'use strict'
var redis = require('redis'); var redis = require('redis')
var client1 = redis.createClient(); var client1 = redis.createClient()
var client2 = redis.createClient(); var client2 = redis.createClient()
var client3 = redis.createClient(); var client3 = redis.createClient()
var client4 = redis.createClient(); var client4 = redis.createClient()
var msgCount = 0; var msgCount = 0
client1.on('psubscribe', function (pattern, count) { client1.on('psubscribe', function (pattern, count) {
console.log('client1 psubscribed to ' + pattern + ', ' + count + ' total subscriptions'); console.log('client1 psubscribed to ' + pattern + ', ' + count + ' total subscriptions')
client2.publish('channeltwo', 'Me!'); client2.publish('channeltwo', 'Me!')
client3.publish('channelthree', 'Me too!'); client3.publish('channelthree', 'Me too!')
client4.publish('channelfour', 'And me too!'); client4.publish('channelfour', 'And me too!')
}); })
client1.on('punsubscribe', function (pattern, count) { client1.on('punsubscribe', function (pattern, count) {
console.log('client1 punsubscribed from ' + pattern + ', ' + count + ' total subscriptions'); console.log('client1 punsubscribed from ' + pattern + ', ' + count + ' total subscriptions')
client4.end(); client4.end()
client3.end(); client3.end()
client2.end(); client2.end()
client1.end(); client1.end()
}); })
client1.on('pmessage', function (pattern, channel, message) { client1.on('pmessage', function (pattern, channel, message) {
console.log('(' + pattern + ') client1 received message on ' + channel + ': ' + message); console.log('(' + pattern + ') client1 received message on ' + channel + ': ' + message)
msgCount += 1; msgCount += 1
if (msgCount === 3) { if (msgCount === 3) {
client1.punsubscribe(); client1.punsubscribe()
} }
}); })
client1.psubscribe('channel*'); client1.psubscribe('channel*')

View File

@@ -1,42 +1,42 @@
'use strict'; 'use strict'
var redis = require('redis'); var redis = require('redis')
var client1 = redis.createClient(); var client1 = redis.createClient()
var msgCount = 0; var msgCount = 0
var client2 = redis.createClient(); var client2 = redis.createClient()
// Most clients probably don't do much on 'subscribe'. This example uses it to coordinate things within one program. // 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) { client1.on('subscribe', function (channel, count) {
console.log('client1 subscribed to ' + channel + ', ' + count + ' total subscriptions'); console.log('client1 subscribed to ' + channel + ', ' + count + ' total subscriptions')
if (count === 2) { if (count === 2) {
client2.publish('a nice channel', 'I am sending a message.'); client2.publish('a nice channel', 'I am sending a message.')
client2.publish('another one', 'I am sending a second message.'); client2.publish('another one', 'I am sending a second message.')
client2.publish('a nice channel', 'I am sending my last message.'); client2.publish('a nice channel', 'I am sending my last message.')
} }
}); })
client1.on('unsubscribe', function (channel, count) { client1.on('unsubscribe', function (channel, count) {
console.log('client1 unsubscribed from ' + channel + ', ' + count + ' total subscriptions'); console.log('client1 unsubscribed from ' + channel + ', ' + count + ' total subscriptions')
if (count === 0) { if (count === 0) {
client2.end(); client2.end()
client1.end(); client1.end()
} }
}); })
client1.on('message', function (channel, message) { client1.on('message', function (channel, message) {
console.log('client1 channel ' + channel + ': ' + message); console.log('client1 channel ' + channel + ': ' + message)
msgCount += 1; msgCount += 1
if (msgCount === 3) { if (msgCount === 3) {
client1.unsubscribe(); client1.unsubscribe()
} }
}); })
client1.on('ready', function () { client1.on('ready', function () {
// if you need auth, do it here // if you need auth, do it here
client1.incr('did a thing'); client1.incr('did a thing')
client1.subscribe('a nice channel', 'another one'); client1.subscribe('a nice channel', 'another one')
}); })
client2.on('ready', function () { client2.on('ready', function () {
// if you need auth, do it here // if you need auth, do it here
}); })

View File

@@ -1,9 +1,9 @@
'use strict'; 'use strict'
var redis = require('redis'); var redis = require('redis')
var client = redis.createClient(); var client = redis.createClient()
var cursor = '0'; var cursor = '0'
function scan () { function scan () {
client.scan( client.scan(
@@ -11,12 +11,12 @@ function scan () {
'MATCH', 'q:job:*', 'MATCH', 'q:job:*',
'COUNT', '10', 'COUNT', '10',
function (err, res) { function (err, res) {
if (err) throw err; if (err) throw err
// Update the cursor position for the next scan // Update the cursor position for the next scan
cursor = res[0]; cursor = res[0]
// get the SCAN result for this iteration // get the SCAN result for this iteration
var keys = res[1]; var keys = res[1]
// Remember: more or less than COUNT or no keys may be returned // Remember: more or less than COUNT or no keys may be returned
// See http://redis.io/commands/scan#the-count-option // See http://redis.io/commands/scan#the-count-option
@@ -25,7 +25,7 @@ function scan () {
// Additionally, you should always have the code that uses the keys // Additionally, you should always have the code that uses the keys
// before the code checking the cursor. // before the code checking the cursor.
if (keys.length > 0) { if (keys.length > 0) {
console.log('Array of matching keys', keys); console.log('Array of matching keys', keys)
} }
// It's important to note that the cursor and returned keys // It's important to note that the cursor and returned keys
@@ -41,11 +41,11 @@ function scan () {
// 'An iteration starts when the cursor is set to 0, // 'An iteration starts when the cursor is set to 0,
// and terminates when the cursor returned by the server is 0.' // and terminates when the cursor returned by the server is 0.'
if (cursor === '0') { if (cursor === '0') {
return console.log('Iteration complete'); return console.log('Iteration complete')
} }
return scan(); return scan()
} }
); )
} }
scan(); scan()

View File

@@ -1,26 +1,27 @@
'use strict'; 'use strict'
var redis = require('redis'); var redis = require('redis')
var client = redis.createClient(); var client = redis.createClient()
client.on('error', function (err) { 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.set('string key', 'string val', console.log)
client.hset('hash key', 'hashtest 1', 'some value', console.log); client.hset('hash key', 'hashtest 1', 'some value', console.log)
client.hset(['hash key', 'hashtest 2', 'some other value'], console.log); client.hset(['hash key', 'hashtest 2', 'some other value'], console.log)
client.hkeys('hash key', function (err, replies) { client.hkeys('hash key', function (err, replies) {
if (err) { if (err) {
return console.error('error response - ' + err); return console.error('error response - ' + err)
} }
console.log(replies.length + ' replies:'); console.log(replies.length + ' replies:')
replies.forEach(function (reply, i) { replies.forEach(function (reply, i) {
console.log(' ' + i + ': ' + reply); console.log(' ' + i + ': ' + reply)
}); })
}); })
client.quit(function (err, res) { client.quit(function (err, res) {
console.log('Exiting from quit command.'); if (err) throw err
}); console.log('Exiting from quit command.')
})

View File

@@ -1,19 +1,19 @@
'use strict'; 'use strict'
var redis = require('redis'); var redis = require('redis')
var client = redis.createClient(); var client = redis.createClient()
client.sadd('mylist', 1); client.sadd('mylist', 1)
client.sadd('mylist', 2); client.sadd('mylist', 2)
client.sadd('mylist', 3); client.sadd('mylist', 3)
client.set('weight_1', 5); client.set('weight_1', 5)
client.set('weight_2', 500); client.set('weight_2', 500)
client.set('weight_3', 1); client.set('weight_3', 1)
client.set('object_1', 'foo'); client.set('object_1', 'foo')
client.set('object_2', 'bar'); client.set('object_2', 'bar')
client.set('object_3', 'qux'); 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 // Prints Reply: qux,foo,bar

View File

@@ -1,17 +1,19 @@
'use strict'; 'use strict'
// Sending commands in response to other commands. // Sending commands in response to other commands.
// This example runs 'type' against every key in the database // 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) { client.keys('*', function (err, keys) {
if (err) throw err
keys.forEach(function (key, pos) { keys.forEach(function (key, pos) {
client.type(key, function (err, keytype) { client.type(key, function (err, keytype) {
console.log(key + ' is ' + keytype); if (err) throw err
console.log(key + ' is ' + keytype)
if (pos === (keys.length - 1)) { if (pos === (keys.length - 1)) {
client.quit(); client.quit()
} }
}); })
}); })
}); })

View File

@@ -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 // build a map of all keys and their types
client.keys('*', function (err, allKeys) { 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 allKeys.forEach(function (key, pos) { // use second arg of forEach to get pos
client.type(key, function (err, type) { client.type(key, function (err, type) {
keyTypes[key] = type; if (err) throw err
keyTypes[key] = type
if (pos === allKeys.length - 1) { // callbacks all run in order if (pos === allKeys.length - 1) { // callbacks all run in order
console.dir(keyTypes); console.dir(keyTypes)
} }
}); })
}); })
}); })

View File

@@ -1,32 +1,33 @@
'use strict'; 'use strict'
var redis = require('redis'); var redis = require('redis')
var client = redis.createClient('/tmp/redis.sock'); var client = redis.createClient('/tmp/redis.sock')
var profiler = require('v8-profiler'); var profiler = require('v8-profiler')
client.on('connect', function () { client.on('connect', function () {
console.log('Got Unix socket connection.'); console.log('Got Unix socket connection.')
}); })
client.on('error', function (err) { 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 () { setInterval(function () {
client.get('space chars'); client.get('space chars')
}, 100); }, 100)
function done () { function done () {
client.info(function (err, reply) { client.info(function (err, reply) {
console.log(reply.toString()); if (err) throw err
client.quit(); console.log(reply.toString())
}); client.quit()
})
} }
setTimeout(function () { setTimeout(function () {
console.log('Taking snapshot.'); console.log('Taking snapshot.')
profiler.takeSnapshot(); profiler.takeSnapshot()
done(); done()
}, 5000); }, 5000)

View File

@@ -1,33 +1,36 @@
'use strict'; 'use strict'
// A simple web server that generates dyanmic content based on responses from Redis // A simple web server that generates dyanmic content based on responses from Redis
var http = require('http'); var http = require('http')
var redisClient = require('redis').createClient(); var redisClient = require('redis').createClient()
http.createServer(function (request, response) { // The server http.createServer(function (request, response) { // The server
response.writeHead(200, { response.writeHead(200, {
'Content-Type': 'text/plain' 'Content-Type': 'text/plain'
}); })
var redisInfo, totalRequests; var redisInfo, totalRequests
redisClient.info(function (err, reply) { redisClient.info(function (err, reply) {
redisInfo = reply; // stash response in outer scope if (err) throw err
}); redisInfo = reply // stash response in outer scope
})
redisClient.incr('requests', function (err, reply) { redisClient.incr('requests', function (err, reply) {
totalRequests = reply; // stash response in outer scope if (err) throw err
}); totalRequests = reply // stash response in outer scope
redisClient.hincrby('ip', request.connection.remoteAddress, 1); })
redisClient.hincrby('ip', request.connection.remoteAddress, 1)
redisClient.hgetall('ip', function (err, reply) { 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 // 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' + 'Redis info:\n' + redisInfo + '\n' +
'Total requests: ' + totalRequests + '\n\n' + 'Total requests: ' + totalRequests + '\n\n' +
'IP count: \n'); 'IP count: \n')
Object.keys(reply).forEach(function (ip) { Object.keys(reply).forEach(function (ip) {
response.write(' ' + ip + ': ' + reply[ip] + '\n'); response.write(' ' + ip + ': ' + reply[ip] + '\n')
}); })
response.end(); response.end()
}); })
}).listen(80); }).listen(80)

795
index.js

File diff suppressed because it is too large Load Diff

View File

@@ -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) { function Command (command, args, callback, callOnWrite) {
this.command = command; this.command = command
this.args = args; this.args = args
this.bufferArgs = false; this.bufferArgs = false
this.callback = callback; this.callback = callback
this.callOnWrite = callOnWrite; this.callOnWrite = callOnWrite
if (betterStackTraces) { if (betterStackTraces) {
this.error = new Error(); this.error = new Error()
} }
} }
module.exports = Command; module.exports = Command

View File

@@ -1,100 +1,99 @@
'use strict'; 'use strict'
var commands = require('redis-commands'); var commands = require('redis-commands')
var Multi = require('./multi'); var Multi = require('./multi')
var RedisClient = require('../').RedisClient; var RedisClient = require('../').RedisClient
var Command = require('./command'); var Command = require('./command')
// TODO: Rewrite this including the individual commands into a Commands class // TODO: Rewrite this including the individual commands into a Commands class
// that provided a functionality to add new commands to the client // that provided a functionality to add new commands to the client
commands.list.forEach(function (command) { commands.list.forEach(function (command) {
// Some rare Redis commands use special characters in their command name // Some rare Redis commands use special characters in their command name
// Convert those to a underscore to prevent using invalid function names // Convert those to a underscore to prevent using invalid function names
var commandName = command.replace(/(?:^([0-9])|[^a-zA-Z0-9_$])/g, '_$1'); var commandName = command.replace(/(?:^([0-9])|[^a-zA-Z0-9_$])/g, '_$1')
// Do not override existing functions // Do not override existing functions
if (!RedisClient.prototype[command]) { if (!RedisClient.prototype[command]) {
RedisClient.prototype[command] = function () { RedisClient.prototype[command] = function () {
var arr; var arr
var len = arguments.length; var len = arguments.length
var callback; var callback
var i = 0; var i = 0
if (Array.isArray(arguments[0])) { if (Array.isArray(arguments[0])) {
arr = arguments[0]; arr = arguments[0]
if (len === 2) { if (len === 2) {
callback = arguments[1]; callback = arguments[1]
} }
} else if (len > 1 && Array.isArray(arguments[1])) { } else if (len > 1 && Array.isArray(arguments[1])) {
if (len === 3) { if (len === 3) {
callback = arguments[2]; callback = arguments[2]
} }
len = arguments[1].length; len = arguments[1].length
arr = new Array(len + 1); arr = new Array(len + 1)
arr[0] = arguments[0]; arr[0] = arguments[0]
for (; i < len; i += 1) { for (; i < len; i += 1) {
arr[i + 1] = arguments[1][i]; arr[i + 1] = arguments[1][i]
} }
} else { } else {
// The later should not be the average use case // The later should not be the average use case
if (len !== 0 && (typeof arguments[len - 1] === 'function' || typeof arguments[len - 1] === 'undefined')) { if (len !== 0 && (typeof arguments[len - 1] === 'function' || typeof arguments[len - 1] === 'undefined')) {
len--; len--
callback = arguments[len]; callback = arguments[len]
} }
arr = new Array(len); arr = new Array(len)
for (; i < len; i += 1) { for (; i < len; i += 1) {
arr[i] = arguments[i]; arr[i] = arguments[i]
} }
} }
return this.internalSendCommand(new Command(command, arr, callback)); return this.internalSendCommand(new Command(command, arr, callback))
}; }
if (RedisClient.prototype[command] !== commandName) { if (RedisClient.prototype[command] !== commandName) {
Object.defineProperty(RedisClient.prototype[command], 'name', { Object.defineProperty(RedisClient.prototype[command], 'name', {
value: commandName value: commandName
}); })
} }
} }
// Do not override existing functions // Do not override existing functions
if (!Multi.prototype[command]) { if (!Multi.prototype[command]) {
Multi.prototype[command] = function () { Multi.prototype[command] = function () {
var arr; var arr
var len = arguments.length; var len = arguments.length
var callback; var callback
var i = 0; var i = 0
if (Array.isArray(arguments[0])) { if (Array.isArray(arguments[0])) {
arr = arguments[0]; arr = arguments[0]
if (len === 2) { if (len === 2) {
callback = arguments[1]; callback = arguments[1]
} }
} else if (len > 1 && Array.isArray(arguments[1])) { } else if (len > 1 && Array.isArray(arguments[1])) {
if (len === 3) { if (len === 3) {
callback = arguments[2]; callback = arguments[2]
} }
len = arguments[1].length; len = arguments[1].length
arr = new Array(len + 1); arr = new Array(len + 1)
arr[0] = arguments[0]; arr[0] = arguments[0]
for (; i < len; i += 1) { for (; i < len; i += 1) {
arr[i + 1] = arguments[1][i]; arr[i + 1] = arguments[1][i]
} }
} else { } else {
// The later should not be the average use case // The later should not be the average use case
if (len !== 0 && (typeof arguments[len - 1] === 'function' || typeof arguments[len - 1] === 'undefined')) { if (len !== 0 && (typeof arguments[len - 1] === 'function' || typeof arguments[len - 1] === 'undefined')) {
len--; len--
callback = arguments[len]; callback = arguments[len]
} }
arr = new Array(len); arr = new Array(len)
for (; i < len; i += 1) { for (; i < len; i += 1) {
arr[i] = arguments[i]; arr[i] = arguments[i]
} }
} }
this.queue.push(new Command(command, arr, callback)); this.queue.push(new Command(command, arr, callback))
return this; return this
}; }
if (Multi.prototype[command] !== commandName) { if (Multi.prototype[command] !== commandName) {
Object.defineProperty(Multi.prototype[command], 'name', { Object.defineProperty(Multi.prototype[command], 'name', {
value: commandName value: commandName
}); })
} }
} }
}); })

View File

@@ -1,80 +1,75 @@
'use strict'; 'use strict'
var utils = require('./utils'); var utils = require('./utils')
var URL = require('url'); var URL = require('url')
module.exports = function createClient (portArg, hostArg, options) { module.exports = function createClient (portArg, hostArg, options) {
if (typeof portArg === 'number' || (typeof portArg === 'string' && /^\d+$/.test(portArg))) {
if (typeof portArg === 'number' || typeof portArg === 'string' && /^\d+$/.test(portArg)) { var host
var host;
if (typeof hostArg === 'string') { if (typeof hostArg === 'string') {
host = hostArg; host = hostArg
} else { } else {
if (options && hostArg) { if (options && hostArg) {
throw new TypeError('Unknown type of connection in createClient()'); throw new TypeError('Unknown type of connection in createClient()')
} }
options = options || hostArg; options = options || hostArg
} }
options = utils.clone(options); options = utils.clone(options)
options.host = host || options.host; options.host = host || options.host
options.port = portArg; options.port = portArg
} else if (typeof portArg === 'string' || (portArg && portArg.url)) {
options = utils.clone(portArg.url ? portArg : (hostArg || options))
} else if (typeof portArg === 'string' || portArg && portArg.url) { var parsed = URL.parse(portArg.url || portArg, true, true)
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]]] // [redis:]//[[user][:password]@][host][:port][/db-number][?db=db-number[&password=bar[&option=value]]]
if (parsed.slashes) { // We require slashes if (parsed.slashes) { // We require slashes
if (parsed.auth) { if (parsed.auth) {
options.password = parsed.auth.split(':')[1]; options.password = parsed.auth.split(':')[1]
} }
if (parsed.protocol && parsed.protocol !== 'redis:') { 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!'); 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 !== '/') { if (parsed.pathname && parsed.pathname !== '/') {
options.db = parsed.pathname.substr(1); options.db = parsed.pathname.substr(1)
} }
if (parsed.hostname) { if (parsed.hostname) {
options.host = parsed.hostname; options.host = parsed.hostname
} }
if (parsed.port) { if (parsed.port) {
options.port = parsed.port; options.port = parsed.port
} }
if (parsed.search !== '') { if (parsed.search !== '') {
var elem; var elem
for (elem in parsed.query) { for (elem in parsed.query) {
// If options are passed twice, only the parsed options will be used // If options are passed twice, only the parsed options will be used
if (elem in options) { if (elem in options) {
if (options[elem] === parsed.query[elem]) { if (options[elem] === parsed.query[elem]) {
console.warn('nodeRedis: WARNING: You passed the ' + elem + ' option twice!'); console.warn('nodeRedis: WARNING: You passed the ' + elem + ' option twice!')
} else { } else {
throw new RangeError('The ' + elem + ' option is added twice and does not match'); throw new RangeError('The ' + elem + ' option is added twice and does not match')
} }
} }
options[elem] = parsed.query[elem]; options[elem] = parsed.query[elem]
} }
} }
} else if (parsed.hostname) { } else if (parsed.hostname) {
throw new RangeError('The redis url must begin with slashes "//" or contain slashes after the redis protocol'); throw new RangeError('The redis url must begin with slashes "//" or contain slashes after the redis protocol')
} else { } else {
options.path = portArg; options.path = portArg
} }
} else if (typeof portArg === 'object' || portArg === undefined) { } else if (typeof portArg === 'object' || portArg === undefined) {
options = utils.clone(portArg || options); options = utils.clone(portArg || options)
options.host = options.host || hostArg; options.host = options.host || hostArg
if (portArg && arguments.length !== 1) { if (portArg && arguments.length !== 1) {
throw new TypeError('To many arguments passed to createClient. Please only pass the options object'); throw new TypeError('To many arguments passed to createClient. Please only pass the options object')
} }
} }
if (!options) { if (!options) {
throw new TypeError('Unknown type of connection in createClient()'); throw new TypeError('Unknown type of connection in createClient()')
} }
return options; return options
}; }

View File

@@ -1,59 +1,59 @@
'use strict'; 'use strict'
var util = require('util'); var util = require('util')
var assert = require('assert'); var assert = require('assert')
var RedisError = require('redis-parser').RedisError; var RedisError = require('redis-parser').RedisError
var ADD_STACKTRACE = false; var ADD_STACKTRACE = false
function AbortError (obj, stack) { function AbortError (obj, stack) {
assert(obj, 'The options argument is required'); assert(obj, 'The options argument is required')
assert.strictEqual(typeof obj, 'object', 'The options argument has to be of type object'); assert.strictEqual(typeof obj, 'object', 'The options argument has to be of type object')
RedisError.call(this, obj.message, ADD_STACKTRACE); RedisError.call(this, obj.message, ADD_STACKTRACE)
Object.defineProperty(this, 'message', { Object.defineProperty(this, 'message', {
value: obj.message || '', value: obj.message || '',
configurable: true, configurable: true,
writable: true writable: true
}); })
if (stack || stack === undefined) { if (stack || stack === undefined) {
Error.captureStackTrace(this, AbortError); Error.captureStackTrace(this, AbortError)
} }
for (var keys = Object.keys(obj), key = keys.pop(); key; key = keys.pop()) { for (var keys = Object.keys(obj), key = keys.pop(); key; key = keys.pop()) {
this[key] = obj[key]; this[key] = obj[key]
} }
} }
function AggregateError (obj) { function AggregateError (obj) {
assert(obj, 'The options argument is required'); assert(obj, 'The options argument is required')
assert.strictEqual(typeof obj, 'object', 'The options argument has to be of type object'); assert.strictEqual(typeof obj, 'object', 'The options argument has to be of type object')
AbortError.call(this, obj, ADD_STACKTRACE); AbortError.call(this, obj, ADD_STACKTRACE)
Object.defineProperty(this, 'message', { Object.defineProperty(this, 'message', {
value: obj.message || '', value: obj.message || '',
configurable: true, configurable: true,
writable: true writable: true
}); })
Error.captureStackTrace(this, AggregateError); Error.captureStackTrace(this, AggregateError)
for (var keys = Object.keys(obj), key = keys.pop(); key; key = keys.pop()) { for (var keys = Object.keys(obj), key = keys.pop(); key; key = keys.pop()) {
this[key] = obj[key]; this[key] = obj[key]
} }
} }
util.inherits(AbortError, RedisError); util.inherits(AbortError, RedisError)
util.inherits(AggregateError, AbortError); util.inherits(AggregateError, AbortError)
Object.defineProperty(AbortError.prototype, 'name', { Object.defineProperty(AbortError.prototype, 'name', {
value: 'AbortError', value: 'AbortError',
configurable: true, configurable: true,
writable: true writable: true
}); })
Object.defineProperty(AggregateError.prototype, 'name', { Object.defineProperty(AggregateError.prototype, 'name', {
value: 'AggregateError', value: 'AggregateError',
configurable: true, configurable: true,
writable: true writable: true
}); })
module.exports = { module.exports = {
AbortError: AbortError, AbortError: AbortError,
AggregateError: AggregateError AggregateError: AggregateError
}; }

View File

@@ -1,11 +1,11 @@
'use strict'; 'use strict'
var index = require('../'); var index = require('../')
function debug () { function debug () {
if (index.debugMode) { if (index.debugMode) {
console.error.apply(null, arguments); console.error.apply(null, arguments)
} }
} }
module.exports = debug; module.exports = debug

View File

@@ -1,10 +1,10 @@
'use strict'; 'use strict'
var utils = require('./utils'); var utils = require('./utils')
var debug = require('./debug'); var debug = require('./debug')
var RedisClient = require('../').RedisClient; var RedisClient = require('../').RedisClient
var Command = require('./command'); var Command = require('./command')
var noop = function () {}; var noop = function () {}
/********************************************** /**********************************************
All documented and exposed API belongs in here All documented and exposed API belongs in here
@@ -14,20 +14,20 @@ All documented and exposed API belongs in here
RedisClient.prototype.sendCommand = function (command, args, callback) { RedisClient.prototype.sendCommand = function (command, args, callback) {
// Throw to fail early instead of relying in order in this case // Throw to fail early instead of relying in order in this case
if (typeof command !== 'string') { if (typeof command !== 'string') {
throw new TypeError('Wrong input type "' + (command !== null && command !== undefined ? command.constructor.name : command) + '" for command name'); throw new TypeError('Wrong input type "' + (command !== null && command !== undefined ? command.constructor.name : command) + '" for command name')
} }
if (!Array.isArray(args)) { if (!Array.isArray(args)) {
if (args === undefined || args === null) { if (args === undefined || args === null) {
args = []; args = []
} else if (typeof args === 'function' && callback === undefined) { } else if (typeof args === 'function' && callback === undefined) {
callback = args; callback = args
args = []; args = []
} else { } else {
throw new TypeError('Wrong input type "' + args.constructor.name + '" for args'); throw new TypeError('Wrong input type "' + args.constructor.name + '" for args')
} }
} }
if (typeof callback !== 'function' && callback !== undefined) { if (typeof callback !== 'function' && callback !== undefined) {
throw new TypeError('Wrong input type "' + (callback !== null ? callback.constructor.name : 'null') + '" for callback function'); 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 // Using the raw multi command is only possible with this function
@@ -37,13 +37,13 @@ RedisClient.prototype.sendCommand = function (command, args, callback) {
// but this might change from time to time and at the moment there's no good way to distinguish them // but this might change from time to time and at the moment there's no good way to distinguish them
// from each other, so let's just do it do it this way for the time being // from each other, so let's just do it do it this way for the time being
if (command === 'multi' || typeof this[command] !== 'function') { if (command === 'multi' || typeof this[command] !== 'function') {
return this.internalSendCommand(new Command(command, args, callback)); return this.internalSendCommand(new Command(command, args, callback))
} }
if (typeof callback === 'function') { if (typeof callback === 'function') {
args = args.concat([callback]); // Prevent manipulating the input array args = args.concat([callback]) // Prevent manipulating the input array
} }
return this[command].apply(this, args); return this[command].apply(this, args)
}; }
RedisClient.prototype.end = function (flush) { RedisClient.prototype.end = function (flush) {
// Flush queue if wanted // Flush queue if wanted
@@ -51,62 +51,62 @@ RedisClient.prototype.end = function (flush) {
this.flushAndError({ this.flushAndError({
message: 'Connection forcefully ended and command aborted.', message: 'Connection forcefully ended and command aborted.',
code: 'NR_CLOSED' code: 'NR_CLOSED'
}); })
} else if (arguments.length === 0) { } else if (arguments.length === 0) {
this.warn( this.warn(
'Using .end() without the flush parameter is deprecated and throws from v.3.0.0 on.\n' + '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.' 'Please check the documentation (https://github.com/NodeRedis/nodeRedis) and explicitly use flush.'
); )
} }
// Clear retryTimer // Clear retryTimer
if (this.retryTimer) { if (this.retryTimer) {
clearTimeout(this.retryTimer); clearTimeout(this.retryTimer)
this.retryTimer = null; this.retryTimer = null
} }
this.stream.removeAllListeners(); this.stream.removeAllListeners()
this.stream.on('error', noop); this.stream.on('error', noop)
this.connected = false; this.connected = false
this.ready = false; this.ready = false
this.closing = true; this.closing = true
return this.stream.destroySoon(); return this.stream.destroySoon()
}; }
RedisClient.prototype.unref = function () { RedisClient.prototype.unref = function () {
if (this.connected) { if (this.connected) {
debug("Unref'ing the socket connection"); debug('Unref\'ing the socket connection')
this.stream.unref(); this.stream.unref()
} else { } else {
debug('Not connected yet, will unref later'); debug('Not connected yet, will unref later')
this.once('connect', function () { this.once('connect', function () {
this.unref(); this.unref()
}); })
} }
}; }
RedisClient.prototype.duplicate = function (options, callback) { RedisClient.prototype.duplicate = function (options, callback) {
if (typeof options === 'function') { if (typeof options === 'function') {
callback = options; callback = options
options = null; options = null
} }
var existingOptions = utils.clone(this.options); var existingOptions = utils.clone(this.options)
options = utils.clone(options); options = utils.clone(options)
for (var elem in options) { for (var elem in options) {
existingOptions[elem] = options[elem]; existingOptions[elem] = options[elem]
} }
var client = new RedisClient(existingOptions); var client = new RedisClient(existingOptions)
client.selectedDb = this.selectedDb; client.selectedDb = this.selectedDb
if (typeof callback === 'function') { if (typeof callback === 'function') {
var readyListener = function () { var readyListener = function () {
callback(null, client); callback(null, client)
client.removeAllListeners(errorListener); client.removeAllListeners(errorListener)
};
var errorListener = function (err) {
callback(err);
client.end(true);
};
client.once('ready', readyListener);
client.once('error', errorListener);
return;
} }
return client; var errorListener = function (err) {
}; callback(err)
client.end(true)
}
client.once('ready', readyListener)
client.once('error', errorListener)
return
}
return client
}

View File

@@ -1,12 +1,12 @@
'use strict'; 'use strict'
var utils = require('./utils'); var utils = require('./utils')
var debug = require('./debug'); var debug = require('./debug')
var Multi = require('./multi'); var Multi = require('./multi')
var Command = require('./command'); var Command = require('./command')
var noPasswordIsSet = /no password is set/; var noPasswordIsSet = /no password is set/
var loading = /LOADING/; var loading = /LOADING/
var RedisClient = require('../').RedisClient; var RedisClient = require('../').RedisClient
/******************************************************************************************** /********************************************************************************************
Replace built-in redis functions Replace built-in redis functions
@@ -22,62 +22,62 @@ var RedisClient = require('../').RedisClient;
********************************************************************************************/ ********************************************************************************************/
RedisClient.prototype.multi = function multi (args) { RedisClient.prototype.multi = function multi (args) {
var multi = new Multi(this, args); var multi = new Multi(this, args)
multi.exec = multi.EXEC = multi.execTransaction; multi.exec = multi.EXEC = multi.execTransaction
return multi; 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 // 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) { RedisClient.prototype.batch = function batch (args) {
return new Multi(this, args); return new Multi(this, args)
}; }
function selectCallback (self, db, callback) { function selectCallback (self, db, callback) {
return function (err, res) { return function (err, res) {
if (err === null) { if (err === null) {
// Store db in this.selectDb to restore it on reconnect // Store db in this.selectDb to restore it on reconnect
self.selectedDb = db; self.selectedDb = db
}
utils.callbackOrEmit(self, callback, err, res)
} }
utils.callbackOrEmit(self, callback, err, res);
};
} }
RedisClient.prototype.select = function select (db, callback) { 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) { Multi.prototype.select = function select (db, callback) {
this.queue.push(new Command('select', [db], selectCallback(this._client, db, callback))); this.queue.push(new Command('select', [db], selectCallback(this._client, db, callback)))
return this; return this
}; }
RedisClient.prototype.monitor = RedisClient.prototype.MONITOR = function monitor (callback) { 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 // 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 self = this
var callOnWrite = function () { var callOnWrite = function () {
// Activating monitor mode has to happen before Redis returned the callback. The monitor result is returned first. // Activating monitor mode has to happen before Redis returned the callback. 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. // Therefore we expect the command to be properly processed. If this is not the case, it's not an issue either.
self.monitoring = true; self.monitoring = true
}; }
return this.internalSendCommand(new Command('monitor', [], callback, callOnWrite)); return this.internalSendCommand(new Command('monitor', [], callback, callOnWrite))
}; }
// Only works with batch, not in a transaction // Only works with batch, not in a transaction
Multi.prototype.monitor = function monitor (callback) { 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 // 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) { if (this.exec !== this.execTransaction) {
var self = this; var self = this
var callOnWrite = function () { var callOnWrite = function () {
self._client.monitoring = true; self._client.monitoring = true
}; }
this.queue.push(new Command('monitor', [], callback, callOnWrite)); this.queue.push(new Command('monitor', [], callback, callOnWrite))
return this; return this
} }
// Set multi monitoring to indicate the exec that it should abort // Set multi monitoring to indicate the exec that it should abort
// Remove this "hack" as soon as Redis might fix this // Remove this "hack" as soon as Redis might fix this
this.monitoring = true; this.monitoring = true
return this; return this
}; }
function quitCallback (self, callback) { function quitCallback (self, callback) {
return function (err, res) { return function (err, res) {
@@ -87,531 +87,531 @@ function quitCallback (self, callback) {
// or the offline queue is deactivated and the command was rejected right away // or the offline queue is deactivated and the command was rejected right away
// or the stream is not writable // or the stream is not writable
// or while sending the quit, the connection ended / closed // or while sending the quit, the connection ended / closed
err = null; err = null
res = 'OK'; res = 'OK'
} }
utils.callbackOrEmit(self, callback, err, res); utils.callbackOrEmit(self, callback, err, res)
if (self.stream.writable) { if (self.stream.writable) {
// If the socket is still alive, kill it. This could happen if quit got a NR_CLOSED error code // If the socket is still alive, kill it. This could happen if quit got a NR_CLOSED error code
self.stream.destroy(); self.stream.destroy()
}
} }
};
} }
RedisClient.prototype.quit = function quit (callback) { RedisClient.prototype.quit = function quit (callback) {
// TODO: Consider this for v.3 // TODO: Consider this for v.3
// Allow the quit command to be fired as soon as possible to prevent it landing in the offline queue. // 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; // this.ready = this.offlineQueue.length === 0;
var backpressureIndicator = this.internalSendCommand(new Command('quit', [], quitCallback(this, callback))); 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 // Calling quit should always end the connection, no matter if there's a connection or not
this.closing = true; this.closing = true
this.ready = false; this.ready = false
return backpressureIndicator; return backpressureIndicator
}; }
// Only works with batch, not in a transaction // Only works with batch, not in a transaction
Multi.prototype.quit = function quit (callback) { Multi.prototype.quit = function quit (callback) {
var self = this._client; var self = this._client
var callOnWrite = function () { var callOnWrite = function () {
// If called in a multi context, we expect redis is available // If called in a multi context, we expect redis is available
self.closing = true; self.closing = true
self.ready = false; self.ready = false
}; }
this.queue.push(new Command('quit', [], quitCallback(self, callback), callOnWrite)); this.queue.push(new Command('quit', [], quitCallback(self, callback), callOnWrite))
return this; return this
}; }
function infoCallback (self, callback) { function infoCallback (self, callback) {
return function (err, res) { return function (err, res) {
if (res) { if (res) {
var obj = {}; var obj = {}
var lines = res.toString().split('\r\n'); var lines = res.toString().split('\r\n')
var line, parts, subParts; var line, parts, subParts
for (var i = 0; i < lines.length; i++) { for (var i = 0; i < lines.length; i++) {
parts = lines[i].split(':'); parts = lines[i].split(':')
if (parts[1]) { if (parts[1]) {
if (parts[0].indexOf('db') === 0) { if (parts[0].indexOf('db') === 0) {
subParts = parts[1].split(','); subParts = parts[1].split(',')
obj[parts[0]] = {}; obj[parts[0]] = {}
while (line = subParts.pop()) { for (line = subParts.pop(); line !== undefined; line = subParts.pop()) {
line = line.split('='); line = line.split('=')
obj[parts[0]][line[0]] = +line[1]; obj[parts[0]][line[0]] = +line[1]
} }
} else { } else {
obj[parts[0]] = parts[1]; obj[parts[0]] = parts[1]
} }
} }
} }
obj.versions = []; obj.versions = []
if (obj.redis_version) { if (obj.redis_version) {
obj.redis_version.split('.').forEach(function (num) { obj.redis_version.split('.').forEach(function (num) {
obj.versions.push(+num); obj.versions.push(+num)
}); })
} }
// Expose info key/values to users // Expose info key/values to users
self.serverInfo = obj; self.serverInfo = obj
} else { } else {
self.serverInfo = {}; self.serverInfo = {}
}
utils.callbackOrEmit(self, callback, err, res)
} }
utils.callbackOrEmit(self, callback, err, res);
};
} }
// Store info in this.serverInfo after each call // Store info in this.serverInfo after each call
RedisClient.prototype.info = function info (section, callback) { RedisClient.prototype.info = function info (section, callback) {
var args = []; var args = []
if (typeof section === 'function') { if (typeof section === 'function') {
callback = section; callback = section
} else if (section !== undefined) { } else if (section !== undefined) {
args = Array.isArray(section) ? section : [section]; args = Array.isArray(section) ? section : [section]
} }
return this.internalSendCommand(new Command('info', args, infoCallback(this, callback))); return this.internalSendCommand(new Command('info', args, infoCallback(this, callback)))
}; }
Multi.prototype.info = function info (section, callback) { Multi.prototype.info = function info (section, callback) {
var args = []; var args = []
if (typeof section === 'function') { if (typeof section === 'function') {
callback = section; callback = section
} else if (section !== undefined) { } else if (section !== undefined) {
args = Array.isArray(section) ? section : [section]; args = Array.isArray(section) ? section : [section]
} }
this.queue.push(new Command('info', args, infoCallback(this._client, callback))); this.queue.push(new Command('info', args, infoCallback(this._client, callback)))
return this; return this
}; }
function authCallback (self, pass, callback) { function authCallback (self, pass, callback) {
return function (err, res) { return function (err, res) {
if (err) { if (err) {
if (noPasswordIsSet.test(err.message)) { if (noPasswordIsSet.test(err.message)) {
self.warn('Warning: Redis server does not require a password, but a password was supplied.'); self.warn('Warning: Redis server does not require a password, but a password was supplied.')
err = null; err = null
res = 'OK'; res = 'OK'
} else if (loading.test(err.message)) { } else if (loading.test(err.message)) {
// If redis is still loading the db, it will not authenticate and everything else will fail // If redis is still loading the db, it will not authenticate and everything else will fail
debug('Redis still loading, trying to authenticate later'); debug('Redis still loading, trying to authenticate later')
setTimeout(function () { setTimeout(function () {
self.auth(pass, callback); self.auth(pass, callback)
}, 100); }, 100)
return; return
} }
} }
utils.callbackOrEmit(self, callback, err, res); utils.callbackOrEmit(self, callback, err, res)
}; }
} }
RedisClient.prototype.auth = function auth (pass, callback) { 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. // Stash auth for connect and reconnect.
this.authPass = pass; this.authPass = pass
var ready = this.ready; var ready = this.ready
this.ready = ready || this.offlineQueue.length === 0; this.ready = ready || this.offlineQueue.length === 0
var tmp = this.internalSendCommand(new Command('auth', [pass], authCallback(this, pass, callback))); var tmp = this.internalSendCommand(new Command('auth', [pass], authCallback(this, pass, callback)))
this.ready = ready; this.ready = ready
return tmp; return tmp
}; }
// Only works with batch, not in a transaction // Only works with batch, not in a transaction
Multi.prototype.auth = function auth (pass, callback) { 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. // Stash auth for connect and reconnect.
this.authPass = pass; this.authPass = pass
this.queue.push(new Command('auth', [pass], authCallback(this._client, callback))); this.queue.push(new Command('auth', [pass], authCallback(this._client, callback)))
return this; return this
}; }
RedisClient.prototype.client = function client () { RedisClient.prototype.client = function client () {
var arr, var arr
len = arguments.length, var len = arguments.length
callback, var callback
i = 0; var i = 0
if (Array.isArray(arguments[0])) { if (Array.isArray(arguments[0])) {
arr = arguments[0]; arr = arguments[0]
callback = arguments[1]; callback = arguments[1]
} else if (Array.isArray(arguments[1])) { } else if (Array.isArray(arguments[1])) {
if (len === 3) { if (len === 3) {
callback = arguments[2]; callback = arguments[2]
} }
len = arguments[1].length; len = arguments[1].length
arr = new Array(len + 1); arr = new Array(len + 1)
arr[0] = arguments[0]; arr[0] = arguments[0]
for (; i < len; i += 1) { for (; i < len; i += 1) {
arr[i + 1] = arguments[1][i]; arr[i + 1] = arguments[1][i]
} }
} else { } else {
len = arguments.length; len = arguments.length
// The later should not be the average use case // The later should not be the average use case
if (len !== 0 && (typeof arguments[len - 1] === 'function' || typeof arguments[len - 1] === 'undefined')) { if (len !== 0 && (typeof arguments[len - 1] === 'function' || typeof arguments[len - 1] === 'undefined')) {
len--; len--
callback = arguments[len]; callback = arguments[len]
} }
arr = new Array(len); arr = new Array(len)
for (; i < len; i += 1) { for (; i < len; i += 1) {
arr[i] = arguments[i]; arr[i] = arguments[i]
} }
} }
var self = this; var self = this
var callOnWrite = undefined; var callOnWrite
// CLIENT REPLY ON|OFF|SKIP // CLIENT REPLY ON|OFF|SKIP
/* istanbul ignore next: TODO: Remove this as soon as Travis runs Redis 3.2 */ /* istanbul ignore next: TODO: Remove this as soon as Travis runs Redis 3.2 */
if (arr.length === 2 && arr[0].toString().toUpperCase() === 'REPLY') { if (arr.length === 2 && arr[0].toString().toUpperCase() === 'REPLY') {
var replyOnOff = arr[1].toString().toUpperCase(); var replyOnOff = arr[1].toString().toUpperCase()
if (replyOnOff === 'ON' || replyOnOff === 'OFF' || replyOnOff === 'SKIP') { if (replyOnOff === 'ON' || replyOnOff === 'OFF' || replyOnOff === 'SKIP') {
callOnWrite = function () { callOnWrite = function () {
self.reply = replyOnOff; self.reply = replyOnOff
};
} }
} }
return this.internalSendCommand(new Command('client', arr, callback, callOnWrite)); }
}; return this.internalSendCommand(new Command('client', arr, callback, callOnWrite))
}
Multi.prototype.client = function client () { Multi.prototype.client = function client () {
var arr, var arr
len = arguments.length, var len = arguments.length
callback, var callback
i = 0; var i = 0
if (Array.isArray(arguments[0])) { if (Array.isArray(arguments[0])) {
arr = arguments[0]; arr = arguments[0]
callback = arguments[1]; callback = arguments[1]
} else if (Array.isArray(arguments[1])) { } else if (Array.isArray(arguments[1])) {
if (len === 3) { if (len === 3) {
callback = arguments[2]; callback = arguments[2]
} }
len = arguments[1].length; len = arguments[1].length
arr = new Array(len + 1); arr = new Array(len + 1)
arr[0] = arguments[0]; arr[0] = arguments[0]
for (; i < len; i += 1) { for (; i < len; i += 1) {
arr[i + 1] = arguments[1][i]; arr[i + 1] = arguments[1][i]
} }
} else { } else {
len = arguments.length; len = arguments.length
// The later should not be the average use case // The later should not be the average use case
if (len !== 0 && (typeof arguments[len - 1] === 'function' || typeof arguments[len - 1] === 'undefined')) { if (len !== 0 && (typeof arguments[len - 1] === 'function' || typeof arguments[len - 1] === 'undefined')) {
len--; len--
callback = arguments[len]; callback = arguments[len]
} }
arr = new Array(len); arr = new Array(len)
for (; i < len; i += 1) { for (; i < len; i += 1) {
arr[i] = arguments[i]; arr[i] = arguments[i]
} }
} }
var self = this._client; var self = this._client
var callOnWrite = undefined; var callOnWrite
// CLIENT REPLY ON|OFF|SKIP // CLIENT REPLY ON|OFF|SKIP
/* istanbul ignore next: TODO: Remove this as soon as Travis runs Redis 3.2 */ /* istanbul ignore next: TODO: Remove this as soon as Travis runs Redis 3.2 */
if (arr.length === 2 && arr[0].toString().toUpperCase() === 'REPLY') { if (arr.length === 2 && arr[0].toString().toUpperCase() === 'REPLY') {
var replyOnOff = arr[1].toString().toUpperCase(); var replyOnOff = arr[1].toString().toUpperCase()
if (replyOnOff === 'ON' || replyOnOff === 'OFF' || replyOnOff === 'SKIP') { if (replyOnOff === 'ON' || replyOnOff === 'OFF' || replyOnOff === 'SKIP') {
callOnWrite = function () { callOnWrite = function () {
self.reply = replyOnOff; self.reply = replyOnOff
};
} }
} }
this.queue.push(new Command('client', arr, callback, callOnWrite)); }
return this; this.queue.push(new Command('client', arr, callback, callOnWrite))
}; return this
}
RedisClient.prototype.hmset = function hmset () { RedisClient.prototype.hmset = function hmset () {
var arr, var arr
len = arguments.length, var len = arguments.length
callback, var callback
i = 0; var i = 0
if (Array.isArray(arguments[0])) { if (Array.isArray(arguments[0])) {
arr = arguments[0]; arr = arguments[0]
callback = arguments[1]; callback = arguments[1]
} else if (Array.isArray(arguments[1])) { } else if (Array.isArray(arguments[1])) {
if (len === 3) { if (len === 3) {
callback = arguments[2]; callback = arguments[2]
} }
len = arguments[1].length; len = arguments[1].length
arr = new Array(len + 1); arr = new Array(len + 1)
arr[0] = arguments[0]; arr[0] = arguments[0]
for (; i < len; i += 1) { for (; i < len; i += 1) {
arr[i + 1] = arguments[1][i]; arr[i + 1] = arguments[1][i]
} }
} else if (typeof arguments[1] === 'object' && (arguments.length === 2 || arguments.length === 3 && (typeof arguments[2] === 'function' || typeof arguments[2] === 'undefined'))) { } else if (typeof arguments[1] === 'object' && (arguments.length === 2 || (arguments.length === 3 && (typeof arguments[2] === 'function' || typeof arguments[2] === 'undefined')))) {
arr = [arguments[0]]; arr = [arguments[0]]
for (var field in arguments[1]) { for (var field in arguments[1]) {
arr.push(field, arguments[1][field]); arr.push(field, arguments[1][field])
} }
callback = arguments[2]; callback = arguments[2]
} else { } else {
len = arguments.length; len = arguments.length
// The later should not be the average use case // The later should not be the average use case
if (len !== 0 && (typeof arguments[len - 1] === 'function' || typeof arguments[len - 1] === 'undefined')) { if (len !== 0 && (typeof arguments[len - 1] === 'function' || typeof arguments[len - 1] === 'undefined')) {
len--; len--
callback = arguments[len]; callback = arguments[len]
} }
arr = new Array(len); arr = new Array(len)
for (; i < len; i += 1) { for (; i < len; i += 1) {
arr[i] = arguments[i]; arr[i] = arguments[i]
} }
} }
return this.internalSendCommand(new Command('hmset', arr, callback)); return this.internalSendCommand(new Command('hmset', arr, callback))
}; }
Multi.prototype.hmset = function hmset () { Multi.prototype.hmset = function hmset () {
var arr, var arr
len = arguments.length, var len = arguments.length
callback, var callback
i = 0; var i = 0
if (Array.isArray(arguments[0])) { if (Array.isArray(arguments[0])) {
arr = arguments[0]; arr = arguments[0]
callback = arguments[1]; callback = arguments[1]
} else if (Array.isArray(arguments[1])) { } else if (Array.isArray(arguments[1])) {
if (len === 3) { if (len === 3) {
callback = arguments[2]; callback = arguments[2]
} }
len = arguments[1].length; len = arguments[1].length
arr = new Array(len + 1); arr = new Array(len + 1)
arr[0] = arguments[0]; arr[0] = arguments[0]
for (; i < len; i += 1) { for (; i < len; i += 1) {
arr[i + 1] = arguments[1][i]; arr[i + 1] = arguments[1][i]
} }
} else if (typeof arguments[1] === 'object' && (arguments.length === 2 || arguments.length === 3 && (typeof arguments[2] === 'function' || typeof arguments[2] === 'undefined'))) { } else if (typeof arguments[1] === 'object' && (arguments.length === 2 || (arguments.length === 3 && (typeof arguments[2] === 'function' || typeof arguments[2] === 'undefined')))) {
arr = [arguments[0]]; arr = [arguments[0]]
for (var field in arguments[1]) { for (var field in arguments[1]) {
arr.push(field, arguments[1][field]); arr.push(field, arguments[1][field])
} }
callback = arguments[2]; callback = arguments[2]
} else { } else {
len = arguments.length; len = arguments.length
// The later should not be the average use case // The later should not be the average use case
if (len !== 0 && (typeof arguments[len - 1] === 'function' || typeof arguments[len - 1] === 'undefined')) { if (len !== 0 && (typeof arguments[len - 1] === 'function' || typeof arguments[len - 1] === 'undefined')) {
len--; len--
callback = arguments[len]; callback = arguments[len]
} }
arr = new Array(len); arr = new Array(len)
for (; i < len; i += 1) { for (; i < len; i += 1) {
arr[i] = arguments[i]; arr[i] = arguments[i]
} }
} }
this.queue.push(new Command('hmset', arr, callback)); this.queue.push(new Command('hmset', arr, callback))
return this; return this
}; }
RedisClient.prototype.subscribe = function subscribe () { RedisClient.prototype.subscribe = function subscribe () {
var arr, var arr
len = arguments.length, var len = arguments.length
callback, var callback
i = 0; var i = 0
if (Array.isArray(arguments[0])) { if (Array.isArray(arguments[0])) {
arr = arguments[0].slice(0); arr = arguments[0].slice(0)
callback = arguments[1]; callback = arguments[1]
} else { } else {
len = arguments.length; len = arguments.length
// The later should not be the average use case // The later should not be the average use case
if (len !== 0 && (typeof arguments[len - 1] === 'function' || typeof arguments[len - 1] === 'undefined')) { if (len !== 0 && (typeof arguments[len - 1] === 'function' || typeof arguments[len - 1] === 'undefined')) {
len--; len--
callback = arguments[len]; callback = arguments[len]
} }
arr = new Array(len); arr = new Array(len)
for (; i < len; i += 1) { for (; i < len; i += 1) {
arr[i] = arguments[i]; arr[i] = arguments[i]
} }
} }
var self = this; var self = this
var callOnWrite = function () { var callOnWrite = function () {
self.pubSubMode = self.pubSubMode || self.commandQueue.length + 1; self.pubSubMode = self.pubSubMode || self.commandQueue.length + 1
}; }
return this.internalSendCommand(new Command('subscribe', arr, callback, callOnWrite)); return this.internalSendCommand(new Command('subscribe', arr, callback, callOnWrite))
}; }
Multi.prototype.subscribe = function subscribe () { Multi.prototype.subscribe = function subscribe () {
var arr, var arr
len = arguments.length, var len = arguments.length
callback, var callback
i = 0; var i = 0
if (Array.isArray(arguments[0])) { if (Array.isArray(arguments[0])) {
arr = arguments[0].slice(0); arr = arguments[0].slice(0)
callback = arguments[1]; callback = arguments[1]
} else { } else {
len = arguments.length; len = arguments.length
// The later should not be the average use case // The later should not be the average use case
if (len !== 0 && (typeof arguments[len - 1] === 'function' || typeof arguments[len - 1] === 'undefined')) { if (len !== 0 && (typeof arguments[len - 1] === 'function' || typeof arguments[len - 1] === 'undefined')) {
len--; len--
callback = arguments[len]; callback = arguments[len]
} }
arr = new Array(len); arr = new Array(len)
for (; i < len; i += 1) { for (; i < len; i += 1) {
arr[i] = arguments[i]; arr[i] = arguments[i]
} }
} }
var self = this._client; var self = this._client
var callOnWrite = function () { var callOnWrite = function () {
self.pubSubMode = self.pubSubMode || self.commandQueue.length + 1; self.pubSubMode = self.pubSubMode || self.commandQueue.length + 1
}; }
this.queue.push(new Command('subscribe', arr, callback, callOnWrite)); this.queue.push(new Command('subscribe', arr, callback, callOnWrite))
return this; return this
}; }
RedisClient.prototype.unsubscribe = function unsubscribe () { RedisClient.prototype.unsubscribe = function unsubscribe () {
var arr, var arr
len = arguments.length, var len = arguments.length
callback, var callback
i = 0; var i = 0
if (Array.isArray(arguments[0])) { if (Array.isArray(arguments[0])) {
arr = arguments[0].slice(0); arr = arguments[0].slice(0)
callback = arguments[1]; callback = arguments[1]
} else { } else {
len = arguments.length; len = arguments.length
// The later should not be the average use case // The later should not be the average use case
if (len !== 0 && (typeof arguments[len - 1] === 'function' || typeof arguments[len - 1] === 'undefined')) { if (len !== 0 && (typeof arguments[len - 1] === 'function' || typeof arguments[len - 1] === 'undefined')) {
len--; len--
callback = arguments[len]; callback = arguments[len]
} }
arr = new Array(len); arr = new Array(len)
for (; i < len; i += 1) { for (; i < len; i += 1) {
arr[i] = arguments[i]; arr[i] = arguments[i]
} }
} }
var self = this; var self = this
var callOnWrite = function () { 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 // 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; self.pubSubMode = self.pubSubMode || self.commandQueue.length + 1
}; }
return this.internalSendCommand(new Command('unsubscribe', arr, callback, callOnWrite)); return this.internalSendCommand(new Command('unsubscribe', arr, callback, callOnWrite))
}; }
Multi.prototype.unsubscribe = function unsubscribe () { Multi.prototype.unsubscribe = function unsubscribe () {
var arr, var arr
len = arguments.length, var len = arguments.length
callback, var callback
i = 0; var i = 0
if (Array.isArray(arguments[0])) { if (Array.isArray(arguments[0])) {
arr = arguments[0].slice(0); arr = arguments[0].slice(0)
callback = arguments[1]; callback = arguments[1]
} else { } else {
len = arguments.length; len = arguments.length
// The later should not be the average use case // The later should not be the average use case
if (len !== 0 && (typeof arguments[len - 1] === 'function' || typeof arguments[len - 1] === 'undefined')) { if (len !== 0 && (typeof arguments[len - 1] === 'function' || typeof arguments[len - 1] === 'undefined')) {
len--; len--
callback = arguments[len]; callback = arguments[len]
} }
arr = new Array(len); arr = new Array(len)
for (; i < len; i += 1) { for (; i < len; i += 1) {
arr[i] = arguments[i]; arr[i] = arguments[i]
} }
} }
var self = this._client; var self = this._client
var callOnWrite = function () { 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 // 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; self.pubSubMode = self.pubSubMode || self.commandQueue.length + 1
}; }
this.queue.push(new Command('unsubscribe', arr, callback, callOnWrite)); this.queue.push(new Command('unsubscribe', arr, callback, callOnWrite))
return this; return this
}; }
RedisClient.prototype.psubscribe = function psubscribe () { RedisClient.prototype.psubscribe = function psubscribe () {
var arr, var arr
len = arguments.length, var len = arguments.length
callback, var callback
i = 0; var i = 0
if (Array.isArray(arguments[0])) { if (Array.isArray(arguments[0])) {
arr = arguments[0].slice(0); arr = arguments[0].slice(0)
callback = arguments[1]; callback = arguments[1]
} else { } else {
len = arguments.length; len = arguments.length
// The later should not be the average use case // The later should not be the average use case
if (len !== 0 && (typeof arguments[len - 1] === 'function' || typeof arguments[len - 1] === 'undefined')) { if (len !== 0 && (typeof arguments[len - 1] === 'function' || typeof arguments[len - 1] === 'undefined')) {
len--; len--
callback = arguments[len]; callback = arguments[len]
} }
arr = new Array(len); arr = new Array(len)
for (; i < len; i += 1) { for (; i < len; i += 1) {
arr[i] = arguments[i]; arr[i] = arguments[i]
} }
} }
var self = this; var self = this
var callOnWrite = function () { var callOnWrite = function () {
self.pubSubMode = self.pubSubMode || self.commandQueue.length + 1; self.pubSubMode = self.pubSubMode || self.commandQueue.length + 1
}; }
return this.internalSendCommand(new Command('psubscribe', arr, callback, callOnWrite)); return this.internalSendCommand(new Command('psubscribe', arr, callback, callOnWrite))
}; }
Multi.prototype.psubscribe = function psubscribe () { Multi.prototype.psubscribe = function psubscribe () {
var arr, var arr
len = arguments.length, var len = arguments.length
callback, var callback
i = 0; var i = 0
if (Array.isArray(arguments[0])) { if (Array.isArray(arguments[0])) {
arr = arguments[0].slice(0); arr = arguments[0].slice(0)
callback = arguments[1]; callback = arguments[1]
} else { } else {
len = arguments.length; len = arguments.length
// The later should not be the average use case // The later should not be the average use case
if (len !== 0 && (typeof arguments[len - 1] === 'function' || typeof arguments[len - 1] === 'undefined')) { if (len !== 0 && (typeof arguments[len - 1] === 'function' || typeof arguments[len - 1] === 'undefined')) {
len--; len--
callback = arguments[len]; callback = arguments[len]
} }
arr = new Array(len); arr = new Array(len)
for (; i < len; i += 1) { for (; i < len; i += 1) {
arr[i] = arguments[i]; arr[i] = arguments[i]
} }
} }
var self = this._client; var self = this._client
var callOnWrite = function () { var callOnWrite = function () {
self.pubSubMode = self.pubSubMode || self.commandQueue.length + 1; self.pubSubMode = self.pubSubMode || self.commandQueue.length + 1
}; }
this.queue.push(new Command('psubscribe', arr, callback, callOnWrite)); this.queue.push(new Command('psubscribe', arr, callback, callOnWrite))
return this; return this
}; }
RedisClient.prototype.punsubscribe = function punsubscribe () { RedisClient.prototype.punsubscribe = function punsubscribe () {
var arr, var arr
len = arguments.length, var len = arguments.length
callback, var callback
i = 0; var i = 0
if (Array.isArray(arguments[0])) { if (Array.isArray(arguments[0])) {
arr = arguments[0].slice(0); arr = arguments[0].slice(0)
callback = arguments[1]; callback = arguments[1]
} else { } else {
len = arguments.length; len = arguments.length
// The later should not be the average use case // The later should not be the average use case
if (len !== 0 && (typeof arguments[len - 1] === 'function' || typeof arguments[len - 1] === 'undefined')) { if (len !== 0 && (typeof arguments[len - 1] === 'function' || typeof arguments[len - 1] === 'undefined')) {
len--; len--
callback = arguments[len]; callback = arguments[len]
} }
arr = new Array(len); arr = new Array(len)
for (; i < len; i += 1) { for (; i < len; i += 1) {
arr[i] = arguments[i]; arr[i] = arguments[i]
} }
} }
var self = this; var self = this
var callOnWrite = function () { 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 // 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; self.pubSubMode = self.pubSubMode || self.commandQueue.length + 1
}; }
return this.internalSendCommand(new Command('punsubscribe', arr, callback, callOnWrite)); return this.internalSendCommand(new Command('punsubscribe', arr, callback, callOnWrite))
}; }
Multi.prototype.punsubscribe = function punsubscribe () { Multi.prototype.punsubscribe = function punsubscribe () {
var arr, var arr
len = arguments.length, var len = arguments.length
callback, var callback
i = 0; var i = 0
if (Array.isArray(arguments[0])) { if (Array.isArray(arguments[0])) {
arr = arguments[0].slice(0); arr = arguments[0].slice(0)
callback = arguments[1]; callback = arguments[1]
} else { } else {
len = arguments.length; len = arguments.length
// The later should not be the average use case // The later should not be the average use case
if (len !== 0 && (typeof arguments[len - 1] === 'function' || typeof arguments[len - 1] === 'undefined')) { if (len !== 0 && (typeof arguments[len - 1] === 'function' || typeof arguments[len - 1] === 'undefined')) {
len--; len--
callback = arguments[len]; callback = arguments[len]
} }
arr = new Array(len); arr = new Array(len)
for (; i < len; i += 1) { for (; i < len; i += 1) {
arr[i] = arguments[i]; arr[i] = arguments[i]
} }
} }
var self = this._client; var self = this._client
var callOnWrite = function () { 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 // 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; self.pubSubMode = self.pubSubMode || self.commandQueue.length + 1
}; }
this.queue.push(new Command('punsubscribe', arr, callback, callOnWrite)); this.queue.push(new Command('punsubscribe', arr, callback, callOnWrite))
return this; return this
}; }

View File

@@ -1,21 +1,21 @@
'use strict'; 'use strict'
var Queue = require('double-ended-queue'); var Queue = require('double-ended-queue')
var utils = require('./utils'); var utils = require('./utils')
var Command = require('./command'); var Command = require('./command')
function Multi (client, args) { function Multi (client, args) {
this._client = client; this._client = client
this.queue = new Queue(); this.queue = new Queue()
var command, tmpArgs; var command, tmpArgs
if (args) { // Either undefined or an array. Fail hard if it's not an array if (args) { // Either undefined or an array. Fail hard if it's not an array
for (var i = 0; i < args.length; i++) { for (var i = 0; i < args.length; i++) {
command = args[i][0]; command = args[i][0]
tmpArgs = args[i].slice(1); tmpArgs = args[i].slice(1)
if (Array.isArray(command)) { if (Array.isArray(command)) {
this[command[0]].apply(this, command.slice(1).concat(tmpArgs)); this[command[0]].apply(this, command.slice(1).concat(tmpArgs))
} else { } else {
this[command].apply(this, tmpArgs); this[command].apply(this, tmpArgs)
} }
} }
} }
@@ -23,70 +23,70 @@ function Multi (client, args) {
function pipelineTransactionCommand (self, commandObj, index) { function pipelineTransactionCommand (self, commandObj, index) {
// Queueing is done first, then the commands are executed // Queueing is done first, then the commands are executed
var tmp = commandObj.callback; var tmp = commandObj.callback
commandObj.callback = function (err, reply) { commandObj.callback = function (err, reply) {
// Ignore the multi command. This is applied by nodeRedis and the user does not benefit by it // Ignore the multi command. This is applied by nodeRedis and the user does not benefit by it
if (err && index !== -1) { if (err && index !== -1) {
if (tmp) { if (tmp) {
tmp(err); tmp(err)
} }
err.position = index; err.position = index
self.errors.push(err); self.errors.push(err)
} }
// Keep track of who wants buffer responses: // Keep track of who wants buffer responses:
// By the time the callback is called the commandObj got the bufferArgs attribute attached // By the time the callback is called the commandObj got the bufferArgs attribute attached
self.wantsBuffers[index] = commandObj.bufferArgs; self.wantsBuffers[index] = commandObj.bufferArgs
commandObj.callback = tmp; commandObj.callback = tmp
}; }
self._client.internalSendCommand(commandObj); self._client.internalSendCommand(commandObj)
} }
Multi.prototype.execAtomic = function execAtomic (callback) { Multi.prototype.execAtomic = function execAtomic (callback) {
if (this.queue.length < 2) { if (this.queue.length < 2) {
return this.execBatch(callback); return this.execBatch(callback)
} }
return this.exec(callback); return this.exec(callback)
}; }
function multiCallback (self, err, replies) { function multiCallback (self, err, replies) {
var i = 0, commandObj; var i = 0
if (err) { if (err) {
err.errors = self.errors; err.errors = self.errors
if (self.callback) { if (self.callback) {
self.callback(err); self.callback(err)
// Exclude connection errors so that those errors won't be emitted twice // Exclude connection errors so that those errors won't be emitted twice
} else if (err.code !== 'CONNECTION_BROKEN') { } else if (err.code !== 'CONNECTION_BROKEN') {
self._client.emit('error', err); self._client.emit('error', err)
} }
return; return
} }
if (replies) { if (replies) {
while (commandObj = self.queue.shift()) { for (var commandObj = self.queue.shift(); commandObj !== undefined; commandObj = self.queue.shift()) {
if (replies[i] instanceof Error) { if (replies[i] instanceof Error) {
var match = replies[i].message.match(utils.errCode); var match = replies[i].message.match(utils.errCode)
// LUA script could return user errors that don't behave like all other errors! // LUA script could return user errors that don't behave like all other errors!
if (match) { if (match) {
replies[i].code = match[1]; replies[i].code = match[1]
} }
replies[i].command = commandObj.command.toUpperCase(); replies[i].command = commandObj.command.toUpperCase()
if (typeof commandObj.callback === 'function') { if (typeof commandObj.callback === 'function') {
commandObj.callback(replies[i]); commandObj.callback(replies[i])
} }
} else { } else {
// If we asked for strings, even in detectBuffers mode, then return strings: // If we asked for strings, even in detectBuffers mode, then return strings:
replies[i] = self._client.handleReply(replies[i], commandObj.command, self.wantsBuffers[i]); replies[i] = self._client.handleReply(replies[i], commandObj.command, self.wantsBuffers[i])
if (typeof commandObj.callback === 'function') { if (typeof commandObj.callback === 'function') {
commandObj.callback(null, replies[i]); commandObj.callback(null, replies[i])
} }
} }
i++; i++
} }
} }
if (self.callback) { if (self.callback) {
self.callback(null, replies); self.callback(null, replies)
} }
} }
@@ -94,94 +94,94 @@ Multi.prototype.execTransaction = function execTransaction (callback) {
if (this.monitoring || this._client.monitoring) { if (this.monitoring || this._client.monitoring) {
var err = new RangeError( var err = new RangeError(
'Using transaction with a client that is in monitor mode does not work due to faulty return values of Redis.' 'Using transaction with a client that is in monitor mode does not work due to faulty return values of Redis.'
); )
err.command = 'EXEC'; err.command = 'EXEC'
err.code = 'EXECABORT'; err.code = 'EXECABORT'
return utils.replyInOrder(this._client, callback, err); return utils.replyInOrder(this._client, callback, err)
} }
var self = this; var self = this
var len = self.queue.length; var len = self.queue.length
self.errors = []; self.errors = []
self.callback = callback; self.callback = callback
self._client.cork(); self._client.cork()
self.wantsBuffers = new Array(len); self.wantsBuffers = new Array(len)
pipelineTransactionCommand(self, new Command('multi', []), -1); pipelineTransactionCommand(self, new Command('multi', []), -1)
// Drain queue, callback will catch 'QUEUED' or error // Drain queue, callback will catch 'QUEUED' or error
for (var index = 0; index < len; index++) { for (var index = 0; index < len; index++) {
// The commands may not be shifted off, since they are needed in the result handler // The commands may not be shifted off, since they are needed in the result handler
pipelineTransactionCommand(self, self.queue.get(index), index); pipelineTransactionCommand(self, self.queue.get(index), index)
} }
self._client.internalSendCommand(new Command('exec', [], function (err, replies) { self._client.internalSendCommand(new Command('exec', [], function (err, replies) {
multiCallback(self, err, replies); multiCallback(self, err, replies)
})); }))
self._client.uncork(); self._client.uncork()
return !self._client.shouldBuffer; return !self._client.shouldBuffer
}; }
function batchCallback (self, cb, i) { function batchCallback (self, cb, i) {
return function batchCallback (err, res) { return function batchCallback (err, res) {
if (err) { if (err) {
self.results[i] = err; self.results[i] = err
// Add the position to the error // Add the position to the error
self.results[i].position = i; self.results[i].position = i
} else { } else {
self.results[i] = res; self.results[i] = res
}
cb(err, res)
} }
cb(err, res);
};
} }
Multi.prototype.exec = Multi.prototype.execBatch = function execBatch (callback) { Multi.prototype.exec = Multi.prototype.execBatch = function execBatch (callback) {
var self = this; var self = this
var len = self.queue.length; var len = self.queue.length
var index = 0; var index = 0
var commandObj; var commandObj
if (len === 0) { if (len === 0) {
utils.replyInOrder(self._client, callback, null, []); utils.replyInOrder(self._client, callback, null, [])
return !self._client.shouldBuffer; return !self._client.shouldBuffer
} }
self._client.cork(); self._client.cork()
if (!callback) { if (!callback) {
while (commandObj = self.queue.shift()) { for (commandObj = self.queue.shift(); commandObj !== undefined; commandObj = self.queue.shift()) {
self._client.internalSendCommand(commandObj); self._client.internalSendCommand(commandObj)
} }
self._client.uncork(); self._client.uncork()
return !self._client.shouldBuffer; return !self._client.shouldBuffer
} }
var callbackWithoutOwnCb = function (err, res) { var callbackWithoutOwnCb = function (err, res) {
if (err) { if (err) {
self.results.push(err); self.results.push(err)
// Add the position to the error // Add the position to the error
var i = self.results.length - 1; var i = self.results.length - 1
self.results[i].position = i; self.results[i].position = i
} else { } else {
self.results.push(res); self.results.push(res)
} }
// Do not emit an error here. Otherwise each error would result in one emit. // Do not emit an error here. Otherwise each error would result in one emit.
// The errors will be returned in the result anyway // The errors will be returned in the result anyway
}; }
var lastCallback = function (cb) { var lastCallback = function (cb) {
return function (err, res) { return function (err, res) {
cb(err, res); cb(err, res)
callback(null, self.results); callback(null, self.results)
}; }
}; }
self.results = []; self.results = []
while (commandObj = self.queue.shift()) { for (commandObj = self.queue.shift(); commandObj !== undefined; commandObj = self.queue.shift()) {
if (typeof commandObj.callback === 'function') { if (typeof commandObj.callback === 'function') {
commandObj.callback = batchCallback(self, commandObj.callback, index); commandObj.callback = batchCallback(self, commandObj.callback, index)
} else { } else {
commandObj.callback = callbackWithoutOwnCb; commandObj.callback = callbackWithoutOwnCb
} }
if (typeof callback === 'function' && index === len - 1) { if (typeof callback === 'function' && index === len - 1) {
commandObj.callback = lastCallback(commandObj.callback); commandObj.callback = lastCallback(commandObj.callback)
} }
this._client.internalSendCommand(commandObj); this._client.internalSendCommand(commandObj)
index++; index++
} }
self._client.uncork(); self._client.uncork()
return !self._client.shouldBuffer; return !self._client.shouldBuffer
}; }
module.exports = Multi; module.exports = Multi

View File

@@ -1,96 +1,95 @@
'use strict'; 'use strict'
// hgetall converts its replies to an Object. If the reply is empty, null is returned. // 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 // These function are only called with internal data and have therefore always the same instanceof X
function replyToObject (reply) { function replyToObject (reply) {
// The reply might be a string or a buffer if this is called in a transaction (multi) // The reply might be a string or a buffer if this is called in a transaction (multi)
if (reply.length === 0 || !(reply instanceof Array)) { if (reply.length === 0 || !(reply instanceof Array)) {
return null; return null
} }
var obj = {}; var obj = {}
for (var i = 0; i < reply.length; i += 2) { for (var i = 0; i < reply.length; i += 2) {
obj[reply[i].toString('binary')] = reply[i + 1]; obj[reply[i].toString('binary')] = reply[i + 1]
} }
return obj; return obj
} }
function replyToStrings (reply) { function replyToStrings (reply) {
if (reply instanceof Buffer) { if (reply instanceof Buffer) {
return reply.toString(); return reply.toString()
} }
if (reply instanceof Array) { if (reply instanceof Array) {
var res = new Array(reply.length); var res = new Array(reply.length)
for (var i = 0; i < reply.length; i++) { for (var i = 0; i < reply.length; i++) {
// Recursively call the function as slowlog returns deep nested replies // Recursively call the function as slowlog returns deep nested replies
res[i] = replyToStrings(reply[i]); res[i] = replyToStrings(reply[i])
} }
return res; return res
} }
return reply; return reply
} }
// Deep clone arbitrary objects with arrays. Can't handle cyclic structures (results in a range error) // 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) // Any attribute with a non primitive value besides object and array will be passed by reference (e.g. Buffers, Maps, Functions)
function clone (obj) { function clone (obj) {
var copy; var copy
if (Array.isArray(obj)) { if (Array.isArray(obj)) {
copy = new Array(obj.length); copy = new Array(obj.length)
for (var i = 0; i < obj.length; i++) { for (var i = 0; i < obj.length; i++) {
copy[i] = clone(obj[i]); copy[i] = clone(obj[i])
} }
return copy; return copy
} }
if (Object.prototype.toString.call(obj) === '[object Object]') { if (Object.prototype.toString.call(obj) === '[object Object]') {
copy = {}; copy = {}
var elements = Object.keys(obj); var elements = Object.keys(obj)
var elem; for (var elem = elements.pop(); elem !== undefined; elem = elements.pop()) {
while (elem = elements.pop()) { copy[elem] = clone(obj[elem])
copy[elem] = clone(obj[elem]);
} }
return copy; return copy
} }
return obj; return obj
} }
function convenienceClone (obj) { function convenienceClone (obj) {
return clone(obj) || {}; return clone(obj) || {}
} }
function callbackOrEmit (self, callback, err, res) { function callbackOrEmit (self, callback, err, res) {
if (callback) { if (callback) {
callback(err, res); callback(err, res)
} else if (err) { } else if (err) {
self.emit('error', err); self.emit('error', err)
} }
} }
function replyInOrder (self, callback, err, res, queue) { function replyInOrder (self, callback, err, res, queue) {
// If the queue is explicitly passed, use that, otherwise fall back to the offline queue first, // 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 // as there might be commands in both queues at the same time
var commandObj; var commandObj
if (queue) { if (queue) {
commandObj = queue.peekBack(); commandObj = queue.peekBack()
} else { } else {
commandObj = self.offlineQueue.peekBack() || self.commandQueue.peekBack(); commandObj = self.offlineQueue.peekBack() || self.commandQueue.peekBack()
} }
if (!commandObj) { if (!commandObj) {
process.nextTick(function () { process.nextTick(function () {
callbackOrEmit(self, callback, err, res); callbackOrEmit(self, callback, err, res)
}); })
} else { } else {
var tmp = commandObj.callback; var tmp = commandObj.callback
commandObj.callback = tmp ? commandObj.callback = tmp
function (e, r) { ? function (e, r) {
tmp(e, r); tmp(e, r)
callbackOrEmit(self, callback, err, res); callbackOrEmit(self, callback, err, res)
} : }
function (e, r) { : function (e) {
if (e) { if (e) {
self.emit('error', e); self.emit('error', e)
}
callbackOrEmit(self, callback, err, res)
} }
callbackOrEmit(self, callback, err, res);
};
} }
} }
@@ -98,8 +97,8 @@ module.exports = {
replyToStrings: replyToStrings, replyToStrings: replyToStrings,
replyToObject: replyToObject, replyToObject: replyToObject,
errCode: /^([A-Z]+)\s+(.+)$/, errCode: /^([A-Z]+)\s+(.+)$/,
monitorRegex: /^[0-9]{10,11}\.[0-9]+ \[[0-9]+ .+\]( ".+?")+$/, monitorRegex: /^[0-9]{10,11}\.[0-9]+ \[[0-9]+ .+]( ".+?")+$/,
clone: convenienceClone, clone: convenienceClone,
callbackOrEmit: callbackOrEmit, callbackOrEmit: callbackOrEmit,
replyInOrder: replyInOrder replyInOrder: replyInOrder
}; }

View File

@@ -23,12 +23,13 @@
"test": "nyc --cache mocha ./test/*.js ./test/commands/*.js --timeout=8000", "test": "nyc --cache mocha ./test/*.js ./test/commands/*.js --timeout=8000",
"posttest": "npm run coverage", "posttest": "npm run coverage",
"compare": "node benchmarks/diff_multi_bench_output.js beforeBench.txt afterBench.txt", "compare": "node benchmarks/diff_multi_bench_output.js beforeBench.txt afterBench.txt",
"lint": "standard . --fix" "lint": "standard --fix"
}, },
"dependencies": { "dependencies": {
"double-ended-queue": "^2.1.0-0", "double-ended-queue": "^2.1.0-0",
"redis-commands": "^1.2.0", "redis-commands": "^1.2.0",
"redis-parser": "^2.6.0" "redis-parser": "^2.6.0",
"safe-buffer": "^5.0.1"
}, },
"engines": { "engines": {
"node": ">=6" "node": ">=6"
@@ -49,6 +50,11 @@
"type": "git", "type": "git",
"url": "git://github.com/NodeRedis/node_redis.git" "url": "git://github.com/NodeRedis/node_redis.git"
}, },
"standard": {
"envs": [
"mocha"
]
},
"bugs": { "bugs": {
"url": "https://github.com/NodeRedis/node_redis/issues" "url": "https://github.com/NodeRedis/node_redis/issues"
}, },

View File

@@ -1,295 +1,295 @@
'use strict'; 'use strict'
var assert = require('assert'); var assert = require('assert')
var config = require('./lib/config'); var config = require('./lib/config')
var helper = require('./helper'); var helper = require('./helper')
var redis = config.redis; var redis = config.redis
if (process.platform === 'win32') {
// TODO: Fix redis process spawn on windows // TODO: Fix redis process spawn on windows
return; if (process.platform !== 'win32') {
} describe('client authentication', function () {
describe('client authentication', function () {
before(function (done) { before(function (done) {
helper.stopRedis(function () { helper.stopRedis(function () {
helper.startRedis('./conf/password.conf', done); helper.startRedis('./conf/password.conf', done)
}); })
}); })
helper.allTests({ helper.allTests({
allConnections: true allConnections: true
}, function (ip, args) { }, function (ip, args) {
describe('using ' + ip, function () { describe('using ' + ip, function () {
var auth = 'porkchopsandwiches'; var auth = 'porkchopsandwiches'
var client = null; var client = null
beforeEach(function () { beforeEach(function () {
client = null; client = null
}); })
afterEach(function () { afterEach(function () {
// Explicitly ignore still running commands // Explicitly ignore still running commands
// The ready command could still be running // The ready command could still be running
client.end(false); client.end(false)
}); })
it("allows auth to be provided with 'auth' method", function (done) { it('allows auth to be provided with \'auth\' method', function (done) {
if (helper.redisProcess().spawnFailed()) this.skip(); if (helper.redisProcess().spawnFailed()) this.skip()
client = redis.createClient.apply(null, args); client = redis.createClient.apply(null, args)
client.auth(auth, function (err, res) { client.auth(auth, function (err, res) {
assert.strictEqual(null, err); assert.strictEqual(null, err)
assert.strictEqual('OK', res.toString()); assert.strictEqual('OK', res.toString())
return done(err); return done(err)
}); })
}); })
it('support redis 2.4 with retrying auth commands if still loading', function (done) { it('support redis 2.4 with retrying auth commands if still loading', function (done) {
if (helper.redisProcess().spawnFailed()) this.skip(); if (helper.redisProcess().spawnFailed()) this.skip()
client = redis.createClient.apply(null, args); client = redis.createClient.apply(null, args)
var time = Date.now(); var time = Date.now()
client.auth(auth, function (err, res) { client.auth(auth, function (err, res) {
assert.strictEqual('retry worked', res); assert.strictEqual(err, null)
var now = Date.now(); 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 // 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 >= 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)); 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(); done()
}); })
var tmp = client.commandQueue.get(0).callback; var tmp = client.commandQueue.get(0).callback
client.commandQueue.get(0).callback = function (err, res) { client.commandQueue.get(0).callback = function (err) {
assert.strictEqual(err, null)
client.auth = function (pass, callback) { client.auth = function (pass, callback) {
callback(null, 'retry worked'); callback(null, 'retry worked')
}; }
tmp(new Error('ERR redis is still LOADING')); tmp(new Error('ERR redis is still LOADING'))
}; }
}); })
it('emits error when auth is bad without callback', function (done) { it('emits error when auth is bad without callback', function (done) {
if (helper.redisProcess().spawnFailed()) this.skip(); if (helper.redisProcess().spawnFailed()) this.skip()
client = redis.createClient.apply(null, args); client = redis.createClient.apply(null, args)
client.once('error', function (err) { client.once('error', function (err) {
assert.strictEqual(err.command, 'AUTH'); assert.strictEqual(err.command, 'AUTH')
assert.ok(/ERR invalid password/.test(err.message)); assert.ok(/ERR invalid password/.test(err.message))
return done(); return done()
}); })
client.auth(auth + 'bad'); client.auth(auth + 'bad')
}); })
it('returns an error when auth is bad (empty string) with a callback', function (done) { it('returns an error when auth is bad (empty string) with a callback', function (done) {
if (helper.redisProcess().spawnFailed()) this.skip(); if (helper.redisProcess().spawnFailed()) this.skip()
client = redis.createClient.apply(null, args); client = redis.createClient.apply(null, args)
client.auth('', function (err, res) { client.auth('', function (err) {
assert.strictEqual(err.command, 'AUTH'); assert.strictEqual(err.command, 'AUTH')
assert.ok(/ERR invalid password/.test(err.message)); assert.ok(/ERR invalid password/.test(err.message))
done(); done()
}); })
}); })
if (ip === 'IPv4') { if (ip === 'IPv4') {
it('allows auth to be provided as part of redis url and do not fire commands before auth is done', function (done) { it('allows auth to be provided as part of redis url and do not fire commands before auth is done', function (done) {
if (helper.redisProcess().spawnFailed()) this.skip(); if (helper.redisProcess().spawnFailed()) this.skip()
var end = helper.callFuncAfter(done, 2); var end = helper.callFuncAfter(done, 2)
client = redis.createClient('redis://:' + auth + '@' + config.HOST[ip] + ':' + config.PORT); client = redis.createClient('redis://:' + auth + '@' + config.HOST[ip] + ':' + config.PORT)
client.on('ready', function () { client.on('ready', function () {
end(); end()
}); })
// The info command may be used while loading but not if not yet authenticated // The info command may be used while loading but not if not yet authenticated
client.info(function (err, res) { client.info(function (err) {
assert(!err); assert.strictEqual(err, null)
end(); end(err)
}); })
}); })
it('allows auth and database to be provided as part of redis url query parameter', function (done) { it('allows auth and database to be provided as part of redis url query parameter', function (done) {
if (helper.redisProcess().spawnFailed()) this.skip(); if (helper.redisProcess().spawnFailed()) this.skip()
client = redis.createClient('redis://' + config.HOST[ip] + ':' + config.PORT + '?db=2&password=' + auth); client = redis.createClient('redis://' + config.HOST[ip] + ':' + config.PORT + '?db=2&password=' + auth)
assert.strictEqual(client.options.db, '2'); assert.strictEqual(client.options.db, '2')
assert.strictEqual(client.options.password, auth); assert.strictEqual(client.options.password, auth)
assert.strictEqual(client.authPass, auth); assert.strictEqual(client.authPass, auth)
client.on('ready', function () { client.on('ready', function () {
// Set a key so the used database is returned in the info command // Set a key so the used database is returned in the info command
client.set('foo', 'bar'); client.set('foo', 'bar')
client.get('foo'); client.get('foo')
assert.strictEqual(client.serverInfo.db2, undefined); assert.strictEqual(client.serverInfo.db2, undefined)
// Using the info command should update the serverInfo // Using the info command should update the serverInfo
client.info(function (err, res) { client.info(function (err) {
assert(typeof client.serverInfo.db2 === 'object'); assert.strictEqual(err, null)
}); assert(typeof client.serverInfo.db2 === 'object')
client.flushdb(done); })
}); client.flushdb(done)
}); })
})
} }
it('allows auth to be provided as config option for client', function (done) { it('allows auth to be provided as config option for client', function (done) {
if (helper.redisProcess().spawnFailed()) this.skip(); if (helper.redisProcess().spawnFailed()) this.skip()
var args = config.configureClient(ip, { var args = config.configureClient(ip, {
authPass: auth authPass: auth
}); })
client = redis.createClient.apply(null, args); client = redis.createClient.apply(null, args)
client.on('ready', done); client.on('ready', done)
}); })
it('allows auth and noReadyCheck to be provided as config option for client', function (done) { it('allows auth and noReadyCheck to be provided as config option for client', function (done) {
if (helper.redisProcess().spawnFailed()) this.skip(); if (helper.redisProcess().spawnFailed()) this.skip()
var args = config.configureClient(ip, { var args = config.configureClient(ip, {
password: auth, password: auth,
noReadyCheck: true noReadyCheck: true
}); })
client = redis.createClient.apply(null, args); client = redis.createClient.apply(null, args)
client.on('ready', done); client.on('ready', done)
}); })
it('allows auth to be provided post-hoc with auth method', function (done) { it('allows auth to be provided post-hoc with auth method', function (done) {
if (helper.redisProcess().spawnFailed()) this.skip(); if (helper.redisProcess().spawnFailed()) this.skip()
var args = config.configureClient(ip); var args = config.configureClient(ip)
client = redis.createClient.apply(null, args); client = redis.createClient.apply(null, args)
client.auth(auth); client.auth(auth)
client.on('ready', done); client.on('ready', done)
}); })
it('reconnects with appropriate authentication while offline commands are present', function (done) { it('reconnects with appropriate authentication while offline commands are present', function (done) {
if (helper.redisProcess().spawnFailed()) this.skip(); if (helper.redisProcess().spawnFailed()) this.skip()
client = redis.createClient.apply(null, args); client = redis.createClient.apply(null, args)
client.auth(auth); client.auth(auth)
client.on('ready', function () { client.on('ready', function () {
if (this.timesConnected < 3) { if (this.timesConnected < 3) {
var interval = setInterval(function () { var interval = setInterval(function () {
if (client.commandQueue.length !== 0) { if (client.commandQueue.length !== 0) {
return; return
} }
clearInterval(interval); clearInterval(interval)
interval = null; interval = null
client.stream.destroy(); client.stream.destroy()
client.set('foo', 'bar'); client.set('foo', 'bar')
client.get('foo'); // Errors would bubble client.get('foo') // Errors would bubble
assert.strictEqual(client.offlineQueue.length, 2); assert.strictEqual(client.offlineQueue.length, 2)
}, 1); }, 1)
} else { } else {
done(); done()
} }
}); })
client.on('reconnecting', function (params) { client.on('reconnecting', function (params) {
assert.strictEqual(params.error, null); assert.strictEqual(params.error, null)
}); })
}); })
it('should return an error if the password is not correct and a callback has been provided', function (done) { 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(); if (helper.redisProcess().spawnFailed()) this.skip()
client = redis.createClient.apply(null, args); client = redis.createClient.apply(null, args)
var async = true; var async = true
client.auth(undefined, function (err, res) { client.auth(undefined, function (err, res) {
assert.strictEqual(err.message, 'ERR invalid password'); assert.strictEqual(err.message, 'ERR invalid password')
assert.strictEqual(err.command, 'AUTH'); assert.strictEqual(err.command, 'AUTH')
assert.strictEqual(res, undefined); assert.strictEqual(res, undefined)
async = false; async = false
done(); done()
}); })
assert(async); assert(async)
}); })
it('should emit an error if the password is not correct and no callback has been provided', function (done) { 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(); if (helper.redisProcess().spawnFailed()) this.skip()
client = redis.createClient.apply(null, args); client = redis.createClient.apply(null, args)
client.on('error', function (err) { client.on('error', function (err) {
assert.strictEqual(err.message, 'ERR invalid password'); assert.strictEqual(err.message, 'ERR invalid password')
assert.strictEqual(err.command, 'AUTH'); assert.strictEqual(err.command, 'AUTH')
done(); done()
}); })
client.auth(234567); client.auth(234567)
}); })
it('allows auth to be provided post-hoc with auth method again', function (done) { it('allows auth to be provided post-hoc with auth method again', function (done) {
if (helper.redisProcess().spawnFailed()) this.skip(); if (helper.redisProcess().spawnFailed()) this.skip()
var args = config.configureClient(ip, { var args = config.configureClient(ip, {
authPass: auth authPass: auth
}); })
client = redis.createClient.apply(null, args); client = redis.createClient.apply(null, args)
client.on('ready', function () { client.on('ready', function () {
client.auth(auth, helper.isString('OK', done)); client.auth(auth, helper.isString('OK', done))
}); })
}); })
it('does not allow any commands to be processed if not authenticated using noReadyCheck true', function (done) { it('does not allow any commands to be processed if not authenticated using noReadyCheck true', function (done) {
if (helper.redisProcess().spawnFailed()) this.skip(); if (helper.redisProcess().spawnFailed()) this.skip()
var args = config.configureClient(ip, { var args = config.configureClient(ip, {
noReadyCheck: true noReadyCheck: true
}); })
client = redis.createClient.apply(null, args); client = redis.createClient.apply(null, args)
client.on('ready', function () { client.on('ready', function () {
client.set('foo', 'bar', function (err, res) { client.set('foo', 'bar', function (err) {
assert.equal(err.message, 'NOAUTH Authentication required.'); assert.strictEqual(err.message, 'NOAUTH Authentication required.')
assert.equal(err.code, 'NOAUTH'); assert.strictEqual(err.code, 'NOAUTH')
assert.equal(err.command, 'SET'); assert.strictEqual(err.command, 'SET')
done(); done()
}); })
}); })
}); })
it('does not allow auth to be provided post-hoc with auth method if not authenticated before', function (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(); if (helper.redisProcess().spawnFailed()) this.skip()
client = redis.createClient.apply(null, args); client = redis.createClient.apply(null, args)
client.on('error', function (err) { client.on('error', function (err) {
assert.equal(err.code, 'NOAUTH'); assert.strictEqual(err.code, 'NOAUTH')
assert.equal(err.message, 'Ready check failed: NOAUTH Authentication required.'); assert.strictEqual(err.message, 'Ready check failed: NOAUTH Authentication required.')
assert.equal(err.command, 'INFO'); assert.strictEqual(err.command, 'INFO')
done(); done()
}); })
}); })
it('should emit an error if the provided password is faulty', function (done) { it('should emit an error if the provided password is faulty', function (done) {
if (helper.redisProcess().spawnFailed()) this.skip(); if (helper.redisProcess().spawnFailed()) this.skip()
client = redis.createClient({ client = redis.createClient({
password: 'wrongPassword' password: 'wrongPassword'
}); })
client.once('error', function (err) { client.once('error', function (err) {
assert.strictEqual(err.message, 'ERR invalid password'); assert.strictEqual(err.message, 'ERR invalid password')
done(); done()
}); })
}); })
it('pubsub working with auth', function (done) { it('pubsub working with auth', function (done) {
if (helper.redisProcess().spawnFailed()) this.skip(); if (helper.redisProcess().spawnFailed()) this.skip()
var args = config.configureClient(ip, { var args = config.configureClient(ip, {
password: auth password: auth
}); })
client = redis.createClient.apply(null, args); client = redis.createClient.apply(null, args)
client.set('foo', 'bar'); client.set('foo', 'bar')
client.subscribe('somechannel', 'another channel', function (err, res) { client.subscribe('somechannel', 'another channel', function (err) {
assert.strictEqual(err, null)
client.once('ready', function () { client.once('ready', function () {
assert.strictEqual(client.pubSubMode, 1); assert.strictEqual(client.pubSubMode, 1)
client.get('foo', function (err, res) { client.get('foo', function (err) {
assert(/ERR only \(P\)SUBSCRIBE \/ \(P\)UNSUBSCRIBE/.test(err.message)); assert(/ERR only \(P\)SUBSCRIBE \/ \(P\)UNSUBSCRIBE/.test(err.message))
done(); done()
}); })
}); })
}); })
client.once('ready', function () { client.once('ready', function () {
// Coherent behavior with all other offline commands fires commands before emitting but does not wait till they return // Coherent behavior with all other offline commands fires commands before emitting but does not wait till they return
assert.strictEqual(client.pubSubMode, 2); assert.strictEqual(client.pubSubMode, 2)
client.ping(function () { // Make sure all commands were properly processed already client.ping(function () { // Make sure all commands were properly processed already
client.stream.destroy(); client.stream.destroy()
}); })
}); })
}); })
it('individual commands work properly with batch', function (done) { it('individual commands work properly with batch', function (done) {
// quit => might return an error instead of "OK" in the exec callback... (if not connected) // quit => might return an error instead of "OK" in the exec callback... (if not connected)
@@ -300,48 +300,52 @@ describe('client authentication', function () {
var args = config.configureClient('localhost', { var args = config.configureClient('localhost', {
noReadyCheck: true noReadyCheck: true
}); })
client = redis.createClient.apply(null, args); client = redis.createClient.apply(null, args)
assert.strictEqual(client.selectedDb, undefined); assert.strictEqual(client.selectedDb, undefined)
var end = helper.callFuncAfter(done, 8); var end = helper.callFuncAfter(done, 8)
client.on('monitor', function () { client.on('monitor', function () {
end(); // Should be called for each command after monitor end() // Should be called for each command after monitor
}); })
client.batch() client.batch()
.auth(auth) .auth(auth)
.select(5, function (err, res) { .select(5, function (err, res) {
assert.strictEqual(client.selectedDb, 5); assert.strictEqual(err, null)
assert.strictEqual(res, 'OK'); assert.strictEqual(client.selectedDb, 5)
assert.notDeepEqual(client.serverInfo.db5, { avgTtl: 0, expires: 0, keys: 1 }); assert.strictEqual(res, 'OK')
assert.notDeepEqual(client.serverInfo.db5, { avgTtl: 0, expires: 0, keys: 1 })
}) })
.monitor() .monitor()
.set('foo', 'bar', helper.isString('OK')) .set('foo', 'bar', helper.isString('OK'))
.info('stats', function (err, res) { .info('stats', function (err, res) {
assert.strictEqual(res.indexOf('# Stats\r\n'), 0); assert.strictEqual(err, null)
assert.strictEqual(client.serverInfo.sync_full, '0'); assert.strictEqual(res.indexOf('# Stats\r\n'), 0)
assert.strictEqual(client.serverInfo.sync_full, '0')
}) })
.get('foo', helper.isString('bar')) .get('foo', helper.isString('bar'))
.subscribe(['foo', 'bar', 'foo'], helper.isUnSubscribe(2, ['foo', 'bar', 'foo'])) .subscribe(['foo', 'bar', 'foo'], helper.isDeepEqual([2, ['foo', 'bar', 'foo']]))
.unsubscribe('foo') .unsubscribe('foo')
.subscribe('/foo', helper.isUnSubscribe(2, '/foo')) .subscribe('/foo', helper.isDeepEqual([2, ['/foo']]))
.psubscribe('*') .psubscribe('*')
.quit(helper.isString('OK')) .quit(helper.isString('OK'))
.exec(function (err, res) { .exec(function (err, res) {
res[4] = res[4].substr(0, 9); assert.strictEqual(err, null)
res[4] = res[4].substr(0, 9)
assert.deepStrictEqual( assert.deepStrictEqual(
res, res,
['OK', 'OK', 'OK', 'OK', '# Stats\r\n', 'bar', [2, ['foo', 'bar', 'foo']], [1, ['foo']], [2, ['/foo']], [3, ['*']], 'OK'] ['OK', 'OK', 'OK', 'OK', '# Stats\r\n', 'bar', [2, ['foo', 'bar', 'foo']], [1, ['foo']], [2, ['/foo']], [3, ['*']], 'OK']
); )
end(); end()
}); })
}); })
}); })
}); })
after(function (done) { after(function (done) {
if (helper.redisProcess().spawnFailed()) return done(); if (helper.redisProcess().spawnFailed()) return done()
helper.stopRedis(function () { helper.stopRedis(function () {
helper.startRedis('./conf/redis.conf', done); helper.startRedis('./conf/redis.conf', done)
}); })
}); })
}); })
}

View File

@@ -1,185 +1,184 @@
'use strict'; 'use strict'
var assert = require('assert'); var assert = require('assert')
var config = require('./lib/config'); var config = require('./lib/config')
var helper = require('./helper'); var helper = require('./helper')
var redis = config.redis; var redis = config.redis
describe("The 'batch' method", function () {
describe('The \'batch\' method', function () {
helper.allTests(function (ip, args) { helper.allTests(function (ip, args) {
describe('using ' + ip, function () { describe('using ' + ip, function () {
describe('when not connected', function () { describe('when not connected', function () {
var client; var client
beforeEach(function (done) { beforeEach(function (done) {
client = redis.createClient.apply(null, args); client = redis.createClient.apply(null, args)
client.once('connect', function () { client.once('connect', function () {
client.quit(); client.quit()
}); })
client.on('end', done); client.on('end', done)
}); })
it('returns an empty array for missing commands', function (done) { it('returns an empty array for missing commands', function (done) {
var batch = client.batch(); var batch = client.batch()
batch.exec(function (err, res) { batch.exec(function (err, res) {
assert.strictEqual(err, null); assert.strictEqual(err, null)
assert.strictEqual(res.length, 0); assert.strictEqual(res.length, 0)
done(); done()
}); })
}); })
it('returns an error for batch with commands', function (done) { it('returns an error for batch with commands', function (done) {
var batch = client.batch(); var batch = client.batch()
batch.set('foo', 'bar'); batch.set('foo', 'bar')
batch.exec(function (err, res) { batch.exec(function (err, res) {
assert.strictEqual(err, null); assert.strictEqual(err, null)
assert.strictEqual(res[0].code, 'NR_CLOSED'); assert.strictEqual(res[0].code, 'NR_CLOSED')
done(); done()
}); })
}); })
it('returns an empty array for missing commands if promisified', function () { it('returns an empty array for missing commands if promisified', function () {
return client.batch().execAsync().then(function (res) { return client.batch().execAsync().then(function (res) {
assert.strictEqual(res.length, 0); assert.strictEqual(res.length, 0)
}); })
}); })
}); })
describe('when connected', function () { describe('when connected', function () {
var client; var client
beforeEach(function (done) { beforeEach(function (done) {
client = redis.createClient.apply(null, args); client = redis.createClient.apply(null, args)
client.once('ready', function () { client.once('ready', function () {
client.flushdb(function (err) { client.flushdb(function (err) {
return done(err); return done(err)
}); })
}); })
}); })
afterEach(function () { afterEach(function () {
client.end(true); client.end(true)
}); })
it('returns an empty array and keep the execution order in tact', function (done) { it('returns an empty array and keep the execution order in tact', function (done) {
var called = false; var called = false
client.set('foo', 'bar', function (err, res) { client.set('foo', 'bar', function () {
called = true; called = true
}); })
var batch = client.batch(); var batch = client.batch()
batch.exec(function (err, res) { batch.exec(function (err, res) {
assert.strictEqual(err, null); assert.strictEqual(err, null)
assert.strictEqual(res.length, 0); assert.strictEqual(res.length, 0)
assert(called); assert(called)
done(); done()
}); })
}); })
it('runs normal calls in-between batch', function (done) { it('runs normal calls in-between batch', function (done) {
var batch = client.batch(); var batch = client.batch()
batch.set('m1', '123'); batch.set('m1', '123')
client.set('m2', '456', done); client.set('m2', '456', done)
}); })
it('returns an empty array if promisified', function () { it('returns an empty array if promisified', function () {
return client.batch().execAsync().then(function (res) { return client.batch().execAsync().then(function (res) {
assert.strictEqual(res.length, 0); assert.strictEqual(res.length, 0)
}); })
}); })
it('returns an empty result array', function (done) { it('returns an empty result array', function (done) {
var batch = client.batch(); var batch = client.batch()
var async = true; var async = true
var notBuffering = batch.exec(function (err, res) { var notBuffering = batch.exec(function (err, res) {
assert.strictEqual(err, null); assert.strictEqual(err, null)
assert.strictEqual(res.length, 0); assert.strictEqual(res.length, 0)
async = false; async = false
done(); done()
}); })
assert(async); assert(async)
assert.strictEqual(notBuffering, true); assert.strictEqual(notBuffering, true)
}); })
it('fail individually when one command fails using chaining notation', function (done) { it('fail individually when one command fails using chaining notation', function (done) {
var batch1, batch2; var batch1, batch2
batch1 = client.batch(); batch1 = client.batch()
batch1.mset('batchfoo', '10', 'batchbar', '20', helper.isString('OK')); batch1.mset('batchfoo', '10', 'batchbar', '20', helper.isString('OK'))
// Provoke an error at queue time // Provoke an error at queue time
batch1.set('foo2', helper.isError()); batch1.set('foo2', helper.isError())
batch1.incr('batchfoo'); batch1.incr('batchfoo')
batch1.incr('batchbar'); batch1.incr('batchbar')
batch1.exec(function () { batch1.exec(function () {
// Confirm that the previous command, while containing an error, still worked. // Confirm that the previous command, while containing an error, still worked.
batch2 = client.batch(); batch2 = client.batch()
batch2.get('foo2', helper.isNull()); batch2.get('foo2', helper.isNull())
batch2.incr('batchbar', helper.isNumber(22)); batch2.incr('batchbar', helper.isNumber(22))
batch2.incr('batchfoo', helper.isNumber(12)); batch2.incr('batchfoo', helper.isNumber(12))
batch2.exec(function (err, replies) { batch2.exec(function (err, replies) {
assert.strictEqual(null, replies[0]); assert.strictEqual(err, null)
assert.strictEqual(22, replies[1]); assert.strictEqual(null, replies[0])
assert.strictEqual(12, replies[2]); assert.strictEqual(22, replies[1])
return done(); assert.strictEqual(12, replies[2])
}); return done()
}); })
}); })
})
it('fail individually when one command fails and emit the error if no callback has been provided', function (done) { it('fail individually when one command fails and emit the error if no callback has been provided', function (done) {
var batch1; var batch1
client.on('error', function (err) { client.on('error', function (err) {
done(err); done(err)
}); })
batch1 = client.batch(); batch1 = client.batch()
batch1.mset('batchfoo', '10', 'batchbar', '20', helper.isString('OK')); batch1.mset('batchfoo', '10', 'batchbar', '20', helper.isString('OK'))
// Provoke an error at queue time // Provoke an error at queue time
batch1.set('foo2'); batch1.set('foo2')
batch1.incr('batchfoo'); batch1.incr('batchfoo')
batch1.incr('batchbar'); batch1.incr('batchbar')
batch1.exec(function (err, res) { batch1.exec(function (err, res) {
assert.strictEqual(res[1].command, 'SET'); // TODO: This should actually return an error!
assert.strictEqual(res[1].code, 'ERR'); assert.strictEqual(err, null)
done(); assert.strictEqual(res[1].command, 'SET')
}); assert.strictEqual(res[1].code, 'ERR')
}); done()
})
})
it('fail individually when one command in an array of commands fails', function (done) { it('fail individually when one command in an array of commands fails', function (done) {
// test nested batch-bulk replies // test nested batch-bulk replies
client.batch([ client.batch([
['mget', 'batchfoo', 'batchbar', function (err, res) { ['mget', 'batchfoo', 'batchbar', helper.isDeepEqual([null, null])],
assert.strictEqual(2, res.length);
assert.strictEqual(0, +res[0]);
assert.strictEqual(0, +res[1]);
}],
['set', 'foo2', helper.isError()], ['set', 'foo2', helper.isError()],
['incr', 'batchfoo'], ['incr', 'batchfoo'],
['incr', 'batchbar'] ['incr', 'batchbar']
]).exec(function (err, replies) { ]).exec(function (err, replies) {
assert.strictEqual(2, replies[0].length); // TODO: This should actually return an error!
assert.strictEqual(null, replies[0][0]); assert.strictEqual(err, null)
assert.strictEqual(null, replies[0][1]); assert.strictEqual(2, replies[0].length)
assert.strictEqual('SET', replies[1].command); assert.strictEqual(null, replies[0][0])
assert.strictEqual('1', replies[2].toString()); assert.strictEqual(null, replies[0][1])
assert.strictEqual('1', replies[3].toString()); assert.strictEqual('SET', replies[1].command)
return done(); assert.strictEqual('1', replies[2].toString())
}); assert.strictEqual('1', replies[3].toString())
}); return done()
})
})
it('handles multiple operations being applied to a set', function (done) { it('handles multiple operations being applied to a set', function (done) {
client.sadd('some set', 'mem 1'); client.sadd('some set', 'mem 1')
client.sadd(['some set', 'mem 2']); client.sadd(['some set', 'mem 2'])
client.sadd('some set', 'mem 3'); client.sadd('some set', 'mem 3')
client.sadd('some set', 'mem 4'); client.sadd('some set', 'mem 4')
// make sure empty mb reply works // make sure empty mb reply works
client.del('some missing set'); client.del('some missing set')
client.smembers('some missing set', function (err, reply) { client.smembers('some missing set', function (err, reply) {
assert.strictEqual(err, null)
// make sure empty mb reply works // make sure empty mb reply works
assert.strictEqual(0, reply.length); assert.strictEqual(0, reply.length)
}); })
// test nested batch-bulk replies with empty mb elements. // test nested batch-bulk replies with empty mb elements.
client.batch([ client.batch([
@@ -189,18 +188,19 @@ describe("The 'batch' method", function () {
]) ])
.scard('some set') .scard('some set')
.exec(function (err, replies) { .exec(function (err, replies) {
assert.strictEqual(4, replies[0].length); assert.strictEqual(err, null)
assert.strictEqual(0, replies[2].length); assert.strictEqual(4, replies[0].length)
return done(); assert.strictEqual(0, replies[2].length)
}); return done()
}); })
})
it('allows multiple operations to be performed using constructor with all kinds of syntax', function (done) { it('allows multiple operations to be performed using constructor with all kinds of syntax', function (done) {
var now = Date.now(); var now = Date.now()
var arr = ['batchhmset', 'batchbar', 'batchbaz']; var arr = ['batchhmset', 'batchbar', 'batchbaz']
var arr2 = ['some manner of key', 'otherTypes']; var arr2 = ['some manner of key', 'otherTypes']
var arr3 = [5768, 'batchbarx', 'batchfoox']; var arr3 = [5768, 'batchbarx', 'batchfoox']
var arr4 = ['mset', [578, 'batchbar'], helper.isString('OK')]; var arr4 = ['mset', [578, 'batchbar'], helper.isString('OK')]
client.batch([ client.batch([
arr4, arr4,
[['mset', 'batchfoo2', 'batchbar2', 'batchfoo3', 'batchbar3'], helper.isString('OK')], [['mset', 'batchfoo2', 'batchbar2', 'batchfoo3', 'batchbar3'], helper.isString('OK')],
@@ -211,54 +211,55 @@ describe("The 'batch' method", function () {
['hmset', now, {123456789: 'abcdefghij', 'some manner of key': 'a type of value', 'otherTypes': 555}], ['hmset', now, {123456789: 'abcdefghij', 'some manner of key': 'a type of value', 'otherTypes': 555}],
['hmset', 'key2', {'0123456789': 'abcdefghij', 'some manner of key': 'a type of value', 'otherTypes': 999}, helper.isString('OK')], ['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']],
['hmset', 'batchhmset', ['batchbar', 'batchbaz'], helper.isString('OK')], ['hmset', 'batchhmset', ['batchbar', 'batchbaz'], helper.isString('OK')]
]) ])
.hmget(now, 123456789, 'otherTypes') .hmget(now, 123456789, 'otherTypes')
.hmget('key2', arr2, function noop () {}) .hmget('key2', arr2, function noop () {})
.hmget(['batchhmset2', 'some manner of key', 'batchbar3']) .hmget(['batchhmset2', 'some manner of key', 'batchbar3'])
.mget('batchfoo2', ['batchfoo3', 'batchfoo'], function (err, res) { .mget('batchfoo2', ['batchfoo3', 'batchfoo'], function (err, res) {
assert.strictEqual(res[0], 'batchbar2'); assert.strictEqual(err, null)
assert.strictEqual(res[1], 'batchbar3'); assert.strictEqual(res[0], 'batchbar2')
assert.strictEqual(res[2], null); assert.strictEqual(res[1], 'batchbar3')
assert.strictEqual(res[2], null)
}) })
.exec(function (err, replies) { .exec(function (err, replies) {
assert.equal(arr.length, 3); assert.strictEqual(arr.length, 3)
assert.equal(arr2.length, 2); assert.strictEqual(arr2.length, 2)
assert.equal(arr3.length, 3); assert.strictEqual(arr3.length, 3)
assert.equal(arr4.length, 3); assert.strictEqual(arr4.length, 3)
assert.strictEqual(null, err); assert.strictEqual(null, err)
assert.equal(replies[10][1], '555'); assert.strictEqual(replies[10][1], '555')
assert.equal(replies[11][0], 'a type of value'); assert.strictEqual(replies[11][0], 'a type of value')
assert.strictEqual(replies[12][0], null); assert.strictEqual(replies[12][0], null)
assert.equal(replies[12][1], 'test'); assert.strictEqual(replies[12][1], 'test')
assert.equal(replies[13][0], 'batchbar2'); assert.strictEqual(replies[13][0], 'batchbar2')
assert.equal(replies[13].length, 3); assert.strictEqual(replies[13].length, 3)
assert.equal(replies.length, 14); assert.strictEqual(replies.length, 14)
return done(); return done()
}); })
}); })
it('converts a non string key to a string', function (done) { it('converts a non string key to a string', function (done) {
// TODO: Converting the key might change soon again. // TODO: Converting the key might change soon again.
client.batch().hmset(true, { client.batch().hmset(true, {
test: 123, test: 123,
bar: 'baz' bar: 'baz'
}).exec(done); }).exec(done)
}); })
it('runs a batch without any further commands', function (done) { it('runs a batch without any further commands', function (done) {
var buffering = client.batch().exec(function (err, res) { var buffering = client.batch().exec(function (err, res) {
assert.strictEqual(err, null); assert.strictEqual(err, null)
assert.strictEqual(res.length, 0); assert.strictEqual(res.length, 0)
done(); done()
}); })
assert(typeof buffering === 'boolean'); assert(typeof buffering === 'boolean')
}); })
it('runs a batch without any further commands and without callback', function () { it('runs a batch without any further commands and without callback', function () {
var buffering = client.batch().exec(); var buffering = client.batch().exec()
assert.strictEqual(buffering, true); assert.strictEqual(buffering, true)
}); })
it('allows multiple operations to be performed using a chaining API', function (done) { it('allows multiple operations to be performed using a chaining API', function (done) {
client.batch() client.batch()
@@ -266,16 +267,8 @@ describe("The 'batch' method", function () {
.incr('some') .incr('some')
.incr('keys') .incr('keys')
.mget('some', 'keys') .mget('some', 'keys')
.exec(function (err, replies) { .exec(helper.isDeepEqual(['OK', 11, 21, ['11', '21']], done))
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) { it('allows multiple commands to work the same as normal to be performed using a chaining API', function (done) {
client.batch() client.batch()
@@ -283,16 +276,8 @@ describe("The 'batch' method", function () {
.incr('some', helper.isNumber(11)) .incr('some', helper.isNumber(11))
.incr(['keys'], helper.isNumber(21)) .incr(['keys'], helper.isNumber(21))
.mget('some', 'keys') .mget('some', 'keys')
.exec(function (err, replies) { .exec(helper.isDeepEqual(['OK', 11, 21, ['11', '21']], done))
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 () { it('allows multiple commands to work the same as normal to be performed using a chaining API promisified', function () {
return client.batch() return client.batch()
@@ -301,14 +286,10 @@ describe("The 'batch' method", function () {
.incr(['keys'], helper.isNumber(21)) .incr(['keys'], helper.isNumber(21))
.mget('some', 'keys') .mget('some', 'keys')
.execAsync() .execAsync()
.then(function (replies) { .then(function (res) {
assert.equal('OK', replies[0]); helper.isDeepEqual(['OK', 11, 21, ['11', '21']])(null, res)
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) { it('allows an array to be provided indicating multiple operations to perform', function (done) {
// test nested batch-bulk replies with nulls. // test nested batch-bulk replies with nulls.
@@ -317,11 +298,12 @@ describe("The 'batch' method", function () {
['incr', 'batchfoo'] ['incr', 'batchfoo']
]) ])
.exec(function (err, replies) { .exec(function (err, replies) {
assert.strictEqual(replies.length, 2); assert.strictEqual(err, null)
assert.strictEqual(replies[0].length, 4); assert.strictEqual(replies.length, 2)
return done(); assert.strictEqual(replies[0].length, 4)
}); return done()
}); })
})
it('allows multiple operations to be performed on a hash', function (done) { it('allows multiple operations to be performed on a hash', function (done) {
client.batch() client.batch()
@@ -331,20 +313,19 @@ describe("The 'batch' method", function () {
things: 'here' things: 'here'
}) })
.hgetall('batchhash') .hgetall('batchhash')
.exec(done); .exec(done)
}); })
it('should work without any callback or arguments', function (done) { it('should work without any callback or arguments', function (done) {
var batch = client.batch(); var batch = client.batch()
batch.set('baz', 'binary'); batch.set('baz', 'binary')
batch.set('foo', 'bar'); batch.set('foo', 'bar')
batch.ping(); batch.ping()
batch.exec(); batch.exec()
client.get('foo', helper.isString('bar', done)); client.get('foo', helper.isString('bar', done))
}); })
})
}); })
}); })
}); })
});

View File

@@ -1,77 +1,75 @@
'use strict'; 'use strict'
var assert = require('assert'); var assert = require('assert')
var config = require('../lib/config'); var config = require('../lib/config')
var helper = require('../helper'); var helper = require('../helper')
var redis = config.redis; var redis = config.redis
var intercept = require('intercept-stdout'); var intercept = require('intercept-stdout')
describe("The 'blpop' method", function () {
describe('The \'blpop\' method', function () {
helper.allTests(function (ip, args) { helper.allTests(function (ip, args) {
describe('using ' + ip, function () { describe('using ' + ip, function () {
var client; var client
var bclient; var bclient
beforeEach(function (done) { beforeEach(function (done) {
client = redis.createClient.apply(null, args); client = redis.createClient.apply(null, args)
client.once('ready', function () { client.once('ready', function () {
client.flushdb(done); client.flushdb(done)
}); })
}); })
it('pops value immediately if list contains values', function (done) { it('pops value immediately if list contains values', function (done) {
bclient = redis.createClient.apply(null, args); bclient = redis.createClient.apply(null, args)
redis.debugMode = true; redis.debugMode = true
var text = ''; var text = ''
var unhookIntercept = intercept(function (data) { var unhookIntercept = intercept(function (data) {
text += data; text += data
return ''; return ''
}); })
client.rpush('blocking list', 'initial value', helper.isNumber(1)); client.rpush('blocking list', 'initial value', helper.isNumber(1))
unhookIntercept(); unhookIntercept()
assert(/^Send 127\.0\.0\.1:6379 id [0-9]+: \*3\r\n\$5\r\nrpush\r\n\$13\r\nblocking list\r\n\$13\r\ninitial value\r\n\n$/.test(text)); assert(/^Send 127\.0\.0\.1:6379 id [0-9]+: \*3\r\n\$5\r\nrpush\r\n\$13\r\nblocking list\r\n\$13\r\ninitial value\r\n\n$/.test(text))
redis.debugMode = false; redis.debugMode = false
bclient.blpop('blocking list', 0, function (err, value) { bclient.blpop('blocking list', 0, function (err, value) {
assert.strictEqual(value[0], 'blocking list'); assert.strictEqual(value[0], 'blocking list')
assert.strictEqual(value[1], 'initial value'); assert.strictEqual(value[1], 'initial value')
return done(err); return done(err)
}); })
}); })
it('pops value immediately if list contains values using array notation', function (done) { it('pops value immediately if list contains values using array notation', function (done) {
bclient = redis.createClient.apply(null, args); bclient = redis.createClient.apply(null, args)
client.rpush(['blocking list', 'initial value'], helper.isNumber(1)); client.rpush(['blocking list', 'initial value'], helper.isNumber(1))
bclient.blpop(['blocking list', 0], function (err, value) { bclient.blpop(['blocking list', 0], function (err, value) {
assert.strictEqual(value[0], 'blocking list'); assert.strictEqual(value[0], 'blocking list')
assert.strictEqual(value[1], 'initial value'); assert.strictEqual(value[1], 'initial value')
return done(err); return done(err)
}); })
}); })
it('waits for value if list is not yet populated', function (done) { it('waits for value if list is not yet populated', function (done) {
bclient = redis.createClient.apply(null, args); bclient = redis.createClient.apply(null, args)
bclient.blpop('blocking list 2', 5, function (err, value) { bclient.blpop('blocking list 2', 5, function (err, value) {
assert.strictEqual(value[0], 'blocking list 2'); assert.strictEqual(value[0], 'blocking list 2')
assert.strictEqual(value[1], 'initial value'); assert.strictEqual(value[1], 'initial value')
return done(err); return done(err)
}); })
client.rpush('blocking list 2', 'initial value', helper.isNumber(1)); client.rpush('blocking list 2', 'initial value', helper.isNumber(1))
}); })
it('times out after specified time', function (done) { it('times out after specified time', function (done) {
bclient = redis.createClient.apply(null, args); bclient = redis.createClient.apply(null, args)
bclient.blpop('blocking list', 1, function (err, res) { bclient.blpop('blocking list', 1, function (err, res) {
assert.strictEqual(res, null); assert.strictEqual(res, null)
return done(err); return done(err)
}); })
}); })
afterEach(function () { afterEach(function () {
client.end(true); client.end(true)
bclient.end(true); bclient.end(true)
}); })
}); })
}); })
}); })

View File

@@ -1,155 +1,148 @@
'use strict'; 'use strict'
var assert = require('assert'); var Buffer = require('safe-buffer').Buffer
var config = require('../lib/config'); var assert = require('assert')
var helper = require('../helper'); var config = require('../lib/config')
var redis = config.redis; var helper = require('../helper')
var redis = config.redis
describe("The 'client' method", function () {
describe('The \'client\' method', function () {
helper.allTests(function (ip, args) { helper.allTests(function (ip, args) {
var pattern = /addr=/; var pattern = /addr=/
describe('using ' + ip, function () { describe('using ' + ip, function () {
var client; var client
beforeEach(function (done) { beforeEach(function (done) {
client = redis.createClient.apply(null, args); client = redis.createClient.apply(null, args)
client.once('ready', function () { client.once('ready', function () {
client.flushdb(done); client.flushdb(done)
}); })
}); })
afterEach(function () { afterEach(function () {
client.end(true); client.end(true)
}); })
describe('list', function () { describe('list', function () {
it('lists connected clients', function (done) { it('lists connected clients', function (done) {
client.client('LIST', helper.match(pattern, done)); client.client('LIST', helper.match(pattern, done))
}); })
it("lists connected clients when invoked with multi's chaining syntax", function (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)); 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) { it('lists connected clients when invoked with array syntax on client', function (done) {
client.multi().client(['list']).exec(helper.match(pattern, done)); client.multi().client(['list']).exec(helper.match(pattern, done))
}); })
it("lists connected clients when invoked with multi's array syntax", function (done) { it('lists connected clients when invoked with multi\'s array syntax', function (done) {
client.multi([ client.multi([
['client', 'list'] ['client', 'list']
]).exec(helper.match(pattern, done)); ]).exec(helper.match(pattern, done))
}); })
}); })
describe('reply', function () { describe('reply', function () {
describe('as normal command', function () { describe('as normal command', function () {
it('on', function (done) { it('on', function (done) {
helper.serverVersionAtLeast.call(this, client, [3, 2, 0]); helper.serverVersionAtLeast.call(this, client, [3, 2, 0])
assert.strictEqual(client.reply, 'ON'); assert.strictEqual(client.reply, 'ON')
client.client('reply', 'on', helper.isString('OK')); client.client('reply', 'on', helper.isString('OK'))
assert.strictEqual(client.reply, 'ON'); assert.strictEqual(client.reply, 'ON')
client.set('foo', 'bar', done); client.set('foo', 'bar', done)
}); })
it('off', function (done) { it('off', function (done) {
helper.serverVersionAtLeast.call(this, client, [3, 2, 0]); helper.serverVersionAtLeast.call(this, client, [3, 2, 0])
assert.strictEqual(client.reply, 'ON'); assert.strictEqual(client.reply, 'ON')
client.client(new Buffer('REPLY'), 'OFF', helper.isUndefined()); client.client(Buffer.from('REPLY'), 'OFF', helper.isUndefined())
assert.strictEqual(client.reply, 'OFF'); assert.strictEqual(client.reply, 'OFF')
client.set('foo', 'bar', helper.isUndefined(done)); client.set('foo', 'bar', helper.isUndefined(done))
}); })
it('skip', function (done) { it('skip', function (done) {
helper.serverVersionAtLeast.call(this, client, [3, 2, 0]); helper.serverVersionAtLeast.call(this, client, [3, 2, 0])
assert.strictEqual(client.reply, 'ON'); assert.strictEqual(client.reply, 'ON')
client.client('REPLY', new Buffer('SKIP'), helper.isUndefined()); client.client('REPLY', Buffer.from('SKIP'), helper.isUndefined())
assert.strictEqual(client.reply, 'SKIP_ONE_MORE'); assert.strictEqual(client.reply, 'SKIP_ONE_MORE')
client.set('foo', 'bar', helper.isUndefined()); client.set('foo', 'bar', helper.isUndefined())
client.get('foo', helper.isString('bar', done)); client.get('foo', helper.isString('bar', done))
}); })
}); })
describe('in a batch context', function () { describe('in a batch context', function () {
it('on', function (done) { it('on', function (done) {
helper.serverVersionAtLeast.call(this, client, [3, 2, 0]); helper.serverVersionAtLeast.call(this, client, [3, 2, 0])
var batch = client.batch(); var batch = client.batch()
assert.strictEqual(client.reply, 'ON'); assert.strictEqual(client.reply, 'ON')
batch.client('reply', 'on', helper.isString('OK')); batch.client('reply', 'on', helper.isString('OK'))
assert.strictEqual(client.reply, 'ON'); assert.strictEqual(client.reply, 'ON')
batch.set('foo', 'bar'); batch.set('foo', 'bar')
batch.exec(function (err, res) { batch.exec(function (err, res) {
assert.deepEqual(res, ['OK', 'OK']); assert.deepEqual(res, ['OK', 'OK'])
done(err); done(err)
}); })
}); })
it('off', function (done) { it('off', function (done) {
helper.serverVersionAtLeast.call(this, client, [3, 2, 0]); helper.serverVersionAtLeast.call(this, client, [3, 2, 0])
var batch = client.batch(); var batch = client.batch()
assert.strictEqual(client.reply, 'ON'); assert.strictEqual(client.reply, 'ON')
batch.set('hello', 'world'); batch.set('hello', 'world')
batch.client(new Buffer('REPLY'), new Buffer('OFF'), helper.isUndefined()); batch.client(Buffer.from('REPLY'), Buffer.from('OFF'), helper.isUndefined())
batch.set('foo', 'bar', helper.isUndefined()); batch.set('foo', 'bar', helper.isUndefined())
batch.exec(function (err, res) { batch.exec(function (err, res) {
assert.strictEqual(client.reply, 'OFF'); assert.strictEqual(client.reply, 'OFF')
assert.deepEqual(res, ['OK', undefined, undefined]); assert.deepEqual(res, ['OK', undefined, undefined])
done(err); done(err)
}); })
}); })
it('skip', function (done) { it('skip', function (done) {
helper.serverVersionAtLeast.call(this, client, [3, 2, 0]); helper.serverVersionAtLeast.call(this, client, [3, 2, 0])
assert.strictEqual(client.reply, 'ON'); assert.strictEqual(client.reply, 'ON')
client.batch() client.batch()
.set('hello', 'world') .set('hello', 'world')
.client('REPLY', 'SKIP', helper.isUndefined()) .client('REPLY', 'SKIP', helper.isUndefined())
.set('foo', 'bar', helper.isUndefined()) .set('foo', 'bar', helper.isUndefined())
.get('foo') .get('foo')
.exec(function (err, res) { .exec(function (err, res) {
assert.strictEqual(client.reply, 'ON'); assert.strictEqual(client.reply, 'ON')
assert.deepEqual(res, ['OK', undefined, undefined, 'bar']); assert.deepEqual(res, ['OK', undefined, undefined, 'bar'])
done(err); done(err)
}); })
}); })
}); })
}); })
describe('setname / getname', function () { describe('setname / getname', function () {
var client2; var client2
beforeEach(function (done) { beforeEach(function (done) {
client2 = redis.createClient.apply(null, args); client2 = redis.createClient.apply(null, args)
client2.once('ready', function () { client2.once('ready', function () {
done(); done()
}); })
}); })
afterEach(function () { afterEach(function () {
client2.end(true); client2.end(true)
}); })
it('sets the name', function (done) { it('sets the name', function (done) {
// The querys are auto pipelined and the response is a response to all querys of one client // The querys are auto pipelined and the response is a response to all querys of one client
// per chunk. So the execution order is only garanteed on each client // per chunk. So the execution order is only guaranteed on each client
var end = helper.callFuncAfter(done, 2); var end = helper.callFuncAfter(done, 2)
client.client('setname', 'RUTH'); client.client('setname', 'RUTH')
client2.client('setname', ['RENEE'], helper.isString('OK')); client2.client('setname', ['RENEE'], helper.isString('OK'))
client2.client(['setname', 'MARTIN'], helper.isString('OK')); client2.client(['setname', 'MARTIN'], helper.isString('OK'))
client2.client('getname', function (err, res) { client2.client('getname', helper.isString('MARTIN', end))
assert.equal(res, 'MARTIN'); client.client('getname', helper.isString('RUTH', end))
end(); })
}); })
client.client('getname', function (err, res) { })
assert.equal(res, 'RUTH'); })
end(); })
});
});
});
});
});
});

View File

@@ -1,95 +1,93 @@
'use strict'; 'use strict'
var assert = require('assert'); var assert = require('assert')
var config = require('../lib/config'); var config = require('../lib/config')
var helper = require('../helper'); var helper = require('../helper')
var redis = config.redis; var redis = config.redis
var uuid = require('uuid'); var uuid = require('uuid')
describe("The 'dbsize' method", function () {
describe('The \'dbsize\' method', function () {
helper.allTests(function (ip, args) { helper.allTests(function (ip, args) {
describe('using ' + ip, function () { describe('using ' + ip, function () {
var key, value; var key, value
beforeEach(function () { beforeEach(function () {
key = uuid.v4(); key = uuid.v4()
value = uuid.v4(); value = uuid.v4()
}); })
describe('when not connected', function () { describe('when not connected', function () {
var client; var client
beforeEach(function (done) { beforeEach(function (done) {
client = redis.createClient.apply(null, args); client = redis.createClient.apply(null, args)
client.once('ready', function () { client.once('ready', function () {
client.quit(); client.quit()
}); })
client.on('end', done); client.on('end', done)
}); })
it('reports an error', function (done) { it('reports an error', function (done) {
client.dbsize([], function (err, res) { client.dbsize([], function (err, res) {
assert(err.message.match(/The connection is already closed/)); assert(err.message.match(/The connection is already closed/))
done(); done()
}); })
}); })
}); })
describe('when connected', function () { describe('when connected', function () {
var client; var client
beforeEach(function (done) { beforeEach(function (done) {
client = redis.createClient.apply(null, args); client = redis.createClient.apply(null, args)
client.once('ready', function () { client.once('ready', function () {
client.flushdb(function (err, res) { client.flushdb(function (err, res) {
helper.isString('OK')(err, res); helper.isString('OK')(err, res)
done(); done()
}); })
}); })
}); })
afterEach(function () { afterEach(function () {
client.end(true); client.end(true)
}); })
it('returns a zero db size', function (done) { it('returns a zero db size', function (done) {
client.dbsize([], function (err, res) { client.dbsize([], function (err, res) {
helper.isNotError()(err, res); helper.isNotError()(err, res)
helper.isType.number()(err, res); helper.isType.number()(err, res)
assert.strictEqual(res, 0, 'Initial db size should be 0'); assert.strictEqual(res, 0, 'Initial db size should be 0')
done(); done()
}); })
}); })
describe('when more data is added to Redis', function () { describe('when more data is added to Redis', function () {
var oldSize; var oldSize
beforeEach(function (done) { beforeEach(function (done) {
client.dbsize(function (err, res) { client.dbsize(function (err, res) {
helper.isType.number()(err, res); helper.isType.number()(err, res)
assert.strictEqual(res, 0, 'Initial db size should be 0'); assert.strictEqual(res, 0, 'Initial db size should be 0')
oldSize = res; oldSize = res
client.set(key, value, function (err, res) { client.set(key, value, function (err, res) {
helper.isNotError()(err, res); helper.isNotError()(err, res)
done(); done()
}); })
}); })
}); })
it('returns a larger db size', function (done) { it('returns a larger db size', function (done) {
client.dbsize([], function (err, res) { client.dbsize([], function (err, res) {
helper.isNotError()(err, res); helper.isNotError()(err, res)
helper.isType.positiveNumber()(err, res); helper.isType.positiveNumber()(err, res)
assert.strictEqual(true, (oldSize < res), 'Adding data should increase db size.'); assert.strictEqual(true, (oldSize < res), 'Adding data should increase db size.')
done(); done()
}); })
}); })
}); })
}); })
}); })
}); })
}); })

View File

@@ -1,57 +1,55 @@
'use strict'; 'use strict'
var config = require('../lib/config'); var config = require('../lib/config')
var helper = require('../helper'); var helper = require('../helper')
var redis = config.redis; var redis = config.redis
describe("The 'del' method", function () {
describe('The \'del\' method', function () {
helper.allTests(function (ip, args) { helper.allTests(function (ip, args) {
describe('using ' + ip, function () { describe('using ' + ip, function () {
var client; var client
beforeEach(function (done) { beforeEach(function (done) {
client = redis.createClient.apply(null, args); client = redis.createClient.apply(null, args)
client.once('ready', function () { client.once('ready', function () {
client.flushdb(done); client.flushdb(done)
}); })
}); })
it('allows a single key to be deleted', function (done) { it('allows a single key to be deleted', function (done) {
client.set('foo', 'bar'); client.set('foo', 'bar')
client.del('foo', helper.isNumber(1)); client.del('foo', helper.isNumber(1))
client.get('foo', helper.isNull(done)); client.get('foo', helper.isNull(done))
}); })
it('allows del to be called on a key that does not exist', function (done) { it('allows del to be called on a key that does not exist', function (done) {
client.del('foo', helper.isNumber(0, done)); client.del('foo', helper.isNumber(0, done))
}); })
it('allows multiple keys to be deleted', function (done) { it('allows multiple keys to be deleted', function (done) {
client.mset('foo', 'bar', 'apple', 'banana'); client.mset('foo', 'bar', 'apple', 'banana')
client.del('foo', 'apple', helper.isNumber(2)); client.del('foo', 'apple', helper.isNumber(2))
client.get('foo', helper.isNull()); client.get('foo', helper.isNull())
client.get('apple', helper.isNull(done)); client.get('apple', helper.isNull(done))
}); })
it('allows multiple keys to be deleted with the array syntax', function (done) { it('allows multiple keys to be deleted with the array syntax', function (done) {
client.mset('foo', 'bar', 'apple', 'banana'); client.mset('foo', 'bar', 'apple', 'banana')
client.del(['foo', 'apple'], helper.isNumber(2)); client.del(['foo', 'apple'], helper.isNumber(2))
client.get('foo', helper.isNull()); client.get('foo', helper.isNull())
client.get('apple', helper.isNull(done)); client.get('apple', helper.isNull(done))
}); })
it('allows multiple keys to be deleted with the array syntax and no callback', function (done) { it('allows multiple keys to be deleted with the array syntax and no callback', function (done) {
client.mset('foo', 'bar', 'apple', 'banana'); client.mset('foo', 'bar', 'apple', 'banana')
client.del(['foo', 'apple']); client.del(['foo', 'apple'])
client.get('foo', helper.isNull()); client.get('foo', helper.isNull())
client.get('apple', helper.isNull(done)); client.get('apple', helper.isNull(done))
}); })
afterEach(function () { afterEach(function () {
client.end(true); client.end(true)
}); })
}); })
}); })
}); })

View File

@@ -1,156 +1,142 @@
'use strict'; 'use strict'
var assert = require('assert'); var assert = require('assert')
var config = require('../lib/config'); var config = require('../lib/config')
var crypto = require('crypto'); var crypto = require('crypto')
var helper = require('../helper'); var helper = require('../helper')
var redis = config.redis; var redis = config.redis
describe("The 'eval' method", function () {
describe('The \'eval\' method', function () {
helper.allTests(function (ip, args) { helper.allTests(function (ip, args) {
describe('using ' + ip, function () { describe('using ' + ip, function () {
var client; var client
var source = "return redis.call('set', 'sha', 'test')"; var source = 'return redis.call(\'set\', \'sha\', \'test\')'
beforeEach(function (done) { beforeEach(function (done) {
client = redis.createClient.apply(null, args); client = redis.createClient.apply(null, args)
client.once('ready', function () { client.once('ready', function () {
client.flushdb(done); client.flushdb(done)
}); })
}); })
afterEach(function () { afterEach(function () {
client.end(true); client.end(true)
}); })
it('converts a float to an integer when evaluated', function (done) { it('converts a float to an integer when evaluated', function (done) {
client.eval('return 100.5', 0, helper.isNumber(100, done)); client.eval('return 100.5', 0, helper.isNumber(100, done))
}); })
it('returns a string', function (done) { it('returns a string', function (done) {
client.eval("return 'hello world'", 0, helper.isString('hello world', done)); client.eval('return \'hello world\'', 0, helper.isString('hello world', done))
}); })
it('converts boolean true to integer 1', function (done) { it('converts boolean true to integer 1', function (done) {
client.eval('return true', 0, helper.isNumber(1, done)); client.eval('return true', 0, helper.isNumber(1, done))
}); })
it('converts boolean false to null', function (done) { it('converts boolean false to null', function (done) {
client.eval('return false', 0, helper.isNull(done)); client.eval('return false', 0, helper.isNull(done))
}); })
it('converts lua status code to string representation', function (done) { it('converts lua status code to string representation', function (done) {
client.eval("return {ok='fine'}", 0, helper.isString('fine', done)); client.eval('return {ok=\'fine\'}', 0, helper.isString('fine', done))
}); })
it('converts lua error to an error response', function (done) { it('converts lua error to an error response', function (done) {
client.eval("return {err='this is an error'}", 0, function (err) { client.eval('return {err=\'this is an error\'}', 0, function (err) {
assert(err.code === undefined); assert(err.code === undefined)
helper.isError()(err); helper.isError()(err)
done(); done()
}); })
}); })
it('represents a lua table appropritely', function (done) { it('represents a lua table appropritely', function (done) {
client.eval("return {1,2,3,'ciao',{1,2}}", 0, function (err, res) { client.eval('return {1,2,3,\'ciao\',{1,2}}', 0, function (err, res) {
assert.strictEqual(5, res.length); assert.strictEqual(err, null)
assert.strictEqual(1, res[0]); assert.strictEqual(5, res.length)
assert.strictEqual(2, res[1]); assert.strictEqual(1, res[0])
assert.strictEqual(3, res[2]); assert.strictEqual(2, res[1])
assert.strictEqual('ciao', res[3]); assert.strictEqual(3, res[2])
assert.strictEqual(2, res[4].length); assert.strictEqual('ciao', res[3])
assert.strictEqual(1, res[4][0]); assert.strictEqual(2, res[4].length)
assert.strictEqual(2, res[4][1]); assert.strictEqual(1, res[4][0])
return done(); assert.strictEqual(2, res[4][1])
}); return done()
}); })
})
it('populates keys and argv correctly', function (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) { client.eval('return {KEYS[1],KEYS[2],ARGV[1],ARGV[2]}', 2, 'a', 'b', 'c', 'd', helper.isDeepEqual(['a', 'b', 'c', 'd'], done))
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 arguments to be provided in array rather than as multiple parameters', function (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) { client.eval(['return {KEYS[1],KEYS[2],ARGV[1],ARGV[2]}', 2, 'a', 'b', 'c', 'd'], helper.isDeepEqual(['a', 'b', 'c', 'd'], done))
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) { it('allows a script to be executed that accesses the redis API without callback', function (done) {
client.eval(source, 0); client.eval(source, 0)
client.get('sha', helper.isString('test', done)); client.get('sha', helper.isString('test', done))
}); })
describe('evalsha', function () { describe('evalsha', function () {
var sha = crypto.createHash('sha1').update(source).digest('hex'); var sha = crypto.createHash('sha1').update(source).digest('hex')
it('allows a script to be executed that accesses the redis API', function (done) { it('allows a script to be executed that accesses the redis API', function (done) {
client.eval(source, 0, helper.isString('OK')); client.eval(source, 0, helper.isString('OK'))
client.get('sha', helper.isString('test', done)); client.get('sha', helper.isString('test', done))
}); })
it('can execute a script if the SHA exists', function (done) { it('can execute a script if the SHA exists', function (done) {
client.evalsha(sha, 0, helper.isString('OK')); client.evalsha(sha, 0, helper.isString('OK'))
client.get('sha', helper.isString('test', done)); client.get('sha', helper.isString('test', done))
}); })
it('returns an error if SHA does not exist', function (done) { it('returns an error if SHA does not exist', function (done) {
client.evalsha('ffffffffffffffffffffffffffffffffffffffff', 0, helper.isError(done)); client.evalsha('ffffffffffffffffffffffffffffffffffffffff', 0, helper.isError(done))
}); })
it('emit an error if SHA does not exist without any callback', function (done) { it('emit an error if SHA does not exist without any callback', function (done) {
client.evalsha('ffffffffffffffffffffffffffffffffffffffff', 0); client.evalsha('ffffffffffffffffffffffffffffffffffffffff', 0)
client.on('error', function (err) { client.on('error', function (err) {
assert.equal(err.code, 'NOSCRIPT'); assert.strictEqual(err.code, 'NOSCRIPT')
assert(/NOSCRIPT No matching script. Please use EVAL./.test(err.message)); assert(/NOSCRIPT No matching script. Please use EVAL./.test(err.message))
done(); done()
}); })
}); })
it('emits an error if SHA does not exist and no callback has been provided', function (done) { it('emits an error if SHA does not exist and no callback has been provided', function (done) {
client.on('error', function (err) { client.on('error', function (err) {
assert.equal(err.message, 'NOSCRIPT No matching script. Please use EVAL.'); assert.strictEqual(err.message, 'NOSCRIPT No matching script. Please use EVAL.')
done(); done()
}); })
client.evalsha('ffffffffffffffffffffffffffffffffffffffff', 0); client.evalsha('ffffffffffffffffffffffffffffffffffffffff', 0)
}); })
}); })
it('allows a key to be incremented, and performs appropriate conversion from LUA type', function (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) { client.set('incr key', 0, function (err, reply) {
if (err) return done(err); if (err) return done(err)
client.eval("local foo = redis.call('incr','incr key')\nreturn {type(foo),foo}", 0, function (err, res) { client.eval('local foo = redis.call(\'incr\',\'incr key\')\nreturn {type(foo),foo}', 0, function (err, res) {
assert.strictEqual(2, res.length); assert.strictEqual(2, res.length)
assert.strictEqual('number', res[0]); assert.strictEqual('number', res[0])
assert.strictEqual(1, res[1]); assert.strictEqual(1, res[1])
return done(err); return done(err)
}); })
}); })
}); })
it('allows a bulk operation to be performed, and performs appropriate conversion from LUA type', function (done) { 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.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(err, null)
assert.strictEqual(2, res.length); client.eval('local foo = redis.call(\'get\',\'bulk reply key\'); return {type(foo),foo}', 0, function (err, res) {
assert.strictEqual('string', res[0]); assert.strictEqual(2, res.length)
assert.strictEqual('bulk reply value', res[1]); assert.strictEqual('string', res[0])
return done(err); assert.strictEqual('bulk reply value', res[1])
}); return done(err)
}); })
}); })
})
it('allows a multi mulk operation to be performed, with the appropriate type conversion', function (done) { it('allows a multi mulk operation to be performed, with the appropriate type conversion', function (done) {
client.multi() client.multi()
@@ -159,52 +145,52 @@ describe("The 'eval' method", function () {
.rpush('mylist', 'b') .rpush('mylist', 'b')
.rpush('mylist', 'c') .rpush('mylist', 'c')
.exec(function (err, replies) { .exec(function (err, replies) {
if (err) return done(err); 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) { 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(5, res.length)
assert.strictEqual('table', res[0]); assert.strictEqual('table', res[0])
assert.strictEqual('a', res[1]); assert.strictEqual('a', res[1])
assert.strictEqual('b', res[2]); assert.strictEqual('b', res[2])
assert.strictEqual('c', res[3]); assert.strictEqual('c', res[3])
assert.strictEqual(3, res[4]); assert.strictEqual(3, res[4])
return done(err); return done(err)
}); })
}); })
}); })
it('returns an appropriate representation of Lua status reply', function (done) { 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) { 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(2, res.length)
assert.strictEqual('table', res[0]); assert.strictEqual('table', res[0])
assert.strictEqual('OK', res[1]); assert.strictEqual('OK', res[1])
return done(err); return done(err)
}); })
}); })
it('returns an appropriate representation of a Lua error reply', function (done) { it('returns an appropriate representation of a Lua error reply', function (done) {
client.set('error reply key', 'error reply value', function (err, res) { client.set('error reply key', 'error reply value', function (err, res) {
if (err) return done(err); if (err) return done(err)
client.eval("local foo = redis.pcall('incr','error reply key'); return {type(foo),foo['err']}", 0, function (err, res) { 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(2, res.length)
assert.strictEqual('table', res[0]); assert.strictEqual('table', res[0])
assert.strictEqual('ERR value is not an integer or out of range', res[1]); assert.strictEqual('ERR value is not an integer or out of range', res[1])
return done(err); return done(err)
}); })
}); })
}); })
it('returns an appropriate representation of a Lua nil reply', function (done) { it('returns an appropriate representation of a Lua nil reply', function (done) {
client.del('nil reply key', function (err, res) { client.del('nil reply key', function (err, res) {
if (err) return done(err); if (err) return done(err)
client.eval("local foo = redis.call('get','nil reply key'); return {type(foo),foo == false}", 0, function (err, res) { client.eval('local foo = redis.call(\'get\',\'nil reply key\'); return {type(foo),foo == false}', 0, function (err, res) {
if (err) throw err; if (err) throw err
assert.strictEqual(2, res.length); assert.strictEqual(2, res.length)
assert.strictEqual('boolean', res[0]); assert.strictEqual('boolean', res[0])
assert.strictEqual(1, res[1]); assert.strictEqual(1, res[1])
return done(err); return done(err)
}); })
}); })
}); })
}); })
}); })
}); })

View File

@@ -1,40 +1,38 @@
'use strict'; 'use strict'
var config = require('../lib/config'); var config = require('../lib/config')
var helper = require('../helper'); var helper = require('../helper')
var redis = config.redis; var redis = config.redis
describe("The 'exists' method", function () {
describe('The \'exists\' method', function () {
helper.allTests(function (ip, args) { helper.allTests(function (ip, args) {
describe('using ' + ip, function () { describe('using ' + ip, function () {
var client; var client
beforeEach(function (done) { beforeEach(function (done) {
client = redis.createClient.apply(null, args); client = redis.createClient.apply(null, args)
client.once('ready', function () { client.once('ready', function () {
client.flushdb(done); client.flushdb(done)
}); })
}); })
it('returns 1 if the key exists', function (done) { it('returns 1 if the key exists', function (done) {
client.set('foo', 'bar'); client.set('foo', 'bar')
client.exists('foo', helper.isNumber(1, done)); client.exists('foo', helper.isNumber(1, done))
}); })
it('returns 1 if the key exists with array syntax', function (done) { it('returns 1 if the key exists with array syntax', function (done) {
client.set('foo', 'bar'); client.set('foo', 'bar')
client.exists(['foo'], helper.isNumber(1, done)); client.exists(['foo'], helper.isNumber(1, done))
}); })
it('returns 0 if the key does not exist', function (done) { it('returns 0 if the key does not exist', function (done) {
client.exists('bar', helper.isNumber(0, done)); client.exists('bar', helper.isNumber(0, done))
}); })
afterEach(function () { afterEach(function () {
client.end(true); client.end(true)
}); })
}); })
}); })
}); })

View File

@@ -1,42 +1,40 @@
'use strict'; 'use strict'
var config = require('../lib/config'); var config = require('../lib/config')
var helper = require('../helper'); var helper = require('../helper')
var redis = config.redis; var redis = config.redis
describe("The 'expire' method", function () {
describe('The \'expire\' method', function () {
helper.allTests(function (ip, args) { helper.allTests(function (ip, args) {
describe('using ' + ip, function () { describe('using ' + ip, function () {
var client; var client
beforeEach(function (done) { beforeEach(function (done) {
client = redis.createClient.apply(null, args); client = redis.createClient.apply(null, args)
client.once('ready', function () { client.once('ready', function () {
client.flushdb(done); client.flushdb(done)
}); })
}); })
it('expires key after timeout', function (done) { it('expires key after timeout', function (done) {
client.set(['expiry key', 'bar'], helper.isString('OK')); client.set(['expiry key', 'bar'], helper.isString('OK'))
client.expire('expiry key', '1', helper.isNumber(1)); client.expire('expiry key', '1', helper.isNumber(1))
setTimeout(function () { setTimeout(function () {
client.exists(['expiry key'], helper.isNumber(0, done)); client.exists(['expiry key'], helper.isNumber(0, done))
}, 1050); }, 1050)
}); })
it('expires key after timeout with array syntax', function (done) { it('expires key after timeout with array syntax', function (done) {
client.set(['expiry key', 'bar'], helper.isString('OK')); client.set(['expiry key', 'bar'], helper.isString('OK'))
client.expire(['expiry key', '1'], helper.isNumber(1)); client.expire(['expiry key', '1'], helper.isNumber(1))
setTimeout(function () { setTimeout(function () {
client.exists(['expiry key'], helper.isNumber(0, done)); client.exists(['expiry key'], helper.isNumber(0, done))
}, 1050); }, 1050)
}); })
afterEach(function () { afterEach(function () {
client.end(true); client.end(true)
}); })
}); })
}); })
}); })

View File

@@ -1,105 +1,94 @@
'use strict'; 'use strict'
var assert = require('assert'); var assert = require('assert')
var config = require('../lib/config'); var config = require('../lib/config')
var helper = require('../helper'); var helper = require('../helper')
var redis = config.redis; var redis = config.redis
var uuid = require('uuid'); var uuid = require('uuid')
describe("The 'flushdb' method", function () {
describe('The \'flushdb\' method', function () {
helper.allTests(function (ip, args) { helper.allTests(function (ip, args) {
describe('using ' + ip, function () { describe('using ' + ip, function () {
var key, key2; var key, key2
beforeEach(function () { beforeEach(function () {
key = uuid.v4(); key = uuid.v4()
key2 = uuid.v4(); key2 = uuid.v4()
}); })
describe('when not connected', function () { describe('when not connected', function () {
var client; var client
beforeEach(function (done) { beforeEach(function (done) {
client = redis.createClient.apply(null, args); client = redis.createClient.apply(null, args)
client.once('ready', function () { client.once('ready', function () {
client.quit(); client.quit()
}); })
client.on('end', done); client.on('end', done)
}); })
it('reports an error', function (done) { it('reports an error', function (done) {
client.flushdb(function (err, res) { client.flushdb(function (err, res) {
assert(err.message.match(/The connection is already closed/)); assert(err.message.match(/The connection is already closed/))
done(); done()
}); })
}); })
}); })
describe('when connected', function () { describe('when connected', function () {
var client; var client
beforeEach(function (done) { beforeEach(function (done) {
client = redis.createClient.apply(null, args); client = redis.createClient.apply(null, args)
client.once('ready', function () { client.once('ready', function () {
done(); done()
}); })
}); })
afterEach(function () { afterEach(function () {
client.end(true); client.end(true)
}); })
describe('when there is data in Redis', function () { describe('when there is data in Redis', function () {
beforeEach(function (done) { beforeEach(function (done) {
client.mset(key, uuid.v4(), key2, uuid.v4(), helper.isNotError()); client.mset(key, uuid.v4(), key2, uuid.v4(), helper.isNotError())
client.dbsize([], function (err, res) { client.dbsize([], function (err, res) {
helper.isType.positiveNumber()(err, res); helper.isType.positiveNumber()(err, res)
assert.equal(res, 2, 'Two keys should have been inserted'); assert.strictEqual(res, 2, 'Two keys should have been inserted')
done(); done()
}); })
}); })
it('deletes all the keys', function (done) { it('deletes all the keys', function (done) {
client.flushdb(function (err, res) { client.flushdb(function (err, res) {
assert.equal(res, 'OK'); assert.strictEqual(err, null)
assert.strictEqual(res, 'OK')
client.mget(key, key2, function (err, res) { client.mget(key, key2, function (err, res) {
assert.strictEqual(null, err, 'Unexpected error returned'); assert.strictEqual(null, err, 'Unexpected error returned')
assert.strictEqual(true, Array.isArray(res), 'Results object should be an array.'); 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(2, res.length, 'Results array should have length 2.')
assert.strictEqual(null, res[0], 'Redis key should have been flushed.'); assert.strictEqual(null, res[0], 'Redis key should have been flushed.')
assert.strictEqual(null, res[1], 'Redis key should have been flushed.'); assert.strictEqual(null, res[1], 'Redis key should have been flushed.')
done(err); done(err)
}); })
}); })
}); })
it('results in a db size of zero', function (done) { it('results in a db size of zero', function (done) {
client.flushdb(function (err, res) { client.flushdb(function (err, res) {
client.dbsize([], function (err, res) { assert.strictEqual(err, null)
helper.isNotError()(err, res); client.dbsize([], helper.isNumber(0, done))
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) { it('results in a db size of zero without a callback', function (done) {
client.flushdb(); client.flushdb()
setTimeout(function (err, res) { setTimeout(function () {
client.dbsize(function (err, res) { client.dbsize(helper.isNumber(0, done))
helper.isNotError()(err, res); }, 25)
helper.isType.number()(err, res); })
assert.strictEqual(0, res, 'Flushing db should result in db size 0'); })
done(); })
}); })
}, 25); })
}); })
});
});
});
});
});

View File

@@ -1,35 +1,33 @@
'use strict'; 'use strict'
var config = require('../lib/config'); var config = require('../lib/config')
var helper = require('../helper'); var helper = require('../helper')
var redis = config.redis; var redis = config.redis
describe("The 'geoadd' method", function () {
describe('The \'geoadd\' method', function () {
helper.allTests(function (ip, args) { helper.allTests(function (ip, args) {
describe('using ' + ip, function () { describe('using ' + ip, function () {
var client; var client
beforeEach(function (done) { beforeEach(function (done) {
client = redis.createClient.apply(null, args); client = redis.createClient.apply(null, args)
client.once('ready', function () { client.once('ready', function () {
client.flushdb(done); client.flushdb(done)
}); })
}); })
it('returns 1 if the key exists', function (done) { it('returns 1 if the key exists', function (done) {
helper.serverVersionAtLeast.call(this, client, [3, 2, 0]); helper.serverVersionAtLeast.call(this, client, [3, 2, 0])
client.geoadd('mycity:21:0:location', '13.361389', '38.115556', 'COR', function (err, res) { client.geoadd('mycity:21:0:location', '13.361389', '38.115556', 'COR', function (err, res) {
console.log(err, res); console.log(err, res)
// geoadd is still in the unstable branch. As soon as it reaches the stable one, activate this test // geoadd is still in the unstable branch. As soon as it reaches the stable one, activate this test
done(); done()
}); })
}); })
afterEach(function () { afterEach(function () {
client.end(true); client.end(true)
}); })
}); })
}); })
}); })

View File

@@ -1,95 +1,93 @@
'use strict'; 'use strict'
var assert = require('assert'); var assert = require('assert')
var config = require('../lib/config'); var config = require('../lib/config')
var helper = require('../helper'); var helper = require('../helper')
var redis = config.redis; var redis = config.redis
var uuid = require('uuid'); var uuid = require('uuid')
describe("The 'get' method", function () {
describe('The \'get\' method', function () {
helper.allTests(function (ip, args) { helper.allTests(function (ip, args) {
describe('using ' + ip, function () { describe('using ' + ip, function () {
var key, value; var key, value
beforeEach(function () { beforeEach(function () {
key = uuid.v4(); key = uuid.v4()
value = uuid.v4(); value = uuid.v4()
}); })
describe('when not connected', function () { describe('when not connected', function () {
var client; var client
beforeEach(function (done) { beforeEach(function (done) {
client = redis.createClient.apply(null, args); client = redis.createClient.apply(null, args)
client.once('ready', function () { client.once('ready', function () {
client.quit(); client.quit()
}); })
client.on('end', done); client.on('end', done)
}); })
it('reports an error', function (done) { it('reports an error', function (done) {
client.get(key, function (err, res) { client.get(key, function (err, res) {
assert(err.message.match(/The connection is already closed/)); assert(err.message.match(/The connection is already closed/))
done(); done()
}); })
}); })
it('reports an error promisified', function () { it('reports an error promisified', function () {
return client.getAsync(key).then(assert, function (err) { return client.getAsync(key).then(assert, function (err) {
assert(err.message.match(/The connection is already closed/)); assert(err.message.match(/The connection is already closed/))
}); })
}); })
}); })
describe('when connected', function () { describe('when connected', function () {
var client; var client
beforeEach(function (done) { beforeEach(function (done) {
client = redis.createClient.apply(null, args); client = redis.createClient.apply(null, args)
client.once('ready', function () { client.once('ready', function () {
done(); done()
}); })
}); })
afterEach(function () { afterEach(function () {
client.end(true); client.end(true)
}); })
describe('when the key exists in Redis', function () { describe('when the key exists in Redis', function () {
beforeEach(function (done) { beforeEach(function (done) {
client.set(key, value, function (err, res) { client.set(key, value, function (err, res) {
helper.isNotError()(err, res); helper.isNotError()(err, res)
done(); done()
}); })
}); })
it('gets the value correctly', function (done) { it('gets the value correctly', function (done) {
client.get(key, function (err, res) { client.get(key, function (err, res) {
helper.isString(value)(err, res); helper.isString(value)(err, res)
done(err); done(err)
}); })
}); })
it("should not throw on a get without callback (even if it's not useful)", function (done) { it('should not throw on a get without callback (even if it\'s not useful)', function (done) {
client.get(key); client.get(key)
client.on('error', function (err) { client.on('error', function (err) {
throw err; throw err
}); })
setTimeout(done, 25); setTimeout(done, 25)
}); })
}); })
describe('when the key does not exist in Redis', function () { describe('when the key does not exist in Redis', function () {
it('gets a null value', function (done) { it('gets a null value', function (done) {
client.get(key, function (err, res) { client.get(key, function (err, res) {
helper.isNull()(err, res); helper.isNull()(err, res)
done(err); done(err)
}); })
}); })
}); })
}); })
}); })
}); })
}); })

View File

@@ -1,105 +1,103 @@
'use strict'; 'use strict'
var assert = require('assert'); var assert = require('assert')
var config = require('../lib/config'); var config = require('../lib/config')
var helper = require('../helper'); var helper = require('../helper')
var redis = config.redis; var redis = config.redis
var uuid = require('uuid'); var uuid = require('uuid')
describe("The 'getset' method", function () {
describe('The \'getset\' method', function () {
helper.allTests(function (ip, args) { helper.allTests(function (ip, args) {
describe('using ' + ip, function () { describe('using ' + ip, function () {
var key, value, value2; var key, value, value2
beforeEach(function () { beforeEach(function () {
key = uuid.v4(); key = uuid.v4()
value = uuid.v4(); value = uuid.v4()
value2 = uuid.v4(); value2 = uuid.v4()
}); })
describe('when not connected', function () { describe('when not connected', function () {
var client; var client
beforeEach(function (done) { beforeEach(function (done) {
client = redis.createClient.apply(null, args); client = redis.createClient.apply(null, args)
client.once('ready', function () { client.once('ready', function () {
client.quit(); client.quit()
}); })
client.on('end', done); client.on('end', done)
}); })
it('reports an error', function (done) { it('reports an error', function (done) {
client.get(key, function (err, res) { client.get(key, function (err, res) {
assert(err.message.match(/The connection is already closed/)); assert(err.message.match(/The connection is already closed/))
done(); done()
}); })
}); })
}); })
describe('when connected', function () { describe('when connected', function () {
var client; var client
beforeEach(function (done) { beforeEach(function (done) {
client = redis.createClient.apply(null, args); client = redis.createClient.apply(null, args)
client.once('ready', function () { client.once('ready', function () {
done(); done()
}); })
}); })
afterEach(function () { afterEach(function () {
client.end(true); client.end(true)
}); })
describe('when the key exists in Redis', function () { describe('when the key exists in Redis', function () {
beforeEach(function (done) { beforeEach(function (done) {
client.set(key, value, function (err, res) { client.set(key, value, function (err, res) {
helper.isNotError()(err, res); helper.isNotError()(err, res)
done(); done()
}); })
}); })
it('gets the value correctly', function (done) { it('gets the value correctly', function (done) {
client.getset(key, value2, function (err, res) { client.getset(key, value2, function (err, res) {
helper.isString(value)(err, res); helper.isString(value)(err, res)
client.get(key, function (err, res) { client.get(key, function (err, res) {
helper.isString(value2)(err, res); helper.isString(value2)(err, res)
done(err); done(err)
}); })
}); })
}); })
it('gets the value correctly with array syntax', function (done) { it('gets the value correctly with array syntax', function (done) {
client.getset([key, value2], function (err, res) { client.getset([key, value2], function (err, res) {
helper.isString(value)(err, res); helper.isString(value)(err, res)
client.get(key, function (err, res) { client.get(key, function (err, res) {
helper.isString(value2)(err, res); helper.isString(value2)(err, res)
done(err); done(err)
}); })
}); })
}); })
it('gets the value correctly with array syntax style 2', function (done) { it('gets the value correctly with array syntax style 2', function (done) {
client.getset(key, [value2], function (err, res) { client.getset(key, [value2], function (err, res) {
helper.isString(value)(err, res); helper.isString(value)(err, res)
client.get(key, function (err, res) { client.get(key, function (err, res) {
helper.isString(value2)(err, res); helper.isString(value2)(err, res)
done(err); done(err)
}); })
}); })
}); })
}); })
describe('when the key does not exist in Redis', function () { describe('when the key does not exist in Redis', function () {
it('gets a null value', function (done) { it('gets a null value', function (done) {
client.getset(key, value, function (err, res) { client.getset(key, value, function (err, res) {
helper.isNull()(err, res); helper.isNull()(err, res)
done(err); done(err)
}); })
}); })
}); })
}); })
}); })
}); })
}); })

View File

@@ -1,84 +1,82 @@
'use strict'; 'use strict'
var assert = require('assert'); var Buffer = require('safe-buffer').Buffer
var config = require('../lib/config'); var assert = require('assert')
var helper = require('../helper'); var config = require('../lib/config')
var redis = config.redis; var helper = require('../helper')
var redis = config.redis
describe("The 'hgetall' method", function () {
describe('The \'hgetall\' method', function () {
helper.allTests(function (ip, args) { helper.allTests(function (ip, args) {
describe('using ' + ip, function () { describe('using ' + ip, function () {
var client; var client
describe('regular client', function () { describe('regular client', function () {
beforeEach(function (done) { beforeEach(function (done) {
client = redis.createClient.apply(null, args); client = redis.createClient.apply(null, args)
client.once('ready', function () { client.once('ready', function () {
client.flushdb(done); client.flushdb(done)
}); })
}); })
it('handles simple keys and values', function (done) { it('handles simple keys and values', function (done) {
client.hmset(['hosts', 'hasOwnProperty', '1', 'another', '23', 'home', '1234'], helper.isString('OK')); client.hmset(['hosts', 'hasOwnProperty', '1', 'another', '23', 'home', '1234'], helper.isString('OK'))
client.hgetall(['hosts'], function (err, obj) { client.hgetall(['hosts'], function (err, obj) {
assert.strictEqual(3, Object.keys(obj).length); assert.strictEqual(3, Object.keys(obj).length)
assert.strictEqual('1', obj.hasOwnProperty.toString()); assert.strictEqual('1', obj.hasOwnProperty.toString())
assert.strictEqual('23', obj.another.toString()); assert.strictEqual('23', obj.another.toString())
assert.strictEqual('1234', obj.home.toString()); assert.strictEqual('1234', obj.home.toString())
done(err); done(err)
}); })
}); })
it('handles fetching keys set using an object', function (done) { it('handles fetching keys set using an object', function (done) {
client.batch().hmset('msgTest', { message: 'hello' }, undefined).exec(); client.batch().hmset('msgTest', { message: 'hello' }, undefined).exec()
client.hgetall('msgTest', function (err, obj) { client.hgetall('msgTest', function (err, obj) {
assert.strictEqual(1, Object.keys(obj).length); assert.strictEqual(1, Object.keys(obj).length)
assert.strictEqual(obj.message, 'hello'); assert.strictEqual(obj.message, 'hello')
done(err); done(err)
}); })
}); })
it('handles fetching a messing key', function (done) { it('handles fetching a messing key', function (done) {
client.hgetall('missing', function (err, obj) { client.hgetall('missing', function (err, obj) {
assert.strictEqual(null, obj); assert.strictEqual(null, obj)
done(err); done(err)
}); })
}); })
}); })
describe('binary client', function () { describe('binary client', function () {
var client; var client
var args = config.configureClient(ip, { var args = config.configureClient(ip, {
returnBuffers: true returnBuffers: true
}); })
beforeEach(function (done) { beforeEach(function (done) {
client = redis.createClient.apply(null, args); client = redis.createClient.apply(null, args)
client.once('ready', function () { client.once('ready', function () {
client.flushdb(done); client.flushdb(done)
}); })
}); })
it('returns binary results', function (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.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) { client.hgetall('bhosts', function (err, obj) {
assert.strictEqual(4, Object.keys(obj).length); assert.strictEqual(4, Object.keys(obj).length)
assert.strictEqual('1', obj.mjr.toString()); assert.strictEqual('1', obj.mjr.toString())
assert.strictEqual('23', obj.another.toString()); assert.strictEqual('23', obj.another.toString())
assert.strictEqual('1234', obj.home.toString()); assert.strictEqual('1234', obj.home.toString())
assert.strictEqual((new Buffer([0xAA, 0xBB, 0x00, 0xF0])).toString('binary'), Object.keys(obj)[3]); assert.strictEqual((Buffer.from([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')); assert.strictEqual((Buffer.from([0xCC, 0xDD, 0x00, 0xF0])).toString('binary'), obj[(Buffer.from([0xAA, 0xBB, 0x00, 0xF0])).toString('binary')].toString('binary'))
return done(err); return done(err)
}); })
}); })
}); })
afterEach(function () { afterEach(function () {
client.end(true); client.end(true)
}); })
}); })
}); })
}); })

View File

@@ -1,40 +1,38 @@
'use strict'; 'use strict'
var config = require('../lib/config'); var config = require('../lib/config')
var helper = require('../helper'); var helper = require('../helper')
var redis = config.redis; var redis = config.redis
describe("The 'hincrby' method", function () {
describe('The \'hincrby\' method', function () {
helper.allTests(function (ip, args) { helper.allTests(function (ip, args) {
describe('using ' + ip, function () { describe('using ' + ip, function () {
var client; var client
var hash = 'test hash'; var hash = 'test hash'
beforeEach(function (done) { beforeEach(function (done) {
client = redis.createClient.apply(null, args); client = redis.createClient.apply(null, args)
client.once('ready', function () { client.once('ready', function () {
client.flushdb(done); client.flushdb(done)
}); })
}); })
it('increments a key that has already been set', function (done) { it('increments a key that has already been set', function (done) {
var field = 'field 1'; var field = 'field 1'
client.hset(hash, field, 33); client.hset(hash, field, 33)
client.hincrby(hash, field, 10, helper.isNumber(43, done)); client.hincrby(hash, field, 10, helper.isNumber(43, done))
}); })
it('increments a key that has not been set', function (done) { it('increments a key that has not been set', function (done) {
var field = 'field 2'; var field = 'field 2'
client.hincrby(hash, field, 10, helper.isNumber(10, done)); client.hincrby(hash, field, 10, helper.isNumber(10, done))
}); })
afterEach(function () { afterEach(function () {
client.end(true); client.end(true)
}); })
}); })
}); })
}); })

View File

@@ -1,38 +1,37 @@
'use strict'; 'use strict'
var config = require('../lib/config'); var Buffer = require('safe-buffer').Buffer
var helper = require('../helper'); var config = require('../lib/config')
var redis = config.redis; var helper = require('../helper')
var redis = config.redis
describe("The 'hlen' method", function () {
describe('The \'hlen\' method', function () {
helper.allTests(function (ip, args) { helper.allTests(function (ip, args) {
describe('using ' + ip, function () { describe('using ' + ip, function () {
var client; var client
beforeEach(function (done) { beforeEach(function (done) {
client = redis.createClient.apply(null, args); client = redis.createClient.apply(null, args)
client.once('ready', function () { client.once('ready', function () {
client.flushdb(done); client.flushdb(done)
}); })
}); })
it('reports the count of keys', function (done) { it('reports the count of keys', function (done) {
var hash = 'test hash'; var hash = 'test hash'
var field1 = new Buffer('0123456789'); var field1 = Buffer.from('0123456789')
var value1 = new Buffer('abcdefghij'); var value1 = Buffer.from('abcdefghij')
var field2 = new Buffer(0); var field2 = Buffer.from('')
var value2 = new Buffer(0); var value2 = Buffer.from('')
client.hset(hash, field1, value1, helper.isNumber(1)); client.hset(hash, field1, value1, helper.isNumber(1))
client.hset(hash, field2, value2, helper.isNumber(1)); client.hset(hash, field2, value2, helper.isNumber(1))
client.hlen(hash, helper.isNumber(2, done)); client.hlen(hash, helper.isNumber(2, done))
}); })
afterEach(function () { afterEach(function () {
client.end(true); client.end(true)
}); })
}); })
}); })
}); })

View File

@@ -1,71 +1,69 @@
'use strict'; 'use strict'
var assert = require('assert'); var assert = require('assert')
var config = require('../lib/config'); var config = require('../lib/config')
var helper = require('../helper'); var helper = require('../helper')
var redis = config.redis; var redis = config.redis
describe("The 'hmget' method", function () {
describe('The \'hmget\' method', function () {
helper.allTests(function (ip, args) { helper.allTests(function (ip, args) {
describe('using ' + ip, function () { describe('using ' + ip, function () {
var client; var client
var hash = 'test hash'; var hash = 'test hash'
beforeEach(function (done) { beforeEach(function (done) {
client = redis.createClient.apply(null, args); client = redis.createClient.apply(null, args)
client.once('error', done); client.once('error', done)
client.once('ready', function () { client.once('ready', function () {
client.flushdb(); client.flushdb()
client.hmset(hash, {'0123456789': 'abcdefghij', 'some manner of key': 'a type of value'}, helper.isString('OK', done)); client.hmset(hash, {'0123456789': 'abcdefghij', 'some manner of key': 'a type of value'}, helper.isString('OK', done))
}); })
}); })
it('allows keys to be specified using multiple arguments', function (done) { it('allows keys to be specified using multiple arguments', function (done) {
client.hmget(hash, '0123456789', 'some manner of key', function (err, reply) { client.hmget(hash, '0123456789', 'some manner of key', function (err, reply) {
assert.strictEqual('abcdefghij', reply[0].toString()); assert.strictEqual('abcdefghij', reply[0].toString())
assert.strictEqual('a type of value', reply[1].toString()); assert.strictEqual('a type of value', reply[1].toString())
return done(err); return done(err)
}); })
}); })
it('allows keys to be specified by passing an array without manipulating the array', function (done) { it('allows keys to be specified by passing an array without manipulating the array', function (done) {
var data = ['0123456789', 'some manner of key']; var data = ['0123456789', 'some manner of key']
client.hmget(hash, data, function (err, reply) { client.hmget(hash, data, function (err, reply) {
assert.strictEqual(data.length, 2); assert.strictEqual(data.length, 2)
assert.strictEqual('abcdefghij', reply[0].toString()); assert.strictEqual('abcdefghij', reply[0].toString())
assert.strictEqual('a type of value', reply[1].toString()); assert.strictEqual('a type of value', reply[1].toString())
return done(err); return done(err)
}); })
}); })
it('allows keys to be specified by passing an array as first argument', function (done) { 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) { client.hmget([hash, '0123456789', 'some manner of key'], function (err, reply) {
assert.strictEqual('abcdefghij', reply[0].toString()); assert.strictEqual('abcdefghij', reply[0].toString())
assert.strictEqual('a type of value', reply[1].toString()); assert.strictEqual('a type of value', reply[1].toString())
return done(err); return done(err)
}); })
}); })
it('allows a single key to be specified in an array', function (done) { it('allows a single key to be specified in an array', function (done) {
client.hmget(hash, ['0123456789'], function (err, reply) { client.hmget(hash, ['0123456789'], function (err, reply) {
assert.strictEqual('abcdefghij', reply[0].toString()); assert.strictEqual('abcdefghij', reply[0].toString())
return done(err); return done(err)
}); })
}); })
it('allows keys to be specified that have not yet been set', function (done) { 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) { client.hmget(hash, 'missing thing', 'another missing thing', function (err, reply) {
assert.strictEqual(null, reply[0]); assert.strictEqual(null, reply[0])
assert.strictEqual(null, reply[1]); assert.strictEqual(null, reply[1])
return done(err); return done(err)
}); })
}); })
afterEach(function () { afterEach(function () {
client.end(true); client.end(true)
}); })
}); })
}); })
}); })

View File

@@ -1,117 +1,115 @@
'use strict'; 'use strict'
var assert = require('assert'); var assert = require('assert')
var config = require('../lib/config'); var config = require('../lib/config')
var helper = require('../helper'); var helper = require('../helper')
var redis = config.redis; var redis = config.redis
describe("The 'hmset' method", function () {
describe('The \'hmset\' method', function () {
helper.allTests(function (ip, args) { helper.allTests(function (ip, args) {
describe('using ' + ip, function () { describe('using ' + ip, function () {
var client; var client
var hash = 'test hash'; var hash = 'test hash'
beforeEach(function (done) { beforeEach(function (done) {
client = redis.createClient.apply(null, args); client = redis.createClient.apply(null, args)
client.once('ready', function () { client.once('ready', function () {
client.flushdb(done); client.flushdb(done)
}); })
}); })
it('handles redis-style syntax', function (done) { 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.hmset(hash, '0123456789', 'abcdefghij', 'some manner of key', 'a type of value', 'otherTypes', 555, helper.isString('OK'))
client.hgetall(hash, function (err, obj) { client.hgetall(hash, function (err, obj) {
assert.equal(obj['0123456789'], 'abcdefghij'); assert.strictEqual(obj['0123456789'], 'abcdefghij')
assert.equal(obj['some manner of key'], 'a type of value'); assert.strictEqual(obj['some manner of key'], 'a type of value')
return done(err); return done(err)
}); })
}); })
it('handles object-style syntax', function (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.hmset(hash, {'0123456789': 'abcdefghij', 'some manner of key': 'a type of value', 'otherTypes': 555}, helper.isString('OK'))
client.hgetall(hash, function (err, obj) { client.hgetall(hash, function (err, obj) {
assert.equal(obj['0123456789'], 'abcdefghij'); assert.strictEqual(obj['0123456789'], 'abcdefghij')
assert.equal(obj['some manner of key'], 'a type of value'); assert.strictEqual(obj['some manner of key'], 'a type of value')
return done(err); return done(err)
}); })
}); })
it('handles object-style syntax and the key being a number', function (done) { 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.hmset(231232, {'0123456789': 'abcdefghij', 'some manner of key': 'a type of value', 'otherTypes': 555}, undefined)
client.hgetall(231232, function (err, obj) { client.hgetall(231232, function (err, obj) {
assert.equal(obj['0123456789'], 'abcdefghij'); assert.strictEqual(obj['0123456789'], 'abcdefghij')
assert.equal(obj['some manner of key'], 'a type of value'); assert.strictEqual(obj['some manner of key'], 'a type of value')
return done(err); return done(err)
}); })
}); })
it('allows a numeric key', function (done) { it('allows a numeric key', function (done) {
client.hmset(hash, 99, 'banana', helper.isString('OK')); client.hmset(hash, 99, 'banana', helper.isString('OK'))
client.hgetall(hash, function (err, obj) { client.hgetall(hash, function (err, obj) {
assert.equal(obj['99'], 'banana'); assert.strictEqual(obj['99'], 'banana')
return done(err); return done(err)
}); })
}); })
it('allows a numeric key without callback', function (done) { it('allows a numeric key without callback', function (done) {
client.hmset(hash, 99, 'banana', 'test', 25); client.hmset(hash, 99, 'banana', 'test', 25)
client.hgetall(hash, function (err, obj) { client.hgetall(hash, function (err, obj) {
assert.equal(obj['99'], 'banana'); assert.strictEqual(obj['99'], 'banana')
assert.equal(obj.test, '25'); assert.strictEqual(obj.test, '25')
return done(err); return done(err)
}); })
}); })
it('allows an array without callback', function (done) { it('allows an array without callback', function (done) {
client.hmset([hash, 99, 'banana', 'test', 25]); client.hmset([hash, 99, 'banana', 'test', 25])
client.hgetall(hash, function (err, obj) { client.hgetall(hash, function (err, obj) {
assert.equal(obj['99'], 'banana'); assert.strictEqual(obj['99'], 'banana')
assert.equal(obj.test, '25'); assert.strictEqual(obj.test, '25')
return done(err); return done(err)
}); })
}); })
it('allows an array and a callback', function (done) { it('allows an array and a callback', function (done) {
client.hmset([hash, 99, 'banana', 'test', 25], helper.isString('OK')); client.hmset([hash, 99, 'banana', 'test', 25], helper.isString('OK'))
client.hgetall(hash, function (err, obj) { client.hgetall(hash, function (err, obj) {
assert.equal(obj['99'], 'banana'); assert.strictEqual(obj['99'], 'banana')
assert.equal(obj.test, '25'); assert.strictEqual(obj.test, '25')
return done(err); return done(err)
}); })
}); })
it('allows a key plus array without callback', function (done) { it('allows a key plus array without callback', function (done) {
client.hmset(hash, [99, 'banana', 'test', 25]); client.hmset(hash, [99, 'banana', 'test', 25])
client.hgetall(hash, function (err, obj) { client.hgetall(hash, function (err, obj) {
assert.equal(obj['99'], 'banana'); assert.strictEqual(obj['99'], 'banana')
assert.equal(obj.test, '25'); assert.strictEqual(obj.test, '25')
return done(err); return done(err)
}); })
}); })
it('allows a key plus array and a callback', function (done) { it('allows a key plus array and a callback', function (done) {
client.hmset(hash, [99, 'banana', 'test', 25], helper.isString('OK')); client.hmset(hash, [99, 'banana', 'test', 25], helper.isString('OK'))
client.hgetall(hash, function (err, obj) { client.hgetall(hash, function (err, obj) {
assert.equal(obj['99'], 'banana'); assert.strictEqual(obj['99'], 'banana')
assert.equal(obj.test, '25'); assert.strictEqual(obj.test, '25')
return done(err); return done(err)
}); })
}); })
it('handles object-style syntax without callback', function (done) { it('handles object-style syntax without callback', function (done) {
client.hmset(hash, {'0123456789': 'abcdefghij', 'some manner of key': 'a type of value'}); client.hmset(hash, {'0123456789': 'abcdefghij', 'some manner of key': 'a type of value'})
client.hgetall(hash, function (err, obj) { client.hgetall(hash, function (err, obj) {
assert.equal(obj['0123456789'], 'abcdefghij'); assert.strictEqual(obj['0123456789'], 'abcdefghij')
assert.equal(obj['some manner of key'], 'a type of value'); assert.strictEqual(obj['some manner of key'], 'a type of value')
return done(err); return done(err)
}); })
}); })
afterEach(function () { afterEach(function () {
client.end(true); client.end(true)
}); })
}); })
}); })
}); })

View File

@@ -1,91 +1,91 @@
'use strict'; 'use strict'
var assert = require('assert'); var Buffer = require('safe-buffer').Buffer
var config = require('../lib/config'); var assert = require('assert')
var helper = require('../helper'); var config = require('../lib/config')
var redis = config.redis; var helper = require('../helper')
var redis = config.redis
describe("The 'hset' method", function () {
describe('The \'hset\' method', function () {
helper.allTests(function (ip, args) { helper.allTests(function (ip, args) {
describe('using ' + ip, function () { describe('using ' + ip, function () {
var client; var client
var hash = 'test hash'; var hash = 'test hash'
beforeEach(function (done) { beforeEach(function (done) {
client = redis.createClient.apply(null, args); client = redis.createClient.apply(null, args)
client.once('ready', function () { client.once('ready', function () {
client.flushdb(done); client.flushdb(done)
}); })
}); })
it('allows a value to be set in a hash', function (done) { it('allows a value to be set in a hash', function (done) {
var field = new Buffer('0123456789'); var field = Buffer.from('0123456789')
var value = new Buffer('abcdefghij'); var value = Buffer.from('abcdefghij')
client.hset(hash, field, value, helper.isNumber(1)); client.hset(hash, field, value, helper.isNumber(1))
client.hget(hash, field, helper.isString(value.toString(), done)); client.hget(hash, field, helper.isString(value.toString(), done))
}); })
it('handles an empty value', function (done) { it('handles an empty value', function (done) {
var field = new Buffer('0123456789'); var field = Buffer.from('0123456789')
var value = new Buffer(0); var value = Buffer.from('')
client.hset(hash, field, value, helper.isNumber(1)); client.hset(hash, field, value, helper.isNumber(1))
client.hget([hash, field], helper.isString('', done)); client.hget([hash, field], helper.isString('', done))
}); })
it('handles empty key and value', function (done) { it('handles empty key and value', function (done) {
var field = new Buffer(0); var field = Buffer.from('')
var value = new Buffer(0); var value = Buffer.from('')
client.hset([hash, field, value], function (err, res) { client.hset([hash, field, value], function (err, res) {
assert.strictEqual(res, 1); assert.strictEqual(err, null)
client.hset(hash, field, value, helper.isNumber(0, done)); assert.strictEqual(res, 1)
}); client.hset(hash, field, value, helper.isNumber(0, done))
}); })
})
it('warns if someone passed a array either as field or as value', function (done) { it('warns if someone passed a array either as field or as value', function (done) {
var hash = 'test hash'; var hash = 'test hash'
var field = 'array'; var field = 'array'
// This would be converted to "array contents" but if you use more than one entry, // This would be converted to "array contents" but if you use more than one entry,
// it'll result in e.g. "array contents,second content" and this is not supported and considered harmful // it'll result in e.g. "array contents,second content" and this is not supported and considered harmful
var value = ['array contents']; var value = ['array contents']
client.on('warning', function (msg) { client.on('warning', function (msg) {
assert.strictEqual( assert.strictEqual(
msg, msg,
'Deprecated: The HMSET command contains a argument of type Array.\n' + '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' + '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.' 'Please handle this in your code to make sure everything works as you intended it to.'
); )
done(); done()
}); })
client.hmset(hash, field, value); client.hmset(hash, field, value)
}); })
it('does not error when a buffer and date are set as values on the same hash', function (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 hash = 'test hash'
var field1 = 'buffer'; var field1 = 'buffer'
var value1 = new Buffer('abcdefghij'); var value1 = Buffer.from('abcdefghij')
var field2 = 'date'; var field2 = 'date'
var value2 = new Date(); var value2 = 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) { it('does not error when a buffer and date are set as fields on the same hash', function (done) {
var hash = 'test hash'; var hash = 'test hash'
var value1 = 'buffer'; var value1 = 'buffer'
var field1 = new Buffer('abcdefghij'); var field1 = Buffer.from('abcdefghij')
var value2 = 'date'; var value2 = 'date'
var field2 = new 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))
}); })
afterEach(function () { afterEach(function () {
client.end(true); client.end(true)
}); })
}); })
}); })
}); })

View File

@@ -1,24 +1,20 @@
'use strict'; 'use strict'
var assert = require('assert'); var assert = require('assert')
var config = require('../lib/config'); var config = require('../lib/config')
var helper = require('../helper'); var helper = require('../helper')
var redis = config.redis; var redis = config.redis
describe("The 'incr' method", function () {
describe('The \'incr\' method', function () {
helper.allTests(function (ip, args) { helper.allTests(function (ip, args) {
describe('using ' + ip, function () { describe('using ' + ip, function () {
describe('when connected and a value in Redis', function () { describe('when connected and a value in Redis', function () {
var client
var client; var key = 'ABOVE_SAFE_JAVASCRIPT_INTEGER'
var key = 'ABOVE_SAFE_JAVASCRIPT_INTEGER';
afterEach(function () { afterEach(function () {
client.end(true); client.end(true)
}); })
/* /*
Number.MAX_SAFE_INTEGER === Math.pow(2, 53) - 1 === 9007199254740991 Number.MAX_SAFE_INTEGER === Math.pow(2, 53) - 1 === 9007199254740991
@@ -32,44 +28,44 @@ describe("The 'incr' method", function () {
... ...
*/ */
it('count above the safe integers as numbers', function (done) { it('count above the safe integers as numbers', function (done) {
client = redis.createClient.apply(null, args); client = redis.createClient.apply(null, args)
// Set a value to the maximum safe allowed javascript number (2^53) - 1 // Set a value to the maximum safe allowed javascript number (2^53) - 1
client.set(key, Number.MAX_SAFE_INTEGER, helper.isNotError()); 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 + 1))
client.incr(key, helper.isNumber(Number.MAX_SAFE_INTEGER + 2)); 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 + 3))
client.incr(key, helper.isNumber(Number.MAX_SAFE_INTEGER + 4)); client.incr(key, helper.isNumber(Number.MAX_SAFE_INTEGER + 4))
client.incr(key, helper.isNumber(Number.MAX_SAFE_INTEGER + 5)); client.incr(key, helper.isNumber(Number.MAX_SAFE_INTEGER + 5))
client.incr(key, function (err, res) { client.incr(key, function (err, res) {
helper.isNumber(Number.MAX_SAFE_INTEGER + 6)(err, res); helper.isNumber(Number.MAX_SAFE_INTEGER + 6)(err, res)
assert.strictEqual(typeof res, 'number'); assert.strictEqual(typeof res, 'number')
}); })
client.incr(key, helper.isNumber(Number.MAX_SAFE_INTEGER + 7)); 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 + 8))
client.incr(key, helper.isNumber(Number.MAX_SAFE_INTEGER + 9)); client.incr(key, helper.isNumber(Number.MAX_SAFE_INTEGER + 9))
client.incr(key, helper.isNumber(Number.MAX_SAFE_INTEGER + 10, done)); client.incr(key, helper.isNumber(Number.MAX_SAFE_INTEGER + 10, done))
}); })
it('count above the safe integers as strings', function (done) { it('count above the safe integers as strings', function (done) {
args[2].stringNumbers = true; args[2].stringNumbers = true
client = redis.createClient.apply(null, args); client = redis.createClient.apply(null, args)
// Set a value to the maximum safe allowed javascript number (2^53) // Set a value to the maximum safe allowed javascript number (2^53)
client.set(key, Number.MAX_SAFE_INTEGER, helper.isNotError()); client.set(key, Number.MAX_SAFE_INTEGER, helper.isNotError())
client.incr(key, helper.isString('9007199254740992')); client.incr(key, helper.isString('9007199254740992'))
client.incr(key, helper.isString('9007199254740993')); client.incr(key, helper.isString('9007199254740993'))
client.incr(key, helper.isString('9007199254740994')); client.incr(key, helper.isString('9007199254740994'))
client.incr(key, helper.isString('9007199254740995')); client.incr(key, helper.isString('9007199254740995'))
client.incr(key, helper.isString('9007199254740996')); client.incr(key, helper.isString('9007199254740996'))
client.incr(key, function (err, res) { client.incr(key, function (err, res) {
helper.isString('9007199254740997')(err, res); helper.isString('9007199254740997')(err, res)
assert.strictEqual(typeof res, 'string'); assert.strictEqual(typeof res, 'string')
}); })
client.incr(key, helper.isString('9007199254740998')); client.incr(key, helper.isString('9007199254740998'))
client.incr(key, helper.isString('9007199254740999')); client.incr(key, helper.isString('9007199254740999'))
client.incr(key, helper.isString('9007199254741000')); client.incr(key, helper.isString('9007199254741000'))
client.incr(key, helper.isString('9007199254741001', done)); client.incr(key, helper.isString('9007199254741001', done))
}); })
}); })
}); })
}); })
}); })

View File

@@ -1,79 +1,78 @@
'use strict'; 'use strict'
var assert = require('assert'); var assert = require('assert')
var config = require('../lib/config'); var config = require('../lib/config')
var helper = require('../helper'); var helper = require('../helper')
var redis = config.redis; var redis = config.redis
describe("The 'info' method", function () {
describe('The \'info\' method', function () {
helper.allTests(function (ip, args) { helper.allTests(function (ip, args) {
describe('using ' + ip, function () { describe('using ' + ip, function () {
var client; var client
beforeEach(function (done) { beforeEach(function (done) {
client = redis.createClient.apply(null, args); client = redis.createClient.apply(null, args)
client.once('ready', function () { client.once('ready', function () {
client.flushall(done); client.flushall(done)
}); })
}); })
afterEach(function () { afterEach(function () {
client.end(true); client.end(true)
}); })
it('update serverInfo after a info command', function (done) { it('update serverInfo after a info command', function (done) {
client.set('foo', 'bar'); client.set('foo', 'bar')
client.info(); client.info()
client.select(2, function () { client.select(2, function () {
assert.strictEqual(client.serverInfo.db2, undefined); assert.strictEqual(client.serverInfo.db2, undefined)
}); })
client.set('foo', 'bar'); client.set('foo', 'bar')
client.info(); client.info()
setTimeout(function () { setTimeout(function () {
assert.strictEqual(typeof client.serverInfo.db2, 'object'); assert.strictEqual(typeof client.serverInfo.db2, 'object')
done(); done()
}, 30); }, 30)
}); })
it('works with optional section provided with and without callback', function (done) { it('works with optional section provided with and without callback', function (done) {
client.set('foo', 'bar'); client.set('foo', 'bar')
client.info('keyspace'); client.info('keyspace')
client.select(2, function () { client.select(2, function () {
assert.strictEqual(Object.keys(client.serverInfo).length, 2, 'Key length should be three'); 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'); assert.strictEqual(typeof client.serverInfo.db0, 'object', 'db0 keyspace should be an object')
}); })
client.info(['keyspace']); client.info(['keyspace'])
client.set('foo', 'bar'); client.set('foo', 'bar')
client.info('all', function (err, res) { client.info('all', function (err, res) {
assert(Object.keys(client.serverInfo).length > 3, 'Key length should be way above three'); assert.strictEqual(err, null)
assert.strictEqual(typeof client.serverInfo.redis_version, 'string'); assert(Object.keys(client.serverInfo).length > 3, 'Key length should be way above three')
assert.strictEqual(typeof client.serverInfo.db2, 'object'); assert.strictEqual(typeof client.serverInfo.redis_version, 'string')
done(); assert.strictEqual(typeof client.serverInfo.db2, 'object')
}); done()
}); })
})
it('check redis v.2.4 support', function (done) { it('check redis v.2.4 support', function (done) {
var end = helper.callFuncAfter(done, 2); var end = helper.callFuncAfter(done, 2)
client.internalSendCommand = function (commandObj) { client.internalSendCommand = function (commandObj) {
assert.strictEqual(commandObj.args.length, 0); assert.strictEqual(commandObj.args.length, 0)
assert.strictEqual(commandObj.command, 'info'); assert.strictEqual(commandObj.command, 'info')
end(); end()
}; }
client.info(); client.info()
client.info(function () {}); client.info(function () {})
}); })
it('emit error after a failure', function (done) { it('emit error after a failure', function (done) {
client.info(); client.info()
client.once('error', function (err) { client.once('error', function (err) {
assert.strictEqual(err.code, 'UNCERTAIN_STATE'); assert.strictEqual(err.code, 'UNCERTAIN_STATE')
assert.strictEqual(err.command, 'INFO'); assert.strictEqual(err.command, 'INFO')
done(); done()
}); })
client.stream.destroy(); client.stream.destroy()
}); })
}); })
}); })
}); })

View File

@@ -1,69 +1,67 @@
'use strict'; 'use strict'
var assert = require('assert'); var assert = require('assert')
var config = require('../lib/config'); var config = require('../lib/config')
var crypto = require('crypto'); var crypto = require('crypto')
var helper = require('../helper'); var helper = require('../helper')
var redis = config.redis; var redis = config.redis
describe("The 'keys' method", function () {
describe('The \'keys\' method', function () {
helper.allTests(function (ip, args) { helper.allTests(function (ip, args) {
describe('using ' + ip, function () { describe('using ' + ip, function () {
var client; var client
beforeEach(function (done) { beforeEach(function (done) {
client = redis.createClient.apply(null, args); client = redis.createClient.apply(null, args)
client.once('ready', function () { client.once('ready', function () {
client.flushall(done); client.flushall(done)
}); })
}); })
it('returns matching keys', function (done) { it('returns matching keys', function (done) {
client.mset(['test keys 1', 'test val 1', 'test keys 2', 'test val 2'], helper.isString('OK')); client.mset(['test keys 1', 'test val 1', 'test keys 2', 'test val 2'], helper.isString('OK'))
client.keys('test keys*', function (err, results) { client.keys('test keys*', function (err, results) {
assert.strictEqual(2, results.length); assert.strictEqual(2, results.length)
assert.ok(~results.indexOf('test keys 1')); assert.ok(~results.indexOf('test keys 1'))
assert.ok(~results.indexOf('test keys 2')); assert.ok(~results.indexOf('test keys 2'))
return done(err); return done(err)
}); })
}); })
it('handles a large packet size', function (done) { it('handles a large packet size', function (done) {
var keysValues = []; var keysValues = []
for (var i = 0; i < 200; i++) { for (var i = 0; i < 200; i++) {
var keyValue = [ var keyValue = [
'multibulk:' + crypto.randomBytes(256).toString('hex'), // use long strings as keys to ensure generation of large packet 'multibulk:' + crypto.randomBytes(256).toString('hex'), // use long strings as keys to ensure generation of large packet
'test val ' + i 'test val ' + i
]; ]
keysValues.push(keyValue); keysValues.push(keyValue)
} }
client.mset(keysValues.reduce(function (a, b) { client.mset(keysValues.reduce(function (a, b) {
return a.concat(b); return a.concat(b)
}), helper.isString('OK')); }), helper.isString('OK'))
client.keys('multibulk:*', function (err, results) { client.keys('multibulk:*', function (err, results) {
assert.deepEqual(keysValues.map(function (val) { assert.deepEqual(keysValues.map(function (val) {
return val[0]; return val[0]
}).sort(), results.sort()); }).sort(), results.sort())
return done(err); return done(err)
}); })
}); })
it('handles an empty response', function (done) { it('handles an empty response', function (done) {
client.keys(['users:*'], function (err, results) { client.keys(['users:*'], function (err, results) {
assert.strictEqual(results.length, 0); assert.strictEqual(results.length, 0)
assert.ok(Array.isArray(results)); assert.ok(Array.isArray(results))
return done(err); return done(err)
}); })
}); })
afterEach(function () { afterEach(function () {
client.end(true); client.end(true)
}); })
}); })
}); })
}); })

View File

@@ -1,70 +1,68 @@
'use strict'; 'use strict'
var assert = require('assert'); var assert = require('assert')
var config = require('../lib/config'); var config = require('../lib/config')
var helper = require('../helper'); var helper = require('../helper')
var redis = config.redis; var redis = config.redis
describe("The 'mget' method", function () {
describe('The \'mget\' method', function () {
helper.allTests(function (ip, args) { helper.allTests(function (ip, args) {
describe('using ' + ip, function () { describe('using ' + ip, function () {
var client; var client
beforeEach(function (done) { beforeEach(function (done) {
client = redis.createClient.apply(null, args); client = redis.createClient.apply(null, args)
client.once('error', done); client.once('error', done)
client.once('ready', function () { client.once('ready', function () {
client.flushdb(); client.flushdb()
client.mset(['mget keys 1', 'mget val 1', 'mget keys 2', 'mget val 2', 'mget keys 3', 'mget val 3'], done); 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 in argument form', function (done) { 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.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) { client.mget('mget keys 1', 'mget keys 2', 'mget keys 3', function (err, results) {
assert.strictEqual(3, results.length); assert.strictEqual(3, results.length)
assert.strictEqual('mget val 1', results[0].toString()); assert.strictEqual('mget val 1', results[0].toString())
assert.strictEqual('mget val 2', results[1].toString()); assert.strictEqual('mget val 2', results[1].toString())
assert.strictEqual('mget val 3', results[2].toString()); assert.strictEqual('mget val 3', results[2].toString())
return done(err); return done(err)
}); })
}); })
it('handles fetching multiple keys via an array', function (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) { 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 1', results[0].toString())
assert.strictEqual('mget val 2', results[1].toString()); assert.strictEqual('mget val 2', results[1].toString())
assert.strictEqual('mget val 3', results[2].toString()); assert.strictEqual('mget val 3', results[2].toString())
return done(err); return done(err)
}); })
}); })
it('handles fetching multiple keys, when some keys do not exist', function (done) { 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) { client.mget('mget keys 1', ['some random shit', 'mget keys 2', 'mget keys 3'], function (err, results) {
assert.strictEqual(4, results.length); assert.strictEqual(4, results.length)
assert.strictEqual('mget val 1', results[0].toString()); assert.strictEqual('mget val 1', results[0].toString())
assert.strictEqual(null, results[1]); assert.strictEqual(null, results[1])
assert.strictEqual('mget val 2', results[2].toString()); assert.strictEqual('mget val 2', results[2].toString())
assert.strictEqual('mget val 3', results[3].toString()); assert.strictEqual('mget val 3', results[3].toString())
return done(err); return done(err)
}); })
}); })
it('handles fetching multiple keys, when some keys do not exist promisified', function () { 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) { 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(4, results.length)
assert.strictEqual('mget val 1', results[0].toString()); assert.strictEqual('mget val 1', results[0].toString())
assert.strictEqual(null, results[1]); assert.strictEqual(null, results[1])
assert.strictEqual('mget val 2', results[2].toString()); assert.strictEqual('mget val 2', results[2].toString())
assert.strictEqual('mget val 3', results[3].toString()); assert.strictEqual('mget val 3', results[3].toString())
}); })
}); })
afterEach(function () { afterEach(function () {
client.end(true); client.end(true)
}); })
}); })
}); })
}); })

View File

@@ -1,167 +1,166 @@
'use strict'; 'use strict'
var assert = require('assert'); var Buffer = require('safe-buffer').Buffer
var config = require('../lib/config'); var assert = require('assert')
var helper = require('../helper'); var config = require('../lib/config')
var utils = require('../../lib/utils'); var helper = require('../helper')
var redis = config.redis; 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) { helper.allTests(function (parser, ip, args) {
var client
var client;
afterEach(function () { afterEach(function () {
client.end(true); client.end(true)
}); })
beforeEach(function (done) { beforeEach(function (done) {
client = redis.createClient.apply(null, args); client = redis.createClient.apply(null, args)
client.once('connect', function () { client.once('connect', function () {
client.flushdb(done); client.flushdb(done)
}); })
}); })
it('monitors commands on all redis clients and works in the correct order', function (done) { it('monitors commands on all redis clients and works in the correct order', function (done) {
var monitorClient = redis.createClient.apply(null, args); var monitorClient = redis.createClient.apply(null, args)
var responses = [ var responses = [
['mget', 'some', 'keys', 'foo', 'bar'], ['mget', 'some', 'keys', 'foo', 'bar'],
['set', 'json', '{"foo":"123","bar":"sdflkdfsjk","another":false}'], ['set', 'json', '{"foo":"123","bar":"sdflkdfsjk","another":false}'],
['eval', "return redis.call('set', 'sha', 'test')", '0'], ['eval', 'return redis.call(\'set\', \'sha\', \'test\')', '0'],
['set', 'sha', 'test'], ['set', 'sha', 'test'],
['get', 'baz'], ['get', 'baz'],
['set', 'foo', 'bar" "s are " " good!"'], ['set', 'foo', 'bar" "s are " " good!"'],
['mget', 'foo', 'baz'], ['mget', 'foo', 'baz'],
['subscribe', 'foo', 'baz'] ['subscribe', 'foo', 'baz']
]; ]
var end = helper.callFuncAfter(done, 5); var end = helper.callFuncAfter(done, 5)
monitorClient.set('foo', 'bar'); monitorClient.set('foo', 'bar')
monitorClient.flushdb(); monitorClient.flushdb()
monitorClient.monitor(function (err, res) { monitorClient.monitor(function (err, res) {
assert.strictEqual(res, 'OK'); assert.strictEqual(err, null)
client.mget('some', 'keys', 'foo', 'bar'); assert.strictEqual(res, 'OK')
client.mget('some', 'keys', 'foo', 'bar')
client.set('json', JSON.stringify({ client.set('json', JSON.stringify({
foo: '123', foo: '123',
bar: 'sdflkdfsjk', bar: 'sdflkdfsjk',
another: false another: false
})); }))
client.eval("return redis.call('set', 'sha', 'test')", 0); client.eval('return redis.call(\'set\', \'sha\', \'test\')', 0)
monitorClient.get('baz', function (err, res) { monitorClient.get('baz', function (err, res) {
assert.strictEqual(res, null); assert.strictEqual(res, null)
end(err); end(err)
}); })
monitorClient.set('foo', 'bar" "s are " " good!"', function (err, res) { monitorClient.set('foo', 'bar" "s are " " good!"', function (err, res) {
assert.strictEqual(res, 'OK'); assert.strictEqual(res, 'OK')
end(err); end(err)
}); })
monitorClient.mget('foo', 'baz', function (err, res) { monitorClient.mget('foo', 'baz', function (err, res) {
assert.strictEqual(res[0], 'bar" "s are " " good!"'); assert.strictEqual(res[0], 'bar" "s are " " good!"')
assert.strictEqual(res[1], null); assert.strictEqual(res[1], null)
end(err); end(err)
}); })
monitorClient.subscribe('foo', 'baz', function (err, res) { monitorClient.subscribe('foo', 'baz', function (err, res) {
// The return value might change in v.3 // The return value might change in v.3
// assert.strictEqual(res, 'baz'); // assert.strictEqual(res, 'baz');
// TODO: Fix the return value of subscribe calls // TODO: Fix the return value of subscribe calls
end(err); end(err)
}); })
}); })
monitorClient.on('monitor', function (time, args, rawOutput) { monitorClient.on('monitor', function (time, args, rawOutput) {
assert.strictEqual(monitorClient.monitoring, true); assert.strictEqual(monitorClient.monitoring, true)
assert.deepEqual(args, responses.shift()); assert.deepEqual(args, responses.shift())
assert(utils.monitorRegex.test(rawOutput), rawOutput); assert(utils.monitorRegex.test(rawOutput), rawOutput)
if (responses.length === 0) { if (responses.length === 0) {
monitorClient.quit(end); monitorClient.quit(end)
} }
}); })
}); })
it('monitors returns strings in the rawOutput even with returnBuffers activated', function (done) { it('monitors returns strings in the rawOutput even with returnBuffers activated', function (done) {
if (process.platform === 'win32') { if (process.platform === 'win32') {
this.skip(); this.skip()
} }
var monitorClient = redis.createClient({ var monitorClient = redis.createClient({
returnBuffers: true, returnBuffers: true,
path: '/tmp/redis.sock' path: '/tmp/redis.sock'
}); })
monitorClient.monitor(function (err, res) { monitorClient.monitor(function (err, res) {
assert.strictEqual(monitorClient.monitoring, true); assert.strictEqual(err, null)
assert.strictEqual(res.inspect(), new Buffer('OK').inspect()); assert.strictEqual(monitorClient.monitoring, true)
monitorClient.mget('hello', new Buffer('world')); assert.strictEqual(res.inspect(), Buffer.from('OK').inspect())
}); monitorClient.mget('hello', Buffer.from('world'))
})
monitorClient.on('monitor', function (time, args, rawOutput) { monitorClient.on('monitor', function (time, args, rawOutput) {
assert.strictEqual(typeof rawOutput, 'string'); assert.strictEqual(typeof rawOutput, 'string')
assert(utils.monitorRegex.test(rawOutput), rawOutput); assert(utils.monitorRegex.test(rawOutput), rawOutput)
assert.deepEqual(args, ['mget', 'hello', 'world']); assert.deepEqual(args, ['mget', 'hello', 'world'])
// Quit immediatly ends monitoring mode and therefore does not stream back the quit command // Quit immediately ends monitoring mode and therefore does not stream back the quit command
monitorClient.quit(done); monitorClient.quit(done)
}); })
}); })
it('monitors reconnects properly and works with the offline queue', function (done) { it('monitors reconnects properly and works with the offline queue', function (done) {
var called = false; var called = false
client.monitor(helper.isString('OK')); client.monitor(helper.isString('OK'))
client.mget('hello', 'world'); client.mget('hello', 'world')
client.on('monitor', function (time, args, rawOutput) { client.on('monitor', function (time, args, rawOutput) {
assert.strictEqual(client.monitoring, true); assert.strictEqual(client.monitoring, true)
assert(utils.monitorRegex.test(rawOutput), rawOutput); assert(utils.monitorRegex.test(rawOutput), rawOutput)
assert.deepEqual(args, ['mget', 'hello', 'world']); assert.deepEqual(args, ['mget', 'hello', 'world'])
if (called) { if (called) {
// End after a reconnect // End after a reconnect
return done(); return done()
} }
client.stream.destroy(); client.stream.destroy()
client.mget('hello', 'world'); client.mget('hello', 'world')
called = true; called = true
}); })
}); })
it('monitors reconnects properly and works with the offline queue in a batch statement', function (done) { it('monitors reconnects properly and works with the offline queue in a batch statement', function (done) {
var called = false; var called = false
var multi = client.batch(); var multi = client.batch()
multi.monitor(helper.isString('OK')); multi.monitor(helper.isString('OK'))
multi.mget('hello', 'world'); multi.mget('hello', 'world')
multi.exec(function (err, res) { multi.exec(helper.isDeepEqual(['OK', [null, null]]))
assert.deepEqual(res, ['OK', [null, null]]);
});
client.on('monitor', function (time, args, rawOutput) { client.on('monitor', function (time, args, rawOutput) {
assert.strictEqual(client.monitoring, true); assert.strictEqual(client.monitoring, true)
assert(utils.monitorRegex.test(rawOutput), rawOutput); assert(utils.monitorRegex.test(rawOutput), rawOutput)
assert.deepEqual(args, ['mget', 'hello', 'world']); assert.deepEqual(args, ['mget', 'hello', 'world'])
if (called) { if (called) {
// End after a reconnect // End after a reconnect
return done(); return done()
} }
client.stream.destroy(); client.stream.destroy()
client.mget('hello', 'world'); client.mget('hello', 'world')
called = true; called = true
}); })
}); })
it('monitor activates even if the command could not be processed properly after a reconnect', function (done) { it('monitor activates even if the command could not be processed properly after a reconnect', function (done) {
client.monitor(function (err, res) { client.monitor(function (err, res) {
assert.strictEqual(err.code, 'UNCERTAIN_STATE'); assert.strictEqual(err.code, 'UNCERTAIN_STATE')
}); })
client.on('error', function (err) {}); // Ignore error here client.on('error', function () {}) // Ignore error here
client.stream.destroy(); client.stream.destroy()
var end = helper.callFuncAfter(done, 2); var end = helper.callFuncAfter(done, 2)
client.on('monitor', function (time, args, rawOutput) { client.on('monitor', function (time, args, rawOutput) {
assert.strictEqual(client.monitoring, true); assert.strictEqual(client.monitoring, true)
end(); end()
}); })
client.on('reconnecting', function () { client.on('reconnecting', function () {
client.get('foo', function (err, res) { client.get('foo', function (err, res) {
assert(!err); assert(!err)
assert.strictEqual(client.monitoring, true); assert.strictEqual(client.monitoring, true)
end(); end()
}); })
}); })
}); })
it('monitors works in combination with the pub sub mode and the offline queue', function (done) { it('monitors works in combination with the pub sub mode and the offline queue', function (done) {
var responses = [ var responses = [
@@ -172,44 +171,45 @@ describe("The 'monitor' method", function () {
['subscribe', 'baz'], ['subscribe', 'baz'],
['unsubscribe', 'baz'], ['unsubscribe', 'baz'],
['publish', '/foo', 'hello world'] ['publish', '/foo', 'hello world']
]; ]
var pub = redis.createClient(); var pub = redis.createClient()
pub.on('ready', function () { pub.on('ready', function () {
client.monitor(function (err, res) { client.monitor(function (err, res) {
assert.strictEqual(res, 'OK'); assert.strictEqual(err, null)
pub.get('foo', helper.isNull()); assert.strictEqual(res, 'OK')
}); pub.get('foo', helper.isNull())
client.subscribe('/foo', '/bar'); })
client.unsubscribe('/bar'); client.subscribe('/foo', '/bar')
client.unsubscribe('/bar')
setTimeout(function () { setTimeout(function () {
client.stream.destroy(); client.stream.destroy()
client.once('ready', function () { client.once('ready', function () {
pub.publish('/foo', 'hello world'); pub.publish('/foo', 'hello world')
}); })
client.set('foo', 'bar', helper.isError()); client.set('foo', 'bar', helper.isError())
client.subscribe('baz'); client.subscribe('baz')
client.unsubscribe('baz'); client.unsubscribe('baz')
}, 150); }, 150)
var called = false; var called = false
client.on('monitor', function (time, args, rawOutput) { client.on('monitor', function (time, args, rawOutput) {
assert.deepEqual(args, responses.shift()); assert.deepEqual(args, responses.shift())
assert(utils.monitorRegex.test(rawOutput), rawOutput); assert(utils.monitorRegex.test(rawOutput), rawOutput)
if (responses.length === 0) { if (responses.length === 0) {
// The publish is called right after the reconnect and the monitor is called before the message is emitted. // The publish is called right after the reconnect and the monitor is called before the message is emitted.
// Therefore we have to wait till the next tick // Therefore we have to wait till the next tick
process.nextTick(function () { process.nextTick(function () {
assert(called); assert(called)
client.quit(done); client.quit(done)
pub.end(false); pub.end(false)
}); })
} }
}); })
client.on('message', function (channel, msg) { client.on('message', function (channel, msg) {
assert.strictEqual(channel, '/foo'); assert.strictEqual(channel, '/foo')
assert.strictEqual(msg, 'hello world'); assert.strictEqual(msg, 'hello world')
called = true; called = true
}); })
}); })
}); })
}); })
}); })

View File

@@ -1,111 +1,108 @@
'use strict'; 'use strict'
var assert = require('assert'); var assert = require('assert')
var config = require('../lib/config'); var config = require('../lib/config')
var helper = require('../helper'); var helper = require('../helper')
var redis = config.redis; var redis = config.redis
var uuid = require('uuid'); var uuid = require('uuid')
describe("The 'mset' method", function () {
describe('The \'mset\' method', function () {
helper.allTests(function (ip, args) { helper.allTests(function (ip, args) {
describe('using ' + ip, function () { describe('using ' + ip, function () {
var key, value, key2, value2; var key, value, key2, value2
beforeEach(function () { beforeEach(function () {
key = uuid.v4(); key = uuid.v4()
value = uuid.v4(); value = uuid.v4()
key2 = uuid.v4(); key2 = uuid.v4()
value2 = uuid.v4(); value2 = uuid.v4()
}); })
describe('when not connected', function () { describe('when not connected', function () {
var client; var client
beforeEach(function (done) { beforeEach(function (done) {
client = redis.createClient.apply(null, args); client = redis.createClient.apply(null, args)
client.once('ready', function () { client.once('ready', function () {
client.quit(); client.quit()
}); })
client.on('end', done); client.on('end', done)
}); })
it('reports an error', function (done) { it('reports an error', function (done) {
client.mset(key, value, key2, value2, function (err, res) { client.mset(key, value, key2, value2, function (err, res) {
assert(err.message.match(/The connection is already closed/)); assert(err.message.match(/The connection is already closed/))
done(); done()
}); })
}); })
}); })
describe('when connected', function () { describe('when connected', function () {
var client; var client
beforeEach(function (done) { beforeEach(function (done) {
client = redis.createClient.apply(null, args); client = redis.createClient.apply(null, args)
client.once('ready', function () { client.once('ready', function () {
done(); done()
}); })
}); })
afterEach(function () { afterEach(function () {
client.end(true); client.end(true)
}); })
describe('and a callback is specified', function () { describe('and a callback is specified', function () {
describe('with valid parameters', function () { describe('with valid parameters', function () {
it('sets the value correctly', function (done) { it('sets the value correctly', function (done) {
client.mset(key, value, key2, value2, function (err) { client.mset(key, value, key2, value2, function (err) {
if (err) { if (err) {
return done(err); return done(err)
} }
client.get(key, helper.isString(value)); client.get(key, helper.isString(value))
client.get(key2, helper.isString(value2, done)); client.get(key2, helper.isString(value2, done))
}); })
}); })
}); })
describe("with undefined 'key' parameter and missing 'value' parameter", function () { describe('with undefined \'key\' parameter and missing \'value\' parameter', function () {
it('reports an error', function (done) { it('reports an error', function (done) {
client.mset(undefined, function (err, res) { client.mset(undefined, function (err, res) {
helper.isError()(err, null); helper.isError()(err, null)
done(); done()
}); })
}); })
}); })
})
});
describe('and no callback is specified', function () { describe('and no callback is specified', function () {
describe('with valid parameters', function () { describe('with valid parameters', function () {
it('sets the value correctly', function (done) { it('sets the value correctly', function (done) {
client.mset(key, value2, key2, value); client.mset(key, value2, key2, value)
client.get(key, helper.isString(value2)); client.get(key, helper.isString(value2))
client.get(key2, helper.isString(value, done)); client.get(key2, helper.isString(value, done))
}); })
it('sets the value correctly with array syntax', function (done) { it('sets the value correctly with array syntax', function (done) {
client.mset([key, value2, key2, value]); client.mset([key, value2, key2, value])
client.get(key, helper.isString(value2)); client.get(key, helper.isString(value2))
client.get(key2, helper.isString(value, done)); client.get(key2, helper.isString(value, done))
}); })
}); })
describe("with undefined 'key' and missing 'value' parameter", function () { describe('with undefined \'key\' and missing \'value\' parameter', function () {
// this behavior is different from the 'set' behavior. // this behavior is different from the 'set' behavior.
it('emits an error', function (done) { it('emits an error', function (done) {
client.on('error', function (err) { client.on('error', function (err) {
assert.strictEqual(err.message, "ERR wrong number of arguments for 'mset' command"); assert.strictEqual(err.message, 'ERR wrong number of arguments for \'mset\' command')
assert.strictEqual(err.name, 'ReplyError'); assert.strictEqual(err.name, 'ReplyError')
done(); done()
}); })
client.mset(); client.mset()
}); })
}); })
}); })
}); })
}); })
}); })
}); })

View File

@@ -1,38 +1,36 @@
'use strict'; 'use strict'
var config = require('../lib/config'); var config = require('../lib/config')
var helper = require('../helper'); var helper = require('../helper')
var redis = config.redis; var redis = config.redis
describe("The 'msetnx' method", function () {
describe('The \'msetnx\' method', function () {
helper.allTests(function (ip, args) { helper.allTests(function (ip, args) {
describe('using ' + ip, function () { describe('using ' + ip, function () {
var client; var client
beforeEach(function (done) { beforeEach(function (done) {
client = redis.createClient.apply(null, args); client = redis.createClient.apply(null, args)
client.once('ready', function () { client.once('ready', function () {
client.flushdb(done); client.flushdb(done)
}); })
}); })
it('if any keys exist entire operation fails', function (done) { it('if any keys exist entire operation fails', function (done) {
client.mset(['mset1', 'val1', 'mset2', 'val2', 'mset3', 'val3'], helper.isString('OK')); client.mset(['mset1', 'val1', 'mset2', 'val2', 'mset3', 'val3'], helper.isString('OK'))
client.msetnx(['mset3', 'val3', 'mset4', 'val4'], helper.isNumber(0)); client.msetnx(['mset3', 'val3', 'mset4', 'val4'], helper.isNumber(0))
client.exists(['mset4'], helper.isNumber(0, done)); client.exists(['mset4'], helper.isNumber(0, done))
}); })
it('sets multiple keys if all keys are not set', function (done) { it('sets multiple keys if all keys are not set', function (done) {
client.msetnx(['mset3', 'val3', 'mset4', 'val4'], helper.isNumber(1)); client.msetnx(['mset3', 'val3', 'mset4', 'val4'], helper.isNumber(1))
client.exists(['mset3'], helper.isNumber(1)); client.exists(['mset3'], helper.isNumber(1))
client.exists(['mset3'], helper.isNumber(1, done)); client.exists(['mset3'], helper.isNumber(1, done))
}); })
afterEach(function () { afterEach(function () {
client.end(true); client.end(true)
}); })
}); })
}); })
}); })

View File

@@ -1,35 +1,33 @@
'use strict'; 'use strict'
var assert = require('assert'); var assert = require('assert')
var config = require('../lib/config'); var config = require('../lib/config')
var helper = require('../helper'); var helper = require('../helper')
var redis = config.redis; var redis = config.redis
describe("The 'randomkey' method", function () {
describe('The \'randomkey\' method', function () {
helper.allTests(function (ip, args) { helper.allTests(function (ip, args) {
describe('using ' + ip, function () { describe('using ' + ip, function () {
var client; var client
beforeEach(function (done) { beforeEach(function (done) {
client = redis.createClient.apply(null, args); client = redis.createClient.apply(null, args)
client.once('ready', function () { client.once('ready', function () {
client.flushdb(done); client.flushdb(done)
}); })
}); })
it('returns a random key', function (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.mset(['test keys 1', 'test val 1', 'test keys 2', 'test val 2'], helper.isString('OK'))
client.randomkey([], function (err, results) { client.randomkey([], function (err, results) {
assert.strictEqual(true, /test keys.+/.test(results)); assert.strictEqual(true, /test keys.+/.test(results))
return done(err); return done(err)
}); })
}); })
afterEach(function () { afterEach(function () {
client.end(true); client.end(true)
}); })
}); })
}); })
}); })

View File

@@ -1,38 +1,36 @@
'use strict'; 'use strict'
var config = require('../lib/config'); var config = require('../lib/config')
var helper = require('../helper'); var helper = require('../helper')
var redis = config.redis; var redis = config.redis
describe("The 'rename' method", function () {
describe('The \'rename\' method', function () {
helper.allTests(function (ip, args) { helper.allTests(function (ip, args) {
describe('using ' + ip, function () { describe('using ' + ip, function () {
var client; var client
beforeEach(function (done) { beforeEach(function (done) {
client = redis.createClient.apply(null, args); client = redis.createClient.apply(null, args)
client.once('ready', function () { client.once('ready', function () {
client.flushdb(done); client.flushdb(done)
}); })
}); })
it('populates the new key', function (done) { it('populates the new key', function (done) {
client.set(['foo', 'bar'], helper.isString('OK')); client.set(['foo', 'bar'], helper.isString('OK'))
client.rename(['foo', 'new foo'], helper.isString('OK')); client.rename(['foo', 'new foo'], helper.isString('OK'))
client.exists(['new foo'], helper.isNumber(1, done)); client.exists(['new foo'], helper.isNumber(1, done))
}); })
it('removes the old key', function (done) { it('removes the old key', function (done) {
client.set(['foo', 'bar'], helper.isString('OK')); client.set(['foo', 'bar'], helper.isString('OK'))
client.rename(['foo', 'new foo'], helper.isString('OK')); client.rename(['foo', 'new foo'], helper.isString('OK'))
client.exists(['foo'], helper.isNumber(0, done)); client.exists(['foo'], helper.isNumber(0, done))
}); })
afterEach(function () { afterEach(function () {
client.end(true); client.end(true)
}); })
}); })
}); })
}); })

View File

@@ -1,41 +1,39 @@
'use strict'; 'use strict'
var config = require('../lib/config'); var config = require('../lib/config')
var helper = require('../helper'); var helper = require('../helper')
var redis = config.redis; var redis = config.redis
describe("The 'renamenx' method", function () {
describe('The \'renamenx\' method', function () {
helper.allTests(function (ip, args) { helper.allTests(function (ip, args) {
describe('using ' + ip, function () { describe('using ' + ip, function () {
var client; var client
beforeEach(function (done) { beforeEach(function (done) {
client = redis.createClient.apply(null, args); client = redis.createClient.apply(null, args)
client.once('ready', function () { client.once('ready', function () {
client.flushdb(done); client.flushdb(done)
}); })
}); })
it('renames the key if target does not yet exist', function (done) { it('renames the key if target does not yet exist', function (done) {
client.set('foo', 'bar', helper.isString('OK')); client.set('foo', 'bar', helper.isString('OK'))
client.renamenx('foo', 'foo2', helper.isNumber(1)); client.renamenx('foo', 'foo2', helper.isNumber(1))
client.exists('foo', helper.isNumber(0)); client.exists('foo', helper.isNumber(0))
client.exists(['foo2'], helper.isNumber(1, done)); client.exists(['foo2'], helper.isNumber(1, done))
}); })
it('does not rename the key if the target exists', function (done) { it('does not rename the key if the target exists', function (done) {
client.set('foo', 'bar', helper.isString('OK')); client.set('foo', 'bar', helper.isString('OK'))
client.set('foo2', 'apple', helper.isString('OK')); client.set('foo2', 'apple', helper.isString('OK'))
client.renamenx('foo', 'foo2', helper.isNumber(0)); client.renamenx('foo', 'foo2', helper.isNumber(0))
client.exists('foo', helper.isNumber(1)); client.exists('foo', helper.isNumber(1))
client.exists(['foo2'], helper.isNumber(1, done)); client.exists(['foo2'], helper.isNumber(1, done))
}); })
afterEach(function () { afterEach(function () {
client.end(true); client.end(true)
}); })
}); })
}); })
}); })

View File

@@ -1,36 +1,34 @@
'use strict'; 'use strict'
var config = require('../lib/config'); var config = require('../lib/config')
var helper = require('../helper'); var helper = require('../helper')
var redis = config.redis; var redis = config.redis
var assert = require('assert'); var assert = require('assert')
describe("The 'rpush' command", function () {
describe('The \'rpush\' command', function () {
helper.allTests(function (ip, args) { helper.allTests(function (ip, args) {
describe('using ' + ip, function () { describe('using ' + ip, function () {
var client; var client
beforeEach(function (done) { beforeEach(function (done) {
client = redis.createClient.apply(null, args); client = redis.createClient.apply(null, args)
client.once('ready', function () { client.once('ready', function () {
client.flushdb(done); client.flushdb(done)
}); })
}); })
it('inserts multiple values at a time into a list', function (done) { it('inserts multiple values at a time into a list', function (done) {
client.rpush('test', ['list key', 'should be a list']); client.rpush('test', ['list key', 'should be a list'])
client.lrange('test', 0, -1, function (err, res) { client.lrange('test', 0, -1, function (err, res) {
assert.equal(res[0], 'list key'); assert.strictEqual(res[0], 'list key')
assert.equal(res[1], 'should be a list'); assert.strictEqual(res[1], 'should be a list')
done(err); done(err)
}); })
}); })
afterEach(function () { afterEach(function () {
client.end(true); client.end(true)
}); })
}); })
}); })
}); })

View File

@@ -1,62 +1,60 @@
'use strict'; 'use strict'
var assert = require('assert'); var assert = require('assert')
var config = require('../lib/config'); var config = require('../lib/config')
var helper = require('../helper'); var helper = require('../helper')
var redis = config.redis; var redis = config.redis
describe("The 'sadd' method", function () {
describe('The \'sadd\' method', function () {
helper.allTests(function (ip, args) { helper.allTests(function (ip, args) {
describe('using ' + ip, function () { describe('using ' + ip, function () {
var client; var client
beforeEach(function (done) { beforeEach(function (done) {
client = redis.createClient.apply(null, args); client = redis.createClient.apply(null, args)
client.once('ready', function () { client.once('ready', function () {
client.flushdb(done); client.flushdb(done)
}); })
}); })
it('allows a single value to be added to the set', function (done) { it('allows a single value to be added to the set', function (done) {
client.sadd('set0', 'member0', helper.isNumber(1)); client.sadd('set0', 'member0', helper.isNumber(1))
client.smembers('set0', function (err, res) { client.smembers('set0', function (err, res) {
assert.ok(~res.indexOf('member0')); assert.ok(~res.indexOf('member0'))
return done(err); return done(err)
}); })
}); })
it('does not add the same value to the set twice', function (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(1))
client.sadd('set0', 'member0', helper.isNumber(0, done)); client.sadd('set0', 'member0', helper.isNumber(0, done))
}); })
it('allows multiple values to be added to the set', function (done) { it('allows multiple values to be added to the set', function (done) {
client.sadd('set0', ['member0', 'member1', 'member2'], helper.isNumber(3)); client.sadd('set0', ['member0', 'member1', 'member2'], helper.isNumber(3))
client.smembers('set0', function (err, res) { client.smembers('set0', function (err, res) {
assert.strictEqual(res.length, 3); assert.strictEqual(res.length, 3)
assert.ok(~res.indexOf('member0')); assert.ok(~res.indexOf('member0'))
assert.ok(~res.indexOf('member1')); assert.ok(~res.indexOf('member1'))
assert.ok(~res.indexOf('member2')); assert.ok(~res.indexOf('member2'))
return done(err); return done(err)
}); })
}); })
it('allows multiple values to be added to the set with a different syntax', function (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.sadd(['set0', 'member0', 'member1', 'member2'], helper.isNumber(3))
client.smembers('set0', function (err, res) { client.smembers('set0', function (err, res) {
assert.strictEqual(res.length, 3); assert.strictEqual(res.length, 3)
assert.ok(~res.indexOf('member0')); assert.ok(~res.indexOf('member0'))
assert.ok(~res.indexOf('member1')); assert.ok(~res.indexOf('member1'))
assert.ok(~res.indexOf('member2')); assert.ok(~res.indexOf('member2'))
return done(err); return done(err)
}); })
}); })
afterEach(function () { afterEach(function () {
client.end(true); client.end(true)
}); })
}); })
}); })
}); })

View File

@@ -1,31 +1,29 @@
'use strict'; 'use strict'
var config = require('../lib/config'); var config = require('../lib/config')
var helper = require('../helper'); var helper = require('../helper')
var redis = config.redis; var redis = config.redis
describe("The 'scard' method", function () {
describe('The \'scard\' method', function () {
helper.allTests(function (ip, args) { helper.allTests(function (ip, args) {
describe('using ' + ip, function () { describe('using ' + ip, function () {
var client; var client
beforeEach(function (done) { beforeEach(function (done) {
client = redis.createClient.apply(null, args); client = redis.createClient.apply(null, args)
client.once('ready', function () { client.once('ready', function () {
client.flushdb(done); client.flushdb(done)
}); })
}); })
it('returns the number of values in a set', function (done) { it('returns the number of values in a set', function (done) {
client.sadd('foo', [1, 2, 3], helper.isNumber(3)); client.sadd('foo', [1, 2, 3], helper.isNumber(3))
client.scard('foo', helper.isNumber(3, done)); client.scard('foo', helper.isNumber(3, done))
}); })
afterEach(function () { afterEach(function () {
client.end(true); client.end(true)
}); })
}); })
}); })
}); })

View File

@@ -1,55 +1,44 @@
'use strict'; 'use strict'
var assert = require('assert'); var config = require('../lib/config')
var config = require('../lib/config'); var crypto = require('crypto')
var crypto = require('crypto'); var helper = require('../helper')
var helper = require('../helper'); var redis = config.redis
var redis = config.redis;
describe("The 'script' method", function () {
describe('The \'script\' method', function () {
helper.allTests(function (ip, args) { helper.allTests(function (ip, args) {
var command = 'return 99'; var command = 'return 99'
var commandSha = crypto.createHash('sha1').update(command).digest('hex'); var commandSha = crypto.createHash('sha1').update(command).digest('hex')
describe('using ' + ip, function () { describe('using ' + ip, function () {
var client; var client
beforeEach(function (done) { beforeEach(function (done) {
client = redis.createClient.apply(null, args); client = redis.createClient.apply(null, args)
client.once('ready', function () { client.once('ready', function () {
client.flushdb(done); client.flushdb(done)
}); })
}); })
afterEach(function () { afterEach(function () {
client.end(true); client.end(true)
}); })
it("loads script with client.script('load')", function (done) { it('loads script with client.script(\'load\')', function (done) {
client.script('load', command, function (err, result) { client.script('load', command, helper.isString(commandSha, done))
assert.strictEqual(result, commandSha); })
return done();
});
});
it('allows a loaded script to be evaluated', function (done) { it('allows a loaded script to be evaluated', function (done) {
client.evalsha(commandSha, 0, helper.isNumber(99, done)); client.evalsha(commandSha, 0, helper.isNumber(99, done))
}); })
it('allows a script to be loaded as part of a chained transaction', function (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) { client.multi().script('load', command).exec(helper.isDeepEqual([commandSha], done))
assert.strictEqual(result[0], commandSha); })
return done();
});
});
it("allows a script to be loaded using a transaction's array syntax", function (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) { client.multi([['script', 'load', command]]).exec(helper.isDeepEqual([commandSha], done))
assert.strictEqual(result[0], commandSha); })
return done(); })
}); })
}); })
});
});
});

View File

@@ -1,47 +1,45 @@
'use strict'; 'use strict'
var assert = require('assert'); var assert = require('assert')
var config = require('../lib/config'); var config = require('../lib/config')
var helper = require('../helper'); var helper = require('../helper')
var redis = config.redis; var redis = config.redis
describe("The 'sdiff' method", function () {
describe('The \'sdiff\' method', function () {
helper.allTests(function (ip, args) { helper.allTests(function (ip, args) {
describe('using ' + ip, function () { describe('using ' + ip, function () {
var client; var client
beforeEach(function (done) { beforeEach(function (done) {
client = redis.createClient.apply(null, args); client = redis.createClient.apply(null, args)
client.once('ready', function () { client.once('ready', function () {
client.flushdb(done); client.flushdb(done)
}); })
}); })
it('returns set difference', function (done) { it('returns set difference', function (done) {
client.sadd('foo', 'x', helper.isNumber(1)); client.sadd('foo', 'x', helper.isNumber(1))
client.sadd('foo', ['a'], helper.isNumber(1)); client.sadd('foo', ['a'], helper.isNumber(1))
client.sadd('foo', 'b', helper.isNumber(1)); client.sadd('foo', 'b', helper.isNumber(1))
client.sadd(['foo', 'c'], helper.isNumber(1)); client.sadd(['foo', 'c'], helper.isNumber(1))
client.sadd(['bar', 'c', helper.isNumber(1)]); client.sadd(['bar', 'c', helper.isNumber(1)])
client.sadd('baz', 'a', helper.isNumber(1)); client.sadd('baz', 'a', helper.isNumber(1))
client.sadd('baz', 'd', helper.isNumber(1)); client.sadd('baz', 'd', helper.isNumber(1))
client.sdiff('foo', 'bar', 'baz', function (err, values) { client.sdiff('foo', 'bar', 'baz', function (err, values) {
values.sort(); values.sort()
assert.equal(values.length, 2); assert.strictEqual(values.length, 2)
assert.equal(values[0], 'b'); assert.strictEqual(values[0], 'b')
assert.equal(values[1], 'x'); assert.strictEqual(values[1], 'x')
return done(err); return done(err)
}); })
}); })
afterEach(function () { afterEach(function () {
client.end(true); client.end(true)
}); })
}); })
}); })
}); })

View File

@@ -1,47 +1,45 @@
'use strict'; 'use strict'
var assert = require('assert'); var assert = require('assert')
var config = require('../lib/config'); var config = require('../lib/config')
var helper = require('../helper'); var helper = require('../helper')
var redis = config.redis; var redis = config.redis
describe("The 'sdiffstore' method", function () {
describe('The \'sdiffstore\' method', function () {
helper.allTests(function (ip, args) { helper.allTests(function (ip, args) {
describe('using ' + ip, function () { describe('using ' + ip, function () {
var client; var client
beforeEach(function (done) { beforeEach(function (done) {
client = redis.createClient.apply(null, args); client = redis.createClient.apply(null, args)
client.once('ready', function () { client.once('ready', function () {
client.flushdb(done); client.flushdb(done)
}); })
}); })
it('calculates set difference ands stores it in a key', function (done) { it('calculates set difference ands stores it in a key', function (done) {
client.sadd('foo', 'x', helper.isNumber(1)); client.sadd('foo', 'x', helper.isNumber(1))
client.sadd('foo', 'a', helper.isNumber(1)); client.sadd('foo', 'a', helper.isNumber(1))
client.sadd('foo', 'b', helper.isNumber(1)); client.sadd('foo', 'b', helper.isNumber(1))
client.sadd('foo', 'c', helper.isNumber(1)); client.sadd('foo', 'c', helper.isNumber(1))
client.sadd('bar', 'c', helper.isNumber(1)); client.sadd('bar', 'c', helper.isNumber(1))
client.sadd('baz', 'a', helper.isNumber(1)); client.sadd('baz', 'a', helper.isNumber(1))
client.sadd('baz', 'd', helper.isNumber(1)); client.sadd('baz', 'd', helper.isNumber(1))
client.sdiffstore('quux', 'foo', 'bar', 'baz', helper.isNumber(2)); client.sdiffstore('quux', 'foo', 'bar', 'baz', helper.isNumber(2))
client.smembers('quux', function (err, values) { client.smembers('quux', function (err, values) {
var members = values.sort(); var members = values.sort()
assert.deepEqual(members, [ 'b', 'x' ]); assert.deepEqual(members, [ 'b', 'x' ])
return done(err); return done(err)
}); })
}); })
afterEach(function () { afterEach(function () {
client.end(true); client.end(true)
}); })
}); })
}); })
}); })

View File

@@ -1,126 +1,124 @@
'use strict'; 'use strict'
var assert = require('assert'); var assert = require('assert')
var config = require('../lib/config'); var config = require('../lib/config')
var helper = require('../helper'); var helper = require('../helper')
var redis = config.redis; var redis = config.redis
describe("The 'select' method", function () {
describe('The \'select\' method', function () {
helper.allTests(function (ip, args) { helper.allTests(function (ip, args) {
describe('using ' + ip, function () { describe('using ' + ip, function () {
describe('when not connected', function () { describe('when not connected', function () {
var client; var client
beforeEach(function (done) { beforeEach(function (done) {
client = redis.createClient.apply(null, args); client = redis.createClient.apply(null, args)
client.once('ready', function () { client.once('ready', function () {
client.quit(); client.quit()
}); })
client.on('end', done); client.on('end', done)
}); })
it('returns an error if redis is not connected', function (done) { it('returns an error if redis is not connected', function (done) {
var buffering = client.select(1, function (err, res) { var buffering = client.select(1, function (err, res) {
assert(err.message.match(/The connection is already closed/)); assert(err.message.match(/The connection is already closed/))
done(); done()
}); })
assert(typeof buffering === 'boolean'); assert(typeof buffering === 'boolean')
}); })
}); })
describe('when connected', function () { describe('when connected', function () {
var client; var client
beforeEach(function (done) { beforeEach(function (done) {
client = redis.createClient.apply(null, args); client = redis.createClient.apply(null, args)
client.once('ready', function () { client.once('ready', function () {
client.flushdb(done); client.flushdb(done)
}); })
}); })
afterEach(function () { afterEach(function () {
client.end(true); client.end(true)
}); })
it('changes the database and calls the callback', function (done) { it('changes the database and calls the callback', function (done) {
// default value of null means database 0 will be used. // default value of null means database 0 will be used.
assert.strictEqual(client.selectedDb, undefined, 'default db should be undefined'); assert.strictEqual(client.selectedDb, undefined, 'default db should be undefined')
var buffering = client.select(1, function (err, res) { var buffering = client.select(1, function (err, res) {
helper.isNotError()(err, res); helper.isNotError()(err, res)
assert.strictEqual(client.selectedDb, 1, 'db should be 1 after select'); assert.strictEqual(client.selectedDb, 1, 'db should be 1 after select')
done(); done()
}); })
assert(typeof buffering === 'boolean'); assert(typeof buffering === 'boolean')
}); })
describe('and a callback is specified', function () { describe('and a callback is specified', function () {
describe('with a valid db index', function () { describe('with a valid db index', function () {
it('selects the appropriate database', function (done) { it('selects the appropriate database', function (done) {
assert.strictEqual(client.selectedDb, undefined, 'default db should be undefined'); assert.strictEqual(client.selectedDb, undefined, 'default db should be undefined')
client.select(1, function (err) { client.select(1, function (err) {
assert.equal(err, null); assert.strictEqual(err, null)
assert.equal(client.selectedDb, 1, 'we should have selected the new valid DB'); assert.strictEqual(client.selectedDb, 1, 'we should have selected the new valid DB')
done(); done()
}); })
}); })
}); })
describe('with an invalid db index', function () { describe('with an invalid db index', function () {
it('returns an error', function (done) { it('returns an error', function (done) {
assert.strictEqual(client.selectedDb, undefined, 'default db should be undefined'); assert.strictEqual(client.selectedDb, undefined, 'default db should be undefined')
client.select(9999, function (err) { client.select(9999, function (err) {
assert.equal(err.code, 'ERR'); assert.strictEqual(err.code, 'ERR')
assert.equal(err.message, 'ERR invalid DB index'); assert.strictEqual(err.message, 'ERR invalid DB index')
done(); done()
}); })
}); })
}); })
}); })
describe('and no callback is specified', function () { describe('and no callback is specified', function () {
describe('with a valid db index', function () { describe('with a valid db index', function () {
it('selects the appropriate database', function (done) { it('selects the appropriate database', function (done) {
assert.strictEqual(client.selectedDb, undefined, 'default db should be undefined'); assert.strictEqual(client.selectedDb, undefined, 'default db should be undefined')
client.select(1); client.select(1)
setTimeout(function () { setTimeout(function () {
assert.equal(client.selectedDb, 1, 'we should have selected the new valid DB'); assert.strictEqual(client.selectedDb, 1, 'we should have selected the new valid DB')
done(); done()
}, 25); }, 25)
}); })
}); })
describe('with an invalid db index', function () { describe('with an invalid db index', function () {
it('emits an error when callback not provided', function (done) { it('emits an error when callback not provided', function (done) {
assert.strictEqual(client.selectedDb, undefined, 'default db should be undefined'); assert.strictEqual(client.selectedDb, undefined, 'default db should be undefined')
client.on('error', function (err) { client.on('error', function (err) {
assert.strictEqual(err.command, 'SELECT'); assert.strictEqual(err.command, 'SELECT')
assert.equal(err.message, 'ERR invalid DB index'); assert.strictEqual(err.message, 'ERR invalid DB index')
done(); done()
}); })
client.select(9999); client.select(9999)
}); })
}); })
}); })
describe('reconnection occurs', function () { describe('reconnection occurs', function () {
it('selects the appropriate database after a reconnect', function (done) { it('selects the appropriate database after a reconnect', function (done) {
assert.strictEqual(client.selectedDb, undefined, 'default db should be undefined'); assert.strictEqual(client.selectedDb, undefined, 'default db should be undefined')
client.select(3); client.select(3)
client.set('foo', 'bar', function () { client.set('foo', 'bar', function () {
client.stream.destroy(); client.stream.destroy()
}); })
client.once('ready', function () { client.once('ready', function () {
assert.strictEqual(client.selectedDb, 3); assert.strictEqual(client.selectedDb, 3)
assert(typeof client.serverInfo.db3 === 'object'); assert(typeof client.serverInfo.db3 === 'object')
done(); done()
}); })
}); })
}); })
}); })
}); })
}); })
}); })

View File

@@ -1,170 +1,168 @@
'use strict'; 'use strict'
var assert = require('assert'); var assert = require('assert')
var config = require('../lib/config'); var config = require('../lib/config')
var helper = require('../helper'); var helper = require('../helper')
var redis = config.redis; var redis = config.redis
var uuid = require('uuid'); var uuid = require('uuid')
describe("The 'set' method", function () {
describe('The \'set\' method', function () {
helper.allTests(function (ip, args) { helper.allTests(function (ip, args) {
describe('using ' + ip, function () { describe('using ' + ip, function () {
var key, value; var key, value
beforeEach(function () { beforeEach(function () {
key = uuid.v4(); key = uuid.v4()
value = uuid.v4(); value = uuid.v4()
}); })
describe('when not connected', function () { describe('when not connected', function () {
var client; var client
beforeEach(function (done) { beforeEach(function (done) {
client = redis.createClient.apply(null, args); client = redis.createClient.apply(null, args)
client.once('ready', function () { client.once('ready', function () {
client.quit(); client.quit()
}); })
client.on('end', done); client.on('end', done)
}); })
it('reports an error', function (done) { it('reports an error', function (done) {
client.set(key, value, function (err, res) { client.set(key, value, function (err, res) {
assert(err.message.match(/The connection is already closed/)); assert(err.message.match(/The connection is already closed/))
done(); done()
}); })
}); })
}); })
describe('when connected', function () { describe('when connected', function () {
var client; var client
beforeEach(function (done) { beforeEach(function (done) {
client = redis.createClient.apply(null, args); client = redis.createClient.apply(null, args)
client.once('ready', function () { client.once('ready', function () {
client.flushdb(done); client.flushdb(done)
}); })
}); })
afterEach(function () { afterEach(function () {
client.end(true); client.end(true)
}); })
describe('and a callback is specified', function () { describe('and a callback is specified', function () {
describe('with valid parameters', function () { describe('with valid parameters', function () {
it('sets the value correctly', function (done) { it('sets the value correctly', function (done) {
client.set(key, value, function (err, res) { client.set(key, value, function (err, res) {
helper.isNotError()(err, res); helper.isNotError()(err, res)
client.get(key, function (err, res) { client.get(key, function (err, res) {
helper.isString(value)(err, res); helper.isString(value)(err, res)
done(); done()
}); })
}); })
}); })
it('set expire date in seconds', function (done) { it('set expire date in seconds', function (done) {
client.set('foo', 'bar', 'ex', 10, helper.isString('OK')); client.set('foo', 'bar', 'ex', 10, helper.isString('OK'))
client.pttl('foo', function (err, res) { client.pttl('foo', function (err, res) {
assert(res >= 10000 - 50); // Max 50 ms should have passed assert(res >= 10000 - 50) // Max 50 ms should have passed
assert(res <= 10000); // Max possible should be 10.000 assert(res <= 10000) // Max possible should be 10.000
done(err); done(err)
}); })
}); })
it('set expire date in milliseconds', function (done) { it('set expire date in milliseconds', function (done) {
client.set('foo', 'bar', 'px', 100, helper.isString('OK')); client.set('foo', 'bar', 'px', 100, helper.isString('OK'))
client.pttl('foo', function (err, res) { client.pttl('foo', function (err, res) {
assert(res >= 50); // Max 50 ms should have passed assert(res >= 50) // Max 50 ms should have passed
assert(res <= 100); // Max possible should be 100 assert(res <= 100) // Max possible should be 100
done(err); done(err)
}); })
}); })
it('only set the key if (not) already set', function (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.isString('OK'))
client.set('foo', 'bar', 'nx', helper.isNull()); client.set('foo', 'bar', 'nx', helper.isNull())
client.set('foo', 'bar', 'EX', '10', 'XX', helper.isString('OK')); client.set('foo', 'bar', 'EX', '10', 'XX', helper.isString('OK'))
client.ttl('foo', function (err, res) { client.ttl('foo', function (err, res) {
assert(res >= 9); // Min 9s should be left assert(res >= 9) // Min 9s should be left
assert(res <= 10); // Max 10s should be left assert(res <= 10) // Max 10s should be left
done(err); done(err)
}); })
}); })
}); })
describe('reports an error with invalid parameters', function () { describe('reports an error with invalid parameters', function () {
it("undefined 'key' and missing 'value' parameter", function (done) { it('undefined \'key\' and missing \'value\' parameter', function (done) {
client.set(undefined, function (err, res) { client.set(undefined, function (err, res) {
helper.isError()(err, null); helper.isError()(err, null)
assert.equal(err.command, 'SET'); assert.strictEqual(err.command, 'SET')
done(); done()
}); })
}); })
it('empty array as second parameter', function (done) { it('empty array as second parameter', function (done) {
client.set('foo', [], function (err, res) { client.set('foo', [], function (err, res) {
assert.strictEqual(err.message, "ERR wrong number of arguments for 'set' command"); assert.strictEqual(err.message, 'ERR wrong number of arguments for \'set\' command')
done(); done()
}); })
}); })
}); })
}); })
describe('and no callback is specified', function () { describe('and no callback is specified', function () {
describe('with valid parameters', function () { describe('with valid parameters', function () {
it('sets the value correctly', function (done) { it('sets the value correctly', function (done) {
client.set(key, value); client.set(key, value)
client.get(key, helper.isString(value, done)); client.get(key, helper.isString(value, done))
}); })
it('sets the value correctly even if the callback is explicitly set to undefined', function (done) { it('sets the value correctly even if the callback is explicitly set to undefined', function (done) {
client.set(key, value, undefined); client.set(key, value, undefined)
client.get(key, helper.isString(value, done)); client.get(key, helper.isString(value, done))
}); })
it('sets the value correctly with the array syntax', function (done) { it('sets the value correctly with the array syntax', function (done) {
client.set([key, value]); client.set([key, value])
client.get(key, helper.isString(value, done)); client.get(key, helper.isString(value, done))
}); })
}); })
describe("with undefined 'key' and missing 'value' parameter", function () { describe('with undefined \'key\' and missing \'value\' parameter', function () {
it('emits an error without callback', function (done) { it('emits an error without callback', function (done) {
client.on('error', function (err) { client.on('error', function (err) {
assert.equal(err.message, "ERR wrong number of arguments for 'set' command"); assert.strictEqual(err.message, 'ERR wrong number of arguments for \'set\' command')
assert.equal(err.command, 'SET'); assert.strictEqual(err.command, 'SET')
done(); done()
}); })
client.set(undefined); client.set(undefined)
}); })
}); })
// TODO: This test has to be refactored from v.3.0 on to expect an error instead // TODO: This test has to be refactored from v.3.0 on to expect an error instead
it("converts null to 'null'", function (done) { it('converts null to \'null\'', function (done) {
client.set('foo', null); client.set('foo', null)
client.get('foo', helper.isString('null', done)); client.get('foo', helper.isString('null', done))
}); })
it('emit an error with only the key set', function (done) { it('emit an error with only the key set', function (done) {
client.on('error', function (err) { client.on('error', function (err) {
assert.equal(err.message, "ERR wrong number of arguments for 'set' command"); assert.strictEqual(err.message, 'ERR wrong number of arguments for \'set\' command')
done(); done()
}); })
client.set('foo'); client.set('foo')
}); })
it('emit an error without any parameters', function (done) { it('emit an error without any parameters', function (done) {
client.once('error', function (err) { client.once('error', function (err) {
assert.equal(err.message, "ERR wrong number of arguments for 'set' command"); assert.strictEqual(err.message, 'ERR wrong number of arguments for \'set\' command')
assert.equal(err.command, 'SET'); assert.strictEqual(err.command, 'SET')
done(); done()
}); })
client.set(); client.set()
}); })
}); })
}); })
}); })
}); })
}); })

View File

@@ -1,37 +1,36 @@
'use strict'; 'use strict'
var assert = require('assert'); var assert = require('assert')
var config = require('../lib/config'); var config = require('../lib/config')
var helper = require('../helper'); var helper = require('../helper')
var redis = config.redis; var redis = config.redis
describe("The 'setex' method", function () {
describe('The \'setex\' method', function () {
helper.allTests(function (ip, args) { helper.allTests(function (ip, args) {
describe('using ' + ip, function () { describe('using ' + ip, function () {
var client; var client
beforeEach(function (done) { beforeEach(function (done) {
client = redis.createClient.apply(null, args); client = redis.createClient.apply(null, args)
client.once('ready', function () { client.once('ready', function () {
client.flushdb(done); client.flushdb(done)
}); })
}); })
it('sets a key with an expiry', function (done) { it('sets a key with an expiry', function (done) {
client.setex(['setex key', '100', 'setex val'], helper.isString('OK')); client.setex(['setex key', '100', 'setex val'], helper.isString('OK'))
var buffering = client.exists(['setex key'], helper.isNumber(1)); var buffering = client.exists(['setex key'], helper.isNumber(1))
assert(typeof buffering === 'boolean'); assert(typeof buffering === 'boolean')
client.ttl(['setex key'], function (err, ttl) { client.ttl(['setex key'], function (err, ttl) {
assert(ttl > 0); assert.strictEqual(err, null)
return done(); assert(ttl > 0)
}); return done()
}); })
})
afterEach(function () { afterEach(function () {
client.end(true); client.end(true)
}); })
}); })
}); })
}); })

View File

@@ -1,37 +1,35 @@
'use strict'; 'use strict'
var config = require('../lib/config'); var config = require('../lib/config')
var helper = require('../helper'); var helper = require('../helper')
var redis = config.redis; var redis = config.redis
describe("The 'setnx' method", function () {
describe('The \'setnx\' method', function () {
helper.allTests(function (ip, args) { helper.allTests(function (ip, args) {
describe('using ' + ip, function () { describe('using ' + ip, function () {
var client; var client
beforeEach(function (done) { beforeEach(function (done) {
client = redis.createClient.apply(null, args); client = redis.createClient.apply(null, args)
client.once('ready', function () { client.once('ready', function () {
client.flushdb(done); client.flushdb(done)
}); })
}); })
it('sets key if it does not have a value', function (done) { it('sets key if it does not have a value', function (done) {
client.setnx('foo', 'banana', helper.isNumber(1)); client.setnx('foo', 'banana', helper.isNumber(1))
client.get('foo', helper.isString('banana', done)); client.get('foo', helper.isString('banana', done))
}); })
it('does not set key if it already has a value', function (done) { it('does not set key if it already has a value', function (done) {
client.set('foo', 'bar', helper.isString('OK')); client.set('foo', 'bar', helper.isString('OK'))
client.setnx('foo', 'banana', helper.isNumber(0)); client.setnx('foo', 'banana', helper.isNumber(0))
client.get('foo', helper.isString('bar', done)); client.get('foo', helper.isString('bar', done))
}); })
afterEach(function () { afterEach(function () {
client.end(true); client.end(true)
}); })
}); })
}); })
}); })

View File

@@ -1,63 +1,61 @@
'use strict'; 'use strict'
var assert = require('assert'); var assert = require('assert')
var config = require('../lib/config'); var config = require('../lib/config')
var helper = require('../helper'); var helper = require('../helper')
var redis = config.redis; var redis = config.redis
describe("The 'sinter' method", function () {
describe('The \'sinter\' method', function () {
helper.allTests(function (ip, args) { helper.allTests(function (ip, args) {
describe('using ' + ip, function () { describe('using ' + ip, function () {
var client; var client
beforeEach(function (done) { beforeEach(function (done) {
client = redis.createClient.apply(null, args); client = redis.createClient.apply(null, args)
client.once('ready', function () { client.once('ready', function () {
client.flushdb(done); client.flushdb(done)
}); })
}); })
it('handles two sets being intersected', function (done) { it('handles two sets being intersected', function (done) {
client.sadd('sa', 'a', helper.isNumber(1)); client.sadd('sa', 'a', helper.isNumber(1))
client.sadd('sa', 'b', helper.isNumber(1)); client.sadd('sa', 'b', helper.isNumber(1))
client.sadd('sa', 'c', helper.isNumber(1)); client.sadd('sa', 'c', helper.isNumber(1))
client.sadd('sb', 'b', helper.isNumber(1)); client.sadd('sb', 'b', helper.isNumber(1))
client.sadd('sb', 'c', helper.isNumber(1)); client.sadd('sb', 'c', helper.isNumber(1))
client.sadd('sb', 'd', helper.isNumber(1)); client.sadd('sb', 'd', helper.isNumber(1))
client.sinter('sa', 'sb', function (err, intersection) { client.sinter('sa', 'sb', function (err, intersection) {
assert.equal(intersection.length, 2); assert.strictEqual(intersection.length, 2)
assert.deepEqual(intersection.sort(), [ 'b', 'c' ]); assert.deepEqual(intersection.sort(), [ 'b', 'c' ])
return done(err); return done(err)
}); })
}); })
it('handles three sets being intersected', function (done) { it('handles three sets being intersected', function (done) {
client.sadd('sa', 'a', helper.isNumber(1)); client.sadd('sa', 'a', helper.isNumber(1))
client.sadd('sa', 'b', helper.isNumber(1)); client.sadd('sa', 'b', helper.isNumber(1))
client.sadd('sa', 'c', helper.isNumber(1)); client.sadd('sa', 'c', helper.isNumber(1))
client.sadd('sb', 'b', helper.isNumber(1)); client.sadd('sb', 'b', helper.isNumber(1))
client.sadd('sb', 'c', helper.isNumber(1)); client.sadd('sb', 'c', helper.isNumber(1))
client.sadd('sb', 'd', helper.isNumber(1)); client.sadd('sb', 'd', helper.isNumber(1))
client.sadd('sc', 'c', helper.isNumber(1)); client.sadd('sc', 'c', helper.isNumber(1))
client.sadd('sc', 'd', helper.isNumber(1)); client.sadd('sc', 'd', helper.isNumber(1))
client.sadd('sc', 'e', helper.isNumber(1)); client.sadd('sc', 'e', helper.isNumber(1))
client.sinter('sa', 'sb', 'sc', function (err, intersection) { client.sinter('sa', 'sb', 'sc', function (err, intersection) {
assert.equal(intersection.length, 1); assert.strictEqual(intersection.length, 1)
assert.equal(intersection[0], 'c'); assert.strictEqual(intersection[0], 'c')
return done(err); return done(err)
}); })
}); })
afterEach(function () { afterEach(function () {
client.end(true); client.end(true)
}); })
}); })
}); })
}); })

View File

@@ -1,48 +1,46 @@
'use strict'; 'use strict'
var assert = require('assert'); var assert = require('assert')
var config = require('../lib/config'); var config = require('../lib/config')
var helper = require('../helper'); var helper = require('../helper')
var redis = config.redis; var redis = config.redis
describe("The 'sinterstore' method", function () {
describe('The \'sinterstore\' method', function () {
helper.allTests(function (ip, args) { helper.allTests(function (ip, args) {
describe('using ' + ip, function () { describe('using ' + ip, function () {
var client; var client
beforeEach(function (done) { beforeEach(function (done) {
client = redis.createClient.apply(null, args); client = redis.createClient.apply(null, args)
client.once('ready', function () { client.once('ready', function () {
client.flushdb(done); client.flushdb(done)
}); })
}); })
it('calculates set intersection and stores it in a key', function (done) { it('calculates set intersection and stores it in a key', function (done) {
client.sadd('sa', 'a', helper.isNumber(1)); client.sadd('sa', 'a', helper.isNumber(1))
client.sadd('sa', 'b', helper.isNumber(1)); client.sadd('sa', 'b', helper.isNumber(1))
client.sadd('sa', 'c', helper.isNumber(1)); client.sadd('sa', 'c', helper.isNumber(1))
client.sadd('sb', 'b', helper.isNumber(1)); client.sadd('sb', 'b', helper.isNumber(1))
client.sadd('sb', 'c', helper.isNumber(1)); client.sadd('sb', 'c', helper.isNumber(1))
client.sadd('sb', 'd', helper.isNumber(1)); client.sadd('sb', 'd', helper.isNumber(1))
client.sadd('sc', 'c', helper.isNumber(1)); client.sadd('sc', 'c', helper.isNumber(1))
client.sadd('sc', 'd', helper.isNumber(1)); client.sadd('sc', 'd', helper.isNumber(1))
client.sadd('sc', 'e', helper.isNumber(1)); client.sadd('sc', 'e', helper.isNumber(1))
client.sinterstore('foo', 'sa', 'sb', 'sc', helper.isNumber(1)); client.sinterstore('foo', 'sa', 'sb', 'sc', helper.isNumber(1))
client.smembers('foo', function (err, members) { client.smembers('foo', function (err, members) {
assert.deepEqual(members, [ 'c' ]); assert.deepEqual(members, [ 'c' ])
return done(err); return done(err)
}); })
}); })
afterEach(function () { afterEach(function () {
client.end(true); client.end(true)
}); })
}); })
}); })
}); })

View File

@@ -1,35 +1,33 @@
'use strict'; 'use strict'
var config = require('../lib/config'); var config = require('../lib/config')
var helper = require('../helper'); var helper = require('../helper')
var redis = config.redis; var redis = config.redis
describe("The 'sismember' method", function () {
describe('The \'sismember\' method', function () {
helper.allTests(function (ip, args) { helper.allTests(function (ip, args) {
describe('using ' + ip, function () { describe('using ' + ip, function () {
var client; var client
beforeEach(function (done) { beforeEach(function (done) {
client = redis.createClient.apply(null, args); client = redis.createClient.apply(null, args)
client.once('ready', function () { client.once('ready', function () {
client.flushdb(done); client.flushdb(done)
}); })
}); })
it('returns 0 if the value is not in the set', function (done) { it('returns 0 if the value is not in the set', function (done) {
client.sismember('foo', 'banana', helper.isNumber(0, done)); client.sismember('foo', 'banana', helper.isNumber(0, done))
}); })
it('returns 1 if the value is in the set', function (done) { it('returns 1 if the value is in the set', function (done) {
client.sadd('foo', 'banana', helper.isNumber(1)); client.sadd('foo', 'banana', helper.isNumber(1))
client.sismember('foo', 'banana', helper.isNumber(1, done)); client.sismember('foo', 'banana', helper.isNumber(1, done))
}); })
afterEach(function () { afterEach(function () {
client.end(true); client.end(true)
}); })
}); })
}); })
}); })

View File

@@ -1,41 +1,39 @@
'use strict'; 'use strict'
var assert = require('assert'); var assert = require('assert')
var config = require('../lib/config'); var config = require('../lib/config')
var helper = require('../helper'); var helper = require('../helper')
var redis = config.redis; var redis = config.redis
describe("The 'slowlog' method", function () {
describe('The \'slowlog\' method', function () {
helper.allTests(function (ip, args) { helper.allTests(function (ip, args) {
describe('using ' + ip, function () { describe('using ' + ip, function () {
var client; var client
beforeEach(function (done) { beforeEach(function (done) {
client = redis.createClient.apply(null, args); client = redis.createClient.apply(null, args)
client.once('ready', function () { client.once('ready', function () {
client.flushdb(done); client.flushdb(done)
}); })
}); })
it('logs operations in slowlog', function (done) { it('logs operations in slowlog', function (done) {
client.config('set', 'slowlog-log-slower-than', 0, helper.isString('OK')); client.config('set', 'slowlog-log-slower-than', 0, helper.isString('OK'))
client.slowlog('reset', helper.isString('OK')); client.slowlog('reset', helper.isString('OK'))
client.set('foo', 'bar', helper.isString('OK')); client.set('foo', 'bar', helper.isString('OK'))
client.get('foo', helper.isString('bar')); client.get('foo', helper.isString('bar'))
client.slowlog('get', function (err, res) { client.slowlog('get', function (err, res) {
assert.equal(res.length, 3); assert.strictEqual(res.length, 3)
assert.equal(res[0][3].length, 2); assert.strictEqual(res[0][3].length, 2)
assert.deepEqual(res[1][3], ['set', 'foo', 'bar']); assert.deepEqual(res[1][3], ['set', 'foo', 'bar'])
assert.deepEqual(res[2][3], ['slowlog', 'reset']); assert.deepEqual(res[2][3], ['slowlog', 'reset'])
return done(err); return done(err)
}); })
}); })
afterEach(function () { afterEach(function () {
client.end(true); client.end(true)
}); })
}); })
}); })
}); })

View File

@@ -1,38 +1,36 @@
'use strict'; 'use strict'
var assert = require('assert'); var assert = require('assert')
var config = require('../lib/config'); var config = require('../lib/config')
var helper = require('../helper'); var helper = require('../helper')
var redis = config.redis; var redis = config.redis
describe("The 'smembers' method", function () {
describe('The \'smembers\' method', function () {
helper.allTests(function (ip, args) { helper.allTests(function (ip, args) {
describe('using ' + ip, function () { describe('using ' + ip, function () {
var client; var client
beforeEach(function (done) { beforeEach(function (done) {
client = redis.createClient.apply(null, args); client = redis.createClient.apply(null, args)
client.once('ready', function () { client.once('ready', function () {
client.flushdb(done); client.flushdb(done)
}); })
}); })
it('returns all values in a set', function (done) { it('returns all values in a set', function (done) {
client.sadd('foo', 'x', helper.isNumber(1)); client.sadd('foo', 'x', helper.isNumber(1))
client.sadd('foo', 'y', helper.isNumber(1)); client.sadd('foo', 'y', helper.isNumber(1))
client.smembers('foo', function (err, values) { client.smembers('foo', function (err, values) {
assert.equal(values.length, 2); assert.strictEqual(values.length, 2)
var members = values.sort(); var members = values.sort()
assert.deepEqual(members, [ 'x', 'y' ]); assert.deepEqual(members, [ 'x', 'y' ])
return done(err); return done(err)
}); })
}); })
afterEach(function () { afterEach(function () {
client.end(true); client.end(true)
}); })
}); })
}); })
}); })

View File

@@ -1,40 +1,38 @@
'use strict'; 'use strict'
var config = require('../lib/config'); var config = require('../lib/config')
var helper = require('../helper'); var helper = require('../helper')
var redis = config.redis; var redis = config.redis
describe("The 'smove' method", function () {
describe('The \'smove\' method', function () {
helper.allTests(function (ip, args) { helper.allTests(function (ip, args) {
describe('using ' + ip, function () { describe('using ' + ip, function () {
var client; var client
beforeEach(function (done) { beforeEach(function (done) {
client = redis.createClient.apply(null, args); client = redis.createClient.apply(null, args)
client.once('ready', function () { client.once('ready', function () {
client.flushdb(done); client.flushdb(done)
}); })
}); })
it('moves a value to a set that does not yet exist', function (done) { it('moves a value to a set that does not yet exist', function (done) {
client.sadd('foo', 'x', helper.isNumber(1)); client.sadd('foo', 'x', helper.isNumber(1))
client.smove('foo', 'bar', 'x', helper.isNumber(1)); client.smove('foo', 'bar', 'x', helper.isNumber(1))
client.sismember('foo', 'x', helper.isNumber(0)); client.sismember('foo', 'x', helper.isNumber(0))
client.sismember('bar', 'x', helper.isNumber(1, done)); 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) { 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.sadd('foo', 'x', helper.isNumber(1))
client.smove('foo', 'bar', 'y', helper.isNumber(0)); client.smove('foo', 'bar', 'y', helper.isNumber(0))
client.sismember('foo', 'y', helper.isNumber(0)); client.sismember('foo', 'y', helper.isNumber(0))
client.sismember('bar', 'y', helper.isNumber(0, done)); client.sismember('bar', 'y', helper.isNumber(0, done))
}); })
afterEach(function () { afterEach(function () {
client.end(true); client.end(true)
}); })
}); })
}); })
}); })

View File

@@ -1,130 +1,127 @@
'use strict'; 'use strict'
var assert = require('assert'); var assert = require('assert')
var config = require('../lib/config'); var config = require('../lib/config')
var helper = require('../helper'); var helper = require('../helper')
var redis = config.redis; var redis = config.redis
function setupData (client, done) { function setupData (client, done) {
client.rpush('y', 'd'); client.rpush('y', 'd')
client.rpush('y', 'b'); client.rpush('y', 'b')
client.rpush('y', 'a'); client.rpush('y', 'a')
client.rpush('y', 'c'); client.rpush('y', 'c')
client.rpush('x', '3'); client.rpush('x', '3')
client.rpush('x', '9'); client.rpush('x', '9')
client.rpush('x', '2'); client.rpush('x', '2')
client.rpush('x', '4'); client.rpush('x', '4')
client.set('w3', '4'); client.set('w3', '4')
client.set('w9', '5'); client.set('w9', '5')
client.set('w2', '12'); client.set('w2', '12')
client.set('w4', '6'); client.set('w4', '6')
client.set('o2', 'buz'); client.set('o2', 'buz')
client.set('o3', 'foo'); client.set('o3', 'foo')
client.set('o4', 'baz'); client.set('o4', 'baz')
client.set('o9', 'bar'); client.set('o9', 'bar')
client.set('p2', 'qux'); client.set('p2', 'qux')
client.set('p3', 'bux'); client.set('p3', 'bux')
client.set('p4', 'lux'); client.set('p4', 'lux')
client.set('p9', 'tux', done); client.set('p9', 'tux', done)
} }
describe("The 'sort' method", function () { describe('The \'sort\' method', function () {
helper.allTests(function (ip, args) { helper.allTests(function (ip, args) {
describe('using ' + ip, function () { describe('using ' + ip, function () {
var client; var client
beforeEach(function (done) { beforeEach(function (done) {
client = redis.createClient.apply(null, args); client = redis.createClient.apply(null, args)
client.once('error', done); client.once('error', done)
client.once('connect', function () { client.once('connect', function () {
client.flushdb(); client.flushdb()
setupData(client, done); setupData(client, done)
}); })
}); })
describe('alphabetical', function () { describe('alphabetical', function () {
it('sorts in ascending alphabetical order', function (done) { it('sorts in ascending alphabetical order', function (done) {
client.sort('y', 'asc', 'alpha', function (err, sorted) { client.sort('y', 'asc', 'alpha', function (err, sorted) {
assert.deepEqual(sorted, ['a', 'b', 'c', 'd']); assert.deepEqual(sorted, ['a', 'b', 'c', 'd'])
return done(err); return done(err)
}); })
}); })
it('sorts in descending alphabetical order', function (done) { it('sorts in descending alphabetical order', function (done) {
client.sort('y', 'desc', 'alpha', function (err, sorted) { client.sort('y', 'desc', 'alpha', function (err, sorted) {
assert.deepEqual(sorted, ['d', 'c', 'b', 'a']); assert.deepEqual(sorted, ['d', 'c', 'b', 'a'])
return done(err); return done(err)
}); })
}); })
}); })
describe('numeric', function () { describe('numeric', function () {
it('sorts in ascending numeric order', function (done) { it('sorts in ascending numeric order', function (done) {
client.sort('x', 'asc', function (err, sorted) { client.sort('x', 'asc', function (err, sorted) {
assert.deepEqual(sorted, [2, 3, 4, 9]); assert.deepEqual(sorted, [2, 3, 4, 9])
return done(err); return done(err)
}); })
}); })
it('sorts in descending numeric order', function (done) { it('sorts in descending numeric order', function (done) {
client.sort('x', 'desc', function (err, sorted) { client.sort('x', 'desc', function (err, sorted) {
assert.deepEqual(sorted, [9, 4, 3, 2]); assert.deepEqual(sorted, [9, 4, 3, 2])
return done(err); return done(err)
}); })
}); })
}); })
describe('pattern', function () { describe('pattern', function () {
it('handles sorting with a pattern', function (done) { it('handles sorting with a pattern', function (done) {
client.sort('x', 'by', 'w*', 'asc', function (err, sorted) { client.sort('x', 'by', 'w*', 'asc', function (err, sorted) {
assert.deepEqual(sorted, [3, 9, 4, 2]); assert.deepEqual(sorted, [3, 9, 4, 2])
return done(err); return done(err)
}); })
}); })
it("handles sorting with a 'by' pattern and 1 'get' pattern", function (done) { it('handles sorting with a \'by\' pattern and 1 \'get\' pattern', function (done) {
client.sort('x', 'by', 'w*', 'asc', 'get', 'o*', function (err, sorted) { client.sort('x', 'by', 'w*', 'asc', 'get', 'o*', function (err, sorted) {
assert.deepEqual(sorted, ['foo', 'bar', 'baz', 'buz']); assert.deepEqual(sorted, ['foo', 'bar', 'baz', 'buz'])
return done(err); return done(err)
}); })
}); })
it("handles sorting with a 'by' pattern and 2 'get' patterns", function (done) { 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) { client.sort('x', 'by', 'w*', 'asc', 'get', 'o*', 'get', 'p*', function (err, sorted) {
assert.deepEqual(sorted, ['foo', 'bux', 'bar', 'tux', 'baz', 'lux', 'buz', 'qux']); assert.deepEqual(sorted, ['foo', 'bux', 'bar', 'tux', 'baz', 'lux', 'buz', 'qux'])
return done(err); return done(err)
}); })
}); })
it("handles sorting with a 'by' pattern and 2 'get' patterns with the array syntax", function (done) { 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) { client.sort(['x', 'by', 'w*', 'asc', 'get', 'o*', 'get', 'p*'], function (err, sorted) {
assert.deepEqual(sorted, ['foo', 'bux', 'bar', 'tux', 'baz', 'lux', 'buz', 'qux']); assert.deepEqual(sorted, ['foo', 'bux', 'bar', 'tux', 'baz', 'lux', 'buz', 'qux'])
return done(err); return done(err)
}); })
}); })
it("sorting with a 'by' pattern and 2 'get' patterns and stores results", function (done) { 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) { client.sort('x', 'by', 'w*', 'asc', 'get', 'o*', 'get', 'p*', 'store', 'bacon', function (err) {
if (err) return done(err); if (err) return done(err)
}); })
client.lrange('bacon', 0, -1, function (err, values) { client.lrange('bacon', 0, -1, function (err, values) {
assert.deepEqual(values, ['foo', 'bux', 'bar', 'tux', 'baz', 'lux', 'buz', 'qux']); assert.deepEqual(values, ['foo', 'bux', 'bar', 'tux', 'baz', 'lux', 'buz', 'qux'])
return done(err); return done(err)
}); })
}); })
}); })
afterEach(function () { afterEach(function () {
client.end(true); client.end(true)
}); })
}); })
}); })
})
});

View File

@@ -1,38 +1,36 @@
'use strict'; 'use strict'
var assert = require('assert'); var assert = require('assert')
var config = require('../lib/config'); var config = require('../lib/config')
var helper = require('../helper'); var helper = require('../helper')
var redis = config.redis; var redis = config.redis
describe("The 'spop' method", function () {
describe('The \'spop\' method', function () {
helper.allTests(function (ip, args) { helper.allTests(function (ip, args) {
describe('using ' + ip, function () { describe('using ' + ip, function () {
var client; var client
beforeEach(function (done) { beforeEach(function (done) {
client = redis.createClient.apply(null, args); client = redis.createClient.apply(null, args)
client.once('ready', function () { client.once('ready', function () {
client.flushdb(done); client.flushdb(done)
}); })
}); })
it('returns a random element from the set', function (done) { it('returns a random element from the set', function (done) {
client.sadd('zzz', 'member0', helper.isNumber(1)); client.sadd('zzz', 'member0', helper.isNumber(1))
client.scard('zzz', helper.isNumber(1)); client.scard('zzz', helper.isNumber(1))
client.spop('zzz', function (err, value) { client.spop('zzz', function (err, value) {
if (err) return done(err); if (err) return done(err)
assert.equal(value, 'member0'); assert.strictEqual(value, 'member0')
client.scard('zzz', helper.isNumber(0, done)); client.scard('zzz', helper.isNumber(0, done))
}); })
}); })
afterEach(function () { afterEach(function () {
client.end(true); client.end(true)
}); })
}); })
}); })
}); })

View File

@@ -1,69 +1,67 @@
'use strict'; 'use strict'
var assert = require('assert'); var assert = require('assert')
var config = require('../lib/config'); var config = require('../lib/config')
var helper = require('../helper'); var helper = require('../helper')
var redis = config.redis; var redis = config.redis
describe("The 'srem' method", function () {
describe('The \'srem\' method', function () {
helper.allTests(function (ip, args) { helper.allTests(function (ip, args) {
describe('using ' + ip, function () { describe('using ' + ip, function () {
var client; var client
beforeEach(function (done) { beforeEach(function (done) {
client = redis.createClient.apply(null, args); client = redis.createClient.apply(null, args)
client.once('ready', function () { client.once('ready', function () {
client.flushdb(done); client.flushdb(done)
}); })
}); })
it('removes a value', function (done) { it('removes a value', function (done) {
client.sadd('set0', 'member0', helper.isNumber(1)); client.sadd('set0', 'member0', helper.isNumber(1))
client.srem('set0', 'member0', helper.isNumber(1)); client.srem('set0', 'member0', helper.isNumber(1))
client.scard('set0', helper.isNumber(0, done)); client.scard('set0', helper.isNumber(0, done))
}); })
it('handles attempting to remove a missing value', function (done) { it('handles attempting to remove a missing value', function (done) {
client.srem('set0', 'member0', helper.isNumber(0, done)); client.srem('set0', 'member0', helper.isNumber(0, done))
}); })
it('allows multiple values to be removed', function (done) { it('allows multiple values to be removed', function (done) {
client.sadd('set0', ['member0', 'member1', 'member2'], helper.isNumber(3)); client.sadd('set0', ['member0', 'member1', 'member2'], helper.isNumber(3))
client.srem('set0', ['member1', 'member2'], helper.isNumber(2)); client.srem('set0', ['member1', 'member2'], helper.isNumber(2))
client.smembers('set0', function (err, res) { client.smembers('set0', function (err, res) {
assert.strictEqual(res.length, 1); assert.strictEqual(res.length, 1)
assert.ok(~res.indexOf('member0')); assert.ok(~res.indexOf('member0'))
return done(err); return done(err)
}); })
}); })
it('allows multiple values to be removed with sendCommand', function (done) { it('allows multiple values to be removed with sendCommand', function (done) {
client.sendCommand('sadd', ['set0', 'member0', 'member1', 'member2'], helper.isNumber(3)); client.sendCommand('sadd', ['set0', 'member0', 'member1', 'member2'], helper.isNumber(3))
client.sendCommand('srem', ['set0', 'member1', 'member2'], helper.isNumber(2)); client.sendCommand('srem', ['set0', 'member1', 'member2'], helper.isNumber(2))
client.smembers('set0', function (err, res) { client.smembers('set0', function (err, res) {
assert.strictEqual(res.length, 1); assert.strictEqual(res.length, 1)
assert.ok(~res.indexOf('member0')); assert.ok(~res.indexOf('member0'))
return done(err); return done(err)
}); })
}); })
it('handles a value missing from the set of values being removed', function (done) { it('handles a value missing from the set of values being removed', function (done) {
client.sadd(['set0', 'member0', 'member1', 'member2'], helper.isNumber(3)); client.sadd(['set0', 'member0', 'member1', 'member2'], helper.isNumber(3))
client.srem(['set0', 'member3', 'member4'], helper.isNumber(0)); client.srem(['set0', 'member3', 'member4'], helper.isNumber(0))
client.smembers('set0', function (err, res) { client.smembers('set0', function (err, res) {
assert.strictEqual(res.length, 3); assert.strictEqual(res.length, 3)
assert.ok(~res.indexOf('member0')); assert.ok(~res.indexOf('member0'))
assert.ok(~res.indexOf('member1')); assert.ok(~res.indexOf('member1'))
assert.ok(~res.indexOf('member2')); assert.ok(~res.indexOf('member2'))
return done(err); return done(err)
}); })
}); })
afterEach(function () { afterEach(function () {
client.end(true); client.end(true)
}); })
}); })
}); })
}); })

View File

@@ -1,46 +1,44 @@
'use strict'; 'use strict'
var assert = require('assert'); var assert = require('assert')
var config = require('../lib/config'); var config = require('../lib/config')
var helper = require('../helper'); var helper = require('../helper')
var redis = config.redis; var redis = config.redis
describe("The 'sunion' method", function () {
describe('The \'sunion\' method', function () {
helper.allTests(function (ip, args) { helper.allTests(function (ip, args) {
describe('using ' + ip, function () { describe('using ' + ip, function () {
var client; var client
beforeEach(function (done) { beforeEach(function (done) {
client = redis.createClient.apply(null, args); client = redis.createClient.apply(null, args)
client.once('ready', function () { client.once('ready', function () {
client.flushdb(done); client.flushdb(done)
}); })
}); })
it('returns the union of a group of sets', function (done) { it('returns the union of a group of sets', function (done) {
client.sadd('sa', 'a', helper.isNumber(1)); client.sadd('sa', 'a', helper.isNumber(1))
client.sadd('sa', 'b', helper.isNumber(1)); client.sadd('sa', 'b', helper.isNumber(1))
client.sadd('sa', 'c', helper.isNumber(1)); client.sadd('sa', 'c', helper.isNumber(1))
client.sadd('sb', 'b', helper.isNumber(1)); client.sadd('sb', 'b', helper.isNumber(1))
client.sadd('sb', 'c', helper.isNumber(1)); client.sadd('sb', 'c', helper.isNumber(1))
client.sadd('sb', 'd', helper.isNumber(1)); client.sadd('sb', 'd', helper.isNumber(1))
client.sadd('sc', 'c', helper.isNumber(1)); client.sadd('sc', 'c', helper.isNumber(1))
client.sadd('sc', 'd', helper.isNumber(1)); client.sadd('sc', 'd', helper.isNumber(1))
client.sadd('sc', 'e', helper.isNumber(1)); client.sadd('sc', 'e', helper.isNumber(1))
client.sunion('sa', 'sb', 'sc', function (err, union) { client.sunion('sa', 'sb', 'sc', function (err, union) {
assert.deepEqual(union.sort(), ['a', 'b', 'c', 'd', 'e']); assert.deepEqual(union.sort(), ['a', 'b', 'c', 'd', 'e'])
return done(err); return done(err)
}); })
}); })
afterEach(function () { afterEach(function () {
client.end(true); client.end(true)
}); })
}); })
}); })
}); })

View File

@@ -1,49 +1,47 @@
'use strict'; 'use strict'
var assert = require('assert'); var assert = require('assert')
var config = require('../lib/config'); var config = require('../lib/config')
var helper = require('../helper'); var helper = require('../helper')
var redis = config.redis; var redis = config.redis
describe("The 'sunionstore' method", function () {
describe('The \'sunionstore\' method', function () {
helper.allTests(function (ip, args) { helper.allTests(function (ip, args) {
describe('using ' + ip, function () { describe('using ' + ip, function () {
var client; var client
beforeEach(function (done) { beforeEach(function (done) {
client = redis.createClient.apply(null, args); client = redis.createClient.apply(null, args)
client.once('ready', function () { client.once('ready', function () {
client.flushdb(done); client.flushdb(done)
}); })
}); })
it('stores the result of a union', function (done) { it('stores the result of a union', function (done) {
client.sadd('sa', 'a', helper.isNumber(1)); client.sadd('sa', 'a', helper.isNumber(1))
client.sadd('sa', 'b', helper.isNumber(1)); client.sadd('sa', 'b', helper.isNumber(1))
client.sadd('sa', 'c', helper.isNumber(1)); client.sadd('sa', 'c', helper.isNumber(1))
client.sadd('sb', 'b', helper.isNumber(1)); client.sadd('sb', 'b', helper.isNumber(1))
client.sadd('sb', 'c', helper.isNumber(1)); client.sadd('sb', 'c', helper.isNumber(1))
client.sadd('sb', 'd', helper.isNumber(1)); client.sadd('sb', 'd', helper.isNumber(1))
client.sadd('sc', 'c', helper.isNumber(1)); client.sadd('sc', 'c', helper.isNumber(1))
client.sadd('sc', 'd', helper.isNumber(1)); client.sadd('sc', 'd', helper.isNumber(1))
client.sadd('sc', 'e', helper.isNumber(1)); client.sadd('sc', 'e', helper.isNumber(1))
client.sunionstore('foo', 'sa', 'sb', 'sc', helper.isNumber(5)); client.sunionstore('foo', 'sa', 'sb', 'sc', helper.isNumber(5))
client.smembers('foo', function (err, members) { client.smembers('foo', function (err, members) {
assert.equal(members.length, 5); assert.strictEqual(members.length, 5)
assert.deepEqual(members.sort(), ['a', 'b', 'c', 'd', 'e']); assert.deepEqual(members.sort(), ['a', 'b', 'c', 'd', 'e'])
return done(err); return done(err)
}); })
}); })
afterEach(function () { afterEach(function () {
client.end(true); client.end(true)
}); })
}); })
}); })
}); })

View File

@@ -1,37 +1,35 @@
'use strict'; 'use strict'
var assert = require('assert'); var assert = require('assert')
var config = require('../lib/config'); var config = require('../lib/config')
var helper = require('../helper'); var helper = require('../helper')
var redis = config.redis; var redis = config.redis
describe("The 'ttl' method", function () {
describe('The \'ttl\' method', function () {
helper.allTests(function (ip, args) { helper.allTests(function (ip, args) {
describe('using ' + ip, function () { describe('using ' + ip, function () {
var client; var client
beforeEach(function (done) { beforeEach(function (done) {
client = redis.createClient.apply(null, args); client = redis.createClient.apply(null, args)
client.once('ready', function () { client.once('ready', function () {
client.flushdb(done); client.flushdb(done)
}); })
}); })
it('returns the current ttl on a key', function (done) { it('returns the current ttl on a key', function (done) {
client.set(['ttl key', 'ttl val'], helper.isString('OK')); client.set(['ttl key', 'ttl val'], helper.isString('OK'))
client.expire(['ttl key', '100'], helper.isNumber(1)); client.expire(['ttl key', '100'], helper.isNumber(1))
client.ttl(['ttl key'], function (err, ttl) { client.ttl(['ttl key'], function (err, ttl) {
assert(ttl >= 99); assert(ttl >= 99)
assert(ttl <= 100); assert(ttl <= 100)
done(err); done(err)
}); })
}); })
afterEach(function () { afterEach(function () {
client.end(true); client.end(true)
}); })
}); })
}); })
}); })

View File

@@ -1,56 +1,53 @@
'use strict'; 'use strict'
var config = require('../lib/config'); var config = require('../lib/config')
var helper = require('../helper'); var helper = require('../helper')
var redis = config.redis; var redis = config.redis
describe("The 'type' method", function () {
describe('The \'type\' method', function () {
helper.allTests(function (ip, args) { helper.allTests(function (ip, args) {
describe('using ' + ip, function () { describe('using ' + ip, function () {
var client; var client
beforeEach(function (done) { beforeEach(function (done) {
client = redis.createClient.apply(null, args); client = redis.createClient.apply(null, args)
client.once('ready', function () { client.once('ready', function () {
client.flushdb(done); client.flushdb(done)
}); })
}); })
it('reports string type', function (done) { it('reports string type', function (done) {
client.set(['string key', 'should be a string'], helper.isString('OK')); client.set(['string key', 'should be a string'], helper.isString('OK'))
client.type(['string key'], helper.isString('string', done)); client.type(['string key'], helper.isString('string', done))
}); })
it('reports list type', function (done) { it('reports list type', function (done) {
client.rpush(['list key', 'should be a list'], helper.isNumber(1)); client.rpush(['list key', 'should be a list'], helper.isNumber(1))
client.type(['list key'], helper.isString('list', done)); client.type(['list key'], helper.isString('list', done))
}); })
it('reports set type', function (done) { it('reports set type', function (done) {
client.sadd(['set key', 'should be a set'], helper.isNumber(1)); client.sadd(['set key', 'should be a set'], helper.isNumber(1))
client.type(['set key'], helper.isString('set', done)); client.type(['set key'], helper.isString('set', done))
}); })
it('reports zset type', function (done) { it('reports zset type', function (done) {
client.zadd('zset key', ['10.0', 'should be a zset'], helper.isNumber(1)); client.zadd('zset key', ['10.0', 'should be a zset'], helper.isNumber(1))
client.type(['zset key'], helper.isString('zset', done)); client.type(['zset key'], helper.isString('zset', done))
}); })
it('reports hash type', function (done) { it('reports hash type', function (done) {
client.hset('hash key', 'hashtest', 'should be a hash', helper.isNumber(1)); client.hset('hash key', 'hashtest', 'should be a hash', helper.isNumber(1))
client.type(['hash key'], helper.isString('hash', done)); client.type(['hash key'], helper.isString('hash', done))
}); })
it('reports none for null key', function (done) { it('reports none for null key', function (done) {
client.type('not here yet', helper.isString('none', done)); client.type('not here yet', helper.isString('none', done))
}); })
afterEach(function () { afterEach(function () {
client.end(true); client.end(true)
}); })
}); })
}); })
})
});

View File

@@ -1,54 +1,50 @@
'use strict'; 'use strict'
var assert = require('assert'); var assert = require('assert')
var config = require('../lib/config'); var config = require('../lib/config')
var helper = require('../helper'); var helper = require('../helper')
var redis = config.redis; var redis = config.redis
describe("The 'watch' method", function () {
describe('The \'watch\' method', function () {
helper.allTests(function (ip, args) { helper.allTests(function (ip, args) {
var watched = 'foobar'
var watched = 'foobar';
describe('using ' + ip, function () { describe('using ' + ip, function () {
var client; var client
beforeEach(function (done) { beforeEach(function (done) {
client = redis.createClient.apply(null, args); client = redis.createClient.apply(null, args)
client.once('ready', function () { client.once('ready', function () {
client.flushdb(done); client.flushdb(done)
}); })
}); })
afterEach(function () { afterEach(function () {
client.end(true); client.end(true)
}); })
it('does not execute transaction if watched key was modified prior to execution', function (done) { it('does not execute transaction if watched key was modified prior to execution', function (done) {
client.watch(watched); client.watch(watched)
client.incr(watched); client.incr(watched)
var multi = client.multi(); var multi = client.multi()
multi.incr(watched); multi.incr(watched)
multi.exec(helper.isNull(done)); multi.exec(helper.isNull(done))
}); })
it('successfully modifies other keys independently of transaction', function (done) { it('successfully modifies other keys independently of transaction', function (done) {
client.set('unwatched', 200); client.set('unwatched', 200)
client.set(watched, 0); client.set(watched, 0)
client.watch(watched); client.watch(watched)
client.incr(watched); client.incr(watched)
client.multi().incr(watched).exec(function (err, replies) { client.multi().incr(watched).exec(function (err, replies) {
assert.strictEqual(replies, null, 'Aborted transaction multi-bulk reply should be null.'); assert.strictEqual(err, null)
assert.strictEqual(replies, null, 'Aborted transaction multi-bulk reply should be null.')
client.get('unwatched', function (err, reply) { client.get('unwatched', helper.isString('200', done))
assert.equal(reply, 200, 'Expected 200, got ' + reply); })
return done(err); })
}); })
}); })
}); })
});
});
});

View File

@@ -1,48 +1,46 @@
'use strict'; 'use strict'
var config = require('../lib/config'); var config = require('../lib/config')
var helper = require('../helper'); var helper = require('../helper')
var assert = require('assert'); var assert = require('assert')
var redis = config.redis; var redis = config.redis
describe("The 'zadd' method", function () {
describe('The \'zadd\' method', function () {
helper.allTests(function (ip, args) { helper.allTests(function (ip, args) {
describe('using ' + ip, function () { describe('using ' + ip, function () {
var client; var client
beforeEach(function (done) { beforeEach(function (done) {
client = redis.createClient.apply(null, args); client = redis.createClient.apply(null, args)
client.once('ready', function () { client.once('ready', function () {
client.flushdb(done); client.flushdb(done)
}); })
}); })
it('reports an error', function (done) { it('reports an error', function (done) {
if (helper.redisProcess().spawnFailed()) this.skip(); if (helper.redisProcess().spawnFailed()) this.skip()
client.zadd('infinity', [+'5t', 'should not be possible'], helper.isError(done)); client.zadd('infinity', [+'5t', 'should not be possible'], helper.isError(done))
}); })
it('return inf / -inf', function (done) { it('return inf / -inf', function (done) {
if (helper.redisProcess().spawnFailed()) this.skip(); if (helper.redisProcess().spawnFailed()) this.skip()
helper.serverVersionAtLeast.call(this, client, [3, 0, 2]); helper.serverVersionAtLeast.call(this, client, [3, 0, 2])
client.zadd('infinity', [+Infinity, 'should be inf'], helper.isNumber(1)); 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', ['inf', 'should be also be inf'], helper.isNumber(1))
client.zadd('infinity', -Infinity, 'should be negative 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.zadd('infinity', [99999999999999999999999, 'should not be inf'], helper.isNumber(1))
client.zrange('infinity', 0, -1, 'WITHSCORES', function (err, res) { client.zrange('infinity', 0, -1, 'WITHSCORES', function (err, res) {
assert.equal(res[5], 'inf'); assert.strictEqual(err, null)
assert.equal(res[1], '-inf'); assert.strictEqual(res[5], 'inf')
assert.equal(res[3], '9.9999999999999992e+22'); assert.strictEqual(res[1], '-inf')
done(); assert.strictEqual(res[3], '9.9999999999999992e+22')
}); done()
}); })
})
afterEach(function () { afterEach(function () {
client.end(true); client.end(true)
}); })
}); })
}); })
})
});

View File

@@ -1,50 +1,47 @@
'use strict'; 'use strict'
var config = require('../lib/config'); var config = require('../lib/config')
var helper = require('../helper'); var helper = require('../helper')
var assert = require('assert'); var assert = require('assert')
var redis = config.redis; var redis = config.redis
describe("The 'zscan' method", function () {
describe('The \'zscan\' method', function () {
helper.allTests(function (ip, args) { helper.allTests(function (ip, args) {
describe('using ' + ip, function () { describe('using ' + ip, function () {
var client; var client
beforeEach(function (done) { beforeEach(function (done) {
client = redis.createClient.apply(null, args); client = redis.createClient.apply(null, args)
client.once('ready', function () { client.once('ready', function () {
client.flushdb(done); client.flushdb(done)
}); })
}); })
it('return values', function (done) { it('return values', function (done) {
if (helper.redisProcess().spawnFailed()) this.skip(); if (helper.redisProcess().spawnFailed()) this.skip()
helper.serverVersionAtLeast.call(this, client, [2, 8, 0]); helper.serverVersionAtLeast.call(this, client, [2, 8, 0])
var hash = {}; var hash = {}
var set = []; var set = []
var zset = ['zset:1']; var zset = ['zset:1']
for (var i = 0; i < 500; i++) { for (var i = 0; i < 500; i++) {
hash['key_' + i] = 'value_' + i; hash['key_' + i] = 'value_' + i
set.push('member_' + i); set.push('member_' + i)
zset.push(i, 'zMember_' + i); zset.push(i, 'zMember_' + i)
} }
client.hmset('hash:1', hash); client.hmset('hash:1', hash)
client.sadd('set:1', set); client.sadd('set:1', set)
client.zadd(zset); client.zadd(zset)
client.zscan('zset:1', 0, 'MATCH', '*', 'COUNT', 500, function (err, res) { client.zscan('zset:1', 0, 'MATCH', '*', 'COUNT', 500, function (err, res) {
assert(!err); assert(!err)
assert.strictEqual(res.length, 2); assert.strictEqual(res.length, 2)
assert.strictEqual(res[1].length, 1000); assert.strictEqual(res[1].length, 1000)
done(); done()
}); })
}); })
afterEach(function () { afterEach(function () {
client.end(true); client.end(true)
}); })
}); })
}); })
})
});

View File

@@ -1,35 +1,29 @@
'use strict'; 'use strict'
var config = require('../lib/config'); var config = require('../lib/config')
var helper = require('../helper'); var helper = require('../helper')
var assert = require('assert'); var redis = config.redis
var redis = config.redis;
describe("The 'zscore' method", function () {
describe('The \'zscore\' method', function () {
helper.allTests(function (ip, args) { helper.allTests(function (ip, args) {
describe('using ' + ip, function () { describe('using ' + ip, function () {
var client; var client
beforeEach(function (done) { beforeEach(function (done) {
client = redis.createClient.apply(null, args); client = redis.createClient.apply(null, args)
client.once('ready', function () { client.once('ready', function () {
client.flushdb(done); client.flushdb(done)
}); })
}); })
it('should return the score of member in the sorted set at key', function (done) { it('should return the score of member in the sorted set at key', function (done) {
client.zadd('myzset', 1, 'one'); client.zadd('myzset', 1, 'one')
client.zscore('myzset', 'one', function (err, res) { client.zscore('myzset', 'one', helper.isString('1', done))
assert.equal(res, 1); })
done();
});
});
afterEach(function () { afterEach(function () {
client.end(true); client.end(true)
}); })
}); })
}); })
}); })

View File

@@ -1,99 +1,97 @@
'use strict'; 'use strict'
var assert = require('assert'); var assert = require('assert')
var config = require('./lib/config'); var config = require('./lib/config')
var helper = require('./helper'); var helper = require('./helper')
var RedisProcess = require('./lib/redis-process'); var RedisProcess = require('./lib/redis-process')
var rp; var rp
var path = require('path'); var path = require('path')
var redis = config.redis; var redis = config.redis
if (process.platform === 'win32') {
// TODO: Fix redis process spawn on windows // TODO: Fix redis process spawn on windows
return; if (process.platform !== 'win32') {
} describe('master slave sync', function () {
var master = null
describe('master slave sync', function () { var slave = null
var master = null;
var slave = null;
before(function (done) { before(function (done) {
helper.stopRedis(function () { helper.stopRedis(function () {
helper.startRedis('./conf/password.conf', done); helper.startRedis('./conf/password.conf', done)
}); })
}); })
before(function (done) { before(function (done) {
if (helper.redisProcess().spawnFailed()) return done(); if (helper.redisProcess().spawnFailed()) return done()
master = redis.createClient({ master = redis.createClient({
password: 'porkchopsandwiches' password: 'porkchopsandwiches'
}); })
var multi = master.multi(); var multi = master.multi()
var i = 0; var i = 0
while (i < 1000) { while (i < 1000) {
i++; i++
// Write some data in the redis instance, so there's something to sync // Write some data in the redis instance, so there's something to sync
multi.set('foo' + i, 'bar' + new Array(500).join(Math.random())); multi.set('foo' + i, 'bar' + new Array(500).join(Math.random()))
} }
multi.exec(done); multi.exec(done)
}); })
it('sync process and no master should delay ready being emitted for slaves', function (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 port = 6381
var firstInfo; var firstInfo
slave = redis.createClient({ slave = redis.createClient({
port: port, port: port,
retryStrategy: function (options) { retryStrategy: function (options) {
// Try to reconnect in very small intervals to catch the master_link_status down before the sync completes // Try to reconnect in very small intervals to catch the master_link_status down before the sync completes
return 10; return 10
} }
}); })
var tmp = slave.info.bind(slave); var tmp = slave.info.bind(slave)
var i = 0; var i = 0
slave.info = function (err, res) { slave.info = function (err, res) {
i++; i++
tmp(err, res); tmp(err, res)
if (!firstInfo || Object.keys(firstInfo).length === 0) { if (!firstInfo || Object.keys(firstInfo).length === 0) {
firstInfo = slave.serverInfo; firstInfo = slave.serverInfo
}
} }
};
slave.on('connect', function () { slave.on('connect', function () {
assert.strictEqual(i, 0); assert.strictEqual(i, 0)
}); })
var end = helper.callFuncAfter(done, 2); var end = helper.callFuncAfter(done, 2)
slave.on('ready', function () { slave.on('ready', function () {
assert.strictEqual(this.serverInfo.master_link_status, 'up'); assert.strictEqual(this.serverInfo.master_link_status, 'up')
assert.strictEqual(firstInfo.master_link_status, 'down'); assert.strictEqual(firstInfo.master_link_status, 'down')
assert(i > 1); assert(i > 1)
this.get('foo300', function (err, res) { this.get('foo300', function (err, res) {
assert.strictEqual(res.substr(0, 3), 'bar'); assert.strictEqual(res.substr(0, 3), 'bar')
end(err); end(err)
}); })
}); })
RedisProcess.start(function (err, _rp) { RedisProcess.start(function (err, _rp) {
rp = _rp; rp = _rp
end(err); end(err)
}, path.resolve(__dirname, './conf/slave.conf'), port); }, path.resolve(__dirname, './conf/slave.conf'), port)
}); })
after(function (done) { after(function (done) {
if (helper.redisProcess().spawnFailed()) return done(); if (helper.redisProcess().spawnFailed()) return done()
var end = helper.callFuncAfter(done, 3); var end = helper.callFuncAfter(done, 3)
rp.stop(end); rp.stop(end)
slave.end(true); slave.end(true)
master.flushdb(function (err) { master.flushdb(function (err) {
end(err); end(err)
master.end(true); master.end(true)
}); })
helper.stopRedis(function () { helper.stopRedis(function () {
helper.startRedis('./conf/redis.conf', end); helper.startRedis('./conf/redis.conf', end)
}); })
}); })
}); })
}

View File

@@ -1,174 +1,170 @@
'use strict'; 'use strict'
var assert = require('assert'); var assert = require('assert')
var config = require('./lib/config'); var config = require('./lib/config')
var helper = require('./helper'); var helper = require('./helper')
var redis = config.redis; var redis = config.redis
var intercept = require('intercept-stdout'); var intercept = require('intercept-stdout')
var net = require('net'); var net = require('net')
var client; var client
describe('connection tests', function () { describe('connection tests', function () {
beforeEach(function () { beforeEach(function () {
client = null; client = null
}); })
afterEach(function () { afterEach(function () {
client.end(true); client.end(true)
}); })
it('unofficially support for a private stream', function () { it('unofficially support for a private stream', function () {
// While using a private stream, reconnection and other features are not going to work properly. // 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. // Besides that some functions also have to be monkey patched to be safe from errors in this case.
// Therefore this is not officially supported! // Therefore this is not officially supported!
var socket = new net.Socket(); var socket = new net.Socket()
client = new redis.RedisClient({ client = new redis.RedisClient({
prefix: 'test' prefix: 'test'
}, socket); }, socket)
assert.strictEqual(client.stream, socket); assert.strictEqual(client.stream, socket)
assert.strictEqual(client.stream.listeners('error').length, 1); assert.strictEqual(client.stream.listeners('error').length, 1)
assert.strictEqual(client.address, '"Private stream"'); assert.strictEqual(client.address, '"Private stream"')
// Pretent a reconnect event // Pretend a reconnect event
client.createStream(); client.createStream()
assert.strictEqual(client.stream, socket); assert.strictEqual(client.stream, socket)
assert.strictEqual(client.stream.listeners('error').length, 1); assert.strictEqual(client.stream.listeners('error').length, 1)
}); })
describe('quit on lost connections', function () { describe('quit on lost connections', function () {
it('calling quit while the connection is down should not end in reconnecting version a', function (done) { it('calling quit while the connection is down should not end in reconnecting version a', function (done) {
var called = 0; var called = 0
client = redis.createClient({ client = redis.createClient({
port: 9999, port: 9999,
retryStrategy: function (options) { retryStrategy: function (options) {
var bool = client.quit(function (err, res) { var bool = client.quit(function (err, res) {
assert.strictEqual(res, 'OK'); assert.strictEqual(res, 'OK')
assert.strictEqual(err, null); assert.strictEqual(err, null)
assert.strictEqual(called++, -1); assert.strictEqual(called++, -1)
setTimeout(done, 25); setTimeout(done, 25)
}); })
assert.strictEqual(bool, false); assert.strictEqual(bool, false)
assert.strictEqual(called++, 0); assert.strictEqual(called++, 0)
return 5; return 5
} }
}); })
client.set('foo', 'bar', function (err, res) { client.set('foo', 'bar', function (err, res) {
assert.strictEqual(err.message, 'Stream connection ended and command aborted.'); assert.strictEqual(err.message, 'Stream connection ended and command aborted.')
called = -1; called = -1
}); })
}); })
it('calling quit while the connection is down should not end in reconnecting version b', function (done) { it('calling quit while the connection is down should not end in reconnecting version b', function (done) {
var called = false; var called = false
client = redis.createClient(9999); client = redis.createClient(9999)
client.set('foo', 'bar', function (err, res) { client.set('foo', 'bar', function (err, res) {
assert.strictEqual(err.message, 'Stream connection ended and command aborted.'); assert.strictEqual(err.message, 'Stream connection ended and command aborted.')
called = true; called = true
}); })
var bool = client.quit(function (err, res) { var bool = client.quit(function (err, res) {
assert.strictEqual(res, 'OK'); assert.strictEqual(res, 'OK')
assert.strictEqual(err, null); assert.strictEqual(err, null)
assert(called); assert(called)
done(); done()
}); })
assert.strictEqual(bool, false); assert.strictEqual(bool, false)
}); })
it('calling quit while the connection is down without offline queue should end the connection right away', function (done) { it('calling quit while the connection is down without offline queue should end the connection right away', function (done) {
var called = false; var called = false
client = redis.createClient(9999, { client = redis.createClient(9999, {
enableOfflineQueue: false enableOfflineQueue: false
}); })
client.set('foo', 'bar', function (err, res) { 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.'); assert.strictEqual(err.message, 'SET can\'t be processed. The connection is not yet established and the offline queue is deactivated.')
called = true; called = true
}); })
var bool = client.quit(function (err, res) { var bool = client.quit(function (err, res) {
assert.strictEqual(res, 'OK'); assert.strictEqual(res, 'OK')
assert.strictEqual(err, null); assert.strictEqual(err, null)
assert(called); assert(called)
done(); done()
}); })
// TODO: In v.3 the quit command would be fired right away, so bool should be true // TODO: In v.3 the quit command would be fired right away, so bool should be true
assert.strictEqual(bool, false); assert.strictEqual(bool, false)
}); })
it('calling quit while connected without offline queue should end the connection when all commands have finished', function (done) { it('calling quit while connected without offline queue should end the connection when all commands have finished', function (done) {
var called = false; var called = false
client = redis.createClient({ client = redis.createClient({
enableOfflineQueue: false enableOfflineQueue: false
}); })
client.on('ready', function () { client.on('ready', function () {
client.set('foo', 'bar', function (err, res) { client.set('foo', 'bar', function (err, res) {
assert.strictEqual(res, 'OK'); assert.strictEqual(err, null)
called = true; assert.strictEqual(res, 'OK')
}); called = true
})
var bool = client.quit(function (err, res) { var bool = client.quit(function (err, res) {
assert.strictEqual(res, 'OK'); assert.strictEqual(res, 'OK')
assert.strictEqual(err, null); assert.strictEqual(err, null)
assert(called); assert(called)
done(); done()
}); })
// TODO: In v.3 the quit command would be fired right away, so bool should be true // TODO: In v.3 the quit command would be fired right away, so bool should be true
assert.strictEqual(bool, true); assert.strictEqual(bool, true)
}); })
}); })
it('do not quit before connected or a connection issue is detected', function (done) { it('do not quit before connected or a connection issue is detected', function (done) {
client = redis.createClient(); client = redis.createClient()
client.set('foo', 'bar', helper.isString('OK')); client.set('foo', 'bar', helper.isString('OK'))
var bool = client.quit(done); var bool = client.quit(done)
assert.strictEqual(bool, false); assert.strictEqual(bool, false)
}); })
it('quit "succeeds" even if the client connection is closed while doing so', function (done) { it('quit "succeeds" even if the client connection is closed while doing so', function (done) {
client = redis.createClient(); client = redis.createClient()
client.set('foo', 'bar', function (err, res) { client.set('foo', 'bar', function (err, res) {
assert.strictEqual(res, 'OK'); assert.strictEqual(err, null)
assert.strictEqual(res, 'OK')
client.quit(function (err, res) { client.quit(function (err, res) {
assert.strictEqual(res, 'OK'); assert.strictEqual(res, 'OK')
done(err); done(err)
}); })
client.end(true); // Flushing the quit command should result in a success 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) { it('quit right away if connection drops while quit command is on the fly', function (done) {
client = redis.createClient(); client = redis.createClient()
client.once('ready', function () { client.once('ready', function () {
client.set('foo', 'bar', helper.isError()); client.set('foo', 'bar', helper.isError())
var bool = client.quit(done); var bool = client.quit(done)
assert.strictEqual(bool, true); assert.strictEqual(bool, true)
process.nextTick(function () { process.nextTick(function () {
client.stream.destroy(); client.stream.destroy()
}); })
}); })
}); })
})
});
helper.allTests(function (ip, args) { helper.allTests(function (ip, args) {
describe('using ' + ip, function () { describe('using ' + ip, function () {
describe('on lost connection', function () { describe('on lost connection', function () {
it('end connection while retry is still ongoing', function (done) { it('end connection while retry is still ongoing', function (done) {
var connectTimeout = 1000; // in ms var connectTimeout = 1000 // in ms
client = redis.createClient({ client = redis.createClient({
connectTimeout: connectTimeout connectTimeout: connectTimeout
}); })
client.once('ready', function () { client.once('ready', function () {
helper.killConnection(client); helper.killConnection(client)
}); })
client.on('reconnecting', function (params) { client.on('reconnecting', function (params) {
client.end(true); client.end(true)
assert.strictEqual(params.timesConnected, 1); assert.strictEqual(params.timesConnected, 1)
setTimeout(done, 5); setTimeout(done, 5)
}); })
}); })
it.skip('can not connect with wrong host / port in the options object', function (done) { it.skip('can not connect with wrong host / port in the options object', function (done) {
var options = { var options = {
@@ -176,413 +172,409 @@ describe('connection tests', function () {
port: 6379, port: 6379,
family: ip, family: ip,
retryStrategy: function () {} retryStrategy: function () {}
}; }
client = redis.createClient(options); client = redis.createClient(options)
assert.strictEqual(client.connectionOptions.family, ip === 'IPv6' ? 6 : 4); assert.strictEqual(client.connectionOptions.family, ip === 'IPv6' ? 6 : 4)
assert.strictEqual(Object.keys(options).length, 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) { client.on('error', function (err) {
assert(/CONNECTION_BROKEN|ENOTFOUND|EAI_AGAIN/.test(err.code)); assert(/CONNECTION_BROKEN|ENOTFOUND|EAI_AGAIN/.test(err.code))
end(); end()
}); })
})
});
it('emits error once if reconnecting after command has been executed but not yet returned without callback', function (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 = redis.createClient.apply(null, args)
client.on('ready', function () { client.on('ready', function () {
client.set('foo', 'bar', function (err) { client.set('foo', 'bar', function (err) {
assert.strictEqual(err.code, 'UNCERTAIN_STATE'); assert.strictEqual(err.code, 'UNCERTAIN_STATE')
done(); done()
}); })
// Abort connection before the value returned // Abort connection before the value returned
client.stream.destroy(); client.stream.destroy()
}); })
}); })
it('retryStrategy used to reconnect with individual error', function (done) { it('retryStrategy used to reconnect with individual error', function (done) {
client = redis.createClient({ client = redis.createClient({
retryStrategy: function (options) { retryStrategy: function (options) {
if (options.totalRetryTime > 150) { if (options.totalRetryTime > 150) {
client.set('foo', 'bar', function (err, res) { client.set('foo', 'bar', function (err, res) {
assert.strictEqual(err.message, 'Stream connection ended and command aborted.'); assert.strictEqual(err.message, 'Stream connection ended and command aborted.')
assert.strictEqual(err.origin.message, 'Connection timeout'); assert.strictEqual(err.origin.message, 'Connection timeout')
done(); done()
}); })
// Pass a individual error message to the error handler // Pass a individual error message to the error handler
return new Error('Connection timeout'); return new Error('Connection timeout')
} }
return Math.min(options.attempt * 25, 200); return Math.min(options.attempt * 25, 200)
}, },
port: 9999 port: 9999
}); })
}); })
it('retryStrategy used to reconnect', function (done) { it('retryStrategy used to reconnect', function (done) {
client = redis.createClient({ client = redis.createClient({
retryStrategy: function (options) { retryStrategy: function (options) {
if (options.totalRetryTime > 150) { if (options.totalRetryTime > 150) {
client.set('foo', 'bar', function (err, res) { client.set('foo', 'bar', function (err, res) {
assert.strictEqual(err.message, 'Stream connection ended and command aborted.'); assert.strictEqual(err.message, 'Stream connection ended and command aborted.')
assert.strictEqual(err.code, 'NR_CLOSED'); assert.strictEqual(err.code, 'NR_CLOSED')
assert.strictEqual(err.origin.code, 'ECONNREFUSED'); assert.strictEqual(err.origin.code, 'ECONNREFUSED')
done(); done()
}); })
return false; return false
} }
return Math.min(options.attempt * 25, 200); return Math.min(options.attempt * 25, 200)
}, },
port: 9999 port: 9999
}); })
}); })
it('retryStrategy used to reconnect with defaults', function (done) { it('retryStrategy used to reconnect with defaults', function (done) {
var unhookIntercept = intercept(function () { var unhookIntercept = intercept(function () {
return ''; return ''
}); })
redis.debugMode = true; redis.debugMode = true
client = redis.createClient({ client = redis.createClient({
retryStrategy: function (options) { retryStrategy: function (options) {
client.set('foo', 'bar'); client.set('foo', 'bar')
assert(redis.debugMode); assert(redis.debugMode)
return null; return null
} }
}); })
setTimeout(function () { setTimeout(function () {
client.stream.destroy(); client.stream.destroy()
}, 50); }, 50)
client.on('error', function (err) { client.on('error', function (err) {
assert.strictEqual(err.code, 'NR_CLOSED'); assert.strictEqual(err.code, 'NR_CLOSED')
assert.strictEqual(err.message, 'Stream connection ended and command aborted.'); assert.strictEqual(err.message, 'Stream connection ended and command aborted.')
unhookIntercept(); unhookIntercept()
redis.debugMode = false; redis.debugMode = false
done(); done()
}); })
}); })
}); })
describe('when not connected', function () { describe('when not connected', function () {
// TODO: Fix this test // TODO: Fix this test
it.skip('emit an error after the socket timeout exceeded the connectTimeout time', function (done) { it.skip('emit an error after the socket timeout exceeded the connectTimeout time', function (done) {
var connectTimeout = 500; // in ms var connectTimeout = 500 // in ms
client = redis.createClient({ client = redis.createClient({
// Auto detect ipv4 and use non routable ip to trigger the timeout // Auto detect ipv4 and use non routable ip to trigger the timeout
host: '10.255.255.1', host: '10.255.255.1',
connectTimeout: connectTimeout connectTimeout: connectTimeout
}); })
process.nextTick(function () { process.nextTick(function () {
assert.strictEqual(client.stream.listeners('timeout').length, 1); assert.strictEqual(client.stream.listeners('timeout').length, 1)
}); })
assert.strictEqual(client.address, '10.255.255.1:6379'); assert.strictEqual(client.address, '10.255.255.1:6379')
assert.strictEqual(client.connectionOptions.family, 4); assert.strictEqual(client.connectionOptions.family, 4)
client.on('reconnecting', function (params) { client.on('reconnecting', function (params) {
throw new Error('No reconnect, since no connection was ever established'); throw new Error('No reconnect, since no connection was ever established')
}); })
var time = Date.now(); var time = Date.now()
client.on('error', function (err) { client.on('error', function (err) {
if (err.code === 'ENETUNREACH') { // The test is run without a internet connection. Pretent it works if (err.code === 'ENETUNREACH') { // The test is run without a internet connection. Pretent it works
return done(); return done()
} }
assert(/Redis connection in broken state: connection timeout.*?exceeded./.test(err.message), err.message); assert(/Redis connection in broken state: connection timeout.*?exceeded./.test(err.message), err.message)
// The code execution on windows is very slow at times // The code execution on windows is very slow at times
var add = process.platform !== 'win32' ? 15 : 200; var add = process.platform !== 'win32' ? 15 : 200
var now = Date.now(); var now = Date.now()
assert(now - time < connectTimeout + add, 'The real timeout time should be below ' + (connectTimeout + add) + 'ms but is: ' + (now - time)); 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) // 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)); assert(now - time >= connectTimeout - 5, 'The real timeout time should be above ' + connectTimeout + 'ms, but it is: ' + (now - time))
done(); done()
}); })
}); })
it('use the system socket timeout if the connectTimeout has not been provided', function (done) { it('use the system socket timeout if the connectTimeout has not been provided', function (done) {
client = redis.createClient({ client = redis.createClient({
host: '2001:db8::ff00:42:8329' // auto detect ip v6 host: '2001:db8::ff00:42:8329' // auto detect ip v6
}); })
assert.strictEqual(client.address, '2001:db8::ff00:42:8329:6379'); assert.strictEqual(client.address, '2001:db8::ff00:42:8329:6379')
assert.strictEqual(client.connectionOptions.family, 6); assert.strictEqual(client.connectionOptions.family, 6)
process.nextTick(function () { process.nextTick(function () {
assert.strictEqual(client.stream.listeners('timeout').length, 0); assert.strictEqual(client.stream.listeners('timeout').length, 0)
done(); done()
}); })
client.end(true); client.end(true)
}); })
it('clears the socket timeout after a connection has been established', function (done) { it('clears the socket timeout after a connection has been established', function (done) {
client = redis.createClient({ client = redis.createClient({
connectTimeout: 1000 connectTimeout: 1000
}); })
process.nextTick(function () { process.nextTick(function () {
assert.strictEqual(client.stream._idleTimeout, 1000); assert.strictEqual(client.stream._idleTimeout, 1000)
}); })
client.on('connect', function () { client.on('connect', function () {
assert.strictEqual(client.stream._idleTimeout, -1); assert.strictEqual(client.stream._idleTimeout, -1)
assert.strictEqual(client.stream.listeners('timeout').length, 0); assert.strictEqual(client.stream.listeners('timeout').length, 0)
client.on('ready', done); client.on('ready', done)
}); })
}); })
it('connect with host and port provided in the options object', function (done) { it('connect with host and port provided in the options object', function (done) {
client = redis.createClient({ client = redis.createClient({
host: 'localhost', host: 'localhost',
port: '6379', port: '6379',
connectTimeout: 1000 connectTimeout: 1000
}); })
client.once('ready', done); client.once('ready', done)
}); })
it('connect with path provided in the options object', function (done) { it('connect with path provided in the options object', function (done) {
if (process.platform === 'win32') { if (process.platform === 'win32') {
this.skip(); this.skip()
} }
client = redis.createClient({ client = redis.createClient({
path: '/tmp/redis.sock', path: '/tmp/redis.sock',
connectTimeout: 1000 connectTimeout: 1000
}); })
var end = helper.callFuncAfter(done, 2); var end = helper.callFuncAfter(done, 2)
client.once('ready', end); client.once('ready', end)
client.set('foo', 'bar', end); client.set('foo', 'bar', end)
}); })
it('connects correctly with args', function (done) { it('connects correctly with args', function (done) {
client = redis.createClient.apply(null, args); client = redis.createClient.apply(null, args)
client.on('error', done); client.on('error', done)
client.once('ready', function () { client.once('ready', function () {
client.removeListener('error', done); client.removeListener('error', done)
client.get('recon 1', done); client.get('recon 1', done)
}); })
}); })
it('connects correctly with default values', function (done) { it('connects correctly with default values', function (done) {
client = redis.createClient(); client = redis.createClient()
client.on('error', done); client.on('error', done)
client.once('ready', function () { client.once('ready', function () {
client.removeListener('error', done); client.removeListener('error', done)
client.get('recon 1', done); client.get('recon 1', done)
}); })
}); })
it('connects with a port only', function (done) { it('connects with a port only', function (done) {
client = redis.createClient(6379); client = redis.createClient(6379)
assert.strictEqual(client.connectionOptions.family, 4); assert.strictEqual(client.connectionOptions.family, 4)
client.on('error', done); client.on('error', done)
client.once('ready', function () { client.once('ready', function () {
client.removeListener('error', done); client.removeListener('error', done)
client.get('recon 1', done); client.get('recon 1', done)
}); })
}); })
it('connects correctly to localhost', function (done) { it('connects correctly to localhost', function (done) {
client = redis.createClient(null, null); client = redis.createClient(null, null)
client.on('error', done); client.on('error', done)
client.once('ready', function () { client.once('ready', function () {
client.removeListener('error', done); client.removeListener('error', done)
client.get('recon 1', done); client.get('recon 1', done)
}); })
}); })
it('connects correctly to the provided host with the port set to null', function (done) { it('connects correctly to the provided host with the port set to null', function (done) {
client = redis.createClient(null, 'localhost'); client = redis.createClient(null, 'localhost')
client.on('error', done); client.on('error', done)
assert.strictEqual(client.address, 'localhost:6379'); assert.strictEqual(client.address, 'localhost:6379')
client.once('ready', function () { client.once('ready', function () {
client.set('foo', 'bar'); client.set('foo', 'bar')
client.get('foo', function (err, res) { client.get('foo', function (err, res) {
assert.strictEqual(res, 'bar'); assert.strictEqual(res, 'bar')
done(err); done(err)
}); })
}); })
}); })
it('connects correctly to localhost and no ready check', function (done) { it('connects correctly to localhost and no ready check', function (done) {
client = redis.createClient(undefined, undefined, { client = redis.createClient(undefined, undefined, {
noReadyCheck: true noReadyCheck: true
}); })
client.on('error', done); client.on('error', done)
client.once('ready', function () { client.once('ready', function () {
client.set('foo', 'bar'); client.set('foo', 'bar')
client.get('foo', function (err, res) { client.get('foo', function (err, res) {
assert.strictEqual(res, 'bar'); assert.strictEqual(res, 'bar')
done(err); done(err)
}); })
}); })
}); })
it('connects correctly to the provided host with the port set to undefined', function (done) { it('connects correctly to the provided host with the port set to undefined', function (done) {
client = redis.createClient(undefined, 'localhost', { client = redis.createClient(undefined, 'localhost', {
noReadyCheck: true noReadyCheck: true
}); })
client.on('error', done); client.on('error', done)
assert.strictEqual(client.address, 'localhost:6379'); assert.strictEqual(client.address, 'localhost:6379')
client.once('ready', function () { client.once('ready', function () {
client.set('foo', 'bar'); client.set('foo', 'bar')
client.get('foo', function (err, res) { client.get('foo', function (err, res) {
assert.strictEqual(res, 'bar'); assert.strictEqual(res, 'bar')
done(err); done(err)
}); })
}); })
}); })
it('connects correctly even if the info command is not present on the redis server', function (done) { it('connects correctly even if the info command is not present on the redis server', function (done) {
client = redis.createClient.apply(null, args); client = redis.createClient.apply(null, args)
client.info = function (cb) { client.info = function (cb) {
// Mock the result // Mock the result
cb(new Error("ERR unknown command 'info'")); cb(new Error('ERR unknown command \'info\''))
}; }
client.once('ready', function () { client.once('ready', function () {
assert.strictEqual(Object.keys(client.serverInfo).length, 0); assert.strictEqual(Object.keys(client.serverInfo).length, 0)
done(); done()
}); })
}); })
it('fake the stream to mock redis', function () { it('fake the stream to mock redis', function () {
// This is needed for libraries that want to mock the stream like fakeredis // This is needed for libraries that want to mock the stream like fakeredis
var temp = redis.RedisClient.prototype.createStream; var temp = redis.RedisClient.prototype.createStream
var createStreamString = String(temp); var createStreamString = String(temp)
redis.RedisClient.prototype.createStream = function () { redis.RedisClient.prototype.createStream = function () {
this.connected = true; this.connected = true
this.ready = true; this.ready = true
}; }
client = new redis.RedisClient(); client = new redis.RedisClient()
assert.strictEqual(client.stream, undefined); assert.strictEqual(client.stream, undefined)
assert.strictEqual(client.ready, true); assert.strictEqual(client.ready, true)
assert.strictEqual(client.connected, true); assert.strictEqual(client.connected, true)
client.end = function () {}; client.end = function () {}
assert(createStreamString !== String(redis.RedisClient.prototype.createStream)); assert(createStreamString !== String(redis.RedisClient.prototype.createStream))
redis.RedisClient.prototype.createStream = temp; redis.RedisClient.prototype.createStream = temp
assert(createStreamString === String(redis.RedisClient.prototype.createStream)); assert(createStreamString === String(redis.RedisClient.prototype.createStream))
}); })
if (ip === 'IPv4') { 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) { 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'); client = redis.createClient('redis:///3?db=3')
assert.strictEqual(client.selectedDb, '3'); assert.strictEqual(client.selectedDb, '3')
client.on('ready', done); 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) { 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] + '/'); client = redis.createClient('redis://:porkchopsandwiches@' + config.HOST[ip] + '/')
var end = helper.callFuncAfter(done, 2); var end = helper.callFuncAfter(done, 2)
client.on('warning', function (msg) { client.on('warning', function (msg) {
assert.strictEqual(msg, 'Warning: Redis server does not require a password, but a password was supplied.'); assert.strictEqual(msg, 'Warning: Redis server does not require a password, but a password was supplied.')
end(); end()
}); })
client.on('ready', end); client.on('ready', end)
}); })
it('allows connecting with the redis url as first parameter and the options as second parameter', function (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', { client = redis.createClient('//127.0.0.1', {
connectTimeout: 1000 connectTimeout: 1000
}); })
assert.strictEqual(client.options.connectTimeout, 1000); assert.strictEqual(client.options.connectTimeout, 1000)
client.on('ready', done); 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) { 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({ client = redis.createClient({
url: 'http://foo:porkchopsandwiches@' + config.HOST[ip] + '/3' url: 'http://foo:porkchopsandwiches@' + config.HOST[ip] + '/3'
}); })
assert.strictEqual(client.authPass, 'porkchopsandwiches'); assert.strictEqual(client.authPass, 'porkchopsandwiches')
assert.strictEqual(+client.selectedDb, 3); assert.strictEqual(+client.selectedDb, 3)
assert(!client.options.port); assert(!client.options.port)
assert.strictEqual(client.options.host, config.HOST[ip]); assert.strictEqual(client.options.host, config.HOST[ip])
client.on('ready', done); client.on('ready', done)
}); })
it('allows connecting with the redis url and no auth and options as second parameter', function (done) { it('allows connecting with the redis url and no auth and options as second parameter', function (done) {
var options = { var options = {
detectBuffers: false detectBuffers: false
}; }
client = redis.createClient('redis://' + config.HOST[ip] + ':' + config.PORT, options); client = redis.createClient('redis://' + config.HOST[ip] + ':' + config.PORT, options)
assert.strictEqual(Object.keys(options).length, 1); assert.strictEqual(Object.keys(options).length, 1)
client.on('ready', done); client.on('ready', done)
}); })
it('allows connecting with the redis url and no auth and options as third parameter', function (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, { client = redis.createClient('redis://' + config.HOST[ip] + ':' + config.PORT, null, {
detectBuffers: false detectBuffers: false
}); })
client.on('ready', done); client.on('ready', done)
}); })
} }
it('redis still loading <= 500', function (done) { it('redis still loading <= 500', function (done) {
client = redis.createClient.apply(null, args); client = redis.createClient.apply(null, args)
var tmp = client.info.bind(client); var tmp = client.info.bind(client)
var end = helper.callFuncAfter(done, 3); var end = helper.callFuncAfter(done, 3)
var delayed = false; var delayed = false
var time; var time
// Mock original function and pretent redis is still loading // Mock original function and pretent redis is still loading
client.info = function (cb) { client.info = function (cb) {
tmp(function (err, res) { tmp(function (err, res) {
if (!delayed) { if (!delayed) {
assert(!err); assert(!err)
client.serverInfo.loading = 1; client.serverInfo.loading = 1
client.serverInfo.loading_eta_seconds = 0.5; client.serverInfo.loading_eta_seconds = 0.5
delayed = true; delayed = true
time = Date.now(); time = Date.now()
}
end()
cb(err, res)
})
} }
end();
cb(err, res);
});
};
client.on('ready', function () { client.on('ready', function () {
var rest = Date.now() - time; var rest = Date.now() - time
assert(rest >= 495, 'Rest should be equal or above 500 ms but is: ' + rest); // setTimeout might trigger early 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 // 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(rest - 250 < 500, 'Rest - 250 should be below 500 ms but is: ' + (rest - 250))
assert(delayed); assert(delayed)
end(); end()
}); })
}); })
it('redis still loading > 1000ms', function (done) { it('redis still loading > 1000ms', function (done) {
client = redis.createClient.apply(null, args); client = redis.createClient.apply(null, args)
var tmp = client.info.bind(client); var tmp = client.info.bind(client)
var end = helper.callFuncAfter(done, 3); var end = helper.callFuncAfter(done, 3)
var delayed = false; var delayed = false
var time; var time
// Mock original function and pretent redis is still loading // Mock original function and pretent redis is still loading
client.info = function (cb) { client.info = function (cb) {
tmp(function (err, res) { tmp(function (err, res) {
if (!delayed) { if (!delayed) {
assert(!err); assert(!err)
// Try reconnecting after one second even if redis tells us the time needed is above one second // Try reconnecting after one second even if redis tells us the time needed is above one second
client.serverInfo.loading = 1; client.serverInfo.loading = 1
client.serverInfo.loading_eta_seconds = 2.5; client.serverInfo.loading_eta_seconds = 2.5
delayed = true; delayed = true
time = Date.now(); time = Date.now()
}
end()
cb(err, res)
})
} }
end();
cb(err, res);
});
};
client.on('ready', function () { client.on('ready', function () {
var rest = Date.now() - time; var rest = Date.now() - time
assert(rest >= 998, '`rest` should be equal or above 1000 ms but is: ' + rest); // setTimeout might trigger early 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 // 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(rest - 250 < 1000, '`rest` - 250 should be below 1000 ms but is: ' + (rest - 250))
assert(delayed); assert(delayed)
end(); end()
}); })
}); })
})
}); })
})
}); })
});
});

View File

@@ -1,89 +1,88 @@
'use strict'; 'use strict'
var assert = require('assert'); var assert = require('assert')
var errors = require('../lib/customErrors'); var errors = require('../lib/customErrors')
describe('errors', function () { describe('errors', function () {
describe('AbortError', function () { describe('AbortError', function () {
it('should inherit from Error', function () { it('should inherit from Error', function () {
var e = new errors.AbortError({}); var e = new errors.AbortError({})
assert.strictEqual(e.message, ''); assert.strictEqual(e.message, '')
assert.strictEqual(e.name, 'AbortError'); assert.strictEqual(e.name, 'AbortError')
assert.strictEqual(Object.keys(e).length, 0); assert.strictEqual(Object.keys(e).length, 0)
assert(e instanceof Error); assert(e instanceof Error)
assert(e instanceof errors.AbortError); assert(e instanceof errors.AbortError)
}); })
it('should list options properties but not name and message', function () { it('should list options properties but not name and message', function () {
var e = new errors.AbortError({ var e = new errors.AbortError({
name: 'weird', name: 'weird',
message: 'hello world', message: 'hello world',
property: true property: true
}); })
assert.strictEqual(e.message, 'hello world'); assert.strictEqual(e.message, 'hello world')
assert.strictEqual(e.name, 'weird'); assert.strictEqual(e.name, 'weird')
assert.strictEqual(e.property, true); assert.strictEqual(e.property, true)
assert.strictEqual(Object.keys(e).length, 2); assert.strictEqual(Object.keys(e).length, 2)
assert(e instanceof Error); assert(e instanceof Error)
assert(e instanceof errors.AbortError); assert(e instanceof errors.AbortError)
assert(delete e.name); assert(delete e.name)
assert.strictEqual(e.name, 'AbortError'); assert.strictEqual(e.name, 'AbortError')
}); })
it('should change name and message', function () { it('should change name and message', function () {
var e = new errors.AbortError({ var e = new errors.AbortError({
message: 'hello world', message: 'hello world',
property: true property: true
}); })
assert.strictEqual(e.name, 'AbortError'); assert.strictEqual(e.name, 'AbortError')
assert.strictEqual(e.message, 'hello world'); assert.strictEqual(e.message, 'hello world')
e.name = 'foo'; e.name = 'foo'
e.message = 'foobar'; e.message = 'foobar'
assert.strictEqual(e.name, 'foo'); assert.strictEqual(e.name, 'foo')
assert.strictEqual(e.message, 'foobar'); assert.strictEqual(e.message, 'foobar')
}); })
}); })
describe('AggregateError', function () { describe('AggregateError', function () {
it('should inherit from Error and AbortError', function () { it('should inherit from Error and AbortError', function () {
var e = new errors.AggregateError({}); var e = new errors.AggregateError({})
assert.strictEqual(e.message, ''); assert.strictEqual(e.message, '')
assert.strictEqual(e.name, 'AggregateError'); assert.strictEqual(e.name, 'AggregateError')
assert.strictEqual(Object.keys(e).length, 0); assert.strictEqual(Object.keys(e).length, 0)
assert(e instanceof Error); assert(e instanceof Error)
assert(e instanceof errors.AggregateError); assert(e instanceof errors.AggregateError)
assert(e instanceof errors.AbortError); assert(e instanceof errors.AbortError)
}); })
it('should list options properties but not name and message', function () { it('should list options properties but not name and message', function () {
var e = new errors.AggregateError({ var e = new errors.AggregateError({
name: 'weird', name: 'weird',
message: 'hello world', message: 'hello world',
property: true property: true
}); })
assert.strictEqual(e.message, 'hello world'); assert.strictEqual(e.message, 'hello world')
assert.strictEqual(e.name, 'weird'); assert.strictEqual(e.name, 'weird')
assert.strictEqual(e.property, true); assert.strictEqual(e.property, true)
assert.strictEqual(Object.keys(e).length, 2); assert.strictEqual(Object.keys(e).length, 2)
assert(e instanceof Error); assert(e instanceof Error)
assert(e instanceof errors.AggregateError); assert(e instanceof errors.AggregateError)
assert(e instanceof errors.AbortError); assert(e instanceof errors.AbortError)
assert(delete e.name); assert(delete e.name)
assert.strictEqual(e.name, 'AggregateError'); assert.strictEqual(e.name, 'AggregateError')
}); })
it('should change name and message', function () { it('should change name and message', function () {
var e = new errors.AggregateError({ var e = new errors.AggregateError({
message: 'hello world', message: 'hello world',
property: true property: true
}); })
assert.strictEqual(e.name, 'AggregateError'); assert.strictEqual(e.name, 'AggregateError')
assert.strictEqual(e.message, 'hello world'); assert.strictEqual(e.message, 'hello world')
e.name = 'foo'; e.name = 'foo'
e.message = 'foobar'; e.message = 'foobar'
assert.strictEqual(e.name, 'foo'); assert.strictEqual(e.name, 'foo')
assert.strictEqual(e.message, 'foobar'); assert.strictEqual(e.message, 'foobar')
}); })
}); })
}); })

View File

@@ -1,268 +1,268 @@
'use strict'; 'use strict'
var assert = require('assert'); var Buffer = require('safe-buffer').Buffer
var config = require('./lib/config'); var assert = require('assert')
var helper = require('./helper'); var config = require('./lib/config')
var redis = config.redis; var helper = require('./helper')
var redis = config.redis
describe('detectBuffers', function () { describe('detectBuffers', function () {
var client
var client;
var args = config.configureClient('localhost', { var args = config.configureClient('localhost', {
detectBuffers: true detectBuffers: true
}); })
beforeEach(function (done) { beforeEach(function (done) {
client = redis.createClient.apply(null, args); client = redis.createClient.apply(null, args)
client.once('error', done); client.once('error', done)
client.once('connect', function () { client.once('connect', function () {
client.flushdb(function (err) { client.flushdb(function (err) {
client.hmset('hash key 2', 'key 1', 'val 1', 'key 2', 'val 2'); client.hmset('hash key 2', 'key 1', 'val 1', 'key 2', 'val 2')
client.set('string key 1', 'string value'); client.set('string key 1', 'string value')
return done(err); return done(err)
}); })
}); })
}); })
afterEach(function () { afterEach(function () {
client.end(true); client.end(true)
}); })
describe('get', function () { describe('get', function () {
describe('first argument is a string', function () { describe('first argument is a string', function () {
it('returns a string', function (done) { it('returns a string', function (done) {
client.get('string key 1', helper.isString('string value', done)); client.get('string key 1', helper.isString('string value', done))
}); })
it('returns a string when executed as part of transaction', function (done) { it('returns a string when executed as part of transaction', function (done) {
client.multi().get('string key 1').exec(function (err, res) { client.multi().get('string key 1').exec(function (err, res) {
helper.isString('string value', done)(err, res[0]); helper.isString('string value', done)(err, res[0])
}); })
}); })
}); })
describe('first argument is a buffer', function () { describe('first argument is a buffer', function () {
it('returns a buffer', function (done) { it('returns a buffer', function (done) {
client.get(new Buffer('string key 1'), function (err, reply) { client.get(Buffer.from('string key 1'), function (err, reply) {
assert.strictEqual(true, Buffer.isBuffer(reply)); assert.strictEqual(true, Buffer.isBuffer(reply))
assert.strictEqual('<Buffer 73 74 72 69 6e 67 20 76 61 6c 75 65>', reply.inspect()); assert.strictEqual('<Buffer 73 74 72 69 6e 67 20 76 61 6c 75 65>', reply.inspect())
return done(err); return done(err)
}); })
}); })
it('returns a bufffer when executed as part of transaction', function (done) { it('returns a bufffer when executed as part of transaction', function (done) {
client.multi().get(new Buffer('string key 1')).exec(function (err, reply) { client.multi().get(Buffer.from('string key 1')).exec(function (err, reply) {
assert.strictEqual(1, reply.length); assert.strictEqual(1, reply.length)
assert.strictEqual(true, Buffer.isBuffer(reply[0])); assert.strictEqual(true, Buffer.isBuffer(reply[0]))
assert.strictEqual('<Buffer 73 74 72 69 6e 67 20 76 61 6c 75 65>', reply[0].inspect()); assert.strictEqual('<Buffer 73 74 72 69 6e 67 20 76 61 6c 75 65>', reply[0].inspect())
return done(err); return done(err)
}); })
}); })
}); })
}); })
describe('multi.hget', function () { describe('multi.hget', function () {
it('can interleave string and buffer results', function (done) { it('can interleave string and buffer results', function (done) {
client.multi() client.multi()
.hget('hash key 2', 'key 1') .hget('hash key 2', 'key 1')
.hget(new Buffer('hash key 2'), 'key 1') .hget(Buffer.from('hash key 2'), 'key 1')
.hget('hash key 2', new Buffer('key 2')) .hget('hash key 2', Buffer.from('key 2'))
.hget('hash key 2', 'key 2') .hget('hash key 2', 'key 2')
.exec(function (err, reply) { .exec(function (err, reply) {
assert.strictEqual(true, Array.isArray(reply)); assert.strictEqual(true, Array.isArray(reply))
assert.strictEqual(4, reply.length); assert.strictEqual(4, reply.length)
assert.strictEqual('val 1', reply[0]); assert.strictEqual('val 1', reply[0])
assert.strictEqual(true, Buffer.isBuffer(reply[1])); assert.strictEqual(true, Buffer.isBuffer(reply[1]))
assert.strictEqual('<Buffer 76 61 6c 20 31>', reply[1].inspect()); assert.strictEqual('<Buffer 76 61 6c 20 31>', reply[1].inspect())
assert.strictEqual(true, Buffer.isBuffer(reply[2])); assert.strictEqual(true, Buffer.isBuffer(reply[2]))
assert.strictEqual('<Buffer 76 61 6c 20 32>', reply[2].inspect()); assert.strictEqual('<Buffer 76 61 6c 20 32>', reply[2].inspect())
assert.strictEqual('val 2', reply[3]); assert.strictEqual('val 2', reply[3])
return done(err); return done(err)
}); })
}); })
}); })
describe('batch.hget', function () { describe('batch.hget', function () {
it('can interleave string and buffer results', function (done) { it('can interleave string and buffer results', function (done) {
client.batch() client.batch()
.hget('hash key 2', 'key 1') .hget('hash key 2', 'key 1')
.hget(new Buffer('hash key 2'), 'key 1') .hget(Buffer.from('hash key 2'), 'key 1')
.hget('hash key 2', new Buffer('key 2')) .hget('hash key 2', Buffer.from('key 2'))
.hget('hash key 2', 'key 2') .hget('hash key 2', 'key 2')
.exec(function (err, reply) { .exec(function (err, reply) {
assert.strictEqual(true, Array.isArray(reply)); assert.strictEqual(true, Array.isArray(reply))
assert.strictEqual(4, reply.length); assert.strictEqual(4, reply.length)
assert.strictEqual('val 1', reply[0]); assert.strictEqual('val 1', reply[0])
assert.strictEqual(true, Buffer.isBuffer(reply[1])); assert.strictEqual(true, Buffer.isBuffer(reply[1]))
assert.strictEqual('<Buffer 76 61 6c 20 31>', reply[1].inspect()); assert.strictEqual('<Buffer 76 61 6c 20 31>', reply[1].inspect())
assert.strictEqual(true, Buffer.isBuffer(reply[2])); assert.strictEqual(true, Buffer.isBuffer(reply[2]))
assert.strictEqual('<Buffer 76 61 6c 20 32>', reply[2].inspect()); assert.strictEqual('<Buffer 76 61 6c 20 32>', reply[2].inspect())
assert.strictEqual('val 2', reply[3]); assert.strictEqual('val 2', reply[3])
return done(err); return done(err)
}); })
}); })
}); })
describe('hmget', function () { describe('hmget', function () {
describe('first argument is a string', function () { describe('first argument is a string', function () {
it('returns strings for keys requested', function (done) { it('returns strings for keys requested', function (done) {
client.hmget('hash key 2', 'key 1', 'key 2', function (err, reply) { client.hmget('hash key 2', 'key 1', 'key 2', function (err, reply) {
assert.strictEqual(true, Array.isArray(reply)); assert.strictEqual(true, Array.isArray(reply))
assert.strictEqual(2, reply.length); assert.strictEqual(2, reply.length)
assert.strictEqual('val 1', reply[0]); assert.strictEqual('val 1', reply[0])
assert.strictEqual('val 2', reply[1]); assert.strictEqual('val 2', reply[1])
return done(err); return done(err)
}); })
}); })
it('returns strings for keys requested in transaction', function (done) { it('returns strings for keys requested in transaction', function (done) {
client.multi().hmget('hash key 2', 'key 1', 'key 2').exec(function (err, reply) { client.multi().hmget('hash key 2', 'key 1', 'key 2').exec(function (err, reply) {
assert.strictEqual(true, Array.isArray(reply)); assert.strictEqual(true, Array.isArray(reply))
assert.strictEqual(1, reply.length); assert.strictEqual(1, reply.length)
assert.strictEqual(2, reply[0].length); assert.strictEqual(2, reply[0].length)
assert.strictEqual('val 1', reply[0][0]); assert.strictEqual('val 1', reply[0][0])
assert.strictEqual('val 2', reply[0][1]); assert.strictEqual('val 2', reply[0][1])
return done(err); return done(err)
}); })
}); })
it('handles array of strings with undefined values (repro #344)', function (done) { it('handles array of strings with undefined values (repro #344)', function (done) {
client.hmget('hash key 2', 'key 3', 'key 4', function (err, reply) { client.hmget('hash key 2', 'key 3', 'key 4', function (err, reply) {
assert.strictEqual(true, Array.isArray(reply)); assert.strictEqual(true, Array.isArray(reply))
assert.strictEqual(2, reply.length); assert.strictEqual(2, reply.length)
assert.equal(null, reply[0]); assert.strictEqual(null, reply[0])
assert.equal(null, reply[1]); assert.strictEqual(null, reply[1])
return done(err); return done(err)
}); })
}); })
it('handles array of strings with undefined values in transaction (repro #344)', function (done) { 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) { client.multi().hmget('hash key 2', 'key 3', 'key 4').exec(function (err, reply) {
assert.strictEqual(true, Array.isArray(reply)); assert.strictEqual(true, Array.isArray(reply))
assert.strictEqual(1, reply.length); assert.strictEqual(1, reply.length)
assert.strictEqual(2, reply[0].length); assert.strictEqual(2, reply[0].length)
assert.equal(null, reply[0][0]); assert.strictEqual(null, reply[0][0])
assert.equal(null, reply[0][1]); assert.strictEqual(null, reply[0][1])
return done(err); return done(err)
}); })
}); })
}); })
describe('first argument is a buffer', function () { describe('first argument is a buffer', function () {
it('returns buffers for keys requested', function (done) { it('returns buffers for keys requested', function (done) {
client.hmget(new Buffer('hash key 2'), 'key 1', 'key 2', function (err, reply) { client.hmget(Buffer.from('hash key 2'), 'key 1', 'key 2', function (err, reply) {
assert.strictEqual(true, Array.isArray(reply)); assert.strictEqual(true, Array.isArray(reply))
assert.strictEqual(2, reply.length); assert.strictEqual(2, reply.length)
assert.strictEqual(true, Buffer.isBuffer(reply[0])); assert.strictEqual(true, Buffer.isBuffer(reply[0]))
assert.strictEqual(true, Buffer.isBuffer(reply[1])); assert.strictEqual(true, Buffer.isBuffer(reply[1]))
assert.strictEqual('<Buffer 76 61 6c 20 31>', reply[0].inspect()); assert.strictEqual('<Buffer 76 61 6c 20 31>', reply[0].inspect())
assert.strictEqual('<Buffer 76 61 6c 20 32>', reply[1].inspect()); assert.strictEqual('<Buffer 76 61 6c 20 32>', reply[1].inspect())
return done(err); return done(err)
}); })
}); })
it('returns buffers for keys requested in transaction', function (done) { 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) { client.multi().hmget(Buffer.from('hash key 2'), 'key 1', 'key 2').exec(function (err, reply) {
assert.strictEqual(true, Array.isArray(reply)); assert.strictEqual(true, Array.isArray(reply))
assert.strictEqual(1, reply.length); assert.strictEqual(1, reply.length)
assert.strictEqual(2, reply[0].length); assert.strictEqual(2, reply[0].length)
assert.strictEqual(true, Buffer.isBuffer(reply[0][0])); assert.strictEqual(true, Buffer.isBuffer(reply[0][0]))
assert.strictEqual(true, Buffer.isBuffer(reply[0][1])); assert.strictEqual(true, Buffer.isBuffer(reply[0][1]))
assert.strictEqual('<Buffer 76 61 6c 20 31>', reply[0][0].inspect()); assert.strictEqual('<Buffer 76 61 6c 20 31>', reply[0][0].inspect())
assert.strictEqual('<Buffer 76 61 6c 20 32>', reply[0][1].inspect()); assert.strictEqual('<Buffer 76 61 6c 20 32>', reply[0][1].inspect())
return done(err); return done(err)
}); })
}); })
it('returns buffers for keys requested in .batch', function (done) { 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) { client.batch().hmget(Buffer.from('hash key 2'), 'key 1', 'key 2').exec(function (err, reply) {
assert.strictEqual(true, Array.isArray(reply)); assert.strictEqual(true, Array.isArray(reply))
assert.strictEqual(1, reply.length); assert.strictEqual(1, reply.length)
assert.strictEqual(2, reply[0].length); assert.strictEqual(2, reply[0].length)
assert.strictEqual(true, Buffer.isBuffer(reply[0][0])); assert.strictEqual(true, Buffer.isBuffer(reply[0][0]))
assert.strictEqual(true, Buffer.isBuffer(reply[0][1])); assert.strictEqual(true, Buffer.isBuffer(reply[0][1]))
assert.strictEqual('<Buffer 76 61 6c 20 31>', reply[0][0].inspect()); assert.strictEqual('<Buffer 76 61 6c 20 31>', reply[0][0].inspect())
assert.strictEqual('<Buffer 76 61 6c 20 32>', reply[0][1].inspect()); assert.strictEqual('<Buffer 76 61 6c 20 32>', reply[0][1].inspect())
return done(err); return done(err)
}); })
}); })
}); })
}); })
describe('hgetall', function (done) { describe('hgetall', function (done) {
describe('first argument is a string', function () { describe('first argument is a string', function () {
it('returns string values', function (done) { it('returns string values', function (done) {
client.hgetall('hash key 2', function (err, reply) { client.hgetall('hash key 2', function (err, reply) {
assert.strictEqual('object', typeof reply); assert.strictEqual('object', typeof reply)
assert.strictEqual(2, Object.keys(reply).length); assert.strictEqual(2, Object.keys(reply).length)
assert.strictEqual('val 1', reply['key 1']); assert.strictEqual('val 1', reply['key 1'])
assert.strictEqual('val 2', reply['key 2']); assert.strictEqual('val 2', reply['key 2'])
return done(err); return done(err)
}); })
}); })
it('returns string values when executed in transaction', function (done) { it('returns string values when executed in transaction', function (done) {
client.multi().hgetall('hash key 2').exec(function (err, reply) { client.multi().hgetall('hash key 2').exec(function (err, reply) {
assert.strictEqual(1, reply.length); assert.strictEqual(1, reply.length)
assert.strictEqual('object', typeof reply[0]); assert.strictEqual('object', typeof reply[0])
assert.strictEqual(2, Object.keys(reply[0]).length); assert.strictEqual(2, Object.keys(reply[0]).length)
assert.strictEqual('val 1', reply[0]['key 1']); assert.strictEqual('val 1', reply[0]['key 1'])
assert.strictEqual('val 2', reply[0]['key 2']); assert.strictEqual('val 2', reply[0]['key 2'])
return done(err); return done(err)
}); })
}); })
it('returns string values when executed in .batch', function (done) { it('returns string values when executed in .batch', function (done) {
client.batch().hgetall('hash key 2').exec(function (err, reply) { client.batch().hgetall('hash key 2').exec(function (err, reply) {
assert.strictEqual(1, reply.length); assert.strictEqual(1, reply.length)
assert.strictEqual('object', typeof reply[0]); assert.strictEqual('object', typeof reply[0])
assert.strictEqual(2, Object.keys(reply[0]).length); assert.strictEqual(2, Object.keys(reply[0]).length)
assert.strictEqual('val 1', reply[0]['key 1']); assert.strictEqual('val 1', reply[0]['key 1'])
assert.strictEqual('val 2', reply[0]['key 2']); assert.strictEqual('val 2', reply[0]['key 2'])
return done(err); return done(err)
}); })
}); })
}); })
describe('first argument is a buffer', function () { describe('first argument is a buffer', function () {
it('returns buffer values', function (done) { it('returns buffer values', function (done) {
client.hgetall(new Buffer('hash key 2'), function (err, reply) { client.hgetall(Buffer.from('hash key 2'), function (err, reply) {
assert.strictEqual(null, err); assert.strictEqual(null, err)
assert.strictEqual('object', typeof reply); assert.strictEqual('object', typeof reply)
assert.strictEqual(2, Object.keys(reply).length); assert.strictEqual(2, Object.keys(reply).length)
assert.strictEqual(true, Buffer.isBuffer(reply['key 1'])); assert.strictEqual(true, Buffer.isBuffer(reply['key 1']))
assert.strictEqual(true, Buffer.isBuffer(reply['key 2'])); assert.strictEqual(true, Buffer.isBuffer(reply['key 2']))
assert.strictEqual('<Buffer 76 61 6c 20 31>', reply['key 1'].inspect()); assert.strictEqual('<Buffer 76 61 6c 20 31>', reply['key 1'].inspect())
assert.strictEqual('<Buffer 76 61 6c 20 32>', reply['key 2'].inspect()); assert.strictEqual('<Buffer 76 61 6c 20 32>', reply['key 2'].inspect())
return done(err); return done(err)
}); })
}); })
it('returns buffer values when executed in transaction', function (done) { it('returns buffer values when executed in transaction', function (done) {
client.multi().hgetall(new Buffer('hash key 2')).exec(function (err, reply) { client.multi().hgetall(Buffer.from('hash key 2')).exec(function (err, reply) {
assert.strictEqual(1, reply.length); assert.strictEqual(1, reply.length)
assert.strictEqual('object', typeof reply[0]); assert.strictEqual('object', typeof reply[0])
assert.strictEqual(2, Object.keys(reply[0]).length); 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 1']))
assert.strictEqual(true, Buffer.isBuffer(reply[0]['key 2'])); assert.strictEqual(true, Buffer.isBuffer(reply[0]['key 2']))
assert.strictEqual('<Buffer 76 61 6c 20 31>', reply[0]['key 1'].inspect()); assert.strictEqual('<Buffer 76 61 6c 20 31>', reply[0]['key 1'].inspect())
assert.strictEqual('<Buffer 76 61 6c 20 32>', reply[0]['key 2'].inspect()); assert.strictEqual('<Buffer 76 61 6c 20 32>', reply[0]['key 2'].inspect())
return done(err); return done(err)
}); })
}); })
it('returns buffer values when executed in .batch', function (done) { it('returns buffer values when executed in .batch', function (done) {
client.batch().hgetall(new Buffer('hash key 2')).exec(function (err, reply) { client.batch().hgetall(Buffer.from('hash key 2')).exec(function (err, reply) {
assert.strictEqual(1, reply.length); assert.strictEqual(1, reply.length)
assert.strictEqual('object', typeof reply[0]); assert.strictEqual('object', typeof reply[0])
assert.strictEqual(2, Object.keys(reply[0]).length); 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 1']))
assert.strictEqual(true, Buffer.isBuffer(reply[0]['key 2'])); assert.strictEqual(true, Buffer.isBuffer(reply[0]['key 2']))
assert.strictEqual('<Buffer 76 61 6c 20 31>', reply[0]['key 1'].inspect()); assert.strictEqual('<Buffer 76 61 6c 20 31>', reply[0]['key 1'].inspect())
assert.strictEqual('<Buffer 76 61 6c 20 32>', reply[0]['key 2'].inspect()); assert.strictEqual('<Buffer 76 61 6c 20 32>', reply[0]['key 2'].inspect())
return done(err); return done(err)
}); })
}); })
}); })
}); })
}); })

View File

@@ -1,30 +1,29 @@
'use strict'; 'use strict'
var assert = require('assert'); var assert = require('assert')
var config = require('./lib/config'); var config = require('./lib/config')
var fork = require('child_process').fork; var fork = require('child_process').fork
var redis = config.redis; var redis = config.redis
describe('stack traces', function () { describe('stack traces', function () {
it('should return good traces with NODE_ENV=development set', function (done) { it('should return good traces with NODE_ENV=development set', function (done) {
var external = fork('./test/lib/good-traces.js', { var external = fork('./test/lib/good-traces.js', {
env: { env: {
NODE_ENV: 'development' NODE_ENV: 'development'
} }
}); })
var id = setTimeout(function () { var id = setTimeout(function () {
external.kill(); external.kill()
done(new Error('Timeout')); done(new Error('Timeout'))
}, 6000); }, 6000)
external.on('close', function (code) { external.on('close', function (code) {
clearTimeout(id); clearTimeout(id)
assert.strictEqual(code, 0); assert.strictEqual(code, 0)
done(); done()
}); })
}); })
it('should return good traces with NODE_DEBUG=redis env set', function (done) { it('should return good traces with NODE_DEBUG=redis env set', function (done) {
var external = fork('./test/lib/good-traces.js', { var external = fork('./test/lib/good-traces.js', {
@@ -32,28 +31,28 @@ describe('stack traces', function () {
NODE_DEBUG: 'redis' NODE_DEBUG: 'redis'
}, },
silent: true silent: true
}); })
var id = setTimeout(function () { var id = setTimeout(function () {
external.kill(); external.kill()
done(new Error('Timeout')); done(new Error('Timeout'))
}, 6000); }, 6000)
external.on('close', function (code) { external.on('close', function (code) {
clearTimeout(id); clearTimeout(id)
assert.strictEqual(code, 0); assert.strictEqual(code, 0)
done(); done()
}); })
}); })
// This is always going to return good stack traces // This is always going to return good stack traces
it('should always return good stack traces for rejected offline commands', function (done) { it('should always return good stack traces for rejected offline commands', function (done) {
var client = redis.createClient({ var client = redis.createClient({
enableOfflineQueue: false enableOfflineQueue: false
}); })
client.set('foo', function (err, res) { client.set('foo', function (err, res) {
assert(/good_traces.spec.js/.test(err.stack)); assert(/good_traces.spec.js/.test(err.stack))
client.quit(done); client.quit(done)
}); })
}); })
}); })

View File

@@ -1,236 +1,228 @@
'use strict'; 'use strict'
var assert = require('assert'); var assert = require('assert')
var path = require('path'); var path = require('path')
var config = require('./lib/config'); var config = require('./lib/config')
var RedisProcess = require('./lib/redis-process'); var RedisProcess = require('./lib/redis-process')
var StunnelProcess = require('./lib/stunnel-process'); var StunnelProcess = require('./lib/stunnel-process')
var rp; var rp
var stunnelProcess; var stunnelProcess
function startRedis (conf, done, port) { function startRedis (conf, done, port) {
RedisProcess.start(function (err, _rp) { RedisProcess.start(function (err, _rp) {
rp = _rp; rp = _rp
return done(err); return done(err)
}, path.resolve(__dirname, conf), port); }, path.resolve(__dirname, conf), port)
} }
// don't start redis every time we // don't start redis every time we
// include this helper file! // include this helper file!
if (!process.env.REDIS_TESTS_STARTED) { if (!process.env.REDIS_TESTS_STARTED) {
process.env.REDIS_TESTS_STARTED = true; process.env.REDIS_TESTS_STARTED = true
before(function (done) { before(function (done) {
startRedis('./conf/redis.conf', done); startRedis('./conf/redis.conf', done)
}); })
after(function (done) { after(function (done) {
if (rp) rp.stop(done); if (rp) rp.stop(done)
}); })
} }
function arrayHelper (results) { function arrayHelper (results) {
if (results instanceof Array) { if (results instanceof Array) {
assert.strictEqual(results.length, 1, 'The array length may only be one element'); assert.strictEqual(results.length, 1, 'The array length may only be one element')
return results[0]; return results[0]
} }
return results; 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 = { module.exports = {
redisProcess: function () { redisProcess: function () {
return rp; return rp
}, },
stopRedis: function (done) { stopRedis: function (done) {
rp.stop(done); rp.stop(done)
}, },
startRedis: startRedis, startRedis: startRedis,
stopStunnel: function (done) { stopStunnel: function (done) {
if (stunnelProcess) { if (stunnelProcess) {
StunnelProcess.stop(stunnelProcess, done); StunnelProcess.stop(stunnelProcess, done)
} else { } else {
done(); done()
} }
}, },
startStunnel: function (done) { startStunnel: function (done) {
StunnelProcess.start(function (err, _stunnelProcess) { StunnelProcess.start(function (err, _stunnelProcess) {
stunnelProcess = _stunnelProcess; stunnelProcess = _stunnelProcess
return done(err); return done(err)
}, path.resolve(__dirname, './conf')); }, path.resolve(__dirname, './conf'))
}, },
isNumber: function (expected, done) { isNumber: function (expected, done) {
return function (err, results) { return function (err, results) {
assert.strictEqual(err, null, 'expected ' + expected + ', got error: ' + err); assert.strictEqual(err, null, 'expected ' + expected + ', got error: ' + err)
results = arrayHelper(results); results = arrayHelper(results)
assert.strictEqual(results, expected, expected + ' !== ' + results); assert.strictEqual(results, expected, expected + ' !== ' + results)
assert.strictEqual(typeof results, 'number', 'expected a number, got ' + typeof results); assert.strictEqual(typeof results, 'number', 'expected a number, got ' + typeof results)
if (done) done(); if (done) done()
}; }
}, },
isString: function (str, done) { isString: function (str, done) {
str = '' + str; // Make sure it's a string str = '' + str // Make sure it's a string
return function (err, results) { return function (err, results) {
assert.strictEqual(err, null, "expected string '" + str + "', got error: " + err); assert.strictEqual(err, null, 'expected string \'' + str + '\', got error: ' + err)
results = arrayHelper(results); results = arrayHelper(results)
if (Buffer.isBuffer(results)) { // If options are passed to return either strings or buffers... results = toString(results)
results = results.toString(); assert.strictEqual(results, str, str + ' does not match ' + results)
if (done) done()
} }
assert.strictEqual(results, str, str + ' does not match ' + results);
if (done) done();
};
}, },
isNull: function (done) { isNull: function (done) {
return function (err, results) { return function (err, results) {
assert.strictEqual(err, null, 'expected null, got error: ' + err); assert.strictEqual(err, null, 'expected null, got error: ' + err)
results = arrayHelper(results); results = arrayHelper(results)
assert.strictEqual(results, null, results + ' is not null'); assert.strictEqual(results, null, results + ' is not null')
if (done) done(); if (done) done()
}; }
}, },
isUndefined: function (done) { isUndefined: function (done) {
return function (err, results) { return function (err, results) {
assert.strictEqual(err, null, 'expected null, got error: ' + err); assert.strictEqual(err, null, 'expected null, got error: ' + err)
results = arrayHelper(results); results = arrayHelper(results)
assert.strictEqual(results, undefined, results + ' is not undefined'); assert.strictEqual(results, undefined, results + ' is not undefined')
if (done) done(); if (done) done()
}; }
}, },
isError: function (done) { isError: function (done) {
return function (err, results) { return function (err, results) {
assert(err instanceof Error, "err is not instance of 'Error', but an error is expected here."); assert(err instanceof Error, 'err is not instance of \'Error\', but an error is expected here.')
if (done) done(); if (done) done()
}; }
}, },
isNotError: function (done) { isNotError: function (done) {
return function (err, results) { return function (err, results) {
assert.strictEqual(err, null, 'expected success, got an error: ' + err); assert.strictEqual(err, null, 'expected success, got an error: ' + err)
if (done) done(); 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: { isType: {
number: function (done) { number: function (done) {
return function (err, results) { return function (err, results) {
assert.strictEqual(err, null, 'expected any number, got error: ' + err); assert.strictEqual(err, null, 'expected any number, got error: ' + err)
assert.strictEqual(typeof results, 'number', results + ' is not a number'); assert.strictEqual(typeof results, 'number', results + ' is not a number')
if (done) done(); if (done) done()
}; }
}, },
string: function (done) { string: function (done) {
return function (err, results) { return function (err, results) {
assert.strictEqual(err, null, 'expected any string, got error: ' + err); assert.strictEqual(err, null, 'expected any string, got error: ' + err)
assert.strictEqual(typeof results, 'string', results + ' is not a string'); assert.strictEqual(typeof results, 'string', results + ' is not a string')
if (done) done(); if (done) done()
}; }
}, },
positiveNumber: function (done) { positiveNumber: function (done) {
return function (err, results) { return function (err, results) {
assert.strictEqual(err, null, 'expected positive number, got error: ' + err); assert.strictEqual(err, null, 'expected positive number, got error: ' + err)
assert(results > 0, results + ' is not a positive number'); assert(results > 0, results + ' is not a positive number')
if (done) done(); 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) { match: function (pattern, done) {
return function (err, results) { return function (err, results) {
assert.strictEqual(err, null, 'expected ' + pattern.toString() + ', got error: ' + err); assert.strictEqual(err, null, 'expected ' + pattern.toString() + ', got error: ' + err)
results = arrayHelper(results); results = arrayHelper(results)
assert(pattern.test(results), "expected string '" + results + "' to match " + pattern.toString()); assert(pattern.test(results), 'expected string \'' + results + '\' to match ' + pattern.toString())
if (done) done(); if (done) done()
}; }
}, },
serverVersionAtLeast: function (connection, desiredVersion) { serverVersionAtLeast: function (connection, desiredVersion) {
// Wait until a connection has established (otherwise a timeout is going to be triggered at some point) // Wait until a connection has established (otherwise a timeout is going to be triggered at some point)
if (Object.keys(connection.serverInfo).length === 0) { if (Object.keys(connection.serverInfo).length === 0) {
throw new Error('Version check not possible as the client is not yet ready or did not expose the version'); throw new Error('Version check not possible as the client is not yet ready or did not expose the version')
} }
// Return true if the server version >= desiredVersion // Return true if the server version >= desiredVersion
var version = connection.serverInfo.versions; var version = connection.serverInfo.versions
for (var i = 0; i < 3; i++) { for (var i = 0; i < 3; i++) {
if (version[i] > desiredVersion[i]) { if (version[i] > desiredVersion[i]) {
return true; return true
} }
if (version[i] < desiredVersion[i]) { if (version[i] < desiredVersion[i]) {
if (this.skip) this.skip(); if (this.skip) this.skip()
return false; return false
} }
} }
return true; return true
}, },
allTests: function (opts, cb) { allTests: function (opts, cb) {
if (!cb) { if (!cb) {
cb = opts; cb = opts
opts = {}; opts = {}
} }
var protocols = ['IPv4']; var protocols = ['IPv4']
if (process.platform !== 'win32') { if (process.platform !== 'win32') {
protocols.push('IPv6', '/tmp/redis.sock'); protocols.push('IPv6', '/tmp/redis.sock')
} }
var options = [{ var options = [{
detectBuffers: true detectBuffers: true
}, { }, {
detectBuffers: false detectBuffers: false
}]; }]
options.forEach(function (options) { options.forEach(function (options) {
var strOptions = ''; var strOptions = ''
var key; var key
for (key in options) { for (key in options) {
if (options.hasOwnProperty(key)) { if (options.hasOwnProperty(key)) {
strOptions += key + ': ' + options[key] + '; '; strOptions += key + ': ' + options[key] + '; '
} }
} }
describe('using options: ' + strOptions, function () { describe('using options: ' + strOptions, function () {
protocols.forEach(function (ip, i) { protocols.forEach(function (ip, i) {
if (i !== 0 && !opts.allConnections) { if (i !== 0 && !opts.allConnections) {
return; return
} }
cb(ip, config.configureClient(ip, options)); cb(ip, config.configureClient(ip, options))
}); })
}); })
}); })
}, },
removeMochaListener: function () { removeMochaListener: function () {
var mochaListener = process.listeners('uncaughtException').pop(); var mochaListener = process.listeners('uncaughtException').pop()
process.removeListener('uncaughtException', mochaListener); process.removeListener('uncaughtException', mochaListener)
return mochaListener; return mochaListener
}, },
callFuncAfter: function (func, max) { callFuncAfter: function (func, max) {
var i = 0; var i = 0
return function (err) { return function (err) {
if (err) { if (err) {
throw err; throw err
} }
i++; i++
if (i >= max) { if (i >= max) {
func(); func()
return true; return true
}
return false
} }
return false;
};
}, },
killConnection: function (client) { killConnection: function (client) {
// Change the connection option to a non existing one and destroy the stream // Change the connection option to a non existing one and destroy the stream
@@ -238,8 +230,8 @@ module.exports = {
port: 65535, port: 65535,
host: '127.0.0.1', host: '127.0.0.1',
family: 4 family: 4
};
client.address = '127.0.0.1:65535';
client.stream.destroy();
} }
}; client.address = '127.0.0.1:65535'
client.stream.destroy()
}
}

View File

@@ -1,13 +1,13 @@
'use strict'; 'use strict'
// helpers for configuring a redis client in // helpers for configuring a redis client in
// its various modes, ipV6, ipV4, socket. // its various modes, ipV6, ipV4, socket.
var redis = require('../../index'); var redis = require('../../index')
var bluebird = require('bluebird'); var bluebird = require('bluebird')
// Promisify everything // Promisify everything
bluebird.promisifyAll(redis.RedisClient.prototype); bluebird.promisifyAll(redis.RedisClient.prototype)
bluebird.promisifyAll(redis.Multi.prototype); bluebird.promisifyAll(redis.Multi.prototype)
var config = { var config = {
redis: redis, redis: redis,
@@ -17,22 +17,22 @@ var config = {
IPv6: '::1' IPv6: '::1'
}, },
configureClient: function (ip, opts) { configureClient: function (ip, opts) {
var args = []; var args = []
// Do not manipulate the opts => copy them each time // Do not manipulate the opts => copy them each time
opts = opts ? JSON.parse(JSON.stringify(opts)) : {}; opts = opts ? JSON.parse(JSON.stringify(opts)) : {}
if (ip.match(/\.sock/)) { if (ip.match(/\.sock/)) {
args.push(ip); args.push(ip)
} else { } else {
args.push(config.PORT); args.push(config.PORT)
args.push(config.HOST[ip]); args.push(config.HOST[ip])
opts.family = ip; opts.family = ip
} }
args.push(opts); args.push(opts)
return args; return args
} }
}; }
module.exports = config; module.exports = config

View File

@@ -1,20 +1,20 @@
// Spawned by the goodStacks.spec.js tests // Spawned by the goodStacks.spec.js tests
'use strict'; 'use strict'
var assert = require('assert'); var assert = require('assert')
var redis = require('../../index'); var redis = require('../../index')
var client = redis.createClient(); var client = redis.createClient()
// Both error cases would normally return bad stack traces // Both error cases would normally return bad stack traces
client.set('foo', function (err, res) { client.set('foo', function (err, res) {
assert(/good-traces.js:9:8/.test(err.stack)); assert(/good-traces.js:9:8/.test(err.stack))
client.set('foo', 'bar', function (err, res) { client.set('foo', 'bar', function (err, res) {
assert(/good-traces.js:11:12/.test(err.stack)); assert(/good-traces.js:11:10/.test(err.stack))
client.quit(function () { client.quit(function () {
process.exit(0); process.exit(0)
}); })
}); })
process.nextTick(function () { process.nextTick(function () {
client.stream.destroy(); client.stream.destroy()
}); })
}); })

View File

@@ -1,64 +1,64 @@
'use strict'; 'use strict'
// helper to start and stop the redis process. // helper to start and stop the redis process.
var config = require('./config'); var config = require('./config')
var fs = require('fs'); var fs = require('fs')
var path = require('path'); var path = require('path')
var spawn = require('win-spawn'); var spawn = require('win-spawn')
var tcpPortUsed = require('tcp-port-used'); var tcpPortUsed = require('tcp-port-used')
var bluebird = require('bluebird'); var bluebird = require('bluebird')
// wait for redis to be listening in // wait for redis to be listening in
// all three modes (ipv4, ipv6, socket). // all three modes (ipv4, ipv6, socket).
function waitForRedis (available, cb, port) { function waitForRedis (available, cb, port) {
if (process.platform === 'win32') return cb(); if (process.platform === 'win32') return cb()
var time = Date.now(); var time = Date.now()
var running = false; var running = false
var socket = '/tmp/redis.sock'; var socket = '/tmp/redis.sock'
if (port) { if (port) {
// We have to distinguishe the redis sockets if we have more than a single redis instance running // We have to distinguish the redis sockets if we have more than a single redis instance running
socket = '/tmp/redis' + port + '.sock'; socket = '/tmp/redis' + port + '.sock'
} }
port = port || config.PORT; port = port || config.PORT
var id = setInterval(function () { var id = setInterval(function () {
if (running) return; if (running) return
running = true; running = true
bluebird.join( bluebird.join(
tcpPortUsed.check(port, '127.0.0.1'), tcpPortUsed.check(port, '127.0.0.1'),
tcpPortUsed.check(port, '::1'), tcpPortUsed.check(port, '::1'),
function (ipV4, ipV6) { function (ipV4, ipV6) {
if (ipV6 === available && ipV4 === available) { if (ipV6 === available && ipV4 === available) {
if (fs.existsSync(socket) === available) { if (fs.existsSync(socket) === available) {
clearInterval(id); clearInterval(id)
return cb(); return cb()
} }
// The same message applies for can't stop but we ignore that case // 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'); throw new Error('Port ' + port + ' is already in use. Tests can\'t start.\n')
} }
if (Date.now() - time > 6000) { if (Date.now() - time > 6000) {
throw new Error('Redis could not start on port ' + (port || config.PORT) + '\n'); throw new Error('Redis could not start on port ' + (port || config.PORT) + '\n')
} }
running = false; running = false
}).catch(function (err) { }).catch(function (err) {
console.error('\x1b[31m' + err.stack + '\x1b[0m\n'); console.error('\x1b[31m' + err.stack + '\x1b[0m\n')
process.exit(1); process.exit(1)
}); })
}, 100); }, 100)
} }
module.exports = { module.exports = {
start: function (done, conf, port) { start: function (done, conf, port) {
var spawnFailed = false; var spawnFailed = false
// spawn redis with our testing configuration. // spawn redis with our testing configuration.
var confFile = conf || path.resolve(__dirname, '../conf/redis.conf'); var confFile = conf || path.resolve(__dirname, '../conf/redis.conf')
var rp = spawn('redis-server', [confFile], {}); var rp = spawn('redis-server', [confFile], {})
// capture a failure booting redis, and give // capture a failure booting redis, and give
// the user running the test some directions. // the user running the test some directions.
rp.once('exit', function (code) { rp.once('exit', function (code) {
if (code !== 0) spawnFailed = true; if (code !== 0) spawnFailed = true
}); })
// wait for redis to become available, by // wait for redis to become available, by
// checking the port we bind on. // checking the port we bind on.
@@ -67,22 +67,22 @@ module.exports = {
// an after() block to shutdown redis. // an after() block to shutdown redis.
return done(null, { return done(null, {
spawnFailed: function () { spawnFailed: function () {
return spawnFailed; return spawnFailed
}, },
stop: function (done) { stop: function (done) {
if (spawnFailed) return done(); if (spawnFailed) return done()
rp.once('exit', function (code) { rp.once('exit', function (code) {
var error = null; var error = null
if (code !== null && code !== 0) { if (code !== null && code !== 0) {
error = new Error('Redis shutdown failed with code ' + code); error = new Error('Redis shutdown failed with code ' + code)
} }
waitForRedis(false, function () { waitForRedis(false, function () {
return done(error); return done(error)
}, port); }, port)
}); })
rp.kill('SIGTERM'); rp.kill('SIGTERM')
} }
}); })
}, port); }, port)
} }
}; }

View File

@@ -1,83 +1,83 @@
'use strict'; 'use strict'
// helper to start and stop the stunnel process. // helper to start and stop the stunnel process.
var spawn = require('child_process').spawn; var spawn = require('child_process').spawn
var EventEmitter = require('events'); var EventEmitter = require('events')
var fs = require('fs'); var fs = require('fs')
var path = require('path'); var path = require('path')
var util = require('util'); var util = require('util')
function once (cb) { function once (cb) {
var called = false; var called = false
return function () { return function () {
if (called) return; if (called) return
called = true; called = true
cb.apply(this, arguments); cb.apply(this, arguments)
}; }
} }
function StunnelProcess (confDir) { function StunnelProcess (confDir) {
EventEmitter.call(this); EventEmitter.call(this)
// set up an stunnel to redis; edit the conf file to include required absolute paths // set up an stunnel to redis; edit the conf file to include required absolute paths
var confFile = path.resolve(confDir, 'stunnel.conf'); var confFile = path.resolve(confDir, 'stunnel.conf')
var confText = fs.readFileSync(confFile + '.template').toString().replace(/__dirname,/g, confDir); var confText = fs.readFileSync(confFile + '.template').toString().replace(/__dirname,/g, confDir)
fs.writeFileSync(confFile, confText); fs.writeFileSync(confFile, confText)
var stunnel = this.stunnel = spawn('stunnel', [confFile]); var stunnel = this.stunnel = spawn('stunnel', [confFile])
// handle child process events, and failure to set up tunnel // handle child process events, and failure to set up tunnel
var self = this; var self = this
this.timer = setTimeout(function () { this.timer = setTimeout(function () {
self.emit('error', new Error('Timeout waiting for stunnel to start')); self.emit('error', new Error('Timeout waiting for stunnel to start'))
}, 8000); }, 8000)
stunnel.on('error', function (err) { stunnel.on('error', function (err) {
self.clear(); self.clear()
self.emit('error', err); self.emit('error', err)
}); })
stunnel.on('exit', function (code) { stunnel.on('exit', function (code) {
self.clear(); self.clear()
if (code === 0) { if (code === 0) {
self.emit('stopped'); self.emit('stopped')
} else { } else {
self.emit('error', new Error('Stunnel exited unexpectedly; code = ' + code)); self.emit('error', new Error('Stunnel exited unexpectedly; code = ' + code))
} }
}); })
// wait to stunnel to start // wait to stunnel to start
stunnel.stderr.on('data', function (data) { stunnel.stderr.on('data', function (data) {
if (data.toString().match(/Service.+redis.+bound/)) { if (data.toString().match(/Service.+redis.+bound/)) {
clearTimeout(this.timer); clearTimeout(this.timer)
self.emit('started'); self.emit('started')
} }
}); })
} }
util.inherits(StunnelProcess, EventEmitter); util.inherits(StunnelProcess, EventEmitter)
StunnelProcess.prototype.clear = function () { StunnelProcess.prototype.clear = function () {
this.stunnel = null; this.stunnel = null
clearTimeout(this.timer); clearTimeout(this.timer)
}; }
StunnelProcess.prototype.stop = function (done) { StunnelProcess.prototype.stop = function (done) {
if (this.stunnel) { if (this.stunnel) {
this.stunnel.kill(); this.stunnel.kill()
} }
}; }
module.exports = { module.exports = {
start: function (done, confDir) { start: function (done, confDir) {
done = once(done); done = once(done)
var stunnel = new StunnelProcess(confDir); var stunnel = new StunnelProcess(confDir)
stunnel.once('error', done.bind(done)); stunnel.once('error', done.bind(done))
stunnel.once('started', done.bind(done, null, stunnel)); stunnel.once('started', done.bind(done, null, stunnel))
}, },
stop: function (stunnel, done) { stop: function (stunnel, done) {
stunnel.removeAllListeners(); stunnel.removeAllListeners()
stunnel.stop(); stunnel.stop()
stunnel.once('error', done.bind(done)); stunnel.once('error', done.bind(done))
stunnel.once('stopped', done.bind(done, null)); stunnel.once('stopped', done.bind(done, null))
} }
}; }

View File

@@ -1,17 +1,17 @@
// spawned by the unref tests in nodeRedis.spec.js. // spawned by the unref tests in nodeRedis.spec.js.
// when configured, unref causes the client to exit // when configured, unref causes the client to exit
// as soon as there are no outstanding commands. // as soon as there are no outstanding commands.
'use strict'; 'use strict'
var redis = require('../../index'); var redis = require('../../index')
var HOST = process.argv[2] || '127.0.0.1'; var HOST = process.argv[2] || '127.0.0.1'
var PORT = process.argv[3]; var PORT = process.argv[3]
var args = PORT ? [PORT, HOST] : [HOST]; 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) { c.info(function (err, reply) {
if (err) process.exit(-1); if (err) process.exit(-1)
if (!reply.length) process.exit(-1); if (!reply.length) process.exit(-1)
process.stdout.write(reply.length.toString()); process.stdout.write(reply.length.toString())
}); })
c.unref(); c.unref()

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@@ -1,118 +1,94 @@
'use strict'; 'use strict'
var assert = require('assert'); var assert = require('assert')
var config = require('./lib/config'); var config = require('./lib/config')
var helper = require('./helper'); var helper = require('./helper')
var redis = config.redis; var redis = config.redis
describe('prefix key names', function () { describe('prefix key names', function () {
helper.allTests(function (ip, args) { helper.allTests(function (ip, args) {
describe('using ' + ip, function () { describe('using ' + ip, function () {
var client = null; var client = null
beforeEach(function (done) { beforeEach(function (done) {
client = redis.createClient({ client = redis.createClient({
prefix: 'test:prefix:' prefix: 'test:prefix:'
}); })
client.on('ready', function () { client.on('ready', function () {
client.flushdb(function (err) { client.flushdb(function (err) {
done(err); done(err)
}); })
}); })
}); })
afterEach(function () { afterEach(function () {
client.end(true); client.end(true)
}); })
it('auto prefix set / get', function (done) { it('auto prefix set / get', function (done) {
client.set('key', 'value', function (err, reply) { client.set('key', 'value', helper.isString('OK'))
assert.strictEqual(reply, 'OK'); client.get('key', helper.isString('value'))
});
client.get('key', function (err, reply) {
assert.strictEqual(reply, 'value');
});
client.getrange('key', 1, -1, function (err, reply) { client.getrange('key', 1, -1, function (err, reply) {
assert.strictEqual(reply, 'alue'); assert.strictEqual(reply, 'alue')
assert.strictEqual(err, null); assert.strictEqual(err, null)
}); })
client.exists('key', function (err, res) { client.exists('key', helper.isNumber(1))
assert.strictEqual(res, 1);
});
client.exists('test:prefix:key', function (err, res) {
// The key will be prefixed itself // The key will be prefixed itself
assert.strictEqual(res, 0); client.exists('test:prefix:key', helper.isNumber(0))
}); client.mset('key2', 'value2', 'key3', 'value3')
client.mset('key2', 'value2', 'key3', 'value3');
client.keys('*', function (err, res) { client.keys('*', function (err, res) {
assert.strictEqual(res.length, 3); assert.strictEqual(err, null)
assert(res.indexOf('test:prefix:key') !== -1); assert.strictEqual(res.length, 3)
assert(res.indexOf('test:prefix:key2') !== -1); assert(res.indexOf('test:prefix:key') !== -1)
assert(res.indexOf('test:prefix:key3') !== -1); assert(res.indexOf('test:prefix:key2') !== -1)
done(); assert(res.indexOf('test:prefix:key3') !== -1)
}); done()
}); })
})
it('auto prefix set / get with .batch', function (done) { it('auto prefix set / get with .batch', function (done) {
var batch = client.batch(); var batch = client.batch()
batch.set('key', 'value', function (err, reply) { batch.set('key', 'value', helper.isString('OK'))
assert.strictEqual(reply, 'OK'); batch.get('key', helper.isString('value'))
});
batch.get('key', function (err, reply) {
assert.strictEqual(reply, 'value');
});
batch.getrange('key', 1, -1, function (err, reply) { batch.getrange('key', 1, -1, function (err, reply) {
assert.strictEqual(reply, 'alue'); assert.strictEqual(reply, 'alue')
assert.strictEqual(err, null); assert.strictEqual(err, null)
}); })
batch.exists('key', function (err, res) { batch.exists('key', helper.isNumber(1))
assert.strictEqual(res, 1);
});
batch.exists('test:prefix:key', function (err, res) {
// The key will be prefixed itself // The key will be prefixed itself
assert.strictEqual(res, 0); batch.exists('test:prefix:key', helper.isNumber(0))
}); batch.mset('key2', 'value2', 'key3', 'value3')
batch.mset('key2', 'value2', 'key3', 'value3');
batch.keys('*', function (err, res) { batch.keys('*', function (err, res) {
assert.strictEqual(res.length, 3); assert.strictEqual(err, null)
assert(res.indexOf('test:prefix:key') !== -1); assert.strictEqual(res.length, 3)
assert(res.indexOf('test:prefix:key2') !== -1); assert(res.indexOf('test:prefix:key') !== -1)
assert(res.indexOf('test:prefix:key3') !== -1); assert(res.indexOf('test:prefix:key2') !== -1)
}); assert(res.indexOf('test:prefix:key3') !== -1)
batch.exec(done); })
}); batch.exec(done)
})
it('auto prefix set / get with .multi', function (done) { it('auto prefix set / get with .multi', function (done) {
var multi = client.multi(); var multi = client.multi()
multi.set('key', 'value', function (err, reply) { multi.set('key', 'value', helper.isString('OK'))
assert.strictEqual(reply, 'OK'); multi.get('key', helper.isString('value'))
});
multi.get('key', function (err, reply) {
assert.strictEqual(reply, 'value');
});
multi.getrange('key', 1, -1, function (err, reply) { multi.getrange('key', 1, -1, function (err, reply) {
assert.strictEqual(reply, 'alue'); assert.strictEqual(reply, 'alue')
assert.strictEqual(err, null); assert.strictEqual(err, null)
}); })
multi.exists('key', function (err, res) { multi.exists('key', helper.isNumber(1))
assert.strictEqual(res, 1);
});
multi.exists('test:prefix:key', function (err, res) {
// The key will be prefixed itself // The key will be prefixed itself
assert.strictEqual(res, 0); multi.exists('test:prefix:key', helper.isNumber(0))
}); multi.mset('key2', 'value2', 'key3', 'value3')
multi.mset('key2', 'value2', 'key3', 'value3');
multi.keys('*', function (err, res) { multi.keys('*', function (err, res) {
assert.strictEqual(res.length, 3); assert.strictEqual(err, null)
assert(res.indexOf('test:prefix:key') !== -1); assert.strictEqual(res.length, 3)
assert(res.indexOf('test:prefix:key2') !== -1); assert(res.indexOf('test:prefix:key') !== -1)
assert(res.indexOf('test:prefix:key3') !== -1); assert(res.indexOf('test:prefix:key2') !== -1)
}); assert(res.indexOf('test:prefix:key3') !== -1)
multi.exec(done); })
}); multi.exec(done)
})
}); })
}); })
}); })

Some files were not shown because too many files have changed in this diff Show More