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>2022-03-08 00:18:32 +0300
committerGitLab Bot <gitlab-bot@gitlab.com>2022-03-08 00:18:32 +0300
commit0f6fb8a8c9ccad0d0f4b8c5e2f72aa50d35a0d0d (patch)
treef6f7b86a0d17096a1a27740d5c2fedbd5a3b6815 /spec
parent7fcb54624b31ff4b118d64ca4df36cba6d26c3eb (diff)
Add latest changes from gitlab-org/gitlab@master
Diffstat (limited to 'spec')
-rw-r--r--spec/controllers/projects/ci/secure_files_controller_spec.rb49
-rw-r--r--spec/features/projects/ci/secure_files_spec.rb19
-rw-r--r--spec/frontend/api_spec.js22
-rw-r--r--spec/frontend/ci_secure_files/components/secure_files_list_spec.js139
-rw-r--r--spec/frontend/ci_secure_files/mock_data.js18
-rw-r--r--spec/graphql/types/global_id_type_spec.rb6
-rw-r--r--spec/graphql/types/work_item_id_type_spec.rb51
-rw-r--r--spec/lib/gitlab/import_export/base/relation_object_saver_spec.rb132
-rw-r--r--spec/lib/gitlab/import_export/group/tree_restorer_spec.rb282
-rw-r--r--spec/lib/gitlab/import_export/project/tree_restorer_spec.rb32
-rw-r--r--spec/requests/api/ci/secure_files_spec.rb1
-rw-r--r--spec/requests/api/graphql/work_item_spec.rb11
-rw-r--r--spec/routing/project_routing_spec.rb6
13 files changed, 630 insertions, 138 deletions
diff --git a/spec/controllers/projects/ci/secure_files_controller_spec.rb b/spec/controllers/projects/ci/secure_files_controller_spec.rb
new file mode 100644
index 00000000000..1138897bcc6
--- /dev/null
+++ b/spec/controllers/projects/ci/secure_files_controller_spec.rb
@@ -0,0 +1,49 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+
+RSpec.describe Projects::Ci::SecureFilesController do
+ let_it_be(:project) { create(:project) }
+ let_it_be(:user) { create(:user) }
+
+ subject(:show_request) { get :show, params: { namespace_id: project.namespace, project_id: project } }
+
+ describe 'GET #show' do
+ context 'with enough privileges' do
+ before do
+ sign_in(user)
+ project.add_developer(user)
+ show_request
+ end
+
+ it { expect(response).to have_gitlab_http_status(:ok) }
+
+ it 'renders show page' do
+ expect(response).to render_template :show
+ end
+ end
+
+ context 'without enough privileges' do
+ before do
+ sign_in(user)
+ project.add_reporter(user)
+ show_request
+ end
+
+ it 'responds with 404' do
+ expect(response).to have_gitlab_http_status(:not_found)
+ end
+ end
+
+ context 'an unauthenticated user' do
+ before do
+ show_request
+ end
+
+ it 'redirects to sign in' do
+ expect(response).to have_gitlab_http_status(:found)
+ expect(response).to redirect_to('/users/sign_in')
+ end
+ end
+ end
+end
diff --git a/spec/features/projects/ci/secure_files_spec.rb b/spec/features/projects/ci/secure_files_spec.rb
new file mode 100644
index 00000000000..65c41eaf2ac
--- /dev/null
+++ b/spec/features/projects/ci/secure_files_spec.rb
@@ -0,0 +1,19 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+
+RSpec.describe 'Secure Files', :js do
+ let(:project) { create(:project) }
+ let(:user) { create(:user) }
+
+ before do
+ project.add_maintainer(user)
+ sign_in(user)
+
+ visit project_ci_secure_files_path(project)
+ end
+
+ it 'user sees the Secure Files list component' do
+ expect(page).to have_content('There are no records to show')
+ end
+end
diff --git a/spec/frontend/api_spec.js b/spec/frontend/api_spec.js
index 75faf6d66fa..bc3e12d3fc4 100644
--- a/spec/frontend/api_spec.js
+++ b/spec/frontend/api_spec.js
@@ -1619,6 +1619,28 @@ describe('Api', () => {
});
});
+ describe('projectSecureFiles', () => {
+ it('fetches secure files for a project', async () => {
+ const projectId = 1;
+ const secureFiles = [
+ {
+ id: projectId,
+ title: 'File Name',
+ permissions: 'read_only',
+ checksum: '12345',
+ checksum_algorithm: 'sha256',
+ created_at: '2022-02-21T15:27:18',
+ },
+ ];
+
+ const expectedUrl = `${dummyUrlRoot}/api/${dummyApiVersion}/projects/${projectId}/secure_files`;
+ mock.onGet(expectedUrl).reply(httpStatus.OK, secureFiles);
+ const { data } = await Api.projectSecureFiles(projectId, {});
+
+ expect(data).toEqual(secureFiles);
+ });
+ });
+
describe('Feature Flag User List', () => {
let expectedUrl;
let projectId;
diff --git a/spec/frontend/ci_secure_files/components/secure_files_list_spec.js b/spec/frontend/ci_secure_files/components/secure_files_list_spec.js
new file mode 100644
index 00000000000..042376c71e8
--- /dev/null
+++ b/spec/frontend/ci_secure_files/components/secure_files_list_spec.js
@@ -0,0 +1,139 @@
+import { GlLoadingIcon } from '@gitlab/ui';
+import MockAdapter from 'axios-mock-adapter';
+import { mount } from '@vue/test-utils';
+import axios from '~/lib/utils/axios_utils';
+import SecureFilesList from '~/ci_secure_files/components/secure_files_list.vue';
+import TimeAgoTooltip from '~/vue_shared/components/time_ago_tooltip.vue';
+import waitForPromises from 'helpers/wait_for_promises';
+
+import { secureFiles } from '../mock_data';
+
+const dummyApiVersion = 'v3000';
+const dummyProjectId = 1;
+const dummyUrlRoot = '/gitlab';
+const dummyGon = {
+ api_version: dummyApiVersion,
+ relative_url_root: dummyUrlRoot,
+};
+let originalGon;
+const expectedUrl = `${dummyUrlRoot}/api/${dummyApiVersion}/projects/${dummyProjectId}/secure_files`;
+
+describe('SecureFilesList', () => {
+ let wrapper;
+ let mock;
+
+ beforeEach(() => {
+ originalGon = window.gon;
+ window.gon = { ...dummyGon };
+ });
+
+ afterEach(() => {
+ wrapper.destroy();
+ mock.restore();
+ window.gon = originalGon;
+ });
+
+ const createWrapper = (props = {}) => {
+ wrapper = mount(SecureFilesList, {
+ provide: { projectId: dummyProjectId },
+ ...props,
+ });
+ };
+
+ const findRows = () => wrapper.findAll('tbody tr');
+ const findRowAt = (i) => findRows().at(i);
+ const findCell = (i, col) => findRowAt(i).findAll('td').at(col);
+ const findHeaderAt = (i) => wrapper.findAll('thead th').at(i);
+ const findPagination = () => wrapper.findAll('ul.pagination');
+ const findLoadingIcon = () => wrapper.findComponent(GlLoadingIcon);
+
+ describe('when secure files exist in a project', () => {
+ beforeEach(async () => {
+ mock = new MockAdapter(axios);
+ mock.onGet(expectedUrl).reply(200, secureFiles);
+
+ createWrapper();
+ await waitForPromises();
+ });
+
+ it('displays a table with expected headers', () => {
+ const headers = ['Filename', 'Permissions', 'Uploaded'];
+ headers.forEach((header, i) => {
+ expect(findHeaderAt(i).text()).toBe(header);
+ });
+ });
+
+ it('displays a table with rows', () => {
+ expect(findRows()).toHaveLength(secureFiles.length);
+
+ const [secureFile] = secureFiles;
+
+ expect(findCell(0, 0).text()).toBe(secureFile.name);
+ expect(findCell(0, 1).text()).toBe(secureFile.permissions);
+ expect(findCell(0, 2).find(TimeAgoTooltip).props('time')).toBe(secureFile.created_at);
+ });
+ });
+
+ describe('when no secure files exist in a project', () => {
+ beforeEach(async () => {
+ mock = new MockAdapter(axios);
+ mock.onGet(expectedUrl).reply(200, []);
+
+ createWrapper();
+ await waitForPromises();
+ });
+
+ it('displays a table with expected headers', () => {
+ const headers = ['Filename', 'Permissions', 'Uploaded'];
+ headers.forEach((header, i) => {
+ expect(findHeaderAt(i).text()).toBe(header);
+ });
+ });
+
+ it('displays a table with a no records message', () => {
+ expect(findCell(0, 0).text()).toBe('There are no records to show');
+ });
+ });
+
+ describe('pagination', () => {
+ it('displays the pagination component with there are more than 20 items', async () => {
+ mock = new MockAdapter(axios);
+ mock.onGet(expectedUrl).reply(200, secureFiles, { 'x-total': 30 });
+
+ createWrapper();
+ await waitForPromises();
+
+ expect(findPagination().exists()).toBe(true);
+ });
+
+ it('does not display the pagination component with there are 20 items', async () => {
+ mock = new MockAdapter(axios);
+ mock.onGet(expectedUrl).reply(200, secureFiles, { 'x-total': 20 });
+
+ createWrapper();
+ await waitForPromises();
+
+ expect(findPagination().exists()).toBe(false);
+ });
+ });
+
+ describe('loading state', () => {
+ it('displays the loading icon while waiting for the backend request', () => {
+ mock = new MockAdapter(axios);
+ mock.onGet(expectedUrl).reply(200, secureFiles);
+ createWrapper();
+
+ expect(findLoadingIcon().exists()).toBe(true);
+ });
+
+ it('does not display the loading icon after the backend request has completed', async () => {
+ mock = new MockAdapter(axios);
+ mock.onGet(expectedUrl).reply(200, secureFiles);
+
+ createWrapper();
+ await waitForPromises();
+
+ expect(findLoadingIcon().exists()).toBe(false);
+ });
+ });
+});
diff --git a/spec/frontend/ci_secure_files/mock_data.js b/spec/frontend/ci_secure_files/mock_data.js
new file mode 100644
index 00000000000..5a9e16d1ad6
--- /dev/null
+++ b/spec/frontend/ci_secure_files/mock_data.js
@@ -0,0 +1,18 @@
+export const secureFiles = [
+ {
+ id: 1,
+ name: 'myfile.jks',
+ checksum: '16630b189ab34b2e3504f4758e1054d2e478deda510b2b08cc0ef38d12e80aac',
+ checksum_algorithm: 'sha256',
+ permissions: 'read_only',
+ created_at: '2022-02-22T22:22:22.222Z',
+ },
+ {
+ id: 2,
+ name: 'myotherfile.jks',
+ checksum: '16630b189ab34b2e3504f4758e1054d2e478deda510b2b08cc0ef38d12e80aa2',
+ checksum_algorithm: 'sha256',
+ permissions: 'execute',
+ created_at: '2022-02-22T22:22:22.222Z',
+ },
+];
diff --git a/spec/graphql/types/global_id_type_spec.rb b/spec/graphql/types/global_id_type_spec.rb
index e7e69cfad9e..f9391efdf08 100644
--- a/spec/graphql/types/global_id_type_spec.rb
+++ b/spec/graphql/types/global_id_type_spec.rb
@@ -376,4 +376,10 @@ RSpec.describe Types::GlobalIDType do
expect(described_class.model_name_to_graphql_name('DesignManagement::Design')).to eq('DesignManagementDesignID')
end
end
+
+ describe '.[]' do
+ it 'returns a custom class for work items' do
+ expect(described_class[::WorkItem]).to eq(::Types::WorkItemIdType)
+ end
+ end
end
diff --git a/spec/graphql/types/work_item_id_type_spec.rb b/spec/graphql/types/work_item_id_type_spec.rb
new file mode 100644
index 00000000000..dc02401a3d0
--- /dev/null
+++ b/spec/graphql/types/work_item_id_type_spec.rb
@@ -0,0 +1,51 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+
+RSpec.describe Types::WorkItemIdType do
+ let_it_be(:project) { create(:project) }
+ let_it_be(:work_item) { create(:work_item, project: project) }
+ let_it_be(:issue) { create(:issue, project: project) }
+
+ let(:work_item_gid) { work_item.to_gid }
+ let(:issue_gid) { issue.to_gid }
+ let(:ctx) { {} }
+
+ describe '.coerce_input' do
+ it 'can coerce valid issue input' do
+ coerced = described_class.coerce_input(issue_gid.to_s, ctx)
+
+ expect(coerced).to eq(WorkItem.find(issue.id).to_gid)
+ end
+
+ it 'can coerce valid work item input' do
+ coerced = described_class.coerce_input(work_item_gid.to_s, ctx)
+
+ expect(coerced).to eq(work_item_gid)
+ end
+
+ it 'fails for other input types' do
+ project_gid = project.to_gid
+
+ expect { described_class.coerce_input(project_gid.to_s, ctx) }
+ .to raise_error(GraphQL::CoercionError, "#{project_gid.to_s.inspect} does not represent an instance of WorkItem")
+ end
+ end
+
+ describe '.coerce_result' do
+ it 'can coerce issue results and return a WorkItem global ID' do
+ expect(described_class.coerce_result(issue_gid, ctx)).to eq(WorkItem.find(issue.id).to_gid.to_s)
+ end
+
+ it 'can coerce work item results' do
+ expect(described_class.coerce_result(work_item_gid, ctx)).to eq(work_item_gid.to_s)
+ end
+
+ it 'fails for other input types' do
+ project_gid = project.to_gid
+
+ expect { described_class.coerce_result(project_gid, ctx) }
+ .to raise_error(GraphQL::CoercionError, "Expected a WorkItem ID, got #{project_gid}")
+ end
+ end
+end
diff --git a/spec/lib/gitlab/import_export/base/relation_object_saver_spec.rb b/spec/lib/gitlab/import_export/base/relation_object_saver_spec.rb
new file mode 100644
index 00000000000..7c84b9604a6
--- /dev/null
+++ b/spec/lib/gitlab/import_export/base/relation_object_saver_spec.rb
@@ -0,0 +1,132 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+
+RSpec.describe Gitlab::ImportExport::Base::RelationObjectSaver do
+ let(:project) { create(:project) }
+ let(:relation_object) { build(:issue, project: project) }
+ let(:relation_definition) { {} }
+ let(:importable) { project }
+ let(:relation_key) { 'issues' }
+
+ subject(:saver) do
+ described_class.new(
+ relation_object: relation_object,
+ relation_key: relation_key,
+ relation_definition: relation_definition,
+ importable: importable
+ )
+ end
+
+ describe '#save' do
+ before do
+ expect(relation_object).to receive(:save!).and_call_original
+ end
+
+ it 'saves relation object' do
+ expect { saver.execute }.to change(project.issues, :count).by(1)
+ end
+
+ context 'when subrelation is present' do
+ let(:notes) { build_list(:note, 6, project: project, importing: true) }
+ let(:relation_object) { build(:issue, project: project, notes: notes) }
+ let(:relation_definition) { { 'notes' => {} } }
+
+ it 'saves relation object with subrelations' do
+ expect(relation_object.notes).to receive(:<<).and_call_original
+
+ saver.execute
+
+ issue = project.issues.last
+ expect(issue.notes.count).to eq(6)
+ end
+ end
+
+ context 'when subrelation is not a collection' do
+ let(:sentry_issue) { build(:sentry_issue, importing: true) }
+ let(:relation_object) { build(:issue, project: project, sentry_issue: sentry_issue) }
+ let(:relation_definition) { { 'sentry_issue' => {} } }
+
+ it 'saves subrelation as part of the relation object itself' do
+ expect(relation_object.notes).not_to receive(:<<)
+
+ saver.execute
+
+ issue = project.issues.last
+ expect(issue.sentry_issue.persisted?).to eq(true)
+ end
+ end
+
+ context 'when subrelation collection count is small' do
+ let(:notes) { build_list(:note, 2, project: project, importing: true) }
+ let(:relation_object) { build(:issue, project: project, notes: notes) }
+ let(:relation_definition) { { 'notes' => {} } }
+
+ it 'saves subrelation as part of the relation object itself' do
+ expect(relation_object.notes).not_to receive(:<<)
+
+ saver.execute
+
+ issue = project.issues.last
+ expect(issue.notes.count).to eq(2)
+ end
+ end
+
+ context 'when some subrelations are invalid' do
+ let(:notes) { build_list(:note, 5, project: project, importing: true) }
+ let(:invalid_note) { build(:note) }
+ let(:relation_object) { build(:issue, project: project, notes: notes + [invalid_note]) }
+ let(:relation_definition) { { 'notes' => {} } }
+
+ it 'saves valid subrelations and logs invalid subrelation' do
+ expect(relation_object.notes).to receive(:<<).and_call_original
+ expect(Gitlab::Import::Logger)
+ .to receive(:info)
+ .with(
+ message: '[Project/Group Import] Invalid subrelation',
+ project_id: project.id,
+ relation_key: 'issues',
+ error_messages: "Noteable can't be blank and Project does not match noteable project"
+ )
+
+ saver.execute
+
+ issue = project.issues.last
+ import_failure = project.import_failures.last
+
+ expect(issue.notes.count).to eq(5)
+ expect(import_failure.source).to eq('RelationObjectSaver#save!')
+ expect(import_failure.exception_message).to eq("Noteable can't be blank and Project does not match noteable project")
+ end
+
+ context 'when importable is group' do
+ let(:relation_key) { 'labels' }
+ let(:relation_definition) { { 'priorities' => {} } }
+ let(:importable) { create(:group) }
+ let(:valid_priorities) { build_list(:label_priority, 5, importing: true) }
+ let(:invalid_priority) { build(:label_priority, priority: -1) }
+ let(:relation_object) { build(:group_label, group: importable, title: 'test', priorities: valid_priorities + [invalid_priority]) }
+
+ it 'logs invalid subrelation for a group' do
+ expect(Gitlab::Import::Logger)
+ .to receive(:info)
+ .with(
+ message: '[Project/Group Import] Invalid subrelation',
+ group_id: importable.id,
+ relation_key: 'labels',
+ error_messages: 'Priority must be greater than or equal to 0'
+ )
+
+ saver.execute
+
+ label = importable.labels.last
+ import_failure = importable.import_failures.last
+
+ expect(label.priorities.count).to eq(5)
+ expect(import_failure.source).to eq('RelationObjectSaver#save!')
+ expect(import_failure.exception_message).to eq('Priority must be greater than or equal to 0')
+ end
+ end
+ end
+ end
+end
diff --git a/spec/lib/gitlab/import_export/group/tree_restorer_spec.rb b/spec/lib/gitlab/import_export/group/tree_restorer_spec.rb
index b67d42d1b71..9b01005c2e9 100644
--- a/spec/lib/gitlab/import_export/group/tree_restorer_spec.rb
+++ b/spec/lib/gitlab/import_export/group/tree_restorer_spec.rb
@@ -5,116 +5,117 @@ require 'spec_helper'
RSpec.describe Gitlab::ImportExport::Group::TreeRestorer do
include ImportExport::CommonUtil
- describe 'restore group tree' do
- before_all do
- # Using an admin for import, so we can check assignment of existing members
- user = create(:admin, email: 'root@gitlabexample.com')
- create(:user, email: 'adriene.mcclure@gitlabexample.com')
- create(:user, email: 'gwendolyn_robel@gitlabexample.com')
+ shared_examples 'group restoration' do
+ describe 'restore group tree' do
+ before_all do
+ # Using an admin for import, so we can check assignment of existing members
+ user = create(:admin, email: 'root@gitlabexample.com')
+ create(:user, email: 'adriene.mcclure@gitlabexample.com')
+ create(:user, email: 'gwendolyn_robel@gitlabexample.com')
- RSpec::Mocks.with_temporary_scope do
- @group = create(:group, name: 'group', path: 'group')
- @shared = Gitlab::ImportExport::Shared.new(@group)
+ RSpec::Mocks.with_temporary_scope do
+ @group = create(:group, name: 'group', path: 'group')
+ @shared = Gitlab::ImportExport::Shared.new(@group)
- setup_import_export_config('group_exports/complex')
+ setup_import_export_config('group_exports/complex')
- group_tree_restorer = described_class.new(user: user, shared: @shared, group: @group)
+ group_tree_restorer = described_class.new(user: user, shared: @shared, group: @group)
- expect(group_tree_restorer.restore).to be_truthy
- expect(group_tree_restorer.groups_mapping).not_to be_empty
+ expect(group_tree_restorer.restore).to be_truthy
+ expect(group_tree_restorer.groups_mapping).not_to be_empty
+ end
end
- end
-
- it 'has the group description' do
- expect(Group.find_by_path('group').description).to eq('Group Description')
- end
- it 'has group labels' do
- expect(@group.labels.count).to eq(10)
- end
+ it 'has the group description' do
+ expect(Group.find_by_path('group').description).to eq('Group Description')
+ end
- context 'issue boards' do
- it 'has issue boards' do
- expect(@group.boards.count).to eq(1)
+ it 'has group labels' do
+ expect(@group.labels.count).to eq(10)
end
- it 'has board label lists' do
- lists = @group.boards.find_by(name: 'first board').lists
+ context 'issue boards' do
+ it 'has issue boards' do
+ expect(@group.boards.count).to eq(1)
+ end
+
+ it 'has board label lists' do
+ lists = @group.boards.find_by(name: 'first board').lists
- expect(lists.count).to eq(3)
- expect(lists.first.label.title).to eq('TSL')
- expect(lists.second.label.title).to eq('Sosync')
+ expect(lists.count).to eq(3)
+ expect(lists.first.label.title).to eq('TSL')
+ expect(lists.second.label.title).to eq('Sosync')
+ end
end
- end
- it 'has badges' do
- expect(@group.badges.count).to eq(1)
- end
+ it 'has badges' do
+ expect(@group.badges.count).to eq(1)
+ end
- it 'has milestones' do
- expect(@group.milestones.count).to eq(5)
- end
+ it 'has milestones' do
+ expect(@group.milestones.count).to eq(5)
+ end
- it 'has group children' do
- expect(@group.children.count).to eq(2)
- end
+ it 'has group children' do
+ expect(@group.children.count).to eq(2)
+ end
- it 'has group members' do
- expect(@group.members.map(&:user).map(&:email)).to contain_exactly(
- 'root@gitlabexample.com',
- 'adriene.mcclure@gitlabexample.com',
- 'gwendolyn_robel@gitlabexample.com'
- )
+ it 'has group members' do
+ expect(@group.members.map(&:user).map(&:email)).to contain_exactly(
+ 'root@gitlabexample.com',
+ 'adriene.mcclure@gitlabexample.com',
+ 'gwendolyn_robel@gitlabexample.com'
+ )
+ end
end
- end
- context 'child with no parent' do
- let(:user) { create(:user) }
- let(:group) { create(:group) }
- let(:shared) { Gitlab::ImportExport::Shared.new(group) }
- let(:group_tree_restorer) { described_class.new(user: user, shared: shared, group: group) }
+ context 'child with no parent' do
+ let(:user) { create(:user) }
+ let(:group) { create(:group) }
+ let(:shared) { Gitlab::ImportExport::Shared.new(group) }
+ let(:group_tree_restorer) { described_class.new(user: user, shared: shared, group: group) }
- before do
- setup_import_export_config('group_exports/child_with_no_parent')
- end
+ before do
+ setup_import_export_config('group_exports/child_with_no_parent')
+ end
- it 'captures import failures when a child group does not have a valid parent_id' do
- group_tree_restorer.restore
+ it 'captures import failures when a child group does not have a valid parent_id' do
+ group_tree_restorer.restore
- expect(group.import_failures.first.exception_message).to eq('Parent group not found')
+ expect(group.import_failures.first.exception_message).to eq('Parent group not found')
+ end
end
- end
- context 'when child group creation fails' do
- let(:user) { create(:user) }
- let(:group) { create(:group) }
- let(:shared) { Gitlab::ImportExport::Shared.new(group) }
- let(:group_tree_restorer) { described_class.new(user: user, shared: shared, group: group) }
+ context 'when child group creation fails' do
+ let(:user) { create(:user) }
+ let(:group) { create(:group) }
+ let(:shared) { Gitlab::ImportExport::Shared.new(group) }
+ let(:group_tree_restorer) { described_class.new(user: user, shared: shared, group: group) }
- before do
- setup_import_export_config('group_exports/child_short_name')
- end
+ before do
+ setup_import_export_config('group_exports/child_short_name')
+ end
- it 'captures import failure' do
- exception_message = 'Validation failed: Group URL is too short (minimum is 2 characters)'
+ it 'captures import failure' do
+ exception_message = 'Validation failed: Group URL is too short (minimum is 2 characters)'
- group_tree_restorer.restore
+ group_tree_restorer.restore
- expect(group.import_failures.first.exception_message).to eq(exception_message)
+ expect(group.import_failures.first.exception_message).to eq(exception_message)
+ end
end
- end
- context 'excluded attributes' do
- let!(:source_user) { create(:user, id: 123) }
- let!(:importer_user) { create(:user) }
- let(:group) { create(:group, name: 'user-inputed-name', path: 'user-inputed-path') }
- let(:shared) { Gitlab::ImportExport::Shared.new(group) }
- let(:group_tree_restorer) { described_class.new(user: importer_user, shared: shared, group: group) }
- let(:exported_file) { File.join(shared.export_path, 'tree/groups/4352.json') }
- let(:group_json) { Gitlab::Json.parse(IO.read(exported_file)) }
-
- shared_examples 'excluded attributes' do
- excluded_attributes = %w[
+ context 'excluded attributes' do
+ let!(:source_user) { create(:user, id: 123) }
+ let!(:importer_user) { create(:user) }
+ let(:group) { create(:group, name: 'user-inputed-name', path: 'user-inputed-path') }
+ let(:shared) { Gitlab::ImportExport::Shared.new(group) }
+ let(:group_tree_restorer) { described_class.new(user: importer_user, shared: shared, group: group) }
+ let(:exported_file) { File.join(shared.export_path, 'tree/groups/4352.json') }
+ let(:group_json) { Gitlab::Json.parse(IO.read(exported_file)) }
+
+ shared_examples 'excluded attributes' do
+ excluded_attributes = %w[
id
parent_id
owner_id
@@ -125,80 +126,97 @@ RSpec.describe Gitlab::ImportExport::Group::TreeRestorer do
saml_discovery_token
]
- before do
- group.add_owner(importer_user)
+ before do
+ group.add_owner(importer_user)
- setup_import_export_config('group_exports/complex')
+ setup_import_export_config('group_exports/complex')
- expect(File.exist?(exported_file)).to be_truthy
+ expect(File.exist?(exported_file)).to be_truthy
- group_tree_restorer.restore
- group.reload
- end
+ group_tree_restorer.restore
+ group.reload
+ end
- it 'does not import root group name' do
- expect(group.name).to eq('user-inputed-name')
- end
+ it 'does not import root group name' do
+ expect(group.name).to eq('user-inputed-name')
+ end
- it 'does not import root group path' do
- expect(group.path).to eq('user-inputed-path')
- end
+ it 'does not import root group path' do
+ expect(group.path).to eq('user-inputed-path')
+ end
- excluded_attributes.each do |excluded_attribute|
- it 'does not allow override of excluded attributes' do
- unless group.public_send(excluded_attribute).nil?
- expect(group_json[excluded_attribute]).not_to eq(group.public_send(excluded_attribute))
+ excluded_attributes.each do |excluded_attribute|
+ it 'does not allow override of excluded attributes' do
+ unless group.public_send(excluded_attribute).nil?
+ expect(group_json[excluded_attribute]).not_to eq(group.public_send(excluded_attribute))
+ end
end
end
end
- end
- include_examples 'excluded attributes'
- end
+ include_examples 'excluded attributes'
+ end
- context 'group.json file access check' do
- let(:user) { create(:user) }
- let!(:group) { create(:group, name: 'group2', path: 'group2') }
- let(:shared) { Gitlab::ImportExport::Shared.new(group) }
- let(:group_tree_restorer) { described_class.new(user: user, shared: shared, group: group) }
+ context 'group.json file access check' do
+ let(:user) { create(:user) }
+ let!(:group) { create(:group, name: 'group2', path: 'group2') }
+ let(:shared) { Gitlab::ImportExport::Shared.new(group) }
+ let(:group_tree_restorer) { described_class.new(user: user, shared: shared, group: group) }
- it 'does not read a symlink' do
- Dir.mktmpdir do |tmpdir|
- FileUtils.mkdir_p(File.join(tmpdir, 'tree', 'groups'))
- setup_symlink(tmpdir, 'tree/groups/_all.ndjson')
+ it 'does not read a symlink' do
+ Dir.mktmpdir do |tmpdir|
+ FileUtils.mkdir_p(File.join(tmpdir, 'tree', 'groups'))
+ setup_symlink(tmpdir, 'tree/groups/_all.ndjson')
- allow(shared).to receive(:export_path).and_return(tmpdir)
+ allow(shared).to receive(:export_path).and_return(tmpdir)
- expect(group_tree_restorer.restore).to eq(false)
- expect(shared.errors).to include('Incorrect JSON format')
+ expect(group_tree_restorer.restore).to eq(false)
+ expect(shared.errors).to include('Incorrect JSON format')
+ end
end
end
- end
- context 'group visibility levels' do
- let(:user) { create(:user) }
- let(:shared) { Gitlab::ImportExport::Shared.new(group) }
- let(:group_tree_restorer) { described_class.new(user: user, shared: shared, group: group) }
+ context 'group visibility levels' do
+ let(:user) { create(:user) }
+ let(:shared) { Gitlab::ImportExport::Shared.new(group) }
+ let(:group_tree_restorer) { described_class.new(user: user, shared: shared, group: group) }
- before do
- setup_import_export_config(filepath)
+ before do
+ setup_import_export_config(filepath)
- group_tree_restorer.restore
- end
+ group_tree_restorer.restore
+ end
- shared_examples 'with visibility level' do |visibility_level, expected_visibilities|
- context "when visibility level is #{visibility_level}" do
- let(:group) { create(:group, visibility_level) }
- let(:filepath) { "group_exports/visibility_levels/#{visibility_level}" }
+ shared_examples 'with visibility level' do |visibility_level, expected_visibilities|
+ context "when visibility level is #{visibility_level}" do
+ let(:group) { create(:group, visibility_level) }
+ let(:filepath) { "group_exports/visibility_levels/#{visibility_level}" }
- it "imports all subgroups as #{visibility_level}" do
- expect(group.children.map(&:visibility_level)).to match_array(expected_visibilities)
+ it "imports all subgroups as #{visibility_level}" do
+ expect(group.children.map(&:visibility_level)).to match_array(expected_visibilities)
+ end
end
end
+
+ include_examples 'with visibility level', :public, [20, 10, 0]
+ include_examples 'with visibility level', :private, [0, 0, 0]
+ include_examples 'with visibility level', :internal, [10, 10, 0]
+ end
+ end
+
+ context 'when import_relation_object_persistence feature flag is enabled' do
+ before do
+ stub_feature_flags(import_relation_object_persistence: true)
+ end
+
+ include_examples 'group restoration'
+ end
+
+ context 'when import_relation_object_persistence feature flag is disabled' do
+ before do
+ stub_feature_flags(import_relation_object_persistence: false)
end
- include_examples 'with visibility level', :public, [20, 10, 0]
- include_examples 'with visibility level', :private, [0, 0, 0]
- include_examples 'with visibility level', :internal, [10, 10, 0]
+ include_examples 'group restoration'
end
end
diff --git a/spec/lib/gitlab/import_export/project/tree_restorer_spec.rb b/spec/lib/gitlab/import_export/project/tree_restorer_spec.rb
index 8884722254d..fdf8260c058 100644
--- a/spec/lib/gitlab/import_export/project/tree_restorer_spec.rb
+++ b/spec/lib/gitlab/import_export/project/tree_restorer_spec.rb
@@ -1058,13 +1058,35 @@ RSpec.describe Gitlab::ImportExport::Project::TreeRestorer do
end
end
- context 'enable ndjson import' do
- it_behaves_like 'project tree restorer work properly', :legacy_reader, true
+ context 'when import_relation_object_persistence feature flag is enabled' do
+ before do
+ stub_feature_flags(import_relation_object_persistence: true)
+ end
+
+ context 'enable ndjson import' do
+ it_behaves_like 'project tree restorer work properly', :legacy_reader, true
+
+ it_behaves_like 'project tree restorer work properly', :ndjson_reader, true
+ end
- it_behaves_like 'project tree restorer work properly', :ndjson_reader, true
+ context 'disable ndjson import' do
+ it_behaves_like 'project tree restorer work properly', :legacy_reader, false
+ end
end
- context 'disable ndjson import' do
- it_behaves_like 'project tree restorer work properly', :legacy_reader, false
+ context 'when import_relation_object_persistence feature flag is disabled' do
+ before do
+ stub_feature_flags(import_relation_object_persistence: false)
+ end
+
+ context 'enable ndjson import' do
+ it_behaves_like 'project tree restorer work properly', :legacy_reader, true
+
+ it_behaves_like 'project tree restorer work properly', :ndjson_reader, true
+ end
+
+ context 'disable ndjson import' do
+ it_behaves_like 'project tree restorer work properly', :legacy_reader, false
+ end
end
end
diff --git a/spec/requests/api/ci/secure_files_spec.rb b/spec/requests/api/ci/secure_files_spec.rb
index 5cf6999f60a..c1700bf5760 100644
--- a/spec/requests/api/ci/secure_files_spec.rb
+++ b/spec/requests/api/ci/secure_files_spec.rb
@@ -154,6 +154,7 @@ RSpec.describe API::Ci::SecureFiles do
Digest::SHA256.hexdigest(fixture_file('ci_secure_files/upload-keystore.jks'))
)
expect(json_response['id']).to eq(secure_file.id)
+ expect(Time.parse(json_response['created_at'])).to be_like_time(secure_file.created_at)
end
it 'creates a secure file with read_only permissions by default' do
diff --git a/spec/requests/api/graphql/work_item_spec.rb b/spec/requests/api/graphql/work_item_spec.rb
index 5ed9a3bf7e6..bc5a8b3e006 100644
--- a/spec/requests/api/graphql/work_item_spec.rb
+++ b/spec/requests/api/graphql/work_item_spec.rb
@@ -12,9 +12,10 @@ RSpec.describe 'Query.work_item(id)' do
let(:current_user) { developer }
let(:work_item_data) { graphql_data['workItem'] }
let(:work_item_fields) { all_graphql_fields_for('WorkItem') }
+ let(:global_id) { work_item.to_gid.to_s }
let(:query) do
- graphql_query_for('workItem', { 'id' => work_item.to_gid.to_s }, work_item_fields)
+ graphql_query_for('workItem', { 'id' => global_id }, work_item_fields)
end
context 'when the user can read the work item' do
@@ -35,6 +36,14 @@ RSpec.describe 'Query.work_item(id)' do
'workItemType' => hash_including('id' => work_item.work_item_type.to_gid.to_s)
)
end
+
+ context 'when an Issue Global ID is provided' do
+ let(:global_id) { Issue.find(work_item.id).to_gid.to_s }
+
+ it 'allows an Issue GID as input' do
+ expect(work_item_data).to include('id' => work_item.to_gid.to_s)
+ end
+ end
end
context 'when the user can not read the work item' do
diff --git a/spec/routing/project_routing_spec.rb b/spec/routing/project_routing_spec.rb
index f3d0179ffdd..425b758e748 100644
--- a/spec/routing/project_routing_spec.rb
+++ b/spec/routing/project_routing_spec.rb
@@ -899,6 +899,12 @@ RSpec.describe 'project routing' do
end
end
+ describe Projects::Ci::SecureFilesController, 'routing' do
+ it 'to #show' do
+ expect(get('/gitlab/gitlabhq/-/ci/secure_files')).to route_to('projects/ci/secure_files#show', namespace_id: 'gitlab', project_id: 'gitlabhq')
+ end
+ end
+
context 'with a non-existent project' do
it 'routes to 404 with get request' do
expect(get: "/gitlab/not_exist").to route_to(