Skip to content

Commit 1ac9352

Browse files
authored
Add some test cases (#19)
1 parent c1351fe commit 1ac9352

File tree

4 files changed

+116
-41
lines changed

4 files changed

+116
-41
lines changed

lib/redis_client/cluster/errors.rb

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -33,8 +33,15 @@ class CommandErrorCollection < ::RedisClient::Error
3333
attr_reader :errors
3434

3535
def initialize(errors)
36-
@errors = ERR_ARG_NORMALIZATION.call(errors)
37-
super("Command errors were replied on any node: #{@errors.map(&:message).uniq.join(',')}")
36+
@errors = {}
37+
if !errors.is_a?(Hash) || errors.empty?
38+
super('')
39+
return
40+
end
41+
42+
@errors = errors
43+
messages = @errors.map { |node_key, error| "#{node_key}: #{error.message}" }
44+
super("Command errors were replied on any node: #{messages.join(', ')}")
3845
end
3946
end
4047

lib/redis_client/cluster/node.rb

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -129,9 +129,11 @@ def process_all(commands, &block)
129129
end
130130

131131
def scale_reading_clients
132-
@clients.select do |node_key, _|
132+
clients = @clients.select do |node_key, _|
133133
replica_disabled? ? primary?(node_key) : replica?(node_key)
134-
end.values
134+
end
135+
136+
clients.values.sort_by { |client| "#{client.config.host}:#{client.config.port}" }
135137
end
136138

137139
def slot_exists?(slot)

test/redis_client/cluster/test_errors.rb

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -44,16 +44,16 @@ def test_orchestration_command_not_supported_error
4444
def test_command_error_collection_error
4545
[
4646
{
47-
errors: [DummyError.new('foo')],
48-
want: { msg: 'Command errors were replied on any node: foo', size: 1 }
47+
errors: { '127.0.0.1:6379' => DummyError.new('foo') },
48+
want: { msg: 'Command errors were replied on any node: 127.0.0.1:6379: foo', size: 1 }
4949
},
5050
{
51-
errors: [DummyError.new('foo'), DummyError.new('bar')],
52-
want: { msg: 'Command errors were replied on any node: foo,bar', size: 2 }
51+
errors: { '127.0.0.1:6379' => DummyError.new('foo'), '127.0.0.1:6380' => DummyError.new('bar') },
52+
want: { msg: 'Command errors were replied on any node: 127.0.0.1:6379: foo, 127.0.0.1:6380: bar', size: 2 }
5353
},
54-
{ errors: [], want: { msg: 'Command errors were replied on any node: ', size: 0 } },
55-
{ errors: '', want: { msg: 'Command errors were replied on any node: ', size: 0 } },
56-
{ errors: nil, want: { msg: 'Command errors were replied on any node: ', size: 0 } }
54+
{ errors: {}, want: { msg: '', size: 0 } },
55+
{ errors: '', want: { msg: '', size: 0 } },
56+
{ errors: nil, want: { msg: '', size: 0 } }
5757
].each_with_index do |c, idx|
5858
raise ::RedisClient::Cluster::CommandErrorCollection, c[:errors]
5959
rescue StandardError => e

test/redis_client/cluster/test_node.rb

Lines changed: 96 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -24,12 +24,12 @@ def test_connection_prelude
2424

2525
class TestNode < Minitest::Test
2626
def setup
27-
config = ::RedisClient::ClusterConfig.new(nodes: TEST_NODE_URIS)
28-
@node_info = ::RedisClient::Cluster::Node.load_info(config.per_node_key, timeout: TEST_TIMEOUT_SEC)
29-
node_addrs = @node_info.map { |info| ::RedisClient::Cluster::NodeKey.hashify(info[:node_key]) }
30-
config.update_node(node_addrs)
31-
@test_node = ::RedisClient::Cluster::Node.new(config.per_node_key, node_info: @node_info, timeout: TEST_TIMEOUT_SEC)
32-
@test_node_with_scale_read = ::RedisClient::Cluster::Node.new(config.per_node_key, node_info: @node_info, with_replica: true, timeout: TEST_TIMEOUT_SEC)
27+
@test_config = ::RedisClient::ClusterConfig.new(nodes: TEST_NODE_URIS)
28+
@test_node_info = ::RedisClient::Cluster::Node.load_info(@test_config.per_node_key, timeout: TEST_TIMEOUT_SEC)
29+
node_addrs = @test_node_info.map { |info| ::RedisClient::Cluster::NodeKey.hashify(info[:node_key]) }
30+
@test_config.update_node(node_addrs)
31+
@test_node = ::RedisClient::Cluster::Node.new(@test_config.per_node_key, node_info: @test_node_info, timeout: TEST_TIMEOUT_SEC)
32+
@test_node_with_scale_read = ::RedisClient::Cluster::Node.new(@test_config.per_node_key, node_info: @test_node_info, with_replica: true, timeout: TEST_TIMEOUT_SEC)
3333
end
3434

3535
def teardown
@@ -157,14 +157,14 @@ def test_enumerable
157157
end
158158

159159
def test_node_keys
160-
want = @node_info.map { |info| info[:node_key] }
160+
want = @test_node_info.map { |info| info[:node_key] }
161161
@test_node.node_keys.each do |got|
162162
assert_includes(want, got, "Case: #{got}")
163163
end
164164
end
165165

166166
def test_find_by
167-
@node_info.each do |info|
167+
@test_node_info.each do |info|
168168
msg = "Case: primary only: #{info[:node_key]}"
169169
got = -> { @test_node.find_by(info[:node_key]) }
170170
if info[:role] == 'master'
@@ -180,38 +180,38 @@ def test_find_by
180180
end
181181

182182
def test_call_all
183-
want = (1..(@node_info.count { |info| info[:role] == 'master' })).map { |_| 'PONG' }
183+
want = (1..(@test_node_info.count { |info| info[:role] == 'master' })).map { |_| 'PONG' }
184184
got = @test_node.call_all(:call, 'PING')
185185
assert_equal(want, got, 'Case: primary only')
186186

187-
want = (1..(@node_info.count)).map { |_| 'PONG' }
187+
want = (1..(@test_node_info.count)).map { |_| 'PONG' }
188188
got = @test_node_with_scale_read.call_all(:call, 'PING')
189189
assert_equal(want, got, 'Case: scale read')
190190
end
191191

192192
def test_call_primary
193-
want = (1..(@node_info.count { |info| info[:role] == 'master' })).map { |_| 'PONG' }
193+
want = (1..(@test_node_info.count { |info| info[:role] == 'master' })).map { |_| 'PONG' }
194194
got = @test_node.call_primary(:call, 'PING')
195195
assert_equal(want, got)
196196
end
197197

198198
def test_call_replica
199-
want = (1..(@node_info.count { |info| info[:role] == 'master' })).map { |_| 'PONG' }
199+
want = (1..(@test_node_info.count { |info| info[:role] == 'master' })).map { |_| 'PONG' }
200200
got = @test_node.call_replica(:call, 'PING')
201201
assert_equal(want, got, 'Case: primary only')
202202

203-
want = (1..(@node_info.count { |info| info[:role] == 'slave' })).map { |_| 'PONG' }
203+
want = (1..(@test_node_info.count { |info| info[:role] == 'slave' })).map { |_| 'PONG' }
204204
got = @test_node_with_scale_read.call_replica(:call, 'PING')
205205
assert_equal(want, got, 'Case: scale read')
206206
end
207207

208208
def test_scale_reading_clients
209-
want = @node_info.select { |info| info[:role] == 'master' }.map { |info| info[:node_key] }.sort
210-
got = @test_node.scale_reading_clients.map { |client| "#{client.config.host}:#{client.config.port}" }.sort
209+
want = @test_node_info.select { |info| info[:role] == 'master' }.map { |info| info[:node_key] }.sort
210+
got = @test_node.scale_reading_clients.map { |client| "#{client.config.host}:#{client.config.port}" }
211211
assert_equal(want, got, 'Case: primary only')
212212

213-
want = @node_info.select { |info| info[:role] == 'slave' }.map { |info| info[:node_key] }.sort
214-
got = @test_node_with_scale_read.scale_reading_clients.map { |client| "#{client.config.host}:#{client.config.port}" }.sort
213+
want = @test_node_info.select { |info| info[:role] == 'slave' }.map { |info| info[:node_key] }.sort
214+
got = @test_node_with_scale_read.scale_reading_clients.map { |client| "#{client.config.host}:#{client.config.port}" }
215215
assert_equal(want, got, 'Case: scale read')
216216
end
217217

@@ -224,20 +224,20 @@ def test_slot_exists?
224224
end
225225

226226
def test_find_node_key_of_primary
227-
sample_node = @node_info.find { |info| info[:role] == 'master' }
227+
sample_node = @test_node_info.find { |info| info[:role] == 'master' }
228228
sample_slot = sample_node[:slots].first.first
229229
got = @test_node.find_node_key_of_primary(sample_slot)
230230
assert_equal(sample_node[:node_key], got)
231231
end
232232

233233
def test_find_node_key_of_replica
234-
sample_node = @node_info.find { |info| info[:role] == 'master' }
234+
sample_node = @test_node_info.find { |info| info[:role] == 'master' }
235235
sample_slot = sample_node[:slots].first.first
236236
got = @test_node.find_node_key_of_replica(sample_slot)
237237
assert_equal(sample_node[:node_key], got, 'Case: primary only')
238238

239-
sample_replica = @node_info.find { |info| info[:role] == 'slave' }
240-
sample_primary = @node_info.find { |info| info[:id] == sample_replica[:primary_id] }
239+
sample_replica = @test_node_info.find { |info| info[:role] == 'slave' }
240+
sample_primary = @test_node_info.find { |info| info[:id] == sample_replica[:primary_id] }
241241
sample_slot = sample_primary[:slots].first.first
242242
got = @test_node_with_scale_read.find_node_key_of_replica(sample_slot)
243243
assert_equal(sample_replica[:node_key], got, 'Case: scale read')
@@ -246,7 +246,7 @@ def test_find_node_key_of_replica
246246
def test_update_slot
247247
sample_slot = 0
248248
base_node_key = @test_node.find_node_key_of_primary(sample_slot)
249-
another_node_key = @node_info.find { |info| info[:node_key] != base_node_key && info[:role] == 'master' }
249+
another_node_key = @test_node_info.find { |info| info[:node_key] != base_node_key && info[:role] == 'master' }
250250
@test_node.update_slot(sample_slot, another_node_key)
251251
assert_equal(another_node_key, @test_node.find_node_key_of_primary(sample_slot))
252252
end
@@ -257,33 +257,99 @@ def test_replica_disabled?
257257
end
258258

259259
def test_primary?
260-
sample_primary = @node_info.find { |info| info[:role] == 'master' }
261-
sample_replica = @node_info.find { |info| info[:role] == 'slave' }
260+
sample_primary = @test_node_info.find { |info| info[:role] == 'master' }
261+
sample_replica = @test_node_info.find { |info| info[:role] == 'slave' }
262262
assert(@test_node.send(:primary?, sample_primary[:node_key]))
263263
refute(@test_node.send(:primary?, sample_replica[:node_key]))
264264
end
265265

266266
def test_replica?
267-
sample_primary = @node_info.find { |info| info[:role] == 'master' }
268-
sample_replica = @node_info.find { |info| info[:role] == 'slave' }
267+
sample_primary = @test_node_info.find { |info| info[:role] == 'master' }
268+
sample_replica = @test_node_info.find { |info| info[:role] == 'slave' }
269269
refute(@test_node.send(:replica?, sample_primary[:node_key]))
270270
assert(@test_node.send(:replica?, sample_replica[:node_key]))
271271
end
272272

273273
def test_build_slot_node_mappings
274-
skip('TODO')
274+
node_info = [
275+
{ node_key: '127.0.0.1:7001', slots: [[0, 3000], [3002, 5460], [15_001, 15_001]] },
276+
{ node_key: '127.0.0.1:7002', slots: [[3001, 3001], [5461, 7000], [7002, 10_922]] },
277+
{ node_key: '127.0.0.1:7003', slots: [[7001, 7001], [10_923, 15_000], [15_002, 16_383]] },
278+
{ node_key: '127.0.0.1:7004', slots: [] },
279+
{ node_key: '127.0.0.1:7005', slots: [] },
280+
{ node_key: '127.0.0.1:7006', slots: [] }
281+
]
282+
got = @test_node.send(:build_slot_node_mappings, node_info)
283+
node_info.each do |info|
284+
next if info[:slots].empty?
285+
286+
info[:slots].each do |range|
287+
(range[0]..range[1]).each { |slot| assert_same(info[:node_key], got[slot], "Case: #{slot}") }
288+
end
289+
end
275290
end
276291

277292
def test_build_replication_mappings
278-
skip('TODO')
293+
node_key1 = '127.0.0.1:7001'
294+
node_key2 = '127.0.0.1:7002'
295+
node_key3 = '127.0.0.1:7003'
296+
node_key4 = '127.0.0.1:7004'
297+
node_key5 = '127.0.0.1:7005'
298+
node_key6 = '127.0.0.1:7006'
299+
node_key7 = '127.0.0.1:7007'
300+
node_key8 = '127.0.0.1:7008'
301+
node_key9 = '127.0.0.1:7009'
302+
node_info = [
303+
{ id: '1', node_key: node_key1, primary_id: '-' },
304+
{ id: '2', node_key: node_key2, primary_id: '-' },
305+
{ id: '3', node_key: node_key3, primary_id: '-' },
306+
{ id: '4', node_key: node_key4, primary_id: '1' },
307+
{ id: '5', node_key: node_key5, primary_id: '2' },
308+
{ id: '6', node_key: node_key6, primary_id: '3' },
309+
{ id: '7', node_key: node_key7, primary_id: '1' },
310+
{ id: '8', node_key: node_key8, primary_id: '2' },
311+
{ id: '9', node_key: node_key9, primary_id: '3' }
312+
]
313+
got = @test_node.send(:build_replication_mappings, node_info)
314+
got.transform_values!(&:sort!)
315+
assert_same(node_key4, got[node_key1][0])
316+
assert_same(node_key7, got[node_key1][1])
317+
assert_same(node_key5, got[node_key2][0])
318+
assert_same(node_key8, got[node_key2][1])
319+
assert_same(node_key6, got[node_key3][0])
320+
assert_same(node_key9, got[node_key3][1])
279321
end
280322

281323
def test_build_clients
282-
skip('TODO')
324+
attrs = %i[connect_timeout read_timeout write_timeout].freeze
325+
326+
got = @test_node.send(:build_clients, @test_config.per_node_key, timeout: 10)
327+
assert_equal(@test_node_info.count { |info| info[:role] == 'master' }, got.size, 'Case: primary only: size')
328+
got.each do |_, client|
329+
attrs.each { |attr| assert_equal(10, client.config.send(attr), "Case: primary only: #{attr}") }
330+
end
331+
332+
got = @test_node_with_scale_read.send(:build_clients, @test_config.per_node_key, timeout: 11)
333+
assert_equal(@test_node_info.size, got.size, 'Case: scale read: size')
334+
got.each do |_, client|
335+
attrs.each { |attr| assert_equal(11, client.config.send(attr), "Case: scale read: #{attr}") }
336+
end
283337
end
284338

285339
def test_try_map
286-
skip('TODO')
340+
primary_node_keys = @test_node_info.select { |info| info[:role] == 'master' }.map { |info| info[:node_key] }
341+
[
342+
{ block: ->(_, client) { client.call('PING') }, want: primary_node_keys.to_h { |k| [k, 'PONG'] } },
343+
{ block: ->(_, client) { client.call('UNKNOWN') }, error: ::RedisClient::Cluster::CommandErrorCollection }
344+
].each_with_index do |c, idx|
345+
msg = "Case: #{idx}"
346+
got = -> { @test_node.send(:try_map, &c[:block]) }
347+
if c.key?(:error)
348+
assert_raises(c[:error], msg, &got)
349+
else
350+
assert_equal(c[:want], got.call, msg)
351+
end
352+
end
287353
end
288354
end
289355
end

0 commit comments

Comments
 (0)