# encoding: utf-8

describe Attestor::Validations do

  let(:collection_class) { Attestor::Validations::Collection }
  let(:item_class)       { Attestor::Validations::Item }
  let(:message_class)    { Attestor::Validations::Message }
  let(:invalid_error)    { Attestor::InvalidError }
  let(:test_class)       { Class.new.send(:include, described_class) }

  before { Test = test_class }
  after  { Object.send :remove_const, :Test }

  subject { test_class.new }

  describe ".validations" do

    it "returns a Collection" do
      expect(test_class.validations).to be_kind_of collection_class
    end

    it "is empty by default" do
      expect(test_class.validations.to_a).to be_empty
    end

  end # describe .validations

  describe ".validate" do

    context "without options" do

      before { test_class.validate :foo }

      it "adds the item to the collection" do
        expect(test_class.validations.to_a).to eq [:foo]
      end

    end # context

    context "with options" do

      before { test_class.validate :foo, only: %w(bar baz), except: "bar" }

      it "uses options" do
        expect(test_class.validations.to_a).to eq [:foo]
        expect(test_class.validations.set(:baz).to_a).to eq [:foo]
        expect(test_class.validations.set(:bar).to_a).to eq []
      end

    end # context

  end # describe .validate

  describe "#validate" do

    before do
      test_class.validate :foo
      test_class.validate :bar, only: :all
      test_class.validate :baz, only: :foo
      %i(foo bar baz).each { |method| allow(subject).to receive(method) }
    end

    context "without an argument" do

      it "calls validations for :all context" do
        expect(subject).to receive(:foo)
        expect(subject).to receive(:bar)
        expect(subject).not_to receive(:baz)
        subject.validate
      end

    end # context

    context ":foo" do

      it "calls validations for :foo context" do
        expect(subject).to receive(:foo)
        expect(subject).to receive(:baz)
        expect(subject).not_to receive(:bar)
        subject.validate :foo
      end

    end # context

  end # describe #validate

  describe "#invalid" do

    shared_examples "raising an error" do |name, options = {}|

      let(:message) { message_class.new(name, subject, options) }

      it "raises an InvalidError" do
        expect { invalid }.to raise_error invalid_error
      end

      it "assings itself to the exception" do
        begin
          invalid
        rescue => error
          expect(error.object).to eq subject
          expect(error.messages).to contain_exactly message
        end
      end

    end # shared examples

    context "without options" do

      let(:invalid) { subject.invalid :foo }

      it_behaves_like "raising an error", :foo

    end

    context "with options" do

      let(:invalid) { subject.invalid :foo, bar: :baz }

      it_behaves_like "raising an error", :foo, bar: :baz

    end

  end # invalid

end # describe Attestor::Validations