bunny-mock 1.0.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: 845c697ca137bf613dc4b11eccaeafdaad4d6cf8
4
+ data.tar.gz: 4b4322cf0a31e2557bf3088fc621e0d0d153087d
5
+ SHA512:
6
+ metadata.gz: 7a7e65dea49c9844023700baf5fb46bd2b69255d2736c0b1dece7189698482bf7d538a411bfca438a82cfcb694e84697f8dae209f0f1295393f602da0a72fb33
7
+ data.tar.gz: 82c071096f87d173b139870b42e555f8ffc0219c1f662a77ef14eb94d6b3b6b1f245a44e237c4934d1316b308b2fe31b27b96dbc563c33ba8caa2bd2b07897f5
@@ -0,0 +1,23 @@
1
+ .DS_Store
2
+ .*.swp
3
+ *.class
4
+ *.rbc
5
+ *.gem
6
+ /doc/
7
+ .yardoc
8
+ .rvmrc
9
+ Gemfile.lock
10
+ .rbx/*
11
+ .tags
12
+ .tags_sorted_by_file
13
+ .Apple*
14
+ bin/*
15
+ .bundle/*
16
+ vendor/*
17
+ playground/*
18
+ *.org
19
+ repl-*
20
+ debug/*
21
+ *.dump
22
+ deploy.docs.sh
23
+ .ruby-version
data/.rspec ADDED
@@ -0,0 +1,3 @@
1
+ --color
2
+ --require spec_helper
3
+ --format=documentation
@@ -0,0 +1,10 @@
1
+ language: ruby
2
+ bundler_args: --without development
3
+ script: bundle exec rspec
4
+ rvm:
5
+ - "2.0.0"
6
+ - "2.1.8"
7
+ - "2.3.0"
8
+ branches:
9
+ only:
10
+ - master
@@ -0,0 +1,7 @@
1
+ --no-private
2
+ --protected
3
+ --markup="markdown" lib/**/*.rb
4
+ --main README.md
5
+ --hide-tag todo
6
+ -
7
+ LICENSE
data/Gemfile ADDED
@@ -0,0 +1,15 @@
1
+ source 'https://rubygems.org'
2
+
3
+ gem 'amq-protocol'
4
+
5
+ group :development do
6
+
7
+ gem 'yard'
8
+ end
9
+
10
+ group :test do
11
+
12
+ gem 'rspec', '~> 3.4.0'
13
+ end
14
+
15
+ gemspec
data/LICENSE ADDED
@@ -0,0 +1,20 @@
1
+ Copyright (c) 2015 Andrew Rempe and contributors.
2
+
3
+ Permission is hereby granted, free of charge, to any person obtaining
4
+ a copy of this software and associated documentation files (the
5
+ "Software"), to deal in the Software without restriction, including
6
+ without limitation the rights to use, copy, modify, merge, publish,
7
+ distribute, sublicense, and/or sell copies of the Software, and to
8
+ permit persons to whom the Software is furnished to do so, subject to
9
+ the following conditions:
10
+
11
+ The above copyright notice and this permission notice shall be
12
+ included in all copies or substantial portions of the Software.
13
+
14
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
15
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
16
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
17
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
18
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
19
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
20
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
@@ -0,0 +1,151 @@
1
+ Bunny Mock
2
+ ==========
3
+
4
+ [![Build Status](https://travis-ci.org/arempe93/bunny-mock.svg?branch=master)](https://travis-ci.org/arempe93/bunny-mock)
5
+
6
+ A mock client for RabbitMQ, modeled after the popular [Bunny client](https://github.com/ruby-amqp/bunny). It currently supports basic usage of Bunny for managing exchanges and queues, with the goal of being able to handle and test all Bunny use cases.
7
+
8
+ ## Usage
9
+
10
+ BunnyMock can be injected into your RabbitMQ application in place of Bunny for testing.
11
+
12
+ ### Example
13
+
14
+ Consider the following example of an RabibtMQ helper module, in Rails
15
+
16
+ ```ruby
17
+ require 'bunny'
18
+
19
+ module AMQP
20
+ module Factory
21
+
22
+ ## Connection, can be mocked for tests
23
+ mattr_accessor :connection
24
+
25
+ ####################################################
26
+ # Connection Management
27
+ ####################################################
28
+
29
+ def self.connect
30
+
31
+ # create bunny rmq client
32
+ @connection = Bunny.new Global.amqp.to_hash
33
+
34
+ # make connection
35
+ @connection.start
36
+
37
+ # return connection
38
+ @connection
39
+ end
40
+
41
+ def self.get_channel
42
+
43
+ # make connection if not connected
44
+ connect unless defined?(@connection) and @connection.open?
45
+
46
+ # get channel
47
+ @connection.channel
48
+ end
49
+
50
+ ...
51
+ end
52
+ end
53
+ ```
54
+
55
+ In this case, to set up your tests, you can simply set `AMQP::Factory.connection = BunnyMock.new.start` to inject the mock library. Then you can use the mock helpers in your tests.
56
+
57
+ ### Publishing
58
+
59
+ ```ruby
60
+ it 'should publish messages to queues' do
61
+
62
+ channel = BunnyMock.new.start.channel
63
+ queue = channel.queue 'queue.test'
64
+
65
+ queue.publish 'Testing message', priority: 5
66
+
67
+ expect(queue.message_count).to eq(1)
68
+
69
+ payload = queue.pop
70
+ expect(queue.message_count).to eq(0)
71
+
72
+ expect(payload[:message]).to eq('Testing message')
73
+ expect(payload[:options][:priority]).to eq(5)
74
+ end
75
+ ```
76
+
77
+ ### Binding
78
+
79
+ ```ruby
80
+ it 'should bind queues to exchanges' do
81
+
82
+ channel = BunnyMock.new.start.channel
83
+
84
+ queue = channel.queue 'queue.test'
85
+ xchg = channel.exchange 'xchg.test'
86
+
87
+ queue.bind xchg
88
+ expect(queue.bound_to?(xchg)).to be_truthy
89
+ expect(xchg.has_binding?(queue)).to be_truthy
90
+
91
+ queue.unbind xchg
92
+ expect(queue.bound_to?(xchg)).to be_falsey
93
+ expect(xchg.has_binding?(queue)).to be_falsey
94
+
95
+ queue.bind 'xchg.test'
96
+ expect(queue.bound_to?(xchg)).to be_truthy
97
+ expect(xchg.has_binding?(queue)).to be_truthy
98
+ end
99
+
100
+ it 'should bind exchanges to exchanges' do
101
+
102
+ channel = BunnyMock.new.start.channel
103
+
104
+ source = channel.exchange 'xchg.source'
105
+ receiver = channel.exchange 'xchg.receiver'
106
+
107
+ receiver.bind source
108
+ expect(receiver.bound_to?(source)).to be_truthy
109
+ expect(source.has_binding?(receiver)).to be_truthy
110
+
111
+ receiver.unbind source
112
+ expect(receiver.bound_to?(source)).to be_falsey
113
+ expect(xchg.has_binding?(receiver)).to be_falsey
114
+
115
+ receiver.bind 'xchg.source'
116
+ expect(receiver.bound_to?(source)).to be_truthy
117
+ expect(source.has_binding?(receiver)).to be_truthy
118
+ end
119
+ ```
120
+
121
+ ## Installation
122
+
123
+ ### With RubyGems
124
+
125
+ To install BunnyMock with RubyGems:
126
+
127
+ ```
128
+ gem install bunny-mock
129
+ ```
130
+
131
+ ### With Bundler
132
+
133
+ To use BunnyMock with a Bundler managed project:
134
+
135
+ ```
136
+ gem 'bunny-mock'
137
+ ```
138
+
139
+ ## Documentation
140
+
141
+ View the documentation on [RubyDoc](http://www.rubydoc.info/github/arempe93/bunny-mock)
142
+
143
+ ## Dependencies
144
+
145
+ * Ruby version >= 2.0
146
+
147
+ * [AMQ Protocol](https://github.com/ruby-amqp/amq-protocol) - Also a dependency of Bunny
148
+
149
+ ## License
150
+
151
+ Released under the MIT license
@@ -0,0 +1,27 @@
1
+ #!/usr/bin/env gem build
2
+ # encoding: utf-8
3
+
4
+ require 'base64'
5
+ require File.expand_path("../lib/bunny_mock/version", __FILE__)
6
+
7
+ Gem::Specification.new do |s|
8
+ s.name = 'bunny-mock'
9
+ s.version = BunnyMock::VERSION.dup
10
+ s.summary = 'Mocking for the popular Bunny client for RabbitMQ'
11
+ s.description = 'Easy to use mocking for testing the Bunny client for RabbitMQ'
12
+ s.license = 'MIT'
13
+ s.required_ruby_version = Gem::Requirement.new '>= 2.0'
14
+
15
+ s.authors = [ 'Andrew Rempe' ]
16
+ s.email = [ Base64.encode64('YW5kcmV3cmVtcGVAZ21haWwuY29t\n') ]
17
+
18
+ # Dependencies
19
+ s.add_dependency 'amq-protocol', '>= 2.0.1'
20
+
21
+ # Files
22
+ s.has_rdoc = true
23
+ s.extra_rdoc_files = [ 'README.md' ]
24
+ s.files = `git ls-files`.split "\n"
25
+ s.test_files = `git ls-files -- spec/*`.split "\n"
26
+ s.require_paths = [ 'lib' ]
27
+ end
@@ -0,0 +1,48 @@
1
+ require 'bunny_mock/version'
2
+ require 'amq/protocol/client'
3
+
4
+ require 'bunny_mock/session'
5
+ require 'bunny_mock/channel'
6
+ require 'bunny_mock/exchange'
7
+ require 'bunny_mock/queue'
8
+
9
+ require 'bunny_mock/exchanges/direct'
10
+ require 'bunny_mock/exchanges/topic'
11
+ require 'bunny_mock/exchanges/fanout'
12
+ require 'bunny_mock/exchanges/headers'
13
+
14
+ ##
15
+ # A mock RabbitMQ client based on Bunny
16
+ #
17
+ # @see https://github.com/ruby-amq/bunny
18
+ #
19
+ module BunnyMock
20
+
21
+ # AMQP protocol version
22
+ PROTOCOL_VERSION = AMQ::Protocol::PROTOCOL_VERSION
23
+
24
+ #
25
+ # API
26
+ #
27
+
28
+ ##
29
+ # Instantiate a new mock Bunny session
30
+ #
31
+ # @return [BunnyMock::Session] Session instance
32
+ # @api public
33
+ def self.new(*args)
34
+
35
+ # return new mock session
36
+ BunnyMock::Session.new
37
+ end
38
+
39
+ # @return [String] Bunny mock version
40
+ def self.version
41
+ VERSION
42
+ end
43
+
44
+ # @return [String] AMQP protocol version
45
+ def self.protocol_version
46
+ AMQ::Protocol::PROTOCOL_VERSION
47
+ end
48
+ end
@@ -0,0 +1,310 @@
1
+ module BunnyMock
2
+ class Channel
3
+
4
+ #
5
+ # API
6
+ #
7
+
8
+ # @return [Integer] Channel identifier
9
+ attr_reader :id
10
+
11
+ # @return [BunnyMock::Session] Session this channel belongs to
12
+ attr_reader :connection
13
+
14
+ # @return [Symbol] Current channel state
15
+ attr_reader :status
16
+
17
+ # @return [Hash<String, BunnyMock::Exchange>] Exchanges created by this channel
18
+ attr_reader :exchanges
19
+
20
+ # @return [Hash<String, BunnyMock::Queue>] Queues created by this channel
21
+ attr_reader :queues
22
+
23
+ ##
24
+ # Create a new {BunnyMock::Channel} instance
25
+ #
26
+ # @param [BunnyMock::Session] connection Mocked session instance
27
+ # @param [Integer] id Channel identifier
28
+ #
29
+ # @api public
30
+ #
31
+ def initialize(connection = nil, id = nil)
32
+
33
+ # store channel id
34
+ @id = id
35
+
36
+ # store connection information
37
+ @connection = connection
38
+
39
+ # initialize exchange and queue storage
40
+ @exchanges = Hash.new
41
+ @queues = Hash.new
42
+
43
+ # set status to opening
44
+ @status = :opening
45
+ end
46
+
47
+ # @return [Boolean] true if status is open, false otherwise
48
+ # @api public
49
+ def open?
50
+ @status == :open
51
+ end
52
+
53
+ # @return [Boolean] true if status is closed, false otherwise
54
+ # @api public
55
+ def closed?
56
+ @status == :closed
57
+ end
58
+
59
+ ##
60
+ # Sets status to open
61
+ #
62
+ # @return [BunnyMock::Channel] self
63
+ # @api public
64
+ #
65
+ def open
66
+
67
+ @status = :open
68
+
69
+ self
70
+ end
71
+
72
+ ##
73
+ # Sets status to closed
74
+ #
75
+ # @return [BunnyMock::Channel] self
76
+ # @api public
77
+ #
78
+ def close
79
+
80
+ @status = :closed
81
+
82
+ self
83
+ end
84
+
85
+ # @return [String] Object representation
86
+ def to_s
87
+ "#<#{self.class.name}:#{self.object_id} @id=#{@id} @open=#{open?}>"
88
+ end
89
+ alias inspect to_s
90
+
91
+ # @group Exchange API
92
+
93
+ ##
94
+ # Mocks an exchange
95
+ #
96
+ # @param [String] name Exchange name
97
+ # @param [Hash] opts Exchange parameters
98
+ #
99
+ # @option opts [Symbol,String] :type Type of exchange
100
+ # @option opts [Boolean] :durable
101
+ # @option opts [Boolean] :auto_delete
102
+ # @option opts [Hash] :arguments
103
+ #
104
+ # @return [BunnyMock::Exchange] Mocked exchange instance
105
+ # @api public
106
+ #
107
+ def exchange(name, opts = {})
108
+
109
+ xchg = find_exchange(name) || Exchange.declare(self, name, opts)
110
+
111
+ register_exchange xchg
112
+ end
113
+
114
+ ##
115
+ # Mocks a fanout exchange
116
+ #
117
+ # @param [String] name Exchange name
118
+ # @param [Hash] opts Exchange parameters
119
+ #
120
+ # @option opts [Boolean] :durable
121
+ # @option opts [Boolean] :auto_delete
122
+ # @option opts [Hash] :arguments
123
+ #
124
+ # @return [BunnyMock::Exchange] Mocked exchange instance
125
+ # @api public
126
+ #
127
+ def fanout(name, opts = {})
128
+ self.exchange name, opts.merge(type: :fanout)
129
+ end
130
+
131
+ ##
132
+ # Mocks a direct exchange
133
+ #
134
+ # @param [String] name Exchange name
135
+ # @param [Hash] opts Exchange parameters
136
+ #
137
+ # @option opts [Boolean] :durable
138
+ # @option opts [Boolean] :auto_delete
139
+ # @option opts [Hash] :arguments
140
+ #
141
+ # @return [BunnyMock::Exchange] Mocked exchange instance
142
+ # @api public
143
+ #
144
+ def direct(name, opts = {})
145
+ self.exchange name, opts.merge(type: :direct)
146
+ end
147
+
148
+ ##
149
+ # Mocks a topic exchange
150
+ #
151
+ # @param [String] name Exchange name
152
+ # @param [Hash] opts Exchange parameters
153
+ #
154
+ # @option opts [Boolean] :durable
155
+ # @option opts [Boolean] :auto_delete
156
+ # @option opts [Hash] :arguments
157
+ #
158
+ # @return [BunnyMock::Exchange] Mocked exchange instance
159
+ # @api public
160
+ #
161
+ def topic(name, opts = {})
162
+ self.exchange name, opts.merge(type: :topic)
163
+ end
164
+
165
+ ##
166
+ # Mocks a headers exchange
167
+ #
168
+ # @param [String] name Exchange name
169
+ # @param [Hash] opts Exchange parameters
170
+ #
171
+ # @option opts [Boolean] :durable
172
+ # @option opts [Boolean] :auto_delete
173
+ # @option opts [Hash] :arguments
174
+ #
175
+ # @return [BunnyMock::Exchange] Mocked exchange instance
176
+ # @api public
177
+ #
178
+ def header(name, opts = {})
179
+ self.exchange name, opts.merge(type: :header)
180
+ end
181
+
182
+ ##
183
+ # Mocks RabbitMQ default exchange
184
+ #
185
+ # @return [BunnyMock::Exchange] Mocked default exchange instance
186
+ # @api public
187
+ #
188
+ def default_exchange
189
+ self.direct '', no_declare: true
190
+ end
191
+
192
+ # @endgroup
193
+
194
+ # @group Queue API
195
+
196
+ ##
197
+ # Create a new {BunnyMock::Queue} instance, or find in channel cache
198
+ #
199
+ # @param [String] name Name of queue
200
+ # @param [Hash] opts Queue creation options
201
+ #
202
+ # @return [BunnyMock::Queue] Queue that was mocked or looked up
203
+ # @api public
204
+ #
205
+ def queue(name = '', opts = {})
206
+
207
+ queue = find_queue(name) || Queue.new(self, name, opts)
208
+
209
+ register_queue queue
210
+ end
211
+
212
+ ##
213
+ # Create a new {BunnyMock::Queue} instance with no name
214
+ #
215
+ # @param [Hash] opts Queue creation options
216
+ #
217
+ # @return [BunnyMock::Queue] Queue that was mocked or looked up
218
+ # @see #queue
219
+ # @api public
220
+ #
221
+ def temporary_queue(opts = {})
222
+
223
+ queue '', opts.merge(exclusive: true)
224
+ end
225
+
226
+ # @endgroup
227
+
228
+ #
229
+ # Implementation
230
+ #
231
+
232
+ # @private
233
+ def find_queue(name)
234
+ @queues[name]
235
+ end
236
+
237
+ # @private
238
+ def register_queue(queue)
239
+ @queues[queue.name] = queue
240
+ end
241
+
242
+ # @private
243
+ def deregister_queue(queue)
244
+ @queues.delete queue.name
245
+ end
246
+
247
+ # @private
248
+ def queue_bind(queue, key, xchg)
249
+
250
+ exchange = @exchanges[xchg] || exchange(xchg)
251
+
252
+ exchange.add_route key, queue
253
+ end
254
+
255
+ # @private
256
+ def queue_unbind(key, xchg)
257
+
258
+ exchange = @exchanges[xchg] || exchange(xchg)
259
+
260
+ exchange.remove_route key
261
+ end
262
+
263
+ # @private
264
+ def xchg_bound_to?(receiver, key, name)
265
+
266
+ source = @exchanges[name] || exchange(name)
267
+
268
+ source.has_binding? receiver, routing_key: key
269
+ end
270
+
271
+ # @private
272
+ def xchg_has_binding?(key, xchg)
273
+
274
+ exchange = @exchanges[xchg] || exchange(xchg)
275
+
276
+ exchange.has_binding? key
277
+ end
278
+
279
+ # @private
280
+ def find_exchange(name)
281
+ @exchanges[name]
282
+ end
283
+
284
+ # @private
285
+ def register_exchange(xchg)
286
+ @exchanges[xchg.name] = xchg
287
+ end
288
+
289
+ # @private
290
+ def deregister_exchange(xchg)
291
+ @exchanges.delete xchg.name
292
+ end
293
+
294
+ # @private
295
+ def xchg_bind(receiver, routing_key, name)
296
+
297
+ source = @exchanges[name] || exchange(name)
298
+
299
+ source.add_route routing_key, receiver
300
+ end
301
+
302
+ # @private
303
+ def xchg_unbind(routing_key, name)
304
+
305
+ source = @exchanges[name] || exchange(name)
306
+
307
+ source.remove_route routing_key
308
+ end
309
+ end
310
+ end