search_flip 2.0.0.beta2 → 2.0.0.beta3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,67 @@
1
+
2
+ require File.expand_path("../spec_helper", __dir__)
3
+
4
+ class HttpTestRequest
5
+ attr_accessor :calls
6
+
7
+ def initialize
8
+ self.calls = []
9
+ end
10
+
11
+ [:via, :basic_auth, :auth].each do |method|
12
+ define_method method do |*args|
13
+ dup.tap do |request|
14
+ request.calls = calls + [[method, args]]
15
+ end
16
+ end
17
+ end
18
+ end
19
+
20
+ RSpec.describe SearchFlip::HTTPClient do
21
+ describe "delegation" do
22
+ subject { SearchFlip::HTTPClient }
23
+
24
+ [:headers, :via, :basic_auth, :auth].each do |method|
25
+ it { should delegate(method).to(:new) }
26
+ end
27
+
28
+ [:get, :post, :put, :delete, :head].each do |method|
29
+ it { should delegate(method).to(:new) }
30
+ end
31
+ end
32
+
33
+ [:get, :put, :delete, :post, :head].each do |method|
34
+ describe "##{method}" do
35
+ it "performs the specified request" do
36
+ stub_request(method, "http://localhost/path").with(body: "body", query: { key: "value" }).to_return(body: "success")
37
+
38
+ expect(SearchFlip::HTTPClient.new.send(method, "http://localhost/path", body: "body", params: { key: "value" }).body.to_s).to eq("success")
39
+ end
40
+ end
41
+ end
42
+
43
+ [:via, :basic_auth, :auth].each do |method|
44
+ describe "##{method}" do
45
+ it "creates a dupped instance" do
46
+ client = SearchFlip::HTTPClient.new
47
+ client.request = HttpTestRequest.new
48
+
49
+ client1 = client.send(method, "key1")
50
+ client2 = client1.send(method, "key2")
51
+
52
+ expect(client1.object_id).not_to eq(client2.object_id)
53
+ end
54
+
55
+ it "extends the request" do
56
+ client = SearchFlip::HTTPClient.new
57
+ client.request = HttpTestRequest.new
58
+
59
+ client1 = client.send(method, "key1")
60
+ client2 = client1.send(method, "key2")
61
+
62
+ expect(client1.request.calls).to eq([[method, ["key1"]]])
63
+ expect(client2.request.calls).to eq([[method, ["key1"]], [method, ["key2"]]])
64
+ end
65
+ end
66
+ end
67
+ end
@@ -0,0 +1,455 @@
1
+
2
+ require File.expand_path("../spec_helper", __dir__)
3
+
4
+ RSpec.describe SearchFlip::Index do
5
+ describe "delegation" do
6
+ subject { ProductIndex }
7
+
8
+ methods = [
9
+ :profile, :where, :where_not, :filter, :range, :match_all, :exists,
10
+ :exists_not, :post_where, :post_where_not, :post_filter, :post_must,
11
+ :post_must_not, :post_should, :post_range, :post_exists, :post_exists_not,
12
+ :aggregate, :scroll, :source, :includes, :eager_load, :preload, :sort, :resort,
13
+ :order, :reorder, :offset, :limit, :paginate, :page, :per, :search,
14
+ :find_in_batches, :highlight, :suggest, :custom, :find_each, :failsafe,
15
+ :total_entries, :total_count, :terminate_after, :timeout, :records, :results,
16
+ :should, :should_not, :must, :must_not, :find_each_result,
17
+ :find_results_in_batches
18
+ ]
19
+
20
+ methods.each do |method|
21
+ it { should delegate(method).to(:criteria) }
22
+ end
23
+ end
24
+
25
+ describe ".serialize" do
26
+ it "raises a SearchFlip::MethodNotImplemented by default" do
27
+ klass = Class.new do
28
+ include SearchFlip::Index
29
+ end
30
+
31
+ expect { klass.serialize(Object.new) }.to raise_error(SearchFlip::MethodNotImplemented)
32
+ end
33
+ end
34
+
35
+ describe ".type_name" do
36
+ it "raises a SearchFlip::MethodNotImplemented by default" do
37
+ klass = Class.new do
38
+ include SearchFlip::Index
39
+ end
40
+
41
+ expect { klass.type_name }.to raise_error(SearchFlip::MethodNotImplemented)
42
+ end
43
+ end
44
+
45
+ describe ".create_index" do
46
+ it "delegates to connection" do
47
+ allow(TestIndex.connection).to receive(:create_index).and_call_original
48
+
49
+ TestIndex.create_index
50
+
51
+ expect(TestIndex.connection).to have_received(:create_index).with("test", {})
52
+ end
53
+
54
+ it "includes the mapping if specified" do
55
+ mapping = { test: { properties: { id: { type: "long" } } } }
56
+
57
+ allow(TestIndex).to receive(:mapping).and_return(mapping)
58
+ allow(TestIndex.connection).to receive(:create_index).and_call_original
59
+
60
+ TestIndex.create_index(include_mapping: true)
61
+
62
+ expect(TestIndex.connection).to have_received(:create_index).with("test", mappings: mapping)
63
+ end
64
+
65
+ it "includes the index settings" do
66
+ allow(TestIndex).to receive(:index_settings).and_return(number_of_shards: 2)
67
+ allow(TestIndex.connection).to receive(:create_index).and_call_original
68
+
69
+ TestIndex.create_index
70
+
71
+ expect(TestIndex.connection).to have_received(:create_index).with("test", number_of_shards: 2)
72
+ end
73
+ end
74
+
75
+ describe ".index_exists?" do
76
+ it "delegates to connection" do
77
+ TestIndex.create_index
78
+
79
+ allow(TestIndex.connection).to receive(:index_exists?).and_call_original
80
+
81
+ TestIndex.index_exists?
82
+
83
+ expect(TestIndex.connection).to have_received(:index_exists?).with("test")
84
+ end
85
+ end
86
+
87
+ describe ".delete_index" do
88
+ it "delegates to connection" do
89
+ TestIndex.create_index
90
+
91
+ expect(TestIndex.index_exists?).to eq(true)
92
+
93
+ allow(TestIndex.connection).to receive(:delete_index).and_call_original
94
+
95
+ TestIndex.delete_index
96
+
97
+ expect(TestIndex.index_exists?).to eq(false)
98
+ end
99
+ end
100
+
101
+ describe ".get_index_settings" do
102
+ it "delegates to connection" do
103
+ TestIndex.create_index
104
+
105
+ allow(TestIndex.connection).to receive(:get_index_settings).and_call_original
106
+
107
+ TestIndex.get_index_settings
108
+
109
+ expect(TestIndex.connection).to have_received(:get_index_settings).with("test")
110
+ end
111
+ end
112
+
113
+ describe ".update_index_settings" do
114
+ it "delegates to connection" do
115
+ TestIndex.create_index
116
+
117
+ allow(TestIndex).to receive(:index_settings).and_return(number_of_replicas: 3)
118
+ allow(TestIndex.connection).to receive(:update_index_settings).and_call_original
119
+
120
+ TestIndex.update_index_settings
121
+
122
+ expect(TestIndex.connection).to have_received(:update_index_settings).with("test", number_of_replicas: 3)
123
+ end
124
+ end
125
+
126
+ describe ".update_mapping" do
127
+ it "delegates to connection" do
128
+ TestIndex.create_index
129
+
130
+ mapping = { test: { properties: { id: { type: "long" } } } }
131
+
132
+ allow(TestIndex).to receive(:mapping).and_return(mapping)
133
+ allow(TestIndex.connection).to receive(:update_mapping).and_call_original
134
+
135
+ TestIndex.update_mapping
136
+
137
+ expect(TestIndex.connection).to have_received(:update_mapping).with("test", "test", mapping)
138
+ end
139
+ end
140
+
141
+ describe ".get_mapping" do
142
+ it "delegates to connection" do
143
+ TestIndex.create_index
144
+ TestIndex.update_mapping
145
+
146
+ allow(TestIndex.connection).to receive(:get_mapping).and_call_original
147
+
148
+ TestIndex.get_mapping
149
+
150
+ expect(TestIndex.connection).to have_received(:get_mapping).with("test", "test")
151
+ end
152
+ end
153
+
154
+ describe ".refresh" do
155
+ it "delegates to connection" do
156
+ TestIndex.create_index
157
+
158
+ allow(TestIndex.connection).to receive(:refresh).and_call_original
159
+
160
+ TestIndex.refresh
161
+
162
+ expect(TestIndex.connection).to have_received(:refresh).with("test")
163
+ end
164
+ end
165
+
166
+ describe ".index_url" do
167
+ it "delegates to connection" do
168
+ allow(TestIndex.connection).to receive(:index_url).and_call_original
169
+
170
+ TestIndex.index_url
171
+
172
+ expect(TestIndex.connection).to have_received(:index_url).with("test")
173
+ end
174
+
175
+ it "includes the index prefix" do
176
+ begin
177
+ SearchFlip::Config[:index_prefix] = "prefix-"
178
+
179
+ allow(TestIndex.connection).to receive(:index_url).and_call_original
180
+
181
+ TestIndex.index_url
182
+
183
+ expect(TestIndex.connection).to have_received(:index_url).with("prefix-test")
184
+ ensure
185
+ SearchFlip::Config[:index_prefix] = nil
186
+ end
187
+ end
188
+ end
189
+
190
+ describe ".type_url" do
191
+ it "delegates to connection" do
192
+ allow(TestIndex.connection).to receive(:type_url).and_call_original
193
+
194
+ TestIndex.type_url
195
+
196
+ expect(TestIndex.connection).to have_received(:type_url).with("test", "test")
197
+ end
198
+ end
199
+
200
+ describe ".import" do
201
+ it "imports an object" do
202
+ expect { ProductIndex.import create(:product) }.to(change { ProductIndex.total_count }.by(1))
203
+ end
204
+
205
+ it "imports an array of objects" do
206
+ expect { ProductIndex.import [create(:product), create(:product)] }.to(change { ProductIndex.total_count }.by(2))
207
+ end
208
+
209
+ it "imports a scope" do
210
+ create_list :product, 2
211
+
212
+ expect { ProductIndex.import Product.all }.to(change { ProductIndex.total_count }.by(2))
213
+ end
214
+
215
+ it "allows param options" do
216
+ products = create_list(:product, 2)
217
+
218
+ expect { ProductIndex.import products, {}, version: 1, version_type: "external" }.to(change { ProductIndex.total_count }.by(2))
219
+ expect { ProductIndex.import products, {}, version: 2, version_type: "external" }.not_to(change { ProductIndex.total_count })
220
+ expect { ProductIndex.import products, { ignore_errors: [409] }, version: 2, version_type: "external" }.not_to(change { ProductIndex.total_count })
221
+ expect { ProductIndex.import products, {}, version: 2, version_type: "external" }.to raise_error(SearchFlip::Bulk::Error)
222
+ end
223
+
224
+ it "passes param options" do
225
+ product = create(:product)
226
+
227
+ ProductIndex.import product, {}, version: 10, version_type: "external"
228
+
229
+ expect(ProductIndex.get(product.id)["_version"]).to eq(10)
230
+ end
231
+
232
+ it "passes class options" do
233
+ product = create(:product)
234
+
235
+ allow(ProductIndex).to receive(:index_options).and_return(version: 10, version_type: "external")
236
+
237
+ ProductIndex.import product
238
+
239
+ expect(ProductIndex.get(product.id)["_version"]).to eq(10)
240
+ end
241
+ end
242
+
243
+ describe ".index" do
244
+ it "indexes an object" do
245
+ expect { ProductIndex.index create(:product) }.to(change { ProductIndex.total_count }.by(1))
246
+ end
247
+
248
+ it "indexes an array of objects" do
249
+ expect { ProductIndex.index [create(:product), create(:product)] }.to(change { ProductIndex.total_count }.by(2))
250
+ end
251
+
252
+ it "indexes a scope" do
253
+ create_list :product, 2
254
+
255
+ expect { ProductIndex.index Product.all }.to(change { ProductIndex.total_count }.by(2))
256
+ end
257
+ end
258
+
259
+ describe ".create" do
260
+ it "creates an object" do
261
+ product = create(:product)
262
+
263
+ expect { ProductIndex.create product }.to(change { ProductIndex.total_count }.by(1))
264
+ expect { ProductIndex.create product }.to raise_error(SearchFlip::Bulk::Error)
265
+ end
266
+
267
+ it "create an array of objects" do
268
+ products = create_list(:product, 2)
269
+
270
+ expect { ProductIndex.create products }.to(change { ProductIndex.total_count }.by(2))
271
+ expect { ProductIndex.create products }.to raise_error(SearchFlip::Bulk::Error)
272
+ end
273
+
274
+ it "creates a scope of objects" do
275
+ create_list(:product, 2)
276
+
277
+ expect { ProductIndex.create Product.all }.to(change { ProductIndex.total_count }.by(2))
278
+ expect { ProductIndex.create Product.all }.to raise_error(SearchFlip::Bulk::Error)
279
+ end
280
+
281
+ it "allows respects param options" do
282
+ products = create_list(:product, 2)
283
+
284
+ expect { ProductIndex.create products }.to(change { ProductIndex.total_count }.by(2))
285
+ expect { ProductIndex.create products, ignore_errors: [409] }.not_to(change { ProductIndex.total_count })
286
+
287
+ products = create_list(:product, 2)
288
+
289
+ if ProductIndex.connection.version.to_i >= 5
290
+ expect { ProductIndex.create products, {}, routing: "r1" }.to(change { ProductIndex.total_count }.by(2))
291
+
292
+ expect(ProductIndex.get(products.first.id, routing: "r1")["_routing"]).to eq("r1")
293
+ else
294
+ expect { ProductIndex.create products, {}, version: 2, version_type: "external" }.to(change { ProductIndex.total_count }.by(2))
295
+
296
+ expect(ProductIndex.get(products.first.id)["_version"]).to eq(2)
297
+ end
298
+ end
299
+
300
+ it "allows respects class options" do
301
+ products = create_list(:product, 2)
302
+
303
+ if ProductIndex.connection.version.to_i >= 5
304
+ allow(ProductIndex).to receive(:index_options).and_return(routing: "r1")
305
+
306
+ expect { ProductIndex.create products }.to(change { ProductIndex.total_count }.by(2))
307
+
308
+ expect(ProductIndex.get(products.first.id, routing: "r1")["_routing"]).to eq("r1")
309
+ else
310
+ allow(ProductIndex).to receive(:index_options).and_return(version: 2, version_type: "external")
311
+
312
+ expect { ProductIndex.create products }.to(change { ProductIndex.total_count }.by(2))
313
+
314
+ expect(ProductIndex.get(products.first.id)["_version"]).to eq(2)
315
+ end
316
+ end
317
+ end
318
+
319
+ describe ".update" do
320
+ it "updates an object" do
321
+ product = create(:product)
322
+
323
+ ProductIndex.import product
324
+
325
+ expect { ProductIndex.update product }.not_to(change { ProductIndex.total_count })
326
+ end
327
+
328
+ it "updates an array of objects" do
329
+ products = create_list(:product, 2)
330
+
331
+ ProductIndex.import products
332
+
333
+ expect { ProductIndex.update products }.not_to(change { ProductIndex.total_count })
334
+ end
335
+
336
+ it "updates a scope of objects" do
337
+ products = create_list(:product, 2)
338
+
339
+ ProductIndex.import products
340
+
341
+ expect { ProductIndex.update Product.all }.not_to(change { ProductIndex.total_count })
342
+ end
343
+ end
344
+
345
+ describe ".delete" do
346
+ it "deletes an object" do
347
+ product = create(:product)
348
+
349
+ ProductIndex.import product
350
+
351
+ expect { ProductIndex.delete product }.to(change { ProductIndex.total_count }.by(-1))
352
+ end
353
+
354
+ it "deletes an array of objects" do
355
+ products = create_list(:product, 2)
356
+
357
+ ProductIndex.import products
358
+
359
+ expect { ProductIndex.delete products }.to(change { ProductIndex.total_count }.by(-2))
360
+ end
361
+
362
+ it "deletes a scope of objects" do
363
+ products = create_list(:product, 2)
364
+
365
+ ProductIndex.import products
366
+
367
+ expect { ProductIndex.delete Product.all }.to(change { ProductIndex.total_count }.by(-2))
368
+ end
369
+ end
370
+
371
+ describe ".get" do
372
+ it "retrieves the document" do
373
+ product = create(:product)
374
+
375
+ ProductIndex.import product
376
+
377
+ expect(ProductIndex.get(product.id)["_id"]).to eq(product.id.to_s)
378
+ end
379
+
380
+ it "passes params" do
381
+ product = create(:product)
382
+ ProductIndex.import product
383
+
384
+ expect(ProductIndex.get(product.id).keys).to include("_source")
385
+ expect(ProductIndex.get(product.id, _source: false).keys).not_to include("_source")
386
+ end
387
+ end
388
+
389
+ describe ".scope" do
390
+ it "adds a scope" do
391
+ temp_product_index = Class.new(ProductIndex)
392
+ temp_product_index.scope(:with_title) { |title| where(title: title) }
393
+
394
+ expected = create(:product, title: "expected")
395
+ rejected = create(:product, title: "rejected")
396
+
397
+ temp_product_index.import [expected, rejected]
398
+
399
+ results = temp_product_index.with_title("expected").records
400
+
401
+ expect(results).to eq([expected])
402
+ end
403
+ end
404
+
405
+ describe ".bulk" do
406
+ it "imports objects" do
407
+ bulk = proc do
408
+ ProductIndex.bulk do |indexer|
409
+ indexer.index 1, id: 1
410
+ indexer.index 2, id: 2
411
+ end
412
+ end
413
+
414
+ expect(&bulk).to(change { ProductIndex.total_count }.by(2))
415
+ end
416
+
417
+ it "respects options" do
418
+ ProductIndex.bulk do |indexer|
419
+ indexer.index 1, id: 1
420
+ indexer.index 2, id: 2
421
+ end
422
+
423
+ bulk = proc do
424
+ ProductIndex.bulk do |indexer|
425
+ indexer.index 1, { id: 1 }, version: 1, version_type: "external"
426
+ indexer.index 2, { id: 2 }, version: 1, version_type: "external"
427
+ end
428
+ end
429
+
430
+ expect(&bulk).to raise_error(SearchFlip::Bulk::Error)
431
+
432
+ bulk = proc do
433
+ ProductIndex.bulk ignore_errors: [409] do |indexer|
434
+ indexer.index 1, { id: 1 }, version: 1, version_type: "external"
435
+ indexer.index 2, { id: 2 }, version: 1, version_type: "external"
436
+ end
437
+ end
438
+
439
+ expect(&bulk).not_to(change { ProductIndex.total_count })
440
+ end
441
+ end
442
+
443
+ describe ".connection" do
444
+ it "returns a SearchFlip::Connection" do
445
+ expect(ProductIndex.connection).to be_instance_of(SearchFlip::Connection)
446
+ end
447
+
448
+ it "memoizes" do
449
+ connection = ProductIndex.connection
450
+
451
+ expect(ProductIndex.connection).to equal(connection)
452
+ end
453
+ end
454
+ end
455
+
@@ -0,0 +1,44 @@
1
+
2
+ require File.expand_path("../spec_helper", __dir__)
3
+
4
+ class TestProduct < Product
5
+ include SearchFlip::Model
6
+
7
+ notifies_index(ProductIndex)
8
+ end
9
+
10
+ RSpec.describe SearchFlip::Model do
11
+ describe "#save" do
12
+ it "imports the record to the index" do
13
+ expect(ProductIndex.total_count).to eq(0)
14
+
15
+ TestProduct.create!
16
+
17
+ expect(ProductIndex.total_count).to eq(1)
18
+ end
19
+ end
20
+
21
+ describe "#destroy" do
22
+ it "delete the record from the index" do
23
+ test_product = TestProduct.create!
24
+
25
+ expect(ProductIndex.total_count).to eq(1)
26
+
27
+ test_product.destroy
28
+
29
+ expect(ProductIndex.total_count).to eq(0)
30
+ end
31
+ end
32
+
33
+ describe "#touch" do
34
+ it "imports the record to the index" do
35
+ test_product = Timecop.freeze(Time.parse("2016-01-01 12:00:00")) { TestProduct.create! }
36
+
37
+ updated_at = ProductIndex.match_all.results.first.updated_at
38
+
39
+ Timecop.freeze(Time.parse("2017-01-01 12:00:00")) { test_product.touch }
40
+
41
+ expect(ProductIndex.match_all.results.first.updated_at).not_to eq(updated_at)
42
+ end
43
+ end
44
+ end