mock_redis 0.22.0 → 0.23.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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: d9dd8cc0a4f19edccfb6c7b3f1a82724f0da757ad7139f4a576eed1d0b815b8d
4
- data.tar.gz: 980f4326447c75afb034b6292baab71bb723ca05689b04aa7ed4933bd8bbf356
3
+ metadata.gz: dd9fd0d5b4ccad106eee03697d49082baa7e34065ff76f67b7e18da9991847a9
4
+ data.tar.gz: 11f940cdee40f2646c3e6087faac7ac1c70e0aae5eaff7ddfc8839f0fb50e1b6
5
5
  SHA512:
6
- metadata.gz: 53a8419ef9f62a4d276993714241f22f9da4087df9ce69158821ea42c735e4fc022f4ab9431bbef268eb562130cc139308fd46ad9eeef8941ef95d9b56afc083
7
- data.tar.gz: 266de40ac11c22fa449d709380cec9461a93cd18c6950d31aee538b295c613ab2a0ee2fb828420ff02ea2e2351e92e2d565a9bad630abfd21abf8b8eab7c144d
6
+ metadata.gz: 347930a11a59575cd3a4bbc48cb689d46d25b27c6cfbdc28c28b6bfdfa1d833c317dd5ce7566ba016aae7b13b909629d078486bd95dee68f4e7e6b5cfc7e1e44
7
+ data.tar.gz: 689b8dc84b58c38201bf3220641f467cd56b3ef2d6c4efc12829ec08c3a84f6dbc1e2c8207fdb18808c10bdb71f878cc571d21df2548a9d240881fff8b9ddd68
@@ -1,5 +1,14 @@
1
1
  # MockRedis Changelog
2
2
 
3
+ ### 0.23.0
4
+
5
+ * Raise error when `setex` called with negative timeout ([#174](https://github.com/sds/mock_redis/pull/174))
6
+ * Add support for `dump`/`restore` between MockRedis instances ([#176](https://github.com/sds/mock_redis/pull/176))
7
+ * Fix warnings for ZSET methods on Ruby 2.7 ([#177](https://github.com/sds/mock_redis/pull/177))
8
+ * Add support for returning time in pipelines ([#179](https://github.com/sds/mock_redis/pull/179))
9
+ * Fix SET methods to correct set milliseconds with `px` ([#180](https://github.com/sds/mock_redis/pull/180))
10
+ * Add support for unsorted sets within `zinterstore`/`zunionstore`([#182](https://github.com/sds/mock_redis/pull/182))
11
+
3
12
  ### 0.22.0
4
13
 
5
14
  * Gracefully handle cursors larger than the collection size in scan commands ([#171](https://github.com/sds/mock_redis/pull/171))
@@ -64,13 +64,6 @@ class MockRedis
64
64
  options[:db]
65
65
  end
66
66
 
67
- def now
68
- current_time = options[:time_class].now
69
- miliseconds = (current_time.to_r - current_time.to_i) * 1_000
70
- [current_time.to_i, miliseconds.to_i]
71
- end
72
- alias time now
73
-
74
67
  def time_at(timestamp)
75
68
  options[:time_class].at(timestamp)
76
69
  end
@@ -124,6 +124,22 @@ class MockRedis
124
124
  'OK'
125
125
  end
126
126
 
127
+ def dump(key)
128
+ value = data[key]
129
+ value ? Marshal.dump(value) : nil
130
+ end
131
+
132
+ def restore(key, ttl, value, replace: false)
133
+ if !replace && exists(key)
134
+ raise Redis::CommandError, 'BUSYKEY Target key name already exists.'
135
+ end
136
+ data[key] = Marshal.load(value) # rubocop:disable Security/MarshalLoad
137
+ if ttl > 0
138
+ pexpire(key, ttl)
139
+ end
140
+ 'OK'
141
+ end
142
+
127
143
  def keys(format = '*')
128
144
  data.keys.grep(redis_pattern_to_ruby_regex(format))
129
145
  end
@@ -143,7 +159,7 @@ class MockRedis
143
159
  end
144
160
 
145
161
  def lastsave
146
- @base.now.first
162
+ now.first
147
163
  end
148
164
 
149
165
  def persist(key)
@@ -224,6 +240,13 @@ class MockRedis
224
240
  end
225
241
  end
226
242
 
243
+ def now
244
+ current_time = @base.options[:time_class].now
245
+ miliseconds = (current_time.to_r - current_time.to_i) * 1_000
246
+ [current_time.to_i, miliseconds.to_i]
247
+ end
248
+ alias time now
249
+
227
250
  def type(key)
228
251
  if !exists(key)
229
252
  'none'
@@ -328,7 +351,7 @@ class MockRedis
328
351
  # This method isn't private, but it also isn't a Redis command, so
329
352
  # it doesn't belong up above with all the Redis commands.
330
353
  def expire_keys
331
- now, miliseconds = @base.now
354
+ now, miliseconds = self.now
332
355
  now_ms = now * 1_000 + miliseconds
333
356
 
334
357
  to_delete = expire_times.take_while do |(time, _key)|
@@ -233,7 +233,7 @@ class MockRedis
233
233
  if duration == 0
234
234
  raise Redis::CommandError, 'ERR invalid expire time in set'
235
235
  end
236
- expire(key, duration / 1000.0)
236
+ pexpire(key, duration)
237
237
  end
238
238
 
239
239
  return_true ? true : 'OK'
@@ -313,9 +313,13 @@ class MockRedis
313
313
  end
314
314
 
315
315
  def setex(key, seconds, value)
316
- set(key, value)
317
- expire(key, seconds)
318
- 'OK'
316
+ if seconds <= 0
317
+ raise Redis::CommandError, 'ERR invalid expire time in setex'
318
+ else
319
+ set(key, value)
320
+ expire(key, seconds)
321
+ 'OK'
322
+ end
319
323
  end
320
324
 
321
325
  def setnx(key, value)
@@ -2,5 +2,5 @@
2
2
 
3
3
  # Defines the gem version.
4
4
  class MockRedis
5
- VERSION = '0.22.0'
5
+ VERSION = '0.23.0'
6
6
  end
@@ -26,7 +26,7 @@ class MockRedis
26
26
  end
27
27
 
28
28
  def zadd_one_member(key, score, member, zadd_options = {})
29
- assert_scorey(score) unless score =~ /(\+|\-)inf/
29
+ assert_scorey(score) unless score.to_s =~ /(\+|\-)inf/
30
30
 
31
31
  with_zset_at(key) do |zset|
32
32
  if zadd_options[:incr]
@@ -282,7 +282,7 @@ class MockRedis
282
282
  raise Redis::CommandError, 'ERR syntax error'
283
283
  end
284
284
 
285
- with_zsets_at(*keys) do |*zsets|
285
+ with_zsets_at(*keys, coercible: true) do |*zsets|
286
286
  zsets.zip(weights).map do |(zset, weight)|
287
287
  zset.reduce(Zset.new) do |acc, (score, member)|
288
288
  acc.add(score * weight, member)
@@ -293,16 +293,30 @@ class MockRedis
293
293
  end
294
294
  end
295
295
 
296
- def with_zset_at(key, &blk)
297
- with_thing_at(key, :assert_zsety, proc { Zset.new }, &blk)
296
+ def coerce_to_zset(set)
297
+ zset = Zset.new
298
+ set.each do |member|
299
+ zset.add(1.0, member)
300
+ end
301
+ zset
298
302
  end
299
303
 
300
- def with_zsets_at(*keys, &blk)
304
+ def with_zset_at(key, coercible: false, &blk)
305
+ if coercible
306
+ with_thing_at(key, :assert_coercible_zsety, proc { Zset.new }) do |value|
307
+ blk.call value.is_a?(Set) ? coerce_to_zset(value) : value
308
+ end
309
+ else
310
+ with_thing_at(key, :assert_zsety, proc { Zset.new }, &blk)
311
+ end
312
+ end
313
+
314
+ def with_zsets_at(*keys, coercible: false, &blk)
301
315
  if keys.length == 1
302
- with_zset_at(keys.first, &blk)
316
+ with_zset_at(keys.first, coercible: coercible, &blk)
303
317
  else
304
- with_zset_at(keys.first) do |set|
305
- with_zsets_at(*(keys[1..-1])) do |*sets|
318
+ with_zset_at(keys.first, coercible: coercible) do |set|
319
+ with_zsets_at(*(keys[1..-1]), coercible: coercible) do |*sets|
306
320
  yield(*([set] + sets))
307
321
  end
308
322
  end
@@ -313,6 +327,10 @@ class MockRedis
313
327
  data[key].nil? || data[key].is_a?(Zset)
314
328
  end
315
329
 
330
+ def coercible_zsety?(key)
331
+ zsety?(key) || data[key].is_a?(Set)
332
+ end
333
+
316
334
  def assert_zsety(key)
317
335
  unless zsety?(key)
318
336
  raise Redis::CommandError,
@@ -320,13 +338,20 @@ class MockRedis
320
338
  end
321
339
  end
322
340
 
341
+ def assert_coercible_zsety(key)
342
+ unless coercible_zsety?(key)
343
+ raise Redis::CommandError,
344
+ 'WRONGTYPE Operation against a key holding the wrong kind of value'
345
+ end
346
+ end
347
+
323
348
  def looks_like_float?(x)
324
349
  # ugh, exceptions for flow control.
325
350
  !!Float(x) rescue false
326
351
  end
327
352
 
328
353
  def assert_scorey(value, message = 'ERR value is not a valid float')
329
- return if value =~ /\(?(\-|\+)inf/
354
+ return if value.to_s =~ /\(?(\-|\+)inf/
330
355
 
331
356
  value = $1 if value.to_s =~ /\((.*)/
332
357
  unless looks_like_float?(value)
@@ -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
@@ -66,6 +66,26 @@ describe '#pipelined' do
66
66
  end
67
67
  end
68
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
+
69
89
  context 'with nested pipelines' do
70
90
  let(:key1) { 'hello' }
71
91
  let(:key2) { 'world' }
@@ -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
@@ -59,9 +59,11 @@ describe '#set(key, value)' do
59
59
 
60
60
  it 'accepts PX milliseconds' do
61
61
  key = 'mock-redis-test'
62
- @mock.set(key, 1, px: 1000).should == 'OK'
62
+ @mock.set(key, 1, px: 500).should == 'OK'
63
63
  @mock.get(key).should_not be_nil
64
- Time.stub(:now).and_return(@now + 2)
64
+ Time.stub(:now).and_return(@now + 300 / 1000.to_f)
65
+ @mock.get(key).should_not be_nil
66
+ Time.stub(:now).and_return(@now + 600 / 1000.to_f)
65
67
  @mock.get(key).should be_nil
66
68
  end
67
69
  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
@@ -42,6 +42,40 @@ describe '#zinterstore(destination, keys, [:weights => [w,w,], [:aggregate => :s
42
42
  end.should raise_error(Redis::CommandError)
43
43
  end
44
44
 
45
+ context 'when used with a set' do
46
+ before do
47
+ @primes_text = 'mock-redis-test:zinterstore:primes-text'
48
+
49
+ @redises.sadd(@primes_text, 'two')
50
+ @redises.sadd(@primes_text, 'three')
51
+ @redises.sadd(@primes_text, 'five')
52
+ @redises.sadd(@primes_text, 'seven')
53
+ end
54
+
55
+ it 'returns the number of elements in the new set' do
56
+ @redises.zinterstore(@dest, [@odds, @primes_text]).should == 3
57
+ end
58
+
59
+ it 'sums the scores, substituting 1.0 for set values' do
60
+ @redises.zinterstore(@dest, [@odds, @primes_text])
61
+ @redises.zrange(@dest, 0, -1, :with_scores => true).should ==
62
+ [['three', 4.0], ['five', 6.0], ['seven', 8.0]]
63
+ end
64
+ end
65
+
66
+ context 'when used with a non-coercible structure' do
67
+ before do
68
+ @non_set = 'mock-redis-test:zinterstore:non-set'
69
+
70
+ @redises.set(@non_set, 'one')
71
+ end
72
+ it 'raises an error for wrong value type' do
73
+ lambda do
74
+ @redises.zinterstore(@dest, [@odds, @non_set])
75
+ end.should raise_error(Redis::CommandError)
76
+ end
77
+ end
78
+
45
79
  context 'the :weights argument' do
46
80
  it 'multiplies the scores by the weights while aggregating' do
47
81
  @redises.zinterstore(@dest, [@odds, @primes], :weights => [2, 3])
@@ -41,6 +41,39 @@ describe '#zunionstore(destination, keys, [:weights => [w,w,], [:aggregate => :s
41
41
  end.should raise_error(Redis::CommandError)
42
42
  end
43
43
 
44
+ context 'when used with a set' do
45
+ before do
46
+ @set4 = 'mock-redis-test:zunionstore4'
47
+
48
+ @redises.sadd(@set4, 'two')
49
+ @redises.sadd(@set4, 'three')
50
+ @redises.sadd(@set4, 'four')
51
+ end
52
+
53
+ it 'returns the number of elements in the new set' do
54
+ @redises.zunionstore(@dest, [@set3, @set4]).should == 4
55
+ end
56
+
57
+ it 'sums the scores, substituting 1.0 for set values' do
58
+ @redises.zunionstore(@dest, [@set3, @set4])
59
+ @redises.zrange(@dest, 0, -1, :with_scores => true).should ==
60
+ [['four', 1.0], ['one', 1.0], ['two', 3.0], ['three', 4.0]]
61
+ end
62
+ end
63
+
64
+ context 'when used with a non-coercible structure' do
65
+ before do
66
+ @non_set = 'mock-redis-test:zunionstore4'
67
+
68
+ @redises.set(@non_set, 'one')
69
+ end
70
+ it 'raises an error for wrong value type' do
71
+ lambda do
72
+ @redises.zunionstore(@dest, [@set1, @non_set])
73
+ end.should raise_error(Redis::CommandError)
74
+ end
75
+ end
76
+
44
77
  context 'the :weights argument' do
45
78
  it 'multiplies the scores by the weights while aggregating' do
46
79
  @redises.zunionstore(@dest, [@set1, @set2, @set3], :weights => [2, 3, 5])
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: mock_redis
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.22.0
4
+ version: 0.23.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Shane da Silva
@@ -9,7 +9,7 @@ authors:
9
9
  autorequire:
10
10
  bindir: bin
11
11
  cert_chain: []
12
- date: 2019-10-10 00:00:00.000000000 Z
12
+ date: 2020-04-22 00:00:00.000000000 Z
13
13
  dependencies:
14
14
  - !ruby/object:Gem::Dependency
15
15
  name: redis
@@ -131,6 +131,7 @@ files:
131
131
  - spec/commands/decrby_spec.rb
132
132
  - spec/commands/del_spec.rb
133
133
  - spec/commands/disconnect_spec.rb
134
+ - spec/commands/dump_spec.rb
134
135
  - spec/commands/echo_spec.rb
135
136
  - spec/commands/eval_spec.rb
136
137
  - spec/commands/evalsha_spec.rb
@@ -198,6 +199,7 @@ files:
198
199
  - spec/commands/randomkey_spec.rb
199
200
  - spec/commands/rename_spec.rb
200
201
  - spec/commands/renamenx_spec.rb
202
+ - spec/commands/restore_spec.rb
201
203
  - spec/commands/rpop_spec.rb
202
204
  - spec/commands/rpoplpush_spec.rb
203
205
  - spec/commands/rpush_spec.rb
@@ -291,7 +293,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
291
293
  - !ruby/object:Gem::Version
292
294
  version: '0'
293
295
  requirements: []
294
- rubygems_version: 3.0.3
296
+ rubygems_version: 3.1.1
295
297
  signing_key:
296
298
  specification_version: 4
297
299
  summary: Redis mock that just lives in memory; useful for testing.
@@ -313,6 +315,7 @@ test_files:
313
315
  - spec/commands/decrby_spec.rb
314
316
  - spec/commands/del_spec.rb
315
317
  - spec/commands/disconnect_spec.rb
318
+ - spec/commands/dump_spec.rb
316
319
  - spec/commands/echo_spec.rb
317
320
  - spec/commands/eval_spec.rb
318
321
  - spec/commands/evalsha_spec.rb
@@ -380,6 +383,7 @@ test_files:
380
383
  - spec/commands/randomkey_spec.rb
381
384
  - spec/commands/rename_spec.rb
382
385
  - spec/commands/renamenx_spec.rb
386
+ - spec/commands/restore_spec.rb
383
387
  - spec/commands/rpop_spec.rb
384
388
  - spec/commands/rpoplpush_spec.rb
385
389
  - spec/commands/rpush_spec.rb