em-hiredis 0.1.1 → 0.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1,5 +1,5 @@
1
1
  module EventMachine
2
2
  module Hiredis
3
- VERSION = "0.1.1"
3
+ VERSION = "0.2.0"
4
4
  end
5
5
  end
@@ -0,0 +1,116 @@
1
+ require 'spec_helper'
2
+
3
+ describe EM::Hiredis::BaseClient do
4
+ it "should be able to connect to redis (required for all tests!)" do
5
+ em {
6
+ redis = EM::Hiredis.connect
7
+ redis.callback {
8
+ done
9
+ }
10
+ redis.errback {
11
+ puts "CHECK THAT THE REDIS SERVER IS RUNNING ON PORT 6379"
12
+ fail
13
+ }
14
+ }
15
+ end
16
+
17
+ it "should emit an event on reconnect failure, with the retry count" do
18
+ # Assumes there is no redis server on 9999
19
+ connect(1, "redis://localhost:9999/") do |redis|
20
+ expected = 1
21
+ redis.on(:reconnect_failed) { |count|
22
+ count.should == expected
23
+ expected += 1
24
+ done if expected == 3
25
+ }
26
+ end
27
+ end
28
+
29
+ it "should emit disconnected when the connection closes" do
30
+ connect do |redis|
31
+ redis.on(:disconnected) {
32
+ done
33
+ }
34
+ redis.close_connection
35
+ end
36
+ end
37
+
38
+ it "should fail the client deferrable after 4 unsuccessful attempts" do
39
+ connect(1, "redis://localhost:9999/") do |redis|
40
+ events = []
41
+ redis.on(:reconnect_failed) { |count|
42
+ events << count
43
+ }
44
+ redis.errback { |error|
45
+ error.class.should == EM::Hiredis::Error
46
+ error.message.should == 'Could not connect after 4 attempts'
47
+ events.should == [1,2,3,4]
48
+ done
49
+ }
50
+ end
51
+ end
52
+
53
+ it "should fail commands immediately when in failed state" do
54
+ connect(1, "redis://localhost:9999/") do |redis|
55
+ redis.fail
56
+ redis.get('foo').errback { |error|
57
+ error.class.should == EM::Hiredis::Error
58
+ error.message.should == 'Redis connection in failed state'
59
+ done
60
+ }
61
+ end
62
+ end
63
+
64
+ it "should fail queued commands when entering failed state" do
65
+ connect(1, "redis://localhost:9999/") do |redis|
66
+ redis.get('foo').errback { |error|
67
+ error.class.should == EM::Hiredis::Error
68
+ error.message.should == 'Redis connection in failed state'
69
+ done
70
+ }
71
+ redis.fail
72
+ end
73
+ end
74
+
75
+ it "should allow reconfiguring the client at runtime" do
76
+ connect(1, "redis://localhost:9999/") do |redis|
77
+ redis.on(:reconnect_failed) {
78
+ redis.configure("redis://localhost:6379/9")
79
+ redis.info {
80
+ done
81
+ }
82
+ }
83
+ end
84
+ end
85
+
86
+ it "should allow connection to be reconnected" do
87
+ connect do |redis|
88
+ redis.on(:reconnected) {
89
+ done
90
+ }
91
+ # Wait for first connection to complete
92
+ redis.callback {
93
+ redis.reconnect_connection
94
+ }
95
+ end
96
+ end
97
+
98
+ it "should wrap error responses returned by redis" do
99
+ connect do |redis|
100
+ redis.sadd('foo', 'bar') {
101
+ df = redis.get('foo')
102
+ df.callback {
103
+ fail "Should have received error response from redis"
104
+ }
105
+ df.errback { |e|
106
+ e.class.should == EM::Hiredis::RedisError
107
+ e.should be_kind_of(EM::Hiredis::Error)
108
+ e.message.should == 'Error reply from redis (wrapped in redis_error)'
109
+ # This is the wrapped error from redis:
110
+ e.redis_error.message.should == 'ERR Operation against a key holding the wrong kind of value'
111
+ done
112
+ }
113
+ }
114
+ end
115
+ end
116
+ end
@@ -14,7 +14,7 @@ describe EventMachine::Hiredis, "connecting" do
14
14
 
15
15
  def connect_to_mock(url, &blk)
16
16
  redis_mock(replies) do
17
- connect(url, &blk)
17
+ connect(1, url, &blk)
18
18
  end
19
19
  end
20
20
 
@@ -494,7 +494,7 @@ end
494
494
 
495
495
  describe EventMachine::Hiredis, "when reconnecting" do
496
496
  it "select previously selected dataset" do
497
- connect do |redis|
497
+ connect(3) do |redis|
498
498
  #simulate disconnect
499
499
  redis.set('foo', 'a') { redis.close_connection_after_writing }
500
500
 
@@ -0,0 +1,314 @@
1
+ require 'spec_helper'
2
+
3
+ describe EventMachine::Hiredis::PubsubClient, '(un)subscribe' do
4
+ describe "subscribing" do
5
+ it "should return deferrable which succeeds with subscribe call result" do
6
+ connect do |redis|
7
+ df = redis.pubsub.subscribe("channel") { }
8
+ df.should be_kind_of(EventMachine::DefaultDeferrable)
9
+ df.callback { |subscription_count|
10
+ # Subscribe response from redis - indicates that subscription has
11
+ # succeeded and that the current connection has a single
12
+ # subscription
13
+ subscription_count.should == 1
14
+ done
15
+ }
16
+ end
17
+ end
18
+
19
+ it "should run the passed block when message received" do
20
+ connect do |redis|
21
+ redis.pubsub.subscribe("channel") { |message|
22
+ message.should == 'hello'
23
+ done
24
+ }.callback {
25
+ redis.publish('channel', 'hello')
26
+ }
27
+ end
28
+ end
29
+
30
+ it "should run the passed proc when message received on channel" do
31
+ connect do |redis|
32
+ proc = Proc.new { |message|
33
+ message.should == 'hello'
34
+ done
35
+ }
36
+ redis.pubsub.subscribe("channel", proc).callback {
37
+ redis.publish('channel', 'hello')
38
+ }
39
+ end
40
+ end
41
+ end
42
+
43
+ describe "unsubscribing" do
44
+ it "should allow unsubscribing a single callback without unsubscribing from redis" do
45
+ connect do |redis|
46
+ proc1 = Proc.new { |message| fail }
47
+ proc2 = Proc.new { |message|
48
+ message.should == 'hello'
49
+ done
50
+ }
51
+ redis.pubsub.subscribe("channel", proc1)
52
+ redis.pubsub.subscribe("channel", proc2).callback {
53
+ redis.pubsub.unsubscribe_proc("channel", proc1)
54
+ redis.publish("channel", "hello")
55
+ }
56
+ end
57
+ end
58
+
59
+ it "should unsubscribe from redis on last proc unsubscription" do
60
+ connect do |redis|
61
+ proc = Proc.new { |message| }
62
+ redis.pubsub.subscribe("channel", proc).callback { |subs_count|
63
+ subs_count.should == 1
64
+ redis.pubsub.unsubscribe_proc("channel", proc).callback {
65
+ # Slightly awkward way to check that unsubscribe happened:
66
+ redis.pubsub.subscribe('channel2').callback { |count|
67
+ # If count is 1 this implies that channel unsubscribed
68
+ count.should == 1
69
+ done
70
+ }
71
+ }
72
+ }
73
+ end
74
+ end
75
+
76
+ it "should allow unsubscribing from redis channel, including all callbacks, and return deferrable for redis unsubscribe" do
77
+ connect do |redis|
78
+ # Raw pubsub event
79
+ redis.pubsub.on('message') { |channel, message| fail }
80
+ # Block subscription
81
+ redis.pubsub.subscribe("channel") { |m| fail } # block
82
+ # Proc example
83
+ df = redis.pubsub.subscribe("channel", Proc.new { |m| fail })
84
+
85
+ df.callback {
86
+ redis.pubsub.unsubscribe("channel").callback { |remaining_subs|
87
+ remaining_subs.should == 0
88
+ redis.publish("channel", "hello") {
89
+ done
90
+ }
91
+ }
92
+ }
93
+ end
94
+ end
95
+ end
96
+
97
+ it "should expose raw pubsub events from redis" do
98
+ channel = "channel"
99
+ callback_count = 0
100
+ connect do |redis|
101
+ redis.pubsub.on(:subscribe) { |channel, subscription_count|
102
+ # 2. Get subscribe callback
103
+ callback_count += 1
104
+ channel.should == channel
105
+ subscription_count.should == 1
106
+
107
+ # 3. Publish on channel
108
+ redis.publish(channel, 'foo')
109
+ }
110
+
111
+ redis.pubsub.on(:message) { |channel, message|
112
+ # 4. Get message callback
113
+ callback_count += 1
114
+ channel.should == channel
115
+ message.should == 'foo'
116
+
117
+ callback_count.should == 2
118
+ done
119
+ }
120
+
121
+ # 1. Subscribe to channel
122
+ redis.pubsub.subscribe(channel)
123
+ end
124
+ end
125
+
126
+ it "should resubscribe to all channels on reconnect" do
127
+ callback_count = 0
128
+ connect do |redis|
129
+ # 1. Subscribe to channels
130
+ redis.pubsub.subscribe('channel1') {
131
+ callback_count += 1
132
+ }
133
+ redis.pubsub.subscribe('channel2') {
134
+ callback_count += 1
135
+ EM.next_tick {
136
+ # 4. Success if both messages have been received
137
+ callback_count.should == 2
138
+ done
139
+ }
140
+ }.callback { |subscription_count|
141
+ subscription_count.should == 2
142
+ # 2. Subscriptions complete. Now force disconnect
143
+ redis.pubsub.instance_variable_get(:@connection).close_connection
144
+
145
+ EM.add_timer(0.1) {
146
+ # 3. After giving time to reconnect publish to both channels
147
+ redis.publish('channel1', 'foo')
148
+ redis.publish('channel2', 'bar')
149
+ }
150
+
151
+ }
152
+
153
+ end
154
+ end
155
+ end
156
+
157
+ describe EventMachine::Hiredis::PubsubClient, 'p(un)subscribe' do
158
+ describe "psubscribing" do
159
+ it "should return deferrable which succeeds with psubscribe call result" do
160
+ connect do |redis|
161
+ df = redis.pubsub.psubscribe("channel") { }
162
+ df.should be_kind_of(EventMachine::DefaultDeferrable)
163
+ df.callback { |subscription_count|
164
+ # Subscribe response from redis - indicates that subscription has
165
+ # succeeded and that the current connection has a single
166
+ # subscription
167
+ subscription_count.should == 1
168
+ done
169
+ }
170
+ end
171
+ end
172
+
173
+ it "should run the passed block when message received" do
174
+ connect do |redis|
175
+ redis.pubsub.psubscribe("channel:*") { |channel, message|
176
+ channel.should == 'channel:foo'
177
+ message.should == 'hello'
178
+ done
179
+ }.callback {
180
+ redis.publish('channel:foo', 'hello')
181
+ }
182
+ end
183
+ end
184
+
185
+ it "should run the passed proc when message received on channel" do
186
+ connect do |redis|
187
+ proc = Proc.new { |channel, message|
188
+ channel.should == 'channel:foo'
189
+ message.should == 'hello'
190
+ done
191
+ }
192
+ redis.pubsub.psubscribe("channel:*", proc).callback {
193
+ redis.publish('channel:foo', 'hello')
194
+ }
195
+ end
196
+ end
197
+ end
198
+
199
+ describe "punsubscribing" do
200
+ it "should allow punsubscribing a single callback without punsubscribing from redis" do
201
+ connect do |redis|
202
+ proc1 = Proc.new { |channel, message| fail }
203
+ proc2 = Proc.new { |channel, message|
204
+ channel.should == 'channel:foo'
205
+ message.should == 'hello'
206
+ done
207
+ }
208
+ redis.pubsub.psubscribe("channel:*", proc1)
209
+ redis.pubsub.psubscribe("channel:*", proc2).callback {
210
+ redis.pubsub.punsubscribe_proc("channel:*", proc1)
211
+ redis.publish("channel:foo", "hello")
212
+ }
213
+ end
214
+ end
215
+
216
+ it "should punsubscribe from redis on last proc punsubscription" do
217
+ connect do |redis|
218
+ proc = Proc.new { |message| }
219
+ redis.pubsub.psubscribe("channel:*", proc).callback { |subs_count|
220
+ subs_count.should == 1
221
+ redis.pubsub.punsubscribe_proc("channel:*", proc).callback {
222
+ # Slightly awkward way to check that unsubscribe happened:
223
+ redis.pubsub.psubscribe('channel2').callback { |count|
224
+ # If count is 1 this implies that channel unsubscribed
225
+ count.should == 1
226
+ done
227
+ }
228
+ }
229
+ }
230
+ end
231
+ end
232
+
233
+ it "should allow punsubscribing from redis channel, including all callbacks, and return deferrable for redis punsubscribe" do
234
+ connect do |redis|
235
+ # Raw pubsub event
236
+ redis.pubsub.on('pmessage') { |pattern, channel, message| fail }
237
+ # Block subscription
238
+ redis.pubsub.psubscribe("channel") { |c, m| fail } # block
239
+ # Proc example
240
+ df = redis.pubsub.psubscribe("channel", Proc.new { |c, m| fail })
241
+
242
+ df.callback {
243
+ redis.pubsub.punsubscribe("channel").callback { |remaining_subs|
244
+ remaining_subs.should == 0
245
+ redis.publish("channel", "hello") {
246
+ done
247
+ }
248
+ }
249
+ }
250
+ end
251
+ end
252
+ end
253
+
254
+ it "should expose raw pattern pubsub events from redis" do
255
+ callback_count = 0
256
+ connect do |redis|
257
+ redis.pubsub.on(:psubscribe) { |pattern, subscription_count|
258
+ # 2. Get subscribe callback
259
+ callback_count += 1
260
+ pattern.should == "channel:*"
261
+ subscription_count.should == 1
262
+
263
+ # 3. Publish on channel
264
+ redis.publish('channel:foo', 'foo')
265
+ }
266
+
267
+ redis.pubsub.on(:pmessage) { |pattern, channel, message|
268
+ # 4. Get message callback
269
+ callback_count += 1
270
+ pattern.should == 'channel:*'
271
+ channel.should == 'channel:foo'
272
+ message.should == 'foo'
273
+
274
+ callback_count.should == 2
275
+ done
276
+ }
277
+
278
+ # 1. Subscribe to channel
279
+ redis.pubsub.psubscribe('channel:*')
280
+ end
281
+ end
282
+
283
+ it "should resubscribe to all pattern subscriptions on reconnect" do
284
+ callback_count = 0
285
+ connect do |redis|
286
+ # 1. Subscribe to channels
287
+ redis.pubsub.psubscribe('foo:*') { |channel, message|
288
+ channel.should == 'foo:a'
289
+ message.should == 'hello foo'
290
+ callback_count += 1
291
+ }
292
+ redis.pubsub.psubscribe('bar:*') { |channel, message|
293
+ channel.should == 'bar:b'
294
+ message.should == 'hello bar'
295
+ callback_count += 1
296
+ EM.next_tick {
297
+ # 4. Success if both messages have been received
298
+ callback_count.should == 2
299
+ done
300
+ }
301
+ }.callback { |subscription_count|
302
+ subscription_count.should == 2
303
+ # 2. Subscriptions complete. Now force disconnect
304
+ redis.pubsub.instance_variable_get(:@connection).close_connection
305
+
306
+ EM.add_timer(0.1) {
307
+ # 3. After giving time to reconnect publish to both channels
308
+ redis.publish('foo:a', 'hello foo')
309
+ redis.publish('bar:b', 'hello bar')
310
+ }
311
+ }
312
+ end
313
+ end
314
+ end