diff options
author | GitLab Bot <gitlab-bot@gitlab.com> | 2023-02-28 12:14:07 +0000 |
---|---|---|
committer | GitLab Bot <gitlab-bot@gitlab.com> | 2023-02-28 12:14:07 +0000 |
commit | 22ecb1e3fc02bb923c3e9941b1baa849348a036f (patch) | |
tree | c01d9e91564f50e790a63c71675dd0f6e7735153 /spec | |
parent | 5eab6dcdd923ca375b86d6993f20a3e37dbd7a51 (diff) | |
download | gitlab-ce-22ecb1e3fc02bb923c3e9941b1baa849348a036f.tar.gz |
Add latest changes from gitlab-org/gitlab@master
Diffstat (limited to 'spec')
21 files changed, 691 insertions, 88 deletions
diff --git a/spec/controllers/projects/notes_controller_spec.rb b/spec/controllers/projects/notes_controller_spec.rb index 96006974c13..5e4e47be2c5 100644 --- a/spec/controllers/projects/notes_controller_spec.rb +++ b/spec/controllers/projects/notes_controller_spec.rb @@ -436,6 +436,13 @@ RSpec.describe Projects::NotesController, type: :controller, feature_category: : expect(json_response['commands_changes']).to include('emoji_award', 'time_estimate', 'spend_time') expect(json_response['commands_changes']).not_to include('target_project', 'title') end + + it 'includes command_names' do + create! + + expect(response).to have_gitlab_http_status(:ok) + expect(json_response['command_names']).to include('award', 'estimate', 'spend') + end end context 'with commands that do not return changes' do @@ -454,6 +461,13 @@ RSpec.describe Projects::NotesController, type: :controller, feature_category: : expect(response).to have_gitlab_http_status(:ok) expect(json_response['commands_changes']).not_to include('target_project', 'title') end + + it 'includes command_names' do + create! + + expect(response).to have_gitlab_http_status(:ok) + expect(json_response['command_names']).to include('move', 'title') + end end end end diff --git a/spec/features/groups/new_group_page_spec.rb b/spec/features/groups/new_group_page_spec.rb index 4670df3fb5e..6d9513ce84f 100644 --- a/spec/features/groups/new_group_page_spec.rb +++ b/spec/features/groups/new_group_page_spec.rb @@ -30,16 +30,42 @@ RSpec.describe 'New group page', :js, feature_category: :subgroups do end describe 'sidebar' do - context 'for a new top-level group' do - it_behaves_like 'a dashboard page with sidebar', :new_group_path, :groups + context 'in the current navigation' do + before do + user.update!(use_new_navigation: false) + end + + context 'for a new top-level group' do + it_behaves_like 'a dashboard page with sidebar', :new_group_path, :groups + end + + context 'for a new subgroup' do + it 'shows the group sidebar of the parent group' do + visit new_group_path(parent_id: parent_group.id, anchor: 'create-group-pane') + expect(page).to have_selector( + ".nav-sidebar[aria-label=\"Group navigation\"] .context-header[title=\"#{parent_group.name}\"]" + ) + end + end end - context 'for a new subgroup' do - it 'shows the group sidebar of the parent group' do - visit new_group_path(parent_id: parent_group.id, anchor: 'create-group-pane') - expect(page).to have_selector( - ".nav-sidebar[aria-label=\"Group navigation\"] .context-header[title=\"#{parent_group.name}\"]" - ) + context 'in the new navigation' do + before do + user.update!(use_new_navigation: true) + end + + context 'for a new top-level group' do + it 'shows the "Your work" navigation' do + visit new_group_path + expect(page).to have_selector(".super-sidebar .context-switcher-toggle", text: "Your work") + end + end + + context 'for a new subgroup' do + it 'shows the group navigation of the parent group' do + visit new_group_path(parent_id: parent_group.id, anchor: 'create-group-pane') + expect(page).to have_selector(".super-sidebar .context-switcher-toggle", text: parent_group.name) + end end end end diff --git a/spec/features/projects/new_project_spec.rb b/spec/features/projects/new_project_spec.rb index 3cbfa14208f..439ae4275ae 100644 --- a/spec/features/projects/new_project_spec.rb +++ b/spec/features/projects/new_project_spec.rb @@ -588,14 +588,42 @@ RSpec.describe 'New project', :js, feature_category: :projects do sign_in(user) end - context 'for a new top-level project' do - it_behaves_like 'a dashboard page with sidebar', :new_project_path, :projects + context 'in the current navigation' do + before do + user.update!(use_new_navigation: false) + end + + context 'for a new top-level project' do + it_behaves_like 'a dashboard page with sidebar', :new_project_path, :projects + end + + context 'for a new group project' do + it 'shows the group sidebar of the parent group' do + visit new_project_path(namespace_id: parent_group.id) + expect(page).to have_selector(".nav-sidebar[aria-label=\"Group navigation\"] .context-header[title=\"#{parent_group.name}\"]") + end + end end - context 'for a new group project' do - it 'shows the group sidebar of the parent group' do - visit new_project_path(namespace_id: parent_group.id) - expect(page).to have_selector(".nav-sidebar[aria-label=\"Group navigation\"] .context-header[title=\"#{parent_group.name}\"]") + context 'in the new navigation' do + before do + parent_group.add_owner(user) + user.update!(use_new_navigation: true) + sign_in(user) + end + + context 'for a new top-level project' do + it 'shows the "Your work" navigation' do + visit new_project_path + expect(page).to have_selector(".super-sidebar .context-switcher-toggle", text: "Your work") + end + end + + context 'for a new group project' do + it 'shows the group sidebar of the parent group' do + visit new_project_path(namespace_id: parent_group.id) + expect(page).to have_selector(".super-sidebar .context-switcher-toggle", text: parent_group.name) + end end end end diff --git a/spec/frontend/issues/create_merge_request_dropdown_spec.js b/spec/frontend/issues/create_merge_request_dropdown_spec.js index cc2ee84348a..21ae844e2dd 100644 --- a/spec/frontend/issues/create_merge_request_dropdown_spec.js +++ b/spec/frontend/issues/create_merge_request_dropdown_spec.js @@ -65,6 +65,14 @@ describe('CreateMergeRequestDropdown', () => { expect(dropdown.createMrPath).toBe( `${TEST_HOST}/create_merge_request?merge_request%5Bsource_branch%5D=contains%23hash&merge_request%5Btarget_branch%5D=master&merge_request%5Bissue_iid%5D=42`, ); + + expect(dropdown.wrapperEl.dataset.createBranchPath).toBe( + `${TEST_HOST}/branches?branch_name=contains%23hash&issue=42`, + ); + + expect(dropdown.wrapperEl.dataset.createMrPath).toBe( + `${TEST_HOST}/create_merge_request?merge_request%5Bsource_branch%5D=contains%23hash&merge_request%5Btarget_branch%5D=master&merge_request%5Bissue_iid%5D=42`, + ); }); }); diff --git a/spec/helpers/sidebars_helper_spec.rb b/spec/helpers/sidebars_helper_spec.rb index aa6b1dba6e8..1afe4efaf22 100644 --- a/spec/helpers/sidebars_helper_spec.rb +++ b/spec/helpers/sidebars_helper_spec.rb @@ -192,7 +192,7 @@ RSpec.describe SidebarsHelper, feature_category: :navigation do before do allow(helper).to receive(:project_sidebar_context_data).and_return( - { current_user: nil, container: project, can_view_pipeline_editor: false }) + { current_user: nil, container: project, can_view_pipeline_editor: false, learn_gitlab_enabled: false }) allow(helper).to receive(:group_sidebar_context_data).and_return({ current_user: nil, container: group }) allow(group).to receive(:to_global_id).and_return(5) @@ -204,7 +204,7 @@ RSpec.describe SidebarsHelper, feature_category: :navigation do end it 'returns Project Panel for project nav' do - expect(helper.super_sidebar_nav_panel(nav: 'project')).to be_a(Sidebars::Projects::Panel) + expect(helper.super_sidebar_nav_panel(nav: 'project')).to be_a(Sidebars::Projects::SuperSidebarPanel) end it 'returns Group Panel for group nav' do diff --git a/spec/lib/gitlab/ci/config/entry/job_spec.rb b/spec/lib/gitlab/ci/config/entry/job_spec.rb index 4773c0b5e1e..c8b4a8b8a0e 100644 --- a/spec/lib/gitlab/ci/config/entry/job_spec.rb +++ b/spec/lib/gitlab/ci/config/entry/job_spec.rb @@ -728,27 +728,6 @@ RSpec.describe Gitlab::Ci::Config::Entry::Job, feature_category: :pipeline_compo scheduling_type: :stage, id_tokens: { TEST_ID_TOKEN: { aud: 'https://gitlab.com' } }) end - - context 'when the FF ci_hooks_pre_get_sources_script is disabled' do - before do - stub_feature_flags(ci_hooks_pre_get_sources_script: false) - end - - it 'returns correct value' do - expect(entry.value) - .to eq(name: :rspec, - before_script: %w[ls pwd], - script: %w[rspec], - stage: 'test', - ignore: false, - after_script: %w[cleanup], - only: { refs: %w[branches tags] }, - job_variables: {}, - root_variables_inheritance: true, - scheduling_type: :stage, - id_tokens: { TEST_ID_TOKEN: { aud: 'https://gitlab.com' } }) - end - end end end diff --git a/spec/lib/gitlab/database/migrations/test_batched_background_runner_spec.rb b/spec/lib/gitlab/database/migrations/test_batched_background_runner_spec.rb index 57c5011590c..6bcefa455cf 100644 --- a/spec/lib/gitlab/database/migrations/test_batched_background_runner_spec.rb +++ b/spec/lib/gitlab/database/migrations/test_batched_background_runner_spec.rb @@ -48,6 +48,7 @@ RSpec.describe Gitlab::Database::Migrations::TestBatchedBackgroundRunner, :freez let(:result_dir) { Pathname.new(Dir.mktmpdir) } let(:connection) { base_model.connection } let(:table_name) { "_test_column_copying" } + let(:num_rows_in_table) { 1000 } let(:from_id) { 0 } after do @@ -61,7 +62,7 @@ RSpec.describe Gitlab::Database::Migrations::TestBatchedBackgroundRunner, :freez data bigint default 0 ); - insert into #{table_name} (id) select i from generate_series(1, 1000) g(i); + insert into #{table_name} (id) select i from generate_series(1, #{num_rows_in_table}) g(i); SQL end @@ -134,6 +135,24 @@ RSpec.describe Gitlab::Database::Migrations::TestBatchedBackgroundRunner, :freez expect(calls).not_to be_empty end + it 'samples 1 job with a batch size higher than the table size' do + calls = [] + define_background_migration(migration_name) do |*args| + travel 1.minute + calls << args + end + + queue_migration(migration_name, table_name, :id, + job_interval: 5.minutes, + batch_size: num_rows_in_table * 2, + sub_batch_size: num_rows_in_table * 2) + + described_class.new(result_dir: result_dir, connection: connection, + from_id: from_id).run_jobs(for_duration: 3.minutes) + + expect(calls.size).to eq(1) + end + context 'with multiple jobs to run' do let(:last_id) do Gitlab::Database::SharedModel.using_connection(connection) do diff --git a/spec/lib/sidebars/concerns/super_sidebar_panel_spec.rb b/spec/lib/sidebars/concerns/super_sidebar_panel_spec.rb new file mode 100644 index 00000000000..55598948271 --- /dev/null +++ b/spec/lib/sidebars/concerns/super_sidebar_panel_spec.rb @@ -0,0 +1,141 @@ +# frozen_string_literal: true + +require 'fast_spec_helper' + +RSpec.describe Sidebars::Concerns::SuperSidebarPanel, feature_category: :navigation do + let(:menu_class_foo) { Class.new(Sidebars::Menu) } + let(:menu_foo) { menu_class_foo.new({}) } + + let(:menu_class_bar) do + Class.new(Sidebars::Menu) do + def title + "Bar" + end + end + end + + let(:menu_bar) { menu_class_bar.new({}) } + + subject do + Class.new(Sidebars::Panel) do + include Sidebars::Concerns::SuperSidebarPanel + end.new({}) + end + + before do + allow(menu_foo).to receive(:render?).and_return(true) + allow(menu_bar).to receive(:render?).and_return(true) + end + + describe '#pick_from_old_menus' do + it 'removes element of a given class from a list and adds it to menus' do + old_menus = [menu_foo, menu_bar] + + subject.pick_from_old_menus(old_menus, menu_class_foo) + + expect(old_menus).not_to include(menu_foo) + expect(subject.renderable_menus).to include(menu_foo) + end + + it 'is a noop, if the list does not contain an element of the wanted class' do + old_menus = [menu_foo] + + subject.pick_from_old_menus(old_menus, menu_class_bar) + + expect(old_menus).to eq([menu_foo]) + expect(subject.renderable_menus).to eq([]) + end + end + + describe '#transform_old_menus' do + let(:uncategorized_menu) { ::Sidebars::UncategorizedMenu.new({}) } + + let(:menu_item) do + Sidebars::MenuItem.new(title: 'foo3', link: 'foo3', active_routes: { controller: 'barc' }, + super_sidebar_parent: menu_class_foo) + end + + let(:nil_menu_item) { Sidebars::NilMenuItem.new(item_id: :nil_item) } + let(:existing_item) do + Sidebars::MenuItem.new( + item_id: :exists, + title: 'Existing item', + link: 'foo2', + active_routes: { controller: 'foo2' } + ) + end + + let(:current_menus) { [menu_foo, uncategorized_menu] } + + before do + allow(menu_bar).to receive(:serialize_as_menu_item_args).and_return(nil) + menu_foo.add_item(existing_item) + end + + context 'for Menus with Menu Items' do + before do + menu_bar.add_item(menu_item) + menu_bar.add_item(nil_menu_item) + end + + it 'adds Menu Items to defined super_sidebar_parent' do + subject.transform_old_menus(current_menus, menu_bar) + + expect(menu_foo.renderable_items).to eq([existing_item, menu_item]) + expect(uncategorized_menu.renderable_items).to eq([]) + end + + it 'adds Menu Items to defined super_sidebar_parent, before super_sidebar_before' do + allow(menu_item).to receive(:super_sidebar_before).and_return(:exists) + subject.transform_old_menus(current_menus, menu_bar) + + expect(menu_foo.renderable_items).to eq([menu_item, existing_item]) + expect(uncategorized_menu.renderable_items).to eq([]) + end + + it 'considers Menu Items uncategorized if super_sidebar_parent is nil' do + allow(menu_item).to receive(:super_sidebar_parent).and_return(nil) + subject.transform_old_menus(current_menus, menu_bar) + + expect(menu_foo.renderable_items).to eq([existing_item]) + expect(uncategorized_menu.renderable_items).to eq([menu_item]) + end + + it 'considers Menu Items uncategorized if super_sidebar_parent cannot be found' do + allow(menu_item).to receive(:super_sidebar_parent).and_return(menu_class_bar) + subject.transform_old_menus(current_menus, menu_bar) + + expect(menu_foo.renderable_items).to eq([existing_item]) + expect(uncategorized_menu.renderable_items).to eq([menu_item]) + end + + it 'considers Menu Items deleted if super_sidebar_parent is Sidebars::NilMenuItem' do + allow(menu_item).to receive(:super_sidebar_parent).and_return(::Sidebars::NilMenuItem) + subject.transform_old_menus(current_menus, menu_bar) + + expect(menu_foo.renderable_items).to eq([existing_item]) + expect(uncategorized_menu.renderable_items).to eq([]) + end + end + + it 'converts "solo" top-level Menu entry to Menu Item' do + allow(Sidebars::MenuItem).to receive(:new).and_return(menu_item) + allow(menu_bar).to receive(:serialize_as_menu_item_args).and_return({}) + + subject.transform_old_menus(current_menus, menu_bar) + + expect(menu_foo.renderable_items).to eq([existing_item, menu_item]) + expect(uncategorized_menu.renderable_items).to eq([]) + end + + it 'drops "solo" top-level Menu entries, if they serialize to nil' do + allow(Sidebars::MenuItem).to receive(:new).and_return(menu_item) + allow(menu_bar).to receive(:serialize_as_menu_item_args).and_return(nil) + + subject.transform_old_menus(current_menus, menu_bar) + + expect(menu_foo.renderable_items).to eq([existing_item]) + expect(uncategorized_menu.renderable_items).to eq([]) + end + end +end diff --git a/spec/lib/sidebars/menu_spec.rb b/spec/lib/sidebars/menu_spec.rb index c84e04a738f..7c4d74aecc8 100644 --- a/spec/lib/sidebars/menu_spec.rb +++ b/spec/lib/sidebars/menu_spec.rb @@ -56,6 +56,22 @@ RSpec.describe Sidebars::Menu, feature_category: :navigation do end end + describe '#serialize_as_menu_item_args' do + it 'returns hash of title, link, active_routes, container_html_options' do + allow(menu).to receive(:title).and_return('Title') + allow(menu).to receive(:active_routes).and_return({ path: 'foo' }) + allow(menu).to receive(:container_html_options).and_return({ class: 'foo' }) + allow(menu).to receive(:link).and_return('/link') + + expect(menu.serialize_as_menu_item_args).to eq({ + title: 'Title', + link: '/link', + active_routes: { path: 'foo' }, + container_html_options: { class: 'foo' } + }) + end + end + describe '#render?' do context 'when the menus has no items' do it 'returns false' do diff --git a/spec/lib/sidebars/projects/panel_spec.rb b/spec/lib/sidebars/projects/panel_spec.rb index a581b982f9f..ec1df438cf1 100644 --- a/spec/lib/sidebars/projects/panel_spec.rb +++ b/spec/lib/sidebars/projects/panel_spec.rb @@ -13,12 +13,6 @@ RSpec.describe Sidebars::Projects::Panel, feature_category: :navigation do expect(subject.scope_menu).to be_a(Sidebars::Projects::Menus::ScopeMenu) end - it 'implements #super_sidebar_context_header' do - expect(subject.super_sidebar_context_header).to eq({ - title: project.name, avatar: project.avatar_url, id: project.id - }) - end - context 'Confluence menu item' do subject { described_class.new(context).instance_variable_get(:@menus) } diff --git a/spec/lib/sidebars/projects/super_sidebar_menus/plan_menu_spec.rb b/spec/lib/sidebars/projects/super_sidebar_menus/plan_menu_spec.rb new file mode 100644 index 00000000000..3917d26f6f2 --- /dev/null +++ b/spec/lib/sidebars/projects/super_sidebar_menus/plan_menu_spec.rb @@ -0,0 +1,12 @@ +# frozen_string_literal: true + +require 'spec_helper' + +RSpec.describe Sidebars::Projects::SuperSidebarMenus::PlanMenu, feature_category: :navigation do + subject { described_class.new({}) } + + it 'has title and sprite_icon' do + expect(subject.title).to eq(_("Plan")) + expect(subject.sprite_icon).to eq("planning") + end +end diff --git a/spec/lib/sidebars/projects/super_sidebar_panel_spec.rb b/spec/lib/sidebars/projects/super_sidebar_panel_spec.rb new file mode 100644 index 00000000000..a4df46ca493 --- /dev/null +++ b/spec/lib/sidebars/projects/super_sidebar_panel_spec.rb @@ -0,0 +1,47 @@ +# frozen_string_literal: true + +require 'spec_helper' + +RSpec.describe Sidebars::Projects::SuperSidebarPanel, feature_category: :navigation do + let_it_be(:project) { create(:project, :repository) } + + let(:user) { project.first_owner } + + let(:context) do + double("Stubbed context", current_user: user, container: project, project: project, current_ref: 'master').as_null_object # rubocop:disable RSpec/VerifiedDoubles + end + + subject { described_class.new(context) } + + it 'implements #super_sidebar_context_header' do + expect(subject.super_sidebar_context_header).to eq( + { + title: project.name, + avatar: project.avatar_url, + id: project.id + }) + end + + describe '#renderable_menus' do + let(:category_menu) do + [ + Sidebars::StaticMenu, + Sidebars::Projects::SuperSidebarMenus::PlanMenu, + Sidebars::Projects::Menus::RepositoryMenu, + Sidebars::Projects::Menus::CiCdMenu, + Sidebars::Projects::Menus::SecurityComplianceMenu, + Sidebars::Projects::Menus::DeploymentsMenu, + Sidebars::Projects::Menus::PackagesRegistriesMenu, + Sidebars::Projects::Menus::InfrastructureMenu, + Sidebars::Projects::Menus::MonitorMenu, + Sidebars::Projects::Menus::AnalyticsMenu, + Sidebars::UncategorizedMenu, + Sidebars::Projects::Menus::SettingsMenu + ] + end + + it "is exposed as a renderable menu" do + expect(subject.renderable_menus.map(&:class)).to eq(category_menu) + end + end +end diff --git a/spec/lib/sidebars/static_menu_spec.rb b/spec/lib/sidebars/static_menu_spec.rb new file mode 100644 index 00000000000..a42fed4b170 --- /dev/null +++ b/spec/lib/sidebars/static_menu_spec.rb @@ -0,0 +1,36 @@ +# frozen_string_literal: true + +require 'spec_helper' + +RSpec.describe Sidebars::StaticMenu, feature_category: :navigation do + let(:context) { {} } + + subject { described_class.new(context) } + + describe '#serialize_for_super_sidebar' do + it 'returns flat list of all menu items' do + subject.add_item(Sidebars::MenuItem.new(title: 'Is active', link: 'foo2', active_routes: { controller: 'fooc' })) + subject.add_item(Sidebars::MenuItem.new(title: 'Not active', link: 'foo3', active_routes: { controller: 'barc' })) + subject.add_item(Sidebars::NilMenuItem.new(item_id: 'nil_item')) + + allow(context).to receive(:route_is_active).and_return(->(x) { x[:controller] == 'fooc' }) + + expect(subject.serialize_for_super_sidebar).to eq( + [ + { + title: "Is active", + icon: nil, + link: "foo2", + is_active: true + }, + { + title: "Not active", + icon: nil, + link: "foo3", + is_active: false + } + ] + ) + end + end +end diff --git a/spec/lib/sidebars/uncategorized_menu_spec.rb b/spec/lib/sidebars/uncategorized_menu_spec.rb new file mode 100644 index 00000000000..45e7c0c87e2 --- /dev/null +++ b/spec/lib/sidebars/uncategorized_menu_spec.rb @@ -0,0 +1,12 @@ +# frozen_string_literal: true + +require 'spec_helper' + +RSpec.describe Sidebars::UncategorizedMenu, feature_category: :navigation do + subject { described_class.new({}) } + + it 'has title and sprite_icon' do + expect(subject.title).to eq(_("Uncategorized")) + expect(subject.sprite_icon).to eq("question") + end +end diff --git a/spec/models/namespace_spec.rb b/spec/models/namespace_spec.rb index 4e3d8f633b7..2d6ddd74dfd 100644 --- a/spec/models/namespace_spec.rb +++ b/spec/models/namespace_spec.rb @@ -465,6 +465,14 @@ RSpec.describe Namespace, feature_category: :subgroups do end end + context 'when parent is nil' do + let(:namespace) { build(:group, parent: nil) } + + it 'returns []' do + expect(namespace.traversal_ids).to eq [] + end + end + context 'when made a child group' do let!(:namespace) { create(:group) } let!(:parent_namespace) { create(:group, children: [namespace]) } @@ -1961,6 +1969,29 @@ RSpec.describe Namespace, feature_category: :subgroups do expect(very_deep_nested_group.root_ancestor).to eq(root_group) end end + + context 'when parent is changed' do + let(:group) { create(:group) } + let(:new_parent) { create(:group) } + + shared_examples 'updates root_ancestor' do + it do + expect { subject }.to change { group.root_ancestor }.from(group).to(new_parent) + end + end + + context 'by object' do + subject { group.parent = new_parent } + + include_examples 'updates root_ancestor' + end + + context 'by id' do + subject { group.parent_id = new_parent.id } + + include_examples 'updates root_ancestor' + end + end end describe '#full_path_before_last_save' do diff --git a/spec/models/uploads/fog_spec.rb b/spec/models/uploads/fog_spec.rb index 1ffe7c6c43b..a1b0bcf95e0 100644 --- a/spec/models/uploads/fog_spec.rb +++ b/spec/models/uploads/fog_spec.rb @@ -3,10 +3,21 @@ require 'spec_helper' RSpec.describe Uploads::Fog do + let(:credentials) do + { + provider: "AWS", + aws_access_key_id: "AWS_ACCESS_KEY_ID", + aws_secret_access_key: "AWS_SECRET_ACCESS_KEY", + region: "eu-central-1" + } + end + + let(:bucket_prefix) { nil } let(:data_store) { described_class.new } + let(:config) { { connection: credentials, bucket_prefix: bucket_prefix, remote_directory: 'uploads' } } before do - stub_uploads_object_storage(FileUploader) + stub_uploads_object_storage(FileUploader, config: config) end describe '#available?' do @@ -18,7 +29,7 @@ RSpec.describe Uploads::Fog do context 'when object storage is disabled' do before do - stub_uploads_object_storage(FileUploader, enabled: false) + stub_uploads_object_storage(FileUploader, config: config, enabled: false) end it { is_expected.to be_falsy } @@ -28,6 +39,60 @@ RSpec.describe Uploads::Fog do context 'model with uploads' do let(:project) { create(:project) } let(:relation) { project.uploads } + let(:connection) { ::Fog::Storage.new(credentials) } + let(:paths) { relation.pluck(:path) } + + # Only fog-aws simulates mocking of deleting an object properly. + # We'll just test that the various providers implement the require methods. + describe 'Fog provider acceptance tests' do + let!(:uploads) { create_list(:upload, 2, :with_file, :issuable_upload, model: project) } + + shared_examples 'Fog provider' do + describe '#get_object' do + it 'returns a Hash with a body' do + expect(connection.get_object('uploads', paths.first)[:body]).not_to be_nil + end + end + + describe '#delete_object' do + it 'returns true' do + expect(connection.delete_object('uploads', paths.first)).to be_truthy + end + end + end + + before do + uploads.each { |upload| upload.retrieve_uploader.migrate!(2) } + end + + context 'with AWS provider' do + it_behaves_like 'Fog provider' + end + + context 'with Google provider' do + let(:credentials) do + { + provider: "Google", + google_storage_access_key_id: 'ACCESS_KEY_ID', + google_storage_secret_access_key: 'SECRET_ACCESS_KEY' + } + end + + it_behaves_like 'Fog provider' + end + + context 'with AzureRM provider' do + let(:credentials) do + { + provider: 'AzureRM', + azure_storage_account_name: 'test-access-id', + azure_storage_access_key: 'secret' + } + end + + it_behaves_like 'Fog provider' + end + end describe '#keys' do let!(:uploads) { create_list(:upload, 2, :object_storage, uploader: FileUploader, model: project) } @@ -40,7 +105,7 @@ RSpec.describe Uploads::Fog do end describe '#delete_keys' do - let(:connection) { ::Fog::Storage.new(FileUploader.object_store_credentials) } + let(:connection) { ::Fog::Storage.new(credentials) } let(:keys) { data_store.keys(relation) } let(:paths) { relation.pluck(:path) } let!(:uploads) { create_list(:upload, 2, :with_file, :issuable_upload, model: project) } @@ -63,6 +128,22 @@ RSpec.describe Uploads::Fog do end end + context 'with bucket prefix' do + let(:bucket_prefix) { 'test-prefix' } + + it 'deletes multiple data' do + paths.each do |path| + expect(connection.get_object('uploads', File.join(bucket_prefix, path))[:body]).not_to be_nil + end + + subject + + paths.each do |path| + expect { connection.get_object('uploads', File.join(bucket_prefix, path))[:body] }.to raise_error(Excon::Error::NotFound) + end + end + end + context 'when one of keys is missing' do let(:keys) { ['unknown'] + super() } diff --git a/spec/requests/api/ci/runner/jobs_request_post_spec.rb b/spec/requests/api/ci/runner/jobs_request_post_spec.rb index 6e721d40560..28dbc4fd168 100644 --- a/spec/requests/api/ci/runner/jobs_request_post_spec.rb +++ b/spec/requests/api/ci/runner/jobs_request_post_spec.rb @@ -831,19 +831,6 @@ RSpec.describe API::Ci::Runner, :clean_gitlab_redis_shared_state, feature_catego end end end - - context 'when the FF ci_hooks_pre_get_sources_script is disabled' do - before do - stub_feature_flags(ci_hooks_pre_get_sources_script: false) - end - - it 'does not return the pre_get_sources_script' do - request_job - - expect(response).to have_gitlab_http_status(:created) - expect(json_response).not_to have_key('hooks') - end - end end describe 'port support' do diff --git a/spec/scripts/generate_rspec_pipeline_spec.rb b/spec/scripts/generate_rspec_pipeline_spec.rb new file mode 100644 index 00000000000..b3eaf9e9127 --- /dev/null +++ b/spec/scripts/generate_rspec_pipeline_spec.rb @@ -0,0 +1,198 @@ +# frozen_string_literal: true + +require 'fast_spec_helper' +require 'tempfile' + +require_relative '../../scripts/generate_rspec_pipeline' + +RSpec.describe GenerateRspecPipeline, :silence_stdout, feature_category: :tooling do + describe '#generate!' do + let!(:rspec_files) { Tempfile.new(['rspec_files_path', '.txt']) } + let(:rspec_files_content) do + "spec/migrations/a_spec.rb spec/migrations/b_spec.rb " \ + "spec/lib/gitlab/background_migration/a_spec.rb spec/lib/gitlab/background_migration/b_spec.rb " \ + "spec/models/a_spec.rb spec/models/b_spec.rb " \ + "spec/controllers/a_spec.rb spec/controllers/b_spec.rb " \ + "spec/features/a_spec.rb spec/features/b_spec.rb" + end + + let(:pipeline_template) { Tempfile.new(['pipeline_template', '.yml.erb']) } + let(:pipeline_template_content) do + <<~YAML + <% if rspec_files_per_test_level[:migration][:files].size > 0 %> + rspec migration: + <% if rspec_files_per_test_level[:migration][:parallelization] > 1 %> + parallel: <%= rspec_files_per_test_level[:migration][:parallelization] %> + <% end %> + <% end %> + <% if rspec_files_per_test_level[:background_migration][:files].size > 0 %> + rspec background_migration: + <% if rspec_files_per_test_level[:background_migration][:parallelization] > 1 %> + parallel: <%= rspec_files_per_test_level[:background_migration][:parallelization] %> + <% end %> + <% end %> + <% if rspec_files_per_test_level[:unit][:files].size > 0 %> + rspec unit: + <% if rspec_files_per_test_level[:unit][:parallelization] > 1 %> + parallel: <%= rspec_files_per_test_level[:unit][:parallelization] %> + <% end %> + <% end %> + <% if rspec_files_per_test_level[:integration][:files].size > 0 %> + rspec integration: + <% if rspec_files_per_test_level[:integration][:parallelization] > 1 %> + parallel: <%= rspec_files_per_test_level[:integration][:parallelization] %> + <% end %> + <% end %> + <% if rspec_files_per_test_level[:system][:files].size > 0 %> + rspec system: + <% if rspec_files_per_test_level[:system][:parallelization] > 1 %> + parallel: <%= rspec_files_per_test_level[:system][:parallelization] %> + <% end %> + <% end %> + YAML + end + + let(:knapsack_report) { Tempfile.new(['knapsack_report', '.json']) } + let(:knapsack_report_content) do + <<~JSON + { + "spec/migrations/a_spec.rb": 360.3, + "spec/migrations/b_spec.rb": 180.1, + "spec/lib/gitlab/background_migration/a_spec.rb": 60.5, + "spec/lib/gitlab/background_migration/b_spec.rb": 180.3, + "spec/models/a_spec.rb": 360.2, + "spec/models/b_spec.rb": 180.6, + "spec/controllers/a_spec.rb": 60.2, + "spec/controllers/ab_spec.rb": 180.4, + "spec/features/a_spec.rb": 360.1, + "spec/features/b_spec.rb": 180.5 + } + JSON + end + + around do |example| + rspec_files.write(rspec_files_content) + rspec_files.rewind + pipeline_template.write(pipeline_template_content) + pipeline_template.rewind + knapsack_report.write(knapsack_report_content) + knapsack_report.rewind + example.run + ensure + rspec_files.close + rspec_files.unlink + pipeline_template.close + pipeline_template.unlink + knapsack_report.close + knapsack_report.unlink + end + + context 'when rspec_files and pipeline_template_path exists' do + subject do + described_class.new( + rspec_files_path: rspec_files.path, + pipeline_template_path: pipeline_template.path + ) + end + + it 'generates the pipeline config with default parallelization' do + subject.generate! + + expect(File.read("#{pipeline_template.path}.yml")) + .to eq( + "rspec migration:\nrspec background_migration:\nrspec unit:\n" \ + "rspec integration:\nrspec system:" + ) + end + + context 'when parallelization > 0' do + before do + stub_const("#{described_class}::DEFAULT_AVERAGE_TEST_FILE_DURATION_IN_SECONDS", 360) + end + + it 'generates the pipeline config' do + subject.generate! + + expect(File.read("#{pipeline_template.path}.yml")) + .to eq( + "rspec migration:\n parallel: 2\nrspec background_migration:\n parallel: 2\n" \ + "rspec unit:\n parallel: 2\nrspec integration:\n parallel: 2\n" \ + "rspec system:\n parallel: 2" + ) + end + end + + context 'when parallelization > MAX_NODES_COUNT' do + let(:rspec_files_content) do + Array.new(51) { |i| "spec/migrations/#{i}_spec.rb" }.join(' ') + end + + before do + stub_const( + "#{described_class}::DEFAULT_AVERAGE_TEST_FILE_DURATION_IN_SECONDS", + described_class::OPTIMAL_TEST_JOB_DURATION_IN_SECONDS + ) + end + + it 'generates the pipeline config with max parallelization of 50' do + subject.generate! + + expect(File.read("#{pipeline_template.path}.yml")).to eq("rspec migration:\n parallel: 50") + end + end + end + + context 'when knapsack_report_path is given' do + subject do + described_class.new( + rspec_files_path: rspec_files.path, + pipeline_template_path: pipeline_template.path, + knapsack_report_path: knapsack_report.path + ) + end + + it 'generates the pipeline config with parallelization based on Knapsack' do + subject.generate! + + expect(File.read("#{pipeline_template.path}.yml")) + .to eq( + "rspec migration:\n parallel: 2\nrspec background_migration:\n" \ + "rspec unit:\n parallel: 2\nrspec integration:\n" \ + "rspec system:\n parallel: 2" + ) + end + + context 'and Knapsack report does not contain valid JSON' do + let(:knapsack_report_content) { "#{super()}," } + + it 'generates the pipeline config with default parallelization' do + subject.generate! + + expect(File.read("#{pipeline_template.path}.yml")) + .to eq( + "rspec migration:\nrspec background_migration:\nrspec unit:\n" \ + "rspec integration:\nrspec system:" + ) + end + end + end + + context 'when rspec_files does not exist' do + subject { described_class.new(rspec_files_path: nil, pipeline_template_path: pipeline_template.path) } + + it 'generates the pipeline config using the no-op template' do + subject.generate! + + expect(File.read("#{pipeline_template.path}.yml")).to include("no-op:") + end + end + + context 'when pipeline_template_path does not exist' do + subject { described_class.new(rspec_files_path: rspec_files.path, pipeline_template_path: nil) } + + it 'generates the pipeline config using the no-op template' do + expect { subject }.to raise_error(ArgumentError) + end + end + end +end diff --git a/spec/services/ci/create_pipeline_service/scripts_spec.rb b/spec/services/ci/create_pipeline_service/scripts_spec.rb index 50b558e505a..770db9331cd 100644 --- a/spec/services/ci/create_pipeline_service/scripts_spec.rb +++ b/spec/services/ci/create_pipeline_service/scripts_spec.rb @@ -83,30 +83,5 @@ RSpec.describe Ci::CreatePipelineService, :yaml_processor_feature_flag_corectnes options: { script: ["echo 'hello job3 script'"] } ) end - - context 'when the FF ci_hooks_pre_get_sources_script is disabled' do - before do - stub_feature_flags(ci_hooks_pre_get_sources_script: false) - end - - it 'creates jobs without hook data' do - expect(pipeline).to be_created_successfully - expect(pipeline.builds.find_by(name: 'job1')).to have_attributes( - name: 'job1', - stage: 'test', - options: { script: ["echo 'hello job1 script'"] } - ) - expect(pipeline.builds.find_by(name: 'job2')).to have_attributes( - name: 'job2', - stage: 'test', - options: { script: ["echo 'hello job2 script'"] } - ) - expect(pipeline.builds.find_by(name: 'job3')).to have_attributes( - name: 'job3', - stage: 'test', - options: { script: ["echo 'hello job3 script'"] } - ) - end - end end end diff --git a/spec/support/helpers/stub_object_storage.rb b/spec/support/helpers/stub_object_storage.rb index c163ce1d880..6b633856228 100644 --- a/spec/support/helpers/stub_object_storage.rb +++ b/spec/support/helpers/stub_object_storage.rb @@ -15,7 +15,7 @@ module StubObjectStorage direct_upload: false, cdn: {} ) - + old_config = Settingslogic.new(config.deep_stringify_keys) new_config = config.to_h.deep_symbolize_keys.merge({ enabled: enabled, proxy_download: proxy_download, @@ -37,7 +37,7 @@ module StubObjectStorage return unless enabled stub_object_storage(connection_params: uploader.object_store_credentials, - remote_directory: config.remote_directory) + remote_directory: old_config.remote_directory) end def stub_object_storage(connection_params:, remote_directory:) diff --git a/spec/support/rspec_order_todo.yml b/spec/support/rspec_order_todo.yml index 8d1e5e907c5..7b7b7da13e5 100644 --- a/spec/support/rspec_order_todo.yml +++ b/spec/support/rspec_order_todo.yml @@ -226,7 +226,6 @@ - './ee/spec/features/analytics/code_analytics_spec.rb' - './ee/spec/features/analytics/group_analytics_spec.rb' - './ee/spec/features/billings/billing_plans_spec.rb' -- './ee/spec/features/billings/extend_reactivate_trial_spec.rb' - './ee/spec/features/billings/qrtly_reconciliation_alert_spec.rb' - './ee/spec/features/boards/board_filters_spec.rb' - './ee/spec/features/boards/boards_licensed_features_spec.rb' |