require 'spec_helper'
require 'aruba/api'
require 'fileutils'

RSpec.describe Aruba::Api::Core do
  include_context 'uses aruba API'

  describe '#cd' do
    before do
      @directory_name = 'test_dir'
      @directory_path = File.join(@aruba.aruba.current_directory, @directory_name)
    end

    context 'with a block given' do
      it 'runs the passed block in the given directory' do
        @aruba.create_directory @directory_name
        full_path = File.expand_path(@directory_path)
        @aruba.cd @directory_name do
          expect(Dir.pwd).to eq full_path
        end
        expect(Dir.pwd).not_to eq full_path
      end

      it 'sets directory environment in the passed block' do
        @aruba.create_directory @directory_name
        old_pwd = Dir.pwd
        full_path = File.expand_path(@directory_path)
        @aruba.cd @directory_name do
          expect(ENV['PWD']).to eq full_path
          expect(ENV['OLDPWD']).to eq old_pwd
        end
      end

      it 'sets aruba environment in the passed block' do
        @aruba.create_directory @directory_name
        @aruba.set_environment_variable('FOO', 'bar')
        @aruba.cd @directory_name do
          expect(ENV['FOO']).to eq 'bar'
        end
      end

      it 'does not touch other environment variables in the passed block' do
        keys = ENV.keys - %w(PWD OLDPWD)
        old_values = ENV.values_at(*keys)
        @aruba.create_directory @directory_name
        @aruba.cd @directory_name do
          expect(ENV.values_at(*keys)).to eq old_values
        end
      end

      it 'expands "~" to the aruba home directory' do
        full_path = aruba.config.home_directory
        @aruba.cd '~' do
          expect(Dir.pwd).to eq full_path
        end
        expect(Dir.pwd).not_to eq full_path
      end
    end

    context 'with no block given' do
      it "sets aruba's current directory to the given directory" do
        @aruba.create_directory @directory_name
        full_path = File.expand_path(@directory_path)
        @aruba.cd @directory_name
        expect(File.expand_path(@aruba.aruba.current_directory)).to eq full_path
      end

      it 'expands "~" to the aruba home directory' do
        full_path = aruba.config.home_directory
        @aruba.cd '~'
        expect(File.expand_path(@aruba.aruba.current_directory)).to eq full_path
      end
    end
  end

  describe '#expand_path' do
    context 'when file_name is given' do
      it { expect(@aruba.expand_path(@file_name)).to eq File.expand_path(@file_path) }
    end

    context 'when an absolute file_path is given' do
      let(:logger) { @aruba.aruba.logger }

      before do
        allow(@aruba.aruba.logger).to receive :warn
      end

      it { expect(@aruba.expand_path(@file_path)).to eq @file_path }

      it 'warns about it' do
        @aruba.expand_path(@file_path)
        expect(logger).to have_received(:warn)
          .with(/Aruba's `expand_path` method was called with an absolute path/)
      end

      it 'does not warn about it if told not to' do
        @aruba.aruba.config.allow_absolute_paths = true
        @aruba.expand_path(@file_path)
        expect(logger).not_to have_received(:warn)
      end
    end

    context 'when path contains "."' do
      it { expect(@aruba.expand_path('.')).to eq File.expand_path(aruba.current_directory) }
    end

    context 'when path contains ".."' do
      it {
        expect(@aruba.expand_path('path/..'))
          .to eq File.expand_path(File.join(aruba.current_directory))
      }
    end

    context 'when path is nil' do
      it { expect { @aruba.expand_path(nil) }.to raise_error ArgumentError }
    end

    context 'when path is empty' do
      it { expect { @aruba.expand_path('') }.to raise_error ArgumentError }
    end

    context 'when second argument is given' do
      it 'behaves similar to File.expand_path' do
        expect(@aruba.expand_path(@file_name, 'path'))
          .to eq File.expand_path(File.join(aruba.current_directory, 'path', @file_name))
      end
    end

    context 'when file_name contains fixtures "%" string' do
      it 'finds files in the fixtures directory' do
        expect(@aruba.expand_path('%/cli-app'))
          .to eq File.expand_path('cli-app', File.join(aruba.fixtures_directory))
      end
    end
  end

  describe '#in_current_directory' do
    let(:directory_path) { @aruba.aruba.current_directory }
    let!(:full_path) { File.expand_path(directory_path) }

    context 'with a block given' do
      it 'runs the passed block in the given directory' do
        @aruba.in_current_directory do
          expect(Dir.pwd).to eq full_path
        end
        expect(Dir.pwd).not_to eq full_path
      end

      it 'sets directory environment in the passed block' do
        old_pwd = Dir.pwd
        @aruba.in_current_directory do
          expect(ENV['PWD']).to eq full_path
          expect(ENV['OLDPWD']).to eq old_pwd
        end
      end

      it 'sets aruba environment in the passed block' do
        @aruba.set_environment_variable('FOO', 'bar')
        @aruba.in_current_directory do
          expect(ENV['FOO']).to eq 'bar'
        end
      end

      it 'does not touch other environment variables in the passed block' do
        keys = ENV.keys - %w(PWD OLDPWD)
        old_values = ENV.values_at(*keys)
        @aruba.in_current_directory do
          expect(ENV.values_at(*keys)).to eq old_values
        end
      end
    end
  end

  describe '#with_environment' do
    it 'modifies env for block' do
      variable = 'THIS_IS_A_ENV_VAR'
      ENV[variable] = '1'

      @aruba.with_environment variable => '0' do
        expect(ENV[variable]).to eq '0'
      end

      expect(ENV[variable]).to eq '1'
    end

    it 'modifies env for recursive calls block' do
      variable = 'THIS_IS_A_ENV_VAR'
      ENV[variable] = '1'

      @aruba.with_environment variable => '0' do
        @aruba.with_environment do
          expect(ENV[variable]).to eq '0'
        end
      end

      expect(ENV[variable]).to eq '1'
    end

    it 'works together with #set_environment_variable' do
      variable = 'THIS_IS_A_ENV_VAR'
      @aruba.set_environment_variable variable, '1'

      @aruba.with_environment do
        expect(ENV[variable]).to eq '1'
        @aruba.set_environment_variable variable, '0'
        @aruba.with_environment do
          expect(ENV[variable]).to eq '0'
        end
        expect(ENV[variable]).to eq '1'
      end
    end

    it 'works with a mix of ENV and #set_environment_variable' do
      variable = 'THIS_IS_A_ENV_VAR'
      @aruba.set_environment_variable variable, '1'
      ENV[variable] = '2'
      expect(ENV[variable]).to eq '2'

      @aruba.with_environment do
        expect(ENV[variable]).to eq '1'
        @aruba.set_environment_variable variable, '0'
        @aruba.with_environment do
          expect(ENV[variable]).to eq '0'
        end
        expect(ENV[variable]).to eq '1'
      end
      expect(ENV[variable]).to eq '2'
    end

    it 'works with a mix of argument and #set_environment_variable' do
      variable = 'THIS_IS_A_ENV_VAR'

      @aruba.set_environment_variable variable, '1'
      @aruba.with_environment variable => '2' do
        expect(ENV[variable]).to eq '2'
        @aruba.with_environment do
          expect(ENV[variable]).to eq '2'
        end
      end
    end

    it 'works together with #delete_environment_variable' do
      variable = 'THIS_IS_A_ENV_VAR'
      ENV[variable] = '2'
      @aruba.delete_environment_variable variable

      @aruba.with_environment do
        expect(ENV[variable]).to be_nil
      end
      expect(ENV[variable]).to eq '2'
    end

    it 'works with #delete_environment_variable when called several times' do
      variable = 'THIS_IS_A_ENV_VAR'
      ENV[variable] = '2'
      @aruba.delete_environment_variable variable

      @aruba.with_environment do
        expect(ENV[variable]).to be_nil
      end

      @aruba.with_environment do
        expect(ENV[variable]).to be_nil
      end
      expect(ENV[variable]).to eq '2'
    end

    it 'forgets passed argument when called again' do
      variable = 'THIS_IS_A_ENV_VAR'

      @aruba.with_environment variable => 2 do
        expect(ENV[variable]).to eq '2'
      end

      @aruba.with_environment do
        expect(ENV[variable]).to be_nil
      end
    end

    it 'keeps values not set in argument' do
      variable = 'THIS_IS_A_ENV_VAR'
      ENV[variable] = '2'
      expect(ENV[variable]).to eq '2'

      @aruba.with_environment do
        expect(ENV[variable]).to eq '2'
      end
      expect(ENV[variable]).to eq '2'
    end
  end
end
