]> git.saurik.com Git - redis.git/blob - client-libraries/ruby/spec/redis_spec.rb
client libraries updated
[redis.git] / client-libraries / ruby / spec / redis_spec.rb
1 require File.dirname(__FILE__) + '/spec_helper'
2
3 class Foo
4 attr_accessor :bar
5 def initialize(bar)
6 @bar = bar
7 end
8
9 def ==(other)
10 @bar == other.bar
11 end
12 end
13
14 describe "redis" do
15 before(:all) do
16 # use database 15 for testing so we dont accidentally step on you real data
17 @r = Redis.new :db => 15
18 end
19
20 before(:each) do
21 @r['foo'] = 'bar'
22 end
23
24 after(:each) do
25 @r.keys('*').each {|k| @r.del k}
26 end
27
28 after(:all) do
29 @r.quit
30 end
31
32 it "should be able connect without a timeout" do
33 lambda { Redis.new :timeout => 0 }.should_not raise_error
34 end
35
36 it "should be able to PING" do
37 @r.ping.should == 'PONG'
38 end
39
40 it "should be able to GET a key" do
41 @r['foo'].should == 'bar'
42 end
43
44 it "should be able to SET a key" do
45 @r['foo'] = 'nik'
46 @r['foo'].should == 'nik'
47 end
48
49 it "should properly handle trailing newline characters" do
50 @r['foo'] = "bar\n"
51 @r['foo'].should == "bar\n"
52 end
53
54 it "should store and retrieve all possible characters at the beginning and the end of a string" do
55 (0..255).each do |char_idx|
56 string = "#{char_idx.chr}---#{char_idx.chr}"
57 @r['foo'] = string
58 @r['foo'].should == string
59 end
60 end
61
62 it "should be able to SET a key with an expiry" do
63 @r.set('foo', 'bar', 1)
64 @r['foo'].should == 'bar'
65 sleep 2
66 @r['foo'].should == nil
67 end
68
69 it "should be able to return a TTL for a key" do
70 @r.set('foo', 'bar', 1)
71 @r.ttl('foo').should == 1
72 end
73
74 it "should be able to SETNX" do
75 @r['foo'] = 'nik'
76 @r['foo'].should == 'nik'
77 @r.setnx 'foo', 'bar'
78 @r['foo'].should == 'nik'
79 end
80 #
81 it "should be able to GETSET" do
82 @r.getset('foo', 'baz').should == 'bar'
83 @r['foo'].should == 'baz'
84 end
85 #
86 it "should be able to INCR a key" do
87 @r.del('counter')
88 @r.incr('counter').should == 1
89 @r.incr('counter').should == 2
90 @r.incr('counter').should == 3
91 end
92 #
93 it "should be able to INCRBY a key" do
94 @r.del('counter')
95 @r.incrby('counter', 1).should == 1
96 @r.incrby('counter', 2).should == 3
97 @r.incrby('counter', 3).should == 6
98 end
99 #
100 it "should be able to DECR a key" do
101 @r.del('counter')
102 @r.incr('counter').should == 1
103 @r.incr('counter').should == 2
104 @r.incr('counter').should == 3
105 @r.decr('counter').should == 2
106 @r.decr('counter', 2).should == 0
107 end
108 #
109 it "should be able to RANDKEY" do
110 @r.randkey.should_not be_nil
111 end
112 #
113 it "should be able to RENAME a key" do
114 @r.del 'foo'
115 @r.del'bar'
116 @r['foo'] = 'hi'
117 @r.rename 'foo', 'bar'
118 @r['bar'].should == 'hi'
119 end
120 #
121 it "should be able to RENAMENX a key" do
122 @r.del 'foo'
123 @r.del 'bar'
124 @r['foo'] = 'hi'
125 @r['bar'] = 'ohai'
126 @r.renamenx 'foo', 'bar'
127 @r['bar'].should == 'ohai'
128 end
129 #
130 it "should be able to get DBSIZE of the database" do
131 @r.delete 'foo'
132 dbsize_without_foo = @r.dbsize
133 @r['foo'] = 0
134 dbsize_with_foo = @r.dbsize
135
136 dbsize_with_foo.should == dbsize_without_foo + 1
137 end
138 #
139 it "should be able to EXPIRE a key" do
140 @r['foo'] = 'bar'
141 @r.expire 'foo', 1
142 @r['foo'].should == "bar"
143 sleep 2
144 @r['foo'].should == nil
145 end
146 #
147 it "should be able to EXISTS" do
148 @r['foo'] = 'nik'
149 @r.exists('foo').should be_true
150 @r.del 'foo'
151 @r.exists('foo').should be_false
152 end
153 #
154 it "should be able to KEYS" do
155 @r.keys("f*").each { |key| @r.del key }
156 @r['f'] = 'nik'
157 @r['fo'] = 'nak'
158 @r['foo'] = 'qux'
159 @r.keys("f*").sort.should == ['f','fo', 'foo'].sort
160 end
161 #
162 it "should be able to return a random key (RANDOMKEY)" do
163 3.times { @r.exists(@r.randomkey).should be_true }
164 end
165 #BTM - TODO
166 it "should be able to check the TYPE of a key" do
167 @r['foo'] = 'nik'
168 @r.type('foo').should == "string"
169 @r.del 'foo'
170 @r.type('foo').should == "none"
171 end
172 #
173 it "should be able to push to the head of a list (LPUSH)" do
174 @r.lpush "list", 'hello'
175 @r.lpush "list", 42
176 @r.type('list').should == "list"
177 @r.llen('list').should == 2
178 @r.lpop('list').should == '42'
179 end
180 #
181 it "should be able to push to the tail of a list (RPUSH)" do
182 @r.rpush "list", 'hello'
183 @r.type('list').should == "list"
184 @r.llen('list').should == 1
185 end
186 #
187 it "should be able to pop the tail of a list (RPOP)" do
188 @r.rpush "list", 'hello'
189 @r.rpush"list", 'goodbye'
190 @r.type('list').should == "list"
191 @r.llen('list').should == 2
192 @r.rpop('list').should == 'goodbye'
193 end
194 #
195 it "should be able to pop the head of a list (LPOP)" do
196 @r.rpush "list", 'hello'
197 @r.rpush "list", 'goodbye'
198 @r.type('list').should == "list"
199 @r.llen('list').should == 2
200 @r.lpop('list').should == 'hello'
201 end
202 #
203 it "should be able to get the length of a list (LLEN)" do
204 @r.rpush "list", 'hello'
205 @r.rpush "list", 'goodbye'
206 @r.type('list').should == "list"
207 @r.llen('list').should == 2
208 end
209 #
210 it "should be able to get a range of values from a list (LRANGE)" do
211 @r.rpush "list", 'hello'
212 @r.rpush "list", 'goodbye'
213 @r.rpush "list", '1'
214 @r.rpush "list", '2'
215 @r.rpush "list", '3'
216 @r.type('list').should == "list"
217 @r.llen('list').should == 5
218 @r.lrange('list', 2, -1).should == ['1', '2', '3']
219 end
220 #
221 it "should be able to trim a list (LTRIM)" do
222 @r.rpush "list", 'hello'
223 @r.rpush "list", 'goodbye'
224 @r.rpush "list", '1'
225 @r.rpush "list", '2'
226 @r.rpush "list", '3'
227 @r.type('list').should == "list"
228 @r.llen('list').should == 5
229 @r.ltrim 'list', 0, 1
230 @r.llen('list').should == 2
231 @r.lrange('list', 0, -1).should == ['hello', 'goodbye']
232 end
233 #
234 it "should be able to get a value by indexing into a list (LINDEX)" do
235 @r.rpush "list", 'hello'
236 @r.rpush "list", 'goodbye'
237 @r.type('list').should == "list"
238 @r.llen('list').should == 2
239 @r.lindex('list', 1).should == 'goodbye'
240 end
241 #
242 it "should be able to set a value by indexing into a list (LSET)" do
243 @r.rpush "list", 'hello'
244 @r.rpush "list", 'hello'
245 @r.type('list').should == "list"
246 @r.llen('list').should == 2
247 @r.lset('list', 1, 'goodbye').should == 'OK'
248 @r.lindex('list', 1).should == 'goodbye'
249 end
250 #
251 it "should be able to remove values from a list (LREM)" do
252 @r.rpush "list", 'hello'
253 @r.rpush "list", 'goodbye'
254 @r.type('list').should == "list"
255 @r.llen('list').should == 2
256 @r.lrem('list', 1, 'hello').should == 1
257 @r.lrange('list', 0, -1).should == ['goodbye']
258 end
259 #
260 it "should be able add members to a set (SADD)" do
261 @r.sadd "set", 'key1'
262 @r.sadd "set", 'key2'
263 @r.type('set').should == "set"
264 @r.scard('set').should == 2
265 @r.smembers('set').sort.should == ['key1', 'key2'].sort
266 end
267 #
268 it "should be able delete members to a set (SREM)" do
269 @r.sadd "set", 'key1'
270 @r.sadd "set", 'key2'
271 @r.type('set').should == "set"
272 @r.scard('set').should == 2
273 @r.smembers('set').sort.should == ['key1', 'key2'].sort
274 @r.srem('set', 'key1')
275 @r.scard('set').should == 1
276 @r.smembers('set').should == ['key2']
277 end
278 #
279 it "should be able count the members of a set (SCARD)" do
280 @r.sadd "set", 'key1'
281 @r.sadd "set", 'key2'
282 @r.type('set').should == "set"
283 @r.scard('set').should == 2
284 end
285 #
286 it "should be able test for set membership (SISMEMBER)" do
287 @r.sadd "set", 'key1'
288 @r.sadd "set", 'key2'
289 @r.type('set').should == "set"
290 @r.scard('set').should == 2
291 @r.sismember('set', 'key1').should be_true
292 @r.sismember('set', 'key2').should be_true
293 @r.sismember('set', 'notthere').should be_false
294 end
295 #
296 it "should be able to do set intersection (SINTER)" do
297 @r.sadd "set", 'key1'
298 @r.sadd "set", 'key2'
299 @r.sadd "set2", 'key2'
300 @r.sinter('set', 'set2').should == ['key2']
301 end
302 #
303 it "should be able to do set intersection and store the results in a key (SINTERSTORE)" do
304 @r.sadd "set", 'key1'
305 @r.sadd "set", 'key2'
306 @r.sadd "set2", 'key2'
307 @r.sinterstore('newone', 'set', 'set2').should == 1
308 @r.smembers('newone').should == ['key2']
309 end
310 #
311 it "should be able to do set union (SUNION)" do
312 @r.sadd "set", 'key1'
313 @r.sadd "set", 'key2'
314 @r.sadd "set2", 'key2'
315 @r.sadd "set2", 'key3'
316 @r.sunion('set', 'set2').sort.should == ['key1','key2','key3'].sort
317 end
318 #
319 it "should be able to do set union and store the results in a key (SUNIONSTORE)" do
320 @r.sadd "set", 'key1'
321 @r.sadd "set", 'key2'
322 @r.sadd "set2", 'key2'
323 @r.sadd "set2", 'key3'
324 @r.sunionstore('newone', 'set', 'set2').should == 3
325 @r.smembers('newone').sort.should == ['key1','key2','key3'].sort
326 end
327 #
328 it "should be able to do set difference (SDIFF)" do
329 @r.sadd "set", 'a'
330 @r.sadd "set", 'b'
331 @r.sadd "set2", 'b'
332 @r.sadd "set2", 'c'
333 @r.sdiff('set', 'set2').should == ['a']
334 end
335 #
336 it "should be able to do set difference and store the results in a key (SDIFFSTORE)" do
337 @r.sadd "set", 'a'
338 @r.sadd "set", 'b'
339 @r.sadd "set2", 'b'
340 @r.sadd "set2", 'c'
341 @r.sdiffstore('newone', 'set', 'set2')
342 @r.smembers('newone').should == ['a']
343 end
344 #
345 it "should be able move elements from one set to another (SMOVE)" do
346 @r.sadd 'set1', 'a'
347 @r.sadd 'set1', 'b'
348 @r.sadd 'set2', 'x'
349 @r.smove('set1', 'set2', 'a').should be_true
350 @r.sismember('set2', 'a').should be_true
351 @r.delete('set1')
352 end
353 #
354 it "should be able to do crazy SORT queries" do
355 @r['dog_1'] = 'louie'
356 @r.rpush 'dogs', 1
357 @r['dog_2'] = 'lucy'
358 @r.rpush 'dogs', 2
359 @r['dog_3'] = 'max'
360 @r.rpush 'dogs', 3
361 @r['dog_4'] = 'taj'
362 @r.rpush 'dogs', 4
363 @r.sort('dogs', :get => 'dog_*', :limit => [0,1]).should == ['louie']
364 @r.sort('dogs', :get => 'dog_*', :limit => [0,1], :order => 'desc alpha').should == ['taj']
365 end
366
367 it "should be able to handle array of :get using SORT" do
368 @r['dog:1:name'] = 'louie'
369 @r['dog:1:breed'] = 'mutt'
370 @r.rpush 'dogs', 1
371 @r['dog:2:name'] = 'lucy'
372 @r['dog:2:breed'] = 'poodle'
373 @r.rpush 'dogs', 2
374 @r['dog:3:name'] = 'max'
375 @r['dog:3:breed'] = 'hound'
376 @r.rpush 'dogs', 3
377 @r['dog:4:name'] = 'taj'
378 @r['dog:4:breed'] = 'terrier'
379 @r.rpush 'dogs', 4
380 @r.sort('dogs', :get => ['dog:*:name', 'dog:*:breed'], :limit => [0,1]).should == ['louie', 'mutt']
381 @r.sort('dogs', :get => ['dog:*:name', 'dog:*:breed'], :limit => [0,1], :order => 'desc alpha').should == ['taj', 'terrier']
382 end
383 #
384 it "should provide info (INFO)" do
385 [: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|
386 @r.info.keys.should include(x)
387 end
388 end
389 #
390 it "should be able to flush the database (FLUSHDB)" do
391 @r['key1'] = 'keyone'
392 @r['key2'] = 'keytwo'
393 @r.keys('*').sort.should == ['foo', 'key1', 'key2'].sort #foo from before
394 @r.flushdb
395 @r.keys('*').should == []
396 end
397 #
398 it "should raise exception when manually try to change the database" do
399 lambda { @r.select(0) }.should raise_error
400 end
401 #
402 it "should be able to provide the last save time (LASTSAVE)" do
403 savetime = @r.lastsave
404 Time.at(savetime).class.should == Time
405 Time.at(savetime).should <= Time.now
406 end
407
408 it "should be able to MGET keys" do
409 @r['foo'] = 1000
410 @r['bar'] = 2000
411 @r.mget('foo', 'bar').should == ['1000', '2000']
412 @r.mget('foo', 'bar', 'baz').should == ['1000', '2000', nil]
413 end
414
415 it "should bgsave" do
416 @r.bgsave.should == 'OK'
417 end
418
419 it "should should be able to ECHO" do
420 @r.echo("message in a bottle\n").should == "message in a bottle\n"
421 end
422
423 it "should handle multiple servers" do
424 require 'dist_redis'
425 @r = DistRedis.new(:hosts=> ['localhost:6379', '127.0.0.1:6379'], :db => 15)
426
427 100.times do |idx|
428 @r[idx] = "foo#{idx}"
429 end
430
431 100.times do |idx|
432 @r[idx].should == "foo#{idx}"
433 end
434 end
435
436 it "should be able to pipeline writes" do
437 @r.pipelined do |pipeline|
438 pipeline.lpush 'list', "hello"
439 pipeline.lpush 'list', 42
440 end
441
442 @r.type('list').should == "list"
443 @r.llen('list').should == 2
444 @r.lpop('list').should == '42'
445 end
446
447 end