Welcome to mirror list, hosted at ThFree Co, Russian Federation.

gitlab.com/gitlab-org/gitlab-foss.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
path: root/spec
diff options
context:
space:
mode:
authorGitLab Bot <gitlab-bot@gitlab.com>2023-04-04 15:15:02 +0300
committerGitLab Bot <gitlab-bot@gitlab.com>2023-04-04 15:15:02 +0300
commitf00510286b6ccda154c4926503397590a8851939 (patch)
treec4cd69ef2d0d6dd5abf30e3963ac00ead7421a19 /spec
parent9e5c2e7342d1393f90e74a2ae4b3f27492c22e1f (diff)
Add latest changes from gitlab-org/gitlab@master
Diffstat (limited to 'spec')
-rw-r--r--spec/factories/ci/reports/security/reports.rb13
-rw-r--r--spec/features/nav/pinned_nav_items_spec.rb159
-rw-r--r--spec/fixtures/api/schemas/internal/pages/lookup_path.json3
-rw-r--r--spec/fixtures/pages_with_custom_root.zipbin0 -> 631 bytes
-rw-r--r--spec/fixtures/pages_with_custom_root.zip.metabin0 -> 175 bytes
-rw-r--r--spec/fixtures/pages_with_custom_root.zip.meta0bin0 -> 197 bytes
-rw-r--r--spec/frontend/super_sidebar/components/sidebar_menu_spec.js151
-rw-r--r--spec/frontend/super_sidebar/mock_data.js3
-rw-r--r--spec/helpers/ci/catalog/resources_helper_spec.rb8
-rw-r--r--spec/helpers/sidebars_helper_spec.rb17
-rw-r--r--spec/lib/bulk_imports/clients/http_spec.rb4
-rw-r--r--spec/lib/bulk_imports/groups/pipelines/project_entities_pipeline_spec.rb18
-rw-r--r--spec/lib/bulk_imports/groups/transformers/group_attributes_transformer_spec.rb87
-rw-r--r--spec/lib/bulk_imports/projects/pipelines/project_pipeline_spec.rb2
-rw-r--r--spec/lib/bulk_imports/projects/transformers/project_attributes_transformer_spec.rb56
-rw-r--r--spec/lib/gitlab/bullet/exclusions_spec.rb15
-rw-r--r--spec/lib/gitlab/ci/variables/builder_spec.rb4
-rw-r--r--spec/lib/gitlab/regex_spec.rb49
-rw-r--r--spec/migrations/ensure_issue_user_mentions_bigint_backfill_is_finished_for_gitlab_dot_com_spec.rb35
-rw-r--r--spec/models/bulk_imports/entity_spec.rb29
-rw-r--r--spec/models/ci/build_spec.rb2
-rw-r--r--spec/models/ci/catalog/listing_spec.rb1
-rw-r--r--spec/models/pages/lookup_path_spec.rb23
-rw-r--r--spec/models/user_spec.rb3
-rw-r--r--spec/policies/project_policy_spec.rb110
-rw-r--r--spec/requests/api/bulk_imports_spec.rb41
-rw-r--r--spec/requests/api/graphql/ci/config_variables_spec.rb33
-rw-r--r--spec/requests/api/internal/pages_spec.rb12
-rw-r--r--spec/requests/users/pins_spec.rb67
-rw-r--r--spec/rubocop/cop/sidekiq_load_balancing/worker_data_consistency_spec.rb123
-rw-r--r--spec/serializers/import/bulk_import_entity_spec.rb2
-rw-r--r--spec/services/bulk_imports/create_service_spec.rb78
-rw-r--r--spec/services/ci/catalog/add_resource_service_spec.rb55
-rw-r--r--spec/services/projects/update_pages_service_spec.rb46
-rw-r--r--spec/support/helpers/project_template_test_helper.rb2
-rw-r--r--spec/uploaders/attachment_uploader_spec.rb10
-rw-r--r--spec/uploaders/avatar_uploader_spec.rb10
-rw-r--r--spec/uploaders/ci/pipeline_artifact_uploader_spec.rb6
-rw-r--r--spec/uploaders/dependency_proxy/file_uploader_spec.rb9
-rw-r--r--spec/uploaders/design_management/design_v432x230_uploader_spec.rb14
-rw-r--r--spec/uploaders/external_diff_uploader_spec.rb8
-rw-r--r--spec/uploaders/file_uploader_spec.rb14
-rw-r--r--spec/uploaders/job_artifact_uploader_spec.rb8
-rw-r--r--spec/uploaders/lfs_object_uploader_spec.rb8
-rw-r--r--spec/uploaders/packages/composer/cache_uploader_spec.rb8
-rw-r--r--spec/uploaders/packages/debian/component_file_uploader_spec.rb12
-rw-r--r--spec/uploaders/packages/debian/distribution_release_file_uploader_spec.rb12
-rw-r--r--spec/uploaders/packages/package_file_uploader_spec.rb8
-rw-r--r--spec/uploaders/packages/rpm/repository_file_uploader_spec.rb8
-rw-r--r--spec/uploaders/pages/deployment_uploader_spec.rb6
-rw-r--r--spec/uploaders/personal_file_uploader_spec.rb10
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
new file mode 100644
index 00000000000..40dea253245
--- /dev/null
+++ b/spec/fixtures/pages_with_custom_root.zip
Binary files differ
diff --git a/spec/fixtures/pages_with_custom_root.zip.meta b/spec/fixtures/pages_with_custom_root.zip.meta
new file mode 100644
index 00000000000..2cb04e0c33b
--- /dev/null
+++ b/spec/fixtures/pages_with_custom_root.zip.meta
Binary files differ
diff --git a/spec/fixtures/pages_with_custom_root.zip.meta0 b/spec/fixtures/pages_with_custom_root.zip.meta0
new file mode 100644
index 00000000000..9b348055b5f
--- /dev/null
+++ b/spec/fixtures/pages_with_custom_root.zip.meta0
Binary files differ
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'