diff --git a/test/batch.spec.js b/test/batch.spec.js index a99cb82173..ba4c9020bb 100644 --- a/test/batch.spec.js +++ b/test/batch.spec.js @@ -63,11 +63,16 @@ describe("The 'batch' method", function () { client.end(true); }); - it("returns an empty array", function (done) { + it("returns an empty array and keep the execution order in takt", function (done) { + var called = false; + client.set('foo', 'bar', function (err, res) { + called = true; + }); var batch = client.batch(); batch.exec(function (err, res) { assert.strictEqual(err, null); assert.strictEqual(res.length, 0); + assert(called); done(); }); }); @@ -328,10 +333,11 @@ describe("The 'batch' method", function () { .exec(done); }); - it("should work without any callback", function (done) { + it("should work without any callback or arguments", function (done) { var batch = client.batch(); batch.set("baz", "binary"); batch.set("foo", "bar"); + batch.ping(); batch.exec(); client.get('foo', helper.isString('bar', done)); diff --git a/test/commands/set.spec.js b/test/commands/set.spec.js index c486629733..3235d634f6 100644 --- a/test/commands/set.spec.js +++ b/test/commands/set.spec.js @@ -66,14 +66,21 @@ describe("The 'set' method", function () { }); }); - describe("with undefined 'key' and missing 'value' parameter", function () { - it("reports an error", function (done) { + describe("reports an error with invalid parameters", function () { + it("undefined 'key' and missing 'value' parameter", function (done) { client.set(undefined, function (err, res) { helper.isError()(err, null); assert.equal(err.command, 'SET'); done(); }); }); + + it("empty array as second parameter", function (done) { + client.set('foo', [], function (err, res) { + assert.strictEqual(err.message, "ERR wrong number of arguments for 'set' command"); + done(); + }); + }); }); }); diff --git a/test/connection.spec.js b/test/connection.spec.js index 5f5892dea2..c047788847 100644 --- a/test/connection.spec.js +++ b/test/connection.spec.js @@ -136,13 +136,14 @@ describe("connection tests", function () { var time = Date.now(); client = redis.createClient({ parser: parser, - host: '192.168.74.167', // Should be auto detected as ipv4 + // Auto detect ipv4 and use non routable ip to trigger the timeout + host: '10.255.255.1', connect_timeout: connect_timeout }); process.nextTick(function() { - assert(client.stream._events.timeout); + assert.strictEqual(client.stream.listeners('timeout').length, 1); }); - assert.strictEqual(client.address, '192.168.74.167:6379'); + assert.strictEqual(client.address, '10.255.255.1:6379'); assert.strictEqual(client.connection_options.family, 4); client.on("reconnecting", function (params) { @@ -151,8 +152,8 @@ describe("connection tests", function () { client.on('error', function(err) { assert(/Redis connection in broken state: connection timeout.*?exceeded./.test(err.message)); - assert(Date.now() - time < connect_timeout + 50); - assert(Date.now() - time >= connect_timeout - 50); // Somehow this is triggered to early at times + assert(Date.now() - time < connect_timeout + 25); + assert(Date.now() - time >= connect_timeout - 3); // Timers sometimes trigger early (e.g. 1ms to early) done(); }); }); @@ -165,7 +166,7 @@ describe("connection tests", function () { assert.strictEqual(client.address, '2001:db8::ff00:42:8329:6379'); assert.strictEqual(client.connection_options.family, 6); process.nextTick(function() { - assert.strictEqual(client.stream._events.timeout, undefined); + assert.strictEqual(client.stream.listeners('timeout').length, 0); }); }); @@ -179,7 +180,7 @@ describe("connection tests", function () { }); client.on('connect', function () { assert.strictEqual(client.stream._idleTimeout, -1); - assert.strictEqual(client.stream._events.timeout, undefined); + assert.strictEqual(client.stream.listeners('timeout').length, 0); client.on('ready', done); }); }); diff --git a/test/multi.spec.js b/test/multi.spec.js index 4d17c927ef..766d579875 100644 --- a/test/multi.spec.js +++ b/test/multi.spec.js @@ -241,6 +241,7 @@ describe("The 'multi' method", function () { multi1.set("m1", "123"); multi1.get('m1'); multi2.get('m2'); + multi2.ping(); multi1.exec(end); multi2.exec(function(err, res) { @@ -538,7 +539,7 @@ describe("The 'multi' method", function () { client.get('foo', helper.isString('bar', done)); }); - it("should not use a transaction with exec_atomic if only no command is used", function () { + it("should not use a transaction with exec_atomic if no command is used", function () { var multi = client.multi(); var test = false; multi.exec_batch = function () { diff --git a/test/node_redis.spec.js b/test/node_redis.spec.js index 12107993a9..ef5090c3a8 100644 --- a/test/node_redis.spec.js +++ b/test/node_redis.spec.js @@ -423,6 +423,41 @@ describe("The node_redis client", function () { }); }); + describe('execution order / fire query while loading', function () { + it('keep execution order for commands that may fire while redis is still loading', function (done) { + client = redis.createClient.apply(null, args); + var fired = false; + client.set('foo', 'bar', function (err, res) { + assert(fired === false); + done(); + }); + client.info(function (err, res) { + fired = true; + }); + }); + + it('should fire early', function (done) { + client = redis.createClient.apply(null, args); + var fired = false; + client.info(function (err, res) { + fired = true; + }); + client.set('foo', 'bar', function (err, res) { + assert(fired); + done(); + }); + assert.strictEqual(client.offline_queue.length, 1); + assert.strictEqual(client.command_queue.length, 1); + client.on('connect', function () { + assert.strictEqual(client.offline_queue.length, 1); + assert.strictEqual(client.command_queue.length, 1); + }); + client.on('ready', function () { + assert.strictEqual(client.offline_queue.length, 0); + }); + }); + }); + describe('socket_nodelay', function () { describe('true', function () { var args = config.configureClient(parser, ip, { diff --git a/test/rename.spec.js b/test/rename.spec.js index 5ac7435138..9607210567 100644 --- a/test/rename.spec.js +++ b/test/rename.spec.js @@ -109,6 +109,25 @@ describe("rename commands", function () { }); }); + it("should also work prefixed commands", function (done) { + if (helper.redisProcess().spawnFailed()) this.skip(); + + client.end(true); + client = redis.createClient({ + rename_commands: { + set: '807081f5afa96845a02816a28b7258c3' + }, + parser: parser, + prefix: 'baz' + }); + client.set('foo', 'bar'); + client.keys('*', function(err, reply) { + assert.strictEqual(reply[0], 'bazfoo'); + assert.strictEqual(err, null); + done(); + }); + }); + }); }); diff --git a/test/utils.spec.js b/test/utils.spec.js new file mode 100644 index 0000000000..d75e035486 --- /dev/null +++ b/test/utils.spec.js @@ -0,0 +1,129 @@ +'use strict'; + +var assert = require('assert'); +var Queue = require('double-ended-queue'); +var utils = require('../lib/utils'); + +describe('utils.js', function () { + + describe('clone', function () { + it('ignore the object prototype and clone a nested array / object', function () { + var obj = { + a: [null, 'foo', ['bar'], { + "I'm special": true + }], + number: 5, + fn: function noop () {} + }; + var clone = utils.clone(obj); + assert.deepEqual(clone, obj); + assert.strictEqual(obj.fn, clone.fn); + assert(typeof clone.fn === 'function'); + }); + + it('replace faulty values with an empty object as return value', function () { + var a = utils.clone(); + var b = utils.clone(null); + assert.strictEqual(Object.keys(a).length, 0); + assert.strictEqual(Object.keys(b).length, 0); + }); + + it('throws on circular data', function () { + try { + var a = {}; + a.b = a; + utils.clone(a); + throw new Error('failed'); + } catch (e) { + assert(e.message !== 'failed'); + } + }); + }); + + describe('reply_in_order', function () { + + var err_count = 0; + var res_count = 0; + var emitted = false; + var clientMock = { + emit: function () { emitted = true; }, + offline_queue: new Queue(), + command_queue: new Queue() + }; + var create_command_obj = function () { + return { + callback: function (err, res) { + if (err) err_count++; + else res_count++; + } + }; + }; + + beforeEach(function () { + clientMock.offline_queue.clear(); + clientMock.command_queue.clear(); + err_count = 0; + res_count = 0; + emitted = false; + }); + + it('no elements in either queue. Reply in the next tick', function (done) { + var called = false; + utils.reply_in_order(clientMock, function () { + called = true; + done(); + }, null, null); + assert(!called); + }); + + it('no elements in either queue. Reply in the next tick', function (done) { + assert(!emitted); + utils.reply_in_order(clientMock, null, new Error('tada')); + assert(!emitted); + setTimeout(function () { + assert(emitted); + done(); + }, 1); + }); + + it('elements in the offline queue. Reply after the offline queue is empty and respect the command_obj callback', function (done) { + clientMock.offline_queue.push(create_command_obj(), create_command_obj()); + utils.reply_in_order(clientMock, function () { + assert.strictEqual(clientMock.offline_queue.length, 0); + assert.strictEqual(res_count, 2); + done(); + }, null, null); + while (clientMock.offline_queue.length) clientMock.offline_queue.shift().callback(null, 'foo'); + }); + + it('elements in the offline queue. Reply after the offline queue is empty and respect the command_obj error emit', function (done) { + clientMock.command_queue.push({}, create_command_obj(), {}); + utils.reply_in_order(clientMock, function () { + assert.strictEqual(clientMock.command_queue.length, 0); + assert(emitted); + assert.strictEqual(err_count, 1); + assert.strictEqual(res_count, 0); + done(); + }, null, null); + while (clientMock.command_queue.length) { + var command_obj = clientMock.command_queue.shift(); + if (command_obj.callback) { + command_obj.callback(new Error('tada')); + } + } + }); + + it('elements in the offline queue. Reply after the offline queue is empty and respect the command_obj', function (done) { + clientMock.command_queue.push(create_command_obj(), {}); + utils.reply_in_order(clientMock, function () { + assert.strictEqual(clientMock.command_queue.length, 0); + assert(!emitted); + assert.strictEqual(res_count, 1); + done(); + }, null, null); + while (clientMock.command_queue.length) { + clientMock.command_queue.shift().callback(null, 'bar'); + } + }); + }); +});