txgh-queue 1.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 +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
|