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-10-17 15:10:08 +0300
committerGitLab Bot <gitlab-bot@gitlab.com>2022-10-17 15:10:08 +0300
commit8060e5c60901ab0f6b890414dccbdf5d1b95c3ad (patch)
treefc217fe53f68a45ea225c0d1b966642852d96321 /spec
parentb9b58dba70466949d761132d2d96f0f24c0b469c (diff)
Add latest changes from gitlab-org/gitlab@master
Diffstat (limited to 'spec')
-rw-r--r--spec/controllers/groups/runners_controller_spec.rb26
-rw-r--r--spec/controllers/groups_controller_spec.rb22
-rw-r--r--spec/factories/users/namespace_user_callouts.rb10
-rw-r--r--spec/features/groups/group_runners_spec.rb44
-rw-r--r--spec/finders/ci/runners_finder_spec.rb36
-rw-r--r--spec/frontend/access_tokens/components/new_access_token_app_spec.js19
-rw-r--r--spec/frontend/runner/group_runners/group_runners_app_spec.js61
-rw-r--r--spec/frontend/token_access/token_access_spec.js6
-rw-r--r--spec/frontend/vue_merge_request_widget/components/approvals/approvals_spec.js8
-rw-r--r--spec/frontend/vue_merge_request_widget/deployment/deployment_actions_spec.js9
-rw-r--r--spec/graphql/mutations/namespace/package_settings/update_spec.rb37
-rw-r--r--spec/graphql/types/namespace/package_settings_type_spec.rb20
-rw-r--r--spec/helpers/events_helper_spec.rb39
-rw-r--r--spec/initializers/memory_watchdog_spec.rb76
-rw-r--r--spec/lib/gitlab/bitbucket_import/importer_spec.rb51
-rw-r--r--spec/lib/gitlab/github_import/importer/attachments/issues_importer_spec.rb61
-rw-r--r--spec/lib/gitlab/github_import/importer/attachments/merge_requests_importer_spec.rb61
-rw-r--r--spec/lib/gitlab/github_import/importer/attachments/notes_importer_spec.rb8
-rw-r--r--spec/lib/gitlab/github_import/importer/attachments/releases_importer_spec.rb8
-rw-r--r--spec/lib/gitlab/github_import/importer/note_attachments_importer_spec.rb50
-rw-r--r--spec/lib/gitlab/github_import/representation/note_text_spec.rb18
-rw-r--r--spec/lib/gitlab/memory/watchdog/configuration_spec.rb121
-rw-r--r--spec/lib/gitlab/memory/watchdog/monitor/heap_fragmentation_spec.rb60
-rw-r--r--spec/lib/gitlab/memory/watchdog/monitor/unique_memory_growth_spec.rb62
-rw-r--r--spec/lib/gitlab/memory/watchdog/monitor_state_spec.rb72
-rw-r--r--spec/lib/gitlab/memory/watchdog_spec.rb396
-rw-r--r--spec/models/user_spec.rb91
-rw-r--r--spec/models/users/namespace_callout_spec.rb39
-rw-r--r--spec/policies/group_policy_spec.rb32
-rw-r--r--spec/requests/api/graphql/mutations/namespace/package_settings/update_spec.rb45
-rw-r--r--spec/requests/api/helm_packages_spec.rb11
-rw-r--r--spec/requests/groups/settings/access_tokens_controller_spec.rb15
-rw-r--r--spec/requests/projects/settings/access_tokens_controller_spec.rb15
-rw-r--r--spec/requests/users/namespace_callouts_spec.rb57
-rw-r--r--spec/serializers/group_access_token_entity_spec.rb4
-rw-r--r--spec/serializers/project_access_token_entity_spec.rb4
-rw-r--r--spec/services/namespaces/package_settings/update_service_spec.rb37
-rw-r--r--spec/services/users/dismiss_namespace_callout_service_spec.rb24
-rw-r--r--spec/support/shared_examples/features/access_tokens_shared_examples.rb19
-rw-r--r--spec/support/shared_examples/lib/gitlab/memory/watchdog/monitor_result_shared_examples.rb10
-rw-r--r--spec/support/shared_examples/requests/access_tokens_controller_shared_examples.rb40
-rw-r--r--spec/support/shared_examples/requests/api/helm_packages_shared_examples.rb9
-rw-r--r--spec/workers/every_sidekiq_worker_spec.rb2
-rw-r--r--spec/workers/gitlab/github_import/attachments/import_issue_worker_spec.rb34
-rw-r--r--spec/workers/gitlab/github_import/attachments/import_merge_request_worker_spec.rb34
-rw-r--r--spec/workers/gitlab/github_import/stage/import_attachments_worker_spec.rb56
-rw-r--r--spec/workers/pages/invalidate_domain_cache_worker_spec.rb30
47 files changed, 1205 insertions, 784 deletions
diff --git a/spec/controllers/groups/runners_controller_spec.rb b/spec/controllers/groups/runners_controller_spec.rb
index 1da868c6c4b..6dbf0803892 100644
--- a/spec/controllers/groups/runners_controller_spec.rb
+++ b/spec/controllers/groups/runners_controller_spec.rb
@@ -72,29 +72,11 @@ RSpec.describe Groups::RunnersController do
expect(response).to render_template(:show)
end
- context 'when runners_finder_all_available is enabled' do
- before do
- stub_feature_flags(runners_finder_all_available: true)
- end
-
- it 'renders show with 200 status code instance runner' do
- get :show, params: { group_id: group, id: instance_runner }
-
- expect(response).to have_gitlab_http_status(:ok)
- expect(response).to render_template(:show)
- end
- end
-
- context 'when runners_finder_all_available is disabled' do
- before do
- stub_feature_flags(runners_finder_all_available: false)
- end
-
- it 'renders show with a 404 instance runner' do
- get :show, params: { group_id: group, id: instance_runner }
+ it 'renders show with 200 status code instance runner' do
+ get :show, params: { group_id: group, id: instance_runner }
- expect(response).to have_gitlab_http_status(:not_found)
- end
+ expect(response).to have_gitlab_http_status(:ok)
+ expect(response).to render_template(:show)
end
it 'renders show with 200 status code project runner' do
diff --git a/spec/controllers/groups_controller_spec.rb b/spec/controllers/groups_controller_spec.rb
index c4e4eeec953..5bbe236077c 100644
--- a/spec/controllers/groups_controller_spec.rb
+++ b/spec/controllers/groups_controller_spec.rb
@@ -229,7 +229,7 @@ RSpec.describe GroupsController, factory_default: :keep do
sign_in(user)
expect do
- post :create, params: { group: { name: 'new_group', path: "new_group" } }
+ post :create, params: { group: { name: 'new_group', path: 'new_group' } }
end.to change { Group.count }.by(1)
expect(response).to have_gitlab_http_status(:found)
@@ -240,13 +240,31 @@ RSpec.describe GroupsController, factory_default: :keep do
sign_in(create(:admin))
expect do
- post :create, params: { group: { name: 'new_group', path: "new_group" } }
+ post :create, params: { group: { name: 'new_group', path: 'new_group' } }
end.to change { Group.count }.by(1)
expect(response).to have_gitlab_http_status(:found)
end
end
+ context 'when creating chat team' do
+ before do
+ stub_mattermost_setting(enabled: true)
+ end
+
+ it 'triggers Mattermost::CreateTeamService' do
+ sign_in(user)
+
+ expect_next_instance_of(::Mattermost::CreateTeamService) do |service|
+ expect(service).to receive(:execute).and_return({ name: 'test-chat-team', id: 1 })
+ end
+
+ post :create, params: { group: { name: 'new_group', path: 'new_group', create_chat_team: 1 } }
+
+ expect(response).to have_gitlab_http_status(:found)
+ end
+ end
+
context 'when creating subgroups' do
[true, false].each do |can_create_group_status|
context "and can_create_group is #{can_create_group_status}" do
diff --git a/spec/factories/users/namespace_user_callouts.rb b/spec/factories/users/namespace_user_callouts.rb
deleted file mode 100644
index fded63d0cce..00000000000
--- a/spec/factories/users/namespace_user_callouts.rb
+++ /dev/null
@@ -1,10 +0,0 @@
-# frozen_string_literal: true
-
-FactoryBot.define do
- factory :namespace_callout, class: 'Users::NamespaceCallout' do
- feature_name { :invite_members_banner }
-
- user
- namespace
- end
-end
diff --git a/spec/features/groups/group_runners_spec.rb b/spec/features/groups/group_runners_spec.rb
index 97c9103a054..e9807c487d5 100644
--- a/spec/features/groups/group_runners_spec.rb
+++ b/spec/features/groups/group_runners_spec.rb
@@ -119,45 +119,27 @@ RSpec.describe "Group Runners" do
create(:ci_runner, :instance, description: 'runner-baz', contacted_at: Time.zone.now)
end
- context "when runners_finder_all_available is enabled" do
- before do
- stub_feature_flags(runners_finder_all_available: true)
-
- visit group_runners_path(group)
- end
-
- context "when selecting 'Show only inherited'" do
- before do
- find("[data-testid='runner-membership-toggle'] button").click
-
- wait_for_requests
- end
-
- it_behaves_like 'shows runner in list' do
- let(:runner) { instance_runner }
- end
-
- it 'shows runner details page' do
- click_link("##{instance_runner.id} (#{instance_runner.short_sha})")
-
- expect(current_url).to include(group_runner_path(group, instance_runner))
- expect(page).to have_content "#{s_('Runners|Description')} runner-baz"
- end
- end
+ before do
+ visit group_runners_path(group)
end
- context "when runners_finder_all_available is disabled" do
+ context "when selecting 'Show only inherited'" do
before do
- stub_feature_flags(runners_finder_all_available: false)
+ find("[data-testid='runner-membership-toggle'] button").click
- visit group_runners_path(group)
+ wait_for_requests
end
- it "does not display 'Show only inherited' toggle" do
- expect(page).not_to have_content(s_('Runners|Show only inherited'))
+ it_behaves_like 'shows runner in list' do
+ let(:runner) { instance_runner }
end
- it_behaves_like 'shows no runners registered'
+ it 'shows runner details page' do
+ click_link("##{instance_runner.id} (#{instance_runner.short_sha})")
+
+ expect(current_url).to include(group_runner_path(group, instance_runner))
+ expect(page).to have_content "#{s_('Runners|Description')} runner-baz"
+ end
end
end
diff --git a/spec/finders/ci/runners_finder_spec.rb b/spec/finders/ci/runners_finder_spec.rb
index 62663cd5c27..18eecd0f073 100644
--- a/spec/finders/ci/runners_finder_spec.rb
+++ b/spec/finders/ci/runners_finder_spec.rb
@@ -322,27 +322,11 @@ RSpec.describe Ci::RunnersFinder do
context 'with :all_available membership' do
let(:membership) { :all_available }
- context 'with runners_finder_all_available FF disabled' do
- before do
- stub_feature_flags(runners_finder_all_available: false)
- end
-
- it 'returns no runners' do
- expect(subject).to be_empty
- end
- end
-
- context 'with runners_finder_all_available FF enabled' do
- before do
- stub_feature_flags(runners_finder_all_available: [target_group])
- end
-
- it 'returns runners available to group' do
- expect(subject).to match_array([runner_project_7, runner_project_6, runner_project_5,
- runner_project_4, runner_project_3, runner_project_2,
- runner_project_1, runner_sub_group_4, runner_sub_group_3,
- runner_sub_group_2, runner_sub_group_1, runner_group, runner_instance])
- end
+ it 'returns runners available to group' do
+ expect(subject).to match_array([runner_project_7, runner_project_6, runner_project_5,
+ runner_project_4, runner_project_3, runner_project_2,
+ runner_project_1, runner_sub_group_4, runner_sub_group_3,
+ runner_sub_group_2, runner_sub_group_1, runner_group, runner_instance])
end
end
@@ -448,14 +432,8 @@ RSpec.describe Ci::RunnersFinder do
context 'with :all_available membership' do
let(:membership) { :all_available }
- context 'with runners_finder_all_available FF enabled' do
- before do
- stub_feature_flags(runners_finder_all_available: [target_group])
- end
-
- it 'returns no runners' do
- expect(subject).to be_empty
- end
+ it 'returns no runners' do
+ expect(subject).to be_empty
end
end
end
diff --git a/spec/frontend/access_tokens/components/new_access_token_app_spec.js b/spec/frontend/access_tokens/components/new_access_token_app_spec.js
index d12d200d214..b4af11169ad 100644
--- a/spec/frontend/access_tokens/components/new_access_token_app_spec.js
+++ b/spec/frontend/access_tokens/components/new_access_token_app_spec.js
@@ -22,6 +22,8 @@ describe('~/access_tokens/components/new_access_token_app', () => {
});
};
+ const findButtonEl = () => document.querySelector('[type=submit]');
+
const triggerSuccess = async (newToken = 'new token') => {
wrapper
.findComponent(DomElementListener)
@@ -41,7 +43,7 @@ describe('~/access_tokens/components/new_access_token_app', () => {
<input type="text" id="expires_at" value="2022-01-01"/>
<input type="text" value='1'/>
<input type="checkbox" checked/>
- <input type="submit" value="Create"/>
+ <button type="submit" value="Create" class="disabled" disabled="disabled"/>
</form>`,
);
@@ -120,10 +122,10 @@ describe('~/access_tokens/components/new_access_token_app', () => {
});
it('should not reset the submit button value', async () => {
- expect(document.querySelector('input[type=submit]').value).toBe('Create');
+ expect(findButtonEl().value).toBe('Create');
await triggerSuccess();
- expect(document.querySelector('input[type=submit]').value).toBe('Create');
+ expect(findButtonEl().value).toBe('Create');
});
});
});
@@ -162,6 +164,17 @@ describe('~/access_tokens/components/new_access_token_app', () => {
expect(wrapper.findComponent(GlAlert).exists()).toBe(false);
});
+
+ it('should enable the submit button', async () => {
+ const button = findButtonEl();
+ expect(button).toBeDisabled();
+ expect(button.className).toBe('disabled');
+
+ await triggerError();
+
+ expect(button).not.toBeDisabled();
+ expect(button.className).toBe('');
+ });
});
describe('before error or success', () => {
diff --git a/spec/frontend/runner/group_runners/group_runners_app_spec.js b/spec/frontend/runner/group_runners/group_runners_app_spec.js
index ce91f5a6ba6..5f355e27d9e 100644
--- a/spec/frontend/runner/group_runners/group_runners_app_spec.js
+++ b/spec/frontend/runner/group_runners/group_runners_app_spec.js
@@ -159,55 +159,30 @@ describe('GroupRunnersApp', () => {
});
describe('show all available runners toggle', () => {
- describe('when runners_finder_all_available is enabled', () => {
- it('shows the membership toggle', () => {
- createComponent({
- provide: {
- glFeatures: { runnersFinderAllAvailable: true },
- },
- });
-
- expect(findRunnerMembershipToggle().exists()).toBe(true);
- });
-
- it('sets the membership toggle', () => {
- setWindowLocation(`?membership[]=${MEMBERSHIP_ALL_AVAILABLE}`);
- createComponent({
- provide: {
- glFeatures: { runnersFinderAllAvailable: true },
- },
- });
-
- expect(findRunnerMembershipToggle().props('value')).toBe(MEMBERSHIP_ALL_AVAILABLE);
- });
-
- it('requests filter', async () => {
- createComponent({
- provide: {
- glFeatures: { runnersFinderAllAvailable: true },
- },
- });
+ it('shows the membership toggle', () => {
+ createComponent();
+ expect(findRunnerMembershipToggle().exists()).toBe(true);
+ });
- findRunnerMembershipToggle().vm.$emit('input', MEMBERSHIP_ALL_AVAILABLE);
+ it('sets the membership toggle', () => {
+ setWindowLocation(`?membership[]=${MEMBERSHIP_ALL_AVAILABLE}`);
- await waitForPromises();
+ createComponent();
- expect(mockGroupRunnersHandler).toHaveBeenLastCalledWith(
- expect.objectContaining({
- membership: MEMBERSHIP_ALL_AVAILABLE,
- }),
- );
- });
+ expect(findRunnerMembershipToggle().props('value')).toBe(MEMBERSHIP_ALL_AVAILABLE);
});
- describe('when runners_finder_all_available is disabled', () => {
- beforeEach(() => {
- createComponent();
- });
+ it('requests filter', async () => {
+ createComponent();
+ findRunnerMembershipToggle().vm.$emit('input', MEMBERSHIP_ALL_AVAILABLE);
- it('does not show the membership toggle', () => {
- expect(findRunnerMembershipToggle().exists()).toBe(false);
- });
+ await waitForPromises();
+
+ expect(mockGroupRunnersHandler).toHaveBeenLastCalledWith(
+ expect.objectContaining({
+ membership: MEMBERSHIP_ALL_AVAILABLE,
+ }),
+ );
});
});
diff --git a/spec/frontend/token_access/token_access_spec.js b/spec/frontend/token_access/token_access_spec.js
index e6aef41cc8b..c55ac32b6a6 100644
--- a/spec/frontend/token_access/token_access_spec.js
+++ b/spec/frontend/token_access/token_access_spec.js
@@ -4,7 +4,7 @@ import VueApollo from 'vue-apollo';
import createMockApollo from 'helpers/mock_apollo_helper';
import { mountExtended, shallowMountExtended } from 'helpers/vue_test_utils_helper';
import waitForPromises from 'helpers/wait_for_promises';
-import createFlash from '~/flash';
+import { createAlert } from '~/flash';
import TokenAccess from '~/token_access/components/token_access.vue';
import addProjectCIJobTokenScopeMutation from '~/token_access/graphql/mutations/add_project_ci_job_token_scope.mutation.graphql';
import removeProjectCIJobTokenScopeMutation from '~/token_access/graphql/mutations/remove_project_ci_job_token_scope.mutation.graphql';
@@ -144,7 +144,7 @@ describe('TokenAccess component', () => {
await waitForPromises();
- expect(createFlash).toHaveBeenCalled();
+ expect(createAlert).toHaveBeenCalled();
});
});
@@ -187,7 +187,7 @@ describe('TokenAccess component', () => {
await waitForPromises();
- expect(createFlash).toHaveBeenCalled();
+ expect(createAlert).toHaveBeenCalled();
});
});
});
diff --git a/spec/frontend/vue_merge_request_widget/components/approvals/approvals_spec.js b/spec/frontend/vue_merge_request_widget/components/approvals/approvals_spec.js
index 612fc2112d0..1f3b6dce620 100644
--- a/spec/frontend/vue_merge_request_widget/components/approvals/approvals_spec.js
+++ b/spec/frontend/vue_merge_request_widget/components/approvals/approvals_spec.js
@@ -1,7 +1,7 @@
import { nextTick } from 'vue';
import { GlButton, GlSprintf } from '@gitlab/ui';
import { shallowMount } from '@vue/test-utils';
-import createFlash from '~/flash';
+import { createAlert } from '~/flash';
import Approvals from '~/vue_merge_request_widget/components/approvals/approvals.vue';
import ApprovalsSummary from '~/vue_merge_request_widget/components/approvals/approvals_summary.vue';
import ApprovalsSummaryOptional from '~/vue_merge_request_widget/components/approvals/approvals_summary_optional.vue';
@@ -129,7 +129,7 @@ describe('MRWidget approvals', () => {
});
it('flashes error', () => {
- expect(createFlash).toHaveBeenCalledWith({ message: FETCH_ERROR });
+ expect(createAlert).toHaveBeenCalledWith({ message: FETCH_ERROR });
});
});
@@ -268,7 +268,7 @@ describe('MRWidget approvals', () => {
});
it('flashes error message', () => {
- expect(createFlash).toHaveBeenCalledWith({ message: APPROVE_ERROR });
+ expect(createAlert).toHaveBeenCalledWith({ message: APPROVE_ERROR });
});
});
});
@@ -319,7 +319,7 @@ describe('MRWidget approvals', () => {
});
it('flashes error message', () => {
- expect(createFlash).toHaveBeenCalledWith({ message: UNAPPROVE_ERROR });
+ expect(createAlert).toHaveBeenCalledWith({ message: UNAPPROVE_ERROR });
});
});
});
diff --git a/spec/frontend/vue_merge_request_widget/deployment/deployment_actions_spec.js b/spec/frontend/vue_merge_request_widget/deployment/deployment_actions_spec.js
index d3e17065d89..58dadb2c679 100644
--- a/spec/frontend/vue_merge_request_widget/deployment/deployment_actions_spec.js
+++ b/spec/frontend/vue_merge_request_widget/deployment/deployment_actions_spec.js
@@ -1,6 +1,6 @@
import { mount } from '@vue/test-utils';
import waitForPromises from 'helpers/wait_for_promises';
-import createFlash from '~/flash';
+import { createAlert } from '~/flash';
import { confirmAction } from '~/lib/utils/confirm_via_gl_modal/confirm_via_gl_modal';
import { visitUrl } from '~/lib/utils/url_utility';
import {
@@ -168,7 +168,7 @@ describe('DeploymentAction component', () => {
});
it('should not throw an error', () => {
- expect(createFlash).not.toHaveBeenCalled();
+ expect(createAlert).not.toHaveBeenCalled();
});
describe('response includes redirect_url', () => {
@@ -225,9 +225,8 @@ describe('DeploymentAction component', () => {
finderFn().trigger('click');
});
- it('should call createFlash with error message', () => {
- expect(createFlash).toHaveBeenCalled();
- expect(createFlash).toHaveBeenCalledWith({
+ it('should call createAlert with error message', () => {
+ expect(createAlert).toHaveBeenCalledWith({
message: actionButtonMocks[configConst].errorMessage,
});
});
diff --git a/spec/graphql/mutations/namespace/package_settings/update_spec.rb b/spec/graphql/mutations/namespace/package_settings/update_spec.rb
index 631e02ff3dc..09ac1c99b10 100644
--- a/spec/graphql/mutations/namespace/package_settings/update_spec.rb
+++ b/spec/graphql/mutations/namespace/package_settings/update_spec.rb
@@ -26,8 +26,29 @@ RSpec.describe Mutations::Namespace::PackageSettings::Update do
RSpec.shared_examples 'updating the namespace package setting' do
it_behaves_like 'updating the namespace package setting attributes',
- from: { maven_duplicates_allowed: true, maven_duplicate_exception_regex: 'SNAPSHOT', generic_duplicates_allowed: true, generic_duplicate_exception_regex: 'foo' },
- to: { maven_duplicates_allowed: false, maven_duplicate_exception_regex: 'RELEASE', generic_duplicates_allowed: false, generic_duplicate_exception_regex: 'bar' }
+ from: {
+ maven_duplicates_allowed: true,
+ maven_duplicate_exception_regex: 'SNAPSHOT',
+ generic_duplicates_allowed: true,
+ generic_duplicate_exception_regex: 'foo',
+ maven_package_requests_forwarding: nil,
+ lock_maven_package_requests_forwarding: false,
+ npm_package_requests_forwarding: nil,
+ lock_npm_package_requests_forwarding: false,
+ pypi_package_requests_forwarding: nil,
+ lock_pypi_package_requests_forwarding: false
+ }, to: {
+ maven_duplicates_allowed: false,
+ maven_duplicate_exception_regex: 'RELEASE',
+ generic_duplicates_allowed: false,
+ generic_duplicate_exception_regex: 'bar',
+ maven_package_requests_forwarding: true,
+ lock_maven_package_requests_forwarding: true,
+ npm_package_requests_forwarding: true,
+ lock_npm_package_requests_forwarding: true,
+ pypi_package_requests_forwarding: true,
+ lock_pypi_package_requests_forwarding: true
+ }
it_behaves_like 'returning a success'
@@ -59,11 +80,19 @@ RSpec.describe Mutations::Namespace::PackageSettings::Update do
context 'with existing namespace package setting' do
let_it_be(:package_settings) { create(:namespace_package_setting, namespace: namespace) }
let_it_be(:params) do
- { namespace_path: namespace.full_path,
+ {
+ namespace_path: namespace.full_path,
maven_duplicates_allowed: false,
maven_duplicate_exception_regex: 'RELEASE',
generic_duplicates_allowed: false,
- generic_duplicate_exception_regex: 'bar' }
+ generic_duplicate_exception_regex: 'bar',
+ maven_package_requests_forwarding: true,
+ lock_maven_package_requests_forwarding: true,
+ npm_package_requests_forwarding: true,
+ lock_npm_package_requests_forwarding: true,
+ pypi_package_requests_forwarding: true,
+ lock_pypi_package_requests_forwarding: true
+ }
end
where(:user_role, :shared_examples_name) do
diff --git a/spec/graphql/types/namespace/package_settings_type_spec.rb b/spec/graphql/types/namespace/package_settings_type_spec.rb
index f63a0a7010f..5039f2d6153 100644
--- a/spec/graphql/types/namespace/package_settings_type_spec.rb
+++ b/spec/graphql/types/namespace/package_settings_type_spec.rb
@@ -14,4 +14,24 @@ RSpec.describe GitlabSchema.types['PackageSettings'] do
it { is_expected.to have_graphql_type(Types::UntrustedRegexp) }
end
+
+ it 'includes package setting fields' do
+ expected_fields = %w[
+ maven_duplicates_allowed
+ maven_duplicate_exception_regex
+ generic_duplicates_allowed
+ generic_duplicate_exception_regex
+ maven_package_requests_forwarding
+ lock_maven_package_requests_forwarding
+ npm_package_requests_forwarding
+ lock_npm_package_requests_forwarding
+ pypi_package_requests_forwarding
+ lock_pypi_package_requests_forwarding
+ maven_package_requests_forwarding_locked
+ npm_package_requests_forwarding_locked
+ pypi_package_requests_forwarding_locked
+ ]
+
+ expect(described_class).to include_graphql_fields(*expected_fields)
+ end
end
diff --git a/spec/helpers/events_helper_spec.rb b/spec/helpers/events_helper_spec.rb
index cc6804f0355..7005b3dc53e 100644
--- a/spec/helpers/events_helper_spec.rb
+++ b/spec/helpers/events_helper_spec.rb
@@ -24,6 +24,45 @@ RSpec.describe EventsHelper do
end
end
+ describe '#localized_action_name' do
+ it 'handles all valid design events' do
+ created, updated, destroyed = %i[created updated destroyed].map do |trait|
+ event = build(:design_event, trait)
+ helper.localized_action_name(event)
+ end
+
+ expect(created).to eq(_('added'))
+ expect(updated).to eq(_('updated'))
+ expect(destroyed).to eq(_('removed'))
+ end
+
+ context 'handles correct base actions' do
+ using RSpec::Parameterized::TableSyntax
+
+ where(:trait, :localized_action_name) do
+ :created | s_('Event|created')
+ :updated | s_('Event|opened')
+ :closed | s_('Event|closed')
+ :reopened | s_('Event|opened')
+ :commented | s_('Event|commented on')
+ :merged | s_('Event|accepted')
+ :joined | s_('Event|joined')
+ :left | s_('Event|left')
+ :destroyed | s_('Event|destroyed')
+ :expired | s_('Event|removed due to membership expiration from')
+ :approved | s_('Event|approved')
+ end
+
+ with_them do
+ it 'with correct name and method' do
+ event = build(:event, trait)
+
+ expect(helper.localized_action_name(event)).to eq(localized_action_name)
+ end
+ end
+ end
+ end
+
describe '#event_commit_title' do
let(:message) { 'foo & bar ' + 'A' * 70 + '\n' + 'B' * 80 }
diff --git a/spec/initializers/memory_watchdog_spec.rb b/spec/initializers/memory_watchdog_spec.rb
index 56f995b5cd3..36f96131c3d 100644
--- a/spec/initializers/memory_watchdog_spec.rb
+++ b/spec/initializers/memory_watchdog_spec.rb
@@ -4,7 +4,7 @@ require 'fast_spec_helper'
RSpec.describe 'memory watchdog' do
subject(:run_initializer) do
- load Rails.root.join('config/initializers/memory_watchdog.rb')
+ load rails_root_join('config/initializers/memory_watchdog.rb')
end
context 'when GITLAB_MEMORY_WATCHDOG_ENABLED is truthy' do
@@ -17,6 +17,7 @@ RSpec.describe 'memory watchdog' do
context 'when runtime is an application' do
let(:watchdog) { instance_double(Gitlab::Memory::Watchdog) }
let(:background_task) { instance_double(Gitlab::BackgroundTask) }
+ let(:logger) { Gitlab::AppLogger }
before do
allow(Gitlab::Runtime).to receive(:application?).and_return(true)
@@ -28,16 +29,65 @@ RSpec.describe 'memory watchdog' do
run_initializer
end
- shared_examples 'starts watchdog with handler' do |handler_class|
- it "uses the #{handler_class} and starts the watchdog" do
- expect(Gitlab::Memory::Watchdog).to receive(:new).with(
- handler: an_instance_of(handler_class),
- logger: Gitlab::AppLogger).and_return(watchdog)
- expect(Gitlab::BackgroundTask).to receive(:new).with(watchdog).and_return(background_task)
- expect(background_task).to receive(:start)
- expect(Gitlab::Cluster::LifecycleEvents).to receive(:on_worker_start).and_yield
+ shared_examples 'starts configured watchdog' do |handler_class|
+ let(:configuration) { Gitlab::Memory::Watchdog::Configuration.new }
+ let(:watchdog_monitors_params) do
+ {
+ Gitlab::Memory::Watchdog::Monitor::HeapFragmentation => {
+ max_heap_fragmentation: max_heap_fragmentation,
+ max_strikes: max_strikes
+ },
+ Gitlab::Memory::Watchdog::Monitor::UniqueMemoryGrowth => {
+ max_mem_growth: max_mem_growth,
+ max_strikes: max_strikes
+ }
+ }
+ end
+
+ shared_examples 'configures and starts watchdog' do
+ it "correctly configures and starts watchdog", :aggregate_failures do
+ expect(watchdog).to receive(:configure).and_yield(configuration)
+
+ watchdog_monitors_params.each do |monitor_class, params|
+ expect(configuration.monitors).to receive(:use).with(monitor_class, **params)
+ end
+
+ expect(Gitlab::Memory::Watchdog).to receive(:new).and_return(watchdog)
+ expect(Gitlab::BackgroundTask).to receive(:new).with(watchdog).and_return(background_task)
+ expect(background_task).to receive(:start)
+ expect(Gitlab::Cluster::LifecycleEvents).to receive(:on_worker_start).and_yield
+
+ run_initializer
+
+ expect(configuration.handler).to be_an_instance_of(handler_class)
+ expect(configuration.logger).to eq(logger)
+ expect(configuration.sleep_time_seconds).to eq(sleep_time_seconds)
+ end
+ end
+
+ context 'when settings are not passed through the environment' do
+ let(:max_strikes) { 5 }
+ let(:max_heap_fragmentation) { 0.5 }
+ let(:max_mem_growth) { 3.0 }
+ let(:sleep_time_seconds) { 60 }
+
+ include_examples 'configures and starts watchdog'
+ end
+
+ context 'when settings are passed through the environment' do
+ let(:max_strikes) { 6 }
+ let(:max_heap_fragmentation) { 0.4 }
+ let(:max_mem_growth) { 2.0 }
+ let(:sleep_time_seconds) { 50 }
+
+ before do
+ stub_env('GITLAB_MEMWD_MAX_STRIKES', 6)
+ stub_env('GITLAB_MEMWD_SLEEP_TIME_SEC', 50)
+ stub_env('GITLAB_MEMWD_MAX_MEM_GROWTH', 2.0)
+ stub_env('GITLAB_MEMWD_MAX_HEAP_FRAG', 0.4)
+ end
- run_initializer
+ include_examples 'configures and starts watchdog'
end
end
@@ -59,7 +109,7 @@ RSpec.describe 'memory watchdog' do
allow(Gitlab::Runtime).to receive(:puma?).and_return(true)
end
- it_behaves_like 'starts watchdog with handler', Gitlab::Memory::Watchdog::PumaHandler
+ it_behaves_like 'starts configured watchdog', Gitlab::Memory::Watchdog::PumaHandler
end
# rubocop: enable RSpec/VerifiedDoubles
@@ -68,11 +118,11 @@ RSpec.describe 'memory watchdog' do
allow(Gitlab::Runtime).to receive(:sidekiq?).and_return(true)
end
- it_behaves_like 'starts watchdog with handler', Gitlab::Memory::Watchdog::TermProcessHandler
+ it_behaves_like 'starts configured watchdog', Gitlab::Memory::Watchdog::TermProcessHandler
end
context 'when other runtime' do
- it_behaves_like 'starts watchdog with handler', Gitlab::Memory::Watchdog::NullHandler
+ it_behaves_like 'starts configured watchdog', Gitlab::Memory::Watchdog::NullHandler
end
end
diff --git a/spec/lib/gitlab/bitbucket_import/importer_spec.rb b/spec/lib/gitlab/bitbucket_import/importer_spec.rb
index a9534101567..186d4e1fb42 100644
--- a/spec/lib/gitlab/bitbucket_import/importer_spec.rb
+++ b/spec/lib/gitlab/bitbucket_import/importer_spec.rb
@@ -58,16 +58,15 @@ RSpec.describe Gitlab::BitbucketImport::Importer do
issues
end
- let(:project_identifier) { 'namespace/repo' }
- let(:data) { { 'token' => 'token' } }
+ let_it_be(:project_identifier) { 'namespace/repo' }
- let(:project) do
+ let_it_be_with_reload(:project) do
create(
:project,
:repository,
import_source: project_identifier,
import_url: "https://bitbucket.org/#{project_identifier}.git",
- import_data_attributes: { credentials: data }
+ import_data_attributes: { credentials: { 'token' => 'token' } }
)
end
@@ -80,6 +79,14 @@ RSpec.describe Gitlab::BitbucketImport::Importer do
}
end
+ let(:last_issue_data) do
+ {
+ page: 1,
+ pagelen: 1,
+ values: [sample_issues_statuses.last]
+ }
+ end
+
let(:counter) { double('counter', increment: true) }
subject { described_class.new(project) }
@@ -245,6 +252,13 @@ RSpec.describe Gitlab::BitbucketImport::Importer do
stub_request(
:get,
+ "https://api.bitbucket.org/2.0/repositories/#{project_identifier}/issues?pagelen=1&sort=-created_on&state=ALL"
+ ).to_return(status: 200,
+ headers: { "Content-Type" => "application/json" },
+ body: last_issue_data.to_json)
+
+ stub_request(
+ :get,
"https://api.bitbucket.org/2.0/repositories/#{project_identifier}/issues?pagelen=50&sort=created_on"
).to_return(status: 200,
headers: { "Content-Type" => "application/json" },
@@ -344,6 +358,12 @@ RSpec.describe Gitlab::BitbucketImport::Importer do
end
describe 'issue import' do
+ it 'allocates internal ids' do
+ expect(Issue).to receive(:track_project_iid!).with(project, 6)
+
+ importer.execute
+ end
+
it 'maps reporters to anonymous if bitbucket reporter is nil' do
allow(importer).to receive(:import_wiki)
importer.execute
@@ -363,6 +383,29 @@ RSpec.describe Gitlab::BitbucketImport::Importer do
expect(project.issues.map(&:work_item_type_id).uniq).to contain_exactly(WorkItems::Type.default_issue_type.id)
end
+
+ context 'with issue comments' do
+ let(:inline_note) do
+ instance_double(Bitbucket::Representation::Comment, note: 'Hello world', author: 'someuser', created_at: Time.now, updated_at: Time.now)
+ end
+
+ before do
+ allow_next_instance_of(Bitbucket::Client) do |instance|
+ allow(instance).to receive(:issue_comments).and_return([inline_note])
+ end
+ end
+
+ it 'imports issue comments' do
+ allow(importer).to receive(:import_wiki)
+ importer.execute
+
+ comment = project.notes.first
+ expect(project.notes.size).to eq(7)
+ expect(comment.note).to include(inline_note.note)
+ expect(comment.note).to include(inline_note.author)
+ expect(importer.errors).to be_empty
+ end
+ end
end
context 'metrics' do
diff --git a/spec/lib/gitlab/github_import/importer/attachments/issues_importer_spec.rb b/spec/lib/gitlab/github_import/importer/attachments/issues_importer_spec.rb
new file mode 100644
index 00000000000..85bc67376d3
--- /dev/null
+++ b/spec/lib/gitlab/github_import/importer/attachments/issues_importer_spec.rb
@@ -0,0 +1,61 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+
+RSpec.describe Gitlab::GithubImport::Importer::Attachments::IssuesImporter do
+ subject(:importer) { described_class.new(project, client) }
+
+ let_it_be(:project) { create(:project) }
+
+ let(:client) { instance_double(Gitlab::GithubImport::Client) }
+
+ describe '#sequential_import', :clean_gitlab_redis_cache do
+ let_it_be(:issue_1) { create(:issue, project: project) }
+ let_it_be(:issue_2) { create(:issue, project: project) }
+
+ let(:importer_stub) { instance_double('Gitlab::GithubImport::Importer::NoteAttachmentsImporter') }
+ let(:importer_attrs) { [instance_of(Gitlab::GithubImport::Representation::NoteText), project, client] }
+
+ it 'imports each project issue attachments' do
+ expect_next_instances_of(
+ Gitlab::GithubImport::Importer::NoteAttachmentsImporter, 2, false, *importer_attrs
+ ) do |note_attachments_importer|
+ expect(note_attachments_importer).to receive(:execute)
+ end
+
+ importer.sequential_import
+ end
+
+ context 'when issue is already processed' do
+ it "doesn't import this issue attachments" do
+ importer.mark_as_imported(issue_1)
+
+ expect_next_instance_of(
+ Gitlab::GithubImport::Importer::NoteAttachmentsImporter, *importer_attrs
+ ) do |note_attachments_importer|
+ expect(note_attachments_importer).to receive(:execute)
+ end
+
+ importer.sequential_import
+ end
+ end
+ end
+
+ describe '#sidekiq_worker_class' do
+ it { expect(importer.sidekiq_worker_class).to eq(Gitlab::GithubImport::Attachments::ImportIssueWorker) }
+ end
+
+ describe '#collection_method' do
+ it { expect(importer.collection_method).to eq(:issue_attachments) }
+ end
+
+ describe '#object_type' do
+ it { expect(importer.object_type).to eq(:issue_attachment) }
+ end
+
+ describe '#id_for_already_imported_cache' do
+ let(:issue) { build_stubbed(:issue) }
+
+ it { expect(importer.id_for_already_imported_cache(issue)).to eq(issue.id) }
+ end
+end
diff --git a/spec/lib/gitlab/github_import/importer/attachments/merge_requests_importer_spec.rb b/spec/lib/gitlab/github_import/importer/attachments/merge_requests_importer_spec.rb
new file mode 100644
index 00000000000..e4718c2d17c
--- /dev/null
+++ b/spec/lib/gitlab/github_import/importer/attachments/merge_requests_importer_spec.rb
@@ -0,0 +1,61 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+
+RSpec.describe Gitlab::GithubImport::Importer::Attachments::MergeRequestsImporter do
+ subject(:importer) { described_class.new(project, client) }
+
+ let_it_be(:project) { create(:project) }
+
+ let(:client) { instance_double(Gitlab::GithubImport::Client) }
+
+ describe '#sequential_import', :clean_gitlab_redis_cache do
+ let_it_be(:merge_request_1) { create(:merge_request, source_project: project, target_branch: 'feature1') }
+ let_it_be(:merge_request_2) { create(:merge_request, source_project: project, target_branch: 'feature2') }
+
+ let(:importer_stub) { instance_double('Gitlab::GithubImport::Importer::NoteAttachmentsImporter') }
+ let(:importer_attrs) { [instance_of(Gitlab::GithubImport::Representation::NoteText), project, client] }
+
+ it 'imports each project merge request attachments' do
+ expect_next_instances_of(
+ Gitlab::GithubImport::Importer::NoteAttachmentsImporter, 2, false, *importer_attrs
+ ) do |note_attachments_importer|
+ expect(note_attachments_importer).to receive(:execute)
+ end
+
+ importer.sequential_import
+ end
+
+ context 'when merge request is already processed' do
+ it "doesn't import this merge request attachments" do
+ importer.mark_as_imported(merge_request_1)
+
+ expect_next_instance_of(
+ Gitlab::GithubImport::Importer::NoteAttachmentsImporter, *importer_attrs
+ ) do |note_attachments_importer|
+ expect(note_attachments_importer).to receive(:execute)
+ end
+
+ importer.sequential_import
+ end
+ end
+ end
+
+ describe '#sidekiq_worker_class' do
+ it { expect(importer.sidekiq_worker_class).to eq(Gitlab::GithubImport::Attachments::ImportMergeRequestWorker) }
+ end
+
+ describe '#collection_method' do
+ it { expect(importer.collection_method).to eq(:merge_request_attachments) }
+ end
+
+ describe '#object_type' do
+ it { expect(importer.object_type).to eq(:merge_request_attachment) }
+ end
+
+ describe '#id_for_already_imported_cache' do
+ let(:merge_request) { build_stubbed(:merge_request) }
+
+ it { expect(importer.id_for_already_imported_cache(merge_request)).to eq(merge_request.id) }
+ end
+end
diff --git a/spec/lib/gitlab/github_import/importer/attachments/notes_importer_spec.rb b/spec/lib/gitlab/github_import/importer/attachments/notes_importer_spec.rb
index f66a7b2eb8f..7ed353e1b71 100644
--- a/spec/lib/gitlab/github_import/importer/attachments/notes_importer_spec.rb
+++ b/spec/lib/gitlab/github_import/importer/attachments/notes_importer_spec.rb
@@ -38,14 +38,6 @@ RSpec.describe Gitlab::GithubImport::Importer::Attachments::NotesImporter do
end
end
- describe '#representation_class' do
- it { expect(importer.representation_class).to eq(Gitlab::GithubImport::Representation::NoteText) }
- end
-
- describe '#importer_class' do
- it { expect(importer.importer_class).to eq(Gitlab::GithubImport::Importer::NoteAttachmentsImporter) }
- end
-
describe '#sidekiq_worker_class' do
it { expect(importer.sidekiq_worker_class).to eq(Gitlab::GithubImport::Attachments::ImportNoteWorker) }
end
diff --git a/spec/lib/gitlab/github_import/importer/attachments/releases_importer_spec.rb b/spec/lib/gitlab/github_import/importer/attachments/releases_importer_spec.rb
index 584e6301f4c..b989345ae09 100644
--- a/spec/lib/gitlab/github_import/importer/attachments/releases_importer_spec.rb
+++ b/spec/lib/gitlab/github_import/importer/attachments/releases_importer_spec.rb
@@ -37,14 +37,6 @@ RSpec.describe Gitlab::GithubImport::Importer::Attachments::ReleasesImporter do
end
end
- describe '#representation_class' do
- it { expect(importer.representation_class).to eq(Gitlab::GithubImport::Representation::NoteText) }
- end
-
- describe '#importer_class' do
- it { expect(importer.importer_class).to eq(Gitlab::GithubImport::Importer::NoteAttachmentsImporter) }
- end
-
describe '#sidekiq_worker_class' do
it { expect(importer.sidekiq_worker_class).to eq(Gitlab::GithubImport::Attachments::ImportReleaseWorker) }
end
diff --git a/spec/lib/gitlab/github_import/importer/note_attachments_importer_spec.rb b/spec/lib/gitlab/github_import/importer/note_attachments_importer_spec.rb
index d4c5612e906..7d4e3c3bcce 100644
--- a/spec/lib/gitlab/github_import/importer/note_attachments_importer_spec.rb
+++ b/spec/lib/gitlab/github_import/importer/note_attachments_importer_spec.rb
@@ -7,6 +7,7 @@ RSpec.describe Gitlab::GithubImport::Importer::NoteAttachmentsImporter do
let_it_be(:project) { create(:project) }
+ let(:note_text) { Gitlab::GithubImport::Representation::NoteText.from_db_record(record) }
let(:client) { instance_double('Gitlab::GithubImport::Client') }
let(:doc_url) { 'https://github.com/nickname/public-test-repo/files/9020437/git-cheat-sheet.txt' }
@@ -22,6 +23,17 @@ RSpec.describe Gitlab::GithubImport::Importer::NoteAttachmentsImporter do
TEXT
end
+ shared_examples 'updates record description' do
+ it do
+ importer.execute
+
+ record.reload
+ expect(record.description).to start_with("Some text...\n\n[special-doc](/uploads/")
+ expect(record.description).to include('![image.jpeg](/uploads/')
+ expect(record.description).to include('<img width="248" alt="tag-image" src="/uploads')
+ end
+ end
+
describe '#execute' do
let(:downloader_stub) { instance_double(Gitlab::GithubImport::AttachmentsDownloader) }
let(:tmp_stub_doc) { Tempfile.create('attachment_download_test.txt') }
@@ -40,37 +52,33 @@ RSpec.describe Gitlab::GithubImport::Importer::NoteAttachmentsImporter do
end
context 'when importing release attachments' do
- let(:release) { create(:release, project: project, description: text) }
- let(:note_text) { Gitlab::GithubImport::Representation::NoteText.from_db_record(release) }
+ let(:record) { create(:release, project: project, description: text) }
+
+ it_behaves_like 'updates record description'
+ end
- it 'updates release description with new attachment url' do
- expect(UploadService).to receive(:new)
- .with(project, tmp_stub_doc, FileUploader).and_call_original
- expect(UploadService).to receive(:new)
- .with(project, tmp_stub_image, FileUploader).and_call_original
- expect(UploadService).to receive(:new)
- .with(project, tmp_stub_image_tag, FileUploader).and_call_original
+ context 'when importing issue attachments' do
+ let(:record) { create(:issue, project: project, description: text) }
- importer.execute
+ it_behaves_like 'updates record description'
+ end
- release.reload
- expect(release.description).to start_with("Some text...\n\n[special-doc](/uploads/")
- expect(release.description).to include('![image.jpeg](/uploads/')
- expect(release.description).to include('<img width="248" alt="tag-image" src="/uploads')
- end
+ context 'when importing merge request attachments' do
+ let(:record) { create(:merge_request, source_project: project, description: text) }
+
+ it_behaves_like 'updates record description'
end
context 'when importing note attachments' do
- let(:note) { create(:note, project: project, note: text) }
- let(:note_text) { Gitlab::GithubImport::Representation::NoteText.from_db_record(note) }
+ let(:record) { create(:note, project: project, note: text) }
it 'updates note text with new attachment urls' do
importer.execute
- note.reload
- expect(note.note).to start_with("Some text...\n\n[special-doc](/uploads/")
- expect(note.note).to include('![image.jpeg](/uploads/')
- expect(note.note).to include('<img width="248" alt="tag-image" src="/uploads')
+ record.reload
+ expect(record.note).to start_with("Some text...\n\n[special-doc](/uploads/")
+ expect(record.note).to include('![image.jpeg](/uploads/')
+ expect(record.note).to include('<img width="248" alt="tag-image" src="/uploads')
end
end
end
diff --git a/spec/lib/gitlab/github_import/representation/note_text_spec.rb b/spec/lib/gitlab/github_import/representation/note_text_spec.rb
index adced223032..8b57c9a0373 100644
--- a/spec/lib/gitlab/github_import/representation/note_text_spec.rb
+++ b/spec/lib/gitlab/github_import/representation/note_text_spec.rb
@@ -4,7 +4,7 @@ require 'spec_helper'
RSpec.describe Gitlab::GithubImport::Representation::NoteText do
shared_examples 'a Note text data' do |match_record_type|
- it 'returns an instance of ReleaseAttachments' do
+ it 'returns an instance of NoteText' do
expect(representation).to be_an_instance_of(described_class)
end
@@ -30,6 +30,22 @@ RSpec.describe Gitlab::GithubImport::Representation::NoteText do
end
end
+ context 'with Issue' do
+ let(:record) { build_stubbed(:issue, id: 42, description: 'Some text here..') }
+
+ it_behaves_like 'a Note text data', 'Issue' do
+ let(:representation) { described_class.from_db_record(record) }
+ end
+ end
+
+ context 'with MergeRequest' do
+ let(:record) { build_stubbed(:merge_request, id: 42, description: 'Some text here..') }
+
+ it_behaves_like 'a Note text data', 'MergeRequest' do
+ let(:representation) { described_class.from_db_record(record) }
+ end
+ end
+
context 'with Note' do
let(:record) { build_stubbed(:note, id: 42, note: 'Some text here..') }
diff --git a/spec/lib/gitlab/memory/watchdog/configuration_spec.rb b/spec/lib/gitlab/memory/watchdog/configuration_spec.rb
new file mode 100644
index 00000000000..892a4b06ad0
--- /dev/null
+++ b/spec/lib/gitlab/memory/watchdog/configuration_spec.rb
@@ -0,0 +1,121 @@
+# frozen_string_literal: true
+
+require 'fast_spec_helper'
+require_dependency 'gitlab/cluster/lifecycle_events'
+
+RSpec.describe Gitlab::Memory::Watchdog::Configuration do
+ subject(:configuration) { described_class.new }
+
+ describe '#initialize' do
+ it 'initialize monitors' do
+ expect(configuration.monitors).to be_an_instance_of(described_class::MonitorStack)
+ end
+ end
+
+ describe '#handler' do
+ context 'when handler is not set' do
+ it 'defaults to NullHandler' do
+ expect(configuration.handler).to be(Gitlab::Memory::Watchdog::NullHandler.instance)
+ end
+ end
+ end
+
+ describe '#logger' do
+ context 'when logger is not set, defaults to stdout logger' do
+ it 'defaults to Logger' do
+ expect(configuration.logger).to be_an_instance_of(::Gitlab::Logger)
+ end
+ end
+ end
+
+ describe '#sleep_time_seconds' do
+ context 'when sleep_time_seconds is not set' do
+ it 'defaults to SLEEP_TIME_SECONDS' do
+ expect(configuration.sleep_time_seconds).to eq(described_class::DEFAULT_SLEEP_TIME_SECONDS)
+ end
+ end
+ end
+
+ describe '#monitors' do
+ context 'when monitors are configured to be used' do
+ let(:payload1) do
+ {
+ message: 'monitor_1_text',
+ memwd_max_strikes: 5,
+ memwd_cur_strikes: 0
+ }
+ end
+
+ let(:payload2) do
+ {
+ message: 'monitor_2_text',
+ memwd_max_strikes: 0,
+ memwd_cur_strikes: 1
+ }
+ end
+
+ let(:monitor_class_1) do
+ Struct.new(:threshold_violated, :payload) do
+ def call
+ { threshold_violated: !!threshold_violated, payload: payload || {} }
+ end
+
+ def self.name
+ 'Monitor1'
+ end
+ end
+ end
+
+ let(:monitor_class_2) do
+ Struct.new(:threshold_violated, :payload) do
+ def call
+ { threshold_violated: !!threshold_violated, payload: payload || {} }
+ end
+
+ def self.name
+ 'Monitor2'
+ end
+ end
+ end
+
+ context 'when two monitors are configured to be used' do
+ before do
+ configuration.monitors.use monitor_class_1, false, { message: 'monitor_1_text' }, max_strikes: 5
+ configuration.monitors.use monitor_class_2, true, { message: 'monitor_2_text' }, max_strikes: 0
+ end
+
+ it 'calls each monitor and returns correct results', :aggregate_failures do
+ payloads = []
+ thresholds = []
+ strikes = []
+ monitor_names = []
+
+ configuration.monitors.call_each do |result|
+ payloads << result.payload
+ thresholds << result.threshold_violated?
+ strikes << result.strikes_exceeded?
+ monitor_names << result.monitor_name
+ end
+
+ expect(payloads).to eq([payload1, payload2])
+ expect(thresholds).to eq([false, true])
+ expect(strikes).to eq([false, true])
+ expect(monitor_names).to eq([:monitor1, :monitor2])
+ end
+ end
+
+ context 'when same monitor class is configured to be used twice' do
+ before do
+ configuration.monitors.use monitor_class_1, max_strikes: 1
+ configuration.monitors.use monitor_class_1, max_strikes: 1
+ end
+
+ it 'calls same monitor only once' do
+ expect do |b|
+ configuration.monitors.call_each(&b)
+ end.to yield_control.once
+ end
+ end
+ end
+ end
+end
diff --git a/spec/lib/gitlab/memory/watchdog/monitor/heap_fragmentation_spec.rb b/spec/lib/gitlab/memory/watchdog/monitor/heap_fragmentation_spec.rb
new file mode 100644
index 00000000000..dad19cfd588
--- /dev/null
+++ b/spec/lib/gitlab/memory/watchdog/monitor/heap_fragmentation_spec.rb
@@ -0,0 +1,60 @@
+# frozen_string_literal: true
+
+require 'fast_spec_helper'
+require 'support/shared_examples/lib/gitlab/memory/watchdog/monitor_result_shared_examples'
+require 'prometheus/client'
+
+RSpec.describe Gitlab::Memory::Watchdog::Monitor::HeapFragmentation do
+ let(:heap_frag_limit_gauge) { instance_double(::Prometheus::Client::Gauge) }
+ let(:max_heap_fragmentation) { 0.2 }
+ let(:fragmentation) { 0.3 }
+
+ subject(:monitor) do
+ described_class.new(max_heap_fragmentation: max_heap_fragmentation)
+ end
+
+ before do
+ allow(Gitlab::Metrics).to receive(:gauge)
+ .with(:gitlab_memwd_heap_frag_limit, anything)
+ .and_return(heap_frag_limit_gauge)
+ allow(heap_frag_limit_gauge).to receive(:set)
+
+ allow(Gitlab::Metrics::Memory).to receive(:gc_heap_fragmentation).and_return(fragmentation)
+ end
+
+ describe '#initialize' do
+ it 'sets the heap fragmentation limit gauge' do
+ expect(heap_frag_limit_gauge).to receive(:set).with({}, max_heap_fragmentation)
+
+ monitor
+ end
+ end
+
+ describe '#call' do
+ it 'gets gc_heap_fragmentation' do
+ expect(Gitlab::Metrics::Memory).to receive(:gc_heap_fragmentation)
+
+ monitor.call
+ end
+
+ context 'when process exceeds threshold' do
+ let(:fragmentation) { max_heap_fragmentation + 0.1 }
+ let(:payload) do
+ {
+ message: 'heap fragmentation limit exceeded',
+ memwd_cur_heap_frag: fragmentation,
+ memwd_max_heap_frag: max_heap_fragmentation
+ }
+ end
+
+ include_examples 'returns Watchdog Monitor result', threshold_violated: true
+ end
+
+ context 'when process does not exceed threshold' do
+ let(:fragmentation) { max_heap_fragmentation - 0.1 }
+ let(:payload) { {} }
+
+ include_examples 'returns Watchdog Monitor result', threshold_violated: false
+ end
+ end
+end
diff --git a/spec/lib/gitlab/memory/watchdog/monitor/unique_memory_growth_spec.rb b/spec/lib/gitlab/memory/watchdog/monitor/unique_memory_growth_spec.rb
new file mode 100644
index 00000000000..22494af4425
--- /dev/null
+++ b/spec/lib/gitlab/memory/watchdog/monitor/unique_memory_growth_spec.rb
@@ -0,0 +1,62 @@
+# frozen_string_literal: true
+
+require 'fast_spec_helper'
+require 'support/shared_examples/lib/gitlab/memory/watchdog/monitor_result_shared_examples'
+require_dependency 'gitlab/cluster/lifecycle_events'
+
+RSpec.describe Gitlab::Memory::Watchdog::Monitor::UniqueMemoryGrowth do
+ let(:primary_memory) { 2048 }
+ let(:worker_memory) { 0 }
+ let(:max_mem_growth) { 2 }
+
+ subject(:monitor) do
+ described_class.new(max_mem_growth: max_mem_growth)
+ end
+
+ before do
+ allow(Gitlab::Metrics::System).to receive(:memory_usage_uss_pss).and_return({ uss: worker_memory })
+ allow(Gitlab::Metrics::System).to receive(:memory_usage_uss_pss).with(
+ pid: Gitlab::Cluster::PRIMARY_PID
+ ).and_return({ uss: primary_memory })
+ end
+
+ describe '#call' do
+ it 'gets memory_usage_uss_pss' do
+ expect(Gitlab::Metrics::System).to receive(:memory_usage_uss_pss).with(no_args)
+ expect(Gitlab::Metrics::System).to receive(:memory_usage_uss_pss).with(pid: Gitlab::Cluster::PRIMARY_PID)
+
+ monitor.call
+ end
+
+ context 'when monitor is called twice' do
+ it 'reference memory is calculated only once' do
+ expect(Gitlab::Metrics::System).to receive(:memory_usage_uss_pss).with(no_args).twice
+ expect(Gitlab::Metrics::System).to receive(:memory_usage_uss_pss).with(pid: Gitlab::Cluster::PRIMARY_PID).once
+
+ monitor.call
+ monitor.call
+ end
+ end
+
+ context 'when process exceeds threshold' do
+ let(:worker_memory) { max_mem_growth * primary_memory + 1 }
+ let(:payload) do
+ {
+ message: 'memory limit exceeded',
+ memwd_max_uss_bytes: max_mem_growth * primary_memory,
+ memwd_ref_uss_bytes: primary_memory,
+ memwd_uss_bytes: worker_memory
+ }
+ end
+
+ include_examples 'returns Watchdog Monitor result', threshold_violated: true
+ end
+
+ context 'when process does not exceed threshold' do
+ let(:worker_memory) { max_mem_growth * primary_memory - 1 }
+ let(:payload) { {} }
+
+ include_examples 'returns Watchdog Monitor result', threshold_violated: false
+ end
+ end
+end
diff --git a/spec/lib/gitlab/memory/watchdog/monitor_state_spec.rb b/spec/lib/gitlab/memory/watchdog/monitor_state_spec.rb
new file mode 100644
index 00000000000..ace1353c6e3
--- /dev/null
+++ b/spec/lib/gitlab/memory/watchdog/monitor_state_spec.rb
@@ -0,0 +1,72 @@
+# frozen_string_literal: true
+
+require 'fast_spec_helper'
+
+RSpec.describe Gitlab::Memory::Watchdog::MonitorState do
+ let(:max_strikes) { 2 }
+ let(:payload) { { message: 'DummyMessage' } }
+ let(:threshold_violated) { true }
+ let(:monitor) { monitor_class.new(threshold_violated, payload) }
+ let(:monitor_class) do
+ Struct.new(:threshold_violated, :payload) do
+ def call
+ { threshold_violated: threshold_violated, payload: payload }
+ end
+
+ def self.name
+ 'MonitorName'
+ end
+ end
+ end
+
+ subject(:monitor_state) { described_class.new(monitor, max_strikes: max_strikes) }
+
+ shared_examples 'returns correct result' do
+ it 'returns correct result', :aggregate_failures do
+ result = monitor_state.call
+
+ expect(result).to be_an_instance_of(described_class::Result)
+ expect(result.strikes_exceeded?).to eq(strikes_exceeded)
+ expect(result.threshold_violated?).to eq(threshold_violated)
+ expect(result.payload).to eq(expected_payload)
+ expect(result.monitor_name).to eq(:monitor_name)
+ end
+ end
+
+ describe '#call' do
+ let(:strikes_exceeded) { false }
+ let(:curr_strikes) { 0 }
+ let(:expected_payload) do
+ {
+ memwd_max_strikes: max_strikes,
+ memwd_cur_strikes: curr_strikes
+ }.merge(payload)
+ end
+
+ context 'when threshold is not violated' do
+ let(:threshold_violated) { false }
+
+ include_examples 'returns correct result'
+ end
+
+ context 'when threshold is violated' do
+ let(:curr_strikes) { 1 }
+ let(:threshold_violated) { true }
+
+ include_examples 'returns correct result'
+
+ context 'when strikes_exceeded' do
+ let(:max_strikes) { 0 }
+ let(:strikes_exceeded) { true }
+
+ include_examples 'returns correct result'
+ end
+ end
+ end
+
+ describe '#monitor_class' do
+ subject { monitor_state.monitor_class }
+
+ it { is_expected.to eq(monitor_class) }
+ end
+end
diff --git a/spec/lib/gitlab/memory/watchdog_spec.rb b/spec/lib/gitlab/memory/watchdog_spec.rb
index beb49660022..84e9a577afb 100644
--- a/spec/lib/gitlab/memory/watchdog_spec.rb
+++ b/spec/lib/gitlab/memory/watchdog_spec.rb
@@ -1,35 +1,35 @@
# frozen_string_literal: true
require 'spec_helper'
-require_relative '../../../../lib/gitlab/cluster/lifecycle_events'
-RSpec.describe Gitlab::Memory::Watchdog, :aggregate_failures, :prometheus do
+RSpec.describe Gitlab::Memory::Watchdog, :aggregate_failures do
context 'watchdog' do
- let(:logger) { instance_double(::Logger) }
+ let(:configuration) { instance_double(described_class::Configuration) }
let(:handler) { instance_double(described_class::NullHandler) }
-
- let(:heap_frag_limit_gauge) { instance_double(::Prometheus::Client::Gauge) }
+ let(:logger) { instance_double(::Logger) }
+ let(:sleep_time_seconds) { 60 }
+ let(:threshold_violated) { false }
let(:violations_counter) { instance_double(::Prometheus::Client::Counter) }
let(:violations_handled_counter) { instance_double(::Prometheus::Client::Counter) }
-
- let(:sleep_time) { 0.1 }
- let(:max_heap_fragmentation) { 0.2 }
- let(:max_mem_growth) { 2 }
-
- # Defaults that will not trigger any events.
- let(:fragmentation) { 0 }
- let(:worker_memory) { 0 }
- let(:primary_memory) { 0 }
- let(:max_strikes) { 0 }
-
- # Tests should set this to control the number of loop iterations in `call`.
let(:watchdog_iterations) { 1 }
+ let(:name) { :monitor_name }
+ let(:payload) { { message: 'dummy_text' } }
+ let(:max_strikes) { 2 }
+ let(:monitor_class) do
+ Struct.new(:threshold_violated, :payload) do
+ def call
+ { threshold_violated: threshold_violated, payload: payload }
+ end
+
+ def self.name
+ 'MonitorName'
+ end
+ end
+ end
subject(:watchdog) do
- described_class.new(handler: handler, logger: logger, sleep_time_seconds: sleep_time,
- max_strikes: max_strikes, max_mem_growth: max_mem_growth,
- max_heap_fragmentation: max_heap_fragmentation).tap do |instance|
- # We need to defuse `sleep` and stop the internal loop after N iterations.
+ described_class.new.tap do |instance|
+ # We need to defuse `sleep` and stop the internal loop after 1 iteration
iterations = 0
allow(instance).to receive(:sleep) do
instance.stop if (iterations += 1) > watchdog_iterations
@@ -38,9 +38,6 @@ RSpec.describe Gitlab::Memory::Watchdog, :aggregate_failures, :prometheus do
end
def stub_prometheus_metrics
- allow(Gitlab::Metrics).to receive(:gauge)
- .with(:gitlab_memwd_heap_frag_limit, anything)
- .and_return(heap_frag_limit_gauge)
allow(Gitlab::Metrics).to receive(:counter)
.with(:gitlab_memwd_violations_total, anything, anything)
.and_return(violations_counter)
@@ -48,318 +45,195 @@ RSpec.describe Gitlab::Memory::Watchdog, :aggregate_failures, :prometheus do
.with(:gitlab_memwd_violations_handled_total, anything, anything)
.and_return(violations_handled_counter)
- allow(heap_frag_limit_gauge).to receive(:set)
allow(violations_counter).to receive(:increment)
allow(violations_handled_counter).to receive(:increment)
end
- before do
- stub_prometheus_metrics
-
- allow(handler).to receive(:call).and_return(true)
-
- allow(logger).to receive(:warn)
- allow(logger).to receive(:info)
-
- allow(Gitlab::Metrics::Memory).to receive(:gc_heap_fragmentation).and_return(fragmentation)
- allow(Gitlab::Metrics::System).to receive(:memory_usage_uss_pss).and_return({ uss: worker_memory })
- allow(Gitlab::Metrics::System).to receive(:memory_usage_uss_pss).with(
- pid: Gitlab::Cluster::PRIMARY_PID
- ).and_return({ uss: primary_memory })
-
- allow(::Prometheus::PidProvider).to receive(:worker_id).and_return('worker_1')
- end
-
- context 'when created' do
- it 'sets the heap fragmentation limit gauge' do
- expect(heap_frag_limit_gauge).to receive(:set).with({}, max_heap_fragmentation)
+ describe '#initialize' do
+ it 'initialize new configuration' do
+ expect(described_class::Configuration).to receive(:new)
watchdog
end
-
- context 'when no settings are set in the environment' do
- it 'initializes with defaults' do
- watchdog = described_class.new(handler: handler, logger: logger)
-
- expect(watchdog.max_heap_fragmentation).to eq(described_class::DEFAULT_MAX_HEAP_FRAG)
- expect(watchdog.max_mem_growth).to eq(described_class::DEFAULT_MAX_MEM_GROWTH)
- expect(watchdog.max_strikes).to eq(described_class::DEFAULT_MAX_STRIKES)
- expect(watchdog.sleep_time_seconds).to eq(described_class::DEFAULT_SLEEP_TIME_SECONDS)
- end
- end
-
- context 'when settings are passed through the environment' do
- before do
- stub_env('GITLAB_MEMWD_MAX_HEAP_FRAG', 1)
- stub_env('GITLAB_MEMWD_MAX_STRIKES', 2)
- stub_env('GITLAB_MEMWD_SLEEP_TIME_SEC', 3)
- stub_env('GITLAB_MEMWD_MAX_MEM_GROWTH', 4)
- end
-
- it 'initializes with these settings' do
- watchdog = described_class.new(handler: handler, logger: logger)
-
- expect(watchdog.max_heap_fragmentation).to eq(1)
- expect(watchdog.max_strikes).to eq(2)
- expect(watchdog.sleep_time_seconds).to eq(3)
- expect(watchdog.max_mem_growth).to eq(4)
- end
- end
end
- shared_examples 'has strikes left' do |stat|
- context 'when process has not exceeded allowed number of strikes' do
- let(:watchdog_iterations) { max_strikes }
-
- it 'does not signal the handler' do
- expect(handler).not_to receive(:call)
-
- watchdog.call
- end
-
- it 'does not log any events' do
- expect(logger).not_to receive(:warn)
-
- watchdog.call
- end
-
- it 'increments the violations counter' do
- expect(violations_counter).to receive(:increment).with(reason: stat).exactly(watchdog_iterations)
-
- watchdog.call
+ describe '#call' do
+ before do
+ stub_prometheus_metrics
+ allow(Gitlab::Metrics::System).to receive(:memory_usage_rss).at_least(:once).and_return(1024)
+ allow(::Prometheus::PidProvider).to receive(:worker_id).and_return('worker_1')
+
+ watchdog.configure do |config|
+ config.handler = handler
+ config.logger = logger
+ config.sleep_time_seconds = sleep_time_seconds
+ config.monitors.use monitor_class, threshold_violated, payload, max_strikes: max_strikes
end
- it 'does not increment violations handled counter' do
- expect(violations_handled_counter).not_to receive(:increment)
-
- watchdog.call
- end
+ allow(handler).to receive(:call).and_return(true)
+ allow(logger).to receive(:info)
+ allow(logger).to receive(:warn)
end
- end
- shared_examples 'no strikes left' do |stat|
- it 'signals the handler and resets strike counter' do
- expect(handler).to receive(:call).and_return(true)
+ it 'logs start message once' do
+ expect(logger).to receive(:info).once
+ .with(
+ pid: Process.pid,
+ worker_id: 'worker_1',
+ memwd_handler_class: handler.class.name,
+ memwd_sleep_time_s: sleep_time_seconds,
+ memwd_rss_bytes: 1024,
+ message: 'started')
watchdog.call
-
- expect(watchdog.strikes(stat.to_sym)).to eq(0)
end
- it 'increments both the violations and violations handled counters' do
- expect(violations_counter).to receive(:increment).with(reason: stat).exactly(watchdog_iterations)
- expect(violations_handled_counter).to receive(:increment).with(reason: stat)
+ it 'waits for check interval seconds' do
+ expect(watchdog).to receive(:sleep).with(sleep_time_seconds)
watchdog.call
end
- context 'when enforce_memory_watchdog ops toggle is off' do
+ context 'when gitlab_memory_watchdog ops toggle is off' do
before do
- stub_feature_flags(enforce_memory_watchdog: false)
+ stub_feature_flags(gitlab_memory_watchdog: false)
end
- it 'always uses the NullHandler' do
- expect(handler).not_to receive(:call)
- expect(described_class::NullHandler.instance).to receive(:call).and_return(true)
-
- watchdog.call
+ it 'does not trigger any monitor' do
+ expect(configuration).not_to receive(:monitors)
end
end
- context 'when handler result is true' do
- it 'considers the event handled and stops itself' do
- expect(handler).to receive(:call).once.and_return(true)
- expect(logger).to receive(:info).with(hash_including(message: 'stopped'))
+ context 'when process does not exceed threshold' do
+ it 'does not increment violations counters' do
+ expect(violations_counter).not_to receive(:increment)
+ expect(violations_handled_counter).not_to receive(:increment)
watchdog.call
end
- end
-
- context 'when handler result is false' do
- let(:max_strikes) { 0 } # to make sure the handler fires each iteration
- let(:watchdog_iterations) { 3 }
- it 'keeps running' do
- expect(violations_counter).to receive(:increment).exactly(watchdog_iterations)
- expect(violations_handled_counter).to receive(:increment).exactly(watchdog_iterations)
- # Return true the third time to terminate the daemon.
- expect(handler).to receive(:call).and_return(false, false, true)
+ it 'does not log violation' do
+ expect(logger).not_to receive(:warn)
watchdog.call
end
- end
- end
-
- context 'when monitoring memory growth' do
- let(:primary_memory) { 2048 }
-
- context 'when process does not exceed threshold' do
- let(:worker_memory) { max_mem_growth * primary_memory - 1 }
- it 'does not signal the handler' do
+ it 'does not execute handler' do
expect(handler).not_to receive(:call)
watchdog.call
end
end
- context 'when process exceeds threshold permanently' do
- let(:worker_memory) { max_mem_growth * primary_memory + 1 }
- let(:max_strikes) { 3 }
-
- it_behaves_like 'has strikes left', 'mem_growth'
+ context 'when process exceeds threshold' do
+ let(:threshold_violated) { true }
- context 'when process exceeds the allowed number of strikes' do
- let(:watchdog_iterations) { max_strikes + 1 }
+ it 'increments violations counter' do
+ expect(violations_counter).to receive(:increment).with(reason: name)
- it_behaves_like 'no strikes left', 'mem_growth'
+ watchdog.call
+ end
- it 'only reads reference memory once' do
- expect(Gitlab::Metrics::System).to receive(:memory_usage_uss_pss)
- .with(pid: Gitlab::Cluster::PRIMARY_PID)
- .once
+ context 'when process does not exceed the allowed number of strikes' do
+ it 'does not increment handled violations counter' do
+ expect(violations_handled_counter).not_to receive(:increment)
watchdog.call
end
- it 'logs the event' do
- expect(Gitlab::Metrics::System).to receive(:memory_usage_rss).at_least(:once).and_return(1024)
- expect(logger).to receive(:warn).with({
- message: 'memory limit exceeded',
- pid: Process.pid,
- worker_id: 'worker_1',
- memwd_handler_class: 'RSpec::Mocks::InstanceVerifyingDouble',
- memwd_sleep_time_s: sleep_time,
- memwd_max_uss_bytes: max_mem_growth * primary_memory,
- memwd_ref_uss_bytes: primary_memory,
- memwd_uss_bytes: worker_memory,
- memwd_rss_bytes: 1024,
- memwd_max_strikes: max_strikes,
- memwd_cur_strikes: max_strikes + 1
- })
+ it 'does not log violation' do
+ expect(logger).not_to receive(:warn)
watchdog.call
end
- end
- end
- context 'when process exceeds threshold temporarily' do
- let(:worker_memory) { max_mem_growth * primary_memory }
- let(:max_strikes) { 1 }
- let(:watchdog_iterations) { 4 }
+ it 'does not execute handler' do
+ expect(handler).not_to receive(:call)
- before do
- allow(Gitlab::Metrics::System).to receive(:memory_usage_uss_pss).and_return(
- { uss: worker_memory - 0.1 },
- { uss: worker_memory + 0.2 },
- { uss: worker_memory - 0.1 },
- { uss: worker_memory + 0.1 }
- )
- allow(Gitlab::Metrics::System).to receive(:memory_usage_uss_pss).with(
- pid: Gitlab::Cluster::PRIMARY_PID
- ).and_return({ uss: primary_memory })
+ watchdog.call
+ end
end
- it 'does not signal the handler' do
- expect(handler).not_to receive(:call)
+ context 'when monitor exceeds the allowed number of strikes' do
+ let(:max_strikes) { 0 }
- watchdog.call
- end
- end
- end
+ it 'increments handled violations counter' do
+ expect(violations_handled_counter).to receive(:increment).with(reason: name)
- context 'when monitoring heap fragmentation' do
- context 'when process does not exceed threshold' do
- let(:fragmentation) { max_heap_fragmentation - 0.1 }
-
- it 'does not signal the handler' do
- expect(handler).not_to receive(:call)
-
- watchdog.call
- end
- end
-
- context 'when process exceeds threshold permanently' do
- let(:fragmentation) { max_heap_fragmentation + 0.1 }
- let(:max_strikes) { 3 }
-
- it_behaves_like 'has strikes left', 'heap_frag'
+ watchdog.call
+ end
- context 'when process exceeds the allowed number of strikes' do
- let(:watchdog_iterations) { max_strikes + 1 }
+ it 'logs violation' do
+ expect(logger).to receive(:warn)
+ .with(
+ pid: Process.pid,
+ worker_id: 'worker_1',
+ memwd_handler_class: handler.class.name,
+ memwd_sleep_time_s: sleep_time_seconds,
+ memwd_rss_bytes: 1024,
+ memwd_cur_strikes: 1,
+ memwd_max_strikes: max_strikes,
+ message: 'dummy_text')
- it_behaves_like 'no strikes left', 'heap_frag'
+ watchdog.call
+ end
- it 'logs the event' do
- expect(Gitlab::Metrics::System).to receive(:memory_usage_rss).at_least(:once).and_return(1024)
- expect(logger).to receive(:warn).with({
- message: 'heap fragmentation limit exceeded',
- pid: Process.pid,
- worker_id: 'worker_1',
- memwd_handler_class: 'RSpec::Mocks::InstanceVerifyingDouble',
- memwd_sleep_time_s: sleep_time,
- memwd_max_heap_frag: max_heap_fragmentation,
- memwd_cur_heap_frag: fragmentation,
- memwd_max_strikes: max_strikes,
- memwd_cur_strikes: max_strikes + 1,
- memwd_rss_bytes: 1024
- })
+ it 'executes handler' do
+ expect(handler).to receive(:call)
watchdog.call
end
- end
- end
- context 'when process exceeds threshold temporarily' do
- let(:fragmentation) { max_heap_fragmentation }
- let(:max_strikes) { 1 }
- let(:watchdog_iterations) { 4 }
+ context 'when enforce_memory_watchdog ops toggle is off' do
+ before do
+ stub_feature_flags(enforce_memory_watchdog: false)
+ end
- before do
- allow(Gitlab::Metrics::Memory).to receive(:gc_heap_fragmentation).and_return(
- fragmentation - 0.1,
- fragmentation + 0.2,
- fragmentation - 0.1,
- fragmentation + 0.1
- )
- end
+ it 'always uses the NullHandler' do
+ expect(handler).not_to receive(:call)
+ expect(described_class::NullHandler.instance).to receive(:call).and_return(true)
- it 'does not signal the handler' do
- expect(handler).not_to receive(:call)
+ watchdog.call
+ end
+ end
- watchdog.call
+ context 'when multiple monitors exceeds allowed number of strikes' do
+ before do
+ watchdog.configure do |config|
+ config.handler = handler
+ config.logger = logger
+ config.sleep_time_seconds = sleep_time_seconds
+ config.monitors.use monitor_class, threshold_violated, payload, max_strikes: max_strikes
+ config.monitors.use monitor_class, threshold_violated, payload, max_strikes: max_strikes
+ end
+ end
+
+ it 'only calls the handler once' do
+ expect(handler).to receive(:call).once.and_return(true)
+
+ watchdog.call
+ end
+ end
end
end
- end
-
- context 'when both memory fragmentation and growth exceed thresholds' do
- let(:fragmentation) { max_heap_fragmentation + 0.1 }
- let(:primary_memory) { 2048 }
- let(:worker_memory) { max_mem_growth * primary_memory + 1 }
- let(:watchdog_iterations) { max_strikes + 1 }
- it 'only calls the handler once' do
- expect(handler).to receive(:call).once.and_return(true)
+ it 'logs stop message once' do
+ expect(logger).to receive(:info).once
+ .with(
+ pid: Process.pid,
+ worker_id: 'worker_1',
+ memwd_handler_class: handler.class.name,
+ memwd_sleep_time_s: sleep_time_seconds,
+ memwd_rss_bytes: 1024,
+ message: 'stopped')
watchdog.call
end
end
- context 'when gitlab_memory_watchdog ops toggle is off' do
- before do
- stub_feature_flags(gitlab_memory_watchdog: false)
- end
-
- it 'does not monitor heap fragmentation' do
- expect(Gitlab::Metrics::Memory).not_to receive(:gc_heap_fragmentation)
-
- watchdog.call
- end
-
- it 'does not monitor memory growth' do
- expect(Gitlab::Metrics::System).not_to receive(:memory_usage_uss_pss)
-
- watchdog.call
+ describe '#configure' do
+ it 'yields block' do
+ expect { |b| watchdog.configure(&b) }.to yield_control
end
end
end
diff --git a/spec/models/user_spec.rb b/spec/models/user_spec.rb
index d3cf0c79a56..b08c85339d1 100644
--- a/spec/models/user_spec.rb
+++ b/spec/models/user_spec.rb
@@ -140,7 +140,6 @@ RSpec.describe User do
it { is_expected.to have_many(:timelogs) }
it { is_expected.to have_many(:callouts).class_name('Users::Callout') }
it { is_expected.to have_many(:group_callouts).class_name('Users::GroupCallout') }
- it { is_expected.to have_many(:namespace_callouts).class_name('Users::NamespaceCallout') }
it { is_expected.to have_many(:project_callouts).class_name('Users::ProjectCallout') }
describe '#user_detail' do
@@ -6666,96 +6665,6 @@ RSpec.describe User do
end
end
- describe 'Users::NamespaceCallout' do
- describe '#dismissed_callout_for_namespace?' do
- let_it_be(:user, refind: true) { create(:user) }
- let_it_be(:namespace) { create(:namespace) }
- let_it_be(:feature_name) { Users::NamespaceCallout.feature_names.each_key.first }
-
- let(:query) do
- { feature_name: feature_name, namespace: namespace }
- end
-
- def have_dismissed_callout
- be_dismissed_callout_for_namespace(**query)
- end
-
- context 'when no callout dismissal record exists' do
- it 'returns false when no ignore_dismissal_earlier_than provided' do
- expect(user).not_to have_dismissed_callout
- end
- end
-
- context 'when dismissed callout exists' do
- before_all do
- create(:namespace_callout,
- user: user,
- namespace_id: namespace.id,
- feature_name: feature_name,
- dismissed_at: 4.months.ago)
- end
-
- it 'returns true when no ignore_dismissal_earlier_than provided' do
- expect(user).to have_dismissed_callout
- end
-
- it 'returns true when ignore_dismissal_earlier_than is earlier than dismissed_at' do
- query[:ignore_dismissal_earlier_than] = 6.months.ago
-
- expect(user).to have_dismissed_callout
- end
-
- it 'returns false when ignore_dismissal_earlier_than is later than dismissed_at' do
- query[:ignore_dismissal_earlier_than] = 2.months.ago
-
- expect(user).not_to have_dismissed_callout
- end
- end
- end
-
- describe '#find_or_initialize_namespace_callout' do
- let_it_be(:user, refind: true) { create(:user) }
- let_it_be(:namespace) { create(:namespace) }
- let_it_be(:feature_name) { Users::NamespaceCallout.feature_names.each_key.first }
-
- subject(:callout_with_source) do
- user.find_or_initialize_namespace_callout(feature_name, namespace.id)
- end
-
- context 'when callout exists' do
- let!(:callout) do
- create(:namespace_callout, user: user, feature_name: feature_name, namespace_id: namespace.id)
- end
-
- it 'returns existing callout' do
- expect(callout_with_source).to eq(callout)
- end
- end
-
- context 'when callout does not exist' do
- context 'when feature name is valid' do
- it 'initializes a new callout' do
- expect(callout_with_source)
- .to be_a_new(Users::NamespaceCallout)
- .and be_valid
- end
- end
-
- context 'when feature name is not valid' do
- let(:feature_name) { 'notvalid' }
-
- it 'initializes a new callout' do
- expect(callout_with_source).to be_a_new(Users::NamespaceCallout)
- end
-
- it 'is not valid' do
- expect(callout_with_source).not_to be_valid
- end
- end
- end
- end
- end
-
describe '#dismissed_callout_for_group?' do
let_it_be(:user, refind: true) { create(:user) }
let_it_be(:group) { create(:group) }
diff --git a/spec/models/users/namespace_callout_spec.rb b/spec/models/users/namespace_callout_spec.rb
deleted file mode 100644
index f8207f2abc8..00000000000
--- a/spec/models/users/namespace_callout_spec.rb
+++ /dev/null
@@ -1,39 +0,0 @@
-# frozen_string_literal: true
-
-require 'spec_helper'
-
-RSpec.describe Users::NamespaceCallout do
- let_it_be(:user) { create_default(:user) }
- let_it_be(:namespace) { create_default(:namespace) }
- let_it_be(:callout) { create(:namespace_callout) }
-
- it_behaves_like 'having unique enum values'
-
- describe 'relationships' do
- it { is_expected.to belong_to(:namespace) }
- end
-
- describe 'validations' do
- it { is_expected.to validate_presence_of(:namespace) }
- it { is_expected.to validate_presence_of(:user) }
- it { is_expected.to validate_presence_of(:feature_name) }
-
- specify do
- is_expected.to validate_uniqueness_of(:feature_name)
- .scoped_to(:user_id, :namespace_id)
- .ignoring_case_sensitivity
- end
-
- it { is_expected.to allow_value(:web_hook_disabled).for(:feature_name) }
-
- it 'rejects invalid feature names' do
- expect { callout.feature_name = :non_existent_feature }.to raise_error(ArgumentError)
- end
- end
-
- describe '#source_feature_name' do
- it 'provides string based off source and feature' do
- expect(callout.source_feature_name).to eq "#{callout.feature_name}_#{callout.namespace_id}"
- end
- end
-end
diff --git a/spec/policies/group_policy_spec.rb b/spec/policies/group_policy_spec.rb
index 184f7d676ba..c65933c5208 100644
--- a/spec/policies/group_policy_spec.rb
+++ b/spec/policies/group_policy_spec.rb
@@ -1241,21 +1241,7 @@ RSpec.describe GroupPolicy do
let(:current_user) { admin }
context 'when admin mode is enabled', :enable_admin_mode do
- context 'with runners_finder_all_available FF disabled' do
- before do
- stub_feature_flags(runners_finder_all_available: false)
- end
-
- specify { is_expected.to be_disallowed(:read_group_all_available_runners) }
- end
-
- context 'with runners_finder_all_available FF enabled' do
- before do
- stub_feature_flags(runners_finder_all_available: [group])
- end
-
- specify { is_expected.to be_allowed(:read_group_all_available_runners) }
- end
+ specify { is_expected.to be_allowed(:read_group_all_available_runners) }
end
context 'when admin mode is disabled' do
@@ -1266,21 +1252,7 @@ RSpec.describe GroupPolicy do
context 'with owner' do
let(:current_user) { owner }
- context 'with runners_finder_all_available FF disabled' do
- before do
- stub_feature_flags(runners_finder_all_available: false)
- end
-
- specify { is_expected.to be_disallowed(:read_group_all_available_runners) }
- end
-
- context 'with runners_finder_all_available FF enabled' do
- before do
- stub_feature_flags(runners_finder_all_available: [group])
- end
-
- specify { is_expected.to be_allowed(:read_group_all_available_runners) }
- end
+ specify { is_expected.to be_allowed(:read_group_all_available_runners) }
end
context 'with maintainer' do
diff --git a/spec/requests/api/graphql/mutations/namespace/package_settings/update_spec.rb b/spec/requests/api/graphql/mutations/namespace/package_settings/update_spec.rb
index 194e42bf59d..567d8799d93 100644
--- a/spec/requests/api/graphql/mutations/namespace/package_settings/update_spec.rb
+++ b/spec/requests/api/graphql/mutations/namespace/package_settings/update_spec.rb
@@ -14,7 +14,13 @@ RSpec.describe 'Updating the package settings' do
maven_duplicates_allowed: false,
maven_duplicate_exception_regex: 'foo-.*',
generic_duplicates_allowed: false,
- generic_duplicate_exception_regex: 'bar-.*'
+ generic_duplicate_exception_regex: 'bar-.*',
+ maven_package_requests_forwarding: true,
+ lock_maven_package_requests_forwarding: true,
+ npm_package_requests_forwarding: true,
+ lock_npm_package_requests_forwarding: true,
+ pypi_package_requests_forwarding: true,
+ lock_pypi_package_requests_forwarding: true
}
end
@@ -26,6 +32,12 @@ RSpec.describe 'Updating the package settings' do
mavenDuplicateExceptionRegex
genericDuplicatesAllowed
genericDuplicateExceptionRegex
+ mavenPackageRequestsForwarding
+ lockMavenPackageRequestsForwarding
+ npmPackageRequestsForwarding
+ lockNpmPackageRequestsForwarding
+ pypiPackageRequestsForwarding
+ lockPypiPackageRequestsForwarding
}
errors
QL
@@ -46,6 +58,12 @@ RSpec.describe 'Updating the package settings' do
expect(package_settings_response['mavenDuplicateExceptionRegex']).to eq(params[:maven_duplicate_exception_regex])
expect(package_settings_response['genericDuplicatesAllowed']).to eq(params[:generic_duplicates_allowed])
expect(package_settings_response['genericDuplicateExceptionRegex']).to eq(params[:generic_duplicate_exception_regex])
+ expect(package_settings_response['mavenPackageRequestsForwarding']).to eq(params[:maven_package_requests_forwarding])
+ expect(package_settings_response['lockMavenPackageRequestsForwarding']).to eq(params[:lock_maven_package_requests_forwarding])
+ expect(package_settings_response['pypiPackageRequestsForwarding']).to eq(params[:pypi_package_requests_forwarding])
+ expect(package_settings_response['lockPypiPackageRequestsForwarding']).to eq(params[:lock_pypi_package_requests_forwarding])
+ expect(package_settings_response['npmPackageRequestsForwarding']).to eq(params[:npm_package_requests_forwarding])
+ expect(package_settings_response['lockNpmPackageRequestsForwarding']).to eq(params[:lock_npm_package_requests_forwarding])
end
end
@@ -75,8 +93,29 @@ RSpec.describe 'Updating the package settings' do
RSpec.shared_examples 'accepting the mutation request updating the package settings' do
it_behaves_like 'updating the namespace package setting attributes',
- from: { maven_duplicates_allowed: true, maven_duplicate_exception_regex: 'SNAPSHOT', generic_duplicates_allowed: true, generic_duplicate_exception_regex: 'foo' },
- to: { maven_duplicates_allowed: false, maven_duplicate_exception_regex: 'foo-.*', generic_duplicates_allowed: false, generic_duplicate_exception_regex: 'bar-.*' }
+ from: {
+ maven_duplicates_allowed: true,
+ maven_duplicate_exception_regex: 'SNAPSHOT',
+ generic_duplicates_allowed: true,
+ generic_duplicate_exception_regex: 'foo',
+ maven_package_requests_forwarding: nil,
+ lock_maven_package_requests_forwarding: false,
+ npm_package_requests_forwarding: nil,
+ lock_npm_package_requests_forwarding: false,
+ pypi_package_requests_forwarding: nil,
+ lock_pypi_package_requests_forwarding: false
+ }, to: {
+ maven_duplicates_allowed: false,
+ maven_duplicate_exception_regex: 'foo-.*',
+ generic_duplicates_allowed: false,
+ generic_duplicate_exception_regex: 'bar-.*',
+ maven_package_requests_forwarding: true,
+ lock_maven_package_requests_forwarding: true,
+ npm_package_requests_forwarding: true,
+ lock_npm_package_requests_forwarding: true,
+ pypi_package_requests_forwarding: true,
+ lock_pypi_package_requests_forwarding: true
+ }
it_behaves_like 'returning a success'
it_behaves_like 'rejecting invalid regex'
diff --git a/spec/requests/api/helm_packages_spec.rb b/spec/requests/api/helm_packages_spec.rb
index 5212e225351..6bd81f64913 100644
--- a/spec/requests/api/helm_packages_spec.rb
+++ b/spec/requests/api/helm_packages_spec.rb
@@ -73,6 +73,17 @@ RSpec.describe API::HelmPackages do
end
end
+ context 'with access to package registry for everyone' do
+ let(:snowplow_gitlab_standard_context) { { project: project, namespace: project.namespace } }
+
+ before do
+ project.update!(visibility: Gitlab::VisibilityLevel::PRIVATE)
+ project.project_feature.update!(package_registry_access_level: ProjectFeature::PUBLIC)
+ end
+
+ it_behaves_like 'process helm download content request', :anonymous, :success
+ end
+
context 'when an invalid token is passed' do
let(:headers) { basic_auth_header(user.username, 'wrong') }
diff --git a/spec/requests/groups/settings/access_tokens_controller_spec.rb b/spec/requests/groups/settings/access_tokens_controller_spec.rb
index eabdef3c41e..cf728b3935f 100644
--- a/spec/requests/groups/settings/access_tokens_controller_spec.rb
+++ b/spec/requests/groups/settings/access_tokens_controller_spec.rb
@@ -87,4 +87,19 @@ RSpec.describe Groups::Settings::AccessTokensController do
it_behaves_like 'feature unavailable'
it_behaves_like 'PUT resource access tokens available'
end
+
+ describe '#index' do
+ let_it_be(:resource_access_tokens) { create_list(:personal_access_token, 3, user: bot_user) }
+
+ before do
+ get group_settings_access_tokens_path(resource)
+ end
+
+ it 'includes details of the active group access tokens' do
+ active_resource_access_tokens =
+ ::GroupAccessTokenSerializer.new.represent(resource_access_tokens.reverse, group: resource)
+
+ expect(assigns(:active_resource_access_tokens).to_json).to eq(active_resource_access_tokens.to_json)
+ end
+ end
end
diff --git a/spec/requests/projects/settings/access_tokens_controller_spec.rb b/spec/requests/projects/settings/access_tokens_controller_spec.rb
index 780d1b8caef..48114834c65 100644
--- a/spec/requests/projects/settings/access_tokens_controller_spec.rb
+++ b/spec/requests/projects/settings/access_tokens_controller_spec.rb
@@ -88,4 +88,19 @@ RSpec.describe Projects::Settings::AccessTokensController do
it_behaves_like 'feature unavailable'
it_behaves_like 'PUT resource access tokens available'
end
+
+ describe '#index' do
+ let_it_be(:resource_access_tokens) { create_list(:personal_access_token, 3, user: bot_user) }
+
+ before do
+ get project_settings_access_tokens_path(resource)
+ end
+
+ it 'includes details of the active project access tokens' do
+ active_resource_access_tokens =
+ ::ProjectAccessTokenSerializer.new.represent(resource_access_tokens.reverse, project: resource)
+
+ expect(assigns(:active_resource_access_tokens).to_json).to eq(active_resource_access_tokens.to_json)
+ end
+ end
end
diff --git a/spec/requests/users/namespace_callouts_spec.rb b/spec/requests/users/namespace_callouts_spec.rb
deleted file mode 100644
index 5a4e269eefb..00000000000
--- a/spec/requests/users/namespace_callouts_spec.rb
+++ /dev/null
@@ -1,57 +0,0 @@
-# frozen_string_literal: true
-
-require 'spec_helper'
-
-RSpec.describe 'Namespace callouts' do
- let_it_be(:user) { create(:user) }
-
- before do
- sign_in(user)
- end
-
- describe 'POST /-/users/namespace_callouts' do
- let(:params) { { feature_name: feature_name, namespace_id: user.namespace.id } }
-
- subject { post namespace_callouts_path, params: params, headers: { 'ACCEPT' => 'application/json' } }
-
- context 'with valid feature name and group' do
- let(:feature_name) { Users::NamespaceCallout.feature_names.each_key.first }
-
- context 'when callout entry does not exist' do
- it 'creates a callout entry with dismissed state' do
- expect { subject }.to change { Users::NamespaceCallout.count }.by(1)
- end
-
- it 'returns success' do
- subject
-
- expect(response).to have_gitlab_http_status(:ok)
- end
- end
-
- context 'when callout entry already exists' do
- let!(:callout) do
- create(:namespace_callout,
- feature_name: Users::GroupCallout.feature_names.each_key.first,
- user: user,
- namespace: user.namespace)
- end
-
- it 'returns success', :aggregate_failures do
- expect { subject }.not_to change { Users::NamespaceCallout.count }
- expect(response).to have_gitlab_http_status(:ok)
- end
- end
- end
-
- context 'with invalid feature name' do
- let(:feature_name) { 'bogus_feature_name' }
-
- it 'returns bad request' do
- subject
-
- expect(response).to have_gitlab_http_status(:bad_request)
- end
- end
- end
-end
diff --git a/spec/serializers/group_access_token_entity_spec.rb b/spec/serializers/group_access_token_entity_spec.rb
index 05609dc3c7a..586eb0a8588 100644
--- a/spec/serializers/group_access_token_entity_spec.rb
+++ b/spec/serializers/group_access_token_entity_spec.rb
@@ -18,7 +18,7 @@ RSpec.describe GroupAccessTokenEntity do
expected_revoke_path = Gitlab::Routing.url_helpers
.revoke_group_settings_access_token_path(
{ id: token,
- group_id: group.path })
+ group_id: group.full_path })
expect(json).to(
include(
@@ -39,7 +39,7 @@ RSpec.describe GroupAccessTokenEntity do
expected_revoke_path = Gitlab::Routing.url_helpers
.revoke_group_settings_access_token_path(
{ id: token,
- group_id: group.path })
+ group_id: group.full_path })
expect(json).to(
include(
diff --git a/spec/serializers/project_access_token_entity_spec.rb b/spec/serializers/project_access_token_entity_spec.rb
index 4b5b4d4d77d..8af09b0a45d 100644
--- a/spec/serializers/project_access_token_entity_spec.rb
+++ b/spec/serializers/project_access_token_entity_spec.rb
@@ -18,7 +18,7 @@ RSpec.describe ProjectAccessTokenEntity do
expected_revoke_path = Gitlab::Routing.url_helpers
.revoke_namespace_project_settings_access_token_path(
{ id: token,
- namespace_id: project.namespace.path,
+ namespace_id: project.namespace.full_path,
project_id: project.path })
expect(json).to(
@@ -42,7 +42,7 @@ RSpec.describe ProjectAccessTokenEntity do
expected_revoke_path = Gitlab::Routing.url_helpers
.revoke_namespace_project_settings_access_token_path(
{ id: token,
- namespace_id: project.namespace.path,
+ namespace_id: project.namespace.full_path,
project_id: project.path })
expect(json).to(
diff --git a/spec/services/namespaces/package_settings/update_service_spec.rb b/spec/services/namespaces/package_settings/update_service_spec.rb
index ed385f1cd7f..10926c5ef57 100644
--- a/spec/services/namespaces/package_settings/update_service_spec.rb
+++ b/spec/services/namespaces/package_settings/update_service_spec.rb
@@ -33,8 +33,29 @@ RSpec.describe ::Namespaces::PackageSettings::UpdateService do
shared_examples 'updating the namespace package setting' do
it_behaves_like 'updating the namespace package setting attributes',
- from: { maven_duplicates_allowed: true, maven_duplicate_exception_regex: 'SNAPSHOT', generic_duplicates_allowed: true, generic_duplicate_exception_regex: 'foo' },
- to: { maven_duplicates_allowed: false, maven_duplicate_exception_regex: 'RELEASE', generic_duplicates_allowed: false, generic_duplicate_exception_regex: 'bar' }
+ from: {
+ maven_duplicates_allowed: true,
+ maven_duplicate_exception_regex: 'SNAPSHOT',
+ generic_duplicates_allowed: true,
+ generic_duplicate_exception_regex: 'foo',
+ maven_package_requests_forwarding: true,
+ lock_maven_package_requests_forwarding: false,
+ npm_package_requests_forwarding: nil,
+ lock_npm_package_requests_forwarding: false,
+ pypi_package_requests_forwarding: nil,
+ lock_pypi_package_requests_forwarding: false
+ }, to: {
+ maven_duplicates_allowed: false,
+ maven_duplicate_exception_regex: 'RELEASE',
+ generic_duplicates_allowed: false,
+ generic_duplicate_exception_regex: 'bar',
+ maven_package_requests_forwarding: true,
+ lock_maven_package_requests_forwarding: true,
+ npm_package_requests_forwarding: true,
+ lock_npm_package_requests_forwarding: true,
+ pypi_package_requests_forwarding: true,
+ lock_pypi_package_requests_forwarding: true
+ }
it_behaves_like 'returning a success'
@@ -63,10 +84,18 @@ RSpec.describe ::Namespaces::PackageSettings::UpdateService do
context 'with existing namespace package setting' do
let_it_be(:package_settings) { create(:namespace_package_setting, namespace: namespace) }
let_it_be(:params) do
- { maven_duplicates_allowed: false,
+ {
+ maven_duplicates_allowed: false,
maven_duplicate_exception_regex: 'RELEASE',
generic_duplicates_allowed: false,
- generic_duplicate_exception_regex: 'bar' }
+ generic_duplicate_exception_regex: 'bar',
+ maven_package_requests_forwarding: true,
+ lock_maven_package_requests_forwarding: true,
+ npm_package_requests_forwarding: true,
+ lock_npm_package_requests_forwarding: true,
+ pypi_package_requests_forwarding: true,
+ lock_pypi_package_requests_forwarding: true
+ }
end
where(:user_role, :shared_examples_name) do
diff --git a/spec/services/users/dismiss_namespace_callout_service_spec.rb b/spec/services/users/dismiss_namespace_callout_service_spec.rb
deleted file mode 100644
index fbcdb66c9e8..00000000000
--- a/spec/services/users/dismiss_namespace_callout_service_spec.rb
+++ /dev/null
@@ -1,24 +0,0 @@
-# frozen_string_literal: true
-
-require 'spec_helper'
-
-RSpec.describe Users::DismissNamespaceCalloutService do
- describe '#execute' do
- let_it_be(:user) { create(:user) }
-
- let(:params) { { feature_name: feature_name, namespace_id: user.namespace.id } }
- let(:feature_name) { Users::NamespaceCallout.feature_names.each_key.first }
-
- subject(:execute) do
- described_class.new(
- container: nil, current_user: user, params: params
- ).execute
- end
-
- it_behaves_like 'dismissing user callout', Users::NamespaceCallout
-
- it 'sets the namespace_id' do
- expect(execute.namespace_id).to eq(user.namespace.id)
- end
- end
-end
diff --git a/spec/support/shared_examples/features/access_tokens_shared_examples.rb b/spec/support/shared_examples/features/access_tokens_shared_examples.rb
index 07e64446ab3..cd255abd7a8 100644
--- a/spec/support/shared_examples/features/access_tokens_shared_examples.rb
+++ b/spec/support/shared_examples/features/access_tokens_shared_examples.rb
@@ -10,11 +10,11 @@ end
RSpec.shared_examples 'resource access tokens creation' do |resource_type|
def active_resource_access_tokens
- find('.table.active-tokens')
+ find("[data-testid='active-tokens']")
end
def created_resource_access_token
- find('#created-personal-access-token').value
+ find_field('new-access-token').value
end
it 'allows creation of an access token', :aggregate_failures do
@@ -106,7 +106,7 @@ end
RSpec.shared_examples 'active resource access tokens' do
def active_resource_access_tokens
- find('.table.active-tokens')
+ find("[data-testid='active-tokens']")
end
it 'shows active access tokens' do
@@ -129,24 +129,22 @@ RSpec.shared_examples 'active resource access tokens' do
end
RSpec.shared_examples 'inactive resource access tokens' do |no_active_tokens_text|
- def no_resource_access_tokens_message
- find('.settings-message')
+ def active_resource_access_tokens
+ find("[data-testid='active-tokens']")
end
it 'allows revocation of an active token' do
visit resource_settings_access_tokens_path
accept_gl_confirm(button_text: 'Revoke') { click_on 'Revoke' }
- expect(page).to have_selector('.settings-message')
- expect(no_resource_access_tokens_message).to have_text(no_active_tokens_text)
+ expect(active_resource_access_tokens).to have_text(no_active_tokens_text)
end
it 'removes expired tokens from active section' do
resource_access_token.update!(expires_at: 5.days.ago)
visit resource_settings_access_tokens_path
- expect(page).to have_selector('.settings-message')
- expect(no_resource_access_tokens_message).to have_text(no_active_tokens_text)
+ expect(active_resource_access_tokens).to have_text(no_active_tokens_text)
end
context 'when resource access token creation is not allowed' do
@@ -158,8 +156,7 @@ RSpec.shared_examples 'inactive resource access tokens' do |no_active_tokens_tex
visit resource_settings_access_tokens_path
accept_gl_confirm(button_text: 'Revoke') { click_on 'Revoke' }
- expect(page).to have_selector('.settings-message')
- expect(no_resource_access_tokens_message).to have_text(no_active_tokens_text)
+ expect(active_resource_access_tokens).to have_text(no_active_tokens_text)
end
end
end
diff --git a/spec/support/shared_examples/lib/gitlab/memory/watchdog/monitor_result_shared_examples.rb b/spec/support/shared_examples/lib/gitlab/memory/watchdog/monitor_result_shared_examples.rb
new file mode 100644
index 00000000000..98c0e7d506b
--- /dev/null
+++ b/spec/support/shared_examples/lib/gitlab/memory/watchdog/monitor_result_shared_examples.rb
@@ -0,0 +1,10 @@
+# frozen_string_literal: true
+
+RSpec.shared_examples 'returns Watchdog Monitor result' do |threshold_violated:|
+ it 'returns if threshold is violated and payload' do
+ result = monitor.call
+
+ expect(result[:threshold_violated]).to eq(threshold_violated)
+ expect(result[:payload]).to eq(payload)
+ end
+end
diff --git a/spec/support/shared_examples/requests/access_tokens_controller_shared_examples.rb b/spec/support/shared_examples/requests/access_tokens_controller_shared_examples.rb
index 017e6274cb0..59e641e2af6 100644
--- a/spec/support/shared_examples/requests/access_tokens_controller_shared_examples.rb
+++ b/spec/support/shared_examples/requests/access_tokens_controller_shared_examples.rb
@@ -2,18 +2,13 @@
RSpec.shared_examples 'GET resource access tokens available' do
let_it_be(:active_resource_access_token) { create(:personal_access_token, user: bot_user) }
- let_it_be(:inactive_resource_access_token) { create(:personal_access_token, :revoked, user: bot_user) }
it 'retrieves active resource access tokens' do
subject
- expect(assigns(:active_resource_access_tokens)).to contain_exactly(active_resource_access_token)
- end
-
- it 'retrieves inactive resource access tokens' do
- subject
-
- expect(assigns(:inactive_resource_access_tokens)).to contain_exactly(inactive_resource_access_token)
+ token_entities = assigns(:active_resource_access_tokens)
+ expect(token_entities.length).to eq(1)
+ expect(token_entities[0][:name]).to eq(active_resource_access_token.name)
end
it 'lists all available scopes' do
@@ -21,15 +16,6 @@ RSpec.shared_examples 'GET resource access tokens available' do
expect(assigns(:scopes)).to eq(Gitlab::Auth.resource_bot_scopes)
end
-
- it 'retrieves newly created personal access token value' do
- token_value = 'random-value'
- allow(PersonalAccessToken).to receive(:redis_getdel).with("#{user.id}:#{resource.id}").and_return(token_value)
-
- subject
-
- expect(assigns(:new_resource_access_token)).to eq(token_value)
- end
end
RSpec.shared_examples 'POST resource access tokens available' do
@@ -37,10 +23,13 @@ RSpec.shared_examples 'POST resource access tokens available' do
PersonalAccessToken.order(:created_at).last
end
- it 'returns success message' do
+ it 'renders JSON with a token' do
subject
- expect(flash[:notice]).to match('Your new access token has been created.')
+ parsed_body = Gitlab::Json.parse(response.body)
+ expect(parsed_body['new_token']).not_to be_blank
+ expect(parsed_body['errors']).to be_blank
+ expect(response).to have_gitlab_http_status(:success)
end
it 'creates resource access token' do
@@ -59,12 +48,6 @@ RSpec.shared_examples 'POST resource access tokens available' do
expect(created_token.user).to be_project_bot
end
- it 'stores newly created token redis store' do
- expect(PersonalAccessToken).to receive(:redis_store!)
-
- subject
- end
-
it { expect { subject }.to change { User.count }.by(1) }
it { expect { subject }.to change { PersonalAccessToken.count }.by(1) }
@@ -87,10 +70,13 @@ RSpec.shared_examples 'POST resource access tokens available' do
expect { subject }.not_to change { User.count }
end
- it 'shows a failure alert' do
+ it 'renders JSON with an error' do
subject
- expect(flash[:alert]).to match("Failed to create new access token: Failed!")
+ parsed_body = Gitlab::Json.parse(response.body)
+ expect(parsed_body['new_token']).to be_blank
+ expect(parsed_body['errors']).to contain_exactly('Failed!')
+ expect(response).to have_gitlab_http_status(:unprocessable_entity)
end
end
end
diff --git a/spec/support/shared_examples/requests/api/helm_packages_shared_examples.rb b/spec/support/shared_examples/requests/api/helm_packages_shared_examples.rb
index 06ed0448b50..8bf6b162508 100644
--- a/spec/support/shared_examples/requests/api/helm_packages_shared_examples.rb
+++ b/spec/support/shared_examples/requests/api/helm_packages_shared_examples.rb
@@ -247,6 +247,15 @@ RSpec.shared_examples 'handling helm chart index requests' do
end
end
+ context 'with access to package registry for everyone' do
+ before do
+ project.update!(visibility: Gitlab::VisibilityLevel::PRIVATE)
+ project.project_feature.update!(package_registry_access_level: ProjectFeature::PUBLIC)
+ end
+
+ it_behaves_like 'process helm service index request', :anonymous, :success
+ end
+
context 'when an invalid token is passed' do
let(:headers) { basic_auth_header(user.username, 'wrong') }
diff --git a/spec/workers/every_sidekiq_worker_spec.rb b/spec/workers/every_sidekiq_worker_spec.rb
index 2f652ac6472..0c2ed26459a 100644
--- a/spec/workers/every_sidekiq_worker_spec.rb
+++ b/spec/workers/every_sidekiq_worker_spec.rb
@@ -260,6 +260,8 @@ RSpec.describe 'Every Sidekiq worker' do
'Gitlab::GithubImport::ImportReleaseAttachmentsWorker' => 5,
'Gitlab::GithubImport::Attachments::ImportReleaseWorker' => 5,
'Gitlab::GithubImport::Attachments::ImportNoteWorker' => 5,
+ 'Gitlab::GithubImport::Attachments::ImportIssueWorker' => 5,
+ 'Gitlab::GithubImport::Attachments::ImportMergeRequestWorker' => 5,
'Gitlab::GithubImport::ImportDiffNoteWorker' => 5,
'Gitlab::GithubImport::ImportIssueWorker' => 5,
'Gitlab::GithubImport::ImportIssueEventWorker' => 5,
diff --git a/spec/workers/gitlab/github_import/attachments/import_issue_worker_spec.rb b/spec/workers/gitlab/github_import/attachments/import_issue_worker_spec.rb
new file mode 100644
index 00000000000..6d617755861
--- /dev/null
+++ b/spec/workers/gitlab/github_import/attachments/import_issue_worker_spec.rb
@@ -0,0 +1,34 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+
+RSpec.describe Gitlab::GithubImport::Attachments::ImportIssueWorker do
+ subject(:worker) { described_class.new }
+
+ describe '#import' do
+ let(:import_state) { create(:import_state, :started) }
+
+ let(:project) do
+ instance_double('Project', full_path: 'foo/bar', id: 1, import_state: import_state)
+ end
+
+ let(:client) { instance_double('Gitlab::GithubImport::Client') }
+
+ it 'imports an issue attachments' do
+ expect_next_instance_of(
+ Gitlab::GithubImport::Importer::NoteAttachmentsImporter,
+ an_instance_of(Gitlab::GithubImport::Representation::NoteText),
+ project,
+ client
+ ) do |note_attachments_importer|
+ expect(note_attachments_importer).to receive(:execute)
+ end
+
+ expect(Gitlab::GithubImport::ObjectCounter)
+ .to receive(:increment)
+ .and_call_original
+
+ worker.import(project, client, {})
+ end
+ end
+end
diff --git a/spec/workers/gitlab/github_import/attachments/import_merge_request_worker_spec.rb b/spec/workers/gitlab/github_import/attachments/import_merge_request_worker_spec.rb
new file mode 100644
index 00000000000..66dfc027e6e
--- /dev/null
+++ b/spec/workers/gitlab/github_import/attachments/import_merge_request_worker_spec.rb
@@ -0,0 +1,34 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+
+RSpec.describe Gitlab::GithubImport::Attachments::ImportMergeRequestWorker do
+ subject(:worker) { described_class.new }
+
+ describe '#import' do
+ let(:import_state) { create(:import_state, :started) }
+
+ let(:project) do
+ instance_double('Project', full_path: 'foo/bar', id: 1, import_state: import_state)
+ end
+
+ let(:client) { instance_double('Gitlab::GithubImport::Client') }
+
+ it 'imports an merge request attachments' do
+ expect_next_instance_of(
+ Gitlab::GithubImport::Importer::NoteAttachmentsImporter,
+ an_instance_of(Gitlab::GithubImport::Representation::NoteText),
+ project,
+ client
+ ) do |note_attachments_importer|
+ expect(note_attachments_importer).to receive(:execute)
+ end
+
+ expect(Gitlab::GithubImport::ObjectCounter)
+ .to receive(:increment)
+ .and_call_original
+
+ worker.import(project, client, {})
+ end
+ end
+end
diff --git a/spec/workers/gitlab/github_import/stage/import_attachments_worker_spec.rb b/spec/workers/gitlab/github_import/stage/import_attachments_worker_spec.rb
index 33e2443d106..ecfece735af 100644
--- a/spec/workers/gitlab/github_import/stage/import_attachments_worker_spec.rb
+++ b/spec/workers/gitlab/github_import/stage/import_attachments_worker_spec.rb
@@ -5,8 +5,7 @@ require 'spec_helper'
RSpec.describe Gitlab::GithubImport::Stage::ImportAttachmentsWorker do
subject(:worker) { described_class.new }
- let(:project) { create(:project) }
- let!(:group) { create(:group, projects: [project]) }
+ let_it_be(:project) { create(:project) }
let(:settings) { ::Gitlab::GithubImport::Settings.new(project) }
let(:stage_enabled) { true }
@@ -15,28 +14,42 @@ RSpec.describe Gitlab::GithubImport::Stage::ImportAttachmentsWorker do
end
describe '#import' do
- let(:releases_importer) { instance_double('Gitlab::GithubImport::Importer::Attachments::ReleasesImporter') }
- let(:notes_importer) { instance_double('Gitlab::GithubImport::Importer::Attachments::NotesImporter') }
let(:client) { instance_double('Gitlab::GithubImport::Client') }
- let(:releases_waiter) { Gitlab::JobWaiter.new(2, '123') }
- let(:notes_waiter) { Gitlab::JobWaiter.new(3, '234') }
-
- it 'imports release attachments' do
- expect(Gitlab::GithubImport::Importer::Attachments::ReleasesImporter)
- .to receive(:new)
- .with(project, client)
- .and_return(releases_importer)
- expect(releases_importer).to receive(:execute).and_return(releases_waiter)
-
- expect(Gitlab::GithubImport::Importer::Attachments::NotesImporter)
- .to receive(:new)
- .with(project, client)
- .and_return(notes_importer)
- expect(notes_importer).to receive(:execute).and_return(notes_waiter)
+ let(:importers) do
+ [
+ {
+ klass: Gitlab::GithubImport::Importer::Attachments::ReleasesImporter,
+ double: instance_double('Gitlab::GithubImport::Importer::Attachments::ReleasesImporter'),
+ waiter: Gitlab::JobWaiter.new(2, '123')
+ },
+ {
+ klass: Gitlab::GithubImport::Importer::Attachments::NotesImporter,
+ double: instance_double('Gitlab::GithubImport::Importer::Attachments::NotesImporter'),
+ waiter: Gitlab::JobWaiter.new(3, '234')
+ },
+ {
+ klass: Gitlab::GithubImport::Importer::Attachments::IssuesImporter,
+ double: instance_double('Gitlab::GithubImport::Importer::Attachments::IssuesImporter'),
+ waiter: Gitlab::JobWaiter.new(4, '345')
+ },
+ {
+ klass: Gitlab::GithubImport::Importer::Attachments::MergeRequestsImporter,
+ double: instance_double('Gitlab::GithubImport::Importer::Attachments::MergeRequestsImporter'),
+ waiter: Gitlab::JobWaiter.new(5, '456')
+ }
+ ]
+ end
+
+ it 'imports attachments' do
+ importers.each do |importer|
+ expect_next_instance_of(importer[:klass], project, client) do |instance|
+ expect(instance).to receive(:execute).and_return(importer[:waiter])
+ end
+ end
expect(Gitlab::GithubImport::AdvanceStageWorker)
.to receive(:perform_async)
- .with(project.id, { '123' => 2, '234' => 3 }, :protected_branches)
+ .with(project.id, { '123' => 2, '234' => 3, '345' => 4, '456' => 5 }, :protected_branches)
worker.import(client, project)
end
@@ -45,8 +58,7 @@ RSpec.describe Gitlab::GithubImport::Stage::ImportAttachmentsWorker do
let(:stage_enabled) { false }
it 'skips release attachments import and calls next stage' do
- expect(Gitlab::GithubImport::Importer::Attachments::ReleasesImporter).not_to receive(:new)
- expect(Gitlab::GithubImport::Importer::Attachments::NotesImporter).not_to receive(:new)
+ importers.each { |importer| expect(importer[:klass]).not_to receive(:new) }
expect(Gitlab::GithubImport::AdvanceStageWorker)
.to receive(:perform_async).with(project.id, {}, :protected_branches)
diff --git a/spec/workers/pages/invalidate_domain_cache_worker_spec.rb b/spec/workers/pages/invalidate_domain_cache_worker_spec.rb
index 9658aacd093..b9c27c54fa1 100644
--- a/spec/workers/pages/invalidate_domain_cache_worker_spec.rb
+++ b/spec/workers/pages/invalidate_domain_cache_worker_spec.rb
@@ -197,6 +197,36 @@ RSpec.describe Pages::InvalidateDomainCacheWorker do
end
end
+ context 'when project features change' do
+ it_behaves_like 'clears caches with',
+ event_class: Projects::ProjectFeaturesChangedEvent,
+ event_data: {
+ project_id: 1,
+ namespace_id: 2,
+ root_namespace_id: 3,
+ features: ["pages_access_level"]
+ },
+ caches: [
+ { type: :project, id: 1 },
+ { type: :namespace, id: 3 }
+ ]
+
+ it 'does not clear the cache when the features is not pages related' do
+ event = Projects::ProjectFeaturesChangedEvent.new(
+ data: {
+ project_id: 1,
+ namespace_id: 2,
+ root_namespace_id: 3,
+ features: ['unknown']
+ }
+ )
+
+ expect(described_class).not_to receive(:clear_cache)
+
+ ::Gitlab::EventStore.publish(event)
+ end
+ end
+
context 'when namespace based cache keys are duplicated' do
# de-dups namespace cache keys
it_behaves_like 'clears caches with',