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

Add tests and improve older tests

This commit is contained in:
Ruben Bridgewater
2015-12-30 16:14:23 +01:00
parent 2cd3818ea9
commit 5ef24a90b6
12 changed files with 199 additions and 98 deletions

View File

@@ -308,7 +308,6 @@ RedisClient.prototype.on_ready = function () {
if (this.old_state !== null) { if (this.old_state !== null) {
this.monitoring = this.old_state.monitoring; this.monitoring = this.old_state.monitoring;
this.pub_sub_mode = this.old_state.pub_sub_mode; this.pub_sub_mode = this.old_state.pub_sub_mode;
this.selected_db = this.old_state.selected_db;
this.old_state = null; this.old_state = null;
} }
@@ -456,13 +455,11 @@ RedisClient.prototype.connection_gone = function (why) {
if (this.old_state === null) { if (this.old_state === null) {
var state = { var state = {
monitoring: this.monitoring, monitoring: this.monitoring,
pub_sub_mode: this.pub_sub_mode, pub_sub_mode: this.pub_sub_mode
selected_db: this.selected_db
}; };
this.old_state = state; this.old_state = state;
this.monitoring = false; this.monitoring = false;
this.pub_sub_mode = false; this.pub_sub_mode = false;
this.selected_db = undefined;
} }
// since we are collapsing end and close, users don't expect to be called twice // since we are collapsing end and close, users don't expect to be called twice
@@ -940,7 +937,7 @@ RedisClient.prototype.select = RedisClient.prototype.SELECT = function (db, call
}); });
}; };
// Store db in this.select_db to restore it on reconnect // Store info in this.server_info after each call
RedisClient.prototype.info = RedisClient.prototype.INFO = function (callback) { RedisClient.prototype.info = RedisClient.prototype.INFO = function (callback) {
var self = this; var self = this;
this.send_anyway = true; this.send_anyway = true;

View File

@@ -67,12 +67,38 @@ describe("client authentication", function () {
}); });
if (ip === 'IPv4') { if (ip === 'IPv4') {
it('allows auth to be provided as part of redis url', 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();
client = redis.createClient('redis://foo:' + auth + '@' + config.HOST[ip] + ':' + config.PORT); var end = helper.callFuncAfter(done, 2);
client = redis.createClient('redis://:' + auth + '@' + config.HOST[ip] + ':' + config.PORT);
client.on("ready", function () { client.on("ready", function () {
return done(); end();
});
// The info command may be used while loading but not if not yet authenticated
client.info(function (err, res) {
assert(!err);
end();
});
});
it('allows auth and database to be provided as part of redis url query parameter', function (done) {
if (helper.redisProcess().spawnFailed()) this.skip();
client = redis.createClient('redis://' + config.HOST[ip] + ':' + config.PORT + '?db=2&password=' + auth);
assert.strictEqual(client.options.db, '2');
assert.strictEqual(client.options.password, auth);
assert.strictEqual(client.auth_pass, auth);
client.on("ready", function () {
// Set a key so the used database is returned in the info command
client.set('foo', 'bar');
client.get('foo');
assert.strictEqual(client.server_info.db2, undefined);
// Using the info command should update the server_info
client.info(function (err, res) {
assert(typeof client.server_info.db2 === 'object');
});
client.flushdb(done);
}); });
}); });
} }
@@ -93,7 +119,7 @@ describe("client authentication", function () {
if (helper.redisProcess().spawnFailed()) this.skip(); if (helper.redisProcess().spawnFailed()) this.skip();
var args = config.configureClient(parser, ip, { var args = config.configureClient(parser, ip, {
auth_pass: auth, password: auth,
no_ready_check: true no_ready_check: true
}); });
client = redis.createClient.apply(redis.createClient, args); client = redis.createClient.apply(redis.createClient, args);
@@ -195,6 +221,18 @@ describe("client authentication", function () {
done(); done();
}); });
}); });
it('should emit an error if the provided password is faulty', function (done) {
if (helper.redisProcess().spawnFailed()) this.skip();
client = redis.createClient({
password: 'wrong_password',
parser: parser
});
client.once("error", function (err) {
assert.strictEqual(err.message, 'ERR invalid password');
done();
});
});
}); });
}); });

View File

@@ -179,7 +179,7 @@ describe("The 'batch' method", function () {
client.BATCH([ client.BATCH([
["smembers", ["some set"]], ["smembers", ["some set"]],
["del", "some set"], ["del", "some set"],
["smembers", "some set"] ["smembers", "some set", undefined] // The explicit undefined is handled as a callback that is undefined
]) ])
.scard("some set") .scard("some set")
.exec(function (err, replies) { .exec(function (err, replies) {

View File

@@ -45,12 +45,40 @@ describe("The 'hset' method", function () {
}); });
}); });
it('does not error when a buffer and array are set as fields on the same hash', function (done) { it('throws a error if someone passed a array either as field or as value', function (done) {
var hash = "test hash";
var field = "array";
// This would be converted to "array contents" but if you use more than one entry,
// it'll result in e.g. "array contents,second content" and this is not supported and considered harmful
var value = ["array contents"];
try {
client.HMSET(hash, field, value);
throw new Error('test failed');
} catch (err) {
if (/invalid data/.test(err.message)) {
done();
} else {
done(err);
}
}
});
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 = new Buffer("abcdefghij");
var field2 = "array"; var field2 = "date";
var value2 = ["array contents"]; var value2 = new Date();
client.HMSET(hash, field1, value1, field2, value2, helper.isString("OK", done));
});
it('does not error when a buffer and date are set as fields on the same hash', function (done) {
var hash = "test hash";
var value1 = "buffer";
var field1 = new Buffer("abcdefghij");
var value2 = "date";
var field2 = new Date();
client.HMSET(hash, field1, value1, field2, value2, helper.isString("OK", done)); client.HMSET(hash, field1, value1, field2, value2, helper.isString("OK", done));
}); });

View File

@@ -0,0 +1,51 @@
'use strict';
var assert = require('assert');
var config = require("../lib/config");
var helper = require('../helper');
var redis = config.redis;
describe("The 'info' method", function () {
helper.allTests(function(parser, ip, args) {
describe("using " + parser + " and " + ip, function () {
var client;
before(function (done) {
client = redis.createClient.apply(redis.createClient, args);
client.once("ready", function () {
client.flushall(done);
});
});
after(function () {
client.end(true);
});
it("update server_info after a info command", function (done) {
client.set('foo', 'bar');
client.info();
client.select(2, function () {
assert.strictEqual(client.server_info.db2, undefined);
});
client.set('foo', 'bar');
client.info();
setTimeout(function () {
assert.strictEqual(typeof client.server_info.db2, 'object');
done();
}, 150);
});
it("emit error after a failure", function (done) {
client.info();
client.once('error', function (err) {
assert.strictEqual(err.code, 'UNCERTAIN_STATE');
assert.strictEqual(err.command, 'INFO');
done();
});
client.stream.destroy();
});
});
});
});

View File

@@ -14,10 +14,9 @@ describe("The 'keys' method", function () {
var client; var client;
beforeEach(function (done) { beforeEach(function (done) {
args = args || {};
client = redis.createClient.apply(redis.createClient, args); client = redis.createClient.apply(redis.createClient, args);
client.once("ready", function () { client.once("ready", function () {
client.flushdb(done); client.flushall(done);
}); });
}); });

View File

@@ -37,7 +37,9 @@ describe("The 'select' method", function () {
beforeEach(function (done) { beforeEach(function (done) {
client = redis.createClient.apply(redis.createClient, args); client = redis.createClient.apply(redis.createClient, args);
client.once("ready", function () { done(); }); client.once("ready", function () {
client.flushdb(done);
});
}); });
afterEach(function () { afterEach(function () {
@@ -46,7 +48,7 @@ describe("The 'select' method", function () {
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.selected_db, null, "default db should be null"); assert.strictEqual(client.selected_db, 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.selected_db, 1, "db should be 1 after select"); assert.strictEqual(client.selected_db, 1, "db should be 1 after select");
@@ -58,7 +60,7 @@ describe("The 'select' method", function () {
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.selected_db, null, "default db should be null"); assert.strictEqual(client.selected_db, undefined, "default db should be undefined");
client.select(1, function (err) { client.select(1, function (err) {
assert.equal(err, null); assert.equal(err, null);
assert.equal(client.selected_db, 1, "we should have selected the new valid DB"); assert.equal(client.selected_db, 1, "we should have selected the new valid DB");
@@ -69,7 +71,7 @@ describe("The 'select' method", function () {
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.selected_db, null, "default db should be null"); assert.strictEqual(client.selected_db, undefined, "default db should be undefined");
client.select(9999, function (err) { client.select(9999, function (err) {
assert.equal(err.code, 'ERR'); assert.equal(err.code, 'ERR');
assert.equal(err.message, 'ERR invalid DB index'); assert.equal(err.message, 'ERR invalid DB index');
@@ -82,7 +84,7 @@ describe("The 'select' method", function () {
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.selected_db, null, "default db should be null"); assert.strictEqual(client.selected_db, undefined, "default db should be undefined");
client.select(1); client.select(1);
setTimeout(function () { setTimeout(function () {
assert.equal(client.selected_db, 1, "we should have selected the new valid DB"); assert.equal(client.selected_db, 1, "we should have selected the new valid DB");
@@ -93,7 +95,7 @@ describe("The 'select' method", function () {
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.selected_db, null, "default db should be null"); assert.strictEqual(client.selected_db, 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');
@@ -105,6 +107,21 @@ describe("The 'select' method", function () {
}); });
}); });
}); });
describe("reconnection occurs", function () {
it("selects the appropriate database after a reconnect", function (done) {
assert.strictEqual(client.selected_db, undefined, "default db should be undefined");
client.select(3);
client.set('foo', 'bar', function () {
client.stream.destroy();
});
client.once('ready', function () {
assert.strictEqual(client.selected_db, 3);
assert(typeof client.server_info.db3 === 'object');
done();
});
});
});
}); });
}); });
}); });

View File

@@ -81,32 +81,17 @@ describe("The 'set' method", 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);
setTimeout(function () { client.get(key, helper.isString(value, done));
client.get(key, function (err, res) {
helper.isString(value)(err, res);
done();
});
}, 100);
}); });
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);
setTimeout(function () { client.get(key, helper.isString(value, done));
client.get(key, function (err, res) {
helper.isString(value)(err, res);
done();
});
}, 100);
}); });
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]);
setTimeout(function () { client.get(key, helper.isString(value, done));
client.get(key, function (err, res) {
helper.isString(value)(err, res);
done();
});
}, 100);
}); });
}); });
@@ -121,6 +106,12 @@ describe("The 'set' method", function () {
}); });
}); });
// TODO: This test has to be refactored from v.3.0 on to expect an error instead
it("converts null to 'null'", function (done) {
client.set('foo', null);
client.get('foo', helper.isString('null', done));
});
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.equal(err.message, "ERR wrong number of arguments for 'set' command");

View File

@@ -1,44 +0,0 @@
'use strict';
var assert = require('assert');
var config = require("../lib/config");
var helper = require("../helper");
var redis = config.redis;
describe.skip("The 'sync' method", function () {
helper.allTests(function(parser, ip, args) {
describe("using " + parser + " and " + ip, function () {
var client;
beforeEach(function (done) {
client = redis.createClient.apply(redis.createClient, args);
client.once("ready", function () {
client.flushdb(done);
});
});
// This produces a parser error
// "Protocol error, got "K" as reply type byte"
// I'm uncertain if this is correct behavior or not
// TODO: Fix the command queue state error occuring
it('try to sync with the server and fail other commands', function (done) {
client.on('error', function(err) {
assert.equal(err.message, 'Protocol error, got "K" as reply type byte');
assert.equal(err.command, 'SET');
done();
});
client.sync(function(err, res) {
assert.equal(err, null);
assert(!!res);
});
client.set('foo', 'bar');
});
afterEach(function () {
client.end(true);
});
});
});
});

View File

@@ -152,6 +152,7 @@ describe("connection tests", function () {
client.on('error', function(err) { client.on('error', function(err) {
assert(/Redis connection in broken state: connection timeout.*?exceeded./.test(err.message)); 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);
assert(Date.now() - time >= connect_timeout);
done(); done();
}); });
}); });
@@ -358,20 +359,20 @@ describe("connection tests", function () {
redis.createClient(config.HOST[ip] + ':' + config.PORT); redis.createClient(config.HOST[ip] + ':' + config.PORT);
throw new Error('failed'); throw new Error('failed');
} catch (err) { } catch (err) {
assert.equal(err.message, 'Connection string must use the "redis:" protocol'); assert.equal(err.message, 'Connection string must use the "redis:" protocol or begin with slashes //');
} }
}); });
if (ip === 'IPv4') { if (ip === 'IPv4') {
it('allows connecting with the redis url and the default port', function (done) { it('allows connecting with the redis url and the default port', function (done) {
client = redis.createClient('redis://foo:porkchopsandwiches@' + config.HOST[ip]); client = redis.createClient('redis://:porkchopsandwiches@' + config.HOST[ip] + '/');
client.on("ready", function () { client.on("ready", function () {
return done(); return done();
}); });
}); });
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('redis://127.0.0.1', { client = redis.createClient('//127.0.0.1', {
connect_timeout: 1000 connect_timeout: 1000
}); });
assert.strictEqual(client.options.connect_timeout, 1000); assert.strictEqual(client.options.connect_timeout, 1000);
@@ -380,11 +381,12 @@ describe("connection tests", function () {
}); });
}); });
it('allows connecting with the redis url in the options object', 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: 'redis://foo:porkchopsandwiches@' + config.HOST[ip] url: 'http://foo:porkchopsandwiches@' + config.HOST[ip] + '/3'
}); });
assert.strictEqual(client.options.auth_pass, 'porkchopsandwiches'); assert.strictEqual(client.auth_pass, 'porkchopsandwiches');
assert.strictEqual(+client.selected_db, 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", function () { client.on("ready", function () {
@@ -424,7 +426,8 @@ describe("connection tests", function () {
tmp(function(err, res) { tmp(function(err, res) {
if (!delayed) { if (!delayed) {
assert(!err); assert(!err);
res = res.toString().replace(/loading:0/, 'loading:1\r\nloading_eta_seconds:0.5'); client.server_info.loading = 1;
client.server_info.loading_eta_seconds = 0.5;
delayed = true; delayed = true;
time = Date.now(); time = Date.now();
} }
@@ -454,7 +457,8 @@ describe("connection tests", function () {
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
res = res.toString().replace(/loading:0/, 'loading:1\r\nloading_eta_seconds:2.5'); client.server_info.loading = 1;
client.server_info.loading_eta_seconds = 2.5;
delayed = true; delayed = true;
time = Date.now(); time = Date.now();
} }

View File

@@ -335,7 +335,7 @@ describe("The 'multi' method", function () {
["hmset", arr3, helper.isString('OK')], ["hmset", arr3, helper.isString('OK')],
['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", "multihmset", ["multibar", "multibaz"]], ["HMSET", "multihmset", ["multibar", "multibaz"], undefined], // undefined is used as a explicit not set callback variable
["hmset", "multihmset", ["multibar", "multibaz"], helper.isString('OK')], ["hmset", "multihmset", ["multibar", "multibaz"], helper.isString('OK')],
]) ])
.hmget(now, 123456789, 'otherTypes') .hmget(now, 123456789, 'otherTypes')

View File

@@ -8,9 +8,7 @@ var redis = config.redis;
describe("The node_redis client", function () { describe("The node_redis client", function () {
helper.allTests({ helper.allTests(function(parser, ip, args) {
allConnections: true
}, function(parser, ip, args) {
describe("using " + parser + " and " + ip, function () { describe("using " + parser + " and " + ip, function () {
var client; var client;
@@ -128,7 +126,7 @@ describe("The node_redis client", function () {
it("misusing the function should eventually throw (no command)", function (done) { it("misusing the function should eventually throw (no command)", function (done) {
client.send_command(true, 'info', function (err, res) { client.send_command(true, 'info', function (err, res) {
assert(/ERR Protocol error/.test(err.message)); assert(/ERR Protocol error/.test(err.message));
assert.equal(err.command, true); assert.equal(err.command, undefined);
assert.equal(err.code, 'ERR'); assert.equal(err.code, 'ERR');
done(); done();
}); });
@@ -143,6 +141,28 @@ describe("The node_redis client", function () {
}); });
describe("retry_unfulfilled_commands", function () {
it("should retry all commands instead of returning an error if a command did not yet return after a connection loss", function (done) {
var bclient = redis.createClient({
parser: parser,
retry_unfulfilled_commands: true
});
bclient.blpop("blocking list 2", 5, function (err, value) {
assert.strictEqual(value[0], "blocking list 2");
assert.strictEqual(value[1], "initial value");
return done(err);
});
bclient.once('ready', function () {
setTimeout(function () {
bclient.stream.destroy();
client.rpush("blocking list 2", "initial value", helper.isNumber(1));
}, 100);
});
});
});
describe(".end", function () { describe(".end", function () {
it('used without flush / flush set to false', function(done) { it('used without flush / flush set to false', function(done) {
@@ -191,8 +211,8 @@ describe("The node_redis client", function () {
it("return an error in the callback", function (done) { it("return an error in the callback", function (done) {
if (helper.redisProcess().spawnFailed()) this.skip(); if (helper.redisProcess().spawnFailed()) this.skip();
// TODO: Investigate why this test is failing hard and killing mocha if the client is created with .apply // TODO: Investigate why this test is failing hard and killing mocha.
// Seems like something is wrong while passing a socket connection to create client! args[1] // Seems like something is wrong with nyc while passing a socket connection to create client!
client = redis.createClient(); client = redis.createClient();
client.quit(function() { client.quit(function() {
client.get("foo", function(err, res) { client.get("foo", function(err, res) {