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.
Files changed (54) hide show
  1. checksums.yaml +4 -4
  2. data/.rspec +1 -1
  3. data/.travis.yml +3 -0
  4. data/CHANGELOG.md +10 -0
  5. data/Gemfile +0 -5
  6. data/README.md +2 -2
  7. data/lib/mock_redis.rb +1 -0
  8. data/lib/mock_redis/database.rb +11 -2
  9. data/lib/mock_redis/future.rb +22 -0
  10. data/lib/mock_redis/pipelined_wrapper.rb +0 -21
  11. data/lib/mock_redis/set_methods.rb +1 -0
  12. data/lib/mock_redis/string_methods.rb +13 -5
  13. data/lib/mock_redis/transaction_wrapper.rb +22 -8
  14. data/lib/mock_redis/version.rb +1 -1
  15. data/lib/mock_redis/zset_methods.rb +12 -9
  16. data/mock_redis.gemspec +2 -1
  17. data/spec/commands/connected_spec.rb +1 -1
  18. data/spec/commands/exists_spec.rb +2 -2
  19. data/spec/commands/expire_spec.rb +18 -2
  20. data/spec/commands/expireat_spec.rb +2 -2
  21. data/spec/commands/hexists_spec.rb +4 -4
  22. data/spec/commands/hsetnx_spec.rb +2 -2
  23. data/spec/commands/move_spec.rb +8 -8
  24. data/spec/commands/msetnx_spec.rb +2 -2
  25. data/spec/commands/persist_spec.rb +4 -4
  26. data/spec/commands/pexpire_spec.rb +2 -2
  27. data/spec/commands/pexpireat_spec.rb +2 -2
  28. data/spec/commands/renamenx_spec.rb +2 -2
  29. data/spec/commands/sadd_spec.rb +2 -2
  30. data/spec/commands/scan_spec.rb +51 -0
  31. data/spec/commands/set_spec.rb +6 -6
  32. data/spec/commands/setbit_spec.rb +7 -0
  33. data/spec/commands/setnx_spec.rb +2 -2
  34. data/spec/commands/sismember_spec.rb +5 -5
  35. data/spec/commands/smove_spec.rb +6 -6
  36. data/spec/commands/srem_spec.rb +3 -3
  37. data/spec/commands/sunion_spec.rb +4 -0
  38. data/spec/commands/zadd_spec.rb +2 -2
  39. data/spec/commands/zrange_spec.rb +1 -1
  40. data/spec/commands/zrangebyscore_spec.rb +1 -1
  41. data/spec/commands/zrem_spec.rb +3 -3
  42. data/spec/commands/zremrangebyscore_spec.rb +1 -0
  43. data/spec/commands/zrevrange_spec.rb +1 -1
  44. data/spec/commands/zrevrangebyscore_spec.rb +1 -1
  45. data/spec/commands/zscore_spec.rb +2 -2
  46. data/spec/spec_helper.rb +11 -2
  47. data/spec/support/redis_multiplexer.rb +6 -6
  48. data/spec/support/shared_examples/only_operates_on_hashes.rb +2 -2
  49. data/spec/support/shared_examples/only_operates_on_lists.rb +2 -2
  50. data/spec/support/shared_examples/only_operates_on_sets.rb +2 -2
  51. data/spec/support/shared_examples/only_operates_on_strings.rb +2 -2
  52. data/spec/support/shared_examples/only_operates_on_zsets.rb +4 -4
  53. data/spec/transactions_spec.rb +23 -0
  54. metadata +36 -18
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: b3d7978080e5d75386c90cbae717fee17d6194c7
4
- data.tar.gz: abfc802fe90cf8e2a9a996311b0859451a8abff0
3
+ metadata.gz: 994ce4cc7000897238012b0a9f11a8417b31358d
4
+ data.tar.gz: 44f03b45a1310b977d0bf996fe042a7b32c70b36
5
5
  SHA512:
6
- metadata.gz: 19b2a107cbb896b8fd5435a83e84c45e64a5b0603e1e130611fcf4655e4f20a68427d230c4cd6c4133925a1bbf8bd49a783a0f94b676c5999df7f1f94bdb8b96
7
- data.tar.gz: a74a10f31f47764a4c09ed3a838587b384771f3a72130ea57dc8e09cb82556fa0974fc3e12285d78c4374580b0f7f5bd3686badea137fee95aa20231c2558f5f
6
+ metadata.gz: 84bd359ec9c9eff9b5b40cacc7ac65f206bdd8a720d00445fcb53cd0729abcd003c3fe66f96e220728575f305e00495c0fa75a55290a268ba304f4cf92597035
7
+ data.tar.gz: e09387fa284e38847e8ebec0d8ed1a7c3681ebfad0b661a239c54b976adada92ffb08661f736abdde1686aaba5ed394e53d6620326979b84811c42d65d63ba2c
data/.rspec CHANGED
@@ -1 +1 @@
1
- -c -d
1
+ -c
data/.travis.yml CHANGED
@@ -7,6 +7,9 @@ rvm:
7
7
  - 2.1.1
8
8
  - jruby-19mode
9
9
 
10
+ script:
11
+ - bundle exec rspec
12
+
10
13
  matrix:
11
14
  allow_failures:
12
15
  - rvm: 2.1.0
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
@@ -2,8 +2,3 @@ source "http://rubygems.org"
2
2
 
3
3
  # Specify your gem's dependencies in mock_redis.gemspec
4
4
  gemspec
5
-
6
- group :test do
7
- gem 'debugger', platforms: [:mri, :mswin]
8
- gem 'ruby-debug', platforms: [:jruby]
9
- end
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.4*. Older versions
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 `2.8.4` running on localhost without any passwords
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'
@@ -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
 
@@ -127,6 +127,7 @@ class MockRedis
127
127
  end
128
128
 
129
129
  def with_sets_at(*keys, &blk)
130
+ keys = keys.flatten
130
131
  if keys.length == 1
131
132
  with_set_at(keys.first, &blk)
132
133
  else
@@ -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,v)| exists(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 SETEX'
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
- char_as_number ^=
203
- (2**((char.bytesize * 8)-1) >>
204
- (offset_within_char * 8 + offset_within_byte))
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
- @queued_commands = []
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
- @queued_commands << [method, *args]
20
- 'QUEUED'
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
- @queued_commands = @queued_commands.clone
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
- @queued_commands = []
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
- responses = @queued_commands.map do |cmd|
55
+ @multi_block_given = false
56
+
57
+ responses = @transaction_futures.map do |future|
48
58
  begin
49
- send(*cmd)
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
- @queued_commands = []
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
@@ -1,4 +1,4 @@
1
1
  # Defines the gem version.
2
2
  class MockRedis
3
- VERSION = '0.13.2'
3
+ VERSION = '0.14.0'
4
4
  end
@@ -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
- assert_scorey(min, 'ERR min or max is not a float') unless min == '-inf'
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
- assert_scorey(min, 'ERR min or max is not a float') unless min == '-inf'
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
- assert_scorey(min, 'ERR min or max is not a float') unless min == '-inf'
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
- assert_scorey(min, 'ERR min or max is not a float') unless min == '-inf'
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', '~> 2.14.0'
26
+ s.add_development_dependency 'rspec', '~> 3.1.0'
27
+ s.add_development_dependency 'rspec-its', '~> 1.0'
27
28
  end
@@ -2,6 +2,6 @@ require 'spec_helper'
2
2
 
3
3
  describe "#connected? [mock only]" do
4
4
  it "returns true" do
5
- @redises.mock.connected?.should be_true
5
+ @redises.mock.connected?.should == true
6
6
  end
7
7
  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 be_false
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 be_true
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 be_true
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 be_false
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 be_true
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 be_false
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 be_true
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 be_false
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 be_true
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 be_false
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"