freddy-jruby 0.4.3

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,25 @@
1
+ require 'spec_helper'
2
+
3
+ describe Freddy::Consumer do
4
+ let(:freddy) { Freddy.build(logger, config) }
5
+
6
+ let(:destination) { random_destination }
7
+ let(:payload) { {pay: 'load'} }
8
+
9
+ let(:consumer) { freddy.consumer }
10
+
11
+ after { freddy.close }
12
+
13
+ it 'raises exception when no consumer is provided' do
14
+ expect { consumer.consume destination }.to raise_error described_class::EmptyConsumer
15
+ end
16
+
17
+ it "doesn't call passed block without any messages" do
18
+ consumer.consume destination do
19
+ @message_received = true
20
+ end
21
+ default_sleep
22
+
23
+ expect(@message_received).to be_falsy
24
+ end
25
+ end
@@ -0,0 +1,59 @@
1
+ require 'spec_helper'
2
+
3
+ describe Freddy::ErrorResponse do
4
+ subject(:error) { described_class.new(input) }
5
+
6
+ context 'with an error type' do
7
+ let(:input) { {error: 'SomeError'} }
8
+
9
+ describe '#response' do
10
+ subject { error.response }
11
+
12
+ it { should eq(input) }
13
+ end
14
+
15
+ describe '#message' do
16
+ subject { error.message }
17
+
18
+ it 'uses error type as a message' do
19
+ should eq('SomeError')
20
+ end
21
+ end
22
+ end
23
+
24
+ context 'with an error type and message' do
25
+ let(:input) { {error: 'SomeError', message: 'extra info'} }
26
+
27
+ describe '#response' do
28
+ subject { error.response }
29
+
30
+ it { should eq(input) }
31
+ end
32
+
33
+ describe '#message' do
34
+ subject { error.message }
35
+
36
+ it 'uses error type as a message' do
37
+ should eq('SomeError: extra info')
38
+ end
39
+ end
40
+ end
41
+
42
+ context 'without an error type' do
43
+ let(:input) { {something: 'else'} }
44
+
45
+ describe '#response' do
46
+ subject { error.response }
47
+
48
+ it { should eq(input) }
49
+ end
50
+
51
+ describe '#message' do
52
+ subject { error.message }
53
+
54
+ it 'uses default error message as a message' do
55
+ should eq('Use #response to get the error response')
56
+ end
57
+ end
58
+ end
59
+ end
@@ -0,0 +1,225 @@
1
+ require 'spec_helper'
2
+
3
+ describe Freddy do
4
+ let(:freddy) { described_class.build(logger, config) }
5
+
6
+ let(:destination) { random_destination }
7
+ let(:destination2) { random_destination }
8
+ let(:payload) { {pay: 'load'} }
9
+
10
+ after { freddy.close }
11
+
12
+ def respond_to(&block)
13
+ freddy.respond_to(destination, &block)
14
+ end
15
+
16
+ context 'when making a send-and-forget request' do
17
+ context 'with timeout' do
18
+ it 'removes the message from the queue after the timeout' do
19
+ # Assume that there already is a queue. Otherwise will get an early
20
+ # return.
21
+ freddy.channel.queue(destination)
22
+
23
+ freddy.deliver(destination, {}, timeout: 0.1)
24
+ sleep 0.2
25
+
26
+ processed_after_timeout = false
27
+ respond_to { processed_after_timeout = true }
28
+ default_sleep
29
+
30
+ expect(processed_after_timeout).to be(false)
31
+ end
32
+ end
33
+
34
+ context 'without timeout' do
35
+ it 'keeps the message in the queue' do
36
+ # Assume that there already is a queue. Otherwise will get an early
37
+ # return.
38
+ freddy.channel.queue(destination)
39
+
40
+ freddy.deliver(destination, {})
41
+ default_sleep # to ensure everything is properly cleaned
42
+
43
+ processed_after_timeout = false
44
+ respond_to { processed_after_timeout = true }
45
+ default_sleep
46
+
47
+ expect(processed_after_timeout).to be(true)
48
+ end
49
+ end
50
+ end
51
+
52
+ context 'when making a synchronized request' do
53
+ it 'returns response as soon as possible' do
54
+ respond_to { |payload, msg_handler| msg_handler.success(res: 'yey') }
55
+ response = freddy.deliver_with_response(destination, {a: 'b'})
56
+
57
+ expect(response).to eq(res: 'yey')
58
+ end
59
+
60
+ it 'raises an error if the message was errored' do
61
+ respond_to { |payload, msg_handler| msg_handler.error(error: 'not today') }
62
+
63
+ expect {
64
+ freddy.deliver_with_response(destination, payload)
65
+ }.to raise_error(Freddy::InvalidRequestError) {|error|
66
+ expect(error.response).to eq(error: 'not today')
67
+ }
68
+ end
69
+
70
+ it 'does not leak consumers' do
71
+ respond_to { |payload, msg_handler| msg_handler.success(res: 'yey') }
72
+
73
+ old_count = freddy.channel.consumers.keys.count
74
+
75
+ response1 = freddy.deliver_with_response(destination, {a: 'b'})
76
+ response2 = freddy.deliver_with_response(destination, {a: 'b'})
77
+
78
+ expect(response1).to eq(res: 'yey')
79
+ expect(response2).to eq(res: 'yey')
80
+
81
+ new_count = freddy.channel.consumers.keys.count
82
+ expect(new_count).to be(old_count + 1)
83
+ end
84
+
85
+ it 'responds to the correct requester' do
86
+ respond_to { |payload, msg_handler| msg_handler.success(res: 'yey') }
87
+
88
+ response = freddy.deliver_with_response(destination, payload)
89
+ expect(response).to eq(res: 'yey')
90
+
91
+ expect {
92
+ freddy.deliver_with_response(destination2, payload)
93
+ }.to raise_error(Freddy::InvalidRequestError)
94
+ end
95
+
96
+ context 'when queue does not exist' do
97
+ it 'gives a no route error' do
98
+ begin
99
+ Timeout::timeout(0.5) do
100
+ expect {
101
+ freddy.deliver_with_response(destination, {a: 'b'}, timeout: 3)
102
+ }.to raise_error(Freddy::InvalidRequestError) {|error|
103
+ expect(error.response).to eq(error: 'Specified queue does not exist')
104
+ }
105
+ end
106
+ rescue Timeout::Error
107
+ fail('Received a timeout error instead of the no route error')
108
+ end
109
+ end
110
+ end
111
+
112
+ context 'on timeout' do
113
+ it 'gives timeout error' do
114
+ respond_to { |payload, msg_handler| sleep 0.2 }
115
+
116
+ expect {
117
+ freddy.deliver_with_response(destination, {a: 'b'}, timeout: 0.1)
118
+ }.to raise_error(Freddy::TimeoutError) {|error|
119
+ expect(error.response).to eq(error: 'RequestTimeout', message: 'Timed out waiting for response')
120
+ }
121
+ end
122
+
123
+ context 'with delete_on_timeout is set to true' do
124
+ it 'removes the message from the queue' do
125
+ # Assume that there already is a queue. Otherwise will get an early
126
+ # return.
127
+ freddy.channel.queue(destination)
128
+
129
+ expect {
130
+ freddy.deliver_with_response(destination, {}, timeout: 0.1)
131
+ }.to raise_error(Freddy::TimeoutError)
132
+ default_sleep # to ensure everything is properly cleaned
133
+
134
+ processed_after_timeout = false
135
+ respond_to { processed_after_timeout = true }
136
+ default_sleep
137
+
138
+ expect(processed_after_timeout).to be(false)
139
+ end
140
+ end
141
+
142
+ context 'with delete_on_timeout is set to false' do
143
+ it 'removes the message from the queue' do
144
+ # Assume that there already is a queue. Otherwise will get an early
145
+ # return.
146
+ freddy.channel.queue(destination)
147
+
148
+ expect {
149
+ freddy.deliver_with_response(destination, {}, timeout: 0.1, delete_on_timeout: false)
150
+ }.to raise_error(Freddy::TimeoutError)
151
+ default_sleep # to ensure everything is properly cleaned
152
+
153
+ processed_after_timeout = false
154
+ respond_to { processed_after_timeout = true }
155
+ default_sleep
156
+
157
+ expect(processed_after_timeout).to be(true)
158
+ end
159
+ end
160
+ end
161
+ end
162
+
163
+ describe 'when tapping' do
164
+ def tap(custom_destination = destination, &block)
165
+ freddy.tap_into(custom_destination, &block)
166
+ end
167
+
168
+ it 'receives messages' do
169
+ tap {|msg| @tapped_message = msg }
170
+ deliver
171
+
172
+ wait_for { @tapped_message }
173
+ expect(@tapped_message).to eq(payload)
174
+ end
175
+
176
+ it 'has the destination' do
177
+ tap "somebody.*.love" do |message, destination|
178
+ @destination = destination
179
+ end
180
+ deliver "somebody.to.love"
181
+
182
+ wait_for { @destination }
183
+ expect(@destination).to eq("somebody.to.love")
184
+ end
185
+
186
+ it "doesn't consume the message" do
187
+ tap { @tapped = true }
188
+ respond_to { @message_received = true }
189
+
190
+ deliver
191
+
192
+ wait_for { @tapped }
193
+ wait_for { @message_received }
194
+ expect(@tapped).to be(true)
195
+ expect(@message_received).to be(true)
196
+ end
197
+
198
+ it "allows * wildcard" do
199
+ tap("somebody.*.love") { @tapped = true }
200
+
201
+ deliver "somebody.to.love"
202
+
203
+ wait_for { @tapped }
204
+ expect(@tapped).to be(true)
205
+ end
206
+
207
+ it "* matches only one word" do
208
+ tap("somebody.*.love") { @tapped = true }
209
+
210
+ deliver "somebody.not.to.love"
211
+
212
+ default_sleep
213
+ expect(@tapped).to be_falsy
214
+ end
215
+
216
+ it "allows # wildcard" do
217
+ tap("i.#.free") { @tapped = true }
218
+
219
+ deliver "i.want.to.break.free"
220
+
221
+ wait_for { @tapped }
222
+ expect(@tapped).to be(true)
223
+ end
224
+ end
225
+ end
@@ -0,0 +1,27 @@
1
+ require 'spec_helper'
2
+
3
+ describe Freddy::MessageHandler do
4
+ subject(:handler) { described_class.new(adapter, delivery) }
5
+
6
+ let(:adapter) { double }
7
+ let(:delivery) { double(metadata: metadata) }
8
+ let(:metadata) { double(reply_to: reply_to, correlation_id: 'abc') }
9
+
10
+ let(:reply_to) { double }
11
+
12
+ describe '#success' do
13
+ it 'delegates to the adapter' do
14
+ expect(adapter).to receive(:success).with(reply_to, x: 'y')
15
+
16
+ subject.success(x: 'y')
17
+ end
18
+ end
19
+
20
+ describe '#error' do
21
+ it 'delegates to the adapter' do
22
+ expect(adapter).to receive(:error).with(reply_to, error: 'text')
23
+
24
+ subject.error(error: 'text')
25
+ end
26
+ end
27
+ end
@@ -0,0 +1,41 @@
1
+ require 'spec_helper'
2
+
3
+ describe Freddy::Request do
4
+ let(:freddy) { Freddy.build(logger, config) }
5
+
6
+ let(:destination) { random_destination }
7
+ let(:payload) { {pay: 'load'} }
8
+
9
+ let(:request) { freddy.request }
10
+
11
+ after { freddy.close }
12
+
13
+ it 'raises empty responder exception when responding without callback' do
14
+ expect {@responder = request.respond_to destination }.to raise_error described_class::EmptyResponder
15
+ end
16
+
17
+ context 'requesting from multiple threads' do
18
+ let(:nr_of_threads) { 50 }
19
+
20
+ before do
21
+ freddy.respond_to 'thread-queue' do |payload, msg_handler|
22
+ msg_handler.success(payload)
23
+ end
24
+ end
25
+
26
+ it 'handles multiple threads' do
27
+ require 'hamster/experimental/mutable_set'
28
+ msg_counter = Hamster.mutable_set
29
+ nr_of_threads.times.map do |index|
30
+ Thread.new do
31
+ response = freddy.deliver_with_response 'thread-queue', payload
32
+ msg_counter << index
33
+ expect(response).to eq(payload)
34
+ end
35
+ end.each(&:join)
36
+ expect(msg_counter.count).to eq(nr_of_threads)
37
+ end
38
+
39
+ end
40
+
41
+ end
@@ -0,0 +1,22 @@
1
+ require 'spec_helper'
2
+
3
+ describe Freddy::ResponderHandler do
4
+ let(:freddy) { Freddy.build(logger, config) }
5
+
6
+ let(:destination) { random_destination }
7
+ let(:payload) { {pay: 'load'} }
8
+
9
+ after { freddy.close }
10
+
11
+ it 'can cancel listening for messages' do
12
+ consumer_handler = freddy.respond_to destination do
13
+ @messages_count ||= 0
14
+ @messages_count += 1
15
+ end
16
+ deliver
17
+ consumer_handler.cancel
18
+ deliver
19
+
20
+ expect(@messages_count).to eq 1
21
+ end
22
+ end
@@ -0,0 +1,65 @@
1
+ require 'spec_helper'
2
+
3
+ describe 'Concurrency' do
4
+ let(:freddy1) { Freddy.build(logger, config) }
5
+ let(:freddy2) { Freddy.build(logger, config) }
6
+ let(:freddy3) { Freddy.build(logger, config) }
7
+
8
+ after { [freddy1, freddy2, freddy3].each(&:close) }
9
+
10
+ it 'supports multiple requests in #respond_to' do
11
+ freddy1.respond_to 'Concurrency1' do |payload, msg_handler|
12
+ begin
13
+ freddy1.deliver_with_response 'Concurrency2', msg: 'noop'
14
+ result2 = freddy1.deliver_with_response 'Concurrency3', msg: 'noop'
15
+ msg_handler.success(result2)
16
+ rescue Freddy::InvalidRequestError => e
17
+ msg_handler.error(e.response)
18
+ end
19
+ end
20
+
21
+ freddy2.respond_to 'Concurrency2' do |payload, msg_handler|
22
+ begin
23
+ msg_handler.success({from: 'Concurrency2'})
24
+ rescue Freddy::InvalidRequestError => e
25
+ msg_handler.error(e.response)
26
+ end
27
+ end
28
+
29
+ freddy3.respond_to 'Concurrency3' do |payload, msg_handler|
30
+ msg_handler.success({from: 'Concurrency3'})
31
+ end
32
+
33
+ result =
34
+ begin
35
+ freddy1.deliver_with_response 'Concurrency1', msg: 'noop'
36
+ rescue Freddy::InvalidRequestError => e
37
+ e.response
38
+ end
39
+
40
+ expect(result).to eq(from: 'Concurrency3')
41
+ end
42
+
43
+ it 'supports nested calls in #tap_into' do
44
+ received1 = false
45
+ received2 = false
46
+
47
+ freddy1.tap_into 'concurrency.*.queue1' do
48
+ result = freddy1.deliver_with_response 'TapConcurrency', msg: 'noop'
49
+ expect(result).to eq(from: 'TapConcurrency')
50
+ received1 = true
51
+ end
52
+
53
+ freddy2.respond_to 'TapConcurrency' do |payload, msg_handler|
54
+ msg_handler.success({from: 'TapConcurrency'})
55
+ received2 = true
56
+ end
57
+
58
+ freddy1.deliver 'concurrency.q.queue1', msg: 'noop'
59
+
60
+ wait_for { received1 && received2 }
61
+
62
+ expect(received1).to be(true)
63
+ expect(received2).to be(true)
64
+ end
65
+ end