elasticsearch-model 6.0.0 → 6.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (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