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
@@ -64,7 +64,7 @@ describe '#move(key, db)' do
64
64
  end
65
65
 
66
66
  it 'removes key from srcdb' do
67
- @redises.exists(@key).should == false
67
+ @redises.exists?(@key).should == false
68
68
  end
69
69
 
70
70
  it 'copies key to destdb' do
@@ -81,7 +81,7 @@ describe '#move(key, db)' do
81
81
  end
82
82
 
83
83
  it 'removes key from srcdb' do
84
- @redises.exists(@key).should == false
84
+ @redises.exists?(@key).should == false
85
85
  end
86
86
 
87
87
  it 'copies key to destdb' do
@@ -99,7 +99,7 @@ describe '#move(key, db)' do
99
99
  end
100
100
 
101
101
  it 'removes key from srcdb' do
102
- @redises.exists(@key).should == false
102
+ @redises.exists?(@key).should == false
103
103
  end
104
104
 
105
105
  it 'copies key to destdb' do
@@ -117,7 +117,7 @@ describe '#move(key, db)' do
117
117
  end
118
118
 
119
119
  it 'removes key from srcdb' do
120
- @redises.exists(@key).should == false
120
+ @redises.exists?(@key).should == false
121
121
  end
122
122
 
123
123
  it 'copies key to destdb' do
@@ -135,7 +135,7 @@ describe '#move(key, db)' do
135
135
  end
136
136
 
137
137
  it 'removes key from srcdb' do
138
- @redises.exists(@key).should == false
138
+ @redises.exists?(@key).should == false
139
139
  end
140
140
 
141
141
  it 'copies key to destdb' do
@@ -10,6 +10,14 @@ describe '#mset(key, value [, key, value, ...])' do
10
10
  @redises.mset(@key1, 1).should == 'OK'
11
11
  end
12
12
 
13
+ it "responds with 'OK' for passed Array with 1 item" do
14
+ @redises.mset([@key1, 1]).should == 'OK'
15
+ end
16
+
17
+ it "responds with 'OK' for passed Array with 2 items" do
18
+ @redises.mset([@key1, 1, @key2, 2]).should == 'OK'
19
+ end
20
+
13
21
  it 'sets the values' do
14
22
  @redises.mset(@key1, 'value1', @key2, 'value2')
15
23
  @redises.mget(@key1, @key2).should == %w[value1 value2]
@@ -26,4 +34,10 @@ describe '#mset(key, value [, key, value, ...])' do
26
34
  @redises.mset
27
35
  end.should raise_error(Redis::CommandError)
28
36
  end
37
+
38
+ it 'raises an error if given odd-sized array' do
39
+ lambda do
40
+ @redises.mset([@key1, 1, @key2])
41
+ end.should raise_error(Redis::CommandError)
42
+ end
29
43
  end
@@ -39,4 +39,76 @@ describe '#pipelined' do
39
39
  future.class.should be MockRedis::Future
40
40
  end
41
41
  end
42
+
43
+ context 'with pipelined operations returning array replies' do
44
+ let(:key1) { 'colors' }
45
+ let(:key2) { 'seasons' }
46
+ let(:value1) { %w[blue yellow] }
47
+ let(:value2) { %w[summer winter] }
48
+
49
+ before do
50
+ @redises.rpush(key1, value1)
51
+ @redises.rpush(key2, value2)
52
+ end
53
+
54
+ after do
55
+ @redises.del key1
56
+ @redises.del key2
57
+ end
58
+
59
+ it 'returns an array of the array replies' do
60
+ results = @redises.pipelined do |_redis|
61
+ @redises.lrange(key1, 0, -1)
62
+ @redises.lrange(key2, 0, -1)
63
+ end
64
+
65
+ results.should == [value1, value2]
66
+ end
67
+ end
68
+
69
+ context 'with redis time return value' do
70
+ let(:time_stub) { double 'Time', :now => Time.new(2019, 1, 2, 3, 4, 6, '+00:00') }
71
+ let(:options) { { :time_class => time_stub } }
72
+
73
+ subject { MockRedis.new(options) }
74
+
75
+ it 'returns the time value' do
76
+ subject.set('foo', 'bar')
77
+
78
+ results = subject.pipelined do
79
+ subject.get('foo')
80
+ subject.host # defined on MockRedis, so not captured
81
+ subject.time
82
+ subject.echo('baz')
83
+ end
84
+
85
+ expect(results).to eq(['bar', [1_546_398_246, 0], 'baz'])
86
+ end
87
+ end
88
+
89
+ context 'with nested pipelines' do
90
+ let(:key1) { 'hello' }
91
+ let(:key2) { 'world' }
92
+ let(:value1) { 'foo' }
93
+ let(:value2) { 'bar' }
94
+
95
+ before do
96
+ @redises.set key1, value1
97
+ @redises.set key2, value2
98
+ end
99
+
100
+ it 'returns results of the nested pipelines' do
101
+ results = @redises.pipelined do |pr1|
102
+ pr1.pipelined do |pr2|
103
+ pr2.get key1
104
+ end
105
+
106
+ pr1.pipelined do |pr2|
107
+ pr2.get key2
108
+ end
109
+ end
110
+
111
+ results.should == [value1, value2]
112
+ end
113
+ end
42
114
  end
@@ -0,0 +1,47 @@
1
+ require 'spec_helper'
2
+
3
+ describe '#restore(key, ttl, value)' do
4
+ before do
5
+ @key = 'mock-redis-test:45794'
6
+ @src = MockRedis.new
7
+ @src.set(@key, '123')
8
+ @dumped_value = @src.dump(@key)
9
+ @dst = MockRedis.new
10
+ @now = Time.now.round
11
+ Time.stub(:now).and_return(@now)
12
+ end
13
+
14
+ it 'allows dump/restoring values between two redis instances' do
15
+ expect(@dst.restore(@key, 0, @dumped_value)).to eq('OK')
16
+ expect(@dst.get(@key)).to eq('123')
17
+ expect(@dst.pttl(@key)).to eq(-1)
18
+ end
19
+
20
+ context 'when the key being restored to already exists' do
21
+ before do
22
+ @dst.set(@key, '456')
23
+ end
24
+
25
+ it 'raises an error by default' do
26
+ expect { @dst.restore(@key, 0, @dumped_value) }.to raise_error(Redis::CommandError)
27
+ expect(@dst.get(@key)).to eq('456')
28
+ end
29
+
30
+ it 'allows replacing the key if replace==true' do
31
+ expect(@dst.restore(@key, 0, @dumped_value, replace: true)).to eq('OK')
32
+ expect(@dst.get(@key)).to eq('123')
33
+ end
34
+ end
35
+
36
+ it 'sets ttl in ms' do
37
+ @dst.restore(@key, 500, @dumped_value)
38
+ expect(@dst.pttl(@key)).to eq(500)
39
+ end
40
+
41
+ it 'can dump/restore more complex data types' do
42
+ key = 'a_hash'
43
+ @src.mapped_hmset(key, foo: 'bar')
44
+ @dst.restore(key, 0, @src.dump(key))
45
+ expect(@dst.hgetall(key)).to eq('foo' => 'bar')
46
+ end
47
+ end
@@ -42,6 +42,15 @@ describe '#scan' do
42
42
  end
43
43
  end
44
44
 
45
+ context 'when cursor is greater than collection size' do
46
+ let(:collection) { Array.new(count) { |i| "mock:key#{i}" } }
47
+ let(:expected) { ['0', []] }
48
+
49
+ it 'returns a 0 cursor and empty collection' do
50
+ expect(subject.scan(20, count: count, match: match)).to eq(expected)
51
+ end
52
+ end
53
+
45
54
  context 'when giving a custom match filter' do
46
55
  let(:match) { 'mock:key*' }
47
56
  let(:collection) { %w[mock:key mock:key2 mock:otherkey] }
@@ -33,6 +33,14 @@ describe '#set(key, value)' do
33
33
  @redises.set(key, 1, xx: true).should == true
34
34
  end
35
35
 
36
+ it 'raises on unknown options' do
37
+ key = 'mock-redis-test'
38
+ @redises.del(key)
39
+ expect do
40
+ @redises.set(key, 1, logger: :something)
41
+ end.to raise_error(ArgumentError, 'unknown keyword: logger')
42
+ end
43
+
36
44
  context '[mock only]' do
37
45
  before(:all) do
38
46
  @mock = @redises.mock
@@ -53,9 +61,11 @@ describe '#set(key, value)' do
53
61
 
54
62
  it 'accepts PX milliseconds' do
55
63
  key = 'mock-redis-test'
56
- @mock.set(key, 1, px: 1000).should == 'OK'
64
+ @mock.set(key, 1, px: 500).should == 'OK'
57
65
  @mock.get(key).should_not be_nil
58
- Time.stub(:now).and_return(@now + 2)
66
+ Time.stub(:now).and_return(@now + 300 / 1000.to_f)
67
+ @mock.get(key).should_not be_nil
68
+ Time.stub(:now).and_return(@now + 600 / 1000.to_f)
59
69
  @mock.get(key).should be_nil
60
70
  end
61
71
  end
@@ -2,6 +2,7 @@ require 'spec_helper'
2
2
 
3
3
  describe '#setbit(key, offset)' do
4
4
  before do
5
+ Encoding.default_external = 'UTF-8'
5
6
  @key = 'mock-redis-test:setbit'
6
7
  @redises.set(@key, 'h') # ASCII 0x68
7
8
  end
@@ -19,4 +19,20 @@ describe '#setex(key, seconds, value)' do
19
19
  @redises.real.ttl(@key).should > 0
20
20
  @redises.mock.ttl(@key).should > 0
21
21
  end
22
+
23
+ context 'when expiration time is zero' do
24
+ it 'raises Redis::CommandError' do
25
+ expect do
26
+ @redises.setex(@key, 0, 'value')
27
+ end.to raise_error(Redis::CommandError, 'ERR invalid expire time in setex')
28
+ end
29
+ end
30
+
31
+ context 'when expiration time is negative' do
32
+ it 'raises Redis::CommandError' do
33
+ expect do
34
+ @redises.setex(@key, -2, 'value')
35
+ end.to raise_error(Redis::CommandError, 'ERR invalid expire time in setex')
36
+ end
37
+ end
22
38
  end
@@ -21,5 +21,20 @@ describe '#spop(key)' do
21
21
  @redises.spop(@key).should be_nil
22
22
  end
23
23
 
24
+ it 'returns an array if count is not nil' do
25
+ @redises.sadd(@key, 'value2')
26
+ @redises.spop(@key, 2).should == %w[value value2]
27
+ end
28
+
29
+ it 'returns only whats in the set' do
30
+ @redises.spop(@key, 2).should == ['value']
31
+ @redises.smembers(@key).should == []
32
+ end
33
+
34
+ it 'returns an empty array if count is not nil and the set it empty' do
35
+ @redises.spop(@key)
36
+ @redises.spop(@key, 100).should == []
37
+ end
38
+
24
39
  it_should_behave_like 'a set-only command'
25
40
  end
@@ -37,7 +37,7 @@ describe '#srandmember(key)' do
37
37
  @redises.send_without_checking(:srandmember, @key, 2).size.should == 2
38
38
  end
39
39
 
40
- it 'returns random members up to count from the set when count is negative even if count.abs is greater than the set size' do # rubocop:disable Metrics/LineLength
40
+ it 'returns random members up to count from the set when count is negative even if count.abs is greater than the set size' do # rubocop:disable Layout/LineLength
41
41
  @redises.send_without_checking(:srandmember, @key, -5).size.should == 5
42
42
  end
43
43
 
@@ -36,5 +36,10 @@ describe '#srem(key, member)' do
36
36
  @redises.get(@key).should be_nil
37
37
  end
38
38
 
39
+ it 'allow passing an array of integers as argument' do
40
+ @redises.sadd(@key, %w[1 2])
41
+ @redises.srem(@key, [1, 2]).should == 2
42
+ end
43
+
39
44
  it_should_behave_like 'a set-only command'
40
45
  end
@@ -1,12 +1,17 @@
1
1
  require 'spec_helper'
2
2
 
3
- describe '#watch(key)' do
3
+ describe '#watch(key [, key, ...)' do
4
+ before do
5
+ @key1 = 'mock-redis-test-watch1'
6
+ @key2 = 'mock-redis-test-watch2'
7
+ end
8
+
4
9
  it "returns 'OK'" do
5
- @redises.watch('mock-redis-test').should == 'OK'
10
+ @redises.watch(@key1, @key2).should == 'OK'
6
11
  end
7
12
 
8
13
  it 'EXECs its MULTI on successes' do
9
- @redises.watch 'foo' do
14
+ @redises.watch @key1, @key2 do
10
15
  @redises.multi do |multi|
11
16
  multi.set 'bar', 'baz'
12
17
  end
@@ -0,0 +1,104 @@
1
+ require 'spec_helper'
2
+
3
+ describe '#xadd("mystream", { f1: "v1", f2: "v2" }, id: "0-0", maxlen: 1000, approximate: true)' do
4
+ before :all do
5
+ sleep 1 - (Time.now.to_f % 1)
6
+ @key = 'mock-redis-test:xadd'
7
+ end
8
+
9
+ before :each do
10
+ @redises._gsub(/\d{3}-\d/, '...-.')
11
+ end
12
+
13
+ it 'returns an id based on the timestamp' do
14
+ t = Time.now.to_i
15
+ expect(@redises.xadd(@key, key: 'value')).to match(/#{t}\d{3}-0/)
16
+ end
17
+
18
+ it 'adds data with symbols' do
19
+ @redises.xadd(@key, symbol_key: :symbol_value)
20
+ expect(@redises.xrange(@key, '-', '+').last[1])
21
+ .to eq('symbol_key' => 'symbol_value')
22
+ end
23
+
24
+ it 'increments the sequence number with the same timestamp' do
25
+ Timecop.freeze do
26
+ @redises.xadd(@key, key: 'value')
27
+ expect(@redises.xadd(@key, key: 'value')).to match(/\d+-1/)
28
+ end
29
+ end
30
+
31
+ it 'sets the id if it is given' do
32
+ expect(@redises.xadd(@key, { key: 'value' }, id: '1234567891234-2'))
33
+ .to eq '1234567891234-2'
34
+ end
35
+
36
+ it 'accepts is as an integer' do
37
+ expect(@redises.xadd(@key, { key: 'value' }, id: 1_234_567_891_234))
38
+ .to eq '1234567891234-0'
39
+ end
40
+
41
+ it 'raises exception if id is less that stream top item' do
42
+ @redises.xadd(@key, { key: 'value' }, id: '1234567891234-0')
43
+ expect { @redises.xadd(@key, { key: 'value' }, id: '1234567891233-0') }
44
+ .to raise_error(
45
+ Redis::CommandError,
46
+ 'ERR The ID specified in XADD is equal or smaller than the target ' \
47
+ 'stream top item'
48
+ )
49
+ end
50
+
51
+ it 'raises exception if id of 0 is added to an empty stream' do
52
+ expect { @redises.xadd('mock-redis-test:unknown-stream', { key: 'value' }, id: '0') }
53
+ .to raise_error(
54
+ Redis::CommandError,
55
+ 'ERR The ID specified in XADD is equal or smaller than the target ' \
56
+ 'stream top item'
57
+ # TOOD: Redis version 6.0.4, w redis 4.2.1 generates the following error message:
58
+ # 'ERR The ID specified in XADD must be greater than 0-0'
59
+ )
60
+ end
61
+
62
+ it 'does not raise exception on empty stream with id of 0 and positive sequence number' do
63
+ expect { @redises.xadd('mock-redis-test:unknown-stream', { key: 'value' }, id: '0-1') }
64
+ .to_not raise_error
65
+ end
66
+
67
+ it 'caters for the current time being before the last time' do
68
+ t = (Time.now.to_f * 1000).to_i + 2000
69
+ @redises.xadd(@key, { key: 'value' }, id: "#{t}-0")
70
+ expect(@redises.xadd(@key, key: 'value')).to match(/#{t}-1/)
71
+ end
72
+
73
+ it 'appends a sequence number if it is missing' do
74
+ expect(@redises.xadd(@key, { key: 'value' }, id: '1234567891234'))
75
+ .to eq '1234567891234-0'
76
+ end
77
+
78
+ it 'raises an invalid stream id error' do
79
+ expect { @redises.xadd(@key, { key: 'value' }, id: 'X') }
80
+ .to raise_error(
81
+ Redis::CommandError,
82
+ 'ERR Invalid stream ID specified as stream command argument'
83
+ )
84
+ end
85
+
86
+ it 'caps the stream to 5 elements' do
87
+ @redises.xadd(@key, { key1: 'value1' }, id: '1234567891234-0')
88
+ @redises.xadd(@key, { key2: 'value2' }, id: '1234567891245-0')
89
+ @redises.xadd(@key, { key3: 'value3' }, id: '1234567891245-1')
90
+ @redises.xadd(@key, { key4: 'value4' }, id: '1234567891278-0')
91
+ @redises.xadd(@key, { key5: 'value5' }, id: '1234567891278-1')
92
+ @redises.xadd(@key, { key6: 'value6' }, id: '1234567891299-0')
93
+ @redises.xadd(@key, { key7: 'value7' }, id: '1234567891300-0', maxlen: 5)
94
+ expect(@redises.xrange(@key, '-', '+')).to eq(
95
+ [
96
+ ['1234567891245-1', { 'key3' => 'value3' }],
97
+ ['1234567891278-0', { 'key4' => 'value4' }],
98
+ ['1234567891278-1', { 'key5' => 'value5' }],
99
+ ['1234567891299-0', { 'key6' => 'value6' }],
100
+ ['1234567891300-0', { 'key7' => 'value7' }]
101
+ ]
102
+ )
103
+ end
104
+ end
@@ -0,0 +1,20 @@
1
+ require 'spec_helper'
2
+
3
+ describe '#xlen(key)' do
4
+ before :all do
5
+ sleep 1 - (Time.now.to_f % 1)
6
+ @key = 'mock-redis-test:xlen'
7
+ end
8
+
9
+ before :each do
10
+ @redises._gsub(/\d{3}-\d/, '...-.')
11
+ end
12
+
13
+ it 'returns the number of items in the stream' do
14
+ expect(@redises.xlen(@key)).to eq 0
15
+ @redises.xadd(@key, key: 'value')
16
+ expect(@redises.xlen(@key)).to eq 1
17
+ 3.times { @redises.xadd(@key, key: 'value') }
18
+ expect(@redises.xlen(@key)).to eq 4
19
+ end
20
+ end