beetle 0.2.5 → 0.2.9.1

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/RELEASE_NOTES.rdoc CHANGED
@@ -1,5 +1,17 @@
1
1
  = Release Notes
2
2
 
3
+ == Version 0.2.9
4
+
5
+ * Beetle::Client now raises an exception when it fails to publish a message to at least 1 RabbitMQ server
6
+ * Subscribers are now stopped cleanly to avoid 'closed abruptly' messages in the RabbitMQ server log
7
+ * Added send and receive timeouts on the socket and use system_timer for ruby side timeouts
8
+
9
+ == Version 0.2.6
10
+
11
+ * Set dependency on ActiveSupport to 2.3.x since it ain't compatible to version 3.x yet
12
+ * Publishers catch a wider range (all?) of possible exceptions when publishing messages
13
+ * Redis Configuration Servers detect and warn when unknown Redis Configuration Clients connect
14
+
3
15
  == Version 0.2.5
4
16
 
5
17
  Added missing files to gem and rdoc
data/Rakefile CHANGED
@@ -2,6 +2,7 @@ require 'rake'
2
2
  require 'rake/testtask'
3
3
  require 'rcov/rcovtask'
4
4
  require 'cucumber/rake/task'
5
+ require 'active_support'
5
6
 
6
7
  # 1.8/1.9 compatible way of loading lib/beetle.rb
7
8
  $:.unshift 'lib'
data/beetle.gemspec CHANGED
@@ -1,6 +1,6 @@
1
1
  Gem::Specification.new do |s|
2
2
  s.name = "beetle"
3
- s.version = "0.2.5"
3
+ s.version = "0.2.9.1"
4
4
 
5
5
  s.required_rubygems_version = ">= 1.3.1"
6
6
  s.authors = ["Stefan Kaes", "Pascal Friederich", "Ali Jelveh", "Sebastian Roebke"]
@@ -31,9 +31,9 @@ Gem::Specification.new do |s|
31
31
  s.specification_version = 3
32
32
  s.add_runtime_dependency("uuid4r", [">= 0.1.1"])
33
33
  s.add_runtime_dependency("bunny", [">= 0.6.0"])
34
- s.add_runtime_dependency("redis", [">= 2.0.4"])
34
+ s.add_runtime_dependency("redis", ["= 2.0.4"])
35
35
  s.add_runtime_dependency("amqp", [">= 0.6.7"])
36
- s.add_runtime_dependency("activesupport", [">= 2.3.4"])
36
+ s.add_runtime_dependency("activesupport", ["~> 2.3.4"])
37
37
  s.add_runtime_dependency("daemons", [">= 1.0.10"])
38
38
  s.add_development_dependency("mocha", [">= 0"])
39
39
  s.add_development_dependency("rcov", [">= 0"])
data/examples/attempts.rb CHANGED
@@ -14,15 +14,15 @@ require File.expand_path("../lib/beetle", File.dirname(__FILE__))
14
14
  Beetle.config.logger.level = Logger::INFO
15
15
 
16
16
  # setup client
17
- client = Beetle::Client.new
18
- client.register_queue(:test)
19
- client.register_message(:test)
17
+ $client = Beetle::Client.new
18
+ $client.register_queue(:test)
19
+ $client.register_message(:test)
20
20
 
21
21
  # purge the test queue
22
- client.purge(:test)
22
+ $client.purge(:test)
23
23
 
24
24
  # empty the dedup store
25
- client.deduplication_store.flushdb
25
+ $client.deduplication_store.flushdb
26
26
 
27
27
  # we're starting with 0 exceptions and expect our handler to process the message until the exception count has reached 10
28
28
  $exceptions = 0
@@ -32,35 +32,35 @@ $max_exceptions = 10
32
32
  # in this example we've not only overwritten the process method but also the
33
33
  # error and failure methods of the handler baseclass
34
34
  class Handler < Beetle::Handler
35
-
35
+
36
36
  # called when the handler receives the message - fail everytime
37
37
  def process
38
38
  raise "failed #{$exceptions += 1} times"
39
39
  end
40
-
40
+
41
41
  # called when handler process raised an exception
42
42
  def error(exception)
43
43
  logger.info "execution failed: #{exception}"
44
44
  end
45
-
45
+
46
46
  # called when the handler has finally failed
47
47
  # we're stopping the event loop so this script stops after that
48
48
  def failure(result)
49
49
  super
50
- EM.stop_event_loop
50
+ $client.stop_listening
51
51
  end
52
52
  end
53
53
 
54
54
  # register our handler to the message, configure it to our max_exceptions limit, we configure a delay of 0 to have it not wait before retrying
55
- client.register_handler(:test, Handler, :exceptions => $max_exceptions, :delay => 0)
55
+ $client.register_handler(:test, Handler, :exceptions => $max_exceptions, :delay => 0)
56
56
 
57
57
  # publish a our test message
58
- client.publish(:test, "snafu")
58
+ $client.publish(:test, "snafu")
59
59
 
60
60
  # and start our listening loop...
61
- client.listen
61
+ $client.listen
62
62
 
63
63
  # error handling, if everything went right this shouldn't happen.
64
64
  if $exceptions != $max_exceptions + 1
65
65
  raise "something is fishy. Failed #{$exceptions} times"
66
- end
66
+ end
data/lib/beetle/client.rb CHANGED
@@ -24,6 +24,9 @@ module Beetle
24
24
  # the AMQP servers available for publishing
25
25
  attr_reader :servers
26
26
 
27
+ # additional AMQP servers available for subscribing. useful for migration scenarios.
28
+ attr_reader :additional_subscription_servers
29
+
27
30
  # an options hash for the configured exchanges
28
31
  attr_reader :exchanges
29
32
 
@@ -46,6 +49,7 @@ module Beetle
46
49
  def initialize(config = Beetle.config)
47
50
  @config = config
48
51
  @servers = config.servers.split(/ *, */)
52
+ @additional_subscription_servers = config.additional_subscription_servers.split(/ *, */)
49
53
  @exchanges = {}
50
54
  @queues = {}
51
55
  @messages = {}
@@ -38,6 +38,8 @@ module Beetle
38
38
 
39
39
  # list of amqp servers to use (defaults to <tt>"localhost:5672"</tt>)
40
40
  attr_accessor :servers
41
+ # list of additional amqp servers to use for subscribers (defaults to <tt>""</tt>)
42
+ attr_accessor :additional_subscription_servers
41
43
  # the virtual host to use on the AMQP servers (defaults to <tt>"/"</tt>)
42
44
  attr_accessor :vhost
43
45
  # the AMQP user to use when connecting to the AMQP servers (defaults to <tt>"guest"</tt>)
@@ -63,6 +65,7 @@ module Beetle
63
65
  self.redis_configuration_client_ids = ""
64
66
 
65
67
  self.servers = "localhost:5672"
68
+ self.additional_subscription_servers = ""
66
69
  self.vhost = "/"
67
70
  self.user = "guest"
68
71
  self.password = "guest"
@@ -77,13 +80,14 @@ module Beetle
77
80
  end
78
81
 
79
82
  def logger
80
- @logger ||= begin
81
- l = Logger.new(log_file)
82
- l.formatter = Logger::Formatter.new
83
- l.level = Logger::INFO
84
- l.datetime_format = "%Y-%m-%d %H:%M:%S"
85
- l
86
- end
83
+ @logger ||=
84
+ begin
85
+ l = Logger.new(log_file)
86
+ l.formatter = Logger::Formatter.new
87
+ l.level = Logger::INFO
88
+ l.datetime_format = "%Y-%m-%d %H:%M:%S"
89
+ l
90
+ end
87
91
  end
88
92
 
89
93
  private
@@ -93,6 +97,7 @@ module Beetle
93
97
  send("#{key}=", value)
94
98
  end
95
99
  rescue Exception
100
+ Beetle::reraise_expectation_errors!
96
101
  logger.error "Error loading beetle config file '#{config_file}': #{$!}"
97
102
  raise
98
103
  end
@@ -80,6 +80,7 @@ module Beetle
80
80
  @flags = headers[:flags].to_i
81
81
  @expires_at = headers[:expires_at].to_i
82
82
  rescue Exception => @exception
83
+ Beetle::reraise_expectation_errors!
83
84
  logger.error "Could not decode message. #{self.inspect}"
84
85
  end
85
86
 
@@ -9,6 +9,17 @@ module Beetle
9
9
  @bunnies = {}
10
10
  end
11
11
 
12
+ # list of exceptions potentially raised by bunny
13
+ # these need to be lazy, because qrack exceptions are only defined after a connection has been established
14
+ def bunny_exceptions
15
+ [
16
+ Bunny::ConnectionError, Bunny::ForcedChannelCloseError, Bunny::ForcedConnectionCloseError,
17
+ Bunny::MessageError, Bunny::ProtocolError, Bunny::ServerDownError, Bunny::UnsubscribeError,
18
+ Bunny::AcknowledgementError, Qrack::BufferOverflowError, Qrack::InvalidTypeError,
19
+ Errno::EHOSTUNREACH, Errno::ECONNRESET
20
+ ]
21
+ end
22
+
12
23
  def publish(message_name, data, opts={}) #:nodoc:
13
24
  opts = @client.messages[message_name].merge(opts.symbolize_keys)
14
25
  exchange_name = opts.delete(:exchange)
@@ -33,12 +44,13 @@ module Beetle
33
44
  exchange(exchange_name).publish(data, opts)
34
45
  logger.debug "Beetle: message sent!"
35
46
  published = 1
36
- rescue Bunny::ServerDownError, Bunny::ConnectionError
47
+ rescue *bunny_exceptions
37
48
  stop!
38
49
  mark_server_dead
39
50
  tries -= 1
40
51
  retry if tries > 0
41
52
  logger.error "Beetle: message could not be delivered: #{message_name}"
53
+ raise NoMessageSent.new
42
54
  end
43
55
  published
44
56
  end
@@ -60,7 +72,7 @@ module Beetle
60
72
  exchange(exchange_name).publish(data, opts)
61
73
  published << @server
62
74
  logger.debug "Beetle: message sent (#{published})!"
63
- rescue Bunny::ServerDownError, Bunny::ConnectionError
75
+ rescue *bunny_exceptions
64
76
  stop!
65
77
  mark_server_dead
66
78
  end
@@ -68,9 +80,11 @@ module Beetle
68
80
  case published.size
69
81
  when 0
70
82
  logger.error "Beetle: message could not be delivered: #{message_name}"
83
+ raise NoMessageSent.new
71
84
  when 1
72
85
  logger.warn "Beetle: failed to send message redundantly"
73
86
  end
87
+
74
88
  published.size
75
89
  end
76
90
 
@@ -101,7 +115,7 @@ module Beetle
101
115
  status = msg[:header].properties[:headers][:status]
102
116
  end
103
117
  logger.debug "Beetle: rpc complete!"
104
- rescue Bunny::ServerDownError, Bunny::ConnectionError
118
+ rescue *bunny_exceptions
105
119
  stop!
106
120
  mark_server_dead
107
121
  tries -= 1
@@ -37,6 +37,7 @@ module Beetle
37
37
  # loop, reacting to failover related messages sent by RedisConfigurationServer.
38
38
  def start
39
39
  verify_redis_master_file_string
40
+ client_started!
40
41
  logger.info "RedisConfigurationClient starting (client id: #{id})"
41
42
  determine_initial_master
42
43
  clear_redis_master_file unless current_master.try(:master?)
@@ -102,6 +103,7 @@ module Beetle
102
103
  config.message :client_invalidated
103
104
  config.message :reconfigure
104
105
  config.queue :reconfigure, :amqp_name => "#{system}_reconfigure_#{id}"
106
+ config.message :client_started
105
107
 
106
108
  config.handler [:ping, :invalidate, :reconfigure], MessageDispatcher
107
109
  end
@@ -118,6 +120,10 @@ module Beetle
118
120
  beetle.publish(:pong, {"id" => id, "token" => @current_token}.to_json)
119
121
  end
120
122
 
123
+ def client_started!
124
+ beetle.publish(:client_started, {"id" => id}.to_json)
125
+ end
126
+
121
127
  def invalidate!
122
128
  @current_master = nil
123
129
  clear_redis_master_file
@@ -75,6 +75,17 @@ module Beetle
75
75
  end
76
76
  end
77
77
 
78
+ def client_started(payload)
79
+ id = payload["id"]
80
+ if client_id_valid?(id)
81
+ logger.info("Received client_started message from id #{id}")
82
+ else
83
+ msg = "Received client_started message from unknown id '#{id}'"
84
+ logger.error(msg)
85
+ beetle.publish(:system_notification, {"message" => msg}.to_json)
86
+ end
87
+ end
88
+
78
89
  # called by the message dispatcher when a "client_invalidated" message from a RedisConfigurationClient is received
79
90
  def client_invalidated(payload)
80
91
  id = payload["id"]
@@ -139,8 +150,10 @@ module Beetle
139
150
  config.message :invalidate
140
151
  config.message :reconfigure
141
152
  config.message :system_notification
153
+ config.message :client_started
154
+ config.queue :client_started, :amqp_name => "#{system}_client_started"
142
155
 
143
- config.handler [:pong, :client_invalidated], MessageDispatcher
156
+ config.handler [:pong, :client_invalidated, :client_started], MessageDispatcher
144
157
  end
145
158
  end
146
159
 
@@ -166,13 +179,17 @@ module Beetle
166
179
  end
167
180
 
168
181
  def validate_pong_client_id(client_id)
169
- unless known_client = @client_ids.include?(client_id)
170
- msg = "Received pong message from unknown client '#{client_id}'"
182
+ unless known_client = client_id_valid?(client_id)
183
+ msg = "Received pong message from unknown id '#{client_id}'"
171
184
  logger.error(msg)
172
185
  beetle.publish(:system_notification, {"message" => msg}.to_json)
173
186
  end
174
187
  known_client
175
188
  end
189
+
190
+ def client_id_valid?(client_id)
191
+ @client_ids.include?(client_id)
192
+ end
176
193
 
177
194
  def redeem_token(token)
178
195
  valid_token = token == @current_token
@@ -5,6 +5,7 @@ module Beetle
5
5
  # create a new subscriber instance
6
6
  def initialize(client, options = {}) #:nodoc:
7
7
  super
8
+ @servers.concat @client.additional_subscription_servers
8
9
  @handlers = {}
9
10
  @amqp_connections = {}
10
11
  @mqs = {}
@@ -30,9 +31,14 @@ module Beetle
30
31
  end
31
32
  end
32
33
 
33
- # stops the eventmachine loop
34
+ # closes all AMQP connections and stops the eventmachine loop
34
35
  def stop! #:nodoc:
35
- EM.stop_event_loop
36
+ if @amqp_connections.empty?
37
+ EM.stop_event_loop
38
+ else
39
+ server, connection = @amqp_connections.shift
40
+ connection.close { stop! }
41
+ end
36
42
  end
37
43
 
38
44
  # register handler for the given queues (see Client#register_handler)
data/lib/beetle.rb CHANGED
@@ -1,3 +1,4 @@
1
+ $:.unshift(File.expand_path('..', __FILE__))
1
2
  require 'amqp'
2
3
  require 'mq'
3
4
  require 'bunny'
@@ -17,6 +18,8 @@ module Beetle
17
18
  class UnknownQueue < Error; end
18
19
  # raised when no redis master server can be found
19
20
  class NoRedisMaster < Error; end
21
+ # raise when no message could be sent by the publisher
22
+ class NoMessageSent < Error; end
20
23
 
21
24
  # AMQP options for exchange creation
22
25
  EXCHANGE_CREATION_KEYS = [:auto_delete, :durable, :internal, :nowait, :passive]
@@ -56,3 +59,5 @@ module Beetle
56
59
 
57
60
  Timer = RUBY_VERSION < "1.9" ? SystemTimer : Timeout
58
61
  end
62
+
63
+ require 'ext/qrack/client'
@@ -0,0 +1,28 @@
1
+ require 'qrack/client'
2
+
3
+ unless Qrack::Client.instance_methods.include?("socket_with_reliable_timeout")
4
+ module Qrack
5
+ class Client
6
+ # overwrite the timeout method so that SystemTimer is used
7
+ # instead the standard timeout.rb: http://ph7spot.com/musings/system-timer
8
+ delegate :timeout, :to => Beetle::Timer
9
+
10
+ def socket_with_reliable_timeout
11
+ socket_without_reliable_timeout
12
+
13
+ secs = Integer(CONNECT_TIMEOUT)
14
+ usecs = Integer((CONNECT_TIMEOUT - secs) * 1_000_000)
15
+ optval = [secs, usecs].pack("l_2")
16
+
17
+ begin
18
+ @socket.setsockopt Socket::SOL_SOCKET, Socket::SO_RCVTIMEO, optval
19
+ @socket.setsockopt Socket::SOL_SOCKET, Socket::SO_SNDTIMEO, optval
20
+ rescue Errno::ENOPROTOOPT
21
+ end
22
+ @socket
23
+ end
24
+ alias_method_chain :socket, :reliable_timeout
25
+
26
+ end
27
+ end
28
+ end
@@ -11,6 +11,10 @@ module Beetle
11
11
  assert_equal ["localhost:5672"], @client.servers
12
12
  end
13
13
 
14
+ test "should have no additional subscription servers" do
15
+ assert_equal [], @client.additional_subscription_servers
16
+ end
17
+
14
18
  test "should have no exchanges" do
15
19
  assert @client.exchanges.empty?
16
20
  end
@@ -12,7 +12,7 @@ module Beetle
12
12
  config.config_file = "some/path/to/a/file"
13
13
  assert_equal new_value, config.gc_threshold
14
14
  end
15
-
15
+
16
16
  test "should log to STDOUT if no log_file given" do
17
17
  config = Configuration.new
18
18
  Logger.expects(:new).with(STDOUT).returns(stub_everything)
@@ -27,4 +27,4 @@ module Beetle
27
27
  config.logger
28
28
  end
29
29
  end
30
- end
30
+ end
@@ -95,6 +95,8 @@ module Beetle
95
95
  redis1.expects(:get).with("foo:x").raises("disconnected").in_sequence(s)
96
96
  @store.expects(:redis).returns(redis2).in_sequence(s)
97
97
  redis2.expects(:get).with("foo:x").returns("42").in_sequence(s)
98
+ @store.logger.expects(:info)
99
+ @store.logger.expects(:error)
98
100
  assert_equal("42", @store.get("foo", "x"))
99
101
  end
100
102
 
@@ -103,6 +105,8 @@ module Beetle
103
105
  @store.stubs(:redis).returns(redis1)
104
106
  redis1.stubs(:get).with("foo:x").raises("disconnected")
105
107
  @store.stubs(:sleep)
108
+ @store.logger.stubs(:info)
109
+ @store.logger.stubs(:error)
106
110
  assert_raises(NoRedisMaster) { @store.get("foo", "x") }
107
111
  end
108
112
  end
@@ -0,0 +1,26 @@
1
+ require File.expand_path(File.dirname(__FILE__) + '/../test_helper')
2
+
3
+
4
+ class QrackClientExtTest < Test::Unit::TestCase
5
+ def setup
6
+ Qrack::Client.any_instance.stubs(:create_channel).returns(nil)
7
+ @client = Qrack::Client.new
8
+ end
9
+
10
+
11
+ test "should use system-timer for reliable timeouts" do
12
+ Beetle::Timer.expects(:timeout)
13
+ @client.send :timeout, 1, 1 do
14
+ end
15
+ end
16
+
17
+ test "should set send/receive timeouts on the socket" do
18
+ socket_mock = mock("socket")
19
+ @client.instance_variable_set(:@socket, socket_mock)
20
+ @client.stubs(:socket_without_reliable_timeout)
21
+
22
+ socket_mock.expects(:setsockopt).with(Socket::SOL_SOCKET, Socket::SO_RCVTIMEO, anything)
23
+ socket_mock.expects(:setsockopt).with(Socket::SOL_SOCKET, Socket::SO_SNDTIMEO, anything)
24
+ @client.send(:socket)
25
+ end
26
+ end
@@ -525,7 +525,7 @@ module Beetle
525
525
  test "processing a message catches internal exceptions risen by process_internal and returns an internal error" do
526
526
  header = header_with_params({})
527
527
  message = Message.new("somequeue", header, 'foo', :store => @store)
528
- message.expects(:process_internal).raises(Exception.new)
528
+ message.expects(:process_internal).raises(Exception.new("this is expected"))
529
529
  handler = Handler.new
530
530
  handler.expects(:process_exception).never
531
531
  handler.expects(:process_failure).never
@@ -76,13 +76,16 @@ module Beetle
76
76
  end
77
77
 
78
78
  test "publishing should fail over to the next server" do
79
- failover = sequence('failover')
80
- @pub.expects(:select_next_server).in_sequence(failover)
81
- e = mock("exchange")
82
- @pub.expects(:exchange).with("mama-exchange").returns(e).in_sequence(failover)
83
- e.expects(:publish).raises(Bunny::ConnectionError).in_sequence(failover)
84
- @pub.expects(:stop!).in_sequence(failover)
85
- @pub.expects(:mark_server_dead).in_sequence(failover)
79
+ @pub.servers << "localhost:3333"
80
+ raising_exchange = mock("raising exchange")
81
+ nice_exchange = mock("nice exchange")
82
+ @pub.stubs(:exchange).with("mama-exchange").returns(raising_exchange).then.returns(nice_exchange)
83
+
84
+ raising_exchange.expects(:publish).raises(Bunny::ConnectionError)
85
+ nice_exchange.expects(:publish)
86
+ @pub.expects(:set_current_server).twice
87
+ @pub.expects(:stop!).once
88
+ @pub.expects(:mark_server_dead).once
86
89
  @pub.publish_with_failover("mama-exchange", "mama", @data, @opts)
87
90
  end
88
91
 
@@ -114,7 +117,7 @@ module Beetle
114
117
  assert_equal 1, @pub.publish_with_redundancy("mama-exchange", "mama", @data, @opts)
115
118
  end
116
119
 
117
- test "redundant publishing should return 0 if the message was published to no server" do
120
+ test "redundant publishing should raise an exception if the message was published to no server" do
118
121
  redundant = sequence("redundant")
119
122
  @pub.servers = ["someserver", "someotherserver"]
120
123
  @pub.server = "someserver"
@@ -125,7 +128,9 @@ module Beetle
125
128
  @pub.expects(:exchange).with("mama-exchange").returns(e).in_sequence(redundant)
126
129
  e.expects(:publish).raises(Bunny::ConnectionError).in_sequence(redundant)
127
130
 
128
- assert_equal 0, @pub.publish_with_redundancy("mama-exchange", "mama", @data, @opts)
131
+ assert_raises Beetle::NoMessageSent do
132
+ @pub.publish_with_redundancy("mama-exchange", "mama", @data, @opts)
133
+ end
129
134
  end
130
135
 
131
136
  test "redundant publishing should fallback to failover publishing if less than one server is available" do
@@ -10,6 +10,12 @@ module Beetle
10
10
  @client.stubs(:verify_redis_master_file_string)
11
11
  end
12
12
 
13
+ test "should send a client_started message when started" do
14
+ @client.stubs(:clear_redis_master_file)
15
+ @client.beetle.expects(:publish).with(:client_started, {:id => @client.id}.to_json)
16
+ @client.start
17
+ end
18
+
13
19
  test "config should return the beetle config" do
14
20
  assert_equal Beetle.config, @client.config
15
21
  end
@@ -59,13 +65,17 @@ module Beetle
59
65
 
60
66
  test "should clear redis master file if redis from master file is slave" do
61
67
  @client.stubs(:redis_master_from_master_file).returns(stub(:master? => false))
68
+ Beetle::Client.any_instance.stubs(:publish)
62
69
  @client.expects(:clear_redis_master_file)
70
+ @client.expects(:client_started!)
63
71
  @client.start
64
72
  end
65
73
 
66
74
  test "should clear redis master file if redis from master file is not available" do
67
75
  @client.stubs(:redis_master_from_master_file).returns(nil)
76
+ Beetle::Client.any_instance.stubs(:publish)
68
77
  @client.expects(:clear_redis_master_file)
78
+ @client.expects(:client_started!)
69
79
  @client.start
70
80
  end
71
81
 
@@ -287,10 +287,18 @@ module Beetle
287
287
 
288
288
  test "should log and send a system notification when pong message from unknown client received" do
289
289
  payload = {"id" => "unknown-client", "token" => @server.current_token}
290
- msg = "Received pong message from unknown client 'unknown-client'"
290
+ msg = "Received pong message from unknown id 'unknown-client'"
291
291
  @server.beetle.expects(:publish).with(:system_notification, ({:message => msg}).to_json)
292
292
  @server.logger.expects(:error).with(msg)
293
293
  @server.pong(payload)
294
294
  end
295
+
296
+ test "should warn about unknown clients when receiving client_started messages" do
297
+ payload = {"id" => "unknown-client"}
298
+ msg = "Received client_started message from unknown id 'unknown-client'"
299
+ @server.beetle.expects(:publish).with(:system_notification, ({:message => msg}).to_json)
300
+ @server.logger.expects(:error).with(msg)
301
+ @server.client_started(payload)
302
+ end
295
303
  end
296
304
  end
@@ -51,6 +51,19 @@ module Beetle
51
51
 
52
52
  end
53
53
 
54
+ class AdditionalSubscriptionServersTest < Test::Unit::TestCase
55
+ def setup
56
+ @config = Configuration.new
57
+ @config.additional_subscription_servers = "localhost:1234"
58
+ @client = Client.new(@config)
59
+ @sub = @client.send(:subscriber)
60
+ end
61
+
62
+ test "subscribers server list should contain addtional subcription hosts" do
63
+ assert_equal ["localhost:5672", "localhost:1234"], @sub.servers
64
+ end
65
+ end
66
+
54
67
  class SubscriberQueueManagementTest < Test::Unit::TestCase
55
68
  def setup
56
69
  @client = Client.new
@@ -143,7 +156,7 @@ module Beetle
143
156
 
144
157
  test "exceptions raised from message processing should be ignored" do
145
158
  header = header_with_params({})
146
- Message.any_instance.expects(:process).raises(Exception.new)
159
+ Message.any_instance.expects(:process).raises(Exception.new("don't worry"))
147
160
  assert_nothing_raised { @callback.call(header, 'foo') }
148
161
  end
149
162
 
@@ -214,12 +227,12 @@ module Beetle
214
227
  proc = lambda do |m|
215
228
  block_called = true
216
229
  assert_equal header, m.header
217
- assert_equal "data", m.data
230
+ assert_equal "foo", m.data
218
231
  assert_equal server, m.server
219
232
  end
220
233
  @sub.register_handler("some_queue", &proc)
221
234
  q = mock("QUEUE")
222
- q.expects(:subscribe).with({:ack => true, :key => "#"}).yields(header, 'foo')
235
+ q.expects(:subscribe).with({:ack => true, :key => "#"}).yields(header, "foo")
223
236
  @sub.expects(:queues).returns({"some_queue" => q})
224
237
  @sub.send(:subscribe, "some_queue")
225
238
  assert block_called
data/test/test_helper.rb CHANGED
@@ -3,9 +3,20 @@ require 'active_support'
3
3
  require 'active_support/testing/declarative'
4
4
  require 'test/unit'
5
5
  begin
6
- require 'redgreen' unless ENV['TM_FILENAME']
6
+ require 'redgreen' unless ENV['TM_FILENAME']
7
7
  rescue MissingSourceFile
8
8
  end
9
+
10
+ # we can remove this hack which is needed only for testing
11
+ begin
12
+ require 'qrack/errors'
13
+ rescue LoadError
14
+ module Qrack
15
+ class BufferOverflowError < StandardError; end
16
+ class InvalidTypeError < StandardError; end
17
+ end
18
+ end
19
+
9
20
  require 'mocha'
10
21
  require File.expand_path(File.dirname(__FILE__) + '/../lib/beetle')
11
22
 
metadata CHANGED
@@ -1,13 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: beetle
3
3
  version: !ruby/object:Gem::Version
4
- hash: 29
4
+ hash: 121
5
5
  prerelease: false
6
6
  segments:
7
7
  - 0
8
8
  - 2
9
- - 5
10
- version: 0.2.5
9
+ - 9
10
+ - 1
11
+ version: 0.2.9.1
11
12
  platform: ruby
12
13
  authors:
13
14
  - Stefan Kaes
@@ -18,7 +19,7 @@ autorequire:
18
19
  bindir: bin
19
20
  cert_chain: []
20
21
 
21
- date: 2010-08-23 00:00:00 +02:00
22
+ date: 2010-11-15 00:00:00 +01:00
22
23
  default_executable: beetle
23
24
  dependencies:
24
25
  - !ruby/object:Gem::Dependency
@@ -59,7 +60,7 @@ dependencies:
59
60
  requirement: &id003 !ruby/object:Gem::Requirement
60
61
  none: false
61
62
  requirements:
62
- - - ">="
63
+ - - "="
63
64
  - !ruby/object:Gem::Version
64
65
  hash: 7
65
66
  segments:
@@ -91,7 +92,7 @@ dependencies:
91
92
  requirement: &id005 !ruby/object:Gem::Requirement
92
93
  none: false
93
94
  requirements:
94
- - - ">="
95
+ - - ~>
95
96
  - !ruby/object:Gem::Version
96
97
  hash: 11
97
98
  segments:
@@ -217,6 +218,7 @@ files:
217
218
  - lib/beetle/redis_server_info.rb
218
219
  - lib/beetle/subscriber.rb
219
220
  - lib/beetle.rb
221
+ - lib/ext/qrack/client.rb
220
222
  - features/README.rdoc
221
223
  - features/redis_auto_failover.feature
222
224
  - features/step_definitions/redis_auto_failover_steps.rb
@@ -240,6 +242,7 @@ files:
240
242
  - test/beetle/client_test.rb
241
243
  - test/beetle/configuration_test.rb
242
244
  - test/beetle/deduplication_store_test.rb
245
+ - test/beetle/ext_test.rb
243
246
  - test/beetle/handler_test.rb
244
247
  - test/beetle/message_test.rb
245
248
  - test/beetle/publisher_test.rb
@@ -293,6 +296,7 @@ test_files:
293
296
  - test/beetle/client_test.rb
294
297
  - test/beetle/configuration_test.rb
295
298
  - test/beetle/deduplication_store_test.rb
299
+ - test/beetle/ext_test.rb
296
300
  - test/beetle/handler_test.rb
297
301
  - test/beetle/message_test.rb
298
302
  - test/beetle/publisher_test.rb