mock_redis 0.27.1 → 0.29.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: cbd4308bd9413896742121468e39d64096e55b7760453a4220696aaa0554e38e
4
- data.tar.gz: 0f8e0c66ac4533044ad91ee89a383bba1297cc186fdf514c66923a6188024b0c
3
+ metadata.gz: a2607bfdcb34be30d5f98395241f578ca9980f73b1d2f1ac37fa3a1baa736ba1
4
+ data.tar.gz: 7b4c15ece6a897bfa29c86512f9364b73b03eed78dc4764e4c542a056ec5c472
5
5
  SHA512:
6
- metadata.gz: 6fde8339234a0e1c8022cd63208e8e279cc834ff4280c6690d85a516259f7c29ac36fd3882a433a94af4e4e0bb584a7c332c823cc54db59556b17e7bf42d2f97
7
- data.tar.gz: 4f7d508739c4eef78afa8e155d2ec4166f5b5b644f37f846a59f9b555c34bb948d545fca55c3d2c7dae2781fe90c24c7fabb804b3406f97030e8ee4e68eff9f4
6
+ metadata.gz: 28d6b6302b703e48245d3bb0e2511d446c5a58bd3620eabbfa806f1c58835b48ac4f2082961a8e1110fca1b1246cc1b9c06cb590d8e8eb53facb2716e98fec9f
7
+ data.tar.gz: 2d749de988854e1bb5ff64e38f596d1f0efa508e2e78ab90980f86d217e791be617851bc0aaa8593a0497c233258256544e95fe86bc50ff784140a6a63b83e9b
data/CHANGELOG.md CHANGED
@@ -1,5 +1,26 @@
1
1
  # MockRedis Changelog
2
2
 
3
+ ### 0.29.0
4
+
5
+ * Add support for `logger` option ([#211](https://github.com/sds/mock_redis/pull/211))
6
+ * Fix `zadd` to not perform conditional type conversion ([#214](https://github.com/sds/mock_redis/pull/214))
7
+ * Fix `hdel` to raise error when called with empty array ([#215](https://github.com/sds/mock_redis/pull/215))
8
+
9
+ ### 0.28.0
10
+
11
+ * Fix `hmset` exception ([#206](https://github.com/sds/mock_redis/pull/206))
12
+ * Fix `hmset` to accept hashes in addition to key/value pairs ([#208](https://github.com/sds/mock_redis/pull/208))
13
+ * Fix stream ID regex to support `(` ([#209](https://github.com/sds/mock_redis/pull/209))
14
+ * Allow `mget` to accept a block ([#210](https://github.com/sds/mock_redis/pull/210))
15
+
16
+ ### 0.27.3
17
+
18
+ * Ensure `ruby2_keywords` dependency is `require`d at runtime
19
+
20
+ ### 0.27.2
21
+
22
+ * Switch `ruby2_keywords` gem from development dependency to runtime dependency
23
+
3
24
  ### 0.27.1
4
25
 
5
26
  * Fix missing `ruby2_keywords` gem
data/README.md CHANGED
@@ -12,7 +12,7 @@ for use in tests.
12
12
 
13
13
  * Ruby 2.4+
14
14
 
15
- The current implementation is tested against **Redis 5**. Older versions may work, but can also return different results or not support some commands.
15
+ The current implementation is tested against **Redis 6**. Older versions may work, but can also return different results or not support some commands.
16
16
 
17
17
  ## Getting Started
18
18
 
data/lib/mock_redis.rb CHANGED
@@ -1,4 +1,5 @@
1
1
  require 'set'
2
+ require 'ruby2_keywords'
2
3
 
3
4
  require 'mock_redis/assertions'
4
5
  require 'mock_redis/database'
@@ -21,6 +22,7 @@ class MockRedis
21
22
  :path => nil,
22
23
  :timeout => 5.0,
23
24
  :password => nil,
25
+ :logger => nil,
24
26
  :db => 0,
25
27
  :time_class => Time,
26
28
  }.freeze
@@ -64,6 +66,10 @@ class MockRedis
64
66
  options[:db]
65
67
  end
66
68
 
69
+ def logger
70
+ options[:logger]
71
+ end
72
+
67
73
  def time_at(timestamp)
68
74
  options[:time_class].at(timestamp)
69
75
  end
@@ -85,7 +91,9 @@ class MockRedis
85
91
  end
86
92
 
87
93
  ruby2_keywords def method_missing(method, *args, &block)
88
- @db.send(method, *args, &block)
94
+ logging([[method, *args]]) do
95
+ @db.send(method, *args, &block)
96
+ end
89
97
  end
90
98
 
91
99
  def initialize_copy(source)
@@ -138,4 +146,28 @@ class MockRedis
138
146
 
139
147
  options
140
148
  end
149
+
150
+ def logging(commands)
151
+ return yield unless logger&.debug?
152
+
153
+ begin
154
+ commands.each do |name, *args|
155
+ logged_args = args.map do |a|
156
+ if a.respond_to?(:inspect) then a.inspect
157
+ elsif a.respond_to?(:to_s) then a.to_s
158
+ else
159
+ # handle poorly-behaved descendants of BasicObject
160
+ klass = a.instance_exec { (class << self; self end).superclass }
161
+ "\#<#{klass}:#{a.__id__}>"
162
+ end
163
+ end
164
+ logger.debug("[MockRedis] command=#{name.to_s.upcase} args=#{logged_args.join(' ')}")
165
+ end
166
+
167
+ t1 = Time.now
168
+ yield
169
+ ensure
170
+ logger.debug("[MockRedis] call_time=%0.2f ms" % ((Time.now - t1) * 1000)) if t1
171
+ end
172
+ end
141
173
  end
@@ -10,6 +10,11 @@ class MockRedis
10
10
  with_hash_at(key) do |hash|
11
11
  orig_size = hash.size
12
12
  fields = Array(fields).flatten.map(&:to_s)
13
+
14
+ if fields.empty?
15
+ raise Redis::CommandError, "ERR wrong number of arguments for 'hdel' command"
16
+ end
17
+
13
18
  hash.delete_if { |k, _v| fields.include?(k) }
14
19
  orig_size - hash.size
15
20
  end
@@ -94,7 +99,7 @@ class MockRedis
94
99
  assert_has_args(kvpairs, 'hmset')
95
100
 
96
101
  if kvpairs.length.odd?
97
- raise Redis::CommandError, err_msg || 'ERR wrong number of arguments for HMSET'
102
+ raise Redis::CommandError, err_msg || 'ERR wrong number of arguments for \'hmset\' command'
98
103
  end
99
104
 
100
105
  kvpairs.each_slice(2) do |(k, v)|
@@ -131,6 +136,10 @@ class MockRedis
131
136
  def hset(key, *args)
132
137
  added = 0
133
138
  with_hash_at(key) do |hash|
139
+ if args.length == 1 && args[0].is_a?(Hash)
140
+ args = args[0].to_a.flatten
141
+ end
142
+
134
143
  args.each_slice(2) do |field, value|
135
144
  added += 1 unless hash.key?(field.to_s)
136
145
  hash[field.to_s] = value.to_s
@@ -49,9 +49,15 @@ class MockRedis
49
49
  opts = options opts_in, ['count']
50
50
  start_id = MockRedis::Stream::Id.new(start)
51
51
  finish_id = MockRedis::Stream::Id.new(finish, sequence: Float::INFINITY)
52
- items = members
53
- .select { |m| (start_id <= m[0]) && (finish_id >= m[0]) }
54
- .map { |m| [m[0].to_s, m[1]] }
52
+ if start_id.exclusive
53
+ items = members
54
+ .select { |m| (start_id < m[0]) && (finish_id >= m[0]) }
55
+ .map { |m| [m[0].to_s, m[1]] }
56
+ else
57
+ items = members
58
+ .select { |m| (start_id <= m[0]) && (finish_id >= m[0]) }
59
+ .map { |m| [m[0].to_s, m[1]] }
60
+ end
55
61
  items.reverse! if reversed
56
62
  return items.first(opts['count'].to_i) if opts.key?('count')
57
63
  items
@@ -3,9 +3,10 @@ class MockRedis
3
3
  class Id
4
4
  include Comparable
5
5
 
6
- attr_accessor :timestamp, :sequence
6
+ attr_accessor :timestamp, :sequence, :exclusive
7
7
 
8
8
  def initialize(id, min: nil, sequence: 0)
9
+ @exclusive = false
9
10
  case id
10
11
  when '*'
11
12
  @timestamp = (Time.now.to_f * 1000).to_i
@@ -20,8 +21,12 @@ class MockRedis
20
21
  @timestamp = @sequence = Float::INFINITY
21
22
  else
22
23
  if id.is_a? String
23
- (_, @timestamp, @sequence) = id.match(/^(\d+)-?(\d+)?$/)
24
- .to_a
24
+ # See https://redis.io/topics/streams-intro
25
+ # Ids are a unix timestamp in milliseconds followed by an
26
+ # optional dash sequence number, e.g. -0. They can also optionally
27
+ # be prefixed with '(' to change the XRANGE to exclusive.
28
+ (_, @timestamp, @sequence) = id.match(/^\(?(\d+)-?(\d+)?$/).to_a
29
+ @exclusive = true if id[0] == '('
25
30
  if @timestamp.nil?
26
31
  raise Redis::CommandError,
27
32
  'ERR Invalid stream ID specified as stream command argument'
@@ -154,14 +154,16 @@ class MockRedis
154
154
  new_value
155
155
  end
156
156
 
157
- def mget(*keys)
157
+ def mget(*keys, &blk)
158
158
  keys.flatten!
159
159
 
160
160
  assert_has_args(keys, 'mget')
161
161
 
162
- keys.map do |key|
162
+ data = keys.map do |key|
163
163
  get(key) if stringy?(key)
164
164
  end
165
+
166
+ blk ? blk.call(data) : data
165
167
  end
166
168
 
167
169
  def mapped_mget(*keys)
@@ -2,5 +2,5 @@
2
2
 
3
3
  # Defines the gem version.
4
4
  class MockRedis
5
- VERSION = '0.27.1'
5
+ VERSION = '0.29.0'
6
6
  end
@@ -23,12 +23,7 @@ class MockRedis
23
23
 
24
24
  def add(score, member)
25
25
  members.add(member)
26
- scores[member] =
27
- if score.to_f.to_i == score.to_f
28
- score.to_f.to_i
29
- else
30
- score.to_f
31
- end
26
+ scores[member] = score.to_f
32
27
  self
33
28
  end
34
29
 
data/mock_redis.gemspec CHANGED
@@ -23,9 +23,10 @@ Gem::Specification.new do |s|
23
23
 
24
24
  s.required_ruby_version = '>= 2.4'
25
25
 
26
+ s.add_runtime_dependency 'ruby2_keywords'
27
+
26
28
  s.add_development_dependency 'redis', '~> 4.2.0'
27
29
  s.add_development_dependency 'rspec', '~> 3.0'
28
30
  s.add_development_dependency 'rspec-its', '~> 1.0'
29
- s.add_development_dependency 'ruby2_keywords'
30
31
  s.add_development_dependency 'timecop', '~> 0.9.1'
31
32
  end
@@ -66,5 +66,12 @@ describe '#hdel(key, field)' do
66
66
  @redises.get(@key).should be_nil
67
67
  end
68
68
 
69
+ it 'raises error if an empty array is passed' do
70
+ expect { @redises.hdel(@key, []) }.to raise_error(
71
+ Redis::CommandError,
72
+ "ERR wrong number of arguments for 'hdel' command"
73
+ )
74
+ end
75
+
69
76
  it_should_behave_like 'a hash-only command'
70
77
  end
@@ -34,5 +34,9 @@ describe '#hset(key, field)' do
34
34
  @redises.hget(@key, '1').should == 'one'
35
35
  end
36
36
 
37
+ it 'stores fields sent in a hash' do
38
+ @redises.hset(@key, {'k1' => 'v1', 'k2' => 'v2'}).should == 2
39
+ end
40
+
37
41
  it_should_behave_like 'a hash-only command'
38
42
  end
@@ -56,4 +56,10 @@ describe '#mget(key [, key, ...])' do
56
56
  end.should raise_error(Redis::CommandError)
57
57
  end
58
58
  end
59
+
60
+ context 'emulate block' do
61
+ it 'returns an array of values' do
62
+ @redises.mget(@key1, @key2) { |values| values.map(&:to_i) }.should == [1, 2]
63
+ end
64
+ end
59
65
  end
@@ -14,20 +14,20 @@ describe '#xadd("mystream", { f1: "v1", f2: "v2" }, id: "0-0", maxlen: 1000, app
14
14
 
15
15
  it 'returns an id based on the timestamp' do
16
16
  t = Time.now.to_i
17
- id = @redises.xadd(@key, key: 'value')
18
- expect(@redises.xadd(@key, key: 'value')).to match(/#{t}\d{3}-0/)
17
+ id = @redises.xadd(@key, { key: 'value' })
18
+ expect(@redises.xadd(@key, { key: 'value' })).to match(/#{t}\d{3}-0/)
19
19
  end
20
20
 
21
21
  it 'adds data with symbols' do
22
- @redises.xadd(@key, symbol_key: :symbol_value)
22
+ @redises.xadd(@key, { symbol_key: :symbol_value })
23
23
  expect(@redises.xrange(@key, '-', '+').last[1])
24
24
  .to eq('symbol_key' => 'symbol_value')
25
25
  end
26
26
 
27
27
  it 'increments the sequence number with the same timestamp' do
28
28
  Timecop.freeze do
29
- @redises.xadd(@key, key: 'value')
30
- expect(@redises.xadd(@key, key: 'value')).to match(/\d+-1/)
29
+ @redises.xadd(@key, { key: 'value' })
30
+ expect(@redises.xadd(@key, { key: 'value' })).to match(/\d+-1/)
31
31
  end
32
32
  end
33
33
 
@@ -67,7 +67,7 @@ describe '#xadd("mystream", { f1: "v1", f2: "v2" }, id: "0-0", maxlen: 1000, app
67
67
  it 'caters for the current time being before the last time' do
68
68
  t = (Time.now.to_f * 1000).to_i + 2000
69
69
  @redises.xadd(@key, { key: 'value' }, id: "#{t}-0")
70
- expect(@redises.xadd(@key, key: 'value')).to match(/#{t}-1/)
70
+ expect(@redises.xadd(@key, { key: 'value' })).to match(/#{t}-1/)
71
71
  end
72
72
 
73
73
  it 'appends a sequence number if it is missing' do
@@ -14,9 +14,9 @@ describe '#xlen(key)' do
14
14
 
15
15
  it 'returns the number of items in the stream' do
16
16
  expect(@redises.xlen(@key)).to eq 0
17
- @redises.xadd(@key, key: 'value')
17
+ @redises.xadd(@key, { key: 'value' })
18
18
  expect(@redises.xlen(@key)).to eq 1
19
- 3.times { @redises.xadd(@key, key: 'value') }
19
+ 3.times { @redises.xadd(@key, { key: 'value' }) }
20
20
  expect(@redises.xlen(@key)).to eq 4
21
21
  end
22
22
  end
@@ -79,13 +79,23 @@ describe '#xrange("mystream", first: "0-1", last: "0-3", count: 10)' do
79
79
  )
80
80
  end
81
81
 
82
- it 'returns entries with both a lower and an upper limit' do
83
- expect(@redises.xrange(@key, '1234567891239-0', '1234567891285-0')).to eq(
82
+ it 'returns entries with both a lower and an upper limit inclusive' do
83
+ expect(@redises.xrange(@key, '1234567891245-0', '1234567891278-0')).to eq(
84
84
  [
85
85
  ['1234567891245-0', { 'key2' => 'value2' }],
86
86
  ['1234567891245-1', { 'key3' => 'value3' }],
87
+ ['1234567891278-0', { 'key4' => 'value4' }]
88
+ ]
89
+ )
90
+ end
91
+
92
+ it 'returns entries with both a lower and an upper limit exclusive' do
93
+ expect(@redises.xrange(@key, '(1234567891245-0', '1234567891285-1')).to eq(
94
+ [
95
+ # We no longer get '1234567891245-0'
96
+ ['1234567891245-1', { 'key3' => 'value3' }],
87
97
  ['1234567891278-0', { 'key4' => 'value4' }],
88
- ['1234567891278-1', { 'key5' => 'value5' }]
98
+ ['1234567891278-1', { 'key5' => 'value5' }] # Note sequence -1
89
99
  ]
90
100
  )
91
101
  end
@@ -23,6 +23,12 @@ describe '#zadd(key, score, member)' do
23
23
  @redises.zrange(@key, 0, -1).should == [member.to_s]
24
24
  end
25
25
 
26
+ it 'allows scores to be set to Float::INFINITY' do
27
+ member = '1'
28
+ @redises.zadd(@key, Float::INFINITY, member)
29
+ @redises.zrange(@key, 0, -1).should == [member]
30
+ end
31
+
26
32
  it 'updates the score' do
27
33
  @redises.zadd(@key, 1, 'foo')
28
34
  @redises.zadd(@key, 2, 'foo')
@@ -81,4 +81,13 @@ describe MockRedis do
81
81
  end
82
82
  end
83
83
  end
84
+
85
+ describe 'supplying a logger' do
86
+ it 'logs redis commands' do
87
+ logger = double('Logger', debug?: true, debug: nil)
88
+ mock_redis = MockRedis.new(logger: logger)
89
+ expect(logger).to receive(:debug).with(/command=HMGET args="hash" "key1" "key2"/)
90
+ mock_redis.hmget("hash", "key1", "key2")
91
+ end
92
+ end
84
93
  end
data/spec/spec_helper.rb CHANGED
@@ -11,7 +11,6 @@ end
11
11
  require 'rspec/its'
12
12
  require 'redis'
13
13
  $LOAD_PATH.unshift(File.expand_path(File.join(__FILE__, '..', '..', 'lib')))
14
- require 'ruby2_keywords'
15
14
  require 'mock_redis'
16
15
  require 'timecop'
17
16
 
metadata CHANGED
@@ -1,16 +1,30 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: mock_redis
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.27.1
4
+ version: 0.29.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Shane da Silva
8
8
  - Samuel Merritt
9
- autorequire:
9
+ autorequire:
10
10
  bindir: bin
11
11
  cert_chain: []
12
- date: 2021-01-07 00:00:00.000000000 Z
12
+ date: 2021-08-12 00:00:00.000000000 Z
13
13
  dependencies:
14
+ - !ruby/object:Gem::Dependency
15
+ name: ruby2_keywords
16
+ requirement: !ruby/object:Gem::Requirement
17
+ requirements:
18
+ - - ">="
19
+ - !ruby/object:Gem::Version
20
+ version: '0'
21
+ type: :runtime
22
+ prerelease: false
23
+ version_requirements: !ruby/object:Gem::Requirement
24
+ requirements:
25
+ - - ">="
26
+ - !ruby/object:Gem::Version
27
+ version: '0'
14
28
  - !ruby/object:Gem::Dependency
15
29
  name: redis
16
30
  requirement: !ruby/object:Gem::Requirement
@@ -53,20 +67,6 @@ dependencies:
53
67
  - - "~>"
54
68
  - !ruby/object:Gem::Version
55
69
  version: '1.0'
56
- - !ruby/object:Gem::Dependency
57
- name: ruby2_keywords
58
- requirement: !ruby/object:Gem::Requirement
59
- requirements:
60
- - - ">="
61
- - !ruby/object:Gem::Version
62
- version: '0'
63
- type: :development
64
- prerelease: false
65
- version_requirements: !ruby/object:Gem::Requirement
66
- requirements:
67
- - - ">="
68
- - !ruby/object:Gem::Version
69
- version: '0'
70
70
  - !ruby/object:Gem::Dependency
71
71
  name: timecop
72
72
  requirement: !ruby/object:Gem::Requirement
@@ -295,7 +295,7 @@ homepage: https://github.com/sds/mock_redis
295
295
  licenses:
296
296
  - MIT
297
297
  metadata: {}
298
- post_install_message:
298
+ post_install_message:
299
299
  rdoc_options: []
300
300
  require_paths:
301
301
  - lib
@@ -311,7 +311,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
311
311
  version: '0'
312
312
  requirements: []
313
313
  rubygems_version: 3.1.4
314
- signing_key:
314
+ signing_key:
315
315
  specification_version: 4
316
316
  summary: Redis mock that just lives in memory; useful for testing.
317
317
  test_files: