# encoding: utf-8

require 'spec_helper'

describe Relation::Operation::Limit, '#optimize' do
  subject { object.optimize }

  let(:body)       { LazyEnumerable.new([[1], [2], [3]])  }
  let(:relation)   { Relation.new([[:id, Integer]], body) }
  let(:directions) { [relation[:id]]                      }
  let(:sorted)     { relation.sort_by { directions }      }
  let(:operand)    { sorted                               }
  let(:limit)      { 1                                    }
  let(:object)     { described_class.new(operand, limit)  }

  context 'when the limit is 0' do
    let(:limit) { 0 }

    it { should be_kind_of(Relation::Empty) }

    it 'returns an equivalent relation to the unoptimized operation' do
      should == object
    end

    it 'does not execute body#each' do
      expect(body).not_to receive(:each)
      subject
    end

    it_should_behave_like 'an optimize method'
  end

  context 'containing a sorted operation' do
    it { should be(object) }

    it 'does not execute body#each' do
      expect(body).not_to receive(:each)
      subject
    end

    it_should_behave_like 'an optimize method'
  end

  context 'containing an optimizable sorted operation' do
    let(:operand) { sorted.rename({}) }

    it { should be_kind_of(described_class) }

    its(:operand) { should be(sorted) }

    its(:limit) { should == 1 }

    it 'returns an equivalent relation to the unoptimized operation' do
      should == object
    end

    it 'does not execute body#each' do
      expect(body).not_to receive(:each)
      subject
    end

    it_should_behave_like 'an optimize method'
  end

  context 'containing a more restrictive object operation' do
    let(:operand) { sorted.take(5) }
    let(:limit)   { 10             }

    it { should be_kind_of(described_class) }

    its(:operand) { should be(sorted) }

    it 'uses the more restrictive object' do
      expect(subject.limit).to be(5)
    end

    it 'returns an equivalent relation to the unoptimized operation' do
      should == object
    end

    it 'does not execute body#each' do
      expect(body).not_to receive(:each)
      subject
    end

    it_should_behave_like 'an optimize method'
  end

  context 'containing a less restrictive object operation' do
    let(:operand) { sorted.take(10) }
    let(:limit)   { 5               }

    it { should be_kind_of(described_class) }

    its(:operand) { should be(sorted) }

    it 'uses the more restrictive object' do
      expect(subject.limit).to be(5)
    end

    it 'returns an equivalent relation to the unoptimized operation' do
      should == object
    end

    it 'does not execute body#each' do
      expect(body).not_to receive(:each)
      subject
    end

    it_should_behave_like 'an optimize method'
  end

  context 'containing a similar object operation' do
    let(:operand) { sorted.take(10) }
    let(:limit)   { 10              }

    it { should be(operand) }

    it 'returns an equivalent relation to the unoptimized operation' do
      should == object
    end

    it 'does not execute body#each' do
      expect(body).not_to receive(:each)
      subject
    end

    it_should_behave_like 'an optimize method'
  end

  context 'containing a materialized relation' do
    let(:relation) { Relation.new([[:id, Integer]], [[1], [2], [3]]) }

    it { should eql(Relation::Materialized.new([[:id, Integer]], [[1]])) }

    it 'returns an equivalent relation to the unoptimized operation' do
      should == object
    end

    it_should_behave_like 'an optimize method'
  end
end