UNPKG

5.29 kBJavaScriptView Raw
1"use strict";
2
3
4// By default fakeredis simulates a ridiculous amount of network latency
5// to help you discover race-conditions when testing multi-client setups.
6// Instantiate your 'clients' with a truthy .fast option,
7// or set it here globally to make things go a bit faster.
8exports.fast = false;
9
10
11/**
12
13 TODO:
14 - lint negative count and offset LIMITs away,
15 SORT and ZRANGEBYSCORE treat them differently, so it's just confusing and a bad practice.
16
17 **/
18
19var index = require ( "redis" ),
20 Backend = require ( "./lib/backend" ).Backend,
21 Connection = require ( "./lib/connection" ).Connection,
22 helpers = require ( "./lib/helpers" ),
23
24 backends = {},
25 RedisClient = index.RedisClient,
26
27 anon = 0;
28
29
30 //// Re-export redis exports.
31
32exports.RedisClient = index.RedisClient;
33exports.Multi = index.Multi;
34exports.print = index.print;
35
36
37 //// Overriden client factory.
38
39exports.createClient = function ( port, host, options )
40{
41 var id = !port && !host ? 'fake_' + ( ++ anon ) : ( host || "" ) + ( port || "" ),
42 lat = options && options.fast || exports.fast ? 1 : null,
43 c = new Connection ( backends [ id ] || ( backends [ id ] = new Backend ), lat, lat ),
44 cl = new RedisClient ( { on : function () {} } /* , options */ ),
45 ns = options && options.no_sugar;
46
47 if ( options && options.verbose )
48 c.verbose = true;
49
50 cl.connected = true;
51 cl.ready = true;
52
53 cl.end = function() {
54 cl.send_command = function(command) {
55 throw new Error("fakeredis: You've closed this connection with .end(), cannot " + command);
56 };
57 };
58
59 cl.send_command = function ( command, args, callback )
60 {
61 //// Interpret arguments, copy-paste from mranney/redis/index.js for best compat.
62
63 if (typeof command !== "string") {
64 throw new Error("First argument to send_command must be the command name string, not " + typeof command);
65 }
66
67 if (Array.isArray(args)) {
68 if (typeof callback === "function") {
69 // probably the fastest way:
70 // client.command([arg1, arg2], cb); (straight passthrough)
71 // send_command(command, [arg1, arg2], cb);
72 } else if (! callback) {
73 // most people find this variable argument length form more convenient, but it uses arguments, which is slower
74 // client.command(arg1, arg2, cb); (wraps up arguments into an array)
75 // send_command(command, [arg1, arg2, cb]);
76 // client.command(arg1, arg2); (callback is optional)
77 // send_command(command, [arg1, arg2]);
78 // client.command(arg1, arg2, undefined); (callback is undefined)
79 // send_command(command, [arg1, arg2, undefined]);
80 var last_arg_type = typeof args[args.length - 1];
81 if (last_arg_type === "function" || last_arg_type === "undefined") {
82 callback = args.pop();
83 }
84 } else {
85 throw new Error("send_command: last argument must be a callback or undefined");
86 }
87 } else {
88 throw new Error("send_command: second argument must be an array");
89 }
90
91 // if the last argument is an array, expand it out. This allows commands like this:
92 // client.command(arg1, [arg2, arg3, arg4], cb);
93 // and converts to:
94 // client.command(arg1, arg2, arg3, arg4, cb);
95 // which is convenient for some things like sadd
96 if (Array.isArray(args[args.length - 1])) {
97 args = args.slice(0, -1).concat(args[args.length - 1]);
98 }
99
100 //// Lint args.
101
102 if ( !options || !options.no_lint )
103 {
104 var i, n;
105 n = args.length;
106 for ( i = 0; i < n; i ++ )
107 if ( typeof args [ i ] !== 'string' && typeof args [ i ] !== 'number' )
108 throw new Error ( "fakeredis/lint: Argument #" + i + " for " + command + " is not a String or Number: " + args [ i ] );
109 }
110
111 //// You can disable hash sugar with the no_sugar option.
112
113 var cb;
114 if ( callback && !ns && /^hgetall/i.test ( command ) )
115 cb = function ( err, data )
116 {
117 if ( !err && data )
118 data = reply_to_object ( data );
119
120 callback ( err, data );
121 };
122
123 else
124 cb = callback;
125
126 c.push ( this, command, args, cb );
127 };
128
129 cl.pushMessage = cl.emit.bind ( cl );
130
131 ( function ()
132 {
133 var prop;
134 for ( prop in helpers )
135 cl [ prop ] = helpers [ prop ];
136 }
137 () );
138
139 return cl;
140};
141
142
143 //// Helpers for node_redis compat.
144
145// hgetall converts its replies to an Object. If the reply is empty, null is returned.
146function reply_to_object(reply) {
147 var obj = {}, j, jl, key, val;
148
149 if (reply.length === 0) {
150 return null;
151 }
152
153 for (j = 0, jl = reply.length; j < jl; j += 2) {
154 key = reply[j].toString();
155 val = reply[j + 1];
156 obj[key] = val;
157 }
158
159 return obj;
160}
161