state_machine_rspec 0.1.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.
- data/.gitignore +17 -0
- data/.rspec +2 -0
- data/.rvmrc +52 -0
- data/Gemfile +8 -0
- data/Guardfile +6 -0
- data/LICENSE.txt +22 -0
- data/README.md +56 -0
- data/Rakefile +6 -0
- data/lib/matchers/events/handle_event.rb +29 -0
- data/lib/matchers/events/matcher.rb +55 -0
- data/lib/matchers/events/reject_event.rb +29 -0
- data/lib/matchers/states/have_state.rb +40 -0
- data/lib/matchers/states/matcher.rb +42 -0
- data/lib/matchers/states/reject_state.rb +28 -0
- data/lib/state_machine_rspec/state_machine_introspector.rb +70 -0
- data/lib/state_machine_rspec/version.rb +3 -0
- data/lib/state_machine_rspec.rb +11 -0
- data/spec/integration/integration_spec.rb +327 -0
- data/spec/integration/models/vehicle.rb +119 -0
- data/spec/matchers/events/handle_event_spec.rb +109 -0
- data/spec/matchers/events/reject_event_spec.rb +109 -0
- data/spec/matchers/states/have_state_spec.rb +110 -0
- data/spec/matchers/states/reject_state_spec.rb +81 -0
- data/spec/spec_helper.rb +12 -0
- data/state_machine_rspec.gemspec +31 -0
- metadata +228 -0
@@ -0,0 +1,327 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
require 'integration/models/vehicle'
|
3
|
+
|
4
|
+
describe Vehicle do
|
5
|
+
let(:vehicle) { Vehicle.new }
|
6
|
+
subject { vehicle }
|
7
|
+
|
8
|
+
its(:passed_inspection?) { should be_false }
|
9
|
+
|
10
|
+
shared_examples 'crashable' do
|
11
|
+
describe 'crash' do
|
12
|
+
context 'having passed inspection' do
|
13
|
+
before { vehicle.stub(:passed_inspection).and_return(true) }
|
14
|
+
pending 'keeps running' do
|
15
|
+
initial_state = vehicle.state
|
16
|
+
vehicle.crash!
|
17
|
+
|
18
|
+
vehicle.state.should eq initial_state
|
19
|
+
end
|
20
|
+
end
|
21
|
+
|
22
|
+
context 'not having passed inspection' do
|
23
|
+
before { vehicle.stub(:passed_inspection).and_return(false) }
|
24
|
+
it 'stalls' do
|
25
|
+
vehicle.crash!
|
26
|
+
vehicle.state.should eq :stalled.to_s
|
27
|
+
end
|
28
|
+
end
|
29
|
+
end
|
30
|
+
end
|
31
|
+
|
32
|
+
shared_examples 'speedless' do
|
33
|
+
it 'does not respond to speed' do
|
34
|
+
expect { vehicle.speed }.to raise_error NoMethodError
|
35
|
+
end
|
36
|
+
end
|
37
|
+
|
38
|
+
describe '#initialize' do
|
39
|
+
its(:seatbelt_on) { should be_false }
|
40
|
+
its(:time_used) { should eq 0 }
|
41
|
+
its(:auto_shop_busy) { should be_true }
|
42
|
+
end
|
43
|
+
|
44
|
+
describe '#put_on_seatbelt' do
|
45
|
+
it 'sets seatbelt_on to true' do
|
46
|
+
vehicle.seatbelt_on = false
|
47
|
+
vehicle.put_on_seatbelt
|
48
|
+
|
49
|
+
vehicle.seatbelt_on.should be_true
|
50
|
+
end
|
51
|
+
end
|
52
|
+
|
53
|
+
describe 'state machine' do
|
54
|
+
it { should have_states :parked, :idling, :stalled, :first_gear,
|
55
|
+
:second_gear, :third_gear }
|
56
|
+
it { should reject_state :flying }
|
57
|
+
|
58
|
+
it { should handle_event :ignite, when: :parked }
|
59
|
+
it { should reject_events :park, :idle, :shift_up,
|
60
|
+
:shift_down, :crash, :repair,
|
61
|
+
when: :parked }
|
62
|
+
|
63
|
+
it { should handle_events :park, :shift_up, :crash, when: :idling }
|
64
|
+
it { should reject_events :ignite, :idle, :shift_down, :repair,
|
65
|
+
when: :idling }
|
66
|
+
|
67
|
+
it { should handle_events :ignite, :repair, when: :stalled }
|
68
|
+
it { should reject_events :park, :idle, :shift_up, :shift_down, :crash,
|
69
|
+
when: :stalled }
|
70
|
+
|
71
|
+
it { should handle_events :park, :idle, :shift_up, :crash,
|
72
|
+
when: :first_gear }
|
73
|
+
it { should reject_events :ignite, :shift_down, :repair,
|
74
|
+
when: :first_gear }
|
75
|
+
|
76
|
+
it { should handle_events :shift_up, :shift_down, :crash,
|
77
|
+
when: :second_gear }
|
78
|
+
it { should reject_events :park, :ignite, :idle, :repair,
|
79
|
+
when: :second_gear }
|
80
|
+
|
81
|
+
it { should handle_events :shift_down, :crash, when: :third_gear }
|
82
|
+
it { should reject_events :park, :ignite, :idle, :shift_up, :repair,
|
83
|
+
when: :third_gear }
|
84
|
+
|
85
|
+
it 'has an initial state of "parked"' do
|
86
|
+
vehicle.should be_parked
|
87
|
+
end
|
88
|
+
|
89
|
+
it 'has an initial alarm state of "active"' do
|
90
|
+
vehicle.alarm_active?.should be_true
|
91
|
+
end
|
92
|
+
|
93
|
+
describe 'around transitions' do
|
94
|
+
it 'updates the time used' do
|
95
|
+
vehicle.should_receive(:time_used=).with(0)
|
96
|
+
Timecop.freeze { vehicle.ignite! }
|
97
|
+
end
|
98
|
+
end
|
99
|
+
|
100
|
+
context 'when parked' do
|
101
|
+
before { vehicle.state = :parked.to_s }
|
102
|
+
|
103
|
+
its(:speed) { should be_zero }
|
104
|
+
it { should_not be_moving }
|
105
|
+
|
106
|
+
describe 'before transitions' do
|
107
|
+
it 'puts on a seatbelt' do
|
108
|
+
vehicle.should_receive :put_on_seatbelt
|
109
|
+
vehicle.ignite!
|
110
|
+
end
|
111
|
+
end
|
112
|
+
|
113
|
+
describe 'ignite' do
|
114
|
+
it 'should transition to idling' do
|
115
|
+
vehicle.ignite!
|
116
|
+
vehicle.should be_idling
|
117
|
+
end
|
118
|
+
end
|
119
|
+
end
|
120
|
+
|
121
|
+
context 'when transitioning to parked' do
|
122
|
+
before { vehicle.state = :idling.to_s }
|
123
|
+
it 'removes seatbelts' do
|
124
|
+
vehicle.should_receive(:seatbelt_on=).with(false)
|
125
|
+
vehicle.park!
|
126
|
+
end
|
127
|
+
end
|
128
|
+
|
129
|
+
context 'when idling' do
|
130
|
+
before { vehicle.state = :idling.to_s }
|
131
|
+
|
132
|
+
its(:speed) { should eq 10 }
|
133
|
+
it { should_not be_moving }
|
134
|
+
|
135
|
+
describe 'park' do
|
136
|
+
it 'should transition to a parked state' do
|
137
|
+
vehicle.park!
|
138
|
+
vehicle.should be_parked
|
139
|
+
end
|
140
|
+
end
|
141
|
+
|
142
|
+
describe 'shift up' do
|
143
|
+
it 'should shift into first gear' do
|
144
|
+
vehicle.shift_up!
|
145
|
+
vehicle.should be_first_gear
|
146
|
+
end
|
147
|
+
end
|
148
|
+
|
149
|
+
it_behaves_like 'crashable'
|
150
|
+
end
|
151
|
+
|
152
|
+
context 'when stalled' do
|
153
|
+
before { vehicle.state = :stalled.to_s }
|
154
|
+
|
155
|
+
it { should_not be_moving }
|
156
|
+
it_behaves_like 'speedless'
|
157
|
+
|
158
|
+
describe 'ignite' do
|
159
|
+
it 'remains stalled' do
|
160
|
+
vehicle.ignite!
|
161
|
+
vehicle.should be_stalled
|
162
|
+
end
|
163
|
+
end
|
164
|
+
|
165
|
+
describe 'repair' do
|
166
|
+
context 'the auto shop is busy' do
|
167
|
+
before { vehicle.stub(:auto_shop_busy).and_return(true) }
|
168
|
+
it 'remains stalled' do
|
169
|
+
vehicle.repair!
|
170
|
+
vehicle.should be_stalled
|
171
|
+
end
|
172
|
+
end
|
173
|
+
|
174
|
+
context 'the auto shop is not busy' do
|
175
|
+
before { vehicle.stub(:auto_shop_busy).and_return(false) }
|
176
|
+
it 'is parked' do
|
177
|
+
vehicle.repair!
|
178
|
+
vehicle.should be_parked
|
179
|
+
end
|
180
|
+
end
|
181
|
+
end
|
182
|
+
end
|
183
|
+
|
184
|
+
context 'when in first gear' do
|
185
|
+
before { vehicle.state = :first_gear.to_s }
|
186
|
+
|
187
|
+
its(:speed) { should eq 10 }
|
188
|
+
it { should be_moving }
|
189
|
+
|
190
|
+
describe 'park' do
|
191
|
+
it 'parks' do
|
192
|
+
vehicle.park!
|
193
|
+
vehicle.should be_parked
|
194
|
+
end
|
195
|
+
end
|
196
|
+
|
197
|
+
describe 'idle' do
|
198
|
+
it 'idles' do
|
199
|
+
vehicle.idle!
|
200
|
+
vehicle.should be_idling
|
201
|
+
end
|
202
|
+
end
|
203
|
+
|
204
|
+
describe 'shift up' do
|
205
|
+
it 'shift into second gear' do
|
206
|
+
vehicle.shift_up!
|
207
|
+
vehicle.should be_second_gear
|
208
|
+
end
|
209
|
+
end
|
210
|
+
|
211
|
+
it_behaves_like 'crashable'
|
212
|
+
end
|
213
|
+
|
214
|
+
context 'when in second gear' do
|
215
|
+
before { vehicle.state = :second_gear.to_s }
|
216
|
+
|
217
|
+
it { should be_moving }
|
218
|
+
it_behaves_like 'speedless'
|
219
|
+
|
220
|
+
describe 'shift up' do
|
221
|
+
it 'shifts into third gear' do
|
222
|
+
vehicle.shift_up!
|
223
|
+
vehicle.should be_third_gear
|
224
|
+
end
|
225
|
+
end
|
226
|
+
|
227
|
+
describe 'shift down' do
|
228
|
+
it 'shifts back into first gear' do
|
229
|
+
vehicle.shift_down!
|
230
|
+
vehicle.should be_first_gear
|
231
|
+
end
|
232
|
+
end
|
233
|
+
|
234
|
+
it_behaves_like 'crashable'
|
235
|
+
end
|
236
|
+
|
237
|
+
context 'when in third gear' do
|
238
|
+
before { vehicle.state = :third_gear.to_s }
|
239
|
+
|
240
|
+
it { should be_moving }
|
241
|
+
it_behaves_like 'speedless'
|
242
|
+
|
243
|
+
describe 'shift down' do
|
244
|
+
it 'shifts back into second gear' do
|
245
|
+
vehicle.shift_down!
|
246
|
+
vehicle.should be_second_gear
|
247
|
+
end
|
248
|
+
end
|
249
|
+
|
250
|
+
it_behaves_like 'crashable'
|
251
|
+
end
|
252
|
+
|
253
|
+
context 'on ignition' do
|
254
|
+
context 'when it fails' do
|
255
|
+
before { vehicle.stub(:ignite).and_return(false) }
|
256
|
+
pending 'logs the failure' do
|
257
|
+
vehicle.should_receive(:log_start_failure)
|
258
|
+
vehicle.ignite
|
259
|
+
end
|
260
|
+
end
|
261
|
+
end
|
262
|
+
|
263
|
+
context 'on a crash' do
|
264
|
+
before { vehicle.state = :third_gear.to_s }
|
265
|
+
it 'gets towed' do
|
266
|
+
vehicle.should_receive(:tow)
|
267
|
+
vehicle.crash!
|
268
|
+
end
|
269
|
+
end
|
270
|
+
|
271
|
+
context 'upon being repaired' do
|
272
|
+
before { vehicle.state = :stalled.to_s }
|
273
|
+
it 'gets fixed' do
|
274
|
+
vehicle.should_receive(:fix)
|
275
|
+
vehicle.repair!
|
276
|
+
end
|
277
|
+
end
|
278
|
+
end
|
279
|
+
|
280
|
+
describe 'alarm state machine' do
|
281
|
+
it { should have_state :active, on: :alarm_state, value: 1 }
|
282
|
+
it { should have_state :off, on: :alarm_state, value: 0 }
|
283
|
+
it { should reject_states :broken, :ringing, on: :alarm_state }
|
284
|
+
|
285
|
+
it { should handle_events :enable_alarm, :disable_alarm,
|
286
|
+
when: :active, state: :alarm_state }
|
287
|
+
it { should handle_events :enable_alarm, :disable_alarm,
|
288
|
+
when: :off, state: :alarm_state }
|
289
|
+
|
290
|
+
it 'has an initial state of activated' do
|
291
|
+
vehicle.alarm_active?.should be_true
|
292
|
+
end
|
293
|
+
|
294
|
+
context 'when active' do
|
295
|
+
describe 'enable' do
|
296
|
+
it 'becomes active' do
|
297
|
+
vehicle.enable_alarm!
|
298
|
+
vehicle.alarm_active?.should be_true
|
299
|
+
end
|
300
|
+
end
|
301
|
+
|
302
|
+
describe 'disable' do
|
303
|
+
it 'turns the alarm off' do
|
304
|
+
vehicle.disable_alarm!
|
305
|
+
vehicle.alarm_off?.should be_true
|
306
|
+
end
|
307
|
+
end
|
308
|
+
end
|
309
|
+
|
310
|
+
context 'when off' do
|
311
|
+
before { vehicle.alarm_state = 0 }
|
312
|
+
describe 'enable' do
|
313
|
+
it 'becomes active' do
|
314
|
+
vehicle.enable_alarm!
|
315
|
+
vehicle.alarm_active?.should be_true
|
316
|
+
end
|
317
|
+
end
|
318
|
+
|
319
|
+
describe 'disable' do
|
320
|
+
it 'turns the alarm off' do
|
321
|
+
vehicle.disable_alarm!
|
322
|
+
vehicle.alarm_off?.should be_true
|
323
|
+
end
|
324
|
+
end
|
325
|
+
end
|
326
|
+
end
|
327
|
+
end
|
@@ -0,0 +1,119 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
class Vehicle
|
4
|
+
attr_accessor :seatbelt_on, :time_used, :auto_shop_busy
|
5
|
+
|
6
|
+
state_machine :state, :initial => :parked do
|
7
|
+
before_transition :parked => any - :parked, :do => :put_on_seatbelt
|
8
|
+
|
9
|
+
after_transition :on => :crash, :do => :tow
|
10
|
+
after_transition :on => :repair, :do => :fix
|
11
|
+
after_transition any => :parked do |vehicle, transition|
|
12
|
+
vehicle.seatbelt_on = false
|
13
|
+
end
|
14
|
+
|
15
|
+
after_failure :on => :ignite, :do => :log_start_failure
|
16
|
+
|
17
|
+
around_transition do |vehicle, transition, block|
|
18
|
+
start = Time.now
|
19
|
+
block.call
|
20
|
+
vehicle.time_used += Time.now - start
|
21
|
+
end
|
22
|
+
|
23
|
+
event :park do
|
24
|
+
transition [:idling, :first_gear] => :parked
|
25
|
+
end
|
26
|
+
|
27
|
+
event :ignite do
|
28
|
+
transition :stalled => same, :parked => :idling
|
29
|
+
end
|
30
|
+
|
31
|
+
event :idle do
|
32
|
+
transition :first_gear => :idling
|
33
|
+
end
|
34
|
+
|
35
|
+
event :shift_up do
|
36
|
+
transition :idling => :first_gear, :first_gear => :second_gear, :second_gear => :third_gear
|
37
|
+
end
|
38
|
+
|
39
|
+
event :shift_down do
|
40
|
+
transition :third_gear => :second_gear, :second_gear => :first_gear
|
41
|
+
end
|
42
|
+
|
43
|
+
event :crash do
|
44
|
+
transition all - [:parked, :stalled] => :stalled, :if => lambda {|vehicle| !vehicle.passed_inspection?}
|
45
|
+
end
|
46
|
+
|
47
|
+
event :repair do
|
48
|
+
# The first transition that matches the state and passes its conditions
|
49
|
+
# will be used
|
50
|
+
transition :stalled => :parked, :unless => :auto_shop_busy
|
51
|
+
transition :stalled => same
|
52
|
+
end
|
53
|
+
|
54
|
+
state :parked do
|
55
|
+
def speed
|
56
|
+
0
|
57
|
+
end
|
58
|
+
end
|
59
|
+
|
60
|
+
state :idling, :first_gear do
|
61
|
+
def speed
|
62
|
+
10
|
63
|
+
end
|
64
|
+
end
|
65
|
+
|
66
|
+
state all - [:parked, :stalled, :idling] do
|
67
|
+
def moving?
|
68
|
+
true
|
69
|
+
end
|
70
|
+
end
|
71
|
+
|
72
|
+
state :parked, :stalled, :idling do
|
73
|
+
def moving?
|
74
|
+
false
|
75
|
+
end
|
76
|
+
end
|
77
|
+
end
|
78
|
+
|
79
|
+
state_machine :alarm_state, :initial => :active, :namespace => 'alarm' do
|
80
|
+
event :enable do
|
81
|
+
transition all => :active
|
82
|
+
end
|
83
|
+
|
84
|
+
event :disable do
|
85
|
+
transition all => :off
|
86
|
+
end
|
87
|
+
|
88
|
+
state :active, :value => 1
|
89
|
+
state :off, :value => 0
|
90
|
+
end
|
91
|
+
|
92
|
+
def initialize
|
93
|
+
@seatbelt_on = false
|
94
|
+
@time_used = 0
|
95
|
+
@auto_shop_busy = true
|
96
|
+
super() # NOTE: This *must* be called, otherwise states won't get initialized
|
97
|
+
end
|
98
|
+
|
99
|
+
def put_on_seatbelt
|
100
|
+
@seatbelt_on = true
|
101
|
+
end
|
102
|
+
|
103
|
+
def passed_inspection?
|
104
|
+
false
|
105
|
+
end
|
106
|
+
|
107
|
+
def tow
|
108
|
+
# tow the vehicle
|
109
|
+
end
|
110
|
+
|
111
|
+
def fix
|
112
|
+
# get the vehicle fixed by a mechanic
|
113
|
+
end
|
114
|
+
|
115
|
+
def log_start_failure
|
116
|
+
# log a failed attempt to start the vehicle
|
117
|
+
end
|
118
|
+
end
|
119
|
+
|
@@ -0,0 +1,109 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe StateMachineRspec::Matchers::HandleEventMatcher do
|
4
|
+
describe '#matches?' do
|
5
|
+
context 'when :when state is specified' do
|
6
|
+
context 'but the state doesn\'t exist' do
|
7
|
+
before do
|
8
|
+
matcher_class = Class.new do
|
9
|
+
state_machine :state, initial: :mathy
|
10
|
+
end
|
11
|
+
@matcher_subject = matcher_class.new
|
12
|
+
@matcher = described_class.new([when: :artsy])
|
13
|
+
end
|
14
|
+
|
15
|
+
it 'raises' do
|
16
|
+
expect { @matcher.matches? @matcher_subject }.
|
17
|
+
to raise_error StateMachineIntrospectorError
|
18
|
+
end
|
19
|
+
end
|
20
|
+
|
21
|
+
context 'and that state exists' do
|
22
|
+
before do
|
23
|
+
matcher_class = Class.new do
|
24
|
+
state_machine :state, initial: :mathy do
|
25
|
+
state :artsy
|
26
|
+
end
|
27
|
+
end
|
28
|
+
@matcher_subject = matcher_class.new
|
29
|
+
@matcher = described_class.new([when: :artsy])
|
30
|
+
end
|
31
|
+
|
32
|
+
it 'sets the state' do
|
33
|
+
@matcher.matches? @matcher_subject
|
34
|
+
@matcher_subject.state.should eq 'artsy'
|
35
|
+
end
|
36
|
+
end
|
37
|
+
end
|
38
|
+
|
39
|
+
context 'when subject can perform events' do
|
40
|
+
before do
|
41
|
+
matcher_class = Class.new do
|
42
|
+
state_machine :state, initial: :mathy do
|
43
|
+
event(:mathematize) { transition any => same }
|
44
|
+
end
|
45
|
+
end
|
46
|
+
@matcher_subject = matcher_class.new
|
47
|
+
@matcher = described_class.new([:mathematize])
|
48
|
+
end
|
49
|
+
|
50
|
+
it 'does not set a failure message' do
|
51
|
+
@matcher.matches? @matcher_subject
|
52
|
+
@matcher.failure_message.should be_nil
|
53
|
+
end
|
54
|
+
it 'returns true' do
|
55
|
+
@matcher.matches?(@matcher_subject).should be_true
|
56
|
+
end
|
57
|
+
end
|
58
|
+
|
59
|
+
context 'when subject cannot perform events' do
|
60
|
+
before do
|
61
|
+
matcher_class = Class.new do
|
62
|
+
state_machine :state, initial: :mathy do
|
63
|
+
state :polynomial
|
64
|
+
|
65
|
+
event(:mathematize) { transition any => same }
|
66
|
+
event(:algebraify) { transition :polynomial => same }
|
67
|
+
event(:trigonomalize) { transition :trigonomalize => same }
|
68
|
+
end
|
69
|
+
end
|
70
|
+
@matcher_subject = matcher_class.new
|
71
|
+
end
|
72
|
+
|
73
|
+
context 'because it cannot perform the transition' do
|
74
|
+
before do
|
75
|
+
@matcher = described_class.new([:mathematize, :algebraify, :trigonomalize])
|
76
|
+
end
|
77
|
+
|
78
|
+
it 'sets a failure message' do
|
79
|
+
@matcher.matches? @matcher_subject
|
80
|
+
@matcher.failure_message.
|
81
|
+
should eq 'Expected to be able to handle events: algebraify, trigonomalize ' +
|
82
|
+
'in state: mathy'
|
83
|
+
end
|
84
|
+
it 'returns false' do
|
85
|
+
@matcher.matches?(@matcher_subject).should be_false
|
86
|
+
end
|
87
|
+
end
|
88
|
+
|
89
|
+
context 'because no such events exist' do
|
90
|
+
before do
|
91
|
+
@matcher = described_class.new([:polynomialize, :eulerasterize])
|
92
|
+
end
|
93
|
+
|
94
|
+
it 'does not raise' do
|
95
|
+
expect { @matcher.matches?(@matcher_subject) }.to_not raise_error
|
96
|
+
end
|
97
|
+
it 'sets a failure message' do
|
98
|
+
@matcher.matches? @matcher_subject
|
99
|
+
@matcher.failure_message.
|
100
|
+
should eq 'state_machine: state does not ' +
|
101
|
+
'define events: polynomialize, eulerasterize'
|
102
|
+
end
|
103
|
+
it 'returns false' do
|
104
|
+
@matcher.matches?(@matcher_subject).should be_false
|
105
|
+
end
|
106
|
+
end
|
107
|
+
end
|
108
|
+
end
|
109
|
+
end
|
@@ -0,0 +1,109 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe StateMachineRspec::Matchers::RejectEventMatcher do
|
4
|
+
describe '#matches?' do
|
5
|
+
context 'when :when state is specified' do
|
6
|
+
context 'but that state doesn\'t exist' do
|
7
|
+
before do
|
8
|
+
matcher_class = Class.new do
|
9
|
+
state_machine :state, initial: :sleazy
|
10
|
+
end
|
11
|
+
@matcher_subject = matcher_class.new
|
12
|
+
@matcher = described_class.new([when: :sneezy])
|
13
|
+
end
|
14
|
+
|
15
|
+
it 'raises' do
|
16
|
+
expect { @matcher.matches? @matcher_subject }.
|
17
|
+
to raise_error StateMachineIntrospectorError
|
18
|
+
end
|
19
|
+
end
|
20
|
+
|
21
|
+
context 'and that state exists' do
|
22
|
+
before do
|
23
|
+
matcher_class = Class.new do
|
24
|
+
state_machine :state, initial: :sleazy do
|
25
|
+
state :sneezy
|
26
|
+
end
|
27
|
+
end
|
28
|
+
@matcher_subject = matcher_class.new
|
29
|
+
@matcher = described_class.new([when: :sneezy])
|
30
|
+
end
|
31
|
+
|
32
|
+
it 'sets the state' do
|
33
|
+
@matcher.matches? @matcher_subject
|
34
|
+
@matcher_subject.state.should eq 'sneezy'
|
35
|
+
end
|
36
|
+
end
|
37
|
+
end
|
38
|
+
|
39
|
+
context 'when an expectation is made on an event that is undefined' do
|
40
|
+
before do
|
41
|
+
matcher_class = Class.new do
|
42
|
+
state_machine :state, initial: :snarky do
|
43
|
+
event(:primmadonnalize) { transition any => same }
|
44
|
+
end
|
45
|
+
end
|
46
|
+
@matcher_subject = matcher_class.new
|
47
|
+
@matcher = described_class.new([:primmadonnalize, :martinilunchitize])
|
48
|
+
end
|
49
|
+
|
50
|
+
it 'does not raise' do
|
51
|
+
expect { @matcher.matches?(@matcher_subject) }.to_not raise_error
|
52
|
+
end
|
53
|
+
it 'sets a failure message' do
|
54
|
+
@matcher.matches? @matcher_subject
|
55
|
+
@matcher.failure_message.
|
56
|
+
should eq 'state_machine: state does not ' +
|
57
|
+
'define events: martinilunchitize'
|
58
|
+
end
|
59
|
+
it 'returns false' do
|
60
|
+
@matcher.matches?(@matcher_subject).should be_false
|
61
|
+
end
|
62
|
+
end
|
63
|
+
|
64
|
+
context 'when subject cannot perform any of the specified events' do
|
65
|
+
before do
|
66
|
+
matcher_class = Class.new do
|
67
|
+
state_machine :state, initial: :snarky do
|
68
|
+
state :haughty
|
69
|
+
event(:primmadonnalize) { transition :haughty => same }
|
70
|
+
end
|
71
|
+
end
|
72
|
+
@matcher_subject = matcher_class.new
|
73
|
+
@matcher = described_class.new([:primmadonnalize])
|
74
|
+
end
|
75
|
+
|
76
|
+
it 'does not set a failure message' do
|
77
|
+
@matcher.matches? @matcher_subject
|
78
|
+
@matcher.failure_message.should be_nil
|
79
|
+
end
|
80
|
+
it 'returns true' do
|
81
|
+
@matcher.matches?(@matcher_subject).should be_true
|
82
|
+
end
|
83
|
+
end
|
84
|
+
|
85
|
+
context 'when subject can perform any one of the specified events' do
|
86
|
+
before do
|
87
|
+
matcher_class = Class.new do
|
88
|
+
state_machine :state, initial: :snarky do
|
89
|
+
state :haughty
|
90
|
+
event(:primmadonnalize) { transition :haughty => same }
|
91
|
+
event(:defer_to_management) { transition any => same }
|
92
|
+
end
|
93
|
+
end
|
94
|
+
@matcher_subject = matcher_class.new
|
95
|
+
@matcher = described_class.new([:primmadonnalize, :defer_to_management])
|
96
|
+
end
|
97
|
+
|
98
|
+
it 'sets a failure message' do
|
99
|
+
@matcher.matches? @matcher_subject
|
100
|
+
@matcher.failure_message.
|
101
|
+
should eq 'Did not expect to be able to handle events: defer_to_management ' +
|
102
|
+
'in state: snarky'
|
103
|
+
end
|
104
|
+
it 'returns false' do
|
105
|
+
@matcher.matches?(@matcher_subject).should be_false
|
106
|
+
end
|
107
|
+
end
|
108
|
+
end
|
109
|
+
end
|