em-hiredis 0.1.1 → 0.2.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -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