aboutsummaryrefslogtreecommitdiffstats
path: root/activemodel/test/cases/state_machine_test.rb
diff options
context:
space:
mode:
Diffstat (limited to 'activemodel/test/cases/state_machine_test.rb')
-rw-r--r--activemodel/test/cases/state_machine_test.rb312
1 files changed, 312 insertions, 0 deletions
diff --git a/activemodel/test/cases/state_machine_test.rb b/activemodel/test/cases/state_machine_test.rb
new file mode 100644
index 0000000000..f66299741e
--- /dev/null
+++ b/activemodel/test/cases/state_machine_test.rb
@@ -0,0 +1,312 @@
+require 'cases/helper'
+
+class StateMachineSubject
+ include ActiveModel::StateMachine
+
+ state_machine do
+ state :open, :exit => :exit
+ state :closed, :enter => :enter
+
+ event :close, :success => :success_callback do
+ transitions :to => :closed, :from => [:open]
+ end
+
+ event :null do
+ transitions :to => :closed, :from => [:open], :guard => :always_false
+ end
+ end
+
+ state_machine :bar do
+ state :read
+ state :ended
+
+ event :foo do
+ transitions :to => :ended, :from => [:read]
+ end
+ end
+
+ def always_false
+ false
+ end
+
+ def success_callback
+ end
+
+ def enter
+ end
+ def exit
+ end
+end
+
+class StateMachineSubjectSubclass < StateMachineSubject
+end
+
+class StateMachineClassLevelTest < ActiveModel::TestCase
+ test 'defines a class level #state_machine method on its including class' do
+ assert StateMachineSubject.respond_to?(:state_machine)
+ end
+
+ test 'defines a class level #state_machines method on its including class' do
+ assert StateMachineSubject.respond_to?(:state_machines)
+ end
+
+ test 'class level #state_machine returns machine instance' do
+ assert_kind_of ActiveModel::StateMachine::Machine, StateMachineSubject.state_machine
+ end
+
+ test 'class level #state_machine returns machine instance with given name' do
+ assert_kind_of ActiveModel::StateMachine::Machine, StateMachineSubject.state_machine(:default)
+ end
+
+ test 'class level #state_machines returns hash of machine instances' do
+ assert_kind_of ActiveModel::StateMachine::Machine, StateMachineSubject.state_machines[:default]
+ end
+
+ test "should return a select friendly array of states in the form of [['Friendly name', 'state_name']]" do
+ assert_equal [['Open', 'open'], ['Closed', 'closed']], StateMachineSubject.state_machine.states_for_select
+ end
+end
+
+class StateMachineInstanceLevelTest < ActiveModel::TestCase
+ def setup
+ @foo = StateMachineSubject.new
+ end
+
+ test 'defines an accessor for the current state' do
+ assert @foo.respond_to?(:current_state)
+ end
+
+ test 'defines a state querying instance method on including class' do
+ assert @foo.respond_to?(:open?)
+ end
+
+ test 'defines an event! instance method' do
+ assert @foo.respond_to?(:close!)
+ end
+
+ test 'defines an event instance method' do
+ assert @foo.respond_to?(:close)
+ end
+end
+
+class StateMachineInitialStatesTest < ActiveModel::TestCase
+ def setup
+ @foo = StateMachineSubject.new
+ end
+
+ test 'sets the initial state' do
+ assert_equal :open, @foo.current_state
+ end
+
+ test '#open? should be initially true' do
+ assert @foo.open?
+ end
+
+ test '#closed? should be initially false' do
+ assert !@foo.closed?
+ end
+
+ test 'uses the first state defined if no initial state is given' do
+ assert_equal :read, @foo.current_state(:bar)
+ end
+end
+
+class StateMachineEventFiringWithPersistenceTest < ActiveModel::TestCase
+ def setup
+ @subj = StateMachineSubject.new
+ end
+
+ test 'updates the current state' do
+ @subj.close!
+
+ assert_equal :closed, @subj.current_state
+ end
+
+ test 'fires the Event' do
+ @subj.class.state_machine.events[:close].expects(:fire).with(@subj)
+ @subj.close!
+ end
+
+ test 'calls the success callback if one was provided' do
+ @subj.expects(:success_callback)
+ @subj.close!
+ end
+
+ test 'attempts to persist if write_state is defined' do
+ def @subj.write_state
+ end
+
+ @subj.expects(:write_state)
+ @subj.close!
+ end
+end
+
+class StateMachineEventFiringWithoutPersistence < ActiveModel::TestCase
+ test 'updates the current state' do
+ subj = StateMachineSubject.new
+ assert_equal :open, subj.current_state
+ subj.close
+ assert_equal :closed, subj.current_state
+ end
+
+ test 'fires the Event' do
+ subj = StateMachineSubject.new
+
+ StateMachineSubject.state_machine.events[:close].expects(:fire).with(subj)
+ subj.close
+ end
+
+ test 'attempts to persist if write_state is defined' do
+ subj = StateMachineSubject.new
+
+ def subj.write_state
+ end
+
+ subj.expects(:write_state_without_persistence)
+
+ subj.close
+ end
+end
+
+class StateMachinePersistenceTest < ActiveModel::TestCase
+ test 'reads the state if it has not been set and read_state is defined' do
+ subj = StateMachineSubject.new
+ def subj.read_state
+ end
+
+ subj.expects(:read_state).with(StateMachineSubject.state_machine)
+
+ subj.current_state
+ end
+end
+
+class StateMachineEventCallbacksTest < ActiveModel::TestCase
+ test 'should call aasm_event_fired if defined and successful for bang fire' do
+ subj = StateMachineSubject.new
+ def subj.aasm_event_fired(from, to)
+ end
+
+ subj.expects(:event_fired)
+
+ subj.close!
+ end
+
+ test 'should call aasm_event_fired if defined and successful for non-bang fire' do
+ subj = StateMachineSubject.new
+ def subj.aasm_event_fired(from, to)
+ end
+
+ subj.expects(:event_fired)
+
+ subj.close
+ end
+
+ test 'should call aasm_event_failed if defined and transition failed for bang fire' do
+ subj = StateMachineSubject.new
+ def subj.event_failed(event)
+ end
+
+ subj.expects(:event_failed)
+
+ subj.null!
+ end
+
+ test 'should call aasm_event_failed if defined and transition failed for non-bang fire' do
+ subj = StateMachineSubject.new
+ def subj.aasm_event_failed(event)
+ end
+
+ subj.expects(:event_failed)
+
+ subj.null
+ end
+end
+
+class StateMachineStateActionsTest < ActiveModel::TestCase
+ test "calls enter when entering state" do
+ subj = StateMachineSubject.new
+ subj.expects(:enter)
+ subj.close
+ end
+
+ test "calls exit when exiting state" do
+ subj = StateMachineSubject.new
+ subj.expects(:exit)
+ subj.close
+ end
+end
+
+class StateMachineInheritanceTest < ActiveModel::TestCase
+ test "has the same states as its parent" do
+ assert_equal StateMachineSubject.state_machine.states, StateMachineSubjectSubclass.state_machine.states
+ end
+
+ test "has the same events as its parent" do
+ assert_equal StateMachineSubject.state_machine.events, StateMachineSubjectSubclass.state_machine.events
+ end
+end
+
+class StateMachineSubject
+ state_machine :chetan_patil, :initial => :sleeping do
+ state :sleeping
+ state :showering
+ state :working
+ state :dating
+
+ event :wakeup do
+ transitions :from => :sleeping, :to => [:showering, :working]
+ end
+
+ event :dress do
+ transitions :from => :sleeping, :to => :working, :on_transition => :wear_clothes
+ transitions :from => :showering, :to => [:working, :dating], :on_transition => Proc.new { |obj, *args| obj.wear_clothes(*args) }
+ end
+ end
+
+ def wear_clothes(shirt_color, trouser_type)
+ end
+end
+
+class StateMachineWithComplexTransitionsTest < ActiveModel::TestCase
+ def setup
+ @subj = StateMachineSubject.new
+ end
+
+ test 'transitions to specified next state (sleeping to showering)' do
+ @subj.wakeup! :showering
+
+ assert_equal :showering, @subj.current_state(:chetan_patil)
+ end
+
+ test 'transitions to specified next state (sleeping to working)' do
+ @subj.wakeup! :working
+
+ assert_equal :working, @subj.current_state(:chetan_patil)
+ end
+
+ test 'transitions to default (first or showering) state' do
+ @subj.wakeup!
+
+ assert_equal :showering, @subj.current_state(:chetan_patil)
+ end
+
+ test 'transitions to default state when on_transition invoked' do
+ @subj.dress!(nil, 'purple', 'dressy')
+
+ assert_equal :working, @subj.current_state(:chetan_patil)
+ end
+
+ test 'calls on_transition method with args' do
+ @subj.wakeup! :showering
+
+ @subj.expects(:wear_clothes).with('blue', 'jeans')
+ @subj.dress! :working, 'blue', 'jeans'
+ end
+
+ test 'calls on_transition proc' do
+ @subj.wakeup! :showering
+
+ @subj.expects(:wear_clothes).with('purple', 'slacks')
+ @subj.dress!(:dating, 'purple', 'slacks')
+ end
+end