Skip to content

Commit 198230c

Browse files
authored
Add some test cases (#12)
1 parent 570fcfa commit 198230c

File tree

7 files changed

+149
-30
lines changed

7 files changed

+149
-30
lines changed

.github/workflows/test.yaml

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -48,11 +48,13 @@ jobs:
4848
# @see https://docs.docker.com/engine/reference/commandline/create/#options
4949
- name: Pull Docker images
5050
run: docker pull redis:$REDIS_VERSION
51-
- name: Docker compose up
51+
- name: Run containers
5252
run: docker compose up -d
5353
- name: Wait for Redis cluster to be ready
5454
run: bundle exec rake wait
5555
- name: Print containers
5656
run: docker compose ps
5757
- name: Run minitest
5858
run: bundle exec rake test
59+
- name: Stop containers
60+
run: docker compose down

lib/redis_client/cluster.rb

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -195,7 +195,7 @@ def send_script_command(method, *command, **kwargs, &block)
195195
end
196196
end
197197

198-
def send_pubsub_command(method, *command, **kwargs, &block) # rubocop:disable Metircs/AbcSize, Metrics/CyclomaticComplexity
198+
def send_pubsub_command(method, *command, **kwargs, &block) # rubocop:disable Metrics/AbcSize, Metrics/CyclomaticComplexity
199199
case command[1].to_s.downcase
200200
when 'channels' then @node.call_all(method, *command, **kwargs, &block).flatten.uniq.sort
201201
when 'numsub'

lib/redis_client/cluster/command.rb

Lines changed: 9 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -22,8 +22,8 @@ def load(nodes)
2222
private
2323

2424
def parse_command_details(rows)
25-
rows.to_h do |row|
26-
[row[0], { arity: row[1], flags: row[2], first: row[3], last: row[4], step: row[5] }]
25+
rows&.reject { |row| row[0].nil? }.to_h do |row|
26+
[row[0].downcase, { arity: row[1], flags: row[2], first: row[3], last: row[4], step: row[5] }]
2727
end
2828
end
2929
end
@@ -62,14 +62,14 @@ def pick_details(details)
6262
end
6363

6464
def dig_details(command, key)
65-
name = command.first.to_s
66-
return unless @details.key?(name)
65+
name = command&.flatten&.first.to_s.downcase
66+
return if name.empty? || !@details.key?(name)
6767

6868
@details.fetch(name).fetch(key)
6969
end
7070

71-
def determine_first_key_position(command)
72-
case command.first.to_s.downcase
71+
def determine_first_key_position(command) # rubocop:disable Metrics/CyclomaticComplexity
72+
case command&.flatten&.first.to_s.downcase
7373
when 'eval', 'evalsha', 'migrate', 'zinterstore', 'zunionstore' then 3
7474
when 'object' then 2
7575
when 'memory'
@@ -81,13 +81,14 @@ def determine_first_key_position(command)
8181
end
8282
end
8383

84-
def determine_optional_key_position(command, option_name)
85-
idx = command.map(&:to_s).map(&:downcase).index(option_name)
84+
def determine_optional_key_position(command, option_name) # rubocop:disable Metrics/CyclomaticComplexity, Metrics/PerceivedComplexity
85+
idx = command&.flatten&.map(&:to_s)&.map(&:downcase)&.index(option_name&.downcase)
8686
idx.nil? ? 0 : idx + 1
8787
end
8888

8989
# @see https://redis.io/topics/cluster-spec#keys-hash-tags Keys hash tags
9090
def extract_hash_tag(key)
91+
key = key.to_s
9192
s = key.index('{')
9293
e = key.index('}', s.to_i + 1)
9394

test/.keep

Whitespace-only changes.

test/redis_client/cluster/controller.rb

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -7,11 +7,11 @@ class Cluster
77
class Controller
88
SLOT_SIZE = 16_384
99

10-
def initialize(node_addrs, timeout: 30.0, reconnect_attempts: 10)
10+
def initialize(node_addrs, timeout: 30.0, reconnect_attempts: 10, **kwargs)
1111
raise 'Redis Cluster requires at least 3 master nodes.' if node_addrs.size < 3
1212

1313
@clients = node_addrs.map do |addr|
14-
RedisClient.new(url: addr, timeout: timeout, reconnect_attempts: reconnect_attempts)
14+
RedisClient.new(url: addr, timeout: timeout, reconnect_attempts: reconnect_attempts, **kwargs)
1515
end
1616

1717
@timeout = timeout

test/redis_client/cluster/test_command.rb

Lines changed: 118 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -2,12 +2,16 @@
22

33
require 'set'
44
require 'testing_helper'
5+
require 'redis_client/cluster'
56
require 'redis_client/cluster/command'
67

78
class RedisClient
89
class Cluster
910
class TestCommand < Minitest::Test
11+
include ::RedisClient::TestingHelper
12+
1013
def test_parse_command_details
14+
keys = %i[arity flags first last step].freeze
1115
[
1216
{
1317
rows: [
@@ -19,24 +23,30 @@ def test_parse_command_details
1923
'set' => { arity: -3, flags: Set['write', 'denyoom', 'movablekeys'], first: 1, last: 1, step: 1 }
2024
}
2125
},
26+
{
27+
rows: [
28+
['GET', 2, Set['readonly', 'fast'], 1, 1, 1, Set['@read', '@string', '@fast'], Set[], Set[], Set[]]
29+
],
30+
want: {
31+
'get' => { arity: 2, flags: Set['readonly', 'fast'], first: 1, last: 1, step: 1 }
32+
}
33+
},
34+
{ rows: [[]], want: {} },
2235
{ rows: [], want: {} },
2336
{ rows: nil, want: {} }
24-
].each do |c|
37+
].each_with_index do |c, idx|
38+
msg = "Case: #{idx}"
2539
got = ::RedisClient::Cluster::Command.send(:parse_command_details, c[:rows])
26-
assert_equal(c[:want].size, got.size)
27-
assert_equal(c[:want].keys.sort, got.keys.sort)
28-
c[:want].each do |k, v|
29-
a = got[k]
30-
assert_equal(v[:arity], a[:arity])
31-
assert_equal(v[:flags], a[:flags])
32-
assert_equal(v[:first], a[:first])
33-
assert_equal(v[:last], a[:last])
34-
assert_equal(v[:step], a[:step])
40+
assert_equal(c[:want].size, got.size, msg)
41+
assert_equal(c[:want].keys.sort, got.keys.sort, msg)
42+
c[:want].each do |k1, v|
43+
keys.each { |k2| assert_equal(v[k2], got[k1][k2], "#{msg}: #{k2}") }
3544
end
3645
end
3746
end
3847

3948
def test_pick_details
49+
keys = %i[first_key_position write readonly].freeze
4050
[
4151
{
4252
details: {
@@ -50,19 +60,109 @@ def test_pick_details
5060
},
5161
{ details: {}, want: {} },
5262
{ details: nil, want: {} }
53-
].each do |c|
63+
].each_with_index do |c, idx|
64+
msg = "Case: #{idx}"
5465
cmd = ::RedisClient::Cluster::Command.new(c[:details])
5566
got = cmd.send(:pick_details, c[:details])
56-
assert_equal(c[:want].size, got.size)
57-
assert_equal(c[:want].keys.sort, got.keys.sort)
58-
c[:want].each do |k, v|
59-
a = got[k]
60-
assert_equal(v[:first_key_position], a[:first_key_position])
61-
assert_equal(v[:write], a[:write])
62-
assert_equal(v[:readonly], a[:readonly])
67+
assert_equal(c[:want].size, got.size, msg)
68+
assert_equal(c[:want].keys.sort, got.keys.sort, msg)
69+
c[:want].each do |k1, v|
70+
keys.each { |k2| assert_equal(v[k2], got[k1][k2], "#{msg}: #{k2}") }
6371
end
6472
end
6573
end
74+
75+
def test_dig_details
76+
cmd = ::RedisClient::Cluster::Command.new(
77+
{
78+
'get' => { arity: 2, flags: Set['readonly', 'fast'], first: 1, last: 1, step: 1 },
79+
'set' => { arity: -3, flags: Set['write', 'denyoom', 'movablekeys'], first: 1, last: 1, step: 1 }
80+
}
81+
)
82+
[
83+
{ params: { command: %w[SET foo 1], key: :first_key_position }, want: 1 },
84+
{ params: { command: %w[SET foo 1], key: :write }, want: true },
85+
{ params: { command: %w[set foo 1], key: :write }, want: true },
86+
{ params: { command: %w[SET foo 1], key: :readonly }, want: false },
87+
{ params: { command: %w[GET foo], key: :first_key_position }, want: 1 },
88+
{ params: { command: %w[GET foo], key: :write }, want: false },
89+
{ params: { command: %w[GET foo], key: :readonly }, want: true },
90+
{ params: { command: %w[get foo], key: :readonly }, want: true },
91+
{ params: { command: %w[UNKNOWN foo], key: :readonly }, want: nil },
92+
{ params: { command: [['SET'], 'foo', 1], key: :write }, want: true },
93+
{ params: { command: [], key: :readonly }, want: nil },
94+
{ params: { command: nil, key: :readonly }, want: nil }
95+
].each_with_index do |c, idx|
96+
msg = "Case: #{idx}"
97+
got = cmd.send(:dig_details, c[:params][:command], c[:params][:key])
98+
c[:want].nil? ? assert_nil(got, msg) : assert_equal(c[:want], got, msg)
99+
end
100+
end
101+
102+
def test_determine_first_key_position
103+
cmd = ::RedisClient::Cluster::Command.load(@clients)
104+
[
105+
{ command: %w[EVAL "return ARGV[1]" 0 hello], want: 3 },
106+
{ command: [['EVAL'], '"return ARGV[1]"', 0, 'hello'], want: 3 },
107+
{ command: %w[EVALSHA sha1 2 foo bar baz zap], want: 3 },
108+
{ command: %w[MIGRATE host port key 0 5 COPY], want: 3 },
109+
{ command: %w[ZINTERSTORE out 2 zset1 zset2 WEIGHTS 2 3], want: 3 },
110+
{ command: %w[ZUNIONSTORE out 2 zset1 zset2 WEIGHTS 2 3], want: 3 },
111+
{ command: %w[OBJECT HELP], want: 2 },
112+
{ command: %w[MEMORY HELP], want: 0 },
113+
{ command: %w[MEMORY USAGE key], want: 2 },
114+
{ command: %w[XREAD COUNT 2 STREAMS mystream writers 0-0 0-0], want: 4 },
115+
{ command: %w[XREADGROUP GROUP group consumer STREAMS key id], want: 5 },
116+
{ command: %w[SET foo 1], want: 1 },
117+
{ command: %w[set foo 1], want: 1 },
118+
{ command: [['SET'], 'foo', 1], want: 1 },
119+
{ command: %w[GET foo], want: 1 }
120+
].each_with_index do |c, idx|
121+
msg = "Case: #{idx}"
122+
got = cmd.send(:determine_first_key_position, c[:command])
123+
assert_equal(c[:want], got, msg)
124+
end
125+
end
126+
127+
def test_determine_optional_key_position
128+
cmd = ::RedisClient::Cluster::Command.load(@clients)
129+
[
130+
{ params: { command: %w[XREAD COUNT 2 STREAMS mystream writers 0-0 0-0], option_name: 'streams' }, want: 4 },
131+
{ params: { command: %w[XREADGROUP GROUP group consumer STREAMS key id], option_name: 'streams' }, want: 5 },
132+
{ params: { command: %w[GET foo], option_name: 'bar' }, want: 0 },
133+
{ params: { command: ['FOO', ['BAR'], 'BAZ'], option_name: 'bar' }, want: 2 },
134+
{ params: { command: %w[FOO BAR BAZ], option_name: 'BAR' }, want: 2 },
135+
{ params: { command: [], option_name: nil }, want: 0 },
136+
{ params: { command: [], option_name: '' }, want: 0 },
137+
{ params: { command: nil, option_name: nil }, want: 0 }
138+
].each_with_index do |c, idx|
139+
msg = "Case: #{idx}"
140+
got = cmd.send(:determine_optional_key_position, c[:params][:command], c[:params][:option_name])
141+
assert_equal(c[:want], got, msg)
142+
end
143+
end
144+
145+
def test_extract_hash_tag
146+
cmd = ::RedisClient::Cluster::Command.load(@clients)
147+
[
148+
{ key: 'foo', want: '' },
149+
{ key: 'foo{bar}baz', want: 'bar' },
150+
{ key: 'foo{bar}baz{qux}quuc', want: 'bar' },
151+
{ key: 'foo}bar{baz', want: '' },
152+
{ key: 'foo{bar', want: '' },
153+
{ key: 'foo}bar', want: '' },
154+
{ key: 'foo{}bar', want: '' },
155+
{ key: '{}foo', want: '' },
156+
{ key: 'foo{}', want: '' },
157+
{ key: '{}', want: '' },
158+
{ key: '', want: '' },
159+
{ key: nil, want: '' }
160+
].each_with_index do |c, idx|
161+
msg = "Case: #{idx}"
162+
got = cmd.send(:extract_hash_tag, c[:key])
163+
assert_equal(c[:want], got, msg)
164+
end
165+
end
66166
end
67167
end
68168
end

test/testing_helper.rb

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,19 @@
11
# frozen_string_literal: true
22

33
require 'minitest/autorun'
4+
require 'redis_client'
5+
6+
class RedisClient
7+
module TestingHelper
8+
REDIS_SCHEME = ENV.fetch('REDIS_SCHEME', 'redis')
9+
NODE_ADDRS = (7000..7005).map { |port| "#{REDIS_SCHEME}://127.0.0.1:#{port}" }.freeze
10+
11+
def setup
12+
@clients = NODE_ADDRS.map { |addr| ::RedisClient.config(url: addr).new_client }
13+
end
14+
15+
def teardown
16+
@clients&.each(&:close)
17+
end
18+
end
19+
end

0 commit comments

Comments
 (0)