RSpec.describe Patterns::Query do
  after { Object.send(:remove_const, :CustomQuery) if defined?(CustomQuery) }

  describe ".new" do
    it "accepts a relation only as argument" do
      CustomQuery = Class.new(Patterns::Query)

      expect {
        CustomQuery.new([1, 2, 3])
      }.to raise_error(
               Patterns::Query::RelationRequired,
               "Queries accept only ActiveRecord::Relation as input"
           )
    end

    it "requires an argument" do
      CustomQuery = Class.new(Patterns::Query)

      expect {
        CustomQuery.new
      }.to raise_error(
                     Patterns::Query::RelationRequired,
               "Queries require a base relation defined. Use .queries method to define relation."
           )
    end

    it "initializes a query object" do
      CustomQuery = Class.new(Patterns::Query)
      relation = ActiveRecord::Relation.allocate

      query = CustomQuery.new(relation)

      expect(query).to be_a_kind_of(CustomQuery)
    end
  end

  describe ".call" do
    it "calls #call and passes argument to constructor" do
      CustomQuery = Class.new(Patterns::Query)
      relation = ActiveRecord::Relation.allocate
      result_relation = ActiveRecord::Relation.allocate
      query_double = instance_double(CustomQuery)
      allow(CustomQuery).to receive(:new) { query_double }
      allow(query_double).to receive(:call) { result_relation }

      result = CustomQuery.call(relation)

      expect(result).to eql result_relation
      expect(query_double).to have_received(:call)
    end
  end

  describe "#call" do
    it "requires impementing #query method" do
      relation =  ActiveRecord::Relation.allocate
      CustomQuery = Class.new(Patterns::Query)

      expect {
        CustomQuery.new(relation).call
      }.to raise_error(
               NotImplementedError,
               "You need to implement #query method which returns ActiveRecord::Relation object"
           )
    end

    it "returns result of calling #query" do
      relation = ActiveRecord::Relation.allocate
      result_relation = ActiveRecord::Relation.allocate
      CustomQuery = Class.new(Patterns::Query) do
        attr_accessor :internal_query

        def query
          internal_query
        end
      end

      query = CustomQuery.new(relation)
      query.internal_query = result_relation
      result = query.call

      expect(result).to eql result_relation
    end

    it "ensures that #query returns a relation" do
      relation = ActiveRecord::Relation.allocate
      CustomQuery = Class.new(Patterns::Query) do
        def query
          [1, 2, 3]
        end
      end

      expect {
        CustomQuery.call(relation)
      }.to raise_error(
                     Patterns::Query::RelationRequired,
               "#query method should return object of ActiveRecord::Relation class"
           )
    end
  end
end