redis 0.6.7 → 0.7.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (36) hide show
  1. package/.npmignore +1 -0
  2. package/README.md +172 -48
  3. package/{tests → benches}/buffer_bench.js +0 -0
  4. package/benches/hiredis_parser.js +38 -0
  5. package/benches/re_sub_test.js +14 -0
  6. package/{tests → benches}/reconnect_test.js +4 -2
  7. package/{tests → benches}/stress/codec.js +0 -0
  8. package/{tests → benches}/stress/pubsub/pub.js +0 -0
  9. package/{tests → benches}/stress/pubsub/run +0 -0
  10. package/{tests → benches}/stress/pubsub/server.js +0 -0
  11. package/{tests → benches}/stress/rpushblpop/pub.js +0 -0
  12. package/{tests → benches}/stress/rpushblpop/run +0 -0
  13. package/{tests → benches}/stress/rpushblpop/server.js +0 -0
  14. package/{tests → benches}/stress/speed/00 +0 -0
  15. package/{tests → benches}/stress/speed/plot +0 -0
  16. package/{tests → benches}/stress/speed/size-rate.png +0 -0
  17. package/{tests → benches}/stress/speed/speed.js +0 -0
  18. package/{tests → benches}/sub_quit_test.js +0 -0
  19. package/changelog.md +35 -0
  20. package/diff_multi_bench_output.js +87 -0
  21. package/{eval_test.js → examples/eval.js} +0 -0
  22. package/examples/simple.js +9 -2
  23. package/examples/sort.js +17 -0
  24. package/generate_commands.js +0 -1
  25. package/index.js +467 -214
  26. package/lib/commands.js +22 -1
  27. package/lib/parser/hiredis.js +15 -10
  28. package/lib/parser/javascript.js +14 -13
  29. package/lib/queue.js +5 -2
  30. package/lib/util.js +10 -5
  31. package/mem.js +11 -0
  32. package/multi_bench.js +197 -107
  33. package/package.json +6 -13
  34. package/test.js +465 -95
  35. package/simple_test.js +0 -3
  36. package/tests/test_start_stop.js +0 -17
package/test.js CHANGED
@@ -3,9 +3,9 @@ var redis = require("./index"),
3
3
  client = redis.createClient(),
4
4
  client2 = redis.createClient(),
5
5
  client3 = redis.createClient(),
6
- client4 = redis.createClient(9006, "filefish.redistogo.com"),
7
6
  assert = require("assert"),
8
- util = require("./lib/util").util,
7
+ crypto = require("crypto"),
8
+ util = require("./lib/util"),
9
9
  test_db_num = 15, // this DB will be flushed and used for testing
10
10
  tests = {},
11
11
  connected = false,
@@ -23,7 +23,7 @@ function buffers_to_strings(arr) {
23
23
 
24
24
  function require_number(expected, label) {
25
25
  return function (err, results) {
26
- assert.strictEqual(null, err, "result sent back unexpected error: " + err);
26
+ assert.strictEqual(null, err, label + " expected " + expected + ", got error: " + err);
27
27
  assert.strictEqual(expected, results, label + " " + expected + " !== " + results);
28
28
  assert.strictEqual(typeof results, "number", label);
29
29
  return true;
@@ -32,7 +32,7 @@ function require_number(expected, label) {
32
32
 
33
33
  function require_number_any(label) {
34
34
  return function (err, results) {
35
- assert.strictEqual(null, err, "result sent back unexpected error: " + err);
35
+ assert.strictEqual(null, err, label + " expected any number, got error: " + err);
36
36
  assert.strictEqual(typeof results, "number", label + " " + results + " is not a number");
37
37
  return true;
38
38
  };
@@ -40,7 +40,7 @@ function require_number_any(label) {
40
40
 
41
41
  function require_number_pos(label) {
42
42
  return function (err, results) {
43
- assert.strictEqual(null, err, "result sent back unexpected error: " + err);
43
+ assert.strictEqual(null, err, label + " expected positive number, got error: " + err);
44
44
  assert.strictEqual(true, (results > 0), label + " " + results + " is not a positive number");
45
45
  return true;
46
46
  };
@@ -48,7 +48,7 @@ function require_number_pos(label) {
48
48
 
49
49
  function require_string(str, label) {
50
50
  return function (err, results) {
51
- assert.strictEqual(null, err, "result sent back unexpected error: " + err);
51
+ assert.strictEqual(null, err, label + " expected string '" + str + "', got error: " + err);
52
52
  assert.equal(str, results, label + " " + str + " does not match " + results);
53
53
  return true;
54
54
  };
@@ -56,7 +56,7 @@ function require_string(str, label) {
56
56
 
57
57
  function require_null(label) {
58
58
  return function (err, results) {
59
- assert.strictEqual(null, err, "result sent back unexpected error: " + err);
59
+ assert.strictEqual(null, err, label + " expected null, got error: " + err);
60
60
  assert.strictEqual(null, results, label + ": " + results + " is not null");
61
61
  return true;
62
62
  };
@@ -69,7 +69,7 @@ function require_error(label) {
69
69
  };
70
70
  }
71
71
 
72
- function is_empty_array(obj) {
72
+ function is_empty_array(obj) {
73
73
  return Array.isArray(obj) && obj.length === 0;
74
74
  }
75
75
 
@@ -157,7 +157,7 @@ tests.MULTI_3 = function () {
157
157
  // make sure empty mb reply works
158
158
  assert.strictEqual(true, is_empty_array(reply), name);
159
159
  });
160
-
160
+
161
161
  // test nested multi-bulk replies with empty mb elements.
162
162
  client.multi([
163
163
  ["smembers", "some set"],
@@ -230,7 +230,7 @@ tests.MULTI_6 = function () {
230
230
  tests.EVAL_1 = function () {
231
231
  var name = "EVAL_1";
232
232
 
233
- if (client.server_info.versions[0] >= 2 && client.server_info.versions[1] >= 9) {
233
+ if (client.server_info.versions[0] >= 2 && client.server_info.versions[1] >= 5) {
234
234
  // test {EVAL - Lua integer -> Redis protocol type conversion}
235
235
  client.eval("return 100.5", 0, require_number(100, name));
236
236
  // test {EVAL - Lua string -> Redis protocol type conversion}
@@ -262,62 +262,89 @@ tests.EVAL_1 = function () {
262
262
  assert.strictEqual("c", res[2], name);
263
263
  assert.strictEqual("d", res[3], name);
264
264
  });
265
- // test {EVAL - is Lua able to call Redis API?}
266
- client.set("mykey", "myval");
267
- client.eval("return redis.call('get','mykey')", 0, require_string("myval", name));
268
- // test {EVALSHA - Can we call a SHA1 if already defined?}
269
- client.evalsha("9bd632c7d33e571e9f24556ebed26c3479a87129", 0, require_string("myval", name));
270
- // test {EVALSHA - Do we get an error on non defined SHA1?}
271
- client.evalsha("ffffffffffffffffffffffffffffffffffffffff", 0, require_error(name));
265
+
266
+ // prepare sha sum for evalsha cache test
267
+ var source = "return redis.call('get', 'sha test')",
268
+ sha = crypto.createHash('sha1').update(source).digest('hex');
269
+
270
+ client.set("sha test", "eval get sha test", function (err, res) {
271
+ if (err) throw err;
272
+ // test {EVAL - is Lua able to call Redis API?}
273
+ client.eval(source, 0, function (err, res) {
274
+ require_string("eval get sha test", name)(err, res);
275
+ // test {EVALSHA - Can we call a SHA1 if already defined?}
276
+ client.evalsha(sha, 0, require_string("eval get sha test", name));
277
+ // test {EVALSHA - Do we get an error on non defined SHA1?}
278
+ client.evalsha("ffffffffffffffffffffffffffffffffffffffff", 0, require_error(name));
279
+ });
280
+ });
281
+
272
282
  // test {EVAL - Redis integer -> Lua type conversion}
273
- client.set("x", 0);
274
- client.eval("local foo = redis.call('incr','x')\n" + "return {type(foo),foo}", 0, function (err, res) {
275
- assert.strictEqual(2, res.length, name);
276
- assert.strictEqual("number", res[0], name);
277
- assert.strictEqual(1, res[1], name);
283
+ client.set("incr key", 0, function (err, reply) {
284
+ if (err) throw err;
285
+ client.eval("local foo = redis.call('incr','incr key')\n" + "return {type(foo),foo}", 0, function (err, res) {
286
+ if (err) throw err;
287
+ assert.strictEqual(2, res.length, name);
288
+ assert.strictEqual("number", res[0], name);
289
+ assert.strictEqual(1, res[1], name);
290
+ });
278
291
  });
279
- // test {EVAL - Redis bulk -> Lua type conversion}
280
- client.eval("local foo = redis.call('get','mykey'); return {type(foo),foo}", 0, function (err, res) {
281
- assert.strictEqual(2, res.length, name);
282
- assert.strictEqual("string", res[0], name);
283
- assert.strictEqual("myval", res[1], name);
292
+
293
+ client.set("bulk reply key", "bulk reply value", function (err, res) {
294
+ // test {EVAL - Redis bulk -> Lua type conversion}
295
+ client.eval("local foo = redis.call('get','bulk reply key'); return {type(foo),foo}", 0, function (err, res) {
296
+ if (err) throw err;
297
+ assert.strictEqual(2, res.length, name);
298
+ assert.strictEqual("string", res[0], name);
299
+ assert.strictEqual("bulk reply value", res[1], name);
300
+ });
284
301
  });
302
+
285
303
  // test {EVAL - Redis multi bulk -> Lua type conversion}
286
- client.del("mylist");
287
- client.rpush("mylist", "a");
288
- client.rpush("mylist", "b");
289
- client.rpush("mylist", "c");
290
- client.eval("local foo = redis.call('lrange','mylist',0,-1)\n" + "return {type(foo),foo[1],foo[2],foo[3],# foo}", 0, function (err, res) {
291
- assert.strictEqual(5, res.length, name);
292
- assert.strictEqual("table", res[0], name);
293
- assert.strictEqual("a", res[1], name);
294
- assert.strictEqual("b", res[2], name);
295
- assert.strictEqual("c", res[3], name);
296
- assert.strictEqual(3, res[4], name);
297
- });
304
+ client.multi()
305
+ .del("mylist")
306
+ .rpush("mylist", "a")
307
+ .rpush("mylist", "b")
308
+ .rpush("mylist", "c")
309
+ .exec(function (err, replies) {
310
+ if (err) throw err;
311
+ client.eval("local foo = redis.call('lrange','mylist',0,-1); return {type(foo),foo[1],foo[2],foo[3],# foo}", 0, function (err, res) {
312
+ assert.strictEqual(5, res.length, name);
313
+ assert.strictEqual("table", res[0], name);
314
+ assert.strictEqual("a", res[1], name);
315
+ assert.strictEqual("b", res[2], name);
316
+ assert.strictEqual("c", res[3], name);
317
+ assert.strictEqual(3, res[4], name);
318
+ });
319
+ });
298
320
  // test {EVAL - Redis status reply -> Lua type conversion}
299
321
  client.eval("local foo = redis.call('set','mykey','myval'); return {type(foo),foo['ok']}", 0, function (err, res) {
322
+ if (err) throw err;
300
323
  assert.strictEqual(2, res.length, name);
301
324
  assert.strictEqual("table", res[0], name);
302
325
  assert.strictEqual("OK", res[1], name);
303
326
  });
304
327
  // test {EVAL - Redis error reply -> Lua type conversion}
305
- client.set("mykey", "myval");
306
- client.eval("local foo = redis.call('incr','mykey'); return {type(foo),foo['err']}", 0, function (err, res) {
307
- assert.strictEqual(2, res.length, name);
308
- assert.strictEqual("table", res[0], name);
309
- assert.strictEqual("ERR value is not an integer or out of range", res[1], name);
328
+ client.set("error reply key", "error reply value", function (err, res) {
329
+ if (err) throw err;
330
+ client.eval("local foo = redis.pcall('incr','error reply key'); return {type(foo),foo['err']}", 0, function (err, res) {
331
+ if (err) throw err;
332
+ assert.strictEqual(2, res.length, name);
333
+ assert.strictEqual("table", res[0], name);
334
+ assert.strictEqual("ERR value is not an integer or out of range", res[1], name);
335
+ });
310
336
  });
311
337
  // test {EVAL - Redis nil bulk reply -> Lua type conversion}
312
- client.del("mykey");
313
- client.eval("local foo = redis.call('get','mykey'); return {type(foo),foo == false}", 0, function (err, res) {
314
- assert.strictEqual(2, res.length, name);
315
- assert.strictEqual("boolean", res[0], name);
316
- assert.strictEqual(1, res[1], name);
338
+ client.del("nil reply key", function (err, res) {
339
+ if (err) throw err;
340
+ client.eval("local foo = redis.call('get','nil reply key'); return {type(foo),foo == false}", 0, function (err, res) {
341
+ if (err) throw err;
342
+ assert.strictEqual(2, res.length, name);
343
+ assert.strictEqual("boolean", res[0], name);
344
+ assert.strictEqual(1, res[1], name);
345
+ next(name);
346
+ });
317
347
  });
318
- // test {EVAL - Script can't run more than configured time limit} {
319
- client.config("set", "lua-time-limit", 1);
320
- client.eval("local i = 0; while true do i=i+1 end", 0, last("name", require_error(name)));
321
348
  } else {
322
349
  console.log("Skipping " + name + " because server version isn't new enough.");
323
350
  next(name);
@@ -339,6 +366,114 @@ tests.WATCH_MULTI = function () {
339
366
  }
340
367
  };
341
368
 
369
+ tests.detect_buffers = function () {
370
+ var name = "detect_buffers", detect_client = redis.createClient(null, null, {detect_buffers: true});
371
+
372
+ detect_client.on("ready", function () {
373
+ // single Buffer or String
374
+ detect_client.set("string key 1", "string value");
375
+ detect_client.get("string key 1", require_string("string value", name));
376
+ detect_client.get(new Buffer("string key 1"), function (err, reply) {
377
+ assert.strictEqual(null, err, name);
378
+ assert.strictEqual(true, Buffer.isBuffer(reply), name);
379
+ assert.strictEqual("<Buffer 73 74 72 69 6e 67 20 76 61 6c 75 65>", reply.inspect(), name);
380
+ });
381
+
382
+ detect_client.hmset("hash key 2", "key 1", "val 1", "key 2", "val 2");
383
+ // array of Buffers or Strings
384
+ detect_client.hmget("hash key 2", "key 1", "key 2", function (err, reply) {
385
+ assert.strictEqual(null, err, name);
386
+ assert.strictEqual(true, Array.isArray(reply), name);
387
+ assert.strictEqual(2, reply.length, name);
388
+ assert.strictEqual("val 1", reply[0], name);
389
+ assert.strictEqual("val 2", reply[1], name);
390
+ });
391
+ detect_client.hmget(new Buffer("hash key 2"), "key 1", "key 2", function (err, reply) {
392
+ assert.strictEqual(null, err, name);
393
+ assert.strictEqual(true, Array.isArray(reply));
394
+ assert.strictEqual(2, reply.length, name);
395
+ assert.strictEqual(true, Buffer.isBuffer(reply[0]));
396
+ assert.strictEqual(true, Buffer.isBuffer(reply[1]));
397
+ assert.strictEqual("<Buffer 76 61 6c 20 31>", reply[0].inspect(), name);
398
+ assert.strictEqual("<Buffer 76 61 6c 20 32>", reply[1].inspect(), name);
399
+ });
400
+
401
+ // Object of Buffers or Strings
402
+ detect_client.hgetall("hash key 2", function (err, reply) {
403
+ assert.strictEqual(null, err, name);
404
+ assert.strictEqual("object", typeof reply, name);
405
+ assert.strictEqual(2, Object.keys(reply).length, name);
406
+ assert.strictEqual("val 1", reply["key 1"], name);
407
+ assert.strictEqual("val 2", reply["key 2"], name);
408
+ });
409
+ detect_client.hgetall(new Buffer("hash key 2"), function (err, reply) {
410
+ assert.strictEqual(null, err, name);
411
+ assert.strictEqual("object", typeof reply, name);
412
+ assert.strictEqual(2, Object.keys(reply).length, name);
413
+ assert.strictEqual(true, Buffer.isBuffer(reply["key 1"]));
414
+ assert.strictEqual(true, Buffer.isBuffer(reply["key 2"]));
415
+ assert.strictEqual("<Buffer 76 61 6c 20 31>", reply["key 1"].inspect(), name);
416
+ assert.strictEqual("<Buffer 76 61 6c 20 32>", reply["key 2"].inspect(), name);
417
+ });
418
+
419
+ detect_client.quit(function (err, res) {
420
+ next(name);
421
+ });
422
+ });
423
+ };
424
+
425
+ tests.socket_nodelay = function () {
426
+ var name = "socket_nodelay", c1, c2, c3, ready_count = 0, quit_count = 0;
427
+
428
+ c1 = redis.createClient(null, null, {socket_nodelay: true});
429
+ c2 = redis.createClient(null, null, {socket_nodelay: false});
430
+ c3 = redis.createClient(null, null);
431
+
432
+ function quit_check() {
433
+ quit_count++;
434
+
435
+ if (quit_count === 3) {
436
+ next(name);
437
+ }
438
+ }
439
+
440
+ function run() {
441
+ assert.strictEqual(true, c1.options.socket_nodelay, name);
442
+ assert.strictEqual(false, c2.options.socket_nodelay, name);
443
+ assert.strictEqual(true, c3.options.socket_nodelay, name);
444
+
445
+ c1.set(["set key 1", "set val"], require_string("OK", name));
446
+ c1.set(["set key 2", "set val"], require_string("OK", name));
447
+ c1.get(["set key 1"], require_string("set val", name));
448
+ c1.get(["set key 2"], require_string("set val", name));
449
+
450
+ c2.set(["set key 3", "set val"], require_string("OK", name));
451
+ c2.set(["set key 4", "set val"], require_string("OK", name));
452
+ c2.get(["set key 3"], require_string("set val", name));
453
+ c2.get(["set key 4"], require_string("set val", name));
454
+
455
+ c3.set(["set key 5", "set val"], require_string("OK", name));
456
+ c3.set(["set key 6", "set val"], require_string("OK", name));
457
+ c3.get(["set key 5"], require_string("set val", name));
458
+ c3.get(["set key 6"], require_string("set val", name));
459
+
460
+ c1.quit(quit_check);
461
+ c2.quit(quit_check);
462
+ c3.quit(quit_check);
463
+ }
464
+
465
+ function ready_check() {
466
+ ready_count++;
467
+ if (ready_count === 3) {
468
+ run();
469
+ }
470
+ }
471
+
472
+ c1.on("ready", ready_check);
473
+ c2.on("ready", ready_check);
474
+ c3.on("ready", ready_check);
475
+ };
476
+
342
477
  tests.reconnect = function () {
343
478
  var name = "reconnect";
344
479
 
@@ -348,7 +483,7 @@ tests.reconnect = function () {
348
483
  // For orderly shutdown in normal programs, do client.quit()
349
484
  client.stream.destroy();
350
485
  });
351
-
486
+
352
487
  client.on("reconnecting", function on_recon(params) {
353
488
  client.on("connect", function on_connect() {
354
489
  client.select(test_db_num, require_string("OK", name));
@@ -363,6 +498,17 @@ tests.reconnect = function () {
363
498
  });
364
499
  };
365
500
 
501
+ tests.idle = function () {
502
+ var name = "idle";
503
+
504
+ client.on("idle", function on_idle() {
505
+ client.removeListener("idle", on_idle);
506
+ next(name);
507
+ });
508
+
509
+ client.set("idle", "test");
510
+ };
511
+
366
512
  tests.HSET = function () {
367
513
  var key = "test hash",
368
514
  field1 = new Buffer("0123456789"),
@@ -383,6 +529,23 @@ tests.HSET = function () {
383
529
  client.HSET(key, field2, value2, last(name, require_number(0, name)));
384
530
  };
385
531
 
532
+ tests.HLEN = function () {
533
+ var key = "test hash",
534
+ field1 = new Buffer("0123456789"),
535
+ value1 = new Buffer("abcdefghij"),
536
+ field2 = new Buffer(0),
537
+ value2 = new Buffer(0),
538
+ name = "HSET",
539
+ timeout = 1000;
540
+
541
+ client.HSET(key, field1, value1, function (err, results) {
542
+ client.HLEN(key, function (err, len) {
543
+ assert.ok(2 === +len);
544
+ next(name);
545
+ });
546
+ });
547
+ }
548
+
386
549
  tests.HMSET_BUFFER_AND_ARRAY = function () {
387
550
  // Saving a buffer and an array to the same key should not error
388
551
  var key = "test hash",
@@ -395,7 +558,7 @@ tests.HMSET_BUFFER_AND_ARRAY = function () {
395
558
  client.HMSET(key, field1, value1, field2, value2, last(name, require_string("OK", name)));
396
559
  };
397
560
 
398
- // TODO - add test for HMSET. It is special. Test for all forms as well as optional callbacks
561
+ // TODO - add test for HMSET with optional callbacks
399
562
 
400
563
  tests.HMGET = function () {
401
564
  var key1 = "test hash 1", key2 = "test hash 2", name = "HMGET";
@@ -469,7 +632,25 @@ tests.SUBSCRIBE = function () {
469
632
  });
470
633
 
471
634
  client1.set("did a thing", 1, require_string("OK", name));
472
- client1.subscribe("chan1", "chan2");
635
+ client1.subscribe("chan1", "chan2", function (err, results) {
636
+ assert.strictEqual(null, err, "result sent back unexpected error: " + err);
637
+ assert.strictEqual("chan1", results.toString(), name);
638
+ });
639
+ };
640
+
641
+ tests.SUB_UNSUB_SUB = function () {
642
+ var name = "SUB_UNSUB_SUB";
643
+ client3.subscribe('chan3');
644
+ client3.unsubscribe('chan3');
645
+ client3.subscribe('chan3', function (err, results) {
646
+ assert.strictEqual(null, err, "unexpected error: " + err);
647
+ client2.publish('chan3', 'foo');
648
+ });
649
+ client3.on('message', function (channel, message) {
650
+ assert.strictEqual(channel, 'chan3');
651
+ assert.strictEqual(message, 'foo');
652
+ next(name);
653
+ });
473
654
  };
474
655
 
475
656
  tests.SUBSCRIBE_QUIT = function () {
@@ -483,6 +664,66 @@ tests.SUBSCRIBE_QUIT = function () {
483
664
  client3.subscribe("chan3");
484
665
  };
485
666
 
667
+ tests.SUBSCRIBE_CLOSE_RESUBSCRIBE = function () {
668
+ var name = "SUBSCRIBE_CLOSE_RESUBSCRIBE";
669
+ var c1 = redis.createClient();
670
+ var c2 = redis.createClient();
671
+ var count = 0;
672
+
673
+ /* Create two clients. c1 subscribes to two channels, c2 will publish to them.
674
+ c2 publishes the first message.
675
+ c1 gets the message and drops its connection. It must resubscribe itself.
676
+ When it resubscribes, c2 publishes the second message, on the same channel
677
+ c1 gets the message and drops its connection. It must resubscribe itself, again.
678
+ When it resubscribes, c2 publishes the third message, on the second channel
679
+ c1 gets the message and drops its connection. When it reconnects, the test ends.
680
+ */
681
+
682
+ c1.on("message", function(channel, message) {
683
+ if (channel === "chan1") {
684
+ assert.strictEqual(message, "hi on channel 1");
685
+ c1.stream.end();
686
+
687
+ } else if (channel === "chan2") {
688
+ assert.strictEqual(message, "hi on channel 2");
689
+ c1.stream.end();
690
+
691
+ } else {
692
+ c1.quit();
693
+ c2.quit();
694
+ assert.fail("test failed");
695
+ }
696
+ })
697
+
698
+ c1.subscribe("chan1", "chan2");
699
+
700
+ c2.once("ready", function() {
701
+ console.log("c2 is ready");
702
+ c1.on("ready", function(err, results) {
703
+ console.log("c1 is ready", count);
704
+
705
+ count++;
706
+ if (count == 1) {
707
+ c2.publish("chan1", "hi on channel 1");
708
+ return;
709
+
710
+ } else if (count == 2) {
711
+ c2.publish("chan2", "hi on channel 2");
712
+
713
+ } else {
714
+ c1.quit(function() {
715
+ c2.quit(function() {
716
+ next(name);
717
+ });
718
+ });
719
+ }
720
+ });
721
+
722
+ c2.publish("chan1", "hi on channel 1");
723
+
724
+ });
725
+ };
726
+
486
727
  tests.EXISTS = function () {
487
728
  var name = "EXISTS";
488
729
  client.del("foo", "foo2", require_number_any(name));
@@ -509,7 +750,7 @@ tests.TYPE = function () {
509
750
  client.sadd(["set key", "should be a set"], require_number_any(name));
510
751
  client.zadd(["zset key", "10.0", "should be a zset"], require_number_any(name));
511
752
  client.hset(["hash key", "hashtest", "should be a hash"], require_number_any(0, name));
512
-
753
+
513
754
  client.TYPE(["string key"], require_string("string", name));
514
755
  client.TYPE(["list key"], require_string("list", name));
515
756
  client.TYPE(["set key"], require_string("set", name));
@@ -666,15 +907,15 @@ tests.HGETALL = function () {
666
907
  tests.HGETALL_NULL = function () {
667
908
  var name = "HGETALL_NULL";
668
909
 
669
- client.hgetall('missing', function (err, obj) {
910
+ client.hgetall("missing", function (err, obj) {
670
911
  assert.strictEqual(null, err);
671
- assert.deepEqual([], obj);
912
+ assert.strictEqual(null, obj);
672
913
  next(name);
673
914
  });
674
915
  };
675
916
 
676
917
  tests.UTF8 = function () {
677
- var name = "UTF8",
918
+ var name = "UTF8",
678
919
  utf8_sample = "ಠ_ಠ";
679
920
 
680
921
  client.set(["utf8test", utf8_sample], require_string("OK", name));
@@ -689,15 +930,15 @@ tests.UTF8 = function () {
689
930
 
690
931
  tests.SADD = function () {
691
932
  var name = "SADD";
692
-
933
+
693
934
  client.del('set0');
694
- client.sadd('set0', 'member0', require_number(1, name));
935
+ client.SADD('set0', 'member0', require_number(1, name));
695
936
  client.sadd('set0', 'member0', last(name, require_number(0, name)));
696
937
  };
697
938
 
698
939
  tests.SADD2 = function () {
699
940
  var name = "SADD2";
700
-
941
+
701
942
  client.del("set0");
702
943
  client.sadd("set0", ["member0", "member1", "member2"], require_number(3, name));
703
944
  client.smembers("set0", function (err, res) {
@@ -705,13 +946,20 @@ tests.SADD2 = function () {
705
946
  assert.strictEqual(res[0], "member0");
706
947
  assert.strictEqual(res[1], "member1");
707
948
  assert.strictEqual(res[2], "member2");
949
+ });
950
+ client.SADD("set1", ["member0", "member1", "member2"], require_number(3, name));
951
+ client.smembers("set1", function (err, res) {
952
+ assert.strictEqual(res.length, 3);
953
+ assert.strictEqual(res[0], "member0");
954
+ assert.strictEqual(res[1], "member1");
955
+ assert.strictEqual(res[2], "member2");
708
956
  next(name);
709
957
  });
710
958
  };
711
959
 
712
960
  tests.SISMEMBER = function () {
713
961
  var name = "SISMEMBER";
714
-
962
+
715
963
  client.del('set0');
716
964
  client.sadd('set0', 'member0', require_number(1, name));
717
965
  client.sismember('set0', 'member0', require_number(1, name));
@@ -720,7 +968,7 @@ tests.SISMEMBER = function () {
720
968
 
721
969
  tests.SCARD = function () {
722
970
  var name = "SCARD";
723
-
971
+
724
972
  client.del('set0');
725
973
  client.sadd('set0', 'member0', require_number(1, name));
726
974
  client.scard('set0', require_number(1, name));
@@ -740,7 +988,7 @@ tests.SREM = function () {
740
988
 
741
989
  tests.SPOP = function () {
742
990
  var name = "SPOP";
743
-
991
+
744
992
  client.del('zzz');
745
993
  client.sadd('zzz', 'member0', require_number(1, name));
746
994
  client.scard('zzz', require_number(1, name));
@@ -757,7 +1005,7 @@ tests.SPOP = function () {
757
1005
 
758
1006
  tests.SDIFF = function () {
759
1007
  var name = "SDIFF";
760
-
1008
+
761
1009
  client.del('foo');
762
1010
  client.sadd('foo', 'x', require_number(1, name));
763
1011
  client.sadd('foo', 'a', require_number(1, name));
@@ -783,7 +1031,7 @@ tests.SDIFF = function () {
783
1031
 
784
1032
  tests.SDIFFSTORE = function () {
785
1033
  var name = "SDIFFSTORE";
786
-
1034
+
787
1035
  client.del('foo');
788
1036
  client.del('bar');
789
1037
  client.del('baz');
@@ -799,7 +1047,7 @@ tests.SDIFFSTORE = function () {
799
1047
  client.sadd('baz', 'a', require_number(1, name));
800
1048
  client.sadd('baz', 'd', require_number(1, name));
801
1049
 
802
- // NB: SDIFFSTORE returns the number of elements in the dstkey
1050
+ // NB: SDIFFSTORE returns the number of elements in the dstkey
803
1051
 
804
1052
  client.sdiffstore('quux', 'foo', 'bar', 'baz', require_number(2, name));
805
1053
 
@@ -816,7 +1064,7 @@ tests.SDIFFSTORE = function () {
816
1064
 
817
1065
  tests.SMEMBERS = function () {
818
1066
  var name = "SMEMBERS";
819
-
1067
+
820
1068
  client.del('foo');
821
1069
  client.sadd('foo', 'x', require_number(1, name));
822
1070
 
@@ -860,7 +1108,7 @@ tests.SINTER = function () {
860
1108
  client.del('sa');
861
1109
  client.del('sb');
862
1110
  client.del('sc');
863
-
1111
+
864
1112
  client.sadd('sa', 'a', require_number(1, name));
865
1113
  client.sadd('sa', 'b', require_number(1, name));
866
1114
  client.sadd('sa', 'c', require_number(1, name));
@@ -942,11 +1190,11 @@ tests.SINTERSTORE = function () {
942
1190
 
943
1191
  tests.SUNION = function () {
944
1192
  var name = "SUNION";
945
-
1193
+
946
1194
  client.del('sa');
947
1195
  client.del('sb');
948
1196
  client.del('sc');
949
-
1197
+
950
1198
  client.sadd('sa', 'a', require_number(1, name));
951
1199
  client.sadd('sa', 'b', require_number(1, name));
952
1200
  client.sadd('sa', 'c', require_number(1, name));
@@ -970,12 +1218,12 @@ tests.SUNION = function () {
970
1218
 
971
1219
  tests.SUNIONSTORE = function () {
972
1220
  var name = "SUNIONSTORE";
973
-
1221
+
974
1222
  client.del('sa');
975
1223
  client.del('sb');
976
1224
  client.del('sc');
977
1225
  client.del('foo');
978
-
1226
+
979
1227
  client.sadd('sa', 'a', require_number(1, name));
980
1228
  client.sadd('sa', 'b', require_number(1, name));
981
1229
  client.sadd('sa', 'c', require_number(1, name));
@@ -1012,7 +1260,7 @@ tests.SORT = function () {
1012
1260
 
1013
1261
  client.del('y');
1014
1262
  client.del('x');
1015
-
1263
+
1016
1264
  client.rpush('y', 'd', require_number(1, name));
1017
1265
  client.rpush('y', 'b', require_number(2, name));
1018
1266
  client.rpush('y', 'a', require_number(3, name));
@@ -1121,18 +1369,53 @@ tests.SORT = function () {
1121
1369
  assert.deepEqual(buffers_to_strings(values), ['foo', 'bux', 'bar', 'tux', 'baz', 'lux', 'buz', 'qux'], name);
1122
1370
  next(name);
1123
1371
  });
1124
-
1372
+
1125
1373
  // TODO - sort by hash value
1126
1374
  };
1127
1375
 
1376
+ tests.MONITOR = function () {
1377
+ var name = "MONITOR", responses = [], monitor_client;
1378
+
1379
+ monitor_client = redis.createClient();
1380
+ monitor_client.monitor(function (err, res) {
1381
+ client.mget("some", "keys", "foo", "bar");
1382
+ client.set("json", JSON.stringify({
1383
+ foo: "123",
1384
+ bar: "sdflkdfsjk",
1385
+ another: false
1386
+ }));
1387
+ });
1388
+ monitor_client.on("monitor", function (time, args) {
1389
+ // skip monitor command for Redis <= 2.4.16
1390
+ if (args[0] === "monitor") return;
1391
+
1392
+ responses.push(args);
1393
+ if (responses.length === 2) {
1394
+ assert.strictEqual(5, responses[0].length);
1395
+ assert.strictEqual("mget", responses[0][0]);
1396
+ assert.strictEqual("some", responses[0][1]);
1397
+ assert.strictEqual("keys", responses[0][2]);
1398
+ assert.strictEqual("foo", responses[0][3]);
1399
+ assert.strictEqual("bar", responses[0][4]);
1400
+ assert.strictEqual(3, responses[1].length);
1401
+ assert.strictEqual("set", responses[1][0]);
1402
+ assert.strictEqual("json", responses[1][1]);
1403
+ assert.strictEqual('{"foo":"123","bar":"sdflkdfsjk","another":false}', responses[1][2]);
1404
+ monitor_client.quit(function (err, res) {
1405
+ next(name);
1406
+ });
1407
+ }
1408
+ });
1409
+ };
1410
+
1128
1411
  tests.BLPOP = function () {
1129
1412
  var name = "BLPOP";
1130
-
1413
+
1131
1414
  client.rpush("blocking list", "initial value", function (err, res) {
1132
1415
  client2.BLPOP("blocking list", 0, function (err, res) {
1133
1416
  assert.strictEqual("blocking list", res[0].toString());
1134
1417
  assert.strictEqual("initial value", res[1].toString());
1135
-
1418
+
1136
1419
  client.rpush("blocking list", "wait for this value");
1137
1420
  });
1138
1421
  client2.BLPOP("blocking list", 0, function (err, res) {
@@ -1145,7 +1428,7 @@ tests.BLPOP = function () {
1145
1428
 
1146
1429
  tests.BLPOP_TIMEOUT = function () {
1147
1430
  var name = "BLPOP_TIMEOUT";
1148
-
1431
+
1149
1432
  // try to BLPOP the list again, which should be empty. This should timeout and return null.
1150
1433
  client2.BLPOP("blocking list", 1, function (err, res) {
1151
1434
  if (err) {
@@ -1175,6 +1458,107 @@ tests.TTL = function () {
1175
1458
  }, 500);
1176
1459
  };
1177
1460
 
1461
+ tests.OPTIONAL_CALLBACK = function () {
1462
+ var name = "OPTIONAL_CALLBACK";
1463
+ client.del("op_cb1");
1464
+ client.set("op_cb1", "x");
1465
+ client.get("op_cb1", last(name, require_string("x", name)));
1466
+ };
1467
+
1468
+ tests.OPTIONAL_CALLBACK_UNDEFINED = function () {
1469
+ var name = "OPTIONAL_CALLBACK_UNDEFINED";
1470
+ client.del("op_cb2");
1471
+ client.set("op_cb2", "y", undefined);
1472
+ client.get("op_cb2", last(name, require_string("y", name)));
1473
+ };
1474
+
1475
+ tests.HMSET_THROWS_ON_NON_STRINGS = function () {
1476
+ var name = "HMSET_THROWS_ON_NON_STRINGS";
1477
+ var hash = name;
1478
+ var data = { "a": [ "this is not a string" ] };
1479
+
1480
+ client.hmset(hash, data, cb);
1481
+ function cb(e, r) {
1482
+ assert(e); // should be an error!
1483
+ }
1484
+
1485
+ // alternative way it throws
1486
+ function thrower() {
1487
+ client.hmset(hash, data);
1488
+ }
1489
+ assert.throws(thrower);
1490
+ next(name);
1491
+ };
1492
+
1493
+ tests.ENABLE_OFFLINE_QUEUE_TRUE = function () {
1494
+ var name = "ENABLE_OFFLINE_QUEUE_TRUE";
1495
+ var cli = redis.createClient(9999, null, {
1496
+ max_attempts: 1
1497
+ // default :)
1498
+ // enable_offline_queue: true
1499
+ });
1500
+ cli.on('error', function(e) {
1501
+ // ignore, b/c expecting a "can't connect" error
1502
+ });
1503
+ return setTimeout(function() {
1504
+ cli.set(name, name, function(err, result) {
1505
+ assert.ifError(err);
1506
+ });
1507
+
1508
+ return setTimeout(function(){
1509
+ assert.strictEqual(cli.offline_queue.length, 1);
1510
+ return next(name);
1511
+ }, 25);
1512
+ }, 50);
1513
+ };
1514
+
1515
+ tests.ENABLE_OFFLINE_QUEUE_FALSE = function () {
1516
+ var name = "ENABLE_OFFLINE_QUEUE_FALSE";
1517
+ var cli = redis.createClient(9999, null, {
1518
+ max_attempts: 1,
1519
+ enable_offline_queue: false
1520
+ });
1521
+ cli.on('error', function() {
1522
+ // ignore, see above
1523
+ });
1524
+ assert.throws(function () {
1525
+ cli.set(name, name)
1526
+ })
1527
+ assert.doesNotThrow(function () {
1528
+ cli.set(name, name, function (err) {
1529
+ // should callback with an error
1530
+ assert.ok(err);
1531
+ setTimeout(function () {
1532
+ next(name);
1533
+ }, 50);
1534
+ });
1535
+ });
1536
+ };
1537
+
1538
+ // TODO - need a better way to test auth, maybe auto-config a local Redis server or something.
1539
+ // Yes, this is the real password. Please be nice, thanks.
1540
+ tests.auth = function () {
1541
+ var name = "AUTH", client4, ready_count = 0;
1542
+
1543
+ client4 = redis.createClient(9006, "filefish.redistogo.com");
1544
+ client4.auth("664b1b6aaf134e1ec281945a8de702a9", function (err, res) {
1545
+ assert.strictEqual(null, err, name);
1546
+ assert.strictEqual("OK", res.toString(), name);
1547
+ });
1548
+
1549
+ // test auth, then kill the connection so it'll auto-reconnect and auto-re-auth
1550
+ client4.on("ready", function () {
1551
+ ready_count++;
1552
+ if (ready_count === 1) {
1553
+ client4.stream.destroy();
1554
+ } else {
1555
+ client4.quit(function (err, res) {
1556
+ next(name);
1557
+ });
1558
+ }
1559
+ });
1560
+ };
1561
+
1178
1562
  all_tests = Object.keys(tests);
1179
1563
  all_start = new Date();
1180
1564
  test_count = 0;
@@ -1190,14 +1574,12 @@ run_next_test = function run_next_test() {
1190
1574
  console.log('\n completed \x1b[32m%d\x1b[0m tests in \x1b[33m%d\x1b[0m ms\n', test_count, new Date() - all_start);
1191
1575
  client.quit();
1192
1576
  client2.quit();
1193
- client4.quit();
1194
1577
  }
1195
1578
  };
1196
1579
 
1197
- console.log("Using reply parser " + client.reply_parser.name);
1198
-
1199
1580
  client.once("ready", function start_tests() {
1200
1581
  console.log("Connected to " + client.host + ":" + client.port + ", Redis server version " + client.server_info.redis_version + "\n");
1582
+ console.log("Using reply parser " + client.reply_parser.name);
1201
1583
 
1202
1584
  run_next_test();
1203
1585
 
@@ -1208,17 +1590,6 @@ client.on('end', function () {
1208
1590
  ended = true;
1209
1591
  });
1210
1592
 
1211
- // TODO - need a better way to test auth, maybe auto-config a local Redis server? Sounds hard.
1212
- // Yes, this is the real password. Please be nice, thanks.
1213
- client4.auth("664b1b6aaf134e1ec281945a8de702a9", function (err, res) {
1214
- var name = "AUTH_4";
1215
-
1216
- if (err) {
1217
- assert.fail(err, name);
1218
- }
1219
- assert.strictEqual("OK", res.toString(), "auth");
1220
- });
1221
-
1222
1593
  // Exit immediately on connection failure, which triggers "exit", below, which fails the test
1223
1594
  client.on("error", function (err) {
1224
1595
  console.error("client: " + err.stack);
@@ -1232,9 +1603,8 @@ client3.on("error", function (err) {
1232
1603
  console.error("client3: " + err.stack);
1233
1604
  process.exit();
1234
1605
  });
1235
-
1236
1606
  client.on("reconnecting", function (params) {
1237
- // console.log("reconnecting: " + util.inspect(params));
1607
+ console.log("reconnecting: " + util.inspect(params));
1238
1608
  });
1239
1609
 
1240
1610
  process.on('uncaughtException', function (err) {