discourse-redis 3.2.2

Sign up to get free protection for your applications and to get access to all the features.
Files changed (107) hide show
  1. checksums.yaml +7 -0
  2. data/.gitignore +16 -0
  3. data/.travis.yml +59 -0
  4. data/.travis/Gemfile +11 -0
  5. data/.yardopts +3 -0
  6. data/CHANGELOG.md +349 -0
  7. data/Gemfile +4 -0
  8. data/LICENSE +20 -0
  9. data/README.md +328 -0
  10. data/Rakefile +87 -0
  11. data/benchmarking/logging.rb +71 -0
  12. data/benchmarking/pipeline.rb +51 -0
  13. data/benchmarking/speed.rb +21 -0
  14. data/benchmarking/suite.rb +24 -0
  15. data/benchmarking/worker.rb +71 -0
  16. data/examples/basic.rb +15 -0
  17. data/examples/consistency.rb +114 -0
  18. data/examples/dist_redis.rb +43 -0
  19. data/examples/incr-decr.rb +17 -0
  20. data/examples/list.rb +26 -0
  21. data/examples/pubsub.rb +37 -0
  22. data/examples/sentinel.rb +41 -0
  23. data/examples/sentinel/start +49 -0
  24. data/examples/sets.rb +36 -0
  25. data/examples/unicorn/config.ru +3 -0
  26. data/examples/unicorn/unicorn.rb +20 -0
  27. data/lib/redis.rb +2731 -0
  28. data/lib/redis/client.rb +575 -0
  29. data/lib/redis/connection.rb +9 -0
  30. data/lib/redis/connection/command_helper.rb +44 -0
  31. data/lib/redis/connection/hiredis.rb +64 -0
  32. data/lib/redis/connection/registry.rb +12 -0
  33. data/lib/redis/connection/ruby.rb +322 -0
  34. data/lib/redis/connection/synchrony.rb +124 -0
  35. data/lib/redis/distributed.rb +873 -0
  36. data/lib/redis/errors.rb +40 -0
  37. data/lib/redis/hash_ring.rb +132 -0
  38. data/lib/redis/pipeline.rb +141 -0
  39. data/lib/redis/subscribe.rb +83 -0
  40. data/lib/redis/version.rb +3 -0
  41. data/redis.gemspec +34 -0
  42. data/test/bitpos_test.rb +69 -0
  43. data/test/blocking_commands_test.rb +42 -0
  44. data/test/command_map_test.rb +30 -0
  45. data/test/commands_on_hashes_test.rb +21 -0
  46. data/test/commands_on_hyper_log_log_test.rb +21 -0
  47. data/test/commands_on_lists_test.rb +20 -0
  48. data/test/commands_on_sets_test.rb +77 -0
  49. data/test/commands_on_sorted_sets_test.rb +137 -0
  50. data/test/commands_on_strings_test.rb +101 -0
  51. data/test/commands_on_value_types_test.rb +133 -0
  52. data/test/connection_handling_test.rb +250 -0
  53. data/test/distributed_blocking_commands_test.rb +46 -0
  54. data/test/distributed_commands_on_hashes_test.rb +10 -0
  55. data/test/distributed_commands_on_hyper_log_log_test.rb +33 -0
  56. data/test/distributed_commands_on_lists_test.rb +22 -0
  57. data/test/distributed_commands_on_sets_test.rb +83 -0
  58. data/test/distributed_commands_on_sorted_sets_test.rb +18 -0
  59. data/test/distributed_commands_on_strings_test.rb +59 -0
  60. data/test/distributed_commands_on_value_types_test.rb +95 -0
  61. data/test/distributed_commands_requiring_clustering_test.rb +164 -0
  62. data/test/distributed_connection_handling_test.rb +23 -0
  63. data/test/distributed_internals_test.rb +79 -0
  64. data/test/distributed_key_tags_test.rb +52 -0
  65. data/test/distributed_persistence_control_commands_test.rb +26 -0
  66. data/test/distributed_publish_subscribe_test.rb +92 -0
  67. data/test/distributed_remote_server_control_commands_test.rb +66 -0
  68. data/test/distributed_scripting_test.rb +102 -0
  69. data/test/distributed_sorting_test.rb +20 -0
  70. data/test/distributed_test.rb +58 -0
  71. data/test/distributed_transactions_test.rb +32 -0
  72. data/test/encoding_test.rb +18 -0
  73. data/test/error_replies_test.rb +59 -0
  74. data/test/fork_safety_test.rb +65 -0
  75. data/test/helper.rb +232 -0
  76. data/test/helper_test.rb +24 -0
  77. data/test/internals_test.rb +437 -0
  78. data/test/lint/blocking_commands.rb +150 -0
  79. data/test/lint/hashes.rb +162 -0
  80. data/test/lint/hyper_log_log.rb +60 -0
  81. data/test/lint/lists.rb +143 -0
  82. data/test/lint/sets.rb +125 -0
  83. data/test/lint/sorted_sets.rb +316 -0
  84. data/test/lint/strings.rb +260 -0
  85. data/test/lint/value_types.rb +122 -0
  86. data/test/persistence_control_commands_test.rb +26 -0
  87. data/test/pipelining_commands_test.rb +242 -0
  88. data/test/publish_subscribe_test.rb +254 -0
  89. data/test/remote_server_control_commands_test.rb +118 -0
  90. data/test/scanning_test.rb +413 -0
  91. data/test/scripting_test.rb +78 -0
  92. data/test/sentinel_command_test.rb +80 -0
  93. data/test/sentinel_test.rb +255 -0
  94. data/test/sorting_test.rb +59 -0
  95. data/test/support/connection/hiredis.rb +1 -0
  96. data/test/support/connection/ruby.rb +1 -0
  97. data/test/support/connection/synchrony.rb +17 -0
  98. data/test/support/redis_mock.rb +119 -0
  99. data/test/support/wire/synchrony.rb +24 -0
  100. data/test/support/wire/thread.rb +5 -0
  101. data/test/synchrony_driver.rb +88 -0
  102. data/test/test.conf.erb +9 -0
  103. data/test/thread_safety_test.rb +32 -0
  104. data/test/transactions_test.rb +264 -0
  105. data/test/unknown_commands_test.rb +14 -0
  106. data/test/url_param_test.rb +138 -0
  107. metadata +182 -0
@@ -0,0 +1,122 @@
1
+ module Lint
2
+
3
+ module ValueTypes
4
+
5
+ def test_exists
6
+ assert_equal false, r.exists("foo")
7
+
8
+ r.set("foo", "s1")
9
+
10
+ assert_equal true, r.exists("foo")
11
+ end
12
+
13
+ def test_type
14
+ assert_equal "none", r.type("foo")
15
+
16
+ r.set("foo", "s1")
17
+
18
+ assert_equal "string", r.type("foo")
19
+ end
20
+
21
+ def test_keys
22
+ r.set("f", "s1")
23
+ r.set("fo", "s2")
24
+ r.set("foo", "s3")
25
+
26
+ assert_equal ["f","fo", "foo"], r.keys("f*").sort
27
+ end
28
+
29
+ def test_expire
30
+ r.set("foo", "s1")
31
+ assert r.expire("foo", 2)
32
+ assert_in_range 0..2, r.ttl("foo")
33
+ end
34
+
35
+ def test_pexpire
36
+ target_version "2.5.4" do
37
+ r.set("foo", "s1")
38
+ assert r.pexpire("foo", 2000)
39
+ assert_in_range 0..2, r.ttl("foo")
40
+ end
41
+ end
42
+
43
+ def test_expireat
44
+ r.set("foo", "s1")
45
+ assert r.expireat("foo", (Time.now + 2).to_i)
46
+ assert_in_range 0..2, r.ttl("foo")
47
+ end
48
+
49
+ def test_pexpireat
50
+ target_version "2.5.4" do
51
+ r.set("foo", "s1")
52
+ assert r.pexpireat("foo", (Time.now + 2).to_i * 1_000)
53
+ assert_in_range 0..2, r.ttl("foo")
54
+ end
55
+ end
56
+
57
+ def test_persist
58
+ r.set("foo", "s1")
59
+ r.expire("foo", 1)
60
+ r.persist("foo")
61
+
62
+ assert(-1 == r.ttl("foo"))
63
+ end
64
+
65
+ def test_ttl
66
+ r.set("foo", "s1")
67
+ r.expire("foo", 2)
68
+ assert_in_range 0..2, r.ttl("foo")
69
+ end
70
+
71
+ def test_pttl
72
+ target_version "2.5.4" do
73
+ r.set("foo", "s1")
74
+ r.expire("foo", 2)
75
+ assert_in_range 1..2000, r.pttl("foo")
76
+ end
77
+ end
78
+
79
+ def test_dump_and_restore
80
+ target_version "2.5.7" do
81
+ r.set("foo", "a")
82
+ v = r.dump("foo")
83
+ r.del("foo")
84
+
85
+ assert r.restore("foo", 1000, v)
86
+ assert_equal "a", r.get("foo")
87
+ assert [0, 1].include? r.ttl("foo")
88
+
89
+ r.rpush("bar", ["b", "c", "d"])
90
+ w = r.dump("bar")
91
+ r.del("bar")
92
+
93
+ assert r.restore("bar", 1000, w)
94
+ assert_equal ["b", "c", "d"], r.lrange("bar", 0, -1)
95
+ assert [0, 1].include? r.ttl("bar")
96
+ end
97
+ end
98
+
99
+ def test_move
100
+ r.select 14
101
+ r.flushdb
102
+
103
+ r.set "bar", "s3"
104
+
105
+ r.select 15
106
+
107
+ r.set "foo", "s1"
108
+ r.set "bar", "s2"
109
+
110
+ assert r.move("foo", 14)
111
+ assert_equal nil, r.get("foo")
112
+
113
+ assert !r.move("bar", 14)
114
+ assert_equal "s2", r.get("bar")
115
+
116
+ r.select 14
117
+
118
+ assert_equal "s1", r.get("foo")
119
+ assert_equal "s3", r.get("bar")
120
+ end
121
+ end
122
+ end
@@ -0,0 +1,26 @@
1
+ # encoding: UTF-8
2
+
3
+ require File.expand_path("helper", File.dirname(__FILE__))
4
+
5
+ class TestPersistenceControlCommands < Test::Unit::TestCase
6
+
7
+ include Helper::Client
8
+
9
+ def test_save
10
+ redis_mock(:save => lambda { "+SAVE" }) do |redis|
11
+ assert_equal "SAVE", redis.save
12
+ end
13
+ end
14
+
15
+ def test_bgsave
16
+ redis_mock(:bgsave => lambda { "+BGSAVE" }) do |redis|
17
+ assert_equal "BGSAVE", redis.bgsave
18
+ end
19
+ end
20
+
21
+ def test_lastsave
22
+ redis_mock(:lastsave => lambda { "+LASTSAVE" }) do |redis|
23
+ assert_equal "LASTSAVE", redis.lastsave
24
+ end
25
+ end
26
+ end
@@ -0,0 +1,242 @@
1
+ # encoding: UTF-8
2
+
3
+ require File.expand_path("helper", File.dirname(__FILE__))
4
+
5
+ class TestPipeliningCommands < Test::Unit::TestCase
6
+
7
+ include Helper::Client
8
+
9
+ def test_bulk_commands
10
+ r.pipelined do
11
+ r.lpush "foo", "s1"
12
+ r.lpush "foo", "s2"
13
+ end
14
+
15
+ assert_equal 2, r.llen("foo")
16
+ assert_equal "s2", r.lpop("foo")
17
+ assert_equal "s1", r.lpop("foo")
18
+ end
19
+
20
+ def test_multi_bulk_commands
21
+ r.pipelined do
22
+ r.mset("foo", "s1", "bar", "s2")
23
+ r.mset("baz", "s3", "qux", "s4")
24
+ end
25
+
26
+ assert_equal "s1", r.get("foo")
27
+ assert_equal "s2", r.get("bar")
28
+ assert_equal "s3", r.get("baz")
29
+ assert_equal "s4", r.get("qux")
30
+ end
31
+
32
+ def test_bulk_and_multi_bulk_commands_mixed
33
+ r.pipelined do
34
+ r.lpush "foo", "s1"
35
+ r.lpush "foo", "s2"
36
+ r.mset("baz", "s3", "qux", "s4")
37
+ end
38
+
39
+ assert_equal 2, r.llen("foo")
40
+ assert_equal "s2", r.lpop("foo")
41
+ assert_equal "s1", r.lpop("foo")
42
+ assert_equal "s3", r.get("baz")
43
+ assert_equal "s4", r.get("qux")
44
+ end
45
+
46
+ def test_multi_bulk_and_bulk_commands_mixed
47
+ r.pipelined do
48
+ r.mset("baz", "s3", "qux", "s4")
49
+ r.lpush "foo", "s1"
50
+ r.lpush "foo", "s2"
51
+ end
52
+
53
+ assert_equal 2, r.llen("foo")
54
+ assert_equal "s2", r.lpop("foo")
55
+ assert_equal "s1", r.lpop("foo")
56
+ assert_equal "s3", r.get("baz")
57
+ assert_equal "s4", r.get("qux")
58
+ end
59
+
60
+ def test_pipelined_with_an_empty_block
61
+ assert_nothing_raised do
62
+ r.pipelined do
63
+ end
64
+ end
65
+
66
+ assert_equal 0, r.dbsize
67
+ end
68
+
69
+ def test_returning_the_result_of_a_pipeline
70
+ result = r.pipelined do
71
+ r.set "foo", "bar"
72
+ r.get "foo"
73
+ r.get "bar"
74
+ end
75
+
76
+ assert_equal ["OK", "bar", nil], result
77
+ end
78
+
79
+ def test_assignment_of_results_inside_the_block
80
+ r.pipelined do
81
+ @first = r.sadd("foo", 1)
82
+ @second = r.sadd("foo", 1)
83
+ end
84
+
85
+ assert_equal true, @first.value
86
+ assert_equal false, @second.value
87
+ end
88
+
89
+ # Although we could support accessing the values in these futures,
90
+ # it doesn't make a lot of sense.
91
+ def test_assignment_of_results_inside_the_block_with_errors
92
+ assert_raise(Redis::CommandError) do
93
+ r.pipelined do
94
+ r.doesnt_exist
95
+ @first = r.sadd("foo", 1)
96
+ @second = r.sadd("foo", 1)
97
+ end
98
+ end
99
+
100
+ assert_raise(Redis::FutureNotReady) { @first.value }
101
+ assert_raise(Redis::FutureNotReady) { @second.value }
102
+ end
103
+
104
+ def test_assignment_of_results_inside_a_nested_block
105
+ r.pipelined do
106
+ @first = r.sadd("foo", 1)
107
+
108
+ r.pipelined do
109
+ @second = r.sadd("foo", 1)
110
+ end
111
+ end
112
+
113
+ assert_equal true, @first.value
114
+ assert_equal false, @second.value
115
+ end
116
+
117
+ def test_futures_raise_when_confused_with_something_else
118
+ r.pipelined do
119
+ @result = r.sadd("foo", 1)
120
+ end
121
+
122
+ assert_raise(NoMethodError) { @result.to_s }
123
+ end
124
+
125
+ def test_futures_raise_when_trying_to_access_their_values_too_early
126
+ r.pipelined do
127
+ assert_raise(Redis::FutureNotReady) do
128
+ r.sadd("foo", 1).value
129
+ end
130
+ end
131
+ end
132
+
133
+ def test_futures_can_be_identified
134
+ r.pipelined do
135
+ @result = r.sadd("foo", 1)
136
+ end
137
+
138
+ assert_equal true, @result.is_a?(Redis::Future)
139
+ if defined?(::BasicObject)
140
+ assert_equal true, @result.is_a?(::BasicObject)
141
+ end
142
+ assert_equal Redis::Future, @result.class
143
+ end
144
+
145
+ def test_returning_the_result_of_an_empty_pipeline
146
+ result = r.pipelined do
147
+ end
148
+
149
+ assert_equal [], result
150
+ end
151
+
152
+ def test_nesting_pipeline_blocks
153
+ r.pipelined do
154
+ r.set("foo", "s1")
155
+ r.pipelined do
156
+ r.set("bar", "s2")
157
+ end
158
+ end
159
+
160
+ assert_equal "s1", r.get("foo")
161
+ assert_equal "s2", r.get("bar")
162
+ end
163
+
164
+ def test_info_in_a_pipeline_returns_hash
165
+ result = r.pipelined do
166
+ r.info
167
+ end
168
+
169
+ assert result.first.kind_of?(Hash)
170
+ end
171
+
172
+ def test_config_get_in_a_pipeline_returns_hash
173
+ result = r.pipelined do
174
+ r.config(:get, "*")
175
+ end
176
+
177
+ assert result.first.kind_of?(Hash)
178
+ end
179
+
180
+ def test_hgetall_in_a_pipeline_returns_hash
181
+ r.hmset("hash", "field", "value")
182
+ result = r.pipelined do
183
+ r.hgetall("hash")
184
+ end
185
+
186
+ assert_equal result.first, { "field" => "value" }
187
+ end
188
+
189
+ def test_keys_in_a_pipeline
190
+ r.set("key", "value")
191
+ result = r.pipelined do
192
+ r.keys("*")
193
+ end
194
+
195
+ assert_equal ["key"], result.first
196
+ end
197
+
198
+ def test_pipeline_yields_a_connection
199
+ r.pipelined do |p|
200
+ p.set("foo", "bar")
201
+ end
202
+
203
+ assert_equal "bar", r.get("foo")
204
+ end
205
+
206
+ def test_pipeline_select
207
+ r.select 1
208
+ r.set("db", "1")
209
+
210
+ r.pipelined do |p|
211
+ p.select 2
212
+ p.set("db", "2")
213
+ end
214
+
215
+ r.select 1
216
+ assert_equal "1", r.get("db")
217
+
218
+ r.select 2
219
+ assert_equal "2", r.get("db")
220
+ end
221
+
222
+ def test_pipeline_select_client_db
223
+ r.select 1
224
+ r.pipelined do |p2|
225
+ p2.select 2
226
+ end
227
+
228
+ assert_equal 2, r.client.db
229
+ end
230
+
231
+ def test_nested_pipeline_select_client_db
232
+ r.select 1
233
+ r.pipelined do |p2|
234
+ p2.select 2
235
+ p2.pipelined do |p3|
236
+ p3.select 3
237
+ end
238
+ end
239
+
240
+ assert_equal 3, r.client.db
241
+ end
242
+ end
@@ -0,0 +1,254 @@
1
+ # encoding: UTF-8
2
+
3
+ require File.expand_path("helper", File.dirname(__FILE__))
4
+
5
+ class TestPublishSubscribe < Test::Unit::TestCase
6
+
7
+ include Helper::Client
8
+
9
+ class TestError < StandardError
10
+ end
11
+
12
+ def test_subscribe_and_unsubscribe
13
+ @subscribed = false
14
+ @unsubscribed = false
15
+
16
+ wire = Wire.new do
17
+ r.subscribe("foo") do |on|
18
+ on.subscribe do |channel, total|
19
+ @subscribed = true
20
+ @t1 = total
21
+ end
22
+
23
+ on.message do |channel, message|
24
+ if message == "s1"
25
+ r.unsubscribe
26
+ @message = message
27
+ end
28
+ end
29
+
30
+ on.unsubscribe do |channel, total|
31
+ @unsubscribed = true
32
+ @t2 = total
33
+ end
34
+ end
35
+ end
36
+
37
+ # Wait until the subscription is active before publishing
38
+ Wire.pass while !@subscribed
39
+
40
+ Redis.new(OPTIONS).publish("foo", "s1")
41
+
42
+ wire.join
43
+
44
+ assert @subscribed
45
+ assert_equal 1, @t1
46
+ assert @unsubscribed
47
+ assert_equal 0, @t2
48
+ assert_equal "s1", @message
49
+ end
50
+
51
+ def test_psubscribe_and_punsubscribe
52
+ @subscribed = false
53
+ @unsubscribed = false
54
+
55
+ wire = Wire.new do
56
+ r.psubscribe("f*") do |on|
57
+ on.psubscribe do |pattern, total|
58
+ @subscribed = true
59
+ @t1 = total
60
+ end
61
+
62
+ on.pmessage do |pattern, channel, message|
63
+ if message == "s1"
64
+ r.punsubscribe
65
+ @message = message
66
+ end
67
+ end
68
+
69
+ on.punsubscribe do |pattern, total|
70
+ @unsubscribed = true
71
+ @t2 = total
72
+ end
73
+ end
74
+ end
75
+
76
+ # Wait until the subscription is active before publishing
77
+ Wire.pass while !@subscribed
78
+
79
+ Redis.new(OPTIONS).publish("foo", "s1")
80
+
81
+ wire.join
82
+
83
+ assert @subscribed
84
+ assert_equal 1, @t1
85
+ assert @unsubscribed
86
+ assert_equal 0, @t2
87
+ assert_equal "s1", @message
88
+ end
89
+
90
+ def test_pubsub_with_numpat_subcommand
91
+ target_version("2.8.0") do
92
+ @subscribed = false
93
+ wire = Wire.new do
94
+ r.psubscribe("f*") do |on|
95
+ on.psubscribe { |channel, total| @subscribed = true }
96
+ on.pmessage { |pattern, channel, message| r.punsubscribe }
97
+ end
98
+ end
99
+ Wire.pass while !@subscribed
100
+ redis = Redis.new(OPTIONS)
101
+ numpat_result = redis.pubsub(:numpat)
102
+
103
+ redis.publish("foo", "s1")
104
+ wire.join
105
+
106
+ assert_equal redis.pubsub(:numpat), 0
107
+ assert_equal numpat_result, 1
108
+ end
109
+ end
110
+
111
+
112
+ def test_pubsub_with_channels_and_numsub_subcommnads
113
+ target_version("2.8.0") do
114
+ @subscribed = false
115
+ wire = Wire.new do
116
+ r.subscribe("foo") do |on|
117
+ on.subscribe { |channel, total| @subscribed = true }
118
+ on.message { |channel, message| r.unsubscribe }
119
+ end
120
+ end
121
+ Wire.pass while !@subscribed
122
+ redis = Redis.new(OPTIONS)
123
+ channels_result = redis.pubsub(:channels)
124
+ numsub_result = redis.pubsub(:numsub, 'foo', 'boo')
125
+
126
+ redis.publish("foo", "s1")
127
+ wire.join
128
+
129
+ assert_equal channels_result, ['foo']
130
+ assert_equal numsub_result, ['foo', 1, 'boo', 0]
131
+ end
132
+ end
133
+
134
+ def test_subscribe_connection_usable_after_raise
135
+ @subscribed = false
136
+
137
+ wire = Wire.new do
138
+ begin
139
+ r.subscribe("foo") do |on|
140
+ on.subscribe do |channel, total|
141
+ @subscribed = true
142
+ end
143
+
144
+ on.message do |channel, message|
145
+ raise TestError
146
+ end
147
+ end
148
+ rescue TestError
149
+ end
150
+ end
151
+
152
+ # Wait until the subscription is active before publishing
153
+ Wire.pass while !@subscribed
154
+
155
+ Redis.new(OPTIONS).publish("foo", "s1")
156
+
157
+ wire.join
158
+
159
+ assert_equal "PONG", r.ping
160
+ end
161
+
162
+ def test_psubscribe_connection_usable_after_raise
163
+ @subscribed = false
164
+
165
+ wire = Wire.new do
166
+ begin
167
+ r.psubscribe("f*") do |on|
168
+ on.psubscribe do |pattern, total|
169
+ @subscribed = true
170
+ end
171
+
172
+ on.pmessage do |pattern, channel, message|
173
+ raise TestError
174
+ end
175
+ end
176
+ rescue TestError
177
+ end
178
+ end
179
+
180
+ # Wait until the subscription is active before publishing
181
+ Wire.pass while !@subscribed
182
+
183
+ Redis.new(OPTIONS).publish("foo", "s1")
184
+
185
+ wire.join
186
+
187
+ assert_equal "PONG", r.ping
188
+ end
189
+
190
+ def test_subscribe_within_subscribe
191
+ @channels = []
192
+
193
+ wire = Wire.new do
194
+ r.subscribe("foo") do |on|
195
+ on.subscribe do |channel, total|
196
+ @channels << channel
197
+
198
+ r.subscribe("bar") if channel == "foo"
199
+ r.unsubscribe if channel == "bar"
200
+ end
201
+ end
202
+ end
203
+
204
+ wire.join
205
+
206
+ assert_equal ["foo", "bar"], @channels
207
+ end
208
+
209
+ def test_other_commands_within_a_subscribe
210
+ assert_raise Redis::CommandError do
211
+ r.subscribe("foo") do |on|
212
+ on.subscribe do |channel, total|
213
+ r.set("bar", "s2")
214
+ end
215
+ end
216
+ end
217
+ end
218
+
219
+ def test_subscribe_without_a_block
220
+ assert_raise LocalJumpError do
221
+ r.subscribe("foo")
222
+ end
223
+ end
224
+
225
+ def test_unsubscribe_without_a_subscribe
226
+ assert_raise RuntimeError do
227
+ r.unsubscribe
228
+ end
229
+
230
+ assert_raise RuntimeError do
231
+ r.punsubscribe
232
+ end
233
+ end
234
+
235
+ def test_subscribe_past_a_timeout
236
+ # For some reason, a thread here doesn't reproduce the issue.
237
+ sleep = %{sleep #{OPTIONS[:timeout] * 2}}
238
+ publish = %{ruby -rsocket -e 't=TCPSocket.new("127.0.0.1",#{OPTIONS[:port]});t.write("publish foo bar\\r\\n");t.read(4);t.close'}
239
+ cmd = [sleep, publish].join("; ")
240
+
241
+ IO.popen(cmd, "r+") do |pipe|
242
+ received = false
243
+
244
+ r.subscribe "foo" do |on|
245
+ on.message do |channel, message|
246
+ received = true
247
+ r.unsubscribe
248
+ end
249
+ end
250
+
251
+ assert received
252
+ end
253
+ end
254
+ end