message-driver 0.4.0 → 0.5.0

Sign up to get free protection for your applications and to get access to all the features.
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