diff --git a/elasticsearch-model/Gemfile b/elasticsearch-model/Gemfile index a54f5084e..ee48fe61b 100644 --- a/elasticsearch-model/Gemfile +++ b/elasticsearch-model/Gemfile @@ -2,3 +2,8 @@ source 'https://rubygems.org' # Specify your gem's dependencies in elasticsearch-model.gemspec gemspec + +group :development, :testing do + gem 'rspec' + gem 'pry-nav' +end \ No newline at end of file diff --git a/elasticsearch-model/Rakefile b/elasticsearch-model/Rakefile index f80c46b23..bda261913 100644 --- a/elasticsearch-model/Rakefile +++ b/elasticsearch-model/Rakefile @@ -17,6 +17,7 @@ end require 'rake/testtask' namespace :test do + Rake::TestTask.new(:run_unit) do |test| test.libs << 'lib' << 'test' test.test_files = FileList["test/unit/**/*_test.rb"] @@ -33,9 +34,11 @@ namespace :test do desc "Run unit tests against ActiveModel 3, 4 and 5" task :unit do - sh "BUNDLE_GEMFILE='#{File.expand_path('../gemfiles/3.0.gemfile', __FILE__)}' bundle exec rake test:run_unit" - sh "BUNDLE_GEMFILE='#{File.expand_path('../gemfiles/4.0.gemfile', __FILE__)}' bundle exec rake test:run_unit" - sh "BUNDLE_GEMFILE='#{File.expand_path('../gemfiles/5.0.gemfile', __FILE__)}' bundle exec rake test:run_unit" + ['3.0.gemfile', '4.0.gemfile', '5.0.gemfile'].each do |gemfile| + ['bundle exec rake test:run_unit', 'bundle exec rspec'].each do |cmd| + sh "BUNDLE_GEMFILE='#{File.expand_path('../gemfiles/'+gemfile, __FILE__)}' #{cmd}" + end + end end desc "Run integration tests against latest stable ActiveModel (5)" diff --git a/elasticsearch-model/gemfiles/3.0.gemfile b/elasticsearch-model/gemfiles/3.0.gemfile index 97ea5f60a..b0141ef48 100644 --- a/elasticsearch-model/gemfiles/3.0.gemfile +++ b/elasticsearch-model/gemfiles/3.0.gemfile @@ -11,3 +11,8 @@ gem 'activemodel', '>= 3.0' gem 'activerecord', '~> 3.2' gem 'mongoid', '>= 3.0' gem 'sqlite3' unless defined?(JRUBY_VERSION) + +group :development, :testing do + gem 'rspec' + gem 'pry-nav' +end \ No newline at end of file diff --git a/elasticsearch-model/gemfiles/4.0.gemfile b/elasticsearch-model/gemfiles/4.0.gemfile index fa0cc73e9..eea429e12 100644 --- a/elasticsearch-model/gemfiles/4.0.gemfile +++ b/elasticsearch-model/gemfiles/4.0.gemfile @@ -10,3 +10,8 @@ gemspec path: '../' gem 'activemodel', '~> 4' gem 'activerecord', '~> 4' gem 'sqlite3' unless defined?(JRUBY_VERSION) + +group :development, :testing do + gem 'rspec' + gem 'pry-nav' +end \ No newline at end of file diff --git a/elasticsearch-model/gemfiles/5.0.gemfile b/elasticsearch-model/gemfiles/5.0.gemfile index 77e10c7bb..aa8e77655 100644 --- a/elasticsearch-model/gemfiles/5.0.gemfile +++ b/elasticsearch-model/gemfiles/5.0.gemfile @@ -10,3 +10,8 @@ gemspec path: '../' gem 'activemodel', '~> 5' gem 'activerecord', '~> 5' gem 'sqlite3' unless defined?(JRUBY_VERSION) + +group :development, :testing do + gem 'rspec' + gem 'pry-nav' +end diff --git a/elasticsearch-model/lib/elasticsearch/model/response/pagination.rb b/elasticsearch-model/lib/elasticsearch/model/response/pagination.rb index c8e74b793..82f1301a5 100644 --- a/elasticsearch-model/lib/elasticsearch/model/response/pagination.rb +++ b/elasticsearch-model/lib/elasticsearch/model/response/pagination.rb @@ -1,192 +1,2 @@ -module Elasticsearch - module Model - module Response - - # Pagination for search results/records - # - module Pagination - # Allow models to be paginated with the "kaminari" gem [https://github.com/amatsuda/kaminari] - # - module Kaminari - def self.included(base) - # Include the Kaminari configuration and paging method in response - # - base.__send__ :include, ::Kaminari::ConfigurationMethods::ClassMethods - base.__send__ :include, ::Kaminari::PageScopeMethods - - # Include the Kaminari paging methods in results and records - # - Elasticsearch::Model::Response::Results.__send__ :include, ::Kaminari::ConfigurationMethods::ClassMethods - Elasticsearch::Model::Response::Results.__send__ :include, ::Kaminari::PageScopeMethods - Elasticsearch::Model::Response::Records.__send__ :include, ::Kaminari::PageScopeMethods - - Elasticsearch::Model::Response::Results.__send__ :delegate, :limit_value, :offset_value, :total_count, :max_pages, to: :response - Elasticsearch::Model::Response::Records.__send__ :delegate, :limit_value, :offset_value, :total_count, :max_pages, to: :response - - base.class_eval <<-RUBY, __FILE__, __LINE__ + 1 - # Define the `page` Kaminari method - # - def #{::Kaminari.config.page_method_name}(num=nil) - @results = nil - @records = nil - @response = nil - @page = [num.to_i, 1].max - @per_page ||= __default_per_page - - self.search.definition.update size: @per_page, - from: @per_page * (@page - 1) - - self - end - RUBY - end - - # Returns the current "limit" (`size`) value - # - def limit_value - case - when search.definition[:size] - search.definition[:size] - else - __default_per_page - end - end - - # Returns the current "offset" (`from`) value - # - def offset_value - case - when search.definition[:from] - search.definition[:from] - else - 0 - end - end - - # Set the "limit" (`size`) value - # - def limit(value) - return self if value.to_i <= 0 - @results = nil - @records = nil - @response = nil - @per_page = value.to_i - - search.definition.update :size => @per_page - search.definition.update :from => @per_page * (@page - 1) if @page - self - end - - # Set the "offset" (`from`) value - # - def offset(value) - return self if value.to_i < 0 - @results = nil - @records = nil - @response = nil - @page = nil - search.definition.update :from => value.to_i - self - end - - # Returns the total number of results - # - def total_count - results.total - end - - # Returns the models's `per_page` value or the default - # - # @api private - # - def __default_per_page - klass.respond_to?(:default_per_page) && klass.default_per_page || ::Kaminari.config.default_per_page - end - end - - # Allow models to be paginated with the "will_paginate" gem [https://github.com/mislav/will_paginate] - # - module WillPaginate - def self.included(base) - base.__send__ :include, ::WillPaginate::CollectionMethods - - # Include the paging methods in results and records - # - methods = [:current_page, :offset, :length, :per_page, :total_entries, :total_pages, :previous_page, :next_page, :out_of_bounds?] - Elasticsearch::Model::Response::Results.__send__ :delegate, *methods, to: :response - Elasticsearch::Model::Response::Records.__send__ :delegate, *methods, to: :response - end - - def offset - (current_page - 1) * per_page - end - - def length - search.definition[:size] - end - - # Main pagination method - # - # @example - # - # Article.search('foo').paginate(page: 1, per_page: 30) - # - def paginate(options) - param_name = options[:param_name] || :page - page = [options[param_name].to_i, 1].max - per_page = (options[:per_page] || __default_per_page).to_i - - search.definition.update size: per_page, - from: (page - 1) * per_page - self - end - - # Return the current page - # - def current_page - search.definition[:from] / per_page + 1 if search.definition[:from] && per_page - end - - # Pagination method - # - # @example - # - # Article.search('foo').page(2) - # - def page(num) - paginate(page: num, per_page: per_page) # shorthand - end - - # Return or set the "size" value - # - # @example - # - # Article.search('foo').per_page(15).page(2) - # - def per_page(num = nil) - if num.nil? - search.definition[:size] - else - paginate(page: current_page, per_page: num) # shorthand - end - end - - # Returns the total number of results - # - def total_entries - results.total - end - - # Returns the models's `per_page` value or the default - # - # @api private - # - def __default_per_page - klass.respond_to?(:per_page) && klass.per_page || ::WillPaginate.per_page - end - end - end - - end - end -end +require 'elasticsearch/model/response/pagination/kaminari' +require 'elasticsearch/model/response/pagination/will_paginate' diff --git a/elasticsearch-model/lib/elasticsearch/model/response/pagination/kaminari.rb b/elasticsearch-model/lib/elasticsearch/model/response/pagination/kaminari.rb new file mode 100644 index 000000000..5b1acfd9b --- /dev/null +++ b/elasticsearch-model/lib/elasticsearch/model/response/pagination/kaminari.rb @@ -0,0 +1,109 @@ +module Elasticsearch + module Model + module Response + + # Pagination for search results/records + # + module Pagination + # Allow models to be paginated with the "kaminari" gem [https://github.com/amatsuda/kaminari] + # + module Kaminari + def self.included(base) + # Include the Kaminari configuration and paging method in response + # + base.__send__ :include, ::Kaminari::ConfigurationMethods::ClassMethods + base.__send__ :include, ::Kaminari::PageScopeMethods + + # Include the Kaminari paging methods in results and records + # + Elasticsearch::Model::Response::Results.__send__ :include, ::Kaminari::ConfigurationMethods::ClassMethods + Elasticsearch::Model::Response::Results.__send__ :include, ::Kaminari::PageScopeMethods + Elasticsearch::Model::Response::Records.__send__ :include, ::Kaminari::PageScopeMethods + + Elasticsearch::Model::Response::Results.__send__ :delegate, :limit_value, :offset_value, :total_count, :max_pages, to: :response + Elasticsearch::Model::Response::Records.__send__ :delegate, :limit_value, :offset_value, :total_count, :max_pages, to: :response + + base.class_eval <<-RUBY, __FILE__, __LINE__ + 1 + # Define the `page` Kaminari method + # + def #{::Kaminari.config.page_method_name}(num=nil) + @results = nil + @records = nil + @response = nil + @page = [num.to_i, 1].max + @per_page ||= __default_per_page + + self.search.definition.update size: @per_page, + from: @per_page * (@page - 1) + + self + end + RUBY + end + + # Returns the current "limit" (`size`) value + # + def limit_value + case + when search.definition[:size] + search.definition[:size] + else + __default_per_page + end + end + + # Returns the current "offset" (`from`) value + # + def offset_value + case + when search.definition[:from] + search.definition[:from] + else + 0 + end + end + + # Set the "limit" (`size`) value + # + def limit(value) + return self if value.to_i <= 0 + @results = nil + @records = nil + @response = nil + @per_page = value.to_i + + search.definition.update :size => @per_page + search.definition.update :from => @per_page * (@page - 1) if @page + self + end + + # Set the "offset" (`from`) value + # + def offset(value) + return self if value.to_i < 0 + @results = nil + @records = nil + @response = nil + @page = nil + search.definition.update :from => value.to_i + self + end + + # Returns the total number of results + # + def total_count + results.total + end + + # Returns the models's `per_page` value or the default + # + # @api private + # + def __default_per_page + klass.respond_to?(:default_per_page) && klass.default_per_page || ::Kaminari.config.default_per_page + end + end + end + end + end +end diff --git a/elasticsearch-model/lib/elasticsearch/model/response/pagination/will_paginate.rb b/elasticsearch-model/lib/elasticsearch/model/response/pagination/will_paginate.rb new file mode 100644 index 000000000..7cfc36d0c --- /dev/null +++ b/elasticsearch-model/lib/elasticsearch/model/response/pagination/will_paginate.rb @@ -0,0 +1,95 @@ +module Elasticsearch + module Model + module Response + + # Pagination for search results/records + # + module Pagination + + + # Allow models to be paginated with the "will_paginate" gem [https://github.com/mislav/will_paginate] + # + module WillPaginate + def self.included(base) + base.__send__ :include, ::WillPaginate::CollectionMethods + + # Include the paging methods in results and records + # + methods = [:current_page, :offset, :length, :per_page, :total_entries, :total_pages, :previous_page, :next_page, :out_of_bounds?] + Elasticsearch::Model::Response::Results.__send__ :delegate, *methods, to: :response + Elasticsearch::Model::Response::Records.__send__ :delegate, *methods, to: :response + end + + def offset + (current_page - 1) * per_page + end + + def length + search.definition[:size] + end + + # Main pagination method + # + # @example + # + # Article.search('foo').paginate(page: 1, per_page: 30) + # + def paginate(options) + param_name = options[:param_name] || :page + page = [options[param_name].to_i, 1].max + per_page = (options[:per_page] || __default_per_page).to_i + + search.definition.update size: per_page, + from: (page - 1) * per_page + self + end + + # Return the current page + # + def current_page + search.definition[:from] / per_page + 1 if search.definition[:from] && per_page + end + + # Pagination method + # + # @example + # + # Article.search('foo').page(2) + # + def page(num) + paginate(page: num, per_page: per_page) # shorthand + end + + # Return or set the "size" value + # + # @example + # + # Article.search('foo').per_page(15).page(2) + # + def per_page(num = nil) + if num.nil? + search.definition[:size] + else + paginate(page: current_page, per_page: num) # shorthand + end + end + + # Returns the total number of results + # + def total_entries + results.total + end + + # Returns the models's `per_page` value or the default + # + # @api private + # + def __default_per_page + klass.respond_to?(:per_page) && klass.per_page || ::WillPaginate.per_page + end + end + end + + end + end +end diff --git a/elasticsearch-model/spec/elasticsearch/model/response/aggregations_spec.rb b/elasticsearch-model/spec/elasticsearch/model/response/aggregations_spec.rb new file mode 100644 index 000000000..9d20730da --- /dev/null +++ b/elasticsearch-model/spec/elasticsearch/model/response/aggregations_spec.rb @@ -0,0 +1,66 @@ +require 'spec_helper' + +describe Elasticsearch::Model::Response::Aggregations do + + before(:all) do + class OriginClass + def self.index_name; 'foo'; end + def self.document_type; 'bar'; end + end + end + + after(:all) do + Object.send(:remove_const, :OriginClass) if defined?(OriginClass) + end + + let(:response_document) do + { + 'aggregations' => { + 'foo' => {'bar' => 10 }, + 'price' => { 'doc_count' => 123, + 'min' => { 'value' => 1.0}, + 'max' => { 'value' => 99 } + } + } + } + end + + let(:search) do + Elasticsearch::Model::Searching::SearchRequest.new(OriginClass, '*').tap do |request| + allow(request).to receive(:execute!).and_return(response_document) + end + end + + let(:aggregations) do + Elasticsearch::Model::Response::Response.new(OriginClass, search).aggregations + end + + describe 'method delegation' do + + it 'delegates methods to the response document' do + expect(aggregations.foo).to be_a(Hashie::Mash) + expect(aggregations.foo.bar).to be(10) + end + end + + describe '#doc_count' do + + it 'returns the doc count value from the response document' do + expect(aggregations.price.doc_count).to eq(123) + end + end + + describe '#min' do + + it 'returns the min value from the response document' do + expect(aggregations.price.min.value).to eq(1) + end + end + + describe '#max' do + + it 'returns the max value from the response document' do + expect(aggregations.price.max.value).to eq(99) + end + end +end diff --git a/elasticsearch-model/spec/elasticsearch/model/response/base_spec.rb b/elasticsearch-model/spec/elasticsearch/model/response/base_spec.rb new file mode 100644 index 000000000..eae7a8a67 --- /dev/null +++ b/elasticsearch-model/spec/elasticsearch/model/response/base_spec.rb @@ -0,0 +1,91 @@ +require 'spec_helper' + +describe Elasticsearch::Model::Response::Base do + + before(:all) do + class DummyBaseClass + include Elasticsearch::Model::Response::Base + end + + class OriginClass + def self.index_name; 'foo'; end + def self.document_type; 'bar'; end + end + end + + after(:all) do + Object.send(:remove_const, :DummyBaseClass) if defined?(DummyBaseClass) + Object.send(:remove_const, :OriginClass) if defined?(OriginClass) + end + + let(:response_document) do + { 'hits' => { 'total' => 123, 'max_score' => 456, 'hits' => [] } } + end + + let(:search) do + Elasticsearch::Model::Searching::SearchRequest.new(OriginClass, '*').tap do |request| + allow(request).to receive(:execute!).and_return(response_document) + end + end + + let(:response) do + Elasticsearch::Model::Response::Response.new(OriginClass, search) + end + + let(:response_base) do + DummyBaseClass.new(OriginClass, response) + end + + describe '#klass' do + + it 'returns the class' do + expect(response.klass).to be(OriginClass) + end + end + + describe '#response' do + + it 'returns the response object' do + expect(response_base.response).to eq(response) + end + end + + describe 'response document' do + + it 'returns the response document' do + expect(response_base.response.response).to eq(response_document) + end + end + + describe '#total' do + + it 'returns the total' do + expect(response_base.total).to eq(123) + end + end + + describe '#max_score' do + + it 'returns the total' do + expect(response_base.max_score).to eq(456) + end + end + + describe '#results' do + + it 'raises a NotImplemented error' do + expect { + response_base.results + }.to raise_exception(Elasticsearch::Model::NotImplemented) + end + end + + describe '#records' do + + it 'raises a NotImplemented error' do + expect { + response_base.records + }.to raise_exception(Elasticsearch::Model::NotImplemented) + end + end +end diff --git a/elasticsearch-model/spec/elasticsearch/model/response/pagination/kaminari_spec.rb b/elasticsearch-model/spec/elasticsearch/model/response/pagination/kaminari_spec.rb new file mode 100644 index 000000000..6198fafac --- /dev/null +++ b/elasticsearch-model/spec/elasticsearch/model/response/pagination/kaminari_spec.rb @@ -0,0 +1,411 @@ +require 'spec_helper' + +describe Elasticsearch::Model::Response::Response do + + before(:all) do + class ModelClass + include ::Kaminari::ConfigurationMethods + def self.index_name; 'foo'; end + def self.document_type; 'bar'; end + end + end + + after(:all) do + Object.send(:remove_const, :ModelClass) if defined?(ModelClass) + end + + let(:response_document) do + { 'took' => '5', 'timed_out' => false, '_shards' => {'one' => 'OK'}, + 'hits' => { 'total' => 100, 'hits' => (1..100).to_a.map { |i| { _id: i } } } } + end + + let(:search) do + Elasticsearch::Model::Searching::SearchRequest.new(model, '*') + end + + let(:response) do + allow(model).to receive(:client).and_return(client) + Elasticsearch::Model::Response::Response.new(model, search, response_document).tap do |resp| + allow(resp).to receive(:client).and_return(client) + end + + end + + let(:client) do + double('client') + end + + shared_examples_for 'a search request that can be paginated' do + + describe '#page' do + + it 'does not set an initial from and size on the search definition' do + expect(response.search.definition[:from]).to be(nil) + expect(response.search.definition[:size]).to be(nil) + end + + context 'when page is called once' do + + let(:search_request) do + { index: index_field, from: 25, size: 25, q: '*', type: type_field} + end + + before do + expect(client).to receive(:search).with(search_request).and_return(response_document) + response.page(2).to_a + end + + it 'advances the from/size in the search request' do + expect(response.search.definition[:from]).to be(25) + expect(response.search.definition[:size]).to be(25) + end + end + + context 'when page is called more than once' do + + let(:search_request_one) do + { index: index_field, from: 25, size: 25, q: '*', type: type_field} + end + + let(:search_request_two) do + { index: index_field, from: 75, size: 25, q: '*', type: type_field} + end + + before do + expect(client).to receive(:search).with(search_request_one).and_return(response_document) + response.page(2).to_a + expect(client).to receive(:search).with(search_request_two).and_return(response_document) + response.page(4).to_a + end + + it 'advances the from/size in the search request' do + expect(response.search.definition[:from]).to be(75) + expect(response.search.definition[:size]).to be(25) + end + end + + context 'when limit is also set' do + + before do + response.records + response.results + end + + context 'when page is called before limit' do + + before do + response.page(3).limit(35) + end + + it 'sets the correct values' do + expect(response.search.definition[:size]).to eq(35) + expect(response.search.definition[:from]).to eq(70) + end + + it 'resets the instance variables' do + expect(response.instance_variable_get(:@response)).to be(nil) + expect(response.instance_variable_get(:@records)).to be(nil) + expect(response.instance_variable_get(:@results)).to be(nil) + end + end + + context 'when limit is called before page' do + + before do + response.limit(35).page(3) + end + + it 'sets the correct values' do + expect(response.search.definition[:size]).to eq(35) + expect(response.search.definition[:from]).to eq(70) + end + + it 'resets the instance variables' do + expect(response.instance_variable_get(:@response)).to be(nil) + expect(response.instance_variable_get(:@records)).to be(nil) + expect(response.instance_variable_get(:@results)).to be(nil) + end + end + end + end + + describe '#limit_value' do + + context 'when there is no default set' do + + it 'uses the limit value from the Kaminari configuration' do + expect(response.limit_value).to eq(Kaminari.config.default_per_page) + end + end + + context 'when there is a limit in the search definition' do + + let(:search) do + Elasticsearch::Model::Searching::SearchRequest.new(model, '*', size: 10) + end + + it 'gets the limit from the search definition' do + expect(response.limit_value).to eq(10) + end + end + + context 'when there is a limit in the search body' do + + let(:search) do + Elasticsearch::Model::Searching::SearchRequest.new(model, { query: { match_all: {} }, size: 999 }) + end + + it 'does not use the limit' do + expect(response.limit_value).to be(Kaminari.config.default_per_page) + end + end + end + + describe '#offset_value' do + + context 'when there is no default set' do + + it 'uses an offset of 0' do + expect(response.offset_value).to eq(0) + end + end + + context 'when there is an offset in the search definition' do + + let(:search) do + Elasticsearch::Model::Searching::SearchRequest.new(model, '*', from: 50) + end + + it 'gets the limit from the search definition' do + expect(response.offset_value).to eq(50) + end + end + + context 'when there is an offset in the search body' do + + let(:search) do + Elasticsearch::Model::Searching::SearchRequest.new(model, { query: { match_all: {} }, from: 333 }) + end + + it 'does not use the offset' do + expect(response.offset_value).to be(0) + end + end + end + + describe '#limit' do + + context 'when a limit is set' do + + before do + response.records + response.results + response.limit(35) + end + + it 'sets the limit on the search defintiion' do + expect(response.search.definition[:size]).to eq(35) + end + + it 'resets the instance variables' do + expect(response.instance_variable_get(:@response)).to be(nil) + expect(response.instance_variable_get(:@records)).to be(nil) + expect(response.instance_variable_get(:@results)).to be(nil) + end + + context 'when the limit is provided as a string' do + + before do + response.limit('35') + end + + it 'coerces the string to an integer' do + expect(response.search.definition[:size]).to eq(35) + end + end + + context 'when the limit is an invalid type' do + + before do + response.limit('asdf') + end + + it 'does not apply the setting' do + expect(response.search.definition[:size]).to eq(35) + end + end + end + end + + describe '#offset' do + + context 'when an offset is set' do + + before do + response.records + response.results + response.offset(15) + end + + it 'sets the limit on the search defintiion' do + expect(response.search.definition[:from]).to eq(15) + end + + it 'resets the instance variables' do + expect(response.instance_variable_get(:@response)).to be(nil) + expect(response.instance_variable_get(:@records)).to be(nil) + expect(response.instance_variable_get(:@results)).to be(nil) + end + + context 'when the offset is provided as a string' do + + before do + response.offset('15') + end + + it 'coerces the string to an integer' do + expect(response.search.definition[:from]).to eq(15) + end + end + + context 'when the offset is an invalid type' do + + before do + response.offset('asdf') + end + + it 'does not apply the setting' do + expect(response.search.definition[:from]).to eq(0) + end + end + end + end + + describe '#total' do + + before do + allow(response.results).to receive(:total).and_return(100) + end + + it 'returns the total number of hits' do + expect(response.total_count).to eq(100) + end + end + + context 'results' do + + before do + allow(search).to receive(:execute!).and_return(response_document) + end + + describe '#current_page' do + + it 'returns the current page' do + expect(response.results.current_page).to eq(1) + end + + context 'when a particular page is accessed' do + + it 'returns the correct current page' do + expect(response.page(5).results.current_page).to eq(5) + end + end + end + + describe '#prev_page' do + + it 'returns the previous page' do + expect(response.page(1).results.prev_page).to be(nil) + expect(response.page(2).results.prev_page).to be(1) + expect(response.page(3).results.prev_page).to be(2) + expect(response.page(4).results.prev_page).to be(3) + end + end + + describe '#next_page' do + + it 'returns the previous page' do + expect(response.page(1).results.next_page).to be(2) + expect(response.page(2).results.next_page).to be(3) + expect(response.page(3).results.next_page).to be(4) + expect(response.page(4).results.next_page).to be(nil) + end + end + end + + context 'records' do + + before do + allow(search).to receive(:execute!).and_return(response_document) + end + + describe '#current_page' do + + it 'returns the current page' do + expect(response.records.current_page).to eq(1) + end + + context 'when a particular page is accessed' do + + it 'returns the correct current page' do + expect(response.page(5).records.current_page).to eq(5) + end + end + end + + describe '#prev_page' do + + it 'returns the previous page' do + expect(response.page(1).records.prev_page).to be(nil) + expect(response.page(2).records.prev_page).to be(1) + expect(response.page(3).records.prev_page).to be(2) + expect(response.page(4).records.prev_page).to be(3) + end + end + + describe '#next_page' do + + it 'returns the previous page' do + expect(response.page(1).records.next_page).to be(2) + expect(response.page(2).records.next_page).to be(3) + expect(response.page(3).records.next_page).to be(4) + expect(response.page(4).records.next_page).to be(nil) + end + end + end + end + + context 'when the model is a single one' do + + let(:model) do + ModelClass + end + + let(:type_field) do + 'bar' + end + + let(:index_field) do + 'foo' + end + + it_behaves_like 'a search request that can be paginated' + end + + context 'when the model is a multimodel' do + + let(:model) do + Elasticsearch::Model::Multimodel.new(ModelClass) + end + + let(:type_field) do + ['bar'] + end + + let(:index_field) do + ['foo'] + end + + it_behaves_like 'a search request that can be paginated' + end +end \ No newline at end of file diff --git a/elasticsearch-model/spec/elasticsearch/model/response/pagination/will_paginate_spec.rb b/elasticsearch-model/spec/elasticsearch/model/response/pagination/will_paginate_spec.rb new file mode 100644 index 000000000..1e12ebb95 --- /dev/null +++ b/elasticsearch-model/spec/elasticsearch/model/response/pagination/will_paginate_spec.rb @@ -0,0 +1,263 @@ +require 'spec_helper' + +describe Elasticsearch::Model::Response::Response do + + before(:all) do + class ModelClass + def self.index_name; 'foo'; end + def self.document_type; 'bar'; end + + def self.per_page + 33 + end + end + + # Subclass Response so we can include WillPaginate module without conflicts with Kaminari. + class WillPaginateResponse < Elasticsearch::Model::Response::Response + include Elasticsearch::Model::Response::Pagination::WillPaginate + end + end + + after(:all) do + Object.send(:remove_const, :ModelClass) if defined?(ModelClass) + Object.send(:remove_const, :WillPaginateResponse) if defined?(WillPaginateResponse) + end + + let(:response_document) do + { 'took' => '5', 'timed_out' => false, '_shards' => {'one' => 'OK'}, + 'hits' => { 'total' => 100, 'hits' => (1..100).to_a.map { |i| { _id: i } } } } + end + + let(:search) do + Elasticsearch::Model::Searching::SearchRequest.new(model, '*') + end + + let(:response) do + allow(model).to receive(:client).and_return(client) + WillPaginateResponse.new(model, search, response_document).tap do |resp| + allow(resp).to receive(:client).and_return(client) + end + end + + let(:client) do + double('client') + end + + shared_examples_for 'a search request that can be paginated' do + + describe '#offset' do + + context 'when per_page and page are set' do + + before do + response.per_page(3).page(3) + end + + it 'sets the correct offset' do + expect(response.offset).to eq(6) + end + end + end + + describe '#length' do + + context 'when per_page and page are set' do + + before do + response.per_page(3).page(3) + end + + it 'sets the correct offset' do + expect(response.length).to eq(3) + end + end + end + + describe '#paginate' do + + context 'when there are no settings' do + + context 'when page is set to nil' do + + before do + response.paginate(page: nil) + end + + it 'uses the defaults' do + expect(response.search.definition[:size]).to eq(default_per_page) + expect(response.search.definition[:from]).to eq(0) + end + end + + context 'when page is set to a value' do + + before do + response.paginate(page: 2) + end + + it 'uses the defaults' do + expect(response.search.definition[:size]).to eq(default_per_page) + expect(response.search.definition[:from]).to eq(default_per_page) + end + end + + context 'when a custom page and per_page is set' do + + before do + response.paginate(page: 3, per_page: 9) + end + + it 'uses the custom values' do + expect(response.search.definition[:size]).to eq(9) + expect(response.search.definition[:from]).to eq(18) + end + end + + context 'fall back to first page if invalid value is provided' do + + before do + response.paginate(page: -1) + end + + it 'uses the custom values' do + expect(response.search.definition[:size]).to eq(default_per_page) + expect(response.search.definition[:from]).to eq(0) + end + end + end + end + + describe '#page' do + + context 'when a value is provided for page' do + + before do + response.page(5) + end + + it 'calculates the correct :size and :from' do + expect(response.search.definition[:size]).to eq(default_per_page) + expect(response.search.definition[:from]).to eq(default_per_page * 4) + end + end + + context 'when a value is provided for page and per_page' do + + before do + response.page(5).per_page(3) + end + + it 'calculates the correct :size and :from' do + expect(response.search.definition[:size]).to eq(3) + expect(response.search.definition[:from]).to eq(12) + end + end + + context 'when a value is provided for per_page and page' do + + before do + response.per_page(3).page(5) + end + + it 'calculates the correct :size and :from' do + expect(response.search.definition[:size]).to eq(3) + expect(response.search.definition[:from]).to eq(12) + end + end + end + + describe '#current_page' do + + context 'when no values are set' do + + before do + response.paginate({}) + end + + it 'returns the first page' do + expect(response.current_page).to eq(1) + end + end + + context 'when values are provided for per_page and page' do + + before do + response.paginate(page: 3, per_page: 9) + end + + it 'calculates the correct current page' do + expect(response.current_page).to eq(3) + end + end + + context 'when #paginate has not been called on the response' do + + it 'returns nil' do + expect(response.current_page).to be_nil + end + end + end + + describe '#per_page' do + + context 'when a value is set via the #paginate method' do + + before do + response.paginate(per_page: 8) + end + + it 'returns the per_page value' do + expect(response.per_page).to eq(8) + end + end + + context 'when a value is set via the #per_page method' do + + before do + response.per_page(8) + end + + it 'returns the per_page value' do + expect(response.per_page).to eq(8) + end + end + end + + describe '#total_entries' do + + before do + allow(response).to receive(:results).and_return(double('results', total: 100)) + end + + it 'returns the total results' do + expect(response.total_entries).to eq(100) + end + end + end + + context 'when the model is a single one' do + + let(:model) do + ModelClass + end + + let(:default_per_page) do + 33 + end + + it_behaves_like 'a search request that can be paginated' + end + + context 'when the model is a multimodel' do + + let(:model) do + Elasticsearch::Model::Multimodel.new(ModelClass) + end + + let(:default_per_page) do + ::WillPaginate.per_page + end + + it_behaves_like 'a search request that can be paginated' + end +end \ No newline at end of file diff --git a/elasticsearch-model/spec/elasticsearch/model/response/records_spec.rb b/elasticsearch-model/spec/elasticsearch/model/response/records_spec.rb new file mode 100644 index 000000000..a378cc56a --- /dev/null +++ b/elasticsearch-model/spec/elasticsearch/model/response/records_spec.rb @@ -0,0 +1,118 @@ +require 'spec_helper' + +describe Elasticsearch::Model::Response::Records do + + before(:all) do + class DummyCollection + include Enumerable + + def each(&block); ['FOO'].each(&block); end + def size; ['FOO'].size; end + def empty?; ['FOO'].empty?; end + def foo; 'BAR'; end + end + + class DummyModel + def self.index_name; 'foo'; end + def self.document_type; 'bar'; end + + def self.find(*args) + DummyCollection.new + end + end + end + + after(:all) do + Object.send(:remove_const, :DummyCollection) if defined?(DummyCollection) + Object.send(:remove_const, :DummyModel) if defined?(DummyModel) + end + + let(:response_document) do + { 'hits' => { 'total' => 123, 'max_score' => 456, 'hits' => [{'_id' => '1', 'foo' => 'bar'}] } } + end + + let(:results) do + Elasticsearch::Model::Response::Results.new(DummyModel, response_document) + end + + let(:search) do + Elasticsearch::Model::Searching::SearchRequest.new(DummyModel, '*').tap do |request| + allow(request).to receive(:execute!).and_return(response_document) + end + end + + let(:response) do + Elasticsearch::Model::Response::Response.new(DummyModel, search) + end + + let(:records) do + described_class.new(DummyModel, response) + end + + context 'when the records are accessed' do + + it 'returns the records' do + expect(records.records.size).to eq(1) + expect(records.records.first).to eq('FOO') + end + + it 'delegates methods to records' do + expect(records.foo).to eq('BAR') + end + end + + describe '#each_with_hit' do + + it 'returns each record with its Elasticsearch hit' do + records.each_with_hit do |record, hit| + expect(record).to eq('FOO') + expect(hit.foo).to eq('bar') + end + end + end + + describe '#map_with_hit' do + + let(:value) do + records.map_with_hit { |record, hit| "#{record}---#{hit.foo}" } + end + + it 'returns each record with its Elasticsearch hit' do + expect(value).to eq(['FOO---bar']) + end + end + + describe '#ids' do + + it 'returns the ids' do + expect(records.ids).to eq(['1']) + end + end + + context 'when an adapter is used' do + + before do + module DummyAdapter + module RecordsMixin + def records + ['FOOBAR'] + end + end + + def records_mixin + RecordsMixin + end; module_function :records_mixin + end + + allow(Elasticsearch::Model::Adapter).to receive(:from_class).and_return(DummyAdapter) + end + + after do + Object.send(:remove_const, :DummyAdapter) if defined?(DummyAdapter) + end + + it 'delegates the records method to the adapter' do + expect(records.records).to eq(['FOOBAR']) + end + end +end diff --git a/elasticsearch-model/spec/elasticsearch/model/response/response_spec.rb b/elasticsearch-model/spec/elasticsearch/model/response/response_spec.rb new file mode 100644 index 000000000..2bb96fb8a --- /dev/null +++ b/elasticsearch-model/spec/elasticsearch/model/response/response_spec.rb @@ -0,0 +1,131 @@ +require 'spec_helper' + +describe Elasticsearch::Model::Response::Response do + + before(:all) do + class OriginClass + def self.index_name; 'foo'; end + def self.document_type; 'bar'; end + end + end + + after(:all) do + Object.send(:remove_const, :OriginClass) if defined?(OriginClass) + end + + let(:response_document) do + { 'took' => '5', 'timed_out' => false, '_shards' => {'one' => 'OK'}, 'hits' => { 'hits' => [] }, + 'aggregations' => {'foo' => {'bar' => 10}}, + 'suggest' => {'my_suggest' => [ { 'text' => 'foo', 'options' => [ { 'text' => 'Foo', 'score' => 2.0 }, + { 'text' => 'Bar', 'score' => 1.0 } ] } ]}} + + end + + let(:search) do + Elasticsearch::Model::Searching::SearchRequest.new(OriginClass, '*').tap do |request| + allow(request).to receive(:execute!).and_return(response_document) + end + end + + let(:response) do + Elasticsearch::Model::Response::Response.new(OriginClass, search) + end + + it 'performs the Elasticsearch request lazily' do + expect(search).not_to receive(:execute!) + response + end + + describe '#klass' do + + it 'returns the class' do + expect(response.klass).to be(OriginClass) + end + end + + describe '#search' do + + it 'returns the search object' do + expect(response.search).to eq(search) + end + end + + describe '#took' do + + it 'returns the took field' do + expect(response.took).to eq('5') + end + end + + describe '#timed_out' do + + it 'returns the timed_out field' do + expect(response.timed_out).to eq(false) + end + end + + describe '#shards' do + + it 'returns a Hashie::Mash' do + expect(response.shards.one).to eq('OK') + end + end + + describe '#response' do + + it 'returns the response document' do + expect(response.response).to eq(response_document) + end + end + + describe '#results' do + + it 'provides access to the results' do + expect(response.results).to be_a(Elasticsearch::Model::Response::Results) + expect(response.size).to be(0) + end + end + + describe '#records' do + + it 'provides access to the records' do + expect(response.records).to be_a(Elasticsearch::Model::Response::Records) + expect(response.size).to be(0) + end + end + + describe 'enumerable methods' do + + it 'delegates the methods to the results' do + expect(response.empty?).to be(true) + end + end + + describe 'aggregations' do + + it 'provides access to the aggregations' do + expect(response.aggregations).to be_a(Hashie::Mash) + expect(response.aggregations.foo.bar).to eq(10) + end + end + + describe 'suggestions' do + + it 'provides access to the suggestions' do + expect(response.suggestions).to be_a(Hashie::Mash) + expect(response.suggestions.my_suggest.first.options.first.text).to eq('Foo') + expect(response.suggestions.terms).to eq([ 'Foo', 'Bar' ]) + end + + context 'when there are no suggestions' do + + let(:response_document) do + { } + end + + it 'returns an empty list' do + expect(response.suggestions.terms).to eq([ ]) + end + end + end +end diff --git a/elasticsearch-model/spec/elasticsearch/model/response/result_spec.rb b/elasticsearch-model/spec/elasticsearch/model/response/result_spec.rb new file mode 100644 index 000000000..c998b81b8 --- /dev/null +++ b/elasticsearch-model/spec/elasticsearch/model/response/result_spec.rb @@ -0,0 +1,114 @@ +require 'spec_helper' +require 'active_support/json/encoding' + +describe Elasticsearch::Model::Response::Result do + + let(:result) do + described_class.new(foo: 'bar', bar: { bam: 'baz' }) + end + + it 'provides access to the properties' do + expect(result.foo).to eq('bar') + expect(result.bar.bam).to eq('baz') + expect { result.xoxo }.to raise_exception(NoMethodError) + end + + describe '#id' do + + let(:result) do + described_class.new(foo: 'bar', _id: 42, _source: { id: 12 }) + end + + it 'returns the _id field' do + expect(result.id).to eq(42) + end + + it 'provides access to the source id field' do + expect(result._source.id).to eq(12) + end + end + + describe '#type' do + + let(:result) do + described_class.new(foo: 'bar', _type: 'baz', _source: { type: 'BAM' }) + end + + it 'returns the _type field' do + expect(result.type).to eq('baz') + end + + it 'provides access to the source type field' do + expect(result._source.type).to eq('BAM') + end + end + + describe 'method delegation' do + + let(:result) do + described_class.new(foo: 'bar', _source: { bar: { bam: 'baz' } }) + end + + it 'provides access to the _source field via a method' do + expect(result._source).to eq('bar' => { 'bam' => 'baz' }) + end + + context 'when methods map to keys in subdocuments of the response from Elasticsearch' do + + it 'provides access to top level fields via a method' do + expect(result.foo).to eq('bar') + expect(result.fetch(:foo)).to eq('bar') + expect(result.fetch(:does_not_exist, 'moo')).to eq('moo') + end + + it 'responds to hash methods' do + expect(result.keys).to eq(['foo', '_source']) + expect(result.to_hash).to eq('foo' => 'bar', '_source' => { 'bar' => { 'bam' => 'baz' } }) + end + + it 'provides access to fields in the _source subdocument via a method' do + expect(result.bar).to eq('bam' => 'baz') + expect(result.bar.bam).to eq('baz') + expect(result._source.bar).to eq('bam' => 'baz') + expect(result._source.bar.bam).to eq('baz') + end + + context 'when boolean methods are called' do + + it 'provides access to top level fields via a method' do + expect(result.foo?).to eq(true) + expect(result.boo?).to eq(false) + end + + it 'delegates to fields in the _source subdocument via a method' do + expect(result.bar?).to eq(true) + expect(result.bar.bam?).to eq(true) + expect(result.boo?).to eq(false) + expect(result.bar.boo?).to eq(false) + expect(result._source.bar?).to eq(true) + expect(result._source.bar.bam?).to eq(true) + expect(result._source.boo?).to eq(false) + expect(result._source.bar.boo?).to eq(false) + end + end + end + + context 'when methods do not map to keys in subdocuments of the response from Elasticsearch' do + + it 'raises a NoMethodError' do + expect { result.does_not_exist }.to raise_exception(NoMethodError) + end + end + end + + describe '#as_json' do + + let(:result) do + described_class.new(foo: 'bar', _source: { bar: { bam: 'baz' } }) + end + + it 'returns a json string' do + expect(result.as_json(except: 'foo')).to eq({'_source'=>{'bar'=>{'bam'=>'baz'}}}) + end + end +end \ No newline at end of file diff --git a/elasticsearch-model/spec/elasticsearch/model/response/results_spec.rb b/elasticsearch-model/spec/elasticsearch/model/response/results_spec.rb new file mode 100644 index 000000000..5168022b3 --- /dev/null +++ b/elasticsearch-model/spec/elasticsearch/model/response/results_spec.rb @@ -0,0 +1,56 @@ +require 'spec_helper' + +describe Elasticsearch::Model::Response::Results do + + before(:all) do + class OriginClass + def self.index_name; 'foo'; end + def self.document_type; 'bar'; end + end + end + + after(:all) do + Object.send(:remove_const, :OriginClass) if defined?(OriginClass) + end + + let(:response_document) do + { 'hits' => { 'total' => 123, 'max_score' => 456, 'hits' => [{'foo' => 'bar'}] } } + end + + let(:search) do + Elasticsearch::Model::Searching::SearchRequest.new(OriginClass, '*').tap do |request| + allow(request).to receive(:execute!).and_return(response_document) + end + end + + let(:response) do + Elasticsearch::Model::Response::Response.new(OriginClass, search) + end + + let(:results) do + response.results + end + + describe '#results' do + + it 'provides access to the results' do + expect(results.results.size).to be(1) + expect(results.results.first.foo).to eq('bar') + end + end + + describe 'Enumerable' do + + it 'deletebates enumerable methods to the results' do + expect(results.empty?).to be(false) + expect(results.first.foo).to eq('bar') + end + end + + describe '#raw_response' do + + it 'returns the raw response document' do + expect(response.raw_response).to eq(response_document) + end + end +end diff --git a/elasticsearch-model/spec/spec_helper.rb b/elasticsearch-model/spec/spec_helper.rb new file mode 100644 index 000000000..ec55da253 --- /dev/null +++ b/elasticsearch-model/spec/spec_helper.rb @@ -0,0 +1,10 @@ +require 'pry-nav' +require 'kaminari' +require 'will_paginate' +require 'will_paginate/collection' +require 'elasticsearch/model' + +RSpec.configure do |config| + config.formatter = 'documentation' + config.color = true +end diff --git a/elasticsearch-model/test/unit/response_aggregations_test.rb b/elasticsearch-model/test/unit/response_aggregations_test.rb deleted file mode 100644 index cac17759d..000000000 --- a/elasticsearch-model/test/unit/response_aggregations_test.rb +++ /dev/null @@ -1,46 +0,0 @@ -require 'test_helper' - -class Elasticsearch::Model::ResponseAggregationsTest < Test::Unit::TestCase - context "Response aggregations" do - class OriginClass - def self.index_name; 'foo'; end - def self.document_type; 'bar'; end - end - - RESPONSE = { - 'aggregations' => { - 'foo' => {'bar' => 10 }, - 'price' => { 'doc_count' => 123, - 'min' => { 'value' => 1.0}, - 'max' => { 'value' => 99 } - } - } - } - - setup do - @search = Elasticsearch::Model::Searching::SearchRequest.new OriginClass, '*' - @search.stubs(:execute!).returns(RESPONSE) - end - - should "access the aggregations" do - @search.expects(:execute!).returns(RESPONSE) - - response = Elasticsearch::Model::Response::Response.new OriginClass, @search - assert_respond_to response, :aggregations - assert_kind_of Elasticsearch::Model::Response::Aggregations, response.aggregations - assert_kind_of Hashie::Mash, response.aggregations.foo - assert_equal 10, response.aggregations.foo.bar - end - - should "properly return min and max values" do - @search.expects(:execute!).returns(RESPONSE) - - response = Elasticsearch::Model::Response::Response.new OriginClass, @search - - assert_equal 123, response.aggregations.price.doc_count - assert_equal 1, response.aggregations.price.min.value - assert_equal 99, response.aggregations.price.max.value - end - - end -end diff --git a/elasticsearch-model/test/unit/response_base_test.rb b/elasticsearch-model/test/unit/response_base_test.rb deleted file mode 100644 index aa9b4244d..000000000 --- a/elasticsearch-model/test/unit/response_base_test.rb +++ /dev/null @@ -1,40 +0,0 @@ -require 'test_helper' - -class Elasticsearch::Model::BaseTest < Test::Unit::TestCase - context "Response base module" do - class OriginClass - def self.index_name; 'foo'; end - def self.document_type; 'bar'; end - end - - class DummyBaseClass - include Elasticsearch::Model::Response::Base - end - - RESPONSE = { 'hits' => { 'total' => 123, 'max_score' => 456, 'hits' => [] } } - - setup do - @search = Elasticsearch::Model::Searching::SearchRequest.new OriginClass, '*' - @response = Elasticsearch::Model::Response::Response.new OriginClass, @search - @search.stubs(:execute!).returns(RESPONSE) - end - - should "access klass, response, total and max_score" do - r = DummyBaseClass.new OriginClass, @response - - assert_equal OriginClass, r.klass - assert_equal @response, r.response - assert_equal RESPONSE, r.response.response - assert_equal 123, r.total - assert_equal 456, r.max_score - end - - should "have abstract methods results and records" do - r = DummyBaseClass.new OriginClass, @response - - assert_raise(Elasticsearch::Model::NotImplemented) { |e| r.results } - assert_raise(Elasticsearch::Model::NotImplemented) { |e| r.records } - end - - end -end diff --git a/elasticsearch-model/test/unit/response_pagination_kaminari_test.rb b/elasticsearch-model/test/unit/response_pagination_kaminari_test.rb deleted file mode 100644 index 1fc9b2f3c..000000000 --- a/elasticsearch-model/test/unit/response_pagination_kaminari_test.rb +++ /dev/null @@ -1,433 +0,0 @@ -require 'test_helper' - -class Elasticsearch::Model::ResponsePaginationKaminariTest < Test::Unit::TestCase - class ModelClass - include ::Kaminari::ConfigurationMethods - - def self.index_name; 'foo'; end - def self.document_type; 'bar'; end - end - - RESPONSE = { 'took' => '5', 'timed_out' => false, '_shards' => {'one' => 'OK'}, - 'hits' => { 'total' => 100, 'hits' => (1..100).to_a.map { |i| { _id: i } } } } - - context "Response pagination" do - - setup do - @search = Elasticsearch::Model::Searching::SearchRequest.new ModelClass, '*' - @response = Elasticsearch::Model::Response::Response.new ModelClass, @search, RESPONSE - @response.klass.stubs(:client).returns mock('client') - end - - should "have pagination methods" do - assert_respond_to @response, :page - assert_respond_to @response, :limit_value - assert_respond_to @response, :offset_value - assert_respond_to @response, :limit - assert_respond_to @response, :offset - assert_respond_to @response, :total_count - end - - context "#page method" do - should "advance the from/size" do - @response.klass.client - .expects(:search) - .with do |definition| - assert_equal 25, definition[:from] - assert_equal 25, definition[:size] - true - end - .returns(RESPONSE) - - assert_nil @response.search.definition[:from] - assert_nil @response.search.definition[:size] - - @response.page(2).to_a - assert_equal 25, @response.search.definition[:from] - assert_equal 25, @response.search.definition[:size] - end - - should "advance the from/size further" do - @response.klass.client - .expects(:search) - .with do |definition| - assert_equal 75, definition[:from] - assert_equal 25, definition[:size] - true - end - .returns(RESPONSE) - - @response.page(4).to_a - assert_equal 75, @response.search.definition[:from] - assert_equal 25, @response.search.definition[:size] - end - end - - context "limit/offset readers" do - should "return the default" do - assert_equal Kaminari.config.default_per_page, @response.limit_value - assert_equal 0, @response.offset_value - end - - should "return the value from URL parameters" do - search = Elasticsearch::Model::Searching::SearchRequest.new ModelClass, '*', size: 10, from: 50 - @response = Elasticsearch::Model::Response::Response.new ModelClass, search, RESPONSE - - assert_equal 10, @response.limit_value - assert_equal 50, @response.offset_value - end - - should "ignore the value from request body" do - search = Elasticsearch::Model::Searching::SearchRequest.new ModelClass, - { query: { match_all: {} }, from: 333, size: 999 } - @response = Elasticsearch::Model::Response::Response.new ModelClass, search, RESPONSE - - assert_equal Kaminari.config.default_per_page, @response.limit_value - assert_equal 0, @response.offset_value - end - end - - context "limit setter" do - setup do - @response.records - @response.results - end - - should "set the values" do - @response.limit(35) - assert_equal 35, @response.search.definition[:size] - end - - should "reset the variables" do - @response.limit(35) - - assert_nil @response.instance_variable_get(:@response) - assert_nil @response.instance_variable_get(:@records) - assert_nil @response.instance_variable_get(:@results) - end - - should 'coerce string parameters' do - @response.limit("35") - assert_equal 35, @response.search.definition[:size] - end - - should 'ignore invalid string parameters' do - @response.limit(35) - @response.limit("asdf") - assert_equal 35, @response.search.definition[:size] - end - end - - context "with the page() and limit() methods" do - setup do - @response.records - @response.results - end - - should "set the values" do - @response.page(3).limit(35) - assert_equal 35, @response.search.definition[:size] - assert_equal 70, @response.search.definition[:from] - end - - should "set the values when limit is called first" do - @response.limit(35).page(3) - assert_equal 35, @response.search.definition[:size] - assert_equal 70, @response.search.definition[:from] - end - - should "reset the instance variables" do - @response.page(3).limit(35) - - assert_nil @response.instance_variable_get(:@response) - assert_nil @response.instance_variable_get(:@records) - assert_nil @response.instance_variable_get(:@results) - end - end - - context "offset setter" do - setup do - @response.records - @response.results - end - - should "set the values" do - @response.offset(15) - assert_equal 15, @response.search.definition[:from] - end - - should "reset the variables" do - @response.offset(35) - - assert_nil @response.instance_variable_get(:@response) - assert_nil @response.instance_variable_get(:@records) - assert_nil @response.instance_variable_get(:@results) - end - - should 'coerce string parameters' do - @response.offset("35") - assert_equal 35, @response.search.definition[:from] - end - - should 'coerce invalid string parameters' do - @response.offset(35) - @response.offset("asdf") - assert_equal 0, @response.search.definition[:from] - end - end - - context "total" do - should "return the number of hits" do - @response.expects(:results).returns(mock('results', total: 100)) - assert_equal 100, @response.total_count - end - end - - context "results" do - setup do - @search.stubs(:execute!).returns RESPONSE - end - - should "return current page and total count" do - assert_equal 1, @response.page(1).results.current_page - assert_equal 100, @response.results.total_count - - assert_equal 5, @response.page(5).results.current_page - end - - should "return previous page and next page" do - assert_equal nil, @response.page(1).results.prev_page - assert_equal 2, @response.page(1).results.next_page - - assert_equal 3, @response.page(4).results.prev_page - assert_equal nil, @response.page(4).results.next_page - - assert_equal 2, @response.page(3).results.prev_page - assert_equal 4, @response.page(3).results.next_page - end - end - - context "records" do - setup do - @search.stubs(:execute!).returns RESPONSE - end - - should "return current page and total count" do - assert_equal 1, @response.page(1).records.current_page - assert_equal 100, @response.records.total_count - - assert_equal 5, @response.page(5).records.current_page - end - - should "return previous page and next page" do - assert_equal nil, @response.page(1).records.prev_page - assert_equal 2, @response.page(1).records.next_page - - assert_equal 3, @response.page(4).records.prev_page - assert_equal nil, @response.page(4).records.next_page - - assert_equal 2, @response.page(3).records.prev_page - assert_equal 4, @response.page(3).records.next_page - end - end - end - - context "Multimodel response pagination" do - setup do - @multimodel = Elasticsearch::Model::Multimodel.new(ModelClass) - @search = Elasticsearch::Model::Searching::SearchRequest.new @multimodel, '*' - @response = Elasticsearch::Model::Response::Response.new @multimodel, @search, RESPONSE - @response.klass.stubs(:client).returns mock('client') - end - - should "have pagination methods" do - assert_respond_to @response, :page - assert_respond_to @response, :limit_value - assert_respond_to @response, :offset_value - assert_respond_to @response, :limit - assert_respond_to @response, :offset - assert_respond_to @response, :total_count - end - - context "#page method" do - should "advance the from/size" do - @response.klass.client - .expects(:search) - .with do |definition| - assert_equal 25, definition[:from] - assert_equal 25, definition[:size] - true - end - .returns(RESPONSE) - - assert_nil @response.search.definition[:from] - assert_nil @response.search.definition[:size] - - @response.page(2).to_a - assert_equal 25, @response.search.definition[:from] - assert_equal 25, @response.search.definition[:size] - end - - should "advance the from/size further" do - @response.klass.client - .expects(:search) - .with do |definition| - assert_equal 75, definition[:from] - assert_equal 25, definition[:size] - true - end - .returns(RESPONSE) - - @response.page(4).to_a - assert_equal 75, @response.search.definition[:from] - assert_equal 25, @response.search.definition[:size] - end - end - - context "limit/offset readers" do - should "return the default" do - assert_equal Kaminari.config.default_per_page, @response.limit_value - assert_equal 0, @response.offset_value - end - - should "return the value from URL parameters" do - search = Elasticsearch::Model::Searching::SearchRequest.new ModelClass, '*', size: 10, from: 50 - @response = Elasticsearch::Model::Response::Response.new ModelClass, search, RESPONSE - - assert_equal 10, @response.limit_value - assert_equal 50, @response.offset_value - end - - should "ignore the value from request body" do - search = Elasticsearch::Model::Searching::SearchRequest.new ModelClass, - { query: { match_all: {} }, from: 333, size: 999 } - @response = Elasticsearch::Model::Response::Response.new ModelClass, search, RESPONSE - - assert_equal Kaminari.config.default_per_page, @response.limit_value - assert_equal 0, @response.offset_value - end - end - - context "limit setter" do - setup do - @response.records - @response.results - end - - should "set the values" do - @response.limit(35) - assert_equal 35, @response.search.definition[:size] - end - - should "reset the variables" do - @response.limit(35) - - assert_nil @response.instance_variable_get(:@response) - assert_nil @response.instance_variable_get(:@records) - assert_nil @response.instance_variable_get(:@results) - end - end - - context "with the page() and limit() methods" do - setup do - @response.records - @response.results - end - - should "set the values" do - @response.page(3).limit(35) - assert_equal 35, @response.search.definition[:size] - assert_equal 70, @response.search.definition[:from] - end - - should "set the values when limit is called first" do - @response.limit(35).page(3) - assert_equal 35, @response.search.definition[:size] - assert_equal 70, @response.search.definition[:from] - end - - should "reset the instance variables" do - @response.page(3).limit(35) - - assert_nil @response.instance_variable_get(:@response) - assert_nil @response.instance_variable_get(:@records) - assert_nil @response.instance_variable_get(:@results) - end - end - - context "offset setter" do - setup do - @response.records - @response.results - end - - should "set the values" do - @response.offset(15) - assert_equal 15, @response.search.definition[:from] - end - - should "reset the variables" do - @response.offset(35) - - assert_nil @response.instance_variable_get(:@response) - assert_nil @response.instance_variable_get(:@records) - assert_nil @response.instance_variable_get(:@results) - end - end - - context "total" do - should "return the number of hits" do - @response.expects(:results).returns(mock('results', total: 100)) - assert_equal 100, @response.total_count - end - end - - context "results" do - setup do - @search.stubs(:execute!).returns RESPONSE - end - - should "return current page and total count" do - assert_equal 1, @response.page(1).results.current_page - assert_equal 100, @response.results.total_count - - assert_equal 5, @response.page(5).results.current_page - end - - should "return previous page and next page" do - assert_equal nil, @response.page(1).results.prev_page - assert_equal 2, @response.page(1).results.next_page - - assert_equal 3, @response.page(4).results.prev_page - assert_equal nil, @response.page(4).results.next_page - - assert_equal 2, @response.page(3).results.prev_page - assert_equal 4, @response.page(3).results.next_page - end - end - - context "records" do - setup do - @search.stubs(:execute!).returns RESPONSE - end - - should "return current page and total count" do - assert_equal 1, @response.page(1).records.current_page - assert_equal 100, @response.records.total_count - - assert_equal 5, @response.page(5).records.current_page - end - - should "return previous page and next page" do - assert_equal nil, @response.page(1).records.prev_page - assert_equal 2, @response.page(1).records.next_page - - assert_equal 3, @response.page(4).records.prev_page - assert_equal nil, @response.page(4).records.next_page - - assert_equal 2, @response.page(3).records.prev_page - assert_equal 4, @response.page(3).records.next_page - end - end - end -end diff --git a/elasticsearch-model/test/unit/response_pagination_will_paginate_test.rb b/elasticsearch-model/test/unit/response_pagination_will_paginate_test.rb deleted file mode 100644 index 6c9383525..000000000 --- a/elasticsearch-model/test/unit/response_pagination_will_paginate_test.rb +++ /dev/null @@ -1,398 +0,0 @@ -require 'test_helper' -require 'will_paginate' -require 'will_paginate/collection' - -class Elasticsearch::Model::ResponsePaginationWillPaginateTest < Test::Unit::TestCase - class ModelClass - def self.index_name; 'foo'; end - def self.document_type; 'bar'; end - - # WillPaginate adds this method to models (see WillPaginate::PerPage module) - def self.per_page - 33 - end - end - - # Subsclass Response so we can include WillPaginate module without conflicts with Kaminari. - class WillPaginateResponse < Elasticsearch::Model::Response::Response - include Elasticsearch::Model::Response::Pagination::WillPaginate - end - - RESPONSE = { 'took' => '5', 'timed_out' => false, '_shards' => {'one' => 'OK'}, - 'hits' => { 'total' => 100, 'hits' => (1..100).to_a.map { |i| { _id: i } } } } - - context "Response pagination" do - - setup do - @search = Elasticsearch::Model::Searching::SearchRequest.new ModelClass, '*' - @response = WillPaginateResponse.new ModelClass, @search, RESPONSE - @response.klass.stubs(:client).returns mock('client') - - @expected_methods = [ - # methods needed by WillPaginate::CollectionMethods - :current_page, - :offset, - :per_page, - :total_entries, - :length, - - # methods defined by WillPaginate::CollectionMethods - :total_pages, - :previous_page, - :next_page, - :out_of_bounds?, - ] - end - - should "have pagination methods" do - assert_respond_to @response, :paginate - - @expected_methods.each do |method| - assert_respond_to @response, method - end - end - - context "response.results" do - should "have pagination methods" do - @expected_methods.each do |method| - assert_respond_to @response.results, method - end - end - end - - context "response.records" do - should "have pagination methods" do - @expected_methods.each do |method| - @response.klass.stubs(:find).returns([]) - assert_respond_to @response.records, method - end - end - end - - context "#offset method" do - should "calculate offset using current_page and per_page" do - @response.per_page(3).page(3) - assert_equal 6, @response.offset - end - end - context "#length method" do - should "return count of paginated results" do - @response.per_page(3).page(3) - assert_equal 3, @response.length - end - end - - context "#paginate method" do - should "set from/size using defaults" do - @response.klass.client - .expects(:search) - .with do |definition| - assert_equal 0, definition[:from] - assert_equal 33, definition[:size] - true - end - .returns(RESPONSE) - - assert_nil @response.search.definition[:from] - assert_nil @response.search.definition[:size] - - @response.paginate(page: nil).to_a - assert_equal 0, @response.search.definition[:from] - assert_equal 33, @response.search.definition[:size] - end - - should "set from/size using default per_page" do - @response.klass.client - .expects(:search) - .with do |definition| - assert_equal 33, definition[:from] - assert_equal 33, definition[:size] - true - end - .returns(RESPONSE) - - assert_nil @response.search.definition[:from] - assert_nil @response.search.definition[:size] - - @response.paginate(page: 2).to_a - assert_equal 33, @response.search.definition[:from] - assert_equal 33, @response.search.definition[:size] - end - - should "set from/size using custom page and per_page" do - @response.klass.client - .expects(:search) - .with do |definition| - assert_equal 18, definition[:from] - assert_equal 9, definition[:size] - true - end - .returns(RESPONSE) - - assert_nil @response.search.definition[:from] - assert_nil @response.search.definition[:size] - - @response.paginate(page: 3, per_page: 9).to_a - assert_equal 18, @response.search.definition[:from] - assert_equal 9, @response.search.definition[:size] - end - - should "search for first page if specified page is < 1" do - @response.klass.client - .expects(:search) - .with do |definition| - assert_equal 0, definition[:from] - assert_equal 33, definition[:size] - true - end - .returns(RESPONSE) - - assert_nil @response.search.definition[:from] - assert_nil @response.search.definition[:size] - - @response.paginate(page: "-1").to_a - assert_equal 0, @response.search.definition[:from] - assert_equal 33, @response.search.definition[:size] - end - - should "use the param_name" do - @response.klass.client - .expects(:search) - .with do |definition| - assert_equal 10, definition[:from] - true - end - .returns(RESPONSE) - - @response.paginate(my_page: 2, per_page: 10, param_name: :my_page).to_a - end - end - - context "#page and #per_page shorthand methods" do - should "set from/size using default per_page" do - @response.page(5) - assert_equal 132, @response.search.definition[:from] - assert_equal 33, @response.search.definition[:size] - end - - should "set from/size when calling #page then #per_page" do - @response.page(5).per_page(3) - assert_equal 12, @response.search.definition[:from] - assert_equal 3, @response.search.definition[:size] - end - - should "set from/size when calling #per_page then #page" do - @response.per_page(3).page(5) - assert_equal 12, @response.search.definition[:from] - assert_equal 3, @response.search.definition[:size] - end - end - - context "#current_page method" do - should "return 1 by default" do - @response.paginate({}) - assert_equal 1, @response.current_page - end - - should "return current page number" do - @response.paginate(page: 3, per_page: 9) - assert_equal 3, @response.current_page - end - - should "return nil if not pagination set" do - assert_equal nil, @response.current_page - end - end - - context "#per_page method" do - should "return value set in paginate call" do - @response.paginate(per_page: 8) - assert_equal 8, @response.per_page - end - end - - context "#total_entries method" do - should "return total from response" do - @response.expects(:results).returns(mock('results', total: 100)) - assert_equal 100, @response.total_entries - end - end - end - - context "Multimodel response pagination" do - setup do - @multimodel = Elasticsearch::Model::Multimodel.new ModelClass - @search = Elasticsearch::Model::Searching::SearchRequest.new @multimodel, '*' - @response = WillPaginateResponse.new @multimodel, @search, RESPONSE - @response.klass.stubs(:client).returns mock('client') - - @expected_methods = [ - # methods needed by WillPaginate::CollectionMethods - :current_page, - :offset, - :per_page, - :total_entries, - :length, - - # methods defined by WillPaginate::CollectionMethods - :total_pages, - :previous_page, - :next_page, - :out_of_bounds?, - ] - end - - should "have pagination methods" do - assert_respond_to @response, :paginate - - @expected_methods.each do |method| - assert_respond_to @response, method - end - end - - context "response.results" do - should "have pagination methods" do - @expected_methods.each do |method| - assert_respond_to @response.results, method - end - end - end - - context "#offset method" do - should "calculate offset using current_page and per_page" do - @response.per_page(3).page(3) - assert_equal 6, @response.offset - end - end - context "#length method" do - should "return count of paginated results" do - @response.per_page(3).page(3) - assert_equal 3, @response.length - end - end - - context "#paginate method" do - should "set from/size using WillPaginate defaults, ignoring aggregated models configuration" do - @response.klass.client - .expects(:search) - .with do |definition| - assert_equal 0, definition[:from] - assert_equal ::WillPaginate.per_page, definition[:size] - true - end - .returns(RESPONSE) - - assert_nil @response.search.definition[:from] - assert_nil @response.search.definition[:size] - - @response.paginate(page: nil).to_a - assert_equal 0, @response.search.definition[:from] - assert_equal ::WillPaginate.per_page, @response.search.definition[:size] - end - - should "set from/size using default per_page, ignoring aggregated models' configuration" do - @response.klass.client - .expects(:search) - .with do |definition| - assert_equal ::WillPaginate.per_page, definition[:from] - assert_equal ::WillPaginate.per_page, definition[:size] - true - end - .returns(RESPONSE) - - assert_nil @response.search.definition[:from] - assert_nil @response.search.definition[:size] - - @response.paginate(page: 2).to_a - assert_equal ::WillPaginate.per_page, @response.search.definition[:from] - assert_equal ::WillPaginate.per_page, @response.search.definition[:size] - end - - should "set from/size using custom page and per_page" do - @response.klass.client - .expects(:search) - .with do |definition| - assert_equal 18, definition[:from] - assert_equal 9, definition[:size] - true - end - .returns(RESPONSE) - - assert_nil @response.search.definition[:from] - assert_nil @response.search.definition[:size] - - @response.paginate(page: 3, per_page: 9).to_a - assert_equal 18, @response.search.definition[:from] - assert_equal 9, @response.search.definition[:size] - end - - should "search for first page if specified page is < 1" do - @response.klass.client - .expects(:search) - .with do |definition| - assert_equal 0, definition[:from] - assert_equal ::WillPaginate.per_page, definition[:size] - true - end - .returns(RESPONSE) - - assert_nil @response.search.definition[:from] - assert_nil @response.search.definition[:size] - - @response.paginate(page: "-1").to_a - assert_equal 0, @response.search.definition[:from] - assert_equal ::WillPaginate.per_page, @response.search.definition[:size] - end - end - - context "#page and #per_page shorthand methods" do - should "set from/size using default per_page" do - @response.page(5) - assert_equal 120, @response.search.definition[:from] - assert_equal ::WillPaginate.per_page, @response.search.definition[:size] - end - - should "set from/size when calling #page then #per_page" do - @response.page(5).per_page(3) - assert_equal 12, @response.search.definition[:from] - assert_equal 3, @response.search.definition[:size] - end - - should "set from/size when calling #per_page then #page" do - @response.per_page(3).page(5) - assert_equal 12, @response.search.definition[:from] - assert_equal 3, @response.search.definition[:size] - end - end - - context "#current_page method" do - should "return 1 by default" do - @response.paginate({}) - assert_equal 1, @response.current_page - end - - should "return current page number" do - @response.paginate(page: 3, per_page: 9) - assert_equal 3, @response.current_page - end - - should "return nil if not pagination set" do - assert_equal nil, @response.current_page - end - end - - context "#per_page method" do - should "return value set in paginate call" do - @response.paginate(per_page: 8) - assert_equal 8, @response.per_page - end - end - - context "#total_entries method" do - should "return total from response" do - @response.expects(:results).returns(mock('results', total: 100)) - assert_equal 100, @response.total_entries - end - end - end -end diff --git a/elasticsearch-model/test/unit/response_records_test.rb b/elasticsearch-model/test/unit/response_records_test.rb deleted file mode 100644 index 8a78255d7..000000000 --- a/elasticsearch-model/test/unit/response_records_test.rb +++ /dev/null @@ -1,91 +0,0 @@ -require 'test_helper' - -class Elasticsearch::Model::RecordsTest < Test::Unit::TestCase - context "Response records" do - class DummyCollection - include Enumerable - - def each(&block); ['FOO'].each(&block); end - def size; ['FOO'].size; end - def empty?; ['FOO'].empty?; end - def foo; 'BAR'; end - end - - class DummyModel - def self.index_name; 'foo'; end - def self.document_type; 'bar'; end - - def self.find(*args) - DummyCollection.new - end - end - - RESPONSE = { 'hits' => { 'total' => 123, 'max_score' => 456, 'hits' => [{'_id' => '1', 'foo' => 'bar'}] } } - RESULTS = Elasticsearch::Model::Response::Results.new DummyModel, RESPONSE - - setup do - search = Elasticsearch::Model::Searching::SearchRequest.new DummyModel, '*' - search.stubs(:execute!).returns RESPONSE - - response = Elasticsearch::Model::Response::Response.new DummyModel, search - @records = Elasticsearch::Model::Response::Records.new DummyModel, response - end - - should "access the records" do - assert_respond_to @records, :records - assert_equal 1, @records.records.size - assert_equal 'FOO', @records.records.first - end - - should "delegate Enumerable methods to records" do - assert ! @records.empty? - assert_equal 'FOO', @records.first - end - - should "delegate methods to records" do - assert_respond_to @records, :foo - assert_equal 'BAR', @records.foo - end - - should "have each_with_hit method" do - @records.each_with_hit do |record, hit| - assert_equal 'FOO', record - assert_equal 'bar', hit.foo - end - end - - should "have map_with_hit method" do - assert_equal ['FOO---bar'], @records.map_with_hit { |record, hit| "#{record}---#{hit.foo}" } - end - - should "return the IDs" do - assert_equal ['1'], @records.ids - end - - context "with adapter" do - module DummyAdapter - module RecordsMixin - def records - ['FOOBAR'] - end - end - - def records_mixin - RecordsMixin - end; module_function :records_mixin - end - - should "delegate the records method to the adapter" do - Elasticsearch::Model::Adapter.expects(:from_class) - .with(DummyModel) - .returns(DummyAdapter) - - @records = Elasticsearch::Model::Response::Records.new DummyModel, - RESPONSE - - assert_equal ['FOOBAR'], @records.records - end - end - - end -end diff --git a/elasticsearch-model/test/unit/response_result_test.rb b/elasticsearch-model/test/unit/response_result_test.rb deleted file mode 100644 index c357d46ba..000000000 --- a/elasticsearch-model/test/unit/response_result_test.rb +++ /dev/null @@ -1,90 +0,0 @@ -require 'test_helper' -require 'active_support/json/encoding' - -class Elasticsearch::Model::ResultTest < Test::Unit::TestCase - context "Response result" do - - should "have method access to properties" do - result = Elasticsearch::Model::Response::Result.new foo: 'bar', bar: { bam: 'baz' } - - assert_respond_to result, :foo - assert_respond_to result, :bar - - assert_equal 'bar', result.foo - assert_equal 'baz', result.bar.bam - - assert_raise(NoMethodError) { result.xoxo } - end - - should "return _id as #id" do - result = Elasticsearch::Model::Response::Result.new foo: 'bar', _id: 42, _source: { id: 12 } - - assert_equal 42, result.id - assert_equal 12, result._source.id - end - - should "return _type as #type" do - result = Elasticsearch::Model::Response::Result.new foo: 'bar', _type: 'baz', _source: { type: 'BAM' } - - assert_equal 'baz', result.type - assert_equal 'BAM', result._source.type - end - - should "delegate method calls to `_source` when available" do - result = Elasticsearch::Model::Response::Result.new foo: 'bar', _source: { bar: 'baz' } - - assert_respond_to result, :foo - assert_respond_to result, :_source - assert_respond_to result, :bar - - assert_equal 'bar', result.foo - assert_equal 'baz', result._source.bar - assert_equal 'baz', result.bar - end - - should "delegate existence method calls to `_source`" do - result = Elasticsearch::Model::Response::Result.new foo: 'bar', _source: { bar: { bam: 'baz' } } - - assert_respond_to result._source, :bar? - assert_respond_to result, :bar? - - assert_equal true, result._source.bar? - assert_equal true, result.bar? - assert_equal false, result.boo? - - assert_equal true, result.bar.bam? - assert_equal false, result.bar.boo? - end - - should "delegate methods to @result" do - result = Elasticsearch::Model::Response::Result.new foo: 'bar' - - assert_equal 'bar', result.foo - assert_equal 'bar', result.fetch('foo') - assert_equal 'moo', result.fetch('NOT_EXIST', 'moo') - assert_equal ['foo'], result.keys - - assert_respond_to result, :to_hash - assert_equal({'foo' => 'bar'}, result.to_hash) - - assert_raise(NoMethodError) { result.does_not_exist } - end - - should "delegate existence method calls to @result" do - result = Elasticsearch::Model::Response::Result.new foo: 'bar', _source: { bar: 'bam' } - assert_respond_to result, :foo? - - assert_equal true, result.foo? - assert_equal false, result.boo? - assert_equal false, result._source.foo? - assert_equal false, result._source.boo? - end - - should "delegate as_json to @result even when ActiveSupport changed half of Ruby" do - result = Elasticsearch::Model::Response::Result.new foo: 'bar' - - result.instance_variable_get(:@result).expects(:as_json) - result.as_json(except: 'foo') - end - end -end diff --git a/elasticsearch-model/test/unit/response_results_test.rb b/elasticsearch-model/test/unit/response_results_test.rb deleted file mode 100644 index 900094bf5..000000000 --- a/elasticsearch-model/test/unit/response_results_test.rb +++ /dev/null @@ -1,34 +0,0 @@ -require 'test_helper' - -class Elasticsearch::Model::ResultsTest < Test::Unit::TestCase - context "Response results" do - class OriginClass - def self.index_name; 'foo'; end - def self.document_type; 'bar'; end - end - - RESPONSE = { 'hits' => { 'total' => 123, 'max_score' => 456, 'hits' => [{'foo' => 'bar'}] } } - - setup do - @search = Elasticsearch::Model::Searching::SearchRequest.new OriginClass, '*' - @response = Elasticsearch::Model::Response::Response.new OriginClass, @search - @results = Elasticsearch::Model::Response::Results.new OriginClass, @response - @search.stubs(:execute!).returns(RESPONSE) - end - - should "access the results" do - assert_respond_to @results, :results - assert_equal 1, @results.results.size - assert_equal 'bar', @results.results.first.foo - end - - should "delegate Enumerable methods to results" do - assert ! @results.empty? - assert_equal 'bar', @results.first.foo - end - - should "provide access to the raw response" do - assert_equal RESPONSE, @response.raw_response - end - end -end diff --git a/elasticsearch-model/test/unit/response_test.rb b/elasticsearch-model/test/unit/response_test.rb deleted file mode 100644 index e18f6c7ef..000000000 --- a/elasticsearch-model/test/unit/response_test.rb +++ /dev/null @@ -1,104 +0,0 @@ -require 'test_helper' - -class Elasticsearch::Model::ResponseTest < Test::Unit::TestCase - context "Response" do - class OriginClass - def self.index_name; 'foo'; end - def self.document_type; 'bar'; end - end - - RESPONSE = { 'took' => '5', 'timed_out' => false, '_shards' => {'one' => 'OK'}, 'hits' => { 'hits' => [] }, - 'aggregations' => {'foo' => {'bar' => 10}}, - 'suggest' => {'my_suggest' => [ { 'text' => 'foo', 'options' => [ { 'text' => 'Foo', 'score' => 2.0 }, { 'text' => 'Bar', 'score' => 1.0 } ] } ]}} - - setup do - @search = Elasticsearch::Model::Searching::SearchRequest.new OriginClass, '*' - @search.stubs(:execute!).returns(RESPONSE) - end - - should "access klass, response, took, timed_out, shards" do - response = Elasticsearch::Model::Response::Response.new OriginClass, @search - - assert_equal OriginClass, response.klass - assert_equal @search, response.search - assert_equal RESPONSE, response.response - assert_equal '5', response.took - assert_equal false, response.timed_out - assert_equal 'OK', response.shards.one - end - - should "wrap the raw Hash response in a HashWrapper" do - @search = Elasticsearch::Model::Searching::SearchRequest.new OriginClass, '*' - @search.stubs(:execute!).returns({'hits' => { 'hits' => [] }, 'aggregations' => { 'dates' => 'FOO' }}) - - response = Elasticsearch::Model::Response::Response.new OriginClass, @search - - assert_respond_to response.response, :aggregations - assert_equal 'FOO', response.response.aggregations.dates - end - - should "load and access the results" do - @search.expects(:execute!).returns(RESPONSE) - - response = Elasticsearch::Model::Response::Response.new OriginClass, @search - assert_instance_of Elasticsearch::Model::Response::Results, response.results - assert_equal 0, response.size - end - - should "load and access the records" do - @search.expects(:execute!).returns(RESPONSE) - - response = Elasticsearch::Model::Response::Response.new OriginClass, @search - assert_instance_of Elasticsearch::Model::Response::Records, response.records - assert_equal 0, response.size - end - - should "delegate Enumerable methods to results" do - @search.expects(:execute!).returns(RESPONSE) - - response = Elasticsearch::Model::Response::Response.new OriginClass, @search - assert response.empty? - end - - should "be initialized lazily" do - @search.expects(:execute!).never - - Elasticsearch::Model::Response::Response.new OriginClass, @search - end - - should "access the aggregations" do - @search.expects(:execute!).returns(RESPONSE) - - response = Elasticsearch::Model::Response::Response.new OriginClass, @search - assert_respond_to response, :aggregations - assert_kind_of Hashie::Mash, response.aggregations.foo - assert_equal 10, response.aggregations.foo.bar - end - - should "access the suggest" do - @search.expects(:execute!).returns(RESPONSE) - - response = Elasticsearch::Model::Response::Response.new OriginClass, @search - - assert_respond_to response, :suggestions - assert_kind_of Hashie::Mash, response.suggestions - assert_equal 'Foo', response.suggestions.my_suggest.first.options.first.text - end - - should "return array of terms from the suggestions" do - @search.expects(:execute!).returns(RESPONSE) - response = Elasticsearch::Model::Response::Response.new OriginClass, @search - - assert_not_empty response.suggestions - assert_equal [ 'Foo', 'Bar' ], response.suggestions.terms - end - - should "return empty array as suggest terms when there are no suggestions" do - @search.expects(:execute!).returns({}) - response = Elasticsearch::Model::Response::Response.new OriginClass, @search - - assert_empty response.suggestions - assert_equal [], response.suggestions.terms - end - end -end