who_can 0.3.4

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.
@@ -0,0 +1,260 @@
1
+ require 'spec_helper'
2
+
3
+ module WhoCan
4
+ describe 'ConnectionManager' do
5
+ include EventedSpec::EMSpec
6
+ default_timeout 3.0
7
+
8
+ em_before do
9
+ @connection1 = ConnectionWrapper.new("amqp://localhost")
10
+ @connection2 = ConnectionWrapper.new("amqp://localhost")
11
+ @connection_manager = ConnectionManager.new(@connection1, @connection2)
12
+ end
13
+
14
+ def clean_and_done
15
+ # logger.debug {"calling test done on next tick"}
16
+ EM.next_tick do
17
+ # logger.debug {"test done, closing"}
18
+ @connection_manager.close do
19
+ done
20
+ end
21
+ end
22
+ end
23
+
24
+ def neuter_connection(connection)
25
+ flexmock(connection).should_receive(:connect).and_return true
26
+ end
27
+
28
+
29
+ describe "under totally normal circumstances" do
30
+
31
+ it "should enter a usable state after calling start" do
32
+ @connection_manager.start do
33
+ # logger.debug {"we DID get the start callback"}
34
+ @connection_manager.should be_usable
35
+ clean_and_done
36
+ end
37
+ end
38
+
39
+ it "should enter the degraded state if #2 never starts" do
40
+ neuter_connection(@connection2)
41
+ @connection_manager.start do
42
+ # logger.debug {"we DID get the start callback for #2 not starting"}
43
+ @connection_manager.should be_degraded
44
+ clean_and_done
45
+ end
46
+ end
47
+
48
+ it "should be useable after grace period if only secondary starts up" do
49
+ neuter_connection(@connection1)
50
+ @connection_manager.primary_grace_time = 0.1
51
+ @connection_manager.start do
52
+ # logger.debug {"we DID get the start callback for primary not starting"}
53
+
54
+ @connection_manager.should be_failed_over
55
+ @connection_manager.should be_usable
56
+ clean_and_done
57
+ end
58
+ end
59
+
60
+ end
61
+
62
+ describe "primary failing" do
63
+ it "should get the 2nd connection after the first goes down and does not come back and #2 is open" do
64
+ @connection_manager.on_failure do
65
+ neuter_connection(@connection1)
66
+ @connection_manager.on_open do
67
+ @connection_manager.connection.should == @connection2
68
+ @connection_manager.should be_failed_over
69
+ clean_and_done
70
+ end
71
+ end
72
+
73
+ @connection_manager.start do
74
+ @connection2.on_open do
75
+ @connection_manager.should be_usable
76
+ @connection1.ekg.heartbeat_failure!
77
+ end
78
+ end
79
+ end
80
+
81
+ it "should failback to primary when it comes back up" do
82
+ @connection_manager.on_failure do
83
+ #neuter_connection(@connection1)
84
+ @connection_manager.on_open do
85
+ @connection_manager.connection.should == @connection2
86
+ @connection_manager.should be_failed_over
87
+
88
+ #this 2nd on_failure will be triggered by the primary becoming available
89
+ @connection_manager.on_failure do
90
+ @connection_manager.on_open do
91
+ @connection_manager.should be_connected
92
+ @connection_manager.connection.should == @connection1
93
+ clean_and_done
94
+ end
95
+ end
96
+ end
97
+ end
98
+
99
+ @connection_manager.start do
100
+ @connection2.on_open do
101
+ @connection_manager.should be_usable
102
+ @connection1.ekg.heartbeat_failure!
103
+ end
104
+ end
105
+ end
106
+
107
+ it "should go from degraded to down if primary was up but secondary was not" do
108
+ neuter_connection(@connection2)
109
+ @connection_manager.on_failure do
110
+ @connection_manager.should be_down
111
+ clean_and_done
112
+ end
113
+
114
+ @connection_manager.start do
115
+ neuter_connection(@connection1)
116
+ @connection1.ekg.heartbeat_failure!
117
+ end
118
+
119
+ end
120
+
121
+ end
122
+
123
+ describe "secondary going down" do
124
+
125
+ it "should be degraded when down" do
126
+ @connection_manager.start do
127
+ @connection2.on_open do
128
+ @connection_manager.should be_connected
129
+
130
+ @connection2.on_failure do
131
+ #make sure #2 never comes back up (seriously, that's gross)
132
+ neuter_connection(@connection2)
133
+ EM.next_tick do
134
+ @connection_manager.should be_degraded
135
+ clean_and_done
136
+ end
137
+ end
138
+
139
+ #then fail #2
140
+ @connection2.ekg.heartbeat_failure!
141
+
142
+ end
143
+ end
144
+ end
145
+
146
+ it "should become connected again when it comes back" do
147
+ @connection_manager.start do
148
+ @connection2.on_open do
149
+ @connection_manager.should be_connected
150
+
151
+ @connection2.on_failure do
152
+ # logger.debug {"connection2 failure"}
153
+ #we failed #2, now wait for it to come back up
154
+ @connection2.on_open do
155
+ #it's back up - now make sure we're connected
156
+ EM.next_tick do
157
+ @connection_manager.should be_connected
158
+ clean_and_done
159
+ end
160
+ end
161
+ end
162
+ # logger.debug {"heartbeat failure"}
163
+ @connection2.ekg.heartbeat_failure!
164
+ end
165
+ end
166
+ end
167
+
168
+
169
+
170
+ end
171
+
172
+ describe "both failing" do
173
+ it "should enter a failed over state when secondary comes back up" do
174
+ @connection_manager.on_failure do
175
+ neuter_connection(@connection1)
176
+ @connection_manager.should be_failed_over
177
+ #second failure is from number 2 going down
178
+ @connection_manager.on_failure do
179
+ @connection_manager.should be_down
180
+ #now #2 comes back up
181
+ @connection_manager.on_open do
182
+ @connection_manager.should be_failed_over
183
+ @connection_manager.connection.should == @connection2
184
+ clean_and_done
185
+ end
186
+ end
187
+ end
188
+
189
+ @connection_manager.start do
190
+ @connection2.on_open do
191
+ @connection_manager.should be_usable
192
+ @connection1.ekg.heartbeat_failure!
193
+ EM.next_tick { @connection2.ekg.heartbeat_failure! }
194
+ end
195
+ end
196
+
197
+ end
198
+
199
+ it "should come back up degraded when primary comes back up" do
200
+ @connection_manager.on_failure do
201
+ neuter_connection(@connection2)
202
+ @connection_manager.should be_failed_over
203
+ #second failure is from number 2 going down
204
+ @connection_manager.on_failure do
205
+ @connection_manager.should be_down
206
+ #now #1 comes back up
207
+ @connection_manager.on_open do
208
+ @connection_manager.should be_degraded
209
+ @connection_manager.connection.should == @connection1
210
+ clean_and_done
211
+ end
212
+ end
213
+ end
214
+
215
+ @connection_manager.start do
216
+ @connection2.on_open do
217
+ @connection_manager.should be_usable
218
+ @connection1.ekg.heartbeat_failure!
219
+ EM.next_tick { @connection2.ekg.heartbeat_failure! }
220
+ end
221
+ end
222
+
223
+ end
224
+
225
+
226
+ end
227
+
228
+
229
+ #
230
+ # describe 'connect!' do
231
+ # it %[should take a block that will be called back when a connection is ready]
232
+ # it %[should fire the on_open deferred when ready]
233
+ # it %[should reset the on_open deferred after it's first fired]
234
+ #
235
+ # describe 'primary down' do
236
+ # it %[should time out waiting and use the secondary if the secondary is up]
237
+ # end
238
+ #
239
+ # describe 'secondary down' do
240
+ # it %[should not affect startup]
241
+ # end
242
+ # end
243
+ #
244
+ # describe 'failure' do
245
+ # describe 'of the primary connection' do
246
+ # it %[should fire the on_failure deferred]
247
+ # it %[should fire the on_open deferred when the secondary is ready]
248
+ # end
249
+ #
250
+ # describe 'of the secondary connection' do
251
+ # it %[should not fire the on_failure deferred]
252
+ # end
253
+ # end
254
+ #
255
+ # describe 'fail-back' do
256
+ # end
257
+ end
258
+ end
259
+
260
+
@@ -0,0 +1,91 @@
1
+ require 'spec_helper'
2
+
3
+ module WhoCan
4
+ describe :ConnectionWrapper do
5
+
6
+
7
+ before do
8
+ @config = 'amqp://localhost'
9
+ @cw = ConnectionWrapper.new(@config, :reconnect_delay => 0.2)
10
+ # flexmock(AMQP).should_receive(:connect).with(@config, Hash, Proc)
11
+ end
12
+
13
+ describe :state_machine do
14
+ it %[should be in state new] do
15
+ @cw.new?.should be_true
16
+ end
17
+ end
18
+
19
+
20
+ describe :connect do
21
+ include EventedSpec::EMSpec
22
+ default_timeout 2.0
23
+
24
+ def with_connection
25
+ @cw.connect do
26
+ # logger.debug { "on_open fired" }
27
+ yield
28
+ end
29
+ end
30
+
31
+ def close_and_done
32
+ # logger.debug { "calling @cw.close!" }
33
+ @cw.close! { done }
34
+ end
35
+
36
+ describe 'success' do
37
+ it %[should connect and fire the callbacks] do
38
+ with_connection do
39
+ @cw.should be_connected
40
+ @cw.can_close?.should be_true
41
+ @cw.amqp_session.should_not be_nil
42
+ @cw.amqp_session.should be_kind_of(AMQP::Session)
43
+ @cw.amqp_session.should be_connected
44
+
45
+ close_and_done
46
+ end
47
+ end
48
+ end
49
+
50
+ describe 'initial connection failure' do
51
+ before do
52
+ flexmock(AMQP).should_receive(:connect).with(@config, Hash, Proc).and_return do |conf,opts,blk|
53
+ # logger.debug { "firing on_tcp_connection_failure callback" }
54
+ opts[:on_tcp_connection_failure].call(@config)
55
+ end
56
+ end
57
+
58
+ it %[should enter retrying state] do
59
+ @cw.on_initial_connection_failure do
60
+ # logger.debug { "initial connection failed" }
61
+
62
+ @cw.should be_retrying
63
+
64
+ @cw.close! { done }
65
+ end
66
+
67
+ @cw.connect
68
+ end
69
+ end
70
+
71
+ describe 'established connection failure' do
72
+ it %[should enter retrying state] do
73
+ orig_on_failure = @cw.on_failure
74
+
75
+ with_connection do
76
+ @cw.on_failure do
77
+ orig_on_failure.should_not == @cw.on_failure
78
+ @cw.should be_retrying
79
+
80
+ close_and_done
81
+ end
82
+
83
+ # logger.debug { "calling heartbeat_failure!" }
84
+ @cw.ekg.heartbeat_failure!
85
+ end
86
+ end
87
+ end
88
+ end
89
+ end
90
+ end
91
+
@@ -0,0 +1,101 @@
1
+ require 'spec_helper'
2
+
3
+ module WhoCan::Heartbeater
4
+ describe 'Heartbeater' do
5
+ include EventedSpec::AMQPSpec
6
+ default_timeout 3.0
7
+ default_options :host => 'localhost'
8
+
9
+ describe 'Beat' do
10
+ before do
11
+ @beat_id = "beat_0"
12
+ end
13
+
14
+ amqp_before do
15
+ @chan = AMQP::Channel.new(AMQP.connection)
16
+ @chan.once_opened do
17
+ @queue = @chan.queue('', :exclusive => true, :auto_delete => true)
18
+ end
19
+ end
20
+
21
+ it %[should fire its callback on success] do
22
+ @chan.once_opened do
23
+ @chan.should be_open
24
+
25
+ beat = Beat.new(@chan, @queue, @beat_id, 0.5)
26
+ beat.should be_pending
27
+
28
+ beat.callback { done }
29
+
30
+ beat.start!
31
+
32
+ beat.after_publish do
33
+ beat.ping_received!
34
+ end
35
+ end
36
+ end
37
+
38
+ it %[should fire its errback if it times out] do
39
+ @chan.once_opened do
40
+ @default_exchange = flexmock(:default_exchange) do |m|
41
+ m.should_receive(:publish).once.with(String, Hash, Proc).and_return do |_,_,blk|
42
+ blk.call
43
+ end
44
+ end
45
+
46
+ mchan = flexmock(@chan, :default_exchange => @default_exchange)
47
+
48
+ beat = Beat.new(mchan, @queue, @beat_id, 0.25)
49
+
50
+ beat.errback do
51
+ # logger.debug { "errback fired!" }
52
+ done
53
+ end
54
+
55
+ beat.callback do
56
+ # logger.debug { "GAH! callback fired! this is bad!" }
57
+ raise "NO! FAIL!"
58
+ end
59
+
60
+ beat.start!
61
+ end
62
+ end
63
+
64
+ it %[should be cancelled? after cancel!] do
65
+ @chan.once_opened do
66
+ beat = Beat.new(@chan, @queue, @beat_id, 0.5)
67
+
68
+ beat.callback do
69
+ # logger.debug { "I HAVE BEEN CALLED BACK!!" }
70
+ beat.should be_cancelled
71
+ done
72
+ end
73
+
74
+ beat.after_publish do
75
+ beat.cancel!
76
+ end
77
+
78
+ beat.start!
79
+ end
80
+ end
81
+
82
+ it "should call the errback on a queue opening failure" do
83
+ @chan.once_opened do
84
+ beat = Beat.new(@chan, @queue, @beat_id, 0.25)
85
+ flexmock(@chan).should_receive(:queue).and_raise(AMQ::Client::ConnectionClosedError)
86
+
87
+ beat.errback do
88
+ # logger.debug { "errback fired!" }
89
+ done
90
+ end
91
+
92
+ beat.callback do
93
+ raise "FAIL!"
94
+ end
95
+
96
+ beat.start!
97
+ end
98
+ end
99
+ end
100
+ end
101
+ end