redis 4.0.2 → 4.0.3

Sign up to get free protection for your applications and to get access to all the features.
Files changed (70) hide show
  1. checksums.yaml +4 -4
  2. data/.gitignore +3 -0
  3. data/.travis.yml +2 -2
  4. data/CHANGELOG.md +6 -0
  5. data/lib/redis.rb +97 -11
  6. data/lib/redis/client.rb +19 -11
  7. data/lib/redis/cluster.rb +285 -0
  8. data/lib/redis/cluster/command.rb +81 -0
  9. data/lib/redis/cluster/command_loader.rb +32 -0
  10. data/lib/redis/cluster/key_slot_converter.rb +72 -0
  11. data/lib/redis/cluster/node.rb +104 -0
  12. data/lib/redis/cluster/node_key.rb +35 -0
  13. data/lib/redis/cluster/node_loader.rb +35 -0
  14. data/lib/redis/cluster/option.rb +76 -0
  15. data/lib/redis/cluster/slot.rb +69 -0
  16. data/lib/redis/cluster/slot_loader.rb +47 -0
  17. data/lib/redis/errors.rb +46 -0
  18. data/lib/redis/version.rb +1 -1
  19. data/makefile +54 -16
  20. data/redis.gemspec +2 -1
  21. data/test/client_test.rb +17 -0
  22. data/test/cluster_abnormal_state_test.rb +38 -0
  23. data/test/cluster_blocking_commands_test.rb +15 -0
  24. data/test/cluster_client_internals_test.rb +77 -0
  25. data/test/cluster_client_key_hash_tags_test.rb +88 -0
  26. data/test/cluster_client_options_test.rb +147 -0
  27. data/test/cluster_client_pipelining_test.rb +59 -0
  28. data/test/cluster_client_replicas_test.rb +36 -0
  29. data/test/cluster_client_slots_test.rb +94 -0
  30. data/test/cluster_client_transactions_test.rb +71 -0
  31. data/test/cluster_commands_on_cluster_test.rb +165 -0
  32. data/test/cluster_commands_on_connection_test.rb +40 -0
  33. data/test/cluster_commands_on_geo_test.rb +74 -0
  34. data/test/cluster_commands_on_hashes_test.rb +11 -0
  35. data/test/cluster_commands_on_hyper_log_log_test.rb +17 -0
  36. data/test/cluster_commands_on_keys_test.rb +134 -0
  37. data/test/cluster_commands_on_lists_test.rb +15 -0
  38. data/test/cluster_commands_on_pub_sub_test.rb +101 -0
  39. data/test/cluster_commands_on_scripting_test.rb +56 -0
  40. data/test/cluster_commands_on_server_test.rb +221 -0
  41. data/test/cluster_commands_on_sets_test.rb +39 -0
  42. data/test/cluster_commands_on_sorted_sets_test.rb +35 -0
  43. data/test/cluster_commands_on_streams_test.rb +196 -0
  44. data/test/cluster_commands_on_strings_test.rb +15 -0
  45. data/test/cluster_commands_on_transactions_test.rb +41 -0
  46. data/test/cluster_commands_on_value_types_test.rb +14 -0
  47. data/test/commands_on_hashes_test.rb +2 -14
  48. data/test/commands_on_hyper_log_log_test.rb +2 -14
  49. data/test/commands_on_lists_test.rb +2 -13
  50. data/test/commands_on_sets_test.rb +2 -70
  51. data/test/commands_on_sorted_sets_test.rb +2 -145
  52. data/test/commands_on_strings_test.rb +2 -94
  53. data/test/distributed_blocking_commands_test.rb +8 -0
  54. data/test/distributed_commands_on_hashes_test.rb +16 -3
  55. data/test/distributed_commands_on_hyper_log_log_test.rb +8 -13
  56. data/test/distributed_commands_on_lists_test.rb +4 -5
  57. data/test/distributed_commands_on_sets_test.rb +45 -46
  58. data/test/distributed_commands_on_sorted_sets_test.rb +51 -8
  59. data/test/distributed_commands_on_strings_test.rb +10 -0
  60. data/test/helper.rb +176 -32
  61. data/test/internals_test.rb +13 -0
  62. data/test/lint/blocking_commands.rb +40 -16
  63. data/test/lint/hashes.rb +26 -0
  64. data/test/lint/hyper_log_log.rb +15 -1
  65. data/test/lint/lists.rb +16 -0
  66. data/test/lint/sets.rb +142 -0
  67. data/test/lint/sorted_sets.rb +183 -2
  68. data/test/lint/strings.rb +102 -0
  69. data/test/support/cluster/orchestrator.rb +199 -0
  70. metadata +79 -4
@@ -0,0 +1,221 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative 'helper'
4
+
5
+ # ruby -w -Itest test/cluster_commands_on_server_test.rb
6
+ # @see https://redis.io/commands#server
7
+ class TestClusterCommandsOnServer < Test::Unit::TestCase
8
+ include Helper::Cluster
9
+
10
+ def test_bgrewriteaof
11
+ assert_equal 'Background append only file rewriting started', redis.bgrewriteaof
12
+ end
13
+
14
+ def test_bgsave
15
+ redis_cluster_mock(bgsave: ->(*_) { '+OK' }) do |redis|
16
+ assert_equal 'OK', redis.bgsave
17
+ end
18
+
19
+ err_msg = 'ERR An AOF log rewriting in progress: '\
20
+ "can't BGSAVE right now. "\
21
+ 'Use BGSAVE SCHEDULE in order to schedule a BGSAVE whenever possible.'
22
+
23
+ redis_cluster_mock(bgsave: ->(*_) { "-Error #{err_msg}" }) do |redis|
24
+ assert_raise(Redis::Cluster::CommandErrorCollection, 'Command error replied on any node') do
25
+ redis.bgsave
26
+ end
27
+ end
28
+ end
29
+
30
+ def test_client_kill
31
+ redis_cluster_mock(client: ->(*_) { '-Error ERR No such client' }) do |redis|
32
+ assert_raise(Redis::CommandError, 'ERR No such client') do
33
+ redis.client(:kill, '127.0.0.1:6379')
34
+ end
35
+ end
36
+
37
+ redis_cluster_mock(client: ->(*_) { '+OK' }) do |redis|
38
+ assert_equal 'OK', redis.client(:kill, '127.0.0.1:6379')
39
+ end
40
+ end
41
+
42
+ def test_client_list
43
+ a_client_info = redis.client(:list).first
44
+ actual = a_client_info.keys.sort
45
+ expected = %w[addr age cmd db events fd flags id idle multi name obl oll omem psub qbuf qbuf-free sub]
46
+ assert_equal expected, actual
47
+ end
48
+
49
+ def test_client_getname
50
+ redis.client(:setname, 'my-client-01')
51
+ assert_equal 'my-client-01', redis.client(:getname)
52
+ end
53
+
54
+ def test_client_pause
55
+ assert_equal 'OK', redis.client(:pause, 0)
56
+ end
57
+
58
+ def test_client_reply
59
+ target_version('3.2.0') do
60
+ assert_equal 'OK', redis.client(:reply, 'ON')
61
+ end
62
+ end
63
+
64
+ def test_client_setname
65
+ assert_equal 'OK', redis.client(:setname, 'my-client-01')
66
+ end
67
+
68
+ def test_command
69
+ assert_instance_of Array, redis.command
70
+ end
71
+
72
+ def test_command_count
73
+ assert_true(redis.command(:count) > 0)
74
+ end
75
+
76
+ def test_command_getkeys
77
+ assert_equal %w[a c e], redis.command(:getkeys, :mset, 'a', 'b', 'c', 'd', 'e', 'f')
78
+ end
79
+
80
+ def test_command_info
81
+ expected = [
82
+ ['get', 2, %w[readonly fast], 1, 1, 1],
83
+ ['set', -3, %w[write denyoom], 1, 1, 1],
84
+ ['eval', -3, %w[noscript movablekeys], 0, 0, 0]
85
+ ]
86
+ assert_equal expected, redis.command(:info, :get, :set, :eval)
87
+ end
88
+
89
+ def test_config_get
90
+ expected_keys = if version < '3.2.0'
91
+ %w[hash-max-ziplist-entries list-max-ziplist-entries set-max-intset-entries zset-max-ziplist-entries]
92
+ else
93
+ %w[hash-max-ziplist-entries set-max-intset-entries zset-max-ziplist-entries]
94
+ end
95
+
96
+ assert_equal expected_keys, redis.config(:get, '*max-*-entries*').keys.sort
97
+ end
98
+
99
+ def test_config_rewrite
100
+ redis_cluster_mock(config: ->(*_) { '-Error ERR Rewriting config file: Permission denied' }) do |redis|
101
+ assert_raise(Redis::Cluster::CommandErrorCollection, 'Command error replied on any node') do
102
+ redis.config(:rewrite)
103
+ end
104
+ end
105
+
106
+ redis_cluster_mock(config: ->(*_) { '+OK' }) do |redis|
107
+ assert_equal 'OK', redis.config(:rewrite)
108
+ end
109
+ end
110
+
111
+ def test_config_set
112
+ assert_equal 'OK', redis.config(:set, 'hash-max-ziplist-entries', 512)
113
+ end
114
+
115
+ def test_config_resetstat
116
+ assert_equal 'OK', redis.config(:resetstat)
117
+ end
118
+
119
+ def test_config_db_size
120
+ 10.times { |i| redis.set("key#{i}", 1) }
121
+ assert_equal 10, redis.dbsize
122
+ end
123
+
124
+ def test_debug_object
125
+ # DEBUG OBJECT is a debugging command that should not be used by clients.
126
+ end
127
+
128
+ def test_debug_segfault
129
+ # DEBUG SEGFAULT performs an invalid memory access that crashes Redis.
130
+ # It is used to simulate bugs during the development.
131
+ end
132
+
133
+ def test_flushall
134
+ assert_equal 'OK', redis.flushall
135
+ end
136
+
137
+ def test_flushdb
138
+ assert_equal 'OK', redis.flushdb
139
+ end
140
+
141
+ def test_info
142
+ assert_equal({ 'cluster_enabled' => '1' }, redis.info(:cluster))
143
+ end
144
+
145
+ def test_lastsave
146
+ assert_instance_of Array, redis.lastsave
147
+ end
148
+
149
+ def test_memory_doctor
150
+ target_version('4.0.0') do
151
+ assert_instance_of String, redis.memory(:doctor)
152
+ end
153
+ end
154
+
155
+ def test_memory_help
156
+ target_version('4.0.0') do
157
+ assert_instance_of Array, redis.memory(:help)
158
+ end
159
+ end
160
+
161
+ def test_memory_malloc_stats
162
+ target_version('4.0.0') do
163
+ assert_instance_of String, redis.memory('malloc-stats')
164
+ end
165
+ end
166
+
167
+ def test_memory_purge
168
+ target_version('4.0.0') do
169
+ assert_equal 'OK', redis.memory(:purge)
170
+ end
171
+ end
172
+
173
+ def test_memory_stats
174
+ target_version('4.0.0') do
175
+ assert_instance_of Array, redis.memory(:stats)
176
+ end
177
+ end
178
+
179
+ def test_memory_usage
180
+ target_version('4.0.0') do
181
+ redis.set('key1', 'Hello World')
182
+ assert_equal 61, redis.memory(:usage, 'key1')
183
+ end
184
+ end
185
+
186
+ def test_monitor
187
+ # Add MONITOR command test
188
+ end
189
+
190
+ def test_role
191
+ assert_equal %w[master master master], redis.role.map(&:first)
192
+ end
193
+
194
+ def test_save
195
+ assert_equal 'OK', redis.save
196
+ end
197
+
198
+ def test_shutdown
199
+ assert_raise(Redis::Cluster::OrchestrationCommandNotSupported, 'SHUTDOWN command should be...') do
200
+ redis.shutdown
201
+ end
202
+ end
203
+
204
+ def test_slaveof
205
+ assert_raise(Redis::CommandError, 'ERR SLAVEOF not allowed in cluster mode.') do
206
+ redis.slaveof(:no, :one)
207
+ end
208
+ end
209
+
210
+ def test_slowlog
211
+ assert_instance_of Array, redis.slowlog(:get, 1)
212
+ end
213
+
214
+ def test_sync
215
+ # Internal command used for replication
216
+ end
217
+
218
+ def test_time
219
+ assert_instance_of Array, redis.time
220
+ end
221
+ end
@@ -0,0 +1,39 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative 'helper'
4
+ require_relative 'lint/sets'
5
+
6
+ # ruby -w -Itest test/cluster_commands_on_sets_test.rb
7
+ # @see https://redis.io/commands#set
8
+ class TestClusterCommandsOnSets < Test::Unit::TestCase
9
+ include Helper::Cluster
10
+ include Lint::Sets
11
+
12
+ def test_sdiff
13
+ assert_raise(Redis::CommandError) { super }
14
+ end
15
+
16
+ def test_sdiffstore
17
+ assert_raise(Redis::CommandError) { super }
18
+ end
19
+
20
+ def test_sinter
21
+ assert_raise(Redis::CommandError) { super }
22
+ end
23
+
24
+ def test_sinterstore
25
+ assert_raise(Redis::CommandError) { super }
26
+ end
27
+
28
+ def test_smove
29
+ assert_raise(Redis::CommandError) { super }
30
+ end
31
+
32
+ def test_sunion
33
+ assert_raise(Redis::CommandError) { super }
34
+ end
35
+
36
+ def test_sunionstore
37
+ assert_raise(Redis::CommandError) { super }
38
+ end
39
+ end
@@ -0,0 +1,35 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative 'helper'
4
+ require_relative 'lint/sorted_sets'
5
+
6
+ # ruby -w -Itest test/cluster_commands_on_sorted_sets_test.rb
7
+ # @see https://redis.io/commands#sorted_set
8
+ class TestClusterCommandsOnSortedSets < Test::Unit::TestCase
9
+ include Helper::Cluster
10
+ include Lint::SortedSets
11
+
12
+ def test_zinterstore
13
+ assert_raise(Redis::CommandError) { super }
14
+ end
15
+
16
+ def test_zinterstore_with_aggregate
17
+ assert_raise(Redis::CommandError) { super }
18
+ end
19
+
20
+ def test_zinterstore_with_weights
21
+ assert_raise(Redis::CommandError) { super }
22
+ end
23
+
24
+ def test_zunionstore
25
+ assert_raise(Redis::CommandError) { super }
26
+ end
27
+
28
+ def test_zunionstore_with_aggregate
29
+ assert_raise(Redis::CommandError) { super }
30
+ end
31
+
32
+ def test_zunionstore_with_weights
33
+ assert_raise(Redis::CommandError) { super }
34
+ end
35
+ end
@@ -0,0 +1,196 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative 'helper'
4
+
5
+ # ruby -w -Itest test/cluster_commands_on_streams_test.rb
6
+ # @see https://redis.io/commands#stream
7
+ class TestClusterCommandsOnStreams < Test::Unit::TestCase
8
+ include Helper::Cluster
9
+
10
+ MIN_REDIS_VERSION = '4.9.0'
11
+ ENTRY_ID_FORMAT = /\d+-\d+/
12
+
13
+ def setup
14
+ super
15
+ add_some_entries_to_streams_without_hashtag
16
+ add_some_entries_to_streams_with_hashtag
17
+ end
18
+
19
+ def add_some_entries_to_streams_without_hashtag
20
+ target_version(MIN_REDIS_VERSION) do
21
+ redis.xadd('stream1', '*', 'name', 'John', 'surname', 'Connor')
22
+ redis.xadd('stream1', '*', 'name', 'Sarah', 'surname', 'Connor')
23
+ redis.xadd('stream1', '*', 'name', 'Miles', 'surname', 'Dyson')
24
+ redis.xadd('stream1', '*', 'name', 'Peter', 'surname', 'Silberman')
25
+ end
26
+ end
27
+
28
+ def add_some_entries_to_streams_with_hashtag
29
+ target_version(MIN_REDIS_VERSION) do
30
+ redis.xadd('{stream}1', '*', 'name', 'John', 'surname', 'Connor')
31
+ redis.xadd('{stream}1', '*', 'name', 'Sarah', 'surname', 'Connor')
32
+ redis.xadd('{stream}1', '*', 'name', 'Miles', 'surname', 'Dyson')
33
+ redis.xadd('{stream}1', '*', 'name', 'Peter', 'surname', 'Silberman')
34
+ end
35
+ end
36
+
37
+ def assert_stream_entry(actual, expected_name, expected_surname)
38
+ actual_key = actual.keys.first
39
+ actual_values = actual[actual_key]
40
+
41
+ assert_match ENTRY_ID_FORMAT, actual_key
42
+ assert_equal expected_name, actual_values['name']
43
+ assert_equal expected_surname, actual_values['surname']
44
+ end
45
+
46
+ def assert_stream_pending(actual, expected_size_of_group, expected_consumer_name, expected_size_of_consumer)
47
+ assert_equal expected_size_of_group, actual[:size]
48
+ assert_match ENTRY_ID_FORMAT, actual[:min_entry_id]
49
+ assert_match ENTRY_ID_FORMAT, actual[:max_entry_id]
50
+ assert_equal({ expected_consumer_name => expected_size_of_consumer }, actual[:consumers])
51
+ end
52
+
53
+ # TODO: Remove this helper method when we implement streams interfaces
54
+ def hashify_stream_entries(reply)
55
+ reply.map do |entry_id, values|
56
+ [entry_id, Hash[values.each_slice(2).to_a]]
57
+ end.to_h
58
+ end
59
+
60
+ # TODO: Remove this helper method when we implement streams interfaces
61
+ def hashify_streams(reply)
62
+ reply.map do |stream_key, entries|
63
+ [stream_key, hashify_stream_entries(entries)]
64
+ end.to_h
65
+ end
66
+
67
+ # TODO: Remove this helper method when we implement streams interfaces
68
+ def hashify_stream_pendings(reply)
69
+ {
70
+ size: reply.first,
71
+ min_entry_id: reply[1],
72
+ max_entry_id: reply[2],
73
+ consumers: Hash[reply[3]]
74
+ }
75
+ end
76
+
77
+ def test_xadd
78
+ target_version(MIN_REDIS_VERSION) do
79
+ assert_match ENTRY_ID_FORMAT, redis.xadd('mystream', '*', 'type', 'T-800', 'model', '101')
80
+ assert_match ENTRY_ID_FORMAT, redis.xadd('my{stream}', '*', 'type', 'T-1000')
81
+ end
82
+ end
83
+
84
+ def test_xrange
85
+ target_version(MIN_REDIS_VERSION) do
86
+ actual = redis.xrange('stream1', '-', '+', 'COUNT', 1)
87
+ actual = hashify_stream_entries(actual) # TODO: Remove this step when we implement streams interfaces
88
+ assert_stream_entry(actual, 'John', 'Connor')
89
+
90
+ actual = redis.xrange('{stream}1', '-', '+', 'COUNT', 1)
91
+ actual = hashify_stream_entries(actual) # TODO: Remove this step when we implement streams interfaces
92
+ assert_stream_entry(actual, 'John', 'Connor')
93
+ end
94
+ end
95
+
96
+ def test_xrevrange
97
+ target_version(MIN_REDIS_VERSION) do
98
+ actual = redis.xrevrange('stream1', '+', '-', 'COUNT', 1)
99
+ actual = hashify_stream_entries(actual) # TODO: Remove this step when we implement streams interfaces
100
+ assert_stream_entry(actual, 'Peter', 'Silberman')
101
+
102
+ actual = redis.xrevrange('{stream}1', '+', '-', 'COUNT', 1)
103
+ actual = hashify_stream_entries(actual) # TODO: Remove this step when we implement streams interfaces
104
+ assert_stream_entry(actual, 'Peter', 'Silberman')
105
+ end
106
+ end
107
+
108
+ def test_xlen
109
+ target_version(MIN_REDIS_VERSION) do
110
+ assert_equal 4, redis.xlen('stream1')
111
+ assert_equal 4, redis.xlen('{stream}1')
112
+ end
113
+ end
114
+
115
+ def test_xread
116
+ target_version(MIN_REDIS_VERSION) do
117
+ # non blocking without hashtag
118
+ actual = redis.xread('COUNT', 1, 'STREAMS', 'stream1', 0)
119
+ actual = hashify_streams(actual) # TODO: Remove this step when we implement streams interfaces
120
+ assert_equal 'stream1', actual.keys.first
121
+ assert_stream_entry(actual['stream1'], 'John', 'Connor')
122
+
123
+ # blocking without hashtag
124
+ actual = redis.xread('COUNT', 1, 'BLOCK', 1, 'STREAMS', 'stream1', 0)
125
+ actual = hashify_streams(actual) # TODO: Remove this step when we implement streams interfaces
126
+ assert_equal 'stream1', actual.keys.first
127
+ assert_stream_entry(actual['stream1'], 'John', 'Connor')
128
+
129
+ # non blocking with hashtag
130
+ actual = redis.xread('COUNT', 1, 'STREAMS', '{stream}1', 0)
131
+ actual = hashify_streams(actual) # TODO: Remove this step when we implement streams interfaces
132
+ assert_equal '{stream}1', actual.keys.first
133
+ assert_stream_entry(actual['{stream}1'], 'John', 'Connor')
134
+
135
+ # blocking with hashtag
136
+ actual = redis.xread('COUNT', 1, 'BLOCK', 1, 'STREAMS', '{stream}1', 0)
137
+ actual = hashify_streams(actual) # TODO: Remove this step when we implement streams interfaces
138
+ assert_equal '{stream}1', actual.keys.first
139
+ assert_stream_entry(actual['{stream}1'], 'John', 'Connor')
140
+ end
141
+ end
142
+
143
+ def test_xreadgroup
144
+ target_version(MIN_REDIS_VERSION) do
145
+ # non blocking without hashtag
146
+ redis.xgroup('create', 'stream1', 'mygroup1', '$')
147
+ add_some_entries_to_streams_without_hashtag
148
+ actual = redis.xreadgroup('GROUP', 'mygroup1', 'T-1000', 'COUNT', 1, 'STREAMS', 'stream1', '>')
149
+ actual = hashify_streams(actual) # TODO: Remove this step when we implement streams interfaces
150
+ assert_equal 'stream1', actual.keys.first
151
+ assert_stream_entry(actual['stream1'], 'John', 'Connor')
152
+
153
+ # blocking without hashtag
154
+ redis.xgroup('create', 'stream1', 'mygroup2', '$')
155
+ add_some_entries_to_streams_without_hashtag
156
+ actual = redis.xreadgroup('GROUP', 'mygroup2', 'T-800', 'COUNT', 1, 'BLOCK', 1, 'STREAMS', 'stream1', '>')
157
+ actual = hashify_streams(actual) # TODO: Remove this step when we implement streams interfaces
158
+ assert_equal 'stream1', actual.keys.first
159
+ assert_stream_entry(actual['stream1'], 'John', 'Connor')
160
+
161
+ # non blocking with hashtag
162
+ redis.xgroup('create', '{stream}1', 'mygroup3', '$')
163
+ add_some_entries_to_streams_with_hashtag
164
+ actual = redis.xreadgroup('GROUP', 'mygroup3', 'T-1000', 'COUNT', 1, 'STREAMS', '{stream}1', '>')
165
+ actual = hashify_streams(actual) # TODO: Remove this step when we implement streams interfaces
166
+ assert_equal '{stream}1', actual.keys.first
167
+ assert_stream_entry(actual['{stream}1'], 'John', 'Connor')
168
+
169
+ # blocking with hashtag
170
+ redis.xgroup('create', '{stream}1', 'mygroup4', '$')
171
+ add_some_entries_to_streams_with_hashtag
172
+ actual = redis.xreadgroup('GROUP', 'mygroup4', 'T-800', 'COUNT', 1, 'BLOCK', 1, 'STREAMS', '{stream}1', '>')
173
+ actual = hashify_streams(actual) # TODO: Remove this step when we implement streams interfaces
174
+ assert_equal '{stream}1', actual.keys.first
175
+ assert_stream_entry(actual['{stream}1'], 'John', 'Connor')
176
+ end
177
+ end
178
+
179
+ def test_xpending
180
+ target_version(MIN_REDIS_VERSION) do
181
+ redis.xgroup('create', 'stream1', 'mygroup1', '$')
182
+ add_some_entries_to_streams_without_hashtag
183
+ redis.xreadgroup('GROUP', 'mygroup1', 'T-800', 'COUNT', 1, 'STREAMS', 'stream1', '>')
184
+ actual = redis.xpending('stream1', 'mygroup1')
185
+ actual = hashify_stream_pendings(actual) # TODO: Remove this step when we implement streams interfaces
186
+ assert_stream_pending(actual, 1, 'T-800', '1')
187
+
188
+ redis.xgroup('create', '{stream}1', 'mygroup2', '$')
189
+ add_some_entries_to_streams_with_hashtag
190
+ redis.xreadgroup('GROUP', 'mygroup2', 'T-800', 'COUNT', 1, 'STREAMS', '{stream}1', '>')
191
+ actual = redis.xpending('{stream}1', 'mygroup2')
192
+ actual = hashify_stream_pendings(actual) # TODO: Remove this step when we implement streams interfaces
193
+ assert_stream_pending(actual, 1, 'T-800', '1')
194
+ end
195
+ end
196
+ end