require 'test_helper'

$VERBOSE = false
require 'active_record'
require 'logger'
require 'sqlite3'
require 'workflow'
require 'mocha/setup'
require 'stringio'
require 'protected_attributes' if ActiveRecord::VERSION::MAJOR >= 4

ActiveRecord::Migration.verbose = false

class AttrProtectedTestOrder < ActiveRecord::Base
  include Workflow

  workflow do
    state :submitted do
      event :accept, :transitions_to => :accepted, :meta => {:doc_weight => 8} do |reviewer, args|
      end
    end
    state :accepted do
      event :ship, :transitions_to => :shipped
    end
    state :shipped
  end

  attr_accessible :title # protecting all the other attributes

end

AttrProtectedTestOrder.logger = Logger.new(STDOUT) # active_record 2.3 expects a logger instance
AttrProtectedTestOrder.logger.level = Logger::WARN # switch to Logger::DEBUG to see the SQL statements

class AttrProtectedTest < ActiveRecordTestCase

  def setup
    super

    ActiveRecord::Schema.define do
      create_table :attr_protected_test_orders do |t|
        t.string :title, :null => false
        t.string :workflow_state
      end
    end

    exec "INSERT INTO attr_protected_test_orders(title, workflow_state) VALUES('order1', 'submitted')"
    exec "INSERT INTO attr_protected_test_orders(title, workflow_state) VALUES('order2', 'accepted')"
    exec "INSERT INTO attr_protected_test_orders(title, workflow_state) VALUES('order3', 'accepted')"
    exec "INSERT INTO attr_protected_test_orders(title, workflow_state) VALUES('order4', 'accepted')"
    exec "INSERT INTO attr_protected_test_orders(title, workflow_state) VALUES('order5', 'accepted')"
    exec "INSERT INTO attr_protected_test_orders(title, workflow_state) VALUES('protected order', 'submitted')"
  end

  def assert_state(title, expected_state, klass = AttrProtectedTestOrder)
    o = klass.find_by_title(title)
    assert_equal expected_state, o.read_attribute(klass.workflow_column)
    o
  end

  test 'cannot mass-assign workflow_state if attr_protected' do
     o = AttrProtectedTestOrder.find_by_title('order1')
     assert_equal 'submitted', o.read_attribute(:workflow_state).to_s
     AttrProtectedTestOrder.logger.level = Logger::ERROR # ignore warnings
     o.update_attributes :workflow_state => 'some_bad_value'
     AttrProtectedTestOrder.logger.level = Logger::WARN
     assert_equal 'submitted', o.read_attribute(:workflow_state).to_s
     o.update_attribute :workflow_state, 'some_overridden_value'
     assert_equal 'some_overridden_value', o.read_attribute(:workflow_state).to_s
   end

  test 'immediately save the new workflow_state on state machine transition' do
    o = assert_state 'order2', 'accepted'
    assert o.ship!
    assert_state 'order2', 'shipped'
  end

  test 'persist workflow_state in the db and reload' do
    o = assert_state 'order3', 'accepted'
    assert_equal :accepted, o.current_state.name
    o.ship! # should save in the database, no `o.save!` needed

    assert_state 'order3', 'shipped'

    o.reload
    assert_equal 'shipped', o.read_attribute(:workflow_state)
  end

  test 'default workflow column should be workflow_state' do
    o = assert_state 'order4', 'accepted'
    assert_equal :workflow_state, o.class.workflow_column
  end

  test 'access workflow specification' do
    assert_equal 3, AttrProtectedTestOrder.workflow_spec.states.length
    assert_equal ['submitted', 'accepted', 'shipped'].sort,
      AttrProtectedTestOrder.workflow_spec.state_names.map{|n| n.to_s}.sort
  end

  test 'current state object' do
    o = assert_state 'order5', 'accepted'
    assert_equal 'accepted', o.current_state.to_s
    assert_equal 1, o.current_state.events.length
  end

end