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-12-02 15:08:02 +0300
committerGitLab Bot <gitlab-bot@gitlab.com>2022-12-02 15:08:02 +0300
commitf62c9f693fbe0d9bfac43cbe24b86af3c35f6a17 (patch)
tree7bbeb4fdf92d78324a92ff8a1385ea807c78665a /spec
parent81745c5a7143999d492943a2dceb977ced0a766f (diff)
Add latest changes from gitlab-org/gitlab@master
Diffstat (limited to 'spec')
-rw-r--r--spec/features/issue_rebalancing_spec.rb4
-rw-r--r--spec/frontend/boards/board_list_spec.js4
-rw-r--r--spec/frontend/editor/schema/ci/yaml_tests/negative_tests/artifacts.yml13
-rw-r--r--spec/frontend/editor/schema/ci/yaml_tests/negative_tests/cache.yml31
-rw-r--r--spec/frontend/editor/schema/ci/yaml_tests/positive_tests/artifacts.yml10
-rw-r--r--spec/frontend/editor/schema/ci/yaml_tests/positive_tests/cache.yml17
-rw-r--r--spec/frontend/issues/list/components/issues_list_app_spec.js23
-rw-r--r--spec/frontend/vue_shared/components/source_viewer/plugins/utils/go_sum_linker_spec.js4
-rw-r--r--spec/lib/gitlab/analytics/cycle_analytics/stage_events_spec.rb14
-rw-r--r--spec/lib/gitlab/ci/config/entry/artifacts_spec.rb20
-rw-r--r--spec/lib/gitlab/ci/config/entry/cache_spec.rb72
-rw-r--r--spec/lib/gitlab/ci/yaml_processor_spec.rb2
-rw-r--r--spec/lib/gitlab/workhorse_spec.rb127
-rw-r--r--spec/models/group_spec.rb2
-rw-r--r--spec/services/protected_branches/api_service_spec.rb69
-rw-r--r--spec/services/protected_branches/cache_service_spec.rb186
-rw-r--r--spec/services/protected_branches/create_service_spec.rb97
-rw-r--r--spec/services/protected_branches/destroy_service_spec.rb59
-rw-r--r--spec/services/protected_branches/update_service_spec.rb74
19 files changed, 525 insertions, 303 deletions
diff --git a/spec/features/issue_rebalancing_spec.rb b/spec/features/issue_rebalancing_spec.rb
index 686aa5eb1b6..4fa29361c54 100644
--- a/spec/features/issue_rebalancing_spec.rb
+++ b/spec/features/issue_rebalancing_spec.rb
@@ -41,13 +41,13 @@ RSpec.describe 'Issue rebalancing' do
it 'shows an alert in project issues list with manual sort', :js do
visit project_issues_path(project, sort: 'relative_position')
- expect(page).to have_selector('.flash-notice', text: alert_message_regex, count: 1)
+ expect(page).to have_selector('.gl-alert-danger', text: alert_message_regex, count: 1)
end
it 'shows an alert in group issues list with manual sort', :js do
visit issues_group_path(group, sort: 'relative_position')
- expect(page).to have_selector('.flash-notice', text: alert_message_regex, count: 1)
+ expect(page).to have_selector('.gl-alert-danger', text: alert_message_regex, count: 1)
end
it 'does not show an alert in project issues list with other sorts' do
diff --git a/spec/frontend/boards/board_list_spec.js b/spec/frontend/boards/board_list_spec.js
index 3a2beb714e9..eddc916ed14 100644
--- a/spec/frontend/boards/board_list_spec.js
+++ b/spec/frontend/boards/board_list_spec.js
@@ -269,6 +269,10 @@ describe('Board list component', () => {
it('Draggable is not used', () => {
expect(findDraggable().exists()).toBe(false);
});
+
+ it('Board card move to position is not visible', () => {
+ expect(findMoveToPositionComponent().exists()).toBe(false);
+ });
});
});
});
diff --git a/spec/frontend/editor/schema/ci/yaml_tests/negative_tests/artifacts.yml b/spec/frontend/editor/schema/ci/yaml_tests/negative_tests/artifacts.yml
index f5670376efc..29f4a0cd76d 100644
--- a/spec/frontend/editor/schema/ci/yaml_tests/negative_tests/artifacts.yml
+++ b/spec/frontend/editor/schema/ci/yaml_tests/negative_tests/artifacts.yml
@@ -16,3 +16,16 @@ cyclonedx not an array or string:
paths:
- foo
- bar
+
+# invalid artifacts:when
+artifacts-when-unknown:
+ artifacts:
+ when: unknown
+
+artifacts-when-array:
+ artifacts:
+ when: [always]
+
+artifacts-when-boolean:
+ artifacts:
+ when: true
diff --git a/spec/frontend/editor/schema/ci/yaml_tests/negative_tests/cache.yml b/spec/frontend/editor/schema/ci/yaml_tests/negative_tests/cache.yml
index 3979c9ae2ac..9baed2a7922 100644
--- a/spec/frontend/editor/schema/ci/yaml_tests/negative_tests/cache.yml
+++ b/spec/frontend/editor/schema/ci/yaml_tests/negative_tests/cache.yml
@@ -51,12 +51,39 @@ cache-untracked-string:
cache:
untracked: 'true'
-when_integer:
+# invalid cache:when
+cache-when-integer:
script: echo "This job uses a cache."
cache:
when: 0
-when_not_reserved_keyword:
+cache-when-array:
+ script: echo "This job uses a cache."
+ cache:
+ when: [always]
+
+cache-when-boolean:
+ script: echo "This job uses a cache."
+ cache:
+ when: true
+
+cache-when-never:
script: echo "This job uses a cache."
cache:
when: 'never'
+
+# invalid cache:policy
+cache-policy-array:
+ script: echo "This job uses a cache."
+ cache:
+ policy: [push]
+
+cache-policy-boolean:
+ script: echo "This job uses a cache."
+ cache:
+ policy: true
+
+cache-when-unknown:
+ script: echo "This job uses a cache."
+ cache:
+ policy: unknown
diff --git a/spec/frontend/editor/schema/ci/yaml_tests/positive_tests/artifacts.yml b/spec/frontend/editor/schema/ci/yaml_tests/positive_tests/artifacts.yml
index 20c1fc2c50f..a5c9153ee13 100644
--- a/spec/frontend/editor/schema/ci/yaml_tests/positive_tests/artifacts.yml
+++ b/spec/frontend/editor/schema/ci/yaml_tests/positive_tests/artifacts.yml
@@ -23,3 +23,13 @@ cylonedx mixed list of string paths and globs:
cyclonedx:
- ./foo
- "bar/*.baz"
+
+# valid artifacts:when
+artifacts-when-on-failure:
+ artifacts:
+ when: on_failure
+
+artifacts-no-when:
+ artifacts:
+ paths:
+ - binaries/
diff --git a/spec/frontend/editor/schema/ci/yaml_tests/positive_tests/cache.yml b/spec/frontend/editor/schema/ci/yaml_tests/positive_tests/cache.yml
index 75918cd2a1b..d50b74e1448 100644
--- a/spec/frontend/editor/schema/ci/yaml_tests/positive_tests/cache.yml
+++ b/spec/frontend/editor/schema/ci/yaml_tests/positive_tests/cache.yml
@@ -122,3 +122,20 @@ cache-untracked-false:
script: test
cache:
untracked: false
+
+# valid cache:policy
+cache-policy-push:
+ script: echo "This job uses a cache."
+ cache:
+ policy: push
+
+cache-policy-pull:
+ script: echo "This job uses a cache."
+ cache:
+ policy: pull
+
+cache-no-policy:
+ script: echo "This job uses a cache."
+ cache:
+ paths:
+ - binaries/
diff --git a/spec/frontend/issues/list/components/issues_list_app_spec.js b/spec/frontend/issues/list/components/issues_list_app_spec.js
index 4b09c92391c..f5624923b46 100644
--- a/spec/frontend/issues/list/components/issues_list_app_spec.js
+++ b/spec/frontend/issues/list/components/issues_list_app_spec.js
@@ -21,7 +21,7 @@ import {
setSortPreferenceMutationResponseWithErrors,
urlParams,
} from 'jest/issues/list/mock_data';
-import createFlash, { FLASH_TYPES } from '~/flash';
+import { createAlert, FLASH_TYPES } from '~/flash';
import { convertToGraphQLId, getIdFromGraphQLId } from '~/graphql_shared/utils';
import CsvImportExportButtons from '~/issuable/components/csv_import_export_buttons.vue';
import IssuableByEmail from '~/issuable/components/issuable_by_email.vue';
@@ -35,6 +35,7 @@ import {
CREATED_DESC,
RELATIVE_POSITION,
RELATIVE_POSITION_ASC,
+ UPDATED_DESC,
urlSortParams,
} from '~/issues/list/constants';
import eventHub from '~/issues/list/eventhub';
@@ -399,7 +400,7 @@ describe('CE IssuesListApp component', () => {
});
it('shows an alert to tell the user that manual reordering is disabled', () => {
- expect(createFlash).toHaveBeenCalledWith({
+ expect(createAlert).toHaveBeenCalledWith({
message: IssuesListApp.i18n.issueRepositioningMessage,
type: FLASH_TYPES.NOTICE,
});
@@ -439,7 +440,7 @@ describe('CE IssuesListApp component', () => {
});
it('shows an alert to tell the user they must be signed in to search', () => {
- expect(createFlash).toHaveBeenCalledWith({
+ expect(createAlert).toHaveBeenCalledWith({
message: IssuesListApp.i18n.anonymousSearchingMessage,
type: FLASH_TYPES.NOTICE,
});
@@ -796,7 +797,11 @@ describe('CE IssuesListApp component', () => {
it.each(Object.keys(urlSortParams))(
'updates to the new sort when payload is `%s`',
async (sortKey) => {
- wrapper = mountComponent();
+ // Ensure initial sort key is different so we can trigger an update when emitting a sort key
+ wrapper =
+ sortKey === CREATED_DESC
+ ? mountComponent({ provide: { initialSort: UPDATED_DESC } })
+ : mountComponent();
router.push = jest.fn();
findIssuableList().vm.$emit('sort', sortKey);
@@ -826,7 +831,7 @@ describe('CE IssuesListApp component', () => {
});
it('shows an alert to tell the user that manual reordering is disabled', () => {
- expect(createFlash).toHaveBeenCalledWith({
+ expect(createAlert).toHaveBeenCalledWith({
message: IssuesListApp.i18n.issueRepositioningMessage,
type: FLASH_TYPES.NOTICE,
});
@@ -838,9 +843,9 @@ describe('CE IssuesListApp component', () => {
const mutationMock = jest.fn().mockResolvedValue(setSortPreferenceMutationResponse);
wrapper = mountComponent({ sortPreferenceMutationResponse: mutationMock });
- findIssuableList().vm.$emit('sort', CREATED_DESC);
+ findIssuableList().vm.$emit('sort', UPDATED_DESC);
- expect(mutationMock).toHaveBeenCalledWith({ input: { issuesSort: CREATED_DESC } });
+ expect(mutationMock).toHaveBeenCalledWith({ input: { issuesSort: UPDATED_DESC } });
});
it('captures error when mutation response has errors', async () => {
@@ -849,7 +854,7 @@ describe('CE IssuesListApp component', () => {
.mockResolvedValue(setSortPreferenceMutationResponseWithErrors);
wrapper = mountComponent({ sortPreferenceMutationResponse: mutationMock });
- findIssuableList().vm.$emit('sort', CREATED_DESC);
+ findIssuableList().vm.$emit('sort', UPDATED_DESC);
await waitForPromises();
expect(Sentry.captureException).toHaveBeenCalledWith(new Error('oh no!'));
@@ -913,7 +918,7 @@ describe('CE IssuesListApp component', () => {
});
it('shows an alert to tell the user they must be signed in to search', () => {
- expect(createFlash).toHaveBeenCalledWith({
+ expect(createAlert).toHaveBeenCalledWith({
message: IssuesListApp.i18n.anonymousSearchingMessage,
type: FLASH_TYPES.NOTICE,
});
diff --git a/spec/frontend/vue_shared/components/source_viewer/plugins/utils/go_sum_linker_spec.js b/spec/frontend/vue_shared/components/source_viewer/plugins/utils/go_sum_linker_spec.js
index 293396c9de7..cc3ee41523f 100644
--- a/spec/frontend/vue_shared/components/source_viewer/plugins/utils/go_sum_linker_spec.js
+++ b/spec/frontend/vue_shared/components/source_viewer/plugins/utils/go_sum_linker_spec.js
@@ -3,9 +3,9 @@ import goSumLinker from '~/vue_shared/components/source_viewer/plugins/utils/go_
describe('Highlight.js plugin for linking go.sum dependencies', () => {
it('mutates the input value by wrapping dependencies and tags in anchors', () => {
const inputValue =
- '<span class="">cloud.google.com/go/bigquery v1.0.1/go.mod h1:i/xbL2UlR5RvWAURpBYZTtm/cXjCha9lbfbpx4poX+o=</span>';
+ '<span class="">cloud.google.com/Go/bigquery v1.0.1/go.mod h1:i/xbL2UlR5RvWAURpBYZTtm/cXjCha9lbfbpx4poX+o=</span>';
const outputValue =
- '<span class=""><a href="https://pkg.go.dev/cloud.google.com/go/bigquery" target="_blank" rel="nofollow noreferrer noopener">cloud.google.com/go/bigquery</a> v1.0.1/go.mod h1:<a href="https://sum.golang.org/lookup/cloud.google.com/go/bigquery@v1.0.1" target="_blank" rel="nofollow noreferrer noopener">i/xbL2UlR5RvWAURpBYZTtm/cXjCha9lbfbpx4poX+o=</a></span>';
+ '<span class=""><a href="https://pkg.go.dev/cloud.google.com/go/bigquery" target="_blank" rel="nofollow noreferrer noopener">cloud.google.com/Go/bigquery</a> v1.0.1/go.mod h1:<a href="https://sum.golang.org/lookup/cloud.google.com/go/bigquery@v1.0.1" target="_blank" rel="nofollow noreferrer noopener">i/xbL2UlR5RvWAURpBYZTtm/cXjCha9lbfbpx4poX+o=</a></span>';
const hljsResultMock = { value: inputValue };
const output = goSumLinker(hljsResultMock);
diff --git a/spec/lib/gitlab/analytics/cycle_analytics/stage_events_spec.rb b/spec/lib/gitlab/analytics/cycle_analytics/stage_events_spec.rb
new file mode 100644
index 00000000000..f5a8035b9b4
--- /dev/null
+++ b/spec/lib/gitlab/analytics/cycle_analytics/stage_events_spec.rb
@@ -0,0 +1,14 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+
+RSpec.describe Gitlab::Analytics::CycleAnalytics::StageEvents, feature_category: :value_stream_management do
+ describe '#selectable_events' do
+ subject(:selectable_events) { described_class.selectable_events }
+
+ it 'excludes internal events' do
+ expect(selectable_events).to include(Gitlab::Analytics::CycleAnalytics::StageEvents::MergeRequestCreated)
+ expect(selectable_events).to exclude(Gitlab::Analytics::CycleAnalytics::StageEvents::IssueStageEnd)
+ end
+ end
+end
diff --git a/spec/lib/gitlab/ci/config/entry/artifacts_spec.rb b/spec/lib/gitlab/ci/config/entry/artifacts_spec.rb
index 7476fc6c25f..6264e0c8e33 100644
--- a/spec/lib/gitlab/ci/config/entry/artifacts_spec.rb
+++ b/spec/lib/gitlab/ci/config/entry/artifacts_spec.rb
@@ -142,6 +142,26 @@ RSpec.describe Gitlab::Ci::Config::Entry::Artifacts do
end
end
+ context 'when the `when` keyword is not a string' do
+ context 'when it is an array' do
+ let(:config) { { paths: %w[results.txt], when: ['always'] } }
+
+ it 'returns error' do
+ expect(entry).not_to be_valid
+ expect(entry.errors).to include 'artifacts when should be a string'
+ end
+ end
+
+ context 'when it is a boolean' do
+ let(:config) { { paths: %w[results.txt], when: true } }
+
+ it 'returns error' do
+ expect(entry).not_to be_valid
+ expect(entry.errors).to include 'artifacts when should be a string'
+ end
+ end
+ end
+
describe 'excluded artifacts' do
context 'when configuration is valid' do
let(:config) { { untracked: true, exclude: ['some/directory/'] } }
diff --git a/spec/lib/gitlab/ci/config/entry/cache_spec.rb b/spec/lib/gitlab/ci/config/entry/cache_spec.rb
index 247f4b63910..414cbb169b9 100644
--- a/spec/lib/gitlab/ci/config/entry/cache_spec.rb
+++ b/spec/lib/gitlab/ci/config/entry/cache_spec.rb
@@ -163,22 +163,6 @@ RSpec.describe Gitlab::Ci::Config::Entry::Cache do
end
end
- context 'when policy is unknown' do
- let(:config) { { policy: 'unknown' } }
-
- it 'reports error' do
- is_expected.to include('cache policy should be pull-push, push, or pull')
- end
- end
-
- context 'when `when` is unknown' do
- let(:config) { { when: 'unknown' } }
-
- it 'reports error' do
- is_expected.to include('cache when should be on_success, on_failure or always')
- end
- end
-
context 'when descendants are invalid' do
context 'with invalid keys' do
let(:config) { { key: 1 } }
@@ -228,6 +212,62 @@ RSpec.describe Gitlab::Ci::Config::Entry::Cache do
is_expected.to include 'cache config contains unknown keys: invalid'
end
end
+
+ context 'when the `when` keyword is not a valid string' do
+ context 'when `when` is unknown' do
+ let(:config) { { when: 'unknown' } }
+
+ it 'returns error' do
+ is_expected.to include('cache when should be one of: on_success, on_failure, always')
+ end
+ end
+
+ context 'when it is an array' do
+ let(:config) { { when: ['always'] } }
+
+ it 'returns error' do
+ expect(entry).not_to be_valid
+ is_expected.to include('cache when should be a string')
+ end
+ end
+
+ context 'when it is a boolean' do
+ let(:config) { { when: true } }
+
+ it 'returns error' do
+ expect(entry).not_to be_valid
+ is_expected.to include('cache when should be a string')
+ end
+ end
+ end
+
+ context 'when the `policy` keyword is not a valid string' do
+ context 'when `policy` is unknown' do
+ let(:config) { { policy: 'unknown' } }
+
+ it 'returns error' do
+ is_expected.to include('cache policy should be one of: pull-push, push, pull')
+ end
+ end
+
+ context 'when it is an array' do
+ let(:config) { { policy: ['pull-push'] } }
+
+ it 'returns error' do
+ expect(entry).not_to be_valid
+ is_expected.to include('cache policy should be a string')
+ end
+ end
+
+ context 'when it is a boolean' do
+ let(:config) { { policy: true } }
+
+ it 'returns error' do
+ expect(entry).not_to be_valid
+ is_expected.to include('cache policy should be a string')
+ end
+ end
+ end
end
end
end
diff --git a/spec/lib/gitlab/ci/yaml_processor_spec.rb b/spec/lib/gitlab/ci/yaml_processor_spec.rb
index 0d1deb863b1..ae98d2e0cad 100644
--- a/spec/lib/gitlab/ci/yaml_processor_spec.rb
+++ b/spec/lib/gitlab/ci/yaml_processor_spec.rb
@@ -2946,7 +2946,7 @@ module Gitlab
context 'returns errors if job artifacts:when is not an a predefined value' do
let(:config) { YAML.dump({ stages: %w(build test), rspec: { script: "test", artifacts: { when: 1 } } }) }
- it_behaves_like 'returns errors', 'jobs:rspec:artifacts when should be on_success, on_failure or always'
+ it_behaves_like 'returns errors', 'jobs:rspec:artifacts when should be one of: on_success, on_failure, always'
end
context 'returns errors if job artifacts:expire_in is not an a string' do
diff --git a/spec/lib/gitlab/workhorse_spec.rb b/spec/lib/gitlab/workhorse_spec.rb
index 918b091eef2..3c7542ea5f9 100644
--- a/spec/lib/gitlab/workhorse_spec.rb
+++ b/spec/lib/gitlab/workhorse_spec.rb
@@ -209,6 +209,16 @@ RSpec.describe Gitlab::Workhorse do
describe '.git_http_ok' do
let(:user) { create(:user) }
+ let(:gitaly_params) do
+ {
+ GitalyServer: {
+ call_metadata: call_metadata,
+ address: Gitlab::GitalyClient.address('default'),
+ token: Gitlab::GitalyClient.token('default')
+ }
+ }
+ end
+
let(:repo_path) { 'ignored but not allowed to be empty in gitlab-workhorse' }
let(:action) { 'info_refs' }
let(:params) do
@@ -246,100 +256,79 @@ RSpec.describe Gitlab::Workhorse do
it { expect(subject).to include(params) }
end
- context 'when Gitaly is enabled' do
- let(:gitaly_params) do
- {
- GitalyServer: {
- call_metadata: call_metadata,
- address: Gitlab::GitalyClient.address('default'),
- token: Gitlab::GitalyClient.token('default')
- }
- }
- end
+ it 'includes a Repository param' do
+ repo_param = {
+ storage_name: 'default',
+ relative_path: project.disk_path + '.git',
+ gl_repository: "project-#{project.id}"
+ }
- before do
- allow(Gitlab.config.gitaly).to receive(:enabled).and_return(true)
- end
+ expect(subject[:Repository]).to include(repo_param)
+ end
- it 'includes a Repository param' do
- repo_param = {
- storage_name: 'default',
- relative_path: project.disk_path + '.git',
- gl_repository: "project-#{project.id}"
- }
+ context "when git_upload_pack action is passed" do
+ let(:action) { 'git_upload_pack' }
- expect(subject[:Repository]).to include(repo_param)
- end
+ it { expect(subject).to include(gitaly_params) }
- context "when git_upload_pack action is passed" do
- let(:action) { 'git_upload_pack' }
- let(:feature_flag) { :post_upload_pack }
+ context 'show_all_refs enabled' do
+ subject { described_class.git_http_ok(repository, Gitlab::GlRepository::PROJECT, user, action, show_all_refs: true) }
- it 'includes Gitaly params in the returned value' do
- allow(Gitlab::GitalyClient).to receive(:feature_enabled?).with(feature_flag).and_return(true)
+ it { is_expected.to include(ShowAllRefs: true) }
+ end
- expect(subject).to include(gitaly_params)
+ context 'when a feature flag is set for a single project' do
+ before do
+ stub_feature_flags(gitaly_mep_mep: project)
end
- context 'show_all_refs enabled' do
- subject { described_class.git_http_ok(repository, Gitlab::GlRepository::PROJECT, user, action, show_all_refs: true) }
+ it 'sets the flag to true for that project' do
+ response = described_class.git_http_ok(repository, Gitlab::GlRepository::PROJECT, user, action)
- it { is_expected.to include(ShowAllRefs: true) }
+ expect(response.dig(:GitalyServer, :call_metadata)).to include('gitaly-feature-enforce-requests-limits' => 'true',
+ 'gitaly-feature-mep-mep' => 'true')
end
- context 'when a feature flag is set for a single project' do
- before do
- stub_feature_flags(gitaly_mep_mep: project)
- end
-
- it 'sets the flag to true for that project' do
- response = described_class.git_http_ok(repository, Gitlab::GlRepository::PROJECT, user, action)
+ it 'sets the flag to false for other projects' do
+ other_project = create(:project, :public, :repository)
+ response = described_class.git_http_ok(other_project.repository, Gitlab::GlRepository::PROJECT, user, action)
- expect(response.dig(:GitalyServer, :call_metadata)).to include('gitaly-feature-enforce-requests-limits' => 'true',
- 'gitaly-feature-mep-mep' => 'true')
- end
-
- it 'sets the flag to false for other projects' do
- other_project = create(:project, :public, :repository)
- response = described_class.git_http_ok(other_project.repository, Gitlab::GlRepository::PROJECT, user, action)
-
- expect(response.dig(:GitalyServer, :call_metadata)).to include('gitaly-feature-enforce-requests-limits' => 'true',
- 'gitaly-feature-mep-mep' => 'false')
- end
+ expect(response.dig(:GitalyServer, :call_metadata)).to include('gitaly-feature-enforce-requests-limits' => 'true',
+ 'gitaly-feature-mep-mep' => 'false')
+ end
- it 'sets the flag to false when there is no project' do
- snippet = create(:personal_snippet, :repository)
- response = described_class.git_http_ok(snippet.repository, Gitlab::GlRepository::SNIPPET, user, action)
+ it 'sets the flag to false when there is no project' do
+ snippet = create(:personal_snippet, :repository)
+ response = described_class.git_http_ok(snippet.repository, Gitlab::GlRepository::SNIPPET, user, action)
- expect(response.dig(:GitalyServer, :call_metadata)).to include('gitaly-feature-enforce-requests-limits' => 'true',
- 'gitaly-feature-mep-mep' => 'false')
- end
+ expect(response.dig(:GitalyServer, :call_metadata)).to include('gitaly-feature-enforce-requests-limits' => 'true',
+ 'gitaly-feature-mep-mep' => 'false')
end
end
+ end
- context "when git_receive_pack action is passed" do
- let(:action) { 'git_receive_pack' }
+ context "when git_receive_pack action is passed" do
+ let(:action) { 'git_receive_pack' }
- it { expect(subject).to include(gitaly_params) }
- end
+ it { expect(subject).to include(gitaly_params) }
+ end
- context "when info_refs action is passed" do
- let(:action) { 'info_refs' }
+ context "when info_refs action is passed" do
+ let(:action) { 'info_refs' }
- it { expect(subject).to include(gitaly_params) }
+ it { expect(subject).to include(gitaly_params) }
- context 'show_all_refs enabled' do
- subject { described_class.git_http_ok(repository, Gitlab::GlRepository::PROJECT, user, action, show_all_refs: true) }
+ context 'show_all_refs enabled' do
+ subject { described_class.git_http_ok(repository, Gitlab::GlRepository::PROJECT, user, action, show_all_refs: true) }
- it { is_expected.to include(ShowAllRefs: true) }
- end
+ it { is_expected.to include(ShowAllRefs: true) }
end
+ end
- context 'when action passed is not supported by Gitaly' do
- let(:action) { 'download' }
+ context 'when action passed is not supported by Gitaly' do
+ let(:action) { 'download' }
- it { expect { subject }.to raise_exception('Unsupported action: download') }
- end
+ it { expect { subject }.to raise_exception('Unsupported action: download') }
end
context 'when receive_max_input_size has been updated' do
diff --git a/spec/models/group_spec.rb b/spec/models/group_spec.rb
index d7fc5486ad6..8cdb195f9c7 100644
--- a/spec/models/group_spec.rb
+++ b/spec/models/group_spec.rb
@@ -40,7 +40,7 @@ RSpec.describe Group do
it { is_expected.to have_many(:bulk_import_exports).class_name('BulkImports::Export') }
it { is_expected.to have_many(:contacts).class_name('CustomerRelations::Contact') }
it { is_expected.to have_many(:organizations).class_name('CustomerRelations::Organization') }
- it { is_expected.to have_many(:protected_branches) }
+ it { is_expected.to have_many(:protected_branches).inverse_of(:group).with_foreign_key(:namespace_id) }
it { is_expected.to have_one(:crm_settings) }
it { is_expected.to have_one(:group_feature) }
it { is_expected.to have_one(:harbor_integration) }
diff --git a/spec/services/protected_branches/api_service_spec.rb b/spec/services/protected_branches/api_service_spec.rb
index 94484f5a7b9..c98e253267b 100644
--- a/spec/services/protected_branches/api_service_spec.rb
+++ b/spec/services/protected_branches/api_service_spec.rb
@@ -3,32 +3,55 @@
require 'spec_helper'
RSpec.describe ProtectedBranches::ApiService do
- let_it_be(:project) { create(:project) }
- let_it_be(:user) { create(:user, maintainer_projects: [project]) }
-
- it 'creates a protected branch with prefilled defaults' do
- expect(::ProtectedBranches::CreateService).to receive(:new).with(
- project, user, hash_including(
- push_access_levels_attributes: [{ access_level: Gitlab::Access::MAINTAINER }],
- merge_access_levels_attributes: [{ access_level: Gitlab::Access::MAINTAINER }]
- )
- ).and_call_original
-
- expect(described_class.new(project, user, { name: 'new name' }).create).to be_valid
+ shared_examples 'execute with entity' do
+ it 'creates a protected branch with prefilled defaults' do
+ expect(::ProtectedBranches::CreateService).to receive(:new).with(
+ entity, user, hash_including(
+ push_access_levels_attributes: [{ access_level: Gitlab::Access::MAINTAINER }],
+ merge_access_levels_attributes: [{ access_level: Gitlab::Access::MAINTAINER }]
+ )
+ ).and_call_original
+
+ expect(described_class.new(entity, user, { name: 'new name' }).create).to be_valid
+ end
+
+ it 'updates a protected branch without prefilled defaults' do
+ expect(::ProtectedBranches::UpdateService).to receive(:new).with(
+ entity, user, hash_including(
+ push_access_levels_attributes: [],
+ merge_access_levels_attributes: []
+ )
+ ).and_call_original
+
+ expect do
+ expect(described_class.new(entity, user, { name: 'new name' }).update(protected_branch)).to be_valid
+ end.not_to change { protected_branch.reload.allow_force_push }
+ end
+ end
+
+ context 'with entity project' do
+ let_it_be_with_reload(:entity) { create(:project) }
+ let_it_be_with_reload(:protected_branch) { create(:protected_branch, project: entity, allow_force_push: true) }
+ let(:user) { entity.first_owner }
+
+ it_behaves_like 'execute with entity'
end
- it 'updates a protected branch without prefilled defaults' do
- protected_branch = create(:protected_branch, project: project, allow_force_push: true)
+ context 'with entity group' do
+ let_it_be_with_reload(:entity) { create(:group) }
+ let_it_be_with_reload(:user) { create(:user) }
+ let_it_be_with_reload(:protected_branch) do
+ create(:protected_branch, group: entity, project: nil, allow_force_push: true)
+ end
- expect(::ProtectedBranches::UpdateService).to receive(:new).with(
- project, user, hash_including(
- push_access_levels_attributes: [],
- merge_access_levels_attributes: []
- )
- ).and_call_original
+ before do
+ allow(Ability).to receive(:allowed?).with(user, :update_protected_branch, protected_branch).and_return(true)
+ allow(Ability)
+ .to receive(:allowed?)
+ .with(user, :create_protected_branch, instance_of(ProtectedBranch))
+ .and_return(true)
+ end
- expect do
- expect(described_class.new(project, user, { name: 'new name' }).update(protected_branch)).to be_valid
- end.not_to change { protected_branch.reload.allow_force_push }
+ it_behaves_like 'execute with entity'
end
end
diff --git a/spec/services/protected_branches/cache_service_spec.rb b/spec/services/protected_branches/cache_service_spec.rb
index d7a3258160b..ea434922661 100644
--- a/spec/services/protected_branches/cache_service_spec.rb
+++ b/spec/services/protected_branches/cache_service_spec.rb
@@ -4,122 +4,150 @@
require 'spec_helper'
RSpec.describe ProtectedBranches::CacheService, :clean_gitlab_redis_cache do
- subject(:service) { described_class.new(project, user) }
+ shared_examples 'execute with entity' do
+ subject(:service) { described_class.new(entity, user) }
- let_it_be(:project) { create(:project) }
- let_it_be(:user) { project.first_owner }
+ let(:immediate_expiration) { 0 }
- let(:immediate_expiration) { 0 }
-
- describe '#fetch' do
- it 'caches the value' do
- expect(service.fetch('main') { true }).to eq(true)
- expect(service.fetch('not-found') { false }).to eq(false)
-
- # Uses cached values
- expect(service.fetch('main') { false }).to eq(true)
- expect(service.fetch('not-found') { true }).to eq(false)
- end
-
- it 'sets expiry on the key' do
- stub_const("#{described_class.name}::CACHE_EXPIRE_IN", immediate_expiration)
-
- expect(service.fetch('main') { true }).to eq(true)
- expect(service.fetch('not-found') { false }).to eq(false)
-
- expect(service.fetch('main') { false }).to eq(false)
- expect(service.fetch('not-found') { true }).to eq(true)
- end
-
- it 'does not set an expiry on the key after the hash is already created' do
- expect(service.fetch('main') { true }).to eq(true)
+ describe '#fetch' do
+ it 'caches the value' do
+ expect(service.fetch('main') { true }).to eq(true)
+ expect(service.fetch('not-found') { false }).to eq(false)
- stub_const("#{described_class.name}::CACHE_EXPIRE_IN", immediate_expiration)
+ # Uses cached values
+ expect(service.fetch('main') { false }).to eq(true)
+ expect(service.fetch('not-found') { true }).to eq(false)
+ end
- expect(service.fetch('not-found') { false }).to eq(false)
+ it 'sets expiry on the key' do
+ stub_const("#{described_class.name}::CACHE_EXPIRE_IN", immediate_expiration)
- expect(service.fetch('main') { false }).to eq(true)
- expect(service.fetch('not-found') { true }).to eq(false)
- end
+ expect(service.fetch('main') { true }).to eq(true)
+ expect(service.fetch('not-found') { false }).to eq(false)
- context 'when CACHE_LIMIT is exceeded' do
- before do
- stub_const("#{described_class.name}::CACHE_LIMIT", 2)
+ expect(service.fetch('main') { false }).to eq(false)
+ expect(service.fetch('not-found') { true }).to eq(true)
end
- it 'recreates cache' do
+ it 'does not set an expiry on the key after the hash is already created' do
expect(service.fetch('main') { true }).to eq(true)
+
+ stub_const("#{described_class.name}::CACHE_EXPIRE_IN", immediate_expiration)
+
expect(service.fetch('not-found') { false }).to eq(false)
- # Uses cached values
expect(service.fetch('main') { false }).to eq(true)
expect(service.fetch('not-found') { true }).to eq(false)
+ end
- # Overflow
- expect(service.fetch('new-branch') { true }).to eq(true)
+ context 'when CACHE_LIMIT is exceeded' do
+ before do
+ stub_const("#{described_class.name}::CACHE_LIMIT", 2)
+ end
- # Refreshes values
- expect(service.fetch('main') { false }).to eq(false)
- expect(service.fetch('not-found') { true }).to eq(true)
- end
- end
+ it 'recreates cache' do
+ expect(service.fetch('main') { true }).to eq(true)
+ expect(service.fetch('not-found') { false }).to eq(false)
+
+ # Uses cached values
+ expect(service.fetch('main') { false }).to eq(true)
+ expect(service.fetch('not-found') { true }).to eq(false)
- context 'when dry_run is on' do
- it 'does not use cached value' do
- expect(service.fetch('main', dry_run: true) { true }).to eq(true)
- expect(service.fetch('main', dry_run: true) { false }).to eq(false)
+ # Overflow
+ expect(service.fetch('new-branch') { true }).to eq(true)
+
+ # Refreshes values
+ expect(service.fetch('main') { false }).to eq(false)
+ expect(service.fetch('not-found') { true }).to eq(true)
+ end
end
- context 'when cache mismatch' do
- it 'logs an error' do
+ context 'when dry_run is on' do
+ it 'does not use cached value' do
expect(service.fetch('main', dry_run: true) { true }).to eq(true)
+ expect(service.fetch('main', dry_run: true) { false }).to eq(false)
+ end
+
+ context 'when cache mismatch' do
+ it 'logs an error' do
+ expect(service.fetch('main', dry_run: true) { true }).to eq(true)
+
+ expect(Gitlab::AppLogger).to receive(:error).with(
+ {
+ 'class' => described_class.name,
+ 'message' => /Cache mismatch/,
+ 'record_class' => entity.class.name,
+ 'record_id' => entity.id,
+ 'record_path' => entity.full_path
+ }
+ )
+
+ expect(service.fetch('main', dry_run: true) { false }).to eq(false)
+ end
+ end
- expect(Gitlab::AppLogger).to receive(:error).with(
- {
- 'class' => described_class.name,
- 'message' => /Cache mismatch/,
- 'project_id' => project.id,
- 'project_path' => project.full_path
- }
- )
+ context 'when cache matches' do
+ it 'does not log an error' do
+ expect(service.fetch('main', dry_run: true) { true }).to eq(true)
- expect(service.fetch('main', dry_run: true) { false }).to eq(false)
+ expect(Gitlab::AppLogger).not_to receive(:error)
+
+ expect(service.fetch('main', dry_run: true) { true }).to eq(true)
+ end
end
end
+ end
- context 'when cache matches' do
- it 'does not log an error' do
- expect(service.fetch('main', dry_run: true) { true }).to eq(true)
+ describe '#refresh' do
+ it 'clears cached values' do
+ expect(service.fetch('main') { true }).to eq(true)
+ expect(service.fetch('not-found') { false }).to eq(false)
- expect(Gitlab::AppLogger).not_to receive(:error)
+ service.refresh
- expect(service.fetch('main', dry_run: true) { true }).to eq(true)
+ # Recreates cache
+ expect(service.fetch('main') { false }).to eq(false)
+ expect(service.fetch('not-found') { true }).to eq(true)
+ end
+ end
+
+ describe 'metrics' do
+ it 'records hit ratio metrics' do
+ expect_next_instance_of(Gitlab::Cache::Metrics) do |metrics|
+ expect(metrics).to receive(:increment_cache_miss).once
+ expect(metrics).to receive(:increment_cache_hit).exactly(4).times
end
+
+ 5.times { service.fetch('main') { true } }
end
end
end
- describe '#refresh' do
- it 'clears cached values' do
- expect(service.fetch('main') { true }).to eq(true)
- expect(service.fetch('not-found') { false }).to eq(false)
+ context 'with entity project' do
+ let_it_be_with_reload(:entity) { create(:project) }
+ let(:user) { entity.first_owner }
- service.refresh
+ it_behaves_like 'execute with entity'
+ end
+
+ context 'with entity group' do
+ let_it_be_with_reload(:entity) { create(:group) }
+ let_it_be_with_reload(:user) { create(:user) }
- # Recreates cache
- expect(service.fetch('main') { false }).to eq(false)
- expect(service.fetch('not-found') { true }).to eq(true)
+ before do
+ entity.add_owner(user)
end
- end
- describe 'metrics' do
- it 'records hit ratio metrics' do
- expect_next_instance_of(Gitlab::Cache::Metrics) do |metrics|
- expect(metrics).to receive(:increment_cache_miss).once
- expect(metrics).to receive(:increment_cache_hit).exactly(4).times
+ context 'when feature flag enabled' do
+ it_behaves_like 'execute with entity'
+ end
+
+ context 'when feature flag disabled' do
+ before do
+ stub_feature_flags(group_protected_branches: false)
end
- 5.times { service.fetch('main') { true } }
+ it_behaves_like 'execute with entity'
end
end
end
diff --git a/spec/services/protected_branches/create_service_spec.rb b/spec/services/protected_branches/create_service_spec.rb
index b42524e761c..9c8fe769ed8 100644
--- a/spec/services/protected_branches/create_service_spec.rb
+++ b/spec/services/protected_branches/create_service_spec.rb
@@ -3,70 +3,75 @@
require 'spec_helper'
RSpec.describe ProtectedBranches::CreateService do
- let_it_be_with_reload(:project) { create(:project) }
-
- let(:user) { project.first_owner }
- let(:params) do
- {
- name: name,
- merge_access_levels_attributes: [{ access_level: Gitlab::Access::MAINTAINER }],
- push_access_levels_attributes: [{ access_level: Gitlab::Access::MAINTAINER }]
- }
- end
-
- subject(:service) { described_class.new(project, user, params) }
-
- describe '#execute' do
- let(:name) { 'master' }
-
- it 'creates a new protected branch' do
- expect { service.execute }.to change(ProtectedBranch, :count).by(1)
- expect(project.protected_branches.last.push_access_levels.map(&:access_level)).to eq([Gitlab::Access::MAINTAINER])
- expect(project.protected_branches.last.merge_access_levels.map(&:access_level)).to eq([Gitlab::Access::MAINTAINER])
+ shared_examples 'execute with entity' do
+ let(:params) do
+ {
+ name: name,
+ merge_access_levels_attributes: [{ access_level: Gitlab::Access::MAINTAINER }],
+ push_access_levels_attributes: [{ access_level: Gitlab::Access::MAINTAINER }]
+ }
end
- it 'refreshes the cache' do
- expect_next_instance_of(ProtectedBranches::CacheService) do |cache_service|
- expect(cache_service).to receive(:refresh)
- end
+ subject(:service) { described_class.new(entity, user, params) }
- service.execute
- end
-
- context 'when protecting a branch with a name that contains HTML tags' do
- let(:name) { 'foo<b>bar<\b>' }
+ describe '#execute' do
+ let(:name) { 'master' }
it 'creates a new protected branch' do
expect { service.execute }.to change(ProtectedBranch, :count).by(1)
- expect(project.protected_branches.last.name).to eq(name)
+ expect(entity.protected_branches.last.push_access_levels.map(&:access_level)).to match_array([Gitlab::Access::MAINTAINER])
+ expect(entity.protected_branches.last.merge_access_levels.map(&:access_level)).to match_array([Gitlab::Access::MAINTAINER])
end
- end
- context 'when user does not have permission' do
- let(:user) { create(:user) }
+ it 'refreshes the cache' do
+ expect_next_instance_of(ProtectedBranches::CacheService) do |cache_service|
+ expect(cache_service).to receive(:refresh)
+ end
- before do
- project.add_developer(user)
+ service.execute
end
- it 'creates a new protected branch if we skip authorization step' do
- expect { service.execute(skip_authorization: true) }.to change(ProtectedBranch, :count).by(1)
+ context 'when protecting a branch with a name that contains HTML tags' do
+ let(:name) { 'foo<b>bar<\b>' }
+
+ it 'creates a new protected branch' do
+ expect { service.execute }.to change(ProtectedBranch, :count).by(1)
+ expect(entity.protected_branches.last.name).to eq(name)
+ end
end
- it 'raises Gitlab::Access:AccessDeniedError' do
- expect { service.execute }.to raise_error(Gitlab::Access::AccessDeniedError)
+ context 'when a policy restricts rule creation' do
+ it "prevents creation of the protected branch rule" do
+ disallow(:create_protected_branch, an_instance_of(ProtectedBranch))
+
+ expect do
+ service.execute
+ end.to raise_error(Gitlab::Access::AccessDeniedError)
+ end
+
+ it 'creates a new protected branch if we skip authorization step' do
+ expect { service.execute(skip_authorization: true) }.to change(ProtectedBranch, :count).by(1)
+ end
end
end
+ end
- context 'when a policy restricts rule creation' do
- it "prevents creation of the protected branch rule" do
- disallow(:create_protected_branch, an_instance_of(ProtectedBranch))
+ context 'with entity project' do
+ let_it_be_with_reload(:entity) { create(:project) }
+ let(:user) { entity.first_owner }
- expect do
- service.execute
- end.to raise_error(Gitlab::Access::AccessDeniedError)
- end
+ it_behaves_like 'execute with entity'
+ end
+
+ context 'with entity group' do
+ let_it_be_with_reload(:entity) { create(:group) }
+ let_it_be_with_reload(:user) { create(:user) }
+
+ before do
+ allow(Ability).to receive(:allowed?).with(user, :create_protected_branch, instance_of(ProtectedBranch)).and_return(true)
end
+
+ it_behaves_like 'execute with entity'
end
def disallow(ability, protected_branch)
diff --git a/spec/services/protected_branches/destroy_service_spec.rb b/spec/services/protected_branches/destroy_service_spec.rb
index 123deeea005..421d4aae5bb 100644
--- a/spec/services/protected_branches/destroy_service_spec.rb
+++ b/spec/services/protected_branches/destroy_service_spec.rb
@@ -3,37 +3,54 @@
require 'spec_helper'
RSpec.describe ProtectedBranches::DestroyService do
- let_it_be_with_reload(:project) { create(:project) }
+ shared_examples 'execute with entity' do
+ subject(:service) { described_class.new(entity, user) }
- let!(:protected_branch) { create(:protected_branch, project: project) }
- let(:user) { project.first_owner }
+ describe '#execute' do
+ it 'destroys a protected branch' do
+ service.execute(protected_branch)
- subject(:service) { described_class.new(project, user) }
-
- describe '#execute' do
- it 'destroys a protected branch' do
- service.execute(protected_branch)
+ expect(protected_branch).to be_destroyed
+ end
- expect(protected_branch).to be_destroyed
- end
+ it 'refreshes the cache' do
+ expect_next_instance_of(ProtectedBranches::CacheService) do |cache_service|
+ expect(cache_service).to receive(:refresh)
+ end
- it 'refreshes the cache' do
- expect_next_instance_of(ProtectedBranches::CacheService) do |cache_service|
- expect(cache_service).to receive(:refresh)
+ service.execute(protected_branch)
end
- service.execute(protected_branch)
+ context 'when a policy restricts rule deletion' do
+ it "prevents deletion of the protected branch rule" do
+ disallow(:destroy_protected_branch, protected_branch)
+
+ expect do
+ service.execute(protected_branch)
+ end.to raise_error(Gitlab::Access::AccessDeniedError)
+ end
+ end
end
+ end
- context 'when a policy restricts rule deletion' do
- it "prevents deletion of the protected branch rule" do
- disallow(:destroy_protected_branch, protected_branch)
+ context 'with entity project' do
+ let_it_be_with_reload(:entity) { create(:project) }
+ let!(:protected_branch) { create(:protected_branch, project: entity) }
+ let(:user) { entity.first_owner }
- expect do
- service.execute(protected_branch)
- end.to raise_error(Gitlab::Access::AccessDeniedError)
- end
+ it_behaves_like 'execute with entity'
+ end
+
+ context 'with entity group' do
+ let_it_be_with_reload(:entity) { create(:group) }
+ let_it_be_with_reload(:user) { create(:user) }
+ let!(:protected_branch) { create(:protected_branch, group: entity, project: nil) }
+
+ before do
+ allow(Ability).to receive(:allowed?).with(user, :destroy_protected_branch, protected_branch).and_return(true)
end
+
+ it_behaves_like 'execute with entity'
end
def disallow(ability, protected_branch)
diff --git a/spec/services/protected_branches/update_service_spec.rb b/spec/services/protected_branches/update_service_spec.rb
index 2ff6c3c489a..c70cc032a6a 100644
--- a/spec/services/protected_branches/update_service_spec.rb
+++ b/spec/services/protected_branches/update_service_spec.rb
@@ -3,54 +3,64 @@
require 'spec_helper'
RSpec.describe ProtectedBranches::UpdateService do
- let_it_be_with_reload(:project) { create(:project) }
+ shared_examples 'execute with entity' do
+ let(:params) { { name: new_name } }
- let!(:protected_branch) { create(:protected_branch, project: project) }
- let(:user) { project.first_owner }
- let(:params) { { name: new_name } }
+ subject(:service) { described_class.new(entity, user, params) }
- subject(:service) { described_class.new(project, user, params) }
+ describe '#execute' do
+ let(:new_name) { 'new protected branch name' }
+ let(:result) { service.execute(protected_branch) }
- describe '#execute' do
- let(:new_name) { 'new protected branch name' }
- let(:result) { service.execute(protected_branch) }
+ it 'updates a protected branch' do
+ expect(result.reload.name).to eq(params[:name])
+ end
- it 'updates a protected branch' do
- expect(result.reload.name).to eq(params[:name])
- end
+ it 'refreshes the cache' do
+ expect_next_instance_of(ProtectedBranches::CacheService) do |cache_service|
+ expect(cache_service).to receive(:refresh)
+ end
- it 'refreshes the cache' do
- expect_next_instance_of(ProtectedBranches::CacheService) do |cache_service|
- expect(cache_service).to receive(:refresh)
+ result
end
- result
- end
-
- context 'when updating name of a protected branch to one that contains HTML tags' do
- let(:new_name) { 'foo<b>bar<\b>' }
- let(:result) { service.execute(protected_branch) }
+ context 'when updating name of a protected branch to one that contains HTML tags' do
+ let(:new_name) { 'foo<b>bar<\b>' }
+ let(:result) { service.execute(protected_branch) }
- it 'updates a protected branch' do
- expect(result.reload.name).to eq(new_name)
+ it 'updates a protected branch' do
+ expect(result.reload.name).to eq(new_name)
+ end
end
- end
- context 'without admin_project permissions' do
- let(:user) { create(:user) }
+ context 'when a policy restricts rule update' do
+ it "prevents update of the protected branch rule" do
+ disallow(:update_protected_branch, protected_branch)
- it "raises error" do
- expect { service.execute(protected_branch) }.to raise_error(Gitlab::Access::AccessDeniedError)
+ expect { service.execute(protected_branch) }.to raise_error(Gitlab::Access::AccessDeniedError)
+ end
end
end
+ end
- context 'when a policy restricts rule update' do
- it "prevents update of the protected branch rule" do
- disallow(:update_protected_branch, protected_branch)
+ context 'with entity project' do
+ let_it_be_with_reload(:entity) { create(:project) }
+ let!(:protected_branch) { create(:protected_branch, project: entity) }
+ let(:user) { entity.first_owner }
- expect { service.execute(protected_branch) }.to raise_error(Gitlab::Access::AccessDeniedError)
- end
+ it_behaves_like 'execute with entity'
+ end
+
+ context 'with entity group' do
+ let_it_be_with_reload(:entity) { create(:group) }
+ let_it_be_with_reload(:user) { create(:user) }
+ let!(:protected_branch) { create(:protected_branch, group: entity, project: nil) }
+
+ before do
+ allow(Ability).to receive(:allowed?).with(user, :update_protected_branch, protected_branch).and_return(true)
end
+
+ it_behaves_like 'execute with entity'
end
def disallow(ability, protected_branch)