require "spec_helper"

require "festoon/dynamic"

RSpec.describe Festoon::Dynamic do
  subject(:composed_object) { Festoon::Dynamic.new(underlying_object) }
  let(:underlying_object)   { double(:underlying_object) }

  context "when a method is not implemented by the decorator" do
    let(:arguments) { [double, double] }

    it "delegates messages" do
      expect(underlying_object).to receive(:arbitrary_message)
        .with(*arguments)

      composed_object.arbitrary_message(*arguments)
    end

    it "delegates with the block" do
      allow(underlying_object).to receive(:arbitrary_message).and_yield

      expect { |blk|
        composed_object.arbitrary_message(&blk)
      }.to yield_with_no_args
    end
  end

  context "methods on the underlying object that return self" do
    before do
      allow(underlying_object).to receive(:arbitrary_message)
        .and_return(underlying_object)
    end

    it "returns self to maintain decoration" do
      expect(composed_object.arbitrary_message).to be(composed_object)
    end
  end

  describe "#respond_to?" do
    before do
      allow(underlying_object).to receive(:arbitrary_message)
    end

    it "responds to its own methods" do
      expect(composed_object).to respond_to(:__decompose__)
    end

    it "purports to respond to messages that the underlying object responds to" do
      expect(composed_object).to respond_to(:arbitrary_message)
    end
  end

  describe "#==" do
    let(:object) { Object.new }
    let(:other) { Object.new }

    let(:composed_object) { Festoon::Dynamic.new(object) }

    it "delegates equality to the underlying object" do
      expect(composed_object == object).to be(true)
      expect(composed_object == other).to be(false)
    end

    context "when comparing to another decorator" do
      let(:composed_similar) { Festoon::Dynamic.new(object) }

      it "inverts the operation offering up its underying object for comparison" do
        expect(composed_object == composed_similar).to be(true)
      end
    end
  end

  describe "#__decompose__" do
    let(:base_object) { Object.new }
    let(:middle_wrap) { Festoon::Dynamic.new(base_object) }
    let(:top_wrap) { Festoon::Dynamic.new(middle_wrap) }

    it "recursivly unwraps the composed objects into a flat array" do
      expect(top_wrap.__decompose__).to eq([top_wrap, middle_wrap, base_object])
    end
  end
end
