gilmour-em-hiredis 0.3.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.
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