elasticsearch-extensions 0.0.3 → 0.0.33

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.
@@ -0,0 +1,124 @@
1
+ # Licensed to Elasticsearch B.V. under one or more contributor
2
+ # license agreements. See the NOTICE file distributed with
3
+ # this work for additional information regarding copyright
4
+ # ownership. Elasticsearch B.V. licenses this file to you under
5
+ # the Apache License, Version 2.0 (the "License"); you may
6
+ # not use this file except in compliance with the License.
7
+ # You may obtain a copy of the License at
8
+ #
9
+ # http://www.apache.org/licenses/LICENSE-2.0
10
+ #
11
+ # Unless required by applicable law or agreed to in writing,
12
+ # software distributed under the License is distributed on an
13
+ # "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
14
+ # KIND, either express or implied. See the License for the
15
+ # specific language governing permissions and limitations
16
+ # under the License.
17
+
18
+ require 'ruby-prof'
19
+ require 'benchmark'
20
+ require 'ansi'
21
+
22
+ module Elasticsearch
23
+ module Extensions
24
+ module Test
25
+
26
+ # Allows to define and execute profiling tests within [Shoulda](https://github.com/thoughtbot/shoulda) contexts.
27
+ #
28
+ # Measures operations and reports statistics, including code profile.
29
+ #
30
+ # Uses the "benchmark" standard library and the "ruby-prof" gem.
31
+ #
32
+ # File: profiling_test.rb
33
+ #
34
+ # require 'test/unit'
35
+ # require 'shoulda/context'
36
+ # require 'elasticsearch/extensions/test/profiling'
37
+ #
38
+ # class ProfilingTest < Test::Unit::TestCase
39
+ # extend Elasticsearch::Extensions::Test::Profiling
40
+ #
41
+ # context "Mathematics" do
42
+ # measure "divide numbers", count: 10_000 do
43
+ # assert_nothing_raised { 1/2 }
44
+ # end
45
+ # end
46
+ #
47
+ # end
48
+ #
49
+ # $ QUIET=y ruby profiling_test.rb
50
+ #
51
+ # ...
52
+ # ProfilingTest
53
+ #
54
+ # -------------------------------------------------------------------------------
55
+ # Context: Mathematics should divide numbers (10000x)
56
+ # mean: 0.03ms | avg: 0.03ms | max: 0.14ms
57
+ # -------------------------------------------------------------------------------
58
+ # PASS (0:00:00.490) test: Mathematics should divide numbers (10000x).
59
+ # ...
60
+ #
61
+ module Profiling
62
+
63
+ # Profiles the passed block of code.
64
+ #
65
+ # measure "divide numbers", count: 10_000 do
66
+ # assert_nothing_raised { 1/2 }
67
+ # end
68
+ #
69
+ # @todo Try to make progress bar not to interfere with tests
70
+ #
71
+ def measure(name, options={}, &block)
72
+ ___ = '-'*ANSI::Terminal.terminal_width
73
+ test_name = name
74
+ suite_name = self.name.split('::').last
75
+ context_name = self.context(nil) {}.first.parent.name
76
+ count = Integer(ENV['COUNT'] || options[:count] || 1_000)
77
+ ticks = []
78
+ progress = ANSI::Progressbar.new(suite_name, count) unless ENV['QUIET'] || options[:quiet]
79
+
80
+ should "#{test_name} (#{count}x)" do
81
+ RubyProf.start
82
+
83
+ begin
84
+ count.times do
85
+ ticks << Benchmark.realtime { self.instance_eval(&block) }
86
+
87
+ if progress
88
+ RubyProf.pause
89
+ progress.inc
90
+ RubyProf.resume
91
+ end
92
+ end
93
+ ensure
94
+ result = RubyProf.stop
95
+ progress.finish if progress
96
+ end
97
+
98
+ total = result.threads.reduce(0) { |t,info| t += info.total_time; t }
99
+ mean = (ticks.sort[(ticks.size/2).round-1])*1000
100
+ avg = (ticks.inject {|sum,el| sum += el; sum}.to_f/ticks.size)*1000
101
+ min = ticks.min*1000
102
+ max = ticks.max*1000
103
+
104
+
105
+ result.eliminate_methods!([/Integer#times|Benchmark.realtime|ANSI::Code#.*|ANSI::ProgressBar#.*/])
106
+ printer = RubyProf::FlatPrinter.new(result)
107
+ # printer = RubyProf::GraphPrinter.new(result)
108
+
109
+ puts "\n",
110
+ ___,
111
+ "#{suite_name}: " + ANSI.bold(context_name) + ' should ' + ANSI.bold(name) + " (#{count}x)",
112
+ "total: #{sprintf('%.2f', total)}s | " +
113
+ "mean: #{sprintf('%.2f', mean)}ms | " +
114
+ "avg: #{sprintf('%.2f', avg)}ms | " +
115
+ "min: #{sprintf('%.2f', min)}ms | " +
116
+ "max: #{sprintf('%.2f', max)}ms",
117
+ ___
118
+ printer.print(STDOUT, {}) unless ENV['QUIET'] || options[:quiet]
119
+ end
120
+ end
121
+ end
122
+ end
123
+ end
124
+ end
@@ -0,0 +1,71 @@
1
+ # Licensed to Elasticsearch B.V. under one or more contributor
2
+ # license agreements. See the NOTICE file distributed with
3
+ # this work for additional information regarding copyright
4
+ # ownership. Elasticsearch B.V. licenses this file to you under
5
+ # the Apache License, Version 2.0 (the "License"); you may
6
+ # not use this file except in compliance with the License.
7
+ # You may obtain a copy of the License at
8
+ #
9
+ # http://www.apache.org/licenses/LICENSE-2.0
10
+ #
11
+ # Unless required by applicable law or agreed to in writing,
12
+ # software distributed under the License is distributed on an
13
+ # "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
14
+ # KIND, either express or implied. See the License for the
15
+ # specific language governing permissions and limitations
16
+ # under the License.
17
+
18
+ module Elasticsearch
19
+ module Extensions
20
+ module Test
21
+ # Startup/shutdown support for test suites
22
+ #
23
+ # Example:
24
+ #
25
+ # class MyTest < Test::Unit::TestCase
26
+ # extend Elasticsearch::Extensions::Test::StartupShutdown
27
+ #
28
+ # startup { puts "Suite starting up..." }
29
+ # shutdown { puts "Suite shutting down..." }
30
+ # end
31
+ #
32
+ # *** IMPORTANT NOTE: **********************************************************
33
+ #
34
+ # You have to register the handler for shutdown before requiring 'test/unit':
35
+ #
36
+ # # File: test_helper.rb
37
+ # at_exit { MyTest.__run_at_exit_hooks }
38
+ # require 'test/unit'
39
+ #
40
+ # The API follows Test::Unit 2.0
41
+ # <https://github.com/test-unit/test-unit/blob/master/lib/test/unit/testcase.rb>
42
+ #
43
+ module StartupShutdown
44
+ @@started = false
45
+ @@shutdown_blocks ||= []
46
+
47
+ def startup &block
48
+ return if started?
49
+ @@started = true
50
+ yield block if block_given?
51
+ end
52
+
53
+ def shutdown &block
54
+ @@shutdown_blocks << block if block_given?
55
+ end
56
+
57
+ def started?
58
+ !! @@started
59
+ end
60
+
61
+ def __run_at_exit_hooks
62
+ return unless started?
63
+ STDERR.puts ANSI.faint("Running at_exit hooks...")
64
+ puts ANSI.faint('-'*80)
65
+ @@shutdown_blocks.each { |b| b.call }
66
+ puts ANSI.faint('-'*80)
67
+ end
68
+ end
69
+ end
70
+ end
71
+ end
@@ -1,5 +1,22 @@
1
+ # Licensed to Elasticsearch B.V. under one or more contributor
2
+ # license agreements. See the NOTICE file distributed with
3
+ # this work for additional information regarding copyright
4
+ # ownership. Elasticsearch B.V. licenses this file to you under
5
+ # the Apache License, Version 2.0 (the "License"); you may
6
+ # not use this file except in compliance with the License.
7
+ # You may obtain a copy of the License at
8
+ #
9
+ # http://www.apache.org/licenses/LICENSE-2.0
10
+ #
11
+ # Unless required by applicable law or agreed to in writing,
12
+ # software distributed under the License is distributed on an
13
+ # "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
14
+ # KIND, either express or implied. See the License for the
15
+ # specific language governing permissions and limitations
16
+ # under the License.
17
+
1
18
  module Elasticsearch
2
19
  module Extensions
3
- VERSION = "0.0.3"
20
+ VERSION = '0.0.33'.freeze
4
21
  end
5
22
  end
@@ -1,3 +1,22 @@
1
+ # Licensed to Elasticsearch B.V. under one or more contributor
2
+ # license agreements. See the NOTICE file distributed with
3
+ # this work for additional information regarding copyright
4
+ # ownership. Elasticsearch B.V. licenses this file to you under
5
+ # the Apache License, Version 2.0 (the "License"); you may
6
+ # not use this file except in compliance with the License.
7
+ # You may obtain a copy of the License at
8
+ #
9
+ # http://www.apache.org/licenses/LICENSE-2.0
10
+ #
11
+ # Unless required by applicable law or agreed to in writing,
12
+ # software distributed under the License is distributed on an
13
+ # "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
14
+ # KIND, either express or implied. See the License for the
15
+ # specific language governing permissions and limitations
16
+ # under the License.
17
+
18
+ # encoding: utf-8
19
+
1
20
  require 'elasticsearch'
2
21
  require 'elasticsearch/extensions/version'
3
22
 
@@ -1 +1,6 @@
1
+ # Licensed to Elasticsearch B.V under one or more agreements.
2
+ # Elasticsearch B.V licenses this file to you under the Apache 2.0 License.
3
+ # See the LICENSE file in the project root for more information
4
+ #
5
+
1
6
  require 'elasticsearch/extensions'
@@ -0,0 +1,66 @@
1
+ # Licensed to Elasticsearch B.V. under one or more contributor
2
+ # license agreements. See the NOTICE file distributed with
3
+ # this work for additional information regarding copyright
4
+ # ownership. Elasticsearch B.V. licenses this file to you under
5
+ # the Apache License, Version 2.0 (the "License"); you may
6
+ # not use this file except in compliance with the License.
7
+ # You may obtain a copy of the License at
8
+ #
9
+ # http://www.apache.org/licenses/LICENSE-2.0
10
+ #
11
+ # Unless required by applicable law or agreed to in writing,
12
+ # software distributed under the License is distributed on an
13
+ # "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
14
+ # KIND, either express or implied. See the License for the
15
+ # specific language governing permissions and limitations
16
+ # under the License.
17
+
18
+ require 'test_helper'
19
+ require 'elasticsearch/extensions/ansi'
20
+
21
+ class Elasticsearch::Extensions::AnsiTest < Elasticsearch::Test::UnitTestCase
22
+ context "The ANSI extension" do
23
+ setup do
24
+ @client = Elasticsearch::Client.new
25
+ @client.stubs(:perform_request).returns \
26
+ Elasticsearch::Transport::Transport::Response.new(200, { "ok" => true, "status" => 200, "name" => "Hit-Maker",
27
+ "version" => { "number" => "0.90.7",
28
+ "build_hash" => "abc123",
29
+ "build_timestamp"=>"2013-11-13T12:06:54Z", "build_snapshot"=>false, "lucene_version"=>"4.5.1" },
30
+ "tagline"=>"You Know, for Search" })
31
+ end
32
+
33
+ should "wrap the response" do
34
+ response = @client.info
35
+
36
+ assert_instance_of Elasticsearch::Extensions::ANSI::ResponseBody, response
37
+ assert_instance_of Hash, response.to_hash
38
+ end
39
+
40
+ should "extend the response object with `to_ansi`" do
41
+ response = @client.info
42
+
43
+ assert_respond_to response, :to_ansi
44
+ assert_instance_of String, response.to_ansi
45
+ end
46
+
47
+ should "call the 'awesome_inspect' method when available and no handler found" do
48
+ @client.stubs(:perform_request).returns \
49
+ Elasticsearch::Transport::Transport::Response.new(200, {"index-1"=>{"aliases"=>{}}})
50
+ response = @client.cat.aliases
51
+
52
+ response.instance_eval do
53
+ def awesome_inspect; "---PRETTY---"; end
54
+ end
55
+ assert_equal '---PRETTY---', response.to_ansi
56
+ end
57
+
58
+ should "call `to_s` method when no pretty printer or handler found" do
59
+ @client.stubs(:perform_request).returns \
60
+ Elasticsearch::Transport::Transport::Response.new(200, {"index-1"=>{"aliases"=>{}}})
61
+ response = @client.cat.aliases
62
+
63
+ assert_equal '{"index-1"=>{"aliases"=>{}}}', response.to_ansi
64
+ end
65
+ end
66
+ end
@@ -0,0 +1,131 @@
1
+ # Licensed to Elasticsearch B.V. under one or more contributor
2
+ # license agreements. See the NOTICE file distributed with
3
+ # this work for additional information regarding copyright
4
+ # ownership. Elasticsearch B.V. licenses this file to you under
5
+ # the Apache License, Version 2.0 (the "License"); you may
6
+ # not use this file except in compliance with the License.
7
+ # You may obtain a copy of the License at
8
+ #
9
+ # http://www.apache.org/licenses/LICENSE-2.0
10
+ #
11
+ # Unless required by applicable law or agreed to in writing,
12
+ # software distributed under the License is distributed on an
13
+ # "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
14
+ # KIND, either express or implied. See the License for the
15
+ # specific language governing permissions and limitations
16
+ # under the License.
17
+
18
+ require 'test_helper'
19
+ require 'logger'
20
+
21
+ # Mock the Backup modules and classes so we're not depending on the gem in the unit test
22
+ #
23
+ module Backup
24
+ class Error < StandardError; end
25
+
26
+ class Logger < ::Logger
27
+ def self.logger
28
+ self.new($stderr)
29
+ end
30
+ end
31
+
32
+ module Config
33
+ module DSL
34
+ end
35
+ end
36
+
37
+ module Database
38
+ class Base
39
+ def initialize(model, database_id = nil)
40
+ end
41
+
42
+ def dump_path; 'dump_path'; end
43
+ def dump_filename; 'dump_filename'; end
44
+
45
+ def log!(*args)
46
+ puts "LOGGING..." if ENV['DEBUG']
47
+ end
48
+
49
+ def perform!
50
+ puts "PERFORMING..." if ENV['DEBUG']
51
+ end
52
+ end
53
+ end
54
+ end
55
+
56
+ require 'elasticsearch/extensions/backup'
57
+
58
+ class Elasticsearch::Extensions::BackupTest < Elasticsearch::Test::UnitTestCase
59
+ context "The Backup gem extension" do
60
+ setup do
61
+ @model = stub trigger: true
62
+ @subject = ::Backup::Database::Elasticsearch.new(@model)
63
+ end
64
+
65
+ should "have a client" do
66
+ assert_instance_of Elasticsearch::Transport::Client, @subject.client
67
+ end
68
+
69
+ should "have a path" do
70
+ assert_instance_of Pathname, @subject.path
71
+ end
72
+
73
+ should "have defaults" do
74
+ assert_equal 'http://localhost:9200', @subject.url
75
+ assert_equal '_all', @subject.indices
76
+ end
77
+
78
+ should "be configurable" do
79
+ @subject = ::Backup::Database::Elasticsearch.new(@model) do |db|
80
+ db.url = 'https://example.com'
81
+ db.indices = 'foo,bar'
82
+ end
83
+
84
+ assert_equal 'https://example.com', @subject.url
85
+ assert_equal 'foo,bar', @subject.indices
86
+
87
+ assert_equal 'example.com', @subject.client.transport.connections.first.host[:host]
88
+ end
89
+
90
+ should "perform the backup" do
91
+ @subject.expects(:__perform_single)
92
+ @subject.perform!
93
+ end
94
+
95
+ should "raise an expection for an unsupported type of backup" do
96
+ @subject = ::Backup::Database::Elasticsearch.new(@model) { |db| db.mode = 'foobar' }
97
+ assert_raise ::Backup::Database::Elasticsearch::Error do
98
+ @subject.perform!
99
+ end
100
+ end
101
+
102
+ should "scan and scroll the index" do
103
+ @subject = ::Backup::Database::Elasticsearch.new(@model) { |db| db.indices = 'test' }
104
+
105
+ @subject.client
106
+ .expects(:search)
107
+ .with do |params|
108
+ assert_equal 'test', params[:index]
109
+ true # Thanks, Ruby 2.2
110
+ end
111
+ .returns({"_scroll_id" => "abc123"})
112
+
113
+ @subject.client
114
+ .expects(:scroll)
115
+ .twice
116
+ .returns({"_scroll_id" => "def456",
117
+ "hits" => { "hits" => [ {"_index"=>"test", "_type"=>"doc", "_id"=>"1", "_source"=>{"title"=>"Test"}} ] }
118
+ })
119
+ .then
120
+ .returns({"_scroll_id" => "ghi789",
121
+ "hits" => { "hits" => [] }
122
+ })
123
+
124
+ @subject.__perform_single
125
+ end
126
+
127
+ should "sanitize filename" do
128
+ assert_equal "foo-bar-baz", @subject.__sanitize_filename("foo/bar\nbaz")
129
+ end
130
+ end
131
+ end
@@ -0,0 +1,107 @@
1
+ # Licensed to Elasticsearch B.V. under one or more contributor
2
+ # license agreements. See the NOTICE file distributed with
3
+ # this work for additional information regarding copyright
4
+ # ownership. Elasticsearch B.V. licenses this file to you under
5
+ # the Apache License, Version 2.0 (the "License"); you may
6
+ # not use this file except in compliance with the License.
7
+ # You may obtain a copy of the License at
8
+ #
9
+ # http://www.apache.org/licenses/LICENSE-2.0
10
+ #
11
+ # Unless required by applicable law or agreed to in writing,
12
+ # software distributed under the License is distributed on an
13
+ # "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
14
+ # KIND, either express or implied. See the License for the
15
+ # specific language governing permissions and limitations
16
+ # under the License.
17
+
18
+ require 'test_helper'
19
+ require 'elasticsearch/extensions/reindex'
20
+
21
+ class Elasticsearch::Extensions::ReindexIntegrationTest < Elasticsearch::Test::IntegrationTestCase
22
+ context "The Reindex extension" do
23
+ setup do
24
+ @port = (ENV['TEST_CLUSTER_PORT'] || 9250).to_i
25
+
26
+ @logger = ::Logger.new(STDERR)
27
+ @logger.formatter = proc do |severity, datetime, progname, msg|
28
+ color = case severity
29
+ when /INFO/ then :green
30
+ when /ERROR|WARN|FATAL/ then :red
31
+ when /DEBUG/ then :cyan
32
+ else :white
33
+ end
34
+ ANSI.ansi(severity[0] + ' ', color, :faint) + ANSI.ansi(msg, :white, :faint) + "\n"
35
+ end
36
+
37
+ @client = Elasticsearch::Client.new host: "#{TEST_HOST}:#{TEST_PORT}", logger: @logger
38
+ @client.indices.delete index: '_all'
39
+
40
+ @client.index index: 'test1', type: 'd', id: 1, body: { title: 'TEST 1', category: 'one' }
41
+ @client.index index: 'test1', type: 'd', id: 2, body: { title: 'TEST 2', category: 'two' }
42
+ @client.index index: 'test1', type: 'd', id: 3, body: { title: 'TEST 3', category: 'three' }
43
+ @client.indices.refresh index: 'test1'
44
+
45
+ @client.indices.create index: 'test2'
46
+
47
+ @client.cluster.health wait_for_status: 'yellow'
48
+ end
49
+
50
+ teardown do
51
+ @client.indices.delete index: '_all'
52
+ end
53
+
54
+ should "copy documents from one index to another" do
55
+ reindex = Elasticsearch::Extensions::Reindex.new \
56
+ source: { index: 'test1', client: @client },
57
+ dest: { index: 'test2' },
58
+ batch_size: 2,
59
+ refresh: true
60
+
61
+ result = reindex.perform
62
+
63
+ assert_equal 0, result[:errors]
64
+ assert_equal 3, @client.search(index: 'test2')['hits']['total']['value']
65
+ end
66
+
67
+ should "transform documents with a lambda" do
68
+ reindex = Elasticsearch::Extensions::Reindex.new \
69
+ source: { index: 'test1', client: @client },
70
+ dest: { index: 'test2' },
71
+ transform: lambda { |d| d['_source']['category'].upcase! },
72
+ refresh: true
73
+
74
+ result = reindex.perform
75
+
76
+ assert_equal 0, result[:errors]
77
+ assert_equal 3, @client.search(index: 'test2')['hits']['total']['value']
78
+ assert_equal 'ONE', @client.get(index: 'test2', type: 'd', id: 1)['_source']['category']
79
+ end
80
+
81
+ should "return the number of errors" do
82
+ @client.indices.create index: 'test3', body: { mappings: { properties: { category: { type: 'integer' } }}}
83
+ @client.cluster.health wait_for_status: 'yellow'
84
+
85
+ reindex = Elasticsearch::Extensions::Reindex.new \
86
+ source: { index: 'test1', client: @client },
87
+ dest: { index: 'test3' }
88
+
89
+ result = reindex.perform
90
+
91
+ @client.indices.refresh index: 'test3'
92
+
93
+ assert_equal 3, result[:errors]
94
+ assert_equal 0, @client.search(index: 'test3')['hits']['total']['value']
95
+ end
96
+
97
+ should "reindex via the API integration" do
98
+ @client.indices.create index: 'test4'
99
+
100
+ @client.reindex source: { index: 'test1' }, dest: { index: 'test4' }
101
+ @client.indices.refresh index: 'test4'
102
+
103
+ assert_equal 3, @client.search(index: 'test4')['hits']['total']['value']
104
+ end
105
+ end
106
+
107
+ end
@@ -0,0 +1,123 @@
1
+ # Licensed to Elasticsearch B.V. under one or more contributor
2
+ # license agreements. See the NOTICE file distributed with
3
+ # this work for additional information regarding copyright
4
+ # ownership. Elasticsearch B.V. licenses this file to you under
5
+ # the Apache License, Version 2.0 (the "License"); you may
6
+ # not use this file except in compliance with the License.
7
+ # You may obtain a copy of the License at
8
+ #
9
+ # http://www.apache.org/licenses/LICENSE-2.0
10
+ #
11
+ # Unless required by applicable law or agreed to in writing,
12
+ # software distributed under the License is distributed on an
13
+ # "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
14
+ # KIND, either express or implied. See the License for the
15
+ # specific language governing permissions and limitations
16
+ # under the License.
17
+
18
+ require 'test_helper'
19
+ require 'elasticsearch/extensions/reindex'
20
+
21
+ class Elasticsearch::Extensions::ReindexTest < Elasticsearch::Test::UnitTestCase
22
+ context "The Reindex extension module" do
23
+ DEFAULT_OPTIONS = { source: { index: 'foo', client: Object.new }, dest: { index: 'bar' } }
24
+
25
+ should "require options" do
26
+ assert_raise ArgumentError do
27
+ Elasticsearch::Extensions::Reindex.new
28
+ end
29
+ end
30
+
31
+ should "allow to initialize the class" do
32
+ assert_instance_of Elasticsearch::Extensions::Reindex::Reindex,
33
+ Elasticsearch::Extensions::Reindex.new(DEFAULT_OPTIONS)
34
+ end
35
+
36
+ should "add the reindex to the API and client" do
37
+ assert_includes Elasticsearch::API::Actions.public_instance_methods.sort, :reindex
38
+ assert_respond_to Elasticsearch::Client.new, :reindex
39
+ end
40
+
41
+ should "pass the client when used in API mode" do
42
+ client = Elasticsearch::Client.new
43
+
44
+ Elasticsearch::Extensions::Reindex::Reindex
45
+ .expects(:new)
46
+ .with({source: { client: client }})
47
+ .returns(stub perform: {})
48
+
49
+ client.reindex
50
+ end
51
+
52
+ context "when performing the operation" do
53
+ setup do
54
+ d = { '_id' => 'foo', '_type' => 'type', '_source' => { 'foo' => 'bar' } }
55
+ @default_response = { 'hits' => { 'hits' => [d] } }
56
+ @empty_response = { 'hits' => { 'hits' => [] } }
57
+ @bulk_request = [{ index: {
58
+ '_index' => 'bar',
59
+ '_type' => d['_type'],
60
+ '_id' => d['_id'],
61
+ 'data' => d['_source']
62
+ } }]
63
+ @bulk_response = {'errors'=>false, 'items' => [{'index' => {}}, {'index' => {}}]}
64
+ @bulk_response_error = {'errors'=>true, 'items' => [{'index' => {}}, {'index' => {'error' => 'FOOBAR'}}]}
65
+ end
66
+
67
+ should "scroll through the index and save batches in bulk" do
68
+ client = mock()
69
+ subject = Elasticsearch::Extensions::Reindex.new source: { index: 'foo', client: client },
70
+ dest: { index: 'bar' }
71
+
72
+ client.expects(:search)
73
+ .returns({ '_scroll_id' => 'scroll_id_1' }.merge(Marshal.load(Marshal.dump(@default_response))))
74
+ client.expects(:scroll)
75
+ .returns(Marshal.load(Marshal.dump(@default_response)))
76
+ .then
77
+ .returns(@empty_response).times(2)
78
+ client.expects(:bulk)
79
+ .with(body: @bulk_request)
80
+ .returns(@bulk_response).times(2)
81
+
82
+ result = subject.perform
83
+
84
+ assert_equal 0, result[:errors]
85
+ end
86
+
87
+ should "return the number of errors" do
88
+ client = mock()
89
+ subject = Elasticsearch::Extensions::Reindex.new source: { index: 'foo', client: client },
90
+ dest: { index: 'bar' }
91
+
92
+ client.expects(:search).returns({ '_scroll_id' => 'scroll_id_1' }.merge(@default_response))
93
+ client.expects(:scroll).returns(@empty_response)
94
+ client.expects(:bulk).with(body: @bulk_request).returns(@bulk_response_error)
95
+
96
+ result = subject.perform
97
+
98
+ assert_equal 1, result[:errors]
99
+ end
100
+
101
+ should "transform the documents with a lambda" do
102
+ client = mock()
103
+ subject = Elasticsearch::Extensions::Reindex.new \
104
+ source: { index: 'foo', client: client },
105
+ dest: { index: 'bar' },
106
+ transform: lambda { |d| d['_source']['foo'].upcase!; d }
107
+
108
+ client.expects(:search).returns({ '_scroll_id' => 'scroll_id_1' }.merge(@default_response))
109
+ client.expects(:scroll).returns(@empty_response)
110
+ client.expects(:bulk).with do |arguments|
111
+ assert_equal 'BAR', arguments[:body][0][:index]['data']['foo']
112
+ true
113
+ end
114
+ .returns(@bulk_response)
115
+
116
+ result = subject.perform
117
+
118
+ assert_equal 0, result[:errors]
119
+ end
120
+ end
121
+
122
+ end
123
+ end