shoryuken 6.2.1 → 7.0.2
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 +36 -0
- data/.github/workflows/specs.yml +49 -44
- data/.github/workflows/verify-action-pins.yml +16 -0
- data/.gitignore +4 -1
- data/.rspec +3 -1
- data/.rubocop.yml +6 -1
- data/.ruby-version +1 -0
- data/.yard-lint.yml +279 -0
- data/CHANGELOG.md +308 -139
- data/Gemfile +1 -8
- data/Gemfile.lint +9 -0
- data/Gemfile.lint.lock +69 -0
- data/README.md +16 -33
- data/Rakefile +6 -10
- data/bin/clean_sqs +52 -0
- data/bin/cli/base.rb +22 -2
- data/bin/cli/sqs.rb +74 -7
- data/bin/integrations +275 -0
- data/bin/scenario +154 -0
- data/bin/shoryuken +3 -2
- data/docker-compose.yml +6 -0
- data/lib/{shoryuken/extensions/active_job_extensions.rb → active_job/extensions.rb} +20 -6
- 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 +11 -1
- data/lib/shoryuken/client.rb +16 -0
- data/lib/shoryuken/default_exception_handler.rb +11 -0
- data/lib/shoryuken/default_worker_registry.rb +39 -11
- data/lib/shoryuken/environment_loader.rb +85 -15
- data/lib/shoryuken/errors.rb +36 -0
- data/lib/shoryuken/fetcher.rb +41 -3
- data/lib/shoryuken/helpers/atomic_boolean.rb +58 -0
- data/lib/shoryuken/helpers/atomic_counter.rb +104 -0
- data/lib/shoryuken/helpers/atomic_hash.rb +182 -0
- data/lib/shoryuken/helpers/hash_utils.rb +56 -0
- data/lib/shoryuken/helpers/string_utils.rb +65 -0
- data/lib/shoryuken/helpers/timer_task.rb +80 -0
- data/lib/shoryuken/inline_message.rb +22 -0
- data/lib/shoryuken/launcher.rb +55 -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 +43 -15
- data/lib/shoryuken/manager.rb +84 -5
- data/lib/shoryuken/message.rb +116 -1
- data/lib/shoryuken/middleware/chain.rb +141 -43
- data/lib/shoryuken/middleware/entry.rb +30 -0
- data/lib/shoryuken/middleware/server/active_record.rb +10 -0
- data/lib/shoryuken/middleware/server/auto_delete.rb +12 -0
- data/lib/shoryuken/middleware/server/auto_extend_visibility.rb +37 -11
- data/lib/shoryuken/middleware/server/exponential_backoff_retry.rb +34 -3
- data/lib/shoryuken/middleware/server/non_retryable_exception.rb +95 -0
- data/lib/shoryuken/middleware/server/timing.rb +13 -0
- data/lib/shoryuken/options.rb +154 -13
- data/lib/shoryuken/polling/base_strategy.rb +127 -0
- data/lib/shoryuken/polling/queue_configuration.rb +103 -0
- data/lib/shoryuken/polling/strict_priority.rb +41 -0
- data/lib/shoryuken/polling/weighted_round_robin.rb +44 -0
- data/lib/shoryuken/processor.rb +37 -3
- data/lib/shoryuken/queue.rb +99 -8
- data/lib/shoryuken/runner.rb +54 -16
- data/lib/shoryuken/util.rb +32 -7
- data/lib/shoryuken/version.rb +4 -1
- data/lib/shoryuken/worker/default_executor.rb +23 -1
- data/lib/shoryuken/worker/inline_executor.rb +33 -2
- data/lib/shoryuken/worker.rb +224 -0
- data/lib/shoryuken/worker_registry.rb +35 -0
- data/lib/shoryuken.rb +27 -38
- data/renovate.json +62 -0
- data/shoryuken.gemspec +8 -4
- 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/keyword_arguments/keyword_arguments_spec.rb +63 -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/custom_group_polling_strategy/custom_group_polling_strategy_spec.rb +87 -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/non_retryable_exception/non_retryable_exception_spec.rb +149 -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 +225 -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} +5 -4
- data/spec/{shoryuken/extensions/active_job_wrapper_spec.rb → lib/shoryuken/active_job/job_wrapper_spec.rb} +6 -5
- data/spec/{shoryuken → lib/shoryuken}/body_parser_spec.rb +2 -4
- data/spec/{shoryuken → lib/shoryuken}/client_spec.rb +1 -1
- data/spec/{shoryuken → lib/shoryuken}/default_exception_handler_spec.rb +9 -10
- data/spec/{shoryuken → lib/shoryuken}/default_worker_registry_spec.rb +1 -2
- data/spec/{shoryuken → lib/shoryuken}/environment_loader_spec.rb +10 -9
- data/spec/{shoryuken → lib/shoryuken}/fetcher_spec.rb +23 -26
- data/spec/lib/shoryuken/helpers/atomic_boolean_spec.rb +196 -0
- data/spec/lib/shoryuken/helpers/atomic_counter_spec.rb +177 -0
- data/spec/lib/shoryuken/helpers/atomic_hash_spec.rb +307 -0
- data/spec/lib/shoryuken/helpers/hash_utils_spec.rb +145 -0
- data/spec/lib/shoryuken/helpers/string_utils_spec.rb +124 -0
- data/spec/lib/shoryuken/helpers/timer_task_spec.rb +298 -0
- data/spec/lib/shoryuken/helpers_integration_spec.rb +96 -0
- data/spec/lib/shoryuken/inline_message_spec.rb +196 -0
- data/spec/{shoryuken → lib/shoryuken}/launcher_spec.rb +23 -2
- data/spec/lib/shoryuken/logging_spec.rb +242 -0
- data/spec/{shoryuken → lib/shoryuken}/manager_spec.rb +1 -2
- data/spec/lib/shoryuken/message_spec.rb +109 -0
- data/spec/{shoryuken → lib/shoryuken}/middleware/chain_spec.rb +1 -1
- 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_delete_spec.rb +1 -1
- data/spec/{shoryuken → lib/shoryuken}/middleware/server/auto_extend_visibility_spec.rb +51 -1
- data/spec/{shoryuken → lib/shoryuken}/middleware/server/exponential_backoff_retry_spec.rb +1 -1
- data/spec/lib/shoryuken/middleware/server/non_retryable_exception_spec.rb +214 -0
- data/spec/{shoryuken → lib/shoryuken}/middleware/server/timing_spec.rb +1 -1
- data/spec/{shoryuken → lib/shoryuken}/options_spec.rb +49 -6
- data/spec/lib/shoryuken/polling/base_strategy_spec.rb +280 -0
- data/spec/lib/shoryuken/polling/queue_configuration_spec.rb +195 -0
- data/spec/{shoryuken → lib/shoryuken}/polling/strict_priority_spec.rb +1 -1
- data/spec/{shoryuken → lib/shoryuken}/polling/weighted_round_robin_spec.rb +1 -1
- data/spec/{shoryuken → lib/shoryuken}/processor_spec.rb +1 -1
- data/spec/{shoryuken → lib/shoryuken}/queue_spec.rb +2 -3
- data/spec/{shoryuken → lib/shoryuken}/runner_spec.rb +1 -3
- data/spec/{shoryuken → lib/shoryuken}/util_spec.rb +2 -2
- data/spec/lib/shoryuken/version_spec.rb +17 -0
- data/spec/{shoryuken → lib/shoryuken}/worker/default_executor_spec.rb +1 -1
- data/spec/lib/shoryuken/worker/inline_executor_spec.rb +105 -0
- data/spec/lib/shoryuken/worker_registry_spec.rb +63 -0
- data/spec/{shoryuken → lib/shoryuken}/worker_spec.rb +15 -11
- data/spec/{shoryuken_spec.rb → lib/shoryuken_spec.rb} +1 -1
- data/spec/shared_examples_for_active_job.rb +40 -15
- data/spec/spec_helper.rb +48 -2
- metadata +295 -101
- data/.codeclimate.yml +0 -20
- data/.devcontainer/Dockerfile +0 -17
- data/.devcontainer/base.Dockerfile +0 -43
- data/.devcontainer/devcontainer.json +0 -35
- data/.github/FUNDING.yml +0 -12
- data/.github/dependabot.yml +0 -6
- data/.github/workflows/stale.yml +0 -20
- data/.reek.yml +0 -5
- data/Appraisals +0 -42
- data/gemfiles/.gitignore +0 -1
- data/gemfiles/aws_sdk_core_2.gemfile +0 -21
- data/gemfiles/rails_4_2.gemfile +0 -20
- data/gemfiles/rails_5_2.gemfile +0 -21
- data/gemfiles/rails_6_0.gemfile +0 -21
- data/gemfiles/rails_6_1.gemfile +0 -21
- data/gemfiles/rails_7_0.gemfile +0 -22
- data/lib/shoryuken/core_ext.rb +0 -69
- data/lib/shoryuken/extensions/active_job_adapter.rb +0 -103
- data/lib/shoryuken/extensions/active_job_concurrent_send_adapter.rb +0 -50
- data/lib/shoryuken/polling/base.rb +0 -67
- data/shoryuken.jpg +0 -0
- data/spec/integration/launcher_spec.rb +0 -128
- data/spec/shoryuken/core_ext_spec.rb +0 -40
- data/spec/shoryuken/extensions/active_job_adapter_spec.rb +0 -7
- data/spec/shoryuken/extensions/active_job_base_spec.rb +0 -84
- data/spec/shoryuken/worker/inline_executor_spec.rb +0 -49
|
@@ -0,0 +1,196 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
RSpec.describe Shoryuken::InlineMessage do
|
|
4
|
+
let(:body) { '{"message": "test"}' }
|
|
5
|
+
let(:attributes) { { 'SentTimestamp' => '1234567890' } }
|
|
6
|
+
let(:md5_of_body) { 'abc123def456' }
|
|
7
|
+
let(:md5_of_message_attributes) { 'def456abc123' }
|
|
8
|
+
let(:message_attributes) { { 'CustomAttribute' => { string_value: 'value', data_type: 'String' } } }
|
|
9
|
+
let(:message_id) { 'msg-12345' }
|
|
10
|
+
let(:receipt_handle) { 'receipt-handle-12345' }
|
|
11
|
+
let(:delete) { nil }
|
|
12
|
+
let(:queue_name) { 'test-queue' }
|
|
13
|
+
|
|
14
|
+
describe '#new' do
|
|
15
|
+
context 'with positional arguments' do
|
|
16
|
+
subject do
|
|
17
|
+
described_class.new(
|
|
18
|
+
body: body,
|
|
19
|
+
attributes: attributes,
|
|
20
|
+
md5_of_body: md5_of_body,
|
|
21
|
+
md5_of_message_attributes: md5_of_message_attributes,
|
|
22
|
+
message_attributes: message_attributes,
|
|
23
|
+
message_id: message_id,
|
|
24
|
+
receipt_handle: receipt_handle,
|
|
25
|
+
delete: delete,
|
|
26
|
+
queue_name: queue_name
|
|
27
|
+
)
|
|
28
|
+
end
|
|
29
|
+
|
|
30
|
+
it 'initializes with all attributes' do
|
|
31
|
+
expect(subject.body).to eq(body)
|
|
32
|
+
expect(subject.attributes).to eq(attributes)
|
|
33
|
+
expect(subject.md5_of_body).to eq(md5_of_body)
|
|
34
|
+
expect(subject.md5_of_message_attributes).to eq(md5_of_message_attributes)
|
|
35
|
+
expect(subject.message_attributes).to eq(message_attributes)
|
|
36
|
+
expect(subject.message_id).to eq(message_id)
|
|
37
|
+
expect(subject.receipt_handle).to eq(receipt_handle)
|
|
38
|
+
expect(subject.delete).to eq(delete)
|
|
39
|
+
expect(subject.queue_name).to eq(queue_name)
|
|
40
|
+
end
|
|
41
|
+
end
|
|
42
|
+
|
|
43
|
+
context 'with keyword arguments' do
|
|
44
|
+
subject do
|
|
45
|
+
described_class.new(
|
|
46
|
+
body: body,
|
|
47
|
+
attributes: attributes,
|
|
48
|
+
md5_of_body: md5_of_body,
|
|
49
|
+
md5_of_message_attributes: md5_of_message_attributes,
|
|
50
|
+
message_attributes: message_attributes,
|
|
51
|
+
message_id: message_id,
|
|
52
|
+
receipt_handle: receipt_handle,
|
|
53
|
+
delete: delete,
|
|
54
|
+
queue_name: queue_name
|
|
55
|
+
)
|
|
56
|
+
end
|
|
57
|
+
|
|
58
|
+
it 'initializes with all attributes' do
|
|
59
|
+
expect(subject.body).to eq(body)
|
|
60
|
+
expect(subject.attributes).to eq(attributes)
|
|
61
|
+
expect(subject.md5_of_body).to eq(md5_of_body)
|
|
62
|
+
expect(subject.md5_of_message_attributes).to eq(md5_of_message_attributes)
|
|
63
|
+
expect(subject.message_attributes).to eq(message_attributes)
|
|
64
|
+
expect(subject.message_id).to eq(message_id)
|
|
65
|
+
expect(subject.receipt_handle).to eq(receipt_handle)
|
|
66
|
+
expect(subject.delete).to eq(delete)
|
|
67
|
+
expect(subject.queue_name).to eq(queue_name)
|
|
68
|
+
end
|
|
69
|
+
end
|
|
70
|
+
|
|
71
|
+
context 'with nil values' do
|
|
72
|
+
subject do
|
|
73
|
+
described_class.new(
|
|
74
|
+
body: body,
|
|
75
|
+
attributes: nil,
|
|
76
|
+
md5_of_body: nil,
|
|
77
|
+
md5_of_message_attributes: nil,
|
|
78
|
+
message_attributes: message_attributes,
|
|
79
|
+
message_id: nil,
|
|
80
|
+
receipt_handle: nil,
|
|
81
|
+
delete: nil,
|
|
82
|
+
queue_name: queue_name
|
|
83
|
+
)
|
|
84
|
+
end
|
|
85
|
+
|
|
86
|
+
it 'handles nil values correctly' do
|
|
87
|
+
expect(subject.body).to eq(body)
|
|
88
|
+
expect(subject.attributes).to be_nil
|
|
89
|
+
expect(subject.md5_of_body).to be_nil
|
|
90
|
+
expect(subject.md5_of_message_attributes).to be_nil
|
|
91
|
+
expect(subject.message_attributes).to eq(message_attributes)
|
|
92
|
+
expect(subject.message_id).to be_nil
|
|
93
|
+
expect(subject.receipt_handle).to be_nil
|
|
94
|
+
expect(subject.delete).to be_nil
|
|
95
|
+
expect(subject.queue_name).to eq(queue_name)
|
|
96
|
+
end
|
|
97
|
+
end
|
|
98
|
+
|
|
99
|
+
context 'with minimal required attributes' do
|
|
100
|
+
subject { described_class.new(body: body, queue_name: queue_name) }
|
|
101
|
+
|
|
102
|
+
it 'initializes with only required attributes' do
|
|
103
|
+
expect(subject.body).to eq(body)
|
|
104
|
+
expect(subject.queue_name).to eq(queue_name)
|
|
105
|
+
expect(subject.attributes).to be_nil
|
|
106
|
+
expect(subject.md5_of_body).to be_nil
|
|
107
|
+
expect(subject.md5_of_message_attributes).to be_nil
|
|
108
|
+
expect(subject.message_attributes).to be_nil
|
|
109
|
+
expect(subject.message_id).to be_nil
|
|
110
|
+
expect(subject.receipt_handle).to be_nil
|
|
111
|
+
expect(subject.delete).to be_nil
|
|
112
|
+
end
|
|
113
|
+
end
|
|
114
|
+
end
|
|
115
|
+
|
|
116
|
+
describe 'attribute accessors' do
|
|
117
|
+
subject do
|
|
118
|
+
described_class.new(
|
|
119
|
+
body: body,
|
|
120
|
+
attributes: attributes,
|
|
121
|
+
md5_of_body: md5_of_body,
|
|
122
|
+
md5_of_message_attributes: md5_of_message_attributes,
|
|
123
|
+
message_attributes: message_attributes,
|
|
124
|
+
message_id: message_id,
|
|
125
|
+
receipt_handle: receipt_handle,
|
|
126
|
+
delete: delete,
|
|
127
|
+
queue_name: queue_name
|
|
128
|
+
)
|
|
129
|
+
end
|
|
130
|
+
|
|
131
|
+
it 'provides read access to all attributes' do
|
|
132
|
+
expect(subject.body).to eq(body)
|
|
133
|
+
expect(subject.attributes).to eq(attributes)
|
|
134
|
+
expect(subject.md5_of_body).to eq(md5_of_body)
|
|
135
|
+
expect(subject.md5_of_message_attributes).to eq(md5_of_message_attributes)
|
|
136
|
+
expect(subject.message_attributes).to eq(message_attributes)
|
|
137
|
+
expect(subject.message_id).to eq(message_id)
|
|
138
|
+
expect(subject.receipt_handle).to eq(receipt_handle)
|
|
139
|
+
expect(subject.delete).to eq(delete)
|
|
140
|
+
expect(subject.queue_name).to eq(queue_name)
|
|
141
|
+
end
|
|
142
|
+
|
|
143
|
+
it 'provides write access to all attributes' do
|
|
144
|
+
new_body = '{"updated": "message"}'
|
|
145
|
+
new_queue_name = 'updated-queue'
|
|
146
|
+
|
|
147
|
+
subject.body = new_body
|
|
148
|
+
subject.queue_name = new_queue_name
|
|
149
|
+
|
|
150
|
+
expect(subject.body).to eq(new_body)
|
|
151
|
+
expect(subject.queue_name).to eq(new_queue_name)
|
|
152
|
+
end
|
|
153
|
+
end
|
|
154
|
+
|
|
155
|
+
describe 'struct behavior' do
|
|
156
|
+
subject { described_class.new(body: body, queue_name: queue_name) }
|
|
157
|
+
|
|
158
|
+
it 'behaves like a struct' do
|
|
159
|
+
expect(subject).to be_a(Struct)
|
|
160
|
+
expect(subject.class.superclass).to eq(Struct)
|
|
161
|
+
end
|
|
162
|
+
|
|
163
|
+
it 'supports array-like access' do
|
|
164
|
+
expect(subject[0]).to eq(body) # body is first attribute
|
|
165
|
+
expect(subject[-1]).to eq(queue_name) # queue_name is last attribute
|
|
166
|
+
end
|
|
167
|
+
|
|
168
|
+
it 'supports enumeration' do
|
|
169
|
+
values = subject.to_a
|
|
170
|
+
expect(values.first).to eq(body)
|
|
171
|
+
expect(values.last).to eq(queue_name)
|
|
172
|
+
expect(values.length).to eq(9) # 9 attributes total
|
|
173
|
+
end
|
|
174
|
+
|
|
175
|
+
it 'supports hash conversion' do
|
|
176
|
+
hash = subject.to_h
|
|
177
|
+
expect(hash[:body]).to eq(body)
|
|
178
|
+
expect(hash[:queue_name]).to eq(queue_name)
|
|
179
|
+
expect(hash.keys).to contain_exactly(
|
|
180
|
+
:body, :attributes, :md5_of_body, :md5_of_message_attributes,
|
|
181
|
+
:message_attributes, :message_id, :receipt_handle, :delete, :queue_name
|
|
182
|
+
)
|
|
183
|
+
end
|
|
184
|
+
end
|
|
185
|
+
|
|
186
|
+
describe 'equality' do
|
|
187
|
+
let(:message1) { described_class.new(body: body, queue_name: queue_name) }
|
|
188
|
+
let(:message2) { described_class.new(body: body, queue_name: queue_name) }
|
|
189
|
+
let(:message3) { described_class.new(body: 'different', queue_name: queue_name) }
|
|
190
|
+
|
|
191
|
+
it 'compares messages by attribute values' do
|
|
192
|
+
expect(message1).to eq(message2)
|
|
193
|
+
expect(message1).not_to eq(message3)
|
|
194
|
+
end
|
|
195
|
+
end
|
|
196
|
+
end
|
|
@@ -1,5 +1,4 @@
|
|
|
1
|
-
|
|
2
|
-
require 'shoryuken/launcher'
|
|
1
|
+
# frozen_string_literal: true
|
|
3
2
|
|
|
4
3
|
RSpec.describe Shoryuken::Launcher do
|
|
5
4
|
let(:executor) do
|
|
@@ -102,4 +101,26 @@ RSpec.describe Shoryuken::Launcher do
|
|
|
102
101
|
expect(second_group_manager).to have_received(:stop_new_dispatching)
|
|
103
102
|
end
|
|
104
103
|
end
|
|
104
|
+
|
|
105
|
+
describe '#stopping?' do
|
|
106
|
+
it 'returns false by default' do
|
|
107
|
+
expect(subject.stopping?).to be false
|
|
108
|
+
end
|
|
109
|
+
|
|
110
|
+
it 'returns true after stop is called' do
|
|
111
|
+
allow(first_group_manager).to receive(:stop_new_dispatching)
|
|
112
|
+
allow(first_group_manager).to receive(:await_dispatching_in_progress)
|
|
113
|
+
allow(second_group_manager).to receive(:stop_new_dispatching)
|
|
114
|
+
allow(second_group_manager).to receive(:await_dispatching_in_progress)
|
|
115
|
+
|
|
116
|
+
expect { subject.stop }.to change { subject.stopping? }.from(false).to(true)
|
|
117
|
+
end
|
|
118
|
+
|
|
119
|
+
it 'returns true after stop! is called' do
|
|
120
|
+
allow(first_group_manager).to receive(:stop_new_dispatching)
|
|
121
|
+
allow(second_group_manager).to receive(:stop_new_dispatching)
|
|
122
|
+
|
|
123
|
+
expect { subject.stop! }.to change { subject.stopping? }.from(false).to(true)
|
|
124
|
+
end
|
|
125
|
+
end
|
|
105
126
|
end
|
|
@@ -0,0 +1,242 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require 'spec_helper'
|
|
4
|
+
|
|
5
|
+
RSpec.describe Shoryuken::Logging do
|
|
6
|
+
describe Shoryuken::Logging::Base do
|
|
7
|
+
let(:formatter) { described_class.new }
|
|
8
|
+
|
|
9
|
+
describe '#tid' do
|
|
10
|
+
it 'returns a string representing the thread ID' do
|
|
11
|
+
expect(formatter.tid).to be_a(String)
|
|
12
|
+
end
|
|
13
|
+
|
|
14
|
+
it 'returns the same value for the same thread' do
|
|
15
|
+
tid1 = formatter.tid
|
|
16
|
+
tid2 = formatter.tid
|
|
17
|
+
expect(tid1).to eq(tid2)
|
|
18
|
+
end
|
|
19
|
+
|
|
20
|
+
it 'caches the thread ID in thread-local storage' do
|
|
21
|
+
tid = formatter.tid
|
|
22
|
+
expect(Thread.current['shoryuken_tid']).to eq(tid)
|
|
23
|
+
end
|
|
24
|
+
end
|
|
25
|
+
|
|
26
|
+
describe '#context' do
|
|
27
|
+
after do
|
|
28
|
+
Shoryuken::Logging.context_storage[:shoryuken_context] = nil
|
|
29
|
+
end
|
|
30
|
+
|
|
31
|
+
it 'returns empty string when no context is set' do
|
|
32
|
+
Shoryuken::Logging.context_storage[:shoryuken_context] = nil
|
|
33
|
+
expect(formatter.context).to eq('')
|
|
34
|
+
end
|
|
35
|
+
|
|
36
|
+
it 'returns formatted context when context is set' do
|
|
37
|
+
Shoryuken::Logging.context_storage[:shoryuken_context] = 'test_context'
|
|
38
|
+
expect(formatter.context).to eq(' test_context')
|
|
39
|
+
end
|
|
40
|
+
end
|
|
41
|
+
end
|
|
42
|
+
|
|
43
|
+
describe Shoryuken::Logging::Pretty do
|
|
44
|
+
let(:formatter) { described_class.new }
|
|
45
|
+
let(:time) { Time.new(2023, 8, 15, 10, 30, 45, '+00:00') }
|
|
46
|
+
|
|
47
|
+
describe '#call' do
|
|
48
|
+
after do
|
|
49
|
+
Shoryuken::Logging.context_storage[:shoryuken_context] = nil
|
|
50
|
+
end
|
|
51
|
+
|
|
52
|
+
it 'formats log messages with timestamp' do
|
|
53
|
+
allow(formatter).to receive(:tid).and_return('abc123')
|
|
54
|
+
Shoryuken::Logging.context_storage[:shoryuken_context] = nil
|
|
55
|
+
|
|
56
|
+
result = formatter.call('INFO', time, 'program', 'test message')
|
|
57
|
+
expect(result).to eq("2023-08-15T10:30:45Z #{Process.pid} TID-abc123 INFO: test message\n")
|
|
58
|
+
end
|
|
59
|
+
|
|
60
|
+
it 'includes context when present' do
|
|
61
|
+
allow(formatter).to receive(:tid).and_return('abc123')
|
|
62
|
+
Shoryuken::Logging.context_storage[:shoryuken_context] = 'worker-1'
|
|
63
|
+
|
|
64
|
+
result = formatter.call('ERROR', time, 'program', 'error message')
|
|
65
|
+
expect(result).to eq("2023-08-15T10:30:45Z #{Process.pid} TID-abc123 worker-1 ERROR: error message\n")
|
|
66
|
+
end
|
|
67
|
+
end
|
|
68
|
+
end
|
|
69
|
+
|
|
70
|
+
describe Shoryuken::Logging::WithoutTimestamp do
|
|
71
|
+
let(:formatter) { described_class.new }
|
|
72
|
+
|
|
73
|
+
describe '#call' do
|
|
74
|
+
after do
|
|
75
|
+
Shoryuken::Logging.context_storage[:shoryuken_context] = nil
|
|
76
|
+
end
|
|
77
|
+
|
|
78
|
+
it 'formats log messages without timestamp' do
|
|
79
|
+
allow(formatter).to receive(:tid).and_return('xyz789')
|
|
80
|
+
Shoryuken::Logging.context_storage[:shoryuken_context] = nil
|
|
81
|
+
|
|
82
|
+
result = formatter.call('DEBUG', Time.now, 'program', 'debug message')
|
|
83
|
+
expect(result).to eq("pid=#{Process.pid} tid=xyz789 DEBUG: debug message\n")
|
|
84
|
+
end
|
|
85
|
+
|
|
86
|
+
it 'includes context when present' do
|
|
87
|
+
allow(formatter).to receive(:tid).and_return('xyz789')
|
|
88
|
+
Shoryuken::Logging.context_storage[:shoryuken_context] = 'queue-processor'
|
|
89
|
+
|
|
90
|
+
result = formatter.call('WARN', Time.now, 'program', 'warning message')
|
|
91
|
+
expect(result).to eq("pid=#{Process.pid} tid=xyz789 queue-processor WARN: warning message\n")
|
|
92
|
+
end
|
|
93
|
+
end
|
|
94
|
+
end
|
|
95
|
+
|
|
96
|
+
describe '.with_context' do
|
|
97
|
+
after do
|
|
98
|
+
described_class.context_storage[:shoryuken_context] = nil
|
|
99
|
+
end
|
|
100
|
+
|
|
101
|
+
it 'sets context for the duration of the block' do
|
|
102
|
+
described_class.with_context('test_context') do
|
|
103
|
+
expect(described_class.current_context).to eq('test_context')
|
|
104
|
+
end
|
|
105
|
+
end
|
|
106
|
+
|
|
107
|
+
it 'clears context after the block completes' do
|
|
108
|
+
described_class.with_context('test_context') do
|
|
109
|
+
# context is set
|
|
110
|
+
end
|
|
111
|
+
expect(described_class.current_context).to be_nil
|
|
112
|
+
end
|
|
113
|
+
|
|
114
|
+
it 'clears context even when an exception is raised' do
|
|
115
|
+
expect do
|
|
116
|
+
described_class.with_context('test_context') do
|
|
117
|
+
raise StandardError, 'test error'
|
|
118
|
+
end
|
|
119
|
+
end.to raise_error(StandardError, 'test error')
|
|
120
|
+
|
|
121
|
+
expect(described_class.current_context).to be_nil
|
|
122
|
+
end
|
|
123
|
+
|
|
124
|
+
it 'returns the value of the block' do
|
|
125
|
+
result = described_class.with_context('test_context') do
|
|
126
|
+
'block_result'
|
|
127
|
+
end
|
|
128
|
+
expect(result).to eq('block_result')
|
|
129
|
+
end
|
|
130
|
+
|
|
131
|
+
it 'preserves outer context in nested calls' do
|
|
132
|
+
described_class.with_context('outer') do
|
|
133
|
+
expect(described_class.current_context).to eq('outer')
|
|
134
|
+
|
|
135
|
+
described_class.with_context('inner') do
|
|
136
|
+
expect(described_class.current_context).to eq('inner')
|
|
137
|
+
end
|
|
138
|
+
|
|
139
|
+
expect(described_class.current_context).to eq('outer')
|
|
140
|
+
end
|
|
141
|
+
expect(described_class.current_context).to be_nil
|
|
142
|
+
end
|
|
143
|
+
|
|
144
|
+
it 'restores outer context even when inner block raises' do
|
|
145
|
+
described_class.with_context('outer') do
|
|
146
|
+
expect do
|
|
147
|
+
described_class.with_context('inner') do
|
|
148
|
+
raise StandardError, 'inner error'
|
|
149
|
+
end
|
|
150
|
+
end.to raise_error(StandardError, 'inner error')
|
|
151
|
+
|
|
152
|
+
expect(described_class.current_context).to eq('outer')
|
|
153
|
+
end
|
|
154
|
+
end
|
|
155
|
+
end
|
|
156
|
+
|
|
157
|
+
describe '.current_context' do
|
|
158
|
+
after do
|
|
159
|
+
described_class.context_storage[:shoryuken_context] = nil
|
|
160
|
+
end
|
|
161
|
+
|
|
162
|
+
it 'returns nil when no context is set' do
|
|
163
|
+
expect(described_class.current_context).to be_nil
|
|
164
|
+
end
|
|
165
|
+
|
|
166
|
+
it 'returns the current context value' do
|
|
167
|
+
described_class.context_storage[:shoryuken_context] = 'test_value'
|
|
168
|
+
expect(described_class.current_context).to eq('test_value')
|
|
169
|
+
end
|
|
170
|
+
end
|
|
171
|
+
|
|
172
|
+
describe '.context_storage' do
|
|
173
|
+
it 'returns Fiber for fiber-local storage' do
|
|
174
|
+
expect(described_class.context_storage).to eq(Fiber)
|
|
175
|
+
end
|
|
176
|
+
end
|
|
177
|
+
|
|
178
|
+
describe '.initialize_logger' do
|
|
179
|
+
it 'creates a new Logger instance' do
|
|
180
|
+
logger = described_class.initialize_logger
|
|
181
|
+
expect(logger).to be_a(Logger)
|
|
182
|
+
end
|
|
183
|
+
|
|
184
|
+
it 'sets default log level to INFO' do
|
|
185
|
+
logger = described_class.initialize_logger
|
|
186
|
+
expect(logger.level).to eq(Logger::INFO)
|
|
187
|
+
end
|
|
188
|
+
|
|
189
|
+
it 'uses Pretty formatter by default' do
|
|
190
|
+
logger = described_class.initialize_logger
|
|
191
|
+
expect(logger.formatter).to be_a(Shoryuken::Logging::Pretty)
|
|
192
|
+
end
|
|
193
|
+
|
|
194
|
+
it 'accepts custom log target' do
|
|
195
|
+
log_target = StringIO.new
|
|
196
|
+
logger = described_class.initialize_logger(log_target)
|
|
197
|
+
expect(logger.instance_variable_get(:@logdev).dev).to eq(log_target)
|
|
198
|
+
end
|
|
199
|
+
end
|
|
200
|
+
|
|
201
|
+
describe '.logger' do
|
|
202
|
+
after do
|
|
203
|
+
# Reset the instance variable to avoid affecting other tests
|
|
204
|
+
described_class.instance_variable_set(:@logger, nil)
|
|
205
|
+
end
|
|
206
|
+
|
|
207
|
+
it 'returns a logger instance' do
|
|
208
|
+
expect(described_class.logger).to be_a(Logger)
|
|
209
|
+
end
|
|
210
|
+
|
|
211
|
+
it 'memoizes the logger instance' do
|
|
212
|
+
logger1 = described_class.logger
|
|
213
|
+
logger2 = described_class.logger
|
|
214
|
+
expect(logger1).to be(logger2)
|
|
215
|
+
end
|
|
216
|
+
|
|
217
|
+
it 'initializes logger if not already set' do
|
|
218
|
+
expect(described_class).to receive(:initialize_logger).and_call_original
|
|
219
|
+
described_class.logger
|
|
220
|
+
end
|
|
221
|
+
end
|
|
222
|
+
|
|
223
|
+
describe '.logger=' do
|
|
224
|
+
after do
|
|
225
|
+
# Reset the instance variable to avoid affecting other tests
|
|
226
|
+
described_class.instance_variable_set(:@logger, nil)
|
|
227
|
+
end
|
|
228
|
+
|
|
229
|
+
it 'sets the logger instance' do
|
|
230
|
+
custom_logger = Logger.new('/dev/null')
|
|
231
|
+
described_class.logger = custom_logger
|
|
232
|
+
expect(described_class.logger).to be(custom_logger)
|
|
233
|
+
end
|
|
234
|
+
|
|
235
|
+
it 'sets null logger when passed nil' do
|
|
236
|
+
described_class.logger = nil
|
|
237
|
+
logger = described_class.logger
|
|
238
|
+
# The logger should be configured to output to /dev/null
|
|
239
|
+
expect(logger).to be_a(Logger)
|
|
240
|
+
end
|
|
241
|
+
end
|
|
242
|
+
end
|
|
@@ -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
|