# frozen_string_literal: true

require 'spec_helper'

describe Gitlab::Ci::Config::External::Processor do
  let(:project) { create(:project, :repository) }
  let(:processor) { described_class.new(values, project, '12345') }

  describe "#perform" do
    context 'when no external files defined' do
      let(:values) { { image: 'ruby:2.2' } }

      it 'should return the same values' do
        expect(processor.perform).to eq(values)
      end
    end

    context 'when an invalid local file is defined' do
      let(:values) { { include: '/lib/gitlab/ci/templates/non-existent-file.yml', image: 'ruby:2.2' } }

      it 'should raise an error' do
        expect { processor.perform }.to raise_error(
          described_class::IncludeError,
          "Local file `/lib/gitlab/ci/templates/non-existent-file.yml` does not exist!"
        )
      end
    end

    context 'when an invalid remote file is defined' do
      let(:remote_file) { 'http://doesntexist.com/.gitlab-ci-1.yml' }
      let(:values) { { include: remote_file, image: 'ruby:2.2' } }

      before do
        WebMock.stub_request(:get, remote_file).to_raise(SocketError.new('Some HTTP error'))
      end

      it 'should raise an error' do
        expect { processor.perform }.to raise_error(
          described_class::IncludeError,
          "Remote file `#{remote_file}` could not be fetched because of a socket error!"
        )
      end
    end

    context 'with a valid remote external file is defined' do
      let(:remote_file) { 'https://gitlab.com/gitlab-org/gitlab-ce/blob/1234/.gitlab-ci-1.yml' }
      let(:values) { { include: remote_file, image: 'ruby:2.2' } }
      let(:external_file_content) do
        <<-HEREDOC
        before_script:
          - apt-get update -qq && apt-get install -y -qq sqlite3 libsqlite3-dev nodejs
          - ruby -v
          - which ruby
          - gem install bundler --no-ri --no-rdoc
          - bundle install --jobs $(nproc)  "${FLAGS[@]}"

        rspec:
          script:
            - bundle exec rspec

        rubocop:
          script:
            - bundle exec rubocop
        HEREDOC
      end

      before do
        WebMock.stub_request(:get, remote_file).to_return(body: external_file_content)
      end

      it 'should append the file to the values' do
        output = processor.perform
        expect(output.keys).to match_array([:image, :before_script, :rspec, :rubocop])
      end

      it "should remove the 'include' keyword" do
        expect(processor.perform[:include]).to be_nil
      end
    end

    context 'with a valid local external file is defined' do
      let(:values) { { include: '/lib/gitlab/ci/templates/template.yml', image: 'ruby:2.2' } }
      let(:local_file_content) do
        <<-HEREDOC
        before_script:
          - apt-get update -qq && apt-get install -y -qq sqlite3 libsqlite3-dev nodejs
          - ruby -v
          - which ruby
          - gem install bundler --no-ri --no-rdoc
          - bundle install --jobs $(nproc)  "${FLAGS[@]}"
        HEREDOC
      end

      before do
        allow_any_instance_of(Gitlab::Ci::Config::External::File::Local)
          .to receive(:fetch_local_content).and_return(local_file_content)
      end

      it 'should append the file to the values' do
        output = processor.perform
        expect(output.keys).to match_array([:image, :before_script])
      end

      it "should remove the 'include' keyword" do
        expect(processor.perform[:include]).to be_nil
      end
    end

    context 'with multiple external files are defined' do
      let(:remote_file) { 'https://gitlab.com/gitlab-org/gitlab-ce/blob/1234/.gitlab-ci-1.yml' }
      let(:external_files) do
        [
          '/spec/fixtures/gitlab/ci/external_files/.gitlab-ci-template-1.yml',
          remote_file
        ]
      end
      let(:values) do
        {
          include: external_files,
          image: 'ruby:2.2'
        }
      end

      let(:remote_file_content) do
        <<-HEREDOC
        stages:
          - build
          - review
          - cleanup
        HEREDOC
      end

      before do
        local_file_content = File.read(Rails.root.join('spec/fixtures/gitlab/ci/external_files/.gitlab-ci-template-1.yml'))

        allow_any_instance_of(Gitlab::Ci::Config::External::File::Local)
          .to receive(:fetch_local_content).and_return(local_file_content)

        WebMock.stub_request(:get, remote_file).to_return(body: remote_file_content)
      end

      it 'should append the files to the values' do
        expect(processor.perform.keys).to match_array([:image, :stages, :before_script, :rspec])
      end

      it "should remove the 'include' keyword" do
        expect(processor.perform[:include]).to be_nil
      end
    end

    context 'when external files are defined but not valid' do
      let(:values) { { include: '/lib/gitlab/ci/templates/template.yml', image: 'ruby:2.2' } }

      let(:local_file_content) { 'invalid content file ////' }

      before do
        allow_any_instance_of(Gitlab::Ci::Config::External::File::Local)
          .to receive(:fetch_local_content).and_return(local_file_content)
      end

      it 'should raise an error' do
        expect { processor.perform }.to raise_error(
          described_class::IncludeError,
          "Included file `/lib/gitlab/ci/templates/template.yml` does not have valid YAML syntax!"
        )
      end
    end

    context "when both external files and values defined the same key" do
      let(:remote_file) { 'https://gitlab.com/gitlab-org/gitlab-ce/blob/1234/.gitlab-ci-1.yml' }
      let(:values) do
        {
          include: remote_file,
          image: 'ruby:2.2'
        }
      end

      let(:remote_file_content) do
        <<~HEREDOC
        image: php:5-fpm-alpine
        HEREDOC
      end

      it 'should take precedence' do
        WebMock.stub_request(:get, remote_file).to_return(body: remote_file_content)
        expect(processor.perform[:image]).to eq('ruby:2.2')
      end
    end
  end
end
