cloudsearchable 0.0.1

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,71 @@
1
+ require 'spec_helper'
2
+ require 'test_classes/cloud_searchable_test_class'
3
+
4
+ describe Cloudsearchable do
5
+ let(:clazz){ CloudSearchableSampleClassFactory.call }
6
+
7
+ it 'can describe an index that returns ids for the class type' do
8
+ test_index = clazz.cloudsearch_index(:test_index)
9
+ test_index.should be_kind_of(Cloudsearchable::Domain)
10
+ test_index.fields.should have(4).items #3 explicit + 1 for the id of the object
11
+ end
12
+
13
+ it 'has a default index' do
14
+ clazz.cloudsearch_index.should be_kind_of(Cloudsearchable::Domain)
15
+ clazz.cloudsearch_index(:test_index).should_not be(clazz.cloudsearch_index)
16
+ end
17
+
18
+ it 'names domains consistent with CloudSearch limitations' do
19
+ clazz.cloudsearch_index(:test_index).name.should =~ /^[a-z][a-z0-9\-]+$/
20
+ end
21
+
22
+ describe 'an ordinary object' do
23
+ #An instance of the searchable class
24
+ let(:inst) do
25
+ inst = clazz.new
26
+ #arbitrary but plausible values for the core fields
27
+ inst.destroyed = false
28
+ inst.id = 42
29
+ inst.lock_version = 18
30
+ inst.customer = OpenStruct.new :id => 123
31
+ inst.name = "My Name"
32
+ inst
33
+ end
34
+
35
+ it 'supplies the right values to the fields' do
36
+ test_index = clazz.cloudsearch_index(:test_index)
37
+ test_index.fields[:test_class_id].value_for(inst).should be(inst.id)
38
+ test_index.fields[:customer_id].value_for(inst).should be(inst.customer)
39
+ test_index.fields[:test_name].value_for(inst).should be(inst.name)
40
+ test_index.fields[:helpfulness].value_for(inst).should be(1234)
41
+ end
42
+
43
+ it 'reindexes when told to' do
44
+ clazz.cloudsearch_index( ).should_receive(:post_record).with(inst, inst.id, inst.lock_version)
45
+ clazz.cloudsearch_index(:test_index).should_receive(:post_record).with(inst, inst.id, inst.lock_version)
46
+ inst.update_indexes
47
+ end
48
+
49
+ it 'generates a sensible addition sdf document' do
50
+ sdf = clazz.cloudsearch_index(:test_index).send :addition_sdf, inst, inst.id, inst.lock_version
51
+ sdf[:fields][:helpfulness].should be(1234)
52
+ end
53
+ end
54
+
55
+ describe 'a destroyed object' do
56
+ #An instance of the searchable class
57
+ let(:inst) do
58
+ inst = clazz.new
59
+ #arbitrary but plausible values for the core fields
60
+ inst.destroyed = true
61
+ inst
62
+ end
63
+
64
+ it 'reindexes when told to' do
65
+ clazz.cloudsearch_index( ).should_receive(:delete_record).with(inst.id, inst.lock_version)
66
+ clazz.cloudsearch_index(:test_index).should_receive(:delete_record).with(inst.id, inst.lock_version)
67
+ inst.update_indexes
68
+ end
69
+ end
70
+
71
+ end
@@ -0,0 +1,158 @@
1
+ require 'spec_helper'
2
+ require 'test_classes/cloud_searchable_test_class'
3
+
4
+ describe Cloudsearchable::Domain do
5
+ before(:each) do
6
+ fake_client = double('client')
7
+ CloudSearch.stub(:client).and_return(fake_client)
8
+ end
9
+
10
+ #
11
+ # First call to describe_domains returns it needs reindexing,
12
+ # Second call returns that it is processing,
13
+ # Third call returns that it is done processing
14
+ #
15
+ let(:needs_rebuild_domain) do
16
+ described_class.new('nrb-index').tap do |dom|
17
+ resp = describe_domain_response(dom.name)
18
+ CloudSearch.client.stub(:describe_domains).with(:domain_names => [dom.name]).
19
+ and_return(
20
+ describe_domain_response(dom.name, :required_index_documents => true),
21
+ describe_domain_response(dom.name, :processing => true),
22
+ describe_domain_response(dom.name)
23
+ )
24
+ end
25
+ end
26
+
27
+ # A domain name named 'my-index'
28
+ let(:domain) do
29
+ described_class.new('my-index').tap do |dom|
30
+ CloudSearch.client.stub(:describe_domains).and_return(describe_domain_response(dom.name))
31
+ CloudSearch.client.stub(:describe_domains).with(:domain_names => [dom.name]).and_return(describe_domain_response(dom.name))
32
+ end
33
+ end
34
+
35
+ let(:empty_domain) do
36
+ described_class.new('my-index').tap do |dom|
37
+ CloudSearch.client.stub(:describe_domains).and_return({})
38
+ end
39
+ end
40
+
41
+ it 'can be instantiated' do
42
+ index = domain
43
+ index.name.should end_with('my-index')
44
+ end
45
+
46
+ it 'can haz a literal field' do
47
+ index = domain
48
+ index.add_field(:literary, :literal) { nil }
49
+ index.fields[:literary].type.should eq(:literal)
50
+ end
51
+
52
+ it 'can be initialized with a nested class' do
53
+ class OuterClassForCloudSearch
54
+ class InnerClass
55
+ include Cloudsearchable
56
+ end
57
+ end
58
+
59
+ OuterClassForCloudSearch::InnerClass.cloudsearch_prefix.should match(/^[A-Za-z0-9_-]+$/)
60
+ Object.instance_eval { remove_const :OuterClassForCloudSearch }
61
+ end
62
+
63
+ context "SDF documents" do
64
+ let(:object) { OpenStruct.new(:field_with_nil_value => nil, :field_with_present_value => 42) }
65
+ subject do
66
+ described_class.new('my-index').tap do |d|
67
+ d.add_field(:field_with_nil_value, :literal)
68
+ d.add_field(:field_with_present_value, :literal)
69
+ end
70
+ end
71
+
72
+ it "should generate present fields" do
73
+ subject.addition_sdf(object, "id", 1)[:fields][:field_with_present_value].should == 42
74
+ end
75
+
76
+ it "should not generate nil fields" do
77
+ subject.addition_sdf(object, "id", 1)[:fields][:field_with_nil_value].should be_nil
78
+ end
79
+ end
80
+
81
+ it 'raises if the domain cannot be found' do
82
+ expect { empty_domain.send(:search_endpoint) }.to raise_error( Cloudsearchable::Domain::DomainNotFound,
83
+ /Cloudsearchable could not find the domain/)
84
+ end
85
+
86
+ #
87
+ # A test for the accidental clashing of domain caching
88
+ # on a class variable
89
+ #
90
+ it 'caches endpoints for multiple domains' do
91
+ domain.send(:search_endpoint).should_not eq(needs_rebuild_domain.send(:search_endpoint))
92
+ end
93
+
94
+ it 'endpoint selected is based on the domain name' do
95
+ domain.send(:search_endpoint).should eq describe_domain_response(domain.name)[:domain_status_list][0][:search_service][:endpoint]
96
+ domain.send(:doc_endpoint).should eq describe_domain_response(domain.name)[:domain_status_list][0][:doc_service][:endpoint]
97
+ end
98
+
99
+ it 'sleeps, waiting for reindexing' do
100
+ CloudSearch.client.should_receive(:index_documents).with(:domain_name => needs_rebuild_domain.name)
101
+ CloudSearch.client.should_receive(:describe_domains).exactly(3).times
102
+ needs_rebuild_domain.apply_changes(3).should == true
103
+ end
104
+
105
+ protected
106
+
107
+ #
108
+ # A mockup of the response to a describe_domain request
109
+ #
110
+ def describe_domain_response(domain_name, options = {})
111
+ {
112
+ :domain_status_list=> [
113
+ {
114
+ :search_partition_count=>1,
115
+ :search_service=>{
116
+ :arn=>"arn:aws:cs:us-east-1:510523556749:search/#{domain_name}",
117
+ :endpoint=>"search-#{domain_name}-7bq6utq4fdrwax5r6irje7xlra.us-east-1.cloudsearch.amazonaws.com"
118
+ },
119
+ :num_searchable_docs=>23,
120
+ :search_instance_type=>"search.m1.small",
121
+ :created=>true,
122
+ :domain_id=>"510523556749/#{domain_name}",
123
+ :processing=> options.fetch(:processing, false),
124
+ :search_instance_count=>1,
125
+ :domain_name=>"#{domain_name}",
126
+ :requires_index_documents=> options.fetch(:required_index_documents,false),
127
+ :deleted=>false,
128
+ :doc_service=>{
129
+ :arn=>"arn:aws:cs:us-east-1:510523556749:doc/#{domain_name}",
130
+ :endpoint=>"doc-#{domain_name}-7bq6utq4fdrwax5r6irje7xlra.us-east-1.cloudsearch.amazonaws.com"
131
+ }
132
+ },
133
+ {:search_partition_count=>1,
134
+ :search_service=>
135
+ {:arn=>
136
+ "arn:aws:cs:us-east-1:510523556749:search/dev-llarue-collection-items",
137
+ :endpoint=>
138
+ "search-dev-llarue-collection-items-hjopg2yzhcjdd4qxeglr2v5v7m.us-east-1.cloudsearch.amazonaws.com"},
139
+ :num_searchable_docs=>2,
140
+ :search_instance_type=>"search.m1.small",
141
+ :created=>true,
142
+ :domain_id=>"510523556749/dev-llarue-collection-items",
143
+ :processing=>false,
144
+ :search_instance_count=>1,
145
+ :domain_name=>"dev-llarue-collection-items",
146
+ :requires_index_documents=>false,
147
+ :deleted=>false,
148
+ :doc_service=>
149
+ {:arn=>"arn:aws:cs:us-east-1:510523556749:doc/dev-llarue-collection-items",
150
+ :endpoint=>
151
+ "doc-dev-llarue-collection-items-hjopg2yzhcjdd4qxeglr2v5v7m.us-east-1.cloudsearch.amazonaws.com"}}
152
+ ],
153
+ :response_metadata=>{
154
+ :request_id=>"7d9487a7-1c9f-11e2-9f96-0958b8a97a74"
155
+ }
156
+ }
157
+ end
158
+ end
@@ -0,0 +1,30 @@
1
+ require 'spec_helper'
2
+ require 'ostruct'
3
+
4
+ describe Cloudsearchable::Field do
5
+ it 'has a name' do
6
+ field = described_class.new 'fnord', :literal
7
+ field.name.should eq(:fnord)
8
+ end
9
+
10
+ it 'can find its value' do
11
+ test_value = nil
12
+ field = described_class.new('foo', :literal, :source => Proc.new { test_value })
13
+ test_value = 123
14
+ field.value_for(Object.new).should eq(test_value)
15
+
16
+ record = OpenStruct.new :a => test_value
17
+ field2 = described_class.new('bar', :literal, :source => :a)
18
+ field.value_for(record).should eq(test_value)
19
+ end
20
+
21
+ it 'generates a field definition' do
22
+ domain_name = 'narnia'
23
+ field = described_class.new('fnord', :literal, :search_enabled => true)
24
+ CloudSearch.client.should_receive(:define_index_field) do |call|
25
+ call[:domain_name].should eq(domain_name)
26
+ call[:index_field][:literal_options][:search_enabled].should be_true
27
+ end
28
+ field.define_in_domain domain_name
29
+ end
30
+ end
@@ -0,0 +1,305 @@
1
+ require 'spec_helper'
2
+ require 'test_classes/cloud_searchable_test_class'
3
+
4
+ describe Cloudsearchable::Query do
5
+ let(:clazz){ CloudSearchableSampleClassFactory.call }
6
+
7
+ it 'can build a simple search query' do
8
+ clazz.search.where(:customer_id, :eq, 12345).query.to_q[:bq].should =~ /customer_id:'12345'/
9
+ end
10
+
11
+ it 'chains' do
12
+ query = clazz.search.where(customer_id: 12345).where(helpfulness: 42).query.to_q[:bq]
13
+ query.should =~ /customer_id:'12345'/
14
+ query.should =~ /helpfulness:'42'/
15
+ end
16
+
17
+ it 'can build a query with "not equal to" condition' do
18
+ query = clazz.search.where(:customer_id, :!=, 1234).query.to_q[:bq].should =~ /\(not customer_id:'1234'\)/
19
+ end
20
+
21
+ it 'can build a query from a hash' do
22
+ query = clazz.search.where(customer_id: 12345, helpfulness: 42).query.to_q[:bq]
23
+ query.should =~ /customer_id:'12345'/
24
+ query.should =~ /helpfulness:'42'/
25
+ end
26
+
27
+ it "doesn't build queries without a query term" do
28
+ expect do
29
+ query = clazz.search.limit(10).query.to_q
30
+ end.to raise_exception
31
+ end
32
+
33
+ describe '#where' do
34
+ describe 'uint data type' do
35
+ describe 'within_range' do
36
+ it 'supports range query with uint fields' do
37
+ clazz.search.where(:helpfulness, :within_range, "0..#{123}").query.to_q[:bq].should =~ /helpfulness:0..123/
38
+ end
39
+
40
+ it 'supports range query with uint fields using a ruby range' do
41
+ clazz.search.where(:helpfulness, :within_range, 0..123).query.to_q[:bq].should =~ /helpfulness:0..123/
42
+ end
43
+ end
44
+
45
+ describe 'inequalities' do
46
+ it 'supports greater-than with uint fields' do
47
+ clazz.search.where(:helpfulness, :>, 123).query.to_q[:bq].should =~ /helpfulness:124../
48
+ end
49
+
50
+ it 'supports greater-than-or-equal-to with uint fields' do
51
+ clazz.search.where(:helpfulness, :>=, 123).query.to_q[:bq].should =~ /helpfulness:123../
52
+ end
53
+
54
+ it 'supports less-than with uint fields' do
55
+ clazz.search.where(:helpfulness, :<, 123).query.to_q[:bq].should =~ /helpfulness:..122/
56
+ end
57
+
58
+ it 'supports less-than-or-equal-to with uint fields' do
59
+ clazz.search.where(:helpfulness, :<=, 123).query.to_q[:bq].should =~ /helpfulness:..123/
60
+ end
61
+
62
+ it 'does not permit inequality operators with non-integer fields' do
63
+ [:>, :>=, :<, :<=].each do |op|
64
+ ['123', nil, 12.0].each do |value|
65
+ expect { clazz.search.where(:helpfulness, op, value).query.to_q[:bq] }.to raise_error
66
+ end
67
+ end
68
+ end
69
+ end
70
+ end
71
+ end
72
+
73
+
74
+ it 'supports querying for any of several values of a field' do
75
+ clazz.search.where(:category, :any, %w{big small}).query.to_q[:bq].should include("(or category:'big' category:'small')")
76
+ end
77
+
78
+ it 'supports text method' do
79
+ query = clazz.search.text('test').query.to_q[:q]
80
+ query.should =~ /test/
81
+ end
82
+
83
+ it 'supports chaining text and where clauses together' do
84
+ query = clazz.search.text('test').where(:featured, :==, 'f').query
85
+ query.to_q[:q].should =~ /test/
86
+ query.to_q[:bq].should =~ /featured:'f'/
87
+ end
88
+
89
+ it 'supports ordering with a rank expression' do
90
+ clazz.search.where(customer_id: 12345).order('-helpfulness').query.to_q[:rank].should eq '-helpfulness'
91
+ end
92
+
93
+ it 'supports limit' do
94
+ clazz.search.where(customer_id: 12345).limit(10).query.to_q[:size].should eq 10
95
+ end
96
+
97
+ it 'has high default limit' do
98
+ clazz.search.where(customer_id: 12345).query.to_q[:size].should eq 100000
99
+ end
100
+
101
+ it 'supports offset' do
102
+ clazz.search.where(customer_id: 12345).offset(100).query.to_q[:start].should eq 100
103
+ end
104
+
105
+ context 'queries' do
106
+ before(:each) do
107
+ clazz.cloudsearch_index.should_receive(:execute_query).and_return(cloudsearch_response)
108
+ end
109
+
110
+ context 'query warning' do
111
+ before(:each) do
112
+ clazz.stub(:find).and_return([])
113
+ Cloudsearchable.logger.should_receive(:warn).with(/CS-InvalidFieldOrRankAliasInRankParameter/)
114
+ end
115
+
116
+ let(:query){clazz.search.where(customer_id: 12345).order("-adult")}
117
+ let(:cloudsearch_response) do
118
+ # generated by ranking a literal field that is search-enabled but not result-enabled
119
+ {
120
+ "rank" => "adult",
121
+ "match-expr" => "(label initialized_at:1363464074..1366056074)",
122
+ "hits" => {
123
+ "found" => 285,
124
+ "start" => 0,
125
+ "hit" => [
126
+ {"id" => "40bdd5072b6dbae6245fe4ee837d22e3","data" => {"collection_item_id" => ["PCxSz65GIcZTtc0UpRdT-i--w-1365550370"]}},
127
+ {"id" => "00af8f5f96aa1db7aff77be5651b3bb1","data" => {"collection_item_id" => ["PCxhksJTLRYnoGXvwZik82Fkw-1365020313"]}},
128
+ {"id" => "00b6ac84e3ae402e7698959bf692a53e","data" => {"collection_item_id" => ["PCxs-fIVZnBcTzZ4MtfDguS1A-1365020274"]}},
129
+ {"id" => "018fdee653bff74abd12ac30152a2837","data" => {"collection_item_id" => ["PCxmAGHFtAgyqUrgI3HgM_P6Q-1365548349"]}},
130
+ {"id" => "01d062d24c389906eea2d16b8193eb56","data" => {"collection_item_id" => ["PCxqjaTmwydKM82NqymbryNfg-1365470479"]}},
131
+ {"id" => "01e3ee5d848a30385a4e90eb851b094d","data" => {"collection_item_id" => ["PCxSz65GIcZTtc0UpRdT-i--w-1365550369"]}},
132
+ {"id" => "01fca44cc596adb295ca6ee9f9f36499","data" => {"collection_item_id" => ["PCx7XKbKwOVf1VvEWvTl5c1Eg-1365020176"]}},
133
+ {"id" => "02b85c9835b5045065ee389954a60c5f","data" => {"collection_item_id" => ["PCxp_xid_WeTfTmb5MySEfxhQ-1365115565"]}},
134
+ {"id" => "040c01be434552a1d9e99eef9db87bdd","data" => {"collection_item_id" => ["PCxLOYzA4bCt7-bP6wsZnl-ow-1365020297"]}},
135
+ {"id" => "048567c755e30d6d64d757508f1feaa0","data" => {"collection_item_id" => ["PCxJhhnpYkeSKrOxteQo5Jckw-1365115667"]}}
136
+ ]
137
+ },
138
+ "info" => {
139
+ "rid" => "7df344e77e1076a903e1f2dc1effcf3dde0a89442fb459d00a6e60ac64b8bbfcab1fbc5b35c10949",
140
+ "time-ms" => 3,
141
+ "cpu-time-ms" => 0,
142
+ "messages" => [
143
+ {
144
+ "severity" => "warning",
145
+ "code" => "CS-InvalidFieldOrRankAliasInRankParameter",
146
+ "host" => "7df344e77e1076a9884a6c43665da57c",
147
+ "message" => "Unable to create score object for rank 'adult'"
148
+ }
149
+ ]
150
+ }
151
+ }
152
+ end
153
+
154
+ it 'causes WarningInQueryResult exception' do
155
+ lambda{ query.to_a }.should raise_error(Cloudsearchable::WarningInQueryResult)
156
+ end
157
+
158
+ it 'takes a :fatal_warnings option, and when set to false, does not raise' do
159
+ sample_query = Cloudsearchable::QueryChain.new(double, fatal_warnings: false)
160
+ sample_query.instance_variable_get(:@fatal_warnings).should eq false
161
+
162
+ q = query
163
+ q.query.instance_variable_set(:@fatal_warnings, false)
164
+ lambda{ q.to_a }.should_not raise_error(Cloudsearchable::WarningInQueryResult)
165
+ end
166
+ end
167
+
168
+ context 'valid query results' do
169
+ let(:customer_id){ '12345' }
170
+ let(:other_customer_id){ 'foo' }
171
+
172
+ let(:cloudsearch_response) do
173
+ {
174
+ "rank"=>"-text_relevance",
175
+ "match-expr"=>"(label customer_id:'12345')",
176
+ "hits"=>{
177
+ "found"=>11,
178
+ "start"=>0,
179
+ "info"=>{
180
+ "rid"=>"e2467862eecf73ec8dfcfe0cba1893abbe2e8803402f4da65b1195593c0f78ec3e8f1d29f6e40723",
181
+ "time-ms"=>2,
182
+ "cpu-time-ms"=>0
183
+ },
184
+ "hit"=>[
185
+ {"id"=>"0633e1c9793f5288c58b664356533e81", "data"=>{"test_class_id"=>["ANINSTANCEID"]}},
186
+ # {"id"=>"04931ebede796ae8b435f1fd5291e772", "data"=>{"test_class_id"=>["PCxTj26ZRmV_EnHigQWx0S06w"]}},
187
+ # {"id"=>"72159a172d3043bfcdadb5244862b9ee", "data"=>{"test_class_id"=>["PCxS_apFtZMrKuqyPhFNstzMQ"]}},
188
+ # {"id"=>"1eb815b075bc005e97dc5827e53b9615", "data"=>{"test_class_id"=>["PCxSksjDUBehPWhYYW2Dtj4KQ"]}},
189
+ # {"id"=>"3e4950b829456b13bf1460b25a7aca26", "data"=>{"test_class_id"=>["PCx1oiyh6vrHGSeLvis4USMfQ"]}},
190
+ # {"id"=>"00b441f55fff86d2d746227988da77a9", "data"=>{"test_class_id"=>["PCxpt-aW8topsnTGs-AIkzWCA"]}},
191
+ # {"id"=>"919ea27d21bbdc07ead4688a0d7ceca1", "data"=>{"test_class_id"=>["PCxFHGLbGJ2mzau_a6-gh5ORw"]}},
192
+ # {"id"=>"c663c2d9af342b0038fc808322143cfd", "data"=>{"test_class_id"=>["PCxyFwShwjWBp_WiXB0rFb2WA"]}},
193
+ # {"id"=>"de8f00af5636393e2553c4b4710d3393", "data"=>{"test_class_id"=>["PCxnqXfm8McflBgi4HsYoUXVw"]}},
194
+ # {"id"=>"e297cf21741a4c43697ea2586164a987", "data"=>{"test_class_id"=>["PCxrdk8gAEVbkuUCazu2-qLjQ"]}}
195
+ ]
196
+ }
197
+ }
198
+ end
199
+
200
+ it 'materializes' do
201
+ clazz.should_receive(:find).with(["ANINSTANCEID"]).and_return([customer_id])
202
+ query = clazz.search.where(customer_id: 12345)
203
+ query.to_a.should == [customer_id]
204
+ end
205
+
206
+ it 'materializes db results only once' do
207
+ expected_results = [customer_id, other_customer_id]
208
+ clazz.should_receive(:find).once.and_return(expected_results)
209
+
210
+ query = clazz.search.where(customer_id: 12345)
211
+ query.materialize!
212
+ query.materialize!
213
+ end
214
+
215
+ it 'should not materialize if only asking for found_count' do
216
+ clazz.should_not_receive(:find)
217
+ clazz.search.where(customer_id: 12345).found_count
218
+ end
219
+
220
+ it 'supports each for multiple results' do
221
+ expected_results = [customer_id, other_customer_id]
222
+ clazz.should_receive(:find).with(["ANINSTANCEID"]).and_return(expected_results)
223
+
224
+ results = clazz.search.where(customer_id: 12345).to_a
225
+ (0..results.length).each{ |i| results[i].should eq expected_results[i] }
226
+ end
227
+
228
+ it 'supports each for single results' do
229
+ clazz.should_receive(:find).with(["ANINSTANCEID"]).and_return(customer_id)
230
+
231
+ results = clazz.search.where(customer_id: 12345).to_a
232
+ results.each{ |r| r.should eq customer_id }
233
+ end
234
+
235
+ it 'supports each for nil result' do
236
+ clazz.should_receive(:find).with(["ANINSTANCEID"]).and_return(nil)
237
+
238
+ results = clazz.search.where(customer_id: 12345).to_a
239
+ results.each{ |r| r.should_not be }
240
+ end
241
+
242
+ it 'uses materialized method' do
243
+ clazz.should_receive(:another_find).with(["ANINSTANCEID"]).and_return(customer_id)
244
+ clazz.materialize_method :another_find
245
+ clazz.search.where(customer_id: 12345).to_a
246
+ end
247
+
248
+ it 'returns the correct found count' do
249
+ clazz.search.where(customer_id: 12345).found_count.should == 11
250
+ end
251
+ end
252
+
253
+ context 'invalid query results' do
254
+ let(:cloudsearch_response) do
255
+ {
256
+ "error"=>"info",
257
+ "rid"=>"6ddcaa561c05c4cc85ddb10cb46568af2ef64b0583910e32210f551c238586e40fc3abe629ca87b250796d395a628af6",
258
+ "time-ms"=>20,
259
+ "cpu-time-ms"=>0,
260
+ "messages"=>[
261
+ {
262
+ "severity"=>"fatal",
263
+ "code"=>"CS-UnknownFieldInMatchExpression",
264
+ "message"=>"Field 'asdf' is not defined in the metadata for this collection."
265
+ }
266
+ ]
267
+ }
268
+ end
269
+
270
+ it 'raises an exception when requesting found count with an error response' do
271
+ expect { clazz.search.where(customer_id: 12345).found_count }.to raise_error
272
+ end
273
+ end
274
+
275
+ context 'empty results with non-empty data' do
276
+ let(:cloudsearch_response) do
277
+ {
278
+ # Empty-yet-present data may occur with a NOT query, such as "(not customer_id:'XYZ')".
279
+ # Refer to: https://aws.amazon.com/support/case?caseId=107084141&language=en
280
+ "rank" => "-text_relevance",
281
+ "match-expr" => "(not customer_id:'A3E4T85Q6WPY4F')",
282
+ "hits" => {
283
+ "found" => 2087,
284
+ "start" => 0,
285
+ "hit" => [
286
+ {"id" => "fb9fb53e32c4b3714cf39be4b855d34b", "data" => { "test_class_id" => []}},
287
+ ]
288
+ },
289
+ "info" => {
290
+ "rid" => "621cf310b88f32076b1908e45b4930aafb872497bdbf3b5e64065619c0dcec96bbe513281093d6c7",
291
+ "time-ms" => 3,
292
+ "cpu-time-ms" => 0
293
+ }
294
+ }
295
+ end
296
+
297
+ it 'does not raise an exception' do
298
+ clazz.should_receive(:find).with([]).and_return(nil)
299
+ clazz.search.where(:customer_id, :!=, 'ABCDE')
300
+ expect { clazz.search.where(:customer_id, :!=, 'ABCDE').to_a }.to_not raise_error
301
+ end
302
+ end
303
+ end
304
+
305
+ end