beetle 0.3.0.rc.2 → 0.3.0.rc.5

Sign up to get free protection for your applications and to get access to all the features.
@@ -8,40 +8,18 @@ module Beetle
8
8
  end
9
9
 
10
10
  test "initially there should be no amqp connections" do
11
- assert_equal({}, @sub.instance_variable_get("@amqp_connections"))
11
+ assert_equal({}, @sub.instance_variable_get("@connections"))
12
12
  end
13
13
 
14
- test "initially there should be no instances of MQ" do
15
- assert_equal({}, @sub.instance_variable_get("@mqs"))
14
+ test "initially there should be no channels" do
15
+ assert_equal({}, @sub.instance_variable_get("@channels"))
16
16
  end
17
17
 
18
- test "acccessing an amq_connection for a server which doesn't have one should create it and associate it with the server" do
19
- @sub.expects(:new_amqp_connection).returns(42)
20
- # TODO: smarter way to test? what triggers the amqp_connection private method call?
21
- assert_equal 42, @sub.send(:amqp_connection)
22
- connections = @sub.instance_variable_get("@amqp_connections")
23
- assert_equal 42, connections[@sub.server]
24
- end
25
-
26
- test "new amqp connections should be created using current host and port" do
27
- m = mock("dummy")
28
- expected_amqp_options = {
29
- :host => @sub.send(:current_host), :port => @sub.send(:current_port),
30
- :user => "guest", :pass => "guest", :vhost => "/", :logging => false
31
- }
32
- AMQP.expects(:connect).with(expected_amqp_options).returns(m)
33
- # TODO: smarter way to test? what triggers the amqp_connection private method call?
34
- assert_equal m, @sub.send(:new_amqp_connection)
35
- end
36
-
37
- test "mq instances should be created for the current server if accessed" do
38
- @sub.expects(:amqp_connection).returns(11)
39
- mq_mock = mock('mq')
40
- mq_mock.expects(:prefetch).with(1).returns(42)
41
- MQ.expects(:new).with(11).returns(mq_mock)
42
- assert_equal 42, @sub.send(:mq)
43
- mqs = @sub.instance_variable_get("@mqs")
44
- assert_equal 42, mqs[@sub.server]
18
+ test "channel should return the channel associated with the current server, if there is one" do
19
+ channel = mock("donald")
20
+ @sub.instance_variable_set("@channels", {"donald:1" => channel})
21
+ assert_nil @sub.send(:channel, "goofy:123")
22
+ assert_equal channel, @sub.send(:channel, "donald:1")
45
23
  end
46
24
 
47
25
  test "stop! should close all amqp connections and then stop the event loop" do
@@ -49,7 +27,7 @@ module Beetle
49
27
  connection1.expects(:close).yields
50
28
  connection2 = mock('con2')
51
29
  connection2.expects(:close).yields
52
- @sub.instance_variable_set "@amqp_connections", [["server1", connection1], ["server2",connection2]]
30
+ @sub.instance_variable_set "@connections", [["server1", connection1], ["server2",connection2]]
53
31
  EM.expects(:stop_event_loop)
54
32
  @sub.send(:stop!)
55
33
  end
@@ -156,31 +134,43 @@ module Beetle
156
134
  q.expects(:bind).with(:the_exchange, {:key => "haha.#"})
157
135
  m = mock("MQ")
158
136
  m.expects(:queue).with("some_queue", :durable => true, :passive => false, :auto_delete => false, :exclusive => false).returns(q)
159
- @sub.expects(:mq).returns(m)
137
+ @sub.expects(:channel).returns(m)
160
138
 
161
139
  @sub.send(:queue, "some_queue")
162
140
  assert_equal q, @sub.send(:queues)["some_queue"]
163
141
  end
164
142
 
165
- test "binding queues should iterate over all servers" do
166
- s = sequence("binding")
143
+ test "binding queues should bind all queues" do
167
144
  @client.register_queue(:x)
168
145
  @client.register_queue(:y)
169
146
  @client.register_handler(%w(x y)){}
170
- @sub.servers = %w(a b)
171
- @sub.expects(:set_current_server).with("a").in_sequence(s)
172
- @sub.expects(:queue).with("x").in_sequence(s)
173
- @sub.expects(:queue).with("y").in_sequence(s)
174
- @sub.expects(:set_current_server).with("b").in_sequence(s)
175
- @sub.expects(:queue).with("x").in_sequence(s)
176
- @sub.expects(:queue).with("y").in_sequence(s)
147
+ @sub.expects(:queue).with("x")
148
+ @sub.expects(:queue).with("y")
177
149
  @sub.send(:bind_queues, %W(x y))
178
150
  end
179
151
 
152
+ test "subscribing to queues should subscribe on all queues" do
153
+ @client.register_queue(:x)
154
+ @client.register_queue(:y)
155
+ @client.register_handler(%w(x y)){}
156
+ @sub.expects(:subscribe).with("x")
157
+ @sub.expects(:subscribe).with("y")
158
+ @sub.send(:subscribe_queues, %W(x y))
159
+ end
160
+
180
161
  test "should not try to bind a queue for an exchange which has no queue" do
181
162
  @client.register_message(:without_queue)
182
163
  assert_equal [], @sub.send(:queues_for_exchanges, ["without_queue"])
183
164
  end
165
+
166
+ test "should not subscribe on a queue for which there is no handler" do
167
+ @client.register_queue(:x)
168
+ @client.register_queue(:y)
169
+ @client.register_handler(%w(y)){}
170
+ @sub.expects(:subscribe).with("y")
171
+ @sub.send(:subscribe_queues, %W(x y))
172
+ end
173
+
184
174
  end
185
175
 
186
176
  class SubscriberExchangeManagementTest < Test::Unit::TestCase
@@ -197,26 +187,20 @@ module Beetle
197
187
  @client.register_exchange("some_exchange", "type" => "topic", "durable" => true)
198
188
  m = mock("AMQP")
199
189
  m.expects(:topic).with("some_exchange", :durable => true).returns(42)
200
- @sub.expects(:mq).returns(m)
190
+ @sub.expects(:channel).returns(m)
201
191
  ex = @sub.send(:exchange, "some_exchange")
202
192
  assert @sub.send(:exchanges).include?("some_exchange")
203
193
  ex2 = @sub.send(:exchange, "some_exchange")
204
194
  assert_equal ex2, ex
205
195
  end
206
196
 
207
- test "should create exchanges for all exchanges passed to create_exchanges, for all servers" do
208
- @sub.servers = %w(x y)
197
+ test "should create exchanges for all exchanges passed to create_exchanges for the current server" do
209
198
  @client.register_queue(:donald, :exchange => 'duck')
210
199
  @client.register_queue(:mickey)
211
200
  @client.register_queue(:mouse, :exchange => 'mickey')
212
201
 
213
- exchange_creation = sequence("exchange creation")
214
- @sub.expects(:set_current_server).with('x').in_sequence(exchange_creation)
215
- @sub.expects(:create_exchange!).with("duck", anything).in_sequence(exchange_creation)
216
- @sub.expects(:create_exchange!).with("mickey", anything).in_sequence(exchange_creation)
217
- @sub.expects(:set_current_server).with('y', anything).in_sequence(exchange_creation)
218
- @sub.expects(:create_exchange!).with("duck", anything).in_sequence(exchange_creation)
219
- @sub.expects(:create_exchange!).with("mickey", anything).in_sequence(exchange_creation)
202
+ @sub.expects(:create_exchange!).with("duck", anything)
203
+ @sub.expects(:create_exchange!).with("mickey", anything)
220
204
  @sub.send(:create_exchanges, %w(duck mickey))
221
205
  end
222
206
  end
@@ -240,7 +224,7 @@ module Beetle
240
224
  assert_nothing_raised { @callback.call(header, 'foo') }
241
225
  end
242
226
 
243
- test "should call reject on the message header when processing the handler returns true on recover?" do
227
+ test "should call reject on the message header when processing the handler returns true on reject?" do
244
228
  header = header_with_params({})
245
229
  result = mock("result")
246
230
  result.expects(:reject?).returns(true)
@@ -256,10 +240,10 @@ module Beetle
256
240
  Message.any_instance.expects(:process).returns(result)
257
241
  Message.any_instance.expects(:handler_result).returns("response-data")
258
242
  mq = mock("MQ")
259
- @sub.expects(:mq).with(@sub.server).returns(mq)
243
+ @sub.expects(:channel).with(@sub.server).returns(mq)
260
244
  exchange = mock("exchange")
261
- exchange.expects(:publish).with("response-data", :headers => {:status => "OK"})
262
- MQ::Exchange.expects(:new).with(mq, :direct, "", :key => "tmp-queue").returns(exchange)
245
+ exchange.expects(:publish).with("response-data", :routing_key => "tmp-queue", :headers => {:status => "OK"}, :persistent => false)
246
+ AMQP::Exchange.expects(:new).with(mq, :direct, "").returns(exchange)
263
247
  @callback.call(header, 'foo')
264
248
  end
265
249
 
@@ -269,10 +253,10 @@ module Beetle
269
253
  Message.any_instance.expects(:process).returns(result)
270
254
  Message.any_instance.expects(:handler_result).returns(nil)
271
255
  mq = mock("MQ")
272
- @sub.expects(:mq).with(@sub.server).returns(mq)
256
+ @sub.expects(:channel).with(@sub.server).returns(mq)
273
257
  exchange = mock("exchange")
274
- exchange.expects(:publish).with("", :headers => {:status => "FAILED"})
275
- MQ::Exchange.expects(:new).with(mq, :direct, "", :key => "tmp-queue").returns(exchange)
258
+ exchange.expects(:publish).with("", :routing_key => "tmp-queue", :headers => {:status => "FAILED"}, :persistent => false)
259
+ AMQP::Exchange.expects(:new).with(mq, :direct, "").returns(exchange)
276
260
  @callback.call(header, 'foo')
277
261
  end
278
262
 
@@ -284,18 +268,6 @@ module Beetle
284
268
  @sub = @client.send(:subscriber)
285
269
  end
286
270
 
287
- test "subscribe should create subscriptions on all queues for all servers" do
288
- @sub.servers << "localhost:7777"
289
- @client.register_message(:a)
290
- @client.register_message(:b)
291
- @client.register_queue(:a)
292
- @client.register_queue(:b)
293
- @client.register_handler(%W(a b)){}
294
- @sub.expects(:subscribe).with("a").times(2)
295
- @sub.expects(:subscribe).with("b").times(2)
296
- @sub.send(:subscribe_queues, %W(a b))
297
- end
298
-
299
271
  test "subscribe should subscribe with a subscription callback created from the registered block and remember the subscription" do
300
272
  @client.register_queue(:some_queue, :exchange => "some_exchange", :key => "some_key")
301
273
  server = @sub.server
@@ -312,28 +284,32 @@ module Beetle
312
284
  q = mock("QUEUE")
313
285
  subscription_options = {:ack => true, :key => "#"}
314
286
  q.expects(:subscribe).with(subscription_options).yields(header, "foo")
315
- @sub.expects(:queues).returns({"some_queue" => q}).twice
287
+ @sub.expects(:queues).returns({"some_queue" => q}).once
316
288
  @sub.send(:subscribe, "some_queue")
317
289
  assert block_called
318
290
  assert @sub.__send__(:has_subscription?, "some_queue")
319
- q.expects(:subscribe).with(subscription_options).raises(MQ::Error)
320
- assert_raises(Error) { @sub.send(:subscribe, "some_queue") }
291
+ # q.expects(:subscribe).with(subscription_options).raises(MQ::Error)
292
+ # assert_raises(Error) { @sub.send(:subscribe, "some_queue") }
321
293
  end
322
294
 
323
295
  test "subscribe should fail if no handler exists for given message" do
324
296
  assert_raises(Error){ @sub.send(:subscribe, "some_queue") }
325
297
  end
326
298
 
327
- test "listening on queues should use eventmachine. create exchanges. bind queues. install subscribers. and yield." do
299
+ test "listening on queues should use eventmachine, connect to each server, and yield" do
328
300
  @client.register_exchange(:an_exchange)
329
301
  @client.register_queue(:a_queue, :exchange => :an_exchange)
330
302
  @client.register_message(:a_message, :key => "foo", :exchange => :an_exchange)
303
+ @sub.servers << "localhost:7777"
331
304
 
305
+ @sub.expects(:connect_server).twice
332
306
  EM.expects(:run).yields
333
- @sub.expects(:create_exchanges).with(["an_exchange"])
334
- @sub.expects(:bind_queues).with(["a_queue"])
335
- @sub.expects(:subscribe_queues).with(["a_queue"])
336
- @sub.listen_queues(["a_queue"]) {}
307
+ # @sub.expects(:create_exchanges).with(["an_exchange"])
308
+ # @sub.expects(:bind_queues).with(["a_queue"])
309
+ # @sub.expects(:subscribe_queues).with(["a_queue"])
310
+ yielded = false
311
+ @sub.listen_queues(["a_queue"]) { yielded = true}
312
+ assert yielded
337
313
  end
338
314
  end
339
315
 
@@ -357,4 +333,64 @@ module Beetle
357
333
 
358
334
  end
359
335
 
336
+ class ConnectionTest < Test::Unit::TestCase
337
+ def setup
338
+ @client = Client.new
339
+ @sub = @client.send(:subscriber)
340
+ @sub.send(:set_current_server, "mickey:42")
341
+ @settings = @sub.send(:connection_settings)
342
+ end
343
+
344
+ test "connection settings should use current host and port and specify connection failure callback" do
345
+ assert_equal "mickey", @settings[:host]
346
+ assert_equal 42, @settings[:port]
347
+ assert @settings.has_key?(:on_tcp_connection_failure)
348
+ end
349
+
350
+ test "tcp connection failure should try to connect again after 10 seconds" do
351
+ cb = @sub.send(:on_tcp_connection_failure)
352
+ EM::Timer.expects(:new).with(10).yields
353
+ @sub.expects(:connect_server).with(@settings)
354
+ @sub.logger.expects(:warn).with("Beetle: connection failed: mickey:42")
355
+ cb.call(@settings)
356
+ end
357
+
358
+ test "tcp connection loss handler tries to reconnect" do
359
+ connection = mock("connection")
360
+ connection.expects(:reconnect).with(false, 10)
361
+ @sub.logger.expects(:warn).with("Beetle: lost connection: mickey:42. reconnecting.")
362
+ @sub.send(:on_tcp_connection_loss, connection, {:host => "mickey", :port => 42})
363
+ end
364
+
365
+ test "event machine connection error" do
366
+ connection = mock("connection")
367
+ AMQP.expects(:connect).raises(EventMachine::ConnectionError)
368
+ @settings[:on_tcp_connection_failure].expects(:call).with(@settings)
369
+ @sub.send(:connect_server, @settings)
370
+ end
371
+
372
+ test "successfull connection to broker" do
373
+ connection = mock("connection")
374
+ connection.expects(:on_tcp_connection_loss)
375
+ @sub.expects(:open_channel_and_subscribe).with(connection, @settings)
376
+ AMQP.expects(:connect).with(@settings).yields(connection)
377
+ @sub.send(:connect_server, @settings)
378
+ assert_equal connection, @sub.instance_variable_get("@connections")["mickey:42"]
379
+ end
380
+
381
+ test "channel opening, exchange creation, queue bindings and subscription" do
382
+ connection = mock("connection")
383
+ channel = mock("channel")
384
+ channel.expects(:prefetch).with(1)
385
+ channel.expects(:auto_recovery=).with(true)
386
+ AMQP::Channel.expects(:new).with(connection).yields(channel)
387
+ @sub.expects(:create_exchanges)
388
+ @sub.expects(:bind_queues)
389
+ @sub.expects(:subscribe_queues)
390
+ @sub.send(:open_channel_and_subscribe, connection, @settings)
391
+ assert_equal channel, @sub.instance_variable_get("@channels")["mickey:42"]
392
+ end
393
+
394
+ end
395
+
360
396
  end
@@ -0,0 +1,25 @@
1
+ # colorized output for Test::Unit / MiniTest
2
+ begin
3
+ if RUBY_VERSION < "1.9"
4
+ require 'redgreen'
5
+ else
6
+ module ColorizedDots
7
+ require 'ansi/code'
8
+ def run(runner)
9
+ r = super
10
+ ANSI.ansi(r, ANSI_COLOR_MAPPING[r])
11
+ end
12
+ ANSI_COLOR_MAPPING = Hash.new(:white).merge!('.' => :green, 'S' => :magenta, 'F' => :yellow, 'E' => :red )
13
+ end
14
+ class MiniTest::Unit
15
+ TestCase.send(:include, ColorizedDots)
16
+ def status(io = @@out)
17
+ format = "%d tests, %d assertions, %d failures, %d errors, %d skips"
18
+ color = (errors + failures) > 0 ? :red : :green
19
+ io.puts ANSI.ansi(format % [test_count, assertion_count, failures, errors, skips], color)
20
+ end
21
+ end
22
+ end
23
+ rescue LoadError => e
24
+ # do nothing
25
+ end unless ENV['TM_FILENAME']
data/test/test_helper.rb CHANGED
@@ -4,11 +4,7 @@ require 'mocha'
4
4
  require 'active_support/testing/declarative'
5
5
 
6
6
  require File.expand_path(File.dirname(__FILE__) + '/../lib/beetle')
7
-
8
- begin
9
- require 'redgreen' unless ENV['TM_FILENAME']
10
- rescue LoadError => e
11
- end
7
+ require File.expand_path(File.dirname(__FILE__) + '/colorized_test_output')
12
8
 
13
9
  # we can remove this hack which is needed only for testing
14
10
  begin
@@ -29,13 +25,15 @@ end
29
25
  Beetle.config.logger = Logger.new(File.dirname(__FILE__) + '/../test.log')
30
26
  Beetle.config.redis_server = "localhost:6379"
31
27
 
28
+
32
29
  def header_with_params(opts = {})
33
30
  beetle_headers = Beetle::Message.publishing_options(opts)
34
31
  header = mock("header")
35
- header.stubs(:properties).returns(beetle_headers)
32
+ header.stubs(:attributes).returns(beetle_headers)
36
33
  header
37
34
  end
38
35
 
36
+
39
37
  def redis_stub(name, opts = {})
40
38
  default_port = opts['port'] || "1234"
41
39
  default_host = opts['host'] || "foo"