# encoding: utf-8

describe Policy::Follower do

  let(:test_class) { Class.new.send(:include, described_class) }
  subject          { test_class.new }

  describe ".policies" do

    it "is a collection of policies" do
      expect(test_class.policies).to be_kind_of Policy::Follower::Policies
    end

    it "refers to itself" do
      expect(test_class.policies.follower).to eq test_class
    end

  end # describe .policies

  describe ".follows_policy" do

    it "adds a name to the collection of policies" do
      expect(test_class.policies).to receive(:add).with("foo")
      test_class.follows_policy "foo"
    end

    it "returns the name of the method" do
      expect(test_class.follows_policy "foo").to eq :follows_policy
    end

  end # describe .follows_policy

  describe ".follows_policies" do

    it "adds all names to the collection of policies" do
      expect(test_class.policies).to receive(:add).with("foo").ordered
      expect(test_class.policies).to receive(:add).with("bar").ordered
      test_class.follows_policies "foo", "bar"
    end

    it "returns the name of the method" do
      expect(test_class.follows_policies "foo").to eq :follows_policies
    end

  end # describe .follows_policies

  describe "#follow_policies?" do

    before { test_class.follows_policies :one, :two }

    context "with valid policies" do

      let(:one) { double valid?: true }
      before    { allow(subject).to receive(:one) { one } }

      it "returns true" do
        expect(subject.follow_policies? :one).to eq true
      end

    end # context

    context "with invalid policy's name" do

      let(:one) { double valid?: false, errors: [] }
      before    { allow(subject).to receive(:one) { one } }

      it "raises ViolationError" do
        expect { subject.follow_policies? :one }
          .to raise_error Policy::Follower::ViolationError
      end

      it "stores both the follower and policy in the exception" do
        begin
          subject.follow_policies? :one
        rescue => exception
          expect(exception.follower).to eq subject
          expect(exception.policy).to eq one
        end
      end

    end # context

    context "with undefined name" do

      it "raises NoMethodError" do
        expect { subject.follow_policies? :one }
          .to raise_error NoMethodError
      end

    end # context

    context "with unregistered name" do

      it "raises NameError" do
        expect { subject.follow_policies? :three }
          .to raise_error Policy::Follower::NameError
      end

      it "stores both the follower class and name in the exception" do
        begin
          subject.follow_policies? :three
        rescue => exception
          expect(exception.follower).to eq test_class
          expect(exception.name).to eq :three
        end
      end

    end # context

    context "with a list of names" do

      let(:one) { double valid?: true }
      let(:two) { double valid?: true }

      before    { allow(subject).to receive(:one) { one } }
      before    { allow(subject).to receive(:two) { two } }

      it "checks listed policies in given order" do
        expect(two).to receive(:valid?).ordered
        expect(one).to receive(:valid?).ordered
        subject.follow_policies? :two, :one
      end

    end # context

    context "without names" do

      let(:one) { double valid?: true }
      let(:two) { double valid?: true }

      before    { allow(subject).to receive(:one) { one } }
      before    { allow(subject).to receive(:two) { two } }

      it "checks listed policies in predefined order" do
        expect(one).to receive(:valid?).ordered
        expect(two).to receive(:valid?).ordered
        subject.follow_policies? []
      end

    end # context

  end # describe #follow_policies?

  describe "#follow_policy?" do

    it "is an alias for #follow_policies?" do
      expect(subject).to receive(:follow_policies?).with "foo"
      subject.follow_policy? "foo"
    end

  end # describe #follow_policy?

end # describe Policy::Follower