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,78 @@
|
|
1
|
+
RSpec.describe Handler::Command do
|
2
|
+
subject(:handler) { Handler::Command.new message, server }
|
3
|
+
|
4
|
+
let(:command) { double }
|
5
|
+
let(:commandee_id) { SecureRandom.uuid }
|
6
|
+
let(:message) { double to_command: command, commandee_id: commandee_id }
|
7
|
+
let(:server) { double }
|
8
|
+
|
9
|
+
let(:node) { double }
|
10
|
+
let(:local) { true }
|
11
|
+
let(:fake_locator) { double local?: local, primary_node: node }
|
12
|
+
let(:locator_class) { double new: fake_locator }
|
13
|
+
|
14
|
+
before do
|
15
|
+
stub_const 'Aggro::Locator', locator_class
|
16
|
+
end
|
17
|
+
|
18
|
+
describe '#call' do
|
19
|
+
context 'comandee is not handled by the server' do
|
20
|
+
let(:node_id) { SecureRandom.uuid }
|
21
|
+
let(:client) { spy post: Message::OK.new }
|
22
|
+
let(:node) { double id: node_id, client: client }
|
23
|
+
let(:local) { false }
|
24
|
+
|
25
|
+
it 'should forward the request to the correct node and return reply' do
|
26
|
+
expect(handler.call).to be_a Message::OK
|
27
|
+
expect(client).to have_received(:post)
|
28
|
+
end
|
29
|
+
end
|
30
|
+
|
31
|
+
context 'local system knows the command' do
|
32
|
+
context 'commandee exists on system' do
|
33
|
+
let(:channel) { spy handles_command?: handles }
|
34
|
+
|
35
|
+
before do
|
36
|
+
fake_channels = { commandee_id => channel }
|
37
|
+
stub_const 'Aggro', double(channels: fake_channels)
|
38
|
+
end
|
39
|
+
|
40
|
+
context 'channel understands command type' do
|
41
|
+
let(:handles) { true }
|
42
|
+
|
43
|
+
it 'should return OK' do
|
44
|
+
expect(handler.call).to be_a Message::OK
|
45
|
+
end
|
46
|
+
|
47
|
+
it 'should forward command to the channel' do
|
48
|
+
handler.call
|
49
|
+
|
50
|
+
expect(channel).to have_received(:forward_command).with command
|
51
|
+
end
|
52
|
+
end
|
53
|
+
|
54
|
+
context 'channel does not understand command type' do
|
55
|
+
let(:handles) { false }
|
56
|
+
|
57
|
+
it 'should return UnhandledOperation' do
|
58
|
+
expect(handler.call).to be_a Message::UnhandledOperation
|
59
|
+
end
|
60
|
+
end
|
61
|
+
end
|
62
|
+
|
63
|
+
context 'commandee does not exist on system' do
|
64
|
+
it 'should return InvalidTarget' do
|
65
|
+
expect(handler.call).to be_a Message::InvalidTarget
|
66
|
+
end
|
67
|
+
end
|
68
|
+
end
|
69
|
+
|
70
|
+
context 'local system does not know the command' do
|
71
|
+
it 'should return UnknownOperation' do
|
72
|
+
allow(message).to receive(:to_command).and_return(nil)
|
73
|
+
|
74
|
+
expect(handler.call).to be_a Message::UnknownOperation
|
75
|
+
end
|
76
|
+
end
|
77
|
+
end
|
78
|
+
end
|
@@ -0,0 +1,64 @@
|
|
1
|
+
RSpec.describe Handler::CreateAggregate do
|
2
|
+
subject(:handler) { Handler::CreateAggregate.new message, server }
|
3
|
+
|
4
|
+
let(:type) { 'Test' }
|
5
|
+
let(:id) { SecureRandom.uuid }
|
6
|
+
let(:message) { double id: id, type: type }
|
7
|
+
let(:server) { double }
|
8
|
+
|
9
|
+
let(:node) { double }
|
10
|
+
let(:local) { true }
|
11
|
+
let(:fake_locator) { double local?: local, primary_node: node }
|
12
|
+
let(:locator_class) { double new: fake_locator }
|
13
|
+
|
14
|
+
let(:fake_store) { spy(create: true) }
|
15
|
+
|
16
|
+
before do
|
17
|
+
stub_const 'Aggro::Locator', locator_class
|
18
|
+
|
19
|
+
allow(Aggro).to receive(:store).and_return fake_store
|
20
|
+
end
|
21
|
+
|
22
|
+
describe '#call' do
|
23
|
+
context 'id is handled by the server' do
|
24
|
+
it 'should return an OK message' do
|
25
|
+
expect(handler.call).to be_a Message::OK
|
26
|
+
end
|
27
|
+
|
28
|
+
it 'should create the aggregate in the current store' do
|
29
|
+
handler.call
|
30
|
+
|
31
|
+
expect(fake_store).to have_received(:create).with(id, type)
|
32
|
+
end
|
33
|
+
|
34
|
+
context 'does not exist in the channel list' do
|
35
|
+
let(:fake_channels) { {} }
|
36
|
+
|
37
|
+
let(:fake_channel) { double }
|
38
|
+
let(:channel_class) { double new: fake_channel }
|
39
|
+
|
40
|
+
before do
|
41
|
+
allow(Aggro).to receive(:channels).and_return fake_channels
|
42
|
+
stub_const 'Aggro::Channel', channel_class
|
43
|
+
end
|
44
|
+
|
45
|
+
it 'should add a channel for the aggregate to the channels' do
|
46
|
+
handler.call
|
47
|
+
|
48
|
+
expect(fake_channels[id]).to eq fake_channel
|
49
|
+
end
|
50
|
+
end
|
51
|
+
end
|
52
|
+
|
53
|
+
context 'id is not handled by the server' do
|
54
|
+
let(:node_id) { SecureRandom.uuid }
|
55
|
+
let(:node) { double id: node_id }
|
56
|
+
let(:local) { false }
|
57
|
+
|
58
|
+
it 'should return Ask with another node to try' do
|
59
|
+
expect(handler.call).to be_a Message::Ask
|
60
|
+
expect(handler.call.node_id).to eq node_id
|
61
|
+
end
|
62
|
+
end
|
63
|
+
end
|
64
|
+
end
|
@@ -0,0 +1,45 @@
|
|
1
|
+
RSpec.describe Handler::GetEvents do
|
2
|
+
subject(:handler) { Handler::GetEvents.new message, server }
|
3
|
+
|
4
|
+
let(:type) { 'Test' }
|
5
|
+
let(:id) { SecureRandom.uuid }
|
6
|
+
let(:message) { double id: id, from_version: 0 }
|
7
|
+
let(:server) { double }
|
8
|
+
|
9
|
+
let(:node) { double }
|
10
|
+
let(:local) { true }
|
11
|
+
let(:fake_locator) { double local?: local, primary_node: node }
|
12
|
+
let(:locator_class) { double new: fake_locator }
|
13
|
+
|
14
|
+
let(:stream) { double id: id, type: type, events: [] }
|
15
|
+
let(:fake_store) { spy(read: [stream]) }
|
16
|
+
|
17
|
+
before do
|
18
|
+
stub_const 'Aggro::Locator', locator_class
|
19
|
+
|
20
|
+
allow(Aggro).to receive(:store).and_return fake_store
|
21
|
+
end
|
22
|
+
|
23
|
+
describe '#call' do
|
24
|
+
context 'id is handled by the server' do
|
25
|
+
it 'should return an Events message' do
|
26
|
+
response = handler.call
|
27
|
+
|
28
|
+
expect(response).to be_a Message::Events
|
29
|
+
expect(response.id).to eq id
|
30
|
+
expect(response.events).to eq []
|
31
|
+
end
|
32
|
+
end
|
33
|
+
|
34
|
+
context 'id is not handled by the server' do
|
35
|
+
let(:node_id) { SecureRandom.uuid }
|
36
|
+
let(:node) { double id: node_id }
|
37
|
+
let(:local) { false }
|
38
|
+
|
39
|
+
it 'should return Ask with another node to try' do
|
40
|
+
expect(handler.call).to be_a Message::Ask
|
41
|
+
expect(handler.call.node_id).to eq node_id
|
42
|
+
end
|
43
|
+
end
|
44
|
+
end
|
45
|
+
end
|
@@ -0,0 +1,78 @@
|
|
1
|
+
RSpec.describe Handler::Query do
|
2
|
+
subject(:handler) { Handler::Query.new message, server }
|
3
|
+
|
4
|
+
let(:query) { double }
|
5
|
+
let(:queryable_id) { SecureRandom.uuid }
|
6
|
+
let(:message) { double to_query: query, queryable_id: queryable_id }
|
7
|
+
let(:server) { double }
|
8
|
+
|
9
|
+
let(:node) { double }
|
10
|
+
let(:local) { true }
|
11
|
+
let(:fake_locator) { double local?: local, primary_node: node }
|
12
|
+
let(:locator_class) { double new: fake_locator }
|
13
|
+
|
14
|
+
before do
|
15
|
+
stub_const 'Aggro::Locator', locator_class
|
16
|
+
end
|
17
|
+
|
18
|
+
describe '#call' do
|
19
|
+
context 'comandee is not handled by the server' do
|
20
|
+
let(:node_id) { SecureRandom.uuid }
|
21
|
+
let(:client) { spy post: Message::Result.new }
|
22
|
+
let(:node) { double id: node_id, client: client }
|
23
|
+
let(:local) { false }
|
24
|
+
|
25
|
+
it 'should forward the request to the correct node and return reply' do
|
26
|
+
expect(handler.call).to be_a Message::Result
|
27
|
+
expect(client).to have_received(:post)
|
28
|
+
end
|
29
|
+
end
|
30
|
+
|
31
|
+
context 'local system knows the query' do
|
32
|
+
context 'queryee exists on system' do
|
33
|
+
let(:channel) { spy handles_query?: handles }
|
34
|
+
|
35
|
+
before do
|
36
|
+
fake_channels = { queryable_id => channel }
|
37
|
+
stub_const 'Aggro', double(channels: fake_channels)
|
38
|
+
end
|
39
|
+
|
40
|
+
context 'channel understands query type' do
|
41
|
+
let(:handles) { true }
|
42
|
+
|
43
|
+
it 'should return Result' do
|
44
|
+
expect(handler.call).to be_a Message::Result
|
45
|
+
end
|
46
|
+
|
47
|
+
it 'should forward query to the channel' do
|
48
|
+
handler.call
|
49
|
+
|
50
|
+
expect(channel).to have_received(:run_query).with query
|
51
|
+
end
|
52
|
+
end
|
53
|
+
|
54
|
+
context 'channel does not understand query type' do
|
55
|
+
let(:handles) { false }
|
56
|
+
|
57
|
+
it 'should return UnhandledOperation' do
|
58
|
+
expect(handler.call).to be_a Message::UnhandledOperation
|
59
|
+
end
|
60
|
+
end
|
61
|
+
end
|
62
|
+
|
63
|
+
context 'queryee does not exist on system' do
|
64
|
+
it 'should return InvalidTarget' do
|
65
|
+
expect(handler.call).to be_a Message::InvalidTarget
|
66
|
+
end
|
67
|
+
end
|
68
|
+
end
|
69
|
+
|
70
|
+
context 'local system does not know the query' do
|
71
|
+
it 'should return UnknownOperation' do
|
72
|
+
allow(message).to receive(:to_query).and_return(nil)
|
73
|
+
|
74
|
+
expect(handler.call).to be_a Message::UnknownOperation
|
75
|
+
end
|
76
|
+
end
|
77
|
+
end
|
78
|
+
end
|
@@ -0,0 +1,64 @@
|
|
1
|
+
RSpec.describe Handler::StartSaga do
|
2
|
+
subject(:handler) { Handler::StartSaga.new message, server }
|
3
|
+
|
4
|
+
class TestSaga
|
5
|
+
include Saga
|
6
|
+
end
|
7
|
+
|
8
|
+
let(:id) { SecureRandom.uuid }
|
9
|
+
let(:args) { { test: 'foo' } }
|
10
|
+
let(:message) { double name: 'TestSaga', id: id, args: args }
|
11
|
+
let(:server) { double }
|
12
|
+
|
13
|
+
let(:node) { double }
|
14
|
+
let(:local) { true }
|
15
|
+
let(:fake_locator) { double local?: local, primary_node: node }
|
16
|
+
let(:locator_class) { double new: fake_locator }
|
17
|
+
|
18
|
+
before do
|
19
|
+
stub_const 'Aggro::Locator', locator_class
|
20
|
+
end
|
21
|
+
|
22
|
+
describe '#call' do
|
23
|
+
context 'saga is handled by the server' do
|
24
|
+
let(:channel) { spy }
|
25
|
+
|
26
|
+
before do
|
27
|
+
stub_const 'Aggro::Channel', double(new: channel)
|
28
|
+
end
|
29
|
+
|
30
|
+
context 'channel understands command type' do
|
31
|
+
it 'should return OK' do
|
32
|
+
expect(handler.call).to be_a Message::OK
|
33
|
+
end
|
34
|
+
|
35
|
+
it 'should send :start to the channel' do
|
36
|
+
handler.call
|
37
|
+
|
38
|
+
expect(channel).to have_received(:forward_command).with \
|
39
|
+
SagaRunner::StartSaga
|
40
|
+
end
|
41
|
+
end
|
42
|
+
|
43
|
+
context 'saga is not handled by the server' do
|
44
|
+
let(:node_id) { SecureRandom.uuid }
|
45
|
+
let(:client) { spy post: Message::OK.new }
|
46
|
+
let(:node) { double id: node_id, client: client }
|
47
|
+
let(:local) { false }
|
48
|
+
|
49
|
+
it 'should forward the request to the correct node and return reply' do
|
50
|
+
expect(handler.call).to be_a Message::OK
|
51
|
+
expect(client).to have_received(:post)
|
52
|
+
end
|
53
|
+
end
|
54
|
+
end
|
55
|
+
|
56
|
+
context 'local system does not know the command' do
|
57
|
+
it 'should return an UnknownOperation' do
|
58
|
+
allow(message).to receive(:name).and_return('NotReal')
|
59
|
+
|
60
|
+
expect(handler.call).to be_a Message::UnknownOperation
|
61
|
+
end
|
62
|
+
end
|
63
|
+
end
|
64
|
+
end
|
@@ -0,0 +1,52 @@
|
|
1
|
+
RSpec.describe LocalNode do
|
2
|
+
subject(:node) { LocalNode.new('flashing-sparkle') }
|
3
|
+
|
4
|
+
let(:fake_server) { spy(handle_message: 'OK') }
|
5
|
+
|
6
|
+
describe '#client' do
|
7
|
+
before do
|
8
|
+
allow(Aggro).to receive(:server).and_return(fake_server)
|
9
|
+
end
|
10
|
+
|
11
|
+
it 'should return a Client-like object which locally routes messages' do
|
12
|
+
node.client.post 'MSG'
|
13
|
+
expect(fake_server).to have_received(:handle_message).with('MSG')
|
14
|
+
end
|
15
|
+
end
|
16
|
+
|
17
|
+
describe '#endpoint' do
|
18
|
+
before do
|
19
|
+
allow(Aggro).to receive(:port).and_return 6000
|
20
|
+
end
|
21
|
+
|
22
|
+
it 'should have a local TCP endpoint with the correct port' do
|
23
|
+
expect(node.endpoint).to eq 'tcp://127.0.0.1:6000'
|
24
|
+
end
|
25
|
+
end
|
26
|
+
|
27
|
+
describe '#publisher_endpoint' do
|
28
|
+
before do
|
29
|
+
allow(Aggro).to receive(:publisher_port).and_return 7000
|
30
|
+
end
|
31
|
+
|
32
|
+
it 'should have a local TCP endpoint with the correct port' do
|
33
|
+
expect(node.publisher_endpoint).to eq 'tcp://127.0.0.1:7000'
|
34
|
+
end
|
35
|
+
end
|
36
|
+
|
37
|
+
describe '#to_s' do
|
38
|
+
let(:moved_node) { LocalNode.new('flashing-sparkle') }
|
39
|
+
let(:other_node) { LocalNode.new('dancing-sparkle') }
|
40
|
+
|
41
|
+
let(:ring) { ConsistentHashing::Ring.new }
|
42
|
+
let(:hasher) { ring.method(:hash_key) }
|
43
|
+
|
44
|
+
it 'should be consistently hashed the same if id matches' do
|
45
|
+
expect(hasher.call(node)).to eq hasher.call(moved_node)
|
46
|
+
end
|
47
|
+
|
48
|
+
it 'should be consistently hashed differently if id differs' do
|
49
|
+
expect(hasher.call(node)).to_not eq hasher.call(other_node)
|
50
|
+
end
|
51
|
+
end
|
52
|
+
end
|
@@ -0,0 +1,61 @@
|
|
1
|
+
RSpec.describe Locator do
|
2
|
+
subject(:locator) { Locator.new id }
|
3
|
+
|
4
|
+
let(:id) { SecureRandom.uuid }
|
5
|
+
let(:node) { Node.new('flashing-sparkle', '10.0.0.70') }
|
6
|
+
let(:other_node) { Node.new('winking-tiger', '10.0.0.90') }
|
7
|
+
let(:nodes) { [node, other_node] }
|
8
|
+
let(:node_list) { spy(nodes_for: nodes, state: 'initial') }
|
9
|
+
|
10
|
+
before do
|
11
|
+
allow(Aggro).to receive(:node_list).and_return node_list
|
12
|
+
end
|
13
|
+
|
14
|
+
describe '#local?' do
|
15
|
+
context 'primary node is a LocalNode' do
|
16
|
+
let(:node) { LocalNode.new('flashing-sparkle') }
|
17
|
+
|
18
|
+
it 'should return true' do
|
19
|
+
expect(locator.local?).to be_truthy
|
20
|
+
end
|
21
|
+
end
|
22
|
+
|
23
|
+
context 'primary node is not a LocalNode' do
|
24
|
+
it 'should return true' do
|
25
|
+
expect(locator.local?).to be_falsey
|
26
|
+
end
|
27
|
+
end
|
28
|
+
end
|
29
|
+
|
30
|
+
describe '#nodes' do
|
31
|
+
it 'should return the nodes on which the aggregate should persist' do
|
32
|
+
expect(locator.nodes.first.endpoint).to eq '10.0.0.70'
|
33
|
+
end
|
34
|
+
|
35
|
+
it 'should memorize the lookup to reduce hashing' do
|
36
|
+
5.times { locator.nodes }
|
37
|
+
|
38
|
+
expect(node_list).to have_received(:nodes_for).once
|
39
|
+
end
|
40
|
+
|
41
|
+
it 'should forget memorized servers if ring state changes' do
|
42
|
+
locator.nodes
|
43
|
+
allow(node_list).to receive(:state).and_return('changed')
|
44
|
+
locator.nodes
|
45
|
+
|
46
|
+
expect(node_list).to have_received(:nodes_for).twice
|
47
|
+
end
|
48
|
+
end
|
49
|
+
|
50
|
+
describe '#primary_node' do
|
51
|
+
it 'should return the first associated node' do
|
52
|
+
expect(locator.primary_node).to eq node
|
53
|
+
end
|
54
|
+
end
|
55
|
+
|
56
|
+
describe '#secondary_nodes' do
|
57
|
+
it 'should return the rest of the associated nodes' do
|
58
|
+
expect(locator.secondary_nodes).to eq [other_node]
|
59
|
+
end
|
60
|
+
end
|
61
|
+
end
|