chewy 7.2.1 → 7.6.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/.github/CODEOWNERS +1 -0
- data/.github/dependabot.yml +42 -0
- data/.github/workflows/ruby.yml +28 -26
- data/.rubocop.yml +4 -1
- data/CHANGELOG.md +196 -0
- data/Gemfile +4 -3
- data/README.md +203 -20
- data/chewy.gemspec +4 -18
- data/gemfiles/base.gemfile +12 -0
- data/gemfiles/rails.6.1.activerecord.gemfile +2 -1
- data/gemfiles/{rails.5.2.activerecord.gemfile → rails.7.0.activerecord.gemfile} +6 -3
- data/gemfiles/{rails.6.0.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 +11 -2
- data/lib/chewy/fields/base.rb +69 -13
- data/lib/chewy/fields/root.rb +2 -10
- data/lib/chewy/index/actions.rb +11 -16
- data/lib/chewy/index/adapter/active_record.rb +18 -3
- data/lib/chewy/index/adapter/object.rb +0 -10
- data/lib/chewy/index/adapter/orm.rb +4 -14
- data/lib/chewy/index/crutch.rb +15 -7
- data/lib/chewy/index/import/bulk_builder.rb +219 -32
- data/lib/chewy/index/import/bulk_request.rb +1 -1
- data/lib/chewy/index/import/routine.rb +3 -3
- data/lib/chewy/index/import.rb +45 -31
- data/lib/chewy/index/mapping.rb +2 -2
- 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 +77 -0
- data/lib/chewy/minitest/search_index_receiver.rb +3 -1
- data/lib/chewy/rake_helper.rb +92 -11
- data/lib/chewy/rspec/build_query.rb +12 -0
- data/lib/chewy/rspec/helpers.rb +55 -0
- data/lib/chewy/rspec/update_index.rb +14 -7
- data/lib/chewy/rspec.rb +2 -0
- data/lib/chewy/runtime/version.rb +1 -1
- data/lib/chewy/runtime.rb +1 -1
- data/lib/chewy/search/parameters/collapse.rb +16 -0
- data/lib/chewy/search/parameters/ignore_unavailable.rb +27 -0
- data/lib/chewy/search/parameters/indices.rb +1 -1
- data/lib/chewy/search/parameters/knn.rb +16 -0
- data/lib/chewy/search/parameters/order.rb +6 -19
- data/lib/chewy/search/parameters/storage.rb +1 -1
- data/lib/chewy/search/parameters/track_total_hits.rb +16 -0
- data/lib/chewy/search/parameters.rb +4 -4
- data/lib/chewy/search/request.rb +74 -16
- data/lib/chewy/search/scoping.rb +1 -1
- data/lib/chewy/search.rb +5 -2
- data/lib/chewy/stash.rb +3 -3
- data/lib/chewy/strategy/active_job.rb +1 -1
- 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/sidekiq.rb +1 -1
- data/lib/chewy/strategy.rb +3 -0
- data/lib/chewy/version.rb +1 -1
- data/lib/chewy.rb +21 -14
- data/lib/tasks/chewy.rake +18 -2
- 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 +39 -18
- data/spec/chewy/index/actions_spec.rb +10 -10
- data/spec/chewy/index/adapter/active_record_spec.rb +88 -0
- data/spec/chewy/index/import/bulk_builder_spec.rb +309 -1
- data/spec/chewy/index/import/routine_spec.rb +5 -5
- data/spec/chewy/index/import_spec.rb +48 -26
- 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 +111 -1
- data/spec/chewy/minitest/search_index_receiver_spec.rb +6 -4
- data/spec/chewy/rake_helper_spec.rb +170 -0
- data/spec/chewy/rspec/build_query_spec.rb +34 -0
- data/spec/chewy/rspec/helpers_spec.rb +61 -0
- 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/ignore_unavailable_spec.rb +67 -0
- data/spec/chewy/search/parameters/knn_spec.rb +5 -0
- data/spec/chewy/search/parameters/order_spec.rb +18 -11
- data/spec/chewy/search/parameters/track_total_hits_spec.rb +5 -0
- data/spec/chewy/search/parameters_spec.rb +6 -1
- data/spec/chewy/search/request_spec.rb +58 -9
- 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 +10 -7
- data/spec/spec_helper.rb +1 -2
- data/spec/support/active_record.rb +8 -1
- metadata +45 -264
- data/lib/chewy/backports/deep_dup.rb +0 -46
- data/lib/chewy/backports/duplicable.rb +0 -91
- 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
|
-
|
206
|
-
|
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
|
498
|
-
|
499
|
-
|
500
|
-
|
501
|
-
|
502
|
-
|
503
|
-
|
504
|
-
|
505
|
-
|
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
|
-
|
508
|
-
|
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
|
-
|
512
|
-
|
513
|
-
|
514
|
-
|
529
|
+
def imported_comments
|
530
|
+
CommentsIndex.all.map do |comment|
|
531
|
+
comment.attributes.except('_score', '_explanation')
|
532
|
+
end
|
533
|
+
end
|
515
534
|
|
516
|
-
|
517
|
-
|
535
|
+
it 'imports parent and children' do
|
536
|
+
CommentsIndex.import!(comments.map(&:id))
|
518
537
|
|
519
|
-
|
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
|
-
|
522
|
-
expect(
|
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
|
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.
|
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
|
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,11 @@ describe :minitest_helper do
|
|
10
10
|
expect(haystack).to include(needle)
|
11
11
|
end
|
12
12
|
|
13
|
-
include
|
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 =
|
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
|