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
@@ -1,4 +1,5 @@
1
1
  @bunny
2
+ @read_queues_directly
2
3
  Feature: Binding AMQP destinations to exchanges
3
4
  Background:
4
5
  Given the following broker configuration
@@ -1,4 +1,5 @@
1
1
  @bunny
2
+ @read_queues_directly
2
3
  Feature: Declaring AMQP exchanges
3
4
  If you want to create an exchange that doesn't exist on the broker, you can do so by adding
4
5
  the "declare" option to your destination.
@@ -1,4 +1,5 @@
1
1
  @bunny
2
+ @read_queues_directly
2
3
  Feature: Server-Named Destinations
3
4
  AMQP brokers allow you to create queues that are named by the server. Here's
4
5
  how you do it with message_driver.
@@ -29,3 +29,29 @@ Feature: Destination Metadata
29
29
 
30
30
  Then I expect to have no errors
31
31
  And I expect to find 2 messages on :my_queue
32
+
33
+ Scenario: Check the consumer count when the queue has no consumers
34
+ When I execute the following code
35
+ """ruby
36
+ destination = MessageDriver::Client.find_destination(:my_queue)
37
+ expect(destination.consumer_count).to eq(0)
38
+ """
39
+
40
+ Then I expect to have no errors
41
+
42
+ Scenario: Check the consumer count when the queue has consumers
43
+ Given I create some subscriptions
44
+ """ruby
45
+ 3.times do
46
+ MessageDriver::Client.subscribe_with(:my_queue) do |message|
47
+ puts message.inspect
48
+ end
49
+ end
50
+ """
51
+ When I execute the following code
52
+ """ruby
53
+ destination = MessageDriver::Client.find_destination(:my_queue)
54
+ expect(destination.consumer_count).to eq(3)
55
+ """
56
+
57
+ Then I expect to have no errors
@@ -1,5 +1,5 @@
1
1
  @all_adapters
2
- Feature: Stuff gets logged if you set a logger
2
+ Feature: Logging
3
3
 
4
4
  You can configure the logger by setting it on `MessageDriver.logger`.
5
5
  If you don't provide a logger, then an info level logger will be created
@@ -0,0 +1,91 @@
1
+ @bunny
2
+ @in_memory
3
+ Feature: Middleware Basics
4
+
5
+ Middlewares can be used to transform messages that are about to be published
6
+ or that are about to be consumed. This allows for handling of things like
7
+ serializing and deserializing the message body in a way that is transparent to
8
+ your application code.
9
+
10
+ Middlewares are applied in a stack to destinations, much like Rack middleware.
11
+ As a message that is about to be consumed, it starts by coming in the top of
12
+ the middleware stack and works it's way down before it is returned by
13
+ `pop_message` or passed to a consumer.
14
+
15
+ For messages that are being published, they start at the bottom of the stack
16
+ and work their way up until they are finally passed to the underlying driver
17
+ and sent to the message broker.
18
+
19
+ Background:
20
+ Given I am connected to the broker
21
+ And I have a destination :middleware_queue with no messages on it
22
+ And I have a middleware class
23
+ """ruby
24
+ class ExampleMiddleware < MessageDriver::Middleware::Base
25
+ def on_publish(body, headers, properties)
26
+ [body+':about_to_publish', headers, properties]
27
+ end
28
+
29
+ def on_consume(body, headers, properties)
30
+ ['about_to_be_consumed:'+body, headers, properties]
31
+ end
32
+ end
33
+ """
34
+
35
+ Scenario: The middleware stack of a destination is initially empty
36
+ When I execute the following code
37
+ """ruby
38
+ destination = MessageDriver::Client.find_destination(:middleware_queue)
39
+ expect(destination.middleware).to be_empty
40
+ """
41
+ Then I expect to have no errors
42
+
43
+
44
+ Scenario: Adding a piece of middleware to a destination
45
+ When I execute the following code
46
+ """ruby
47
+ destination = MessageDriver::Client.find_destination(:middleware_queue)
48
+ destination.middleware.append ExampleMiddleware
49
+ """
50
+
51
+ Then I expect the following check to pass
52
+ """ruby
53
+ destination = MessageDriver::Client.find_destination(:middleware_queue)
54
+ expect(destination.middleware).to include(an_instance_of(ExampleMiddleware))
55
+ """
56
+
57
+
58
+ Scenario: Middleware is applied to messages as they are published
59
+ When I append middleware "ExampleMiddleware" to :middleware_queue
60
+ And I send the following messages to :middleware_queue
61
+ | body |
62
+ | Test Message 1 |
63
+ | Test Message 2 |
64
+
65
+ Then I expect to find the following 2 messages on :middleware_queue
66
+ | raw_body |
67
+ | Test Message 1:about_to_publish |
68
+ | Test Message 2:about_to_publish |
69
+
70
+
71
+ Scenario: Middleware is applied to messages as they are consumed
72
+ Given I have a destination :dest_queue with no messages on it
73
+
74
+ When I send the following messages to :middleware_queue
75
+ | body |
76
+ | Test Message 1 |
77
+ | Test Message 2 |
78
+ And I append middleware "ExampleMiddleware" to :middleware_queue
79
+ And I create a subscription
80
+ """ruby
81
+ MessageDriver::Client.subscribe_with(:middleware_queue) do |message|
82
+ MessageDriver::Client.publish(:dest_queue, message.body)
83
+ end
84
+ """
85
+ And I let the subscription process
86
+
87
+ Then I expect to find no messages on :middleware_queue
88
+ And I expect to find the following 2 messages on :dest_queue
89
+ | raw_body |
90
+ | about_to_be_consumed:Test Message 1 |
91
+ | about_to_be_consumed:Test Message 2 |
@@ -0,0 +1,60 @@
1
+ @bunny
2
+ @in_memory
3
+ Feature: Middleware Ordering
4
+
5
+ Middleware are applied based on the order they appear in the stack. For
6
+ publish operations, middlewares are applied bottom of the stack to the top.
7
+ For consume operations, middleware are applied from the top of the stack to
8
+ the bottom.
9
+
10
+ Scenario: Apply two middlewares to a destination
11
+ Given I am connected to the broker
12
+ And I have a destination :middleware_queue with no messages on it
13
+ And I have a middleware class
14
+ """ruby
15
+ class MiddlewareOne < MessageDriver::Middleware::Base
16
+ def on_publish(body, headers, properties)
17
+ [body+':publish1', headers, properties]
18
+ end
19
+
20
+ def on_consume(body, headers, properties)
21
+ ['consume1:'+body, headers, properties]
22
+ end
23
+ end
24
+ """
25
+ And I have a middleware class
26
+ """ruby
27
+ class MiddlewareTwo < MessageDriver::Middleware::Base
28
+ def on_publish(body, headers, properties)
29
+ [body+':publish2', headers, properties]
30
+ end
31
+
32
+ def on_consume(body, headers, properties)
33
+ ['consume2:'+body, headers, properties]
34
+ end
35
+ end
36
+ """
37
+ And I append middleware "MiddlewareOne" to :middleware_queue
38
+ And I append middleware "MiddlewareTwo" to :middleware_queue
39
+
40
+ And I have a destination :dest_queue with no messages on it
41
+ And I create a subscription
42
+ """ruby
43
+ MessageDriver::Client.subscribe_with(:middleware_queue) do |message|
44
+ MessageDriver::Client.publish(:dest_queue, message.body)
45
+ end
46
+ """
47
+
48
+ When I send the following messages to :middleware_queue
49
+ | body |
50
+ | Test Message 1 |
51
+ | Test Message 2 |
52
+ | Test Message 3 |
53
+ And I let the subscription process
54
+
55
+ Then I expect to find no messages on :middleware_queue
56
+ And I expect to find the following 3 messages on :dest_queue
57
+ | body |
58
+ | consume1:consume2:Test Message 1:publish1:publish2 |
59
+ | consume1:consume2:Test Message 2:publish1:publish2 |
60
+ | consume1:consume2:Test Message 3:publish1:publish2 |
@@ -0,0 +1,43 @@
1
+ @bunny
2
+ @in_memory
3
+ Feature: Middleware Parameters
4
+
5
+ You can provide parameters to your middleware by passing them as additional
6
+ paremeters to the append and prepend calls.
7
+
8
+ Background:
9
+ Given I am connected to the broker
10
+ And I have a destination :middleware_queue with no messages on it
11
+ And I have a middleware class
12
+ """ruby
13
+ class ParameterizedMiddleware < MessageDriver::Middleware::Base
14
+
15
+ attr_reader :prefix, :seperator
16
+
17
+ def initialize(destination, prefix, seperator)
18
+ super(destination)
19
+ @prefix = prefix
20
+ @seperator = seperator
21
+ end
22
+
23
+ def on_publish(body, headers, properties)
24
+ ["#{prefix}#{seperator}#{body}", headers, properties]
25
+ end
26
+ end
27
+ """
28
+
29
+ Scenario: Adding a parameterized middleware to the stack
30
+ When I execute the following code
31
+ """ruby
32
+ destination = MessageDriver::Client.find_destination(:middleware_queue)
33
+ destination.middleware.append ParameterizedMiddleware, 'pub_pre', ':'
34
+ """
35
+ And I send the following messages to :middleware_queue
36
+ | body |
37
+ | Test Message 1 |
38
+ | Test Message 2 |
39
+
40
+ Then I expect to find the following 2 messages on :middleware_queue
41
+ | raw_body |
42
+ | pub_pre:Test Message 1 |
43
+ | pub_pre:Test Message 2 |
@@ -0,0 +1,85 @@
1
+ @bunny
2
+ @in_memory
3
+ Feature: Middleware with Blocks
4
+
5
+ You can also create middleware by passing a hash with blocks as the input to
6
+ append or prepend
7
+
8
+ Background:
9
+ Given I am connected to the broker
10
+ And I have a destination :middleware_queue with no messages on it
11
+
12
+ Scenario: providing an on_publish block
13
+ When I execute the following code
14
+ """ruby
15
+ destination = MessageDriver::Client.find_destination(:middleware_queue)
16
+ destination.middleware.append on_publish: ->(body, headers, properties) { [body+' published with a block!', headers, properties] }
17
+ """
18
+
19
+ And I send the following messages to :middleware_queue
20
+ | body |
21
+ | Test Message 1 |
22
+ | Test Message 2 |
23
+
24
+ Then I expect to find the following 2 messages on :middleware_queue
25
+ | raw_body |
26
+ | Test Message 1 published with a block! |
27
+ | Test Message 2 published with a block! |
28
+
29
+
30
+ Scenario: providing an on_consume block
31
+ Given I have a destination :dest_queue with no messages on it
32
+
33
+ When I execute the following code
34
+ """ruby
35
+ destination = MessageDriver::Client.find_destination(:middleware_queue)
36
+ destination.middleware.append on_consume: ->(body, headers, properties) { [body+' consumed with a block!', headers, properties] }
37
+ """
38
+
39
+ And I send the following messages to :middleware_queue
40
+ | body |
41
+ | Test Message 1 |
42
+ | Test Message 2 |
43
+
44
+ And I create a subscription
45
+ """ruby
46
+ MessageDriver::Client.subscribe_with(:middleware_queue) do |message|
47
+ MessageDriver::Client.publish(:dest_queue, message.body)
48
+ end
49
+ """
50
+ And I let the subscription process
51
+
52
+ Then I expect to find no messages on :middleware_queue
53
+ And I expect to find the following 2 messages on :dest_queue
54
+ | raw_body |
55
+ | Test Message 1 consumed with a block! |
56
+ | Test Message 2 consumed with a block! |
57
+
58
+ Scenario: providing an on_consume block and an on_publish block
59
+ Given I have a destination :dest_queue with no messages on it
60
+
61
+ When I execute the following code
62
+ """ruby
63
+ destination = MessageDriver::Client.find_destination(:middleware_queue)
64
+ destination.middleware.append(on_consume: ->(body, headers, properties) { [body + ' consumed', headers, properties] },
65
+ on_publish: ->(body, headers, properties) { ['published ' + body, headers, properties] })
66
+ """
67
+
68
+ And I send the following messages to :middleware_queue
69
+ | body |
70
+ | Test Message 1 |
71
+ | Test Message 2 |
72
+
73
+ And I create a subscription
74
+ """ruby
75
+ MessageDriver::Client.subscribe_with(:middleware_queue) do |message|
76
+ MessageDriver::Client.publish(:dest_queue, message.body)
77
+ end
78
+ """
79
+ And I let the subscription process
80
+
81
+ Then I expect to find no messages on :middleware_queue
82
+ And I expect to find the following 2 messages on :dest_queue
83
+ | raw_body |
84
+ | published Test Message 1 consumed |
85
+ | published Test Message 2 consumed |
@@ -7,6 +7,6 @@ Then(/^I expect to find (#{NUMBER}) messages? on the dynamic destination "(#{STR
7
7
  expect(test_runner).to have_no_errors
8
8
  dest = MessageDriver::Client[test_runner.broker_name].dynamic_destination(destination, passive: true)
9
9
  messages = test_runner.fetch_messages(dest)
10
- expect(messages).to have(count).items
10
+ expect(messages.size).to eq(count)
11
11
  expect(messages).to match_message_table(table)
12
12
  end
@@ -12,6 +12,11 @@ Given 'I create a subscription' do |src|
12
12
  expect(test_runner).to have_no_errors
13
13
  end
14
14
 
15
+ Given 'I create some subscriptions' do |src|
16
+ test_runner.run_test_code(src)
17
+ expect(test_runner).to have_no_errors
18
+ end
19
+
15
20
  When 'I cancel the subscription' do
16
21
  test_runner.run_test_code('@subscription.unsubscribe')
17
22
  step 'I allow for processing'
@@ -0,0 +1,10 @@
1
+ Given('I have a middleware class') do |src|
2
+ write_file('feature_middleware.rb', src)
3
+ in_current_dir { load './feature_middleware.rb' }
4
+ end
5
+
6
+ When(/^I append middleware "(.*?)" to (#{STRING_OR_SYM})$/) do |class_name, dest_name|
7
+ klass = Object.const_get(class_name)
8
+ destination = test_runner.fetch_destination(dest_name)
9
+ destination.middleware.append klass
10
+ end
@@ -59,14 +59,14 @@ end
59
59
  Then(/^I expect to find (#{NUMBER}) messages? on (#{STRING_OR_SYM})$/) do |count, destination|
60
60
  expect(test_runner).to have_no_errors
61
61
  messages = test_runner.fetch_messages(destination)
62
- expect(messages).to have(count).items, "expected #{count} messages, but got these instead: #{messages.map(&:body)}"
62
+ expect(messages.size).to eq(count), "expected #{count} messages, but got these instead: #{messages.map(&:body)}"
63
63
  end
64
64
 
65
65
  Then(/^I expect to find the following (#{NUMBER}) messages? on (#{STRING_OR_SYM})$/) do |count, destination, table|
66
66
  expect(test_runner).to have_no_errors
67
67
  messages = test_runner.fetch_messages(destination)
68
68
  expect(messages).to match_message_table(table)
69
- expect(messages).to have(count).items
69
+ expect(messages.size).to eq(count)
70
70
  end
71
71
 
72
72
  Then(/^I expect to find the following message on (#{STRING_OR_SYM})$/) do |destination, table|
@@ -99,3 +99,11 @@ end
99
99
  Before do |current_scenario|
100
100
  test_runner.current_feature_file = current_scenario.feature.file
101
101
  end
102
+
103
+ Before '@read_queues_directly' do
104
+ test_runner.provider.read_queues_directly = true
105
+ end
106
+
107
+ After '@read_queues_directly' do
108
+ test_runner.provider.read_queues_directly = false
109
+ end
@@ -1,11 +1,12 @@
1
- require 'coveralls'
2
- Coveralls.wear!
3
-
1
+ ENV['COMMAND_NAME'] = 'features'
2
+ require File.join(File.dirname(__FILE__), '..', '..', 'test_lib', 'coverage')
4
3
  require File.join(File.dirname(__FILE__), '..', '..', 'test_lib', 'broker_config')
5
4
 
6
5
  require 'aruba/cucumber'
7
6
  require 'message_driver'
8
7
 
8
+ BrokerConfig.setup_provider
9
+
9
10
  After do
10
11
  MessageDriver::Broker.reset
11
12
  end
@@ -35,7 +35,7 @@ module FirewallHelper
35
35
  def run_commands(step)
36
36
  COMMANDS[os][step].each do |cmd|
37
37
  result = system(cmd)
38
- raise "command `#{cmd}` failed!" unless result
38
+ fail "command `#{cmd}` failed!" unless result
39
39
  end
40
40
  end
41
41
 
@@ -13,10 +13,11 @@ RSpec::Matchers.define :match_message_table do |expected_tbl|
13
13
 
14
14
  match do |messages|
15
15
  @actual = messages_to_hash(messages)
16
- @actual == expected_hash
16
+ expect(@actual).to match_array(expected_hash)
17
+ true
17
18
  end
18
19
 
19
- failure_message_for_should do |_|
20
+ failure_message do |_|
20
21
  "expected #{expected_hash} and got #{@actual}"
21
22
  end
22
23