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

gitlab.com/gitlab-org/gitlab-foss.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
path: root/spec
diff options
context:
space:
mode:
authorGitLab Bot <gitlab-bot@gitlab.com>2023-09-21 15:07:54 +0300
committerGitLab Bot <gitlab-bot@gitlab.com>2023-09-21 15:07:54 +0300
commitda5028d723994eae4e66b0893ebf73e0a602955e (patch)
treefa21e80c039566630c584692da906921da718b20 /spec
parent7e29b8f3fcc32a6adcf735a5a9069de5d9d814d6 (diff)
Add latest changes from gitlab-org/gitlab@master
Diffstat (limited to 'spec')
-rw-r--r--spec/factories/vs_code_settings.rb10
-rw-r--r--spec/finders/vs_code/settings_finder_spec.rb64
-rw-r--r--spec/frontend/import_entities/import_groups/components/import_table_spec.js22
-rw-r--r--spec/frontend/pages/import/bulk_imports/history/components/bulk_imports_history_app_spec.js19
-rw-r--r--spec/frontend/releases/stores/modules/detail/actions_spec.js442
-rw-r--r--spec/frontend/releases/stores/modules/detail/getters_spec.js16
-rw-r--r--spec/lib/gitlab/ci/config/header/input_spec.rb6
-rw-r--r--spec/lib/gitlab/git_audit_event_spec.rb79
-rw-r--r--spec/lib/gitlab/prometheus/queries/deployment_query_spec.rb39
-rw-r--r--spec/models/concerns/prometheus_adapter_spec.rb96
-rw-r--r--spec/models/environment_spec.rb36
-rw-r--r--spec/models/vs_code/vs_code_setting_spec.rb29
-rw-r--r--spec/requests/api/vs_code_settings_sync_spec.rb135
-rw-r--r--spec/services/ci/create_pipeline_service_spec.rb26
-rw-r--r--spec/services/vs_code/settings/create_or_update_service_spec.rb39
-rw-r--r--spec/support/shared_examples/namespaces/traversal_scope_examples.rb19
16 files changed, 774 insertions, 303 deletions
diff --git a/spec/factories/vs_code_settings.rb b/spec/factories/vs_code_settings.rb
new file mode 100644
index 00000000000..ac4bf3e61bd
--- /dev/null
+++ b/spec/factories/vs_code_settings.rb
@@ -0,0 +1,10 @@
+# frozen_string_literal: true
+
+FactoryBot.define do
+ factory :vscode_setting, class: 'VsCode::VsCodeSetting' do
+ user
+
+ setting_type { 'settings' }
+ content { '{}' }
+ end
+end
diff --git a/spec/finders/vs_code/settings_finder_spec.rb b/spec/finders/vs_code/settings_finder_spec.rb
new file mode 100644
index 00000000000..4b8e9d00f30
--- /dev/null
+++ b/spec/finders/vs_code/settings_finder_spec.rb
@@ -0,0 +1,64 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+
+RSpec.describe VsCode::SettingsFinder, feature_category: :web_ide do
+ let_it_be(:user) { create(:user) }
+
+ describe '#execute' do
+ context 'when nil is passed in as the list of settings' do
+ let(:finder) { described_class.new(user, nil) }
+
+ subject { finder.execute }
+
+ context 'when user has no settings' do
+ it 'returns an empty array' do
+ expect(subject).to eq([])
+ end
+ end
+
+ context 'when user has settings' do
+ before do
+ create(:vscode_setting, user: user)
+ end
+
+ it 'returns an array of settings' do
+ expect(subject.length).to eq(1)
+ expect(subject[0].user_id).to eq(user.id)
+ expect(subject[0].setting_type).to eq('settings')
+ end
+ end
+ end
+
+ context 'when a list of settings is passed, filters by the setting' do
+ let_it_be(:setting) { create(:vscode_setting, user: user) }
+
+ context 'when user has no settings with that type' do
+ subject { finder.execute }
+
+ it 'returns an empty array' do
+ finder = described_class.new(user, ['profile'])
+ expect(finder.execute).to eq([])
+ end
+ end
+
+ context 'when user does have settings with the type' do
+ subject { finder.execute }
+
+ it 'returns the record when a single setting exists' do
+ result = described_class.new(user, ['settings']).execute
+ expect(result.length).to eq(1)
+ expect(result[0].user_id).to eq(user.id)
+ expect(result[0].setting_type).to eq('settings')
+ end
+
+ it 'returns multiple records when more than one setting exists' do
+ create(:vscode_setting, user: user, setting_type: 'profile')
+
+ result = described_class.new(user, %w[settings profile]).execute
+ expect(result.length).to eq(2)
+ end
+ end
+ end
+ end
+end
diff --git a/spec/frontend/import_entities/import_groups/components/import_table_spec.js b/spec/frontend/import_entities/import_groups/components/import_table_spec.js
index 03d0920994c..773fdc093c7 100644
--- a/spec/frontend/import_entities/import_groups/components/import_table_spec.js
+++ b/spec/frontend/import_entities/import_groups/components/import_table_spec.js
@@ -345,6 +345,28 @@ describe('import table', () => {
);
});
+ it('resets page to 1 when page size is changed', async () => {
+ wrapper.findComponent(PaginationBar).vm.$emit('set-page', 2);
+ await waitForPromises();
+
+ expect(bulkImportSourceGroupsQueryMock).toHaveBeenCalledWith(
+ expect.anything(),
+ expect.objectContaining({ page: 2, perPage: 50 }),
+ expect.anything(),
+ expect.anything(),
+ );
+
+ wrapper.findComponent(PaginationBar).vm.$emit('set-page-size', 200);
+ await waitForPromises();
+
+ expect(bulkImportSourceGroupsQueryMock).toHaveBeenCalledWith(
+ expect.anything(),
+ expect.objectContaining({ page: 1, perPage: 200 }),
+ expect.anything(),
+ expect.anything(),
+ );
+ });
+
it('updates status text when page is changed', async () => {
const REQUESTED_PAGE = 2;
bulkImportSourceGroupsQueryMock.mockResolvedValue({
diff --git a/spec/frontend/pages/import/bulk_imports/history/components/bulk_imports_history_app_spec.js b/spec/frontend/pages/import/bulk_imports/history/components/bulk_imports_history_app_spec.js
index 0037934cbc5..7d65dddc3f9 100644
--- a/spec/frontend/pages/import/bulk_imports/history/components/bulk_imports_history_app_spec.js
+++ b/spec/frontend/pages/import/bulk_imports/history/components/bulk_imports_history_app_spec.js
@@ -141,6 +141,25 @@ describe('BulkImportsHistoryApp', () => {
);
});
+ it('resets page to 1 when page size is changed', async () => {
+ const NEW_PAGE_SIZE = 4;
+
+ mock.onGet(API_URL).reply(200, DUMMY_RESPONSE, DEFAULT_HEADERS);
+ createComponent();
+ await axios.waitForAll();
+ wrapper.findComponent(PaginationBar).vm.$emit('set-page', 2);
+ await axios.waitForAll();
+ mock.resetHistory();
+
+ wrapper.findComponent(PaginationBar).vm.$emit('set-page-size', NEW_PAGE_SIZE);
+ await axios.waitForAll();
+
+ expect(mock.history.get.length).toBe(1);
+ expect(mock.history.get[0].params).toStrictEqual(
+ expect.objectContaining({ per_page: NEW_PAGE_SIZE, page: 1 }),
+ );
+ });
+
it('sets up the local storage sync correctly', async () => {
const NEW_PAGE_SIZE = 4;
diff --git a/spec/frontend/releases/stores/modules/detail/actions_spec.js b/spec/frontend/releases/stores/modules/detail/actions_spec.js
index 1d164b9f5c1..d18437ccec3 100644
--- a/spec/frontend/releases/stores/modules/detail/actions_spec.js
+++ b/spec/frontend/releases/stores/modules/detail/actions_spec.js
@@ -1,9 +1,11 @@
import { cloneDeep } from 'lodash';
import originalOneReleaseForEditingQueryResponse from 'test_fixtures/graphql/releases/graphql/queries/one_release_for_editing.query.graphql.json';
import testAction from 'helpers/vuex_action_helper';
+import { useLocalStorageSpy } from 'helpers/local_storage_helper';
import { getTag } from '~/api/tags_api';
import { createAlert } from '~/alert';
import { redirectTo } from '~/lib/utils/url_utility'; // eslint-disable-line import/no-deprecated
+import AccessorUtilities from '~/lib/utils/accessor';
import { s__ } from '~/locale';
import { ASSET_LINK_TYPE } from '~/releases/constants';
import createReleaseAssetLinkMutation from '~/releases/graphql/mutations/create_release_link.mutation.graphql';
@@ -20,6 +22,7 @@ jest.mock('~/api/tags_api');
jest.mock('~/alert');
+jest.mock('~/lib/utils/accessor');
jest.mock('~/lib/utils/url_utility', () => ({
redirectTo: jest.fn(),
joinPaths: jest.requireActual('~/lib/utils/url_utility').joinPaths,
@@ -34,78 +37,203 @@ jest.mock('~/releases/util', () => ({
}));
describe('Release edit/new actions', () => {
+ useLocalStorageSpy();
+
let state;
let releaseResponse;
let error;
const projectPath = 'test/project-path';
+ const draftActions = [{ type: 'saveDraftRelease' }, { type: 'saveDraftCreateFrom' }];
const setupState = (updates = {}) => {
state = {
...createState({
projectPath,
projectId: '18',
- isExistingRelease: true,
+ isExistingRelease: false,
tagName: releaseResponse.tag_name,
releasesPagePath: 'path/to/releases/page',
markdownDocsPath: 'path/to/markdown/docs',
markdownPreviewPath: 'path/to/markdown/preview',
}),
+ localStorageKey: `${projectPath}/release/new`,
+ localStorageCreateFromKey: `${projectPath}/release/new/createFrom`,
...updates,
};
};
beforeEach(() => {
+ AccessorUtilities.canUseLocalStorage.mockReturnValue(true);
releaseResponse = cloneDeep(originalOneReleaseForEditingQueryResponse);
gon.api_version = 'v4';
error = new Error('Yikes!');
createAlert.mockClear();
});
+ afterEach(() => {
+ jest.clearAllMocks();
+ });
+
describe('when creating a new release', () => {
beforeEach(() => {
setupState({ isExistingRelease: false });
});
describe('initializeRelease', () => {
- it(`commits ${types.INITIALIZE_EMPTY_RELEASE}`, () => {
- testAction(actions.initializeRelease, undefined, state, [
- { type: types.INITIALIZE_EMPTY_RELEASE },
- ]);
+ it('dispatches loadDraftRelease', () => {
+ return testAction({
+ action: actions.initializeRelease,
+ state,
+ expectedMutations: [],
+ expectedActions: [{ type: 'loadDraftRelease' }],
+ });
+ });
+ });
+
+ describe('loadDraftRelease', () => {
+ it(`with no saved release, it commits ${types.INITIALIZE_EMPTY_RELEASE}`, () => {
+ testAction({
+ action: actions.loadDraftRelease,
+ state,
+ expectedMutations: [{ type: types.INITIALIZE_EMPTY_RELEASE }],
+ });
+ });
+
+ it('with saved release, loads the release from local storage', () => {
+ const release = {
+ tagName: 'v1.3',
+ tagMessage: 'hello',
+ name: '',
+ description: '',
+ milestones: [],
+ groupMilestones: [],
+ releasedAt: new Date(),
+ assets: {
+ links: [],
+ },
+ };
+ const createFrom = 'main';
+
+ window.localStorage.setItem(`${state.projectPath}/release/new`, JSON.stringify(release));
+ window.localStorage.setItem(
+ `${state.projectPath}/release/new/createFrom`,
+ JSON.stringify(createFrom),
+ );
+
+ return testAction({
+ action: actions.loadDraftRelease,
+ state,
+ expectedMutations: [
+ { type: types.INITIALIZE_RELEASE, payload: release },
+ { type: types.UPDATE_CREATE_FROM, payload: createFrom },
+ ],
+ });
+ });
+ });
+
+ describe('clearDraftRelease', () => {
+ it('calls window.localStorage.clear', () => {
+ return testAction({ action: actions.clearDraftRelease, state }).then(() => {
+ expect(window.localStorage.removeItem).toHaveBeenCalledTimes(2);
+ expect(window.localStorage.removeItem).toHaveBeenCalledWith(state.localStorageKey);
+ expect(window.localStorage.removeItem).toHaveBeenCalledWith(
+ state.localStorageCreateFromKey,
+ );
+ });
+ });
+ });
+
+ describe('saveDraftCreateFrom', () => {
+ it('saves the create from to local storage', () => {
+ const createFrom = 'main';
+ setupState({ createFrom });
+ return testAction({ action: actions.saveDraftCreateFrom, state }).then(() => {
+ expect(window.localStorage.setItem).toHaveBeenCalledTimes(1);
+ expect(window.localStorage.setItem).toHaveBeenCalledWith(
+ state.localStorageCreateFromKey,
+ JSON.stringify(createFrom),
+ );
+ });
+ });
+ });
+
+ describe('saveDraftRelease', () => {
+ let release;
+
+ beforeEach(() => {
+ release = {
+ tagName: 'v1.3',
+ tagMessage: 'hello',
+ name: '',
+ description: '',
+ milestones: [],
+ groupMilestones: [],
+ releasedAt: new Date(),
+ assets: {
+ links: [],
+ },
+ };
+ });
+
+ it('saves the draft release to local storage', () => {
+ setupState({ release, releasedAtChanged: true });
+
+ return testAction({ action: actions.saveDraftRelease, state }).then(() => {
+ expect(window.localStorage.setItem).toHaveBeenCalledTimes(1);
+ expect(window.localStorage.setItem).toHaveBeenCalledWith(
+ state.localStorageKey,
+ JSON.stringify(state.release),
+ );
+ });
+ });
+
+ it('ignores the released at date if it has not been changed', () => {
+ setupState({ release, releasedAtChanged: false });
+
+ return testAction({ action: actions.saveDraftRelease, state }).then(() => {
+ expect(window.localStorage.setItem).toHaveBeenCalledTimes(1);
+ expect(window.localStorage.setItem).toHaveBeenCalledWith(
+ state.localStorageKey,
+ JSON.stringify({ ...state.release, releasedAt: undefined }),
+ );
+ });
});
});
describe('saveRelease', () => {
it(`commits ${types.REQUEST_SAVE_RELEASE} and then dispatched "createRelease"`, () => {
- testAction(
- actions.saveRelease,
- undefined,
+ testAction({
+ action: actions.saveRelease,
state,
- [{ type: types.REQUEST_SAVE_RELEASE }],
- [{ type: 'createRelease' }],
- );
+ expectedMutations: [{ type: types.REQUEST_SAVE_RELEASE }],
+ expectedActions: [{ type: 'createRelease' }],
+ });
});
});
});
describe('when editing an existing release', () => {
- beforeEach(setupState);
+ beforeEach(() => setupState({ isExistingRelease: true }));
describe('initializeRelease', () => {
it('dispatches "fetchRelease"', () => {
- testAction(actions.initializeRelease, undefined, state, [], [{ type: 'fetchRelease' }]);
+ testAction({
+ action: actions.initializeRelease,
+ state,
+ expectedActions: [{ type: 'fetchRelease' }],
+ });
});
});
describe('saveRelease', () => {
it(`commits ${types.REQUEST_SAVE_RELEASE} and then dispatched "updateRelease"`, () => {
- testAction(
- actions.saveRelease,
- undefined,
+ testAction({
+ action: actions.saveRelease,
state,
- [{ type: types.REQUEST_SAVE_RELEASE }],
- [{ type: 'updateRelease' }],
- );
+ expectedMutations: [{ type: types.REQUEST_SAVE_RELEASE }],
+ expectedActions: [{ type: 'updateRelease' }],
+ });
});
});
});
@@ -120,15 +248,19 @@ describe('Release edit/new actions', () => {
});
it(`commits ${types.REQUEST_RELEASE} and then commits ${types.RECEIVE_RELEASE_SUCCESS} with the converted release object`, () => {
- return testAction(actions.fetchRelease, undefined, state, [
- {
- type: types.REQUEST_RELEASE,
- },
- {
- type: types.RECEIVE_RELEASE_SUCCESS,
- payload: convertOneReleaseGraphQLResponse(releaseResponse).data,
- },
- ]);
+ return testAction({
+ action: actions.fetchRelease,
+ state,
+ expectedMutations: [
+ {
+ type: types.REQUEST_RELEASE,
+ },
+ {
+ type: types.RECEIVE_RELEASE_SUCCESS,
+ payload: convertOneReleaseGraphQLResponse(releaseResponse).data,
+ },
+ ],
+ });
});
});
@@ -138,15 +270,19 @@ describe('Release edit/new actions', () => {
});
it(`commits ${types.REQUEST_RELEASE} and then commits ${types.RECEIVE_RELEASE_ERROR} with an error object`, () => {
- return testAction(actions.fetchRelease, undefined, state, [
- {
- type: types.REQUEST_RELEASE,
- },
- {
- type: types.RECEIVE_RELEASE_ERROR,
- payload: expect.any(Error),
- },
- ]);
+ return testAction({
+ action: actions.fetchRelease,
+ state,
+ expectedMutations: [
+ {
+ type: types.REQUEST_RELEASE,
+ },
+ {
+ type: types.RECEIVE_RELEASE_ERROR,
+ payload: expect.any(Error),
+ },
+ ],
+ });
});
it(`shows an alert message`, () => {
@@ -163,89 +299,140 @@ describe('Release edit/new actions', () => {
describe('updateReleaseTagName', () => {
it(`commits ${types.UPDATE_RELEASE_TAG_NAME} with the updated tag name`, () => {
const newTag = 'updated-tag-name';
- return testAction(actions.updateReleaseTagName, newTag, state, [
- { type: types.UPDATE_RELEASE_TAG_NAME, payload: newTag },
- ]);
+ return testAction({
+ action: actions.updateReleaseTagName,
+ payload: newTag,
+ state,
+ expectedMutations: [{ type: types.UPDATE_RELEASE_TAG_NAME, payload: newTag }],
+ expectedActions: draftActions,
+ });
+ });
+ it('does not save drafts when editing', () => {
+ const newTag = 'updated-tag-name';
+ return testAction({
+ action: actions.updateReleaseTagName,
+ payload: newTag,
+ state: { ...state, isExistingRelease: true },
+ expectedMutations: [{ type: types.UPDATE_RELEASE_TAG_NAME, payload: newTag }],
+ });
});
});
describe('updateReleaseTagMessage', () => {
it(`commits ${types.UPDATE_RELEASE_TAG_MESSAGE} with the updated tag name`, () => {
const newMessage = 'updated-tag-message';
- return testAction(actions.updateReleaseTagMessage, newMessage, state, [
- { type: types.UPDATE_RELEASE_TAG_MESSAGE, payload: newMessage },
- ]);
+ return testAction({
+ action: actions.updateReleaseTagMessage,
+ payload: newMessage,
+ state,
+ expectedMutations: [{ type: types.UPDATE_RELEASE_TAG_MESSAGE, payload: newMessage }],
+ expectedActions: draftActions,
+ });
});
});
describe('updateReleasedAt', () => {
it(`commits ${types.UPDATE_RELEASED_AT} with the updated date`, () => {
const newDate = new Date();
- return testAction(actions.updateReleasedAt, newDate, state, [
- { type: types.UPDATE_RELEASED_AT, payload: newDate },
- ]);
+ return testAction({
+ action: actions.updateReleasedAt,
+ payload: newDate,
+ state,
+ expectedMutations: [{ type: types.UPDATE_RELEASED_AT, payload: newDate }],
+ expectedActions: draftActions,
+ });
});
});
describe('updateCreateFrom', () => {
it(`commits ${types.UPDATE_CREATE_FROM} with the updated ref`, () => {
const newRef = 'my-feature-branch';
- return testAction(actions.updateCreateFrom, newRef, state, [
- { type: types.UPDATE_CREATE_FROM, payload: newRef },
- ]);
+ return testAction({
+ action: actions.updateCreateFrom,
+ payload: newRef,
+ state,
+ expectedMutations: [{ type: types.UPDATE_CREATE_FROM, payload: newRef }],
+ expectedActions: draftActions,
+ });
});
});
describe('updateShowCreateFrom', () => {
it(`commits ${types.UPDATE_SHOW_CREATE_FROM} with the updated ref`, () => {
const newRef = 'my-feature-branch';
- return testAction(actions.updateShowCreateFrom, newRef, state, [
- { type: types.UPDATE_SHOW_CREATE_FROM, payload: newRef },
- ]);
+ return testAction({
+ action: actions.updateShowCreateFrom,
+ payload: newRef,
+ state,
+ expectedMutations: [{ type: types.UPDATE_SHOW_CREATE_FROM, payload: newRef }],
+ });
});
});
describe('updateReleaseTitle', () => {
it(`commits ${types.UPDATE_RELEASE_TITLE} with the updated release title`, () => {
const newTitle = 'The new release title';
- return testAction(actions.updateReleaseTitle, newTitle, state, [
- { type: types.UPDATE_RELEASE_TITLE, payload: newTitle },
- ]);
+ return testAction({
+ action: actions.updateReleaseTitle,
+ payload: newTitle,
+ state,
+ expectedMutations: [{ type: types.UPDATE_RELEASE_TITLE, payload: newTitle }],
+ expectedActions: draftActions,
+ });
});
});
describe('updateReleaseNotes', () => {
it(`commits ${types.UPDATE_RELEASE_NOTES} with the updated release notes`, () => {
const newReleaseNotes = 'The new release notes';
- return testAction(actions.updateReleaseNotes, newReleaseNotes, state, [
- { type: types.UPDATE_RELEASE_NOTES, payload: newReleaseNotes },
- ]);
+ return testAction({
+ action: actions.updateReleaseNotes,
+ payload: newReleaseNotes,
+ state,
+ expectedMutations: [{ type: types.UPDATE_RELEASE_NOTES, payload: newReleaseNotes }],
+ expectedActions: draftActions,
+ });
});
});
describe('updateReleaseMilestones', () => {
it(`commits ${types.UPDATE_RELEASE_MILESTONES} with the updated release milestones`, () => {
const newReleaseMilestones = ['v0.0', 'v0.1'];
- return testAction(actions.updateReleaseMilestones, newReleaseMilestones, state, [
- { type: types.UPDATE_RELEASE_MILESTONES, payload: newReleaseMilestones },
- ]);
+ return testAction({
+ action: actions.updateReleaseMilestones,
+ payload: newReleaseMilestones,
+ state,
+ expectedMutations: [
+ { type: types.UPDATE_RELEASE_MILESTONES, payload: newReleaseMilestones },
+ ],
+ expectedActions: draftActions,
+ });
});
});
describe('updateReleaseGroupMilestones', () => {
it(`commits ${types.UPDATE_RELEASE_GROUP_MILESTONES} with the updated release group milestones`, () => {
const newReleaseGroupMilestones = ['v0.0', 'v0.1'];
- return testAction(actions.updateReleaseGroupMilestones, newReleaseGroupMilestones, state, [
- { type: types.UPDATE_RELEASE_GROUP_MILESTONES, payload: newReleaseGroupMilestones },
- ]);
+ return testAction({
+ action: actions.updateReleaseGroupMilestones,
+ payload: newReleaseGroupMilestones,
+ state,
+ expectedMutations: [
+ { type: types.UPDATE_RELEASE_GROUP_MILESTONES, payload: newReleaseGroupMilestones },
+ ],
+ expectedActions: draftActions,
+ });
});
});
describe('addEmptyAssetLink', () => {
it(`commits ${types.ADD_EMPTY_ASSET_LINK}`, () => {
- return testAction(actions.addEmptyAssetLink, undefined, state, [
- { type: types.ADD_EMPTY_ASSET_LINK },
- ]);
+ return testAction({
+ action: actions.addEmptyAssetLink,
+ state,
+ expectedMutations: [{ type: types.ADD_EMPTY_ASSET_LINK }],
+ expectedActions: draftActions,
+ });
});
});
@@ -256,9 +443,13 @@ describe('Release edit/new actions', () => {
newUrl: 'https://example.com/updated',
};
- return testAction(actions.updateAssetLinkUrl, params, state, [
- { type: types.UPDATE_ASSET_LINK_URL, payload: params },
- ]);
+ return testAction({
+ action: actions.updateAssetLinkUrl,
+ payload: params,
+ state,
+ expectedMutations: [{ type: types.UPDATE_ASSET_LINK_URL, payload: params }],
+ expectedActions: draftActions,
+ });
});
});
@@ -269,9 +460,13 @@ describe('Release edit/new actions', () => {
newName: 'Updated link name',
};
- return testAction(actions.updateAssetLinkName, params, state, [
- { type: types.UPDATE_ASSET_LINK_NAME, payload: params },
- ]);
+ return testAction({
+ action: actions.updateAssetLinkName,
+ payload: params,
+ state,
+ expectedMutations: [{ type: types.UPDATE_ASSET_LINK_NAME, payload: params }],
+ expectedActions: draftActions,
+ });
});
});
@@ -282,30 +477,45 @@ describe('Release edit/new actions', () => {
newType: ASSET_LINK_TYPE.RUNBOOK,
};
- return testAction(actions.updateAssetLinkType, params, state, [
- { type: types.UPDATE_ASSET_LINK_TYPE, payload: params },
- ]);
+ return testAction({
+ action: actions.updateAssetLinkType,
+ payload: params,
+ state,
+ expectedMutations: [{ type: types.UPDATE_ASSET_LINK_TYPE, payload: params }],
+ expectedActions: draftActions,
+ });
});
});
describe('removeAssetLink', () => {
it(`commits ${types.REMOVE_ASSET_LINK} with the ID of the asset link to remove`, () => {
const idToRemove = 2;
- return testAction(actions.removeAssetLink, idToRemove, state, [
- { type: types.REMOVE_ASSET_LINK, payload: idToRemove },
- ]);
+ return testAction({
+ action: actions.removeAssetLink,
+ payload: idToRemove,
+ state,
+ expectedMutations: [{ type: types.REMOVE_ASSET_LINK, payload: idToRemove }],
+ expectedActions: draftActions,
+ });
});
});
describe('receiveSaveReleaseSuccess', () => {
- it(`commits ${types.RECEIVE_SAVE_RELEASE_SUCCESS}`, () =>
- testAction(actions.receiveSaveReleaseSuccess, releaseResponse, state, [
- { type: types.RECEIVE_SAVE_RELEASE_SUCCESS },
- ]));
+ it(`commits ${types.RECEIVE_SAVE_RELEASE_SUCCESS} and dispatches clearDraftRelease`, () =>
+ testAction({
+ action: actions.receiveSaveReleaseSuccess,
+ payload: releaseResponse,
+ state,
+ expectedMutations: [{ type: types.RECEIVE_SAVE_RELEASE_SUCCESS }],
+ expectedActions: [{ type: 'clearDraftRelease' }],
+ }));
it("redirects to the release's dedicated page", () => {
const { selfUrl } = releaseResponse.data.project.release.links;
- actions.receiveSaveReleaseSuccess({ commit: jest.fn(), state }, selfUrl);
+ actions.receiveSaveReleaseSuccess(
+ { commit: jest.fn(), state, dispatch: jest.fn() },
+ selfUrl,
+ );
expect(redirectTo).toHaveBeenCalledTimes(1); // eslint-disable-line import/no-deprecated
expect(redirectTo).toHaveBeenCalledWith(selfUrl); // eslint-disable-line import/no-deprecated
});
@@ -346,18 +556,16 @@ describe('Release edit/new actions', () => {
});
it(`dispatches "receiveSaveReleaseSuccess" with the converted release object`, () => {
- return testAction(
- actions.createRelease,
- undefined,
+ return testAction({
+ action: actions.createRelease,
state,
- [],
- [
+ expectedActions: [
{
type: 'receiveSaveReleaseSuccess',
payload: selfUrl,
},
],
- );
+ });
});
});
@@ -367,12 +575,16 @@ describe('Release edit/new actions', () => {
});
it(`commits ${types.RECEIVE_SAVE_RELEASE_ERROR} with an error object`, () => {
- return testAction(actions.createRelease, undefined, state, [
- {
- type: types.RECEIVE_SAVE_RELEASE_ERROR,
- payload: expect.any(Error),
- },
- ]);
+ return testAction({
+ action: actions.createRelease,
+ state,
+ expectedMutations: [
+ {
+ type: types.RECEIVE_SAVE_RELEASE_ERROR,
+ payload: expect.any(Error),
+ },
+ ],
+ });
});
it(`shows an alert message`, () => {
@@ -393,12 +605,16 @@ describe('Release edit/new actions', () => {
});
it(`commits ${types.RECEIVE_SAVE_RELEASE_ERROR} with an error object`, () => {
- return testAction(actions.createRelease, undefined, state, [
- {
- type: types.RECEIVE_SAVE_RELEASE_ERROR,
- payload: expect.any(Error),
- },
- ]);
+ return testAction({
+ action: actions.createRelease,
+ state,
+ expectedMutations: [
+ {
+ type: types.RECEIVE_SAVE_RELEASE_ERROR,
+ payload: expect.any(Error),
+ },
+ ],
+ });
});
it(`shows an alert message`, () => {
@@ -760,16 +976,15 @@ describe('Release edit/new actions', () => {
const tag = { message: 'this is a tag' };
getTag.mockResolvedValue({ data: tag });
- await testAction(
- actions.fetchTagNotes,
- tagName,
+ await testAction({
+ action: actions.fetchTagNotes,
+ payload: tagName,
state,
- [
+ expectedMutations: [
{ type: types.REQUEST_TAG_NOTES },
{ type: types.RECEIVE_TAG_NOTES_SUCCESS, payload: tag },
],
- [],
- );
+ });
expect(getTag).toHaveBeenCalledWith(state.projectId, tagName);
});
@@ -777,16 +992,15 @@ describe('Release edit/new actions', () => {
error = new Error();
getTag.mockRejectedValue(error);
- await testAction(
- actions.fetchTagNotes,
- tagName,
+ await testAction({
+ action: actions.fetchTagNotes,
+ payload: tagName,
state,
- [
+ expectedMutations: [
{ type: types.REQUEST_TAG_NOTES },
{ type: types.RECEIVE_TAG_NOTES_ERROR, payload: error },
],
- [],
- );
+ });
expect(createAlert).toHaveBeenCalledWith({
message: s__('Release|Unable to fetch the tag notes.'),
diff --git a/spec/frontend/releases/stores/modules/detail/getters_spec.js b/spec/frontend/releases/stores/modules/detail/getters_spec.js
index 736eae13fb3..24490e19296 100644
--- a/spec/frontend/releases/stores/modules/detail/getters_spec.js
+++ b/spec/frontend/releases/stores/modules/detail/getters_spec.js
@@ -470,4 +470,20 @@ describe('Release edit/new getters', () => {
expect(getters.releasedAtChanged({ originalReleasedAt, release: { releasedAt } })).toBe(true);
});
});
+
+ describe('localStorageKey', () => {
+ it('returns a string key with the project path for local storage', () => {
+ const projectPath = 'test/project';
+ expect(getters.localStorageKey({ projectPath })).toBe('test/project/release/new');
+ });
+ });
+
+ describe('localStorageCreateFromKey', () => {
+ it('returns a string key with the project path for local storage', () => {
+ const projectPath = 'test/project';
+ expect(getters.localStorageCreateFromKey({ projectPath })).toBe(
+ 'test/project/release/new/createFrom',
+ );
+ });
+ });
});
diff --git a/spec/lib/gitlab/ci/config/header/input_spec.rb b/spec/lib/gitlab/ci/config/header/input_spec.rb
index b5155dff6e8..b4e02c2b005 100644
--- a/spec/lib/gitlab/ci/config/header/input_spec.rb
+++ b/spec/lib/gitlab/ci/config/header/input_spec.rb
@@ -46,6 +46,12 @@ RSpec.describe Gitlab::Ci::Config::Header::Input, feature_category: :pipeline_co
it_behaves_like 'a valid input'
end
+ context 'when has a description value' do
+ let(:input_hash) { { description: 'bar' } }
+
+ it_behaves_like 'a valid input'
+ end
+
context 'when is a required input' do
let(:input_hash) { nil }
diff --git a/spec/lib/gitlab/git_audit_event_spec.rb b/spec/lib/gitlab/git_audit_event_spec.rb
new file mode 100644
index 00000000000..c533b39f550
--- /dev/null
+++ b/spec/lib/gitlab/git_audit_event_spec.rb
@@ -0,0 +1,79 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+
+RSpec.describe Gitlab::GitAuditEvent, feature_category: :source_code_management do
+ let_it_be(:player) { create(:user) }
+ let_it_be(:group) { create(:group, :public) }
+ let_it_be(:project) { create(:project) }
+
+ subject { described_class.new(player, project) }
+
+ describe '#send_audit_event' do
+ let(:msg) { 'valid_msg' }
+
+ context 'with successfully sending' do
+ let_it_be(:project) { create(:project, namespace: group) }
+
+ before do
+ allow(::Gitlab::Audit::Auditor).to receive(:audit)
+ end
+
+ context 'when player is a regular user' do
+ it 'sends git audit event' do
+ expect(::Gitlab::Audit::Auditor).to receive(:audit).with(a_hash_including(
+ name: 'repository_git_operation',
+ stream_only: true,
+ author: player,
+ scope: project,
+ target: project,
+ message: msg
+ )).once
+
+ subject.send_audit_event(msg)
+ end
+ end
+
+ context 'when player is ::API::Support::GitAccessActor' do
+ let_it_be(:user) { player }
+ let_it_be(:key) { create(:key, user: user) }
+ let_it_be(:git_access_actor) { ::API::Support::GitAccessActor.new(user: user, key: key) }
+
+ subject { described_class.new(git_access_actor, project) }
+
+ it 'sends git audit event' do
+ expect(::Gitlab::Audit::Auditor).to receive(:audit).with(a_hash_including(
+ name: 'repository_git_operation',
+ stream_only: true,
+ author: git_access_actor.deploy_key_or_user,
+ scope: project,
+ target: project,
+ message: msg
+ )).once
+
+ subject.send_audit_event(msg)
+ end
+ end
+ end
+
+ context 'when user is blank' do
+ let_it_be(:player) { nil }
+
+ it 'does not send git audit event' do
+ expect(::Gitlab::Audit::Auditor).not_to receive(:audit)
+
+ subject.send_audit_event(msg)
+ end
+ end
+
+ context 'when project is blank' do
+ let_it_be(:project) { nil }
+
+ it 'does not send git audit event' do
+ expect(::Gitlab::Audit::Auditor).not_to receive(:audit)
+
+ subject.send_audit_event(msg)
+ end
+ end
+ end
+end
diff --git a/spec/lib/gitlab/prometheus/queries/deployment_query_spec.rb b/spec/lib/gitlab/prometheus/queries/deployment_query_spec.rb
deleted file mode 100644
index 66b93d0dd72..00000000000
--- a/spec/lib/gitlab/prometheus/queries/deployment_query_spec.rb
+++ /dev/null
@@ -1,39 +0,0 @@
-# frozen_string_literal: true
-
-require 'spec_helper'
-
-RSpec.describe Gitlab::Prometheus::Queries::DeploymentQuery do
- let(:environment) { create(:environment, slug: 'environment-slug') }
- let(:deployment) { create(:deployment, environment: environment) }
- let(:client) { double('prometheus_client') }
-
- subject { described_class.new(client) }
-
- around do |example|
- time_without_subsecond_values = Time.local(2008, 9, 1, 12, 0, 0)
- travel_to(time_without_subsecond_values) { example.run }
- end
-
- it 'sends appropriate queries to prometheus' do
- start_time = (deployment.created_at - 30.minutes).to_f
- end_time = (deployment.created_at + 30.minutes).to_f
- created_at = deployment.created_at.to_f
-
- expect(client).to receive(:query_range).with('avg(container_memory_usage_bytes{container_name!="POD",environment="environment-slug"}) / 2^20',
- start_time: start_time, end_time: end_time)
- expect(client).to receive(:query).with('avg(avg_over_time(container_memory_usage_bytes{container_name!="POD",environment="environment-slug"}[30m]))',
- time: created_at)
- expect(client).to receive(:query).with('avg(avg_over_time(container_memory_usage_bytes{container_name!="POD",environment="environment-slug"}[30m]))',
- time: end_time)
-
- expect(client).to receive(:query_range).with('avg(rate(container_cpu_usage_seconds_total{container_name!="POD",environment="environment-slug"}[2m])) * 100',
- start_time: start_time, end_time: end_time)
- expect(client).to receive(:query).with('avg(rate(container_cpu_usage_seconds_total{container_name!="POD",environment="environment-slug"}[30m])) * 100',
- time: created_at)
- expect(client).to receive(:query).with('avg(rate(container_cpu_usage_seconds_total{container_name!="POD",environment="environment-slug"}[30m])) * 100',
- time: end_time)
-
- expect(subject.query(deployment.id)).to eq(memory_values: nil, memory_before: nil, memory_after: nil,
- cpu_values: nil, cpu_before: nil, cpu_after: nil)
- end
-end
diff --git a/spec/models/concerns/prometheus_adapter_spec.rb b/spec/models/concerns/prometheus_adapter_spec.rb
index abb89832174..d17059ccc6d 100644
--- a/spec/models/concerns/prometheus_adapter_spec.rb
+++ b/spec/models/concerns/prometheus_adapter_spec.rb
@@ -15,102 +15,6 @@ RSpec.describe PrometheusAdapter, :use_clean_rails_memory_store_caching do
end
end
- let(:environment_query) { Gitlab::Prometheus::Queries::EnvironmentQuery }
-
- describe '#query' do
- describe 'environment' do
- let(:environment) { build_stubbed(:environment, slug: 'env-slug') }
-
- around do |example|
- freeze_time { example.run }
- end
-
- context 'with valid data' do
- subject { integration.query(:environment, environment) }
-
- before do
- stub_reactive_cache(integration, prometheus_data, environment_query, environment.id)
- end
-
- it 'returns reactive data' do
- is_expected.to eq(prometheus_metrics_data)
- end
- end
- end
-
- describe 'deployment' do
- let(:deployment) { build_stubbed(:deployment) }
- let(:deployment_query) { Gitlab::Prometheus::Queries::DeploymentQuery }
-
- around do |example|
- freeze_time { example.run }
- end
-
- context 'with valid data' do
- subject { integration.query(:deployment, deployment) }
-
- before do
- stub_reactive_cache(integration, prometheus_data, deployment_query, deployment.id)
- end
-
- it 'returns reactive data' do
- expect(subject).to eq(prometheus_metrics_data)
- end
- end
- end
- end
-
- describe '#calculate_reactive_cache' do
- let(:environment) { create(:environment, slug: 'env-slug') }
-
- before do
- integration.manual_configuration = true
- integration.active = true
- end
-
- subject do
- integration.calculate_reactive_cache(environment_query.name, environment.id)
- end
-
- around do |example|
- freeze_time { example.run }
- end
-
- context 'when integration is inactive' do
- before do
- integration.active = false
- end
-
- it { is_expected.to be_nil }
- end
-
- context 'when Prometheus responds with valid data' do
- before do
- stub_all_prometheus_requests(environment.slug)
- end
-
- it { expect(subject.to_json).to eq(prometheus_data.to_json) }
- end
-
- [404, 500].each do |status|
- context "when Prometheus responds with #{status}" do
- before do
- stub_all_prometheus_requests(environment.slug, status: status, body: "QUERY FAILED!")
- end
-
- it { is_expected.to eq(success: false, result: %(#{status} - "QUERY FAILED!")) }
- end
- end
-
- context "when client raises Gitlab::PrometheusClient::ConnectionError" do
- before do
- stub_any_prometheus_request.to_raise(Gitlab::PrometheusClient::ConnectionError)
- end
-
- it { is_expected.to include(success: false, result: kind_of(String)) }
- end
- end
-
describe '#build_query_args' do
subject { integration.build_query_args(*args) }
diff --git a/spec/models/environment_spec.rb b/spec/models/environment_spec.rb
index 9d4699cb91e..dcfee7fcc8c 100644
--- a/spec/models/environment_spec.rb
+++ b/spec/models/environment_spec.rb
@@ -1516,42 +1516,6 @@ RSpec.describe Environment, :use_clean_rails_memory_store_caching, feature_categ
end
end
- describe '#metrics' do
- let_it_be(:project) { create(:project, :with_prometheus_integration) }
-
- subject { environment.metrics }
-
- context 'when the environment has metrics' do
- before do
- allow(environment).to receive(:has_metrics?).and_return(true)
- end
-
- it 'returns the metrics from the deployment service' do
- expect(environment.prometheus_adapter)
- .to receive(:query).with(:environment, environment)
- .and_return(:fake_metrics)
-
- is_expected.to eq(:fake_metrics)
- end
-
- context 'and the prometheus client is not present' do
- before do
- allow(environment.prometheus_adapter).to receive(:promethus_client).and_return(nil)
- end
-
- it { is_expected.to be_nil }
- end
- end
-
- context 'when the environment does not have metrics' do
- before do
- allow(environment).to receive(:has_metrics?).and_return(false)
- end
-
- it { is_expected.to be_nil }
- end
- end
-
describe '#additional_metrics' do
let_it_be(:project) { create(:project, :with_prometheus_integration) }
let(:metric_params) { [] }
diff --git a/spec/models/vs_code/vs_code_setting_spec.rb b/spec/models/vs_code/vs_code_setting_spec.rb
new file mode 100644
index 00000000000..aa206c72f29
--- /dev/null
+++ b/spec/models/vs_code/vs_code_setting_spec.rb
@@ -0,0 +1,29 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+
+RSpec.describe VsCode::VsCodeSetting, feature_category: :web_ide do
+ let!(:user) { create(:user) }
+ let!(:setting) { create(:vscode_setting, user: user, setting_type: 'settings') }
+
+ describe 'validates the presence of required attributes' do
+ it { is_expected.to validate_presence_of(:setting_type) }
+ it { is_expected.to validate_presence_of(:content) }
+ end
+
+ describe 'relationship validation' do
+ it { is_expected.to belong_to(:user) }
+ end
+
+ describe '.by_setting_type' do
+ subject { described_class.by_setting_type('settings') }
+
+ it { is_expected.to contain_exactly(setting) }
+ end
+
+ describe '.by_user' do
+ subject { described_class.by_user(user) }
+
+ it { is_expected.to contain_exactly(setting) }
+ end
+end
diff --git a/spec/requests/api/vs_code_settings_sync_spec.rb b/spec/requests/api/vs_code_settings_sync_spec.rb
new file mode 100644
index 00000000000..092b13e8e68
--- /dev/null
+++ b/spec/requests/api/vs_code_settings_sync_spec.rb
@@ -0,0 +1,135 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+
+RSpec.describe API::VsCodeSettingsSync, :aggregate_failures, factory_default: :keep, feature_category: :web_ide do
+ let_it_be(:user) { create_default(:user) }
+ let_it_be(:user_token) { create(:personal_access_token) }
+
+ shared_examples "returns unauthorized when not authenticated" do
+ it 'returns 401 for non-authenticated' do
+ get api(path)
+
+ expect(response).to have_gitlab_http_status(:unauthorized)
+ end
+ end
+
+ shared_examples "returns 200 when authenticated" do
+ it 'returns 200 when authenticated' do
+ get api(path, personal_access_token: user_token)
+ expect(response).to have_gitlab_http_status(:ok)
+ end
+ end
+
+ describe 'GET /vscode/settings_sync/v1/manifest' do
+ let(:path) { "/vscode/settings_sync/v1/manifest" }
+
+ it_behaves_like "returns unauthorized when not authenticated"
+ it_behaves_like "returns 200 when authenticated"
+
+ context 'when no settings record is present' do
+ it 'returns a session id' do
+ get api(path, personal_access_token: user_token)
+ expect(json_response).to have_key('latest')
+ expect(json_response).to have_key('session')
+ end
+
+ it 'returns no latest keys' do
+ get api(path, personal_access_token: user_token)
+ expect(json_response).to have_key('latest')
+ expect(json_response['latest']).not_to have_key('settings')
+ end
+ end
+
+ context 'when settings record is present' do
+ let_it_be(:settings) { create(:vscode_setting) }
+
+ it 'returns the latest keys' do
+ get api(path, personal_access_token: user_token)
+ expect(json_response).to have_key('latest')
+ expect(json_response).to have_key('session')
+ expect(json_response['latest']).to have_key('settings')
+ expect(json_response.dig('latest', 'settings')).to eq settings.id
+ end
+ end
+ end
+
+ describe 'GET /vscode/settings_sync/v1/resource/machines/latest' do
+ let(:path) { "/vscode/settings_sync/v1/resource/machines/latest" }
+
+ it_behaves_like "returns unauthorized when not authenticated"
+ it_behaves_like "returns 200 when authenticated"
+
+ it 'returns a list of machines' do
+ get api(path, personal_access_token: user_token)
+ expect(json_response).to have_key('version')
+ expect(json_response).to have_key('machines')
+ expect(json_response['machines']).to be_an Array
+ expect(json_response['machines'].first).to have_key('id')
+ end
+ end
+
+ describe 'GET /vscode/settings_sync/v1/resource/:resource_name/:id' do
+ let(:path) { "/vscode/settings_sync/v1/resource/settings/1" }
+
+ it_behaves_like "returns 200 when authenticated"
+ it_behaves_like "returns unauthorized when not authenticated"
+
+ context 'when settings with that type are not present' do
+ it 'returns settings with empty json content' do
+ get api(path, personal_access_token: user_token)
+ expect(json_response).to have_key('content')
+ expect(json_response).to have_key('version')
+ expect(json_response['content']).to eq('{}')
+ end
+ end
+
+ context 'when settings with that type are present' do
+ let_it_be(:settings) { create(:vscode_setting, content: '{ "key": "value" }') }
+
+ it 'returns settings with the correct json content' do
+ get api(path, personal_access_token: user_token)
+ expect(json_response).to have_key('content')
+ expect(json_response).to have_key('version')
+ expect(json_response['content']).to eq('{ "key": "value" }')
+ end
+ end
+ end
+
+ describe 'POST /vscode/settings_sync/v1/resource/:resource_name' do
+ let(:path) { "/vscode/settings_sync/v1/resource/settings" }
+
+ subject(:request) do
+ post api(path, personal_access_token: user_token), params: { content: '{ "editor.fontSize": 12 }' }
+ end
+
+ it 'returns unauthorized when not authenticated' do
+ post api(path)
+ expect(response).to have_gitlab_http_status(:unauthorized)
+ end
+
+ it 'returns 201 when a valid request is sent' do
+ request
+
+ expect(response).to have_gitlab_http_status(:created)
+ end
+
+ it 'creates a new record for the setting when the setting is not present' do
+ expect { request }.to change { User.find(user.id).vscode_settings.count }.from(0).to(1)
+ record = User.find(user.id).vscode_settings.by_setting_type('settings').first
+ expect(record.content).to eq('{ "editor.fontSize": 12 }')
+ end
+
+ it 'updates a record if the setting is already present' do
+ create(:vscode_setting)
+ expect { request }.not_to change { User.find(user.id).vscode_settings.count }
+ record = User.find(user.id).vscode_settings.by_setting_type('settings').first
+ expect(record.content).to eq('{ "editor.fontSize": 12 }')
+ end
+
+ it 'fails if required fields not passed' do
+ post api(path, personal_access_token: user_token), params: {}
+ expect(response).to have_gitlab_http_status(:bad_request)
+ end
+ end
+end
diff --git a/spec/services/ci/create_pipeline_service_spec.rb b/spec/services/ci/create_pipeline_service_spec.rb
index a28dd9e7a55..11f9708f9f3 100644
--- a/spec/services/ci/create_pipeline_service_spec.rb
+++ b/spec/services/ci/create_pipeline_service_spec.rb
@@ -1953,6 +1953,32 @@ RSpec.describe Ci::CreatePipelineService, :yaml_processor_feature_flag_corectnes
expect(pipeline.statuses.count).to eq 2
expect(pipeline.statuses.map(&:name)).to match_array %w[test-1 test-my-job]
end
+
+ context 'when inputs have a description' do
+ let(:template) do
+ <<~YAML
+ spec:
+ inputs:
+ stage:
+ suffix:
+ default: my-job
+ description: description
+ ---
+ test-$[[ inputs.suffix ]]:
+ stage: $[[ inputs.stage ]]
+ script: run tests
+ YAML
+ end
+
+ it 'creates a pipeline' do
+ response = execute_service(save_on_errors: true)
+
+ pipeline = response.payload
+
+ expect(pipeline).to be_persisted
+ expect(pipeline.yaml_errors).to be_blank
+ end
+ end
end
context 'when interpolation is invalid' do
diff --git a/spec/services/vs_code/settings/create_or_update_service_spec.rb b/spec/services/vs_code/settings/create_or_update_service_spec.rb
new file mode 100644
index 00000000000..c2749cd01bc
--- /dev/null
+++ b/spec/services/vs_code/settings/create_or_update_service_spec.rb
@@ -0,0 +1,39 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+
+RSpec.describe VsCode::Settings::CreateOrUpdateService, feature_category: :web_ide do
+ describe '#execute' do
+ let_it_be(:user) { create(:user) }
+
+ let(:opts) do
+ {
+ setting_type: "settings",
+ content: '{ "editor.fontSize": 12 }'
+ }
+ end
+
+ subject { described_class.new(current_user: user, params: opts).execute }
+
+ it 'creates a new record when a record with the setting does not exist' do
+ expect { subject }.to change { User.find(user.id).vscode_settings.count }.from(0).to(1)
+ record = User.find(user.id).vscode_settings.by_setting_type('settings').first
+ expect(record.content).to eq('{ "editor.fontSize": 12 }')
+ end
+
+ it 'updates the existing record if setting exists' do
+ setting = create(:vscode_setting, user: user)
+
+ expect { subject }.to change {
+ VsCode::VsCodeSetting.find(setting.id).content
+ }.from(setting.content).to(opts[:content])
+ end
+
+ it 'fails if an invalid value is passed' do
+ invalid_opts = { setting_type: nil, content: nil }
+ result = described_class.new(current_user: user, params: invalid_opts).execute
+
+ expect(result.status).to eq(:error)
+ end
+ end
+end
diff --git a/spec/support/shared_examples/namespaces/traversal_scope_examples.rb b/spec/support/shared_examples/namespaces/traversal_scope_examples.rb
index b308295b5fb..627d1b33489 100644
--- a/spec/support/shared_examples/namespaces/traversal_scope_examples.rb
+++ b/spec/support/shared_examples/namespaces/traversal_scope_examples.rb
@@ -263,7 +263,7 @@ RSpec.shared_examples 'namespace traversal scopes' do
include_examples '.self_and_descendant_ids'
end
- shared_examples '.self_and_hierarchy' do
+ describe '.self_and_hierarchy' do
let(:base_scope) { Group.where(id: base_groups) }
subject { base_scope.self_and_hierarchy }
@@ -292,21 +292,4 @@ RSpec.shared_examples 'namespace traversal scopes' do
it { is_expected.to contain_exactly(group_1, nested_group_1, deep_nested_group_1) }
end
end
-
- describe '.self_and_hierarchy' do
- it_behaves_like '.self_and_hierarchy'
-
- context "use_traversal_ids_for_self_and_hierarchy_scopes feature flag is false" do
- before do
- stub_feature_flags(use_traversal_ids_for_self_and_hierarchy_scopes: false)
- end
-
- it_behaves_like '.self_and_hierarchy'
-
- it 'makes recursive queries' do
- base_groups = Group.where(id: nested_group_1)
- expect { base_groups.self_and_hierarchy.load }.to make_queries_matching(/WITH RECURSIVE/)
- end
- end
- end
end