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

/test/unit/integrations/data_mapper_test.rb

https://github.com/johnsonzes/state_machine
Ruby | 1909 lines | 1499 code | 401 blank | 9 comment | 24 complexity | a433209610c25900fbf23069a3b1c319 MD5 | raw file

Large files files are truncated, but you can click here to view the full file

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

Large files files are truncated, but you can click here to view the full file