diff options
author | GitLab Bot <gitlab-bot@gitlab.com> | 2023-12-14 00:09:54 +0300 |
---|---|---|
committer | GitLab Bot <gitlab-bot@gitlab.com> | 2023-12-14 00:09:54 +0300 |
commit | 0fea036ba7c8d26ffeecc7f52bf770ea3a88689e (patch) | |
tree | 5937f3181e46bdc2c96ba9b530bc66d0ef46311f /spec | |
parent | 46e1fdb8bbdaf149371334f1a1757ba4d68fe020 (diff) |
Add latest changes from gitlab-org/gitlab@master
Diffstat (limited to 'spec')
26 files changed, 560 insertions, 235 deletions
diff --git a/spec/features/environments/environments_folder_spec.rb b/spec/features/environments/environments_folder_spec.rb index 9e2932e315f..da9de6fba0c 100644 --- a/spec/features/environments/environments_folder_spec.rb +++ b/spec/features/environments/environments_folder_spec.rb @@ -99,6 +99,30 @@ RSpec.describe 'Environments Folder page', :js, feature_category: :environment_m expect(page).not_to have_content(get_env_name(stopped_env)) end end + + describe 'pagination' do + # rubocop:disable RSpec/FactoryBot/ExcessiveCreateList -- need >20 items to test pagination + let!(:envs) { create_list(:environment, 25, :with_folders, project: project, folder: folder_name) } + + # rubocop:enable RSpec/FactoryBot/ExcessiveCreateList + it 'shows pagination' do + pagination = find('.pagination') + + expect(pagination).to have_content('2') + end + + it 'can navigate to the next page and updates the url' do + pagination = find('.pagination') + pagination.scroll_to(:bottom) + within(pagination) do + click_link 'Next' + end + + wait_for_requests + + expect(current_url).to include('page=2') + end + end end describe 'legacy folders page' do diff --git a/spec/frontend/diffs/components/shared/__snapshots__/findings_drawer_spec.js.snap b/spec/frontend/diffs/components/shared/__snapshots__/findings_drawer_spec.js.snap index d7364e80542..33a268c06cc 100644 --- a/spec/frontend/diffs/components/shared/__snapshots__/findings_drawer_spec.js.snap +++ b/spec/frontend/diffs/components/shared/__snapshots__/findings_drawer_spec.js.snap @@ -1,6 +1,6 @@ // Jest Snapshot v1, https://goo.gl/fbAQLP -exports[`FindingsDrawer matches the snapshot with detected badge 1`] = ` +exports[`FindingsDrawer General Rendering matches the snapshot with detected badge 1`] = ` <transition-stub class="findings-drawer" name="gl-drawer" @@ -16,7 +16,7 @@ exports[`FindingsDrawer matches the snapshot with detected badge 1`] = ` class="gl-drawer-title" > <h2 - class="drawer-heading gl-font-base gl-mb-0 gl-mt-0" + class="drawer-heading gl-font-base gl-mb-0 gl-mt-0 gl-w-28" > <svg aria-hidden="true" @@ -61,6 +61,7 @@ exports[`FindingsDrawer matches the snapshot with detected badge 1`] = ` > <li class="gl-mb-4" + data-testid="findings-drawer-title" > <p class="gl-line-height-20" @@ -219,7 +220,7 @@ exports[`FindingsDrawer matches the snapshot with detected badge 1`] = ` </transition-stub> `; -exports[`FindingsDrawer matches the snapshot with dismissed badge 1`] = ` +exports[`FindingsDrawer General Rendering matches the snapshot with dismissed badge 1`] = ` <transition-stub class="findings-drawer" name="gl-drawer" @@ -235,7 +236,7 @@ exports[`FindingsDrawer matches the snapshot with dismissed badge 1`] = ` class="gl-drawer-title" > <h2 - class="drawer-heading gl-font-base gl-mb-0 gl-mt-0" + class="drawer-heading gl-font-base gl-mb-0 gl-mt-0 gl-w-28" > <svg aria-hidden="true" @@ -280,6 +281,7 @@ exports[`FindingsDrawer matches the snapshot with dismissed badge 1`] = ` > <li class="gl-mb-4" + data-testid="findings-drawer-title" > <p class="gl-line-height-20" @@ -310,9 +312,9 @@ exports[`FindingsDrawer matches the snapshot with dismissed badge 1`] = ` Status </span> <span - class="badge badge-muted badge-pill gl-badge md text-capitalize" + class="badge badge-pill badge-warning gl-badge md text-capitalize" > - dismissed + detected </span> </p> </li> diff --git a/spec/frontend/diffs/components/shared/findings_drawer_spec.js b/spec/frontend/diffs/components/shared/findings_drawer_spec.js index a6528978e3b..00b4ca262be 100644 --- a/spec/frontend/diffs/components/shared/findings_drawer_spec.js +++ b/spec/frontend/diffs/components/shared/findings_drawer_spec.js @@ -1,3 +1,4 @@ +import { nextTick } from 'vue'; import { GlDrawer } from '@gitlab/ui'; import FindingsDrawer from '~/diffs/components/shared/findings_drawer.vue'; import { mountExtended } from 'helpers/vue_test_utils_helper'; @@ -5,41 +6,101 @@ import { mockFindingDismissed, mockFindingDetected, mockProject, + mockFindingsMultiple, } from '../../mock_data/findings_drawer'; -let wrapper; -const getDrawer = () => wrapper.findComponent(GlDrawer); -const closeEvent = 'close'; +describe('FindingsDrawer', () => { + let wrapper; -const createWrapper = (finding = mockFindingDismissed) => { - return mountExtended(FindingsDrawer, { - propsData: { - drawer: finding, - project: mockProject, - }, - }); -}; + const findPreviousButton = () => wrapper.findByTestId('findings-drawer-prev-button'); + const findNextButton = () => wrapper.findByTestId('findings-drawer-next-button'); + const findTitle = () => wrapper.findByTestId('findings-drawer-title'); + const createWrapper = ( + drawer = { findings: [mockFindingDetected], index: 0 }, + project = mockProject, + ) => { + return mountExtended(FindingsDrawer, { + propsData: { + drawer, + project, + }, + }); + }; -describe('FindingsDrawer', () => { - it('renders without errors', () => { - wrapper = createWrapper(); - expect(wrapper.exists()).toBe(true); - }); + describe('General Rendering', () => { + beforeEach(() => { + wrapper = createWrapper(); + }); + it('renders without errors', () => { + expect(wrapper.exists()).toBe(true); + }); - it('emits close event when gl-drawer emits close event', () => { - wrapper = createWrapper(); + it('emits close event when gl-drawer emits close event', () => { + wrapper.findComponent(GlDrawer).vm.$emit('close'); + expect(wrapper.emitted('close')).toHaveLength(1); + }); - getDrawer().vm.$emit(closeEvent); - expect(wrapper.emitted(closeEvent)).toHaveLength(1); + it('matches the snapshot with dismissed badge', () => { + expect(wrapper.element).toMatchSnapshot(); + }); + + it('matches the snapshot with detected badge', () => { + expect(wrapper.element).toMatchSnapshot(); + }); }); - it('matches the snapshot with dismissed badge', () => { - wrapper = createWrapper(); - expect(wrapper.element).toMatchSnapshot(); + describe('Prev/Next Buttons with Multiple Items', () => { + it('renders prev/next buttons when there are multiple items', () => { + wrapper = createWrapper({ findings: mockFindingsMultiple, index: 0 }); + expect(findPreviousButton().exists()).toBe(true); + expect(findNextButton().exists()).toBe(true); + }); + + it('does not render prev/next buttons when there is only one item', () => { + wrapper = createWrapper({ findings: [mockFindingDismissed], index: 0 }); + expect(findPreviousButton().exists()).toBe(false); + expect(findNextButton().exists()).toBe(false); + }); + + it('calls prev method on prev button click and loops correct activeIndex', async () => { + wrapper = createWrapper({ findings: mockFindingsMultiple, index: 0 }); + expect(findTitle().text()).toBe(`Name ${mockFindingsMultiple[0].title}`); + + await findPreviousButton().trigger('click'); + await nextTick(); + expect(findTitle().text()).toBe(`Name ${mockFindingsMultiple[2].title}`); + + await findPreviousButton().trigger('click'); + await nextTick(); + expect(findTitle().text()).toBe(`Name ${mockFindingsMultiple[1].title}`); + }); + + it('calls next method on next button click', async () => { + wrapper = createWrapper({ findings: mockFindingsMultiple, index: 0 }); + expect(findTitle().text()).toBe(`Name ${mockFindingsMultiple[0].title}`); + + await findNextButton().trigger('click'); + await nextTick(); + expect(findTitle().text()).toBe(`Name ${mockFindingsMultiple[1].title}`); + + await findNextButton().trigger('click'); + await nextTick(); + expect(findTitle().text()).toBe(`Name ${mockFindingsMultiple[2].title}`); + + await findNextButton().trigger('click'); + await nextTick(); + expect(findTitle().text()).toBe(`Name ${mockFindingsMultiple[0].title}`); + }); }); - it('matches the snapshot with detected badge', () => { - wrapper = createWrapper(mockFindingDetected); - expect(wrapper.element).toMatchSnapshot(); + describe('Active Index Handling', () => { + it('watcher sets active index on drawer prop change', async () => { + wrapper = createWrapper(); + const newFinding = { findings: mockFindingsMultiple, index: 2 }; + + await wrapper.setProps({ drawer: newFinding }); + await nextTick(); + expect(findTitle().text()).toBe(`Name ${mockFindingsMultiple[2].title}`); + }); }); }); diff --git a/spec/frontend/diffs/mock_data/findings_drawer.js b/spec/frontend/diffs/mock_data/findings_drawer.js index f6fc33b1dcf..257a3b3e499 100644 --- a/spec/frontend/diffs/mock_data/findings_drawer.js +++ b/spec/frontend/diffs/mock_data/findings_drawer.js @@ -31,3 +31,45 @@ export const mockProject = { nameWithNamespace: 'testname', fullPath: 'testpath', }; + +export const mockFindingsMultiple = [ + { + ...mockFindingDismissed, + title: 'Finding 1', + severity: 'critical', + engineName: 'Engine 1', + identifiers: [ + { + ...mockFindingDismissed.identifiers[0], + name: 'identifier 1', + url: 'https://example.com/identifier1', + }, + ], + }, + { + ...mockFindingDetected, + title: 'Finding 2', + severity: 'medium', + engineName: 'Engine 2', + identifiers: [ + { + ...mockFindingDetected.identifiers[0], + name: 'identifier 2', + url: 'https://example.com/identifier2', + }, + ], + }, + { + ...mockFindingDetected, + title: 'Finding 3', + severity: 'medium', + engineName: 'Engine 3', + identifiers: [ + { + ...mockFindingDetected.identifiers[0], + name: 'identifier 3', + url: 'https://example.com/identifier3', + }, + ], + }, +]; diff --git a/spec/frontend/editor/schema/ci/ci_schema_spec.js b/spec/frontend/editor/schema/ci/ci_schema_spec.js index 8e7f6e8e00a..7986509074e 100644 --- a/spec/frontend/editor/schema/ci/ci_schema_spec.js +++ b/spec/frontend/editor/schema/ci/ci_schema_spec.js @@ -38,6 +38,8 @@ import SecretsYaml from './yaml_tests/positive_tests/secrets.yml'; import ServicesYaml from './yaml_tests/positive_tests/services.yml'; import NeedsParallelMatrixYaml from './yaml_tests/positive_tests/needs_parallel_matrix.yml'; import ScriptYaml from './yaml_tests/positive_tests/script.yml'; +import AutoCancelPipelineOnJobFailureAllYaml from './yaml_tests/positive_tests/auto_cancel_pipeline/on_job_failure/all.yml'; +import AutoCancelPipelineOnJobFailureNoneYaml from './yaml_tests/positive_tests/auto_cancel_pipeline/on_job_failure/none.yml'; // YAML NEGATIVE TEST import ArtifactsNegativeYaml from './yaml_tests/negative_tests/artifacts.yml'; @@ -64,6 +66,7 @@ import NeedsParallelMatrixNumericYaml from './yaml_tests/negative_tests/needs/pa import NeedsParallelMatrixWrongParallelValueYaml from './yaml_tests/negative_tests/needs/parallel_matrix/wrong_parallel_value.yml'; import NeedsParallelMatrixWrongMatrixValueYaml from './yaml_tests/negative_tests/needs/parallel_matrix/wrong_matrix_value.yml'; import ScriptNegativeYaml from './yaml_tests/negative_tests/script.yml'; +import AutoCancelPipelineNegativeYaml from './yaml_tests/negative_tests/auto_cancel_pipeline.yml'; const ajv = new Ajv({ strictTypes: false, @@ -107,6 +110,8 @@ describe('positive tests', () => { SecretsYaml, NeedsParallelMatrixYaml, ScriptYaml, + AutoCancelPipelineOnJobFailureAllYaml, + AutoCancelPipelineOnJobFailureNoneYaml, }), )('schema validates %s', (_, input) => { // We construct a new "JSON" from each main key that is inside a @@ -152,6 +157,7 @@ describe('negative tests', () => { NeedsParallelMatrixWrongParallelValueYaml, NeedsParallelMatrixWrongMatrixValueYaml, ScriptNegativeYaml, + AutoCancelPipelineNegativeYaml, }), )('schema validates %s', (_, input) => { // We construct a new "JSON" from each main key that is inside a diff --git a/spec/frontend/editor/schema/ci/yaml_tests/negative_tests/auto_cancel_pipeline.yml b/spec/frontend/editor/schema/ci/yaml_tests/negative_tests/auto_cancel_pipeline.yml new file mode 100644 index 00000000000..0ba3e5632e3 --- /dev/null +++ b/spec/frontend/editor/schema/ci/yaml_tests/negative_tests/auto_cancel_pipeline.yml @@ -0,0 +1,4 @@ +# invalid workflow:auto-cancel:on-job-failure +workflow: + auto_cancel: + on_job_failure: unexpected_value diff --git a/spec/frontend/editor/schema/ci/yaml_tests/positive_tests/auto_cancel_pipeline/on_job_failure/all.yml b/spec/frontend/editor/schema/ci/yaml_tests/positive_tests/auto_cancel_pipeline/on_job_failure/all.yml new file mode 100644 index 00000000000..bf84ff16f42 --- /dev/null +++ b/spec/frontend/editor/schema/ci/yaml_tests/positive_tests/auto_cancel_pipeline/on_job_failure/all.yml @@ -0,0 +1,4 @@ +# valid workflow:auto-cancel:on-job-failure +workflow: + auto_cancel: + on_job_failure: all diff --git a/spec/frontend/editor/schema/ci/yaml_tests/positive_tests/auto_cancel_pipeline/on_job_failure/none.yml b/spec/frontend/editor/schema/ci/yaml_tests/positive_tests/auto_cancel_pipeline/on_job_failure/none.yml new file mode 100644 index 00000000000..b99eb50e962 --- /dev/null +++ b/spec/frontend/editor/schema/ci/yaml_tests/positive_tests/auto_cancel_pipeline/on_job_failure/none.yml @@ -0,0 +1,4 @@ +# valid workflow:auto-cancel:on-job-failure +workflow: + auto_cancel: + on_job_failure: none diff --git a/spec/frontend/environments/folder/environments_folder_app_spec.js b/spec/frontend/environments/folder/environments_folder_app_spec.js index fbb252fb152..0b76a74e3a0 100644 --- a/spec/frontend/environments/folder/environments_folder_app_spec.js +++ b/spec/frontend/environments/folder/environments_folder_app_spec.js @@ -1,6 +1,6 @@ import Vue from 'vue'; import VueApollo from 'vue-apollo'; -import { GlSkeletonLoader, GlTab } from '@gitlab/ui'; +import { GlSkeletonLoader, GlTab, GlPagination } from '@gitlab/ui'; import { shallowMountExtended } from 'helpers/vue_test_utils_helper'; import EnvironmentsFolderAppComponent from '~/environments/folder/environments_folder_app.vue'; import EnvironmentItem from '~/environments/components/new_environment_item.vue'; @@ -62,6 +62,7 @@ describe('EnvironmentsFolderAppComponent', () => { folderName: mockFolderName, folderPath: '/gitlab-org/test-project/-/environments/folder/dev', scope: 'active', + page: 1, }, }); }; @@ -119,5 +120,12 @@ describe('EnvironmentsFolderAppComponent', () => { expect(modal.props().environment).toEqual(resolvedEnvironment); expect(modal.props().weight).toBe(1); }); + + it('should render pagination component', () => { + const pagination = wrapper.findComponent(GlPagination); + + expect(pagination.props().perPage).toBe(20); + expect(pagination.props().totalItems).toBe(2); + }); }); }); diff --git a/spec/frontend/environments/graphql/resolvers/base_spec.js b/spec/frontend/environments/graphql/resolvers/base_spec.js index 244c86fa679..f78146fe48e 100644 --- a/spec/frontend/environments/graphql/resolvers/base_spec.js +++ b/spec/frontend/environments/graphql/resolvers/base_spec.js @@ -131,13 +131,14 @@ describe('~/frontend/environments/graphql/resolvers', () => { describe('folder', () => { it('should fetch the folder url passed to it', async () => { mock - .onGet(ENDPOINT, { params: { per_page: 3, scope: 'available', search: '' } }) + .onGet(ENDPOINT, { params: { per_page: 3, scope: 'available', search: '', page: 1 } }) .reply(HTTP_STATUS_OK, folder); const environmentFolder = await mockResolvers.Query.folder(null, { environment: { folderPath: ENDPOINT }, scope: 'available', search: '', + page: 1, }); expect(environmentFolder).toEqual(resolvedFolder); diff --git a/spec/lib/gitlab/background_migration/backfill_vs_code_settings_uuid_spec.rb b/spec/lib/gitlab/background_migration/backfill_vs_code_settings_uuid_spec.rb new file mode 100644 index 00000000000..bf1fce4094e --- /dev/null +++ b/spec/lib/gitlab/background_migration/backfill_vs_code_settings_uuid_spec.rb @@ -0,0 +1,74 @@ +# frozen_string_literal: true + +require 'spec_helper' + +RSpec.describe Gitlab::BackgroundMigration::BackfillVsCodeSettingsUuid, schema: 20231130140901, feature_category: :web_ide do + let!(:vs_code_settings) { table(:vs_code_settings) } + let!(:users) { table(:users) } + + let!(:user) do + users.create!( + email: "test1@example.com", + username: "test1", + notification_email: "test@example.com", + name: "test", + state: "active", + projects_limit: 10) + end + + subject(:migration) do + described_class.new( + start_id: vs_code_setting_one.id, + end_id: vs_code_setting_two.id, + batch_table: :vs_code_settings, + batch_column: :id, + sub_batch_size: 100, + pause_ms: 0, + connection: ActiveRecord::Base.connection + ) + end + + describe "#perform" do + context 'when it finds vs_code_setting rows with empty uuid' do + let(:vs_code_setting_one) do + vs_code_settings.create!(user_id: user.id, setting_type: 'profiles', content: '{}') + end + + let(:vs_code_setting_two) do + vs_code_settings.create!(user_id: user.id, setting_type: 'tasks', content: '{}') + end + + it 'populates uuid column with a generated uuid' do + expect(vs_code_setting_one.uuid).to be_nil + expect(vs_code_setting_two.uuid).to be_nil + + migration.perform + + expect(vs_code_setting_one.reload.uuid).not_to be_nil + expect(vs_code_setting_two.reload.uuid).not_to be_nil + end + end + + context 'when it finds vs_code_setting rows with non-empty uuid' do + let(:vs_code_setting_one) do + vs_code_settings.create!(user_id: user.id, setting_type: 'profiles', content: '{}', uuid: SecureRandom.uuid) + end + + let(:vs_code_setting_two) do + vs_code_settings.create!(user_id: user.id, setting_type: 'tasks', content: '{}') + end + + it 'populates uuid column with a generated uuid' do + expect(vs_code_setting_one.uuid).not_to be_nil + expect(vs_code_setting_two.uuid).to be_nil + + previous_uuid = vs_code_setting_one.uuid + + migration.perform + + expect(vs_code_setting_one.reload.uuid).to eq(previous_uuid) + expect(vs_code_setting_two.reload.uuid).not_to be_nil + end + end + end +end diff --git a/spec/lib/gitlab/ci/config/entry/auto_cancel_spec.rb b/spec/lib/gitlab/ci/config/entry/auto_cancel_spec.rb index dff96fc6787..bdd66cc00a1 100644 --- a/spec/lib/gitlab/ci/config/entry/auto_cancel_spec.rb +++ b/spec/lib/gitlab/ci/config/entry/auto_cancel_spec.rb @@ -30,6 +30,33 @@ RSpec.describe Gitlab::Ci::Config::Entry::AutoCancel, feature_category: :pipelin end end + context 'with on_job_failure' do + ['all', 'none', nil].each do |value| + context 'when the `on_job_failure` value is valid' do + let(:config_hash) { { on_job_failure: value } } + + it { is_expected.to be_valid } + + it 'returns value correctly' do + expect(config.value).to eq(on_job_failure: value) + end + end + end + + context 'when on_job_failure is invalid' do + let(:config_hash) do + { on_job_failure: 'invalid' } + end + + it { is_expected.not_to be_valid } + + it 'returns errors' do + expect(config.errors) + .to include('auto cancel on job failure must be one of: none, all') + end + end + end + context 'with invalid key' do let(:config_hash) do { invalid: 'interruptible' } diff --git a/spec/lib/gitlab/ci/config/entry/workflow_spec.rb b/spec/lib/gitlab/ci/config/entry/workflow_spec.rb index 632072534ab..d3ce3ffe641 100644 --- a/spec/lib/gitlab/ci/config/entry/workflow_spec.rb +++ b/spec/lib/gitlab/ci/config/entry/workflow_spec.rb @@ -123,7 +123,8 @@ RSpec.describe Gitlab::Ci::Config::Entry::Workflow, feature_category: :pipeline_ let(:workflow_hash) do { auto_cancel: { - on_new_commit: 'interruptible' + on_new_commit: 'interruptible', + on_job_failure: 'none' } } end diff --git a/spec/lib/gitlab/ci/pipeline/chain/populate_metadata_spec.rb b/spec/lib/gitlab/ci/pipeline/chain/populate_metadata_spec.rb index d6277c91b2d..732748d8c8b 100644 --- a/spec/lib/gitlab/ci/pipeline/chain/populate_metadata_spec.rb +++ b/spec/lib/gitlab/ci/pipeline/chain/populate_metadata_spec.rb @@ -133,9 +133,10 @@ RSpec.describe Gitlab::Ci::Pipeline::Chain::PopulateMetadata, feature_category: end context 'with auto_cancel' do - let(:config) do - { workflow: { auto_cancel: { on_new_commit: 'interruptible' } }, rspec: { script: 'rspec' } } - end + let(:on_new_commit) { 'interruptible' } + let(:on_job_failure) { 'all' } + let(:auto_cancel) { { on_new_commit: on_new_commit, on_job_failure: on_job_failure } } + let(:config) { { workflow: { auto_cancel: auto_cancel }, rspec: { script: 'rspec' } } } it_behaves_like 'not breaking the chain' @@ -143,6 +144,7 @@ RSpec.describe Gitlab::Ci::Pipeline::Chain::PopulateMetadata, feature_category: run_chain expect(pipeline.pipeline_metadata.auto_cancel_on_new_commit).to eq('interruptible') + expect(pipeline.pipeline_metadata.auto_cancel_on_job_failure).to eq('all') expect(pipeline.pipeline_metadata).not_to be_persisted end @@ -155,20 +157,89 @@ RSpec.describe Gitlab::Ci::Pipeline::Chain::PopulateMetadata, feature_category: end context 'with auto_cancel: nil' do - let(:config) do - { workflow: { auto_cancel: nil }, rspec: { script: 'rspec' } } - end + let(:auto_cancel) { nil } it_behaves_like 'not saving pipeline metadata' end - context 'with auto_cancel_on_new_commit: nil' do - let(:config) do - { workflow: { auto_cancel: { on_new_commit: nil } }, rspec: { script: 'rspec' } } + context 'with auto_cancel_on_new_commit and no auto_cancel_on_job_failure' do + let(:auto_cancel) { { on_new_commit: on_new_commit } } + + it 'builds pipeline_metadata' do + run_chain + + expect(pipeline.pipeline_metadata.auto_cancel_on_new_commit).to eq('interruptible') + expect(pipeline.pipeline_metadata.auto_cancel_on_job_failure).to eq('none') + expect(pipeline.pipeline_metadata).not_to be_persisted end + end + + context 'with auto_cancel_on_job_failure and no auto_cancel_on_new_commit' do + let(:auto_cancel) { { on_job_failure: on_job_failure } } + + it 'builds pipeline_metadata' do + run_chain + + expect(pipeline.pipeline_metadata.auto_cancel_on_new_commit).to eq('conservative') + expect(pipeline.pipeline_metadata.auto_cancel_on_job_failure).to eq('all') + expect(pipeline.pipeline_metadata).not_to be_persisted + end + end + + context 'with auto_cancel_on_new_commit: nil and auto_cancel_on_job_failure: nil' do + let(:on_new_commit) { nil } + let(:on_job_failure) { nil } it_behaves_like 'not saving pipeline metadata' end + + context 'with auto_cancel_on_new_commit valid and auto_cancel_on_job_failure: nil' do + let(:on_job_failure) { nil } + + it 'builds pipeline_metadata' do + run_chain + + expect(pipeline.pipeline_metadata.auto_cancel_on_new_commit).to eq('interruptible') + expect(pipeline.pipeline_metadata.auto_cancel_on_job_failure).to eq('none') + expect(pipeline.pipeline_metadata).not_to be_persisted + end + end + + context 'with auto_cancel_on_new_commit: nil and auto_cancel_on_job_failure valid' do + let(:on_new_commit) { nil } + + it 'builds pipeline_metadata' do + run_chain + + expect(pipeline.pipeline_metadata.auto_cancel_on_new_commit).to eq('conservative') + expect(pipeline.pipeline_metadata.auto_cancel_on_job_failure).to eq('all') + expect(pipeline.pipeline_metadata).not_to be_persisted + end + end + + context 'when auto_cancel_on_job_failure: none' do + let(:on_job_failure) { 'none' } + + it 'builds pipeline_metadata' do + run_chain + + expect(pipeline.pipeline_metadata.auto_cancel_on_job_failure).to eq('none') + expect(pipeline.pipeline_metadata).not_to be_persisted + end + end + + context 'when auto_cancel_pipeline_on_job_failure feature is disabled' do + before do + stub_feature_flags(auto_cancel_pipeline_on_job_failure: false) + end + + it 'ignores the auto_cancel_on_job_failure value' do + run_chain + + expect(pipeline.pipeline_metadata.auto_cancel_on_job_failure).to eq('none') + expect(pipeline.pipeline_metadata).not_to be_persisted + end + end end context 'with both pipeline name and auto_cancel' do @@ -176,7 +247,10 @@ RSpec.describe Gitlab::Ci::Pipeline::Chain::PopulateMetadata, feature_category: { workflow: { name: 'Pipeline name', - auto_cancel: { on_new_commit: 'interruptible' } + auto_cancel: { + on_new_commit: 'interruptible', + on_job_failure: 'none' + } }, rspec: { script: 'rspec' } } @@ -189,6 +263,7 @@ RSpec.describe Gitlab::Ci::Pipeline::Chain::PopulateMetadata, feature_category: expect(pipeline.pipeline_metadata.name).to eq('Pipeline name') expect(pipeline.pipeline_metadata.auto_cancel_on_new_commit).to eq('interruptible') + expect(pipeline.pipeline_metadata.auto_cancel_on_job_failure).to eq('none') expect(pipeline.pipeline_metadata).not_to be_persisted end end diff --git a/spec/lib/gitlab/ci/yaml_processor_spec.rb b/spec/lib/gitlab/ci/yaml_processor_spec.rb index 3ba62888b7c..844a6849c8f 100644 --- a/spec/lib/gitlab/ci/yaml_processor_spec.rb +++ b/spec/lib/gitlab/ci/yaml_processor_spec.rb @@ -502,6 +502,7 @@ module Gitlab workflow: auto_cancel: on_new_commit: interruptible + on_job_failure: all hello: script: echo world @@ -509,7 +510,10 @@ module Gitlab end it 'parses the workflow:auto_cancel as workflow_auto_cancel' do - expect(subject.workflow_auto_cancel).to eq(on_new_commit: 'interruptible') + expect(subject.workflow_auto_cancel).to eq({ + on_new_commit: 'interruptible', + on_job_failure: 'all' + }) end end end diff --git a/spec/migrations/20231130140901_queue_backfill_vs_code_settings_uuid_spec.rb b/spec/migrations/20231130140901_queue_backfill_vs_code_settings_uuid_spec.rb new file mode 100644 index 00000000000..3e697d6b1f3 --- /dev/null +++ b/spec/migrations/20231130140901_queue_backfill_vs_code_settings_uuid_spec.rb @@ -0,0 +1,26 @@ +# frozen_string_literal: true + +require 'spec_helper' +require_migration! + +RSpec.describe QueueBackfillVsCodeSettingsUuid, feature_category: :web_ide do + let!(:batched_migration) { described_class::MIGRATION } + + it 'schedules a new batched migration' do + reversible_migration do |migration| + migration.before -> { + expect(batched_migration).not_to have_scheduled_batched_migration + } + + migration.after -> { + expect(batched_migration).to have_scheduled_batched_migration( + table_name: :vs_code_settings, + column_name: :id, + interval: described_class::DELAY_INTERVAL, + batch_size: described_class::BATCH_SIZE, + sub_batch_size: described_class::SUB_BATCH_SIZE + ) + } + end + end +end diff --git a/spec/models/ci/build_spec.rb b/spec/models/ci/build_spec.rb index 4a71b34391c..18c7e57d464 100644 --- a/spec/models/ci/build_spec.rb +++ b/spec/models/ci/build_spec.rb @@ -987,24 +987,6 @@ RSpec.describe Ci::Build, feature_category: :continuous_integration, factory_def describe '#artifacts_public?' do subject { build.artifacts_public? } - context 'when non_public_artifacts flag is disabled' do - before do - stub_feature_flags(non_public_artifacts: false) - end - - context 'artifacts with defaults - public' do - let(:build) { create(:ci_build, :artifacts, pipeline: pipeline) } - - it { is_expected.to be_truthy } - end - - context 'non public artifacts' do - let(:build) { create(:ci_build, :private_artifacts, pipeline: pipeline) } - - it { is_expected.to be_truthy } - end - end - context 'artifacts with defaults - public' do let(:build) { create(:ci_build, :artifacts, pipeline: pipeline) } @@ -1027,30 +1009,6 @@ RSpec.describe Ci::Build, feature_category: :continuous_integration, factory_def describe '#artifact_is_public_in_config?' do subject { build.artifact_is_public_in_config? } - context 'when non_public_artifacts flag is disabled' do - before do - stub_feature_flags(non_public_artifacts: false) - end - - context 'artifacts with defaults' do - let(:build) { create(:ci_build, :artifacts, pipeline: pipeline) } - - it { is_expected.to be_truthy } - end - - context 'non public artifacts' do - let(:build) { create(:ci_build, :with_private_artifacts_config, pipeline: pipeline) } - - it { is_expected.to be_truthy } - end - - context 'public artifacts' do - let(:build) { create(:ci_build, :with_public_artifacts_config, pipeline: pipeline) } - - it { is_expected.to be_truthy } - end - end - context 'artifacts with defaults' do let(:build) { create(:ci_build, :artifacts, pipeline: pipeline) } @@ -5270,7 +5228,7 @@ RSpec.describe Ci::Build, feature_category: :continuous_integration, factory_def describe '.with_project_and_metadata' do it 'does not join across databases' do with_cross_joins_prevented do - ::Ci::Build.with_project_and_metadata(project).to_a + ::Ci::Build.with_project_and_metadata.to_a end end end diff --git a/spec/models/ci/job_artifact_spec.rb b/spec/models/ci/job_artifact_spec.rb index 48d46824c11..e65c1e2f577 100644 --- a/spec/models/ci/job_artifact_spec.rb +++ b/spec/models/ci/job_artifact_spec.rb @@ -176,16 +176,6 @@ RSpec.describe Ci::JobArtifact, feature_category: :build_artifacts do let!(:artifact) { build(:ci_job_artifact, :private) } it { is_expected.to be_falsey } - - context 'and the non_public_artifacts feature flag is disabled' do - let!(:artifact) { build(:ci_job_artifact, :private) } - - before do - stub_feature_flags(non_public_artifacts: false) - end - - it { is_expected.to be_truthy } - end end end diff --git a/spec/models/ci/pipeline_metadata_spec.rb b/spec/models/ci/pipeline_metadata_spec.rb index 2db7a2c23e9..1a426118063 100644 --- a/spec/models/ci/pipeline_metadata_spec.rb +++ b/spec/models/ci/pipeline_metadata_spec.rb @@ -18,5 +18,13 @@ RSpec.describe Ci::PipelineMetadata, feature_category: :pipeline_composition do conservative: 0, interruptible: 1, disabled: 2 ).with_prefix end + + it do + is_expected.to define_enum_for( + :auto_cancel_on_job_failure + ).with_values( + none: 0, all: 1 + ).with_prefix + end end end diff --git a/spec/models/ci/pipeline_spec.rb b/spec/models/ci/pipeline_spec.rb index c03ad704cb8..9f504833494 100644 --- a/spec/models/ci/pipeline_spec.rb +++ b/spec/models/ci/pipeline_spec.rb @@ -86,6 +86,7 @@ RSpec.describe Ci::Pipeline, :mailer, factory_default: :keep, feature_category: it { is_expected.to respond_to :short_sha } it { is_expected.to delegate_method(:full_path).to(:project).with_prefix } it { is_expected.to delegate_method(:name).to(:pipeline_metadata).allow_nil } + it { is_expected.to delegate_method(:auto_cancel_on_job_failure).to(:pipeline_metadata).allow_nil } describe 'validations' do it { is_expected.to validate_presence_of(:sha) } diff --git a/spec/requests/api/ci/job_artifacts_spec.rb b/spec/requests/api/ci/job_artifacts_spec.rb index 9fb3f7e85ba..a8c09a5191d 100644 --- a/spec/requests/api/ci/job_artifacts_spec.rb +++ b/spec/requests/api/ci/job_artifacts_spec.rb @@ -197,21 +197,6 @@ RSpec.describe API::Ci::JobArtifacts, feature_category: :build_artifacts do expect(response).to have_gitlab_http_status(:forbidden) end - - context 'with the non_public_artifacts feature flag disabled' do - before do - stub_feature_flags(non_public_artifacts: false) - end - - it 'allows access to artifacts' do - project.update_column(:visibility_level, Gitlab::VisibilityLevel::PUBLIC) - project.update_column(:public_builds, true) - - get_artifact_file(artifact) - - expect(response).to have_gitlab_http_status(:ok) - end - end end context 'when project is public with builds access disabled' do @@ -445,17 +430,6 @@ RSpec.describe API::Ci::JobArtifacts, feature_category: :build_artifacts do it 'rejects access and hides existence of artifacts' do expect(response).to have_gitlab_http_status(:forbidden) end - - context 'with the non_public_artifacts feature flag disabled' do - before do - stub_feature_flags(non_public_artifacts: false) - get api("/projects/#{project.id}/jobs/#{job.id}/artifacts", api_user) - end - - it 'allows access to artifacts' do - expect(response).to have_gitlab_http_status(:ok) - end - end end it 'does not return job artifacts if not uploaded' do @@ -651,18 +625,6 @@ RSpec.describe API::Ci::JobArtifacts, feature_category: :build_artifacts do expect(response.headers.to_h) .not_to include('Gitlab-Workhorse-Send-Data' => /artifacts-entry/) end - - context 'with the non_public_artifacts feature flag disabled' do - before do - stub_feature_flags(non_public_artifacts: false) - end - - it 'allows access to artifacts', :sidekiq_might_not_need_inline do - get_artifact_file(artifact) - - expect(response).to have_gitlab_http_status(:ok) - end - end end context 'when project is private' do diff --git a/spec/requests/projects/merge_requests/content_spec.rb b/spec/requests/projects/merge_requests/content_spec.rb index 54066756f3e..1ecad609416 100644 --- a/spec/requests/projects/merge_requests/content_spec.rb +++ b/spec/requests/projects/merge_requests/content_spec.rb @@ -29,13 +29,5 @@ RSpec.describe 'merge request content spec', feature_category: :code_review_work describe 'GET cached_widget' do it_behaves_like 'cached widget request' - - context 'with non_public_artifacts disabled' do - before do - stub_feature_flags(non_public_artifacts: false) - end - - it_behaves_like 'cached widget request' - end end end diff --git a/spec/serializers/build_details_entity_spec.rb b/spec/serializers/build_details_entity_spec.rb index 9ef1e91ebcd..a899a798fa0 100644 --- a/spec/serializers/build_details_entity_spec.rb +++ b/spec/serializers/build_details_entity_spec.rb @@ -286,16 +286,6 @@ RSpec.describe BuildDetailsEntity do it 'does not expose non public artifacts' do expect(subject.keys).not_to include(:artifact) end - - context 'with the non_public_artifacts feature flag disabled' do - before do - stub_feature_flags(non_public_artifacts: false) - end - - it 'exposes artifact details' do - expect(subject[:artifact].keys).to include(:download_path, :browse_path, :locked) - end - end end end diff --git a/spec/services/ci/create_pipeline_service/workflow_auto_cancel_spec.rb b/spec/services/ci/create_pipeline_service/workflow_auto_cancel_spec.rb index 9737b85d654..851c6f8fbea 100644 --- a/spec/services/ci/create_pipeline_service/workflow_auto_cancel_spec.rb +++ b/spec/services/ci/create_pipeline_service/workflow_auto_cancel_spec.rb @@ -14,49 +14,156 @@ RSpec.describe Ci::CreatePipelineService, :yaml_processor_feature_flag_corectnes stub_ci_pipeline_yaml_file(config) end - context 'when on_new_commit is set to interruptible' do - let(:config) do - <<~YAML - workflow: - auto_cancel: - on_new_commit: interruptible - - test1: - script: exit 0 - YAML - end + describe 'on_new_commit' do + context 'when is set to interruptible' do + let(:config) do + <<~YAML + workflow: + auto_cancel: + on_new_commit: interruptible + + test1: + script: exit 0 + YAML + end + + before do + stub_ci_pipeline_yaml_file(config) + end - before do - stub_ci_pipeline_yaml_file(config) + it 'creates a pipeline with on_new_commit' do + expect(pipeline).to be_persisted + expect(pipeline.errors).to be_empty + expect(pipeline.pipeline_metadata.auto_cancel_on_new_commit).to eq('interruptible') + end end - it 'creates a pipeline with on_new_commit' do - expect(pipeline).to be_persisted - expect(pipeline.errors).to be_empty - expect(pipeline.pipeline_metadata.auto_cancel_on_new_commit).to eq('interruptible') + context 'when is set to invalid' do + let(:config) do + <<~YAML + workflow: + auto_cancel: + on_new_commit: invalid + + test1: + script: exit 0 + YAML + end + + before do + stub_ci_pipeline_yaml_file(config) + end + + it 'creates a pipeline with errors' do + expect(pipeline).to be_persisted + expect(pipeline.errors.full_messages).to include( + 'workflow:auto_cancel on new commit must be one of: conservative, interruptible, disabled') + end end end - context 'when on_new_commit is set to invalid' do - let(:config) do - <<~YAML - workflow: - auto_cancel: - on_new_commit: invalid + describe 'on_job_failure' do + context 'when is set to none' do + let(:config) do + <<~YAML + workflow: + auto_cancel: + on_job_failure: none + + test1: + script: exit 0 + YAML + end + + before do + stub_ci_pipeline_yaml_file(config) + end - test1: - script: exit 0 - YAML + it 'creates a pipeline with on_job_failure' do + expect(pipeline).to be_persisted + expect(pipeline.errors).to be_empty + expect(pipeline.pipeline_metadata.auto_cancel_on_job_failure).to eq('none') + end end - before do - stub_ci_pipeline_yaml_file(config) + context 'when is set to all' do + let(:config) do + <<~YAML + workflow: + auto_cancel: + on_job_failure: all + + test1: + script: exit 0 + YAML + end + + before do + stub_ci_pipeline_yaml_file(config) + end + + it 'creates a pipeline with on_job_failure' do + expect(pipeline).to be_persisted + expect(pipeline.errors).to be_empty + expect(pipeline.pipeline_metadata.auto_cancel_on_job_failure).to eq('all') + end + + context 'when auto_cancel_pipeline_on_job_failure feature flag is disabled' do + before do + stub_feature_flags(auto_cancel_pipeline_on_job_failure: false) + end + + context 'when there are no other metadata settings present' do + it 'creates a pipeline without metadata' do + expect(pipeline).to be_persisted + expect(pipeline.errors).to be_empty + expect(pipeline.pipeline_metadata).to be_nil + end + end + + context 'when other metadata settings are present' do + let(:config) do + <<~YAML + workflow: + name: pipeline_name + auto_cancel: + on_job_failure: all + + test1: + script: exit 0 + YAML + end + + it 'creates a pipeline with on_job_failure' do + expect(pipeline).to be_persisted + expect(pipeline.errors).to be_empty + expect(pipeline.pipeline_metadata.auto_cancel_on_job_failure).to eq('none') + end + end + end end - it 'creates a pipeline with errors' do - expect(pipeline).to be_persisted - expect(pipeline.errors.full_messages).to include( - 'workflow:auto_cancel on new commit must be one of: conservative, interruptible, disabled') + context 'when on_job_failure is set to invalid' do + let(:config) do + <<~YAML + workflow: + auto_cancel: + on_job_failure: invalid + + test1: + script: exit 0 + YAML + end + + before do + stub_ci_pipeline_yaml_file(config) + end + + it 'creates a pipeline with errors' do + expect(pipeline).to be_persisted + expect(pipeline.errors.full_messages).to include( + 'workflow:auto_cancel on job failure must be one of: none, all') + end end end end diff --git a/spec/services/ci/job_artifacts/create_service_spec.rb b/spec/services/ci/job_artifacts/create_service_spec.rb index ffb6bd9d145..0d6a15b0ea3 100644 --- a/spec/services/ci/job_artifacts/create_service_spec.rb +++ b/spec/services/ci/job_artifacts/create_service_spec.rb @@ -155,52 +155,6 @@ RSpec.describe Ci::JobArtifacts::CreateService, :clean_gitlab_redis_shared_state end end - context 'when non_public_artifacts flag is disabled' do - before do - stub_feature_flags(non_public_artifacts: false) - end - - it_behaves_like 'public accessibility' - end - - context 'when non_public_artifacts flag is enabled' do - context 'and accessibility is defined in the params' do - context 'and is passed as private' do - before do - params.merge!('accessibility' => 'private') - end - - it_behaves_like 'private accessibility' - end - - context 'and is passed as public' do - before do - params.merge!('accessibility' => 'public') - end - - it_behaves_like 'public accessibility' - end - end - - context 'and accessibility is not defined in the params' do - context 'and job has no public artifacts defined in its CI config' do - it_behaves_like 'public accessibility' - end - - context 'and job artifacts defined as private in the CI config' do - let(:job) { create(:ci_build, :with_private_artifacts_config, project: project) } - - it_behaves_like 'private accessibility' - end - - context 'and job artifacts defined as public in the CI config' do - let(:job) { create(:ci_build, :with_public_artifacts_config, project: project) } - - it_behaves_like 'public accessibility' - end - end - end - context 'when accessibility passed as invalid value' do before do params.merge!('accessibility' => 'foo') diff --git a/spec/workers/click_house/events_sync_worker_spec.rb b/spec/workers/click_house/events_sync_worker_spec.rb index da74e5e376d..9662f26115a 100644 --- a/spec/workers/click_house/events_sync_worker_spec.rb +++ b/spec/workers/click_house/events_sync_worker_spec.rb @@ -151,7 +151,7 @@ RSpec.describe ClickHouse::EventsSyncWorker, feature_category: :value_stream_man context 'when clickhouse is not configured' do before do - allow(ClickHouse::Client.configuration).to receive(:databases).and_return({}) + allow(ClickHouse::Client).to receive(:database_configured?).and_return(false) end it 'skips execution' do @@ -165,7 +165,7 @@ RSpec.describe ClickHouse::EventsSyncWorker, feature_category: :value_stream_man context 'when exclusive lease error happens' do it 'skips execution' do stub_feature_flags(event_sync_worker_for_click_house: true) - allow(ClickHouse::Client.configuration).to receive(:databases).and_return({ main: :some_db }) + allow(ClickHouse::Client).to receive(:database_configured?).with(:main).and_return(true) expect(worker).to receive(:in_lock).and_raise(Gitlab::ExclusiveLeaseHelpers::FailedToObtainLockError) expect(worker).to receive(:log_extra_metadata_on_done).with(:result, { status: :skipped }) |