update_deployment_service_spec.rb 6.62 KB
Newer Older
1 2
require 'spec_helper'

3
describe UpdateDeploymentService do
4
  let(:user) { create(:user) }
5
  let(:options) { { name: 'production' } }
6 7 8 9 10 11

  let(:job) do
    create(:ci_build,
      ref: 'master',
      tag: false,
      environment: 'production',
12 13
      options: { environment: options },
      project: project)
14 15
  end

16 17 18 19
  let(:project) { create(:project, :repository) }
  let(:environment) { deployment.environment }
  let(:deployment) { job.deployment }
  let(:service) { described_class.new(deployment) }
20

Lin Jen-Shin's avatar
Lin Jen-Shin committed
21
  before do
22
    job.success! # Create/Succeed deployment
Lin Jen-Shin's avatar
Lin Jen-Shin committed
23 24
  end

25 26 27
  describe '#execute' do
    subject { service.execute }

28 29 30 31 32 33 34 35
    let(:store) { Gitlab::EtagCaching::Store.new }

    it 'invalidates the environment etag cache' do
      old_value = store.get(environment.etag_cache_key)

      subject

      expect(store.get(environment.etag_cache_key)).not_to eq(old_value)
36 37
    end

38 39 40 41
    it 'creates ref' do
      expect_any_instance_of(Repository)
        .to receive(:create_ref)
        .with(deployment.ref, deployment.send(:ref_path))
42

43 44 45 46 47 48 49 50
      subject
    end

    it 'updates merge request metrics' do
      expect_any_instance_of(Deployment)
        .to receive(:update_merge_request_metrics!)

      subject
51
    end
Kamil Trzcinski's avatar
Kamil Trzcinski committed
52

53
    context 'when start action is defined' do
54
      let(:options) { { name: 'production', action: 'start' } }
Kamil Trzcinski's avatar
Kamil Trzcinski committed
55

56 57 58 59
      context 'and environment is stopped' do
        before do
          environment.stop
        end
Kamil Trzcinski's avatar
Kamil Trzcinski committed
60

61 62
        it 'makes environment available' do
          subject
Kamil Trzcinski's avatar
Kamil Trzcinski committed
63

64 65
          expect(environment.reload).to be_available
        end
Kamil Trzcinski's avatar
Kamil Trzcinski committed
66
      end
67
    end
Kamil Trzcinski's avatar
Kamil Trzcinski committed
68

69
    context 'when variables are used' do
70 71 72 73 74 75
      let(:options) do
        { name: 'review-apps/$CI_COMMIT_REF_NAME',
          url: 'http://$CI_COMMIT_REF_NAME.review-apps.gitlab.com' }
      end

      before do
76
        environment.update(name: 'review-apps/master')
77
        job.update(environment: 'review-apps/$CI_COMMIT_REF_NAME')
78 79
      end

80 81 82
      it 'does not create a new environment' do
        expect { subject }.not_to change { Environment.count }
      end
Kamil Trzcinski's avatar
Kamil Trzcinski committed
83

84 85
      it 'updates external url' do
        subject
Kamil Trzcinski's avatar
Kamil Trzcinski committed
86

87 88
        expect(subject.environment.name).to eq('review-apps/master')
        expect(subject.environment.external_url).to eq('http://master.review-apps.gitlab.com')
Kamil Trzcinski's avatar
Kamil Trzcinski committed
89
      end
90
    end
91
  end
92

93 94 95 96 97 98 99
  describe '#expanded_environment_url' do
    subject { service.send(:expanded_environment_url) }

    context 'when yaml environment uses $CI_COMMIT_REF_NAME' do
      let(:job) do
        create(:ci_build,
               ref: 'master',
100 101 102
               environment: 'production',
               project: project,
               options: { environment: { name: 'production', url: 'http://review/$CI_COMMIT_REF_NAME' } })
103 104 105 106 107
      end

      it { is_expected.to eq('http://review/master') }
    end

108 109 110 111
    context 'when yaml environment uses $CI_ENVIRONMENT_SLUG' do
      let(:job) do
        create(:ci_build,
               ref: 'master',
112 113 114
               environment: 'prod-slug',
               project: project,
               options: { environment: { name: 'prod-slug', url: 'http://review/$CI_ENVIRONMENT_SLUG' } })
115 116 117 118 119
      end

      it { is_expected.to eq('http://review/prod-slug') }
    end

120 121 122 123
    context 'when yaml environment uses yaml_variables containing symbol keys' do
      let(:job) do
        create(:ci_build,
               yaml_variables: [{ key: :APP_HOST, value: 'host' }],
124 125 126
               environment: 'production',
               project: project,
               options: { environment: { name: 'production', url: 'http://review/$APP_HOST' } })
127 128 129 130 131 132
      end

      it { is_expected.to eq('http://review/host') }
    end

    context 'when yaml environment does not have url' do
133
      let(:job) { create(:ci_build, environment: 'staging', project: project) }
134 135

      it 'returns the external_url from persisted environment' do
136
        is_expected.to be_nil
137 138 139 140
      end
    end
  end

141 142 143 144
  describe "merge request metrics" do
    let(:merge_request) { create(:merge_request, target_branch: 'master', source_branch: 'feature', source_project: project) }

    context "while updating the 'first_deployed_to_production_at' time" do
145
      before do
146
        merge_request.metrics.update!(merged_at: 1.hour.ago)
147
      end
148 149 150

      context "for merge requests merged before the current deploy" do
        it "sets the time if the deploy's environment is 'production'" do
151
          service.execute
152

153
          expect(merge_request.reload.metrics.first_deployed_to_production_at).to be_like_time(deployment.finished_at)
154 155
        end

156 157 158 159 160 161 162 163 164 165 166 167
        context 'when job deploys to staging' do
          let(:job) do
            create(:ci_build,
              ref: 'master',
              tag: false,
              environment: 'staging',
              options: { environment: { name: 'staging' } },
              project: project)
          end

          it "doesn't set the time if the deploy's environment is not 'production'" do
            service.execute
168

169 170
            expect(merge_request.reload.metrics.first_deployed_to_production_at).to be_nil
          end
171 172 173 174 175 176 177 178 179 180 181 182 183 184
        end

        it 'does not raise errors if the merge request does not have a metrics record' do
          merge_request.metrics.destroy

          expect(merge_request.reload.metrics).to be_nil
          expect { service.execute }.not_to raise_error
        end
      end

      context "for merge requests merged before the previous deploy" do
        context "if the 'first_deployed_to_production_at' time is already set" do
          it "does not overwrite the older 'first_deployed_to_production_at' time" do
            # Previous deploy
Shinya Maeda's avatar
Shinya Maeda committed
185
            service.execute
186

Shinya Maeda's avatar
Shinya Maeda committed
187
            expect(merge_request.reload.metrics.first_deployed_to_production_at).to be_like_time(deployment.finished_at)
188 189

            # Current deploy
Shinya Maeda's avatar
Shinya Maeda committed
190 191
            Timecop.travel(12.hours.from_now) do
              service.execute
192

Shinya Maeda's avatar
Shinya Maeda committed
193 194
              expect(merge_request.reload.metrics.first_deployed_to_production_at).to be_like_time(deployment.finished_at)
            end
195 196 197 198 199 200
          end
        end

        context "if the 'first_deployed_to_production_at' time is not already set" do
          it "does not overwrite the older 'first_deployed_to_production_at' time" do
            # Previous deploy
201
            time = 5.minutes.from_now
202
            Timecop.freeze(time) { service.execute }
203 204 205

            expect(merge_request.reload.metrics.merged_at).to be < merge_request.reload.metrics.first_deployed_to_production_at

206
            previous_time = merge_request.reload.metrics.first_deployed_to_production_at
207 208 209 210

            # Current deploy
            Timecop.freeze(time + 12.hours) { service.execute }

211
            expect(merge_request.reload.metrics.first_deployed_to_production_at).to eq(previous_time)
212 213 214 215 216
          end
        end
      end
    end
  end
217
end