hutch 0.25.0-java → 2.0.0.rc1-java
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.
- checksums.yaml +4 -4
- data/CHANGELOG.md +402 -5
- data/LICENSE +1 -0
- data/README.md +157 -47
- data/bin/ci/before_build.sh +20 -0
- data/bin/ci/before_build_docker.sh +15 -0
- data/bin/ci/install_on_debian.sh +46 -0
- data/lib/hutch/acknowledgements/nack_on_all_failures.rb +1 -1
- data/lib/hutch/adapters/bunny.rb +29 -1
- data/lib/hutch/adapters/march_hare.rb +24 -0
- data/lib/hutch/broker.rb +98 -35
- data/lib/hutch/cli.rb +16 -12
- data/lib/hutch/config.rb +42 -13
- data/lib/hutch/consumer.rb +63 -4
- data/lib/hutch/error_handlers/bugsnag.rb +30 -0
- data/lib/hutch/error_handlers/{opbeat.rb → rollbar.rb} +11 -13
- data/lib/hutch/error_handlers/sentry.rb +6 -11
- data/lib/hutch/error_handlers.rb +2 -1
- data/lib/hutch/publisher.rb +1 -1
- data/lib/hutch/serializers/json.rb +3 -3
- data/lib/hutch/tracers/datadog.rb +18 -0
- data/lib/hutch/tracers.rb +1 -1
- data/lib/hutch/version.rb +1 -2
- data/lib/hutch/worker.rb +46 -9
- data/lib/hutch.rb +8 -4
- data/lib/yard-settings/yard-settings.rb +2 -2
- data/spec/hutch/broker_spec.rb +192 -14
- data/spec/hutch/cli_spec.rb +12 -9
- data/spec/hutch/config_spec.rb +84 -12
- data/spec/hutch/consumer_spec.rb +93 -4
- data/spec/hutch/error_handlers/airbrake_spec.rb +4 -1
- data/spec/hutch/error_handlers/bugsnag_spec.rb +56 -0
- data/spec/hutch/error_handlers/honeybadger_spec.rb +1 -1
- data/spec/hutch/error_handlers/logger_spec.rb +1 -1
- data/spec/hutch/error_handlers/rollbar_spec.rb +45 -0
- data/spec/hutch/error_handlers/sentry_spec.rb +13 -3
- data/spec/hutch/message_spec.rb +1 -1
- data/spec/hutch/tracers/datadog_spec.rb +46 -0
- data/spec/hutch/waiter_spec.rb +2 -2
- data/spec/hutch/worker_spec.rb +138 -24
- data/spec/integration/channel_recovery_spec.rb +187 -0
- data/spec/integration/publish_consume_spec.rb +47 -0
- metadata +32 -59
- data/.gitignore +0 -10
- data/.rspec +0 -1
- data/.travis.yml +0 -16
- data/.yardopts +0 -5
- data/Gemfile +0 -33
- data/Guardfile +0 -14
- data/Rakefile +0 -21
- data/examples/consumer.rb +0 -13
- data/examples/producer.rb +0 -10
- data/hutch.gemspec +0 -29
- data/lib/hutch/tracers/opbeat.rb +0 -37
- data/spec/hutch/error_handlers/opbeat_spec.rb +0 -37
- data/spec/spec_helper.rb +0 -42
- data/spec/tracers/opbeat_spec.rb +0 -44
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
require 'datadog'
|
|
2
|
+
require 'datadog/auto_instrument'
|
|
3
|
+
|
|
4
|
+
module Hutch
|
|
5
|
+
module Tracers
|
|
6
|
+
class Datadog
|
|
7
|
+
def initialize(klass)
|
|
8
|
+
@klass = klass
|
|
9
|
+
end
|
|
10
|
+
|
|
11
|
+
def handle(message)
|
|
12
|
+
::Datadog::Tracing.trace(@klass.class.name, continue_from: nil, service: 'hutch', type: 'rabbitmq') do
|
|
13
|
+
@klass.process(message)
|
|
14
|
+
end
|
|
15
|
+
end
|
|
16
|
+
end
|
|
17
|
+
end
|
|
18
|
+
end
|
data/lib/hutch/tracers.rb
CHANGED
data/lib/hutch/version.rb
CHANGED
data/lib/hutch/worker.rb
CHANGED
|
@@ -11,8 +11,9 @@ module Hutch
|
|
|
11
11
|
include Logging
|
|
12
12
|
|
|
13
13
|
def initialize(broker, consumers, setup_procs)
|
|
14
|
-
@broker
|
|
15
|
-
|
|
14
|
+
@broker = broker
|
|
15
|
+
@recovery_lock = Mutex.new
|
|
16
|
+
self.consumers = consumers
|
|
16
17
|
self.setup_procs = setup_procs
|
|
17
18
|
end
|
|
18
19
|
|
|
@@ -45,17 +46,53 @@ module Hutch
|
|
|
45
46
|
# Bind a consumer's routing keys to its queue, and set up a subscription to
|
|
46
47
|
# receive messages sent to the queue.
|
|
47
48
|
def setup_queue(consumer)
|
|
48
|
-
|
|
49
|
+
queue_name = consumer.get_queue_name
|
|
50
|
+
queue_name = @broker.namespaced_queue_name(queue_name) unless consumer.without_namespace?
|
|
51
|
+
logger.info "setting up queue: #{queue_name}"
|
|
49
52
|
|
|
50
|
-
queue = @broker.queue(
|
|
53
|
+
queue = @broker.queue(queue_name, consumer.get_options)
|
|
51
54
|
@broker.bind_queue(queue, consumer.routing_keys)
|
|
52
55
|
|
|
53
|
-
|
|
56
|
+
on_cancellation = proc { handle_cancellation(queue_name, queue.channel) }
|
|
57
|
+
queue.subscribe(consumer_tag: unique_consumer_tag, manual_ack: true,
|
|
58
|
+
on_cancellation: on_cancellation) do |*args|
|
|
54
59
|
delivery_info, properties, payload = Hutch::Adapter.decode_message(*args)
|
|
55
60
|
handle_message(consumer, delivery_info, properties, payload)
|
|
56
61
|
end
|
|
57
62
|
end
|
|
58
63
|
|
|
64
|
+
# A server-sent `basic.cancel` carries no reason, so a consumer timeout
|
|
65
|
+
# is indistinguishable from a queue deletion. Only the former is recoverable.
|
|
66
|
+
def handle_cancellation(queue_name, cancelled_channel)
|
|
67
|
+
unless @broker.queue_exists?(queue_name)
|
|
68
|
+
logger.error "consumer on queue #{queue_name} was cancelled by the server: the queue no longer exists"
|
|
69
|
+
return
|
|
70
|
+
end
|
|
71
|
+
|
|
72
|
+
logger.warn "consumer on queue #{queue_name} was cancelled by the server, re-subscribing"
|
|
73
|
+
resubscribe_on_a_new_channel(cancelled_channel)
|
|
74
|
+
rescue => ex
|
|
75
|
+
logger.error "consumer re-subscription failed: #{ex.class}: #{ex.message}"
|
|
76
|
+
end
|
|
77
|
+
|
|
78
|
+
# Runs on its own thread: closing the channel kills the consumer work
|
|
79
|
+
# pool this callback runs on. Returns the thread so that tests can join it.
|
|
80
|
+
def resubscribe_on_a_new_channel(cancelled_channel)
|
|
81
|
+
Thread.new do
|
|
82
|
+
begin
|
|
83
|
+
@recovery_lock.synchronize do
|
|
84
|
+
# All consumers share the channel, so only the first cancellation replaces it.
|
|
85
|
+
next unless @broker.channel.equal?(cancelled_channel)
|
|
86
|
+
|
|
87
|
+
@broker.replace_channel!
|
|
88
|
+
setup_queues
|
|
89
|
+
end
|
|
90
|
+
rescue => ex
|
|
91
|
+
logger.error "consumer re-subscription failed: #{ex.class}: #{ex.message}"
|
|
92
|
+
end
|
|
93
|
+
end
|
|
94
|
+
end
|
|
95
|
+
|
|
59
96
|
# Called internally when a new messages comes in from RabbitMQ. Responsible
|
|
60
97
|
# for wrapping up the message and passing it to the consumer.
|
|
61
98
|
def handle_message(consumer, delivery_info, properties, payload)
|
|
@@ -71,10 +108,10 @@ module Hutch
|
|
|
71
108
|
message = Message.new(delivery_info, properties, payload, serializer)
|
|
72
109
|
consumer_instance = consumer.new.tap { |c| c.broker, c.delivery_info = @broker, delivery_info }
|
|
73
110
|
with_tracing(consumer_instance).handle(message)
|
|
74
|
-
@broker.ack(delivery_info.delivery_tag)
|
|
111
|
+
@broker.ack(delivery_info.delivery_tag, channel: delivery_info.channel) unless consumer_instance.message_rejected?
|
|
75
112
|
rescue => ex
|
|
76
113
|
acknowledge_error(delivery_info, properties, @broker, ex)
|
|
77
|
-
handle_error(properties, payload, consumer, ex)
|
|
114
|
+
handle_error(properties, payload, consumer, ex, delivery_info)
|
|
78
115
|
end
|
|
79
116
|
|
|
80
117
|
def with_tracing(klass)
|
|
@@ -83,7 +120,7 @@ module Hutch
|
|
|
83
120
|
|
|
84
121
|
def handle_error(*args)
|
|
85
122
|
Hutch::Config[:error_handlers].each do |backend|
|
|
86
|
-
backend.handle
|
|
123
|
+
backend.handle *args.first(backend.method(:handle).arity)
|
|
87
124
|
end
|
|
88
125
|
end
|
|
89
126
|
|
|
@@ -118,7 +155,7 @@ module Hutch
|
|
|
118
155
|
def group_restricted?(consumer)
|
|
119
156
|
consumers_to_load = consumer_groups[group]
|
|
120
157
|
if consumers_to_load
|
|
121
|
-
!
|
|
158
|
+
!consumers_to_load.include?(consumer.name)
|
|
122
159
|
else
|
|
123
160
|
true
|
|
124
161
|
end
|
data/lib/hutch.rb
CHANGED
|
@@ -14,6 +14,8 @@ require 'hutch/exceptions'
|
|
|
14
14
|
require 'hutch/tracers'
|
|
15
15
|
|
|
16
16
|
module Hutch
|
|
17
|
+
@@connection_mutex = Mutex.new
|
|
18
|
+
|
|
17
19
|
def self.register_consumer(consumer)
|
|
18
20
|
self.consumers << consumer
|
|
19
21
|
end
|
|
@@ -40,10 +42,12 @@ module Hutch
|
|
|
40
42
|
# @param config [Hash] Configuration
|
|
41
43
|
# @option options [Boolean] :enable_http_api_use
|
|
42
44
|
def self.connect(options = {}, config = Hutch::Config)
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
45
|
+
@@connection_mutex.synchronize do
|
|
46
|
+
unless connected?
|
|
47
|
+
@broker = Hutch::Broker.new(config)
|
|
48
|
+
@broker.connect(options)
|
|
49
|
+
end
|
|
50
|
+
end
|
|
47
51
|
end
|
|
48
52
|
|
|
49
53
|
def self.disconnect
|
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
YARD::Templates::Engine.register_template_path(File.
|
|
2
|
-
|
|
1
|
+
YARD::Templates::Engine.register_template_path(File.join(__dir__, '../../templates'))
|
|
2
|
+
require_relative 'handler'
|
data/spec/hutch/broker_spec.rb
CHANGED
|
@@ -95,7 +95,7 @@ describe Hutch::Broker do
|
|
|
95
95
|
|
|
96
96
|
context 'when configured with a URI' do
|
|
97
97
|
context 'which specifies the port' do
|
|
98
|
-
before { config[:uri] =
|
|
98
|
+
before { config[:uri] = "amqp://#{config[:mq_username]}:#{config[:mq_password]}@#{config[:mq_host]}:#{config[:mq_port]}/" }
|
|
99
99
|
|
|
100
100
|
it 'successfully connects' do
|
|
101
101
|
c = broker.open_connection
|
|
@@ -105,7 +105,7 @@ describe Hutch::Broker do
|
|
|
105
105
|
end
|
|
106
106
|
|
|
107
107
|
context 'which does not specify port and uses the amqp scheme' do
|
|
108
|
-
before { config[:uri] =
|
|
108
|
+
before { config[:uri] = "amqp://#{config[:mq_username]}:#{config[:mq_password]}@#{config[:mq_host]}/" }
|
|
109
109
|
|
|
110
110
|
it 'successfully connects' do
|
|
111
111
|
c = broker.open_connection
|
|
@@ -115,11 +115,11 @@ describe Hutch::Broker do
|
|
|
115
115
|
end
|
|
116
116
|
|
|
117
117
|
context 'which specifies the amqps scheme' do
|
|
118
|
-
before { config[:uri] =
|
|
118
|
+
before { config[:uri] = "amqps://#{config[:mq_username]}:#{config[:mq_password]}@#{config[:mq_host]}/" }
|
|
119
119
|
|
|
120
120
|
it 'utilises TLS' do
|
|
121
121
|
expect(Hutch::Adapter).to receive(:new).with(
|
|
122
|
-
hash_including(tls: true
|
|
122
|
+
hash_including(tls: true)
|
|
123
123
|
).and_return(instance_double('Hutch::Adapter', start: nil))
|
|
124
124
|
|
|
125
125
|
broker.open_connection
|
|
@@ -128,6 +128,76 @@ describe Hutch::Broker do
|
|
|
128
128
|
end
|
|
129
129
|
end
|
|
130
130
|
|
|
131
|
+
describe '#parse_uri' do
|
|
132
|
+
{
|
|
133
|
+
"amqp://host/vhost" => "vhost",
|
|
134
|
+
"amqp://host/v%2fhost" => "v/host",
|
|
135
|
+
"amqp://host/%2f" => "/",
|
|
136
|
+
"amqp://host/" => Hutch::Adapter::DEFAULT_VHOST,
|
|
137
|
+
"amqp://host" => Hutch::Adapter::DEFAULT_VHOST,
|
|
138
|
+
}.each do |uri, vhost|
|
|
139
|
+
it "parses the vhost of #{uri} as #{vhost.inspect}" do
|
|
140
|
+
config[:uri] = uri
|
|
141
|
+
broker.send(:parse_uri)
|
|
142
|
+
|
|
143
|
+
expect(config[:mq_vhost]).to eq(vhost)
|
|
144
|
+
end
|
|
145
|
+
end
|
|
146
|
+
|
|
147
|
+
it 'passes the vhost to the connection unchanged' do
|
|
148
|
+
config[:uri] = "amqp://host/%2f"
|
|
149
|
+
|
|
150
|
+
expect(broker.send(:connection_params)[:vhost]).to eq("/")
|
|
151
|
+
end
|
|
152
|
+
|
|
153
|
+
it 'percent-decodes the username and password' do
|
|
154
|
+
config[:uri] = "amqp://al%23pha:be%20ta@host:10000/vhost"
|
|
155
|
+
broker.send(:parse_uri)
|
|
156
|
+
|
|
157
|
+
expect(config[:mq_username]).to eq("al#pha")
|
|
158
|
+
expect(config[:mq_password]).to eq("be ta")
|
|
159
|
+
end
|
|
160
|
+
|
|
161
|
+
it 'leaves absent credentials nil' do
|
|
162
|
+
config[:uri] = "amqp://host:10000/vhost"
|
|
163
|
+
broker.send(:parse_uri)
|
|
164
|
+
|
|
165
|
+
expect(config[:mq_username]).to be_nil
|
|
166
|
+
expect(config[:mq_password]).to be_nil
|
|
167
|
+
end
|
|
168
|
+
end
|
|
169
|
+
|
|
170
|
+
describe '#bindings', 'vhost filtering' do
|
|
171
|
+
let(:api_bindings) do
|
|
172
|
+
[{ 'source' => 'hutch', 'vhost' => '/', 'destination' => 'q', 'routing_key' => 'key' }]
|
|
173
|
+
end
|
|
174
|
+
|
|
175
|
+
before do
|
|
176
|
+
config[:mq_exchange] = 'hutch'
|
|
177
|
+
broker.api_client = double('api_client', bindings: api_bindings)
|
|
178
|
+
end
|
|
179
|
+
|
|
180
|
+
it 'filters on the default vhost when the configured vhost is blank' do
|
|
181
|
+
config[:mq_vhost] = ''
|
|
182
|
+
|
|
183
|
+
expect(broker.bindings).to eq('q' => ['key'])
|
|
184
|
+
end
|
|
185
|
+
end
|
|
186
|
+
|
|
187
|
+
describe '#sanitized_uri' do
|
|
188
|
+
it 'percent-encodes the decoded username and vhost again' do
|
|
189
|
+
config[:uri] = 'amqp://al%23pha:be%20ta@host:10000/v%2Fhost'
|
|
190
|
+
|
|
191
|
+
expect(broker.send(:sanitized_uri)).to eq('amqp://al%23pha@host:10000/v%2Fhost')
|
|
192
|
+
end
|
|
193
|
+
|
|
194
|
+
it 'renders the default vhost as a bare trailing slash' do
|
|
195
|
+
config[:uri] = 'amqp://alpha:beta@host:10000/'
|
|
196
|
+
|
|
197
|
+
expect(broker.send(:sanitized_uri)).to eq('amqp://alpha@host:10000/')
|
|
198
|
+
end
|
|
199
|
+
end
|
|
200
|
+
|
|
131
201
|
describe '#open_connection!' do
|
|
132
202
|
it 'sets the #connection to #open_connection' do
|
|
133
203
|
connection = double('connection').as_null_object
|
|
@@ -199,6 +269,100 @@ describe Hutch::Broker do
|
|
|
199
269
|
end
|
|
200
270
|
end
|
|
201
271
|
|
|
272
|
+
describe '#replace_channel!', adapter: :bunny do
|
|
273
|
+
let(:old_channel) { double('Bunny::Channel', open?: true, close: nil) }
|
|
274
|
+
let(:new_channel) { double('Bunny::Channel').as_null_object }
|
|
275
|
+
let(:work_pool) { double('Bunny::ConsumerWorkPool', shutdown: nil, join: nil, kill: nil) }
|
|
276
|
+
|
|
277
|
+
before do
|
|
278
|
+
broker.channel = old_channel
|
|
279
|
+
allow(broker).to receive(:channel_work_pool).and_return(work_pool)
|
|
280
|
+
allow(broker).to receive(:open_channel).and_return(new_channel)
|
|
281
|
+
allow(broker).to receive(:declare_exchange!)
|
|
282
|
+
allow(broker).to receive(:declare_publisher!)
|
|
283
|
+
end
|
|
284
|
+
|
|
285
|
+
it 'drains the work pool before closing the old channel' do
|
|
286
|
+
expect(work_pool).to receive(:shutdown).ordered
|
|
287
|
+
expect(work_pool).to receive(:join).with(config[:graceful_exit_timeout]).ordered
|
|
288
|
+
expect(work_pool).to receive(:kill).ordered
|
|
289
|
+
expect(old_channel).to receive(:close).ordered
|
|
290
|
+
|
|
291
|
+
broker.replace_channel!
|
|
292
|
+
end
|
|
293
|
+
|
|
294
|
+
it 'redeclares the exchange and the publisher on the new channel' do
|
|
295
|
+
expect(broker).to receive(:declare_exchange!)
|
|
296
|
+
expect(broker).to receive(:declare_publisher!)
|
|
297
|
+
|
|
298
|
+
broker.replace_channel!
|
|
299
|
+
|
|
300
|
+
expect(broker.channel).to eq(new_channel)
|
|
301
|
+
end
|
|
302
|
+
|
|
303
|
+
context 'when the old channel is already closed' do
|
|
304
|
+
let(:old_channel) { double('Bunny::Channel', open?: false) }
|
|
305
|
+
|
|
306
|
+
it 'opens a new channel anyway' do
|
|
307
|
+
expect(old_channel).not_to receive(:close)
|
|
308
|
+
expect(work_pool).not_to receive(:shutdown)
|
|
309
|
+
|
|
310
|
+
broker.replace_channel!
|
|
311
|
+
|
|
312
|
+
expect(broker.channel).to eq(new_channel)
|
|
313
|
+
end
|
|
314
|
+
end
|
|
315
|
+
|
|
316
|
+
context 'when the server closes the old channel concurrently' do
|
|
317
|
+
it 'opens a new channel anyway' do
|
|
318
|
+
allow(old_channel).to receive(:close).
|
|
319
|
+
and_raise(Hutch::Adapter::ChannelAlreadyClosed.new('already closed', old_channel))
|
|
320
|
+
|
|
321
|
+
broker.replace_channel!
|
|
322
|
+
|
|
323
|
+
expect(broker.channel).to eq(new_channel)
|
|
324
|
+
end
|
|
325
|
+
end
|
|
326
|
+
end
|
|
327
|
+
|
|
328
|
+
describe 'acknowledgements' do
|
|
329
|
+
let(:current_channel) { double('Channel').as_null_object }
|
|
330
|
+
let(:superseded_channel) { double('Channel') }
|
|
331
|
+
|
|
332
|
+
before { broker.channel = current_channel }
|
|
333
|
+
|
|
334
|
+
it 'sends them on the channel the delivery arrived on' do
|
|
335
|
+
expect(current_channel).to receive(:ack).with('dt', false)
|
|
336
|
+
broker.ack('dt', channel: current_channel)
|
|
337
|
+
|
|
338
|
+
expect(current_channel).to receive(:nack).with('dt', false, false)
|
|
339
|
+
broker.nack('dt', channel: current_channel)
|
|
340
|
+
|
|
341
|
+
expect(current_channel).to receive(:reject).with('dt', false)
|
|
342
|
+
broker.reject('dt', channel: current_channel)
|
|
343
|
+
|
|
344
|
+
expect(current_channel).to receive(:reject).with('dt', true)
|
|
345
|
+
broker.requeue('dt', channel: current_channel)
|
|
346
|
+
end
|
|
347
|
+
|
|
348
|
+
it 'drops them when the channel has been replaced' do
|
|
349
|
+
expect(superseded_channel).not_to receive(:ack)
|
|
350
|
+
expect(superseded_channel).not_to receive(:nack)
|
|
351
|
+
expect(superseded_channel).not_to receive(:reject)
|
|
352
|
+
|
|
353
|
+
broker.ack('dt', channel: superseded_channel)
|
|
354
|
+
broker.nack('dt', channel: superseded_channel)
|
|
355
|
+
broker.reject('dt', channel: superseded_channel)
|
|
356
|
+
broker.requeue('dt', channel: superseded_channel)
|
|
357
|
+
end
|
|
358
|
+
|
|
359
|
+
it 'defaults to the current channel' do
|
|
360
|
+
expect(current_channel).to receive(:ack).with('dt', false)
|
|
361
|
+
|
|
362
|
+
broker.ack('dt')
|
|
363
|
+
end
|
|
364
|
+
end
|
|
365
|
+
|
|
202
366
|
describe '#declare_exchange' do
|
|
203
367
|
before do
|
|
204
368
|
broker.open_connection!
|
|
@@ -245,9 +409,9 @@ describe Hutch::Broker do
|
|
|
245
409
|
context 'when given invalid details' do
|
|
246
410
|
before { config[:mq_api_host] = 'notarealhost' }
|
|
247
411
|
after { broker.disconnect }
|
|
248
|
-
let(:set_up_api_connection) {
|
|
412
|
+
let(:set_up_api_connection) { broker.set_up_api_connection }
|
|
249
413
|
|
|
250
|
-
specify { expect
|
|
414
|
+
specify { expect { broker.set_up_api_connection }.to raise_error(StandardError) }
|
|
251
415
|
end
|
|
252
416
|
end
|
|
253
417
|
|
|
@@ -256,13 +420,27 @@ describe Hutch::Broker do
|
|
|
256
420
|
let(:arguments) { { foo: :bar } }
|
|
257
421
|
before { allow(broker).to receive(:channel) { channel } }
|
|
258
422
|
|
|
259
|
-
it '
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
423
|
+
it 'creates a queue with the given name' do
|
|
424
|
+
expect(broker.channel).to receive(:queue).with('test', arguments: arguments)
|
|
425
|
+
broker.queue('test'.freeze, arguments: arguments)
|
|
426
|
+
end
|
|
427
|
+
end
|
|
428
|
+
|
|
429
|
+
describe '#namespaced_queue_name' do
|
|
430
|
+
context 'with a namespace configured' do
|
|
431
|
+
before { config[:namespace] = 'mirror-all.service' }
|
|
432
|
+
|
|
433
|
+
it 'prepends the namespace' do
|
|
434
|
+
expect(broker.namespaced_queue_name('test')).to eq('mirror-all.service:test')
|
|
435
|
+
end
|
|
436
|
+
end
|
|
437
|
+
|
|
438
|
+
context 'without a namespace configured' do
|
|
439
|
+
before { config[:namespace] = nil }
|
|
440
|
+
|
|
441
|
+
it 'returns the name unchanged' do
|
|
442
|
+
expect(broker.namespaced_queue_name('test')).to eq('test')
|
|
264
443
|
end
|
|
265
|
-
broker.queue('test', arguments)
|
|
266
444
|
end
|
|
267
445
|
end
|
|
268
446
|
|
|
@@ -279,7 +457,7 @@ describe Hutch::Broker do
|
|
|
279
457
|
|
|
280
458
|
context 'with a binding' do
|
|
281
459
|
around do |example|
|
|
282
|
-
queue = broker.queue('test').bind(broker.exchange, routing_key: 'key')
|
|
460
|
+
queue = broker.queue('test', durable: true).bind(broker.exchange, routing_key: 'key')
|
|
283
461
|
example.run
|
|
284
462
|
queue.unbind(broker.exchange, routing_key: 'key').delete
|
|
285
463
|
end
|
|
@@ -309,7 +487,7 @@ describe Hutch::Broker do
|
|
|
309
487
|
end
|
|
310
488
|
|
|
311
489
|
context '(rabbitmq integration test)', rabbitmq: true do
|
|
312
|
-
let(:queue) { broker.queue('consumer') }
|
|
490
|
+
let(:queue) { broker.queue('consumer', durable: true) }
|
|
313
491
|
let(:routing_key) { 'key' }
|
|
314
492
|
|
|
315
493
|
before { allow(broker).to receive(:bindings).and_call_original }
|
data/spec/hutch/cli_spec.rb
CHANGED
|
@@ -31,9 +31,10 @@ describe Hutch::CLI do
|
|
|
31
31
|
end
|
|
32
32
|
|
|
33
33
|
context "when the config file exists" do
|
|
34
|
-
let
|
|
35
|
-
|
|
36
|
-
|
|
34
|
+
# Kept in a let, so the Tempfile is not garbage collected - and with it
|
|
35
|
+
# unlinked - before the CLI checks that the path exists.
|
|
36
|
+
let(:config_file) { Tempfile.new("hutch-test-config.yaml") }
|
|
37
|
+
let(:file) { config_file.to_path }
|
|
37
38
|
|
|
38
39
|
it "parses the config" do
|
|
39
40
|
expect(Hutch::Config).to receive(:load_from_file)
|
|
@@ -55,9 +56,10 @@ describe Hutch::CLI do
|
|
|
55
56
|
end
|
|
56
57
|
|
|
57
58
|
context "when the keyfile file exists" do
|
|
58
|
-
let
|
|
59
|
-
|
|
60
|
-
|
|
59
|
+
# Kept in a let, so the Tempfile is not garbage collected - and with it
|
|
60
|
+
# unlinked - before the CLI checks that the path exists.
|
|
61
|
+
let(:key_file) { Tempfile.new("hutch-test-key.pem") }
|
|
62
|
+
let(:file) { key_file.to_path }
|
|
61
63
|
|
|
62
64
|
it "sets mq_tls_key to the file" do
|
|
63
65
|
expect(Hutch::Config).to receive(:mq_tls_key=)
|
|
@@ -79,9 +81,10 @@ describe Hutch::CLI do
|
|
|
79
81
|
end
|
|
80
82
|
|
|
81
83
|
context "when the certfile file exists" do
|
|
82
|
-
let
|
|
83
|
-
|
|
84
|
-
|
|
84
|
+
# Kept in a let, so the Tempfile is not garbage collected - and with it
|
|
85
|
+
# unlinked - before the CLI checks that the path exists.
|
|
86
|
+
let(:cert_file) { Tempfile.new("hutch-test-cert.pem") }
|
|
87
|
+
let(:file) { cert_file.to_path }
|
|
85
88
|
|
|
86
89
|
it "sets mq_tls_cert to the file" do
|
|
87
90
|
expect(Hutch::Config).to receive(:mq_tls_cert=)
|
data/spec/hutch/config_spec.rb
CHANGED
|
@@ -31,12 +31,10 @@ describe Hutch::Config do
|
|
|
31
31
|
end
|
|
32
32
|
|
|
33
33
|
context 'for invalid attributes' do
|
|
34
|
-
let(:invalid_get)
|
|
35
|
-
-> { Hutch::Config.get(:invalid_attr) }
|
|
36
|
-
end
|
|
34
|
+
let(:invalid_get) { Hutch::Config.get(:invalid_attr) }
|
|
37
35
|
|
|
38
36
|
specify do
|
|
39
|
-
expect
|
|
37
|
+
expect { invalid_get }.to raise_error Hutch::UnknownAttributeError
|
|
40
38
|
end
|
|
41
39
|
end
|
|
42
40
|
end
|
|
@@ -49,15 +47,89 @@ describe Hutch::Config do
|
|
|
49
47
|
context 'sets value in user config hash' do
|
|
50
48
|
it { is_expected.to eq(new_value) }
|
|
51
49
|
end
|
|
50
|
+
|
|
51
|
+
context 'type casting' do
|
|
52
|
+
context 'number attributes' do
|
|
53
|
+
before { Hutch::Config.set(:heartbeat, new_value) }
|
|
54
|
+
subject(:value) { Hutch::Config.user_config[:heartbeat] }
|
|
55
|
+
|
|
56
|
+
let(:new_value) { "0" }
|
|
57
|
+
|
|
58
|
+
|
|
59
|
+
specify 'casts values to integers' do
|
|
60
|
+
expect(value).to eq 0
|
|
61
|
+
end
|
|
62
|
+
end
|
|
63
|
+
end
|
|
64
|
+
|
|
65
|
+
context 'boolean attributes' do
|
|
66
|
+
context 'from non-empty string' do
|
|
67
|
+
before { Hutch::Config.set(:autoload_rails, new_value) }
|
|
68
|
+
subject(:value) { Hutch::Config.user_config[:autoload_rails] }
|
|
69
|
+
|
|
70
|
+
let(:new_value) { "t" }
|
|
71
|
+
|
|
72
|
+
|
|
73
|
+
specify 'casts values to booleans' do
|
|
74
|
+
expect(value).to eq true
|
|
75
|
+
end
|
|
76
|
+
end
|
|
77
|
+
|
|
78
|
+
context 'from empty string' do
|
|
79
|
+
before { Hutch::Config.set(:autoload_rails, new_value) }
|
|
80
|
+
subject(:value) { Hutch::Config.user_config[:autoload_rails] }
|
|
81
|
+
|
|
82
|
+
let(:new_value) { "" }
|
|
83
|
+
|
|
84
|
+
|
|
85
|
+
specify 'casts values to booleans' do
|
|
86
|
+
expect(value).to eq false
|
|
87
|
+
end
|
|
88
|
+
end
|
|
89
|
+
|
|
90
|
+
context 'from boolean' do
|
|
91
|
+
before { Hutch::Config.set(:autoload_rails, new_value) }
|
|
92
|
+
subject(:value) { Hutch::Config.user_config[:autoload_rails] }
|
|
93
|
+
|
|
94
|
+
let(:new_value) { true }
|
|
95
|
+
|
|
96
|
+
|
|
97
|
+
specify 'casts values to booleans' do
|
|
98
|
+
expect(value).to eq true
|
|
99
|
+
end
|
|
100
|
+
end
|
|
101
|
+
|
|
102
|
+
context 'from nil' do
|
|
103
|
+
before { Hutch::Config.set(:autoload_rails, new_value) }
|
|
104
|
+
subject(:value) { Hutch::Config.user_config[:autoload_rails] }
|
|
105
|
+
|
|
106
|
+
let(:new_value) { nil }
|
|
107
|
+
|
|
108
|
+
|
|
109
|
+
specify 'casts values to booleans' do
|
|
110
|
+
expect(value).to eq false
|
|
111
|
+
end
|
|
112
|
+
end
|
|
113
|
+
end
|
|
114
|
+
|
|
115
|
+
context 'string attributes' do
|
|
116
|
+
before { Hutch::Config.set(:mq_exchange_type, new_value) }
|
|
117
|
+
subject(:value) { Hutch::Config.user_config[:mq_exchange_type] }
|
|
118
|
+
|
|
119
|
+
let(:new_value) { 1 }
|
|
120
|
+
|
|
121
|
+
|
|
122
|
+
specify 'does not perform any typecasting' do
|
|
123
|
+
expect(value).to eq new_value
|
|
124
|
+
end
|
|
125
|
+
end
|
|
52
126
|
end
|
|
53
127
|
|
|
54
128
|
context 'for invalid attributes' do
|
|
55
|
-
let(:invalid_set)
|
|
56
|
-
-> { Hutch::Config.set(:invalid_attr, new_value) }
|
|
57
|
-
end
|
|
129
|
+
let(:invalid_set) { Hutch::Config.set(:invalid_attr, new_value) }
|
|
58
130
|
|
|
59
131
|
specify do
|
|
60
|
-
expect
|
|
132
|
+
expect { invalid_set }.to raise_error Hutch::UnknownAttributeError
|
|
61
133
|
end
|
|
62
134
|
end
|
|
63
135
|
end
|
|
@@ -71,8 +143,8 @@ describe Hutch::Config do
|
|
|
71
143
|
end
|
|
72
144
|
|
|
73
145
|
context 'for an invalid attribute' do
|
|
74
|
-
let(:invalid_getter) {
|
|
75
|
-
specify { expect
|
|
146
|
+
let(:invalid_getter) { Hutch::Config.invalid_attr }
|
|
147
|
+
specify { expect { invalid_getter }.to raise_error NoMethodError }
|
|
76
148
|
end
|
|
77
149
|
|
|
78
150
|
context 'for an ENV-overriden value attribute' do
|
|
@@ -109,8 +181,8 @@ describe Hutch::Config do
|
|
|
109
181
|
end
|
|
110
182
|
|
|
111
183
|
context 'for an invalid attribute' do
|
|
112
|
-
let(:invalid_setter) {
|
|
113
|
-
specify { expect
|
|
184
|
+
let(:invalid_setter) { Hutch::Config.invalid_attr = new_value }
|
|
185
|
+
specify { expect { invalid_setter }.to raise_error NoMethodError }
|
|
114
186
|
end
|
|
115
187
|
end
|
|
116
188
|
|