txgh-queue 1.0.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/lib/txgh-queue/application.rb +35 -0
- data/lib/txgh-queue/backends/null.rb +19 -0
- data/lib/txgh-queue/backends/sqs/config.rb +29 -0
- data/lib/txgh-queue/backends/sqs/consumer.rb +30 -0
- data/lib/txgh-queue/backends/sqs/history_sequence.rb +62 -0
- data/lib/txgh-queue/backends/sqs/job.rb +115 -0
- data/lib/txgh-queue/backends/sqs/message_attributes.rb +33 -0
- data/lib/txgh-queue/backends/sqs/producer.rb +33 -0
- data/lib/txgh-queue/backends/sqs/queue.rb +45 -0
- data/lib/txgh-queue/backends/sqs/retry_logic.rb +118 -0
- data/lib/txgh-queue/backends/sqs.rb +36 -0
- data/lib/txgh-queue/backends.rb +22 -0
- data/lib/txgh-queue/config.rb +48 -0
- data/lib/txgh-queue/error_handlers/github.rb +47 -0
- data/lib/txgh-queue/error_handlers/server_response.rb +22 -0
- data/lib/txgh-queue/error_handlers/standard_errors.rb +15 -0
- data/lib/txgh-queue/error_handlers/transifex.rb +23 -0
- data/lib/txgh-queue/error_handlers/txgh_errors.rb +27 -0
- data/lib/txgh-queue/error_handlers.rb +9 -0
- data/lib/txgh-queue/job.rb +77 -0
- data/lib/txgh-queue/result.rb +26 -0
- data/lib/txgh-queue/status.rb +51 -0
- data/lib/txgh-queue/supervisor.rb +76 -0
- data/lib/txgh-queue/version.rb +3 -0
- data/lib/txgh-queue/webhooks/github/request_handler.rb +34 -0
- data/lib/txgh-queue/webhooks/github.rb +7 -0
- data/lib/txgh-queue/webhooks/transifex/request_handler.rb +21 -0
- data/lib/txgh-queue/webhooks/transifex.rb +7 -0
- data/lib/txgh-queue/webhooks.rb +6 -0
- data/lib/txgh-queue.rb +14 -0
- data/spec/application_spec.rb +97 -0
- data/spec/backends/sqs/config_spec.rb +30 -0
- data/spec/backends/sqs/consumer_spec.rb +34 -0
- data/spec/backends/sqs/history_sequence_spec.rb +75 -0
- data/spec/backends/sqs/job_spec.rb +189 -0
- data/spec/backends/sqs/message_attributes_spec.rb +64 -0
- data/spec/backends/sqs/producer_spec.rb +32 -0
- data/spec/backends/sqs/queue_spec.rb +65 -0
- data/spec/backends/sqs/retry_logic_spec.rb +157 -0
- data/spec/backends/sqs_spec.rb +57 -0
- data/spec/backends_spec.rb +31 -0
- data/spec/config_spec.rb +15 -0
- data/spec/error_handlers/github_spec.rb +30 -0
- data/spec/error_handlers/server_response_spec.rb +36 -0
- data/spec/error_handlers/standard_errors_spec.rb +22 -0
- data/spec/error_handlers/transifex_spec.rb +43 -0
- data/spec/error_handlers/txgh_errors_spec.rb +25 -0
- data/spec/helpers/env_helpers.rb +13 -0
- data/spec/helpers/nil_logger.rb +10 -0
- data/spec/helpers/sqs/sqs_test_message.rb +47 -0
- data/spec/helpers/test_backend.rb +54 -0
- data/spec/job_spec.rb +111 -0
- data/spec/result_spec.rb +63 -0
- data/spec/spec_helper.rb +66 -0
- data/spec/status_spec.rb +68 -0
- data/spec/supervisor_spec.rb +40 -0
- data/spec/webhooks/github/request_handler_spec.rb +145 -0
- data/spec/webhooks/transifex/request_handler_spec.rb +87 -0
- data/txgh-queue.gemspec +24 -0
- metadata +172 -0
@@ -0,0 +1,189 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
require 'helpers/sqs/sqs_test_message'
|
3
|
+
|
4
|
+
include TxghQueue
|
5
|
+
include TxghQueue::Backends
|
6
|
+
|
7
|
+
describe Sqs::Job, auto_configure: true do
|
8
|
+
let(:queue_config) { sqs_queue_config }
|
9
|
+
let(:queue) { Sqs::Config.queues.first }
|
10
|
+
let(:failure_queue) { Sqs::Config.failure_queue }
|
11
|
+
let(:logger) { NilLogger.new }
|
12
|
+
let(:body) { { 'foo' => 'bar' } }
|
13
|
+
let(:message) { SqsTestMessage.new('123abc', body.to_json) }
|
14
|
+
let(:new_message) { SqsTestMessage.new('456def', body.to_json) }
|
15
|
+
let(:job) { described_class.new(message, queue, logger) }
|
16
|
+
|
17
|
+
shared_examples "it updates the message's history sequence" do |status, queue_sym|
|
18
|
+
it 'updates the history sequence with failure details for an exception' do
|
19
|
+
error = StandardError.new('foobar')
|
20
|
+
error.set_backtrace('path/to/file.rb:10')
|
21
|
+
result = Result.new(status, error)
|
22
|
+
expect(job).to receive(:process).with(body).and_return(result)
|
23
|
+
|
24
|
+
# this call to send_message signifies a retry
|
25
|
+
expect(send(queue_sym)).to receive(:send_message) do |body, attributes|
|
26
|
+
message_attributes = Sqs::MessageAttributes.from_h(attributes[:message_attributes])
|
27
|
+
current_retry = message_attributes.history_sequence.current
|
28
|
+
expect(current_retry).to include(
|
29
|
+
response_type: 'error',
|
30
|
+
class: 'StandardError',
|
31
|
+
message: 'foobar',
|
32
|
+
backtrace: 'path/to/file.rb:10'
|
33
|
+
)
|
34
|
+
|
35
|
+
new_message
|
36
|
+
end
|
37
|
+
|
38
|
+
job.complete
|
39
|
+
end
|
40
|
+
|
41
|
+
it 'updates the history sequence with failure details for a txgh response' do
|
42
|
+
response = TxghServer::Response.new(502, 'Bad gateway')
|
43
|
+
result = Result.new(status, response)
|
44
|
+
expect(job).to receive(:process).with(body).and_return(result)
|
45
|
+
|
46
|
+
# this call to send_message signifies a retry
|
47
|
+
expect(send(queue_sym)).to receive(:send_message) do |body, attributes|
|
48
|
+
message_attributes = Sqs::MessageAttributes.from_h(attributes[:message_attributes])
|
49
|
+
current_retry = message_attributes.history_sequence.current
|
50
|
+
expect(current_retry).to include(
|
51
|
+
response_type: 'response',
|
52
|
+
code: 502,
|
53
|
+
body: 'Bad gateway'
|
54
|
+
)
|
55
|
+
|
56
|
+
new_message
|
57
|
+
end
|
58
|
+
|
59
|
+
job.complete
|
60
|
+
end
|
61
|
+
end
|
62
|
+
|
63
|
+
describe '#complete' do
|
64
|
+
it 'processes a single job and deletes the message' do
|
65
|
+
result = Result.new(Status.ok, TxghServer::Response.new(200, 'Ok'))
|
66
|
+
expect(queue).to receive(:delete_message).with(message.receipt_handle)
|
67
|
+
expect(job).to receive(:process).with(body).and_return(result)
|
68
|
+
job.complete
|
69
|
+
end
|
70
|
+
|
71
|
+
context 'error reporting' do
|
72
|
+
let(:error) { StandardError.new('jelly beans') }
|
73
|
+
let(:result) { Result.new(Status.fail, error) }
|
74
|
+
|
75
|
+
before(:each) do
|
76
|
+
expect(job).to receive(:process).and_return(result)
|
77
|
+
expect(queue).to receive(:delete_message).with(message.receipt_handle)
|
78
|
+
end
|
79
|
+
|
80
|
+
it 'reports errors to the event system' do
|
81
|
+
expect(failure_queue).to receive(:send_message)
|
82
|
+
|
83
|
+
expect(Txgh.events).to receive(:publish_error) do |e, params|
|
84
|
+
expect(e).to eq(error)
|
85
|
+
expect(params).to eq(
|
86
|
+
payload: body, message_id: message.message_id, queue: queue.name
|
87
|
+
)
|
88
|
+
end
|
89
|
+
|
90
|
+
job.complete
|
91
|
+
end
|
92
|
+
|
93
|
+
it 'includes error tracking details returned from publishing the event' do
|
94
|
+
expect(Txgh.events).to receive(:publish_error).and_return(foo: 'bar')
|
95
|
+
expect(failure_queue).to receive(:send_message) do |body, attributes|
|
96
|
+
message_attributes = Sqs::MessageAttributes.from_h(attributes[:message_attributes])
|
97
|
+
current_retry = message_attributes.history_sequence.current
|
98
|
+
expect(current_retry).to include(error_tracking: { foo: 'bar' })
|
99
|
+
end
|
100
|
+
|
101
|
+
job.complete
|
102
|
+
end
|
103
|
+
end
|
104
|
+
|
105
|
+
context 'retries' do
|
106
|
+
let(:response) { TxghServer::Response.new(502, 'Bad gateway') }
|
107
|
+
|
108
|
+
before(:each) do
|
109
|
+
# should delete the old message
|
110
|
+
expect(queue).to receive(:delete_message).with(message.receipt_handle)
|
111
|
+
end
|
112
|
+
|
113
|
+
it 're-enqueues the message if told to retry' do
|
114
|
+
response = TxghServer::Response.new(502, 'Bad gateway')
|
115
|
+
result = Result.new(Status.retry_without_delay, response)
|
116
|
+
expect(job).to receive(:process).with(body).and_return(result)
|
117
|
+
|
118
|
+
# this call to send_message signifies a retry
|
119
|
+
expect(queue).to receive(:send_message) do |body, attributes|
|
120
|
+
message_attributes = Sqs::MessageAttributes.from_h(attributes[:message_attributes])
|
121
|
+
history_sequence = message_attributes.history_sequence.sequence.map { |elem| elem[:status] }
|
122
|
+
expect(history_sequence).to eq(%w(retry_without_delay))
|
123
|
+
new_message
|
124
|
+
end
|
125
|
+
|
126
|
+
job.complete
|
127
|
+
end
|
128
|
+
|
129
|
+
it 're-enqueues with delay if told to do so' do
|
130
|
+
response = TxghServer::Response.new(502, 'Bad gateway')
|
131
|
+
result = Result.new(Status.retry_with_delay, response)
|
132
|
+
expect(job).to receive(:process).with(body).and_return(result)
|
133
|
+
|
134
|
+
# this call to send_message signifies a retry
|
135
|
+
expect(queue).to receive(:send_message) do |body, attributes|
|
136
|
+
message_attributes = Sqs::MessageAttributes.from_h(attributes[:message_attributes])
|
137
|
+
history_sequence = message_attributes.history_sequence.sequence.map { |elem| elem[:status] }
|
138
|
+
expect(history_sequence).to eq(%w(retry_with_delay))
|
139
|
+
|
140
|
+
expect(attributes[:delay_seconds]).to eq(
|
141
|
+
Sqs::RetryLogic::DELAY_INTERVALS.first
|
142
|
+
)
|
143
|
+
|
144
|
+
new_message
|
145
|
+
end
|
146
|
+
|
147
|
+
job.complete
|
148
|
+
end
|
149
|
+
|
150
|
+
context 'with all retries exceeded' do
|
151
|
+
let(:message) do
|
152
|
+
SqsTestMessage.new('123abc', body.to_json, {
|
153
|
+
'history_sequence' => {
|
154
|
+
'string_value' => (Sqs::RetryLogic::OVERALL_MAX_RETRIES - 1).times.map do
|
155
|
+
{ status: 'retry_without_delay' }
|
156
|
+
end.to_json
|
157
|
+
}
|
158
|
+
})
|
159
|
+
end
|
160
|
+
|
161
|
+
it 'deletes the message and adds it to the failure queue' do
|
162
|
+
response = TxghServer::Response.new(502, 'Bad gateway')
|
163
|
+
result = Result.new(Status.retry_with_delay, response)
|
164
|
+
expect(job).to receive(:process).with(body).and_return(result)
|
165
|
+
expect(failure_queue).to receive(:send_message).and_return(new_message)
|
166
|
+
job.complete
|
167
|
+
end
|
168
|
+
end
|
169
|
+
|
170
|
+
it_behaves_like "it updates the message's history sequence", Status.retry_without_delay, :queue
|
171
|
+
it_behaves_like "it updates the message's history sequence", Status.retry_with_delay, :queue
|
172
|
+
end
|
173
|
+
|
174
|
+
context 'failures' do
|
175
|
+
before(:each) do
|
176
|
+
expect(queue).to receive(:delete_message).with(message.receipt_handle)
|
177
|
+
end
|
178
|
+
|
179
|
+
it 'sends the message to the failure queue' do
|
180
|
+
result = Result.new(Status.fail, TxghServer::Response.new(500, '💩'))
|
181
|
+
expect(failure_queue).to receive(:send_message).with(body.to_json, anything)
|
182
|
+
expect(job).to receive(:process).with(body).and_return(result)
|
183
|
+
job.complete
|
184
|
+
end
|
185
|
+
|
186
|
+
it_behaves_like "it updates the message's history sequence", Status.fail, :failure_queue
|
187
|
+
end
|
188
|
+
end
|
189
|
+
end
|
@@ -0,0 +1,64 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
require 'helpers/sqs/sqs_test_message'
|
3
|
+
require 'json'
|
4
|
+
|
5
|
+
include TxghQueue
|
6
|
+
include TxghQueue::Backends
|
7
|
+
|
8
|
+
describe Sqs::MessageAttributes do
|
9
|
+
let(:message) { SqsTestMessage.new('abc123', '{}', attributes_hash) }
|
10
|
+
let(:attributes_hash) do
|
11
|
+
{
|
12
|
+
'history_sequence' => {
|
13
|
+
'string_value' => [{
|
14
|
+
'status' => 'retry_without_delay'
|
15
|
+
}].to_json
|
16
|
+
}
|
17
|
+
}
|
18
|
+
end
|
19
|
+
|
20
|
+
describe '.from_message' do
|
21
|
+
it 'extracts the history sequence from the message attributes' do
|
22
|
+
attributes = described_class.from_message(message)
|
23
|
+
expect(attributes.history_sequence).to be_a(Sqs::HistorySequence)
|
24
|
+
expect(attributes.history_sequence.sequence.first[:status]).to(
|
25
|
+
eq('retry_without_delay')
|
26
|
+
)
|
27
|
+
end
|
28
|
+
end
|
29
|
+
|
30
|
+
describe '.from_h' do
|
31
|
+
it 'creates the history sequence from the hash elements' do
|
32
|
+
attributes = described_class.from_h(attributes_hash)
|
33
|
+
expect(attributes.history_sequence).to be_a(Sqs::HistorySequence)
|
34
|
+
expect(attributes.history_sequence.sequence.first[:status]).to(
|
35
|
+
eq('retry_without_delay')
|
36
|
+
)
|
37
|
+
end
|
38
|
+
end
|
39
|
+
|
40
|
+
describe '#to_h' do
|
41
|
+
it 'converts the history sequence into a hash' do
|
42
|
+
attributes = described_class.from_message(message).to_h
|
43
|
+
expect(attributes).to eq(
|
44
|
+
history_sequence: {
|
45
|
+
data_type: 'String',
|
46
|
+
string_value: [
|
47
|
+
status: 'retry_without_delay'
|
48
|
+
].to_json
|
49
|
+
}
|
50
|
+
)
|
51
|
+
end
|
52
|
+
end
|
53
|
+
|
54
|
+
describe '#dup' do
|
55
|
+
it 'duplicates the attributes' do
|
56
|
+
original_attributes = described_class.from_message(message)
|
57
|
+
copied_attributes = original_attributes.dup
|
58
|
+
expect(original_attributes.object_id).to_not eq(copied_attributes.object_id)
|
59
|
+
expect(original_attributes.history_sequence.object_id).to_not(
|
60
|
+
eq(copied_attributes.object_id)
|
61
|
+
)
|
62
|
+
end
|
63
|
+
end
|
64
|
+
end
|
@@ -0,0 +1,32 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
require 'securerandom'
|
3
|
+
|
4
|
+
include TxghQueue::Backends
|
5
|
+
|
6
|
+
describe Sqs::Producer, auto_configure: true do
|
7
|
+
let(:queue_config) { sqs_queue_config }
|
8
|
+
let(:logger) { NilLogger.new }
|
9
|
+
let(:queues) { TxghQueue::Backends::Sqs::Config.queues }
|
10
|
+
let(:producer) { described_class.new(queues, logger) }
|
11
|
+
|
12
|
+
describe '#enqueue' do
|
13
|
+
it 'sends a message to each SQS queue' do
|
14
|
+
payload = { abc: 'def' }
|
15
|
+
message_ids = []
|
16
|
+
|
17
|
+
queues.each do |queue|
|
18
|
+
message_ids << SecureRandom.hex
|
19
|
+
message = double(:Message, message_id: message_ids.last)
|
20
|
+
|
21
|
+
expect(queue).to(
|
22
|
+
receive(:send_message)
|
23
|
+
.with(payload.to_json, foo: 'bar')
|
24
|
+
.and_return(message)
|
25
|
+
)
|
26
|
+
end
|
27
|
+
|
28
|
+
result = producer.enqueue(payload, foo: 'bar')
|
29
|
+
expect(result).to eq(message_ids: message_ids)
|
30
|
+
end
|
31
|
+
end
|
32
|
+
end
|
@@ -0,0 +1,65 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
include TxghQueue::Backends
|
4
|
+
|
5
|
+
describe Sqs::Queue do
|
6
|
+
let(:options) { { name: 'test-queue', region: 'us-east-1', events: %w(a b c) } }
|
7
|
+
let(:queue) { described_class.new(options) }
|
8
|
+
|
9
|
+
describe '#client' do
|
10
|
+
it 'instantiates an AWS SQS client' do
|
11
|
+
expect(queue.client).to be_a(Aws::SQS::Client)
|
12
|
+
end
|
13
|
+
end
|
14
|
+
|
15
|
+
describe '#url' do
|
16
|
+
it 'grabs the queue URL from the SQS client' do
|
17
|
+
queue_url = double(:QueueUrl, queue_url: 'test-queue')
|
18
|
+
|
19
|
+
expect(queue.client).to(
|
20
|
+
receive(:get_queue_url)
|
21
|
+
.with(queue_name: 'test-queue')
|
22
|
+
.and_return(queue_url)
|
23
|
+
)
|
24
|
+
|
25
|
+
queue.url
|
26
|
+
end
|
27
|
+
end
|
28
|
+
|
29
|
+
describe '#receive_message' do
|
30
|
+
it 'proxies to the client' do
|
31
|
+
url = 'test://host'
|
32
|
+
allow(queue).to receive(:url).and_return(url)
|
33
|
+
expect(queue.client).to receive(:receive_message).with(queue_url: url, foo: 'bar')
|
34
|
+
queue.receive_message(foo: 'bar')
|
35
|
+
end
|
36
|
+
end
|
37
|
+
|
38
|
+
describe '#send_message' do
|
39
|
+
it 'proxies to the client' do
|
40
|
+
url = 'test://host'
|
41
|
+
body = 'All your base are belong to us'
|
42
|
+
allow(queue).to receive(:url).and_return(url)
|
43
|
+
|
44
|
+
expect(queue.client).to(
|
45
|
+
receive(:send_message).with(message_body: body, queue_url: url, foo: 'bar')
|
46
|
+
)
|
47
|
+
|
48
|
+
queue.send_message(body, foo: 'bar')
|
49
|
+
end
|
50
|
+
end
|
51
|
+
|
52
|
+
describe '#delete_message' do
|
53
|
+
it 'proxies to the client' do
|
54
|
+
url = 'test://host'
|
55
|
+
receipt_handle = 'abc123'
|
56
|
+
allow(queue).to receive(:url).and_return(url)
|
57
|
+
|
58
|
+
expect(queue.client).to(
|
59
|
+
receive(:delete_message).with(queue_url: url, receipt_handle: receipt_handle)
|
60
|
+
)
|
61
|
+
|
62
|
+
queue.delete_message(receipt_handle)
|
63
|
+
end
|
64
|
+
end
|
65
|
+
end
|
@@ -0,0 +1,157 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
require 'helpers/sqs/sqs_test_message'
|
3
|
+
|
4
|
+
include TxghQueue
|
5
|
+
include TxghQueue::Backends
|
6
|
+
|
7
|
+
describe Sqs::RetryLogic do
|
8
|
+
context 'with overall retries exceeded' do
|
9
|
+
let(:logic) { described_class.new(message_attributes, current_status) }
|
10
|
+
let(:current_status) { Status.retry_without_delay }
|
11
|
+
let(:message) { SqsTestMessage.new('abc123', '{}', message_attributes.to_h) }
|
12
|
+
let(:message_attributes) do
|
13
|
+
Sqs::MessageAttributes.from_h(
|
14
|
+
history_sequence: {
|
15
|
+
string_value: described_class::OVERALL_MAX_RETRIES.times.map do
|
16
|
+
{ status: 'retry_without_delay' }
|
17
|
+
end.to_json
|
18
|
+
}
|
19
|
+
)
|
20
|
+
end
|
21
|
+
|
22
|
+
describe '#retries_exceeded?' do
|
23
|
+
it 'indicates retries have been exceeded' do
|
24
|
+
expect(logic.retries_exceeded?).to eq(true)
|
25
|
+
end
|
26
|
+
end
|
27
|
+
|
28
|
+
describe '#retry?' do
|
29
|
+
it 'indicates another retry should not be attempted' do
|
30
|
+
expect(logic.retry?).to eq(false)
|
31
|
+
end
|
32
|
+
end
|
33
|
+
|
34
|
+
describe '#next_delay_seconds' do
|
35
|
+
it 'raises an error' do
|
36
|
+
expect { logic.next_delay_seconds }.to raise_error(Sqs::RetriesExceededError)
|
37
|
+
end
|
38
|
+
end
|
39
|
+
|
40
|
+
describe '#sqs_retry_params' do
|
41
|
+
it 'raises an error' do
|
42
|
+
expect { logic.sqs_retry_params }.to raise_error(Sqs::RetriesExceededError)
|
43
|
+
end
|
44
|
+
end
|
45
|
+
end
|
46
|
+
|
47
|
+
context 'with a run of no-delay retries' do
|
48
|
+
let(:logic) { described_class.new(message_attributes, current_status) }
|
49
|
+
let(:current_status) { Status.retry_without_delay }
|
50
|
+
let(:message) { SqsTestMessage.new('abc123', '{}', message_attributes.to_h) }
|
51
|
+
let(:message_attributes) do
|
52
|
+
Sqs::MessageAttributes.from_h(
|
53
|
+
history_sequence: {
|
54
|
+
string_value: [
|
55
|
+
{ status: 'retry_without_delay' },
|
56
|
+
{ status: 'retry_without_delay' },
|
57
|
+
{ status: 'retry_without_delay' }
|
58
|
+
].to_json
|
59
|
+
}
|
60
|
+
)
|
61
|
+
end
|
62
|
+
|
63
|
+
describe '#retries_exceeded?' do
|
64
|
+
it 'indicates retries have not been exceeded' do
|
65
|
+
expect(logic.retries_exceeded?).to eq(false)
|
66
|
+
end
|
67
|
+
end
|
68
|
+
|
69
|
+
describe '#retry?' do
|
70
|
+
it 'indicates another retry may be attempted' do
|
71
|
+
expect(logic.retry?).to eq(true)
|
72
|
+
end
|
73
|
+
end
|
74
|
+
|
75
|
+
describe '#next_delay_seconds' do
|
76
|
+
it 'indicates a delay of zero seconds' do
|
77
|
+
expect(logic.next_delay_seconds).to eq(0)
|
78
|
+
end
|
79
|
+
end
|
80
|
+
|
81
|
+
describe '#sqs_retry_params' do
|
82
|
+
it 'contains the correct parameters' do
|
83
|
+
expect(logic.sqs_retry_params[:message_attributes]).to(
|
84
|
+
eq(message_attributes.to_h)
|
85
|
+
)
|
86
|
+
end
|
87
|
+
end
|
88
|
+
|
89
|
+
context 'and a delayed current status' do
|
90
|
+
let(:current_status) { Status.retry_with_delay }
|
91
|
+
|
92
|
+
describe '#next_delay_seconds' do
|
93
|
+
it 'indicates a first-stage delay' do
|
94
|
+
expect(logic.next_delay_seconds).to(
|
95
|
+
eq(described_class::DELAY_INTERVALS.first)
|
96
|
+
)
|
97
|
+
end
|
98
|
+
end
|
99
|
+
end
|
100
|
+
end
|
101
|
+
|
102
|
+
context 'with a run of delayed retries' do
|
103
|
+
let(:logic) { described_class.new(message_attributes, current_status) }
|
104
|
+
let(:current_status) { Status.retry_with_delay }
|
105
|
+
let(:message) { SqsTestMessage.new('abc123', '{}', message_attributes.to_h) }
|
106
|
+
let(:message_attributes) do
|
107
|
+
Sqs::MessageAttributes.from_h(
|
108
|
+
history_sequence: {
|
109
|
+
string_value: [
|
110
|
+
{ status: 'retry_with_delay' },
|
111
|
+
{ status: 'retry_with_delay' },
|
112
|
+
{ status: 'retry_with_delay' }
|
113
|
+
].to_json
|
114
|
+
}
|
115
|
+
)
|
116
|
+
end
|
117
|
+
|
118
|
+
describe '#retries_exceeded?' do
|
119
|
+
it 'indicates retries have not been exceeded' do
|
120
|
+
expect(logic.retries_exceeded?).to eq(false)
|
121
|
+
end
|
122
|
+
end
|
123
|
+
|
124
|
+
describe '#retry?' do
|
125
|
+
it 'indicates another retry may be attempted' do
|
126
|
+
expect(logic.retry?).to eq(true)
|
127
|
+
end
|
128
|
+
end
|
129
|
+
|
130
|
+
describe '#next_delay_seconds' do
|
131
|
+
it 'indicates a third-stage delay' do
|
132
|
+
expect(logic.next_delay_seconds).to eq(
|
133
|
+
described_class::DELAY_INTERVALS[2]
|
134
|
+
)
|
135
|
+
end
|
136
|
+
end
|
137
|
+
|
138
|
+
describe '#sqs_retry_params' do
|
139
|
+
it 'contains the correct parameters, including the delay' do
|
140
|
+
expect(logic.sqs_retry_params).to eq(
|
141
|
+
message_attributes: message_attributes.to_h,
|
142
|
+
delay_seconds: described_class::DELAY_INTERVALS[2]
|
143
|
+
)
|
144
|
+
end
|
145
|
+
end
|
146
|
+
|
147
|
+
context 'and a non-delayed current status' do
|
148
|
+
let(:current_status) { Status.retry_without_delay }
|
149
|
+
|
150
|
+
describe '#next_delay_seconds' do
|
151
|
+
it 'indicates no delay' do
|
152
|
+
expect(logic.next_delay_seconds).to eq(0)
|
153
|
+
end
|
154
|
+
end
|
155
|
+
end
|
156
|
+
end
|
157
|
+
end
|
@@ -0,0 +1,57 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
include TxghQueue::Backends
|
4
|
+
|
5
|
+
describe Sqs, auto_configure: true do
|
6
|
+
let(:queue_config) { sqs_queue_config }
|
7
|
+
|
8
|
+
describe '.producer_for' do
|
9
|
+
it 'looks up the queues for the given event and returns a producer object' do
|
10
|
+
producer = described_class.producer_for('a')
|
11
|
+
expect(producer).to be_a(Sqs::Producer)
|
12
|
+
expect(producer.queues.size).to eq(1)
|
13
|
+
expect(producer.queues.first).to be_a(Sqs::Queue)
|
14
|
+
expect(producer.queues.first.name).to eq('test-queue')
|
15
|
+
end
|
16
|
+
|
17
|
+
it 'looks up multiple events if given an array' do
|
18
|
+
producer = described_class.producer_for(%w(a d))
|
19
|
+
expect(producer.queues.size).to eq(2)
|
20
|
+
expect(producer.queues.map(&:name).sort).to eq(%w(test-queue test-queue-2))
|
21
|
+
end
|
22
|
+
|
23
|
+
it 'dedupes the list of matching queues' do
|
24
|
+
producer = described_class.producer_for(%w(a b c d e f))
|
25
|
+
expect(producer.queues.size).to eq(2)
|
26
|
+
expect(producer.queues.map(&:name).sort).to eq(%w(test-queue test-queue-2))
|
27
|
+
end
|
28
|
+
end
|
29
|
+
|
30
|
+
describe '.consumer_for' do
|
31
|
+
it 'looks up the queue for the given event and returns a consumer object' do
|
32
|
+
consumer = described_class.consumer_for('b')
|
33
|
+
expect(consumer).to be_a(Sqs::Consumer)
|
34
|
+
expect(consumer.queues.size).to eq(1)
|
35
|
+
expect(consumer.queues.first).to be_a(Sqs::Queue)
|
36
|
+
expect(consumer.queues.first.name).to eq('test-queue')
|
37
|
+
end
|
38
|
+
|
39
|
+
it 'handles the case if the event matches multiple queues' do
|
40
|
+
consumer = described_class.consumer_for('c')
|
41
|
+
expect(consumer.queues.size).to eq(2)
|
42
|
+
expect(consumer.queues.map(&:name).sort).to eq(%w(test-queue test-queue-2))
|
43
|
+
end
|
44
|
+
|
45
|
+
it 'looks up multiple events if given an array' do
|
46
|
+
consumer = described_class.consumer_for(%w(a d))
|
47
|
+
expect(consumer.queues.size).to eq(2)
|
48
|
+
expect(consumer.queues.map(&:name).sort).to eq(%w(test-queue test-queue-2))
|
49
|
+
end
|
50
|
+
|
51
|
+
it 'dedupes the list of matching queues' do
|
52
|
+
consumer = described_class.consumer_for(%w(a b c d e f))
|
53
|
+
expect(consumer.queues.size).to eq(2)
|
54
|
+
expect(consumer.queues.map(&:name).sort).to eq(%w(test-queue test-queue-2))
|
55
|
+
end
|
56
|
+
end
|
57
|
+
end
|
@@ -0,0 +1,31 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
include TxghQueue
|
4
|
+
|
5
|
+
describe Backends do
|
6
|
+
describe '.register' do
|
7
|
+
it 'associates the name of a backend with a class' do
|
8
|
+
# use a string just for testing purposes; second argument should be a class
|
9
|
+
described_class.register(:foo, 'BarClass')
|
10
|
+
expect(described_class.all[:foo]).to eq('BarClass')
|
11
|
+
end
|
12
|
+
end
|
13
|
+
|
14
|
+
describe '.get' do
|
15
|
+
it 'retrieves the class for the given name' do
|
16
|
+
described_class.register(:foo, 'BarClass')
|
17
|
+
expect(described_class.get(:foo)).to eq('BarClass')
|
18
|
+
end
|
19
|
+
end
|
20
|
+
|
21
|
+
describe '.all' do
|
22
|
+
it 'returns a hash of all the name/class pairs' do
|
23
|
+
described_class.register(:foo, 'BarClass')
|
24
|
+
described_class.register(:baz, 'BooClass')
|
25
|
+
|
26
|
+
expect(described_class.all).to include(
|
27
|
+
foo: 'BarClass', baz: 'BooClass'
|
28
|
+
)
|
29
|
+
end
|
30
|
+
end
|
31
|
+
end
|
data/spec/config_spec.rb
ADDED
@@ -0,0 +1,15 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe TxghQueue::Config, auto_configure: true do
|
4
|
+
describe '.backend' do
|
5
|
+
it 'identifies the class to use for the queue backend' do
|
6
|
+
expect(described_class.backend).to eq(TxghQueue::TestBackend)
|
7
|
+
end
|
8
|
+
end
|
9
|
+
|
10
|
+
describe '.options' do
|
11
|
+
it 'identifies the backend options' do
|
12
|
+
expect(described_class.options).to eq(queue_config[:options])
|
13
|
+
end
|
14
|
+
end
|
15
|
+
end
|
@@ -0,0 +1,30 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
include TxghQueue
|
4
|
+
|
5
|
+
describe ErrorHandlers::Github do
|
6
|
+
describe '.can_handle?' do
|
7
|
+
it 'can reply to all configured error classes' do
|
8
|
+
described_class::ERROR_CLASSES.keys.each do |klass|
|
9
|
+
expect(described_class.can_handle?(klass.new)).to eq(true)
|
10
|
+
end
|
11
|
+
end
|
12
|
+
|
13
|
+
it "can't reply to unsupported error classes" do
|
14
|
+
expect(described_class.can_handle?(StandardError.new)).to eq(false)
|
15
|
+
end
|
16
|
+
end
|
17
|
+
|
18
|
+
describe '.status_for' do
|
19
|
+
it 'replies to all configured errors correctly' do
|
20
|
+
described_class::ERROR_CLASSES.each_pair do |klass, expected_response|
|
21
|
+
expect(described_class.status_for(klass.new)).to eq(expected_response)
|
22
|
+
end
|
23
|
+
end
|
24
|
+
|
25
|
+
it 'replies to all unconfigured errors with fail' do
|
26
|
+
# i.e. if octokit raises an error we didn't account for
|
27
|
+
expect(described_class.status_for(StandardError.new)).to eq(Status.fail)
|
28
|
+
end
|
29
|
+
end
|
30
|
+
end
|
@@ -0,0 +1,36 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
include TxghQueue
|
4
|
+
|
5
|
+
describe ErrorHandlers::ServerResponse do
|
6
|
+
describe '.can_handle?' do
|
7
|
+
it 'can reply to TxghServer responses' do
|
8
|
+
server_response = TxghServer::Response.new(200, 'Ok')
|
9
|
+
expect(described_class.can_handle?(server_response)).to eq(true)
|
10
|
+
end
|
11
|
+
|
12
|
+
it "can't reply to anything else" do
|
13
|
+
expect(described_class.can_handle?('foo')).to eq(false)
|
14
|
+
end
|
15
|
+
end
|
16
|
+
|
17
|
+
describe '.status_for' do
|
18
|
+
it 'replies with ok if the status code is in the 200 range' do
|
19
|
+
server_response = TxghServer::Response.new(201, 'Created')
|
20
|
+
reply = described_class.status_for(server_response)
|
21
|
+
expect(reply).to eq(Status.ok)
|
22
|
+
end
|
23
|
+
|
24
|
+
it 'replies with fail if the status code is in the 400 range' do
|
25
|
+
server_response = TxghServer::Response.new(404, 'Not found')
|
26
|
+
reply = described_class.status_for(server_response)
|
27
|
+
expect(reply).to eq(Status.fail)
|
28
|
+
end
|
29
|
+
|
30
|
+
it 'replies with fail if the status code is in the 500 range' do
|
31
|
+
server_response = TxghServer::Response.new(502, 'Bad gateway')
|
32
|
+
reply = described_class.status_for(server_response)
|
33
|
+
expect(reply).to eq(Status.fail)
|
34
|
+
end
|
35
|
+
end
|
36
|
+
end
|