# frozen_string_literal: true

require 'spec_helper'

describe ReleaseTools::Deployments::DeploymentTracker do
  describe '#commit_range' do
    let(:version) { '12.7.202001101501-94b8fd8d152.6fea3031ec9' }

    context 'when deploying to a QA environment with deployments' do
      it 'returns the current and previous deployment SHAs' do
        tracker = described_class.new(version: version)

        allow(ReleaseTools::GitlabClient)
          .to receive(:deployments)
          .and_return([
            double(:deployment, sha: 'bar'),
            double(:deployment, sha: 'foo')
          ])

        expect(tracker.commit_range).to eq(%w[foo bar])
      end
    end

    context 'when deploying to a QA environment with for the first time' do
      it 'does not return the previous deployment SHA' do
        tracker = described_class.new(version: version)

        allow(ReleaseTools::GitlabClient)
          .to receive(:deployments)
          .and_return([])

        expect(tracker.commit_range).to eq([])
      end
    end

    context 'when deploying to a non-QA environment' do
      it 'returns an empty Array' do
        tracker = described_class.new(version: version)

        allow(ReleaseTools::GitlabClient)
          .to receive(:deployments)
          .and_return([])

        expect(tracker.commit_range).to be_empty
      end
    end
  end

  describe '#track' do
    context 'when using a valid status' do
      let(:version) { '12.7.202001101501-94b8fd8d152.6fea3031ec9' }
      let(:product_version) { ReleaseTools::ProductVersion.from_package_version(version) }
      let(:product_meta) do
        {
          'releases' => {
            'gitlab-ee' => {
              'ref' => '1-2-auto-deploy-01022020',
              'sha' => '123',
              'tag' => false
            },
            'omnibus-gitlab-ee' => {
              'ref' => '1-2-auto-deploy-01022020',
              'sha' => '456',
              'tag' => false
            },
            'gitaly' => {
              'sha' => '94b8fd8d152680445ec14241f14d1e4c04b0b5ab',
              'ref' => 'master',
              'tag' => false
            },
            'gitlab_kas' => {
              'ref' => 'master',
              'sha' => '5bbaac6e3d907fba9568a2e36aa1e521f589c897',
              'tag' => false
            }
          }
        }
      end

      before do
        allow(ReleaseTools::ProductVersion)
          .to receive(:from_package_version)
          .and_return(product_version)

        allow(product_version)
          .to receive(:metadata)
          .and_return(product_meta)

        # We test the logging of previous deployments separately.
        allow(ReleaseTools::GitlabClient)
          .to receive(:last_successful_deployment)
      end

      it 'tracks the deployment of GitLab, Gitaly, kas, and Omnibus GitLab in 2 repositories' do
        tracker = described_class.new(environment: 'staging', status: 'running', version: version)
        allow(tracker).to receive(:log_previous_deployment)

        # Verify the Security deploys on the auto-deploy branch

        expect(ReleaseTools::GitlabClient)
          .to receive(:update_or_create_deployment)
          .with(
            ReleaseTools::Project::GitlabEe.auto_deploy_path,
            'staging',
            {
              ref: '1-2-auto-deploy-01022020',
              sha: '123',
              status: 'running',
              tag: false
            }
          )
          .and_return(build(:deployment, id: 1, sha: '123'))

        expect(ReleaseTools::GitlabClient)
          .to receive(:update_or_create_deployment)
          .with(
            ReleaseTools::Project::Gitaly.auto_deploy_path,
            'staging',
            {
              ref: 'master', # Gitaly will always use `master`
              sha: '94b8fd8d152680445ec14241f14d1e4c04b0b5ab',
              status: 'running',
              tag: false
            }
          )
          .and_return(build(:deployment, id: 2, sha: '94b8fd8d152680445ec14241f14d1e4c04b0b5ab'))

        expect(ReleaseTools::GitlabClient)
          .to receive(:update_or_create_deployment)
          .with(
            ReleaseTools::Project::OmnibusGitlab.auto_deploy_path,
            'staging',
            {
              ref: '1-2-auto-deploy-01022020',
              sha: '456',
              status: 'running',
              tag: false
            }
          )
          .and_return(build(:deployment, id: 3, sha: '456'))

        expect(ReleaseTools::GitlabClient)
          .to receive(:update_or_create_deployment)
          .with(
            ReleaseTools::Project::Kas.auto_deploy_path,
            'staging',
            {
              ref: 'master',
              sha: '5bbaac6e3d907fba9568a2e36aa1e521f589c897',
              status: 'running',
              tag: false
            }
          )
          .and_return(build(:deployment, id: 4, sha: '5bbaac6e3d907fba9568a2e36aa1e521f589c897'))

        # Verify the Canonical deploys on the `master` branch

        expect(tracker).to receive(:auto_deploy_intersection)
          .with(ReleaseTools::Project::GitlabEe, '123')
          .and_return('abcde')

        expect(tracker).to receive(:auto_deploy_intersection)
          .with(ReleaseTools::Project::Gitaly, '94b8fd8d152680445ec14241f14d1e4c04b0b5ab')
          .and_return('94b8fd8d152680445ec14241f14d1e4c04b0b5ab')

        expect(tracker).to receive(:auto_deploy_intersection)
          .with(ReleaseTools::Project::OmnibusGitlab, '456')
          .and_return('fghij')

        expect(tracker).to receive(:auto_deploy_intersection)
          .with(ReleaseTools::Project::Kas, '5bbaac6e3d907fba9568a2e36aa1e521f589c897')
          .and_return('5bbaac6e3d907fba9568a2e36aa1e521f589c897')

        expect(ReleaseTools::GitlabClient)
          .to receive(:update_or_create_deployment)
          .with(
            ReleaseTools::Project::GitlabEe.path,
            'staging',
            {
              ref: 'master',
              sha: 'abcde',
              status: 'running',
              tag: false
            }
          )
          .and_return(build(:deployment, id: 5, sha: 'abcde'))

        expect(ReleaseTools::GitlabClient)
          .to receive(:update_or_create_deployment)
          .with(
            ReleaseTools::Project::Gitaly.path,
            'staging',
            {
              ref: 'master',
              sha: '94b8fd8d152680445ec14241f14d1e4c04b0b5ab',
              status: 'running',
              tag: false
            }
          )
          .and_return(build(:deployment, id: 6, sha: '94b8fd8d152680445ec14241f14d1e4c04b0b5ab'))

        expect(ReleaseTools::GitlabClient)
          .to receive(:update_or_create_deployment)
          .with(
            ReleaseTools::Project::OmnibusGitlab.path,
            'staging',
            {
              ref: 'master',
              sha: 'fghij',
              status: 'running',
              tag: false
            }
          )
          .and_return(build(:deployment, id: 7, sha: 'fghij'))

        expect(ReleaseTools::GitlabClient)
          .to receive(:update_or_create_deployment)
          .with(
            ReleaseTools::Project::Kas.path,
            'staging',
            {
              ref: 'master',
              sha: '5bbaac6e3d907fba9568a2e36aa1e521f589c897',
              status: 'running',
              tag: false
            }
          )
          .and_return(build(:deployment, id: 8, sha: '5bbaac6e3d907fba9568a2e36aa1e521f589c897'))

        deployments = without_dry_run { tracker.track }

        expect(deployments.length).to eq(8)
        expect(deployments.collect(&:id)).to contain_exactly(1, 2, 3, 4, 5, 6, 7, 8)
        expect(deployments.collect(&:project_path)).to include(
          ReleaseTools::Project::GitlabEe.auto_deploy_path,
          ReleaseTools::Project::GitlabEe.path
        )
      end

      it 'does not create a canonical deployment when no canonical commit is found' do
        tracker = described_class.new(environment: 'staging', status: 'success', version: version)

        expect(ReleaseTools::GitlabClient)
          .to receive(:update_or_create_deployment)
          .with(
            ReleaseTools::Project::GitlabEe.auto_deploy_path,
            'staging',
            {
              ref: '1-2-auto-deploy-01022020',
              sha: '123',
              status: 'success',
              tag: false
            }
          )
          .and_return(build(:deployment, id: 1, sha: '123'))

        expect(ReleaseTools::GitlabClient)
          .to receive(:update_or_create_deployment)
          .with(
            ReleaseTools::Project::Gitaly.auto_deploy_path,
            'staging',
            {
              ref: 'master', # Gitaly will always use `master`
              sha: '94b8fd8d152680445ec14241f14d1e4c04b0b5ab',
              status: 'success',
              tag: false
            }
          )
          .and_return(build(:deployment, id: 2, sha: '94b8fd8d152680445ec14241f14d1e4c04b0b5ab'))

        expect(ReleaseTools::GitlabClient)
          .to receive(:update_or_create_deployment)
          .with(
            ReleaseTools::Project::OmnibusGitlab.auto_deploy_path,
            'staging',
            {
              ref: '1-2-auto-deploy-01022020',
              sha: '456',
              status: 'success',
              tag: false
            }
          )
          .and_return(build(:deployment, id: 3, sha: '456'))

        expect(ReleaseTools::GitlabClient)
          .to receive(:update_or_create_deployment)
          .with(
            ReleaseTools::Project::Kas.auto_deploy_path,
            'staging',
            {
              ref: 'master',
              sha: '5bbaac6e3d907fba9568a2e36aa1e521f589c897',
              status: 'success',
              tag: false
            }
          )
          .and_return(build(:deployment, id: 4, sha: '5bbaac6e3d907fba9568a2e36aa1e521f589c897'))

        expect(tracker).to receive(:auto_deploy_intersection)
          .and_return(nil)
          .exactly(4).times

        deployments = without_dry_run { tracker.track }

        expect(deployments.length).to eq(4)
        expect(deployments.collect(&:id)).to contain_exactly(1, 2, 3, 4)
      end
    end

    context 'when using an invalid status' do
      it 'raises ArgumentError' do
        version = '12.7.202001101501-94b8fd8d152.6fea3031ec9'

        expect { described_class.new('staging', 'foo', version).track }
          .to raise_error(ArgumentError)
      end
    end
  end

  describe '.record_metadata_deployment' do
    context 'when using a valid status' do
      let(:version) { '12.7.202001101501-94b8fd8d152.6fea3031ec9' }
      let(:product_version) { ReleaseTools::ProductVersion.from_package_version(version) }

      before do
        allow(ReleaseTools::ProductVersion)
          .to receive(:from_package_version)
          .and_return(product_version)
      end

      it 'tracks the deployment on release/metadata' do
        tracker = described_class.new(environment: 'staging', status: 'running', version: version)

        metadata_commit_id = '123abc'
        allow(product_version).to receive(:metadata_commit_id).and_return(metadata_commit_id)

        expect(ReleaseTools::GitlabOpsClient)
          .to receive(:update_or_create_deployment)
          .with(
            ReleaseTools::Project::Release::Metadata,
            'staging',
            {
              ref: 'master',
              sha: metadata_commit_id,
              status: 'running'
            }
          )

        without_dry_run { tracker.record_metadata_deployment }
      end
    end

    context 'when using an invalid status' do
      it 'raises ArgumentError' do
        version = '12.7.202001101501-94b8fd8d152.6fea3031ec9'

        expect { described_class.new('staging', 'foo', version).record_metadata_deployment }
          .to raise_error(ArgumentError)
      end
    end
  end

  describe '#log_new_deployment' do
    it 'logs a new deployment' do
      deploy = double(:deployment, id: 1, iid: 1, ref: 'master', sha: 'abc')
      version = '12.7.202001101501-94b8fd8d152.6fea3031ec9'
      tracker = described_class.new(environment: 'staging', status: 'success', version: version)

      expect(tracker.logger).to receive(:log)

      tracker.send(:log_new_deployment, 'foo/bar', deploy)
    end
  end

  describe '#log_previous_deployment' do
    it 'logs the previous deployment deployment' do
      deploy = double(:deployment, id: 1, iid: 1, ref: 'master', sha: 'abc')
      version = '12.7.202001101501-94b8fd8d152.6fea3031ec9'
      tracker = described_class.new(environment: 'staging', status: 'success', version: version)

      expect(ReleaseTools::GitlabClient)
        .to receive(:last_successful_deployment)
        .with('foo/bar', 'staging')
        .and_return(deploy)

      expect(tracker.logger).to receive(:log)

      tracker.send(:log_previous_deployment, 'foo/bar')
    end
  end
end
