elasticsearch-model 6.0.0 → 6.1.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (118) hide show
  1. checksums.yaml +4 -4
  2. data/Gemfile +5 -0
  3. data/README.md +14 -7
  4. data/Rakefile +27 -36
  5. data/elasticsearch-model.gemspec +1 -1
  6. data/examples/activerecord_mapping_completion.rb +2 -15
  7. data/gemfiles/3.0.gemfile +6 -1
  8. data/gemfiles/4.0.gemfile +7 -1
  9. data/gemfiles/5.0.gemfile +6 -0
  10. data/lib/elasticsearch/model.rb +15 -8
  11. data/lib/elasticsearch/model/adapters/active_record.rb +7 -26
  12. data/lib/elasticsearch/model/indexing.rb +5 -3
  13. data/lib/elasticsearch/model/naming.rb +6 -1
  14. data/lib/elasticsearch/model/response.rb +2 -2
  15. data/lib/elasticsearch/model/response/pagination.rb +2 -192
  16. data/lib/elasticsearch/model/response/pagination/kaminari.rb +109 -0
  17. data/lib/elasticsearch/model/response/pagination/will_paginate.rb +95 -0
  18. data/lib/elasticsearch/model/response/result.rb +1 -1
  19. data/lib/elasticsearch/model/version.rb +1 -1
  20. data/spec/elasticsearch/model/adapter_spec.rb +119 -0
  21. data/spec/elasticsearch/model/adapters/active_record/associations_spec.rb +334 -0
  22. data/spec/elasticsearch/model/adapters/active_record/basic_spec.rb +340 -0
  23. data/spec/elasticsearch/model/adapters/active_record/dynamic_index_name_spec.rb +18 -0
  24. data/spec/elasticsearch/model/adapters/active_record/import_spec.rb +187 -0
  25. data/spec/elasticsearch/model/adapters/active_record/multi_model_spec.rb +110 -0
  26. data/spec/elasticsearch/model/adapters/active_record/namespaced_model_spec.rb +38 -0
  27. data/spec/elasticsearch/model/adapters/active_record/pagination_spec.rb +315 -0
  28. data/spec/elasticsearch/model/adapters/active_record/parent_child_spec.rb +75 -0
  29. data/spec/elasticsearch/model/adapters/active_record/serialization_spec.rb +61 -0
  30. data/spec/elasticsearch/model/adapters/active_record_spec.rb +207 -0
  31. data/spec/elasticsearch/model/adapters/default_spec.rb +41 -0
  32. data/spec/elasticsearch/model/adapters/mongoid/basic_spec.rb +267 -0
  33. data/spec/elasticsearch/model/adapters/mongoid/multi_model_spec.rb +66 -0
  34. data/spec/elasticsearch/model/adapters/mongoid_spec.rb +235 -0
  35. data/spec/elasticsearch/model/adapters/multiple_spec.rb +125 -0
  36. data/spec/elasticsearch/model/callbacks_spec.rb +33 -0
  37. data/spec/elasticsearch/model/client_spec.rb +66 -0
  38. data/spec/elasticsearch/model/hash_wrapper_spec.rb +12 -0
  39. data/spec/elasticsearch/model/importing_spec.rb +214 -0
  40. data/spec/elasticsearch/model/indexing_spec.rb +918 -0
  41. data/spec/elasticsearch/model/module_spec.rb +101 -0
  42. data/spec/elasticsearch/model/multimodel_spec.rb +55 -0
  43. data/spec/elasticsearch/model/naming_inheritance_spec.rb +184 -0
  44. data/spec/elasticsearch/model/naming_spec.rb +186 -0
  45. data/spec/elasticsearch/model/proxy_spec.rb +107 -0
  46. data/spec/elasticsearch/model/response/aggregations_spec.rb +66 -0
  47. data/spec/elasticsearch/model/response/base_spec.rb +90 -0
  48. data/spec/elasticsearch/model/response/pagination/kaminari_spec.rb +410 -0
  49. data/spec/elasticsearch/model/response/pagination/will_paginate_spec.rb +262 -0
  50. data/spec/elasticsearch/model/response/records_spec.rb +118 -0
  51. data/spec/elasticsearch/model/response/response_spec.rb +131 -0
  52. data/spec/elasticsearch/model/response/result_spec.rb +122 -0
  53. data/spec/elasticsearch/model/response/results_spec.rb +56 -0
  54. data/spec/elasticsearch/model/searching_search_request_spec.rb +112 -0
  55. data/spec/elasticsearch/model/searching_spec.rb +49 -0
  56. data/spec/elasticsearch/model/serializing_spec.rb +22 -0
  57. data/spec/spec_helper.rb +161 -0
  58. data/spec/support/app.rb +21 -0
  59. data/spec/support/app/answer.rb +33 -0
  60. data/spec/support/app/article.rb +22 -0
  61. data/spec/support/app/article_for_pagination.rb +12 -0
  62. data/spec/support/app/article_with_custom_serialization.rb +13 -0
  63. data/spec/support/app/article_with_dynamic_index_name.rb +15 -0
  64. data/spec/support/app/author.rb +9 -0
  65. data/spec/support/app/authorship.rb +4 -0
  66. data/spec/support/app/category.rb +3 -0
  67. data/spec/support/app/comment.rb +3 -0
  68. data/spec/support/app/episode.rb +11 -0
  69. data/spec/support/app/image.rb +19 -0
  70. data/spec/support/app/import_article.rb +12 -0
  71. data/spec/support/app/mongoid_article.rb +21 -0
  72. data/spec/support/app/namespaced_book.rb +10 -0
  73. data/spec/support/app/parent_and_child_searchable.rb +24 -0
  74. data/spec/support/app/post.rb +14 -0
  75. data/spec/support/app/question.rb +27 -0
  76. data/spec/support/app/searchable.rb +48 -0
  77. data/spec/support/app/series.rb +11 -0
  78. data/spec/support/model.json +1 -0
  79. data/{test → spec}/support/model.yml +0 -0
  80. metadata +129 -86
  81. data/test/integration/active_record_associations_parent_child_test.rb +0 -188
  82. data/test/integration/active_record_associations_test.rb +0 -339
  83. data/test/integration/active_record_basic_test.rb +0 -255
  84. data/test/integration/active_record_custom_serialization_test.rb +0 -67
  85. data/test/integration/active_record_import_test.rb +0 -168
  86. data/test/integration/active_record_namespaced_model_test.rb +0 -56
  87. data/test/integration/active_record_pagination_test.rb +0 -149
  88. data/test/integration/dynamic_index_name_test.rb +0 -52
  89. data/test/integration/mongoid_basic_test.rb +0 -240
  90. data/test/integration/multiple_models_test.rb +0 -176
  91. data/test/support/model.json +0 -1
  92. data/test/test_helper.rb +0 -92
  93. data/test/unit/adapter_active_record_test.rb +0 -157
  94. data/test/unit/adapter_default_test.rb +0 -41
  95. data/test/unit/adapter_mongoid_test.rb +0 -161
  96. data/test/unit/adapter_multiple_test.rb +0 -106
  97. data/test/unit/adapter_test.rb +0 -69
  98. data/test/unit/callbacks_test.rb +0 -31
  99. data/test/unit/client_test.rb +0 -27
  100. data/test/unit/hash_wrapper_test.rb +0 -13
  101. data/test/unit/importing_test.rb +0 -224
  102. data/test/unit/indexing_test.rb +0 -720
  103. data/test/unit/module_test.rb +0 -68
  104. data/test/unit/multimodel_test.rb +0 -38
  105. data/test/unit/naming_inheritance_test.rb +0 -94
  106. data/test/unit/naming_test.rb +0 -103
  107. data/test/unit/proxy_test.rb +0 -98
  108. data/test/unit/response_aggregations_test.rb +0 -46
  109. data/test/unit/response_base_test.rb +0 -40
  110. data/test/unit/response_pagination_kaminari_test.rb +0 -433
  111. data/test/unit/response_pagination_will_paginate_test.rb +0 -398
  112. data/test/unit/response_records_test.rb +0 -91
  113. data/test/unit/response_result_test.rb +0 -90
  114. data/test/unit/response_results_test.rb +0 -34
  115. data/test/unit/response_test.rb +0 -104
  116. data/test/unit/searching_search_request_test.rb +0 -78
  117. data/test/unit/searching_test.rb +0 -41
  118. data/test/unit/serializing_test.rb +0 -17
@@ -0,0 +1,66 @@
1
+ require 'spec_helper'
2
+
3
+ describe Elasticsearch::Model::Client do
4
+
5
+ before(:all) do
6
+ class ::DummyClientModel
7
+ extend Elasticsearch::Model::Client::ClassMethods
8
+ include Elasticsearch::Model::Client::InstanceMethods
9
+ end
10
+ end
11
+
12
+ after(:all) do
13
+ remove_classes(DummyClientModel)
14
+ end
15
+
16
+ context 'when a class includes the client module class methods' do
17
+
18
+ it 'defines the client module class methods on the model' do
19
+ expect(DummyClientModel.client).to be_a(Elasticsearch::Transport::Client)
20
+ end
21
+ end
22
+
23
+ context 'when a class includes the client module instance methods' do
24
+
25
+ it 'defines the client module class methods on the model' do
26
+ expect(DummyClientModel.new.client).to be_a(Elasticsearch::Transport::Client)
27
+ end
28
+ end
29
+
30
+ context 'when the client is set on the class' do
31
+
32
+ around do |example|
33
+ original_client = DummyClientModel.client
34
+ DummyClientModel.client = 'foobar'
35
+ example.run
36
+ DummyClientModel.client = original_client
37
+ end
38
+
39
+ it 'sets the client on the class' do
40
+ expect(DummyClientModel.client).to eq('foobar')
41
+ end
42
+
43
+ it 'sets the client on an instance' do
44
+ expect(DummyClientModel.new.client).to eq('foobar')
45
+ end
46
+ end
47
+
48
+ context 'when the client is set on an instance' do
49
+
50
+ before do
51
+ model_instance.client = 'foo'
52
+ end
53
+
54
+ let(:model_instance) do
55
+ DummyClientModel.new
56
+ end
57
+
58
+ it 'sets the client on an instance' do
59
+ expect(model_instance.client).to eq('foo')
60
+ end
61
+
62
+ it 'does not set the client on the class' do
63
+ expect(DummyClientModel.client).to be_a(Elasticsearch::Transport::Client)
64
+ end
65
+ end
66
+ end
@@ -0,0 +1,12 @@
1
+ require 'spec_helper'
2
+
3
+ describe Elasticsearch::Model::HashWrapper, if: Hashie::VERSION >= '3.5.3' do
4
+
5
+ before do
6
+ expect(Hashie.logger).to receive(:warn).never
7
+ end
8
+
9
+ it 'does not print a warning for re-defined methods' do
10
+ Elasticsearch::Model::HashWrapper.new(:foo => 'bar', :sort => true)
11
+ end
12
+ end
@@ -0,0 +1,214 @@
1
+ require 'spec_helper'
2
+
3
+ describe Elasticsearch::Model::Importing do
4
+
5
+ before(:all) do
6
+ class DummyImportingModel
7
+ end
8
+
9
+ module DummyImportingAdapter
10
+ module ImportingMixin
11
+ def __find_in_batches(options={}, &block)
12
+ yield if block_given?
13
+ end
14
+ def __transform
15
+ lambda {|a|}
16
+ end
17
+ end
18
+
19
+ def importing_mixin
20
+ ImportingMixin
21
+ end; module_function :importing_mixin
22
+ end
23
+ end
24
+
25
+ after(:all) do
26
+ remove_classes(DummyImportingModel, DummyImportingAdapter)
27
+ end
28
+
29
+ before do
30
+ allow(Elasticsearch::Model::Adapter).to receive(:from_class).with(DummyImportingModel).and_return(DummyImportingAdapter)
31
+ DummyImportingModel.__send__ :include, Elasticsearch::Model::Importing
32
+ end
33
+
34
+ context 'when a model includes the Importing module' do
35
+
36
+ it 'provides importing methods' do
37
+ expect(DummyImportingModel.respond_to?(:import)).to be(true)
38
+ expect(DummyImportingModel.respond_to?(:__find_in_batches)).to be(true)
39
+ end
40
+ end
41
+
42
+ describe '#import' do
43
+
44
+ before do
45
+ allow(DummyImportingModel).to receive(:index_name).and_return('foo')
46
+ allow(DummyImportingModel).to receive(:document_type).and_return('foo')
47
+ allow(DummyImportingModel).to receive(:index_exists?).and_return(true)
48
+ allow(DummyImportingModel).to receive(:__batch_to_bulk)
49
+ allow(client).to receive(:bulk).and_return(response)
50
+ end
51
+
52
+ let(:client) do
53
+ double('client')
54
+ end
55
+
56
+ let(:response) do
57
+ { 'items' => [] }
58
+ end
59
+
60
+ context 'when no options are provided' do
61
+
62
+ before do
63
+ expect(DummyImportingModel).to receive(:client).and_return(client)
64
+ allow(DummyImportingModel).to receive(:index_exists?).and_return(true)
65
+ end
66
+
67
+ it 'uses the client to import documents' do
68
+ expect(DummyImportingModel.import).to eq(0)
69
+ end
70
+ end
71
+
72
+ context 'when there is an error' do
73
+
74
+ before do
75
+ expect(DummyImportingModel).to receive(:client).and_return(client)
76
+ allow(DummyImportingModel).to receive(:index_exists?).and_return(true)
77
+ end
78
+
79
+ let(:response) do
80
+ { 'items' => [{ 'index' => { } }, { 'index' => { 'error' => 'FAILED' } }] }
81
+ end
82
+
83
+ it 'returns the number of errors' do
84
+ expect(DummyImportingModel.import).to eq(1)
85
+ end
86
+
87
+ context 'when the method is called with the option to return the errors' do
88
+
89
+ it 'returns the errors' do
90
+ expect(DummyImportingModel.import(return: 'errors')).to eq([{ 'index' => { 'error' => 'FAILED' } }])
91
+ end
92
+ end
93
+
94
+ context 'when the method is called with a block' do
95
+
96
+ it 'yields the response to the block' do
97
+ DummyImportingModel.import do |response|
98
+ expect(response['items'].size).to eq(2)
99
+ end
100
+ end
101
+ end
102
+ end
103
+
104
+ context 'when the index does not exist' do
105
+
106
+ before do
107
+ allow(DummyImportingModel).to receive(:index_exists?).and_return(false)
108
+ end
109
+
110
+ it 'raises an exception' do
111
+ expect {
112
+ DummyImportingModel.import
113
+ }.to raise_exception(ArgumentError)
114
+ end
115
+ end
116
+
117
+ context 'when the method is called with the force option' do
118
+
119
+ before do
120
+ expect(DummyImportingModel).to receive(:create_index!).with(force: true, index: 'foo').and_return(true)
121
+ expect(DummyImportingModel).to receive(:__find_in_batches).with(foo: 'bar').and_return(true)
122
+ end
123
+
124
+ it 'deletes and creates the index' do
125
+ expect(DummyImportingModel.import(force: true, foo: 'bar')).to eq(0)
126
+ end
127
+ end
128
+
129
+ context 'when the method is called with the refresh option' do
130
+
131
+ before do
132
+ expect(DummyImportingModel).to receive(:refresh_index!).with(index: 'foo').and_return(true)
133
+ expect(DummyImportingModel).to receive(:__find_in_batches).with(foo: 'bar').and_return(true)
134
+ end
135
+
136
+ it 'refreshes the index' do
137
+ expect(DummyImportingModel.import(refresh: true, foo: 'bar')).to eq(0)
138
+ end
139
+ end
140
+
141
+ context 'when a different index name is provided' do
142
+
143
+ before do
144
+ expect(DummyImportingModel).to receive(:client).and_return(client)
145
+ expect(client).to receive(:bulk).with(body: nil, index: 'my-new-index', type: 'foo').and_return(response)
146
+ end
147
+
148
+ it 'uses the alternate index name' do
149
+ expect(DummyImportingModel.import(index: 'my-new-index')).to eq(0)
150
+ end
151
+ end
152
+
153
+ context 'when a different document type is provided' do
154
+
155
+ before do
156
+ expect(DummyImportingModel).to receive(:client).and_return(client)
157
+ expect(client).to receive(:bulk).with(body: nil, index: 'foo', type: 'my-new-type').and_return(response)
158
+ end
159
+
160
+ it 'uses the alternate index name' do
161
+ expect(DummyImportingModel.import(type: 'my-new-type')).to eq(0)
162
+ end
163
+ end
164
+
165
+ context 'the transform method' do
166
+
167
+ before do
168
+ expect(DummyImportingModel).to receive(:client).and_return(client)
169
+ expect(DummyImportingModel).to receive(:__transform).and_return(transform)
170
+ expect(DummyImportingModel).to receive(:__batch_to_bulk).with(anything, transform)
171
+ end
172
+
173
+ let(:transform) do
174
+ lambda {|a|}
175
+ end
176
+
177
+ it 'applies the transform method to the results' do
178
+ expect(DummyImportingModel.import).to eq(0)
179
+ end
180
+ end
181
+
182
+ context 'when a transform is provided as an option' do
183
+
184
+ context 'when the transform option is not a lambda' do
185
+
186
+ let(:transform) do
187
+ 'not_callable'
188
+ end
189
+
190
+ it 'raises an error' do
191
+ expect {
192
+ DummyImportingModel.import(transform: transform)
193
+ }.to raise_exception(ArgumentError)
194
+ end
195
+ end
196
+
197
+ context 'when the transform option is a lambda' do
198
+
199
+ before do
200
+ expect(DummyImportingModel).to receive(:client).and_return(client)
201
+ expect(DummyImportingModel).to receive(:__batch_to_bulk).with(anything, transform)
202
+ end
203
+
204
+ let(:transform) do
205
+ lambda {|a|}
206
+ end
207
+
208
+ it 'applies the transform lambda to the results' do
209
+ expect(DummyImportingModel.import(transform: transform)).to eq(0)
210
+ end
211
+ end
212
+ end
213
+ end
214
+ end
@@ -0,0 +1,918 @@
1
+ require 'spec_helper'
2
+
3
+ describe Elasticsearch::Model::Indexing do
4
+
5
+ before(:all) do
6
+ class ::DummyIndexingModel
7
+ extend ActiveModel::Naming
8
+ extend Elasticsearch::Model::Naming::ClassMethods
9
+ extend Elasticsearch::Model::Indexing::ClassMethods
10
+
11
+ def self.foo
12
+ 'bar'
13
+ end
14
+ end
15
+
16
+ class NotFound < Exception; end
17
+ end
18
+
19
+ after(:all) do
20
+ remove_classes(DummyIndexingModel, NotFound)
21
+ end
22
+
23
+ describe 'the Settings class' do
24
+
25
+ it 'should be convertible to a hash' do
26
+ expect(Elasticsearch::Model::Indexing::Settings.new(foo: 'bar').to_hash).to eq(foo: 'bar')
27
+ end
28
+
29
+ it 'should be convertible to json' do
30
+ expect(Elasticsearch::Model::Indexing::Settings.new(foo: 'bar').as_json).to eq(foo: 'bar')
31
+ end
32
+ end
33
+
34
+ describe '#settings' do
35
+
36
+ it 'returns an instance of the Settings class' do
37
+ expect(DummyIndexingModel.settings).to be_a(Elasticsearch::Model::Indexing::Settings)
38
+ end
39
+
40
+ context 'when the settings are updated' do
41
+
42
+ before do
43
+ DummyIndexingModel.settings(foo: 'boo')
44
+ DummyIndexingModel.settings(bar: 'bam')
45
+ end
46
+
47
+ it 'updates the settings on the class' do
48
+ expect(DummyIndexingModel.settings.to_hash).to eq(foo: 'boo', bar: 'bam')
49
+ end
50
+ end
51
+
52
+ context 'when the settings are updated with a yml file' do
53
+
54
+ before do
55
+ DummyIndexingModel.settings File.open('spec/support/model.yml')
56
+ DummyIndexingModel.settings bar: 'bam'
57
+ end
58
+
59
+ it 'updates the settings on the class' do
60
+ expect(DummyIndexingModel.settings.to_hash).to eq(foo: 'boo', bar: 'bam', 'baz' => 'qux')
61
+ end
62
+ end
63
+
64
+ context 'when the settings are updated with a json file' do
65
+
66
+ before do
67
+ DummyIndexingModel.settings File.open('spec/support/model.json')
68
+ DummyIndexingModel.settings bar: 'bam'
69
+ end
70
+
71
+ it 'updates the settings on the class' do
72
+ expect(DummyIndexingModel.settings.to_hash).to eq(foo: 'boo', bar: 'bam', 'baz' => 'qux', 'laz' => 'qux')
73
+ end
74
+ end
75
+ end
76
+
77
+ describe '#mappings' do
78
+
79
+ let(:expected_mapping_hash) do
80
+ { :mytype => { foo: 'bar', :properties => {} } }
81
+ end
82
+
83
+ it 'returns an instance of the Mappings class' do
84
+ expect(DummyIndexingModel.mappings).to be_a(Elasticsearch::Model::Indexing::Mappings)
85
+ end
86
+
87
+ it 'raises an exception when there is no type passed to the #initialize method' do
88
+ expect {
89
+ Elasticsearch::Model::Indexing::Mappings.new
90
+ }.to raise_exception(ArgumentError)
91
+ end
92
+
93
+ it 'should be convertible to a hash' do
94
+ expect(Elasticsearch::Model::Indexing::Mappings.new(:mytype, { foo: 'bar' }).to_hash).to eq(expected_mapping_hash)
95
+ end
96
+
97
+ it 'should be convertible to json' do
98
+ expect(Elasticsearch::Model::Indexing::Mappings.new(:mytype, { foo: 'bar' }).as_json).to eq(expected_mapping_hash)
99
+ end
100
+
101
+ context 'when specific mappings are defined' do
102
+
103
+ let(:mappings) do
104
+ Elasticsearch::Model::Indexing::Mappings.new(:mytype)
105
+ end
106
+
107
+ before do
108
+ mappings.indexes :foo, { type: 'boolean', include_in_all: false }
109
+ mappings.indexes :bar
110
+ end
111
+
112
+ it 'creates the correct mapping definition' do
113
+ expect(mappings.to_hash[:mytype][:properties][:foo][:type]).to eq('boolean')
114
+ end
115
+
116
+ it 'uses text as the default type' do
117
+ expect(mappings.to_hash[:mytype][:properties][:bar][:type]).to eq('text')
118
+ end
119
+
120
+ context 'when mappings are defined for multiple fields' do
121
+
122
+ before do
123
+ mappings.indexes :my_field, type: 'text' do
124
+ indexes :raw, type: 'keyword'
125
+ end
126
+ end
127
+
128
+ it 'defines the mapping for all the fields' do
129
+ expect(mappings.to_hash[:mytype][:properties][:my_field][:type]).to eq('text')
130
+ expect(mappings.to_hash[:mytype][:properties][:my_field][:fields][:raw][:type]).to eq('keyword')
131
+ expect(mappings.to_hash[:mytype][:properties][:my_field][:fields][:raw][:properties]).to be_nil
132
+ end
133
+ end
134
+
135
+ context 'when embedded properties are defined' do
136
+
137
+ before do
138
+ mappings.indexes :foo do
139
+ indexes :bar
140
+ end
141
+
142
+ mappings.indexes :foo_object, type: 'object' do
143
+ indexes :bar
144
+ end
145
+
146
+ mappings.indexes :foo_nested, type: 'nested' do
147
+ indexes :bar
148
+ end
149
+
150
+ mappings.indexes :foo_nested_as_symbol, type: :nested do
151
+ indexes :bar
152
+ end
153
+ end
154
+
155
+ it 'defines mappings for the embedded properties' do
156
+ expect(mappings.to_hash[:mytype][:properties][:foo][:type]).to eq('object')
157
+ expect(mappings.to_hash[:mytype][:properties][:foo][:properties][:bar][:type]).to eq('text')
158
+ expect(mappings.to_hash[:mytype][:properties][:foo][:fields]).to be_nil
159
+
160
+ expect(mappings.to_hash[:mytype][:properties][:foo_object][:type]).to eq('object')
161
+ expect(mappings.to_hash[:mytype][:properties][:foo_object][:properties][:bar][:type]).to eq('text')
162
+ expect(mappings.to_hash[:mytype][:properties][:foo_object][:fields]).to be_nil
163
+
164
+ expect(mappings.to_hash[:mytype][:properties][:foo_nested][:type]).to eq('nested')
165
+ expect(mappings.to_hash[:mytype][:properties][:foo_nested][:properties][:bar][:type]).to eq('text')
166
+ expect(mappings.to_hash[:mytype][:properties][:foo_nested][:fields]).to be_nil
167
+
168
+ expect(mappings.to_hash[:mytype][:properties][:foo_nested_as_symbol][:type]).to eq(:nested)
169
+ expect(mappings.to_hash[:mytype][:properties][:foo_nested_as_symbol][:properties]).not_to be_nil
170
+ expect(mappings.to_hash[:mytype][:properties][:foo_nested_as_symbol][:fields]).to be_nil
171
+ end
172
+ end
173
+ end
174
+
175
+ context 'when the method is called on a class' do
176
+
177
+ before do
178
+ DummyIndexingModel.mappings(foo: 'boo')
179
+ DummyIndexingModel.mappings(bar: 'bam')
180
+ end
181
+
182
+ let(:expected_mappings_hash) do
183
+ { _doc: { foo: "boo", bar: "bam", properties: {} } }
184
+ end
185
+
186
+ it 'sets the mappings' do
187
+ expect(DummyIndexingModel.mappings.to_hash).to eq(expected_mappings_hash)
188
+ end
189
+
190
+ context 'when the method is called with a block' do
191
+
192
+ before do
193
+ DummyIndexingModel.mapping do
194
+ indexes :foo, type: 'boolean'
195
+ end
196
+ end
197
+
198
+ it 'sets the mappings' do
199
+ expect(DummyIndexingModel.mapping.to_hash[:_doc][:properties][:foo][:type]).to eq('boolean')
200
+ end
201
+ end
202
+ end
203
+ end
204
+
205
+ describe 'instance methods' do
206
+
207
+ before(:all) do
208
+ class ::DummyIndexingModelWithCallbacks
209
+ extend Elasticsearch::Model::Indexing::ClassMethods
210
+ include Elasticsearch::Model::Indexing::InstanceMethods
211
+
212
+ def self.before_save(&block)
213
+ (@callbacks ||= {})[block.hash] = block
214
+ end
215
+
216
+ def changes_to_save
217
+ {:foo => ['One', 'Two']}
218
+ end
219
+ end
220
+
221
+ class ::DummyIndexingModelWithNoChanges
222
+ extend Elasticsearch::Model::Indexing::ClassMethods
223
+ include Elasticsearch::Model::Indexing::InstanceMethods
224
+
225
+ def self.before_save(&block)
226
+ (@callbacks ||= {})[block.hash] = block
227
+ end
228
+
229
+ def changes_to_save
230
+ {}
231
+ end
232
+ end
233
+
234
+ class ::DummyIndexingModelWithCallbacksAndCustomAsIndexedJson
235
+ extend Elasticsearch::Model::Indexing::ClassMethods
236
+ include Elasticsearch::Model::Indexing::InstanceMethods
237
+
238
+ def self.before_save(&block)
239
+ (@callbacks ||= {})[block.hash] = block
240
+ end
241
+
242
+ def changes_to_save
243
+ {:foo => ['A', 'B'], :bar => ['C', 'D']}
244
+ end
245
+
246
+ def as_indexed_json(options={})
247
+ { :foo => 'B' }
248
+ end
249
+ end
250
+
251
+ class ::DummyIndexingModelWithOldDirty
252
+ extend Elasticsearch::Model::Indexing::ClassMethods
253
+ include Elasticsearch::Model::Indexing::InstanceMethods
254
+
255
+ def self.before_save(&block)
256
+ (@callbacks ||= {})[block.hash] = block
257
+ end
258
+
259
+ def changes
260
+ {:foo => ['One', 'Two']}
261
+ end
262
+ end
263
+ end
264
+
265
+ after(:all) do
266
+ Object.send(:remove_const, :DummyIndexingModelWithCallbacks) if defined?(DummyIndexingModelWithCallbacks)
267
+ Object.send(:remove_const, :DummyIndexingModelWithNoChanges) if defined?(DummyIndexingModelWithNoChanges)
268
+ Object.send(:remove_const, :DummyIndexingModelWithCallbacksAndCustomAsIndexedJson) if defined?(DummyIndexingModelWithCallbacksAndCustomAsIndexedJson)
269
+ Object.send(:remove_const, :DummyIndexingModelWithOldDirty) if defined?(DummyIndexingModelWithOldDirty)
270
+ end
271
+
272
+ context 'when the module is included' do
273
+
274
+ context 'when the model uses the old ActiveModel::Dirty' do
275
+
276
+ before do
277
+ DummyIndexingModelWithOldDirty.__send__ :include, Elasticsearch::Model::Indexing::InstanceMethods
278
+ end
279
+
280
+ it 'registers callbacks' do
281
+ expect(DummyIndexingModelWithOldDirty.instance_variable_get(:@callbacks)).not_to be_empty
282
+ end
283
+
284
+ let(:instance) do
285
+ DummyIndexingModelWithOldDirty.new
286
+ end
287
+
288
+ it 'sets the @__changed_model_attributes variable before the callback' do
289
+ DummyIndexingModelWithOldDirty.instance_variable_get(:@callbacks).each do |n, callback|
290
+ instance.instance_eval(&callback)
291
+ expect(instance.instance_variable_get(:@__changed_model_attributes)).to eq(foo: 'Two')
292
+ end
293
+ end
294
+ end
295
+
296
+ context 'when the model users the current ActiveModel::Dirty' do
297
+
298
+ before do
299
+ DummyIndexingModelWithCallbacks.__send__ :include, Elasticsearch::Model::Indexing::InstanceMethods
300
+ end
301
+
302
+ it 'registers callbacks' do
303
+ expect(DummyIndexingModelWithCallbacks.instance_variable_get(:@callbacks)).not_to be_empty
304
+ end
305
+
306
+ let(:instance) do
307
+ DummyIndexingModelWithCallbacks.new
308
+ end
309
+
310
+ it 'sets the @__changed_model_attributes variable before the callback' do
311
+ DummyIndexingModelWithCallbacks.instance_variable_get(:@callbacks).each do |n, callback|
312
+ instance.instance_eval(&callback)
313
+ expect(instance.instance_variable_get(:@__changed_model_attributes)).to eq(foo: 'Two')
314
+ end
315
+ end
316
+ end
317
+ end
318
+
319
+ describe '#index_document' do
320
+
321
+ before do
322
+ expect(instance).to receive(:client).and_return(client)
323
+ expect(instance).to receive(:as_indexed_json).and_return('JSON')
324
+ expect(instance).to receive(:index_name).and_return('foo')
325
+ expect(instance).to receive(:document_type).and_return('bar')
326
+ expect(instance).to receive(:id).and_return('1')
327
+ end
328
+
329
+ let(:client) do
330
+ double('client')
331
+ end
332
+
333
+ let(:instance) do
334
+ DummyIndexingModelWithCallbacks.new
335
+ end
336
+
337
+ context 'when no options are passed to the method' do
338
+
339
+ before do
340
+ expect(client).to receive(:index).with(index: 'foo', type: 'bar', id: '1', body: 'JSON').and_return(true)
341
+ end
342
+
343
+ it 'provides the method on an instance' do
344
+ expect(instance.index_document).to be(true)
345
+ end
346
+ end
347
+
348
+ context 'when extra options are passed to the method' do
349
+
350
+ before do
351
+ expect(client).to receive(:index).with(index: 'foo', type: 'bar', id: '1', body: 'JSON', parent: 'A').and_return(true)
352
+ end
353
+
354
+ it 'passes the extra options to the method call on the client' do
355
+ expect(instance.index_document(parent: 'A')).to be(true)
356
+ end
357
+ end
358
+ end
359
+
360
+ describe '#delete_document' do
361
+
362
+ before do
363
+ expect(instance).to receive(:client).and_return(client)
364
+ expect(instance).to receive(:index_name).and_return('foo')
365
+ expect(instance).to receive(:document_type).and_return('bar')
366
+ expect(instance).to receive(:id).and_return('1')
367
+ end
368
+
369
+ let(:client) do
370
+ double('client')
371
+ end
372
+
373
+ let(:instance) do
374
+ DummyIndexingModelWithCallbacks.new
375
+ end
376
+
377
+ context 'when no options are passed to the method' do
378
+
379
+ before do
380
+ expect(client).to receive(:delete).with(index: 'foo', type: 'bar', id: '1').and_return(true)
381
+ end
382
+
383
+ it 'provides the method on an instance' do
384
+ expect(instance.delete_document).to be(true)
385
+ end
386
+ end
387
+
388
+ context 'when extra options are passed to the method' do
389
+
390
+ before do
391
+ expect(client).to receive(:delete).with(index: 'foo', type: 'bar', id: '1', parent: 'A').and_return(true)
392
+ end
393
+
394
+ it 'passes the extra options to the method call on the client' do
395
+ expect(instance.delete_document(parent: 'A')).to be(true)
396
+ end
397
+ end
398
+ end
399
+
400
+ describe '#update_document' do
401
+
402
+ let(:client) do
403
+ double('client')
404
+ end
405
+
406
+ let(:instance) do
407
+ DummyIndexingModelWithCallbacks.new
408
+ end
409
+
410
+ context 'when no changes are present' do
411
+
412
+ before do
413
+ expect(instance).to receive(:index_document).and_return(true)
414
+ expect(client).to receive(:update).never
415
+ instance.instance_variable_set(:@__changed_model_attributes, nil)
416
+ end
417
+
418
+ it 'updates the document' do
419
+ expect(instance.update_document).to be(true)
420
+ end
421
+ end
422
+
423
+ context 'when changes are present' do
424
+
425
+ before do
426
+ allow(instance).to receive(:client).and_return(client)
427
+ allow(instance).to receive(:index_name).and_return('foo')
428
+ allow(instance).to receive(:document_type).and_return('bar')
429
+ allow(instance).to receive(:id).and_return('1')
430
+ end
431
+
432
+ context 'when the changes are included in the as_indexed_json representation' do
433
+
434
+ before do
435
+ instance.instance_variable_set(:@__changed_model_attributes, { foo: 'bar' })
436
+ expect(client).to receive(:update).with(index: 'foo', type: 'bar', id: '1', body: { doc: { foo: 'bar' } }).and_return(true)
437
+ end
438
+
439
+ it 'updates the document' do
440
+ expect(instance.update_document).to be(true)
441
+ end
442
+ end
443
+
444
+ context 'when the changes are not all included in the as_indexed_json representation' do
445
+
446
+ let(:instance) do
447
+ DummyIndexingModelWithCallbacksAndCustomAsIndexedJson.new
448
+ end
449
+
450
+ before do
451
+ instance.instance_variable_set(:@__changed_model_attributes, {'foo' => 'B', 'bar' => 'D' })
452
+ expect(client).to receive(:update).with(index: 'foo', type: 'bar', id: '1', body: { doc: { foo: 'B' } }).and_return(true)
453
+ end
454
+
455
+ it 'updates the document' do
456
+ expect(instance.update_document).to be(true)
457
+ end
458
+ end
459
+
460
+ context 'when none of the changes are included in the as_indexed_json representation' do
461
+
462
+ let(:instance) do
463
+ DummyIndexingModelWithCallbacksAndCustomAsIndexedJson.new
464
+ end
465
+
466
+ before do
467
+ instance.instance_variable_set(:@__changed_model_attributes, {'bar' => 'D' })
468
+ end
469
+
470
+ it 'does not update the document' do
471
+ expect(instance.update_document).to_not be(true)
472
+ end
473
+ end
474
+
475
+ context 'when there are partial updates' do
476
+
477
+ let(:instance) do
478
+ DummyIndexingModelWithCallbacksAndCustomAsIndexedJson.new
479
+ end
480
+
481
+ before do
482
+ instance.instance_variable_set(:@__changed_model_attributes, { 'foo' => { 'bar' => 'BAR'} })
483
+ expect(instance).to receive(:as_indexed_json).and_return('foo' => 'BAR')
484
+ expect(client).to receive(:update).with(index: 'foo', type: 'bar', id: '1', body: { doc: { 'foo' => 'BAR' } }).and_return(true)
485
+ end
486
+
487
+ it 'updates the document' do
488
+ expect(instance.update_document).to be(true)
489
+ end
490
+ end
491
+ end
492
+ end
493
+
494
+ describe '#update_document_attributes' do
495
+
496
+ let(:client) do
497
+ double('client')
498
+ end
499
+
500
+ let(:instance) do
501
+ DummyIndexingModelWithCallbacks.new
502
+ end
503
+
504
+ context 'when changes are present' do
505
+
506
+ before do
507
+ expect(instance).to receive(:client).and_return(client)
508
+ expect(instance).to receive(:index_name).and_return('foo')
509
+ expect(instance).to receive(:document_type).and_return('bar')
510
+ expect(instance).to receive(:id).and_return('1')
511
+ instance.instance_variable_set(:@__changed_model_attributes, { author: 'john' })
512
+ end
513
+
514
+ context 'when no options are specified' do
515
+
516
+ before do
517
+ expect(client).to receive(:update).with(index: 'foo', type: 'bar', id: '1', body: { doc: { title: 'green' } }).and_return(true)
518
+ end
519
+
520
+ it 'updates the document' do
521
+ expect(instance.update_document_attributes(title: 'green')).to be(true)
522
+ end
523
+ end
524
+
525
+ context 'when extra options are provided' do
526
+
527
+ before do
528
+ expect(client).to receive(:update).with(index: 'foo', type: 'bar', id: '1', body: { doc: { title: 'green' } }, refresh: true).and_return(true)
529
+ end
530
+
531
+ it 'updates the document' do
532
+ expect(instance.update_document_attributes({ title: 'green' }, refresh: true)).to be(true)
533
+ end
534
+ end
535
+ end
536
+ end
537
+ end
538
+
539
+ describe '#index_exists?' do
540
+
541
+ before do
542
+ expect(DummyIndexingModel).to receive(:client).and_return(client)
543
+ end
544
+
545
+ context 'when the index exists' do
546
+
547
+ let(:client) do
548
+ double('client', indices: double('indices', exists: true))
549
+ end
550
+
551
+ it 'returns true' do
552
+ expect(DummyIndexingModel.index_exists?).to be(true)
553
+ end
554
+ end
555
+
556
+ context 'when the index does not exists' do
557
+
558
+ let(:client) do
559
+ double('client', indices: double('indices', exists: false))
560
+ end
561
+
562
+ it 'returns false' do
563
+ expect(DummyIndexingModel.index_exists?).to be(false)
564
+ end
565
+ end
566
+
567
+ context 'when the index API raises an error' do
568
+
569
+ let(:client) do
570
+ double('client').tap do |cl|
571
+ expect(cl).to receive(:indices).and_raise(StandardError)
572
+ end
573
+ end
574
+
575
+ it 'returns false' do
576
+ expect(DummyIndexingModel.index_exists?).to be(false)
577
+ end
578
+ end
579
+
580
+ context 'when the indices.exists API raises an error' do
581
+
582
+ let(:client) do
583
+ double('client', indices: indices)
584
+ end
585
+
586
+ let(:indices) do
587
+ double('indices').tap do |ind|
588
+ expect(ind).to receive(:exists).and_raise(StandardError)
589
+ end
590
+ end
591
+
592
+ it 'returns false' do
593
+ expect(DummyIndexingModel.index_exists?).to be(false)
594
+ end
595
+ end
596
+ end
597
+
598
+ describe '#delete_index!' do
599
+
600
+ before(:all) do
601
+ class ::DummyIndexingModelForRecreate
602
+ extend ActiveModel::Naming
603
+ extend Elasticsearch::Model::Naming::ClassMethods
604
+ extend Elasticsearch::Model::Indexing::ClassMethods
605
+ end
606
+ end
607
+
608
+ after(:all) do
609
+ Object.send(:remove_const, :DummyIndexingModelForRecreate) if defined?(DummyIndexingModelForRecreate)
610
+ end
611
+
612
+ context 'when the index is not found' do
613
+
614
+ let(:client) do
615
+ double('client', indices: indices, transport: double('transport', { logger: nil }))
616
+ end
617
+
618
+ let(:indices) do
619
+ double('indices').tap do |ind|
620
+ expect(ind).to receive(:delete).and_raise(NotFound)
621
+ end
622
+ end
623
+
624
+ before do
625
+ expect(DummyIndexingModelForRecreate).to receive(:client).at_most(3).times.and_return(client)
626
+ end
627
+
628
+ context 'when the force option is true' do
629
+
630
+ it 'deletes the index without raising an exception' do
631
+ expect(DummyIndexingModelForRecreate.delete_index!(force: true)).to be_nil
632
+ end
633
+
634
+ context 'when the client has a logger' do
635
+
636
+ let(:logger) do
637
+ Logger.new(STDOUT).tap { |l| l.level = Logger::DEBUG }
638
+ end
639
+
640
+ let(:client) do
641
+ double('client', indices: indices, transport: double('transport', { logger: logger }))
642
+ end
643
+
644
+ it 'deletes the index without raising an exception' do
645
+ expect(DummyIndexingModelForRecreate.delete_index!(force: true)).to be_nil
646
+ end
647
+
648
+ it 'logs the message that the index is not found' do
649
+ expect(logger).to receive(:debug)
650
+ expect(DummyIndexingModelForRecreate.delete_index!(force: true)).to be_nil
651
+ end
652
+ end
653
+ end
654
+
655
+ context 'when the force option is not provided' do
656
+
657
+ it 'raises an exception' do
658
+ expect {
659
+ DummyIndexingModelForRecreate.delete_index!
660
+ }.to raise_exception(NotFound)
661
+ end
662
+ end
663
+
664
+ context 'when the exception is not NotFound' do
665
+
666
+ let(:indices) do
667
+ double('indices').tap do |ind|
668
+ expect(ind).to receive(:delete).and_raise(Exception)
669
+ end
670
+ end
671
+
672
+ it 'raises an exception' do
673
+ expect {
674
+ DummyIndexingModelForRecreate.delete_index!
675
+ }.to raise_exception(Exception)
676
+ end
677
+ end
678
+ end
679
+
680
+ context 'when an index name is provided in the options' do
681
+
682
+ before do
683
+ expect(DummyIndexingModelForRecreate).to receive(:client).and_return(client)
684
+ expect(indices).to receive(:delete).with(index: 'custom-foo')
685
+ end
686
+
687
+ let(:client) do
688
+ double('client', indices: indices)
689
+ end
690
+
691
+ let(:indices) do
692
+ double('indices', delete: true)
693
+ end
694
+
695
+ it 'uses the index name' do
696
+ expect(DummyIndexingModelForRecreate.delete_index!(index: 'custom-foo'))
697
+ end
698
+ end
699
+ end
700
+
701
+ describe '#create_index' do
702
+
703
+ before(:all) do
704
+ class ::DummyIndexingModelForCreate
705
+ extend ActiveModel::Naming
706
+ extend Elasticsearch::Model::Naming::ClassMethods
707
+ extend Elasticsearch::Model::Indexing::ClassMethods
708
+
709
+ index_name 'foo'
710
+
711
+ settings index: { number_of_shards: 1 } do
712
+ mappings do
713
+ indexes :foo, analyzer: 'keyword'
714
+ end
715
+ end
716
+ end
717
+ end
718
+
719
+ after(:all) do
720
+ Object.send(:remove_const, :DummyIndexingModelForCreate) if defined?(DummyIndexingModelForCreate)
721
+ end
722
+
723
+ let(:client) do
724
+ double('client', indices: indices)
725
+ end
726
+
727
+ let(:indices) do
728
+ double('indices')
729
+ end
730
+
731
+ context 'when the index does not exist' do
732
+
733
+ before do
734
+ expect(DummyIndexingModelForCreate).to receive(:client).and_return(client)
735
+ expect(DummyIndexingModelForCreate).to receive(:index_exists?).and_return(false)
736
+ end
737
+
738
+ context 'when options are not provided' do
739
+
740
+ let(:expected_body) do
741
+ { mappings: { _doc: { properties: { foo: { analyzer: 'keyword',
742
+ type: 'text' } } } },
743
+ settings: { index: { number_of_shards: 1 } } }
744
+ end
745
+
746
+ before do
747
+ expect(indices).to receive(:create).with(index: 'foo', body: expected_body).and_return(true)
748
+ end
749
+
750
+ it 'creates the index' do
751
+ expect(DummyIndexingModelForCreate.create_index!).to be(true)
752
+ end
753
+ end
754
+
755
+ context 'when options are provided' do
756
+
757
+ let(:expected_body) do
758
+ { mappings: { foobar: { properties: { foo: { analyzer: 'bar' } } } },
759
+ settings: { index: { number_of_shards: 3 } } }
760
+ end
761
+
762
+ before do
763
+ expect(indices).to receive(:create).with(index: 'foobar', body: expected_body).and_return(true)
764
+ end
765
+
766
+ it 'creates the index' do
767
+ expect(DummyIndexingModelForCreate.create_index! \
768
+ index: 'foobar',
769
+ settings: { index: { number_of_shards: 3 } },
770
+ mappings: { foobar: { properties: { foo: { analyzer: 'bar' } } } }
771
+ ).to be(true)
772
+ end
773
+ end
774
+ end
775
+
776
+ context 'when the index exists' do
777
+
778
+ before do
779
+ expect(DummyIndexingModelForCreate).to receive(:index_exists?).and_return(true)
780
+ expect(indices).to receive(:create).never
781
+ end
782
+
783
+ it 'does not create the index' do
784
+ expect(DummyIndexingModelForCreate.create_index!).to be_nil
785
+ end
786
+ end
787
+
788
+ context 'when creating the index raises an exception' do
789
+
790
+ before do
791
+ expect(DummyIndexingModelForCreate).to receive(:client).and_return(client)
792
+ expect(DummyIndexingModelForCreate).to receive(:index_exists?).and_return(false)
793
+ expect(DummyIndexingModelForCreate).to receive(:delete_index!).and_return(true)
794
+ expect(indices).to receive(:create).and_raise(Exception)
795
+ end
796
+
797
+ it 'raises the exception' do
798
+ expect {
799
+ DummyIndexingModelForCreate.create_index!(force: true)
800
+ }.to raise_exception(Exception)
801
+ end
802
+ end
803
+
804
+ context 'when an index name is provided in the options' do
805
+
806
+ before do
807
+ expect(DummyIndexingModelForCreate).to receive(:client).and_return(client).twice
808
+ expect(indices).to receive(:exists).and_return(false)
809
+ expect(indices).to receive(:create).with(index: 'custom-foo', body: expected_body)
810
+ end
811
+
812
+ let(:expected_body) do
813
+ { mappings: { _doc: { properties: { foo: { analyzer: 'keyword',
814
+ type: 'text' } } } },
815
+ settings: { index: { number_of_shards: 1 } } }
816
+ end
817
+
818
+ it 'uses the index name' do
819
+ expect(DummyIndexingModelForCreate.create_index!(index: 'custom-foo'))
820
+ end
821
+ end
822
+
823
+ context 'when the logging level is debug'
824
+ end
825
+
826
+ describe '#refresh_index!' do
827
+
828
+ before(:all) do
829
+ class ::DummyIndexingModelForRefresh
830
+ extend ActiveModel::Naming
831
+ extend Elasticsearch::Model::Naming::ClassMethods
832
+ extend Elasticsearch::Model::Indexing::ClassMethods
833
+
834
+ index_name 'foo'
835
+
836
+ settings index: { number_of_shards: 1 } do
837
+ mappings do
838
+ indexes :foo, analyzer: 'keyword'
839
+ end
840
+ end
841
+ end
842
+ end
843
+
844
+ after(:all) do
845
+ Object.send(:remove_const, :DummyIndexingModelForRefresh) if defined?(DummyIndexingModelForRefresh)
846
+ end
847
+
848
+ let(:client) do
849
+ double('client', indices: indices, transport: double('transport', { logger: nil }))
850
+ end
851
+
852
+ let(:indices) do
853
+ double('indices')
854
+ end
855
+
856
+ before do
857
+ expect(DummyIndexingModelForRefresh).to receive(:client).at_most(3).times.and_return(client)
858
+ end
859
+
860
+ context 'when the force option is true' do
861
+
862
+ context 'when the operation raises a NotFound exception' do
863
+
864
+ before do
865
+ expect(indices).to receive(:refresh).and_raise(NotFound)
866
+ end
867
+
868
+ it 'does not raise an exception' do
869
+ expect(DummyIndexingModelForRefresh.refresh_index!(force: true)).to be_nil
870
+ end
871
+
872
+ context 'when the client has a logger' do
873
+
874
+ let(:logger) do
875
+ Logger.new(STDOUT).tap { |l| l.level = Logger::DEBUG }
876
+ end
877
+
878
+ let(:client) do
879
+ double('client', indices: indices, transport: double('transport', { logger: logger }))
880
+ end
881
+
882
+ it 'does not raise an exception' do
883
+ expect(DummyIndexingModelForRefresh.refresh_index!(force: true)).to be_nil
884
+ end
885
+
886
+ it 'logs the message that the index is not found' do
887
+ expect(logger).to receive(:debug)
888
+ expect(DummyIndexingModelForRefresh.refresh_index!(force: true)).to be_nil
889
+ end
890
+ end
891
+ end
892
+
893
+ context 'when the operation raises another type of exception' do
894
+
895
+ before do
896
+ expect(indices).to receive(:refresh).and_raise(Exception)
897
+ end
898
+
899
+ it 'does not raise an exception' do
900
+ expect {
901
+ DummyIndexingModelForRefresh.refresh_index!(force: true)
902
+ }.to raise_exception(Exception)
903
+ end
904
+ end
905
+ end
906
+
907
+ context 'when an index name is provided in the options' do
908
+
909
+ before do
910
+ expect(indices).to receive(:refresh).with(index: 'custom-foo')
911
+ end
912
+
913
+ it 'uses the index name' do
914
+ expect(DummyIndexingModelForRefresh.refresh_index!(index: 'custom-foo'))
915
+ end
916
+ end
917
+ end
918
+ end