require 'spec_helper'

describe IEX::Api::Client do
  before do
    IEX::Api.config.reset!
    IEX::Api.logger.reset!
  end

  context 'with defaults' do
    let(:client) { described_class.new }

    describe '#initialize' do
      it 'implements endpoint' do
        expect(client.endpoint).not_to be nil
      end

      it 'includes default http configuration' do
        expect(client.user_agent).to eq "IEX Ruby Client/#{IEX::VERSION}"
      end

      it 'sets user-agent' do
        expect(client.user_agent).to eq IEX::Api::Config::Client.user_agent
        expect(client.user_agent).to include IEX::VERSION
      end

      it 'caches the Faraday connection to allow persistent adapters' do
        first = client.send(:connection)
        second = client.send(:connection)
        expect(first).to equal second
      end

      it 'sets a nil logger' do
        expect(client.logger.instance).to be_nil
        expect(client.send(:connection).builder.handlers).not_to include(::Faraday::Response::Logger)
      end

      IEX::Api::Config::Client::ATTRIBUTES.each do |key|
        it "sets #{key}" do
          expect(client.send(key)).to eq IEX::Api::Config::Client.send(key)
        end
      end
    end
  end

  context 'with custom settings' do
    describe '#initialize' do
      IEX::Api::Config::Client::ATTRIBUTES.each do |key|
        context key.to_s do
          let(:client) { described_class.new(key => 'custom') }

          it "sets #{key}" do
            expect(client.send(key)).not_to eq IEX::Api::Config::Client.send(key)
            expect(client.send(key)).to eq 'custom'
          end
        end
      end
    end
  end

  context 'global config' do
    let(:client) { described_class.new }

    context 'user-agent' do
      before do
        IEX::Api.configure do |config|
          config.user_agent = 'custom/user-agent'
        end
      end

      describe '#initialize' do
        it 'sets user-agent' do
          expect(client.user_agent).to eq 'custom/user-agent'
        end

        it 'creates a connection with the custom user-agent' do
          expect(client.send(:connection).headers).to include(
            'Accept' => 'application/json; charset=utf-8',
            'User-Agent' => 'custom/user-agent'
          )
        end
      end
    end

    context 'proxy' do
      before do
        IEX::Api.configure do |config|
          config.proxy = 'http://localhost:8080'
        end
      end

      describe '#initialize' do
        it 'sets proxy' do
          expect(client.proxy).to eq 'http://localhost:8080'
        end

        it 'creates a connection with the proxy' do
          expect(client.send(:connection).proxy.uri.to_s).to eq 'http://localhost:8080'
        end
      end
    end

    context 'SSL options' do
      before do
        IEX::Api.configure do |config|
          config.ca_path = '/ca_path'
          config.ca_file = '/ca_file'
        end
      end

      describe '#initialize' do
        it 'sets ca_path and ca_file' do
          expect(client.ca_path).to eq '/ca_path'
          expect(client.ca_file).to eq '/ca_file'
        end

        it 'creates a connection with ssl options' do
          ssl = client.send(:connection).ssl
          expect(ssl.ca_path).to eq '/ca_path'
          expect(ssl.ca_file).to eq '/ca_file'
        end
      end
    end

    context 'logger option' do
      let(:logger) { Logger.new(STDOUT) }

      after { IEX::Api.logger.reset! }

      context 'when assigning an instance' do
        describe '#initialize' do
          context 'when directly assigning `logger`' do
            before { IEX::Api.logger = logger }

            it 'sets logger' do
              expect(client.logger.instance).to eq(logger)
            end

            it 'creates a connection with a logger' do
              expect(client.send(:connection).builder.handlers).to include ::Faraday::Response::Logger
            end
          end

          context 'when assigning through `configure.logger`' do
            it 'sets the logger' do
              IEX::Api.configure.logger = logger
              expect(client.logger.instance).to eq(logger)
            end
          end

          context 'when passing in at initialization' do
            it 'sets the logger' do
              client = described_class.new(logger: logger)
              expect(client.logger.instance).to eq(logger)
            end

            it 'can overwrite a set logger' do
              IEX::Api.logger = logger
              client = described_class.new(logger: nil)
              expect(client.logger.instance).to be_nil
            end
          end
        end
      end

      context 'when assigning a configuration' do
        let(:opts) { { bodies: true } }
        let(:proc_arg) { proc {} }

        before do
          IEX::Api.logger do |log_config|
            log_config.instance = logger
            log_config.options = opts
            log_config.proc = proc_arg
          end
        end

        describe '#initialize' do
          it 'sets logger' do
            expect(client.logger.instance).to eq logger
            expect(client.logger.options).to eq opts
            expect(client.logger.proc).to eq proc_arg
          end

          it 'creates a connection with a logger' do
            expect(client.send(:connection).builder.handlers).to include ::Faraday::Response::Logger
          end
        end
      end
    end

    context 'timeout options' do
      before do
        IEX::Api.configure do |config|
          config.timeout = 10
          config.open_timeout = 15
        end
      end

      describe '#initialize' do
        it 'sets timeout and open_timeout' do
          expect(client.timeout).to eq 10
          expect(client.open_timeout).to eq 15
        end

        it 'creates a connection with timeout options' do
          conn = client.send(:connection)
          expect(conn.options.timeout).to eq 10
          expect(conn.options.open_timeout).to eq 15
        end
      end
    end

    context 'when resetting/changing configuration' do
      before do
        IEX::Api.configure { |config| config.user_agent = 'custom/user-agent' }
      end

      it 'does not reset the client' do
        expect { IEX::Api.config.reset! }.not_to change(client, :user_agent).from('custom/user-agent')
      end

      it 'effects the next client' do
        pre_config_client = described_class.new
        IEX::Api.configure { |config| config.user_agent = 'custom/user-agent-2' }
        expect(described_class.new.user_agent).not_to eq(pre_config_client.user_agent)
      end

      it 'does not allow the client to reset' do
        expect { client.reset! }.to raise_error(NoMethodError)
      end
    end

    context 'without a token' do
      let(:client) { described_class.new }

      it 'results in an API key error', vcr: { cassette_name: 'client/access_denied' } do
        expect do
          client.get '/stock/msft/quote', token: client.publishable_token
        end.to raise_error IEX::Errors::PermissionDeniedError, /The API key provided is not valid./
      end
    end
  end
end
