chewy 7.2.4 → 7.6.0

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.
Files changed (86) 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 +26 -32
  5. data/.rubocop.yml +4 -1
  6. data/CHANGELOG.md +144 -0
  7. data/Gemfile +4 -4
  8. data/README.md +165 -10
  9. data/chewy.gemspec +4 -17
  10. data/gemfiles/base.gemfile +12 -0
  11. data/gemfiles/rails.6.1.activerecord.gemfile +2 -1
  12. data/gemfiles/rails.7.0.activerecord.gemfile +2 -1
  13. data/gemfiles/{rails.5.2.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 +5 -2
  17. data/lib/chewy/fields/base.rb +1 -1
  18. data/lib/chewy/fields/root.rb +1 -1
  19. data/lib/chewy/index/adapter/active_record.rb +13 -3
  20. data/lib/chewy/index/adapter/object.rb +3 -3
  21. data/lib/chewy/index/adapter/orm.rb +2 -2
  22. data/lib/chewy/index/crutch.rb +15 -7
  23. data/lib/chewy/index/import/bulk_builder.rb +6 -7
  24. data/lib/chewy/index/import/routine.rb +1 -1
  25. data/lib/chewy/index/import.rb +31 -4
  26. data/lib/chewy/index/observe/active_record_methods.rb +87 -0
  27. data/lib/chewy/index/observe/callback.rb +34 -0
  28. data/lib/chewy/index/observe.rb +3 -58
  29. data/lib/chewy/index/syncer.rb +1 -1
  30. data/lib/chewy/index.rb +25 -0
  31. data/lib/chewy/journal.rb +17 -6
  32. data/lib/chewy/log_subscriber.rb +5 -1
  33. data/lib/chewy/minitest/helpers.rb +1 -1
  34. data/lib/chewy/minitest/search_index_receiver.rb +3 -1
  35. data/lib/chewy/rake_helper.rb +74 -13
  36. data/lib/chewy/rspec/update_index.rb +13 -6
  37. data/lib/chewy/runtime/version.rb +1 -1
  38. data/lib/chewy/search/parameters/collapse.rb +16 -0
  39. data/lib/chewy/search/parameters/indices.rb +1 -1
  40. data/lib/chewy/search/parameters/knn.rb +16 -0
  41. data/lib/chewy/search/parameters/storage.rb +1 -1
  42. data/lib/chewy/search/parameters.rb +3 -3
  43. data/lib/chewy/search/request.rb +45 -11
  44. data/lib/chewy/search.rb +6 -3
  45. data/lib/chewy/stash.rb +3 -3
  46. data/lib/chewy/strategy/atomic_no_refresh.rb +18 -0
  47. data/lib/chewy/strategy/base.rb +10 -0
  48. data/lib/chewy/strategy/delayed_sidekiq/scheduler.rb +168 -0
  49. data/lib/chewy/strategy/delayed_sidekiq/worker.rb +76 -0
  50. data/lib/chewy/strategy/delayed_sidekiq.rb +30 -0
  51. data/lib/chewy/strategy/lazy_sidekiq.rb +64 -0
  52. data/lib/chewy/strategy.rb +3 -0
  53. data/lib/chewy/version.rb +1 -1
  54. data/lib/chewy.rb +5 -8
  55. data/lib/tasks/chewy.rake +17 -1
  56. data/migration_guide.md +1 -1
  57. data/spec/chewy/config_spec.rb +2 -2
  58. data/spec/chewy/elastic_client_spec.rb +26 -0
  59. data/spec/chewy/fields/base_spec.rb +1 -0
  60. data/spec/chewy/index/actions_spec.rb +4 -4
  61. data/spec/chewy/index/adapter/active_record_spec.rb +62 -0
  62. data/spec/chewy/index/import/bulk_builder_spec.rb +7 -3
  63. data/spec/chewy/index/import_spec.rb +16 -3
  64. data/spec/chewy/index/observe/active_record_methods_spec.rb +68 -0
  65. data/spec/chewy/index/observe/callback_spec.rb +139 -0
  66. data/spec/chewy/index/observe_spec.rb +27 -0
  67. data/spec/chewy/journal_spec.rb +13 -49
  68. data/spec/chewy/minitest/helpers_spec.rb +3 -3
  69. data/spec/chewy/minitest/search_index_receiver_spec.rb +6 -4
  70. data/spec/chewy/rake_helper_spec.rb +155 -4
  71. data/spec/chewy/rspec/helpers_spec.rb +1 -1
  72. data/spec/chewy/search/pagination/kaminari_examples.rb +1 -1
  73. data/spec/chewy/search/pagination/kaminari_spec.rb +1 -1
  74. data/spec/chewy/search/parameters/collapse_spec.rb +5 -0
  75. data/spec/chewy/search/parameters/knn_spec.rb +5 -0
  76. data/spec/chewy/search/request_spec.rb +37 -0
  77. data/spec/chewy/search_spec.rb +9 -0
  78. data/spec/chewy/strategy/active_job_spec.rb +8 -8
  79. data/spec/chewy/strategy/atomic_no_refresh_spec.rb +60 -0
  80. data/spec/chewy/strategy/delayed_sidekiq_spec.rb +208 -0
  81. data/spec/chewy/strategy/lazy_sidekiq_spec.rb +214 -0
  82. data/spec/chewy/strategy/sidekiq_spec.rb +4 -4
  83. data/spec/chewy_spec.rb +7 -4
  84. data/spec/spec_helper.rb +1 -1
  85. metadata +32 -253
  86. data/gemfiles/rails.6.0.activerecord.gemfile +0 -11
@@ -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
@@ -554,7 +567,7 @@ describe Chewy::Index::Import do
554
567
  before do
555
568
  stub_index(:cities) do
556
569
  crutch :names do |collection|
557
- collection.map { |o| [o.name, "#{o.name}42"] }.to_h
570
+ collection.to_h { |o| [o.name, "#{o.name}42"] }
558
571
  end
559
572
  field :name, value: ->(o, c) { c.names[o.name] }
560
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,7 @@ 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
14
 
15
15
  def assert_equal(expected, actual, message)
16
16
  raise message unless expected == actual
@@ -32,14 +32,14 @@ describe :minitest_helper do
32
32
  {
33
33
  '_index' => 'dummies',
34
34
  '_type' => '_doc',
35
- '_id' => '1',
35
+ '_id' => '2',
36
36
  '_score' => 3.14,
37
37
  '_source' => source
38
38
  }
39
39
  ]
40
40
  end
41
41
 
42
- let(:source) { {'name' => 'some_name'} }
42
+ let(:source) { {'name' => 'some_name', id: '2'} }
43
43
  let(:sources) { [source] }
44
44
 
45
45
  context 'mocks by raw response' 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
@@ -1,4 +1,5 @@
1
1
  require 'spec_helper'
2
+ require 'rake'
2
3
 
3
4
  describe Chewy::RakeHelper, :orm do
4
5
  before { Chewy.massacre }
@@ -104,10 +105,10 @@ Total: \\d+s\\Z
104
105
  expect { described_class.reset(only: [CitiesIndex], output: output) }
105
106
  .to update_index(CitiesIndex)
106
107
  expect(output.string).to include(
107
- "############################################################\n"\
108
- "WARN: You are risking to lose some changes during the reset.\n" \
109
- " Please consider enabling journaling.\n" \
110
- ' See https://github.com/toptal/chewy#journaling'
108
+ "############################################################\n" \
109
+ "WARN: You are risking to lose some changes during the reset.\n " \
110
+ "Please consider enabling journaling.\n " \
111
+ 'See https://github.com/toptal/chewy#journaling'
111
112
  )
112
113
  end
113
114
  end
@@ -426,6 +427,108 @@ Total: \\d+s\\Z
426
427
  described_class.journal_clean(except: CitiesIndex, output: output)
427
428
  expect(output.string).to match(Regexp.new(<<-OUTPUT, Regexp::MULTILINE))
428
429
  \\ACleaned up 1 journal entries
430
+ Total: \\d+s\\Z
431
+ OUTPUT
432
+ end
433
+
434
+ it 'executes asynchronously' do
435
+ output = StringIO.new
436
+ expect(Chewy.client).to receive(:delete_by_query).with(
437
+ {
438
+ body: {query: {match_all: {}}},
439
+ index: ['chewy_journal'],
440
+ refresh: false,
441
+ requests_per_second: 10.0,
442
+ scroll_size: 200,
443
+ wait_for_completion: false
444
+ }
445
+ ).and_call_original
446
+ described_class.journal_clean(
447
+ output: output,
448
+ delete_by_query_options: {
449
+ wait_for_completion: false,
450
+ requests_per_second: 10.0,
451
+ scroll_size: 200
452
+ }
453
+ )
454
+
455
+ expect(output.string).to match(Regexp.new(<<-OUTPUT, Regexp::MULTILINE))
456
+ \\ATask to cleanup the journal has been created, [^\\n]*
457
+ Total: \\d+s\\Z
458
+ OUTPUT
459
+ end
460
+
461
+ context 'execute "chewy:journal:clean" rake task' do
462
+ subject(:task) { Rake.application['chewy:journal:clean'] }
463
+ before do
464
+ Rake::DefaultLoader.new.load('lib/tasks/chewy.rake')
465
+ Rake::Task.define_task(:environment)
466
+ end
467
+ it 'does not raise error' do
468
+ expect { task.invoke }.to_not raise_error
469
+ end
470
+ end
471
+ end
472
+
473
+ describe '.create_missing_indexes!' do
474
+ before do
475
+ [CountriesIndex, Chewy::Stash::Specification].map(&:create!)
476
+
477
+ # To avoid flaky issues when previous specs were run
478
+ expect(Chewy::Index).to receive(:descendants).and_return(
479
+ [
480
+ UsersIndex,
481
+ CountriesIndex,
482
+ CitiesIndex,
483
+ Chewy::Stash::Specification,
484
+ Chewy::Stash::Journal
485
+ ]
486
+ )
487
+ end
488
+
489
+ specify do
490
+ output = StringIO.new
491
+ described_class.create_missing_indexes!(output: output)
492
+ expect(CitiesIndex.exists?).to be_truthy
493
+ expect(UsersIndex.exists?).to be_truthy
494
+ expect(Chewy::Stash::Journal.exists?).to be_falsey
495
+ expect(output.string).to match(Regexp.new(<<-OUTPUT, Regexp::MULTILINE))
496
+ UsersIndex index successfully created
497
+ CitiesIndex index successfully created
498
+ Total: \\d+s\\Z
499
+ OUTPUT
500
+ end
501
+
502
+ context 'when verbose' do
503
+ specify do
504
+ output = StringIO.new
505
+ described_class.create_missing_indexes!(output: output, env: {'VERBOSE' => '1'})
506
+ expect(output.string).to match(Regexp.new(<<-OUTPUT, Regexp::MULTILINE))
507
+ UsersIndex index successfully created
508
+ CountriesIndex already exists, skipping
509
+ CitiesIndex index successfully created
510
+ Chewy::Stash::Specification already exists, skipping
511
+ Total: \\d+s\\Z
512
+ OUTPUT
513
+ end
514
+ end
515
+
516
+ context 'when journaling is enabled' do
517
+ before { Chewy.config.settings[:journal] = true }
518
+ after { Chewy.config.settings.delete(:journal) }
519
+ specify do
520
+ described_class.create_missing_indexes!(output: StringIO.new)
521
+ expect(Chewy::Stash::Journal.exists?).to be_truthy
522
+ end
523
+ end
524
+ end
525
+
526
+ describe '.journal_create' do
527
+ specify do
528
+ output = StringIO.new
529
+ described_class.journal_create(output: output)
530
+ expect(Chewy::Stash::Journal.exists?).to be_truthy
531
+ expect(output.string).to match(Regexp.new(<<-OUTPUT, Regexp::MULTILINE))
429
532
  Total: \\d+s\\Z
430
533
  OUTPUT
431
534
  end
@@ -502,4 +605,52 @@ Total: \\d+s\\Z
502
605
  end
503
606
  end
504
607
  end
608
+
609
+ describe '.delete_by_query_options_from_env' do
610
+ subject(:options) { described_class.delete_by_query_options_from_env(env) }
611
+ let(:env) do
612
+ {
613
+ 'WAIT_FOR_COMPLETION' => 'false',
614
+ 'REQUESTS_PER_SECOND' => '10',
615
+ 'SCROLL_SIZE' => '5000'
616
+ }
617
+ end
618
+
619
+ it 'parses the options' do
620
+ expect(options).to eq(
621
+ wait_for_completion: false,
622
+ requests_per_second: 10.0,
623
+ scroll_size: 5000
624
+ )
625
+ end
626
+
627
+ context 'with different boolean values' do
628
+ it 'parses the option correctly' do
629
+ %w[1 t true TRUE on ON].each do |v|
630
+ expect(described_class.delete_by_query_options_from_env({'WAIT_FOR_COMPLETION' => v}))
631
+ .to eq(wait_for_completion: true)
632
+ end
633
+
634
+ %w[0 f false FALSE off OFF].each do |v|
635
+ expect(described_class.delete_by_query_options_from_env({'WAIT_FOR_COMPLETION' => v}))
636
+ .to eq(wait_for_completion: false)
637
+ end
638
+ end
639
+ end
640
+
641
+ context 'with other env' do
642
+ let(:env) { {'SOME_ENV' => '123', 'REQUESTS_PER_SECOND' => '15'} }
643
+
644
+ it 'parses only the options' do
645
+ expect(options).to eq(requests_per_second: 15.0)
646
+ end
647
+ end
648
+ end
649
+
650
+ describe '.subscribed_task_stats' do
651
+ specify do
652
+ block_output = described_class.subscribed_task_stats(StringIO.new) { 'expected output' }
653
+ expect(block_output).to eq('expected output')
654
+ end
655
+ end
505
656
  end
@@ -1,7 +1,7 @@
1
1
  require 'spec_helper'
2
2
 
3
3
  describe :rspec_helper do
4
- include ::Chewy::Rspec::Helpers
4
+ include Chewy::Rspec::Helpers
5
5
 
6
6
  before do
7
7
  stub_model(:city)
@@ -24,7 +24,7 @@ shared_examples :kaminari do |request_base_class|
24
24
  let(:data) { Array.new(10) { |i| {id: i.next.to_s, name: "Name#{i.next}", age: 10 * i.next}.stringify_keys! } }
25
25
 
26
26
  before { ProductsIndex.import!(data.map { |h| double(h) }) }
27
- before { allow(::Kaminari.config).to receive_messages(default_per_page: 3) }
27
+ before { allow(Kaminari.config).to receive_messages(default_per_page: 3) }
28
28
 
29
29
  describe '#per, #page' do
30
30
  specify { expect(search.map { |e| e.attributes.except(*except_fields) }).to match_array(data) }
@@ -6,7 +6,7 @@ describe Chewy::Search::Pagination::Kaminari do
6
6
  let(:data) { Array.new(12) { |i| {id: i.next.to_s, name: "Name#{i.next}", age: 10 * i.next}.stringify_keys! } }
7
7
 
8
8
  before { ProductsIndex.import!(data.map { |h| double(h) }) }
9
- before { allow(::Kaminari.config).to receive_messages(default_per_page: 17) }
9
+ before { allow(Kaminari.config).to receive_messages(default_per_page: 17) }
10
10
 
11
11
  specify { expect(search.objects.class).to eq(Kaminari::PaginatableArray) }
12
12
  specify { expect(search.objects.total_count).to eq(12) }
@@ -0,0 +1,5 @@
1
+ require 'chewy/search/parameters/hash_storage_examples'
2
+
3
+ describe Chewy::Search::Parameters::Collapse do
4
+ it_behaves_like :hash_storage, :collapse
5
+ end
@@ -0,0 +1,5 @@
1
+ require 'chewy/search/parameters/hash_storage_examples'
2
+
3
+ describe Chewy::Search::Parameters::Knn do
4
+ it_behaves_like :hash_storage, :knn
5
+ end