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>2021-01-18 18:10:42 +0300
committerGitLab Bot <gitlab-bot@gitlab.com>2021-01-18 18:10:42 +0300
commitbfc7eec58ee891178d2a81c424f6c1de22feae5f (patch)
tree7cf9e2f76befc383e99d28d397ecec4bb9e8d2c9 /spec
parentf23a9a17ed6237c346d2e9210c6841e319e8d030 (diff)
Add latest changes from gitlab-org/gitlab@master
Diffstat (limited to 'spec')
-rw-r--r--spec/features/security/dashboard_access_spec.rb2
-rw-r--r--spec/features/security/profile_access_spec.rb12
-rw-r--r--spec/frontend/boards/components/board_card_layout_spec.js69
-rw-r--r--spec/frontend/environments/environment_table_spec.js36
-rw-r--r--spec/frontend/environments/environments_app_spec.js28
-rw-r--r--spec/frontend/environments/environments_folder_view_spec.js2
-rw-r--r--spec/frontend/environments/environments_store_spec.js14
-rw-r--r--spec/frontend/environments/folder/environments_folder_view_spec.js2
-rw-r--r--spec/frontend/jira_connect/api_spec.js34
-rw-r--r--spec/frontend/jira_connect/components/groups_list_item_spec.js46
-rw-r--r--spec/frontend/jira_connect/components/groups_list_spec.js71
-rw-r--r--spec/frontend/jira_connect/mock_data.js15
-rw-r--r--spec/frontend/lib/utils/datetime_utility_spec.js55
-rw-r--r--spec/graphql/types/notes/note_type_spec.rb1
-rw-r--r--spec/helpers/jira_connect_helper_spec.rb15
-rw-r--r--spec/lib/atlassian/jira_connect/client_spec.rb154
-rw-r--r--spec/lib/gitlab/experimentation/controller_concern_spec.rb27
-rw-r--r--spec/lib/gitlab/experimentation/experiment_spec.rb3
-rw-r--r--spec/lib/gitlab/experimentation_spec.rb12
-rw-r--r--spec/lib/gitlab/path_regex_spec.rb6
-rw-r--r--spec/requests/profiles/notifications_controller_spec.rb2
-rw-r--r--spec/routing/notifications_routing_spec.rb8
-rw-r--r--spec/routing/project_routing_spec.rb24
-rw-r--r--spec/routing/routing_spec.rb92
-rw-r--r--spec/services/jira_connect/sync_service_spec.rb4
-rw-r--r--spec/services/projects/update_pages_service_spec.rb13
-rw-r--r--spec/support/shared_examples/routing/legacy_path_redirect_shared_examples.rb2
-rw-r--r--spec/workers/container_expiration_policies/cleanup_container_repository_worker_spec.rb36
28 files changed, 613 insertions, 172 deletions
diff --git a/spec/features/security/dashboard_access_spec.rb b/spec/features/security/dashboard_access_spec.rb
index 5ac4a5c1840..5430329d47d 100644
--- a/spec/features/security/dashboard_access_spec.rb
+++ b/spec/features/security/dashboard_access_spec.rb
@@ -57,7 +57,7 @@ RSpec.describe "Dashboard access" do
it { expect(new_group_path).to be_denied_for :visitor }
end
- describe "GET /profile/groups" do
+ describe "GET /dashboard/groups" do
subject { dashboard_groups_path }
it { is_expected.to be_allowed_for :admin }
diff --git a/spec/features/security/profile_access_spec.rb b/spec/features/security/profile_access_spec.rb
index 3aa8278866c..301efd2d99b 100644
--- a/spec/features/security/profile_access_spec.rb
+++ b/spec/features/security/profile_access_spec.rb
@@ -5,7 +5,7 @@ require 'spec_helper'
RSpec.describe "Profile access" do
include AccessMatchers
- describe "GET /profile/keys" do
+ describe "GET /-/profile/keys" do
subject { profile_keys_path }
it { is_expected.to be_allowed_for :admin }
@@ -13,7 +13,7 @@ RSpec.describe "Profile access" do
it { is_expected.to be_denied_for :visitor }
end
- describe "GET /profile" do
+ describe "GET /-/profile" do
subject { profile_path }
it { is_expected.to be_allowed_for :admin }
@@ -21,7 +21,7 @@ RSpec.describe "Profile access" do
it { is_expected.to be_denied_for :visitor }
end
- describe "GET /profile/account" do
+ describe "GET /-/profile/account" do
subject { profile_account_path }
it { is_expected.to be_allowed_for :admin }
@@ -29,7 +29,7 @@ RSpec.describe "Profile access" do
it { is_expected.to be_denied_for :visitor }
end
- describe "GET /profile/preferences" do
+ describe "GET /-/profile/preferences" do
subject { profile_preferences_path }
it { is_expected.to be_allowed_for :admin }
@@ -37,7 +37,7 @@ RSpec.describe "Profile access" do
it { is_expected.to be_denied_for :visitor }
end
- describe "GET /profile/audit_log" do
+ describe "GET /-/profile/audit_log" do
subject { audit_log_profile_path }
it { is_expected.to be_allowed_for :admin }
@@ -45,7 +45,7 @@ RSpec.describe "Profile access" do
it { is_expected.to be_denied_for :visitor }
end
- describe "GET /profile/notifications" do
+ describe "GET /-/profile/notifications" do
subject { profile_notifications_path }
it { is_expected.to be_allowed_for :admin }
diff --git a/spec/frontend/boards/components/board_card_layout_spec.js b/spec/frontend/boards/components/board_card_layout_spec.js
index d627f971724..d8633871e8d 100644
--- a/spec/frontend/boards/components/board_card_layout_spec.js
+++ b/spec/frontend/boards/components/board_card_layout_spec.js
@@ -1,7 +1,8 @@
/* global List */
/* global ListLabel */
-import { shallowMount } from '@vue/test-utils';
+import Vuex from 'vuex';
+import { createLocalVue, shallowMount } from '@vue/test-utils';
import MockAdapter from 'axios-mock-adapter';
import waitForPromises from 'helpers/wait_for_promises';
@@ -10,20 +11,35 @@ import axios from '~/lib/utils/axios_utils';
import '~/boards/models/label';
import '~/boards/models/assignee';
import '~/boards/models/list';
-import store from '~/boards/stores';
+import boardsVuexStore from '~/boards/stores';
import boardsStore from '~/boards/stores/boards_store';
import BoardCardLayout from '~/boards/components/board_card_layout.vue';
import issueCardInner from '~/boards/components/issue_card_inner.vue';
import { listObj, boardsMockInterceptor, setMockEndpoints } from '../mock_data';
+import { ISSUABLE } from '~/boards/constants';
+
describe('Board card layout', () => {
let wrapper;
let mock;
let list;
+ let store;
+
+ const localVue = createLocalVue();
+ localVue.use(Vuex);
+
+ const createStore = ({ getters = {}, actions = {} } = {}) => {
+ store = new Vuex.Store({
+ ...boardsVuexStore,
+ actions,
+ getters,
+ });
+ };
// this particular mount component needs to be used after the root beforeEach because it depends on list being initialized
- const mountComponent = (propsData) => {
+ const mountComponent = ({ propsData = {}, provide = {} } = {}) => {
wrapper = shallowMount(BoardCardLayout, {
+ localVue,
stubs: {
issueCardInner,
},
@@ -39,6 +55,7 @@ describe('Board card layout', () => {
groupId: null,
rootPath: '/',
scopedLabelsAvailable: false,
+ ...provide,
},
});
};
@@ -75,6 +92,7 @@ describe('Board card layout', () => {
describe('mouse events', () => {
it('sets showDetail to true on mousedown', async () => {
+ createStore();
mountComponent();
wrapper.trigger('mousedown');
@@ -84,6 +102,7 @@ describe('Board card layout', () => {
});
it('sets showDetail to false on mousemove', async () => {
+ createStore();
mountComponent();
wrapper.trigger('mousedown');
await wrapper.vm.$nextTick();
@@ -92,5 +111,49 @@ describe('Board card layout', () => {
await wrapper.vm.$nextTick();
expect(wrapper.vm.showDetail).toBe(false);
});
+
+ it("calls 'setActiveId' when 'graphqlBoardLists' feature flag is turned on", async () => {
+ const setActiveId = jest.fn();
+ createStore({
+ actions: {
+ setActiveId,
+ },
+ });
+ mountComponent({
+ provide: {
+ glFeatures: { graphqlBoardLists: true },
+ },
+ });
+
+ wrapper.trigger('mouseup');
+ await wrapper.vm.$nextTick();
+
+ expect(setActiveId).toHaveBeenCalledTimes(1);
+ expect(setActiveId).toHaveBeenCalledWith(expect.any(Object), {
+ id: list.issues[0].id,
+ sidebarType: ISSUABLE,
+ });
+ });
+
+ it("calls 'setActiveId' when epic swimlanes is active", async () => {
+ const setActiveId = jest.fn();
+ const isSwimlanesOn = () => true;
+ createStore({
+ getters: { isSwimlanesOn },
+ actions: {
+ setActiveId,
+ },
+ });
+ mountComponent();
+
+ wrapper.trigger('mouseup');
+ await wrapper.vm.$nextTick();
+
+ expect(setActiveId).toHaveBeenCalledTimes(1);
+ expect(setActiveId).toHaveBeenCalledWith(expect.any(Object), {
+ id: list.issues[0].id,
+ sidebarType: ISSUABLE,
+ });
+ });
});
});
diff --git a/spec/frontend/environments/environment_table_spec.js b/spec/frontend/environments/environment_table_spec.js
index c9dc29af515..daef35bcf99 100644
--- a/spec/frontend/environments/environment_table_spec.js
+++ b/spec/frontend/environments/environment_table_spec.js
@@ -6,8 +6,6 @@ import CanaryUpdateModal from '~/environments/components/canary_update_modal.vue
import { folder, deployBoardMockData } from './mock_data';
const eeOnlyProps = {
- canaryDeploymentFeatureId: 'canary_deployment',
- showCanaryDeploymentCallout: true,
userCalloutsPath: '/callouts',
lockPromotionSvgPath: '/assets/illustrations/lock-promotion.svg',
helpCanaryDeploymentsPath: 'help/canary-deployments',
@@ -53,8 +51,6 @@ describe('Environment table', () => {
propsData: {
environments: [mockItem],
canReadEnvironment: true,
- canaryDeploymentFeatureId: 'canary_deployment',
- showCanaryDeploymentCallout: true,
userCalloutsPath: '/callouts',
lockPromotionSvgPath: '/assets/illustrations/lock-promotion.svg',
helpCanaryDeploymentsPath: 'help/canary-deployments',
@@ -83,8 +79,6 @@ describe('Environment table', () => {
environments: [mockItem],
canCreateDeployment: false,
canReadEnvironment: true,
- canaryDeploymentFeatureId: 'canary_deployment',
- showCanaryDeploymentCallout: true,
userCalloutsPath: '/callouts',
lockPromotionSvgPath: '/assets/illustrations/lock-promotion.svg',
helpCanaryDeploymentsPath: 'help/canary-deployments',
@@ -122,8 +116,6 @@ describe('Environment table', () => {
propsData: {
environments: [mockItem],
canReadEnvironment: true,
- canaryDeploymentFeatureId: 'canary_deployment',
- showCanaryDeploymentCallout: true,
userCalloutsPath: '/callouts',
lockPromotionSvgPath: '/assets/illustrations/lock-promotion.svg',
helpCanaryDeploymentsPath: 'help/canary-deployments',
@@ -152,8 +144,6 @@ describe('Environment table', () => {
environments: [mockItem],
canCreateDeployment: false,
canReadEnvironment: true,
- canaryDeploymentFeatureId: 'canary_deployment',
- showCanaryDeploymentCallout: true,
userCalloutsPath: '/callouts',
lockPromotionSvgPath: '/assets/illustrations/lock-promotion.svg',
helpCanaryDeploymentsPath: 'help/canary-deployments',
@@ -169,32 +159,6 @@ describe('Environment table', () => {
});
});
- it('should render canary callout', async () => {
- const mockItem = {
- name: 'review',
- folderName: 'review',
- size: 3,
- isFolder: true,
- environment_path: 'url',
- showCanaryCallout: true,
- };
-
- await factory({
- propsData: {
- environments: [mockItem],
- canCreateDeployment: false,
- canReadEnvironment: true,
- canaryDeploymentFeatureId: 'canary_deployment',
- showCanaryDeploymentCallout: true,
- userCalloutsPath: '/callouts',
- lockPromotionSvgPath: '/assets/illustrations/lock-promotion.svg',
- helpCanaryDeploymentsPath: 'help/canary-deployments',
- },
- });
-
- expect(wrapper.find('.canary-deployment-callout').exists()).toBe(true);
- });
-
describe('sortEnvironments', () => {
it('should sort environments by last updated', () => {
const mockItems = [
diff --git a/spec/frontend/environments/environments_app_spec.js b/spec/frontend/environments/environments_app_spec.js
index 7d61e99ec63..d6614e2fd2b 100644
--- a/spec/frontend/environments/environments_app_spec.js
+++ b/spec/frontend/environments/environments_app_spec.js
@@ -6,7 +6,6 @@ import Container from '~/environments/components/container.vue';
import EmptyState from '~/environments/components/empty_state.vue';
import EnvironmentsApp from '~/environments/components/environments_app.vue';
import DeployBoard from '~/environments/components/deploy_board.vue';
-import CanaryDeploymentBoard from '~/environments/components/canary_deployment_callout.vue';
import axios from '~/lib/utils/axios_utils';
import { environment, folder } from './mock_data';
@@ -20,8 +19,6 @@ describe('Environment', () => {
canReadEnvironment: true,
newEnvironmentPath: 'environments/new',
helpPagePath: 'help',
- canaryDeploymentFeatureId: 'canary_deployment',
- showCanaryDeploymentCallout: true,
userCalloutsPath: '/callouts',
lockPromotionSvgPath: '/assets/illustrations/lock-promotion.svg',
helpCanaryDeploymentsPath: 'help/canary-deployments',
@@ -38,9 +35,6 @@ describe('Environment', () => {
});
};
- const canaryPromoKeyValue = () =>
- wrapper.find(CanaryDeploymentBoard).attributes('data-js-canary-promo-key');
-
const createWrapper = (shallow = false, props = {}) => {
const fn = shallow ? shallowMount : mount;
wrapper = extendedWrapper(fn(EnvironmentsApp, { propsData: { ...mockData, ...props } }));
@@ -148,28 +142,6 @@ describe('Environment', () => {
).toBe(true);
});
});
-
- describe('canary callout with one environment', () => {
- it('should render banner underneath first environment', () => {
- expect(canaryPromoKeyValue()).toBe('0');
- });
- });
-
- describe('canary callout with multiple environments', () => {
- beforeEach(() => {
- mockRequest(200, {
- environments: [environment, environment],
- stopped_count: 1,
- available_count: 0,
- });
-
- return createWrapper();
- });
-
- it('should render banner underneath second environment', () => {
- expect(canaryPromoKeyValue()).toBe('1');
- });
- });
});
});
diff --git a/spec/frontend/environments/environments_folder_view_spec.js b/spec/frontend/environments/environments_folder_view_spec.js
index 43f809dda88..e4661d27872 100644
--- a/spec/frontend/environments/environments_folder_view_spec.js
+++ b/spec/frontend/environments/environments_folder_view_spec.js
@@ -13,8 +13,6 @@ describe('Environments Folder View', () => {
folderName: 'review',
canReadEnvironment: true,
cssContainerClass: 'container',
- canaryDeploymentFeatureId: 'canary_deployment',
- showCanaryDeploymentCallout: true,
userCalloutsPath: '/callouts',
lockPromotionSvgPath: '/assets/illustrations/lock-promotion.svg',
helpCanaryDeploymentsPath: 'help/canary-deployments',
diff --git a/spec/frontend/environments/environments_store_spec.js b/spec/frontend/environments/environments_store_spec.js
index 60f0152bd47..4a07281353f 100644
--- a/spec/frontend/environments/environments_store_spec.js
+++ b/spec/frontend/environments/environments_store_spec.js
@@ -214,18 +214,4 @@ describe('Store', () => {
expect(store.state.environments[0].deployBoardData).toEqual(deployBoardMockData);
});
});
-
- describe('canaryCallout', () => {
- it('should add banner underneath the second environment', () => {
- store.storeEnvironments(serverData);
-
- expect(store.state.environments[1].showCanaryCallout).toEqual(true);
- });
-
- it('should add banner underneath first environment when only one environment', () => {
- store.storeEnvironments(serverData.slice(0, 1));
-
- expect(store.state.environments[0].showCanaryCallout).toEqual(true);
- });
- });
});
diff --git a/spec/frontend/environments/folder/environments_folder_view_spec.js b/spec/frontend/environments/folder/environments_folder_view_spec.js
index abead001832..3943e89c6cf 100644
--- a/spec/frontend/environments/folder/environments_folder_view_spec.js
+++ b/spec/frontend/environments/folder/environments_folder_view_spec.js
@@ -16,8 +16,6 @@ describe('Environments Folder View', () => {
folderName: 'review',
canReadEnvironment: true,
cssContainerClass: 'container',
- canaryDeploymentFeatureId: 'canary_deployment',
- showCanaryDeploymentCallout: true,
userCalloutsPath: '/callouts',
lockPromotionSvgPath: '/assets/illustrations/lock-promotion.svg',
helpCanaryDeploymentsPath: 'help/canary-deployments',
diff --git a/spec/frontend/jira_connect/api_spec.js b/spec/frontend/jira_connect/api_spec.js
index e5a2484c827..8fecbee9ca7 100644
--- a/spec/frontend/jira_connect/api_spec.js
+++ b/spec/frontend/jira_connect/api_spec.js
@@ -2,7 +2,7 @@ import MockAdapter from 'axios-mock-adapter';
import axios from '~/lib/utils/axios_utils';
import httpStatus from '~/lib/utils/http_status';
-import { addSubscription, removeSubscription } from '~/jira_connect/api';
+import { addSubscription, removeSubscription, fetchGroups } from '~/jira_connect/api';
describe('JiraConnect API', () => {
let mock;
@@ -72,4 +72,36 @@ describe('JiraConnect API', () => {
expect(response.data).toEqual(mockResponse);
});
});
+
+ describe('fetchGroups', () => {
+ const mockGroupsPath = 'groupsPath';
+ const mockPage = 1;
+ const mockPerPage = 10;
+
+ const makeRequest = () =>
+ fetchGroups(mockGroupsPath, {
+ page: mockPage,
+ perPage: mockPerPage,
+ });
+
+ it('returns success response', async () => {
+ jest.spyOn(axios, 'get');
+ mock
+ .onGet(mockGroupsPath, {
+ page: mockPage,
+ per_page: mockPerPage,
+ })
+ .replyOnce(httpStatus.OK, mockResponse);
+
+ response = await makeRequest();
+
+ expect(axios.get).toHaveBeenCalledWith(mockGroupsPath, {
+ params: {
+ page: mockPage,
+ per_page: mockPerPage,
+ },
+ });
+ expect(response.data).toEqual(mockResponse);
+ });
+ });
});
diff --git a/spec/frontend/jira_connect/components/groups_list_item_spec.js b/spec/frontend/jira_connect/components/groups_list_item_spec.js
new file mode 100644
index 00000000000..77577c53cf4
--- /dev/null
+++ b/spec/frontend/jira_connect/components/groups_list_item_spec.js
@@ -0,0 +1,46 @@
+import { shallowMount } from '@vue/test-utils';
+import { GlAvatar } from '@gitlab/ui';
+import { extendedWrapper } from 'helpers/vue_test_utils_helper';
+import { mockGroup1 } from '../mock_data';
+
+import GroupsListItem from '~/jira_connect/components/groups_list_item.vue';
+
+describe('GroupsListItem', () => {
+ let wrapper;
+
+ const createComponent = () => {
+ wrapper = extendedWrapper(
+ shallowMount(GroupsListItem, {
+ propsData: {
+ group: mockGroup1,
+ },
+ }),
+ );
+ };
+
+ beforeEach(() => {
+ createComponent();
+ });
+
+ afterEach(() => {
+ wrapper.destroy();
+ wrapper = null;
+ });
+
+ const findGlAvatar = () => wrapper.find(GlAvatar);
+ const findGroupName = () => wrapper.findByTestId('group-list-item-name');
+ const findGroupDescription = () => wrapper.findByTestId('group-list-item-description');
+
+ it('renders group avatar', () => {
+ expect(findGlAvatar().exists()).toBe(true);
+ expect(findGlAvatar().props('src')).toBe(mockGroup1.avatar_url);
+ });
+
+ it('renders group name', () => {
+ expect(findGroupName().text()).toBe(mockGroup1.full_name);
+ });
+
+ it('renders group description', () => {
+ expect(findGroupDescription().text()).toBe(mockGroup1.description);
+ });
+});
diff --git a/spec/frontend/jira_connect/components/groups_list_spec.js b/spec/frontend/jira_connect/components/groups_list_spec.js
new file mode 100644
index 00000000000..94f158e6344
--- /dev/null
+++ b/spec/frontend/jira_connect/components/groups_list_spec.js
@@ -0,0 +1,71 @@
+import { shallowMount } from '@vue/test-utils';
+import { GlLoadingIcon } from '@gitlab/ui';
+import waitForPromises from 'helpers/wait_for_promises';
+
+import { fetchGroups } from '~/jira_connect/api';
+import GroupsList from '~/jira_connect/components/groups_list.vue';
+import GroupsListItem from '~/jira_connect/components/groups_list_item.vue';
+import { mockGroup1, mockGroup2 } from '../mock_data';
+
+jest.mock('~/jira_connect/api', () => {
+ return {
+ fetchGroups: jest.fn(),
+ };
+});
+describe('GroupsList', () => {
+ let wrapper;
+
+ const mockEmptyResponse = { data: [] };
+
+ const createComponent = (options = {}) => {
+ wrapper = shallowMount(GroupsList, {
+ ...options,
+ });
+ };
+
+ afterEach(() => {
+ wrapper.destroy();
+ wrapper = null;
+ });
+
+ const findGlLoadingIcon = () => wrapper.find(GlLoadingIcon);
+ const findAllItems = () => wrapper.findAll(GroupsListItem);
+ const findFirstItem = () => findAllItems().at(0);
+ const findSecondItem = () => findAllItems().at(1);
+
+ describe('isLoading is true', () => {
+ it('renders loading icon', async () => {
+ fetchGroups.mockResolvedValue(mockEmptyResponse);
+ createComponent();
+
+ wrapper.setData({ isLoading: true });
+ await wrapper.vm.$nextTick();
+
+ expect(findGlLoadingIcon().exists()).toBe(true);
+ });
+ });
+
+ describe('no groups returned', () => {
+ it('renders empty state', async () => {
+ fetchGroups.mockResolvedValue(mockEmptyResponse);
+ createComponent();
+
+ await waitForPromises();
+
+ expect(wrapper.text()).toContain('No available namespaces');
+ });
+ });
+
+ describe('with groups returned', () => {
+ it('renders groups list', async () => {
+ fetchGroups.mockResolvedValue({ data: [mockGroup1, mockGroup2] });
+ createComponent();
+
+ await waitForPromises();
+
+ expect(findAllItems().length).toBe(2);
+ expect(findFirstItem().props('group')).toBe(mockGroup1);
+ expect(findSecondItem().props('group')).toBe(mockGroup2);
+ });
+ });
+});
diff --git a/spec/frontend/jira_connect/mock_data.js b/spec/frontend/jira_connect/mock_data.js
new file mode 100644
index 00000000000..31565912489
--- /dev/null
+++ b/spec/frontend/jira_connect/mock_data.js
@@ -0,0 +1,15 @@
+export const mockGroup1 = {
+ id: 1,
+ avatar_url: 'avatar.png',
+ name: 'Gitlab Org',
+ full_name: 'Gitlab Org',
+ description: 'Open source software to collaborate on code',
+};
+
+export const mockGroup2 = {
+ id: 2,
+ avatar_url: 'avatar.png',
+ name: 'Gitlab Com',
+ full_name: 'Gitlab Com',
+ description: 'For GitLab company related projects',
+};
diff --git a/spec/frontend/lib/utils/datetime_utility_spec.js b/spec/frontend/lib/utils/datetime_utility_spec.js
index 3f0d73cbb10..66efd43262b 100644
--- a/spec/frontend/lib/utils/datetime_utility_spec.js
+++ b/spec/frontend/lib/utils/datetime_utility_spec.js
@@ -842,3 +842,58 @@ describe('format24HourTimeStringFromInt', () => {
});
});
});
+
+describe('getOverlappingDaysInPeriods', () => {
+ const start = new Date(2021, 0, 11);
+ const end = new Date(2021, 0, 13);
+
+ describe('when date periods overlap', () => {
+ const givenPeriodLeft = new Date(2021, 0, 11);
+ const givenPeriodRight = new Date(2021, 0, 14);
+
+ it('returns an overlap object that contains the amount of days overlapping, start date of overlap and end date of overlap', () => {
+ expect(
+ datetimeUtility.getOverlappingDaysInPeriods(
+ { start, end },
+ { start: givenPeriodLeft, end: givenPeriodRight },
+ ),
+ ).toEqual({
+ daysOverlap: 2,
+ overlapStartDate: givenPeriodLeft.getTime(),
+ overlapEndDate: end.getTime(),
+ });
+ });
+ });
+
+ describe('when date periods do not overlap', () => {
+ const givenPeriodLeft = new Date(2021, 0, 9);
+ const givenPeriodRight = new Date(2021, 0, 10);
+
+ it('returns an overlap object that contains a 0 value for days overlapping', () => {
+ expect(
+ datetimeUtility.getOverlappingDaysInPeriods(
+ { start, end },
+ { start: givenPeriodLeft, end: givenPeriodRight },
+ ),
+ ).toEqual({ daysOverlap: 0 });
+ });
+ });
+
+ describe('when date periods contain an invalid Date', () => {
+ const startInvalid = new Date(NaN);
+ const endInvalid = new Date(NaN);
+ const error = __('Invalid period');
+
+ it('throws an exception when the left period contains an invalid date', () => {
+ expect(() =>
+ datetimeUtility.getOverlappingDaysInPeriods({ start, end }, { start: startInvalid, end }),
+ ).toThrow(error);
+ });
+
+ it('throws an exception when the right period contains an invalid date', () => {
+ expect(() =>
+ datetimeUtility.getOverlappingDaysInPeriods({ start, end }, { start, end: endInvalid }),
+ ).toThrow(error);
+ });
+ });
+});
diff --git a/spec/graphql/types/notes/note_type_spec.rb b/spec/graphql/types/notes/note_type_spec.rb
index 180d13d35d2..03ff7828cf5 100644
--- a/spec/graphql/types/notes/note_type_spec.rb
+++ b/spec/graphql/types/notes/note_type_spec.rb
@@ -22,6 +22,7 @@ RSpec.describe GitlabSchema.types['Note'] do
system_note_icon_name
updated_at
user_permissions
+ url
]
expect(described_class).to have_graphql_fields(*expected_fields)
diff --git a/spec/helpers/jira_connect_helper_spec.rb b/spec/helpers/jira_connect_helper_spec.rb
new file mode 100644
index 00000000000..a99072527c8
--- /dev/null
+++ b/spec/helpers/jira_connect_helper_spec.rb
@@ -0,0 +1,15 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+
+RSpec.describe JiraConnectHelper do
+ describe '#jira_connect_app_data' do
+ subject { helper.jira_connect_app_data }
+
+ it 'includes Jira Connect app attributes' do
+ is_expected.to include(
+ :groups_path
+ )
+ end
+ end
+end
diff --git a/spec/lib/atlassian/jira_connect/client_spec.rb b/spec/lib/atlassian/jira_connect/client_spec.rb
index e042b2b183d..21ee40f22fe 100644
--- a/spec/lib/atlassian/jira_connect/client_spec.rb
+++ b/spec/lib/atlassian/jira_connect/client_spec.rb
@@ -107,6 +107,75 @@ RSpec.describe Atlassian::JiraConnect::Client do
}
end
+ describe '#handle_response' do
+ let(:errors) { [{ 'message' => 'X' }, { 'message' => 'Y' }] }
+ let(:processed) { subject.send(:handle_response, response, 'foo') { |x| [:data, x] } }
+
+ context 'the response is 200 OK' do
+ let(:response) { double(code: 200, parsed_response: :foo) }
+
+ it 'yields to the block' do
+ expect(processed).to eq [:data, :foo]
+ end
+ end
+
+ context 'the response is 400 bad request' do
+ let(:response) { double(code: 400, parsed_response: errors) }
+
+ it 'extracts the errors messages' do
+ expect(processed).to eq('errorMessages' => %w(X Y))
+ end
+ end
+
+ context 'the response is 401 forbidden' do
+ let(:response) { double(code: 401, parsed_response: nil) }
+
+ it 'reports that our JWT is wrong' do
+ expect(processed).to eq('errorMessages' => ['Invalid JWT'])
+ end
+ end
+
+ context 'the response is 403' do
+ let(:response) { double(code: 403, parsed_response: nil) }
+
+ it 'reports that the App is misconfigured' do
+ expect(processed).to eq('errorMessages' => ['App does not support foo'])
+ end
+ end
+
+ context 'the response is 413' do
+ let(:response) { double(code: 413, parsed_response: errors) }
+
+ it 'extracts the errors messages' do
+ expect(processed).to eq('errorMessages' => ['Data too large', 'X', 'Y'])
+ end
+ end
+
+ context 'the response is 429' do
+ let(:response) { double(code: 429, parsed_response: nil) }
+
+ it 'reports that we exceeded the rate limit' do
+ expect(processed).to eq('errorMessages' => ['Rate limit exceeded'])
+ end
+ end
+
+ context 'the response is 503' do
+ let(:response) { double(code: 503, parsed_response: nil) }
+
+ it 'reports that the service is unavailable' do
+ expect(processed).to eq('errorMessages' => ['Service unavailable'])
+ end
+ end
+
+ context 'the response is anything else' do
+ let(:response) { double(code: 1000, parsed_response: :something) }
+
+ it 'reports that this was unanticipated' do
+ expect(processed).to eq('errorMessages' => ['Unknown error'], 'response' => :something)
+ end
+ end
+ end
+
describe '#store_deploy_info' do
let_it_be(:environment) { create(:environment, name: 'DEV', project: project) }
let_it_be(:deployments) do
@@ -126,10 +195,20 @@ RSpec.describe Atlassian::JiraConnect::Client do
->(text) { matcher.matches?(text) }
end
+ let(:rejections) { [] }
+ let(:response_body) do
+ {
+ acceptedDeployments: [],
+ rejectedDeployments: rejections,
+ unknownIssueKeys: []
+ }.to_json
+ end
+
before do
path = '/rest/deployments/0.1/bulk'
stub_full_request('https://gitlab-test.atlassian.net' + path, method: :post)
.with(body: body, headers: expected_headers(path))
+ .to_return(body: response_body, headers: { 'Content-Type': 'application/json' })
end
it "calls the API with auth headers" do
@@ -137,7 +216,7 @@ RSpec.describe Atlassian::JiraConnect::Client do
end
it 'only sends information about relevant MRs' do
- expect(subject).to receive(:post).with('/rest/deployments/0.1/bulk', { deployments: have_attributes(size: 6) })
+ expect(subject).to receive(:post).with('/rest/deployments/0.1/bulk', { deployments: have_attributes(size: 6) }).and_call_original
subject.send(:store_deploy_info, project: project, deployments: deployments)
end
@@ -148,6 +227,18 @@ RSpec.describe Atlassian::JiraConnect::Client do
subject.send(:store_deploy_info, project: project, deployments: deployments.take(1))
end
+ context 'there are errors' do
+ let(:rejections) do
+ [{ errors: [{ message: 'X' }, { message: 'Y' }] }, { errors: [{ message: 'Z' }] }]
+ end
+
+ it 'reports the errors' do
+ response = subject.send(:store_deploy_info, project: project, deployments: deployments)
+
+ expect(response['errorMessages']).to eq(%w(X Y Z))
+ end
+ end
+
it 'does not call the API if the feature flag is not enabled' do
stub_feature_flags(jira_sync_deployments: false)
@@ -159,7 +250,7 @@ RSpec.describe Atlassian::JiraConnect::Client do
it 'does call the API if the feature flag enabled for the project' do
stub_feature_flags(jira_sync_deployments: project)
- expect(subject).to receive(:post).with('/rest/deployments/0.1/bulk', { deployments: Array })
+ expect(subject).to receive(:post).with('/rest/deployments/0.1/bulk', { deployments: Array }).and_call_original
subject.send(:store_deploy_info, project: project, deployments: deployments)
end
@@ -178,12 +269,22 @@ RSpec.describe Atlassian::JiraConnect::Client do
->(text) { matcher.matches?(text) }
end
+ let(:failures) { {} }
+ let(:response_body) do
+ {
+ acceptedFeatureFlags: [],
+ failedFeatureFlags: failures,
+ unknownIssueKeys: []
+ }.to_json
+ end
+
before do
feature_flags.first.update!(description: 'RELEVANT-123')
feature_flags.second.update!(description: 'RELEVANT-123')
path = '/rest/featureflags/0.1/bulk'
stub_full_request('https://gitlab-test.atlassian.net' + path, method: :post)
.with(body: body, headers: expected_headers(path))
+ .to_return(body: response_body, headers: { 'Content-Type': 'application/json' })
end
it "calls the API with auth headers" do
@@ -193,7 +294,7 @@ RSpec.describe Atlassian::JiraConnect::Client do
it 'only sends information about relevant MRs' do
expect(subject).to receive(:post).with('/rest/featureflags/0.1/bulk', {
flags: have_attributes(size: 2), properties: Hash
- })
+ }).and_call_original
subject.send(:store_ff_info, project: project, feature_flags: feature_flags)
end
@@ -204,6 +305,21 @@ RSpec.describe Atlassian::JiraConnect::Client do
subject.send(:store_ff_info, project: project, feature_flags: [feature_flags.last])
end
+ context 'there are errors' do
+ let(:failures) do
+ {
+ a: [{ message: 'X' }, { message: 'Y' }],
+ b: [{ message: 'Z' }]
+ }
+ end
+
+ it 'reports the errors' do
+ response = subject.send(:store_ff_info, project: project, feature_flags: feature_flags)
+
+ expect(response['errorMessages']).to eq(['a: X', 'a: Y', 'b: Z'])
+ end
+ end
+
it 'does not call the API if the feature flag is not enabled' do
stub_feature_flags(jira_sync_feature_flags: false)
@@ -217,7 +333,7 @@ RSpec.describe Atlassian::JiraConnect::Client do
expect(subject).to receive(:post).with('/rest/featureflags/0.1/bulk', {
flags: Array, properties: Hash
- })
+ }).and_call_original
subject.send(:store_ff_info, project: project, feature_flags: feature_flags)
end
@@ -234,10 +350,20 @@ RSpec.describe Atlassian::JiraConnect::Client do
->(text) { matcher.matches?(text) }
end
+ let(:failures) { [] }
+ let(:response_body) do
+ {
+ acceptedBuilds: [],
+ rejectedBuilds: failures,
+ unknownIssueKeys: []
+ }.to_json
+ end
+
before do
path = '/rest/builds/0.1/bulk'
stub_full_request('https://gitlab-test.atlassian.net' + path, method: :post)
.with(body: body, headers: expected_headers(path))
+ .to_return(body: response_body, headers: { 'Content-Type': 'application/json' })
end
it "calls the API with auth headers" do
@@ -245,7 +371,9 @@ RSpec.describe Atlassian::JiraConnect::Client do
end
it 'only sends information about relevant MRs' do
- expect(subject).to receive(:post).with('/rest/builds/0.1/bulk', { builds: have_attributes(size: 6) })
+ expect(subject).to receive(:post)
+ .with('/rest/builds/0.1/bulk', { builds: have_attributes(size: 6) })
+ .and_call_original
subject.send(:store_build_info, project: project, pipelines: pipelines)
end
@@ -267,11 +395,25 @@ RSpec.describe Atlassian::JiraConnect::Client do
it 'does call the API if the feature flag enabled for the project' do
stub_feature_flags(jira_sync_builds: project)
- expect(subject).to receive(:post).with('/rest/builds/0.1/bulk', { builds: Array })
+ expect(subject).to receive(:post)
+ .with('/rest/builds/0.1/bulk', { builds: Array })
+ .and_call_original
subject.send(:store_build_info, project: project, pipelines: pipelines)
end
+ context 'there are errors' do
+ let(:failures) do
+ [{ errors: [{ message: 'X' }, { message: 'Y' }] }, { errors: [{ message: 'Z' }] }]
+ end
+
+ it 'reports the errors' do
+ response = subject.send(:store_build_info, project: project, pipelines: pipelines)
+
+ expect(response['errorMessages']).to eq(%w(X Y Z))
+ end
+ end
+
it 'avoids N+1 database queries' do
pending 'https://gitlab.com/gitlab-org/gitlab/-/issues/292818'
diff --git a/spec/lib/gitlab/experimentation/controller_concern_spec.rb b/spec/lib/gitlab/experimentation/controller_concern_spec.rb
index c47f71c207d..1cebe37bea5 100644
--- a/spec/lib/gitlab/experimentation/controller_concern_spec.rb
+++ b/spec/lib/gitlab/experimentation/controller_concern_spec.rb
@@ -10,6 +10,10 @@ RSpec.describe Gitlab::Experimentation::ControllerConcern, type: :controller do
use_backwards_compatible_subject_index: true
},
test_experiment: {
+ tracking_category: 'Team',
+ rollout_strategy: rollout_strategy
+ },
+ my_experiment: {
tracking_category: 'Team'
}
}
@@ -20,6 +24,7 @@ RSpec.describe Gitlab::Experimentation::ControllerConcern, type: :controller do
end
let(:enabled_percentage) { 10 }
+ let(:rollout_strategy) { nil }
controller(ApplicationController) do
include Gitlab::Experimentation::ControllerConcern
@@ -117,6 +122,7 @@ RSpec.describe Gitlab::Experimentation::ControllerConcern, type: :controller do
end
context 'when subject is given' do
+ let(:rollout_strategy) { :user }
let(:user) { build(:user) }
it 'uses the subject' do
@@ -244,6 +250,7 @@ RSpec.describe Gitlab::Experimentation::ControllerConcern, type: :controller do
it "provides the subject's hashed global_id as label" do
experiment_subject = double(:subject, to_global_id: 'abc')
+ allow(Gitlab::Experimentation).to receive(:valid_subject_for_rollout_strategy?).and_return(true)
controller.track_experiment_event(:test_experiment, 'start', 1, subject: experiment_subject)
@@ -420,6 +427,26 @@ RSpec.describe Gitlab::Experimentation::ControllerConcern, type: :controller do
controller.record_experiment_user(:test_experiment, context)
end
+
+ context 'with a cookie based rollout strategy' do
+ it 'calls tracking_group with a nil subject' do
+ expect(controller).to receive(:tracking_group).with(:test_experiment, nil, subject: nil).and_return(:experimental)
+ allow(::Experiment).to receive(:add_user).with(:test_experiment, :experimental, user, context)
+
+ controller.record_experiment_user(:test_experiment, context)
+ end
+ end
+
+ context 'with a user based rollout strategy' do
+ let(:rollout_strategy) { :user }
+
+ it 'calls tracking_group with a user subject' do
+ expect(controller).to receive(:tracking_group).with(:test_experiment, nil, subject: user).and_return(:experimental)
+ allow(::Experiment).to receive(:add_user).with(:test_experiment, :experimental, user, context)
+
+ controller.record_experiment_user(:test_experiment, context)
+ end
+ end
end
context 'the user is part of the control group' do
diff --git a/spec/lib/gitlab/experimentation/experiment_spec.rb b/spec/lib/gitlab/experimentation/experiment_spec.rb
index 008e6699597..94dbf1d7e4b 100644
--- a/spec/lib/gitlab/experimentation/experiment_spec.rb
+++ b/spec/lib/gitlab/experimentation/experiment_spec.rb
@@ -9,7 +9,8 @@ RSpec.describe Gitlab::Experimentation::Experiment do
let(:params) do
{
tracking_category: 'Category1',
- use_backwards_compatible_subject_index: true
+ use_backwards_compatible_subject_index: true,
+ rollout_strategy: nil
}
end
diff --git a/spec/lib/gitlab/experimentation_spec.rb b/spec/lib/gitlab/experimentation_spec.rb
index b503960b8c7..71d08903532 100644
--- a/spec/lib/gitlab/experimentation_spec.rb
+++ b/spec/lib/gitlab/experimentation_spec.rb
@@ -102,7 +102,11 @@ RSpec.describe Gitlab::Experimentation do
context 'when subject has a global_id' do
let(:experiment_subject) { double(:subject, to_global_id: 'z') }
- it { is_expected.to eq(true) }
+ it do
+ allow(Gitlab::Experimentation).to receive(:valid_subject_for_rollout_strategy?).and_return(true)
+
+ is_expected.to eq(true)
+ end
end
context 'when subject is nil' do
@@ -150,7 +154,11 @@ RSpec.describe Gitlab::Experimentation do
context 'when subject has a global_id' do
let(:experiment_subject) { double(:subject, to_global_id: 'abcd') }
- it { is_expected.to eq(true) }
+ it do
+ allow(Gitlab::Experimentation).to receive(:valid_subject_for_rollout_strategy?).and_return(true)
+
+ is_expected.to eq(true)
+ end
end
context 'when subject is nil' do
diff --git a/spec/lib/gitlab/path_regex_spec.rb b/spec/lib/gitlab/path_regex_spec.rb
index 8e9f7e372c5..cd89674af0f 100644
--- a/spec/lib/gitlab/path_regex_spec.rb
+++ b/spec/lib/gitlab/path_regex_spec.rb
@@ -102,6 +102,7 @@ RSpec.describe Gitlab::PathRegex do
.concat(files_in_public)
.concat(Array(API::API.prefix.to_s))
.concat(sitemap_words)
+ .concat(deprecated_routes)
.compact
.uniq
end
@@ -110,6 +111,11 @@ RSpec.describe Gitlab::PathRegex do
%w(sitemap sitemap.xml sitemap.xml.gz)
end
+ let(:deprecated_routes) do
+ # profile was deprecated in https://gitlab.com/gitlab-org/gitlab/-/merge_requests/51646
+ %w(profile)
+ end
+
let(:ee_top_level_words) do
%w(unsubscribes v2)
end
diff --git a/spec/requests/profiles/notifications_controller_spec.rb b/spec/requests/profiles/notifications_controller_spec.rb
index 87669b3594c..d7dfb1c675d 100644
--- a/spec/requests/profiles/notifications_controller_spec.rb
+++ b/spec/requests/profiles/notifications_controller_spec.rb
@@ -24,7 +24,7 @@ RSpec.describe 'view user notifications' do
get profile_notifications_path
end
- describe 'GET /profile/notifications' do
+ describe 'GET /-/profile/notifications' do
it 'does not have an N+1 due to an additional groups (with no parent group)' do
get_profile_notifications
diff --git a/spec/routing/notifications_routing_spec.rb b/spec/routing/notifications_routing_spec.rb
index 007e8ff4816..d66aa7f219f 100644
--- a/spec/routing/notifications_routing_spec.rb
+++ b/spec/routing/notifications_routing_spec.rb
@@ -4,15 +4,15 @@ require "spec_helper"
RSpec.describe "notifications routing" do
it "routes to #show" do
- expect(get("/profile/notifications")).to route_to("profiles/notifications#show")
+ expect(get("/-/profile/notifications")).to route_to("profiles/notifications#show")
end
it "routes to #update" do
- expect(put("/profile/notifications")).to route_to("profiles/notifications#update")
+ expect(put("/-/profile/notifications")).to route_to("profiles/notifications#update")
end
it 'routes to group #update' do
- expect(put("/profile/notifications/groups/gitlab-org")).to route_to("profiles/groups#update", id: 'gitlab-org')
- expect(put("/profile/notifications/groups/gitlab.org")).to route_to("profiles/groups#update", id: 'gitlab.org')
+ expect(put("/-/profile/notifications/groups/gitlab-org")).to route_to("profiles/groups#update", id: 'gitlab-org')
+ expect(put("/-/profile/notifications/groups/gitlab.org")).to route_to("profiles/groups#update", id: 'gitlab.org')
end
end
diff --git a/spec/routing/project_routing_spec.rb b/spec/routing/project_routing_spec.rb
index a683dc28f4f..29e5c1b4bae 100644
--- a/spec/routing/project_routing_spec.rb
+++ b/spec/routing/project_routing_spec.rb
@@ -106,8 +106,8 @@ RSpec.describe 'project routing' do
let(:base_path) { '/gitlab/gitlabhq/-/wikis' }
end
- it_behaves_like 'redirecting a legacy project path', "/gitlab/gitlabhq/wikis", "/gitlab/gitlabhq/-/wikis"
- it_behaves_like 'redirecting a legacy project path', "/gitlab/gitlabhq/wikis/home/edit", "/gitlab/gitlabhq/-/wikis/home/edit"
+ it_behaves_like 'redirecting a legacy path', "/gitlab/gitlabhq/wikis", "/gitlab/gitlabhq/-/wikis"
+ it_behaves_like 'redirecting a legacy path', "/gitlab/gitlabhq/wikis/home/edit", "/gitlab/gitlabhq/-/wikis/home/edit"
end
# branches_project_repository GET /:project_id/repository/branches(.:format) projects/repositories#branches
@@ -171,7 +171,7 @@ RSpec.describe 'project routing' do
expect(delete('/gitlab/gitlabhq/-/tags/feature@45/foo/bar/baz')).to route_to('projects/tags#destroy', namespace_id: 'gitlab', project_id: 'gitlabhq', id: 'feature@45/foo/bar/baz')
end
- it_behaves_like 'redirecting a legacy project path', "/gitlab/gitlabhq/tags", "/gitlab/gitlabhq/-/tags"
+ it_behaves_like 'redirecting a legacy path', "/gitlab/gitlabhq/tags", "/gitlab/gitlabhq/-/tags"
end
# project_deploy_keys GET /:project_id/deploy_keys(.:format) deploy_keys#index
@@ -259,8 +259,8 @@ RSpec.describe 'project routing' do
let(:base_path) { '/gitlab/gitlabhq/-/merge_requests' }
end
- it_behaves_like 'redirecting a legacy project path', "/gitlab/gitlabhq/merge_requests", "/gitlab/gitlabhq/-/merge_requests"
- it_behaves_like 'redirecting a legacy project path', "/gitlab/gitlabhq/merge_requests/1/diffs", "/gitlab/gitlabhq/-/merge_requests/1/diffs"
+ it_behaves_like 'redirecting a legacy path', "/gitlab/gitlabhq/merge_requests", "/gitlab/gitlabhq/-/merge_requests"
+ it_behaves_like 'redirecting a legacy path', "/gitlab/gitlabhq/merge_requests/1/diffs", "/gitlab/gitlabhq/-/merge_requests/1/diffs"
end
describe Projects::MergeRequests::CreationsController, 'routing' do
@@ -290,7 +290,7 @@ RSpec.describe 'project routing' do
expect(get('/gitlab/gitlabhq/-/merge_requests/new/diffs.json')).to route_to('projects/merge_requests/creations#diffs', namespace_id: 'gitlab', project_id: 'gitlabhq', format: 'json')
end
- it_behaves_like 'redirecting a legacy project path', "/gitlab/gitlabhq/merge_requests/new", "/gitlab/gitlabhq/-/merge_requests/new"
+ it_behaves_like 'redirecting a legacy path', "/gitlab/gitlabhq/merge_requests/new", "/gitlab/gitlabhq/-/merge_requests/new"
end
describe Projects::MergeRequests::DiffsController, 'routing' do
@@ -454,8 +454,8 @@ RSpec.describe 'project routing' do
let(:base_path) { '/gitlab/gitlabhq/-/issues' }
end
- it_behaves_like 'redirecting a legacy project path', "/gitlab/gitlabhq/issues", "/gitlab/gitlabhq/-/issues"
- it_behaves_like 'redirecting a legacy project path', "/gitlab/gitlabhq/issues/1/edit", "/gitlab/gitlabhq/-/issues/1/edit"
+ it_behaves_like 'redirecting a legacy path', "/gitlab/gitlabhq/issues", "/gitlab/gitlabhq/-/issues"
+ it_behaves_like 'redirecting a legacy path', "/gitlab/gitlabhq/issues/1/edit", "/gitlab/gitlabhq/-/issues/1/edit"
end
# project_noteable_notes GET /:project_id/noteable/:target_type/:target_id/notes notes#index
@@ -769,25 +769,25 @@ RSpec.describe 'project routing' do
describe Projects::EnvironmentsController, 'routing' do
describe 'legacy routing' do
- it_behaves_like 'redirecting a legacy project path', "/gitlab/gitlabhq/environments", "/gitlab/gitlabhq/-/environments"
+ it_behaves_like 'redirecting a legacy path', "/gitlab/gitlabhq/environments", "/gitlab/gitlabhq/-/environments"
end
end
describe Projects::ClustersController, 'routing' do
describe 'legacy routing' do
- it_behaves_like 'redirecting a legacy project path', "/gitlab/gitlabhq/clusters", "/gitlab/gitlabhq/-/clusters"
+ it_behaves_like 'redirecting a legacy path', "/gitlab/gitlabhq/clusters", "/gitlab/gitlabhq/-/clusters"
end
end
describe Projects::ErrorTrackingController, 'routing' do
describe 'legacy routing' do
- it_behaves_like 'redirecting a legacy project path', "/gitlab/gitlabhq/error_tracking", "/gitlab/gitlabhq/-/error_tracking"
+ it_behaves_like 'redirecting a legacy path', "/gitlab/gitlabhq/error_tracking", "/gitlab/gitlabhq/-/error_tracking"
end
end
describe Projects::Serverless, 'routing' do
describe 'legacy routing' do
- it_behaves_like 'redirecting a legacy project path', "/gitlab/gitlabhq/serverless", "/gitlab/gitlabhq/-/serverless"
+ it_behaves_like 'redirecting a legacy path', "/gitlab/gitlabhq/serverless", "/gitlab/gitlabhq/-/serverless"
end
end
diff --git a/spec/routing/routing_spec.rb b/spec/routing/routing_spec.rb
index 26ad1f14786..7b9ba783885 100644
--- a/spec/routing/routing_spec.rb
+++ b/spec/routing/routing_spec.rb
@@ -122,111 +122,115 @@ RSpec.describe HelpController, "routing" do
end
end
-# profile_account GET /profile/account(.:format) profile#account
-# profile_history GET /profile/history(.:format) profile#history
-# profile_password PUT /profile/password(.:format) profile#password_update
-# profile_token GET /profile/token(.:format) profile#token
-# profile GET /profile(.:format) profile#show
-# profile_update PUT /profile/update(.:format) profile#update
+# profile_account GET /-/profile/account(.:format) profile#account
+# profile_history GET /-/profile/history(.:format) profile#history
+# profile_password PUT /-/profile/password(.:format) profile#password_update
+# profile_token GET /-/profile/token(.:format) profile#token
+# profile GET /-/profile(.:format) profile#show
+# profile_update PUT /-/profile/update(.:format) profile#update
RSpec.describe ProfilesController, "routing" do
it "to #account" do
- expect(get("/profile/account")).to route_to('profiles/accounts#show')
+ expect(get("/-/profile/account")).to route_to('profiles/accounts#show')
end
+ it_behaves_like 'redirecting a legacy path', '/profile/account', '/-/profile/account'
it "to #audit_log" do
- expect(get("/profile/audit_log")).to route_to('profiles#audit_log')
+ expect(get("/-/profile/audit_log")).to route_to('profiles#audit_log')
end
+ it_behaves_like 'redirecting a legacy path', '/profile/audit_log', '/-/profile/audit_log'
it "to #reset_feed_token" do
- expect(put("/profile/reset_feed_token")).to route_to('profiles#reset_feed_token')
+ expect(put("/-/profile/reset_feed_token")).to route_to('profiles#reset_feed_token')
end
it "to #show" do
- expect(get("/profile")).to route_to('profiles#show')
- end
-
- it 'to #show from scope routing' do
expect(get("/-/profile")).to route_to('profiles#show')
end
+ it_behaves_like 'redirecting a legacy path', '/profile', '/-/profile'
end
-# profile_preferences GET /profile/preferences(.:format) profiles/preferences#show
-# PATCH /profile/preferences(.:format) profiles/preferences#update
-# PUT /profile/preferences(.:format) profiles/preferences#update
+# profile_preferences GET /-/profile/preferences(.:format) profiles/preferences#show
+# PATCH /-/profile/preferences(.:format) profiles/preferences#update
+# PUT /-/profile/preferences(.:format) profiles/preferences#update
RSpec.describe Profiles::PreferencesController, 'routing' do
it 'to #show' do
- expect(get('/profile/preferences')).to route_to('profiles/preferences#show')
+ expect(get('/-/profile/preferences')).to route_to('profiles/preferences#show')
end
+ it_behaves_like 'redirecting a legacy path', '/profile/preferences', '/-/profile/preferences'
it 'to #update' do
- expect(put('/profile/preferences')).to route_to('profiles/preferences#update')
- expect(patch('/profile/preferences')).to route_to('profiles/preferences#update')
+ expect(put('/-/profile/preferences')).to route_to('profiles/preferences#update')
+ expect(patch('/-/profile/preferences')).to route_to('profiles/preferences#update')
end
end
-# keys GET /keys(.:format) keys#index
-# POST /keys(.:format) keys#create
-# edit_key GET /keys/:id/edit(.:format) keys#edit
-# key GET /keys/:id(.:format) keys#show
-# PUT /keys/:id(.:format) keys#update
-# DELETE /keys/:id(.:format) keys#destroy
+# keys GET /-/profile/keys(.:format) keys#index
+# POST /-/profile/keys(.:format) keys#create
+# edit_key GET /-/profile/keys/:id/edit(.:format) keys#edit
+# key GET /-/profile/keys/:id(.:format) keys#show
+# PUT /-/profile/keys/:id(.:format) keys#update
+# DELETE /-/profile/keys/:id(.:format) keys#destroy
RSpec.describe Profiles::KeysController, "routing" do
it "to #index" do
- expect(get("/profile/keys")).to route_to('profiles/keys#index')
+ expect(get("/-/profile/keys")).to route_to('profiles/keys#index')
end
+ it_behaves_like 'redirecting a legacy path', '/profile/keys', '/-/profile/keys'
it "to #create" do
- expect(post("/profile/keys")).to route_to('profiles/keys#create')
+ expect(post("/-/profile/keys")).to route_to('profiles/keys#create')
end
it "to #show" do
- expect(get("/profile/keys/1")).to route_to('profiles/keys#show', id: '1')
+ expect(get("/-/profile/keys/1")).to route_to('profiles/keys#show', id: '1')
end
+ it_behaves_like 'redirecting a legacy path', '/profile/keys/1', '/-/profile/keys/1'
it "to #destroy" do
- expect(delete("/profile/keys/1")).to route_to('profiles/keys#destroy', id: '1')
+ expect(delete("/-/profile/keys/1")).to route_to('profiles/keys#destroy', id: '1')
end
end
-# keys GET /gpg_keys gpg_keys#index
-# key POST /gpg_keys gpg_keys#create
-# PUT /gpg_keys/:id gpg_keys#revoke
-# DELETE /gpg_keys/:id gpg_keys#desroy
+# keys GET /-/profile/gpg_keys gpg_keys#index
+# key POST /-/profile/gpg_keys gpg_keys#create
+# PUT /-/profile/gpg_keys/:id gpg_keys#revoke
+# DELETE /-/profile/gpg_keys/:id gpg_keys#desroy
RSpec.describe Profiles::GpgKeysController, "routing" do
it "to #index" do
- expect(get("/profile/gpg_keys")).to route_to('profiles/gpg_keys#index')
+ expect(get("/-/profile/gpg_keys")).to route_to('profiles/gpg_keys#index')
end
+ it_behaves_like 'redirecting a legacy path', '/profile/gpg_keys', '/-/profile/gpg_keys'
it "to #create" do
- expect(post("/profile/gpg_keys")).to route_to('profiles/gpg_keys#create')
+ expect(post("/-/profile/gpg_keys")).to route_to('profiles/gpg_keys#create')
end
it "to #destroy" do
- expect(delete("/profile/gpg_keys/1")).to route_to('profiles/gpg_keys#destroy', id: '1')
+ expect(delete("/-/profile/gpg_keys/1")).to route_to('profiles/gpg_keys#destroy', id: '1')
end
end
-# emails GET /emails(.:format) emails#index
-# POST /keys(.:format) emails#create
-# DELETE /keys/:id(.:format) keys#destroy
+# emails GET /-/profile/emails(.:format) emails#index
+# POST /-/profile/emails(.:format) emails#create
+# DELETE /-/profile/emails/:id(.:format) keys#destroy
RSpec.describe Profiles::EmailsController, "routing" do
it "to #index" do
- expect(get("/profile/emails")).to route_to('profiles/emails#index')
+ expect(get("/-/profile/emails")).to route_to('profiles/emails#index')
end
+ it_behaves_like 'redirecting a legacy path', '/profile/emails', '/-/profile/emails'
it "to #create" do
- expect(post("/profile/emails")).to route_to('profiles/emails#create')
+ expect(post("/-/profile/emails")).to route_to('profiles/emails#create')
end
it "to #destroy" do
- expect(delete("/profile/emails/1")).to route_to('profiles/emails#destroy', id: '1')
+ expect(delete("/-/profile/emails/1")).to route_to('profiles/emails#destroy', id: '1')
end
end
-# profile_avatar DELETE /profile/avatar(.:format) profiles/avatars#destroy
+# profile_avatar DELETE /-/profile/avatar(.:format) profiles/avatars#destroy
RSpec.describe Profiles::AvatarsController, "routing" do
it "to #destroy" do
- expect(delete("/profile/avatar")).to route_to('profiles/avatars#destroy')
+ expect(delete("/-/profile/avatar")).to route_to('profiles/avatars#destroy')
end
end
diff --git a/spec/services/jira_connect/sync_service_spec.rb b/spec/services/jira_connect/sync_service_spec.rb
index 4b434348146..edd0bad70f5 100644
--- a/spec/services/jira_connect/sync_service_spec.rb
+++ b/spec/services/jira_connect/sync_service_spec.rb
@@ -45,11 +45,11 @@ RSpec.describe JiraConnect::SyncService do
it 'logs the response as an error' do
expect_next(client).to store_info([
{ 'errorMessages' => ['some error message'] },
- { 'rejectedBuilds' => ['x'] }
+ { 'errorMessages' => ['x'] }
])
expect_log(:error, { 'errorMessages' => ['some error message'] })
- expect_log(:error, { 'rejectedBuilds' => ['x'] })
+ expect_log(:error, { 'errorMessages' => ['x'] })
subject
end
diff --git a/spec/services/projects/update_pages_service_spec.rb b/spec/services/projects/update_pages_service_spec.rb
index a15f6bdbe2c..a6730c5de52 100644
--- a/spec/services/projects/update_pages_service_spec.rb
+++ b/spec/services/projects/update_pages_service_spec.rb
@@ -79,6 +79,19 @@ RSpec.describe Projects::UpdatePagesService do
end
end
+ it 'fails if sha on branch was updated before deployment was uploaded' do
+ expect(subject).to receive(:create_pages_deployment).and_wrap_original do |m, *args|
+ build.update!(ref: 'feature')
+ m.call(*args)
+ end
+
+ expect(execute).not_to eq(:success)
+ expect(project.pages_metadatum).not_to be_deployed
+
+ expect(deploy_status).to be_failed
+ expect(deploy_status.description).to eq('build SHA is outdated for this ref')
+ end
+
it 'does not fail if pages_metadata is absent' do
project.pages_metadatum.destroy!
project.reload
diff --git a/spec/support/shared_examples/routing/legacy_path_redirect_shared_examples.rb b/spec/support/shared_examples/routing/legacy_path_redirect_shared_examples.rb
index 808336db7b1..ae3f6425b5e 100644
--- a/spec/support/shared_examples/routing/legacy_path_redirect_shared_examples.rb
+++ b/spec/support/shared_examples/routing/legacy_path_redirect_shared_examples.rb
@@ -1,6 +1,6 @@
# frozen_string_literal: true
-RSpec.shared_examples 'redirecting a legacy project path' do |source, target|
+RSpec.shared_examples 'redirecting a legacy path' do |source, target|
include RSpec::Rails::RequestExampleGroup
it "redirects #{source} to #{target}" do
diff --git a/spec/workers/container_expiration_policies/cleanup_container_repository_worker_spec.rb b/spec/workers/container_expiration_policies/cleanup_container_repository_worker_spec.rb
index 01bd856e2a8..e6592f7f204 100644
--- a/spec/workers/container_expiration_policies/cleanup_container_repository_worker_spec.rb
+++ b/spec/workers/container_expiration_policies/cleanup_container_repository_worker_spec.rb
@@ -3,6 +3,8 @@
require 'spec_helper'
RSpec.describe ContainerExpirationPolicies::CleanupContainerRepositoryWorker do
+ using RSpec::Parameterized::TableSyntax
+
let_it_be(:repository, reload: true) { create(:container_repository, :cleanup_scheduled) }
let_it_be(:project) { repository.project }
let_it_be(:policy) { project.container_expiration_policy }
@@ -42,11 +44,32 @@ RSpec.describe ContainerExpirationPolicies::CleanupContainerRepositoryWorker do
service_response = cleanup_service_response(status: :unfinished, repository: repository, cleanup_tags_service_after_truncate_size: 10, cleanup_tags_service_before_delete_size: 5)
expect(ContainerExpirationPolicies::CleanupService)
.to receive(:new).with(repository).and_return(double(execute: service_response))
- expect_log_extra_metadata(service_response: service_response, cleanup_status: :unfinished)
+ expect_log_extra_metadata(service_response: service_response, cleanup_status: :unfinished, truncated: true)
subject
end
end
+
+ context 'the truncated log field' do
+ where(:before_truncate_size, :after_truncate_size, :truncated) do
+ 100 | 100 | false
+ 100 | 80 | true
+ nil | 100 | false
+ 100 | nil | false
+ nil | nil | false
+ end
+
+ with_them do
+ it 'is logged properly' do
+ service_response = cleanup_service_response(status: :unfinished, repository: repository, cleanup_tags_service_after_truncate_size: after_truncate_size, cleanup_tags_service_before_truncate_size: before_truncate_size)
+ expect(ContainerExpirationPolicies::CleanupService)
+ .to receive(:new).with(repository).and_return(double(execute: service_response))
+ expect_log_extra_metadata(service_response: service_response, cleanup_status: :unfinished, truncated: truncated)
+
+ subject
+ end
+ end
+ end
end
context 'with policy running shortly' do
@@ -189,13 +212,14 @@ RSpec.describe ContainerExpirationPolicies::CleanupContainerRepositoryWorker do
)
end
- def expect_log_extra_metadata(service_response:, cleanup_status: :finished)
+ def expect_log_extra_metadata(service_response:, cleanup_status: :finished, truncated: false)
expect(worker).to receive(:log_extra_metadata_on_done).with(:cleanup_status, cleanup_status)
expect(worker).to receive(:log_extra_metadata_on_done).with(:container_repository_id, repository.id)
- expect(worker).to receive(:log_extra_metadata_on_done).with(:cleanup_tags_service_original_size, service_response.payload[:cleanup_tags_service_original_size])
- expect(worker).to receive(:log_extra_metadata_on_done).with(:cleanup_tags_service_before_truncate_size, service_response.payload[:cleanup_tags_service_before_truncate_size])
- expect(worker).to receive(:log_extra_metadata_on_done).with(:cleanup_tags_service_after_truncate_size, service_response.payload[:cleanup_tags_service_after_truncate_size])
- expect(worker).to receive(:log_extra_metadata_on_done).with(:cleanup_tags_service_before_delete_size, service_response.payload[:cleanup_tags_service_before_delete_size])
+ %i[cleanup_tags_service_original_size cleanup_tags_service_before_truncate_size cleanup_tags_service_after_truncate_size cleanup_tags_service_before_delete_size].each do |field|
+ value = service_response.payload[field]
+ expect(worker).to receive(:log_extra_metadata_on_done).with(field, value) unless value.nil?
+ end
+ expect(worker).to receive(:log_extra_metadata_on_done).with(:cleanup_tags_service_truncated, truncated)
end
end