shoryuken 3.1.1 → 3.2.0

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 (43) hide show
  1. checksums.yaml +4 -4
  2. data/.rubocop.yml +1 -1
  3. data/.travis.yml +13 -3
  4. data/CHANGELOG.md +82 -1
  5. data/Gemfile +5 -2
  6. data/Gemfile.aws-sdk-core-v2 +13 -0
  7. data/bin/cli/sqs.rb +8 -4
  8. data/bin/shoryuken +2 -1
  9. data/lib/shoryuken/body_parser.rb +27 -0
  10. data/lib/shoryuken/environment_loader.rb +25 -11
  11. data/lib/shoryuken/fetcher.rb +40 -13
  12. data/lib/shoryuken/launcher.rb +4 -20
  13. data/lib/shoryuken/logging.rb +0 -5
  14. data/lib/shoryuken/manager.rb +31 -22
  15. data/lib/shoryuken/middleware/chain.rb +4 -0
  16. data/lib/shoryuken/middleware/server/auto_extend_visibility.rb +2 -5
  17. data/lib/shoryuken/middleware/server/timing.rb +12 -14
  18. data/lib/shoryuken/options.rb +25 -1
  19. data/lib/shoryuken/processor.rb +9 -21
  20. data/lib/shoryuken/queue.rb +12 -5
  21. data/lib/shoryuken/runner.rb +3 -1
  22. data/lib/shoryuken/util.rb +3 -3
  23. data/lib/shoryuken/version.rb +1 -1
  24. data/lib/shoryuken/worker/default_executor.rb +33 -0
  25. data/lib/shoryuken/worker/inline_executor.rb +28 -0
  26. data/lib/shoryuken/worker.rb +68 -31
  27. data/lib/shoryuken.rb +14 -1
  28. data/shoryuken.gemspec +1 -1
  29. data/spec/shoryuken/body_parser_spec.rb +89 -0
  30. data/spec/shoryuken/environment_loader_spec.rb +32 -3
  31. data/spec/shoryuken/fetcher_spec.rb +61 -9
  32. data/spec/shoryuken/manager_spec.rb +31 -18
  33. data/spec/shoryuken/middleware/chain_spec.rb +16 -4
  34. data/spec/shoryuken/middleware/server/timing_spec.rb +5 -3
  35. data/spec/shoryuken/options_spec.rb +80 -0
  36. data/spec/shoryuken/processor_spec.rb +15 -97
  37. data/spec/shoryuken/queue_spec.rb +43 -32
  38. data/spec/shoryuken/util_spec.rb +25 -1
  39. data/spec/shoryuken/worker/default_executor_spec.rb +100 -0
  40. data/spec/shoryuken/worker/inline_executor_spec.rb +23 -0
  41. data/spec/shoryuken/worker_spec.rb +32 -91
  42. data/spec/spec_helper.rb +5 -0
  43. metadata +15 -5
@@ -13,8 +13,9 @@ RSpec.describe Shoryuken::Manager do
13
13
  let(:polling_strategy) { Shoryuken::Polling::WeightedRoundRobin.new(queues) }
14
14
  let(:fetcher) { double Shoryuken::Fetcher }
15
15
  let(:concurrency) { 1 }
16
+ let(:executor) { Concurrent::ImmediateExecutor.new }
16
17
 
17
- subject { Shoryuken::Manager.new(fetcher, polling_strategy, concurrency) }
18
+ subject { Shoryuken::Manager.new(fetcher, polling_strategy, concurrency, executor) }
18
19
 
19
20
  before do
20
21
  allow(fetcher).to receive(:fetch).and_return([])
@@ -27,9 +28,9 @@ RSpec.describe Shoryuken::Manager do
27
28
 
28
29
  describe '#stop' do
29
30
  specify do
30
- allow(subject).to receive(:stopped?).and_return(false, false, true)
31
- expect(subject).to receive(:dispatch).thrice.and_call_original
32
- expect(subject).to receive(:dispatch_later).once.and_call_original
31
+ allow(subject).to receive(:running?).and_return(true, true, false)
32
+ expect(subject).to receive(:dispatch).once.and_call_original
33
+ expect(subject).to receive(:dispatch_loop).twice.and_call_original
33
34
  subject.start
34
35
  end
35
36
  end
@@ -37,12 +38,12 @@ RSpec.describe Shoryuken::Manager do
37
38
  describe '#start' do
38
39
  before do
39
40
  # prevent dispatch loop
40
- allow(subject).to receive(:stopped?).and_return(false, true)
41
+ allow(subject).to receive(:running?).and_return(true, true, false)
41
42
  end
42
43
 
43
44
  it 'pauses when there are no active queues' do
44
45
  expect(polling_strategy).to receive(:next_queue).and_return(nil)
45
- expect(subject).to receive(:dispatch_later)
46
+ expect(subject).to receive(:dispatch).and_call_original
46
47
  subject.start
47
48
  end
48
49
 
@@ -59,24 +60,36 @@ RSpec.describe Shoryuken::Manager do
59
60
  end
60
61
 
61
62
  describe '#dispatch' do
62
- it 'fires a dispatch event' do
63
- # prevent dispatch loop
64
- allow(subject).to receive(:stopped?).and_return(false, true)
63
+ before do
64
+ allow(subject).to receive(:running?).and_return(true, true, false)
65
+ end
65
66
 
66
- expect(subject).to receive(:fire_event).with(:dispatch)
67
+ specify do
68
+ message = ['test1']
69
+ messages = [message]
70
+ q = Shoryuken::Polling::QueueConfiguration.new(queue, {})
71
+
72
+ expect(fetcher).to receive(:fetch).with(q, concurrency).and_return(messages)
73
+ expect(subject).to receive(:fire_event).with(:dispatch, false, queue_name: q.name)
74
+ expect(Shoryuken::Processor).to receive(:process).with(q, message)
67
75
  expect(Shoryuken.logger).to_not receive(:info)
68
76
 
69
77
  subject.send(:dispatch)
70
78
  end
71
- end
72
79
 
73
- describe '#dispatch_batch' do
74
- it 'assings batch as a single message' do
75
- q = polling_strategy.next_queue
76
- messages = [1, 2, 3]
77
- expect(fetcher).to receive(:fetch).with(q, described_class::BATCH_LIMIT).and_return(messages)
78
- expect_any_instance_of(described_class).to receive(:assign).with(q.name, messages)
79
- subject.send(:dispatch_batch, q)
80
+ context 'when batch' do
81
+ specify do
82
+ messages = %w(test1 test2 test3)
83
+ q = Shoryuken::Polling::QueueConfiguration.new(queue, {})
84
+
85
+ expect(fetcher).to receive(:fetch).with(q, described_class::BATCH_LIMIT).and_return(messages)
86
+ expect(subject).to receive(:fire_event).with(:dispatch, false, queue_name: q.name)
87
+ allow(subject).to receive(:batched_queue?).with(q).and_return(true)
88
+ expect(Shoryuken::Processor).to receive(:process).with(q, messages)
89
+ expect(Shoryuken.logger).to_not receive(:info)
90
+
91
+ subject.send(:dispatch)
92
+ end
80
93
  end
81
94
  end
82
95
 
@@ -1,6 +1,6 @@
1
1
  require 'spec_helper'
2
2
 
3
- describe Shoryuken::Middleware::Chain do
3
+ RSpec.describe Shoryuken::Middleware::Chain do
4
4
  class CustomMiddleware
5
5
  def initialize(name, recorder)
6
6
  @name = name
@@ -14,20 +14,32 @@ describe Shoryuken::Middleware::Chain do
14
14
  end
15
15
  end
16
16
 
17
+ class CustomMiddlewareB < CustomMiddleware; end
18
+
17
19
  it 'supports custom middleware' do
18
20
  subject.add CustomMiddleware, 1, []
19
21
 
20
22
  expect(CustomMiddleware).to eq subject.entries.last.klass
21
23
  end
22
24
 
25
+ it 'can add middleware to the front of chain' do
26
+ subject.prepend CustomMiddleware, 1, []
27
+
28
+ expect([CustomMiddleware]).to eq subject.entries.map(&:klass)
29
+
30
+ subject.prepend CustomMiddlewareB, 1, []
31
+
32
+ expect([CustomMiddlewareB, CustomMiddleware]).to eq subject.entries.map(&:klass)
33
+ end
34
+
23
35
  it 'invokes a middleware' do
24
36
  recorder = []
25
- subject.add CustomMiddleware, 'Pablo', recorder
37
+ subject.add CustomMiddleware, 'custom', recorder
26
38
 
27
39
  final_action = nil
28
40
  subject.invoke { final_action = true }
29
41
  expect(final_action).to eq true
30
- expect(recorder).to eq [%w[Pablo before], %w[Pablo after]]
42
+ expect(recorder).to eq [%w(custom before), %w(custom after)]
31
43
  end
32
44
 
33
45
  class NonYieldingMiddleware
@@ -37,7 +49,7 @@ describe Shoryuken::Middleware::Chain do
37
49
  it 'allows middleware to abruptly stop processing rest of chain' do
38
50
  recorder = []
39
51
  subject.add NonYieldingMiddleware
40
- subject.add CustomMiddleware, 'Pablo', recorder
52
+ subject.add CustomMiddleware, 'custom', recorder
41
53
 
42
54
  final_action = nil
43
55
  subject.invoke { final_action = true }
@@ -1,14 +1,16 @@
1
1
  require 'spec_helper'
2
2
 
3
- describe Shoryuken::Middleware::Server::Timing do
3
+ RSpec.describe Shoryuken::Middleware::Server::Timing do
4
4
  let(:queue) { 'default' }
5
5
  let(:sqs_queue) { double Shoryuken::Queue, visibility_timeout: 60 }
6
6
 
7
7
  let(:sqs_msg) do
8
- double Shoryuken::Message,
8
+ double(
9
+ Shoryuken::Message,
9
10
  queue_url: queue,
10
11
  body: 'test',
11
12
  message_id: 'fc754df7-9cc2-4c41-96ca-5996a44b771e'
13
+ )
12
14
  end
13
15
 
14
16
  before do
@@ -28,7 +30,7 @@ describe Shoryuken::Middleware::Server::Timing do
28
30
 
29
31
  context 'when exceeded the `visibility_timeout`' do
30
32
  it 'logs exceeded' do
31
- allow(subject).to receive(:elapsed).and_return(120000)
33
+ allow(subject).to receive(:elapsed).and_return(120_000)
32
34
 
33
35
  expect(Shoryuken.logger).to receive(:info) do |&block|
34
36
  expect(block.call).to match(/started at/)
@@ -97,4 +97,84 @@ RSpec.describe Shoryuken::Options do
97
97
  "and Shoryuken doesn't support a batchable worker for a queue with multiple workers")
98
98
  end
99
99
  end
100
+
101
+ describe '.polling_strategy' do
102
+ context 'when not set' do
103
+ specify do
104
+ expect(Shoryuken.polling_strategy('default')).to eq Shoryuken::Polling::WeightedRoundRobin
105
+ expect(Shoryuken.polling_strategy('group1')).to eq Shoryuken::Polling::WeightedRoundRobin
106
+ end
107
+ end
108
+
109
+ context 'when set to StrictPriority string' do
110
+ before do
111
+ Shoryuken.options[:polling_strategy] = 'StrictPriority'
112
+
113
+ Shoryuken.options[:groups] = {
114
+ 'group1' => {
115
+ polling_strategy: 'StrictPriority'
116
+ }
117
+ }
118
+ end
119
+
120
+ specify do
121
+ expect(Shoryuken.polling_strategy('default')).to eq Shoryuken::Polling::StrictPriority
122
+ expect(Shoryuken.polling_strategy('group1')).to eq Shoryuken::Polling::StrictPriority
123
+ end
124
+ end
125
+
126
+ context 'when set to WeightedRoundRobin string' do
127
+ before do
128
+ Shoryuken.options[:polling_strategy] = 'WeightedRoundRobin'
129
+
130
+ Shoryuken.options[:groups] = {
131
+ 'group1' => {
132
+ polling_strategy: 'WeightedRoundRobin'
133
+ }
134
+ }
135
+ end
136
+
137
+ specify do
138
+ expect(Shoryuken.polling_strategy('default')).to eq Shoryuken::Polling::WeightedRoundRobin
139
+ expect(Shoryuken.polling_strategy('group1')).to eq Shoryuken::Polling::WeightedRoundRobin
140
+ end
141
+ end
142
+
143
+ context 'when set to non-existent string' do
144
+ before do
145
+ Shoryuken.options[:polling_strategy] = 'NonExistent1'
146
+
147
+ Shoryuken.options[:groups] = {
148
+ 'group1' => {
149
+ polling_strategy: 'NonExistent2'
150
+ }
151
+ }
152
+ end
153
+
154
+ specify do
155
+ expect { Shoryuken.polling_strategy('default') }.to raise_error(ArgumentError)
156
+ expect { Shoryuken.polling_strategy('group1') }.to raise_error(ArgumentError)
157
+ end
158
+ end
159
+
160
+ context 'when set to a class' do
161
+ before do
162
+ class Foo < Shoryuken::Polling::BaseStrategy; end
163
+ class Bar < Shoryuken::Polling::BaseStrategy; end
164
+
165
+ Shoryuken.options[:polling_strategy] = Foo
166
+
167
+ Shoryuken.options[:groups] = {
168
+ 'group1' => {
169
+ polling_strategy: Bar
170
+ }
171
+ }
172
+ end
173
+
174
+ specify do
175
+ expect(Shoryuken.polling_strategy('default')).to eq Foo
176
+ expect(Shoryuken.polling_strategy('group1')).to eq Bar
177
+ end
178
+ end
179
+ end
100
180
  end
@@ -1,6 +1,4 @@
1
1
  require 'spec_helper'
2
- require 'shoryuken/processor'
3
- require 'shoryuken/manager'
4
2
 
5
3
  RSpec.describe Shoryuken::Processor do
6
4
  let(:manager) { double Shoryuken::Manager }
@@ -8,12 +6,14 @@ RSpec.describe Shoryuken::Processor do
8
6
  let(:queue) { 'default' }
9
7
 
10
8
  let(:sqs_msg) do
11
- double Shoryuken::Message,
9
+ double(
10
+ Shoryuken::Message,
12
11
  queue_url: queue,
13
12
  body: 'test',
14
13
  message_attributes: {},
15
14
  message_id: SecureRandom.uuid,
16
15
  receipt_handle: SecureRandom.uuid
16
+ )
17
17
  end
18
18
 
19
19
  before do
@@ -25,101 +25,15 @@ RSpec.describe Shoryuken::Processor do
25
25
  subject { described_class.new(queue, sqs_msg) }
26
26
 
27
27
  describe '#process' do
28
- it 'parses the body into JSON' do
29
- TestWorker.get_shoryuken_options['body_parser'] = :json
28
+ it 'sets log context' do
29
+ expect(Shoryuken::Logging).to receive(:with_context).with("TestWorker/#{queue}/#{sqs_msg.message_id}")
30
30
 
31
- body = { 'test' => 'hi' }
32
-
33
- expect_any_instance_of(TestWorker).to receive(:perform).with(sqs_msg, body)
34
-
35
- allow(sqs_msg).to receive(:body).and_return(JSON.dump(body))
31
+ allow_any_instance_of(TestWorker).to receive(:perform)
32
+ allow(sqs_msg).to receive(:body)
36
33
 
37
34
  subject.process
38
35
  end
39
36
 
40
- it 'parses the body calling the proc' do
41
- TestWorker.get_shoryuken_options['body_parser'] = proc { |sqs_msg| "*#{sqs_msg.body}*" }
42
-
43
- expect_any_instance_of(TestWorker).to receive(:perform).with(sqs_msg, '*test*')
44
-
45
- allow(sqs_msg).to receive(:body).and_return('test')
46
-
47
- subject.process
48
- end
49
-
50
- it 'parses the body as text' do
51
- TestWorker.get_shoryuken_options['body_parser'] = :text
52
-
53
- body = 'test'
54
-
55
- expect_any_instance_of(TestWorker).to receive(:perform).with(sqs_msg, body)
56
-
57
- allow(sqs_msg).to receive(:body).and_return(body)
58
-
59
- subject.process
60
- end
61
-
62
- it 'parses calling `.load`' do
63
- TestWorker.get_shoryuken_options['body_parser'] = Class.new do
64
- def self.load(*args)
65
- JSON.load(*args)
66
- end
67
- end
68
-
69
- body = { 'test' => 'hi' }
70
-
71
- expect_any_instance_of(TestWorker).to receive(:perform).with(sqs_msg, body)
72
-
73
- allow(sqs_msg).to receive(:body).and_return(JSON.dump(body))
74
-
75
- subject.process
76
- end
77
-
78
- it 'parses calling `.parse`' do
79
- TestWorker.get_shoryuken_options['body_parser'] = Class.new do
80
- def self.parse(*args)
81
- JSON.parse(*args)
82
- end
83
- end
84
-
85
- body = { 'test' => 'hi' }
86
-
87
- expect_any_instance_of(TestWorker).to receive(:perform).with(sqs_msg, body)
88
-
89
- allow(sqs_msg).to receive(:body).and_return(JSON.dump(body))
90
-
91
- subject.process
92
- end
93
-
94
- context 'when parse errors' do
95
- before do
96
- TestWorker.get_shoryuken_options['body_parser'] = :json
97
-
98
- allow(sqs_msg).to receive(:body).and_return('invalid JSON')
99
- end
100
-
101
- specify do
102
- expect(subject.logger).to receive(:error).twice
103
-
104
- expect { subject.process }.
105
- to raise_error(JSON::ParserError, /unexpected token at 'invalid JSON'/)
106
- end
107
- end
108
-
109
- context 'when `object_type: nil`' do
110
- it 'parses the body as text' do
111
- TestWorker.get_shoryuken_options['body_parser'] = nil
112
-
113
- body = 'test'
114
-
115
- expect_any_instance_of(TestWorker).to receive(:perform).with(sqs_msg, body)
116
-
117
- allow(sqs_msg).to receive(:body).and_return(body)
118
-
119
- subject.process
120
- end
121
- end
122
-
123
37
  context 'when custom middleware' do
124
38
  let(:queue) { 'worker_called_middleware' }
125
39
 
@@ -220,15 +134,19 @@ RSpec.describe Shoryuken::Processor do
220
134
 
221
135
  context 'when shoryuken_class header' do
222
136
  let(:sqs_msg) do
223
- double Shoryuken::Message,
137
+ double(
138
+ Shoryuken::Message,
224
139
  queue_url: queue,
225
140
  body: 'test',
226
141
  message_attributes: {
227
142
  'shoryuken_class' => {
228
143
  string_value: TestWorker.to_s,
229
- data_type: 'String' }},
230
- message_id: SecureRandom.uuid,
231
- receipt_handle: SecureRandom.uuid
144
+ data_type: 'String'
145
+ }
146
+ },
147
+ message_id: SecureRandom.uuid,
148
+ receipt_handle: SecureRandom.uuid
149
+ )
232
150
  end
233
151
 
234
152
  it 'performs without delete' do
@@ -1,5 +1,6 @@
1
1
  require 'spec_helper'
2
2
 
3
+ # rubocop:disable Metrics/BlockLength
3
4
  RSpec.describe Shoryuken::Queue do
4
5
  let(:credentials) { Aws::Credentials.new('access_key_id', 'secret_access_key') }
5
6
  let(:sqs) { Aws::SQS::Client.new(stub_responses: true, credentials: credentials) }
@@ -15,10 +16,12 @@ RSpec.describe Shoryuken::Queue do
15
16
  end
16
17
 
17
18
  describe '#new' do
18
- context 'when queue url supplied' do
19
- subject { described_class.new(sqs, queue_url) }
19
+ context 'when queue URL supplied' do
20
+ it 'instantiates by URL and validate the URL' do
21
+ expect_any_instance_of(described_class).to receive(:fifo?).and_return(false)
22
+
23
+ subject = described_class.new(sqs, queue_url)
20
24
 
21
- specify do
22
25
  expect(subject.name).to eq(queue_name)
23
26
  end
24
27
  end
@@ -51,7 +54,9 @@ RSpec.describe Shoryuken::Queue do
51
54
  failure = double(id: 'id', code: 'code', message: '...', sender_fault: false)
52
55
  logger = double 'Logger'
53
56
 
54
- expect(sqs).to receive(:delete_message_batch).with(entries: entries, queue_url: queue_url).and_return(double(failed: [failure]))
57
+ expect(sqs).to(
58
+ receive(:delete_message_batch).with(entries: entries, queue_url: queue_url).and_return(double(failed: [failure]))
59
+ )
55
60
  expect(subject).to receive(:logger).and_return(logger)
56
61
  expect(logger).to receive(:error)
57
62
 
@@ -111,36 +116,30 @@ RSpec.describe Shoryuken::Queue do
111
116
  end
112
117
 
113
118
  describe '#send_messages' do
114
- before {
115
- allow(subject).to receive(:fifo?).and_return(false)
116
- }
119
+ before { allow(subject).to receive(:fifo?).and_return(false) }
120
+
117
121
  it 'accepts SQS request parameters' do
118
122
  # https://docs.aws.amazon.com/sdkforruby/api/Aws/SQS/Client.html#send_message_batch-instance_method
119
- expect(sqs).to receive(:send_message_batch).with(hash_including(entries: [{id: '0', message_body: 'msg1'}, {id: '1', message_body: 'msg2'}]))
123
+ expect(sqs).to(
124
+ receive(:send_message_batch).with(hash_including(entries: [{id: '0', message_body: 'msg1'}, {id: '1', message_body: 'msg2'}]))
125
+ )
120
126
 
121
127
  subject.send_messages(entries: [{id: '0', message_body: 'msg1'}, {id: '1', message_body: 'msg2'}])
122
128
  end
123
129
 
124
130
  it 'accepts an array of messages' do
125
- options = { entries: [{ id: '0',
126
- message_body: 'msg1',
127
- delay_seconds: 1,
128
- message_attributes: { attr: 'attr1' } },
129
- { id: '1',
130
- message_body: 'msg2',
131
- delay_seconds: 1,
132
- message_attributes: { attr: 'attr2' } }] }
133
-
131
+ options = { entries: [
132
+ { id: '0', message_body: 'msg1', delay_seconds: 1, message_attributes: { attr: 'attr1' } },
133
+ { id: '1', message_body: 'msg2', delay_seconds: 1, message_attributes: { attr: 'attr2' } }
134
+ ] }
134
135
  expect(sqs).to receive(:send_message_batch).with(hash_including(options))
135
136
 
136
- subject.send_messages([{ message_body: 'msg1',
137
- delay_seconds: 1,
138
- message_attributes: { attr: 'attr1' }
139
- }, {
140
- message_body: 'msg2',
141
- delay_seconds: 1,
142
- message_attributes: { attr: 'attr2' }
143
- }])
137
+ subject.send_messages(
138
+ [
139
+ { message_body: 'msg1', delay_seconds: 1, message_attributes: { attr: 'attr1' } },
140
+ { message_body: 'msg2', delay_seconds: 1, message_attributes: { attr: 'attr2' } }
141
+ ]
142
+ )
144
143
  end
145
144
 
146
145
  context 'when FIFO' do
@@ -170,16 +169,24 @@ RSpec.describe Shoryuken::Queue do
170
169
  expect(first_entry[:message_deduplication_id]).to eq 'my id'
171
170
  end
172
171
 
173
- subject.send_messages([{ message_body: 'msg1',
174
- message_attributes: { attr: 'attr1' },
175
- message_group_id: 'my group',
176
- message_deduplication_id: 'my id' }])
172
+ subject.send_messages(
173
+ [
174
+ { message_body: 'msg1',
175
+ message_attributes: { attr: 'attr1' },
176
+ message_group_id: 'my group',
177
+ message_deduplication_id: 'my id' }
178
+ ]
179
+ )
177
180
  end
178
181
  end
179
182
  end
180
183
 
181
184
  it 'accepts an array of string' do
182
- expect(sqs).to receive(:send_message_batch).with(hash_including(entries: [{ id: '0', message_body: 'msg1' }, { id: '1', message_body: 'msg2' }]))
185
+ expect(sqs).to(
186
+ receive(:send_message_batch).with(
187
+ hash_including(entries: [{ id: '0', message_body: 'msg1' }, { id: '1', message_body: 'msg2' }])
188
+ )
189
+ )
183
190
 
184
191
  subject.send_messages(%w(msg1 msg2))
185
192
  end
@@ -189,9 +196,13 @@ RSpec.describe Shoryuken::Queue do
189
196
  before do
190
197
  attribute_response = double 'Aws::SQS::Types::GetQueueAttributesResponse'
191
198
 
192
- allow(attribute_response).to receive(:attributes).and_return('FifoQueue' => fifo.to_s, 'ContentBasedDeduplication' => 'true')
199
+ allow(attribute_response).to(
200
+ receive(:attributes).and_return('FifoQueue' => fifo.to_s, 'ContentBasedDeduplication' => 'true')
201
+ )
193
202
  allow(subject).to receive(:url).and_return(queue_url)
194
- allow(sqs).to receive(:get_queue_attributes).with(queue_url: queue_url, attribute_names: ['All']).and_return(attribute_response)
203
+ allow(sqs).to(
204
+ receive(:get_queue_attributes).with(queue_url: queue_url, attribute_names: ['All']).and_return(attribute_response)
205
+ )
195
206
  end
196
207
 
197
208
  context 'when queue is FIFO' do
@@ -9,7 +9,7 @@ describe 'Shoryuken::Util' do
9
9
 
10
10
  describe '#unparse_queues' do
11
11
  it 'returns queues and weights' do
12
- queues = %w[queue1 queue1 queue2 queue3 queue4 queue4 queue4]
12
+ queues = %w(queue1 queue1 queue2 queue3 queue4 queue4 queue4)
13
13
 
14
14
  expect(subject.unparse_queues(queues)).to eq([['queue1', 2], ['queue2', 1], ['queue3', 1], ['queue4', 3]])
15
15
  end
@@ -26,4 +26,28 @@ describe 'Shoryuken::Util' do
26
26
 
27
27
  it 'returns ActiveJob worker name'
28
28
  end
29
+
30
+ describe '#fire_event' do
31
+ let(:value_holder) { Object.new }
32
+ let(:callback_without_options) { proc { value_holder.value = :without_options } }
33
+ let(:callback_with_options) { proc { |options| value_holder.value = [:with_options, options] } }
34
+
35
+ after :all do
36
+ Shoryuken.options[:lifecycle_events].delete(:some_event)
37
+ end
38
+
39
+ it 'triggers callbacks that do not accept arguments' do
40
+ Shoryuken.options[:lifecycle_events][:some_event] = [callback_without_options]
41
+
42
+ expect(value_holder).to receive(:value=).with(:without_options)
43
+ subject.fire_event(:some_event)
44
+ end
45
+
46
+ it 'triggers callbacks that accept an argument' do
47
+ Shoryuken.options[:lifecycle_events][:some_event] = [callback_with_options]
48
+
49
+ expect(value_holder).to receive(:value=).with([:with_options, { my_option: :some_option }])
50
+ subject.fire_event(:some_event, false, my_option: :some_option)
51
+ end
52
+ end
29
53
  end
@@ -0,0 +1,100 @@
1
+ require 'spec_helper'
2
+
3
+ RSpec.describe Shoryuken::Worker::DefaultExecutor do
4
+ let(:sqs_queue) { double 'SQS Queue' }
5
+ let(:queue) { 'default' }
6
+
7
+ before do
8
+ allow(Shoryuken::Client).to receive(:queues).with(queue).and_return(sqs_queue)
9
+ end
10
+
11
+ describe '.perform_in' do
12
+ it 'delays a message' do
13
+ expect(sqs_queue).to receive(:send_message).with(
14
+ message_attributes: {
15
+ 'shoryuken_class' => {
16
+ string_value: TestWorker.to_s,
17
+ data_type: 'String'
18
+ }
19
+ },
20
+ message_body: 'message',
21
+ delay_seconds: 60)
22
+
23
+ TestWorker.perform_in(60, 'message')
24
+ end
25
+
26
+ it 'raises an exception' do
27
+ expect {
28
+ TestWorker.perform_in(901, 'message')
29
+ }.to raise_error 'The maximum allowed delay is 15 minutes'
30
+ end
31
+ end
32
+
33
+ describe '.perform_at' do
34
+ it 'delays a message' do
35
+ expect(sqs_queue).to receive(:send_message).with(
36
+ message_attributes: {
37
+ 'shoryuken_class' => {
38
+ string_value: TestWorker.to_s,
39
+ data_type: 'String'
40
+ }
41
+ },
42
+ message_body: 'message',
43
+ delay_seconds: 60)
44
+
45
+ TestWorker.perform_in(Time.now + 60, 'message')
46
+ end
47
+
48
+ it 'raises an exception' do
49
+ expect {
50
+ TestWorker.perform_in(Time.now + 901, 'message')
51
+ }.to raise_error 'The maximum allowed delay is 15 minutes'
52
+ end
53
+ end
54
+
55
+ describe '.perform_async' do
56
+ it 'enqueues a message' do
57
+ expect(sqs_queue).to receive(:send_message).with(
58
+ message_attributes: {
59
+ 'shoryuken_class' => {
60
+ string_value: TestWorker.to_s,
61
+ data_type: 'String'
62
+ }
63
+ },
64
+ message_body: 'message')
65
+
66
+ TestWorker.perform_async('message')
67
+ end
68
+
69
+ it 'enqueues a message with options' do
70
+ expect(sqs_queue).to receive(:send_message).with(
71
+ delay_seconds: 60,
72
+ message_attributes: {
73
+ 'shoryuken_class' => {
74
+ string_value: TestWorker.to_s,
75
+ data_type: 'String'
76
+ }
77
+ },
78
+ message_body: 'delayed message')
79
+
80
+ TestWorker.perform_async('delayed message', delay_seconds: 60)
81
+ end
82
+
83
+ it 'accepts an `queue` option' do
84
+ new_queue = 'some_different_queue'
85
+
86
+ expect(Shoryuken::Client).to receive(:queues).with(new_queue).and_return(sqs_queue)
87
+
88
+ expect(sqs_queue).to receive(:send_message).with(
89
+ message_attributes: {
90
+ 'shoryuken_class' => {
91
+ string_value: TestWorker.to_s,
92
+ data_type: 'String'
93
+ }
94
+ },
95
+ message_body: 'delayed message')
96
+
97
+ TestWorker.perform_async('delayed message', queue: new_queue)
98
+ end
99
+ end
100
+ end