mock_redis 0.27.2 → 0.30.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: 5a02f4ad5b82a9a913511533ca04c80bb75025dd0a4a1537917b57d65d34982c
4
- data.tar.gz: 0fac5aab079e4cbad0cab684f3c670ae7f0b17650670347eae1402306d2f05e3
3
+ metadata.gz: 15bcc9fb58837462df30acd61176547493ce217061459f2c73e134f8ce15c5ab
4
+ data.tar.gz: 46df9da818b1b4cdf817a2d8700d6321c182934379974aaa21393bd588b6ab04
5
5
  SHA512:
6
- metadata.gz: f5276d3d1f052bdf6c7e1ab8a78b450091ca673deb8e15bd02d4ee2e1f22aa43e98bff1cc33e03fd370e4db790ce3b59caadc5675358ffa85687e2972be873f5
7
- data.tar.gz: ad4882bf89ce3654b99f2b90880b43a693dbd4ee7569458539d2e115dacc896ae4e86f766fbb9fed5cbab5868172b711c76d2fa7c37f9f05fc7a5a96495f6d47
6
+ metadata.gz: 7322c682d6b702f98afcfd42aac3fe7c6676d63e064d977ddbbfdbaf0f40bca96cd88636e7fd43384dc00455e4421f56555342aabe5707af28ea084587060db9
7
+ data.tar.gz: 36fa3abf3f2eef55e305a79ea6010edf90ba561e89b10466942f4f8f587c82cb400d97dae0cd330ff8bb73a494cff5171a8b3ab09485cdf76ae0804e9d1af0bb
@@ -0,0 +1,31 @@
1
+ name: Lint
2
+ on:
3
+ push:
4
+ branches: [main]
5
+ pull_request:
6
+ branches: [main]
7
+
8
+ jobs:
9
+ overcommit:
10
+ timeout-minutes: 10
11
+ runs-on: ubuntu-latest
12
+
13
+ steps:
14
+ - uses: actions/checkout@v2
15
+
16
+ - name: Set up Ruby
17
+ uses: ruby/setup-ruby@v1.97.0
18
+ with:
19
+ ruby-version: 2.7
20
+
21
+ - name: Install dependencies
22
+ run: bundle install
23
+
24
+ - name: Prepare environment
25
+ run: |
26
+ git config --local user.email "gh-actions@example.com"
27
+ git config --local user.name "GitHub Actions"
28
+ bundle exec overcommit --sign
29
+
30
+ - name: Run pre-commit checks
31
+ run: bundle exec overcommit --run
@@ -0,0 +1,45 @@
1
+ name: Tests
2
+ on:
3
+ push:
4
+ branches: [main]
5
+ pull_request:
6
+ branches: [main]
7
+
8
+ jobs:
9
+ rspec:
10
+ timeout-minutes: 10
11
+ runs-on: ubuntu-latest
12
+
13
+ strategy:
14
+ matrix:
15
+ ruby-version:
16
+ - '2.6'
17
+ - '2.7'
18
+ - '3.0'
19
+ redis-version:
20
+ - '6.2'
21
+
22
+ services:
23
+ redis:
24
+ image: redis:${{ matrix.redis-version }}-alpine
25
+ options: >-
26
+ --health-cmd "redis-cli ping"
27
+ --health-interval 10s
28
+ --health-timeout 5s
29
+ --health-retries 5
30
+ ports:
31
+ - 6379:6379
32
+
33
+ steps:
34
+ - uses: actions/checkout@v2
35
+
36
+ - name: Set up Ruby ${{ matrix.ruby-version }}
37
+ uses: ruby/setup-ruby@v1.97.0
38
+ with:
39
+ ruby-version: ${{ matrix.ruby-version }}
40
+
41
+ - name: Install dependencies
42
+ run: bundle install
43
+
44
+ - name: Run tests
45
+ run: bundle exec rspec
data/CHANGELOG.md CHANGED
@@ -1,5 +1,27 @@
1
1
  # MockRedis Changelog
2
2
 
3
+ ### 0.30.0
4
+
5
+ * Drop support for Ruby 2.4 and Ruby 2.5 since they are EOL
6
+ * Fix `expire` to to raise error on invalid integer
7
+
8
+ ### 0.29.0
9
+
10
+ * Add support for `logger` option ([#211](https://github.com/sds/mock_redis/pull/211))
11
+ * Fix `zadd` to not perform conditional type conversion ([#214](https://github.com/sds/mock_redis/pull/214))
12
+ * Fix `hdel` to raise error when called with empty array ([#215](https://github.com/sds/mock_redis/pull/215))
13
+
14
+ ### 0.28.0
15
+
16
+ * Fix `hmset` exception ([#206](https://github.com/sds/mock_redis/pull/206))
17
+ * Fix `hmset` to accept hashes in addition to key/value pairs ([#208](https://github.com/sds/mock_redis/pull/208))
18
+ * Fix stream ID regex to support `(` ([#209](https://github.com/sds/mock_redis/pull/209))
19
+ * Allow `mget` to accept a block ([#210](https://github.com/sds/mock_redis/pull/210))
20
+
21
+ ### 0.27.3
22
+
23
+ * Ensure `ruby2_keywords` dependency is `require`d at runtime
24
+
3
25
  ### 0.27.2
4
26
 
5
27
  * Switch `ruby2_keywords` gem from development dependency to runtime dependency
data/README.md CHANGED
@@ -10,9 +10,9 @@ for use in tests.
10
10
 
11
11
  ## Requirements
12
12
 
13
- * Ruby 2.4+
13
+ Ruby 2.6+
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.2. Older versions may work, but can also return different results or not support some commands.
16
16
 
17
17
  ## Getting Started
18
18
 
@@ -86,27 +86,27 @@ class MockRedis
86
86
  end
87
87
 
88
88
  def expire(key, seconds)
89
+ assert_valid_integer(seconds)
90
+
89
91
  pexpire(key, seconds.to_i * 1000)
90
92
  end
91
93
 
92
94
  def pexpire(key, ms)
95
+ assert_valid_integer(ms)
96
+
93
97
  now, miliseconds = @base.now
94
98
  now_ms = (now * 1000) + miliseconds
95
99
  pexpireat(key, now_ms + ms.to_i)
96
100
  end
97
101
 
98
102
  def expireat(key, timestamp)
99
- unless looks_like_integer?(timestamp.to_s)
100
- raise Redis::CommandError, 'ERR value is not an integer or out of range'
101
- end
103
+ assert_valid_integer(timestamp)
102
104
 
103
105
  pexpireat(key, timestamp.to_i * 1000)
104
106
  end
105
107
 
106
108
  def pexpireat(key, timestamp_ms)
107
- unless looks_like_integer?(timestamp_ms.to_s)
108
- raise Redis::CommandError, 'ERR value is not an integer or out of range'
109
- end
109
+ assert_valid_integer(timestamp_ms)
110
110
 
111
111
  if exists?(key)
112
112
  timestamp = Rational(timestamp_ms.to_i, 1000)
@@ -280,6 +280,13 @@ class MockRedis
280
280
 
281
281
  private
282
282
 
283
+ def assert_valid_integer(integer)
284
+ unless looks_like_integer?(integer.to_s)
285
+ raise Redis::CommandError, 'ERR value is not an integer or out of range'
286
+ end
287
+ integer
288
+ end
289
+
283
290
  def assert_valid_timeout(timeout)
284
291
  if !looks_like_integer?(timeout.to_s)
285
292
  raise Redis::CommandError, 'ERR timeout is not an integer or out of range'
@@ -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
@@ -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'
@@ -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
+ items = if start_id.exclusive
53
+ members
54
+ .select { |m| (start_id < m[0]) && (finish_id >= m[0]) }
55
+ .map { |m| [m[0].to_s, m[1]] }
56
+ else
57
+ 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
@@ -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.2'
5
+ VERSION = '0.30.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/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,30 @@ 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
+ if t1
171
+ logger.debug(format('[MockRedis] call_time=%<time>0.2f ms', time: ((Time.now - t1) * 1000)))
172
+ end
173
+ end
174
+ end
141
175
  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(id).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,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: mock_redis
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.27.2
4
+ version: 0.30.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: 2021-01-07 00:00:00.000000000 Z
12
+ date: 2022-02-17 00:00:00.000000000 Z
13
13
  dependencies:
14
14
  - !ruby/object:Gem::Dependency
15
15
  name: ruby2_keywords
@@ -89,6 +89,8 @@ executables: []
89
89
  extensions: []
90
90
  extra_rdoc_files: []
91
91
  files:
92
+ - ".github/workflows/lint.yml"
93
+ - ".github/workflows/tests.yml"
92
94
  - ".gitignore"
93
95
  - ".mailmap"
94
96
  - ".overcommit.yml"
@@ -96,7 +98,6 @@ files:
96
98
  - ".rubocop.yml"
97
99
  - ".rubocop_todo.yml"
98
100
  - ".simplecov"
99
- - ".travis.yml"
100
101
  - CHANGELOG.md
101
102
  - Gemfile
102
103
  - LICENSE.md
@@ -310,7 +311,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
310
311
  - !ruby/object:Gem::Version
311
312
  version: '0'
312
313
  requirements: []
313
- rubygems_version: 3.1.4
314
+ rubygems_version: 3.1.6
314
315
  signing_key:
315
316
  specification_version: 4
316
317
  summary: Redis mock that just lives in memory; useful for testing.
data/.travis.yml DELETED
@@ -1,33 +0,0 @@
1
- language: ruby
2
-
3
- cache: bundler
4
-
5
- addons:
6
- apt:
7
- packages:
8
- - redis-server
9
-
10
- services:
11
- - redis-server
12
-
13
- before_install:
14
- - sudo sed -e 's/^bind.*/bind 127.0.0.1/' /etc/redis/redis.conf > redis.conf
15
- - sudo mv redis.conf /etc/redis
16
- - sudo service redis-server start
17
- - echo PING | nc localhost 6379
18
-
19
- rvm:
20
- - 2.4
21
- - 2.5
22
- - 2.6
23
- - 2.7
24
-
25
- before_script:
26
- - git config --local user.email "travis@travis.ci"
27
- - git config --local user.name "Travis CI"
28
-
29
- script:
30
- - redis-cli --version
31
- - bundle exec rspec
32
- - bundle exec overcommit --sign
33
- - bundle exec overcommit --run