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.
- checksums.yaml +4 -4
- data/.github/CODEOWNERS +1 -0
- data/.github/dependabot.yml +42 -0
- data/.github/workflows/ruby.yml +26 -32
- data/.rubocop.yml +4 -1
- data/CHANGELOG.md +144 -0
- data/Gemfile +4 -4
- data/README.md +165 -10
- data/chewy.gemspec +4 -17
- data/gemfiles/base.gemfile +12 -0
- data/gemfiles/rails.6.1.activerecord.gemfile +2 -1
- data/gemfiles/rails.7.0.activerecord.gemfile +2 -1
- data/gemfiles/{rails.5.2.activerecord.gemfile → rails.7.1.activerecord.gemfile} +6 -3
- data/lib/chewy/config.rb +22 -14
- data/lib/chewy/elastic_client.rb +31 -0
- data/lib/chewy/errors.rb +5 -2
- data/lib/chewy/fields/base.rb +1 -1
- data/lib/chewy/fields/root.rb +1 -1
- data/lib/chewy/index/adapter/active_record.rb +13 -3
- data/lib/chewy/index/adapter/object.rb +3 -3
- data/lib/chewy/index/adapter/orm.rb +2 -2
- data/lib/chewy/index/crutch.rb +15 -7
- data/lib/chewy/index/import/bulk_builder.rb +6 -7
- data/lib/chewy/index/import/routine.rb +1 -1
- data/lib/chewy/index/import.rb +31 -4
- data/lib/chewy/index/observe/active_record_methods.rb +87 -0
- data/lib/chewy/index/observe/callback.rb +34 -0
- data/lib/chewy/index/observe.rb +3 -58
- data/lib/chewy/index/syncer.rb +1 -1
- data/lib/chewy/index.rb +25 -0
- data/lib/chewy/journal.rb +17 -6
- data/lib/chewy/log_subscriber.rb +5 -1
- data/lib/chewy/minitest/helpers.rb +1 -1
- data/lib/chewy/minitest/search_index_receiver.rb +3 -1
- data/lib/chewy/rake_helper.rb +74 -13
- data/lib/chewy/rspec/update_index.rb +13 -6
- data/lib/chewy/runtime/version.rb +1 -1
- data/lib/chewy/search/parameters/collapse.rb +16 -0
- data/lib/chewy/search/parameters/indices.rb +1 -1
- data/lib/chewy/search/parameters/knn.rb +16 -0
- data/lib/chewy/search/parameters/storage.rb +1 -1
- data/lib/chewy/search/parameters.rb +3 -3
- data/lib/chewy/search/request.rb +45 -11
- data/lib/chewy/search.rb +6 -3
- data/lib/chewy/stash.rb +3 -3
- data/lib/chewy/strategy/atomic_no_refresh.rb +18 -0
- data/lib/chewy/strategy/base.rb +10 -0
- data/lib/chewy/strategy/delayed_sidekiq/scheduler.rb +168 -0
- data/lib/chewy/strategy/delayed_sidekiq/worker.rb +76 -0
- data/lib/chewy/strategy/delayed_sidekiq.rb +30 -0
- data/lib/chewy/strategy/lazy_sidekiq.rb +64 -0
- data/lib/chewy/strategy.rb +3 -0
- data/lib/chewy/version.rb +1 -1
- data/lib/chewy.rb +5 -8
- data/lib/tasks/chewy.rake +17 -1
- data/migration_guide.md +1 -1
- data/spec/chewy/config_spec.rb +2 -2
- data/spec/chewy/elastic_client_spec.rb +26 -0
- data/spec/chewy/fields/base_spec.rb +1 -0
- data/spec/chewy/index/actions_spec.rb +4 -4
- data/spec/chewy/index/adapter/active_record_spec.rb +62 -0
- data/spec/chewy/index/import/bulk_builder_spec.rb +7 -3
- data/spec/chewy/index/import_spec.rb +16 -3
- data/spec/chewy/index/observe/active_record_methods_spec.rb +68 -0
- data/spec/chewy/index/observe/callback_spec.rb +139 -0
- data/spec/chewy/index/observe_spec.rb +27 -0
- data/spec/chewy/journal_spec.rb +13 -49
- data/spec/chewy/minitest/helpers_spec.rb +3 -3
- data/spec/chewy/minitest/search_index_receiver_spec.rb +6 -4
- data/spec/chewy/rake_helper_spec.rb +155 -4
- data/spec/chewy/rspec/helpers_spec.rb +1 -1
- data/spec/chewy/search/pagination/kaminari_examples.rb +1 -1
- data/spec/chewy/search/pagination/kaminari_spec.rb +1 -1
- data/spec/chewy/search/parameters/collapse_spec.rb +5 -0
- data/spec/chewy/search/parameters/knn_spec.rb +5 -0
- data/spec/chewy/search/request_spec.rb +37 -0
- data/spec/chewy/search_spec.rb +9 -0
- data/spec/chewy/strategy/active_job_spec.rb +8 -8
- data/spec/chewy/strategy/atomic_no_refresh_spec.rb +60 -0
- data/spec/chewy/strategy/delayed_sidekiq_spec.rb +208 -0
- data/spec/chewy/strategy/lazy_sidekiq_spec.rb +214 -0
- data/spec/chewy/strategy/sidekiq_spec.rb +4 -4
- data/spec/chewy_spec.rb +7 -4
- data/spec/spec_helper.rb +1 -1
- metadata +32 -253
- 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
|
-
|
|
206
|
-
|
|
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.
|
|
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
|
data/spec/chewy/journal_spec.rb
CHANGED
|
@@ -199,59 +199,23 @@ describe Chewy::Journal do
|
|
|
199
199
|
end
|
|
200
200
|
end
|
|
201
201
|
|
|
202
|
-
context '
|
|
203
|
-
let(:time) { Time.now
|
|
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
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
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
|
-
|
|
247
|
-
|
|
213
|
+
CitiesIndex.purge!
|
|
214
|
+
expect(CitiesIndex.all.to_a.length).to eq 0
|
|
248
215
|
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
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
|
|
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' => '
|
|
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 =
|
|
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 =
|
|
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 =
|
|
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 =
|
|
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
|
-
"
|
|
110
|
-
'
|
|
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
|
|
@@ -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(
|
|
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(
|
|
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) }
|