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

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.
@@ -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"