cs-em-hiredis 0.1.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -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