# Licensed to Elasticsearch B.V. under one or more contributor
# license agreements. See the NOTICE file distributed with
# this work for additional information regarding copyright
# ownership. Elasticsearch B.V. licenses this file to you under
# the Apache License, Version 2.0 (the "License"); you may
# not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
#   http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing,
# software distributed under the License is distributed on an
# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
# KIND, either express or implied.  See the License for the
# specific language governing permissions and limitations
# under the License.

require 'test_helper'

if JRUBY
  require 'elasticsearch/transport/transport/http/manticore'
  require 'manticore'

  module Elasticsearch
    module Transport
      module Transport
        module HTTP
          class ManticoreTest < Minitest::Test
            include Elasticsearch::Transport::Transport::HTTP

            def common_headers
              {
                'Content-Type' => 'application/json',
                'User-Agent' => @transport.send(:user_agent_header)
              }
            end

            context 'Manticore transport' do
              setup do
                @transport = Manticore.new(hosts: [{ host: '127.0.0.1', port: 8080 }])
              end

              should 'implement host_unreachable_exceptions' do
                assert_instance_of Array, @transport.host_unreachable_exceptions
              end

              should 'implement __build_connections' do
                assert_equal 1, @transport.hosts.size
                assert_equal 1, @transport.connections.size

                assert_instance_of(::Manticore::Client, @transport.connections.first.connection)
              end

              should 'not close connections in __close_connections' do
                assert_equal 1, @transport.connections.size
                @transport.__close_connections
                assert_equal 1, @transport.connections.size
              end

              should 'perform the request' do
                @transport.connections.first.connection.expects(:get).returns(stub_everything)
                response = @transport.perform_request('GET', '/')
              end

              should 'set body for GET request' do
                @transport.connections.first.connection.expects(:get)
                  .with(
                    'http://127.0.0.1:8080/',
                    {
                      body: '{"foo":"bar"}',
                      headers: common_headers
                    }
                  ).returns(stub_everything)
                @transport.perform_request 'GET', '/', {}, '{"foo":"bar"}'
              end

              should 'set body for PUT request' do
                @transport.connections.first.connection.expects(:put)
                  .with(
                    'http://127.0.0.1:8080/',
                    {
                      body: '{"foo":"bar"}',
                      headers: {
                        'Content-Type' => 'application/json',
                        'User-Agent' => @transport.send(:user_agent_header)
                      }
                    }
                  ).returns(stub_everything)
                @transport.perform_request 'PUT', '/', {}, { foo: 'bar' }
              end

              should 'serialize the request body' do
                @transport.connections.first.connection.expects(:post)
                  .with(
                    'http://127.0.0.1:8080/',
                    {
                      body: '{"foo":"bar"}',
                      headers: {
                        'Content-Type' => 'application/json',
                        'User-Agent' => @transport.send(:user_agent_header)
                      }
                    }
                  ).returns(stub_everything)
                @transport.perform_request 'POST', '/', {}, { 'foo' => 'bar' }
              end

              should 'set custom headers for PUT request' do
                @transport.connections.first.connection.expects(:put)
                  .with(
                    'http://127.0.0.1:8080/',
                    {
                      body: '{"foo":"bar"}',
                      headers: {
                        'Content-Type' => 'application/json',
                        'User-Agent' => @transport.send(:user_agent_header)
                      }
                    }
                  ).returns(stub_everything)
                @transport.perform_request 'PUT', '/', {}, '{"foo":"bar"}', { 'Content-Type' => 'application/x-ndjson' }
              end

              should 'not serialize a String request body' do
                @transport.connections.first.connection.expects(:post)
                  .with(
                    'http://127.0.0.1:8080/',
                    {
                      body: '{"foo":"bar"}',
                      headers: {
                        'Content-Type' => 'application/json',
                        'User-Agent' => @transport.send(:user_agent_header)
                      }
                    }
                  ).returns(stub_everything)
                @transport.serializer.expects(:dump).never
                @transport.perform_request 'POST', '/', {}, '{"foo":"bar"}'
              end

              should 'set application/json header' do
                options = {
                  headers: { 'content-type' => 'application/json' }
                }

                transport = Manticore.new(hosts: [{ host: 'localhost', port: 8080 }], options: options)
                transport.connections.first.connection.stub(
                  'http://localhost:8080/',
                  body: '""',
                  headers: {
                    'Content-Type' => 'application/x-ndjson',
                    'User-Agent' => @transport.send(:user_agent_header)
                  },
                  code: 200
                )
                response = transport.perform_request('GET', '/', {})
                assert_equal response.status, 200
              end

              should "set headers from 'transport_options'" do
                options = {
                  transport_options: {
                    headers: { 'Content-Type' => 'foo/bar' }
                  }
                }

                transport = Manticore.new(hosts: [{ host: 'localhost', port: 8080 }], options: options)

                assert_equal(
                  'foo/bar',
                  transport.connections.first.connection.instance_variable_get(:@options)[:headers]['Content-Type']
                )
                # TODO: Needs to check @request_options
              end

              should 'handle HTTP methods' do
                @transport.connections.first.connection.expects(:delete).with('http://127.0.0.1:8080/', { headers: common_headers }).returns(stub_everything)
                @transport.connections.first.connection.expects(:head).with('http://127.0.0.1:8080/', { headers: common_headers }).returns(stub_everything)
                @transport.connections.first.connection.expects(:get).with('http://127.0.0.1:8080/', { headers: common_headers }).returns(stub_everything)
                @transport.connections.first.connection.expects(:put).with('http://127.0.0.1:8080/', { headers: common_headers }).returns(stub_everything)
                @transport.connections.first.connection.expects(:post).with('http://127.0.0.1:8080/', { headers: common_headers }).returns(stub_everything)

                %w[HEAD GET PUT POST DELETE].each { |method| @transport.perform_request method, '/' }

                assert_raise(ArgumentError) { @transport.perform_request 'FOOBAR', '/' }
              end

              should 'allow to set options for Manticore' do
                options = { headers: { 'User-Agent' => 'myapp-0.0' } }
                transport = Manticore.new(hosts: [{ host: 'foobar', port: 1234 }], options: options)
                transport.connections.first.connection
                  .expects(:get)
                  .with do |_host, _options|
                  assert_equal 'myapp-0.0', _options[:headers]['User-Agent']
                  true
                end
                  .returns(stub_everything)

                transport.perform_request 'GET', '/', {}
              end

              should 'allow to set ssl options for Manticore' do
                options = {
                  ssl: {
                    truststore: 'test.jks',
                    truststore_password: 'test',
                    verify: false
                  }
                }

                ::Manticore::Client.expects(:new).with(options)
                transport = Manticore.new(hosts: [{ host: 'foobar', port: 1234 }], options: options)
                assert_equal(transport.options[:ssl][:truststore_password], 'test')
              end

              should 'pass :transport_options to Manticore::Client' do
                options = {
                  transport_options: { potatoes: 1 }
                }

                ::Manticore::Client.expects(:new).with(potatoes: 1, ssl: {})
                transport = Manticore.new(hosts: [{ host: 'foobar', port: 1234 }], options: options)
                assert_equal(transport.options[:transport_options][:potatoes], 1)
              end

              context 'custom headers' do
                should 'allow authorization headers' do
                  transport_options = { headers: { 'Authorization' => 'Basic token' } }
                  transport = Manticore.new(
                    hosts: [{ host: 'foobar', port: 1234 }],
                    transport_options: transport_options
                  )

                  assert_equal(
                    transport.instance_variable_get(:@request_options)[:headers]['Authorization'],
                    'Basic token'
                  )
                  transport.connections.first.connection.expects(:get).with do |_host, options|
                    assert_equal('Basic token', options[:headers]['Authorization'])
                    true
                  end.returns(stub_everything)
                  transport.perform_request('GET', '/', {})
                end

                should 'allow user agent headers' do
                  transport_options = { headers: { 'User-Agent' => 'Custom UA' } }
                  transport = Manticore.new(
                    hosts: [{ host: 'localhost' }],
                    transport_options: transport_options
                  )
                  transport.connections.first.connection.expects(:get).with do |_host, options|
                    assert_equal('Custom UA', options[:headers]['User-Agent'])
                    true
                  end.returns(stub_everything)
                  transport.perform_request('GET', '/', {})

                  assert_equal(
                    transport.instance_variable_get('@request_options')[:headers]['User-Agent'],
                    'Custom UA'
                  )
                end
              end
            end
          end
        end
      end
    end
  end
else
  version = "#{defined?(RUBY_ENGINE) ? RUBY_ENGINE : 'Ruby'} #{RUBY_VERSION}"
  puts "SKIP: '#{File.basename(__FILE__)}' only supported on JRuby (you're running #{version})"
end
