PageRenderTime 56ms CodeModel.GetById 19ms RepoModel.GetById 1ms app.codeStats 0ms

/test/unit/integrations/mongo_mapper_test.rb

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