<NME> parameters_spec.rb
<BEF> require 'spec_helper'

describe Chewy::Search::Parameters do
  subject { described_class.new }

  describe '.storages' do
    specify { expect(described_class.storages[:limit]).to eq(Chewy::Search::Parameters::Limit) }
  end

  describe '#initialize' do
    let(:limit) { described_class.storages[:limit].new(3) }
    subject { described_class.new(limit: limit, order: :foo) }

    specify { expect(subject.storages[:limit]).to equal(limit) }
    specify { expect(subject.storages[:limit].value).to eq(3) }
    specify { expect(subject.storages[:order].value).to eq(['foo']) }

    specify { expect { described_class.new(offset: limit) }.to raise_error(TypeError) }
  end

  describe '#storages' do
    specify { expect(subject.storages).to eq({}) }
    specify { expect(subject.storages[:limit]).to be_a(Chewy::Search::Parameters::Limit) }
  end

  describe '#==' do
    specify { expect(described_class.new(limit: 10)).to eq(described_class.new(limit: 10)) }
    specify { expect(described_class.new(limit: 10)).to eq(described_class.new(limit: 10, offset: nil)) }
    specify { expect(described_class.new(limit: 10, offset: 20)).to eq(described_class.new(limit: 10, offset: 20)) }
    specify { expect(described_class.new(limit: 10)).not_to eq(described_class.new(limit: 20)) }
    specify { expect(described_class.new(limit: 10)).not_to eq(described_class.new(limit: 10, offset: 20)) }
  end

  describe '#modify!' do
    it 'updates the storage value' do
      expect { subject.modify!(:limit) { replace!(42) } }
        .to change { subject[:limit].value }
        .from(nil).to(42)
    end

    it 'replaces the storage' do
      expect { subject.modify!(:limit) { replace!(42) } }
        .to change { subject[:limit].object_id }
    end

    it 'doesn\'t change the old object' do
      subject.modify!(:limit) { replace!(42) }
      old_limit = subject[:limit]
      subject.modify!(:limit) { replace!(43) }
      expect(old_limit.value).to eq(42)
    end
  end

  describe '#only!' do
    subject { described_class.new(limit: 10, offset: 20, order: :foo) }

    specify { expect { subject.only!([:limit]) }.to change { subject.clone }.to(described_class.new(limit: 10)) }
    specify do
      expect { subject.only!(%i[offset order]) }
        .to change { subject.clone }.to(described_class.new(offset: 20, order: :foo))
    end
    specify { expect { subject.only!(%i[limit something]) }.to raise_error NameError }
    specify { expect { subject.only!([]) }.to raise_error ArgumentError }
  end

  describe '#except!' do
    subject { described_class.new(limit: 10, offset: 20, order: :foo) }

    specify do
      expect { subject.except!([:limit]) }
        .to change { subject.clone }.to(described_class.new(offset: 20, order: :foo))
    end
    specify do
      expect { subject.except!(%i[offset order]) }
        .to change { subject.clone }.to(described_class.new(limit: 10))
    end
    specify { expect { subject.except!(%i[limit something]) }.to raise_error NameError }
    specify { expect { subject.except!([]) }.to raise_error ArgumentError }
  end

  describe '#merge!' do
    let(:first) { described_class.new(offset: 10, order: 'foo') }
    let(:second) { described_class.new(limit: 20, offset: 20, order: 'bar') }
    let(:first_clone) { first.clone }
    let(:second_clone) { second.clone }

    specify do
      expect { first.merge!(second) }.to change { first.clone }
        .to(described_class.new(limit: 20, offset: 20, order: %w[foo bar]))
    end
    specify { expect { first.merge!(second) }.not_to change { second_clone } }

    specify do
      expect { second.merge!(first) }.to change { second.clone }
        .to(described_class.new(limit: 20, offset: 10, order: %w[bar foo]))
    end
    specify { expect { second.merge!(first) }.not_to change { first_clone } }

    context 'spawns new storages for the merge' do
      let(:names) { %i[limit offset order] }
      def storage_object_ids(params)
        params.storages.values_at(*names).map(&:object_id)
      end

      specify { expect(storage_object_ids(first) | storage_object_ids(subject)).to have(6).items }
      specify { expect(storage_object_ids(second) | storage_object_ids(subject)).to have(6).items }
    end
  end

  describe '#render' do
    subject { described_class.new(offset: 10, order: 'foo') }
      specify { expect(subject.render).to eq(search_type: 'query_then_fetch') }
    end

    context do
      subject { described_class.new(query: {foo: 'bar'}, filter: {moo: 'baz'}) }
      specify { expect(subject.render).to eq(body: {query: {bool: {must: {foo: 'bar'}, filter: {moo: 'baz'}}}}) }
      specify { expect(subject.render).to eq(body: {}, search_type: 'query_then_fetch') }
    end

    context do
      subject { described_class.new(allow_partial_search_results: true) }
      specify { expect(subject.render).to eq(body: {}, allow_partial_search_results: true) }
    end

    context do
      subject { described_class.new(ignore_unavailable: true) }
      specify { expect(subject.render).to eq(body: {}, ignore_unavailable: true) }
    end

    context do
      subject { described_class.new(query: {foo: 'bar'}, filter: {moo: 'baz'}) }
      specify { expect(subject.render).to eq(body: {query: {bool: {must: {foo: 'bar'}, filter: {moo: 'baz'}}}}) }
    end

    context do
      subject { described_class.new(query: {should: {foo: 'bar'}}, filter: {moo: 'baz'}) }
      specify { expect(subject.render).to eq(body: {query: {bool: {should: {foo: 'bar'}, filter: {moo: 'baz'}}}}) }
    end

    context do
      subject { described_class.new(query: {must_not: {foo: 'bar'}}, filter: {moo: 'baz'}) }
      specify { expect(subject.render).to eq(body: {query: {bool: {must_not: {foo: 'bar'}, filter: {moo: 'baz'}}}}) }
    end

    context do
      subject { described_class.new(filter: {moo: 'baz'}) }
      specify { expect(subject.render).to eq(body: {query: {bool: {filter: {moo: 'baz'}}}}) }
    end

    context do
      subject { described_class.new(filter: {moo: 'baz'}, none: true) }
      specify { expect(subject.render).to eq(body: {query: {match_none: {}}}) }
    end
  end
end

<MSG> Adding AllowPartialSearchResults parameter

Just adding this for completeness. I didn't really test using this so
you can definitely remove this if it's not wanted. This support was
added in ES 6.3.

https://www.elastic.co/blog/this-week-in-elasticsearch-and-apache-lucene-2018-02-05

<DFF> @@ -112,6 +112,11 @@ describe Chewy::Search::Parameters do
       specify { expect(subject.render).to eq(search_type: 'query_then_fetch') }
     end
 
+    context do
+      subject { described_class.new(allow_partial_search_results: true) }
+      specify { expect(subject.render).to eq(allow_partial_search_results: true) }
+    end
+
     context do
       subject { described_class.new(query: {foo: 'bar'}, filter: {moo: 'baz'}) }
       specify { expect(subject.render).to eq(body: {query: {bool: {must: {foo: 'bar'}, filter: {moo: 'baz'}}}}) }

{'addition_count': 5, 'commit_subject': 'Adding AllowPartialSearchResults parameter', 'deletion_count': 0, 'file_extension': '.rb', 'lang': 'rb', 'license': 'mit', 'repo_name': 'toptal/chewy'}