require 'spec_helper'

RSpec.describe Base::APIClient::ClientSecret do
  subject { Base::APIClient::ClientSecret.new }
  let(:secret_file) { FIXTURES_PATH / 'files' / 'client_secret.json' }
  let(:data) { MultiJson.load(File.open(secret_file, 'r').read) }

  before(:each) do
    allow_any_instance_of(Base::APIClient::ClientSecret)
      .to receive(:fetch_token) do
      { access_token:  'fake_access_token',
        refresh_token: 'fake_refresh_token' }
    end
  end

  describe '#load_file' do
    it 'reads client_secret.json' do
      obj = instance_double('ClientSecret')
      allow(obj).to receive(:load_file) { data }
      expect(obj.send(:load_file)).to eq data
    end
  end

  describe '::new' do
    describe 'existence of instance variables' do
      it 'has @client_id' do
        expect(subject).to respond_to(:client_id)
      end

      it 'has @client_secret' do
        expect(subject).to respond_to(:client_secret)
      end

      it 'has @code' do
        expect(subject).to respond_to(:code)
      end

      it 'has @redirect_uri' do
        expect(subject).to respond_to(:redirect_uri)
      end

      it 'has @search_client_id' do
        expect(subject).to respond_to(:search_client_id)
      end

      it 'has @search_client_secret' do
        expect(subject).to respond_to(:search_client_secret)
      end
    end

    describe 'without parameters' do
      it 'instance variables have correct values' do
        expect(subject.client_id).to eq 'fake_client_id'
        expect(subject.client_secret).to eq 'fake_client_secret'
        expect(subject.code).to eq 'fake_code'
        expect(subject.redirect_uri).to eq 'http://fake_redirect_uri.com'
        expect(subject.search_client_id).to eq 'fake_search_client_id'
        expect(subject.search_client_secret).to eq 'fake_search_client_secret'
      end
    end

    describe 'with parameters' do
      subject do
        Base::APIClient::ClientSecret.new do |zelf|
          zelf.client_id = 'optional_client_id'
          zelf.client_secret = 'optional_client_secret'
          zelf.code = 'optional_code'
          zelf.redirect_uri = 'optional_redirect_uri'
          zelf.search_client_id = 'optional_search_client_id'
          zelf.search_client_secret = 'optional_search_client_secret'
        end
      end

      describe 'content of variables' do
        it 'set the variable by given value' do
          expect(subject.client_id).to eq 'optional_client_id'
          expect(subject.client_secret).to eq 'optional_client_secret'
          expect(subject.code).to eq 'optional_code'
          expect(subject.redirect_uri).to eq 'optional_redirect_uri'
          expect(subject.search_client_id).to eq 'optional_search_client_id'
          expect(subject.search_client_secret).to eq \
            'optional_search_client_secret'
        end
      end
    end
  end

  describe '#update' do
    it 'update attributes of the instance' do
      subject.update! do |zelf|
        zelf.client_id            = 'updated_client_id'
        zelf.client_secret        = 'updated_client_secret'
        zelf.code                 = 'updated_code'
        zelf.redirect_uri         = 'updated_redirect_uri'
        zelf.search_client_id     = 'updated_search_client_id'
        zelf.search_client_secret = 'updated_search_client_secret'
      end

      expect(subject.client_id).to eq 'updated_client_id'
      expect(subject.client_secret).to eq 'updated_client_secret'
      expect(subject.code).to eq 'updated_code'
      expect(subject.redirect_uri).to eq 'updated_redirect_uri'
      expect(subject.search_client_id).to eq 'updated_search_client_id'
      expect(subject.search_client_secret).to eq 'updated_search_client_secret'
    end
  end

  describe '#header_parameter' do
    it 'returns correct parameter' do
      subject.instance_variable_set(:@access_token, 'fake_access_token')
      expect(subject.header_parameter).to eq(
        'Authorization' => 'Bearer fake_access_token')
    end
  end

  describe '#to_hash' do
    it 'returns values of instance variables in hash' do
      expect(subject.send(:to_hash)).to eq(
        client_id:            'fake_client_id',
        client_secret:        'fake_client_secret',
        code:                 'fake_code',
        redirect_uri:         'http://fake_redirect_uri.com',
        refresh_token:        nil,
        search_client_id:     'fake_search_client_id',
        search_client_secret: 'fake_search_client_secret')
    end
  end

  describe '#to_json' do
    it 'returns values of instance variables in json' do
      expect(subject.send(:to_json)).to eq \
        <<"EOS""{\"client_id\":\"fake_client_id\",\"client_secret\":\"fake_client_secret\",\"code\":\"fake_code\",\"redirect_uri\":\"http://fake_redirect_uri.com\",\"refresh_token\":null,\"search_client_id\":\"fake_search_client_id\",\"search_client_secret\":\"fake_search_client_secret\"}"
EOS
    end
  end

  describe '#generate_authorize_parameters' do
    it 'returns parameters' do
      expect(subject.send(:generate_authorize_parameters)).to eq \
        <<"EOS""?client_id=fake_client_id&redirect_uri=http://fake_redirect_uri.com&state=&response_type=code&scope=read_items read_orders read_savings read_users read_users_mail write_items write_orders"
EOS
    end
  end

  describe '#command' do
    context 'OS is Linux' do
      it 'returns "sensible-browser"' do
        allow(OS).to receive(:linux?) { true }
        expect(subject.send(:command)).to eq 'sensible-browser'
      end
    end

    context 'OS is OSX' do
      it 'returns "open"' do
        allow(OS).to receive(:linux?) { false }
        allow(OS).to receive(:mac?) { true }
        expect(subject.send(:command)).to eq 'open'
      end
    end
  end

  describe '#generate_code' do
    context 'OS is Linux' do
      before(:each) do
        allow(subject).to receive(:command) { 'sensible-browser' }
      end

      it 'call sensible-browser' do
        expect(subject).to receive(:system).with(
          'sensible-browser',
          'https://api.thebase.in/1/oauth/authorize?client_id=fake_client_id&redirect_uri=http://fake_redirect_uri.com&state=&response_type=code&scope=read_items read_orders read_savings read_users read_users_mail write_items write_orders')

        subject.send(:generate_code)
      end

      it 'returns ture' do
        allow(subject).to receive(:generate_code) { true }
        expect(subject.send(:generate_code)).to eq true
      end
    end

    context 'OS is OSX' do
      before(:each) do
        allow(subject).to receive(:command) { 'open' }
      end

      it 'call open' do
        expect(subject).to receive(:system).with(
          'open',
          'https://api.thebase.in/1/oauth/authorize?client_id=fake_client_id&redirect_uri=http://fake_redirect_uri.com&state=&response_type=code&scope=read_items read_orders read_savings read_users read_users_mail write_items write_orders')

        subject.send(:generate_code)
      end

      it 'returns ture' do
        allow(subject).to receive(:generate_code) { true }
        expect(subject.send(:generate_code)).to eq true
      end
    end
  end

  describe '#fetch_token' do
    context 'passed arg "refresh: false"' do
      let(:arg) { { refresh: false } }

      it 'returns access and refresh tokens' do
        stub_request(
          :post,
          URI.parse(Base::APIClient::ClientSecret::TOKEN_URI.to_s))
          .with(body:
            subject
            .send(:to_hash)
            .merge(grant_type: 'authorization_code'))

        expect(subject.send(:fetch_token, arg)).to eq(
          access_token:  'fake_access_token',
          refresh_token: 'fake_refresh_token')
      end
    end

    context 'passed arg "refresh: true"' do
      let(:arg) { { refresh: true } }

      it 'returns access and refresh tokens' do
        stub_request(
          :post,
          URI.parse(Base::APIClient::ClientSecret::TOKEN_URI.to_s))
          .with(body:
            subject
            .send(:to_hash)
            .merge(grant_type: 'refresh_token'))

        expect(subject.send(:fetch_token, arg)).to eq(
          access_token:  'fake_access_token',
          refresh_token: 'fake_refresh_token')
      end
    end
  end

  describe '#set_tokens!' do
    context 'passed arg "refresh: false"' do
      let(:arg) { { refresh: false } }

      it 'set access and refresh tokens' do
        allow(subject).to receive(:fetch_token) do
          { 'access_token'  => 'stubbed_access_token',
            'refresh_token' => 'stubbed_refresh_token' }
        end

        subject.send(:set_tokens!, arg)
        expect(subject.access_token).to  eq 'stubbed_access_token'
        expect(subject.refresh_token).to eq 'stubbed_refresh_token'
      end
    end

    context 'passed arg "refresh: true"' do
      let(:arg) { { refresh: true } }

      it 'set access and refresh tokens' do
        allow(subject).to receive(:fetch_token).with(arg) do
          { 'access_token'  => 'stubbed_access_token',
            'refresh_token' => 'stubbed_refresh_token' }
        end

        subject.send(:set_tokens!, arg)
        expect(subject.access_token).to  eq 'stubbed_access_token'
        expect(subject.refresh_token).to eq 'stubbed_refresh_token'
      end
    end
  end

  describe '#my_info' do
    let(:response) do
      VCR.use_cassette('ClientSecret_my_info') { subject.my_info }
    end

    it 'contains code "200"' do
      expect(response.code).to eq '200'
    end

    it '@body contains information of the user' do
      expect(response.body).to match(/user/)
      expect(response.body).to match(/shop_id/)
      expect(response.body).to match(/shop_name/)
      expect(response.body).to match(/shop_introduction/)
      expect(response.body).to match(/shop_url/)
      expect(response.body).to match(/twitter_id/)
      expect(response.body).to match(/facebook_id/)
      expect(response.body).to match(/ameba_id/)
      expect(response.body).to match(/instagram_id/)
      expect(response.body).to match(/background/)
      expect(response.body).to match(/display_background/)
      expect(response.body).to match(/repeat_background/)
      expect(response.body).to match(/logo/)
      expect(response.body).to match(/display_logo/)
      expect(response.body).to match(/mail_address/)
    end
  end

  describe '#my_items' do
    let(:response) do
      VCR.use_cassette('ClientSecret_my_items') { subject.my_items }
    end

    it 'contains code "200"' do
      expect(response.code).to eq '200'
    end

    it '@body contains information of the items' do
      expect(response.body).to match(/items/)
      expect(response.body).to match(/item_id/)
      expect(response.body).to match(/title/)
      expect(response.body).to match(/detail/)
      expect(response.body).to match(/price/)
      expect(response.body).to match(/stock/)
      expect(response.body).to match(/visible/)
      expect(response.body).to match(/list_order/)
      expect(response.body).to match(/identifier/)
    end
  end
end