bunny-mock 1.5.0 → 1.6.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/.rubocop.yml +1 -1
- data/CHANGELOG.md +6 -1
- data/bunny-mock.gemspec +3 -1
- data/lib/bunny-mock.rb +1 -0
- data/lib/bunny_mock/channel.rb +45 -3
- data/lib/bunny_mock/exchanges/topic.rb +2 -2
- data/lib/bunny_mock/get_response.rb +11 -1
- data/lib/bunny_mock/queue.rb +29 -5
- data/lib/bunny_mock/session.rb +2 -2
- data/lib/bunny_mock/version.rb +1 -1
- data/spec/integration/message_acknowledgement_spec.rb +87 -0
- data/spec/unit/bunny_mock/queue_spec.rb +33 -0
- metadata +37 -7
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 884e4998df1d5701f0384a2cb1c009d80ed81b67
|
4
|
+
data.tar.gz: 198414bf1eabff599a44310c514cbfa9ce770b0d
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 868c41d0849c0ea46f2049b15e6ee9122d662f4953089baefc7e35efc37114baff7ea09c6f66bb322a03252820513115526f27e40e2c71c30f312a9526c34e7a
|
7
|
+
data.tar.gz: ee9f02bc843c883b4df2780a3373219bf283d9780219eccea3edc6e7bcfebca177c53806f6a379dba35469c22d2dfc1dc9dec7a2d8462eb8fbad34cc99d69f26
|
data/.rubocop.yml
CHANGED
data/CHANGELOG.md
CHANGED
@@ -2,9 +2,14 @@
|
|
2
2
|
|
3
3
|
_Add contribution here_
|
4
4
|
|
5
|
+
## v1.6.0
|
6
|
+
|
7
|
+
* [#27](https://github.com/arempe93/bunny-mock/pull/27): Allow `Session#create_channel` to accept additional args - [@eebs](https://github.com/eebs)
|
8
|
+
* [#20](https://github.com/arempe93/bunny-mock/pull/30): Adds implementation of `ack` and `nack` - [@dwhenry](https://github.com/dwhenry)
|
9
|
+
|
5
10
|
## v1.5.0
|
6
11
|
|
7
|
-
*[#20](https://github.com/arempe93/bunny-mock/pull/20): Adds implementation of `Queue#subscribe` - [@baelter](https://github.com/baelter)
|
12
|
+
* [#20](https://github.com/arempe93/bunny-mock/pull/20): Adds implementation of `Queue#subscribe` - [@baelter](https://github.com/baelter)
|
8
13
|
|
9
14
|
## v1.4.0
|
10
15
|
|
data/bunny-mock.gemspec
CHANGED
@@ -26,9 +26,11 @@ Gem::Specification.new do |s|
|
|
26
26
|
end
|
27
27
|
|
28
28
|
s.add_development_dependency 'rake', '~> 10.5.0'
|
29
|
-
s.add_development_dependency 'rubocop'
|
29
|
+
s.add_development_dependency 'rubocop', '= 0.40.0'
|
30
30
|
s.add_development_dependency 'yard'
|
31
31
|
s.add_development_dependency 'rspec', '~> 3.4.0'
|
32
|
+
s.add_development_dependency 'term-ansicolor', '~> 1.3.0'
|
33
|
+
s.add_development_dependency 'tins', '= 1.6.0'
|
32
34
|
s.add_development_dependency 'coveralls'
|
33
35
|
|
34
36
|
s.files = `git ls-files`.split "\n"
|
data/lib/bunny-mock.rb
CHANGED
data/lib/bunny_mock/channel.rb
CHANGED
@@ -14,6 +14,9 @@ module BunnyMock
|
|
14
14
|
# @return [Symbol] Current channel state
|
15
15
|
attr_reader :status
|
16
16
|
|
17
|
+
# @return [Hash] with details of pending, acked and nacked messaged
|
18
|
+
attr_reader :acknowledged_state
|
19
|
+
|
17
20
|
##
|
18
21
|
# Create a new {BunnyMock::Channel} instance
|
19
22
|
#
|
@@ -32,6 +35,7 @@ module BunnyMock
|
|
32
35
|
# initialize exchange and queue storage
|
33
36
|
@exchanges = {}
|
34
37
|
@queues = {}
|
38
|
+
@acknowledged_state = { pending: {}, acked: {}, nacked: {} }
|
35
39
|
|
36
40
|
# set status to opening
|
37
41
|
@status = :opening
|
@@ -256,13 +260,46 @@ module BunnyMock
|
|
256
260
|
end
|
257
261
|
|
258
262
|
##
|
259
|
-
#
|
263
|
+
# Acknowledge message.
|
264
|
+
#
|
265
|
+
# @param [Integer] delivery_tag Delivery tag to acknowledge
|
266
|
+
# @param [Boolean] multiple (false) Should all unacknowledged messages up to this be acknowleded as well?
|
260
267
|
#
|
261
268
|
# @return nil
|
262
269
|
# @api public
|
263
270
|
#
|
264
|
-
def
|
265
|
-
|
271
|
+
def ack(delivery_tag, multiple = false)
|
272
|
+
if multiple
|
273
|
+
@acknowledged_state[:pending].keys.each do |key|
|
274
|
+
ack(key, false) if key <= delivery_tag
|
275
|
+
end
|
276
|
+
elsif @acknowledged_state[:pending].key?(delivery_tag)
|
277
|
+
update_acknowledgement_state(delivery_tag, :acked)
|
278
|
+
end
|
279
|
+
nil
|
280
|
+
end
|
281
|
+
alias acknowledge ack
|
282
|
+
|
283
|
+
##
|
284
|
+
# Unacknowledge message.
|
285
|
+
#
|
286
|
+
# @param [Integer] delivery_tag Delivery tag to acknowledge
|
287
|
+
# @param [Boolean] multiple (false) Should all unacknowledged messages up to this be rejected as well?
|
288
|
+
# @param [Boolean] requeue (false) Should this message be requeued instead of dropping it?
|
289
|
+
#
|
290
|
+
# @return nil
|
291
|
+
# @api public
|
292
|
+
#
|
293
|
+
def nack(delivery_tag, multiple = false, requeue = false)
|
294
|
+
if multiple
|
295
|
+
@acknowledged_state[:pending].keys.each do |key|
|
296
|
+
nack(key, false, requeue) if key <= delivery_tag
|
297
|
+
end
|
298
|
+
elsif @acknowledged_state[:pending].key?(delivery_tag)
|
299
|
+
delivery, header, body = update_acknowledgement_state(delivery_tag, :nacked)
|
300
|
+
delivery.queue.publish(body, header.to_hash) if requeue
|
301
|
+
end
|
302
|
+
nil
|
266
303
|
end
|
267
304
|
|
268
305
|
##
|
@@ -345,5 +382,10 @@ module BunnyMock
|
|
345
382
|
def xchg_find_or_create(name, opts = {})
|
346
383
|
@connection.find_exchange(name) || Exchange.declare(self, name, opts)
|
347
384
|
end
|
385
|
+
|
386
|
+
# @private
|
387
|
+
def update_acknowledgement_state(delivery_tag, new_state)
|
388
|
+
@acknowledged_state[new_state][delivery_tag] = @acknowledged_state[:pending].delete(delivery_tag)
|
389
|
+
end
|
348
390
|
end
|
349
391
|
end
|
@@ -4,11 +4,11 @@ module BunnyMock
|
|
4
4
|
class Topic < BunnyMock::Exchange
|
5
5
|
# @private
|
6
6
|
# @return [String] Multiple subdomain wildcard
|
7
|
-
MULTI_WILDCARD = '#'
|
7
|
+
MULTI_WILDCARD = '#'.freeze
|
8
8
|
|
9
9
|
# @private
|
10
10
|
# @return [String] Single subdomain wildcard
|
11
|
-
SINGLE_WILDCARD = '*'
|
11
|
+
SINGLE_WILDCARD = '*'.freeze
|
12
12
|
|
13
13
|
#
|
14
14
|
# API
|
@@ -14,11 +14,15 @@ module BunnyMock
|
|
14
14
|
# @return [BunnyMock::Channel] Channel the response is from
|
15
15
|
attr_reader :channel
|
16
16
|
|
17
|
+
# @return [BunnyMock::Queue] Queue the response is from
|
18
|
+
attr_reader :queue
|
19
|
+
|
17
20
|
# @private
|
18
21
|
def initialize(channel, queue, opts = {})
|
19
22
|
@channel = channel
|
23
|
+
@queue = queue
|
20
24
|
@hash = {
|
21
|
-
delivery_tag:
|
25
|
+
delivery_tag: self.class.next_delivery_tag,
|
22
26
|
redelivered: false,
|
23
27
|
exchange: opts.fetch(:exchange, ''),
|
24
28
|
routing_key: opts.fetch(:routing_key, queue.name)
|
@@ -72,5 +76,11 @@ module BunnyMock
|
|
72
76
|
def routing_key
|
73
77
|
@hash[:routing_key]
|
74
78
|
end
|
79
|
+
|
80
|
+
# @return [Integer] incrementing numerically value to support `#ack` with multiple=true
|
81
|
+
def self.next_delivery_tag
|
82
|
+
@delivery_tag ||= 0
|
83
|
+
@delivery_tag += 1
|
84
|
+
end
|
75
85
|
end
|
76
86
|
end
|
data/lib/bunny_mock/queue.rb
CHANGED
@@ -72,14 +72,30 @@ module BunnyMock
|
|
72
72
|
##
|
73
73
|
# Adds a consumer to the queue (subscribes for message deliveries).
|
74
74
|
#
|
75
|
-
#
|
76
|
-
# to the queue
|
75
|
+
# Params are so they can be used when the message is processed. Takes a block which is called when a message
|
76
|
+
# is delivered to the queue
|
77
77
|
#
|
78
78
|
# @api public
|
79
79
|
#
|
80
|
-
def subscribe(*
|
80
|
+
def subscribe(*args, &block)
|
81
81
|
@consumers ||= []
|
82
|
-
@consumers << block
|
82
|
+
@consumers << [block, args]
|
83
|
+
yield_consumers
|
84
|
+
|
85
|
+
self
|
86
|
+
end
|
87
|
+
|
88
|
+
##
|
89
|
+
# Adds a specific consumer object to the queue (subscribes for message deliveries).
|
90
|
+
#
|
91
|
+
# @param [#call] consumer A subclass of Bunny::Consumer or any callable object
|
92
|
+
# Secondary params are so they can be used when the message is processed.
|
93
|
+
#
|
94
|
+
# @api public
|
95
|
+
#
|
96
|
+
def subscribe_with(consumer, *args)
|
97
|
+
@consumers ||= []
|
98
|
+
@consumers << [consumer, args]
|
83
99
|
yield_consumers
|
84
100
|
|
85
101
|
self
|
@@ -241,13 +257,21 @@ module BunnyMock
|
|
241
257
|
# @private
|
242
258
|
def yield_consumers
|
243
259
|
return if @consumers.nil?
|
244
|
-
@consumers.each do |c|
|
260
|
+
@consumers.each do |c, args|
|
245
261
|
# rubocop:disable AssignmentInCondition
|
246
262
|
while message = all.pop
|
247
263
|
response = pop_response(message)
|
264
|
+
store_acknowledgement(response, args)
|
248
265
|
c.call(response)
|
249
266
|
end
|
250
267
|
end
|
251
268
|
end
|
269
|
+
|
270
|
+
def store_acknowledgement(response, args)
|
271
|
+
if args[0].is_a?(Hash) && args[0][:manual_ack]
|
272
|
+
delivery_tag = response[0][:delivery_tag]
|
273
|
+
@channel.acknowledged_state[:pending][delivery_tag] = response
|
274
|
+
end
|
275
|
+
end
|
252
276
|
end
|
253
277
|
end
|
data/lib/bunny_mock/session.rb
CHANGED
@@ -88,9 +88,9 @@ module BunnyMock
|
|
88
88
|
#
|
89
89
|
# @return [BunnyMock::Channel] Channel instance
|
90
90
|
# @api public
|
91
|
-
def create_channel(n = nil, _pool_size = 1)
|
91
|
+
def create_channel(n = nil, _pool_size = 1, *_args)
|
92
92
|
# raise same error as {Bunny::Session#create_channel}
|
93
|
-
raise ArgumentError, 'channel number 0 is reserved in the protocol and cannot be used' if n
|
93
|
+
raise ArgumentError, 'channel number 0 is reserved in the protocol and cannot be used' if n && n.zero?
|
94
94
|
|
95
95
|
# return cached channel if exists
|
96
96
|
return @channels[n] if n && @channels.key?(n)
|
data/lib/bunny_mock/version.rb
CHANGED
@@ -0,0 +1,87 @@
|
|
1
|
+
describe BunnyMock::Channel, 'acknowledgement' do
|
2
|
+
let(:queue) { @channel.queue('test.q') }
|
3
|
+
let(:delivery_tags) { {} }
|
4
|
+
|
5
|
+
context 'when `manual_ack` = true' do
|
6
|
+
before do
|
7
|
+
queue.subscribe(manual_ack: true) do |delivery, _headers, body|
|
8
|
+
delivery_tags[body] = delivery[:delivery_tag]
|
9
|
+
end
|
10
|
+
queue.publish 'Another message on the queue'
|
11
|
+
end
|
12
|
+
|
13
|
+
it 'should allow messages which have been acked to be identified' do
|
14
|
+
queue.publish 'Message to acknowledge'
|
15
|
+
delivery_tag = delivery_tags['Message to acknowledge']
|
16
|
+
@channel.ack delivery_tag
|
17
|
+
|
18
|
+
expect(@channel.acknowledged_state[:pending]).not_to include(delivery_tag)
|
19
|
+
expect(@channel.acknowledged_state[:acked]).to include(delivery_tag)
|
20
|
+
end
|
21
|
+
|
22
|
+
it 'should allow messages which have not been acked to be identified' do
|
23
|
+
queue.publish 'Message without acknowledgement'
|
24
|
+
delivery_tag = delivery_tags['Message without acknowledgement']
|
25
|
+
|
26
|
+
expect(@channel.acknowledged_state[:pending]).to include(delivery_tag)
|
27
|
+
expect(@channel.acknowledged_state[:acked]).not_to include(delivery_tag)
|
28
|
+
end
|
29
|
+
|
30
|
+
it 'should allow messages which have been nacked to be identified' do
|
31
|
+
queue.publish 'Message to nack'
|
32
|
+
delivery_tag = delivery_tags['Message to nack']
|
33
|
+
@channel.nack delivery_tag
|
34
|
+
|
35
|
+
expect(@channel.acknowledged_state[:pending]).not_to include(delivery_tag)
|
36
|
+
expect(@channel.acknowledged_state[:acked]).not_to include(delivery_tag)
|
37
|
+
expect(@channel.acknowledged_state[:nacked]).to include(delivery_tag)
|
38
|
+
end
|
39
|
+
|
40
|
+
it 'should requeue messages with have been nacked with `requeue` = true' do
|
41
|
+
queue.publish 'Message to nack'
|
42
|
+
delivery_tag = delivery_tags['Message to nack']
|
43
|
+
@channel.nack delivery_tag, false, true
|
44
|
+
new_delivery_tag = delivery_tags['Message to nack']
|
45
|
+
|
46
|
+
expect(@channel.acknowledged_state[:pending]).not_to include(delivery_tag)
|
47
|
+
expect(@channel.acknowledged_state[:acked]).not_to include(delivery_tag)
|
48
|
+
expect(@channel.acknowledged_state[:nacked]).to include(delivery_tag)
|
49
|
+
|
50
|
+
expect(@channel.acknowledged_state[:pending]).to include(new_delivery_tag)
|
51
|
+
end
|
52
|
+
|
53
|
+
it 'should allow multiple messages to be acked when `multiple` = true' do
|
54
|
+
queue.publish 'Message to be automatically acked'
|
55
|
+
delivery_tag_1 = delivery_tags['Message to be automatically acked']
|
56
|
+
queue.publish 'Message to acked'
|
57
|
+
delivery_tag_2 = delivery_tags['Message to acked']
|
58
|
+
queue.publish 'Message to be left as pending'
|
59
|
+
delivery_tag_3 = delivery_tags['Message to be left as pending']
|
60
|
+
|
61
|
+
@channel.ack delivery_tag_2, true
|
62
|
+
|
63
|
+
expect(@channel.acknowledged_state[:pending]).not_to include(delivery_tag_1)
|
64
|
+
expect(@channel.acknowledged_state[:pending]).not_to include(delivery_tag_2)
|
65
|
+
expect(@channel.acknowledged_state[:pending]).to include(delivery_tag_3)
|
66
|
+
expect(@channel.acknowledged_state[:acked]).to include(delivery_tag_1)
|
67
|
+
expect(@channel.acknowledged_state[:acked]).to include(delivery_tag_2)
|
68
|
+
end
|
69
|
+
|
70
|
+
it 'should allow multiple messages to be nacked when `multiple` = true' do
|
71
|
+
queue.publish 'Message to be automatically nacked'
|
72
|
+
delivery_tag_1 = delivery_tags['Message to be automatically nacked']
|
73
|
+
queue.publish 'Message to nacked'
|
74
|
+
delivery_tag_2 = delivery_tags['Message to nacked']
|
75
|
+
queue.publish 'Message to be left as pending'
|
76
|
+
delivery_tag_3 = delivery_tags['Message to be left as pending']
|
77
|
+
|
78
|
+
@channel.nack delivery_tag_2, true
|
79
|
+
|
80
|
+
expect(@channel.acknowledged_state[:pending]).not_to include(delivery_tag_1)
|
81
|
+
expect(@channel.acknowledged_state[:pending]).not_to include(delivery_tag_2)
|
82
|
+
expect(@channel.acknowledged_state[:pending]).to include(delivery_tag_3)
|
83
|
+
expect(@channel.acknowledged_state[:nacked]).to include(delivery_tag_1)
|
84
|
+
expect(@channel.acknowledged_state[:nacked]).to include(delivery_tag_2)
|
85
|
+
end
|
86
|
+
end
|
87
|
+
end
|
@@ -172,6 +172,39 @@ describe BunnyMock::Queue do
|
|
172
172
|
end
|
173
173
|
@queue.publish 'test'
|
174
174
|
end
|
175
|
+
|
176
|
+
it 'should create responses with uniq delivery_tags' do
|
177
|
+
delivery_tags = []
|
178
|
+
@queue.subscribe do |delivery, _headers, _body|
|
179
|
+
delivery_tags << delivery[:delivery_tag]
|
180
|
+
end
|
181
|
+
@queue.publish 'test one'
|
182
|
+
@queue.publish 'test two'
|
183
|
+
|
184
|
+
expect(delivery_tags.uniq.count).to eq(2)
|
185
|
+
end
|
186
|
+
|
187
|
+
context 'when `manual_ack` is set to true' do
|
188
|
+
|
189
|
+
it 'should mark the message as unacknowledged on the channel before processing' do
|
190
|
+
@queue.subscribe(manual_ack: true) do |delivery, _headers, _body|
|
191
|
+
expect(@channel.acknowledged_state[:pending]).to include(delivery[:delivery_tag])
|
192
|
+
end
|
193
|
+
@queue.publish 'test'
|
194
|
+
end
|
195
|
+
end
|
196
|
+
end
|
197
|
+
|
198
|
+
context '#subscribe_with' do
|
199
|
+
|
200
|
+
it 'should consume messages delivered' do
|
201
|
+
consumer = proc do |_delivery, _headers, body|
|
202
|
+
expect(body).to eq('test')
|
203
|
+
end
|
204
|
+
|
205
|
+
@queue.subscribe_with consumer
|
206
|
+
@queue.publish 'test'
|
207
|
+
end
|
175
208
|
end
|
176
209
|
|
177
210
|
context '#purge' do
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: bunny-mock
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 1.
|
4
|
+
version: 1.6.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Andrew Rempe
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date:
|
11
|
+
date: 2017-04-10 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: bunny
|
@@ -42,16 +42,16 @@ dependencies:
|
|
42
42
|
name: rubocop
|
43
43
|
requirement: !ruby/object:Gem::Requirement
|
44
44
|
requirements:
|
45
|
-
- -
|
45
|
+
- - '='
|
46
46
|
- !ruby/object:Gem::Version
|
47
|
-
version:
|
47
|
+
version: 0.40.0
|
48
48
|
type: :development
|
49
49
|
prerelease: false
|
50
50
|
version_requirements: !ruby/object:Gem::Requirement
|
51
51
|
requirements:
|
52
|
-
- -
|
52
|
+
- - '='
|
53
53
|
- !ruby/object:Gem::Version
|
54
|
-
version:
|
54
|
+
version: 0.40.0
|
55
55
|
- !ruby/object:Gem::Dependency
|
56
56
|
name: yard
|
57
57
|
requirement: !ruby/object:Gem::Requirement
|
@@ -80,6 +80,34 @@ dependencies:
|
|
80
80
|
- - "~>"
|
81
81
|
- !ruby/object:Gem::Version
|
82
82
|
version: 3.4.0
|
83
|
+
- !ruby/object:Gem::Dependency
|
84
|
+
name: term-ansicolor
|
85
|
+
requirement: !ruby/object:Gem::Requirement
|
86
|
+
requirements:
|
87
|
+
- - "~>"
|
88
|
+
- !ruby/object:Gem::Version
|
89
|
+
version: 1.3.0
|
90
|
+
type: :development
|
91
|
+
prerelease: false
|
92
|
+
version_requirements: !ruby/object:Gem::Requirement
|
93
|
+
requirements:
|
94
|
+
- - "~>"
|
95
|
+
- !ruby/object:Gem::Version
|
96
|
+
version: 1.3.0
|
97
|
+
- !ruby/object:Gem::Dependency
|
98
|
+
name: tins
|
99
|
+
requirement: !ruby/object:Gem::Requirement
|
100
|
+
requirements:
|
101
|
+
- - '='
|
102
|
+
- !ruby/object:Gem::Version
|
103
|
+
version: 1.6.0
|
104
|
+
type: :development
|
105
|
+
prerelease: false
|
106
|
+
version_requirements: !ruby/object:Gem::Requirement
|
107
|
+
requirements:
|
108
|
+
- - '='
|
109
|
+
- !ruby/object:Gem::Version
|
110
|
+
version: 1.6.0
|
83
111
|
- !ruby/object:Gem::Dependency
|
84
112
|
name: coveralls
|
85
113
|
requirement: !ruby/object:Gem::Requirement
|
@@ -128,6 +156,7 @@ files:
|
|
128
156
|
- lib/bunny_mock/queue.rb
|
129
157
|
- lib/bunny_mock/session.rb
|
130
158
|
- lib/bunny_mock/version.rb
|
159
|
+
- spec/integration/message_acknowledgement_spec.rb
|
131
160
|
- spec/integration/queue_pop_spec.rb
|
132
161
|
- spec/integration/queue_subscribe_spec.rb
|
133
162
|
- spec/spec_helper.rb
|
@@ -159,11 +188,12 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
159
188
|
version: '0'
|
160
189
|
requirements: []
|
161
190
|
rubyforge_project:
|
162
|
-
rubygems_version: 2.
|
191
|
+
rubygems_version: 2.6.4
|
163
192
|
signing_key:
|
164
193
|
specification_version: 4
|
165
194
|
summary: Mocking for the popular Bunny client for RabbitMQ
|
166
195
|
test_files:
|
196
|
+
- spec/integration/message_acknowledgement_spec.rb
|
167
197
|
- spec/integration/queue_pop_spec.rb
|
168
198
|
- spec/integration/queue_subscribe_spec.rb
|
169
199
|
- spec/spec_helper.rb
|