# frozen_string_literal: true

require 'spec_helper'

describe ReleaseTools::PipelineTracer::Pipeline do
  describe '.from_url' do
    it 'create instance' do
      instance = described_class.from_url('https://ops.gitlab.net/gitlab-org/release/tools/-/pipelines/1590941')

      expect(instance.gitlab_instance).to eq('ops.gitlab.net')
      expect(instance.project).to eq('gitlab-org/release/tools')
      expect(instance.id).to eq('1590941')
    end
  end

  describe '#details' do
    subject(:details) { described_class.new('ops.gitlab.net', 'gitlab-org/release/tools', '1590941').details }

    let(:pipeline_details) { Gitlab::ObjectifiedHash.new({ kind: 'correct' }) }

    before do
      allow(ReleaseTools::GitlabOpsClient).to receive(:pipeline).and_return(pipeline_details)
    end

    it 'calls pipeline API' do
      expect(ReleaseTools::GitlabOpsClient)
        .to receive(:pipeline)
        .with('gitlab-org/release/tools', '1590941')

      expect(details).to eq(pipeline_details)
    end
  end

  describe '#bridge_jobs' do
    subject(:bridge_jobs) { described_class.new('ops.gitlab.net', 'gitlab-org/release/tools', '1590941').bridge_jobs }

    let(:pipeline_bridge_jobs) { [Gitlab::ObjectifiedHash.new({ kind: 'correct' })] }

    before do
      allow(ReleaseTools::GitlabOpsClient).to receive(:pipeline_bridges).and_return(pipeline_bridge_jobs)
    end

    it 'calls pipeline bridge API' do
      expect(ReleaseTools::GitlabOpsClient)
        .to receive(:pipeline_bridges)
        .with('gitlab-org/release/tools', '1590941')

      expect(bridge_jobs).to eq(pipeline_bridge_jobs)
    end
  end

  describe '#jobs' do
    subject(:jobs) { described_class.new('ops.gitlab.net', 'gitlab-org/release/tools', '1590941').jobs }

    let(:pipeline_jobs) { [Gitlab::ObjectifiedHash.new({ kind: 'correct' })] }

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

    it 'calls pipeline jobs API' do
      expect(ReleaseTools::GitlabOpsClient)
        .to receive(:pipeline_jobs)
        .with('gitlab-org/release/tools', '1590941', { include_retried: true, per_page: 100 })

      expect(jobs).to eq(pipeline_jobs)
    end
  end

  describe '#url' do
    subject(:url) { described_class.new('ops.gitlab.net', 'gitlab-org/release/tools', '1590941').url }

    it 'returns URL' do
      expect(url).to eq('https://ops.gitlab.net/gitlab-org/release/tools/-/pipelines/1590941')
    end
  end

  describe '#start_time' do
    subject(:start_time) { described_class.new('ops.gitlab.net', 'gitlab-org/release/tools', '1590941').start_time }

    before do
      allow(ReleaseTools::GitlabOpsClient).to receive(:pipeline).and_return(pipeline_details)
    end

    context 'with started_at available' do
      let(:pipeline_details) { Gitlab::ObjectifiedHash.new({ created_at: '2022-12-07T16:21:00.557Z', started_at: '2022-12-07T16:21:03.514Z' }) }

      it 'returns started_at' do
        expect(ReleaseTools::GitlabOpsClient)
          .to receive(:pipeline)
          .with('gitlab-org/release/tools', '1590941')

        expect(start_time).to eq('2022-12-07T16:21:03.514Z')
      end
    end

    context 'with started_at nil' do
      let(:pipeline_details) { Gitlab::ObjectifiedHash.new({ created_at: '2022-12-07T16:21:00.557Z', started_at: nil }) }

      it 'returns created_at' do
        expect(ReleaseTools::GitlabOpsClient)
          .to receive(:pipeline)
          .with('gitlab-org/release/tools', '1590941')

        expect(start_time).to eq('2022-12-07T16:21:00.557Z')
      end
    end
  end

  describe '#end_time' do
    subject(:end_time) { described_class.new('ops.gitlab.net', 'gitlab-org/release/tools', '1590941').end_time }

    before do
      allow(ReleaseTools::GitlabOpsClient).to receive(:pipeline).and_return(pipeline_details)
    end

    context 'with complete pipeline' do
      let(:pipeline_details) do
        build(
          :pipeline,
          :success,
          {
            updated_at: '2022-12-07T16:21:00.557Z',
            finished_at: '2022-12-07T16:21:03.514Z'
          }
        )
      end

      it 'returns finished_at' do
        expect(ReleaseTools::GitlabOpsClient)
          .to receive(:pipeline)
          .with('gitlab-org/release/tools', '1590941')

        expect(end_time).to eq('2022-12-07T16:21:03.514Z')
      end

      context 'when finished_at is nil' do
        let(:pipeline_details) do
          build(
            :pipeline,
            :success,
            {
              updated_at: '2022-12-07T16:21:00.557Z',
              finished_at: nil
            }
          )
        end

        it 'returns updated_at' do
          expect(ReleaseTools::GitlabOpsClient)
            .to receive(:pipeline)
            .with('gitlab-org/release/tools', '1590941')

          expect(end_time).to eq('2022-12-07T16:21:00.557Z')
        end
      end
    end

    context 'with incomplete pipeline' do
      let(:pipeline_details) do
        build(
          :pipeline,
          :running,
          {
            updated_at: '2022-12-07T16:21:00.557Z',
            finished_at: '2022-12-07T16:21:03.514Z'
          }
        )
      end

      let(:pipeline_jobs) do
        [build(:job, :success, finished_at: '2022-12-07T17:21:03Z'), build(:job, finished_at: nil)]
      end

      let(:pipeline_bridge_jobs) do
        [build(:bridge_job, :success, finished_at: '2022-12-07T18:21:03Z'), build(:bridge_job, finished_at: nil)]
      end

      before do
        allow(ReleaseTools::GitlabOpsClient)
          .to receive_messages(pipeline_jobs: Gitlab::PaginatedResponse.new(pipeline_jobs), pipeline_bridges: Gitlab::PaginatedResponse.new(pipeline_bridge_jobs))
      end

      it 'returns last job finished_at' do
        expect(end_time).to eq(pipeline_bridge_jobs[0].finished_at)
      end

      context 'when there are no bridge jobs' do
        let(:pipeline_bridge_jobs) { [] }

        it 'returns last job finished_at' do
          expect(end_time).to eq(pipeline_jobs[0].finished_at)
        end
      end

      context 'with no complete jobs' do
        let(:pipeline_bridge_jobs) { [build(:bridge_job, :running, finished_at: nil)] }

        let(:pipeline_jobs) do
          [build(:job, :running, finished_at: nil), build(:job, finished_at: nil)]
        end

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

      context 'with ignored job' do
        let(:pipeline_bridge_jobs) { [] }

        let(:pipeline_jobs) do
          [
            build(:job, :success, finished_at: '2022-12-07T17:21:03Z'),
            build(:job, name: 'metrics:trace-pipeline', finished_at: '2022-12-07T18:21:03Z')
          ]
        end

        it 'returns correct finished_at' do
          expect(end_time).to eq(pipeline_jobs[0].finished_at)
        end
      end
    end
  end

  describe '#valid_finished_at?' do
    subject(:pipeline) { described_class.new('ops.gitlab.net', 'gitlab-org/release/tools', '1590941') }

    let(:pipeline_details) { build(:pipeline, status) }

    before do
      allow(ReleaseTools::GitlabOpsClient).to receive(:pipeline).and_return(pipeline_details)
    end

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

      it { is_expected.not_to be_valid_finished_at }
    end

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

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

  describe '#real_time_duration' do
    subject(:real_time_duration) { described_class.new('ops.gitlab.net', 'gitlab-org/release/tools', '1590941').real_time_duration }

    context 'with running pipeline' do
      let(:pipeline_details) do
        build(
          :pipeline,
          :running,
          {
            started_at: '2022-12-07T16:21:00Z',
            finished_at: nil
          }
        )
      end

      let(:pipeline_jobs) do
        [build(:job, :success, finished_at: '2022-12-07T17:21:00Z'), build(:job, finished_at: nil)]
      end

      let(:pipeline_bridge_jobs) do
        [build(:bridge_job, :success, finished_at: '2022-12-07T18:21:00Z'), build(:bridge_job, finished_at: nil)]
      end

      before do
        allow(ReleaseTools::GitlabOpsClient)
          .to receive(:pipeline)
          .with('gitlab-org/release/tools', '1590941')
          .and_return(pipeline_details)

        allow(ReleaseTools::GitlabOpsClient)
          .to receive_messages(pipeline_jobs: Gitlab::PaginatedResponse.new(pipeline_jobs), pipeline_bridges: Gitlab::PaginatedResponse.new(pipeline_bridge_jobs))
      end

      it 'returns duration calculated from latest completed job' do
        expect(real_time_duration).to eq(7200.0)
      end
    end

    context 'with finished_at' do
      let(:pipeline_details) do
        build(
          :pipeline,
          :success,
          {
            started_at: '2022-12-07T16:21:00.557Z',
            finished_at: '2022-12-07T17:21:00.557Z'
          }
        )
      end

      it 'returns duration calculated from finished_at' do
        allow(ReleaseTools::GitlabOpsClient)
          .to receive(:pipeline)
          .with('gitlab-org/release/tools', '1590941')
          .and_return(pipeline_details)

        expect(real_time_duration).to eq(3600)
      end
    end
  end

  describe '#root_attributes' do
    let(:pipeline_details) do
      Gitlab::ObjectifiedHash.new({ "id" => 1_590_941,
       "iid" => 407_638,
       "project_id" => 130,
       "sha" => "b1a943132a61095f6284bba885322fc8a5c71309",
       "ref" => "15.7.202212071620",
       "status" => "success",
       "source" => "push",
       "created_at" => "2022-12-07T16:21:00.557Z",
       "updated_at" => "2022-12-07T22:52:01.402Z",
       "web_url" => "https://ops.gitlab.net/gitlab-org/release/tools/-/pipelines/1590941",
       "before_sha" => "0000000000000000000000000000000000000000",
       "tag" => true,
       "yaml_errors" => nil,
       "user" =>
        { "id" => 49,
         "username" => "gitlab-release-tools-bot",
         "name" => "GitLab Release Tools Bot",
         "state" => "active",
         "avatar_url" => "https://ops.gitlab.net/uploads/-/system/user/avatar/49/avatar.png",
         "web_url" => "https://ops.gitlab.net/gitlab-release-tools-bot" },
       "started_at" => "2022-12-07T16:21:03.514Z",
       "finished_at" => "2022-12-07T22:52:01.395Z",
       "committed_at" => nil,
       "duration" => 1034,
       "queued_duration" => 2,
       "coverage" => nil,
       "detailed_status" =>
        { "icon" => "status_success",
         "text" => "passed",
         "label" => "passed",
         "group" => "success",
         "tooltip" => "passed",
         "has_details" => false,
         "details_path" => "/gitlab-org/release/tools/-/pipelines/1590941",
         "illustration" => nil,
         "favicon" => "/assets/ci_favicons/favicon_status_success-8451333011eee8ce9f2ab25dc487fe24a8758c694827a582f17f42b0a90446a2.png" } })
    end

    subject(:root_attributes) { described_class.new('ops.gitlab.net', 'gitlab-org/release/tools', '1590941').root_attributes }

    before do
      allow(ReleaseTools::GitlabOpsClient).to receive(:pipeline).and_return(pipeline_details)
    end

    context 'with started_at available' do
      it 'returns finished_at' do
        expect(ReleaseTools::GitlabOpsClient)
          .to receive(:pipeline)
          .with('gitlab-org/release/tools', '1590941')

        expect(root_attributes).to eq(
          user: "gitlab-release-tools-bot",
          web_url: "https://ops.gitlab.net/gitlab-org/release/tools/-/pipelines/1590941",
          created_at: "2022-12-07T16:21:00.557Z",
          updated_at: "2022-12-07T22:52:01.402Z",
          status: "success",
          ref: "15.7.202212071620",
          duration: 1034,
          queued_duration: 2,
          started_at: "2022-12-07T16:21:03.514Z",
          finished_at: "2022-12-07T22:52:01.395Z"
        )
      end
    end
  end

  describe 'deploy_environment' do
    subject(:deploy_environment) { described_class.new('ops.gitlab.net', 'gitlab-org/release/tools', '1590941').deploy_environment }

    it 'calls pipeline_variables' do
      expect(ReleaseTools::GitlabOpsClient)
        .to receive(:pipeline_variables)
        .with('gitlab-org/release/tools', '1590941')
        .and_return([build(:gitlab_response, key: 'DEPLOY_ENVIRONMENT', value: 'gstg-cny')])

      expect(deploy_environment).to eq('gstg-cny')
    end

    context 'when DEPLOY_ENVIRONMENT does not exist' do
      it 'calls pipeline_variables' do
        expect(ReleaseTools::GitlabOpsClient)
          .to receive(:pipeline_variables)
          .with('gitlab-org/release/tools', '1590941')
          .and_return([])

        expect(deploy_environment).to be_nil
      end
    end
  end
end
