diff options
author | GitLab Bot <gitlab-bot@gitlab.com> | 2022-04-20 10:00:54 +0000 |
---|---|---|
committer | GitLab Bot <gitlab-bot@gitlab.com> | 2022-04-20 10:00:54 +0000 |
commit | 3cccd102ba543e02725d247893729e5c73b38295 (patch) | |
tree | f36a04ec38517f5deaaacb5acc7d949688d1e187 /spec/models/environment_spec.rb | |
parent | 205943281328046ef7b4528031b90fbda70c75ac (diff) | |
download | gitlab-ce-3cccd102ba543e02725d247893729e5c73b38295.tar.gz |
Add latest changes from gitlab-org/gitlab@14-10-stable-eev14.10.0-rc42
Diffstat (limited to 'spec/models/environment_spec.rb')
-rw-r--r-- | spec/models/environment_spec.rb | 261 |
1 files changed, 241 insertions, 20 deletions
diff --git a/spec/models/environment_spec.rb b/spec/models/environment_spec.rb index 6144593395c..b42e73e6d93 100644 --- a/spec/models/environment_spec.rb +++ b/spec/models/environment_spec.rb @@ -23,7 +23,6 @@ RSpec.describe Environment, :use_clean_rails_memory_store_caching do it { is_expected.to have_one(:upcoming_deployment) } it { is_expected.to have_one(:latest_opened_most_severe_alert) } - it { is_expected.to delegate_method(:stop_action).to(:last_deployment) } it { is_expected.to delegate_method(:manual_actions).to(:last_deployment) } it { is_expected.to validate_presence_of(:name) } @@ -349,15 +348,28 @@ RSpec.describe Environment, :use_clean_rails_memory_store_caching do end describe '.with_deployment' do - subject { described_class.with_deployment(sha) } + subject { described_class.with_deployment(sha, status: status) } let(:environment) { create(:environment, project: project) } let(:sha) { 'b83d6e391c22777fca1ed3012fce84f633d7fed0' } + let(:status) { nil } context 'when deployment has the specified sha' do let!(:deployment) { create(:deployment, environment: environment, sha: sha) } it { is_expected.to eq([environment]) } + + context 'with success status filter' do + let(:status) { :success } + + it { is_expected.to be_empty } + end + + context 'with created status filter' do + let(:status) { :created } + + it { is_expected.to contain_exactly(environment) } + end end context 'when deployment does not have the specified sha' do @@ -459,8 +471,8 @@ RSpec.describe Environment, :use_clean_rails_memory_store_caching do end end - describe '#stop_action_available?' do - subject { environment.stop_action_available? } + describe '#stop_actions_available?' do + subject { environment.stop_actions_available? } context 'when no other actions' do it { is_expected.to be_falsey } @@ -499,10 +511,10 @@ RSpec.describe Environment, :use_clean_rails_memory_store_caching do end end - describe '#stop_with_action!' do + describe '#stop_with_actions!' do let(:user) { create(:user) } - subject { environment.stop_with_action!(user) } + subject { environment.stop_with_actions!(user) } before do expect(environment).to receive(:available?).and_call_original @@ -515,9 +527,10 @@ RSpec.describe Environment, :use_clean_rails_memory_store_caching do end it do - subject + actions = subject expect(environment).to be_stopped + expect(actions).to match_array([]) end end @@ -536,18 +549,18 @@ RSpec.describe Environment, :use_clean_rails_memory_store_caching do context 'when matching action is defined' do let(:pipeline) { create(:ci_pipeline, project: project) } - let(:build) { create(:ci_build, pipeline: pipeline) } + let(:build_a) { create(:ci_build, pipeline: pipeline) } - let!(:deployment) do + before do create(:deployment, :success, - environment: environment, - deployable: build, - on_stop: 'close_app') + environment: environment, + deployable: build_a, + on_stop: 'close_app_a') end context 'when user is not allowed to stop environment' do let!(:close_action) do - create(:ci_build, :manual, pipeline: pipeline, name: 'close_app') + create(:ci_build, :manual, pipeline: pipeline, name: 'close_app_a') end it 'raises an exception' do @@ -565,31 +578,200 @@ RSpec.describe Environment, :use_clean_rails_memory_store_caching do context 'when action did not yet finish' do let!(:close_action) do - create(:ci_build, :manual, pipeline: pipeline, name: 'close_app') + create(:ci_build, :manual, pipeline: pipeline, name: 'close_app_a') end it 'returns the same action' do - expect(subject).to eq(close_action) - expect(subject.user).to eq(user) + action = subject.first + expect(action).to eq(close_action) + expect(action.user).to eq(user) end end context 'if action did finish' do let!(:close_action) do create(:ci_build, :manual, :success, - pipeline: pipeline, name: 'close_app') + pipeline: pipeline, name: 'close_app_a') end it 'returns a new action of the same type' do - expect(subject).to be_persisted - expect(subject.name).to eq(close_action.name) - expect(subject.user).to eq(user) + action = subject.first + + expect(action).to be_persisted + expect(action.name).to eq(close_action.name) + expect(action.user).to eq(user) + end + end + + context 'close action does not raise ActiveRecord::StaleObjectError' do + let!(:close_action) do + create(:ci_build, :manual, pipeline: pipeline, name: 'close_app_a') + end + + before do + # preload the build + environment.stop_actions + + # Update record as the other process. This makes `environment.stop_action` stale. + close_action.drop! end + + it 'successfully plays the build even if the build was a stale object' do + # Since build is droped. + expect(close_action.processed).to be_falsey + + # it encounters the StaleObjectError at first, but reloads the object and runs `build.play` + expect { subject }.not_to raise_error(ActiveRecord::StaleObjectError) + + # Now the build should be processed. + expect(close_action.reload.processed).to be_truthy + end + end + end + end + + context 'when there are more then one stop action for the environment' do + let(:pipeline) { create(:ci_pipeline, project: project) } + let(:build_a) { create(:ci_build, pipeline: pipeline) } + let(:build_b) { create(:ci_build, pipeline: pipeline) } + + let!(:close_actions) do + [ + create(:ci_build, :manual, pipeline: pipeline, name: 'close_app_a'), + create(:ci_build, :manual, pipeline: pipeline, name: 'close_app_b') + ] + end + + before do + project.add_developer(user) + + create(:deployment, :success, + environment: environment, + deployable: build_a, + finished_at: 5.minutes.ago, + on_stop: 'close_app_a') + + create(:deployment, :success, + environment: environment, + deployable: build_b, + finished_at: 1.second.ago, + on_stop: 'close_app_b') + end + + it 'returns the same actions' do + actions = subject + + expect(actions.count).to eq(close_actions.count) + expect(actions.pluck(:id)).to match_array(close_actions.pluck(:id)) + expect(actions.pluck(:user)).to match_array(close_actions.pluck(:user)) + end + + context 'when there are failed deployment jobs' do + before do + create(:ci_build, pipeline: pipeline, name: 'close_app_c') + + create(:deployment, :failed, + environment: environment, + deployable: create(:ci_build, pipeline: pipeline), + on_stop: 'close_app_c') + end + + it 'returns only stop actions from successful deployment jobs' do + actions = subject + + expect(actions).to match_array(close_actions) + expect(actions.count).to eq(environment.successful_deployments.count) + end + end + + context 'when the feature is disabled' do + before do + stub_feature_flags(environment_multiple_stop_actions: false) + end + + it 'returns the last deployment job stop action' do + stop_actions = subject + + expect(stop_actions.first).to eq(close_actions[1]) + expect(stop_actions.count).to eq(1) end end end end + describe '#stop_actions' do + subject { environment.stop_actions } + + context 'when there are no deployments and builds' do + it 'returns empty array' do + is_expected.to match_array([]) + end + end + + context 'when there are multiple deployments with actions' do + let(:pipeline) { create(:ci_pipeline, project: project) } + let(:ci_build_a) { create(:ci_build, project: project, pipeline: pipeline) } + let(:ci_build_b) { create(:ci_build, project: project, pipeline: pipeline) } + let!(:ci_build_c) { create(:ci_build, :manual, project: project, pipeline: pipeline, name: 'close_app_a') } + let!(:ci_build_d) { create(:ci_build, :manual, project: project, pipeline: pipeline, name: 'close_app_b') } + + let!(:deployment_a) do + create(:deployment, + :success, project: project, environment: environment, deployable: ci_build_a, on_stop: 'close_app_a') + end + + let!(:deployment_b) do + create(:deployment, + :success, project: project, environment: environment, deployable: ci_build_b, on_stop: 'close_app_b') + end + + before do + # Create failed deployment without stop_action. + build = create(:ci_build, project: project, pipeline: pipeline) + create(:deployment, :failed, project: project, environment: environment, deployable: build) + end + + it 'returns only the stop actions' do + expect(subject.pluck(:id)).to contain_exactly(ci_build_c.id, ci_build_d.id) + end + end + end + + describe '#last_deployment_group' do + subject { environment.last_deployment_group } + + context 'when there are no deployments and builds' do + it do + is_expected.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, project: project, pipeline: pipeline_a) } + let(:ci_build_b) { create(:ci_build, project: project, pipeline: pipeline_b) } + let(:ci_build_c) { create(:ci_build, project: project, pipeline: pipeline_a) } + let(:ci_build_d) { create(:ci_build, project: project, pipeline: pipeline_a) } + + # Successful deployments for pipeline_a + let!(:deployment_a) { create(:deployment, :success, project: project, environment: environment, deployable: ci_build_a) } + let!(:deployment_b) { create(:deployment, :success, project: project, environment: environment, deployable: ci_build_c) } + + 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.pluck(:id)).to contain_exactly(deployment_a.id, deployment_b.id) + end + end + end + describe 'recently_updated_on_branch?' do subject { environment.recently_updated_on_branch?('feature') } @@ -698,10 +880,29 @@ RSpec.describe Environment, :use_clean_rails_memory_store_caching do context 'when there is a deployment record with success status' do let!(:deployment) { create(:deployment, :success, environment: environment) } + let!(:old_deployment) { create(:deployment, :success, environment: environment, finished_at: 2.days.ago) } it 'returns the latest successful deployment' do is_expected.to eq(deployment) end + + context 'env_last_deployment_by_finished_at feature flag' do + it 'when enabled it returns the deployment with the latest finished_at' do + stub_feature_flags(env_last_deployment_by_finished_at: true) + + expect(old_deployment.finished_at < deployment.finished_at).to be_truthy + + is_expected.to eq(deployment) + end + + it 'when disabled it returns the deployment with the highest id' do + stub_feature_flags(env_last_deployment_by_finished_at: false) + + expect(old_deployment.finished_at < deployment.finished_at).to be_truthy + + is_expected.to eq(old_deployment) + end + end end end end @@ -728,6 +929,26 @@ RSpec.describe Environment, :use_clean_rails_memory_store_caching do end end + describe '#last_deployment_pipeline' do + subject { environment.last_deployment_pipeline } + + let(:pipeline_a) { create(:ci_pipeline, project: project) } + let(:pipeline_b) { create(:ci_pipeline, project: project) } + let(:ci_build_a) { create(:ci_build, project: project, pipeline: pipeline_a) } + let(:ci_build_b) { create(:ci_build, project: project, pipeline: pipeline_b) } + + before do + create(:deployment, :success, project: project, environment: environment, deployable: ci_build_a) + create(:deployment, :failed, project: project, environment: environment, deployable: ci_build_b) + end + + it 'does not join across databases' do + with_cross_joins_prevented do + expect(subject.id).to eq(pipeline_a.id) + end + end + end + describe '#last_visible_deployment' do subject { environment.last_visible_deployment } |