message-driver 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (58) hide show
  1. data/.gitignore +20 -0
  2. data/.rbenv-version +1 -0
  3. data/.relish +2 -0
  4. data/.rspec +2 -0
  5. data/.travis.yml +23 -0
  6. data/CHANGELOG.md +17 -0
  7. data/Gemfile +20 -0
  8. data/Guardfile +39 -0
  9. data/LICENSE.txt +22 -0
  10. data/README.md +36 -0
  11. data/Rakefile +23 -0
  12. data/features/.nav +12 -0
  13. data/features/CHANGELOG.md +17 -0
  14. data/features/README.md +1 -0
  15. data/features/Rails.md +1 -0
  16. data/features/amqp_specific_features/README.md +3 -0
  17. data/features/amqp_specific_features/binding_amqp_destinations.feature +50 -0
  18. data/features/amqp_specific_features/declaring_amqp_exchanges.feature +22 -0
  19. data/features/amqp_specific_features/server_named_destinations.feature +35 -0
  20. data/features/destination_metadata.feature +33 -0
  21. data/features/dynamic_destinations.feature +41 -0
  22. data/features/error_handling.feature +47 -0
  23. data/features/getting_started.md +1 -0
  24. data/features/publishing_a_message.feature +19 -0
  25. data/features/publishing_with_transactions.feature +36 -0
  26. data/features/step_definitions/dynamic_destinations_steps.rb +12 -0
  27. data/features/step_definitions/error_handling_steps.rb +11 -0
  28. data/features/step_definitions/steps.rb +41 -0
  29. data/features/support/env.rb +7 -0
  30. data/features/support/firewall_helper.rb +59 -0
  31. data/features/support/message_table_matcher.rb +11 -0
  32. data/features/support/no_error_matcher.rb +13 -0
  33. data/features/support/test_runner.rb +50 -0
  34. data/features/support/transforms.rb +17 -0
  35. data/lib/message-driver.rb +1 -0
  36. data/lib/message_driver/adapters/base.rb +29 -0
  37. data/lib/message_driver/adapters/bunny_adapter.rb +270 -0
  38. data/lib/message_driver/adapters/in_memory_adapter.rb +58 -0
  39. data/lib/message_driver/broker.rb +95 -0
  40. data/lib/message_driver/destination.rb +31 -0
  41. data/lib/message_driver/exceptions.rb +18 -0
  42. data/lib/message_driver/message.rb +13 -0
  43. data/lib/message_driver/message_publisher.rb +15 -0
  44. data/lib/message_driver/version.rb +5 -0
  45. data/lib/message_driver.rb +18 -0
  46. data/message-driver.gemspec +27 -0
  47. data/spec/integration/amqp_integration_spec.rb +146 -0
  48. data/spec/integration/message_driver/adapters/bunny_adapter_spec.rb +301 -0
  49. data/spec/spec_helper.rb +20 -0
  50. data/spec/support/shared/destination_examples.rb +41 -0
  51. data/spec/units/message_driver/adapters/base_spec.rb +44 -0
  52. data/spec/units/message_driver/adapters/in_memory_adapter_spec.rb +43 -0
  53. data/spec/units/message_driver/broker_spec.rb +98 -0
  54. data/spec/units/message_driver/destination_spec.rb +11 -0
  55. data/spec/units/message_driver/message_publisher_spec.rb +65 -0
  56. data/spec/units/message_driver/message_spec.rb +19 -0
  57. data/test_lib/broker_config.rb +25 -0
  58. metadata +203 -0
@@ -0,0 +1,18 @@
1
+ require 'message_driver/version'
2
+
3
+ require 'message_driver/exceptions'
4
+ require 'message_driver/broker'
5
+ require 'message_driver/message'
6
+ require 'message_driver/destination'
7
+ require 'message_driver/adapters/base'
8
+ require 'message_driver/message_publisher'
9
+
10
+ module MessageDriver
11
+ def self.configure(options={})
12
+ Broker.configure(options)
13
+ end
14
+
15
+ def self.stop
16
+ Broker.stop
17
+ end
18
+ end
@@ -0,0 +1,27 @@
1
+ # -*- encoding: utf-8 -*-
2
+ lib = File.expand_path('../lib', __FILE__)
3
+ $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
4
+ require 'message_driver/version'
5
+
6
+ Gem::Specification.new do |gem|
7
+ gem.name = "message-driver"
8
+ gem.version = Message::Driver::VERSION
9
+ gem.authors = ["Matt Campbell"]
10
+ gem.email = ["matt@soupmatt.com"]
11
+ gem.description = %q{Easy message queues for ruby using AMQ, STOMP and others}
12
+ gem.summary = %q{Easy message queues for ruby}
13
+ gem.homepage = "https://github.com/soupmatt/message_driver"
14
+ gem.license = "MIT"
15
+
16
+ gem.files = `git ls-files`.split($/)
17
+ gem.executables = gem.files.grep(%r{^bin/}).map{ |f| File.basename(f) }
18
+ gem.test_files = gem.files.grep(%r{^(test|spec|features)/})
19
+ gem.require_paths = ["lib"]
20
+
21
+ gem.required_ruby_version = '>= 1.9.2'
22
+
23
+ gem.add_development_dependency "rake"
24
+ gem.add_development_dependency "rspec", "~> 2.13.0"
25
+ gem.add_development_dependency "cucumber"
26
+ gem.add_development_dependency "bunny", "~> 0.9.0.pre7"
27
+ end
@@ -0,0 +1,146 @@
1
+ require 'spec_helper'
2
+
3
+ describe "AMQP Integration", :bunny, type: :integration do
4
+ before do
5
+ MessageDriver.configure BrokerConfig.config
6
+ end
7
+
8
+ context "when a queue can't be found" do
9
+ let(:queue_name) { "my.lost.queue" }
10
+ it "raises a MessageDriver::QueueNotFound error" do
11
+ expect {
12
+ MessageDriver::Broker.dynamic_destination(queue_name, passive: true)
13
+ }.to raise_error(MessageDriver::QueueNotFound, /#{queue_name}/) do |err|
14
+ expect(err.queue_name).to eq(queue_name)
15
+ expect(err.other).to be_a Bunny::NotFound
16
+ end
17
+ end
18
+ end
19
+
20
+ context "when a channel level exception occurs" do
21
+ it "raises a MessageDriver::WrappedException error" do
22
+ expect {
23
+ MessageDriver::Broker.dynamic_destination("not.a.queue", passive: true)
24
+ }.to raise_error(MessageDriver::WrappedException) { |err| err.other.should be_a Bunny::ChannelLevelException }
25
+ end
26
+
27
+ it "reestablishes the channel transparently" do
28
+ expect {
29
+ MessageDriver::Broker.dynamic_destination("not.a.queue", passive: true)
30
+ }.to raise_error(MessageDriver::WrappedException)
31
+ expect {
32
+ MessageDriver::Broker.dynamic_destination("", exclusive: true)
33
+ }.to_not raise_error
34
+ end
35
+
36
+ context "when in a transaction" do
37
+ it "sets the channel_context as rollback-only until the transaction is finished" do
38
+ MessageDriver::Broker.with_transaction do
39
+ expect {
40
+ MessageDriver::Broker.dynamic_destination("not.a.queue", passive: true)
41
+ }.to raise_error(MessageDriver::WrappedException)
42
+ expect {
43
+ MessageDriver::Broker.dynamic_destination("", exclusive: true)
44
+ }.to raise_error(MessageDriver::TransactionRollbackOnly)
45
+ end
46
+ expect {
47
+ MessageDriver::Broker.dynamic_destination("", exclusive: true)
48
+ }.to_not raise_error
49
+ end
50
+ end
51
+ end
52
+
53
+ context "when the broker connection fails" do
54
+ after(:each) do
55
+ MessageDriver::Broker.stop
56
+ end
57
+ def disrupt_connection
58
+ #yes, this is very implementation specific
59
+ MessageDriver::Broker.adapter.connection.instance_variable_get(:@transport).close
60
+ end
61
+
62
+ it "raises a MessageDriver::ConnectionException" do
63
+ dest = MessageDriver::Broker.dynamic_destination("", exclusive: true)
64
+ disrupt_connection
65
+ expect {
66
+ dest.publish("Reconnection Test")
67
+ }.to raise_error(MessageDriver::ConnectionException) do |err|
68
+ expect(err.other).to be_a Bunny::NetworkErrorWrapper
69
+ end
70
+ end
71
+
72
+ def create_destination(queue_name)
73
+ MessageDriver::Broker.dynamic_destination(queue_name, exclusive: true)
74
+ end
75
+ it "seemlessly reconnects" do
76
+ dest = create_destination("seemless.reconnect.queue")
77
+ disrupt_connection
78
+ expect {
79
+ dest.publish("Reconnection Test 1")
80
+ }.to raise_error(MessageDriver::ConnectionException)
81
+ dest = create_destination("seemless.reconnect.queue")
82
+ dest.publish("Reconnection Test 2")
83
+ msg = dest.pop_message
84
+ expect(msg).to_not be_nil
85
+ expect(msg.body).to eq("Reconnection Test 2")
86
+ end
87
+
88
+ context "when in a transaction" do
89
+ it "raises a MessageDriver::ConnectionException" do
90
+ expect {
91
+ MessageDriver::Broker.with_transaction do
92
+ disrupt_connection
93
+ MessageDriver::Broker.dynamic_destination("", exclusive: true)
94
+ end
95
+ }.to raise_error(MessageDriver::ConnectionException)
96
+ end
97
+
98
+ it "sets the channel_context as rollback-only until the transaction is finished" do
99
+ MessageDriver::Broker.with_transaction do
100
+ disrupt_connection
101
+ expect {
102
+ MessageDriver::Broker.dynamic_destination("", exclusive: true)
103
+ }.to raise_error(MessageDriver::ConnectionException)
104
+ expect {
105
+ MessageDriver::Broker.dynamic_destination("", exclusive: true)
106
+ }.to raise_error(MessageDriver::TransactionRollbackOnly)
107
+ end
108
+ expect {
109
+ MessageDriver::Broker.dynamic_destination("", exclusive: true)
110
+ }.to_not raise_error
111
+ end
112
+ end
113
+ end
114
+
115
+ context "when an unhandled expection occurs in a transaction" do
116
+ let(:destination) { MessageDriver::Broker.dynamic_destination("", exclusive: true) }
117
+
118
+ it "rolls back the transaction" do
119
+ expect {
120
+ MessageDriver::Broker.with_transaction do
121
+ destination.publish("Test Message")
122
+ raise "unhandled error"
123
+ end
124
+ }.to raise_error "unhandled error"
125
+ expect(destination.pop_message).to be_nil
126
+ end
127
+
128
+ it "allows the next transaction to continue" do
129
+ expect {
130
+ MessageDriver::Broker.with_transaction do
131
+ destination.publish("Test Message 1")
132
+ raise "unhandled error"
133
+ end
134
+ }.to raise_error "unhandled error"
135
+ expect(destination.pop_message).to be_nil
136
+
137
+ MessageDriver::Broker.with_transaction do
138
+ destination.publish("Test Message 2")
139
+ end
140
+
141
+ msg = destination.pop_message
142
+ expect(msg).to_not be_nil
143
+ expect(msg.body).to eq("Test Message 2")
144
+ end
145
+ end
146
+ end
@@ -0,0 +1,301 @@
1
+ require 'spec_helper'
2
+
3
+ require 'message_driver/adapters/bunny_adapter'
4
+
5
+ module MessageDriver::Adapters
6
+ describe BunnyAdapter, :bunny, type: :integration do
7
+
8
+ let(:valid_connection_attrs) { BrokerConfig.config }
9
+
10
+ describe "#initialize" do
11
+ context "differing bunny versions" do
12
+ shared_examples "raises an error" do
13
+ it "raises an error" do
14
+ stub_const("Bunny::VERSION", version)
15
+ expect {
16
+ described_class.new(valid_connection_attrs)
17
+ }.to raise_error MessageDriver::Exception, "bunny 0.9.0.pre7 or later is required for the bunny adapter"
18
+ end
19
+ end
20
+ shared_examples "doesn't raise an error" do
21
+ it "doesn't raise an an error" do
22
+ stub_const("Bunny::VERSION", version)
23
+ adapter = nil
24
+ expect {
25
+ adapter = described_class.new(valid_connection_attrs)
26
+ }.to_not raise_error
27
+ end
28
+ end
29
+ %w(0.8.0 0.9.0.pre6).each do |v|
30
+ context "bunny version #{v}" do
31
+ let(:version) { v }
32
+ include_examples "raises an error"
33
+ end
34
+ end
35
+ %w(0.9.0.pre7 0.9.0.rc1 0.9.0 0.9.1).each do |v|
36
+ context "bunny version #{v}" do
37
+ let(:version) { v }
38
+ include_examples "doesn't raise an error"
39
+ end
40
+ end
41
+ end
42
+
43
+ it "connects to the rabbit broker" do
44
+ adapter = described_class.new(valid_connection_attrs)
45
+
46
+ expect(adapter.connection).to be_a Bunny::Session
47
+ expect(adapter.connection).to be_open
48
+ end
49
+
50
+ it "connects to the rabbit broker lazily" do
51
+ adapter = described_class.new(valid_connection_attrs)
52
+
53
+ expect(adapter.connection(false)).to_not be_open
54
+ end
55
+
56
+ it "forces bunny into non-threaded mode" do
57
+ #FIXME can be changed once ruby-amqp/bunny#112 is fixed
58
+ adapter = described_class.new(valid_connection_attrs)
59
+ expect(adapter.connection(false).threaded).to be_false
60
+
61
+ adapter = described_class.new(valid_connection_attrs.merge(threaded: true))
62
+ expect(adapter.connection(false).threaded).to be_false
63
+ end
64
+ end
65
+
66
+ shared_context "a connected adapter" do
67
+ let(:adapter) { described_class.new(valid_connection_attrs) }
68
+ let(:connection) { adapter.connection }
69
+
70
+ after do
71
+ connection.close
72
+ end
73
+ end
74
+
75
+ shared_context "with a queue" do
76
+ include_context "a connected adapter"
77
+
78
+ let(:channel) { connection.create_channel }
79
+ let(:tmp_queue_name) { "my_temp_queue" }
80
+ let(:tmp_queue) { channel.queue(tmp_queue_name, exclusive: true) }
81
+ end
82
+
83
+ describe "#pop_message" do
84
+ include_context "with a queue"
85
+ it "needs some real tests"
86
+ end
87
+
88
+ describe "#create_destination" do
89
+ include_context "a connected adapter"
90
+
91
+ context "with defaults" do
92
+ context "the resulting destination" do
93
+ let(:dest_name) { "my_dest" }
94
+ subject(:result) { adapter.create_destination(dest_name, exclusive: true) }
95
+
96
+ it { should be_a BunnyAdapter::QueueDestination }
97
+ end
98
+ end
99
+
100
+ context "the type is queue" do
101
+ context "and there is no destination name given" do
102
+ subject(:destination) { adapter.create_destination("", type: :queue, exclusive: true) }
103
+ it { should be_a BunnyAdapter::QueueDestination }
104
+ its(:name) { should be_a String }
105
+ its(:name) { should_not be_empty }
106
+ end
107
+ context "the resulting destination" do
108
+ let(:dest_name) { "my_dest" }
109
+ subject(:destination) { adapter.create_destination(dest_name, type: :queue, exclusive: true) }
110
+ before do
111
+ destination
112
+ end
113
+
114
+ it { should be_a BunnyAdapter::QueueDestination }
115
+ its(:name) { should be_a String }
116
+ its(:name) { should eq(dest_name) }
117
+
118
+ include_examples "supports #message_count"
119
+
120
+ it "strips off the type so it isn't set on the destination" do
121
+ expect(subject.dest_options).to_not have_key :type
122
+ end
123
+ it "ensures the queue is declared" do
124
+ expect {
125
+ connection.with_channel do |ch|
126
+ ch.queue(dest_name, passive: true)
127
+ end
128
+ }.to_not raise_error
129
+ end
130
+ context "publishing a message" do
131
+ let(:body) { "Testing the QueueDestination" }
132
+ let(:headers) { {"foo" => "bar"} }
133
+ let(:properties) { {persistent: false} }
134
+ before do
135
+ subject.publish(body, headers, properties)
136
+ end
137
+ it "publishes via the default exchange" do
138
+ msg = subject.pop_message
139
+ expect(msg.body).to eq(body)
140
+ expect(msg.headers).to eq(headers)
141
+ expect(msg.properties[:delivery_mode]).to eq(1)
142
+ expect(msg.delivery_info.exchange).to eq("")
143
+ expect(msg.delivery_info.routing_key).to eq(subject.name)
144
+ end
145
+ end
146
+ it_behaves_like "a destination"
147
+ end
148
+ context "and bindings are provided" do
149
+ let(:dest_name) { "binding_test_queue" }
150
+ let(:exchange) { adapter.create_destination("amq.direct", type: :exchange) }
151
+
152
+ it "raises an exception if you don't provide a source" do
153
+ expect {
154
+ adapter.create_destination("bad_bind_queue", type: :queue, exclusive: true, bindings: [{args: {routing_key: "test_exchange_bind"}}])
155
+ }.to raise_error MessageDriver::Exception, /must provide a source/
156
+ end
157
+
158
+ it "routes message to the queue through the exchange" do
159
+ destination = adapter.create_destination(dest_name, type: :queue, exclusive: true, bindings: [{source: "amq.direct", args: {routing_key: "test_queue_bind"}}])
160
+ exchange.publish("test queue bindings", {}, {routing_key: "test_queue_bind"})
161
+ message = destination.pop_message
162
+ expect(message).to_not be_nil
163
+ expect(message.body).to eq("test queue bindings")
164
+ end
165
+ end
166
+
167
+ context "we are not yet connected to the broker and :no_declare is provided" do
168
+ it "doesn't cause a connection to the broker" do
169
+ connection.stop
170
+ adapter.create_destination("test_queue", no_declare: true, type: :queue, exclusive: true)
171
+ expect(adapter.connection(false)).to_not be_open
172
+ end
173
+
174
+ context "with a server-named queue" do
175
+ it "raises an error" do
176
+ expect {
177
+ adapter.create_destination("", no_declare: true, type: :queue, exclusive: true)
178
+ }.to raise_error MessageDriver::Exception, "server-named queues must be declared, but you provided :no_declare => true"
179
+ end
180
+ end
181
+
182
+ context "with bindings" do
183
+ it "raises an error" do
184
+ expect {
185
+ adapter.create_destination("tmp_queue", no_declare: true, bindings: [{source: "amq.fanout"}], type: :queue, exclusive: true)
186
+ }.to raise_error MessageDriver::Exception, "queues with bindings must be declared, but you provided :no_declare => true"
187
+ end
188
+ end
189
+ end
190
+ end
191
+
192
+ context "the type is exchange" do
193
+ context "the resulting destination" do
194
+ let(:dest_name) { "my_dest" }
195
+ subject(:destination) { adapter.create_destination(dest_name, type: :exchange) }
196
+
197
+ it { should be_a BunnyAdapter::ExchangeDestination }
198
+ include_examples "doesn't support #message_count"
199
+
200
+ it "strips off the type so it isn't set on the destination" do
201
+ expect(subject.dest_options).to_not have_key :type
202
+ end
203
+
204
+ it "raises an error when pop_message is called" do
205
+ expect {
206
+ subject.pop_message(dest_name)
207
+ }.to raise_error MessageDriver::Exception, "You can't pop a message off an exchange"
208
+ end
209
+
210
+ context "publishing a message" do
211
+ let(:body) { "Testing the ExchangeDestination" }
212
+ let(:headers) { {"foo" => "bar"} }
213
+ let(:properties) { {persistent: false} }
214
+ before { connection.with_channel { |ch| ch.fanout(dest_name, auto_delete: true) } }
215
+ let!(:queue) do
216
+ q = nil
217
+ connection.with_channel do |ch|
218
+ q = ch.queue("", exclusive: true)
219
+ q.bind(dest_name)
220
+ end
221
+ q
222
+ end
223
+ before do
224
+ subject.publish(body, headers, properties)
225
+ end
226
+
227
+ it "publishes to the specified exchange" do
228
+ connection.with_channel do |ch|
229
+ q = ch.queue(queue.name, passive: true)
230
+ msg = q.pop
231
+ expect(msg[2]).to eq(body)
232
+ expect(msg[0].exchange).to eq(dest_name)
233
+ expect(msg[1][:headers]).to eq(headers)
234
+ expect(msg[1][:delivery_mode]).to eq(1)
235
+ end
236
+ end
237
+ end
238
+ end
239
+
240
+ context "declaring an exchange on the broker" do
241
+ let(:dest_name) { "my.cool.exchange" }
242
+
243
+ it "creates the exchange if you include 'declare' in the options" do
244
+ exchange = adapter.create_destination(dest_name, type: :exchange, declare: {type: :fanout, auto_delete: true})
245
+ queue = adapter.create_destination("", type: :queue, exclusive: true, bindings: [{source: dest_name}])
246
+ exchange.publish("test declaring exchange")
247
+ message = queue.pop_message
248
+ expect(message).to_not be_nil
249
+ expect(message.body).to eq("test declaring exchange")
250
+ end
251
+
252
+ it "raises an error if you don't provide a type" do
253
+ expect {
254
+ adapter.create_destination(dest_name, type: :exchange, declare: {auto_delete: true})
255
+ }.to raise_error MessageDriver::Exception, /you must provide a valid exchange type/
256
+ end
257
+
258
+ end
259
+
260
+ context "and bindings are provided" do
261
+ let(:dest_name) { "binding_exchange_queue" }
262
+ let(:exchange) { adapter.create_destination("amq.direct", type: :exchange) }
263
+
264
+ it "raises an exception if you don't provide a source" do
265
+ expect {
266
+ adapter.create_destination("amq.fanout", type: :exchange, bindings: [{args: {routing_key: "test_exchange_bind"}}])
267
+ }.to raise_error MessageDriver::Exception, /must provide a source/
268
+ end
269
+
270
+ it "routes message to the queue through the exchange" do
271
+ adapter.create_destination("amq.fanout", type: :exchange, bindings: [{source: "amq.direct", args: {routing_key: "test_exchange_bind"}}])
272
+ destination = adapter.create_destination(dest_name, type: :queue, exclusive: true, bindings: [{source: "amq.fanout"}])
273
+ exchange.publish("test exchange bindings", {}, {routing_key: "test_exchange_bind"})
274
+ message = destination.pop_message
275
+ expect(message).to_not be_nil
276
+ expect(message.body).to eq("test exchange bindings")
277
+ end
278
+ end
279
+
280
+ context "we are not yet connected to the broker" do
281
+ before do
282
+ connection.stop
283
+ end
284
+
285
+ it "doesn't cause a connection to the broker" do
286
+ adapter.create_destination("amq.fanout", type: :exchange)
287
+ expect(adapter.connection(false)).to_not be_open
288
+ end
289
+ end
290
+ end
291
+
292
+ context "the type is invalid" do
293
+ it "raises in an error" do
294
+ expect {
295
+ adapter.create_destination("my_dest", type: :foo_bar)
296
+ }.to raise_error MessageDriver::Exception, "invalid destination type #{:foo_bar}"
297
+ end
298
+ end
299
+ end
300
+ end
301
+ end
@@ -0,0 +1,20 @@
1
+ require 'message_driver'
2
+
3
+ require File.join(File.dirname(__FILE__), '..', 'test_lib', 'broker_config')
4
+
5
+ Dir["./spec/support/**/*.rb"].sort.each {|f| require f}
6
+
7
+ RSpec.configure do |c|
8
+ c.treat_symbols_as_metadata_keys_with_true_values = true
9
+ c.order = 'random'
10
+ c.filter_run :focus
11
+
12
+ c.reporter.message("Acceptance Tests running with broker config: #{BrokerConfig.config}")
13
+
14
+ c.filter_run_excluding :no_travis if ENV['TRAVIS']=='true' && ENV['ADAPTER']=='bunny'
15
+ if c.inclusion_filter[:all_adapters] == true
16
+ c.filter_run BrokerConfig.current_adapter
17
+ else
18
+ c.run_all_when_everything_filtered = true
19
+ end
20
+ end
@@ -0,0 +1,41 @@
1
+ shared_examples "a destination" do
2
+ describe "#pop_message" do
3
+ let(:body) { "The message body" }
4
+ let(:headers) { { "foo" => "bar", "bar" => "baz"} }
5
+ let(:properties) { {persistent: true, client_ack: true} }
6
+
7
+ before do
8
+ destination.publish(body, headers, properties)
9
+ end
10
+
11
+ context "the result" do
12
+ let!(:message) { destination.pop_message }
13
+ subject { message }
14
+
15
+ it { should be_a MessageDriver::Message::Base }
16
+ its(:body) { should eq(body) }
17
+ its(:headers) { should eq(headers) }
18
+ its(:properties) { should_not be_nil }
19
+ end
20
+ end
21
+ end
22
+
23
+ shared_examples "doesn't support #message_count" do
24
+ describe "#message_count" do
25
+ it "raises an error" do
26
+ expect {
27
+ destination.message_count
28
+ }.to raise_error "#message_count is not supported by #{destination.class}"
29
+ end
30
+ end
31
+ end
32
+
33
+ shared_examples "supports #message_count" do
34
+ #FIXME this example fails on travis with the bunny adapter :(
35
+ it "reports it's message_count", :no_travis do
36
+ expect {
37
+ destination.publish("msg1")
38
+ destination.publish("msg2")
39
+ }.to change{destination.message_count}.by(2)
40
+ end
41
+ end
@@ -0,0 +1,44 @@
1
+ require 'spec_helper'
2
+
3
+ module MessageDriver::Adapters
4
+ describe Base do
5
+ class TestAdapter < described_class
6
+ def initialize(configuration)
7
+
8
+ end
9
+ end
10
+ subject { TestAdapter.new({}) }
11
+
12
+ describe "#publish" do
13
+ it "raises an error" do
14
+ expect {
15
+ subject.publish(:destination, {foo: "bar"})
16
+ }.to raise_error "Must be implemented in subclass"
17
+ end
18
+ end
19
+
20
+ describe "#pop_message" do
21
+ it "raises an error" do
22
+ expect {
23
+ subject.pop_message(:destination)
24
+ }.to raise_error "Must be implemented in subclass"
25
+ end
26
+ end
27
+
28
+ describe "#stop" do
29
+ it "raises an error" do
30
+ expect {
31
+ subject.stop
32
+ }.to raise_error "Must be implemented in subclass"
33
+ end
34
+ end
35
+
36
+ describe "#create_destination" do
37
+ it "raises an error" do
38
+ expect {
39
+ subject.create_destination("foo")
40
+ }.to raise_error "Must be implemented in subclass"
41
+ end
42
+ end
43
+ end
44
+ end
@@ -0,0 +1,43 @@
1
+ require 'spec_helper'
2
+
3
+ require 'message_driver/adapters/in_memory_adapter'
4
+
5
+ module MessageDriver::Adapters
6
+ describe InMemoryAdapter do
7
+ let(:adapter) { described_class.new }
8
+
9
+ describe "#create_destination" do
10
+ describe "the resulting destination" do
11
+ let!(:destination) { adapter.create_destination("my_test_dest") }
12
+ it_behaves_like "a destination"
13
+
14
+ subject { destination }
15
+
16
+ it { should be_a InMemoryAdapter::Destination }
17
+
18
+ include_examples "supports #message_count"
19
+ end
20
+ end
21
+
22
+ describe "#reset_after_tests" do
23
+ #make some destinations
24
+ # throw some messages on it
25
+ # assert the messages are in the destinations
26
+ # empty the queues on each destination via method
27
+ # assert destinations are empty
28
+
29
+ it "empties all the destination queues" do
30
+ destinations = (1..3).map(&adapter.method(:create_destination))
31
+ destinations.each do |destination|
32
+ destination.publish("There's always money in the banana stand!", {}, {})
33
+ end
34
+
35
+ adapter.reset_after_tests
36
+
37
+ destinations.each do |destination|
38
+ expect(destination.pop_message).to be_nil
39
+ end
40
+ end
41
+ end
42
+ end
43
+ end