mock_redis 0.13.2 → 0.14.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.
- 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"
|