diff options
Diffstat (limited to 'spec')
70 files changed, 876 insertions, 878 deletions
diff --git a/spec/controllers/application_controller_spec.rb b/spec/controllers/application_controller_spec.rb index a5ecb475ce3..7296a4b4526 100644 --- a/spec/controllers/application_controller_spec.rb +++ b/spec/controllers/application_controller_spec.rb @@ -462,7 +462,7 @@ describe ApplicationController do end it 'does log correlation id' do - Gitlab::CorrelationId.use_id('new-id') do + Labkit::Correlation::CorrelationId.use_id('new-id') do get :index end diff --git a/spec/features/boards/sidebar_spec.rb b/spec/features/boards/sidebar_spec.rb index 8f34fddbf06..87c0dc40e5c 100644 --- a/spec/features/boards/sidebar_spec.rb +++ b/spec/features/boards/sidebar_spec.rb @@ -133,7 +133,7 @@ describe 'Issue Boards', :js do close_dropdown_menu_if_visible wait_for_requests - expect(page).to have_content('No assignee') + expect(page).to have_content('None') end expect(card_two).not_to have_selector('.avatar') @@ -143,7 +143,7 @@ describe 'Issue Boards', :js do click_card(card) page.within(find('.assignee')) do - expect(page).to have_content('No assignee') + expect(page).to have_content('None') click_button 'assign yourself' diff --git a/spec/features/issues_spec.rb b/spec/features/issues_spec.rb index 51508b78649..bc0ec58bd24 100644 --- a/spec/features/issues_spec.rb +++ b/spec/features/issues_spec.rb @@ -91,7 +91,7 @@ describe 'Issues' do click_button 'Save changes' page.within('.assignee') do - expect(page).to have_content 'No assignee - assign yourself' + expect(page).to have_content 'None - assign yourself' end expect(issue.reload.assignees).to be_empty @@ -465,7 +465,7 @@ describe 'Issues' do click_link 'Edit' click_link 'Unassigned' first('.title').click - expect(page).to have_content 'No assignee' + expect(page).to have_content 'None' end # wait_for_requests does not work with vue-resource at the moment @@ -479,7 +479,7 @@ describe 'Issues' do visit project_issue_path(project, issue2) page.within('.assignee') do - expect(page).to have_content "No assignee" + expect(page).to have_content "None" end page.within '.assignee' do @@ -522,7 +522,7 @@ describe 'Issues' do close_dropdown_menu_if_visible page.within '.value .assign-yourself' do - expect(page).to have_content "No assignee" + expect(page).to have_content "None" end end end @@ -775,10 +775,10 @@ describe 'Issues' do wait_for_requests - expect(page).to have_no_content 'No due date' + expect(page).to have_no_content 'None' click_link 'remove due date' - expect(page).to have_content 'No due date' + expect(page).to have_content 'None' end end end diff --git a/spec/features/projects/branches/download_buttons_spec.rb b/spec/features/projects/branches/download_buttons_spec.rb index c8dc72a34ec..3e75890725e 100644 --- a/spec/features/projects/branches/download_buttons_spec.rb +++ b/spec/features/projects/branches/download_buttons_spec.rb @@ -35,7 +35,7 @@ describe 'Download buttons in branches page' do it 'shows download artifacts button' do href = latest_succeeded_project_artifacts_path(project, 'binary-encoding/download', job: 'build') - expect(page).to have_link "Download '#{build.name}'", href: href + expect(page).to have_link build.name, href: href end end end diff --git a/spec/features/projects/files/download_buttons_spec.rb b/spec/features/projects/files/download_buttons_spec.rb index 03cb3530e2b..111972a6b00 100644 --- a/spec/features/projects/files/download_buttons_spec.rb +++ b/spec/features/projects/files/download_buttons_spec.rb @@ -30,7 +30,7 @@ describe 'Projects > Files > Download buttons in files tree' do it 'shows download artifacts button' do href = latest_succeeded_project_artifacts_path(project, "#{project.default_branch}/download", job: 'build') - expect(page).to have_link "Download '#{build.name}'", href: href + expect(page).to have_link build.name, href: href end end end diff --git a/spec/features/projects/files/user_edits_files_spec.rb b/spec/features/projects/files/user_edits_files_spec.rb index ec3930c26db..2de22582b2c 100644 --- a/spec/features/projects/files/user_edits_files_spec.rb +++ b/spec/features/projects/files/user_edits_files_spec.rb @@ -171,7 +171,7 @@ describe 'Projects > Files > User edits files', :js do wait_for_requests end - it 'links to the forked project for editing' do + it 'links to the forked project for editing', :quarantine do click_link('.gitignore') find('.js-edit-blob').click diff --git a/spec/features/projects/settings/user_manages_merge_requests_settings_spec.rb b/spec/features/projects/settings/user_manages_merge_requests_settings_spec.rb index b1c2bab08c0..28d52f25f56 100644 --- a/spec/features/projects/settings/user_manages_merge_requests_settings_spec.rb +++ b/spec/features/projects/settings/user_manages_merge_requests_settings_spec.rb @@ -1,3 +1,4 @@ +# frozen_string_literal: true require 'spec_helper' describe 'Projects > Settings > User manages merge request settings' do @@ -30,16 +31,16 @@ describe 'Projects > Settings > User manages merge request settings' do context 'when Merge Request and Pipelines are initially enabled', :js do context 'when Pipelines are initially enabled' do it 'shows the Merge Requests settings' do - expect(page).to have_content('Only allow merge requests to be merged if the pipeline succeeds') - expect(page).to have_content('Only allow merge requests to be merged if all discussions are resolved') + expect(page).to have_content 'Pipelines must succeed' + expect(page).to have_content 'All discussions must be resolved' within('.sharing-permissions-form') do find('.project-feature-controls[data-for="project[project_feature_attributes][merge_requests_access_level]"] .project-feature-toggle').click find('input[value="Save changes"]').send_keys(:return) end - expect(page).not_to have_content('Only allow merge requests to be merged if the pipeline succeeds') - expect(page).not_to have_content('Only allow merge requests to be merged if all discussions are resolved') + expect(page).not_to have_content 'Pipelines must succeed' + expect(page).not_to have_content 'All discussions must be resolved' end end @@ -50,16 +51,16 @@ describe 'Projects > Settings > User manages merge request settings' do end it 'shows the Merge Requests settings that do not depend on Builds feature' do - expect(page).not_to have_content('Only allow merge requests to be merged if the pipeline succeeds') - expect(page).to have_content('Only allow merge requests to be merged if all discussions are resolved') + expect(page).not_to have_content 'Pipelines must succeed' + expect(page).to have_content 'All discussions must be resolved' within('.sharing-permissions-form') do find('.project-feature-controls[data-for="project[project_feature_attributes][builds_access_level]"] .project-feature-toggle').click find('input[value="Save changes"]').send_keys(:return) end - expect(page).to have_content('Only allow merge requests to be merged if the pipeline succeeds') - expect(page).to have_content('Only allow merge requests to be merged if all discussions are resolved') + expect(page).to have_content 'Pipelines must succeed' + expect(page).to have_content 'All discussions must be resolved' end end end @@ -71,16 +72,16 @@ describe 'Projects > Settings > User manages merge request settings' do end it 'does not show the Merge Requests settings' do - expect(page).not_to have_content('Only allow merge requests to be merged if the pipeline succeeds') - expect(page).not_to have_content('Only allow merge requests to be merged if all discussions are resolved') + expect(page).not_to have_content 'Pipelines must succeed' + expect(page).not_to have_content 'All discussions must be resolved' within('.sharing-permissions-form') do find('.project-feature-controls[data-for="project[project_feature_attributes][merge_requests_access_level]"] .project-feature-toggle').click find('input[value="Save changes"]').send_keys(:return) end - expect(page).to have_content('Only allow merge requests to be merged if the pipeline succeeds') - expect(page).to have_content('Only allow merge requests to be merged if all discussions are resolved') + expect(page).to have_content 'Pipelines must succeed' + expect(page).to have_content 'All discussions must be resolved' end end diff --git a/spec/features/projects/show/download_buttons_spec.rb b/spec/features/projects/show/download_buttons_spec.rb index 3a2dcc5aa55..fee5f8001b0 100644 --- a/spec/features/projects/show/download_buttons_spec.rb +++ b/spec/features/projects/show/download_buttons_spec.rb @@ -35,11 +35,10 @@ describe 'Projects > Show > Download buttons' do it 'shows download artifacts button' do href = latest_succeeded_project_artifacts_path(project, "#{project.default_branch}/download", job: 'build') - expect(page).to have_link "Download '#{build.name}'", href: href + expect(page).to have_link build.name, href: href end it 'download links have download attribute' do - expect(page).to have_selector('a', text: 'Download') page.all('a', text: 'Download').each do |link| expect(link[:download]).to eq '' end diff --git a/spec/features/projects/tags/download_buttons_spec.rb b/spec/features/projects/tags/download_buttons_spec.rb index fbfd8cee7aa..4c8ec53836a 100644 --- a/spec/features/projects/tags/download_buttons_spec.rb +++ b/spec/features/projects/tags/download_buttons_spec.rb @@ -36,7 +36,7 @@ describe 'Download buttons in tags page' do it 'shows download artifacts button' do href = latest_succeeded_project_artifacts_path(project, "#{tag}/download", job: 'build') - expect(page).to have_link "Download '#{build.name}'", href: href + expect(page).to have_link build.name, href: href end end end diff --git a/spec/features/projects/wiki/user_creates_wiki_page_spec.rb b/spec/features/projects/wiki/user_creates_wiki_page_spec.rb index bcbba6f14da..aac095bfa6b 100644 --- a/spec/features/projects/wiki/user_creates_wiki_page_spec.rb +++ b/spec/features/projects/wiki/user_creates_wiki_page_spec.rb @@ -141,7 +141,7 @@ describe "User creates wiki page" do end end - it_behaves_like 'wiki file attachments' + it_behaves_like 'wiki file attachments', :quarantine end context "in a group namespace", :js do @@ -151,7 +151,7 @@ describe "User creates wiki page" do expect(page).to have_field("wiki[message]", with: "Create home") end - it "creates a page from the home page" do + it "creates a page from the home page", :quarantine do page.within(".wiki-form") do fill_in(:wiki_content, with: "My awesome wiki!") diff --git a/spec/finders/autocomplete/users_finder_spec.rb b/spec/finders/autocomplete/users_finder_spec.rb index abd0d6b5185..bcde115b1a6 100644 --- a/spec/finders/autocomplete/users_finder_spec.rb +++ b/spec/finders/autocomplete/users_finder_spec.rb @@ -26,9 +26,17 @@ describe Autocomplete::UsersFinder do it { is_expected.to match_array([project.owner]) } context 'when author_id passed' do - let(:params) { { author_id: user2.id } } + context 'and author is active' do + let(:params) { { author_id: user1.id } } - it { is_expected.to match_array([project.owner, user2]) } + it { is_expected.to match_array([project.owner, user1]) } + end + + context 'and author is blocked' do + let(:params) { { author_id: user2.id } } + + it { is_expected.to match_array([project.owner]) } + end end end @@ -104,9 +112,9 @@ describe Autocomplete::UsersFinder do end context 'when filtered by author_id' do - let(:params) { { author_id: user2.id } } + let(:params) { { author_id: user1.id } } - it { is_expected.to match_array([user2, user1, external_user, omniauth_user, current_user]) } + it { is_expected.to match_array([user1, external_user, omniauth_user, current_user]) } end end end diff --git a/spec/fixtures/api/schemas/entities/merge_request_widget.json b/spec/fixtures/api/schemas/entities/merge_request_widget.json index 6b1cd60c25d..7018cb9a305 100644 --- a/spec/fixtures/api/schemas/entities/merge_request_widget.json +++ b/spec/fixtures/api/schemas/entities/merge_request_widget.json @@ -125,6 +125,7 @@ "test_reports_path": { "type": ["string", "null"] }, "can_receive_suggestion": { "type": "boolean" }, "source_branch_protected": { "type": "boolean" }, - "conflicts_docs_path": { "type": ["string", "null"] } + "conflicts_docs_path": { "type": ["string", "null"] }, + "merge_request_pipelines_docs_path": { "type": ["string", "null"] } } } diff --git a/spec/frontend/clusters/components/application_row_spec.js b/spec/frontend/clusters/components/application_row_spec.js index b28d0075d06..038d2be9e98 100644 --- a/spec/frontend/clusters/components/application_row_spec.js +++ b/spec/frontend/clusters/components/application_row_spec.js @@ -114,10 +114,12 @@ describe('Application Row', () => { expect(vm.installButtonDisabled).toEqual(true); }); - it('has disabled "Installed" when APPLICATION_STATUS.INSTALLED', () => { + it('has disabled "Installed" when application is installed and not uninstallable', () => { vm = mountComponent(ApplicationRow, { ...DEFAULT_APPLICATION_STATE, status: APPLICATION_STATUS.INSTALLED, + installed: true, + uninstallable: false, }); expect(vm.installButtonLabel).toEqual('Installed'); @@ -125,15 +127,16 @@ describe('Application Row', () => { expect(vm.installButtonDisabled).toEqual(true); }); - it('has disabled "Installed" when APPLICATION_STATUS.UPDATING', () => { + it('hides when application is installed and uninstallable', () => { vm = mountComponent(ApplicationRow, { ...DEFAULT_APPLICATION_STATE, - status: APPLICATION_STATUS.UPDATING, + status: APPLICATION_STATUS.INSTALLED, + installed: true, + uninstallable: true, }); + const installBtn = vm.$el.querySelector('.js-cluster-application-install-button'); - expect(vm.installButtonLabel).toEqual('Installed'); - expect(vm.installButtonLoading).toEqual(false); - expect(vm.installButtonDisabled).toEqual(true); + expect(installBtn).toBe(null); }); it('has enabled "Install" when APPLICATION_STATUS.ERROR', () => { @@ -208,6 +211,19 @@ describe('Application Row', () => { }); }); + describe('Uninstall button', () => { + it('displays button when app is installed and uninstallable', () => { + vm = mountComponent(ApplicationRow, { + ...DEFAULT_APPLICATION_STATE, + installed: true, + uninstallable: true, + }); + const uninstallButton = vm.$el.querySelector('.js-cluster-application-uninstall-button'); + + expect(uninstallButton).toBeTruthy(); + }); + }); + describe('Upgrade button', () => { it('has indeterminate state on page load', () => { vm = mountComponent(ApplicationRow, { diff --git a/spec/frontend/clusters/stores/clusters_store_spec.js b/spec/frontend/clusters/stores/clusters_store_spec.js index 161722ec571..c0e8b737ea2 100644 --- a/spec/frontend/clusters/stores/clusters_store_spec.js +++ b/spec/frontend/clusters/stores/clusters_store_spec.js @@ -1,5 +1,5 @@ import ClustersStore from '~/clusters/stores/clusters_store'; -import { APPLICATION_STATUS } from '~/clusters/constants'; +import { APPLICATION_INSTALLED_STATUSES, APPLICATION_STATUS, RUNNER } from '~/clusters/constants'; import { CLUSTERS_MOCK_DATA } from '../services/mock_data'; describe('Clusters Store', () => { @@ -70,6 +70,7 @@ describe('Clusters Store', () => { statusReason: mockResponseData.applications[0].status_reason, requestStatus: null, requestReason: null, + installed: false, }, ingress: { title: 'Ingress', @@ -79,6 +80,7 @@ describe('Clusters Store', () => { requestReason: null, externalIp: null, externalHostname: null, + installed: false, }, runner: { title: 'GitLab Runner', @@ -89,6 +91,7 @@ describe('Clusters Store', () => { version: mockResponseData.applications[2].version, upgradeAvailable: mockResponseData.applications[2].update_available, chartRepo: 'https://gitlab.com/charts/gitlab-runner', + installed: false, }, prometheus: { title: 'Prometheus', @@ -96,6 +99,7 @@ describe('Clusters Store', () => { statusReason: mockResponseData.applications[3].status_reason, requestStatus: null, requestReason: null, + installed: false, }, jupyter: { title: 'JupyterHub', @@ -104,6 +108,7 @@ describe('Clusters Store', () => { requestStatus: null, requestReason: null, hostname: '', + installed: false, }, knative: { title: 'Knative', @@ -115,6 +120,7 @@ describe('Clusters Store', () => { isEditingHostName: false, externalIp: null, externalHostname: null, + installed: false, }, cert_manager: { title: 'Cert-Manager', @@ -123,11 +129,26 @@ describe('Clusters Store', () => { requestStatus: null, requestReason: null, email: mockResponseData.applications[6].email, + installed: false, }, }, }); }); + describe.each(APPLICATION_INSTALLED_STATUSES)('given the current app status is %s', () => { + it('marks application as installed', () => { + const mockResponseData = + CLUSTERS_MOCK_DATA.GET['/gitlab-org/gitlab-shell/clusters/2/status.json'].data; + const runnerAppIndex = 2; + + mockResponseData.applications[runnerAppIndex].status = APPLICATION_STATUS.INSTALLED; + + store.updateStateFromServer(mockResponseData); + + expect(store.state.applications[RUNNER].installed).toBe(true); + }); + }); + it('sets default hostname for jupyter when ingress has a ip address', () => { const mockResponseData = CLUSTERS_MOCK_DATA.GET['/gitlab-org/gitlab-shell/clusters/2/status.json'].data; diff --git a/spec/frontend/gfm_auto_complete_spec.js b/spec/frontend/gfm_auto_complete_spec.js index ed12af925f1..841aff0d7ff 100644 --- a/spec/frontend/gfm_auto_complete_spec.js +++ b/spec/frontend/gfm_auto_complete_spec.js @@ -94,7 +94,7 @@ describe('GfmAutoComplete', () => { }); it('should quote if value contains any non-alphanumeric characters', () => { - expect(beforeInsert(atwhoInstance, '~label-20')).toBe('~"label\\-20"'); + expect(beforeInsert(atwhoInstance, '~label-20')).toBe('~"label-20"'); expect(beforeInsert(atwhoInstance, '~label 20')).toBe('~"label 20"'); }); @@ -102,12 +102,21 @@ describe('GfmAutoComplete', () => { expect(beforeInsert(atwhoInstance, '~1234')).toBe('~"1234"'); }); - it('should escape Markdown emphasis characters, except in the first character', () => { - expect(beforeInsert(atwhoInstance, '@_group')).toEqual('@\\_group'); - expect(beforeInsert(atwhoInstance, '~_bug')).toEqual('~\\_bug'); + it('escapes Markdown strikethroughs when needed', () => { + expect(beforeInsert(atwhoInstance, '~a~bug')).toEqual('~"a~bug"'); + expect(beforeInsert(atwhoInstance, '~a~~bug~~')).toEqual('~"a\\~~bug\\~~"'); + }); + + it('escapes Markdown emphasis when needed', () => { + expect(beforeInsert(atwhoInstance, '~a_bug_')).toEqual('~a_bug\\_'); + expect(beforeInsert(atwhoInstance, '~a _bug_')).toEqual('~"a \\_bug\\_"'); + expect(beforeInsert(atwhoInstance, '~a*bug*')).toEqual('~"a\\*bug\\*"'); + expect(beforeInsert(atwhoInstance, '~a *bug*')).toEqual('~"a \\*bug\\*"'); + }); + + it('escapes Markdown code spans when needed', () => { + expect(beforeInsert(atwhoInstance, '~a`bug`')).toEqual('~"a\\`bug\\`"'); expect(beforeInsert(atwhoInstance, '~a `bug`')).toEqual('~"a \\`bug\\`"'); - expect(beforeInsert(atwhoInstance, '~a ~bug')).toEqual('~"a \\~bug"'); - expect(beforeInsert(atwhoInstance, '~a **bug')).toEqual('~"a \\*\\*bug"'); }); }); diff --git a/spec/frontend/notes/components/discussion_actions_spec.js b/spec/frontend/notes/components/discussion_actions_spec.js new file mode 100644 index 00000000000..0a52c81571e --- /dev/null +++ b/spec/frontend/notes/components/discussion_actions_spec.js @@ -0,0 +1,104 @@ +import createStore from '~/notes/stores'; +import { shallowMount, mount, createLocalVue } from '@vue/test-utils'; +import { discussionMock } from '../../../javascripts/notes/mock_data'; +import DiscussionActions from '~/notes/components/discussion_actions.vue'; +import ReplyPlaceholder from '~/notes/components/discussion_reply_placeholder.vue'; +import ResolveDiscussionButton from '~/notes/components/discussion_resolve_button.vue'; +import ResolveWithIssueButton from '~/notes/components/discussion_resolve_with_issue_button.vue'; +import JumpToNextDiscussionButton from '~/notes/components/discussion_jump_to_next_button.vue'; + +describe('DiscussionActions', () => { + let wrapper; + const createComponentFactory = (shallow = true) => props => { + const localVue = createLocalVue(); + const store = createStore(); + const mountFn = shallow ? shallowMount : mount; + + wrapper = mountFn(DiscussionActions, { + localVue, + store, + propsData: { + discussion: discussionMock, + isResolving: false, + resolveButtonTitle: 'Resolve discussion', + resolveWithIssuePath: '/some/issue/path', + shouldShowJumpToNextDiscussion: true, + ...props, + }, + }); + }; + + afterEach(() => { + wrapper.destroy(); + }); + + describe('rendering', () => { + const createComponent = createComponentFactory(); + + it('renders reply placeholder, resolve discussion button, resolve with issue button and jump to next discussion button', () => { + createComponent(); + expect(wrapper.find(ReplyPlaceholder).exists()).toBe(true); + expect(wrapper.find(ResolveDiscussionButton).exists()).toBe(true); + expect(wrapper.find(ResolveWithIssueButton).exists()).toBe(true); + expect(wrapper.find(JumpToNextDiscussionButton).exists()).toBe(true); + }); + + it('only renders reply placholder if disccusion is not resolvable', () => { + const discussion = { ...discussionMock }; + discussion.resolvable = false; + createComponent({ discussion }); + + expect(wrapper.find(ReplyPlaceholder).exists()).toBe(true); + expect(wrapper.find(ResolveDiscussionButton).exists()).toBe(false); + expect(wrapper.find(ResolveWithIssueButton).exists()).toBe(false); + expect(wrapper.find(JumpToNextDiscussionButton).exists()).toBe(false); + }); + + it('does not render resolve with issue button if resolveWithIssuePath is falsy', () => { + createComponent({ resolveWithIssuePath: '' }); + + expect(wrapper.find(ResolveWithIssueButton).exists()).toBe(false); + }); + + it('does not render jump to next discussion button if shouldShowJumpToNextDiscussion is false', () => { + createComponent({ shouldShowJumpToNextDiscussion: false }); + + expect(wrapper.find(JumpToNextDiscussionButton).exists()).toBe(false); + }); + }); + + describe('events handling', () => { + const createComponent = createComponentFactory(false); + + beforeEach(() => { + createComponent(); + }); + + it('emits showReplyForm event when clicking on reply placeholder', () => { + jest.spyOn(wrapper.vm, '$emit'); + wrapper + .find(ReplyPlaceholder) + .find('button') + .trigger('click'); + expect(wrapper.vm.$emit).toHaveBeenCalledWith('showReplyForm'); + }); + + it('emits resolve event when clicking on resolve button', () => { + jest.spyOn(wrapper.vm, '$emit'); + wrapper + .find(ResolveDiscussionButton) + .find('button') + .trigger('click'); + expect(wrapper.vm.$emit).toHaveBeenCalledWith('resolve'); + }); + + it('emits jumpToNextDiscussion event when clicking on jump to next discussion button', () => { + jest.spyOn(wrapper.vm, '$emit'); + wrapper + .find(JumpToNextDiscussionButton) + .find('button') + .trigger('click'); + expect(wrapper.vm.$emit).toHaveBeenCalledWith('jumpToNextDiscussion'); + }); + }); +}); diff --git a/spec/frontend/pages/admin/abuse_reports/abuse_reports_spec.js b/spec/frontend/pages/admin/abuse_reports/abuse_reports_spec.js index 7e9aec84016..f163bdd9913 100644 --- a/spec/frontend/pages/admin/abuse_reports/abuse_reports_spec.js +++ b/spec/frontend/pages/admin/abuse_reports/abuse_reports_spec.js @@ -1,6 +1,9 @@ import $ from 'jquery'; import '~/lib/utils/text_utility'; import AbuseReports from '~/pages/admin/abuse_reports/abuse_reports'; +import { setTestTimeout } from 'helpers/timeout'; + +setTestTimeout(500); describe('Abuse Reports', () => { const FIXTURE = 'abuse_reports/abuse_reports_list.html'; diff --git a/spec/javascripts/boards/board_list_common_spec.js b/spec/javascripts/boards/board_list_common_spec.js new file mode 100644 index 00000000000..cb337e4cc83 --- /dev/null +++ b/spec/javascripts/boards/board_list_common_spec.js @@ -0,0 +1,58 @@ +/* global List */ +/* global ListIssue */ + +import MockAdapter from 'axios-mock-adapter'; +import Vue from 'vue'; +import axios from '~/lib/utils/axios_utils'; +import Sortable from 'sortablejs'; +import BoardList from '~/boards/components/board_list.vue'; + +import '~/boards/models/issue'; +import '~/boards/models/list'; +import { listObj, boardsMockInterceptor, mockBoardService } from './mock_data'; +import boardsStore from '~/boards/stores/boards_store'; + +window.Sortable = Sortable; + +export default function createComponent({ done, listIssueProps = {}, componentProps = {} }) { + const el = document.createElement('div'); + + document.body.appendChild(el); + const mock = new MockAdapter(axios); + mock.onAny().reply(boardsMockInterceptor); + gl.boardService = mockBoardService(); + boardsStore.create(); + + const BoardListComp = Vue.extend(BoardList); + const list = new List(listObj); + const issue = new ListIssue({ + title: 'Testing', + id: 1, + iid: 1, + confidential: false, + labels: [], + assignees: [], + ...listIssueProps, + }); + list.issuesSize = 1; + list.issues.push(issue); + + const component = new BoardListComp({ + el, + propsData: { + disabled: false, + list, + issues: list.issues, + loading: false, + issueLinkBase: '/issues', + rootPath: '/', + ...componentProps, + }, + }).$mount(); + + Vue.nextTick(() => { + done(); + }); + + return { component, mock }; +} diff --git a/spec/javascripts/boards/board_list_spec.js b/spec/javascripts/boards/board_list_spec.js index 396fc823ef5..9c9b435d7fd 100644 --- a/spec/javascripts/boards/board_list_spec.js +++ b/spec/javascripts/boards/board_list_spec.js @@ -1,60 +1,13 @@ -/* global List */ -/* global ListIssue */ - import Vue from 'vue'; -import MockAdapter from 'axios-mock-adapter'; -import axios from '~/lib/utils/axios_utils'; -import Sortable from 'sortablejs'; -import BoardList from '~/boards/components/board_list.vue'; import eventHub from '~/boards/eventhub'; -import '~/boards/models/issue'; -import '~/boards/models/list'; -import boardsStore from '~/boards/stores/boards_store'; -import { listObj, boardsMockInterceptor, mockBoardService } from './mock_data'; - -window.Sortable = Sortable; +import createComponent from './board_list_common_spec'; describe('Board list component', () => { let mock; let component; beforeEach(done => { - const el = document.createElement('div'); - - document.body.appendChild(el); - mock = new MockAdapter(axios); - mock.onAny().reply(boardsMockInterceptor); - gl.boardService = mockBoardService(); - boardsStore.create(); - - const BoardListComp = Vue.extend(BoardList); - const list = new List(listObj); - const issue = new ListIssue({ - title: 'Testing', - id: 1, - iid: 1, - confidential: false, - labels: [], - assignees: [], - }); - list.issuesSize = 1; - list.issues.push(issue); - - component = new BoardListComp({ - el, - propsData: { - disabled: false, - list, - issues: list.issues, - loading: false, - issueLinkBase: '/issues', - rootPath: '/', - }, - }).$mount(); - - Vue.nextTick(() => { - done(); - }); + ({ mock, component } = createComponent({ done })); }); afterEach(() => { diff --git a/spec/javascripts/boards/issue_card_spec.js b/spec/javascripts/boards/issue_card_spec.js index 6eda5047dd0..a5bf97bdcc2 100644 --- a/spec/javascripts/boards/issue_card_spec.js +++ b/spec/javascripts/boards/issue_card_spec.js @@ -285,4 +285,10 @@ describe('Issue card component', () => { .catch(done.fail); }); }); + + describe('weights', () => { + it('not shows weight component', () => { + expect(component.$el.querySelector('.board-card-weight')).toBeNull(); + }); + }); }); diff --git a/spec/javascripts/jobs/mock_data.js b/spec/javascripts/jobs/mock_data.js index 0398f184c0a..1a7f338c5fa 100644 --- a/spec/javascripts/jobs/mock_data.js +++ b/spec/javascripts/jobs/mock_data.js @@ -678,7 +678,7 @@ export const stages = [ icon: 'status_warning', text: 'failed', label: 'failed (allowed to fail)', - group: 'failed_with_warnings', + group: 'failed-with-warnings', tooltip: 'failed - (unknown failure) (allowed to fail)', has_details: true, details_path: '/gitlab-org/gitlab-shell/-/jobs/454', @@ -710,7 +710,7 @@ export const stages = [ icon: 'status_warning', text: 'failed', label: 'failed (allowed to fail)', - group: 'failed_with_warnings', + group: 'failed-with-warnings', tooltip: 'failed - (unknown failure) (allowed to fail)', has_details: true, details_path: '/gitlab-org/gitlab-shell/-/jobs/454', @@ -738,7 +738,7 @@ export const stages = [ icon: 'status_warning', text: 'passed', label: 'passed with warnings', - group: 'success_with_warnings', + group: 'success-with-warnings', tooltip: 'passed', has_details: true, details_path: '/gitlab-org/gitlab-shell/pipelines/27#test', diff --git a/spec/javascripts/monitoring/monitoring_store_spec.js b/spec/javascripts/monitoring/monitoring_store_spec.js index d8a980c874d..5bf6937c92e 100644 --- a/spec/javascripts/monitoring/monitoring_store_spec.js +++ b/spec/javascripts/monitoring/monitoring_store_spec.js @@ -32,4 +32,28 @@ describe('MonitoringStore', () => { it('removes the data if all the values from a query are not defined', () => { expect(store.groups[1].metrics[0].queries[0].result.length).toEqual(0); }); + + it('assigns queries a metric id', () => { + expect(store.groups[1].metrics[0].queries[0].metricId).toEqual('100'); + }); + + it('assigns metric id of null if metric has no id', () => { + const noId = MonitoringMock.data.map(group => ({ + ...group, + ...{ + metrics: group.metrics.map(metric => { + const { id, ...metricWithoutId } = metric; + + return metricWithoutId; + }), + }, + })); + store.storeMetrics(noId); + + store.groups.forEach(group => { + group.metrics.forEach(metric => { + expect(metric.queries.every(query => query.metricId === null)).toBe(true); + }); + }); + }); }); diff --git a/spec/javascripts/performance_bar/components/detailed_metric_spec.js b/spec/javascripts/performance_bar/components/detailed_metric_spec.js index e91685e50c5..8a7aa057186 100644 --- a/spec/javascripts/performance_bar/components/detailed_metric_spec.js +++ b/spec/javascripts/performance_bar/components/detailed_metric_spec.js @@ -27,8 +27,8 @@ describe('detailedMetric', () => { describe('when the current request has details', () => { const requestDetails = [ - { duration: '100', feature: 'find_commit', request: 'abcdef' }, - { duration: '23', feature: 'rebase_in_progress', request: '' }, + { duration: '100', feature: 'find_commit', request: 'abcdef', backtrace: ['hello', 'world'] }, + { duration: '23', feature: 'rebase_in_progress', request: '', backtrace: ['world', 'hello'] }, ]; beforeEach(() => { @@ -54,9 +54,11 @@ describe('detailedMetric', () => { }); it('adds a modal with a table of the details', () => { - vm.$el.querySelectorAll('.performance-bar-modal td strong').forEach((duration, index) => { - expect(duration.innerText).toContain(requestDetails[index].duration); - }); + vm.$el + .querySelectorAll('.performance-bar-modal td:nth-child(1)') + .forEach((duration, index) => { + expect(duration.innerText).toContain(requestDetails[index].duration); + }); vm.$el .querySelectorAll('.performance-bar-modal td:nth-child(2)') @@ -65,10 +67,16 @@ describe('detailedMetric', () => { }); vm.$el - .querySelectorAll('.performance-bar-modal td:nth-child(3)') + .querySelectorAll('.performance-bar-modal td:nth-child(2)') .forEach((request, index) => { - expect(request.innerText).toEqual(requestDetails[index].request); + expect(request.innerText).toContain(requestDetails[index].request); }); + + expect(vm.$el.querySelector('.text-expander.js-toggle-button')).not.toBeNull(); + + vm.$el.querySelectorAll('.performance-bar-modal td:nth-child(2)').forEach(request => { + expect(request.innerText).toContain('world'); + }); }); it('displays the metric name', () => { diff --git a/spec/javascripts/sidebar/assignees_spec.js b/spec/javascripts/sidebar/assignees_spec.js index 47fee5d2b21..4ae2141d5f0 100644 --- a/spec/javascripts/sidebar/assignees_spec.js +++ b/spec/javascripts/sidebar/assignees_spec.js @@ -24,12 +24,12 @@ describe('Assignee component', () => { const collapsed = component.$el.querySelector('.sidebar-collapsed-icon'); expect(collapsed.childElementCount).toEqual(1); - expect(collapsed.children[0].getAttribute('aria-label')).toEqual('No Assignee'); + expect(collapsed.children[0].getAttribute('aria-label')).toEqual('None'); expect(collapsed.children[0].classList.contains('fa')).toEqual(true); expect(collapsed.children[0].classList.contains('fa-user')).toEqual(true); }); - it('displays only "No assignee" when no users are assigned and the issue is read-only', () => { + it('displays only "None" when no users are assigned and the issue is read-only', () => { component = new AssigneeComponent({ propsData: { rootPath: 'http://localhost:3000', @@ -39,11 +39,11 @@ describe('Assignee component', () => { }).$mount(); const componentTextNoUsers = component.$el.querySelector('.assign-yourself').innerText.trim(); - expect(componentTextNoUsers).toBe('No assignee'); + expect(componentTextNoUsers).toBe('None'); expect(componentTextNoUsers.indexOf('assign yourself')).toEqual(-1); }); - it('displays only "No assignee" when no users are assigned and the issue can be edited', () => { + it('displays only "None" when no users are assigned and the issue can be edited', () => { component = new AssigneeComponent({ propsData: { rootPath: 'http://localhost:3000', @@ -53,7 +53,7 @@ describe('Assignee component', () => { }).$mount(); const componentTextNoUsers = component.$el.querySelector('.assign-yourself').innerText.trim(); - expect(componentTextNoUsers.indexOf('No assignee')).toEqual(0); + expect(componentTextNoUsers.indexOf('None')).toEqual(0); expect(componentTextNoUsers.indexOf('assign yourself')).toBeGreaterThan(0); }); diff --git a/spec/javascripts/vue_mr_widget/components/mr_widget_alert_message_spec.js b/spec/javascripts/vue_mr_widget/components/mr_widget_alert_message_spec.js new file mode 100644 index 00000000000..8ec17efffb9 --- /dev/null +++ b/spec/javascripts/vue_mr_widget/components/mr_widget_alert_message_spec.js @@ -0,0 +1,77 @@ +import { shallowMount, createLocalVue } from '@vue/test-utils'; +import MrWidgetAlertMessage from '~/vue_merge_request_widget/components/mr_widget_alert_message.vue'; +import { GlLink } from '@gitlab/ui'; + +describe('MrWidgetAlertMessage', () => { + let wrapper; + + beforeEach(() => { + const localVue = createLocalVue(); + + wrapper = shallowMount(localVue.extend(MrWidgetAlertMessage), { + propsData: {}, + localVue, + sync: false, + }); + }); + + afterEach(() => { + wrapper.destroy(); + }); + + describe('when type is not provided', () => { + it('should render a red message', done => { + wrapper.vm.$nextTick(() => { + expect(wrapper.classes()).toContain('danger_message'); + expect(wrapper.classes()).not.toContain('warning_message'); + done(); + }); + }); + }); + + describe('when type === "danger"', () => { + it('should render a red message', done => { + wrapper.setProps({ type: 'danger' }); + wrapper.vm.$nextTick(() => { + expect(wrapper.classes()).toContain('danger_message'); + expect(wrapper.classes()).not.toContain('warning_message'); + done(); + }); + }); + }); + + describe('when type === "warning"', () => { + it('should render a red message', done => { + wrapper.setProps({ type: 'warning' }); + wrapper.vm.$nextTick(() => { + expect(wrapper.classes()).toContain('warning_message'); + expect(wrapper.classes()).not.toContain('danger_message'); + done(); + }); + }); + }); + + describe('when helpPath is not provided', () => { + it('should not render a help icon/link', done => { + wrapper.vm.$nextTick(() => { + const link = wrapper.find(GlLink); + + expect(link.exists()).toBe(false); + done(); + }); + }); + }); + + describe('when helpPath is provided', () => { + it('should render a help icon/link', done => { + wrapper.setProps({ helpPath: '/path/to/help/docs' }); + wrapper.vm.$nextTick(() => { + const link = wrapper.find(GlLink); + + expect(link.exists()).toBe(true); + expect(link.attributes().href).toBe('/path/to/help/docs'); + done(); + }); + }); + }); +}); diff --git a/spec/javascripts/vue_mr_widget/mock_data.js b/spec/javascripts/vue_mr_widget/mock_data.js index 7ab203a6011..dda16375103 100644 --- a/spec/javascripts/vue_mr_widget/mock_data.js +++ b/spec/javascripts/vue_mr_widget/mock_data.js @@ -233,6 +233,7 @@ export default { merge_commit_path: 'http://localhost:3000/root/acets-app/commit/53027d060246c8f47e4a9310fb332aa52f221775', troubleshooting_docs_path: 'help', + merge_request_pipelines_docs_path: '/help/ci/merge_request_pipelines/index.md', squash: true, }; diff --git a/spec/javascripts/vue_mr_widget/mr_widget_options_spec.js b/spec/javascripts/vue_mr_widget/mr_widget_options_spec.js index 3e8f73646c8..690fcd3e224 100644 --- a/spec/javascripts/vue_mr_widget/mr_widget_options_spec.js +++ b/spec/javascripts/vue_mr_widget/mr_widget_options_spec.js @@ -183,6 +183,85 @@ describe('mrWidgetOptions', () => { }); }); }); + + describe('showMergePipelineForkWarning', () => { + describe('when the source project and target project are the same', () => { + beforeEach(done => { + Vue.set(vm.mr, 'mergePipelinesEnabled', true); + Vue.set(vm.mr, 'sourceProjectId', 1); + Vue.set(vm.mr, 'targetProjectId', 1); + vm.$nextTick(done); + }); + + it('should be false', () => { + expect(vm.showMergePipelineForkWarning).toEqual(false); + }); + }); + + describe('when merge pipelines are not enabled', () => { + beforeEach(done => { + Vue.set(vm.mr, 'mergePipelinesEnabled', false); + Vue.set(vm.mr, 'sourceProjectId', 1); + Vue.set(vm.mr, 'targetProjectId', 2); + vm.$nextTick(done); + }); + + it('should be false', () => { + expect(vm.showMergePipelineForkWarning).toEqual(false); + }); + }); + + describe('when merge pipelines are enabled _and_ the source project and target project are different', () => { + beforeEach(done => { + Vue.set(vm.mr, 'mergePipelinesEnabled', true); + Vue.set(vm.mr, 'sourceProjectId', 1); + Vue.set(vm.mr, 'targetProjectId', 2); + vm.$nextTick(done); + }); + + it('should be true', () => { + expect(vm.showMergePipelineForkWarning).toEqual(true); + }); + }); + }); + + describe('showTargetBranchAdvancedError', () => { + describe(`when the pipeline's target_sha property doesn't exist`, () => { + beforeEach(done => { + Vue.set(vm.mr.pipeline, 'target_sha', undefined); + Vue.set(vm.mr, 'targetBranchSha', 'abcd'); + vm.$nextTick(done); + }); + + it('should be false', () => { + expect(vm.showTargetBranchAdvancedError).toEqual(false); + }); + }); + + describe(`when the pipeline's target_sha matches the target branch's sha`, () => { + beforeEach(done => { + Vue.set(vm.mr.pipeline, 'target_sha', 'abcd'); + Vue.set(vm.mr, 'targetBranchSha', 'abcd'); + vm.$nextTick(done); + }); + + it('should be false', () => { + expect(vm.showTargetBranchAdvancedError).toEqual(false); + }); + }); + + describe(`when the pipeline's target_sha does not match the target branch's sha`, () => { + beforeEach(done => { + Vue.set(vm.mr.pipeline, 'target_sha', 'abcd'); + Vue.set(vm.mr, 'targetBranchSha', 'bcde'); + vm.$nextTick(done); + }); + + it('should be true', () => { + expect(vm.showTargetBranchAdvancedError).toEqual(true); + }); + }); + }); }); describe('methods', () => { diff --git a/spec/javascripts/vue_mr_widget/stores/mr_widget_store_spec.js b/spec/javascripts/vue_mr_widget/stores/mr_widget_store_spec.js index c226704694c..e2cd0f084fd 100644 --- a/spec/javascripts/vue_mr_widget/stores/mr_widget_store_spec.js +++ b/spec/javascripts/vue_mr_widget/stores/mr_widget_store_spec.js @@ -36,8 +36,8 @@ describe('MergeRequestStore', () => { expect(store.isPipelinePassing).toBe(true); }); - it('is true when the CI status is `success_with_warnings`', () => { - store.setData({ ...mockData, ci_status: 'success_with_warnings' }); + it('is true when the CI status is `success-with-warnings`', () => { + store.setData({ ...mockData, ci_status: 'success-with-warnings' }); expect(store.isPipelinePassing).toBe(true); }); diff --git a/spec/javascripts/vue_shared/components/ci_badge_link_spec.js b/spec/javascripts/vue_shared/components/ci_badge_link_spec.js index 4b0b7ba66e5..42481f8c334 100644 --- a/spec/javascripts/vue_shared/components/ci_badge_link_spec.js +++ b/spec/javascripts/vue_shared/components/ci_badge_link_spec.js @@ -59,7 +59,7 @@ describe('CI Badge Link Component', () => { success_warining: { text: 'passed', label: 'passed', - group: 'success_with_warnings', + group: 'success-with-warnings', icon: 'status_warning', details_path: 'status/warning', }, diff --git a/spec/javascripts/vue_shared/components/sidebar/labels_select/dropdown_value_collapsed_spec.js b/spec/javascripts/vue_shared/components/sidebar/labels_select/dropdown_value_collapsed_spec.js index cb49fa31d20..4d3de5e474d 100644 --- a/spec/javascripts/vue_shared/components/sidebar/labels_select/dropdown_value_collapsed_spec.js +++ b/spec/javascripts/vue_shared/components/sidebar/labels_select/dropdown_value_collapsed_spec.js @@ -27,10 +27,10 @@ describe('DropdownValueCollapsedComponent', () => { describe('computed', () => { describe('labelsList', () => { - it('returns empty text when `labels` prop is empty array', () => { + it('returns default text when `labels` prop is empty array', () => { const vmEmptyLabels = createComponent([]); - expect(vmEmptyLabels.labelsList).toBe(''); + expect(vmEmptyLabels.labelsList).toBe('Labels'); vmEmptyLabels.$destroy(); }); diff --git a/spec/lib/gitlab/background_migration/migrate_build_stage_spec.rb b/spec/lib/gitlab/background_migration/migrate_build_stage_spec.rb index 7c7e58d6bb7..582396275ed 100644 --- a/spec/lib/gitlab/background_migration/migrate_build_stage_spec.rb +++ b/spec/lib/gitlab/background_migration/migrate_build_stage_spec.rb @@ -51,7 +51,7 @@ describe Gitlab::BackgroundMigration::MigrateBuildStage, :migration, schema: 201 statuses[:pending]] end - it 'recovers from unique constraint violation only twice' do + it 'recovers from unique constraint violation only twice', :quarantine do allow(described_class::Migratable::Stage) .to receive(:find_by).and_return(nil) diff --git a/spec/lib/gitlab/ci/build/prerequisite/kubernetes_namespace_spec.rb b/spec/lib/gitlab/ci/build/prerequisite/kubernetes_namespace_spec.rb index 62dcd80fad7..e8332b14627 100644 --- a/spec/lib/gitlab/ci/build/prerequisite/kubernetes_namespace_spec.rb +++ b/spec/lib/gitlab/ci/build/prerequisite/kubernetes_namespace_spec.rb @@ -20,7 +20,7 @@ describe Gitlab::Ci::Build::Prerequisite::KubernetesNamespace do let!(:deployment) { create(:deployment, deployable: build) } context 'and a cluster to deploy to' do - let(:cluster) { create(:cluster, projects: [build.project]) } + let(:cluster) { create(:cluster, :group) } before do allow(build.deployment).to receive(:cluster).and_return(cluster) @@ -33,6 +33,12 @@ describe Gitlab::Ci::Build::Prerequisite::KubernetesNamespace do it { is_expected.to be_falsey } end + + context 'and cluster is project type' do + let(:cluster) { create(:cluster, :project) } + + it { is_expected.to be_falsey } + end end context 'and no cluster to deploy to' do @@ -52,7 +58,7 @@ describe Gitlab::Ci::Build::Prerequisite::KubernetesNamespace do subject { described_class.new(build).complete! } context 'completion is required' do - let(:cluster) { create(:cluster, projects: [build.project]) } + let(:cluster) { create(:cluster, :group) } before do allow(build.deployment).to receive(:cluster).and_return(cluster) diff --git a/spec/lib/gitlab/ci/status/build/failed_allowed_spec.rb b/spec/lib/gitlab/ci/status/build/failed_allowed_spec.rb index af03d5a1308..2a5915d75d0 100644 --- a/spec/lib/gitlab/ci/status/build/failed_allowed_spec.rb +++ b/spec/lib/gitlab/ci/status/build/failed_allowed_spec.rb @@ -31,7 +31,7 @@ describe Gitlab::Ci::Status::Build::FailedAllowed do describe '#group' do it 'returns status failed with warnings status group' do - expect(subject.group).to eq 'failed_with_warnings' + expect(subject.group).to eq 'failed-with-warnings' end end diff --git a/spec/lib/gitlab/ci/status/success_warning_spec.rb b/spec/lib/gitlab/ci/status/success_warning_spec.rb index 6d05545d1d8..9493b1d89f2 100644 --- a/spec/lib/gitlab/ci/status/success_warning_spec.rb +++ b/spec/lib/gitlab/ci/status/success_warning_spec.rb @@ -20,7 +20,7 @@ describe Gitlab::Ci::Status::SuccessWarning do end describe '#group' do - it { expect(subject.group).to eq 'success_with_warnings' } + it { expect(subject.group).to eq 'success-with-warnings' } end describe '.matches?' do diff --git a/spec/lib/gitlab/correlation_id_spec.rb b/spec/lib/gitlab/correlation_id_spec.rb deleted file mode 100644 index 584d1f48386..00000000000 --- a/spec/lib/gitlab/correlation_id_spec.rb +++ /dev/null @@ -1,77 +0,0 @@ -# frozen_string_literal: true - -require 'fast_spec_helper' - -describe Gitlab::CorrelationId do - describe '.use_id' do - it 'yields when executed' do - expect { |blk| described_class.use_id('id', &blk) }.to yield_control - end - - it 'stacks correlation ids' do - described_class.use_id('id1') do - described_class.use_id('id2') do |current_id| - expect(current_id).to eq('id2') - end - end - end - - it 'for missing correlation id it generates random one' do - described_class.use_id('id1') do - described_class.use_id(nil) do |current_id| - expect(current_id).not_to be_empty - expect(current_id).not_to eq('id1') - end - end - end - end - - describe '.current_id' do - subject { described_class.current_id } - - it 'returns last correlation id' do - described_class.use_id('id1') do - described_class.use_id('id2') do - is_expected.to eq('id2') - end - end - end - end - - describe '.current_or_new_id' do - subject { described_class.current_or_new_id } - - context 'when correlation id is set' do - it 'returns last correlation id' do - described_class.use_id('id1') do - is_expected.to eq('id1') - end - end - end - - context 'when correlation id is missing' do - it 'returns a new correlation id' do - expect(described_class).to receive(:new_id) - .and_call_original - - is_expected.not_to be_empty - end - end - end - - describe '.ids' do - subject { described_class.send(:ids) } - - it 'returns empty list if not correlation is used' do - is_expected.to be_empty - end - - it 'returns list if correlation ids are used' do - described_class.use_id('id1') do - described_class.use_id('id2') do - is_expected.to eq(%w(id1 id2)) - end - end - end - end -end diff --git a/spec/lib/gitlab/git/commit_spec.rb b/spec/lib/gitlab/git/commit_spec.rb index 507bf222810..25052a79916 100644 --- a/spec/lib/gitlab/git/commit_spec.rb +++ b/spec/lib/gitlab/git/commit_spec.rb @@ -380,7 +380,32 @@ describe Gitlab::Git::Commit, :seed_helper do end end - describe '#batch_by_oid' do + shared_examples '.batch_by_oid' do + context 'with multiple OIDs' do + let(:oids) { [SeedRepo::Commit::ID, SeedRepo::FirstCommit::ID] } + + it 'returns multiple commits' do + commits = described_class.batch_by_oid(repository, oids) + + expect(commits.count).to eq(2) + expect(commits).to all( be_a(Gitlab::Git::Commit) ) + expect(commits.first.sha).to eq(SeedRepo::Commit::ID) + expect(commits.second.sha).to eq(SeedRepo::FirstCommit::ID) + end + end + + context 'when oids is empty' do + it 'returns empty commits' do + commits = described_class.batch_by_oid(repository, []) + + expect(commits.count).to eq(0) + end + end + end + + describe '.batch_by_oid with Gitaly enabled' do + it_should_behave_like '.batch_by_oid' + context 'when oids is empty' do it 'makes no Gitaly request' do expect(Gitlab::GitalyClient).not_to receive(:call) @@ -390,6 +415,16 @@ describe Gitlab::Git::Commit, :seed_helper do end end + describe '.batch_by_oid with Rugged enabled', :enable_rugged do + it_should_behave_like '.batch_by_oid' + + it 'calls out to the Rugged implementation' do + allow_any_instance_of(Rugged).to receive(:rev_parse).with(SeedRepo::Commit::ID).and_call_original + + described_class.batch_by_oid(repository, [SeedRepo::Commit::ID]) + end + end + shared_examples 'extracting commit signature' do context 'when the commit is signed' do let(:commit_id) { '0b4bc9a49b562e85de7cc9e834518ea6828729b9' } diff --git a/spec/lib/gitlab/git/repository_spec.rb b/spec/lib/gitlab/git/repository_spec.rb index 088f8acf554..778950c95e4 100644 --- a/spec/lib/gitlab/git/repository_spec.rb +++ b/spec/lib/gitlab/git/repository_spec.rb @@ -152,13 +152,14 @@ describe Gitlab::Git::Repository, :seed_helper do let(:append_sha) { true } let(:ref) { 'master' } let(:format) { nil } + let(:path) { nil } let(:expected_extension) { 'tar.gz' } let(:expected_filename) { "#{expected_prefix}.#{expected_extension}" } let(:expected_path) { File.join(storage_path, cache_key, expected_filename) } let(:expected_prefix) { "gitlab-git-test-#{ref}-#{SeedRepo::LastCommit::ID}" } - subject(:metadata) { repository.archive_metadata(ref, storage_path, 'gitlab-git-test', format, append_sha: append_sha) } + subject(:metadata) { repository.archive_metadata(ref, storage_path, 'gitlab-git-test', format, append_sha: append_sha, path: path) } it 'sets CommitId to the commit SHA' do expect(metadata['CommitId']).to eq(SeedRepo::LastCommit::ID) @@ -176,6 +177,14 @@ describe Gitlab::Git::Repository, :seed_helper do expect(metadata['ArchivePath']).to eq(expected_path) end + context 'path is set' do + let(:path) { 'foo/bar' } + + it 'appends the path to the prefix' do + expect(metadata['ArchivePrefix']).to eq("#{expected_prefix}-foo-bar") + end + end + context 'append_sha varies archive path and filename' do where(:append_sha, :ref, :expected_prefix) do sha = SeedRepo::LastCommit::ID diff --git a/spec/lib/gitlab/json_logger_spec.rb b/spec/lib/gitlab/json_logger_spec.rb index cff7dd58c8c..d3d9fe9948a 100644 --- a/spec/lib/gitlab/json_logger_spec.rb +++ b/spec/lib/gitlab/json_logger_spec.rb @@ -8,7 +8,7 @@ describe Gitlab::JsonLogger do describe '#format_message' do before do - allow(Gitlab::CorrelationId).to receive(:current_id).and_return('new-correlation-id') + allow(Labkit::Correlation::CorrelationId).to receive(:current_id).and_return('new-correlation-id') end it 'formats strings' do diff --git a/spec/lib/gitlab/sentry_spec.rb b/spec/lib/gitlab/sentry_spec.rb index 1128eaf8560..ae522a588ee 100644 --- a/spec/lib/gitlab/sentry_spec.rb +++ b/spec/lib/gitlab/sentry_spec.rb @@ -27,7 +27,7 @@ describe Gitlab::Sentry do context 'when exceptions should not be raised' do before do allow(described_class).to receive(:should_raise_for_dev?).and_return(false) - allow(Gitlab::CorrelationId).to receive(:current_id).and_return('cid') + allow(Labkit::Correlation::CorrelationId).to receive(:current_id).and_return('cid') end it 'logs the exception with all attributes passed' do @@ -65,7 +65,7 @@ describe Gitlab::Sentry do before do allow(described_class).to receive(:enabled?).and_return(true) - allow(Gitlab::CorrelationId).to receive(:current_id).and_return('cid') + allow(Labkit::Correlation::CorrelationId).to receive(:current_id).and_return('cid') end it 'calls Raven.capture_exception' do diff --git a/spec/lib/gitlab/sidekiq_middleware/correlation_injector_spec.rb b/spec/lib/gitlab/sidekiq_middleware/correlation_injector_spec.rb index a138ad7c910..0ff694d409b 100644 --- a/spec/lib/gitlab/sidekiq_middleware/correlation_injector_spec.rb +++ b/spec/lib/gitlab/sidekiq_middleware/correlation_injector_spec.rb @@ -30,7 +30,7 @@ describe Gitlab::SidekiqMiddleware::CorrelationInjector do it 'injects into payload the correlation id' do expect_any_instance_of(described_class).to receive(:call).and_call_original - Gitlab::CorrelationId.use_id('new-correlation-id') do + Labkit::Correlation::CorrelationId.use_id('new-correlation-id') do TestWorker.perform_async(1234) end diff --git a/spec/lib/gitlab/sidekiq_middleware/correlation_logger_spec.rb b/spec/lib/gitlab/sidekiq_middleware/correlation_logger_spec.rb index 94ae4ffa184..8410467ef1f 100644 --- a/spec/lib/gitlab/sidekiq_middleware/correlation_logger_spec.rb +++ b/spec/lib/gitlab/sidekiq_middleware/correlation_logger_spec.rb @@ -23,7 +23,7 @@ describe Gitlab::SidekiqMiddleware::CorrelationLogger do expect_any_instance_of(described_class).to receive(:call).and_call_original expect_any_instance_of(TestWorker).to receive(:perform).with(1234) do - expect(Gitlab::CorrelationId.current_id).to eq('new-correlation-id') + expect(Labkit::Correlation::CorrelationId.current_id).to eq('new-correlation-id') end Sidekiq::Client.push( diff --git a/spec/lib/gitlab/tracing/factory_spec.rb b/spec/lib/gitlab/tracing/factory_spec.rb deleted file mode 100644 index 945490f0988..00000000000 --- a/spec/lib/gitlab/tracing/factory_spec.rb +++ /dev/null @@ -1,43 +0,0 @@ -# frozen_string_literal: true - -require 'fast_spec_helper' - -describe Gitlab::Tracing::Factory do - describe '.create_tracer' do - let(:service_name) { 'rspec' } - - context "when tracing is not configured" do - it 'ignores null connection strings' do - expect(described_class.create_tracer(service_name, nil)).to be_nil - end - - it 'ignores empty connection strings' do - expect(described_class.create_tracer(service_name, '')).to be_nil - end - - it 'ignores unknown implementations' do - expect(described_class.create_tracer(service_name, 'opentracing://invalid_driver')).to be_nil - end - - it 'ignores invalid connection strings' do - expect(described_class.create_tracer(service_name, 'open?tracing')).to be_nil - end - end - - context "when tracing is configured with jaeger" do - let(:mock_tracer) { double('tracer') } - - it 'processes default connections' do - expect(Gitlab::Tracing::JaegerFactory).to receive(:create_tracer).with(service_name, {}).and_return(mock_tracer) - - expect(described_class.create_tracer(service_name, 'opentracing://jaeger')).to be(mock_tracer) - end - - it 'processes connections with parameters' do - expect(Gitlab::Tracing::JaegerFactory).to receive(:create_tracer).with(service_name, { a: '1', b: '2', c: '3' }).and_return(mock_tracer) - - expect(described_class.create_tracer(service_name, 'opentracing://jaeger?a=1&b=2&c=3')).to be(mock_tracer) - end - end - end -end diff --git a/spec/lib/gitlab/tracing/grpc_interceptor_spec.rb b/spec/lib/gitlab/tracing/grpc_interceptor_spec.rb deleted file mode 100644 index 7f5aecb7baa..00000000000 --- a/spec/lib/gitlab/tracing/grpc_interceptor_spec.rb +++ /dev/null @@ -1,47 +0,0 @@ -# frozen_string_literal: true - -require 'fast_spec_helper' - -describe Gitlab::Tracing::GRPCInterceptor do - subject { described_class.instance } - - shared_examples_for "a grpc interceptor method" do - let(:custom_error) { Class.new(StandardError) } - - it 'yields' do - expect { |b| method.call(kwargs, &b) }.to yield_control - end - - it 'propagates exceptions' do - expect { method.call(kwargs) { raise custom_error } }.to raise_error(custom_error) - end - end - - describe '#request_response' do - let(:method) { subject.method(:request_response) } - let(:kwargs) { { request: {}, call: {}, method: 'grc_method', metadata: {} } } - - it_behaves_like 'a grpc interceptor method' - end - - describe '#client_streamer' do - let(:method) { subject.method(:client_streamer) } - let(:kwargs) { { requests: [], call: {}, method: 'grc_method', metadata: {} } } - - it_behaves_like 'a grpc interceptor method' - end - - describe '#server_streamer' do - let(:method) { subject.method(:server_streamer) } - let(:kwargs) { { request: {}, call: {}, method: 'grc_method', metadata: {} } } - - it_behaves_like 'a grpc interceptor method' - end - - describe '#bidi_streamer' do - let(:method) { subject.method(:bidi_streamer) } - let(:kwargs) { { requests: [], call: {}, method: 'grc_method', metadata: {} } } - - it_behaves_like 'a grpc interceptor method' - end -end diff --git a/spec/lib/gitlab/tracing/jaeger_factory_spec.rb b/spec/lib/gitlab/tracing/jaeger_factory_spec.rb deleted file mode 100644 index 3d6a007cfd9..00000000000 --- a/spec/lib/gitlab/tracing/jaeger_factory_spec.rb +++ /dev/null @@ -1,71 +0,0 @@ -# frozen_string_literal: true - -require 'fast_spec_helper' - -describe Gitlab::Tracing::JaegerFactory do - describe '.create_tracer' do - let(:service_name) { 'rspec' } - - shared_examples_for 'a jaeger tracer' do - it 'responds to active_span methods' do - expect(tracer).to respond_to(:active_span) - end - - it 'yields control' do - expect { |b| tracer.start_active_span('operation_name', &b) }.to yield_control - end - end - - context 'processes default connections' do - it_behaves_like 'a jaeger tracer' do - let(:tracer) { described_class.create_tracer(service_name, {}) } - end - end - - context 'handles debug options' do - it_behaves_like 'a jaeger tracer' do - let(:tracer) { described_class.create_tracer(service_name, { debug: "1" }) } - end - end - - context 'handles const sampler' do - it_behaves_like 'a jaeger tracer' do - let(:tracer) { described_class.create_tracer(service_name, { sampler: "const", sampler_param: "1" }) } - end - end - - context 'handles probabilistic sampler' do - it_behaves_like 'a jaeger tracer' do - let(:tracer) { described_class.create_tracer(service_name, { sampler: "probabilistic", sampler_param: "0.5" }) } - end - end - - context 'handles http_endpoint configurations' do - it_behaves_like 'a jaeger tracer' do - let(:tracer) { described_class.create_tracer(service_name, { http_endpoint: "http://localhost:1234" }) } - end - end - - context 'handles udp_endpoint configurations' do - it_behaves_like 'a jaeger tracer' do - let(:tracer) { described_class.create_tracer(service_name, { udp_endpoint: "localhost:4321" }) } - end - end - - context 'ignores invalid parameters' do - it_behaves_like 'a jaeger tracer' do - let(:tracer) { described_class.create_tracer(service_name, { invalid: "true" }) } - end - end - - context 'accepts the debug parameter when strict_parser is set' do - it_behaves_like 'a jaeger tracer' do - let(:tracer) { described_class.create_tracer(service_name, { debug: "1", strict_parsing: "1" }) } - end - end - - it 'rejects invalid parameters when strict_parser is set' do - expect { described_class.create_tracer(service_name, { invalid: "true", strict_parsing: "1" }) }.to raise_error(StandardError) - end - end -end diff --git a/spec/lib/gitlab/tracing/rack_middleware_spec.rb b/spec/lib/gitlab/tracing/rack_middleware_spec.rb deleted file mode 100644 index 13d4d8a89f7..00000000000 --- a/spec/lib/gitlab/tracing/rack_middleware_spec.rb +++ /dev/null @@ -1,62 +0,0 @@ -# frozen_string_literal: true - -require 'spec_helper' - -describe Gitlab::Tracing::RackMiddleware do - using RSpec::Parameterized::TableSyntax - - describe '#call' do - context 'for normal middleware flow' do - let(:fake_app) { -> (env) { fake_app_response } } - subject { described_class.new(fake_app) } - let(:request) { } - - context 'for 200 responses' do - let(:fake_app_response) { [200, { 'Content-Type': 'text/plain' }, ['OK']] } - - it 'delegates correctly' do - expect(subject.call(Rack::MockRequest.env_for("/"))).to eq(fake_app_response) - end - end - - context 'for 500 responses' do - let(:fake_app_response) { [500, { 'Content-Type': 'text/plain' }, ['Error']] } - - it 'delegates correctly' do - expect(subject.call(Rack::MockRequest.env_for("/"))).to eq(fake_app_response) - end - end - end - - context 'when an application is raising an exception' do - let(:custom_error) { Class.new(StandardError) } - let(:fake_app) { ->(env) { raise custom_error } } - - subject { described_class.new(fake_app) } - - it 'delegates propagates exceptions correctly' do - expect { subject.call(Rack::MockRequest.env_for("/")) }.to raise_error(custom_error) - end - end - end - - describe '.build_sanitized_url_from_env' do - def env_for_url(url) - env = Rack::MockRequest.env_for(input_url) - env['action_dispatch.parameter_filter'] = [/token/] - - env - end - - where(:input_url, :output_url) do - '/gitlab-org/gitlab-ce' | 'http://example.org/gitlab-org/gitlab-ce' - '/gitlab-org/gitlab-ce?safe=1' | 'http://example.org/gitlab-org/gitlab-ce?safe=1' - '/gitlab-org/gitlab-ce?private_token=secret' | 'http://example.org/gitlab-org/gitlab-ce?private_token=%5BFILTERED%5D' - '/gitlab-org/gitlab-ce?mixed=1&private_token=secret' | 'http://example.org/gitlab-org/gitlab-ce?mixed=1&private_token=%5BFILTERED%5D' - end - - with_them do - it { expect(described_class.build_sanitized_url_from_env(env_for_url(input_url))).to eq(output_url) } - end - end -end diff --git a/spec/lib/gitlab/tracing/rails/action_view_subscriber_spec.rb b/spec/lib/gitlab/tracing/rails/action_view_subscriber_spec.rb deleted file mode 100644 index 0bbaf5968ed..00000000000 --- a/spec/lib/gitlab/tracing/rails/action_view_subscriber_spec.rb +++ /dev/null @@ -1,147 +0,0 @@ -# frozen_string_literal: true - -require 'fast_spec_helper' -require 'rspec-parameterized' - -describe Gitlab::Tracing::Rails::ActionViewSubscriber do - using RSpec::Parameterized::TableSyntax - - shared_examples 'an actionview notification' do - it 'notifies the tracer when the hash contains null values' do - expect(subject).to receive(:postnotify_span).with(notification_name, start, finish, tags: expected_tags, exception: exception) - - subject.public_send(notify_method, start, finish, payload) - end - - it 'notifies the tracer when the payload is missing values' do - expect(subject).to receive(:postnotify_span).with(notification_name, start, finish, tags: expected_tags, exception: exception) - - subject.public_send(notify_method, start, finish, payload.compact) - end - - it 'does not throw exceptions when with the default tracer' do - expect { subject.public_send(notify_method, start, finish, payload) }.not_to raise_error - end - end - - describe '.instrument' do - it 'is unsubscribeable' do - unsubscribe = described_class.instrument - - expect(unsubscribe).not_to be_nil - expect { unsubscribe.call }.not_to raise_error - end - end - - describe '#notify_render_template' do - subject { described_class.new } - let(:start) { Time.now } - let(:finish) { Time.now } - let(:notification_name) { 'render_template' } - let(:notify_method) { :notify_render_template } - - where(:identifier, :layout, :exception) do - nil | nil | nil - "" | nil | nil - "show.haml" | nil | nil - nil | "" | nil - nil | "layout.haml" | nil - nil | nil | StandardError.new - end - - with_them do - let(:payload) do - { - exception: exception, - identifier: identifier, - layout: layout - } - end - - let(:expected_tags) do - { - 'component' => 'ActionView', - 'template.id' => identifier, - 'template.layout' => layout - } - end - - it_behaves_like 'an actionview notification' - end - end - - describe '#notify_render_collection' do - subject { described_class.new } - let(:start) { Time.now } - let(:finish) { Time.now } - let(:notification_name) { 'render_collection' } - let(:notify_method) { :notify_render_collection } - - where( - :identifier, :count, :expected_count, :cache_hits, :expected_cache_hits, :exception) do - nil | nil | 0 | nil | 0 | nil - "" | nil | 0 | nil | 0 | nil - "show.haml" | nil | 0 | nil | 0 | nil - nil | 0 | 0 | nil | 0 | nil - nil | 1 | 1 | nil | 0 | nil - nil | nil | 0 | 0 | 0 | nil - nil | nil | 0 | 1 | 1 | nil - nil | nil | 0 | nil | 0 | StandardError.new - end - - with_them do - let(:payload) do - { - exception: exception, - identifier: identifier, - count: count, - cache_hits: cache_hits - } - end - - let(:expected_tags) do - { - 'component' => 'ActionView', - 'template.id' => identifier, - 'template.count' => expected_count, - 'template.cache.hits' => expected_cache_hits - } - end - - it_behaves_like 'an actionview notification' - end - end - - describe '#notify_render_partial' do - subject { described_class.new } - let(:start) { Time.now } - let(:finish) { Time.now } - let(:notification_name) { 'render_partial' } - let(:notify_method) { :notify_render_partial } - - where(:identifier, :exception) do - nil | nil - "" | nil - "show.haml" | nil - nil | StandardError.new - end - - with_them do - let(:payload) do - { - exception: exception, - identifier: identifier - } - end - - let(:expected_tags) do - { - 'component' => 'ActionView', - 'template.id' => identifier - } - end - - it_behaves_like 'an actionview notification' - end - end -end diff --git a/spec/lib/gitlab/tracing/rails/active_record_subscriber_spec.rb b/spec/lib/gitlab/tracing/rails/active_record_subscriber_spec.rb deleted file mode 100644 index 7bd0875fa68..00000000000 --- a/spec/lib/gitlab/tracing/rails/active_record_subscriber_spec.rb +++ /dev/null @@ -1,73 +0,0 @@ -# frozen_string_literal: true - -require 'fast_spec_helper' -require 'rspec-parameterized' - -describe Gitlab::Tracing::Rails::ActiveRecordSubscriber do - using RSpec::Parameterized::TableSyntax - - describe '.instrument' do - it 'is unsubscribeable' do - unsubscribe = described_class.instrument - - expect(unsubscribe).not_to be_nil - expect { unsubscribe.call }.not_to raise_error - end - end - - describe '#notify' do - subject { described_class.new } - let(:start) { Time.now } - let(:finish) { Time.now } - - where(:name, :operation_name, :exception, :connection_id, :cached, :cached_response, :sql) do - nil | "active_record:sqlquery" | nil | nil | nil | false | nil - "" | "active_record:sqlquery" | nil | nil | nil | false | nil - "User Load" | "active_record:User Load" | nil | nil | nil | false | nil - "Repo Load" | "active_record:Repo Load" | StandardError.new | nil | nil | false | nil - nil | "active_record:sqlquery" | nil | 123 | nil | false | nil - nil | "active_record:sqlquery" | nil | nil | false | false | nil - nil | "active_record:sqlquery" | nil | nil | true | true | nil - nil | "active_record:sqlquery" | nil | nil | true | true | "SELECT * FROM users" - end - - with_them do - def payload - { - name: name, - exception: exception, - connection_id: connection_id, - cached: cached, - sql: sql - } - end - - def expected_tags - { - "component" => "ActiveRecord", - "span.kind" => "client", - "db.type" => "sql", - "db.connection_id" => connection_id, - "db.cached" => cached_response, - "db.statement" => sql - } - end - - it 'notifies the tracer when the hash contains null values' do - expect(subject).to receive(:postnotify_span).with(operation_name, start, finish, tags: expected_tags, exception: exception) - - subject.notify(start, finish, payload) - end - - it 'notifies the tracer when the payload is missing values' do - expect(subject).to receive(:postnotify_span).with(operation_name, start, finish, tags: expected_tags, exception: exception) - - subject.notify(start, finish, payload.compact) - end - - it 'does not throw exceptions when with the default tracer' do - expect { subject.notify(start, finish, payload) }.not_to raise_error - end - end - end -end diff --git a/spec/lib/gitlab/tracing/sidekiq/client_middleware_spec.rb b/spec/lib/gitlab/tracing/sidekiq/client_middleware_spec.rb deleted file mode 100644 index 3755860b5ba..00000000000 --- a/spec/lib/gitlab/tracing/sidekiq/client_middleware_spec.rb +++ /dev/null @@ -1,43 +0,0 @@ -# frozen_string_literal: true - -require 'fast_spec_helper' - -describe Gitlab::Tracing::Sidekiq::ClientMiddleware do - describe '#call' do - let(:worker_class) { 'test_worker_class' } - let(:job) do - { - 'class' => "jobclass", - 'queue' => "jobqueue", - 'retry' => 0, - 'args' => %w{1 2 3} - } - end - let(:queue) { 'test_queue' } - let(:redis_pool) { double("redis_pool") } - let(:custom_error) { Class.new(StandardError) } - let(:span) { OpenTracing.start_span('test', ignore_active_scope: true) } - - subject { described_class.new } - - it 'yields' do - expect(subject).to receive(:in_tracing_span).with( - operation_name: "sidekiq:jobclass", - tags: { - "component" => "sidekiq", - "span.kind" => "client", - "sidekiq.queue" => "jobqueue", - "sidekiq.jid" => nil, - "sidekiq.retry" => "0", - "sidekiq.args" => "1, 2, 3" - } - ).and_yield(span) - - expect { |b| subject.call(worker_class, job, queue, redis_pool, &b) }.to yield_control - end - - it 'propagates exceptions' do - expect { subject.call(worker_class, job, queue, redis_pool) { raise custom_error } }.to raise_error(custom_error) - end - end -end diff --git a/spec/lib/gitlab/tracing/sidekiq/server_middleware_spec.rb b/spec/lib/gitlab/tracing/sidekiq/server_middleware_spec.rb deleted file mode 100644 index c3087de785a..00000000000 --- a/spec/lib/gitlab/tracing/sidekiq/server_middleware_spec.rb +++ /dev/null @@ -1,43 +0,0 @@ -# frozen_string_literal: true - -require 'fast_spec_helper' - -describe Gitlab::Tracing::Sidekiq::ServerMiddleware do - describe '#call' do - let(:worker_class) { 'test_worker_class' } - let(:job) do - { - 'class' => "jobclass", - 'queue' => "jobqueue", - 'retry' => 0, - 'args' => %w{1 2 3} - } - end - let(:queue) { 'test_queue' } - let(:custom_error) { Class.new(StandardError) } - let(:span) { OpenTracing.start_span('test', ignore_active_scope: true) } - subject { described_class.new } - - it 'yields' do - expect(subject).to receive(:in_tracing_span).with( - hash_including( - operation_name: "sidekiq:jobclass", - tags: { - "component" => "sidekiq", - "span.kind" => "server", - "sidekiq.queue" => "jobqueue", - "sidekiq.jid" => nil, - "sidekiq.retry" => "0", - "sidekiq.args" => "1, 2, 3" - } - ) - ).and_yield(span) - - expect { |b| subject.call(worker_class, job, queue, &b) }.to yield_control - end - - it 'propagates exceptions' do - expect { subject.call(worker_class, job, queue) { raise custom_error } }.to raise_error(custom_error) - end - end -end diff --git a/spec/lib/gitlab/workhorse_spec.rb b/spec/lib/gitlab/workhorse_spec.rb index d02d9be5c5c..f8332757fcd 100644 --- a/spec/lib/gitlab/workhorse_spec.rb +++ b/spec/lib/gitlab/workhorse_spec.rb @@ -16,40 +16,80 @@ describe Gitlab::Workhorse do let(:ref) { 'master' } let(:format) { 'zip' } let(:storage_path) { Gitlab.config.gitlab.repository_downloads_path } - let(:base_params) { repository.archive_metadata(ref, storage_path, format, append_sha: nil) } - let(:gitaly_params) do - base_params.merge( - 'GitalyServer' => { - 'address' => Gitlab::GitalyClient.address(project.repository_storage), - 'token' => Gitlab::GitalyClient.token(project.repository_storage) - }, - 'GitalyRepository' => repository.gitaly_repository.to_h.deep_stringify_keys - ) - end + let(:path) { 'some/path' if Feature.enabled?(:git_archive_path, default_enabled: true) } + let(:metadata) { repository.archive_metadata(ref, storage_path, format, append_sha: nil, path: path) } let(:cache_disabled) { false } subject do - described_class.send_git_archive(repository, ref: ref, format: format, append_sha: nil) + described_class.send_git_archive(repository, ref: ref, format: format, append_sha: nil, path: path) end before do allow(described_class).to receive(:git_archive_cache_disabled?).and_return(cache_disabled) end - it 'sets the header correctly' do - key, command, params = decode_workhorse_header(subject) + context 'feature flag disabled' do + before do + stub_feature_flags(git_archive_path: false) + end - expect(key).to eq('Gitlab-Workhorse-Send-Data') - expect(command).to eq('git-archive') - expect(params).to include(gitaly_params) + it 'sets the header correctly' do + key, command, params = decode_workhorse_header(subject) + + expected_params = metadata.merge( + 'GitalyRepository' => repository.gitaly_repository.to_h, + 'GitalyServer' => { + address: Gitlab::GitalyClient.address(project.repository_storage), + token: Gitlab::GitalyClient.token(project.repository_storage) + } + ) + + expect(key).to eq('Gitlab-Workhorse-Send-Data') + expect(command).to eq('git-archive') + expect(params).to eq(expected_params.deep_stringify_keys) + end + + context 'when archive caching is disabled' do + let(:cache_disabled) { true } + + it 'tells workhorse not to use the cache' do + _, _, params = decode_workhorse_header(subject) + expect(params).to include({ 'DisableCache' => true }) + end + end end - context 'when archive caching is disabled' do - let(:cache_disabled) { true } + context 'feature flag enabled' do + it 'sets the header correctly' do + key, command, params = decode_workhorse_header(subject) + + expect(key).to eq('Gitlab-Workhorse-Send-Data') + expect(command).to eq('git-archive') + expect(params).to eq({ + 'GitalyServer' => { + address: Gitlab::GitalyClient.address(project.repository_storage), + token: Gitlab::GitalyClient.token(project.repository_storage) + }, + 'ArchivePath' => metadata['ArchivePath'], + 'GetArchiveRequest' => Base64.encode64( + Gitaly::GetArchiveRequest.new( + repository: repository.gitaly_repository, + commit_id: metadata['CommitId'], + prefix: metadata['ArchivePrefix'], + format: Gitaly::GetArchiveRequest::Format::ZIP, + path: path + ).to_proto + ) + }.deep_stringify_keys) + end - it 'tells workhorse not to use the cache' do - _, _, params = decode_workhorse_header(subject) - expect(params).to include({ 'DisableCache' => true }) + context 'when archive caching is disabled' do + let(:cache_disabled) { true } + + it 'tells workhorse not to use the cache' do + _, _, params = decode_workhorse_header(subject) + expect(params).to include({ 'DisableCache' => true }) + end end end diff --git a/spec/models/clusters/applications/knative_spec.rb b/spec/models/clusters/applications/knative_spec.rb index 5e68f2634da..405b5ad691c 100644 --- a/spec/models/clusters/applications/knative_spec.rb +++ b/spec/models/clusters/applications/knative_spec.rb @@ -109,7 +109,7 @@ describe Clusters::Applications::Knative do subject { knative.install_command } it 'is initialized with latest version' do - expect(subject.version).to eq('0.3.0') + expect(subject.version).to eq('0.5.0') end it_behaves_like 'a command' diff --git a/spec/presenters/merge_request_presenter_spec.rb b/spec/presenters/merge_request_presenter_spec.rb index 4a0f91c4c7a..e5f08aeb1fa 100644 --- a/spec/presenters/merge_request_presenter_spec.rb +++ b/spec/presenters/merge_request_presenter_spec.rb @@ -40,8 +40,8 @@ describe MergeRequestPresenter do allow(pipeline).to receive(:has_warnings?) { true } end - it 'returns "success_with_warnings"' do - is_expected.to eq('success_with_warnings') + it 'returns "success-with-warnings"' do + is_expected.to eq('success-with-warnings') end end diff --git a/spec/rack_servers/configs/puma.rb b/spec/rack_servers/configs/puma.rb deleted file mode 100644 index d6b6d83d648..00000000000 --- a/spec/rack_servers/configs/puma.rb +++ /dev/null @@ -1,32 +0,0 @@ -# frozen_string_literal: true - -# Note: this file is used for testing puma in `spec/rack_servers/puma_spec.rb` only -# Note: as per the convention in `config/puma.example.development.rb`, -# this file will replace `/home/git` with the actual working directory - -directory '/home/git' -threads 1, 10 -queue_requests false -pidfile '/home/git/gitlab/tmp/pids/puma.pid' -bind 'unix:///home/git/gitlab/tmp/tests/puma.socket' -workers 1 -preload_app! -worker_timeout 60 - -require_relative "/home/git/gitlab/lib/gitlab/cluster/lifecycle_events" -require_relative "/home/git/gitlab/lib/gitlab/cluster/puma_worker_killer_initializer" - -before_fork do - Gitlab::Cluster::PumaWorkerKillerInitializer.start @config.options - Gitlab::Cluster::LifecycleEvents.do_before_fork -end - -Gitlab::Cluster::LifecycleEvents.set_puma_options @config.options -on_worker_boot do - Gitlab::Cluster::LifecycleEvents.do_worker_start - File.write('/home/git/gitlab/tmp/tests/puma-worker-ready', Process.pid) -end - -on_restart do - Gitlab::Cluster::LifecycleEvents.do_master_restart -end diff --git a/spec/rack_servers/puma_spec.rb b/spec/rack_servers/puma_spec.rb index 891df4f1a66..8290473821c 100644 --- a/spec/rack_servers/puma_spec.rb +++ b/spec/rack_servers/puma_spec.rb @@ -1,20 +1,20 @@ # frozen_string_literal: true -require 'fileutils' +require 'spec_helper' +require 'fileutils' require 'excon' -require 'spec_helper' - describe 'Puma' do before(:all) do - project_root = File.expand_path('../..', __dir__) - - config_lines = File.read('spec/rack_servers/configs/puma.rb') - .gsub('/home/git/gitlab', project_root) - .gsub('/home/git', project_root) - - config_path = File.join(project_root, "tmp/tests/puma.rb") + project_root = Rails.root.to_s + config_lines = File.read(Rails.root.join('config/puma.example.development.rb')) + .gsub('config.ru', File.join(__dir__, 'configs/config.ru')) + .gsub('workers 2', 'workers 1') + .gsub('/home/git/gitlab.socket', File.join(project_root, 'tmp/tests/puma.socket')) + .gsub('on_worker_boot do', "on_worker_boot do\nFile.write('#{File.join(project_root, 'tmp/tests/puma-worker-ready')}', Process.pid)") + .gsub(%r{/home/git(/gitlab)?}, project_root) + config_path = File.join(project_root, 'tmp/tests/puma.rb') @socket_path = File.join(project_root, 'tmp/tests/puma.socket') File.write(config_path, config_lines) diff --git a/spec/requests/api/helpers_spec.rb b/spec/requests/api/helpers_spec.rb index a0c64d295c0..25a312cb734 100644 --- a/spec/requests/api/helpers_spec.rb +++ b/spec/requests/api/helpers_spec.rb @@ -251,7 +251,7 @@ describe API::Helpers do correlation_id: 'new-correlation-id' }, extra: {}) - Gitlab::CorrelationId.use_id('new-correlation-id') do + Labkit::Correlation::CorrelationId.use_id('new-correlation-id') do handle_api_exception(exception) end end diff --git a/spec/requests/api/internal_spec.rb b/spec/requests/api/internal_spec.rb index 1ce8f520962..88c19448373 100644 --- a/spec/requests/api/internal_spec.rb +++ b/spec/requests/api/internal_spec.rb @@ -998,15 +998,13 @@ describe API::Internal do context 'when the feature is disabled' do it 'does not invoke MergeRequests::PushOptionsHandlerService' do - Feature.disable(:mr_push_options) + stub_feature_flags(mr_push_options: false) - expect(MergeRequests::PushOptionsHandlerService).to receive(:new) + expect(MergeRequests::PushOptionsHandlerService).not_to receive(:new) expect do post api('/internal/post_receive'), params: valid_params end.not_to change { MergeRequest.count } - - Feature.enable(:mr_push_options) end end end diff --git a/spec/requests/api/issues_spec.rb b/spec/requests/api/issues_spec.rb index 86484ce62f8..0fa34688371 100644 --- a/spec/requests/api/issues_spec.rb +++ b/spec/requests/api/issues_spec.rb @@ -1480,12 +1480,20 @@ describe API::Issues do let(:params) { { title: 'new issue', labels: 'label, label2', created_at: creation_time } } context 'by an admin' do - it 'sets the creation time on the new issue' do + before do post api("/projects/#{project.id}/issues", admin), params: params + end + it 'sets the creation time on the new issue' do expect(response).to have_gitlab_http_status(201) expect(Time.parse(json_response['created_at'])).to be_like_time(creation_time) end + + it 'sets the system notes timestamp based on creation time' do + issue = Issue.find(json_response['id']) + + expect(issue.resource_label_events.last.created_at).to be_like_time(creation_time) + end end context 'by a project owner' do diff --git a/spec/requests/api/runner_spec.rb b/spec/requests/api/runner_spec.rb index 3585a827838..29515709a74 100644 --- a/spec/requests/api/runner_spec.rb +++ b/spec/requests/api/runner_spec.rb @@ -168,6 +168,32 @@ describe API::Runner, :clean_gitlab_redis_shared_state do end end + context 'when access_level is provided for Runner' do + context 'when access_level is set to ref_protected' do + it 'creates runner' do + post api('/runners'), params: { + token: registration_token, + access_level: 'ref_protected' + } + + expect(response).to have_gitlab_http_status 201 + expect(Ci::Runner.first.ref_protected?).to be true + end + end + + context 'when access_level is set to not_protected' do + it 'creates runner' do + post api('/runners'), params: { + token: registration_token, + access_level: 'not_protected' + } + + expect(response).to have_gitlab_http_status 201 + expect(Ci::Runner.first.ref_protected?).to be false + end + end + end + context 'when maximum job timeout is specified' do it 'creates runner' do post api('/runners'), params: { diff --git a/spec/services/clusters/refresh_service_spec.rb b/spec/services/clusters/refresh_service_spec.rb index 58ab3c3cf73..9e442ebf4e9 100644 --- a/spec/services/clusters/refresh_service_spec.rb +++ b/spec/services/clusters/refresh_service_spec.rb @@ -93,14 +93,32 @@ describe Clusters::RefreshService do let(:group) { cluster.group } let(:project) { create(:project, group: group) } - include_examples 'creates a kubernetes namespace' + context 'when ci_preparing_state feature flag is enabled' do + include_examples 'does not create a kubernetes namespace' - context 'when project already has kubernetes namespace' do + context 'when project already has kubernetes namespace' do + before do + create(:cluster_kubernetes_namespace, project: project, cluster: cluster) + end + + include_examples 'does not create a kubernetes namespace' + end + end + + context 'when ci_preparing_state feature flag is disabled' do before do - create(:cluster_kubernetes_namespace, project: project, cluster: cluster) + stub_feature_flags(ci_preparing_state: false) end - include_examples 'does not create a kubernetes namespace' + include_examples 'creates a kubernetes namespace' + + context 'when project already has kubernetes namespace' do + before do + create(:cluster_kubernetes_namespace, project: project, cluster: cluster) + end + + include_examples 'does not create a kubernetes namespace' + end end end end diff --git a/spec/services/notification_service_spec.rb b/spec/services/notification_service_spec.rb index 8e34406133a..4b40c86574f 100644 --- a/spec/services/notification_service_spec.rb +++ b/spec/services/notification_service_spec.rb @@ -643,6 +643,64 @@ describe NotificationService, :mailer do end end + describe 'Participating project notification settings have priority over group and global settings if available' do + let!(:group) { create(:group) } + let!(:maintainer) { group.add_owner(create(:user, username: 'maintainer')).user } + let!(:user1) { group.add_developer(create(:user, username: 'user_with_project_and_custom_setting')).user } + + let(:project) { create(:project, :public, namespace: group) } + let(:issue) { create :issue, project: project, assignees: [assignee], description: '' } + + before do + reset_delivered_emails! + + create_notification_setting(user1, project, :participating) + end + + context 'custom on group' do + [nil, true].each do |new_issue_value| + value_caption = new_issue_value || 'nil' + it "does not send an email to user1 when a new issue is created and new_issue is set to #{value_caption}" do + update_custom_notification(:new_issue, user1, resource: group, value: new_issue_value) + + notification.new_issue(issue, maintainer) + should_not_email(user1) + end + end + end + + context 'watch on group' do + it 'does not send an email' do + user1.notification_settings_for(group).update!(level: :watch) + + notification.new_issue(issue, maintainer) + should_not_email(user1) + end + end + + context 'custom on global, global on group' do + it 'does not send an email' do + user1.notification_settings_for(nil).update!(level: :custom) + + user1.notification_settings_for(group).update!(level: :global) + + notification.new_issue(issue, maintainer) + should_not_email(user1) + end + end + + context 'watch on global, global on group' do + it 'does not send an email' do + user1.notification_settings_for(nil).update!(level: :watch) + + user1.notification_settings_for(group).update!(level: :global) + + notification.new_issue(issue, maintainer) + should_not_email(user1) + end + end + end + describe 'Issues' do let(:group) { create(:group) } let(:project) { create(:project, :public, namespace: group) } @@ -660,7 +718,7 @@ describe NotificationService, :mailer do end describe '#new_issue' do - it do + it 'notifies the expected users' do notification.new_issue(issue, @u_disabled) should_email(assignee) @@ -1639,7 +1697,7 @@ describe NotificationService, :mailer do end describe '#project_was_moved' do - it do + it 'notifies the expected users' do notification.project_was_moved(project, "gitlab/gitlab") should_email(@u_watcher) diff --git a/spec/spec_helper.rb b/spec/spec_helper.rb index 60db3e1bc46..8ca4c172707 100644 --- a/spec/spec_helper.rb +++ b/spec/spec_helper.rb @@ -83,6 +83,7 @@ RSpec.configure do |config| config.include Devise::Test::IntegrationHelpers, type: :feature config.include LoginHelpers, type: :feature config.include SearchHelpers, type: :feature + config.include WaitHelpers, type: :feature config.include EmailHelpers, :mailer, type: :mailer config.include Warden::Test::Helpers, type: :request config.include Gitlab::Routing, type: :routing diff --git a/spec/support/helpers/email_helpers.rb b/spec/support/helpers/email_helpers.rb index ad6e1064499..ed049daba80 100644 --- a/spec/support/helpers/email_helpers.rb +++ b/spec/support/helpers/email_helpers.rb @@ -16,7 +16,9 @@ module EmailHelpers end def should_email(user, times: 1, recipients: email_recipients) - expect(sent_to_user(user, recipients: recipients)).to eq(times) + amount = sent_to_user(user, recipients: recipients) + failed_message = lambda { "User #{user.username} (#{user.id}): email test failed (expected #{times}, got #{amount})" } + expect(amount).to eq(times), failed_message end def should_not_email(user, recipients: email_recipients) diff --git a/spec/support/helpers/notification_helpers.rb b/spec/support/helpers/notification_helpers.rb index 8d84510fb73..44c2051598c 100644 --- a/spec/support/helpers/notification_helpers.rb +++ b/spec/support/helpers/notification_helpers.rb @@ -17,11 +17,15 @@ module NotificationHelpers def create_user_with_notification(level, username, resource = project) user = create(:user, username: username) + create_notification_setting(user, resource, level) + + user + end + + def create_notification_setting(user, resource, level) setting = user.notification_settings_for(resource) setting.level = level setting.save - - user end # Create custom notifications diff --git a/spec/support/helpers/wait_for_requests.rb b/spec/support/helpers/wait_for_requests.rb index c7f878b7371..45b9faa0fea 100644 --- a/spec/support/helpers/wait_for_requests.rb +++ b/spec/support/helpers/wait_for_requests.rb @@ -50,20 +50,6 @@ module WaitForRequests finished_all_vue_resource_requests? end - # Waits until the passed block returns true - def wait_for(condition_name, max_wait_time: Capybara.default_max_wait_time, polling_interval: 0.01) - wait_until = Time.now + max_wait_time.seconds - loop do - break if yield - - if Time.now > wait_until - raise "Condition not met: #{condition_name}" - else - sleep(polling_interval) - end - end - end - def finished_all_vue_resource_requests? Capybara.page.evaluate_script('window.activeVueResources || 0').zero? end diff --git a/spec/support/helpers/wait_helpers.rb b/spec/support/helpers/wait_helpers.rb new file mode 100644 index 00000000000..7e8e25798e8 --- /dev/null +++ b/spec/support/helpers/wait_helpers.rb @@ -0,0 +1,20 @@ +module WaitHelpers + extend self + + # Waits until the passed block returns true + def wait_for(condition_name, max_wait_time: Capybara.default_max_wait_time, polling_interval: 0.01, reload: false) + wait_until = Time.now + max_wait_time.seconds + loop do + result = yield + break if result + + page.refresh if reload + + if Time.now > wait_until + raise "Condition not met: #{condition_name}" + else + sleep(polling_interval) + end + end + end +end diff --git a/spec/support/shared_examples/quick_actions/issue/remove_due_date_quick_action_shared_examples.rb b/spec/support/shared_examples/quick_actions/issue/remove_due_date_quick_action_shared_examples.rb index dd1676a08e2..657c2a60d24 100644 --- a/spec/support/shared_examples/quick_actions/issue/remove_due_date_quick_action_shared_examples.rb +++ b/spec/support/shared_examples/quick_actions/issue/remove_due_date_quick_action_shared_examples.rb @@ -11,7 +11,7 @@ shared_examples 'remove_due_date quick action' do visit project_issue_path(project, issue) page.within '.due_date' do - expect(page).to have_content 'No due date' + expect(page).to have_content 'None' end end end diff --git a/spec/views/notify/pipeline_failed_email.text.erb_spec.rb b/spec/views/notify/pipeline_failed_email.text.erb_spec.rb new file mode 100644 index 00000000000..a7d3dc09fd4 --- /dev/null +++ b/spec/views/notify/pipeline_failed_email.text.erb_spec.rb @@ -0,0 +1,39 @@ +# frozen_string_literal: true +require 'spec_helper' + +describe 'notify/pipeline_failed_email.text.erb' do + include Devise::Test::ControllerHelpers + + let(:user) { create(:user) } + let(:project) { create(:project, :repository) } + let(:merge_request) { create(:merge_request, :simple, source_project: project) } + + let(:pipeline) do + create(:ci_pipeline, + :failed, + project: project, + user: user, + ref: project.default_branch, + sha: project.commit.sha) + end + + before do + assign(:project, project) + assign(:pipeline, pipeline) + assign(:merge_request, merge_request) + end + + it 'renders the email correctly' do + job = create(:ci_build, :failed, pipeline: pipeline, project: pipeline.project) + + render + + expect(rendered).to have_content('Your pipeline has failed') + expect(rendered).to have_content(pipeline.project.name) + expect(rendered).to have_content(pipeline.git_commit_message.truncate(50)) + expect(rendered).to have_content(pipeline.commit.author_name) + expect(rendered).to have_content("##{pipeline.id}") + expect(rendered).to have_content(pipeline.user.name) + expect(rendered).to have_content("/-/jobs/#{job.id}/raw") + end +end diff --git a/spec/views/shared/_label_row.html.haml.rb b/spec/views/shared/_label_row.html.haml.rb new file mode 100644 index 00000000000..a58d5efc1e3 --- /dev/null +++ b/spec/views/shared/_label_row.html.haml.rb @@ -0,0 +1,32 @@ +require 'spec_helper' + +describe 'shared/_label_row.html.haml' do + label_types = { + 'project label': :label, + 'group label': :group_label + } + + label_types.each do |label_type, label_factory| + let!(:label) { create(label_factory) } + + context "for a #{label_type}" do + it 'has a non-linked label title' do + render 'shared/label_row', label: label + + expect(rendered).not_to have_css('a', text: label.title) + end + + it "has Issues link for #{label_type}" do + render 'shared/label_row', label: label + + expect(rendered).to have_css('a', text: 'Issues') + end + + it "has Merge request link for #{label_type}" do + render 'shared/label_row', label: label + + expect(rendered).to have_css('a', text: 'Merge requests') + end + end + end +end diff --git a/spec/workers/cluster_configure_worker_spec.rb b/spec/workers/cluster_configure_worker_spec.rb index 83f76809435..bdb8e0e9c84 100644 --- a/spec/workers/cluster_configure_worker_spec.rb +++ b/spec/workers/cluster_configure_worker_spec.rb @@ -10,25 +10,35 @@ describe ClusterConfigureWorker, '#perform' do stub_feature_flags(ci_preparing_state: ci_preparing_state_enabled) end - context 'when group cluster' do - let(:cluster) { create(:cluster, :group, :provided_by_gcp) } - let(:group) { cluster.group } + shared_examples 'configured cluster' do + it 'creates a namespace' do + expect(Clusters::RefreshService).to receive(:create_or_update_namespaces_for_cluster).with(cluster).once - context 'when group has no projects' do - it 'does not create a namespace' do - expect_any_instance_of(Clusters::Gcp::Kubernetes::CreateOrUpdateNamespaceService).not_to receive(:execute) + worker.perform(cluster.id) + end + end - worker.perform(cluster.id) - end + shared_examples 'unconfigured cluster' do + it 'does not create a namespace' do + expect(Clusters::RefreshService).not_to receive(:create_or_update_namespaces_for_cluster) + + worker.perform(cluster.id) end + end + + context 'group cluster' do + let(:cluster) { create(:cluster, :group, :provided_by_gcp) } + let(:group) { cluster.group } context 'when group has a project' do let!(:project) { create(:project, group: group) } - it 'creates a namespace for the project' do - expect_any_instance_of(Clusters::Gcp::Kubernetes::CreateOrUpdateNamespaceService).to receive(:execute).once + it_behaves_like 'configured cluster' + + context 'ci_preparing_state feature is enabled' do + let(:ci_preparing_state_enabled) { true } - worker.perform(cluster.id) + it_behaves_like 'unconfigured cluster' end end @@ -36,32 +46,26 @@ describe ClusterConfigureWorker, '#perform' do let!(:subgroup) { create(:group, parent: group) } let!(:project) { create(:project, group: subgroup) } - it 'creates a namespace for the project' do - expect_any_instance_of(Clusters::Gcp::Kubernetes::CreateOrUpdateNamespaceService).to receive(:execute).once + it_behaves_like 'configured cluster' - worker.perform(cluster.id) + context 'ci_preparing_state feature is enabled' do + let(:ci_preparing_state_enabled) { true } + + it_behaves_like 'unconfigured cluster' end end end context 'when provider type is gcp' do - let(:cluster) { create(:cluster, :project, :provided_by_gcp) } - - it 'configures kubernetes platform' do - expect_any_instance_of(Clusters::Gcp::Kubernetes::CreateOrUpdateNamespaceService).to receive(:execute) + let!(:cluster) { create(:cluster, :project, :provided_by_gcp) } - described_class.new.perform(cluster.id) - end + it_behaves_like 'configured cluster' end context 'when provider type is user' do - let(:cluster) { create(:cluster, :project, :provided_by_user) } + let!(:cluster) { create(:cluster, :project, :provided_by_user) } - it 'configures kubernetes platform' do - expect_any_instance_of(Clusters::Gcp::Kubernetes::CreateOrUpdateNamespaceService).to receive(:execute) - - described_class.new.perform(cluster.id) - end + it_behaves_like 'configured cluster' end context 'when cluster does not exist' do @@ -71,15 +75,4 @@ describe ClusterConfigureWorker, '#perform' do described_class.new.perform(123) end end - - context 'ci_preparing_state feature is enabled' do - let(:cluster) { create(:cluster) } - let(:ci_preparing_state_enabled) { true } - - it 'does not configure the cluster' do - expect(Clusters::RefreshService).not_to receive(:create_or_update_namespaces_for_cluster) - - described_class.new.perform(cluster.id) - end - end end diff --git a/spec/workers/cluster_project_configure_worker_spec.rb b/spec/workers/cluster_project_configure_worker_spec.rb index afdea55adf4..2ac9d0f61b4 100644 --- a/spec/workers/cluster_project_configure_worker_spec.rb +++ b/spec/workers/cluster_project_configure_worker_spec.rb @@ -4,18 +4,11 @@ require 'spec_helper' describe ClusterProjectConfigureWorker, '#perform' do let(:worker) { described_class.new } + let(:cluster) { create(:cluster, :project) } - context 'ci_preparing_state feature is enabled' do - let(:cluster) { create(:cluster) } + it 'configures the cluster' do + expect(Clusters::RefreshService).to receive(:create_or_update_namespaces_for_project) - before do - stub_feature_flags(ci_preparing_state: true) - end - - it 'does not configure the cluster' do - expect(Clusters::RefreshService).not_to receive(:create_or_update_namespaces_for_project) - - described_class.new.perform(cluster.id) - end + described_class.new.perform(cluster.projects.first.id) end end |