dkastner-hutch 0.17.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.
Files changed (46) hide show
  1. checksums.yaml +7 -0
  2. data/.gitignore +7 -0
  3. data/.travis.yml +7 -0
  4. data/CHANGELOG.md +397 -0
  5. data/Gemfile +22 -0
  6. data/Guardfile +5 -0
  7. data/LICENSE +22 -0
  8. data/README.md +315 -0
  9. data/Rakefile +14 -0
  10. data/bin/hutch +8 -0
  11. data/circle.yml +3 -0
  12. data/examples/consumer.rb +13 -0
  13. data/examples/producer.rb +10 -0
  14. data/hutch.gemspec +24 -0
  15. data/lib/hutch/broker.rb +356 -0
  16. data/lib/hutch/cli.rb +205 -0
  17. data/lib/hutch/config.rb +121 -0
  18. data/lib/hutch/consumer.rb +66 -0
  19. data/lib/hutch/error_handlers/airbrake.rb +26 -0
  20. data/lib/hutch/error_handlers/honeybadger.rb +28 -0
  21. data/lib/hutch/error_handlers/logger.rb +16 -0
  22. data/lib/hutch/error_handlers/sentry.rb +23 -0
  23. data/lib/hutch/error_handlers.rb +8 -0
  24. data/lib/hutch/exceptions.rb +7 -0
  25. data/lib/hutch/logging.rb +32 -0
  26. data/lib/hutch/message.rb +33 -0
  27. data/lib/hutch/tracers/newrelic.rb +19 -0
  28. data/lib/hutch/tracers/null_tracer.rb +15 -0
  29. data/lib/hutch/tracers.rb +6 -0
  30. data/lib/hutch/version.rb +4 -0
  31. data/lib/hutch/worker.rb +110 -0
  32. data/lib/hutch.rb +60 -0
  33. data/spec/hutch/broker_spec.rb +325 -0
  34. data/spec/hutch/cli_spec.rb +80 -0
  35. data/spec/hutch/config_spec.rb +126 -0
  36. data/spec/hutch/consumer_spec.rb +130 -0
  37. data/spec/hutch/error_handlers/airbrake_spec.rb +34 -0
  38. data/spec/hutch/error_handlers/honeybadger_spec.rb +36 -0
  39. data/spec/hutch/error_handlers/logger_spec.rb +15 -0
  40. data/spec/hutch/error_handlers/sentry_spec.rb +20 -0
  41. data/spec/hutch/logger_spec.rb +28 -0
  42. data/spec/hutch/message_spec.rb +38 -0
  43. data/spec/hutch/worker_spec.rb +98 -0
  44. data/spec/hutch_spec.rb +87 -0
  45. data/spec/spec_helper.rb +35 -0
  46. metadata +187 -0
@@ -0,0 +1,325 @@
1
+ require 'spec_helper'
2
+ require 'hutch/broker'
3
+
4
+ describe Hutch::Broker do
5
+ let(:config) { deep_copy(Hutch::Config.user_config) }
6
+ subject(:broker) { Hutch::Broker.new(config) }
7
+
8
+ describe '#connect' do
9
+ before { allow(broker).to receive(:set_up_amqp_connection) }
10
+ before { allow(broker).to receive(:set_up_api_connection) }
11
+ before { allow(broker).to receive(:disconnect) }
12
+
13
+ it 'sets up the amqp connection' do
14
+ expect(broker).to receive(:set_up_amqp_connection)
15
+ broker.connect
16
+ end
17
+
18
+ it 'sets up the api connection' do
19
+ expect(broker).to receive(:set_up_api_connection)
20
+ broker.connect
21
+ end
22
+
23
+ it 'does not disconnect' do
24
+ expect(broker).not_to receive(:disconnect)
25
+ broker.connect
26
+ end
27
+
28
+ context 'when given a block' do
29
+ it 'disconnects' do
30
+ expect(broker).to receive(:disconnect).once
31
+ broker.connect { }
32
+ end
33
+ end
34
+
35
+ context 'when given a block that fails' do
36
+ let(:exception) { Class.new(StandardError) }
37
+
38
+ it 'disconnects' do
39
+ expect(broker).to receive(:disconnect).once
40
+ expect do
41
+ broker.connect { fail exception }
42
+ end.to raise_error(exception)
43
+ end
44
+ end
45
+
46
+ context "with options" do
47
+ let(:options) { { enable_http_api_use: false } }
48
+
49
+ it "doesnt set up api" do
50
+ expect(broker).not_to receive(:set_up_api_connection)
51
+ broker.connect options
52
+ end
53
+ end
54
+ end
55
+
56
+ describe '#set_up_amqp_connection', rabbitmq: true do
57
+ context 'with valid details' do
58
+ before { broker.set_up_amqp_connection }
59
+ after { broker.disconnect }
60
+
61
+ describe '#connection' do
62
+ subject { super().connection }
63
+ it { is_expected.to be_a Bunny::Session }
64
+ end
65
+
66
+ describe '#channel' do
67
+ subject { super().channel }
68
+ it { is_expected.to be_a Bunny::Channel }
69
+ end
70
+
71
+ describe '#exchange' do
72
+ subject { super().exchange }
73
+ it { is_expected.to be_a Bunny::Exchange }
74
+ end
75
+ end
76
+
77
+ context 'when given invalid details' do
78
+ before { config[:mq_host] = 'notarealhost' }
79
+ let(:set_up_amqp_connection) { ->{ broker.set_up_amqp_connection } }
80
+
81
+ specify { expect(set_up_amqp_connection).to raise_error }
82
+ end
83
+
84
+ context 'with channel_prefetch set' do
85
+ let(:prefetch_value) { 1 }
86
+ before { config[:channel_prefetch] = prefetch_value }
87
+ after { broker.disconnect }
88
+
89
+ it "set's channel's prefetch" do
90
+ expect_any_instance_of(Bunny::Channel).
91
+ to receive(:prefetch).with(prefetch_value)
92
+ broker.set_up_amqp_connection
93
+ end
94
+ end
95
+
96
+ context 'with force_publisher_confirms set' do
97
+ let(:force_publisher_confirms_value) { true }
98
+ before { config[:force_publisher_confirms] = force_publisher_confirms_value }
99
+ after { broker.disconnect }
100
+
101
+ it 'waits for confirmation' do
102
+ expect_any_instance_of(Bunny::Channel).
103
+ to receive(:confirm_select)
104
+ broker.set_up_amqp_connection
105
+ end
106
+ end
107
+ end
108
+
109
+ describe '#set_up_api_connection', rabbitmq: true do
110
+ context 'with valid details' do
111
+ before { broker.set_up_api_connection }
112
+ after { broker.disconnect }
113
+
114
+ describe '#api_client' do
115
+ subject { super().api_client }
116
+ it { is_expected.to be_a CarrotTop }
117
+ end
118
+ end
119
+
120
+ context 'when given invalid details' do
121
+ before { config[:mq_api_host] = 'notarealhost' }
122
+ after { broker.disconnect }
123
+ let(:set_up_api_connection) { ->{ broker.set_up_api_connection } }
124
+
125
+ specify { expect(set_up_api_connection).to raise_error }
126
+ end
127
+ end
128
+
129
+ describe '#queue' do
130
+ let(:channel) { double('Channel') }
131
+ let(:arguments) { { foo: :bar } }
132
+ before { allow(broker).to receive(:channel) { channel } }
133
+
134
+ it 'applies a global namespace' do
135
+ config[:namespace] = 'mirror-all.service'
136
+ expect(broker.channel).to receive(:queue) do |*args|
137
+ args.first == ''
138
+ args.last == arguments
139
+ end
140
+ broker.queue('test', arguments)
141
+ end
142
+ end
143
+
144
+ describe '#bindings', rabbitmq: true do
145
+ around { |example| broker.connect { example.run } }
146
+ subject { broker.bindings }
147
+
148
+ context 'with no bindings' do
149
+ describe '#keys' do
150
+ subject { super().keys }
151
+ it { is_expected.not_to include 'test' }
152
+ end
153
+ end
154
+
155
+ context 'with a binding' do
156
+ around do |example|
157
+ queue = broker.queue('test').bind(broker.exchange, routing_key: 'key')
158
+ example.run
159
+ queue.unbind(broker.exchange, routing_key: 'key').delete
160
+ end
161
+
162
+ it { is_expected.to include({ 'test' => ['key'] }) }
163
+ end
164
+ end
165
+
166
+ describe '#bind_queue' do
167
+
168
+ around { |example| broker.connect { example.run } }
169
+
170
+ let(:routing_keys) { %w( a b c ) }
171
+ let(:queue) { double('Queue', bind: nil, unbind: nil, name: 'consumer') }
172
+ before { allow(broker).to receive(:bindings).and_return('consumer' => ['d']) }
173
+
174
+ it 'calls bind for each routing key' do
175
+ routing_keys.each do |key|
176
+ expect(queue).to receive(:bind).with(broker.exchange, routing_key: key)
177
+ end
178
+ broker.bind_queue(queue, routing_keys)
179
+ end
180
+
181
+ it 'calls unbind for each redundant existing binding' do
182
+ expect(queue).to receive(:unbind).with(broker.exchange, routing_key: 'd')
183
+ broker.bind_queue(queue, routing_keys)
184
+ end
185
+
186
+ context '(rabbitmq integration test)', rabbitmq: true do
187
+ let(:queue) { broker.queue('consumer') }
188
+ let(:routing_key) { 'key' }
189
+
190
+ before { allow(broker).to receive(:bindings).and_call_original }
191
+ before { queue.bind(broker.exchange, routing_key: 'redundant-key') }
192
+ after { queue.unbind(broker.exchange, routing_key: routing_key).delete }
193
+
194
+ it 'results in the correct bindings' do
195
+ broker.bind_queue(queue, [routing_key])
196
+ expect(broker.bindings).to include({ queue.name => [routing_key] })
197
+ end
198
+ end
199
+ end
200
+
201
+ describe '#wait_on_threads' do
202
+ let(:thread) { double('Thread') }
203
+ before { allow(broker).to receive(:work_pool_threads).and_return(threads) }
204
+
205
+ context 'when all threads finish within the timeout' do
206
+ let(:threads) { [double(join: thread), double(join: thread)] }
207
+ specify { expect(broker.wait_on_threads(1)).to be_truthy }
208
+ end
209
+
210
+ context 'when timeout expires for one thread' do
211
+ let(:threads) { [double(join: thread), double(join: nil)] }
212
+ specify { expect(broker.wait_on_threads(1)).to be_falsey }
213
+ end
214
+ end
215
+
216
+ describe '#stop' do
217
+ let(:thread_1) { double('Thread') }
218
+ let(:thread_2) { double('Thread') }
219
+ let(:work_pool) { double('Bunny::ConsumerWorkPool') }
220
+ let(:config) { { graceful_exit_timeout: 2 } }
221
+
222
+ before do
223
+ allow(broker).to receive(:channel_work_pool).and_return(work_pool)
224
+ end
225
+
226
+ it 'gracefully stops the work pool' do
227
+ expect(work_pool).to receive(:shutdown)
228
+ expect(work_pool).to receive(:join).with(2)
229
+ expect(work_pool).to receive(:kill)
230
+
231
+ broker.stop
232
+ end
233
+ end
234
+
235
+ describe '#publish' do
236
+ context 'with a valid connection' do
237
+ before { broker.set_up_amqp_connection }
238
+ after { broker.disconnect }
239
+
240
+ it 'publishes to the exchange' do
241
+ expect(broker.exchange).to receive(:publish).once
242
+ broker.publish('test.key', 'message')
243
+ end
244
+
245
+ it 'sets default properties' do
246
+ expect(broker.exchange).to receive(:publish).with(
247
+ JSON.dump("message"),
248
+ hash_including(
249
+ persistent: true,
250
+ routing_key: 'test.key',
251
+ content_type: 'application/json'
252
+ )
253
+ )
254
+
255
+ broker.publish('test.key', 'message')
256
+ end
257
+
258
+ it 'allows passing message properties' do
259
+ expect(broker.exchange).to receive(:publish).once
260
+ broker.publish('test.key', 'message', {expiration: "2000", persistent: false})
261
+ end
262
+
263
+ context 'when there are global properties' do
264
+ context 'as a hash' do
265
+ before do
266
+ allow(Hutch).to receive(:global_properties).and_return(app_id: 'app')
267
+ end
268
+
269
+ it 'merges the properties' do
270
+ expect(broker.exchange).
271
+ to receive(:publish).with('"message"', hash_including(app_id: 'app'))
272
+ broker.publish('test.key', 'message')
273
+ end
274
+ end
275
+
276
+ context 'as a callable object' do
277
+ before do
278
+ allow(Hutch).to receive(:global_properties).and_return(proc { { app_id: 'app' } })
279
+ end
280
+
281
+ it 'calls the proc and merges the properties' do
282
+ expect(broker.exchange).
283
+ to receive(:publish).with('"message"', hash_including(app_id: 'app'))
284
+ broker.publish('test.key', 'message')
285
+ end
286
+ end
287
+ end
288
+
289
+ context 'with force_publisher_confirms not set in the config' do
290
+ it 'does not wait for confirms on the channel' do
291
+ expect_any_instance_of(Bunny::Channel).
292
+ to_not receive(:wait_for_confirms)
293
+ broker.publish('test.key', 'message')
294
+ end
295
+ end
296
+
297
+ context 'with force_publisher_confirms set in the config' do
298
+ let(:force_publisher_confirms_value) { true }
299
+
300
+ before do
301
+ config[:force_publisher_confirms] = force_publisher_confirms_value
302
+ end
303
+
304
+ it 'waits for confirms on the channel' do
305
+ expect_any_instance_of(Bunny::Channel).
306
+ to receive(:wait_for_confirms)
307
+ broker.publish('test.key', 'message')
308
+ end
309
+ end
310
+ end
311
+
312
+ context 'without a valid connection' do
313
+ it 'raises an exception' do
314
+ expect { broker.publish('test.key', 'message') }.
315
+ to raise_exception(Hutch::PublishError)
316
+ end
317
+
318
+ it 'logs an error' do
319
+ expect(broker.logger).to receive(:error)
320
+ broker.publish('test.key', 'message') rescue nil
321
+ end
322
+ end
323
+ end
324
+ end
325
+
@@ -0,0 +1,80 @@
1
+ require 'hutch/cli'
2
+ require 'tempfile'
3
+
4
+ describe Hutch::CLI do
5
+ let(:cli) { Hutch::CLI.new }
6
+
7
+ describe "#parse_options" do
8
+ context "--config" do
9
+ context "when the config file does not exist" do
10
+ let(:file) { "/path/to/nonexistant/file" }
11
+ before { allow(STDERR).to receive(:write) }
12
+
13
+ it "bails" do
14
+ expect {
15
+ cli.parse_options(["--config=#{file}"])
16
+ }.to raise_error SystemExit
17
+ end
18
+ end
19
+
20
+ context "when the config file exists" do
21
+ let(:file) do
22
+ Tempfile.new("hutch-test-config.yaml").to_path
23
+ end
24
+
25
+ it "parses the config" do
26
+ expect(Hutch::Config).to receive(:load_from_file)
27
+ cli.parse_options(["--config=#{file}"])
28
+ end
29
+ end
30
+ end
31
+
32
+ context "--mq-tls-key" do
33
+ context "when the keyfile file does not exist" do
34
+ let(:file) { "/path/to/nonexistant/file" }
35
+ before { allow(STDERR).to receive(:write) }
36
+
37
+ it "bails" do
38
+ expect {
39
+ cli.parse_options(["--mq-tls-key=#{file}"])
40
+ }.to raise_error SystemExit
41
+ end
42
+ end
43
+
44
+ context "when the keyfile file exists" do
45
+ let(:file) do
46
+ Tempfile.new("hutch-test-key.pem").to_path
47
+ end
48
+
49
+ it "sets mq_tls_key to the file" do
50
+ expect(Hutch::Config).to receive(:mq_tls_key=)
51
+ cli.parse_options(["--mq-tls-key=#{file}"])
52
+ end
53
+ end
54
+ end
55
+
56
+ context "--mq-tls-cert" do
57
+ context "when the certfile file does not exist" do
58
+ let(:file) { "/path/to/nonexistant/file" }
59
+ before { allow(STDERR).to receive(:write) }
60
+
61
+ it "bails" do
62
+ expect {
63
+ cli.parse_options(["--mq-tls-cert=#{file}"])
64
+ }.to raise_error SystemExit
65
+ end
66
+ end
67
+
68
+ context "when the certfile file exists" do
69
+ let(:file) do
70
+ Tempfile.new("hutch-test-cert.pem").to_path
71
+ end
72
+
73
+ it "sets mq_tls_cert to the file" do
74
+ expect(Hutch::Config).to receive(:mq_tls_cert=)
75
+ cli.parse_options(["--mq-tls-cert=#{file}"])
76
+ end
77
+ end
78
+ end
79
+ end
80
+ end
@@ -0,0 +1,126 @@
1
+ require 'hutch/config'
2
+ require 'tempfile'
3
+
4
+ describe Hutch::Config do
5
+ let(:new_value) { 'not-localhost' }
6
+
7
+ describe '.get' do
8
+ context 'for valid attributes' do
9
+ subject { Hutch::Config.get(:mq_host) }
10
+
11
+ context 'with no overridden value' do
12
+ it { is_expected.to eq('localhost') }
13
+ end
14
+
15
+ context 'with an overridden value' do
16
+ before { allow(Hutch::Config).to receive_messages(user_config: { mq_host: new_value }) }
17
+ it { is_expected.to eq(new_value) }
18
+ end
19
+ end
20
+
21
+ context 'for invalid attributes' do
22
+ let(:invalid_get) { ->{ Hutch::Config.get(:invalid_attr) } }
23
+ specify { expect(invalid_get).to raise_error Hutch::UnknownAttributeError }
24
+ end
25
+ end
26
+
27
+ describe '.set' do
28
+ context 'for valid attributes' do
29
+ before { Hutch::Config.set(:mq_host, new_value) }
30
+ subject { Hutch::Config.user_config[:mq_host] }
31
+
32
+ context 'sets value in user config hash' do
33
+ it { is_expected.to eq(new_value) }
34
+ end
35
+ end
36
+
37
+ context 'for invalid attributes' do
38
+ let(:invalid_set) { ->{ Hutch::Config.set(:invalid_attr, new_value) } }
39
+ specify { expect(invalid_set).to raise_error Hutch::UnknownAttributeError }
40
+ end
41
+ end
42
+
43
+ describe 'a magic getter' do
44
+ context 'for a valid attribute' do
45
+ it 'calls get' do
46
+ expect(Hutch::Config).to receive(:get).with(:mq_host)
47
+ Hutch::Config.mq_host
48
+ end
49
+ end
50
+
51
+ context 'for an invalid attribute' do
52
+ let(:invalid_getter) { ->{ Hutch::Config.invalid_attr } }
53
+ specify { expect(invalid_getter).to raise_error NoMethodError }
54
+ end
55
+ end
56
+
57
+ describe 'a magic setter' do
58
+ context 'for a valid attribute' do
59
+ it 'calls set' do
60
+ expect(Hutch::Config).to receive(:set).with(:mq_host, new_value)
61
+ Hutch::Config.mq_host = new_value
62
+ end
63
+ end
64
+
65
+ context 'for an invalid attribute' do
66
+ let(:invalid_setter) { ->{ Hutch::Config.invalid_attr = new_value } }
67
+ specify { expect(invalid_setter).to raise_error NoMethodError }
68
+ end
69
+ end
70
+
71
+ describe '.load_from_file' do
72
+ let(:host) { 'broker.yourhost.com' }
73
+ let(:username) { 'calvin' }
74
+ let(:file) do
75
+ Tempfile.new('configs.yaml').tap do |t|
76
+ t.write(YAML.dump(config_data))
77
+ t.rewind
78
+ end
79
+ end
80
+
81
+ context 'when an attribute is invalid' do
82
+ let(:config_data) { { random_attribute: 'socks' } }
83
+ it 'raises an error' do
84
+ expect {
85
+ Hutch::Config.load_from_file(file)
86
+ }.to raise_error(NoMethodError)
87
+ end
88
+ end
89
+
90
+ context 'when attributes are valid' do
91
+ let(:config_data) { { mq_host: host, mq_username: username } }
92
+
93
+ it 'loads in the config data' do
94
+ Hutch::Config.load_from_file(file)
95
+ expect(Hutch::Config.mq_host).to eq host
96
+ expect(Hutch::Config.mq_username).to eq username
97
+ end
98
+ end
99
+ end
100
+
101
+ describe '.load_from_file' do
102
+ let(:host) { 'localhost' }
103
+ let(:username) { 'calvin' }
104
+ let(:file) do
105
+ Tempfile.new('configs.yaml').tap do |t|
106
+ t.write(config_contents)
107
+ t.rewind
108
+ end
109
+ end
110
+
111
+ context 'when using ERb' do
112
+ let(:config_contents) do
113
+ <<-YAML
114
+ mq_host: 'localhost'
115
+ mq_username: '<%= "calvin" %>'
116
+ YAML
117
+ end
118
+
119
+ it 'loads in the config data' do
120
+ Hutch::Config.load_from_file(file)
121
+ expect(Hutch::Config.mq_host).to eq host
122
+ expect(Hutch::Config.mq_username).to eq username
123
+ end
124
+ end
125
+ end
126
+ end
@@ -0,0 +1,130 @@
1
+ require 'spec_helper'
2
+
3
+ describe Hutch::Consumer do
4
+ around(:each) do |example|
5
+ isolate_constants do
6
+ example.run
7
+ end
8
+ end
9
+
10
+ let(:simple_consumer) do
11
+ unless defined? SimpleConsumer
12
+ class SimpleConsumer
13
+ include Hutch::Consumer
14
+ consume 'hutch.test1'
15
+ end
16
+ end
17
+ SimpleConsumer
18
+ end
19
+
20
+ let(:complex_consumer) do
21
+ unless defined? ComplexConsumer
22
+ class ComplexConsumer
23
+ include Hutch::Consumer
24
+ consume 'hutch.test1', 'hutch.test2'
25
+ arguments foo: :bar
26
+ end
27
+ end
28
+ ComplexConsumer
29
+ end
30
+
31
+ describe 'module inclusion' do
32
+ it 'registers the class as a consumer' do
33
+ expect(Hutch).to receive(:register_consumer) do |klass|
34
+ expect(klass).to eq(simple_consumer)
35
+ end
36
+
37
+ simple_consumer
38
+ end
39
+ end
40
+
41
+
42
+ describe '.consume' do
43
+ it 'saves the routing key to the consumer' do
44
+ expect(simple_consumer.routing_keys).to include 'hutch.test1'
45
+ end
46
+
47
+ context 'with multiple routing keys' do
48
+ it 'registers the class once for each routing key' do
49
+ expect(complex_consumer.routing_keys).to include 'hutch.test1'
50
+ expect(complex_consumer.routing_keys).to include 'hutch.test2'
51
+ end
52
+ end
53
+
54
+ context 'when given the same routing key multiple times' do
55
+ subject { simple_consumer.routing_keys }
56
+ before { simple_consumer.consume 'hutch.test1' }
57
+
58
+ describe '#length' do
59
+ subject { super().length }
60
+ it { is_expected.to eq(1)}
61
+ end
62
+ end
63
+ end
64
+
65
+ describe '.queue_name' do
66
+ let(:queue_name) { 'foo' }
67
+
68
+ it 'overrides the queue name' do
69
+ simple_consumer.queue_name(queue_name)
70
+ expect(simple_consumer.get_queue_name).to eq(queue_name)
71
+ end
72
+ end
73
+
74
+ describe '.arguments' do
75
+ let(:args) { { foo: :bar} }
76
+
77
+ it 'overrides the arguments' do
78
+ simple_consumer.arguments(args)
79
+ expect(simple_consumer.get_arguments).to eq(args)
80
+ end
81
+
82
+ end
83
+
84
+ describe '.get_arguments' do
85
+
86
+ context 'when defined' do
87
+ it { expect(complex_consumer.get_arguments).to eq(foo: :bar) }
88
+ end
89
+
90
+ context 'when not defined' do
91
+ it { expect(simple_consumer.get_arguments).to eq({}) }
92
+ end
93
+
94
+ end
95
+
96
+ describe '.get_queue_name' do
97
+
98
+ context 'when queue name has been set explicitly' do
99
+ it 'returns the give queue name' do
100
+ class Foo
101
+ include Hutch::Consumer
102
+ queue_name "bar"
103
+ end
104
+
105
+ expect(Foo.get_queue_name).to eq("bar")
106
+ end
107
+ end
108
+
109
+ context 'when no queue name has been set' do
110
+ it 'replaces module separators with colons' do
111
+ module Foo
112
+ class Bar
113
+ include Hutch::Consumer
114
+ end
115
+ end
116
+
117
+ expect(Foo::Bar.get_queue_name).to eq('foo:bar')
118
+ end
119
+
120
+ it 'converts camelcase class names to snake case' do
121
+ class FooBarBAZ
122
+ include Hutch::Consumer
123
+ end
124
+
125
+ expect(FooBarBAZ.get_queue_name).to eq('foo_bar_baz')
126
+ end
127
+ end
128
+ end
129
+ end
130
+
@@ -0,0 +1,34 @@
1
+ require 'spec_helper'
2
+
3
+ describe Hutch::ErrorHandlers::Airbrake do
4
+ let(:error_handler) { Hutch::ErrorHandlers::Airbrake.new }
5
+
6
+ describe '#handle' do
7
+ let(:error) do
8
+ begin
9
+ raise "Stuff went wrong"
10
+ rescue RuntimeError => err
11
+ err
12
+ end
13
+ end
14
+
15
+ it "logs the error to Airbrake" do
16
+ message_id = "1"
17
+ payload = "{}"
18
+ consumer = double
19
+ ex = error
20
+ message = {
21
+ :error_class => ex.class.name,
22
+ :error_message => "#{ ex.class.name }: #{ ex.message }",
23
+ :backtrace => ex.backtrace,
24
+ :parameters => {
25
+ :payload => payload,
26
+ :consumer => consumer,
27
+ },
28
+ :cgi_data => ENV.to_hash,
29
+ }
30
+ expect(::Airbrake).to receive(:notify_or_ignore).with(ex, message)
31
+ error_handler.handle(message_id, payload, consumer, ex)
32
+ end
33
+ end
34
+ end