cs-em-hiredis 0.1.2

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,314 @@
1
+ require 'spec_helper'
2
+
3
+ describe EventMachine::Hiredis::PubsubClient, '(un)subscribe' do
4
+ describe "subscribing" do
5
+ it "should return deferrable which succeeds with subscribe call result" do
6
+ connect do |redis|
7
+ df = redis.pubsub.subscribe("channel") { }
8
+ df.should be_kind_of(EventMachine::DefaultDeferrable)
9
+ df.callback { |subscription_count|
10
+ # Subscribe response from redis - indicates that subscription has
11
+ # succeeded and that the current connection has a single
12
+ # subscription
13
+ subscription_count.should == 1
14
+ done
15
+ }
16
+ end
17
+ end
18
+
19
+ it "should run the passed block when message received" do
20
+ connect do |redis|
21
+ redis.pubsub.subscribe("channel") { |message|
22
+ message.should == 'hello'
23
+ done
24
+ }.callback {
25
+ redis.publish('channel', 'hello')
26
+ }
27
+ end
28
+ end
29
+
30
+ it "should run the passed proc when message received on channel" do
31
+ connect do |redis|
32
+ proc = Proc.new { |message|
33
+ message.should == 'hello'
34
+ done
35
+ }
36
+ redis.pubsub.subscribe("channel", proc).callback {
37
+ redis.publish('channel', 'hello')
38
+ }
39
+ end
40
+ end
41
+ end
42
+
43
+ describe "unsubscribing" do
44
+ it "should allow unsubscribing a single callback without unsubscribing from redis" do
45
+ connect do |redis|
46
+ proc1 = Proc.new { |message| fail }
47
+ proc2 = Proc.new { |message|
48
+ message.should == 'hello'
49
+ done
50
+ }
51
+ redis.pubsub.subscribe("channel", proc1)
52
+ redis.pubsub.subscribe("channel", proc2).callback {
53
+ redis.pubsub.unsubscribe_proc("channel", proc1)
54
+ redis.publish("channel", "hello")
55
+ }
56
+ end
57
+ end
58
+
59
+ it "should unsubscribe from redis on last proc unsubscription" do
60
+ connect do |redis|
61
+ proc = Proc.new { |message| }
62
+ redis.pubsub.subscribe("channel", proc).callback { |subs_count|
63
+ subs_count.should == 1
64
+ redis.pubsub.unsubscribe_proc("channel", proc).callback {
65
+ # Slightly awkward way to check that unsubscribe happened:
66
+ redis.pubsub.subscribe('channel2').callback { |count|
67
+ # If count is 1 this implies that channel unsubscribed
68
+ count.should == 1
69
+ done
70
+ }
71
+ }
72
+ }
73
+ end
74
+ end
75
+
76
+ it "should allow unsubscribing from redis channel, including all callbacks, and return deferrable for redis unsubscribe" do
77
+ connect do |redis|
78
+ # Raw pubsub event
79
+ redis.pubsub.on('message') { |channel, message| fail }
80
+ # Block subscription
81
+ redis.pubsub.subscribe("channel") { |m| fail } # block
82
+ # Proc example
83
+ df = redis.pubsub.subscribe("channel", Proc.new { |m| fail })
84
+
85
+ df.callback {
86
+ redis.pubsub.unsubscribe("channel").callback { |remaining_subs|
87
+ remaining_subs.should == 0
88
+ redis.publish("channel", "hello") {
89
+ done
90
+ }
91
+ }
92
+ }
93
+ end
94
+ end
95
+ end
96
+
97
+ it "should expose raw pubsub events from redis" do
98
+ channel = "channel"
99
+ callback_count = 0
100
+ connect do |redis|
101
+ redis.pubsub.on(:subscribe) { |channel, subscription_count|
102
+ # 2. Get subscribe callback
103
+ callback_count += 1
104
+ channel.should == channel
105
+ subscription_count.should == 1
106
+
107
+ # 3. Publish on channel
108
+ redis.publish(channel, 'foo')
109
+ }
110
+
111
+ redis.pubsub.on(:message) { |channel, message|
112
+ # 4. Get message callback
113
+ callback_count += 1
114
+ channel.should == channel
115
+ message.should == 'foo'
116
+
117
+ callback_count.should == 2
118
+ done
119
+ }
120
+
121
+ # 1. Subscribe to channel
122
+ redis.pubsub.subscribe(channel)
123
+ end
124
+ end
125
+
126
+ it "should resubscribe to all channels on reconnect" do
127
+ callback_count = 0
128
+ connect do |redis|
129
+ # 1. Subscribe to channels
130
+ redis.pubsub.subscribe('channel1') {
131
+ callback_count += 1
132
+ }
133
+ redis.pubsub.subscribe('channel2') {
134
+ callback_count += 1
135
+ EM.next_tick {
136
+ # 4. Success if both messages have been received
137
+ callback_count.should == 2
138
+ done
139
+ }
140
+ }.callback { |subscription_count|
141
+ subscription_count.should == 2
142
+ # 2. Subscriptions complete. Now force disconnect
143
+ redis.pubsub.instance_variable_get(:@connection).close_connection
144
+
145
+ EM.add_timer(0.1) {
146
+ # 3. After giving time to reconnect publish to both channels
147
+ redis.publish('channel1', 'foo')
148
+ redis.publish('channel2', 'bar')
149
+ }
150
+
151
+ }
152
+
153
+ end
154
+ end
155
+ end
156
+
157
+ describe EventMachine::Hiredis::PubsubClient, 'p(un)subscribe' do
158
+ describe "psubscribing" do
159
+ it "should return deferrable which succeeds with psubscribe call result" do
160
+ connect do |redis|
161
+ df = redis.pubsub.psubscribe("channel") { }
162
+ df.should be_kind_of(EventMachine::DefaultDeferrable)
163
+ df.callback { |subscription_count|
164
+ # Subscribe response from redis - indicates that subscription has
165
+ # succeeded and that the current connection has a single
166
+ # subscription
167
+ subscription_count.should == 1
168
+ done
169
+ }
170
+ end
171
+ end
172
+
173
+ it "should run the passed block when message received" do
174
+ connect do |redis|
175
+ redis.pubsub.psubscribe("channel:*") { |channel, message|
176
+ channel.should == 'channel:foo'
177
+ message.should == 'hello'
178
+ done
179
+ }.callback {
180
+ redis.publish('channel:foo', 'hello')
181
+ }
182
+ end
183
+ end
184
+
185
+ it "should run the passed proc when message received on channel" do
186
+ connect do |redis|
187
+ proc = Proc.new { |channel, message|
188
+ channel.should == 'channel:foo'
189
+ message.should == 'hello'
190
+ done
191
+ }
192
+ redis.pubsub.psubscribe("channel:*", proc).callback {
193
+ redis.publish('channel:foo', 'hello')
194
+ }
195
+ end
196
+ end
197
+ end
198
+
199
+ describe "punsubscribing" do
200
+ it "should allow punsubscribing a single callback without punsubscribing from redis" do
201
+ connect do |redis|
202
+ proc1 = Proc.new { |channel, message| fail }
203
+ proc2 = Proc.new { |channel, message|
204
+ channel.should == 'channel:foo'
205
+ message.should == 'hello'
206
+ done
207
+ }
208
+ redis.pubsub.psubscribe("channel:*", proc1)
209
+ redis.pubsub.psubscribe("channel:*", proc2).callback {
210
+ redis.pubsub.punsubscribe_proc("channel:*", proc1)
211
+ redis.publish("channel:foo", "hello")
212
+ }
213
+ end
214
+ end
215
+
216
+ it "should punsubscribe from redis on last proc punsubscription" do
217
+ connect do |redis|
218
+ proc = Proc.new { |message| }
219
+ redis.pubsub.psubscribe("channel:*", proc).callback { |subs_count|
220
+ subs_count.should == 1
221
+ redis.pubsub.punsubscribe_proc("channel:*", proc).callback {
222
+ # Slightly awkward way to check that unsubscribe happened:
223
+ redis.pubsub.psubscribe('channel2').callback { |count|
224
+ # If count is 1 this implies that channel unsubscribed
225
+ count.should == 1
226
+ done
227
+ }
228
+ }
229
+ }
230
+ end
231
+ end
232
+
233
+ it "should allow punsubscribing from redis channel, including all callbacks, and return deferrable for redis punsubscribe" do
234
+ connect do |redis|
235
+ # Raw pubsub event
236
+ redis.pubsub.on('pmessage') { |pattern, channel, message| fail }
237
+ # Block subscription
238
+ redis.pubsub.psubscribe("channel") { |c, m| fail } # block
239
+ # Proc example
240
+ df = redis.pubsub.psubscribe("channel", Proc.new { |c, m| fail })
241
+
242
+ df.callback {
243
+ redis.pubsub.punsubscribe("channel").callback { |remaining_subs|
244
+ remaining_subs.should == 0
245
+ redis.publish("channel", "hello") {
246
+ done
247
+ }
248
+ }
249
+ }
250
+ end
251
+ end
252
+ end
253
+
254
+ it "should expose raw pattern pubsub events from redis" do
255
+ callback_count = 0
256
+ connect do |redis|
257
+ redis.pubsub.on(:psubscribe) { |pattern, subscription_count|
258
+ # 2. Get subscribe callback
259
+ callback_count += 1
260
+ pattern.should == "channel:*"
261
+ subscription_count.should == 1
262
+
263
+ # 3. Publish on channel
264
+ redis.publish('channel:foo', 'foo')
265
+ }
266
+
267
+ redis.pubsub.on(:pmessage) { |pattern, channel, message|
268
+ # 4. Get message callback
269
+ callback_count += 1
270
+ pattern.should == 'channel:*'
271
+ channel.should == 'channel:foo'
272
+ message.should == 'foo'
273
+
274
+ callback_count.should == 2
275
+ done
276
+ }
277
+
278
+ # 1. Subscribe to channel
279
+ redis.pubsub.psubscribe('channel:*')
280
+ end
281
+ end
282
+
283
+ it "should resubscribe to all pattern subscriptions on reconnect" do
284
+ callback_count = 0
285
+ connect do |redis|
286
+ # 1. Subscribe to channels
287
+ redis.pubsub.psubscribe('foo:*') { |channel, message|
288
+ channel.should == 'foo:a'
289
+ message.should == 'hello foo'
290
+ callback_count += 1
291
+ }
292
+ redis.pubsub.psubscribe('bar:*') { |channel, message|
293
+ channel.should == 'bar:b'
294
+ message.should == 'hello bar'
295
+ callback_count += 1
296
+ EM.next_tick {
297
+ # 4. Success if both messages have been received
298
+ callback_count.should == 2
299
+ done
300
+ }
301
+ }.callback { |subscription_count|
302
+ subscription_count.should == 2
303
+ # 2. Subscriptions complete. Now force disconnect
304
+ redis.pubsub.instance_variable_get(:@connection).close_connection
305
+
306
+ EM.add_timer(0.1) {
307
+ # 3. After giving time to reconnect publish to both channels
308
+ redis.publish('foo:a', 'hello foo')
309
+ redis.publish('bar:b', 'hello bar')
310
+ }
311
+ }
312
+ end
313
+ end
314
+ end
@@ -0,0 +1,910 @@
1
+ require 'spec_helper'
2
+
3
+ describe EventMachine::Hiredis, "commands" do
4
+ it "pings" do
5
+ connect do |redis|
6
+ redis.ping { |r| r.should == 'PONG'; done }
7
+ end
8
+ end
9
+
10
+ it "SETs and GETs a key" do
11
+ connect do |redis|
12
+ redis.set('foo', 'nik')
13
+ redis.get('foo') { |r| r.should == 'nik'; done }
14
+ end
15
+ end
16
+
17
+ it "handles trailing newline characters" do
18
+ connect do |redis|
19
+ redis.set('foo', "bar\n")
20
+ redis.get('foo') { |r| r.should == "bar\n"; done }
21
+ end
22
+ end
23
+
24
+ it "stores and retrieves all possible characters at the beginning and the end of a string" do
25
+ connect do |redis|
26
+ (0..255).each do |char_idx|
27
+ string = "#{char_idx.chr}---#{char_idx.chr}"
28
+ if RUBY_VERSION > "1.9"
29
+ string.force_encoding("UTF-8")
30
+ end
31
+ redis.set('foo', string)
32
+ redis.get('foo') { |r| r.should == string }
33
+ end
34
+ redis.ping { done }
35
+ end
36
+ end
37
+
38
+ it "SETs a key with an expiry" do
39
+ connect do |redis|
40
+ timeout(3)
41
+
42
+ redis.setex('foo', 1, 'bar')
43
+ redis.get('foo') { |r| r.should == 'bar' }
44
+ EventMachine.add_timer(2) do
45
+ redis.get('foo') { |r| r.should == nil }
46
+ redis.ping { done }
47
+ end
48
+ end
49
+ end
50
+
51
+ it "gets TTL for a key" do
52
+ connect do |redis|
53
+ redis.setex('foo', 1, 'bar')
54
+ redis.ttl('foo') { |r| r.should == 1; done }
55
+ end
56
+ end
57
+
58
+ it "can SETNX" do
59
+ connect do |redis|
60
+ redis.set('foo', 'nik')
61
+ redis.get('foo') { |r| r.should == 'nik' }
62
+ redis.setnx 'foo', 'bar'
63
+ redis.get('foo') { |r| r.should == 'nik' }
64
+
65
+ redis.ping { done }
66
+ end
67
+ end
68
+
69
+ it "can GETSET" do
70
+ connect do |redis|
71
+ redis.set('foo', 'bar')
72
+ redis.getset('foo', 'baz') { |r| r.should == 'bar' }
73
+ redis.get('foo') { |r| r.should == 'baz'; done }
74
+ end
75
+ end
76
+
77
+ it "can INCR a key" do
78
+ connect do |redis|
79
+ redis.del('counter')
80
+ redis.incr('counter') { |r| r.should == 1 }
81
+ redis.incr('counter') { |r| r.should == 2 }
82
+ redis.incr('counter') { |r| r.should == 3 }
83
+
84
+ redis.ping { done }
85
+ end
86
+ end
87
+
88
+ it "can INCRBY a key" do
89
+ connect do |redis|
90
+ redis.del('counter')
91
+ redis.incrby('counter', 1) { |r| r.should == 1 }
92
+ redis.incrby('counter', 2) { |r| r.should == 3 }
93
+ redis.incrby('counter', 3) { |r| r.should == 6 }
94
+
95
+ redis.ping { done }
96
+ end
97
+ end
98
+
99
+ it "can DECR a key" do
100
+ connect do |redis|
101
+ redis.del('counter')
102
+ redis.incr('counter') { |r| r.should == 1 }
103
+ redis.incr('counter') { |r| r.should == 2 }
104
+ redis.incr('counter') { |r| r.should == 3 }
105
+ redis.decr('counter') { |r| r.should == 2 }
106
+ redis.decrby('counter', 2) { |r| r.should == 0; done }
107
+ end
108
+ end
109
+
110
+ it "can RANDOMKEY" do
111
+ connect do |redis|
112
+ redis.set('foo', 'bar')
113
+ redis.randomkey { |r| r.should_not == nil; done }
114
+ end
115
+ end
116
+
117
+ it "can RENAME a key" do
118
+ connect do |redis|
119
+ redis.del 'foo'
120
+ redis.del 'bar'
121
+ redis.set('foo', 'hi')
122
+ redis.rename 'foo', 'bar'
123
+ redis.get('bar') { |r| r.should == 'hi' ; done }
124
+ end
125
+ end
126
+
127
+ it "can RENAMENX a key" do
128
+ connect do |redis|
129
+ redis.del 'foo'
130
+ redis.del 'bar'
131
+ redis.set('foo', 'hi')
132
+ redis.set('bar', 'ohai')
133
+ redis.renamenx 'foo', 'bar'
134
+ redis.get('bar') { |r| r.should == 'ohai' ; done }
135
+ end
136
+ end
137
+
138
+ it "can get DBSIZE of the database" do
139
+ connect do |redis|
140
+ redis.set('foo1', 'bar')
141
+ redis.set('foo2', 'baz')
142
+ redis.set('foo3', 'bat')
143
+ redis.dbsize do |r|
144
+ r.should == 3
145
+ done
146
+ end
147
+ end
148
+ end
149
+
150
+ it "can EXPIRE a key" do
151
+ connect do |redis|
152
+ timeout(3)
153
+
154
+ redis.set('foo', 'bar')
155
+ redis.expire 'foo', 1
156
+ redis.get('foo') { |r| r.should == "bar" }
157
+ EventMachine.add_timer(2) do
158
+ redis.get('foo') { |r| r.should == nil }
159
+ redis.ping { done }
160
+ end
161
+ end
162
+ end
163
+
164
+
165
+ it "can check if a key EXISTS" do
166
+ connect do |redis|
167
+ redis.set 'foo', 'nik'
168
+ redis.exists('foo') { |r| r.should == 1 }
169
+ redis.del 'foo'
170
+ redis.exists('foo') { |r| r.should == 0 ; done }
171
+ end
172
+ end
173
+
174
+ it "can list KEYS" do
175
+ connect do |redis|
176
+ redis.keys("f*") { |keys| keys.each { |key| @r.del key } }
177
+ redis.set('f', 'nik')
178
+ redis.set('fo', 'nak')
179
+ redis.set('foo', 'qux')
180
+ redis.keys("f*") { |r| r.sort.should == ['f', 'fo', 'foo'].sort }
181
+
182
+ redis.ping { done }
183
+ end
184
+ end
185
+
186
+ it "returns a random key (RANDOMKEY)" do
187
+ connect do |redis|
188
+ redis.set("foo", "bar")
189
+ redis.randomkey do |r|
190
+ redis.exists(r) do |e|
191
+ e.should == 1
192
+ done
193
+ end
194
+ end
195
+ end
196
+ end
197
+
198
+ it "should be able to check the TYPE of a key" do
199
+ connect do |redis|
200
+ redis.set('foo', 'nik')
201
+ redis.type('foo') { |r| r.should == "string" }
202
+ redis.del 'foo'
203
+ redis.type('foo') { |r| r.should == "none" ; done }
204
+ end
205
+ end
206
+
207
+ it "pushes to the head of a list (LPUSH)" do
208
+ connect do |redis|
209
+ redis.lpush "list", 'hello'
210
+ redis.lpush "list", 42
211
+ redis.type('list') { |r| r.should == "list" }
212
+ redis.llen('list') { |r| r.should == 2 }
213
+ redis.lpop('list') { |r| r.should == '42'; done }
214
+ end
215
+ end
216
+
217
+ it "pushes to the tail of a list (RPUSH)" do
218
+ connect do |redis|
219
+ redis.rpush "list", 'hello'
220
+ redis.type('list') { |r| r.should == "list" }
221
+ redis.llen('list') { |r| r.should == 1 ; done }
222
+ end
223
+ end
224
+
225
+ it "pops the tail of a list (RPOP)" do
226
+ connect do |redis|
227
+ redis.rpush "list", 'hello'
228
+ redis.rpush"list", 'goodbye'
229
+ redis.type('list') { |r| r.should == "list" }
230
+ redis.llen('list') { |r| r.should == 2 }
231
+ redis.rpop('list') { |r| r.should == 'goodbye'; done }
232
+ end
233
+ end
234
+
235
+ it "pop the head of a list (LPOP)" do
236
+ connect do |redis|
237
+ redis.rpush "list", 'hello'
238
+ redis.rpush "list", 'goodbye'
239
+ redis.type('list') { |r| r.should == "list" }
240
+ redis.llen('list') { |r| r.should == 2 }
241
+ redis.lpop('list') { |r| r.should == 'hello'; done }
242
+ end
243
+ end
244
+
245
+ it "gets the length of a list (LLEN)" do
246
+ connect do |redis|
247
+ redis.rpush "list", 'hello'
248
+ redis.rpush "list", 'goodbye'
249
+ redis.type('list') { |r| r.should == "list" }
250
+ redis.llen('list') { |r| r.should == 2 ; done }
251
+ end
252
+ end
253
+
254
+ it "gets a range of values from a list (LRANGE)" do
255
+ connect do |redis|
256
+ redis.rpush "list", 'hello'
257
+ redis.rpush "list", 'goodbye'
258
+ redis.rpush "list", '1'
259
+ redis.rpush "list", '2'
260
+ redis.rpush "list", '3'
261
+ redis.type('list') { |r| r.should == "list" }
262
+ redis.llen('list') { |r| r.should == 5 }
263
+ redis.lrange('list', 2, -1) { |r| r.should == ['1', '2', '3']; done }
264
+ end
265
+ end
266
+
267
+ it "trims a list (LTRIM)" do
268
+ connect do |redis|
269
+ redis.rpush "list", 'hello'
270
+ redis.rpush "list", 'goodbye'
271
+ redis.rpush "list", '1'
272
+ redis.rpush "list", '2'
273
+ redis.rpush "list", '3'
274
+ redis.type('list') { |r| r.should == "list" }
275
+ redis.llen('list') { |r| r.should == 5 }
276
+ redis.ltrim 'list', 0, 1
277
+ redis.llen('list') { |r| r.should == 2 }
278
+ redis.lrange('list', 0, -1) { |r| r.should == ['hello', 'goodbye']; done }
279
+ end
280
+ end
281
+
282
+ it "gets a value by indexing into a list (LINDEX)" do
283
+ connect do |redis|
284
+ redis.rpush "list", 'hello'
285
+ redis.rpush "list", 'goodbye'
286
+ redis.type('list') { |r| r.should == "list" }
287
+ redis.llen('list') { |r| r.should == 2 }
288
+ redis.lindex('list', 1) { |r| r.should == 'goodbye'; done }
289
+ end
290
+ end
291
+
292
+ it "sets a value by indexing into a list (LSET)" do
293
+ connect do |redis|
294
+ redis.rpush "list", 'hello'
295
+ redis.rpush "list", 'hello'
296
+ redis.type('list') { |r| r.should == "list" }
297
+ redis.llen('list') { |r| r.should == 2 }
298
+ redis.lset('list', 1, 'goodbye') { |r| r.should == 'OK' }
299
+ redis.lindex('list', 1) { |r| r.should == 'goodbye'; done }
300
+ end
301
+ end
302
+
303
+ it "removes values from a list (LREM)" do
304
+ connect do |redis|
305
+ redis.rpush "list", 'hello'
306
+ redis.rpush "list", 'goodbye'
307
+ redis.type('list') { |r| r.should == "list" }
308
+ redis.llen('list') { |r| r.should == 2 }
309
+ redis.lrem('list', 1, 'hello') { |r| r.should == 1 }
310
+ redis.lrange('list', 0, -1) { |r| r.should == ['goodbye']; done }
311
+ end
312
+ end
313
+
314
+ it "pops values from a list and push them onto a temp list(RPOPLPUSH)" do
315
+ connect do |redis|
316
+ redis.rpush "list", 'one'
317
+ redis.rpush "list", 'two'
318
+ redis.rpush "list", 'three'
319
+ redis.type('list') { |r| r.should == "list" }
320
+ redis.llen('list') { |r| r.should == 3 }
321
+ redis.lrange('list', 0, -1) { |r| r.should == ['one', 'two', 'three'] }
322
+ redis.lrange('tmp', 0, -1) { |r| r.should == [] }
323
+ redis.rpoplpush('list', 'tmp') { |r| r.should == 'three' }
324
+ redis.lrange('tmp', 0, -1) { |r| r.should == ['three'] }
325
+ redis.rpoplpush('list', 'tmp') { |r| r.should == 'two' }
326
+ redis.lrange('tmp', 0, -1) { |r| r.should == ['two', 'three'] }
327
+ redis.rpoplpush('list', 'tmp') { |r| r.should == 'one' }
328
+ redis.lrange('tmp', 0, -1) { |r| r.should == ['one', 'two', 'three']; done }
329
+ end
330
+ end
331
+
332
+ it "adds members to a set (SADD)" do
333
+ connect do |redis|
334
+ redis.sadd "set", 'key1'
335
+ redis.sadd "set", 'key2'
336
+ redis.type('set') { |r| r.should == "set" }
337
+ redis.scard('set') { |r| r.should == 2 }
338
+ redis.smembers('set') { |r| r.sort.should == ['key1', 'key2'].sort; done }
339
+ end
340
+ end
341
+
342
+ it "deletes members to a set (SREM)" do
343
+ connect do |redis|
344
+ redis.sadd "set", 'key1'
345
+ redis.sadd "set", 'key2'
346
+ redis.type('set') { |r| r.should == "set" }
347
+ redis.scard('set') { |r| r.should == 2 }
348
+ redis.smembers('set') { |r| r.sort.should == ['key1', 'key2'].sort }
349
+ redis.srem('set', 'key1')
350
+ redis.scard('set') { |r| r.should == 1 }
351
+ redis.smembers('set') { |r| r.should == ['key2']; done }
352
+ end
353
+ end
354
+
355
+ it "returns and remove random key from set (SPOP)" do
356
+ connect do |redis|
357
+ redis.sadd "set_pop", "key1"
358
+ redis.sadd "set_pop", "key2"
359
+ redis.spop("set_pop") { |r| r.should_not == nil }
360
+ redis.scard("set_pop") { |r| r.should == 1; done }
361
+ end
362
+ end
363
+
364
+ it "returns random key without delete the key from a set (SRANDMEMBER)" do
365
+ connect do |redis|
366
+ redis.sadd "set_srandmember", "key1"
367
+ redis.sadd "set_srandmember", "key2"
368
+ redis.srandmember("set_srandmember") { |r| r.should_not == nil }
369
+ redis.scard("set_srandmember") { |r| r.should == 2; done }
370
+ end
371
+ end
372
+
373
+ it "counts the members of a set (SCARD)" do
374
+ connect do |redis|
375
+ redis.sadd "set", 'key1'
376
+ redis.sadd "set", 'key2'
377
+ redis.type('set') { |r| r.should == "set" }
378
+ redis.scard('set') { |r| r.should == 2; done }
379
+ end
380
+ end
381
+
382
+ it "tests for set membership (SISMEMBER)" do
383
+ connect do |redis|
384
+ redis.sadd "set", 'key1'
385
+ redis.sadd "set", 'key2'
386
+ redis.type('set') { |r| r.should == "set" }
387
+ redis.scard('set') { |r| r.should == 2 }
388
+ redis.sismember('set', 'key1') { |r| r.should == 1 }
389
+ redis.sismember('set', 'key2') { |r| r.should == 1 }
390
+ redis.sismember('set', 'notthere') { |r| r.should == 0; done }
391
+ end
392
+ end
393
+
394
+ it "intersects sets (SINTER)" do
395
+ connect do |redis|
396
+ redis.sadd "set", 'key1'
397
+ redis.sadd "set", 'key2'
398
+ redis.sadd "set2", 'key2'
399
+ redis.sinter('set', 'set2') { |r| r.should == ['key2']; done }
400
+ end
401
+ end
402
+
403
+ it "intersects set and stores the results in a key (SINTERSTORE)" do
404
+ connect do |redis|
405
+ redis.sadd "set", 'key1'
406
+ redis.sadd "set", 'key2'
407
+ redis.sadd "set2", 'key2'
408
+ redis.sinterstore('newone', 'set', 'set2') { |r| r.should == 1 }
409
+ redis.smembers('newone') { |r| r.should == ['key2']; done }
410
+ end
411
+ end
412
+
413
+ it "performs set unions (SUNION)" do
414
+ connect do |redis|
415
+ redis.sadd "set", 'key1'
416
+ redis.sadd "set", 'key2'
417
+ redis.sadd "set2", 'key2'
418
+ redis.sadd "set2", 'key3'
419
+ redis.sunion('set', 'set2') { |r| r.sort.should == ['key1','key2','key3'].sort; done }
420
+ end
421
+ end
422
+
423
+ it "performs a set union and store the results in a key (SUNIONSTORE)" do
424
+ connect do |redis|
425
+ redis.sadd "set", 'key1'
426
+ redis.sadd "set", 'key2'
427
+ redis.sadd "set2", 'key2'
428
+ redis.sadd "set2", 'key3'
429
+ redis.sunionstore('newone', 'set', 'set2') { |r| r.should == 3 }
430
+ redis.smembers('newone') { |r| r.sort.should == ['key1','key2','key3'].sort; done }
431
+ end
432
+ end
433
+
434
+ it "takes a set difference (SDIFF)" do
435
+ connect do |redis|
436
+ redis.sadd "set", 'a'
437
+ redis.sadd "set", 'b'
438
+ redis.sadd "set2", 'b'
439
+ redis.sadd "set2", 'c'
440
+ redis.sdiff('set', 'set2') { |r| r.should == ['a']; done }
441
+ end
442
+ end
443
+
444
+ it "takes set difference and store the results in a key (SDIFFSTORE)" do
445
+ connect do |redis|
446
+ redis.sadd "set", 'a'
447
+ redis.sadd "set", 'b'
448
+ redis.sadd "set2", 'b'
449
+ redis.sadd "set2", 'c'
450
+ redis.sdiffstore('newone', 'set', 'set2')
451
+ redis.smembers('newone') { |r| r.should == ['a']; done }
452
+ end
453
+ end
454
+
455
+ it "moves elements from one set to another (SMOVE)" do
456
+ connect do |redis|
457
+ redis.sadd 'set1', 'a'
458
+ redis.sadd 'set1', 'b'
459
+ redis.sadd 'set2', 'x'
460
+ redis.smove('set1', 'set2', 'a') { |r| r.should == 1 }
461
+ redis.sismember('set2', 'a') { |r| r.should == 1 }
462
+ redis.del('set1') { done }
463
+ end
464
+ end
465
+
466
+ it "counts the members of a zset" do
467
+ connect do |redis|
468
+ redis.sadd "set", 'key1'
469
+ redis.sadd "set", 'key2'
470
+ redis.zadd 'zset', 1, 'set'
471
+ redis.zcount('zset') { |r| r.should == 1 }
472
+ redis.del('set')
473
+ redis.del('zset') { done }
474
+ end
475
+ end
476
+
477
+ it "adds members to a zset" do
478
+ connect do |redis|
479
+ redis.sadd "set", 'key1'
480
+ redis.sadd "set", 'key2'
481
+ redis.zadd 'zset', 1, 'set'
482
+ redis.zrange('zset', 0, 1) { |r| r.should == ['set'] }
483
+ redis.zcount('zset') { |r| r.should == 1 }
484
+ redis.del('set')
485
+ redis.del('zset') { done }
486
+ end
487
+ end
488
+
489
+ it "deletes members to a zset" do
490
+ connect do |redis|
491
+ redis.sadd "set", 'key1'
492
+ redis.sadd "set", 'key2'
493
+ redis.type?('set') { |r| r.should == "set" }
494
+ redis.sadd "set2", 'key3'
495
+ redis.sadd "set2", 'key4'
496
+ redis.type?('set2') { |r| r.should == "set" }
497
+ redis.zadd 'zset', 1, 'set'
498
+ redis.zcount('zset') { |r| r.should == 1 }
499
+ redis.zadd 'zset', 2, 'set2'
500
+ redis.zcount('zset') { |r| r.should == 2 }
501
+ redis.zset_delete 'zset', 'set'
502
+ redis.zcount('zset') { |r| r.should == 1 }
503
+ redis.del('set')
504
+ redis.del('set2')
505
+ redis.del('zset') { done }
506
+ end
507
+ end
508
+
509
+ it "gets a range of values from a zset" do
510
+ connect do |redis|
511
+ redis.sadd "set", 'key1'
512
+ redis.sadd "set", 'key2'
513
+ redis.sadd "set2", 'key3'
514
+ redis.sadd "set2", 'key4'
515
+ redis.sadd "set3", 'key1'
516
+ redis.type?('set') { |r| r.should == 'set' }
517
+ redis.type?('set2') { |r| r.should == 'set' }
518
+ redis.type?('set3') { |r| r.should == 'set' }
519
+ redis.zadd 'zset', 1, 'set'
520
+ redis.zadd 'zset', 2, 'set2'
521
+ redis.zadd 'zset', 3, 'set3'
522
+ redis.zcount('zset') { |r| r.should == 3 }
523
+ redis.zrange('zset', 0, 3) { |r| r.should == ['set', 'set2', 'set3'] }
524
+ redis.del('set')
525
+ redis.del('set2')
526
+ redis.del('set3')
527
+ redis.del('zset') { done }
528
+ end
529
+ end
530
+
531
+ it "gets a reverse range of values from a zset" do
532
+ connect do |redis|
533
+ redis.sadd "set", 'key1'
534
+ redis.sadd "set", 'key2'
535
+ redis.sadd "set2", 'key3'
536
+ redis.sadd "set2", 'key4'
537
+ redis.sadd "set3", 'key1'
538
+ redis.type?('set') { |r| r.should == 'set' }
539
+ redis.type?('set2') { |r| r.should == 'set' }
540
+ redis.type?('set3') { |r| r.should == 'set' }
541
+ redis.zadd 'zset', 1, 'set'
542
+ redis.zadd 'zset', 2, 'set2'
543
+ redis.zadd 'zset', 3, 'set3'
544
+ redis.zcount('zset') { |r| r.should == 3 }
545
+ redis.zrevrange('zset', 0, 3) { |r| r.should == ['set3', 'set2', 'set'] }
546
+ redis.del('set')
547
+ redis.del('set2')
548
+ redis.del('set3')
549
+ redis.del('zset') { done }
550
+ end
551
+ end
552
+
553
+ it "gets a range by score of values from a zset" do
554
+ connect do |redis|
555
+ redis.sadd "set", 'key1'
556
+ redis.sadd "set", 'key2'
557
+ redis.sadd "set2", 'key3'
558
+ redis.sadd "set2", 'key4'
559
+ redis.sadd "set3", 'key1'
560
+ redis.sadd "set4", 'key4'
561
+ redis.zadd 'zset', 1, 'set'
562
+ redis.zadd 'zset', 2, 'set2'
563
+ redis.zadd 'zset', 3, 'set3'
564
+ redis.zadd 'zset', 4, 'set4'
565
+ redis.zcount('zset') { |r| r.should == 4 }
566
+ redis.zrangebyscore('zset', 2, 3) { |r| r.should == ['set2', 'set3'] }
567
+ redis.del('set')
568
+ redis.del('set2')
569
+ redis.del('set3')
570
+ redis.del('set4')
571
+ redis.del('zset') { done }
572
+ end
573
+ end
574
+
575
+ it "gets a score for a specific value in a zset (ZSCORE)" do
576
+ connect do |redis|
577
+ redis.zadd "zset", 23, "value"
578
+ redis.zscore("zset", "value") { |r| r.should == "23" }
579
+
580
+ redis.zscore("zset", "value2") { |r| r.should == nil }
581
+ redis.zscore("unknown_zset", "value") { |r| r.should == nil }
582
+
583
+ redis.del("zset") { done }
584
+ end
585
+ end
586
+
587
+ it "increments a range score of a zset (ZINCRBY)" do
588
+ connect do |redis|
589
+ # create a new zset
590
+ redis.zincrby "hackers", 1965, "Yukihiro Matsumoto"
591
+ redis.zscore("hackers", "Yukihiro Matsumoto") { |r| r.should == "1965" }
592
+
593
+ # add a new element
594
+ redis.zincrby "hackers", 1912, "Alan Turing"
595
+ redis.zscore("hackers", "Alan Turing") { |r| r.should == "1912" }
596
+
597
+ # update the score
598
+ redis.zincrby "hackers", 100, "Alan Turing" # yeah, we are making Turing a bit younger
599
+ redis.zscore("hackers", "Alan Turing") { |r| r.should == "2012" }
600
+
601
+ # attempt to update a key that's not a zset
602
+ redis.set("i_am_not_a_zet", "value")
603
+ # shouldn't raise error anymore
604
+ redis.zincrby("i_am_not_a_zet", 23, "element") { |r| r.should == nil }
605
+
606
+ redis.del("hackers")
607
+ redis.del("i_am_not_a_zet") { done }
608
+ end
609
+ end
610
+
611
+ it "provides info (INFO)" do
612
+ connect do |redis|
613
+ redis.info do |r|
614
+ [:last_save_time, :redis_version, :total_connections_received, :connected_clients, :total_commands_processed, :connected_slaves, :uptime_in_seconds, :used_memory, :uptime_in_days, :changes_since_last_save].each do |x|
615
+ r.keys.include?(x).should == true
616
+ end
617
+ done
618
+ end
619
+ end
620
+ end
621
+
622
+ it "flushes the database (FLUSHDB)" do
623
+ connect do |redis|
624
+ redis.set('key1', 'keyone')
625
+ redis.set('key2', 'keytwo')
626
+ redis.keys('*') { |r| r.sort.should == ['key1', 'key2'].sort }
627
+ redis.flushdb
628
+ redis.keys('*') { |r| r.should == []; done }
629
+ end
630
+ end
631
+
632
+ it "SELECTs database" do
633
+ connect do |redis|
634
+ redis.set("foo", "bar") do |set_response|
635
+ redis.select("10") do |select_response|
636
+ redis.get("foo") do |get_response|
637
+ get_response.should == nil; done
638
+ end
639
+ end
640
+ end
641
+ end
642
+ end
643
+
644
+ it "SELECTs database without a callback" do
645
+ connect do |redis|
646
+ redis.select("9")
647
+ redis.incr("foo") do |response|
648
+ response.should == 1
649
+ done
650
+ end
651
+ end
652
+ end
653
+
654
+ it "provides the last save time (LASTSAVE)" do
655
+ connect do |redis|
656
+ redis.lastsave do |savetime|
657
+ Time.at(savetime).class.should == Time
658
+ Time.at(savetime).should <= Time.now
659
+ done
660
+ end
661
+ end
662
+ end
663
+
664
+ it "can MGET keys" do
665
+ connect do |redis|
666
+ redis.set('foo', 1000)
667
+ redis.set('bar', 2000)
668
+ redis.mget('foo', 'bar') { |r| r.should == ['1000', '2000'] }
669
+ redis.mget('foo', 'bar', 'baz') { |r| r.should == ['1000', '2000', nil] }
670
+ redis.ping { done }
671
+ end
672
+ end
673
+
674
+ it "can MSET values" do
675
+ connect do |redis|
676
+ redis.mset "key1", "value1", "key2", "value2"
677
+ redis.get('key1') { |r| r.should == "value1" }
678
+ redis.get('key2') { |r| r.should == "value2"; done }
679
+ end
680
+ end
681
+
682
+ it "can MSETNX values" do
683
+ connect do |redis|
684
+ redis.msetnx "keynx1", "valuenx1", "keynx2", "valuenx2"
685
+ redis.mget('keynx1', 'keynx2') { |r| r.should == ["valuenx1", "valuenx2"] }
686
+
687
+ redis.set("keynx1", "value1")
688
+ redis.set("keynx2", "value2")
689
+ redis.msetnx "keynx1", "valuenx1", "keynx2", "valuenx2"
690
+ redis.mget('keynx1', 'keynx2') { |r| r.should == ["value1", "value2"]; done }
691
+ end
692
+ end
693
+
694
+ it "can BGSAVE" do
695
+ connect do |redis|
696
+ redis.bgsave do |r|
697
+ ['OK', 'Background saving started'].include?(r).should == true
698
+ done
699
+ end
700
+ end
701
+ end
702
+
703
+ it "can ECHO" do
704
+ connect do |redis|
705
+ redis.echo("message in a bottle\n") { |r| r.should == "message in a bottle\n"; done }
706
+ end
707
+ end
708
+
709
+ it "runs MULTI without a block" do
710
+ connect do |redis|
711
+ redis.multi
712
+ redis.get("key1") { |r| r.should == "QUEUED" }
713
+ redis.discard { done }
714
+ end
715
+ end
716
+
717
+ it "runs MULTI/EXEC" do
718
+ connect do |redis|
719
+ redis.multi
720
+ redis.set "key1", "value1"
721
+ redis.exec
722
+
723
+ redis.get("key1") { |r| r.should == "value1" }
724
+
725
+ begin
726
+ redis.multi
727
+ redis.set "key2", "value2"
728
+ raise "Some error"
729
+ redis.set "key3", "value3"
730
+ redis.exec
731
+ rescue
732
+ redis.discard
733
+ end
734
+
735
+ redis.get("key2") { |r| r.should == nil }
736
+ redis.get("key3") { |r| r.should == nil; done}
737
+ end
738
+ end
739
+
740
+ it "sets and get hash values" do
741
+ connect do |redis|
742
+ redis.hset("rush", "signals", "1982") { |r| r.should == 1 }
743
+ redis.hexists("rush", "signals") { |r| r.should == 1 }
744
+ redis.hget("rush", "signals") { |r| r.should == "1982"; done }
745
+ end
746
+ end
747
+
748
+ it "deletes hash values" do
749
+ connect do |redis|
750
+ redis.hset("rush", "YYZ", "1981")
751
+ redis.hdel("rush", "YYZ") { |r| r.should == 1 }
752
+ redis.hexists("rush", "YYZ") { |r| r.should == 0; done }
753
+ end
754
+ end
755
+ end
756
+
757
+ describe EventMachine::Hiredis, "with hash values" do
758
+ def set(&blk)
759
+ connect do |redis|
760
+ redis.hset("rush", "permanent waves", "1980")
761
+ redis.hset("rush", "moving pictures", "1981")
762
+ redis.hset("rush", "signals", "1982")
763
+ blk.call(redis)
764
+ end
765
+ end
766
+
767
+ it "gets the length of the hash" do
768
+ set do |redis|
769
+ redis.hlen("rush") { |r| r.should == 3 }
770
+ redis.hlen("yyz") { |r| r.should == 0; done }
771
+ end
772
+ end
773
+
774
+ it "gets the keys and values of the hash" do
775
+ set do |redis|
776
+ redis.hkeys("rush") { |r| r.should == ["permanent waves", "moving pictures", "signals"] }
777
+ redis.hvals("rush") { |r| r.should == %w[1980 1981 1982] }
778
+ redis.hvals("yyz") { |r| r.should == []; done }
779
+ end
780
+ end
781
+
782
+ it "returns all hash values" do
783
+ set do |redis|
784
+ redis.hgetall("rush") do |r|
785
+ r.should == [
786
+ "permanent waves", "1980",
787
+ "moving pictures", "1981",
788
+ "signals" , "1982"
789
+ ]
790
+ end
791
+ redis.hgetall("yyz") { |r| r.should == []; done }
792
+ end
793
+ end
794
+ end
795
+
796
+ describe EventMachine::Hiredis, "with nested multi-bulk response" do
797
+ def set(&blk)
798
+ connect do |redis|
799
+ redis.set 'user:one:id', 'id-one'
800
+ redis.set 'user:two:id', 'id-two'
801
+ redis.sadd "user:one:interests", "first-interest"
802
+ redis.sadd "user:one:interests", "second-interest"
803
+ redis.sadd "user:two:interests", "third-interest"
804
+ blk.call(redis)
805
+ end
806
+ end
807
+
808
+ it "returns array of arrays" do
809
+ set do |redis|
810
+ redis.multi
811
+ redis.smembers "user:one:interests"
812
+ redis.smembers "user:two:interests"
813
+ redis.exec do |user_interests|
814
+ user_interests.should == [["second-interest", "first-interest"], ['third-interest']]
815
+ end
816
+ redis.mget("user:one:id", "user:two:id") do |user_ids|
817
+ user_ids.should == ['id-one', 'id-two']
818
+ done
819
+ end
820
+ end
821
+ end
822
+ end
823
+
824
+ describe EventMachine::Hiredis, "monitor" do
825
+ it "returns monitored commands" do
826
+ connect do |redis|
827
+ redis.monitor do |reply|
828
+ reply.should == "OK"
829
+ end
830
+ redis.on(:monitor) do |line|
831
+ line.should =~ /monitor/
832
+ done
833
+ end
834
+ end
835
+ end
836
+ end
837
+
838
+ describe EventMachine::Hiredis, "sorting" do
839
+ context "with some simple sorting data" do
840
+ def set(&blk)
841
+ connect do |redis|
842
+ redis.set('dog_1', 'louie')
843
+ redis.rpush 'Dogs', 1
844
+ redis.set('dog_2', 'lucy')
845
+ redis.rpush 'Dogs', 2
846
+ redis.set('dog_3', 'max')
847
+ redis.rpush 'Dogs', 3
848
+ redis.set('dog_4', 'taj')
849
+ redis.rpush 'Dogs', 4
850
+ blk.call(redis)
851
+ end
852
+ end
853
+
854
+ it "sorts with a limit" do
855
+ set do |redis|
856
+ redis.sort('Dogs', "GET", 'dog_*', "LIMIT", "0", "1") do |r|
857
+ r.should == ['louie']
858
+ done
859
+ end
860
+ end
861
+ end
862
+
863
+ it "sorts with a limit and order" do
864
+ set do |redis|
865
+ redis.sort('Dogs', "GET", 'dog_*', "LIMIT", "0", "1", "desc", "alpha") do |r|
866
+ r.should == ['taj']
867
+ done
868
+ end
869
+ end
870
+ end
871
+ end
872
+
873
+ context "with more complex sorting data" do
874
+ def set(&blk)
875
+ connect do |redis|
876
+ redis.set('dog:1:name', 'louie')
877
+ redis.set('dog:1:breed', 'mutt')
878
+ redis.rpush 'dogs', 1
879
+ redis.set('dog:2:name', 'lucy')
880
+ redis.set('dog:2:breed', 'poodle')
881
+ redis.rpush 'dogs', 2
882
+ redis.set('dog:3:name', 'max')
883
+ redis.set('dog:3:breed', 'hound')
884
+ redis.rpush 'dogs', 3
885
+ redis.set('dog:4:name', 'taj')
886
+ redis.set('dog:4:breed', 'terrier')
887
+ redis.rpush 'dogs', 4
888
+ blk.call(redis)
889
+ end
890
+ end
891
+
892
+ it "handles multiple GETs" do
893
+ set do |redis|
894
+ redis.sort('dogs', 'GET', 'dog:*:name', 'GET', 'dog:*:breed', 'LIMIT', '0', '1') do |r|
895
+ r.should == ['louie', 'mutt']
896
+ done
897
+ end
898
+ end
899
+ end
900
+
901
+ it "handles multiple GETs with an order" do
902
+ set do |redis|
903
+ redis.sort('dogs', 'GET', 'dog:*:name', 'GET', 'dog:*:breed', 'LIMIT', '0', '1', 'desc', 'alpha') do |r|
904
+ r.should == ['taj', 'terrier']
905
+ done
906
+ end
907
+ end
908
+ end
909
+ end
910
+ end