fakeredis 0.5.0 → 0.8.0

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.
Files changed (44) hide show
  1. checksums.yaml +5 -5
  2. data/.gitignore +3 -0
  3. data/.travis.yml +14 -5
  4. data/LICENSE +1 -1
  5. data/README.md +42 -24
  6. data/fakeredis.gemspec +1 -1
  7. data/lib/fakeredis.rb +28 -0
  8. data/lib/fakeredis/bitop_command.rb +56 -0
  9. data/lib/fakeredis/command_executor.rb +6 -9
  10. data/lib/fakeredis/expiring_hash.rb +3 -5
  11. data/lib/fakeredis/geo_commands.rb +142 -0
  12. data/lib/fakeredis/geo_set.rb +84 -0
  13. data/lib/fakeredis/minitest.rb +24 -0
  14. data/lib/fakeredis/rspec.rb +1 -0
  15. data/lib/fakeredis/sort_method.rb +3 -3
  16. data/lib/fakeredis/sorted_set_store.rb +1 -1
  17. data/lib/fakeredis/transaction_commands.rb +2 -2
  18. data/lib/fakeredis/version.rb +1 -1
  19. data/lib/fakeredis/zset.rb +8 -2
  20. data/lib/redis/connection/memory.rb +650 -82
  21. data/spec/bitop_command_spec.rb +209 -0
  22. data/spec/command_executor_spec.rb +15 -0
  23. data/spec/compatibility_spec.rb +1 -1
  24. data/spec/connection_spec.rb +21 -21
  25. data/spec/fakeredis_spec.rb +73 -0
  26. data/spec/geo_set_spec.rb +164 -0
  27. data/spec/hashes_spec.rb +138 -57
  28. data/spec/hyper_log_logs_spec.rb +50 -0
  29. data/spec/keys_spec.rb +232 -90
  30. data/spec/lists_spec.rb +91 -35
  31. data/spec/memory_spec.rb +80 -7
  32. data/spec/server_spec.rb +38 -24
  33. data/spec/sets_spec.rb +112 -46
  34. data/spec/sort_method_spec.rb +6 -0
  35. data/spec/sorted_sets_spec.rb +482 -150
  36. data/spec/spec_helper.rb +9 -18
  37. data/spec/spec_helper_live_redis.rb +4 -4
  38. data/spec/strings_spec.rb +113 -79
  39. data/spec/subscription_spec.rb +107 -0
  40. data/spec/support/shared_examples/bitwise_operation.rb +59 -0
  41. data/spec/support/shared_examples/sortable.rb +20 -16
  42. data/spec/transactions_spec.rb +34 -13
  43. data/spec/upcase_method_name_spec.rb +2 -2
  44. metadata +23 -6
data/spec/lists_spec.rb CHANGED
@@ -10,41 +10,63 @@ module FakeRedis
10
10
  @client.lpush("key1", "val1")
11
11
  @client.lpush("key1", "val2")
12
12
 
13
- @client.lindex("key1", 0).should be == "val2"
14
- @client.lindex("key1", -1).should be == "val1"
15
- @client.lindex("key1", 3).should be == nil
13
+ expect(@client.lindex("key1", 0)).to eq("val2")
14
+ expect(@client.lindex("key1", -1)).to eq("val1")
15
+ expect(@client.lindex("key1", 3)).to eq(nil)
16
16
  end
17
17
 
18
18
  it "should insert an element before or after another element in a list" do
19
19
  @client.rpush("key1", "v1")
20
20
  @client.rpush("key1", "v3")
21
21
  @client.linsert("key1", :before, "v3", "v2")
22
+ @client.linsert("key1", :after, "v3", 100)
23
+ @client.linsert("key1", :before, 100, 99)
22
24
 
23
- @client.lrange("key1", 0, -1).should be == ["v1", "v2", "v3"]
25
+ expect(@client.lrange("key1", 0, -1)).to eq(["v1", "v2", "v3", "99", "100"])
26
+ end
27
+
28
+ it "inserts with case-insensitive position token" do
29
+ @client.rpush("key1", "v1")
30
+ @client.rpush("key1", "v4")
31
+
32
+ @client.linsert("key1", :BEFORE, "v4", "v2")
33
+ @client.linsert("key1", "Before", "v4", "v3")
34
+ @client.linsert("key1", :AFTER, "v4", "v5")
35
+ @client.linsert("key1", "After", "v5", "v6")
36
+
37
+ expect(@client.lrange("key1", 0, -1)).to eq(%w(v1 v2 v3 v4 v5 v6))
38
+ end
39
+
40
+ it "should not insert if after/before index not found" do
41
+ @client.rpush("key", "v1")
42
+ expect(@client.linsert("key", :before, "unknown", "v2")).to eq(-1)
43
+ expect(@client.linsert("key", :after, "unknown", "v3")).to eq(-1)
44
+
45
+ expect(@client.lrange("key", 0, -1)).to eq(["v1"])
24
46
  end
25
47
 
26
48
  it 'should allow multiple values to be added to a list in a single rpush' do
27
49
  @client.rpush('key1', [1, 2, 3])
28
- @client.lrange('key1', 0, -1).should be == ['1', '2', '3']
50
+ expect(@client.lrange('key1', 0, -1)).to eq(['1', '2', '3'])
29
51
  end
30
52
 
31
53
  it 'should allow multiple values to be added to a list in a single lpush' do
32
54
  @client.lpush('key1', [1, 2, 3])
33
- @client.lrange('key1', 0, -1).should be == ['3', '2', '1']
55
+ expect(@client.lrange('key1', 0, -1)).to eq(['3', '2', '1'])
34
56
  end
35
57
 
36
58
  it "should error if an invalid where argument is given" do
37
59
  @client.rpush("key1", "v1")
38
60
  @client.rpush("key1", "v3")
39
- lambda { @client.linsert("key1", :invalid, "v3", "v2") }.should raise_error(Redis::CommandError, "ERR syntax error")
61
+ expect { @client.linsert("key1", :invalid, "v3", "v2") }.to raise_error(Redis::CommandError, "ERR syntax error")
40
62
  end
41
63
 
42
64
  it "should get the length of a list" do
43
65
  @client.rpush("key1", "v1")
44
66
  @client.rpush("key1", "v2")
45
67
 
46
- @client.llen("key1").should be == 2
47
- @client.llen("key2").should be == 0
68
+ expect(@client.llen("key1")).to eq(2)
69
+ expect(@client.llen("key2")).to eq(0)
48
70
  end
49
71
 
50
72
  it "should remove and get the first element in a list" do
@@ -52,15 +74,15 @@ module FakeRedis
52
74
  @client.rpush("key1", "v2")
53
75
  @client.rpush("key1", "v3")
54
76
 
55
- @client.lpop("key1").should be == "v1"
56
- @client.lrange("key1", 0, -1).should be == ["v2", "v3"]
77
+ expect(@client.lpop("key1")).to eq("v1")
78
+ expect(@client.lrange("key1", 0, -1)).to eq(["v2", "v3"])
57
79
  end
58
80
 
59
81
  it "should prepend a value to a list" do
60
82
  @client.rpush("key1", "v1")
61
83
  @client.rpush("key1", "v2")
62
84
 
63
- @client.lrange("key1", 0, -1).should be == ["v1", "v2"]
85
+ expect(@client.lrange("key1", 0, -1)).to eq(["v1", "v2"])
64
86
  end
65
87
 
66
88
  it "should prepend a value to a list, only if the list exists" do
@@ -69,8 +91,8 @@ module FakeRedis
69
91
  @client.lpushx("key1", "v2")
70
92
  @client.lpushx("key2", "v3")
71
93
 
72
- @client.lrange("key1", 0, -1).should be == ["v2", "v1"]
73
- @client.llen("key2").should be == 0
94
+ expect(@client.lrange("key1", 0, -1)).to eq(["v2", "v1"])
95
+ expect(@client.llen("key2")).to eq(0)
74
96
  end
75
97
 
76
98
  it "should get a range of elements from a list" do
@@ -78,7 +100,8 @@ module FakeRedis
78
100
  @client.rpush("key1", "v2")
79
101
  @client.rpush("key1", "v3")
80
102
 
81
- @client.lrange("key1", 1, -1).should be == ["v2", "v3"]
103
+ expect(@client.lrange("key1", 1, -1)).to eq(["v2", "v3"])
104
+ expect(@client.lrange("key1", -999, -1)).to eq(["v1", "v2", "v3"])
82
105
  end
83
106
 
84
107
  it "should remove elements from a list" do
@@ -87,10 +110,17 @@ module FakeRedis
87
110
  @client.rpush("key1", "v2")
88
111
  @client.rpush("key1", "v2")
89
112
  @client.rpush("key1", "v1")
113
+ @client.rpush("key1", 42)
114
+
115
+ expect(@client.lrem("key1", 1, "v1")).to eq(1)
116
+ expect(@client.lrem("key1", -2, "v2")).to eq(2)
117
+ expect(@client.lrem("key1", 0, 42)).to eq(1)
118
+ expect(@client.llen("key1")).to eq(2)
119
+ end
90
120
 
91
- @client.lrem("key1", 1, "v1").should be == 1
92
- @client.lrem("key1", -2, "v2").should be == 2
93
- @client.llen("key1").should be == 2
121
+ it "should return 0 if key does not map to a list" do
122
+ expect(@client.exists("nonexistant")).to eq(false)
123
+ expect(@client.lrem("nonexistant", 0, "value")).to eq(0)
94
124
  end
95
125
 
96
126
  it "should remove list's key when list is empty" do
@@ -99,7 +129,7 @@ module FakeRedis
99
129
  @client.lrem("key1", 1, "v1")
100
130
  @client.lrem("key1", 1, "v2")
101
131
 
102
- @client.exists("key1").should be == false
132
+ expect(@client.exists("key1")).to eq(false)
103
133
  end
104
134
 
105
135
  it "should set the value of an element in a list by its index" do
@@ -109,9 +139,10 @@ module FakeRedis
109
139
 
110
140
  @client.lset("key1", 0, "four")
111
141
  @client.lset("key1", -2, "five")
112
- @client.lrange("key1", 0, -1).should be == ["four", "five", "three"]
142
+ @client.lset("key1", 2, 6)
113
143
 
114
- lambda { @client.lset("key1", 4, "six") }.should raise_error(Redis::CommandError, "ERR index out of range")
144
+ expect(@client.lrange("key1", 0, -1)).to eq(["four", "five", "6"])
145
+ expect { @client.lset("key1", 4, "seven") }.to raise_error(Redis::CommandError, "ERR index out of range")
115
146
  end
116
147
 
117
148
  it "should trim a list to the specified range" do
@@ -119,8 +150,8 @@ module FakeRedis
119
150
  @client.rpush("key1", "two")
120
151
  @client.rpush("key1", "three")
121
152
 
122
- @client.ltrim("key1", 1, -1).should be == "OK"
123
- @client.lrange("key1", 0, -1).should be == ["two", "three"]
153
+ expect(@client.ltrim("key1", 1, -1)).to eq("OK")
154
+ expect(@client.lrange("key1", 0, -1)).to eq(["two", "three"])
124
155
  end
125
156
 
126
157
 
@@ -131,7 +162,7 @@ module FakeRedis
131
162
  before { @client.ltrim("listOfOne", -5, -1) }
132
163
 
133
164
  it "returns the unmodified list" do
134
- @client.lrange("listOfOne", 0, -1).should be == ["one"]
165
+ expect(@client.lrange("listOfOne", 0, -1)).to eq(["one"])
135
166
  end
136
167
  end
137
168
  end
@@ -150,7 +181,7 @@ module FakeRedis
150
181
  before { @client.ltrim("maxTest", -5, -1) }
151
182
 
152
183
  it "should trim a list to the specified maximum size" do
153
- @client.lrange("maxTest", 0, -1).should be == ["two","three", "four", "five", "six"]
184
+ expect(@client.lrange("maxTest", 0, -1)).to eq(["two","three", "four", "five", "six"])
154
185
  end
155
186
  end
156
187
  end
@@ -161,26 +192,43 @@ module FakeRedis
161
192
  @client.rpush("key1", "two")
162
193
  @client.rpush("key1", "three")
163
194
 
164
- @client.rpop("key1").should be == "three"
165
- @client.lrange("key1", 0, -1).should be == ["one", "two"]
195
+ expect(@client.rpop("key1")).to eq("three")
196
+ expect(@client.lrange("key1", 0, -1)).to eq(["one", "two"])
166
197
  end
167
198
 
168
- it "should remove the last element in a list, append it to another list and return it" do
199
+ it "rpoplpush should remove the last element in a list, append it to another list and return it" do
169
200
  @client.rpush("key1", "one")
170
201
  @client.rpush("key1", "two")
171
202
  @client.rpush("key1", "three")
172
203
 
173
- @client.rpoplpush("key1", "key2").should be == "three"
204
+ expect(@client.rpoplpush("key1", "key2")).to eq("three")
174
205
 
175
- @client.lrange("key1", 0, -1).should be == ["one", "two"]
176
- @client.lrange("key2", 0, -1).should be == ["three"]
206
+ expect(@client.lrange("key1", 0, -1)).to eq(["one", "two"])
207
+ expect(@client.lrange("key2", 0, -1)).to eq(["three"])
208
+ end
209
+
210
+ it "brpoplpush should remove the last element in a list, append it to another list and return it" do
211
+ @client.rpush("key1", "one")
212
+ @client.rpush("key1", "two")
213
+ @client.rpush("key1", "three")
214
+
215
+ expect(@client.brpoplpush("key1", "key2")).to eq("three")
216
+
217
+ expect(@client.lrange("key1", 0, -1)).to eq(["one", "two"])
218
+ expect(@client.lrange("key2", 0, -1)).to eq(["three"])
177
219
  end
178
220
 
179
221
  context 'when the source list is empty' do
180
222
  it 'rpoplpush does not add anything to the destination list' do
181
223
  @client.rpoplpush("source", "destination")
182
224
 
183
- @client.lrange("destination", 0, -1).should be == []
225
+ expect(@client.lrange("destination", 0, -1)).to eq([])
226
+ end
227
+
228
+ it 'brpoplpush does not add anything to the destination list' do
229
+ expect(@client.brpoplpush("source", "destination")).to be_nil
230
+
231
+ expect(@client.lrange("destination", 0, -1)).to eq([])
184
232
  end
185
233
  end
186
234
 
@@ -188,7 +236,7 @@ module FakeRedis
188
236
  @client.rpush("key1", "one")
189
237
  @client.rpush("key1", "two")
190
238
 
191
- @client.lrange("key1", 0, -1).should be == ["one", "two"]
239
+ expect(@client.lrange("key1", 0, -1)).to eq(["one", "two"])
192
240
  end
193
241
 
194
242
  it "should append a value to a list, only if the list exists" do
@@ -196,8 +244,16 @@ module FakeRedis
196
244
  @client.rpushx("key1", "two")
197
245
  @client.rpushx("key2", "two")
198
246
 
199
- @client.lrange("key1", 0, -1).should be == ["one", "two"]
200
- @client.lrange("key2", 0, -1).should be == []
247
+ expect(@client.lrange("key1", 0, -1)).to eq(["one", "two"])
248
+ expect(@client.lrange("key2", 0, -1)).to eq([])
249
+ end
250
+
251
+ it 'should not allow pushing empty list of objects' do
252
+ expect { @client.lpush("key1", []) }.to raise_error(Redis::CommandError, /lpush[^x]/)
253
+ expect { @client.lpush("key1", 1); @client.lpushx("key1", []) }.to raise_error(Redis::CommandError, /lpushx/)
254
+
255
+ expect { @client.rpush("key1", []) }.to raise_error(Redis::CommandError, /rpush[^x]/)
256
+ expect { @client.rpush("key1", 1); @client.rpushx("key1", []) }.to raise_error(Redis::CommandError, /rpushx/)
201
257
  end
202
258
  end
203
259
  end
data/spec/memory_spec.rb CHANGED
@@ -1,28 +1,101 @@
1
1
  require 'spec_helper'
2
2
 
3
- module FakeRedis
3
+ RSpec.describe FakeRedis do
4
+ let(:redis) { Redis.new }
5
+
6
+ def populate_keys_in_redis(num)
7
+ num.times do |count|
8
+ redis.set("key#{count}", count)
9
+ end
10
+ end
11
+
12
+ describe '#write' do
13
+ it 'should not send unexpected arguments' do
14
+ expect { redis.write(['info', 'server']) }.not_to raise_error
15
+ end
16
+ end
17
+
18
+ describe '#scan' do
19
+ def result
20
+ returned_keys = []
21
+ cursor = 0
22
+
23
+ loop do
24
+ cursor, keys = redis.scan(cursor, match_arguments)
25
+ returned_keys += keys
26
+ break if cursor == '0'
27
+ end
28
+ returned_keys
29
+ end
30
+
31
+ before do
32
+ populate_keys_in_redis(11)
33
+ end
34
+
35
+ context('when deleting') do
36
+ it('preverves cursor') do
37
+ cursor, keys = redis.scan('0')
38
+ keys.each { |key| redis.del(key) }
39
+ _, keys = redis.scan(cursor)
40
+ expect(keys).to eq(%w(key10))
41
+ end
42
+ end
43
+
44
+ context 'with one namespace' do
45
+ let(:match_arguments) { {} }
46
+
47
+ it 'returns the expected array of keys' do
48
+ expect(result).to match_array(redis.keys)
49
+ end
50
+ end
51
+
52
+ context 'with multiple namespaces' do
53
+ let(:namespaced_key) { 'test' }
54
+ let(:match_arguments) { { match: namespaced_key } }
55
+
56
+ before { redis.set(namespaced_key, 12) }
57
+
58
+ it 'returns the expected array of keys' do
59
+ expect(result).to match_array([namespaced_key])
60
+ end
61
+ end
62
+ end
63
+
4
64
  describe 'time' do
5
65
  before(:each) do
6
- @client = Redis.new
7
- Time.stub_chain(:now, :to_f).and_return(1397845595.5139461)
66
+ allow(Time).to receive_message_chain(:now, :to_f).and_return(1397845595.5139461)
8
67
  end
9
68
 
10
69
  it 'is an array' do
11
- expect(@client.time).to be_an_instance_of(Array)
70
+ expect(redis.time).to be_an_instance_of(Array)
12
71
  end
13
72
 
14
73
  it 'has two elements' do
15
- expect(@client.time.count).to eql 2
74
+ expect(redis.time.count).to eql 2
16
75
  end
17
76
 
18
77
  if fakeredis?
19
78
  it 'has the current time in seconds' do
20
- expect(@client.time.first).to eql 1397845595
79
+ expect(redis.time.first).to eql 1397845595
21
80
  end
22
81
 
23
82
  it 'has the current leftover microseconds' do
24
- expect(@client.time.last).to eql 513946
83
+ expect(redis.time.last).to eql 513946
25
84
  end
26
85
  end
27
86
  end
87
+
88
+ describe '#client' do
89
+ it 'returns OK when command is :setname' do
90
+ expect(redis.client(:setname, 'my-client-01')).to eq 'OK'
91
+ end
92
+
93
+ it 'returns nil when command is :getname' do
94
+ expect(redis.client(:getname)).to eq nil
95
+ end
96
+
97
+ it 'raises error for other commands' do
98
+ expect { redis.write([:client, :wrong]) }.to raise_error(Redis::CommandError, "ERR unknown command 'wrong'")
99
+ end
100
+ end
28
101
  end
data/spec/server_spec.rb CHANGED
@@ -12,29 +12,29 @@ module FakeRedis
12
12
  @client.set("key2", "2")
13
13
  @client.set("key2", "two")
14
14
 
15
- @client.dbsize.should be == 2
15
+ expect(@client.dbsize).to eq(2)
16
16
  end
17
17
 
18
18
  it "should get information and statistics about the server" do
19
- @client.info.key?("redis_version").should be == true
19
+ expect(@client.info.key?("redis_version")).to eq(true)
20
20
  end
21
21
 
22
22
  it "should handle non-existent methods" do
23
- lambda { @client.idontexist }.should raise_error(Redis::CommandError, "ERR unknown command 'idontexist'")
23
+ expect { @client.idontexist }.to raise_error(Redis::CommandError, "ERR unknown command 'idontexist'")
24
24
  end
25
25
 
26
26
  describe "multiple databases" do
27
27
  it "should default to database 0" do
28
- @client.inspect.should =~ %r#/0>$#
28
+ expect(@client.inspect).to match(%r#/0>$#)
29
29
  end
30
30
 
31
31
  it "should select another database" do
32
32
  @client.select(1)
33
- @client.inspect.should =~ %r#/1>$#
33
+ expect(@client.inspect).to match(%r#/1>$#)
34
34
  end
35
35
 
36
36
  it "should store keys separately in each database" do
37
- @client.select(0).should be == "OK"
37
+ expect(@client.select(0)).to eq("OK")
38
38
  @client.set("key1", "1")
39
39
  @client.set("key2", "2")
40
40
 
@@ -44,56 +44,70 @@ module FakeRedis
44
44
  @client.set("key5", "5")
45
45
 
46
46
  @client.select(0)
47
- @client.dbsize.should be == 2
48
- @client.exists("key1").should be true
49
- @client.exists("key3").should be false
47
+ expect(@client.dbsize).to eq(2)
48
+ expect(@client.exists("key1")).to be true
49
+ expect(@client.exists("key3")).to be false
50
50
 
51
51
  @client.select(1)
52
- @client.dbsize.should be == 3
53
- @client.exists("key4").should be true
54
- @client.exists("key2").should be false
52
+ expect(@client.dbsize).to eq(3)
53
+ expect(@client.exists("key4")).to be true
54
+ expect(@client.exists("key2")).to be false
55
55
 
56
56
  @client.flushall
57
- @client.dbsize.should be == 0
57
+ expect(@client.dbsize).to eq(0)
58
58
 
59
59
  @client.select(0)
60
- @client.dbsize.should be == 0
60
+ expect(@client.dbsize).to eq(0)
61
61
  end
62
62
 
63
63
  it "should flush a database" do
64
64
  @client.select(0)
65
65
  @client.set("key1", "1")
66
66
  @client.set("key2", "2")
67
- @client.dbsize.should be == 2
67
+ expect(@client.dbsize).to eq(2)
68
68
 
69
69
  @client.select(1)
70
70
  @client.set("key3", "3")
71
71
  @client.set("key4", "4")
72
- @client.dbsize.should be == 2
72
+ expect(@client.dbsize).to eq(2)
73
73
 
74
- @client.flushdb.should be == "OK"
74
+ expect(@client.flushdb).to eq("OK")
75
75
 
76
- @client.dbsize.should be == 0
76
+ expect(@client.dbsize).to eq(0)
77
77
  @client.select(0)
78
- @client.dbsize.should be == 2
78
+ expect(@client.dbsize).to eq(2)
79
79
  end
80
80
 
81
81
  it "should flush all databases" do
82
82
  @client.select(0)
83
83
  @client.set("key3", "3")
84
84
  @client.set("key4", "4")
85
- @client.dbsize.should be == 2
85
+ expect(@client.dbsize).to eq(2)
86
86
 
87
87
  @client.select(1)
88
88
  @client.set("key3", "3")
89
89
  @client.set("key4", "4")
90
- @client.dbsize.should be == 2
90
+ expect(@client.dbsize).to eq(2)
91
91
 
92
- @client.flushall.should be == "OK"
92
+ expect(@client.flushall).to eq("OK")
93
93
 
94
- @client.dbsize.should be == 0
94
+ expect(@client.dbsize).to eq(0)
95
95
  @client.select(0)
96
- @client.dbsize.should be == 0
96
+ expect(@client.dbsize).to eq(0)
97
+ end
98
+ end
99
+ end
100
+
101
+ describe 'custom options' do
102
+ describe 'version' do
103
+ it 'reports default Redis version when not provided' do
104
+ client = Redis.new
105
+ expect(client.info['redis_version']).to eq Redis::Connection::DEFAULT_REDIS_VERSION
106
+ end
107
+
108
+ it 'creates with and reports properly' do
109
+ client = Redis.new(version: '3.3.0')
110
+ expect(client.info['redis_version']).to eq '3.3.0'
97
111
  end
98
112
  end
99
113
  end