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

Make sure all individual handled command work in multi context the same

Fix quit possibly resulting in reconnections
This commit is contained in:
Ruben Bridgewater
2016-04-14 01:14:41 +02:00
parent 0424cb0bf3
commit 3038c9043d
7 changed files with 334 additions and 99 deletions

View File

@@ -291,6 +291,48 @@ describe('client authentication', function () {
});
});
});
it('indivdual commands work properly with batch', function (done) {
// quit => might return an error instead of "OK" in the exec callback... (if not connected)
// auth => might return an error instead of "OK" in the exec callback... (if no password is required / still loading on Redis <= 2.4)
// This could be fixed by checking the return value of the callback in the exec callback and
// returning the manipulated [error, result] from the callback.
// There should be a better solution though
var args = config.configureClient(parser, 'localhost', {
noReadyCheck: true
});
client = redis.createClient.apply(redis.createClient, args);
assert.strictEqual(client.selected_db, undefined);
var end = helper.callFuncAfter(done, 8);
client.on('monitor', function () {
end(); // Should be called for each command after monitor
});
client.batch()
.auth(auth)
.SELECT(5, function (err, res) {
assert.strictEqual(client.selected_db, 5);
assert.strictEqual(res, 'OK');
assert.notDeepEqual(client.serverInfo.db5, { avg_ttl: 0, expires: 0, keys: 1 });
})
.monitor()
.set('foo', 'bar', helper.isString('OK'))
.INFO(function (err, res) {
assert.strictEqual(res.indexOf('# Server\r\nredis_version:'), 0);
assert.deepEqual(client.serverInfo.db5, { avg_ttl: 0, expires: 0, keys: 1 });
})
.get('foo', helper.isString('bar'))
.subscribe(['foo', 'bar'])
.unsubscribe('foo')
.SUBSCRIBE('/foo', helper.isString('/foo'))
.psubscribe('*')
.quit(helper.isString('OK')) // this might be interesting
.exec(function (err, res) {
res[4] = res[4].substr(0, 10);
assert.deepEqual(res, ['OK', 'OK', 'OK', 'OK', '# Server\r\n', 'bar', 'bar', 'foo', '/foo', '*', 'OK']);
end();
});
});
});
});

View File

@@ -12,8 +12,6 @@ describe("The 'batch' method", function () {
describe('using ' + parser + ' and ' + ip, function () {
describe('when not connected', function () {
// TODO: This is somewhat broken and should be fixed in v.3
// The commands should return an error instead of returning an empty result
var client;
beforeEach(function (done) {
@@ -24,7 +22,7 @@ describe("The 'batch' method", function () {
client.on('end', done);
});
it('returns an empty array', function (done) {
it('returns an empty array for missing commands', function (done) {
var batch = client.batch();
batch.exec(function (err, res) {
assert.strictEqual(err, null);
@@ -33,7 +31,17 @@ describe("The 'batch' method", function () {
});
});
it('returns an empty array if promisified', function () {
it('returns an error for batch with commands', function (done) {
var batch = client.batch();
batch.set('foo', 'bar');
batch.exec(function (err, res) {
assert.strictEqual(err, null);
assert.strictEqual(res[0].code, 'NR_OFFLINE');
done();
});
});
it('returns an empty array for missing commands if promisified', function () {
return client.batch().execAsync().then(function (res) {
assert.strictEqual(res.length, 0);
});

View File

@@ -93,6 +93,27 @@ describe('connection tests', function () {
assert.strictEqual(bool, false);
});
it('calling quit while connected without offline queue should end the connection when all commands have finished', function (done) {
var called = false;
client = redis.createClient({
enable_offline_queue: false
});
client.on('ready', function () {
client.set('foo', 'bar', function (err, res) {
assert.strictEqual(res, 'OK');
called = true;
});
var bool = client.quit(function (err, res) {
assert.strictEqual(res, 'OK');
assert.strictEqual(err, null);
assert(called);
done();
});
// TODO: In v.3 the quit command would be fired right away, so bool should be true
assert.strictEqual(bool, true);
});
});
it('do not quit before connected or a connection issue is detected', function (done) {
client = redis.createClient();
client.set('foo', 'bar', helper.isString('OK'));

View File

@@ -628,6 +628,40 @@ describe("The 'multi' method", function () {
client.stream.destroy();
});
it('indivdual commands work properly with multi', function (done) {
// Neither of the following work properly in a transactions:
// (This is due to Redis not returning the reply as expected / resulting in undefined behavior)
// (Likely there are more commands that do not work with a transaction)
//
// auth => can't be called after a multi command
// monitor => results in faulty return values e.g. multi().monitor().set('foo', 'bar').get('foo')
// returns ['OK, 'OK', 'monitor reply'] instead of ['OK', 'OK', 'bar']
// quit => ends the connection before the exec
// client reply skip|off => results in weird return values. Not sure what exactly happens
// subscribe => enters subscribe mode and this does not work in combination with exec (the same for psubscribe, unsubscribe...)
//
assert.strictEqual(client.selected_db, undefined);
var multi = client.multi();
multi.select(5, function (err, res) {
assert.strictEqual(client.selected_db, 5);
assert.strictEqual(res, 'OK');
assert.notDeepEqual(client.server_info.db5, { avg_ttl: 0, expires: 0, keys: 1 });
});
// multi.client('reply', 'on', helper.isString('OK')); // Redis v.3.2
multi.set('foo', 'bar', helper.isString('OK'));
multi.info(function (err, res) {
assert.strictEqual(res.indexOf('# Server\r\nredis_version:'), 0);
assert.deepEqual(client.server_info.db5, { avg_ttl: 0, expires: 0, keys: 1 });
});
multi.get('foo', helper.isString('bar'));
multi.exec(function (err, res) {
res[3] = res[3].substr(0, 10);
assert.deepEqual(res, ['OK', 'OK', '# Server\r\n', 'bar']);
done();
});
});
});
});
});

View File

@@ -618,6 +618,26 @@ describe('The node_redis client', function () {
});
});
it('monitors reconnects properly and works with the offline queue in a batch statement', function (done) {
var i = 0;
var multi = client.batch();
multi.MONITOR(helper.isString('OK'));
multi.mget('hello', 'world');
multi.exec(function (err, res) {
assert.deepEqual(res, ['OK', [null, null]]);
});
client.on('monitor', function (time, args, rawOutput) {
assert(utils.monitor_regex.test(rawOutput), rawOutput);
assert.deepEqual(args, ['mget', 'hello', 'world']);
if (i++ === 2) {
// End after two reconnects
return done();
}
client.stream.destroy();
client.mget('hello', 'world');
});
});
it('monitor does not activate if the command could not be processed properly', function (done) {
client.MONITOR(function (err, res) {
assert.strictEqual(err.code, 'UNCERTAIN_STATE');
@@ -748,26 +768,27 @@ describe('The node_redis client', function () {
});
});
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);
});
});
// TODO: consider allowing loading commands in v.3
// it('should fire early', function (done) {
// client = redis.createClient.apply(null, args);
// var fired = false;
// client.info(function (err, res) {
// fired = true;
// });
// client.set('foo', 'bar', function (err, res) {
// assert(fired);
// done();
// });
// assert.strictEqual(client.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 () {