require 'helper' class ActiveRecordTest < Minitest::Test class PostStates < StateManager::Base attr_accessor :before_callbacks_called attr_accessor :after_callbacks_called state :unsubmitted do event :submit, :transitions_to => 'submitted.awaiting_review' end state :submitted do state :awaiting_review do event :review, :transitions_to => 'submitted.reviewing' end state :reviewing do event :accept, :transitions_to => 'active' event :clarify, :transitions_to => 'submitted.clarifying' end state :clarifying do event :review, :transitions_to => 'submitted.reviewing' end end state :active state :rejected attr_accessor :unsubmitted_entered_count attr_accessor :active_entered_count def initialize(*args) super @unsubmitted_entered_count = 0 @active_entered_count = 0 end def will_transition(*args) self.before_callbacks_called = true end def did_transition(*args) self.after_callbacks_called = true end class Unsubmitted def entered state_manager.unsubmitted_entered_count += 1 end end class Active def entered state_manager.active_entered_count += 1 end end end class Post < ActiveRecord::Base extend StateManager::Resource state_manager end class Post2 < ActiveRecord::Base self.table_name = 'posts' extend StateManager::Resource state_manager(:state, PostStates, :save_callback => :after_commit) end def exec(sql) ActiveRecord::Base.connection.execute sql end def setup ActiveRecord::Base.establish_connection( :adapter => "sqlite3", :database => ":memory:" #"tmp/test" ) ActiveRecord::Schema.define do create_table :posts do |t| t.string :title t.string :body t.string :state end end exec "INSERT INTO posts VALUES(1, NULL, NULL, NULL)" exec "INSERT INTO posts VALUES(2, NULL, NULL, 'unsubmitted')" exec "INSERT INTO posts VALUES(3, NULL, NULL, 'submitted.reviewing')" exec "INSERT INTO posts VALUES(4, NULL, NULL, 'submitted.bad_state')" @resource = nil DatabaseCleaner.start end def teardown DatabaseCleaner.clean ActiveRecord::Base.connection.disconnect! end def test_adapter_included @resource = Post.find(1) assert @resource.is_a?(StateManager::Adapters::ActiveRecord::ResourceMethods) assert @resource.state_manager.is_a?(StateManager::Adapters::ActiveRecord::ManagerMethods) end def test_persist_initial_state @resource = Post.find(1) assert_state 'unsubmitted' assert !@resource.state_manager.before_callbacks_called assert !@resource.state_manager.after_callbacks_called assert @resource.changed?, "state should not have been persisted" @resource.transaction do @resource.save assert @resource.state_manager.before_callbacks_called assert @resource.state_manager.after_callbacks_called end end def test_initial_state_value @resource = Post.find(3) assert_state 'submitted.reviewing' end def test_validate_nil_state @resource = Post.find(1) assert !@resource.state @resource.save assert_state 'unsubmitted' end def test_validate_invalid_state @resource = Post.find(4) assert_equal 'submitted.bad_state', @resource.state @resource.save assert_state 'unsubmitted' end def test_new_record @resource = Post.new assert_state 'unsubmitted' assert @resource.new_record?, 'record should not have been persisted' @resource.save @resource.submit! assert_state 'submitted.awaiting_review' assert !@resource.new_record?, 'record should be persisted' end def test_scopes exec "INSERT INTO posts VALUES(5, NULL, NULL, 'submitted.reviewing')" exec "INSERT INTO posts VALUES(6, NULL, NULL, 'submitted.reviewing')" exec "INSERT INTO posts VALUES(7, NULL, NULL, 'submitted.reviewing')" exec "INSERT INTO posts VALUES(8, NULL, NULL, 'submitted.reviewing')" exec "INSERT INTO posts VALUES(9, NULL, NULL, 'submitted.clarifying')" exec "INSERT INTO posts VALUES(10, NULL, NULL, 'submitted.clarifying')" exec "INSERT INTO posts VALUES(11, NULL, NULL, 'active')" exec "INSERT INTO posts VALUES(12, NULL, NULL, 'active')" exec "INSERT INTO posts VALUES(13, NULL, NULL, 'active')" exec "INSERT INTO posts VALUES(14, NULL, NULL, 'active')" # +1 from setup assert_equal 1, Post.unsubmitted.count # +2 from setup assert_equal 8, Post.submitted.count assert_equal 4, Post.active.count assert_equal 0, Post.rejected.count end def test_multiple_transitions @resource = Post.find(2) @resource.submit! assert_state 'submitted.awaiting_review' @resource.review! assert_state 'submitted.reviewing' end def test_dirty_transition @resource = Post.find(2) @resource.state_manager.send_event :submit assert_state 'submitted.awaiting_review' assert_raises(StateManager::Adapters::ActiveRecord::DirtyTransition) do @resource.state_manager.send_event :review end end def test_after_commit_callback @resource = Post2.find(1) assert_state 'unsubmitted' assert !@resource.state_manager.before_callbacks_called assert !@resource.state_manager.after_callbacks_called assert @resource.changed?, "state should not have been persisted" @resource.transaction do @resource.save! assert @resource.state_manager.before_callbacks_called assert !@resource.state_manager.after_callbacks_called end assert @resource.state_manager.after_callbacks_called end def test_after_commit_callback_on_create Post2.transaction do @resource = Post2.new assert !@resource.state_manager.after_callbacks_called @resource.save assert !@resource.state_manager.after_callbacks_called end assert_equal @resource.state_manager.unsubmitted_entered_count, 1 assert @resource.state_manager.after_callbacks_called end def test_after_commit_callback_on_different_initial_state Post2.transaction do @resource = Post2.new(:state => 'active') assert !@resource.state_manager.after_callbacks_called @resource.save assert !@resource.state_manager.after_callbacks_called end assert_equal @resource.state_manager.unsubmitted_entered_count, 0 assert_equal @resource.state_manager.active_entered_count, 1 assert @resource.state_manager.after_callbacks_called end end