# frozen_string_literal: true

require 'spec_helper'

describe ReleaseTools::PipelineTracer::Job do
  let(:job) do
    build(
      :job,
      name: job_name,
      allow_failure: true,
      started_at: '2022-12-07T22:51:42.229Z',
      finished_at: '2022-12-07T22:52:01.299Z',
      duration: 19.069423,
      queued_duration: 0.60046,
      web_url: 'https://ops.gitlab.net/gitlab-org/release/tools/-/jobs/8694895'
    )
  end

  let(:job_name) { 'job1' }

  describe '#completed?' do
    subject(:job) { described_class.new(job_attributes, ReleaseTools::GitlabOpsClient) }

    let(:job_attributes) { Gitlab::ObjectifiedHash.new(status: status) }

    context 'with non completed status' do
      let(:status) { 'running' }

      it { is_expected.not_to be_completed }
    end

    %w[success failed canceled].each do |status|
      let(:status) { status }

      context "with #{status} status" do
        it { is_expected.to be_completed }
      end
    end
  end

  describe '#root_attributes' do
    subject(:root_attributes) { described_class.new(job, ReleaseTools::GitlabOpsClient).root_attributes }

    let(:job_attributes) do
      {
        job_name: job.name,
        id: job.id,
        web_url: job.web_url,
        allow_failure: job.allow_failure,
        user: job.user.username,
        stage: job.stage,
        started_at: job.started_at,
        finished_at: job.finished_at,
        status: job.status,
        duration: job.duration,
        queued_duration: job.queued_duration
      }.stringify_keys
    end

    it 'returns root attributes' do
      expect(root_attributes).to eq(job_attributes)
    end
  end

  describe '#trace' do
    subject(:trace) { described_class.new(job, ReleaseTools::GitlabOpsClient).trace }

    let(:job_log) { 'correct' }

    before do
      allow(ReleaseTools::GitlabOpsClient).to receive(:job_trace).and_return(job_log)
      allow(ReleaseTools::GitlabDevClient).to receive(:job_trace).and_return(job_log)
      allow(ReleaseTools::GitlabClient).to receive(:job_trace).and_return(job_log)
    end

    it 'calls job_trace API' do
      expect(ReleaseTools::GitlabOpsClient)
        .to receive(:job_trace)
        .with(job.pipeline.project_id, job.id)

      expect(trace).to eq(job_log)
    end
  end

  describe '#triggered_pipeline_url' do
    subject(:triggered_pipeline_url) { described_class.new(job, ReleaseTools::GitlabOpsClient).triggered_pipeline_url }

    let(:job_log) { instance_double(String, match: nil) }
    let(:job_name) { 'wait:cng' }

    before do
      allow(ReleaseTools::GitlabOpsClient).to receive(:job_trace).and_return(job_log)
    end

    it 'memoizes nil return value' do
      expect(job_log).to receive(:match).once

      instance = described_class.new(job, ReleaseTools::GitlabOpsClient)

      instance.triggered_pipeline_url
      expect(instance.triggered_pipeline_url).to be_nil
    end

    it 'memoizes non-nil return value' do
      allow(job_log).to receive(:match).and_return({ pipeline_url: 'url' })

      expect(job_log).to receive(:match).once

      instance = described_class.new(job, ReleaseTools::GitlabOpsClient)

      instance.triggered_pipeline_url
      expect(instance.triggered_pipeline_url).to eq('url')
    end

    context 'with different job name' do
      let(:job_name) { 'job1' }

      it 'ignores jobs whose names are not in trigger jobs list' do
        expect(ReleaseTools::GitlabOpsClient).not_to receive(:job_trace)

        expect(triggered_pipeline_url).to be_nil
      end
    end

    context 'with log message from wait:cng' do
      let(:job_log) { 'Checking status of pipeline -- {pipeline: "https://dev.gitlab.org/gitlab/omnibus-gitlab/-/pipelines/361107"}' }

      it 'returns pipeline URL' do
        expect(triggered_pipeline_url).to eq('https://dev.gitlab.org/gitlab/omnibus-gitlab/-/pipelines/361107')
      end
    end
  end

  describe '#triggered_downstream_pipeline?' do
    subject(:instance) { described_class.new(job, ReleaseTools::GitlabOpsClient) }

    let(:triggered_url) { nil }
    let(:job_name) { 'wait:cng' }

    before do
      instance.instance_variable_set(:@triggered_pipeline_url, triggered_url)
    end

    context 'when triggered_pipeline_url is nil' do
      let(:triggered_url) { nil }

      it 'returns false' do
        expect(instance.triggered_downstream_pipeline?).to be(false)
      end
    end

    context 'when triggered_pipeline_url is not nil' do
      let(:triggered_url) { 'url' }

      it 'returns false' do
        expect(instance.triggered_downstream_pipeline?).to be(true)
      end
    end
  end

  describe '#real_time_duration' do
    subject(:instance) { described_class.new(job, ReleaseTools::GitlabOpsClient) }

    it 'returns job duration' do
      expect(instance.real_time_duration).to eq(job.duration)
    end
  end

  describe '#environment_from_name' do
    subject(:environment_from_name) { described_class.new(job, ReleaseTools::GitlabOpsClient).environment_from_name }

    described_class::VALID_ENVIRONMENTS.each do |env|
      context "with #{env} in name" do
        let(:job_name) { "qa:smoke-main:#{env}" }

        it 'returns environment derived from name' do
          expect(environment_from_name).to eq(env)
        end
      end
    end

    context 'when name does not contain environment' do
      let(:job_name) { 'qa:smoke-main' }

      it 'returns nil' do
        expect(environment_from_name).to be_nil
      end
    end

    context 'when environment is in beginning of name' do
      let(:job_name) { 'gstg-cny-prepare' }

      it 'returns environment derived from name' do
        expect(environment_from_name).to eq('gstg-cny')
      end
    end
  end

  describe '#name_without_environment' do
    subject(:name_without_environment) { described_class.new(job, ReleaseTools::GitlabOpsClient).name_without_environment }

    let(:job_name) { 'qa:smoke-main:gstg-cny' }

    it 'returns job name without environment' do
      expect(name_without_environment).to eq('qa:smoke-main:')
    end

    context 'with no environment in name' do
      let(:job_name) { 'qa:smoke-main' }

      it 'returns name' do
        expect(name_without_environment).to eq(job_name)
      end
    end
  end
end
