supplejack_client 1.0.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (47) hide show
  1. checksums.yaml +7 -0
  2. data/.gitignore +27 -0
  3. data/.rspec +2 -0
  4. data/Gemfile +16 -0
  5. data/Guardfile +24 -0
  6. data/LICENSE.txt +674 -0
  7. data/README.md +153 -0
  8. data/Rakefile +9 -0
  9. data/lib/generators/locales/en.yml +11 -0
  10. data/lib/generators/supplejack/install_generator.rb +28 -0
  11. data/lib/generators/templates/README +19 -0
  12. data/lib/generators/templates/supplejack_client.rb +120 -0
  13. data/lib/supplejack/config.rb +116 -0
  14. data/lib/supplejack/controllers/helpers.rb +172 -0
  15. data/lib/supplejack/engine.rb +20 -0
  16. data/lib/supplejack/exceptions.rb +17 -0
  17. data/lib/supplejack/facet.rb +33 -0
  18. data/lib/supplejack/item.rb +94 -0
  19. data/lib/supplejack/item_relation.rb +73 -0
  20. data/lib/supplejack/log_subscriber.rb +58 -0
  21. data/lib/supplejack/paginated_collection.rb +61 -0
  22. data/lib/supplejack/record.rb +147 -0
  23. data/lib/supplejack/request.rb +95 -0
  24. data/lib/supplejack/search.rb +346 -0
  25. data/lib/supplejack/url_formats/item_hash.rb +208 -0
  26. data/lib/supplejack/user.rb +132 -0
  27. data/lib/supplejack/user_set.rb +349 -0
  28. data/lib/supplejack/user_set_relation.rb +143 -0
  29. data/lib/supplejack/util.rb +120 -0
  30. data/lib/supplejack/version.rb +10 -0
  31. data/lib/supplejack_client.rb +29 -0
  32. data/spec/spec_helper.rb +23 -0
  33. data/spec/supplejack/controllers/helpers_spec.rb +277 -0
  34. data/spec/supplejack/facet_spec.rb +44 -0
  35. data/spec/supplejack/item_relation_spec.rb +111 -0
  36. data/spec/supplejack/item_spec.rb +115 -0
  37. data/spec/supplejack/log_subscriber_spec.rb +40 -0
  38. data/spec/supplejack/paginated_collection_spec.rb +43 -0
  39. data/spec/supplejack/record_spec.rb +255 -0
  40. data/spec/supplejack/request_spec.rb +195 -0
  41. data/spec/supplejack/search_spec.rb +727 -0
  42. data/spec/supplejack/url_formats/item_hash_spec.rb +341 -0
  43. data/spec/supplejack/user_set_relation_spec.rb +149 -0
  44. data/spec/supplejack/user_set_spec.rb +465 -0
  45. data/spec/supplejack/user_spec.rb +159 -0
  46. data/supplejack_client.gemspec +30 -0
  47. metadata +159 -0
@@ -0,0 +1,195 @@
1
+ # The Supplejack Client code is Crown copyright (C) 2014, New Zealand Government,
2
+ # and is licensed under the GNU General Public License, version 3.
3
+ # See https://github.com/DigitalNZ/supplejack_client for details.
4
+ #
5
+ # Supplejack was created by DigitalNZ at the National Library of NZ
6
+ # and the Department of Internal Affairs. http://digitalnz.org/supplejack
7
+
8
+ require 'spec_helper'
9
+
10
+ class Test
11
+ include Supplejack::Request
12
+ end
13
+
14
+ module Supplejack
15
+ describe Request do
16
+ before(:each) do
17
+ @test = Test.new
18
+ Supplejack.stub(:api_key) { '123' }
19
+ Supplejack.stub(:api_url) { 'http://api.org' }
20
+ Supplejack.stub(:timeout) { 20 }
21
+ end
22
+
23
+ describe '#get' do
24
+ before(:each) do
25
+ RestClient::Request.stub(:execute).and_return(%{ {"search": {}} })
26
+ end
27
+
28
+ it 'serializes the parameters in the url' do
29
+ RestClient::Request.should_receive(:execute).with(hash_including(:url => "http://api.org/records.json?#{{:and => {:name => 'John'}}.to_query}&api_key=123"))
30
+ @test.get('/records', {:and => {:name => 'John'}})
31
+ end
32
+
33
+ it 'parses the JSON returned' do
34
+ RestClient::Request.stub(:execute).and_return(%{ {"search": {}} })
35
+ @test.get('/records').should eq({'search' => {}})
36
+ end
37
+
38
+ it 'logs a error correctly when the response from the API failed' do
39
+ RestClient::Request.stub(:execute).and_return(nil)
40
+ @subscriber = Supplejack::LogSubscriber.new
41
+ Supplejack::LogSubscriber.stub(:new) { @subscriber }
42
+ @subscriber.should_receive(:log_request)
43
+ @test.get('/records')
44
+ end
45
+
46
+ context 'request format' do
47
+ it 'executes a request in a json format' do
48
+ RestClient::Request.should_receive(:execute).with(hash_including(:url => 'http://api.org/records.json?api_key=123'))
49
+ @test.get('/records')
50
+ end
51
+
52
+ it 'overrides the response format with xml' do
53
+ RestClient::Request.should_receive(:execute).with(hash_including(:url => 'http://api.org/records.xml?api_key=123'))
54
+ @test.get('/records', {}, {:format => :xml})
55
+ end
56
+ end
57
+
58
+ context 'api key' do
59
+ it 'overrides the api key' do
60
+ RestClient::Request.should_receive(:execute).with(hash_including(:url => 'http://api.org/records.json?api_key=456'))
61
+ @test.get('/records', {:api_key => '456'})
62
+ end
63
+ end
64
+
65
+ context 'timeout' do
66
+ it 'calculates the timeout' do
67
+ @test.should_receive(:timeout).with(hash_including({:timeout => 60}))
68
+ @test.get('/', {}, {:timeout => 60})
69
+ end
70
+ end
71
+ end
72
+
73
+ describe '#post' do
74
+ before(:each) do
75
+ RestClient::Request.stub(:execute)
76
+ end
77
+
78
+ it 'executes a post request' do
79
+ RestClient::Request.should_receive(:execute).with(hash_including(:method => :post))
80
+ @test.post('/records/1/ucm', {})
81
+ end
82
+
83
+ it 'passes the payload along' do
84
+ payload = {'ucm_record' => {:name => 'geocords', :value => '1234'}}
85
+ RestClient::Request.should_receive(:execute).with(hash_including(:payload => payload.to_json))
86
+ @test.post('/records/1/ucm', {}, payload)
87
+ end
88
+
89
+ it 'adds the extra parameters to the post request' do
90
+ @test.should_receive(:full_url).with('/records/1/ucm', nil, {api_key: '12344'})
91
+ @test.post('/records/1/ucm', {api_key: '12344'}, {})
92
+ end
93
+
94
+ it 'adds json headers and converts the payload into json' do
95
+ RestClient::Request.should_receive(:execute).with(hash_including(:headers => {:content_type => :json, :accept => :json}, :payload => {records: [{record_id: 1, position: 1}, {record_id:2, position:2}]}.to_json))
96
+ @test.post('/records/1/ucm', {}, {records: [{record_id: 1, position: 1}, {record_id:2, position:2}]})
97
+ end
98
+
99
+ it 'parses the JSON response' do
100
+ RestClient::Request.stub(:execute) { {user: {name: 'John'}}.to_json }
101
+ @test.post('/users', {}, {}).should eq({'user' => {'name' => 'John'}})
102
+ end
103
+ end
104
+
105
+ describe '#delete' do
106
+ before(:each) do
107
+ RestClient::Request.stub(:execute)
108
+ end
109
+
110
+ it 'executes a delete request' do
111
+ RestClient::Request.should_receive(:execute).with(hash_including(:method => :delete))
112
+ @test.delete('/records/1/ucm/1')
113
+ end
114
+
115
+ it 'adds the extra parameters to the delete request' do
116
+ @test.should_receive(:full_url).with('/records/1/ucm/1', nil, {api_key: '12344'})
117
+ @test.delete('/records/1/ucm/1', {api_key: '12344'})
118
+ end
119
+ end
120
+
121
+ describe '#put' do
122
+ before(:each) do
123
+ RestClient::Request.stub(:execute)
124
+ end
125
+
126
+ it 'executes a put request' do
127
+ RestClient::Request.should_receive(:execute).with(hash_including(:method => :put))
128
+ @test.put('/records/1/ucm/1')
129
+ end
130
+
131
+ it 'passes the payload along' do
132
+ RestClient::Request.should_receive(:execute).with(hash_including(:payload => {:name => 1}.to_json))
133
+ @test.put('/records/1/ucm/1', {}, {:name => 1})
134
+ end
135
+
136
+ it 'adds the extra parameters to the put request' do
137
+ @test.should_receive(:full_url).with('/records/1/ucm/1', nil, {api_key: '12344'})
138
+ @test.put('/records/1/ucm/1', {api_key: '12344'}, {})
139
+ end
140
+
141
+ it 'adds json headers and converts the payload into json' do
142
+ RestClient::Request.should_receive(:execute).with(hash_including(:headers => {:content_type => :json, :accept => :json}, :payload => {records: [1,2,3]}.to_json))
143
+ @test.put('/records/1/ucm/1', {}, {records: [1,2,3]})
144
+ end
145
+
146
+ it 'parses the JSON response' do
147
+ RestClient::Request.stub(:execute) { {user: {name: 'John'}}.to_json }
148
+ @test.put('/users/1', {}, {}).should eq({'user' => {'name' => 'John'}})
149
+ end
150
+ end
151
+
152
+ describe '#timeout' do
153
+ it 'defaults to the timeout in the configuration' do
154
+ @test.send(:timeout).should eq 20
155
+ end
156
+
157
+ it 'defaults to 30 when not set in the configuration' do
158
+ Supplejack.stub(:timeout) { nil }
159
+ @test.send(:timeout).should eq 30
160
+ end
161
+
162
+ it 'overrides the timeout' do
163
+ @test.send(:timeout, {:timeout => 60}).should eq 60
164
+ end
165
+ end
166
+
167
+ describe '#full_url' do
168
+ it 'returns the full url with default api_url, format and api_key' do
169
+ @test.send(:full_url, '/records').should eq('http://api.org/records.json?api_key=123')
170
+ end
171
+
172
+ it 'overrides the format' do
173
+ @test.send(:full_url, '/records', 'xml').should eq('http://api.org/records.xml?api_key=123')
174
+ end
175
+
176
+ it 'overrides the api key' do
177
+ @test.send(:full_url, '/records', nil, {:api_key => '456'}).should eq('http://api.org/records.json?api_key=456')
178
+ end
179
+
180
+ it 'url encodes the parameters' do
181
+ @test.send(:full_url, '/records', nil, {:api_key => '456', :i => {:category => 'Images'}}).should eq('http://api.org/records.json?api_key=456&i%5Bcategory%5D=Images')
182
+ end
183
+
184
+ it 'adds debug=true when enable_debugging is set to true' do
185
+ Supplejack.stub(:enable_debugging) { true }
186
+ @test.send(:full_url, '/records', nil, {}).should eq 'http://api.org/records.json?api_key=123&debug=true'
187
+ end
188
+
189
+ it 'handles nil params' do
190
+ @test.send(:full_url, '/records', nil, nil).should eq 'http://api.org/records.json?api_key=123'
191
+ end
192
+ end
193
+
194
+ end
195
+ end
@@ -0,0 +1,727 @@
1
+ # The Supplejack Client code is Crown copyright (C) 2014, New Zealand Government,
2
+ # and is licensed under the GNU General Public License, version 3.
3
+ # See https://github.com/DigitalNZ/supplejack_client for details.
4
+ #
5
+ # Supplejack was created by DigitalNZ at the National Library of NZ
6
+ # and the Department of Internal Affairs. http://digitalnz.org/supplejack
7
+
8
+ require 'spec_helper'
9
+
10
+ class TestRecord
11
+ def initialize(attributes={})
12
+ end
13
+ end
14
+
15
+ class TestItem
16
+ def initialize(attributes={})
17
+ end
18
+ end
19
+
20
+ module Supplejack
21
+ describe Search do
22
+
23
+ describe '#initalize' do
24
+ it 'doesn\'t break when initalized with nil' do
25
+ Search.new(nil)
26
+ end
27
+ end
28
+
29
+ describe '#params' do
30
+ it 'defaults to facets set in initializer' do
31
+ Supplejack.stub(:facets) { ['location', 'description'] }
32
+ Search.new.api_params[:facets].should eq('location,description')
33
+ end
34
+
35
+ it 'sets the facets through the params' do
36
+ Search.new(:facets => 'name,description').api_params[:facets].should eq('name,description')
37
+ end
38
+
39
+ it 'defaults to facets_per_page set in initializer' do
40
+ Supplejack.stub(:facets_per_page) { 33 }
41
+ Search.new.api_params[:facets_per_page].should eq 33
42
+ end
43
+
44
+ it 'sets the facets_per_page through the params' do
45
+ Search.new(:facets_per_page => 13).api_params[:facets_per_page].should eq 13
46
+ end
47
+
48
+ it 'deletes rails specific params' do
49
+ params = Search.new(:controller => 'records', :action => 'index').api_params
50
+ params.should_not have_key(:controller)
51
+ params.should_not have_key(:action)
52
+ end
53
+ end
54
+
55
+ describe '#text' do
56
+ it 'returns the search text' do
57
+ Search.new(:text => 'dog').text.should eq('dog')
58
+ end
59
+ end
60
+
61
+ # describe '#geo_bbox' do
62
+ # end
63
+
64
+ # describe "#record_type" do
65
+ # end
66
+
67
+ describe '#page' do
68
+ it 'defaults to 1' do
69
+ Search.new.page.should eq 1
70
+ end
71
+
72
+ it 'converts the page to a number' do
73
+ Search.new(:page => '2').page.should eq 2
74
+ end
75
+ end
76
+
77
+ describe '#per_page' do
78
+ it 'defaults to the per_page in the initializer' do
79
+ Supplejack.stub(:per_page) { 16 }
80
+ Search.new.per_page.should eq 16
81
+ end
82
+
83
+ it 'sers the per_page through the params' do
84
+ Search.new(:per_page => '3').per_page.should eq 3
85
+ end
86
+ end
87
+
88
+ describe '#sort' do
89
+ it 'returns the field to sort on' do
90
+ Search.new(:sort => 'content_partner').sort.should eq 'content_partner'
91
+ end
92
+
93
+ it 'returns nil when not set' do
94
+ Search.new.sort.should be_nil
95
+ end
96
+ end
97
+
98
+ describe '#direction' do
99
+ it 'returns the direction to sort the results' do
100
+ Search.new(:direction => 'asc').direction.should eq 'asc'
101
+ end
102
+
103
+ it 'returns nil when not set' do
104
+ Search.new.direction.should be_nil
105
+ end
106
+ end
107
+
108
+ # describe "#custom_search" do
109
+ # end
110
+
111
+ describe '#search_attributes' do
112
+ it 'sets the filter defined in Supplejack.search_attributes with its current value' do
113
+ Supplejack.stub(:search_attributes) { [:location] }
114
+ Search.new(:i => {:location => 'Wellington'}).location.should eq 'Wellington'
115
+ end
116
+ end
117
+
118
+ describe '#filters' do
119
+ before(:each) do
120
+ @filters = {:location => 'Wellington', :country => 'New Zealand'}
121
+ end
122
+
123
+ it 'returns filters in a array format by default' do
124
+ Search.new(:i => @filters).filters.should include([:location, 'Wellington'], [:country, 'New Zealand'])
125
+ end
126
+
127
+ it 'returns a hash of the current search filters' do
128
+ Search.new(:i => @filters).filters(:format => :hash).should include(@filters)
129
+ end
130
+
131
+ it 'expands filters that contain arrays' do
132
+ @filters = {:location => ['Wellington', 'Auckland']}
133
+ Search.new(:i => @filters).filters.should include([:location, 'Wellington'], [:location, 'Auckland'])
134
+ end
135
+
136
+ it 'removes the location filter' do
137
+ @filters = {:location => ['Wellington', 'Auckland']}
138
+ Search.new(:i => @filters).filters(:except => [:location]).should_not include([:location, 'Wellington'], [:location, 'Auckland'])
139
+ end
140
+ end
141
+
142
+ describe "#facets" do
143
+ before(:each) do
144
+ @search = Search.new
145
+ Supplejack.facets = [:location]
146
+ @facets_hash = {"location" => {"Wellington" => 100}, "mayor" => {"Brake, Brian" => 20}}
147
+ @search.instance_variable_set(:@response, {"search" => {"facets" => @facets_hash}})
148
+ end
149
+
150
+ it 'executes the request only once' do
151
+ @search.should_receive(:execute_request).once
152
+ @search.facets
153
+ @search.facets
154
+ end
155
+
156
+ it 'handles a failed request from the API' do
157
+ @search.instance_variable_set(:@response, {'search' => {}})
158
+ @search.facets.should be_empty
159
+ end
160
+
161
+ it 'initializes a facet object for each facet' do
162
+ Supplejack::Facet.should_receive(:new).with('location', {'Wellington' => 100})
163
+ Supplejack::Facet.should_receive(:new).with('mayor', {'Brake, Brian' => 20})
164
+ @search.facets
165
+ end
166
+
167
+ it 'orders the facets based on the order set in the initializer' do
168
+ Supplejack.stub(:facets) { [:mayor, :location] }
169
+ @search.facets.first.name.should eq 'mayor'
170
+ @search.facets.last.name.should eq 'location'
171
+ end
172
+
173
+ it 'orders the facets the other way around' do
174
+ Supplejack.stub(:facets) { [:location, :mayor] }
175
+ @search.facets.first.name.should eq 'location'
176
+ @search.facets.last.name.should eq 'mayor'
177
+ end
178
+
179
+ it 'returns the facet values' do
180
+ @search.facets.first.values.should eq({'Wellington' => 100})
181
+ end
182
+ end
183
+
184
+ describe '#facet' do
185
+ before(:each) do
186
+ @search = Search.new
187
+ end
188
+
189
+ it 'returns the specified facet' do
190
+ facet = Supplejack::Facet.new('collection', [])
191
+ @search.stub(:facets) { [facet] }
192
+
193
+ @search.facet('collection').should eq facet
194
+ end
195
+
196
+ it 'returns nil when facet is not found' do
197
+ @search.stub(:facets) { [] }
198
+ @search.facet('collection').should be_nil
199
+ end
200
+
201
+ it 'returns nil if value is nil' do
202
+ facet = Supplejack::Facet.new('collection', [])
203
+ @search.stub(:facets) { [facet] }
204
+ @search.facet(nil).should be_nil
205
+ end
206
+ end
207
+
208
+ describe '#total' do
209
+ before(:each) do
210
+ @search = Search.new
211
+ @search.instance_variable_set(:@response, {'search' => {'result_count' => 100}})
212
+ end
213
+
214
+ it 'executes the request only once' do
215
+ @search.should_receive(:execute_request).once
216
+ @search.total
217
+ @search.total
218
+ end
219
+
220
+ it 'returns the total from the response' do
221
+ @search.total.should eq 100
222
+ end
223
+
224
+ it 'returns 0 when the request to the API failed' do
225
+ @search.instance_variable_set(:@response, {'search' => {}})
226
+ @search.total.should eq 0
227
+ end
228
+ end
229
+
230
+ describe '#results' do
231
+ before(:each) do
232
+ Supplejack.stub(:record_klass).and_return('TestRecord')
233
+ @search = Search.new
234
+ @record1 = {'id' => 1, 'title' => 'Wellington'}
235
+ @record2 = {'id' => 2, 'title' => 'Auckland'}
236
+ @search.instance_variable_set(:@response, {'search' => {'results' => [@record1, @record2]}})
237
+ end
238
+
239
+ it 'executes the request only once' do
240
+ @search.stub(:total) { 10 }
241
+ @search.should_receive(:execute_request).once
242
+ @search.results
243
+ @search.results
244
+ end
245
+
246
+ it 'initializes record objects with the default class' do
247
+ TestRecord.should_receive(:new).with(@record1)
248
+ TestRecord.should_receive(:new).with(@record2)
249
+ @search.results
250
+ end
251
+
252
+ it 'initializes record objects with the class provided in the params' do
253
+ @search = Search.new(:record_klass => 'test_item')
254
+ @search.instance_variable_set(:@response, {'search' => {'results' => [@record1, @record2]}})
255
+ TestItem.should_receive(:new).with(@record1)
256
+ TestItem.should_receive(:new).with(@record2)
257
+ @search.results
258
+ end
259
+
260
+ it 'returns a array of objects of the provided class' do
261
+ @search.results.first.class.should eq TestRecord
262
+ end
263
+
264
+ it 'returns an array wraped in paginated collection object' do
265
+ @search.results.current_page.should eq 1
266
+ end
267
+
268
+ it 'returns empty paginated collection when API request failed' do
269
+ @search.instance_variable_set(:@response, {'search' => {}})
270
+ @search.results.size.should eq 0
271
+ end
272
+ end
273
+
274
+ describe '#counts' do
275
+ before(:each) do
276
+ @search = Search.new
277
+ @search.stub(:fetch_counts) { {'images' => 100} }
278
+ end
279
+
280
+ context 'caching disabled' do
281
+ before(:each) do
282
+ Supplejack.stub(:enable_caching) { false }
283
+ end
284
+
285
+ it 'fetches the counts' do
286
+ query_params = {'images' => {:category => 'Images'}}
287
+ @search.should_receive(:fetch_counts).with(query_params)
288
+ @search.counts(query_params)
289
+ end
290
+ end
291
+ end
292
+
293
+ describe '#fetch_counts' do
294
+ context 'without filters' do
295
+ before(:each) do
296
+ @search = Search.new
297
+ end
298
+
299
+ it 'returns a hash with row names and its values' do
300
+ @search.stub(:get).and_return({'search' => {'facets' => {'counts' => {'images' => 151818}}}})
301
+ @search.fetch_counts({}).should eq({'images' => 151818})
302
+ end
303
+
304
+ it 'returns every count even when there are no results matching' do
305
+ @search.stub(:get).and_return({'search' => {'facets' => {'counts' => {'images' => 151818}}}})
306
+ @search.fetch_counts({:images => {}, :headings => {}}).should eq({'images' => 151818, 'headings' => 0})
307
+ end
308
+
309
+ it 'returns 0 for every facet when the API response fails' do
310
+ @search.stub(:get).and_raise(StandardError)
311
+ @search.fetch_counts({:images => {}}).should eq({'images' => 0})
312
+ end
313
+ end
314
+ end
315
+
316
+ describe '#counts_params' do
317
+ context 'without filters' do
318
+ before(:each) do
319
+ @search = Search.new
320
+ end
321
+
322
+ it 'requests record_type == all' do
323
+ query_parameters = {:headings => {:record_type => '1'}}
324
+ @search.counts_params(query_parameters).should include(:facet_query => query_parameters, :record_type => 'all')
325
+ end
326
+
327
+ it 'adds the restrictions set in the without variable' do
328
+ query_parameters = {:headings => {:record_type => '1'}}
329
+ @search.without = {:location => 'Wellington'}
330
+ @search.counts_params(query_parameters).should include(:without => {:location => 'Wellington'})
331
+ end
332
+
333
+ it 'restricts the result set to only ones that match the and filters' do
334
+ query_parameters = {:headings => {:record_type => '1'}}
335
+ @search.and = {:location => 'Wellington'}
336
+ @search.counts_params(query_parameters).should include(:and => {:location => 'Wellington'})
337
+ end
338
+
339
+ it 'restricts the result set to results that match any of the or filters' do
340
+ query_parameters = {:headings => {:record_type => '1'}}
341
+ @search.or = {:location => ['Wellington', 'Auckland']}
342
+ @search.counts_params(query_parameters).should include(:or => {:location => ['Wellington', 'Auckland']})
343
+ end
344
+
345
+ it 'executes a request with facet_queries' do
346
+ query_parameters = {:images => {:creator => 'all', :record_type => '0'}, :headings => {:record_type => '1'}}
347
+ @search.counts_params(query_parameters).should include(:facet_query => query_parameters)
348
+ end
349
+
350
+ it 'passes the text when present' do
351
+ @search = Search.new(:text => 'dogs')
352
+ @search.counts_params({}).should include(:text => 'dogs')
353
+ end
354
+
355
+ # it 'passes the geo_bbox when present' do
356
+ # @search = Search.new(geo_bbox: '1,2,3,4')
357
+ # @search.counts_params({}).should include(geo_bbox: '1,2,3,4')
358
+ # end
359
+
360
+ it 'merges the :i and :il filters with record_type 0' do
361
+ query_parameters = {:images => {'creator' => 'all', 'record_type' => '0'}, :headings => {'record_type' => '1', :dc_type => 'Group'}}
362
+ @search = Search.new(:i => {:category => 'Images'}, :il => {:year => '1998'})
363
+
364
+ images_query = {:creator => 'all', :record_type => '0', :category => 'Images', :year => '1998'}
365
+ headings_query = {:record_type => '1', :dc_type => 'Group'}
366
+
367
+ @search.counts_params(query_parameters).should include(:facet_query => {:images => images_query, :headings => headings_query})
368
+ end
369
+
370
+ it 'merges *_text fields' do
371
+ query_parameters = {:images => {'creator' => 'all', 'record_type' => '0'}}
372
+ @search = Search.new(:i => {:subject_text => 'dog'})
373
+
374
+ images_query = {:creator => 'all', :record_type => '0'}
375
+
376
+ @search.counts_params(query_parameters).should include(:text => 'dog', :query_fields => [:subject], :facet_query => {:images => images_query})
377
+ end
378
+ end
379
+
380
+ context 'with active filters' do
381
+ before(:each) do
382
+ @search = Search.new(:i => {:location => 'Wellington'})
383
+ end
384
+
385
+ it 'merges the existing filters into every facet query' do
386
+ query_parameters = {:images => {'creator' => 'all', 'record_type' => 0}}
387
+ expected_filters = {:images => {:creator => 'all', :location => 'Wellington', :record_type => 0}}
388
+ @search.counts_params(query_parameters).should include(:facet_query => expected_filters)
389
+ end
390
+
391
+ it 'merges existing filters without overriding' do
392
+ query_parameters = {:images => {'location' => 'Matapihi', 'record_type' => 0}}
393
+ expected_filters = {:images => {:location => ['Wellington', 'Matapihi'], :record_type => 0}}
394
+ @search.counts_params(query_parameters).should include(:facet_query => expected_filters)
395
+ end
396
+
397
+ it 'overrides the record_type' do
398
+ @search = Search.new(:record_type => '1')
399
+ query_parameters = {:images => {'record_type' => '0'}}
400
+ expected_filters = {:images => {:record_type => '0'}}
401
+ @search.counts_params(query_parameters).should include(:facet_query => expected_filters)
402
+ end
403
+
404
+ it 'merges existing negative filters' do
405
+ @search = Search.new(:i => {'-category' => 'Groups'})
406
+ query_parameters = {:photos => {'has_large_thumbnail_url' => 'Y'}}
407
+ expected_filters = {:photos => {:has_large_thumbnail_url => 'Y', :'-category' => 'Groups'}}
408
+ @search.counts_params(query_parameters).should include(:facet_query => expected_filters)
409
+ end
410
+ end
411
+ end
412
+
413
+ describe '#request_path' do
414
+ before(:each) do
415
+ @search = Search.new
416
+ end
417
+
418
+ it 'returns /records by default' do
419
+ @search.request_path.should eq '/records'
420
+ end
421
+ end
422
+
423
+ describe '#execute_request' do
424
+ before(:each) do
425
+ @search = Search.new
426
+ end
427
+
428
+ it 'only executes the request once' do
429
+ @search.should_receive(:get).once.and_return('{}')
430
+ @search.execute_request
431
+ @search.execute_request
432
+ end
433
+
434
+ it 'removes the results that match the without filters' do
435
+ @search.without = {:location => 'Wellington'}
436
+ @search.should_receive(:get).with('/records', hash_including(:without => {:location => 'Wellington'}))
437
+ @search.execute_request
438
+ end
439
+
440
+ it 'restricts the result set to only ones that match the and filters' do
441
+ @search.and = {:location => 'Wellington'}
442
+ @search.should_receive(:get).with('/records', hash_including(:and => {:location => 'Wellington'}))
443
+ @search.execute_request
444
+ end
445
+
446
+ it 'restricts the result set to only ones that match any of the or filters' do
447
+ @search.or = {:location => ['Wellington']}
448
+ @search.should_receive(:get).with('/records', hash_including(:or => {:location => ['Wellington']}))
449
+ @search.execute_request
450
+ end
451
+
452
+ it 'returns a empty search hash when a error is raised' do
453
+ @search.stub(:get).and_raise(StandardError)
454
+ @search.execute_request.should eq({'search' => {}})
455
+ end
456
+
457
+ context 'caching enabled' do
458
+ before :each do
459
+ @cache = double(:cache).as_null_object
460
+ Rails.stub(:cache) { @cache }
461
+ Supplejack.stub(:enable_caching) { true }
462
+ end
463
+
464
+ it 'caches the response when it is cacheable' do
465
+ search = Supplejack::Search.new
466
+ search.stub(:cacheable?) { true }
467
+ cache_key = Digest::MD5.hexdigest("/records?#{search.api_params.to_query}")
468
+ Rails.cache.should_receive(:fetch).with(cache_key, expires_in: 1.hour)
469
+ search.execute_request
470
+ end
471
+
472
+ it 'doesnt cache the response it is not cacheable' do
473
+ search = Supplejack::Search.new(text: "dogs")
474
+ search.stub(:cacheable?) { false }
475
+ Rails.cache.should_not_receive(:fetch)
476
+ search.execute_request
477
+ end
478
+ end
479
+ end
480
+
481
+ describe '#cacheable?' do
482
+ it 'returns true when it doesn\'t have a text parameter' do
483
+ Supplejack::Search.new.cacheable?.should be_true
484
+ end
485
+
486
+ it 'returns false when it has a text parameter' do
487
+ Supplejack::Search.new(text: 'Dogs').cacheable?.should be_false
488
+ end
489
+
490
+ it 'returns false then it\'s not the first page of results' do
491
+ Supplejack::Search.new(page: '2').cacheable?.should be_false
492
+ end
493
+ end
494
+
495
+ describe '#has_attribute_name?' do
496
+ before(:each) do
497
+ @search = Search.new
498
+ @search.location = ['Wellington', 'Auckland']
499
+ end
500
+
501
+ it 'returns true if value is in filter' do
502
+ @search.has_location?('Wellington').should be_true
503
+ end
504
+
505
+ it 'returns false is value is not in filter' do
506
+ @search.has_location?('Videos').should be_false
507
+ end
508
+
509
+ context 'search filter is single valued' do
510
+ before(:each) do
511
+ @search = Search.new
512
+ @search.location = 'Wellington'
513
+ end
514
+
515
+ it 'returns true if value matches filter' do
516
+ @search.has_location?('Wellington').should be_true
517
+ end
518
+
519
+ it 'returns false if value does not match the filter' do
520
+ @search.has_location?('Cats').should be_false
521
+ end
522
+
523
+ it 'returns false when location has nil value' do
524
+ @search.location = nil
525
+ @search.has_category?('Cats').should be_false
526
+ end
527
+
528
+ it 'shouldn\'t search for a non existent search attribute' do
529
+ Supplejack.stub(:search_attributes) { [] }
530
+ @search.should_not_receive(:has_filter_and_value?)
531
+ @search.has_category?('Cats')
532
+ end
533
+ end
534
+ end
535
+
536
+ describe '#categories' do
537
+ before(:each) do
538
+ @search = Search.new({:i => {:category => 'Books', :year => 2001}, :text => 'Dogs'})
539
+ @search.stub(:get) { {'search' => {'facets' => {'category' => {'Books' => 123}}, 'result_count' => 123}} }
540
+ end
541
+
542
+ it 'should call the fetch_values method' do
543
+ @search.should_receive(:facet_values).with('category', {})
544
+ @search.categories
545
+ end
546
+
547
+ it 'removes category filter from the search request' do
548
+ @search.should_receive(:get).with('/records', hash_including(:and => {:year => 2001})).and_return({'search' => {'facets' => {'category' => {'Books' => 123}}}})
549
+ @search.categories
550
+ end
551
+
552
+ it 'returns the category facet hash ' do
553
+ @search.categories.should include('Books' => 123)
554
+ end
555
+
556
+ it 'asks the API for 0 results' do
557
+ @search.should_receive(:get).with('/records', hash_including({:per_page => 0}))
558
+ @search.categories
559
+ end
560
+
561
+ it 'should return add the All count to the hash' do
562
+ @search.categories['All'].should eq 123
563
+ end
564
+
565
+ it 'orders the category values by :count' do
566
+ @search.should_receive(:facet_values).with('category', {:sort => :count})
567
+ @search.categories({:sort => :count})
568
+ end
569
+ end
570
+
571
+ describe '#fetch_facet_values' do
572
+ before(:each) do
573
+ @search = Search.new({:i => {:category => 'Books', :year => 2001}, :text => 'Dogs'})
574
+ @search.stub(:get) { {'search' => {'facets' => {'category' => {'Books' => 123}}, 'result_count' => 123}} }
575
+ end
576
+
577
+ it 'returns the category facet hash' do
578
+ @search.fetch_facet_values('category').should include('Books' => 123)
579
+ end
580
+
581
+ it 'returns empty values when the request to the API failed' do
582
+ @search.stub(:get).and_raise(StandardError)
583
+ @search.fetch_facet_values('category').should eq({'All' => 0})
584
+ end
585
+
586
+ it 'should add the All count to the hash' do
587
+ @search.fetch_facet_values('category')['All'].should eq 123
588
+ end
589
+
590
+ it 'doesnt return the All count ' do
591
+ @search.fetch_facet_values('category', {:all => false}).should_not have_key('All')
592
+ end
593
+
594
+ it 'memoizes the facet_values' do
595
+ @search.should_receive(:get).once
596
+ @search.fetch_facet_values('category')
597
+ @search.fetch_facet_values('category')
598
+ end
599
+
600
+ context 'sorting' do
601
+ before(:each) do
602
+ @facet = Supplejack::Facet.new('category', {'All' => 123, 'Books' => 123})
603
+ Supplejack::Facet.stub(:new) { @facet }
604
+ end
605
+
606
+ it 'initializes a Supplejack::Facet' do
607
+ Supplejack::Facet.should_receive(:new).with('category', {'All' => 123, 'Books' => 123})
608
+ @search.fetch_facet_values('category')
609
+ end
610
+
611
+ it 'tells the facet how to sort the values' do
612
+ @facet.should_receive(:values).with(:index)
613
+ @search.fetch_facet_values('category', {:sort => :index})
614
+ end
615
+
616
+ it 'doesn\'t sort by default' do
617
+ @facet.should_receive(:values).with(nil)
618
+ @search.fetch_facet_values('category')
619
+ end
620
+ end
621
+ end
622
+
623
+ describe 'facet_values_params' do
624
+ before(:each) do
625
+ @search = Search.new({:i => {:type => 'Person', :year => 2001}, :text => 'Dogs'})
626
+ end
627
+
628
+ it 'removes type filter from the search request' do
629
+ @search.facet_values_params('type').should include(:and => {:year => 2001})
630
+ end
631
+
632
+ it 'requests 0 results per_page' do
633
+ @search.facet_values_params('type').should include(:per_page => 0)
634
+ end
635
+
636
+ it 'adds without filters' do
637
+ @search = Search.new({:i => {:type => 'Person', :year => 2001, '-group' => 'Group'}, :text => 'Dogs'})
638
+ @search.facet_values_params('type').should include(:without => {:group => 'Group'})
639
+ end
640
+
641
+ it 'only adds the and_filters to :and' do
642
+ @search = Search.new({:i => {:type => 'Person', :year => 2001, '-group' => 'Group'}, :text => 'Dogs'})
643
+ @search.facet_values_params('type').should include(:and => {:year => 2001})
644
+ end
645
+
646
+ it 'gets the facet_values for a record_type 1' do
647
+ @search = Search.new({:i => {:type => 'Person'}, :h => {:group => 'Group'}, :text => 'Dogs', :record_type => 1})
648
+ @search.facet_values_params('group').should include(:and => {})
649
+ end
650
+
651
+ it 'restricts results to filters specified in without accessor' do
652
+ @search = Search.new
653
+ @search.without = {:website => 'Flickr'}
654
+ @search.facet_values_params('type').should include(:without => {:website => 'Flickr'})
655
+ end
656
+
657
+ it 'merges in the filters specified in without' do
658
+ @search = Search.new({:i => {'-type' => 'Person'}})
659
+ @search.without = {:website => 'Flickr'}
660
+ @search.facet_values_params('type').should include(:without => {:website => 'Flickr', :type => 'Person'})
661
+ end
662
+
663
+ it 'adds the restrictions set in the and variable' do
664
+ @search = Search.new
665
+ @search.and = {:content_partner => 'NLNZ'}
666
+ @search.facet_values_params('type').should include(:and => {:content_partner => 'NLNZ'})
667
+ end
668
+
669
+ it 'adds the restrictions set in the or variable' do
670
+ @search = Search.new
671
+ @search.or = {:content_partner => 'NLNZ'}
672
+ @search.facet_values_params('type').should include(:or => {:content_partner => 'NLNZ'})
673
+ end
674
+
675
+ it 'memoizes the params' do
676
+ @search = Search.new
677
+ @search.should_receive(:url_format).once.and_return(double(:url_format, :and_filters => {}))
678
+ @search.facet_values_params('type')
679
+ @search.facet_values_params('type')
680
+ end
681
+
682
+ it 'adds a parameter for facets_per_page if the option is present' do
683
+ @search.facet_values_params('type', {:facets_per_page => 15}).should include(:facets_per_page => 15)
684
+ end
685
+ end
686
+
687
+ describe '#facet_values' do
688
+ before(:each) do
689
+ @search = Search.new
690
+ @search.stub(:fetch_facet_values) { {'Books' => 100} }
691
+ end
692
+
693
+ context 'caching disabled' do
694
+ before(:each) do
695
+ Supplejack.stub(:enable_caching) { false }
696
+ end
697
+
698
+ it 'fetches the facet values' do
699
+ @search.should_receive(:fetch_facet_values).with('category', anything)
700
+ @search.facet_values('category', anything)
701
+ end
702
+ end
703
+ end
704
+
705
+ describe '#merge_extra_filters' do
706
+ before(:each) do
707
+ @search = Search.new
708
+ end
709
+
710
+ it 'merges the and filters' do
711
+ @search.and = {:type => 'Person'}
712
+ @search.merge_extra_filters({:and => {:location => 'Wellington'}}).should eq({:and => {:location => 'Wellington', :type => 'Person'}})
713
+ end
714
+
715
+ it 'merges the or filters' do
716
+ @search.or = {:type => 'Person'}
717
+ @search.merge_extra_filters({:and => {:location => 'Wellington'}}).should eq({:and => {:location => 'Wellington'}, :or => {:type => 'Person'}})
718
+ end
719
+
720
+ it 'merges the without filters' do
721
+ @search.without = {:type => 'Person'}
722
+ @search.merge_extra_filters({:and => {:location => 'Wellington'}}).should eq({:and => {:location => 'Wellington'}, :without => {:type => 'Person'}})
723
+ end
724
+ end
725
+
726
+ end
727
+ end