Permalink
Browse files

Moved redis to lib/redis

  • Loading branch information...
1 parent 317ae54 commit 1b4d5da617f7baf94c68367e0917040963fc63a3 @tj committed Jul 27, 2010
View
1 index.js
View
2 index.js
@@ -0,0 +1,2 @@
+
+module.exports = require('./lib/connect-redis');
View
2 lib/connect-redis.js
@@ -11,7 +11,7 @@
var sys = require('sys'),
Store = require('connect/middleware/session/store'),
- redis = require('./support/redis/lib/redis-client');
+ redis = require('./redis/lib/redis-client');
/**
* Initialize RedisStore with the given `options`.
1 lib/redis
@@ -0,0 +1 @@
+Subproject commit 30e9f41e7f62137ba47a0f5929ddd8a1e40a6553
View
1 support/redis/.gitignore
@@ -1 +0,0 @@
-*.swp
View
19 support/redis/LICENSE
@@ -1,19 +0,0 @@
-© 2010 by Fictorial LLC
-
-Permission is hereby granted, free of charge, to any person obtaining a copy
-of this software and associated documentation files (the "Software"), to deal
-in the Software without restriction, including without limitation the rights
-to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
-copies of the Software, and to permit persons to whom the Software is
-furnished to do so, subject to the following conditions:
-
-The above copyright notice and this permission notice shall be included in
-all copies or substantial portions of the Software.
-
-THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
-IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
-FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
-AUTHORS OR COPYRIGHT 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.
View
103 support/redis/README.md
@@ -1,103 +0,0 @@
-# Redis client for Node.js
-
-## In a nutshell
-
-- Talk to Redis from Node.js
-- Fully asynchronous; your code is called back when an operation completes
-- [Binary-safe](http://github.com/fictorial/redis-node-client/blob/master/test/test.js#L353-363); uses Node.js Buffer objects for request serialization and reply parsing
- - e.g. store a PNG in Redis if you'd like
-- Client API directly follows Redis' [command specification](http://code.google.com/p/redis/wiki/CommandReference)
-- *You have to understand how Redis works and the semantics of its command set to most effectively use this client*
-- Supports Redis' new exciting [PUBSUB](http://code.google.com/p/redis/wiki/PublishSubscribe) commands
-- Automatically reconnects to Redis (doesn't drop commands sent while waiting to reconnect either) using [exponential backoff](http://en.wikipedia.org/wiki/Exponential_backoff)
- - Be sure to see [this script](http://github.com/fictorial/redis-node-client/blob/master/test/test_shutdown_reconnect.js) for a deeper discussion
-
-## Synopsis
-
-When working from a git clone:
-
- var sys = require("sys");
- var client = require("../lib/redis-client").createClient();
- client.info(function (err, info) {
- if (err) throw new Error(err);
- sys.puts("Redis Version is: " + info.redis_version);
- client.close();
- });
-
-When working with a Kiwi-based installation:
-
- // $ kiwi install redis-client
-
- var sys = require("sys"),
- kiwi = require("kiwi"),
- client = kiwi.require("redis-client").createClient();
-
- client.info(function (err, info) {
- if (err) throw new Error(err);
- sys.puts("Redis Version is: " + info.redis_version);
- client.close();
- });
-
-- Refer to the many tests in `test/test.js` for many usage examples.
-- Refer to the `examples/` directory for focused examples.
-
-## Installation
-
-This version requires at least `Node.js v0.1.90` and Redis `1.3.8`.
-
-Tested with Node.js `v0.1.95` and `v0.1.96` and Redis `2.1.1` (the current unstable).
-
-You have a number of choices:
-
-- git clone this repo or download a tarball and simply copy `lib/redis-client.js` into your project
-- use git submodule
-- use the [Kiwi](http://github.com/visionmedia/kiwi) package manager for Node.js
-
-Please let me know if the package manager "seeds" and/or metadata have issues.
-Installation via Kiwi or NPM at this point isn't really possible since this repo
-depends on a unreleased version of Node.js.
-
-## Running the tests
-
-A good way to learn about this client is to read the test code.
-
-To run the tests, install and run redis on the localhost on port 6379 (defaults).
-Then run `node test/test.js [-v|-q]` where `-v` is for "verbose" and `-q` is for "quiet".
-
- $ node test/test.js
- ..................................................................
- ...........................++++++++++++++++++++++++++++++++++++
-
- [INFO] All tests have passed.
-
-If you see something like "PSUBSCRIBE: unknown command" then it is time to upgrade
-your Redis installation.
-
-## Documentation
-
-There is a method per Redis command. E.g. `SETNX` becomes `client.setnx`.
-
-For example, the Redis command [INCRBY](http://code.google.com/p/redis/wiki/IncrCommand)
-is specified as `INCRBY key integer`. Also, the INCRBY spec says that the reply will
-be "... the new value of key after the increment or decrement."
-
-This translates to the following client code which increments key 'foo' by 42. If
-the value at key 'foo' was 0 or non-existent, 'newValue' will take value 42 when
-the callback function is called.
-
- client.incrby('foo', 42, function (err, newValue) {
- // ...
- });
-
-This can get [a little wacky](http://github.com/fictorial/redis-node-client/blob/master/test/test.js#L1093-1097).
-I'm open to suggestions for improvement here.
-
-Note: for PUBSUB, you should use `subscribeTo` and `unsubscribeFrom` instead of the generated
-methods for Redis' `SUBSCRIBE` and `UNSUBSCRIBE` commands. See [this](http://github.com/fictorial/redis-node-client/blob/master/lib/redis-client.js#L682-694)
-and [this](http://github.com/fictorial/redis-node-client/blob/master/examples/subscriber.js#L14).
-
-## Notes
-
-All commands/requests use the Redis *multi-bulk request* format which will be
-the only accepted request protocol come Redis 2.0.
-
View
23 support/redis/TODO.md
@@ -1,23 +0,0 @@
-## Soon
-
-- Support MULTI/EXEC/DISCARD. This is a little tricky given that its
- referred to as a "transaction" (but it's really a macro). The API
- must clearly define what the hell is going on in error cases, etc.
- Also, we'll need to add support for nested multi-bulk replies. The
- reply parser, while it handles non-bulk replies inside a multi-bulk
- reply, does not handle multi-bulk replies inside multi-bulk replies.
- This is required for MULTI/EXEC.
-
-- WATCH support
-
-- Now that Node.js has UDP support and Redis 2.0 will have UDP support,
- I suppose we should add, you know, UDP support here.
-
-## Later
-
-- Provide wrapper around the pretty-raw sort method?
-
-## Maybe
-
-- Add support for consistent hashing ala redis-rb and txRedisAPI
-- Add a higher-level interface similar to Ohm (Ruby)
View
32 support/redis/examples/README.md
@@ -1,32 +0,0 @@
-## Some examples.
-
-Note: a large number of usage examples can be found in `../test/test.js`.
-
-## Publisher-Subscriber (PUBSUB)
-
-In one terminal:
-
- $ ./publisher.js
- Published message to no one.
- Published message to no one.
- Published message to no one.
- Published message to 1 subscriber(s). <-- Started the subscriber.
- Published message to 1 subscriber(s).
- Published message to 1 subscriber(s).
- Published message to 1 subscriber(s).
- Published message to no one. <-- Killed (^C) the subscriber.
- ^C
-
-In another terminal:
-
- $ ./subscriber.js
- waiting for messages...
- [channel-6702921148389578]: The time is Fri Apr 02 2010 16:52:19 GMT-0400 (EDT)
- [channel-9212789069861174]: The time is Fri Apr 02 2010 16:52:20 GMT-0400 (EDT)
- [channel-30327219143509865]: The time is Fri Apr 02 2010 16:52:21 GMT-0400 (EDT)
- [channel-35810230672359467]: The time is Fri Apr 02 2010 16:52:22 GMT-0400 (EDT)
- [channel-5208229701966047]: The time is Fri Apr 02 2010 16:52:23 GMT-0400 (EDT)
- [channel-26559297926723957]: The time is Fri Apr 02 2010 16:52:24 GMT-0400 (EDT)
- [channel-9280104916542768]: The time is Fri Apr 02 2010 16:52:25 GMT-0400 (EDT)
- ^C
-
View
21 support/redis/examples/publisher.js
@@ -1,21 +0,0 @@
-#!/usr/bin/env node
-
-// This script plays the role of publisher.
-
-var
- sys = require("sys"),
- client = require("../lib/redis-client").createClient();
-
-// Publish a message once a second to a random channel.
-
-setInterval(function () {
- var
- channelName = "channel-" + Math.random().toString().substr(2),
- payload = "The time is " + (new Date());
-
- client.publish(channelName, payload,
- function (err, reply) {
- sys.puts("Published message to " +
- (reply === 0 ? "no one" : (reply + " subscriber(s).")));
- });
-}, 1000);
View
7 support/redis/examples/redis-version.js
@@ -1,7 +0,0 @@
-var sys = require("sys");
-var client = require("../lib/redis-client").createClient();
-client.info(function (err, info) {
- if (err) throw new Error(err);
- sys.puts("Redis Version is: " + info.redis_version);
- client.close();
-});
View
19 support/redis/examples/subscriber.js
@@ -1,19 +0,0 @@
-#!/usr/bin/env node
-
-// This script plays the role of listener/subscriber/consumer
-// to **all** channels/classes.
-
-var
- sys = require("sys"),
- client = require("../lib/redis-client").createClient();
-
-sys.puts("waiting for messages...");
-
-client.subscribeTo("*",
- function (channel, message, subscriptionPattern) {
- var output = "[" + channel;
- if (subscriptionPattern)
- output += " (from pattern '" + subscriptionPattern + "')";
- output += "]: " + message;
- sys.puts(output);
- });
View
16 support/redis/examples/using-kiwi.js
@@ -1,16 +0,0 @@
-// Kiwi is a package manager for Node.js
-// http://wiki.github.com/visionmedia/kiwi/getting-started
-//
-// $ kiwi install redis-client
-
-var sys = require("sys"),
- kiwi = require("kiwi"),
- client = kiwi.require("redis-client").createClient();
-
-client.stream.addListener("connect", function () {
- client.info(function (err, info) {
- if (err) throw new Error(err);
- sys.puts("Redis Version is: " + info.redis_version);
- client.close();
- });
-});
View
917 support/redis/lib/redis-client.js
@@ -1,917 +0,0 @@
-/*
-
-© 2010 by Fictorial LLC
-
-Permission is hereby granted, free of charge, to any person obtaining a copy
-of this software and associated documentation files (the "Software"), to deal
-in the Software without restriction, including without limitation the rights
-to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
-copies of the Software, and to permit persons to whom the Software is
-furnished to do so, subject to the following conditions:
-
-The above copyright notice and this permission notice shall be included in
-all copies or substantial portions of the Software.
-
-THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
-IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
-FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
-AUTHORS OR COPYRIGHT 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.
-
-*/
-
-// To add support for new commands, edit the array called "commands" at the
-// bottom of this file.
-
-// Set this to true to aid in debugging wire protocol input/output,
-// parsing methods, etc.
-
-exports.debugMode = false;
-
-var net = require("net"),
- sys = require("sys"),
- Buffer = require('buffer').Buffer,
- events = require('events'),
-
- CRLF = "\r\n",
- CRLF_LEN = 2,
-
- PLUS = exports.PLUS = 0x2B, // +
- MINUS = exports.MINUS = 0x2D, // -
- DOLLAR = exports.DOLLAR = 0x24, // $
- STAR = exports.STAR = 0x2A, // *
- COLON = exports.COLON = 0x3A, // :
- CR = exports.CR = 0x0D, // \r
- LF = exports.LF = 0x0A, // \n
-
- NONE = exports.NONE = "NONE",
- BULK = exports.BULK = "BULK",
- MULTIBULK = exports.MULTIBULK = "MULTIBULK",
- INLINE = exports.INLINE = "INLINE",
- INTEGER = exports.INTEGER = "INTEGER",
- ERROR = exports.ERROR = "ERROR";
-
-exports.DEFAULT_HOST = '127.0.0.1';
-exports.DEFAULT_PORT = 6379;
-
-exports.COMMAND_ORPHANED_ERROR = "connection lost before reply received";
-exports.NO_CONNECTION_ERROR = "failed to establish a connection to Redis";
-
-function debugFilter(buffer, len) {
- // Redis is binary-safe but assume for debug display that
- // the encoding of textual data is UTF-8.
-
- var filtered = buffer.utf8Slice(0, len || buffer.length);
-
- filtered = filtered.replace(/\r\n/g, '<CRLF>');
- filtered = filtered.replace(/\r/g, '<CR>');
- filtered = filtered.replace(/\n/g, '<LF>');
-
- return filtered;
-}
-
-// A fully interruptable, binary-safe Redis reply parser.
-// 'callback' is called with each reply parsed in 'feed'.
-// 'thisArg' is the "thisArg" for the callback "call".
-
-function ReplyParser(callback, thisArg) {
- this.onReply = callback;
- this.thisArg = thisArg;
- this.clearState();
- this.clearMultiBulkState();
-}
-
-exports.ReplyParser = ReplyParser;
-
-ReplyParser.prototype.clearState = function () {
- this.type = NONE;
- this.bulkLengthExpected = null;
- this.valueBufferLen = 0;
- this.skip = 0;
- this.valueBuffer = new Buffer(4096);
-};
-
-ReplyParser.prototype.clearMultiBulkState = function () {
- this.multibulkReplies = null;
- this.multibulkRepliesExpected = null;
-};
-
-ReplyParser.prototype.feed = function (inbound) {
- for (var i=0; i < inbound.length; ++i) {
- if (this.skip > 0) {
- this.skip--;
- continue;
- }
-
- var typeBefore = this.type;
-
- if (this.type === NONE) {
- switch (inbound[i]) {
- case DOLLAR: this.type = BULK; break;
- case STAR: this.type = MULTIBULK; break;
- case COLON: this.type = INTEGER; break;
- case PLUS: this.type = INLINE; break;
- case MINUS: this.type = ERROR; break;
- }
- }
-
- // Just a state transition on '*', '+', etc.?
-
- if (typeBefore != this.type)
- continue;
-
- // If the reply is a part of a multi-bulk reply. Save it. If we have
- // received all the expected replies of a multi-bulk reply, then
- // callback. If the reply is not part of a multi-bulk. Call back
- // immediately.
-
- var self = this;
-
- var maybeCallbackWithReply = function (reply) {
- if (self.multibulkReplies != null) {
- self.multibulkReplies.push(reply);
- if (--self.multibulkRepliesExpected == 0) {
- self.onReply.call(self.thisArg, {
- type: MULTIBULK,
- value: self.multibulkReplies
- });
- self.clearMultiBulkState();
- }
- } else {
- self.onReply.call(self.thisArg, reply);
- }
- self.clearState();
- self.skip = 1; // Skip LF
- };
-
- switch (inbound[i]) {
- case CR:
- switch (this.type) {
- case INLINE:
- case ERROR:
- // CR denotes end of the inline/error value.
- // +OK\r\n
- // ^
-
- var inlineBuf = new Buffer(this.valueBufferLen);
- this.valueBuffer.copy(inlineBuf, 0, 0, this.valueBufferLen);
- maybeCallbackWithReply({ type:this.type, value:inlineBuf });
- break;
-
- case INTEGER:
- // CR denotes the end of the integer value.
- // :42\r\n
- // ^
-
- var n = parseInt(this.valueBuffer.asciiSlice(0, this.valueBufferLen), 10);
- maybeCallbackWithReply({ type:INTEGER, value:n });
- break;
-
- case BULK:
- if (this.bulkLengthExpected == null) {
- // CR denotes end of first line of a bulk reply,
- // which is the length of the bulk reply value.
- // $5\r\nhello\r\n
- // ^
-
- var bulkLengthExpected =
- parseInt(this.valueBuffer.asciiSlice(0, this.valueBufferLen), 10);
-
- if (bulkLengthExpected <= 0) {
- maybeCallbackWithReply({ type:BULK, value:null });
- } else {
- this.clearState();
-
- this.bulkLengthExpected = bulkLengthExpected;
- this.type = BULK;
- this.skip = 1; // skip LF
- }
- } else if (this.valueBufferLen == this.bulkLengthExpected) {
- // CR denotes end of the bulk reply value.
- // $5\r\nhello\r\n
- // ^
-
- var bulkBuf = new Buffer(this.valueBufferLen);
- this.valueBuffer.copy(bulkBuf, 0, 0, this.valueBufferLen);
- maybeCallbackWithReply({ type:BULK, value:bulkBuf });
- } else {
- // CR is just an embedded CR and has nothing to do
- // with the reply specification.
- // $11\r\nhello\rworld\r\n
- // ^
-
- this.valueBuffer[this.valueBufferLen++] = inbound[i];
- }
- break;
-
- case MULTIBULK:
- // Parse the count which is the number of expected replies
- // in the multi-bulk reply.
- // *2\r\n$5\r\nhello\r\n$5\r\nworld\r\n
- // ^
-
- var multibulkRepliesExpected =
- parseInt(this.valueBuffer.asciiSlice(0, this.valueBufferLen), 10);
-
- if (multibulkRepliesExpected <= 0) {
- maybeCallbackWithReply({ type:MULTIBULK, value:null });
- } else {
- this.clearState();
- this.skip = 1; // skip LF
- this.multibulkReplies = [];
- this.multibulkRepliesExpected = multibulkRepliesExpected;
- }
- break;
- }
- break;
-
- default:
- this.valueBuffer[this.valueBufferLen++] = inbound[i];
- break;
- }
-
- // If the current value buffer is too big, create a new buffer, copy in
- // the old buffer, and replace the old buffer with the new buffer.
-
- if (this.valueBufferLen === this.valueBuffer.length) {
- var newBuffer = new Buffer(this.valueBuffer.length * 2);
- this.valueBuffer.copy(newBuffer, 0, 0);
- this.valueBuffer = newBuffer;
- }
- }
-};
-
-/**
- * Emits:
- *
- * - 'connected' when connected (or on a reconnection, reconnected).
- * - 'reconnecting' when about to retry to connect to Redis.
- * - 'reconnected' when connected after a reconnection was established.
- * - 'noconnection' when a connection (or reconnection) cannot be established.
- * - 'drained' when no submitted commands are expecting a reply from Redis.
- *
- * Options:
- *
- * - maxReconnectionAttempts (default: 10)
- */
-
-function Client(stream, options) {
- events.EventEmitter.call(this);
-
- this.stream = stream;
- this.originalCommands = [];
- this.queuedOriginalCommands = [];
- this.queuedRequestBuffers = [];
- this.channelCallbacks = {};
- this.requestBuffer = new Buffer(512);
- this.replyParser = new ReplyParser(this.onReply_, this);
- this.reconnectionTimer = null;
- this.maxReconnectionAttempts = 10;
- this.reconnectionAttempts = 0;
- this.reconnectionDelay = 500; // doubles, so starts at 1s delay
- this.connectionsMade = 0;
-
- if (options !== undefined)
- this.maxReconnectionAttempts = Math.abs(options.maxReconnectionAttempts || 10);
-
- var client = this;
-
- stream.addListener("connect", function () {
- if (exports.debugMode)
- sys.debug("[CONNECT]");
-
- stream.setNoDelay();
- stream.setTimeout(0);
-
- client.reconnectionAttempts = 0;
- client.reconnectionDelay = 500;
- if (client.reconnectionTimer) {
- clearTimeout(client.reconnectionTimer);
- client.reconnectionTimer = null;
- }
-
- var eventName = client.connectionsMade == 0
- ? 'connected'
- : 'reconnected';
-
- client.connectionsMade++;
- client.expectingClose = false;
-
- // If this a reconnection and there were commands submitted, then they
- // are gone! We cannot say with any confidence which were processed by
- // Redis; perhaps some were processed but we never got the reply, or
- // perhaps all were processed but Redis is configured with less than
- // 100% durable writes, etc.
- //
- // We punt to the user by calling their callback with an I/O error.
- // However, we provide enough information to allow the user to retry
- // the interrupted operation. We are certainly not retrying anything
- // for them as it is too dangerous and application-specific.
-
- if (client.connectionsMade > 1 && client.originalCommands.length > 0) {
- if (exports.debug) {
- sys.debug("[RECONNECTION] some commands orphaned (" +
- client.originalCommands.length + "). notifying...");
- }
-
- client.callbackOrphanedCommandsWithError();
- }
-
- client.originalCommands = [];
- client.flushQueuedCommands();
-
- client.emit(eventName, client);
- });
-
- stream.addListener('error', function (e) {
- if (exports.debugMode)
- sys.debug("[ERROR] Connection to redis encountered an error: " + e);
- });
-
- stream.addListener("data", function (buffer) {
- if (exports.debugMode)
- sys.debug("[RECV] " + debugFilter(buffer));
-
- client.replyParser.feed(buffer);
- });
-
- stream.addListener("error", function (e) {
- if (exports.debugMode)
- sys.debug('[ERROR] ' + e);
- client.replyParser.clearState();
- client.maybeReconnect();
- throw e;
- });
-
- stream.addListener("end", function () {
- if (exports.debugMode && client.originalCommands.length > 0) {
- sys.debug("Connection to redis closed with " +
- client.originalCommands.length +
- " commands pending replies that will never arrive!");
- }
-
- stream.end();
- });
-
- stream.addListener("close", function (hadError) {
- if (exports.debugMode)
- sys.debug("[NO CONNECTION]");
-
- client.maybeReconnect();
- });
-}
-
-sys.inherits(Client, events.EventEmitter);
-
-exports.Client = Client;
-
-exports.createClient = function (port, host, options) {
- var port = port || exports.DEFAULT_PORT;
- var host = host || exports.DEFAULT_HOST;
-
- var client = new Client(net.createConnection(port, host), options);
-
- client.port = port;
- client.host = host;
-
- return client;
-};
-
-Client.prototype.close = function () {
- this.expectingClose = true;
- this.stream.end();
-};
-
-Client.prototype.onReply_ = function (reply) {
- this.flushQueuedCommands();
-
- if (this.handlePublishedMessage_(reply))
- return;
-
- var originalCommand = this.originalCommands.shift();
- var callback = originalCommand[originalCommand.length - 1];
-
- // Callbacks expect (err, reply) as args.
-
- if (typeof callback == "function") {
- if (reply.type == ERROR) {
- callback(reply.value.utf8Slice(0, reply.value.length), null);
- } else {
- callback(null, maybeConvertReplyValue(originalCommand[0], reply));
- }
- }
-
- if (this.originalCommands.length == 0)
- this.emit('drained', this);
-};
-
-Client.prototype.handlePublishedMessage_ = function (reply) {
- // We're looking for a multibulk resembling
- // ["message", "channelName", messageBuffer]; or
- // ["pmessage", "matchingPattern", "channelName", messageBuffer]
- // The latter is sent when the client subscribed to a channel by a pattern;
- // the former when subscribed to a channel by name.
- // If the client subscribes by name -and- by pattern and there's some
- // overlap, the client -will- receive multiple p/message notifications.
-
- if (reply.type != MULTIBULK || !(reply.value instanceof Array))
- return false;
-
- var isMessage = (reply.value.length == 3 &&
- reply.value[0].value.length == 7 &&
- reply.value[0].value.asciiSlice(0, 7) == 'message');
-
- var isPMessage = (reply.value.length == 4 &&
- reply.value[0].value.length == 8 &&
- reply.value[0].value.asciiSlice(0, 8) == 'pmessage');
-
- if (!isMessage && !isPMessage)
- return false;
-
- // This is tricky. We are returning true even though there
- // might not be any callback called! This may happen when a
- // caller subscribes then unsubscribes while a published
- // message is in transit to us. When the message arrives, no
- // one is there to consume it. In essence, as long as the
- // reply type is a published message (see above), then we've
- // "handled" the reply.
-
- if (Object.getOwnPropertyNames(this.channelCallbacks).length == 0)
- return true;
-
- var channelName, channelPattern, channelCallback, payload;
-
- if (isMessage) {
- channelName = reply.value[1].value;
- channelCallback = this.channelCallbacks[channelName];
- payload = reply.value[2].value;
- } else if (isPMessage) {
- channelPattern = reply.value[1].value;
- channelName = reply.value[2].value;
- channelCallback = this.channelCallbacks[channelPattern];
- payload = reply.value[3].value;
- } else {
- return false;
- }
-
- if (typeof channelCallback == "function") {
- channelCallback(channelName, payload, channelPattern);
- return true;
- }
-
- return false;
-}
-
-function maybeAsNumber(str) {
- var value = parseInt(str, 10);
-
- if (isNaN(value))
- value = parseFloat(str);
-
- if (isNaN(value))
- return str;
-
- return value;
-}
-
-function maybeConvertReplyValue(commandName, reply) {
- if (reply.value === null)
- return null;
-
- // Redis' INFO command returns a BULK reply of the form:
- // "redis_version:1.3.8
- // arch_bits:64
- // multiplexing_api:kqueue
- // process_id:11604
- // ..."
- //
- // We convert that to a JS object like:
- // { redis_version: '1.3.8'
- // , arch_bits: '64'
- // , multiplexing_api: 'kqueue'
- // , process_id: '11604'
- // , ... }
-
- if (commandName === 'info' && reply.type === BULK) {
- var info = {};
- reply.value.asciiSlice(0, reply.value.length).split(/\r\n/g)
- .forEach(function (line) {
- var parts = line.split(':');
- if (parts.length === 2)
- info[parts[0]] = parts[1];
- });
- return info;
- }
-
- // HGETALL returns a MULTIBULK where each consecutive reply-pair
- // is a key and value for the Redis HASH. We convert this into
- // a JS object.
-
- if (commandName === 'hgetall' &&
- reply.type === MULTIBULK &&
- reply.value.length % 2 === 0) {
-
- var hash = {};
- for (var i=0; i<reply.value.length; i += 2)
- hash[reply.value[i].value] = reply.value[i + 1].value;
- return hash;
- }
-
- // Redis returns "+OK\r\n" to signify success.
- // We convert this into a JS boolean with value true.
-
- if (reply.type === INLINE && reply.value.asciiSlice(0,2) === 'OK')
- return true;
-
- // ZSCORE returns a string representation of a floating point number.
- // We convert this into a JS number.
-
- if (commandName === "zscore")
- return maybeAsNumber(reply.value);
-
- // Multibulk replies are returned from our reply parser as an
- // array like: [ {type:BULK, value:"foo"}, {type:BULK, value:"bar"} ]
- // But, end-users want the value and don't care about the
- // Redis protocol reply types. We here extract the value from each
- // object in the multi-bulk array.
-
- if (reply.type === MULTIBULK)
- return reply.value.map(function (element) { return element.value; });
-
- // Otherwise, we have no conversions to offer.
-
- return reply.value;
-}
-
-exports.maybeConvertReplyValue_ = maybeConvertReplyValue;
-
-var commands = [
- "append",
- "auth",
- "bgsave",
- "blpop",
- "brpop",
- "dbsize",
- "decr",
- "decrby",
- "del",
- "exists",
- "expire",
- "expireat",
- "flushall",
- "flushdb",
- "get",
- "getset",
- "hdel",
- "hexists",
- "hget",
- "hgetall",
- "hincrby",
- "hkeys",
- "hlen",
- "hmget",
- "hmset",
- "hset",
- "hvals",
- "incr",
- "incrby",
- "info",
- "keys",
- "lastsave",
- "len",
- "lindex",
- "llen",
- "lpop",
- "lpush",
- "lrange",
- "lrem",
- "lset",
- "ltrim",
- "mget",
- "move",
- "mset",
- "msetnx",
- "psubscribe",
- "publish",
- "punsubscribe",
- "randomkey",
- "rename",
- "renamenx",
- "rpop",
- "rpoplpush",
- "rpush",
- "sadd",
- "save",
- "scard",
- "sdiff",
- "sdiffstore",
- "select",
- "set",
- "setex",
- "setnx",
- "shutdown",
- "sinter",
- "sinterstore",
- "sismember",
- "smembers",
- "smove",
- "sort",
- "spop",
- "srandmember",
- "srem",
- "subscribe",
- "sunion",
- "sunionstore",
- "ttl",
- "type",
- "unsubscribe",
- "zadd",
- "zcard",
- "zcount",
- "zincrby",
- "zinter",
- "zrange",
- "zrangebyscore",
- "zrank",
- "zrem",
- "zrembyrank",
- "zremrangebyrank",
- "zremrangebyscore",
- "zrevrange",
- "zrevrank",
- "zscore",
- "zunion",
-];
-
-// For internal use but maybe useful in rare cases or when the client command
-// set is not 100% up to date with Redis' latest commands.
-// client.sendCommand('GET', 'foo', function (err, value) {...});
-//
-// arguments[0] = commandName
-// arguments[1..N-2] = Redis command arguments
-// arguments[N-1] = callback function
-
-Client.prototype.sendCommand = function () {
- var originalCommand = Array.prototype.slice.call(arguments);
-
- // If this client has given up trying to connect/reconnect to Redis,
- // just call the errback (if any). Regardless, don't enqueue the command.
-
- if (this.noConnection) {
- if (arguments.length > 0 && typeof arguments[arguments.length - 1] == 'function')
- arguments[arguments.length - 1](this.makeErrorForCommand(originalCommand, exports.NO_CONNECTION_ERROR));
- return;
- }
-
- this.flushQueuedCommands();
-
- var commandName = arguments[0].toLowerCase();
-
- // Invariant: number of queued callbacks == number of commands sent to
- // Redis whose replies have not yet been received and processed. Thus,
- // if no callback was given, we create a dummy callback.
-
- var argCount = arguments.length;
- if (typeof arguments[argCount - 1] == 'function')
- --argCount;
-
- // All requests are formatted as multi-bulk.
- // The first line of a multi-bulk request is "*<number of parts to follow>\r\n".
- // Next is: "$<length of the command name>\r\n<command name>\r\n".
-
- // Write the request as we go into a request Buffer. Recall that buffers
- // are fixed length. We thus guess at how much space is needed. If we
- // need to grow beyond this, we create a new buffer, copy the old one, and
- // continue. Once we're ready to write the buffer, we use a 0-copy slice
- // to send just that which we've written to the buffer.
- //
- // We reuse the buffer after each request. When the buffer "grows" to
- // accomodate a request, it stays that size until it needs to grown again,
- // which may of course be never.
-
- var offset = this.requestBuffer.utf8Write('*' + argCount.toString() + CRLF +
- '$' + commandName.length + CRLF +
- commandName + CRLF, 0);
-
- var self = this;
-
- function ensureSpaceFor(atLeast) {
- var currentLength = self.requestBuffer.length;
-
- if (offset + atLeast > currentLength) {
- // If we know how much space we need, use that + 10%.
- // Else double the size of the buffer.
-
- var bufferLength = Math.max(currentLength * 2, atLeast * 1.1);
- var newBuffer = new Buffer(Math.round(bufferLength));
- self.requestBuffer.copy(newBuffer, 0, 0, offset); // target, targetStart, srcStart, srcEnd
- self.requestBuffer = newBuffer;
- }
- }
-
- // Serialize the arguments into the request buffer
- // If the request is a Buffer, just copy. Else if
- // the arg has a .toString() method, call it and write
- // it to the request buffer as UTF8.
-
- var extrasLength = 5; // '$', '\r\n', '\r\n'
-
- for (var i=1; i < argCount; ++i) {
- var arg = arguments[i];
- if (arg instanceof Buffer) {
- ensureSpaceFor(arg.length + arg.length.toString().length + extrasLength);
- offset += this.requestBuffer.asciiWrite('$' + arg.length + CRLF, offset);
- offset += arg.copy(this.requestBuffer, offset, 0); // target, targetStart, srcStart
- offset += this.requestBuffer.asciiWrite(CRLF, offset);
- } else if (arg.toString) {
- var asString = arg.toString();
- var serialized = '$' + Buffer.byteLength(asString, "binary") + CRLF + asString + CRLF;
- ensureSpaceFor(Buffer.byteLength(serialized, "binary"));
- offset += this.requestBuffer.binaryWrite(serialized, offset);
- }
- }
-
- // If the stream is writable, write the command. Else enqueue the command
- // for when we first establish a connection or reconnect.
-
- if (this.stream.writable) {
- this.originalCommands.push(originalCommand);
- var outBuffer = new Buffer(offset);
- this.requestBuffer.copy(outBuffer, 0, 0, offset);
- this.stream.write(outBuffer, 'binary');
-
- if (exports.debugMode)
- sys.debug("[SEND] " + debugFilter(this.requestBuffer, offset) +
- " originalCommands = " + this.originalCommands.length);
- } else {
- var toEnqueue = new Buffer(offset);
- this.requestBuffer.copy(toEnqueue, 0, 0, offset); // dst, dstStart, srcStart, srcEnd
- this.queuedRequestBuffers.push(toEnqueue);
- this.queuedOriginalCommands.push(originalCommand);
-
- if (exports.debugMode) {
- sys.debug("[ENQUEUE] Not connected. Request queued. There are " +
- this.queuedRequestBuffers.length + " requests queued.");
- }
- }
-};
-
-commands.forEach(function (commandName) {
- Client.prototype[commandName] = function () {
- var args = Array.prototype.slice.call(arguments);
- // [[1,2,3],function(){}] => [1,2,3,function(){}]
- if (args.length > 0 && Array.isArray(args[0]))
- args = args.shift().concat(args);
- args.unshift(commandName);
- this.sendCommand.apply(this, args);
- };
-});
-
-// Send any commands that were queued while we were not connected.
-
-Client.prototype.flushQueuedCommands = function () {
- if (exports.debugMode && this.queuedRequestBuffers.length > 0)
- sys.debug("[FLUSH QUEUE] " + this.queuedRequestBuffers.length +
- " queued request buffers.");
-
- for (var i=0; i<this.queuedRequestBuffers.length && this.stream.writable; ++i) {
- var buffer = this.queuedRequestBuffers.shift();
- this.stream.write(buffer, 'binary');
- this.originalCommands.push(this.queuedOriginalCommands.shift());
-
- if (exports.debugMode)
- sys.debug("[DEQUEUE/SEND] " + debugFilter(buffer) +
- ". queued buffers remaining = " +
- this.queuedRequestBuffers.length);
- }
-};
-
-Client.prototype.makeErrorForCommand = function (command, errorMessage) {
- var err = new Error(errorMessage);
- err.originalCommand = command;
- return err;
-};
-
-Client.prototype.callbackCommandWithError = function (command, errorMessage) {
- var callback = command[command.length - 1];
- if (typeof callback == "function")
- callback(this.makeErrorForCommand(command, errorMessage));
-};
-
-Client.prototype.callbackOrphanedCommandsWithError = function () {
- for (var i=0, n=this.originalCommands.length; i<n; ++i)
- this.callbackCommandWithError(this.originalCommands[i], exports.COMMAND_ORPHANED_ERROR);
- this.originalCommands = [];
-};
-
-Client.prototype.callbackQueuedCommandsWithError = function () {
- for (var i=0, n=this.queuedOriginalCommands.length; i<n; ++i)
- this.callbackCommandWithError(this.queuedOriginalCommands[i], exports.NO_CONNECTION_ERROR);
- this.queuedOriginalCommands = [];
- this.queuedRequestBuffers = [];
-};
-
-Client.prototype.giveupConnectionAttempts = function () {
- this.callbackOrphanedCommandsWithError();
- this.callbackQueuedCommandsWithError();
- this.noConnection = true;
- this.emit('noconnection', this);
-};
-
-Client.prototype.maybeReconnect = function () {
- if (this.stream.writable && this.stream.readable)
- return;
-
- if (this.expectingClose)
- return;
-
- // Do not reconnect on first connection failure.
- // Else try to reconnect if we're asked to.
-
- if (this.connectionsMade == 0) {
- this.giveupConnectionAttempts();
- } else if (this.maxReconnectionAttempts > 0) {
- if (this.reconnectionAttempts++ >= this.maxReconnectionAttempts) {
- this.giveupConnectionAttempts();
- } else {
- this.reconnectionDelay *= 2;
-
- if (exports.debugMode) {
- sys.debug("[RECONNECTING " + this.reconnectionAttempts + "/" +
- this.maxReconnectionAttempts + "]");
-
- sys.debug("[WAIT " + this.reconnectionDelay + " ms]");
- }
-
- var self = this;
-
- this.reconnectionTimer = setTimeout(function () {
- self.emit('reconnecting', self);
- self.stream.connect(self.port, self.host);
- }, this.reconnectionDelay);
- }
- }
-};
-
-// Wraps 'subscribe' and 'psubscribe' methods to manage a single
-// callback function per subscribed channel name/pattern.
-//
-// 'nameOrPattern' is a channel name like "hello" or a pattern like
-// "h*llo", "h?llo", or "h[ae]llo".
-//
-// 'callback' is a function that is called back with 2 args:
-// channel name/pattern and message payload.
-//
-// Note: You are not permitted to do anything but subscribe to
-// additional channels or unsubscribe from subscribed channels
-// when there are >= 1 subscriptions active. Should you need to
-// issue other commands, use a second client instance.
-
-Client.prototype.subscribeTo = function (nameOrPattern, callback) {
- if (typeof this.channelCallbacks[nameOrPattern] === 'function')
- return;
-
- if (typeof(callback) !== 'function')
- throw new Error("requires a callback function");
-
- this.channelCallbacks[nameOrPattern] = callback;
-
- var method = nameOrPattern.match(/[\*\?\[]/)
- ? "psubscribe"
- : "subscribe";
-
- this[method](nameOrPattern);
-};
-
-Client.prototype.unsubscribeFrom = function (nameOrPattern) {
- if (typeof this.channelCallbacks[nameOrPattern] === 'undefined')
- return;
-
- delete this.channelCallbacks[nameOrPattern];
-
- var method = nameOrPattern.match(/[\*\?\[]/)
- ? "punsubscribe"
- : "unsubscribe";
-
- this[method](nameOrPattern);
-};
-
-// Multi-bulk replies return an array of other replies. Perhaps all you care
-// about is the representation of such buffers as UTF-8 encoded strings? Use
-// this to convert each such Buffer to a (UTF-8 encoded) String in-place.
-
-exports.convertMultiBulkBuffersToUTF8Strings = function (o) {
- if (o instanceof Array) {
- for (var i=0; i<o.length; ++i)
- if (o[i] instanceof Buffer)
- o[i] = o[i].utf8Slice(0, o[i].length);
- } else if (o instanceof Object) {
- var props = Object.getOwnPropertyNames(o);
- for (var i=0; i<props.length; ++i)
- if (o[props[i]] instanceof Buffer)
- o[props[i]] = o[props[i]].utf8Slice(0, o[props[i]].length);
- }
-};
-
View
34 support/redis/package.json
@@ -1,34 +0,0 @@
-{
- "name": "redis-client",
- "description": "Redis client for Node.js",
- "version": "0.3.5",
- "keywords": [ "redis", "node", "client" ],
- "author": "Brian Hammond <brian@fictorial.com>",
- "contributors": [
- { "name": "Brian Hammond", "web": "http://fictorial.com" },
- { "name": "Brit Gardner", "web": "http://britg.com" },
- { "name": "Corey Donohoe", "web": "http://www.atmos.org" },
- { "name": "Elliott Cable", "web": "http://elliottcable.name/" },
- { "name": "Jake McGraw", "web": "http://jakemcgraw.com" },
- { "name": "James Herdman", "web": "http://jherdman.github.com" },
- { "name": "Justin Tulloss", "web": "http://justin.harmonize.fm/" },
- { "name": "Nikhil", "web": "http://kodeclutz.blogspot.com" },
- { "name": "technoweenie (rick)", "web": "http://techno-weenie.net" },
- { "name": "Donovan Hide", "web": "http://availableimagination.com" },
- { "name": "Philip Hofstetter", "web": "http://www.gnegg.ch/" },
- { "name": "Chris Winberry", "web": "http://tautologistics.com/" },
- { "name": "Brian McKinney", "web": "http://twitter.com/tritonrc" },
- { "name": "Alberto Piai", "web": "http://github.com/brainlock" }
- ],
- "licenses": [ "MIT" ],
- "repositories": {
- "type": "git",
- "url": "http://github.com/fictorial/redis-node-client.git"
- },
- "bugs": { "web": "http://github.com/fictorial/redis-node-client/issues" },
- "directories": { "lib": "./lib", "test": "./test" },
- "os": [ "linux", "darwin" ],
- "dependencies": {},
- "engines": { "node": ">0.1.90" },
- "main": "./redis-client"
-}
View
6 support/redis/seed.yml
@@ -1,6 +0,0 @@
----
- name: redis-client
- description: A Redis client
- tags: redis
- version: 0.3.5
-
View
BIN support/redis/test/sample.png
Deleted file not rendered
View
1,896 support/redis/test/test.js
@@ -1,1896 +0,0 @@
-#!/usr/bin/env node
-
-/*
- Redis client for Node.js -- tests
-
- Copyright (C) 2010 Fictorial LLC.
-
- Permission is hereby granted, free of charge, to any person obtaining
- a copy of this software and associated documentation files (the
- "Software"), to deal in the Software without restriction, including without
- limitation the rights to use, copy, modify, merge, publish, distribute,
- sublicense, and/or sell copies of the Software, and to permit persons to
- whom the Software is furnished to do so, subject to the following
- conditions:
-
- The above copyright notice and this permission notice shall be included in
- all copies or substantial portions of the Software.
-
- THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
- IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
- FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
- AUTHORS OR COPYRIGHT 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.
-*/
-
-// http://github.com/fictorial/redis-node-client
-// Brian Hammond <brian at fictorial dot com>
-
-// NOTE: this test suite uses databases 14 and 15 for test purposes!
-// Make sure your Redis instance has at least this many databases; the default has 16.
-// These databases will be flushed these databases at the start of each test run.
-// If you want to use a different database number, update TEST_DB_NUMBER* below.
-
-// NOTE: each test is responsible for configuring the dataset needed to
-// run that test. There are no "fixtures" or similar.
-
-var TEST_DB_NUMBER = 15,
- TEST_DB_NUMBER_FOR_MOVE = 14;
-
-var sys = require("sys"),
- assert = require("assert"),
- fs = require("fs"),
- redisclient = require("../lib/redis-client"),
- Buffer = require("buffer").Buffer;
-
-var verbose = process.argv.indexOf("-v") != -1;
-var quiet = process.argv.indexOf("-q") != -1;
-
-redisclient.debugMode = verbose && !quiet;
-
-function log(level, msg) {
- var colorsForLevel = { info:37, warn:33, error:31 };
- sys.error("\033[" + colorsForLevel[level || 'info'] + "m[" +
- level.toUpperCase() + "] " + msg + "\033[0m");
-}
-
-function showContext(context) {
- sys.error("\n");
- log('error', context + " FAILED!");
- sys.error("");
-}
-
-// These wrappers around the assert module exist because we generate functions
-// to test for expected conditions which lose context, and assert's functions
-// use the 'message' in place of the failed condition.
-
-function checkEqual(actual, expected, context) {
- try {
- assert.equal(actual, expected);
- } catch (e) {
- showContext(context);
- throw e;
- }
-}
-
-function check(what, context) {
- try {
- assert.ok(what);
- } catch (e) {
- showContext(context);
- throw e;
- }
-}
-
-function checkDeepEqual(actual, expected, context) {
- try {
- assert.deepEqual(actual, expected);
- } catch (e) {
- showContext(context);
- throw e;
- }
-}
-
-// Redis' protocol returns +OK for some operations to mean "true" or "success".
-// The client converts this into a boolean with value true.
-
-function expectOK(context) {
- return function (err, truthiness) {
- if (err) assert.fail(err, context);
- checkEqual(typeof(truthiness), 'boolean', context);
- checkEqual(truthiness, true, context);
- };
-}
-
-function maybeAsNumber(str) {
- var value = parseInt(str, 10);
-
- if (isNaN(value))
- value = parseFloat(str);
-
- if (isNaN(value))
- return str;
-
- return value;
-}
-
-function expectNumber(expectedValue, context) {
- return function (err, reply) {
- if (err) assert.fail(err, context);
- var value = maybeAsNumber(reply);
- checkEqual(value, expectedValue, context);
- };
-}
-
-function clearTestDatabasesBeforeEachTest() {
- client.select(TEST_DB_NUMBER_FOR_MOVE, expectOK("select"));
- client.flushdb(expectOK("flushdb"));
-
- client.select(TEST_DB_NUMBER, expectOK("select"));
- client.flushdb(expectOK("flushdb"));
-}
-
-function bufferFromString(str, encoding) {
- var enc = encoding || 'utf8';
- var buf = new Buffer(str.length);
- switch (enc) {
- case 'utf8': buf.utf8Write(str); break;
- case 'ascii': buf.asciiWrite(str); break;
- case 'binary': buf.binaryWrite(str); break;
- default:
- assert.fail("unknown encoding: " + encoding);
- }
- return buf;
-}
-
-function testParseBulkReply() {
- var a = new redisclient.ReplyParser(function (reply) {
- checkEqual(reply.type, redisclient.BULK, "testParseBulkReply a-0");
- check(reply.value instanceof Buffer, "testParseBulkReply a-1");
- checkEqual(reply.value.utf8Slice(0, reply.value.length), "FOOBAR", "testParseBulkReply a-2");
- });
- a.feed(bufferFromString("$6\r\nFOOBAR\r\n"));
-
- var b = new redisclient.ReplyParser(function (reply) {
- checkEqual(reply.type, redisclient.BULK, "testParseBulkReply b-0");
- checkEqual(reply.value, null, "testParseBulkReply b-1");
- });
- b.feed(bufferFromString("$-1\r\n"));
-}
-
-Buffer.prototype.toString=function() {
- return this.utf8Slice(0,this.length);
-}
-
-function testParseMultiBulkReply() {
- var a = new redisclient.ReplyParser(function (reply) {
- checkEqual(reply.type, redisclient.MULTIBULK, "testParseMultiBulkReply a-0");
- check(reply.value instanceof Array, "testParseMultiBulkReply a-1");
- checkEqual(reply.value.length, 4, "testParseMultiBulkReply a-2");
- check(reply.value[0].type === redisclient.BULK, "testParseMultiBulkReply a-3");
- check(reply.value[1].type === redisclient.BULK, "testParseMultiBulkReply a-4");
- check(reply.value[2].type === redisclient.BULK, "testParseMultiBulkReply a-5");
- check(reply.value[3].type === redisclient.BULK, "testParseMultiBulkReply a-6");
- check(reply.value[0].value instanceof Buffer, "testParseMultiBulkReply a-7");
- check(reply.value[1].value instanceof Buffer, "testParseMultiBulkReply a-8");
- check(reply.value[2].value instanceof Buffer, "testParseMultiBulkReply a-9");
- check(reply.value[3].value instanceof Buffer, "testParseMultiBulkReply a-10");
- checkEqual(reply.value[0].value.length, 3, "testParseMultiBulkReply a-11");
- checkEqual(reply.value[1].value.length, 3, "testParseMultiBulkReply a-12");
- checkEqual(reply.value[2].value.length, 5, "testParseMultiBulkReply a-13");
- checkEqual(reply.value[3].value.length, 6, "testParseMultiBulkReply a-14");
- checkEqual(reply.value[0].value.utf8Slice(0, reply.value[0].value.length), 'FOO', "testParseMultiBulkReply a-15");
- checkEqual(reply.value[1].value.utf8Slice(0, reply.value[1].value.length), 'BAR', "testParseMultiBulkReply a-16");
- checkEqual(reply.value[2].value.utf8Slice(0, reply.value[2].value.length), 'HELLO', "testParseMultiBulkReply a-17");
- checkEqual(reply.value[3].value.utf8Slice(0, reply.value[3].value.length), 'WORLD!', "testParseMultiBulkReply a-18");
- });
- a.feed(bufferFromString("*4\r\n$3\r\nFOO\r\n$3\r\nBAR\r\n$5\r\nHELLO\r\n$6\r\nWORLD!\r\n"));
-
- var b = new redisclient.ReplyParser(function (reply) {
- checkEqual(reply.type, redisclient.MULTIBULK, "testParseMultiBulkReply b-0");
- checkEqual(reply.value, null, "testParseMultiBulkReply b-1");
- });
- b.feed(bufferFromString("*-1\r\n"));
-
- var c = new redisclient.ReplyParser(function (reply) {
- checkEqual(reply.type, redisclient.MULTIBULK, "testParseMultiBulkReply c-0");
- check(reply.value instanceof Array, "testParseMultiBulkReply c-1");
- checkEqual(reply.value.length, 3, "testParseMultiBulkReply c-2");
- check(reply.value[0].type === redisclient.BULK, "testParseMultiBulkReply c-3");
- check(reply.value[1].type === redisclient.BULK, "testParseMultiBulkReply c-4");
- check(reply.value[2].type === redisclient.BULK, "testParseMultiBulkReply c-5");
- checkEqual(reply.value[0].value.utf8Slice(0, reply.value[0].value.length), 'FOO', "testParseMultiBulkReply c-6");
- checkEqual(reply.value[1].value, null, "testParseMultiBulkReply c-7");
- checkEqual(reply.value[2].value.utf8Slice(0, reply.value[2].value.length), 'BARZ', "testParseMultiBulkReply c-8");
- });
- c.feed(bufferFromString("*3\r\n$3\r\nFOO\r\n$-1\r\n$4\r\nBARZ\r\n"));
-
- // Test with a multi-bulk reply containing a subreply that's non-bulk
- // but an inline/integer reply instead.
-
- var d = new redisclient.ReplyParser(function (reply) {
- checkEqual(reply.type, redisclient.MULTIBULK, "testParseMultiBulkReply d-0");
- check(reply.value instanceof Array, "testParseMultiBulkReply d-1");
- checkEqual(reply.value.length, 3, "testParseMultiBulkReply d-2");
- check(reply.value[0].type === redisclient.BULK, "testParseMultiBulkReply d-3");
- check(reply.value[1].type === redisclient.BULK, "testParseMultiBulkReply d-4");
- check(reply.value[2].type === redisclient.INTEGER, "testParseMultiBulkReply d-5");
- check(reply.value[0].value instanceof Buffer, "testParseMultiBulkReply d-6");
- check(reply.value[1].value instanceof Buffer, "testParseMultiBulkReply d-7");
- checkEqual(typeof(reply.value[2].value), "number", "testParseMultiBulkReply d-8");
- checkEqual(reply.value[0].value.utf8Slice(0, reply.value[0].value.length), 'subscribe', "testParseMultiBulkReply d-9");
- checkEqual(reply.value[1].value.utf8Slice(0, reply.value[1].value.length), '#redis', "testParseMultiBulkReply d-10");
- checkEqual(reply.value[2].value, 1, "testParseMultiBulkReply d-11");
- });
- d.feed(bufferFromString("*3\r\n$9\r\nsubscribe\r\n$6\r\n#redis\r\n:1\r\n*3\r\n$7\r\nmessage\r\n"));
-
- var e = new redisclient.ReplyParser(function (reply) {
- checkEqual(reply.type, redisclient.MULTIBULK, "testParseMultiBulkReply e-0");
- checkEqual(reply.value, null, "testParseMultiBulkReply e-1");
- });
- e.feed(bufferFromString("*0\r\n"));
-}
-
-function testParseInlineReply() {
- var a = new redisclient.ReplyParser(function (reply) {
- // maybeConvertReplyValue is called by redisclient for non-test calls
- reply.value = redisclient.maybeConvertReplyValue_('N/A', reply);
- checkEqual(reply.type, redisclient.INLINE, "testParseInlineReply a-0");
- checkEqual(typeof(reply.value), 'boolean', "testParseInlineReply a-1");
- checkEqual(reply.value, true, "testParseInlineReply a-2");
- });
- a.feed(bufferFromString("+OK\r\n"));
-
- var b = new redisclient.ReplyParser(function (reply) {
- checkEqual(reply.type, redisclient.INLINE, "testParseInlineReply b-0");
- check(reply.value instanceof Buffer, "testParseInlineReply b-1");
- checkEqual(reply.value.utf8Slice(0, reply.value.length), 'WHATEVER', "testParseInlineReply b-2");
- });
- b.feed(bufferFromString("+WHATEVER\r\n"));
-}
-
-function testParseIntegerReply() {
- var a = new redisclient.ReplyParser(function (reply) {
- checkEqual(reply.type, redisclient.INTEGER, "testParseIntegerReply a-0");
- checkEqual(typeof(reply.value), 'number', "testParseIntegerReply a-1");
- checkEqual(reply.value, -1, "testParseIntegerReply a-2");
- });
- a.feed(bufferFromString(":-1\r\n"));
-
- var b = new redisclient.ReplyParser(function (reply) {
- checkEqual(reply.type, redisclient.INTEGER, "testParseIntegerReply b-0");
- checkEqual(typeof(reply.value), 'number', "testParseIntegerReply b-1");
- checkEqual(reply.value, 1000, "testParseIntegerReply b-2");
- });
- b.feed(bufferFromString(":1000\r\n"));
-}
-
-function testParseErrorReply() {
- var a = new redisclient.ReplyParser(function (reply) {
- checkEqual(reply.type, redisclient.ERROR, "testParseErrorReply c-0");
- check(reply.value instanceof Buffer, "testParseErrorReply c-1");
- checkEqual(reply.value.utf8Slice(0, reply.value.length), "ERR solar flare", "testParseErrorReply c-2");
- });
- a.feed(bufferFromString("-ERR solar flare\r\n"));
-
- var b = new redisclient.ReplyParser(function (reply) {
- checkEqual(reply.type, redisclient.ERROR, "testParseErrorReply b-0");
- check(reply.value instanceof Buffer, "testParseErrorReply b-1");
- checkEqual(reply.value.utf8Slice(0, reply.value.length), "hiccup", "testParseErrorReply b-2");
- });
- b.feed(bufferFromString("-hiccup\r\n"));
-}
-
-function testAUTH() {
- // You need to configure redis to enable auth.
- // This unit test suite assumes the auth feature is off/disabled.
- // Auth *would be* the first command required after connecting.
-
- printDisclaimer();
-}
-
-function testSELECT() {
- printDisclaimer();
-}
-
-function testFLUSHDB() {
- // no-op; tested in testSelect
-
- printDisclaimer();
-}
-
-function testSET() {
- client.set('foo', 'bar', expectOK("testSET"));
- client.set('baz', 'buz', expectOK("testSET"));
- client.set('ggg', '123', expectOK("testSET"));
-}
-
-function testSETNX() {
- client.set('foo', 'bar', expectOK("testSETNX"));
- client.setnx('foo', 'quux', expectNumber(0, "testSETNX")); // fails when already set
- client.setnx('boo', 'apple', expectNumber(1, "testSETNX")); // no such key already so OK
-}
-
-function testGET() {
- client.set('foo', 'bar', expectOK("testGET"));
- client.set('baz', 'buz', expectOK("testGET"));
-
- client.get('foo', function (err, value) {
- if (err) assert.fail(err, "testGET");
- checkEqual(value, 'bar', "testGET");
- });
-
- client.get('baz', function (err, value) {
- if (err) assert.fail(err, "testGET");
- checkEqual(value, 'buz', "testGET");
- });
-}
-
-function testMGET() {
- client.set('foo', 'bar', expectOK("testMGET"));
- client.set('baz', 'buz', expectOK("testMGET"));
-
- client.mget('foo', 'baz', function (err, values) {
- if (err) assert.fail(err, "testMGET");
- checkEqual(values[0], 'bar', "testMGET");
- checkEqual(values[1], 'buz', "testMGET");
- });
-
- // Accept an Array for the keys to MGET as well.
-
- client.mget(['foo', 'baz'], function (err, values) {
- if (err) assert.fail(err, "testMGET");
- checkEqual(values[0], 'bar', "testMGET");
- checkEqual(values[1], 'buz', "testMGET");
- });
-}
-
-function testGETSET() {
- client.set('getsetfoo', 'getsetbar', expectOK("testGETSET 0"));
-
- client.getset('getsetfoo', 'fuzz', function (err, previousValue) {
- if (err) assert.fail(err, "testGETSET 1");
- checkEqual(previousValue, 'getsetbar', "testGETSET 2");
- });
-
- client.get('getsetfoo', function (err, value) {
- if (err) assert.fail(err, "testGETSET 3");
- checkEqual(value, 'fuzz', "testGETSET 4");
- });
-}
-
-function testSETANDGETMULTIBYTE() {
- var testValue = '\u00F6\u65E5\u672C\u8A9E'; // ö日本語
- var buffer = new Buffer(32);
- var size = buffer.utf8Write(testValue,0);
- client.set('testUtf8Key', buffer.slice(0,size), expectOK("testSETANDGETMULTIBYTE"))
-
- client.get('testUtf8Key', function (err, value) {
- if (err) assert.fail(err, "testSETANDGETMULTIBYTE");
- checkEqual(value.utf8Slice(0, value.length), testValue, "testSETANDGETMULTIBYTE");
- });
-}
-
-function testINFO() {
- client.info(function (err, info) {
- check(info instanceof Object, "testINFO");
- check(info.hasOwnProperty('redis_version'), "testINFO");
- check(info.hasOwnProperty('connected_clients'), "testINFO");
- check(info.hasOwnProperty('uptime_in_seconds'), "testINFO");
- checkEqual(typeof(info.uptime_in_seconds), 'string', "testINFO");
- checkEqual(typeof(info.connected_clients), 'string', "testINFO");
- });
-}
-
-function testINCR() {
- client.incr('counter', expectNumber(1, "testINCR"))
- client.incr('counter', expectNumber(2, "testINCR"))
-}
-
-function testINCRBY() {
- client.incrby('counter', 2, expectNumber(2, "testINCRBY"))
- client.incrby('counter', -1, expectNumber(1, "testINCRBY"))
-}
-
-function testDECR() {
- client.decr('counter', expectNumber(-1, "testDECR"))
- client.decr('counter', expectNumber(-2, "testDECR"))
-}
-
-function testDECRBY() {
- client.decrby('counter', '1', expectNumber(-1, "testDECRBY"))
- client.decrby('counter', '2', expectNumber(-3, "testDECRBY"))
- client.decrby('counter', '-3', expectNumber(0, "testDECRBY"))
-}
-
-function testEXISTS() {
- client.set('foo', 'bar', expectOK("testEXISTS"));
- client.exists('foo', expectNumber(1, "testEXISTS"))
- client.exists('foo2', expectNumber(0, "testEXISTS"))
-}
-
-function testDEL() {
- client.set('goo', 'bar', expectOK("testDEL"));
- client.del('goo', expectNumber(1, "testDEL"));
- client.exists('goo', expectNumber(0, "testDEL"));
- client.del('goo', expectNumber(0, "testDEL"));
-}
-
-function testKEYS() {
- client.set('foo1', 'foo1Value', expectOK("testKEYS"))
- client.set('foo2', 'foo2Value', expectOK("testKEYS"))
-
- client.keys('foo*', function (err, keys) {
- if (err) assert.fail(err, "testKEYS");
- checkEqual(keys.length, 2, "testKEYS");
- redisclient.convertMultiBulkBuffersToUTF8Strings(keys);
- checkDeepEqual(keys.sort(), ['foo1', 'foo2'], "testKEYS");
- });
-
- client.set('baz', 'bazValue', expectOK("testKEYS"))
- client.set('boo', 'booValue', expectOK("testKEYS"))
-
- // At this point we have foo1, foo2, baz, boo
-
- client.keys('*', function (err, keys) {
- if (err) assert.fail(err, "testKEYS");
- checkEqual(keys.length, 4, "testKEYS");
- redisclient.convertMultiBulkBuffersToUTF8Strings(keys);
- checkDeepEqual(keys.sort(), ['baz', 'boo', 'foo1', 'foo2'], "testKEYS");
- });
-
- client.keys('?oo', function (err, keys) {
- if (err) assert.fail(err, "testKEYS");
- checkEqual(keys.length, 1, "testKEYS");
- redisclient.convertMultiBulkBuffersToUTF8Strings(keys);
- checkDeepEqual(keys.sort(), ['boo'], "testKEYS");
- });
-
- // Now try a key with a space in it.
- // At this point we have keys: foo1, foo2, baz, boo, a key is this
-
- client.set('a key is this', 'whatever', expectOK("testKEYS"))
-
- client.keys('*', function (err, keys) {
- if (err) assert.fail(err, "testKEYS");
- checkEqual(keys.length, 5, "testKEYS");
- redisclient.convertMultiBulkBuffersToUTF8Strings(keys);
- checkDeepEqual(keys.sort(), ['a key is this', 'baz', 'boo', 'foo1', 'foo2'], "testKEYS");
- });
-}
-
-function testRANDOMKEY() {
- client.set('foo', 'bar', expectOK("testRANDOMKEY"));
- client.set('baz', 'buz', expectOK("testRANDOMKEY"));
-
- client.randomkey(function (err, someKey) {
- if (err) assert.fail(err, "testRANDOMKEY");
- check(/^(foo|baz)$/.test(someKey), "testRANDOMKEY");
- });
-}
-
-function testRENAME() {
- client.set('foo', 'bar', expectOK("testRENAME"));
- client.rename('foo', 'zoo', expectOK("testRENAME"));
- client.exists('foo', expectNumber(0, "testRENAME"));
- client.exists('zoo', expectNumber(1, "testRENAME"));
-}
-
-function testRENAMENX() {
- client.set('roo', 'bar', expectOK("testRENAMENX"));
- client.set('bar', 'baz', expectOK("testRENAMENX"));
- client.renamenx('roo', 'bar', expectNumber(0, "testRENAMENX")); // bar already exists
- client.exists('roo', expectNumber(1, "testRENAMENX")); // was not renamed
- client.exists('bar', expectNumber(1, "testRENAMENX")); // was not touched
- client.renamenx('roo', 'too', expectNumber(1, "testRENAMENX")); // too did not exist... OK
- client.exists('roo', expectNumber(0, "testRENAMENX")); // was renamed
- client.exists('too', expectNumber(1, "testRENAMENX")); // was created
-}
-
-function testDBSIZE() {
- client.set('foo', 'bar', expectOK("testDBSIZE"));
- client.set('bar', 'baz', expectOK("testDBSIZE"));
-
- client.dbsize(function (err, value) {
- if (err) assert.fail(err, "testDBSIZE");
- checkEqual(value, 2, "testDBSIZE");
- });
-}
-
-function testEXPIRE() {
- showTestBanner("testEXPIRE");
-
- // set 'expfoo' to expire in 2 seconds
-
- client.set('expfoo', 'bar', expectOK("testEXPIRE"));
- client.expire('expfoo', 2, expectNumber(1, "testEXPIRE"));
-
- // subsequent expirations cannot be set.
-
- client.expire('expfoo', 10, expectNumber(0, "testEXPIRE"));
-
- if (verbose)
- log("info", "Please wait while a test key expires ...");
-
- setTimeout(function () {
- client.exists('expfoo', function (err, value) {
- if (err) assert.fail(err, "testEXPIRE");
- checkEqual(value, 0, "testEXPIRE");
- testSETEX();
- });
- }, 3000); // give it time to expire after 2 seconds
-}
-
-function testSETEX() {
- showTestBanner("testSETEX");
-
- client.setex('foo', 2, 'bar', function (err, value) {
- if (err) assert.fail(err, "testSETEX");
- checkEqual(value, true, "testSETEX");
- });
-
- if (verbose)
- log("info", "Please wait while a test key expires ...");
-
- setTimeout(function () {
- client.exists('foo', function (err, value) {
- if (err) assert.fail(err, "testSETEX");
- checkEqual(value, 0, "testSETEX");
- testSUBSCRIBEandPUBLISH();
- });
- }, 3000);
-}
-
-function testTTL() {
- client.set('ttlfoo', 'bar', expectOK("testTTL"));
-
- // ttlfoo is not set to expire
-
- client.ttl('ttlfoo', function (err, value) {
- if (err) assert.fail(err, "testTTL");
- checkEqual(value, -1, "testTTL");
- });
-
- client.set('ttlbar', 'baz', expectOK("testTTL"));
- client.expire('ttlbar', 3, expectNumber(1, "testTTL"));
-
- client.ttl('ttlbar', function (err, value) {
- if (err) assert.fail(err, "testTTL");
- check(value > 0, "testTTL");
- });
-}
-
-function testRPUSH() {
- client.rpush('list0', 'list0value0', expectNumber(1, "testRPUSH"));
- client.exists('list0', expectNumber(1, "testRPUSH"));
-}
-
-function testLPUSH() {
- client.exists('list1', expectNumber(0, "testLPUSH"));
- client.lpush('list1', 'list1value0', expectNumber(1, "testLPUSH"));
- client.exists('list1', expectNumber(1, "testLPUSH"));
-}
-
-function testLLEN() {
- client.rpush('list0', 'list0value0', expectNumber(1, "testLLEN"));
- client.llen('list0', expectNumber(1, "testLLEN"));
-
- client.rpush('list0', 'list0value1', expectNumber(2, "testLLEN"));
- client.llen('list0', expectNumber(2, "testLLEN"));
-}
-
-function testLRANGE() {
- client.rpush('list0', 'list0value0', expectNumber(1, "testLRANGE"));
- client.rpush('list0', 'list0value1', expectNumber(2, "testLRANGE"));
-
- client.lrange('list0', 0, -1, function (err, values) {
- if (err) assert.fail(err, "testLRANGE");
- checkEqual(values.length, 2, "testLRANGE");
- checkEqual(values[0], 'list0value0', "testLRANGE");
- checkEqual(values[1], 'list0value1', "testLRANGE");
- });
-
- client.lrange('list0', 0, 0, function (err, values) {
- if (err) assert.fail(err, "testLRANGE");
- checkEqual(values.length, 1, "testLRANGE");
- checkEqual(values[0], 'list0value0', "testLRANGE");
- });
-
- client.lrange('list0', -1, -1, function (err, values) {
- if (err) assert.fail(err, "testLRANGE");
- checkEqual(values.length, 1, "testLRANGE");
- checkEqual(values[0], 'list0value1', "testLRANGE");
- });
-}
-
-function testLTRIM() {
- client.rpush('list0', 'list0value0', expectNumber(1, "testLTRIM"));
- client.rpush('list0', 'list0value1', expectNumber(2, "testLTRIM"));
- client.rpush('list0', 'list0value2', expectNumber(3, "testLTRIM"));
-
- client.llen('list0', function (err, len) {
- if (err) assert.fail(err, "testLTRIM");
- checkEqual(len, 3, "testLTRIM");
- });
-
- client.ltrim('list0', 0, 1, expectOK("testLTRIM"))
-
- client.llen('list0', function (err, len) {
- if (err) assert.fail(err, "testLTRIM");
- checkEqual(len, 2, "testLTRIM");
- });
-
- client.lrange('list0', 0, -1, function (err, values) {
- if (err) assert.fail(err, "testLTRIM");
- checkEqual(values.length, 2, "testLTRIM");
- checkEqual(values[0], 'list0value0', "testLTRIM");
- checkEqual(values[1], 'list0value1', "testLTRIM");
- });
-}
-
-function testLINDEX() {
- client.rpush('list0', 'list0value0', expectNumber(1, "testLINDEX"));
- client.rpush('list0', 'list0value1', expectNumber(2, "testLINDEX"));
-
- client.lindex('list0', 0, function (err, value) {
- if (err) assert.fail(err, "testLINDEX");
- checkEqual(value, 'list0value0', "testLINDEX");
- });
-
- client.lindex('list0', 1, function (err, value) {
- if (err) assert.fail(err, "testLINDEX");
- checkEqual(value, 'list0value1', "testLINDEX");
- });
-
- // out of range => null
-
- client.lindex('list0', 2, function (err, value) {
- if (err) assert.fail(err, "testLINDEX");
- checkEqual(value, null, "testLINDEX");
- });
-}
-
-function testLSET() {
- client.rpush('list0', 'list0value0', expectNumber(1, "testLSET"));
- client.lset('list0', 0, 'LIST0VALUE0', expectOK("testLSET"));
-
- client.lrange('list0', 0, 0, function (err, values) {
- if (err) assert.fail(err, "testLSET");
- checkEqual(values.length, 1, "testLSET");
- checkEqual(values[0], 'LIST0VALUE0', "testLSET");
- });
-}
-
-function testLREM() {
- client.lpush('list0', 'ABC', expectNumber(1, "testLREM"));
- client.lpush('list0', 'DEF', expectNumber(2, "testLREM"));
- client.lpush('list0', 'ABC', expectNumber(3, "testLREM"));
-
- client.lrem('list0', 1, 'ABC', expectNumber(1, "testLREM"));
-
- client.lrange('list0', 0, -1, function (err, values) {
- if (err) assert.fail(err, "testLREM");
- checkEqual(values.length, 2, "testLREM");
- checkEqual(values[0], 'DEF', "testLREM");
- checkEqual(values[1], 'ABC', "testLREM");
- });
-}
-
-function testLPOP() {
- client.lpush('list0', 'ABC', expectNumber(1, "testLPOP"));
- client.lpush('list0', 'DEF', expectNumber(2, "testLPOP"));
- client.lpush('list0', 'GHI', expectNumber(3, "testLPOP"));
-
- client.lpop('list0', function (err, value) {
- if (err) assert.fail(err, "testLPOP");
- checkEqual(value, 'GHI', "testLPOP");
- });
-
- client.lpop('list0', function (err, value) {
- if (err) assert.fail(err, "testLPOP");
- checkEqual(value, 'DEF', "testLPOP");
- });
-
- client.lrange('list0', 0, -1, function (err, values) {
- if (err) assert.fail(err, "testLPOP");
- checkEqual(values.length, 1, "testLPOP");
- checkEqual(values[0], 'ABC', "testLPOP");
- });
-}
-
-function testRPOP() {
- client.lpush('list0', 'ABC', expectNumber(1, "testRPOP"));
- client.lpush('list0', 'DEF', expectNumber(2, "testRPOP"));
-
- client.rpop('list0', function (err, value) {
- if (err) assert.fail(err, "testRPOP");
- checkEqual(value, 'ABC', "testRPOP");
- });
-
- client.rpop('list0', function (err, value) {
- if (err) assert.fail(err, "testRPOP");
- checkEqual(value, 'DEF', "testRPOP");
- });
-
- client.llen('list0', function (err, len) {
- if (err) assert.fail(err, "testRPOP");
- checkEqual(len, 0, "testRPOP");
- });
-}
-
-function testRPOPLPUSH() {
- client.rpush('src', 'ABC', expectNumber(1, "testRPOPLPUSH"));
- client.rpush('src', 'DEF', expectNumber(2, "testRPOPLPUSH"));
-
- client.rpoplpush('src', 'dst', function (err, value) {
- if (err) assert.fail(err, "testRPOPLPUSH");
- checkEqual(value, 'DEF', "testRPOPLPUSH");
- });
-
- client.lrange('src', 0, -1, function (err, values) {
- if (err) assert.fail(err, "testRPOPLPUSH");
- redisclient.convertMultiBulkBuffersToUTF8Strings(values);
- checkDeepEqual(values, [ 'ABC' ], "testRPOPLPUSH");
- });
-
- client.lrange('dst', 0, -1, function (err, values) {
- if (err) assert.fail(err, "testRPOPLPUSH");
- redisclient.convertMultiBulkBuffersToUTF8Strings(values);
- checkDeepEqual(values, [ 'DEF' ], "testRPOPLPUSH");
- });
-}
-
-function testSADD() {
- client.sadd('set0', 'member0', expectNumber(1, "testSADD"));
- client.sadd('set0', 'member0', expectNumber(0, "testSADD")); // already member
-}
-
-function testSISMEMBER() {
- client.sadd('set0', 'member0', expectNumber(1, "testSISMEMBER"));
- client.sismember('set0', 'member0', expectNumber(1, "testSISMEMBER"));
- client.sismember('set0', 'member1', expectNumber(0, "testSISMEMBER"));
-}
-
-function testSCARD() {
- client.sadd('set0', 'member0', expectNumber(1, "testSCARD"));
- client.scard('set0', expectNumber(1, "testSCARD"));
-
- client.sadd('set0', 'member1', expectNumber(1, "testSCARD"));
- client.scard('set0', expectNumber(2, "testSCARD"));
-}
-
-function testSREM() {
- client.sadd('set0', 'member0', expectNumber(1, "testSREM"));
- client.srem('set0', 'foobar', expectNumber(0, "testSREM"))
- client.srem('set0', 'member0', expectNumber(1, "testSREM"))
- client.scard('set0', expectNumber(0, "testSREM"));
-}
-
-function testSPOP() {
- client.sadd('zzz', 'member0', expectNumber(1, "testSPOP"));
- client.scard('zzz', expectNumber(1, "testSPOP"));
-
- client.spop('zzz', function (err, value) {
- if (err) assert.fail(err, "testSPOP");
- checkEqual(value, 'member0', "testSPOP");
- });
-
- client.scard('zzz', expectNumber(0, "testSPOP"));
-}
-
-function testSDIFF() {
- client.sadd('foo', 'x', expectNumber(1, "testSDIFF"));
- client.sadd('foo', 'a', expectNumber(1, "testSDIFF"));
- client.sadd('foo', 'b', expectNumber(1, "testSDIFF"));
- client.sadd('foo', 'c', expectNumber(1, "testSDIFF"));
-
- client.sadd('bar', 'c', expectNumber(1, "testSDIFF"));
-
- client.sadd('baz', 'a', expectNumber(1, "testSDIFF"));
- client.sadd('baz', 'd', expectNumber(1, "testSDIFF"));
-
- client.sdiff('foo', 'bar', 'baz', function (err, values) {
- if (err) assert.fail(err, "testSDIFF");
- values.sort();
- checkEqual(values.length, 2, "testSDIFF");
- checkEqual(values[0], 'b', "testSDIFF");
- checkEqual(values[1], 'x', "testSDIFF");
- });
-}
-
-function testSDIFFSTORE() {
- client.sadd('foo', 'x', expectNumber(1, "testSDIFFSTORE"))
- client.sadd('foo', 'a', expectNumber(1, "testSDIFFSTORE"))
- client.sadd('foo', 'b', expectNumber(1, "testSDIFFSTORE"))
- client.sadd('foo', 'c', expectNumber(1, "testSDIFFSTORE"))
-
- client.sadd('bar', 'c', expectNumber(1, "testSDIFFSTORE"))
-
- client.sadd('baz', 'a', expectNumber(1, "testSDIFFSTORE"))
- client.sadd('baz', 'd', expectNumber(1, "testSDIFFSTORE"))
-
- // NB: SDIFFSTORE returns the number of elements in the dstkey
-
- client.sdiffstore('quux', 'foo', 'bar', 'baz', expectNumber(2, "testSDIFFSTORE"))
-
- client.smembers('quux', function (err, members) {
- if (err) assert.fail(err, "testSDIFFSTORE");
- members.sort();
- redisclient.convertMultiBulkBuffersToUTF8Strings(members);
- checkDeepEqual(members, [ 'b', 'x' ], "testSDIFFSTORE");
- });
-}
-
-function testSMEMBERS() {
- client.sadd('foo', 'x', expectNumber(1, "testSMEMBERS"));
-
- client.smembers('foo', function (err, members) {
- if (err) assert.fail(err, "testSMEMBERS");
- redisclient.convertMultiBulkBuffersToUTF8Strings(members);
- checkDeepEqual(members, [ 'x' ], "testSMEMBERS");
- });
-
- client.sadd('foo', 'y', expectNumber(1, "testSMEMBERS"));
-
- client.smembers('foo', function (err, members) {
- if (err) assert.fail(err, "testSMEMBERS");
- checkEqual(members.length, 2, "testSMEMBERS");
- redisclient.convertMultiBulkBuffersToUTF8Strings(members);
- checkDeepEqual(members.sort(), [ 'x', 'y' ], "testSMEMBERS");
- });
-}
-
-function testSMOVE() {
- client.sadd('foo', 'x', expectNumber(1, "testSMOVE"));
- client.smove('foo', 'bar', 'x', expectNumber(1, "testSMOVE"));
- client.sismember('foo', 'x', expectNumber(0, "testSMOVE"));
- client.sismember('bar', 'x', expectNumber(1, "testSMOVE"));
- client.smove('foo', 'bar', 'x', expectNumber(0, "testSMOVE"));
-}
-
-function testSINTER() {
- client.sadd('sa', 'a', expectNumber(1, "testSINTER"));
- client.sadd('sa', 'b', expectNumber(1, "testSINTER"));
- client.sadd('sa', 'c', expectNumber(1, "testSINTER"));
-
- client.sadd('sb', 'b', expectNumber(1, "testSINTER"));
- client.sadd('sb', 'c', expectNumber(1, "testSINTER"));
- client.sadd('sb', 'd', expectNumber(1, "testSINTER"));
-
- client.sadd('sc', 'c', expectNumber(1, "testSINTER"));
- client.sadd('sc', 'd', expectNumber(1, "testSINTER"));
- client.sadd('sc', 'e', expectNumber(1, "testSINTER"));
-
- client.sinter('sa', 'sb', function (err, intersection) {
- if (err) assert.fail(err, "testSINTER");
- checkEqual(intersection.length, 2, "testSINTER");
- redisclient.convertMultiBulkBuffersToUTF8Strings(intersection);
- checkDeepEqual(intersection.sort(), [ 'b', 'c' ], "testSINTER");
- });
-
- client.sinter('sb', 'sc', function (err, intersection) {
- if (err) assert.fail(err, "testSINTER");
- checkEqual(intersection.length, 2, "testSINTER");
- redisclient.convertMultiBulkBuffersToUTF8Strings(intersection);
- checkDeepEqual(intersection.sort(), [ 'c', 'd' ], "testSINTER");
- });
-
- client.sinter('sa', 'sc', function (err, intersection) {
- if (err) assert.fail(err, "testSINTER");
- checkEqual(intersection.length, 1, "testSINTER");
- checkEqual(intersection[0], 'c', "testSINTER");