shoryuken 5.2.3 → 6.2.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 (49) hide show
  1. checksums.yaml +4 -4
  2. data/.devcontainer/Dockerfile +17 -0
  3. data/.devcontainer/base.Dockerfile +43 -0
  4. data/.devcontainer/devcontainer.json +35 -0
  5. data/.github/dependabot.yml +6 -0
  6. data/.github/workflows/specs.yml +5 -2
  7. data/.github/workflows/stale.yml +20 -0
  8. data/.gitignore +1 -1
  9. data/.rubocop.yml +1 -1
  10. data/Appraisals +6 -0
  11. data/CHANGELOG.md +152 -0
  12. data/Gemfile +6 -3
  13. data/README.md +19 -1
  14. data/bin/cli/sqs.rb +1 -1
  15. data/gemfiles/aws_sdk_core_2.gemfile +1 -1
  16. data/gemfiles/rails_7_0.gemfile +22 -0
  17. data/lib/shoryuken/default_exception_handler.rb +10 -0
  18. data/lib/shoryuken/environment_loader.rb +22 -3
  19. data/lib/shoryuken/extensions/active_job_adapter.rb +5 -2
  20. data/lib/shoryuken/extensions/active_job_extensions.rb +1 -1
  21. data/lib/shoryuken/launcher.rb +25 -3
  22. data/lib/shoryuken/logging.rb +2 -2
  23. data/lib/shoryuken/manager.rb +27 -10
  24. data/lib/shoryuken/message.rb +11 -28
  25. data/lib/shoryuken/middleware/server/active_record.rb +5 -1
  26. data/lib/shoryuken/options.rb +14 -6
  27. data/lib/shoryuken/polling/strict_priority.rb +4 -2
  28. data/lib/shoryuken/polling/weighted_round_robin.rb +3 -5
  29. data/lib/shoryuken/processor.rb +14 -6
  30. data/lib/shoryuken/queue.rb +5 -3
  31. data/lib/shoryuken/runner.rb +4 -3
  32. data/lib/shoryuken/version.rb +1 -1
  33. data/lib/shoryuken.rb +11 -0
  34. data/shoryuken.gemspec +1 -1
  35. data/spec/shared_examples_for_active_job.rb +4 -2
  36. data/spec/shoryuken/default_exception_handler_spec.rb +71 -0
  37. data/spec/shoryuken/environment_loader_spec.rb +42 -9
  38. data/spec/shoryuken/extensions/active_job_base_spec.rb +1 -1
  39. data/spec/shoryuken/fetcher_spec.rb +12 -12
  40. data/spec/shoryuken/launcher_spec.rb +105 -0
  41. data/spec/shoryuken/manager_spec.rb +32 -6
  42. data/spec/shoryuken/polling/weighted_round_robin_spec.rb +31 -6
  43. data/spec/shoryuken/processor_spec.rb +38 -0
  44. data/spec/shoryuken/queue_spec.rb +10 -5
  45. data/spec/shoryuken/util_spec.rb +24 -4
  46. data/spec/shoryuken/worker/default_executor_spec.rb +48 -48
  47. data/spec/shoryuken_spec.rb +9 -0
  48. data/spec/spec_helper.rb +2 -0
  49. metadata +18 -7
@@ -0,0 +1,71 @@
1
+ require 'spec_helper'
2
+
3
+ # rubocop:disable Metrics/BlockLength
4
+ RSpec.describe Shoryuken::DefaultExceptionHandler do
5
+ class CustomErrorHandler
6
+ extend Shoryuken::Util
7
+
8
+ def self.call(_ex, queue, _msg)
9
+ logger.error("#{queue.to_s} failed to process the message")
10
+ end
11
+ end
12
+
13
+ before do
14
+ Shoryuken.worker_executor = Shoryuken::Worker::InlineExecutor
15
+ allow(manager).to receive(:async).and_return(manager)
16
+ allow(manager).to receive(:real_thread)
17
+ allow(Shoryuken::Client).to receive(:queues).with(queue).and_return(sqs_queue)
18
+ end
19
+
20
+ after do
21
+ Shoryuken.worker_executor = Shoryuken::Worker::DefaultExecutor
22
+ end
23
+
24
+ let(:manager) { double Shoryuken::Manager }
25
+ let(:sqs_queue) { double Shoryuken::Queue, visibility_timeout: 30 }
26
+ let(:queue) { 'default' }
27
+
28
+ let(:sqs_msg) do
29
+ double(
30
+ Shoryuken::Message,
31
+ queue_url: queue,
32
+ body: 'test',
33
+ message_attributes: {},
34
+ message_id: SecureRandom.uuid,
35
+ receipt_handle: SecureRandom.uuid
36
+ )
37
+ end
38
+
39
+ subject { Shoryuken::Processor.new(queue, sqs_msg) }
40
+
41
+ context "with default handler" do
42
+ before do
43
+ Shoryuken.exception_handlers = described_class
44
+ end
45
+
46
+ it "logs an error message" do
47
+ expect(Shoryuken::Logging.logger).to receive(:error).twice
48
+
49
+ allow_any_instance_of(TestWorker).to receive(:perform).and_raise(StandardError, "error")
50
+ allow(sqs_msg).to receive(:body)
51
+
52
+ expect { subject.process }.to raise_error(StandardError)
53
+ end
54
+ end
55
+
56
+ context "with custom handler" do
57
+ before do
58
+ Shoryuken.exception_handlers = [described_class, CustomErrorHandler]
59
+ end
60
+
61
+ it "logs default and custom error messages" do
62
+ expect(Shoryuken::Logging.logger).to receive(:error).twice
63
+ expect(Shoryuken::Logging.logger).to receive(:error).with("default failed to process the message").once
64
+
65
+ allow_any_instance_of(TestWorker).to receive(:perform).and_raise(StandardError, "error")
66
+ allow(sqs_msg).to receive(:body)
67
+
68
+ expect { subject.process }.to raise_error(StandardError)
69
+ end
70
+ end
71
+ end
@@ -4,11 +4,41 @@ require 'active_job'
4
4
  RSpec.describe Shoryuken::EnvironmentLoader do
5
5
  subject { described_class.new({}) }
6
6
 
7
- describe '#parse_queues loads default queues' do
7
+ describe '#load' do
8
8
  before do
9
+ Shoryuken.groups.clear
10
+ # See issue: https://stackoverflow.com/a/63699568 for stubbing AWS errors
11
+ allow(Shoryuken::Client)
12
+ .to receive(:queues)
13
+ .with('stubbed_queue')
14
+ .and_raise(Aws::SQS::Errors::NonExistentQueue.new(nil, nil))
9
15
  allow(subject).to receive(:load_rails)
10
16
  allow(subject).to receive(:prefix_active_job_queue_names)
11
17
  allow(subject).to receive(:require_workers)
18
+ allow(subject).to receive(:validate_workers)
19
+ allow(subject).to receive(:patch_deprecated_workers)
20
+ Shoryuken.options[:groups] = [['custom', { queues: ['stubbed_queue'] }]]
21
+ end
22
+
23
+ context "when given queues don't exist" do
24
+ specify do
25
+ expect { subject.load }.to raise_error(
26
+ ArgumentError,
27
+ <<-MSG.gsub(/^\s+/, '')
28
+ The specified queue(s) stubbed_queue do not exist.
29
+ Try 'shoryuken sqs create QUEUE-NAME' for creating a queue with default settings.
30
+ It's also possible that you don't have permission to access the specified queues.
31
+ MSG
32
+ )
33
+ end
34
+ end
35
+ end
36
+
37
+ describe '#parse_queues loads default queues' do
38
+ before do
39
+ allow(subject).to receive(:initialize_rails)
40
+ allow(subject).to receive(:prefix_active_job_queue_names)
41
+ allow(subject).to receive(:require_workers)
12
42
  allow(subject).to receive(:validate_queues)
13
43
  allow(subject).to receive(:validate_workers)
14
44
  allow(subject).to receive(:patch_deprecated_workers)
@@ -24,7 +54,7 @@ RSpec.describe Shoryuken::EnvironmentLoader do
24
54
 
25
55
  describe '#parse_queues includes delay per groups' do
26
56
  before do
27
- allow(subject).to receive(:load_rails)
57
+ allow(subject).to receive(:initialize_rails)
28
58
  allow(subject).to receive(:prefix_active_job_queue_names)
29
59
  allow(subject).to receive(:require_workers)
30
60
  allow(subject).to receive(:validate_queues)
@@ -34,7 +64,7 @@ RSpec.describe Shoryuken::EnvironmentLoader do
34
64
 
35
65
  specify do
36
66
  Shoryuken.options[:queues] = ['queue1', 'queue2'] # default queues
37
- Shoryuken.options[:groups] = [[ 'custom', { queues: ['queue3'], delay: 25 }]]
67
+ Shoryuken.options[:groups] = [['custom', { queues: ['queue3'], delay: 25 }]]
38
68
  subject.load
39
69
 
40
70
  expect(Shoryuken.groups['default'][:queues]).to eq(%w[queue1 queue2])
@@ -44,10 +74,9 @@ RSpec.describe Shoryuken::EnvironmentLoader do
44
74
  end
45
75
  end
46
76
 
47
-
48
77
  describe '#prefix_active_job_queue_names' do
49
78
  before do
50
- allow(subject).to receive(:load_rails)
79
+ allow(subject).to receive(:initialize_rails)
51
80
  allow(subject).to receive(:require_workers)
52
81
  allow(subject).to receive(:validate_queues)
53
82
  allow(subject).to receive(:validate_workers)
@@ -76,7 +105,7 @@ RSpec.describe Shoryuken::EnvironmentLoader do
76
105
 
77
106
  it 'does not prefix url-based queues' do
78
107
  Shoryuken.options[:queues] = ['https://example.com/test_queue1']
79
- Shoryuken.options[:groups] = {'group1' => {queues: ['https://example.com/test_group1_queue1']}}
108
+ Shoryuken.options[:groups] = { 'group1' => { queues: ['https://example.com/test_group1_queue1'] } }
80
109
 
81
110
  subject.load
82
111
 
@@ -86,7 +115,7 @@ RSpec.describe Shoryuken::EnvironmentLoader do
86
115
 
87
116
  it 'does not prefix arn-based queues' do
88
117
  Shoryuken.options[:queues] = ['arn:aws:sqs:fake-region-1:1234:test_queue1']
89
- Shoryuken.options[:groups] = {'group1' => {queues: ['arn:aws:sqs:fake-region-1:1234:test_group1_queue1']}}
118
+ Shoryuken.options[:groups] = { 'group1' => { queues: ['arn:aws:sqs:fake-region-1:1234:test_group1_queue1'] } }
90
119
 
91
120
  subject.load
92
121
 
@@ -94,9 +123,11 @@ RSpec.describe Shoryuken::EnvironmentLoader do
94
123
  expect(Shoryuken.groups['group1'][:queues]).to(eq(['arn:aws:sqs:fake-region-1:1234:test_group1_queue1']))
95
124
  end
96
125
  end
126
+
97
127
  describe "#setup_options" do
98
- let (:cli_queues) { { "queue1"=> 10, "queue2" => 20 } }
99
- let (:config_queues) { [["queue1", 8], ["queue2", 4]] }
128
+ let(:cli_queues) { { "queue1" => 10, "queue2" => 20 } }
129
+ let(:config_queues) { [["queue1", 8], ["queue2", 4]] }
130
+
100
131
  context "when given queues through config and CLI" do
101
132
  specify do
102
133
  allow_any_instance_of(Shoryuken::EnvironmentLoader).to receive(:config_file_options).and_return({ queues: config_queues })
@@ -104,6 +135,7 @@ RSpec.describe Shoryuken::EnvironmentLoader do
104
135
  expect(Shoryuken.options[:queues]).to eq(cli_queues)
105
136
  end
106
137
  end
138
+
107
139
  context "when given queues through config only" do
108
140
  specify do
109
141
  allow_any_instance_of(Shoryuken::EnvironmentLoader).to receive(:config_file_options).and_return({ queues: config_queues })
@@ -111,6 +143,7 @@ RSpec.describe Shoryuken::EnvironmentLoader do
111
143
  expect(Shoryuken.options[:queues]).to eq(config_queues)
112
144
  end
113
145
  end
146
+
114
147
  context "when given queues through CLI only" do
115
148
  specify do
116
149
  Shoryuken::EnvironmentLoader.setup_options(queues: cli_queues)
@@ -18,7 +18,7 @@ RSpec.describe ActiveJob::Base do
18
18
  end
19
19
 
20
20
  describe '#perform_now' do
21
- it 'allows keyward args' do
21
+ it 'allows keyword args' do
22
22
  collaborator = double 'worker collaborator'
23
23
  subject.send(:define_method, :perform) do |**kwargs|
24
24
  collaborator.foo(**kwargs)
@@ -28,12 +28,12 @@ RSpec.describe Shoryuken::Fetcher do
28
28
 
29
29
  Shoryuken.sqs_client_receive_message_opts[group] = { wait_time_seconds: 10 }
30
30
 
31
- expect(queue).to receive(:receive_messages).with(
31
+ expect(queue).to receive(:receive_messages).with({
32
32
  wait_time_seconds: 10,
33
33
  max_number_of_messages: limit,
34
34
  message_attribute_names: ['All'],
35
35
  attribute_names: ['All']
36
- ).and_return([])
36
+ }).and_return([])
37
37
 
38
38
  subject.fetch(queue_config, limit)
39
39
  end
@@ -62,11 +62,11 @@ RSpec.describe Shoryuken::Fetcher do
62
62
 
63
63
  Shoryuken.sqs_client_receive_message_opts[queue_name] = { max_number_of_messages: 1 }
64
64
 
65
- expect(queue).to receive(:receive_messages).with(
65
+ expect(queue).to receive(:receive_messages).with({
66
66
  max_number_of_messages: 1,
67
67
  message_attribute_names: ['All'],
68
68
  attribute_names: ['All']
69
- ).and_return([])
69
+ }).and_return([])
70
70
 
71
71
  subject.fetch(queue_config, limit)
72
72
  end
@@ -78,11 +78,11 @@ RSpec.describe Shoryuken::Fetcher do
78
78
 
79
79
  Shoryuken.sqs_client_receive_message_opts[queue_name] = { max_number_of_messages: 20 }
80
80
 
81
- expect(queue).to receive(:receive_messages).with(
81
+ expect(queue).to receive(:receive_messages).with({
82
82
  max_number_of_messages: limit,
83
83
  message_attribute_names: ['All'],
84
84
  attribute_names: ['All']
85
- ).and_return([])
85
+ }).and_return([])
86
86
 
87
87
  subject.fetch(queue_config, limit)
88
88
  end
@@ -93,9 +93,9 @@ RSpec.describe Shoryuken::Fetcher do
93
93
 
94
94
  specify do
95
95
  allow(Shoryuken::Client).to receive(:queues).with(queue_name).and_return(queue)
96
- expect(queue).to receive(:receive_messages).with(
96
+ expect(queue).to receive(:receive_messages).with({
97
97
  max_number_of_messages: described_class::FETCH_LIMIT, attribute_names: ['All'], message_attribute_names: ['All']
98
- ).and_return([])
98
+ }).and_return([])
99
99
 
100
100
  subject.fetch(queue_config, limit)
101
101
  end
@@ -109,9 +109,9 @@ RSpec.describe Shoryuken::Fetcher do
109
109
  # see https://github.com/phstc/shoryuken/pull/530
110
110
 
111
111
  allow(Shoryuken::Client).to receive(:queues).with(queue_name).and_return(queue)
112
- expect(queue).to receive(:receive_messages).with(
112
+ expect(queue).to receive(:receive_messages).with({
113
113
  max_number_of_messages: 1, attribute_names: ['All'], message_attribute_names: ['All']
114
- ).and_return([])
114
+ }).and_return([])
115
115
 
116
116
  subject.fetch(queue_config, limit)
117
117
  end
@@ -123,9 +123,9 @@ RSpec.describe Shoryuken::Fetcher do
123
123
  allow(Shoryuken::Client).to receive(:queues).with(queue_name).and_return(queue)
124
124
  allow(Shoryuken.worker_registry).to receive(:batch_receive_messages?).with(queue.name).and_return(true)
125
125
 
126
- expect(queue).to receive(:receive_messages).with(
126
+ expect(queue).to receive(:receive_messages).with({
127
127
  max_number_of_messages: limit, attribute_names: ['All'], message_attribute_names: ['All']
128
- ).and_return([])
128
+ }).and_return([])
129
129
 
130
130
  subject.fetch(queue_config, limit)
131
131
  end
@@ -0,0 +1,105 @@
1
+ require 'spec_helper'
2
+ require 'shoryuken/launcher'
3
+
4
+ RSpec.describe Shoryuken::Launcher do
5
+ let(:executor) do
6
+ # We can't use Concurrent.global_io_executor in these tests since once you
7
+ # shut down a thread pool, you can't start it back up. Instead, we create
8
+ # one new thread pool executor for each spec. We use a new
9
+ # CachedThreadPool, since that most closely resembles
10
+ # Concurrent.global_io_executor
11
+ Concurrent::CachedThreadPool.new auto_terminate: true
12
+ end
13
+
14
+ let(:first_group_manager) { double(:first_group_manager, group: 'first_group') }
15
+ let(:second_group_manager) { double(:second_group_manager, group: 'second_group') }
16
+ let(:first_queue) { "launcher_spec_#{SecureRandom.uuid}" }
17
+ let(:second_queue) { "launcher_spec_#{SecureRandom.uuid}" }
18
+
19
+ before do
20
+ Shoryuken.add_group('first_group', 1)
21
+ Shoryuken.add_group('second_group', 1)
22
+ Shoryuken.add_queue(first_queue, 1, 'first_group')
23
+ Shoryuken.add_queue(second_queue, 1, 'second_group')
24
+ allow(Shoryuken).to receive(:launcher_executor).and_return(executor)
25
+ allow(Shoryuken::Manager).to receive(:new).with('first_group', any_args).and_return(first_group_manager)
26
+ allow(Shoryuken::Manager).to receive(:new).with('second_group', any_args).and_return(second_group_manager)
27
+ allow(first_group_manager).to receive(:running?).and_return(true)
28
+ allow(second_group_manager).to receive(:running?).and_return(true)
29
+ end
30
+
31
+ describe '#healthy?' do
32
+ context 'when all groups have managers' do
33
+ context 'when all managers are running' do
34
+ it 'returns true' do
35
+ expect(subject.healthy?).to be true
36
+ end
37
+ end
38
+
39
+ context 'when one manager is not running' do
40
+ before do
41
+ allow(second_group_manager).to receive(:running?).and_return(false)
42
+ end
43
+
44
+ it 'returns false' do
45
+ expect(subject.healthy?).to be false
46
+ end
47
+ end
48
+ end
49
+
50
+ context 'when all groups do not have managers' do
51
+ before do
52
+ allow(second_group_manager).to receive(:group).and_return('some_random_group')
53
+ end
54
+
55
+ it 'returns false' do
56
+ expect(subject.healthy?).to be false
57
+ end
58
+ end
59
+ end
60
+
61
+ describe '#stop' do
62
+ before do
63
+ allow(first_group_manager).to receive(:stop_new_dispatching)
64
+ allow(first_group_manager).to receive(:await_dispatching_in_progress)
65
+ allow(second_group_manager).to receive(:stop_new_dispatching)
66
+ allow(second_group_manager).to receive(:await_dispatching_in_progress)
67
+ end
68
+
69
+ it 'fires quiet, shutdown and stopped event' do
70
+ allow(subject).to receive(:fire_event)
71
+ subject.stop
72
+ expect(subject).to have_received(:fire_event).with(:quiet, true)
73
+ expect(subject).to have_received(:fire_event).with(:shutdown, true)
74
+ expect(subject).to have_received(:fire_event).with(:stopped)
75
+ end
76
+
77
+ it 'stops the managers' do
78
+ subject.stop
79
+ expect(first_group_manager).to have_received(:stop_new_dispatching)
80
+ expect(second_group_manager).to have_received(:stop_new_dispatching)
81
+ end
82
+ end
83
+
84
+ describe '#stop!' do
85
+ before do
86
+ allow(first_group_manager).to receive(:stop_new_dispatching)
87
+ allow(first_group_manager).to receive(:await_dispatching_in_progress)
88
+ allow(second_group_manager).to receive(:stop_new_dispatching)
89
+ allow(second_group_manager).to receive(:await_dispatching_in_progress)
90
+ end
91
+
92
+ it 'fires shutdown and stopped event' do
93
+ allow(subject).to receive(:fire_event)
94
+ subject.stop!
95
+ expect(subject).to have_received(:fire_event).with(:shutdown, true)
96
+ expect(subject).to have_received(:fire_event).with(:stopped)
97
+ end
98
+
99
+ it 'stops the managers' do
100
+ subject.stop!
101
+ expect(first_group_manager).to have_received(:stop_new_dispatching)
102
+ expect(second_group_manager).to have_received(:stop_new_dispatching)
103
+ end
104
+ end
105
+ end
@@ -73,9 +73,11 @@ RSpec.describe Shoryuken::Manager do
73
73
  expect(subject).to receive(:fire_event).with(:dispatch, false, queue_name: q.name)
74
74
  expect(subject).to receive(:fire_event).with(:utilization_update,
75
75
  false,
76
- group: 'default',
77
- busy_processors: 1,
78
- max_processors: 1)
76
+ {
77
+ group: 'default',
78
+ busy_processors: 1,
79
+ max_processors: 1
80
+ })
79
81
  expect(Shoryuken::Processor).to receive(:process).with(q, message)
80
82
  expect(Shoryuken.logger).to receive(:info).never
81
83
 
@@ -106,9 +108,11 @@ RSpec.describe Shoryuken::Manager do
106
108
  expect(fetcher).to receive(:fetch).with(q, described_class::BATCH_LIMIT).and_return(messages)
107
109
  expect(subject).to receive(:fire_event).with(:utilization_update,
108
110
  false,
109
- group: 'default',
110
- busy_processors: 1,
111
- max_processors: 1)
111
+ {
112
+ group: 'default',
113
+ busy_processors: 1,
114
+ max_processors: 1
115
+ })
112
116
  expect(subject).to receive(:fire_event).with(:dispatch, false, queue_name: q.name)
113
117
  allow(subject).to receive(:batched_queue?).with(q).and_return(true)
114
118
  expect(Shoryuken::Processor).to receive(:process).with(q, messages)
@@ -173,4 +177,26 @@ RSpec.describe Shoryuken::Manager do
173
177
  end
174
178
  end
175
179
  end
180
+
181
+ describe '#running?' do
182
+ context 'when the executor is running' do
183
+ before do
184
+ allow(executor).to receive(:running?).and_return(true)
185
+ end
186
+
187
+ it 'returns true' do
188
+ expect(subject.running?).to be true
189
+ end
190
+ end
191
+
192
+ context 'when the executor is not running' do
193
+ before do
194
+ allow(executor).to receive(:running?).and_return(false)
195
+ end
196
+
197
+ it 'returns false' do
198
+ expect(subject.running?).to be false
199
+ end
200
+ end
201
+ end
176
202
  end
@@ -106,12 +106,37 @@ RSpec.describe Shoryuken::Polling::WeightedRoundRobin do
106
106
  end
107
107
 
108
108
  describe '#message_processed' do
109
- it 'removes paused queue, adds to active queues' do
110
- strategy = Shoryuken::Polling::WeightedRoundRobin.new([queue1, queue2])
111
- strategy.send(:pause, queue1)
112
- expect(strategy.active_queues).to eq([[queue2, 1]])
113
- strategy.message_processed(queue1)
114
- expect(strategy.active_queues).to eq([[queue2, 1], [queue1, 1]])
109
+ it 'removes delay from paused queue' do
110
+ queues << queue1
111
+ queues << queue2
112
+
113
+ expect(subject.next_queue).to eq(queue1)
114
+ subject.messages_found(queue1, 0) # pauses queue1
115
+
116
+ expect(subject.active_queues).to eq([[queue2, 1]])
117
+
118
+ subject.message_processed(queue1) # marks queue1 to be unpaused
119
+
120
+ expect(subject.next_queue).to eq(queue2) # implicitly unpauses queue1
121
+ expect(subject.active_queues).to eq([[queue1, 1], [queue2, 1]])
122
+ end
123
+
124
+ it 'preserves weight of queues when unpausing' do
125
+ queues << queue1
126
+ queues << queue1
127
+ queues << queue2
128
+
129
+ expect(subject.next_queue).to eq(queue1)
130
+ subject.messages_found(queue1, 1)
131
+
132
+ expect(subject.next_queue).to eq(queue2)
133
+ subject.messages_found(queue2, 0) # pauses queue2
134
+
135
+ expect(subject.active_queues).to eq([[queue1, 2]])
136
+ subject.message_processed(queue2) # marks queue2 to be unpaused
137
+
138
+ expect(subject.next_queue).to eq(queue1) # implicitly unpauses queue2
139
+ expect(subject.active_queues).to eq([[queue1, 2], [queue2, 1]])
115
140
  end
116
141
  end
117
142
  end
@@ -159,5 +159,43 @@ RSpec.describe Shoryuken::Processor do
159
159
  subject.process
160
160
  end
161
161
  end
162
+
163
+ context 'when specifying a reloader' do
164
+ before do
165
+ Shoryuken.reloader = proc do |_|
166
+ TestWorker.new.called
167
+ end
168
+ end
169
+
170
+ after do
171
+ Shoryuken.reloader = proc { |&block| block.call }
172
+ end
173
+
174
+ context 'when reloader is enabled' do
175
+ before do
176
+ Shoryuken.enable_reloading = true
177
+ end
178
+
179
+ it 'wraps execution in reloader' do
180
+ expect_any_instance_of(TestWorker).to receive(:called)
181
+ expect_any_instance_of(TestWorker).to_not receive(:perform)
182
+
183
+ subject.process
184
+ end
185
+
186
+ after do
187
+ Shoryuken.enable_reloading = false
188
+ end
189
+ end
190
+
191
+ context 'when reloader is disabled' do
192
+ it 'does not wrap execution in reloader' do
193
+ expect_any_instance_of(TestWorker).to_not receive(:called)
194
+ expect_any_instance_of(TestWorker).to receive(:perform).with(sqs_msg, sqs_msg.body)
195
+
196
+ subject.process
197
+ end
198
+ end
199
+ end
162
200
  end
163
201
  end
@@ -80,7 +80,8 @@ RSpec.describe Shoryuken::Queue do
80
80
  end
81
81
 
82
82
  it 'deletes' do
83
- expect(sqs).to receive(:delete_message_batch).with(entries: entries, queue_url: queue_url).and_return(double(failed: []))
83
+ expect(sqs).to receive(:delete_message_batch).with({ entries: entries,
84
+ queue_url: queue_url }).and_return(double(failed: []))
84
85
 
85
86
  subject.delete_messages(entries: entries)
86
87
  end
@@ -91,7 +92,8 @@ RSpec.describe Shoryuken::Queue do
91
92
  logger = double 'Logger'
92
93
 
93
94
  expect(sqs).to(
94
- receive(:delete_message_batch).with(entries: entries, queue_url: queue_url).and_return(double(failed: [failure]))
95
+ receive(:delete_message_batch).with({ entries: entries,
96
+ queue_url: queue_url }).and_return(double(failed: [failure]))
95
97
  )
96
98
  expect(subject).to receive(:logger).and_return(logger)
97
99
  expect(logger).to receive(:error)
@@ -157,7 +159,8 @@ RSpec.describe Shoryuken::Queue do
157
159
  it 'accepts SQS request parameters' do
158
160
  # https://docs.aws.amazon.com/sdkforruby/api/Aws/SQS/Client.html#send_message_batch-instance_method
159
161
  expect(sqs).to(
160
- receive(:send_message_batch).with(hash_including(entries: [{ id: '0', message_body: 'msg1' }, { id: '1', message_body: 'msg2' }]))
162
+ receive(:send_message_batch).with(hash_including(entries: [{ id: '0', message_body: 'msg1' },
163
+ { id: '1', message_body: 'msg2' }]))
161
164
  )
162
165
 
163
166
  subject.send_messages(entries: [{ id: '0', message_body: 'msg1' }, { id: '1', message_body: 'msg2' }])
@@ -286,7 +289,8 @@ RSpec.describe Shoryuken::Queue do
286
289
  Shoryuken.cache_visibility_timeout = false
287
290
 
288
291
  expect(sqs).to(
289
- receive(:get_queue_attributes).with(queue_url: queue_url, attribute_names: ['All']).and_return(attribute_response).exactly(3).times
292
+ receive(:get_queue_attributes).with(queue_url: queue_url,
293
+ attribute_names: ['All']).and_return(attribute_response).exactly(3).times
290
294
  )
291
295
  expect(subject.visibility_timeout).to eq(30)
292
296
  expect(subject.visibility_timeout).to eq(30)
@@ -299,7 +303,8 @@ RSpec.describe Shoryuken::Queue do
299
303
  Shoryuken.cache_visibility_timeout = true
300
304
 
301
305
  expect(sqs).to(
302
- receive(:get_queue_attributes).with(queue_url: queue_url, attribute_names: ['All']).and_return(attribute_response).once
306
+ receive(:get_queue_attributes).with({ queue_url: queue_url,
307
+ attribute_names: ['All'] }).and_return(attribute_response).once
303
308
  )
304
309
  expect(subject.visibility_timeout).to eq(30)
305
310
  expect(subject.visibility_timeout).to eq(30)
@@ -17,14 +17,34 @@ describe 'Shoryuken::Util' do
17
17
 
18
18
  describe '#worker_name' do
19
19
  let(:sqs_msg) do
20
- double Shoryuken::Message, message_id: 'fc754df7-9cc2-4c41-96ca-5996a44b771e', message_attributes: {}
20
+ double Shoryuken::Message, message_id: 'fc754df7-9cc2-4c41-96ca-5996a44b771e', message_attributes: message_attributes
21
21
  end
22
22
 
23
- it 'returns Shoryuken worker name' do
24
- expect(subject.worker_name(TestWorker, sqs_msg)).to eq 'TestWorker'
23
+ context 'when has integration with ActiveJob' do
24
+ before do
25
+ allow(Shoryuken).to receive(:active_job?).and_return(true)
26
+ end
27
+
28
+ let(:message_attributes) do
29
+ { 'shoryuken_class' => { string_value: ActiveJob::QueueAdapters::ShoryukenAdapter::JobWrapper.to_s } }
30
+ end
31
+
32
+ let(:body) do
33
+ { 'job_class' => 'TestJob' }
34
+ end
35
+
36
+ it 'returns ActiveJob worker name' do
37
+ expect(subject.worker_name(TestWorker, sqs_msg, body)).to eq 'ActiveJob/TestJob'
38
+ end
25
39
  end
26
40
 
27
- it 'returns ActiveJob worker name'
41
+ context 'when has not integration with ActiveJob' do
42
+ let(:message_attributes) { {} }
43
+
44
+ it 'returns Shoryuken worker name' do
45
+ expect(subject.worker_name(TestWorker, sqs_msg)).to eq 'TestWorker'
46
+ end
47
+ end
28
48
  end
29
49
 
30
50
  describe '#fire_event' do