You've already forked node-redis
mirror of
https://github.com/redis/node-redis.git
synced 2025-08-06 02:15:48 +03:00
Fix some minor issues and add more tests
Do not mutate the options object and add some more tests
This commit is contained in:
16
README.md
16
README.md
@@ -175,8 +175,7 @@ port and host are probably fine and you don't need to supply any arguments. `cre
|
|||||||
* `redis.createClient('redis://user:pass@host:port', options)`
|
* `redis.createClient('redis://user:pass@host:port', options)`
|
||||||
* `redis.createClient(port, host, options)`
|
* `redis.createClient(port, host, options)`
|
||||||
|
|
||||||
`options` is an object with the following possible properties:
|
### `options` is an object with the following possible properties:
|
||||||
|
|
||||||
* `host`: *127.0.0.1*; The host to connect to
|
* `host`: *127.0.0.1*; The host to connect to
|
||||||
* `port`: *6370*; The port to connect to
|
* `port`: *6370*; The port to connect to
|
||||||
* `parser`: *hiredis*; Which Redis protocol reply parser to use. If `hiredis` is not installed it will fallback to `javascript`.
|
* `parser`: *hiredis*; Which Redis protocol reply parser to use. If `hiredis` is not installed it will fallback to `javascript`.
|
||||||
@@ -229,7 +228,7 @@ client.get(new Buffer("foo_rand000000000000"), function (err, reply) {
|
|||||||
client.end();
|
client.end();
|
||||||
```
|
```
|
||||||
|
|
||||||
## client.auth(password, callback)
|
## client.auth(password[, callback])
|
||||||
|
|
||||||
When connecting to a Redis server that requires authentication, the `AUTH` command must be sent as the
|
When connecting to a Redis server that requires authentication, the `AUTH` command must be sent as the
|
||||||
first command after connecting. This can be tricky to coordinate with reconnections, the ready check,
|
first command after connecting. This can be tricky to coordinate with reconnections, the ready check,
|
||||||
@@ -290,7 +289,7 @@ client.get("foo", function (err, value){
|
|||||||
Most Redis commands take a single String or an Array of Strings as arguments, and replies are sent back as a single String or an Array of Strings.
|
Most Redis commands take a single String or an Array of Strings as arguments, and replies are sent back as a single String or an Array of Strings.
|
||||||
When dealing with hash values, there are a couple of useful exceptions to this.
|
When dealing with hash values, there are a couple of useful exceptions to this.
|
||||||
|
|
||||||
### client.hgetall(hash)
|
### client.hgetall(hash, callback)
|
||||||
|
|
||||||
The reply from an HGETALL command will be converted into a JavaScript Object by `node_redis`. That way you can interact
|
The reply from an HGETALL command will be converted into a JavaScript Object by `node_redis`. That way you can interact
|
||||||
with the responses using JavaScript syntax.
|
with the responses using JavaScript syntax.
|
||||||
@@ -310,7 +309,7 @@ Output:
|
|||||||
{ mjr: '1', another: '23', home: '1234' }
|
{ mjr: '1', another: '23', home: '1234' }
|
||||||
```
|
```
|
||||||
|
|
||||||
### client.hmset(hash, obj, [callback])
|
### client.hmset(hash, obj[, callback])
|
||||||
|
|
||||||
Multiple values in a hash can be set by supplying an object:
|
Multiple values in a hash can be set by supplying an object:
|
||||||
|
|
||||||
@@ -440,7 +439,7 @@ client.multi()
|
|||||||
});
|
});
|
||||||
```
|
```
|
||||||
|
|
||||||
### Multi.exec( callback )
|
### Multi.exec([callback])
|
||||||
|
|
||||||
`client.multi()` is a constructor that returns a `Multi` object. `Multi` objects share all of the
|
`client.multi()` is a constructor that returns a `Multi` object. `Multi` objects share all of the
|
||||||
same command methods as `client` objects do. Commands are queued up inside the `Multi` object
|
same command methods as `client` objects do. Commands are queued up inside the `Multi` object
|
||||||
@@ -492,7 +491,7 @@ client.multi([
|
|||||||
});
|
});
|
||||||
```
|
```
|
||||||
|
|
||||||
### Multi.exec_atomic( callback )
|
### Multi.exec_atomic([callback])
|
||||||
|
|
||||||
Identical to Multi.exec but with the difference that executing a single command will not use transactions.
|
Identical to Multi.exec but with the difference that executing a single command will not use transactions.
|
||||||
|
|
||||||
@@ -577,7 +576,7 @@ the second word as first parameter:
|
|||||||
client.multi().script('load', 'return 1').exec(...);
|
client.multi().script('load', 'return 1').exec(...);
|
||||||
client.multi([['script', 'load', 'return 1']]).exec(...);
|
client.multi([['script', 'load', 'return 1']]).exec(...);
|
||||||
|
|
||||||
## client.send_command(command_name, args, callback)
|
## client.send_command(command_name[, [args][, callback]])
|
||||||
|
|
||||||
Used internally to send commands to Redis. Nearly all Redis commands have been added to the `client` object.
|
Used internally to send commands to Redis. Nearly all Redis commands have been added to the `client` object.
|
||||||
However, if new commands are introduced before this library is updated, you can use `send_command()` to send arbitrary commands to Redis.
|
However, if new commands are introduced before this library is updated, you can use `send_command()` to send arbitrary commands to Redis.
|
||||||
@@ -713,6 +712,7 @@ To get debug output run your `node_redis` application with `NODE_DEBUG=redis`.
|
|||||||
|
|
||||||
## How to Contribute
|
## How to Contribute
|
||||||
- Open a pull request or an issue about what you want to implement / change. We're glad for any help!
|
- Open a pull request or an issue about what you want to implement / change. We're glad for any help!
|
||||||
|
- Please be aware that we'll only accept fully tested code.
|
||||||
|
|
||||||
## Contributors
|
## Contributors
|
||||||
Many [people](https://github.com/NodeRedis/node_redis/graphs/contributors) have have added features and fixed bugs in `node_redis`. Thanks to all of them!
|
Many [people](https://github.com/NodeRedis/node_redis/graphs/contributors) have have added features and fixed bugs in `node_redis`. Thanks to all of them!
|
||||||
|
@@ -10,6 +10,9 @@ var metrics = require('metrics');
|
|||||||
var num_clients = parseInt(process.argv[2], 10) || 5;
|
var num_clients = parseInt(process.argv[2], 10) || 5;
|
||||||
var num_requests = 50000;
|
var num_requests = 50000;
|
||||||
var tests = [];
|
var tests = [];
|
||||||
|
// var bluebird = require('bluebird');
|
||||||
|
// bluebird.promisifyAll(redis.RedisClient.prototype);
|
||||||
|
// bluebird.promisifyAll(redis.Multi.prototype);
|
||||||
var versions_logged = false;
|
var versions_logged = false;
|
||||||
var client_options = {
|
var client_options = {
|
||||||
return_buffers: false,
|
return_buffers: false,
|
||||||
|
19
changelog.md
19
changelog.md
@@ -1,17 +1,28 @@
|
|||||||
Changelog
|
Changelog
|
||||||
=========
|
=========
|
||||||
|
|
||||||
|
## v.2.2.4 - 17 Oct, 2015
|
||||||
|
|
||||||
|
Bugfixes
|
||||||
|
|
||||||
|
- Fixed unspecific error message for unresolvable commands ([@BridgeAR](https://github.com/BridgeAR))
|
||||||
|
- Fixed not allowed command error in pubsub mode not being returned in a provided callback ([@BridgeAR](https://github.com/BridgeAR))
|
||||||
|
- Fixed to many commands forbidden in pub sub mode ([@BridgeAR](https://github.com/BridgeAR))
|
||||||
|
- Fixed mutation of the arguments array passed to .multi / .batch constructor ([@BridgeAR](https://github.com/BridgeAR))
|
||||||
|
- Fixed mutation of the options object passed to createClient ([@BridgeAR](https://github.com/BridgeAR))
|
||||||
|
- Fixed error callback in .multi not called if connection in broken mode ([@BridgeAR](https://github.com/BridgeAR))
|
||||||
|
|
||||||
## v.2.2.3 - 14 Oct, 2015
|
## v.2.2.3 - 14 Oct, 2015
|
||||||
|
|
||||||
Bugfixes
|
Bugfixes
|
||||||
|
|
||||||
- Fix multi not being executed on Node 0.10.x if node_redis not yet ready ([@BridgeAR](https://github.com/BridgeAR))
|
- Fixed multi not being executed on Node 0.10.x if node_redis not yet ready ([@BridgeAR](https://github.com/BridgeAR))
|
||||||
|
|
||||||
## v.2.2.2 - 14 Oct, 2015
|
## v.2.2.2 - 14 Oct, 2015
|
||||||
|
|
||||||
Bugfixes
|
Bugfixes
|
||||||
|
|
||||||
- Fix regular commands not being executed after a .multi until .exec was called ([@BridgeAR](https://github.com/BridgeAR))
|
- Fixed regular commands not being executed after a .multi until .exec was called ([@BridgeAR](https://github.com/BridgeAR))
|
||||||
|
|
||||||
## v.2.2.1 - 12 Oct, 2015
|
## v.2.2.1 - 12 Oct, 2015
|
||||||
|
|
||||||
@@ -34,7 +45,7 @@ Features
|
|||||||
|
|
||||||
Bugfixes
|
Bugfixes
|
||||||
|
|
||||||
- Fix a javascript parser regression introduced in 2.0 that could result in timeouts on high load. ([@BridgeAR](https://github.com/BridgeAR))
|
- Fixed a javascript parser regression introduced in 2.0 that could result in timeouts on high load. ([@BridgeAR](https://github.com/BridgeAR))
|
||||||
- I was not able to write a regression test for this, since the error seems to only occur under heavy load with special conditions. So please have a look for timeouts with the js parser, if you use it and report all issues and switch to the hiredis parser in the meanwhile. If you're able to come up with a reproducable test case, this would be even better :)
|
- I was not able to write a regression test for this, since the error seems to only occur under heavy load with special conditions. So please have a look for timeouts with the js parser, if you use it and report all issues and switch to the hiredis parser in the meanwhile. If you're able to come up with a reproducable test case, this would be even better :)
|
||||||
- Fixed should_buffer boolean for .exec, .select and .auth commands not being returned and fix a couple special conditions ([@BridgeAR](https://github.com/BridgeAR))
|
- Fixed should_buffer boolean for .exec, .select and .auth commands not being returned and fix a couple special conditions ([@BridgeAR](https://github.com/BridgeAR))
|
||||||
|
|
||||||
@@ -141,7 +152,7 @@ This is the biggest release that node_redis had since it was released in 2010. A
|
|||||||
- Do not wrap errors into other errors (@BridgeAR)
|
- Do not wrap errors into other errors (@BridgeAR)
|
||||||
- Authentication failures are now returned in the callback instead of being emitted (@BridgeAR)
|
- Authentication failures are now returned in the callback instead of being emitted (@BridgeAR)
|
||||||
- Fix a memory leak on reconnect (@rahar)
|
- Fix a memory leak on reconnect (@rahar)
|
||||||
- Using `send_command` directly may no also be called without the args as stated in the [README.md](./README.md) (@BridgeAR)
|
- Using `send_command` directly may now also be called without the args as stated in the [README.md](./README.md) (@BridgeAR)
|
||||||
- Fix the multi.exec error handling (@BridgeAR)
|
- Fix the multi.exec error handling (@BridgeAR)
|
||||||
- Fix commands being inconsistent and behaving wrong (@BridgeAR)
|
- Fix commands being inconsistent and behaving wrong (@BridgeAR)
|
||||||
- Channel names with spaces are now properly resubscribed after a reconnection (@pbihler)
|
- Channel names with spaces are now properly resubscribed after a reconnection (@pbihler)
|
||||||
|
114
index.js
114
index.js
@@ -20,6 +20,8 @@ var debug = function(msg) {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
function noop () {}
|
||||||
|
|
||||||
exports.debug_mode = /\bredis\b/i.test(process.env.NODE_DEBUG);
|
exports.debug_mode = /\bredis\b/i.test(process.env.NODE_DEBUG);
|
||||||
|
|
||||||
// hiredis might not be installed
|
// hiredis might not be installed
|
||||||
@@ -33,24 +35,24 @@ try {
|
|||||||
parsers.push(require('./lib/parsers/javascript'));
|
parsers.push(require('./lib/parsers/javascript'));
|
||||||
|
|
||||||
function RedisClient(stream, options) {
|
function RedisClient(stream, options) {
|
||||||
options = options || {};
|
// Copy the options so they are not mutated
|
||||||
|
options = JSON.parse(JSON.stringify(options));
|
||||||
var self = this;
|
var self = this;
|
||||||
|
|
||||||
if (!stream.cork) {
|
if (!stream.cork) {
|
||||||
this.pipeline = 0;
|
this.pipeline = 0;
|
||||||
this.cork = function noop (len) {};
|
this.cork = noop;
|
||||||
this.once('ready', function () {
|
this.once('ready', function () {
|
||||||
self.cork = function (len) {
|
self.cork = function (len) {
|
||||||
self.pipeline = len;
|
self.pipeline = len;
|
||||||
self.pipeline_queue = new Queue(len);
|
self.pipeline_queue = new Queue(len);
|
||||||
};
|
};
|
||||||
});
|
});
|
||||||
stream.uncork = function noop() {};
|
stream.uncork = noop;
|
||||||
this.write = this.writeStream;
|
this.write = this.writeStream;
|
||||||
}
|
}
|
||||||
|
|
||||||
this.stream = stream;
|
this.stream = stream;
|
||||||
this.options = options;
|
|
||||||
this.connection_id = ++connection_id;
|
this.connection_id = ++connection_id;
|
||||||
this.connected = false;
|
this.connected = false;
|
||||||
this.ready = false;
|
this.ready = false;
|
||||||
@@ -62,26 +64,23 @@ function RedisClient(stream, options) {
|
|||||||
options.socket_keepalive = true;
|
options.socket_keepalive = true;
|
||||||
}
|
}
|
||||||
if (options.rename_commands) {
|
if (options.rename_commands) {
|
||||||
for (var command in options.rename_commands) {
|
for (var command in options.rename_commands) { // jshint ignore: line
|
||||||
if (options.rename_commands.hasOwnProperty(command)) {
|
|
||||||
options.rename_commands[command.toLowerCase()] = options.rename_commands[command];
|
options.rename_commands[command.toLowerCase()] = options.rename_commands[command];
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
options.return_buffers = !!options.return_buffers;
|
||||||
this.options.return_buffers = !!this.options.return_buffers;
|
options.detect_buffers = !!options.detect_buffers;
|
||||||
this.options.detect_buffers = !!this.options.detect_buffers;
|
|
||||||
// Override the detect_buffers setting if return_buffers is active and print a warning
|
// Override the detect_buffers setting if return_buffers is active and print a warning
|
||||||
if (this.options.return_buffers && this.options.detect_buffers) {
|
if (options.return_buffers && options.detect_buffers) {
|
||||||
console.warn('>> WARNING: You activated return_buffers and detect_buffers at the same time. The return value is always going to be a buffer.');
|
console.warn('>> WARNING: You activated return_buffers and detect_buffers at the same time. The return value is always going to be a buffer.');
|
||||||
this.options.detect_buffers = false;
|
options.detect_buffers = false;
|
||||||
}
|
}
|
||||||
this.should_buffer = false;
|
this.should_buffer = false;
|
||||||
this.command_queue_high_water = options.command_queue_high_water || 1000;
|
this.command_queue_high_water = +options.command_queue_high_water || 1000;
|
||||||
this.command_queue_low_water = options.command_queue_low_water || 0;
|
this.command_queue_low_water = options.command_queue_low_water | 0;
|
||||||
this.max_attempts = +options.max_attempts || 0;
|
this.max_attempts = options.max_attempts | 0;
|
||||||
this.command_queue = new Queue(); // Holds sent commands to de-pipeline them
|
this.command_queue = new Queue(); // Holds sent commands to de-pipeline them
|
||||||
this.offline_queue = new Queue(); // Holds commands issued but not able to be sent
|
this.offline_queue = new Queue(); // Holds commands issued but not able to be sent
|
||||||
this.commands_sent = 0;
|
|
||||||
this.connect_timeout = +options.connect_timeout || 86400000; // 24 * 60 * 60 * 1000 ms
|
this.connect_timeout = +options.connect_timeout || 86400000; // 24 * 60 * 60 * 1000 ms
|
||||||
this.enable_offline_queue = options.enable_offline_queue === false ? false : true;
|
this.enable_offline_queue = options.enable_offline_queue === false ? false : true;
|
||||||
this.retry_max_delay = +options.retry_max_delay || null;
|
this.retry_max_delay = +options.retry_max_delay || null;
|
||||||
@@ -95,6 +94,7 @@ function RedisClient(stream, options) {
|
|||||||
this.parser_module = null;
|
this.parser_module = null;
|
||||||
this.selected_db = null; // Save the selected db here, used when reconnecting
|
this.selected_db = null; // Save the selected db here, used when reconnecting
|
||||||
this.old_state = null;
|
this.old_state = null;
|
||||||
|
this.options = options;
|
||||||
|
|
||||||
this.install_stream_listeners();
|
this.install_stream_listeners();
|
||||||
events.EventEmitter.call(this);
|
events.EventEmitter.call(this);
|
||||||
@@ -118,17 +118,16 @@ RedisClient.prototype.install_stream_listeners = function() {
|
|||||||
self.on_error(err);
|
self.on_error(err);
|
||||||
});
|
});
|
||||||
|
|
||||||
this.stream.on('close', function () {
|
this.stream.once('close', function () {
|
||||||
self.connection_gone('close');
|
self.connection_gone('close');
|
||||||
});
|
});
|
||||||
|
|
||||||
this.stream.on('end', function () {
|
this.stream.once('end', function () {
|
||||||
self.connection_gone('end');
|
self.connection_gone('end');
|
||||||
});
|
});
|
||||||
|
|
||||||
this.stream.on('drain', function () {
|
this.stream.on('drain', function () {
|
||||||
self.should_buffer = false;
|
self.drain();
|
||||||
self.emit('drain');
|
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -450,11 +449,6 @@ RedisClient.prototype.send_offline_queue = function () {
|
|||||||
}
|
}
|
||||||
// Even though items were shifted off, Queue backing store still uses memory until next add, so just get a new Queue
|
// Even though items were shifted off, Queue backing store still uses memory until next add, so just get a new Queue
|
||||||
this.offline_queue = new Queue();
|
this.offline_queue = new Queue();
|
||||||
|
|
||||||
if (buffered_writes === 0) {
|
|
||||||
this.should_buffer = false;
|
|
||||||
this.emit('drain');
|
|
||||||
}
|
|
||||||
};
|
};
|
||||||
|
|
||||||
var retry_connection = function (self) {
|
var retry_connection = function (self) {
|
||||||
@@ -558,6 +552,11 @@ RedisClient.prototype.return_error = function (err) {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
RedisClient.prototype.drain = function () {
|
||||||
|
this.emit('drain');
|
||||||
|
this.should_buffer = false;
|
||||||
|
};
|
||||||
|
|
||||||
RedisClient.prototype.emit_drain_idle = function (queue_len) {
|
RedisClient.prototype.emit_drain_idle = function (queue_len) {
|
||||||
if (this.pub_sub_mode === false && queue_len === 0) {
|
if (this.pub_sub_mode === false && queue_len === 0) {
|
||||||
// Free the queue capacity memory by using a new queue
|
// Free the queue capacity memory by using a new queue
|
||||||
@@ -566,8 +565,7 @@ RedisClient.prototype.emit_drain_idle = function (queue_len) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (this.should_buffer && queue_len <= this.command_queue_low_water) {
|
if (this.should_buffer && queue_len <= this.command_queue_low_water) {
|
||||||
this.emit('drain');
|
this.drain();
|
||||||
this.should_buffer = false;
|
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -688,11 +686,7 @@ RedisClient.prototype.send_command = function (command, args, callback) {
|
|||||||
command = command.toUpperCase();
|
command = command.toUpperCase();
|
||||||
err = new Error('send_command: ' + command + ' value must not be undefined or null');
|
err = new Error('send_command: ' + command + ' value must not be undefined or null');
|
||||||
err.command = command;
|
err.command = command;
|
||||||
if (callback) {
|
this.callback_emit_error(callback, err);
|
||||||
callback(err);
|
|
||||||
} else {
|
|
||||||
this.emit('error', err);
|
|
||||||
}
|
|
||||||
// Singal no buffering
|
// Singal no buffering
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
@@ -714,16 +708,15 @@ RedisClient.prototype.send_command = function (command, args, callback) {
|
|||||||
if (this.closing || !this.enable_offline_queue) {
|
if (this.closing || !this.enable_offline_queue) {
|
||||||
command = command.toUpperCase();
|
command = command.toUpperCase();
|
||||||
if (!this.closing) {
|
if (!this.closing) {
|
||||||
err = new Error(command + " can't be processed. Stream not writeable and enable_offline_queue is deactivated.");
|
var msg = !stream.writable ?
|
||||||
|
'Stream not writeable.' :
|
||||||
|
'The connection is not yet established and the offline queue is deactivated.';
|
||||||
|
err = new Error(command + " can't be processed. " + msg);
|
||||||
} else {
|
} else {
|
||||||
err = new Error(command + " can't be processed. The connection has already been closed.");
|
err = new Error(command + " can't be processed. The connection has already been closed.");
|
||||||
}
|
}
|
||||||
err.command = command;
|
err.command = command;
|
||||||
if (callback) {
|
this.callback_emit_error(callback, err);
|
||||||
callback(err);
|
|
||||||
} else {
|
|
||||||
this.emit('error', err);
|
|
||||||
}
|
|
||||||
} else {
|
} else {
|
||||||
debug('Queueing ' + command + ' for next server connection.');
|
debug('Queueing ' + command + ' for next server connection.');
|
||||||
this.offline_queue.push(command_obj);
|
this.offline_queue.push(command_obj);
|
||||||
@@ -739,14 +732,8 @@ RedisClient.prototype.send_command = function (command, args, callback) {
|
|||||||
this.monitoring = true;
|
this.monitoring = true;
|
||||||
} else if (command === 'quit') {
|
} else if (command === 'quit') {
|
||||||
this.closing = true;
|
this.closing = true;
|
||||||
} else if (this.pub_sub_mode === true) {
|
|
||||||
err = new Error('Connection in subscriber mode, only subscriber commands may be used');
|
|
||||||
err.command = command.toUpperCase();
|
|
||||||
this.emit('error', err);
|
|
||||||
return true;
|
|
||||||
}
|
}
|
||||||
this.command_queue.push(command_obj);
|
this.command_queue.push(command_obj);
|
||||||
this.commands_sent += 1;
|
|
||||||
|
|
||||||
if (typeof this.options.rename_commands !== 'undefined' && this.options.rename_commands[command]) {
|
if (typeof this.options.rename_commands !== 'undefined' && this.options.rename_commands[command]) {
|
||||||
command = this.options.rename_commands[command];
|
command = this.options.rename_commands[command];
|
||||||
@@ -864,7 +851,7 @@ RedisClient.prototype.end = function (flush) {
|
|||||||
clearTimeout(this.retry_timer);
|
clearTimeout(this.retry_timer);
|
||||||
this.retry_timer = null;
|
this.retry_timer = null;
|
||||||
}
|
}
|
||||||
this.stream.on('error', function noop(){});
|
this.stream.on('error', noop);
|
||||||
|
|
||||||
// Flush queue if wanted
|
// Flush queue if wanted
|
||||||
if (flush) {
|
if (flush) {
|
||||||
@@ -882,9 +869,9 @@ function Multi(client, args) {
|
|||||||
this.queue = new Queue();
|
this.queue = new Queue();
|
||||||
var command, tmp_args;
|
var command, tmp_args;
|
||||||
if (Array.isArray(args)) {
|
if (Array.isArray(args)) {
|
||||||
while (tmp_args = args.shift()) {
|
for (var i = 0; i < args.length; i++) {
|
||||||
command = tmp_args[0];
|
command = args[i][0];
|
||||||
tmp_args = tmp_args.slice(1);
|
tmp_args = args[i].slice(1);
|
||||||
if (Array.isArray(command)) {
|
if (Array.isArray(command)) {
|
||||||
this[command[0]].apply(this, command.slice(1).concat(tmp_args));
|
this[command[0]].apply(this, command.slice(1).concat(tmp_args));
|
||||||
} else {
|
} else {
|
||||||
@@ -972,11 +959,7 @@ RedisClient.prototype.select = RedisClient.prototype.SELECT = function (db, call
|
|||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
// Stash auth for connect and reconnect. Send immediately if already connected.
|
RedisClient.prototype.callback_emit_error = function (callback, err) {
|
||||||
RedisClient.prototype.auth = RedisClient.prototype.AUTH = function (pass, callback) {
|
|
||||||
if (typeof pass !== 'string') {
|
|
||||||
var err = new Error('The password has to be of type "string"');
|
|
||||||
err.command = 'AUTH';
|
|
||||||
if (callback) {
|
if (callback) {
|
||||||
setImmediate(function () {
|
setImmediate(function () {
|
||||||
callback(err);
|
callback(err);
|
||||||
@@ -984,6 +967,14 @@ RedisClient.prototype.auth = RedisClient.prototype.AUTH = function (pass, callba
|
|||||||
} else {
|
} else {
|
||||||
this.emit('error', err);
|
this.emit('error', err);
|
||||||
}
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
// Stash auth for connect and reconnect. Send immediately if already connected.
|
||||||
|
RedisClient.prototype.auth = RedisClient.prototype.AUTH = function (pass, callback) {
|
||||||
|
if (typeof pass !== 'string') {
|
||||||
|
var err = new Error('The password has to be of type "string"');
|
||||||
|
err.command = 'AUTH';
|
||||||
|
this.callback_emit_error(callback, err);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
this.auth_pass = pass;
|
this.auth_pass = pass;
|
||||||
@@ -1121,15 +1112,14 @@ Multi.prototype.execute_callback = function (err, replies) {
|
|||||||
var i = 0, args;
|
var i = 0, args;
|
||||||
|
|
||||||
if (err) {
|
if (err) {
|
||||||
if (err.code !== 'CONNECTION_BROKEN') {
|
// The errors would be circular
|
||||||
err.errors = this.errors;
|
err.errors = err.code !== 'CONNECTION_BROKEN' ? this.errors : [];
|
||||||
if (this.callback) {
|
if (this.callback) {
|
||||||
this.callback(err);
|
this.callback(err);
|
||||||
} else {
|
} else if (err.code !== 'CONNECTION_BROKEN') {
|
||||||
// Exclude CONNECTION_BROKEN so that error won't be emitted twice
|
// Exclude CONNECTION_BROKEN so that error won't be emitted twice
|
||||||
this._client.emit('error', err);
|
this._client.emit('error', err);
|
||||||
}
|
}
|
||||||
}
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1169,7 +1159,7 @@ Multi.prototype.execute_callback = function (err, replies) {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
Multi.prototype.callback = function (cb, command, i) {
|
Multi.prototype.callback = function (cb, i) {
|
||||||
var self = this;
|
var self = this;
|
||||||
return function (err, res) {
|
return function (err, res) {
|
||||||
if (err) {
|
if (err) {
|
||||||
@@ -1210,9 +1200,9 @@ Multi.prototype.exec = Multi.prototype.EXEC = Multi.prototype.exec_batch = funct
|
|||||||
var command = args.shift();
|
var command = args.shift();
|
||||||
var cb;
|
var cb;
|
||||||
if (typeof args[args.length - 1] === 'function') {
|
if (typeof args[args.length - 1] === 'function') {
|
||||||
cb = this.callback(args.pop(), command, index);
|
cb = this.callback(args.pop(), index);
|
||||||
} else {
|
} else {
|
||||||
cb = this.callback(undefined, command, index);
|
cb = this.callback(undefined, index);
|
||||||
}
|
}
|
||||||
if (callback && index === len - 1) {
|
if (callback && index === len - 1) {
|
||||||
cb = lastCallback(cb);
|
cb = lastCallback(cb);
|
||||||
@@ -1241,7 +1231,7 @@ var createClient_tcp = function (port_arg, host_arg, options) {
|
|||||||
var cnxOptions = {
|
var cnxOptions = {
|
||||||
port : port_arg || default_port,
|
port : port_arg || default_port,
|
||||||
host : host_arg || default_host,
|
host : host_arg || default_host,
|
||||||
family : options && options.family === 'IPv6' ? 6 : 4
|
family : options.family === 'IPv6' ? 6 : 4
|
||||||
};
|
};
|
||||||
var net_client = net.createConnection(cnxOptions);
|
var net_client = net.createConnection(cnxOptions);
|
||||||
var redis_client = new RedisClient(net_client, options);
|
var redis_client = new RedisClient(net_client, options);
|
||||||
@@ -1258,10 +1248,10 @@ var createClient = function (port_arg, host_arg, options) {
|
|||||||
return createClient_tcp(+options.port, options.host, options);
|
return createClient_tcp(+options.port, options.host, options);
|
||||||
}
|
}
|
||||||
if (typeof port_arg === 'number' || typeof port_arg === 'string' && /^\d+$/.test(port_arg)) {
|
if (typeof port_arg === 'number' || typeof port_arg === 'string' && /^\d+$/.test(port_arg)) {
|
||||||
return createClient_tcp(port_arg, host_arg, options);
|
return createClient_tcp(port_arg, host_arg, options || {});
|
||||||
}
|
}
|
||||||
if (typeof port_arg === 'string') {
|
if (typeof port_arg === 'string') {
|
||||||
options = host_arg || {};
|
options = host_arg || options || {};
|
||||||
|
|
||||||
var parsed = URL.parse(port_arg, true, true);
|
var parsed = URL.parse(port_arg, true, true);
|
||||||
if (parsed.hostname) {
|
if (parsed.hostname) {
|
||||||
|
@@ -2,12 +2,10 @@
|
|||||||
|
|
||||||
var util = require('util');
|
var util = require('util');
|
||||||
|
|
||||||
function ReplyParser() {
|
function JavascriptReplyParser() {
|
||||||
this.name = exports.name;
|
this.name = exports.name;
|
||||||
|
|
||||||
this._buffer = new Buffer(0);
|
this._buffer = new Buffer(0);
|
||||||
this._offset = 0;
|
this._offset = 0;
|
||||||
this._encoding = 'utf-8';
|
|
||||||
}
|
}
|
||||||
|
|
||||||
function IncompleteReadBuffer(message) {
|
function IncompleteReadBuffer(message) {
|
||||||
@@ -16,13 +14,13 @@ function IncompleteReadBuffer(message) {
|
|||||||
}
|
}
|
||||||
util.inherits(IncompleteReadBuffer, Error);
|
util.inherits(IncompleteReadBuffer, Error);
|
||||||
|
|
||||||
ReplyParser.prototype._parseResult = function (type) {
|
JavascriptReplyParser.prototype._parseResult = function (type) {
|
||||||
var start = 0,
|
var start = 0,
|
||||||
end = 0,
|
end = 0,
|
||||||
offset = 0,
|
offset = 0,
|
||||||
packetHeader = 0;
|
packetHeader = 0;
|
||||||
|
|
||||||
if (type === 43 || type === 45) { // + or -
|
if (type === 43 || type === 58 || type === 45) { // + or : or -
|
||||||
// up to the delimiter
|
// up to the delimiter
|
||||||
end = this._packetEndOffset() - 1;
|
end = this._packetEndOffset() - 1;
|
||||||
start = this._offset;
|
start = this._offset;
|
||||||
@@ -34,24 +32,13 @@ ReplyParser.prototype._parseResult = function (type) {
|
|||||||
// include the delimiter
|
// include the delimiter
|
||||||
this._offset = end + 2;
|
this._offset = end + 2;
|
||||||
|
|
||||||
if (type === 45) {
|
if (type === 43) {
|
||||||
return new Error(this._buffer.toString(this._encoding, start, end));
|
|
||||||
}
|
|
||||||
return this._buffer.slice(start, end);
|
return this._buffer.slice(start, end);
|
||||||
} else if (type === 58) { // :
|
} else if (type === 58) {
|
||||||
// up to the delimiter
|
|
||||||
end = this._packetEndOffset() - 1;
|
|
||||||
start = this._offset;
|
|
||||||
|
|
||||||
if (end > this._buffer.length) {
|
|
||||||
throw new IncompleteReadBuffer('Wait for more data.');
|
|
||||||
}
|
|
||||||
|
|
||||||
// include the delimiter
|
|
||||||
this._offset = end + 2;
|
|
||||||
|
|
||||||
// return the coerced numeric value
|
// return the coerced numeric value
|
||||||
return +this._buffer.toString('ascii', start, end);
|
return +this._buffer.toString('ascii', start, end);
|
||||||
|
}
|
||||||
|
return new Error(this._buffer.toString('utf-8', start, end));
|
||||||
} else if (type === 36) { // $
|
} else if (type === 36) { // $
|
||||||
// set a rewind point, as the packet could be larger than the
|
// set a rewind point, as the packet could be larger than the
|
||||||
// buffer in memory
|
// buffer in memory
|
||||||
@@ -107,7 +94,7 @@ ReplyParser.prototype._parseResult = function (type) {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
ReplyParser.prototype.execute = function (buffer) {
|
JavascriptReplyParser.prototype.execute = function (buffer) {
|
||||||
this.append(buffer);
|
this.append(buffer);
|
||||||
|
|
||||||
var type, ret, offset;
|
var type, ret, offset;
|
||||||
@@ -161,7 +148,7 @@ ReplyParser.prototype.execute = function (buffer) {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
ReplyParser.prototype.append = function (newBuffer) {
|
JavascriptReplyParser.prototype.append = function (newBuffer) {
|
||||||
|
|
||||||
// out of data
|
// out of data
|
||||||
if (this._offset >= this._buffer.length) {
|
if (this._offset >= this._buffer.length) {
|
||||||
@@ -174,7 +161,7 @@ ReplyParser.prototype.append = function (newBuffer) {
|
|||||||
this._offset = 0;
|
this._offset = 0;
|
||||||
};
|
};
|
||||||
|
|
||||||
ReplyParser.prototype.parseHeader = function () {
|
JavascriptReplyParser.prototype.parseHeader = function () {
|
||||||
var end = this._packetEndOffset(),
|
var end = this._packetEndOffset(),
|
||||||
value = this._buffer.toString('ascii', this._offset, end - 1) | 0;
|
value = this._buffer.toString('ascii', this._offset, end - 1) | 0;
|
||||||
|
|
||||||
@@ -183,7 +170,7 @@ ReplyParser.prototype.parseHeader = function () {
|
|||||||
return value;
|
return value;
|
||||||
};
|
};
|
||||||
|
|
||||||
ReplyParser.prototype._packetEndOffset = function () {
|
JavascriptReplyParser.prototype._packetEndOffset = function () {
|
||||||
var offset = this._offset;
|
var offset = this._offset;
|
||||||
|
|
||||||
while (this._buffer[offset] !== 0x0d && this._buffer[offset + 1] !== 0x0a) {
|
while (this._buffer[offset] !== 0x0d && this._buffer[offset + 1] !== 0x0a) {
|
||||||
@@ -199,9 +186,9 @@ ReplyParser.prototype._packetEndOffset = function () {
|
|||||||
return offset;
|
return offset;
|
||||||
};
|
};
|
||||||
|
|
||||||
ReplyParser.prototype._bytesRemaining = function () {
|
JavascriptReplyParser.prototype._bytesRemaining = function () {
|
||||||
return (this._buffer.length - this._offset) < 0 ? 0 : (this._buffer.length - this._offset);
|
return (this._buffer.length - this._offset) < 0 ? 0 : (this._buffer.length - this._offset);
|
||||||
};
|
};
|
||||||
|
|
||||||
exports.Parser = ReplyParser;
|
exports.Parser = JavascriptReplyParser;
|
||||||
exports.name = 'javascript';
|
exports.name = 'javascript';
|
||||||
|
@@ -4,7 +4,9 @@
|
|||||||
"description": "Redis client library",
|
"description": "Redis client library",
|
||||||
"keywords": [
|
"keywords": [
|
||||||
"database",
|
"database",
|
||||||
"redis"
|
"redis",
|
||||||
|
"transaction",
|
||||||
|
"pipelining"
|
||||||
],
|
],
|
||||||
"author": "Matt Ranney <mjr@ranney.com>",
|
"author": "Matt Ranney <mjr@ranney.com>",
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
@@ -13,13 +15,16 @@
|
|||||||
"coveralls": "nyc report --reporter=text-lcov | coveralls",
|
"coveralls": "nyc report --reporter=text-lcov | coveralls",
|
||||||
"coverage": "nyc report --reporter=html",
|
"coverage": "nyc report --reporter=html",
|
||||||
"benchmark": "node benchmarks/multi_bench.js",
|
"benchmark": "node benchmarks/multi_bench.js",
|
||||||
"test": "nyc ./node_modules/.bin/_mocha ./test/*.js ./test/commands/*.js ./test/parser/*.js --timeout=8000",
|
"test": "nyc ./node_modules/.bin/_mocha ./test/*.js ./test/commands/*.js --timeout=8000",
|
||||||
"pretest": "optional-dev-dependency hiredis",
|
"pretest": "optional-dev-dependency hiredis",
|
||||||
"posttest": "jshint ."
|
"posttest": "jshint ."
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"double-ended-queue": "^2.1.0-0"
|
"double-ended-queue": "^2.1.0-0"
|
||||||
},
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">=0.10.0"
|
||||||
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"coveralls": "^2.11.2",
|
"coveralls": "^2.11.2",
|
||||||
"jshint": "^2.8.0",
|
"jshint": "^2.8.0",
|
||||||
|
@@ -31,7 +31,7 @@ describe("The 'expire' method", function () {
|
|||||||
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));
|
||||||
}, 3000);
|
}, 1100);
|
||||||
});
|
});
|
||||||
|
|
||||||
afterEach(function () {
|
afterEach(function () {
|
||||||
|
@@ -37,6 +37,12 @@ describe("The 'get' method", function () {
|
|||||||
done();
|
done();
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it("reports an error promisified", function () {
|
||||||
|
return client.getAsync(key).then(assert, function (err) {
|
||||||
|
assert(err.message.match(/The connection has already been closed/));
|
||||||
|
});
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
describe("when connected", function () {
|
describe("when connected", function () {
|
||||||
@@ -76,7 +82,7 @@ describe("The 'get' method", function () {
|
|||||||
}]);
|
}]);
|
||||||
});
|
});
|
||||||
|
|
||||||
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;
|
||||||
|
@@ -67,6 +67,50 @@ describe("The 'multi' method", function () {
|
|||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
describe("when connection is broken", function () {
|
||||||
|
var client;
|
||||||
|
|
||||||
|
afterEach(function () {
|
||||||
|
client.end();
|
||||||
|
});
|
||||||
|
|
||||||
|
it("return an error even if connection is in broken mode if callback is present", function (done) {
|
||||||
|
client = redis.createClient({
|
||||||
|
host: 'somewhere',
|
||||||
|
port: 6379,
|
||||||
|
max_attempts: 1
|
||||||
|
});
|
||||||
|
|
||||||
|
client.on('error', function(err) {
|
||||||
|
if (/Redis connection in broken state/.test(err.message)) {
|
||||||
|
done();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
client.multi([['set', 'foo', 'bar'], ['get', 'foo']]).exec(function (err, res) {
|
||||||
|
assert(/Redis connection in broken state/.test(err.message));
|
||||||
|
assert.strictEqual(err.errors.length, 0);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it("does not emit an error twice if connection is in broken mode with no callback", function (done) {
|
||||||
|
client = redis.createClient({
|
||||||
|
host: 'somewhere',
|
||||||
|
port: 6379,
|
||||||
|
max_attempts: 1
|
||||||
|
});
|
||||||
|
|
||||||
|
client.on('error', function(err) {
|
||||||
|
// Results in multiple done calls if test fails
|
||||||
|
if (/Redis connection in broken state/.test(err.message)) {
|
||||||
|
done();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
client.multi([['set', 'foo', 'bar'], ['get', 'foo']]).exec();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
describe("when ready", function () {
|
describe("when ready", function () {
|
||||||
var client;
|
var client;
|
||||||
|
|
||||||
@@ -481,6 +525,18 @@ describe("The 'multi' method", function () {
|
|||||||
assert(!test);
|
assert(!test);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it("do not mutate arguments in the multi constructor", function (done) {
|
||||||
|
helper.serverVersionAtLeast.call(this, client, [2, 6, 5]);
|
||||||
|
|
||||||
|
var input = [['set', 'foo', 'bar'], ['get', 'foo']];
|
||||||
|
client.multi(input).exec(function (err, res) {
|
||||||
|
assert.strictEqual(input.length, 2);
|
||||||
|
assert.strictEqual(input[0].length, 3);
|
||||||
|
assert.strictEqual(input[1].length, 2);
|
||||||
|
done();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
@@ -5,17 +5,28 @@ var config = require("./lib/config");
|
|||||||
var helper = require('./helper');
|
var helper = require('./helper');
|
||||||
var redis = config.redis;
|
var redis = config.redis;
|
||||||
|
|
||||||
describe("on lost connection", function () {
|
describe("connection tests", function () {
|
||||||
helper.allTests(function(parser, ip, args) {
|
helper.allTests(function(parser, ip, args) {
|
||||||
|
|
||||||
describe("using " + parser + " and " + ip, function () {
|
describe("using " + parser + " and " + ip, function () {
|
||||||
|
|
||||||
|
var client;
|
||||||
|
|
||||||
|
afterEach(function () {
|
||||||
|
if (client) {
|
||||||
|
client.end();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
describe("on lost connection", function () {
|
||||||
it("emit an error after max retry attempts and do not try to reconnect afterwards", function (done) {
|
it("emit an error after max retry attempts and do not try to reconnect afterwards", function (done) {
|
||||||
var max_attempts = 4;
|
var max_attempts = 4;
|
||||||
var client = redis.createClient({
|
var options = {
|
||||||
parser: parser,
|
parser: parser,
|
||||||
max_attempts: max_attempts
|
max_attempts: max_attempts
|
||||||
});
|
};
|
||||||
|
client = redis.createClient(options);
|
||||||
|
assert.strictEqual(Object.keys(options).length, 2);
|
||||||
var calls = 0;
|
var calls = 0;
|
||||||
|
|
||||||
client.once('ready', function() {
|
client.once('ready', function() {
|
||||||
@@ -31,14 +42,14 @@ describe("on lost connection", function () {
|
|||||||
setTimeout(function () {
|
setTimeout(function () {
|
||||||
assert.strictEqual(calls, max_attempts - 1);
|
assert.strictEqual(calls, max_attempts - 1);
|
||||||
done();
|
done();
|
||||||
}, 1500);
|
}, 500);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
it("emit an error after max retry timeout and do not try to reconnect afterwards", function (done) {
|
it("emit an error after max retry timeout and do not try to reconnect afterwards", function (done) {
|
||||||
var connect_timeout = 1000; // in ms
|
var connect_timeout = 500; // in ms
|
||||||
var client = redis.createClient({
|
client = redis.createClient({
|
||||||
parser: parser,
|
parser: parser,
|
||||||
connect_timeout: connect_timeout
|
connect_timeout: connect_timeout
|
||||||
});
|
});
|
||||||
@@ -57,14 +68,14 @@ describe("on lost connection", function () {
|
|||||||
setTimeout(function () {
|
setTimeout(function () {
|
||||||
assert(time === connect_timeout);
|
assert(time === connect_timeout);
|
||||||
done();
|
done();
|
||||||
}, 1500);
|
}, 500);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
it("end connection while retry is still ongoing", function (done) {
|
it("end connection while retry is still ongoing", function (done) {
|
||||||
var connect_timeout = 1000; // in ms
|
var connect_timeout = 1000; // in ms
|
||||||
var client = redis.createClient({
|
client = redis.createClient({
|
||||||
parser: parser,
|
parser: parser,
|
||||||
connect_timeout: connect_timeout
|
connect_timeout: connect_timeout
|
||||||
});
|
});
|
||||||
@@ -80,11 +91,13 @@ describe("on lost connection", function () {
|
|||||||
});
|
});
|
||||||
|
|
||||||
it("can not connect with wrong host / port in the options object", function (done) {
|
it("can not connect with wrong host / port in the options object", function (done) {
|
||||||
var client = redis.createClient({
|
var options = {
|
||||||
host: 'somewhere',
|
host: 'somewhere',
|
||||||
port: 6379,
|
port: 6379,
|
||||||
max_attempts: 1
|
max_attempts: 1
|
||||||
});
|
};
|
||||||
|
client = redis.createClient(options);
|
||||||
|
assert.strictEqual(Object.keys(options).length, 3);
|
||||||
var end = helper.callFuncAfter(done, 2);
|
var end = helper.callFuncAfter(done, 2);
|
||||||
|
|
||||||
client.on('error', function (err) {
|
client.on('error', function (err) {
|
||||||
@@ -93,9 +106,12 @@ describe("on lost connection", function () {
|
|||||||
});
|
});
|
||||||
|
|
||||||
});
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe("when not connected", function () {
|
||||||
|
|
||||||
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) {
|
||||||
var client = redis.createClient({
|
client = redis.createClient({
|
||||||
host: 'localhost',
|
host: 'localhost',
|
||||||
port: '6379',
|
port: '6379',
|
||||||
parser: parser,
|
parser: parser,
|
||||||
@@ -107,6 +123,131 @@ describe("on lost connection", function () {
|
|||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it("connects correctly with args", function (done) {
|
||||||
|
client = redis.createClient.apply(redis.createClient, args);
|
||||||
|
client.on("error", done);
|
||||||
|
|
||||||
|
client.once("ready", function () {
|
||||||
|
client.removeListener("error", done);
|
||||||
|
client.get("recon 1", function (err, res) {
|
||||||
|
done(err);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it("connects correctly with default values", function (done) {
|
||||||
|
client = redis.createClient();
|
||||||
|
client.on("error", done);
|
||||||
|
|
||||||
|
client.once("ready", function () {
|
||||||
|
client.removeListener("error", done);
|
||||||
|
client.get("recon 1", function (err, res) {
|
||||||
|
done(err);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it("connects with a port only", function (done) {
|
||||||
|
client = redis.createClient(6379);
|
||||||
|
client.on("error", done);
|
||||||
|
|
||||||
|
client.once("ready", function () {
|
||||||
|
client.removeListener("error", done);
|
||||||
|
client.get("recon 1", function (err, res) {
|
||||||
|
done(err);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it("connects correctly to localhost", function (done) {
|
||||||
|
client = redis.createClient(null, null);
|
||||||
|
client.on("error", done);
|
||||||
|
|
||||||
|
client.once("ready", function () {
|
||||||
|
client.removeListener("error", done);
|
||||||
|
client.get("recon 1", function (err, res) {
|
||||||
|
done(err);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it("connects correctly to localhost and no ready check", function (done) {
|
||||||
|
client = redis.createClient(undefined, undefined, {
|
||||||
|
no_ready_check: true
|
||||||
|
});
|
||||||
|
client.on("error", done);
|
||||||
|
|
||||||
|
client.once("ready", function () {
|
||||||
|
client.set('foo', 'bar');
|
||||||
|
client.get('foo', function(err, res) {
|
||||||
|
assert.strictEqual(res, 'bar');
|
||||||
|
done(err);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it("buffer commands and flush them after ", function (done) {
|
||||||
|
client = redis.createClient(9999, null, {
|
||||||
|
parser: parser
|
||||||
|
});
|
||||||
|
|
||||||
|
client.on('error', function(e) {
|
||||||
|
// ignore, b/c expecting a "can't connect" error
|
||||||
|
});
|
||||||
|
|
||||||
|
return setTimeout(function() {
|
||||||
|
client.set('foo', 'bar', function(err, result) {
|
||||||
|
// This should never be called
|
||||||
|
return done(err);
|
||||||
|
});
|
||||||
|
|
||||||
|
return setTimeout(function() {
|
||||||
|
assert.strictEqual(client.offline_queue.length, 1);
|
||||||
|
return done();
|
||||||
|
}, 25);
|
||||||
|
}, 50);
|
||||||
|
});
|
||||||
|
|
||||||
|
it("throws on strange connection info", function () {
|
||||||
|
try {
|
||||||
|
redis.createClient(true);
|
||||||
|
throw new Error('failed');
|
||||||
|
} catch (err) {
|
||||||
|
assert.equal(err.message, 'Unknown type of connection in createClient()');
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
if (ip === 'IPv4') {
|
||||||
|
it('allows connecting with the redis url and the default port', function (done) {
|
||||||
|
client = redis.createClient('redis://foo:porkchopsandwiches@' + config.HOST[ip]);
|
||||||
|
client.on("ready", function () {
|
||||||
|
return done();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it('allows connecting with the redis url and no auth and options as second parameter', function (done) {
|
||||||
|
var options = {
|
||||||
|
detect_buffers: false
|
||||||
|
};
|
||||||
|
client = redis.createClient('redis://' + config.HOST[ip] + ':' + config.PORT, options);
|
||||||
|
assert.strictEqual(Object.keys(options).length, 1);
|
||||||
|
client.on("ready", function () {
|
||||||
|
return 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, {
|
||||||
|
detect_buffers: false
|
||||||
|
});
|
||||||
|
client.on("ready", function () {
|
||||||
|
return done();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
});
|
||||||
|
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
@@ -122,9 +122,9 @@ module.exports = {
|
|||||||
}
|
}
|
||||||
var options = [{
|
var options = [{
|
||||||
detect_buffers: true
|
detect_buffers: true
|
||||||
// Somehow we need a undefined here - otherwise the parsers return_buffers value is always true
|
}, {
|
||||||
// Investigate this further
|
detect_buffers: false
|
||||||
}, undefined];
|
}];
|
||||||
options.forEach(function (options) {
|
options.forEach(function (options) {
|
||||||
var strOptions = '';
|
var strOptions = '';
|
||||||
var key;
|
var key;
|
||||||
|
@@ -8,11 +8,6 @@ var redis = config.redis;
|
|||||||
|
|
||||||
describe("The node_redis client", function () {
|
describe("The node_redis client", function () {
|
||||||
|
|
||||||
helper.allTests({
|
|
||||||
allConnections: true
|
|
||||||
}, function(parser, ip, args) {
|
|
||||||
|
|
||||||
if (args[2]) { // skip if options are undefined
|
|
||||||
describe("testing parser existence", function () {
|
describe("testing parser existence", function () {
|
||||||
it('throws on non-existence', function (done) {
|
it('throws on non-existence', function (done) {
|
||||||
var mochaListener = helper.removeMochaListener();
|
var mochaListener = helper.removeMochaListener();
|
||||||
@@ -23,117 +18,31 @@ describe("The node_redis client", function () {
|
|||||||
return done();
|
return done();
|
||||||
});
|
});
|
||||||
|
|
||||||
// Don't pollute the args for the other connections
|
redis.createClient({
|
||||||
var tmp = JSON.parse(JSON.stringify(args));
|
parser: 'nonExistingParser'
|
||||||
tmp[2].parser = 'nonExistingParser';
|
|
||||||
redis.createClient.apply(redis.createClient, tmp);
|
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
}
|
});
|
||||||
|
|
||||||
|
helper.allTests({
|
||||||
|
allConnections: true
|
||||||
|
}, function(parser, ip, args) {
|
||||||
|
|
||||||
describe("using " + parser + " and " + ip, function () {
|
describe("using " + parser + " and " + ip, function () {
|
||||||
var client;
|
var client;
|
||||||
|
|
||||||
describe("when not connected", function () {
|
|
||||||
afterEach(function () {
|
afterEach(function () {
|
||||||
if (client) {
|
|
||||||
client.end();
|
client.end();
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
it("connects correctly with args", function (done) {
|
|
||||||
client = redis.createClient.apply(redis.createClient, args);
|
|
||||||
client.on("error", done);
|
|
||||||
|
|
||||||
client.once("ready", function () {
|
|
||||||
client.removeListener("error", done);
|
|
||||||
client.get("recon 1", function (err, res) {
|
|
||||||
done(err);
|
|
||||||
});
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
it("connects correctly with default values", function (done) {
|
|
||||||
client = redis.createClient();
|
|
||||||
client.on("error", done);
|
|
||||||
|
|
||||||
client.once("ready", function () {
|
|
||||||
client.removeListener("error", done);
|
|
||||||
client.get("recon 1", function (err, res) {
|
|
||||||
done(err);
|
|
||||||
});
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
it("connects correctly to localhost", function (done) {
|
|
||||||
client = redis.createClient(null, null);
|
|
||||||
client.on("error", done);
|
|
||||||
|
|
||||||
client.once("ready", function () {
|
|
||||||
client.removeListener("error", done);
|
|
||||||
client.get("recon 1", function (err, res) {
|
|
||||||
done(err);
|
|
||||||
});
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
it("connects correctly to localhost and no ready check", function (done) {
|
|
||||||
client = redis.createClient(undefined, undefined, {
|
|
||||||
no_ready_check: true
|
|
||||||
});
|
|
||||||
client.on("error", done);
|
|
||||||
|
|
||||||
client.once("ready", function () {
|
|
||||||
client.set('foo', 'bar');
|
|
||||||
client.get('foo', function(err, res) {
|
|
||||||
assert.strictEqual(res, 'bar');
|
|
||||||
done(err);
|
|
||||||
});
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
it("throws on strange connection info", function () {
|
|
||||||
try {
|
|
||||||
redis.createClient(true);
|
|
||||||
throw new Error('failed');
|
|
||||||
} catch (err) {
|
|
||||||
assert.equal(err.message, 'Unknown type of connection in createClient()');
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
if (ip === 'IPv4') {
|
|
||||||
it('allows connecting with the redis url and the default port', function (done) {
|
|
||||||
client = redis.createClient('redis://foo:porkchopsandwiches@' + config.HOST[ip]);
|
|
||||||
client.on("ready", function () {
|
|
||||||
return done();
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
it('allows connecting with the redis url and no auth', function (done) {
|
|
||||||
client = redis.createClient('redis://' + config.HOST[ip] + ':' + config.PORT, {
|
|
||||||
detect_buffers: false
|
|
||||||
});
|
|
||||||
client.on("ready", function () {
|
|
||||||
return done();
|
|
||||||
});
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
});
|
});
|
||||||
|
|
||||||
describe("when connected", function () {
|
describe("when connected", function () {
|
||||||
beforeEach(function (done) {
|
beforeEach(function (done) {
|
||||||
client = redis.createClient.apply(redis.createClient, args);
|
client = redis.createClient.apply(redis.createClient, args);
|
||||||
client.once("error", done);
|
|
||||||
client.once("connect", function () {
|
client.once("connect", function () {
|
||||||
client.flushdb(done);
|
client.flushdb(done);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
afterEach(function () {
|
|
||||||
client.end();
|
|
||||||
});
|
|
||||||
|
|
||||||
describe("send_command", function () {
|
describe("send_command", function () {
|
||||||
|
|
||||||
it("omitting args should be fine in some cases", function (done) {
|
it("omitting args should be fine in some cases", function (done) {
|
||||||
@@ -211,7 +120,9 @@ 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();
|
||||||
|
|
||||||
var client = redis.createClient();
|
// TODO: Investigate why this test is failing hard and killing mocha if the client is created with .apply
|
||||||
|
// Seems like something is wrong while passing a socket connection to create client! args[1]
|
||||||
|
client = redis.createClient();
|
||||||
client.quit(function() {
|
client.quit(function() {
|
||||||
client.get("foo", function(err, res) {
|
client.get("foo", function(err, res) {
|
||||||
assert(err.message.indexOf('Redis connection gone') !== -1);
|
assert(err.message.indexOf('Redis connection gone') !== -1);
|
||||||
@@ -224,7 +135,6 @@ describe("The node_redis client", function () {
|
|||||||
it("return an error in the callback version two", function (done) {
|
it("return an error in the callback version two", function (done) {
|
||||||
if (helper.redisProcess().spawnFailed()) this.skip();
|
if (helper.redisProcess().spawnFailed()) this.skip();
|
||||||
|
|
||||||
var client = redis.createClient();
|
|
||||||
client.quit();
|
client.quit();
|
||||||
setTimeout(function() {
|
setTimeout(function() {
|
||||||
client.get("foo", function(err, res) {
|
client.get("foo", function(err, res) {
|
||||||
@@ -239,7 +149,6 @@ describe("The node_redis client", function () {
|
|||||||
it("emit an error", function (done) {
|
it("emit an error", function (done) {
|
||||||
if (helper.redisProcess().spawnFailed()) this.skip();
|
if (helper.redisProcess().spawnFailed()) this.skip();
|
||||||
|
|
||||||
var client = redis.createClient();
|
|
||||||
client.quit();
|
client.quit();
|
||||||
client.on('error', function(err) {
|
client.on('error', function(err) {
|
||||||
assert.strictEqual(err.message, 'SET can\'t be processed. The connection has already been closed.');
|
assert.strictEqual(err.message, 'SET can\'t be processed. The connection has already been closed.');
|
||||||
@@ -339,7 +248,6 @@ describe("The node_redis client", function () {
|
|||||||
return done();
|
return done();
|
||||||
}
|
}
|
||||||
|
|
||||||
if (domain) {
|
|
||||||
domain.run(function () {
|
domain.run(function () {
|
||||||
client.set('domain', 'value', function (err, res) {
|
client.set('domain', 'value', function (err, res) {
|
||||||
assert.ok(process.domain);
|
assert.ok(process.domain);
|
||||||
@@ -349,10 +257,10 @@ describe("The node_redis client", function () {
|
|||||||
|
|
||||||
// this is the expected and desired behavior
|
// this is the expected and desired behavior
|
||||||
domain.on('error', function (err) {
|
domain.on('error', function (err) {
|
||||||
|
assert.strictEqual(err.message, 'ohhhh noooo');
|
||||||
domain.exit();
|
domain.exit();
|
||||||
return done();
|
return done();
|
||||||
});
|
});
|
||||||
}
|
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
@@ -435,7 +343,6 @@ describe("The node_redis client", function () {
|
|||||||
|
|
||||||
describe('socket_nodelay', function () {
|
describe('socket_nodelay', function () {
|
||||||
describe('true', function () {
|
describe('true', function () {
|
||||||
var client;
|
|
||||||
var args = config.configureClient(parser, ip, {
|
var args = config.configureClient(parser, ip, {
|
||||||
socket_nodelay: true
|
socket_nodelay: true
|
||||||
});
|
});
|
||||||
@@ -470,7 +377,6 @@ describe("The node_redis client", function () {
|
|||||||
});
|
});
|
||||||
|
|
||||||
describe('false', function () {
|
describe('false', function () {
|
||||||
var client;
|
|
||||||
var args = config.configureClient(parser, ip, {
|
var args = config.configureClient(parser, ip, {
|
||||||
socket_nodelay: false
|
socket_nodelay: false
|
||||||
});
|
});
|
||||||
@@ -505,7 +411,6 @@ describe("The node_redis client", function () {
|
|||||||
});
|
});
|
||||||
|
|
||||||
describe('defaults to true', function () {
|
describe('defaults to true', function () {
|
||||||
var client;
|
|
||||||
|
|
||||||
it("fires client.on('ready')", function (done) {
|
it("fires client.on('ready')", function (done) {
|
||||||
client = redis.createClient.apply(redis.createClient, args);
|
client = redis.createClient.apply(redis.createClient, args);
|
||||||
@@ -538,7 +443,6 @@ describe("The node_redis client", function () {
|
|||||||
});
|
});
|
||||||
|
|
||||||
describe('retry_max_delay', function () {
|
describe('retry_max_delay', function () {
|
||||||
var client;
|
|
||||||
var args = config.configureClient(parser, ip, {
|
var args = config.configureClient(parser, ip, {
|
||||||
retry_max_delay: 1 // ms
|
retry_max_delay: 1 // ms
|
||||||
});
|
});
|
||||||
@@ -566,8 +470,36 @@ describe("The node_redis client", function () {
|
|||||||
|
|
||||||
describe('enable_offline_queue', function () {
|
describe('enable_offline_queue', function () {
|
||||||
describe('true', function () {
|
describe('true', function () {
|
||||||
|
it("should emit drain after info command and nothing to buffer", function (done) {
|
||||||
|
client = redis.createClient({
|
||||||
|
parser: parser
|
||||||
|
});
|
||||||
|
client.set('foo', 'bar');
|
||||||
|
client.get('foo', function () {
|
||||||
|
assert(!client.should_buffer);
|
||||||
|
setTimeout(done, 25);
|
||||||
|
});
|
||||||
|
client.on('drain', function() {
|
||||||
|
assert(client.offline_queue.length === 2);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it("should emit drain if offline queue is flushed and nothing to buffer", function (done) {
|
||||||
|
client = redis.createClient({
|
||||||
|
parser: parser,
|
||||||
|
no_ready_check: true
|
||||||
|
});
|
||||||
|
var end = helper.callFuncAfter(done, 2);
|
||||||
|
client.set('foo', 'bar');
|
||||||
|
client.get('foo', end);
|
||||||
|
client.on('drain', function() {
|
||||||
|
assert(client.offline_queue.length === 0);
|
||||||
|
end();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
it("does not return an error and enqueues operation", function (done) {
|
it("does not return an error and enqueues operation", function (done) {
|
||||||
var client = redis.createClient(9999, null, {
|
client = redis.createClient(9999, null, {
|
||||||
max_attempts: 0,
|
max_attempts: 0,
|
||||||
parser: parser
|
parser: parser
|
||||||
});
|
});
|
||||||
@@ -578,10 +510,8 @@ describe("The node_redis client", function () {
|
|||||||
|
|
||||||
return setTimeout(function() {
|
return setTimeout(function() {
|
||||||
client.set('foo', 'bar', function(err, result) {
|
client.set('foo', 'bar', function(err, result) {
|
||||||
// TODO: figure out why we emit an error on
|
// This should never be called
|
||||||
// even though we've enabled the offline queue.
|
return done(err);
|
||||||
if (process.platform === 'win32') return;
|
|
||||||
if (err) return done(err);
|
|
||||||
});
|
});
|
||||||
|
|
||||||
return setTimeout(function() {
|
return setTimeout(function() {
|
||||||
@@ -592,7 +522,7 @@ describe("The node_redis client", function () {
|
|||||||
});
|
});
|
||||||
|
|
||||||
it("enqueues operation and keep the queue while trying to reconnect", function (done) {
|
it("enqueues operation and keep the queue while trying to reconnect", function (done) {
|
||||||
var client = redis.createClient(9999, null, {
|
client = redis.createClient(9999, null, {
|
||||||
max_attempts: 4,
|
max_attempts: 4,
|
||||||
parser: parser
|
parser: parser
|
||||||
});
|
});
|
||||||
@@ -627,8 +557,23 @@ describe("The node_redis client", function () {
|
|||||||
});
|
});
|
||||||
|
|
||||||
describe('false', function () {
|
describe('false', function () {
|
||||||
|
|
||||||
|
it('stream not writable', function(done) {
|
||||||
|
client = redis.createClient({
|
||||||
|
parser: parser,
|
||||||
|
enable_offline_queue: false
|
||||||
|
});
|
||||||
|
client.on('ready', function () {
|
||||||
|
client.stream.writable = false;
|
||||||
|
client.set('foo', 'bar', function (err, res) {
|
||||||
|
assert.strictEqual(err.message, "SET can't be processed. Stream not writeable.");
|
||||||
|
done();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
it("emit an error and does not enqueues operation", function (done) {
|
it("emit an error and does not enqueues operation", function (done) {
|
||||||
var client = redis.createClient(9999, null, {
|
client = redis.createClient(9999, null, {
|
||||||
parser: parser,
|
parser: parser,
|
||||||
max_attempts: 0,
|
max_attempts: 0,
|
||||||
enable_offline_queue: false
|
enable_offline_queue: false
|
||||||
@@ -636,7 +581,7 @@ describe("The node_redis client", function () {
|
|||||||
var end = helper.callFuncAfter(done, 3);
|
var end = helper.callFuncAfter(done, 3);
|
||||||
|
|
||||||
client.on('error', function(err) {
|
client.on('error', function(err) {
|
||||||
assert(/Stream not writeable|ECONNREFUSED/.test(err.message));
|
assert(/offline queue is deactivated|ECONNREFUSED/.test(err.message));
|
||||||
assert.equal(client.command_queue.length, 0);
|
assert.equal(client.command_queue.length, 0);
|
||||||
end();
|
end();
|
||||||
});
|
});
|
||||||
@@ -653,7 +598,7 @@ describe("The node_redis client", function () {
|
|||||||
});
|
});
|
||||||
|
|
||||||
it("flushes the command queue connection if in broken connection mode", function (done) {
|
it("flushes the command queue connection if in broken connection mode", function (done) {
|
||||||
var client = redis.createClient({
|
client = redis.createClient({
|
||||||
parser: parser,
|
parser: parser,
|
||||||
max_attempts: 2,
|
max_attempts: 2,
|
||||||
enable_offline_queue: false
|
enable_offline_queue: false
|
||||||
|
113
test/parser.spec.js
Normal file
113
test/parser.spec.js
Normal file
@@ -0,0 +1,113 @@
|
|||||||
|
'use strict';
|
||||||
|
|
||||||
|
var assert = require('assert');
|
||||||
|
var config = require("./lib/config");
|
||||||
|
var utils = require("../lib/utils");
|
||||||
|
var redis = config.redis;
|
||||||
|
var parsers = [
|
||||||
|
require("../lib/parsers/javascript").Parser
|
||||||
|
];
|
||||||
|
try {
|
||||||
|
// Test the hiredis parser if available
|
||||||
|
parsers.push(require("../lib/parsers/hiredis").Parser);
|
||||||
|
} catch (e) {}
|
||||||
|
|
||||||
|
describe('parsers', function () {
|
||||||
|
|
||||||
|
parsers.forEach(function (Parser) {
|
||||||
|
|
||||||
|
describe(Parser.name, function () {
|
||||||
|
|
||||||
|
it('handles multi-bulk reply', function (done) {
|
||||||
|
var parser = new Parser();
|
||||||
|
var reply_count = 0;
|
||||||
|
function check_reply(reply) {
|
||||||
|
reply = utils.reply_to_strings(reply);
|
||||||
|
assert.deepEqual(reply, [['a']], "Expecting multi-bulk reply of [['a']]");
|
||||||
|
reply_count++;
|
||||||
|
}
|
||||||
|
parser.send_reply = check_reply;
|
||||||
|
|
||||||
|
parser.execute(new Buffer('*1\r\n*1\r\n$1\r\na\r\n'));
|
||||||
|
|
||||||
|
parser.execute(new Buffer('*1\r\n*1\r'));
|
||||||
|
parser.execute(new Buffer('\n$1\r\na\r\n'));
|
||||||
|
|
||||||
|
parser.execute(new Buffer('*1\r\n*1\r\n'));
|
||||||
|
parser.execute(new Buffer('$1\r\na\r\n'));
|
||||||
|
|
||||||
|
assert.equal(reply_count, 3, "check reply should have been called three times");
|
||||||
|
return done();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('parser error', function (done) {
|
||||||
|
var parser = new Parser();
|
||||||
|
var reply_count = 0;
|
||||||
|
function check_reply(reply) {
|
||||||
|
assert.strictEqual(reply.message, 'Protocol error, got "a" as reply type byte');
|
||||||
|
reply_count++;
|
||||||
|
}
|
||||||
|
parser.send_error = check_reply;
|
||||||
|
|
||||||
|
parser.execute(new Buffer('a*1\r*1\r$1`zasd\r\na'));
|
||||||
|
|
||||||
|
assert.equal(reply_count, 1, "check reply should have been called one time");
|
||||||
|
return done();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('line breaks in the beginning', function (done) {
|
||||||
|
var parser = new Parser();
|
||||||
|
var reply_count = 0;
|
||||||
|
function check_reply(reply) {
|
||||||
|
reply = utils.reply_to_strings(reply);
|
||||||
|
assert.deepEqual(reply, [['a']], "Expecting multi-bulk reply of [['a']]");
|
||||||
|
reply_count++;
|
||||||
|
}
|
||||||
|
parser.send_reply = check_reply;
|
||||||
|
|
||||||
|
parser.execute(new Buffer('*1\r\n*1\r\n$1\r\na'));
|
||||||
|
|
||||||
|
parser.execute(new Buffer('\r\n*1\r\n*1\r'));
|
||||||
|
parser.execute(new Buffer('\n$1\r\na\r\n'));
|
||||||
|
|
||||||
|
parser.execute(new Buffer('*1\r\n*1\r\n'));
|
||||||
|
parser.execute(new Buffer('$1\r\na\r\n'));
|
||||||
|
|
||||||
|
assert.equal(reply_count, 3, "check reply should have been called three times");
|
||||||
|
return done();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
// Activate this if you want to fry your cpu / memory
|
||||||
|
describe.skip("test out of memory", function () {
|
||||||
|
var args = config.configureClient('javascript', '127.0.0.1');
|
||||||
|
var clients = new Array(300).join(" ").split(" ");
|
||||||
|
var client;
|
||||||
|
beforeEach(function (done) {
|
||||||
|
client = redis.createClient.apply(redis.createClient, args);
|
||||||
|
client.once("connect", function () {
|
||||||
|
client.flushdb(done);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it('reach limit and wait for further data', function (done) {
|
||||||
|
setTimeout(done, 5000);
|
||||||
|
clients.forEach(function(entry, a) {
|
||||||
|
var max = 0;
|
||||||
|
var client = redis.createClient.apply(redis.createClient, args);
|
||||||
|
client.on('ready', function() {
|
||||||
|
while (++max < 50) {
|
||||||
|
var item = [];
|
||||||
|
for (var i = 0; i < 100; ++i) {
|
||||||
|
item.push('aaa' + (Math.random() * 1000000 | 0));
|
||||||
|
}
|
||||||
|
client.del('foo' + a);
|
||||||
|
client.lpush('foo' + a, item);
|
||||||
|
client.lrange('foo' + a, 0, 99);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
@@ -1,63 +0,0 @@
|
|||||||
'use strict';
|
|
||||||
|
|
||||||
var assert = require('assert');
|
|
||||||
var Parser = require("../../lib/parsers/javascript").Parser;
|
|
||||||
var config = require("../lib/config");
|
|
||||||
var utils = require("../../lib/utils");
|
|
||||||
var redis = config.redis;
|
|
||||||
|
|
||||||
describe('javascript parser', function () {
|
|
||||||
it('handles multi-bulk reply', function (done) {
|
|
||||||
var parser = new Parser();
|
|
||||||
var reply_count = 0;
|
|
||||||
function check_reply(reply) {
|
|
||||||
reply = utils.reply_to_strings(reply);
|
|
||||||
assert.deepEqual(reply, [['a']], "Expecting multi-bulk reply of [['a']]");
|
|
||||||
reply_count++;
|
|
||||||
}
|
|
||||||
parser.send_reply = check_reply;
|
|
||||||
|
|
||||||
parser.execute(new Buffer('*1\r\n*1\r\n$1\r\na\r\n'));
|
|
||||||
|
|
||||||
parser.execute(new Buffer('*1\r\n*1\r'));
|
|
||||||
parser.execute(new Buffer('\n$1\r\na\r\n'));
|
|
||||||
|
|
||||||
parser.execute(new Buffer('*1\r\n*1\r\n'));
|
|
||||||
parser.execute(new Buffer('$1\r\na\r\n'));
|
|
||||||
|
|
||||||
assert.equal(reply_count, 3, "check reply should have been called three times");
|
|
||||||
return done();
|
|
||||||
});
|
|
||||||
|
|
||||||
// Activate this if you want to fry your cpu / memory
|
|
||||||
describe.skip("test out of memory", function () {
|
|
||||||
var args = config.configureClient('javascript', '127.0.0.1');
|
|
||||||
var clients = new Array(300).join(" ").split(" ");
|
|
||||||
var client;
|
|
||||||
beforeEach(function (done) {
|
|
||||||
client = redis.createClient.apply(redis.createClient, args);
|
|
||||||
client.once("connect", function () {
|
|
||||||
client.flushdb(done);
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
it('reach limit and wait for further data', function (done) {
|
|
||||||
setTimeout(done, 5000);
|
|
||||||
clients.forEach(function(entry, a) {
|
|
||||||
var max = 0;
|
|
||||||
var client = redis.createClient.apply(redis.createClient, args);
|
|
||||||
client.on('ready', function() {
|
|
||||||
while (++max < 50) {
|
|
||||||
var item = [];
|
|
||||||
for (var i = 0; i < 100; ++i) {
|
|
||||||
item.push('aaa' + (Math.random() * 1000000 | 0));
|
|
||||||
}
|
|
||||||
client.del('foo' + a);
|
|
||||||
client.lpush('foo' + a, item);
|
|
||||||
client.lrange('foo' + a, 0, 99);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
});
|
|
||||||
});
|
|
||||||
});
|
|
||||||
});
|
|
@@ -242,8 +242,12 @@ describe("publish/subscribe", function () {
|
|||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
it('does not complain when unsubscribe is called and there are no subscriptions', function () {
|
it('does not complain when unsubscribe is called and there are no subscriptions', function (done) {
|
||||||
sub.unsubscribe();
|
sub.unsubscribe(function (err, res) {
|
||||||
|
assert.strictEqual(err, null);
|
||||||
|
assert.strictEqual(res, null);
|
||||||
|
done();
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
it('executes callback when unsubscribe is called and there are no subscriptions', function (done) {
|
it('executes callback when unsubscribe is called and there are no subscriptions', function (done) {
|
||||||
@@ -285,6 +289,34 @@ describe("publish/subscribe", function () {
|
|||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
describe('fail for other commands while in pub sub mode', function () {
|
||||||
|
it('return error if only pub sub commands are allowed', function (done) {
|
||||||
|
sub.subscribe('channel');
|
||||||
|
// Ping is allowed even if not listed as such!
|
||||||
|
sub.ping(function (err, res) {
|
||||||
|
assert.strictEqual(err, null);
|
||||||
|
assert.strictEqual(res[0], 'pong');
|
||||||
|
});
|
||||||
|
// Get is forbidden
|
||||||
|
sub.get('foo', function (err, res) {
|
||||||
|
assert.strictEqual(err.message, 'ERR only (P)SUBSCRIBE / (P)UNSUBSCRIBE / QUIT allowed in this context');
|
||||||
|
assert.strictEqual(err.command, 'GET');
|
||||||
|
});
|
||||||
|
// Quit is allowed
|
||||||
|
sub.quit(done);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('emit error if only pub sub commands are allowed without callback', function (done) {
|
||||||
|
sub.subscribe('channel');
|
||||||
|
sub.on('error', function (err) {
|
||||||
|
assert.strictEqual(err.message, 'ERR only (P)SUBSCRIBE / (P)UNSUBSCRIBE / QUIT allowed in this context');
|
||||||
|
assert.strictEqual(err.command, 'GET');
|
||||||
|
done();
|
||||||
|
});
|
||||||
|
sub.get('foo');
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
// TODO: Fix pub sub
|
// TODO: Fix pub sub
|
||||||
// And there's more than just those two issues
|
// And there's more than just those two issues
|
||||||
describe.skip('FIXME: broken pub sub', function () {
|
describe.skip('FIXME: broken pub sub', function () {
|
||||||
@@ -297,7 +329,7 @@ describe("publish/subscribe", function () {
|
|||||||
pub.on('message', function (msg) {
|
pub.on('message', function (msg) {
|
||||||
done(new Error('This message should not have been published: ' + msg));
|
done(new Error('This message should not have been published: ' + msg));
|
||||||
});
|
});
|
||||||
setTimeout(done, 500);
|
setTimeout(done, 200);
|
||||||
});
|
});
|
||||||
|
|
||||||
it("should not publish a message multiple times per command", function (done) {
|
it("should not publish a message multiple times per command", function (done) {
|
||||||
@@ -325,8 +357,8 @@ describe("publish/subscribe", function () {
|
|||||||
sub.unsubscribe();
|
sub.unsubscribe();
|
||||||
setTimeout(function () {
|
setTimeout(function () {
|
||||||
subscribe('world');
|
subscribe('world');
|
||||||
}, 400);
|
}, 40);
|
||||||
}, 400);
|
}, 40);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@@ -19,6 +19,8 @@ describe("return_buffers", function () {
|
|||||||
beforeEach(function (done) {
|
beforeEach(function (done) {
|
||||||
client = redis.createClient.apply(redis.createClient, args);
|
client = redis.createClient.apply(redis.createClient, args);
|
||||||
if (args[2].detect_buffers) {
|
if (args[2].detect_buffers) {
|
||||||
|
// Test if detect_buffer option was deactivated
|
||||||
|
assert.strictEqual(client.options.detect_buffers, false);
|
||||||
args[2].detect_buffers = false;
|
args[2].detect_buffers = false;
|
||||||
}
|
}
|
||||||
client.once("error", done);
|
client.once("error", done);
|
||||||
|
Reference in New Issue
Block a user