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.
- data/lib/synapse/command/async_command_bus.rb +41 -0
- data/lib/synapse/command/command_callback.rb +49 -2
- data/lib/synapse/command/command_handler.rb +2 -0
- data/lib/synapse/command/filters/validation.rb +2 -2
- data/lib/synapse/command/gateway/interval_retry_scheduler.rb +75 -0
- data/lib/synapse/command/gateway/retry_scheduler.rb +26 -0
- data/lib/synapse/command/gateway/retrying_callback.rb +46 -0
- data/lib/synapse/command/gateway.rb +56 -11
- data/lib/synapse/command/interceptor_chain.rb +2 -2
- data/lib/synapse/command/interceptors/serialization.rb +2 -2
- data/lib/synapse/command/message.rb +16 -0
- data/lib/synapse/command/simple_command_bus.rb +6 -2
- data/lib/synapse/command/wiring.rb +3 -3
- data/lib/synapse/command.rb +7 -1
- data/lib/synapse/common/concurrency/identifier_lock.rb +2 -17
- data/lib/synapse/common/concurrency/public_lock.rb +8 -9
- data/lib/synapse/common/errors.rb +1 -1
- data/lib/synapse/common/message.rb +2 -6
- data/lib/synapse/common.rb +9 -0
- data/lib/synapse/configuration/component/command_bus/async_command_bus.rb +41 -0
- data/lib/synapse/configuration/component/command_bus/gateway.rb +38 -0
- data/lib/synapse/configuration/component/command_bus/simple_command_bus.rb +81 -0
- data/lib/synapse/configuration/component/command_bus.rb +35 -0
- data/lib/synapse/configuration/component/event_bus/simple_event_bus.rb +41 -0
- data/lib/synapse/configuration/component/event_bus.rb +15 -0
- data/lib/synapse/configuration/component/event_sourcing/repository.rb +62 -0
- data/lib/synapse/configuration/component/event_sourcing.rb +15 -0
- data/lib/synapse/configuration/component/repository/locking_repository.rb +85 -0
- data/lib/synapse/configuration/component/repository/simple_repository.rb +31 -0
- data/lib/synapse/configuration/component/repository.rb +15 -0
- data/lib/synapse/configuration/component/serialization/converter_factory.rb +50 -0
- data/lib/synapse/configuration/component/serialization/serializer.rb +90 -0
- data/lib/synapse/configuration/component/serialization.rb +25 -0
- data/lib/synapse/configuration/component/uow/unit_factory.rb +51 -0
- data/lib/synapse/configuration/component/uow.rb +22 -0
- data/lib/synapse/configuration/component/upcasting/upcaster_chain.rb +61 -0
- data/lib/synapse/configuration/component/upcasting.rb +15 -0
- data/lib/synapse/configuration/container.rb +80 -0
- data/lib/synapse/configuration/container_builder.rb +108 -0
- data/lib/synapse/configuration/definition.rb +31 -0
- data/lib/synapse/configuration/definition_builder.rb +138 -0
- data/lib/synapse/configuration/dependent.rb +31 -0
- data/lib/synapse/configuration/ext.rb +35 -0
- data/lib/synapse/configuration.rb +32 -0
- data/lib/synapse/domain/aggregate_root.rb +25 -26
- data/lib/synapse/domain/event_container.rb +5 -5
- data/lib/synapse/domain/message.rb +16 -0
- data/lib/synapse/event_bus/event_listener.rb +2 -0
- data/lib/synapse/event_bus/event_publisher.rb +19 -0
- data/lib/synapse/event_bus/simple_event_bus.rb +21 -30
- data/lib/synapse/event_bus/wiring.rb +5 -4
- data/lib/synapse/event_bus.rb +1 -1
- data/lib/synapse/event_sourcing/aggregate_root.rb +6 -4
- data/lib/synapse/process_manager/container_resource_injector.rb +18 -0
- data/lib/synapse/process_manager/pessimistic_lock_manager.rb +1 -1
- data/lib/synapse/process_manager/process.rb +4 -0
- data/lib/synapse/process_manager/process_factory.rb +3 -3
- data/lib/synapse/process_manager/repository/in_memory.rb +1 -1
- data/lib/synapse/process_manager/wiring/process.rb +6 -6
- data/lib/synapse/process_manager/wiring/process_manager.rb +8 -8
- data/lib/synapse/process_manager.rb +2 -0
- data/lib/synapse/repository/locking.rb +5 -3
- data/lib/synapse/repository/optimistic_lock_manager.rb +2 -7
- data/lib/synapse/repository/pessimistic_lock_manager.rb +3 -3
- data/lib/synapse/repository/repository.rb +2 -2
- data/lib/synapse/repository/simple_repository.rb +69 -0
- data/lib/synapse/repository.rb +1 -0
- data/lib/synapse/serialization/converter/chain.rb +2 -2
- data/lib/synapse/serialization/converter_factory.rb +2 -2
- data/lib/synapse/serialization/lazy_object.rb +2 -2
- data/lib/synapse/serialization/message/serialization_aware_message.rb +3 -3
- data/lib/synapse/serialization/message/serialized_message.rb +6 -10
- data/lib/synapse/serialization/message/serialized_message_builder.rb +4 -4
- data/lib/synapse/serialization/message/serialized_object_cache.rb +2 -2
- data/lib/synapse/serialization/message/serializer.rb +2 -2
- data/lib/synapse/serialization/revision_resolver.rb +3 -3
- data/lib/synapse/serialization/serialized_object.rb +2 -2
- data/lib/synapse/serialization/serialized_type.rb +2 -2
- data/lib/synapse/serialization/serializer/attribute.rb +2 -2
- data/lib/synapse/serialization/serializer/marshal.rb +2 -2
- data/lib/synapse/serialization/serializer/oj.rb +2 -2
- data/lib/synapse/serialization/serializer/ox.rb +2 -2
- data/lib/synapse/serialization/serializer.rb +2 -2
- data/lib/synapse/uow/factory.rb +2 -2
- data/lib/synapse/uow/listener_collection.rb +2 -2
- data/lib/synapse/uow/nesting.rb +9 -2
- data/lib/synapse/uow/provider.rb +2 -2
- data/lib/synapse/uow/uow.rb +8 -2
- data/lib/synapse/upcasting/{chain.rb → upcaster_chain.rb} +0 -0
- data/lib/synapse/upcasting.rb +1 -1
- data/lib/synapse/version.rb +1 -1
- data/lib/synapse/wiring/message_wiring.rb +31 -0
- data/lib/synapse.rb +2 -14
- data/test/auditing/data_provider_test.rb +2 -2
- data/test/auditing/dispatch_interceptor_test.rb +1 -1
- data/test/auditing/unit_listener_test.rb +3 -3
- data/test/command/async_command_bus_test.rb +49 -0
- data/test/command/duplication_test.rb +2 -2
- data/test/command/gateway/interval_retry_scheduler_test.rb +42 -0
- data/test/command/gateway/retrying_callback_test.rb +57 -0
- data/test/command/gateway_test.rb +41 -7
- data/test/command/interceptor_chain_test.rb +1 -1
- data/test/command/message_test.rb +17 -0
- data/test/command/serialization_test.rb +2 -2
- data/test/command/simple_command_bus_test.rb +7 -7
- data/test/command/validation_test.rb +3 -3
- data/test/command/wiring_test.rb +3 -3
- data/test/common/concurrency/identifier_lock_test.rb +2 -13
- data/test/common/concurrency/public_lock_test.rb +6 -6
- data/test/{duplication_test.rb → common/duplication_test.rb} +3 -3
- data/test/configuration/component/command_bus/async_command_bus_test.rb +36 -0
- data/test/configuration/component/command_bus/simple_command_bus_test.rb +57 -0
- data/test/configuration/component/event_bus/simple_event_bus_test.rb +58 -0
- data/test/configuration/component/serialization/converter_factory_test.rb +48 -0
- data/test/configuration/component/serialization/serializer_test.rb +78 -0
- data/test/configuration/component/uow/unit_factory_test.rb +46 -0
- data/test/configuration/container_builder_test.rb +47 -0
- data/test/configuration/container_test.rb +88 -0
- data/test/configuration/definition_builder_test.rb +126 -0
- data/test/configuration/definition_test.rb +41 -0
- data/test/configuration/dependent_test.rb +30 -0
- data/test/configuration/ext_test.rb +19 -0
- data/test/configuration/fixtures/dependent.rb +10 -0
- data/test/domain/aggregate_root_test.rb +5 -5
- data/test/domain/message_test.rb +15 -3
- data/test/domain/stream_test.rb +2 -2
- data/test/event_bus/publisher_test.rb +29 -0
- data/test/event_bus/wiring_test.rb +1 -1
- data/test/event_sourcing/aggregate_factory_test.rb +12 -6
- data/test/event_sourcing/aggregate_root_test.rb +4 -4
- data/test/event_sourcing/entity_test.rb +10 -9
- data/test/event_sourcing/repository_test.rb +6 -6
- data/test/event_sourcing/storage_listener_test.rb +8 -4
- data/test/event_store/in_memory_test.rb +3 -3
- data/test/process_manager/container_resource_injector_test.rb +19 -0
- data/test/process_manager/correlation_set_test.rb +2 -2
- data/test/process_manager/correlation_test.rb +2 -2
- data/test/process_manager/in_memory_test.rb +3 -3
- data/test/process_manager/process_factory_test.rb +2 -2
- data/test/process_manager/process_test.rb +3 -3
- data/test/process_manager/simple_process_manager_test.rb +5 -5
- data/test/process_manager/wiring/process_manager_test.rb +4 -4
- data/test/process_manager/wiring/process_test.rb +2 -2
- data/test/repository/locking_test.rb +4 -4
- data/test/repository/optimistic_test.rb +2 -2
- data/test/repository/pessimistic_test.rb +1 -1
- data/test/repository/simple_repository_test.rb +79 -0
- data/test/support/countdown_latch.rb +18 -0
- data/test/test_helper.rb +6 -3
- data/test/upcasting/data_test.rb +31 -0
- metadata +84 -25
- data/lib/synapse/event_bus/event_listener_proxy.rb +0 -12
- data/lib/synapse/partitioning/memory_queue_reader.rb +0 -31
- data/lib/synapse/partitioning/memory_queue_writer.rb +0 -19
- data/lib/synapse/partitioning/message_receipt.rb +0 -25
- data/lib/synapse/partitioning/packing/json_packer.rb +0 -93
- data/lib/synapse/partitioning/packing/json_unpacker.rb +0 -83
- data/lib/synapse/partitioning/packing.rb +0 -27
- data/lib/synapse/partitioning/queue_reader.rb +0 -32
- data/lib/synapse/partitioning/queue_writer.rb +0 -17
- data/lib/synapse/partitioning.rb +0 -18
- data/test/partitioning/memory_test.rb +0 -34
- data/test/partitioning/packing/json_test.rb +0 -62
data/lib/synapse/uow/factory.rb
CHANGED
data/lib/synapse/uow/nesting.rb
CHANGED
@@ -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
|
data/lib/synapse/uow/provider.rb
CHANGED
data/lib/synapse/uow/uow.rb
CHANGED
@@ -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
|
File without changes
|
data/lib/synapse/upcasting.rb
CHANGED
data/lib/synapse/version.rb
CHANGED
@@ -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
|
-
|
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
|
-
|
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 AuditingUnitOfWorkListenerTest < Test::Unit::TestCase
|
7
|
-
|
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
|
-
|
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
|
-
|
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
|
-
|
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
|
-
|
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
|
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).
|
17
|
-
mock(command_bus).
|
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
|
-
|
20
|
-
|
52
|
+
wait_until do
|
53
|
+
received_result.equal? result
|
54
|
+
end
|
21
55
|
end
|
22
56
|
end
|
23
57
|
|
@@ -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
|
-
|
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
|
-
|
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
|
-
|
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
|
-
|
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
|
-
|
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
|
-
|
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
|
-
|
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
|
-
|
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
|
-
|
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
|
-
|
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
|
-
|
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
|