message-driver 0.4.0 → 0.5.0

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.
Files changed (74) hide show
  1. checksums.yaml +4 -4
  2. data/.coveralls.yml +2 -0
  3. data/.rubocop.yml +26 -2
  4. data/.rubocop_todo.yml +15 -123
  5. data/.travis.yml +2 -1
  6. data/CHANGELOG.md +10 -1
  7. data/Gemfile +15 -6
  8. data/Rakefile +23 -12
  9. data/ci/reset_vhost +8 -3
  10. data/ci/travis_setup +0 -3
  11. data/features/.nav +6 -1
  12. data/features/CHANGELOG.md +10 -1
  13. data/features/amqp_specific_features/binding_amqp_destinations.feature +1 -0
  14. data/features/amqp_specific_features/declaring_amqp_exchanges.feature +1 -0
  15. data/features/amqp_specific_features/server_named_destinations.feature +1 -0
  16. data/features/destination_metadata.feature +26 -0
  17. data/features/logging.feature +1 -1
  18. data/features/middleware/middleware_basics.feature +91 -0
  19. data/features/middleware/middleware_ordering.feature +60 -0
  20. data/features/middleware/middleware_parameters.feature +43 -0
  21. data/features/middleware/middleware_with_blocks.feature +85 -0
  22. data/features/step_definitions/dynamic_destinations_steps.rb +1 -1
  23. data/features/step_definitions/message_consumers_steps.rb +5 -0
  24. data/features/step_definitions/middleware_steps.rb +10 -0
  25. data/features/step_definitions/steps.rb +10 -2
  26. data/features/support/env.rb +4 -3
  27. data/features/support/firewall_helper.rb +1 -1
  28. data/features/support/message_table_matcher.rb +3 -2
  29. data/features/support/no_error_matcher.rb +2 -2
  30. data/features/support/test_runner.rb +11 -57
  31. data/features/support/transforms.rb +12 -10
  32. data/lib/message_driver.rb +3 -1
  33. data/lib/message_driver/adapters/base.rb +11 -11
  34. data/lib/message_driver/adapters/bunny_adapter.rb +189 -132
  35. data/lib/message_driver/adapters/in_memory_adapter.rb +51 -34
  36. data/lib/message_driver/adapters/stomp_adapter.rb +22 -23
  37. data/lib/message_driver/broker.rb +21 -16
  38. data/lib/message_driver/client.rb +15 -16
  39. data/lib/message_driver/destination.rb +26 -8
  40. data/lib/message_driver/message.rb +5 -4
  41. data/lib/message_driver/middleware.rb +8 -0
  42. data/lib/message_driver/middleware/base.rb +19 -0
  43. data/lib/message_driver/middleware/block_middleware.rb +33 -0
  44. data/lib/message_driver/middleware/middleware_stack.rb +61 -0
  45. data/lib/message_driver/subscription.rb +2 -2
  46. data/lib/message_driver/version.rb +1 -1
  47. data/message-driver.gemspec +3 -4
  48. data/spec/integration/bunny/amqp_integration_spec.rb +21 -82
  49. data/spec/integration/bunny/bunny_adapter_spec.rb +288 -268
  50. data/spec/integration/in_memory/in_memory_adapter_spec.rb +93 -90
  51. data/spec/integration/stomp/stomp_adapter_spec.rb +126 -93
  52. data/spec/spec_helper.rb +11 -9
  53. data/spec/support/shared/adapter_examples.rb +1 -1
  54. data/spec/support/shared/client_ack_examples.rb +4 -4
  55. data/spec/support/shared/context_examples.rb +6 -4
  56. data/spec/support/shared/destination_examples.rb +54 -14
  57. data/spec/support/shared/subscription_examples.rb +33 -26
  58. data/spec/support/shared/transaction_examples.rb +12 -12
  59. data/spec/support/utils.rb +1 -1
  60. data/spec/units/message_driver/adapters/base_spec.rb +42 -40
  61. data/spec/units/message_driver/broker_spec.rb +38 -38
  62. data/spec/units/message_driver/client_spec.rb +87 -87
  63. data/spec/units/message_driver/destination_spec.rb +16 -11
  64. data/spec/units/message_driver/message_spec.rb +96 -70
  65. data/spec/units/message_driver/middleware/base_spec.rb +30 -0
  66. data/spec/units/message_driver/middleware/block_middleware_spec.rb +82 -0
  67. data/spec/units/message_driver/middleware/middleware_stack_spec.rb +165 -0
  68. data/spec/units/message_driver/subscription_spec.rb +18 -16
  69. data/test_lib/broker_config.rb +21 -5
  70. data/test_lib/coverage.rb +11 -0
  71. data/test_lib/provider/base.rb +59 -0
  72. data/test_lib/provider/in_memory.rb +6 -0
  73. data/test_lib/provider/rabbitmq.rb +67 -0
  74. metadata +46 -35
@@ -2,126 +2,129 @@ require 'spec_helper'
2
2
 
3
3
  require 'message_driver/adapters/in_memory_adapter'
4
4
 
5
- module MessageDriver::Adapters
6
- describe InMemoryAdapter, :in_memory, type: :integration do
7
- let(:broker) { double('broker') }
8
- subject(:adapter) { described_class.new(broker) }
9
-
10
- describe '#new_context' do
11
- it 'returns a InMemoryAdapter::InMemoryContext' do
12
- expect(subject.new_context).to be_a InMemoryAdapter::InMemoryContext
5
+ module MessageDriver
6
+ module Adapters
7
+ RSpec.describe InMemoryAdapter, :in_memory, type: :integration do
8
+ let(:broker) { double(Broker) }
9
+ subject(:adapter) { described_class.new(broker) }
10
+
11
+ describe '#new_context' do
12
+ it 'returns a InMemoryAdapter::InMemoryContext' do
13
+ expect(subject.new_context).to be_a InMemoryAdapter::InMemoryContext
14
+ end
13
15
  end
14
- end
15
-
16
- it_behaves_like 'an adapter'
17
-
18
- describe InMemoryAdapter::InMemoryContext do
19
- subject(:adapter_context) { adapter.new_context }
20
-
21
- it_behaves_like 'an adapter context'
22
- it_behaves_like 'transactions are not supported'
23
- it_behaves_like 'client acks are not supported'
24
- it_behaves_like 'subscriptions are supported', InMemoryAdapter::Subscription
25
- end
26
16
 
27
- describe '#create_destination' do
28
- describe 'the resulting destination' do
29
- subject(:destination) { adapter.create_destination('my_test_dest') }
17
+ it_behaves_like 'an adapter'
30
18
 
31
- it { should be_a InMemoryAdapter::Destination }
19
+ describe 'InMemoryAdapter::InMemoryContext' do
20
+ subject(:adapter_context) { adapter.new_context }
32
21
 
33
- it_behaves_like 'a destination'
34
- include_examples 'supports #message_count'
22
+ it_behaves_like 'an adapter context'
23
+ it_behaves_like 'transactions are not supported'
24
+ it_behaves_like 'client acks are not supported'
25
+ it_behaves_like 'subscriptions are supported', InMemoryAdapter::Subscription
35
26
  end
36
27
 
37
- context 'when creating two destinations for the same queue' do
38
- it 'creates seperate destination instances' do
39
- queue_name = 'my_queue'
40
- dest1 = adapter.create_destination(queue_name)
41
- dest2 = adapter.create_destination(queue_name)
42
- expect(dest1).to_not be(dest2)
43
- end
44
- end
45
- end
28
+ describe '#create_destination' do
29
+ describe 'the resulting destination' do
30
+ subject(:destination) { adapter.create_destination('my_test_dest') }
46
31
 
47
- describe '#reset_after_tests' do
48
- it 'empties all the destination queues' do
49
- destinations = (1..3).map(&adapter.method(:create_destination))
50
- destinations.each do |destination|
51
- destination.publish("There's always money in the banana stand!", {}, {})
52
- end
32
+ it { is_expected.to be_a InMemoryAdapter::Destination }
53
33
 
54
- adapter.reset_after_tests
34
+ it_behaves_like 'a destination'
35
+ include_examples 'supports #message_count'
36
+ include_examples 'supports #consumer_count'
37
+ end
55
38
 
56
- destinations.each do |destination|
57
- expect(destination.message_count).to eq(0)
39
+ context 'when creating two destinations for the same queue' do
40
+ it 'creates seperate destination instances' do
41
+ queue_name = 'my_queue'
42
+ dest1 = adapter.create_destination(queue_name)
43
+ dest2 = adapter.create_destination(queue_name)
44
+ expect(dest1).to_not be(dest2)
45
+ end
58
46
  end
59
47
  end
60
48
 
61
- it 'removes any existing subscriptions' do
62
- destinations = (1..3).map(&adapter.method(:create_destination))
63
- consumer = lambda do |_| end
64
- destinations.each do |destination|
65
- destination.subscribe(&consumer)
66
- end
49
+ describe '#reset_after_tests' do
50
+ it 'empties all the destination queues' do
51
+ destinations = (1..3).map(&adapter.method(:create_destination))
52
+ destinations.each do |destination|
53
+ destination.publish("There's always money in the banana stand!", {}, {})
54
+ end
67
55
 
68
- adapter.reset_after_tests
56
+ adapter.reset_after_tests
69
57
 
70
- destinations.each do |destination|
71
- expect(destination.subscription).to be_nil
58
+ destinations.each do |destination|
59
+ expect(destination.message_count).to eq(0)
60
+ end
72
61
  end
73
62
 
74
- end
75
- end
63
+ it 'removes any existing subscriptions' do
64
+ destinations = (1..3).map(&adapter.method(:create_destination))
65
+ consumer = ->(_) {}
66
+ destinations.each do |destination|
67
+ destination.subscribe(&consumer)
68
+ end
76
69
 
77
- describe 'accessing the same queue from two destinations' do
78
- let(:queue_name) { 'my_queue' }
79
- let(:dest1) { adapter.create_destination(queue_name) }
80
- let(:dest2) { adapter.create_destination(queue_name) }
70
+ adapter.reset_after_tests
71
+
72
+ destinations.each do |destination|
73
+ expect(destination.subscription).to be_nil
74
+ end
81
75
 
82
- context 'when I have a consumer on one destination' do
83
- let(:consumer) { lambda do |_| end }
84
- before do
85
- dest1.subscribe(&consumer)
86
- end
87
- it 'is the same consumer on the other destination' do
88
- expect(dest2.subscription.consumer).to be(consumer)
89
76
  end
90
77
  end
91
78
 
92
- context 'when I publish a message to one destination' do
93
- it 'changes the message_count on the other' do
94
- expect {
95
- dest1.publish('my test message')
96
- }.to change{dest2.message_count}.from(0).to(1)
79
+ describe 'accessing the same queue from two destinations' do
80
+ let(:queue_name) { 'my_queue' }
81
+ let(:dest1) { adapter.create_destination(queue_name) }
82
+ let(:dest2) { adapter.create_destination(queue_name) }
83
+
84
+ context 'when I have a consumer on one destination' do
85
+ let(:consumer) { ->(_) {} }
86
+ before do
87
+ dest1.subscribe(&consumer)
88
+ end
89
+ it 'is the same consumer on the other destination' do
90
+ expect(dest2.subscription.consumer).to be(consumer)
91
+ end
97
92
  end
98
93
 
99
- it 'can be popped off the other' do
100
- dest1.publish('my test message')
101
- msg = dest2.pop_message
102
- expect(msg).to_not be_nil
103
- expect(msg.body).to eq('my test message')
104
- end
105
- end
94
+ context 'when I publish a message to one destination' do
95
+ it 'changes the message_count on the other' do
96
+ expect do
97
+ dest1.publish('my test message')
98
+ end.to change { dest2.message_count }.from(0).to(1)
99
+ end
106
100
 
107
- context 'when I pop a message off one destination' do
108
- let(:message_body) { 'test popping a message' }
109
- before do
110
- dest2.publish(message_body)
101
+ it 'can be popped off the other' do
102
+ dest1.publish('my test message')
103
+ msg = dest2.pop_message
104
+ expect(msg).to_not be_nil
105
+ expect(msg.body).to eq('my test message')
106
+ end
111
107
  end
112
108
 
113
- it 'changes the message_count on the other' do
114
- expect {
115
- dest1.pop_message
116
- }.to change{dest2.message_count}.from(1).to(0)
109
+ context 'when I pop a message off one destination' do
110
+ let(:message_body) { 'test popping a message' }
111
+ before do
112
+ dest2.publish(message_body)
113
+ end
114
+
115
+ it 'changes the message_count on the other' do
116
+ expect do
117
+ dest1.pop_message
118
+ end.to change { dest2.message_count }.from(1).to(0)
119
+ end
117
120
  end
118
121
  end
119
- end
120
122
 
121
- describe 'subscribing a consumer' do
122
- let(:destination) { adapter.create_destination(:my_queue) }
123
+ describe 'subscribing a consumer' do
124
+ let(:destination) { adapter.create_destination(:my_queue) }
123
125
 
124
- let(:subscription_type) { MessageDriver::Adapters::InMemoryAdapter::Subscription }
126
+ let(:subscription_type) { MessageDriver::Adapters::InMemoryAdapter::Subscription }
127
+ end
125
128
  end
126
129
  end
127
130
  end
@@ -2,134 +2,167 @@ require 'spec_helper'
2
2
 
3
3
  require 'message_driver/adapters/stomp_adapter'
4
4
 
5
- module MessageDriver::Adapters
6
- describe StompAdapter, :stomp, type: :integration do
7
-
8
- let(:valid_connection_attrs) { BrokerConfig.config }
9
-
10
- describe '#initialize' do
11
- let(:connection_attrs) { valid_connection_attrs }
12
- let(:broker) { double('broker') }
13
-
14
- context 'differing stomp versions' do
15
- shared_examples 'raises a stomp error' do
16
- it 'raises an error' do
17
- stub_const('Stomp::Version::STRING', version)
18
- expect {
19
- described_class.new(broker, connection_attrs)
20
- }.to raise_error MessageDriver::Error, 'stomp 1.3.1 or a later version of the 1.3.x series is required for the stomp adapter'
5
+ module MessageDriver
6
+ module Adapters
7
+ RSpec.describe StompAdapter, :stomp, type: :integration do
8
+
9
+ let(:valid_connection_attrs) { BrokerConfig.config }
10
+
11
+ describe '#initialize' do
12
+ let(:connection_attrs) { valid_connection_attrs }
13
+ let(:broker) { double('broker') }
14
+
15
+ context 'differing stomp versions' do
16
+ shared_examples 'raises a stomp error' do
17
+ it 'raises an error' do
18
+ stub_const('Stomp::Version::STRING', version)
19
+ expect do
20
+ described_class.new(broker, connection_attrs)
21
+ end.to raise_error MessageDriver::Error, 'stomp 1.3.1 or a later version of the 1.3.x series is required for the stomp adapter'
22
+ end
21
23
  end
22
- end
23
- shared_examples "doesn't raise a stomp error" do
24
- it "doesn't raise an an error" do
25
- stub_const('Stomp::Version::STRING', version)
26
- adapter = nil
27
- expect {
28
- adapter = described_class.new(broker, connection_attrs)
29
- }.to_not raise_error
24
+ shared_examples "doesn't raise a stomp error" do
25
+ it "doesn't raise an an error" do
26
+ stub_const('Stomp::Version::STRING', version)
27
+ adapter = nil
28
+ expect do
29
+ adapter = described_class.new(broker, connection_attrs)
30
+ end.to_not raise_error
31
+ end
30
32
  end
31
- end
32
- %w(1.1.0 1.2.9 1.3.0 1.4.0).each do |v|
33
- context "stomp version #{v}" do
34
- let(:version) { v }
35
- include_examples 'raises a stomp error'
33
+ %w(1.1.0 1.2.9 1.3.0 1.4.0).each do |v|
34
+ context "stomp version #{v}" do
35
+ let(:version) { v }
36
+ include_examples 'raises a stomp error'
37
+ end
36
38
  end
37
- end
38
- %w(1.3.1 1.3.5).each do |v|
39
- context "stomp version #{v}" do
40
- let(:version) { v }
41
- include_examples "doesn't raise a stomp error"
39
+ %w(1.3.1 1.3.5).each do |v|
40
+ context "stomp version #{v}" do
41
+ let(:version) { v }
42
+ include_examples "doesn't raise a stomp error"
43
+ end
42
44
  end
43
45
  end
44
- end
45
46
 
46
- describe 'the resulting config' do
47
- let(:connection_attrs) { {hosts: [{host: 'my_host'}]} }
48
- subject(:config) { described_class.new(broker, connection_attrs).config }
47
+ describe 'the resulting config' do
48
+ let(:connection_attrs) { { hosts: [{ host: 'my_host' }] } }
49
+ subject(:config) { described_class.new(broker, connection_attrs).config }
49
50
 
50
- its([:connect_headers]) { should eq(:"accept-version" => '1.1,1.2') }
51
- its([:hosts]) { should eq(connection_attrs[:hosts]) }
51
+ it 'has the expected values' do
52
+ is_expected.to eq(
53
+ connect_headers: { :"accept-version" => '1.1,1.2' },
54
+ hosts: connection_attrs[:hosts]
55
+ )
56
+ end
52
57
 
53
- context 'when vhost is specified' do
54
- let(:connection_attrs) { {hosts: [{host: 'my_host'}], vhost: 'my_vhost'} }
58
+ context 'when vhost is specified' do
59
+ let(:connection_attrs) { { hosts: [{ host: 'my_host' }], vhost: 'my_vhost' } }
55
60
 
56
- it { should_not have_key(:vhost) }
57
- its([:connect_headers]) { should eq(:"accept-version" => '1.1,1.2', :"host" => 'my_vhost') }
58
- end
61
+ it 'has the vhost value in the connect headers' do
62
+ is_expected.not_to have_key(:vhost)
63
+ is_expected.to include(connect_headers: { :'accept-version' => '1.1,1.2', host: 'my_vhost' })
64
+ end
65
+ end
59
66
 
60
- context 'when there are things in the connect_headers' do
61
- let(:connection_attrs) { {hosts: [{host: 'my_host'}], connect_headers: {'foo' => 'bar'}} }
67
+ context 'when there are things in the connect_headers' do
68
+ let(:connection_attrs) { { hosts: [{ host: 'my_host' }], connect_headers: { 'foo' => 'bar' } } }
62
69
 
63
- its([:connect_headers]) { should eq(:"accept-version" => '1.1,1.2', 'foo' => 'bar') }
70
+ it 'passes them through' do
71
+ is_expected.to include(connect_headers: { :'accept-version' => '1.1,1.2', 'foo' => 'bar' })
72
+ end
64
73
 
65
- context 'and accept-version is one of the parameters' do
66
- let(:connection_attrs) { {hosts: [{host: 'my_host'}], connect_headers: {'foo' => 'bar', :"accept-version" => 'foo!'}} }
74
+ context 'and accept-version is one of the parameters' do
75
+ let(:connection_attrs) { { hosts: [{ host: 'my_host' }], connect_headers: { 'foo' => 'bar', :"accept-version" => 'foo!' } } }
67
76
 
68
- its([:connect_headers]) { should eq(:"accept-version" => '1.1,1.2', 'foo' => 'bar') }
77
+ it 'overrides it' do
78
+ is_expected.to include(connect_headers: { :'accept-version' => '1.1,1.2', 'foo' => 'bar' })
79
+ end
80
+ end
69
81
  end
70
82
  end
71
83
  end
72
- end
73
84
 
74
- shared_context 'a connected stomp adapter' do
75
- let(:broker) { MessageDriver::Broker.configure(valid_connection_attrs) }
76
- subject(:adapter) { broker.adapter }
85
+ shared_context 'a connected stomp adapter' do
86
+ let(:broker) { MessageDriver::Broker.configure(valid_connection_attrs) }
87
+ subject(:adapter) { broker.adapter }
77
88
 
78
- after do
79
- adapter.stop
89
+ after do
90
+ adapter.stop
91
+ end
80
92
  end
81
- end
82
93
 
83
- it_behaves_like 'an adapter' do
84
- include_context 'a connected stomp adapter'
85
- end
94
+ it_behaves_like 'an adapter' do
95
+ include_context 'a connected stomp adapter'
96
+ end
86
97
 
87
- describe '#new_context' do
88
- include_context 'a connected stomp adapter'
98
+ describe '#new_context' do
99
+ include_context 'a connected stomp adapter'
89
100
 
90
- it 'returns a StompAdapter::StompContext' do
91
- expect(adapter.new_context).to be_a StompAdapter::StompContext
101
+ it 'returns a StompAdapter::StompContext' do
102
+ expect(adapter.new_context).to be_a StompAdapter::StompContext
103
+ end
92
104
  end
93
- end
94
105
 
95
- describe StompAdapter::StompContext do
96
- include_context 'a connected stomp adapter'
97
- subject(:adapter_context) { adapter.new_context }
106
+ describe StompAdapter::StompContext do
107
+ include_context 'a connected stomp adapter'
108
+ subject(:adapter_context) { adapter.new_context }
109
+
110
+ it_behaves_like 'an adapter context'
111
+ it_behaves_like 'transactions are not supported'
112
+ it_behaves_like 'client acks are not supported'
113
+ it_behaves_like 'subscriptions are not supported'
98
114
 
99
- it_behaves_like 'an adapter context'
100
- it_behaves_like 'transactions are not supported'
101
- it_behaves_like 'client acks are not supported'
102
- it_behaves_like 'subscriptions are not supported'
115
+ describe '#create_destination' do
103
116
 
104
- describe '#create_destination' do
117
+ context 'the resulting destination' do
118
+ let(:dest_name) { '/queue/stomp_destination_spec' }
119
+ subject(:destination) { adapter_context.create_destination(dest_name) }
105
120
 
106
- context 'the resulting destination' do
107
- let(:dest_name) { '/queue/stomp_destination_spec' }
108
- subject(:destination) { adapter_context.create_destination(dest_name) }
121
+ it { is_expected.to be_a StompAdapter::Destination }
109
122
 
110
- it { should be_a StompAdapter::Destination }
123
+ it_behaves_like 'a destination'
124
+ include_examples "doesn't support #message_count"
125
+ include_examples "doesn't support #consumer_count"
111
126
 
112
- it_behaves_like 'a destination'
113
- include_examples "doesn't support #message_count"
127
+ describe '#queue_path' do
128
+ it 'equals name' do
129
+ expect(subject.queue_path).to eq(subject.name)
130
+ end
114
131
 
115
- describe 'pop_message' do
116
- context 'when there is a message on the queue' do
117
- let(:body) { 'Testing stomp pop_message' }
118
- before do
119
- destination.publish(body)
132
+ context "when name does not start with '/'" do
133
+ let(:dest_name) { 'stomp_destination_spec' }
134
+ it 'prepends /queue' do
135
+ expect(subject.queue_path).to eq("/queue/#{dest_name}")
136
+ end
120
137
  end
121
138
 
122
- it 'returns the message' do
123
- msg = destination.pop_message
124
- expect(msg).to be_a MessageDriver::Adapters::StompAdapter::Message
125
- expect(msg.body).to eq(body)
139
+ context "when the name does start with a '/'" do
140
+ let(:dest_name) { '/stomp_destination_spec' }
141
+ it 'equals the name' do
142
+ expect(subject.queue_path).to eq(dest_name)
143
+ end
126
144
  end
127
145
  end
128
146
 
129
- context 'when the queue is empty' do
130
- it 'returns nil' do
131
- msg = destination.pop_message
132
- expect(msg).to be_nil
147
+ describe 'pop_message' do
148
+ context 'when there is a message on the queue' do
149
+ let(:body) { 'Testing stomp pop_message' }
150
+ before do
151
+ destination.publish(body)
152
+ end
153
+
154
+ it 'returns the message' do
155
+ msg = destination.pop_message
156
+ expect(msg).to be_a MessageDriver::Adapters::StompAdapter::Message
157
+ expect(msg.body).to eq(body)
158
+ end
159
+ end
160
+
161
+ context 'when the queue is empty' do
162
+ it 'returns nil' do
163
+ msg = destination.pop_message
164
+ expect(msg).to be_nil
165
+ end
133
166
  end
134
167
  end
135
168
  end