# encoding: utf-8

describe ServiceObjects::Helpers::Parameterized do

  let(:parameters) { ServiceObjects::Helpers::Parameters }
  let(:test_class) { Class.new }
  before  { test_class.extend described_class }
  subject { test_class.new }

  it "includes ServiceObjects::Helpers::Parameters" do
    expect(test_class).to include parameters
  end

  describe ".allows_params" do

    # ==========================================================================
    # Defines behaviours
    # ==========================================================================

    shared_examples "white list" do

      # 'allowed_params' taken from context

      let(:whitelist) { Array(allowed_params).flatten.map(&:to_sym) }

      it "[sets whitelist]" do
        subject
        expect(test_class.whitelist).to match_array whitelist
      end

      it "[returns whitelist]" do
        expect(subject).to eq test_class.whitelist
      end

    end # behaviour

    shared_examples "attributes creator" do

      # 'allowed_params' taken from context

      let(:whitelist) { Array(allowed_params).flatten.map(&:to_sym) }
      let(:object)    { test_class.new options }
      let(:options) do
        whitelist.inject({}) { |a, e| a.merge(e => rand(1..10)) }
      end

      before  { subject }

      it "[defines setters]" do
        whitelist.each { |name| expect(object).to respond_to name }
      end

      it "[sets default values]" do
        whitelist.each { |name| expect(object.send name).to eq options[name] }
      end

      it "[makes getters aliases to params]" do
        whitelist.each do |name|
          value = ("a".."z").to_a.sample

          expect { object.params[name] = value }
            .to change { object.send name }
            .to value
        end
      end

      it "[defines getters]" do
        whitelist.each { |name| expect(object).to respond_to "#{ name }=" }
      end

      it "[makes setters aliases to params]" do
        whitelist.each do |name|
          value = ("a".."z").to_a.sample

          expect { object.send "#{ name }=", value }
            .to change { object.params[name] }
            .to value
        end
      end

    end # behaviour

    # ==========================================================================
    # Tests behaviours
    # ==========================================================================

    context "without arguments" do

      let(:allowed_params) { [] }
      subject { test_class.allows_params allowed_params }

      it_behaves_like "white list"

    end # context

    context "with one symbolic argument" do

      let(:allowed_params) { :name }
      subject { test_class.allows_params allowed_params }

      it_behaves_like "white list"
      it_behaves_like "attributes creator"

    end # context

    context "with one string argument" do

      let(:allowed_params) { "name" }
      subject { test_class.allows_params allowed_params }

      it_behaves_like "white list"
      it_behaves_like "attributes creator"

    end # context

    context "with a list of arguments" do

      let(:allowed_params) { [:name, "code"] }
      subject { test_class.allows_params(*allowed_params) }

      it_behaves_like "white list"
      it_behaves_like "attributes creator"

    end # context

    context "with an array of arguments" do

      let(:allowed_params) { [:name, "code"] }
      subject { test_class.allows_params allowed_params }

      it_behaves_like "white list"
      it_behaves_like "attributes creator"

    end # context
  end
end