mock_redis 0.19.0 → 0.24.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 (81) hide show
  1. checksums.yaml +4 -4
  2. data/.rubocop.yml +32 -5
  3. data/.rubocop_todo.yml +1 -1
  4. data/.travis.yml +9 -10
  5. data/CHANGELOG.md +46 -0
  6. data/Gemfile +2 -2
  7. data/LICENSE.md +21 -0
  8. data/README.md +39 -15
  9. data/lib/mock_redis.rb +0 -5
  10. data/lib/mock_redis/database.rb +59 -22
  11. data/lib/mock_redis/future.rb +1 -1
  12. data/lib/mock_redis/geospatial_methods.rb +14 -22
  13. data/lib/mock_redis/hash_methods.rb +23 -15
  14. data/lib/mock_redis/indifferent_hash.rb +0 -8
  15. data/lib/mock_redis/info_method.rb +2 -2
  16. data/lib/mock_redis/list_methods.rb +2 -2
  17. data/lib/mock_redis/multi_db_wrapper.rb +2 -2
  18. data/lib/mock_redis/pipelined_wrapper.rb +25 -6
  19. data/lib/mock_redis/set_methods.rb +16 -4
  20. data/lib/mock_redis/stream.rb +62 -0
  21. data/lib/mock_redis/stream/id.rb +60 -0
  22. data/lib/mock_redis/stream_methods.rb +87 -0
  23. data/lib/mock_redis/string_methods.rb +31 -20
  24. data/lib/mock_redis/transaction_wrapper.rb +27 -14
  25. data/lib/mock_redis/utility_methods.rb +6 -3
  26. data/lib/mock_redis/version.rb +1 -1
  27. data/lib/mock_redis/zset_methods.rb +54 -11
  28. data/mock_redis.gemspec +6 -6
  29. data/spec/client_spec.rb +12 -0
  30. data/spec/commands/blpop_spec.rb +0 -6
  31. data/spec/commands/brpop_spec.rb +6 -5
  32. data/spec/commands/dump_spec.rb +19 -0
  33. data/spec/commands/exists_spec.rb +34 -5
  34. data/spec/commands/future_spec.rb +11 -1
  35. data/spec/commands/geoadd_spec.rb +1 -1
  36. data/spec/commands/geodist_spec.rb +8 -4
  37. data/spec/commands/geohash_spec.rb +4 -4
  38. data/spec/commands/geopos_spec.rb +4 -4
  39. data/spec/commands/get_spec.rb +1 -0
  40. data/spec/commands/hdel_spec.rb +18 -2
  41. data/spec/commands/hmset_spec.rb +26 -0
  42. data/spec/commands/hset_spec.rb +6 -6
  43. data/spec/commands/keys_spec.rb +17 -0
  44. data/spec/commands/mget_spec.rb +34 -15
  45. data/spec/commands/move_spec.rb +5 -5
  46. data/spec/commands/mset_spec.rb +14 -0
  47. data/spec/commands/pipelined_spec.rb +72 -0
  48. data/spec/commands/restore_spec.rb +47 -0
  49. data/spec/commands/scan_spec.rb +9 -0
  50. data/spec/commands/set_spec.rb +12 -2
  51. data/spec/commands/setbit_spec.rb +1 -0
  52. data/spec/commands/setex_spec.rb +16 -0
  53. data/spec/commands/spop_spec.rb +15 -0
  54. data/spec/commands/srandmember_spec.rb +1 -1
  55. data/spec/commands/srem_spec.rb +5 -0
  56. data/spec/commands/watch_spec.rb +8 -3
  57. data/spec/commands/xadd_spec.rb +104 -0
  58. data/spec/commands/xlen_spec.rb +20 -0
  59. data/spec/commands/xrange_spec.rb +141 -0
  60. data/spec/commands/xrevrange_spec.rb +130 -0
  61. data/spec/commands/xtrim_spec.rb +30 -0
  62. data/spec/commands/zinterstore_spec.rb +34 -0
  63. data/spec/commands/zpopmax_spec.rb +60 -0
  64. data/spec/commands/zpopmin_spec.rb +60 -0
  65. data/spec/commands/zrange_spec.rb +1 -1
  66. data/spec/commands/zrangebyscore_spec.rb +1 -1
  67. data/spec/commands/zrevrange_spec.rb +1 -1
  68. data/spec/commands/zrevrangebyscore_spec.rb +1 -1
  69. data/spec/commands/zunionstore_spec.rb +33 -0
  70. data/spec/mock_redis_spec.rb +4 -6
  71. data/spec/spec_helper.rb +6 -2
  72. data/spec/support/redis_multiplexer.rb +18 -1
  73. data/spec/support/shared_examples/does_not_cleanup_empty_strings.rb +14 -0
  74. data/spec/support/shared_examples/only_operates_on_hashes.rb +2 -0
  75. data/spec/support/shared_examples/only_operates_on_lists.rb +2 -0
  76. data/spec/support/shared_examples/only_operates_on_sets.rb +2 -0
  77. data/spec/support/shared_examples/only_operates_on_zsets.rb +2 -0
  78. data/spec/transactions_spec.rb +30 -26
  79. metadata +45 -31
  80. data/LICENSE +0 -19
  81. data/spec/commands/hash_operator_spec.rb +0 -21
@@ -6,9 +6,9 @@ Gem::Specification.new do |s|
6
6
  s.version = MockRedis::VERSION
7
7
  s.license = 'MIT'
8
8
  s.platform = Gem::Platform::RUBY
9
- s.authors = ['Brigade Engineering', 'Samuel Merritt']
10
- s.email = ['eng@brigade.com']
11
- s.homepage = 'https://github.com/brigade/mock_redis'
9
+ s.authors = ['Shane da Silva', 'Samuel Merritt']
10
+ s.email = ['shane@dasilva.io']
11
+ s.homepage = 'https://github.com/sds/mock_redis'
12
12
  s.summary = 'Redis mock that just lives in memory; useful for testing.'
13
13
 
14
14
  s.description = <<-MSG.strip.gsub(/\s+/, ' ')
@@ -21,10 +21,10 @@ Gem::Specification.new do |s|
21
21
  s.executables = `git ls-files -- bin/*`.split("\n").map { |f| File.basename(f) }
22
22
  s.require_paths = ['lib']
23
23
 
24
- s.required_ruby_version = '>= 2.2'
24
+ s.required_ruby_version = '>= 2.4'
25
25
 
26
- s.add_development_dependency 'rake', '>= 10', '< 12'
27
- s.add_development_dependency 'redis', '~> 3.3.0'
26
+ s.add_development_dependency 'redis', '~> 4.2.0'
28
27
  s.add_development_dependency 'rspec', '~> 3.0'
29
28
  s.add_development_dependency 'rspec-its', '~> 1.0'
29
+ s.add_development_dependency 'timecop', '~> 0.9.1'
30
30
  end
@@ -14,4 +14,16 @@ describe 'client' do
14
14
  redis.connect.should == redis
15
15
  end
16
16
  end
17
+
18
+ context '#disconnect!' do
19
+ it 'responds to disconnect!' do
20
+ expect(MockRedis.new).to respond_to(:disconnect!)
21
+ end
22
+ end
23
+
24
+ context '#close' do
25
+ it 'responds to close' do
26
+ expect(MockRedis.new).to respond_to(:close)
27
+ end
28
+ end
17
29
  end
@@ -27,12 +27,6 @@ describe '#blpop(key [, key, ...,], timeout)' do
27
27
  [@list1, 'one']
28
28
  end
29
29
 
30
- it 'raises an error on subsecond timeouts' do
31
- lambda do
32
- @redises.blpop(@list1, @list2, :timeout => 0.5)
33
- end.should raise_error(Redis::CommandError)
34
- end
35
-
36
30
  it 'raises an error on negative timeout' do
37
31
  lambda do
38
32
  @redises.blpop(@list1, @list2, :timeout => -1)
@@ -26,11 +26,12 @@ describe '#brpop(key [, key, ...,], timeout)' do
26
26
  [@list1, 'two']
27
27
  end
28
28
 
29
- it 'raises an error on subsecond timeouts' do
30
- lambda do
31
- @redises.brpop(@list1, @list2, :timeout => 0.5)
32
- end.should raise_error(Redis::CommandError)
33
- end
29
+ # TODO: Not sure how redis-rb is handling this but they're not raising an error
30
+ # it 'raises an error on subsecond timeouts' do
31
+ # lambda do
32
+ # @redises.brpop(@list1, @list2, :timeout => 0.5)
33
+ # end.should raise_error(Redis::CommandError)
34
+ # end
34
35
 
35
36
  it 'raises an error on negative timeout' do
36
37
  lambda do
@@ -0,0 +1,19 @@
1
+ require 'spec_helper'
2
+
3
+ describe '#dump(key)' do
4
+ before do
5
+ @key = 'mock-redis-test:45794'
6
+ # These are mock-only, since our dump/restore implementations
7
+ # aren't compatible with real redis.
8
+ @mock = @redises.mock
9
+ end
10
+
11
+ it 'returns nil for keys that do not exist' do
12
+ @mock.dump(@key).should be_nil
13
+ end
14
+
15
+ it 'returns a serialized value for keys that do exist' do
16
+ @mock.set(@key, '2')
17
+ @mock.dump(@key).should == Marshal.dump('2')
18
+ end
19
+ end
@@ -1,14 +1,43 @@
1
1
  require 'spec_helper'
2
2
 
3
- describe '#exists(key)' do
4
- before { @key = 'mock-redis-test:45794' }
3
+ describe '#exists(*keys)' do
4
+ before { @key1 = 'mock-redis-test:exists1' }
5
+ before { @key2 = 'mock-redis-test:exists2' }
6
+
7
+ it 'returns 0 for keys that do not exist' do
8
+ @redises.exists(@key1).should == 0
9
+ @redises.exists(@key1, @key2).should == 0
10
+ end
11
+
12
+ it 'returns 1 for keys that do exist' do
13
+ @redises.set(@key1, 1)
14
+ @redises.exists(@key1).should == 1
15
+ end
16
+
17
+ it 'returns the count of all keys that exist' do
18
+ @redises.set(@key1, 1)
19
+ @redises.set(@key2, 1)
20
+ @redises.exists(@key1, @key2).should == 2
21
+ @redises.exists(@key1, @key2, 'does-not-exist').should == 2
22
+ end
23
+ end
24
+
25
+ describe '#exists?(*keys)' do
26
+ before { @key1 = 'mock-redis-test:exists1' }
27
+ before { @key2 = 'mock-redis-test:exists2' }
5
28
 
6
29
  it 'returns false for keys that do not exist' do
7
- @redises.exists(@key).should == false
30
+ @redises.exists?(@key1).should == false
31
+ @redises.exists?(@key1, @key2).should == false
8
32
  end
9
33
 
10
34
  it 'returns true for keys that do exist' do
11
- @redises.set(@key, 1)
12
- @redises.exists(@key).should == true
35
+ @redises.set(@key1, 1)
36
+ @redises.exists?(@key1).should == true
37
+ end
38
+
39
+ it 'returns true if any keys exist' do
40
+ @redises.set(@key2, 1)
41
+ @redises.exists?(@key1, @key2).should == true
13
42
  end
14
43
  end
@@ -3,7 +3,12 @@ require 'spec_helper'
3
3
  describe MockRedis::Future do
4
4
  let(:command) { [:get, 'foo'] }
5
5
  let(:result) { 'bar' }
6
- before { @future = MockRedis::Future.new(command) }
6
+ let(:block) { ->(value) { value.upcase } }
7
+
8
+ before do
9
+ @future = MockRedis::Future.new(command)
10
+ @future2 = MockRedis::Future.new(command, block)
11
+ end
7
12
 
8
13
  it 'remembers the command' do
9
14
  @future.command.should eq(command)
@@ -17,4 +22,9 @@ describe MockRedis::Future do
17
22
  @future.store_result(result)
18
23
  @future.value.should eq(result)
19
24
  end
25
+
26
+ it 'executes the block on the value if block is passed in' do
27
+ @future2.store_result(result)
28
+ @future2.value.should eq('BAR')
29
+ end
20
30
  end
@@ -33,7 +33,7 @@ describe '#geoadd' do
33
33
  context 'when coordinates are not in allowed range' do
34
34
  let(:coords) { [181, 86] }
35
35
  let(:message) do
36
- formatted_coords = coords.map { |c| format('%.6f', c) }
36
+ formatted_coords = coords.map { |c| format('%<coords>.6f', coords: c) }
37
37
  "ERR invalid longitude,latitude pair #{formatted_coords.join(',')}"
38
38
  end
39
39
 
@@ -80,13 +80,14 @@ describe '#geodist' do
80
80
 
81
81
  context 'with less than 3 arguments' do
82
82
  [1, 2].each do |count|
83
- let(:message) { "ERR wrong number of arguments for 'geodist' command" }
84
-
85
83
  context "with #{count} arguments" do
86
84
  it 'raises an error' do
87
85
  args = list.slice(0, count)
88
86
  expect { @redises.geodist(*args) }
89
- .to raise_error(Redis::CommandError, message)
87
+ .to raise_error(
88
+ ArgumentError,
89
+ /wrong number of arguments \((given\s)?#{count}(,\sexpected\s|\sfor\s)3?\.\.4\)$/
90
+ )
90
91
  end
91
92
  end
92
93
  end
@@ -98,7 +99,10 @@ describe '#geodist' do
98
99
  it 'raises an error' do
99
100
  args = list.slice(0, 5)
100
101
  expect { @redises.geodist(*args) }
101
- .to raise_error(Redis::CommandError, message)
102
+ .to raise_error(
103
+ ArgumentError,
104
+ /wrong number of arguments \((given\s)?5(,\sexpected\s|\sfor\s)3?\.\.4\)$/
105
+ )
102
106
  end
103
107
  end
104
108
  end
@@ -17,13 +17,13 @@ describe '#geohash' do
17
17
  end
18
18
 
19
19
  it 'returns decoded coordinates pairs for each point' do
20
- results = @redises.geohash(key, 'SF', 'LA')
20
+ results = @redises.geohash(key, %w[SF LA])
21
21
  expect(results).to be == expected_result
22
22
  end
23
23
 
24
24
  context 'with non-existing points only' do
25
25
  it 'returns array filled with nils' do
26
- results = @redises.geohash(key, 'FF', 'FA')
26
+ results = @redises.geohash(key, %w[FF FA])
27
27
  expect(results).to be == [nil, nil]
28
28
  end
29
29
  end
@@ -34,7 +34,7 @@ describe '#geohash' do
34
34
  end
35
35
 
36
36
  it 'returns mixture of nil and coordinates pair' do
37
- results = @redises.geohash(key, 'SF', 'FA')
37
+ results = @redises.geohash(key, %w[SF FA])
38
38
  expect(results).to be == expected_result
39
39
  end
40
40
  end
@@ -45,7 +45,7 @@ describe '#geohash' do
45
45
  before { @redises.del(key) }
46
46
 
47
47
  it 'returns empty array' do
48
- results = @redises.geohash(key, 'SF', 'LA')
48
+ results = @redises.geohash(key, %w[SF LA])
49
49
  expect(results).to be == [nil, nil]
50
50
  end
51
51
  end
@@ -20,13 +20,13 @@ describe '#geopos' do
20
20
  end
21
21
 
22
22
  it 'returns decoded coordinates pairs for each point' do
23
- coords = @redises.geopos(key, 'SF', 'LA')
23
+ coords = @redises.geopos(key, %w[SF LA])
24
24
  expect(coords).to be == expected_result
25
25
  end
26
26
 
27
27
  context 'with non-existing points only' do
28
28
  it 'returns array filled with nils' do
29
- coords = @redises.geopos(key, 'FF', 'FA')
29
+ coords = @redises.geopos(key, %w[FF FA])
30
30
  expect(coords).to be == [nil, nil]
31
31
  end
32
32
  end
@@ -37,7 +37,7 @@ describe '#geopos' do
37
37
  end
38
38
 
39
39
  it 'returns mixture of nil and coordinates pair' do
40
- coords = @redises.geopos(key, 'SF', 'FA')
40
+ coords = @redises.geopos(key, %w[SF FA])
41
41
  expect(coords).to be == expected_result
42
42
  end
43
43
  end
@@ -48,7 +48,7 @@ describe '#geopos' do
48
48
  before { @redises.del(key) }
49
49
 
50
50
  it 'returns empty array' do
51
- coords = @redises.geopos(key, 'SF', 'LA')
51
+ coords = @redises.geopos(key, %w[SF LA])
52
52
  expect(coords).to be == [nil, nil]
53
53
  end
54
54
  end
@@ -24,6 +24,7 @@ describe '#get(key)' do
24
24
 
25
25
  @redises.set(key, 'hello')
26
26
  @redises.get(key.to_s).should == 'hello'
27
+ @redises.get(key).should == 'hello'
27
28
  end
28
29
 
29
30
  it_should_behave_like 'a string-only command'
@@ -39,16 +39,32 @@ describe '#hdel(key, field)' do
39
39
  end
40
40
 
41
41
  it 'supports a variable number of arguments' do
42
- @redises.hdel(@key, %w[k1 k2])
42
+ @redises.hdel(@key, 'k1', 'k2')
43
43
  @redises.get(@key).should be_nil
44
44
  end
45
45
 
46
46
  it 'treats variable arguments as strings' do
47
47
  field = 2
48
48
  @redises.hset(@key, field, 'two')
49
- @redises.hdel(@key, [field])
49
+ @redises.hdel(@key, field)
50
50
  @redises.hget(@key, field).should be_nil
51
51
  end
52
52
 
53
+ it 'supports a variable number of fields as array' do
54
+ @redises.hdel(@key, %w[k1 k2]).should == 2
55
+
56
+ @redises.hget(@key, 'k1').should be_nil
57
+ @redises.hget(@key, 'k2').should be_nil
58
+ @redises.get(@key).should be_nil
59
+ end
60
+
61
+ it 'supports a list of fields in various way' do
62
+ @redises.hdel(@key, ['k1'], 'k2').should == 2
63
+
64
+ @redises.hget(@key, 'k1').should be_nil
65
+ @redises.hget(@key, 'k2').should be_nil
66
+ @redises.get(@key).should be_nil
67
+ end
68
+
53
69
  it_should_behave_like 'a hash-only command'
54
70
  end
@@ -39,5 +39,31 @@ describe '#hmset(key, field, value [, field, value ...])' do
39
39
  end.should raise_error(Redis::CommandError)
40
40
  end
41
41
 
42
+ # The following tests address https://github.com/sds/mock_redis/issues/170
43
+ context 'keys are stored as strings' do
44
+ before do
45
+ @redises.hmset(1, :foo, :bar)
46
+ @redises.hmset(:a_sym, :boo, :bas)
47
+ end
48
+
49
+ it { expect(@redises.hgetall('1')).to eq @redises.hgetall(1) }
50
+ it { expect(@redises.hgetall('a_sym')).to eq @redises.hgetall(:a_sym) }
51
+ it { expect(@redises.del('1')).to eq 1 }
52
+ it { expect(@redises.del(1)).to eq 1 }
53
+ it { expect(@redises.del('a_sym')).to eq 1 }
54
+ it { expect(@redises.del(:a_sym)).to eq 1 }
55
+
56
+ after do
57
+ @redises.del(1)
58
+ @redises.del(:a_sym)
59
+ end
60
+ end
61
+
62
+ # The following tests address https://github.com/sds/mock_redis/issues/134
63
+ context 'hmset accepts an array as its only argument' do
64
+ it { expect(@redises.hmset([@key, :bar, :bas])).to eq 'OK' }
65
+ it { lambda { @redises.hmset([:foo, :bar]) }.should raise_error(Redis::CommandError) }
66
+ end
67
+
42
68
  it_should_behave_like 'a hash-only command'
43
69
  end
@@ -5,18 +5,18 @@ describe '#hset(key, field)' do
5
5
  @key = 'mock-redis-test:hset'
6
6
  end
7
7
 
8
- it 'returns true if the key does not exist' do
9
- @redises.hset(@key, 'k1', 'v1').should == true
8
+ it 'returns 1 if the key does not exist' do
9
+ @redises.hset(@key, 'k1', 'v1').should == 1
10
10
  end
11
11
 
12
- it 'returns true if the key exists but the field does not' do
12
+ it 'returns 1 if the key exists but the field does not' do
13
13
  @redises.hset(@key, 'k1', 'v1')
14
- @redises.hset(@key, 'k2', 'v2').should == true
14
+ @redises.hset(@key, 'k2', 'v2').should == 1
15
15
  end
16
16
 
17
- it 'returns false if the field already exists' do
17
+ it 'returns 0 if the field already exists' do
18
18
  @redises.hset(@key, 'k1', 'v1')
19
- @redises.hset(@key, 'k1', 'v1').should == false
19
+ @redises.hset(@key, 'k1', 'v1').should == 0
20
20
  end
21
21
 
22
22
  it 'creates a hash there is no such field' do
@@ -29,6 +29,7 @@ describe '#keys()' do
29
29
 
30
30
  @redises.set('mock-redis-test:special-key?', 'true')
31
31
  @redises.set('mock-redis-test:special-key*', 'true')
32
+ @redises.set('mock-redis-test:special-key-!?*', 'true')
32
33
  end
33
34
 
34
35
  describe 'the ? character' do
@@ -53,6 +54,22 @@ describe '#keys()' do
53
54
  'mock-redis-test:special-key?',
54
55
  ]
55
56
  end
57
+
58
+ context 'multiple ? characters' do
59
+ it "properly handles multiple consequtive '?' characters" do
60
+ @redises.keys('mock-redis-test:special-key-???').sort.should == [
61
+ 'mock-redis-test:special-key-!?*',
62
+ ]
63
+ end
64
+
65
+ context '\\? as a literal ' do
66
+ it 'handles multiple ? as both literal and special character' do
67
+ @redises.keys('mock-redis-test:special-key-?\??').sort.should == [
68
+ 'mock-redis-test:special-key-!?*',
69
+ ]
70
+ end
71
+ end
72
+ end
56
73
  end
57
74
 
58
75
  describe 'the * character' do
@@ -9,26 +9,45 @@ describe '#mget(key [, key, ...])' do
9
9
  @redises.set(@key2, 2)
10
10
  end
11
11
 
12
- it 'returns an array of values' do
13
- @redises.mget(@key1, @key2).should == %w[1 2]
14
- end
12
+ context 'emulate param array' do
13
+ it 'returns an array of values' do
14
+ @redises.mget([@key1, @key2]).should == %w[1 2]
15
+ end
15
16
 
16
- it 'returns nil for missing keys' do
17
- @redises.mget(@key1, 'mock-redis-test:not-found', @key2).
18
- should == ['1', nil, '2']
19
- end
17
+ it 'returns an array of values' do
18
+ @redises.mget([@key1, @key2]).should == %w[1 2]
19
+ end
20
20
 
21
- it 'returns nil for non-string keys' do
22
- list = 'mock-redis-test:mget-list'
21
+ it 'returns nil for non-string keys' do
22
+ list = 'mock-redis-test:mget-list'
23
23
 
24
- @redises.lpush(list, 'bork bork bork')
24
+ @redises.lpush(list, 'bork bork bork')
25
25
 
26
- @redises.mget(@key1, @key2, list).should == ['1', '2', nil]
26
+ @redises.mget([@key1, @key2, list]).should == ['1', '2', nil]
27
+ end
27
28
  end
28
29
 
29
- it 'raises an error if you pass it 0 arguments' do
30
- lambda do
31
- @redises.mget
32
- end.should raise_error(Redis::CommandError)
30
+ context 'emulate params strings' do
31
+ it 'returns an array of values' do
32
+ @redises.mget(@key1, @key2).should == %w[1 2]
33
+ end
34
+
35
+ it 'returns nil for missing keys' do
36
+ @redises.mget(@key1, 'mock-redis-test:not-found', @key2).should == ['1', nil, '2']
37
+ end
38
+
39
+ it 'returns nil for non-string keys' do
40
+ list = 'mock-redis-test:mget-list'
41
+
42
+ @redises.lpush(list, 'bork bork bork')
43
+
44
+ @redises.mget(@key1, @key2, list).should == ['1', '2', nil]
45
+ end
46
+
47
+ it 'raises an error if you pass it 0 arguments' do
48
+ lambda do
49
+ @redises.mget
50
+ end.should raise_error(Redis::CommandError)
51
+ end
33
52
  end
34
53
  end