synapse-core 0.4.0 → 0.5.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (163) hide show
  1. data/lib/synapse/command/async_command_bus.rb +41 -0
  2. data/lib/synapse/command/command_callback.rb +49 -2
  3. data/lib/synapse/command/command_handler.rb +2 -0
  4. data/lib/synapse/command/filters/validation.rb +2 -2
  5. data/lib/synapse/command/gateway/interval_retry_scheduler.rb +75 -0
  6. data/lib/synapse/command/gateway/retry_scheduler.rb +26 -0
  7. data/lib/synapse/command/gateway/retrying_callback.rb +46 -0
  8. data/lib/synapse/command/gateway.rb +56 -11
  9. data/lib/synapse/command/interceptor_chain.rb +2 -2
  10. data/lib/synapse/command/interceptors/serialization.rb +2 -2
  11. data/lib/synapse/command/message.rb +16 -0
  12. data/lib/synapse/command/simple_command_bus.rb +6 -2
  13. data/lib/synapse/command/wiring.rb +3 -3
  14. data/lib/synapse/command.rb +7 -1
  15. data/lib/synapse/common/concurrency/identifier_lock.rb +2 -17
  16. data/lib/synapse/common/concurrency/public_lock.rb +8 -9
  17. data/lib/synapse/common/errors.rb +1 -1
  18. data/lib/synapse/common/message.rb +2 -6
  19. data/lib/synapse/common.rb +9 -0
  20. data/lib/synapse/configuration/component/command_bus/async_command_bus.rb +41 -0
  21. data/lib/synapse/configuration/component/command_bus/gateway.rb +38 -0
  22. data/lib/synapse/configuration/component/command_bus/simple_command_bus.rb +81 -0
  23. data/lib/synapse/configuration/component/command_bus.rb +35 -0
  24. data/lib/synapse/configuration/component/event_bus/simple_event_bus.rb +41 -0
  25. data/lib/synapse/configuration/component/event_bus.rb +15 -0
  26. data/lib/synapse/configuration/component/event_sourcing/repository.rb +62 -0
  27. data/lib/synapse/configuration/component/event_sourcing.rb +15 -0
  28. data/lib/synapse/configuration/component/repository/locking_repository.rb +85 -0
  29. data/lib/synapse/configuration/component/repository/simple_repository.rb +31 -0
  30. data/lib/synapse/configuration/component/repository.rb +15 -0
  31. data/lib/synapse/configuration/component/serialization/converter_factory.rb +50 -0
  32. data/lib/synapse/configuration/component/serialization/serializer.rb +90 -0
  33. data/lib/synapse/configuration/component/serialization.rb +25 -0
  34. data/lib/synapse/configuration/component/uow/unit_factory.rb +51 -0
  35. data/lib/synapse/configuration/component/uow.rb +22 -0
  36. data/lib/synapse/configuration/component/upcasting/upcaster_chain.rb +61 -0
  37. data/lib/synapse/configuration/component/upcasting.rb +15 -0
  38. data/lib/synapse/configuration/container.rb +80 -0
  39. data/lib/synapse/configuration/container_builder.rb +108 -0
  40. data/lib/synapse/configuration/definition.rb +31 -0
  41. data/lib/synapse/configuration/definition_builder.rb +138 -0
  42. data/lib/synapse/configuration/dependent.rb +31 -0
  43. data/lib/synapse/configuration/ext.rb +35 -0
  44. data/lib/synapse/configuration.rb +32 -0
  45. data/lib/synapse/domain/aggregate_root.rb +25 -26
  46. data/lib/synapse/domain/event_container.rb +5 -5
  47. data/lib/synapse/domain/message.rb +16 -0
  48. data/lib/synapse/event_bus/event_listener.rb +2 -0
  49. data/lib/synapse/event_bus/event_publisher.rb +19 -0
  50. data/lib/synapse/event_bus/simple_event_bus.rb +21 -30
  51. data/lib/synapse/event_bus/wiring.rb +5 -4
  52. data/lib/synapse/event_bus.rb +1 -1
  53. data/lib/synapse/event_sourcing/aggregate_root.rb +6 -4
  54. data/lib/synapse/process_manager/container_resource_injector.rb +18 -0
  55. data/lib/synapse/process_manager/pessimistic_lock_manager.rb +1 -1
  56. data/lib/synapse/process_manager/process.rb +4 -0
  57. data/lib/synapse/process_manager/process_factory.rb +3 -3
  58. data/lib/synapse/process_manager/repository/in_memory.rb +1 -1
  59. data/lib/synapse/process_manager/wiring/process.rb +6 -6
  60. data/lib/synapse/process_manager/wiring/process_manager.rb +8 -8
  61. data/lib/synapse/process_manager.rb +2 -0
  62. data/lib/synapse/repository/locking.rb +5 -3
  63. data/lib/synapse/repository/optimistic_lock_manager.rb +2 -7
  64. data/lib/synapse/repository/pessimistic_lock_manager.rb +3 -3
  65. data/lib/synapse/repository/repository.rb +2 -2
  66. data/lib/synapse/repository/simple_repository.rb +69 -0
  67. data/lib/synapse/repository.rb +1 -0
  68. data/lib/synapse/serialization/converter/chain.rb +2 -2
  69. data/lib/synapse/serialization/converter_factory.rb +2 -2
  70. data/lib/synapse/serialization/lazy_object.rb +2 -2
  71. data/lib/synapse/serialization/message/serialization_aware_message.rb +3 -3
  72. data/lib/synapse/serialization/message/serialized_message.rb +6 -10
  73. data/lib/synapse/serialization/message/serialized_message_builder.rb +4 -4
  74. data/lib/synapse/serialization/message/serialized_object_cache.rb +2 -2
  75. data/lib/synapse/serialization/message/serializer.rb +2 -2
  76. data/lib/synapse/serialization/revision_resolver.rb +3 -3
  77. data/lib/synapse/serialization/serialized_object.rb +2 -2
  78. data/lib/synapse/serialization/serialized_type.rb +2 -2
  79. data/lib/synapse/serialization/serializer/attribute.rb +2 -2
  80. data/lib/synapse/serialization/serializer/marshal.rb +2 -2
  81. data/lib/synapse/serialization/serializer/oj.rb +2 -2
  82. data/lib/synapse/serialization/serializer/ox.rb +2 -2
  83. data/lib/synapse/serialization/serializer.rb +2 -2
  84. data/lib/synapse/uow/factory.rb +2 -2
  85. data/lib/synapse/uow/listener_collection.rb +2 -2
  86. data/lib/synapse/uow/nesting.rb +9 -2
  87. data/lib/synapse/uow/provider.rb +2 -2
  88. data/lib/synapse/uow/uow.rb +8 -2
  89. data/lib/synapse/upcasting/{chain.rb → upcaster_chain.rb} +0 -0
  90. data/lib/synapse/upcasting.rb +1 -1
  91. data/lib/synapse/version.rb +1 -1
  92. data/lib/synapse/wiring/message_wiring.rb +31 -0
  93. data/lib/synapse.rb +2 -14
  94. data/test/auditing/data_provider_test.rb +2 -2
  95. data/test/auditing/dispatch_interceptor_test.rb +1 -1
  96. data/test/auditing/unit_listener_test.rb +3 -3
  97. data/test/command/async_command_bus_test.rb +49 -0
  98. data/test/command/duplication_test.rb +2 -2
  99. data/test/command/gateway/interval_retry_scheduler_test.rb +42 -0
  100. data/test/command/gateway/retrying_callback_test.rb +57 -0
  101. data/test/command/gateway_test.rb +41 -7
  102. data/test/command/interceptor_chain_test.rb +1 -1
  103. data/test/command/message_test.rb +17 -0
  104. data/test/command/serialization_test.rb +2 -2
  105. data/test/command/simple_command_bus_test.rb +7 -7
  106. data/test/command/validation_test.rb +3 -3
  107. data/test/command/wiring_test.rb +3 -3
  108. data/test/common/concurrency/identifier_lock_test.rb +2 -13
  109. data/test/common/concurrency/public_lock_test.rb +6 -6
  110. data/test/{duplication_test.rb → common/duplication_test.rb} +3 -3
  111. data/test/configuration/component/command_bus/async_command_bus_test.rb +36 -0
  112. data/test/configuration/component/command_bus/simple_command_bus_test.rb +57 -0
  113. data/test/configuration/component/event_bus/simple_event_bus_test.rb +58 -0
  114. data/test/configuration/component/serialization/converter_factory_test.rb +48 -0
  115. data/test/configuration/component/serialization/serializer_test.rb +78 -0
  116. data/test/configuration/component/uow/unit_factory_test.rb +46 -0
  117. data/test/configuration/container_builder_test.rb +47 -0
  118. data/test/configuration/container_test.rb +88 -0
  119. data/test/configuration/definition_builder_test.rb +126 -0
  120. data/test/configuration/definition_test.rb +41 -0
  121. data/test/configuration/dependent_test.rb +30 -0
  122. data/test/configuration/ext_test.rb +19 -0
  123. data/test/configuration/fixtures/dependent.rb +10 -0
  124. data/test/domain/aggregate_root_test.rb +5 -5
  125. data/test/domain/message_test.rb +15 -3
  126. data/test/domain/stream_test.rb +2 -2
  127. data/test/event_bus/publisher_test.rb +29 -0
  128. data/test/event_bus/wiring_test.rb +1 -1
  129. data/test/event_sourcing/aggregate_factory_test.rb +12 -6
  130. data/test/event_sourcing/aggregate_root_test.rb +4 -4
  131. data/test/event_sourcing/entity_test.rb +10 -9
  132. data/test/event_sourcing/repository_test.rb +6 -6
  133. data/test/event_sourcing/storage_listener_test.rb +8 -4
  134. data/test/event_store/in_memory_test.rb +3 -3
  135. data/test/process_manager/container_resource_injector_test.rb +19 -0
  136. data/test/process_manager/correlation_set_test.rb +2 -2
  137. data/test/process_manager/correlation_test.rb +2 -2
  138. data/test/process_manager/in_memory_test.rb +3 -3
  139. data/test/process_manager/process_factory_test.rb +2 -2
  140. data/test/process_manager/process_test.rb +3 -3
  141. data/test/process_manager/simple_process_manager_test.rb +5 -5
  142. data/test/process_manager/wiring/process_manager_test.rb +4 -4
  143. data/test/process_manager/wiring/process_test.rb +2 -2
  144. data/test/repository/locking_test.rb +4 -4
  145. data/test/repository/optimistic_test.rb +2 -2
  146. data/test/repository/pessimistic_test.rb +1 -1
  147. data/test/repository/simple_repository_test.rb +79 -0
  148. data/test/support/countdown_latch.rb +18 -0
  149. data/test/test_helper.rb +6 -3
  150. data/test/upcasting/data_test.rb +31 -0
  151. metadata +84 -25
  152. data/lib/synapse/event_bus/event_listener_proxy.rb +0 -12
  153. data/lib/synapse/partitioning/memory_queue_reader.rb +0 -31
  154. data/lib/synapse/partitioning/memory_queue_writer.rb +0 -19
  155. data/lib/synapse/partitioning/message_receipt.rb +0 -25
  156. data/lib/synapse/partitioning/packing/json_packer.rb +0 -93
  157. data/lib/synapse/partitioning/packing/json_unpacker.rb +0 -83
  158. data/lib/synapse/partitioning/packing.rb +0 -27
  159. data/lib/synapse/partitioning/queue_reader.rb +0 -32
  160. data/lib/synapse/partitioning/queue_writer.rb +0 -17
  161. data/lib/synapse/partitioning.rb +0 -18
  162. data/test/partitioning/memory_test.rb +0 -34
  163. data/test/partitioning/packing/json_test.rb +0 -62
@@ -29,6 +29,6 @@ module Synapse
29
29
  def native_content_type
30
30
  String
31
31
  end
32
- end
33
- end
32
+ end # OjSerializer
33
+ end # Serialization
34
34
  end
@@ -26,6 +26,6 @@ module Synapse
26
26
  def native_content_type
27
27
  String
28
28
  end
29
- end
30
- end
29
+ end # OxSerializer
30
+ end # Serialization
31
31
  end
@@ -95,6 +95,6 @@ module Synapse
95
95
  def convert(original, source_type, target_type)
96
96
  converter_factory.converter(source_type, target_type).convert_content(original)
97
97
  end
98
- end
99
- end
98
+ end # Serializer
99
+ end # Serialization
100
100
  end
@@ -23,6 +23,6 @@ module Synapse
23
23
  unit.start
24
24
  end
25
25
  end
26
- end
27
- end
26
+ end # UnitOfWorkFactory
27
+ end # UnitOfWork
28
28
  end
@@ -146,6 +146,6 @@ module Synapse
146
146
 
147
147
  @logger.debug 'Listeners successfully notified'
148
148
  end
149
- end
150
- end
149
+ end # UnitOfWorkListenerCollection
150
+ end # UnitOfWork
151
151
  end
@@ -26,6 +26,7 @@ module Synapse
26
26
  # resources it acquired. The effectively means that a rollback is done if the unit of work
27
27
  # failed to commit.
28
28
  #
29
+ # @api public
29
30
  # @raise [RuntimeError] If unit of work hasn't been started yet
30
31
  # @return [undefined]
31
32
  def commit
@@ -61,6 +62,7 @@ module Synapse
61
62
  # Any buffered events and registered aggregates are discarded and any registered unit of work
62
63
  # listeners are notified of the rollback.
63
64
  #
65
+ # @api public
64
66
  # @param [Error] cause
65
67
  # @return [undefined]
66
68
  def rollback(cause = nil)
@@ -79,6 +81,7 @@ module Synapse
79
81
 
80
82
  # Starts the unit of work, preparing it for aggregate registration
81
83
  #
84
+ # @api public
82
85
  # @raise [RuntimeError] If unit of work has already been started
83
86
  # @return [undefined]
84
87
  def start
@@ -105,6 +108,8 @@ module Synapse
105
108
  end
106
109
 
107
110
  # Returns true if this unit of work has been started
111
+ #
112
+ # @api public
108
113
  # @return [Boolean]
109
114
  def started?
110
115
  @started
@@ -224,6 +229,8 @@ module Synapse
224
229
 
225
230
  # Listener that allows a nested unit of work to properly operate within in a unit of
226
231
  # work that is not aware of nesting
232
+ #
233
+ # @api private
227
234
  class OuterCommitUnitOfWorkListener < UnitOfWorkListener
228
235
  # @param [UnitOfWork] inner_unit
229
236
  # @param [UnitOfWorkProvider] provider
@@ -257,6 +264,6 @@ module Synapse
257
264
  def on_cleanup(outer_unit)
258
265
  @inner_unit.perform_cleanup
259
266
  end
260
- end
261
- end
267
+ end # NestableUnitOfWork
268
+ end # UnitOfWork
262
269
  end
@@ -66,6 +66,6 @@ module Synapse
66
66
  rescue KeyError
67
67
  @threads.store Thread.current, Array.new
68
68
  end
69
- end
70
- end
69
+ end # UnitOfWorkProvider
70
+ end # UnitOfWork
71
71
  end
@@ -13,6 +13,8 @@ module Synapse
13
13
  end
14
14
 
15
15
  # Returns true if this unit of work is bound to a transaction
16
+ #
17
+ # @api public
16
18
  # @return [Boolean]
17
19
  def transactional?
18
20
  !!@transaction_manager
@@ -20,6 +22,7 @@ module Synapse
20
22
 
21
23
  # Registers a listener that is notified of state changes in this unit of work
22
24
  #
25
+ # @api public
23
26
  # @param [UnitOfWorkListener] listener
24
27
  # @return [undefined]
25
28
  def register_listener(listener)
@@ -38,6 +41,7 @@ module Synapse
38
41
  # and with the same identifier, that aggregate will be returned instead of the given
39
42
  # aggregate.
40
43
  #
44
+ # @api public
41
45
  # @param [AggregateRoot] aggregate
42
46
  # @param [EventBus] event_bus
43
47
  # @param [StorageListener] storage_listener
@@ -60,6 +64,7 @@ module Synapse
60
64
  # Buffers an event for publication to the given event bus until this unit of work is
61
65
  # committed
62
66
  #
67
+ # @api public
63
68
  # @param [EventMessage] event
64
69
  # @param [EventBus] event_bus
65
70
  # @return [EventMessage] The event that will be published to the event bus
@@ -78,6 +83,7 @@ module Synapse
78
83
 
79
84
  # Sets the transaction manager that will be used by this unit of work
80
85
  #
86
+ # @api public
81
87
  # @raise [RuntimeError] If unit of work has been started
82
88
  # @param [TransactionManager] transaction_manager
83
89
  # @return [undefined]
@@ -173,6 +179,6 @@ module Synapse
173
179
  end
174
180
  end
175
181
  end
176
- end
177
- end
182
+ end # UnitOfWork
183
+ end # UnitOfWork
178
184
  end
@@ -1,5 +1,5 @@
1
1
  require 'synapse/upcasting/context'
2
- require 'synapse/upcasting/chain'
3
2
  require 'synapse/upcasting/data'
3
+ require 'synapse/upcasting/upcaster_chain'
4
4
  require 'synapse/upcasting/upcaster'
5
5
  require 'synapse/upcasting/single_upcaster'
@@ -1,3 +1,3 @@
1
1
  module Synapse
2
- VERSION = '0.4.0'
2
+ VERSION = '0.5.1'
3
3
  end
@@ -1,6 +1,10 @@
1
1
  module Synapse
2
2
  module Wiring
3
3
  # Base mixin that make it easier to wire handlers to their respective types
4
+ #
5
+ # It is recommended to use mixins more specific to the component being implemented, like
6
+ # wiring command handlers, event listeners, event-sourced members, or processes.
7
+ #
4
8
  # @abstract
5
9
  module MessageWiring
6
10
  extend ActiveSupport::Concern
@@ -14,6 +18,33 @@ module Synapse
14
18
  end
15
19
 
16
20
  module ClassMethods
21
+ # Wires a message handler to messages with payload of the given type
22
+ #
23
+ # @example
24
+ # wire CashWithdrawnEvent do |event|
25
+ # # do something with the event
26
+ # end
27
+ #
28
+ # @example
29
+ # wire CashWithdrawnEvent :to => :on_withdraw
30
+ #
31
+ # def on_withdraw(event)
32
+ # # do something with the event
33
+ # end
34
+ #
35
+ # Certain components that use message handling have different options that can be set
36
+ # on wires, like wiring processes.
37
+ #
38
+ # @example
39
+ # wire SellTransactionStartedEvent, :start => true, :correlate => :transaction_id do
40
+ # # do something with the event
41
+ # end
42
+ #
43
+ # @api public
44
+ # @param [Class] type
45
+ # @param [Object...] args
46
+ # @param [Proc] block
47
+ # @return [undefined]
17
48
  def wire(type, *args, &block)
18
49
  options = args.extract_options!
19
50
 
data/lib/synapse.rb CHANGED
@@ -3,24 +3,12 @@ require 'active_support/core_ext'
3
3
  require 'logging'
4
4
  require 'set'
5
5
 
6
+ require 'synapse/common'
6
7
  require 'synapse/version'
7
8
 
8
- require 'synapse/common/errors'
9
- require 'synapse/common/identifier'
10
- require 'synapse/common/message'
11
- require 'synapse/common/message_builder'
12
-
13
- require 'synapse/common/concurrency/identifier_lock'
14
- require 'synapse/common/concurrency/public_lock'
15
-
16
9
  module Synapse
17
10
  extend ActiveSupport::Autoload
18
11
 
19
- autoload_at 'synapse/common/duplication' do
20
- autoload :DuplicationError
21
- autoload :DuplicationRecorder
22
- end
23
-
24
12
  eager_autoload do
25
13
  # Common components
26
14
  autoload :Command
@@ -34,9 +22,9 @@ module Synapse
34
22
 
35
23
  # Optional components
36
24
  autoload :Auditing
25
+ autoload :Configuration
37
26
  autoload :EventSourcing
38
27
  autoload :EventStore
39
- autoload :Partitioning
40
28
  autoload :ProcessManager
41
29
  autoload :Upcasting
42
30
  end
@@ -4,7 +4,7 @@ module Synapse
4
4
  module Auditing
5
5
 
6
6
  class CommandMetadataProviderTest < Test::Unit::TestCase
7
- def test_provide_data_for
7
+ should 'provide the metadata from a command for auditing' do
8
8
  data = { foo: 0 }
9
9
 
10
10
  provider = CommandMetadataProvider.new
@@ -17,7 +17,7 @@ module Synapse
17
17
  end
18
18
 
19
19
  class CorrelationDataProviderTest < Test::Unit::TestCase
20
- def test_provide_data_for
20
+ should 'provide the identifier of a command for auditing' do
21
21
  provider = CorrelationDataProvider.new
22
22
  command = Command::CommandMessage.build
23
23
 
@@ -4,7 +4,7 @@ module Synapse
4
4
  module Auditing
5
5
 
6
6
  class AuditingDispatchInterceptorTest < Test::Unit::TestCase
7
- def test_intercept
7
+ should 'register a unit of work listener for auditing' do
8
8
  return_value = Object.new
9
9
  chain = Object.new
10
10
  command = Object.new
@@ -4,7 +4,7 @@ module Synapse
4
4
  module Auditing
5
5
 
6
6
  class AuditingUnitOfWorkListenerTest < Test::Unit::TestCase
7
- def test_on_event_registered
7
+ should 'supplement events with auditing data' do
8
8
  data_provider_a = Object.new
9
9
  data_provider_b = Object.new
10
10
 
@@ -30,7 +30,7 @@ module Synapse
30
30
  assert listener.recorded_events.include? event
31
31
  end
32
32
 
33
- def test_after_commit
33
+ should 'notify the audit logger of success after the unit of work is committed' do
34
34
  logger = Object.new
35
35
 
36
36
  command = Object.new
@@ -48,7 +48,7 @@ module Synapse
48
48
  listener.after_commit Object.new
49
49
  end
50
50
 
51
- def test_on_rollback
51
+ should 'notify the audit logger of failure when a unit of work is rolled back' do
52
52
  logger = Object.new
53
53
 
54
54
  command = Object.new
@@ -0,0 +1,49 @@
1
+ require 'test_helper'
2
+ require 'support/countdown_latch'
3
+
4
+ module Synapse
5
+ module Command
6
+ class AsynchronousCommandBusTest < Test::Unit::TestCase
7
+ def setup
8
+ unit_provider = UnitOfWork::UnitOfWorkProvider.new
9
+ unit_factory = UnitOfWork::UnitOfWorkFactory.new unit_provider
10
+
11
+ @bus = AsynchronousCommandBus.new unit_factory
12
+ @bus.thread_pool = Thread.pool 2
13
+ end
14
+
15
+ should 'be able to dispatch commands asynchronously using a thread pool' do
16
+ x = 5 # Number of commands to dispatch
17
+
18
+ command = CommandMessage.as_message TestCommand.new
19
+ handler = TestAsyncHandler.new x
20
+
21
+ @bus.subscribe TestCommand, handler
22
+
23
+ x.times do
24
+ @bus.dispatch command
25
+ end
26
+
27
+ wait_until do
28
+ handler.latch.count == 0
29
+ end
30
+
31
+ @bus.shutdown
32
+ end
33
+ end
34
+
35
+ class TestAsyncHandler
36
+ attr_reader :latch
37
+
38
+ def initialize(x)
39
+ @latch = CountdownLatch.new x
40
+ end
41
+
42
+ def handle(command, unit)
43
+ @latch.countdown!
44
+ end
45
+ end
46
+
47
+ class TestCommand; end
48
+ end
49
+ end
@@ -3,7 +3,7 @@ require 'test_helper'
3
3
  module Synapse
4
4
  module Command
5
5
  class DuplicationFilterTest < Test::Unit::TestCase
6
- def test_filter
6
+ should 'record commands so that duplication can be detected' do
7
7
  recorder = DuplicationRecorder.new
8
8
  filter = DuplicationFilter.new recorder
9
9
 
@@ -17,7 +17,7 @@ module Synapse
17
17
  end
18
18
 
19
19
  class DuplicationCleanupInterceptorTest < Test::Unit::TestCase
20
- def test_intercept
20
+ should 'forget recorded commands only if a transient error occurs' do
21
21
  recorder = DuplicationRecorder.new
22
22
  interceptor = DuplicationCleanupInterceptor.new recorder
23
23
 
@@ -0,0 +1,42 @@
1
+ require 'test_helper'
2
+ require 'eventmachine'
3
+
4
+ module Synapse
5
+ module Command
6
+ class IntervalRetrySchedulerTest < Test::Unit::TestCase
7
+
8
+ def setup
9
+ @maxRetries = 3
10
+
11
+ @scheduler = IntervalRetryScheduler.new 5.0, @maxRetries
12
+ @command = CommandMessage.as_message Object.new
13
+ @dispatcher = proc {}
14
+ end
15
+
16
+ should 'schedule up until the max number of retries' do
17
+ failures = []
18
+
19
+ mock(EventMachine).add_timer(5.0, &@dispatcher).times(@maxRetries)
20
+
21
+ @maxRetries.times do
22
+ failures.push RuntimeError.new
23
+ assert @scheduler.schedule @command, failures, @dispatcher
24
+ end
25
+
26
+ failures.push RuntimeError.new
27
+ refute @scheduler.schedule @command, failures, @dispatcher
28
+ end
29
+
30
+ should 'not schedule if failure was explicitly non-transient' do
31
+ failure = CommandValidationError.new
32
+ refute @scheduler.schedule @command, [failure], @dispatcher
33
+ end
34
+
35
+ should 'not schedule if cause of failure was explicitly non-transient' do
36
+ failure = CommandExecutionError.new NonTransientError.new
37
+ refute @scheduler.schedule @command, [failure], @dispatcher
38
+ end
39
+
40
+ end # IntervalRetrySchedulerTest
41
+ end # Command
42
+ end
@@ -0,0 +1,57 @@
1
+ require 'test_helper'
2
+
3
+ module Synapse
4
+ module Command
5
+ class RetryingCallbackTest < Test::Unit::TestCase
6
+
7
+ def setup
8
+ @command = CommandMessage.as_message Object.new
9
+ @delegate = Object.new
10
+ @retry_scheduler = Object.new
11
+ @command_bus = Object.new
12
+
13
+ @callback = RetryingCallback.new @delegate, @command, @retry_scheduler, @command_bus
14
+ end
15
+
16
+ should 'notify delegate of a successful dispatch' do
17
+ result = Object.new
18
+
19
+ mock(@delegate).on_success(result)
20
+ @callback.on_success result
21
+ end
22
+
23
+ should 'not notify delegate of a failed dispatch when rescheduling' do
24
+ failures = [RuntimeError.new]
25
+
26
+ mock(@command_bus).dispatch_with_callback(@command, @callback)
27
+
28
+ mock(@retry_scheduler).schedule(@command, failures, is_a(Proc)) do |_, _, dispatcher|
29
+ dispatcher.call
30
+ true
31
+ end
32
+
33
+ @callback.on_failure failures.last
34
+ end
35
+
36
+ should 'notify delegate of a failed dispatch when not rescheduling' do
37
+ failures = [RuntimeError.new]
38
+
39
+ mock(@retry_scheduler).schedule(@command, failures, is_a(Proc)) do
40
+ false
41
+ end
42
+ mock(@delegate).on_failure(failures.last)
43
+
44
+ @callback.on_failure failures.last
45
+ end
46
+
47
+ should 'notify delegate of a failed dispatch on an unchecked exception' do
48
+ failure = ThreadError.new
49
+
50
+ mock(@delegate).on_failure(failure)
51
+
52
+ @callback.on_failure failure
53
+ end
54
+
55
+ end
56
+ end
57
+ end
@@ -4,20 +4,54 @@ module Synapse
4
4
  module Command
5
5
 
6
6
  class CommandGatewayTest < Test::Unit::TestCase
7
- def test_send
8
- command_bus = Object.new
9
- gateway = CommandGateway.new command_bus
7
+ def setup
8
+ @command_bus = Object.new
9
+ @gateway = CommandGateway.new @command_bus
10
+ end
10
11
 
12
+ should 'wrap bare command objects in command messages before dispatch' do
11
13
  command = Object.new
12
14
  command_message = CommandMessage.build do |builder|
13
15
  builder.payload = command
14
16
  end
15
17
 
16
- mock(command_bus).dispatch(is_a(CommandMessage)).ordered
17
- mock(command_bus).dispatch(command_message).ordered
18
+ mock(@command_bus).dispatch_with_callback(is_a(CommandMessage), anything).ordered
19
+ mock(@command_bus).dispatch_with_callback(command_message, anything).ordered
20
+
21
+ @gateway.send command
22
+ @gateway.send command_message
23
+ end
24
+
25
+ should 'wrap callback in RetryingCallback if RetryScheduler' do
26
+ @gateway.retry_scheduler = IntervalRetryScheduler.new 3, 3
27
+
28
+ command = Object.new
29
+ callback = CommandCallback.new
30
+
31
+ mock(@command_bus).dispatch_with_callback(is_a(CommandMessage), is_a(RetryingCallback))
32
+
33
+ @gateway.send_with_callback command, callback
34
+ end
35
+
36
+ should 'send a command and wait for it to be dispatched' do
37
+ @gateway.retry_scheduler = IntervalRetryScheduler.new 3, 3
38
+
39
+ command = Object.new
40
+ result = Object.new
41
+
42
+ mock(@command_bus).dispatch_with_callback(is_a(CommandMessage), is_a(RetryingCallback)) do |message, callback|
43
+ callback.on_success result
44
+ end
45
+
46
+ received_result = nil
47
+
48
+ Thread.new do
49
+ received_result = @gateway.send_and_wait(command)
50
+ end
18
51
 
19
- gateway.send command
20
- gateway.send command_message
52
+ wait_until do
53
+ received_result.equal? result
54
+ end
21
55
  end
22
56
  end
23
57
 
@@ -4,7 +4,7 @@ module Synapse
4
4
  module Command
5
5
 
6
6
  class InterceptorChainTest < Test::Unit::TestCase
7
- def test_proceed
7
+ should 'allow interceptors to control the flow of dispatch' do
8
8
  unit = Object.new
9
9
  interceptor = Object.new
10
10
  interceptors = Array.new << interceptor
@@ -0,0 +1,17 @@
1
+ require 'test_helper'
2
+
3
+ module Synapse
4
+ module Command
5
+ class CommandMessageTest < Test::Unit::TestCase
6
+ should 'not wrap objects that are already command messages' do
7
+ command = Object.new
8
+ command_message = CommandMessage.build
9
+
10
+ assert_same command_message, CommandMessage.as_message(command_message)
11
+
12
+ wrapped = CommandMessage.as_message(command)
13
+ assert_same command, wrapped.payload
14
+ end
15
+ end
16
+ end
17
+ end
@@ -4,7 +4,7 @@ module Synapse
4
4
  module Command
5
5
 
6
6
  class SerializationOptimizingInterceptorTest < Test::Unit::TestCase
7
- def test_intercept
7
+ should 'register a serialization optimizing listener to the current unit of work' do
8
8
  interceptor = SerializationOptimizingInterceptor.new
9
9
 
10
10
  command = CommandMessage.build
@@ -19,7 +19,7 @@ module Synapse
19
19
  end
20
20
 
21
21
  class SerializationOptimizingListenerTest < Test::Unit::TestCase
22
- def test_on_event_registered
22
+ should 'wrap event messages with serialization aware event messages' do
23
23
  listener = SerializationOptimizingListener.new
24
24
  unit = Object.new
25
25
 
@@ -15,7 +15,7 @@ module Synapse
15
15
  end
16
16
  end
17
17
 
18
- def test_dispatch
18
+ should 'dispatch a command message to its registered handler' do
19
19
  handler = Object.new
20
20
  command = CommandMessage.build do |m|
21
21
  m.payload = TestCommand.new
@@ -28,7 +28,7 @@ module Synapse
28
28
  @command_bus.dispatch command
29
29
  end
30
30
 
31
- def test_dispatch_result
31
+ should 'invoke a callback with the return value from a command handler' do
32
32
  handler = Object.new
33
33
  callback = Object.new
34
34
  command = CommandMessage.build do |m|
@@ -47,7 +47,7 @@ module Synapse
47
47
  @command_bus.dispatch_with_callback command, callback
48
48
  end
49
49
 
50
- def test_dispatch_no_handler
50
+ should 'raise an exception when a dispatched command has no registered handler' do
51
51
  command = CommandMessage.build do |m|
52
52
  m.payload = TestCommand.new
53
53
  end
@@ -58,7 +58,7 @@ module Synapse
58
58
  @command_bus.dispatch_with_callback command, callback
59
59
  end
60
60
 
61
- def test_dispatch_rollback_on_exception
61
+ should 'roll back the current unit of work if the command handler raises an exception' do
62
62
  handler = Object.new
63
63
  command = CommandMessage.build do |m|
64
64
  m.payload = TestCommand.new
@@ -81,7 +81,7 @@ module Synapse
81
81
  @command_bus.dispatch_with_callback command, callback
82
82
  end
83
83
 
84
- def test_dispatch_commit_on_exception
84
+ should 'commit the current unit of work if the command handler raises an exception' do
85
85
  handler = Object.new
86
86
  command = CommandMessage.build do |m|
87
87
  m.payload = TestCommand.new
@@ -108,7 +108,7 @@ module Synapse
108
108
  @command_bus.dispatch_with_callback command, callback
109
109
  end
110
110
 
111
- def test_subscribe
111
+ should 'log when a subscribed handler is replaced' do
112
112
  handler = Object.new
113
113
 
114
114
  mock(@logger).debug(anything).ordered
@@ -118,7 +118,7 @@ module Synapse
118
118
  @command_bus.subscribe TestCommand, handler
119
119
  end
120
120
 
121
- def test_unsubscribe
121
+ should 'log when a handler is unsubscribed' do
122
122
  handler_a = Object.new
123
123
  handler_b = Object.new
124
124
 
@@ -5,16 +5,16 @@ module Synapse
5
5
  module Command
6
6
 
7
7
  class ActiveModelValidationFilterTest < Test::Unit::TestCase
8
- def test_filter
8
+ should 'continue if payload of a command message is valid' do
9
9
  message = CommandMessage.build do |m|
10
- m.payload = CreatePersonCommand.new 'River'
10
+ m.payload = CreatePersonCommand.new 'River Tam'
11
11
  end
12
12
 
13
13
  filter = ActiveModelValidationFilter.new
14
14
  filter.filter message
15
15
  end
16
16
 
17
- def test_filter_fails
17
+ should 'raise an exception if payload of a command message is invalid' do
18
18
  message = CommandMessage.build do |m|
19
19
  m.payload = CreatePersonCommand.new nil
20
20
  end