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-05-26 18:08:29 +0300
committerGitLab Bot <gitlab-bot@gitlab.com>2022-05-26 18:08:29 +0300
commitcac0926ddbef9d1505c2a23ccad60fd47c7d050a (patch)
treeef6808933314a104d004528f7c84716dc472d93a /spec
parentafd476d5fd62d31c7e0b7509691fa18e632a60df (diff)
Add latest changes from gitlab-org/gitlab@master
Diffstat (limited to 'spec')
-rw-r--r--spec/features/merge_request/user_views_user_status_on_merge_request_spec.rb10
-rw-r--r--spec/frontend/content_editor/remark_markdown_processing_spec.js90
-rw-r--r--spec/frontend/vue_mr_widget/components/mr_widget_header_spec.js176
-rw-r--r--spec/helpers/issues_helper_spec.rb93
-rw-r--r--spec/lib/gitlab/ci/config/external/mapper_spec.rb25
-rw-r--r--spec/lib/gitlab/usage/service_ping_report_spec.rb29
-rw-r--r--spec/models/user_spec.rb20
-rw-r--r--spec/requests/api/personal_access_tokens_spec.rb55
-rw-r--r--spec/requests/api/users_spec.rb20
-rw-r--r--spec/services/users/destroy_service_spec.rb21
-rw-r--r--spec/tooling/lib/tooling/find_codeowners_spec.rb107
11 files changed, 310 insertions, 336 deletions
diff --git a/spec/features/merge_request/user_views_user_status_on_merge_request_spec.rb b/spec/features/merge_request/user_views_user_status_on_merge_request_spec.rb
index f637186ec67..b214486b3c1 100644
--- a/spec/features/merge_request/user_views_user_status_on_merge_request_spec.rb
+++ b/spec/features/merge_request/user_views_user_status_on_merge_request_spec.rb
@@ -10,16 +10,6 @@ RSpec.describe 'Project > Merge request > View user status' do
subject { visit merge_request_path(merge_request) }
- describe 'the status of the merge request author' do
- before do
- stub_feature_flags(updated_mr_header: false)
- end
-
- it_behaves_like 'showing user status' do
- let(:user_with_status) { merge_request.author }
- end
- end
-
context 'for notes', :js do
describe 'the status of the author of a note on a merge request' do
let(:note) { create(:note, noteable: merge_request, project: project, author: create(:user)) }
diff --git a/spec/frontend/content_editor/remark_markdown_processing_spec.js b/spec/frontend/content_editor/remark_markdown_processing_spec.js
index 06f109f8603..58aacd72dda 100644
--- a/spec/frontend/content_editor/remark_markdown_processing_spec.js
+++ b/spec/frontend/content_editor/remark_markdown_processing_spec.js
@@ -207,6 +207,19 @@ describe('Client side Markdown processing', () => {
),
},
{
+ markdown: `
+<i class="foo">
+ *bar*
+</i>
+ `,
+ expectedDoc: doc(
+ paragraph(
+ sourceAttrs('0:28', '<i class="foo">\n *bar*\n</i>'),
+ italic(sourceAttrs('0:28', '<i class="foo">\n *bar*\n</i>'), '\n *bar*\n'),
+ ),
+ ),
+ },
+ {
markdown: '[GitLab](https://gitlab.com "Go to GitLab")',
expectedDoc: doc(
paragraph(
@@ -342,8 +355,14 @@ two
expectedDoc: doc(
bulletList(
sourceAttrs('0:27', '- List item 1\n- List item 2'),
- listItem(sourceAttrs('0:13', '- List item 1'), paragraph('List item 1')),
- listItem(sourceAttrs('14:27', '- List item 2'), paragraph('List item 2')),
+ listItem(
+ sourceAttrs('0:13', '- List item 1'),
+ paragraph(sourceAttrs('0:13', '- List item 1'), 'List item 1'),
+ ),
+ listItem(
+ sourceAttrs('14:27', '- List item 2'),
+ paragraph(sourceAttrs('14:27', '- List item 2'), 'List item 2'),
+ ),
),
),
},
@@ -355,8 +374,14 @@ two
expectedDoc: doc(
bulletList(
sourceAttrs('0:27', '* List item 1\n* List item 2'),
- listItem(sourceAttrs('0:13', '* List item 1'), paragraph('List item 1')),
- listItem(sourceAttrs('14:27', '* List item 2'), paragraph('List item 2')),
+ listItem(
+ sourceAttrs('0:13', '* List item 1'),
+ paragraph(sourceAttrs('0:13', '* List item 1'), 'List item 1'),
+ ),
+ listItem(
+ sourceAttrs('14:27', '* List item 2'),
+ paragraph(sourceAttrs('14:27', '* List item 2'), 'List item 2'),
+ ),
),
),
},
@@ -368,8 +393,14 @@ two
expectedDoc: doc(
bulletList(
sourceAttrs('0:27', '+ List item 1\n+ List item 2'),
- listItem(sourceAttrs('0:13', '+ List item 1'), paragraph('List item 1')),
- listItem(sourceAttrs('14:27', '+ List item 2'), paragraph('List item 2')),
+ listItem(
+ sourceAttrs('0:13', '+ List item 1'),
+ paragraph(sourceAttrs('0:13', '+ List item 1'), 'List item 1'),
+ ),
+ listItem(
+ sourceAttrs('14:27', '+ List item 2'),
+ paragraph(sourceAttrs('14:27', '+ List item 2'), 'List item 2'),
+ ),
),
),
},
@@ -381,8 +412,14 @@ two
expectedDoc: doc(
orderedList(
sourceAttrs('0:29', '1. List item 1\n1. List item 2'),
- listItem(sourceAttrs('0:14', '1. List item 1'), paragraph('List item 1')),
- listItem(sourceAttrs('15:29', '1. List item 2'), paragraph('List item 2')),
+ listItem(
+ sourceAttrs('0:14', '1. List item 1'),
+ paragraph(sourceAttrs('0:14', '1. List item 1'), 'List item 1'),
+ ),
+ listItem(
+ sourceAttrs('15:29', '1. List item 2'),
+ paragraph(sourceAttrs('15:29', '1. List item 2'), 'List item 2'),
+ ),
),
),
},
@@ -394,8 +431,14 @@ two
expectedDoc: doc(
orderedList(
sourceAttrs('0:29', '1. List item 1\n2. List item 2'),
- listItem(sourceAttrs('0:14', '1. List item 1'), paragraph('List item 1')),
- listItem(sourceAttrs('15:29', '2. List item 2'), paragraph('List item 2')),
+ listItem(
+ sourceAttrs('0:14', '1. List item 1'),
+ paragraph(sourceAttrs('0:14', '1. List item 1'), 'List item 1'),
+ ),
+ listItem(
+ sourceAttrs('15:29', '2. List item 2'),
+ paragraph(sourceAttrs('15:29', '2. List item 2'), 'List item 2'),
+ ),
),
),
},
@@ -407,8 +450,14 @@ two
expectedDoc: doc(
orderedList(
sourceAttrs('0:29', '1) List item 1\n2) List item 2'),
- listItem(sourceAttrs('0:14', '1) List item 1'), paragraph('List item 1')),
- listItem(sourceAttrs('15:29', '2) List item 2'), paragraph('List item 2')),
+ listItem(
+ sourceAttrs('0:14', '1) List item 1'),
+ paragraph(sourceAttrs('0:14', '1) List item 1'), 'List item 1'),
+ ),
+ listItem(
+ sourceAttrs('15:29', '2) List item 2'),
+ paragraph(sourceAttrs('15:29', '2) List item 2'), 'List item 2'),
+ ),
),
),
},
@@ -422,10 +471,13 @@ two
sourceAttrs('0:33', '- List item 1\n - Sub list item 1'),
listItem(
sourceAttrs('0:33', '- List item 1\n - Sub list item 1'),
- paragraph('List item 1\n'),
+ paragraph(sourceAttrs('0:33', '- List item 1\n - Sub list item 1'), 'List item 1\n'),
bulletList(
sourceAttrs('16:33', '- Sub list item 1'),
- listItem(sourceAttrs('16:33', '- Sub list item 1'), paragraph('Sub list item 1')),
+ listItem(
+ sourceAttrs('16:33', '- Sub list item 1'),
+ paragraph(sourceAttrs('16:33', '- Sub list item 1'), 'Sub list item 1'),
+ ),
),
),
),
@@ -477,8 +529,14 @@ two
sourceAttrs('0:31', '> - List item 1\n> - List item 2'),
bulletList(
sourceAttrs('2:31', '- List item 1\n> - List item 2'),
- listItem(sourceAttrs('2:15', '- List item 1'), paragraph('List item 1')),
- listItem(sourceAttrs('18:31', '- List item 2'), paragraph('List item 2')),
+ listItem(
+ sourceAttrs('2:15', '- List item 1'),
+ paragraph(sourceAttrs('2:15', '- List item 1'), 'List item 1'),
+ ),
+ listItem(
+ sourceAttrs('18:31', '- List item 2'),
+ paragraph(sourceAttrs('18:31', '- List item 2'), 'List item 2'),
+ ),
),
),
),
diff --git a/spec/frontend/vue_mr_widget/components/mr_widget_header_spec.js b/spec/frontend/vue_mr_widget/components/mr_widget_header_spec.js
deleted file mode 100644
index ed6dc598845..00000000000
--- a/spec/frontend/vue_mr_widget/components/mr_widget_header_spec.js
+++ /dev/null
@@ -1,176 +0,0 @@
-import { shallowMount, mount } from '@vue/test-utils';
-import Header from '~/vue_merge_request_widget/components/mr_widget_header.vue';
-
-describe('MRWidgetHeader', () => {
- let wrapper;
-
- const createComponent = (propsData = {}) => {
- wrapper = shallowMount(Header, {
- propsData,
- });
- };
-
- afterEach(() => {
- wrapper.destroy();
- gon.relative_url_root = '';
- });
-
- const commonMrProps = {
- divergedCommitsCount: 1,
- sourceBranch: 'mr-widget-refactor',
- sourceBranchLink: '<a href="/foo/bar/mr-widget-refactor">Link</a>',
- targetBranch: 'main',
- targetBranchPath: '/foo/bar/main',
- statusPath: 'abc',
- };
-
- describe('computed', () => {
- describe('shouldShowCommitsBehindText', () => {
- it('return true when there are divergedCommitsCount', () => {
- createComponent({
- mr: {
- divergedCommitsCount: 12,
- sourceBranch: 'mr-widget-refactor',
- sourceBranchLink: '<a href="/foo/bar/mr-widget-refactor">Link</a>',
- targetBranch: 'main',
- statusPath: 'abc',
- },
- });
-
- expect(wrapper.vm.shouldShowCommitsBehindText).toBe(true);
- });
-
- it('returns false where there are no divergedComits count', () => {
- createComponent({
- mr: {
- divergedCommitsCount: 0,
- sourceBranch: 'mr-widget-refactor',
- sourceBranchLink: '<a href="/foo/bar/mr-widget-refactor">Link</a>',
- targetBranch: 'main',
- statusPath: 'abc',
- },
- });
-
- expect(wrapper.vm.shouldShowCommitsBehindText).toBe(false);
- });
- });
-
- describe('commitsBehindText', () => {
- it('returns singular when there is one commit', () => {
- wrapper = mount(Header, {
- propsData: {
- mr: commonMrProps,
- },
- });
-
- expect(wrapper.find('.diverged-commits-count').element.innerHTML).toBe(
- 'The source branch is <a href="/foo/bar/main" class="gl-link">1 commit behind</a> the target branch',
- );
- });
-
- it('returns plural when there is more than one commit', () => {
- wrapper = mount(Header, {
- propsData: {
- mr: {
- ...commonMrProps,
- divergedCommitsCount: 2,
- },
- },
- });
- expect(wrapper.find('.diverged-commits-count').element.innerHTML).toBe(
- 'The source branch is <a href="/foo/bar/main" class="gl-link">2 commits behind</a> the target branch',
- );
- });
- });
- });
-
- describe('template', () => {
- describe('common elements', () => {
- beforeEach(() => {
- createComponent({
- mr: {
- divergedCommitsCount: 12,
- sourceBranch: 'mr-widget-refactor',
- sourceBranchLink: '<a href="/foo/bar/mr-widget-refactor">mr-widget-refactor</a>',
- sourceBranchRemoved: false,
- targetBranchPath: 'foo/bar/commits-path',
- targetBranchTreePath: 'foo/bar/tree/path',
- targetBranch: 'main',
- isOpen: true,
- emailPatchesPath: '/mr/email-patches',
- plainDiffPath: '/mr/plainDiffPath',
- statusPath: 'abc',
- },
- });
- });
-
- it('renders source branch link', () => {
- expect(wrapper.find('.js-source-branch').html()).toContain(
- '<a href="/foo/bar/mr-widget-refactor">mr-widget-refactor</a>',
- );
- });
-
- it('renders clipboard button', () => {
- expect(wrapper.find('[data-testid="mr-widget-copy-clipboard"]')).not.toBe(null);
- });
-
- it('renders target branch', () => {
- expect(wrapper.find('.js-target-branch').text().trim()).toBe('main');
- });
- });
-
- describe('without diverged commits', () => {
- beforeEach(() => {
- createComponent({
- mr: {
- divergedCommitsCount: 0,
- sourceBranch: 'mr-widget-refactor',
- sourceBranchLink: '<a href="/foo/bar/mr-widget-refactor">mr-widget-refactor</a>',
- sourceBranchRemoved: false,
- targetBranchPath: 'foo/bar/commits-path',
- targetBranchTreePath: 'foo/bar/tree/path',
- targetBranch: 'main',
- isOpen: true,
- emailPatchesPath: '/mr/email-patches',
- plainDiffPath: '/mr/plainDiffPath',
- statusPath: 'abc',
- },
- });
- });
-
- it('does not render diverged commits info', () => {
- expect(wrapper.find('.diverged-commits-count').exists()).toBe(false);
- });
- });
-
- describe('with diverged commits', () => {
- beforeEach(() => {
- wrapper = mount(Header, {
- propsData: {
- mr: {
- ...commonMrProps,
- divergedCommitsCount: 12,
- sourceBranchRemoved: false,
- targetBranchPath: 'foo/bar/commits-path',
- targetBranchTreePath: 'foo/bar/tree/path',
- isOpen: true,
- emailPatchesPath: '/mr/email-patches',
- plainDiffPath: '/mr/plainDiffPath',
- },
- },
- });
- });
-
- it('renders diverged commits info', () => {
- expect(wrapper.find('.diverged-commits-count').text().trim()).toBe(
- 'The source branch is 12 commits behind the target branch',
- );
-
- expect(wrapper.find('.diverged-commits-count a').text().trim()).toBe('12 commits behind');
- expect(wrapper.find('.diverged-commits-count a').attributes('href')).toBe(
- wrapper.vm.mr.targetBranchPath,
- );
- });
- });
- });
-});
diff --git a/spec/helpers/issues_helper_spec.rb b/spec/helpers/issues_helper_spec.rb
index b55b4aaf76a..dd5e162b444 100644
--- a/spec/helpers/issues_helper_spec.rb
+++ b/spec/helpers/issues_helper_spec.rb
@@ -441,85 +441,38 @@ RSpec.describe IssuesHelper do
end
describe '#status_box_class' do
- context 'when updated_mr_header feature flag is enabled' do
- before do
- stub_feature_flags(updated_mr_header: true)
- end
-
- context 'when object is expired' do
- it 'returns orange background' do
- milestone = build(:milestone, due_date: Date.today.prev_month)
- expect(helper.status_box_class(milestone)).to eq('gl-bg-orange-500')
- end
- end
-
- context 'when object is merged' do
- it 'returns blue background' do
- merge_request = build(:merge_request, :merged)
- expect(helper.status_box_class(merge_request)).to eq('badge-info')
- end
- end
-
- context 'when object is closed' do
- it 'returns red background' do
- merge_request = build(:merge_request, :closed)
- expect(helper.status_box_class(merge_request)).to eq('badge-danger')
- end
- end
-
- context 'when object is upcoming' do
- it 'returns gray background' do
- milestone = build(:milestone, start_date: Date.today.next_month)
- expect(helper.status_box_class(milestone)).to eq('gl-bg-gray-500')
- end
- end
-
- context 'when object is opened' do
- it 'returns green background' do
- merge_request = build(:merge_request, :opened)
- expect(helper.status_box_class(merge_request)).to eq('badge-success')
- end
+ context 'when object is expired' do
+ it 'returns orange background' do
+ milestone = build(:milestone, due_date: Date.today.prev_month)
+ expect(helper.status_box_class(milestone)).to eq('gl-bg-orange-500')
end
end
- context 'when updated_mr_header feature flag is disabled' do
- before do
- stub_feature_flags(updated_mr_header: false)
- end
-
- context 'when object is expired' do
- it 'returns orange background' do
- milestone = build(:milestone, due_date: Date.today.prev_month)
- expect(helper.status_box_class(milestone)).to eq('gl-bg-orange-500')
- end
- end
-
- context 'when object is merged' do
- it 'returns blue background' do
- merge_request = build(:merge_request, :merged)
- expect(helper.status_box_class(merge_request)).to eq('gl-bg-blue-500')
- end
+ context 'when object is merged' do
+ it 'returns blue background' do
+ merge_request = build(:merge_request, :merged)
+ expect(helper.status_box_class(merge_request)).to eq('badge-info')
end
+ end
- context 'when object is closed' do
- it 'returns red background' do
- merge_request = build(:merge_request, :closed)
- expect(helper.status_box_class(merge_request)).to eq('gl-bg-red-500')
- end
+ context 'when object is closed' do
+ it 'returns red background' do
+ merge_request = build(:merge_request, :closed)
+ expect(helper.status_box_class(merge_request)).to eq('badge-danger')
end
+ end
- context 'when object is upcoming' do
- it 'returns gray background' do
- milestone = build(:milestone, start_date: Date.today.next_month)
- expect(helper.status_box_class(milestone)).to eq('gl-bg-gray-500')
- end
+ context 'when object is upcoming' do
+ it 'returns gray background' do
+ milestone = build(:milestone, start_date: Date.today.next_month)
+ expect(helper.status_box_class(milestone)).to eq('gl-bg-gray-500')
end
+ end
- context 'when object is opened' do
- it 'returns green background' do
- merge_request = build(:merge_request, :opened)
- expect(helper.status_box_class(merge_request)).to eq('gl-bg-green-500')
- end
+ context 'when object is opened' do
+ it 'returns green background' do
+ merge_request = build(:merge_request, :opened)
+ expect(helper.status_box_class(merge_request)).to eq('badge-success')
end
end
end
diff --git a/spec/lib/gitlab/ci/config/external/mapper_spec.rb b/spec/lib/gitlab/ci/config/external/mapper_spec.rb
index 2d2adf09a42..7e1b31fea6a 100644
--- a/spec/lib/gitlab/ci/config/external/mapper_spec.rb
+++ b/spec/lib/gitlab/ci/config/external/mapper_spec.rb
@@ -6,7 +6,7 @@ RSpec.describe Gitlab::Ci::Config::External::Mapper do
include StubRequests
let_it_be(:project) { create(:project, :repository) }
- let_it_be(:user) { create(:user) }
+ let_it_be(:user) { project.owner }
let(:local_file) { '/lib/gitlab/ci/templates/non-existent-file.yml' }
let(:remote_url) { 'https://gitlab.com/gitlab-org/gitlab-foss/blob/1234/.gitlab-ci-1.yml' }
@@ -34,6 +34,19 @@ RSpec.describe Gitlab::Ci::Config::External::Mapper do
describe '#process' do
subject(:process) { mapper.process }
+ shared_examples 'logging config file fetch' do |key, count|
+ it 'propagates the pipeline logger' do
+ process
+
+ fetch_content_log_count = mapper
+ .logger
+ .observations_hash
+ .dig(key, 'count')
+
+ expect(fetch_content_log_count).to eq(count)
+ end
+ end
+
context "when single 'include' keyword is defined" do
context 'when the string is a local file' do
let(:values) do
@@ -45,6 +58,8 @@ RSpec.describe Gitlab::Ci::Config::External::Mapper do
expect(subject).to contain_exactly(
an_instance_of(Gitlab::Ci::Config::External::File::Local))
end
+
+ it_behaves_like 'logging config file fetch', 'config_file_fetch_local_content_duration_s', 1
end
context 'when the key is a local file hash' do
@@ -68,6 +83,8 @@ RSpec.describe Gitlab::Ci::Config::External::Mapper do
expect(subject).to contain_exactly(
an_instance_of(Gitlab::Ci::Config::External::File::Remote))
end
+
+ it_behaves_like 'logging config file fetch', 'config_file_fetch_remote_content_duration_s', 1
end
context 'when the key is a remote file hash' do
@@ -92,6 +109,8 @@ RSpec.describe Gitlab::Ci::Config::External::Mapper do
expect(subject).to contain_exactly(
an_instance_of(Gitlab::Ci::Config::External::File::Template))
end
+
+ it_behaves_like 'logging config file fetch', 'config_file_fetch_template_content_duration_s', 1
end
context 'when the key is a hash of file and remote' do
@@ -118,6 +137,8 @@ RSpec.describe Gitlab::Ci::Config::External::Mapper do
expect(subject).to contain_exactly(
an_instance_of(Gitlab::Ci::Config::External::File::Project))
end
+
+ it_behaves_like 'logging config file fetch', 'config_file_fetch_project_content_duration_s', 1
end
context "when the key is project's files" do
@@ -131,6 +152,8 @@ RSpec.describe Gitlab::Ci::Config::External::Mapper do
an_instance_of(Gitlab::Ci::Config::External::File::Project),
an_instance_of(Gitlab::Ci::Config::External::File::Project))
end
+
+ it_behaves_like 'logging config file fetch', 'config_file_fetch_project_content_duration_s', 2
end
end
diff --git a/spec/lib/gitlab/usage/service_ping_report_spec.rb b/spec/lib/gitlab/usage/service_ping_report_spec.rb
index e7096988035..e007554df4a 100644
--- a/spec/lib/gitlab/usage/service_ping_report_spec.rb
+++ b/spec/lib/gitlab/usage/service_ping_report_spec.rb
@@ -5,7 +5,7 @@ require 'spec_helper'
RSpec.describe Gitlab::Usage::ServicePingReport, :use_clean_rails_memory_store_caching do
include UsageDataHelpers
- let(:usage_data) { { uuid: "1111", counts: { issue: 0 } } }
+ let(:usage_data) { { uuid: "1111", counts: { issue: 0 } }.deep_stringify_keys }
before do
allow_next_instance_of(Gitlab::Usage::ServicePing::PayloadKeysProcessor) do |instance|
@@ -20,7 +20,7 @@ RSpec.describe Gitlab::Usage::ServicePingReport, :use_clean_rails_memory_store_c
context 'all_metrics_values' do
it 'generates the service ping when there are no missing values' do
expect(Gitlab::UsageData).to receive(:data).and_return(usage_data)
- expect(described_class.for(output: :all_metrics_values)).to eq({ uuid: "1111", counts: { issue: 0 } })
+ expect(described_class.for(output: :all_metrics_values)).to eq({ uuid: "1111", counts: { issue: 0 } }.deep_stringify_keys)
end
it 'generates the service ping with the missing values' do
@@ -33,7 +33,24 @@ RSpec.describe Gitlab::Usage::ServicePingReport, :use_clean_rails_memory_store_c
end
expect(Gitlab::UsageData).to receive(:data).and_return(usage_data)
- expect(described_class.for(output: :all_metrics_values)).to eq({ uuid: "1111", counts: { issue: 0, boards: 1 } })
+ expect(described_class.for(output: :all_metrics_values)).to eq({ uuid: "1111", counts: { issue: 0, boards: 1 } }.deep_stringify_keys)
+ end
+
+ context 'with usage data payload with symbol keys and instrumented payload with string keys' do
+ let(:usage_data) { { uuid: "1111", counts: { issue: 0 } } }
+
+ it 'correctly merges string and symbol keys' do
+ expect_next_instance_of(Gitlab::Usage::ServicePing::PayloadKeysProcessor, usage_data) do |instance|
+ expect(instance).to receive(:missing_instrumented_metrics_key_paths).and_return(['counts.boards'])
+ end
+
+ expect_next_instance_of(Gitlab::Usage::ServicePing::InstrumentedPayload, ['counts.boards'], :with_value) do |instance|
+ expect(instance).to receive(:build).and_return({ 'counts' => { 'boards' => 1 } })
+ end
+
+ expect(Gitlab::UsageData).to receive(:data).and_return(usage_data)
+ expect(described_class.for(output: :all_metrics_values)).to eq({ uuid: "1111", counts: { issue: 0, boards: 1 } }.deep_stringify_keys)
+ end
end
end
@@ -54,9 +71,9 @@ RSpec.describe Gitlab::Usage::ServicePingReport, :use_clean_rails_memory_store_c
end
context 'when using cached' do
- context 'for cached: true' do
- let(:new_usage_data) { { uuid: "1112" } }
+ let(:new_usage_data) { { 'uuid' => '1112' } }
+ context 'for cached: true' do
it 'caches the values' do
allow(Gitlab::UsageData).to receive(:data).and_return(usage_data, new_usage_data)
@@ -78,8 +95,6 @@ RSpec.describe Gitlab::Usage::ServicePingReport, :use_clean_rails_memory_store_c
end
context 'when no caching' do
- let(:new_usage_data) { { uuid: "1112" } }
-
it 'returns fresh data' do
allow(Gitlab::UsageData).to receive(:data).and_return(usage_data, new_usage_data)
diff --git a/spec/models/user_spec.rb b/spec/models/user_spec.rb
index 67d7e29627b..ac085d71761 100644
--- a/spec/models/user_spec.rb
+++ b/spec/models/user_spec.rb
@@ -3740,7 +3740,7 @@ RSpec.describe User do
end
context 'has owned groups' do
- let_it_be(:group) { create(:group) }
+ let(:group) { create(:group) }
before do
group.add_owner(user)
@@ -3749,11 +3749,23 @@ RSpec.describe User do
context 'not solo owner' do
let_it_be(:user2) { create(:user) }
- before do
- group.add_owner(user2)
+ context 'with another direct owner' do
+ before do
+ group.add_owner(user2)
+ end
+
+ it { is_expected.to be_empty }
end
- it { is_expected.to be_empty }
+ context 'with an inherited owner' do
+ let_it_be(:group) { create(:group, :nested) }
+
+ before do
+ group.parent.add_owner(user2)
+ end
+
+ it { is_expected.to be_empty }
+ end
end
context 'solo owner' do
diff --git a/spec/requests/api/personal_access_tokens_spec.rb b/spec/requests/api/personal_access_tokens_spec.rb
index 01f69f0aae2..403c646ee32 100644
--- a/spec/requests/api/personal_access_tokens_spec.rb
+++ b/spec/requests/api/personal_access_tokens_spec.rb
@@ -73,6 +73,61 @@ RSpec.describe API::PersonalAccessTokens do
end
end
+ describe 'GET /personal_access_tokens/:id' do
+ let_it_be(:user_token) { create(:personal_access_token, user: current_user) }
+ let_it_be(:user_token_path) { "/personal_access_tokens/#{user_token.id}" }
+ let_it_be(:invalid_path) { "/personal_access_tokens/#{non_existing_record_id}" }
+
+ context 'when current_user is an administrator', :enable_admin_mode do
+ let_it_be(:admin_user) { create(:admin) }
+ let_it_be(:admin_token) { create(:personal_access_token, user: admin_user) }
+ let_it_be(:admin_path) { "/personal_access_tokens/#{admin_token.id}" }
+
+ it 'returns admins own PAT by id' do
+ get api(admin_path, admin_user)
+
+ expect(response).to have_gitlab_http_status(:ok)
+ expect(json_response['id']).to eq(admin_token.id)
+ end
+
+ it 'returns a different users PAT by id' do
+ get api(user_token_path, admin_user)
+
+ expect(response).to have_gitlab_http_status(:ok)
+ expect(json_response['id']).to eq(user_token.id)
+ end
+
+ it 'fails to return PAT because no PAT exists with this id' do
+ get api(invalid_path, admin_user)
+
+ expect(response).to have_gitlab_http_status(:unauthorized)
+ end
+ end
+
+ context 'when current_user is not an administrator' do
+ let_it_be(:other_users_path) { "/personal_access_tokens/#{token1.id}" }
+
+ it 'returns users own PAT by id' do
+ get api(user_token_path, current_user)
+
+ expect(response).to have_gitlab_http_status(:ok)
+ expect(json_response['id']).to eq(user_token.id)
+ end
+
+ it 'fails to return other users PAT by id' do
+ get api(other_users_path, current_user)
+
+ expect(response).to have_gitlab_http_status(:unauthorized)
+ end
+
+ it 'fails to return PAT because no PAT exists with this id' do
+ get api(invalid_path, current_user)
+
+ expect(response).to have_gitlab_http_status(:unauthorized)
+ end
+ end
+ end
+
describe 'DELETE /personal_access_tokens/self' do
let(:path) { '/personal_access_tokens/self' }
let(:token) { create(:personal_access_token, user: current_user) }
diff --git a/spec/requests/api/users_spec.rb b/spec/requests/api/users_spec.rb
index 040ac4f74a7..2c5a734a0e1 100644
--- a/spec/requests/api/users_spec.rb
+++ b/spec/requests/api/users_spec.rb
@@ -2098,7 +2098,7 @@ RSpec.describe API::Users do
describe "DELETE /users/:id" do
let_it_be(:issue) { create(:issue, author: user) }
- it "deletes user", :sidekiq_might_not_need_inline do
+ it "deletes user", :sidekiq_inline do
namespace_id = user.namespace.id
perform_enqueued_jobs { delete api("/users/#{user.id}", admin) }
@@ -2119,11 +2119,27 @@ RSpec.describe API::Users do
end
context "hard delete enabled" do
- it "delete user and group", :sidekiq_might_not_need_inline do
+ it "delete user and group", :sidekiq_inline do
perform_enqueued_jobs { delete api("/users/#{user.id}?hard_delete=true", admin) }
expect(response).to have_gitlab_http_status(:no_content)
expect(Group.exists?(group.id)).to be_falsy
end
+
+ context "with subgroup owning" do
+ let(:parent_group) { create(:group) }
+ let(:subgroup) { create(:group, parent: parent_group) }
+
+ before do
+ parent_group.add_owner(create(:user))
+ subgroup.add_owner(user)
+ end
+
+ it "delete only user", :sidekiq_inline do
+ perform_enqueued_jobs { delete api("/users/#{user.id}?hard_delete=true", admin) }
+ expect(response).to have_gitlab_http_status(:no_content)
+ expect(Group.exists?(subgroup.id)).to be_truthy
+ end
+ end
end
end
diff --git a/spec/services/users/destroy_service_spec.rb b/spec/services/users/destroy_service_spec.rb
index 45dbe83b496..90c4f70d749 100644
--- a/spec/services/users/destroy_service_spec.rb
+++ b/spec/services/users/destroy_service_spec.rb
@@ -215,6 +215,27 @@ RSpec.describe Users::DestroyService do
end
end
+ context 'deletions with inherited group owners' do
+ let(:group) { create(:group, :nested) }
+ let(:user) { create(:user) }
+ let(:inherited_owner) { create(:user) }
+
+ before do
+ group.parent.add_owner(inherited_owner)
+ group.add_owner(user)
+
+ service.execute(user, delete_solo_owned_groups: true)
+ end
+
+ it 'does not delete the group' do
+ expect(Group.exists?(id: group)).to be_truthy
+ end
+
+ it 'deletes the user' do
+ expect(User.exists?(id: user)).to be_falsey
+ end
+ end
+
context 'migrating associated records' do
let!(:issue) { create(:issue, author: user) }
diff --git a/spec/tooling/lib/tooling/find_codeowners_spec.rb b/spec/tooling/lib/tooling/find_codeowners_spec.rb
index b29c5f35ec9..10c2a076847 100644
--- a/spec/tooling/lib/tooling/find_codeowners_spec.rb
+++ b/spec/tooling/lib/tooling/find_codeowners_spec.rb
@@ -31,13 +31,37 @@ RSpec.describe Tooling::FindCodeowners do
end
end.to output(<<~CODEOWNERS).to_stdout
[Section name]
- /dir0/dir1 @group
+ /dir0/dir1/ @group
/file @group
CODEOWNERS
end
end
describe '#load_definitions' do
+ before do
+ allow(subject).to receive(:load_config).and_return(
+ {
+ '[Authentication and Authorization]': {
+ '@gitlab-org/manage/authentication-and-authorization': {
+ allow: {
+ keywords: %w[password auth token],
+ patterns:
+ %w[
+ /{,ee/}app/**/*%{keyword}*{,/**/*}
+ /{,ee/}config/**/*%{keyword}*{,/**/*}
+ /{,ee/}lib/**/*%{keyword}*{,/**/*}
+ ]
+ },
+ deny: {
+ keywords: %w[*author.* *author_* *authored*],
+ patterns: ['%{keyword}']
+ }
+ }
+ }
+ }
+ )
+ end
+
it 'expands the allow and deny list with keywords and patterns' do
subject.load_definitions.each do |section, group_defintions|
group_defintions.each do |group, definitions|
@@ -54,56 +78,20 @@ RSpec.describe Tooling::FindCodeowners do
expect(auth).to eq(
allow: %w[
- /{,ee/}app/**/*password*{/**/*,}
- /{,ee/}config/**/*password*{/**/*,}
- /{,ee/}lib/**/*password*{/**/*,}
- /{,ee/}app/**/*auth*{/**/*,}
- /{,ee/}config/**/*auth*{/**/*,}
- /{,ee/}lib/**/*auth*{/**/*,}
- /{,ee/}app/**/*token*{/**/*,}
- /{,ee/}config/**/*token*{/**/*,}
- /{,ee/}lib/**/*token*{/**/*,}
+ /{,ee/}app/**/*password*{,/**/*}
+ /{,ee/}config/**/*password*{,/**/*}
+ /{,ee/}lib/**/*password*{,/**/*}
+ /{,ee/}app/**/*auth*{,/**/*}
+ /{,ee/}config/**/*auth*{,/**/*}
+ /{,ee/}lib/**/*auth*{,/**/*}
+ /{,ee/}app/**/*token*{,/**/*}
+ /{,ee/}config/**/*token*{,/**/*}
+ /{,ee/}lib/**/*token*{,/**/*}
],
deny: %w[
- **/*author.*{/**/*,}
- **/*author_*{/**/*,}
- **/*authored*{/**/*,}
- **/*authoring*{/**/*,}
- **/*.png*{/**/*,}
- **/*.svg*{/**/*,}
- **/*deploy_token*{/**/*,}
- **/*runner{,s}_token*{/**/*,}
- **/*job_token*{/**/*,}
- **/*autocomplete_tokens*{/**/*,}
- **/*dast_site_token*{/**/*,}
- **/*reset_prometheus_token*{/**/*,}
- **/*reset_registration_token*{/**/*,}
- **/*runners_registration_token*{/**/*,}
- **/*terraform_registry_token*{/**/*,}
- **/*tokenizer*{/**/*,}
- **/*filtered_search*{/**/*,}
- **/*/alert_management/*{/**/*,}
- **/*/analytics/*{/**/*,}
- **/*/bitbucket/*{/**/*,}
- **/*/clusters/*{/**/*,}
- **/*/clusters_list/*{/**/*,}
- **/*/dast/*{/**/*,}
- **/*/dast_profiles/*{/**/*,}
- **/*/dast_site_tokens/*{/**/*,}
- **/*/dast_site_validation/*{/**/*,}
- **/*/dependency_proxy/*{/**/*,}
- **/*/error_tracking/*{/**/*,}
- **/*/google_api/*{/**/*,}
- **/*/google_cloud/*{/**/*,}
- **/*/jira_connect/*{/**/*,}
- **/*/kubernetes/*{/**/*,}
- **/*/protected_environments/*{/**/*,}
- **/*/config/feature_flags/development/jira_connect_*{/**/*,}
- **/*/config/metrics/*{/**/*,}
- **/*/app/controllers/groups/dependency_proxy_auth_controller.rb*{/**/*,}
- **/*/app/finders/ci/auth_job_finder.rb*{/**/*,}
- **/*/ee/config/metrics/*{/**/*,}
- **/*/lib/gitlab/conan_token.rb*{/**/*,}
+ *author.*
+ *author_*
+ *authored*
]
)
end
@@ -159,12 +147,31 @@ RSpec.describe Tooling::FindCodeowners do
expected_flags =
::File::FNM_DOTMATCH | ::File::FNM_PATHNAME | ::File::FNM_EXTGLOB
- expect(File).to receive(:fnmatch?).with(pattern, path, expected_flags)
+ expect(File).to receive(:fnmatch?)
+ .with("/**/#{pattern}", path, expected_flags)
subject.path_matches?(pattern, path)
end
end
+ describe '#normalize_pattern' do
+ it 'returns /**/* if the input is *' do
+ expect(subject.normalize_pattern('*')).to eq('/**/*')
+ end
+
+ it 'prepends /** if the input does not start with /' do
+ expect(subject.normalize_pattern('app')).to eq('/**/app')
+ end
+
+ it 'returns the pattern if the input starts with /' do
+ expect(subject.normalize_pattern('/app')).to eq('/app')
+ end
+
+ it 'appends **/* if the input ends with /' do
+ expect(subject.normalize_pattern('/app/')).to eq('/app/**/*')
+ end
+ end
+
describe '#consolidate_paths' do
before do
allow(subject).to receive(:find_dir_maxdepth_1).and_return(<<~LINES)