shoryuken 7.0.0.alpha2 → 7.0.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.
- checksums.yaml +4 -4
- data/.github/workflows/push.yml +2 -2
- data/.github/workflows/specs.yml +38 -43
- data/.github/workflows/verify-action-pins.yml +1 -1
- data/.gitignore +3 -0
- data/.rspec +1 -0
- data/.ruby-version +1 -1
- data/.yard-lint.yml +279 -0
- data/CHANGELOG.md +69 -1
- data/Gemfile +1 -1
- data/README.md +2 -7
- data/Rakefile +4 -10
- data/bin/clean_localstack +52 -0
- data/bin/cli/base.rb +21 -0
- data/bin/cli/sqs.rb +61 -2
- data/bin/integrations +275 -0
- data/bin/scenario +154 -0
- data/bin/shoryuken +1 -1
- data/lib/{shoryuken/extensions/active_job_extensions.rb → active_job/extensions.rb} +15 -4
- data/lib/active_job/queue_adapters/shoryuken_adapter.rb +208 -0
- data/lib/active_job/queue_adapters/shoryuken_concurrent_send_adapter.rb +78 -0
- data/lib/shoryuken/active_job/current_attributes.rb +139 -0
- data/lib/shoryuken/active_job/job_wrapper.rb +28 -0
- data/lib/shoryuken/body_parser.rb +8 -0
- data/lib/shoryuken/client.rb +14 -0
- data/lib/shoryuken/default_exception_handler.rb +9 -0
- data/lib/shoryuken/default_worker_registry.rb +29 -1
- data/lib/shoryuken/environment_loader.rb +78 -8
- data/lib/shoryuken/errors.rb +33 -0
- data/lib/shoryuken/fetcher.rb +37 -1
- data/lib/shoryuken/helpers/atomic_boolean.rb +19 -5
- data/lib/shoryuken/helpers/timer_task.rb +80 -0
- data/lib/shoryuken/launcher.rb +53 -0
- data/lib/shoryuken/logging/base.rb +26 -0
- data/lib/shoryuken/logging/pretty.rb +25 -0
- data/lib/shoryuken/logging/without_timestamp.rb +25 -0
- data/lib/shoryuken/logging.rb +39 -25
- data/lib/shoryuken/manager.rb +70 -1
- data/lib/shoryuken/message.rb +114 -1
- data/lib/shoryuken/middleware/chain.rb +139 -43
- data/lib/shoryuken/middleware/entry.rb +30 -0
- data/lib/shoryuken/middleware/server/active_record.rb +8 -0
- data/lib/shoryuken/middleware/server/auto_delete.rb +10 -0
- data/lib/shoryuken/middleware/server/auto_extend_visibility.rb +27 -1
- data/lib/shoryuken/middleware/server/exponential_backoff_retry.rb +29 -0
- data/lib/shoryuken/middleware/server/timing.rb +11 -0
- data/lib/shoryuken/options.rb +129 -6
- data/lib/shoryuken/polling/base_strategy.rb +1 -0
- data/lib/shoryuken/polling/strict_priority.rb +39 -0
- data/lib/shoryuken/polling/weighted_round_robin.rb +42 -0
- data/lib/shoryuken/processor.rb +32 -1
- data/lib/shoryuken/queue.rb +93 -4
- data/lib/shoryuken/runner.rb +45 -4
- data/lib/shoryuken/util.rb +26 -1
- data/lib/shoryuken/version.rb +2 -1
- data/lib/shoryuken/worker/default_executor.rb +21 -1
- data/lib/shoryuken/worker/inline_executor.rb +24 -0
- data/lib/shoryuken/worker.rb +193 -0
- data/lib/shoryuken/worker_registry.rb +33 -0
- data/lib/shoryuken.rb +18 -6
- data/renovate.json +29 -2
- data/shoryuken.gemspec +2 -1
- data/spec/integration/.rspec +1 -0
- data/spec/integration/active_job/adapter_configuration/configuration_spec.rb +26 -0
- data/spec/integration/active_job/bulk_enqueue/bulk_enqueue_spec.rb +53 -0
- data/spec/integration/active_job/current_attributes/bulk_enqueue_spec.rb +50 -0
- data/spec/integration/active_job/current_attributes/complex_types_spec.rb +55 -0
- data/spec/integration/active_job/current_attributes/empty_context_spec.rb +41 -0
- data/spec/integration/active_job/current_attributes/full_context_spec.rb +63 -0
- data/spec/integration/active_job/current_attributes/partial_context_spec.rb +57 -0
- data/spec/integration/active_job/custom_attributes/number_attributes_spec.rb +37 -0
- data/spec/integration/active_job/custom_attributes/string_attributes_spec.rb +39 -0
- data/spec/integration/active_job/error_handling/job_wrapper_spec.rb +53 -0
- data/spec/integration/active_job/fifo_and_attributes/deduplication_spec.rb +86 -0
- data/spec/integration/active_job/retry/discard_on_spec.rb +43 -0
- data/spec/integration/active_job/retry/retry_on_spec.rb +36 -0
- data/spec/integration/active_job/roundtrip/roundtrip_spec.rb +52 -0
- data/spec/integration/active_job/scheduled/scheduled_spec.rb +76 -0
- data/spec/integration/active_record_middleware/active_record_middleware_spec.rb +84 -0
- data/spec/integration/auto_delete/auto_delete_spec.rb +53 -0
- data/spec/integration/auto_extend_visibility/auto_extend_visibility_spec.rb +57 -0
- data/spec/integration/aws_config/aws_config_spec.rb +59 -0
- data/spec/integration/batch_processing/batch_processing_spec.rb +37 -0
- data/spec/integration/body_parser/json_parser_spec.rb +45 -0
- data/spec/integration/body_parser/proc_parser_spec.rb +54 -0
- data/spec/integration/body_parser/text_parser_spec.rb +43 -0
- data/spec/integration/concurrent_processing/concurrent_processing_spec.rb +45 -0
- data/spec/integration/dead_letter_queue/dead_letter_queue_spec.rb +91 -0
- data/spec/integration/exception_handlers/exception_handlers_spec.rb +69 -0
- data/spec/integration/exponential_backoff/exponential_backoff_spec.rb +67 -0
- data/spec/integration/fifo_ordering/fifo_ordering_spec.rb +44 -0
- data/spec/integration/large_payloads/large_payloads_spec.rb +30 -0
- data/spec/integration/launcher/launcher_spec.rb +40 -0
- data/spec/integration/message_attributes/message_attributes_spec.rb +54 -0
- data/spec/integration/message_operations/message_operations_spec.rb +59 -0
- data/spec/integration/middleware_chain/empty_chain_spec.rb +11 -0
- data/spec/integration/middleware_chain/execution_order_spec.rb +33 -0
- data/spec/integration/middleware_chain/removal_spec.rb +31 -0
- data/spec/integration/middleware_chain/short_circuit_spec.rb +40 -0
- data/spec/integration/polling_strategies/polling_strategies_spec.rb +46 -0
- data/spec/integration/queue_operations/queue_operations_spec.rb +84 -0
- data/spec/integration/rails/rails_72/Gemfile +6 -0
- data/spec/integration/rails/rails_72/activejob_adapter_spec.rb +98 -0
- data/spec/integration/rails/rails_80/Gemfile +6 -0
- data/spec/integration/rails/rails_80/activejob_adapter_spec.rb +98 -0
- data/spec/integration/rails/rails_80/continuation_spec.rb +79 -0
- data/spec/integration/rails/rails_81/Gemfile +6 -0
- data/spec/integration/rails/rails_81/activejob_adapter_spec.rb +98 -0
- data/spec/integration/rails/rails_81/continuation_spec.rb +79 -0
- data/spec/integration/retry_behavior/retry_behavior_spec.rb +45 -0
- data/spec/integration/spec_helper.rb +7 -0
- data/spec/integration/strict_priority_polling/strict_priority_polling_spec.rb +58 -0
- data/spec/integration/visibility_timeout/visibility_timeout_spec.rb +37 -0
- data/spec/integration/worker_enqueueing/worker_enqueueing_spec.rb +60 -0
- data/spec/integration/worker_groups/worker_groups_spec.rb +79 -0
- data/spec/integration/worker_lifecycle/worker_lifecycle_spec.rb +33 -0
- data/spec/integrations_helper.rb +243 -0
- data/spec/lib/active_job/extensions_spec.rb +149 -0
- data/spec/lib/active_job/queue_adapters/shoryuken_adapter_spec.rb +29 -0
- data/spec/{shoryuken/extensions/active_job_concurrent_send_adapter_spec.rb → lib/active_job/queue_adapters/shoryuken_concurrent_send_adapter_spec.rb} +3 -3
- data/spec/{shoryuken/extensions/active_job_wrapper_spec.rb → lib/shoryuken/active_job/job_wrapper_spec.rb} +4 -4
- data/spec/{shoryuken → lib/shoryuken}/environment_loader_spec.rb +1 -1
- data/spec/{shoryuken → lib/shoryuken}/helpers/hash_utils_spec.rb +14 -14
- data/spec/{shoryuken → lib/shoryuken}/helpers/string_utils_spec.rb +3 -3
- data/spec/lib/shoryuken/helpers/timer_task_spec.rb +298 -0
- data/spec/{shoryuken → lib/shoryuken}/helpers_integration_spec.rb +9 -9
- data/spec/{shoryuken → lib/shoryuken}/launcher_spec.rb +22 -0
- data/spec/lib/shoryuken/logging_spec.rb +242 -0
- data/spec/lib/shoryuken/message_spec.rb +109 -0
- data/spec/lib/shoryuken/middleware/entry_spec.rb +68 -0
- data/spec/lib/shoryuken/middleware/server/active_record_spec.rb +133 -0
- data/spec/{shoryuken → lib/shoryuken}/middleware/server/auto_extend_visibility_spec.rb +50 -0
- data/spec/{shoryuken → lib/shoryuken}/options_spec.rb +2 -2
- data/spec/{shoryuken → lib/shoryuken}/util_spec.rb +1 -1
- data/spec/lib/shoryuken/version_spec.rb +17 -0
- data/spec/lib/shoryuken/worker_registry_spec.rb +63 -0
- data/spec/shared_examples_for_active_job.rb +29 -9
- data/spec/spec_helper.rb +34 -3
- metadata +230 -91
- data/.devcontainer/Dockerfile +0 -17
- data/.devcontainer/base.Dockerfile +0 -43
- data/.devcontainer/devcontainer.json +0 -35
- data/Appraisals +0 -23
- data/gemfiles/.gitignore +0 -1
- data/gemfiles/rails_7_0.gemfile +0 -19
- data/gemfiles/rails_7_1.gemfile +0 -19
- data/gemfiles/rails_7_2.gemfile +0 -19
- data/gemfiles/rails_8_0.gemfile +0 -19
- data/lib/shoryuken/extensions/active_job_adapter.rb +0 -110
- data/lib/shoryuken/extensions/active_job_concurrent_send_adapter.rb +0 -50
- data/spec/integration/launcher_spec.rb +0 -127
- data/spec/shoryuken/extensions/active_job_adapter_spec.rb +0 -8
- data/spec/shoryuken/extensions/active_job_base_spec.rb +0 -85
- /data/spec/{shoryuken → lib/shoryuken}/body_parser_spec.rb +0 -0
- /data/spec/{shoryuken → lib/shoryuken}/client_spec.rb +0 -0
- /data/spec/{shoryuken → lib/shoryuken}/default_exception_handler_spec.rb +0 -0
- /data/spec/{shoryuken → lib/shoryuken}/default_worker_registry_spec.rb +0 -0
- /data/spec/{shoryuken → lib/shoryuken}/fetcher_spec.rb +0 -0
- /data/spec/{shoryuken → lib/shoryuken}/helpers/atomic_boolean_spec.rb +0 -0
- /data/spec/{shoryuken → lib/shoryuken}/helpers/atomic_counter_spec.rb +0 -0
- /data/spec/{shoryuken → lib/shoryuken}/helpers/atomic_hash_spec.rb +0 -0
- /data/spec/{shoryuken → lib/shoryuken}/inline_message_spec.rb +0 -0
- /data/spec/{shoryuken → lib/shoryuken}/manager_spec.rb +0 -0
- /data/spec/{shoryuken → lib/shoryuken}/middleware/chain_spec.rb +0 -0
- /data/spec/{shoryuken → lib/shoryuken}/middleware/server/auto_delete_spec.rb +0 -0
- /data/spec/{shoryuken → lib/shoryuken}/middleware/server/exponential_backoff_retry_spec.rb +0 -0
- /data/spec/{shoryuken → lib/shoryuken}/middleware/server/timing_spec.rb +0 -0
- /data/spec/{shoryuken → lib/shoryuken}/polling/base_strategy_spec.rb +0 -0
- /data/spec/{shoryuken → lib/shoryuken}/polling/queue_configuration_spec.rb +0 -0
- /data/spec/{shoryuken → lib/shoryuken}/polling/strict_priority_spec.rb +0 -0
- /data/spec/{shoryuken → lib/shoryuken}/polling/weighted_round_robin_spec.rb +0 -0
- /data/spec/{shoryuken → lib/shoryuken}/processor_spec.rb +0 -0
- /data/spec/{shoryuken → lib/shoryuken}/queue_spec.rb +0 -0
- /data/spec/{shoryuken → lib/shoryuken}/runner_spec.rb +0 -0
- /data/spec/{shoryuken → lib/shoryuken}/worker/default_executor_spec.rb +0 -0
- /data/spec/{shoryuken → lib/shoryuken}/worker/inline_executor_spec.rb +0 -0
- /data/spec/{shoryuken → lib/shoryuken}/worker_spec.rb +0 -0
- /data/spec/{shoryuken_spec.rb → lib/shoryuken_spec.rb} +0 -0
|
@@ -0,0 +1,109 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require 'spec_helper'
|
|
4
|
+
|
|
5
|
+
RSpec.describe Shoryuken::Message do
|
|
6
|
+
let(:client) { instance_double('Aws::SQS::Client') }
|
|
7
|
+
let(:queue) { instance_double('Shoryuken::Queue', name: 'test-queue', url: 'https://sqs.us-east-1.amazonaws.com/123456789/test-queue') }
|
|
8
|
+
let(:data) do
|
|
9
|
+
instance_double('Aws::SQS::Types::Message',
|
|
10
|
+
message_id: 'msg-123',
|
|
11
|
+
receipt_handle: 'handle-456',
|
|
12
|
+
md5_of_body: 'abcd1234',
|
|
13
|
+
body: '{"test": "data"}',
|
|
14
|
+
attributes: { 'ApproximateReceiveCount' => '1' },
|
|
15
|
+
md5_of_message_attributes: 'efgh5678',
|
|
16
|
+
message_attributes: { 'type' => 'test' })
|
|
17
|
+
end
|
|
18
|
+
|
|
19
|
+
subject { described_class.new(client, queue, data) }
|
|
20
|
+
|
|
21
|
+
describe '#initialize' do
|
|
22
|
+
it 'sets client, queue_url, queue_name, and data' do
|
|
23
|
+
expect(subject.client).to eq(client)
|
|
24
|
+
expect(subject.queue_url).to eq('https://sqs.us-east-1.amazonaws.com/123456789/test-queue')
|
|
25
|
+
expect(subject.queue_name).to eq('test-queue')
|
|
26
|
+
expect(subject.data).to eq(data)
|
|
27
|
+
end
|
|
28
|
+
end
|
|
29
|
+
|
|
30
|
+
describe 'delegated methods' do
|
|
31
|
+
it 'delegates message_id to data' do
|
|
32
|
+
expect(subject.message_id).to eq('msg-123')
|
|
33
|
+
end
|
|
34
|
+
|
|
35
|
+
it 'delegates receipt_handle to data' do
|
|
36
|
+
expect(subject.receipt_handle).to eq('handle-456')
|
|
37
|
+
end
|
|
38
|
+
|
|
39
|
+
it 'delegates md5_of_body to data' do
|
|
40
|
+
expect(subject.md5_of_body).to eq('abcd1234')
|
|
41
|
+
end
|
|
42
|
+
|
|
43
|
+
it 'delegates body to data' do
|
|
44
|
+
expect(subject.body).to eq('{"test": "data"}')
|
|
45
|
+
end
|
|
46
|
+
|
|
47
|
+
it 'delegates attributes to data' do
|
|
48
|
+
expect(subject.attributes).to eq({ 'ApproximateReceiveCount' => '1' })
|
|
49
|
+
end
|
|
50
|
+
|
|
51
|
+
it 'delegates md5_of_message_attributes to data' do
|
|
52
|
+
expect(subject.md5_of_message_attributes).to eq('efgh5678')
|
|
53
|
+
end
|
|
54
|
+
|
|
55
|
+
it 'delegates message_attributes to data' do
|
|
56
|
+
expect(subject.message_attributes).to eq({ 'type' => 'test' })
|
|
57
|
+
end
|
|
58
|
+
end
|
|
59
|
+
|
|
60
|
+
describe '#delete' do
|
|
61
|
+
it 'calls delete_message on the client with correct parameters' do
|
|
62
|
+
expect(client).to receive(:delete_message).with(
|
|
63
|
+
queue_url: 'https://sqs.us-east-1.amazonaws.com/123456789/test-queue',
|
|
64
|
+
receipt_handle: 'handle-456'
|
|
65
|
+
)
|
|
66
|
+
|
|
67
|
+
subject.delete
|
|
68
|
+
end
|
|
69
|
+
end
|
|
70
|
+
|
|
71
|
+
describe '#change_visibility' do
|
|
72
|
+
it 'calls change_message_visibility on the client with merged parameters' do
|
|
73
|
+
options = { visibility_timeout: 300 }
|
|
74
|
+
|
|
75
|
+
expect(client).to receive(:change_message_visibility).with(hash_including(
|
|
76
|
+
visibility_timeout: 300,
|
|
77
|
+
queue_url: 'https://sqs.us-east-1.amazonaws.com/123456789/test-queue',
|
|
78
|
+
receipt_handle: 'handle-456'
|
|
79
|
+
))
|
|
80
|
+
|
|
81
|
+
subject.change_visibility(options)
|
|
82
|
+
end
|
|
83
|
+
|
|
84
|
+
it 'merges queue_url and receipt_handle into provided options' do
|
|
85
|
+
options = { visibility_timeout: 120, custom_param: 'value' }
|
|
86
|
+
|
|
87
|
+
expect(client).to receive(:change_message_visibility).with(hash_including(
|
|
88
|
+
visibility_timeout: 120,
|
|
89
|
+
custom_param: 'value',
|
|
90
|
+
queue_url: 'https://sqs.us-east-1.amazonaws.com/123456789/test-queue',
|
|
91
|
+
receipt_handle: 'handle-456'
|
|
92
|
+
))
|
|
93
|
+
|
|
94
|
+
subject.change_visibility(options)
|
|
95
|
+
end
|
|
96
|
+
end
|
|
97
|
+
|
|
98
|
+
describe '#visibility_timeout=' do
|
|
99
|
+
it 'calls change_message_visibility on the client with the timeout' do
|
|
100
|
+
expect(client).to receive(:change_message_visibility).with(
|
|
101
|
+
queue_url: 'https://sqs.us-east-1.amazonaws.com/123456789/test-queue',
|
|
102
|
+
receipt_handle: 'handle-456',
|
|
103
|
+
visibility_timeout: 600
|
|
104
|
+
)
|
|
105
|
+
|
|
106
|
+
subject.visibility_timeout = 600
|
|
107
|
+
end
|
|
108
|
+
end
|
|
109
|
+
end
|
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require 'shoryuken/middleware/entry'
|
|
4
|
+
|
|
5
|
+
RSpec.describe Shoryuken::Middleware::Entry do
|
|
6
|
+
describe '#initialize' do
|
|
7
|
+
it 'stores the middleware class' do
|
|
8
|
+
entry = described_class.new(String)
|
|
9
|
+
expect(entry.klass).to eq String
|
|
10
|
+
end
|
|
11
|
+
|
|
12
|
+
it 'stores initialization arguments' do
|
|
13
|
+
entry = described_class.new(String, 'arg1', 'arg2')
|
|
14
|
+
expect(entry.instance_variable_get(:@args)).to eq ['arg1', 'arg2']
|
|
15
|
+
end
|
|
16
|
+
end
|
|
17
|
+
|
|
18
|
+
describe '#make_new' do
|
|
19
|
+
let(:test_class) do
|
|
20
|
+
Class.new do
|
|
21
|
+
attr_reader :args
|
|
22
|
+
|
|
23
|
+
def initialize(*args)
|
|
24
|
+
@args = args
|
|
25
|
+
end
|
|
26
|
+
end
|
|
27
|
+
end
|
|
28
|
+
|
|
29
|
+
it 'creates a new instance of the stored class without arguments' do
|
|
30
|
+
entry = described_class.new(test_class)
|
|
31
|
+
instance = entry.make_new
|
|
32
|
+
|
|
33
|
+
expect(instance).to be_a test_class
|
|
34
|
+
expect(instance.args).to eq []
|
|
35
|
+
end
|
|
36
|
+
|
|
37
|
+
it 'creates a new instance with stored arguments' do
|
|
38
|
+
entry = described_class.new(test_class, 'arg1', 42, { key: 'value' })
|
|
39
|
+
instance = entry.make_new
|
|
40
|
+
|
|
41
|
+
expect(instance).to be_a test_class
|
|
42
|
+
expect(instance.args).to eq ['arg1', 42, { key: 'value' }]
|
|
43
|
+
end
|
|
44
|
+
|
|
45
|
+
it 'creates a new instance each time it is called' do
|
|
46
|
+
entry = described_class.new(test_class, 'shared_arg')
|
|
47
|
+
instance1 = entry.make_new
|
|
48
|
+
instance2 = entry.make_new
|
|
49
|
+
|
|
50
|
+
expect(instance1).to be_a test_class
|
|
51
|
+
expect(instance2).to be_a test_class
|
|
52
|
+
expect(instance1).not_to be instance2
|
|
53
|
+
expect(instance1.args).to eq instance2.args
|
|
54
|
+
end
|
|
55
|
+
end
|
|
56
|
+
|
|
57
|
+
describe '#klass' do
|
|
58
|
+
it 'returns the stored class' do
|
|
59
|
+
entry = described_class.new(Array)
|
|
60
|
+
expect(entry.klass).to eq Array
|
|
61
|
+
end
|
|
62
|
+
|
|
63
|
+
it 'is readable' do
|
|
64
|
+
entry = described_class.new(Hash)
|
|
65
|
+
expect(entry.klass).to eq Hash
|
|
66
|
+
end
|
|
67
|
+
end
|
|
68
|
+
end
|
|
@@ -0,0 +1,133 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require 'spec_helper'
|
|
4
|
+
|
|
5
|
+
RSpec.describe Shoryuken::Middleware::Server::ActiveRecord do
|
|
6
|
+
subject { described_class.new }
|
|
7
|
+
|
|
8
|
+
# Mock ActiveRecord to avoid requiring the actual gem in tests
|
|
9
|
+
before do
|
|
10
|
+
# Create mock ActiveRecord module
|
|
11
|
+
active_record_module = Module.new
|
|
12
|
+
|
|
13
|
+
# Create mock Base class with simplified methods
|
|
14
|
+
active_record_base = Class.new do
|
|
15
|
+
@connection_handler = nil
|
|
16
|
+
|
|
17
|
+
def self.clear_active_connections!
|
|
18
|
+
# Mock implementation for Rails < 7.1
|
|
19
|
+
end
|
|
20
|
+
|
|
21
|
+
def self.connection_handler
|
|
22
|
+
@connection_handler ||= Object.new.tap do |handler|
|
|
23
|
+
def handler.clear_active_connections!(_pool_key)
|
|
24
|
+
# Mock implementation for Rails 7.1+
|
|
25
|
+
end
|
|
26
|
+
end
|
|
27
|
+
end
|
|
28
|
+
end
|
|
29
|
+
|
|
30
|
+
active_record_module.const_set('Base', active_record_base)
|
|
31
|
+
stub_const('ActiveRecord', active_record_module)
|
|
32
|
+
|
|
33
|
+
# Mock version checking - start with a simple approach
|
|
34
|
+
def active_record_module.version
|
|
35
|
+
@version ||= Object.new.tap do |v|
|
|
36
|
+
def v.>=(other)
|
|
37
|
+
# For our tests, we'll control this with instance variables
|
|
38
|
+
@is_rails_71_or_higher ||= false
|
|
39
|
+
end
|
|
40
|
+
|
|
41
|
+
def v.rails_71_or_higher!
|
|
42
|
+
@is_rails_71_or_higher = true
|
|
43
|
+
end
|
|
44
|
+
|
|
45
|
+
def v.rails_70!
|
|
46
|
+
@is_rails_71_or_higher = false
|
|
47
|
+
end
|
|
48
|
+
end
|
|
49
|
+
end
|
|
50
|
+
|
|
51
|
+
# Mock Gem::Version
|
|
52
|
+
unless defined?(Gem::Version)
|
|
53
|
+
gem_module = Module.new
|
|
54
|
+
gem_version_class = Class.new do
|
|
55
|
+
def initialize(_version)
|
|
56
|
+
# Simple mock
|
|
57
|
+
end
|
|
58
|
+
end
|
|
59
|
+
gem_module.const_set('Version', gem_version_class)
|
|
60
|
+
stub_const('Gem', gem_module)
|
|
61
|
+
end
|
|
62
|
+
end
|
|
63
|
+
|
|
64
|
+
describe '#call' do
|
|
65
|
+
it 'yields to the block' do
|
|
66
|
+
block_called = false
|
|
67
|
+
subject.call do
|
|
68
|
+
block_called = true
|
|
69
|
+
end
|
|
70
|
+
expect(block_called).to be true
|
|
71
|
+
end
|
|
72
|
+
|
|
73
|
+
it 'returns the value from the block' do
|
|
74
|
+
result = subject.call { 'block_result' }
|
|
75
|
+
expect(result).to eq('block_result')
|
|
76
|
+
end
|
|
77
|
+
|
|
78
|
+
context 'when ActiveRecord version is 7.1 or higher' do
|
|
79
|
+
before do
|
|
80
|
+
# Mock Rails 7.1+ behavior
|
|
81
|
+
allow(ActiveRecord).to receive(:version).and_return(double('>=' => true))
|
|
82
|
+
end
|
|
83
|
+
|
|
84
|
+
it 'calls clear_active_connections! on connection_handler with :all parameter' do
|
|
85
|
+
connection_handler = ActiveRecord::Base.connection_handler
|
|
86
|
+
expect(connection_handler).to receive(:clear_active_connections!).with(:all)
|
|
87
|
+
|
|
88
|
+
subject.call { 'test' }
|
|
89
|
+
end
|
|
90
|
+
|
|
91
|
+
it 'clears connections even when an exception is raised' do
|
|
92
|
+
connection_handler = ActiveRecord::Base.connection_handler
|
|
93
|
+
expect(connection_handler).to receive(:clear_active_connections!).with(:all)
|
|
94
|
+
|
|
95
|
+
expect do
|
|
96
|
+
subject.call { raise StandardError, 'test error' }
|
|
97
|
+
end.to raise_error(StandardError, 'test error')
|
|
98
|
+
end
|
|
99
|
+
end
|
|
100
|
+
|
|
101
|
+
context 'when ActiveRecord version is lower than 7.1' do
|
|
102
|
+
before do
|
|
103
|
+
# Mock Rails < 7.1 behavior
|
|
104
|
+
allow(ActiveRecord).to receive(:version).and_return(double('>=' => false))
|
|
105
|
+
end
|
|
106
|
+
|
|
107
|
+
it 'calls clear_active_connections! directly on ActiveRecord::Base' do
|
|
108
|
+
expect(ActiveRecord::Base).to receive(:clear_active_connections!)
|
|
109
|
+
|
|
110
|
+
subject.call { 'test' }
|
|
111
|
+
end
|
|
112
|
+
|
|
113
|
+
it 'clears connections even when an exception is raised' do
|
|
114
|
+
expect(ActiveRecord::Base).to receive(:clear_active_connections!)
|
|
115
|
+
|
|
116
|
+
expect do
|
|
117
|
+
subject.call { raise StandardError, 'test error' }
|
|
118
|
+
end.to raise_error(StandardError, 'test error')
|
|
119
|
+
end
|
|
120
|
+
end
|
|
121
|
+
|
|
122
|
+
it 'works with middleware arguments (ignores them)' do
|
|
123
|
+
allow(ActiveRecord).to receive(:version).and_return(double('>=' => false))
|
|
124
|
+
expect(ActiveRecord::Base).to receive(:clear_active_connections!)
|
|
125
|
+
|
|
126
|
+
worker = double('worker')
|
|
127
|
+
message = double('message')
|
|
128
|
+
|
|
129
|
+
result = subject.call(worker, message) { 'middleware_result' }
|
|
130
|
+
expect(result).to eq('middleware_result')
|
|
131
|
+
end
|
|
132
|
+
end
|
|
133
|
+
end
|
|
@@ -64,4 +64,54 @@ RSpec.describe Shoryuken::Middleware::Server::AutoExtendVisibility do
|
|
|
64
64
|
|
|
65
65
|
Runner.new.run_and_sleep(TestWorker.new, queue, sqs_msg, visibility_timeout)
|
|
66
66
|
end
|
|
67
|
+
|
|
68
|
+
context 'when batch worker with auto_visibility_timeout' do
|
|
69
|
+
it 'warns and does not extend visibility for batch workers' do
|
|
70
|
+
TestWorker.get_shoryuken_options['auto_visibility_timeout'] = true
|
|
71
|
+
|
|
72
|
+
expect(Shoryuken.logger).to receive(:warn) do |&block|
|
|
73
|
+
expect(block.call).to include("Auto extend visibility isn't supported for batch workers")
|
|
74
|
+
end
|
|
75
|
+
|
|
76
|
+
expect { |b| subject.call(TestWorker.new, queue, [sqs_msg], nil, &b) }.to yield_control
|
|
77
|
+
end
|
|
78
|
+
end
|
|
79
|
+
|
|
80
|
+
context 'when visibility extension fails' do
|
|
81
|
+
it 'logs error when change_visibility raises an exception' do
|
|
82
|
+
TestWorker.get_shoryuken_options['auto_visibility_timeout'] = true
|
|
83
|
+
|
|
84
|
+
allow(sqs_msg).to receive(:queue) { sqs_queue }
|
|
85
|
+
allow(sqs_msg).to receive(:message_id).and_return('test-message-id')
|
|
86
|
+
allow(sqs_msg).to receive(:change_visibility).and_raise(StandardError, 'AWS error')
|
|
87
|
+
|
|
88
|
+
expect(Shoryuken.logger).to receive(:error) do |&block|
|
|
89
|
+
msg = block.call
|
|
90
|
+
expect(msg).to include('Could not auto extend the message')
|
|
91
|
+
expect(msg).to include('test-message-id')
|
|
92
|
+
expect(msg).to include('AWS error')
|
|
93
|
+
end
|
|
94
|
+
|
|
95
|
+
Runner.new.run_and_sleep(TestWorker.new, queue, sqs_msg, visibility_timeout)
|
|
96
|
+
end
|
|
97
|
+
end
|
|
98
|
+
|
|
99
|
+
context 'debug logging' do
|
|
100
|
+
it 'logs debug message when extending visibility' do
|
|
101
|
+
TestWorker.get_shoryuken_options['auto_visibility_timeout'] = true
|
|
102
|
+
|
|
103
|
+
allow(sqs_msg).to receive(:queue) { sqs_queue }
|
|
104
|
+
allow(sqs_msg).to receive(:message_id).and_return('test-message-id')
|
|
105
|
+
allow(sqs_msg).to receive(:change_visibility)
|
|
106
|
+
|
|
107
|
+
expect(Shoryuken.logger).to receive(:debug) do |&block|
|
|
108
|
+
msg = block.call
|
|
109
|
+
expect(msg).to include('Extending message')
|
|
110
|
+
expect(msg).to include('test-message-id')
|
|
111
|
+
expect(msg).to include("by #{visibility_timeout}s")
|
|
112
|
+
end
|
|
113
|
+
|
|
114
|
+
Runner.new.run_and_sleep(TestWorker.new, queue, sqs_msg, visibility_timeout)
|
|
115
|
+
end
|
|
116
|
+
end
|
|
67
117
|
end
|
|
@@ -188,8 +188,8 @@ RSpec.describe Shoryuken::Options do
|
|
|
188
188
|
end
|
|
189
189
|
|
|
190
190
|
specify do
|
|
191
|
-
expect { Shoryuken.polling_strategy('default') }.to raise_error(
|
|
192
|
-
expect { Shoryuken.polling_strategy('group1') }.to raise_error(
|
|
191
|
+
expect { Shoryuken.polling_strategy('default') }.to raise_error(Shoryuken::Errors::InvalidPollingStrategyError)
|
|
192
|
+
expect { Shoryuken.polling_strategy('group1') }.to raise_error(Shoryuken::Errors::InvalidPollingStrategyError)
|
|
193
193
|
end
|
|
194
194
|
end
|
|
195
195
|
|
|
@@ -26,7 +26,7 @@ describe 'Shoryuken::Util' do
|
|
|
26
26
|
end
|
|
27
27
|
|
|
28
28
|
let(:message_attributes) do
|
|
29
|
-
{ 'shoryuken_class' => { string_value: ActiveJob::
|
|
29
|
+
{ 'shoryuken_class' => { string_value: 'Shoryuken::ActiveJob::JobWrapper' } }
|
|
30
30
|
end
|
|
31
31
|
|
|
32
32
|
let(:body) do
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require 'spec_helper'
|
|
4
|
+
|
|
5
|
+
RSpec.describe Shoryuken::VERSION do
|
|
6
|
+
it 'has a version number' do
|
|
7
|
+
expect(Shoryuken::VERSION).not_to be_nil
|
|
8
|
+
end
|
|
9
|
+
|
|
10
|
+
it 'follows semantic versioning format' do
|
|
11
|
+
expect(Shoryuken::VERSION).to match(/^\d+\.\d+\.\d+/)
|
|
12
|
+
end
|
|
13
|
+
|
|
14
|
+
it 'is a string' do
|
|
15
|
+
expect(Shoryuken::VERSION).to be_a(String)
|
|
16
|
+
end
|
|
17
|
+
end
|
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require 'spec_helper'
|
|
4
|
+
|
|
5
|
+
RSpec.describe Shoryuken::WorkerRegistry do
|
|
6
|
+
subject { described_class.new }
|
|
7
|
+
|
|
8
|
+
describe '#batch_receive_messages?' do
|
|
9
|
+
it 'raises NotImplementedError' do
|
|
10
|
+
expect { subject.batch_receive_messages?('test-queue') }.to raise_error(NotImplementedError)
|
|
11
|
+
end
|
|
12
|
+
end
|
|
13
|
+
|
|
14
|
+
describe '#clear' do
|
|
15
|
+
it 'raises NotImplementedError' do
|
|
16
|
+
expect { subject.clear }.to raise_error(NotImplementedError)
|
|
17
|
+
end
|
|
18
|
+
end
|
|
19
|
+
|
|
20
|
+
describe '#fetch_worker' do
|
|
21
|
+
it 'raises NotImplementedError' do
|
|
22
|
+
queue = 'test-queue'
|
|
23
|
+
message = double('message')
|
|
24
|
+
expect { subject.fetch_worker(queue, message) }.to raise_error(NotImplementedError)
|
|
25
|
+
end
|
|
26
|
+
end
|
|
27
|
+
|
|
28
|
+
describe '#queues' do
|
|
29
|
+
it 'raises NotImplementedError' do
|
|
30
|
+
expect { subject.queues }.to raise_error(NotImplementedError)
|
|
31
|
+
end
|
|
32
|
+
end
|
|
33
|
+
|
|
34
|
+
describe '#register_worker' do
|
|
35
|
+
it 'raises NotImplementedError' do
|
|
36
|
+
queue = 'test-queue'
|
|
37
|
+
worker_class = Class.new
|
|
38
|
+
expect { subject.register_worker(queue, worker_class) }.to raise_error(NotImplementedError)
|
|
39
|
+
end
|
|
40
|
+
end
|
|
41
|
+
|
|
42
|
+
describe '#workers' do
|
|
43
|
+
it 'raises NotImplementedError' do
|
|
44
|
+
expect { subject.workers('test-queue') }.to raise_error(NotImplementedError)
|
|
45
|
+
end
|
|
46
|
+
end
|
|
47
|
+
|
|
48
|
+
context 'interface documentation' do
|
|
49
|
+
it 'defines the required interface methods' do
|
|
50
|
+
expect(subject).to respond_to(:batch_receive_messages?)
|
|
51
|
+
expect(subject).to respond_to(:clear)
|
|
52
|
+
expect(subject).to respond_to(:fetch_worker)
|
|
53
|
+
expect(subject).to respond_to(:queues)
|
|
54
|
+
expect(subject).to respond_to(:register_worker)
|
|
55
|
+
expect(subject).to respond_to(:workers)
|
|
56
|
+
end
|
|
57
|
+
|
|
58
|
+
it 'is designed to be subclassed' do
|
|
59
|
+
expect(described_class).to be < Object
|
|
60
|
+
expect(described_class.ancestors).to include(described_class)
|
|
61
|
+
end
|
|
62
|
+
end
|
|
63
|
+
end
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
require 'active_job'
|
|
2
|
-
require '
|
|
2
|
+
require 'active_job/extensions'
|
|
3
3
|
|
|
4
4
|
# Stand-in for a job class specified by the user
|
|
5
5
|
class TestJob < ActiveJob::Base; end
|
|
@@ -23,11 +23,11 @@ RSpec.shared_examples 'active_job_adapters' do
|
|
|
23
23
|
specify do
|
|
24
24
|
expect(queue).to receive(:send_message) do |hash|
|
|
25
25
|
expect(hash[:message_deduplication_id]).to_not be
|
|
26
|
-
expect(hash[:message_attributes]['shoryuken_class'][:string_value]).to eq(
|
|
26
|
+
expect(hash[:message_attributes]['shoryuken_class'][:string_value]).to eq(Shoryuken::ActiveJob::JobWrapper.to_s)
|
|
27
27
|
expect(hash[:message_attributes]['shoryuken_class'][:data_type]).to eq('String')
|
|
28
28
|
expect(hash[:message_attributes].keys).to eq(['shoryuken_class'])
|
|
29
29
|
end
|
|
30
|
-
expect(Shoryuken).to receive(:register_worker).with(job.queue_name,
|
|
30
|
+
expect(Shoryuken).to receive(:register_worker).with(job.queue_name, Shoryuken::ActiveJob::JobWrapper)
|
|
31
31
|
|
|
32
32
|
subject.enqueue(job)
|
|
33
33
|
end
|
|
@@ -50,7 +50,7 @@ RSpec.shared_examples 'active_job_adapters' do
|
|
|
50
50
|
|
|
51
51
|
expect(hash[:message_deduplication_id]).to eq(message_deduplication_id)
|
|
52
52
|
end
|
|
53
|
-
expect(Shoryuken).to receive(:register_worker).with(job.queue_name,
|
|
53
|
+
expect(Shoryuken).to receive(:register_worker).with(job.queue_name, Shoryuken::ActiveJob::JobWrapper)
|
|
54
54
|
|
|
55
55
|
subject.enqueue(job)
|
|
56
56
|
end
|
|
@@ -132,12 +132,12 @@ RSpec.shared_examples 'active_job_adapters' do
|
|
|
132
132
|
}
|
|
133
133
|
|
|
134
134
|
expect(queue).to receive(:send_message) do |hash|
|
|
135
|
-
expect(hash[:message_attributes]['shoryuken_class'][:string_value]).to eq(
|
|
135
|
+
expect(hash[:message_attributes]['shoryuken_class'][:string_value]).to eq(Shoryuken::ActiveJob::JobWrapper.to_s)
|
|
136
136
|
expect(hash[:message_attributes]['shoryuken_class'][:data_type]).to eq('String')
|
|
137
137
|
expect(hash[:message_attributes]['tracer_id'][:string_value]).to eq(custom_message_attributes['tracer_id'][:string_value])
|
|
138
138
|
expect(hash[:message_attributes]['tracer_id'][:data_type]).to eq('String')
|
|
139
139
|
end
|
|
140
|
-
expect(Shoryuken).to receive(:register_worker).with(job.queue_name,
|
|
140
|
+
expect(Shoryuken).to receive(:register_worker).with(job.queue_name, Shoryuken::ActiveJob::JobWrapper)
|
|
141
141
|
|
|
142
142
|
subject.enqueue(job, message_attributes: custom_message_attributes)
|
|
143
143
|
end
|
|
@@ -158,7 +158,7 @@ RSpec.shared_examples 'active_job_adapters' do
|
|
|
158
158
|
expect(queue).to receive(:send_message) do |hash|
|
|
159
159
|
expect(hash[:message_attributes]['tracer_id']).to eq({ data_type: 'String', string_value: 'job-value' })
|
|
160
160
|
expect(hash[:message_attributes]['shoryuken_class']).to eq({ data_type: 'String',
|
|
161
|
-
string_value:
|
|
161
|
+
string_value: Shoryuken::ActiveJob::JobWrapper.to_s })
|
|
162
162
|
end
|
|
163
163
|
subject.enqueue job
|
|
164
164
|
end
|
|
@@ -189,7 +189,7 @@ RSpec.shared_examples 'active_job_adapters' do
|
|
|
189
189
|
expect(hash[:message_attributes]['options_tracer_id']).to eq({ data_type: 'String',
|
|
190
190
|
string_value: 'options-value' })
|
|
191
191
|
expect(hash[:message_attributes]['shoryuken_class']).to eq({ data_type: 'String',
|
|
192
|
-
string_value:
|
|
192
|
+
string_value: Shoryuken::ActiveJob::JobWrapper.to_s })
|
|
193
193
|
end
|
|
194
194
|
subject.enqueue job, message_attributes: custom_message_attributes
|
|
195
195
|
end
|
|
@@ -274,13 +274,33 @@ RSpec.shared_examples 'active_job_adapters' do
|
|
|
274
274
|
expect(hash[:delay_seconds]).to eq(delay)
|
|
275
275
|
end
|
|
276
276
|
|
|
277
|
-
expect(Shoryuken).to receive(:register_worker).with(job.queue_name,
|
|
277
|
+
expect(Shoryuken).to receive(:register_worker).with(job.queue_name, Shoryuken::ActiveJob::JobWrapper)
|
|
278
278
|
|
|
279
279
|
# need to figure out what to require Time.current and N.minutes to remove the stub
|
|
280
280
|
allow(subject).to receive(:calculate_delay).and_return(delay)
|
|
281
281
|
|
|
282
282
|
subject.enqueue_at(job, nil)
|
|
283
283
|
end
|
|
284
|
+
|
|
285
|
+
context 'when fifo' do
|
|
286
|
+
let(:fifo) { true }
|
|
287
|
+
|
|
288
|
+
it 'raises ArgumentError when delay is positive' do
|
|
289
|
+
allow(subject).to receive(:calculate_delay).and_return(3)
|
|
290
|
+
allow(queue).to receive(:name).and_return('test.fifo')
|
|
291
|
+
expect(queue).not_to receive(:send_message)
|
|
292
|
+
|
|
293
|
+
expect { subject.enqueue_at(job, nil) }.to raise_error(
|
|
294
|
+
ArgumentError, /FIFO queue.*does not support per-message delays/
|
|
295
|
+
)
|
|
296
|
+
end
|
|
297
|
+
|
|
298
|
+
it 'does not raise when delay is zero' do
|
|
299
|
+
allow(subject).to receive(:calculate_delay).and_return(0)
|
|
300
|
+
expect(queue).to receive(:send_message).with(hash_including(delay_seconds: 0))
|
|
301
|
+
expect { subject.enqueue_at(job, nil) }.not_to raise_error
|
|
302
|
+
end
|
|
303
|
+
end
|
|
284
304
|
end
|
|
285
305
|
end
|
|
286
306
|
# rubocop:enable Metrics/BlockLength
|
data/spec/spec_helper.rb
CHANGED
|
@@ -7,9 +7,20 @@ $VERBOSE = true
|
|
|
7
7
|
require 'warning'
|
|
8
8
|
|
|
9
9
|
Warning.process do |warning|
|
|
10
|
+
# Only check warnings from our code (not dependencies)
|
|
10
11
|
next unless warning.include?(Dir.pwd)
|
|
11
|
-
|
|
12
|
+
|
|
13
|
+
# Filter out warnings we don't care about in specs
|
|
14
|
+
next if warning.include?('_spec')
|
|
15
|
+
|
|
16
|
+
# We redefine methods to simulate various scenarios in tests
|
|
17
|
+
next if warning.include?('previous definition of')
|
|
18
|
+
next if warning.include?('method redefined')
|
|
19
|
+
|
|
20
|
+
# Ignore vendor and bundle directories
|
|
12
21
|
next if warning.include?('vendor/')
|
|
22
|
+
next if warning.include?('bundle/')
|
|
23
|
+
next if warning.include?('.bundle/')
|
|
13
24
|
|
|
14
25
|
raise "Warning in your code: #{warning}"
|
|
15
26
|
end
|
|
@@ -29,8 +40,28 @@ require 'securerandom'
|
|
|
29
40
|
require 'ostruct'
|
|
30
41
|
Dotenv.load
|
|
31
42
|
|
|
32
|
-
|
|
33
|
-
|
|
43
|
+
unless ENV['SIMPLECOV_DISABLED']
|
|
44
|
+
require 'simplecov'
|
|
45
|
+
SimpleCov.start do
|
|
46
|
+
add_filter '/spec/'
|
|
47
|
+
add_filter '/test_workers/'
|
|
48
|
+
add_filter '/examples/'
|
|
49
|
+
add_filter '/vendor/'
|
|
50
|
+
add_filter '/.bundle/'
|
|
51
|
+
|
|
52
|
+
add_group 'Library', 'lib/'
|
|
53
|
+
add_group 'ActiveJob', 'lib/active_job'
|
|
54
|
+
add_group 'Middleware', 'lib/shoryuken/middleware'
|
|
55
|
+
add_group 'Polling', 'lib/shoryuken/polling'
|
|
56
|
+
add_group 'Workers', 'lib/shoryuken/worker'
|
|
57
|
+
add_group 'Helpers', 'lib/shoryuken/helpers'
|
|
58
|
+
|
|
59
|
+
enable_coverage :branch
|
|
60
|
+
|
|
61
|
+
minimum_coverage 89
|
|
62
|
+
minimum_coverage_by_file 60
|
|
63
|
+
end
|
|
64
|
+
end
|
|
34
65
|
|
|
35
66
|
config_file = File.join(File.expand_path('..', __dir__), 'spec', 'shoryuken.yml')
|
|
36
67
|
|