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.
- checksums.yaml +4 -4
- data/.coveralls.yml +2 -0
- data/.rubocop.yml +26 -2
- data/.rubocop_todo.yml +15 -123
- data/.travis.yml +2 -1
- data/CHANGELOG.md +10 -1
- data/Gemfile +15 -6
- data/Rakefile +23 -12
- data/ci/reset_vhost +8 -3
- data/ci/travis_setup +0 -3
- data/features/.nav +6 -1
- data/features/CHANGELOG.md +10 -1
- data/features/amqp_specific_features/binding_amqp_destinations.feature +1 -0
- data/features/amqp_specific_features/declaring_amqp_exchanges.feature +1 -0
- data/features/amqp_specific_features/server_named_destinations.feature +1 -0
- data/features/destination_metadata.feature +26 -0
- data/features/logging.feature +1 -1
- data/features/middleware/middleware_basics.feature +91 -0
- data/features/middleware/middleware_ordering.feature +60 -0
- data/features/middleware/middleware_parameters.feature +43 -0
- data/features/middleware/middleware_with_blocks.feature +85 -0
- data/features/step_definitions/dynamic_destinations_steps.rb +1 -1
- data/features/step_definitions/message_consumers_steps.rb +5 -0
- data/features/step_definitions/middleware_steps.rb +10 -0
- data/features/step_definitions/steps.rb +10 -2
- data/features/support/env.rb +4 -3
- data/features/support/firewall_helper.rb +1 -1
- data/features/support/message_table_matcher.rb +3 -2
- data/features/support/no_error_matcher.rb +2 -2
- data/features/support/test_runner.rb +11 -57
- data/features/support/transforms.rb +12 -10
- data/lib/message_driver.rb +3 -1
- data/lib/message_driver/adapters/base.rb +11 -11
- data/lib/message_driver/adapters/bunny_adapter.rb +189 -132
- data/lib/message_driver/adapters/in_memory_adapter.rb +51 -34
- data/lib/message_driver/adapters/stomp_adapter.rb +22 -23
- data/lib/message_driver/broker.rb +21 -16
- data/lib/message_driver/client.rb +15 -16
- data/lib/message_driver/destination.rb +26 -8
- data/lib/message_driver/message.rb +5 -4
- data/lib/message_driver/middleware.rb +8 -0
- data/lib/message_driver/middleware/base.rb +19 -0
- data/lib/message_driver/middleware/block_middleware.rb +33 -0
- data/lib/message_driver/middleware/middleware_stack.rb +61 -0
- data/lib/message_driver/subscription.rb +2 -2
- data/lib/message_driver/version.rb +1 -1
- data/message-driver.gemspec +3 -4
- data/spec/integration/bunny/amqp_integration_spec.rb +21 -82
- data/spec/integration/bunny/bunny_adapter_spec.rb +288 -268
- data/spec/integration/in_memory/in_memory_adapter_spec.rb +93 -90
- data/spec/integration/stomp/stomp_adapter_spec.rb +126 -93
- data/spec/spec_helper.rb +11 -9
- data/spec/support/shared/adapter_examples.rb +1 -1
- data/spec/support/shared/client_ack_examples.rb +4 -4
- data/spec/support/shared/context_examples.rb +6 -4
- data/spec/support/shared/destination_examples.rb +54 -14
- data/spec/support/shared/subscription_examples.rb +33 -26
- data/spec/support/shared/transaction_examples.rb +12 -12
- data/spec/support/utils.rb +1 -1
- data/spec/units/message_driver/adapters/base_spec.rb +42 -40
- data/spec/units/message_driver/broker_spec.rb +38 -38
- data/spec/units/message_driver/client_spec.rb +87 -87
- data/spec/units/message_driver/destination_spec.rb +16 -11
- data/spec/units/message_driver/message_spec.rb +96 -70
- data/spec/units/message_driver/middleware/base_spec.rb +30 -0
- data/spec/units/message_driver/middleware/block_middleware_spec.rb +82 -0
- data/spec/units/message_driver/middleware/middleware_stack_spec.rb +165 -0
- data/spec/units/message_driver/subscription_spec.rb +18 -16
- data/test_lib/broker_config.rb +21 -5
- data/test_lib/coverage.rb +11 -0
- data/test_lib/provider/base.rb +59 -0
- data/test_lib/provider/in_memory.rb +6 -0
- data/test_lib/provider/rabbitmq.rb +67 -0
- 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
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
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
|
-
|
28
|
-
describe 'the resulting destination' do
|
29
|
-
subject(:destination) { adapter.create_destination('my_test_dest') }
|
17
|
+
it_behaves_like 'an adapter'
|
30
18
|
|
31
|
-
|
19
|
+
describe 'InMemoryAdapter::InMemoryContext' do
|
20
|
+
subject(:adapter_context) { adapter.new_context }
|
32
21
|
|
33
|
-
it_behaves_like '
|
34
|
-
|
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
|
-
|
38
|
-
|
39
|
-
|
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
|
-
|
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
|
-
|
34
|
+
it_behaves_like 'a destination'
|
35
|
+
include_examples 'supports #message_count'
|
36
|
+
include_examples 'supports #consumer_count'
|
37
|
+
end
|
55
38
|
|
56
|
-
destinations
|
57
|
-
|
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
|
-
|
62
|
-
|
63
|
-
|
64
|
-
|
65
|
-
|
66
|
-
|
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
|
-
|
56
|
+
adapter.reset_after_tests
|
69
57
|
|
70
|
-
|
71
|
-
|
58
|
+
destinations.each do |destination|
|
59
|
+
expect(destination.message_count).to eq(0)
|
60
|
+
end
|
72
61
|
end
|
73
62
|
|
74
|
-
|
75
|
-
|
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
|
-
|
78
|
-
|
79
|
-
|
80
|
-
|
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
|
-
|
93
|
-
|
94
|
-
|
95
|
-
|
96
|
-
|
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
|
-
|
100
|
-
|
101
|
-
|
102
|
-
|
103
|
-
|
104
|
-
|
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
|
-
|
108
|
-
|
109
|
-
|
110
|
-
|
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
|
-
|
114
|
-
|
115
|
-
|
116
|
-
|
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
|
-
|
122
|
-
|
123
|
+
describe 'subscribing a consumer' do
|
124
|
+
let(:destination) { adapter.create_destination(:my_queue) }
|
123
125
|
|
124
|
-
|
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
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
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
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
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
|
-
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
|
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
|
-
|
38
|
-
|
39
|
-
|
40
|
-
|
41
|
-
|
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
|
-
|
47
|
-
|
48
|
-
|
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
|
-
|
51
|
-
|
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
|
-
|
54
|
-
|
58
|
+
context 'when vhost is specified' do
|
59
|
+
let(:connection_attrs) { { hosts: [{ host: 'my_host' }], vhost: 'my_vhost' } }
|
55
60
|
|
56
|
-
|
57
|
-
|
58
|
-
|
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
|
-
|
61
|
-
|
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
|
-
|
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
|
-
|
66
|
-
|
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
|
-
|
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
|
-
|
75
|
-
|
76
|
-
|
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
|
-
|
79
|
-
|
89
|
+
after do
|
90
|
+
adapter.stop
|
91
|
+
end
|
80
92
|
end
|
81
|
-
end
|
82
93
|
|
83
|
-
|
84
|
-
|
85
|
-
|
94
|
+
it_behaves_like 'an adapter' do
|
95
|
+
include_context 'a connected stomp adapter'
|
96
|
+
end
|
86
97
|
|
87
|
-
|
88
|
-
|
98
|
+
describe '#new_context' do
|
99
|
+
include_context 'a connected stomp adapter'
|
89
100
|
|
90
|
-
|
91
|
-
|
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
|
-
|
96
|
-
|
97
|
-
|
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
|
-
|
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
|
-
|
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
|
-
|
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
|
-
|
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
|
-
|
113
|
-
|
127
|
+
describe '#queue_path' do
|
128
|
+
it 'equals name' do
|
129
|
+
expect(subject.queue_path).to eq(subject.name)
|
130
|
+
end
|
114
131
|
|
115
|
-
|
116
|
-
|
117
|
-
|
118
|
-
|
119
|
-
|
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
|
-
|
123
|
-
|
124
|
-
|
125
|
-
|
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
|
-
|
130
|
-
|
131
|
-
|
132
|
-
|
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
|