gilmour-em-hiredis 0.3.0

Sign up to get free protection for your applications and to get access to all the features.
data/spec/lock_spec.rb ADDED
@@ -0,0 +1,137 @@
1
+ require 'spec_helper'
2
+
3
+ describe EventMachine::Hiredis::Lock do
4
+
5
+ def start(timeout = 1)
6
+ connect(timeout) do |redis|
7
+ @redis = redis
8
+ yield
9
+ end
10
+ end
11
+
12
+ def new_lock
13
+ EventMachine::Hiredis::Lock.new(@redis, "test-lock", 2)
14
+ end
15
+
16
+ it "can be acquired" do
17
+ start {
18
+ new_lock.acquire.callback {
19
+ done
20
+ }.errback { |e|
21
+ fail e
22
+ }
23
+ }
24
+ end
25
+
26
+ it "is re-entrant" do
27
+ start {
28
+ lock = new_lock
29
+ lock.acquire.callback {
30
+ lock.acquire.callback {
31
+ done
32
+ }.errback { |e|
33
+ fail e
34
+ }
35
+ }.errback { |e|
36
+ fail e
37
+ }
38
+ }
39
+ end
40
+
41
+ it "is exclusive" do
42
+ start {
43
+ new_lock.acquire.callback {
44
+ new_lock.acquire.errback {
45
+ done
46
+ }.callback {
47
+ fail "Should not be able to acquire lock from different client"
48
+ }
49
+ }.errback { |e|
50
+ fail e
51
+ }
52
+ }
53
+ end
54
+
55
+ it "can be released and taken by another instance" do
56
+ start {
57
+ lock = new_lock
58
+ lock.acquire.callback {
59
+ lock.unlock.callback {
60
+ new_lock.acquire.callback {
61
+ done
62
+ }.errback { |e|
63
+ fail e
64
+ }
65
+ }.errback { |e|
66
+ fail e
67
+ }
68
+ }.errback { |e|
69
+ fail e
70
+ }
71
+ }
72
+ end
73
+
74
+ it "times out" do
75
+ start(3) {
76
+ new_lock.acquire.callback {
77
+ EM.add_timer(2) {
78
+ new_lock.acquire.callback {
79
+ done
80
+ }.errback { |e|
81
+ fail e
82
+ }
83
+ }
84
+ }.errback { |e|
85
+ fail e
86
+ }
87
+ }
88
+ end
89
+
90
+ it "extends timeout on re-entry" do
91
+ start(4) {
92
+ lock = new_lock
93
+ lock.acquire.callback {
94
+ EM.add_timer(1) {
95
+ lock.acquire.callback {
96
+ EM.add_timer(1.5) {
97
+ # Check it's still locked by initial instance
98
+ new_lock.acquire.errback {
99
+ done
100
+ }.callback { |e|
101
+ fail e
102
+ }
103
+ }
104
+ }.errback { |e|
105
+ fail e
106
+ }
107
+ }
108
+ }.errback { |e|
109
+ fail e
110
+ }
111
+ }
112
+ end
113
+
114
+ it "fails to release if it has not been taken" do
115
+ start {
116
+ new_lock.unlock.errback {
117
+ done
118
+ }.callback {
119
+ fail "Released lock which had not been taken"
120
+ }
121
+ }
122
+ end
123
+
124
+ it "fails to release if taken by another instance" do
125
+ start {
126
+ new_lock.acquire.callback {
127
+ new_lock.unlock.errback {
128
+ done
129
+ }.callback {
130
+ fail "Released lock belonging to another instance"
131
+ }
132
+ }.errback { |e|
133
+ fail e
134
+ }
135
+ }
136
+ end
137
+ end
@@ -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_client.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_client.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_client.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_client.subscribe("channel", proc1)
52
+ redis.pubsub_client.subscribe("channel", proc2).callback {
53
+ redis.pubsub_client.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_client.subscribe("channel", proc).callback { |subs_count|
63
+ subs_count.should == 1
64
+ redis.pubsub_client.unsubscribe_proc("channel", proc).callback {
65
+ # Slightly awkward way to check that unsubscribe happened:
66
+ redis.pubsub_client.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_client.on('message') { |channel, message| fail }
80
+ # Block subscription
81
+ redis.pubsub_client.subscribe("channel") { |m| fail } # block
82
+ # Proc example
83
+ df = redis.pubsub_client.subscribe("channel", Proc.new { |m| fail })
84
+
85
+ df.callback {
86
+ redis.pubsub_client.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_client.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_client.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_client.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_client.subscribe('channel1') {
131
+ callback_count += 1
132
+ }
133
+ redis.pubsub_client.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_client.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_client.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_client.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_client.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_client.psubscribe("channel:*", proc1)
209
+ redis.pubsub_client.psubscribe("channel:*", proc2).callback {
210
+ redis.pubsub_client.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_client.psubscribe("channel:*", proc).callback { |subs_count|
220
+ subs_count.should == 1
221
+ redis.pubsub_client.punsubscribe_proc("channel:*", proc).callback {
222
+ # Slightly awkward way to check that unsubscribe happened:
223
+ redis.pubsub_client.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_client.on('pmessage') { |pattern, channel, message| fail }
237
+ # Block subscription
238
+ redis.pubsub_client.psubscribe("channel") { |c, m| fail } # block
239
+ # Proc example
240
+ df = redis.pubsub_client.psubscribe("channel", Proc.new { |c, m| fail })
241
+
242
+ df.callback {
243
+ redis.pubsub_client.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_client.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_client.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_client.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_client.psubscribe('foo:*') { |channel, message|
288
+ channel.should == 'foo:a'
289
+ message.should == 'hello foo'
290
+ callback_count += 1
291
+ }
292
+ redis.pubsub_client.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_client.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