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.
Files changed (56) hide show
  1. checksums.yaml +7 -0
  2. data/.gitignore +13 -0
  3. data/.rspec +3 -0
  4. data/.rubocop.yml +3 -0
  5. data/.travis.yml +10 -0
  6. data/Dockerfile +9 -0
  7. data/Gemfile +6 -0
  8. data/Gemfile.lock +153 -0
  9. data/LICENSE +201 -0
  10. data/README.md +107 -0
  11. data/Rakefile +16 -0
  12. data/bin/distribot.flow-created +6 -0
  13. data/bin/distribot.flow-finished +6 -0
  14. data/bin/distribot.handler-finished +6 -0
  15. data/bin/distribot.phase-finished +6 -0
  16. data/bin/distribot.phase-started +6 -0
  17. data/bin/distribot.task-finished +6 -0
  18. data/distribot.gemspec +35 -0
  19. data/docker-compose.yml +29 -0
  20. data/examples/controller +168 -0
  21. data/examples/distribot.eye +49 -0
  22. data/examples/status +38 -0
  23. data/examples/worker +135 -0
  24. data/lib/distribot/connector.rb +162 -0
  25. data/lib/distribot/flow.rb +200 -0
  26. data/lib/distribot/flow_created_handler.rb +12 -0
  27. data/lib/distribot/flow_finished_handler.rb +12 -0
  28. data/lib/distribot/handler.rb +40 -0
  29. data/lib/distribot/handler_finished_handler.rb +29 -0
  30. data/lib/distribot/phase.rb +46 -0
  31. data/lib/distribot/phase_finished_handler.rb +19 -0
  32. data/lib/distribot/phase_handler.rb +15 -0
  33. data/lib/distribot/phase_started_handler.rb +69 -0
  34. data/lib/distribot/task_finished_handler.rb +37 -0
  35. data/lib/distribot/worker.rb +148 -0
  36. data/lib/distribot.rb +108 -0
  37. data/provision/nodes.sh +80 -0
  38. data/provision/templates/fluentd.conf +27 -0
  39. data/spec/distribot/bunny_connector_spec.rb +196 -0
  40. data/spec/distribot/connection_sharer_spec.rb +34 -0
  41. data/spec/distribot/connector_spec.rb +63 -0
  42. data/spec/distribot/flow_created_handler_spec.rb +32 -0
  43. data/spec/distribot/flow_finished_handler_spec.rb +32 -0
  44. data/spec/distribot/flow_spec.rb +661 -0
  45. data/spec/distribot/handler_finished_handler_spec.rb +112 -0
  46. data/spec/distribot/handler_spec.rb +32 -0
  47. data/spec/distribot/module_spec.rb +163 -0
  48. data/spec/distribot/multi_subscription_spec.rb +37 -0
  49. data/spec/distribot/phase_finished_handler_spec.rb +61 -0
  50. data/spec/distribot/phase_started_handler_spec.rb +150 -0
  51. data/spec/distribot/subscription_spec.rb +40 -0
  52. data/spec/distribot/task_finished_handler_spec.rb +71 -0
  53. data/spec/distribot/worker_spec.rb +281 -0
  54. data/spec/fixtures/simple_flow.json +49 -0
  55. data/spec/spec_helper.rb +74 -0
  56. 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