stateful.rb 2.0.0 → 2.2.0

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 (55) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +137 -0
  3. data/Gemfile +2 -0
  4. data/README.md +309 -0
  5. data/lib/Stateful/Sequel/ClassMethods.rb +117 -0
  6. data/lib/Stateful/Sequel.rb +30 -0
  7. data/lib/Stateful/VERSION.rb +1 -1
  8. data/lib/Stateful.rb +4 -1
  9. data/stateful.rb.gemspec +42 -0
  10. data/test/ActiveRecord/WithColumnName.rb +159 -0
  11. data/test/ActiveRecord/WithExplicitDeterministicEventOrdering.rb +159 -0
  12. data/test/ActiveRecord/WithExplicitGlobalNonDeterministicEventOrdering.rb +165 -0
  13. data/test/ActiveRecord/WithExplicitGlobalNonDeterministicEventOrderingAndInitialStateBlock.rb +163 -0
  14. data/test/ActiveRecord/WithExplicitNonDeterministicEventOrdering.rb +161 -0
  15. data/test/ActiveRecord/WithInitialStateBlock.rb +157 -0
  16. data/test/ActiveRecord/WithMultipleFinalStates.rb +188 -0
  17. data/test/ActiveRecord/WithMultipleStateMachines.rb +221 -0
  18. data/test/ActiveRecord/WithMultipleStateMachinesAndStatefulBlock.rb +223 -0
  19. data/test/ActiveRecord/WithPersistenceNonSpecificExtend.rb +159 -0
  20. data/test/ActiveRecord/WithPersistenceNonSpecificExtendAndStatefulBlock.rb +161 -0
  21. data/test/ActiveRecord/WithPersistenceSpecificExtend.rb +159 -0
  22. data/test/ActiveRecord/WithPersistenceSpecificExtendAndStatefulBlock.rb +161 -0
  23. data/test/ActiveRecord/test_helper.rb +9 -0
  24. data/test/ActiveRecord.rb +5 -0
  25. data/test/Poro/WithExplicitDeterministicEventOrdering.rb +147 -0
  26. data/test/Poro/WithExplicitGlobalNonDeterministicEventOrdering.rb +153 -0
  27. data/test/Poro/WithExplicitGlobalNonDeterministicEventOrderingAndInitialStateBlock.rb +151 -0
  28. data/test/Poro/WithExplicitNonDeterministicEventOrdering.rb +149 -0
  29. data/test/Poro/WithInitialStateBlock.rb +145 -0
  30. data/test/Poro/WithMultipleFinalStates.rb +176 -0
  31. data/test/Poro/WithMultipleStateMachines.rb +192 -0
  32. data/test/Poro/WithMultipleStateMachinesAndStatefulBlock.rb +194 -0
  33. data/test/Poro/WithPersistenceNonSpecificExtend.rb +147 -0
  34. data/test/Poro/WithPersistenceNonSpecificExtendAndStatefulBlock.rb +149 -0
  35. data/test/Poro/WithPersistenceSpecificExtend.rb +147 -0
  36. data/test/Poro/WithPersistenceSpecificExtendAndStatefulBlock.rb +149 -0
  37. data/test/Poro/WithVariableName.rb +147 -0
  38. data/test/Poro.rb +5 -0
  39. data/test/Sequel/WithColumnName.rb +154 -0
  40. data/test/Sequel/WithExplicitDeterministicEventOrdering.rb +154 -0
  41. data/test/Sequel/WithExplicitGlobalNonDeterministicEventOrdering.rb +160 -0
  42. data/test/Sequel/WithExplicitGlobalNonDeterministicEventOrderingAndInitialStateBlock.rb +158 -0
  43. data/test/Sequel/WithExplicitNonDeterministicEventOrdering.rb +156 -0
  44. data/test/Sequel/WithInitialStateBlock.rb +152 -0
  45. data/test/Sequel/WithMultipleFinalStates.rb +183 -0
  46. data/test/Sequel/WithMultipleStateMachines.rb +216 -0
  47. data/test/Sequel/WithMultipleStateMachinesAndStatefulBlock.rb +218 -0
  48. data/test/Sequel/WithPersistenceNonSpecificExtend.rb +154 -0
  49. data/test/Sequel/WithPersistenceNonSpecificExtendAndStatefulBlock.rb +156 -0
  50. data/test/Sequel/WithPersistenceSpecificExtend.rb +154 -0
  51. data/test/Sequel/WithPersistenceSpecificExtendAndStatefulBlock.rb +156 -0
  52. data/test/Sequel/test_helper.rb +5 -0
  53. data/test/Sequel.rb +5 -0
  54. data/test/Stateful.rb +15 -0
  55. metadata +69 -4
@@ -0,0 +1,156 @@
1
+ # test/Sequel/WithExplicitNonDeterministicEventOrdering.rb
2
+
3
+ gem 'minitest'
4
+ gem 'minitest-spec-context'
5
+
6
+ require 'minitest/autorun'
7
+ require 'minitest-spec-context'
8
+
9
+ require_relative './test_helper'
10
+
11
+ lib_dir = File.expand_path(File.join(__FILE__, '..', '..', '..', 'lib'))
12
+ $LOAD_PATH.unshift(lib_dir) unless $LOAD_PATH.include?(lib_dir)
13
+
14
+ require 'Stateful'
15
+
16
+ DB.create_table :sequel_machine6s do
17
+ primary_key :id
18
+ String :current_state
19
+ end
20
+
21
+ class SequelMachine6 < Sequel::Model
22
+ extend Stateful
23
+
24
+ initial_state :initial_state, non_deterministic: true do
25
+ on :an_event => :next_state
26
+ on :another_event => :final_state
27
+ end
28
+
29
+ state :next_state do
30
+ on :yet_another_event => :final_state
31
+ end
32
+
33
+ final_state :final_state
34
+ end
35
+
36
+ describe Stateful::Sequel do
37
+ let(:machine){SequelMachine6.create}
38
+
39
+ it "must have an initial state" do
40
+ _(machine.initial_state).wont_be_nil
41
+ end
42
+
43
+ it "must have a final state (if one has been specified)" do
44
+ if SequelMachine6.final_state?
45
+ _(machine.final_state).wont_be_nil
46
+ end
47
+ end
48
+
49
+ context "initial_state" do
50
+ it "must have an initial state before any events occur" do
51
+ _(machine.initial_state?).must_equal true
52
+ end
53
+
54
+ it "must have an initial state consistent with what is given" do
55
+ _(machine.initial_state).must_equal SequelMachine6.stateful_state_machine.initial_state
56
+ end
57
+
58
+ it "must have an initial state with name as per the name given" do
59
+ _(machine.initial_state.name).must_equal :initial_state
60
+ end
61
+
62
+ it "must not be in the final state (assuming that initial and final are different states)" do
63
+ _(machine.final_state?).must_equal false
64
+ end
65
+
66
+ it "must know what it's next state is given an event name" do
67
+ _(machine.next_state(:an_event)).must_equal SequelMachine6.stateful_state_machine.find(:next_state)
68
+ _(machine.next_state(:another_event)).must_equal SequelMachine6.stateful_state_machine.find(:final_state)
69
+ end
70
+
71
+ it "must have an intial state which has a collection of transitions to other states" do
72
+ _(machine.transitions.class).must_equal Array
73
+ _(machine.transitions.all?{|transition| transition.is_a?(Stateful::Transition)}).must_equal true
74
+ end
75
+
76
+ it "must have two transitions to other states" do
77
+ _(machine.transitions.size).must_equal 2
78
+ end
79
+
80
+ it "must know what transitions are available" do
81
+ transition_pairs = machine.transitions.collect{|t| [t.event_name, t.next_state_name]}
82
+ _(transition_pairs).must_include [:an_event, :next_state]
83
+ _(transition_pairs).must_include [:another_event, :final_state]
84
+ end
85
+
86
+ it "must be active" do
87
+ _(machine.active?).must_equal true
88
+ end
89
+ end
90
+
91
+ context "next_state" do
92
+ before do
93
+ machine.an_event
94
+ end
95
+
96
+ it "must not be in the initial state" do
97
+ _(machine.initial_state?).must_equal false
98
+ end
99
+
100
+ it "must not be in the final state" do
101
+ _(machine.final_state?).must_equal false
102
+ end
103
+
104
+ it "must be in the next_state state" do
105
+ _(machine.current_state.name).must_equal :next_state
106
+ _(machine.next_state?).must_equal true
107
+ end
108
+
109
+ it "must have a collection of transitions to other states" do
110
+ _(machine.transitions.class).must_equal Array
111
+ _(machine.transitions.all?{|transition| transition.is_a?(Stateful::Transition)}).must_equal true
112
+ end
113
+
114
+ it "must have one transition to other states" do
115
+ _(machine.transitions.size).must_equal 1
116
+ end
117
+
118
+ it "must know what transitions are available" do
119
+ _(machine.transitions.collect{|t| [t.event_name, t.next_state_name]}).must_equal [[:yet_another_event, :final_state]]
120
+ end
121
+
122
+ it "must be active" do
123
+ _(machine.active?).must_equal true
124
+ end
125
+ end
126
+
127
+ context "final_state" do
128
+ before do
129
+ machine.another_event
130
+ end
131
+
132
+ it "must have a final state" do
133
+ _(machine.final_state?).must_equal true
134
+ end
135
+
136
+ it "must have a final state consistent with what is given" do
137
+ _(machine.final_state).must_equal SequelMachine6.stateful_state_machine.final_state
138
+ end
139
+
140
+ it "must have a state with name as per the name given" do
141
+ _(machine.current_state.name).must_equal :final_state
142
+ end
143
+
144
+ it "must not be in the initial state (assuming that initial and final are different states)" do
145
+ _(machine.initial_state?).must_equal false
146
+ end
147
+
148
+ it "must have no transitions" do
149
+ _(machine.transitions.empty?).must_equal true
150
+ end
151
+
152
+ it "must not be active" do
153
+ _(machine.active?).must_equal false
154
+ end
155
+ end
156
+ end
@@ -0,0 +1,152 @@
1
+ # test/Sequel/WithInitialStateBlock.rb
2
+
3
+ gem 'minitest'
4
+ gem 'minitest-spec-context'
5
+
6
+ require 'minitest/autorun'
7
+ require 'minitest-spec-context'
8
+
9
+ require_relative './test_helper'
10
+
11
+ lib_dir = File.expand_path(File.join(__FILE__, '..', '..', '..', 'lib'))
12
+ $LOAD_PATH.unshift(lib_dir) unless $LOAD_PATH.include?(lib_dir)
13
+
14
+ require 'Stateful'
15
+
16
+ DB.create_table :sequel_machine4s do
17
+ primary_key :id
18
+ String :current_state
19
+ end
20
+
21
+ class SequelMachine4 < Sequel::Model
22
+ extend Stateful
23
+
24
+ initial_state :initial_state do
25
+ on :an_event => :next_state
26
+ on :another_event => :final_state
27
+ end
28
+
29
+ state :next_state do
30
+ on :yet_another_event => :final_state
31
+ end
32
+
33
+ final_state :final_state
34
+ end
35
+
36
+ describe Stateful::Sequel do
37
+ let(:machine){SequelMachine4.create}
38
+
39
+ it "must have an initial state" do
40
+ _(machine.initial_state).wont_be_nil
41
+ end
42
+
43
+ it "must have a final state (if one has been specified)" do
44
+ if SequelMachine4.final_state?
45
+ _(machine.final_state).wont_be_nil
46
+ end
47
+ end
48
+
49
+ context "initial_state" do
50
+ it "must have an initial state before any events occur" do
51
+ _(machine.initial_state?).must_equal true
52
+ end
53
+
54
+ it "must have an initial state consistent with what is given" do
55
+ _(machine.initial_state).must_equal SequelMachine4.stateful_state_machine.initial_state
56
+ end
57
+
58
+ it "must have an initial state with name as per the name given" do
59
+ _(machine.initial_state.name).must_equal :initial_state
60
+ end
61
+
62
+ it "must not be in the final state (assuming that initial and final are different states)" do
63
+ _(machine.final_state?).must_equal false
64
+ end
65
+
66
+ it "must know what it's next state is given an event name" do
67
+ _(machine.next_state(:an_event)).must_equal SequelMachine4.stateful_state_machine.find(:next_state)
68
+ _(machine.next_state(:another_event)).must_equal SequelMachine4.stateful_state_machine.find(:final_state)
69
+ end
70
+
71
+ it "must have an intial state which has as set of transitions to other states" do
72
+ _(machine.transitions.class).must_equal Array
73
+ end
74
+
75
+ it "must have two transitions to other states" do
76
+ _(machine.transitions.size).must_equal 2
77
+ end
78
+
79
+ it "must know what transitions are available" do
80
+ _(machine.transitions.collect{|t| [t.event_name, t.next_state_name]}).must_equal [[:an_event, :next_state], [:another_event, :final_state]]
81
+ end
82
+
83
+ it "must be active" do
84
+ _(machine.active?).must_equal true
85
+ end
86
+ end
87
+
88
+ context "next_state" do
89
+ before do
90
+ machine.an_event
91
+ end
92
+
93
+ it "must not be in the initial state" do
94
+ _(machine.initial_state?).must_equal false
95
+ end
96
+
97
+ it "must not be in the final state" do
98
+ _(machine.final_state?).must_equal false
99
+ end
100
+
101
+ it "must be in the next_state state" do
102
+ _(machine.current_state.name).must_equal :next_state
103
+ _(machine.next_state?).must_equal true
104
+ end
105
+
106
+ it "must have a set of transitions to other states" do
107
+ _(machine.transitions.class).must_equal Array
108
+ end
109
+
110
+ it "must have one transition to other states" do
111
+ _(machine.transitions.size).must_equal 1
112
+ end
113
+
114
+ it "must know what transitions are available" do
115
+ _(machine.transitions.collect{|t| [t.event_name, t.next_state_name]}).must_equal [[:yet_another_event, :final_state]]
116
+ end
117
+
118
+ it "must be active" do
119
+ _(machine.active?).must_equal true
120
+ end
121
+ end
122
+
123
+ context "final_state" do
124
+ before do
125
+ machine.another_event
126
+ end
127
+
128
+ it "must have a final state" do
129
+ _(machine.final_state?).must_equal true
130
+ end
131
+
132
+ it "must have a final state consistent with what is given" do
133
+ _(machine.final_state).must_equal SequelMachine4.stateful_state_machine.final_state
134
+ end
135
+
136
+ it "must have a state with name as per the name given" do
137
+ _(machine.current_state.name).must_equal :final_state
138
+ end
139
+
140
+ it "must not be in the initial state (assuming that initial and final are different states)" do
141
+ _(machine.initial_state?).must_equal false
142
+ end
143
+
144
+ it "must have no transitions" do
145
+ _(machine.transitions.empty?).must_equal true
146
+ end
147
+
148
+ it "must not be active" do
149
+ _(machine.active?).must_equal false
150
+ end
151
+ end
152
+ end
@@ -0,0 +1,183 @@
1
+ # test/Sequel/WithMultipleFinalStates.rb
2
+
3
+ gem 'minitest'
4
+ gem 'minitest-spec-context'
5
+
6
+ require 'minitest/autorun'
7
+ require 'minitest-spec-context'
8
+
9
+ require_relative './test_helper'
10
+
11
+ lib_dir = File.expand_path(File.join(__FILE__, '..', '..', '..', 'lib'))
12
+ $LOAD_PATH.unshift(lib_dir) unless $LOAD_PATH.include?(lib_dir)
13
+
14
+ require 'Stateful'
15
+
16
+ DB.create_table :sequel_machine7s do
17
+ primary_key :id
18
+ String :current_state
19
+ end
20
+
21
+ class SequelMachine7 < Sequel::Model
22
+ extend Stateful
23
+
24
+ initial_state :initial_state do
25
+ on :an_event => :next_state
26
+ on :another_event => :final_state0
27
+ end
28
+
29
+ state :next_state do
30
+ on :yet_another_event => :final_state1
31
+ end
32
+
33
+ final_state :final_state0, :final_state1
34
+ end
35
+
36
+ describe Stateful::Sequel do
37
+ let(:machine){SequelMachine7.new}
38
+
39
+ it "must have an initial state" do
40
+ _(machine.initial_state).wont_be_nil
41
+ end
42
+
43
+ it "must have a final state (if one has been specified)" do
44
+ if SequelMachine7.final_state?
45
+ _(machine.final_state).wont_be_nil
46
+ end
47
+ end
48
+
49
+ context "initial_state" do
50
+ it "must have an initial state before any events occur" do
51
+ _(machine.initial_state?).must_equal true
52
+ end
53
+
54
+ it "must have an initial state consistent with what is given" do
55
+ _(machine.initial_state).must_equal SequelMachine7.stateful_state_machine.initial_state
56
+ end
57
+
58
+ it "must have an initial state with name as per the name given" do
59
+ _(machine.initial_state.name).must_equal :initial_state
60
+ end
61
+
62
+ it "must not be in the final state (assuming that initial and final are different states)" do
63
+ _(machine.final_state?).must_equal false
64
+ end
65
+
66
+ it "must know what it's next state is given an event name" do
67
+ _(machine.next_state(:an_event)).must_equal SequelMachine7.stateful_state_machine.find(:next_state)
68
+ _(machine.next_state(:another_event)).must_equal SequelMachine7.stateful_state_machine.find(:final_state0)
69
+ end
70
+
71
+ it "must have an intial state which has as set of transitions to other states" do
72
+ _(machine.transitions.class).must_equal Array
73
+ end
74
+
75
+ it "must have two transitions to other states" do
76
+ _(machine.transitions.size).must_equal 2
77
+ end
78
+
79
+ it "must know what transitions are available and in what order they are presented" do
80
+ _(machine.transitions.collect{|t| [t.event_name, t.next_state_name]}).must_equal [[:an_event, :next_state], [:another_event, :final_state0]]
81
+ end
82
+
83
+ it "must be active" do
84
+ _(machine.active?).must_equal true
85
+ end
86
+ end
87
+
88
+ context "next_state" do
89
+ before do
90
+ machine.an_event
91
+ end
92
+
93
+ it "must not be in the initial state" do
94
+ _(machine.initial_state?).must_equal false
95
+ end
96
+
97
+ it "must not be in a final state" do
98
+ _(machine.final_state?).must_equal false
99
+ end
100
+
101
+ it "must be in the next_state state" do
102
+ _(machine.current_state.name).must_equal :next_state
103
+ _(machine.next_state?).must_equal true
104
+ end
105
+
106
+ it "must have a set of transitions to other states" do
107
+ _(machine.transitions.class).must_equal Array
108
+ end
109
+
110
+ it "must have one transition to other states" do
111
+ _(machine.transitions.size).must_equal 1
112
+ end
113
+
114
+ it "must know what transitions are available" do
115
+ _(machine.transitions.collect{|t| [t.event_name, t.next_state_name]}).must_equal [[:yet_another_event, :final_state1]]
116
+ end
117
+
118
+ it "must be active" do
119
+ _(machine.active?).must_equal true
120
+ end
121
+ end
122
+
123
+ context "final_state0" do
124
+ before do
125
+ machine.another_event
126
+ end
127
+
128
+ it "must have a final state" do
129
+ _(machine.final_state?).must_equal true
130
+ end
131
+
132
+ it "must have a final state consistent with what is given" do
133
+ _(SequelMachine7.stateful_state_machine.final_states).must_include machine.final_state
134
+ end
135
+
136
+ it "must have a state with name as per the name given" do
137
+ _(machine.current_state.name).must_equal :final_state0
138
+ end
139
+
140
+ it "must not be in the initial state (assuming that initial and final are different states)" do
141
+ _(machine.initial_state?).must_equal false
142
+ end
143
+
144
+ it "must have no transitions" do
145
+ _(machine.transitions.empty?).must_equal true
146
+ end
147
+
148
+ it "must not be active" do
149
+ _(machine.active?).must_equal false
150
+ end
151
+ end
152
+
153
+ context "final_state1" do
154
+ before do
155
+ machine.an_event
156
+ machine.yet_another_event
157
+ end
158
+
159
+ it "must have a final state" do
160
+ _(machine.final_state?).must_equal true
161
+ end
162
+
163
+ it "must have a final state consistent with what is given" do
164
+ _(SequelMachine7.stateful_state_machine.final_states).must_include machine.final_state
165
+ end
166
+
167
+ it "must have a state with name as per the name given" do
168
+ _(machine.current_state.name).must_equal :final_state1
169
+ end
170
+
171
+ it "must not be in the initial state (assuming that initial and final are different states)" do
172
+ _(machine.initial_state?).must_equal false
173
+ end
174
+
175
+ it "must have no transitions" do
176
+ _(machine.transitions.empty?).must_equal true
177
+ end
178
+
179
+ it "must not be active" do
180
+ _(machine.active?).must_equal false
181
+ end
182
+ end
183
+ end
@@ -0,0 +1,216 @@
1
+ # test/Sequel/WithMultipleStateMachines.rb
2
+
3
+ gem 'minitest'
4
+ gem 'minitest-spec-context'
5
+
6
+ require 'minitest/autorun'
7
+ require 'minitest-spec-context'
8
+
9
+ require_relative './test_helper'
10
+
11
+ lib_dir = File.expand_path(File.join(__FILE__, '..', '..', '..', 'lib'))
12
+ $LOAD_PATH.unshift(lib_dir) unless $LOAD_PATH.include?(lib_dir)
13
+
14
+ require 'Stateful'
15
+
16
+ DB.create_table :sequel_machine11s do
17
+ primary_key :id
18
+ String :fulfilment_state
19
+ String :payment_state
20
+ end
21
+
22
+ class SequelMachine11 < Sequel::Model
23
+ extend Stateful
24
+
25
+ state_machine :fulfilment do
26
+ initial_state :pending do
27
+ on :items_collected => :ready
28
+ end
29
+
30
+ state :ready do
31
+ on :dispatch => :dispatched
32
+ end
33
+
34
+ final_state :dispatched
35
+ end
36
+
37
+ state_machine :payment do
38
+ initial_state :unpaid do
39
+ on :pay => :paid
40
+ end
41
+
42
+ state :paid do
43
+ on :refund => :refunded
44
+ end
45
+
46
+ final_state :refunded
47
+ end
48
+ end
49
+
50
+ describe Stateful::Sequel do
51
+ let(:machine){SequelMachine11.create}
52
+
53
+ context "initial states" do
54
+ it "must be in the fulfilment initial state" do
55
+ _(machine.fulfilment_initial_state?).must_equal true
56
+ end
57
+
58
+ it "must be in the payment initial state" do
59
+ _(machine.payment_initial_state?).must_equal true
60
+ end
61
+
62
+ it "must have the correct fulfilment state name" do
63
+ _(machine.fulfilment_state.name).must_equal :pending
64
+ end
65
+
66
+ it "must have the correct payment state name" do
67
+ _(machine.payment_state.name).must_equal :unpaid
68
+ end
69
+
70
+ it "must have fulfilment transitions" do
71
+ _(machine.fulfilment_transitions.size).must_equal 1
72
+ _(machine.fulfilment_transitions.first.event_name).must_equal :items_collected
73
+ end
74
+
75
+ it "must have payment transitions" do
76
+ _(machine.payment_transitions.size).must_equal 1
77
+ _(machine.payment_transitions.first.event_name).must_equal :pay
78
+ end
79
+
80
+ it "must not be in a fulfilment final state" do
81
+ _(machine.fulfilment_final_state?).must_equal false
82
+ end
83
+
84
+ it "must not be in a payment final state" do
85
+ _(machine.payment_final_state?).must_equal false
86
+ end
87
+
88
+ it "must have domain predicates" do
89
+ _(machine.pending?).must_equal true
90
+ _(machine.unpaid?).must_equal true
91
+ end
92
+ end
93
+
94
+ context "independent state transitions" do
95
+ before do
96
+ machine.pay
97
+ end
98
+
99
+ it "must still be in the fulfilment initial state" do
100
+ _(machine.fulfilment_initial_state?).must_equal true
101
+ _(machine.pending?).must_equal true
102
+ end
103
+
104
+ it "must have advanced the payment state" do
105
+ _(machine.payment_state.name).must_equal :paid
106
+ _(machine.paid?).must_equal true
107
+ end
108
+
109
+ it "must not be in the payment initial state" do
110
+ _(machine.payment_initial_state?).must_equal false
111
+ end
112
+
113
+ it "must have persisted the payment state" do
114
+ machine.refresh
115
+ _(machine.payment_state.name).must_equal :paid
116
+ end
117
+
118
+ it "must have persisted the fulfilment state as initial" do
119
+ machine.refresh
120
+ _(machine.fulfilment_state.name).must_equal :pending
121
+ end
122
+ end
123
+
124
+ context "both machines advanced" do
125
+ before do
126
+ machine.items_collected
127
+ machine.pay
128
+ end
129
+
130
+ it "must be in the ready fulfilment state" do
131
+ _(machine.fulfilment_state.name).must_equal :ready
132
+ _(machine.ready?).must_equal true
133
+ end
134
+
135
+ it "must be in the paid payment state" do
136
+ _(machine.payment_state.name).must_equal :paid
137
+ _(machine.paid?).must_equal true
138
+ end
139
+
140
+ it "must have correct fulfilment transitions" do
141
+ _(machine.fulfilment_transitions.size).must_equal 1
142
+ _(machine.fulfilment_transitions.first.event_name).must_equal :dispatch
143
+ end
144
+
145
+ it "must have correct payment transitions" do
146
+ _(machine.payment_transitions.size).must_equal 1
147
+ _(machine.payment_transitions.first.event_name).must_equal :refund
148
+ end
149
+
150
+ it "must have persisted both states" do
151
+ machine.refresh
152
+ _(machine.fulfilment_state.name).must_equal :ready
153
+ _(machine.payment_state.name).must_equal :paid
154
+ end
155
+ end
156
+
157
+ context "fulfilment final state" do
158
+ before do
159
+ machine.items_collected
160
+ machine.dispatch
161
+ end
162
+
163
+ it "must be in the fulfilment final state" do
164
+ _(machine.fulfilment_final_state?).must_equal true
165
+ _(machine.dispatched?).must_equal true
166
+ end
167
+
168
+ it "must have no fulfilment transitions" do
169
+ _(machine.fulfilment_transitions.empty?).must_equal true
170
+ end
171
+ end
172
+
173
+ context "payment final state" do
174
+ before do
175
+ machine.pay
176
+ machine.refund
177
+ end
178
+
179
+ it "must be in the payment final state" do
180
+ _(machine.payment_final_state?).must_equal true
181
+ _(machine.refunded?).must_equal true
182
+ end
183
+
184
+ it "must have no payment transitions" do
185
+ _(machine.payment_transitions.empty?).must_equal true
186
+ end
187
+ end
188
+
189
+ context "active?" do
190
+ it "must be active when all machines are in initial states" do
191
+ _(machine.active?).must_equal true
192
+ end
193
+
194
+ it "must be active when one machine has reached final state" do
195
+ machine.items_collected
196
+ machine.dispatch
197
+ _(machine.fulfilment_final_state?).must_equal true
198
+ _(machine.active?).must_equal true
199
+ end
200
+
201
+ it "must not be active when all machines have reached final states" do
202
+ machine.items_collected
203
+ machine.dispatch
204
+ machine.pay
205
+ machine.refund
206
+ _(machine.active?).must_equal false
207
+ end
208
+
209
+ it "must be able to check a specific machine" do
210
+ machine.items_collected
211
+ machine.dispatch
212
+ _(machine.active?(:fulfilment)).must_equal false
213
+ _(machine.active?(:payment)).must_equal true
214
+ end
215
+ end
216
+ end