freddy-jruby 0.4.3

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.
@@ -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