Skip to content

Commit

Permalink
Support #scan with type option (#270)
Browse files Browse the repository at this point in the history
  • Loading branch information
bertm13 authored May 28, 2023
1 parent 4f5a864 commit e26d205
Show file tree
Hide file tree
Showing 4 changed files with 55 additions and 17 deletions.
4 changes: 3 additions & 1 deletion lib/mock_redis/utility_methods.rb
Original file line number Diff line number Diff line change
Expand Up @@ -28,14 +28,16 @@ def common_scan(values, cursor, opts = {})
cursor = cursor.to_i
match = opts[:match] || '*'
key = opts[:key] || lambda { |x| x }
type_opt = opts[:type]
filtered_values = []

limit = cursor + count
next_cursor = limit >= values.length ? '0' : limit.to_s

unless values[cursor...limit].nil?
filtered_values = values[cursor...limit].select do |val|
redis_pattern_to_ruby_regex(match).match(key.call(val))
redis_pattern_to_ruby_regex(match).match(key.call(val)) &&
(type_opt.nil? || type(val) == type_opt)
end
end

Expand Down
6 changes: 4 additions & 2 deletions lib/mock_redis/zset.rb
Original file line number Diff line number Diff line change
Expand Up @@ -10,9 +10,11 @@ class Zset

def_delegators :members, :empty?, :include?, :size

def initialize
def initialize(enum = nil)
@members = Set.new
@scores = {}
@members.merge(enum) if enum

@scores = {}
end

def initialize_copy(source)
Expand Down
27 changes: 22 additions & 5 deletions spec/commands/scan_each_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -6,11 +6,11 @@
let(:match) { '*' }

before do
allow(subject).to receive_message_chain(:data, :keys).and_return(collection)
allow(subject).to receive(:data).and_return(data)
end

context 'when no keys are found' do
let(:collection) { [] }
let(:data) { {} }

it 'does not iterate over any elements' do
expect(subject.scan_each.to_a).to be_empty
Expand All @@ -19,21 +19,38 @@

context 'when keys are found' do
context 'when no match filter is supplied' do
let(:collection) { Array.new(20) { |i| "mock:key#{i}" } }
let(:data) { Array.new(20) { |i| "mock:key#{i}" }.to_h { |e| [e, nil] } }

it 'iterates over each item in the collection' do
expect(subject.scan_each.to_a).to match_array(collection)
expect(subject.scan_each.to_a).to match_array(data.keys)
end
end

context 'when giving a custom match filter' do
let(:match) { 'mock:key*' }
let(:collection) { %w[mock:key mock:key2 mock:otherkey] }
let(:data) { ['mock:key', 'mock:key2', 'mock:otherkey'].to_h { |e| [e, nil] } }
let(:expected) { %w[mock:key mock:key2] }

it 'iterates over each item in the filtered collection' do
expect(subject.scan_each(match: match).to_a).to match_array(expected)
end
end

context 'when giving a custom match and type filter' do
let(:data) do
{ 'mock:stringkey' => 'mockvalue',
'mock:listkey' => ['mockvalue1'],
'mock:hashkey' => { 'mocksubkey' => 'mockvalue' },
'mock:setkey' => Set.new(['mockvalue']),
'mock:zsetkey' => MockRedis::Zset.new(['mockvalue']) }
end
let(:match) { 'mock:*' }
let(:type) { 'string' }
let(:expected) { %w[mock:stringkey] }

it 'iterates over each item in the filtered collection' do
expect(subject.scan_each(match: match, type: type)).to match_array(expected)
end
end
end
end
35 changes: 26 additions & 9 deletions spec/commands/scan_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -7,11 +7,11 @@
let(:match) { '*' }

before do
allow(subject).to receive_message_chain(:data, :keys).and_return(collection)
allow(subject).to receive(:data).and_return(data)
end

context 'when no keys are found' do
let(:collection) { [] }
let(:data) { {} }

it 'returns a 0 cursor and an empty collection' do
expect(subject.scan(0, count: count, match: match)).to eq(['0', []])
Expand All @@ -20,9 +20,9 @@

context 'when keys are found' do
context 'when count is lower than collection size' do
let(:collection) { Array.new(count * 2) { |i| "mock:key#{i}" } }
let(:expected_first) { [count.to_s, collection[0...count]] }
let(:expected_second) { ['0', collection[count..]] }
let(:data) { Array.new(count * 2) { |i| "mock:key#{i}" }.to_h { |e| [e, nil] } }
let(:expected_first) { [count.to_s, data.keys[0...count]] }
let(:expected_second) { ['0', data.keys[count..]] }

it 'returns a the next cursor and the collection' do
expect(subject.scan(0, count: count, match: match)).to eq(expected_first)
Expand All @@ -34,16 +34,16 @@
end

context 'when count is greater or equal than collection size' do
let(:collection) { Array.new(count) { |i| "mock:key#{i}" } }
let(:expected) { ['0', collection] }
let(:data) { Array.new(count) { |i| "mock:key#{i}" }.to_h { |e| [e, nil] } }
let(:expected) { ['0', data.keys] }

it 'returns a 0 cursor and the collection' do
expect(subject.scan(0, count: count, match: match)).to eq(expected)
end
end

context 'when cursor is greater than collection size' do
let(:collection) { Array.new(count) { |i| "mock:key#{i}" } }
let(:data) { Array.new(count) { |i| "mock:key#{i}" }.to_h { |e| [e, nil] } }
let(:expected) { ['0', []] }

it 'returns a 0 cursor and empty collection' do
Expand All @@ -53,12 +53,29 @@

context 'when giving a custom match filter' do
let(:match) { 'mock:key*' }
let(:collection) { %w[mock:key mock:key2 mock:otherkey] }
let(:data) { ['mock:key', 'mock:key2', 'mock:otherkey'].to_h { |e| [e, nil] } }
let(:expected) { ['0', %w[mock:key mock:key2]] }

it 'returns a 0 cursor and the filtered collection' do
expect(subject.scan(0, count: count, match: match)).to eq(expected)
end
end

context 'when giving a custom match and type filter' do
let(:data) do
{ 'mock:stringkey' => 'mockvalue',
'mock:listkey' => ['mockvalue1'],
'mock:hashkey' => { 'mocksubkey' => 'mockvalue' },
'mock:setkey' => Set.new(['mockvalue']),
'mock:zsetkey' => MockRedis::Zset.new(['mockvalue']) }
end
let(:match) { 'mock:*' }
let(:type) { 'string' }
let(:expected) { ['0', %w[mock:stringkey]] }

it 'returns a 0 cursor and the filtered collection' do
expect(subject.scan(0, count: count, match: match, type: type)).to eq(expected)
end
end
end
end

0 comments on commit e26d205

Please sign in to comment.