mock_redis 0.13.2 → 0.14.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/.rspec +1 -1
- data/.travis.yml +3 -0
- data/CHANGELOG.md +10 -0
- data/Gemfile +0 -5
- data/README.md +2 -2
- data/lib/mock_redis.rb +1 -0
- data/lib/mock_redis/database.rb +11 -2
- data/lib/mock_redis/future.rb +22 -0
- data/lib/mock_redis/pipelined_wrapper.rb +0 -21
- data/lib/mock_redis/set_methods.rb +1 -0
- data/lib/mock_redis/string_methods.rb +13 -5
- data/lib/mock_redis/transaction_wrapper.rb +22 -8
- data/lib/mock_redis/version.rb +1 -1
- data/lib/mock_redis/zset_methods.rb +12 -9
- data/mock_redis.gemspec +2 -1
- data/spec/commands/connected_spec.rb +1 -1
- data/spec/commands/exists_spec.rb +2 -2
- data/spec/commands/expire_spec.rb +18 -2
- data/spec/commands/expireat_spec.rb +2 -2
- data/spec/commands/hexists_spec.rb +4 -4
- data/spec/commands/hsetnx_spec.rb +2 -2
- data/spec/commands/move_spec.rb +8 -8
- data/spec/commands/msetnx_spec.rb +2 -2
- data/spec/commands/persist_spec.rb +4 -4
- data/spec/commands/pexpire_spec.rb +2 -2
- data/spec/commands/pexpireat_spec.rb +2 -2
- data/spec/commands/renamenx_spec.rb +2 -2
- data/spec/commands/sadd_spec.rb +2 -2
- data/spec/commands/scan_spec.rb +51 -0
- data/spec/commands/set_spec.rb +6 -6
- data/spec/commands/setbit_spec.rb +7 -0
- data/spec/commands/setnx_spec.rb +2 -2
- data/spec/commands/sismember_spec.rb +5 -5
- data/spec/commands/smove_spec.rb +6 -6
- data/spec/commands/srem_spec.rb +3 -3
- data/spec/commands/sunion_spec.rb +4 -0
- data/spec/commands/zadd_spec.rb +2 -2
- data/spec/commands/zrange_spec.rb +1 -1
- data/spec/commands/zrangebyscore_spec.rb +1 -1
- data/spec/commands/zrem_spec.rb +3 -3
- data/spec/commands/zremrangebyscore_spec.rb +1 -0
- data/spec/commands/zrevrange_spec.rb +1 -1
- data/spec/commands/zrevrangebyscore_spec.rb +1 -1
- data/spec/commands/zscore_spec.rb +2 -2
- data/spec/spec_helper.rb +11 -2
- data/spec/support/redis_multiplexer.rb +6 -6
- data/spec/support/shared_examples/only_operates_on_hashes.rb +2 -2
- data/spec/support/shared_examples/only_operates_on_lists.rb +2 -2
- data/spec/support/shared_examples/only_operates_on_sets.rb +2 -2
- data/spec/support/shared_examples/only_operates_on_strings.rb +2 -2
- data/spec/support/shared_examples/only_operates_on_zsets.rb +4 -4
- data/spec/transactions_spec.rb +23 -0
- metadata +36 -18
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 994ce4cc7000897238012b0a9f11a8417b31358d
|
4
|
+
data.tar.gz: 44f03b45a1310b977d0bf996fe042a7b32c70b36
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 84bd359ec9c9eff9b5b40cacc7ac65f206bdd8a720d00445fcb53cd0729abcd003c3fe66f96e220728575f305e00495c0fa75a55290a268ba304f4cf92597035
|
7
|
+
data.tar.gz: e09387fa284e38847e8ebec0d8ed1a7c3681ebfad0b661a239c54b976adada92ffb08661f736abdde1686aaba5ed394e53d6620326979b84811c42d65d63ba2c
|
data/.rspec
CHANGED
@@ -1 +1 @@
|
|
1
|
-
-c
|
1
|
+
-c
|
data/.travis.yml
CHANGED
data/CHANGELOG.md
CHANGED
@@ -1,5 +1,15 @@
|
|
1
1
|
# MockRedis Changelog
|
2
2
|
|
3
|
+
### 0.14.0
|
4
|
+
|
5
|
+
* Fix bug where SETBIT command would not correctly unset a bit
|
6
|
+
* Fix bug where a key that expired would cause another key that expired later
|
7
|
+
to prematurely expire
|
8
|
+
* Add support to set methods to take array as argument
|
9
|
+
* Evaluate futures at the end of `#multi` blocks
|
10
|
+
* Add support for the SCAN command
|
11
|
+
* Add support for `[+/-]inf` values for min/max in ordered set commands
|
12
|
+
|
3
13
|
### 0.13.2
|
4
14
|
|
5
15
|
* Fix SMEMBERS command to not return frozen elements
|
data/Gemfile
CHANGED
data/README.md
CHANGED
@@ -8,7 +8,7 @@ MockRedis provides the same interface as `redis-rb`, but it stores its
|
|
8
8
|
data in memory instead of talking to a Redis server. It is intended
|
9
9
|
for use in tests.
|
10
10
|
|
11
|
-
The current implementation is tested against *Redis 2.8.
|
11
|
+
The current implementation is tested against *Redis 2.8.17*. Older versions
|
12
12
|
of Redis may return different results or not support some commands.
|
13
13
|
|
14
14
|
## Getting Started
|
@@ -98,7 +98,7 @@ supported.
|
|
98
98
|
|
99
99
|
If you want to work on this, you'll probably want to run the
|
100
100
|
tests. (Just kidding! There's no probably about it.) These tests were
|
101
|
-
written with Redis
|
101
|
+
written with Redis running on `localhost` without any passwords
|
102
102
|
required. If you're using a different version of Redis, you may see
|
103
103
|
failures due to error message text being different. If you're running
|
104
104
|
a really old version of Redis, you'll definitely see failures due to
|
data/lib/mock_redis.rb
CHANGED
@@ -3,6 +3,7 @@ require 'set'
|
|
3
3
|
require 'mock_redis/assertions'
|
4
4
|
require 'mock_redis/database'
|
5
5
|
require 'mock_redis/expire_wrapper'
|
6
|
+
require 'mock_redis/future'
|
6
7
|
require 'mock_redis/multi_db_wrapper'
|
7
8
|
require 'mock_redis/pipelined_wrapper'
|
8
9
|
require 'mock_redis/transaction_wrapper'
|
data/lib/mock_redis/database.rb
CHANGED
@@ -112,6 +112,17 @@ class MockRedis
|
|
112
112
|
data.keys.grep(redis_pattern_to_ruby_regex(format))
|
113
113
|
end
|
114
114
|
|
115
|
+
def scan(cursor, opts = {})
|
116
|
+
count = (opts[:count] || 10).to_i
|
117
|
+
match = opts[:match] || '*'
|
118
|
+
|
119
|
+
keys = data.keys
|
120
|
+
limit = cursor + count
|
121
|
+
next_cursor = limit >= keys.length ? '0' : limit.to_s
|
122
|
+
|
123
|
+
[next_cursor, keys[cursor..limit].grep(redis_pattern_to_ruby_regex(match))]
|
124
|
+
end
|
125
|
+
|
115
126
|
def lastsave
|
116
127
|
@base.now.to_i
|
117
128
|
end
|
@@ -296,8 +307,6 @@ class MockRedis
|
|
296
307
|
to_delete.each do |(time, key)|
|
297
308
|
del(key)
|
298
309
|
end
|
299
|
-
|
300
|
-
expire_times.slice!(0, to_delete.length)
|
301
310
|
end
|
302
311
|
end
|
303
312
|
end
|
@@ -0,0 +1,22 @@
|
|
1
|
+
class MockRedis
|
2
|
+
class FutureNotReady < RuntimeError; end
|
3
|
+
|
4
|
+
class Future
|
5
|
+
attr_reader :command
|
6
|
+
|
7
|
+
def initialize(command)
|
8
|
+
@command = command
|
9
|
+
@result_set = false
|
10
|
+
end
|
11
|
+
|
12
|
+
def value
|
13
|
+
raise FutureNotReady unless @result_set
|
14
|
+
@result
|
15
|
+
end
|
16
|
+
|
17
|
+
def set_result(result)
|
18
|
+
@result_set = true
|
19
|
+
@result = result
|
20
|
+
end
|
21
|
+
end
|
22
|
+
end
|
@@ -1,25 +1,4 @@
|
|
1
1
|
class MockRedis
|
2
|
-
class FutureNotReady < RuntimeError; end
|
3
|
-
|
4
|
-
class Future
|
5
|
-
attr_reader :command
|
6
|
-
|
7
|
-
def initialize(command)
|
8
|
-
@command = command
|
9
|
-
@result_set = false
|
10
|
-
end
|
11
|
-
|
12
|
-
def value
|
13
|
-
raise FutureNotReady unless @result_set
|
14
|
-
@result
|
15
|
-
end
|
16
|
-
|
17
|
-
def set_result(result)
|
18
|
-
@result_set = true
|
19
|
-
@result = result
|
20
|
-
end
|
21
|
-
end
|
22
|
-
|
23
2
|
class PipelinedWrapper
|
24
3
|
include UndefRedisMethods
|
25
4
|
|
@@ -123,7 +123,7 @@ class MockRedis
|
|
123
123
|
def msetnx(*kvpairs)
|
124
124
|
assert_has_args(kvpairs, 'msetnx')
|
125
125
|
|
126
|
-
if kvpairs.each_slice(2).any? {|(k,
|
126
|
+
if kvpairs.each_slice(2).any? {|(k,_)| exists(k)}
|
127
127
|
false
|
128
128
|
else
|
129
129
|
mset(*kvpairs)
|
@@ -159,7 +159,7 @@ class MockRedis
|
|
159
159
|
if expire_option
|
160
160
|
type, duration = expire_option
|
161
161
|
if duration == 0
|
162
|
-
raise Redis::CommandError, 'ERR invalid expire time in
|
162
|
+
raise Redis::CommandError, 'ERR invalid expire time in set'
|
163
163
|
end
|
164
164
|
expire(key, type.to_sym == :ex ? duration : duration / 1000.0)
|
165
165
|
end
|
@@ -199,9 +199,17 @@ class MockRedis
|
|
199
199
|
char_as_number = char.each_byte.reduce(0) do |a, byte|
|
200
200
|
(a << 8) + byte
|
201
201
|
end
|
202
|
-
|
203
|
-
|
204
|
-
|
202
|
+
|
203
|
+
bitmask_length = (char.bytesize * 8 - offset_within_char * 8 - offset_within_byte - 1)
|
204
|
+
bitmask = 1 << bitmask_length
|
205
|
+
|
206
|
+
if value.zero?
|
207
|
+
bitmask ^= 2**(char.bytesize * 8) - 1
|
208
|
+
char_as_number &= bitmask
|
209
|
+
else
|
210
|
+
char_as_number |= bitmask
|
211
|
+
end
|
212
|
+
|
205
213
|
str[char_index] = char_as_number.chr
|
206
214
|
|
207
215
|
data[key] = str
|
@@ -10,14 +10,21 @@ class MockRedis
|
|
10
10
|
|
11
11
|
def initialize(db)
|
12
12
|
@db = db
|
13
|
-
@
|
13
|
+
@transaction_futures = []
|
14
14
|
@in_multi = false
|
15
|
+
@multi_block_given = false
|
15
16
|
end
|
16
17
|
|
17
18
|
def method_missing(method, *args, &block)
|
18
19
|
if @in_multi
|
19
|
-
|
20
|
-
|
20
|
+
future = MockRedis::Future.new([method, *args])
|
21
|
+
@transaction_futures << future
|
22
|
+
|
23
|
+
if @multi_block_given
|
24
|
+
future
|
25
|
+
else
|
26
|
+
'QUEUED'
|
27
|
+
end
|
21
28
|
else
|
22
29
|
@db.expire_keys
|
23
30
|
@db.send(method, *args, &block)
|
@@ -27,7 +34,7 @@ class MockRedis
|
|
27
34
|
def initialize_copy(source)
|
28
35
|
super
|
29
36
|
@db = @db.clone
|
30
|
-
@
|
37
|
+
@transaction_futures = @transaction_futures.clone
|
31
38
|
end
|
32
39
|
|
33
40
|
def discard
|
@@ -35,7 +42,8 @@ class MockRedis
|
|
35
42
|
raise Redis::CommandError, "ERR DISCARD without MULTI"
|
36
43
|
end
|
37
44
|
@in_multi = false
|
38
|
-
@
|
45
|
+
@multi_block_given = false
|
46
|
+
@transaction_futures = []
|
39
47
|
'OK'
|
40
48
|
end
|
41
49
|
|
@@ -44,14 +52,19 @@ class MockRedis
|
|
44
52
|
raise Redis::CommandError, "ERR EXEC without MULTI"
|
45
53
|
end
|
46
54
|
@in_multi = false
|
47
|
-
|
55
|
+
@multi_block_given = false
|
56
|
+
|
57
|
+
responses = @transaction_futures.map do |future|
|
48
58
|
begin
|
49
|
-
send(*
|
59
|
+
result = send(*future.command)
|
60
|
+
future.set_result(result)
|
61
|
+
result
|
50
62
|
rescue => e
|
51
63
|
e
|
52
64
|
end
|
53
65
|
end
|
54
|
-
|
66
|
+
|
67
|
+
@transaction_futures = []
|
55
68
|
responses
|
56
69
|
end
|
57
70
|
|
@@ -61,6 +74,7 @@ class MockRedis
|
|
61
74
|
end
|
62
75
|
@in_multi = true
|
63
76
|
if block_given?
|
77
|
+
@multi_block_given = true
|
64
78
|
begin
|
65
79
|
yield(self)
|
66
80
|
self.exec
|
data/lib/mock_redis/version.rb
CHANGED
@@ -19,7 +19,7 @@ class MockRedis
|
|
19
19
|
end
|
20
20
|
elsif args.size == 2
|
21
21
|
score, member = args
|
22
|
-
assert_scorey(score)
|
22
|
+
assert_scorey(score) unless score =~ /(\+|\-)inf/
|
23
23
|
retval = !zscore(key, member)
|
24
24
|
with_zset_at(key) {|z| z.add(score, member.to_s)}
|
25
25
|
else
|
@@ -34,8 +34,7 @@ class MockRedis
|
|
34
34
|
end
|
35
35
|
|
36
36
|
def zcount(key, min, max)
|
37
|
-
|
38
|
-
assert_scorey(max, 'ERR min or max is not a float') unless max == '+inf'
|
37
|
+
assert_range_args(min, max)
|
39
38
|
|
40
39
|
with_zset_at(key) do |zset|
|
41
40
|
zset.in_range(min, max).size
|
@@ -67,8 +66,7 @@ class MockRedis
|
|
67
66
|
end
|
68
67
|
|
69
68
|
def zrangebyscore(key, min, max, options={})
|
70
|
-
|
71
|
-
assert_scorey(max, 'ERR min or max is not a float') unless max == '+inf'
|
69
|
+
assert_range_args(min, max)
|
72
70
|
|
73
71
|
with_zset_at(key) do |zset|
|
74
72
|
all_results = zset.in_range(min, max)
|
@@ -107,8 +105,7 @@ class MockRedis
|
|
107
105
|
end
|
108
106
|
|
109
107
|
def zremrangebyscore(key, min, max)
|
110
|
-
|
111
|
-
assert_scorey(max, 'ERR min or max is not a float') unless max == '+inf'
|
108
|
+
assert_range_args(min, max)
|
112
109
|
|
113
110
|
zrangebyscore(key, min, max).
|
114
111
|
each {|member| zrem(key, member)}.
|
@@ -116,8 +113,7 @@ class MockRedis
|
|
116
113
|
end
|
117
114
|
|
118
115
|
def zrevrangebyscore(key, max, min, options={})
|
119
|
-
|
120
|
-
assert_scorey(max, 'ERR min or max is not a float') unless max == '+inf'
|
116
|
+
assert_range_args(min, max)
|
121
117
|
|
122
118
|
with_zset_at(key) do |zset|
|
123
119
|
to_response(
|
@@ -232,11 +228,18 @@ class MockRedis
|
|
232
228
|
end
|
233
229
|
|
234
230
|
def assert_scorey(value, message = "ERR value is not a valid float")
|
231
|
+
return if value =~ /\(?(\-|\+)inf/
|
232
|
+
|
235
233
|
value = $1 if value.to_s.match(/\((.*)/)
|
236
234
|
unless looks_like_float?(value)
|
237
235
|
raise Redis::CommandError, message
|
238
236
|
end
|
239
237
|
end
|
240
238
|
|
239
|
+
def assert_range_args(min, max)
|
240
|
+
[min, max].each do |value|
|
241
|
+
assert_scorey(value, 'ERR min or max is not a float')
|
242
|
+
end
|
243
|
+
end
|
241
244
|
end
|
242
245
|
end
|
data/mock_redis.gemspec
CHANGED
@@ -23,5 +23,6 @@ Gem::Specification.new do |s|
|
|
23
23
|
|
24
24
|
s.add_development_dependency 'rake', '~> 10.0'
|
25
25
|
s.add_development_dependency 'redis', '~> 3.0.0'
|
26
|
-
s.add_development_dependency 'rspec', '~>
|
26
|
+
s.add_development_dependency 'rspec', '~> 3.1.0'
|
27
|
+
s.add_development_dependency 'rspec-its', '~> 1.0'
|
27
28
|
end
|
@@ -4,11 +4,11 @@ describe "#exists(key)" do
|
|
4
4
|
before { @key = 'mock-redis-test:45794' }
|
5
5
|
|
6
6
|
it "returns false for keys that do not exist" do
|
7
|
-
@redises.exists(@key).should
|
7
|
+
@redises.exists(@key).should == false
|
8
8
|
end
|
9
9
|
|
10
10
|
it "returns true for keys that do exist" do
|
11
11
|
@redises.set(@key, 1)
|
12
|
-
@redises.exists(@key).should
|
12
|
+
@redises.exists(@key).should == true
|
13
13
|
end
|
14
14
|
end
|
@@ -7,11 +7,11 @@ describe "#expire(key, seconds)" do
|
|
7
7
|
end
|
8
8
|
|
9
9
|
it "returns true for a key that exists" do
|
10
|
-
@redises.expire(@key, 1).should
|
10
|
+
@redises.expire(@key, 1).should == true
|
11
11
|
end
|
12
12
|
|
13
13
|
it "returns false for a key that does not exist" do
|
14
|
-
@redises.expire('mock-redis-test:nonesuch', 1).should
|
14
|
+
@redises.expire('mock-redis-test:nonesuch', 1).should == false
|
15
15
|
end
|
16
16
|
|
17
17
|
it "removes a key immediately when seconds==0" do
|
@@ -91,5 +91,21 @@ describe "#expire(key, seconds)" do
|
|
91
91
|
end
|
92
92
|
end
|
93
93
|
|
94
|
+
context 'with two key expirations' do
|
95
|
+
let(:other_key) { 'mock-redis-test:expire-other' }
|
96
|
+
|
97
|
+
before { @redises.set(other_key, 'spork-other') }
|
98
|
+
|
99
|
+
it 'removes keys after enough time has passed' do
|
100
|
+
@mock.expire(@key, 5)
|
101
|
+
@mock.expire(other_key, 10)
|
102
|
+
|
103
|
+
Time.stub(:now).and_return(@now + 5)
|
104
|
+
@mock.get(@key).should be_nil
|
105
|
+
|
106
|
+
Time.stub(:now).and_return(@now + 10)
|
107
|
+
@mock.get(other_key).should be_nil
|
108
|
+
end
|
109
|
+
end
|
94
110
|
end
|
95
111
|
end
|
@@ -7,11 +7,11 @@ describe "#expireat(key, timestamp)" do
|
|
7
7
|
end
|
8
8
|
|
9
9
|
it "returns true for a key that exists" do
|
10
|
-
@redises.expireat(@key, Time.now.to_i + 1).should
|
10
|
+
@redises.expireat(@key, Time.now.to_i + 1).should == true
|
11
11
|
end
|
12
12
|
|
13
13
|
it "returns false for a key that does not exist" do
|
14
|
-
@redises.expireat('mock-redis-test:nonesuch', Time.now.to_i + 1).should
|
14
|
+
@redises.expireat('mock-redis-test:nonesuch', Time.now.to_i + 1).should == false
|
15
15
|
end
|
16
16
|
|
17
17
|
it "removes a key immediately when timestamp is now" do
|
@@ -7,20 +7,20 @@ describe "#hexists(key, field)" do
|
|
7
7
|
end
|
8
8
|
|
9
9
|
it "returns true if the hash has that field" do
|
10
|
-
@redises.hexists(@key, 'field').should
|
10
|
+
@redises.hexists(@key, 'field').should == true
|
11
11
|
end
|
12
12
|
|
13
13
|
it "returns false if the hash lacks that field" do
|
14
|
-
@redises.hexists(@key, 'nonesuch').should
|
14
|
+
@redises.hexists(@key, 'nonesuch').should == false
|
15
15
|
end
|
16
16
|
|
17
17
|
it "treats the field as a string" do
|
18
18
|
@redises.hset(@key, 1, 'one')
|
19
|
-
@redises.hexists(@key, 1).should
|
19
|
+
@redises.hexists(@key, 1).should == true
|
20
20
|
end
|
21
21
|
|
22
22
|
it "returns nil when there is no such key" do
|
23
|
-
@redises.hexists('mock-redis-test:nonesuch', 'key').should
|
23
|
+
@redises.hexists('mock-redis-test:nonesuch', 'key').should == false
|
24
24
|
end
|
25
25
|
|
26
26
|
it_should_behave_like "a hash-only command"
|