# == Schema Information
#
# Table name: builds
#
#  id                 :integer          not null, primary key
#  project_id         :integer
#  status             :string(255)
#  finished_at        :datetime
#  trace              :text
#  created_at         :datetime
#  updated_at         :datetime
#  started_at         :datetime
#  runner_id          :integer
#  commit_id          :integer
#  coverage           :float
#  commands           :text
#  job_id             :integer
#  name               :string(255)
#  deploy             :boolean          default(FALSE)
#  options            :text
#  allow_failure      :boolean          default(FALSE), not null
#  stage              :string(255)
#  trigger_request_id :integer
#

require 'spec_helper'

describe Ci::Build do
  let(:project) { FactoryGirl.create :ci_project }
  let(:gl_project) { FactoryGirl.create :empty_project, gitlab_ci_project: project }
  let(:commit) { FactoryGirl.create :ci_commit, gl_project: gl_project }
  let(:build) { FactoryGirl.create :ci_build, commit: commit }

  it { is_expected.to validate_presence_of :ref }

  it { is_expected.to respond_to :trace_html }

  describe :first_pending do
    let(:first) { FactoryGirl.create :ci_build, commit: commit, status: 'pending', created_at: Date.yesterday }
    let(:second) { FactoryGirl.create :ci_build, commit: commit, status: 'pending' }
    before { first; second }
    subject { Ci::Build.first_pending }

    it { is_expected.to be_a(Ci::Build) }
    it('returns with the first pending build') { is_expected.to eq(first) }
  end

  describe :create_from do
    before do
      build.status = 'success'
      build.save
    end
    let(:create_from_build) { Ci::Build.create_from build }

    it 'there should be a pending task' do
      expect(Ci::Build.pending.count(:all)).to eq 0
      create_from_build
      expect(Ci::Build.pending.count(:all)).to be > 0
    end
  end

  describe :ignored? do
    subject { build.ignored? }

    context 'if build is not allowed to fail' do
      before { build.allow_failure = false }

      context 'and build.status is success' do
        before { build.status = 'success' }

        it { is_expected.to be_falsey }
      end

      context 'and build.status is failed' do
        before { build.status = 'failed' }

        it { is_expected.to be_falsey }
      end
    end

    context 'if build is allowed to fail' do
      before { build.allow_failure = true }

      context 'and build.status is success' do
        before { build.status = 'success' }

        it { is_expected.to be_falsey }
      end

      context 'and build.status is failed' do
        before { build.status = 'failed' }

        it { is_expected.to be_truthy }
      end
    end
  end

  describe :trace do
    subject { build.trace_html }

    it { is_expected.to be_empty }

    context 'if build.trace contains text' do
      let(:text) { 'example output' }
      before { build.trace = text }

      it { is_expected.to include(text) }
      it { expect(subject.length).to be >= text.length }
    end

    context 'if build.trace hides token' do
      let(:token) { 'my_secret_token' }

      before do
        build.project.update_attributes(token: token)
        build.update_attributes(trace: token)
      end

      it { is_expected.to_not include(token) }
    end
  end

  describe :timeout do
    subject { build.timeout }

    it { is_expected.to eq(commit.project.timeout) }
  end

  describe :options do
    let(:options) do
      {
        image: "ruby:2.1",
        services: [
          "postgres"
        ]
      }
    end

    subject { build.options }
    it { is_expected.to eq(options) }
  end

  describe :allow_git_fetch do
    subject { build.allow_git_fetch }

    it { is_expected.to eq(project.allow_git_fetch) }
  end

  describe :project do
    subject { build.project }

    it { is_expected.to eq(commit.project) }
  end

  describe :project_id do
    subject { build.project_id }

    it { is_expected.to eq(commit.project_id) }
  end

  describe :project_name do
    subject { build.project_name }

    it { is_expected.to eq(project.name) }
  end

  describe :repo_url do
    subject { build.repo_url }

    it { is_expected.to eq(project.repo_url_with_auth) }
  end

  describe :extract_coverage do
    context 'valid content & regex' do
      subject { build.extract_coverage('Coverage 1033 / 1051 LOC (98.29%) covered', '\(\d+.\d+\%\) covered') }

      it { is_expected.to eq(98.29) }
    end

    context 'valid content & bad regex' do
      subject { build.extract_coverage('Coverage 1033 / 1051 LOC (98.29%) covered', 'very covered') }

      it { is_expected.to be_nil }
    end

    context 'no coverage content & regex' do
      subject { build.extract_coverage('No coverage for today :sad:', '\(\d+.\d+\%\) covered') }

      it { is_expected.to be_nil }
    end

    context 'multiple results in content & regex' do
      subject { build.extract_coverage(' (98.39%) covered. (98.29%) covered', '\(\d+.\d+\%\) covered') }

      it { is_expected.to eq(98.29) }
    end
  end

  describe :variables do
    context 'returns variables' do
      subject { build.variables }

      let(:predefined_variables) do
        [
          { key: :CI_BUILD_NAME, value: 'test', public: true },
          { key: :CI_BUILD_STAGE, value: 'stage', public: true },
        ]
      end

      let(:yaml_variables) do
        [
          { key: :DB_NAME, value: 'postgres', public: true }
        ]
      end

      before { build.update_attributes(stage: 'stage') }

      it { is_expected.to eq(predefined_variables + yaml_variables) }

      context 'for tag' do
        let(:tag_variable) do
          [
            { key: :CI_BUILD_TAG, value: 'master', public: true }
          ]
        end

        before { build.update_attributes(tag: true) }

        it { is_expected.to eq(tag_variable + predefined_variables + yaml_variables) }
      end

      context 'and secure variables' do
        let(:secure_variables) do
          [
            { key: 'SECRET_KEY', value: 'secret_value', public: false }
          ]
        end

        before do
          build.project.variables << Ci::Variable.new(key: 'SECRET_KEY', value: 'secret_value')
        end

        it { is_expected.to eq(predefined_variables + yaml_variables + secure_variables) }

        context 'and trigger variables' do
          let(:trigger) { FactoryGirl.create :ci_trigger, project: project }
          let(:trigger_request) { FactoryGirl.create :ci_trigger_request_with_variables, commit: commit, trigger: trigger }
          let(:trigger_variables) do
            [
              { key: :TRIGGER_KEY, value: 'TRIGGER_VALUE', public: false }
            ]
          end
          let(:predefined_trigger_variable) do
            [
              { key: :CI_BUILD_TRIGGERED, value: 'true', public: true }
            ]
          end

          before do
            build.trigger_request = trigger_request
          end

          it { is_expected.to eq(predefined_variables + predefined_trigger_variable + yaml_variables + secure_variables + trigger_variables) }
        end
      end
    end
  end

  describe :project_recipients do
    let(:pusher_email) { 'pusher@gitlab.test' }
    let(:user) { User.new(notification_email: pusher_email) }
    subject { build.project_recipients }

    before do
      build.update_attributes(user: user)
    end

    it 'should return pusher_email as only recipient when no additional recipients are given' do
      project.update_attributes(email_add_pusher: true,
                                email_recipients: '')
      is_expected.to eq([pusher_email])
    end

    it 'should return pusher_email and additional recipients' do
      project.update_attributes(email_add_pusher: true,
                                email_recipients: 'rec1 rec2')
      is_expected.to eq(['rec1', 'rec2', pusher_email])
    end

    it 'should return recipients' do
      project.update_attributes(email_add_pusher: false,
                                email_recipients: 'rec1 rec2')
      is_expected.to eq(['rec1', 'rec2'])
    end

    it 'should return unique recipients only' do
      project.update_attributes(email_add_pusher: true,
                                email_recipients: "rec1 rec1 #{pusher_email}")
      is_expected.to eq(['rec1', pusher_email])
    end
  end

  describe :can_be_served? do
    let(:runner) { FactoryGirl.create :ci_specific_runner }

    before { build.project.runners << runner }

    context 'runner without tags' do
      it 'can handle builds without tags' do
        expect(build.can_be_served?(runner)).to be_truthy
      end

      it 'cannot handle build with tags' do
        build.tag_list = ['aa']
        expect(build.can_be_served?(runner)).to be_falsey
      end
    end

    context 'runner with tags' do
      before { runner.tag_list = ['bb', 'cc'] }

      it 'can handle builds without tags' do
        expect(build.can_be_served?(runner)).to be_truthy
      end

      it 'can handle build with matching tags' do
        build.tag_list = ['bb']
        expect(build.can_be_served?(runner)).to be_truthy
      end

      it 'cannot handle build with not matching tags' do
        build.tag_list = ['aa']
        expect(build.can_be_served?(runner)).to be_falsey
      end
    end
  end

  describe :any_runners_online? do
    subject { build.any_runners_online? }

    context 'when no runners' do
      it { is_expected.to be_falsey }
    end

    context 'if there are runner' do
      let(:runner) { FactoryGirl.create :ci_specific_runner }

      before do
        build.project.runners << runner
        runner.update_attributes(contacted_at: 1.second.ago)
      end

      it { is_expected.to be_truthy }

      it 'that is inactive' do
        runner.update_attributes(active: false)
        is_expected.to be_falsey
      end

      it 'that is not online' do
        runner.update_attributes(contacted_at: nil)
        is_expected.to be_falsey
      end

      it 'that cannot handle build' do
        expect_any_instance_of(Ci::Build).to receive(:can_be_served?).and_return(false)
        is_expected.to be_falsey
      end

    end
  end

  describe :show_warning? do
    subject { build.show_warning? }

    %w(pending).each do |state|
      context "if commit_status.status is #{state}" do
        before { build.status = state }

        it { is_expected.to be_truthy }

        context "and there are specific runner" do
          let(:runner) { FactoryGirl.create :ci_specific_runner, contacted_at: 1.second.ago }

          before do
            build.project.runners << runner
            runner.save
          end

          it { is_expected.to be_falsey }
        end
      end
    end

    %w(success failed canceled running).each do |state|
      context "if commit_status.status is #{state}" do
        before { build.status = state }

        it { is_expected.to be_falsey }
      end
    end
  end

  describe :download_url do
    subject { build.download_url }

    it "should be nil if artifact doesn't exist" do
      build.update_attributes(artifacts_file: nil)
      is_expected.to be_nil
    end

    it 'should be nil if artifact exist' do
      gif = fixture_file_upload(Rails.root + 'spec/fixtures/banana_sample.gif', 'image/gif')
      build.update_attributes(artifacts_file: gif)
      is_expected.to_not be_nil
    end
  end
end