PageRenderTime 67ms CodeModel.GetById 30ms RepoModel.GetById 1ms app.codeStats 0ms

/test/unit/transition_test.rb

https://github.com/amatsuda/state_machine
Ruby | 1384 lines | 1090 code | 294 blank | 0 comment | 0 complexity | 5c4731be81bfd3bfe881d406e34fb5ff MD5 | raw file
  1. require File.expand_path(File.dirname(__FILE__) + '/../test_helper')
  2. class TransitionTest < Test::Unit::TestCase
  3. def setup
  4. @klass = Class.new
  5. @machine = StateMachine::Machine.new(@klass)
  6. @machine.state :parked, :idling
  7. @machine.event :ignite
  8. @object = @klass.new
  9. @object.state = 'parked'
  10. @transition = StateMachine::Transition.new(@object, @machine, :ignite, :parked, :idling)
  11. end
  12. def test_should_have_an_object
  13. assert_equal @object, @transition.object
  14. end
  15. def test_should_have_a_machine
  16. assert_equal @machine, @transition.machine
  17. end
  18. def test_should_have_an_event
  19. assert_equal :ignite, @transition.event
  20. end
  21. def test_should_have_a_qualified_event
  22. assert_equal :ignite, @transition.qualified_event
  23. end
  24. def test_should_have_a_human_event
  25. assert_equal 'ignite', @transition.human_event
  26. end
  27. def test_should_have_a_from_value
  28. assert_equal 'parked', @transition.from
  29. end
  30. def test_should_have_a_from_name
  31. assert_equal :parked, @transition.from_name
  32. end
  33. def test_should_have_a_qualified_from_name
  34. assert_equal :parked, @transition.qualified_from_name
  35. end
  36. def test_should_have_a_human_from_name
  37. assert_equal 'parked', @transition.human_from_name
  38. end
  39. def test_should_have_a_to_value
  40. assert_equal 'idling', @transition.to
  41. end
  42. def test_should_have_a_to_name
  43. assert_equal :idling, @transition.to_name
  44. end
  45. def test_should_have_a_qualified_to_name
  46. assert_equal :idling, @transition.qualified_to_name
  47. end
  48. def test_should_have_a_human_to_name
  49. assert_equal 'idling', @transition.human_to_name
  50. end
  51. def test_should_have_an_attribute
  52. assert_equal :state, @transition.attribute
  53. end
  54. def test_should_not_have_an_action
  55. assert_nil @transition.action
  56. end
  57. def test_should_not_be_transient
  58. assert_equal false, @transition.transient?
  59. end
  60. def test_should_generate_attributes
  61. expected = {:object => @object, :attribute => :state, :event => :ignite, :from => 'parked', :to => 'idling'}
  62. assert_equal expected, @transition.attributes
  63. end
  64. def test_should_have_empty_args
  65. assert_equal [], @transition.args
  66. end
  67. def test_should_not_have_a_result
  68. assert_nil @transition.result
  69. end
  70. def test_should_use_pretty_inspect
  71. assert_equal '#<StateMachine::Transition attribute=:state event=:ignite from="parked" from_name=:parked to="idling" to_name=:idling>', @transition.inspect
  72. end
  73. end
  74. class TransitionWithInvalidNodesTest < Test::Unit::TestCase
  75. def setup
  76. @klass = Class.new
  77. @machine = StateMachine::Machine.new(@klass)
  78. @machine.state :parked, :idling
  79. @machine.event :ignite
  80. @object = @klass.new
  81. @object.state = 'parked'
  82. end
  83. def test_should_raise_exception_without_event
  84. assert_raise(IndexError) { StateMachine::Transition.new(@object, @machine, nil, :parked, :idling) }
  85. end
  86. def test_should_raise_exception_with_invalid_event
  87. assert_raise(IndexError) { StateMachine::Transition.new(@object, @machine, :invalid, :parked, :idling) }
  88. end
  89. def test_should_raise_exception_with_invalid_from_state
  90. assert_raise(IndexError) { StateMachine::Transition.new(@object, @machine, :ignite, :invalid, :idling) }
  91. end
  92. def test_should_raise_exception_with_invalid_to_state
  93. assert_raise(IndexError) { StateMachine::Transition.new(@object, @machine, :ignite, :parked, :invalid) }
  94. end
  95. end
  96. class TransitionWithDynamicToValueTest < Test::Unit::TestCase
  97. def setup
  98. @klass = Class.new
  99. @machine = StateMachine::Machine.new(@klass)
  100. @machine.state :parked
  101. @machine.state :idling, :value => lambda {1}
  102. @machine.event :ignite
  103. @object = @klass.new
  104. @object.state = 'parked'
  105. @transition = StateMachine::Transition.new(@object, @machine, :ignite, :parked, :idling)
  106. end
  107. def test_should_evaluate_to_value
  108. assert_equal 1, @transition.to
  109. end
  110. end
  111. class TransitionLoopbackTest < Test::Unit::TestCase
  112. def setup
  113. @klass = Class.new
  114. @machine = StateMachine::Machine.new(@klass)
  115. @machine.state :parked
  116. @machine.event :park
  117. @object = @klass.new
  118. @object.state = 'parked'
  119. @transition = StateMachine::Transition.new(@object, @machine, :park, :parked, :parked)
  120. end
  121. def test_should_be_loopback
  122. assert @transition.loopback?
  123. end
  124. end
  125. class TransitionWithDifferentStatesTest < Test::Unit::TestCase
  126. def setup
  127. @klass = Class.new
  128. @machine = StateMachine::Machine.new(@klass)
  129. @machine.state :parked, :idling
  130. @machine.event :ignite
  131. @object = @klass.new
  132. @object.state = 'parked'
  133. @transition = StateMachine::Transition.new(@object, @machine, :ignite, :parked, :idling)
  134. end
  135. def test_should_not_be_loopback
  136. assert !@transition.loopback?
  137. end
  138. end
  139. class TransitionWithNamespaceTest < Test::Unit::TestCase
  140. def setup
  141. @klass = Class.new
  142. @machine = StateMachine::Machine.new(@klass, :namespace => 'alarm')
  143. @machine.state :off, :active
  144. @machine.event :activate
  145. @object = @klass.new
  146. @object.state = 'off'
  147. @transition = StateMachine::Transition.new(@object, @machine, :activate, :off, :active)
  148. end
  149. def test_should_have_an_event
  150. assert_equal :activate, @transition.event
  151. end
  152. def test_should_have_a_qualified_event
  153. assert_equal :activate_alarm, @transition.qualified_event
  154. end
  155. def test_should_have_a_from_name
  156. assert_equal :off, @transition.from_name
  157. end
  158. def test_should_have_a_qualified_from_name
  159. assert_equal :alarm_off, @transition.qualified_from_name
  160. end
  161. def test_should_have_a_human_from_name
  162. assert_equal 'off', @transition.human_from_name
  163. end
  164. def test_should_have_a_to_name
  165. assert_equal :active, @transition.to_name
  166. end
  167. def test_should_have_a_qualified_to_name
  168. assert_equal :alarm_active, @transition.qualified_to_name
  169. end
  170. def test_should_have_a_human_to_name
  171. assert_equal 'active', @transition.human_to_name
  172. end
  173. end
  174. class TransitionWithCustomMachineAttributeTest < Test::Unit::TestCase
  175. def setup
  176. @klass = Class.new
  177. @machine = StateMachine::Machine.new(@klass, :state, :attribute => :state_id)
  178. @machine.state :off, :value => 1
  179. @machine.state :active, :value => 2
  180. @machine.event :activate
  181. @object = @klass.new
  182. @object.state_id = 1
  183. @transition = StateMachine::Transition.new(@object, @machine, :activate, :off, :active)
  184. end
  185. def test_should_persist
  186. @transition.persist
  187. assert_equal 2, @object.state_id
  188. end
  189. def test_should_rollback
  190. @object.state_id = 2
  191. @transition.rollback
  192. assert_equal 1, @object.state_id
  193. end
  194. end
  195. class TransitionWithoutReadingStateTest < Test::Unit::TestCase
  196. def setup
  197. @klass = Class.new
  198. @machine = StateMachine::Machine.new(@klass)
  199. @machine.state :parked, :idling
  200. @machine.event :ignite
  201. @object = @klass.new
  202. @object.state = 'idling'
  203. @transition = StateMachine::Transition.new(@object, @machine, :ignite, :parked, :idling, false)
  204. end
  205. def test_should_not_read_from_value_from_object
  206. assert_equal 'parked', @transition.from
  207. end
  208. def test_should_have_to_value
  209. assert_equal 'idling', @transition.to
  210. end
  211. end
  212. class TransitionWithActionTest < Test::Unit::TestCase
  213. def setup
  214. @klass = Class.new do
  215. def save
  216. end
  217. end
  218. @machine = StateMachine::Machine.new(@klass, :action => :save)
  219. @machine.state :parked, :idling
  220. @machine.event :ignite
  221. @object = @klass.new
  222. @object.state = 'parked'
  223. @transition = StateMachine::Transition.new(@object, @machine, :ignite, :parked, :idling)
  224. end
  225. def test_should_have_an_action
  226. assert_equal :save, @transition.action
  227. end
  228. def test_should_not_have_a_result
  229. assert_nil @transition.result
  230. end
  231. end
  232. class TransitionAfterBeingPersistedTest < Test::Unit::TestCase
  233. def setup
  234. @klass = Class.new
  235. @machine = StateMachine::Machine.new(@klass, :action => :save)
  236. @machine.state :parked, :idling
  237. @machine.event :ignite
  238. @object = @klass.new
  239. @object.state = 'parked'
  240. @transition = StateMachine::Transition.new(@object, @machine, :ignite, :parked, :idling)
  241. @transition.persist
  242. end
  243. def test_should_update_state_value
  244. assert_equal 'idling', @object.state
  245. end
  246. def test_should_not_change_from_state
  247. assert_equal 'parked', @transition.from
  248. end
  249. def test_should_not_change_to_state
  250. assert_equal 'idling', @transition.to
  251. end
  252. def test_should_not_be_able_to_persist_twice
  253. @object.state = 'parked'
  254. @transition.persist
  255. assert_equal 'parked', @object.state
  256. end
  257. def test_should_be_able_to_persist_again_after_resetting
  258. @object.state = 'parked'
  259. @transition.reset
  260. @transition.persist
  261. assert_equal 'idling', @object.state
  262. end
  263. def test_should_revert_to_from_state_on_rollback
  264. @transition.rollback
  265. assert_equal 'parked', @object.state
  266. end
  267. end
  268. class TransitionAfterBeingRolledBackTest < Test::Unit::TestCase
  269. def setup
  270. @klass = Class.new
  271. @machine = StateMachine::Machine.new(@klass, :action => :save)
  272. @machine.state :parked, :idling
  273. @machine.event :ignite
  274. @object = @klass.new
  275. @object.state = 'parked'
  276. @transition = StateMachine::Transition.new(@object, @machine, :ignite, :parked, :idling)
  277. @object.state = 'idling'
  278. @transition.rollback
  279. end
  280. def test_should_update_state_value_to_from_state
  281. assert_equal 'parked', @object.state
  282. end
  283. def test_should_not_change_from_state
  284. assert_equal 'parked', @transition.from
  285. end
  286. def test_should_not_change_to_state
  287. assert_equal 'idling', @transition.to
  288. end
  289. def test_should_still_be_able_to_persist
  290. @transition.persist
  291. assert_equal 'idling', @object.state
  292. end
  293. end
  294. class TransitionWithoutCallbacksTest < Test::Unit::TestCase
  295. def setup
  296. @klass = Class.new
  297. @machine = StateMachine::Machine.new(@klass)
  298. @machine.state :parked, :idling
  299. @machine.event :ignite
  300. @object = @klass.new
  301. @object.state = 'parked'
  302. @transition = StateMachine::Transition.new(@object, @machine, :ignite, :parked, :idling)
  303. end
  304. def test_should_succeed
  305. assert_equal true, @transition.run_callbacks
  306. end
  307. def test_should_succeed_if_after_callbacks_skipped
  308. assert_equal true, @transition.run_callbacks(:after => false)
  309. end
  310. def test_should_call_block_if_provided
  311. @transition.run_callbacks { @ran_block = true; {} }
  312. assert @ran_block
  313. end
  314. def test_should_track_block_result
  315. @transition.run_callbacks {{:result => 1}}
  316. assert_equal 1, @transition.result
  317. end
  318. end
  319. class TransitionWithBeforeCallbacksTest < Test::Unit::TestCase
  320. def setup
  321. @klass = Class.new
  322. @machine = StateMachine::Machine.new(@klass)
  323. @machine.state :parked, :idling
  324. @machine.event :ignite
  325. @object = @klass.new
  326. @object.state = 'parked'
  327. @transition = StateMachine::Transition.new(@object, @machine, :ignite, :parked, :idling)
  328. end
  329. def test_should_run_before_callbacks
  330. @machine.before_transition {@run = true}
  331. result = @transition.run_callbacks
  332. assert_equal true, result
  333. assert_equal true, @run
  334. end
  335. def test_should_only_run_those_that_match_transition_context
  336. @count = 0
  337. callback = lambda {@count += 1}
  338. @machine.before_transition :from => :parked, :to => :idling, :on => :park, :do => callback
  339. @machine.before_transition :from => :parked, :to => :parked, :on => :park, :do => callback
  340. @machine.before_transition :from => :parked, :to => :idling, :on => :ignite, :do => callback
  341. @machine.before_transition :from => :idling, :to => :idling, :on => :park, :do => callback
  342. @transition.run_callbacks
  343. assert_equal 1, @count
  344. end
  345. def test_should_pass_transition_as_argument
  346. @machine.before_transition {|*args| @args = args}
  347. @transition.run_callbacks
  348. assert_equal [@object, @transition], @args
  349. end
  350. def test_should_catch_halts
  351. @machine.before_transition {throw :halt}
  352. result = nil
  353. assert_nothing_thrown { result = @transition.run_callbacks }
  354. assert_equal false, result
  355. end
  356. def test_should_not_catch_exceptions
  357. @machine.before_transition {raise ArgumentError}
  358. assert_raise(ArgumentError) { @transition.run_callbacks }
  359. end
  360. def test_should_not_be_able_to_run_twice
  361. @count = 0
  362. @machine.before_transition {@count += 1}
  363. @transition.run_callbacks
  364. @transition.run_callbacks
  365. assert_equal 1, @count
  366. end
  367. def test_should_be_able_to_run_again_after_halt
  368. @count = 0
  369. @machine.before_transition {@count += 1; throw :halt}
  370. @transition.run_callbacks
  371. @transition.run_callbacks
  372. assert_equal 2, @count
  373. end
  374. def test_should_be_able_to_run_again_after_resetting
  375. @count = 0
  376. @machine.before_transition {@count += 1}
  377. @transition.run_callbacks
  378. @transition.reset
  379. @transition.run_callbacks
  380. assert_equal 2, @count
  381. end
  382. def test_should_succeed_if_block_result_is_false
  383. @machine.before_transition {@run = true}
  384. assert_equal true, @transition.run_callbacks {{:result => false}}
  385. assert @run
  386. end
  387. def test_should_succeed_if_block_result_is_true
  388. @machine.before_transition {@run = true}
  389. assert_equal true, @transition.run_callbacks {{:result => true}}
  390. assert @run
  391. end
  392. def test_should_succeed_if_block_success_is_false
  393. @machine.before_transition {@run = true}
  394. assert_equal true, @transition.run_callbacks {{:success => false}}
  395. assert @run
  396. end
  397. def test_should_succeed_if_block_success_is_false
  398. @machine.before_transition {@run = true}
  399. assert_equal true, @transition.run_callbacks {{:success => true}}
  400. assert @run
  401. end
  402. end
  403. class TransitionWithMultipleBeforeCallbacksTest < Test::Unit::TestCase
  404. def setup
  405. @klass = Class.new
  406. @machine = StateMachine::Machine.new(@klass)
  407. @machine.state :parked, :idling
  408. @machine.event :ignite
  409. @object = @klass.new
  410. @object.state = 'parked'
  411. @transition = StateMachine::Transition.new(@object, @machine, :ignite, :parked, :idling)
  412. end
  413. def test_should_run_in_the_order_they_were_defined
  414. @callbacks = []
  415. @machine.before_transition {@callbacks << 1}
  416. @machine.before_transition {@callbacks << 2}
  417. @transition.run_callbacks
  418. assert_equal [1, 2], @callbacks
  419. end
  420. def test_should_not_run_further_callbacks_if_halted
  421. @callbacks = []
  422. @machine.before_transition {@callbacks << 1; throw :halt}
  423. @machine.before_transition {@callbacks << 2}
  424. assert_equal false, @transition.run_callbacks
  425. assert_equal [1], @callbacks
  426. end
  427. def test_should_fail_if_any_callback_halted
  428. @machine.before_transition {true}
  429. @machine.before_transition {throw :halt}
  430. assert_equal false, @transition.run_callbacks
  431. end
  432. end
  433. class TransitionWithAfterCallbacksTest < Test::Unit::TestCase
  434. def setup
  435. @klass = Class.new
  436. @machine = StateMachine::Machine.new(@klass)
  437. @machine.state :parked, :idling
  438. @machine.event :ignite
  439. @object = @klass.new
  440. @object.state = 'parked'
  441. @transition = StateMachine::Transition.new(@object, @machine, :ignite, :parked, :idling)
  442. end
  443. def test_should_run_after_callbacks
  444. @machine.after_transition {|object| @run = true}
  445. result = @transition.run_callbacks
  446. assert_equal true, result
  447. assert_equal true, @run
  448. end
  449. def test_should_only_run_those_that_match_transition_context
  450. @count = 0
  451. callback = lambda {@count += 1}
  452. @machine.after_transition :from => :parked, :to => :idling, :on => :park, :do => callback
  453. @machine.after_transition :from => :parked, :to => :parked, :on => :park, :do => callback
  454. @machine.after_transition :from => :parked, :to => :idling, :on => :ignite, :do => callback
  455. @machine.after_transition :from => :idling, :to => :idling, :on => :park, :do => callback
  456. @transition.run_callbacks
  457. assert_equal 1, @count
  458. end
  459. def test_should_not_run_if_not_successful
  460. @machine.after_transition {|object| @run = true}
  461. @transition.run_callbacks {{:success => false}}
  462. assert !@run
  463. end
  464. def test_should_run_if_not_successful_and_includes_failures
  465. @machine.after_transition(:include_failures => true) {|object| @run = true}
  466. @transition.run_callbacks {{:success => false}}
  467. assert @run
  468. end
  469. def test_should_run_if_successful
  470. @machine.after_transition {|object| @run = true}
  471. @transition.run_callbacks {{:success => true}}
  472. assert @run
  473. end
  474. def test_should_run_if_successful_and_includes_failures
  475. @machine.after_transition(:include_failures => true) {|object| @run = true}
  476. @transition.run_callbacks {{:success => true}}
  477. assert @run
  478. end
  479. def test_should_pass_transition_as_argument
  480. @machine.after_transition {|*args| @args = args}
  481. @transition.run_callbacks
  482. assert_equal [@object, @transition], @args
  483. end
  484. def test_should_catch_halts
  485. @machine.after_transition {throw :halt}
  486. result = nil
  487. assert_nothing_thrown { result = @transition.run_callbacks }
  488. assert_equal true, result
  489. end
  490. def test_should_not_catch_exceptions
  491. @machine.after_transition {raise ArgumentError}
  492. assert_raise(ArgumentError) { @transition.run_callbacks }
  493. end
  494. def test_should_not_be_able_to_run_twice
  495. @count = 0
  496. @machine.after_transition {@count += 1}
  497. @transition.run_callbacks
  498. @transition.run_callbacks
  499. assert_equal 1, @count
  500. end
  501. def test_should_not_be_able_to_run_twice_if_halted
  502. @count = 0
  503. @machine.after_transition {@count += 1; throw :halt}
  504. @transition.run_callbacks
  505. @transition.run_callbacks
  506. assert_equal 1, @count
  507. end
  508. def test_should_be_able_to_run_again_after_resetting
  509. @count = 0
  510. @machine.after_transition {@count += 1}
  511. @transition.run_callbacks
  512. @transition.reset
  513. @transition.run_callbacks
  514. assert_equal 2, @count
  515. end
  516. end
  517. class TransitionWithMultipleAfterCallbacksTest < Test::Unit::TestCase
  518. def setup
  519. @klass = Class.new
  520. @machine = StateMachine::Machine.new(@klass)
  521. @machine.state :parked, :idling
  522. @machine.event :ignite
  523. @object = @klass.new
  524. @object.state = 'parked'
  525. @transition = StateMachine::Transition.new(@object, @machine, :ignite, :parked, :idling)
  526. end
  527. def test_should_run_in_the_order_they_were_defined
  528. @callbacks = []
  529. @machine.after_transition {@callbacks << 1}
  530. @machine.after_transition {@callbacks << 2}
  531. @transition.run_callbacks
  532. assert_equal [1, 2], @callbacks
  533. end
  534. def test_should_not_run_further_callbacks_if_halted
  535. @callbacks = []
  536. @machine.after_transition {@callbacks << 1; throw :halt}
  537. @machine.after_transition {@callbacks << 2}
  538. assert_equal true, @transition.run_callbacks
  539. assert_equal [1], @callbacks
  540. end
  541. def test_should_fail_if_any_callback_halted
  542. @machine.after_transition {true}
  543. @machine.after_transition {throw :halt}
  544. assert_equal true, @transition.run_callbacks
  545. end
  546. end
  547. class TransitionWithAroundCallbacksTest < Test::Unit::TestCase
  548. def setup
  549. @klass = Class.new
  550. @machine = StateMachine::Machine.new(@klass)
  551. @machine.state :parked, :idling
  552. @machine.event :ignite
  553. @object = @klass.new
  554. @object.state = 'parked'
  555. @transition = StateMachine::Transition.new(@object, @machine, :ignite, :parked, :idling)
  556. end
  557. def test_should_run_around_callbacks
  558. @machine.around_transition {|object, transition, block| @run_before = true; block.call; @run_after = true}
  559. result = @transition.run_callbacks
  560. assert_equal true, result
  561. assert_equal true, @run_before
  562. assert_equal true, @run_after
  563. end
  564. def test_should_only_run_those_that_match_transition_context
  565. @count = 0
  566. callback = lambda {|object, transition, block| @count += 1; block.call}
  567. @machine.around_transition :from => :parked, :to => :idling, :on => :park, :do => callback
  568. @machine.around_transition :from => :parked, :to => :parked, :on => :park, :do => callback
  569. @machine.around_transition :from => :parked, :to => :idling, :on => :ignite, :do => callback
  570. @machine.around_transition :from => :idling, :to => :idling, :on => :park, :do => callback
  571. @transition.run_callbacks
  572. assert_equal 1, @count
  573. end
  574. def test_should_pass_transition_as_argument
  575. @machine.around_transition {|*args| block = args.pop; @args = args; block.call}
  576. @transition.run_callbacks
  577. assert_equal [@object, @transition], @args
  578. end
  579. def test_should_run_block_between_callback
  580. @callbacks = []
  581. @machine.around_transition {|block| @callbacks << :before; block.call; @callbacks << :after}
  582. @transition.run_callbacks { @callbacks << :within; {:success => true} }
  583. assert_equal [:before, :within, :after], @callbacks
  584. end
  585. def test_should_have_access_to_result_after_yield
  586. @machine.around_transition {|block| @before_result = @transition.result; block.call; @after_result = @transition.result}
  587. @transition.run_callbacks {{:result => 1, :success => true}}
  588. assert_nil @before_result
  589. assert_equal 1, @after_result
  590. end
  591. def test_should_catch_before_yield_halts
  592. @machine.around_transition {throw :halt}
  593. result = nil
  594. assert_nothing_thrown { result = @transition.run_callbacks }
  595. assert_equal false, result
  596. end
  597. def test_should_catch_after_yield_halts
  598. @machine.around_transition {|block| block.call; throw :halt}
  599. result = nil
  600. assert_nothing_thrown { result = @transition.run_callbacks }
  601. assert_equal true, result
  602. end
  603. def test_should_not_catch_before_yield
  604. @machine.around_transition {raise ArgumentError}
  605. assert_raise(ArgumentError) { @transition.run_callbacks }
  606. end
  607. def test_should_not_catch_after_yield
  608. @machine.around_transition {|block| block.call; raise ArgumentError}
  609. assert_raise(ArgumentError) { @transition.run_callbacks }
  610. end
  611. def test_should_fail_if_not_yielded
  612. @machine.around_transition {}
  613. result = nil
  614. assert_nothing_thrown { result = @transition.run_callbacks }
  615. assert_equal false, result
  616. end
  617. def test_should_not_be_able_to_run_twice
  618. @before_count = 0
  619. @after_count = 0
  620. @machine.around_transition {|block| @before_count += 1; block.call; @after_count += 1}
  621. @transition.run_callbacks
  622. @transition.run_callbacks
  623. assert_equal 1, @before_count
  624. assert_equal 1, @after_count
  625. end
  626. def test_should_be_able_to_run_again_after_resetting
  627. @before_count = 0
  628. @after_count = 0
  629. @machine.around_transition {|block| @before_count += 1; block.call; @after_count += 1}
  630. @transition.run_callbacks
  631. @transition.reset
  632. @transition.run_callbacks
  633. assert_equal 2, @before_count
  634. assert_equal 2, @after_count
  635. end
  636. def test_should_succeed_if_block_result_is_false
  637. @machine.around_transition {|block| @before_run = true; block.call; @after_run = true}
  638. assert_equal true, @transition.run_callbacks {{:success => true, :result => false}}
  639. assert @before_run
  640. assert @after_run
  641. end
  642. def test_should_succeed_if_block_result_is_true
  643. @machine.around_transition {|block| @before_run = true; block.call; @after_run = true}
  644. assert_equal true, @transition.run_callbacks {{:success => true, :result => true}}
  645. assert @before_run
  646. assert @after_run
  647. end
  648. def test_should_only_run_before_if_block_success_is_false
  649. @machine.around_transition {|block| @before_run = true; block.call; @after_run = true}
  650. assert_equal true, @transition.run_callbacks {{:success => false}}
  651. assert @before_run
  652. assert !@after_run
  653. end
  654. def test_should_succeed_if_including_failure_and_block_success_is_false
  655. @machine.around_transition(:include_failures => true) {|block| @before_run = true; block.call; @after_run = true}
  656. assert_equal true, @transition.run_callbacks {{:success => false}}
  657. assert @before_run
  658. assert @after_run
  659. end
  660. def test_should_succeed_if_block_success_is_false
  661. @machine.around_transition {|block| @before_run = true; block.call; @after_run = true}
  662. assert_equal true, @transition.run_callbacks {{:success => true}}
  663. assert @before_run
  664. assert @after_run
  665. end
  666. end
  667. class TransitionWithMultipleAroundCallbacksTest < Test::Unit::TestCase
  668. def setup
  669. @klass = Class.new
  670. @machine = StateMachine::Machine.new(@klass)
  671. @machine.state :parked, :idling
  672. @machine.event :ignite
  673. @object = @klass.new
  674. @object.state = 'parked'
  675. @transition = StateMachine::Transition.new(@object, @machine, :ignite, :parked, :idling)
  676. end
  677. def test_should_before_yield_in_the_order_they_were_defined
  678. @callbacks = []
  679. @machine.around_transition {|block| @callbacks << 1; block.call}
  680. @machine.around_transition {|block| @callbacks << 2; block.call}
  681. @transition.run_callbacks
  682. assert_equal [1, 2], @callbacks
  683. end
  684. def test_should_before_yield_multiple_methods_in_the_order_they_were_defined
  685. @callbacks = []
  686. @machine.around_transition(lambda {|block| @callbacks << 1; block.call}, lambda {|block| @callbacks << 2; block.call})
  687. @machine.around_transition(lambda {|block| @callbacks << 3; block.call}, lambda {|block| @callbacks << 4; block.call})
  688. @transition.run_callbacks
  689. assert_equal [1, 2, 3, 4], @callbacks
  690. end
  691. def test_should_after_yield_in_the_reverse_order_they_were_defined
  692. @callbacks = []
  693. @machine.around_transition {|block| block.call; @callbacks << 1}
  694. @machine.around_transition {|block| block.call; @callbacks << 2}
  695. @transition.run_callbacks
  696. assert_equal [2, 1], @callbacks
  697. end
  698. def test_should_after_yield_multiple_methods_in_the_reverse_order_they_were_defined
  699. @callbacks = []
  700. @machine.around_transition(lambda {|block| block.call; @callbacks << 1}) {|block| block.call; @callbacks << 2}
  701. @machine.around_transition(lambda {|block| block.call; @callbacks << 3}) {|block| block.call; @callbacks << 4}
  702. @transition.run_callbacks
  703. assert_equal [4, 3, 2, 1], @callbacks
  704. end
  705. def test_should_run_block_between_callback
  706. @callbacks = []
  707. @machine.around_transition {|block| @callbacks << :before_1; block.call; @callbacks << :after_1}
  708. @machine.around_transition {|block| @callbacks << :before_2; block.call; @callbacks << :after_2}
  709. @transition.run_callbacks { @callbacks << :within; {:success => true} }
  710. assert_equal [:before_1, :before_2, :within, :after_2, :after_1], @callbacks
  711. end
  712. def test_should_have_access_to_result_after_yield
  713. @machine.around_transition {|block| @before_result_1 = @transition.result; block.call; @after_result_1 = @transition.result}
  714. @machine.around_transition {|block| @before_result_2 = @transition.result; block.call; @after_result_2 = @transition.result}
  715. @transition.run_callbacks {{:result => 1, :success => true}}
  716. assert_nil @before_result_1
  717. assert_nil @before_result_2
  718. assert_equal 1, @after_result_1
  719. assert_equal 1, @after_result_2
  720. end
  721. def test_should_fail_if_any_before_yield_halted
  722. @machine.around_transition {|block| block.call}
  723. @machine.around_transition {throw :halt}
  724. assert_equal false, @transition.run_callbacks
  725. end
  726. def test_should_not_continue_around_callbacks_if_before_yield_halted
  727. @callbacks = []
  728. @machine.around_transition {@callbacks << 1; throw :halt}
  729. @machine.around_transition {|block| @callbacks << 2; block.call; @callbacks << 3}
  730. assert_equal false, @transition.run_callbacks
  731. assert_equal [1], @callbacks
  732. end
  733. def test_should_not_continue_around_callbacks_if_later_before_yield_halted
  734. @callbacks = []
  735. @machine.around_transition {|block| block.call; @callbacks << 1}
  736. @machine.around_transition {throw :halt}
  737. @transition.run_callbacks
  738. assert_equal [], @callbacks
  739. end
  740. def test_should_not_run_further_callbacks_if_after_yield_halted
  741. @callbacks = []
  742. @machine.around_transition {|block| block.call; @callbacks << 1}
  743. @machine.around_transition {|block| block.call; throw :halt}
  744. assert_equal true, @transition.run_callbacks
  745. assert_equal [], @callbacks
  746. end
  747. def test_should_fail_if_any_fail_to_yield
  748. @callbacks = []
  749. @machine.around_transition {@callbacks << 1}
  750. @machine.around_transition {|block| @callbacks << 2; block.call; @callbacks << 3}
  751. assert_equal false, @transition.run_callbacks
  752. assert_equal [1], @callbacks
  753. end
  754. end
  755. class TransitionWithMixedCallbacksTest < Test::Unit::TestCase
  756. def setup
  757. @klass = Class.new
  758. @machine = StateMachine::Machine.new(@klass)
  759. @machine.state :parked, :idling
  760. @machine.event :ignite
  761. @object = @klass.new
  762. @object.state = 'parked'
  763. @transition = StateMachine::Transition.new(@object, @machine, :ignite, :parked, :idling)
  764. end
  765. def test_should_before_and_around_callbacks_in_order_defined
  766. @callbacks = []
  767. @machine.before_transition {@callbacks << :before_1}
  768. @machine.around_transition {|block| @callbacks << :around; block.call}
  769. @machine.before_transition {@callbacks << :before_2}
  770. assert_equal true, @transition.run_callbacks
  771. assert_equal [:before_1, :around, :before_2], @callbacks
  772. end
  773. def test_should_run_around_callbacks_before_after_callbacks
  774. @callbacks = []
  775. @machine.after_transition {@callbacks << :after_1}
  776. @machine.around_transition {|block| block.call; @callbacks << :after_2}
  777. @machine.after_transition {@callbacks << :after_3}
  778. assert_equal true, @transition.run_callbacks
  779. assert_equal [:after_2, :after_1, :after_3], @callbacks
  780. end
  781. def test_should_have_access_to_result_for_both_after_and_around_callbacks
  782. @machine.after_transition {@after_result = @transition.result}
  783. @machine.around_transition {|block| block.call; @around_result = @transition.result}
  784. @transition.run_callbacks {{:result => 1, :success => true}}
  785. assert_equal 1, @after_result
  786. assert_equal 1, @around_result
  787. end
  788. def test_should_not_run_further_callbacks_if_before_callback_halts
  789. @callbacks = []
  790. @machine.before_transition {@callbacks << :before_1}
  791. @machine.around_transition {|block| @callbacks << :before_around_1; block.call; @callbacks << :after_around_1}
  792. @machine.before_transition {@callbacks << :before_2; throw :halt}
  793. @machine.around_transition {|block| @callbacks << :before_around_2; block.call; @callbacks << :after_around_2}
  794. @machine.after_transition {@callbacks << :after}
  795. assert_equal false, @transition.run_callbacks
  796. assert_equal [:before_1, :before_around_1, :before_2], @callbacks
  797. end
  798. def test_should_not_run_further_callbacks_if_before_yield_halts
  799. @callbacks = []
  800. @machine.before_transition {@callbacks << :before_1}
  801. @machine.around_transition {|block| @callbacks << :before_around_1; throw :halt}
  802. @machine.before_transition {@callbacks << :before_2; throw :halt}
  803. @machine.around_transition {|block| @callbacks << :before_around_2; block.call; @callbacks << :after_around_2}
  804. @machine.after_transition {@callbacks << :after}
  805. assert_equal false, @transition.run_callbacks
  806. assert_equal [:before_1, :before_around_1], @callbacks
  807. end
  808. def test_should_not_run_further_callbacks_if_around_callback_fails_to_yield
  809. @callbacks = []
  810. @machine.before_transition {@callbacks << :before_1}
  811. @machine.around_transition {|block| @callbacks << :before_around_1}
  812. @machine.before_transition {@callbacks << :before_2; throw :halt}
  813. @machine.around_transition {|block| @callbacks << :before_around_2; block.call; @callbacks << :after_around_2}
  814. @machine.after_transition {@callbacks << :after}
  815. assert_equal false, @transition.run_callbacks
  816. assert_equal [:before_1, :before_around_1], @callbacks
  817. end
  818. def test_should_not_run_further_callbacks_if_after_yield_halts
  819. @callbacks = []
  820. @machine.before_transition {@callbacks << :before_1}
  821. @machine.around_transition {|block| @callbacks << :before_around_1; block.call; @callbacks << :after_around_1; throw :halt}
  822. @machine.before_transition {@callbacks << :before_2}
  823. @machine.around_transition {|block| @callbacks << :before_around_2; block.call; @callbacks << :after_around_2}
  824. @machine.after_transition {@callbacks << :after}
  825. assert_equal true, @transition.run_callbacks
  826. assert_equal [:before_1, :before_around_1, :before_2, :before_around_2, :after_around_2, :after_around_1], @callbacks
  827. end
  828. def test_should_not_run_further_callbacks_if_after_callback_halts
  829. @callbacks = []
  830. @machine.before_transition {@callbacks << :before_1}
  831. @machine.around_transition {|block| @callbacks << :before_around_1; block.call; @callbacks << :after_around_1}
  832. @machine.before_transition {@callbacks << :before_2}
  833. @machine.around_transition {|block| @callbacks << :before_around_2; block.call; @callbacks << :after_around_2}
  834. @machine.after_transition {@callbacks << :after_1; throw :halt}
  835. @machine.after_transition {@callbacks << :after_2}
  836. assert_equal true, @transition.run_callbacks
  837. assert_equal [:before_1, :before_around_1, :before_2, :before_around_2, :after_around_2, :after_around_1, :after_1], @callbacks
  838. end
  839. def test_should_run_any_callbacks_that_include_failures_if_block_success_is_false
  840. @callbacks = []
  841. @machine.around_transition {|block| block.call; @callbacks << :after_around_1}
  842. @machine.around_transition(:include_failures => true) {|block| block.call; @callbacks << :after_around_2}
  843. @machine.after_transition {@callbacks << :after_1}
  844. @machine.after_transition(:include_failures => true) {@callbacks << :after_2}
  845. assert_equal true, @transition.run_callbacks {{:success => false}}
  846. assert_equal [:after_around_2, :after_2], @callbacks
  847. end
  848. end
  849. class TransitionWithAfterCallbacksSkippedTest < Test::Unit::TestCase
  850. def setup
  851. @klass = Class.new
  852. @machine = StateMachine::Machine.new(@klass)
  853. @machine.state :parked, :idling
  854. @machine.event :ignite
  855. @object = @klass.new
  856. @object.state = 'parked'
  857. @transition = StateMachine::Transition.new(@object, @machine, :ignite, :parked, :idling)
  858. end
  859. def test_should_run_before_callbacks
  860. @machine.before_transition {@run = true}
  861. assert_equal true, @transition.run_callbacks(:after => false)
  862. assert @run
  863. end
  864. def test_should_run_around_callbacks_before_yield
  865. @machine.around_transition {|block| @run = true; block.call}
  866. assert_equal true, @transition.run_callbacks(:after => false)
  867. assert @run
  868. end
  869. def test_should_not_run_after_callbacks
  870. @machine.after_transition {@run = true}
  871. assert_equal true, @transition.run_callbacks(:after => false)
  872. assert !@run
  873. end
  874. def test_should_not_run_around_callbacks_after_yield
  875. @machine.around_transition {|block| block.call; @run = true}
  876. assert_equal true, @transition.run_callbacks(:after => false)
  877. assert !@run
  878. end
  879. def test_should_run_after_callbacks_that_include_failures_if_block_success_is_false
  880. @machine.after_transition(:include_failures => true) {@run = true}
  881. assert_equal true, @transition.run_callbacks(:after => false) {{:success => false}}
  882. assert @run
  883. end
  884. def test_should_run_around_callbacks_that_include_failures_if_block_success_is_false
  885. @machine.around_transition(:include_failures => true) {|block| block.call; @run = true}
  886. assert_equal true, @transition.run_callbacks(:after => false) {{:success => false}}
  887. assert @run
  888. end
  889. def test_should_continue_around_transition_execution_on_second_call
  890. @callbacks = []
  891. @machine.around_transition {|block| @callbacks << :before_around_1; block.call; @callbacks << :after_around_1}
  892. @machine.around_transition {|block| @callbacks << :before_around_2; block.call; @callbacks << :after_around_2}
  893. @machine.after_transition {@callbacks << :after}
  894. assert_equal true, @transition.run_callbacks(:after => false)
  895. assert_equal [:before_around_1, :before_around_2], @callbacks
  896. assert_equal true, @transition.run_callbacks
  897. assert_equal [:before_around_1, :before_around_2, :after_around_2, :after_around_1, :after], @callbacks
  898. end
  899. def test_should_not_run_further_callbacks_if_halted_during_continue_around_transition
  900. @callbacks = []
  901. @machine.around_transition {|block| @callbacks << :before_around_1; block.call; @callbacks << :after_around_1}
  902. @machine.around_transition {|block| @callbacks << :before_around_2; block.call; @callbacks << :after_around_2; throw :halt}
  903. @machine.after_transition {@callbacks << :after}
  904. assert_equal true, @transition.run_callbacks(:after => false)
  905. assert_equal [:before_around_1, :before_around_2], @callbacks
  906. assert_equal true, @transition.run_callbacks
  907. assert_equal [:before_around_1, :before_around_2, :after_around_2], @callbacks
  908. end
  909. def test_should_not_be_able_to_continue_twice
  910. @count = 0
  911. @machine.around_transition {|block| block.call; @count += 1}
  912. @machine.after_transition {@count += 1}
  913. @transition.run_callbacks(:after => false)
  914. 2.times do
  915. assert_equal true, @transition.run_callbacks
  916. assert_equal 2, @count
  917. end
  918. end
  919. def test_should_not_be_able_to_continue_again_after_halted
  920. @count = 0
  921. @machine.around_transition {|block| block.call; @count += 1; throw :halt}
  922. @machine.after_transition {@count += 1}
  923. @transition.run_callbacks(:after => false)
  924. 2.times do
  925. assert_equal true, @transition.run_callbacks
  926. assert_equal 1, @count
  927. end
  928. end
  929. def test_should_have_access_to_result_after_continued
  930. @machine.around_transition {|block| @around_before_result = @transition.result; block.call; @around_after_result = @transition.result}
  931. @machine.after_transition {@after_result = @transition.result}
  932. @transition.run_callbacks(:after => false)
  933. @transition.run_callbacks {{:result => 1}}
  934. assert_nil @around_before_result
  935. assert_equal 1, @around_after_result
  936. assert_equal 1, @after_result
  937. end
  938. def test_should_raise_exceptions_during_around_callbacks_after_yield_in_second_execution
  939. @machine.around_transition {|block| block.call; raise ArgumentError}
  940. assert_nothing_raised { @transition.run_callbacks(:after => false) }
  941. assert_raise(ArgumentError) { @transition.run_callbacks }
  942. end
  943. end
  944. class TransitionAfterBeingPerformedTest < Test::Unit::TestCase
  945. def setup
  946. @klass = Class.new do
  947. attr_reader :saved, :save_state
  948. def save
  949. @save_state = state
  950. @saved = true
  951. 1
  952. end
  953. end
  954. @machine = StateMachine::Machine.new(@klass, :action => :save)
  955. @machine.state :parked, :idling
  956. @machine.event :ignite
  957. @object = @klass.new
  958. @object.state = 'parked'
  959. @transition = StateMachine::Transition.new(@object, @machine, :ignite, :parked, :idling)
  960. @result = @transition.perform
  961. end
  962. def test_should_have_empty_args
  963. assert_equal [], @transition.args
  964. end
  965. def test_should_have_a_result
  966. assert_equal 1, @transition.result
  967. end
  968. def test_should_be_successful
  969. assert_equal true, @result
  970. end
  971. def test_should_change_the_current_state
  972. assert_equal 'idling', @object.state
  973. end
  974. def test_should_run_the_action
  975. assert @object.saved
  976. end
  977. def test_should_run_the_action_after_saving_the_state
  978. assert_equal 'idling', @object.save_state
  979. end
  980. end
  981. class TransitionWithPerformArgumentsTest < Test::Unit::TestCase
  982. def setup
  983. @klass = Class.new do
  984. attr_reader :saved
  985. def save
  986. @saved = true
  987. end
  988. end
  989. @machine = StateMachine::Machine.new(@klass, :action => :save)
  990. @machine.state :parked, :idling
  991. @machine.event :ignite
  992. @object = @klass.new
  993. @object.state = 'parked'
  994. @transition = StateMachine::Transition.new(@object, @machine, :ignite, :parked, :idling)
  995. end
  996. def test_should_have_arguments
  997. @transition.perform(1, 2)
  998. assert_equal [1, 2], @transition.args
  999. assert @object.saved
  1000. end
  1001. def test_should_not_include_run_action_in_arguments
  1002. @transition.perform(1, 2, false)
  1003. assert_equal [1, 2], @transition.args
  1004. assert !@object.saved
  1005. end
  1006. end
  1007. class TransitionWithoutRunningActionTest < Test::Unit::TestCase
  1008. def setup
  1009. @klass = Class.new do
  1010. attr_reader :saved
  1011. def save
  1012. @saved = true
  1013. end
  1014. end
  1015. @machine = StateMachine::Machine.new(@klass, :action => :save)
  1016. @machine.state :parked, :idling
  1017. @machine.event :ignite
  1018. @machine.after_transition {|object| @run_after = true}
  1019. @object = @klass.new
  1020. @object.state = 'parked'
  1021. @transition = StateMachine::Transition.new(@object, @machine, :ignite, :parked, :idling)
  1022. @result = @transition.perform(false)
  1023. end
  1024. def test_should_have_empty_args
  1025. assert_equal [], @transition.args
  1026. end
  1027. def test_should_not_have_a_result
  1028. assert_nil @transition.result
  1029. end
  1030. def test_should_be_successful
  1031. assert_equal true, @result
  1032. end
  1033. def test_should_change_the_current_state
  1034. assert_equal 'idling', @object.state
  1035. end
  1036. def test_should_not_run_the_action
  1037. assert !@object.saved
  1038. end
  1039. def test_should_run_after_callbacks
  1040. assert @run_after
  1041. end
  1042. end
  1043. class TransitionWithTransactionsTest < Test::Unit::TestCase
  1044. def setup
  1045. @klass = Class.new do
  1046. class << self
  1047. attr_accessor :running_transaction
  1048. end
  1049. attr_accessor :result
  1050. def save
  1051. @result = self.class.running_transaction
  1052. true
  1053. end
  1054. end
  1055. @machine = StateMachine::Machine.new(@klass, :action => :save)
  1056. @machine.state :parked, :idling
  1057. @machine.event :ignite
  1058. @object = @klass.new
  1059. @object.state = 'parked'
  1060. @transition = StateMachine::Transition.new(@object, @machine, :ignite, :parked, :idling)
  1061. class << @machine
  1062. def within_transaction(object)
  1063. owner_class.running_transaction = object
  1064. yield
  1065. owner_class.running_transaction = false
  1066. end
  1067. end
  1068. end
  1069. def test_should_run_blocks_within_transaction_for_object
  1070. @transition.within_transaction do
  1071. @result = @klass.running_transaction
  1072. end
  1073. assert_equal @object, @result
  1074. end
  1075. end
  1076. class TransitionTransientTest < Test::Unit::TestCase
  1077. def setup
  1078. @klass = Class.new
  1079. @machine = StateMachine::Machine.new(@klass)
  1080. @machine.state :parked, :idling
  1081. @machine.event :ignite
  1082. @object = @klass.new
  1083. @object.state = 'parked'
  1084. @transition = StateMachine::Transition.new(@object, @machine, :ignite, :parked, :idling)
  1085. @transition.transient = true
  1086. end
  1087. def test_should_be_transient
  1088. assert @transition.transient?
  1089. end
  1090. end