shoryuken 5.0.0 → 5.1.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/.github/FUNDING.yml +12 -0
  3. data/.github/workflows/specs.yml +57 -0
  4. data/.reek.yml +5 -0
  5. data/.rubocop.yml +3 -6
  6. data/Appraisals +28 -0
  7. data/CHANGELOG.md +49 -0
  8. data/Gemfile +3 -1
  9. data/README.md +19 -3
  10. data/Rakefile +3 -0
  11. data/bin/cli/sqs.rb +9 -1
  12. data/gemfiles/.gitignore +1 -0
  13. data/gemfiles/rails_4_2.gemfile +20 -0
  14. data/gemfiles/rails_5_2.gemfile +21 -0
  15. data/gemfiles/rails_6_0.gemfile +21 -0
  16. data/gemfiles/rails_6_1.gemfile +21 -0
  17. data/lib/shoryuken/default_worker_registry.rb +1 -1
  18. data/lib/shoryuken/environment_loader.rb +5 -13
  19. data/lib/shoryuken/extensions/active_job_adapter.rb +21 -16
  20. data/lib/shoryuken/extensions/active_job_concurrent_send_adapter.rb +50 -0
  21. data/lib/shoryuken/extensions/active_job_extensions.rb +37 -0
  22. data/lib/shoryuken/manager.rb +10 -4
  23. data/lib/shoryuken/options.rb +5 -3
  24. data/lib/shoryuken/polling/base.rb +2 -0
  25. data/lib/shoryuken/polling/strict_priority.rb +6 -0
  26. data/lib/shoryuken/polling/weighted_round_robin.rb +11 -0
  27. data/lib/shoryuken/queue.rb +39 -11
  28. data/lib/shoryuken/version.rb +1 -1
  29. data/lib/shoryuken.rb +5 -1
  30. data/shoryuken.gemspec +0 -1
  31. data/spec/shared_examples_for_active_job.rb +279 -0
  32. data/spec/shoryuken/environment_loader_spec.rb +24 -0
  33. data/spec/shoryuken/extensions/active_job_adapter_spec.rb +2 -59
  34. data/spec/shoryuken/extensions/active_job_base_spec.rb +73 -0
  35. data/spec/shoryuken/extensions/active_job_concurrent_send_adapter_spec.rb +35 -0
  36. data/spec/shoryuken/manager_spec.rb +24 -0
  37. data/spec/shoryuken/options_spec.rb +18 -0
  38. data/spec/shoryuken/polling/strict_priority_spec.rb +10 -0
  39. data/spec/shoryuken/polling/weighted_round_robin_spec.rb +10 -0
  40. data/spec/shoryuken/queue_spec.rb +23 -0
  41. data/spec/spec_helper.rb +6 -6
  42. metadata +23 -22
  43. data/.travis.yml +0 -34
@@ -38,6 +38,11 @@ module Shoryuken
38
38
  .reverse
39
39
  end
40
40
 
41
+ def message_processed(queue)
42
+ logger.debug "Unpausing #{queue}"
43
+ @paused_until[queue] = Time.now
44
+ end
45
+
41
46
  private
42
47
 
43
48
  def next_active_queue
@@ -70,6 +75,7 @@ module Shoryuken
70
75
 
71
76
  def pause(queue)
72
77
  return unless delay > 0
78
+
73
79
  @paused_until[queue] = Time.now + delay
74
80
  logger.debug "Paused #{queue}"
75
81
  end
@@ -35,10 +35,20 @@ module Shoryuken
35
35
  unparse_queues(@queues)
36
36
  end
37
37
 
38
+ def message_processed(queue)
39
+ return if @paused_queues.empty?
40
+
41
+ logger.debug "Unpausing #{queue}"
42
+ @paused_queues.reject! { |_time, name| name == queue }
43
+ @queues << queue
44
+ @queues.uniq!
45
+ end
46
+
38
47
  private
39
48
 
40
49
  def pause(queue)
41
50
  return unless @queues.delete(queue)
51
+
42
52
  @paused_queues << [Time.now + delay, queue]
43
53
  logger.debug "Paused #{queue}"
44
54
  end
@@ -46,6 +56,7 @@ module Shoryuken
46
56
  def unpause_queues
47
57
  return if @paused_queues.empty?
48
58
  return if Time.now < @paused_queues.first[0]
59
+
49
60
  pause = @paused_queues.shift
50
61
  @queues << pause[1]
51
62
  logger.debug "Unpaused #{pause[1]}"
@@ -8,9 +8,9 @@ module Shoryuken
8
8
 
9
9
  attr_accessor :name, :client, :url
10
10
 
11
- def initialize(client, name_or_url)
11
+ def initialize(client, name_or_url_or_arn)
12
12
  self.client = client
13
- set_name_and_url(name_or_url)
13
+ set_name_and_url(name_or_url_or_arn)
14
14
  end
15
15
 
16
16
  def visibility_timeout
@@ -50,32 +50,60 @@ module Shoryuken
50
50
  # Make sure the memoization work with boolean to avoid multiple calls to SQS
51
51
  # see https://github.com/phstc/shoryuken/pull/529
52
52
  return @_fifo if defined?(@_fifo)
53
+
53
54
  @_fifo = queue_attributes.attributes[FIFO_ATTR] == 'true'
55
+ @_fifo
54
56
  end
55
57
 
56
58
  private
57
59
 
58
- def set_by_name(name)
60
+ def initialize_fifo_attribute
61
+ # calling fifo? will also initialize it
62
+ fifo?
63
+ end
64
+
65
+ def set_by_name(name) # rubocop:disable Naming/AccessorMethodName
59
66
  self.name = name
60
67
  self.url = client.get_queue_url(queue_name: name).queue_url
61
68
  end
62
69
 
63
- def set_by_url(url)
70
+ def set_by_url(url) # rubocop:disable Naming/AccessorMethodName
64
71
  self.name = url.split('/').last
65
72
  self.url = url
66
73
  end
67
74
 
68
- def set_name_and_url(name_or_url)
69
- if name_or_url.include?('://')
70
- set_by_url(name_or_url)
75
+ def arn_to_url(arn_str)
76
+ *, region, account_id, resource = arn_str.split(':')
77
+
78
+ required = [region, account_id, resource].map(&:to_s)
79
+ valid = required.none?(&:empty?)
80
+
81
+ abort "Invalid ARN: #{arn_str}. A valid ARN must include: region, account_id and resource." unless valid
82
+
83
+ "https://sqs.#{region}.amazonaws.com/#{account_id}/#{resource}"
84
+ end
85
+
86
+ def set_name_and_url(name_or_url_or_arn) # rubocop:disable Naming/AccessorMethodName
87
+ if name_or_url_or_arn.include?('://')
88
+ set_by_url(name_or_url_or_arn)
89
+
90
+ # anticipate the fifo? checker for validating the queue URL
91
+ initialize_fifo_attribute
92
+ return
93
+ end
94
+
95
+ if name_or_url_or_arn.start_with?('arn:')
96
+ url = arn_to_url(name_or_url_or_arn)
97
+ set_by_url(url)
71
98
 
72
99
  # anticipate the fifo? checker for validating the queue URL
73
- return fifo?
100
+ initialize_fifo_attribute
101
+ return
74
102
  end
75
103
 
76
- set_by_name(name_or_url)
77
- rescue Aws::Errors::NoSuchEndpointError, Aws::SQS::Errors::NonExistentQueue => ex
78
- raise ex, "The specified queue #{name_or_url} does not exist."
104
+ set_by_name(name_or_url_or_arn)
105
+ rescue Aws::Errors::NoSuchEndpointError, Aws::SQS::Errors::NonExistentQueue => e
106
+ raise e, "The specified queue #{name_or_url_or_arn} does not exist."
79
107
  end
80
108
 
81
109
  def queue_attributes
@@ -1,3 +1,3 @@
1
1
  module Shoryuken
2
- VERSION = '5.0.0'.freeze
2
+ VERSION = '5.1.0'.freeze
3
3
  end
data/lib/shoryuken.rb CHANGED
@@ -88,4 +88,8 @@ module Shoryuken
88
88
  )
89
89
  end
90
90
 
91
- require 'shoryuken/extensions/active_job_adapter' if Shoryuken.active_job?
91
+ if Shoryuken.active_job?
92
+ require 'shoryuken/extensions/active_job_extensions'
93
+ require 'shoryuken/extensions/active_job_adapter'
94
+ require 'shoryuken/extensions/active_job_concurrent_send_adapter'
95
+ end
data/shoryuken.gemspec CHANGED
@@ -18,7 +18,6 @@ Gem::Specification.new do |spec|
18
18
  spec.require_paths = ['lib']
19
19
 
20
20
  spec.add_development_dependency 'dotenv'
21
- spec.add_development_dependency 'pry-byebug'
22
21
  spec.add_development_dependency 'rake'
23
22
  spec.add_development_dependency 'rspec'
24
23
 
@@ -0,0 +1,279 @@
1
+ require 'active_job'
2
+ require 'shoryuken/extensions/active_job_extensions'
3
+
4
+ # Stand-in for a job class specified by the user
5
+ class TestJob < ActiveJob::Base; end
6
+
7
+ # rubocop:disable Metrics/BlockLength
8
+ RSpec.shared_examples 'active_job_adapters' do
9
+ let(:job_sqs_send_message_parameters) { {} }
10
+ let(:job) do
11
+ job = TestJob.new
12
+ job.sqs_send_message_parameters = job_sqs_send_message_parameters
13
+ job
14
+ end
15
+ let(:fifo) { false }
16
+ let(:queue) { double 'Queue', fifo?: fifo }
17
+
18
+ before do
19
+ allow(Shoryuken::Client).to receive(:queues).with(job.queue_name).and_return(queue)
20
+ end
21
+
22
+ describe '#enqueue' do
23
+ specify do
24
+ expect(queue).to receive(:send_message) do |hash|
25
+ expect(hash[:message_deduplication_id]).to_not be
26
+ expect(hash[:message_attributes]['shoryuken_class'][:string_value]).to eq(described_class::JobWrapper.to_s)
27
+ expect(hash[:message_attributes]['shoryuken_class'][:data_type]).to eq("String")
28
+ expect(hash[:message_attributes].keys).to eq(['shoryuken_class'])
29
+ end
30
+ expect(Shoryuken).to receive(:register_worker).with(job.queue_name, described_class::JobWrapper)
31
+
32
+ subject.enqueue(job)
33
+ end
34
+
35
+ it "should mutate the job's sqs_send_message_parameters reference to match those sent to the queue" do
36
+ expect(queue).to receive(:send_message) do |options|
37
+ expect(options).to be(job.sqs_send_message_parameters)
38
+ end
39
+ subject.enqueue(job)
40
+ end
41
+
42
+ context 'when fifo' do
43
+ let(:fifo) { true }
44
+
45
+ it 'does not include job_id in the deduplication_id' do
46
+ expect(queue).to receive(:send_message) do |hash|
47
+ message_deduplication_id = Digest::SHA256.hexdigest(JSON.dump(job.serialize.except('job_id')))
48
+
49
+ expect(hash[:message_deduplication_id]).to eq(message_deduplication_id)
50
+ end
51
+ expect(Shoryuken).to receive(:register_worker).with(job.queue_name, described_class::JobWrapper)
52
+
53
+ subject.enqueue(job)
54
+ end
55
+
56
+ context 'with message_deduplication_id' do
57
+ context 'when message_deduplication_id is specified in options' do
58
+ it 'should enqueue a message with the deduplication_id specified in options' do
59
+ expect(queue).to receive(:send_message) do |hash|
60
+ expect(hash[:message_deduplication_id]).to eq('options-dedupe-id')
61
+ end
62
+ subject.enqueue(job, message_deduplication_id: 'options-dedupe-id')
63
+ end
64
+ end
65
+
66
+ context 'when message_deduplication_id is specified on the job' do
67
+ let(:job_sqs_send_message_parameters) { { message_deduplication_id: 'job-dedupe-id' } }
68
+
69
+ it 'should enqueue a message with the deduplication_id specified on the job' do
70
+ expect(queue).to receive(:send_message) do |hash|
71
+ expect(hash[:message_deduplication_id]).to eq('job-dedupe-id')
72
+ end
73
+ subject.enqueue job
74
+ end
75
+ end
76
+
77
+ context 'when message_deduplication_id is specified on the job and also in options' do
78
+ let(:job_sqs_send_message_parameters) { { message_deduplication_id: 'job-dedupe-id' } }
79
+
80
+ it 'should enqueue a message with the deduplication_id specified in options' do
81
+ expect(queue).to receive(:send_message) do |hash|
82
+ expect(hash[:message_deduplication_id]).to eq('options-dedupe-id')
83
+ end
84
+ subject.enqueue(job, message_deduplication_id: 'options-dedupe-id')
85
+ end
86
+ end
87
+ end
88
+ end
89
+
90
+ context 'with message_group_id' do
91
+ context 'when message_group_id is specified in options' do
92
+ it 'should enqueue a message with the group_id specified in options' do
93
+ expect(queue).to receive(:send_message) do |hash|
94
+ expect(hash[:message_group_id]).to eq('options-group-id')
95
+ end
96
+ subject.enqueue(job, message_group_id: 'options-group-id')
97
+ end
98
+ end
99
+
100
+ context 'when message_group_id is specified on the job' do
101
+ let(:job_sqs_send_message_parameters) { { message_group_id: 'job-group-id' } }
102
+
103
+ it 'should enqueue a message with the group_id specified on the job' do
104
+ expect(queue).to receive(:send_message) do |hash|
105
+ expect(hash[:message_group_id]).to eq('job-group-id')
106
+ end
107
+ subject.enqueue job
108
+ end
109
+ end
110
+
111
+ context 'when message_group_id is specified on the job and also in options' do
112
+ let(:job_sqs_send_message_parameters) { { message_group_id: 'job-group-id' } }
113
+
114
+ it 'should enqueue a message with the group_id specified in options' do
115
+ expect(queue).to receive(:send_message) do |hash|
116
+ expect(hash[:message_group_id]).to eq('options-group-id')
117
+ end
118
+ subject.enqueue(job, message_group_id: 'options-group-id')
119
+ end
120
+ end
121
+ end
122
+
123
+ context 'with additional message attributes' do
124
+ it 'should combine with activejob attributes' do
125
+ custom_message_attributes = {
126
+ 'tracer_id' => {
127
+ string_value: SecureRandom.hex,
128
+ data_type: 'String'
129
+ }
130
+ }
131
+
132
+ expect(queue).to receive(:send_message) do |hash|
133
+ expect(hash[:message_attributes]['shoryuken_class'][:string_value]).to eq(described_class::JobWrapper.to_s)
134
+ expect(hash[:message_attributes]['shoryuken_class'][:data_type]).to eq("String")
135
+ expect(hash[:message_attributes]['tracer_id'][:string_value]).to eq(custom_message_attributes['tracer_id'][:string_value])
136
+ expect(hash[:message_attributes]['tracer_id'][:data_type]).to eq("String")
137
+ end
138
+ expect(Shoryuken).to receive(:register_worker).with(job.queue_name, described_class::JobWrapper)
139
+
140
+ subject.enqueue(job, message_attributes: custom_message_attributes)
141
+ end
142
+
143
+ context 'when message_attributes are specified on the job' do
144
+ let(:job_sqs_send_message_parameters) do
145
+ {
146
+ message_attributes: {
147
+ 'tracer_id' => {
148
+ data_type: 'String',
149
+ string_value: 'job-value'
150
+ }
151
+ }
152
+ }
153
+ end
154
+
155
+ it 'should enqueue a message with the message_attributes specified on the job' do
156
+ expect(queue).to receive(:send_message) do |hash|
157
+ expect(hash[:message_attributes]['tracer_id']).to eq({ data_type: 'String', string_value: 'job-value' })
158
+ expect(hash[:message_attributes]['shoryuken_class']).to eq({ data_type: 'String', string_value: described_class::JobWrapper.to_s })
159
+ end
160
+ subject.enqueue job
161
+ end
162
+ end
163
+
164
+ context 'when message_attributes are specified on the job and also in options' do
165
+ let(:job_sqs_send_message_parameters) do
166
+ {
167
+ message_attributes: {
168
+ 'tracer_id' => {
169
+ data_type: 'String',
170
+ string_value: 'job-value'
171
+ }
172
+ }
173
+ }
174
+ end
175
+
176
+ it 'should enqueue a message with the message_attributes speficied in options' do
177
+ custom_message_attributes = {
178
+ 'options_tracer_id' => {
179
+ string_value: 'options-value',
180
+ data_type: 'String'
181
+ }
182
+ }
183
+
184
+ expect(queue).to receive(:send_message) do |hash|
185
+ expect(hash[:message_attributes]['tracer_id']).to be_nil
186
+ expect(hash[:message_attributes]['options_tracer_id']).to eq({ data_type: 'String', string_value: 'options-value' })
187
+ expect(hash[:message_attributes]['shoryuken_class']).to eq({ data_type: 'String', string_value: described_class::JobWrapper.to_s })
188
+ end
189
+ subject.enqueue job, message_attributes: custom_message_attributes
190
+ end
191
+ end
192
+ end
193
+ end
194
+
195
+ context 'with message_system_attributes' do
196
+ context 'when message_system_attributes are specified in options' do
197
+ it 'should enqueue a message with message_system_attributes specified in options' do
198
+ system_attributes = {
199
+ 'AWSTraceHeader' => {
200
+ string_value: 'trace_id',
201
+ data_type: 'String'
202
+ }
203
+ }
204
+ expect(queue).to receive(:send_message) do |hash|
205
+ expect(hash[:message_system_attributes]['AWSTraceHeader'][:string_value]).to eq('trace_id')
206
+ expect(hash[:message_system_attributes]['AWSTraceHeader'][:data_type]).to eq('String')
207
+ end
208
+ subject.enqueue(job, message_system_attributes: system_attributes)
209
+ end
210
+ end
211
+
212
+ context 'when message_system_attributes are specified on the job' do
213
+ let(:job_sqs_send_message_parameters) do
214
+ {
215
+ message_system_attributes: {
216
+ 'AWSTraceHeader' => {
217
+ string_value: 'job-value',
218
+ data_type: 'String'
219
+ }
220
+ }
221
+ }
222
+ end
223
+
224
+ it 'should enqueue a message with the message_system_attributes specified on the job' do
225
+ expect(queue).to receive(:send_message) do |hash|
226
+ expect(hash[:message_system_attributes]['AWSTraceHeader']).to eq({ data_type: 'String', string_value: 'job-value' })
227
+ end
228
+ subject.enqueue job
229
+ end
230
+ end
231
+
232
+ context 'when message_system_attributes are specified on the job and also in options' do
233
+ let(:job_sqs_send_message_parameters) do
234
+ {
235
+ message_system_attributes: {
236
+ 'job_trace_header' => {
237
+ string_value: 'job-value',
238
+ data_type: 'String'
239
+ }
240
+ }
241
+ }
242
+ end
243
+
244
+ it 'should enqueue a message with the message_system_attributes speficied in options' do
245
+ custom_message_attributes = {
246
+ 'options_trace_header' => {
247
+ string_value: 'options-value',
248
+ data_type: 'String'
249
+ }
250
+ }
251
+
252
+ expect(queue).to receive(:send_message) do |hash|
253
+ expect(hash[:message_system_attributes]['job_trace_header']).to be_nil
254
+ expect(hash[:message_system_attributes]['options_trace_header']).to eq({ data_type: 'String', string_value: 'options-value' })
255
+ end
256
+ subject.enqueue job, message_system_attributes: custom_message_attributes
257
+ end
258
+ end
259
+ end
260
+
261
+ describe '#enqueue_at' do
262
+ specify do
263
+ delay = 1
264
+
265
+ expect(queue).to receive(:send_message) do |hash|
266
+ expect(hash[:message_deduplication_id]).to_not be
267
+ expect(hash[:delay_seconds]).to eq(delay)
268
+ end
269
+
270
+ expect(Shoryuken).to receive(:register_worker).with(job.queue_name, described_class::JobWrapper)
271
+
272
+ # need to figure out what to require Time.current and N.minutes to remove the stub
273
+ allow(subject).to receive(:calculate_delay).and_return(delay)
274
+
275
+ subject.enqueue_at(job, nil)
276
+ end
277
+ end
278
+ end
279
+ # rubocop:enable Metrics/BlockLength
@@ -74,4 +74,28 @@ RSpec.describe Shoryuken::EnvironmentLoader do
74
74
  expect(Shoryuken.groups['group1'][:queues]).to eq(%w[test_group1_queue1 test_group1_queue2])
75
75
  end
76
76
  end
77
+ describe "#setup_options" do
78
+ let (:cli_queues) { { "queue1"=> 10, "queue2" => 20 } }
79
+ let (:config_queues) { [["queue1", 8], ["queue2", 4]] }
80
+ context "when given queues through config and CLI" do
81
+ specify do
82
+ allow_any_instance_of(Shoryuken::EnvironmentLoader).to receive(:config_file_options).and_return({ queues: config_queues })
83
+ Shoryuken::EnvironmentLoader.setup_options(queues: cli_queues)
84
+ expect(Shoryuken.options[:queues]).to eq(cli_queues)
85
+ end
86
+ end
87
+ context "when given queues through config only" do
88
+ specify do
89
+ allow_any_instance_of(Shoryuken::EnvironmentLoader).to receive(:config_file_options).and_return({ queues: config_queues })
90
+ Shoryuken::EnvironmentLoader.setup_options({})
91
+ expect(Shoryuken.options[:queues]).to eq(config_queues)
92
+ end
93
+ end
94
+ context "when given queues through CLI only" do
95
+ specify do
96
+ Shoryuken::EnvironmentLoader.setup_options(queues: cli_queues)
97
+ expect(Shoryuken.options[:queues]).to eq(cli_queues)
98
+ end
99
+ end
100
+ end
77
101
  end
@@ -1,64 +1,7 @@
1
1
  require 'spec_helper'
2
- require 'active_job'
2
+ require 'shared_examples_for_active_job'
3
3
  require 'shoryuken/extensions/active_job_adapter'
4
4
 
5
5
  RSpec.describe ActiveJob::QueueAdapters::ShoryukenAdapter do
6
- let(:job) { double 'Job', id: '123', queue_name: 'queue' }
7
- let(:fifo) { false }
8
- let(:queue) { double 'Queue', fifo?: fifo }
9
-
10
- before do
11
- allow(Shoryuken::Client).to receive(:queues).with(job.queue_name).and_return(queue)
12
- allow(job).to receive(:serialize).and_return(
13
- 'job_class' => 'Worker',
14
- 'job_id' => job.id,
15
- 'queue_name' => job.queue_name,
16
- 'arguments' => nil,
17
- 'locale' => nil
18
- )
19
- end
20
-
21
- describe '#enqueue' do
22
- specify do
23
- expect(queue).to receive(:send_message) do |hash|
24
- expect(hash[:message_deduplication_id]).to_not be
25
- end
26
- expect(Shoryuken).to receive(:register_worker).with(job.queue_name, described_class::JobWrapper)
27
-
28
- subject.enqueue(job)
29
- end
30
-
31
- context 'when fifo' do
32
- let(:fifo) { true }
33
-
34
- it 'does not include job_id in the deduplication_id' do
35
- expect(queue).to receive(:send_message) do |hash|
36
- message_deduplication_id = Digest::SHA256.hexdigest(JSON.dump(job.serialize.except('job_id')))
37
-
38
- expect(hash[:message_deduplication_id]).to eq(message_deduplication_id)
39
- end
40
- expect(Shoryuken).to receive(:register_worker).with(job.queue_name, described_class::JobWrapper)
41
-
42
- subject.enqueue(job)
43
- end
44
- end
45
- end
46
-
47
- describe '#enqueue_at' do
48
- specify do
49
- delay = 1
50
-
51
- expect(queue).to receive(:send_message) do |hash|
52
- expect(hash[:message_deduplication_id]).to_not be
53
- expect(hash[:delay_seconds]).to eq(delay)
54
- end
55
-
56
- expect(Shoryuken).to receive(:register_worker).with(job.queue_name, described_class::JobWrapper)
57
-
58
- # need to figure out what to require Time.current and N.minutes to remove the stub
59
- allow(subject).to receive(:calculate_delay).and_return(delay)
60
-
61
- subject.enqueue_at(job, nil)
62
- end
63
- end
6
+ include_examples 'active_job_adapters'
64
7
  end
@@ -0,0 +1,73 @@
1
+ require 'spec_helper'
2
+ require 'active_job'
3
+ require 'shoryuken/extensions/active_job_extensions'
4
+ require 'shoryuken/extensions/active_job_adapter'
5
+
6
+ RSpec.describe ActiveJob::Base do
7
+ let(:queue_adapter) { ActiveJob::QueueAdapters::ShoryukenAdapter.new }
8
+
9
+ subject do
10
+ worker_class = Class.new(described_class)
11
+ Object.const_set :MyWorker, worker_class
12
+ worker_class.queue_adapter = queue_adapter
13
+ worker_class
14
+ end
15
+
16
+ after do
17
+ Object.send :remove_const, :MyWorker
18
+ end
19
+
20
+ describe '#perform_later' do
21
+ it 'calls enqueue on the adapter with the expected job' do
22
+ expect(queue_adapter).to receive(:enqueue) do |job|
23
+ expect(job.arguments).to eq([1, 2])
24
+ end
25
+
26
+ subject.perform_later 1, 2
27
+ end
28
+
29
+ it 'passes message_group_id to the queue_adapter' do
30
+ expect(queue_adapter).to receive(:enqueue) do |job|
31
+ expect(job.sqs_send_message_parameters[:message_group_id]).to eq('group-2')
32
+ end
33
+
34
+ subject.set(message_group_id: 'group-2').perform_later 1, 2
35
+ end
36
+
37
+ it 'passes message_deduplication_id to the queue_adapter' do
38
+ expect(queue_adapter).to receive(:enqueue) do |job|
39
+ expect(job.sqs_send_message_parameters[:message_deduplication_id]).to eq('dedupe-id')
40
+ end
41
+
42
+ subject.set(message_deduplication_id: 'dedupe-id').perform_later 1, 2
43
+ end
44
+
45
+ it 'passes message_attributes to the queue_adapter' do
46
+ message_attributes = {
47
+ 'custom_tracing_id' => {
48
+ string_value: 'value',
49
+ data_type: 'String'
50
+ }
51
+ }
52
+ expect(queue_adapter).to receive(:enqueue) do |job|
53
+ expect(job.sqs_send_message_parameters[:message_attributes]).to eq(message_attributes)
54
+ end
55
+
56
+ subject.set(message_attributes: message_attributes).perform_later 1, 2
57
+ end
58
+
59
+ it 'passes message_system_attributes to the queue_adapter' do
60
+ message_system_attributes = {
61
+ 'AWSTraceHeader' => {
62
+ string_value: 'trace_id',
63
+ data_type: 'String'
64
+ }
65
+ }
66
+ expect(queue_adapter).to receive(:enqueue) do |job|
67
+ expect(job.sqs_send_message_parameters[:message_system_attributes]).to eq(message_system_attributes)
68
+ end
69
+
70
+ subject.set(message_system_attributes: message_system_attributes).perform_later 1, 2
71
+ end
72
+ end
73
+ end
@@ -0,0 +1,35 @@
1
+ require 'spec_helper'
2
+ require 'shared_examples_for_active_job'
3
+ require 'shoryuken/extensions/active_job_adapter'
4
+ require 'shoryuken/extensions/active_job_concurrent_send_adapter'
5
+
6
+ RSpec.describe ActiveJob::QueueAdapters::ShoryukenConcurrentSendAdapter do
7
+ include_examples 'active_job_adapters'
8
+
9
+ let(:options) { {} }
10
+ let(:error_handler) { -> {} }
11
+ let(:success_handler) { -> {} }
12
+
13
+ subject { described_class.new(success_handler, error_handler) }
14
+
15
+ context 'when success' do
16
+ it 'calls success_handler' do
17
+ response = true
18
+ allow(queue).to receive(:send_message).and_return(response)
19
+ expect(success_handler).to receive(:call).with(response, job, options)
20
+
21
+ subject.enqueue(job, options)
22
+ end
23
+ end
24
+
25
+ context 'when failure' do
26
+ it 'calls error_handler' do
27
+ response = Aws::SQS::Errors::InternalError.new('error', 'error')
28
+
29
+ allow(queue).to receive(:send_message).and_raise(response)
30
+ expect(error_handler).to receive(:call).with(response, job, options).and_call_original
31
+
32
+ subject.enqueue(job, options)
33
+ end
34
+ end
35
+ end
@@ -139,4 +139,28 @@ RSpec.describe Shoryuken::Manager do
139
139
  subject.send(:dispatch_single_messages, q)
140
140
  end
141
141
  end
142
+
143
+ describe '#processor_done' do
144
+ let(:sqs_queue) { double Shoryuken::Queue }
145
+
146
+ before do
147
+ allow(Shoryuken::Client).to receive(:queues).with(queue).and_return(sqs_queue)
148
+ end
149
+
150
+ context 'when queue.fifo? is true' do
151
+ it 'calls message_processed on strategy' do
152
+ expect(sqs_queue).to receive(:fifo?).and_return(true)
153
+ expect(polling_strategy).to receive(:message_processed).with(queue)
154
+ subject.send(:processor_done, queue)
155
+ end
156
+ end
157
+
158
+ context 'when queue.fifo? is false' do
159
+ it 'does not call message_processed on strategy' do
160
+ expect(sqs_queue).to receive(:fifo?).and_return(false)
161
+ expect(polling_strategy).to_not receive(:message_processed)
162
+ subject.send(:processor_done, queue)
163
+ end
164
+ end
165
+ end
142
166
  end