diff options
Diffstat (limited to 'spec/models/deployment_spec.rb')
-rw-r--r-- | spec/models/deployment_spec.rb | 283 |
1 files changed, 258 insertions, 25 deletions
diff --git a/spec/models/deployment_spec.rb b/spec/models/deployment_spec.rb index 409353bdbcf..a58d32dfe5d 100644 --- a/spec/models/deployment_spec.rb +++ b/spec/models/deployment_spec.rb @@ -17,7 +17,6 @@ RSpec.describe Deployment do it { is_expected.to delegate_method(:name).to(:environment).with_prefix } it { is_expected.to delegate_method(:commit).to(:project) } it { is_expected.to delegate_method(:commit_title).to(:commit).as(:try) } - it { is_expected.to delegate_method(:manual_actions).to(:deployable).as(:try) } it { is_expected.to delegate_method(:kubernetes_namespace).to(:deployment_cluster).as(:kubernetes_namespace) } it { is_expected.to validate_presence_of(:ref) } @@ -25,20 +24,23 @@ RSpec.describe Deployment do it_behaves_like 'having unique enum values' - describe '#scheduled_actions' do - subject { deployment.scheduled_actions } + describe '#manual_actions' do + let(:deployment) { create(:deployment) } - let(:project) { create(:project, :repository) } - let(:pipeline) { create(:ci_pipeline, project: project) } - let(:build) { create(:ci_build, :success, pipeline: pipeline) } - let(:deployment) { create(:deployment, deployable: build) } + it 'delegates to environment_manual_actions' do + expect(deployment.deployable).to receive(:environment_manual_actions).and_call_original - it 'delegates to other_scheduled_actions' do - expect_next_instance_of(Ci::Build) do |instance| - expect(instance).to receive(:other_scheduled_actions) - end + deployment.manual_actions + end + end - subject + describe '#scheduled_actions' do + let(:deployment) { create(:deployment) } + + it 'delegates to environment_scheduled_actions' do + expect(deployment.deployable).to receive(:environment_scheduled_actions).and_call_original + + deployment.scheduled_actions end end @@ -137,15 +139,29 @@ RSpec.describe Deployment do end end - it 'executes Deployments::HooksWorker asynchronously' do + it 'executes deployment hooks' do freeze_time do - expect(Deployments::HooksWorker) - .to receive(:perform_async).with(deployment_id: deployment.id, status_changed_at: Time.current) + expect(deployment).to receive(:execute_hooks).with(Time.current) deployment.run! end end + context 'when `deployment_hooks_skip_worker` flag is disabled' do + before do + stub_feature_flags(deployment_hooks_skip_worker: false) + end + + it 'executes Deployments::HooksWorker asynchronously' do + freeze_time do + expect(Deployments::HooksWorker) + .to receive(:perform_async).with(deployment_id: deployment.id, status_changed_at: Time.current) + + deployment.run! + end + end + end + it 'executes Deployments::DropOlderDeploymentsWorker asynchronously' do expect(Deployments::DropOlderDeploymentsWorker) .to receive(:perform_async).once.with(deployment.id) @@ -173,14 +189,28 @@ RSpec.describe Deployment do deployment.succeed! end - it 'executes Deployments::HooksWorker asynchronously' do + it 'executes deployment hooks' do freeze_time do - expect(Deployments::HooksWorker) - .to receive(:perform_async).with(deployment_id: deployment.id, status_changed_at: Time.current) + expect(deployment).to receive(:execute_hooks).with(Time.current) deployment.succeed! end end + + context 'when `deployment_hooks_skip_worker` flag is disabled' do + before do + stub_feature_flags(deployment_hooks_skip_worker: false) + end + + it 'executes Deployments::HooksWorker asynchronously' do + freeze_time do + expect(Deployments::HooksWorker) + .to receive(:perform_async).with(deployment_id: deployment.id, status_changed_at: Time.current) + + deployment.succeed! + end + end + end end context 'when deployment failed' do @@ -202,14 +232,28 @@ RSpec.describe Deployment do deployment.drop! end - it 'executes Deployments::HooksWorker asynchronously' do + it 'executes deployment hooks' do freeze_time do - expect(Deployments::HooksWorker) - .to receive(:perform_async).with(deployment_id: deployment.id, status_changed_at: Time.current) + expect(deployment).to receive(:execute_hooks).with(Time.current) deployment.drop! end end + + context 'when `deployment_hooks_skip_worker` flag is disabled' do + before do + stub_feature_flags(deployment_hooks_skip_worker: false) + end + + it 'executes Deployments::HooksWorker asynchronously' do + freeze_time do + expect(Deployments::HooksWorker) + .to receive(:perform_async).with(deployment_id: deployment.id, status_changed_at: Time.current) + + deployment.drop! + end + end + end end context 'when deployment was canceled' do @@ -231,14 +275,28 @@ RSpec.describe Deployment do deployment.cancel! end - it 'executes Deployments::HooksWorker asynchronously' do + it 'executes deployment hooks' do freeze_time do - expect(Deployments::HooksWorker) - .to receive(:perform_async).with(deployment_id: deployment.id, status_changed_at: Time.current) + expect(deployment).to receive(:execute_hooks).with(Time.current) deployment.cancel! end end + + context 'when `deployment_hooks_skip_worker` flag is disabled' do + before do + stub_feature_flags(deployment_hooks_skip_worker: false) + end + + it 'executes Deployments::HooksWorker asynchronously' do + freeze_time do + expect(Deployments::HooksWorker) + .to receive(:perform_async).with(deployment_id: deployment.id, status_changed_at: Time.current) + + deployment.cancel! + end + end + end end context 'when deployment was skipped' do @@ -266,6 +324,12 @@ RSpec.describe Deployment do deployment.skip! end end + + it 'does not execute deployment hooks' do + expect(deployment).not_to receive(:execute_hooks) + + deployment.skip! + end end context 'when deployment is blocked' do @@ -289,6 +353,12 @@ RSpec.describe Deployment do deployment.block! end + + it 'does not execute deployment hooks' do + expect(deployment).not_to receive(:execute_hooks) + + deployment.block! + end end describe 'synching status to Jira' do @@ -550,6 +620,143 @@ RSpec.describe Deployment do is_expected.to contain_exactly(deployment1, deployment2) end end + + describe 'last_deployment_group_for_environment' do + def subject_method(environment) + described_class.last_deployment_group_for_environment(environment) + end + + let!(:project) { create(:project, :repository) } + let!(:environment) { create(:environment, project: project) } + + context 'when there are no deployments and builds' do + it do + expect(subject_method(environment)).to eq(Deployment.none) + end + end + + context 'when there are no successful builds' do + let(:pipeline) { create(:ci_pipeline, project: project) } + let(:ci_build) { create(:ci_build, :running, project: project, pipeline: pipeline) } + + before do + create(:deployment, :success, environment: environment, project: project, deployable: ci_build) + end + + it do + expect(subject_method(environment)).to eq(Deployment.none) + end + end + + context 'when there are deployments for multiple pipelines' do + let(:pipeline_a) { create(:ci_pipeline, project: project) } + let(:pipeline_b) { create(:ci_pipeline, project: project) } + let(:ci_build_a) { create(:ci_build, :success, project: project, pipeline: pipeline_a) } + let(:ci_build_b) { create(:ci_build, :failed, project: project, pipeline: pipeline_b) } + let(:ci_build_c) { create(:ci_build, :success, project: project, pipeline: pipeline_a) } + let(:ci_build_d) { create(:ci_build, :failed, project: project, pipeline: pipeline_a) } + + # Successful deployments for pipeline_a + let!(:deployment_a) do + create(:deployment, :success, project: project, environment: environment, deployable: ci_build_a) + end + + let!(:deployment_b) do + create(:deployment, :success, project: project, environment: environment, deployable: ci_build_c) + end + + before do + # Failed deployment for pipeline_a + create(:deployment, :failed, project: project, environment: environment, deployable: ci_build_d) + + # Failed deployment for pipeline_b + create(:deployment, :failed, project: project, environment: environment, deployable: ci_build_b) + end + + it 'returns the successful deployment jobs for the last deployment pipeline' do + expect(subject_method(environment).pluck(:id)).to contain_exactly(deployment_a.id, deployment_b.id) + end + end + + context 'when there are many environments' do + let(:environment_b) { create(:environment, project: project) } + + let(:pipeline_a) { create(:ci_pipeline, project: project) } + let(:pipeline_b) { create(:ci_pipeline, project: project) } + let(:pipeline_c) { create(:ci_pipeline, project: project) } + let(:pipeline_d) { create(:ci_pipeline, project: project) } + + # Builds for first environment: 'environment' with pipeline_a and pipeline_b + let(:ci_build_a) { create(:ci_build, :success, project: project, pipeline: pipeline_a) } + let(:ci_build_b) { create(:ci_build, :failed, project: project, pipeline: pipeline_b) } + let(:ci_build_c) { create(:ci_build, :success, project: project, pipeline: pipeline_a) } + let(:ci_build_d) { create(:ci_build, :failed, project: project, pipeline: pipeline_a) } + let!(:stop_env_a) { create(:ci_build, :manual, project: project, pipeline: pipeline_a, name: 'stop_env_a') } + + # Builds for second environment: 'environment_b' with pipeline_c and pipeline_d + let(:ci_build_e) { create(:ci_build, :success, project: project, pipeline: pipeline_c) } + let(:ci_build_f) { create(:ci_build, :failed, project: project, pipeline: pipeline_d) } + let(:ci_build_g) { create(:ci_build, :success, project: project, pipeline: pipeline_c) } + let(:ci_build_h) { create(:ci_build, :failed, project: project, pipeline: pipeline_c) } + let!(:stop_env_b) { create(:ci_build, :manual, project: project, pipeline: pipeline_c, name: 'stop_env_b') } + + # Successful deployments for 'environment' from pipeline_a + let!(:deployment_a) do + create(:deployment, :success, project: project, environment: environment, deployable: ci_build_a) + end + + let!(:deployment_b) do + create(:deployment, :success, + project: project, environment: environment, deployable: ci_build_c, on_stop: 'stop_env_a') + end + + # Successful deployments for 'environment_b' from pipeline_c + let!(:deployment_c) do + create(:deployment, :success, project: project, environment: environment_b, deployable: ci_build_e) + end + + let!(:deployment_d) do + create(:deployment, :success, + project: project, environment: environment_b, deployable: ci_build_g, on_stop: 'stop_env_b') + end + + before do + # Failed deployment for 'environment' from pipeline_a and pipeline_b + create(:deployment, :failed, project: project, environment: environment, deployable: ci_build_d) + create(:deployment, :failed, project: project, environment: environment, deployable: ci_build_b) + + # Failed deployment for 'environment_b' from pipeline_c and pipeline_d + create(:deployment, :failed, project: project, environment: environment_b, deployable: ci_build_h) + create(:deployment, :failed, project: project, environment: environment_b, deployable: ci_build_f) + end + + it 'batch loads for environments' do + environments = [environment, environment_b] + + # Loads Batch loader + environments.each do |env| + subject_method(env) + end + + expect(subject_method(environments.first).pluck(:id)) + .to contain_exactly(deployment_a.id, deployment_b.id) + + expect { subject_method(environments.second).pluck(:id) }.not_to exceed_query_limit(0) + + expect(subject_method(environments.second).pluck(:id)) + .to contain_exactly(deployment_c.id, deployment_d.id) + + expect(subject_method(environments.first).map(&:stop_action).compact) + .to contain_exactly(stop_env_a) + + expect { subject_method(environments.second).map(&:stop_action) } + .not_to exceed_query_limit(0) + + expect(subject_method(environments.second).map(&:stop_action).compact) + .to contain_exactly(stop_env_b) + end + end + end end describe 'latest_for_sha' do @@ -845,11 +1052,30 @@ RSpec.describe Deployment do expect(Deployments::UpdateEnvironmentWorker).to receive(:perform_async) expect(Deployments::LinkMergeRequestWorker).to receive(:perform_async) expect(Deployments::ArchiveInProjectWorker).to receive(:perform_async) - expect(Deployments::HooksWorker).to receive(:perform_async) expect(deploy.update_status('success')).to eq(true) end + context 'when `deployment_hooks_skip_worker` flag is disabled' do + before do + stub_feature_flags(deployment_hooks_skip_worker: false) + end + + it 'schedules `Deployments::HooksWorker` when finishing a deploy' do + expect(Deployments::HooksWorker).to receive(:perform_async) + + deploy.update_status('success') + end + end + + it 'executes deployment hooks when finishing a deploy' do + freeze_time do + expect(deploy).to receive(:execute_hooks).with(Time.current) + + deploy.update_status('success') + end + end + it 'updates finished_at when transitioning to a finished status' do freeze_time do deploy.update_status('success') @@ -1173,4 +1399,11 @@ RSpec.describe Deployment do end end end + + context 'loose foreign key on deployments.cluster_id' do + it_behaves_like 'cleanup by a loose foreign key' do + let!(:parent) { create(:cluster) } + let!(:model) { create(:deployment, cluster: parent) } + end + end end |