chewy 7.2.1 → 7.6.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (108) hide show
  1. checksums.yaml +4 -4
  2. data/.github/CODEOWNERS +1 -0
  3. data/.github/dependabot.yml +42 -0
  4. data/.github/workflows/ruby.yml +28 -26
  5. data/.rubocop.yml +4 -1
  6. data/CHANGELOG.md +196 -0
  7. data/Gemfile +4 -3
  8. data/README.md +203 -20
  9. data/chewy.gemspec +4 -18
  10. data/gemfiles/base.gemfile +12 -0
  11. data/gemfiles/rails.6.1.activerecord.gemfile +2 -1
  12. data/gemfiles/{rails.5.2.activerecord.gemfile → rails.7.0.activerecord.gemfile} +6 -3
  13. data/gemfiles/{rails.6.0.activerecord.gemfile → rails.7.1.activerecord.gemfile} +6 -3
  14. data/lib/chewy/config.rb +22 -14
  15. data/lib/chewy/elastic_client.rb +31 -0
  16. data/lib/chewy/errors.rb +11 -2
  17. data/lib/chewy/fields/base.rb +69 -13
  18. data/lib/chewy/fields/root.rb +2 -10
  19. data/lib/chewy/index/actions.rb +11 -16
  20. data/lib/chewy/index/adapter/active_record.rb +18 -3
  21. data/lib/chewy/index/adapter/object.rb +0 -10
  22. data/lib/chewy/index/adapter/orm.rb +4 -14
  23. data/lib/chewy/index/crutch.rb +15 -7
  24. data/lib/chewy/index/import/bulk_builder.rb +219 -32
  25. data/lib/chewy/index/import/bulk_request.rb +1 -1
  26. data/lib/chewy/index/import/routine.rb +3 -3
  27. data/lib/chewy/index/import.rb +45 -31
  28. data/lib/chewy/index/mapping.rb +2 -2
  29. data/lib/chewy/index/observe/active_record_methods.rb +87 -0
  30. data/lib/chewy/index/observe/callback.rb +34 -0
  31. data/lib/chewy/index/observe.rb +3 -58
  32. data/lib/chewy/index/syncer.rb +1 -1
  33. data/lib/chewy/index.rb +25 -0
  34. data/lib/chewy/journal.rb +17 -6
  35. data/lib/chewy/log_subscriber.rb +5 -1
  36. data/lib/chewy/minitest/helpers.rb +77 -0
  37. data/lib/chewy/minitest/search_index_receiver.rb +3 -1
  38. data/lib/chewy/rake_helper.rb +92 -11
  39. data/lib/chewy/rspec/build_query.rb +12 -0
  40. data/lib/chewy/rspec/helpers.rb +55 -0
  41. data/lib/chewy/rspec/update_index.rb +14 -7
  42. data/lib/chewy/rspec.rb +2 -0
  43. data/lib/chewy/runtime/version.rb +1 -1
  44. data/lib/chewy/runtime.rb +1 -1
  45. data/lib/chewy/search/parameters/collapse.rb +16 -0
  46. data/lib/chewy/search/parameters/ignore_unavailable.rb +27 -0
  47. data/lib/chewy/search/parameters/indices.rb +1 -1
  48. data/lib/chewy/search/parameters/knn.rb +16 -0
  49. data/lib/chewy/search/parameters/order.rb +6 -19
  50. data/lib/chewy/search/parameters/storage.rb +1 -1
  51. data/lib/chewy/search/parameters/track_total_hits.rb +16 -0
  52. data/lib/chewy/search/parameters.rb +4 -4
  53. data/lib/chewy/search/request.rb +74 -16
  54. data/lib/chewy/search/scoping.rb +1 -1
  55. data/lib/chewy/search.rb +5 -2
  56. data/lib/chewy/stash.rb +3 -3
  57. data/lib/chewy/strategy/active_job.rb +1 -1
  58. data/lib/chewy/strategy/atomic_no_refresh.rb +18 -0
  59. data/lib/chewy/strategy/base.rb +10 -0
  60. data/lib/chewy/strategy/delayed_sidekiq/scheduler.rb +168 -0
  61. data/lib/chewy/strategy/delayed_sidekiq/worker.rb +76 -0
  62. data/lib/chewy/strategy/delayed_sidekiq.rb +30 -0
  63. data/lib/chewy/strategy/lazy_sidekiq.rb +64 -0
  64. data/lib/chewy/strategy/sidekiq.rb +1 -1
  65. data/lib/chewy/strategy.rb +3 -0
  66. data/lib/chewy/version.rb +1 -1
  67. data/lib/chewy.rb +21 -14
  68. data/lib/tasks/chewy.rake +18 -2
  69. data/migration_guide.md +1 -1
  70. data/spec/chewy/config_spec.rb +2 -2
  71. data/spec/chewy/elastic_client_spec.rb +26 -0
  72. data/spec/chewy/fields/base_spec.rb +39 -18
  73. data/spec/chewy/index/actions_spec.rb +10 -10
  74. data/spec/chewy/index/adapter/active_record_spec.rb +88 -0
  75. data/spec/chewy/index/import/bulk_builder_spec.rb +309 -1
  76. data/spec/chewy/index/import/routine_spec.rb +5 -5
  77. data/spec/chewy/index/import_spec.rb +48 -26
  78. data/spec/chewy/index/observe/active_record_methods_spec.rb +68 -0
  79. data/spec/chewy/index/observe/callback_spec.rb +139 -0
  80. data/spec/chewy/index/observe_spec.rb +27 -0
  81. data/spec/chewy/journal_spec.rb +13 -49
  82. data/spec/chewy/minitest/helpers_spec.rb +111 -1
  83. data/spec/chewy/minitest/search_index_receiver_spec.rb +6 -4
  84. data/spec/chewy/rake_helper_spec.rb +170 -0
  85. data/spec/chewy/rspec/build_query_spec.rb +34 -0
  86. data/spec/chewy/rspec/helpers_spec.rb +61 -0
  87. data/spec/chewy/search/pagination/kaminari_examples.rb +1 -1
  88. data/spec/chewy/search/pagination/kaminari_spec.rb +1 -1
  89. data/spec/chewy/search/parameters/collapse_spec.rb +5 -0
  90. data/spec/chewy/search/parameters/ignore_unavailable_spec.rb +67 -0
  91. data/spec/chewy/search/parameters/knn_spec.rb +5 -0
  92. data/spec/chewy/search/parameters/order_spec.rb +18 -11
  93. data/spec/chewy/search/parameters/track_total_hits_spec.rb +5 -0
  94. data/spec/chewy/search/parameters_spec.rb +6 -1
  95. data/spec/chewy/search/request_spec.rb +58 -9
  96. data/spec/chewy/search_spec.rb +9 -0
  97. data/spec/chewy/strategy/active_job_spec.rb +8 -8
  98. data/spec/chewy/strategy/atomic_no_refresh_spec.rb +60 -0
  99. data/spec/chewy/strategy/delayed_sidekiq_spec.rb +208 -0
  100. data/spec/chewy/strategy/lazy_sidekiq_spec.rb +214 -0
  101. data/spec/chewy/strategy/sidekiq_spec.rb +4 -4
  102. data/spec/chewy_spec.rb +10 -7
  103. data/spec/spec_helper.rb +1 -2
  104. data/spec/support/active_record.rb +8 -1
  105. metadata +45 -264
  106. data/lib/chewy/backports/deep_dup.rb +0 -46
  107. data/lib/chewy/backports/duplicable.rb +0 -91
  108. data/lib/chewy/index/import/thread_safe_progress_bar.rb +0 -40
@@ -60,6 +60,19 @@ describe Chewy::Index::Import do
60
60
  CitiesIndex.import(dummy_city)
61
61
  end
62
62
  end
63
+
64
+ context 'skip journal creation on import' do
65
+ before do
66
+ Chewy::Stash::Journal.create!
67
+ Chewy.config.settings[:skip_journal_creation_on_import] = true
68
+ end
69
+ after { Chewy.config.settings[:skip_journal_creation_on_import] = nil }
70
+
71
+ specify do
72
+ expect(Chewy::Stash::Journal).not_to receive(:create!)
73
+ CitiesIndex.import(dummy_city, journal: true)
74
+ end
75
+ end
63
76
  end
64
77
 
65
78
  shared_examples 'importing' do
@@ -202,8 +215,8 @@ describe Chewy::Index::Import do
202
215
  payload = subscribe_notification
203
216
  import dummy_cities, batch_size: 2
204
217
  expect(payload).to eq(index: CitiesIndex,
205
- errors: {index: {mapper_parsing_exception => %w[1 2 3]}},
206
- import: {index: 3})
218
+ errors: {index: {mapper_parsing_exception => %w[1 2 3]}},
219
+ import: {index: 3})
207
220
  end
208
221
  end
209
222
  end
@@ -494,32 +507,43 @@ describe Chewy::Index::Import do
494
507
  it_behaves_like 'importing'
495
508
  end
496
509
 
497
- context 'with progressbar output' do
498
- let(:mocked_progressbar) { Struct.new(:progress, :total).new(0, 100) }
499
-
500
- it 'imports tracks progress in a single batch' do
501
- expect(ProgressBar).to receive(:create).and_return(mocked_progressbar)
502
- expect(mocked_progressbar).to receive(:progress).at_least(:once).and_call_original
503
- expect(CitiesIndex).to receive(:import_parallel).and_call_original
504
-
505
- CitiesIndex.import(parallel: 1, progressbar: true)
510
+ context 'with parent-child relationship' do
511
+ before do
512
+ stub_model(:comment)
513
+ stub_index(:comments) do
514
+ index_scope Comment
515
+ field :content
516
+ field :comment_type, type: :join, relations: {question: %i[answer comment], answer: :vote}, join: {type: :comment_type, id: :commented_id}
517
+ end
518
+ end
506
519
 
507
- expect(mocked_progressbar.progress).to eq(3)
508
- expect(mocked_progressbar.total).to eq(3)
520
+ let!(:comments) do
521
+ [
522
+ Comment.create!(id: 1, content: 'Where is Nemo?', comment_type: :question),
523
+ Comment.create!(id: 2, content: 'Here.', comment_type: :answer, commented_id: 1),
524
+ Comment.create!(id: 3, content: 'There!', comment_type: :answer, commented_id: 1),
525
+ Comment.create!(id: 4, content: 'Yes, he is here.', comment_type: :vote, commented_id: 2)
526
+ ]
509
527
  end
510
528
 
511
- it 'imports tracks progress in many batches' do
512
- expect(ProgressBar).to receive(:create).and_return(mocked_progressbar)
513
- expect(mocked_progressbar).to receive(:progress).at_least(:once).and_call_original
514
- expect(CitiesIndex).to receive(:import_parallel).and_call_original
529
+ def imported_comments
530
+ CommentsIndex.all.map do |comment|
531
+ comment.attributes.except('_score', '_explanation')
532
+ end
533
+ end
515
534
 
516
- batches = City.pluck(:id).map { |id| [id] }
517
- expect(CitiesIndex.adapter).to receive(:import_references).and_return(batches)
535
+ it 'imports parent and children' do
536
+ CommentsIndex.import!(comments.map(&:id))
518
537
 
519
- CitiesIndex.import(parallel: 3, progressbar: true)
538
+ expect(imported_comments).to match_array([
539
+ {'id' => '1', 'content' => 'Where is Nemo?', 'comment_type' => 'question'},
540
+ {'id' => '2', 'content' => 'Here.', 'comment_type' => {'name' => 'answer', 'parent' => 1}},
541
+ {'id' => '3', 'content' => 'There!', 'comment_type' => {'name' => 'answer', 'parent' => 1}},
542
+ {'id' => '4', 'content' => 'Yes, he is here.', 'comment_type' => {'name' => 'vote', 'parent' => 2}}
543
+ ])
520
544
 
521
- expect(mocked_progressbar.progress).to eq(3)
522
- expect(mocked_progressbar.total).to eq(3)
545
+ answer_ids = CommentsIndex.query(has_parent: {parent_type: 'question', query: {match: {content: 'Where'}}}).pluck(:_id)
546
+ expect(answer_ids).to match_array(%w[2 3])
523
547
  end
524
548
  end
525
549
  end
@@ -535,9 +559,7 @@ describe Chewy::Index::Import do
535
559
  end
536
560
  end
537
561
 
538
- specify do
539
- expect { CitiesIndex.import!(dummy_cities) }.to raise_error Chewy::ImportFailed
540
- end
562
+ specify { expect { CitiesIndex.import!(dummy_cities) }.to raise_error Chewy::ImportFailed }
541
563
  end
542
564
  end
543
565
 
@@ -545,7 +567,7 @@ describe Chewy::Index::Import do
545
567
  before do
546
568
  stub_index(:cities) do
547
569
  crutch :names do |collection|
548
- collection.map { |o| [o.name, "#{o.name}42"] }.to_h
570
+ collection.to_h { |o| [o.name, "#{o.name}42"] }
549
571
  end
550
572
  field :name, value: ->(o, c) { c.names[o.name] }
551
573
  field :rating
@@ -0,0 +1,68 @@
1
+ require 'spec_helper'
2
+
3
+ describe Chewy::Index::Observe::ActiveRecordMethods do
4
+ describe '.update_index' do
5
+ before { stub_model(:city) }
6
+
7
+ it 'initializes chewy callbacks when first update_index is evaluated' do
8
+ expect(City).to receive(:initialize_chewy_callbacks).once
9
+ City.update_index 'cities', :self
10
+ City.update_index 'countries', -> {}
11
+ end
12
+
13
+ it 'adds chewy callbacks to model' do
14
+ expect(City.chewy_callbacks.count).to eq(0)
15
+
16
+ City.update_index 'cities', :self
17
+ City.update_index 'countries', -> {}
18
+
19
+ expect(City.chewy_callbacks.count).to eq(2)
20
+ end
21
+ end
22
+
23
+ describe 'callbacks' do
24
+ before { stub_model(:city) { update_index 'cities', :self } }
25
+ before { stub_index(:cities) { index_scope City } }
26
+ before { allow(Chewy).to receive(:use_after_commit_callbacks).and_return(use_after_commit_callbacks) }
27
+
28
+ let(:city) do
29
+ Chewy.strategy(:bypass) do
30
+ City.create!
31
+ end
32
+ end
33
+
34
+ shared_examples 'handles callbacks correctly' do
35
+ it 'handles callbacks with strategy for possible lazy evaluation on save!' do
36
+ Chewy.strategy(:urgent) do
37
+ expect(city).to receive(:update_chewy_indices).and_call_original
38
+ expect(Chewy.strategy.current).to receive(:update_chewy_indices).with(city)
39
+ expect(city).not_to receive(:run_chewy_callbacks)
40
+
41
+ city.save!
42
+ end
43
+ end
44
+
45
+ it 'runs callbacks at the moment on destroy' do
46
+ Chewy.strategy(:urgent) do
47
+ expect(city).not_to receive(:update_chewy_indices)
48
+ expect(Chewy.strategy.current).not_to receive(:update_chewy_indices)
49
+ expect(city).to receive(:run_chewy_callbacks)
50
+
51
+ city.destroy
52
+ end
53
+ end
54
+ end
55
+
56
+ context 'when Chewy.use_after_commit_callbacks is true' do
57
+ let(:use_after_commit_callbacks) { true }
58
+
59
+ include_examples 'handles callbacks correctly'
60
+ end
61
+
62
+ context 'when Chewy.use_after_commit_callbacks is false' do
63
+ let(:use_after_commit_callbacks) { false }
64
+
65
+ include_examples 'handles callbacks correctly'
66
+ end
67
+ end
68
+ end
@@ -0,0 +1,139 @@
1
+ require 'spec_helper'
2
+
3
+ describe Chewy::Index::Observe::Callback do
4
+ subject(:callback) { described_class.new(executable) }
5
+
6
+ before do
7
+ stub_model(:city) do
8
+ attr_accessor :population
9
+ end
10
+ end
11
+
12
+ let(:city) { City.create!(population: 100) }
13
+
14
+ describe '#call' do
15
+ context 'when executable is has arity 0' do
16
+ let(:executable) { -> { population } }
17
+
18
+ it 'calls exectuable within context' do
19
+ expect(callback.call(city)).to eq(city.population)
20
+ end
21
+ end
22
+
23
+ context 'when executable is has arity 1' do
24
+ let(:executable) { ->(record) { record.population } }
25
+
26
+ it 'calls exectuable within context' do
27
+ expect(callback.call(city)).to eq(city.population)
28
+ end
29
+ end
30
+
31
+ describe 'filters' do
32
+ let(:executable) { ->(_) {} }
33
+
34
+ describe 'if' do
35
+ subject(:callback) { described_class.new(executable, if: filter) }
36
+
37
+ shared_examples 'an if filter' do
38
+ context 'when condition is true' do
39
+ let(:condition) { true }
40
+
41
+ specify do
42
+ expect(executable).to receive(:call).with(city)
43
+
44
+ callback.call(city)
45
+ end
46
+ end
47
+
48
+ context 'when condition is false' do
49
+ let(:condition) { false }
50
+
51
+ specify do
52
+ expect(executable).not_to receive(:call)
53
+
54
+ callback.call(city)
55
+ end
56
+ end
57
+ end
58
+
59
+ context 'when filter is symbol' do
60
+ let(:filter) { :condition }
61
+
62
+ before do
63
+ allow(city).to receive(:condition).and_return(condition)
64
+ end
65
+
66
+ include_examples 'an if filter'
67
+ end
68
+
69
+ context 'when filter is proc' do
70
+ let(:filter) { -> { condition_state } }
71
+
72
+ before do
73
+ allow_any_instance_of(City).to receive(:condition_state).and_return(condition)
74
+ end
75
+
76
+ include_examples 'an if filter'
77
+ end
78
+
79
+ context 'when filter is literal' do
80
+ let(:filter) { condition }
81
+
82
+ include_examples 'an if filter'
83
+ end
84
+ end
85
+
86
+ describe 'unless' do
87
+ subject(:callback) { described_class.new(executable, unless: filter) }
88
+
89
+ shared_examples 'an unless filter' do
90
+ context 'when condition is true' do
91
+ let(:condition) { true }
92
+
93
+ specify do
94
+ expect(executable).not_to receive(:call)
95
+
96
+ callback.call(city)
97
+ end
98
+ end
99
+
100
+ context 'when condition is false' do
101
+ let(:condition) { false }
102
+
103
+ specify do
104
+ expect(executable).to receive(:call).with(city)
105
+
106
+ callback.call(city)
107
+ end
108
+ end
109
+ end
110
+
111
+ context 'when filter is symbol' do
112
+ let(:filter) { :condition }
113
+
114
+ before do
115
+ allow(city).to receive(:condition).and_return(condition)
116
+ end
117
+
118
+ include_examples 'an unless filter'
119
+ end
120
+
121
+ context 'when filter is proc' do
122
+ let(:filter) { -> { condition_state } }
123
+
124
+ before do
125
+ allow_any_instance_of(City).to receive(:condition_state).and_return(condition)
126
+ end
127
+
128
+ include_examples 'an unless filter'
129
+ end
130
+
131
+ context 'when filter is literal' do
132
+ let(:filter) { condition }
133
+
134
+ include_examples 'an unless filter'
135
+ end
136
+ end
137
+ end
138
+ end
139
+ end
@@ -57,6 +57,9 @@ describe Chewy::Index::Observe do
57
57
  specify { expect { city.save! }.to update_index('cities').and_reindex(city).only }
58
58
  specify { expect { city.save! }.to update_index('countries').and_reindex(country1).only }
59
59
 
60
+ specify { expect { city.destroy }.to update_index('cities') }
61
+ specify { expect { city.destroy }.to update_index('countries').and_reindex(country1).only }
62
+
60
63
  specify { expect { city.update!(country: nil) }.to update_index('cities').and_reindex(city).only }
61
64
  specify { expect { city.update!(country: nil) }.to update_index('countries').and_reindex(country1).only }
62
65
 
@@ -78,9 +81,13 @@ describe Chewy::Index::Observe do
78
81
  specify { expect { country.save! }.to update_index('cities').and_reindex(country.cities).only }
79
82
  specify { expect { country.save! }.to update_index('countries').and_reindex(country).only }
80
83
 
84
+ specify { expect { country.destroy }.to update_index('cities').and_reindex(country.cities).only }
85
+ specify { expect { country.destroy }.to update_index('countries') }
86
+
81
87
  context 'conditional update' do
82
88
  let(:update_condition) { false }
83
89
  specify { expect { country.save! }.not_to update_index('cities') }
90
+ specify { expect { country.destroy }.not_to update_index('cities') }
84
91
  end
85
92
  end
86
93
  end
@@ -97,6 +104,16 @@ describe Chewy::Index::Observe do
97
104
  end
98
105
  end
99
106
  end
107
+
108
+ specify do
109
+ city = Chewy.strategy(:bypass) { City.create! }
110
+
111
+ Chewy.strategy(:urgent) do
112
+ ActiveRecord::Base.transaction do
113
+ expect { city.destroy }.not_to update_index('cities')
114
+ end
115
+ end
116
+ end
100
117
  end
101
118
 
102
119
  context do
@@ -111,6 +128,16 @@ describe Chewy::Index::Observe do
111
128
  end
112
129
  end
113
130
  end
131
+
132
+ specify do
133
+ city = Chewy.strategy(:bypass) { City.create! }
134
+
135
+ Chewy.strategy(:urgent) do
136
+ ActiveRecord::Base.transaction do
137
+ expect { city.destroy }.to update_index('cities')
138
+ end
139
+ end
140
+ end
114
141
  end
115
142
  end
116
143
  end
@@ -199,59 +199,23 @@ describe Chewy::Journal do
199
199
  end
200
200
  end
201
201
 
202
- context 'retries' do
203
- let(:time) { Time.now.to_i }
204
- before do
205
- Timecop.freeze
206
- Chewy.strategy(:urgent)
207
- City.create!(id: 1)
208
- end
209
-
210
- after do
211
- Chewy.strategy.pop
212
- Timecop.return
213
- end
214
-
215
- specify 'journal was cleaned after the first call' do
216
- expect(Chewy::Stash::Journal).to receive(:entries).exactly(2).and_call_original
217
- expect(described_class.new.apply(time)).to eq(1)
218
- end
219
-
220
- context 'endless journal' do
221
- let(:count_of_checks) { 10 } # default
222
- let!(:journal_entries) do
223
- record = Chewy::Stash::Journal.entries(time).first
224
- Array.new(count_of_checks) do |i|
225
- Chewy::Stash::Journal.new(
226
- record.attributes.merge(
227
- 'created_at' => time.to_i + i,
228
- 'references' => [i.to_s]
229
- )
230
- )
231
- end
232
- end
233
-
234
- specify '10 retries by default' do
235
- expect(Chewy::Stash::Journal)
236
- .to receive(:entries).exactly(count_of_checks) { [journal_entries.shift].compact }
237
- expect(described_class.new.apply(time)).to eq(10)
238
- end
202
+ context 'when order is not preserved' do
203
+ let(:time) { Time.now }
239
204
 
240
- specify 'with :once parameter set' do
241
- expect(Chewy::Stash::Journal)
242
- .to receive(:entries).exactly(1) { [journal_entries.shift].compact }
243
- expect(described_class.new.apply(time, retries: 1)).to eq(1)
205
+ it 'paginates properly through all items' do
206
+ Chewy.strategy(:urgent) do
207
+ Timecop.travel(time + 1.minute) { City.create!(id: 2) }
208
+ Timecop.travel(time + 3.minute) { City.create!(id: 4) }
209
+ Timecop.travel(time + 2.minute) { City.create!(id: 1) }
210
+ Timecop.travel(time + 4.minute) { City.create!(id: 3) }
244
211
  end
245
212
 
246
- context 'with retries parameter set' do
247
- let(:retries) { 5 }
213
+ CitiesIndex.purge!
214
+ expect(CitiesIndex.all.to_a.length).to eq 0
248
215
 
249
- specify do
250
- expect(Chewy::Stash::Journal)
251
- .to receive(:entries).exactly(retries) { [journal_entries.shift].compact }
252
- expect(described_class.new.apply(time, retries: retries)).to eq(5)
253
- end
254
- end
216
+ # Replay on specific index
217
+ expect(described_class.new(CitiesIndex).apply(time, fetch_limit: 2)).to eq(4)
218
+ expect(CitiesIndex.all.to_a.map(&:id).sort).to eq([1, 2, 3, 4])
255
219
  end
256
220
  end
257
221
  end
@@ -10,7 +10,11 @@ describe :minitest_helper do
10
10
  expect(haystack).to include(needle)
11
11
  end
12
12
 
13
- include ::Chewy::Minitest::Helpers
13
+ include Chewy::Minitest::Helpers
14
+
15
+ def assert_equal(expected, actual, message)
16
+ raise message unless expected == actual
17
+ end
14
18
 
15
19
  before do
16
20
  Chewy.massacre
@@ -22,6 +26,112 @@ describe :minitest_helper do
22
26
  end
23
27
  end
24
28
 
29
+ describe 'mock_elasticsearch_response' do
30
+ let(:hits) do
31
+ [
32
+ {
33
+ '_index' => 'dummies',
34
+ '_type' => '_doc',
35
+ '_id' => '2',
36
+ '_score' => 3.14,
37
+ '_source' => source
38
+ }
39
+ ]
40
+ end
41
+
42
+ let(:source) { {'name' => 'some_name', id: '2'} }
43
+ let(:sources) { [source] }
44
+
45
+ context 'mocks by raw response' do
46
+ let(:raw_response) do
47
+ {
48
+ 'took' => 4,
49
+ 'timed_out' => false,
50
+ '_shards' => {
51
+ 'total' => 1,
52
+ 'successful' => 1,
53
+ 'skipped' => 0,
54
+ 'failed' => 0
55
+ },
56
+ 'hits' => {
57
+ 'total' => {
58
+ 'value' => 1,
59
+ 'relation' => 'eq'
60
+ },
61
+ 'max_score' => 1.0,
62
+ 'hits' => hits
63
+ }
64
+ }
65
+ end
66
+
67
+ specify do
68
+ mock_elasticsearch_response(DummiesIndex, raw_response) do
69
+ expect(DummiesIndex.query({}).hits).to eq(hits)
70
+ end
71
+ end
72
+ end
73
+
74
+ context 'mocks by response sources' do
75
+ specify do
76
+ mock_elasticsearch_response_sources(DummiesIndex, sources) do
77
+ expect(DummiesIndex.query({}).hits).to eq(hits)
78
+ end
79
+ end
80
+ end
81
+ end
82
+
83
+ describe 'assert correct elasticsearch query' do
84
+ let(:query) do
85
+ DummiesIndex.filter.should { multi_match foo: 'bar' }.filter { match foo: 'bar' }
86
+ end
87
+
88
+ let(:expected_query) do
89
+ {
90
+ index: ['dummies'],
91
+ body: {
92
+ query: {
93
+ bool: {
94
+ filter: {
95
+ bool: {
96
+ must: {
97
+ match: {foo: 'bar'}
98
+ },
99
+ should: {
100
+ multi_match: {foo: 'bar'}
101
+ }
102
+ }
103
+ }
104
+ }
105
+ }
106
+ }
107
+ }
108
+ end
109
+
110
+ context 'will be built' do
111
+ specify do
112
+ expect { assert_elasticsearch_query(query, expected_query) }.not_to raise_error
113
+ end
114
+ end
115
+
116
+ context 'will not be built' do
117
+ let(:unexpected_query) do
118
+ {
119
+ index: ['what?'],
120
+ body: {}
121
+ }
122
+ end
123
+
124
+ let(:unexpected_query_error_message) do
125
+ 'got {:index=>["dummies"], :body=>{:query=>{:bool=>{:filter=>{:bool=>{:must=>{:match=>{:foo=>"bar"}}, :should=>{:multi_match=>{:foo=>"bar"}}}}}}}} instead of expected query.'
126
+ end
127
+
128
+ specify do
129
+ expect { assert_elasticsearch_query(query, unexpected_query) }
130
+ .to raise_error(RuntimeError, unexpected_query_error_message)
131
+ end
132
+ end
133
+ end
134
+
25
135
  context 'assert_indexes' do
26
136
  specify 'doesn\'t fail when index updates correctly' do
27
137
  expect do
@@ -24,6 +24,8 @@ describe :search_index_receiver do
24
24
  SearchIndexReceiver.new
25
25
  end
26
26
 
27
+ let(:dummy_class) { Struct.new(:id) }
28
+
27
29
  before do
28
30
  stub_index(:dummies) do
29
31
  root value: ->(_o) { {} }
@@ -82,12 +84,12 @@ describe :search_index_receiver do
82
84
  end
83
85
 
84
86
  specify 'validates that an object was indexed' do
85
- dummy = OpenStruct.new(id: 1)
87
+ dummy = dummy_class.new(1)
86
88
  expect(receiver.indexed?(dummy, DummiesIndex)).to be(true)
87
89
  end
88
90
 
89
91
  specify 'doesn\'t validate than unindexed objects were indexed' do
90
- dummy = OpenStruct.new(id: 2)
92
+ dummy = dummy_class.new(2)
91
93
  expect(receiver.indexed?(dummy, DummiesIndex)).to be(false)
92
94
  end
93
95
  end
@@ -98,12 +100,12 @@ describe :search_index_receiver do
98
100
  end
99
101
 
100
102
  specify 'validates than an object was deleted' do
101
- dummy = OpenStruct.new(id: 1)
103
+ dummy = dummy_class.new(1)
102
104
  expect(receiver.deleted?(dummy, DummiesIndex)).to be(true)
103
105
  end
104
106
 
105
107
  specify 'doesn\'t validate than undeleted objects were deleted' do
106
- dummy = OpenStruct.new(id: 2)
108
+ dummy = dummy_class.new(2)
107
109
  expect(receiver.deleted?(dummy, DummiesIndex)).to be(false)
108
110
  end
109
111
  end