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-06-11 18:09:58 +0300
committerGitLab Bot <gitlab-bot@gitlab.com>2021-06-11 18:09:58 +0300
commit62cd7010ef91dcaa5a5a36790985053db0b38671 (patch)
tree935b69e9d6ad2fe13c5ea0cb0d1bbd0b3413b3ab /spec
parentdcf94a76413ddb50148bdac7b189afb7bffa7580 (diff)
Add latest changes from gitlab-org/gitlab@master
Diffstat (limited to 'spec')
-rw-r--r--spec/controllers/admin/application_settings/appearances_controller_spec.rb (renamed from spec/controllers/admin/appearances_controller_spec.rb)2
-rw-r--r--spec/controllers/import/bulk_imports_controller_spec.rb4
-rw-r--r--spec/features/admin/admin_appearance_spec.rb20
-rw-r--r--spec/features/admin/admin_search_settings_spec.rb2
-rw-r--r--spec/frontend/cycle_analytics/base_spec.js9
-rw-r--r--spec/frontend/cycle_analytics/mock_data.js95
-rw-r--r--spec/frontend/cycle_analytics/store/actions_spec.js165
-rw-r--r--spec/frontend/cycle_analytics/store/mutations_spec.js34
-rw-r--r--spec/frontend/cycle_analytics/utils_spec.js29
-rw-r--r--spec/frontend/lib/utils/url_utility_spec.js57
-rw-r--r--spec/lib/bulk_imports/clients/http_spec.rb2
-rw-r--r--spec/lib/bulk_imports/common/extractors/rest_extractor_spec.rb2
-rw-r--r--spec/lib/bulk_imports/groups/extractors/subgroups_extractor_spec.rb2
-rw-r--r--spec/migrations/migrate_protected_attribute_to_pending_builds_spec.rb33
-rw-r--r--spec/models/bulk_imports/export_status_spec.rb4
-rw-r--r--spec/models/ci/build_spec.rb58
-rw-r--r--spec/models/ci/pending_build_spec.rb2
-rw-r--r--spec/models/ci/running_build_spec.rb55
-rw-r--r--spec/models/concerns/issuable_spec.rb2
-rw-r--r--spec/models/deploy_token_spec.rb76
-rw-r--r--spec/requests/api/projects_spec.rb45
-rw-r--r--spec/services/bulk_imports/file_download_service_spec.rb2
-rw-r--r--spec/services/ci/create_pipeline_service/needs_spec.rb46
-rw-r--r--spec/services/ci/pipeline_creation/drop_not_runnable_builds_service_spec.rb80
-rw-r--r--spec/services/ci/pipeline_creation/start_pipeline_service_spec.rb9
-rw-r--r--spec/services/ci/retry_build_service_spec.rb2
-rw-r--r--spec/services/ci/update_build_queue_service_spec.rb219
-rw-r--r--spec/services/projects/create_service_spec.rb32
-rw-r--r--spec/services/projects/group_links/create_service_spec.rb22
-rw-r--r--spec/workers/bulk_imports/export_request_worker_spec.rb2
-rw-r--r--spec/workers/ci/initial_pipeline_process_worker_spec.rb18
31 files changed, 671 insertions, 459 deletions
diff --git a/spec/controllers/admin/appearances_controller_spec.rb b/spec/controllers/admin/application_settings/appearances_controller_spec.rb
index ee6a4a4c7af..cc914f3c9b8 100644
--- a/spec/controllers/admin/appearances_controller_spec.rb
+++ b/spec/controllers/admin/application_settings/appearances_controller_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe Admin::AppearancesController do
+RSpec.describe Admin::ApplicationSettings::AppearancesController do
let(:admin) { create(:admin) }
let(:header_message) { 'Header message' }
let(:footer_message) { 'Footer' }
diff --git a/spec/controllers/import/bulk_imports_controller_spec.rb b/spec/controllers/import/bulk_imports_controller_spec.rb
index 12aa1d89ecc..8f74d210667 100644
--- a/spec/controllers/import/bulk_imports_controller_spec.rb
+++ b/spec/controllers/import/bulk_imports_controller_spec.rb
@@ -51,7 +51,7 @@ RSpec.describe Import::BulkImportsController do
end
describe 'GET status' do
- let(:client) { BulkImports::Clients::Http.new(uri: 'http://gitlab.example', token: 'token') }
+ let(:client) { BulkImports::Clients::HTTP.new(uri: 'http://gitlab.example', token: 'token') }
describe 'serialized group data' do
let(:client_response) do
@@ -149,7 +149,7 @@ RSpec.describe Import::BulkImportsController do
context 'when connection error occurs' do
before do
allow(controller).to receive(:client).and_return(client)
- allow(client).to receive(:get).and_raise(BulkImports::Clients::Http::ConnectionError)
+ allow(client).to receive(:get).and_raise(BulkImports::Clients::HTTP::ConnectionError)
end
it 'returns 422' do
diff --git a/spec/features/admin/admin_appearance_spec.rb b/spec/features/admin/admin_appearance_spec.rb
index 603e757096f..5596ad7bf21 100644
--- a/spec/features/admin/admin_appearance_spec.rb
+++ b/spec/features/admin/admin_appearance_spec.rb
@@ -9,7 +9,7 @@ RSpec.describe 'Admin Appearance' do
it 'create new appearance' do
sign_in(admin)
gitlab_enable_admin_mode_sign_in(admin)
- visit admin_appearances_path
+ visit admin_application_settings_appearances_path
fill_in 'appearance_title', with: 'MyCompany'
fill_in 'appearance_description', with: 'dev server'
@@ -17,7 +17,7 @@ RSpec.describe 'Admin Appearance' do
fill_in 'appearance_profile_image_guidelines', with: 'Custom profile image guidelines'
click_button 'Update appearance settings'
- expect(current_path).to eq admin_appearances_path
+ expect(current_path).to eq admin_application_settings_appearances_path
expect(page).to have_content 'Appearance'
expect(page).to have_field('appearance_title', with: 'MyCompany')
@@ -31,7 +31,7 @@ RSpec.describe 'Admin Appearance' do
sign_in(admin)
gitlab_enable_admin_mode_sign_in(admin)
- visit admin_appearances_path
+ visit admin_application_settings_appearances_path
click_link "Sign-in page"
expect_custom_sign_in_appearance(appearance)
@@ -41,7 +41,7 @@ RSpec.describe 'Admin Appearance' do
sign_in(admin)
gitlab_enable_admin_mode_sign_in(admin)
- visit admin_appearances_path
+ visit admin_application_settings_appearances_path
click_link "New project page"
expect_custom_new_project_appearance(appearance)
@@ -55,7 +55,7 @@ RSpec.describe 'Admin Appearance' do
context 'when system header and footer messages are empty' do
it 'shows custom system header and footer fields' do
- visit admin_appearances_path
+ visit admin_application_settings_appearances_path
expect(page).to have_field('appearance_header_message', with: '')
expect(page).to have_field('appearance_footer_message', with: '')
@@ -70,7 +70,7 @@ RSpec.describe 'Admin Appearance' do
end
it 'shows custom system header and footer fields' do
- visit admin_appearances_path
+ visit admin_application_settings_appearances_path
expect(page).to have_field('appearance_header_message', with: appearance.header_message)
expect(page).to have_field('appearance_footer_message', with: appearance.footer_message)
@@ -99,7 +99,7 @@ RSpec.describe 'Admin Appearance' do
before do
sign_in(create(:admin))
gitlab_enable_admin_mode_sign_in(admin)
- visit admin_appearances_path
+ visit admin_application_settings_appearances_path
fill_in 'appearance_profile_image_guidelines', with: 'Custom profile image guidelines, please :smile:!'
click_button 'Update appearance settings'
end
@@ -115,7 +115,7 @@ RSpec.describe 'Admin Appearance' do
it 'appearance logo' do
sign_in(admin)
gitlab_enable_admin_mode_sign_in(admin)
- visit admin_appearances_path
+ visit admin_application_settings_appearances_path
attach_file(:appearance_logo, logo_fixture)
click_button 'Update appearance settings'
@@ -128,7 +128,7 @@ RSpec.describe 'Admin Appearance' do
it 'header logos' do
sign_in(admin)
gitlab_enable_admin_mode_sign_in(admin)
- visit admin_appearances_path
+ visit admin_application_settings_appearances_path
attach_file(:appearance_header_logo, logo_fixture)
click_button 'Update appearance settings'
@@ -141,7 +141,7 @@ RSpec.describe 'Admin Appearance' do
it 'Favicon' do
sign_in(admin)
gitlab_enable_admin_mode_sign_in(admin)
- visit admin_appearances_path
+ visit admin_application_settings_appearances_path
attach_file(:appearance_favicon, logo_fixture)
click_button 'Update appearance settings'
diff --git a/spec/features/admin/admin_search_settings_spec.rb b/spec/features/admin/admin_search_settings_spec.rb
index cd61a1db6f3..989cb7cc787 100644
--- a/spec/features/admin/admin_search_settings_spec.rb
+++ b/spec/features/admin/admin_search_settings_spec.rb
@@ -13,7 +13,7 @@ RSpec.describe 'Admin searches application settings', :js do
context 'in appearances page' do
before do
- visit(admin_appearances_path)
+ visit(admin_application_settings_appearances_path)
end
it_behaves_like 'cannot search settings'
diff --git a/spec/frontend/cycle_analytics/base_spec.js b/spec/frontend/cycle_analytics/base_spec.js
index 868a8583555..2f85cc04051 100644
--- a/spec/frontend/cycle_analytics/base_spec.js
+++ b/spec/frontend/cycle_analytics/base_spec.js
@@ -19,6 +19,9 @@ function createStore({ initialState = {} }) {
return new Vuex.Store({
state: {
...initState(),
+ permissions: {
+ [selectedStage.id]: true,
+ },
...initialState,
},
getters: {
@@ -155,7 +158,11 @@ describe('Value stream analytics component', () => {
describe('without enough permissions', () => {
beforeEach(() => {
wrapper = createComponent({
- initialState: { selectedStage: { ...selectedStage, isUserAllowed: false } },
+ initialState: {
+ permissions: {
+ [selectedStage.id]: false,
+ },
+ },
});
});
diff --git a/spec/frontend/cycle_analytics/mock_data.js b/spec/frontend/cycle_analytics/mock_data.js
index ab8bac1011e..242ea1932fb 100644
--- a/spec/frontend/cycle_analytics/mock_data.js
+++ b/spec/frontend/cycle_analytics/mock_data.js
@@ -1,3 +1,4 @@
+import { DEFAULT_VALUE_STREAM } from '~/cycle_analytics/constants';
import { convertObjectPropsToCamelCase } from '~/lib/utils/common_utils';
export const getStageByTitle = (stages, title) =>
@@ -95,54 +96,6 @@ export const rawData = {
};
export const convertedData = {
- stages: [
- selectedStage,
- {
- ...planStage,
- active: false,
- isUserAllowed: true,
- emptyStageText:
- 'The planning stage shows the time from the previous step to pushing your first commit. This time will be added automatically once you push your first commit.',
- component: 'stage-plan-component',
- slug: 'plan',
- },
- {
- ...codeStage,
- active: false,
- isUserAllowed: true,
- emptyStageText:
- 'The coding stage shows the time from the first commit to creating the merge request. The data will automatically be added here once you create your first merge request.',
- component: 'stage-code-component',
- slug: 'code',
- },
- {
- ...testStage,
- active: false,
- isUserAllowed: true,
- emptyStageText:
- 'The testing stage shows the time GitLab CI takes to run every pipeline for the related merge request. The data will automatically be added after your first pipeline finishes running.',
- component: 'stage-test-component',
- slug: 'test',
- },
- {
- ...reviewStage,
- active: false,
- isUserAllowed: true,
- emptyStageText:
- 'The review stage shows the time from creating the merge request to merging it. The data will automatically be added after you merge your first merge request.',
- component: 'stage-review-component',
- slug: 'review',
- },
- {
- ...stagingStage,
- active: false,
- isUserAllowed: true,
- emptyStageText:
- 'The staging stage shows the time between merging the MR and deploying code to the production environment. The data will be automatically added once you deploy to production for the first time.',
- component: 'stage-staging-component',
- slug: 'staging',
- },
- ],
summary: [
{ value: '20', title: 'New Issues' },
{ value: '-', title: 'Commits' },
@@ -256,3 +209,49 @@ export const transformedProjectStagePathData = [
value: 172800,
},
];
+
+export const selectedValueStream = DEFAULT_VALUE_STREAM;
+
+export const rawValueStreamStages = [
+ {
+ title: 'Issue',
+ hidden: false,
+ legend: '',
+ description: 'Time before an issue gets scheduled',
+ id: 'issue',
+ custom: false,
+ start_event_html_description:
+ '\u003cp data-sourcepos="1:1-1:13" dir="auto"\u003eIssue created\u003c/p\u003e',
+ end_event_html_description:
+ '\u003cp data-sourcepos="1:1-1:71" dir="auto"\u003eIssue first associated with a milestone or issue first added to a board\u003c/p\u003e',
+ },
+ {
+ title: 'Plan',
+ hidden: false,
+ legend: '',
+ description: 'Time before an issue starts implementation',
+ id: 'plan',
+ custom: false,
+ start_event_html_description:
+ '\u003cp data-sourcepos="1:1-1:71" dir="auto"\u003eIssue first associated with a milestone or issue first added to a board\u003c/p\u003e',
+ end_event_html_description:
+ '\u003cp data-sourcepos="1:1-1:33" dir="auto"\u003eIssue first mentioned in a commit\u003c/p\u003e',
+ },
+ {
+ title: 'Code',
+ hidden: false,
+ legend: '',
+ description: 'Time until first merge request',
+ id: 'code',
+ custom: false,
+ start_event_html_description:
+ '\u003cp data-sourcepos="1:1-1:33" dir="auto"\u003eIssue first mentioned in a commit\u003c/p\u003e',
+ end_event_html_description:
+ '\u003cp data-sourcepos="1:1-1:21" dir="auto"\u003eMerge request created\u003c/p\u003e',
+ },
+];
+
+export const valueStreamStages = rawValueStreamStages.map((s) => ({
+ ...convertObjectPropsToCamelCase(s, { deep: true }),
+ component: `stage-${s.id}-component`,
+}));
diff --git a/spec/frontend/cycle_analytics/store/actions_spec.js b/spec/frontend/cycle_analytics/store/actions_spec.js
index 55f5d720e9e..4f37e1266fb 100644
--- a/spec/frontend/cycle_analytics/store/actions_spec.js
+++ b/spec/frontend/cycle_analytics/store/actions_spec.js
@@ -3,10 +3,27 @@ import MockAdapter from 'axios-mock-adapter';
import testAction from 'helpers/vuex_action_helper';
import * as actions from '~/cycle_analytics/store/actions';
import httpStatusCodes from '~/lib/utils/http_status';
-import { selectedStage } from '../mock_data';
+import { selectedStage, selectedValueStream } from '../mock_data';
const mockRequestPath = 'some/cool/path';
+const mockFullPath = '/namespace/-/analytics/value_stream_analytics/value_streams';
const mockStartDate = 30;
+const mockRequestedDataActions = ['fetchValueStreams', 'fetchCycleAnalyticsData'];
+const mockInitializeActionCommit = {
+ payload: { requestPath: mockRequestPath },
+ type: 'INITIALIZE_VSA',
+};
+const mockSetDateActionCommit = { payload: { startDate: mockStartDate }, type: 'SET_DATE_RANGE' };
+const mockRequestedDataMutations = [
+ {
+ payload: true,
+ type: 'SET_LOADING',
+ },
+ {
+ payload: false,
+ type: 'SET_LOADING',
+ },
+];
describe('Project Value Stream Analytics actions', () => {
let state;
@@ -22,27 +39,26 @@ describe('Project Value Stream Analytics actions', () => {
state = {};
});
- it.each`
- action | type | payload | expectedActions
- ${'initializeVsa'} | ${'INITIALIZE_VSA'} | ${{ requestPath: mockRequestPath }} | ${['fetchCycleAnalyticsData']}
- ${'setDateRange'} | ${'SET_DATE_RANGE'} | ${{ startDate: 30 }} | ${[]}
- ${'setSelectedStage'} | ${'SET_SELECTED_STAGE'} | ${{ selectedStage }} | ${[]}
- `(
- '$action should dispatch $expectedActions and commit $type',
- ({ action, type, payload, expectedActions }) =>
+ const mutationTypes = (arr) => arr.map(({ type }) => type);
+
+ describe.each`
+ action | payload | expectedActions | expectedMutations
+ ${'initializeVsa'} | ${{ requestPath: mockRequestPath }} | ${mockRequestedDataActions} | ${[mockInitializeActionCommit, ...mockRequestedDataMutations]}
+ ${'setDateRange'} | ${{ startDate: mockStartDate }} | ${mockRequestedDataActions} | ${[mockSetDateActionCommit, ...mockRequestedDataMutations]}
+ ${'setSelectedStage'} | ${{ selectedStage }} | ${['fetchStageData']} | ${[{ type: 'SET_SELECTED_STAGE', payload: { selectedStage } }]}
+ ${'setSelectedValueStream'} | ${{ selectedValueStream }} | ${['fetchValueStreamStages']} | ${[{ type: 'SET_SELECTED_VALUE_STREAM', payload: { selectedValueStream } }]}
+ `('$action', ({ action, payload, expectedActions, expectedMutations }) => {
+ const types = mutationTypes(expectedMutations);
+
+ it(`will dispatch ${expectedActions} and commit ${types}`, () =>
testAction({
action: actions[action],
state,
payload,
- expectedMutations: [
- {
- type,
- payload,
- },
- ],
+ expectedMutations,
expectedActions: expectedActions.map((a) => ({ type: a })),
- }),
- );
+ }));
+ });
describe('fetchCycleAnalyticsData', () => {
beforeEach(() => {
@@ -60,7 +76,7 @@ describe('Project Value Stream Analytics actions', () => {
{ type: 'REQUEST_CYCLE_ANALYTICS_DATA' },
{ type: 'RECEIVE_CYCLE_ANALYTICS_DATA_SUCCESS' },
],
- expectedActions: [{ type: 'setSelectedStage' }, { type: 'fetchStageData' }],
+ expectedActions: [],
}));
describe('with a failing request', () => {
@@ -85,7 +101,7 @@ describe('Project Value Stream Analytics actions', () => {
});
describe('fetchStageData', () => {
- const mockStagePath = `${mockRequestPath}/events/${selectedStage.name}.json`;
+ const mockStagePath = `${mockRequestPath}/events/${selectedStage.name}`;
beforeEach(() => {
state = {
@@ -153,4 +169,115 @@ describe('Project Value Stream Analytics actions', () => {
}));
});
});
+
+ describe('fetchValueStreams', () => {
+ const mockValueStreamPath = /\/analytics\/value_stream_analytics\/value_streams/;
+
+ beforeEach(() => {
+ state = {
+ fullPath: mockFullPath,
+ };
+ mock = new MockAdapter(axios);
+ mock.onGet(mockValueStreamPath).reply(httpStatusCodes.OK);
+ });
+
+ it(`commits the 'REQUEST_VALUE_STREAMS' mutation`, () =>
+ testAction({
+ action: actions.fetchValueStreams,
+ state,
+ payload: {},
+ expectedMutations: [{ type: 'REQUEST_VALUE_STREAMS' }],
+ expectedActions: [{ type: 'receiveValueStreamsSuccess' }, { type: 'setSelectedStage' }],
+ }));
+
+ describe('with a failing request', () => {
+ beforeEach(() => {
+ mock = new MockAdapter(axios);
+ mock.onGet(mockValueStreamPath).reply(httpStatusCodes.BAD_REQUEST);
+ });
+
+ it(`commits the 'RECEIVE_VALUE_STREAMS_ERROR' mutation`, () =>
+ testAction({
+ action: actions.fetchValueStreams,
+ state,
+ payload: {},
+ expectedMutations: [
+ { type: 'REQUEST_VALUE_STREAMS' },
+ { type: 'RECEIVE_VALUE_STREAMS_ERROR', payload: httpStatusCodes.BAD_REQUEST },
+ ],
+ expectedActions: [],
+ }));
+ });
+ });
+
+ describe('receiveValueStreamsSuccess', () => {
+ const mockValueStream = {
+ id: 'mockDefault',
+ name: 'mock default',
+ };
+ const mockValueStreams = [mockValueStream, selectedValueStream];
+ it('with data, will set the first value stream', () => {
+ testAction({
+ action: actions.receiveValueStreamsSuccess,
+ state,
+ payload: mockValueStreams,
+ expectedMutations: [{ type: 'RECEIVE_VALUE_STREAMS_SUCCESS', payload: mockValueStreams }],
+ expectedActions: [{ type: 'setSelectedValueStream', payload: mockValueStream }],
+ });
+ });
+
+ it('without data, will set the default value stream', () => {
+ testAction({
+ action: actions.receiveValueStreamsSuccess,
+ state,
+ payload: [],
+ expectedMutations: [{ type: 'RECEIVE_VALUE_STREAMS_SUCCESS', payload: [] }],
+ expectedActions: [{ type: 'setSelectedValueStream', payload: selectedValueStream }],
+ });
+ });
+ });
+
+ describe('fetchValueStreamStages', () => {
+ const mockValueStreamPath = /\/analytics\/value_stream_analytics\/value_streams/;
+
+ beforeEach(() => {
+ state = {
+ fullPath: mockFullPath,
+ selectedValueStream,
+ };
+ mock = new MockAdapter(axios);
+ mock.onGet(mockValueStreamPath).reply(httpStatusCodes.OK);
+ });
+
+ it(`commits the 'REQUEST_VALUE_STREAM_STAGES' and 'RECEIVE_VALUE_STREAM_STAGES_SUCCESS' mutations`, () =>
+ testAction({
+ action: actions.fetchValueStreamStages,
+ state,
+ payload: {},
+ expectedMutations: [
+ { type: 'REQUEST_VALUE_STREAM_STAGES' },
+ { type: 'RECEIVE_VALUE_STREAM_STAGES_SUCCESS' },
+ ],
+ expectedActions: [],
+ }));
+
+ describe('with a failing request', () => {
+ beforeEach(() => {
+ mock = new MockAdapter(axios);
+ mock.onGet(mockValueStreamPath).reply(httpStatusCodes.BAD_REQUEST);
+ });
+
+ it(`commits the 'RECEIVE_VALUE_STREAM_STAGES_ERROR' mutation`, () =>
+ testAction({
+ action: actions.fetchValueStreamStages,
+ state,
+ payload: {},
+ expectedMutations: [
+ { type: 'REQUEST_VALUE_STREAM_STAGES' },
+ { type: 'RECEIVE_VALUE_STREAM_STAGES_ERROR', payload: httpStatusCodes.BAD_REQUEST },
+ ],
+ expectedActions: [],
+ }));
+ });
+ });
});
diff --git a/spec/frontend/cycle_analytics/store/mutations_spec.js b/spec/frontend/cycle_analytics/store/mutations_spec.js
index 08c70af6ef6..88e1a13f506 100644
--- a/spec/frontend/cycle_analytics/store/mutations_spec.js
+++ b/spec/frontend/cycle_analytics/store/mutations_spec.js
@@ -1,6 +1,15 @@
import * as types from '~/cycle_analytics/store/mutation_types';
import mutations from '~/cycle_analytics/store/mutations';
-import { selectedStage, rawEvents, convertedEvents, rawData, convertedData } from '../mock_data';
+import {
+ selectedStage,
+ rawEvents,
+ convertedEvents,
+ rawData,
+ convertedData,
+ selectedValueStream,
+ rawValueStreamStages,
+ valueStreamStages,
+} from '../mock_data';
let state;
const mockRequestPath = 'fake/request/path';
@@ -17,15 +26,15 @@ describe('Project Value Stream Analytics mutations', () => {
it.each`
mutation | stateKey | value
- ${types.SET_SELECTED_STAGE} | ${'isLoadingStage'} | ${false}
+ ${types.REQUEST_VALUE_STREAMS} | ${'valueStreams'} | ${[]}
+ ${types.RECEIVE_VALUE_STREAMS_ERROR} | ${'valueStreams'} | ${[]}
+ ${types.REQUEST_VALUE_STREAM_STAGES} | ${'stages'} | ${[]}
+ ${types.RECEIVE_VALUE_STREAM_STAGES_ERROR} | ${'stages'} | ${[]}
${types.REQUEST_CYCLE_ANALYTICS_DATA} | ${'isLoading'} | ${true}
- ${types.REQUEST_CYCLE_ANALYTICS_DATA} | ${'stages'} | ${[]}
${types.REQUEST_CYCLE_ANALYTICS_DATA} | ${'hasError'} | ${false}
- ${types.RECEIVE_CYCLE_ANALYTICS_DATA_SUCCESS} | ${'isLoading'} | ${false}
${types.RECEIVE_CYCLE_ANALYTICS_DATA_SUCCESS} | ${'hasError'} | ${false}
${types.RECEIVE_CYCLE_ANALYTICS_DATA_ERROR} | ${'isLoading'} | ${false}
${types.RECEIVE_CYCLE_ANALYTICS_DATA_ERROR} | ${'hasError'} | ${true}
- ${types.RECEIVE_CYCLE_ANALYTICS_DATA_ERROR} | ${'stages'} | ${[]}
${types.REQUEST_STAGE_DATA} | ${'isLoadingStage'} | ${true}
${types.REQUEST_STAGE_DATA} | ${'isEmptyStage'} | ${false}
${types.REQUEST_STAGE_DATA} | ${'hasError'} | ${false}
@@ -44,12 +53,15 @@ describe('Project Value Stream Analytics mutations', () => {
});
it.each`
- mutation | payload | stateKey | value
- ${types.INITIALIZE_VSA} | ${{ requestPath: mockRequestPath }} | ${'requestPath'} | ${mockRequestPath}
- ${types.SET_SELECTED_STAGE} | ${selectedStage} | ${'selectedStage'} | ${selectedStage}
- ${types.SET_DATE_RANGE} | ${{ startDate: mockStartData }} | ${'startDate'} | ${mockStartData}
- ${types.RECEIVE_CYCLE_ANALYTICS_DATA_SUCCESS} | ${rawData} | ${'stages'} | ${convertedData.stages}
- ${types.RECEIVE_CYCLE_ANALYTICS_DATA_SUCCESS} | ${rawData} | ${'summary'} | ${convertedData.summary}
+ mutation | payload | stateKey | value
+ ${types.INITIALIZE_VSA} | ${{ requestPath: mockRequestPath }} | ${'requestPath'} | ${mockRequestPath}
+ ${types.SET_DATE_RANGE} | ${{ startDate: mockStartData }} | ${'startDate'} | ${mockStartData}
+ ${types.SET_LOADING} | ${true} | ${'isLoading'} | ${true}
+ ${types.SET_LOADING} | ${false} | ${'isLoading'} | ${false}
+ ${types.SET_SELECTED_VALUE_STREAM} | ${selectedValueStream} | ${'selectedValueStream'} | ${selectedValueStream}
+ ${types.RECEIVE_CYCLE_ANALYTICS_DATA_SUCCESS} | ${rawData} | ${'summary'} | ${convertedData.summary}
+ ${types.RECEIVE_VALUE_STREAMS_SUCCESS} | ${[selectedValueStream]} | ${'valueStreams'} | ${[selectedValueStream]}
+ ${types.RECEIVE_VALUE_STREAM_STAGES_SUCCESS} | ${{ stages: rawValueStreamStages }} | ${'stages'} | ${valueStreamStages}
`(
'$mutation with $payload will set $stateKey to $value',
({ mutation, payload, stateKey, value }) => {
diff --git a/spec/frontend/cycle_analytics/utils_spec.js b/spec/frontend/cycle_analytics/utils_spec.js
index 2d9d2f5b5b1..15137bb0571 100644
--- a/spec/frontend/cycle_analytics/utils_spec.js
+++ b/spec/frontend/cycle_analytics/utils_spec.js
@@ -53,17 +53,6 @@ describe('Value stream analytics utils', () => {
expect(result.summary).toEqual(convertedData.summary);
});
- it('returns the stages data', () => {
- expect(result.stages).toEqual(convertedData.stages);
- });
-
- it('returns each of the default value stream stages', () => {
- const stages = result.stages.map(({ name }) => name);
- ['issue', 'plan', 'code', 'test', 'review', 'staging'].forEach((stageName) => {
- expect(stages).toContain(stageName);
- });
- });
-
it('returns `-` for summary data that has no value', () => {
const singleSummaryResult = decorateData({
stats: [],
@@ -72,24 +61,6 @@ describe('Value stream analytics utils', () => {
});
expect(singleSummaryResult.summary).toEqual([{ value: '-', title: 'Commits' }]);
});
-
- it('returns additional fields for each stage', () => {
- const singleStageResult = decorateData({
- stats: [{ name: 'issue', value: null }],
- permissions: { issue: false },
- });
- const stage = singleStageResult.stages[0];
- const txt =
- 'The issue stage shows the time it takes from creating an issue to assigning the issue to a milestone, or add the issue to a list on your Issue Board. Begin creating issues to see data for this stage.';
-
- expect(stage).toMatchObject({
- active: false,
- isUserAllowed: false,
- emptyStageText: txt,
- slug: 'issue',
- component: 'stage-issue-component',
- });
- });
});
describe('transformStagesForPathNavigation', () => {
diff --git a/spec/frontend/lib/utils/url_utility_spec.js b/spec/frontend/lib/utils/url_utility_spec.js
index 8a04e93e88c..bffe108896a 100644
--- a/spec/frontend/lib/utils/url_utility_spec.js
+++ b/spec/frontend/lib/utils/url_utility_spec.js
@@ -650,45 +650,24 @@ describe('URL utility', () => {
});
describe('queryToObject', () => {
- it('converts search query into an object', () => {
- const searchQuery = '?one=1&two=2';
-
- expect(urlUtils.queryToObject(searchQuery)).toEqual({ one: '1', two: '2' });
- });
-
- it('removes undefined values from the search query', () => {
- const searchQuery = '?one=1&two=2&three';
-
- expect(urlUtils.queryToObject(searchQuery)).toEqual({ one: '1', two: '2' });
- });
-
- describe('with gatherArrays=false', () => {
- it('overwrites values with the same array-key and does not change the key', () => {
- const searchQuery = '?one[]=1&one[]=2&two=2&two=3';
-
- expect(urlUtils.queryToObject(searchQuery)).toEqual({ 'one[]': '2', two: '3' });
- });
- });
-
- describe('with gatherArrays=true', () => {
- const options = { gatherArrays: true };
- it('gathers only values with the same array-key and strips `[]` from the key', () => {
- const searchQuery = '?one[]=1&one[]=2&two=2&two=3';
-
- expect(urlUtils.queryToObject(searchQuery, options)).toEqual({ one: ['1', '2'], two: '3' });
- });
-
- it('overwrites values with the same array-key name', () => {
- const searchQuery = '?one=1&one[]=2&two=2&two=3';
-
- expect(urlUtils.queryToObject(searchQuery, options)).toEqual({ one: ['2'], two: '3' });
- });
-
- it('overwrites values with the same key name', () => {
- const searchQuery = '?one[]=1&one=2&two=2&two=3';
-
- expect(urlUtils.queryToObject(searchQuery, options)).toEqual({ one: '2', two: '3' });
- });
+ it.each`
+ case | query | options | result
+ ${'converts query'} | ${'?one=1&two=2'} | ${undefined} | ${{ one: '1', two: '2' }}
+ ${'converts query without ?'} | ${'one=1&two=2'} | ${undefined} | ${{ one: '1', two: '2' }}
+ ${'removes undefined values'} | ${'?one=1&two=2&three'} | ${undefined} | ${{ one: '1', two: '2' }}
+ ${'overwrites values with same key and does not change key'} | ${'?one[]=1&one[]=2&two=2&two=3'} | ${undefined} | ${{ 'one[]': '2', two: '3' }}
+ ${'gathers values with the same array-key, strips `[]` from key'} | ${'?one[]=1&one[]=2&two=2&two=3'} | ${{ gatherArrays: true }} | ${{ one: ['1', '2'], two: '3' }}
+ ${'overwrites values with the same array-key name'} | ${'?one=1&one[]=2&two=2&two=3'} | ${{ gatherArrays: true }} | ${{ one: ['2'], two: '3' }}
+ ${'overwrites values with the same key name'} | ${'?one[]=1&one=2&two=2&two=3'} | ${{ gatherArrays: true }} | ${{ one: '2', two: '3' }}
+ ${'ignores plus symbols'} | ${'?search=a+b'} | ${{ legacySpacesDecode: true }} | ${{ search: 'a+b' }}
+ ${'ignores plus symbols in keys'} | ${'?search+term=a'} | ${{ legacySpacesDecode: true }} | ${{ 'search+term': 'a' }}
+ ${'ignores plus symbols when gathering arrays'} | ${'?search[]=a+b'} | ${{ gatherArrays: true, legacySpacesDecode: true }} | ${{ search: ['a+b'] }}
+ ${'replaces plus symbols with spaces'} | ${'?search=a+b'} | ${undefined} | ${{ search: 'a b' }}
+ ${'replaces plus symbols in keys with spaces'} | ${'?search+term=a'} | ${undefined} | ${{ 'search term': 'a' }}
+ ${'replaces plus symbols when gathering arrays'} | ${'?search[]=a+b'} | ${{ gatherArrays: true }} | ${{ search: ['a b'] }}
+ ${'replaces plus symbols when gathering arrays for values with same key'} | ${'?search[]=a+b&search[]=c+d'} | ${{ gatherArrays: true }} | ${{ search: ['a b', 'c d'] }}
+ `('$case', ({ query, options, result }) => {
+ expect(urlUtils.queryToObject(query, options)).toEqual(result);
});
});
diff --git a/spec/lib/bulk_imports/clients/http_spec.rb b/spec/lib/bulk_imports/clients/http_spec.rb
index b84fa1538ef..ac42f12a3d4 100644
--- a/spec/lib/bulk_imports/clients/http_spec.rb
+++ b/spec/lib/bulk_imports/clients/http_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe BulkImports::Clients::Http do
+RSpec.describe BulkImports::Clients::HTTP do
include ImportSpecHelper
let(:uri) { 'http://gitlab.example' }
diff --git a/spec/lib/bulk_imports/common/extractors/rest_extractor_spec.rb b/spec/lib/bulk_imports/common/extractors/rest_extractor_spec.rb
index 721dacbe3f4..5ee5cdbe413 100644
--- a/spec/lib/bulk_imports/common/extractors/rest_extractor_spec.rb
+++ b/spec/lib/bulk_imports/common/extractors/rest_extractor_spec.rb
@@ -3,7 +3,7 @@
require 'spec_helper'
RSpec.describe BulkImports::Common::Extractors::RestExtractor do
- let(:http_client) { instance_double(BulkImports::Clients::Http) }
+ let(:http_client) { instance_double(BulkImports::Clients::HTTP) }
let(:options) { { query: double(to_h: { resource: nil, query: nil }) } }
let(:response) { double(parsed_response: { 'data' => { 'foo' => 'bar' } }, headers: { 'x-next-page' => '2' }) }
diff --git a/spec/lib/bulk_imports/groups/extractors/subgroups_extractor_spec.rb b/spec/lib/bulk_imports/groups/extractors/subgroups_extractor_spec.rb
index ac8786440e9..f7485b188ce 100644
--- a/spec/lib/bulk_imports/groups/extractors/subgroups_extractor_spec.rb
+++ b/spec/lib/bulk_imports/groups/extractors/subgroups_extractor_spec.rb
@@ -12,7 +12,7 @@ RSpec.describe BulkImports::Groups::Extractors::SubgroupsExtractor do
response = [{ 'test' => 'group' }]
context = BulkImports::Pipeline::Context.new(tracker)
- allow_next_instance_of(BulkImports::Clients::Http) do |client|
+ allow_next_instance_of(BulkImports::Clients::HTTP) do |client|
allow(client).to receive(:each_page).and_return(response)
end
diff --git a/spec/migrations/migrate_protected_attribute_to_pending_builds_spec.rb b/spec/migrations/migrate_protected_attribute_to_pending_builds_spec.rb
new file mode 100644
index 00000000000..e838476a650
--- /dev/null
+++ b/spec/migrations/migrate_protected_attribute_to_pending_builds_spec.rb
@@ -0,0 +1,33 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+require Rails.root.join('db', 'post_migrate', '20210610102413_migrate_protected_attribute_to_pending_builds.rb')
+
+RSpec.describe MigrateProtectedAttributeToPendingBuilds do
+ let(:namespaces) { table(:namespaces) }
+ let(:projects) { table(:projects) }
+ let(:queue) { table(:ci_pending_builds) }
+ let(:builds) { table(:ci_builds) }
+
+ before do
+ namespaces.create!(id: 123, name: 'sample', path: 'sample')
+ projects.create!(id: 123, name: 'sample', path: 'sample', namespace_id: 123)
+
+ builds.create!(id: 1, project_id: 123, status: 'pending', protected: false, type: 'Ci::Build')
+ builds.create!(id: 2, project_id: 123, status: 'pending', protected: true, type: 'Ci::Build')
+ builds.create!(id: 3, project_id: 123, status: 'pending', protected: false, type: 'Ci::Build')
+ builds.create!(id: 4, project_id: 123, status: 'pending', protected: true, type: 'Ci::Bridge')
+ builds.create!(id: 5, project_id: 123, status: 'success', protected: true, type: 'Ci::Build')
+
+ queue.create!(id: 1, project_id: 123, build_id: 1)
+ queue.create!(id: 2, project_id: 123, build_id: 2)
+ queue.create!(id: 3, project_id: 123, build_id: 3)
+ end
+
+ it 'updates entries that should be protected' do
+ migrate!
+
+ expect(queue.where(protected: true).count).to eq 1
+ expect(queue.find_by(protected: true).id).to eq 2
+ end
+end
diff --git a/spec/models/bulk_imports/export_status_spec.rb b/spec/models/bulk_imports/export_status_spec.rb
index fde18705e35..48f32a2092a 100644
--- a/spec/models/bulk_imports/export_status_spec.rb
+++ b/spec/models/bulk_imports/export_status_spec.rb
@@ -16,7 +16,7 @@ RSpec.describe BulkImports::ExportStatus do
subject { described_class.new(tracker, relation) }
before do
- allow_next_instance_of(BulkImports::Clients::Http) do |client|
+ allow_next_instance_of(BulkImports::Clients::HTTP) do |client|
allow(client).to receive(:get).and_return(response_double)
end
end
@@ -66,7 +66,7 @@ RSpec.describe BulkImports::ExportStatus do
context 'when something goes wrong during export status fetch' do
it 'returns exception class as error' do
- allow_next_instance_of(BulkImports::Clients::Http) do |client|
+ allow_next_instance_of(BulkImports::Clients::HTTP) do |client|
allow(client).to receive(:get).and_raise(StandardError, 'Error!')
end
diff --git a/spec/models/ci/build_spec.rb b/spec/models/ci/build_spec.rb
index c8430d669b8..600ddb784c5 100644
--- a/spec/models/ci/build_spec.rb
+++ b/spec/models/ci/build_spec.rb
@@ -493,6 +493,34 @@ RSpec.describe Ci::Build do
expect(build.queuing_entry).to be_present
end
end
+
+ context 'when build has been picked by a shared runner' do
+ let(:build) { create(:ci_build, :pending) }
+
+ it 'creates runtime metadata entry' do
+ build.runner = create(:ci_runner, :instance_type)
+
+ build.run!
+
+ expect(build.reload.runtime_metadata).to be_present
+ end
+ end
+ end
+
+ describe '#drop' do
+ context 'when has a runtime tracking entry' do
+ let(:build) { create(:ci_build, :pending) }
+
+ it 'removes runtime tracking entry' do
+ build.runner = create(:ci_runner, :instance_type)
+
+ build.run!
+ expect(build.reload.runtime_metadata).to be_present
+
+ build.drop!
+ expect(build.reload.runtime_metadata).not_to be_present
+ end
+ end
end
describe '#schedulable?' do
@@ -5181,4 +5209,34 @@ RSpec.describe Ci::Build do
it { expect(matcher.project).to eq(build.project) }
end
+
+ describe '#shared_runner_build?' do
+ context 'when build does not have a runner assigned' do
+ it 'is not a shared runner build' do
+ expect(build.runner).to be_nil
+
+ expect(build).not_to be_shared_runner_build
+ end
+ end
+
+ context 'when build has a project runner assigned' do
+ before do
+ build.runner = create(:ci_runner, :project)
+ end
+
+ it 'is not a shared runner build' do
+ expect(build).not_to be_shared_runner_build
+ end
+ end
+
+ context 'when build has an instance runner assigned' do
+ before do
+ build.runner = create(:ci_runner, :instance_type)
+ end
+
+ it 'is a shared runner build' do
+ expect(build).to be_shared_runner_build
+ end
+ end
+ end
end
diff --git a/spec/models/ci/pending_build_spec.rb b/spec/models/ci/pending_build_spec.rb
index 5d52ea606e3..c1d4f4b0a5e 100644
--- a/spec/models/ci/pending_build_spec.rb
+++ b/spec/models/ci/pending_build_spec.rb
@@ -20,7 +20,7 @@ RSpec.describe Ci::PendingBuild do
context 'when another queuing entry exists for given build' do
before do
- described_class.create!(build: build, project: project)
+ described_class.create!(build: build, project: project, protected: false)
end
it 'returns a build id as a result' do
diff --git a/spec/models/ci/running_build_spec.rb b/spec/models/ci/running_build_spec.rb
new file mode 100644
index 00000000000..589e5a86f4d
--- /dev/null
+++ b/spec/models/ci/running_build_spec.rb
@@ -0,0 +1,55 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+
+RSpec.describe Ci::RunningBuild do
+ let_it_be(:project) { create(:project) }
+ let_it_be(:pipeline) { create(:ci_pipeline, project: project) }
+
+ let(:runner) { create(:ci_runner, :instance_type) }
+ let(:build) { create(:ci_build, :running, runner: runner, pipeline: pipeline) }
+
+ describe '.upsert_shared_runner_build!' do
+ context 'another pending entry does not exist' do
+ it 'creates a new pending entry' do
+ result = described_class.upsert_shared_runner_build!(build)
+
+ expect(result.rows.dig(0, 0)).to eq build.id
+ expect(build.reload.runtime_metadata).to be_present
+ end
+ end
+
+ context 'when another queuing entry exists for given build' do
+ before do
+ described_class.create!(build: build,
+ project: project,
+ runner: runner,
+ runner_type: runner.runner_type)
+ end
+
+ it 'returns a build id as a result' do
+ result = described_class.upsert_shared_runner_build!(build)
+
+ expect(result.rows.dig(0, 0)).to eq build.id
+ end
+ end
+
+ context 'when build has been picked by a specific runner' do
+ let(:runner) { create(:ci_runner, :project) }
+
+ it 'raises an error' do
+ expect { described_class.upsert_shared_runner_build!(build) }
+ .to raise_error(ArgumentError, 'build has not been picked by a shared runner')
+ end
+ end
+
+ context 'when build has not been picked by a runner yet' do
+ let(:build) { create(:ci_build, pipeline: pipeline) }
+
+ it 'raises an error' do
+ expect { described_class.upsert_shared_runner_build!(build) }
+ .to raise_error(ArgumentError, 'build has not been picked by a shared runner')
+ end
+ end
+ end
+end
diff --git a/spec/models/concerns/issuable_spec.rb b/spec/models/concerns/issuable_spec.rb
index b3dc486081c..7b100b7a6f3 100644
--- a/spec/models/concerns/issuable_spec.rb
+++ b/spec/models/concerns/issuable_spec.rb
@@ -841,7 +841,7 @@ RSpec.describe Issuable do
it_behaves_like 'matches_cross_reference_regex? fails fast'
end
- context "note with long path string", quarantine: 'https://gitlab.com/gitlab-org/gitlab/-/issues/35269' do
+ context "note with long path string" do
let(:project) { create(:project, :public, :repository) }
let(:mentionable) { project.commit }
diff --git a/spec/models/deploy_token_spec.rb b/spec/models/deploy_token_spec.rb
index c8917a7dd65..dfc37f9e661 100644
--- a/spec/models/deploy_token_spec.rb
+++ b/spec/models/deploy_token_spec.rb
@@ -248,68 +248,54 @@ RSpec.describe DeployToken do
deploy_token.groups << group
end
- context 'and the allow_group_deploy_token feature flag is turned off' do
- it 'is false' do
- stub_feature_flags(allow_group_deploy_token: false)
-
- is_expected.to be_falsy
- end
+ context 'and the passed-in project does not belong to any group' do
+ it { is_expected.to be_falsy }
end
- context 'and the allow_group_deploy_token feature flag is turned on' do
- before do
- stub_feature_flags(allow_group_deploy_token: true)
- end
+ context 'and the passed-in project belongs to the token group' do
+ it 'is true' do
+ group.projects << project
- context 'and the passed-in project does not belong to any group' do
- it { is_expected.to be_falsy }
+ is_expected.to be_truthy
end
+ end
- context 'and the passed-in project belongs to the token group' do
- it 'is true' do
- group.projects << project
+ context 'and the passed-in project belongs to a subgroup' do
+ let(:child_group) { create(:group, parent_id: group.id) }
+ let(:grandchild_group) { create(:group, parent_id: child_group.id) }
- is_expected.to be_truthy
- end
+ before do
+ grandchild_group.projects << project
end
- context 'and the passed-in project belongs to a subgroup' do
- let(:child_group) { create(:group, parent_id: group.id) }
- let(:grandchild_group) { create(:group, parent_id: child_group.id) }
-
- before do
- grandchild_group.projects << project
- end
-
- context 'and the token group is an ancestor (grand-parent) of this group' do
- it { is_expected.to be_truthy }
- end
+ context 'and the token group is an ancestor (grand-parent) of this group' do
+ it { is_expected.to be_truthy }
+ end
- context 'and the token group is not ancestor of this group' do
- let(:child2_group) { create(:group, parent_id: group.id) }
+ context 'and the token group is not ancestor of this group' do
+ let(:child2_group) { create(:group, parent_id: group.id) }
- it 'is false' do
- deploy_token.groups = [child2_group]
+ it 'is false' do
+ deploy_token.groups = [child2_group]
- is_expected.to be_falsey
- end
+ is_expected.to be_falsey
end
end
+ end
- context 'and the passed-in project does not belong to the token group' do
- it { is_expected.to be_falsy }
- end
+ context 'and the passed-in project does not belong to the token group' do
+ it { is_expected.to be_falsy }
+ end
- context 'and the project belongs to a group that is parent of the token group' do
- let(:super_group) { create(:group) }
- let(:deploy_token) { create(:deploy_token, :group) }
- let(:group) { create(:group, parent_id: super_group.id) }
+ context 'and the project belongs to a group that is parent of the token group' do
+ let(:super_group) { create(:group) }
+ let(:deploy_token) { create(:deploy_token, :group) }
+ let(:group) { create(:group, parent_id: super_group.id) }
- it 'is false' do
- super_group.projects << project
+ it 'is false' do
+ super_group.projects << project
- is_expected.to be_falsey
- end
+ is_expected.to be_falsey
end
end
end
diff --git a/spec/requests/api/projects_spec.rb b/spec/requests/api/projects_spec.rb
index 6ec48f88e93..b62f8e6b259 100644
--- a/spec/requests/api/projects_spec.rb
+++ b/spec/requests/api/projects_spec.rb
@@ -109,6 +109,43 @@ RSpec.describe API::Projects do
end
end
+ shared_examples_for 'create project with default branch parameter' do
+ let(:params) { { name: 'Foo Project', initialize_with_readme: true, default_branch: default_branch } }
+ let(:default_branch) { 'main' }
+
+ it 'creates project with provided default branch name' do
+ expect { request }.to change { Project.count }.by(1)
+ expect(response).to have_gitlab_http_status(:created)
+
+ project = Project.find(json_response['id'])
+ expect(project.default_branch).to eq(default_branch)
+ end
+
+ context 'when branch name is empty' do
+ let(:default_branch) { '' }
+
+ it 'creates project with a default project branch name' do
+ expect { request }.to change { Project.count }.by(1)
+ expect(response).to have_gitlab_http_status(:created)
+
+ project = Project.find(json_response['id'])
+ expect(project.default_branch).to eq('master')
+ end
+ end
+
+ context 'when initialize with readme is not set' do
+ let(:params) { super().merge(initialize_with_readme: nil) }
+
+ it 'creates project with a default project branch name' do
+ expect { request }.to change { Project.count }.by(1)
+ expect(response).to have_gitlab_http_status(:created)
+
+ project = Project.find(json_response['id'])
+ expect(project.default_branch).to be_nil
+ end
+ end
+ end
+
describe 'GET /projects' do
shared_examples_for 'projects response' do
it 'returns an array of projects' do
@@ -947,6 +984,10 @@ RSpec.describe API::Projects do
expect(project.path).to eq('path-project-Foo')
end
+ it_behaves_like 'create project with default branch parameter' do
+ let(:request) { post api('/projects', user), params: params }
+ end
+
it 'creates last project before reaching project limit' do
allow_any_instance_of(User).to receive(:projects_limit_left).and_return(1)
post api('/projects', user2), params: { name: 'foo' }
@@ -1427,6 +1468,10 @@ RSpec.describe API::Projects do
expect(project.path).to eq('path-project-Foo')
end
+ it_behaves_like 'create project with default branch parameter' do
+ let(:request) { post api("/projects/user/#{user.id}", admin), params: params }
+ end
+
it 'responds with 400 on failure and not project' do
expect { post api("/projects/user/#{user.id}", admin) }
.not_to change { Project.count }
diff --git a/spec/services/bulk_imports/file_download_service_spec.rb b/spec/services/bulk_imports/file_download_service_spec.rb
index 5171bb40f0a..0961ddce553 100644
--- a/spec/services/bulk_imports/file_download_service_spec.rb
+++ b/spec/services/bulk_imports/file_download_service_spec.rb
@@ -26,7 +26,7 @@ RSpec.describe BulkImports::FileDownloadService do
subject { described_class.new(configuration: config, relative_url: '/test', dir: tmpdir, filename: filename) }
before do
- allow_next_instance_of(BulkImports::Clients::Http) do |client|
+ allow_next_instance_of(BulkImports::Clients::HTTP) do |client|
allow(client).to receive(:head).and_return(response_double)
allow(client).to receive(:stream).and_yield(chunk_double)
end
diff --git a/spec/services/ci/create_pipeline_service/needs_spec.rb b/spec/services/ci/create_pipeline_service/needs_spec.rb
index 3f1a2854d76..3b4a6178b8f 100644
--- a/spec/services/ci/create_pipeline_service/needs_spec.rb
+++ b/spec/services/ci/create_pipeline_service/needs_spec.rb
@@ -202,37 +202,21 @@ RSpec.describe Ci::CreatePipelineService do
YAML
end
- context 'when there are runners matching the builds' do
- before do
- create(:ci_runner, :online)
- end
-
- it 'creates a pipeline with build_a and test_b pending; deploy_b manual', :sidekiq_inline do
- processables = pipeline.processables
-
- build_a = processables.find { |processable| processable.name == 'build_a' }
- test_a = processables.find { |processable| processable.name == 'test_a' }
- test_b = processables.find { |processable| processable.name == 'test_b' }
- deploy_a = processables.find { |processable| processable.name == 'deploy_a' }
- deploy_b = processables.find { |processable| processable.name == 'deploy_b' }
-
- expect(pipeline).to be_created_successfully
- expect(build_a.status).to eq('pending')
- expect(test_a.status).to eq('created')
- expect(test_b.status).to eq('pending')
- expect(deploy_a.status).to eq('created')
- expect(deploy_b.status).to eq('manual')
- end
- end
-
- context 'when there are no runners matching the builds' do
- it 'creates a pipeline but all jobs failed', :sidekiq_inline do
- processables = pipeline.processables
-
- expect(pipeline).to be_created_successfully
- expect(processables).to all be_failed
- expect(processables.map(&:failure_reason)).to all eq('no_matching_runner')
- end
+ it 'creates a pipeline with build_a and test_b pending; deploy_b manual', :sidekiq_inline do
+ processables = pipeline.processables
+
+ build_a = processables.find { |processable| processable.name == 'build_a' }
+ test_a = processables.find { |processable| processable.name == 'test_a' }
+ test_b = processables.find { |processable| processable.name == 'test_b' }
+ deploy_a = processables.find { |processable| processable.name == 'deploy_a' }
+ deploy_b = processables.find { |processable| processable.name == 'deploy_b' }
+
+ expect(pipeline).to be_created_successfully
+ expect(build_a.status).to eq('pending')
+ expect(test_a.status).to eq('created')
+ expect(test_b.status).to eq('pending')
+ expect(deploy_a.status).to eq('created')
+ expect(deploy_b.status).to eq('manual')
end
end
diff --git a/spec/services/ci/pipeline_creation/drop_not_runnable_builds_service_spec.rb b/spec/services/ci/pipeline_creation/drop_not_runnable_builds_service_spec.rb
deleted file mode 100644
index 9b525b39afd..00000000000
--- a/spec/services/ci/pipeline_creation/drop_not_runnable_builds_service_spec.rb
+++ /dev/null
@@ -1,80 +0,0 @@
-# frozen_string_literal: true
-
-require 'spec_helper'
-
-RSpec.describe Ci::PipelineCreation::DropNotRunnableBuildsService do
- let_it_be(:group) { create(:group) }
- let_it_be(:project) { create(:project, group: group) }
-
- let_it_be_with_reload(:pipeline) do
- create(:ci_pipeline, project: project, status: :created)
- end
-
- let_it_be_with_reload(:job) do
- create(:ci_build, project: project, pipeline: pipeline)
- end
-
- describe '#execute' do
- subject(:execute) { described_class.new(pipeline).execute }
-
- shared_examples 'jobs allowed to run' do
- it 'does not drop the jobs' do
- expect { execute }.not_to change { job.reload.status }
- end
- end
-
- context 'when the feature flag is disabled' do
- before do
- stub_feature_flags(ci_drop_new_builds_when_ci_quota_exceeded: false)
- end
-
- it_behaves_like 'jobs allowed to run'
- end
-
- context 'when the pipeline status is running' do
- before do
- pipeline.update!(status: :running)
- end
-
- it_behaves_like 'jobs allowed to run'
- end
-
- context 'when there are no runners available' do
- let_it_be(:offline_project_runner) do
- create(:ci_runner, runner_type: :project_type, projects: [project])
- end
-
- it 'drops the job' do
- execute
- job.reload
-
- expect(job).to be_failed
- expect(job.failure_reason).to eq('no_matching_runner')
- end
- end
-
- context 'with project runners' do
- let_it_be(:project_runner) do
- create(:ci_runner, :online, runner_type: :project_type, projects: [project])
- end
-
- it_behaves_like 'jobs allowed to run'
- end
-
- context 'with group runners' do
- let_it_be(:group_runner) do
- create(:ci_runner, :online, runner_type: :group_type, groups: [group])
- end
-
- it_behaves_like 'jobs allowed to run'
- end
-
- context 'with instance runners' do
- let_it_be(:instance_runner) do
- create(:ci_runner, :online, runner_type: :instance_type)
- end
-
- it_behaves_like 'jobs allowed to run'
- end
- end
-end
diff --git a/spec/services/ci/pipeline_creation/start_pipeline_service_spec.rb b/spec/services/ci/pipeline_creation/start_pipeline_service_spec.rb
index d399843923c..2aa810e8ea1 100644
--- a/spec/services/ci/pipeline_creation/start_pipeline_service_spec.rb
+++ b/spec/services/ci/pipeline_creation/start_pipeline_service_spec.rb
@@ -8,15 +8,6 @@ RSpec.describe Ci::PipelineCreation::StartPipelineService do
subject(:service) { described_class.new(pipeline) }
describe '#execute' do
- it 'calls the pipeline runners matching validation service' do
- expect(Ci::PipelineCreation::DropNotRunnableBuildsService)
- .to receive(:new)
- .with(pipeline)
- .and_return(double('service', execute: true))
-
- service.execute
- end
-
it 'calls the pipeline process service' do
expect(Ci::ProcessPipelineService)
.to receive(:new)
diff --git a/spec/services/ci/retry_build_service_spec.rb b/spec/services/ci/retry_build_service_spec.rb
index f047bf649fb..880adc80b24 100644
--- a/spec/services/ci/retry_build_service_spec.rb
+++ b/spec/services/ci/retry_build_service_spec.rb
@@ -60,7 +60,7 @@ RSpec.describe Ci::RetryBuildService do
artifacts_file artifacts_metadata artifacts_size commands
resource resource_group_id processed security_scans author
pipeline_id report_results pending_state pages_deployments
- queuing_entry].freeze
+ queuing_entry runtime_metadata].freeze
shared_examples 'build duplication' do
let_it_be(:another_pipeline) { create(:ci_empty_pipeline, project: project) }
diff --git a/spec/services/ci/update_build_queue_service_spec.rb b/spec/services/ci/update_build_queue_service_spec.rb
index 5458280ad9d..44d7809b85f 100644
--- a/spec/services/ci/update_build_queue_service_spec.rb
+++ b/spec/services/ci/update_build_queue_service_spec.rb
@@ -4,101 +4,208 @@ require 'spec_helper'
RSpec.describe Ci::UpdateBuildQueueService do
let(:project) { create(:project, :repository) }
- let(:build) { create(:ci_build, pipeline: pipeline) }
let(:pipeline) { create(:ci_pipeline, project: project) }
+ let(:build) { create(:ci_build, pipeline: pipeline) }
- describe '#push' do
- let(:transition) { double('transition') }
+ describe 'pending builds queue push / pop' do
+ describe '#push' do
+ let(:transition) { double('transition') }
- before do
- allow(transition).to receive(:to).and_return('pending')
- allow(transition).to receive(:within_transaction).and_yield
- end
+ before do
+ allow(transition).to receive(:to).and_return('pending')
+ allow(transition).to receive(:within_transaction).and_yield
+ end
- context 'when pending build can be created' do
- it 'creates a new pending build in transaction' do
- queued = subject.push(build, transition)
+ context 'when pending build can be created' do
+ it 'creates a new pending build in transaction' do
+ queued = subject.push(build, transition)
- expect(queued).to eq build.id
- end
+ expect(queued).to eq build.id
+ end
- it 'increments queue push metric' do
- metrics = spy('metrics')
+ it 'increments queue push metric' do
+ metrics = spy('metrics')
- described_class.new(metrics).push(build, transition)
+ described_class.new(metrics).push(build, transition)
- expect(metrics)
- .to have_received(:increment_queue_operation)
- .with(:build_queue_push)
+ expect(metrics)
+ .to have_received(:increment_queue_operation)
+ .with(:build_queue_push)
+ end
end
- end
- context 'when invalid transition is detected' do
- it 'raises an error' do
- allow(transition).to receive(:to).and_return('created')
+ context 'when invalid transition is detected' do
+ it 'raises an error' do
+ allow(transition).to receive(:to).and_return('created')
- expect { subject.push(build, transition) }
- .to raise_error(described_class::InvalidQueueTransition)
+ expect { subject.push(build, transition) }
+ .to raise_error(described_class::InvalidQueueTransition)
+ end
+ end
+
+ context 'when duplicate entry exists' do
+ before do
+ ::Ci::PendingBuild.create!(build: build, project: project)
+ end
+
+ it 'does nothing and returns build id' do
+ queued = subject.push(build, transition)
+
+ expect(queued).to eq build.id
+ end
end
end
- context 'when duplicate entry exists' do
+ describe '#pop' do
+ let(:transition) { double('transition') }
+
before do
- ::Ci::PendingBuild.create!(build: build, project: project)
+ allow(transition).to receive(:from).and_return('pending')
+ allow(transition).to receive(:within_transaction).and_yield
end
- it 'does nothing and returns build id' do
- queued = subject.push(build, transition)
+ context 'when pending build exists' do
+ before do
+ Ci::PendingBuild.create!(build: build, project: project)
+ end
- expect(queued).to eq build.id
+ it 'removes pending build in a transaction' do
+ dequeued = subject.pop(build, transition)
+
+ expect(dequeued).to eq build.id
+ end
+
+ it 'increments queue pop metric' do
+ metrics = spy('metrics')
+
+ described_class.new(metrics).pop(build, transition)
+
+ expect(metrics)
+ .to have_received(:increment_queue_operation)
+ .with(:build_queue_pop)
+ end
+ end
+
+ context 'when pending build does not exist' do
+ it 'does nothing if there is no pending build to remove' do
+ dequeued = subject.pop(build, transition)
+
+ expect(dequeued).to be_nil
+ end
+ end
+
+ context 'when invalid transition is detected' do
+ it 'raises an error' do
+ allow(transition).to receive(:from).and_return('created')
+
+ expect { subject.pop(build, transition) }
+ .to raise_error(described_class::InvalidQueueTransition)
+ end
end
end
end
- describe '#pop' do
- let(:transition) { double('transition') }
+ describe 'shared runner builds tracking' do
+ let(:runner) { create(:ci_runner, :instance_type) }
+ let(:build) { create(:ci_build, runner: runner, pipeline: pipeline) }
- before do
- allow(transition).to receive(:from).and_return('pending')
- allow(transition).to receive(:within_transaction).and_yield
- end
+ describe '#track' do
+ let(:transition) { double('transition') }
- context 'when pending build exists' do
before do
- Ci::PendingBuild.create!(build: build, project: project)
+ allow(transition).to receive(:to).and_return('running')
+ allow(transition).to receive(:within_transaction).and_yield
end
- it 'removes pending build in a transaction' do
- dequeued = subject.pop(build, transition)
+ context 'when a shared runner build can be tracked' do
+ it 'creates a new shared runner build tracking entry' do
+ build_id = subject.track(build, transition)
+
+ expect(build_id).to eq build.id
+ end
+
+ it 'increments new shared runner build metric' do
+ metrics = spy('metrics')
+
+ described_class.new(metrics).track(build, transition)
- expect(dequeued).to eq build.id
+ expect(metrics)
+ .to have_received(:increment_queue_operation)
+ .with(:shared_runner_build_new)
+ end
end
- it 'increments queue pop metric' do
- metrics = spy('metrics')
+ context 'when invalid transition is detected' do
+ it 'raises an error' do
+ allow(transition).to receive(:to).and_return('pending')
- described_class.new(metrics).pop(build, transition)
+ expect { subject.track(build, transition) }
+ .to raise_error(described_class::InvalidQueueTransition)
+ end
+ end
- expect(metrics)
- .to have_received(:increment_queue_operation)
- .with(:build_queue_pop)
+ context 'when duplicate entry exists' do
+ before do
+ ::Ci::RunningBuild.create!(
+ build: build, project: project, runner: runner, runner_type: runner.runner_type
+ )
+ end
+
+ it 'does nothing and returns build id' do
+ build_id = subject.track(build, transition)
+
+ expect(build_id).to eq build.id
+ end
end
end
- context 'when pending build does not exist' do
- it 'does nothing if there is no pending build to remove' do
- dequeued = subject.pop(build, transition)
+ describe '#untrack' do
+ let(:transition) { double('transition') }
- expect(dequeued).to be_nil
+ before do
+ allow(transition).to receive(:from).and_return('running')
+ allow(transition).to receive(:within_transaction).and_yield
end
- end
- context 'when invalid transition is detected' do
- it 'raises an error' do
- allow(transition).to receive(:from).and_return('created')
+ context 'when shared runner build tracking entry exists' do
+ before do
+ Ci::RunningBuild.create!(
+ build: build, project: project, runner: runner, runner_type: runner.runner_type
+ )
+ end
+
+ it 'removes shared runner build' do
+ build_id = subject.untrack(build, transition)
+
+ expect(build_id).to eq build.id
+ end
+
+ it 'increments shared runner build done metric' do
+ metrics = spy('metrics')
+
+ described_class.new(metrics).untrack(build, transition)
+
+ expect(metrics)
+ .to have_received(:increment_queue_operation)
+ .with(:shared_runner_build_done)
+ end
+ end
+
+ context 'when tracking entry does not exist' do
+ it 'does nothing if there is no tracking entry to remove' do
+ build_id = subject.untrack(build, transition)
+
+ expect(build_id).to be_nil
+ end
+ end
- expect { subject.pop(build, transition) }
- .to raise_error(described_class::InvalidQueueTransition)
+ context 'when invalid transition is detected' do
+ it 'raises an error' do
+ allow(transition).to receive(:from).and_return('pending')
+
+ expect { subject.untrack(build, transition) }
+ .to raise_error(described_class::InvalidQueueTransition)
+ end
end
end
end
diff --git a/spec/services/projects/create_service_spec.rb b/spec/services/projects/create_service_spec.rb
index 753c89eb981..cc2a9e4af9b 100644
--- a/spec/services/projects/create_service_spec.rb
+++ b/spec/services/projects/create_service_spec.rb
@@ -298,7 +298,7 @@ RSpec.describe Projects::CreateService, '#execute' do
context 'error handling' do
it 'handles invalid options' do
- opts[:default_branch] = 'master'
+ opts[:invalid] = 'option'
expect(create_project(user, opts)).to eq(nil)
end
end
@@ -806,7 +806,7 @@ RSpec.describe Projects::CreateService, '#execute' do
end
end
- context 'with specialized_project_authorization_workers' do
+ context 'with specialized project_authorization workers' do
let_it_be(:other_user) { create(:user) }
let_it_be(:group) { create(:group) }
@@ -847,34 +847,6 @@ RSpec.describe Projects::CreateService, '#execute' do
create_project(user, opts)
end
-
- context 'when feature is disabled' do
- before do
- stub_feature_flags(specialized_project_authorization_workers: false)
- end
-
- it 'updates authorization for current_user' do
- project = create_project(user, opts)
-
- expect(
- Ability.allowed?(user, :read_project, project)
- ).to be_truthy
- end
-
- it 'uses AuthorizedProjectsWorker' do
- expect(AuthorizedProjectsWorker).to(
- receive(:bulk_perform_async).with(array_including([user.id], [other_user.id])).and_call_original
- )
- expect(AuthorizedProjectUpdate::ProjectCreateWorker).not_to(
- receive(:perform_async)
- )
- expect(AuthorizedProjectUpdate::UserRefreshWithLowUrgencyWorker).not_to(
- receive(:bulk_perform_in)
- )
-
- create_project(user, opts)
- end
- end
end
def create_project(user, opts)
diff --git a/spec/services/projects/group_links/create_service_spec.rb b/spec/services/projects/group_links/create_service_spec.rb
index c249a51fc56..7e617a3568e 100644
--- a/spec/services/projects/group_links/create_service_spec.rb
+++ b/spec/services/projects/group_links/create_service_spec.rb
@@ -38,7 +38,7 @@ RSpec.describe Projects::GroupLinks::CreateService, '#execute' do
expect { subject.execute(create(:group)) }.not_to change { project.project_group_links.count }
end
- context 'with specialized_project_authorization_workers' do
+ context 'with specialized project_authorization workers' do
let_it_be(:other_user) { create(:user) }
before do
@@ -64,25 +64,5 @@ RSpec.describe Projects::GroupLinks::CreateService, '#execute' do
subject.execute(group)
end
-
- context 'when feature is disabled' do
- before do
- stub_feature_flags(specialized_project_authorization_project_share_worker: false)
- end
-
- it 'uses AuthorizedProjectsWorker' do
- expect(AuthorizedProjectsWorker).to(
- receive(:bulk_perform_async).with(array_including([user.id], [other_user.id])).and_call_original
- )
- expect(AuthorizedProjectUpdate::ProjectCreateWorker).not_to(
- receive(:perform_async)
- )
- expect(AuthorizedProjectUpdate::UserRefreshWithLowUrgencyWorker).not_to(
- receive(:bulk_perform_in)
- )
-
- subject.execute(group)
- end
- end
end
end
diff --git a/spec/workers/bulk_imports/export_request_worker_spec.rb b/spec/workers/bulk_imports/export_request_worker_spec.rb
index f7838279212..8d528011752 100644
--- a/spec/workers/bulk_imports/export_request_worker_spec.rb
+++ b/spec/workers/bulk_imports/export_request_worker_spec.rb
@@ -19,7 +19,7 @@ RSpec.describe BulkImports::ExportRequestWorker do
it 'requests relations export' do
expected = "/groups/foo%2Fbar/export_relations"
- expect_next_instance_of(BulkImports::Clients::Http) do |client|
+ expect_next_instance_of(BulkImports::Clients::HTTP) do |client|
expect(client).to receive(:post).with(expected).twice
end
diff --git a/spec/workers/ci/initial_pipeline_process_worker_spec.rb b/spec/workers/ci/initial_pipeline_process_worker_spec.rb
index bb144a3c360..5fb8671fd5c 100644
--- a/spec/workers/ci/initial_pipeline_process_worker_spec.rb
+++ b/spec/workers/ci/initial_pipeline_process_worker_spec.rb
@@ -11,26 +11,12 @@ RSpec.describe Ci::InitialPipelineProcessWorker do
include_examples 'an idempotent worker' do
let(:job_args) { pipeline.id }
- context 'when there are runners available' do
- before do
- create(:ci_runner, :online)
- end
-
- it 'marks the pipeline as pending' do
- expect(pipeline).to be_created
-
- subject
-
- expect(pipeline.reload).to be_pending
- end
- end
-
- it 'marks the pipeline as failed' do
+ it 'marks the pipeline as pending' do
expect(pipeline).to be_created
subject
- expect(pipeline.reload).to be_failed
+ expect(pipeline.reload).to be_pending
end
end
end