PageRenderTime 52ms CodeModel.GetById 16ms RepoModel.GetById 0ms app.codeStats 0ms

/test/unit/integrations/mongoid_test.rb

https://github.com/johnsonzes/state_machine
Ruby | 1591 lines | 1230 code | 355 blank | 6 comment | 3 complexity | b4ea6a01aaea3c3a5ec8c6c34b089036 MD5 | raw file
  1. require File.expand_path(File.dirname(__FILE__) + '/../../test_helper')
  2. # Load library
  3. require 'rubygems'
  4. gem 'mongoid', ENV['VERSION'] ? "=#{ENV['VERSION']}" : '>=2.0.0'
  5. require 'mongoid'
  6. # Establish database connection
  7. Mongoid.configure do |config|
  8. config.master = Mongo::Connection.new('127.0.0.1', 27017, :slave_ok => true).db('test')
  9. end
  10. module MongoidTest
  11. class BaseTestCase < Test::Unit::TestCase
  12. def default_test
  13. end
  14. protected
  15. # Creates a new Mongoid model (and the associated table)
  16. def new_model(table_name = :foo, &block)
  17. model = Class.new do
  18. (class << self; self; end).class_eval do
  19. define_method(:name) { "MongoidTest::#{table_name.to_s.capitalize}" }
  20. define_method(:to_s) { name }
  21. end
  22. end
  23. model.class_eval do
  24. include Mongoid::Document
  25. self.collection_name = table_name
  26. field :state, :type => String
  27. end
  28. model.class_eval(&block) if block_given?
  29. model.collection.remove
  30. model
  31. end
  32. end
  33. class IntegrationTest < BaseTestCase
  34. def test_should_have_an_integration_name
  35. assert_equal :mongoid, StateMachine::Integrations::Mongoid.integration_name
  36. end
  37. def test_should_be_available
  38. assert StateMachine::Integrations::Mongoid.available?
  39. end
  40. def test_should_match_if_class_includes_mongoid
  41. assert StateMachine::Integrations::Mongoid.matches?(new_model)
  42. end
  43. def test_should_not_match_if_class_does_not_include_mongoid
  44. assert !StateMachine::Integrations::Mongoid.matches?(Class.new)
  45. end
  46. def test_should_have_defaults
  47. assert_equal e = {:action => :save}, StateMachine::Integrations::Mongoid.defaults
  48. end
  49. def test_should_have_a_locale_path
  50. assert_not_nil StateMachine::Integrations::Mongoid.locale_path
  51. end
  52. end
  53. class MachineByDefaultTest < BaseTestCase
  54. def setup
  55. @model = new_model
  56. @machine = StateMachine::Machine.new(@model)
  57. end
  58. def test_should_use_save_as_action
  59. assert_equal :save, @machine.action
  60. end
  61. def test_should_create_notifier_before_callback
  62. assert_equal 1, @machine.callbacks[:before].size
  63. end
  64. def test_should_create_notifier_after_callback
  65. assert_equal 1, @machine.callbacks[:after].size
  66. end
  67. end
  68. class MachineWithStatesTest < BaseTestCase
  69. def setup
  70. @model = new_model
  71. @machine = StateMachine::Machine.new(@model)
  72. @machine.state :first_gear
  73. end
  74. def test_should_humanize_name
  75. assert_equal 'first gear', @machine.state(:first_gear).human_name
  76. end
  77. end
  78. class MachineWithStaticInitialStateTest < BaseTestCase
  79. def setup
  80. @model = new_model do
  81. attr_accessor :value
  82. end
  83. @machine = StateMachine::Machine.new(@model, :initial => :parked)
  84. end
  85. def test_should_set_initial_state_on_created_object
  86. record = @model.new
  87. assert_equal 'parked', record.state
  88. end
  89. def test_should_set_initial_state_with_nil_attributes
  90. record = @model.new(nil)
  91. assert_equal 'parked', record.state
  92. end
  93. def test_should_still_set_attributes
  94. record = @model.new(:value => 1)
  95. assert_equal 1, record.value
  96. end
  97. def test_should_still_allow_initialize_blocks
  98. block_args = nil
  99. record = @model.new do |*args|
  100. block_args = args
  101. end
  102. assert_equal [record], block_args
  103. end
  104. def test_should_set_initial_state_before_setting_attributes
  105. @model.class_eval do
  106. attr_accessor :state_during_setter
  107. define_method(:value=) do |value|
  108. self.state_during_setter = state
  109. end
  110. end
  111. record = @model.new(:value => 1)
  112. assert_equal 'parked', record.state_during_setter
  113. end
  114. def test_should_not_set_initial_state_after_already_initialized
  115. record = @model.new(:value => 1)
  116. assert_equal 'parked', record.state
  117. record.state = 'idling'
  118. record.process({})
  119. assert_equal 'idling', record.state
  120. end
  121. def test_should_use_stored_values_when_loading_from_database
  122. @machine.state :idling
  123. record = @model.find(@model.create(:state => 'idling').id)
  124. assert_equal 'idling', record.state
  125. end
  126. def test_should_use_stored_values_when_loading_from_database_with_nil_state
  127. @machine.state nil
  128. record = @model.find(@model.create(:state => nil).id)
  129. assert_nil record.state
  130. end
  131. end
  132. class MachineWithDynamicInitialStateTest < BaseTestCase
  133. def setup
  134. @model = new_model do
  135. attr_accessor :value
  136. end
  137. @machine = StateMachine::Machine.new(@model, :initial => lambda {|object| :parked})
  138. @machine.state :parked
  139. end
  140. def test_should_set_initial_state_on_created_object
  141. record = @model.new
  142. assert_equal 'parked', record.state
  143. end
  144. def test_should_still_set_attributes
  145. record = @model.new(:value => 1)
  146. assert_equal 1, record.value
  147. end
  148. def test_should_still_allow_initialize_blocks
  149. block_args = nil
  150. record = @model.new do |*args|
  151. block_args = args
  152. end
  153. assert_equal [record], block_args
  154. end
  155. def test_should_set_initial_state_after_setting_attributes
  156. @model.class_eval do
  157. attr_accessor :state_during_setter
  158. define_method(:value=) do |value|
  159. self.state_during_setter = state || 'nil'
  160. end
  161. end
  162. record = @model.new(:value => 1)
  163. assert_equal 'nil', record.state_during_setter
  164. end
  165. def test_should_not_set_initial_state_after_already_initialized
  166. record = @model.new(:value => 1)
  167. assert_equal 'parked', record.state
  168. record.state = 'idling'
  169. record.process({})
  170. assert_equal 'idling', record.state
  171. end
  172. def test_should_use_stored_values_when_loading_from_database
  173. @machine.state :idling
  174. record = @model.find(@model.create(:state => 'idling').id)
  175. assert_equal 'idling', record.state
  176. end
  177. def test_should_use_stored_values_when_loading_from_database_with_nil_state
  178. @machine.state nil
  179. record = @model.find(@model.create(:state => nil).id)
  180. assert_nil record.state
  181. end
  182. end
  183. class MachineWithEventsTest < BaseTestCase
  184. def setup
  185. @model = new_model
  186. @machine = StateMachine::Machine.new(@model)
  187. @machine.event :shift_up
  188. end
  189. def test_should_humanize_name
  190. assert_equal 'shift up', @machine.event(:shift_up).human_name
  191. end
  192. end
  193. class MachineWithColumnDefaultTest < BaseTestCase
  194. def setup
  195. @model = new_model do
  196. field :status, :type => String, :default => 'idling'
  197. end
  198. @machine = StateMachine::Machine.new(@model, :status, :initial => :parked)
  199. @record = @model.new
  200. end
  201. def test_should_use_machine_default
  202. assert_equal 'parked', @record.status
  203. end
  204. end
  205. class MachineWithConflictingPredicateTest < BaseTestCase
  206. def setup
  207. @model = new_model do
  208. def state?(*args)
  209. true
  210. end
  211. end
  212. @machine = StateMachine::Machine.new(@model)
  213. @record = @model.new
  214. end
  215. def test_should_not_define_attribute_predicate
  216. assert @record.state?
  217. end
  218. end
  219. class MachineWithColumnStateAttributeTest < BaseTestCase
  220. def setup
  221. @model = new_model
  222. @machine = StateMachine::Machine.new(@model, :initial => :parked)
  223. @machine.other_states(:idling)
  224. @record = @model.new
  225. end
  226. def test_should_not_override_the_column_reader
  227. @record[:state] = 'parked'
  228. assert_equal 'parked', @record.state
  229. end
  230. def test_should_not_override_the_column_writer
  231. @record.state = 'parked'
  232. assert_equal 'parked', @record[:state]
  233. end
  234. def test_should_have_an_attribute_predicate
  235. assert @record.respond_to?(:state?)
  236. end
  237. def test_should_test_for_existence_on_predicate_without_parameters
  238. assert @record.state?
  239. @record.state = nil
  240. assert !@record.state?
  241. end
  242. def test_should_return_false_for_predicate_if_does_not_match_current_value
  243. assert !@record.state?(:idling)
  244. end
  245. def test_should_return_true_for_predicate_if_matches_current_value
  246. assert @record.state?(:parked)
  247. end
  248. def test_should_raise_exception_for_predicate_if_invalid_state_specified
  249. assert_raise(IndexError) { @record.state?(:invalid) }
  250. end
  251. end
  252. class MachineWithNonColumnStateAttributeUndefinedTest < BaseTestCase
  253. def setup
  254. @model = new_model do
  255. def initialize
  256. # Skip attribute initialization
  257. @initialized_state_machines = true
  258. super
  259. end
  260. end
  261. @machine = StateMachine::Machine.new(@model, :status, :initial => :parked)
  262. @machine.other_states(:idling)
  263. @record = @model.new
  264. end
  265. def test_should_define_a_new_key_for_the_attribute
  266. assert_not_nil @model.fields['status']
  267. end
  268. def test_should_define_a_reader_attribute_for_the_attribute
  269. assert @record.respond_to?(:status)
  270. end
  271. def test_should_define_a_writer_attribute_for_the_attribute
  272. assert @record.respond_to?(:status=)
  273. end
  274. def test_should_define_an_attribute_predicate
  275. assert @record.respond_to?(:status?)
  276. end
  277. end
  278. class MachineWithNonColumnStateAttributeDefinedTest < BaseTestCase
  279. def setup
  280. @model = new_model do
  281. def status
  282. self['status']
  283. end
  284. def status=(value)
  285. self['status'] = value
  286. end
  287. end
  288. @machine = StateMachine::Machine.new(@model, :status, :initial => :parked)
  289. @machine.other_states(:idling)
  290. @record = @model.new
  291. end
  292. def test_should_return_false_for_predicate_if_does_not_match_current_value
  293. assert !@record.status?(:idling)
  294. end
  295. def test_should_return_true_for_predicate_if_matches_current_value
  296. assert @record.status?(:parked)
  297. end
  298. def test_should_raise_exception_for_predicate_if_invalid_state_specified
  299. assert_raise(IndexError) { @record.status?(:invalid) }
  300. end
  301. def test_should_set_initial_state_on_created_object
  302. assert_equal 'parked', @record.status
  303. end
  304. end
  305. class MachineWithAliasedAttributeTest < BaseTestCase
  306. def setup
  307. @model = new_model do
  308. alias_attribute :vehicle_status, :state
  309. end
  310. @machine = StateMachine::Machine.new(@model, :status, :attribute => :vehicle_status)
  311. @machine.state :parked
  312. @record = @model.new
  313. end
  314. def test_should_check_custom_attribute_for_predicate
  315. @record.vehicle_status = nil
  316. assert !@record.status?(:parked)
  317. @record.vehicle_status = 'parked'
  318. assert @record.status?(:parked)
  319. end
  320. end
  321. class MachineWithInitializedStateTest < BaseTestCase
  322. def setup
  323. @model = new_model
  324. @machine = StateMachine::Machine.new(@model, :initial => :parked)
  325. @machine.state :idling
  326. end
  327. def test_should_allow_nil_initial_state_when_static
  328. @machine.state nil
  329. record = @model.new(:state => nil)
  330. assert_nil record.state
  331. end
  332. def test_should_allow_nil_initial_state_when_dynamic
  333. @machine.state nil
  334. @machine.initial_state = lambda {:parked}
  335. record = @model.new(:state => nil)
  336. assert_nil record.state
  337. end
  338. def test_should_allow_different_initial_state_when_static
  339. record = @model.new(:state => 'idling')
  340. assert_equal 'idling', record.state
  341. end
  342. def test_should_allow_different_initial_state_when_dynamic
  343. @machine.initial_state = lambda {:parked}
  344. record = @model.new(:state => 'idling')
  345. assert_equal 'idling', record.state
  346. end
  347. def test_should_use_default_state_if_protected
  348. @model.class_eval do
  349. attr_protected :state
  350. end
  351. record = @model.new(:state => 'idling')
  352. assert_equal 'parked', record.state
  353. end
  354. end
  355. class MachineMultipleTest < BaseTestCase
  356. def setup
  357. @model = new_model do
  358. key :status, String, :default => 'idling'
  359. end
  360. @state_machine = StateMachine::Machine.new(@model, :initial => :parked)
  361. @status_machine = StateMachine::Machine.new(@model, :status, :initial => :idling)
  362. end
  363. def test_should_should_initialize_each_state
  364. record = @model.new
  365. assert_equal 'parked', record.state
  366. assert_equal 'idling', record.status
  367. end
  368. end
  369. class MachineWithLoopbackTest < BaseTestCase
  370. def setup
  371. @model = new_model do
  372. field :updated_at, :type => Time
  373. before_update do |record|
  374. record.updated_at = Time.now
  375. end
  376. end
  377. @machine = StateMachine::Machine.new(@model, :initial => :parked)
  378. @machine.event :park
  379. @record = @model.create(:updated_at => Time.now - 1)
  380. @transition = StateMachine::Transition.new(@record, @machine, :park, :parked, :parked)
  381. @timestamp = @record.updated_at
  382. @transition.perform
  383. end
  384. def test_should_update_record
  385. assert_not_equal @timestamp, @record.updated_at
  386. end
  387. end
  388. class MachineWithDirtyAttributesTest < BaseTestCase
  389. def setup
  390. @model = new_model
  391. @machine = StateMachine::Machine.new(@model, :initial => :parked)
  392. @machine.event :ignite
  393. @machine.state :idling
  394. @record = @model.create
  395. @transition = StateMachine::Transition.new(@record, @machine, :ignite, :parked, :idling)
  396. @transition.perform(false)
  397. end
  398. def test_should_include_state_in_changed_attributes
  399. assert_equal %w(state), @record.changed
  400. end
  401. def test_should_track_attribute_change
  402. assert_equal %w(parked idling), @record.changes['state']
  403. end
  404. def test_should_not_reset_changes_on_multiple_transitions
  405. transition = StateMachine::Transition.new(@record, @machine, :ignite, :idling, :idling)
  406. transition.perform(false)
  407. assert_equal %w(parked idling), @record.changes['state']
  408. end
  409. def test_should_not_have_changes_when_loaded_from_database
  410. record = @model.find(@record.id)
  411. assert !record.changed?
  412. end
  413. end
  414. class MachineWithDirtyAttributesDuringLoopbackTest < BaseTestCase
  415. def setup
  416. @model = new_model
  417. @machine = StateMachine::Machine.new(@model, :initial => :parked)
  418. @machine.event :park
  419. @record = @model.create
  420. @transition = StateMachine::Transition.new(@record, @machine, :park, :parked, :parked)
  421. @transition.perform(false)
  422. end
  423. def test_should_include_state_in_changed_attributes
  424. assert_equal %w(state), @record.changed
  425. end
  426. def test_should_track_attribute_changes
  427. assert_equal %w(parked parked), @record.changes['state']
  428. end
  429. end
  430. class MachineWithDirtyAttributesAndCustomAttributeTest < BaseTestCase
  431. def setup
  432. @model = new_model do
  433. key :status, String, :default => 'idling'
  434. end
  435. @machine = StateMachine::Machine.new(@model, :status, :initial => :parked)
  436. @machine.event :ignite
  437. @machine.state :idling
  438. @record = @model.create
  439. @transition = StateMachine::Transition.new(@record, @machine, :ignite, :parked, :idling)
  440. @transition.perform(false)
  441. end
  442. def test_should_include_state_in_changed_attributes
  443. assert_equal %w(status), @record.changed
  444. end
  445. def test_should_track_attribute_change
  446. assert_equal %w(parked idling), @record.changes['status']
  447. end
  448. def test_should_not_reset_changes_on_multiple_transitions
  449. transition = StateMachine::Transition.new(@record, @machine, :ignite, :idling, :idling)
  450. transition.perform(false)
  451. assert_equal %w(parked idling), @record.changes['status']
  452. end
  453. end
  454. class MachineWithDirtyAttributeAndCustomAttributesDuringLoopbackTest < BaseTestCase
  455. def setup
  456. @model = new_model do
  457. key :status, String, :default => 'idling'
  458. end
  459. @machine = StateMachine::Machine.new(@model, :status, :initial => :parked)
  460. @machine.event :park
  461. @record = @model.create
  462. @transition = StateMachine::Transition.new(@record, @machine, :park, :parked, :parked)
  463. @transition.perform(false)
  464. end
  465. def test_should_include_state_in_changed_attributes
  466. assert_equal %w(status), @record.changed
  467. end
  468. def test_should_track_attribute_changes
  469. assert_equal %w(parked parked), @record.changes['status']
  470. end
  471. end
  472. class MachineWithDirtyAttributeAndStateEventsTest < BaseTestCase
  473. def setup
  474. @model = new_model
  475. @machine = StateMachine::Machine.new(@model, :initial => :parked)
  476. @machine.event :ignite
  477. @record = @model.create
  478. @record.state_event = 'ignite'
  479. end
  480. def test_should_include_state_in_changed_attributes
  481. assert_equal %w(state), @record.changed
  482. end
  483. def test_should_track_attribute_change
  484. assert_equal %w(parked parked), @record.changes['state']
  485. end
  486. def test_should_not_reset_changes_on_multiple_changes
  487. @record.state_event = 'ignite'
  488. assert_equal %w(parked parked), @record.changes['state']
  489. end
  490. def test_should_not_include_state_in_changed_attributes_if_nil
  491. @record = @model.create
  492. @record.state_event = nil
  493. assert_equal [], @record.changed
  494. end
  495. end
  496. class MachineWithCallbacksTest < BaseTestCase
  497. def setup
  498. @model = new_model
  499. @machine = StateMachine::Machine.new(@model, :initial => :parked)
  500. @machine.other_states :idling
  501. @machine.event :ignite
  502. @record = @model.new(:state => 'parked')
  503. @transition = StateMachine::Transition.new(@record, @machine, :ignite, :parked, :idling)
  504. end
  505. def test_should_run_before_callbacks
  506. called = false
  507. @machine.before_transition {called = true}
  508. @transition.perform
  509. assert called
  510. end
  511. def test_should_pass_record_to_before_callbacks_with_one_argument
  512. record = nil
  513. @machine.before_transition {|arg| record = arg}
  514. @transition.perform
  515. assert_equal @record, record
  516. end
  517. def test_should_pass_record_and_transition_to_before_callbacks_with_multiple_arguments
  518. callback_args = nil
  519. @machine.before_transition {|*args| callback_args = args}
  520. @transition.perform
  521. assert_equal [@record, @transition], callback_args
  522. end
  523. def test_should_run_before_callbacks_outside_the_context_of_the_record
  524. context = nil
  525. @machine.before_transition {context = self}
  526. @transition.perform
  527. assert_equal self, context
  528. end
  529. def test_should_run_after_callbacks
  530. called = false
  531. @machine.after_transition {called = true}
  532. @transition.perform
  533. assert called
  534. end
  535. def test_should_pass_record_to_after_callbacks_with_one_argument
  536. record = nil
  537. @machine.after_transition {|arg| record = arg}
  538. @transition.perform
  539. assert_equal @record, record
  540. end
  541. def test_should_pass_record_and_transition_to_after_callbacks_with_multiple_arguments
  542. callback_args = nil
  543. @machine.after_transition {|*args| callback_args = args}
  544. @transition.perform
  545. assert_equal [@record, @transition], callback_args
  546. end
  547. def test_should_run_after_callbacks_outside_the_context_of_the_record
  548. context = nil
  549. @machine.after_transition {context = self}
  550. @transition.perform
  551. assert_equal self, context
  552. end
  553. def test_should_run_around_callbacks
  554. before_called = false
  555. after_called = false
  556. @machine.around_transition {|block| before_called = true; block.call; after_called = true}
  557. @transition.perform
  558. assert before_called
  559. assert after_called
  560. end
  561. def test_should_include_transition_states_in_known_states
  562. @machine.before_transition :to => :first_gear, :do => lambda {}
  563. assert_equal [:parked, :idling, :first_gear], @machine.states.map {|state| state.name}
  564. end
  565. def test_should_allow_symbolic_callbacks
  566. callback_args = nil
  567. klass = class << @record; self; end
  568. klass.send(:define_method, :after_ignite) do |*args|
  569. callback_args = args
  570. end
  571. @machine.before_transition(:after_ignite)
  572. @transition.perform
  573. assert_equal [@transition], callback_args
  574. end
  575. def test_should_allow_string_callbacks
  576. class << @record
  577. attr_reader :callback_result
  578. end
  579. @machine.before_transition('@callback_result = [1, 2, 3]')
  580. @transition.perform
  581. assert_equal [1, 2, 3], @record.callback_result
  582. end
  583. end
  584. class MachineWithFailedBeforeCallbacksTest < BaseTestCase
  585. def setup
  586. @callbacks = []
  587. @model = new_model
  588. @machine = StateMachine::Machine.new(@model)
  589. @machine.state :parked, :idling
  590. @machine.event :ignite
  591. @machine.before_transition {@callbacks << :before_1; false}
  592. @machine.before_transition {@callbacks << :before_2}
  593. @machine.after_transition {@callbacks << :after}
  594. @machine.around_transition {|block| @callbacks << :around_before; block.call; @callbacks << :around_after}
  595. @record = @model.new(:state => 'parked')
  596. @transition = StateMachine::Transition.new(@record, @machine, :ignite, :parked, :idling)
  597. @result = @transition.perform
  598. end
  599. def test_should_not_be_successful
  600. assert !@result
  601. end
  602. def test_should_not_change_current_state
  603. assert_equal 'parked', @record.state
  604. end
  605. def test_should_not_run_action
  606. assert @record.new_record?
  607. end
  608. def test_should_not_run_further_callbacks
  609. assert_equal [:before_1], @callbacks
  610. end
  611. end
  612. class MachineWithFailedActionTest < BaseTestCase
  613. def setup
  614. @model = new_model do
  615. validates_numericality_of :state
  616. end
  617. @machine = StateMachine::Machine.new(@model)
  618. @machine.state :parked, :idling
  619. @machine.event :ignite
  620. @callbacks = []
  621. @machine.before_transition {@callbacks << :before}
  622. @machine.after_transition {@callbacks << :after}
  623. @machine.after_failure {@callbacks << :after_failure}
  624. @machine.around_transition {|block| @callbacks << :around_before; block.call; @callbacks << :around_after}
  625. @record = @model.new(:state => 'parked')
  626. @transition = StateMachine::Transition.new(@record, @machine, :ignite, :parked, :idling)
  627. @result = @transition.perform
  628. end
  629. def test_should_not_be_successful
  630. assert !@result
  631. end
  632. def test_should_not_change_current_state
  633. assert_equal 'parked', @record.state
  634. end
  635. def test_should_not_save_record
  636. assert @record.new_record?
  637. end
  638. def test_should_run_before_callbacks_and_after_callbacks_with_failures
  639. assert_equal [:before, :around_before, :after_failure], @callbacks
  640. end
  641. end
  642. class MachineWithFailedAfterCallbacksTest < BaseTestCase
  643. def setup
  644. @callbacks = []
  645. @model = new_model
  646. @machine = StateMachine::Machine.new(@model)
  647. @machine.state :parked, :idling
  648. @machine.event :ignite
  649. @machine.after_transition {@callbacks << :after_1; false}
  650. @machine.after_transition {@callbacks << :after_2}
  651. @machine.around_transition {|block| @callbacks << :around_before; block.call; @callbacks << :around_after}
  652. @record = @model.new(:state => 'parked')
  653. @transition = StateMachine::Transition.new(@record, @machine, :ignite, :parked, :idling)
  654. @result = @transition.perform
  655. end
  656. def test_should_be_successful
  657. assert @result
  658. end
  659. def test_should_change_current_state
  660. assert_equal 'idling', @record.state
  661. end
  662. def test_should_save_record
  663. assert !@record.new_record?
  664. end
  665. def test_should_not_run_further_after_callbacks
  666. assert_equal [:around_before, :around_after, :after_1], @callbacks
  667. end
  668. end
  669. class MachineWithValidationsTest < BaseTestCase
  670. def setup
  671. @model = new_model
  672. @machine = StateMachine::Machine.new(@model)
  673. @machine.state :parked
  674. @record = @model.new
  675. end
  676. def test_should_invalidate_using_errors
  677. I18n.backend = I18n::Backend::Simple.new if Object.const_defined?(:ActiveModel)
  678. @record.state = 'parked'
  679. @machine.invalidate(@record, :state, :invalid_transition, [[:event, 'park']])
  680. assert_equal ['State cannot transition via "park"'], @record.errors.full_messages
  681. end
  682. def test_should_auto_prefix_custom_attributes_on_invalidation
  683. @machine.invalidate(@record, :event, :invalid)
  684. assert_equal ['State event is invalid'], @record.errors.full_messages
  685. end
  686. def test_should_clear_errors_on_reset
  687. @record.state = 'parked'
  688. @record.errors.add(:state, 'is invalid')
  689. @machine.reset(@record)
  690. assert_equal [], @record.errors.full_messages
  691. end
  692. def test_should_be_valid_if_state_is_known
  693. @record.state = 'parked'
  694. assert @record.valid?
  695. end
  696. def test_should_not_be_valid_if_state_is_unknown
  697. @record.state = 'invalid'
  698. assert !@record.valid?
  699. assert_equal ['State is invalid'], @record.errors.full_messages
  700. end
  701. end
  702. class MachineWithValidationsAndCustomAttributeTest < BaseTestCase
  703. def setup
  704. @model = new_model do
  705. alias_attribute :status, :state
  706. end
  707. @machine = StateMachine::Machine.new(@model, :status, :attribute => :state)
  708. @machine.state :parked
  709. @record = @model.new
  710. end
  711. def test_should_add_validation_errors_to_custom_attribute
  712. @record.state = 'invalid'
  713. assert !@record.valid?
  714. assert_equal ['State is invalid'], @record.errors.full_messages
  715. @record.state = 'parked'
  716. assert @record.valid?
  717. end
  718. end
  719. class MachineWithStateDrivenValidationsTest < BaseTestCase
  720. def setup
  721. @model = new_model do
  722. attr_accessor :seatbealt
  723. end
  724. @machine = StateMachine::Machine.new(@model)
  725. @machine.state :first_gear do
  726. validates_presence_of :seatbelt, :key => :first_gear
  727. end
  728. @machine.state :second_gear do
  729. validates_presence_of :seatbelt, :key => :second_gear
  730. end
  731. @machine.other_states :parked
  732. end
  733. def test_should_be_valid_if_validation_fails_outside_state_scope
  734. record = @model.new(:state => 'parked', :seatbelt => nil)
  735. assert record.valid?
  736. end
  737. def test_should_be_invalid_if_validation_fails_within_state_scope
  738. record = @model.new(:state => 'first_gear', :seatbelt => nil)
  739. assert !record.valid?
  740. end
  741. def test_should_be_valid_if_validation_succeeds_within_state_scope
  742. record = @model.new(:state => 'second_gear', :seatbelt => true)
  743. assert record.valid?
  744. end
  745. end
  746. class MachineWithEventAttributesOnValidationTest < BaseTestCase
  747. def setup
  748. @model = new_model
  749. @machine = StateMachine::Machine.new(@model)
  750. @machine.event :ignite do
  751. transition :parked => :idling
  752. end
  753. @record = @model.new
  754. @record.state = 'parked'
  755. @record.state_event = 'ignite'
  756. end
  757. def test_should_fail_if_event_is_invalid
  758. @record.state_event = 'invalid'
  759. assert !@record.valid?
  760. assert_equal ['State event is invalid'], @record.errors.full_messages
  761. end
  762. def test_should_fail_if_event_has_no_transition
  763. @record.state = 'idling'
  764. assert !@record.valid?
  765. assert_equal ['State event cannot transition when idling'], @record.errors.full_messages
  766. end
  767. def test_should_be_successful_if_event_has_transition
  768. assert @record.valid?
  769. end
  770. def test_should_run_before_callbacks
  771. ran_callback = false
  772. @machine.before_transition { ran_callback = true }
  773. @record.valid?
  774. assert ran_callback
  775. end
  776. def test_should_run_around_callbacks_before_yield
  777. ran_callback = false
  778. @machine.around_transition {|block| ran_callback = true; block.call }
  779. @record.valid?
  780. assert ran_callback
  781. end
  782. def test_should_persist_new_state
  783. @record.valid?
  784. assert_equal 'idling', @record.state
  785. end
  786. def test_should_not_run_after_callbacks
  787. ran_callback = false
  788. @machine.after_transition { ran_callback = true }
  789. @record.valid?
  790. assert !ran_callback
  791. end
  792. def test_should_not_run_after_callbacks_with_failures_disabled_if_validation_fails
  793. @model.class_eval do
  794. attr_accessor :seatbelt
  795. validates_presence_of :seatbelt
  796. end
  797. ran_callback = false
  798. @machine.after_transition { ran_callback = true }
  799. @record.valid?
  800. assert !ran_callback
  801. end
  802. def test_should_not_run_around_callbacks_after_yield
  803. ran_callback = false
  804. @machine.around_transition {|block| block.call; ran_callback = true }
  805. @record.valid?
  806. assert !ran_callback
  807. end
  808. def test_should_not_run_around_callbacks_after_yield_with_failures_disabled_if_validation_fails
  809. @model.class_eval do
  810. attr_accessor :seatbelt
  811. validates_presence_of :seatbelt
  812. end
  813. ran_callback = false
  814. @machine.around_transition {|block| block.call; ran_callback = true }
  815. @record.valid?
  816. assert !ran_callback
  817. end
  818. def test_should_run_failure_callbacks_if_validation_fails
  819. @model.class_eval do
  820. attr_accessor :seatbelt
  821. validates_presence_of :seatbelt
  822. end
  823. ran_callback = false
  824. @machine.after_failure { ran_callback = true }
  825. @record.valid?
  826. assert ran_callback
  827. end
  828. end
  829. class MachineWithEventAttributesOnSaveTest < BaseTestCase
  830. def setup
  831. @model = new_model
  832. @machine = StateMachine::Machine.new(@model)
  833. @machine.event :ignite do
  834. transition :parked => :idling
  835. end
  836. @record = @model.new
  837. @record.state = 'parked'
  838. @record.state_event = 'ignite'
  839. end
  840. def test_should_fail_if_event_is_invalid
  841. @record.state_event = 'invalid'
  842. assert_equal false, @record.save
  843. end
  844. def test_should_fail_if_event_has_no_transition
  845. @record.state = 'idling'
  846. assert_equal false, @record.save
  847. end
  848. def test_should_be_successful_if_event_has_transition
  849. assert_equal true, @record.save
  850. end
  851. def test_should_run_before_callbacks
  852. ran_callback = false
  853. @machine.before_transition { ran_callback = true }
  854. @record.save
  855. assert ran_callback
  856. end
  857. def test_should_run_before_callbacks_once
  858. before_count = 0
  859. @machine.before_transition { before_count += 1 }
  860. @record.save
  861. assert_equal 1, before_count
  862. end
  863. def test_should_run_around_callbacks_before_yield
  864. ran_callback = false
  865. @machine.around_transition {|block| ran_callback = true; block.call }
  866. @record.save
  867. assert ran_callback
  868. end
  869. def test_should_run_around_callbacks_before_yield_once
  870. around_before_count = 0
  871. @machine.around_transition {|block| around_before_count += 1; block.call }
  872. @record.save
  873. assert_equal 1, around_before_count
  874. end
  875. def test_should_persist_new_state
  876. @record.save
  877. assert_equal 'idling', @record.state
  878. end
  879. def test_should_run_after_callbacks
  880. ran_callback = false
  881. @machine.after_transition { ran_callback = true }
  882. @record.save
  883. assert ran_callback
  884. end
  885. def test_should_not_run_after_callbacks_with_failures_disabled_if_fails
  886. @model.class_eval do
  887. validates_numericality_of :state
  888. end
  889. ran_callback = false
  890. @machine.after_transition { ran_callback = true }
  891. begin; @record.save; rescue; end
  892. assert !ran_callback
  893. end
  894. def test_should_run_failure_callbacks__if_fails
  895. @model.class_eval do
  896. validates_numericality_of :state
  897. end
  898. ran_callback = false
  899. @machine.after_failure { ran_callback = true }
  900. begin; @record.save; rescue; end
  901. assert ran_callback
  902. end
  903. def test_should_not_run_around_callbacks_with_failures_disabled_if_fails
  904. @model.class_eval do
  905. validates_numericality_of :state
  906. end
  907. ran_callback = false
  908. @machine.around_transition {|block| block.call; ran_callback = true }
  909. begin; @record.save; rescue; end
  910. assert !ran_callback
  911. end
  912. def test_should_run_around_callbacks_after_yield
  913. ran_callback = false
  914. @machine.around_transition {|block| block.call; ran_callback = true }
  915. @record.save
  916. assert ran_callback
  917. end
  918. end
  919. class MachineWithEventAttributesOnSaveBangTest < BaseTestCase
  920. def setup
  921. @model = new_model
  922. @machine = StateMachine::Machine.new(@model)
  923. @machine.event :ignite do
  924. transition :parked => :idling
  925. end
  926. @record = @model.new
  927. @record.state = 'parked'
  928. @record.state_event = 'ignite'
  929. end
  930. def test_should_fail_if_event_is_invalid
  931. @record.state_event = 'invalid'
  932. assert_raise(Mongoid::Errors::Validations) { @record.save! }
  933. end
  934. def test_should_fail_if_event_has_no_transition
  935. @record.state = 'idling'
  936. assert_raise(Mongoid::Errors::Validations) { @record.save! }
  937. end
  938. def test_should_be_successful_if_event_has_transition
  939. assert_equal true, @record.save!
  940. end
  941. def test_should_run_before_callbacks
  942. ran_callback = false
  943. @machine.before_transition { ran_callback = true }
  944. @record.save!
  945. assert ran_callback
  946. end
  947. def test_should_run_before_callbacks_once
  948. before_count = 0
  949. @machine.before_transition { before_count += 1 }
  950. @record.save!
  951. assert_equal 1, before_count
  952. end
  953. def test_should_run_around_callbacks_before_yield
  954. ran_callback = false
  955. @machine.around_transition {|block| ran_callback = true; block.call }
  956. @record.save!
  957. assert ran_callback
  958. end
  959. def test_should_run_around_callbacks_before_yield_once
  960. around_before_count = 0
  961. @machine.around_transition {|block| around_before_count += 1; block.call }
  962. @record.save!
  963. assert_equal 1, around_before_count
  964. end
  965. def test_should_persist_new_state
  966. @record.save!
  967. assert_equal 'idling', @record.state
  968. end
  969. def test_should_persist_new_state
  970. @record.save!
  971. assert_equal 'idling', @record.state
  972. end
  973. def test_should_run_after_callbacks
  974. ran_callback = false
  975. @machine.after_transition { ran_callback = true }
  976. @record.save!
  977. assert ran_callback
  978. end
  979. def test_should_run_around_callbacks_after_yield
  980. ran_callback = false
  981. @machine.around_transition {|block| block.call; ran_callback = true }
  982. @record.save!
  983. assert ran_callback
  984. end
  985. end
  986. class MachineWithEventAttributesOnCustomActionTest < BaseTestCase
  987. def setup
  988. @superclass = new_model do
  989. def persist
  990. upsert
  991. end
  992. end
  993. @model = Class.new(@superclass)
  994. @machine = StateMachine::Machine.new(@model, :action => :persist)
  995. @machine.event :ignite do
  996. transition :parked => :idling
  997. end
  998. @record = @model.new
  999. @record.state = 'parked'
  1000. @record.state_event = 'ignite'
  1001. end
  1002. def test_should_not_transition_on_valid?
  1003. @record.valid?
  1004. assert_equal 'parked', @record.state
  1005. end
  1006. def test_should_not_transition_on_save
  1007. @record.save
  1008. assert_equal 'parked', @record.state
  1009. end
  1010. def test_should_not_transition_on_save!
  1011. @record.save!
  1012. assert_equal 'parked', @record.state
  1013. end
  1014. def test_should_transition_on_custom_action
  1015. @record.persist
  1016. assert_equal 'idling', @record.state
  1017. end
  1018. end
  1019. class MachineWithScopesTest < BaseTestCase
  1020. def setup
  1021. @model = new_model
  1022. @machine = StateMachine::Machine.new(@model)
  1023. @machine.state :parked, :first_gear
  1024. @machine.state :idling, :value => lambda {'idling'}
  1025. end
  1026. def test_should_create_singular_with_scope
  1027. assert @model.respond_to?(:with_state)
  1028. end
  1029. def test_should_only_include_records_with_state_in_singular_with_scope
  1030. parked = @model.create :state => 'parked'
  1031. idling = @model.create :state => 'idling'
  1032. assert_equal [parked], @model.with_state(:parked).to_a
  1033. end
  1034. def test_should_create_plural_with_scope
  1035. assert @model.respond_to?(:with_states)
  1036. end
  1037. def test_should_only_include_records_with_states_in_plural_with_scope
  1038. parked = @model.create :state => 'parked'
  1039. idling = @model.create :state => 'idling'
  1040. assert_equal [parked, idling], @model.with_states(:parked, :idling).to_a
  1041. end
  1042. def test_should_create_singular_without_scope
  1043. assert @model.respond_to?(:without_state)
  1044. end
  1045. def test_should_only_include_records_without_state_in_singular_without_scope
  1046. parked = @model.create :state => 'parked'
  1047. idling = @model.create :state => 'idling'
  1048. assert_equal [parked], @model.without_state(:idling).to_a
  1049. end
  1050. def test_should_create_plural_without_scope
  1051. assert @model.respond_to?(:without_states)
  1052. end
  1053. def test_should_only_include_records_without_states_in_plural_without_scope
  1054. parked = @model.create :state => 'parked'
  1055. idling = @model.create :state => 'idling'
  1056. first_gear = @model.create :state => 'first_gear'
  1057. assert_equal [parked, idling], @model.without_states(:first_gear).to_a
  1058. end
  1059. def test_should_allow_chaining_scopes
  1060. parked = @model.create :state => 'parked'
  1061. idling = @model.create :state => 'idling'
  1062. assert_equal [idling], @model.without_state(:parked).with_state(:idling).all
  1063. end
  1064. end
  1065. class MachineWithScopesAndOwnerSubclassTest < BaseTestCase
  1066. def setup
  1067. @model = new_model
  1068. @machine = StateMachine::Machine.new(@model, :state)
  1069. MongoidTest.const_set('Foo', @model)
  1070. @subclass = Class.new(@model) do
  1071. def self.name
  1072. 'MongoidTest::SubFoo'
  1073. end
  1074. end
  1075. @subclass_machine = @subclass.state_machine(:state) {}
  1076. @subclass_machine.state :parked, :idling, :first_gear
  1077. MongoidTest.const_set('SubFoo', @subclass)
  1078. end
  1079. def test_should_only_include_records_with_subclass_states_in_with_scope
  1080. parked = @subclass.create :state => 'parked'
  1081. idling = @subclass.create :state => 'idling'
  1082. assert_equal [parked, idling], @subclass.with_states(:parked, :idling).to_a
  1083. end
  1084. def test_should_only_include_records_without_subclass_states_in_without_scope
  1085. parked = @subclass.create :state => 'parked'
  1086. idling = @subclass.create :state => 'idling'
  1087. first_gear = @subclass.create :state => 'first_gear'
  1088. assert_equal [parked, idling], @subclass.without_states(:first_gear).to_a
  1089. end
  1090. def teardown
  1091. MongoidTest.send(:remove_const, 'SubFoo')
  1092. MongoidTest.send(:remove_const, 'Foo')
  1093. end
  1094. end
  1095. class MachineWithComplexPluralizationScopesTest < BaseTestCase
  1096. def setup
  1097. @model = new_model
  1098. @machine = StateMachine::Machine.new(@model, :status)
  1099. end
  1100. def test_should_create_singular_with_scope
  1101. assert @model.respond_to?(:with_status)
  1102. end
  1103. def test_should_create_plural_with_scope
  1104. assert @model.respond_to?(:with_statuses)
  1105. end
  1106. end
  1107. class MachineWithDefaultScope < BaseTestCase
  1108. def setup
  1109. @model = new_model
  1110. @machine = StateMachine::Machine.new(@model, :initial => :parked)
  1111. @machine.state :idling
  1112. @model.class_eval do
  1113. default_scope with_state(:parked, :idling)
  1114. end
  1115. end
  1116. def test_should_set_initial_state_on_created_object
  1117. object = @model.new
  1118. assert_equal 'parked', object.state
  1119. end
  1120. end
  1121. class MachineWithInternationalizationTest < BaseTestCase
  1122. def setup
  1123. I18n.backend = I18n::Backend::Simple.new
  1124. # Initialize the backend
  1125. StateMachine::Machine.new(new_model)
  1126. I18n.backend.translate(:en, 'mongoid.errors.messages.invalid_transition', :event => 'ignite', :value => 'idling')
  1127. @model = new_model
  1128. end
  1129. def test_should_use_defaults
  1130. I18n.backend.store_translations(:en, {
  1131. :mongoid => {:errors => {:messages => {:invalid_transition => 'cannot %{event}'}}}
  1132. })
  1133. machine = StateMachine::Machine.new(@model)
  1134. machine.state :parked, :idling
  1135. machine.event :ignite
  1136. record = @model.new(:state => 'idling')
  1137. machine.invalidate(record, :state, :invalid_transition, [[:event, 'ignite']])
  1138. assert_equal ['State cannot ignite'], record.errors.full_messages
  1139. end
  1140. def test_should_allow_customized_error_key
  1141. I18n.backend.store_translations(:en, {
  1142. :mongoid => {:errors => {:messages => {:bad_transition => 'cannot %{event}'}}}
  1143. })
  1144. machine = StateMachine::Machine.new(@model, :messages => {:invalid_transition => :bad_transition})
  1145. machine.state :parked, :idling
  1146. record = @model.new(:state => 'idling')
  1147. machine.invalidate(record, :state, :invalid_transition, [[:event, 'ignite']])
  1148. assert_equal ['State cannot ignite'], record.errors.full_messages
  1149. end
  1150. def test_should_allow_customized_error_string
  1151. machine = StateMachine::Machine.new(@model, :messages => {:invalid_transition => 'cannot %{event}'})
  1152. machine.state :parked, :idling
  1153. record = @model.new(:state => 'idling')
  1154. machine.invalidate(record, :state, :invalid_transition, [[:event, 'ignite']])
  1155. assert_equal ['State cannot ignite'], record.errors.full_messages
  1156. end
  1157. def test_should_allow_customized_state_key_scoped_to_class_and_machine
  1158. I18n.backend.store_translations(:en, {
  1159. :mongoid => {:state_machines => {:'mongoid_test/foo' => {:state => {:states => {:parked => 'shutdown'}}}}}
  1160. })
  1161. machine = StateMachine::Machine.new(@model)
  1162. machine.state :parked
  1163. assert_equal 'shutdown', machine.state(:parked).human_name
  1164. end
  1165. def test_should_allow_customized_state_key_scoped_to_machine
  1166. I18n.backend.store_translations(:en, {
  1167. :mongoid => {:state_machines => {:state => {:states => {:parked => 'shutdown'}}}}
  1168. })
  1169. machine = StateMachine::Machine.new(@model)
  1170. machine.state :parked
  1171. assert_equal 'shutdown', machine.state(:parked).human_name
  1172. end
  1173. def test_should_allow_customized_state_key_unscoped
  1174. I18n.backend.store_translations(:en, {
  1175. :mongoid => {:state_machines => {:states => {:parked => 'shutdown'}}}
  1176. })
  1177. machine = StateMachine::Machine.new(@model)
  1178. machine.state :parked
  1179. assert_equal 'shutdown', machine.state(:parked).human_name
  1180. end
  1181. def test_should_allow_customized_event_key_scoped_to_class_and_machine
  1182. I18n.backend.store_translations(:en, {
  1183. :mongoid => {:state_machines => {:'mongoid_test/foo' => {:state => {:events => {:park => 'stop'}}}}}
  1184. })
  1185. machine = StateMachine::Machine.new(@model)
  1186. machine.event :park
  1187. assert_equal 'stop', machine.event(:park).human_name
  1188. end
  1189. def test_should_allow_customized_event_key_scoped_to_machine
  1190. I18n.backend.store_translations(:en, {
  1191. :mongoid => {:state_machines => {:state => {:events => {:park => 'stop'}}}}
  1192. })
  1193. machine = StateMachine::Machine.new(@model)
  1194. machine.event :park
  1195. assert_equal 'stop', machine.event(:park).human_name
  1196. end
  1197. def test_should_allow_customized_event_key_unscoped
  1198. I18n.backend.store_translations(:en, {
  1199. :mongoid => {:state_machines => {:events => {:park => 'stop'}}}
  1200. })
  1201. machine = StateMachine::Machine.new(@model)
  1202. machine.event :park
  1203. assert_equal 'stop', machine.event(:park).human_name
  1204. end
  1205. def test_should_only_add_locale_once_in_load_path
  1206. assert_equal 1, I18n.load_path.select {|path| path =~ %r{mongoid/locale\.rb$}}.length
  1207. # Create another Mongoid model that will triger the i18n feature
  1208. new_model
  1209. assert_equal 1, I18n.load_path.select {|path| path =~ %r{mongoid/locale\.rb$}}.length
  1210. end
  1211. def test_should_add_locale_to_beginning_of_load_path
  1212. @original_load_path = I18n.load_path
  1213. I18n.backend = I18n::Backend::Simple.new
  1214. app_locale = File.dirname(__FILE__) + '/../../files/en.yml'
  1215. default_locale = File.dirname(__FILE__) + '/../../../lib/state_machine/integrations/mongoid/locale.rb'
  1216. I18n.load_path = [app_locale]
  1217. StateMachine::Machine.new(@model)
  1218. assert_equal [default_locale, app_locale].map {|path| File.expand_path(path)}, I18n.load_path.map {|path| File.expand_path(path)}
  1219. ensure
  1220. I18n.load_path = @original_load_path
  1221. end
  1222. def test_should_prefer_other_locales_first
  1223. @original_load_path = I18n.load_path
  1224. I18n.backend = I18n::Backend::Simple.new
  1225. I18n.load_path = [File.dirname(__FILE__) + '/../../files/en.yml']
  1226. machine = StateMachine::Machine.new(@model)
  1227. machine.state :parked, :idling
  1228. machine.event :ignite
  1229. record = @model.new(:state => 'idling')
  1230. machine.invalidate(record, :state, :invalid_transition, [[:event, 'ignite']])
  1231. assert_equal ['State cannot transition'], record.errors.full_messages
  1232. ensure
  1233. I18n.load_path = @original_load_path
  1234. end
  1235. end
  1236. end