aggro 0.0.1 → 0.0.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/.rubocop.yml +8 -0
- data/.travis.yml +15 -0
- data/Gemfile +9 -0
- data/README.md +5 -1
- data/Rakefile +10 -0
- data/aggro.gemspec +8 -1
- data/lib/aggro.rb +191 -7
- data/lib/aggro/abstract_store.rb +12 -0
- data/lib/aggro/aggregate.rb +98 -0
- data/lib/aggro/aggregate_ref.rb +68 -6
- data/lib/aggro/attribute_dsl.rb +96 -0
- data/lib/aggro/binding_dsl.rb +45 -0
- data/lib/aggro/block_helper.rb +14 -0
- data/lib/aggro/channel.rb +37 -0
- data/lib/aggro/client.rb +12 -0
- data/lib/aggro/cluster_config.rb +57 -0
- data/lib/aggro/command.rb +16 -0
- data/lib/aggro/concurrent_actor.rb +26 -0
- data/lib/aggro/event_bus.rb +94 -0
- data/lib/aggro/event_dsl.rb +53 -0
- data/lib/aggro/event_proxy.rb +23 -0
- data/lib/aggro/event_serializer.rb +14 -0
- data/lib/aggro/file_store.rb +97 -0
- data/lib/aggro/file_store/reader.rb +21 -0
- data/lib/aggro/file_store/writer.rb +27 -0
- data/lib/aggro/handler/command.rb +60 -0
- data/lib/aggro/handler/create_aggregate.rb +42 -0
- data/lib/aggro/handler/get_events.rb +30 -0
- data/lib/aggro/handler/query.rb +60 -0
- data/lib/aggro/handler/start_saga.rb +56 -0
- data/lib/aggro/local_node.rb +28 -0
- data/lib/aggro/locator.rb +32 -0
- data/lib/aggro/message/ask.rb +16 -0
- data/lib/aggro/message/command.rb +36 -0
- data/lib/aggro/message/create_aggregate.rb +16 -0
- data/lib/aggro/message/endpoint.rb +16 -0
- data/lib/aggro/message/events.rb +24 -0
- data/lib/aggro/message/get_events.rb +16 -0
- data/lib/aggro/message/heartbeat.rb +16 -0
- data/lib/aggro/message/invalid_target.rb +20 -0
- data/lib/aggro/message/ok.rb +20 -0
- data/lib/aggro/message/publisher_endpoint_inquiry.rb +16 -0
- data/lib/aggro/message/query.rb +36 -0
- data/lib/aggro/message/result.rb +16 -0
- data/lib/aggro/message/start_saga.rb +28 -0
- data/lib/aggro/message/unhandled_operation.rb +20 -0
- data/lib/aggro/message/unknown_operation.rb +20 -0
- data/lib/aggro/message_parser.rb +10 -0
- data/lib/aggro/message_router.rb +26 -0
- data/lib/aggro/nanomsg_transport.rb +31 -0
- data/lib/aggro/nanomsg_transport/client.rb +35 -0
- data/lib/aggro/nanomsg_transport/connection.rb +98 -0
- data/lib/aggro/nanomsg_transport/publish.rb +17 -0
- data/lib/aggro/nanomsg_transport/publisher.rb +37 -0
- data/lib/aggro/nanomsg_transport/raw_reply.rb +18 -0
- data/lib/aggro/nanomsg_transport/raw_request.rb +18 -0
- data/lib/aggro/nanomsg_transport/reply.rb +17 -0
- data/lib/aggro/nanomsg_transport/request.rb +17 -0
- data/lib/aggro/nanomsg_transport/server.rb +84 -0
- data/lib/aggro/nanomsg_transport/socket_error.rb +20 -0
- data/lib/aggro/nanomsg_transport/subscribe.rb +27 -0
- data/lib/aggro/nanomsg_transport/subscriber.rb +82 -0
- data/lib/aggro/node.rb +29 -0
- data/lib/aggro/node_list.rb +39 -0
- data/lib/aggro/projection.rb +13 -0
- data/lib/aggro/query.rb +11 -0
- data/lib/aggro/saga.rb +94 -0
- data/lib/aggro/saga_runner.rb +87 -0
- data/lib/aggro/saga_runner/start_saga.rb +12 -0
- data/lib/aggro/saga_status.rb +29 -0
- data/lib/aggro/server.rb +88 -0
- data/lib/aggro/subscriber.rb +48 -0
- data/lib/aggro/subscription.rb +41 -0
- data/lib/aggro/transform/boolean.rb +16 -0
- data/lib/aggro/transform/email.rb +26 -0
- data/lib/aggro/transform/id.rb +34 -0
- data/lib/aggro/transform/integer.rb +22 -0
- data/lib/aggro/transform/money.rb +22 -0
- data/lib/aggro/transform/noop.rb +16 -0
- data/lib/aggro/transform/string.rb +16 -0
- data/lib/aggro/transform/time_interval.rb +24 -0
- data/lib/aggro/version.rb +1 -1
- data/spec/lib/aggro/abstract_store_spec.rb +15 -0
- data/spec/lib/aggro/aggregate_ref_spec.rb +63 -12
- data/spec/lib/aggro/aggregate_spec.rb +207 -0
- data/spec/lib/aggro/channel_spec.rb +87 -0
- data/spec/lib/aggro/client_spec.rb +26 -0
- data/spec/lib/aggro/cluster_config_spec.rb +33 -0
- data/spec/lib/aggro/command_spec.rb +52 -0
- data/spec/lib/aggro/concurrent_actor_spec.rb +44 -0
- data/spec/lib/aggro/event_bus_spec.rb +20 -0
- data/spec/lib/aggro/event_serializer_spec.rb +28 -0
- data/spec/lib/aggro/file_store/reader_spec.rb +32 -0
- data/spec/lib/aggro/file_store/writer_spec.rb +67 -0
- data/spec/lib/aggro/file_store_spec.rb +51 -0
- data/spec/lib/aggro/handler/command_spec.rb +78 -0
- data/spec/lib/aggro/handler/create_aggregate_spec.rb +64 -0
- data/spec/lib/aggro/handler/get_events_handler_spec.rb +45 -0
- data/spec/lib/aggro/handler/query_spec.rb +78 -0
- data/spec/lib/aggro/handler/start_saga_spec.rb +64 -0
- data/spec/lib/aggro/local_node_spec.rb +52 -0
- data/spec/lib/aggro/locator_spec.rb +61 -0
- data/spec/lib/aggro/message/ask_spec.rb +23 -0
- data/spec/lib/aggro/message/command_spec.rb +50 -0
- data/spec/lib/aggro/message/create_aggregate_spec.rb +28 -0
- data/spec/lib/aggro/message/endpoint_spec.rb +23 -0
- data/spec/lib/aggro/message/events_spec.rb +37 -0
- data/spec/lib/aggro/message/get_events_spec.rb +33 -0
- data/spec/lib/aggro/message/heartbeat_spec.rb +23 -0
- data/spec/lib/aggro/message/invalid_target_spec.rb +28 -0
- data/spec/lib/aggro/message/ok_spec.rb +27 -0
- data/spec/lib/aggro/message/publisher_endpoint_inquiry_spec.rb +23 -0
- data/spec/lib/aggro/message/query_spec.rb +50 -0
- data/spec/lib/aggro/message/start_saga_spec.rb +37 -0
- data/spec/lib/aggro/message/unhandled_operation_spec.rb +28 -0
- data/spec/lib/aggro/message/unknown_operation_spec.rb +28 -0
- data/spec/lib/aggro/message_parser_spec.rb +16 -0
- data/spec/lib/aggro/message_router_spec.rb +35 -0
- data/spec/lib/aggro/nanomsg_transport/socket_error_spec.rb +21 -0
- data/spec/lib/aggro/nanomsg_transport_spec.rb +37 -0
- data/spec/lib/aggro/node_list_spec.rb +38 -0
- data/spec/lib/aggro/node_spec.rb +44 -0
- data/spec/lib/aggro/projection_spec.rb +22 -0
- data/spec/lib/aggro/query_spec.rb +47 -0
- data/spec/lib/aggro/saga_runner_spec.rb +84 -0
- data/spec/lib/aggro/saga_spec.rb +126 -0
- data/spec/lib/aggro/saga_status_spec.rb +56 -0
- data/spec/lib/aggro/server_spec.rb +118 -0
- data/spec/lib/aggro/subscriber_spec.rb +59 -0
- data/spec/lib/aggro/subscription_spec.rb +50 -0
- data/spec/lib/aggro/transform/boolean_spec.rb +23 -0
- data/spec/lib/aggro/transform/email_spec.rb +13 -0
- data/spec/lib/aggro/transform/id_spec.rb +70 -0
- data/spec/lib/aggro/transform/integer_spec.rb +30 -0
- data/spec/lib/aggro/transform/money_spec.rb +34 -0
- data/spec/lib/aggro/transform/string_spec.rb +15 -0
- data/spec/lib/aggro/transform/time_interval_spec.rb +29 -0
- data/spec/lib/aggro_spec.rb +63 -19
- data/spec/spec_helper.rb +21 -2
- metadata +283 -3
@@ -0,0 +1,16 @@
|
|
1
|
+
RSpec.describe MessageParser do
|
2
|
+
subject(:router) { MessageRouter.new }
|
3
|
+
|
4
|
+
let(:message_class) { spy(parse: double) }
|
5
|
+
|
6
|
+
before do
|
7
|
+
stub_const 'Aggro::MESSAGE_TYPES', '01' => message_class
|
8
|
+
end
|
9
|
+
|
10
|
+
describe '.parse' do
|
11
|
+
it 'should parse messages via message parse function' do
|
12
|
+
MessageParser.parse '01'
|
13
|
+
expect(message_class).to have_received(:parse).with '01'
|
14
|
+
end
|
15
|
+
end
|
16
|
+
end
|
@@ -0,0 +1,35 @@
|
|
1
|
+
RSpec.describe MessageRouter do
|
2
|
+
subject(:router) { MessageRouter.new }
|
3
|
+
|
4
|
+
let(:message) { message_class.new }
|
5
|
+
let(:message_class) { Class.new }
|
6
|
+
|
7
|
+
before do
|
8
|
+
stub_const 'Aggro::MESSAGE_TYPES', '01' => message_class
|
9
|
+
end
|
10
|
+
|
11
|
+
describe '#attach_handler' do
|
12
|
+
it 'should attach a given callable to handle a specific message type' do
|
13
|
+
callable = ->(parsed) { parsed }
|
14
|
+
|
15
|
+
router.attach_handler message_class, callable
|
16
|
+
|
17
|
+
expect(router.handles?(message_class)).to be_truthy
|
18
|
+
end
|
19
|
+
|
20
|
+
it 'should attach a given block to handle a specific message type' do
|
21
|
+
router.attach_handler(message_class) { |parsed| parsed }
|
22
|
+
|
23
|
+
expect(router.handles?(message_class)).to be_truthy
|
24
|
+
end
|
25
|
+
end
|
26
|
+
|
27
|
+
describe '#route' do
|
28
|
+
it 'should route messages to the correct code' do
|
29
|
+
router.attach_handler(message_class) { |msg| @msg = msg }
|
30
|
+
router.route message
|
31
|
+
|
32
|
+
expect(@msg).to eq message
|
33
|
+
end
|
34
|
+
end
|
35
|
+
end
|
@@ -0,0 +1,21 @@
|
|
1
|
+
RSpec.describe NanomsgTransport::SocketError do
|
2
|
+
subject(:error) { NanomsgTransport::SocketError.new 5 }
|
3
|
+
|
4
|
+
let(:nanomsg_api) { spy nn_strerror: 'No u' }
|
5
|
+
|
6
|
+
before do
|
7
|
+
stub_const 'NNCore::LibNanomsg', nanomsg_api
|
8
|
+
end
|
9
|
+
|
10
|
+
describe '#to_s' do
|
11
|
+
it 'should ask nanomsg for the meaning of the errno' do
|
12
|
+
error.to_s
|
13
|
+
|
14
|
+
expect(nanomsg_api).to have_received(:nn_strerror).with 5
|
15
|
+
end
|
16
|
+
|
17
|
+
it 'should provide a human readable error message' do
|
18
|
+
expect(error.to_s).to eq "Last nanomsg API call failed with 'No u'"
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
@@ -0,0 +1,37 @@
|
|
1
|
+
RSpec.describe NanomsgTransport do
|
2
|
+
let(:host) { 'tcp://127.0.0.1:7250' }
|
3
|
+
let(:message) { SecureRandom.hex }
|
4
|
+
|
5
|
+
xit 'should work for REQREP' do
|
6
|
+
server = NanomsgTransport.server(host) { |rec| @rec = rec }.start
|
7
|
+
client = NanomsgTransport.client host
|
8
|
+
|
9
|
+
client.post message
|
10
|
+
|
11
|
+
server.stop
|
12
|
+
client.close_socket
|
13
|
+
|
14
|
+
expect(@rec).to eq message
|
15
|
+
end
|
16
|
+
|
17
|
+
it 'should work for PUBSUB' do
|
18
|
+
publisher = NanomsgTransport.publisher(host)
|
19
|
+
publisher.open_socket
|
20
|
+
|
21
|
+
@reced = []
|
22
|
+
|
23
|
+
subscriber = NanomsgTransport.subscriber(host) { |rec| @reced << rec }
|
24
|
+
subscriber.add_subscription('foo').start
|
25
|
+
|
26
|
+
sleep 0.1
|
27
|
+
|
28
|
+
publisher.publish 'foobar'
|
29
|
+
publisher.publish 'bazbar'
|
30
|
+
|
31
|
+
publisher.close_socket
|
32
|
+
subscriber.stop
|
33
|
+
|
34
|
+
expect(@reced).to include 'foobar'
|
35
|
+
expect(@reced).to_not include 'bazbar'
|
36
|
+
end
|
37
|
+
end
|
@@ -0,0 +1,38 @@
|
|
1
|
+
RSpec.describe NodeList do
|
2
|
+
subject(:node_list) { NodeList.new }
|
3
|
+
|
4
|
+
let(:second_node_list) { NodeList.new }
|
5
|
+
|
6
|
+
let(:id) { SecureRandom.uuid }
|
7
|
+
|
8
|
+
let(:servers) { 10.times.map { |i| "localhost:#{5000 + i}" } }
|
9
|
+
let(:nodes) { servers.map { |server| Node.new(server, server) } }
|
10
|
+
|
11
|
+
describe '#add' do
|
12
|
+
it 'should add the node to the list of nodes' do
|
13
|
+
nodes.each { |node| node_list.add node }
|
14
|
+
|
15
|
+
expect(node_list.nodes.length).to eq nodes.length
|
16
|
+
end
|
17
|
+
end
|
18
|
+
|
19
|
+
describe '#nodes_for' do
|
20
|
+
before do
|
21
|
+
nodes.each { |node| node_list.add node }
|
22
|
+
end
|
23
|
+
|
24
|
+
it 'should get a number of nodes equal to the replication factor' do
|
25
|
+
expect(node_list.nodes_for(id, 4).length).to eq 4
|
26
|
+
end
|
27
|
+
|
28
|
+
it 'should have a default replication factor of 3' do
|
29
|
+
expect(node_list.nodes_for(id).length).to eq 3
|
30
|
+
end
|
31
|
+
|
32
|
+
it 'should give the same servers regardless of order of node addition' do
|
33
|
+
nodes.shuffle.each { |node| second_node_list.add node }
|
34
|
+
|
35
|
+
expect(node_list.nodes_for(id)).to eq second_node_list.nodes_for(id)
|
36
|
+
end
|
37
|
+
end
|
38
|
+
end
|
@@ -0,0 +1,44 @@
|
|
1
|
+
RSpec.describe Node do
|
2
|
+
subject(:node) { Node.new('flashing-sparkle') }
|
3
|
+
|
4
|
+
describe '#client' do
|
5
|
+
it 'should return a client for the node using the current transport' do
|
6
|
+
expect(node.client).to be_a Client
|
7
|
+
end
|
8
|
+
end
|
9
|
+
|
10
|
+
describe '#publisher_endpoint' do
|
11
|
+
context 'node returns an Endpoint message' do
|
12
|
+
it 'should ask the node for the publisher endpoint' do
|
13
|
+
endpoint = Message::Endpoint.new('endpoint')
|
14
|
+
allow(node).to receive(:client).and_return(double post: endpoint)
|
15
|
+
|
16
|
+
expect(node.publisher_endpoint).to eq 'endpoint'
|
17
|
+
end
|
18
|
+
end
|
19
|
+
|
20
|
+
context 'node does not return an Endpoint message' do
|
21
|
+
it 'should ask the node for the publisher endpoint' do
|
22
|
+
allow(node).to receive(:client).and_return(double post: 'not endpoint')
|
23
|
+
|
24
|
+
expect { node.publisher_endpoint }.to raise_error
|
25
|
+
end
|
26
|
+
end
|
27
|
+
end
|
28
|
+
|
29
|
+
describe '#to_s' do
|
30
|
+
let(:moved_node) { Node.new('flashing-sparkle') }
|
31
|
+
let(:other_node) { Node.new('dancing-sparkle') }
|
32
|
+
|
33
|
+
let(:ring) { ConsistentHashing::Ring.new }
|
34
|
+
let(:hasher) { ring.method(:hash_key) }
|
35
|
+
|
36
|
+
it 'should be consistently hashed the same if id matches' do
|
37
|
+
expect(hasher.call(node)).to eq hasher.call(moved_node)
|
38
|
+
end
|
39
|
+
|
40
|
+
it 'should be consistently hashed differently if id differs' do
|
41
|
+
expect(hasher.call(node)).to_not eq hasher.call(other_node)
|
42
|
+
end
|
43
|
+
end
|
44
|
+
end
|
@@ -0,0 +1,22 @@
|
|
1
|
+
RSpec.describe Projection do
|
2
|
+
class CatSerializer
|
3
|
+
include Aggro::Projection
|
4
|
+
end
|
5
|
+
|
6
|
+
subject(:projection) { CatSerializer.new id }
|
7
|
+
|
8
|
+
let(:id) { SecureRandom.uuid }
|
9
|
+
let(:event_bus) { spy }
|
10
|
+
|
11
|
+
before do
|
12
|
+
allow(Aggro).to receive(:event_bus).and_return(event_bus)
|
13
|
+
end
|
14
|
+
|
15
|
+
describe '.new' do
|
16
|
+
it 'should subscribe itself to events for the given ID' do
|
17
|
+
projection
|
18
|
+
|
19
|
+
expect(event_bus).to have_received(:subscribe).with id, projection
|
20
|
+
end
|
21
|
+
end
|
22
|
+
end
|
@@ -0,0 +1,47 @@
|
|
1
|
+
RSpec.describe Query do
|
2
|
+
class TestQuery
|
3
|
+
include Query
|
4
|
+
|
5
|
+
string :term
|
6
|
+
validates :term, presence: true
|
7
|
+
|
8
|
+
integer :times
|
9
|
+
end
|
10
|
+
|
11
|
+
subject(:query) { TestQuery.new term: 'puppy', times: '100' }
|
12
|
+
|
13
|
+
describe '#attributes' do
|
14
|
+
it 'should return a hash of attributes' do
|
15
|
+
expect(query.attributes).to be_a Hash
|
16
|
+
expect(query.attributes[:term]).to eq 'puppy'
|
17
|
+
end
|
18
|
+
end
|
19
|
+
|
20
|
+
describe '#serialized_attributes' do
|
21
|
+
it 'should return a hash of attributes run through the transforms' do
|
22
|
+
expect(query.serialized_attributes).to be_a Hash
|
23
|
+
expect(query.serialized_attributes[:times]).to eq 100
|
24
|
+
end
|
25
|
+
end
|
26
|
+
|
27
|
+
describe '#to_details' do
|
28
|
+
it 'should return a hash containing the query name and arguments' do
|
29
|
+
details = query.to_details
|
30
|
+
|
31
|
+
expect(details).to be_a Hash
|
32
|
+
expect(details[:name]).to eq 'TestQuery'
|
33
|
+
expect(details[:args][:term]).to eq 'puppy'
|
34
|
+
expect(details[:args][:times]).to eq 100
|
35
|
+
end
|
36
|
+
end
|
37
|
+
|
38
|
+
describe '#valid?' do
|
39
|
+
it 'should return true if query is valid' do
|
40
|
+
expect(query.valid?).to be_truthy
|
41
|
+
end
|
42
|
+
|
43
|
+
it 'should return false if query is not valid' do
|
44
|
+
expect(TestQuery.new.valid?).to be_falsey
|
45
|
+
end
|
46
|
+
end
|
47
|
+
end
|
@@ -0,0 +1,84 @@
|
|
1
|
+
RSpec.describe SagaRunner do
|
2
|
+
subject(:runner) { SagaRunner.new id }
|
3
|
+
|
4
|
+
let(:id) { SecureRandom.uuid }
|
5
|
+
|
6
|
+
let(:causation_id) { SecureRandom.uuid }
|
7
|
+
let(:correlation_id) { SecureRandom.uuid }
|
8
|
+
let(:saga) { spy causation_id: causation_id, correlation_id: correlation_id }
|
9
|
+
let(:handler) { -> { @ran = true } }
|
10
|
+
let(:saga_class) do
|
11
|
+
double new: saga, initial: :first_step, handler_for_step: handler
|
12
|
+
end
|
13
|
+
|
14
|
+
before do
|
15
|
+
stub_const 'TestSaga', saga_class
|
16
|
+
|
17
|
+
config = double(server_node?: true, node_name: SecureRandom.uuid, nodes: [])
|
18
|
+
allow(Aggro).to receive(:cluster_config).and_return config
|
19
|
+
end
|
20
|
+
|
21
|
+
describe '#apply_command' do
|
22
|
+
context 'the command is a StartSaga' do
|
23
|
+
let(:details) { { test: 'foo' } }
|
24
|
+
let(:command) do
|
25
|
+
SagaRunner::StartSaga.new name: 'TestSaga', id: id, details: details
|
26
|
+
end
|
27
|
+
|
28
|
+
it 'should create a new saga based on the given name' do
|
29
|
+
runner.send :apply_command, command
|
30
|
+
expect(saga_class).to have_received(:new)
|
31
|
+
end
|
32
|
+
|
33
|
+
it 'should set @runner on the saga' do
|
34
|
+
runner.send :apply_command, command
|
35
|
+
expect(saga.instance_variable_get(:@runner)).to eq runner
|
36
|
+
end
|
37
|
+
|
38
|
+
it 'should execute the initial step' do
|
39
|
+
runner.send :apply_command, command
|
40
|
+
expect(saga.instance_variable_get(:@ran)).to be_truthy
|
41
|
+
end
|
42
|
+
end
|
43
|
+
end
|
44
|
+
|
45
|
+
describe '#reject' do
|
46
|
+
let(:proxy) { spy }
|
47
|
+
|
48
|
+
before do
|
49
|
+
allow(runner).to receive(:did).and_return proxy
|
50
|
+
runner.instance_variable_set :@saga, saga
|
51
|
+
end
|
52
|
+
|
53
|
+
it 'should call the rejected event' do
|
54
|
+
runner.reject 'reason'
|
55
|
+
|
56
|
+
expect(proxy).to have_received(:rejected).with reason: 'reason'
|
57
|
+
end
|
58
|
+
end
|
59
|
+
|
60
|
+
describe '#resolve' do
|
61
|
+
let(:proxy) { spy }
|
62
|
+
|
63
|
+
before do
|
64
|
+
allow(runner).to receive(:did).and_return proxy
|
65
|
+
runner.instance_variable_set :@saga, saga
|
66
|
+
end
|
67
|
+
|
68
|
+
it 'should call the resolved event' do
|
69
|
+
runner.resolve 'value'
|
70
|
+
|
71
|
+
expect(proxy).to have_received(:resolved).with value: 'value'
|
72
|
+
end
|
73
|
+
end
|
74
|
+
|
75
|
+
describe '#did' do
|
76
|
+
it 'should set the @_context with @details' do
|
77
|
+
runner.instance_variable_set :@details, foo: 'bar'
|
78
|
+
|
79
|
+
runner.send :did
|
80
|
+
|
81
|
+
expect(runner.instance_variable_get(:@_context)[:foo]).to eq 'bar'
|
82
|
+
end
|
83
|
+
end
|
84
|
+
end
|
@@ -0,0 +1,126 @@
|
|
1
|
+
RSpec.describe Saga do
|
2
|
+
class PizzaMaker
|
3
|
+
include Saga
|
4
|
+
|
5
|
+
generate_id :pizza_id
|
6
|
+
id :oven_id
|
7
|
+
string :dough_type
|
8
|
+
|
9
|
+
initial :prepare_dough
|
10
|
+
|
11
|
+
step :prepare_dough do
|
12
|
+
pizza = Pizza.create.command(command1)
|
13
|
+
|
14
|
+
bind pizza do
|
15
|
+
def started_to_be_made
|
16
|
+
transition_to :add_toppings
|
17
|
+
end
|
18
|
+
|
19
|
+
def failed_to_start_making
|
20
|
+
reject "I couldn't makea the pizza"
|
21
|
+
end
|
22
|
+
end
|
23
|
+
end
|
24
|
+
|
25
|
+
step :add_toppings do
|
26
|
+
pizza = Pizza.find(pizza_id).command(command2)
|
27
|
+
|
28
|
+
bind pizza do
|
29
|
+
def started_to_be_made
|
30
|
+
transition_to :cook
|
31
|
+
end
|
32
|
+
|
33
|
+
def failed_to_add_toppings
|
34
|
+
reject "I couldn't topa the pizza"
|
35
|
+
end
|
36
|
+
end
|
37
|
+
end
|
38
|
+
|
39
|
+
step :cook do
|
40
|
+
oven = Oven.find(oven_id).command(command3)
|
41
|
+
|
42
|
+
bind oven do
|
43
|
+
def cooked_thing
|
44
|
+
resolve pizza_id
|
45
|
+
end
|
46
|
+
|
47
|
+
def failed_to_cook_thing
|
48
|
+
reject "I couldn't cooka the pizza"
|
49
|
+
end
|
50
|
+
end
|
51
|
+
end
|
52
|
+
end
|
53
|
+
|
54
|
+
subject(:saga) { PizzaMaker.new(oven_id: oven_id, dough_type: 'yummy') }
|
55
|
+
|
56
|
+
let(:oven_id) { SecureRandom.uuid }
|
57
|
+
|
58
|
+
let(:client) { spy }
|
59
|
+
let(:node) { double(client: client) }
|
60
|
+
let(:fake_locator) { double primary_node: node }
|
61
|
+
let(:locator_class) { double new: fake_locator }
|
62
|
+
|
63
|
+
let(:fake_status) { double }
|
64
|
+
let(:saga_status_class) { double new: fake_status }
|
65
|
+
|
66
|
+
before do
|
67
|
+
stub_const 'Aggro::Locator', locator_class
|
68
|
+
stub_const 'Aggro::SagaStatus', saga_status_class
|
69
|
+
|
70
|
+
allow(client).to receive(:post).and_return Message::OK.new
|
71
|
+
end
|
72
|
+
|
73
|
+
describe '#start' do
|
74
|
+
before do
|
75
|
+
saga.start
|
76
|
+
|
77
|
+
expect(client).to have_received(:post).with Message::StartSaga
|
78
|
+
end
|
79
|
+
|
80
|
+
it 'should return a SagaStatus' do
|
81
|
+
expect(saga.start).to eq fake_status
|
82
|
+
end
|
83
|
+
end
|
84
|
+
|
85
|
+
describe '#transition' do
|
86
|
+
it 'should call transition on the runner' do
|
87
|
+
runner = spy transition: true
|
88
|
+
saga.instance_variable_set(:@runner, runner)
|
89
|
+
saga.send :transition, :add_toppings
|
90
|
+
|
91
|
+
expect(runner).to have_received(:transition).with :add_toppings
|
92
|
+
end
|
93
|
+
|
94
|
+
it 'should fail if no runner' do
|
95
|
+
expect { saga.send :transition, :add_toppings }.to raise_error
|
96
|
+
end
|
97
|
+
end
|
98
|
+
|
99
|
+
describe '#resolve' do
|
100
|
+
it 'should call transition on the runner' do
|
101
|
+
runner = spy resolve: true
|
102
|
+
saga.instance_variable_set(:@runner, runner)
|
103
|
+
saga.send :resolve, 'value'
|
104
|
+
|
105
|
+
expect(runner).to have_received(:resolve).with 'value'
|
106
|
+
end
|
107
|
+
|
108
|
+
it 'should fail if no runner' do
|
109
|
+
expect { saga.send :resolve, 'value' }.to raise_error
|
110
|
+
end
|
111
|
+
end
|
112
|
+
|
113
|
+
describe '#reject' do
|
114
|
+
it 'should call transition on the runner' do
|
115
|
+
runner = spy reject: true
|
116
|
+
saga.instance_variable_set(:@runner, runner)
|
117
|
+
saga.send :reject, 'reason'
|
118
|
+
|
119
|
+
expect(runner).to have_received(:reject).with 'reason'
|
120
|
+
end
|
121
|
+
|
122
|
+
it 'should fail if no runner' do
|
123
|
+
expect { saga.send :reject, 'reason' }.to raise_error
|
124
|
+
end
|
125
|
+
end
|
126
|
+
end
|