diff options
author | GitLab Bot <gitlab-bot@gitlab.com> | 2023-04-04 15:15:02 +0300 |
---|---|---|
committer | GitLab Bot <gitlab-bot@gitlab.com> | 2023-04-04 15:15:02 +0300 |
commit | f00510286b6ccda154c4926503397590a8851939 (patch) | |
tree | c4cd69ef2d0d6dd5abf30e3963ac00ead7421a19 /spec | |
parent | 9e5c2e7342d1393f90e74a2ae4b3f27492c22e1f (diff) |
Add latest changes from gitlab-org/gitlab@master
Diffstat (limited to 'spec')
51 files changed, 1093 insertions, 309 deletions
diff --git a/spec/factories/ci/reports/security/reports.rb b/spec/factories/ci/reports/security/reports.rb index 5699b8fee3e..60d1f4615ac 100644 --- a/spec/factories/ci/reports/security/reports.rb +++ b/spec/factories/ci/reports/security/reports.rb @@ -19,6 +19,19 @@ FactoryBot.define do evaluator.findings.each { |o| report.add_finding(o) } end + factory :dependency_scanning_security_report do + type { :dependency_scanning } + + after :create do |report| + artifact = report.pipeline.job_artifacts.dependency_scanning.last + if artifact.present? + content = File.read(artifact.file.path) + + Gitlab::Ci::Parsers::Security::DependencyScanning.parse!(content, report) + end + end + end + skip_create initialize_with do diff --git a/spec/features/nav/pinned_nav_items_spec.rb b/spec/features/nav/pinned_nav_items_spec.rb new file mode 100644 index 00000000000..d05e3042401 --- /dev/null +++ b/spec/features/nav/pinned_nav_items_spec.rb @@ -0,0 +1,159 @@ +# frozen_string_literal: true + +require 'spec_helper' + +RSpec.describe 'Navigation menu item pinning', :js, feature_category: :navigation do + let_it_be(:user) { create(:user, use_new_navigation: true) } + + before do + sign_in(user) + end + + describe 'non-pinnable navigation menu' do + before do + visit explore_projects_path + end + + it 'does not show the Pinned section' do + within '#super-sidebar' do + expect(page).not_to have_content 'Pinned' + end + end + + it 'does not show the buttons to pin items' do + within '#super-sidebar' do + expect(page).not_to have_css 'button svg[data-testid="thumbtack-icon"]' + end + end + end + + describe 'pinnable navigation menu' do + let_it_be(:project) { create(:project) } + + before do + project.add_member(user, :owner) + visit project_path(project) + end + + it 'shows the Pinned section' do + within '#super-sidebar' do + expect(page).to have_content 'Pinned' + end + end + + it 'allows to pin items' do + within '#super-sidebar' do + click_on 'Manage' + add_pin('Activity') + add_pin('Members') + end + + within '[data-testid="pinned-nav-items"]' do + expect(page).to have_link 'Activity' + expect(page).to have_link 'Members' + end + end + + describe 'pinned items' do + before do + within '#super-sidebar' do + click_on 'Operate' + add_pin('Package Registry') + add_pin('Infrastructure Registry') + wait_for_requests + end + end + + it 'can be unpinned from within the pinned section' do + within '[data-testid="pinned-nav-items"]' do + remove_pin('Package Registry') + expect(page).not_to have_content 'Package Registry' + end + end + + it 'can be unpinned from within its section' do + section = find("[data-testid=\"nav-item-link\"]", text: 'Operate') + + within(section.sibling('ul')) do + remove_pin('Infrastructure Registry') + end + + within '[data-testid="pinned-nav-items"]' do + expect(page).not_to have_content 'Infrastructure Registry' + end + end + + it 'can be reordered' do + within '[data-testid="pinned-nav-items"]' do + pinned_items = page.find_all('a').map(&:text) + item1 = page.find('a', text: 'Package Registry') + item2 = page.find('a', text: 'Infrastructure Registry') + expect(pinned_items).to eq [item1.text, item2.text] + + drag_item(item2, to: item1) + + pinned_items = page.find_all('a').map(&:text) + expect(pinned_items).to eq [item2.text, item1.text] + end + end + end + end + + describe 'reordering pins with hidden pins from non-available features' do + let_it_be(:project_with_repo) { create(:project, :repository) } + let_it_be(:project_without_repo) { create(:project, :repository_disabled) } + + before do + project_with_repo.add_member(user, :owner) + project_without_repo.add_member(user, :owner) + + visit project_path(project_with_repo) + within '#super-sidebar' do + click_on 'Code' + add_pin('Commits') + click_on 'Manage' + add_pin('Activity') + add_pin('Members') + end + + visit project_path(project_without_repo) + within '[data-testid="pinned-nav-items"]' do + activity_item = page.find('a', text: 'Activity') + members_item = page.find('a', text: 'Members') + drag_item(members_item, to: activity_item) + end + + visit project_path(project_with_repo) + end + + it 'keeps pins of non-available features' do + within '[data-testid="pinned-nav-items"]' do + pinned_items = page.find_all('a').map(&:text) + expect(pinned_items).to eq %w[Commits Members Activity] + end + end + end + + private + + def add_pin(menu_item_title) + menu_item = find("[data-testid=\"nav-item-link\"]", text: menu_item_title) + menu_item.hover + menu_item.find("[data-testid=\"thumbtack-icon\"]").click + wait_for_requests + end + + def remove_pin(menu_item_title) + menu_item = find("[data-testid=\"nav-item-link\"]", text: menu_item_title) + menu_item.hover + menu_item.find("[data-testid=\"thumbtack-solid-icon\"]").click + wait_for_requests + end + + def drag_item(item, to:) + item.hover + drag_handle = item.find('[data-testid="grip-icon"]') + drag_handle.drag_to(to) + wait_for_requests + end +end diff --git a/spec/fixtures/api/schemas/internal/pages/lookup_path.json b/spec/fixtures/api/schemas/internal/pages/lookup_path.json index 58f102fdea9..fba3efc4ded 100644 --- a/spec/fixtures/api/schemas/internal/pages/lookup_path.json +++ b/spec/fixtures/api/schemas/internal/pages/lookup_path.json @@ -60,6 +60,9 @@ "string", "null" ] + }, + "root_directory": { + "type": "string" } }, "additionalProperties": false diff --git a/spec/fixtures/pages_with_custom_root.zip b/spec/fixtures/pages_with_custom_root.zip Binary files differnew file mode 100644 index 00000000000..40dea253245 --- /dev/null +++ b/spec/fixtures/pages_with_custom_root.zip diff --git a/spec/fixtures/pages_with_custom_root.zip.meta b/spec/fixtures/pages_with_custom_root.zip.meta Binary files differnew file mode 100644 index 00000000000..2cb04e0c33b --- /dev/null +++ b/spec/fixtures/pages_with_custom_root.zip.meta diff --git a/spec/fixtures/pages_with_custom_root.zip.meta0 b/spec/fixtures/pages_with_custom_root.zip.meta0 Binary files differnew file mode 100644 index 00000000000..9b348055b5f --- /dev/null +++ b/spec/fixtures/pages_with_custom_root.zip.meta0 diff --git a/spec/frontend/super_sidebar/components/sidebar_menu_spec.js b/spec/frontend/super_sidebar/components/sidebar_menu_spec.js new file mode 100644 index 00000000000..26b146f0c8b --- /dev/null +++ b/spec/frontend/super_sidebar/components/sidebar_menu_spec.js @@ -0,0 +1,151 @@ +import { mountExtended } from 'helpers/vue_test_utils_helper'; +import SidebarMenu from '~/super_sidebar/components/sidebar_menu.vue'; +import { PANELS_WITH_PINS } from '~/super_sidebar/constants'; +import { sidebarData } from '../mock_data'; + +describe('SidebarMenu component', () => { + let wrapper; + + const createWrapper = (mockData) => { + wrapper = mountExtended(SidebarMenu, { + propsData: { + items: mockData.current_menu_items, + pinnedItemIds: mockData.pinned_items, + panelType: mockData.panel_type, + updatePinsUrl: mockData.update_pins_url, + }, + }); + }; + + describe('computed', () => { + const menuItems = [ + { id: 1, title: 'No subitems' }, + { id: 2, title: 'With subitems', items: [{ id: 21, title: 'Pinned subitem' }] }, + { id: 3, title: 'Empty subitems array', items: [] }, + { id: 4, title: 'Also with subitems', items: [{ id: 41, title: 'Subitem' }] }, + ]; + + describe('supportsPins', () => { + it('is true for the project sidebar', () => { + createWrapper({ ...sidebarData, panel_type: 'project' }); + expect(wrapper.vm.supportsPins).toBe(true); + }); + + it('is true for the group sidebar', () => { + createWrapper({ ...sidebarData, panel_type: 'group' }); + expect(wrapper.vm.supportsPins).toBe(true); + }); + + it('is false for any other sidebar', () => { + createWrapper({ ...sidebarData, panel_type: 'your_work' }); + expect(wrapper.vm.supportsPins).toEqual(false); + }); + }); + + describe('flatPinnableItems', () => { + it('returns all subitems in a flat array', () => { + createWrapper({ ...sidebarData, current_menu_items: menuItems }); + expect(wrapper.vm.flatPinnableItems).toEqual([ + { id: 21, title: 'Pinned subitem' }, + { id: 41, title: 'Subitem' }, + ]); + }); + }); + + describe('staticItems', () => { + describe('when the sidebar supports pins', () => { + beforeEach(() => { + createWrapper({ + ...sidebarData, + current_menu_items: menuItems, + panel_type: PANELS_WITH_PINS[0], + }); + }); + + it('makes everything that has no subitems a static item', () => { + expect(wrapper.vm.staticItems.map((i) => i.title)).toEqual([ + 'No subitems', + 'Empty subitems array', + ]); + }); + }); + + describe('when the sidebar does not support pins', () => { + beforeEach(() => { + createWrapper({ + ...sidebarData, + current_menu_items: menuItems, + panel_type: 'explore', + }); + }); + + it('returns an empty array', () => { + expect(wrapper.vm.staticItems.map((i) => i.title)).toEqual([]); + }); + }); + }); + + describe('nonStaticItems', () => { + describe('when the sidebar supports pins', () => { + beforeEach(() => { + createWrapper({ + ...sidebarData, + current_menu_items: menuItems, + panel_type: PANELS_WITH_PINS[0], + }); + }); + + it('keeps items that have subitems (aka "sections") as non-static', () => { + expect(wrapper.vm.nonStaticItems.map((i) => i.title)).toEqual([ + 'With subitems', + 'Also with subitems', + ]); + }); + }); + + describe('when the sidebar does not support pins', () => { + beforeEach(() => { + createWrapper({ + ...sidebarData, + current_menu_items: menuItems, + panel_type: 'explore', + }); + }); + + it('keeps all items as non-static', () => { + expect(wrapper.vm.nonStaticItems).toEqual(menuItems); + }); + }); + }); + + describe('pinnedItems', () => { + describe('when user has no pinned item ids stored', () => { + beforeEach(() => { + createWrapper({ + ...sidebarData, + current_menu_items: menuItems, + pinned_items: [], + }); + }); + + it('returns an empty array', () => { + expect(wrapper.vm.pinnedItems).toEqual([]); + }); + }); + + describe('when user has some pinned item ids stored', () => { + beforeEach(() => { + createWrapper({ + ...sidebarData, + current_menu_items: menuItems, + pinned_items: [21], + }); + }); + + it('returns the items matching the pinned ids', () => { + expect(wrapper.vm.pinnedItems).toEqual([{ id: 21, title: 'Pinned subitem' }]); + }); + }); + }); + }); +}); diff --git a/spec/frontend/super_sidebar/mock_data.js b/spec/frontend/super_sidebar/mock_data.js index d86cae199ab..0a2dc5216d6 100644 --- a/spec/frontend/super_sidebar/mock_data.js +++ b/spec/frontend/super_sidebar/mock_data.js @@ -90,6 +90,9 @@ export const sidebarData = { search: { search_path: '/search', }, + pinned_items: [], + panel_type: 'your_work', + update_pins_url: 'path/to/pins', }; export const userMenuMockStatus = { diff --git a/spec/helpers/ci/catalog/resources_helper_spec.rb b/spec/helpers/ci/catalog/resources_helper_spec.rb index 1ecce685973..e873b9379fe 100644 --- a/spec/helpers/ci/catalog/resources_helper_spec.rb +++ b/spec/helpers/ci/catalog/resources_helper_spec.rb @@ -3,13 +3,14 @@ require 'spec_helper' RSpec.describe Ci::Catalog::ResourcesHelper, feature_category: :pipeline_composition do + include Devise::Test::ControllerHelpers + let_it_be(:project) { build(:project) } - describe 'can_view_private_catalog?' do - subject { helper.can_view_private_catalog?(project) } + describe '#can_view_namespace_catalog?' do + subject { helper.can_view_namespace_catalog?(project) } before do - allow(helper).to receive(:can_collaborate_with_project?).and_return(true) stub_licensed_features(ci_namespace_catalog: false) end @@ -20,6 +21,7 @@ RSpec.describe Ci::Catalog::ResourcesHelper, feature_category: :pipeline_composi describe '#js_ci_catalog_data' do let(:project) { build(:project, :repository) } + let(:default_helper_data) do {} end diff --git a/spec/helpers/sidebars_helper_spec.rb b/spec/helpers/sidebars_helper_spec.rb index 6a745ae8afb..f7260f14927 100644 --- a/spec/helpers/sidebars_helper_spec.rb +++ b/spec/helpers/sidebars_helper_spec.rb @@ -65,9 +65,10 @@ RSpec.describe SidebarsHelper, feature_category: :navigation do let_it_be(:user) { build(:user) } let_it_be(:group) { build(:group) } let_it_be(:panel) { {} } + let_it_be(:panel_type) { 'project' } subject do - helper.super_sidebar_context(user, group: group, project: nil, panel: panel) + helper.super_sidebar_context(user, group: group, project: nil, panel: panel, panel_type: panel_type) end before do @@ -81,6 +82,7 @@ RSpec.describe SidebarsHelper, feature_category: :navigation do allow(user).to receive(:review_requested_open_merge_requests_count).and_return(0) allow(user).to receive(:todos_pending_count).and_return(3) allow(user).to receive(:total_merge_requests_count).and_return(4) + allow(user).to receive(:pinned_nav_items).and_return({ panel_type => %w[foo bar], 'another_panel' => %w[baz] }) end it 'returns sidebar values from user', :use_clean_rails_memory_store_caching do @@ -134,7 +136,10 @@ RSpec.describe SidebarsHelper, feature_category: :navigation do mr_path: merge_requests_dashboard_path, autocomplete_path: search_autocomplete_path, search_context: helper.header_search_context - } + }, + pinned_items: %w[foo bar], + panel_type: panel_type, + update_pins_url: pins_url }) end @@ -217,7 +222,7 @@ RSpec.describe SidebarsHelper, feature_category: :navigation do let_it_be(:project) { build(:project) } subject do - helper.super_sidebar_context(user, group: nil, project: project, panel: panel) + helper.super_sidebar_context(user, group: nil, project: project, panel: panel, panel_type: panel_type) end before do @@ -240,7 +245,7 @@ RSpec.describe SidebarsHelper, feature_category: :navigation do context 'when current context is a group' do subject do - helper.super_sidebar_context(user, group: group, project: nil, panel: panel) + helper.super_sidebar_context(user, group: group, project: nil, panel: panel, panel_type: panel_type) end before do @@ -263,7 +268,7 @@ RSpec.describe SidebarsHelper, feature_category: :navigation do context 'when current context is not tracked' do subject do - helper.super_sidebar_context(user, group: nil, project: nil, panel: panel) + helper.super_sidebar_context(user, group: nil, project: nil, panel: panel, panel_type: panel_type) end it 'returns no context' do @@ -281,7 +286,7 @@ RSpec.describe SidebarsHelper, feature_category: :navigation do end subject do - helper.super_sidebar_context(user, group: nil, project: nil, panel: panel) + helper.super_sidebar_context(user, group: nil, project: nil, panel: panel, panel_type: panel_type) end context 'when user is not an admin' do diff --git a/spec/lib/bulk_imports/clients/http_spec.rb b/spec/lib/bulk_imports/clients/http_spec.rb index 40261947750..aff049408e2 100644 --- a/spec/lib/bulk_imports/clients/http_spec.rb +++ b/spec/lib/bulk_imports/clients/http_spec.rb @@ -261,7 +261,7 @@ RSpec.describe BulkImports::Clients::HTTP, feature_category: :importers do .to_return(status: 401, body: "", headers: { 'Content-Type' => 'application/json' }) expect { subject.instance_version }.to raise_exception(BulkImports::Error, - "Import aborted as the provided personal access token does not have the required 'api' scope or " \ + "Personal access token does not have the required 'api' scope or " \ "is no longer valid.") end end @@ -273,7 +273,7 @@ RSpec.describe BulkImports::Clients::HTTP, feature_category: :importers do .to_return(status: 403, body: "", headers: { 'Content-Type' => 'application/json' }) expect { subject.instance_version }.to raise_exception(BulkImports::Error, - "Import aborted as the provided personal access token does not have the required 'api' scope or " \ + "Personal access token does not have the required 'api' scope or " \ "is no longer valid.") end end diff --git a/spec/lib/bulk_imports/groups/pipelines/project_entities_pipeline_spec.rb b/spec/lib/bulk_imports/groups/pipelines/project_entities_pipeline_spec.rb index 395f3568913..0155dc8053e 100644 --- a/spec/lib/bulk_imports/groups/pipelines/project_entities_pipeline_spec.rb +++ b/spec/lib/bulk_imports/groups/pipelines/project_entities_pipeline_spec.rb @@ -17,18 +17,18 @@ RSpec.describe BulkImports::Groups::Pipelines::ProjectEntitiesPipeline, feature_ let_it_be(:tracker) { create(:bulk_import_tracker, entity: entity) } let_it_be(:context) { BulkImports::Pipeline::Context.new(tracker) } - let(:extracted_data) do - BulkImports::Pipeline::ExtractedData.new(data: { - 'id' => 'gid://gitlab/Project/1234567', - 'name' => 'My Project', - 'path' => 'my-project', - 'full_path' => 'group/my-project' - }) - end - subject { described_class.new(context) } describe '#run' do + let(:extracted_data) do + BulkImports::Pipeline::ExtractedData.new(data: { + 'id' => 'gid://gitlab/Project/1234567', + 'name' => 'My Project', + 'path' => 'my-project', + 'full_path' => 'group/my-project' + }) + end + before do allow_next_instance_of(BulkImports::Common::Extractors::GraphqlExtractor) do |extractor| allow(extractor).to receive(:extract).and_return(extracted_data) diff --git a/spec/lib/bulk_imports/groups/transformers/group_attributes_transformer_spec.rb b/spec/lib/bulk_imports/groups/transformers/group_attributes_transformer_spec.rb index 138a92a7e6b..ab0d57f574b 100644 --- a/spec/lib/bulk_imports/groups/transformers/group_attributes_transformer_spec.rb +++ b/spec/lib/bulk_imports/groups/transformers/group_attributes_transformer_spec.rb @@ -85,6 +85,22 @@ RSpec.describe BulkImports::Groups::Transformers::GroupAttributesTransformer, fe end end + context 'when the destination_slug has invalid characters' do + let(:entity) do + build_stubbed( + :bulk_import_entity, + bulk_import: bulk_import, + source_full_path: 'source/full/path', + destination_slug: '____destination-_slug-path----__', + destination_namespace: destination_namespace + ) + end + + it 'normalizes the path' do + expect(transformed_data[:path]).to eq('destination-slug-path') + end + end + describe 'parent group transformation' do it 'sets parent id' do expect(transformed_data['parent_id']).to eq(destination_group.id) @@ -101,45 +117,62 @@ RSpec.describe BulkImports::Groups::Transformers::GroupAttributesTransformer, fe end end - describe 'group name transformation' do - context 'when destination namespace is empty' do - before do - entity.destination_namespace = '' - end + context 'when destination namespace is empty' do + before do + entity.destination_namespace = '' + end + it 'does not transform name' do + expect(transformed_data['name']).to eq('Source Group Name') + end + end + + context 'when destination namespace is present' do + context 'when destination namespace does not have a group or project with same path' do it 'does not transform name' do expect(transformed_data['name']).to eq('Source Group Name') end end - context 'when destination namespace is present' do - context 'when destination namespace does not have a group with same name' do - it 'does not transform name' do - expect(transformed_data['name']).to eq('Source Group Name') - end + context 'when destination namespace already has a group or project with the same name' do + before do + create(:project, group: destination_group, name: 'Source Project Name', path: 'project') + create(:group, parent: destination_group, name: 'Source Group Name', path: 'group') + create(:group, parent: destination_group, name: 'Source Group Name_1', path: 'group_1') + create(:group, parent: destination_group, name: 'Source Group Name_2', path: 'group_2') end - context 'when destination namespace already have a group with the same name' do - before do - create(:group, parent: destination_group, name: 'Source Group Name', path: 'group_1') - create(:group, parent: destination_group, name: 'Source Group Name(1)', path: 'group_2') - create(:group, parent: destination_group, name: 'Source Group Name(2)', path: 'group_3') - create(:group, parent: destination_group, name: 'Source Group Name(1)(1)', path: 'group_4') - end + it 'makes the name unique by appending a counter', :aggregate_failures do + transformed_data = described_class.new.transform(context, data.merge('name' => 'Source Group Name')) + expect(transformed_data['name']).to eq('Source Group Name_3') + + transformed_data = described_class.new.transform(context, data.merge('name' => 'Source Group Name_1')) + expect(transformed_data['name']).to eq('Source Group Name_1_1') + + transformed_data = described_class.new.transform(context, data.merge('name' => 'Source Group Name_2')) + expect(transformed_data['name']).to eq('Source Group Name_2_1') - it 'makes the name unique by appeding a counter', :aggregate_failures do - transformed_data = described_class.new.transform(context, data.merge('name' => 'Source Group Name')) - expect(transformed_data['name']).to eq('Source Group Name(3)') + transformed_data = described_class.new.transform(context, data.merge('name' => 'Source Project Name')) + expect(transformed_data['name']).to eq('Source Project Name_1') + end + end + + context 'when destination namespace already has a group or project with the same path' do + before do + create(:project, group: destination_group, name: 'Source Project Name', path: 'destination-slug-path') + create(:group, parent: destination_group, name: 'Source Group Name_4', path: 'destination-slug-path_4') + create(:group, parent: destination_group, name: 'Source Group Name_2', path: 'destination-slug-path_2') + create(:group, parent: destination_group, name: 'Source Group Name_3', path: 'destination-slug-path_3') + end - transformed_data = described_class.new.transform(context, data.merge('name' => 'Source Group Name(2)')) - expect(transformed_data['name']).to eq('Source Group Name(2)(1)') + it 'makes the path unique by appending a counter', :aggregate_failures do + transformed_data = described_class.new.transform(context, data) + expect(transformed_data['path']).to eq('destination-slug-path_1') - transformed_data = described_class.new.transform(context, data.merge('name' => 'Source Group Name(1)')) - expect(transformed_data['name']).to eq('Source Group Name(1)(2)') + create(:group, parent: destination_group, name: 'Source Group Name_1', path: 'destination-slug-path_1') - transformed_data = described_class.new.transform(context, data.merge('name' => 'Source Group Name(1)(1)')) - expect(transformed_data['name']).to eq('Source Group Name(1)(1)(1)') - end + transformed_data = described_class.new.transform(context, data) + expect(transformed_data['path']).to eq('destination-slug-path_5') end end end diff --git a/spec/lib/bulk_imports/projects/pipelines/project_pipeline_spec.rb b/spec/lib/bulk_imports/projects/pipelines/project_pipeline_spec.rb index 09385a261b6..82b8bb3958a 100644 --- a/spec/lib/bulk_imports/projects/pipelines/project_pipeline_spec.rb +++ b/spec/lib/bulk_imports/projects/pipelines/project_pipeline_spec.rb @@ -2,7 +2,7 @@ require 'spec_helper' -RSpec.describe BulkImports::Projects::Pipelines::ProjectPipeline do +RSpec.describe BulkImports::Projects::Pipelines::ProjectPipeline, feature_category: :importers do describe '#run' do let_it_be(:user) { create(:user) } let_it_be(:group) { create(:group) } diff --git a/spec/lib/bulk_imports/projects/transformers/project_attributes_transformer_spec.rb b/spec/lib/bulk_imports/projects/transformers/project_attributes_transformer_spec.rb index 36dc63a9331..d64a0114f7b 100644 --- a/spec/lib/bulk_imports/projects/transformers/project_attributes_transformer_spec.rb +++ b/spec/lib/bulk_imports/projects/transformers/project_attributes_transformer_spec.rb @@ -36,8 +36,8 @@ RSpec.describe BulkImports::Projects::Transformers::ProjectAttributesTransformer expect(transformed_data[:name]).to eq(entity.destination_slug) end - it 'adds path as parameterized name' do - expect(transformed_data[:path]).to eq(entity.destination_slug.parameterize) + it 'adds path as normalized name' do + expect(transformed_data[:path]).to eq(entity.destination_slug.downcase) end it 'adds import type' do @@ -86,6 +86,58 @@ RSpec.describe BulkImports::Projects::Transformers::ProjectAttributesTransformer end end + context 'when destination_slug has invalid characters' do + let(:entity) do + create( + :bulk_import_entity, + source_type: :project_entity, + bulk_import: bulk_import, + source_full_path: 'source/full/path', + destination_slug: '------------Destination_-Project-_Name------------', + destination_namespace: destination_namespace + ) + end + + it 'parameterizes the path' do + expect(transformed_data[:path]).to eq('destination-project-name') + end + end + + context 'when destination namespace already has a group or project with the same name' do + before do + create(:project, group: destination_group, name: 'Destination-Project-Name', path: 'project') + create(:project, group: destination_group, name: 'Destination-Project-Name_1', path: 'project_1') + end + + it 'makes the name unique by appending a counter' do + transformed_data = described_class.new.transform(context, data) + expect(transformed_data['name']).to eq('Destination-Project-Name_2') + end + end + + context 'when destination namespace already has a project with the same path' do + let(:entity) do + create( + :bulk_import_entity, + source_type: :project_entity, + bulk_import: bulk_import, + source_full_path: 'source/full/path', + destination_slug: 'destination-slug-path', + destination_namespace: destination_namespace + ) + end + + before do + create(:project, group: destination_group, name: 'Source Project Name', path: 'destination-slug-path') + create(:project, group: destination_group, name: 'Source Project Name_1', path: 'destination-slug-path_1') + end + + it 'makes the path unique by appending a counter' do + transformed_data = described_class.new.transform(context, data) + expect(transformed_data['path']).to eq('destination-slug-path_2') + end + end + describe 'visibility level' do include_examples 'visibility level settings' end diff --git a/spec/lib/gitlab/bullet/exclusions_spec.rb b/spec/lib/gitlab/bullet/exclusions_spec.rb index 325b0167f58..ccedfee28c7 100644 --- a/spec/lib/gitlab/bullet/exclusions_spec.rb +++ b/spec/lib/gitlab/bullet/exclusions_spec.rb @@ -3,7 +3,7 @@ require 'fast_spec_helper' require 'tempfile' -RSpec.describe Gitlab::Bullet::Exclusions do +RSpec.describe Gitlab::Bullet::Exclusions, feature_category: :application_performance do let(:config_file) do file = Tempfile.new('bullet.yml') File.basename(file) @@ -78,6 +78,19 @@ RSpec.describe Gitlab::Bullet::Exclusions do expect(described_class.new('_some_bogus_file_').execute).to match([]) end end + + context 'with a Symbol' do + let(:exclude) { [] } + let(:config) { { exclusions: { abc: { exclude: exclude } } } } + + before do + File.write(config_file, YAML.dump(config)) + end + + it 'raises an exception' do + expect { executor }.to raise_error(Psych::DisallowedClass) + end + end end describe '#validate_paths!' do diff --git a/spec/lib/gitlab/ci/variables/builder_spec.rb b/spec/lib/gitlab/ci/variables/builder_spec.rb index 914159c0984..4b5f7daefcc 100644 --- a/spec/lib/gitlab/ci/variables/builder_spec.rb +++ b/spec/lib/gitlab/ci/variables/builder_spec.rb @@ -51,6 +51,10 @@ RSpec.describe Gitlab::Ci::Variables::Builder, :clean_gitlab_redis_cache, featur value: Gitlab.config.gitlab.port.to_s }, { key: 'CI_SERVER_PROTOCOL', value: Gitlab.config.gitlab.protocol }, + { key: 'CI_SERVER_SHELL_SSH_HOST', + value: Gitlab.config.gitlab_shell.ssh_host.to_s }, + { key: 'CI_SERVER_SHELL_SSH_PORT', + value: Gitlab.config.gitlab_shell.ssh_port.to_s }, { key: 'CI_SERVER_NAME', value: 'GitLab' }, { key: 'CI_SERVER_VERSION', diff --git a/spec/lib/gitlab/regex_spec.rb b/spec/lib/gitlab/regex_spec.rb index e51e62d5f0a..e01b9fedba4 100644 --- a/spec/lib/gitlab/regex_spec.rb +++ b/spec/lib/gitlab/regex_spec.rb @@ -79,10 +79,10 @@ RSpec.describe Gitlab::Regex, feature_category: :tooling do it { is_expected - .to eq("cannot start with a non-alphanumeric character except for periods or underscores, " \ - "can contain only alphanumeric characters, forward slashes, periods, and underscores, " \ - "cannot end with a period or forward slash, and has a relative path structure " \ - "with no http protocol chars or leading or trailing forward slashes") + .to eq("must have a relative path structure with no HTTP " \ + "protocol characters, or leading or trailing forward slashes. Path segments must not start or " \ + "end with a special character, and must not contain consecutive special characters." + ) } end @@ -101,13 +101,14 @@ RSpec.describe Gitlab::Regex, feature_category: :tooling do it { is_expected.not_to match('good_for+you') } it { is_expected.not_to match('source/') } it { is_expected.not_to match('.source/full./path') } + it { is_expected.not_to match('.source/.full/.path') } + it { is_expected.not_to match('_source') } + it { is_expected.not_to match('.source') } it { is_expected.to match('source') } - it { is_expected.to match('.source') } - it { is_expected.to match('_source') } it { is_expected.to match('source/full') } it { is_expected.to match('source/full/path') } - it { is_expected.to match('.source/.full/.path') } + it { is_expected.to match('sou_rce/fu-ll/pa.th') } it { is_expected.to match('domain_namespace') } it { is_expected.to match('gitlab-migration-test') } it { is_expected.to match('1-project-path') } @@ -115,10 +116,22 @@ RSpec.describe Gitlab::Regex, feature_category: :tooling do it { is_expected.to match('') } # it is possible to pass an empty string for destination_namespace in bulk_import POST request end + describe '.bulk_import_source_full_path_regex_message' do + subject { described_class.bulk_import_source_full_path_regex_message } + + it { + is_expected + .to eq( + "must have a relative path structure with no HTTP " \ + "protocol characters, or leading or trailing forward slashes. Path segments must not start or " \ + "end with a special character, and must not contain consecutive special characters." + ) + } + end + describe '.bulk_import_source_full_path_regex' do subject { described_class.bulk_import_source_full_path_regex } - it { is_expected.not_to match('?gitlab') } it { is_expected.not_to match("Users's something") } it { is_expected.not_to match('/source') } it { is_expected.not_to match('http:') } @@ -126,20 +139,32 @@ RSpec.describe Gitlab::Regex, feature_category: :tooling do it { is_expected.not_to match('example.com/?stuff=true') } it { is_expected.not_to match('example.com:5000/?stuff=true') } it { is_expected.not_to match('http://gitlab.example/gitlab-org/manage/import/gitlab-migration-test') } - it { is_expected.not_to match('_good_for_me!') } - it { is_expected.not_to match('good_for+you') } it { is_expected.not_to match('source/') } - it { is_expected.not_to match('.source/full./path') } it { is_expected.not_to match('') } + it { is_expected.not_to match('.source/full./path') } + it { is_expected.not_to match('?gitlab') } + it { is_expected.not_to match('_good_for_me!') } + it { is_expected.not_to match('group/@*%_my_other-project-----') } + it { is_expected.not_to match('_foog-for-me!') } + it { is_expected.not_to match('.source/full/path.') } + it { is_expected.to match('good_for+you') } it { is_expected.to match('source') } it { is_expected.to match('.source') } it { is_expected.to match('_source') } it { is_expected.to match('source/full') } it { is_expected.to match('source/full/path') } - it { is_expected.to match('.source/.full/.path') } it { is_expected.to match('domain_namespace') } it { is_expected.to match('gitlab-migration-test') } + it { is_expected.to match('source/full/path-') } + it { is_expected.to match('.source/full/path') } + it { is_expected.to match('.source/.full/.path') } + it { is_expected.to match('source/full/.path') } + it { is_expected.to match('source/full/..path') } + it { is_expected.to match('source/full/---1path') } + it { is_expected.to match('source/full/-___path') } + it { is_expected.to match('source/full/path---') } + it { is_expected.to match('group/__my_other-project-----') } end describe '.group_path_regex' do diff --git a/spec/migrations/ensure_issue_user_mentions_bigint_backfill_is_finished_for_gitlab_dot_com_spec.rb b/spec/migrations/ensure_issue_user_mentions_bigint_backfill_is_finished_for_gitlab_dot_com_spec.rb new file mode 100644 index 00000000000..602dd87c593 --- /dev/null +++ b/spec/migrations/ensure_issue_user_mentions_bigint_backfill_is_finished_for_gitlab_dot_com_spec.rb @@ -0,0 +1,35 @@ +# frozen_string_literal: true + +require 'spec_helper' +require_migration! + +RSpec.describe EnsureIssueUserMentionsBigintBackfillIsFinishedForGitlabDotCom, feature_category: :database do + describe '#up' do + let(:migration_arguments) do + { + job_class_name: 'CopyColumnUsingBackgroundMigrationJob', + table_name: 'issue_user_mentions', + column_name: 'id', + job_arguments: [['note_id'], ['note_id_convert_to_bigint']] + } + end + + it 'ensures the migration is completed for GitLab.com, dev, or test' do + expect_next_instance_of(described_class) do |instance| + expect(instance).to receive(:com_or_dev_or_test_but_not_jh?).and_return(true) + expect(instance).to receive(:ensure_batched_background_migration_is_finished).with(migration_arguments) + end + + migrate! + end + + it 'skips the check for other instances' do + expect_next_instance_of(described_class) do |instance| + expect(instance).to receive(:com_or_dev_or_test_but_not_jh?).and_return(false) + expect(instance).not_to receive(:ensure_batched_background_migration_is_finished) + end + + migrate! + end + end +end diff --git a/spec/models/bulk_imports/entity_spec.rb b/spec/models/bulk_imports/entity_spec.rb index 45f120e6773..c7ace3d2b78 100644 --- a/spec/models/bulk_imports/entity_spec.rb +++ b/spec/models/bulk_imports/entity_spec.rb @@ -3,6 +3,8 @@ require 'spec_helper' RSpec.describe BulkImports::Entity, type: :model, feature_category: :importers do + subject { described_class.new(group: Group.new) } + describe 'associations' do it { is_expected.to belong_to(:bulk_import).required } it { is_expected.to belong_to(:parent) } @@ -23,35 +25,8 @@ RSpec.describe BulkImports::Entity, type: :model, feature_category: :importers d it { is_expected.to define_enum_for(:source_type).with_values(%i[group_entity project_entity]) } context 'when formatting with regexes' do - subject { described_class.new(group: Group.new) } - - it { is_expected.to allow_values('namespace', 'parent/namespace', 'parent/group/subgroup', '').for(:destination_namespace) } - it { is_expected.not_to allow_values('parent/namespace/', '/namespace', 'parent group/subgroup', '@namespace').for(:destination_namespace) } - it { is_expected.to allow_values('source', 'source/path', 'source/full/path').for(:source_full_path) } it { is_expected.not_to allow_values('/source', 'http://source/path', 'sou rce/full/path', '').for(:source_full_path) } - - it { is_expected.to allow_values('destination', 'destination-slug', 'new-destination-slug').for(:destination_slug) } - - # it { is_expected.not_to allow_values('destination/slug', '/destination-slug', 'destination slug').for(:destination_slug) } <-- this test should - # succeed but it's failing possibly due to rspec caching. To ensure this case is covered see the more cumbersome test below: - context 'when destination_slug is invalid' do - let(:invalid_slugs) { ['destination/slug', '/destination-slug', 'destination slug'] } - let(:error_message) do - 'cannot start with a non-alphanumeric character except for periods or underscores, ' \ - 'can contain only alphanumeric characters, periods, and underscores, ' \ - 'cannot end with a period or forward slash, and has no ' \ - 'leading or trailing forward slashes' - end - - it 'raises an error' do - invalid_slugs.each do |slug| - entity = build(:bulk_import_entity, :group_entity, group: build(:group), project: nil, destination_slug: slug) - expect(entity).not_to be_valid - expect(entity.errors.errors[0].message).to include(error_message) - end - end - end end context 'when associated with a group and project' do diff --git a/spec/models/ci/build_spec.rb b/spec/models/ci/build_spec.rb index 880e1d3bcee..e808726dae9 100644 --- a/spec/models/ci/build_spec.rb +++ b/spec/models/ci/build_spec.rb @@ -2754,6 +2754,8 @@ RSpec.describe Ci::Build, feature_category: :continuous_integration, factory_def { key: 'CI_SERVER_HOST', value: Gitlab.config.gitlab.host, public: true, masked: false }, { key: 'CI_SERVER_PORT', value: Gitlab.config.gitlab.port.to_s, public: true, masked: false }, { key: 'CI_SERVER_PROTOCOL', value: Gitlab.config.gitlab.protocol, public: true, masked: false }, + { key: 'CI_SERVER_SHELL_SSH_HOST', value: Gitlab.config.gitlab_shell.ssh_host.to_s, public: true, masked: false }, + { key: 'CI_SERVER_SHELL_SSH_PORT', value: Gitlab.config.gitlab_shell.ssh_port.to_s, public: true, masked: false }, { key: 'CI_SERVER_NAME', value: 'GitLab', public: true, masked: false }, { key: 'CI_SERVER_VERSION', value: Gitlab::VERSION, public: true, masked: false }, { key: 'CI_SERVER_VERSION_MAJOR', value: Gitlab.version_info.major.to_s, public: true, masked: false }, diff --git a/spec/models/ci/catalog/listing_spec.rb b/spec/models/ci/catalog/listing_spec.rb index c9ccecbc9fe..93d70a3f63e 100644 --- a/spec/models/ci/catalog/listing_spec.rb +++ b/spec/models/ci/catalog/listing_spec.rb @@ -49,6 +49,7 @@ RSpec.describe Ci::Catalog::Listing, feature_category: :pipeline_composition do before do project_1.add_developer(user) + project_2.add_guest(user) end it 'only returns catalog resources for projects the user has access to' do diff --git a/spec/models/pages/lookup_path_spec.rb b/spec/models/pages/lookup_path_spec.rb index 185a0920a95..88fd1bd9e56 100644 --- a/spec/models/pages/lookup_path_spec.rb +++ b/spec/models/pages/lookup_path_spec.rb @@ -158,4 +158,27 @@ RSpec.describe Pages::LookupPath, feature_category: :pages do end end end + + describe '#root_directory' do + subject(:lookup_path) { described_class.new(project) } + + context 'when there is no deployment' do + it 'returns nil' do + expect(lookup_path.root_directory).to be_nil + end + end + + context 'when there is a deployment' do + let(:deployment) { create(:pages_deployment, project: project, root_directory: 'foo') } + + before do + project.mark_pages_as_deployed + project.pages_metadatum.update!(pages_deployment: deployment) + end + + it 'returns the deployment\'s root_directory' do + expect(lookup_path.root_directory).to eq('foo') + end + end + end end diff --git a/spec/models/user_spec.rb b/spec/models/user_spec.rb index 91fc53dc5ca..27a2f608e23 100644 --- a/spec/models/user_spec.rb +++ b/spec/models/user_spec.rb @@ -84,6 +84,9 @@ RSpec.describe User, feature_category: :user_profile do it { is_expected.to delegate_method(:use_new_navigation).to(:user_preference) } it { is_expected.to delegate_method(:use_new_navigation=).to(:user_preference).with_arguments(:args) } + it { is_expected.to delegate_method(:pinned_nav_items).to(:user_preference) } + it { is_expected.to delegate_method(:pinned_nav_items=).to(:user_preference).with_arguments(:args) } + it { is_expected.to delegate_method(:achievements_enabled).to(:user_preference) } it { is_expected.to delegate_method(:achievements_enabled=).to(:user_preference).with_arguments(:args) } diff --git a/spec/policies/project_policy_spec.rb b/spec/policies/project_policy_spec.rb index 50f425f4efe..cc1029358ae 100644 --- a/spec/policies/project_policy_spec.rb +++ b/spec/policies/project_policy_spec.rb @@ -3004,6 +3004,84 @@ RSpec.describe ProjectPolicy, feature_category: :system_access do end end + describe 'admin_project_runners' do + context 'admin' do + let(:current_user) { admin } + + context 'when admin mode is enabled', :enable_admin_mode do + it { is_expected.to be_allowed(:create_project_runners) } + end + + context 'when admin mode is disabled' do + it { is_expected.to be_disallowed(:create_project_runners) } + end + end + + context 'with owner' do + let(:current_user) { owner } + + it { is_expected.to be_allowed(:create_project_runners) } + end + + context 'with maintainer' do + let(:current_user) { maintainer } + + it { is_expected.to be_allowed(:create_project_runners) } + end + + context 'with reporter' do + let(:current_user) { reporter } + + it { is_expected.to be_disallowed(:create_project_runners) } + end + + context 'with guest' do + let(:current_user) { guest } + + it { is_expected.to be_disallowed(:create_project_runners) } + end + + context 'with developer' do + let(:current_user) { developer } + + it { is_expected.to be_disallowed(:create_project_runners) } + end + + context 'with anonymous' do + let(:current_user) { nil } + + it { is_expected.to be_disallowed(:create_project_runners) } + end + end + + describe 'read_project_runners' do + subject(:policy) { described_class.new(user, project) } + + context 'with maintainer' do + let(:user) { maintainer } + + it { is_expected.to be_allowed(:read_project_runners) } + end + + context 'with admin', :enable_admin_mode do + let(:user) { admin } + + it { is_expected.to be_allowed(:read_project_runners) } + end + + context 'with reporter' do + let(:user) { reporter } + + it { is_expected.to be_disallowed(:read_project_runners) } + end + + context 'when the user is not part of the project' do + let(:user) { non_member } + + it { is_expected.to be_disallowed(:read_project_runners) } + end + end + describe 'update_sentry_issue' do using RSpec::Parameterized::TableSyntax @@ -3104,26 +3182,6 @@ RSpec.describe ProjectPolicy, feature_category: :system_access do end end - describe 'add_catalog_resource' do - using RSpec::Parameterized::TableSyntax - - let(:current_user) { public_send(role) } - - where(:role, :allowed) do - :owner | true - :maintainer | false - :developer | false - :reporter | false - :guest | false - end - - with_them do - it do - expect(subject.can?(:add_catalog_resource)).to be(allowed) - end - end - end - describe 'read_code' do let(:current_user) { create(:user) } @@ -3145,6 +3203,18 @@ RSpec.describe ProjectPolicy, feature_category: :system_access do end end + describe 'read_namespace_catalog' do + let(:current_user) { owner } + + specify { is_expected.to be_disallowed(:read_namespace_catalog) } + end + + describe 'add_catalog_resource' do + let(:current_user) { owner } + + specify { is_expected.to be_disallowed(:read_namespace_catalog) } + end + private def project_subject(project_type) diff --git a/spec/requests/api/bulk_imports_spec.rb b/spec/requests/api/bulk_imports_spec.rb index 70c581716ce..ff76b9369af 100644 --- a/spec/requests/api/bulk_imports_spec.rb +++ b/spec/requests/api/bulk_imports_spec.rb @@ -219,7 +219,7 @@ RSpec.describe API::BulkImports, feature_category: :importers do request expect(response).to have_gitlab_http_status(:bad_request) expect(json_response['error']).to eq("entities[0][source_full_path] must be a relative path and not include protocol, sub-domain, " \ - "or domain information. E.g. 'source/full/path' not 'https://example.com/source/full/path'") + "or domain information. For example, 'source/full/path' not 'https://example.com/source/full/path'") end end @@ -229,10 +229,11 @@ RSpec.describe API::BulkImports, feature_category: :importers do request expect(response).to have_gitlab_http_status(:bad_request) - expect(json_response['error']).to eq("entities[0][destination_namespace] cannot start with a dash or forward slash, " \ - "or end with a period or forward slash. It can only contain alphanumeric " \ - "characters, periods, underscores, forward slashes and dashes. " \ - "E.g. 'destination_namespace' or 'destination/namespace'") + expect(json_response['error']).to eq("entities[0][destination_namespace] must have a relative " \ + "path structure with no HTTP protocol characters, or leading or " \ + "trailing forward slashes. Path segments must not start or " \ + "end with a special character, and must not contain " \ + "consecutive special characters.") end end @@ -248,15 +249,35 @@ RSpec.describe API::BulkImports, feature_category: :importers do end context 'when the destination_slug is invalid' do - it 'returns invalid error' do + it 'returns invalid error when restricting special characters is disabled' do + Feature.disable(:restrict_special_characters_in_namespace_path) + + params[:entities][0][:destination_slug] = 'des?tin?atoi-slugg' + + request + expect(response).to have_gitlab_http_status(:bad_request) + expect(json_response['error']).to include("entities[0][destination_slug] cannot start with " \ + "a non-alphanumeric character except for periods or " \ + "underscores, can contain only alphanumeric characters, " \ + "periods, and underscores, cannot end with a period or " \ + "forward slash, and has no leading or trailing forward " \ + "slashes. It can only contain alphanumeric characters, " \ + "periods, underscores, and dashes. For example, " \ + "'destination_namespace' not 'destination/namespace'") + end + + it 'returns invalid error when restricting special characters is enabled' do + Feature.enable(:restrict_special_characters_in_namespace_path) + params[:entities][0][:destination_slug] = 'des?tin?atoi-slugg' request expect(response).to have_gitlab_http_status(:bad_request) - expect(json_response['error']).to include("entities[0][destination_slug] cannot start with a dash " \ - "or forward slash, or end with a period or forward slash. " \ - "It can only contain alphanumeric characters, periods, underscores, and dashes. " \ - "E.g. 'destination_namespace' not 'destination/namespace'") + expect(json_response['error']).to include("entities[0][destination_slug] must not start or " \ + "end with a special character and must not contain " \ + "consecutive special characters. It can only contain " \ + "alphanumeric characters, periods, underscores, and " \ + "dashes. For example, 'destination_namespace' not 'destination/namespace'") end end diff --git a/spec/requests/api/graphql/ci/config_variables_spec.rb b/spec/requests/api/graphql/ci/config_variables_spec.rb index f2b7a38cc7e..24056dd6b9f 100644 --- a/spec/requests/api/graphql/ci/config_variables_spec.rb +++ b/spec/requests/api/graphql/ci/config_variables_spec.rb @@ -2,7 +2,7 @@ require 'spec_helper' -RSpec.describe 'Query.project(fullPath).ciConfigVariables(sha)', feature_category: :secrets_management do +RSpec.describe 'Query.project(fullPath).ciConfigVariables(ref)', feature_category: :secrets_management do include GraphqlHelpers include ReactiveCachingHelpers @@ -20,7 +20,7 @@ RSpec.describe 'Query.project(fullPath).ciConfigVariables(sha)', feature_categor %( query { project(fullPath: "#{project.full_path}") { - ciConfigVariables(sha: "#{ref}") { + ciConfigVariables(ref: "#{ref}") { key value valueOptions @@ -31,7 +31,7 @@ RSpec.describe 'Query.project(fullPath).ciConfigVariables(sha)', feature_categor ) end - context 'when the user has the correct permissions' do + shared_examples 'when the user has the correct permissions' do before do project.add_maintainer(user) allow(Ci::ListConfigVariablesService) @@ -99,4 +99,31 @@ RSpec.describe 'Query.project(fullPath).ciConfigVariables(sha)', feature_categor expect(graphql_data.dig('project', 'ciConfigVariables')).to be_nil end end + + # Remove this context when `sha` argument is removed + # See https://gitlab.com/gitlab-org/gitlab/-/issues/404493 + describe 'argument validation' do + context 'when `ref` argument is provided' do + it_behaves_like 'when the user has the correct permissions' + end + + context 'when `sha` argument is provided' do + let(:query) do + %( + query { + project(fullPath: "#{project.full_path}") { + ciConfigVariables(sha: "#{ref}") { + key + value + valueOptions + description + } + } + } + ) + end + + it_behaves_like 'when the user has the correct permissions' + end + end end diff --git a/spec/requests/api/internal/pages_spec.rb b/spec/requests/api/internal/pages_spec.rb index 9bda5809112..a816b3870a1 100644 --- a/spec/requests/api/internal/pages_spec.rb +++ b/spec/requests/api/internal/pages_spec.rb @@ -117,7 +117,8 @@ RSpec.describe API::Internal::Pages, feature_category: :pages do 'file_size' => deployment.size, 'file_count' => deployment.file_count }, - 'unique_host' => nil + 'unique_host' => nil, + 'root_directory' => deployment.root_directory } ] ) @@ -206,7 +207,8 @@ RSpec.describe API::Internal::Pages, feature_category: :pages do 'file_size' => deployment.size, 'file_count' => deployment.file_count }, - 'unique_host' => 'unique-domain.example.com' + 'unique_host' => 'unique-domain.example.com', + 'root_directory' => 'public' } ] ) @@ -262,7 +264,8 @@ RSpec.describe API::Internal::Pages, feature_category: :pages do 'file_size' => deployment.size, 'file_count' => deployment.file_count }, - 'unique_host' => nil + 'unique_host' => nil, + 'root_directory' => 'public' } ] ) @@ -310,7 +313,8 @@ RSpec.describe API::Internal::Pages, feature_category: :pages do 'file_size' => deployment.size, 'file_count' => deployment.file_count }, - 'unique_host' => nil + 'unique_host' => nil, + 'root_directory' => 'public' } ] ) diff --git a/spec/requests/users/pins_spec.rb b/spec/requests/users/pins_spec.rb new file mode 100644 index 00000000000..9a32d7e9d76 --- /dev/null +++ b/spec/requests/users/pins_spec.rb @@ -0,0 +1,67 @@ +# frozen_string_literal: true + +require 'spec_helper' + +RSpec.describe 'Pinning navigation menu items', feature_category: :navigation do + let(:user) { create(:user) } + let(:menu_item_ids) { %w[item4 item7] } + let(:other_panel_data) { { 'group' => ['some_item_id'] } } + + before do + user.update!(pinned_nav_items: other_panel_data) + sign_in(user) + end + + describe 'PUT /-/users/pins' do + before do + put pins_path, params: params, headers: { 'ACCEPT' => 'application/json' } + end + + context 'with valid params' do + let(:panel) { 'project' } + let(:params) { { menu_item_ids: menu_item_ids, panel: panel } } + + it 'saves the menu_item_ids for the correct panel' do + expect(user.pinned_nav_items).to include(panel => menu_item_ids) + end + + it 'does not change menu_item_ids of other panels' do + expect(user.pinned_nav_items).to include(other_panel_data) + end + + it 'responds OK' do + expect(response).to have_gitlab_http_status(:ok) + end + end + + context 'with invalid params' do + shared_examples 'unchanged data and error response' do + it 'does not modify existing panel data' do + expect(user.reload.pinned_nav_items).to eq(other_panel_data) + end + + it 'responds with error' do + expect(response).to have_gitlab_http_status(:bad_request) + end + end + + context 'when panel name is unknown' do + let(:params) { { menu_item_ids: menu_item_ids, panel: 'something_else' } } + + it_behaves_like 'unchanged data and error response' + end + + context 'when menu_item_ids is not array of strings' do + let(:params) { { menu_item_ids: 'not_an_array', panel: 'project' } } + + it_behaves_like 'unchanged data and error response' + end + + context 'when params are not permitted' do + let(:params) { { random_param: 'random_value' } } + + it_behaves_like 'unchanged data and error response' + end + end + end +end diff --git a/spec/rubocop/cop/sidekiq_load_balancing/worker_data_consistency_spec.rb b/spec/rubocop/cop/sidekiq_load_balancing/worker_data_consistency_spec.rb index 7b6578a0744..f41a441d6a6 100644 --- a/spec/rubocop/cop/sidekiq_load_balancing/worker_data_consistency_spec.rb +++ b/spec/rubocop/cop/sidekiq_load_balancing/worker_data_consistency_spec.rb @@ -3,46 +3,95 @@ require 'rubocop_spec_helper' require_relative '../../../../rubocop/cop/sidekiq_load_balancing/worker_data_consistency' -RSpec.describe RuboCop::Cop::SidekiqLoadBalancing::WorkerDataConsistency do - before do - allow(cop) - .to receive(:in_worker?) - .and_return(true) - end +RSpec.describe RuboCop::Cop::SidekiqLoadBalancing::WorkerDataConsistency, feature_category: :scalability do + context 'when data_consistency is not set' do + it 'adds an offense when not defining data_consistency' do + expect_offense(<<~CODE) + class SomeWorker + ^^^^^^^^^^^^^^^^ Should define data_consistency expectation.[...] + include ApplicationWorker - it 'adds an offense when not defining data_consistency' do - expect_offense(<<~CODE) - class SomeWorker - ^^^^^^^^^^^^^^^^ Should define data_consistency expectation.[...] - include ApplicationWorker - - queue_namespace :pipeline_hooks - feature_category :continuous_integration - urgency :high - end - CODE - end + queue_namespace :pipeline_hooks + feature_category :continuous_integration + urgency :high + end + CODE + end + + it 'adds no offense when defining data_consistency' do + expect_no_offenses(<<~CODE) + class SomeWorker + include ApplicationWorker - it 'adds no offense when defining data_consistency' do - expect_no_offenses(<<~CODE) - class SomeWorker - include ApplicationWorker - - queue_namespace :pipeline_hooks - feature_category :continuous_integration - data_consistency :delayed - urgency :high - end - CODE + queue_namespace :pipeline_hooks + feature_category :continuous_integration + data_consistency :delayed + urgency :high + end + CODE + end + + it 'adds no offense when worker is not an ApplicationWorker' do + expect_no_offenses(<<~CODE) + class SomeWorker + queue_namespace :pipeline_hooks + feature_category :continuous_integration + urgency :high + end + CODE + end end - it 'adds no offense when worker is not an ApplicationWorker' do - expect_no_offenses(<<~CODE) - class SomeWorker - queue_namespace :pipeline_hooks - feature_category :continuous_integration - urgency :high - end - CODE + context 'when data_consistency set to :always' do + it 'adds an offense when using `always` data_consistency' do + expect_offense(<<~CODE) + class SomeWorker + include ApplicationWorker + data_consistency :always + ^^^^^^^ Refrain from using `:always` if possible.[...] + + queue_namespace :pipeline_hooks + feature_category :continuous_integration + urgency :high + end + CODE + end + + it 'adds no offense when using `sticky` data_consistency' do + expect_no_offenses(<<~CODE) + class SomeWorker + include ApplicationWorker + + data_consistency :sticky + queue_namespace :pipeline_hooks + feature_category :continuous_integration + urgency :high + end + CODE + end + + it 'adds no offense when using `delayed` data_consistency' do + expect_no_offenses(<<~CODE) + class SomeWorker + include ApplicationWorker + + data_consistency :delayed + queue_namespace :pipeline_hooks + feature_category :continuous_integration + urgency :high + end + CODE + end + + it 'adds no offense when worker is not an ApplicationWorker' do + expect_no_offenses(<<~CODE) + class SomeWorker + data_consistency :always + queue_namespace :pipeline_hooks + feature_category :continuous_integration + urgency :high + end + CODE + end end end diff --git a/spec/serializers/import/bulk_import_entity_spec.rb b/spec/serializers/import/bulk_import_entity_spec.rb index 3dfc659daf7..f2f8854174a 100644 --- a/spec/serializers/import/bulk_import_entity_spec.rb +++ b/spec/serializers/import/bulk_import_entity_spec.rb @@ -2,7 +2,7 @@ require 'spec_helper' -RSpec.describe Import::BulkImportEntity do +RSpec.describe Import::BulkImportEntity, feature_category: :importers do let(:importable_data) do { 'id' => 1, diff --git a/spec/services/bulk_imports/create_service_spec.rb b/spec/services/bulk_imports/create_service_spec.rb index 6fe93437694..c68030a89a8 100644 --- a/spec/services/bulk_imports/create_service_spec.rb +++ b/spec/services/bulk_imports/create_service_spec.rb @@ -114,8 +114,8 @@ RSpec.describe BulkImports::CreateService, feature_category: :importers do expect(result).to be_error expect(result.message) .to eq( - "Import aborted as the provided personal access token does not have the required 'api' scope or is " \ - "no longer valid." + "Personal access token does not " \ + "have the required 'api' scope or is no longer valid." ) end end @@ -306,11 +306,10 @@ RSpec.describe BulkImports::CreateService, feature_category: :importers do expect(result).to be_a(ServiceResponse) expect(result).to be_error expect(result.message).to eq("Validation failed: Source full path can't be blank, " \ - "Source full path cannot start with a non-alphanumeric character except " \ - "for periods or underscores, can contain only alphanumeric characters, " \ - "forward slashes, periods, and underscores, cannot end with " \ - "a period or forward slash, and has a relative path structure " \ - "with no http protocol chars or leading or trailing forward slashes") + "Source full path must have a relative path structure with " \ + "no HTTP protocol characters, or leading or trailing forward slashes. " \ + "Path segments must not start or end with a special character, and " \ + "must not contain consecutive special characters.") end describe '#user-role' do @@ -503,6 +502,65 @@ RSpec.describe BulkImports::CreateService, feature_category: :importers do end end + describe '.validate_destination_namespace' do + context 'when the destination_namespace is invalid' do + let(:params) do + [ + { + source_type: 'group_entity', + source_full_path: 'full/path/to/source', + destination_slug: 'destination-slug', + destination_namespace: '---destination----namespace---', + migrate_projects: migrate_projects + } + ] + end + + it 'returns ServiceResponse with an error message' do + result = subject.execute + + expect(result).to be_a(ServiceResponse) + expect(result).to be_error + expect(result.message) + .to eq( + "Import failed. Destination group or subgroup path " \ + "must have a relative path structure with no HTTP protocol characters, or leading " \ + "or trailing forward slashes. Path segments must not start or end with a special " \ + "character, and must not contain consecutive special characters." + ) + end + end + end + + describe '.validate_destination_slug' do + context 'when the destination_slug is invalid' do + let(:params) do + [ + { + source_type: 'group_entity', + source_full_path: 'full/path/to/source', + destination_slug: 'destin-*-ation-slug', + destination_namespace: 'destination_namespace', + migrate_projects: migrate_projects + } + ] + end + + it 'returns ServiceResponse with an error message' do + result = subject.execute + + expect(result).to be_a(ServiceResponse) + expect(result).to be_error + expect(result.message) + .to eq( + "Import failed. Destination URL " \ + "must not start or end with a special character and must " \ + "not contain consecutive special characters." + ) + end + end + end + describe '.validate_destination_full_path' do context 'when the source_type is a group' do context 'when the provided destination_slug already exists in the destination_namespace' do @@ -527,7 +585,7 @@ RSpec.describe BulkImports::CreateService, feature_category: :importers do expect(result).to be_error expect(result.message) .to eq( - "Import aborted as 'parent-group/existing-subgroup' already exists. " \ + "Import failed. 'parent-group/existing-subgroup' already exists. " \ "Change the destination and try again." ) end @@ -554,7 +612,7 @@ RSpec.describe BulkImports::CreateService, feature_category: :importers do expect(result).to be_error expect(result.message) .to eq( - "Import aborted as 'top-level-group' already exists. " \ + "Import failed. 'top-level-group' already exists. " \ "Change the destination and try again." ) end @@ -605,7 +663,7 @@ RSpec.describe BulkImports::CreateService, feature_category: :importers do expect(result).to be_error expect(result.message) .to eq( - "Import aborted as 'existing-group/existing-project' already exists. " \ + "Import failed. 'existing-group/existing-project' already exists. " \ "Change the destination and try again." ) end diff --git a/spec/services/ci/catalog/add_resource_service_spec.rb b/spec/services/ci/catalog/add_resource_service_spec.rb deleted file mode 100644 index ecb939e3c2d..00000000000 --- a/spec/services/ci/catalog/add_resource_service_spec.rb +++ /dev/null @@ -1,55 +0,0 @@ -# frozen_string_literal: true - -require 'spec_helper' - -RSpec.describe Ci::Catalog::AddResourceService, feature_category: :pipeline_composition do - let_it_be(:project) { create(:project, :repository, description: 'Our components') } - let_it_be(:user) { create(:user) } - - let(:service) { described_class.new(project, user) } - - describe '#execute' do - context 'with an unauthorized user' do - it 'raises an AccessDeniedError' do - expect { service.execute }.to raise_error(Gitlab::Access::AccessDeniedError) - end - end - - context 'with an authorized user' do - before do - project.add_owner(user) - end - - context 'and a valid project' do - it 'creates a catalog resource' do - response = service.execute - - expect(response.payload.project).to eq(project) - end - end - - context 'with an invalid project' do - let_it_be(:project) { create(:project, :repository) } - - it 'does not create a catalog resource' do - response = service.execute - - expect(response.message).to eq('Project must have a description') - end - end - - context 'with an invalid catalog resource' do - it 'does not save the catalog resource' do - catalog_resource = instance_double(::Ci::Catalog::Resource, - valid?: false, - errors: instance_double(ActiveModel::Errors, full_messages: ['not valid'])) - allow(::Ci::Catalog::Resource).to receive(:new).and_return(catalog_resource) - - response = service.execute - - expect(response.message).to eq('not valid') - end - end - end - end -end diff --git a/spec/services/projects/update_pages_service_spec.rb b/spec/services/projects/update_pages_service_spec.rb index 8157dce4ce8..db0cf31d83c 100644 --- a/spec/services/projects/update_pages_service_spec.rb +++ b/spec/services/projects/update_pages_service_spec.rb @@ -8,13 +8,16 @@ RSpec.describe Projects::UpdatePagesService, feature_category: :pages do let_it_be(:old_pipeline) { create(:ci_pipeline, project: project, sha: project.commit('HEAD').sha) } let_it_be(:pipeline) { create(:ci_pipeline, project: project, sha: project.commit('HEAD').sha) } - let(:build) { create(:ci_build, pipeline: pipeline, ref: 'HEAD') } + let(:options) { {} } + let(:build) { create(:ci_build, pipeline: pipeline, ref: 'HEAD', options: options) } let(:invalid_file) { fixture_file_upload('spec/fixtures/dk.png') } let(:file) { fixture_file_upload("spec/fixtures/pages.zip") } + let(:custom_root_file) { fixture_file_upload("spec/fixtures/pages_with_custom_root.zip") } let(:empty_file) { fixture_file_upload("spec/fixtures/pages_empty.zip") } let(:empty_metadata_filename) { "spec/fixtures/pages_empty.zip.meta" } let(:metadata_filename) { "spec/fixtures/pages.zip.meta" } + let(:custom_root_file_metadata) { "spec/fixtures/pages_with_custom_root.zip.meta" } let(:metadata) { fixture_file_upload(metadata_filename) if File.exist?(metadata_filename) } subject { described_class.new(project, build) } @@ -97,6 +100,7 @@ RSpec.describe Projects::UpdatePagesService, feature_category: :pages do expect(deployment.file_sha256).to eq(artifacts_archive.file_sha256) expect(project.pages_metadatum.reload.pages_deployment_id).to eq(deployment.id) expect(deployment.ci_build_id).to eq(build.id) + expect(deployment.root_directory).to be_nil end it 'does not fail if pages_metadata is absent' do @@ -140,7 +144,45 @@ RSpec.describe Projects::UpdatePagesService, feature_category: :pages do it 'returns an error' do expect(execute).not_to eq(:success) - expect(GenericCommitStatus.last.description).to eq("Error: The `public/` folder is missing, or not declared in `.gitlab-ci.yml`.") + expect(GenericCommitStatus.last.description).to eq("Error: You need to either include a `public/` folder in your artifacts, or specify which one to use for Pages using `publish` in `.gitlab-ci.yml`") + end + end + + context 'when there is a custom root config' do + let(:file) { custom_root_file } + let(:metadata_filename) { custom_root_file_metadata } + + context 'when the directory specified with `publish` is included in the artifacts' do + let(:options) { { publish: 'foo' } } + + it 'creates pages_deployment and saves it in the metadata' do + expect(execute).to eq(:success) + + deployment = project.pages_deployments.last + expect(deployment.root_directory).to eq(options[:publish]) + end + end + + context 'when the directory specified with `publish` is not included in the artifacts' do + let(:options) { { publish: 'bar' } } + + it 'returns an error' do + expect(execute).not_to eq(:success) + + expect(GenericCommitStatus.last.description).to eq("Error: You need to either include a `public/` folder in your artifacts, or specify which one to use for Pages using `publish` in `.gitlab-ci.yml`") + end + end + + context 'when there is a folder named `public`, but `publish` specifies a different one' do + let(:options) { { publish: 'foo' } } + let(:file) { fixture_file_upload("spec/fixtures/pages.zip") } + let(:metadata_filename) { "spec/fixtures/pages.zip.meta" } + + it 'returns an error' do + expect(execute).not_to eq(:success) + + expect(GenericCommitStatus.last.description).to eq("Error: You need to either include a `public/` folder in your artifacts, or specify which one to use for Pages using `publish` in `.gitlab-ci.yml`") + end end end diff --git a/spec/support/helpers/project_template_test_helper.rb b/spec/support/helpers/project_template_test_helper.rb index bedbb8601e8..2af4a966f6d 100644 --- a/spec/support/helpers/project_template_test_helper.rb +++ b/spec/support/helpers/project_template_test_helper.rb @@ -9,7 +9,7 @@ module ProjectTemplateTestHelper nfjekyll nfplainhtml nfgitbook nfhexo salesforcedx serverless_framework tencent_serverless_framework jsonnet cluster_management kotlin_native_linux - pelican bridgetown typo3_distribution + pelican bridgetown typo3_distribution laravel ] end end diff --git a/spec/uploaders/attachment_uploader_spec.rb b/spec/uploaders/attachment_uploader_spec.rb index 05cffff1f1a..a035402e207 100644 --- a/spec/uploaders/attachment_uploader_spec.rb +++ b/spec/uploaders/attachment_uploader_spec.rb @@ -10,9 +10,9 @@ RSpec.describe AttachmentUploader do subject { uploader } it_behaves_like 'builds correct paths', - store_dir: %r[uploads/-/system/note/attachment/], - upload_path: %r[uploads/-/system/note/attachment/], - absolute_path: %r[#{CarrierWave.root}/uploads/-/system/note/attachment/] + store_dir: %r[uploads/-/system/note/attachment/], + upload_path: %r[uploads/-/system/note/attachment/], + absolute_path: %r[#{CarrierWave.root}/uploads/-/system/note/attachment/] context "object_store is REMOTE" do before do @@ -22,8 +22,8 @@ RSpec.describe AttachmentUploader do include_context 'with storage', described_class::Store::REMOTE it_behaves_like 'builds correct paths', - store_dir: %r[note/attachment/], - upload_path: %r[note/attachment/] + store_dir: %r[note/attachment/], + upload_path: %r[note/attachment/] end describe "#migrate!" do diff --git a/spec/uploaders/avatar_uploader_spec.rb b/spec/uploaders/avatar_uploader_spec.rb index a55e5c23fe8..e472ac46e66 100644 --- a/spec/uploaders/avatar_uploader_spec.rb +++ b/spec/uploaders/avatar_uploader_spec.rb @@ -10,9 +10,9 @@ RSpec.describe AvatarUploader do subject { uploader } it_behaves_like 'builds correct paths', - store_dir: %r[uploads/-/system/user/avatar/], - upload_path: %r[uploads/-/system/user/avatar/], - absolute_path: %r[#{CarrierWave.root}/uploads/-/system/user/avatar/] + store_dir: %r[uploads/-/system/user/avatar/], + upload_path: %r[uploads/-/system/user/avatar/], + absolute_path: %r[#{CarrierWave.root}/uploads/-/system/user/avatar/] context "object_store is REMOTE" do before do @@ -22,8 +22,8 @@ RSpec.describe AvatarUploader do include_context 'with storage', described_class::Store::REMOTE it_behaves_like 'builds correct paths', - store_dir: %r[user/avatar/], - upload_path: %r[user/avatar/] + store_dir: %r[user/avatar/], + upload_path: %r[user/avatar/] end context "with a file" do diff --git a/spec/uploaders/ci/pipeline_artifact_uploader_spec.rb b/spec/uploaders/ci/pipeline_artifact_uploader_spec.rb index 0630e9f6546..3935f081372 100644 --- a/spec/uploaders/ci/pipeline_artifact_uploader_spec.rb +++ b/spec/uploaders/ci/pipeline_artifact_uploader_spec.rb @@ -9,9 +9,9 @@ RSpec.describe Ci::PipelineArtifactUploader do subject { uploader } it_behaves_like "builds correct paths", - store_dir: %r[\h{2}/\h{2}/\h{64}/pipelines/\d+/artifacts/\d+], - cache_dir: %r[artifacts/tmp/cache], - work_dir: %r[artifacts/tmp/work] + store_dir: %r[\h{2}/\h{2}/\h{64}/pipelines/\d+/artifacts/\d+], + cache_dir: %r[artifacts/tmp/cache], + work_dir: %r[artifacts/tmp/work] context 'when object store is REMOTE' do before do diff --git a/spec/uploaders/dependency_proxy/file_uploader_spec.rb b/spec/uploaders/dependency_proxy/file_uploader_spec.rb index eb12e7dffa5..3cb2d1ea0f0 100644 --- a/spec/uploaders/dependency_proxy/file_uploader_spec.rb +++ b/spec/uploaders/dependency_proxy/file_uploader_spec.rb @@ -11,9 +11,9 @@ RSpec.describe DependencyProxy::FileUploader do subject { uploader } it_behaves_like "builds correct paths", - store_dir: %r[\h{2}/\h{2}], - cache_dir: %r[/dependency_proxy/tmp/cache], - work_dir: %r[/dependency_proxy/tmp/work] + store_dir: %r[\h{2}/\h{2}], + cache_dir: %r[/dependency_proxy/tmp/cache], + work_dir: %r[/dependency_proxy/tmp/work] context 'object store is remote' do before do @@ -22,8 +22,7 @@ RSpec.describe DependencyProxy::FileUploader do include_context 'with storage', described_class::Store::REMOTE - it_behaves_like "builds correct paths", - store_dir: %r[\h{2}/\h{2}] + it_behaves_like "builds correct paths", store_dir: %r[\h{2}/\h{2}] end end diff --git a/spec/uploaders/design_management/design_v432x230_uploader_spec.rb b/spec/uploaders/design_management/design_v432x230_uploader_spec.rb index a18a37e73da..f3dd77d67a0 100644 --- a/spec/uploaders/design_management/design_v432x230_uploader_spec.rb +++ b/spec/uploaders/design_management/design_v432x230_uploader_spec.rb @@ -11,10 +11,10 @@ RSpec.describe DesignManagement::DesignV432x230Uploader do subject(:uploader) { described_class.new(model, :image_v432x230) } it_behaves_like 'builds correct paths', - store_dir: %r[uploads/-/system/design_management/action/image_v432x230/], - upload_path: %r[uploads/-/system/design_management/action/image_v432x230/], - relative_path: %r[uploads/-/system/design_management/action/image_v432x230/], - absolute_path: %r[#{CarrierWave.root}/uploads/-/system/design_management/action/image_v432x230/] + store_dir: %r[uploads/-/system/design_management/action/image_v432x230/], + upload_path: %r[uploads/-/system/design_management/action/image_v432x230/], + relative_path: %r[uploads/-/system/design_management/action/image_v432x230/], + absolute_path: %r[#{CarrierWave.root}/uploads/-/system/design_management/action/image_v432x230/] context 'object_store is REMOTE' do before do @@ -24,9 +24,9 @@ RSpec.describe DesignManagement::DesignV432x230Uploader do include_context 'with storage', described_class::Store::REMOTE it_behaves_like 'builds correct paths', - store_dir: %r[design_management/action/image_v432x230/], - upload_path: %r[design_management/action/image_v432x230/], - relative_path: %r[design_management/action/image_v432x230/] + store_dir: %r[design_management/action/image_v432x230/], + upload_path: %r[design_management/action/image_v432x230/], + relative_path: %r[design_management/action/image_v432x230/] end describe "#migrate!" do diff --git a/spec/uploaders/external_diff_uploader_spec.rb b/spec/uploaders/external_diff_uploader_spec.rb index a889181b72c..2121e9cbc29 100644 --- a/spec/uploaders/external_diff_uploader_spec.rb +++ b/spec/uploaders/external_diff_uploader_spec.rb @@ -9,9 +9,9 @@ RSpec.describe ExternalDiffUploader do subject(:uploader) { described_class.new(diff, :external_diff) } it_behaves_like "builds correct paths", - store_dir: %r[merge_request_diffs/mr-\d+], - cache_dir: %r[/external-diffs/tmp/cache], - work_dir: %r[/external-diffs/tmp/work] + store_dir: %r[merge_request_diffs/mr-\d+], + cache_dir: %r[/external-diffs/tmp/cache], + work_dir: %r[/external-diffs/tmp/work] context "object store is REMOTE" do before do @@ -21,7 +21,7 @@ RSpec.describe ExternalDiffUploader do include_context 'with storage', described_class::Store::REMOTE it_behaves_like "builds correct paths", - store_dir: %r[merge_request_diffs/mr-\d+] + store_dir: %r[merge_request_diffs/mr-\d+] end describe 'remote file' do diff --git a/spec/uploaders/file_uploader_spec.rb b/spec/uploaders/file_uploader_spec.rb index 1287b809223..76519545e24 100644 --- a/spec/uploaders/file_uploader_spec.rb +++ b/spec/uploaders/file_uploader_spec.rb @@ -13,9 +13,9 @@ RSpec.describe FileUploader do shared_examples 'builds correct legacy storage paths' do include_examples 'builds correct paths', - store_dir: %r{awesome/project/\h+}, - upload_path: %r{\h+/<filename>}, - absolute_path: %r{#{described_class.root}/awesome/project/55dc16aa0edd05693fd98b5051e83321/foo.jpg} + store_dir: %r{awesome/project/\h+}, + upload_path: %r{\h+/<filename>}, + absolute_path: %r{#{described_class.root}/awesome/project/55dc16aa0edd05693fd98b5051e83321/foo.jpg} end context 'legacy storage' do @@ -26,8 +26,8 @@ RSpec.describe FileUploader do let(:project) { build_stubbed(:project, namespace: group, name: 'project') } include_examples 'builds correct paths', - store_dir: %r{@hashed/\h{2}/\h{2}/\h+}, - upload_path: %r{\h+/<filename>} + store_dir: %r{@hashed/\h{2}/\h{2}/\h+}, + upload_path: %r{\h+/<filename>} end context 'when only repositories are rolled out' do @@ -47,8 +47,8 @@ RSpec.describe FileUploader do # always use hashed storage path for remote uploads it_behaves_like 'builds correct paths', - store_dir: %r{@hashed/\h{2}/\h{2}/\h+}, - upload_path: %r{@hashed/\h{2}/\h{2}/\h+/\h+/<filename>} + store_dir: %r{@hashed/\h{2}/\h{2}/\h+}, + upload_path: %r{@hashed/\h{2}/\h{2}/\h+/\h+/<filename>} end describe 'initialize' do diff --git a/spec/uploaders/job_artifact_uploader_spec.rb b/spec/uploaders/job_artifact_uploader_spec.rb index d7c9ef7e0d5..dac9e97641d 100644 --- a/spec/uploaders/job_artifact_uploader_spec.rb +++ b/spec/uploaders/job_artifact_uploader_spec.rb @@ -10,9 +10,9 @@ RSpec.describe JobArtifactUploader do subject { uploader } it_behaves_like "builds correct paths", - store_dir: %r[\h{2}/\h{2}/\h{64}/\d{4}_\d{1,2}_\d{1,2}/\d+/\d+\z], - cache_dir: %r[artifacts/tmp/cache], - work_dir: %r[artifacts/tmp/work] + store_dir: %r[\h{2}/\h{2}/\h{64}/\d{4}_\d{1,2}_\d{1,2}/\d+/\d+\z], + cache_dir: %r[artifacts/tmp/cache], + work_dir: %r[artifacts/tmp/work] context "object store is REMOTE" do before do @@ -22,7 +22,7 @@ RSpec.describe JobArtifactUploader do include_context 'with storage', described_class::Store::REMOTE it_behaves_like "builds correct paths", - store_dir: %r[\h{2}/\h{2}/\h{64}/\d{4}_\d{1,2}_\d{1,2}/\d+/\d+\z] + store_dir: %r[\h{2}/\h{2}/\h{64}/\d{4}_\d{1,2}_\d{1,2}/\d+/\d+\z] describe '#cdn_enabled_url' do it 'returns URL and false' do diff --git a/spec/uploaders/lfs_object_uploader_spec.rb b/spec/uploaders/lfs_object_uploader_spec.rb index b85892a42b5..9bbfd910ada 100644 --- a/spec/uploaders/lfs_object_uploader_spec.rb +++ b/spec/uploaders/lfs_object_uploader_spec.rb @@ -10,9 +10,9 @@ RSpec.describe LfsObjectUploader do subject { uploader } it_behaves_like "builds correct paths", - store_dir: %r[\h{2}/\h{2}], - cache_dir: %r[/lfs-objects/tmp/cache], - work_dir: %r[/lfs-objects/tmp/work] + store_dir: %r[\h{2}/\h{2}], + cache_dir: %r[/lfs-objects/tmp/cache], + work_dir: %r[/lfs-objects/tmp/work] context "object store is REMOTE" do before do @@ -22,7 +22,7 @@ RSpec.describe LfsObjectUploader do include_context 'with storage', described_class::Store::REMOTE it_behaves_like "builds correct paths", - store_dir: %r[\h{2}/\h{2}] + store_dir: %r[\h{2}/\h{2}] end describe 'remote file' do diff --git a/spec/uploaders/packages/composer/cache_uploader_spec.rb b/spec/uploaders/packages/composer/cache_uploader_spec.rb index 7ceaa24f463..7eea4a839ab 100644 --- a/spec/uploaders/packages/composer/cache_uploader_spec.rb +++ b/spec/uploaders/packages/composer/cache_uploader_spec.rb @@ -9,9 +9,9 @@ RSpec.describe Packages::Composer::CacheUploader do subject { uploader } it_behaves_like "builds correct paths", - store_dir: %r[^\h{2}/\h{2}/\h{64}/packages/composer_cache/\d+$], - cache_dir: %r[/packages/tmp/cache], - work_dir: %r[/packages/tmp/work] + store_dir: %r[^\h{2}/\h{2}/\h{64}/packages/composer_cache/\d+$], + cache_dir: %r[/packages/tmp/cache], + work_dir: %r[/packages/tmp/work] context 'object store is remote' do before do @@ -21,7 +21,7 @@ RSpec.describe Packages::Composer::CacheUploader do include_context 'with storage', described_class::Store::REMOTE it_behaves_like "builds correct paths", - store_dir: %r[^\h{2}/\h{2}/\h{64}/packages/composer_cache/\d+$] + store_dir: %r[^\h{2}/\h{2}/\h{64}/packages/composer_cache/\d+$] end describe 'remote file' do diff --git a/spec/uploaders/packages/debian/component_file_uploader_spec.rb b/spec/uploaders/packages/debian/component_file_uploader_spec.rb index bee82fb2715..84ba751c737 100644 --- a/spec/uploaders/packages/debian/component_file_uploader_spec.rb +++ b/spec/uploaders/packages/debian/component_file_uploader_spec.rb @@ -12,9 +12,9 @@ RSpec.describe Packages::Debian::ComponentFileUploader do subject { uploader } it_behaves_like "builds correct paths", - store_dir: %r[^\h{2}/\h{2}/\h{64}/debian_#{container_type}_component_file/\d+$], - cache_dir: %r[/packages/tmp/cache$], - work_dir: %r[/packages/tmp/work$] + store_dir: %r[^\h{2}/\h{2}/\h{64}/debian_#{container_type}_component_file/\d+$], + cache_dir: %r[/packages/tmp/cache$], + work_dir: %r[/packages/tmp/work$] context 'object store is remote' do before do @@ -24,9 +24,9 @@ RSpec.describe Packages::Debian::ComponentFileUploader do include_context 'with storage', described_class::Store::REMOTE it_behaves_like "builds correct paths", - store_dir: %r[^\h{2}/\h{2}/\h{64}/debian_#{container_type}_component_file/\d+$], - cache_dir: %r[/packages/tmp/cache$], - work_dir: %r[/packages/tmp/work$] + store_dir: %r[^\h{2}/\h{2}/\h{64}/debian_#{container_type}_component_file/\d+$], + cache_dir: %r[/packages/tmp/cache$], + work_dir: %r[/packages/tmp/work$] end describe 'remote file' do diff --git a/spec/uploaders/packages/debian/distribution_release_file_uploader_spec.rb b/spec/uploaders/packages/debian/distribution_release_file_uploader_spec.rb index 96655edb186..df630569856 100644 --- a/spec/uploaders/packages/debian/distribution_release_file_uploader_spec.rb +++ b/spec/uploaders/packages/debian/distribution_release_file_uploader_spec.rb @@ -12,9 +12,9 @@ RSpec.describe Packages::Debian::DistributionReleaseFileUploader do subject { uploader } it_behaves_like "builds correct paths", - store_dir: %r[^\h{2}/\h{2}/\h{64}/debian_#{container_type}_distribution/\d+$], - cache_dir: %r[/packages/tmp/cache$], - work_dir: %r[/packages/tmp/work$] + store_dir: %r[^\h{2}/\h{2}/\h{64}/debian_#{container_type}_distribution/\d+$], + cache_dir: %r[/packages/tmp/cache$], + work_dir: %r[/packages/tmp/work$] context 'object store is remote' do before do @@ -24,9 +24,9 @@ RSpec.describe Packages::Debian::DistributionReleaseFileUploader do include_context 'with storage', described_class::Store::REMOTE it_behaves_like "builds correct paths", - store_dir: %r[^\h{2}/\h{2}/\h{64}/debian_#{container_type}_distribution/\d+$], - cache_dir: %r[/packages/tmp/cache$], - work_dir: %r[/packages/tmp/work$] + store_dir: %r[^\h{2}/\h{2}/\h{64}/debian_#{container_type}_distribution/\d+$], + cache_dir: %r[/packages/tmp/cache$], + work_dir: %r[/packages/tmp/work$] end describe 'remote file' do diff --git a/spec/uploaders/packages/package_file_uploader_spec.rb b/spec/uploaders/packages/package_file_uploader_spec.rb index 7d270ad03c9..ddd9823d55c 100644 --- a/spec/uploaders/packages/package_file_uploader_spec.rb +++ b/spec/uploaders/packages/package_file_uploader_spec.rb @@ -9,9 +9,9 @@ RSpec.describe Packages::PackageFileUploader do subject { uploader } it_behaves_like "builds correct paths", - store_dir: %r[^\h{2}/\h{2}/\h{64}/packages/\d+/files/\d+$], - cache_dir: %r[/packages/tmp/cache], - work_dir: %r[/packages/tmp/work] + store_dir: %r[^\h{2}/\h{2}/\h{64}/packages/\d+/files/\d+$], + cache_dir: %r[/packages/tmp/cache], + work_dir: %r[/packages/tmp/work] context 'object store is remote' do before do @@ -21,7 +21,7 @@ RSpec.describe Packages::PackageFileUploader do include_context 'with storage', described_class::Store::REMOTE it_behaves_like "builds correct paths", - store_dir: %r[^\h{2}/\h{2}/\h{64}/packages/\d+/files/\d+$] + store_dir: %r[^\h{2}/\h{2}/\h{64}/packages/\d+/files/\d+$] end describe 'remote file' do diff --git a/spec/uploaders/packages/rpm/repository_file_uploader_spec.rb b/spec/uploaders/packages/rpm/repository_file_uploader_spec.rb index b3767ae179a..a36a035fde3 100644 --- a/spec/uploaders/packages/rpm/repository_file_uploader_spec.rb +++ b/spec/uploaders/packages/rpm/repository_file_uploader_spec.rb @@ -9,9 +9,9 @@ RSpec.describe Packages::Rpm::RepositoryFileUploader do subject { uploader } it_behaves_like 'builds correct paths', - store_dir: %r[^\h{2}/\h{2}/\h{64}/projects/\d+/rpm/repository_files/\d+$], - cache_dir: %r{/packages/tmp/cache}, - work_dir: %r{/packages/tmp/work} + store_dir: %r[^\h{2}/\h{2}/\h{64}/projects/\d+/rpm/repository_files/\d+$], + cache_dir: %r{/packages/tmp/cache}, + work_dir: %r{/packages/tmp/work} context 'when object store is remote' do before do @@ -21,7 +21,7 @@ RSpec.describe Packages::Rpm::RepositoryFileUploader do include_context 'with storage', described_class::Store::REMOTE it_behaves_like 'builds correct paths', - store_dir: %r[^\h{2}/\h{2}/\h{64}/projects/\d+/rpm/repository_files/\d+$] + store_dir: %r[^\h{2}/\h{2}/\h{64}/projects/\d+/rpm/repository_files/\d+$] end describe 'remote file' do diff --git a/spec/uploaders/pages/deployment_uploader_spec.rb b/spec/uploaders/pages/deployment_uploader_spec.rb index 1832f73bd67..7686efd4fe4 100644 --- a/spec/uploaders/pages/deployment_uploader_spec.rb +++ b/spec/uploaders/pages/deployment_uploader_spec.rb @@ -13,9 +13,9 @@ RSpec.describe Pages::DeploymentUploader do subject { uploader } it_behaves_like "builds correct paths", - store_dir: %r[/\h{2}/\h{2}/\h{64}/pages_deployments/\d+], - cache_dir: %r[pages/@hashed/tmp/cache], - work_dir: %r[pages/@hashed/tmp/work] + store_dir: %r[/\h{2}/\h{2}/\h{64}/pages_deployments/\d+], + cache_dir: %r[pages/@hashed/tmp/cache], + work_dir: %r[pages/@hashed/tmp/work] context 'when object store is REMOTE' do before do diff --git a/spec/uploaders/personal_file_uploader_spec.rb b/spec/uploaders/personal_file_uploader_spec.rb index 1373ccac23d..58edf3f093d 100644 --- a/spec/uploaders/personal_file_uploader_spec.rb +++ b/spec/uploaders/personal_file_uploader_spec.rb @@ -50,9 +50,9 @@ RSpec.describe PersonalFileUploader do context 'object_store is LOCAL' do it_behaves_like 'builds correct paths', - store_dir: %r[uploads/-/system/personal_snippet/\d+/\h+], - upload_path: %r[\h+/\S+], - absolute_path: %r[#{CarrierWave.root}/uploads/-/system/personal_snippet/\d+/\h+/\S+$] + store_dir: %r[uploads/-/system/personal_snippet/\d+/\h+], + upload_path: %r[\h+/\S+], + absolute_path: %r[#{CarrierWave.root}/uploads/-/system/personal_snippet/\d+/\h+/\S+$] it_behaves_like '#base_dir' it_behaves_like '#to_h' @@ -66,8 +66,8 @@ RSpec.describe PersonalFileUploader do include_context 'with storage', described_class::Store::REMOTE it_behaves_like 'builds correct paths', - store_dir: %r[\d+/\h+], - upload_path: %r[^personal_snippet/\d+/\h+/<filename>] + store_dir: %r[\d+/\h+], + upload_path: %r[^personal_snippet/\d+/\h+/<filename>] it_behaves_like '#base_dir' it_behaves_like '#to_h' |