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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: c50a06b6ad300048718d1da4576cf4aa26bb3f7a
4
- data.tar.gz: d3a46df9f089b40a09bf763a55dec107f1adde22
3
+ metadata.gz: 884e4998df1d5701f0384a2cb1c009d80ed81b67
4
+ data.tar.gz: 198414bf1eabff599a44310c514cbfa9ce770b0d
5
5
  SHA512:
6
- metadata.gz: a66ab64795f09af460aecb30c702a177caeed40f20603a2680bdccbc8c2307f6c312fb73ec1de039c4a787c21bf671569ae7ecacae9e59143e238d27b174e852
7
- data.tar.gz: fa47402ad74277744d11cbf0c560476ea507379b385d0ab2453f0d5ba1589d4938777244cd9b86a7241d608f47ade3b8535f5aa200217d3e9ef513ca480a0a44
6
+ metadata.gz: 868c41d0849c0ea46f2049b15e6ee9122d662f4953089baefc7e35efc37114baff7ea09c6f66bb322a03252820513115526f27e40e2c71c30f312a9526c34e7a
7
+ data.tar.gz: ee9f02bc843c883b4df2780a3373219bf283d9780219eccea3edc6e7bcfebca177c53806f6a379dba35469c22d2dfc1dc9dec7a2d8462eb8fbad34cc99d69f26
@@ -1,5 +1,5 @@
1
1
  AllCops:
2
- TargetRubyVersion: 2.3
2
+ TargetRubyVersion: 2.0
3
3
  DisplayCopNames: true
4
4
  Include:
5
5
  - 'lib/**/*'
@@ -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
 
@@ -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"
@@ -1,6 +1,7 @@
1
1
  # frozen_string_literal: true
2
2
  require 'bunny_mock/version'
3
3
 
4
+ require 'timeout'
4
5
  require 'bunny/exceptions'
5
6
  require 'amq/protocol/client'
6
7
 
@@ -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
- # Does nothing atm.
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 acknowledge(*)
265
- # noop
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
@@ -72,14 +72,30 @@ module BunnyMock
72
72
  ##
73
73
  # Adds a consumer to the queue (subscribes for message deliveries).
74
74
  #
75
- # All params are ignored atm. Takes a block which is called when a message is delivered
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(*_args, &block)
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
@@ -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 == 0
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)
@@ -3,5 +3,5 @@
3
3
 
4
4
  module BunnyMock
5
5
  # @return [String] Version of the library
6
- VERSION = '1.5.0'
6
+ VERSION = '1.6.0'.freeze
7
7
  end
@@ -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.5.0
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: 2016-06-17 00:00:00.000000000 Z
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: '0'
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: '0'
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.5.1
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