From a67d3acdd6b6f2e04ba642b43a75d66825ec668b Mon Sep 17 00:00:00 2001 From: Hiroshi Kuwabara Date: Fri, 12 Sep 2014 12:37:32 +0900 Subject: [PATCH 01/49] added URL support to createClient --- index.js | 15 +++++++++++++-- test.js | 19 +++++++++++++++++++ 2 files changed, 32 insertions(+), 2 deletions(-) diff --git a/index.js b/index.js index 7ec74775c0..f2d5ed43cc 100644 --- a/index.js +++ b/index.js @@ -1,6 +1,7 @@ /*global Buffer require exports console setTimeout */ var net = require("net"), + URL = require("url"), util = require("./lib/util"), Queue = require("./lib/queue"), to_array = require("./lib/to_array"), @@ -1227,9 +1228,19 @@ exports.createClient = function(arg0, arg1, arg2){ return createClient_tcp(arg0, arg1, arg2); } else if( typeof arg0 === 'string' ){ + var parsed = URL.parse(arg0, true, true), + options = (arg1 || {}); - // createClient( '/tmp/redis.sock', options) - return createClient_unix(arg0,arg1); + if (parsed.hostname) { + if (parsed.auth) { + options.auth_pass = parsed.auth.split(':')[1]; + } + // createClient(3000, host, options) + return createClient_tcp((parsed.port || default_port), parsed.hostname, options); + } else { + // createClient( '/tmp/redis.sock', options) + return createClient_unix(arg0,options); + } } else if( arg0 !== null && typeof arg0 === 'object' ){ diff --git a/test.js b/test.js index b50c05d5bc..734e5e9c19 100644 --- a/test.js +++ b/test.js @@ -2169,6 +2169,25 @@ tests.auth2 = function () { }); }; +// auth password specified by URL string. +tests.auth3 = function () { + var name = "AUTH3", client4, ready_count = 0; + + client4 = redis.createClient('redis://redistogo:664b1b6aaf134e1ec281945a8de702a9@filefish.redistogo.com:9006/'); + + // test auth, then kill the connection so it'll auto-reconnect and auto-re-auth + client4.on("ready", function () { + ready_count++; + if (ready_count === 1) { + client4.stream.destroy(); + } else { + client4.quit(function (err, res) { + next(name); + }); + } + }); +}; + tests.reconnectRetryMaxDelay = function() { var time = new Date().getTime(), name = 'reconnectRetryMaxDelay', From 13162e554bc7174178a59499f6dc48ac42667c0f Mon Sep 17 00:00:00 2001 From: Kyle Mitchell Date: Tue, 5 May 2015 00:03:36 +0000 Subject: [PATCH 02/49] use a valid SPDX license identifier --- package.json | 1 + 1 file changed, 1 insertion(+) diff --git a/package.json b/package.json index dc1c39abd2..8aba1e91e3 100644 --- a/package.json +++ b/package.json @@ -7,6 +7,7 @@ "database" ], "author": "Matt Ranney ", + "license": "MIT", "main": "./index.js", "scripts": { "test": "node ./test.js" From 06b294c630d9695eba250aba1c57d2e51ab81e54 Mon Sep 17 00:00:00 2001 From: Benjamin Coe Date: Mon, 25 May 2015 16:25:47 -0700 Subject: [PATCH 03/49] add coverage reporting --- .gitignore | 2 ++ package.json | 6 ++++-- 2 files changed, 6 insertions(+), 2 deletions(-) diff --git a/.gitignore b/.gitignore index 6a59b3d64c..f351fac36f 100644 --- a/.gitignore +++ b/.gitignore @@ -1,2 +1,4 @@ node_modules .tern-port +.nyc_output +coverage diff --git a/package.json b/package.json index dc1c39abd2..b02f7d1736 100644 --- a/package.json +++ b/package.json @@ -9,11 +9,13 @@ "author": "Matt Ranney ", "main": "./index.js", "scripts": { - "test": "node ./test.js" + "test": "node ./test.js", + "coverage": "nyc npm test && nyc report" }, "devDependencies": { - "metrics": ">=0.1.5", "colors": "~0.6.0-1", + "metrics": ">=0.1.5", + "nyc": "^2.2.0", "underscore": "~1.4.4" }, "repository": { From eb03bb943c9d0fed40d0c35965ac0c9624176908 Mon Sep 17 00:00:00 2001 From: Matt Ranney Date: Fri, 10 Jul 2015 17:25:17 -0400 Subject: [PATCH 04/49] Update README and multi_bench --- README.md | 150 ++++++++++++++++++++++++++++++++++--------------- multi_bench.js | 82 +++++++++++---------------- 2 files changed, 140 insertions(+), 92 deletions(-) diff --git a/README.md b/README.md index be0daaca52..c3ffd1c898 100644 --- a/README.md +++ b/README.md @@ -1,24 +1,14 @@ redis - a node.js redis client =========================== -This is a complete Redis client for node.js. It supports all Redis commands, including many recently added commands like EVAL from -experimental Redis server branches. - +This is a complete Redis client for node.js. It supports all Redis commands, +including many recently added commands like EVAL from experimental Redis server +branches. Install with: npm install redis -Pieter Noordhuis has provided a binding to the official `hiredis` C library, which is non-blocking and fast. To use `hiredis`, do: - - npm install hiredis redis - -If `hiredis` is installed, `node_redis` will use it by default. Otherwise, a pure JavaScript parser will be used. - -If you use `hiredis`, be sure to rebuild it whenever you upgrade your version of node. There are mysterious failures that can -happen between node and native code modules after a node upgrade. - - ## Usage Simple example, included as `examples/simple.js`: @@ -57,34 +47,9 @@ This will display: 1: hashtest 2 mjr:~/work/node_redis (master)$ - -## Performance - -Here are typical results of `multi_bench.js` which is similar to `redis-benchmark` from the Redis distribution. -It uses 50 concurrent connections with no pipelining. - -JavaScript parser: - - PING: 20000 ops 42283.30 ops/sec 0/5/1.182 - SET: 20000 ops 32948.93 ops/sec 1/7/1.515 - GET: 20000 ops 28694.40 ops/sec 0/9/1.740 - INCR: 20000 ops 39370.08 ops/sec 0/8/1.269 - LPUSH: 20000 ops 36429.87 ops/sec 0/8/1.370 - LRANGE (10 elements): 20000 ops 9891.20 ops/sec 1/9/5.048 - LRANGE (100 elements): 20000 ops 1384.56 ops/sec 10/91/36.072 - -hiredis parser: - - PING: 20000 ops 46189.38 ops/sec 1/4/1.082 - SET: 20000 ops 41237.11 ops/sec 0/6/1.210 - GET: 20000 ops 39682.54 ops/sec 1/7/1.257 - INCR: 20000 ops 40080.16 ops/sec 0/8/1.242 - LPUSH: 20000 ops 41152.26 ops/sec 0/3/1.212 - LRANGE (10 elements): 20000 ops 36563.07 ops/sec 1/8/1.363 - LRANGE (100 elements): 20000 ops 21834.06 ops/sec 0/9/2.287 - -The performance of `node_redis` improves dramatically with pipelining, which happens automatically in most normal programs. - +Note that the API is entire asynchronous. To get data back from the server, +you'll need to use a callback. The return value from most of the API is a +backpressure indicator. ### Sending Commands @@ -658,6 +623,105 @@ client.zadd(args, function (err, response) { }); ``` +## Performance + +Much effort has been spent to make `node_redis` as fast as possible for common +operations. As pipelining happens naturally from shared connections, overall +efficiency goes up. + +Here are typical results of `multi_bench.js` which is similar to +`redis-benchmark` from the Redis distribution. It uses 5 concurrent connections +and shows the difference between pipelines of 1 and 50. + +JavaScript parser: + + Client count: 5, node version: 0.10.32, server version: 2.8.18, parser: javascript + PING, 1/5 min/max/avg/p95: 0/ 3/ 0.05/ 1.00 1103ms total, 18132.37 ops/sec + PING, 50/5 min/max/avg/p95: 0/ 4/ 0.81/ 2.00 327ms total, 61162.08 ops/sec + SET 4B str, 1/5 min/max/avg/p95: 0/ 2/ 0.05/ 0.00 1104ms total, 18115.94 ops/sec + SET 4B str, 50/5 min/max/avg/p95: 0/ 3/ 0.83/ 2.00 333ms total, 60060.06 ops/sec + SET 4B buf, 1/5 min/max/avg/p95: 0/ 2/ 0.09/ 1.00 1876ms total, 10660.98 ops/sec + SET 4B buf, 50/5 min/max/avg/p95: 0/ 11/ 2.55/ 4.00 1025ms total, 19512.20 ops/sec + GET 4B str, 1/5 min/max/avg/p95: 0/ 1/ 0.05/ 1.00 1117ms total, 17905.10 ops/sec + GET 4B str, 50/5 min/max/avg/p95: 0/ 3/ 0.87/ 2.00 347ms total, 57636.89 ops/sec + GET 4B buf, 1/5 min/max/avg/p95: 0/ 1/ 0.05/ 1.00 1110ms total, 18018.02 ops/sec + GET 4B buf, 50/5 min/max/avg/p95: 0/ 2/ 0.85/ 2.00 342ms total, 58479.53 ops/sec + SET 4KiB str, 1/5 min/max/avg/p95: 0/ 1/ 0.05/ 1.00 1119ms total, 17873.10 ops/sec + SET 4KiB str, 50/5 min/max/avg/p95: 0/ 3/ 0.89/ 2.00 358ms total, 55865.92 ops/sec + SET 4KiB buf, 1/5 min/max/avg/p95: 0/ 1/ 0.09/ 1.00 1894ms total, 10559.66 ops/sec + SET 4KiB buf, 50/5 min/max/avg/p95: 0/ 7/ 2.57/ 4.00 1031ms total, 19398.64 ops/sec + GET 4KiB str, 1/5 min/max/avg/p95: 0/ 6/ 0.06/ 1.00 1248ms total, 16025.64 ops/sec + GET 4KiB str, 50/5 min/max/avg/p95: 0/ 3/ 1.03/ 2.00 415ms total, 48192.77 ops/sec + GET 4KiB buf, 1/5 min/max/avg/p95: 0/ 1/ 0.06/ 1.00 1177ms total, 16992.35 ops/sec + GET 4KiB buf, 50/5 min/max/avg/p95: 0/ 10/ 1.02/ 2.00 409ms total, 48899.76 ops/sec + INCR, 1/5 min/max/avg/p95: 0/ 2/ 0.05/ 0.55 1137ms total, 17590.15 ops/sec + INCR, 50/5 min/max/avg/p95: 0/ 2/ 0.85/ 2.00 343ms total, 58309.04 ops/sec + LPUSH, 1/5 min/max/avg/p95: 0/ 1/ 0.06/ 1.00 1143ms total, 17497.81 ops/sec + LPUSH, 50/5 min/max/avg/p95: 0/ 3/ 0.87/ 2.00 350ms total, 57142.86 ops/sec + LRANGE 10, 1/5 min/max/avg/p95: 0/ 2/ 0.06/ 1.00 1283ms total, 15588.46 ops/sec + LRANGE 10, 50/5 min/max/avg/p95: 0/ 3/ 1.12/ 2.00 449ms total, 44543.43 ops/sec + LRANGE 100, 1/5 min/max/avg/p95: 0/ 1/ 0.09/ 1.00 1932ms total, 10351.97 ops/sec + LRANGE 100, 50/5 min/max/avg/p95: 0/ 5/ 2.46/ 4.00 985ms total, 20304.57 ops/sec + SET 4MiB buf, 1/5 min/max/avg/p95: 1/ 4/ 1.37/ 2.00 691ms total, 723.59 ops/sec + SET 4MiB buf, 50/5 min/max/avg/p95: 3/ 166/ 57.66/ 116.00 601ms total, 831.95 ops/sec + GET 4MiB str, 1/5 min/max/avg/p95: 84/ 110/ 93.18/ 106.95 9320ms total, 10.73 ops/sec + GET 4MiB str, 50/5 min/max/avg/p95: 156/7375/3400.10/6840.40 8928ms total, 11.20 ops/sec + GET 4MiB buf, 1/5 min/max/avg/p95: 84/ 105/ 91.21/ 99.00 9129ms total, 10.95 ops/sec + GET 4MiB buf, 50/5 min/max/avg/p95: 424/5704/3518.94/5626.65 9145ms total, 10.93 ops/sec + +If you use very large responses in your application, the JavaScript parser +performs badly. Until the JS parser is fixed, you can use the C-based `hiredis` +parser bound to the official `hiredis` C library. To use `hiredis`, do: + + npm install hiredis redis + +If the `hiredis` npm module is installed, `node_redis` will use it by default. +Otherwise, the pure JavaScript parser will be used. + +If you use `hiredis`, be sure to rebuild it whenever you upgrade your version of +node. There are mysterious failures that can happen between node and native +code modules after a node upgrade. + +Most users find that the JS parser is faster than the `hiredis` parser. Because +of the pain associated with upgrading native code modules, you should only use +`hiredis` if your application needs it. + +hiredis parser: + + Client count: 5, node version: 0.10.32, server version: 2.8.18, parser: hiredis + PING, 1/5 min/max/avg/p95: 0/ 3/ 0.05/ 1.00 1092ms total, 18315.02 ops/sec + PING, 50/5 min/max/avg/p95: 0/ 5/ 0.87/ 2.00 347ms total, 57636.89 ops/sec + SET 4B str, 1/5 min/max/avg/p95: 0/ 2/ 0.06/ 1.00 1151ms total, 17376.19 ops/sec + SET 4B str, 50/5 min/max/avg/p95: 0/ 3/ 0.83/ 2.00 334ms total, 59880.24 ops/sec + SET 4B buf, 1/5 min/max/avg/p95: 0/ 3/ 0.09/ 1.00 1932ms total, 10351.97 ops/sec + SET 4B buf, 50/5 min/max/avg/p95: 0/ 9/ 2.64/ 4.00 1059ms total, 18885.74 ops/sec + GET 4B str, 1/5 min/max/avg/p95: 0/ 1/ 0.06/ 0.00 1185ms total, 16877.64 ops/sec + GET 4B str, 50/5 min/max/avg/p95: 0/ 3/ 0.85/ 2.00 341ms total, 58651.03 ops/sec + GET 4B buf, 1/5 min/max/avg/p95: 0/ 1/ 0.06/ 0.00 1179ms total, 16963.53 ops/sec + GET 4B buf, 50/5 min/max/avg/p95: 0/ 3/ 0.85/ 2.00 340ms total, 58823.53 ops/sec + SET 4KiB str, 1/5 min/max/avg/p95: 0/ 1/ 0.06/ 1.00 1210ms total, 16528.93 ops/sec + SET 4KiB str, 50/5 min/max/avg/p95: 0/ 3/ 0.93/ 2.00 372ms total, 53763.44 ops/sec + SET 4KiB buf, 1/5 min/max/avg/p95: 0/ 1/ 0.10/ 1.00 1967ms total, 10167.77 ops/sec + SET 4KiB buf, 50/5 min/max/avg/p95: 0/ 6/ 2.63/ 4.00 1053ms total, 18993.35 ops/sec + GET 4KiB str, 1/5 min/max/avg/p95: 0/ 6/ 0.06/ 1.00 1176ms total, 17006.80 ops/sec + GET 4KiB str, 50/5 min/max/avg/p95: 0/ 4/ 1.00/ 2.00 399ms total, 50125.31 ops/sec + GET 4KiB buf, 1/5 min/max/avg/p95: 0/ 1/ 0.06/ 1.00 1158ms total, 17271.16 ops/sec + GET 4KiB buf, 50/5 min/max/avg/p95: 0/ 3/ 0.99/ 2.00 398ms total, 50251.26 ops/sec + INCR, 1/5 min/max/avg/p95: 0/ 1/ 0.05/ 0.00 1112ms total, 17985.61 ops/sec + INCR, 50/5 min/max/avg/p95: 0/ 3/ 0.84/ 2.00 339ms total, 58997.05 ops/sec + LPUSH, 1/5 min/max/avg/p95: 0/ 1/ 0.05/ 1.00 1131ms total, 17683.47 ops/sec + LPUSH, 50/5 min/max/avg/p95: 0/ 3/ 0.86/ 2.00 345ms total, 57971.01 ops/sec + LRANGE 10, 1/5 min/max/avg/p95: 0/ 1/ 0.06/ 1.00 1228ms total, 16286.64 ops/sec + LRANGE 10, 50/5 min/max/avg/p95: 0/ 3/ 0.95/ 2.00 382ms total, 52356.02 ops/sec + LRANGE 100, 1/5 min/max/avg/p95: 0/ 1/ 0.08/ 1.00 1567ms total, 12763.24 ops/sec + LRANGE 100, 50/5 min/max/avg/p95: 0/ 6/ 1.68/ 3.00 675ms total, 29629.63 ops/sec + SET 4MiB buf, 1/5 min/max/avg/p95: 1/ 4/ 1.37/ 2.00 692ms total, 722.54 ops/sec + SET 4MiB buf, 50/5 min/max/avg/p95: 3/ 183/ 57.79/ 125.00 605ms total, 826.45 ops/sec + GET 4MiB str, 1/5 min/max/avg/p95: 5/ 16/ 8.14/ 12.95 816ms total, 122.55 ops/sec + GET 4MiB str, 50/5 min/max/avg/p95: 24/ 323/ 202.98/ 309.00 519ms total, 192.68 ops/sec + GET 4MiB buf, 1/5 min/max/avg/p95: 6/ 13/ 8.01/ 11.95 802ms total, 124.69 ops/sec + GET 4MiB buf, 50/5 min/max/avg/p95: 16/ 480/ 203.85/ 435.70 531ms total, 188.32 ops/sec + ## TODO Better tests for auth, disconnect/reconnect, and all combinations thereof. @@ -674,7 +738,7 @@ I think there are more performance improvements left in there for smaller values comment again with indignation!) ## Contributors -Some people have have added features and fixed bugs in `node_redis` other than me. +Many people have have added features and fixed bugs in `node_redis`. Ordered by date of first contribution. [Auto-generated](http://github.com/dtrejo/node-authors) on Wed Jul 25 2012 19:14:59 GMT-0700 (PDT). @@ -740,5 +804,3 @@ HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. - -![spacer](http://ranney.com/1px.gif) diff --git a/multi_bench.js b/multi_bench.js index 3a0d92da14..dbe04e515a 100644 --- a/multi_bench.js +++ b/multi_bench.js @@ -7,7 +7,7 @@ var redis = require("./index"), client_options = { return_buffers: false }, - small_str, large_str, small_buf, large_buf; + small_str, large_str, small_buf, large_buf, very_large_str, very_large_buf; redis.debug_mode = false; @@ -37,7 +37,8 @@ function Test(args) { this.commands_completed = 0; this.max_pipeline = this.args.pipeline || num_requests; this.client_options = args.client_options || client_options; - + this.num_requests = args.reqs || num_requests; + this.connect_latency = new metrics.Histogram(); this.ready_latency = new metrics.Histogram(); this.command_latency = new metrics.Histogram(); @@ -89,13 +90,13 @@ Test.prototype.on_clients_ready = function () { Test.prototype.fill_pipeline = function () { var pipeline = this.commands_sent - this.commands_completed; - while (this.commands_sent < num_requests && pipeline < this.max_pipeline) { + while (this.commands_sent < this.num_requests && pipeline < this.max_pipeline) { this.commands_sent++; pipeline++; this.send_next(); } - if (this.commands_completed === num_requests) { + if (this.commands_completed === this.num_requests) { this.print_stats(); this.stop_clients(); } @@ -134,78 +135,63 @@ Test.prototype.print_stats = function () { var duration = Date.now() - this.test_start; console.log("min/max/avg/p95: " + this.command_latency.print_line() + " " + lpad(duration, 6) + "ms total, " + - lpad((num_requests / (duration / 1000)).toFixed(2), 8) + " ops/sec"); + lpad((this.num_requests / (duration / 1000)).toFixed(2), 8) + " ops/sec"); }; small_str = "1234"; small_buf = new Buffer(small_str); -large_str = (new Array(4097).join("-")); +large_str = (new Array(4096 + 1).join("-")); large_buf = new Buffer(large_str); +very_large_str = (new Array((4 * 1024 * 1024) + 1).join("-")); +very_large_buf = new Buffer(very_large_str); tests.push(new Test({descr: "PING", command: "ping", args: [], pipeline: 1})); tests.push(new Test({descr: "PING", command: "ping", args: [], pipeline: 50})); -tests.push(new Test({descr: "PING", command: "ping", args: [], pipeline: 200})); -tests.push(new Test({descr: "PING", command: "ping", args: [], pipeline: 20000})); -tests.push(new Test({descr: "SET small str", command: "set", args: ["foo_rand000000000000", small_str], pipeline: 1})); -tests.push(new Test({descr: "SET small str", command: "set", args: ["foo_rand000000000000", small_str], pipeline: 50})); -tests.push(new Test({descr: "SET small str", command: "set", args: ["foo_rand000000000000", small_str], pipeline: 200})); -tests.push(new Test({descr: "SET small str", command: "set", args: ["foo_rand000000000000", small_str], pipeline: 20000})); +tests.push(new Test({descr: "SET 4B str", command: "set", args: ["foo_rand000000000000", small_str], pipeline: 1})); +tests.push(new Test({descr: "SET 4B str", command: "set", args: ["foo_rand000000000000", small_str], pipeline: 50})); -tests.push(new Test({descr: "SET small buf", command: "set", args: ["foo_rand000000000000", small_buf], pipeline: 1})); -tests.push(new Test({descr: "SET small buf", command: "set", args: ["foo_rand000000000000", small_buf], pipeline: 50})); -tests.push(new Test({descr: "SET small buf", command: "set", args: ["foo_rand000000000000", small_buf], pipeline: 200})); -tests.push(new Test({descr: "SET small buf", command: "set", args: ["foo_rand000000000000", small_buf], pipeline: 20000})); +tests.push(new Test({descr: "SET 4B buf", command: "set", args: ["foo_rand000000000000", small_buf], pipeline: 1})); +tests.push(new Test({descr: "SET 4B buf", command: "set", args: ["foo_rand000000000000", small_buf], pipeline: 50})); -tests.push(new Test({descr: "GET small str", command: "get", args: ["foo_rand000000000000"], pipeline: 1})); -tests.push(new Test({descr: "GET small str", command: "get", args: ["foo_rand000000000000"], pipeline: 50})); -tests.push(new Test({descr: "GET small str", command: "get", args: ["foo_rand000000000000"], pipeline: 200})); -tests.push(new Test({descr: "GET small str", command: "get", args: ["foo_rand000000000000"], pipeline: 20000})); +tests.push(new Test({descr: "GET 4B str", command: "get", args: ["foo_rand000000000000"], pipeline: 1})); +tests.push(new Test({descr: "GET 4B str", command: "get", args: ["foo_rand000000000000"], pipeline: 50})); -tests.push(new Test({descr: "GET small buf", command: "get", args: ["foo_rand000000000000"], pipeline: 1, client_opts: { return_buffers: true} })); -tests.push(new Test({descr: "GET small buf", command: "get", args: ["foo_rand000000000000"], pipeline: 50, client_opts: { return_buffers: true} })); -tests.push(new Test({descr: "GET small buf", command: "get", args: ["foo_rand000000000000"], pipeline: 200, client_opts: { return_buffers: true} })); -tests.push(new Test({descr: "GET small buf", command: "get", args: ["foo_rand000000000000"], pipeline: 20000, client_opts: { return_buffers: true} })); +tests.push(new Test({descr: "GET 4B buf", command: "get", args: ["foo_rand000000000000"], pipeline: 1, client_opts: { return_buffers: true} })); +tests.push(new Test({descr: "GET 4B buf", command: "get", args: ["foo_rand000000000000"], pipeline: 50, client_opts: { return_buffers: true} })); -tests.push(new Test({descr: "SET large str", command: "set", args: ["foo_rand000000000001", large_str], pipeline: 1})); -tests.push(new Test({descr: "SET large str", command: "set", args: ["foo_rand000000000001", large_str], pipeline: 50})); -tests.push(new Test({descr: "SET large str", command: "set", args: ["foo_rand000000000001", large_str], pipeline: 200})); -tests.push(new Test({descr: "SET large str", command: "set", args: ["foo_rand000000000001", large_str], pipeline: 20000})); +tests.push(new Test({descr: "SET 4KiB str", command: "set", args: ["foo_rand000000000001", large_str], pipeline: 1})); +tests.push(new Test({descr: "SET 4KiB str", command: "set", args: ["foo_rand000000000001", large_str], pipeline: 50})); -tests.push(new Test({descr: "SET large buf", command: "set", args: ["foo_rand000000000001", large_buf], pipeline: 1})); -tests.push(new Test({descr: "SET large buf", command: "set", args: ["foo_rand000000000001", large_buf], pipeline: 50})); -tests.push(new Test({descr: "SET large buf", command: "set", args: ["foo_rand000000000001", large_buf], pipeline: 200})); -tests.push(new Test({descr: "SET large buf", command: "set", args: ["foo_rand000000000001", large_buf], pipeline: 20000})); +tests.push(new Test({descr: "SET 4KiB buf", command: "set", args: ["foo_rand000000000001", large_buf], pipeline: 1})); +tests.push(new Test({descr: "SET 4KiB buf", command: "set", args: ["foo_rand000000000001", large_buf], pipeline: 50})); -tests.push(new Test({descr: "GET large str", command: "get", args: ["foo_rand000000000001"], pipeline: 1})); -tests.push(new Test({descr: "GET large str", command: "get", args: ["foo_rand000000000001"], pipeline: 50})); -tests.push(new Test({descr: "GET large str", command: "get", args: ["foo_rand000000000001"], pipeline: 200})); -tests.push(new Test({descr: "GET large str", command: "get", args: ["foo_rand000000000001"], pipeline: 20000})); +tests.push(new Test({descr: "GET 4KiB str", command: "get", args: ["foo_rand000000000001"], pipeline: 1})); +tests.push(new Test({descr: "GET 4KiB str", command: "get", args: ["foo_rand000000000001"], pipeline: 50})); -tests.push(new Test({descr: "GET large buf", command: "get", args: ["foo_rand000000000001"], pipeline: 1, client_opts: { return_buffers: true} })); -tests.push(new Test({descr: "GET large buf", command: "get", args: ["foo_rand000000000001"], pipeline: 50, client_opts: { return_buffers: true} })); -tests.push(new Test({descr: "GET large buf", command: "get", args: ["foo_rand000000000001"], pipeline: 200, client_opts: { return_buffers: true} })); -tests.push(new Test({descr: "GET large buf", command: "get", args: ["foo_rand000000000001"], pipeline: 20000, client_opts: { return_buffers: true} })); +tests.push(new Test({descr: "GET 4KiB buf", command: "get", args: ["foo_rand000000000001"], pipeline: 1, client_opts: { return_buffers: true} })); +tests.push(new Test({descr: "GET 4KiB buf", command: "get", args: ["foo_rand000000000001"], pipeline: 50, client_opts: { return_buffers: true} })); tests.push(new Test({descr: "INCR", command: "incr", args: ["counter_rand000000000000"], pipeline: 1})); tests.push(new Test({descr: "INCR", command: "incr", args: ["counter_rand000000000000"], pipeline: 50})); -tests.push(new Test({descr: "INCR", command: "incr", args: ["counter_rand000000000000"], pipeline: 200})); -tests.push(new Test({descr: "INCR", command: "incr", args: ["counter_rand000000000000"], pipeline: 20000})); tests.push(new Test({descr: "LPUSH", command: "lpush", args: ["mylist", small_str], pipeline: 1})); tests.push(new Test({descr: "LPUSH", command: "lpush", args: ["mylist", small_str], pipeline: 50})); -tests.push(new Test({descr: "LPUSH", command: "lpush", args: ["mylist", small_str], pipeline: 200})); -tests.push(new Test({descr: "LPUSH", command: "lpush", args: ["mylist", small_str], pipeline: 20000})); tests.push(new Test({descr: "LRANGE 10", command: "lrange", args: ["mylist", "0", "9"], pipeline: 1})); tests.push(new Test({descr: "LRANGE 10", command: "lrange", args: ["mylist", "0", "9"], pipeline: 50})); -tests.push(new Test({descr: "LRANGE 10", command: "lrange", args: ["mylist", "0", "9"], pipeline: 200})); -tests.push(new Test({descr: "LRANGE 10", command: "lrange", args: ["mylist", "0", "9"], pipeline: 20000})); tests.push(new Test({descr: "LRANGE 100", command: "lrange", args: ["mylist", "0", "99"], pipeline: 1})); tests.push(new Test({descr: "LRANGE 100", command: "lrange", args: ["mylist", "0", "99"], pipeline: 50})); -tests.push(new Test({descr: "LRANGE 100", command: "lrange", args: ["mylist", "0", "99"], pipeline: 200})); -tests.push(new Test({descr: "LRANGE 100", command: "lrange", args: ["mylist", "0", "99"], pipeline: 20000})); + +tests.push(new Test({descr: "SET 4MiB buf", command: "set", args: ["foo_rand000000000002", very_large_buf], pipeline: 1, reqs: 500})); +tests.push(new Test({descr: "SET 4MiB buf", command: "set", args: ["foo_rand000000000002", very_large_buf], pipeline: 50, reqs: 500})); + +tests.push(new Test({descr: "GET 4MiB str", command: "get", args: ["foo_rand000000000002"], pipeline: 1, reqs: 100})); +tests.push(new Test({descr: "GET 4MiB str", command: "get", args: ["foo_rand000000000002"], pipeline: 50, reqs: 100})); + +tests.push(new Test({descr: "GET 4MiB buf", command: "get", args: ["foo_rand000000000002"], pipeline: 1, reqs: 100, client_opts: { return_buffers: true} })); +tests.push(new Test({descr: "GET 4MiB buf", command: "get", args: ["foo_rand000000000002"], pipeline: 50, reqs: 100, client_opts: { return_buffers: true} })); function next() { var test = tests.shift(); From 010a2acf3369382db8ccb3b3e67da98c4af9a1ce Mon Sep 17 00:00:00 2001 From: Jeremiah Lee Cohick Date: Fri, 10 Jul 2015 16:10:00 -0700 Subject: [PATCH 05/49] Added example of a Redis SCAN command --- examples/scan.js | 34 ++++++++++++++++++++++++++++++++++ 1 file changed, 34 insertions(+) create mode 100644 examples/scan.js diff --git a/examples/scan.js b/examples/scan.js new file mode 100644 index 0000000000..36f6872f00 --- /dev/null +++ b/examples/scan.js @@ -0,0 +1,34 @@ +var redis = require("redis"), + client = redis.createClient(); + +var cursor = 0; + +function scan() { + client.scan( + cursor, + "MATCH", "q:job:*", + "COUNT", "10", + function(err, res) { + if (err) throw err; + + // Update the cursor position for the next scan + cursor = res[0]; + + // From : + // An iteration starts when the cursor is set to 0, + // and terminates when the cursor returned by the server is 0. + if (cursor === 0) { + return console.log('Iteration complete'); + } else { + // Remember, more keys than COUNT or no keys may be returned + // See http://redis.io/commands/scan#the-count-option + if (res[1].length > 0) { + return console.log('Array of matching keys', res[1]); + } else { + // No keys were returned in this scan, but more keys exist. + return scan(); + } + } + } + ); +} From 6f28c6acf5ae626b53fa93441ad8eb89360938ba Mon Sep 17 00:00:00 2001 From: Jeremiah Lee Cohick Date: Fri, 10 Jul 2015 16:25:52 -0700 Subject: [PATCH 06/49] Cursor should be a string --- examples/scan.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/examples/scan.js b/examples/scan.js index 36f6872f00..35238b6e83 100644 --- a/examples/scan.js +++ b/examples/scan.js @@ -1,7 +1,7 @@ var redis = require("redis"), client = redis.createClient(); -var cursor = 0; +var cursor = '0'; function scan() { client.scan( @@ -17,7 +17,7 @@ function scan() { // From : // An iteration starts when the cursor is set to 0, // and terminates when the cursor returned by the server is 0. - if (cursor === 0) { + if (cursor === '0') { return console.log('Iteration complete'); } else { // Remember, more keys than COUNT or no keys may be returned From ff020d37f34ab503e9cfeac7490c6773eb3f93ee Mon Sep 17 00:00:00 2001 From: Jeremiah Lee Cohick Date: Fri, 10 Jul 2015 18:04:38 -0700 Subject: [PATCH 07/49] Clarified COUNT's behavior. scan() now will run until cursor reaches 0. --- examples/scan.js | 16 +++++++++------- 1 file changed, 9 insertions(+), 7 deletions(-) diff --git a/examples/scan.js b/examples/scan.js index 35238b6e83..0302843a76 100644 --- a/examples/scan.js +++ b/examples/scan.js @@ -15,19 +15,21 @@ function scan() { cursor = res[0]; // From : - // An iteration starts when the cursor is set to 0, - // and terminates when the cursor returned by the server is 0. + // "An iteration starts when the cursor is set to 0, + // and terminates when the cursor returned by the server is 0." if (cursor === '0') { return console.log('Iteration complete'); } else { - // Remember, more keys than COUNT or no keys may be returned + // Remember: more or less than COUNT or no keys may be returned // See http://redis.io/commands/scan#the-count-option + // Also, SCAN may return the same key multiple times + // See http://redis.io/commands/scan#scan-guarantees + if (res[1].length > 0) { - return console.log('Array of matching keys', res[1]); - } else { - // No keys were returned in this scan, but more keys exist. - return scan(); + console.log('Array of matching keys', res[1]); } + + return scan(); } } ); From d30e80abbe918e0919e55f42fdddc0334f65b861 Mon Sep 17 00:00:00 2001 From: Benjamin Coe Date: Sat, 11 Jul 2015 16:14:42 -0700 Subject: [PATCH 08/49] making an attempt to improve the test suite --- .npmignore | 2 +- .travis.yml | 7 ++++ lib/queue.js | 2 +- package.json | 12 +++--- test/queue-test.js | 35 +++++++++++++++++ test/run.sh | 4 ++ test-unref.js => test/test-unref.js | 4 +- test.js => test/test.js | 61 +++++++++++++++++------------ 8 files changed, 92 insertions(+), 35 deletions(-) create mode 100644 .travis.yml create mode 100644 test/queue-test.js create mode 100755 test/run.sh rename test-unref.js => test/test-unref.js (90%) rename test.js => test/test.js (97%) diff --git a/.npmignore b/.npmignore index 61755a69fc..4df6949973 100644 --- a/.npmignore +++ b/.npmignore @@ -1,6 +1,6 @@ examples/ benches/ -test.js +test/ diff_multi_bench_output.js generate_commands.js multi_bench.js diff --git a/.travis.yml b/.travis.yml new file mode 100644 index 0000000000..d75f4b5bb8 --- /dev/null +++ b/.travis.yml @@ -0,0 +1,7 @@ +language: node_js +sudo: false +node_js: + - "0.10" + - "0.12" + - "iojs" +after_success: npm run coverage diff --git a/lib/queue.js b/lib/queue.js index 3fc87ab102..0d258cdfcb 100644 --- a/lib/queue.js +++ b/lib/queue.js @@ -46,7 +46,7 @@ Queue.prototype.forEach = function (fn, thisv) { Queue.prototype.getLength = function () { return this.head.length - this.offset + this.tail.length; }; - + Object.defineProperty(Queue.prototype, "length", { get: function () { return this.getLength(); diff --git a/package.json b/package.json index 21f563ffe1..495091bbea 100644 --- a/package.json +++ b/package.json @@ -3,20 +3,22 @@ "version": "0.12.1", "description": "Redis client library", "keywords": [ - "redis", - "database" + "database", + "redis" ], "author": "Matt Ranney ", "license": "MIT", "main": "./index.js", "scripts": { - "test": "node ./test.js", - "coverage": "nyc npm test && nyc report" + "coverage": "nyc report --reporter=text-lcov | coveralls", + "test": "nyc ./test/run.sh" }, "devDependencies": { "colors": "~0.6.0-1", + "coveralls": "^2.11.2", + "hiredis": "^0.4.0", "metrics": ">=0.1.5", - "nyc": "^2.2.0", + "nyc": "^3.0.0", "underscore": "~1.4.4" }, "repository": { diff --git a/test/queue-test.js b/test/queue-test.js new file mode 100644 index 0000000000..017ea60785 --- /dev/null +++ b/test/queue-test.js @@ -0,0 +1,35 @@ +var assert = require("assert"); +var Queue = require('../lib/queue'); + +module.exports = function (tests, next) { + var q = new Queue(); + + tests.push = function () { + q.push('a'); + q.push(3); + assert.equal(q.length, 2); + return next(); + } + + tests.shift = function () { + assert.equal(q.shift(), 'a'); + return next(); + } + + tests.forEach = function () { + q.forEach(function (v) { + assert.equal(v, 3); + }); + + return next(); + } + + tests.forEachWithScope = function () { + q.forEach(function (v) { + assert.equal(this.foo, 'bar'); + assert.equal(v, 3); + }, {foo: 'bar'}); + + return next(); + } +} diff --git a/test/run.sh b/test/run.sh new file mode 100755 index 0000000000..ab030ba48f --- /dev/null +++ b/test/run.sh @@ -0,0 +1,4 @@ +#!/usr/bin/env bash + +node ./test/test.js false hiredis +node ./test/test.js false javascript diff --git a/test-unref.js b/test/test-unref.js similarity index 90% rename from test-unref.js rename to test/test-unref.js index 4a3cc3622d..d66d77c433 100644 --- a/test-unref.js +++ b/test/test-unref.js @@ -1,4 +1,4 @@ -var redis = require("./") +var redis = require("../") //redis.debug_mode = true var PORT = process.argv[2] || 6379; var HOST = process.argv[3] || '127.0.0.1'; @@ -9,4 +9,4 @@ c.info(function (err, reply) { if (err) process.exit(-1) if (!reply.length) process.exit(-1) process.stdout.write(reply.length.toString()) -}) \ No newline at end of file +}) diff --git a/test.js b/test/test.js similarity index 97% rename from test.js rename to test/test.js index 0bba678fe7..3328673840 100644 --- a/test.js +++ b/test/test.js @@ -1,15 +1,16 @@ /*global require console setTimeout process Buffer */ var PORT = 6379; var HOST = '127.0.0.1'; +var parser = process.argv[3]; -var redis = require("./index"), - client = redis.createClient(PORT, HOST), - client2 = redis.createClient(PORT, HOST), - client3 = redis.createClient(PORT, HOST), - bclient = redis.createClient(PORT, HOST, { return_buffers: true }), +var redis = require("../index"), + client = redis.createClient(PORT, HOST, { parser: parser }), + client2 = redis.createClient(PORT, HOST, { parser: parser }), + client3 = redis.createClient(PORT, HOST, { parser: parser }), + bclient = redis.createClient(PORT, HOST, { return_buffers: true, parser: parser }), assert = require("assert"), crypto = require("crypto"), - util = require("./lib/util"), + util = require("../lib/util"), fork = require("child_process").fork, test_db_num = 15, // this DB will be flushed and used for testing tests = {}, @@ -17,9 +18,8 @@ var redis = require("./index"), ended = false, next, cur_start, run_next_test, all_tests, all_start, test_count; - // Set this to truthy to see the wire protocol and other debugging info -redis.debug_mode = process.argv[2]; +redis.debug_mode = process.argv[2] ? JSON.parse(process.argv[2]) : false; function server_version_at_least(connection, desired_version) { // Return true if the server version >= desired_version @@ -116,7 +116,7 @@ next = function next(name) { // Tests are run in the order they are defined, so FLUSHDB should always be first. tests.IPV4 = function () { - var ipv4Client = redis.createClient( PORT, "127.0.0.1", { "family" : "IPv4" } ); + var ipv4Client = redis.createClient( PORT, "127.0.0.1", { family : "IPv4", parser: parser } ); ipv4Client.once("ready", function start_tests() { console.log("Connected to " + ipv4Client.address + ", Redis server version " + ipv4Client.server_info.redis_version + "\n"); @@ -142,7 +142,7 @@ tests.IPV6 = function () { console.log("Skipping IPV6 for old Redis server version < 2.8.0"); return run_next_test(); } - var ipv6Client = redis.createClient( PORT, "::1", { "family" : "IPv6" } ); + var ipv6Client = redis.createClient( PORT, "::1", { family: "IPv6", parser: parser } ); ipv6Client.once("ready", function start_tests() { console.log("Connected to " + ipv6Client.address + ", Redis server version " + ipv6Client.server_info.redis_version + "\n"); @@ -164,7 +164,7 @@ tests.IPV6 = function () { } tests.UNIX_SOCKET = function () { - var unixClient = redis.createClient('/tmp/redis.sock'); + var unixClient = redis.createClient('/tmp/redis.sock', { parser: parser }); // if this fails, check the permission of unix socket. // unixsocket /tmp/redis.sock @@ -374,7 +374,7 @@ tests.MULTI_7 = function () { return next(name); } - var p = require("./lib/parser/javascript"); + var p = require("../lib/parser/javascript"); var parser = new p.Parser(false); var reply_count = 0; function check_reply(reply) { @@ -728,7 +728,7 @@ tests.WATCH_TRANSACTION = function () { tests.detect_buffers = function () { - var name = "detect_buffers", detect_client = redis.createClient({detect_buffers: true}); + var name = "detect_buffers", detect_client = redis.createClient({ detect_buffers: true, parser: parser }); detect_client.on("ready", function () { // single Buffer or String @@ -795,9 +795,9 @@ tests.detect_buffers = function () { tests.socket_nodelay = function () { var name = "socket_nodelay", c1, c2, c3, ready_count = 0, quit_count = 0; - c1 = redis.createClient({socket_nodelay: true}); - c2 = redis.createClient({socket_nodelay: false}); - c3 = redis.createClient(); + c1 = redis.createClient({ socket_nodelay: true, parser: parser }); + c2 = redis.createClient({ socket_nodelay: false, parser: parser }); + c3 = redis.createClient({ parser: parser }); function quit_check() { quit_count++; @@ -1158,8 +1158,8 @@ tests.SUBSCRIBE_QUIT = function () { tests.SUBSCRIBE_CLOSE_RESUBSCRIBE = function () { var name = "SUBSCRIBE_CLOSE_RESUBSCRIBE"; - var c1 = redis.createClient(); - var c2 = redis.createClient(); + var c1 = redis.createClient({ parser: parser }); + var c2 = redis.createClient({ parser: parser }); var count = 0; /* Create two clients. c1 subscribes to two channels, c2 will publish to them. @@ -1955,7 +1955,7 @@ tests.MONITOR = function () { return next(name); } - monitor_client = redis.createClient(); + monitor_client = redis.createClient({ parser: parser }); monitor_client.monitor(function (err, res) { client.mget("some", "keys", "foo", "bar"); client.set("json", JSON.stringify({ @@ -2056,7 +2056,8 @@ tests.OPTIONAL_CALLBACK_UNDEFINED = function () { tests.ENABLE_OFFLINE_QUEUE_TRUE = function () { var name = "ENABLE_OFFLINE_QUEUE_TRUE"; var cli = redis.createClient(9999, null, { - max_attempts: 1 + max_attempts: 1, + parser: parser // default :) // enable_offline_queue: true }); @@ -2078,6 +2079,7 @@ tests.ENABLE_OFFLINE_QUEUE_TRUE = function () { tests.ENABLE_OFFLINE_QUEUE_FALSE = function () { var name = "ENABLE_OFFLINE_QUEUE_FALSE"; var cli = redis.createClient(9999, null, { + parser: parser, max_attempts: 1, enable_offline_queue: false }); @@ -2134,7 +2136,10 @@ tests.DOMAIN = function () { }); // this is the expected and desired behavior - domain.on('error', function (err) { next(name); }); + domain.on('error', function (err) { + domain.exit(); + next(name); + }); } }; @@ -2143,7 +2148,7 @@ tests.DOMAIN = function () { tests.auth = function () { var name = "AUTH", client4, ready_count = 0; - client4 = redis.createClient(9006, "filefish.redistogo.com"); + client4 = redis.createClient(9006, "filefish.redistogo.com", { parser: parser }); client4.auth("664b1b6aaf134e1ec281945a8de702a9", function (err, res) { assert.strictEqual(null, err, name); assert.strictEqual("OK", res.toString(), name); @@ -2165,7 +2170,7 @@ tests.auth = function () { tests.auth2 = function () { var name = "AUTH2", client4, ready_count = 0; - client4 = redis.createClient(9006, "filefish.redistogo.com", {auth_pass: "664b1b6aaf134e1ec281945a8de702a9"}); + client4 = redis.createClient(9006, "filefish.redistogo.com", { auth_pass: "664b1b6aaf134e1ec281945a8de702a9", parser: parser }); // test auth, then kill the connection so it'll auto-reconnect and auto-re-auth client4.on("ready", function () { @@ -2204,7 +2209,8 @@ tests.reconnectRetryMaxDelay = function() { name = 'reconnectRetryMaxDelay', reconnecting = false; var client = redis.createClient(PORT, HOST, { - retry_max_delay: 1 + retry_max_delay: 1, + parser: parser }); client.on('ready', function() { if (!reconnecting) { @@ -2223,7 +2229,7 @@ tests.reconnectRetryMaxDelay = function() { tests.unref = function () { var name = "unref"; - var external = fork("./test-unref.js"); + var external = fork("./test/test-unref.js"); var done = false; external.on("close", function (code) { assert(code == 0, "test-unref.js failed"); @@ -2235,9 +2241,12 @@ tests.unref = function () { } assert(done, "test-unref.js didn't finish in time."); next(name); - }, 500); + }, 1500); }; +// starting to split tests into multiple files. +require('./queue-test')(tests, next) + all_tests = Object.keys(tests); all_start = new Date(); test_count = 0; From 116a27a120932dede365466f8a867dd30eb6057a Mon Sep 17 00:00:00 2001 From: Benjamin Coe Date: Sat, 11 Jul 2015 18:48:37 -0700 Subject: [PATCH 09/49] update changelog, force travis build --- changelog.md | 1 + 1 file changed, 1 insertion(+) diff --git a/changelog.md b/changelog.md index 817031da7a..89c1692a79 100644 --- a/changelog.md +++ b/changelog.md @@ -1,6 +1,7 @@ Changelog ========= +* Refactor tests, and improve test coverage (Ben Coe) * Fix extraneous error output due to pubsub tests (Mikael Kohlmyr) ## v0.12.1 - Aug 10, 2014 From f4e1e2ce6dee4a6e0b535f37f13fdaf106784142 Mon Sep 17 00:00:00 2001 From: Benjamin Coe Date: Sat, 11 Jul 2015 19:07:43 -0700 Subject: [PATCH 10/49] getting redis configured --- .travis.yml | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/.travis.yml b/.travis.yml index d75f4b5bb8..9797bad1b4 100644 --- a/.travis.yml +++ b/.travis.yml @@ -4,4 +4,8 @@ node_js: - "0.10" - "0.12" - "iojs" +before_install: + - 'printf ''bind ::1 127.0.0.1\nunixsocket /tmp/redis.sock'' >> /tmp/redis.conf' +before_script: + - sudo redis-server /tmp/redis.conf after_success: npm run coverage From e17228a3652bfc77db073ff9fb12d54386832ac6 Mon Sep 17 00:00:00 2001 From: Benjamin Coe Date: Sat, 11 Jul 2015 19:09:19 -0700 Subject: [PATCH 11/49] enable sudo --- .travis.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.travis.yml b/.travis.yml index 9797bad1b4..2df8d3f399 100644 --- a/.travis.yml +++ b/.travis.yml @@ -1,5 +1,5 @@ language: node_js -sudo: false +sudo: true node_js: - "0.10" - "0.12" From 7657fdbfb62df9e31048fcc92c89a2bd8228c46e Mon Sep 17 00:00:00 2001 From: Benjamin Coe Date: Sat, 11 Jul 2015 19:14:21 -0700 Subject: [PATCH 12/49] daemonize sure --- .travis.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.travis.yml b/.travis.yml index 2df8d3f399..657281993e 100644 --- a/.travis.yml +++ b/.travis.yml @@ -5,7 +5,7 @@ node_js: - "0.12" - "iojs" before_install: - - 'printf ''bind ::1 127.0.0.1\nunixsocket /tmp/redis.sock'' >> /tmp/redis.conf' + - 'printf ''bind ::1 127.0.0.1\nunixsocket /tmp/redis.sock\ndaemonize yes'' >> /tmp/redis.conf' before_script: - sudo redis-server /tmp/redis.conf after_success: npm run coverage From 8ca45e3f1faf4a1fb40306e52e8337bb363ddfd8 Mon Sep 17 00:00:00 2001 From: Benjamin Coe Date: Sat, 11 Jul 2015 19:24:44 -0700 Subject: [PATCH 13/49] loose socket perms --- .travis.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.travis.yml b/.travis.yml index 657281993e..e7ed30fd79 100644 --- a/.travis.yml +++ b/.travis.yml @@ -5,7 +5,7 @@ node_js: - "0.12" - "iojs" before_install: - - 'printf ''bind ::1 127.0.0.1\nunixsocket /tmp/redis.sock\ndaemonize yes'' >> /tmp/redis.conf' + - 'printf ''bind ::1 127.0.0.1\nunixsocket /tmp/redis.sock\ndaemonize yes\nunixsocketperm 777'' >> /tmp/redis.conf' before_script: - sudo redis-server /tmp/redis.conf after_success: npm run coverage From 96da40719d8aa01d94321cf69d4dc2e64e2caef8 Mon Sep 17 00:00:00 2001 From: Benjamin Coe Date: Sat, 11 Jul 2015 19:31:19 -0700 Subject: [PATCH 14/49] badges \o/ --- README.md | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/README.md b/README.md index c3ffd1c898..bbd06405e1 100644 --- a/README.md +++ b/README.md @@ -1,6 +1,9 @@ redis - a node.js redis client =========================== +[![Build Status](https://travis-ci.org/NodeRedis/node_redis.png)](https://travis-ci.org/NodeRedis/node_redis) +[![Coverage Status](https://coveralls.io/reposNodeRedis/node_redisbadge.svg?branch=)](https://coveralls.io/r/NodeRedis/node_redis?branch=) + This is a complete Redis client for node.js. It supports all Redis commands, including many recently added commands like EVAL from experimental Redis server branches. @@ -162,7 +165,7 @@ every command on a client. * `socket_nodelay`: defaults to `true`. Whether to call setNoDelay() on the TCP stream, which disables the Nagle algorithm on the underlying socket. Setting this option to `false` can result in additional throughput at the cost of more latency. Most applications will want this set to `true`. -* `socket_keepalive` defaults to `true`. Whether the keep-alive functionality is enabled on the underlying socket. +* `socket_keepalive` defaults to `true`. Whether the keep-alive functionality is enabled on the underlying socket. * `no_ready_check`: defaults to `false`. When a connection is established to the Redis server, the server might still be loading the database from disk. While loading, the server not respond to any commands. To work around this, `node_redis` has a "ready check" which sends the `INFO` command to the server. The response from the `INFO` command @@ -181,8 +184,8 @@ limits total time for client to reconnect. Value is provided in milliseconds and * `max_attempts` defaults to `null`. By default client will try reconnecting until connected. Setting `max_attempts` limits total amount of reconnects. * `auth_pass` defaults to `null`. By default client will try connecting without auth. If set, client will run redis auth command on connect. -* `family` defaults to `IPv4`. The client connects in IPv4 if not specified or if the DNS resolution returns an IPv4 address. -You can force an IPv6 if you set the family to 'IPv6'. See nodejs net or dns modules how to use the family type. +* `family` defaults to `IPv4`. The client connects in IPv4 if not specified or if the DNS resolution returns an IPv4 address. +You can force an IPv6 if you set the family to 'IPv6'. See nodejs net or dns modules how to use the family type. ```js var redis = require("redis"), From 06d051ea1d71921c74f41972e1feff80922ddfd6 Mon Sep 17 00:00:00 2001 From: Benjamin Coe Date: Sat, 11 Jul 2015 19:45:05 -0700 Subject: [PATCH 15/49] had a bad URL to test coverage --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index bbd06405e1..a7dbb80e28 100644 --- a/README.md +++ b/README.md @@ -2,7 +2,7 @@ redis - a node.js redis client =========================== [![Build Status](https://travis-ci.org/NodeRedis/node_redis.png)](https://travis-ci.org/NodeRedis/node_redis) -[![Coverage Status](https://coveralls.io/reposNodeRedis/node_redisbadge.svg?branch=)](https://coveralls.io/r/NodeRedis/node_redis?branch=) +[![Coverage Status](https://coveralls.io/repos/NodeRedis/node_redis/badge.svg?branch=)](https://coveralls.io/r/NodeRedis/node_redis?branch=) This is a complete Redis client for node.js. It supports all Redis commands, including many recently added commands like EVAL from experimental Redis server From 6f1acb0678f7288973e05456bd8a4facf889635c Mon Sep 17 00:00:00 2001 From: Raymond Myers Date: Tue, 31 Mar 2015 23:09:57 -0700 Subject: [PATCH 16/49] Fixed bug where buffer hgetall's in a multi would throw exceptions --- index.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/index.js b/index.js index f2d5ed43cc..abee2973b7 100644 --- a/index.js +++ b/index.js @@ -610,7 +610,7 @@ function try_callback(callback, reply) { function reply_to_object(reply) { var obj = {}, j, jl, key, val; - if (reply.length === 0) { + if (reply.length === 0 || !Array.isArray(reply)) { return null; } From f384d1c774d39090b9634502b230d7c29819135b Mon Sep 17 00:00:00 2001 From: Raymond Myers Date: Wed, 1 Apr 2015 00:10:23 -0700 Subject: [PATCH 17/49] Fixed the pub/sub logic accidentally stringing the first value of a multi/exec response --- index.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/index.js b/index.js index abee2973b7..399ebf2259 100644 --- a/index.js +++ b/index.js @@ -648,7 +648,7 @@ RedisClient.prototype.return_reply = function (reply) { // If the "reply" here is actually a message received asynchronously due to a // pubsub subscription, don't pop the command queue as we'll only be consuming // the head command prematurely. - if (Array.isArray(reply) && reply.length > 0 && reply[0]) { + if (this.pub_sub_mode && Array.isArray(reply) && reply.length > 0 && reply[0]) { type = reply[0].toString(); } From aea03d60beb499e547ca34a6cbdfad634f36c63c Mon Sep 17 00:00:00 2001 From: Raymond Myers Date: Wed, 1 Apr 2015 00:38:21 -0700 Subject: [PATCH 18/49] Fixed exec result arrays being stringed in detect_buffers mode --- index.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/index.js b/index.js index 399ebf2259..5b4c6d56b5 100644 --- a/index.js +++ b/index.js @@ -672,7 +672,7 @@ RedisClient.prototype.return_reply = function (reply) { if (command_obj && !command_obj.sub_command) { if (typeof command_obj.callback === "function") { - if (this.options.detect_buffers && command_obj.buffer_args === false) { + if (this.options.detect_buffers && command_obj.buffer_args === false && 'exec' !== command_obj.command.toLowerCase()) { // If detect_buffers option was specified, then the reply from the parser will be Buffers. // If this command did not use Buffer arguments, then convert the reply to Strings here. reply = reply_to_strings(reply); From ded75c8ea2cbf1194c3426a2534cffe7167b45dc Mon Sep 17 00:00:00 2001 From: Raymond Myers Date: Wed, 1 Apr 2015 01:03:10 -0700 Subject: [PATCH 19/49] Fixed detect_buffers keeping all multi/exec results as buffers --- index.js | 19 +++++++++++++++++-- 1 file changed, 17 insertions(+), 2 deletions(-) diff --git a/index.js b/index.js index 5b4c6d56b5..70479aeb18 100644 --- a/index.js +++ b/index.js @@ -1106,15 +1106,24 @@ Multi.prototype.HMSET = Multi.prototype.hmset; Multi.prototype.exec = function (callback) { var self = this; var errors = []; + var wants_buffers = []; // drain queue, callback will catch "QUEUED" or error // TODO - get rid of all of these anonymous functions which are elegant but slow this.queue.forEach(function (args, index) { - var command = args[0], obj; + var command = args[0], obj, i, il, buffer_args; if (typeof args[args.length - 1] === "function") { args = args.slice(1, -1); } else { args = args.slice(1); } + // Keep track of who wants buffer responses: + buffer_args = false; + for (i = 0, il = args.length; i < il; i += 1) { + if (Buffer.isBuffer(args[i])) { + buffer_args = true; + } + } + wants_buffers.push(buffer_args); if (args.length === 1 && Array.isArray(args[0])) { args = args[0]; } @@ -1149,12 +1158,18 @@ Multi.prototype.exec = function (callback) { } } - var i, il, reply, args; + var i, il, reply, to_buffer, args; if (replies) { for (i = 1, il = self.queue.length; i < il; i += 1) { reply = replies[i - 1]; args = self.queue[i]; + to_buffer = wants_buffers[i]; + + // If we asked for strings, even in detect_buffers mode, then return strings: + if (self._client.options.detect_buffers && to_buffer === false) { + replies[i - 1] = reply = reply_to_strings(reply); + } // TODO - confusing and error-prone that hgetall is special cased in two places if (reply && args[0].toLowerCase() === "hgetall") { From 19c55712293b2bea82e64a22a7ea63c530c81d52 Mon Sep 17 00:00:00 2001 From: Raymond Myers Date: Wed, 1 Apr 2015 01:17:13 -0700 Subject: [PATCH 20/49] Added a test case for detect_buffer behavior in a multi/exec --- test/test.js | 89 ++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 89 insertions(+) diff --git a/test/test.js b/test/test.js index 3328673840..e7db763914 100644 --- a/test/test.js +++ b/test/test.js @@ -792,6 +792,95 @@ tests.detect_buffers = function () { }); }; +tests.detect_buffers_multi = function () { + var name = "detect_buffers_multi", detect_client = redis.createClient({detect_buffers: true}); + + detect_client.on("ready", function () { + // single Buffer or String + detect_client.set("string key 1", "string value"); + detect_client.multi().get("string key 1").exec(require_string("string value", name)); + detect_client.multi().get(new Buffer("string key 1")).exec(function (err, reply) { + assert.strictEqual(null, err, name); + assert.strictEqual(1, reply.length, name); + assert.strictEqual(true, Buffer.isBuffer(reply[0]), name); + assert.strictEqual("", reply[0].inspect(), name); + }); + + detect_client.hmset("hash key 2", "key 1", "val 1", "key 2", "val 2"); + // array of Buffers or Strings + detect_client.multi().hmget("hash key 2", "key 1", "key 2").exec(function (err, reply) { + assert.strictEqual(null, err, name); + assert.strictEqual(true, Array.isArray(reply), name); + assert.strictEqual(1, reply.length, name); + assert.strictEqual(2, reply[0].length, name); + assert.strictEqual("val 1", reply[0][0], name); + assert.strictEqual("val 2", reply[0][1], name); + }); + detect_client.multi().hmget(new Buffer("hash key 2"), "key 1", "key 2").exec(function (err, reply) { + assert.strictEqual(null, err, name); + assert.strictEqual(true, Array.isArray(reply)); + assert.strictEqual(1, reply.length, name); + assert.strictEqual(2, reply[0].length, name); + assert.strictEqual(true, Buffer.isBuffer(reply[0][0])); + assert.strictEqual(true, Buffer.isBuffer(reply[0][1])); + assert.strictEqual("", reply[0][0].inspect(), name); + assert.strictEqual("", reply[0][1].inspect(), name); + }); + + // array of strings with undefined values (repro #344) + detect_client.multi().hmget("hash key 2", "key 3", "key 4").exec(function(err, reply) { + assert.strictEqual(null, err, name); + assert.strictEqual(true, Array.isArray(reply), name); + assert.strictEqual(1, reply.length, name); + assert.strictEqual(2, reply[0].length, name); + assert.equal(null, reply[0][0], name); + assert.equal(null, reply[0][1], name); + }); + + // Object of Buffers or Strings + detect_client.multi().hgetall("hash key 2").exec(function (err, reply) { + assert.strictEqual(null, err, name); + assert.strictEqual(1, reply.length, name); + assert.strictEqual("object", typeof reply[0], name); + assert.strictEqual(2, Object.keys(reply[0]).length, name); + assert.strictEqual("val 1", reply[0]["key 1"], name); + assert.strictEqual("val 2", reply[0]["key 2"], name); + }); + detect_client.multi().hgetall(new Buffer("hash key 2")).exec(function (err, reply) { + assert.strictEqual(null, err, name); + assert.strictEqual(1, reply.length, name); + assert.strictEqual("object", typeof reply, name); + assert.strictEqual(2, Object.keys(reply[0]).length, name); + assert.strictEqual(true, Buffer.isBuffer(reply[0]["key 1"])); + assert.strictEqual(true, Buffer.isBuffer(reply[0]["key 2"])); + assert.strictEqual("", reply[0]["key 1"].inspect(), name); + assert.strictEqual("", reply[0]["key 2"].inspect(), name); + }); + + // Can interleave string and buffer results: + detect_client.multi() + .hget("hash key 2", "key 1") + .hget(new Buffer("hash key 2"), "key 1") + .hget("hash key 2", new Buffer("key 2")) + .hget("hash key 2", "key 2") + .exec(function (err, reply) { + assert.strictEqual(null, err, name); + assert.strictEqual(true, Array.isArray(reply)); + assert.strictEqual(4, reply.length, name); + assert.strictEqual("val 1", reply[0], name); + assert.strictEqual(true, Buffer.isBuffer(reply[1])); + assert.strictEqual("", reply[1].inspect(), name); + assert.strictEqual(true, Buffer.isBuffer(reply[2])); + assert.strictEqual("", reply[2].inspect(), name); + assert.strictEqual("val 2", reply[3], name); + }); + + detect_client.quit(function (err, res) { + next(name); + }); + }); +}; + tests.socket_nodelay = function () { var name = "socket_nodelay", c1, c2, c3, ready_count = 0, quit_count = 0; From dc461f08d4571fb076f8b577270539236ddafffc Mon Sep 17 00:00:00 2001 From: Kyle Date: Thu, 16 Jul 2015 11:01:29 -0500 Subject: [PATCH 21/49] clarifies createClient in README as per #781 --- README.md | 15 ++++++++------- 1 file changed, 8 insertions(+), 7 deletions(-) diff --git a/README.md b/README.md index a7dbb80e28..9a97aaf65d 100644 --- a/README.md +++ b/README.md @@ -144,15 +144,17 @@ resume sending when you get `drain`. `client` will emit `idle` when there are no outstanding commands that are awaiting a response. ## redis.createClient() +If you have `redis-server` running on the same computer as node, then the defaults for +port and host are probably fine and you don't need to supply any arguments. `createClient()` returns a `RedisClient`. ### overloading -* redis.createClient() = redis.createClient(6379, '127.0.0.1', {}) -* redis.createClient(options) = redis.createClient(6379, '127.0.0.1', options) -* redis.createClient(unix_socket, options) -* redis.createClient(port, host, options) +* `redis.createClient(port,host,options)` +* `redis.createClient()` is equivalent to `redis.createClient(6379, '127.0.0.1', {})` +* `redis.createClient(options)` is equivalent to `redis.createClient(6379, '127.0.0.1', options)` +* `redis.createClient(unix_socket, options)` +* `redis.createClient(port, host, options)` -If you have `redis-server` running on the same computer as node, then the defaults for -port and host are probably fine. `options` in an object with the following possible properties: + `options` in an object with the following possible properties: * `parser`: which Redis protocol reply parser to use. Defaults to `hiredis` if that module is installed. This may also be set to `javascript`. @@ -205,7 +207,6 @@ You can force an IPv6 if you set the family to 'IPv6'. See nodejs net or dns mod client.end(); ``` -`createClient()` returns a `RedisClient` object that is named `client` in all of the examples here. ## client.auth(password, callback) From bc48c2b069552d9de9f8d02e1a353f81ba23a42f Mon Sep 17 00:00:00 2001 From: Kyle Date: Thu, 16 Jul 2015 11:02:25 -0500 Subject: [PATCH 22/49] fix typo --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 9a97aaf65d..836bf9d1fa 100644 --- a/README.md +++ b/README.md @@ -145,7 +145,7 @@ resume sending when you get `drain`. ## redis.createClient() If you have `redis-server` running on the same computer as node, then the defaults for -port and host are probably fine and you don't need to supply any arguments. `createClient()` returns a `RedisClient`. +port and host are probably fine and you don't need to supply any arguments. `createClient()` returns a `RedisClient` object. ### overloading * `redis.createClient(port,host,options)` From defec9c1e1a97dbe0531c17b22fd00afd3e27021 Mon Sep 17 00:00:00 2001 From: Kyle Date: Thu, 16 Jul 2015 11:30:32 -0500 Subject: [PATCH 23/49] fixed duplicate and typo --- README.md | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/README.md b/README.md index 836bf9d1fa..c9900bbbef 100644 --- a/README.md +++ b/README.md @@ -152,9 +152,8 @@ port and host are probably fine and you don't need to supply any arguments. `cre * `redis.createClient()` is equivalent to `redis.createClient(6379, '127.0.0.1', {})` * `redis.createClient(options)` is equivalent to `redis.createClient(6379, '127.0.0.1', options)` * `redis.createClient(unix_socket, options)` -* `redis.createClient(port, host, options)` - `options` in an object with the following possible properties: + `options` is an object with the following possible properties: * `parser`: which Redis protocol reply parser to use. Defaults to `hiredis` if that module is installed. This may also be set to `javascript`. From 208b7874bf62344da9a0b39cb85189058830e718 Mon Sep 17 00:00:00 2001 From: Ruben Bridgewater Date: Wed, 22 Jul 2015 17:11:38 +0200 Subject: [PATCH 24/49] Rewrite createClient. Fixes #651 --- index.js | 49 ++++++++++++++----------------------------------- 1 file changed, 14 insertions(+), 35 deletions(-) diff --git a/index.js b/index.js index 70479aeb18..770987beb1 100644 --- a/index.js +++ b/index.js @@ -1228,50 +1228,29 @@ RedisClient.prototype.eval = RedisClient.prototype.EVAL = function () { }); }; +exports.createClient = function(arg0, arg1, options) { + if (typeof arg0 === 'object' || arg0 === undefined) { + options = arg0 || options; + return createClient_tcp(default_port, default_host, options); + } + if (typeof arg0 === 'number' || typeof arg0 === 'string' && arg0.match(/^\d+$/)){ + return createClient_tcp(arg0, arg1, options); + } + if (typeof arg0 === 'string') { + options = arg1 || {}; -exports.createClient = function(arg0, arg1, arg2){ - if( arguments.length === 0 ){ - - // createClient() - return createClient_tcp(default_port, default_host, {}); - - } else if( typeof arg0 === 'number' || - typeof arg0 === 'string' && arg0.match(/^\d+$/) ){ - - // createClient( 3000, host, options) - // createClient('3000', host, options) - return createClient_tcp(arg0, arg1, arg2); - - } else if( typeof arg0 === 'string' ){ - var parsed = URL.parse(arg0, true, true), - options = (arg1 || {}); - + var parsed = URL.parse(arg0, true, true); if (parsed.hostname) { if (parsed.auth) { options.auth_pass = parsed.auth.split(':')[1]; } - // createClient(3000, host, options) return createClient_tcp((parsed.port || default_port), parsed.hostname, options); - } else { - // createClient( '/tmp/redis.sock', options) - return createClient_unix(arg0,options); } - } else if( arg0 !== null && typeof arg0 === 'object' ){ - - // createClient(options) - return createClient_tcp(default_port, default_host, arg0 ); - - } else if( arg0 === null && arg1 === null ){ - - // for backward compatibility - // createClient(null,null,options) - return createClient_tcp(default_port, default_host, arg2); - - } else { - throw new Error('unknown type of connection in createClient()'); + return createClient_unix(arg0, options); } -} + throw new Error('unknown type of connection in createClient()'); +}; var createClient_unix = function(path, options){ var cnxOptions = { From 1f9e536ca0cae200b621145a096ef15e3161c4c4 Mon Sep 17 00:00:00 2001 From: Ruben Bridgewater Date: Wed, 22 Jul 2015 16:17:02 +0200 Subject: [PATCH 25/49] Add use strict statements This is going to improve the performance minimal and improves the safety of the code --- benches/buffer_bench.js | 2 ++ benches/hiredis_parser.js | 2 ++ benches/re_sub_test.js | 2 ++ benches/reconnect_test.js | 2 ++ benches/stress/codec.js | 2 ++ benches/stress/speed/speed.js | 2 ++ benches/sub_quit_test.js | 2 ++ connection_breaker.js | 2 ++ diff_multi_bench_output.js | 2 ++ examples/auth.js | 2 ++ examples/backpressure_drain.js | 2 ++ examples/eval.js | 2 ++ examples/extend.js | 2 ++ examples/file.js | 2 ++ examples/mget.js | 2 ++ examples/monitor.js | 2 ++ examples/multi.js | 2 ++ examples/multi2.js | 2 ++ examples/psubscribe.js | 2 ++ examples/pub_sub.js | 2 ++ examples/scan.js | 2 ++ examples/simple.js | 2 ++ examples/sort.js | 2 ++ examples/subqueries.js | 2 ++ examples/subquery.js | 2 ++ examples/web_server.js | 6 ++++-- generate_commands.js | 2 ++ index.js | 2 ++ lib/commands.js | 2 ++ lib/parser/hiredis.js | 2 ++ lib/parser/javascript.js | 2 ++ lib/queue.js | 2 ++ lib/to_array.js | 2 ++ lib/util.js | 2 ++ multi_bench.js | 2 ++ test/queue-test.js | 2 ++ test/test.js | 2 ++ 37 files changed, 76 insertions(+), 2 deletions(-) diff --git a/benches/buffer_bench.js b/benches/buffer_bench.js index a504fbc087..df16d90bf4 100644 --- a/benches/buffer_bench.js +++ b/benches/buffer_bench.js @@ -1,3 +1,5 @@ +'use strict'; + var source = new Buffer(100), dest = new Buffer(100), i, j, k, tmp, count = 1000000, bytes = 100; diff --git a/benches/hiredis_parser.js b/benches/hiredis_parser.js index f1515b110b..489a4912e2 100644 --- a/benches/hiredis_parser.js +++ b/benches/hiredis_parser.js @@ -1,3 +1,5 @@ +'use strict'; + var Parser = require('../lib/parser/hiredis').Parser; var assert = require('assert'); diff --git a/benches/re_sub_test.js b/benches/re_sub_test.js index 64b8f31287..256d0d0d1d 100644 --- a/benches/re_sub_test.js +++ b/benches/re_sub_test.js @@ -1,3 +1,5 @@ +'use strict'; + var client = require('../index').createClient() , client2 = require('../index').createClient() , assert = require('assert'); diff --git a/benches/reconnect_test.js b/benches/reconnect_test.js index 7abdd51665..7556ea430a 100644 --- a/benches/reconnect_test.js +++ b/benches/reconnect_test.js @@ -1,3 +1,5 @@ +'use strict'; + var redis = require("../index").createClient(null, null, { // max_attempts: 4 }); diff --git a/benches/stress/codec.js b/benches/stress/codec.js index 7d764f6072..c679a6ee01 100644 --- a/benches/stress/codec.js +++ b/benches/stress/codec.js @@ -1,3 +1,5 @@ +'use strict'; + var json = { encode: JSON.stringify, decode: JSON.parse diff --git a/benches/stress/speed/speed.js b/benches/stress/speed/speed.js index 8e43cbc03b..93d54f0c13 100644 --- a/benches/stress/speed/speed.js +++ b/benches/stress/speed/speed.js @@ -1,3 +1,5 @@ +'use strict'; + var msgpack = require('node-msgpack'); var bison = require('bison'); var codec = { diff --git a/benches/sub_quit_test.js b/benches/sub_quit_test.js index ad1f413228..ae3725a999 100644 --- a/benches/sub_quit_test.js +++ b/benches/sub_quit_test.js @@ -1,3 +1,5 @@ +'use strict'; + var client = require("redis").createClient(), client2 = require("redis").createClient(); diff --git a/connection_breaker.js b/connection_breaker.js index 489f5d5645..f2db0eb1b6 100644 --- a/connection_breaker.js +++ b/connection_breaker.js @@ -1,3 +1,5 @@ +'use strict'; + var net = require('net'); var proxyPort = 6379; diff --git a/diff_multi_bench_output.js b/diff_multi_bench_output.js index 7396472019..23cb3ada21 100755 --- a/diff_multi_bench_output.js +++ b/diff_multi_bench_output.js @@ -1,5 +1,7 @@ #!/usr/bin/env node +'use strict'; + var colors = require('colors'), fs = require('fs'), _ = require('underscore'), diff --git a/examples/auth.js b/examples/auth.js index 6c0a563cd8..275c40961e 100644 --- a/examples/auth.js +++ b/examples/auth.js @@ -1,3 +1,5 @@ +'use strict'; + var redis = require("redis"), client = redis.createClient(); diff --git a/examples/backpressure_drain.js b/examples/backpressure_drain.js index 3488ef4d3f..74107bb6a0 100644 --- a/examples/backpressure_drain.js +++ b/examples/backpressure_drain.js @@ -1,3 +1,5 @@ +'use strict'; + var redis = require("../index"), client = redis.createClient(null, null, { command_queue_high_water: 5, diff --git a/examples/eval.js b/examples/eval.js index a3ff6b0793..0ec1c215e9 100644 --- a/examples/eval.js +++ b/examples/eval.js @@ -1,3 +1,5 @@ +'use strict'; + var redis = require("../index"), client = redis.createClient(); diff --git a/examples/extend.js b/examples/extend.js index 488b8c2dc5..b3a10fa716 100644 --- a/examples/extend.js +++ b/examples/extend.js @@ -1,3 +1,5 @@ +'use strict'; + var redis = require("redis"), client = redis.createClient(); diff --git a/examples/file.js b/examples/file.js index 4d2b5d1c98..9197019858 100644 --- a/examples/file.js +++ b/examples/file.js @@ -1,3 +1,5 @@ +'use strict'; + // Read a file from disk, store it in Redis, then read it back from Redis. var redis = require("redis"), diff --git a/examples/mget.js b/examples/mget.js index 936740d32f..30f2bce90a 100644 --- a/examples/mget.js +++ b/examples/mget.js @@ -1,3 +1,5 @@ +'use strict'; + var client = require("redis").createClient(); client.mget(["sessions started", "sessions started", "foo"], function (err, res) { diff --git a/examples/monitor.js b/examples/monitor.js index 2cb6a4e1ec..c67f596294 100644 --- a/examples/monitor.js +++ b/examples/monitor.js @@ -1,3 +1,5 @@ +'use strict'; + var client = require("../index").createClient(), util = require("util"); diff --git a/examples/multi.js b/examples/multi.js index 35c08e1840..31bc14ffb0 100644 --- a/examples/multi.js +++ b/examples/multi.js @@ -1,3 +1,5 @@ +'use strict'; + var redis = require("redis"), client = redis.createClient(), set_size = 20; diff --git a/examples/multi2.js b/examples/multi2.js index 8be4d7313c..da722b23bb 100644 --- a/examples/multi2.js +++ b/examples/multi2.js @@ -1,3 +1,5 @@ +'use strict'; + var redis = require("redis"), client = redis.createClient(), multi; diff --git a/examples/psubscribe.js b/examples/psubscribe.js index c57117b8a6..f46d235bed 100644 --- a/examples/psubscribe.js +++ b/examples/psubscribe.js @@ -1,3 +1,5 @@ +'use strict'; + var redis = require("redis"), client1 = redis.createClient(), client2 = redis.createClient(), diff --git a/examples/pub_sub.js b/examples/pub_sub.js index aa508d6c9d..3446a552d1 100644 --- a/examples/pub_sub.js +++ b/examples/pub_sub.js @@ -1,3 +1,5 @@ +'use strict'; + var redis = require("redis"), client1 = redis.createClient(), msg_count = 0, client2 = redis.createClient(); diff --git a/examples/scan.js b/examples/scan.js index 0302843a76..f4aac2ef4d 100644 --- a/examples/scan.js +++ b/examples/scan.js @@ -1,3 +1,5 @@ +'use strict'; + var redis = require("redis"), client = redis.createClient(); diff --git a/examples/simple.js b/examples/simple.js index f1f2e3209b..e832749895 100644 --- a/examples/simple.js +++ b/examples/simple.js @@ -1,3 +1,5 @@ +'use strict'; + var redis = require("redis"), client = redis.createClient(); diff --git a/examples/sort.js b/examples/sort.js index e7c6249e40..4c804bee1d 100644 --- a/examples/sort.js +++ b/examples/sort.js @@ -1,3 +1,5 @@ +'use strict'; + var redis = require("redis"), client = redis.createClient(); diff --git a/examples/subqueries.js b/examples/subqueries.js index 560db2404e..ebae6d4855 100644 --- a/examples/subqueries.js +++ b/examples/subqueries.js @@ -1,3 +1,5 @@ +'use strict'; + // Sending commands in response to other commands. // This example runs "type" against every key in the database // diff --git a/examples/subquery.js b/examples/subquery.js index 861657e1f3..4f1edb0ffd 100644 --- a/examples/subquery.js +++ b/examples/subquery.js @@ -1,3 +1,5 @@ +'use strict'; + var client = require("redis").createClient(); function print_results(obj) { diff --git a/examples/web_server.js b/examples/web_server.js index 9fd85923de..dfd420bd1a 100644 --- a/examples/web_server.js +++ b/examples/web_server.js @@ -1,3 +1,5 @@ +'use strict'; + // A simple web server that generates dyanmic content based on responses from Redis var http = require("http"), server, @@ -7,9 +9,9 @@ server = http.createServer(function (request, response) { response.writeHead(200, { "Content-Type": "text/plain" }); - + var redis_info, total_requests; - + redis_client.info(function (err, reply) { redis_info = reply; // stash response in outer scope }); diff --git a/generate_commands.js b/generate_commands.js index e6949d3a14..3ad420edc3 100644 --- a/generate_commands.js +++ b/generate_commands.js @@ -1,3 +1,5 @@ +'use strict'; + var http = require("http"), fs = require("fs"); diff --git a/index.js b/index.js index 770987beb1..6c70a25129 100644 --- a/index.js +++ b/index.js @@ -1,3 +1,5 @@ +'use strict'; + /*global Buffer require exports console setTimeout */ var net = require("net"), diff --git a/lib/commands.js b/lib/commands.js index b0365350cd..3b01543d99 100644 --- a/lib/commands.js +++ b/lib/commands.js @@ -1,3 +1,5 @@ +'use strict'; + // This file was generated by ./generate_commands.js on Wed Apr 23 2014 14:51:21 GMT-0700 (PDT) module.exports = [ "append", diff --git a/lib/parser/hiredis.js b/lib/parser/hiredis.js index 940bfeeb76..29dbfd59a9 100644 --- a/lib/parser/hiredis.js +++ b/lib/parser/hiredis.js @@ -1,3 +1,5 @@ +'use strict'; + var events = require("events"), util = require("../util"), hiredis = require("hiredis"); diff --git a/lib/parser/javascript.js b/lib/parser/javascript.js index 0990cc098d..5ad0436215 100644 --- a/lib/parser/javascript.js +++ b/lib/parser/javascript.js @@ -1,3 +1,5 @@ +'use strict'; + var events = require("events"), util = require("../util"); diff --git a/lib/queue.js b/lib/queue.js index 0d258cdfcb..d2eb17e56c 100644 --- a/lib/queue.js +++ b/lib/queue.js @@ -1,3 +1,5 @@ +'use strict'; + // Queue class adapted from Tim Caswell's pattern library // http://github.com/creationix/pattern/blob/master/lib/pattern/queue.js diff --git a/lib/to_array.js b/lib/to_array.js index 88a57e18a4..87804f8c3b 100644 --- a/lib/to_array.js +++ b/lib/to_array.js @@ -1,3 +1,5 @@ +'use strict'; + function to_array(args) { var len = args.length, arr = new Array(len), i; diff --git a/lib/util.js b/lib/util.js index fc255ae953..359cd7e948 100644 --- a/lib/util.js +++ b/lib/util.js @@ -1,3 +1,5 @@ +'use strict'; + // Support for very old versions of node where the module was called "sys". At some point, we should abandon this. var util; diff --git a/multi_bench.js b/multi_bench.js index dbe04e515a..4e99376787 100644 --- a/multi_bench.js +++ b/multi_bench.js @@ -1,3 +1,5 @@ +'use strict'; + var redis = require("./index"), metrics = require("metrics"), num_clients = parseInt(process.argv[2], 10) || 5, diff --git a/test/queue-test.js b/test/queue-test.js index 017ea60785..4811eca90a 100644 --- a/test/queue-test.js +++ b/test/queue-test.js @@ -1,3 +1,5 @@ +'use strict'; + var assert = require("assert"); var Queue = require('../lib/queue'); diff --git a/test/test.js b/test/test.js index e7db763914..325ea13658 100644 --- a/test/test.js +++ b/test/test.js @@ -1,3 +1,5 @@ +'use strict'; + /*global require console setTimeout process Buffer */ var PORT = 6379; var HOST = '127.0.0.1'; From a2bc59721293bbc9504db06c0ee18a51429c54c6 Mon Sep 17 00:00:00 2001 From: Ruben Bridgewater Date: Wed, 22 Jul 2015 16:19:02 +0200 Subject: [PATCH 26/49] Add simicolons This is just a style change --- diff_multi_bench_output.js | 2 +- examples/extend.js | 2 +- examples/file.js | 6 +++--- examples/unix_socket.js | 4 +++- index.js | 10 +++++----- test/queue-test.js | 10 +++++----- test/test-unref.js | 16 +++++++++------- test/test.js | 20 ++++++++++---------- 8 files changed, 37 insertions(+), 33 deletions(-) diff --git a/diff_multi_bench_output.js b/diff_multi_bench_output.js index 23cb3ada21..0aff103198 100755 --- a/diff_multi_bench_output.js +++ b/diff_multi_bench_output.js @@ -49,7 +49,7 @@ before_lines.forEach(function(b, i) { var isNaN = !num && num !== 0; return !isNaN; }); - if (ops.length != 2) return + if (ops.length != 2) return; var delta = ops[1] - ops[0]; var pct = ((delta / ops[0]) * 100).toPrecision(3); diff --git a/examples/extend.js b/examples/extend.js index b3a10fa716..22566082d4 100644 --- a/examples/extend.js +++ b/examples/extend.js @@ -16,7 +16,7 @@ redis.RedisClient.prototype.parse_info = function (callback) { obj[parts[0]] = parts[1]; } }); - callback(obj) + callback(obj); }); }; diff --git a/examples/file.js b/examples/file.js index 9197019858..620b0a1055 100644 --- a/examples/file.js +++ b/examples/file.js @@ -13,9 +13,9 @@ var redis = require("redis"), // Read a file from fs, store it in Redis, get it back from Redis, write it back to fs. fs.readFile(filename, function (err, data) { - if (err) throw err + if (err) throw err; console.log("Read " + data.length + " bytes from filesystem."); - + client.set(filename, data, redis.print); // set entire file client.get(filename, function (err, reply) { // get entire file if (err) { @@ -23,7 +23,7 @@ fs.readFile(filename, function (err, data) { } else { fs.writeFile("duplicate_" + filename, reply, function (err) { if (err) { - console.log("Error on write: " + err) + console.log("Error on write: " + err); } else { console.log("File written."); } diff --git a/examples/unix_socket.js b/examples/unix_socket.js index 4a5e0bb0e8..460b78e857 100644 --- a/examples/unix_socket.js +++ b/examples/unix_socket.js @@ -1,9 +1,11 @@ +'use strict'; + var redis = require("redis"), client = redis.createClient("/tmp/redis.sock"), profiler = require("v8-profiler"); client.on("connect", function () { - console.log("Got Unix socket connection.") + console.log("Got Unix socket connection."); }); client.on("error", function (err) { diff --git a/index.js b/index.js index 6c70a25129..bdc2889b9a 100644 --- a/index.js +++ b/index.js @@ -17,10 +17,10 @@ var net = require("net"), // can set this to true to enable for all connections exports.debug_mode = false; -var arraySlice = Array.prototype.slice +var arraySlice = Array.prototype.slice; function trace() { if (!exports.debug_mode) return; - console.log.apply(null, arraySlice.call(arguments)) + console.log.apply(null, arraySlice.call(arguments)); } // hiredis might not be installed @@ -141,7 +141,7 @@ RedisClient.prototype.unref = function () { trace("Not connected yet, will unref later"); this.once("connect", function () { this.unref(); - }) + }); } }; @@ -219,7 +219,7 @@ RedisClient.prototype.do_auth = function () { }, 2000); // TODO - magic number alert return; } else if (err.toString().match("no password is set")) { - console.log("Warning: Redis server does not require a password, but a password was supplied.") + console.log("Warning: Redis server does not require a password, but a password was supplied."); err = null; res = "OK"; } else { @@ -1265,7 +1265,7 @@ var createClient_unix = function(path, options){ redis_client.address = path; return redis_client; -} +}; var createClient_tcp = function (port_arg, host_arg, options) { var cnxOptions = { diff --git a/test/queue-test.js b/test/queue-test.js index 4811eca90a..dbc9771a0a 100644 --- a/test/queue-test.js +++ b/test/queue-test.js @@ -11,12 +11,12 @@ module.exports = function (tests, next) { q.push(3); assert.equal(q.length, 2); return next(); - } + }; tests.shift = function () { assert.equal(q.shift(), 'a'); return next(); - } + }; tests.forEach = function () { q.forEach(function (v) { @@ -24,7 +24,7 @@ module.exports = function (tests, next) { }); return next(); - } + }; tests.forEachWithScope = function () { q.forEach(function (v) { @@ -33,5 +33,5 @@ module.exports = function (tests, next) { }, {foo: 'bar'}); return next(); - } -} + }; +}; diff --git a/test/test-unref.js b/test/test-unref.js index d66d77c433..c7dc930004 100644 --- a/test/test-unref.js +++ b/test/test-unref.js @@ -1,12 +1,14 @@ -var redis = require("../") +'use strict'; + +var redis = require("../"); //redis.debug_mode = true var PORT = process.argv[2] || 6379; var HOST = process.argv[3] || '127.0.0.1'; -var c = redis.createClient(PORT, HOST) -c.unref() +var c = redis.createClient(PORT, HOST); +c.unref(); c.info(function (err, reply) { - if (err) process.exit(-1) - if (!reply.length) process.exit(-1) - process.stdout.write(reply.length.toString()) -}) + if (err) process.exit(-1); + if (!reply.length) process.exit(-1); + process.stdout.write(reply.length.toString()); +}); diff --git a/test/test.js b/test/test.js index 325ea13658..7bb2966ff8 100644 --- a/test/test.js +++ b/test/test.js @@ -137,7 +137,7 @@ tests.IPV4 = function () { console.error("client: " + err.stack); process.exit(); }); -} +}; tests.IPV6 = function () { if (!server_version_at_least(client, [2, 8, 0])) { @@ -163,7 +163,7 @@ tests.IPV6 = function () { console.error("client: " + err.stack); process.exit(); }); -} +}; tests.UNIX_SOCKET = function () { var unixClient = redis.createClient('/tmp/redis.sock', { parser: parser }); @@ -189,7 +189,7 @@ tests.UNIX_SOCKET = function () { console.error("client: " + err.stack); process.exit(); }); -} +}; tests.FLUSHDB = function () { var name = "FLUSHDB"; @@ -1543,7 +1543,7 @@ tests.HGETALL_MESSAGE = function () { client.hgetall("msg_test", function (err, obj) { assert.strictEqual(null, err, name + " result sent back unexpected error: " + err); assert.strictEqual(1, Object.keys(obj).length, name); - assert.strictEqual(obj.message, "hello") + assert.strictEqual(obj.message, "hello"); next(name); }); }; @@ -2178,8 +2178,8 @@ tests.ENABLE_OFFLINE_QUEUE_FALSE = function () { // ignore, see above }); assert.throws(function () { - cli.set(name, name) - }) + cli.set(name, name); + }); assert.doesNotThrow(function () { cli.set(name, name, function (err) { // should callback with an error @@ -2205,7 +2205,7 @@ tests.SLOWLOG = function () { client.config("set", "slowlog-log-slower-than", 10000, require_string("OK", name)); next(name); }); -} +}; tests.DOMAIN = function () { var name = "DOMAIN"; @@ -2323,9 +2323,9 @@ tests.unref = function () { var external = fork("./test/test-unref.js"); var done = false; external.on("close", function (code) { - assert(code == 0, "test-unref.js failed"); + assert(code === 0, "test-unref.js failed"); done = true; - }) + }); setTimeout(function () { if (!done) { external.kill(); @@ -2336,7 +2336,7 @@ tests.unref = function () { }; // starting to split tests into multiple files. -require('./queue-test')(tests, next) +require('./queue-test')(tests, next); all_tests = Object.keys(tests); all_start = new Date(); From 6866ff9b1ef4bc9e677f79a46bd1996d51b52448 Mon Sep 17 00:00:00 2001 From: Ruben Bridgewater Date: Wed, 22 Jul 2015 16:19:30 +0200 Subject: [PATCH 27/49] Remove trailing whitespace --- benches/buffer_bench.js | 2 +- examples/subquery.js | 2 +- multi_bench.js | 12 ++++++------ 3 files changed, 8 insertions(+), 8 deletions(-) diff --git a/benches/buffer_bench.js b/benches/buffer_bench.js index df16d90bf4..64347e583f 100644 --- a/benches/buffer_bench.js +++ b/benches/buffer_bench.js @@ -2,7 +2,7 @@ var source = new Buffer(100), dest = new Buffer(100), i, j, k, tmp, count = 1000000, bytes = 100; - + for (i = 99 ; i >= 0 ; i--) { source[i] = 120; } diff --git a/examples/subquery.js b/examples/subquery.js index 4f1edb0ffd..ffb7eca382 100644 --- a/examples/subquery.js +++ b/examples/subquery.js @@ -9,7 +9,7 @@ function print_results(obj) { // build a map of all keys and their types client.keys("*", function (err, all_keys) { var key_types = {}; - + all_keys.forEach(function (key, pos) { // use second arg of forEach to get pos client.type(key, function (err, type) { key_types[key] = type; diff --git a/multi_bench.js b/multi_bench.js index 4e99376787..9f210a76d4 100644 --- a/multi_bench.js +++ b/multi_bench.js @@ -25,13 +25,13 @@ function lpad(input, len, chr) { metrics.Histogram.prototype.print_line = function () { var obj = this.printObj(); - + return lpad(obj.min, 4) + "/" + lpad(obj.max, 4) + "/" + lpad(obj.mean.toFixed(2), 7) + "/" + lpad(obj.p95.toFixed(2), 7); }; function Test(args) { this.args = args; - + this.callback = null; this.clients = []; this.clients_ready = 0; @@ -58,7 +58,7 @@ Test.prototype.run = function (callback) { Test.prototype.new_client = function (id) { var self = this, new_client; - + new_client = redis.createClient(6379, "127.0.0.1", this.client_options); new_client.create_time = Date.now(); @@ -97,7 +97,7 @@ Test.prototype.fill_pipeline = function () { pipeline++; this.send_next(); } - + if (this.commands_completed === this.num_requests) { this.print_stats(); this.stop_clients(); @@ -106,7 +106,7 @@ Test.prototype.fill_pipeline = function () { Test.prototype.stop_clients = function () { var self = this; - + this.clients.forEach(function (client, pos) { if (pos === self.clients.length - 1) { client.quit(function (err, res) { @@ -135,7 +135,7 @@ Test.prototype.send_next = function () { Test.prototype.print_stats = function () { var duration = Date.now() - this.test_start; - + console.log("min/max/avg/p95: " + this.command_latency.print_line() + " " + lpad(duration, 6) + "ms total, " + lpad((this.num_requests / (duration / 1000)).toFixed(2), 8) + " ops/sec"); }; From 745fccebfb9e8ac264ad5084944166b493576a91 Mon Sep 17 00:00:00 2001 From: Ruben Bridgewater Date: Wed, 22 Jul 2015 16:19:46 +0200 Subject: [PATCH 28/49] Add var statements to undeclared variables and remove unused variables --- benches/stress/codec.js | 10 ---------- diff_multi_bench_output.js | 4 ++-- 2 files changed, 2 insertions(+), 12 deletions(-) diff --git a/benches/stress/codec.js b/benches/stress/codec.js index c679a6ee01..5a3cc6daa4 100644 --- a/benches/stress/codec.js +++ b/benches/stress/codec.js @@ -5,14 +5,4 @@ var json = { decode: JSON.parse }; -var MsgPack = require('node-msgpack'); -msgpack = { - encode: MsgPack.pack, - decode: function(str) { return MsgPack.unpack(new Buffer(str)); } -}; - -bison = require('bison'); - module.exports = json; -//module.exports = msgpack; -//module.exports = bison; diff --git a/diff_multi_bench_output.js b/diff_multi_bench_output.js index 0aff103198..8eab0a2c69 100755 --- a/diff_multi_bench_output.js +++ b/diff_multi_bench_output.js @@ -37,8 +37,8 @@ before_lines.forEach(function(b, i) { return; } - b_words = b.split(' ').filter(is_whitespace); - a_words = a.split(' ').filter(is_whitespace); + var b_words = b.split(' ').filter(is_whitespace); + var a_words = a.split(' ').filter(is_whitespace); var ops = [b_words, a_words] From 0908e9a46bb1c681410d63fa00da3c9a6b703f43 Mon Sep 17 00:00:00 2001 From: Ruben Bridgewater Date: Wed, 22 Jul 2015 16:27:05 +0200 Subject: [PATCH 29/49] Use console.log instead of deprecated util.print util.print has been deprecated in node --- test/test.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/test.js b/test/test.js index 7bb2966ff8..5bcc32fd51 100644 --- a/test/test.js +++ b/test/test.js @@ -2345,7 +2345,7 @@ test_count = 0; run_next_test = function run_next_test() { var test_name = all_tests.shift(); if (typeof tests[test_name] === "function") { - util.print('- \x1b[1m' + test_name.toLowerCase() + '\x1b[0m:'); + console.log('- \x1b[1m' + test_name.toLowerCase() + '\x1b[0m:'); cur_start = new Date(); test_count += 1; tests[test_name](); From 01a377383bc97522665059ec85b71985d4e2f76e Mon Sep 17 00:00:00 2001 From: Ruben Bridgewater Date: Wed, 22 Jul 2015 16:43:01 +0200 Subject: [PATCH 30/49] Use type safe comparison --- diff_multi_bench_output.js | 4 ++-- test/test.js | 10 +++++----- 2 files changed, 7 insertions(+), 7 deletions(-) diff --git a/diff_multi_bench_output.js b/diff_multi_bench_output.js index 8eab0a2c69..89b6be479c 100755 --- a/diff_multi_bench_output.js +++ b/diff_multi_bench_output.js @@ -49,7 +49,7 @@ before_lines.forEach(function(b, i) { var isNaN = !num && num !== 0; return !isNaN; }); - if (ops.length != 2) return; + if (ops.length !== 2) return; var delta = ops[1] - ops[0]; var pct = ((delta / ops[0]) * 100).toPrecision(3); @@ -60,7 +60,7 @@ before_lines.forEach(function(b, i) { pct = humanize_diff(pct, '%'); console.log( // name of test - command_name(a_words) == command_name(b_words) + command_name(a_words) === command_name(b_words) ? command_name(a_words) + ':' : '404:', // results of test diff --git a/test/test.js b/test/test.js index 5bcc32fd51..ffb097bda4 100644 --- a/test/test.js +++ b/test/test.js @@ -204,7 +204,7 @@ tests.FLUSHDB = function () { tests.INCR = function () { var name = "INCR"; - if (bclient.reply_parser.name == "hiredis") { + if (bclient.reply_parser.name === "hiredis") { console.log("Skipping INCR buffer test with hiredis"); return next(name); } @@ -371,7 +371,7 @@ tests.MULTI_6 = function () { tests.MULTI_7 = function () { var name = "MULTI_7"; - if (bclient.reply_parser.name != "javascript") { + if (bclient.reply_parser.name !== "javascript") { console.log("Skipping wire-protocol test for 3rd-party parser"); return next(name); } @@ -467,7 +467,7 @@ tests.FWD_ERRORS_1 = function () { client3.on("message", function (channel, data) { console.log("incoming"); - if (channel == name) { + if (channel === name) { assert.equal(data, "Some message"); throw toThrow; } @@ -1286,11 +1286,11 @@ tests.SUBSCRIBE_CLOSE_RESUBSCRIBE = function () { console.log("c1 is ready", count); count++; - if (count == 1) { + if (count === 1) { c2.publish("chan1", "hi on channel 1"); return; - } else if (count == 2) { + } else if (count === 2) { c2.publish("chan2", "hi on channel 2"); } else { From d34dcc8b07021748bfaa4fa295601bd14c506129 Mon Sep 17 00:00:00 2001 From: Hans Kristian Flaatten Date: Thu, 23 Jul 2015 22:14:51 +0200 Subject: [PATCH 31/49] Check for REDIS HOST and PORT environment varibles --- test/test.js | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/test/test.js b/test/test.js index e7db763914..638747303f 100644 --- a/test/test.js +++ b/test/test.js @@ -1,6 +1,6 @@ /*global require console setTimeout process Buffer */ -var PORT = 6379; -var HOST = '127.0.0.1'; +var PORT = process.env.REDIS_1_PORT_6379_TCP_PORT || 6379; +var HOST = process.env.REDIS_1_PORT_6379_TCP_ADDR || '127.0.0.1'; var parser = process.argv[3]; var redis = require("../index"), @@ -116,7 +116,8 @@ next = function next(name) { // Tests are run in the order they are defined, so FLUSHDB should always be first. tests.IPV4 = function () { - var ipv4Client = redis.createClient( PORT, "127.0.0.1", { family : "IPv4", parser: parser } ); + var ipv4addr = process.env.REDIS_1_PORT_6379_TCP_ADDR || "127.0.0.1"; + var ipv4Client = redis.createClient( PORT, ipv4addr, { family : "IPv4", parser: parser } ); ipv4Client.once("ready", function start_tests() { console.log("Connected to " + ipv4Client.address + ", Redis server version " + ipv4Client.server_info.redis_version + "\n"); @@ -142,7 +143,8 @@ tests.IPV6 = function () { console.log("Skipping IPV6 for old Redis server version < 2.8.0"); return run_next_test(); } - var ipv6Client = redis.createClient( PORT, "::1", { family: "IPv6", parser: parser } ); + var ipv6addr = process.env.REDIS_1_PORT_6379_TCP_ADDR || "::1"; + var ipv6Client = redis.createClient( PORT, ipv6addr, { family: "IPv6", parser: parser } ); ipv6Client.once("ready", function start_tests() { console.log("Connected to " + ipv6Client.address + ", Redis server version " + ipv6Client.server_info.redis_version + "\n"); From e646b4eb3970df11572163a74728c2852452bfea Mon Sep 17 00:00:00 2001 From: Hans Kristian Flaatten Date: Thu, 23 Jul 2015 22:16:07 +0200 Subject: [PATCH 32/49] Skip SOCKET test if Redis is not listening on a socket --- test/test.js | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/test/test.js b/test/test.js index 638747303f..8344d14b5f 100644 --- a/test/test.js +++ b/test/test.js @@ -166,6 +166,15 @@ tests.IPV6 = function () { } tests.UNIX_SOCKET = function () { + try { + var stat = require('fs').accessSync('/tmp/redis.sock'); + } catch(err) { + if (err.code === 'ENOENT') { + console.log("Skipping SOCKET since none exists"); + return run_next_test(); + } + } + var unixClient = redis.createClient('/tmp/redis.sock', { parser: parser }); // if this fails, check the permission of unix socket. From 8b5ce1088a555174d2202a8852a7ecae6f969a3c Mon Sep 17 00:00:00 2001 From: Hans Kristian Flaatten Date: Thu, 23 Jul 2015 22:16:48 +0200 Subject: [PATCH 33/49] Allways pass HOST and PORT when creating a new client --- test/test.js | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/test/test.js b/test/test.js index 8344d14b5f..955af604b4 100644 --- a/test/test.js +++ b/test/test.js @@ -739,7 +739,7 @@ tests.WATCH_TRANSACTION = function () { tests.detect_buffers = function () { - var name = "detect_buffers", detect_client = redis.createClient({ detect_buffers: true, parser: parser }); + var name = "detect_buffers", detect_client = redis.createClient(PORT, HOST, { detect_buffers: true, parser: parser }); detect_client.on("ready", function () { // single Buffer or String @@ -804,7 +804,7 @@ tests.detect_buffers = function () { }; tests.detect_buffers_multi = function () { - var name = "detect_buffers_multi", detect_client = redis.createClient({detect_buffers: true}); + var name = "detect_buffers_multi", detect_client = redis.createClient(PORT, HOST, {detect_buffers: true}); detect_client.on("ready", function () { // single Buffer or String @@ -895,9 +895,9 @@ tests.detect_buffers_multi = function () { tests.socket_nodelay = function () { var name = "socket_nodelay", c1, c2, c3, ready_count = 0, quit_count = 0; - c1 = redis.createClient({ socket_nodelay: true, parser: parser }); - c2 = redis.createClient({ socket_nodelay: false, parser: parser }); - c3 = redis.createClient({ parser: parser }); + c1 = redis.createClient(PORT, HOST, { socket_nodelay: true, parser: parser }); + c2 = redis.createClient(PORT, HOST, { socket_nodelay: false, parser: parser }); + c3 = redis.createClient(PORT, HOST, { parser: parser }); function quit_check() { quit_count++; @@ -1258,8 +1258,8 @@ tests.SUBSCRIBE_QUIT = function () { tests.SUBSCRIBE_CLOSE_RESUBSCRIBE = function () { var name = "SUBSCRIBE_CLOSE_RESUBSCRIBE"; - var c1 = redis.createClient({ parser: parser }); - var c2 = redis.createClient({ parser: parser }); + var c1 = redis.createClient(PORT, HOST, { parser: parser }); + var c2 = redis.createClient(PORT, HOST, { parser: parser }); var count = 0; /* Create two clients. c1 subscribes to two channels, c2 will publish to them. @@ -2055,7 +2055,7 @@ tests.MONITOR = function () { return next(name); } - monitor_client = redis.createClient({ parser: parser }); + monitor_client = redis.createClient(PORT, HOST, { parser: parser }); monitor_client.monitor(function (err, res) { client.mget("some", "keys", "foo", "bar"); client.set("json", JSON.stringify({ @@ -2329,7 +2329,7 @@ tests.reconnectRetryMaxDelay = function() { tests.unref = function () { var name = "unref"; - var external = fork("./test/test-unref.js"); + var external = fork("./test/test-unref.js", [PORT, HOST]); var done = false; external.on("close", function (code) { assert(code == 0, "test-unref.js failed"); From da535a697768a8f8617544d3a37adb754c3d5f8b Mon Sep 17 00:00:00 2001 From: Hans Kristian Flaatten Date: Fri, 24 Jul 2015 08:07:33 +0200 Subject: [PATCH 34/49] Normalize linked REDIS environment variables --- test/test.js | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/test/test.js b/test/test.js index 955af604b4..a4c79e4f3b 100644 --- a/test/test.js +++ b/test/test.js @@ -1,6 +1,6 @@ /*global require console setTimeout process Buffer */ -var PORT = process.env.REDIS_1_PORT_6379_TCP_PORT || 6379; -var HOST = process.env.REDIS_1_PORT_6379_TCP_ADDR || '127.0.0.1'; +var PORT = process.env.REDIS_PORT_6379_TCP_PORT || 6379; +var HOST = process.env.REDIS_PORT_6379_TCP_ADDR || '127.0.0.1'; var parser = process.argv[3]; var redis = require("../index"), @@ -116,7 +116,7 @@ next = function next(name) { // Tests are run in the order they are defined, so FLUSHDB should always be first. tests.IPV4 = function () { - var ipv4addr = process.env.REDIS_1_PORT_6379_TCP_ADDR || "127.0.0.1"; + var ipv4addr = process.env.REDIS_PORT_6379_TCP_ADDR || "127.0.0.1"; var ipv4Client = redis.createClient( PORT, ipv4addr, { family : "IPv4", parser: parser } ); ipv4Client.once("ready", function start_tests() { @@ -143,7 +143,7 @@ tests.IPV6 = function () { console.log("Skipping IPV6 for old Redis server version < 2.8.0"); return run_next_test(); } - var ipv6addr = process.env.REDIS_1_PORT_6379_TCP_ADDR || "::1"; + var ipv6addr = process.env.REDIS_PORT_6379_TCP_ADDR || "::1"; var ipv6Client = redis.createClient( PORT, ipv6addr, { family: "IPv6", parser: parser } ); ipv6Client.once("ready", function start_tests() { From b1ae21d221fd96d34e0a347630df31c0afd380a6 Mon Sep 17 00:00:00 2001 From: Hans Kristian Flaatten Date: Fri, 24 Jul 2015 15:33:37 +0200 Subject: [PATCH 35/49] Revert "Skip SOCKET test if Redis is not listening on a socket" This reverts commit e646b4eb3970df11572163a74728c2852452bfea. --- test/test.js | 9 --------- 1 file changed, 9 deletions(-) diff --git a/test/test.js b/test/test.js index a4c79e4f3b..48dd86a56b 100644 --- a/test/test.js +++ b/test/test.js @@ -166,15 +166,6 @@ tests.IPV6 = function () { } tests.UNIX_SOCKET = function () { - try { - var stat = require('fs').accessSync('/tmp/redis.sock'); - } catch(err) { - if (err.code === 'ENOENT') { - console.log("Skipping SOCKET since none exists"); - return run_next_test(); - } - } - var unixClient = redis.createClient('/tmp/redis.sock', { parser: parser }); // if this fails, check the permission of unix socket. From 2b442450568744b7585d87483a449a09c29048af Mon Sep 17 00:00:00 2001 From: Erin Spiceland Date: Sun, 12 Jul 2015 13:31:13 -0500 Subject: [PATCH 36/49] Add example test with grunt and mocha. Add test for reconnect. Run each test for both parsers and both IP versions. Don't save a reference to this nodified assertion function. Add DEBUG env var which enables extra debug logging in node_redis. Remove Grunt, run Redis on 6378 for non-interference. Remove the tests already ported to Mocha. Port reconnect after pubsub test; add subscribed after reconnect test. Reconnet after pubsub test confused me. I don't think it tested anything, and it didn't pass for me after I ported it. I've disabled it and added a note. In its place, I've added a test to make sure we are still subscribed and can receive pubsub messages after a reconnect. Move test suite config-like stuff into a library. Move test suite createClient args generation into the config library. WIP. Some select tests, most disabled and still WIP. --- package.json | 2 + run-bootstrapped-mocha.js | 28 ++++++ test/conf/redis.conf | 5 + test/lib/config.js | 30 ++++++ test/lib/nodeify-assertions.js | 60 ++++++++++++ test/lib/redis-process.js | 23 +++++ test/mocha/connecting.spec.js | 165 +++++++++++++++++++++++++++++++++ test/mocha/select.spec.js | 130 ++++++++++++++++++++++++++ test/test.js | 46 +-------- 9 files changed, 444 insertions(+), 45 deletions(-) create mode 100644 run-bootstrapped-mocha.js create mode 100644 test/conf/redis.conf create mode 100644 test/lib/config.js create mode 100644 test/lib/nodeify-assertions.js create mode 100644 test/lib/redis-process.js create mode 100644 test/mocha/connecting.spec.js create mode 100644 test/mocha/select.spec.js diff --git a/package.json b/package.json index 495091bbea..b5118d85de 100644 --- a/package.json +++ b/package.json @@ -14,10 +14,12 @@ "test": "nyc ./test/run.sh" }, "devDependencies": { + "async": "^1.3.0", "colors": "~0.6.0-1", "coveralls": "^2.11.2", "hiredis": "^0.4.0", "metrics": ">=0.1.5", + "mocha": "^2.2.5", "nyc": "^3.0.0", "underscore": "~1.4.4" }, diff --git a/run-bootstrapped-mocha.js b/run-bootstrapped-mocha.js new file mode 100644 index 0000000000..716b7c0708 --- /dev/null +++ b/run-bootstrapped-mocha.js @@ -0,0 +1,28 @@ +var pm = require('./test/lib/redis-process'); +var cp = require('child_process'); +var testSets = 'test/mocha/**/*.spec.js'; +var async = require('async'); +var redis; + +process.on("exit", function () { + if (redis) { + redis.stop(); + } +}); + +async.series([function startRedis(next) { + redis = pm.start(function (err) { + next(err); + }); +}, function runMocha(next) { + var mocha = cp.spawn('mocha', [testSets], { + stdio: "inherit" + }); + mocha.on("exit", function (code) { + next(); + }); +}, function stopRedis(next) { + redis.stop(next); +}], function (err) { + // done; +}); diff --git a/test/conf/redis.conf b/test/conf/redis.conf new file mode 100644 index 0000000000..1e900d9787 --- /dev/null +++ b/test/conf/redis.conf @@ -0,0 +1,5 @@ +daemonize yes +port 6378 +bind ::1 +unixsocket /tmp/redis.sock +unixsocketperm 755 diff --git a/test/lib/config.js b/test/lib/config.js new file mode 100644 index 0000000000..475f2733ce --- /dev/null +++ b/test/lib/config.js @@ -0,0 +1,30 @@ +module.exports = (function () { + var redis = require('../../index'); + redis.debug_mode = process.env.DEBUG ? JSON.parse(process.env.DEBUG) : false; + + var config = { + redis: redis, + PORT: 6378, + HOST: { + IPv4: "127.0.0.1", + IPv6: "::1" + } + }; + + config.configureClient = function (parser, ip, isSocket) { + var args = []; + + if (!isSocket) { + args.push(config.PORT); + args.push(config.HOST[ip]); + args.push({ family: ip, parser: parser }); + } else { + args.push(ip); + args.push({ parser: parser }); + } + + return args; + }; + + return config; +})(); diff --git a/test/lib/nodeify-assertions.js b/test/lib/nodeify-assertions.js new file mode 100644 index 0000000000..c80cccb8e5 --- /dev/null +++ b/test/lib/nodeify-assertions.js @@ -0,0 +1,60 @@ +var assert = require('assert'); + +module.exports = { + isNumber: function (expected) { + return function (err, results) { + assert.strictEqual(null, err, "expected " + expected + ", got error: " + err); + assert.strictEqual(expected, results, expected + " !== " + results); + assert.strictEqual(typeof results, "number", "expected a number, got " + typeof results); + return true; + }; + }, + + isString: function (str) { + return function (err, results) { + assert.strictEqual(null, err, "expected string '" + str + "', got error: " + err); + assert.equal(str, results, str + " does not match " + results); + return true; + }; + }, + + isNull: function () { + return function (err, results) { + assert.strictEqual(null, err, "expected null, got error: " + err); + assert.strictEqual(null, results, results + " is not null"); + return true; + }; + }, + + isError: function () { + return function (err, results) { + assert.notEqual(err, null, "err is null, but an error is expected here."); + return true; + }; + }, + + isNotError: function () { + return function (err, results) { + assert.strictEqual(err, null, "expected success, got an error: " + err); + return true; + }; + }, + + isType: { + number: function () { + return function (err, results) { + assert.strictEqual(null, err, "expected any number, got error: " + err); + assert.strictEqual(typeof results, "number", results + " is not a number"); + return true; + }; + }, + + positiveNumber: function () { + return function (err, results) { + assert.strictEqual(null, err, "expected positive number, got error: " + err); + assert.strictEqual(true, (results > 0), results + " is not a positive number"); + return true; + }; + } + } +}; diff --git a/test/lib/redis-process.js b/test/lib/redis-process.js new file mode 100644 index 0000000000..a77d20bcc8 --- /dev/null +++ b/test/lib/redis-process.js @@ -0,0 +1,23 @@ +var cp = require('child_process'); + +module.exports = { + start: function (done, isSocket) { + var confFile = isSocket ? "test/conf/redis-socket.conf" : "test/conf/redis.conf"; + var redis = cp.spawn("redis-server", [confFile]); + + redis.once('err', done); + setTimeout(function (data) { + redis.removeListener('err', done); + done(); + }, 1000); + + return { + stop: function (done) { + redis.once("exit", function () { + done(); + }); + redis.kill("SIGINT"); + } + }; + } +}; diff --git a/test/mocha/connecting.spec.js b/test/mocha/connecting.spec.js new file mode 100644 index 0000000000..dd46f56f59 --- /dev/null +++ b/test/mocha/connecting.spec.js @@ -0,0 +1,165 @@ +var nodeAssert = require("../lib/nodeify-assertions"); +var config = require("../lib/config"); +var redis = config.redis; +var async = require("async"); + +describe("A node_redis client", function () { + function allTests(parser, ip, isSocket) { + var args = config.configureClient(parser, ip, isSocket); + + describe("using " + parser + " and " + ip, function () { + var client; + + after(function () { + client.end(); + }); + + it("connects correctly", 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); + }); + }); + }); + }); + + describe("when connected", function () { + var client; + + beforeEach(function (done) { + client = redis.createClient.apply(redis.createClient, args); + client.once("error", function onError(err) { + done(err); + }); + client.once("ready", function onReady() { + done(); + }); + }); + + afterEach(function () { + client.end(); + }); + + describe("when redis closes unexpectedly", function () { + it("reconnects and can retrieve the pre-existing data", function (done) { + client.on("reconnecting", function on_recon(params) { + client.on("connect", function on_connect() { + async.parallel([function (cb) { + client.get("recon 1", function (err, res) { + nodeAssert.isString("one")(err, res); + cb(); + }); + }, function (cb) { + client.get("recon 1", function (err, res) { + nodeAssert.isString("one")(err, res); + cb(); + }); + }, function (cb) { + client.get("recon 2", function (err, res) { + nodeAssert.isString("two")(err, res); + cb(); + }); + }, function (cb) { + client.get("recon 2", function (err, res) { + nodeAssert.isString("two")(err, res); + cb(); + }); + }], function (err, results) { + client.removeListener("connect", on_connect); + client.removeListener("reconnecting", on_recon); + done(err); + }); + }); + }); + + client.set("recon 1", "one"); + client.set("recon 2", "two", function (err, res) { + // Do not do this in normal programs. This is to simulate the server closing on us. + // For orderly shutdown in normal programs, do client.quit() + client.stream.destroy(); + }); + }); + + describe("and it's subscribed to a channel", function () { + // reconnect_select_db_after_pubsub + // Does not pass. + // "Connection in subscriber mode, only subscriber commands may be used" + xit("reconnects, unsubscribes, and can retrieve the pre-existing data", function (done) { + client.on("reconnecting", function on_recon(params) { + client.on("ready", function on_connect() { + async.parallel([function (cb) { + client.unsubscribe("recon channel", function (err, res) { + nodeAssert.isNotError()(err, res); + cb(); + }); + }, function (cb) { + client.get("recon 1", function (err, res) { + nodeAssert.isString("one")(err, res); + cb(); + }); + }], function (err, results) { + client.removeListener("connect", on_connect); + client.removeListener("reconnecting", on_recon); + done(err); + }); + }); + }); + + client.set("recon 1", "one"); + client.subscribe("recon channel", function (err, res) { + // Do not do this in normal programs. This is to simulate the server closing on us. + // For orderly shutdown in normal programs, do client.quit() + client.stream.destroy(); + }); + }); + + it("remains subscribed", function () { + var client2 = redis.createClient.apply(redis.createClient, args); + + client.on("reconnecting", function on_recon(params) { + client.on("ready", function on_connect() { + async.parallel([function (cb) { + client.on("message", function (channel, message) { + try { + nodeAssert.isString("recon channel")(null, channel); + nodeAssert.isString("a test message")(null, message); + } catch (err) { + cb(err); + } + }); + + client2.subscribe("recon channel", function (err, res) { + if (err) { + cb(err); + return; + } + client2.publish("recon channel", "a test message"); + }); + }], function (err, results) { + done(err); + }); + }); + }); + + client.subscribe("recon channel", function (err, res) { + // Do not do this in normal programs. This is to simulate the server closing on us. + // For orderly shutdown in normal programs, do client.quit() + client.stream.destroy(); + }); + }); + }); + }); + }); + } + + ['javascript', 'hiredis'].forEach(function (parser) { + allTests(parser, "/tmp/redis.sock", true); + ['IPv4', 'IPv6'].forEach(function (ip) { + allTests(parser, ip); + }) + }); +}); diff --git a/test/mocha/select.spec.js b/test/mocha/select.spec.js new file mode 100644 index 0000000000..418ca4439f --- /dev/null +++ b/test/mocha/select.spec.js @@ -0,0 +1,130 @@ +var nodeAssert = require("../lib/nodeify-assertions"); +var config = require("../lib/config"); +var redis = config.redis; +var async = require("async"); +var assert = require("assert"); + +describe("The 'select' method", function () { + function allTests(parser, ip, isSocket) { + var args = config.configureClient(parser, ip, isSocket); + + describe("using " + parser + " and " + ip, function () { + describe("when not connected", function () { + var client; + + beforeEach(function (done) { + client = redis.createClient.apply(redis.createClient, args); + client.once("error", done); + + client.once("connect", function () { + client.set("doot", "good calsum", function (err, res) { + client.end(); + done(); + }); + }); + }); + + it("doesn't even throw an error or call the callback at all WTF", function (done) { + this.timeout(50); + + client.select(1, function (err, res) { + nodeAssert.isNotError()(err, res); + done(); + }); + + setTimeout(function () { + done(); + }, 45); + }); + }); + + describe("when connected", function () { + var client; + + beforeEach(function (done) { + client = redis.createClient.apply(redis.createClient, args); + client.once("error", done); + + client.once("connect", function () { + done(); + }); + }); + + afterEach(function () { + client.end(); + }); + + it("changes the database and calls the callback", function (done) { + // default value of null means database 0 will be used. + assert.strictEqual(client.selected_db, null, "default db should be null"); + client.select(1, function (err, res) { + nodeAssert.isNotError()(err, res); + assert.strictEqual(client.selected_db, 1, "db should be 1 after select"); + done(); + }); + }); + + describe("and no callback is specified", function () { + // select_error_emits_if_no_callback + // this is another test that was testing the wrong thing. The old test did indeed emit an error, + // but not because of the lacking callback, but because 9999 was an invalid db index. + describe("with a valid db index", function () { + it("works just fine and does not actually emit an error like the old tests assert WTF", function (done) { + assert.strictEqual(client.selected_db, null, "default db should be null"); + this.timeout(50); + client.on("error", function (err) { + nodeAssert.isNotError()(err); + assert.strictEqual(client.selected_db, 1, "db should be 1 after select"); + done(new Error("the old tests were crap")); + }); + client.select(1); + + setTimeout(function () { + done(); + }, 45); + }); + }); + + // Can't seem to catch the errors thrown here. + xdescribe("with an invalid db index", function () { + it("emits an error", function (done) { + this.timeout(50); + + assert.strictEqual(client.selected_db, null, "default db should be null"); + client.on("error", function (err) { + console.log('got an error', err); + done(); + }); + + try { + client.select(9999); + } catch (err) {} + + setTimeout(function () { + done(new Error("It was supposed to emit an error.")); + }, 45); + }); + + it("throws an error bc a callback is not", function (done) { + assert.strictEqual(client.selected_db, null, "default db should be null"); + try { + client.select(9999); + done(new Error("Was supposed to throw an invalid db index error.")); + } catch (err) { + done(); + } + }); + }); + }); + }); + }); + } + + ['javascript', 'hiredis'].forEach(function (parser) { + //allTests(parser, "/tmp/redis.sock", true); + //['IPv4', 'IPv6'].forEach(function (ip) { + ['IPv4'].forEach(function (ip) { + allTests(parser, ip); + }) + }); +}); diff --git a/test/test.js b/test/test.js index fec760923d..9142a144ca 100644 --- a/test/test.js +++ b/test/test.js @@ -117,6 +117,7 @@ next = function next(name) { // Tests are run in the order they are defined, so FLUSHDB should always be first. +<<<<<<< HEAD tests.IPV4 = function () { var ipv4addr = process.env.REDIS_PORT_6379_TCP_ADDR || "127.0.0.1"; var ipv4Client = redis.createClient( PORT, ipv4addr, { family : "IPv4", parser: parser } ); @@ -937,51 +938,6 @@ tests.socket_nodelay = function () { c3.on("ready", ready_check); }; -tests.reconnect = function () { - var name = "reconnect"; - - client.set("recon 1", "one"); - client.set("recon 2", "two", function (err, res) { - // Do not do this in normal programs. This is to simulate the server closing on us. - // For orderly shutdown in normal programs, do client.quit() - client.stream.destroy(); - }); - - client.on("reconnecting", function on_recon(params) { - client.on("connect", function on_connect() { - client.select(test_db_num, require_string("OK", name)); - client.get("recon 1", require_string("one", name)); - client.get("recon 1", require_string("one", name)); - client.get("recon 2", require_string("two", name)); - client.get("recon 2", require_string("two", name)); - client.removeListener("connect", on_connect); - client.removeListener("reconnecting", on_recon); - next(name); - }); - }); -}; - -tests.reconnect_select_db_after_pubsub = function() { - var name = "reconnect_select_db_after_pubsub"; - - client.select(test_db_num); - client.set(name, "one"); - client.subscribe('ChannelV', function (err, res) { - client.stream.destroy(); - }); - - client.on("reconnecting", function on_recon(params) { - client.on("ready", function on_connect() { - client.unsubscribe('ChannelV', function (err, res) { - client.get(name, require_string("one", name)); - client.removeListener("connect", on_connect); - client.removeListener("reconnecting", on_recon); - next(name); - }); - }); - }); -}; - tests.select_error_emits_if_no_callback = function () { var prev = client.listeners("error")[0]; client.removeListener("error", prev); From 5da083322df84d5078467d32b0c055639ee74621 Mon Sep 17 00:00:00 2001 From: Benjamin Coe Date: Mon, 13 Jul 2015 23:00:37 -0700 Subject: [PATCH 37/49] fixed up some tests, changed how redis spawns --- .travis.yml | 5 +- package.json | 4 +- run-bootstrapped-mocha.js | 28 --------- test/conf/redis.conf | 3 +- test/lib/redis-process.js | 49 ++++++++++----- test/mocha/connecting.spec.js | 26 ++++++-- test/mocha/select.spec.js | 112 ++++++++++++++++------------------ 7 files changed, 110 insertions(+), 117 deletions(-) delete mode 100644 run-bootstrapped-mocha.js diff --git a/.travis.yml b/.travis.yml index e7ed30fd79..4884f204e1 100644 --- a/.travis.yml +++ b/.travis.yml @@ -4,8 +4,5 @@ node_js: - "0.10" - "0.12" - "iojs" -before_install: - - 'printf ''bind ::1 127.0.0.1\nunixsocket /tmp/redis.sock\ndaemonize yes\nunixsocketperm 777'' >> /tmp/redis.conf' -before_script: - - sudo redis-server /tmp/redis.conf +script: npm run mocha after_success: npm run coverage diff --git a/package.json b/package.json index b5118d85de..7f9b320b78 100644 --- a/package.json +++ b/package.json @@ -11,7 +11,8 @@ "main": "./index.js", "scripts": { "coverage": "nyc report --reporter=text-lcov | coveralls", - "test": "nyc ./test/run.sh" + "test": "nyc ./test/run.sh", + "mocha": "nyc mocha ./test/mocha/*.js" }, "devDependencies": { "async": "^1.3.0", @@ -21,6 +22,7 @@ "metrics": ">=0.1.5", "mocha": "^2.2.5", "nyc": "^3.0.0", + "tcp-port-used": "^0.1.2", "underscore": "~1.4.4" }, "repository": { diff --git a/run-bootstrapped-mocha.js b/run-bootstrapped-mocha.js deleted file mode 100644 index 716b7c0708..0000000000 --- a/run-bootstrapped-mocha.js +++ /dev/null @@ -1,28 +0,0 @@ -var pm = require('./test/lib/redis-process'); -var cp = require('child_process'); -var testSets = 'test/mocha/**/*.spec.js'; -var async = require('async'); -var redis; - -process.on("exit", function () { - if (redis) { - redis.stop(); - } -}); - -async.series([function startRedis(next) { - redis = pm.start(function (err) { - next(err); - }); -}, function runMocha(next) { - var mocha = cp.spawn('mocha', [testSets], { - stdio: "inherit" - }); - mocha.on("exit", function (code) { - next(); - }); -}, function stopRedis(next) { - redis.stop(next); -}], function (err) { - // done; -}); diff --git a/test/conf/redis.conf b/test/conf/redis.conf index 1e900d9787..dd9a4b6572 100644 --- a/test/conf/redis.conf +++ b/test/conf/redis.conf @@ -1,5 +1,4 @@ -daemonize yes port 6378 -bind ::1 +bind ::1 127.0.0.1 unixsocket /tmp/redis.sock unixsocketperm 755 diff --git a/test/lib/redis-process.js b/test/lib/redis-process.js index a77d20bcc8..b1141f7f10 100644 --- a/test/lib/redis-process.js +++ b/test/lib/redis-process.js @@ -1,23 +1,40 @@ var cp = require('child_process'); +var config = require('./config'); +var path = require('path'); +var tcpPortUsed = require('tcp-port-used'); module.exports = { - start: function (done, isSocket) { - var confFile = isSocket ? "test/conf/redis-socket.conf" : "test/conf/redis.conf"; - var redis = cp.spawn("redis-server", [confFile]); + start: function (done) { + // spawn redis with our testing configuration. + var confFile = path.resolve(__dirname, '../conf/redis.conf'); + var rp = cp.spawn("redis-server", [confFile], {}); - redis.once('err', done); - setTimeout(function (data) { - redis.removeListener('err', done); - done(); - }, 1000); + // wait for redis to become available, by + // checking the port we bind on. + var id = setInterval(function () { + tcpPortUsed.check(config.PORT, '127.0.0.1') + .then(function (inUse) { + if (inUse) { + clearInterval(id); - return { - stop: function (done) { - redis.once("exit", function () { - done(); - }); - redis.kill("SIGINT"); - } - }; + // return an object that can be used in + // an after() block to shutdown redis. + return done(null, { + stop: function (done) { + rp.once("exit", function (code) { + var error = null; + if (code !== 0) error = Error('failed to shutdown redis'); + return done(error); + }); + rp.kill("SIGINT"); + } + }); + } + }) + .catch(function (err) { + clearInterval(id); + return done(err); + }) + }, 100); } }; diff --git a/test/mocha/connecting.spec.js b/test/mocha/connecting.spec.js index dd46f56f59..94ce924679 100644 --- a/test/mocha/connecting.spec.js +++ b/test/mocha/connecting.spec.js @@ -1,11 +1,21 @@ -var nodeAssert = require("../lib/nodeify-assertions"); -var config = require("../lib/config"); -var redis = config.redis; var async = require("async"); +var config = require("../lib/config"); +var nodeAssert = require("../lib/nodeify-assertions"); +var redis = config.redis; +var RedisProcess = require("../lib/redis-process"); describe("A node_redis client", function () { - function allTests(parser, ip, isSocket) { - var args = config.configureClient(parser, ip, isSocket); + + var rp; + before(function (done) { + RedisProcess.start(function (err, _rp) { + rp = _rp; + return done(err); + }); + }) + + function allTests(parser, ip) { + var args = config.configureClient(parser, ip); describe("using " + parser + " and " + ip, function () { var client; @@ -157,9 +167,13 @@ describe("A node_redis client", function () { } ['javascript', 'hiredis'].forEach(function (parser) { - allTests(parser, "/tmp/redis.sock", true); + allTests(parser, "/tmp/redis.sock"); ['IPv4', 'IPv6'].forEach(function (ip) { allTests(parser, ip); }) }); + + after(function (done) { + if (rp) rp.stop(done); + }) }); diff --git a/test/mocha/select.spec.js b/test/mocha/select.spec.js index 418ca4439f..18fb34c642 100644 --- a/test/mocha/select.spec.js +++ b/test/mocha/select.spec.js @@ -1,12 +1,28 @@ -var nodeAssert = require("../lib/nodeify-assertions"); +var async = require('async'); +var assert = require('assert'); var config = require("../lib/config"); +var nodeAssert = require('../lib/nodeify-assertions'); var redis = config.redis; -var async = require("async"); -var assert = require("assert"); +var RedisProcess = require("../lib/redis-process"); describe("The 'select' method", function () { - function allTests(parser, ip, isSocket) { - var args = config.configureClient(parser, ip, isSocket); + + var rp; + before(function (done) { + RedisProcess.start(function (err, _rp) { + rp = _rp; + return done(err); + }); + }) + + function removeMochaListener () { + var mochaListener = process.listeners('uncaughtException').pop(); + process.removeListener('uncaughtException', mochaListener); + return mochaListener; + } + + function allTests(parser, ip) { + var args = config.configureClient(parser, ip); describe("using " + parser + " and " + ip, function () { describe("when not connected", function () { @@ -15,26 +31,19 @@ describe("The 'select' method", function () { beforeEach(function (done) { client = redis.createClient.apply(redis.createClient, args); client.once("error", done); - client.once("connect", function () { - client.set("doot", "good calsum", function (err, res) { - client.end(); - done(); - }); + client.quit(); + }); + client.on('end', function () { + return done(); }); }); - it("doesn't even throw an error or call the callback at all WTF", function (done) { - this.timeout(50); - + it("throws an error if redis is not connected", function (done) { client.select(1, function (err, res) { - nodeAssert.isNotError()(err, res); + assert.equal(err.message, 'Redis connection gone from end event.'); done(); }); - - setTimeout(function () { - done(); - }, 45); }); }); @@ -44,10 +53,7 @@ describe("The 'select' method", function () { beforeEach(function (done) { client = redis.createClient.apply(redis.createClient, args); client.once("error", done); - - client.once("connect", function () { - done(); - }); + client.once("connect", function () { done(); }); }); afterEach(function () { @@ -65,54 +71,37 @@ describe("The 'select' method", function () { }); describe("and no callback is specified", function () { - // select_error_emits_if_no_callback - // this is another test that was testing the wrong thing. The old test did indeed emit an error, - // but not because of the lacking callback, but because 9999 was an invalid db index. describe("with a valid db index", function () { - it("works just fine and does not actually emit an error like the old tests assert WTF", function (done) { + it("selects the appropriate database", function (done) { assert.strictEqual(client.selected_db, null, "default db should be null"); - this.timeout(50); - client.on("error", function (err) { - nodeAssert.isNotError()(err); - assert.strictEqual(client.selected_db, 1, "db should be 1 after select"); - done(new Error("the old tests were crap")); - }); client.select(1); - setTimeout(function () { - done(); - }, 45); + assert.equal(client.selected_db, 1, "we should have selected the new valid DB"); + return done(); + }, 100); }); }); - // Can't seem to catch the errors thrown here. - xdescribe("with an invalid db index", function () { + describe("with an invalid db index", function () { it("emits an error", function (done) { - this.timeout(50); - assert.strictEqual(client.selected_db, null, "default db should be null"); - client.on("error", function (err) { - console.log('got an error', err); - done(); + client.select(9999, function (err) { + assert.equal(err.message, 'ERR invalid DB index') + return done(); }); - - try { - client.select(9999); - } catch (err) {} - - setTimeout(function () { - done(new Error("It was supposed to emit an error.")); - }, 45); }); - it("throws an error bc a callback is not", function (done) { + it("throws an error when callback not provided", function (done) { + var mochaListener = removeMochaListener(); assert.strictEqual(client.selected_db, null, "default db should be null"); - try { - client.select(9999); - done(new Error("Was supposed to throw an invalid db index error.")); - } catch (err) { - done(); - } + + process.once('uncaughtException', function (err) { + process.on('uncaughtException', mochaListener); + assert.equal(err.message, 'ERR invalid DB index'); + return done(); + }); + + client.select(9999); }); }); }); @@ -121,10 +110,13 @@ describe("The 'select' method", function () { } ['javascript', 'hiredis'].forEach(function (parser) { - //allTests(parser, "/tmp/redis.sock", true); - //['IPv4', 'IPv6'].forEach(function (ip) { - ['IPv4'].forEach(function (ip) { + allTests(parser, "/tmp/redis.sock"); + ['IPv4', 'IPv6'].forEach(function (ip) { allTests(parser, ip); }) }); + + after(function (done) { + if (rp) rp.stop(done); + }); }); From 3aaef4775608a9213fdc83df612511bbbe345d59 Mon Sep 17 00:00:00 2001 From: Erin Spiceland Date: Tue, 14 Jul 2015 09:45:48 -0500 Subject: [PATCH 38/49] Fix bug in mocha tests Redis shutdown which expected exit code to eq 0. Move a miscategorized select test into the correct describe. --- test/lib/redis-process.js | 4 ++- test/mocha/select.spec.js | 54 ++++++++++++++++++++++++--------------- test/test.js | 13 ---------- 3 files changed, 37 insertions(+), 34 deletions(-) diff --git a/test/lib/redis-process.js b/test/lib/redis-process.js index b1141f7f10..24d7f04f60 100644 --- a/test/lib/redis-process.js +++ b/test/lib/redis-process.js @@ -23,7 +23,9 @@ module.exports = { stop: function (done) { rp.once("exit", function (code) { var error = null; - if (code !== 0) error = Error('failed to shutdown redis'); + if (code !== null && code !== 0) { + error = Error('Redis shutdown failed with code ' + code); + } return done(error); }); rp.kill("SIGINT"); diff --git a/test/mocha/select.spec.js b/test/mocha/select.spec.js index 18fb34c642..bde75857f6 100644 --- a/test/mocha/select.spec.js +++ b/test/mocha/select.spec.js @@ -9,16 +9,16 @@ describe("The 'select' method", function () { var rp; before(function (done) { - RedisProcess.start(function (err, _rp) { - rp = _rp; - return done(err); - }); + RedisProcess.start(function (err, _rp) { + rp = _rp; + return done(err); + }); }) function removeMochaListener () { - var mochaListener = process.listeners('uncaughtException').pop(); - process.removeListener('uncaughtException', mochaListener); - return mochaListener; + var mochaListener = process.listeners('uncaughtException').pop(); + process.removeListener('uncaughtException', mochaListener); + return mochaListener; } function allTests(parser, ip) { @@ -32,10 +32,10 @@ describe("The 'select' method", function () { client = redis.createClient.apply(redis.createClient, args); client.once("error", done); client.once("connect", function () { - client.quit(); + client.quit(); }); client.on('end', function () { - return done(); + return done(); }); }); @@ -70,6 +70,28 @@ describe("The 'select' method", function () { }); }); + describe("and a callback is specified", function () { + describe("with a valid db index", function () { + it("selects the appropriate database", function (done) { + assert.strictEqual(client.selected_db, null, "default db should be null"); + client.select(1, function () { + assert.equal(client.selected_db, 1, "we should have selected the new valid DB"); + return done(); + }); + }); + }); + + describe("with an invalid db index", function () { + it("emits an error", function (done) { + assert.strictEqual(client.selected_db, null, "default db should be null"); + client.select(9999, function (err) { + assert.equal(err.message, 'ERR invalid DB index') + return done(); + }); + }); + }); + }); + describe("and no callback is specified", function () { describe("with a valid db index", function () { it("selects the appropriate database", function (done) { @@ -83,22 +105,14 @@ describe("The 'select' method", function () { }); describe("with an invalid db index", function () { - it("emits an error", function (done) { - assert.strictEqual(client.selected_db, null, "default db should be null"); - client.select(9999, function (err) { - assert.equal(err.message, 'ERR invalid DB index') - return done(); - }); - }); - it("throws an error when callback not provided", function (done) { var mochaListener = removeMochaListener(); assert.strictEqual(client.selected_db, null, "default db should be null"); process.once('uncaughtException', function (err) { - process.on('uncaughtException', mochaListener); - assert.equal(err.message, 'ERR invalid DB index'); - return done(); + process.on('uncaughtException', mochaListener); + assert.equal(err.message, 'ERR invalid DB index'); + return done(); }); client.select(9999); diff --git a/test/test.js b/test/test.js index 9142a144ca..6021fcdfa9 100644 --- a/test/test.js +++ b/test/test.js @@ -938,19 +938,6 @@ tests.socket_nodelay = function () { c3.on("ready", ready_check); }; -tests.select_error_emits_if_no_callback = function () { - var prev = client.listeners("error")[0]; - client.removeListener("error", prev); - var name = "select_error_emits_if_no_callback"; - var handler = with_timeout(name, function (err) { - require_error(name)(err); - client.removeListener('error', handler); - client.on("error", prev); - next(name); - }, 500); - client.on('error', handler); - client.select(9999); -}; tests.idle = function () { var name = "idle"; From 1e9613fa8f692e9f46385cb7b8b67245570ed478 Mon Sep 17 00:00:00 2001 From: Benjamin Coe Date: Tue, 14 Jul 2015 08:39:33 -0700 Subject: [PATCH 39/49] slight tweak to test command --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 7f9b320b78..145f167c2a 100644 --- a/package.json +++ b/package.json @@ -12,7 +12,7 @@ "scripts": { "coverage": "nyc report --reporter=text-lcov | coveralls", "test": "nyc ./test/run.sh", - "mocha": "nyc mocha ./test/mocha/*.js" + "mocha": "nyc ./node_modules/.bin/_mocha ./test/mocha/*.js" }, "devDependencies": { "async": "^1.3.0", From eaca486ab1aecd1329f7452ad2f2255b1263606f Mon Sep 17 00:00:00 2001 From: Erin Spiceland Date: Tue, 14 Jul 2015 11:00:41 -0500 Subject: [PATCH 40/49] Add Mocha tests for the "set" method. Fix error in organization of connection Mocha tests. Clarify some test descriptions in 'set' Mocha tests. Add some tests for mset. Remove old 'set' tests. Add some Mocha tests for 'get'. Add tests for 'getset'. Add tests for 'dbsize'. Add 'flushdb' tests. Add tests for 'incr'. --- package.json | 3 + test/mocha/connecting.spec.js | 205 +++++++++++++++++----------------- test/mocha/dbsize.spec.js | 125 +++++++++++++++++++++ test/mocha/flushdb.spec.js | 134 ++++++++++++++++++++++ test/mocha/get.spec.js | 111 ++++++++++++++++++ test/mocha/getset.spec.js | 115 +++++++++++++++++++ test/mocha/incr.spec.js | 139 +++++++++++++++++++++++ test/mocha/mset.spec.js | 181 ++++++++++++++++++++++++++++++ test/mocha/set.spec.js | 178 +++++++++++++++++++++++++++++ test/test.js | 34 +----- 10 files changed, 1092 insertions(+), 133 deletions(-) create mode 100644 test/mocha/dbsize.spec.js create mode 100644 test/mocha/flushdb.spec.js create mode 100644 test/mocha/get.spec.js create mode 100644 test/mocha/getset.spec.js create mode 100644 test/mocha/incr.spec.js create mode 100644 test/mocha/mset.spec.js create mode 100644 test/mocha/set.spec.js diff --git a/package.json b/package.json index 145f167c2a..0563794501 100644 --- a/package.json +++ b/package.json @@ -28,5 +28,8 @@ "repository": { "type": "git", "url": "git://github.com/mranney/node_redis.git" + }, + "dependencies": { + "uuid": "^2.0.1" } } diff --git a/test/mocha/connecting.spec.js b/test/mocha/connecting.spec.js index 94ce924679..2319c2173d 100644 --- a/test/mocha/connecting.spec.js +++ b/test/mocha/connecting.spec.js @@ -3,6 +3,7 @@ var config = require("../lib/config"); var nodeAssert = require("../lib/nodeify-assertions"); var redis = config.redis; var RedisProcess = require("../lib/redis-process"); +var uuid = require("uuid"); describe("A node_redis client", function () { @@ -20,90 +21,48 @@ describe("A node_redis client", function () { describe("using " + parser + " and " + ip, function () { var client; - after(function () { - client.end(); - }); - - it("connects correctly", 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); - }); + describe("when not connected", function () { + afterEach(function () { + client.end(); }); - }); - }); - describe("when connected", function () { - var client; + it("connects correctly", function (done) { + client = redis.createClient.apply(redis.createClient, args); + client.on("error", done); - beforeEach(function (done) { - client = redis.createClient.apply(redis.createClient, args); - client.once("error", function onError(err) { - done(err); - }); - client.once("ready", function onReady() { - done(); - }); - }); - - afterEach(function () { - client.end(); - }); - - describe("when redis closes unexpectedly", function () { - it("reconnects and can retrieve the pre-existing data", function (done) { - client.on("reconnecting", function on_recon(params) { - client.on("connect", function on_connect() { - async.parallel([function (cb) { - client.get("recon 1", function (err, res) { - nodeAssert.isString("one")(err, res); - cb(); - }); - }, function (cb) { - client.get("recon 1", function (err, res) { - nodeAssert.isString("one")(err, res); - cb(); - }); - }, function (cb) { - client.get("recon 2", function (err, res) { - nodeAssert.isString("two")(err, res); - cb(); - }); - }, function (cb) { - client.get("recon 2", function (err, res) { - nodeAssert.isString("two")(err, res); - cb(); - }); - }], function (err, results) { - client.removeListener("connect", on_connect); - client.removeListener("reconnecting", on_recon); - done(err); - }); + client.once("ready", function () { + client.removeListener("error", done); + client.get("recon 1", function (err, res) { + done(err); }); }); + }); + }); - client.set("recon 1", "one"); - client.set("recon 2", "two", function (err, res) { - // Do not do this in normal programs. This is to simulate the server closing on us. - // For orderly shutdown in normal programs, do client.quit() - client.stream.destroy(); + describe("when connected", function () { + var client; + + beforeEach(function (done) { + client = redis.createClient.apply(redis.createClient, args); + client.once("error", function onError(err) { + done(err); + }); + client.once("ready", function onReady() { + done(); }); }); - describe("and it's subscribed to a channel", function () { - // reconnect_select_db_after_pubsub - // Does not pass. - // "Connection in subscriber mode, only subscriber commands may be used" - xit("reconnects, unsubscribes, and can retrieve the pre-existing data", function (done) { + afterEach(function () { + client.end(); + }); + + describe("when redis closes unexpectedly", function () { + it("reconnects and can retrieve the pre-existing data", function (done) { client.on("reconnecting", function on_recon(params) { - client.on("ready", function on_connect() { + client.on("connect", function on_connect() { async.parallel([function (cb) { - client.unsubscribe("recon channel", function (err, res) { - nodeAssert.isNotError()(err, res); + client.get("recon 1", function (err, res) { + nodeAssert.isString("one")(err, res); cb(); }); }, function (cb) { @@ -111,6 +70,16 @@ describe("A node_redis client", function () { nodeAssert.isString("one")(err, res); cb(); }); + }, function (cb) { + client.get("recon 2", function (err, res) { + nodeAssert.isString("two")(err, res); + cb(); + }); + }, function (cb) { + client.get("recon 2", function (err, res) { + nodeAssert.isString("two")(err, res); + cb(); + }); }], function (err, results) { client.removeListener("connect", on_connect); client.removeListener("reconnecting", on_recon); @@ -120,45 +89,79 @@ describe("A node_redis client", function () { }); client.set("recon 1", "one"); - client.subscribe("recon channel", function (err, res) { + client.set("recon 2", "two", function (err, res) { // Do not do this in normal programs. This is to simulate the server closing on us. // For orderly shutdown in normal programs, do client.quit() client.stream.destroy(); }); }); - it("remains subscribed", function () { - var client2 = redis.createClient.apply(redis.createClient, args); - - client.on("reconnecting", function on_recon(params) { - client.on("ready", function on_connect() { - async.parallel([function (cb) { - client.on("message", function (channel, message) { - try { - nodeAssert.isString("recon channel")(null, channel); - nodeAssert.isString("a test message")(null, message); - } catch (err) { - cb(err); - } + describe("and it's subscribed to a channel", function () { + // reconnect_select_db_after_pubsub + // Does not pass. + // "Connection in subscriber mode, only subscriber commands may be used" + xit("reconnects, unsubscribes, and can retrieve the pre-existing data", function (done) { + client.on("reconnecting", function on_recon(params) { + client.on("ready", function on_connect() { + async.parallel([function (cb) { + client.unsubscribe("recon channel", function (err, res) { + nodeAssert.isNotError()(err, res); + cb(); + }); + }, function (cb) { + client.get("recon 1", function (err, res) { + nodeAssert.isString("one")(err, res); + cb(); + }); + }], function (err, results) { + client.removeListener("connect", on_connect); + client.removeListener("reconnecting", on_recon); + done(err); }); - - client2.subscribe("recon channel", function (err, res) { - if (err) { - cb(err); - return; - } - client2.publish("recon channel", "a test message"); - }); - }], function (err, results) { - done(err); }); }); + + client.set("recon 1", "one"); + client.subscribe("recon channel", function (err, res) { + // Do not do this in normal programs. This is to simulate the server closing on us. + // For orderly shutdown in normal programs, do client.quit() + client.stream.destroy(); + }); }); - client.subscribe("recon channel", function (err, res) { - // Do not do this in normal programs. This is to simulate the server closing on us. - // For orderly shutdown in normal programs, do client.quit() - client.stream.destroy(); + it("remains subscribed", function () { + var client2 = redis.createClient.apply(redis.createClient, args); + + client.on("reconnecting", function on_recon(params) { + client.on("ready", function on_connect() { + async.parallel([function (cb) { + client.on("message", function (channel, message) { + try { + nodeAssert.isString("recon channel")(null, channel); + nodeAssert.isString("a test message")(null, message); + } catch (err) { + cb(err); + } + }); + + client2.subscribe("recon channel", function (err, res) { + if (err) { + cb(err); + return; + } + client2.publish("recon channel", "a test message"); + }); + }], function (err, results) { + done(err); + }); + }); + }); + + client.subscribe("recon channel", function (err, res) { + // Do not do this in normal programs. This is to simulate the server closing on us. + // For orderly shutdown in normal programs, do client.quit() + client.stream.destroy(); + }); }); }); }); diff --git a/test/mocha/dbsize.spec.js b/test/mocha/dbsize.spec.js new file mode 100644 index 0000000000..aa019c57cb --- /dev/null +++ b/test/mocha/dbsize.spec.js @@ -0,0 +1,125 @@ +var async = require('async'); +var assert = require('assert'); +var config = require("../lib/config"); +var nodeAssert = require('../lib/nodeify-assertions'); +var redis = config.redis; +var RedisProcess = require("../lib/redis-process"); +var uuid = require('uuid'); + +describe("The 'dbsize' method", function () { + + var rp; + before(function (done) { + RedisProcess.start(function (err, _rp) { + rp = _rp; + return done(err); + }); + }) + + function removeMochaListener () { + var mochaListener = process.listeners('uncaughtException').pop(); + process.removeListener('uncaughtException', mochaListener); + return mochaListener; + } + + function allTests(parser, ip) { + var args = config.configureClient(parser, ip); + + describe("using " + parser + " and " + ip, function () { + var key, value; + + beforeEach(function () { + key = uuid.v4(); + value = uuid.v4(); + }); + + describe("when not connected", function () { + var client; + + beforeEach(function (done) { + client = redis.createClient.apply(redis.createClient, args); + client.once("error", done); + client.once("connect", function () { + client.quit(); + }); + client.on('end', function () { + return done(); + }); + }); + + it("reports an error", function (done) { + client.dbsize([], function (err, res) { + assert.equal(err.message, 'Redis connection gone from end event.'); + done(); + }); + }); + }); + + describe("when connected", function () { + var client; + + beforeEach(function (done) { + client = redis.createClient.apply(redis.createClient, args); + client.once("error", done); + client.once("connect", function () { + client.flushdb(function (err, res) { + nodeAssert.isString("OK")(err, res); + done(); + }); + }); + }); + + afterEach(function () { + client.end(); + }); + + it("returns a zero db size", function (done) { + client.dbsize([], function (err, res) { + nodeAssert.isNotError()(err, res); + nodeAssert.isType.number()(err, res); + assert.strictEqual(res, 0, "Initial db size should be 0"); + done(); + }); + }); + + describe("when more data is added to Redis", function () { + var oldSize; + + beforeEach(function (done) { + client.dbsize([], function (err, res) { + nodeAssert.isType.number()(err, res); + assert.strictEqual(res, 0, "Initial db size should be 0"); + + oldSize = res; + + client.set(key, value, function (err, res) { + nodeAssert.isNotError()(err, res); + done(); + }); + }); + }); + + it("returns a larger db size", function (done) { + client.dbsize([], function (err, res) { + nodeAssert.isNotError()(err, res); + nodeAssert.isType.positiveNumber()(err, res); + assert.strictEqual(true, (oldSize < res), "Adding data should increase db size."); + done(); + }); + }); + }); + }); + }); + } + + ['javascript', 'hiredis'].forEach(function (parser) { + allTests(parser, "/tmp/redis.sock"); + ['IPv4', 'IPv6'].forEach(function (ip) { + allTests(parser, ip); + }) + }); + + after(function (done) { + if (rp) rp.stop(done); + }); +}); diff --git a/test/mocha/flushdb.spec.js b/test/mocha/flushdb.spec.js new file mode 100644 index 0000000000..693ff69ab5 --- /dev/null +++ b/test/mocha/flushdb.spec.js @@ -0,0 +1,134 @@ +var async = require('async'); +var assert = require('assert'); +var config = require("../lib/config"); +var nodeAssert = require('../lib/nodeify-assertions'); +var redis = config.redis; +var RedisProcess = require("../lib/redis-process"); +var uuid = require('uuid'); + +describe("The 'flushdb' method", function () { + + var rp; + before(function (done) { + RedisProcess.start(function (err, _rp) { + rp = _rp; + return done(err); + }); + }) + + function removeMochaListener () { + var mochaListener = process.listeners('uncaughtException').pop(); + process.removeListener('uncaughtException', mochaListener); + return mochaListener; + } + + function allTests(parser, ip) { + var args = config.configureClient(parser, ip); + + describe("using " + parser + " and " + ip, function () { + var key, key2; + + beforeEach(function () { + key = uuid.v4(); + key2 = uuid.v4(); + }); + + describe("when not connected", function () { + var client; + + beforeEach(function (done) { + client = redis.createClient.apply(redis.createClient, args); + client.once("error", done); + client.once("connect", function () { + client.quit(); + }); + client.on('end', function () { + return done(); + }); + }); + + it("reports an error", function (done) { + client.flushdb(function (err, res) { + assert.equal(err.message, 'Redis connection gone from end event.'); + done(); + }); + }); + }); + + describe("when connected", function () { + var client; + + beforeEach(function (done) { + client = redis.createClient.apply(redis.createClient, args); + client.once("error", done); + client.once("connect", function () { + done(); + }); + }); + + afterEach(function () { + client.end(); + }); + + describe("when there is data in Redis", function () { + var oldSize; + + beforeEach(function (done) { + async.parallel([function (next) { + client.mset(key, uuid.v4(), key2, uuid.v4(), function (err, res) { + nodeAssert.isNotError()(err, res); + next(err); + }); + }, function (next) { + client.dbsize([], function (err, res) { + nodeAssert.isType.positiveNumber()(err, res); + oldSize = res; + next(err); + }); + }], function (err) { + if (err) { + return done(err); + } + + client.flushdb(function (err, res) { + nodeAssert.isString("OK")(err, res); + done(err); + }); + }); + }); + + it("deletes all the keys", function (done) { + client.mget(key, key2, function (err, res) { + assert.strictEqual(null, err, "Unexpected error returned"); + assert.strictEqual(true, Array.isArray(res), "Results object should be an array."); + assert.strictEqual(2, res.length, "Results array should have length 2."); + assert.strictEqual(null, res[0], "Redis key should have been flushed."); + assert.strictEqual(null, res[1], "Redis key should have been flushed."); + done(err); + }); + }); + + it("results in a db size of zero", function (done) { + client.dbsize([], function (err, res) { + nodeAssert.isNotError()(err, res); + nodeAssert.isType.number()(err, res); + assert.strictEqual(0, res, "Flushing db should result in db size 0"); + done(); + }); + }); + }); + }); + }); + } + + ['javascript', 'hiredis'].forEach(function (parser) { + allTests(parser, "/tmp/redis.sock"); + ['IPv4', 'IPv6'].forEach(function (ip) { + allTests(parser, ip); + }) + }); + + after(function (done) { + if (rp) rp.stop(done); + }); +}); diff --git a/test/mocha/get.spec.js b/test/mocha/get.spec.js new file mode 100644 index 0000000000..6c533f6a81 --- /dev/null +++ b/test/mocha/get.spec.js @@ -0,0 +1,111 @@ +var async = require('async'); +var assert = require('assert'); +var config = require("../lib/config"); +var nodeAssert = require('../lib/nodeify-assertions'); +var redis = config.redis; +var RedisProcess = require("../lib/redis-process"); +var uuid = require('uuid'); + +describe("The 'get' method", function () { + + var rp; + before(function (done) { + RedisProcess.start(function (err, _rp) { + rp = _rp; + return done(err); + }); + }) + + function removeMochaListener () { + var mochaListener = process.listeners('uncaughtException').pop(); + process.removeListener('uncaughtException', mochaListener); + return mochaListener; + } + + function allTests(parser, ip) { + var args = config.configureClient(parser, ip); + + describe("using " + parser + " and " + ip, function () { + var key, value; + + beforeEach(function () { + key = uuid.v4(); + value = uuid.v4(); + }); + + describe("when not connected", function () { + var client; + + beforeEach(function (done) { + client = redis.createClient.apply(redis.createClient, args); + client.once("error", done); + client.once("connect", function () { + client.quit(); + }); + client.on('end', function () { + return done(); + }); + }); + + it("reports an error", function (done) { + client.get(key, function (err, res) { + assert.equal(err.message, 'Redis connection gone from end event.'); + done(); + }); + }); + }); + + describe("when connected", function () { + var client; + + beforeEach(function (done) { + client = redis.createClient.apply(redis.createClient, args); + client.once("error", done); + client.once("connect", function () { + done(); + }); + }); + + afterEach(function () { + client.end(); + }); + + describe("when the key exists in Redis", function () { + beforeEach(function (done) { + client.set(key, value, function (err, res) { + nodeAssert.isNotError()(err, res); + done(); + }); + }); + + it("gets the value correctly", function (done) { + client.get(key, function (err, res) { + nodeAssert.isString(value)(err, res); + done(err); + }); + }); + }); + + describe("when the key does not exist in Redis", function () { + it("gets a null value", function (done) { + client.get(key, function (err, res) { + nodeAssert.isNull()(err, res); + done(err); + }); + }); + }); + }); + }); + } + + ['javascript', 'hiredis'].forEach(function (parser) { + allTests(parser, "/tmp/redis.sock"); + ['IPv4', 'IPv6'].forEach(function (ip) { + allTests(parser, ip); + }) + }); + + after(function (done) { + if (rp) rp.stop(done); + }); +}); diff --git a/test/mocha/getset.spec.js b/test/mocha/getset.spec.js new file mode 100644 index 0000000000..847aa1e462 --- /dev/null +++ b/test/mocha/getset.spec.js @@ -0,0 +1,115 @@ +var async = require('async'); +var assert = require('assert'); +var config = require("../lib/config"); +var nodeAssert = require('../lib/nodeify-assertions'); +var redis = config.redis; +var RedisProcess = require("../lib/redis-process"); +var uuid = require('uuid'); + +describe("The 'getset' method", function () { + + var rp; + before(function (done) { + RedisProcess.start(function (err, _rp) { + rp = _rp; + return done(err); + }); + }) + + function removeMochaListener () { + var mochaListener = process.listeners('uncaughtException').pop(); + process.removeListener('uncaughtException', mochaListener); + return mochaListener; + } + + function allTests(parser, ip) { + var args = config.configureClient(parser, ip); + + describe("using " + parser + " and " + ip, function () { + var key, value, value2; + + beforeEach(function () { + key = uuid.v4(); + value = uuid.v4(); + value2 = uuid.v4(); + }); + + describe("when not connected", function () { + var client; + + beforeEach(function (done) { + client = redis.createClient.apply(redis.createClient, args); + client.once("error", done); + client.once("connect", function () { + client.quit(); + }); + client.on('end', function () { + return done(); + }); + }); + + it("reports an error", function (done) { + client.get(key, function (err, res) { + assert.equal(err.message, 'Redis connection gone from end event.'); + done(); + }); + }); + }); + + describe("when connected", function () { + var client; + + beforeEach(function (done) { + client = redis.createClient.apply(redis.createClient, args); + client.once("error", done); + client.once("connect", function () { + done(); + }); + }); + + afterEach(function () { + client.end(); + }); + + describe("when the key exists in Redis", function () { + beforeEach(function (done) { + client.set(key, value, function (err, res) { + nodeAssert.isNotError()(err, res); + done(); + }); + }); + + it("gets the value correctly", function (done) { + client.getset(key, value2, function (err, res) { + nodeAssert.isString(value)(err, res); + client.get(key, function (err, res) { + nodeAssert.isString(value2)(err, res); + done(err); + }); + }); + }); + }); + + describe("when the key does not exist in Redis", function () { + it("gets a null value", function (done) { + client.getset(key, value, function (err, res) { + nodeAssert.isNull()(err, res); + done(err); + }); + }); + }); + }); + }); + } + + ['javascript', 'hiredis'].forEach(function (parser) { + allTests(parser, "/tmp/redis.sock"); + ['IPv4', 'IPv6'].forEach(function (ip) { + allTests(parser, ip); + }) + }); + + after(function (done) { + if (rp) rp.stop(done); + }); +}); diff --git a/test/mocha/incr.spec.js b/test/mocha/incr.spec.js new file mode 100644 index 0000000000..34bacec70e --- /dev/null +++ b/test/mocha/incr.spec.js @@ -0,0 +1,139 @@ +var async = require('async'); +var assert = require('assert'); +var config = require("../lib/config"); +var nodeAssert = require('../lib/nodeify-assertions'); +var redis = config.redis; +var RedisProcess = require("../lib/redis-process"); +var uuid = require('uuid'); + +describe("The 'incr' method", function () { + + var rp; + before(function (done) { + RedisProcess.start(function (err, _rp) { + rp = _rp; + return done(err); + }); + }) + + function removeMochaListener () { + var mochaListener = process.listeners('uncaughtException').pop(); + process.removeListener('uncaughtException', mochaListener); + return mochaListener; + } + + function allTests(parser, ip) { + var args = config.configureClient(parser, ip); + + describe("using " + parser + " and " + ip, function () { + var key = "sequence"; + + describe("when not connected", function () { + var client; + + beforeEach(function (done) { + client = redis.createClient.apply(redis.createClient, args); + client.once("error", done); + client.once("connect", function () { + client.set(key, "9007199254740992", function (err, res) { + nodeAssert.isNotError()(err, res); + client.quit(); + }); + }); + client.on('end', function () { + return done(); + }); + }); + + it("reports an error", function (done) { + client.incr(function (err, res) { + assert.equal(err.message, 'Redis connection gone from end event.'); + done(); + }); + }); + }); + + describe("when connected and a value in Redis", function () { + var client; + + // Also, why tf were these disabled for hiredis? They work just fine. + before(function (done) { + /* + 9007199254740992 -> 9007199254740992 + 9007199254740993 -> 9007199254740992 + 9007199254740994 -> 9007199254740994 + 9007199254740995 -> 9007199254740996 + 9007199254740996 -> 9007199254740996 + 9007199254740997 -> 9007199254740996 + */ + client = redis.createClient.apply(redis.createClient, args); + client.once("error", done); + client.once("connect", function () { + client.set(key, "9007199254740992", function (err, res) { + nodeAssert.isNotError()(err, res); + done(); + }); + }); + }); + + after(function () { + client.end(); + }); + + it("changes the last digit from 2 to 3", function (done) { + client.incr(key, function (err, res) { + nodeAssert.isString("9007199254740993")(err, res); + done(err); + }); + }); + + describe("and we call it again", function () { + it("changes the last digit from 3 to 4", function (done) { + client.incr(key, function (err, res) { + nodeAssert.isString("9007199254740994")(err, res); + done(err); + }); + }); + + describe("and again", function () { + it("changes the last digit from 4 to 5", function (done) { + client.incr(key, function (err, res) { + nodeAssert.isString("9007199254740995")(err, res); + done(err); + }); + }); + + describe("and again", function () { + it("changes the last digit from 5 to 6", function (done) { + client.incr(key, function (err, res) { + nodeAssert.isString("9007199254740996")(err, res); + done(err); + }); + }); + + describe("and again", function () { + it("changes the last digit from 6 to 7", function (done) { + client.incr(key, function (err, res) { + nodeAssert.isString("9007199254740997")(err, res); + done(err); + }); + }); + }); + }); + }); + }); + }); + }); + } + + ['javascript', 'hiredis'].forEach(function (parser) { + allTests(parser, "/tmp/redis.sock"); + ['IPv4', 'IPv6'].forEach(function (ip) { + allTests(parser, ip); + }) + }); + + after(function (done) { + if (rp) rp.stop(done); + }); +}); diff --git a/test/mocha/mset.spec.js b/test/mocha/mset.spec.js new file mode 100644 index 0000000000..90ffb32a74 --- /dev/null +++ b/test/mocha/mset.spec.js @@ -0,0 +1,181 @@ +var async = require('async'); +var assert = require('assert'); +var config = require("../lib/config"); +var nodeAssert = require('../lib/nodeify-assertions'); +var redis = config.redis; +var RedisProcess = require("../lib/redis-process"); +var uuid = require('uuid'); + +describe("The 'mset' method", function () { + + var rp; + before(function (done) { + RedisProcess.start(function (err, _rp) { + rp = _rp; + return done(err); + }); + }) + + function removeMochaListener () { + var mochaListener = process.listeners('uncaughtException').pop(); + process.removeListener('uncaughtException', mochaListener); + return mochaListener; + } + + function allTests(parser, ip) { + var args = config.configureClient(parser, ip); + + describe("using " + parser + " and " + ip, function () { + var key, value, key2, value2; + + beforeEach(function () { + key = uuid.v4(); + value = uuid.v4(); + key2 = uuid.v4(); + value2 = uuid.v4(); + }); + + describe("when not connected", function () { + var client; + + beforeEach(function (done) { + client = redis.createClient.apply(redis.createClient, args); + client.once("error", done); + client.once("connect", function () { + client.quit(); + }); + client.on('end', function () { + return done(); + }); + }); + + it("reports an error", function (done) { + client.mset(key, value, key2, value2, function (err, res) { + assert.equal(err.message, 'Redis connection gone from end event.'); + done(); + }); + }); + }); + + describe("when connected", function () { + var client; + + beforeEach(function (done) { + client = redis.createClient.apply(redis.createClient, args); + client.once("error", done); + client.once("connect", function () { + done(); + }); + }); + + afterEach(function () { + client.end(); + }); + + describe("and a callback is specified", function () { + describe("with valid parameters", function () { + it("sets the value correctly", function (done) { + client.mset(key, value, key2, value2, function (err, res) { + nodeAssert.isNotError()(err, res); + async.parallel([function (next) { + client.get(key, function (err, res) { + nodeAssert.isString(value)(err, res); + next(); + }); + }, function (next) { + client.get(key2, function (err, res) { + nodeAssert.isString(value2)(err, res); + next(); + }); + }], function (err) { + done(err); + }); + }); + }); + }); + + describe("with undefined 'key' parameter and missing 'value' parameter", function () { + it("reports an error", function (done) { + client.mset(undefined, function (err, res) { + nodeAssert.isError()(err, null); + done(); + }); + }); + }); + + describe("with undefined 'key' and defined 'value' parameters", function () { + it("reports an error", function () { + client.mset(undefined, value, undefined, value2, function (err, res) { + nodeAssert.isError()(err, null); + done(); + }); + }); + }); + }); + + describe("and no callback is specified", function () { + describe("with valid parameters", function () { + it("sets the value correctly", function (done) { + client.mset(key, value, key2, value2); + + setTimeout(function () { + async.parallel([function (next) { + client.get(key, function (err, res) { + nodeAssert.isString(value)(err, res); + next(); + }); + }, function (next) { + client.get(key2, function (err, res) { + nodeAssert.isString(value2)(err, res); + next(); + }); + }], function (err) { + done(err); + }); + }, 100); + }); + }); + + describe("with undefined 'key' and missing 'value' parameter", function () { + // this behavior is different from the 'set' behavior. + it("throws an error", function (done) { + var mochaListener = removeMochaListener(); + + process.once('uncaughtException', function (err) { + process.on('uncaughtException', mochaListener); + nodeAssert.isError()(err, null); + return done(); + }); + + client.mset(); + }); + }); + + describe("with undefined 'key' and defined 'value' parameters", function () { + it("throws an error", function () { + var mochaListener = removeMochaListener(); + + process.once('uncaughtException', function (err) { + process.on('uncaughtException', mochaListener); + nodeAssert.isError()(err, null); + }); + + client.mset(undefined, value, undefined, value2); + }); + }); + }); + }); + }); + } + + ['javascript', 'hiredis'].forEach(function (parser) { + allTests(parser, "/tmp/redis.sock"); + ['IPv4', 'IPv6'].forEach(function (ip) { + allTests(parser, ip); + }) + }); + + after(function (done) { + if (rp) rp.stop(done); + }); +}); diff --git a/test/mocha/set.spec.js b/test/mocha/set.spec.js new file mode 100644 index 0000000000..ed5026710e --- /dev/null +++ b/test/mocha/set.spec.js @@ -0,0 +1,178 @@ +var async = require('async'); +var assert = require('assert'); +var config = require("../lib/config"); +var nodeAssert = require('../lib/nodeify-assertions'); +var redis = config.redis; +var RedisProcess = require("../lib/redis-process"); +var uuid = require('uuid'); + +describe("The 'set' method", function () { + + var rp; + before(function (done) { + RedisProcess.start(function (err, _rp) { + rp = _rp; + return done(err); + }); + }) + + function removeMochaListener () { + var mochaListener = process.listeners('uncaughtException').pop(); + process.removeListener('uncaughtException', mochaListener); + return mochaListener; + } + + function allTests(parser, ip) { + var args = config.configureClient(parser, ip); + + describe("using " + parser + " and " + ip, function () { + var key, value; + + beforeEach(function () { + key = uuid.v4(); + value = uuid.v4(); + }); + + describe("when not connected", function () { + var client; + + beforeEach(function (done) { + client = redis.createClient.apply(redis.createClient, args); + client.once("error", done); + client.once("connect", function () { + client.quit(); + }); + client.on('end', function () { + return done(); + }); + }); + + it("reports an error", function (done) { + client.set(key, value, function (err, res) { + assert.equal(err.message, 'Redis connection gone from end event.'); + done(); + }); + }); + }); + + describe("when connected", function () { + var client; + + beforeEach(function (done) { + client = redis.createClient.apply(redis.createClient, args); + client.once("error", done); + client.once("connect", function () { + done(); + }); + }); + + afterEach(function () { + client.end(); + }); + + describe("and a callback is specified", function () { + describe("with valid parameters", function () { + it("sets the value correctly", function (done) { + client.set(key, value, function (err, res) { + nodeAssert.isNotError()(err, res); + client.get(key, function (err, res) { + nodeAssert.isString(value)(err, res); + done(); + }); + }); + }); + }); + + describe("with undefined 'key' and missing 'value' parameter", function () { + it("reports an error", function (done) { + client.set(undefined, function (err, res) { + nodeAssert.isError()(err, null); + done(); + }); + }); + }); + + describe("with undefined 'key' and defined 'value' parameters", function () { + it("reports an error", function () { + client.set(undefined, value, function (err, res) { + nodeAssert.isError()(err, null); + done(); + }); + }); + }); + }); + + describe("and no callback is specified", function () { + describe("with valid parameters", function () { + it("sets the value correctly", function (done) { + client.set(key, value); + setTimeout(function () { + client.get(key, function (err, res) { + nodeAssert.isString(value)(err, res); + done(); + }); + }, 100); + }); + }); + + describe("with undefined 'key' and missing 'value' parameter", function () { + it("does not emit an error", function (done) { + this.timeout(50); + + client.once("error", function (err) { + nodeAssert.isError()(err, null); + return done(err); + }); + + client.set(); + + setTimeout(function () { + done(); + }, 45); + }); + + it("does not throw an error", function (done) { + this.timeout(50); + var mochaListener = removeMochaListener(); + + process.once('uncaughtException', function (err) { + process.on('uncaughtException', mochaListener); + return done(err); + }); + + client.set(); + + setTimeout(function () { + done(); + }, 45); + }); + }); + + describe("with undefined 'key' and defined 'value' parameters", function () { + it("throws an error", function () { + var mochaListener = removeMochaListener(); + + process.once('uncaughtException', function (err) { + process.on('uncaughtException', mochaListener); + nodeAssert.isError()(err, null); + }); + + client.set(undefined, value); + }); + }); + }); + }); + }); + } + + ['javascript', 'hiredis'].forEach(function (parser) { + allTests(parser, "/tmp/redis.sock"); + ['IPv4', 'IPv6'].forEach(function (ip) { + allTests(parser, ip); + }) + }); + + after(function (done) { + if (rp) rp.stop(done); + }); +}); diff --git a/test/test.js b/test/test.js index 6021fcdfa9..530a75b19a 100644 --- a/test/test.js +++ b/test/test.js @@ -117,6 +117,8 @@ next = function next(name) { // Tests are run in the order they are defined, so FLUSHDB should always be first. +<<<<<<< HEAD +<<<<<<< HEAD <<<<<<< HEAD tests.IPV4 = function () { var ipv4addr = process.env.REDIS_PORT_6379_TCP_ADDR || "127.0.0.1"; @@ -1365,38 +1367,6 @@ tests.RENAMENX = function () { client.exists(["foo2"], last(name, require_number(1, name))); }; -tests.DBSIZE = function () { - var name = "DBSIZE"; - client.set(['foo', 'bar'], require_string("OK", name)); - client.DBSIZE([], last(name, require_number_pos("DBSIZE"))); -}; - -tests.GET_1 = function () { - var name = "GET_1"; - client.set(["get key", "get val"], require_string("OK", name)); - client.GET(["get key"], last(name, require_string("get val", name))); -}; - -tests.GET_2 = function() { - var name = "GET_2"; - - // tests handling of non-existent keys - client.GET('this_key_shouldnt_exist', last(name, require_null(name))); -}; - -tests.SET = function () { - var name = "SET"; - client.SET(["set key", "set val"], require_string("OK", name)); - client.get(["set key"], last(name, require_string("set val", name))); - client.SET(["set key", undefined], require_error(name)); -}; - -tests.GETSET = function () { - var name = "GETSET"; - client.set(["getset key", "getset val"], require_string("OK", name)); - client.GETSET(["getset key", "new getset val"], require_string("getset val", name)); - client.get(["getset key"], last(name, require_string("new getset val", name))); -}; tests.MGET = function () { var name = "MGET"; From 071b3ff27c7580d6c4440e9d487434432b64fd00 Mon Sep 17 00:00:00 2001 From: Benjamin Coe Date: Thu, 16 Jul 2015 00:00:59 -0700 Subject: [PATCH 41/49] moved tests for specific commands into the /commands sub-folder, put a ton of tests around the multi command --- package.json | 2 +- test/lib/nodeify-assertions.js | 10 + test/mocha/{ => commands}/dbsize.spec.js | 6 +- test/mocha/{ => commands}/flushdb.spec.js | 6 +- test/mocha/{ => commands}/get.spec.js | 6 +- test/mocha/{ => commands}/getset.spec.js | 6 +- test/mocha/{ => commands}/incr.spec.js | 6 +- test/mocha/{ => commands}/mset.spec.js | 6 +- test/mocha/commands/multi.spec.js | 269 ++++++++++++++++++ test/mocha/{ => commands}/select.spec.js | 6 +- test/mocha/{ => commands}/set.spec.js | 14 +- ...{connecting.spec.js => node_redis.spec.js} | 0 test/test.js | 55 +--- 13 files changed, 311 insertions(+), 81 deletions(-) rename test/mocha/{ => commands}/dbsize.spec.js (96%) rename test/mocha/{ => commands}/flushdb.spec.js (96%) rename test/mocha/{ => commands}/get.spec.js (95%) rename test/mocha/{ => commands}/getset.spec.js (96%) rename test/mocha/{ => commands}/incr.spec.js (97%) rename test/mocha/{ => commands}/mset.spec.js (97%) create mode 100644 test/mocha/commands/multi.spec.js rename test/mocha/{ => commands}/select.spec.js (97%) rename test/mocha/{ => commands}/set.spec.js (95%) rename test/mocha/{connecting.spec.js => node_redis.spec.js} (100%) diff --git a/package.json b/package.json index 0563794501..ce67d02b83 100644 --- a/package.json +++ b/package.json @@ -12,7 +12,7 @@ "scripts": { "coverage": "nyc report --reporter=text-lcov | coveralls", "test": "nyc ./test/run.sh", - "mocha": "nyc ./node_modules/.bin/_mocha ./test/mocha/*.js" + "mocha": "nyc ./node_modules/.bin/_mocha ./test/mocha/*.js ./test/mocha/commands/*.js" }, "devDependencies": { "async": "^1.3.0", diff --git a/test/lib/nodeify-assertions.js b/test/lib/nodeify-assertions.js index c80cccb8e5..73000e67d0 100644 --- a/test/lib/nodeify-assertions.js +++ b/test/lib/nodeify-assertions.js @@ -56,5 +56,15 @@ module.exports = { return true; }; } + }, + + serverVersionAtLeast: function (connection, desired_version) { + // Return true if the server version >= desired_version + var version = connection.server_info.versions; + for (var i = 0; i < 3; i++) { + if (version[i] > desired_version[i]) return true; + if (version[i] < desired_version[i]) return false; + } + return true; } }; diff --git a/test/mocha/dbsize.spec.js b/test/mocha/commands/dbsize.spec.js similarity index 96% rename from test/mocha/dbsize.spec.js rename to test/mocha/commands/dbsize.spec.js index aa019c57cb..df1b1431bd 100644 --- a/test/mocha/dbsize.spec.js +++ b/test/mocha/commands/dbsize.spec.js @@ -1,9 +1,9 @@ var async = require('async'); var assert = require('assert'); -var config = require("../lib/config"); -var nodeAssert = require('../lib/nodeify-assertions'); +var config = require("../../lib/config"); +var nodeAssert = require('../../lib/nodeify-assertions'); var redis = config.redis; -var RedisProcess = require("../lib/redis-process"); +var RedisProcess = require("../../lib/redis-process"); var uuid = require('uuid'); describe("The 'dbsize' method", function () { diff --git a/test/mocha/flushdb.spec.js b/test/mocha/commands/flushdb.spec.js similarity index 96% rename from test/mocha/flushdb.spec.js rename to test/mocha/commands/flushdb.spec.js index 693ff69ab5..de325a800d 100644 --- a/test/mocha/flushdb.spec.js +++ b/test/mocha/commands/flushdb.spec.js @@ -1,9 +1,9 @@ var async = require('async'); var assert = require('assert'); -var config = require("../lib/config"); -var nodeAssert = require('../lib/nodeify-assertions'); +var config = require("../../lib/config"); +var nodeAssert = require('../../lib/nodeify-assertions'); var redis = config.redis; -var RedisProcess = require("../lib/redis-process"); +var RedisProcess = require("../../lib/redis-process"); var uuid = require('uuid'); describe("The 'flushdb' method", function () { diff --git a/test/mocha/get.spec.js b/test/mocha/commands/get.spec.js similarity index 95% rename from test/mocha/get.spec.js rename to test/mocha/commands/get.spec.js index 6c533f6a81..d232a6ecb1 100644 --- a/test/mocha/get.spec.js +++ b/test/mocha/commands/get.spec.js @@ -1,9 +1,9 @@ var async = require('async'); var assert = require('assert'); -var config = require("../lib/config"); -var nodeAssert = require('../lib/nodeify-assertions'); +var config = require("../../lib/config"); +var nodeAssert = require('../../lib/nodeify-assertions'); var redis = config.redis; -var RedisProcess = require("../lib/redis-process"); +var RedisProcess = require("../../lib/redis-process"); var uuid = require('uuid'); describe("The 'get' method", function () { diff --git a/test/mocha/getset.spec.js b/test/mocha/commands/getset.spec.js similarity index 96% rename from test/mocha/getset.spec.js rename to test/mocha/commands/getset.spec.js index 847aa1e462..ba76384f2c 100644 --- a/test/mocha/getset.spec.js +++ b/test/mocha/commands/getset.spec.js @@ -1,9 +1,9 @@ var async = require('async'); var assert = require('assert'); -var config = require("../lib/config"); -var nodeAssert = require('../lib/nodeify-assertions'); +var config = require("../../lib/config"); +var nodeAssert = require('../../lib/nodeify-assertions'); var redis = config.redis; -var RedisProcess = require("../lib/redis-process"); +var RedisProcess = require("../../lib/redis-process"); var uuid = require('uuid'); describe("The 'getset' method", function () { diff --git a/test/mocha/incr.spec.js b/test/mocha/commands/incr.spec.js similarity index 97% rename from test/mocha/incr.spec.js rename to test/mocha/commands/incr.spec.js index 34bacec70e..ae4534dd30 100644 --- a/test/mocha/incr.spec.js +++ b/test/mocha/commands/incr.spec.js @@ -1,9 +1,9 @@ var async = require('async'); var assert = require('assert'); -var config = require("../lib/config"); -var nodeAssert = require('../lib/nodeify-assertions'); +var config = require("../../lib/config"); +var nodeAssert = require('../../lib/nodeify-assertions'); var redis = config.redis; -var RedisProcess = require("../lib/redis-process"); +var RedisProcess = require("../../lib/redis-process"); var uuid = require('uuid'); describe("The 'incr' method", function () { diff --git a/test/mocha/mset.spec.js b/test/mocha/commands/mset.spec.js similarity index 97% rename from test/mocha/mset.spec.js rename to test/mocha/commands/mset.spec.js index 90ffb32a74..b1f4f367b0 100644 --- a/test/mocha/mset.spec.js +++ b/test/mocha/commands/mset.spec.js @@ -1,9 +1,9 @@ var async = require('async'); var assert = require('assert'); -var config = require("../lib/config"); -var nodeAssert = require('../lib/nodeify-assertions'); +var config = require("../../lib/config"); +var nodeAssert = require('../../lib/nodeify-assertions'); var redis = config.redis; -var RedisProcess = require("../lib/redis-process"); +var RedisProcess = require("../../lib/redis-process"); var uuid = require('uuid'); describe("The 'mset' method", function () { diff --git a/test/mocha/commands/multi.spec.js b/test/mocha/commands/multi.spec.js new file mode 100644 index 0000000000..ba8be4fcbf --- /dev/null +++ b/test/mocha/commands/multi.spec.js @@ -0,0 +1,269 @@ +var async = require('async'); +var assert = require('assert'); +var config = require("../../lib/config"); +var nodeAssert = require('../../lib/nodeify-assertions'); +var redis = config.redis; +var RedisProcess = require("../../lib/redis-process"); +var uuid = require('uuid'); + +describe("The 'multi' method", function () { + + var rp; + before(function (done) { + RedisProcess.start(function (err, _rp) { + rp = _rp; + return done(err); + }); + }) + + function removeMochaListener () { + var mochaListener = process.listeners('uncaughtException').pop(); + process.removeListener('uncaughtException', mochaListener); + return mochaListener; + } + + function allTests(parser, ip) { + var args = config.configureClient(parser, ip); + + describe("using " + parser + " and " + ip, function () { + var key, value; + + beforeEach(function () { + key = uuid.v4(); + value = uuid.v4(); + }); + + describe("when not connected", function () { + var client; + + beforeEach(function (done) { + client = redis.createClient.apply(redis.createClient, args); + client.once("error", done); + client.once("connect", function () { + client.quit(); + }); + client.on('end', function () { + return done(); + }); + }); + + it("reports an error", function (done) { + client.multi(); + client.exec(function (err, res) { + assert.equal(err.message, 'Redis connection gone from end event.'); + done(); + }); + }); + }); + + describe("when connected", function () { + var client; + + beforeEach(function (done) { + client = redis.createClient.apply(redis.createClient, args); + client.once("error", done); + client.once("connect", function () { + client.del('multifoo', 'multibar', 'multifoo_8', 'multibar_8', function (err) { + return done(err); + }) + }); + }); + + afterEach(function () { + client.end(); + }); + + it('roles back a transaction when one command in a sequence of commands fails', function (done) { + var name = "MULTI_1", multi1, multi2; + + // Provoke an error at queue time + multi1 = client.multi(); + multi1.mset("multifoo", "10", "multibar", "20", nodeAssert.isString("OK")); + multi1.set("foo2", nodeAssert.isError()); + multi1.incr("multifoo", nodeAssert.isNumber(11)); + multi1.incr("multibar", nodeAssert.isNumber(21)); + multi1.exec(function () { + // Redis 2.6.5+ will abort transactions with errors + // see: http://redis.io/topics/transactions + var multibar_expected = 22; + var multifoo_expected = 12; + if (nodeAssert.serverVersionAtLeast(client, [2, 6, 5])) { + multibar_expected = 1; + multifoo_expected = 1; + } + + // Confirm that the previous command, while containing an error, still worked. + multi2 = client.multi(); + multi2.incr("multibar", nodeAssert.isNumber(multibar_expected)); + multi2.incr("multifoo", nodeAssert.isNumber(multifoo_expected)); + multi2.exec(function (err, replies) { + assert.strictEqual(multibar_expected, replies[0]); + assert.strictEqual(multifoo_expected, replies[1]); + return done(); + }); + }); + }); + + // I'm unclear as to the difference between this test in the test above, + // perhaps @mranney can clarify? + it('roles back a transaction when an error was provoked at queue time', function (done) { + multi1 = client.multi(); + multi1.mset("multifoo_8", "10", "multibar_8", "20", nodeAssert.isString("OK")); + multi1.set("foo2", nodeAssert.isError()); + multi1.set("foo3", nodeAssert.isError()); + multi1.incr("multifoo_8", nodeAssert.isNumber(11)); + multi1.incr("multibar_8", nodeAssert.isNumber(21)); + multi1.exec(function () { + // Redis 2.6.5+ will abort transactions with errors + // see: http://redis.io/topics/transactions + var multibar_expected = 22; + var multifoo_expected = 12; + if (nodeAssert.serverVersionAtLeast(client, [2, 6, 5])) { + multibar_expected = 1; + multifoo_expected = 1; + } + + // Confirm that the previous command, while containing an error, still worked. + multi2 = client.multi(); + multi2.incr("multibar_8", nodeAssert.isNumber(multibar_expected)); + multi2.incr("multifoo_8", nodeAssert.isNumber(multifoo_expected)); + multi2.exec(function (err, replies) { + assert.strictEqual(multibar_expected, replies[0]); + assert.strictEqual(multifoo_expected, replies[1]); + return done(); + }); + }); + }); + + it('roles back a transaction when one command in an array of commands fails', function (done) { + // test nested multi-bulk replies + client.multi([ + ["mget", "multifoo", "multibar", function (err, res) { + assert.strictEqual(2, res.length); + assert.strictEqual("0", res[0].toString()); + assert.strictEqual("0", res[1].toString()); + }], + ["set", "foo2", nodeAssert.isError()], + ["incr", "multifoo", nodeAssert.isNumber(1)], + ["incr", "multibar", nodeAssert.isNumber(1)] + ]).exec(function (err, replies) { + if (nodeAssert.serverVersionAtLeast(client, [2, 6, 5])) { + assert.notEqual(err, null); + assert.equal(replies, undefined); + } else { + assert.strictEqual(2, replies[0].length); + assert.strictEqual("0", replies[0][0].toString()); + assert.strictEqual("0", replies[0][1].toString()); + + assert.strictEqual("1", replies[1].toString()); + assert.strictEqual("1", replies[2].toString()); + } + + return done(); + }); + }); + + it('handles multiple operations being applied to a set', function (done) { + client.sadd("some set", "mem 1"); + client.sadd("some set", "mem 2"); + client.sadd("some set", "mem 3"); + client.sadd("some set", "mem 4"); + + // make sure empty mb reply works + client.del("some missing set"); + client.smembers("some missing set", function (err, reply) { + // make sure empty mb reply works + assert.strictEqual(0, reply.length); + }); + + // test nested multi-bulk replies with empty mb elements. + client.multi([ + ["smembers", "some set"], + ["del", "some set"], + ["smembers", "some set"] + ]) + .scard("some set") + .exec(function (err, replies) { + assert.strictEqual(4, replies[0].length); + assert.strictEqual(0, replies[2].length); + return done(); + }); + }); + + it('allows multiple operations to be performed using a chaining API', function (done) { + client.multi() + .mset('some', '10', 'keys', '20') + .incr('some') + .incr('keys') + .mget('some', 'keys') + .exec(function (err, replies) { + assert.strictEqual(null, err); + assert.equal('OK', replies[0]); + assert.equal(11, replies[1]); + assert.equal(21, replies[2]); + assert.equal(11, replies[3][0].toString()); + assert.equal(21, replies[3][1].toString()); + return done(); + }); + }); + + it('allows an array to be provided indicating multiple operations to perform', function (done) { + // test nested multi-bulk replies with nulls. + client.multi([ + ["mget", ["multifoo", "some", "random value", "keys"]], + ["incr", "multifoo"] + ]) + .exec(function (err, replies) { + assert.strictEqual(replies.length, 2); + assert.strictEqual(replies[0].length, 4); + return done(); + }); + }); + + it('allows multiple operations to be performed on a hash', function (done) { + client.multi() + .hmset("multihash", "a", "foo", "b", 1) + .hmset("multihash", { + extra: "fancy", + things: "here" + }) + .hgetall("multihash") + .exec(function (err, replies) { + assert.strictEqual(null, err); + assert.equal("OK", replies[0]); + assert.equal(Object.keys(replies[2]).length, 4); + assert.equal("foo", replies[2].a); + assert.equal("1", replies[2].b); + assert.equal("fancy", replies[2].extra); + assert.equal("here", replies[2].things); + return done(); + }); + }); + + it('reports multiple exceptions when they occur', function (done) { + if (!nodeAssert.serverVersionAtLeast(client, [2, 6, 5])) return done(); + + client.multi().set("foo").exec(function (err, reply) { + assert(Array.isArray(err), "err should be an array"); + assert.equal(2, err.length, "err should have 2 items"); + assert(err[0].message.match(/ERR/), "First error message should contain ERR"); + assert(err[1].message.match(/EXECABORT/), "First error message should contain EXECABORT"); + return done(); + }); + }); + + }); + }); + } + + ['javascript', 'hiredis'].forEach(function (parser) { + allTests(parser, "/tmp/redis.sock"); + ['IPv4', 'IPv6'].forEach(function (ip) { + allTests(parser, ip); + }) + }); + + after(function (done) { + if (rp) rp.stop(done); + }); +}); diff --git a/test/mocha/select.spec.js b/test/mocha/commands/select.spec.js similarity index 97% rename from test/mocha/select.spec.js rename to test/mocha/commands/select.spec.js index bde75857f6..24c1d159b8 100644 --- a/test/mocha/select.spec.js +++ b/test/mocha/commands/select.spec.js @@ -1,9 +1,9 @@ var async = require('async'); var assert = require('assert'); -var config = require("../lib/config"); -var nodeAssert = require('../lib/nodeify-assertions'); +var config = require("../../lib/config"); +var nodeAssert = require('../../lib/nodeify-assertions'); var redis = config.redis; -var RedisProcess = require("../lib/redis-process"); +var RedisProcess = require("../../lib/redis-process"); describe("The 'select' method", function () { diff --git a/test/mocha/set.spec.js b/test/mocha/commands/set.spec.js similarity index 95% rename from test/mocha/set.spec.js rename to test/mocha/commands/set.spec.js index ed5026710e..72df4467fb 100644 --- a/test/mocha/set.spec.js +++ b/test/mocha/commands/set.spec.js @@ -1,9 +1,9 @@ var async = require('async'); var assert = require('assert'); -var config = require("../lib/config"); -var nodeAssert = require('../lib/nodeify-assertions'); +var config = require("../../lib/config"); +var nodeAssert = require('../../lib/nodeify-assertions'); var redis = config.redis; -var RedisProcess = require("../lib/redis-process"); +var RedisProcess = require("../../lib/redis-process"); var uuid = require('uuid'); describe("The 'set' method", function () { @@ -117,7 +117,7 @@ describe("The 'set' method", function () { describe("with undefined 'key' and missing 'value' parameter", function () { it("does not emit an error", function (done) { - this.timeout(50); + this.timeout(200); client.once("error", function (err) { nodeAssert.isError()(err, null); @@ -128,11 +128,11 @@ describe("The 'set' method", function () { setTimeout(function () { done(); - }, 45); + }, 100); }); it("does not throw an error", function (done) { - this.timeout(50); + this.timeout(200); var mochaListener = removeMochaListener(); process.once('uncaughtException', function (err) { @@ -144,7 +144,7 @@ describe("The 'set' method", function () { setTimeout(function () { done(); - }, 45); + }, 100); }); }); diff --git a/test/mocha/connecting.spec.js b/test/mocha/node_redis.spec.js similarity index 100% rename from test/mocha/connecting.spec.js rename to test/mocha/node_redis.spec.js diff --git a/test/test.js b/test/test.js index 530a75b19a..7b3cb1cc72 100644 --- a/test/test.js +++ b/test/test.js @@ -120,6 +120,7 @@ next = function next(name) { <<<<<<< HEAD <<<<<<< HEAD <<<<<<< HEAD +<<<<<<< HEAD tests.IPV4 = function () { var ipv4addr = process.env.REDIS_PORT_6379_TCP_ADDR || "127.0.0.1"; var ipv4Client = redis.createClient( PORT, ipv4addr, { family : "IPv4", parser: parser } ); @@ -373,6 +374,8 @@ tests.MULTI_6 = function () { }); }; +// THIS TEST SHOULD BE MOVED IN TO A PARSER +// SPECIFIC TESTING FILE. tests.MULTI_7 = function () { var name = "MULTI_7"; @@ -402,58 +405,6 @@ tests.MULTI_7 = function () { next(name); }; - -tests.MULTI_EXCEPTION_1 = function() { - var name = "MULTI_EXCEPTION_1"; - - if (!server_version_at_least(client, [2, 6, 5])) { - console.log("Skipping " + name + " for old Redis server version < 2.6.5"); - return next(name); - } - - client.multi().set("foo").exec(function (err, reply) { - assert(Array.isArray(err), "err should be an array"); - assert.equal(2, err.length, "err should have 2 items"); - assert(err[0].message.match(/ERR/), "First error message should contain ERR"); - assert(err[1].message.match(/EXECABORT/), "First error message should contain EXECABORT"); - next(name); - }); -}; - -tests.MULTI_8 = function () { - var name = "MULTI_8", multi1, multi2; - - // Provoke an error at queue time - multi1 = client.multi(); - multi1.mset("multifoo_8", "10", "multibar_8", "20", require_string("OK", name)); - multi1.set("foo2", require_error(name)); - multi1.set("foo3", require_error(name)); - multi1.incr("multifoo_8", require_number(11, name)); - multi1.incr("multibar_8", require_number(21, name)); - multi1.exec(function () { - require_error(name); - - // Redis 2.6.5+ will abort transactions with errors - // see: http://redis.io/topics/transactions - var multibar_expected = 22; - var multifoo_expected = 12; - if (server_version_at_least(client, [2, 6, 5])) { - multibar_expected = 1; - multifoo_expected = 1; - } - - // Confirm that the previous command, while containing an error, still worked. - multi2 = client.multi(); - multi2.incr("multibar_8", require_number(multibar_expected, name)); - multi2.incr("multifoo_8", require_number(multifoo_expected, name)); - multi2.exec(function (err, replies) { - assert.strictEqual(multibar_expected, replies[0]); - assert.strictEqual(multifoo_expected, replies[1]); - next(name); - }); - }); -}; - tests.FWD_ERRORS_1 = function () { var name = "FWD_ERRORS_1"; From 04fe11b0f502b92b62695c1bc75c7fbca91cf7eb Mon Sep 17 00:00:00 2001 From: Erin Spiceland Date: Wed, 22 Jul 2015 13:43:25 -0500 Subject: [PATCH 42/49] Add simple tests for generated commands. --- package.json | 1 + .../mocha/commands/generated-commands.spec.js | 119 ++++++++++++++++++ 2 files changed, 120 insertions(+) create mode 100644 test/mocha/commands/generated-commands.spec.js diff --git a/package.json b/package.json index ce67d02b83..a25f796235 100644 --- a/package.json +++ b/package.json @@ -30,6 +30,7 @@ "url": "git://github.com/mranney/node_redis.git" }, "dependencies": { + "sinon": "^1.15.4", "uuid": "^2.0.1" } } diff --git a/test/mocha/commands/generated-commands.spec.js b/test/mocha/commands/generated-commands.spec.js new file mode 100644 index 0000000000..bff15f4839 --- /dev/null +++ b/test/mocha/commands/generated-commands.spec.js @@ -0,0 +1,119 @@ +var async = require('async'); +var assert = require('assert'); +var config = require("../../lib/config"); +var nodeAssert = require('../../lib/nodeify-assertions'); +var redis = config.redis; +var RedisProcess = require("../../lib/redis-process"); +var uuid = require('uuid'); +var commands = require("../../../lib/commands"); +var sinon = require('sinon').sandbox.create(); + +var overwritten = ['eval', 'hmset', 'multi', 'select']; + +describe("The auto-generated methods", function () { + + var rp; + before(function (done) { + RedisProcess.start(function (err, _rp) { + rp = _rp; + return done(err); + }); + }) + + function removeMochaListener () { + var mochaListener = process.listeners('uncaughtException').pop(); + process.removeListener('uncaughtException', mochaListener); + return mochaListener; + } + + function allTests(parser, ip) { + var args = config.configureClient(parser, ip); + + describe("using " + parser + " and " + ip, function () { + var key, value; + var client; + + beforeEach(function (done) { + client = redis.createClient.apply(redis.createClient, args); + client.once("error", function onError(err) { + done(err); + }); + client.once("ready", function onReady() { + done(); + }); + }); + + afterEach(function () { + client.end(); + }); + + commands.forEach(function (method) { + if (overwritten.indexOf(method) > -1) { + // these are in the list of generated commands but are later overwritten with + // different behavior by node_redis + return; + } + + describe("the " + method + " method", function () { + var methodArgs; + var noop = function () { }; + + it("calls sendCommand with whatever arguments it receives", function () { + key = uuid.v4(); + value = uuid.v4(); + + var parts = method.split(' '); + var argNum = 0; + + client.send_command = sinon.spy(); + methodArgs = [key, value, noop]; + + client[parts[0]].apply(client, methodArgs); + + assert.strictEqual(client.send_command.called, true, + "Client.send_command should have been called."); + assert.strictEqual(parts[0], client.send_command.args[0][argNum], + "Command name '" + parts[0] + "' should be passed as arg " + + argNum + " to send_command"); + argNum++; + /* + * Um, except this doesn't work? The second part of the command is never sent???? + if (parts[1]) { + assert.strictEqual(parts[1], client.send_command.args[0][argNum], + "Second command '" + parts[1] + "' should be passed as arg " + + argNum + " to send_command"); + argNum++; + } + */ + assert.strictEqual(methodArgs.length, client.send_command.args[0][argNum].length, + "The rest of the args to " + method + " should be passed as arg an array to send_command"); + assert.strictEqual(methodArgs[0], client.send_command.args[0][argNum][0], + "Arg " + argNum + " to " + method + " should be passed as arg " + + argNum + " to send_command"); + assert.strictEqual(methodArgs[1], client.send_command.args[0][argNum][1], + "Arg " + argNum + " to " + method + " should be passed as arg " + + argNum + " to send_command"); + assert.strictEqual(methodArgs[2], client.send_command.args[0][argNum][2], + "Arg " + argNum + " to " + method + " should be passed as arg " + + argNum + " to send_command"); + }); + }); + }); + }); + } + + ['javascript', 'hiredis'].forEach(function (parser) { + allTests(parser, "/tmp/redis.sock"); + ['IPv4', 'IPv6'].forEach(function (ip) { + allTests(parser, ip); + }) + }); + + afterEach(function () { + sinon.restore(); + }); + + after(function (done) { + if (rp) rp.stop(done); + }); +}); From a0832c3744578402a69c9b1fc44c0883ef51f007 Mon Sep 17 00:00:00 2001 From: Benjamin Coe Date: Wed, 22 Jul 2015 20:59:08 -0700 Subject: [PATCH 43/49] slight refactor from code review smoke test large list of commands ported more tests to mocha, some slight cleanup in tests move sinon and uuid to dev dependencies finished porting eval tests over to mocha rebased mocha testing branch with master ported client and script tests ported watch tests ported detect_buffers tests ported unref tests ported auth tests over to mocha ported idle and no_delay tests ported hlen, hset continuing marching forward ported hincrby, sinter, sort, pubsub tests. improved logic in redis-process, I was still occasionally having issues where redis failed to exit. switch back to default test command ported del, exists, hlen, keys, randomkey, type cleanup based on what I've learned so far from refactor. we now start and stop redis less often. moved tests to their final resting place finished porting node_redis client tests ported hgetall, mget, msetnx, rename, renamenx, setex, setnx ported hgetall, mget, msetnx, rename, renamenx, setex, setnx ported queue tests to mocha amalgamated some of the helper logic ported sadd, scard, sismember, srem, utf-8 --- .travis.yml | 3 +- package.json | 10 +- test/auth.spec.js | 90 + test/commands/client.spec.js | 61 + test/{mocha => }/commands/dbsize.spec.js | 37 +- test/commands/del.spec.js | 51 + test/commands/eval.spec.js | 199 ++ test/commands/exits.spec.js | 43 + test/{mocha => }/commands/flushdb.spec.js | 33 +- test/{mocha => }/commands/get.spec.js | 29 +- test/{mocha => }/commands/getset.spec.js | 31 +- test/commands/hgetall.spec.js | 91 + test/commands/hincrby.spec.js | 48 + test/commands/hlen.spec.js | 46 + test/commands/hmget.spec.js | 68 + test/commands/hmset.spec.js | 61 + test/commands/hset.spec.js | 70 + test/{mocha => }/commands/incr.spec.js | 41 +- test/commands/keys.spec.js | 76 + test/commands/mget.spec.js | 66 + test/{mocha => }/commands/mset.spec.js | 35 +- test/commands/msetnx.spec.js | 46 + test/{mocha => }/commands/multi.spec.js | 65 +- test/commands/randomkey.test.js | 42 + test/commands/rename.spec.js | 46 + test/commands/renamenx.spec.js | 49 + test/commands/sadd.spec.js | 58 + test/commands/scard.spec.js | 39 + test/commands/script.spec.js | 68 + test/{mocha => }/commands/select.spec.js | 19 +- test/{mocha => }/commands/set.spec.js | 31 +- test/commands/setex.spec.js | 47 + test/commands/setnx.spec.js | 46 + test/commands/sinter.spec.js | 70 + test/commands/sismember.spec.js | 43 + test/commands/sort.spec.js | 128 ++ test/commands/srem.spec.js | 66 + test/commands/type.spec.js | 63 + test/commands/watch.spec.js | 68 + test/conf/password.conf | 5 + test/{lib/nodeify-assertions.js => helper.js} | 76 +- test/lib/config.js | 17 +- test/lib/redis-process.js | 71 +- test/lib/unref.js | 18 + .../mocha/commands/generated-commands.spec.js | 119 - test/mocha/node_redis.spec.js | 182 -- test/node_redis.spec.js | 640 ++++++ test/parser/javascript.spec.js | 27 + test/pubsub.spec.js | 240 ++ test/queue-test.js | 37 - test/queue.spec.js | 37 + test/run.sh | 4 - test/test-unref.js | 14 - test/test.js | 1936 +---------------- 54 files changed, 3013 insertions(+), 2593 deletions(-) create mode 100644 test/auth.spec.js create mode 100644 test/commands/client.spec.js rename test/{mocha => }/commands/dbsize.spec.js (75%) create mode 100644 test/commands/del.spec.js create mode 100644 test/commands/eval.spec.js create mode 100644 test/commands/exits.spec.js rename test/{mocha => }/commands/flushdb.spec.js (81%) rename test/{mocha => }/commands/get.spec.js (78%) rename test/{mocha => }/commands/getset.spec.js (77%) create mode 100644 test/commands/hgetall.spec.js create mode 100644 test/commands/hincrby.spec.js create mode 100644 test/commands/hlen.spec.js create mode 100644 test/commands/hmget.spec.js create mode 100644 test/commands/hmset.spec.js create mode 100644 test/commands/hset.spec.js rename test/{mocha => }/commands/incr.spec.js (79%) create mode 100644 test/commands/keys.spec.js create mode 100644 test/commands/mget.spec.js rename test/{mocha => }/commands/mset.spec.js (85%) create mode 100644 test/commands/msetnx.spec.js rename test/{mocha => }/commands/multi.spec.js (82%) create mode 100644 test/commands/randomkey.test.js create mode 100644 test/commands/rename.spec.js create mode 100644 test/commands/renamenx.spec.js create mode 100644 test/commands/sadd.spec.js create mode 100644 test/commands/scard.spec.js create mode 100644 test/commands/script.spec.js rename test/{mocha => }/commands/select.spec.js (92%) rename test/{mocha => }/commands/set.spec.js (87%) create mode 100644 test/commands/setex.spec.js create mode 100644 test/commands/setnx.spec.js create mode 100644 test/commands/sinter.spec.js create mode 100644 test/commands/sismember.spec.js create mode 100644 test/commands/sort.spec.js create mode 100644 test/commands/srem.spec.js create mode 100644 test/commands/type.spec.js create mode 100644 test/commands/watch.spec.js create mode 100644 test/conf/password.conf rename test/{lib/nodeify-assertions.js => helper.js} (55%) create mode 100644 test/lib/unref.js delete mode 100644 test/mocha/commands/generated-commands.spec.js delete mode 100644 test/mocha/node_redis.spec.js create mode 100644 test/node_redis.spec.js create mode 100644 test/parser/javascript.spec.js create mode 100644 test/pubsub.spec.js delete mode 100644 test/queue-test.js create mode 100644 test/queue.spec.js delete mode 100755 test/run.sh delete mode 100644 test/test-unref.js diff --git a/.travis.yml b/.travis.yml index 4884f204e1..f64f437830 100644 --- a/.travis.yml +++ b/.travis.yml @@ -3,6 +3,5 @@ sudo: true node_js: - "0.10" - "0.12" - - "iojs" -script: npm run mocha + - "iojs-v2" after_success: npm run coverage diff --git a/package.json b/package.json index a25f796235..2e7d9ea181 100644 --- a/package.json +++ b/package.json @@ -11,8 +11,7 @@ "main": "./index.js", "scripts": { "coverage": "nyc report --reporter=text-lcov | coveralls", - "test": "nyc ./test/run.sh", - "mocha": "nyc ./node_modules/.bin/_mocha ./test/mocha/*.js ./test/mocha/commands/*.js" + "test": "nyc ./node_modules/.bin/_mocha ./test/*.js ./test/commands/*.js ./test/parser/*.js --timeout=8000" }, "devDependencies": { "async": "^1.3.0", @@ -23,14 +22,11 @@ "mocha": "^2.2.5", "nyc": "^3.0.0", "tcp-port-used": "^0.1.2", - "underscore": "~1.4.4" + "underscore": "~1.4.4", + "uuid": "^2.0.1" }, "repository": { "type": "git", "url": "git://github.com/mranney/node_redis.git" - }, - "dependencies": { - "sinon": "^1.15.4", - "uuid": "^2.0.1" } } diff --git a/test/auth.spec.js b/test/auth.spec.js new file mode 100644 index 0000000000..a149f4b9cf --- /dev/null +++ b/test/auth.spec.js @@ -0,0 +1,90 @@ +var assert = require("assert"); +var config = require("./lib/config"); +var helper = require('./helper') +var path = require('path'); +var redis = config.redis; + +describe("client authentication", function () { + before(function (done) { + helper.stopRedis(function () { + helper.startRedis('./conf/password.conf', done); + }); + }); + + function allTests(parser, ip) { + describe("using " + parser + " and " + ip, function () { + var args = config.configureClient(parser, ip); + var auth = 'porkchopsandwiches'; + var client = null; + + afterEach(function () { + client.end(); + }); + + it("allows auth to be provided with 'auth' method", function (done) { + client = redis.createClient.apply(redis.createClient, args); + client.auth(auth, function (err, res) { + assert.strictEqual(null, err); + assert.strictEqual("OK", res.toString()); + return done(err); + }); + }); + + it("raises error when auth is bad", function (done) { + client = redis.createClient.apply(redis.createClient, args); + + client.once('error', function (error) { + assert.ok(/ERR invalid password/.test(error)) + return done(); + }); + + client.auth(auth + 'bad'); + }); + + if (ip === 'IPv4') + it('allows auth to be provided as config option for client', function (done) { + client = redis.createClient('redis://foo:' + auth + '@' + config.HOST[ip] + ':' + config.PORT); + client.on("ready", function () { + return done(); + }); + }); + + it('allows auth to be provided as part of redis url', function (done) { + var args = config.configureClient(parser, ip, { + auth_pass: auth + }); + client = redis.createClient.apply(redis.createClient, args); + client.on("ready", function () { + return done(); + }); + }); + + it('reconnects with appropriate authentication', function (done) { + var readyCount = 0; + client = redis.createClient.apply(redis.createClient, args); + client.auth(auth); + client.on("ready", function () { + readyCount++; + if (readyCount === 1) { + client.stream.destroy(); + } else { + return done(); + } + }); + }); + }); + } + + ['javascript', 'hiredis'].forEach(function (parser) { + allTests(parser, "/tmp/redis.sock"); + ['IPv4', 'IPv6'].forEach(function (ip) { + allTests(parser, ip); + }) + }); + + after(function (done) { + helper.stopRedis(function () { + helper.startRedis('./conf/redis.conf', done); + }); + }); +}); diff --git a/test/commands/client.spec.js b/test/commands/client.spec.js new file mode 100644 index 0000000000..3710bdc9ed --- /dev/null +++ b/test/commands/client.spec.js @@ -0,0 +1,61 @@ +var assert = require("assert"); +var config = require("../lib/config"); +var helper = require("../helper"); +var redis = config.redis; + +describe("The 'client' method", function () { + + function allTests(parser, ip) { + var args = config.configureClient(parser, ip); + var pattern = /addr=/; + + describe("using " + parser + " and " + ip, function () { + var client; + + beforeEach(function (done) { + client = redis.createClient.apply(redis.createClient, args); + client.once("error", done); + client.once("connect", function () { + client.flushdb(function (err) { + if (!helper.serverVersionAtLeast(client, [2, 4, 0])) { + err = Error('script not supported in redis <= 2.4.0') + } + return done(err); + + }) + }); + }); + + afterEach(function () { + client.end(); + }); + + describe('list', function () { + it('lists connected clients', function (done) { + client.client("list", helper.match(pattern, done)); + }); + + it("lists connected clients when invoked with multi's chaining syntax", function (done) { + client.multi().client("list").exec(function(err, results) { + assert(pattern.test(results[0]), "expected string '" + results + "' to match " + pattern.toString()); + return done() + }) + }); + + it("lists connected clients when invoked with multi's array syntax", function (done) { + client.multi().client("list").exec(function(err, results) { + assert(pattern.test(results[0]), "expected string '" + results + "' to match " + pattern.toString()); + return done() + }) + }); + }); + }); + } + + ['javascript', 'hiredis'].forEach(function (parser) { + allTests(parser, "/tmp/redis.sock"); + ['IPv4', 'IPv6'].forEach(function (ip) { + allTests(parser, ip); + }) + }); +}); diff --git a/test/mocha/commands/dbsize.spec.js b/test/commands/dbsize.spec.js similarity index 75% rename from test/mocha/commands/dbsize.spec.js rename to test/commands/dbsize.spec.js index df1b1431bd..70c7eb178c 100644 --- a/test/mocha/commands/dbsize.spec.js +++ b/test/commands/dbsize.spec.js @@ -1,27 +1,12 @@ var async = require('async'); var assert = require('assert'); -var config = require("../../lib/config"); -var nodeAssert = require('../../lib/nodeify-assertions'); +var config = require("../lib/config"); +var helper = require('../helper'); var redis = config.redis; -var RedisProcess = require("../../lib/redis-process"); var uuid = require('uuid'); describe("The 'dbsize' method", function () { - var rp; - before(function (done) { - RedisProcess.start(function (err, _rp) { - rp = _rp; - return done(err); - }); - }) - - function removeMochaListener () { - var mochaListener = process.listeners('uncaughtException').pop(); - process.removeListener('uncaughtException', mochaListener); - return mochaListener; - } - function allTests(parser, ip) { var args = config.configureClient(parser, ip); @@ -63,7 +48,7 @@ describe("The 'dbsize' method", function () { client.once("error", done); client.once("connect", function () { client.flushdb(function (err, res) { - nodeAssert.isString("OK")(err, res); + helper.isString("OK")(err, res); done(); }); }); @@ -75,8 +60,8 @@ describe("The 'dbsize' method", function () { it("returns a zero db size", function (done) { client.dbsize([], function (err, res) { - nodeAssert.isNotError()(err, res); - nodeAssert.isType.number()(err, res); + helper.isNotError()(err, res); + helper.isType.number()(err, res); assert.strictEqual(res, 0, "Initial db size should be 0"); done(); }); @@ -87,13 +72,13 @@ describe("The 'dbsize' method", function () { beforeEach(function (done) { client.dbsize([], function (err, res) { - nodeAssert.isType.number()(err, res); + helper.isType.number()(err, res); assert.strictEqual(res, 0, "Initial db size should be 0"); oldSize = res; client.set(key, value, function (err, res) { - nodeAssert.isNotError()(err, res); + helper.isNotError()(err, res); done(); }); }); @@ -101,8 +86,8 @@ describe("The 'dbsize' method", function () { it("returns a larger db size", function (done) { client.dbsize([], function (err, res) { - nodeAssert.isNotError()(err, res); - nodeAssert.isType.positiveNumber()(err, res); + helper.isNotError()(err, res); + helper.isType.positiveNumber()(err, res); assert.strictEqual(true, (oldSize < res), "Adding data should increase db size."); done(); }); @@ -118,8 +103,4 @@ describe("The 'dbsize' method", function () { allTests(parser, ip); }) }); - - after(function (done) { - if (rp) rp.stop(done); - }); }); diff --git a/test/commands/del.spec.js b/test/commands/del.spec.js new file mode 100644 index 0000000000..b2b2995c48 --- /dev/null +++ b/test/commands/del.spec.js @@ -0,0 +1,51 @@ +var assert = require("assert"); +var config = require("../lib/config"); +var helper = require("../helper"); +var redis = config.redis; + +describe("The 'del' method", function () { + + function allTests(parser, ip) { + var args = config.configureClient(parser, ip); + + describe("using " + parser + " and " + ip, function () { + var client; + + beforeEach(function (done) { + client = redis.createClient.apply(redis.createClient, args); + client.once("error", done); + client.once("connect", function () { + client.flushdb(done); + }); + }); + + it('allows a single key to be deleted', function (done) { + client.set('foo', 'bar'); + client.del('foo', helper.isNumber(1)); + client.get('foo', helper.isNull(done)); + }); + + it('allows del to be called on a key that does not exist', function (done) { + client.del('foo', helper.isNumber(0, done)); + }); + + it('allows multiple keys to be deleted', function (done) { + client.mset('foo', 'bar', 'apple', 'banana'); + client.del('foo', 'apple', helper.isNumber(2)); + client.get('foo', helper.isNull()); + client.get('apple', helper.isNull(done)); + }); + + afterEach(function () { + client.end(); + }); + }); + } + + ['javascript', 'hiredis'].forEach(function (parser) { + allTests(parser, "/tmp/redis.sock"); + ['IPv4', 'IPv6'].forEach(function (ip) { + allTests(parser, ip); + }) + }); +}); diff --git a/test/commands/eval.spec.js b/test/commands/eval.spec.js new file mode 100644 index 0000000000..a98efa032e --- /dev/null +++ b/test/commands/eval.spec.js @@ -0,0 +1,199 @@ +var assert = require("assert"); +var config = require("../lib/config"); +var crypto = require("crypto"); +var helper = require("../helper"); +var redis = config.redis; + +describe("The 'eval' method", function () { + function allTests(parser, ip) { + var args = config.configureClient(parser, ip); + + describe("using " + parser + " and " + ip, function () { + var client; + + beforeEach(function (done) { + client = redis.createClient.apply(redis.createClient, args); + client.once("error", done); + client.once("connect", function () { + client.flushdb(function (err) { + if (!helper.serverVersionAtLeast(client, [2, 5, 0])) { + err = Error('exec not supported in redis <= 2.5.0') + } + return done(err); + }) + }); + }); + + afterEach(function () { + client.end(); + }); + + it('converts a float to an integer when evaluated', function (done) { + client.eval("return 100.5", 0, helper.isNumber(100, done)); + }); + + it('returns a string', function (done) { + client.eval("return 'hello world'", 0, helper.isString('hello world', done)); + }); + + it('converts boolean true to integer 1', function (done) { + client.eval("return true", 0, helper.isNumber(1, done)); + }); + + it('converts boolean false to null', function (done) { + client.eval("return false", 0, helper.isNull(done)); + }); + + it('converts lua status code to string representation', function (done) { + client.eval("return {ok='fine'}", 0, helper.isString('fine', done)); + }); + + it('converts lua error to an error response', function (done) { + client.eval("return {err='this is an error'}", 0, helper.isError(done)); + }); + + it('represents a lua table appropritely', function (done) { + client.eval("return {1,2,3,'ciao',{1,2}}", 0, function (err, res) { + assert.strictEqual(5, res.length); + assert.strictEqual(1, res[0]); + assert.strictEqual(2, res[1]); + assert.strictEqual(3, res[2]); + assert.strictEqual("ciao", res[3]); + assert.strictEqual(2, res[4].length); + assert.strictEqual(1, res[4][0]); + assert.strictEqual(2, res[4][1]); + return done(); + }); + }); + + it('populates keys and argv correctly', function (done) { + client.eval("return {KEYS[1],KEYS[2],ARGV[1],ARGV[2]}", 2, "a", "b", "c", "d", function (err, res) { + assert.strictEqual(4, res.length); + assert.strictEqual("a", res[0]); + assert.strictEqual("b", res[1]); + assert.strictEqual("c", res[2]); + assert.strictEqual("d", res[3]); + return done(); + }); + }); + + it('allows arguments to be provided in array rather than as multiple parameters', function (done) { + client.eval(["return {KEYS[1],KEYS[2],ARGV[1],ARGV[2]}", 2, "a", "b", "c", "d"], function (err, res) { + assert.strictEqual(4, res.length); + assert.strictEqual("a", res[0]); + assert.strictEqual("b", res[1]); + assert.strictEqual("c", res[2]); + assert.strictEqual("d", res[3]); + return done(); + }); + }); + + describe('evalsha', function () { + var source = "return redis.call('get', 'sha test')"; + var sha = crypto.createHash('sha1').update(source).digest('hex'); + + beforeEach(function (done) { + client.set("sha test", "eval get sha test", function (err, res) { + return done(err); + }); + }); + + it('allows a script to be executed that accesses the redis API', function (done) { + client.eval(source, 0, helper.isString('eval get sha test', done)); + }); + + it('can execute a script if the SHA exists', function (done) { + client.evalsha(sha, 0, helper.isString('eval get sha test', done)); + }); + + it('throws an error if SHA does not exist', function (done) { + client.evalsha('ffffffffffffffffffffffffffffffffffffffff', 0, helper.isError(done)); + }); + }); + + it('allows a key to be incremented, and performs appropriate conversion from LUA type', function (done) { + client.set("incr key", 0, function (err, reply) { + if (err) return done(err); + client.eval("local foo = redis.call('incr','incr key')\n" + "return {type(foo),foo}", 0, function (err, res) { + assert.strictEqual(2, res.length); + assert.strictEqual("number", res[0]); + assert.strictEqual(1, res[1]); + return done(err); + }); + }); + }); + + it('allows a bulk operation to be performed, and performs appropriate conversion from LUA type', function (done) { + client.set("bulk reply key", "bulk reply value", function (err, res) { + client.eval("local foo = redis.call('get','bulk reply key'); return {type(foo),foo}", 0, function (err, res) { + assert.strictEqual(2, res.length); + assert.strictEqual("string", res[0]); + assert.strictEqual("bulk reply value", res[1]); + return done(err); + }); + }); + }); + + it('allows a multi mulk operation to be performed, with the appropriate type conversion', function (done) { + client.multi() + .del("mylist") + .rpush("mylist", "a") + .rpush("mylist", "b") + .rpush("mylist", "c") + .exec(function (err, replies) { + if (err) return done(err); + client.eval("local foo = redis.call('lrange','mylist',0,-1); return {type(foo),foo[1],foo[2],foo[3],# foo}", 0, function (err, res) { + assert.strictEqual(5, res.length); + assert.strictEqual("table", res[0]); + assert.strictEqual("a", res[1]); + assert.strictEqual("b", res[2]); + assert.strictEqual("c", res[3]); + assert.strictEqual(3, res[4]); + return done(err); + }); + }); + }); + + it('returns an appropriate representation of Lua status reply', function (done) { + client.eval("local foo = redis.call('set','mykey','myval'); return {type(foo),foo['ok']}", 0, function (err, res) { + assert.strictEqual(2, res.length); + assert.strictEqual("table", res[0]); + assert.strictEqual("OK", res[1]); + return done(err); + }); + }); + + it('returns an appropriate representation of a Lua error reply', function (done) { + client.set("error reply key", "error reply value", function (err, res) { + if (err) return done(err); + client.eval("local foo = redis.pcall('incr','error reply key'); return {type(foo),foo['err']}", 0, function (err, res) { + assert.strictEqual(2, res.length); + assert.strictEqual("table", res[0]); + assert.strictEqual("ERR value is not an integer or out of range", res[1]); + return done(err); + }); + }); + }); + + it('returns an appropriate representation of a Lua nil reply', function (done) { + client.del("nil reply key", function (err, res) { + if (err) return done(err); + client.eval("local foo = redis.call('get','nil reply key'); return {type(foo),foo == false}", 0, function (err, res) { + if (err) throw err; + assert.strictEqual(2, res.length); + assert.strictEqual("boolean", res[0]); + assert.strictEqual(1, res[1]); + return done(err); + }); + }); + }); + }); + } + + ['javascript', 'hiredis'].forEach(function (parser) { + allTests(parser, "/tmp/redis.sock"); + ['IPv4', 'IPv6'].forEach(function (ip) { + allTests(parser, ip); + }) + }); +}); diff --git a/test/commands/exits.spec.js b/test/commands/exits.spec.js new file mode 100644 index 0000000000..b3f04badcc --- /dev/null +++ b/test/commands/exits.spec.js @@ -0,0 +1,43 @@ +var assert = require("assert"); +var config = require("../lib/config"); +var helper = require("../helper"); +var redis = config.redis; + +describe("The 'exits' method", function () { + + function allTests(parser, ip) { + var args = config.configureClient(parser, ip); + + describe("using " + parser + " and " + ip, function () { + var client; + + beforeEach(function (done) { + client = redis.createClient.apply(redis.createClient, args); + client.once("error", done); + client.once("connect", function () { + client.flushdb(done); + }); + }); + + it('returns 1 if the key exists', function (done) { + client.set('foo', 'bar'); + client.exists('foo', helper.isNumber(1, done)); + }); + + it('returns 0 if the key does not exist', function (done) { + client.exists('bar', helper.isNumber(0, done)); + }); + + afterEach(function () { + client.end(); + }); + }); + } + + ['javascript', 'hiredis'].forEach(function (parser) { + allTests(parser, "/tmp/redis.sock"); + ['IPv4', 'IPv6'].forEach(function (ip) { + allTests(parser, ip); + }) + }); +}); diff --git a/test/mocha/commands/flushdb.spec.js b/test/commands/flushdb.spec.js similarity index 81% rename from test/mocha/commands/flushdb.spec.js rename to test/commands/flushdb.spec.js index de325a800d..8df39f4752 100644 --- a/test/mocha/commands/flushdb.spec.js +++ b/test/commands/flushdb.spec.js @@ -1,27 +1,12 @@ var async = require('async'); var assert = require('assert'); -var config = require("../../lib/config"); -var nodeAssert = require('../../lib/nodeify-assertions'); +var config = require("../lib/config"); +var helper = require('../helper'); var redis = config.redis; -var RedisProcess = require("../../lib/redis-process"); var uuid = require('uuid'); describe("The 'flushdb' method", function () { - var rp; - before(function (done) { - RedisProcess.start(function (err, _rp) { - rp = _rp; - return done(err); - }); - }) - - function removeMochaListener () { - var mochaListener = process.listeners('uncaughtException').pop(); - process.removeListener('uncaughtException', mochaListener); - return mochaListener; - } - function allTests(parser, ip) { var args = config.configureClient(parser, ip); @@ -76,12 +61,12 @@ describe("The 'flushdb' method", function () { beforeEach(function (done) { async.parallel([function (next) { client.mset(key, uuid.v4(), key2, uuid.v4(), function (err, res) { - nodeAssert.isNotError()(err, res); + helper.isNotError()(err, res); next(err); }); }, function (next) { client.dbsize([], function (err, res) { - nodeAssert.isType.positiveNumber()(err, res); + helper.isType.positiveNumber()(err, res); oldSize = res; next(err); }); @@ -91,7 +76,7 @@ describe("The 'flushdb' method", function () { } client.flushdb(function (err, res) { - nodeAssert.isString("OK")(err, res); + helper.isString("OK")(err, res); done(err); }); }); @@ -110,8 +95,8 @@ describe("The 'flushdb' method", function () { it("results in a db size of zero", function (done) { client.dbsize([], function (err, res) { - nodeAssert.isNotError()(err, res); - nodeAssert.isType.number()(err, res); + helper.isNotError()(err, res); + helper.isType.number()(err, res); assert.strictEqual(0, res, "Flushing db should result in db size 0"); done(); }); @@ -127,8 +112,4 @@ describe("The 'flushdb' method", function () { allTests(parser, ip); }) }); - - after(function (done) { - if (rp) rp.stop(done); - }); }); diff --git a/test/mocha/commands/get.spec.js b/test/commands/get.spec.js similarity index 78% rename from test/mocha/commands/get.spec.js rename to test/commands/get.spec.js index d232a6ecb1..31502c2c4b 100644 --- a/test/mocha/commands/get.spec.js +++ b/test/commands/get.spec.js @@ -1,27 +1,12 @@ var async = require('async'); var assert = require('assert'); -var config = require("../../lib/config"); -var nodeAssert = require('../../lib/nodeify-assertions'); +var config = require("../lib/config"); +var helper = require('../helper'); var redis = config.redis; -var RedisProcess = require("../../lib/redis-process"); var uuid = require('uuid'); describe("The 'get' method", function () { - var rp; - before(function (done) { - RedisProcess.start(function (err, _rp) { - rp = _rp; - return done(err); - }); - }) - - function removeMochaListener () { - var mochaListener = process.listeners('uncaughtException').pop(); - process.removeListener('uncaughtException', mochaListener); - return mochaListener; - } - function allTests(parser, ip) { var args = config.configureClient(parser, ip); @@ -73,14 +58,14 @@ describe("The 'get' method", function () { describe("when the key exists in Redis", function () { beforeEach(function (done) { client.set(key, value, function (err, res) { - nodeAssert.isNotError()(err, res); + helper.isNotError()(err, res); done(); }); }); it("gets the value correctly", function (done) { client.get(key, function (err, res) { - nodeAssert.isString(value)(err, res); + helper.isString(value)(err, res); done(err); }); }); @@ -89,7 +74,7 @@ describe("The 'get' method", function () { describe("when the key does not exist in Redis", function () { it("gets a null value", function (done) { client.get(key, function (err, res) { - nodeAssert.isNull()(err, res); + helper.isNull()(err, res); done(err); }); }); @@ -104,8 +89,4 @@ describe("The 'get' method", function () { allTests(parser, ip); }) }); - - after(function (done) { - if (rp) rp.stop(done); - }); }); diff --git a/test/mocha/commands/getset.spec.js b/test/commands/getset.spec.js similarity index 77% rename from test/mocha/commands/getset.spec.js rename to test/commands/getset.spec.js index ba76384f2c..a8e90ecfa2 100644 --- a/test/mocha/commands/getset.spec.js +++ b/test/commands/getset.spec.js @@ -1,27 +1,12 @@ var async = require('async'); var assert = require('assert'); -var config = require("../../lib/config"); -var nodeAssert = require('../../lib/nodeify-assertions'); +var config = require("../lib/config"); +var helper = require('../helper'); var redis = config.redis; -var RedisProcess = require("../../lib/redis-process"); var uuid = require('uuid'); describe("The 'getset' method", function () { - var rp; - before(function (done) { - RedisProcess.start(function (err, _rp) { - rp = _rp; - return done(err); - }); - }) - - function removeMochaListener () { - var mochaListener = process.listeners('uncaughtException').pop(); - process.removeListener('uncaughtException', mochaListener); - return mochaListener; - } - function allTests(parser, ip) { var args = config.configureClient(parser, ip); @@ -74,16 +59,16 @@ describe("The 'getset' method", function () { describe("when the key exists in Redis", function () { beforeEach(function (done) { client.set(key, value, function (err, res) { - nodeAssert.isNotError()(err, res); + helper.isNotError()(err, res); done(); }); }); it("gets the value correctly", function (done) { client.getset(key, value2, function (err, res) { - nodeAssert.isString(value)(err, res); + helper.isString(value)(err, res); client.get(key, function (err, res) { - nodeAssert.isString(value2)(err, res); + helper.isString(value2)(err, res); done(err); }); }); @@ -93,7 +78,7 @@ describe("The 'getset' method", function () { describe("when the key does not exist in Redis", function () { it("gets a null value", function (done) { client.getset(key, value, function (err, res) { - nodeAssert.isNull()(err, res); + helper.isNull()(err, res); done(err); }); }); @@ -108,8 +93,4 @@ describe("The 'getset' method", function () { allTests(parser, ip); }) }); - - after(function (done) { - if (rp) rp.stop(done); - }); }); diff --git a/test/commands/hgetall.spec.js b/test/commands/hgetall.spec.js new file mode 100644 index 0000000000..c49988157e --- /dev/null +++ b/test/commands/hgetall.spec.js @@ -0,0 +1,91 @@ +var assert = require("assert"); +var config = require("../lib/config"); +var helper = require("../helper"); +var redis = config.redis; + +describe("The 'hgetall' method", function () { + + function allTests(parser, ip) { + describe("using " + parser + " and " + ip, function () { + var client; + + describe('regular client', function () { + var args = config.configureClient(parser, ip); + + beforeEach(function (done) { + client = redis.createClient.apply(redis.createClient, args); + client.once("error", done); + client.once("connect", function () { + client.flushdb(done); + }); + }); + + it('handles simple keys and values', function (done) { + client.hmset(["hosts", "mjr", "1", "another", "23", "home", "1234"], helper.isString("OK")); + client.HGETALL(["hosts"], function (err, obj) { + assert.strictEqual(3, Object.keys(obj).length); + assert.strictEqual("1", obj.mjr.toString()); + assert.strictEqual("23", obj.another.toString()); + assert.strictEqual("1234", obj.home.toString()); + return done(err); + }); + }); + + it('handles fetching keys set using an object', function (done) { + client.hmset("msg_test", {message: "hello"}, helper.isString("OK")); + client.hgetall("msg_test", function (err, obj) { + assert.strictEqual(1, Object.keys(obj).length); + assert.strictEqual(obj.message, "hello"); + return done(err); + }); + }); + + it('handles fetching a messing key', function (done) { + client.hgetall("missing", function (err, obj) { + assert.strictEqual(null, obj); + return done(err); + }); + }); + }); + + describe('binary client', function () { + var client; + var args = config.configureClient(parser, ip, { + return_buffers: true + }); + + beforeEach(function (done) { + client = redis.createClient.apply(redis.createClient, args); + client.once("error", done); + client.once("connect", function () { + client.flushdb(done); + }); + }); + + it('returns binary results', function (done) { + client.hmset(["bhosts", "mjr", "1", "another", "23", "home", "1234", new Buffer([0xAA, 0xBB, 0x00, 0xF0]), new Buffer([0xCC, 0xDD, 0x00, 0xF0])], helper.isString("OK")); + client.HGETALL(["bhosts"], function (err, obj) { + assert.strictEqual(4, Object.keys(obj).length); + assert.strictEqual("1", obj.mjr.toString()); + assert.strictEqual("23", obj.another.toString()); + assert.strictEqual("1234", obj.home.toString()); + assert.strictEqual((new Buffer([0xAA, 0xBB, 0x00, 0xF0])).toString('binary'), Object.keys(obj)[3]); + assert.strictEqual((new Buffer([0xCC, 0xDD, 0x00, 0xF0])).toString('binary'), obj[(new Buffer([0xAA, 0xBB, 0x00, 0xF0])).toString('binary')].toString('binary')); + return done(err); + }); + }); + }); + + afterEach(function () { + client.end(); + }); + }); + } + + ['javascript', 'hiredis'].forEach(function (parser) { + allTests(parser, "/tmp/redis.sock"); + ['IPv4', 'IPv6'].forEach(function (ip) { + allTests(parser, ip); + }) + }); +}); diff --git a/test/commands/hincrby.spec.js b/test/commands/hincrby.spec.js new file mode 100644 index 0000000000..f137405f78 --- /dev/null +++ b/test/commands/hincrby.spec.js @@ -0,0 +1,48 @@ +var assert = require("assert"); +var config = require("../lib/config"); +var helper = require("../helper"); +var redis = config.redis; + +describe("The 'hincrby' method", function () { + + function allTests(parser, ip) { + var args = config.configureClient(parser, ip); + + describe("using " + parser + " and " + ip, function () { + var client; + var hash = "test hash"; + + beforeEach(function (done) { + client = redis.createClient.apply(redis.createClient, args); + client.once("error", done); + client.once("connect", function () { + client.flushdb(done); + }); + }); + + it('increments a key that has already been set', function (done) { + var field = "field 1"; + + client.HSET(hash, field, 33); + client.HINCRBY(hash, field, 10, helper.isNumber(43, done)); + }); + + it('increments a key that has not been set', function (done) { + var field = "field 2"; + + client.HINCRBY(hash, field, 10, helper.isNumber(10, done)); + }); + + afterEach(function () { + client.end(); + }); + }); + } + + ['javascript', 'hiredis'].forEach(function (parser) { + allTests(parser, "/tmp/redis.sock"); + ['IPv4', 'IPv6'].forEach(function (ip) { + allTests(parser, ip); + }) + }); +}); diff --git a/test/commands/hlen.spec.js b/test/commands/hlen.spec.js new file mode 100644 index 0000000000..11905809c1 --- /dev/null +++ b/test/commands/hlen.spec.js @@ -0,0 +1,46 @@ +var assert = require("assert"); +var config = require("../lib/config"); +var helper = require("../helper"); +var redis = config.redis; + +describe("The 'hlen' method", function () { + + function allTests(parser, ip) { + var args = config.configureClient(parser, ip); + + describe("using " + parser + " and " + ip, function () { + var client; + + beforeEach(function (done) { + client = redis.createClient.apply(redis.createClient, args); + client.once("error", done); + client.once("connect", function () { + client.flushdb(done); + }); + }); + + it('reports the count of keys', function (done) { + var hash = "test hash"; + var field1 = new Buffer("0123456789"); + var value1 = new Buffer("abcdefghij"); + var field2 = new Buffer(0); + var value2 = new Buffer(0); + + client.HSET(hash, field1, value1, helper.isNumber(1)); + client.HSET(hash, field2, value2, helper.isNumber(1)); + client.HLEN(hash, helper.isNumber(2, done)); + }); + + afterEach(function () { + client.end(); + }); + }); + } + + ['javascript', 'hiredis'].forEach(function (parser) { + allTests(parser, "/tmp/redis.sock"); + ['IPv4', 'IPv6'].forEach(function (ip) { + allTests(parser, ip); + }) + }); +}); diff --git a/test/commands/hmget.spec.js b/test/commands/hmget.spec.js new file mode 100644 index 0000000000..fbced263bb --- /dev/null +++ b/test/commands/hmget.spec.js @@ -0,0 +1,68 @@ +var assert = require("assert"); +var config = require("../lib/config"); +var helper = require("../helper"); +var redis = config.redis; + +describe("The 'hmget' method", function () { + + function allTests(parser, ip) { + var args = config.configureClient(parser, ip); + + describe("using " + parser + " and " + ip, function () { + var client; + var hash = 'test hash'; + + beforeEach(function (done) { + client = redis.createClient.apply(redis.createClient, args); + client.once("error", done); + client.once("connect", function () { + client.flushdb(); + client.HMSET(hash, {"0123456789": "abcdefghij", "some manner of key": "a type of value"}, helper.isString('OK')); + return done(); + }); + }); + + it('allows keys to be specified using multiple arguments', function (done) { + client.HMGET(hash, "0123456789", "some manner of key", function (err, reply) { + assert.strictEqual("abcdefghij", reply[0].toString()); + assert.strictEqual("a type of value", reply[1].toString()); + return done(err); + }); + }); + + it('allows keys to be specified by passing an array', function (done) { + client.HMGET(hash, ["0123456789", "some manner of key"], function (err, reply) { + assert.strictEqual("abcdefghij", reply[0].toString()); + assert.strictEqual("a type of value", reply[1].toString()); + return done(err); + }); + }); + + it('allows a single key to be specified in an array', function (done) { + client.HMGET(hash, ["0123456789"], function (err, reply) { + assert.strictEqual("abcdefghij", reply[0].toString()); + return done(err); + }); + }); + + it('allows keys to be specified that have not yet been set', function (done) { + client.HMGET(hash, "missing thing", "another missing thing", function (err, reply) { + assert.strictEqual(null, reply[0]); + assert.strictEqual(null, reply[1]); + return done(err); + }); + }); + + afterEach(function () { + client.end(); + }); + }); + } + + ['javascript', 'hiredis'].forEach(function (parser) { + allTests(parser, "/tmp/redis.sock"); + ['IPv4', 'IPv6'].forEach(function (ip) { + allTests(parser, ip); + }) + }); +}); diff --git a/test/commands/hmset.spec.js b/test/commands/hmset.spec.js new file mode 100644 index 0000000000..d0f23eb066 --- /dev/null +++ b/test/commands/hmset.spec.js @@ -0,0 +1,61 @@ +var assert = require("assert"); +var config = require("../lib/config"); +var helper = require("../helper"); +var redis = config.redis; + +describe("The 'hmset' method", function () { + + function allTests(parser, ip) { + var args = config.configureClient(parser, ip); + + describe("using " + parser + " and " + ip, function () { + var client; + var hash = 'test hash'; + + beforeEach(function (done) { + client = redis.createClient.apply(redis.createClient, args); + client.once("error", done); + client.once("connect", function () { + client.flushdb(done); + }); + }); + + it('handles redis-style syntax', function (done) { + client.HMSET(hash, "0123456789", "abcdefghij", "some manner of key", "a type of value", helper.isString('OK')); + client.HGETALL(hash, function (err, obj) { + assert.equal(obj['0123456789'], 'abcdefghij'); + assert.equal(obj['some manner of key'], 'a type of value'); + return done(err); + }) + }); + + it('handles object-style syntax', function (done) { + client.HMSET(hash, {"0123456789": "abcdefghij", "some manner of key": "a type of value"}, helper.isString('OK')); + client.HGETALL(hash, function (err, obj) { + assert.equal(obj['0123456789'], 'abcdefghij'); + assert.equal(obj['some manner of key'], 'a type of value'); + return done(err); + }) + }); + + it('allows a numeric key', function (done) { + client.HMSET(hash, 99, 'banana', helper.isString('OK')); + client.HGETALL(hash, function (err, obj) { + assert.equal(obj['99'], 'banana'); + return done(err); + }); + }); + + afterEach(function () { + client.end(); + }); + }); + } + + ['javascript', 'hiredis'].forEach(function (parser) { + allTests(parser, "/tmp/redis.sock"); + ['IPv4', 'IPv6'].forEach(function (ip) { + allTests(parser, ip); + }) + }); +}); diff --git a/test/commands/hset.spec.js b/test/commands/hset.spec.js new file mode 100644 index 0000000000..0ce2432ea9 --- /dev/null +++ b/test/commands/hset.spec.js @@ -0,0 +1,70 @@ +var assert = require("assert"); +var config = require("../lib/config"); +var helper = require("../helper"); +var redis = config.redis; + +describe("The 'hset' method", function () { + + function allTests(parser, ip) { + var args = config.configureClient(parser, ip); + + describe("using " + parser + " and " + ip, function () { + var client; + var hash = 'test hash'; + + beforeEach(function (done) { + client = redis.createClient.apply(redis.createClient, args); + client.once("error", done); + client.once("connect", function () { + client.flushdb(done); + }); + }); + + it('allows a value to be set in a hash', function (done) { + var field = new Buffer("0123456789"); + var value = new Buffer("abcdefghij"); + + client.HSET(hash, field, value, helper.isNumber(1)); + client.HGET(hash, field, helper.isString(value.toString(), done)); + }); + + it('handles an empty value', function (done) { + var field = new Buffer("0123456789"); + var value = new Buffer(0); + + client.HSET(hash, field, value, helper.isNumber(1)); + client.HGET([hash, field], helper.isString("", done)); + }); + + it('handles empty key and value', function (done) { + var field = new Buffer(0); + var value = new Buffer(0); + client.HSET([hash, field, value], function (err, res) { + assert.strictEqual(res, 1); + client.HSET(hash, field, value, helper.isNumber(0, done)); + }); + }); + + it('does not error when a buffer and array are set as fields on the same hash', function (done) { + var hash = "test hash" + var field1 = "buffer" + var value1 = new Buffer("abcdefghij") + var field2 = "array" + var value2 = ["array contents"] + + client.HMSET(hash, field1, value1, field2, value2, helper.isString("OK", done)); + }); + + afterEach(function () { + client.end(); + }); + }); + } + + ['javascript', 'hiredis'].forEach(function (parser) { + allTests(parser, "/tmp/redis.sock"); + ['IPv4', 'IPv6'].forEach(function (ip) { + allTests(parser, ip); + }) + }); +}); diff --git a/test/mocha/commands/incr.spec.js b/test/commands/incr.spec.js similarity index 79% rename from test/mocha/commands/incr.spec.js rename to test/commands/incr.spec.js index ae4534dd30..bdd22bacf1 100644 --- a/test/mocha/commands/incr.spec.js +++ b/test/commands/incr.spec.js @@ -1,27 +1,12 @@ var async = require('async'); var assert = require('assert'); -var config = require("../../lib/config"); -var nodeAssert = require('../../lib/nodeify-assertions'); +var config = require("../lib/config"); +var helper = require('../helper'); var redis = config.redis; -var RedisProcess = require("../../lib/redis-process"); var uuid = require('uuid'); describe("The 'incr' method", function () { - var rp; - before(function (done) { - RedisProcess.start(function (err, _rp) { - rp = _rp; - return done(err); - }); - }) - - function removeMochaListener () { - var mochaListener = process.listeners('uncaughtException').pop(); - process.removeListener('uncaughtException', mochaListener); - return mochaListener; - } - function allTests(parser, ip) { var args = config.configureClient(parser, ip); @@ -36,7 +21,7 @@ describe("The 'incr' method", function () { client.once("error", done); client.once("connect", function () { client.set(key, "9007199254740992", function (err, res) { - nodeAssert.isNotError()(err, res); + helper.isNotError()(err, res); client.quit(); }); }); @@ -45,6 +30,10 @@ describe("The 'incr' method", function () { }); }); + afterEach(function () { + client.end(); + }); + it("reports an error", function (done) { client.incr(function (err, res) { assert.equal(err.message, 'Redis connection gone from end event.'); @@ -70,7 +59,7 @@ describe("The 'incr' method", function () { client.once("error", done); client.once("connect", function () { client.set(key, "9007199254740992", function (err, res) { - nodeAssert.isNotError()(err, res); + helper.isNotError()(err, res); done(); }); }); @@ -82,7 +71,7 @@ describe("The 'incr' method", function () { it("changes the last digit from 2 to 3", function (done) { client.incr(key, function (err, res) { - nodeAssert.isString("9007199254740993")(err, res); + helper.isString("9007199254740993")(err, res); done(err); }); }); @@ -90,7 +79,7 @@ describe("The 'incr' method", function () { describe("and we call it again", function () { it("changes the last digit from 3 to 4", function (done) { client.incr(key, function (err, res) { - nodeAssert.isString("9007199254740994")(err, res); + helper.isString("9007199254740994")(err, res); done(err); }); }); @@ -98,7 +87,7 @@ describe("The 'incr' method", function () { describe("and again", function () { it("changes the last digit from 4 to 5", function (done) { client.incr(key, function (err, res) { - nodeAssert.isString("9007199254740995")(err, res); + helper.isString("9007199254740995")(err, res); done(err); }); }); @@ -106,7 +95,7 @@ describe("The 'incr' method", function () { describe("and again", function () { it("changes the last digit from 5 to 6", function (done) { client.incr(key, function (err, res) { - nodeAssert.isString("9007199254740996")(err, res); + helper.isString("9007199254740996")(err, res); done(err); }); }); @@ -114,7 +103,7 @@ describe("The 'incr' method", function () { describe("and again", function () { it("changes the last digit from 6 to 7", function (done) { client.incr(key, function (err, res) { - nodeAssert.isString("9007199254740997")(err, res); + helper.isString("9007199254740997")(err, res); done(err); }); }); @@ -132,8 +121,4 @@ describe("The 'incr' method", function () { allTests(parser, ip); }) }); - - after(function (done) { - if (rp) rp.stop(done); - }); }); diff --git a/test/commands/keys.spec.js b/test/commands/keys.spec.js new file mode 100644 index 0000000000..b1f5f9cb51 --- /dev/null +++ b/test/commands/keys.spec.js @@ -0,0 +1,76 @@ +var assert = require("assert"); +var config = require("../lib/config"); +var crypto = require("crypto"); +var helper = require("../helper"); +var redis = config.redis; + +describe("The 'keys' method", function () { + + function allTests(parser, ip) { + var args = config.configureClient(parser, ip); + + describe("using " + parser + " and " + ip, function () { + var client; + + beforeEach(function (done) { + client = redis.createClient.apply(redis.createClient, args); + client.once("error", done); + client.once("connect", function () { + client.flushdb(done); + }); + }); + + it('returns matching keys', function (done) { + client.mset(["test keys 1", "test val 1", "test keys 2", "test val 2"], helper.isString("OK")); + client.KEYS(["test keys*"], function (err, results) { + assert.strictEqual(2, results.length); + assert.ok(~results.indexOf("test keys 1")); + assert.ok(~results.indexOf("test keys 2")); + return done(err); + }); + }); + + it('handles a large packet size', function (done) { + var keys_values = []; + + for (var i = 0; i < 200; i++) { + var key_value = [ + "multibulk:" + crypto.randomBytes(256).toString("hex"), // use long strings as keys to ensure generation of large packet + "test val " + i + ]; + keys_values.push(key_value); + } + + client.mset(keys_values.reduce(function(a, b) { + return a.concat(b); + }), helper.isString("OK")); + + client.KEYS("multibulk:*", function(err, results) { + assert.deepEqual(keys_values.map(function(val) { + return val[0]; + }).sort(), results.sort()); + return done(err); + }); + }); + + it('handles an empty response', function (done) { + client.KEYS(['users:*'], function (err, results) { + assert.strictEqual(results.length, 0); + assert.ok(Array.isArray(results)); + return done(err); + }); + }); + + afterEach(function () { + client.end(); + }); + }); + } + + ['javascript', 'hiredis'].forEach(function (parser) { + allTests(parser, "/tmp/redis.sock"); + ['IPv4', 'IPv6'].forEach(function (ip) { + allTests(parser, ip); + }) + }); +}); diff --git a/test/commands/mget.spec.js b/test/commands/mget.spec.js new file mode 100644 index 0000000000..1169b5949f --- /dev/null +++ b/test/commands/mget.spec.js @@ -0,0 +1,66 @@ +var assert = require("assert"); +var config = require("../lib/config"); +var helper = require("../helper"); +var redis = config.redis; + +describe("The 'mget' method", function () { + + function allTests(parser, ip) { + var args = config.configureClient(parser, ip); + + describe("using " + parser + " and " + ip, function () { + var client; + + beforeEach(function (done) { + client = redis.createClient.apply(redis.createClient, args); + client.once("error", done); + client.once("connect", function () { + client.flushdb(); + client.mset(["mget keys 1", "mget val 1", "mget keys 2", "mget val 2", "mget keys 3", "mget val 3"], done); + }); + }); + + it('handles fetching multiple keys in argument form', function (done) { + client.mset(["mget keys 1", "mget val 1", "mget keys 2", "mget val 2", "mget keys 3", "mget val 3"], helper.isString("OK")); + client.MGET("mget keys 1", "mget keys 2", "mget keys 3", function (err, results) { + assert.strictEqual(3, results.length); + assert.strictEqual("mget val 1", results[0].toString()); + assert.strictEqual("mget val 2", results[1].toString()); + assert.strictEqual("mget val 3", results[2].toString()); + return done(err); + }); + }); + + it('handles fetching multiple keys via an array', function (done) { + client.MGET(["mget keys 1", "mget keys 2", "mget keys 3"], function (err, results) { + assert.strictEqual("mget val 1", results[0].toString()); + assert.strictEqual("mget val 2", results[1].toString()); + assert.strictEqual("mget val 3", results[2].toString()); + return done(err); + }); + }); + + it('handles fetching multiple keys, when some keys do not exist', function (done) { + client.MGET(["mget keys 1", "some random shit", "mget keys 2", "mget keys 3"], function (err, results) { + assert.strictEqual(4, results.length); + assert.strictEqual("mget val 1", results[0].toString()); + assert.strictEqual(null, results[1]); + assert.strictEqual("mget val 2", results[2].toString()); + assert.strictEqual("mget val 3", results[3].toString()); + return done(err); + }); + }); + + afterEach(function () { + client.end(); + }); + }); + } + + ['javascript', 'hiredis'].forEach(function (parser) { + allTests(parser, "/tmp/redis.sock"); + ['IPv4', 'IPv6'].forEach(function (ip) { + allTests(parser, ip); + }) + }); +}); diff --git a/test/mocha/commands/mset.spec.js b/test/commands/mset.spec.js similarity index 85% rename from test/mocha/commands/mset.spec.js rename to test/commands/mset.spec.js index b1f4f367b0..1523797a3c 100644 --- a/test/mocha/commands/mset.spec.js +++ b/test/commands/mset.spec.js @@ -1,21 +1,12 @@ var async = require('async'); var assert = require('assert'); -var config = require("../../lib/config"); -var nodeAssert = require('../../lib/nodeify-assertions'); +var config = require("../lib/config"); +var helper = require('../helper'); var redis = config.redis; -var RedisProcess = require("../../lib/redis-process"); var uuid = require('uuid'); describe("The 'mset' method", function () { - var rp; - before(function (done) { - RedisProcess.start(function (err, _rp) { - rp = _rp; - return done(err); - }); - }) - function removeMochaListener () { var mochaListener = process.listeners('uncaughtException').pop(); process.removeListener('uncaughtException', mochaListener); @@ -76,15 +67,15 @@ describe("The 'mset' method", function () { describe("with valid parameters", function () { it("sets the value correctly", function (done) { client.mset(key, value, key2, value2, function (err, res) { - nodeAssert.isNotError()(err, res); + helper.isNotError()(err, res); async.parallel([function (next) { client.get(key, function (err, res) { - nodeAssert.isString(value)(err, res); + helper.isString(value)(err, res); next(); }); }, function (next) { client.get(key2, function (err, res) { - nodeAssert.isString(value2)(err, res); + helper.isString(value2)(err, res); next(); }); }], function (err) { @@ -97,7 +88,7 @@ describe("The 'mset' method", function () { describe("with undefined 'key' parameter and missing 'value' parameter", function () { it("reports an error", function (done) { client.mset(undefined, function (err, res) { - nodeAssert.isError()(err, null); + helper.isError()(err, null); done(); }); }); @@ -106,7 +97,7 @@ describe("The 'mset' method", function () { describe("with undefined 'key' and defined 'value' parameters", function () { it("reports an error", function () { client.mset(undefined, value, undefined, value2, function (err, res) { - nodeAssert.isError()(err, null); + helper.isError()(err, null); done(); }); }); @@ -121,12 +112,12 @@ describe("The 'mset' method", function () { setTimeout(function () { async.parallel([function (next) { client.get(key, function (err, res) { - nodeAssert.isString(value)(err, res); + helper.isString(value)(err, res); next(); }); }, function (next) { client.get(key2, function (err, res) { - nodeAssert.isString(value2)(err, res); + helper.isString(value2)(err, res); next(); }); }], function (err) { @@ -143,7 +134,7 @@ describe("The 'mset' method", function () { process.once('uncaughtException', function (err) { process.on('uncaughtException', mochaListener); - nodeAssert.isError()(err, null); + helper.isError()(err, null); return done(); }); @@ -157,7 +148,7 @@ describe("The 'mset' method", function () { process.once('uncaughtException', function (err) { process.on('uncaughtException', mochaListener); - nodeAssert.isError()(err, null); + helper.isError()(err, null); }); client.mset(undefined, value, undefined, value2); @@ -174,8 +165,4 @@ describe("The 'mset' method", function () { allTests(parser, ip); }) }); - - after(function (done) { - if (rp) rp.stop(done); - }); }); diff --git a/test/commands/msetnx.spec.js b/test/commands/msetnx.spec.js new file mode 100644 index 0000000000..ddbb893c1a --- /dev/null +++ b/test/commands/msetnx.spec.js @@ -0,0 +1,46 @@ +var assert = require("assert"); +var config = require("../lib/config"); +var helper = require("../helper"); +var redis = config.redis; + +describe("The 'msetnx' method", function () { + + function allTests(parser, ip) { + var args = config.configureClient(parser, ip); + + describe("using " + parser + " and " + ip, function () { + var client; + + beforeEach(function (done) { + client = redis.createClient.apply(redis.createClient, args); + client.once("error", done); + client.once("connect", function () { + client.flushdb(done); + }); + }); + + it('if any keys exist entire operation fails', function (done) { + client.mset(["mset1", "val1", "mset2", "val2", "mset3", "val3"], helper.isString("OK")); + client.MSETNX(["mset3", "val3", "mset4", "val4"], helper.isNumber(0)); + client.exists(["mset4"], helper.isNumber(0, done)); + }); + + it('sets multiple keys if all keys are not set', function (done) { + client.MSETNX(["mset3", "val3", "mset4", "val4"], helper.isNumber(1)); + client.exists(["mset3"], helper.isNumber(1)); + client.exists(["mset3"], helper.isNumber(1, done)); + }); + + afterEach(function () { + client.end(); + }); + }); + } + + ['javascript', 'hiredis'].forEach(function (parser) { + allTests(parser, "/tmp/redis.sock"); + ['IPv4', 'IPv6'].forEach(function (ip) { + allTests(parser, ip); + }) + }); +}); diff --git a/test/mocha/commands/multi.spec.js b/test/commands/multi.spec.js similarity index 82% rename from test/mocha/commands/multi.spec.js rename to test/commands/multi.spec.js index ba8be4fcbf..21526334a1 100644 --- a/test/mocha/commands/multi.spec.js +++ b/test/commands/multi.spec.js @@ -1,27 +1,12 @@ var async = require('async'); var assert = require('assert'); -var config = require("../../lib/config"); -var nodeAssert = require('../../lib/nodeify-assertions'); +var config = require("../lib/config"); +var helper = require('../helper'); var redis = config.redis; -var RedisProcess = require("../../lib/redis-process"); var uuid = require('uuid'); describe("The 'multi' method", function () { - var rp; - before(function (done) { - RedisProcess.start(function (err, _rp) { - rp = _rp; - return done(err); - }); - }) - - function removeMochaListener () { - var mochaListener = process.listeners('uncaughtException').pop(); - process.removeListener('uncaughtException', mochaListener); - return mochaListener; - } - function allTests(parser, ip) { var args = config.configureClient(parser, ip); @@ -63,7 +48,7 @@ describe("The 'multi' method", function () { client = redis.createClient.apply(redis.createClient, args); client.once("error", done); client.once("connect", function () { - client.del('multifoo', 'multibar', 'multifoo_8', 'multibar_8', function (err) { + client.flushdb(function (err) { return done(err); }) }); @@ -78,24 +63,24 @@ describe("The 'multi' method", function () { // Provoke an error at queue time multi1 = client.multi(); - multi1.mset("multifoo", "10", "multibar", "20", nodeAssert.isString("OK")); - multi1.set("foo2", nodeAssert.isError()); - multi1.incr("multifoo", nodeAssert.isNumber(11)); - multi1.incr("multibar", nodeAssert.isNumber(21)); + multi1.mset("multifoo", "10", "multibar", "20", helper.isString("OK")); + multi1.set("foo2", helper.isError()); + multi1.incr("multifoo", helper.isNumber(11)); + multi1.incr("multibar", helper.isNumber(21)); multi1.exec(function () { // Redis 2.6.5+ will abort transactions with errors // see: http://redis.io/topics/transactions var multibar_expected = 22; var multifoo_expected = 12; - if (nodeAssert.serverVersionAtLeast(client, [2, 6, 5])) { + if (helper.serverVersionAtLeast(client, [2, 6, 5])) { multibar_expected = 1; multifoo_expected = 1; } // Confirm that the previous command, while containing an error, still worked. multi2 = client.multi(); - multi2.incr("multibar", nodeAssert.isNumber(multibar_expected)); - multi2.incr("multifoo", nodeAssert.isNumber(multifoo_expected)); + multi2.incr("multibar", helper.isNumber(multibar_expected)); + multi2.incr("multifoo", helper.isNumber(multifoo_expected)); multi2.exec(function (err, replies) { assert.strictEqual(multibar_expected, replies[0]); assert.strictEqual(multifoo_expected, replies[1]); @@ -108,25 +93,25 @@ describe("The 'multi' method", function () { // perhaps @mranney can clarify? it('roles back a transaction when an error was provoked at queue time', function (done) { multi1 = client.multi(); - multi1.mset("multifoo_8", "10", "multibar_8", "20", nodeAssert.isString("OK")); - multi1.set("foo2", nodeAssert.isError()); - multi1.set("foo3", nodeAssert.isError()); - multi1.incr("multifoo_8", nodeAssert.isNumber(11)); - multi1.incr("multibar_8", nodeAssert.isNumber(21)); + multi1.mset("multifoo_8", "10", "multibar_8", "20", helper.isString("OK")); + multi1.set("foo2", helper.isError()); + multi1.set("foo3", helper.isError()); + multi1.incr("multifoo_8", helper.isNumber(11)); + multi1.incr("multibar_8", helper.isNumber(21)); multi1.exec(function () { // Redis 2.6.5+ will abort transactions with errors // see: http://redis.io/topics/transactions var multibar_expected = 22; var multifoo_expected = 12; - if (nodeAssert.serverVersionAtLeast(client, [2, 6, 5])) { + if (helper.serverVersionAtLeast(client, [2, 6, 5])) { multibar_expected = 1; multifoo_expected = 1; } // Confirm that the previous command, while containing an error, still worked. multi2 = client.multi(); - multi2.incr("multibar_8", nodeAssert.isNumber(multibar_expected)); - multi2.incr("multifoo_8", nodeAssert.isNumber(multifoo_expected)); + multi2.incr("multibar_8", helper.isNumber(multibar_expected)); + multi2.incr("multifoo_8", helper.isNumber(multifoo_expected)); multi2.exec(function (err, replies) { assert.strictEqual(multibar_expected, replies[0]); assert.strictEqual(multifoo_expected, replies[1]); @@ -143,11 +128,11 @@ describe("The 'multi' method", function () { assert.strictEqual("0", res[0].toString()); assert.strictEqual("0", res[1].toString()); }], - ["set", "foo2", nodeAssert.isError()], - ["incr", "multifoo", nodeAssert.isNumber(1)], - ["incr", "multibar", nodeAssert.isNumber(1)] + ["set", "foo2", helper.isError()], + ["incr", "multifoo", helper.isNumber(1)], + ["incr", "multibar", helper.isNumber(1)] ]).exec(function (err, replies) { - if (nodeAssert.serverVersionAtLeast(client, [2, 6, 5])) { + if (helper.serverVersionAtLeast(client, [2, 6, 5])) { assert.notEqual(err, null); assert.equal(replies, undefined); } else { @@ -241,7 +226,7 @@ describe("The 'multi' method", function () { }); it('reports multiple exceptions when they occur', function (done) { - if (!nodeAssert.serverVersionAtLeast(client, [2, 6, 5])) return done(); + if (!helper.serverVersionAtLeast(client, [2, 6, 5])) return done(); client.multi().set("foo").exec(function (err, reply) { assert(Array.isArray(err), "err should be an array"); @@ -262,8 +247,4 @@ describe("The 'multi' method", function () { allTests(parser, ip); }) }); - - after(function (done) { - if (rp) rp.stop(done); - }); }); diff --git a/test/commands/randomkey.test.js b/test/commands/randomkey.test.js new file mode 100644 index 0000000000..7549800394 --- /dev/null +++ b/test/commands/randomkey.test.js @@ -0,0 +1,42 @@ +var assert = require("assert"); +var config = require("../lib/config"); +var helper = require("../helper"); +var redis = config.redis; + +describe("The 'randomkey' method", function () { + + function allTests(parser, ip) { + var args = config.configureClient(parser, ip); + + describe("using " + parser + " and " + ip, function () { + var client; + + beforeEach(function (done) { + client = redis.createClient.apply(redis.createClient, args); + client.once("error", done); + client.once("connect", function () { + client.flushdb(done); + }); + }); + + it('returns a random key', function (done) { + client.mset(["test keys 1", "test val 1", "test keys 2", "test val 2"], helper.isString('OK')); + client.RANDOMKEY([], function (err, results) { + assert.strictEqual(true, /test keys.+/.test(results)); + return done(err); + }); + }); + + afterEach(function () { + client.end(); + }); + }); + } + + ['javascript', 'hiredis'].forEach(function (parser) { + allTests(parser, "/tmp/redis.sock"); + ['IPv4', 'IPv6'].forEach(function (ip) { + allTests(parser, ip); + }) + }); +}); diff --git a/test/commands/rename.spec.js b/test/commands/rename.spec.js new file mode 100644 index 0000000000..be2145923d --- /dev/null +++ b/test/commands/rename.spec.js @@ -0,0 +1,46 @@ +var assert = require("assert"); +var config = require("../lib/config"); +var helper = require("../helper"); +var redis = config.redis; + +describe("The 'rename' method", function () { + + function allTests(parser, ip) { + var args = config.configureClient(parser, ip); + + describe("using " + parser + " and " + ip, function () { + var client; + + beforeEach(function (done) { + client = redis.createClient.apply(redis.createClient, args); + client.once("error", done); + client.once("connect", function () { + client.flushdb(done); + }); + }); + + it('populates the new key', function (done) { + client.set(['foo', 'bar'], helper.isString("OK")); + client.RENAME(["foo", "new foo"], helper.isString("OK")); + client.exists(["new foo"], helper.isNumber(1, done)); + }); + + it('removes the old key', function (done) { + client.set(['foo', 'bar'], helper.isString("OK")); + client.RENAME(["foo", "new foo"], helper.isString("OK")); + client.exists(["foo"], helper.isNumber(0, done)); + }); + + afterEach(function () { + client.end(); + }); + }); + } + + ['javascript', 'hiredis'].forEach(function (parser) { + allTests(parser, "/tmp/redis.sock"); + ['IPv4', 'IPv6'].forEach(function (ip) { + allTests(parser, ip); + }) + }); +}); diff --git a/test/commands/renamenx.spec.js b/test/commands/renamenx.spec.js new file mode 100644 index 0000000000..6dd1a1b0d6 --- /dev/null +++ b/test/commands/renamenx.spec.js @@ -0,0 +1,49 @@ +var assert = require("assert"); +var config = require("../lib/config"); +var helper = require("../helper"); +var redis = config.redis; + +describe("The 'renamenx' method", function () { + + function allTests(parser, ip) { + var args = config.configureClient(parser, ip); + + describe("using " + parser + " and " + ip, function () { + var client; + + beforeEach(function (done) { + client = redis.createClient.apply(redis.createClient, args); + client.once("error", done); + client.once("connect", function () { + client.flushdb(done); + }); + }); + + it('renames the key if target does not yet exist', function (done) { + client.set('foo', 'bar', helper.isString('OK')); + client.renamenx('foo', 'foo2', helper.isNumber(1)); + client.exists('foo', helper.isNumber(0)); + client.exists(['foo2'], helper.isNumber(1, done)); + }); + + it('does not rename the key if the target exists', function (done) { + client.set('foo', 'bar', helper.isString('OK')); + client.set('foo2', 'apple', helper.isString('OK')); + client.renamenx('foo', 'foo2', helper.isNumber(0)); + client.exists('foo', helper.isNumber(1)); + client.exists(['foo2'], helper.isNumber(1, done)); + }) + + afterEach(function () { + client.end(); + }); + }); + } + + ['javascript', 'hiredis'].forEach(function (parser) { + allTests(parser, "/tmp/redis.sock"); + ['IPv4', 'IPv6'].forEach(function (ip) { + allTests(parser, ip); + }) + }); +}); diff --git a/test/commands/sadd.spec.js b/test/commands/sadd.spec.js new file mode 100644 index 0000000000..ea63a637ea --- /dev/null +++ b/test/commands/sadd.spec.js @@ -0,0 +1,58 @@ +var assert = require("assert"); +var config = require("../lib/config"); +var helper = require("../helper"); +var redis = config.redis; + +describe("The 'sadd' method", function () { + + function allTests(parser, ip) { + var args = config.configureClient(parser, ip); + + describe("using " + parser + " and " + ip, function () { + var client; + + beforeEach(function (done) { + client = redis.createClient.apply(redis.createClient, args); + client.once("error", done); + client.once("connect", function () { + client.flushdb(done); + }); + }); + + it('allows a single value to be added to the set', function (done) { + client.SADD('set0', 'member0', helper.isNumber(1)); + client.smembers('set0', function (err, res) { + assert.ok(~res.indexOf('member0')); + return done(err); + }); + }); + + it('does not add the same value to the set twice', function (done) { + client.sadd('set0', 'member0', helper.isNumber(1)); + client.SADD('set0', 'member0', helper.isNumber(0, done)); + }); + + it('allows multiple values to be added to the set', function (done) { + client.sadd("set0", ["member0", "member1", "member2"], helper.isNumber(3)); + client.smembers("set0", function (err, res) { + assert.strictEqual(res.length, 3); + assert.ok(~res.indexOf("member0")); + assert.ok(~res.indexOf("member1")); + assert.ok(~res.indexOf("member2")); + return done(err); + }); + }); + + afterEach(function () { + client.end(); + }); + }); + } + + ['javascript', 'hiredis'].forEach(function (parser) { + allTests(parser, "/tmp/redis.sock"); + ['IPv4', 'IPv6'].forEach(function (ip) { + allTests(parser, ip); + }) + }); +}); diff --git a/test/commands/scard.spec.js b/test/commands/scard.spec.js new file mode 100644 index 0000000000..4030e27b95 --- /dev/null +++ b/test/commands/scard.spec.js @@ -0,0 +1,39 @@ +var assert = require("assert"); +var config = require("../lib/config"); +var helper = require("../helper"); +var redis = config.redis; + +describe("The 'scard' method", function () { + + function allTests(parser, ip) { + var args = config.configureClient(parser, ip); + + describe("using " + parser + " and " + ip, function () { + var client; + + beforeEach(function (done) { + client = redis.createClient.apply(redis.createClient, args); + client.once("error", done); + client.once("connect", function () { + client.flushdb(done); + }); + }); + + it('returns the number of values in a set', function (done) { + client.sadd('foo', [1, 2, 3], helper.isNumber(3)); + client.scard('foo', helper.isNumber(3, done)); + }); + + afterEach(function () { + client.end(); + }); + }); + } + + ['javascript', 'hiredis'].forEach(function (parser) { + allTests(parser, "/tmp/redis.sock"); + ['IPv4', 'IPv6'].forEach(function (ip) { + allTests(parser, ip); + }) + }); +}); diff --git a/test/commands/script.spec.js b/test/commands/script.spec.js new file mode 100644 index 0000000000..df3d8aa486 --- /dev/null +++ b/test/commands/script.spec.js @@ -0,0 +1,68 @@ +var assert = require("assert"); +var config = require("../lib/config"); +var crypto = require("crypto"); +var helper = require("../helper"); +var redis = config.redis; + +describe("The 'script' method", function () { + + function allTests(parser, ip) { + var args = config.configureClient(parser, ip); + var command = "return 99"; + var commandSha = crypto.createHash('sha1').update(command).digest('hex'); + + describe("using " + parser + " and " + ip, function () { + var client; + + beforeEach(function (done) { + client = redis.createClient.apply(redis.createClient, args); + client.once("error", done); + client.once("connect", function () { + client.flushdb(function (err) { + if (!helper.serverVersionAtLeast(client, [2, 6, 0])) { + err = Error('script not supported in redis <= 2.6.0') + } + return done(err); + + }) + }); + }); + + afterEach(function () { + client.end(); + }); + + it("loads script with client.script('load')", function (done) { + client.script("load", command, function(err, result) { + assert.strictEqual(result, commandSha); + return done(); + }); + }); + + it('allows a loaded script to be evaluated', function (done) { + client.evalsha(commandSha, 0, helper.isString('99', done)); + }) + + it('allows a script to be loaded as part of a chained transaction', function (done) { + client.multi().script("load", command).exec(function(err, result) { + assert.strictEqual(result[0], commandSha); + return done() + }) + }) + + it("allows a script to be loaded using a transaction's array syntax", function (done) { + client.multi([['script', 'load', command]]).exec(function(err, result) { + assert.strictEqual(result[0], commandSha); + return done() + }) + }) + }); + } + + ['javascript', 'hiredis'].forEach(function (parser) { + allTests(parser, "/tmp/redis.sock"); + ['IPv4', 'IPv6'].forEach(function (ip) { + allTests(parser, ip); + }) + }); +}); diff --git a/test/mocha/commands/select.spec.js b/test/commands/select.spec.js similarity index 92% rename from test/mocha/commands/select.spec.js rename to test/commands/select.spec.js index 24c1d159b8..1be845dc00 100644 --- a/test/mocha/commands/select.spec.js +++ b/test/commands/select.spec.js @@ -1,20 +1,11 @@ var async = require('async'); var assert = require('assert'); -var config = require("../../lib/config"); -var nodeAssert = require('../../lib/nodeify-assertions'); +var config = require("../lib/config"); +var helper = require('../helper'); var redis = config.redis; -var RedisProcess = require("../../lib/redis-process"); describe("The 'select' method", function () { - var rp; - before(function (done) { - RedisProcess.start(function (err, _rp) { - rp = _rp; - return done(err); - }); - }) - function removeMochaListener () { var mochaListener = process.listeners('uncaughtException').pop(); process.removeListener('uncaughtException', mochaListener); @@ -64,7 +55,7 @@ describe("The 'select' method", function () { // default value of null means database 0 will be used. assert.strictEqual(client.selected_db, null, "default db should be null"); client.select(1, function (err, res) { - nodeAssert.isNotError()(err, res); + helper.isNotError()(err, res); assert.strictEqual(client.selected_db, 1, "db should be 1 after select"); done(); }); @@ -129,8 +120,4 @@ describe("The 'select' method", function () { allTests(parser, ip); }) }); - - after(function (done) { - if (rp) rp.stop(done); - }); }); diff --git a/test/mocha/commands/set.spec.js b/test/commands/set.spec.js similarity index 87% rename from test/mocha/commands/set.spec.js rename to test/commands/set.spec.js index 72df4467fb..b9bbe79c85 100644 --- a/test/mocha/commands/set.spec.js +++ b/test/commands/set.spec.js @@ -1,21 +1,12 @@ var async = require('async'); var assert = require('assert'); -var config = require("../../lib/config"); -var nodeAssert = require('../../lib/nodeify-assertions'); +var config = require("../lib/config"); +var helper = require('../helper'); var redis = config.redis; -var RedisProcess = require("../../lib/redis-process"); var uuid = require('uuid'); describe("The 'set' method", function () { - var rp; - before(function (done) { - RedisProcess.start(function (err, _rp) { - rp = _rp; - return done(err); - }); - }) - function removeMochaListener () { var mochaListener = process.listeners('uncaughtException').pop(); process.removeListener('uncaughtException', mochaListener); @@ -74,9 +65,9 @@ describe("The 'set' method", function () { describe("with valid parameters", function () { it("sets the value correctly", function (done) { client.set(key, value, function (err, res) { - nodeAssert.isNotError()(err, res); + helper.isNotError()(err, res); client.get(key, function (err, res) { - nodeAssert.isString(value)(err, res); + helper.isString(value)(err, res); done(); }); }); @@ -86,7 +77,7 @@ describe("The 'set' method", function () { describe("with undefined 'key' and missing 'value' parameter", function () { it("reports an error", function (done) { client.set(undefined, function (err, res) { - nodeAssert.isError()(err, null); + helper.isError()(err, null); done(); }); }); @@ -95,7 +86,7 @@ describe("The 'set' method", function () { describe("with undefined 'key' and defined 'value' parameters", function () { it("reports an error", function () { client.set(undefined, value, function (err, res) { - nodeAssert.isError()(err, null); + helper.isError()(err, null); done(); }); }); @@ -108,7 +99,7 @@ describe("The 'set' method", function () { client.set(key, value); setTimeout(function () { client.get(key, function (err, res) { - nodeAssert.isString(value)(err, res); + helper.isString(value)(err, res); done(); }); }, 100); @@ -120,7 +111,7 @@ describe("The 'set' method", function () { this.timeout(200); client.once("error", function (err) { - nodeAssert.isError()(err, null); + helper.isError()(err, null); return done(err); }); @@ -154,7 +145,7 @@ describe("The 'set' method", function () { process.once('uncaughtException', function (err) { process.on('uncaughtException', mochaListener); - nodeAssert.isError()(err, null); + helper.isError()(err, null); }); client.set(undefined, value); @@ -171,8 +162,4 @@ describe("The 'set' method", function () { allTests(parser, ip); }) }); - - after(function (done) { - if (rp) rp.stop(done); - }); }); diff --git a/test/commands/setex.spec.js b/test/commands/setex.spec.js new file mode 100644 index 0000000000..d66366785e --- /dev/null +++ b/test/commands/setex.spec.js @@ -0,0 +1,47 @@ +var assert = require("assert"); +var config = require("../lib/config"); +var helper = require("../helper"); +var redis = config.redis; + +describe("The 'setex' method", function () { + + function allTests(parser, ip) { + var args = config.configureClient(parser, ip); + + describe("using " + parser + " and " + ip, function () { + var client; + + beforeEach(function (done) { + client = redis.createClient.apply(redis.createClient, args); + client.once("error", done); + client.once("connect", function () { + client.flushdb(done); + }); + }); + + it('sets a key with an expiry', function (done) { + client.SETEX(["setex key", "100", "setex val"], helper.isString("OK")); + client.exists(["setex key"], helper.isNumber(1)); + client.ttl(['setex key'], function (err, ttl) { + assert.ok(ttl > 0); + return done(); + }); + }); + + it('returns an error if no value is provided', function (done) { + client.SETEX(["setex key", "100", undefined], helper.isError(done)); + }); + + afterEach(function () { + client.end(); + }); + }); + } + + ['javascript', 'hiredis'].forEach(function (parser) { + allTests(parser, "/tmp/redis.sock"); + ['IPv4', 'IPv6'].forEach(function (ip) { + allTests(parser, ip); + }) + }); +}); diff --git a/test/commands/setnx.spec.js b/test/commands/setnx.spec.js new file mode 100644 index 0000000000..92f892516a --- /dev/null +++ b/test/commands/setnx.spec.js @@ -0,0 +1,46 @@ +var assert = require("assert"); +var config = require("../lib/config"); +var helper = require("../helper"); +var redis = config.redis; + +describe("The 'setnx' method", function () { + + function allTests(parser, ip) { + var args = config.configureClient(parser, ip); + + describe("using " + parser + " and " + ip, function () { + var client; + + beforeEach(function (done) { + client = redis.createClient.apply(redis.createClient, args); + client.once("error", done); + client.once("connect", function () { + client.flushdb(done); + }); + }); + + it('sets key if it does not have a value', function (done) { + client.setnx('foo', 'banana', helper.isNumber(1)); + client.get('foo', helper.isString('banana', done)); + }); + + it('does not set key if it already has a value', function (done) { + client.set('foo', 'bar', helper.isString('OK')); + client.setnx('foo', 'banana', helper.isNumber(0)); + client.get('foo', helper.isString('bar', done)); + return done(); + }); + + afterEach(function () { + client.end(); + }); + }); + } + + ['javascript', 'hiredis'].forEach(function (parser) { + allTests(parser, "/tmp/redis.sock"); + ['IPv4', 'IPv6'].forEach(function (ip) { + allTests(parser, ip); + }) + }); +}); diff --git a/test/commands/sinter.spec.js b/test/commands/sinter.spec.js new file mode 100644 index 0000000000..c1b89051cf --- /dev/null +++ b/test/commands/sinter.spec.js @@ -0,0 +1,70 @@ +var assert = require("assert"); +var config = require("../lib/config"); +var helper = require("../helper"); +var redis = config.redis; + +describe("The 'sinter' method", function () { + + function allTests(parser, ip) { + var args = config.configureClient(parser, ip); + + describe("using " + parser + " and " + ip, function () { + var client; + + beforeEach(function (done) { + client = redis.createClient.apply(redis.createClient, args); + client.once("error", done); + client.once("connect", function () { + client.flushdb(done); + }); + }); + + it('handles two sets being intersected', function (done) { + client.sadd('sa', 'a', helper.isNumber(1)); + client.sadd('sa', 'b', helper.isNumber(1)); + client.sadd('sa', 'c', helper.isNumber(1)); + + client.sadd('sb', 'b', helper.isNumber(1)); + client.sadd('sb', 'c', helper.isNumber(1)); + client.sadd('sb', 'd', helper.isNumber(1)); + + client.sinter('sa', 'sb', function (err, intersection) { + assert.equal(intersection.length, 2); + assert.deepEqual(intersection.sort(), [ 'b', 'c' ]); + return done(err); + }); + }); + + it('handles three sets being intersected', function (done) { + client.sadd('sa', 'a', helper.isNumber(1)); + client.sadd('sa', 'b', helper.isNumber(1)); + client.sadd('sa', 'c', helper.isNumber(1)); + + client.sadd('sb', 'b', helper.isNumber(1)); + client.sadd('sb', 'c', helper.isNumber(1)); + client.sadd('sb', 'd', helper.isNumber(1)); + + client.sadd('sc', 'c', helper.isNumber(1)); + client.sadd('sc', 'd', helper.isNumber(1)); + client.sadd('sc', 'e', helper.isNumber(1)); + + client.sinter('sa', 'sb', 'sc', function (err, intersection) { + assert.equal(intersection.length, 1); + assert.equal(intersection[0], 'c'); + return done(err); + }); + }); + + afterEach(function () { + client.end(); + }); + }); + } + + ['javascript', 'hiredis'].forEach(function (parser) { + allTests(parser, "/tmp/redis.sock"); + ['IPv4', 'IPv6'].forEach(function (ip) { + allTests(parser, ip); + }) + }); +}); diff --git a/test/commands/sismember.spec.js b/test/commands/sismember.spec.js new file mode 100644 index 0000000000..7277692c46 --- /dev/null +++ b/test/commands/sismember.spec.js @@ -0,0 +1,43 @@ +var assert = require("assert"); +var config = require("../lib/config"); +var helper = require("../helper"); +var redis = config.redis; + +describe("The 'sismember' method", function () { + + function allTests(parser, ip) { + var args = config.configureClient(parser, ip); + + describe("using " + parser + " and " + ip, function () { + var client; + + beforeEach(function (done) { + client = redis.createClient.apply(redis.createClient, args); + client.once("error", done); + client.once("connect", function () { + client.flushdb(done); + }); + }); + + it('returns 0 if the value is not in the set', function (done) { + client.sismember('foo', 'banana', helper.isNumber(0, done)); + }); + + it('returns 1 if the value is in the set', function (done) { + client.sadd('foo', 'banana', helper.isNumber(1)); + client.sismember('foo', 'banana', helper.isNumber(1, done)); + }); + + afterEach(function () { + client.end(); + }); + }); + } + + ['javascript', 'hiredis'].forEach(function (parser) { + allTests(parser, "/tmp/redis.sock"); + ['IPv4', 'IPv6'].forEach(function (ip) { + allTests(parser, ip); + }) + }); +}); diff --git a/test/commands/sort.spec.js b/test/commands/sort.spec.js new file mode 100644 index 0000000000..b40b63cf32 --- /dev/null +++ b/test/commands/sort.spec.js @@ -0,0 +1,128 @@ +var assert = require("assert"); +var config = require("../lib/config"); +var helper = require("../helper"); +var redis = config.redis; + +describe("The 'sort' method", function () { + + function allTests(parser, ip) { + var args = config.configureClient(parser, ip); + + describe("using " + parser + " and " + ip, function () { + var client; + + beforeEach(function (done) { + client = redis.createClient.apply(redis.createClient, args); + client.once("error", done); + client.once("connect", function () { + client.flushdb(); + setupData(client, done) + }); + }); + + describe('alphabetical', function () { + it('sorts in ascending alphabetical order', function (done) { + client.sort('y', 'asc', 'alpha', function (err, sorted) { + assert.deepEqual(sorted, ['a', 'b', 'c', 'd']); + return done(err); + }); + }); + + it('sorts in descending alphabetical order', function (done) { + client.sort('y', 'desc', 'alpha', function (err, sorted) { + assert.deepEqual(sorted, ['d', 'c', 'b', 'a']); + return done(err); + }); + }); + }); + + describe('numeric', function () { + it('sorts in ascending numeric order', function (done) { + client.sort('x', 'asc', function (err, sorted) { + assert.deepEqual(sorted, [2, 3, 4, 9]); + return done(err); + }); + }); + + it('sorts in descending numeric order', function (done) { + client.sort('x', 'desc', function (err, sorted) { + assert.deepEqual(sorted, [9, 4, 3, 2]); + return done(err); + }); + }); + }); + + describe('pattern', function () { + it('handles sorting with a pattern', function (done) { + client.sort('x', 'by', 'w*', 'asc', function (err, sorted) { + assert.deepEqual(sorted, [3, 9, 4, 2]); + return done(err); + }); + }); + + it("handles sorting with a 'by' pattern and 1 'get' pattern", function (done) { + client.sort('x', 'by', 'w*', 'asc', 'get', 'o*', function (err, sorted) { + assert.deepEqual(sorted, ['foo', 'bar', 'baz', 'buz']); + return done(err); + }); + }); + + it("handles sorting with a 'by' pattern and 2 'get' patterns", function (done) { + client.sort('x', 'by', 'w*', 'asc', 'get', 'o*', 'get', 'p*', function (err, sorted) { + assert.deepEqual(sorted, ['foo', 'bux', 'bar', 'tux', 'baz', 'lux', 'buz', 'qux']); + return done(err); + }); + }); + + it("sorting with a 'by' pattern and 2 'get' patterns and stores results", function (done) { + client.sort('x', 'by', 'w*', 'asc', 'get', 'o*', 'get', 'p*', 'store', 'bacon', function (err) { + if (err) return done(err); + }); + + client.lrange('bacon', 0, -1, function (err, values) { + assert.deepEqual(values, ['foo', 'bux', 'bar', 'tux', 'baz', 'lux', 'buz', 'qux']); + return done(err); + }); + }); + }); + + afterEach(function () { + client.end(); + }); + }); + } + + function setupData(client, done) { + client.rpush('y', 'd'); + client.rpush('y', 'b'); + client.rpush('y', 'a'); + client.rpush('y', 'c'); + + client.rpush('x', '3'); + client.rpush('x', '9'); + client.rpush('x', '2'); + client.rpush('x', '4'); + + client.set('w3', '4'); + client.set('w9', '5'); + client.set('w2', '12'); + client.set('w4', '6'); + + client.set('o2', 'buz'); + client.set('o3', 'foo'); + client.set('o4', 'baz'); + client.set('o9', 'bar'); + + client.set('p2', 'qux'); + client.set('p3', 'bux'); + client.set('p4', 'lux'); + client.set('p9', 'tux', done); + } + + ['javascript', 'hiredis'].forEach(function (parser) { + allTests(parser, "/tmp/redis.sock"); + ['IPv4', 'IPv6'].forEach(function (ip) { + allTests(parser, ip); + }) + }); +}); diff --git a/test/commands/srem.spec.js b/test/commands/srem.spec.js new file mode 100644 index 0000000000..f0a140e39a --- /dev/null +++ b/test/commands/srem.spec.js @@ -0,0 +1,66 @@ +var assert = require("assert"); +var config = require("../lib/config"); +var helper = require("../helper"); +var redis = config.redis; + +describe("The 'srem' method", function () { + + function allTests(parser, ip) { + var args = config.configureClient(parser, ip); + + describe("using " + parser + " and " + ip, function () { + var client; + + beforeEach(function (done) { + client = redis.createClient.apply(redis.createClient, args); + client.once("error", done); + client.once("connect", function () { + client.flushdb(done); + }); + }); + + it('removes a value', function (done) { + client.sadd('set0', 'member0', helper.isNumber(1)); + client.srem('set0', 'member0', helper.isNumber(1)); + client.scard('set0', helper.isNumber(0, done)); + }); + + it('handles attempting to remove a missing value', function (done) { + client.srem('set0', 'member0', helper.isNumber(0, done)); + }); + + it('allows multiple values to be removed', function (done) { + client.sadd("set0", ["member0", "member1", "member2"], helper.isNumber(3)); + client.SREM("set0", ["member1", "member2"], helper.isNumber(2)); + client.smembers("set0", function (err, res) { + assert.strictEqual(res.length, 1); + assert.ok(~res.indexOf("member0")); + return done(err); + }); + }); + + it('handles a value missing from the set of values being removed', function (done) { + client.sadd("set0", ["member0", "member1", "member2"], helper.isNumber(3)); + client.SREM("set0", ["member3", "member4"], helper.isNumber(0)); + client.smembers("set0", function (err, res) { + assert.strictEqual(res.length, 3); + assert.ok(~res.indexOf("member0")); + assert.ok(~res.indexOf("member1")); + assert.ok(~res.indexOf("member2")); + return done(err); + }); + }); + + afterEach(function () { + client.end(); + }); + }); + } + + ['javascript', 'hiredis'].forEach(function (parser) { + allTests(parser, "/tmp/redis.sock"); + ['IPv4', 'IPv6'].forEach(function (ip) { + allTests(parser, ip); + }) + }); +}); diff --git a/test/commands/type.spec.js b/test/commands/type.spec.js new file mode 100644 index 0000000000..57aa745da1 --- /dev/null +++ b/test/commands/type.spec.js @@ -0,0 +1,63 @@ +var assert = require("assert"); +var config = require("../lib/config"); +var helper = require("../helper"); +var redis = config.redis; + +describe("The 'type' method", function () { + + function allTests(parser, ip) { + var args = config.configureClient(parser, ip); + + describe("using " + parser + " and " + ip, function () { + var client; + + beforeEach(function (done) { + client = redis.createClient.apply(redis.createClient, args); + client.once("error", done); + client.once("connect", function () { + client.flushdb(done); + }); + }); + + it('reports string type', function (done) { + client.set(["string key", "should be a string"], helper.isString("OK")); + client.TYPE(["string key"], helper.isString("string", done)); + }); + + it('reports list type', function (done) { + client.rpush(["list key", "should be a list"], helper.isNumber(1)); + client.TYPE(["list key"], helper.isString("list", done)); + }); + + it('reports set type', function (done) { + client.sadd(["set key", "should be a set"], helper.isNumber(1)); + client.TYPE(["set key"], helper.isString("set", done)); + }); + + it('reports zset type', function (done) { + client.zadd(["zset key", "10.0", "should be a zset"], helper.isNumber(1)); + client.TYPE(["zset key"], helper.isString("zset", done)); + }); + + it('reports hash type', function (done) { + client.hset(["hash key", "hashtest", "should be a hash"], helper.isNumber(1)); + client.TYPE(["hash key"], helper.isString("hash", done)); + }); + + it('reports none for null key', function (done) { + client.TYPE("not here yet", helper.isString("none", done)); + }); + + afterEach(function () { + client.end(); + }); + }); + } + + ['javascript', 'hiredis'].forEach(function (parser) { + allTests(parser, "/tmp/redis.sock"); + ['IPv4', 'IPv6'].forEach(function (ip) { + allTests(parser, ip); + }) + }); +}); diff --git a/test/commands/watch.spec.js b/test/commands/watch.spec.js new file mode 100644 index 0000000000..8494d35dd5 --- /dev/null +++ b/test/commands/watch.spec.js @@ -0,0 +1,68 @@ +var assert = require("assert"); +var config = require("../lib/config"); +var helper = require("../helper"); +var redis = config.redis; + +describe("The 'watch' method", function () { + + function allTests(parser, ip) { + var args = config.configureClient(parser, ip); + var watched = 'foobar' + + describe("using " + parser + " and " + ip, function () { + var client; + + beforeEach(function (done) { + client = redis.createClient.apply(redis.createClient, args); + client.once("error", done); + client.once("connect", function () { + client.flushdb(function (err) { + if (!helper.serverVersionAtLeast(client, [2, 2, 0])) { + err = Error('some watch commands not supported in redis <= 2.2.0') + } + return done(err); + + }) + }); + }); + + afterEach(function () { + client.end(); + }); + + it('does not execute transaction if watched key was modified prior to execution', function (done) { + client.watch(watched); + client.incr(watched); + multi = client.multi(); + multi.incr(watched); + multi.exec(helper.isNull(done)); + }) + + it('successfully modifies other keys independently of transaction', function (done) { + client.set("unwatched", 200); + + client.set(watched, 0); + client.watch(watched); + client.incr(watched); + + var multi = client.multi() + .incr(watched) + .exec(function (err, replies) { + assert.strictEqual(replies, null, "Aborted transaction multi-bulk reply should be null."); + + client.get("unwatched", function (err, reply) { + assert.equal(reply, 200, "Expected 200, got " + reply); + return done(err) + }); + }); + }) + }); + } + + ['javascript', 'hiredis'].forEach(function (parser) { + allTests(parser, "/tmp/redis.sock"); + ['IPv4', 'IPv6'].forEach(function (ip) { + allTests(parser, ip); + }) + }); +}); diff --git a/test/conf/password.conf b/test/conf/password.conf new file mode 100644 index 0000000000..8351cb455f --- /dev/null +++ b/test/conf/password.conf @@ -0,0 +1,5 @@ +requirepass porkchopsandwiches +port 6378 +bind ::1 127.0.0.1 +unixsocket /tmp/redis.sock +unixsocketperm 755 diff --git a/test/lib/nodeify-assertions.js b/test/helper.js similarity index 55% rename from test/lib/nodeify-assertions.js rename to test/helper.js index 73000e67d0..8aaa4d68d4 100644 --- a/test/lib/nodeify-assertions.js +++ b/test/helper.js @@ -1,63 +1,86 @@ -var assert = require('assert'); +var assert = require("assert"); +var path = require('path'); +var RedisProcess = require("./lib/redis-process"); +var rp; + +// don't start redis every time we +// include this helper file! +if (!process.env.REDIS_TESTS_STARTED) { + process.env.REDIS_TESTS_STARTED = true; + + before(function (done) { + startRedis('./conf/redis.conf', done); + }) + + after(function (done) { + if (rp) rp.stop(done); + }); +} module.exports = { - isNumber: function (expected) { + stopRedis: function (done) { + rp.stop(done); + }, + startRedis: function (conf, done) { + startRedis(conf, done); + }, + isNumber: function (expected, done) { return function (err, results) { assert.strictEqual(null, err, "expected " + expected + ", got error: " + err); assert.strictEqual(expected, results, expected + " !== " + results); assert.strictEqual(typeof results, "number", "expected a number, got " + typeof results); - return true; + if (done) return done(); }; }, - - isString: function (str) { + isString: function (str, done) { return function (err, results) { assert.strictEqual(null, err, "expected string '" + str + "', got error: " + err); assert.equal(str, results, str + " does not match " + results); - return true; + if (done) return done(); }; }, - - isNull: function () { + isNull: function (done) { return function (err, results) { assert.strictEqual(null, err, "expected null, got error: " + err); assert.strictEqual(null, results, results + " is not null"); - return true; + if (done) return done(); }; }, - - isError: function () { + isError: function (done) { return function (err, results) { assert.notEqual(err, null, "err is null, but an error is expected here."); - return true; + if (done) return done(); }; }, - - isNotError: function () { + isNotError: function (done) { return function (err, results) { assert.strictEqual(err, null, "expected success, got an error: " + err); - return true; + if (done) return done(); }; }, - isType: { - number: function () { + number: function (done) { return function (err, results) { assert.strictEqual(null, err, "expected any number, got error: " + err); assert.strictEqual(typeof results, "number", results + " is not a number"); - return true; + if (done) return done(); }; }, - - positiveNumber: function () { + positiveNumber: function (done) { return function (err, results) { assert.strictEqual(null, err, "expected positive number, got error: " + err); assert.strictEqual(true, (results > 0), results + " is not a positive number"); - return true; + if (done) return done(); }; } }, - + match: function (pattern, done) { + return function (err, results) { + assert.strictEqual(null, err, "expected " + pattern.toString() + ", got error: " + err); + assert(pattern.test(results), "expected string '" + results + "' to match " + pattern.toString()); + if (done) return done(); + }; + }, serverVersionAtLeast: function (connection, desired_version) { // Return true if the server version >= desired_version var version = connection.server_info.versions; @@ -67,4 +90,11 @@ module.exports = { } return true; } -}; +} + +function startRedis (conf, done) { + RedisProcess.start(function (err, _rp) { + rp = _rp; + return done(err); + }, path.resolve(__dirname, conf)); +} diff --git a/test/lib/config.js b/test/lib/config.js index 475f2733ce..0ddef51447 100644 --- a/test/lib/config.js +++ b/test/lib/config.js @@ -1,3 +1,5 @@ +// helpers for configuring a redis client in +// its various modes, ipV6, ipV4, socket. module.exports = (function () { var redis = require('../../index'); redis.debug_mode = process.env.DEBUG ? JSON.parse(process.env.DEBUG) : false; @@ -11,18 +13,21 @@ module.exports = (function () { } }; - config.configureClient = function (parser, ip, isSocket) { + config.configureClient = function (parser, ip, opts) { var args = []; + opts = opts || {}; - if (!isSocket) { + if (ip.match(/\.sock/)) { + args.push(ip) + } else { args.push(config.PORT); args.push(config.HOST[ip]); - args.push({ family: ip, parser: parser }); - } else { - args.push(ip); - args.push({ parser: parser }); + opts.family = ip; } + opts.parser = parser; + args.push(opts); + return args; }; diff --git a/test/lib/redis-process.js b/test/lib/redis-process.js index 24d7f04f60..212cf359c0 100644 --- a/test/lib/redis-process.js +++ b/test/lib/redis-process.js @@ -1,42 +1,55 @@ +// helper to start and stop the redis process. var cp = require('child_process'); var config = require('./config'); +var fs = require('fs'); var path = require('path'); var tcpPortUsed = require('tcp-port-used'); module.exports = { - start: function (done) { + start: function (done, conf) { // spawn redis with our testing configuration. - var confFile = path.resolve(__dirname, '../conf/redis.conf'); + var confFile = conf || path.resolve(__dirname, '../conf/redis.conf'); var rp = cp.spawn("redis-server", [confFile], {}); // wait for redis to become available, by // checking the port we bind on. - var id = setInterval(function () { - tcpPortUsed.check(config.PORT, '127.0.0.1') - .then(function (inUse) { - if (inUse) { - clearInterval(id); - - // return an object that can be used in - // an after() block to shutdown redis. - return done(null, { - stop: function (done) { - rp.once("exit", function (code) { - var error = null; - if (code !== null && code !== 0) { - error = Error('Redis shutdown failed with code ' + code); - } - return done(error); - }); - rp.kill("SIGINT"); - } - }); - } - }) - .catch(function (err) { - clearInterval(id); - return done(err); - }) - }, 100); + waitForRedis(true, function () { + // return an object that can be used in + // an after() block to shutdown redis. + return done(null, { + stop: function (done) { + rp.once("exit", function (code) { + var error = null; + if (code !== null && code !== 0) { + error = Error('Redis shutdown failed with code ' + code); + } + waitForRedis(false, function () { + return done(error); + }) + }); + rp.kill("SIGTERM"); + } + }); + }); } }; + +// wait for redis to be listening in +// all three modes (ipv4, ipv6, socket). +function waitForRedis (available, cb) { + var ipV4 = false; + var id = setInterval(function () { + tcpPortUsed.check(config.PORT, '127.0.0.1') + .then(function (_ipV4) { + ipV4 = _ipV4; + return tcpPortUsed.check(config.PORT, '::1'); + }) + .then(function (ipV6) { + if (ipV6 === available && ipV4 === available && + fs.existsSync('/tmp/redis.sock') === available) { + clearInterval(id); + return cb(); + } + }); + }, 100); +} diff --git a/test/lib/unref.js b/test/lib/unref.js new file mode 100644 index 0000000000..4ccf51f203 --- /dev/null +++ b/test/lib/unref.js @@ -0,0 +1,18 @@ +// spawned by the unref tests in node_redis.spec.js. +// when configured, unref causes the client to exit +// as soon as there are no outstanding commands. +'use strict'; + +var redis = require("../../"); +//redis.debug_mode = true +var HOST = process.argv[2] || '127.0.0.1'; +var PORT = process.argv[3] +var args = PORT ? [PORT, HOST] : [HOST] + +var c = redis.createClient.apply(redis, args); +c.unref(); +c.info(function (err, reply) { + if (err) process.exit(-1); + if (!reply.length) process.exit(-1); + process.stdout.write(reply.length.toString()); +}); diff --git a/test/mocha/commands/generated-commands.spec.js b/test/mocha/commands/generated-commands.spec.js deleted file mode 100644 index bff15f4839..0000000000 --- a/test/mocha/commands/generated-commands.spec.js +++ /dev/null @@ -1,119 +0,0 @@ -var async = require('async'); -var assert = require('assert'); -var config = require("../../lib/config"); -var nodeAssert = require('../../lib/nodeify-assertions'); -var redis = config.redis; -var RedisProcess = require("../../lib/redis-process"); -var uuid = require('uuid'); -var commands = require("../../../lib/commands"); -var sinon = require('sinon').sandbox.create(); - -var overwritten = ['eval', 'hmset', 'multi', 'select']; - -describe("The auto-generated methods", function () { - - var rp; - before(function (done) { - RedisProcess.start(function (err, _rp) { - rp = _rp; - return done(err); - }); - }) - - function removeMochaListener () { - var mochaListener = process.listeners('uncaughtException').pop(); - process.removeListener('uncaughtException', mochaListener); - return mochaListener; - } - - function allTests(parser, ip) { - var args = config.configureClient(parser, ip); - - describe("using " + parser + " and " + ip, function () { - var key, value; - var client; - - beforeEach(function (done) { - client = redis.createClient.apply(redis.createClient, args); - client.once("error", function onError(err) { - done(err); - }); - client.once("ready", function onReady() { - done(); - }); - }); - - afterEach(function () { - client.end(); - }); - - commands.forEach(function (method) { - if (overwritten.indexOf(method) > -1) { - // these are in the list of generated commands but are later overwritten with - // different behavior by node_redis - return; - } - - describe("the " + method + " method", function () { - var methodArgs; - var noop = function () { }; - - it("calls sendCommand with whatever arguments it receives", function () { - key = uuid.v4(); - value = uuid.v4(); - - var parts = method.split(' '); - var argNum = 0; - - client.send_command = sinon.spy(); - methodArgs = [key, value, noop]; - - client[parts[0]].apply(client, methodArgs); - - assert.strictEqual(client.send_command.called, true, - "Client.send_command should have been called."); - assert.strictEqual(parts[0], client.send_command.args[0][argNum], - "Command name '" + parts[0] + "' should be passed as arg " + - argNum + " to send_command"); - argNum++; - /* - * Um, except this doesn't work? The second part of the command is never sent???? - if (parts[1]) { - assert.strictEqual(parts[1], client.send_command.args[0][argNum], - "Second command '" + parts[1] + "' should be passed as arg " + - argNum + " to send_command"); - argNum++; - } - */ - assert.strictEqual(methodArgs.length, client.send_command.args[0][argNum].length, - "The rest of the args to " + method + " should be passed as arg an array to send_command"); - assert.strictEqual(methodArgs[0], client.send_command.args[0][argNum][0], - "Arg " + argNum + " to " + method + " should be passed as arg " + - argNum + " to send_command"); - assert.strictEqual(methodArgs[1], client.send_command.args[0][argNum][1], - "Arg " + argNum + " to " + method + " should be passed as arg " + - argNum + " to send_command"); - assert.strictEqual(methodArgs[2], client.send_command.args[0][argNum][2], - "Arg " + argNum + " to " + method + " should be passed as arg " + - argNum + " to send_command"); - }); - }); - }); - }); - } - - ['javascript', 'hiredis'].forEach(function (parser) { - allTests(parser, "/tmp/redis.sock"); - ['IPv4', 'IPv6'].forEach(function (ip) { - allTests(parser, ip); - }) - }); - - afterEach(function () { - sinon.restore(); - }); - - after(function (done) { - if (rp) rp.stop(done); - }); -}); diff --git a/test/mocha/node_redis.spec.js b/test/mocha/node_redis.spec.js deleted file mode 100644 index 2319c2173d..0000000000 --- a/test/mocha/node_redis.spec.js +++ /dev/null @@ -1,182 +0,0 @@ -var async = require("async"); -var config = require("../lib/config"); -var nodeAssert = require("../lib/nodeify-assertions"); -var redis = config.redis; -var RedisProcess = require("../lib/redis-process"); -var uuid = require("uuid"); - -describe("A node_redis client", function () { - - var rp; - before(function (done) { - RedisProcess.start(function (err, _rp) { - rp = _rp; - return done(err); - }); - }) - - function allTests(parser, ip) { - var args = config.configureClient(parser, ip); - - describe("using " + parser + " and " + ip, function () { - var client; - - describe("when not connected", function () { - afterEach(function () { - client.end(); - }); - - it("connects correctly", 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); - }); - }); - }); - }); - - describe("when connected", function () { - var client; - - beforeEach(function (done) { - client = redis.createClient.apply(redis.createClient, args); - client.once("error", function onError(err) { - done(err); - }); - client.once("ready", function onReady() { - done(); - }); - }); - - afterEach(function () { - client.end(); - }); - - describe("when redis closes unexpectedly", function () { - it("reconnects and can retrieve the pre-existing data", function (done) { - client.on("reconnecting", function on_recon(params) { - client.on("connect", function on_connect() { - async.parallel([function (cb) { - client.get("recon 1", function (err, res) { - nodeAssert.isString("one")(err, res); - cb(); - }); - }, function (cb) { - client.get("recon 1", function (err, res) { - nodeAssert.isString("one")(err, res); - cb(); - }); - }, function (cb) { - client.get("recon 2", function (err, res) { - nodeAssert.isString("two")(err, res); - cb(); - }); - }, function (cb) { - client.get("recon 2", function (err, res) { - nodeAssert.isString("two")(err, res); - cb(); - }); - }], function (err, results) { - client.removeListener("connect", on_connect); - client.removeListener("reconnecting", on_recon); - done(err); - }); - }); - }); - - client.set("recon 1", "one"); - client.set("recon 2", "two", function (err, res) { - // Do not do this in normal programs. This is to simulate the server closing on us. - // For orderly shutdown in normal programs, do client.quit() - client.stream.destroy(); - }); - }); - - describe("and it's subscribed to a channel", function () { - // reconnect_select_db_after_pubsub - // Does not pass. - // "Connection in subscriber mode, only subscriber commands may be used" - xit("reconnects, unsubscribes, and can retrieve the pre-existing data", function (done) { - client.on("reconnecting", function on_recon(params) { - client.on("ready", function on_connect() { - async.parallel([function (cb) { - client.unsubscribe("recon channel", function (err, res) { - nodeAssert.isNotError()(err, res); - cb(); - }); - }, function (cb) { - client.get("recon 1", function (err, res) { - nodeAssert.isString("one")(err, res); - cb(); - }); - }], function (err, results) { - client.removeListener("connect", on_connect); - client.removeListener("reconnecting", on_recon); - done(err); - }); - }); - }); - - client.set("recon 1", "one"); - client.subscribe("recon channel", function (err, res) { - // Do not do this in normal programs. This is to simulate the server closing on us. - // For orderly shutdown in normal programs, do client.quit() - client.stream.destroy(); - }); - }); - - it("remains subscribed", function () { - var client2 = redis.createClient.apply(redis.createClient, args); - - client.on("reconnecting", function on_recon(params) { - client.on("ready", function on_connect() { - async.parallel([function (cb) { - client.on("message", function (channel, message) { - try { - nodeAssert.isString("recon channel")(null, channel); - nodeAssert.isString("a test message")(null, message); - } catch (err) { - cb(err); - } - }); - - client2.subscribe("recon channel", function (err, res) { - if (err) { - cb(err); - return; - } - client2.publish("recon channel", "a test message"); - }); - }], function (err, results) { - done(err); - }); - }); - }); - - client.subscribe("recon channel", function (err, res) { - // Do not do this in normal programs. This is to simulate the server closing on us. - // For orderly shutdown in normal programs, do client.quit() - client.stream.destroy(); - }); - }); - }); - }); - }); - }); - } - - ['javascript', 'hiredis'].forEach(function (parser) { - allTests(parser, "/tmp/redis.sock"); - ['IPv4', 'IPv6'].forEach(function (ip) { - allTests(parser, ip); - }) - }); - - after(function (done) { - if (rp) rp.stop(done); - }) -}); diff --git a/test/node_redis.spec.js b/test/node_redis.spec.js new file mode 100644 index 0000000000..cd3d86da55 --- /dev/null +++ b/test/node_redis.spec.js @@ -0,0 +1,640 @@ +var async = require("async"); +var assert = require("assert"); +var config = require("./lib/config"); +var helper = require('./helper') +var fork = require("child_process").fork; +var redis = config.redis; + +describe("a node_redis client", function () { + + function allTests(parser, ip) { + var args = config.configureClient(parser, ip); + + describe("using " + parser + " and " + ip, function () { + var client; + + describe("when not connected", function () { + afterEach(function () { + client.end(); + }); + + it("connects correctly", 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); + }); + }); + }); + }); + + describe("when connected", function () { + beforeEach(function (done) { + client = redis.createClient.apply(redis.createClient, args); + client.once("error", done); + client.once("connect", function () { + client.flushdb(done) + }); + }); + + afterEach(function () { + client.end(); + }); + + describe("when redis closes unexpectedly", function () { + it("reconnects and can retrieve the pre-existing data", function (done) { + client.on("reconnecting", function on_recon(params) { + client.on("connect", function on_connect() { + async.parallel([function (cb) { + client.get("recon 1", function (err, res) { + helper.isString("one")(err, res); + cb(); + }); + }, function (cb) { + client.get("recon 1", function (err, res) { + helper.isString("one")(err, res); + cb(); + }); + }, function (cb) { + client.get("recon 2", function (err, res) { + helper.isString("two")(err, res); + cb(); + }); + }, function (cb) { + client.get("recon 2", function (err, res) { + helper.isString("two")(err, res); + cb(); + }); + }], function (err, results) { + client.removeListener("connect", on_connect); + client.removeListener("reconnecting", on_recon); + done(err); + }); + }); + }); + + client.set("recon 1", "one"); + client.set("recon 2", "two", function (err, res) { + // Do not do this in normal programs. This is to simulate the server closing on us. + // For orderly shutdown in normal programs, do client.quit() + client.stream.destroy(); + }); + }); + + describe("and it's subscribed to a channel", function () { + // reconnect_select_db_after_pubsub + // Does not pass. + // "Connection in subscriber mode, only subscriber commands may be used" + xit("reconnects, unsubscribes, and can retrieve the pre-existing data", function (done) { + client.on("reconnecting", function on_recon(params) { + client.on("ready", function on_connect() { + async.parallel([function (cb) { + client.unsubscribe("recon channel", function (err, res) { + helper.isNotError()(err, res); + cb(); + }); + }, function (cb) { + client.get("recon 1", function (err, res) { + helper.isString("one")(err, res); + cb(); + }); + }], function (err, results) { + client.removeListener("connect", on_connect); + client.removeListener("reconnecting", on_recon); + done(err); + }); + }); + }); + + client.set("recon 1", "one"); + client.subscribe("recon channel", function (err, res) { + // Do not do this in normal programs. This is to simulate the server closing on us. + // For orderly shutdown in normal programs, do client.quit() + client.stream.destroy(); + }); + }); + + it("remains subscribed", function () { + var client2 = redis.createClient.apply(redis.createClient, args); + + client.on("reconnecting", function on_recon(params) { + client.on("ready", function on_connect() { + async.parallel([function (cb) { + client.on("message", function (channel, message) { + try { + helper.isString("recon channel")(null, channel); + helper.isString("a test message")(null, message); + } catch (err) { + cb(err); + } + }); + + client2.subscribe("recon channel", function (err, res) { + if (err) { + cb(err); + return; + } + client2.publish("recon channel", "a test message"); + }); + }], function (err, results) { + done(err); + }); + }); + }); + + client.subscribe("recon channel", function (err, res) { + // Do not do this in normal programs. This is to simulate the server closing on us. + // For orderly shutdown in normal programs, do client.quit() + client.stream.destroy(); + }); + }); + }); + + describe('domain', function () { + it('allows client to be executed from within domain', function (done) { + var domain; + + try { + domain = require('domain').create(); + } catch (err) { + console.log("Skipping " + name + " because this version of node doesn't have domains."); + return done(); + } + + if (domain) { + domain.run(function () { + client.set('domain', 'value', function (err, res) { + assert.ok(process.domain); + var notFound = res.not.existing.thing; // ohhh nooooo + }); + }); + + // this is the expected and desired behavior + domain.on('error', function (err) { + domain.exit(); + return done() + }); + } + }) + }) + + }); + + it('emits errors thrown from within an on("message") handler', function (done) { + var client2 = redis.createClient.apply(redis.createClient, args); + var name = 'channel'; + + client2.subscribe(name, function () { + client.publish(name, "some message"); + }); + + client2.on("message", function (channel, data) { + if (channel == name) { + assert.equal(data, "some message"); + throw Error('forced exception'); + } + return done(); + }); + + client2.once("error", function (err) { + client2.end(); + assert.equal(err.message, 'forced exception'); + return done(); + }); + }); + + describe('idle', function () { + it('emits idle as soon as there are no outstanding commands', function (done) { + client.on('idle', function onIdle () { + client.removeListener("idle", onIdle); + client.get('foo', helper.isString('bar', done)); + }); + client.set('foo', 'bar'); + }); + }); + + describe('utf8', function () { + it('handles utf-8 keys', function (done) { + var utf8_sample = "ಠ_ಠ"; + client.set(["utf8test", utf8_sample], helper.isString("OK")); + client.get(["utf8test"], function (err, obj) { + assert.strictEqual(utf8_sample, obj); + return done(err); + }); + }); + }); + }); + + describe('detect_buffers', function () { + var client; + var args = config.configureClient(parser, ip, { + detect_buffers: true + }); + + beforeEach(function (done) { + client = redis.createClient.apply(redis.createClient, args); + client.once("error", done); + client.once("connect", function () { + client.flushdb(function (err) { + client.hmset("hash key 2", "key 1", "val 1", "key 2", "val 2"); + client.set("string key 1", "string value"); + return done(err); + }); + }); + }); + + describe('get', function () { + describe('first argument is a string', function () { + it('returns a string', function (done) { + client.get("string key 1", helper.isString("string value", done)); + }); + + it('returns a string when executed as part of transaction', function (done) { + client.multi().get("string key 1").exec(helper.isString("string value", done)); + }); + }); + + describe('first argument is a buffer', function () { + it('returns a buffer', function (done) { + client.get(new Buffer("string key 1"), function (err, reply) { + assert.strictEqual(true, Buffer.isBuffer(reply)); + assert.strictEqual("", reply.inspect()); + return done(err); + }); + }); + + it('returns a bufffer when executed as part of transaction', function (done) { + client.multi().get(new Buffer("string key 1")).exec(function (err, reply) { + assert.strictEqual(1, reply.length); + assert.strictEqual(true, Buffer.isBuffer(reply[0])); + assert.strictEqual("", reply[0].inspect()); + return done(err); + }); + }); + }); + }); + + describe('multi.hget', function () { + it('can interleave string and buffer results', function (done) { + client.multi() + .hget("hash key 2", "key 1") + .hget(new Buffer("hash key 2"), "key 1") + .hget("hash key 2", new Buffer("key 2")) + .hget("hash key 2", "key 2") + .exec(function (err, reply) { + assert.strictEqual(true, Array.isArray(reply)); + assert.strictEqual(4, reply.length); + assert.strictEqual("val 1", reply[0]); + assert.strictEqual(true, Buffer.isBuffer(reply[1])); + assert.strictEqual("", reply[1].inspect()); + assert.strictEqual(true, Buffer.isBuffer(reply[2])); + assert.strictEqual("", reply[2].inspect()); + assert.strictEqual("val 2", reply[3]); + return done(err); + }); + }); + }); + + describe('hmget', function () { + describe('first argument is a string', function () { + it('returns strings for keys requested', function (done) { + client.hmget("hash key 2", "key 1", "key 2", function (err, reply) { + assert.strictEqual(true, Array.isArray(reply)); + assert.strictEqual(2, reply.length); + assert.strictEqual("val 1", reply[0]); + assert.strictEqual("val 2", reply[1]); + return done(err); + }); + }); + + it('returns strings for keys requested in transaction', function (done) { + client.multi().hmget("hash key 2", "key 1", "key 2").exec(function (err, reply) { + assert.strictEqual(true, Array.isArray(reply)); + assert.strictEqual(1, reply.length); + assert.strictEqual(2, reply[0].length); + assert.strictEqual("val 1", reply[0][0]); + assert.strictEqual("val 2", reply[0][1]); + return done(err); + }); + }); + + it('handles array of strings with undefined values (repro #344)', function (done) { + client.hmget("hash key 2", "key 3", "key 4", function(err, reply) { + assert.strictEqual(true, Array.isArray(reply)); + assert.strictEqual(2, reply.length); + assert.equal(null, reply[0]); + assert.equal(null, reply[1]); + return done(err); + }); + }); + + it('handles array of strings with undefined values in transaction (repro #344)', function (done) { + client.multi().hmget("hash key 2", "key 3", "key 4").exec(function(err, reply) { + assert.strictEqual(true, Array.isArray(reply)); + assert.strictEqual(1, reply.length); + assert.strictEqual(2, reply[0].length); + assert.equal(null, reply[0][0]); + assert.equal(null, reply[0][1]); + return done(err); + }); + }); + }); + + describe('first argument is a buffer', function () { + it('returns buffers for keys requested', function (done) { + client.hmget(new Buffer("hash key 2"), "key 1", "key 2", function (err, reply) { + assert.strictEqual(true, Array.isArray(reply)); + assert.strictEqual(2, reply.length); + assert.strictEqual(true, Buffer.isBuffer(reply[0])); + assert.strictEqual(true, Buffer.isBuffer(reply[1])); + assert.strictEqual("", reply[0].inspect()); + assert.strictEqual("", reply[1].inspect()); + return done(err); + }); + }); + + it("returns buffers for keys requested in transaction", function (done) { + client.multi().hmget(new Buffer("hash key 2"), "key 1", "key 2").exec(function (err, reply) { + assert.strictEqual(true, Array.isArray(reply)); + assert.strictEqual(1, reply.length); + assert.strictEqual(2, reply[0].length); + assert.strictEqual(true, Buffer.isBuffer(reply[0][0])); + assert.strictEqual(true, Buffer.isBuffer(reply[0][1])); + assert.strictEqual("", reply[0][0].inspect()); + assert.strictEqual("", reply[0][1].inspect()); + return done(err); + }); + }); + }); + }); + + describe('hgetall', function (done) { + describe('first argument is a string', function () { + it('returns string values', function (done) { + client.hgetall("hash key 2", function (err, reply) { + assert.strictEqual("object", typeof reply); + assert.strictEqual(2, Object.keys(reply).length); + assert.strictEqual("val 1", reply["key 1"]); + assert.strictEqual("val 2", reply["key 2"]); + return done(err); + }); + }); + + it('returns string values when executed in transaction', function (done) { + client.multi().hgetall("hash key 2").exec(function (err, reply) { + assert.strictEqual(1, reply.length); + assert.strictEqual("object", typeof reply[0]); + assert.strictEqual(2, Object.keys(reply[0]).length); + assert.strictEqual("val 1", reply[0]["key 1"]); + assert.strictEqual("val 2", reply[0]["key 2"]); + return done(err); + }); + }); + }); + + describe('first argument is a buffer', function () { + it('returns buffer values', function (done) { + client.hgetall(new Buffer("hash key 2"), function (err, reply) { + assert.strictEqual(null, err); + assert.strictEqual("object", typeof reply); + assert.strictEqual(2, Object.keys(reply).length); + assert.strictEqual(true, Buffer.isBuffer(reply["key 1"])); + assert.strictEqual(true, Buffer.isBuffer(reply["key 2"])); + assert.strictEqual("", reply["key 1"].inspect()); + assert.strictEqual("", reply["key 2"].inspect()); + return done(err); + }); + }); + + it('returns buffer values when executed in transaction', function (done) { + client.multi().hgetall(new Buffer("hash key 2")).exec(function (err, reply) { + assert.strictEqual(1, reply.length); + assert.strictEqual("object", typeof reply); + assert.strictEqual(2, Object.keys(reply[0]).length); + assert.strictEqual(true, Buffer.isBuffer(reply[0]["key 1"])); + assert.strictEqual(true, Buffer.isBuffer(reply[0]["key 2"])); + assert.strictEqual("", reply[0]["key 1"].inspect()); + assert.strictEqual("", reply[0]["key 2"].inspect()); + return done(err); + }); + }); + }); + }); + }); + + describe('unref', function () { + it('exits subprocess as soon as final command is processed', function (done) { + var args = config.HOST[ip] ? [config.HOST[ip], config.PORT] : [ip]; + var external = fork("./test/lib/unref.js", args); + var id = setTimeout(function () { + external.kill(); + return done(Error('unref subprocess timed out')); + }, 5000); + + external.on("close", function (code) { + clearTimeout(id); + assert.strictEqual(code, 0); + return done(); + }); + }); + }); + + describe('socket_nodelay', function () { + describe('true', function () { + var client; + var args = config.configureClient(parser, ip, { + socket_nodelay: true + }); + + it("fires client.on('ready')", function (done) { + client = redis.createClient.apply(redis.createClient, args); + client.on("ready", function () { + assert.strictEqual(true, client.options.socket_nodelay); + client.quit(); + + client.once('end', function () { + return done(); + }); + }); + }); + + it('client is functional', function (done) { + client = redis.createClient.apply(redis.createClient, args); + client.on("ready", function () { + assert.strictEqual(true, client.options.socket_nodelay); + client.set(["set key 1", "set val"], helper.isString("OK")); + client.set(["set key 2", "set val"], helper.isString("OK")); + client.get(["set key 1"], helper.isString("set val")); + client.get(["set key 2"], helper.isString("set val")); + client.quit(); + + client.once('end', function () { + return done(); + }); + }); + }); + }); + + describe('false', function () { + var client; + var args = config.configureClient(parser, ip, { + socket_nodelay: false + }); + + it("fires client.on('ready')", function (done) { + client = redis.createClient.apply(redis.createClient, args); + client.on("ready", function () { + assert.strictEqual(false, client.options.socket_nodelay); + client.quit(); + + client.once('end', function () { + return done(); + }); + }); + }); + + it('client is functional', function (done) { + client = redis.createClient.apply(redis.createClient, args); + client.on("ready", function () { + assert.strictEqual(false, client.options.socket_nodelay); + client.set(["set key 1", "set val"], helper.isString("OK")); + client.set(["set key 2", "set val"], helper.isString("OK")); + client.get(["set key 1"], helper.isString("set val")); + client.get(["set key 2"], helper.isString("set val")); + client.quit(); + + client.once('end', function () { + return done(); + }); + }); + }); + }); + + describe('defaults to true', function () { + var client; + var args = config.configureClient(parser, ip); + + it("fires client.on('ready')", function (done) { + client = redis.createClient.apply(redis.createClient, args); + client.on("ready", function () { + assert.strictEqual(true, client.options.socket_nodelay); + client.quit(); + + client.once('end', function () { + return done(); + }); + }); + }); + + it('client is functional', function (done) { + client = redis.createClient.apply(redis.createClient, args); + client.on("ready", function () { + assert.strictEqual(true, client.options.socket_nodelay); + client.set(["set key 1", "set val"], helper.isString("OK")); + client.set(["set key 2", "set val"], helper.isString("OK")); + client.get(["set key 1"], helper.isString("set val")); + client.get(["set key 2"], helper.isString("set val")); + client.quit(); + + client.once('end', function () { + return done(); + }); + }); + }); + }); + }); + + describe('retry_max_delay', function () { + var client; + var args = config.configureClient(parser, ip, { + retry_max_delay: 1 + }); + + it("sets upper bound on how long client waits before reconnecting", function (done) { + var time = new Date().getTime() + var reconnecting = false; + + client = redis.createClient.apply(redis.createClient, args); + client.on('ready', function() { + if (!reconnecting) { + reconnecting = true; + client.retry_delay = 1000; + client.retry_backoff = 1; + client.stream.end(); + } else { + client.end(); + var lasted = new Date().getTime() - time; + assert.ok(lasted < 1000); + return done(); + } + }); + }); + }); + + describe('enable_offline_queue', function () { + describe('true', function () { + it("does not throw an error and enqueues operation", function (done) { + var client = redis.createClient(9999, null, { + max_attempts: 1, + 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) { + if (err) return done(err); + }); + + return setTimeout(function(){ + assert.strictEqual(client.offline_queue.length, 1); + return done(); + }, 25); + }, 50); + }); + }); + + describe('false', function () { + it("does not throw an error and enqueues operation", function (done) { + var client = redis.createClient(9999, null, { + parser: parser, + max_attempts: 1, + enable_offline_queue: false + }); + + client.on('error', function() { + // ignore, b/c expecting a "can't connect" error + }); + + assert.throws(function () { + cli.set('foo', 'bar'); + }); + + assert.doesNotThrow(function () { + client.set('foo', 'bar', function (err) { + // should callback with an error + assert.ok(err); + setTimeout(function () { + return done(); + }, 50); + }); + }); + }); + }); + }); + + }); + } + + ['javascript', 'hiredis'].forEach(function (parser) { + allTests(parser, "/tmp/redis.sock"); + ['IPv4', 'IPv6'].forEach(function (ip) { + allTests(parser, ip); + }); + }); +}); diff --git a/test/parser/javascript.spec.js b/test/parser/javascript.spec.js new file mode 100644 index 0000000000..445964371b --- /dev/null +++ b/test/parser/javascript.spec.js @@ -0,0 +1,27 @@ +/* global describe, it */ + +var assert = require('assert'); +var Parser = require("../../lib/parser/javascript").Parser; + +describe('javascript parser', function () { + it('handles multi-bulk reply', function (done) { + var parser = new Parser(false); + var reply_count = 0; + function check_reply(reply) { + assert.deepEqual(reply, [['a']], "Expecting multi-bulk reply of [['a']]"); + reply_count++; + } + parser.on("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(); + }); +}); diff --git a/test/pubsub.spec.js b/test/pubsub.spec.js new file mode 100644 index 0000000000..dc50246151 --- /dev/null +++ b/test/pubsub.spec.js @@ -0,0 +1,240 @@ +var assert = require("assert"); +var config = require("./lib/config"); +var helper = require("./helper"); +var redis = config.redis; + +describe("publish/subscribe", function () { + + function allTests(parser, ip) { + var args = config.configureClient(parser, ip); + + describe("using " + parser + " and " + ip, function () { + var pub = null; + var sub = null; + var channel = "test channel" + var channel2 = "test channel 2" + var message = "test message" + var hash = "test hash"; + + beforeEach(function (done) { + var pubConnected; + var subConnected; + + pub = redis.createClient.apply(redis.createClient, args); + sub = redis.createClient.apply(redis.createClient, args); + pub.once("error", done); + pub.once("connect", function () { + pub.flushdb(function () { + pubConnected = true; + }); + }); + + sub.once("error", done); + sub.once("connect", function () { + subConnected = true; + }); + + var id = setInterval(function () { + if (pubConnected && subConnected) { + clearInterval(id); + return done(); + } + }, 50); + }); + + describe('subscribe', function () { + it('fires a subscribe event for each channel subscribed to', function (done) { + sub.on("subscribe", function (chnl, count) { + if (chnl === channel2) { + assert.equal(2, count) + return done(); + } + }); + + sub.subscribe(channel, channel2); + }); + + it('receives messages on subscribed channel', function (done) { + sub.on("subscribe", function (chnl, count) { + pub.publish(channel, message, helper.isNumber(1)); + }); + + sub.on("message", function (chnl, msg) { + assert.equal(chnl, channel); + assert.equal(msg, message); + return done(); + }); + + sub.subscribe(channel); + }); + + it('receives messages if subscribe is called after unsubscribe', function (done) { + if (!helper.serverVersionAtLeast(pub, [2, 6, 11])) return done(); + + sub.once("subscribe", function (chnl, count) { + pub.publish(channel, message, helper.isNumber(1)); + }); + + sub.on("message", function (chnl, msg) { + assert.equal(chnl, channel); + assert.equal(msg, message); + return done(); + }); + + sub.subscribe(channel); + sub.unsubscribe(channel); + sub.subscribe(channel); + }); + + it('handles SUB_UNSUB_MSG_SUB', function (done) { + if (!helper.serverVersionAtLeast(pub, [2, 6, 11])) return done(); + + sub.subscribe('chan8'); + sub.subscribe('chan9'); + sub.unsubscribe('chan9'); + pub.publish('chan8', 'something'); + sub.subscribe('chan9', function () { + return done(); + }); + }); + + it('handles SUB_UNSUB_MSG_SUB', function (done) { + if (!helper.serverVersionAtLeast(pub, [2, 6, 11])) return done(); + + sub.psubscribe('abc*'); + sub.subscribe('xyz'); + sub.unsubscribe('xyz'); + pub.publish('abcd', 'something'); + sub.subscribe('xyz', function () { + return done(); + }); + }); + + it('emits end event if quit is called from within subscribe', function (done) { + sub.on("end", function () { + return done(); + }); + sub.on("subscribe", function (chnl, count) { + sub.quit(); + }); + sub.subscribe(channel); + }); + + it('handles SUBSCRIBE_CLOSE_RESUBSCRIBE', function (done) { + var count = 0; + /* Create two clients. c1 subscribes to two channels, c2 will publish to them. + c2 publishes the first message. + c1 gets the message and drops its connection. It must resubscribe itself. + When it resubscribes, c2 publishes the second message, on the same channel + c1 gets the message and drops its connection. It must resubscribe itself, again. + When it resubscribes, c2 publishes the third message, on the second channel + c1 gets the message and drops its connection. When it reconnects, the test ends. + */ + sub.on("message", function(channel, message) { + if (channel === "chan1") { + assert.strictEqual(message, "hi on channel 1"); + sub.stream.end(); + } else if (channel === "chan2") { + assert.strictEqual(message, "hi on channel 2"); + sub.stream.end(); + } else { + sub.quit(); + pub.quit(); + assert.fail("test failed"); + } + }); + + sub.subscribe("chan1", "chan2"); + + sub.on("ready", function(err, results) { + count++; + if (count === 1) { + pub.publish("chan1", "hi on channel 1"); + return; + } else if (count === 2) { + pub.publish("chan2", "hi on channel 2"); + } else { + sub.quit(function() { + pub.quit(function() { + return done(); + }); + }); + } + }); + + pub.publish("chan1", "hi on channel 1"); + }); + }); + + describe('unsubscribe', function () { + it('fires an unsubscribe event', function () { + sub.on("subscribe", function (chnl, count) { + sub.unsubscribe(channel) + }); + + sub.subscribe(channel); + + sub.on("unsubscribe", function (chnl, count) { + assert.equal(chnl, channel); + assert.strictEqual(count, 0); + return done(); + }); + }); + + it('puts client back into write mode', function (done) { + sub.on("subscribe", function (chnl, count) { + sub.unsubscribe(channel) + }); + + sub.subscribe(channel); + + sub.on("unsubscribe", function (chnl, count) { + pub.incr("foo", helper.isNumber(1, done)); + }); + }) + + it('does not complain when unsubscribe is called and there are no subscriptions', function () { + sub.unsubscribe() + }); + + it('executes callback when unsubscribe is called and there are no subscriptions', function (done) { + // test hangs on older versions of redis, so skip + if (!helper.serverVersionAtLeast(pub, [2, 6, 11])) return done(); + + pub.unsubscribe(function (err, results) { + assert.strictEqual(null, results); + return done(err); + }); + }); + }); + + describe('punsubscribe', function () { + it('does not complain when punsubscribe is called and there are no subscriptions', function () { + sub.punsubscribe() + }) + + it('executes callback when punsubscribe is called and there are no subscriptions', function (done) { + // test hangs on older versions of redis, so skip + if (!helper.serverVersionAtLeast(pub, [2, 6, 11])) return done(); + + pub.punsubscribe(function (err, results) { + assert.strictEqual(null, results); + return done(err); + }); + }); + }); + + afterEach(function () { + sub.end(); + pub.end(); + }); + }); + } + + ['javascript', 'hiredis'].forEach(function (parser) { + allTests(parser, "/tmp/redis.sock"); + ['IPv4', 'IPv6'].forEach(function (ip) { + allTests(parser, ip); + }) + }); +}); diff --git a/test/queue-test.js b/test/queue-test.js deleted file mode 100644 index dbc9771a0a..0000000000 --- a/test/queue-test.js +++ /dev/null @@ -1,37 +0,0 @@ -'use strict'; - -var assert = require("assert"); -var Queue = require('../lib/queue'); - -module.exports = function (tests, next) { - var q = new Queue(); - - tests.push = function () { - q.push('a'); - q.push(3); - assert.equal(q.length, 2); - return next(); - }; - - tests.shift = function () { - assert.equal(q.shift(), 'a'); - return next(); - }; - - tests.forEach = function () { - q.forEach(function (v) { - assert.equal(v, 3); - }); - - return next(); - }; - - tests.forEachWithScope = function () { - q.forEach(function (v) { - assert.equal(this.foo, 'bar'); - assert.equal(v, 3); - }, {foo: 'bar'}); - - return next(); - }; -}; diff --git a/test/queue.spec.js b/test/queue.spec.js new file mode 100644 index 0000000000..f0b3c31648 --- /dev/null +++ b/test/queue.spec.js @@ -0,0 +1,37 @@ +var assert = require("assert"); +var Queue = require('../lib/queue'); + +describe('queue', function () { + var q = new Queue(); + + describe('push', function () { + it('places values on end of queue', function () { + q.push('a'); + q.push(3); + assert.equal(q.length, 2); + }); + }); + + describe('shift', function () { + it('removes values from front of queue', function () { + assert.equal(q.shift(), 'a'); + }); + }); + + describe('forEach', function () { + it('iterates over values in queue', function () { + q.forEach(function (v) { + assert.equal(v, 3); + }); + }); + }); + + describe('forEachWithScope', function () { + it('provides a scope to the iteration function', function () { + q.forEach(function (v) { + assert.equal(this.foo, 'bar'); + assert.equal(v, 3); + }, {foo: 'bar'}); + }); + }); +}); diff --git a/test/run.sh b/test/run.sh deleted file mode 100755 index ab030ba48f..0000000000 --- a/test/run.sh +++ /dev/null @@ -1,4 +0,0 @@ -#!/usr/bin/env bash - -node ./test/test.js false hiredis -node ./test/test.js false javascript diff --git a/test/test-unref.js b/test/test-unref.js deleted file mode 100644 index c7dc930004..0000000000 --- a/test/test-unref.js +++ /dev/null @@ -1,14 +0,0 @@ -'use strict'; - -var redis = require("../"); -//redis.debug_mode = true -var PORT = process.argv[2] || 6379; -var HOST = process.argv[3] || '127.0.0.1'; - -var c = redis.createClient(PORT, HOST); -c.unref(); -c.info(function (err, reply) { - if (err) process.exit(-1); - if (!reply.length) process.exit(-1); - process.stdout.write(reply.length.toString()); -}); diff --git a/test/test.js b/test/test.js index 7b3cb1cc72..53a2b663bb 100644 --- a/test/test.js +++ b/test/test.js @@ -1,1521 +1,4 @@ -'use strict'; - -/*global require console setTimeout process Buffer */ -var PORT = process.env.REDIS_PORT_6379_TCP_PORT || 6379; -var HOST = process.env.REDIS_PORT_6379_TCP_ADDR || '127.0.0.1'; -var parser = process.argv[3]; - -var redis = require("../index"), - client = redis.createClient(PORT, HOST, { parser: parser }), - client2 = redis.createClient(PORT, HOST, { parser: parser }), - client3 = redis.createClient(PORT, HOST, { parser: parser }), - bclient = redis.createClient(PORT, HOST, { return_buffers: true, parser: parser }), - assert = require("assert"), - crypto = require("crypto"), - util = require("../lib/util"), - fork = require("child_process").fork, - test_db_num = 15, // this DB will be flushed and used for testing - tests = {}, - connected = false, - ended = false, - next, cur_start, run_next_test, all_tests, all_start, test_count; - -// Set this to truthy to see the wire protocol and other debugging info -redis.debug_mode = process.argv[2] ? JSON.parse(process.argv[2]) : false; - -function server_version_at_least(connection, desired_version) { - // Return true if the server version >= desired_version - var version = connection.server_info.versions; - for (var i = 0; i < 3; i++) { - if (version[i] > desired_version[i]) return true; - if (version[i] < desired_version[i]) return false; - } - return true; -} - -function buffers_to_strings(arr) { - return arr.map(function (val) { - return val.toString(); - }); -} - -function require_number(expected, label) { - return function (err, results) { - assert.strictEqual(null, err, label + " expected " + expected + ", got error: " + err); - assert.strictEqual(expected, results, label + " " + expected + " !== " + results); - assert.strictEqual(typeof results, "number", label); - return true; - }; -} - -function require_number_any(label) { - return function (err, results) { - assert.strictEqual(null, err, label + " expected any number, got error: " + err); - assert.strictEqual(typeof results, "number", label + " " + results + " is not a number"); - return true; - }; -} - -function require_number_pos(label) { - return function (err, results) { - assert.strictEqual(null, err, label + " expected positive number, got error: " + err); - assert.strictEqual(true, (results > 0), label + " " + results + " is not a positive number"); - return true; - }; -} - -function require_string(str, label) { - return function (err, results) { - assert.strictEqual(null, err, label + " expected string '" + str + "', got error: " + err); - assert.equal(str, results, label + " " + str + " does not match " + results); - return true; - }; -} - -function require_null(label) { - return function (err, results) { - assert.strictEqual(null, err, label + " expected null, got error: " + err); - assert.strictEqual(null, results, label + ": " + results + " is not null"); - return true; - }; -} - -function require_error(label) { - return function (err, results) { - assert.notEqual(err, null, label + " err is null, but an error is expected here."); - return true; - }; -} - -function is_empty_array(obj) { - return Array.isArray(obj) && obj.length === 0; -} - -function last(name, fn) { - return function (err, results) { - fn(err, results); - next(name); - }; -} - -// Wraps the given callback in a timeout. If the returned function -// is not called within the timeout period, we fail the named test. -function with_timeout(name, cb, millis) { - var timeoutId = setTimeout(function() { - assert.fail("Callback timed out!", name); - }, millis); - return function() { - clearTimeout(timeoutId); - cb.apply(this, arguments); - }; -} - -next = function next(name) { - console.log(" \x1b[33m" + (Date.now() - cur_start) + "\x1b[0m ms"); - run_next_test(); -}; - -// Tests are run in the order they are defined, so FLUSHDB should always be first. - -<<<<<<< HEAD -<<<<<<< HEAD -<<<<<<< HEAD -<<<<<<< HEAD -tests.IPV4 = function () { - var ipv4addr = process.env.REDIS_PORT_6379_TCP_ADDR || "127.0.0.1"; - var ipv4Client = redis.createClient( PORT, ipv4addr, { family : "IPv4", parser: parser } ); - - ipv4Client.once("ready", function start_tests() { - console.log("Connected to " + ipv4Client.address + ", Redis server version " + ipv4Client.server_info.redis_version + "\n"); - console.log("Using reply parser " + ipv4Client.reply_parser.name); - - ipv4Client.quit(); - run_next_test(); - }); - - ipv4Client.on('end', function () { - - }); - - // Exit immediately on connection failure, which triggers "exit", below, which fails the test - ipv4Client.on("error", function (err) { - console.error("client: " + err.stack); - process.exit(); - }); -}; - -tests.IPV6 = function () { - if (!server_version_at_least(client, [2, 8, 0])) { - console.log("Skipping IPV6 for old Redis server version < 2.8.0"); - return run_next_test(); - } - var ipv6addr = process.env.REDIS_PORT_6379_TCP_ADDR || "::1"; - var ipv6Client = redis.createClient( PORT, ipv6addr, { family: "IPv6", parser: parser } ); - - ipv6Client.once("ready", function start_tests() { - console.log("Connected to " + ipv6Client.address + ", Redis server version " + ipv6Client.server_info.redis_version + "\n"); - console.log("Using reply parser " + ipv6Client.reply_parser.name); - - ipv6Client.quit(); - run_next_test(); - }); - - ipv6Client.on('end', function () { - - }); - - // Exit immediately on connection failure, which triggers "exit", below, which fails the test - ipv6Client.on("error", function (err) { - console.error("client: " + err.stack); - process.exit(); - }); -}; - -tests.UNIX_SOCKET = function () { - var unixClient = redis.createClient('/tmp/redis.sock', { parser: parser }); - - // if this fails, check the permission of unix socket. - // unixsocket /tmp/redis.sock - // unixsocketperm 777 - - unixClient.once('ready', function start_tests(){ - console.log("Connected to " + unixClient.address + ", Redis server version " + unixClient.server_info.redis_version + "\n"); - console.log("Using reply parser " + unixClient.reply_parser.name); - - unixClient.quit(); - run_next_test(); - }); - - unixClient.on( 'end', function(){ - - }); - - // Exit immediately on connection failure, which triggers "exit", below, which fails the test - unixClient.on("error", function (err) { - console.error("client: " + err.stack); - process.exit(); - }); -}; - -tests.FLUSHDB = function () { - var name = "FLUSHDB"; - client.select(test_db_num, require_string("OK", name)); - client2.select(test_db_num, require_string("OK", name)); - client3.select(test_db_num, require_string("OK", name)); - client.mset("flush keys 1", "flush val 1", "flush keys 2", "flush val 2", require_string("OK", name)); - client.FLUSHDB(require_string("OK", name)); - client.dbsize(last(name, require_number(0, name))); -}; - -tests.INCR = function () { - var name = "INCR"; - - if (bclient.reply_parser.name === "hiredis") { - console.log("Skipping INCR buffer test with hiredis"); - return next(name); - } - - // Test incr with the maximum JavaScript number value. Since we are - // returning buffers we should get back one more as a Buffer. - bclient.set("seq", "9007199254740992", function (err, result) { - assert.strictEqual(result.toString(), "OK"); - bclient.incr("seq", function (err, result) { - assert.strictEqual("9007199254740993", result.toString()); - next(name); - }); - }); -}; - -tests.MULTI_1 = function () { - var name = "MULTI_1", multi1, multi2; - - // Provoke an error at queue time - multi1 = client.multi(); - multi1.mset("multifoo", "10", "multibar", "20", require_string("OK", name)); - multi1.set("foo2", require_error(name)); - multi1.incr("multifoo", require_number(11, name)); - multi1.incr("multibar", require_number(21, name)); - multi1.exec(function () { - require_error(name); - - // Redis 2.6.5+ will abort transactions with errors - // see: http://redis.io/topics/transactions - var multibar_expected = 22; - var multifoo_expected = 12; - if (server_version_at_least(client, [2, 6, 5])) { - multibar_expected = 1; - multifoo_expected = 1; - } - - // Confirm that the previous command, while containing an error, still worked. - multi2 = client.multi(); - multi2.incr("multibar", require_number(multibar_expected, name)); - multi2.incr("multifoo", require_number(multifoo_expected, name)); - multi2.exec(function (err, replies) { - assert.strictEqual(multibar_expected, replies[0]); - assert.strictEqual(multifoo_expected, replies[1]); - next(name); - }); - }); -}; - -tests.MULTI_2 = function () { - var name = "MULTI_2"; - - // test nested multi-bulk replies - client.multi([ - ["mget", "multifoo", "multibar", function (err, res) { - assert.strictEqual(2, res.length, name); - assert.strictEqual("12", res[0].toString(), name); - assert.strictEqual("22", res[1].toString(), name); - }], - ["set", "foo2", require_error(name)], - ["incr", "multifoo", require_number(13, name)], - ["incr", "multibar", require_number(23, name)] - - ]).exec(function (err, replies) { - - if (server_version_at_least(client, [2, 6, 5])) { - assert.notEqual(err, null, name); - assert.equal(replies, undefined, name); - } else { - assert.strictEqual(2, replies[0].length, name); - assert.strictEqual("12", replies[0][0].toString(), name); - assert.strictEqual("22", replies[0][1].toString(), name); - - assert.strictEqual("13", replies[1].toString()); - assert.strictEqual("23", replies[2].toString()); - } - next(name); - }); -}; - -tests.MULTI_3 = function () { - var name = "MULTI_3"; - - client.sadd("some set", "mem 1"); - client.sadd("some set", "mem 2"); - client.sadd("some set", "mem 3"); - client.sadd("some set", "mem 4"); - - // make sure empty mb reply works - client.del("some missing set"); - client.smembers("some missing set", function (err, reply) { - // make sure empty mb reply works - assert.strictEqual(true, is_empty_array(reply), name); - }); - - // test nested multi-bulk replies with empty mb elements. - client.multi([ - ["smembers", "some set"], - ["del", "some set"], - ["smembers", "some set"] - ]) - .scard("some set") - .exec(function (err, replies) { - assert.strictEqual(true, is_empty_array(replies[2]), name); - next(name); - }); -}; - -tests.MULTI_4 = function () { - var name = "MULTI_4"; - - client.multi() - .mset('some', '10', 'keys', '20') - .incr('some') - .incr('keys') - .mget('some', 'keys') - .exec(function (err, replies) { - assert.strictEqual(null, err); - assert.equal('OK', replies[0]); - assert.equal(11, replies[1]); - assert.equal(21, replies[2]); - assert.equal(11, replies[3][0].toString()); - assert.equal(21, replies[3][1].toString()); - next(name); - }); -}; - -tests.MULTI_5 = function () { - var name = "MULTI_5"; - - // test nested multi-bulk replies with nulls. - client.multi([ - ["mget", ["multifoo", "some", "random value", "keys"]], - ["incr", "multifoo"] - ]) - .exec(function (err, replies) { - assert.strictEqual(replies.length, 2, name); - assert.strictEqual(replies[0].length, 4, name); - next(name); - }); -}; - -tests.MULTI_6 = function () { - var name = "MULTI_6"; - - client.multi() - .hmset("multihash", "a", "foo", "b", 1) - .hmset("multihash", { - extra: "fancy", - things: "here" - }) - .hgetall("multihash") - .exec(function (err, replies) { - assert.strictEqual(null, err); - assert.equal("OK", replies[0]); - assert.equal(Object.keys(replies[2]).length, 4); - assert.equal("foo", replies[2].a); - assert.equal("1", replies[2].b); - assert.equal("fancy", replies[2].extra); - assert.equal("here", replies[2].things); - next(name); - }); -}; - -// THIS TEST SHOULD BE MOVED IN TO A PARSER -// SPECIFIC TESTING FILE. -tests.MULTI_7 = function () { - var name = "MULTI_7"; - - if (bclient.reply_parser.name !== "javascript") { - console.log("Skipping wire-protocol test for 3rd-party parser"); - return next(name); - } - - var p = require("../lib/parser/javascript"); - var parser = new p.Parser(false); - var reply_count = 0; - function check_reply(reply) { - assert.deepEqual(reply, [['a']], "Expecting multi-bulk reply of [['a']]"); - reply_count++; - assert.notEqual(reply_count, 4, "Should only parse 3 replies"); - } - parser.on("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')); - - next(name); -}; - -tests.FWD_ERRORS_1 = function () { - var name = "FWD_ERRORS_1"; - - var toThrow = new Error("Forced exception"); - var recordedError = null; - - var originalHandlers = { - "error": client3.listeners("error"), - "message": client3.listeners("message") - }; - client3.removeAllListeners("error"); - client3.removeAllListeners("message"); - client3.once("error", function (err) { - recordedError = err; - }); - - client3.on("message", function (channel, data) { - console.log("incoming"); - if (channel === name) { - assert.equal(data, "Some message"); - throw toThrow; - } - }); - client3.subscribe(name); - - client.publish(name, "Some message"); - setTimeout(function () { - assert.equal(recordedError, toThrow, "Should have caught our forced exception"); - client3.unsubscribe(name); - client3.removeAllListeners("message"); - originalHandlers.error.forEach(function (fn) { - client3.on("error", fn); - }); - originalHandlers.message.forEach(function (fn) { - client3.on("message", fn); - }); - next(name); - }, 150); -}; - -tests.EVAL_1 = function () { - var name = "EVAL_1"; - - if (!server_version_at_least(client, [2, 5, 0])) { - console.log("Skipping " + name + " for old Redis server version < 2.5.x"); - return next(name); - } - - // test {EVAL - Lua integer -> Redis protocol type conversion} - client.eval("return 100.5", 0, require_number(100, name)); - // test {EVAL - Lua string -> Redis protocol type conversion} - client.eval("return 'hello world'", 0, require_string("hello world", name)); - // test {EVAL - Lua true boolean -> Redis protocol type conversion} - client.eval("return true", 0, require_number(1, name)); - // test {EVAL - Lua false boolean -> Redis protocol type conversion} - client.eval("return false", 0, require_null(name)); - // test {EVAL - Lua status code reply -> Redis protocol type conversion} - client.eval("return {ok='fine'}", 0, require_string("fine", name)); - // test {EVAL - Lua error reply -> Redis protocol type conversion} - client.eval("return {err='this is an error'}", 0, require_error(name)); - // test {EVAL - Lua table -> Redis protocol type conversion} - client.eval("return {1,2,3,'ciao',{1,2}}", 0, function (err, res) { - assert.strictEqual(5, res.length, name); - assert.strictEqual(1, res[0], name); - assert.strictEqual(2, res[1], name); - assert.strictEqual(3, res[2], name); - assert.strictEqual("ciao", res[3], name); - assert.strictEqual(2, res[4].length, name); - assert.strictEqual(1, res[4][0], name); - assert.strictEqual(2, res[4][1], name); - }); - // test {EVAL - Are the KEYS and ARGS arrays populated correctly?} - client.eval("return {KEYS[1],KEYS[2],ARGV[1],ARGV[2]}", 2, "a", "b", "c", "d", function (err, res) { - assert.strictEqual(4, res.length, name); - assert.strictEqual("a", res[0], name); - assert.strictEqual("b", res[1], name); - assert.strictEqual("c", res[2], name); - assert.strictEqual("d", res[3], name); - }); - - // test {EVAL - parameters in array format gives same result} - client.eval(["return {KEYS[1],KEYS[2],ARGV[1],ARGV[2]}", 2, "a", "b", "c", "d"], function (err, res) { - assert.strictEqual(4, res.length, name); - assert.strictEqual("a", res[0], name); - assert.strictEqual("b", res[1], name); - assert.strictEqual("c", res[2], name); - assert.strictEqual("d", res[3], name); - }); - - // prepare sha sum for evalsha cache test - var source = "return redis.call('get', 'sha test')", - sha = crypto.createHash('sha1').update(source).digest('hex'); - - client.set("sha test", "eval get sha test", function (err, res) { - if (err) throw err; - // test {EVAL - is Lua able to call Redis API?} - client.eval(source, 0, function (err, res) { - require_string("eval get sha test", name)(err, res); - // test {EVALSHA - Can we call a SHA1 if already defined?} - client.evalsha(sha, 0, require_string("eval get sha test", name)); - // test {EVALSHA - Do we get an error on non defined SHA1?} - client.evalsha("ffffffffffffffffffffffffffffffffffffffff", 0, require_error(name)); - }); - }); - - // test {EVAL - Redis integer -> Lua type conversion} - client.set("incr key", 0, function (err, reply) { - if (err) throw err; - client.eval("local foo = redis.call('incr','incr key')\n" + "return {type(foo),foo}", 0, function (err, res) { - if (err) throw err; - assert.strictEqual(2, res.length, name); - assert.strictEqual("number", res[0], name); - assert.strictEqual(1, res[1], name); - }); - }); - - client.set("bulk reply key", "bulk reply value", function (err, res) { - // test {EVAL - Redis bulk -> Lua type conversion} - client.eval("local foo = redis.call('get','bulk reply key'); return {type(foo),foo}", 0, function (err, res) { - if (err) throw err; - assert.strictEqual(2, res.length, name); - assert.strictEqual("string", res[0], name); - assert.strictEqual("bulk reply value", res[1], name); - }); - }); - - // test {EVAL - Redis multi bulk -> Lua type conversion} - client.multi() - .del("mylist") - .rpush("mylist", "a") - .rpush("mylist", "b") - .rpush("mylist", "c") - .exec(function (err, replies) { - if (err) throw err; - client.eval("local foo = redis.call('lrange','mylist',0,-1); return {type(foo),foo[1],foo[2],foo[3],# foo}", 0, function (err, res) { - assert.strictEqual(5, res.length, name); - assert.strictEqual("table", res[0], name); - assert.strictEqual("a", res[1], name); - assert.strictEqual("b", res[2], name); - assert.strictEqual("c", res[3], name); - assert.strictEqual(3, res[4], name); - }); - }); - // test {EVAL - Redis status reply -> Lua type conversion} - client.eval("local foo = redis.call('set','mykey','myval'); return {type(foo),foo['ok']}", 0, function (err, res) { - if (err) throw err; - assert.strictEqual(2, res.length, name); - assert.strictEqual("table", res[0], name); - assert.strictEqual("OK", res[1], name); - }); - // test {EVAL - Redis error reply -> Lua type conversion} - client.set("error reply key", "error reply value", function (err, res) { - if (err) throw err; - client.eval("local foo = redis.pcall('incr','error reply key'); return {type(foo),foo['err']}", 0, function (err, res) { - if (err) throw err; - assert.strictEqual(2, res.length, name); - assert.strictEqual("table", res[0], name); - assert.strictEqual("ERR value is not an integer or out of range", res[1], name); - }); - }); - // test {EVAL - Redis nil bulk reply -> Lua type conversion} - client.del("nil reply key", function (err, res) { - if (err) throw err; - client.eval("local foo = redis.call('get','nil reply key'); return {type(foo),foo == false}", 0, function (err, res) { - if (err) throw err; - assert.strictEqual(2, res.length, name); - assert.strictEqual("boolean", res[0], name); - assert.strictEqual(1, res[1], name); - next(name); - }); - }); -}; - -tests.SCRIPT_LOAD = function() { - var name = "SCRIPT_LOAD", - command = "return 1", - commandSha = crypto.createHash('sha1').update(command).digest('hex'); - - if (!server_version_at_least(client, [2, 6, 0])) { - console.log("Skipping " + name + " for old Redis server version < 2.6.x"); - return next(name); - } - - bclient.script("load", command, function(err, result) { - assert.strictEqual(result.toString(), commandSha); - client.multi().script("load", command).exec(function(err, result) { - assert.strictEqual(result[0].toString(), commandSha); - client.multi([['script', 'load', command]]).exec(function(err, result) { - assert.strictEqual(result[0].toString(), commandSha); - next(name); - }); - }); - }); -}; - -tests.CLIENT_LIST = function() { - var name = "CLIENT_LIST"; - - if (!server_version_at_least(client, [2, 4, 0])) { - console.log("Skipping " + name + " for old Redis server version < 2.4.x"); - return next(name); - } - - var pattern = /^addr=/; - if ( server_version_at_least(client, [2, 8, 12])) { - pattern = /^id=\d+ addr=/; - } - - function checkResult(result) { - var lines = result.toString().split('\n').slice(0, -1); - assert.strictEqual(lines.length, 4); - assert(lines.every(function(line) { - return line.match(pattern); - })); - } - - bclient.client("list", function(err, result) { - console.log(result.toString()); - checkResult(result); - client.multi().client("list").exec(function(err, result) { - console.log(result.toString()); - checkResult(result); - client.multi([['client', 'list']]).exec(function(err, result) { - console.log(result.toString()); - checkResult(result); - next(name); - }); - }); - }); -}; - -tests.WATCH_MULTI = function () { - var name = 'WATCH_MULTI', multi; - if (!server_version_at_least(client, [2, 2, 0])) { - console.log("Skipping " + name + " for old Redis server version < 2.2.x"); - return next(name); - } - - client.watch(name); - client.incr(name); - multi = client.multi(); - multi.incr(name); - multi.exec(last(name, require_null(name))); -}; - -tests.WATCH_TRANSACTION = function () { - var name = "WATCH_TRANSACTION"; - - if (!server_version_at_least(client, [2, 1, 0])) { - console.log("Skipping " + name + " because server version isn't new enough."); - return next(name); - } - - // Test WATCH command aborting transactions, look for parser offset errors. - - client.set("unwatched", 200); - - client.set(name, 0); - client.watch(name); - client.incr(name); - var multi = client.multi() - .incr(name) - .exec(function (err, replies) { - // Failure expected because of pre-multi incr - assert.strictEqual(replies, null, "Aborted transaction multi-bulk reply should be null."); - - client.get("unwatched", function (err, reply) { - assert.equal(err, null, name); - assert.equal(reply, 200, "Expected 200, got " + reply); - next(name); - }); - }); - - client.set("unrelated", 100, function (err, reply) { - assert.equal(err, null, name); - assert.equal(reply, "OK", "Expected 'OK', got " + reply); - }); -}; - - -tests.detect_buffers = function () { - var name = "detect_buffers", detect_client = redis.createClient(PORT, HOST, { detect_buffers: true, parser: parser }); - - detect_client.on("ready", function () { - // single Buffer or String - detect_client.set("string key 1", "string value"); - detect_client.get("string key 1", require_string("string value", name)); - detect_client.get(new Buffer("string key 1"), function (err, reply) { - assert.strictEqual(null, err, name); - assert.strictEqual(true, Buffer.isBuffer(reply), name); - assert.strictEqual("", reply.inspect(), name); - }); - - detect_client.hmset("hash key 2", "key 1", "val 1", "key 2", "val 2"); - // array of Buffers or Strings - detect_client.hmget("hash key 2", "key 1", "key 2", function (err, reply) { - assert.strictEqual(null, err, name); - assert.strictEqual(true, Array.isArray(reply), name); - assert.strictEqual(2, reply.length, name); - assert.strictEqual("val 1", reply[0], name); - assert.strictEqual("val 2", reply[1], name); - }); - detect_client.hmget(new Buffer("hash key 2"), "key 1", "key 2", function (err, reply) { - assert.strictEqual(null, err, name); - assert.strictEqual(true, Array.isArray(reply)); - assert.strictEqual(2, reply.length, name); - assert.strictEqual(true, Buffer.isBuffer(reply[0])); - assert.strictEqual(true, Buffer.isBuffer(reply[1])); - assert.strictEqual("", reply[0].inspect(), name); - assert.strictEqual("", reply[1].inspect(), name); - }); - - // array of strings with undefined values (repro #344) - detect_client.hmget("hash key 2", "key 3", "key 4", function(err, reply) { - assert.strictEqual(null, err, name); - assert.strictEqual(true, Array.isArray(reply), name); - assert.strictEqual(2, reply.length, name); - assert.equal(null, reply[0], name); - assert.equal(null, reply[1], name); - }); - - // Object of Buffers or Strings - detect_client.hgetall("hash key 2", function (err, reply) { - assert.strictEqual(null, err, name); - assert.strictEqual("object", typeof reply, name); - assert.strictEqual(2, Object.keys(reply).length, name); - assert.strictEqual("val 1", reply["key 1"], name); - assert.strictEqual("val 2", reply["key 2"], name); - }); - detect_client.hgetall(new Buffer("hash key 2"), function (err, reply) { - assert.strictEqual(null, err, name); - assert.strictEqual("object", typeof reply, name); - assert.strictEqual(2, Object.keys(reply).length, name); - assert.strictEqual(true, Buffer.isBuffer(reply["key 1"])); - assert.strictEqual(true, Buffer.isBuffer(reply["key 2"])); - assert.strictEqual("", reply["key 1"].inspect(), name); - assert.strictEqual("", reply["key 2"].inspect(), name); - }); - - detect_client.quit(function (err, res) { - next(name); - }); - }); -}; - -tests.detect_buffers_multi = function () { - var name = "detect_buffers_multi", detect_client = redis.createClient(PORT, HOST, {detect_buffers: true}); - - detect_client.on("ready", function () { - // single Buffer or String - detect_client.set("string key 1", "string value"); - detect_client.multi().get("string key 1").exec(require_string("string value", name)); - detect_client.multi().get(new Buffer("string key 1")).exec(function (err, reply) { - assert.strictEqual(null, err, name); - assert.strictEqual(1, reply.length, name); - assert.strictEqual(true, Buffer.isBuffer(reply[0]), name); - assert.strictEqual("", reply[0].inspect(), name); - }); - - detect_client.hmset("hash key 2", "key 1", "val 1", "key 2", "val 2"); - // array of Buffers or Strings - detect_client.multi().hmget("hash key 2", "key 1", "key 2").exec(function (err, reply) { - assert.strictEqual(null, err, name); - assert.strictEqual(true, Array.isArray(reply), name); - assert.strictEqual(1, reply.length, name); - assert.strictEqual(2, reply[0].length, name); - assert.strictEqual("val 1", reply[0][0], name); - assert.strictEqual("val 2", reply[0][1], name); - }); - detect_client.multi().hmget(new Buffer("hash key 2"), "key 1", "key 2").exec(function (err, reply) { - assert.strictEqual(null, err, name); - assert.strictEqual(true, Array.isArray(reply)); - assert.strictEqual(1, reply.length, name); - assert.strictEqual(2, reply[0].length, name); - assert.strictEqual(true, Buffer.isBuffer(reply[0][0])); - assert.strictEqual(true, Buffer.isBuffer(reply[0][1])); - assert.strictEqual("", reply[0][0].inspect(), name); - assert.strictEqual("", reply[0][1].inspect(), name); - }); - - // array of strings with undefined values (repro #344) - detect_client.multi().hmget("hash key 2", "key 3", "key 4").exec(function(err, reply) { - assert.strictEqual(null, err, name); - assert.strictEqual(true, Array.isArray(reply), name); - assert.strictEqual(1, reply.length, name); - assert.strictEqual(2, reply[0].length, name); - assert.equal(null, reply[0][0], name); - assert.equal(null, reply[0][1], name); - }); - - // Object of Buffers or Strings - detect_client.multi().hgetall("hash key 2").exec(function (err, reply) { - assert.strictEqual(null, err, name); - assert.strictEqual(1, reply.length, name); - assert.strictEqual("object", typeof reply[0], name); - assert.strictEqual(2, Object.keys(reply[0]).length, name); - assert.strictEqual("val 1", reply[0]["key 1"], name); - assert.strictEqual("val 2", reply[0]["key 2"], name); - }); - detect_client.multi().hgetall(new Buffer("hash key 2")).exec(function (err, reply) { - assert.strictEqual(null, err, name); - assert.strictEqual(1, reply.length, name); - assert.strictEqual("object", typeof reply, name); - assert.strictEqual(2, Object.keys(reply[0]).length, name); - assert.strictEqual(true, Buffer.isBuffer(reply[0]["key 1"])); - assert.strictEqual(true, Buffer.isBuffer(reply[0]["key 2"])); - assert.strictEqual("", reply[0]["key 1"].inspect(), name); - assert.strictEqual("", reply[0]["key 2"].inspect(), name); - }); - - // Can interleave string and buffer results: - detect_client.multi() - .hget("hash key 2", "key 1") - .hget(new Buffer("hash key 2"), "key 1") - .hget("hash key 2", new Buffer("key 2")) - .hget("hash key 2", "key 2") - .exec(function (err, reply) { - assert.strictEqual(null, err, name); - assert.strictEqual(true, Array.isArray(reply)); - assert.strictEqual(4, reply.length, name); - assert.strictEqual("val 1", reply[0], name); - assert.strictEqual(true, Buffer.isBuffer(reply[1])); - assert.strictEqual("", reply[1].inspect(), name); - assert.strictEqual(true, Buffer.isBuffer(reply[2])); - assert.strictEqual("", reply[2].inspect(), name); - assert.strictEqual("val 2", reply[3], name); - }); - - detect_client.quit(function (err, res) { - next(name); - }); - }); -}; - -tests.socket_nodelay = function () { - var name = "socket_nodelay", c1, c2, c3, ready_count = 0, quit_count = 0; - - c1 = redis.createClient(PORT, HOST, { socket_nodelay: true, parser: parser }); - c2 = redis.createClient(PORT, HOST, { socket_nodelay: false, parser: parser }); - c3 = redis.createClient(PORT, HOST, { parser: parser }); - - function quit_check() { - quit_count++; - - if (quit_count === 3) { - next(name); - } - } - - function run() { - assert.strictEqual(true, c1.options.socket_nodelay, name); - assert.strictEqual(false, c2.options.socket_nodelay, name); - assert.strictEqual(true, c3.options.socket_nodelay, name); - - c1.set(["set key 1", "set val"], require_string("OK", name)); - c1.set(["set key 2", "set val"], require_string("OK", name)); - c1.get(["set key 1"], require_string("set val", name)); - c1.get(["set key 2"], require_string("set val", name)); - - c2.set(["set key 3", "set val"], require_string("OK", name)); - c2.set(["set key 4", "set val"], require_string("OK", name)); - c2.get(["set key 3"], require_string("set val", name)); - c2.get(["set key 4"], require_string("set val", name)); - - c3.set(["set key 5", "set val"], require_string("OK", name)); - c3.set(["set key 6", "set val"], require_string("OK", name)); - c3.get(["set key 5"], require_string("set val", name)); - c3.get(["set key 6"], require_string("set val", name)); - - c1.quit(quit_check); - c2.quit(quit_check); - c3.quit(quit_check); - } - - function ready_check() { - ready_count++; - if (ready_count === 3) { - run(); - } - } - - c1.on("ready", ready_check); - c2.on("ready", ready_check); - c3.on("ready", ready_check); -}; - - -tests.idle = function () { - var name = "idle"; - - client.on("idle", function on_idle() { - client.removeListener("idle", on_idle); - next(name); - }); - - client.set("idle", "test"); -}; - -tests.HSET = function () { - var key = "test hash", - field1 = new Buffer("0123456789"), - value1 = new Buffer("abcdefghij"), - field2 = new Buffer(0), - value2 = new Buffer(0), - name = "HSET"; - - client.HSET(key, field1, value1, require_number(1, name)); - client.HGET(key, field1, require_string(value1.toString(), name)); - - // Empty value - client.HSET(key, field1, value2, require_number(0, name)); - client.HGET([key, field1], require_string("", name)); - - // Empty key, empty value - client.HSET([key, field2, value1], require_number(1, name)); - client.HSET(key, field2, value2, last(name, require_number(0, name))); -}; - -tests.HLEN = function () { - var key = "test hash", - field1 = new Buffer("0123456789"), - value1 = new Buffer("abcdefghij"), - field2 = new Buffer(0), - value2 = new Buffer(0), - name = "HSET", - timeout = 1000; - - client.HSET(key, field1, value1, function (err, results) { - client.HLEN(key, function (err, len) { - assert.ok(2 === +len); - next(name); - }); - }); -}; - -tests.HMSET_BUFFER_AND_ARRAY = function () { - // Saving a buffer and an array to the same key should not error - var key = "test hash", - field1 = "buffer", - value1 = new Buffer("abcdefghij"), - field2 = "array", - value2 = ["array contents"], - name = "HSET"; - - client.HMSET(key, field1, value1, field2, value2, last(name, require_string("OK", name))); -}; - -// TODO - add test for HMSET with optional callbacks - -tests.HMGET = function () { - var key1 = "test hash 1", key2 = "test hash 2", key3 = 123456789, name = "HMGET"; - - // redis-like hmset syntax - client.HMSET(key1, "0123456789", "abcdefghij", "some manner of key", "a type of value", require_string("OK", name)); - - // fancy hmset syntax - client.HMSET(key2, { - "0123456789": "abcdefghij", - "some manner of key": "a type of value" - }, require_string("OK", name)); - - // test for numeric key - client.HMSET(key3, { - "0123456789": "abcdefghij", - "some manner of key": "a type of value" - }, require_string("OK", name)); - - client.HMGET(key1, "0123456789", "some manner of key", function (err, reply) { - assert.strictEqual("abcdefghij", reply[0].toString(), name); - assert.strictEqual("a type of value", reply[1].toString(), name); - }); - - client.HMGET(key2, "0123456789", "some manner of key", function (err, reply) { - assert.strictEqual("abcdefghij", reply[0].toString(), name); - assert.strictEqual("a type of value", reply[1].toString(), name); - }); - - client.HMGET(key3, "0123456789", "some manner of key", function (err, reply) { - assert.strictEqual("abcdefghij", reply[0].toString(), name); - assert.strictEqual("a type of value", reply[1].toString(), name); - }); - - client.HMGET(key1, ["0123456789"], function (err, reply) { - assert.strictEqual("abcdefghij", reply[0], name); - }); - - client.HMGET(key1, ["0123456789", "some manner of key"], function (err, reply) { - assert.strictEqual("abcdefghij", reply[0], name); - assert.strictEqual("a type of value", reply[1], name); - }); - - client.HMGET(key1, "missing thing", "another missing thing", function (err, reply) { - assert.strictEqual(null, reply[0], name); - assert.strictEqual(null, reply[1], name); - next(name); - }); -}; - -tests.HINCRBY = function () { - var name = "HINCRBY"; - client.hset("hash incr", "value", 10, require_number(1, name)); - client.HINCRBY("hash incr", "value", 1, require_number(11, name)); - client.HINCRBY("hash incr", "value 2", 1, last(name, require_number(1, name))); -}; - -tests.SUBSCRIBE = function () { - var client1 = client, msg_count = 0, name = "SUBSCRIBE"; - - client1.on("subscribe", function (channel, count) { - if (channel === "chan1") { - client2.publish("chan1", "message 1", require_number(1, name)); - client2.publish("chan2", "message 2", require_number(1, name)); - client2.publish("chan1", "message 3", require_number(1, name)); - } - }); - - client1.on("unsubscribe", function (channel, count) { - if (count === 0) { - // make sure this connection can go into and out of pub/sub mode - client1.incr("did a thing", last(name, require_number(2, name))); - } - }); - - client1.on("message", function (channel, message) { - msg_count += 1; - assert.strictEqual("message " + msg_count, message.toString()); - if (msg_count === 3) { - client1.unsubscribe("chan1", "chan2"); - } - }); - - client1.set("did a thing", 1, require_string("OK", name)); - client1.subscribe("chan1", "chan2", function (err, results) { - assert.strictEqual(null, err, "result sent back unexpected error: " + err); - assert.strictEqual("chan1", results.toString(), name); - }); -}; - -tests.UNSUB_EMPTY = function () { - // test situation where unsubscribe reply[1] is null - var name = "UNSUB_EMPTY"; - client3.unsubscribe(); // unsubscribe from all so can test null - client3.unsubscribe(); // reply[1] will be null - next(name); -}; - -tests.PUNSUB_EMPTY = function () { - // test situation where punsubscribe reply[1] is null - var name = "PUNSUB_EMPTY"; - client3.punsubscribe(); // punsubscribe from all so can test null - client3.punsubscribe(); // reply[1] will be null - next(name); -}; - -tests.UNSUB_EMPTY_CB = function () { - var name = "UNSUB_EMPTY_CB"; - // test hangs on older versions of redis, so skip - if (!server_version_at_least(client, [2, 6, 11])) return next(name); - - // test situation where unsubscribe reply[1] is null - client3.unsubscribe(); // unsubscribe from all so can test null - client3.unsubscribe(function (err, results) { - // reply[1] will be null - assert.strictEqual(null, err, "unexpected error: " + err); - next(name); - }); -}; - -tests.PUNSUB_EMPTY_CB = function () { - var name = "PUNSUB_EMPTY_CB"; - // test hangs on older versions of redis, so skip - if (!server_version_at_least(client, [2, 6, 11])) return next(name); - - // test situation where punsubscribe reply[1] is null - client3.punsubscribe(); // punsubscribe from all so can test null - client3.punsubscribe(function (err, results) { - // reply[1] will be null - assert.strictEqual(null, err, "unexpected error: " + err); - next(name); - }); -}; - -tests.SUB_UNSUB_SUB = function () { - var name = "SUB_UNSUB_SUB"; - // test hangs on older versions of redis, so skip - if (!server_version_at_least(client, [2, 6, 11])) return next(name); - - client3.subscribe('chan3'); - client3.unsubscribe('chan3'); - client3.subscribe('chan3', function (err, results) { - assert.strictEqual(null, err, "unexpected error: " + err); - client2.publish('chan3', 'foo'); - }); - client3.on('message', function (channel, message) { - assert.strictEqual(channel, 'chan3'); - assert.strictEqual(message, 'foo'); - client3.removeAllListeners(); - next(name); - }); -}; - -tests.SUB_UNSUB_MSG_SUB = function () { - var name = "SUB_UNSUB_MSG_SUB"; - // test hangs on older versions of redis, so skip - if (!server_version_at_least(client, [2, 6, 11])) return next(name); - - client3.subscribe('chan8'); - client3.subscribe('chan9'); - client3.unsubscribe('chan9'); - client2.publish('chan8', 'something'); - client3.subscribe('chan9', with_timeout(name, function (err, results) { - next(name); - }, 2000)); -}; - -tests.PSUB_UNSUB_PMSG_SUB = function () { - var name = "PSUB_UNSUB_PMSG_SUB"; - // test hangs on older versions of redis, so skip - if (!server_version_at_least(client, [2, 6, 11])) return next(name); - - client3.psubscribe('abc*'); - client3.subscribe('xyz'); - client3.unsubscribe('xyz'); - client2.publish('abcd', 'something'); - client3.subscribe('xyz', with_timeout(name, function (err, results) { - next(name); - }, 2000)); -}; - -tests.SUBSCRIBE_QUIT = function () { - var name = "SUBSCRIBE_QUIT"; - client3.on("end", function () { - next(name); - }); - client3.on("subscribe", function (channel, count) { - client3.quit(); - }); - client3.subscribe("chan3"); -}; - -tests.SUBSCRIBE_CLOSE_RESUBSCRIBE = function () { - var name = "SUBSCRIBE_CLOSE_RESUBSCRIBE"; - var c1 = redis.createClient(PORT, HOST, { parser: parser }); - var c2 = redis.createClient(PORT, HOST, { parser: parser }); - var count = 0; - - /* Create two clients. c1 subscribes to two channels, c2 will publish to them. - c2 publishes the first message. - c1 gets the message and drops its connection. It must resubscribe itself. - When it resubscribes, c2 publishes the second message, on the same channel - c1 gets the message and drops its connection. It must resubscribe itself, again. - When it resubscribes, c2 publishes the third message, on the second channel - c1 gets the message and drops its connection. When it reconnects, the test ends. - */ - - c1.on("message", function(channel, message) { - if (channel === "chan1") { - assert.strictEqual(message, "hi on channel 1"); - c1.stream.end(); - - } else if (channel === "chan2") { - assert.strictEqual(message, "hi on channel 2"); - c1.stream.end(); - - } else { - c1.quit(); - c2.quit(); - assert.fail("test failed"); - } - }); - - c1.subscribe("chan1", "chan2"); - - c2.once("ready", function() { - console.log("c2 is ready"); - c1.on("ready", function(err, results) { - console.log("c1 is ready", count); - - count++; - if (count === 1) { - c2.publish("chan1", "hi on channel 1"); - return; - - } else if (count === 2) { - c2.publish("chan2", "hi on channel 2"); - - } else { - c1.quit(function() { - c2.quit(function() { - next(name); - }); - }); - } - }); - - c2.publish("chan1", "hi on channel 1"); - - }); -}; - -tests.EXISTS = function () { - var name = "EXISTS"; - client.del("foo", "foo2", require_number_any(name)); - client.set("foo", "bar", require_string("OK", name)); - client.EXISTS("foo", require_number(1, name)); - client.EXISTS("foo2", last(name, require_number(0, name))); -}; - -tests.DEL = function () { - var name = "DEL"; - client.DEL("delkey", require_number_any(name)); - client.set("delkey", "delvalue", require_string("OK", name)); - client.DEL("delkey", require_number(1, name)); - client.exists("delkey", require_number(0, name)); - client.DEL("delkey", require_number(0, name)); - client.mset("delkey", "delvalue", "delkey2", "delvalue2", require_string("OK", name)); - client.DEL("delkey", "delkey2", last(name, require_number(2, name))); -}; - -tests.TYPE = function () { - var name = "TYPE"; - client.set(["string key", "should be a string"], require_string("OK", name)); - client.rpush(["list key", "should be a list"], require_number_pos(name)); - client.sadd(["set key", "should be a set"], require_number_any(name)); - client.zadd(["zset key", "10.0", "should be a zset"], require_number_any(name)); - client.hset(["hash key", "hashtest", "should be a hash"], require_number_any(0, name)); - - client.TYPE(["string key"], require_string("string", name)); - client.TYPE(["list key"], require_string("list", name)); - client.TYPE(["set key"], require_string("set", name)); - client.TYPE(["zset key"], require_string("zset", name)); - client.TYPE("not here yet", require_string("none", name)); - client.TYPE(["hash key"], last(name, require_string("hash", name))); -}; - -tests.KEYS = function () { - var name = "KEYS"; - client.mset(["test keys 1", "test val 1", "test keys 2", "test val 2"], require_string("OK", name)); - client.KEYS(["test keys*"], function (err, results) { - assert.strictEqual(null, err, "result sent back unexpected error: " + err); - assert.strictEqual(2, results.length, name); - assert.ok(~results.indexOf("test keys 1")); - assert.ok(~results.indexOf("test keys 2")); - next(name); - }); -}; - -tests.MULTIBULK = function() { - var name = "MULTIBULK", - keys_values = []; - - for (var i = 0; i < 200; i++) { - var key_value = [ - "multibulk:" + crypto.randomBytes(256).toString("hex"), // use long strings as keys to ensure generation of large packet - "test val " + i - ]; - keys_values.push(key_value); - } - - client.mset(keys_values.reduce(function(a, b) { - return a.concat(b); - }), require_string("OK", name)); - - client.KEYS("multibulk:*", function(err, results) { - assert.strictEqual(null, err, "result sent back unexpected error: " + err); - assert.deepEqual(keys_values.map(function(val) { - return val[0]; - }).sort(), results.sort(), name); - }); - - next(name); -}; - -tests.MULTIBULK_ZERO_LENGTH = function () { - var name = "MULTIBULK_ZERO_LENGTH"; - client.KEYS(['users:*'], function (err, results) { - assert.strictEqual(null, err, 'error on empty multibulk reply'); - assert.strictEqual(true, is_empty_array(results), "not an empty array"); - next(name); - }); -}; - -tests.RANDOMKEY = function () { - var name = "RANDOMKEY"; - client.mset(["test keys 1", "test val 1", "test keys 2", "test val 2"], require_string("OK", name)); - client.RANDOMKEY([], function (err, results) { - assert.strictEqual(null, err, name + " result sent back unexpected error: " + err); - assert.strictEqual(true, /\w+/.test(results), name); - next(name); - }); -}; - -tests.RENAME = function () { - var name = "RENAME"; - client.set(['foo', 'bar'], require_string("OK", name)); - client.RENAME(["foo", "new foo"], require_string("OK", name)); - client.exists(["foo"], require_number(0, name)); - client.exists(["new foo"], last(name, require_number(1, name))); -}; - -tests.RENAMENX = function () { - var name = "RENAMENX"; - client.set(['foo', 'bar'], require_string("OK", name)); - client.set(['foo2', 'bar2'], require_string("OK", name)); - client.RENAMENX(["foo", "foo2"], require_number(0, name)); - client.exists(["foo"], require_number(1, name)); - client.exists(["foo2"], require_number(1, name)); - client.del(["foo2"], require_number(1, name)); - client.RENAMENX(["foo", "foo2"], require_number(1, name)); - client.exists(["foo"], require_number(0, name)); - client.exists(["foo2"], last(name, require_number(1, name))); -}; - - -tests.MGET = function () { - var name = "MGET"; - client.mset(["mget keys 1", "mget val 1", "mget keys 2", "mget val 2", "mget keys 3", "mget val 3"], require_string("OK", name)); - client.MGET("mget keys 1", "mget keys 2", "mget keys 3", function (err, results) { - assert.strictEqual(null, err, "result sent back unexpected error: " + err); - assert.strictEqual(3, results.length, name); - assert.strictEqual("mget val 1", results[0].toString(), name); - assert.strictEqual("mget val 2", results[1].toString(), name); - assert.strictEqual("mget val 3", results[2].toString(), name); - }); - client.MGET(["mget keys 1", "mget keys 2", "mget keys 3"], function (err, results) { - assert.strictEqual(null, err, "result sent back unexpected error: " + err); - assert.strictEqual(3, results.length, name); - assert.strictEqual("mget val 1", results[0].toString(), name); - assert.strictEqual("mget val 2", results[1].toString(), name); - assert.strictEqual("mget val 3", results[2].toString(), name); - }); - client.MGET(["mget keys 1", "some random shit", "mget keys 2", "mget keys 3"], function (err, results) { - assert.strictEqual(null, err, "result sent back unexpected error: " + err); - assert.strictEqual(4, results.length, name); - assert.strictEqual("mget val 1", results[0].toString(), name); - assert.strictEqual(null, results[1], name); - assert.strictEqual("mget val 2", results[2].toString(), name); - assert.strictEqual("mget val 3", results[3].toString(), name); - next(name); - }); -}; - -tests.SETNX = function () { - var name = "SETNX"; - client.set(["setnx key", "setnx value"], require_string("OK", name)); - client.SETNX(["setnx key", "new setnx value"], require_number(0, name)); - client.del(["setnx key"], require_number(1, name)); - client.exists(["setnx key"], require_number(0, name)); - client.SETNX(["setnx key", "new setnx value"], require_number(1, name)); - client.exists(["setnx key"], last(name, require_number(1, name))); -}; - -tests.SETEX = function () { - var name = "SETEX"; - client.SETEX(["setex key", "100", "setex val"], require_string("OK", name)); - client.exists(["setex key"], require_number(1, name)); - client.ttl(["setex key"], last(name, require_number_pos(name))); - client.SETEX(["setex key", "100", undefined], require_error(name)); -}; - -tests.MSETNX = function () { - var name = "MSETNX"; - client.mset(["mset1", "val1", "mset2", "val2", "mset3", "val3"], require_string("OK", name)); - client.MSETNX(["mset3", "val3", "mset4", "val4"], require_number(0, name)); - client.del(["mset3"], require_number(1, name)); - client.MSETNX(["mset3", "val3", "mset4", "val4"], require_number(1, name)); - client.exists(["mset3"], require_number(1, name)); - client.exists(["mset4"], last(name, require_number(1, name))); -}; - -tests.HGETALL = function () { - var name = "HGETALL"; - client.hmset(["hosts", "mjr", "1", "another", "23", "home", "1234"], require_string("OK", name)); - client.HGETALL(["hosts"], function (err, obj) { - assert.strictEqual(null, err, name + " result sent back unexpected error: " + err); - assert.strictEqual(3, Object.keys(obj).length, name); - assert.strictEqual("1", obj.mjr.toString(), name); - assert.strictEqual("23", obj.another.toString(), name); - assert.strictEqual("1234", obj.home.toString(), name); - next(name); - }); -}; - -tests.HGETALL_2 = function () { - var name = "HGETALL (Binary client)"; - bclient.hmset(["bhosts", "mjr", "1", "another", "23", "home", "1234", new Buffer([0xAA, 0xBB, 0x00, 0xF0]), new Buffer([0xCC, 0xDD, 0x00, 0xF0])], require_string("OK", name)); - bclient.HGETALL(["bhosts"], function (err, obj) { - assert.strictEqual(null, err, name + " result sent back unexpected error: " + err); - assert.strictEqual(4, Object.keys(obj).length, name); - assert.strictEqual("1", obj.mjr.toString(), name); - assert.strictEqual("23", obj.another.toString(), name); - assert.strictEqual("1234", obj.home.toString(), name); - assert.strictEqual((new Buffer([0xAA, 0xBB, 0x00, 0xF0])).toString('binary'), Object.keys(obj)[3], name); - assert.strictEqual((new Buffer([0xCC, 0xDD, 0x00, 0xF0])).toString('binary'), obj[(new Buffer([0xAA, 0xBB, 0x00, 0xF0])).toString('binary')].toString('binary'), name); - next(name); - }); -}; - -tests.HGETALL_MESSAGE = function () { - var name = "HGETALL_MESSAGE"; - client.hmset("msg_test", {message: "hello"}, require_string("OK", name)); - client.hgetall("msg_test", function (err, obj) { - assert.strictEqual(null, err, name + " result sent back unexpected error: " + err); - assert.strictEqual(1, Object.keys(obj).length, name); - assert.strictEqual(obj.message, "hello"); - next(name); - }); -}; - -tests.HGETALL_NULL = function () { - var name = "HGETALL_NULL"; - - client.hgetall("missing", function (err, obj) { - assert.strictEqual(null, err); - assert.strictEqual(null, obj); - next(name); - }); -}; - -tests.UTF8 = function () { - var name = "UTF8", - utf8_sample = "ಠ_ಠ"; - - client.set(["utf8test", utf8_sample], require_string("OK", name)); - client.get(["utf8test"], function (err, obj) { - assert.strictEqual(null, err); - assert.strictEqual(utf8_sample, obj); - next(name); - }); -}; - -// Set tests were adapted from Brian Hammond's redis-node-client.js, which has a comprehensive test suite - -tests.SADD = function () { - var name = "SADD"; - - client.del('set0'); - client.SADD('set0', 'member0', require_number(1, name)); - client.sadd('set0', 'member0', last(name, require_number(0, name))); -}; - -tests.SADD2 = function () { - var name = "SADD2"; - - client.del("set0"); - client.sadd("set0", ["member0", "member1", "member2"], require_number(3, name)); - client.smembers("set0", function (err, res) { - assert.strictEqual(res.length, 3); - assert.ok(~res.indexOf("member0")); - assert.ok(~res.indexOf("member1")); - assert.ok(~res.indexOf("member2")); - }); - client.SADD("set1", ["member0", "member1", "member2"], require_number(3, name)); - client.smembers("set1", function (err, res) { - assert.strictEqual(res.length, 3); - assert.ok(~res.indexOf("member0")); - assert.ok(~res.indexOf("member1")); - assert.ok(~res.indexOf("member2")); - next(name); - }); -}; - -tests.SISMEMBER = function () { - var name = "SISMEMBER"; - - client.del('set0'); - client.sadd('set0', 'member0', require_number(1, name)); - client.sismember('set0', 'member0', require_number(1, name)); - client.sismember('set0', 'member1', last(name, require_number(0, name))); -}; - -tests.SCARD = function () { - var name = "SCARD"; - - client.del('set0'); - client.sadd('set0', 'member0', require_number(1, name)); - client.scard('set0', require_number(1, name)); - client.sadd('set0', 'member1', require_number(1, name)); - client.scard('set0', last(name, require_number(2, name))); -}; - -tests.SREM = function () { - var name = "SREM"; - - client.del('set0'); - client.sadd('set0', 'member0', require_number(1, name)); - client.srem('set0', 'foobar', require_number(0, name)); - client.srem('set0', 'member0', require_number(1, name)); - client.scard('set0', last(name, require_number(0, name))); -}; - - -tests.SREM2 = function () { - var name = "SREM2"; - client.del("set0"); - client.sadd("set0", ["member0", "member1", "member2"], require_number(3, name)); - client.SREM("set0", ["member1", "member2"], require_number(2, name)); - client.smembers("set0", function (err, res) { - assert.strictEqual(res.length, 1); - assert.ok(~res.indexOf("member0")); - }); - client.sadd("set0", ["member3", "member4", "member5"], require_number(3, name)); - client.srem("set0", ["member0", "member6"], require_number(1, name)); - client.smembers("set0", function (err, res) { - assert.strictEqual(res.length, 3); - assert.ok(~res.indexOf("member3")); - assert.ok(~res.indexOf("member4")); - assert.ok(~res.indexOf("member5")); - next(name); - }); -}; +return; tests.SPOP = function () { var name = "SPOP"; @@ -1633,61 +116,6 @@ tests.SMOVE = function () { client.smove('foo', 'bar', 'x', last(name, require_number(0, name))); }; -tests.SINTER = function () { - var name = "SINTER"; - - client.del('sa'); - client.del('sb'); - client.del('sc'); - - client.sadd('sa', 'a', require_number(1, name)); - client.sadd('sa', 'b', require_number(1, name)); - client.sadd('sa', 'c', require_number(1, name)); - - client.sadd('sb', 'b', require_number(1, name)); - client.sadd('sb', 'c', require_number(1, name)); - client.sadd('sb', 'd', require_number(1, name)); - - client.sadd('sc', 'c', require_number(1, name)); - client.sadd('sc', 'd', require_number(1, name)); - client.sadd('sc', 'e', require_number(1, name)); - - client.sinter('sa', 'sb', function (err, intersection) { - if (err) { - assert.fail(err, name); - } - assert.equal(intersection.length, 2, name); - assert.deepEqual(buffers_to_strings(intersection).sort(), [ 'b', 'c' ], name); - }); - - client.sinter('sb', 'sc', function (err, intersection) { - if (err) { - assert.fail(err, name); - } - assert.equal(intersection.length, 2, name); - assert.deepEqual(buffers_to_strings(intersection).sort(), [ 'c', 'd' ], name); - }); - - client.sinter('sa', 'sc', function (err, intersection) { - if (err) { - assert.fail(err, name); - } - assert.equal(intersection.length, 1, name); - assert.equal(intersection[0], 'c', name); - }); - - // 3-way - - client.sinter('sa', 'sb', 'sc', function (err, intersection) { - if (err) { - assert.fail(err, name); - } - assert.equal(intersection.length, 1, name); - assert.equal(intersection[0], 'c', name); - next(name); - }); -}; - tests.SINTERSTORE = function () { var name = "SINTERSTORE"; @@ -1784,126 +212,6 @@ tests.SUNIONSTORE = function () { }); }; -// SORT test adapted from Brian Hammond's redis-node-client.js, which has a comprehensive test suite - -tests.SORT = function () { - var name = "SORT"; - - client.del('y'); - client.del('x'); - - client.rpush('y', 'd', require_number(1, name)); - client.rpush('y', 'b', require_number(2, name)); - client.rpush('y', 'a', require_number(3, name)); - client.rpush('y', 'c', require_number(4, name)); - - client.rpush('x', '3', require_number(1, name)); - client.rpush('x', '9', require_number(2, name)); - client.rpush('x', '2', require_number(3, name)); - client.rpush('x', '4', require_number(4, name)); - - client.set('w3', '4', require_string("OK", name)); - client.set('w9', '5', require_string("OK", name)); - client.set('w2', '12', require_string("OK", name)); - client.set('w4', '6', require_string("OK", name)); - - client.set('o2', 'buz', require_string("OK", name)); - client.set('o3', 'foo', require_string("OK", name)); - client.set('o4', 'baz', require_string("OK", name)); - client.set('o9', 'bar', require_string("OK", name)); - - client.set('p2', 'qux', require_string("OK", name)); - client.set('p3', 'bux', require_string("OK", name)); - client.set('p4', 'lux', require_string("OK", name)); - client.set('p9', 'tux', require_string("OK", name)); - - // Now the data has been setup, we can test. - - // But first, test basic sorting. - - // y = [ d b a c ] - // sort y ascending = [ a b c d ] - // sort y descending = [ d c b a ] - - client.sort('y', 'asc', 'alpha', function (err, sorted) { - if (err) { - assert.fail(err, name); - } - assert.deepEqual(buffers_to_strings(sorted), ['a', 'b', 'c', 'd'], name); - }); - - client.sort('y', 'desc', 'alpha', function (err, sorted) { - if (err) { - assert.fail(err, name); - } - assert.deepEqual(buffers_to_strings(sorted), ['d', 'c', 'b', 'a'], name); - }); - - // Now try sorting numbers in a list. - // x = [ 3, 9, 2, 4 ] - - client.sort('x', 'asc', function (err, sorted) { - if (err) { - assert.fail(err, name); - } - assert.deepEqual(buffers_to_strings(sorted), [2, 3, 4, 9], name); - }); - - client.sort('x', 'desc', function (err, sorted) { - if (err) { - assert.fail(err, name); - } - assert.deepEqual(buffers_to_strings(sorted), [9, 4, 3, 2], name); - }); - - // Try sorting with a 'by' pattern. - - client.sort('x', 'by', 'w*', 'asc', function (err, sorted) { - if (err) { - assert.fail(err, name); - } - assert.deepEqual(buffers_to_strings(sorted), [3, 9, 4, 2], name); - }); - - // Try sorting with a 'by' pattern and 1 'get' pattern. - - client.sort('x', 'by', 'w*', 'asc', 'get', 'o*', function (err, sorted) { - if (err) { - assert.fail(err, name); - } - assert.deepEqual(buffers_to_strings(sorted), ['foo', 'bar', 'baz', 'buz'], name); - }); - - // Try sorting with a 'by' pattern and 2 'get' patterns. - - client.sort('x', 'by', 'w*', 'asc', 'get', 'o*', 'get', 'p*', function (err, sorted) { - if (err) { - assert.fail(err, name); - } - assert.deepEqual(buffers_to_strings(sorted), ['foo', 'bux', 'bar', 'tux', 'baz', 'lux', 'buz', 'qux'], name); - }); - - // Try sorting with a 'by' pattern and 2 'get' patterns. - // Instead of getting back the sorted set/list, store the values to a list. - // Then check that the values are there in the expected order. - - client.sort('x', 'by', 'w*', 'asc', 'get', 'o*', 'get', 'p*', 'store', 'bacon', function (err) { - if (err) { - assert.fail(err, name); - } - }); - - client.lrange('bacon', 0, -1, function (err, values) { - if (err) { - assert.fail(err, name); - } - assert.deepEqual(buffers_to_strings(values), ['foo', 'bux', 'bar', 'tux', 'baz', 'lux', 'buz', 'qux'], name); - next(name); - }); - - // TODO - sort by hash value -}; - tests.MONITOR = function () { var name = "MONITOR", responses = [], monitor_client; @@ -2010,53 +318,6 @@ tests.OPTIONAL_CALLBACK_UNDEFINED = function () { client.set("op_cb_undefined", undefined, undefined); }; -tests.ENABLE_OFFLINE_QUEUE_TRUE = function () { - var name = "ENABLE_OFFLINE_QUEUE_TRUE"; - var cli = redis.createClient(9999, null, { - max_attempts: 1, - parser: parser - // default :) - // enable_offline_queue: true - }); - cli.on('error', function(e) { - // ignore, b/c expecting a "can't connect" error - }); - return setTimeout(function() { - cli.set(name, name, function(err, result) { - assert.ifError(err); - }); - - return setTimeout(function(){ - assert.strictEqual(cli.offline_queue.length, 1); - return next(name); - }, 25); - }, 50); -}; - -tests.ENABLE_OFFLINE_QUEUE_FALSE = function () { - var name = "ENABLE_OFFLINE_QUEUE_FALSE"; - var cli = redis.createClient(9999, null, { - parser: parser, - max_attempts: 1, - enable_offline_queue: false - }); - cli.on('error', function() { - // ignore, see above - }); - assert.throws(function () { - cli.set(name, name); - }); - assert.doesNotThrow(function () { - cli.set(name, name, function (err) { - // should callback with an error - assert.ok(err); - setTimeout(function () { - next(name); - }, 50); - }); - }); -}; - tests.SLOWLOG = function () { var name = "SLOWLOG"; client.config("set", "slowlog-log-slower-than", 0, require_string("OK", name)); @@ -2072,198 +333,3 @@ tests.SLOWLOG = function () { next(name); }); }; - -tests.DOMAIN = function () { - var name = "DOMAIN"; - - var domain; - try { - domain = require('domain').create(); - } catch (err) { - console.log("Skipping " + name + " because this version of node doesn't have domains."); - next(name); - } - - if (domain) { - domain.run(function () { - client.set('domain', 'value', function (err, res) { - assert.ok(process.domain); - var notFound = res.not.existing.thing; // ohhh nooooo - }); - }); - - // this is the expected and desired behavior - domain.on('error', function (err) { - domain.exit(); - next(name); - }); - } -}; - -// TODO - need a better way to test auth, maybe auto-config a local Redis server or something. -// Yes, this is the real password. Please be nice, thanks. -tests.auth = function () { - var name = "AUTH", client4, ready_count = 0; - - client4 = redis.createClient(9006, "filefish.redistogo.com", { parser: parser }); - client4.auth("664b1b6aaf134e1ec281945a8de702a9", function (err, res) { - assert.strictEqual(null, err, name); - assert.strictEqual("OK", res.toString(), name); - }); - - // test auth, then kill the connection so it'll auto-reconnect and auto-re-auth - client4.on("ready", function () { - ready_count++; - if (ready_count === 1) { - client4.stream.destroy(); - } else { - client4.quit(function (err, res) { - next(name); - }); - } - }); -}; - -tests.auth2 = function () { - var name = "AUTH2", client4, ready_count = 0; - - client4 = redis.createClient(9006, "filefish.redistogo.com", { auth_pass: "664b1b6aaf134e1ec281945a8de702a9", parser: parser }); - - // test auth, then kill the connection so it'll auto-reconnect and auto-re-auth - client4.on("ready", function () { - ready_count++; - if (ready_count === 1) { - client4.stream.destroy(); - } else { - client4.quit(function (err, res) { - next(name); - }); - } - }); -}; - -// auth password specified by URL string. -tests.auth3 = function () { - var name = "AUTH3", client4, ready_count = 0; - - client4 = redis.createClient('redis://redistogo:664b1b6aaf134e1ec281945a8de702a9@filefish.redistogo.com:9006/'); - - // test auth, then kill the connection so it'll auto-reconnect and auto-re-auth - client4.on("ready", function () { - ready_count++; - if (ready_count === 1) { - client4.stream.destroy(); - } else { - client4.quit(function (err, res) { - next(name); - }); - } - }); -}; - -tests.reconnectRetryMaxDelay = function() { - var time = new Date().getTime(), - name = 'reconnectRetryMaxDelay', - reconnecting = false; - var client = redis.createClient(PORT, HOST, { - retry_max_delay: 1, - parser: parser - }); - client.on('ready', function() { - if (!reconnecting) { - reconnecting = true; - client.retry_delay = 1000; - client.retry_backoff = 1; - client.stream.end(); - } else { - client.end(); - var lasted = new Date().getTime() - time; - assert.ok(lasted < 1000); - next(name); - } - }); -}; - -tests.unref = function () { - var name = "unref"; - var external = fork("./test/test-unref.js", [PORT, HOST]); - var done = false; - external.on("close", function (code) { - assert(code === 0, "test-unref.js failed"); - done = true; - }); - setTimeout(function () { - if (!done) { - external.kill(); - } - assert(done, "test-unref.js didn't finish in time."); - next(name); - }, 1500); -}; - -// starting to split tests into multiple files. -require('./queue-test')(tests, next); - -all_tests = Object.keys(tests); -all_start = new Date(); -test_count = 0; - -run_next_test = function run_next_test() { - var test_name = all_tests.shift(); - if (typeof tests[test_name] === "function") { - console.log('- \x1b[1m' + test_name.toLowerCase() + '\x1b[0m:'); - cur_start = new Date(); - test_count += 1; - tests[test_name](); - } else { - console.log('\n completed \x1b[32m%d\x1b[0m tests in \x1b[33m%d\x1b[0m ms\n', test_count, new Date() - all_start); - client.quit(); - client2.quit(); - bclient.quit(); - } -}; - -client.once("ready", function start_tests() { - console.log("Connected to " + client.address + ", Redis server version " + client.server_info.redis_version + "\n"); - console.log("Using reply parser " + client.reply_parser.name); - - run_next_test(); - - connected = true; -}); - -client.on('end', function () { - ended = true; -}); - -// Exit immediately on connection failure, which triggers "exit", below, which fails the test -client.on("error", function (err) { - console.error("client: " + err.stack); - process.exit(); -}); -client2.on("error", function (err) { - console.error("client2: " + err.stack); - process.exit(); -}); -client3.on("error", function (err) { - console.error("client3: " + err.stack); - process.exit(); -}); -bclient.on("error", function (err) { - console.error("bclient: " + err.stack); - process.exit(); -}); - -client.on("reconnecting", function (params) { - console.log("reconnecting: " + util.inspect(params)); -}); - -process.on('uncaughtException', function (err) { - console.error("Uncaught exception: " + err.stack); - process.exit(1); -}); - -process.on('exit', function (code) { - assert.equal(true, connected); - assert.equal(true, ended); -}); From ce7f21fc34cfedaa07825426a1cdfba2c57dcd78 Mon Sep 17 00:00:00 2001 From: Benjamin Coe Date: Fri, 14 Aug 2015 22:30:51 -0700 Subject: [PATCH 44/49] ported sdiff, sdiffstore, sinterstore, smembers, smove, spop, sunion, client.monitor --- test/node_redis.spec.js | 41 +++++++- test/test.js | 215 ---------------------------------------- 2 files changed, 38 insertions(+), 218 deletions(-) diff --git a/test/node_redis.spec.js b/test/node_redis.spec.js index cd3d86da55..6f10603467 100644 --- a/test/node_redis.spec.js +++ b/test/node_redis.spec.js @@ -5,7 +5,7 @@ var helper = require('./helper') var fork = require("child_process").fork; var redis = config.redis; -describe("a node_redis client", function () { +describe("The node_redis client", function () { function allTests(parser, ip) { var args = config.configureClient(parser, ip); @@ -178,8 +178,43 @@ describe("a node_redis client", function () { return done() }); } - }) - }) + }); + }); + + describe('monitor', function () { + it('monitors commands on all other redis clients', function (done) { + if (!helper.serverVersionAtLeast(client, [2, 6, 0])) return done(); + + var monitorClient = redis.createClient.apply(redis.createClient, args); + var responses = []; + + monitorClient.monitor(function (err, res) { + client.mget("some", "keys", "foo", "bar"); + client.set("json", JSON.stringify({ + foo: "123", + bar: "sdflkdfsjk", + another: false + })); + }); + + monitorClient.on("monitor", function (time, args) { + responses.push(args); + if (responses.length === 2) { + assert.strictEqual(5, responses[0].length); + assert.strictEqual("mget", responses[0][0]); + assert.strictEqual("some", responses[0][1]); + assert.strictEqual("keys", responses[0][2]); + assert.strictEqual("foo", responses[0][3]); + assert.strictEqual("bar", responses[0][4]); + assert.strictEqual(3, responses[1].length); + assert.strictEqual("set", responses[1][0]); + assert.strictEqual("json", responses[1][1]); + assert.strictEqual('{"foo":"123","bar":"sdflkdfsjk","another":false}', responses[1][2]); + monitorClient.quit(done); + } + }); + }); + }); }); diff --git a/test/test.js b/test/test.js index 53a2b663bb..bd165438c0 100644 --- a/test/test.js +++ b/test/test.js @@ -1,180 +1,5 @@ return; -tests.SPOP = function () { - var name = "SPOP"; - - client.del('zzz'); - client.sadd('zzz', 'member0', require_number(1, name)); - client.scard('zzz', require_number(1, name)); - - client.spop('zzz', function (err, value) { - if (err) { - assert.fail(err); - } - assert.equal(value, 'member0', name); - }); - - client.scard('zzz', last(name, require_number(0, name))); -}; - -tests.SDIFF = function () { - var name = "SDIFF"; - - client.del('foo'); - client.sadd('foo', 'x', require_number(1, name)); - client.sadd('foo', 'a', require_number(1, name)); - client.sadd('foo', 'b', require_number(1, name)); - client.sadd('foo', 'c', require_number(1, name)); - - client.sadd('bar', 'c', require_number(1, name)); - - client.sadd('baz', 'a', require_number(1, name)); - client.sadd('baz', 'd', require_number(1, name)); - - client.sdiff('foo', 'bar', 'baz', function (err, values) { - if (err) { - assert.fail(err, name); - } - values.sort(); - assert.equal(values.length, 2, name); - assert.equal(values[0], 'b', name); - assert.equal(values[1], 'x', name); - next(name); - }); -}; - -tests.SDIFFSTORE = function () { - var name = "SDIFFSTORE"; - - client.del('foo'); - client.del('bar'); - client.del('baz'); - client.del('quux'); - - client.sadd('foo', 'x', require_number(1, name)); - client.sadd('foo', 'a', require_number(1, name)); - client.sadd('foo', 'b', require_number(1, name)); - client.sadd('foo', 'c', require_number(1, name)); - - client.sadd('bar', 'c', require_number(1, name)); - - client.sadd('baz', 'a', require_number(1, name)); - client.sadd('baz', 'd', require_number(1, name)); - - // NB: SDIFFSTORE returns the number of elements in the dstkey - - client.sdiffstore('quux', 'foo', 'bar', 'baz', require_number(2, name)); - - client.smembers('quux', function (err, values) { - if (err) { - assert.fail(err, name); - } - var members = buffers_to_strings(values).sort(); - - assert.deepEqual(members, [ 'b', 'x' ], name); - next(name); - }); -}; - -tests.SMEMBERS = function () { - var name = "SMEMBERS"; - - client.del('foo'); - client.sadd('foo', 'x', require_number(1, name)); - - client.smembers('foo', function (err, members) { - if (err) { - assert.fail(err, name); - } - assert.deepEqual(buffers_to_strings(members), [ 'x' ], name); - }); - - client.sadd('foo', 'y', require_number(1, name)); - - client.smembers('foo', function (err, values) { - if (err) { - assert.fail(err, name); - } - assert.equal(values.length, 2, name); - var members = buffers_to_strings(values).sort(); - - assert.deepEqual(members, [ 'x', 'y' ], name); - next(name); - }); -}; - -tests.SMOVE = function () { - var name = "SMOVE"; - - client.del('foo'); - client.del('bar'); - - client.sadd('foo', 'x', require_number(1, name)); - client.smove('foo', 'bar', 'x', require_number(1, name)); - client.sismember('foo', 'x', require_number(0, name)); - client.sismember('bar', 'x', require_number(1, name)); - client.smove('foo', 'bar', 'x', last(name, require_number(0, name))); -}; - -tests.SINTERSTORE = function () { - var name = "SINTERSTORE"; - - client.del('sa'); - client.del('sb'); - client.del('sc'); - client.del('foo'); - - client.sadd('sa', 'a', require_number(1, name)); - client.sadd('sa', 'b', require_number(1, name)); - client.sadd('sa', 'c', require_number(1, name)); - - client.sadd('sb', 'b', require_number(1, name)); - client.sadd('sb', 'c', require_number(1, name)); - client.sadd('sb', 'd', require_number(1, name)); - - client.sadd('sc', 'c', require_number(1, name)); - client.sadd('sc', 'd', require_number(1, name)); - client.sadd('sc', 'e', require_number(1, name)); - - client.sinterstore('foo', 'sa', 'sb', 'sc', require_number(1, name)); - - client.smembers('foo', function (err, members) { - if (err) { - assert.fail(err, name); - } - assert.deepEqual(buffers_to_strings(members), [ 'c' ], name); - next(name); - }); -}; - -tests.SUNION = function () { - var name = "SUNION"; - - client.del('sa'); - client.del('sb'); - client.del('sc'); - - client.sadd('sa', 'a', require_number(1, name)); - client.sadd('sa', 'b', require_number(1, name)); - client.sadd('sa', 'c', require_number(1, name)); - - client.sadd('sb', 'b', require_number(1, name)); - client.sadd('sb', 'c', require_number(1, name)); - client.sadd('sb', 'd', require_number(1, name)); - - client.sadd('sc', 'c', require_number(1, name)); - client.sadd('sc', 'd', require_number(1, name)); - client.sadd('sc', 'e', require_number(1, name)); - - client.sunion('sa', 'sb', 'sc', function (err, union) { - if (err) { - assert.fail(err, name); - } - assert.deepEqual(buffers_to_strings(union).sort(), ['a', 'b', 'c', 'd', 'e'], name); - next(name); - }); -}; - tests.SUNIONSTORE = function () { var name = "SUNIONSTORE"; @@ -212,46 +37,6 @@ tests.SUNIONSTORE = function () { }); }; -tests.MONITOR = function () { - var name = "MONITOR", responses = [], monitor_client; - - if (!server_version_at_least(client, [2, 6, 0])) { - console.log("Skipping " + name + " for old Redis server version < 2.6.x"); - return next(name); - } - - monitor_client = redis.createClient(PORT, HOST, { parser: parser }); - monitor_client.monitor(function (err, res) { - client.mget("some", "keys", "foo", "bar"); - client.set("json", JSON.stringify({ - foo: "123", - bar: "sdflkdfsjk", - another: false - })); - }); - monitor_client.on("monitor", function (time, args) { - // skip monitor command for Redis <= 2.4.16 - if (args[0] === "monitor") return; - - responses.push(args); - if (responses.length === 2) { - assert.strictEqual(5, responses[0].length); - assert.strictEqual("mget", responses[0][0]); - assert.strictEqual("some", responses[0][1]); - assert.strictEqual("keys", responses[0][2]); - assert.strictEqual("foo", responses[0][3]); - assert.strictEqual("bar", responses[0][4]); - assert.strictEqual(3, responses[1].length); - assert.strictEqual("set", responses[1][0]); - assert.strictEqual("json", responses[1][1]); - assert.strictEqual('{"foo":"123","bar":"sdflkdfsjk","another":false}', responses[1][2]); - monitor_client.quit(function (err, res) { - next(name); - }); - } - }); -}; - tests.BLPOP = function () { var name = "BLPOP"; From 2fd3b46835d4ae4e679a8a309f360127578177c0 Mon Sep 17 00:00:00 2001 From: Benjamin Coe Date: Fri, 14 Aug 2015 22:30:58 -0700 Subject: [PATCH 45/49] ported sdiff, sdiffstore, sinterstore, smembers, smove, spop, sunion, client.monitor --- test/commands/sdiff.spec.js | 54 ++++++++++++++++++++++++++++++ test/commands/sdiffstore.spec.js | 54 ++++++++++++++++++++++++++++++ test/commands/sinterstore.spec.js | 55 +++++++++++++++++++++++++++++++ test/commands/smembers.spec.js | 45 +++++++++++++++++++++++++ test/commands/smove.spec.js | 48 +++++++++++++++++++++++++++ test/commands/spop.spec.js | 45 +++++++++++++++++++++++++ test/commands/sunion.spec.js | 53 +++++++++++++++++++++++++++++ 7 files changed, 354 insertions(+) create mode 100644 test/commands/sdiff.spec.js create mode 100644 test/commands/sdiffstore.spec.js create mode 100644 test/commands/sinterstore.spec.js create mode 100644 test/commands/smembers.spec.js create mode 100644 test/commands/smove.spec.js create mode 100644 test/commands/spop.spec.js create mode 100644 test/commands/sunion.spec.js diff --git a/test/commands/sdiff.spec.js b/test/commands/sdiff.spec.js new file mode 100644 index 0000000000..deb0628326 --- /dev/null +++ b/test/commands/sdiff.spec.js @@ -0,0 +1,54 @@ +var assert = require("assert"); +var config = require("../lib/config"); +var helper = require("../helper"); +var redis = config.redis; + +describe("The 'sdiff' method", function () { + + function allTests(parser, ip) { + var args = config.configureClient(parser, ip); + + describe("using " + parser + " and " + ip, function () { + var client; + + beforeEach(function (done) { + client = redis.createClient.apply(redis.createClient, args); + client.once("error", done); + client.once("connect", function () { + client.flushdb(done); + }); + }); + + it('returns set difference', function (done) { + client.sadd('foo', 'x', helper.isNumber(1)); + client.sadd('foo', 'a', helper.isNumber(1)); + client.sadd('foo', 'b', helper.isNumber(1)); + client.sadd('foo', 'c', helper.isNumber(1)); + + client.sadd('bar', 'c', helper.isNumber(1)); + + client.sadd('baz', 'a', helper.isNumber(1)); + client.sadd('baz', 'd', helper.isNumber(1)); + + client.sdiff('foo', 'bar', 'baz', function (err, values) { + values.sort(); + assert.equal(values.length, 2); + assert.equal(values[0], 'b'); + assert.equal(values[1], 'x'); + return done(err); + }); + }); + + afterEach(function () { + client.end(); + }); + }); + } + + ['javascript', 'hiredis'].forEach(function (parser) { + allTests(parser, "/tmp/redis.sock"); + ['IPv4', 'IPv6'].forEach(function (ip) { + allTests(parser, ip); + }) + }); +}); diff --git a/test/commands/sdiffstore.spec.js b/test/commands/sdiffstore.spec.js new file mode 100644 index 0000000000..2aac531b4f --- /dev/null +++ b/test/commands/sdiffstore.spec.js @@ -0,0 +1,54 @@ +var assert = require("assert"); +var config = require("../lib/config"); +var helper = require("../helper"); +var redis = config.redis; + +describe("The 'sdiffstore' method", function () { + + function allTests(parser, ip) { + var args = config.configureClient(parser, ip); + + describe("using " + parser + " and " + ip, function () { + var client; + + beforeEach(function (done) { + client = redis.createClient.apply(redis.createClient, args); + client.once("error", done); + client.once("connect", function () { + client.flushdb(done); + }); + }); + + it('calculates set difference ands stores it in a key', function (done) { + client.sadd('foo', 'x', helper.isNumber(1)); + client.sadd('foo', 'a', helper.isNumber(1)); + client.sadd('foo', 'b', helper.isNumber(1)); + client.sadd('foo', 'c', helper.isNumber(1)); + + client.sadd('bar', 'c', helper.isNumber(1)); + + client.sadd('baz', 'a', helper.isNumber(1)); + client.sadd('baz', 'd', helper.isNumber(1)); + + client.sdiffstore('quux', 'foo', 'bar', 'baz', helper.isNumber(2)); + + client.smembers('quux', function (err, values) { + var members = values.sort(); + assert.deepEqual(members, [ 'b', 'x' ]); + return done(err); + }); + }); + + afterEach(function () { + client.end(); + }); + }); + } + + ['javascript', 'hiredis'].forEach(function (parser) { + allTests(parser, "/tmp/redis.sock"); + ['IPv4', 'IPv6'].forEach(function (ip) { + allTests(parser, ip); + }) + }); +}); diff --git a/test/commands/sinterstore.spec.js b/test/commands/sinterstore.spec.js new file mode 100644 index 0000000000..f27d47eea6 --- /dev/null +++ b/test/commands/sinterstore.spec.js @@ -0,0 +1,55 @@ +var assert = require("assert"); +var config = require("../lib/config"); +var helper = require("../helper"); +var redis = config.redis; + +describe("The 'sinterstore' method", function () { + + function allTests(parser, ip) { + var args = config.configureClient(parser, ip); + + describe("using " + parser + " and " + ip, function () { + var client; + + beforeEach(function (done) { + client = redis.createClient.apply(redis.createClient, args); + client.once("error", done); + client.once("connect", function () { + client.flushdb(done); + }); + }); + + it('calculates set intersection and stores it in a key', function (done) { + client.sadd('sa', 'a', helper.isNumber(1)); + client.sadd('sa', 'b', helper.isNumber(1)); + client.sadd('sa', 'c', helper.isNumber(1)); + + client.sadd('sb', 'b', helper.isNumber(1)); + client.sadd('sb', 'c', helper.isNumber(1)); + client.sadd('sb', 'd', helper.isNumber(1)); + + client.sadd('sc', 'c', helper.isNumber(1)); + client.sadd('sc', 'd', helper.isNumber(1)); + client.sadd('sc', 'e', helper.isNumber(1)); + + client.sinterstore('foo', 'sa', 'sb', 'sc', helper.isNumber(1)); + + client.smembers('foo', function (err, members) { + assert.deepEqual(members, [ 'c' ]); + return done(err); + }); + }); + + afterEach(function () { + client.end(); + }); + }); + } + + ['javascript', 'hiredis'].forEach(function (parser) { + allTests(parser, "/tmp/redis.sock"); + ['IPv4', 'IPv6'].forEach(function (ip) { + allTests(parser, ip); + }) + }); +}); diff --git a/test/commands/smembers.spec.js b/test/commands/smembers.spec.js new file mode 100644 index 0000000000..0832742ec0 --- /dev/null +++ b/test/commands/smembers.spec.js @@ -0,0 +1,45 @@ +var assert = require("assert"); +var config = require("../lib/config"); +var helper = require("../helper"); +var redis = config.redis; + +describe("The 'smembers' method", function () { + + function allTests(parser, ip) { + var args = config.configureClient(parser, ip); + + describe("using " + parser + " and " + ip, function () { + var client; + + beforeEach(function (done) { + client = redis.createClient.apply(redis.createClient, args); + client.once("error", done); + client.once("connect", function () { + client.flushdb(done); + }); + }); + + it('returns all values in a set', function (done) { + client.sadd('foo', 'x', helper.isNumber(1)); + client.sadd('foo', 'y', helper.isNumber(1)); + client.smembers('foo', function (err, values) { + assert.equal(values.length, 2); + var members = values.sort(); + assert.deepEqual(members, [ 'x', 'y' ]); + return done(err); + }); + }); + + afterEach(function () { + client.end(); + }); + }); + } + + ['javascript', 'hiredis'].forEach(function (parser) { + allTests(parser, "/tmp/redis.sock"); + ['IPv4', 'IPv6'].forEach(function (ip) { + allTests(parser, ip); + }) + }); +}); diff --git a/test/commands/smove.spec.js b/test/commands/smove.spec.js new file mode 100644 index 0000000000..598be28860 --- /dev/null +++ b/test/commands/smove.spec.js @@ -0,0 +1,48 @@ +var assert = require("assert"); +var config = require("../lib/config"); +var helper = require("../helper"); +var redis = config.redis; + +describe("The 'smove' method", function () { + + function allTests(parser, ip) { + var args = config.configureClient(parser, ip); + + describe("using " + parser + " and " + ip, function () { + var client; + + beforeEach(function (done) { + client = redis.createClient.apply(redis.createClient, args); + client.once("error", done); + client.once("connect", function () { + client.flushdb(done); + }); + }); + + it('moves a value to a set that does not yet exist', function (done) { + client.sadd('foo', 'x', helper.isNumber(1)); + client.smove('foo', 'bar', 'x', helper.isNumber(1)); + client.sismember('foo', 'x', helper.isNumber(0)); + client.sismember('bar', 'x', helper.isNumber(1, done)); + }); + + it("does not move a value if it does not exist in the first set", function (done) { + client.sadd('foo', 'x', helper.isNumber(1)); + client.smove('foo', 'bar', 'y', helper.isNumber(0)); + client.sismember('foo', 'y', helper.isNumber(0)); + client.sismember('bar', 'y', helper.isNumber(0, done)); + }); + + afterEach(function () { + client.end(); + }); + }); + } + + ['javascript', 'hiredis'].forEach(function (parser) { + allTests(parser, "/tmp/redis.sock"); + ['IPv4', 'IPv6'].forEach(function (ip) { + allTests(parser, ip); + }) + }); +}); diff --git a/test/commands/spop.spec.js b/test/commands/spop.spec.js new file mode 100644 index 0000000000..43b31442e2 --- /dev/null +++ b/test/commands/spop.spec.js @@ -0,0 +1,45 @@ +var assert = require("assert"); +var config = require("../lib/config"); +var helper = require("../helper"); +var redis = config.redis; + +describe("The 'spop' method", function () { + + function allTests(parser, ip) { + var args = config.configureClient(parser, ip); + + describe("using " + parser + " and " + ip, function () { + var client; + + beforeEach(function (done) { + client = redis.createClient.apply(redis.createClient, args); + client.once("error", done); + client.once("connect", function () { + client.flushdb(done); + }); + }); + + it('returns a random element from the set', function (done) { + client.sadd('zzz', 'member0', helper.isNumber(1)); + client.scard('zzz', helper.isNumber(1)); + + client.spop('zzz', function (err, value) { + if (err) return done(err); + assert.equal(value, 'member0'); + client.scard('zzz', helper.isNumber(0, done)); + }); + }); + + afterEach(function () { + client.end(); + }); + }); + } + + ['javascript', 'hiredis'].forEach(function (parser) { + allTests(parser, "/tmp/redis.sock"); + ['IPv4', 'IPv6'].forEach(function (ip) { + allTests(parser, ip); + }) + }); +}); diff --git a/test/commands/sunion.spec.js b/test/commands/sunion.spec.js new file mode 100644 index 0000000000..92cd81d08f --- /dev/null +++ b/test/commands/sunion.spec.js @@ -0,0 +1,53 @@ +var assert = require("assert"); +var config = require("../lib/config"); +var helper = require("../helper"); +var redis = config.redis; + +describe("The 'sunion' method", function () { + + function allTests(parser, ip) { + var args = config.configureClient(parser, ip); + + describe("using " + parser + " and " + ip, function () { + var client; + + beforeEach(function (done) { + client = redis.createClient.apply(redis.createClient, args); + client.once("error", done); + client.once("connect", function () { + client.flushdb(done); + }); + }); + + it('returns the union of a group of sets', function (done) { + client.sadd('sa', 'a', helper.isNumber(1)); + client.sadd('sa', 'b', helper.isNumber(1)); + client.sadd('sa', 'c', helper.isNumber(1)); + + client.sadd('sb', 'b', helper.isNumber(1)); + client.sadd('sb', 'c', helper.isNumber(1)); + client.sadd('sb', 'd', helper.isNumber(1)); + + client.sadd('sc', 'c', helper.isNumber(1)); + client.sadd('sc', 'd', helper.isNumber(1)); + client.sadd('sc', 'e', helper.isNumber(1)); + + client.sunion('sa', 'sb', 'sc', function (err, union) { + assert.deepEqual(union.sort(), ['a', 'b', 'c', 'd', 'e']); + return done(err); + }); + }); + + afterEach(function () { + client.end(); + }); + }); + } + + ['javascript', 'hiredis'].forEach(function (parser) { + allTests(parser, "/tmp/redis.sock"); + ['IPv4', 'IPv6'].forEach(function (ip) { + allTests(parser, ip); + }) + }); +}); From 65db5dbefe3c86040439a2dc2c95fc57c327399b Mon Sep 17 00:00:00 2001 From: Erin Spiceland Date: Sat, 15 Aug 2015 11:51:37 -0500 Subject: [PATCH 46/49] Clarify this one test is only applicable for IPv4. --- test/auth.spec.js | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) diff --git a/test/auth.spec.js b/test/auth.spec.js index a149f4b9cf..1854a84744 100644 --- a/test/auth.spec.js +++ b/test/auth.spec.js @@ -41,13 +41,14 @@ describe("client authentication", function () { client.auth(auth + 'bad'); }); - if (ip === 'IPv4') - it('allows auth to be provided as config option for client', function (done) { - client = redis.createClient('redis://foo:' + auth + '@' + config.HOST[ip] + ':' + config.PORT); - client.on("ready", function () { - return done(); + if (ip === 'IPv4') { + it('allows auth to be provided as config option for client', function (done) { + client = redis.createClient('redis://foo:' + auth + '@' + config.HOST[ip] + ':' + config.PORT); + client.on("ready", function () { + return done(); + }); }); - }); + } it('allows auth to be provided as part of redis url', function (done) { var args = config.configureClient(parser, ip, { From 51b1ba2bef7814fc4afaf76e90950340709295df Mon Sep 17 00:00:00 2001 From: Benjamin Coe Date: Sat, 15 Aug 2015 13:00:05 -0700 Subject: [PATCH 47/49] finished porting blpop, expire, mset, slowlog, sunionstore, ttl --- test/commands/blpop.spec.js | 64 ++++++++++++++++ test/commands/expire.spec.js | 42 +++++++++++ test/commands/mset.spec.js | 37 ++------- test/commands/slowlog.spec.js | 48 ++++++++++++ test/commands/sunionstore.spec.js | 56 ++++++++++++++ test/commands/ttl.spec.js | 45 +++++++++++ test/test.js | 120 ------------------------------ 7 files changed, 260 insertions(+), 152 deletions(-) create mode 100644 test/commands/blpop.spec.js create mode 100644 test/commands/expire.spec.js create mode 100644 test/commands/slowlog.spec.js create mode 100644 test/commands/sunionstore.spec.js create mode 100644 test/commands/ttl.spec.js delete mode 100644 test/test.js diff --git a/test/commands/blpop.spec.js b/test/commands/blpop.spec.js new file mode 100644 index 0000000000..1e344e023b --- /dev/null +++ b/test/commands/blpop.spec.js @@ -0,0 +1,64 @@ +var assert = require("assert"); +var config = require("../lib/config"); +var helper = require("../helper"); +var redis = config.redis; + +describe("The 'blpop' method", function () { + + function allTests(parser, ip) { + var args = config.configureClient(parser, ip); + + describe("using " + parser + " and " + ip, function () { + var client; + var bclient; + + beforeEach(function (done) { + client = redis.createClient.apply(redis.createClient, args); + client.once("error", done); + client.once("connect", function () { + client.flushdb(done); + }); + }); + + it('pops value immediately if list contains values', function (done) { + bclient = redis.createClient.apply(redis.createClient, args); + client.rpush("blocking list", "initial value", helper.isNumber(1)); + bclient.blpop("blocking list", 0, function (err, value) { + assert.strictEqual(value[0], "blocking list"); + assert.strictEqual(value[1], "initial value"); + return done(err); + }); + }); + + it('waits for value if list is not yet populated', function (done) { + bclient = redis.createClient.apply(redis.createClient, args); + 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); + }); + client.rpush("blocking list 2", "initial value", helper.isNumber(1)); + }); + + it('times out after specified time', function (done) { + bclient = redis.createClient.apply(redis.createClient, args); + bclient.BLPOP("blocking list", 1, function (err, res) { + assert.strictEqual(res, null); + return done(err); + }); + }); + + afterEach(function () { + client.end(); + bclient.end(); + }); + }); + } + + ['javascript', 'hiredis'].forEach(function (parser) { + allTests(parser, "/tmp/redis.sock"); + ['IPv4', 'IPv6'].forEach(function (ip) { + allTests(parser, ip); + }) + }); +}); diff --git a/test/commands/expire.spec.js b/test/commands/expire.spec.js new file mode 100644 index 0000000000..eab08468e9 --- /dev/null +++ b/test/commands/expire.spec.js @@ -0,0 +1,42 @@ +var assert = require("assert"); +var config = require("../lib/config"); +var helper = require("../helper"); +var redis = config.redis; + +describe("The 'expire' method", function () { + + function allTests(parser, ip) { + var args = config.configureClient(parser, ip); + + describe("using " + parser + " and " + ip, function () { + var client; + + beforeEach(function (done) { + client = redis.createClient.apply(redis.createClient, args); + client.once("error", done); + client.once("connect", function () { + client.flushdb(done); + }); + }); + + it('expires key after timeout', function (done) { + client.set(['expiry key', 'bar'], helper.isString("OK")); + client.EXPIRE(["expiry key", "1"], helper.isNumber(1)); + setTimeout(function () { + client.exists(["expiry key"], helper.isNumber(0, done)); + }, 1500); + }); + + afterEach(function () { + client.end(); + }); + }); + } + + ['javascript', 'hiredis'].forEach(function (parser) { + allTests(parser, "/tmp/redis.sock"); + ['IPv4', 'IPv6'].forEach(function (ip) { + allTests(parser, ip); + }) + }); +}); diff --git a/test/commands/mset.spec.js b/test/commands/mset.spec.js index 1523797a3c..d15c70c1a4 100644 --- a/test/commands/mset.spec.js +++ b/test/commands/mset.spec.js @@ -66,22 +66,9 @@ describe("The 'mset' method", function () { describe("and a callback is specified", function () { describe("with valid parameters", function () { it("sets the value correctly", function (done) { - client.mset(key, value, key2, value2, function (err, res) { - helper.isNotError()(err, res); - async.parallel([function (next) { - client.get(key, function (err, res) { - helper.isString(value)(err, res); - next(); - }); - }, function (next) { - client.get(key2, function (err, res) { - helper.isString(value2)(err, res); - next(); - }); - }], function (err) { - done(err); - }); - }); + client.mset(key, value, key2, value2); + client.get(key, helper.isString(value)); + client.get(key2, helper.isString(value2, done)); }); }); @@ -108,22 +95,8 @@ describe("The 'mset' method", function () { describe("with valid parameters", function () { it("sets the value correctly", function (done) { client.mset(key, value, key2, value2); - - setTimeout(function () { - async.parallel([function (next) { - client.get(key, function (err, res) { - helper.isString(value)(err, res); - next(); - }); - }, function (next) { - client.get(key2, function (err, res) { - helper.isString(value2)(err, res); - next(); - }); - }], function (err) { - done(err); - }); - }, 100); + client.get(key, helper.isString(value)); + client.get(key2, helper.isString(value2, done)); }); }); diff --git a/test/commands/slowlog.spec.js b/test/commands/slowlog.spec.js new file mode 100644 index 0000000000..d09442d529 --- /dev/null +++ b/test/commands/slowlog.spec.js @@ -0,0 +1,48 @@ +var assert = require("assert"); +var config = require("../lib/config"); +var helper = require("../helper"); +var redis = config.redis; + +describe("The 'slowlog' method", function () { + + function allTests(parser, ip) { + var args = config.configureClient(parser, ip); + + describe("using " + parser + " and " + ip, function () { + var client; + + beforeEach(function (done) { + client = redis.createClient.apply(redis.createClient, args); + client.once("error", done); + client.once("connect", function () { + client.flushdb(done); + }); + }); + + it('logs operations in slowlog', function (done) { + client.config("set", "slowlog-log-slower-than", 0, helper.isString("OK")); + client.slowlog("reset", helper.isString("OK")); + client.set("foo", "bar", helper.isString("OK")); + client.get("foo", helper.isString("bar")); + client.slowlog("get", function (err, res) { + assert.equal(res.length, 3); + assert.equal(res[0][3].length, 2); + assert.deepEqual(res[1][3], ["set", "foo", "bar"]); + assert.deepEqual(res[2][3], ["slowlog", "reset"]); + return done(err); + }); + }); + + afterEach(function () { + client.end(); + }); + }); + } + + ['javascript', 'hiredis'].forEach(function (parser) { + allTests(parser, "/tmp/redis.sock"); + ['IPv4', 'IPv6'].forEach(function (ip) { + allTests(parser, ip); + }) + }); +}); diff --git a/test/commands/sunionstore.spec.js b/test/commands/sunionstore.spec.js new file mode 100644 index 0000000000..702ad131c1 --- /dev/null +++ b/test/commands/sunionstore.spec.js @@ -0,0 +1,56 @@ +var assert = require("assert"); +var config = require("../lib/config"); +var helper = require("../helper"); +var redis = config.redis; + +describe("The 'sunionstore' method", function () { + + function allTests(parser, ip) { + var args = config.configureClient(parser, ip); + + describe("using " + parser + " and " + ip, function () { + var client; + + beforeEach(function (done) { + client = redis.createClient.apply(redis.createClient, args); + client.once("error", done); + client.once("connect", function () { + client.flushdb(done); + }); + }); + + it('stores the result of a union', function (done) { + client.sadd('sa', 'a', helper.isNumber(1)); + client.sadd('sa', 'b', helper.isNumber(1)); + client.sadd('sa', 'c', helper.isNumber(1)); + + client.sadd('sb', 'b', helper.isNumber(1)); + client.sadd('sb', 'c', helper.isNumber(1)); + client.sadd('sb', 'd', helper.isNumber(1)); + + client.sadd('sc', 'c', helper.isNumber(1)); + client.sadd('sc', 'd', helper.isNumber(1)); + client.sadd('sc', 'e', helper.isNumber(1)); + + client.sunionstore('foo', 'sa', 'sb', 'sc', helper.isNumber(5)); + + client.smembers('foo', function (err, members) { + assert.equal(members.length, 5); + assert.deepEqual(members.sort(), ['a', 'b', 'c', 'd', 'e']); + return done(err); + }); + }); + + afterEach(function () { + client.end(); + }); + }); + } + + ['javascript', 'hiredis'].forEach(function (parser) { + allTests(parser, "/tmp/redis.sock"); + ['IPv4', 'IPv6'].forEach(function (ip) { + allTests(parser, ip); + }) + }); +}); diff --git a/test/commands/ttl.spec.js b/test/commands/ttl.spec.js new file mode 100644 index 0000000000..cc93d231e1 --- /dev/null +++ b/test/commands/ttl.spec.js @@ -0,0 +1,45 @@ +var assert = require("assert"); +var config = require("../lib/config"); +var helper = require("../helper"); +var redis = config.redis; + +describe("The 'ttl' method", function () { + + function allTests(parser, ip) { + var args = config.configureClient(parser, ip); + + describe("using " + parser + " and " + ip, function () { + var client; + + beforeEach(function (done) { + client = redis.createClient.apply(redis.createClient, args); + client.once("error", done); + client.once("connect", function () { + client.flushdb(done); + }); + }); + + it('returns the current ttl on a key', function (done) { + client.set(["ttl key", "ttl val"], helper.isString("OK")); + client.expire(["ttl key", "100"], helper.isNumber(1)); + setTimeout(function () { + client.TTL(["ttl key"], function (err, ttl) { + assert.ok(ttl > 50 && ttl <= 100); + return done(err); + }); + }, 500); + }); + + afterEach(function () { + client.end(); + }); + }); + } + + ['javascript', 'hiredis'].forEach(function (parser) { + allTests(parser, "/tmp/redis.sock"); + ['IPv4', 'IPv6'].forEach(function (ip) { + allTests(parser, ip); + }) + }); +}); diff --git a/test/test.js b/test/test.js deleted file mode 100644 index bd165438c0..0000000000 --- a/test/test.js +++ /dev/null @@ -1,120 +0,0 @@ -return; - -tests.SUNIONSTORE = function () { - var name = "SUNIONSTORE"; - - client.del('sa'); - client.del('sb'); - client.del('sc'); - client.del('foo'); - - client.sadd('sa', 'a', require_number(1, name)); - client.sadd('sa', 'b', require_number(1, name)); - client.sadd('sa', 'c', require_number(1, name)); - - client.sadd('sb', 'b', require_number(1, name)); - client.sadd('sb', 'c', require_number(1, name)); - client.sadd('sb', 'd', require_number(1, name)); - - client.sadd('sc', 'c', require_number(1, name)); - client.sadd('sc', 'd', require_number(1, name)); - client.sadd('sc', 'e', require_number(1, name)); - - client.sunionstore('foo', 'sa', 'sb', 'sc', function (err, cardinality) { - if (err) { - assert.fail(err, name); - } - assert.equal(cardinality, 5, name); - }); - - client.smembers('foo', function (err, members) { - if (err) { - assert.fail(err, name); - } - assert.equal(members.length, 5, name); - assert.deepEqual(buffers_to_strings(members).sort(), ['a', 'b', 'c', 'd', 'e'], name); - next(name); - }); -}; - -tests.BLPOP = function () { - var name = "BLPOP"; - - client.rpush("blocking list", "initial value", function (err, res) { - client2.BLPOP("blocking list", 0, function (err, res) { - assert.strictEqual("blocking list", res[0].toString()); - assert.strictEqual("initial value", res[1].toString()); - - client.rpush("blocking list", "wait for this value"); - }); - client2.BLPOP("blocking list", 0, function (err, res) { - assert.strictEqual("blocking list", res[0].toString()); - assert.strictEqual("wait for this value", res[1].toString()); - next(name); - }); - }); -}; - -tests.BLPOP_TIMEOUT = function () { - var name = "BLPOP_TIMEOUT"; - - // try to BLPOP the list again, which should be empty. This should timeout and return null. - client2.BLPOP("blocking list", 1, function (err, res) { - if (err) { - throw err; - } - - assert.strictEqual(res, null); - next(name); - }); -}; - -tests.EXPIRE = function () { - var name = "EXPIRE"; - client.set(['expiry key', 'bar'], require_string("OK", name)); - client.EXPIRE(["expiry key", "1"], require_number_pos(name)); - setTimeout(function () { - client.exists(["expiry key"], last(name, require_number(0, name))); - }, 2000); -}; - -tests.TTL = function () { - var name = "TTL"; - client.set(["ttl key", "ttl val"], require_string("OK", name)); - client.expire(["ttl key", "100"], require_number_pos(name)); - setTimeout(function () { - client.TTL(["ttl key"], last(name, require_number_pos(0, name))); - }, 500); -}; - -tests.OPTIONAL_CALLBACK = function () { - var name = "OPTIONAL_CALLBACK"; - client.del("op_cb1"); - client.set("op_cb1", "x"); - client.get("op_cb1", last(name, require_string("x", name))); -}; - -tests.OPTIONAL_CALLBACK_UNDEFINED = function () { - var name = "OPTIONAL_CALLBACK_UNDEFINED"; - client.del("op_cb2"); - client.set("op_cb2", "y", undefined); - client.get("op_cb2", last(name, require_string("y", name))); - - client.set("op_cb_undefined", undefined, undefined); -}; - -tests.SLOWLOG = function () { - var name = "SLOWLOG"; - client.config("set", "slowlog-log-slower-than", 0, require_string("OK", name)); - client.slowlog("reset", require_string("OK", name)); - client.set("foo", "bar", require_string("OK", name)); - client.get("foo", require_string("bar", name)); - client.slowlog("get", function (err, res) { - assert.equal(res.length, 3, name); - assert.equal(res[0][3].length, 2, name); - assert.deepEqual(res[1][3], ["set", "foo", "bar"], name); - assert.deepEqual(res[2][3], ["slowlog", "reset"], name); - client.config("set", "slowlog-log-slower-than", 10000, require_string("OK", name)); - next(name); - }); -}; From 5d83e64d008f3defe30c75e0b1a2a2d648d1746f Mon Sep 17 00:00:00 2001 From: Benjamin Coe Date: Sat, 15 Aug 2015 13:25:03 -0700 Subject: [PATCH 48/49] fixed up our one commented out test, based on @erinspice's code review --- test/node_redis.spec.js | 25 ++++++++++--------------- 1 file changed, 10 insertions(+), 15 deletions(-) diff --git a/test/node_redis.spec.js b/test/node_redis.spec.js index 6f10603467..41e3a47dcf 100644 --- a/test/node_redis.spec.js +++ b/test/node_redis.spec.js @@ -84,27 +84,22 @@ describe("The node_redis client", function () { }); }); + // TODO: we should only have a single subscription in this this + // test but unsubscribing from the single channel indicates + // that one subscriber still exists, let's dig into this. describe("and it's subscribed to a channel", function () { // reconnect_select_db_after_pubsub // Does not pass. // "Connection in subscriber mode, only subscriber commands may be used" - xit("reconnects, unsubscribes, and can retrieve the pre-existing data", function (done) { + it("reconnects, unsubscribes, and can retrieve the pre-existing data", function (done) { client.on("reconnecting", function on_recon(params) { client.on("ready", function on_connect() { - async.parallel([function (cb) { - client.unsubscribe("recon channel", function (err, res) { - helper.isNotError()(err, res); - cb(); - }); - }, function (cb) { - client.get("recon 1", function (err, res) { - helper.isString("one")(err, res); - cb(); - }); - }], function (err, results) { - client.removeListener("connect", on_connect); - client.removeListener("reconnecting", on_recon); - done(err); + client.unsubscribe(helper.isNotError()); + + client.on('unsubscribe', function (channel, count) { + // we should now be out of subscriber mode. + client.set('foo', 'bar', helper.isNumber(1)); + return done(); }); }); }); From d1558eddc2ed5bec5c09312903fbaba79bb09f8d Mon Sep 17 00:00:00 2001 From: Benjamin Coe Date: Sun, 16 Aug 2015 18:02:26 -0700 Subject: [PATCH 49/49] add test demonstrating psubscribe, and pmessage --- test/pubsub.spec.js | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/test/pubsub.spec.js b/test/pubsub.spec.js index dc50246151..7678fd45bf 100644 --- a/test/pubsub.spec.js +++ b/test/pubsub.spec.js @@ -208,6 +208,20 @@ describe("publish/subscribe", function () { }); }); + describe('psubscribe', function () { + // test motivated by issue #753 + it('allows all channels to be subscribed to using a * pattern', function (done) { + sub.psubscribe('*'); + sub.on("pmessage", function(pattern, channel, message) { + assert.strictEqual(pattern, '*'); + assert.strictEqual(channel, '/foo'); + assert.strictEqual(message, 'hello world'); + return done(); + }) + pub.publish('/foo', 'hello world'); + }); + }); + describe('punsubscribe', function () { it('does not complain when punsubscribe is called and there are no subscriptions', function () { sub.punsubscribe()