em-redis 0.2.2 → 0.2.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -3,8 +3,7 @@ require File.expand_path(File.dirname(__FILE__) + "/test_helper.rb")
3
3
  EM.describe EM::Protocols::Redis, "connected to an empty db" do
4
4
 
5
5
  before do
6
- @c = EM::Protocols::Redis.connect
7
- @c.select "14"
6
+ @c = EM::Protocols::Redis.connect :db => 14
8
7
  @c.flushdb
9
8
  end
10
9
 
@@ -54,14 +53,14 @@ EM.describe EM::Protocols::Redis, "connected to an empty db" do
54
53
 
55
54
  should "be able to 'lpush' to a nonexistent list" do
56
55
  @c.lpush("foo", "bar") do |r|
57
- r.should == "OK"
56
+ r.should == 1
58
57
  done
59
58
  end
60
59
  end
61
60
 
62
61
  should "be able to 'rpush' to a nonexistent list" do
63
62
  @c.rpush("foo", "bar") do |r|
64
- r.should == "OK"
63
+ r.should == 1
65
64
  done
66
65
  end
67
66
  end
@@ -107,8 +106,7 @@ end
107
106
  EM.describe EM::Protocols::Redis, "connected to a db containing some simple string-valued keys" do
108
107
 
109
108
  before do
110
- @c = EM::Protocols::Redis.connect
111
- @c.select "14"
109
+ @c = EM::Protocols::Redis.connect :db => 14
112
110
  @c.flushdb
113
111
  @c.set "a", "b"
114
112
  @c.set "x", "y"
@@ -206,8 +204,7 @@ end
206
204
  EM.describe EM::Protocols::Redis, "connected to a db containing a list" do
207
205
 
208
206
  before do
209
- @c = EM::Protocols::Redis.connect
210
- @c.select "14"
207
+ @c = EM::Protocols::Redis.connect :db => 14
211
208
  @c.flushdb
212
209
  @c.lpush "foo", "c"
213
210
  @c.lpush "foo", "b"
@@ -225,7 +222,7 @@ EM.describe EM::Protocols::Redis, "connected to a db containing a list" do
225
222
 
226
223
  should "be able to 'rpush' onto the tail of the list" do
227
224
  @c.rpush "foo", "d" do |r|
228
- r.should == "OK"
225
+ r.should == 4
229
226
  @c.rpop "foo" do |r|
230
227
  r.should == "d"
231
228
  done
@@ -235,7 +232,7 @@ EM.describe EM::Protocols::Redis, "connected to a db containing a list" do
235
232
 
236
233
  should "be able to 'lpush' onto the head of the list" do
237
234
  @c.lpush "foo", "d" do |r|
238
- r.should == "OK"
235
+ r.should == 4
239
236
  @c.lpop "foo" do |r|
240
237
  r.should == "d"
241
238
  done
@@ -295,8 +292,7 @@ end
295
292
 
296
293
  EM.describe EM::Protocols::Redis, "connected to a db containing two sets" do
297
294
  before do
298
- @c = EM::Protocols::Redis.connect
299
- @c.select "14"
295
+ @c = EM::Protocols::Redis.connect :db => 14
300
296
  @c.flushdb
301
297
  @c.sadd "foo", "a"
302
298
  @c.sadd "foo", "b"
@@ -402,8 +398,7 @@ end
402
398
 
403
399
  EM.describe EM::Protocols::Redis, "connected to a db containing three linked lists" do
404
400
  before do
405
- @c = EM::Protocols::Redis.connect
406
- @c.select "14"
401
+ @c = EM::Protocols::Redis.connect :db => 14
407
402
  @c.flushdb
408
403
  @c.rpush "foo", "a"
409
404
  @c.rpush "foo", "b"
@@ -422,7 +417,7 @@ EM.describe EM::Protocols::Redis, "connected to a db containing three linked lis
422
417
 
423
418
  should "be able to get keys selectively" do
424
419
  @c.keys "a_*" do |r|
425
- r.should == ["a_sort", "a_data"]
420
+ r.should == ["a_sort", "a_data"].sort
426
421
  done
427
422
  end
428
423
  end
@@ -430,8 +425,7 @@ end
430
425
 
431
426
  EM.describe EM::Protocols::Redis, "when reconnecting" do
432
427
  before do
433
- @c = EM::Protocols::Redis.connect
434
- @c.select "14"
428
+ @c = EM::Protocols::Redis.connect :db => 14
435
429
  @c.flushdb
436
430
  end
437
431
 
@@ -449,4 +443,4 @@ EM.describe EM::Protocols::Redis, "when reconnecting" do
449
443
  end
450
444
  end
451
445
  end
452
- end
446
+ end
@@ -0,0 +1,761 @@
1
+ require File.expand_path(File.dirname(__FILE__) + "/test_helper.rb")
2
+ require 'logger'
3
+
4
+ EM.describe EM::Protocols::Redis do
5
+ default_timeout 1
6
+
7
+ before do
8
+ @r = EM::Protocols::Redis.connect :db => 14
9
+ @r.flushdb
10
+ @r['foo'] = 'bar'
11
+ end
12
+
13
+ after { @r.close_connection }
14
+
15
+ should "be able to provide a logger" do
16
+ log = StringIO.new
17
+ r = EM::Protocols::Redis.connect :db => 14, :logger => Logger.new(log)
18
+ r.ping do
19
+ log.string.should.include "ping"
20
+ done
21
+ end
22
+ end
23
+
24
+ it "should be able to PING" do
25
+ @r.ping { |r| r.should == 'PONG'; done }
26
+ end
27
+
28
+ it "should be able to GET a key" do
29
+ @r.get('foo') { |r| r.should == 'bar'; done }
30
+ end
31
+
32
+ it "should be able to SET a key" do
33
+ @r['foo'] = 'nik'
34
+ @r.get('foo') { |r| r.should == 'nik'; done }
35
+ end
36
+
37
+ it "should properly handle trailing newline characters" do
38
+ @r['foo'] = "bar\n"
39
+ @r.get('foo') { |r| r.should == "bar\n"; done }
40
+ end
41
+
42
+ it "should store and retrieve all possible characters at the beginning and the end of a string" do
43
+ (0..255).each do |char_idx|
44
+ string = "#{char_idx.chr}---#{char_idx.chr}"
45
+ @r['foo'] = string
46
+ @r.get('foo') { |r| r.should == string }
47
+ end
48
+ @r.ping { done }
49
+ end
50
+
51
+ it "should be able to SET a key with an expiry" do
52
+ timeout(3)
53
+
54
+ @r.set('foo', 'bar', 1)
55
+ @r.get('foo') { |r| r.should == 'bar' }
56
+ EM.add_timer(2) do
57
+ @r.get('foo') { |r| r.should == nil }
58
+ @r.ping { done }
59
+ end
60
+ end
61
+
62
+ it "should be able to return a TTL for a key" do
63
+ @r.set('foo', 'bar', 1)
64
+ @r.ttl('foo') { |r| r.should == 1; done }
65
+ end
66
+
67
+ it "should be able to SETNX" do
68
+ @r['foo'] = 'nik'
69
+ @r.get('foo') { |r| r.should == 'nik' }
70
+ @r.setnx 'foo', 'bar'
71
+ @r.get('foo') { |r| r.should == 'nik' }
72
+
73
+ @r.ping { done }
74
+ end
75
+ #
76
+ it "should be able to GETSET" do
77
+ @r.getset('foo', 'baz') { |r| r.should == 'bar' }
78
+ @r.get('foo') { |r| r.should == 'baz'; done }
79
+ end
80
+ #
81
+ it "should be able to INCR a key" do
82
+ @r.del('counter')
83
+ @r.incr('counter') { |r| r.should == 1 }
84
+ @r.incr('counter') { |r| r.should == 2 }
85
+ @r.incr('counter') { |r| r.should == 3 }
86
+
87
+ @r.ping { done }
88
+ end
89
+ #
90
+ it "should be able to INCRBY a key" do
91
+ @r.del('counter')
92
+ @r.incrby('counter', 1) { |r| r.should == 1 }
93
+ @r.incrby('counter', 2) { |r| r.should == 3 }
94
+ @r.incrby('counter', 3) { |r| r.should == 6 }
95
+
96
+ @r.ping { done }
97
+ end
98
+ #
99
+ it "should be able to DECR a key" do
100
+ @r.del('counter')
101
+ @r.incr('counter') { |r| r.should == 1 }
102
+ @r.incr('counter') { |r| r.should == 2 }
103
+ @r.incr('counter') { |r| r.should == 3 }
104
+ @r.decr('counter') { |r| r.should == 2 }
105
+ @r.decr('counter', 2) { |r| r.should == 0; done }
106
+ end
107
+ #
108
+ it "should be able to RANDKEY" do
109
+ @r.randkey { |r| r.should.not == nil; done }
110
+ end
111
+ #
112
+ it "should be able to RENAME a key" do
113
+ @r.del 'foo'
114
+ @r.del 'bar'
115
+ @r['foo'] = 'hi'
116
+ @r.rename 'foo', 'bar'
117
+ @r.get('bar') { |r| r.should == 'hi' ; done }
118
+ end
119
+ #
120
+ it "should be able to RENAMENX a key" do
121
+ @r.del 'foo'
122
+ @r.del 'bar'
123
+ @r['foo'] = 'hi'
124
+ @r['bar'] = 'ohai'
125
+ @r.renamenx 'foo', 'bar'
126
+ @r.get('bar') { |r| r.should == 'ohai' ; done }
127
+ end
128
+ #
129
+ it "should be able to get DBSIZE of the database" do
130
+ dbsize_without_foo, dbsize_with_foo = nil
131
+ @r.delete 'foo'
132
+ @r.dbsize { |r| dbsize_without_foo = r }
133
+ @r['foo'] = 0
134
+ @r.dbsize { |r| dbsize_with_foo = r }
135
+
136
+ @r.ping do
137
+ dbsize_with_foo.should == dbsize_without_foo + 1
138
+ done
139
+ end
140
+ end
141
+ #
142
+ it "should be able to EXPIRE a key" do
143
+ timeout(3)
144
+
145
+ @r['foo'] = 'bar'
146
+ @r.expire 'foo', 1
147
+ @r.get('foo') { |r| r.should == "bar" }
148
+ EM.add_timer(2) do
149
+ @r.get('foo') { |r| r.should == nil }
150
+ @r.ping { done }
151
+ end
152
+ end
153
+ #
154
+ it "should be able to EXISTS" do
155
+ @r['foo'] = 'nik'
156
+ @r.exists('foo') { |r| r.should == true }
157
+ @r.del 'foo'
158
+ @r.exists('foo') { |r| r.should == false ; done }
159
+ end
160
+ #
161
+ it "should be able to KEYS" do
162
+ @r.keys("f*") { |keys| keys.each { |key| @r.del key } }
163
+ @r['f'] = 'nik'
164
+ @r['fo'] = 'nak'
165
+ @r['foo'] = 'qux'
166
+ @r.keys("f*") { |r| r.sort.should == ['f', 'fo', 'foo'].sort }
167
+
168
+ @r.ping { done }
169
+ end
170
+ #
171
+ it "should be able to return a random key (RANDOMKEY)" do
172
+ 3.times do |i|
173
+ @r.randomkey do |r|
174
+ @r.exists(r) do |e|
175
+ e.should == true
176
+ done if i == 2
177
+ end
178
+ end
179
+ end
180
+ end
181
+ #
182
+ it "should be able to check the TYPE of a key" do
183
+ @r['foo'] = 'nik'
184
+ @r.type('foo') { |r| r.should == "string" }
185
+ @r.del 'foo'
186
+ @r.type('foo') { |r| r.should == "none" ; done }
187
+ end
188
+ #
189
+ it "should be able to push to the head of a list (LPUSH)" do
190
+ @r.lpush "list", 'hello'
191
+ @r.lpush "list", 42
192
+ @r.type('list') { |r| r.should == "list" }
193
+ @r.llen('list') { |r| r.should == 2 }
194
+ @r.lpop('list') { |r| r.should == '42'; done }
195
+ end
196
+ #
197
+ it "should be able to push to the tail of a list (RPUSH)" do
198
+ @r.rpush "list", 'hello'
199
+ @r.type('list') { |r| r.should == "list" }
200
+ @r.llen('list') { |r| r.should == 1 ; done }
201
+ end
202
+ #
203
+ it "should be able to pop the tail of a list (RPOP)" do
204
+ @r.rpush "list", 'hello'
205
+ @r.rpush"list", 'goodbye'
206
+ @r.type('list') { |r| r.should == "list" }
207
+ @r.llen('list') { |r| r.should == 2 }
208
+ @r.rpop('list') { |r| r.should == 'goodbye'; done }
209
+ end
210
+ #
211
+ it "should be able to pop the head of a list (LPOP)" do
212
+ @r.rpush "list", 'hello'
213
+ @r.rpush "list", 'goodbye'
214
+ @r.type('list') { |r| r.should == "list" }
215
+ @r.llen('list') { |r| r.should == 2 }
216
+ @r.lpop('list') { |r| r.should == 'hello'; done }
217
+ end
218
+ #
219
+ it "should be able to get the length of a list (LLEN)" do
220
+ @r.rpush "list", 'hello'
221
+ @r.rpush "list", 'goodbye'
222
+ @r.type('list') { |r| r.should == "list" }
223
+ @r.llen('list') { |r| r.should == 2 ; done }
224
+ end
225
+ #
226
+ it "should be able to get a range of values from a list (LRANGE)" do
227
+ @r.rpush "list", 'hello'
228
+ @r.rpush "list", 'goodbye'
229
+ @r.rpush "list", '1'
230
+ @r.rpush "list", '2'
231
+ @r.rpush "list", '3'
232
+ @r.type('list') { |r| r.should == "list" }
233
+ @r.llen('list') { |r| r.should == 5 }
234
+ @r.lrange('list', 2, -1) { |r| r.should == ['1', '2', '3']; done }
235
+ end
236
+ #
237
+ it "should be able to trim a list (LTRIM)" do
238
+ @r.rpush "list", 'hello'
239
+ @r.rpush "list", 'goodbye'
240
+ @r.rpush "list", '1'
241
+ @r.rpush "list", '2'
242
+ @r.rpush "list", '3'
243
+ @r.type('list') { |r| r.should == "list" }
244
+ @r.llen('list') { |r| r.should == 5 }
245
+ @r.ltrim 'list', 0, 1
246
+ @r.llen('list') { |r| r.should == 2 }
247
+ @r.lrange('list', 0, -1) { |r| r.should == ['hello', 'goodbye']; done }
248
+ end
249
+ #
250
+ it "should be able to get a value by indexing into a list (LINDEX)" do
251
+ @r.rpush "list", 'hello'
252
+ @r.rpush "list", 'goodbye'
253
+ @r.type('list') { |r| r.should == "list" }
254
+ @r.llen('list') { |r| r.should == 2 }
255
+ @r.lindex('list', 1) { |r| r.should == 'goodbye'; done }
256
+ end
257
+ #
258
+ it "should be able to set a value by indexing into a list (LSET)" do
259
+ @r.rpush "list", 'hello'
260
+ @r.rpush "list", 'hello'
261
+ @r.type('list') { |r| r.should == "list" }
262
+ @r.llen('list') { |r| r.should == 2 }
263
+ @r.lset('list', 1, 'goodbye') { |r| r.should == 'OK' }
264
+ @r.lindex('list', 1) { |r| r.should == 'goodbye'; done }
265
+ end
266
+ #
267
+ it "should be able to remove values from a list (LREM)" do
268
+ @r.rpush "list", 'hello'
269
+ @r.rpush "list", 'goodbye'
270
+ @r.type('list') { |r| r.should == "list" }
271
+ @r.llen('list') { |r| r.should == 2 }
272
+ @r.lrem('list', 1, 'hello') { |r| r.should == 1 }
273
+ @r.lrange('list', 0, -1) { |r| r.should == ['goodbye']; done }
274
+ end
275
+
276
+ it "should be able to pop values from a list and push them onto a temp list(RPOPLPUSH)" do
277
+ @r.rpush "list", 'one'
278
+ @r.rpush "list", 'two'
279
+ @r.rpush "list", 'three'
280
+ @r.type('list') { |r| r.should == "list" }
281
+ @r.llen('list') { |r| r.should == 3 }
282
+ @r.lrange('list', 0, -1) { |r| r.should == ['one', 'two', 'three'] }
283
+ @r.lrange('tmp', 0, -1) { |r| r.should == [] }
284
+ @r.rpoplpush('list', 'tmp') { |r| r.should == 'three' }
285
+ @r.lrange('tmp', 0, -1) { |r| r.should == ['three'] }
286
+ @r.rpoplpush('list', 'tmp') { |r| r.should == 'two' }
287
+ @r.lrange('tmp', 0, -1) { |r| r.should == ['two', 'three'] }
288
+ @r.rpoplpush('list', 'tmp') { |r| r.should == 'one' }
289
+ @r.lrange('tmp', 0, -1) { |r| r.should == ['one', 'two', 'three']; done }
290
+ end
291
+ #
292
+ it "should be able add members to a set (SADD)" do
293
+ @r.sadd "set", 'key1'
294
+ @r.sadd "set", 'key2'
295
+ @r.type('set') { |r| r.should == "set" }
296
+ @r.scard('set') { |r| r.should == 2 }
297
+ @r.smembers('set') { |r| r.sort.should == ['key1', 'key2'].sort; done }
298
+ end
299
+ #
300
+ it "should be able delete members to a set (SREM)" do
301
+ @r.sadd "set", 'key1'
302
+ @r.sadd "set", 'key2'
303
+ @r.type('set') { |r| r.should == "set" }
304
+ @r.scard('set') { |r| r.should == 2 }
305
+ @r.smembers('set') { |r| r.sort.should == ['key1', 'key2'].sort }
306
+ @r.srem('set', 'key1')
307
+ @r.scard('set') { |r| r.should == 1 }
308
+ @r.smembers('set') { |r| r.should == ['key2']; done }
309
+ end
310
+ #
311
+ it "should be able to return and remove random key from set (SPOP)" do
312
+ @r.sadd "set_pop", "key1"
313
+ @r.sadd "set_pop", "key2"
314
+ @r.spop("set_pop") { |r| r.should.not == nil }
315
+ @r.scard("set_pop") { |r| r.should == 1; done }
316
+ end
317
+ #
318
+ it "should be able to return random key without delete the key from a set (SRANDMEMBER)" do
319
+ @r.sadd "set_srandmember", "key1"
320
+ @r.sadd "set_srandmember", "key2"
321
+ @r.srandmember("set_srandmember") { |r| r.should.not == nil }
322
+ @r.scard("set_srandmember") { |r| r.should == 2; done }
323
+ end
324
+ #
325
+ it "should be able count the members of a set (SCARD)" do
326
+ @r.sadd "set", 'key1'
327
+ @r.sadd "set", 'key2'
328
+ @r.type('set') { |r| r.should == "set" }
329
+ @r.scard('set') { |r| r.should == 2; done }
330
+ end
331
+ #
332
+ it "should be able test for set membership (SISMEMBER)" do
333
+ @r.sadd "set", 'key1'
334
+ @r.sadd "set", 'key2'
335
+ @r.type('set') { |r| r.should == "set" }
336
+ @r.scard('set') { |r| r.should == 2 }
337
+ @r.sismember('set', 'key1') { |r| r.should == true }
338
+ @r.sismember('set', 'key2') { |r| r.should == true }
339
+ @r.sismember('set', 'notthere') { |r| r.should == false; done }
340
+ end
341
+ #
342
+ it "should be able to do set intersection (SINTER)" do
343
+ @r.sadd "set", 'key1'
344
+ @r.sadd "set", 'key2'
345
+ @r.sadd "set2", 'key2'
346
+ @r.sinter('set', 'set2') { |r| r.should == ['key2']; done }
347
+ end
348
+ #
349
+ it "should be able to do set intersection and store the results in a key (SINTERSTORE)" do
350
+ @r.sadd "set", 'key1'
351
+ @r.sadd "set", 'key2'
352
+ @r.sadd "set2", 'key2'
353
+ @r.sinterstore('newone', 'set', 'set2') { |r| r.should == 1 }
354
+ @r.smembers('newone') { |r| r.should == ['key2']; done }
355
+ end
356
+ #
357
+ it "should be able to do set union (SUNION)" do
358
+ @r.sadd "set", 'key1'
359
+ @r.sadd "set", 'key2'
360
+ @r.sadd "set2", 'key2'
361
+ @r.sadd "set2", 'key3'
362
+ @r.sunion('set', 'set2') { |r| r.sort.should == ['key1','key2','key3'].sort; done }
363
+ end
364
+ #
365
+ it "should be able to do set union and store the results in a key (SUNIONSTORE)" do
366
+ @r.sadd "set", 'key1'
367
+ @r.sadd "set", 'key2'
368
+ @r.sadd "set2", 'key2'
369
+ @r.sadd "set2", 'key3'
370
+ @r.sunionstore('newone', 'set', 'set2') { |r| r.should == 3 }
371
+ @r.smembers('newone') { |r| r.sort.should == ['key1','key2','key3'].sort; done }
372
+ end
373
+ #
374
+ it "should be able to do set difference (SDIFF)" do
375
+ @r.sadd "set", 'a'
376
+ @r.sadd "set", 'b'
377
+ @r.sadd "set2", 'b'
378
+ @r.sadd "set2", 'c'
379
+ @r.sdiff('set', 'set2') { |r| r.should == ['a']; done }
380
+ end
381
+ #
382
+ it "should be able to do set difference and store the results in a key (SDIFFSTORE)" do
383
+ @r.sadd "set", 'a'
384
+ @r.sadd "set", 'b'
385
+ @r.sadd "set2", 'b'
386
+ @r.sadd "set2", 'c'
387
+ @r.sdiffstore('newone', 'set', 'set2')
388
+ @r.smembers('newone') { |r| r.should == ['a']; done }
389
+ end
390
+ #
391
+ it "should be able move elements from one set to another (SMOVE)" do
392
+ @r.sadd 'set1', 'a'
393
+ @r.sadd 'set1', 'b'
394
+ @r.sadd 'set2', 'x'
395
+ @r.smove('set1', 'set2', 'a') { |r| r.should == true }
396
+ @r.sismember('set2', 'a') { |r| r.should == true }
397
+ @r.delete('set1') { done }
398
+ end
399
+ #
400
+ it "should be able to do crazy SORT queries" do
401
+ # The 'Dogs' is capitialized on purpose
402
+ @r['dog_1'] = 'louie'
403
+ @r.rpush 'Dogs', 1
404
+ @r['dog_2'] = 'lucy'
405
+ @r.rpush 'Dogs', 2
406
+ @r['dog_3'] = 'max'
407
+ @r.rpush 'Dogs', 3
408
+ @r['dog_4'] = 'taj'
409
+ @r.rpush 'Dogs', 4
410
+ @r.sort('Dogs', :get => 'dog_*', :limit => [0,1]) { |r| r.should == ['louie'] }
411
+ @r.sort('Dogs', :get => 'dog_*', :limit => [0,1], :order => 'desc alpha') { |r| r.should == ['taj'] }
412
+ @r.ping { done }
413
+ end
414
+
415
+ it "should be able to handle array of :get using SORT" do
416
+ @r['dog:1:name'] = 'louie'
417
+ @r['dog:1:breed'] = 'mutt'
418
+ @r.rpush 'dogs', 1
419
+ @r['dog:2:name'] = 'lucy'
420
+ @r['dog:2:breed'] = 'poodle'
421
+ @r.rpush 'dogs', 2
422
+ @r['dog:3:name'] = 'max'
423
+ @r['dog:3:breed'] = 'hound'
424
+ @r.rpush 'dogs', 3
425
+ @r['dog:4:name'] = 'taj'
426
+ @r['dog:4:breed'] = 'terrier'
427
+ @r.rpush 'dogs', 4
428
+ @r.sort('dogs', :get => ['dog:*:name', 'dog:*:breed'], :limit => [0,1]) { |r| r.should == ['louie', 'mutt'] }
429
+ @r.sort('dogs', :get => ['dog:*:name', 'dog:*:breed'], :limit => [0,1], :order => 'desc alpha') { |r| r.should == ['taj', 'terrier'] }
430
+ @r.ping { done }
431
+ end
432
+ #
433
+ it "should be able count the members of a zset" do
434
+ @r.set_add "set", 'key1'
435
+ @r.set_add "set", 'key2'
436
+ @r.zset_add 'zset', 1, 'set'
437
+ @r.zset_count('zset') { |r| r.should == 1 }
438
+ @r.delete('set')
439
+ @r.delete('zset') { done }
440
+ end
441
+ #
442
+ it "should be able add members to a zset" do
443
+ @r.set_add "set", 'key1'
444
+ @r.set_add "set", 'key2'
445
+ @r.zset_add 'zset', 1, 'set'
446
+ @r.zset_range('zset', 0, 1) { |r| r.should == ['set'] }
447
+ @r.zset_count('zset') { |r| r.should == 1 }
448
+ @r.delete('set')
449
+ @r.delete('zset') { done }
450
+ end
451
+ #
452
+ it "should be able delete members to a zset" do
453
+ @r.set_add "set", 'key1'
454
+ @r.set_add "set", 'key2'
455
+ @r.type?('set') { |r| r.should == "set" }
456
+ @r.set_add "set2", 'key3'
457
+ @r.set_add "set2", 'key4'
458
+ @r.type?('set2') { |r| r.should == "set" }
459
+ @r.zset_add 'zset', 1, 'set'
460
+ @r.zset_count('zset') { |r| r.should == 1 }
461
+ @r.zset_add 'zset', 2, 'set2'
462
+ @r.zset_count('zset') { |r| r.should == 2 }
463
+ @r.zset_delete 'zset', 'set'
464
+ @r.zset_count('zset') { |r| r.should == 1 }
465
+ @r.delete('set')
466
+ @r.delete('set2')
467
+ @r.delete('zset') { done }
468
+ end
469
+ #
470
+ it "should be able to get a range of values from a zset" do
471
+ @r.set_add "set", 'key1'
472
+ @r.set_add "set", 'key2'
473
+ @r.set_add "set2", 'key3'
474
+ @r.set_add "set2", 'key4'
475
+ @r.set_add "set3", 'key1'
476
+ @r.type?('set') { |r| r.should == 'set' }
477
+ @r.type?('set2') { |r| r.should == 'set' }
478
+ @r.type?('set3') { |r| r.should == 'set' }
479
+ @r.zset_add 'zset', 1, 'set'
480
+ @r.zset_add 'zset', 2, 'set2'
481
+ @r.zset_add 'zset', 3, 'set3'
482
+ @r.zset_count('zset') { |r| r.should == 3 }
483
+ @r.zset_range('zset', 0, 3) { |r| r.should == ['set', 'set2', 'set3'] }
484
+ @r.delete('set')
485
+ @r.delete('set2')
486
+ @r.delete('set3')
487
+ @r.delete('zset') { done }
488
+ end
489
+ #
490
+ it "should be able to get a reverse range of values from a zset" do
491
+ @r.set_add "set", 'key1'
492
+ @r.set_add "set", 'key2'
493
+ @r.set_add "set2", 'key3'
494
+ @r.set_add "set2", 'key4'
495
+ @r.set_add "set3", 'key1'
496
+ @r.type?('set') { |r| r.should == 'set' }
497
+ @r.type?('set2') { |r| r.should == 'set' }
498
+ @r.type?('set3') { |r| r.should == 'set' }
499
+ @r.zset_add 'zset', 1, 'set'
500
+ @r.zset_add 'zset', 2, 'set2'
501
+ @r.zset_add 'zset', 3, 'set3'
502
+ @r.zset_count('zset') { |r| r.should == 3 }
503
+ @r.zset_reverse_range('zset', 0, 3) { |r| r.should == ['set3', 'set2', 'set'] }
504
+ @r.delete('set')
505
+ @r.delete('set2')
506
+ @r.delete('set3')
507
+ @r.delete('zset') { done }
508
+ end
509
+ #
510
+ it "should be able to get a range by score of values from a zset" do
511
+ @r.set_add "set", 'key1'
512
+ @r.set_add "set", 'key2'
513
+ @r.set_add "set2", 'key3'
514
+ @r.set_add "set2", 'key4'
515
+ @r.set_add "set3", 'key1'
516
+ @r.set_add "set4", 'key4'
517
+ @r.zset_add 'zset', 1, 'set'
518
+ @r.zset_add 'zset', 2, 'set2'
519
+ @r.zset_add 'zset', 3, 'set3'
520
+ @r.zset_add 'zset', 4, 'set4'
521
+ @r.zset_count('zset') { |r| r.should == 4 }
522
+ @r.zset_range_by_score('zset', 2, 3) { |r| r.should == ['set2', 'set3'] }
523
+ @r.delete('set')
524
+ @r.delete('set2')
525
+ @r.delete('set3')
526
+ @r.delete('set4')
527
+ @r.delete('zset') { done }
528
+ end
529
+ #
530
+ it "should be able to get a score for a specific value in a zset (ZSCORE)" do
531
+ @r.zset_add "zset", 23, "value"
532
+ @r.zset_score("zset", "value") { |r| r.should == "23" }
533
+
534
+ @r.zset_score("zset", "value2") { |r| r.should == nil }
535
+ @r.zset_score("unknown_zset", "value") { |r| r.should == nil }
536
+
537
+ @r.delete("zset") { done }
538
+ end
539
+ #
540
+ it "should be able to increment a range score of a zset (ZINCRBY)" do
541
+ # create a new zset
542
+ @r.zset_increment_by "hackers", 1965, "Yukihiro Matsumoto"
543
+ @r.zset_score("hackers", "Yukihiro Matsumoto") { |r| r.should == "1965" }
544
+
545
+ # add a new element
546
+ @r.zset_increment_by "hackers", 1912, "Alan Turing"
547
+ @r.zset_score("hackers", "Alan Turing") { |r| r.should == "1912" }
548
+
549
+ # update the score
550
+ @r.zset_increment_by "hackers", 100, "Alan Turing" # yeah, we are making Turing a bit younger
551
+ @r.zset_score("hackers", "Alan Turing") { |r| r.should == "2012" }
552
+
553
+ # attempt to update a key that's not a zset
554
+ @r["i_am_not_a_zet"] = "value"
555
+ # should raise error
556
+ @r.on_error { true.should == true }
557
+ @r.zset_incr_by("i_am_not_a_zet", 23, "element") { false.should == true }
558
+
559
+ @r.delete("hackers")
560
+ @r.delete("i_am_not_a_zet") { done }
561
+ end
562
+ #
563
+ it "should provide info (INFO)" do
564
+ @r.info do |r|
565
+ [: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|
566
+ r.keys.include?(x).should == true
567
+ end
568
+ done
569
+ end
570
+ end
571
+ #
572
+ it "should be able to flush the database (FLUSHDB)" do
573
+ @r['key1'] = 'keyone'
574
+ @r['key2'] = 'keytwo'
575
+ @r.keys('*') { |r| r.sort.should == ['foo', 'key1', 'key2'].sort } #foo from before
576
+ @r.flushdb
577
+ @r.keys('*') { |r| r.should == []; done }
578
+ end
579
+ #
580
+ it "should be able to SELECT database" do
581
+ @r.select(15)
582
+ @r.get('foo') { |r| r.should == nil; done }
583
+ end
584
+ #
585
+ it "should be able to provide the last save time (LASTSAVE)" do
586
+ @r.lastsave do |savetime|
587
+ Time.at(savetime).class.should == Time
588
+ Time.at(savetime).should <= Time.now
589
+ done
590
+ end
591
+ end
592
+
593
+ it "should be able to MGET keys" do
594
+ @r['foo'] = 1000
595
+ @r['bar'] = 2000
596
+ @r.mget('foo', 'bar') { |r| r.should == ['1000', '2000'] }
597
+ @r.mget('foo', 'bar', 'baz') { |r| r.should == ['1000', '2000', nil] }
598
+ @r.ping { done }
599
+ end
600
+
601
+ it "should be able to mapped MGET keys" do
602
+ @r['foo'] = 1000
603
+ @r['bar'] = 2000
604
+ @r.mapped_mget('foo', 'bar') { |r| r.should == { 'foo' => '1000', 'bar' => '2000'} }
605
+ @r.mapped_mget('foo', 'baz', 'bar') { |r| r.should == { 'foo' => '1000', 'bar' => '2000'} }
606
+ @r.ping { done }
607
+ end
608
+
609
+ it "should be able to MSET values" do
610
+ @r.mset :key1 => "value1", :key2 => "value2"
611
+ @r.get('key1') { |r| r.should == "value1" }
612
+ @r.get('key2') { |r| r.should == "value2"; done }
613
+ end
614
+
615
+ it "should be able to MSETNX values" do
616
+ @r.msetnx :keynx1 => "valuenx1", :keynx2 => "valuenx2"
617
+ @r.mget('keynx1', 'keynx2') { |r| r.should == ["valuenx1", "valuenx2"] }
618
+
619
+ @r["keynx1"] = "value1"
620
+ @r["keynx2"] = "value2"
621
+ @r.msetnx :keynx1 => "valuenx1", :keynx2 => "valuenx2"
622
+ @r.mget('keynx1', 'keynx2') { |r| r.should == ["value1", "value2"]; done }
623
+ end
624
+
625
+ it "should bgsave" do
626
+ @r.bgsave do |r|
627
+ ['OK', 'Background saving started'].include?(r).should == true
628
+ done
629
+ end
630
+ end
631
+
632
+ it "should be able to ECHO" do
633
+ @r.echo("message in a bottle\n") { |r| r.should == "message in a bottle\n"; done }
634
+ end
635
+
636
+ # Tests are disabled due uncatchable exceptions. We should use on_error callback,
637
+ # intead of raising exceptions in random places.
638
+ #
639
+ # it "should raise error when invoke MONITOR" do
640
+ # # lambda { @r.monitor }.should.raise
641
+ # done
642
+ # end
643
+ #
644
+ # it "should raise error when invoke SYNC" do
645
+ # # lambda { @r.sync }.should.raise
646
+ # done
647
+ # end
648
+
649
+ it "should run MULTI without a block" do
650
+ @r.multi
651
+ @r.get("key1") { |r| r.should == "QUEUED" }
652
+ @r.discard { done }
653
+ end
654
+
655
+ it "should run MULTI/EXEC with a block" do
656
+ @r.multi do
657
+ @r.set "key1", "value1"
658
+ end
659
+
660
+ @r.get("key1") { |r| r.should == "value1" }
661
+
662
+ begin
663
+ @r.multi do
664
+ @r.set "key2", "value2"
665
+ raise "Some error"
666
+ @r.set "key3", "value3"
667
+ end
668
+ rescue
669
+ end
670
+
671
+ @r.get("key2") { |r| r.should == nil }
672
+ @r.get("key3") { |r| r.should == nil; done}
673
+ end
674
+
675
+ it "should yield the Redis object when using #multi with a block" do
676
+ @r.multi do |multi|
677
+ multi.set "key1", "value1"
678
+ end
679
+
680
+ @r.get("key1") { |r| r.should == "value1"; done }
681
+ end
682
+
683
+ it "can set and get hash values" do
684
+ @r.hset("rush", "signals", "1982") { |r| r.should == true }
685
+ @r.hexists("rush", "signals") { |r| r.should == true }
686
+ @r.hget("rush", "signals") { |r| r.should == "1982"; done }
687
+ end
688
+
689
+ it "can delete hash values" do
690
+ @r.hset("rush", "YYZ", "1981")
691
+ @r.hdel("rush", "YYZ") { |r| r.should == true }
692
+ @r.hexists("rush", "YYZ") { |r| r.should == false; done }
693
+ end
694
+ end
695
+
696
+ # Yup, bacon can't handle nested describe blocks properly
697
+ EM.describe EM::Protocols::Redis, "with some hash values" do
698
+ default_timeout 1
699
+
700
+ before do
701
+ @r = EM::Protocols::Redis.connect :db => 14
702
+ @r.flushdb
703
+ @r['foo'] = 'bar'
704
+ @r.hset("rush", "permanent waves", "1980")
705
+ @r.hset("rush", "moving pictures", "1981")
706
+ @r.hset("rush", "signals", "1982")
707
+ end
708
+
709
+ after { @r.close_connection }
710
+
711
+ it "can get the length of the hash" do
712
+ @r.hlen("rush") { |r| r.should == 3 }
713
+ @r.hlen("yyz") { |r| r.should == 0; done }
714
+ end
715
+
716
+ it "can get the keys and values of the hash" do
717
+ @r.hkeys("rush") { |r| r.should == ["permanent waves", "moving pictures", "signals"] }
718
+ @r.hvals("rush") { |r| r.should == %w[1980 1981 1982] }
719
+ @r.hvals("yyz") { |r| r.should == []; done }
720
+ end
721
+
722
+ it "returns a hash for HGETALL" do
723
+ @r.hgetall("rush") do |r|
724
+ r.should == {
725
+ "permanent waves" => "1980",
726
+ "moving pictures" => "1981",
727
+ "signals" => "1982"
728
+ }
729
+ end
730
+ @r.hgetall("yyz") { |r| r.should == {}; done }
731
+ end
732
+ end
733
+
734
+ EM.describe EM::Protocols::Redis, "with nested multi-bulk response" do
735
+ default_timeout 1
736
+
737
+ before do
738
+ @r = EM::Protocols::Redis.connect :db => 14
739
+ @r.flushdb
740
+ @r.set 'user:one:id', 'id-one'
741
+ @r.set 'user:two:id', 'id-two'
742
+ @r.sadd "user:one:interests", "first-interest"
743
+ @r.sadd "user:one:interests", "second-interest"
744
+ @r.sadd "user:two:interests", "third-interest"
745
+ end
746
+
747
+ after { @r.close_connection }
748
+
749
+ it "returns array of arrays" do
750
+ @r.multi
751
+ @r.smembers "user:one:interests"
752
+ @r.smembers "user:two:interests"
753
+ @r.exec do |user_interests|
754
+ user_interests.should == [["second-interest", "first-interest"], ['third-interest']]
755
+ end
756
+ @r.mget("user:one:id", "user:two:id") do |user_ids|
757
+ user_ids.should == ['id-one', 'id-two']
758
+ done
759
+ end
760
+ end
761
+ end