state_machine 0.6.3 → 0.7.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 (56) hide show
  1. data/CHANGELOG.rdoc +31 -1
  2. data/README.rdoc +33 -21
  3. data/Rakefile +2 -2
  4. data/examples/merb-rest/controller.rb +51 -0
  5. data/examples/merb-rest/model.rb +28 -0
  6. data/examples/merb-rest/view_edit.html.erb +24 -0
  7. data/examples/merb-rest/view_index.html.erb +23 -0
  8. data/examples/merb-rest/view_new.html.erb +13 -0
  9. data/examples/merb-rest/view_show.html.erb +17 -0
  10. data/examples/rails-rest/controller.rb +43 -0
  11. data/examples/rails-rest/migration.rb +11 -0
  12. data/examples/rails-rest/model.rb +23 -0
  13. data/examples/rails-rest/view_edit.html.erb +25 -0
  14. data/examples/rails-rest/view_index.html.erb +23 -0
  15. data/examples/rails-rest/view_new.html.erb +14 -0
  16. data/examples/rails-rest/view_show.html.erb +17 -0
  17. data/lib/state_machine/assertions.rb +2 -2
  18. data/lib/state_machine/callback.rb +14 -8
  19. data/lib/state_machine/condition_proxy.rb +3 -3
  20. data/lib/state_machine/event.rb +19 -21
  21. data/lib/state_machine/event_collection.rb +114 -0
  22. data/lib/state_machine/extensions.rb +127 -11
  23. data/lib/state_machine/guard.rb +1 -1
  24. data/lib/state_machine/integrations/active_record/locale.rb +2 -1
  25. data/lib/state_machine/integrations/active_record.rb +117 -39
  26. data/lib/state_machine/integrations/data_mapper/observer.rb +20 -64
  27. data/lib/state_machine/integrations/data_mapper.rb +71 -26
  28. data/lib/state_machine/integrations/sequel.rb +69 -21
  29. data/lib/state_machine/machine.rb +267 -139
  30. data/lib/state_machine/machine_collection.rb +145 -0
  31. data/lib/state_machine/matcher.rb +2 -2
  32. data/lib/state_machine/node_collection.rb +9 -4
  33. data/lib/state_machine/state.rb +22 -32
  34. data/lib/state_machine/state_collection.rb +66 -17
  35. data/lib/state_machine/transition.rb +259 -28
  36. data/lib/state_machine.rb +121 -56
  37. data/tasks/state_machine.rake +1 -0
  38. data/tasks/state_machine.rb +26 -0
  39. data/test/active_record.log +116877 -0
  40. data/test/functional/state_machine_test.rb +118 -12
  41. data/test/sequel.log +28542 -0
  42. data/test/unit/callback_test.rb +46 -1
  43. data/test/unit/condition_proxy_test.rb +55 -28
  44. data/test/unit/event_collection_test.rb +228 -0
  45. data/test/unit/event_test.rb +51 -46
  46. data/test/unit/integrations/active_record_test.rb +128 -70
  47. data/test/unit/integrations/data_mapper_test.rb +150 -58
  48. data/test/unit/integrations/sequel_test.rb +63 -6
  49. data/test/unit/invalid_event_test.rb +7 -0
  50. data/test/unit/machine_collection_test.rb +678 -0
  51. data/test/unit/machine_test.rb +198 -91
  52. data/test/unit/node_collection_test.rb +33 -30
  53. data/test/unit/state_collection_test.rb +112 -5
  54. data/test/unit/state_test.rb +23 -3
  55. data/test/unit/transition_test.rb +750 -89
  56. metadata +28 -3
@@ -0,0 +1,678 @@
1
+ require File.expand_path(File.dirname(__FILE__) + '/../test_helper')
2
+
3
+ class MachineCollectionByDefaultTest < Test::Unit::TestCase
4
+ def setup
5
+ @machines = StateMachine::MachineCollection.new
6
+ end
7
+
8
+ def test_should_not_have_any_machines
9
+ assert @machines.empty?
10
+ end
11
+ end
12
+
13
+ class MachineCollectionStateInitializationTest < Test::Unit::TestCase
14
+ def setup
15
+ @machines = StateMachine::MachineCollection.new
16
+
17
+ @klass = Class.new do
18
+ def initialize(attributes = {})
19
+ attributes.each do |attribute, value|
20
+ self.send("#{attribute}=", value)
21
+ end
22
+
23
+ super()
24
+ end
25
+ end
26
+
27
+ @machines[:state] = StateMachine::Machine.new(@klass, :state, :initial => :parked)
28
+ @machines[:alarm_state] = StateMachine::Machine.new(@klass, :alarm_state, :initial => :active)
29
+ end
30
+
31
+ def test_should_set_states_if_nil
32
+ object = @klass.new
33
+ assert_equal 'parked', object.state
34
+ assert_equal 'active', object.alarm_state
35
+ end
36
+
37
+ def test_should_set_states_if_empty
38
+ object = @klass.new(:state => '', :alarm_state => '')
39
+ assert_equal 'parked', object.state
40
+ assert_equal 'active', object.alarm_state
41
+ end
42
+
43
+ def test_should_not_set_states_if_not_empty
44
+ object = @klass.new(:state => 'idling', :alarm_state => 'off')
45
+ assert_equal 'idling', object.state
46
+ assert_equal 'off', object.alarm_state
47
+ end
48
+ end
49
+
50
+ class MachineCollectionFireExplicitTest < Test::Unit::TestCase
51
+ def setup
52
+ @machines = StateMachine::MachineCollection.new
53
+
54
+ @klass = Class.new do
55
+ attr_reader :saved
56
+
57
+ def save
58
+ @saved = true
59
+ end
60
+ end
61
+
62
+ # First machine
63
+ @machines[:state] = @state = StateMachine::Machine.new(@klass, :state, :initial => :parked, :action => :save)
64
+ @state.event :ignite do
65
+ transition :parked => :idling
66
+ end
67
+ @state.event :park do
68
+ transition :idling => :parked
69
+ end
70
+
71
+ # Second machine
72
+ @machines[:alarm_state] = @alarm_state = StateMachine::Machine.new(@klass, :alarm_state, :initial => :active, :action => :save, :namespace => 'alarm')
73
+ @alarm_state.event :enable do
74
+ transition :off => :active
75
+ end
76
+ @alarm_state.event :disable do
77
+ transition :active => :off
78
+ end
79
+
80
+ @object = @klass.new
81
+ end
82
+
83
+ def test_should_raise_exception_if_invalid_event_specified
84
+ exception = assert_raise(StateMachine::InvalidEvent) { @machines.fire_events(@object, :invalid) }
85
+ assert_equal ':invalid is an unknown state machine event', exception.message
86
+
87
+ exception = assert_raise(StateMachine::InvalidEvent) { @machines.fire_events(@object, :ignite, :invalid) }
88
+ assert_equal ':invalid is an unknown state machine event', exception.message
89
+ end
90
+
91
+ def test_should_fail_if_any_event_cannot_transition
92
+ assert !@machines.fire_events(@object, :park, :disable_alarm)
93
+ assert_equal 'parked', @object.state
94
+ assert_equal 'active', @object.alarm_state
95
+ assert !@object.saved
96
+
97
+ assert !@machines.fire_events(@object, :ignite, :enable_alarm)
98
+ assert_equal 'parked', @object.state
99
+ assert_equal 'active', @object.alarm_state
100
+ assert !@object.saved
101
+ end
102
+
103
+ def test_should_be_successful_if_all_events_transition
104
+ assert @machines.fire_events(@object, :ignite, :disable_alarm)
105
+ assert_equal 'idling', @object.state
106
+ assert_equal 'off', @object.alarm_state
107
+ assert @object.saved
108
+ end
109
+
110
+ def test_should_not_save_if_skipping_action
111
+ assert @machines.fire_events(@object, :ignite, :disable_alarm, false)
112
+ assert_equal 'idling', @object.state
113
+ assert_equal 'off', @object.alarm_state
114
+ assert !@object.saved
115
+ end
116
+ end
117
+
118
+ class MachineCollectionFireExplicitWithTransactionsTest < Test::Unit::TestCase
119
+ def setup
120
+ @machines = StateMachine::MachineCollection.new
121
+
122
+ @klass = Class.new do
123
+ attr_accessor :allow_save
124
+
125
+ def save
126
+ @allow_save
127
+ end
128
+ end
129
+
130
+ StateMachine::Integrations.const_set('Custom', Module.new do
131
+ attr_reader :rolled_back
132
+
133
+ def transaction(object)
134
+ @rolled_back = yield
135
+ end
136
+ end)
137
+
138
+ # First machine
139
+ @machines[:state] = @state = StateMachine::Machine.new(@klass, :state, :initial => :parked, :action => :save, :integration => :custom)
140
+ @state.event :ignite do
141
+ transition :parked => :idling
142
+ end
143
+
144
+ # Second machine
145
+ @machines[:alarm_state] = @alarm_state = StateMachine::Machine.new(@klass, :alarm_state, :initial => :active, :action => :save, :namespace => 'alarm', :integration => :custom)
146
+ @alarm_state.event :disable do
147
+ transition :active => :off
148
+ end
149
+
150
+ @object = @klass.new
151
+ end
152
+
153
+ def test_should_not_rollback_if_successful
154
+ @object.allow_save = true
155
+
156
+ assert @machines.fire_events(@object, :ignite, :disable_alarm)
157
+ assert_equal true, @state.rolled_back
158
+ assert_nil @alarm_state.rolled_back
159
+ assert_equal 'idling', @object.state
160
+ assert_equal 'off', @object.alarm_state
161
+ end
162
+
163
+ def test_should_rollback_if_not_successful
164
+ @object.allow_save = false
165
+
166
+ assert !@machines.fire_events(@object, :ignite, :disable_alarm)
167
+ assert_equal false, @state.rolled_back
168
+ assert_nil @alarm_state.rolled_back
169
+ assert_equal 'parked', @object.state
170
+ assert_equal 'active', @object.alarm_state
171
+ end
172
+
173
+ def teardown
174
+ StateMachine::Integrations.send(:remove_const, 'Custom')
175
+ end
176
+ end
177
+
178
+ class MachineCollectionFireExplicitWithValidationsTest < Test::Unit::TestCase
179
+ def setup
180
+ StateMachine::Integrations.const_set('Custom', Module.new do
181
+ def invalidate(object, attribute, message, values = [])
182
+ (object.errors ||= []) << generate_message(message, values)
183
+ end
184
+
185
+ def reset(object)
186
+ object.errors = []
187
+ end
188
+ end)
189
+
190
+ @klass = Class.new do
191
+ attr_accessor :errors
192
+
193
+ def initialize
194
+ @errors = []
195
+ super
196
+ end
197
+ end
198
+
199
+ @machines = StateMachine::MachineCollection.new
200
+ @machines[:state] = @state = StateMachine::Machine.new(@klass, :state, :initial => :parked, :integration => :custom)
201
+ @state.event :ignite do
202
+ transition :parked => :idling
203
+ end
204
+
205
+ @machines[:alarm_state] = @alarm_state = StateMachine::Machine.new(@klass, :alarm_state, :initial => :active, :namespace => 'alarm', :integration => :custom)
206
+ @alarm_state.event :disable do
207
+ transition :active => :off
208
+ end
209
+
210
+ @object = @klass.new
211
+ end
212
+
213
+ def test_should_not_invalidate_if_transitions_exist
214
+ assert @machines.fire_events(@object, :ignite, :disable_alarm)
215
+ assert_equal [], @object.errors
216
+ end
217
+
218
+ def test_should_invalidate_if_no_transitions_exist
219
+ @object.state = 'idling'
220
+ @object.alarm_state = 'off'
221
+
222
+ assert !@machines.fire_events(@object, :ignite, :disable_alarm)
223
+ assert_equal ['cannot transition via "ignite"', 'cannot transition via "disable_alarm"'], @object.errors
224
+ end
225
+
226
+ def teardown
227
+ StateMachine::Integrations.send(:remove_const, 'Custom')
228
+ end
229
+ end
230
+
231
+ class MachineCollectionFireImplicitTest < Test::Unit::TestCase
232
+ def setup
233
+ @klass = Class.new
234
+
235
+ @machines = StateMachine::MachineCollection.new
236
+ @machines[:state] = @machine = StateMachine::Machine.new(@klass, :state, :initial => :parked, :action => :save)
237
+ @machine.event :ignite do
238
+ transition :parked => :idling
239
+ end
240
+
241
+ @saved = false
242
+ @object = @klass.new
243
+ end
244
+
245
+ def default_test
246
+ end
247
+ end
248
+
249
+ class MachineCollectionFireImplicitWithoutEventTest < MachineCollectionFireImplicitTest
250
+ def setup
251
+ super
252
+
253
+ @object.state_event = nil
254
+ @result = @machines.fire_attribute_events(@object, :save) { @saved = true }
255
+ end
256
+
257
+ def test_should_be_successful
258
+ assert_equal true, @result
259
+ end
260
+
261
+ def test_should_run_action
262
+ assert @saved
263
+ end
264
+
265
+ def test_should_not_transition_state
266
+ assert_equal 'parked', @object.state
267
+ end
268
+
269
+ def test_should_not_change_event_attribute
270
+ assert_nil @object.state_event
271
+ end
272
+ end
273
+
274
+ class MachineCollectionFireImplicitWithBlankEventTest < MachineCollectionFireImplicitTest
275
+ def setup
276
+ super
277
+
278
+ @object.state_event = ''
279
+ @result = @machines.fire_attribute_events(@object, :save) { @saved = true }
280
+ end
281
+
282
+ def test_should_be_successful
283
+ assert_equal true, @result
284
+ end
285
+
286
+ def test_should_run_action
287
+ assert @saved
288
+ end
289
+
290
+ def test_should_not_transition_state
291
+ assert_equal 'parked', @object.state
292
+ end
293
+
294
+ def test_should_not_change_event_attribute
295
+ assert_nil @object.state_event
296
+ end
297
+ end
298
+
299
+ class MachineCollectionFireImplicitWithInvalidEventTest < MachineCollectionFireImplicitTest
300
+ def setup
301
+ super
302
+
303
+ @object.state_event = 'invalid'
304
+ @result = @machines.fire_attribute_events(@object, :save) { @saved = true }
305
+ end
306
+
307
+ def test_should_not_be_successful
308
+ assert_equal false, @result
309
+ end
310
+
311
+ def test_should_not_run_action
312
+ assert !@saved
313
+ end
314
+
315
+ def test_should_not_transition_state
316
+ assert_equal 'parked', @object.state
317
+ end
318
+
319
+ def test_should_not_reset_event_attribute
320
+ assert_equal :invalid, @object.state_event
321
+ end
322
+
323
+ def test_should_not_have_event_transition
324
+ assert_nil @object.state_event_transition
325
+ end
326
+ end
327
+
328
+ class MachineCollectionFireImplicitWithoutTransitionTest < MachineCollectionFireImplicitTest
329
+ def setup
330
+ super
331
+
332
+ @object.state = 'idling'
333
+ @object.state_event = 'ignite'
334
+ @result = @machines.fire_attribute_events(@object, :save) { @saved = true }
335
+ end
336
+
337
+ def test_should_not_be_successful
338
+ assert_equal false, @result
339
+ end
340
+
341
+ def test_should_not_run_action
342
+ assert !@saved
343
+ end
344
+
345
+ def test_should_not_transition_state
346
+ assert_equal 'idling', @object.state
347
+ end
348
+
349
+ def test_should_not_reset_event_attribute
350
+ assert_equal :ignite, @object.state_event
351
+ end
352
+
353
+ def test_should_not_have_event_transition
354
+ assert_nil @object.state_event_transition
355
+ end
356
+ end
357
+
358
+ class MachineCollectionFireImplicitWithTransitionTest < MachineCollectionFireImplicitTest
359
+ def setup
360
+ super
361
+
362
+ @state_event = nil
363
+ @state_event_transition = nil
364
+
365
+ @object.state_event = 'ignite'
366
+ @result = @machines.fire_attribute_events(@object, :save) do
367
+ @state_event = @object.state_event
368
+ @state_event_transition = @object.state_event_transition
369
+ @saved = true
370
+ end
371
+ end
372
+
373
+ def test_should_be_successful
374
+ assert_equal true, @result
375
+ end
376
+
377
+ def test_should_run_action
378
+ assert @saved
379
+ end
380
+
381
+ def test_should_not_have_event_while_running_action
382
+ assert_nil @state_event
383
+ end
384
+
385
+ def test_should_not_have_event_transition_while_running_action
386
+ assert_nil @state_event_transition
387
+ end
388
+
389
+ def test_should_transition_state
390
+ assert_equal 'idling', @object.state
391
+ end
392
+
393
+ def test_should_reset_event_attribute
394
+ assert_nil @object.state_event
395
+ end
396
+
397
+ def test_should_reset_event_transition
398
+ assert_nil @object.state_event_transition
399
+ end
400
+ end
401
+
402
+ class MachineCollectionFireImplicitWithActionFailureTest < MachineCollectionFireImplicitTest
403
+ def setup
404
+ super
405
+
406
+ @object.state_event = 'ignite'
407
+ @result = @machines.fire_attribute_events(@object, :save) { false }
408
+ end
409
+
410
+ def test_should_not_be_successful
411
+ assert_equal false, @result
412
+ end
413
+
414
+ def test_should_not_transition_state
415
+ assert_equal 'parked', @object.state
416
+ end
417
+
418
+ def test_should_not_reset_event_attribute
419
+ assert_equal :ignite, @object.state_event
420
+ end
421
+
422
+ def test_should_not_have_event_transition
423
+ assert_nil @object.state_event_transition
424
+ end
425
+ end
426
+
427
+ class MachineCollectionFireImplicitWithActionErrorTest < MachineCollectionFireImplicitTest
428
+ def setup
429
+ super
430
+
431
+ @object.state_event = 'ignite'
432
+ assert_raise(ArgumentError) { @machines.fire_attribute_events(@object, :save) { raise ArgumentError } }
433
+ end
434
+
435
+ def test_should_not_transition_state
436
+ assert_equal 'parked', @object.state
437
+ end
438
+
439
+ def test_should_not_reset_event_attribute
440
+ assert_equal :ignite, @object.state_event
441
+ end
442
+
443
+ def test_should_not_have_event_transition
444
+ assert_nil @object.state_event_transition
445
+ end
446
+ end
447
+
448
+ class MachineCollectionFireImplicitPartialTest < MachineCollectionFireImplicitTest
449
+ def setup
450
+ super
451
+
452
+ @ran_before_callback = false
453
+ @ran_after_callback = false
454
+ @machine.before_transition { @ran_before_callback = true }
455
+ @machine.after_transition { @ran_after_callback = true }
456
+
457
+ @state_event = nil
458
+ @state_event_transition = nil
459
+
460
+ @object.state_event = 'ignite'
461
+ @result = @machines.fire_attribute_events(@object, :save, false) do
462
+ @state_event = @object.state_event
463
+ @state_event_transition = @object.state_event_transition
464
+ true
465
+ end
466
+ end
467
+
468
+ def test_should_run_before_callbacks
469
+ assert @ran_before_callback
470
+ end
471
+
472
+ def test_should_not_run_after_callbacks
473
+ assert !@ran_after_callback
474
+ end
475
+
476
+ def test_should_be_successful
477
+ assert @result
478
+ end
479
+
480
+ def test_should_not_have_event_while_running_action
481
+ assert_nil @state_event
482
+ end
483
+
484
+ def test_should_not_have_event_transition_while_running_action
485
+ assert_nil @state_event_transition
486
+ end
487
+
488
+ def test_should_transition_state
489
+ assert_equal 'idling', @object.state
490
+ end
491
+
492
+ def test_should_not_reset_event_attribute
493
+ assert_equal :ignite, @object.state_event
494
+ end
495
+
496
+ def test_should_have_event_transition
497
+ assert_not_nil @object.state_event_transition
498
+ end
499
+
500
+ def test_should_reset_event_attributes_after_next_fire_on_success
501
+ assert @machines.fire_attribute_events(@object, :save) { true }
502
+ assert_equal 'idling', @object.state
503
+ assert_nil @object.state_event
504
+ assert_nil @object.state_event_transition
505
+ end
506
+
507
+ def test_should_rollback_all_attributes_after_next_fire_on_failure
508
+ assert !@machines.fire_attribute_events(@object, :save) { false }
509
+ assert_equal 'parked', @object.state
510
+ assert_equal :ignite, @object.state_event
511
+ assert_nil @object.state_event_transition
512
+ end
513
+
514
+ def test_should_rollback_all_attributes_after_next_fire_on_error
515
+ assert_raise(ArgumentError) { @machines.fire_attribute_events(@object, :save) { raise ArgumentError } }
516
+ assert_equal 'parked', @object.state
517
+ assert_equal :ignite, @object.state_event
518
+ assert_nil @object.state_event_transition
519
+ end
520
+ end
521
+
522
+ class MachineCollectionFireImplicitNestedPartialTest < MachineCollectionFireImplicitTest
523
+ def setup
524
+ super
525
+
526
+ @partial_result = nil
527
+
528
+ @object.state_event = 'ignite'
529
+ @result = @machines.fire_attribute_events(@object, :save) do
530
+ @partial_result = @machines.fire_attribute_events(@object, :save, false) { true }
531
+ true
532
+ end
533
+ end
534
+
535
+ def test_should_be_successful
536
+ assert @result
537
+ end
538
+
539
+ def test_should_have_successful_partial_fire
540
+ assert @partial_result
541
+ end
542
+
543
+ def test_should_transition_state
544
+ assert_equal 'idling', @object.state
545
+ end
546
+
547
+ def test_should_reset_event_attribute
548
+ assert_nil @object.state_event
549
+ end
550
+
551
+ def test_should_not_have_event_transition
552
+ assert_nil @object.state_event_transition
553
+ end
554
+ end
555
+
556
+ class MachineCollectionFireImplicitWithDifferentActionsTest < MachineCollectionFireImplicitTest
557
+ def setup
558
+ super
559
+
560
+ @machines[:alarm_state] = @alarm_machine = StateMachine::Machine.new(@klass, :alarm_state, :initial => :active, :action => :save_alarm)
561
+ @alarm_machine.event :disable do
562
+ transition :active => :off
563
+ end
564
+
565
+ @saved = false
566
+ @object = @klass.new
567
+ @object.state_event = 'ignite'
568
+ @object.alarm_state_event = 'disable'
569
+
570
+ @machines.fire_attribute_events(@object, :save) { true }
571
+ end
572
+
573
+ def test_should_transition_states_for_action
574
+ assert_equal 'idling', @object.state
575
+ end
576
+
577
+ def test_should_reset_event_attributes_for_action
578
+ assert_nil @object.state_event
579
+ end
580
+
581
+ def test_should_not_transition_states_for_other_actions
582
+ assert_equal 'active', @object.alarm_state
583
+ end
584
+
585
+ def test_should_not_reset_event_attributes_for_other_actions
586
+ assert_equal :disable, @object.alarm_state_event
587
+ end
588
+ end
589
+
590
+ class MachineCollectionFireImplicitWithSameActionsTest < MachineCollectionFireImplicitTest
591
+ def setup
592
+ super
593
+
594
+ @machines[:alarm_state] = @alarm_machine = StateMachine::Machine.new(@klass, :alarm_state, :initial => :active, :action => :save)
595
+ @alarm_machine.event :disable do
596
+ transition :active => :off
597
+ end
598
+
599
+ @saved = false
600
+ @object = @klass.new
601
+ @object.state_event = 'ignite'
602
+ @object.alarm_state_event = 'disable'
603
+
604
+ @machines.fire_attribute_events(@object, :save) { true }
605
+ end
606
+
607
+ def test_should_transition_all_states_for_action
608
+ assert_equal 'idling', @object.state
609
+ assert_equal 'off', @object.alarm_state
610
+ end
611
+
612
+ def test_should_reset_all_event_attributes_for_action
613
+ assert_nil @object.state_event
614
+ assert_nil @object.alarm_state_event
615
+ end
616
+
617
+ def test_should_reset_all_event_transitions_for_action
618
+ assert_nil @object.state_event_transition
619
+ assert_nil @object.alarm_state_event_transition
620
+ end
621
+ end
622
+
623
+ class MachineCollectionFireImplicitWithValidationsTest < Test::Unit::TestCase
624
+ def setup
625
+ StateMachine::Integrations.const_set('Custom', Module.new do
626
+ def invalidate(object, attribute, message, values = [])
627
+ (object.errors ||= []) << generate_message(message, values)
628
+ end
629
+
630
+ def reset(object)
631
+ object.errors = []
632
+ end
633
+ end)
634
+
635
+ @klass = Class.new do
636
+ attr_accessor :errors
637
+
638
+ def initialize
639
+ @errors = []
640
+ super
641
+ end
642
+ end
643
+
644
+ @machines = StateMachine::MachineCollection.new
645
+ @machines[:state] = @machine = StateMachine::Machine.new(@klass, :state, :initial => :parked, :action => :save, :integration => :custom)
646
+ @machine.event :ignite do
647
+ transition :parked => :idling
648
+ end
649
+
650
+ @object = @klass.new
651
+ end
652
+
653
+ def test_should_invalidate_if_event_is_invalid
654
+ @object.state_event = 'invalid'
655
+ @machines.fire_attribute_events(@object, :save) { true }
656
+
657
+ assert !@object.errors.empty?
658
+ end
659
+
660
+ def test_should_invalidate_if_no_transition_exists
661
+ @object.state = 'idling'
662
+ @object.state_event = 'ignite'
663
+ @machines.fire_attribute_events(@object, :save) { true }
664
+
665
+ assert !@object.errors.empty?
666
+ end
667
+
668
+ def test_should_not_invalidate_if_transition_exists
669
+ @object.state_event = 'ignite'
670
+ @machines.fire_attribute_events(@object, :save) { true }
671
+
672
+ assert @object.errors.empty?
673
+ end
674
+
675
+ def teardown
676
+ StateMachine::Integrations.send(:remove_const, 'Custom')
677
+ end
678
+ end