mock_redis 0.27.1 → 0.29.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: 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: