distribot 0.1.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.
- checksums.yaml +7 -0
- data/.gitignore +13 -0
- data/.rspec +3 -0
- data/.rubocop.yml +3 -0
- data/.travis.yml +10 -0
- data/Dockerfile +9 -0
- data/Gemfile +6 -0
- data/Gemfile.lock +153 -0
- data/LICENSE +201 -0
- data/README.md +107 -0
- data/Rakefile +16 -0
- data/bin/distribot.flow-created +6 -0
- data/bin/distribot.flow-finished +6 -0
- data/bin/distribot.handler-finished +6 -0
- data/bin/distribot.phase-finished +6 -0
- data/bin/distribot.phase-started +6 -0
- data/bin/distribot.task-finished +6 -0
- data/distribot.gemspec +35 -0
- data/docker-compose.yml +29 -0
- data/examples/controller +168 -0
- data/examples/distribot.eye +49 -0
- data/examples/status +38 -0
- data/examples/worker +135 -0
- data/lib/distribot/connector.rb +162 -0
- data/lib/distribot/flow.rb +200 -0
- data/lib/distribot/flow_created_handler.rb +12 -0
- data/lib/distribot/flow_finished_handler.rb +12 -0
- data/lib/distribot/handler.rb +40 -0
- data/lib/distribot/handler_finished_handler.rb +29 -0
- data/lib/distribot/phase.rb +46 -0
- data/lib/distribot/phase_finished_handler.rb +19 -0
- data/lib/distribot/phase_handler.rb +15 -0
- data/lib/distribot/phase_started_handler.rb +69 -0
- data/lib/distribot/task_finished_handler.rb +37 -0
- data/lib/distribot/worker.rb +148 -0
- data/lib/distribot.rb +108 -0
- data/provision/nodes.sh +80 -0
- data/provision/templates/fluentd.conf +27 -0
- data/spec/distribot/bunny_connector_spec.rb +196 -0
- data/spec/distribot/connection_sharer_spec.rb +34 -0
- data/spec/distribot/connector_spec.rb +63 -0
- data/spec/distribot/flow_created_handler_spec.rb +32 -0
- data/spec/distribot/flow_finished_handler_spec.rb +32 -0
- data/spec/distribot/flow_spec.rb +661 -0
- data/spec/distribot/handler_finished_handler_spec.rb +112 -0
- data/spec/distribot/handler_spec.rb +32 -0
- data/spec/distribot/module_spec.rb +163 -0
- data/spec/distribot/multi_subscription_spec.rb +37 -0
- data/spec/distribot/phase_finished_handler_spec.rb +61 -0
- data/spec/distribot/phase_started_handler_spec.rb +150 -0
- data/spec/distribot/subscription_spec.rb +40 -0
- data/spec/distribot/task_finished_handler_spec.rb +71 -0
- data/spec/distribot/worker_spec.rb +281 -0
- data/spec/fixtures/simple_flow.json +49 -0
- data/spec/spec_helper.rb +74 -0
- metadata +371 -0
@@ -0,0 +1,112 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe Distribot::HandlerFinishedHandler do
|
4
|
+
before do
|
5
|
+
Distribot.stub(:subscribe)
|
6
|
+
end
|
7
|
+
describe 'definition' do
|
8
|
+
it 'subscribes to the correct queue' do
|
9
|
+
expect(Distribot::Handler.queue_for(described_class)).to eq 'distribot.flow.handler.finished'
|
10
|
+
end
|
11
|
+
it 'declares a valid handler' do
|
12
|
+
expect(Distribot::Handler.handler_for(described_class)).to eq :callback
|
13
|
+
end
|
14
|
+
it 'has a method matching the handler name' do
|
15
|
+
expect(described_class.new).to respond_to :callback
|
16
|
+
end
|
17
|
+
end
|
18
|
+
|
19
|
+
describe '#callback(message)' do
|
20
|
+
before do
|
21
|
+
@message = {
|
22
|
+
task_queue: 'task-queue',
|
23
|
+
flow_id: 'flow-id',
|
24
|
+
phase: 'phase1'
|
25
|
+
}
|
26
|
+
@handler = described_class.new
|
27
|
+
end
|
28
|
+
context 'when all the handler\'s tasks' do
|
29
|
+
context 'are complete' do
|
30
|
+
before do
|
31
|
+
expect(Distribot::Flow).to receive(:find).with(@message[:flow_id]) do
|
32
|
+
Distribot::Flow.new(id: @message[:flow_id], phases: [{name:'phase1', handlers: []}])
|
33
|
+
end
|
34
|
+
expect(@handler).to receive(:all_phase_handler_tasks_are_complete?){ true }
|
35
|
+
end
|
36
|
+
it 'announces that this phase has completed and that everyone should stop consuming its task.finished queue' do
|
37
|
+
# Describe what 'nothing' looks like:
|
38
|
+
expect(Distribot).to receive(:publish!).with('distribot.flow.phase.finished', {
|
39
|
+
flow_id: @message[:flow_id],
|
40
|
+
phase: 'phase1'
|
41
|
+
})
|
42
|
+
|
43
|
+
# Finally:
|
44
|
+
@handler.callback(@message)
|
45
|
+
end
|
46
|
+
end
|
47
|
+
context 'are not yet complete' do
|
48
|
+
before do
|
49
|
+
expect(Distribot::Flow).to receive(:find).with(@message[:flow_id]) do
|
50
|
+
Distribot::Flow.new(id: @message[:flow_id], phases: [{name:'phase1', handlers: []}])
|
51
|
+
end
|
52
|
+
expect(@handler).to receive(:all_phase_handler_tasks_are_complete?){ false }
|
53
|
+
end
|
54
|
+
it 'does nothing' do
|
55
|
+
# Describe what 'nothing' looks like:
|
56
|
+
expect(Distribot).not_to receive(:publish!)
|
57
|
+
expect(Distribot).not_to receive(:broadcast!)
|
58
|
+
|
59
|
+
# Finally:
|
60
|
+
@handler.callback(@message)
|
61
|
+
end
|
62
|
+
end
|
63
|
+
end
|
64
|
+
end
|
65
|
+
|
66
|
+
describe '#all_phase_handler_tasks_are_complete?(flow, phase)' do
|
67
|
+
context 'when all tasks are complete' do
|
68
|
+
before do
|
69
|
+
@flow = Distribot::Flow.new(id: 123)
|
70
|
+
handlers = ['handler1']
|
71
|
+
task_counts = {
|
72
|
+
"distribot.flow.123.phase1.handler1.tasks" => 0,
|
73
|
+
}
|
74
|
+
@phase = Distribot::Phase.new(name: 'phase1', handlers: [
|
75
|
+
'handler1'
|
76
|
+
])
|
77
|
+
expect(Distribot).to receive(:redis) do
|
78
|
+
redis = double('redis')
|
79
|
+
expect(redis).to receive(:get).exactly(1).times do |key|
|
80
|
+
task_counts[key]
|
81
|
+
end
|
82
|
+
redis
|
83
|
+
end
|
84
|
+
end
|
85
|
+
it 'returns true' do
|
86
|
+
expect(described_class.new.all_phase_handler_tasks_are_complete?(@flow, @phase)).to be_truthy
|
87
|
+
end
|
88
|
+
end
|
89
|
+
context 'when some tasks remain' do
|
90
|
+
before do
|
91
|
+
@flow = Distribot::Flow.new(id: 123)
|
92
|
+
handlers = ['handler1']
|
93
|
+
task_counts = {
|
94
|
+
"distribot.flow.123.phase1.handler1.tasks" => 1,
|
95
|
+
}
|
96
|
+
@phase = Distribot::Phase.new(name: 'phase1', handlers: [
|
97
|
+
'handler1'
|
98
|
+
])
|
99
|
+
expect(Distribot).to receive(:redis) do
|
100
|
+
redis = double('redis')
|
101
|
+
expect(redis).to receive(:get).exactly(1).times do |key|
|
102
|
+
task_counts[key]
|
103
|
+
end
|
104
|
+
redis
|
105
|
+
end
|
106
|
+
end
|
107
|
+
it 'returns false' do
|
108
|
+
expect(described_class.new.all_phase_handler_tasks_are_complete?(@flow, @phase)).to be_falsey
|
109
|
+
end
|
110
|
+
end
|
111
|
+
end
|
112
|
+
end
|
@@ -0,0 +1,32 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe Distribot::Handler do
|
4
|
+
before do
|
5
|
+
Distribot.stub(:subscribe)
|
6
|
+
Distribot.stub(:subscribe_multi)
|
7
|
+
end
|
8
|
+
describe '.subscribe_to' do
|
9
|
+
before do
|
10
|
+
@id = SecureRandom.hex(8)
|
11
|
+
@queue_name = "queue-#{@id}"
|
12
|
+
@klass_name = "Foo#{@id}"
|
13
|
+
expect(Distribot).to receive(:subscribe).with(@queue_name, {}) do |queue_name, args, &block|
|
14
|
+
block.call(queue_name, args, block)
|
15
|
+
end
|
16
|
+
end
|
17
|
+
it 'subscribes to the queue provided' do
|
18
|
+
eval <<-EOF
|
19
|
+
class #{@klass_name}
|
20
|
+
include Distribot::Handler
|
21
|
+
subscribe_to '#{@queue_name}', handler: :callback
|
22
|
+
def callback(message)
|
23
|
+
end
|
24
|
+
end
|
25
|
+
EOF
|
26
|
+
@klass_ref = Kernel.const_get(@klass_name)
|
27
|
+
@klass_ref.new
|
28
|
+
expect(@klass_ref.handler).to eq :callback
|
29
|
+
expect(@klass_ref.queue).to eq @queue_name
|
30
|
+
end
|
31
|
+
end
|
32
|
+
end
|
@@ -0,0 +1,163 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe Distribot do
|
4
|
+
describe '.debug' do
|
5
|
+
it 'allows true and false values' do
|
6
|
+
Distribot.debug = true
|
7
|
+
expect(Distribot.debug).to be_truthy
|
8
|
+
Distribot.debug = false
|
9
|
+
expect(Distribot.debug).to be_falsey
|
10
|
+
end
|
11
|
+
end
|
12
|
+
|
13
|
+
describe '.configure' do
|
14
|
+
it 'executes the given block and uses the result as the configuration' do
|
15
|
+
Distribot.reset_configuration!
|
16
|
+
Distribot.configuration
|
17
|
+
Distribot.configure do |config|
|
18
|
+
config.foo=:bar
|
19
|
+
end
|
20
|
+
expect(Distribot.configuration.foo).to eq :bar
|
21
|
+
end
|
22
|
+
end
|
23
|
+
|
24
|
+
describe '.redis' do
|
25
|
+
context 'when configuration.redis_url' do
|
26
|
+
context 'is nil' do
|
27
|
+
before do
|
28
|
+
Distribot.reset_configuration!
|
29
|
+
Distribot.configure do |config|
|
30
|
+
config.redis_url = nil
|
31
|
+
end
|
32
|
+
end
|
33
|
+
it 'returns a new Redis instance without argument' do
|
34
|
+
expect(Redis).to receive(:new).with(no_args){ 'HELLO' }
|
35
|
+
expect(Distribot.redis).to eq 'HELLO'
|
36
|
+
end
|
37
|
+
end
|
38
|
+
context 'is given' do
|
39
|
+
before do
|
40
|
+
@redis_url = 'redis://foo:6379/0'
|
41
|
+
Distribot.reset_configuration!
|
42
|
+
Distribot.configure do |config|
|
43
|
+
config.redis_url = @redis_url
|
44
|
+
end
|
45
|
+
end
|
46
|
+
it 'returns a new Redis instance' do
|
47
|
+
expect(Redis).to receive(:new).with(url: @redis_url){ 'HELLO' }
|
48
|
+
expect(Distribot.redis).to eq 'HELLO'
|
49
|
+
end
|
50
|
+
end
|
51
|
+
end
|
52
|
+
end
|
53
|
+
|
54
|
+
describe '.connector' do
|
55
|
+
before do
|
56
|
+
Distribot.configure do |config|
|
57
|
+
config.rabbitmq_url = 'fake-rabbit-url'
|
58
|
+
end
|
59
|
+
end
|
60
|
+
it 'returns a BunnyConnector' do
|
61
|
+
expect(Distribot::BunnyConnector).to receive(:new).with('fake-rabbit-url'){ 'connector' }
|
62
|
+
expect(Distribot.connector).to eq 'connector'
|
63
|
+
end
|
64
|
+
end
|
65
|
+
|
66
|
+
describe '.queue_exists?(name)' do
|
67
|
+
context 'when the queue exists' do
|
68
|
+
before do
|
69
|
+
@queue_name = "queue-#{SecureRandom.uuid}"
|
70
|
+
expect(Distribot).to receive(:connector) do
|
71
|
+
connector = double('connector')
|
72
|
+
expect(connector).to receive(:queue_exists?).with(@queue_name){ true }
|
73
|
+
connector
|
74
|
+
end
|
75
|
+
end
|
76
|
+
it 'returns true' do
|
77
|
+
expect(Distribot.queue_exists?(@queue_name)).to be_truthy
|
78
|
+
end
|
79
|
+
end
|
80
|
+
context 'when the queue does not exist' do
|
81
|
+
before do
|
82
|
+
@queue_name = "queue-#{SecureRandom.uuid}"
|
83
|
+
expect(Distribot).to receive(:connector) do
|
84
|
+
connector = double('connector')
|
85
|
+
expect(connector).to receive(:queue_exists?).with(@queue_name){ false }
|
86
|
+
connector
|
87
|
+
end
|
88
|
+
end
|
89
|
+
it 'returns false' do
|
90
|
+
expect(Distribot.queue_exists?(@queue_name)).to be_falsey
|
91
|
+
end
|
92
|
+
end
|
93
|
+
end
|
94
|
+
|
95
|
+
describe '.subscribe(queue_name, options={}, &block)' do
|
96
|
+
before do
|
97
|
+
@topic = "queue-#{SecureRandom.uuid}"
|
98
|
+
expect(Distribot).to receive(:connector) do
|
99
|
+
connector = double('connector')
|
100
|
+
expect(connector).to receive(:subscribe).with(@topic, {}) do |&block|
|
101
|
+
block.call( hello: 'world' )
|
102
|
+
end
|
103
|
+
connector
|
104
|
+
end
|
105
|
+
end
|
106
|
+
it 'subscribes properly' do
|
107
|
+
Distribot.subscribe(@topic) do |message|
|
108
|
+
expect(message).to have_key :hello
|
109
|
+
expect(message[:hello]).to eq 'world'
|
110
|
+
end
|
111
|
+
end
|
112
|
+
end
|
113
|
+
|
114
|
+
describe '.subscribe_multi(topic, options={}, &block)' do
|
115
|
+
before do
|
116
|
+
@topic = "queue-#{SecureRandom.uuid}"
|
117
|
+
expect(Distribot).to receive(:connector) do
|
118
|
+
connector = double('connector')
|
119
|
+
expect(connector).to receive(:subscribe_multi).with(@topic, {}) do |&block|
|
120
|
+
block.call( hello: 'world' )
|
121
|
+
end
|
122
|
+
connector
|
123
|
+
end
|
124
|
+
end
|
125
|
+
it 'subscribes properly' do
|
126
|
+
Distribot.subscribe_multi(@topic) do |message|
|
127
|
+
expect(message).to have_key :hello
|
128
|
+
expect(message[:hello]).to eq 'world'
|
129
|
+
end
|
130
|
+
end
|
131
|
+
end
|
132
|
+
|
133
|
+
describe '.publish!(topic, data)' do
|
134
|
+
before do
|
135
|
+
@topic = SecureRandom.uuid
|
136
|
+
@data = { id: SecureRandom.uuid }
|
137
|
+
expect(Distribot).to receive(:connector) do
|
138
|
+
connector = double('connector')
|
139
|
+
expect(connector).to receive(:publish).with(@topic, @data)
|
140
|
+
connector
|
141
|
+
end
|
142
|
+
end
|
143
|
+
it 'publishes the message to the topic' do
|
144
|
+
Distribot.publish!(@topic, @data)
|
145
|
+
end
|
146
|
+
end
|
147
|
+
|
148
|
+
describe '.broadcast!(topic, data)' do
|
149
|
+
before do
|
150
|
+
@topic = SecureRandom.uuid
|
151
|
+
@data = { id: SecureRandom.uuid }
|
152
|
+
expect(Distribot).to receive(:connector) do
|
153
|
+
connector = double('connector')
|
154
|
+
expect(connector).to receive(:broadcast).with(@topic, @data)
|
155
|
+
connector
|
156
|
+
end
|
157
|
+
end
|
158
|
+
it 'publishes the message to the topic' do
|
159
|
+
Distribot.broadcast!(@topic, @data)
|
160
|
+
end
|
161
|
+
end
|
162
|
+
|
163
|
+
end
|
@@ -0,0 +1,37 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe Distribot::MultiSubscription do
|
4
|
+
describe 'inheritance' do
|
5
|
+
let(:subject){ described_class.new(nil) }
|
6
|
+
it { should be_a Distribot::ConnectionSharer }
|
7
|
+
end
|
8
|
+
|
9
|
+
describe '#start(topic, options={}, &block)' do
|
10
|
+
it 'subscribes to the rabbit queue' do
|
11
|
+
# Arrange:
|
12
|
+
@topic = 'my.topic'
|
13
|
+
subscription = described_class.new(nil)
|
14
|
+
|
15
|
+
queue = double('queue')
|
16
|
+
channel = double('channel')
|
17
|
+
expect(channel).to receive(:queue).with('', exclusive: true, auto_delete: true){ queue }
|
18
|
+
expect(channel).to receive(:fanout).with(@topic){ 'exchange' }
|
19
|
+
expect(queue).to receive(:bind).with('exchange') do
|
20
|
+
exchange = double('exchange')
|
21
|
+
expect(exchange).to receive(:subscribe) do |&block|
|
22
|
+
block.call(nil, nil, {id: :good_message} )
|
23
|
+
block.call(nil, nil, {id: :bad_message} )
|
24
|
+
end
|
25
|
+
exchange
|
26
|
+
end
|
27
|
+
expect(subscription).to receive(:channel).exactly(2).times{ channel }
|
28
|
+
|
29
|
+
# Act:
|
30
|
+
subscription.start(@topic) do |msg|
|
31
|
+
raise "Test error" if msg[:id] == 'bad_message'
|
32
|
+
end
|
33
|
+
|
34
|
+
# Assert:
|
35
|
+
end
|
36
|
+
end
|
37
|
+
end
|
@@ -0,0 +1,61 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe Distribot::PhaseFinishedHandler do
|
4
|
+
describe 'definition' do
|
5
|
+
it 'subscribes to the correct queue' do
|
6
|
+
expect(Distribot::Handler.queue_for(described_class)).to eq 'distribot.flow.phase.finished'
|
7
|
+
end
|
8
|
+
it 'declares a valid handler' do
|
9
|
+
expect(Distribot::Handler.handler_for(described_class)).to eq :callback
|
10
|
+
end
|
11
|
+
it 'has a method matching the handler name' do
|
12
|
+
expect(Distribot).to receive(:subscribe)
|
13
|
+
expect(described_class.new).to respond_to :callback
|
14
|
+
end
|
15
|
+
end
|
16
|
+
|
17
|
+
describe '#callback( :flow_id, :phase )' do
|
18
|
+
before do
|
19
|
+
expect(Distribot).to receive(:subscribe)
|
20
|
+
end
|
21
|
+
context 'when flow is' do
|
22
|
+
before do
|
23
|
+
@flow = double('flow')
|
24
|
+
@flow_id = 'xxx'
|
25
|
+
expect(Distribot::Flow).to receive(:find).with(@flow_id) { @flow }
|
26
|
+
end
|
27
|
+
context 'still in :phase' do
|
28
|
+
before do
|
29
|
+
expect(@flow).to receive(:current_phase){ 'start' }
|
30
|
+
end
|
31
|
+
context 'and the flow' do
|
32
|
+
context 'has a next phase' do
|
33
|
+
before do
|
34
|
+
expect(@flow).to receive(:next_phase).exactly(2).times{ 'finish' }
|
35
|
+
end
|
36
|
+
it 'tells the flow to transition to the next phase' do
|
37
|
+
expect(@flow).to receive(:transition_to!).with('finish')
|
38
|
+
described_class.new.callback(flow_id: @flow_id, phase: 'start')
|
39
|
+
end
|
40
|
+
end
|
41
|
+
context 'does not have a next phase' do
|
42
|
+
before do
|
43
|
+
expect(@flow).to receive(:next_phase){ nil }
|
44
|
+
expect(@flow).to receive(:id){ @flow_id }
|
45
|
+
end
|
46
|
+
it 'publishes to distribot.flow.finished' do
|
47
|
+
expect(Distribot).to receive(:publish!).with('distribot.flow.finished', {
|
48
|
+
flow_id: @flow_id
|
49
|
+
})
|
50
|
+
described_class.new.callback(flow_id: @flow_id, phase: 'start')
|
51
|
+
end
|
52
|
+
end
|
53
|
+
end
|
54
|
+
end
|
55
|
+
context 'no longer in :phase' do
|
56
|
+
it 'does nothing'
|
57
|
+
end
|
58
|
+
end
|
59
|
+
end
|
60
|
+
|
61
|
+
end
|
@@ -0,0 +1,150 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe Distribot::PhaseStartedHandler do
|
4
|
+
describe 'definition' do
|
5
|
+
it 'subscribes to the correct queue' do
|
6
|
+
expect(Distribot::Handler.queue_for(described_class)).to eq 'distribot.flow.phase.started'
|
7
|
+
end
|
8
|
+
it 'declares a valid handler' do
|
9
|
+
expect(Distribot::Handler.handler_for(described_class)).to eq :callback
|
10
|
+
end
|
11
|
+
it 'has a method matching the handler name' do
|
12
|
+
expect(Distribot).to receive(:subscribe)
|
13
|
+
expect(described_class.new).to respond_to :callback
|
14
|
+
end
|
15
|
+
end
|
16
|
+
|
17
|
+
describe '#callback' do
|
18
|
+
before do
|
19
|
+
@flow = Distribot::Flow.new(
|
20
|
+
id: 1,
|
21
|
+
name: 'test',
|
22
|
+
phases: [{
|
23
|
+
name: 'phase1',
|
24
|
+
is_initial: true,
|
25
|
+
}]
|
26
|
+
)
|
27
|
+
expect(Distribot::Flow).to receive(:find).with(1){ @flow }
|
28
|
+
@phase = double('phase')
|
29
|
+
expect(@phase).to receive(:handlers).at_least(1).times{ @handlers }
|
30
|
+
expect(@flow).to receive(:phase).with('phase1'){ @phase }
|
31
|
+
end
|
32
|
+
context 'when this phase has' do
|
33
|
+
context 'no handlers' do
|
34
|
+
before do
|
35
|
+
@handlers = [ ]
|
36
|
+
expect(@phase).to receive(:name){ 'phase1' }
|
37
|
+
end
|
38
|
+
it 'considers this phase finished and publishes a message to that effect' do
|
39
|
+
expect(Distribot).to receive(:publish!).with('distribot.flow.phase.finished', {
|
40
|
+
flow_id: @flow.id,
|
41
|
+
phase: 'phase1'
|
42
|
+
})
|
43
|
+
expect(Distribot).to receive(:subscribe)
|
44
|
+
described_class.new.callback(flow_id: @flow.id, phase: 'phase1')
|
45
|
+
end
|
46
|
+
end
|
47
|
+
context 'some handlers' do
|
48
|
+
before do
|
49
|
+
@handlers = [
|
50
|
+
Distribot::PhaseHandler.new( name: 'FooHandler' ),
|
51
|
+
Distribot::PhaseHandler.new(
|
52
|
+
name: 'BarHandler',
|
53
|
+
version: '>= 1.0'
|
54
|
+
)
|
55
|
+
]
|
56
|
+
expect(Distribot).to receive(:subscribe)
|
57
|
+
@worker = described_class.new
|
58
|
+
end
|
59
|
+
context 'and all the handlers have suitable version matches' do
|
60
|
+
before do
|
61
|
+
expect(@worker).to receive(:best_version).ordered.with(@handlers[0]){ '1.0' }
|
62
|
+
expect(@worker).to receive(:best_version).ordered.with(@handlers[1]){ '2.0' }
|
63
|
+
expect(@worker).to receive(:init_handler).ordered.with(
|
64
|
+
@flow,
|
65
|
+
@phase,
|
66
|
+
@handlers[0],
|
67
|
+
'1.0'
|
68
|
+
)
|
69
|
+
expect(@worker).to receive(:init_handler).ordered.with(
|
70
|
+
@flow,
|
71
|
+
@phase,
|
72
|
+
@handlers[1],
|
73
|
+
'2.0'
|
74
|
+
)
|
75
|
+
end
|
76
|
+
it 'jumpstarts each handler' do
|
77
|
+
@worker.callback(flow_id: @flow.id, phase: 'phase1')
|
78
|
+
end
|
79
|
+
end
|
80
|
+
context 'any of the handlers cannot find a suitable version' do
|
81
|
+
before do
|
82
|
+
expect(@worker).to receive(:best_version)
|
83
|
+
expect(@worker).not_to receive(:init_handler)
|
84
|
+
end
|
85
|
+
it 'raises an exception' do
|
86
|
+
expect{@worker.callback(flow_id: @flow.id, phase: 'phase1')}.to raise_error RuntimeError
|
87
|
+
end
|
88
|
+
end
|
89
|
+
end
|
90
|
+
end
|
91
|
+
end
|
92
|
+
|
93
|
+
describe '#best_version(handler)' do
|
94
|
+
before do
|
95
|
+
expect(Distribot).to receive(:connector) do
|
96
|
+
connector = double('connector')
|
97
|
+
expect(connector).to receive(:queues) {
|
98
|
+
%w(
|
99
|
+
distribot.flow.handler.FooHandler.0.9.0.tasks
|
100
|
+
distribot.flow.handler.FooHandler.1.0.0.tasks
|
101
|
+
distribot.flow.handler.FooHandler.1.0.1.tasks
|
102
|
+
distribot.flow.handler.FooHandler.2.0.0.tasks
|
103
|
+
distribot.flow.handler.BarHandler.1.0.0.tasks
|
104
|
+
)
|
105
|
+
}
|
106
|
+
connector
|
107
|
+
end
|
108
|
+
expect(Distribot).to receive(:subscribe)
|
109
|
+
@worker = described_class.new
|
110
|
+
end
|
111
|
+
context 'when the handler version' do
|
112
|
+
context 'is specified in the flow ' do
|
113
|
+
before do
|
114
|
+
@handler = Distribot::PhaseHandler.new(name: 'FooHandler', version: '~> 1.0')
|
115
|
+
end
|
116
|
+
it 'returns the highest available *matching* version for that handler' do
|
117
|
+
expect(@worker.best_version(@handler)).to eq '1.0.1'
|
118
|
+
end
|
119
|
+
end
|
120
|
+
context 'is not specified in the flow' do
|
121
|
+
before do
|
122
|
+
@handler = Distribot::PhaseHandler.new(name: 'FooHandler')
|
123
|
+
end
|
124
|
+
it 'returns the highest available version for that handler' do
|
125
|
+
expect(@worker.best_version(@handler)).to eq '2.0.0'
|
126
|
+
end
|
127
|
+
end
|
128
|
+
end
|
129
|
+
end
|
130
|
+
|
131
|
+
describe '#init_handler(flow, phase, handler, version)' do
|
132
|
+
before do
|
133
|
+
@flow = Distribot::Flow.new(id: 'xxx')
|
134
|
+
@phase = Distribot::Phase.new(name: 'phase1')
|
135
|
+
@handler = Distribot::PhaseHandler.new(name: 'FooHandler', version: '1.0')
|
136
|
+
@version = '1.0'
|
137
|
+
expect(Distribot).to receive(:subscribe)
|
138
|
+
expect(Distribot).to receive(:publish!).with("distribot.flow.handler.#{@handler}.#{@version}.enumerate",
|
139
|
+
flow_id: @flow.id,
|
140
|
+
phase: @phase.name,
|
141
|
+
task_queue: a_string_matching(/\.#{@handler}\.#{@version}\.tasks/),
|
142
|
+
task_counter: a_string_matching(/\.#{@flow.id}\.#{@phase.name}\.#{@handler}\.finished/),
|
143
|
+
finished_queue: 'distribot.flow.task.finished',
|
144
|
+
)
|
145
|
+
end
|
146
|
+
it 'publishes a message for the handler with everything it needs to begin task enumeration' do
|
147
|
+
described_class.new.init_handler(@flow, @phase, @handler, @version)
|
148
|
+
end
|
149
|
+
end
|
150
|
+
end
|
@@ -0,0 +1,40 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe Distribot::Subscription do
|
4
|
+
describe 'inheritance' do
|
5
|
+
let(:subject){ described_class.new(nil) }
|
6
|
+
it { should be_a Distribot::ConnectionSharer }
|
7
|
+
end
|
8
|
+
|
9
|
+
describe '#start(topic, options={}, &block)' do
|
10
|
+
it 'subscribes to the rabbit queue' do
|
11
|
+
# Arrange:
|
12
|
+
@topic = 'my.topic'
|
13
|
+
subscription = described_class.new(nil)
|
14
|
+
|
15
|
+
channel = double('channel')
|
16
|
+
expect(channel).to receive(:queue).ordered.with(@topic, anything) do
|
17
|
+
queue = double('queue')
|
18
|
+
expect(queue).to receive(:subscribe).with(hash_including(manual_ack: true)) do |args, &block|
|
19
|
+
# Send a good message:
|
20
|
+
delivery_info1 = OpenStruct.new(delivery_tag: 'tag1')
|
21
|
+
delivery_info2 = OpenStruct.new(delivery_tag: 'tag2')
|
22
|
+
block.call(delivery_info1, nil, {id: :good_message}.to_json )
|
23
|
+
block.call(delivery_info2, nil, {id: :bad_message}.to_json )
|
24
|
+
end
|
25
|
+
queue
|
26
|
+
end
|
27
|
+
expect(channel).to receive(:acknowledge).with('tag1', false)
|
28
|
+
expect(channel).to receive(:basic_reject).with('tag2', true)
|
29
|
+
|
30
|
+
expect(subscription).to receive(:channel).exactly(3).times{ channel }
|
31
|
+
|
32
|
+
# Act:
|
33
|
+
subscription.start(@topic) do |msg|
|
34
|
+
raise "Test error" if msg[:id] == 'bad_message'
|
35
|
+
end
|
36
|
+
|
37
|
+
# Assert:
|
38
|
+
end
|
39
|
+
end
|
40
|
+
end
|
@@ -0,0 +1,71 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe Distribot::TaskFinishedHandler do
|
4
|
+
before do
|
5
|
+
Distribot.stub(:subscribe)
|
6
|
+
end
|
7
|
+
describe 'definition' do
|
8
|
+
it 'subscribes to the correct queue' do
|
9
|
+
expect(Distribot::Handler.queue_for(described_class)).to eq 'distribot.flow.task.finished'
|
10
|
+
end
|
11
|
+
it 'declares a valid handler' do
|
12
|
+
expect(Distribot::Handler.handler_for(described_class)).to eq :callback
|
13
|
+
end
|
14
|
+
it 'has a method matching the handler name' do
|
15
|
+
expect(described_class.new).to respond_to :callback
|
16
|
+
end
|
17
|
+
end
|
18
|
+
|
19
|
+
describe '#callback(message)' do
|
20
|
+
before do
|
21
|
+
@message = {
|
22
|
+
task_queue: 'task-queue',
|
23
|
+
finished_queue: 'finished-queue',
|
24
|
+
phase: 'the-phase',
|
25
|
+
handler: 'the-hander',
|
26
|
+
flow_id: 'flow-id'
|
27
|
+
}
|
28
|
+
end
|
29
|
+
context 'when the redis task counter' do
|
30
|
+
context 'is nil' do
|
31
|
+
before do
|
32
|
+
@redis = double('redis')
|
33
|
+
expect(@redis).to receive(:get){ nil }
|
34
|
+
expect(Distribot).to receive(:redis) do
|
35
|
+
@redis
|
36
|
+
end
|
37
|
+
end
|
38
|
+
it 'does nothing, because the handler is not yet finished' do
|
39
|
+
# Finally, action:
|
40
|
+
handler = Distribot::TaskFinishedHandler.new
|
41
|
+
handler.callback(@message)
|
42
|
+
end
|
43
|
+
end
|
44
|
+
context 'is not nil' do
|
45
|
+
before do
|
46
|
+
@redis = double('redis')
|
47
|
+
expect(Distribot).to receive(:redis).at_least(1).times do
|
48
|
+
@redis
|
49
|
+
end
|
50
|
+
end
|
51
|
+
context 'when the task count after decrementing' do
|
52
|
+
context 'is == 0' do
|
53
|
+
before do
|
54
|
+
expect(@redis).to receive(:get){ 0 }
|
55
|
+
expect(@redis).to receive(:del).ordered
|
56
|
+
end
|
57
|
+
it 'publishes a message to the handler finished queue' do
|
58
|
+
handler = Distribot::TaskFinishedHandler.new
|
59
|
+
|
60
|
+
expect(Distribot).to receive(:publish!).with("distribot.flow.handler.finished", @message.except(:finished_queue))
|
61
|
+
|
62
|
+
# Finally, action:
|
63
|
+
handler.callback(@message)
|
64
|
+
end
|
65
|
+
end
|
66
|
+
end
|
67
|
+
end
|
68
|
+
end
|
69
|
+
end
|
70
|
+
|
71
|
+
end
|