require 'test_helper'

class AuthorizerTest < ActiveSupport::TestCase
  setup do
    @controller = Class.new(ApplicationController) do
      # These would be defined by Devise or some authenication library
      attr_accessor :current_user, :current_admin, :sites
      attr_reader :params

      def params=(params)
        @params = params.with_indifferent_access
      end

      # shortcut to read instance variables
      def [](ivar_name)
        instance_variable_get :"@#{ivar_name}"
      end

      def authenticate_admin! # dummy method
      end

      def authenticate_user! # dummy method
      end

      include SimonSays::Authorizer
    end.new

    @controller.current_user = users(:bob)
    @controller.params = { id: documents(:alpha).id }
  end

  def with_params(params)
    default_params = @controller.params
    @controller.params = params

    yield

  ensure
    @controller.params = default_params
  end

  def with_default_find_attribute(callalbe)
    @controller.class.default_find_attribute = callalbe

    yield

  ensure
    @controller.class.default_find_attribute = nil
  end

  test "find_resource" do
    @controller.find_resource :document

    assert_equal documents(:alpha), @controller[:document]
  end

  test "find_resource with class_name" do
    @controller.find_resource :document, class_name: 'document'

    assert_equal documents(:alpha), @controller[:document]
  end

  test "find_resource with default scope and through" do
    @controller.class.default_authorization_scope = :current_user
    @controller.current_user = users(:bob)

    @controller.find_resource :document, through: :memberships

    assert_equal documents(:alpha), @controller[:document]
  end

  test "find_resource with from" do
    @controller.instance_variable_set :@user, users(:bob)

    @controller.find_resource :document, from: :user

    assert_equal documents(:alpha), @controller[:document]
  end

  test "find_resource with namespace" do
    @controller.current_admin = admins(:support)
    @controller.params = { id: admin_reports(:report_one).id }

    @controller.find_resource :report, namespace: :admin

    assert_equal admin_reports(:report_one), @controller[:report]
  end

  test "find_resource raises RecordNotFound" do
    assert_raises ActiveRecord::RecordNotFound do
      @controller.params = { id: -1 }
      @controller.find_resource :document
    end
  end

  test "find_resource raises RecordNotFound with default scope and through" do
    @controller.class.default_authorization_scope = :current_user
    @controller.current_user = users(:bob)

    assert_raises ActiveRecord::RecordNotFound do
      @controller.params = { id: -1 }
      @controller.find_resource :document, through: :memberships
    end
  end

  test "find_resource raises RecordNotFound with from" do
    @controller.instance_variable_set :@user, users(:bob)

    assert_raises ActiveRecord::RecordNotFound do
      @controller.params = { id: -1 }
      @controller.find_resource :document, from: :user
    end
  end

  test "authorize with membership role" do
    @controller.instance_variable_set :@membership, documents(:alpha).memberships.first

    assert @controller.authorize(:fork, with: :membership)
  end

  test "authorize with current_admin" do
    @controller.current_admin = admins(:support)

    assert @controller.authorize(:support, with: :admin)
  end

  test "authorize with multiple roles" do
    @controller.instance_variable_set :@membership, documents(:alpha).memberships.first

    assert @controller.authorize([:update, :delete], with: :membership)
  end

  test "authorize with through" do
    @controller.instance_variable_set :@membership, documents(:alpha).memberships.first

    assert @controller.authorize(:delete, through: :membership)
  end

  test "authorize invokes authentication_admin" do
    @controller.current_admin = admins(:marketing)

    @controller.expects(:authenticate_admin!).once
    @controller.authorize(:marketing, with: :admin)
  end

  test "authorization failure single role" do
    assert_raises SimonSays::Authorizer::Denied do
      @controller.instance_variable_set :@membership, documents(:beta).memberships.first

      @controller.authorize(:delete, with: :membership)
    end
  end

  test "authorization failire multi roles" do
    @controller.instance_variable_set :@membership, documents(:beta).memberships.first

    assert_raises SimonSays::Authorizer::Denied do
      @controller.authorize([:update, :delete], with: :membership)
    end
  end

  test 'Authorizer.default_find_attribute proc' do
    with_default_find_attribute ->(resource) { :"#{resource}_id" } do
      with_params id: clients(:alice).client_id do
        @controller.find_resource :client
      end
    end

    assert_equal clients(:alice), @controller[:client]
  end
end
