search_flip 2.0.0.beta2 → 2.0.0.beta3

Sign up to get free protection for your applications and to get access to all the features.
@@ -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