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.
Files changed (141) hide show
  1. checksums.yaml +4 -4
  2. data/.rubocop.yml +8 -0
  3. data/.travis.yml +15 -0
  4. data/Gemfile +9 -0
  5. data/README.md +5 -1
  6. data/Rakefile +10 -0
  7. data/aggro.gemspec +8 -1
  8. data/lib/aggro.rb +191 -7
  9. data/lib/aggro/abstract_store.rb +12 -0
  10. data/lib/aggro/aggregate.rb +98 -0
  11. data/lib/aggro/aggregate_ref.rb +68 -6
  12. data/lib/aggro/attribute_dsl.rb +96 -0
  13. data/lib/aggro/binding_dsl.rb +45 -0
  14. data/lib/aggro/block_helper.rb +14 -0
  15. data/lib/aggro/channel.rb +37 -0
  16. data/lib/aggro/client.rb +12 -0
  17. data/lib/aggro/cluster_config.rb +57 -0
  18. data/lib/aggro/command.rb +16 -0
  19. data/lib/aggro/concurrent_actor.rb +26 -0
  20. data/lib/aggro/event_bus.rb +94 -0
  21. data/lib/aggro/event_dsl.rb +53 -0
  22. data/lib/aggro/event_proxy.rb +23 -0
  23. data/lib/aggro/event_serializer.rb +14 -0
  24. data/lib/aggro/file_store.rb +97 -0
  25. data/lib/aggro/file_store/reader.rb +21 -0
  26. data/lib/aggro/file_store/writer.rb +27 -0
  27. data/lib/aggro/handler/command.rb +60 -0
  28. data/lib/aggro/handler/create_aggregate.rb +42 -0
  29. data/lib/aggro/handler/get_events.rb +30 -0
  30. data/lib/aggro/handler/query.rb +60 -0
  31. data/lib/aggro/handler/start_saga.rb +56 -0
  32. data/lib/aggro/local_node.rb +28 -0
  33. data/lib/aggro/locator.rb +32 -0
  34. data/lib/aggro/message/ask.rb +16 -0
  35. data/lib/aggro/message/command.rb +36 -0
  36. data/lib/aggro/message/create_aggregate.rb +16 -0
  37. data/lib/aggro/message/endpoint.rb +16 -0
  38. data/lib/aggro/message/events.rb +24 -0
  39. data/lib/aggro/message/get_events.rb +16 -0
  40. data/lib/aggro/message/heartbeat.rb +16 -0
  41. data/lib/aggro/message/invalid_target.rb +20 -0
  42. data/lib/aggro/message/ok.rb +20 -0
  43. data/lib/aggro/message/publisher_endpoint_inquiry.rb +16 -0
  44. data/lib/aggro/message/query.rb +36 -0
  45. data/lib/aggro/message/result.rb +16 -0
  46. data/lib/aggro/message/start_saga.rb +28 -0
  47. data/lib/aggro/message/unhandled_operation.rb +20 -0
  48. data/lib/aggro/message/unknown_operation.rb +20 -0
  49. data/lib/aggro/message_parser.rb +10 -0
  50. data/lib/aggro/message_router.rb +26 -0
  51. data/lib/aggro/nanomsg_transport.rb +31 -0
  52. data/lib/aggro/nanomsg_transport/client.rb +35 -0
  53. data/lib/aggro/nanomsg_transport/connection.rb +98 -0
  54. data/lib/aggro/nanomsg_transport/publish.rb +17 -0
  55. data/lib/aggro/nanomsg_transport/publisher.rb +37 -0
  56. data/lib/aggro/nanomsg_transport/raw_reply.rb +18 -0
  57. data/lib/aggro/nanomsg_transport/raw_request.rb +18 -0
  58. data/lib/aggro/nanomsg_transport/reply.rb +17 -0
  59. data/lib/aggro/nanomsg_transport/request.rb +17 -0
  60. data/lib/aggro/nanomsg_transport/server.rb +84 -0
  61. data/lib/aggro/nanomsg_transport/socket_error.rb +20 -0
  62. data/lib/aggro/nanomsg_transport/subscribe.rb +27 -0
  63. data/lib/aggro/nanomsg_transport/subscriber.rb +82 -0
  64. data/lib/aggro/node.rb +29 -0
  65. data/lib/aggro/node_list.rb +39 -0
  66. data/lib/aggro/projection.rb +13 -0
  67. data/lib/aggro/query.rb +11 -0
  68. data/lib/aggro/saga.rb +94 -0
  69. data/lib/aggro/saga_runner.rb +87 -0
  70. data/lib/aggro/saga_runner/start_saga.rb +12 -0
  71. data/lib/aggro/saga_status.rb +29 -0
  72. data/lib/aggro/server.rb +88 -0
  73. data/lib/aggro/subscriber.rb +48 -0
  74. data/lib/aggro/subscription.rb +41 -0
  75. data/lib/aggro/transform/boolean.rb +16 -0
  76. data/lib/aggro/transform/email.rb +26 -0
  77. data/lib/aggro/transform/id.rb +34 -0
  78. data/lib/aggro/transform/integer.rb +22 -0
  79. data/lib/aggro/transform/money.rb +22 -0
  80. data/lib/aggro/transform/noop.rb +16 -0
  81. data/lib/aggro/transform/string.rb +16 -0
  82. data/lib/aggro/transform/time_interval.rb +24 -0
  83. data/lib/aggro/version.rb +1 -1
  84. data/spec/lib/aggro/abstract_store_spec.rb +15 -0
  85. data/spec/lib/aggro/aggregate_ref_spec.rb +63 -12
  86. data/spec/lib/aggro/aggregate_spec.rb +207 -0
  87. data/spec/lib/aggro/channel_spec.rb +87 -0
  88. data/spec/lib/aggro/client_spec.rb +26 -0
  89. data/spec/lib/aggro/cluster_config_spec.rb +33 -0
  90. data/spec/lib/aggro/command_spec.rb +52 -0
  91. data/spec/lib/aggro/concurrent_actor_spec.rb +44 -0
  92. data/spec/lib/aggro/event_bus_spec.rb +20 -0
  93. data/spec/lib/aggro/event_serializer_spec.rb +28 -0
  94. data/spec/lib/aggro/file_store/reader_spec.rb +32 -0
  95. data/spec/lib/aggro/file_store/writer_spec.rb +67 -0
  96. data/spec/lib/aggro/file_store_spec.rb +51 -0
  97. data/spec/lib/aggro/handler/command_spec.rb +78 -0
  98. data/spec/lib/aggro/handler/create_aggregate_spec.rb +64 -0
  99. data/spec/lib/aggro/handler/get_events_handler_spec.rb +45 -0
  100. data/spec/lib/aggro/handler/query_spec.rb +78 -0
  101. data/spec/lib/aggro/handler/start_saga_spec.rb +64 -0
  102. data/spec/lib/aggro/local_node_spec.rb +52 -0
  103. data/spec/lib/aggro/locator_spec.rb +61 -0
  104. data/spec/lib/aggro/message/ask_spec.rb +23 -0
  105. data/spec/lib/aggro/message/command_spec.rb +50 -0
  106. data/spec/lib/aggro/message/create_aggregate_spec.rb +28 -0
  107. data/spec/lib/aggro/message/endpoint_spec.rb +23 -0
  108. data/spec/lib/aggro/message/events_spec.rb +37 -0
  109. data/spec/lib/aggro/message/get_events_spec.rb +33 -0
  110. data/spec/lib/aggro/message/heartbeat_spec.rb +23 -0
  111. data/spec/lib/aggro/message/invalid_target_spec.rb +28 -0
  112. data/spec/lib/aggro/message/ok_spec.rb +27 -0
  113. data/spec/lib/aggro/message/publisher_endpoint_inquiry_spec.rb +23 -0
  114. data/spec/lib/aggro/message/query_spec.rb +50 -0
  115. data/spec/lib/aggro/message/start_saga_spec.rb +37 -0
  116. data/spec/lib/aggro/message/unhandled_operation_spec.rb +28 -0
  117. data/spec/lib/aggro/message/unknown_operation_spec.rb +28 -0
  118. data/spec/lib/aggro/message_parser_spec.rb +16 -0
  119. data/spec/lib/aggro/message_router_spec.rb +35 -0
  120. data/spec/lib/aggro/nanomsg_transport/socket_error_spec.rb +21 -0
  121. data/spec/lib/aggro/nanomsg_transport_spec.rb +37 -0
  122. data/spec/lib/aggro/node_list_spec.rb +38 -0
  123. data/spec/lib/aggro/node_spec.rb +44 -0
  124. data/spec/lib/aggro/projection_spec.rb +22 -0
  125. data/spec/lib/aggro/query_spec.rb +47 -0
  126. data/spec/lib/aggro/saga_runner_spec.rb +84 -0
  127. data/spec/lib/aggro/saga_spec.rb +126 -0
  128. data/spec/lib/aggro/saga_status_spec.rb +56 -0
  129. data/spec/lib/aggro/server_spec.rb +118 -0
  130. data/spec/lib/aggro/subscriber_spec.rb +59 -0
  131. data/spec/lib/aggro/subscription_spec.rb +50 -0
  132. data/spec/lib/aggro/transform/boolean_spec.rb +23 -0
  133. data/spec/lib/aggro/transform/email_spec.rb +13 -0
  134. data/spec/lib/aggro/transform/id_spec.rb +70 -0
  135. data/spec/lib/aggro/transform/integer_spec.rb +30 -0
  136. data/spec/lib/aggro/transform/money_spec.rb +34 -0
  137. data/spec/lib/aggro/transform/string_spec.rb +15 -0
  138. data/spec/lib/aggro/transform/time_interval_spec.rb +29 -0
  139. data/spec/lib/aggro_spec.rb +63 -19
  140. data/spec/spec_helper.rb +21 -2
  141. metadata +283 -3
@@ -0,0 +1,56 @@
1
+ RSpec.describe SagaStatus do
2
+ subject(:status) { SagaStatus.new saga_id }
3
+ let(:saga_id) { SecureRandom.uuid }
4
+
5
+ let(:event_bus) { spy subscribe: true }
6
+
7
+ before do
8
+ allow(Aggro).to receive(:event_bus).and_return event_bus
9
+ end
10
+
11
+ describe '.new' do
12
+ it 'should subscribe to the SagaRunner with the saga_runner namespace' do
13
+ status
14
+
15
+ expect(event_bus).to have_received(:subscribe).with saga_id, status
16
+ end
17
+ end
18
+
19
+ describe 'events' do
20
+ describe '#started' do
21
+ it 'should put the status into a pending state' do
22
+ status._started
23
+
24
+ expect(status.state).to eq :pending
25
+ end
26
+ end
27
+
28
+ describe '#rejected' do
29
+ it 'should put the status into a rejected state' do
30
+ status._rejected 'reason'
31
+
32
+ expect(status.state).to eq :rejected
33
+ end
34
+
35
+ it 'should set the reason' do
36
+ status._rejected 'reason'
37
+
38
+ expect(status.reason).to eq 'reason'
39
+ end
40
+ end
41
+
42
+ describe '#rejected' do
43
+ it 'should put the status into a rejected state' do
44
+ status._resolved 'value'
45
+
46
+ expect(status.state).to eq :fulfilled
47
+ end
48
+
49
+ it 'should set the value' do
50
+ status._resolved 'value'
51
+
52
+ expect(status.value).to eq 'value'
53
+ end
54
+ end
55
+ end
56
+ end
@@ -0,0 +1,118 @@
1
+ RSpec.describe Server do
2
+ subject(:server) { Server.new endpoint, publisher_endpoint }
3
+
4
+ let(:endpoint) { 'tcp://127.0.0.1:5000' }
5
+ let(:publisher_endpoint) { 'tcp://127.0.0.1:6000' }
6
+ let(:transport_server) { spy }
7
+ let(:transport_pub) { spy(publish: true) }
8
+ let(:transport) { spy(server: transport_server, publisher: transport_pub) }
9
+
10
+ before do
11
+ allow(Aggro).to receive(:transport).and_return transport
12
+ end
13
+
14
+ describe '.new' do
15
+ let(:handler) { server.method(Server::RAW_HANDLER) }
16
+
17
+ it 'should get a server which calls handler from current transport' do
18
+ server
19
+
20
+ expect(transport).to have_received(:server).with(endpoint, handler)
21
+ end
22
+
23
+ it 'should designate a handler which responds to raw messages' do
24
+ raw_message = Message::Heartbeat.new(SecureRandom.uuid).to_s
25
+
26
+ expect(handler.call(raw_message)).to be_a Message::OK
27
+ end
28
+
29
+ it 'should start a publisher for the node using the current transport' do
30
+ server
31
+
32
+ expect(transport).to have_received(:publisher).with(publisher_endpoint)
33
+ end
34
+ end
35
+
36
+ describe '#bind' do
37
+ it 'should start the transport server' do
38
+ server.bind
39
+
40
+ expect(transport_server).to have_received(:start)
41
+ end
42
+ end
43
+
44
+ describe '#publish' do
45
+ it 'publish the events to the publisher transport' do
46
+ server.publish :events
47
+
48
+ expect(transport_pub).to have_received(:publish).with :events
49
+ end
50
+ end
51
+
52
+ describe '#handle_message' do
53
+ let(:sender) { SecureRandom.uuid }
54
+
55
+ context 'message is a Command' do
56
+ let(:commandee_id) { SecureRandom.uuid }
57
+ let(:details) { { name: 'TestCommand', args: { thing: 'puppy' } } }
58
+ let(:message) { Message::Command.new(sender, commandee_id, details) }
59
+
60
+ it 'should delegate to command handler' do
61
+ handler = spy(call: true)
62
+ handler_class = spy(new: handler)
63
+
64
+ stub_const 'Aggro::Handler::Command', handler_class
65
+
66
+ server.handle_message message
67
+
68
+ expect(handler_class).to have_received(:new).with(message, server)
69
+ expect(handler).to have_received(:call)
70
+ end
71
+ end
72
+
73
+ context 'message is a CreateAggregate' do
74
+ let(:id) { SecureRandom.uuid }
75
+ let(:type) { 'Test' }
76
+ let(:message) { Message::CreateAggregate.new(sender, id, type) }
77
+
78
+ it 'should delegate to command handler' do
79
+ handler = spy(call: true)
80
+ handler_class = spy(new: handler)
81
+
82
+ stub_const 'Aggro::Handler::CreateAggregate', handler_class
83
+
84
+ server.handle_message message
85
+
86
+ expect(handler_class).to have_received(:new).with(message, server)
87
+ expect(handler).to have_received(:call)
88
+ end
89
+ end
90
+
91
+ context 'message is a Heartbeat' do
92
+ let(:message) { Message::Heartbeat.new(sender) }
93
+
94
+ it 'should return an OK message' do
95
+ expect(server.handle_message(message)).to be_a Message::OK
96
+ end
97
+ end
98
+
99
+ context 'message is a PublisherEndpointInquiry' do
100
+ let(:message) { Message::PublisherEndpointInquiry.new(sender) }
101
+
102
+ it 'should return an Endpoint message with the publisher endpoint' do
103
+ response = server.handle_message(message)
104
+
105
+ expect(response).to be_a Message::Endpoint
106
+ expect(response.endpoint).to eq publisher_endpoint
107
+ end
108
+ end
109
+ end
110
+
111
+ describe '#stop' do
112
+ it 'should stop the transport server' do
113
+ server.stop
114
+
115
+ expect(transport_server).to have_received(:stop)
116
+ end
117
+ end
118
+ end
@@ -0,0 +1,59 @@
1
+ RSpec.describe Subscriber do
2
+ subject(:subscriber) { Subscriber.new(endpoint, callable) }
3
+
4
+ let(:callable) { spy }
5
+ let(:endpoint) { 'tcp://127.0.0.1:6000' }
6
+ let(:transport_sub) { spy }
7
+ let(:transport) { spy(subscriber: transport_sub) }
8
+
9
+ before do
10
+ allow(Aggro).to receive(:transport).and_return transport
11
+ end
12
+
13
+ describe '.new' do
14
+ let(:handler) { subscriber.method(Subscriber::RAW_HANDLER) }
15
+
16
+ it 'should get a server which calls handler from current transport' do
17
+ subscriber
18
+
19
+ expect(transport).to have_received(:subscriber).with(endpoint, handler)
20
+ end
21
+
22
+ it 'should designate a handler which parses raw messages' do
23
+ topic = SecureRandom.uuid
24
+ raw_message = Message::Events.new(topic, []).to_s
25
+ handler.call(raw_message)
26
+
27
+ expect(callable).to have_received(:call).with(topic, [])
28
+ end
29
+ end
30
+
31
+ describe '#bind' do
32
+ it 'should start the transport server' do
33
+ subscriber.bind
34
+
35
+ expect(transport_sub).to have_received(:start)
36
+ end
37
+ end
38
+
39
+ describe '#handle_message' do
40
+ context 'message is an Events' do
41
+ let(:topic) { SecureRandom.uuid }
42
+ let(:message) { Message::Events.new(topic, []) }
43
+
44
+ it 'should call the callback with the topic and events' do
45
+ subscriber.handle_message message
46
+
47
+ expect(callable).to have_received(:call).with(topic, [])
48
+ end
49
+ end
50
+ end
51
+
52
+ describe '#stop' do
53
+ it 'should stop the transport server' do
54
+ subscriber.stop
55
+
56
+ expect(transport_sub).to have_received(:stop)
57
+ end
58
+ end
59
+ end
@@ -0,0 +1,50 @@
1
+ RSpec.describe Subscription do
2
+ subject(:subscription) { Subscription.new topic, subscriber, ns, filters, 0 }
3
+
4
+ let(:topic) { SecureRandom.uuid }
5
+ let(:subscriber) { double }
6
+ let(:ns) { :test }
7
+ let(:filters) { { correlation_id: correlation_id } }
8
+
9
+ let(:correlation_id) { SecureRandom.uuid }
10
+ let(:details) { { name: 'Sebastian', correlation_id: correlation_id } }
11
+ let(:event) { double(name: 'added_contact', details: details) }
12
+
13
+ let(:invokr) { spy }
14
+ before { stub_const 'Invokr', invokr }
15
+
16
+ describe '#handle_event' do
17
+ context 'subscriber can handle the event' do
18
+ before { allow(subscriber).to receive(:handles_event?).and_return true }
19
+
20
+ context 'the filters match the event details' do
21
+ it 'should call the event on the subscriber with the event details' do
22
+ subscription.handle_event event
23
+
24
+ expect(invokr).to have_received(:invoke).with \
25
+ on: subscriber, method: 'test_added_contact', using: details
26
+ end
27
+ end
28
+
29
+ context 'the filters do not match the event details' do
30
+ let(:details) { { name: 'Sebastian' } }
31
+
32
+ it 'should not call the event on the subscriber' do
33
+ subscription.handle_event event
34
+
35
+ expect(invokr).to_not have_received(:invoke)
36
+ end
37
+ end
38
+ end
39
+
40
+ context 'subscriber cannot handle the event' do
41
+ before { allow(subscriber).to receive(:handles_event?).and_return false }
42
+
43
+ it 'should not call the event on the subscriber' do
44
+ subscription.handle_event event
45
+
46
+ expect(invokr).to_not have_received(:invoke)
47
+ end
48
+ end
49
+ end
50
+ end
@@ -0,0 +1,23 @@
1
+ RSpec.describe Transform::Boolean do
2
+ describe '.deserialize' do
3
+ it 'should transform the value to a boolean' do
4
+ expect(Transform::Boolean.deserialize(false)).to eq false
5
+ expect(Transform::Boolean.deserialize(true)).to eq true
6
+ end
7
+
8
+ it 'should return nil if no boolean appropriate' do
9
+ expect(Transform::Boolean.deserialize('test')).to eq nil
10
+ end
11
+ end
12
+
13
+ describe '.serialize' do
14
+ it 'should pass through value if a bool' do
15
+ expect(Transform::Boolean.serialize(false)).to eq false
16
+ expect(Transform::Boolean.serialize(true)).to eq true
17
+ end
18
+
19
+ it 'should return nil if not a bool' do
20
+ expect(Transform::Boolean.serialize('test')).to eq nil
21
+ end
22
+ end
23
+ end
@@ -0,0 +1,13 @@
1
+ RSpec.describe Transform::Email do
2
+ describe '#serialize' do
3
+ it 'should return the value if valid' do
4
+ email = 'me@sebastianedwards.co.nz'
5
+
6
+ expect(Transform::Email.serialize(email)).to eq email
7
+ end
8
+
9
+ it 'should return nil if invalid' do
10
+ expect(Transform::Email.serialize('not an id')).to eq nil
11
+ end
12
+ end
13
+ end
@@ -0,0 +1,70 @@
1
+ RSpec.describe Transform::ID do
2
+ REGEX = Transform::ID::ID_REGEX
3
+
4
+ context 'generate flag is true' do
5
+ subject(:transformer) { Transform::ID.new generate: true }
6
+
7
+ describe '#deserialize' do
8
+ it 'should return the value if valid ID value' do
9
+ id = SecureRandom.uuid
10
+
11
+ expect(transformer.deserialize(id)).to eq id
12
+ end
13
+
14
+ it 'should generate a new ID if invalid value' do
15
+ expect(transformer.deserialize('not an id')).to_not eq 'not an id'
16
+ expect(transformer.deserialize('not an id')).to match REGEX
17
+ end
18
+
19
+ it 'should generate a new ID if nil value' do
20
+ expect(transformer.deserialize(nil)).to_not eq nil
21
+ expect(transformer.deserialize(nil)).to match REGEX
22
+ end
23
+ end
24
+
25
+ describe '#serialize' do
26
+ it 'should return the value if valid' do
27
+ id = SecureRandom.uuid
28
+
29
+ expect(transformer.serialize(id)).to eq id
30
+ end
31
+
32
+ it 'should return a generated ID if invalid' do
33
+ expect(transformer.serialize('not an id')).to_not eq 'not an id'
34
+ expect(transformer.serialize('not an id')).to match REGEX
35
+ end
36
+ end
37
+ end
38
+
39
+ context 'generate flag is false or unset' do
40
+ subject(:transformer) { Transform::ID.new }
41
+
42
+ describe '#deserialize' do
43
+ it 'should return the value if valid ID value' do
44
+ id = SecureRandom.uuid
45
+
46
+ expect(transformer.deserialize(id)).to eq id
47
+ end
48
+
49
+ it 'should return nil if invalid value' do
50
+ expect(transformer.deserialize('not an id')).to eq nil
51
+ end
52
+
53
+ it 'should return nil if nil value' do
54
+ expect(transformer.deserialize(nil)).to eq nil
55
+ end
56
+ end
57
+
58
+ describe '#serialize' do
59
+ it 'should return the value if valid' do
60
+ id = SecureRandom.uuid
61
+
62
+ expect(transformer.serialize(id)).to eq id
63
+ end
64
+
65
+ it 'should return nil if invalid' do
66
+ expect(transformer.serialize('not an id')).to eq nil
67
+ end
68
+ end
69
+ end
70
+ end
@@ -0,0 +1,30 @@
1
+ RSpec.describe Transform::Integer do
2
+ describe '.deserialize' do
3
+ it 'should transform the value to a fixnum' do
4
+ expect(Transform::Integer.deserialize(666)).to eq 666
5
+ expect(Transform::Integer.deserialize('200')).to eq 200
6
+ end
7
+
8
+ it 'should ignore extraneous characters' do
9
+ expect(Transform::Integer.deserialize('$6,000')).to eq 6000
10
+ end
11
+
12
+ it 'should round decimals down to nearest integer' do
13
+ expect(Transform::Integer.deserialize('8.0000')).to eq 8
14
+ expect(Transform::Integer.deserialize('6.4')).to eq 6
15
+ expect(Transform::Integer.deserialize('9.6')).to eq 9
16
+ expect(Transform::Integer.deserialize(12.7)).to eq 12
17
+ end
18
+
19
+ it 'should return nil if no number appropriate' do
20
+ expect(Transform::Integer.deserialize('test')).to eq nil
21
+ end
22
+ end
23
+
24
+ describe '.serialize' do
25
+ it 'should transform the value to a fixnum' do
26
+ expect(Transform::Integer.serialize(666)).to eq 666
27
+ expect(Transform::Integer.serialize('200')).to eq 200
28
+ end
29
+ end
30
+ end
@@ -0,0 +1,34 @@
1
+ require 'money'
2
+ require 'monetize'
3
+
4
+ RSpec.describe Transform::Money do
5
+ let(:ten_usd) { Money.new(1000, 'USD') }
6
+ let(:five_nzd) { Money.new(500, 'NZD') }
7
+
8
+ describe '.deserialize' do
9
+ it 'should transform strings to a Money' do
10
+ expect(Transform::Money.deserialize('$10.00 USD')).to eq ten_usd
11
+ expect(Transform::Money.deserialize('$5.00 NZD')).to eq five_nzd
12
+ end
13
+
14
+ it 'should default to USD' do
15
+ expect(Transform::Money.deserialize('$10.00')).to eq ten_usd
16
+ expect(Transform::Money.deserialize('$5.00')).to_not eq five_nzd
17
+ end
18
+
19
+ it 'should accept an integer' do
20
+ expect(Transform::Money.deserialize(10)).to eq ten_usd
21
+ end
22
+
23
+ it 'should return nil if not a string' do
24
+ expect(Transform::Money.deserialize(true)).to eq nil
25
+ end
26
+ end
27
+
28
+ describe '.serialize' do
29
+ it 'should transform the value to a string' do
30
+ expect(Transform::Money.serialize(ten_usd)).to eq '$10.00 USD'
31
+ expect(Transform::Money.serialize(five_nzd)).to eq '$5.00 NZD'
32
+ end
33
+ end
34
+ end