diff options
author | Kamil Trzcinski <ayufan@ayufan.eu> | 2017-12-05 13:29:41 +0100 |
---|---|---|
committer | Kamil Trzcinski <ayufan@ayufan.eu> | 2017-12-05 13:29:41 +0100 |
commit | 9429514e303bdceee0621b5c4bf02ad8ca0a7add (patch) | |
tree | a7bc5cc425601c4ea73ff9a9be90eebc21a93b17 /spec | |
parent | b9d8d85e20af046c7dbe11c69fd3252f0486b9b2 (diff) | |
parent | 3138fcdcd70fd30fea74deff537b9836d6c94d21 (diff) | |
download | gitlab-ce-9429514e303bdceee0621b5c4bf02ad8ca0a7add.tar.gz |
Merge branch '35616-move-k8-to-cluster-page' into cluster-page-with-list-clusters
Diffstat (limited to 'spec')
34 files changed, 440 insertions, 195 deletions
diff --git a/spec/controllers/projects/clusters/gcp_controller_spec.rb b/spec/controllers/projects/clusters/gcp_controller_spec.rb index e0fe291e724..bb5ef7bb365 100644 --- a/spec/controllers/projects/clusters/gcp_controller_spec.rb +++ b/spec/controllers/projects/clusters/gcp_controller_spec.rb @@ -16,7 +16,7 @@ describe Projects::Clusters::GcpController do end context 'when omniauth has been configured' do - let(:key) { 'secere-key' } + let(:key) { 'secret-key' } let(:session_key_for_redirect_uri) do GoogleApi::CloudPlatform::Client.session_key_for_redirect_uri(key) diff --git a/spec/controllers/projects/clusters/user_controller_spec.rb b/spec/controllers/projects/clusters/user_controller_spec.rb index 06a9c50c5cc..22005e0dc66 100644 --- a/spec/controllers/projects/clusters/user_controller_spec.rb +++ b/spec/controllers/projects/clusters/user_controller_spec.rb @@ -58,7 +58,7 @@ describe Projects::Clusters::UserController do project.add_master(user) sign_in(user) end - + context 'when creates a cluster' do it 'creates a new cluster' do expect(ClusterProvisionWorker).to receive(:perform_async) diff --git a/spec/controllers/projects/clusters_controller_spec.rb b/spec/controllers/projects/clusters_controller_spec.rb index 9ff0b745073..806e33e0e4e 100644 --- a/spec/controllers/projects/clusters_controller_spec.rb +++ b/spec/controllers/projects/clusters_controller_spec.rb @@ -181,7 +181,7 @@ describe Projects::ClustersController do end describe 'PUT update' do - context 'Managed' do + context 'when cluster is provided by GCP' do let(:cluster) { create(:cluster, :provided_by_gcp, projects: [project]) } let(:user) { create(:user) } @@ -207,7 +207,7 @@ describe Projects::ClustersController do go cluster.reload - expect(response).to redirect_to(namespace_project_cluster_path(project.namespace, project, project.cluster)) + expect(response).to redirect_to(project_cluster_path(project, project.cluster)) expect(flash[:notice]).to eq('Cluster was successfully updated.') expect(cluster.enabled).to be_falsey end @@ -232,7 +232,7 @@ describe Projects::ClustersController do end end - context 'User' do + context 'when cluster is provided by user' do let(:cluster) { create(:cluster, :provided_by_user, projects: [project]) } let(:user) { create(:user) } @@ -317,7 +317,7 @@ describe Projects::ClustersController do end describe 'security' do - set(:cluster) { create(:cluster, :providing_by_gcp, projects: [project]) } + set(:cluster) { create(:cluster, :provided_by_gcp, projects: [project]) } let(:params) do { cluster: { enabled: false } } @@ -335,8 +335,8 @@ describe Projects::ClustersController do def go put :update, params.merge(namespace_id: project.namespace, - project_id: project, - id: cluster) + project_id: project, + id: cluster) end def go_json @@ -356,7 +356,7 @@ describe Projects::ClustersController do sign_in(user) end - context 'GCP' do + context 'when cluster is provided by GCP' do context 'when cluster is created' do let!(:cluster) { create(:cluster, :provided_by_gcp, projects: [project]) } @@ -366,7 +366,7 @@ describe Projects::ClustersController do .and change { Clusters::Platforms::Kubernetes.count }.by(-1) .and change { Clusters::Providers::Gcp.count }.by(-1) - expect(response).to redirect_to(namespace_project_clusters_path(project.namespace, project)) + expect(response).to redirect_to(project_clusters_path(project)) expect(flash[:notice]).to eq('Cluster integration was successfully removed.') end end @@ -379,25 +379,23 @@ describe Projects::ClustersController do .to change { Clusters::Cluster.count }.by(-1) .and change { Clusters::Providers::Gcp.count }.by(-1) - expect(response).to redirect_to(namespace_project_clusters_path(project.namespace, project)) + expect(response).to redirect_to(project_clusters_path(project)) expect(flash[:notice]).to eq('Cluster integration was successfully removed.') end end end - context 'User' do - context 'when provider is user' do - let!(:cluster) { create(:cluster, :provided_by_user, projects: [project]) } + context 'when cluster is provided by user' do + let!(:cluster) { create(:cluster, :provided_by_user, projects: [project]) } - it "destroys and redirects back to clusters list" do - expect { go } - .to change { Clusters::Cluster.count }.by(-1) - .and change { Clusters::Platforms::Kubernetes.count }.by(-1) - .and change { Clusters::Providers::Gcp.count }.by(0) + it "destroys and redirects back to clusters list" do + expect { go } + .to change { Clusters::Cluster.count }.by(-1) + .and change { Clusters::Platforms::Kubernetes.count }.by(-1) + .and change { Clusters::Providers::Gcp.count }.by(0) - expect(response).to redirect_to(namespace_project_clusters_path(project.namespace, project)) - expect(flash[:notice]).to eq('Cluster integration was successfully removed.') - end + expect(response).to redirect_to(project_clusters_path(project)) + expect(flash[:notice]).to eq('Cluster integration was successfully removed.') end end end diff --git a/spec/controllers/projects_controller_spec.rb b/spec/controllers/projects_controller_spec.rb index e7ab714c550..e4b2bbb7c51 100644 --- a/spec/controllers/projects_controller_spec.rb +++ b/spec/controllers/projects_controller_spec.rb @@ -261,6 +261,27 @@ describe ProjectsController do expect(response).to redirect_to(namespace_project_path) end end + + context 'when the project is forked and has a repository', :request_store do + let(:public_project) { create(:project, :public, :repository) } + let(:other_user) { create(:user) } + + render_views + + before do + # View the project as a user that does not have any rights + sign_in(other_user) + + fork_project(public_project) + end + + it 'does not increase the number of queries when the project is forked' do + expected_query = /#{public_project.fork_network.find_forks_in(other_user.namespace).to_sql}/ + + expect { get(:show, namespace_id: public_project.namespace, id: public_project) } + .not_to exceed_query_limit(1).for_query(expected_query) + end + end end describe "#update" do diff --git a/spec/features/projects/clusters/applications_spec.rb b/spec/features/projects/clusters/applications_spec.rb index 6104bad0125..b34cd061ec6 100644 --- a/spec/features/projects/clusters/applications_spec.rb +++ b/spec/features/projects/clusters/applications_spec.rb @@ -18,7 +18,7 @@ feature 'Clusters Applications', :js do context 'when cluster is being created' do let(:cluster) { create(:cluster, :providing_by_gcp, projects: [project])} - + scenario 'user is unable to install applications' do page.within('.js-cluster-application-row-helm') do expect(page.find(:css, '.js-cluster-application-install-button')['disabled']).to eq('true') diff --git a/spec/features/projects/clusters/gcp_spec.rb b/spec/features/projects/clusters/gcp_spec.rb index 82f94dc4422..5a00b463960 100644 --- a/spec/features/projects/clusters/gcp_spec.rb +++ b/spec/features/projects/clusters/gcp_spec.rb @@ -90,18 +90,18 @@ feature 'Gcp Cluster', :js do page.find(:css, '.js-toggle-cluster').click click_button 'Save' end - + it 'user sees the successful message' do expect(page).to have_content('Cluster was successfully updated.') end end - + context 'when user changes cluster parameters' do before do fill_in 'cluster_platform_kubernetes_attributes_namespace', with: 'my-namespace' click_button 'Save changes' end - + it 'user sees the successful message' do expect(page).to have_content('Cluster was successfully updated.') expect(cluster.reload.platform_kubernetes.namespace).to eq('my-namespace') @@ -114,7 +114,7 @@ feature 'Gcp Cluster', :js do click_link 'Remove integration' end end - + it 'user sees creation form with the successful message' do expect(page).to have_content('Cluster integration was successfully removed.') expect(page).to have_link('Add cluster') diff --git a/spec/features/projects/clusters/user_spec.rb b/spec/features/projects/clusters/user_spec.rb index c30f13c4682..414f4acba86 100644 --- a/spec/features/projects/clusters/user_spec.rb +++ b/spec/features/projects/clusters/user_spec.rb @@ -22,14 +22,19 @@ feature 'User Cluster', :js do context 'when user filled form with valid parameters' do before do + fill_in 'cluster_name', with: 'dev-cluster' fill_in 'cluster_platform_kubernetes_attributes_api_url', with: 'http://example.com' fill_in 'cluster_platform_kubernetes_attributes_token', with: 'my-token' - fill_in 'cluster_name', with: 'dev-cluster' click_button 'Add cluster' end - it 'user sees a cluster details page and creation status' do - expect(page).to have_content('Cluster was successfully created on Google Container Engine') + it 'user sees a cluster details page' do + expect(page).to have_content('Enable cluster integration') + expect(page.find_field('cluster[name]').value).to eq('dev-cluster') + expect(page.find_field('cluster[platform_kubernetes_attributes][api_url]').value) + .to have_content('http://example.com') + expect(page.find_field('cluster[platform_kubernetes_attributes][token]').value) + .to have_content('my-token') end end diff --git a/spec/features/projects/pipelines/pipeline_spec.rb b/spec/features/projects/pipelines/pipeline_spec.rb index b8fa1a54c24..888e290292b 100644 --- a/spec/features/projects/pipelines/pipeline_spec.rb +++ b/spec/features/projects/pipelines/pipeline_spec.rb @@ -185,6 +185,36 @@ describe 'Pipeline', :js do end end + context 'when user does not have access to read jobs' do + before do + project.update(public_builds: false) + end + + describe 'GET /:project/pipelines/:id' do + include_context 'pipeline builds' + + let(:project) { create(:project, :repository) } + let(:pipeline) { create(:ci_pipeline, project: project, ref: 'master', sha: project.commit.id, user: user) } + + before do + visit project_pipeline_path(project, pipeline) + end + + it 'shows the pipeline graph' do + expect(page).to have_selector('.pipeline-visualization') + expect(page).to have_content('Build') + expect(page).to have_content('Test') + expect(page).to have_content('Deploy') + expect(page).to have_content('Retry') + expect(page).to have_content('Cancel running') + end + + it 'should not link to job' do + expect(page).not_to have_selector('.js-pipeline-graph-job-link') + end + end + end + describe 'GET /:project/pipelines/:id/builds' do include_context 'pipeline builds' diff --git a/spec/helpers/markup_helper_spec.rb b/spec/helpers/markup_helper_spec.rb index 62ea6d48542..ba0039f3a11 100644 --- a/spec/helpers/markup_helper_spec.rb +++ b/spec/helpers/markup_helper_spec.rb @@ -205,7 +205,7 @@ describe MarkupHelper do it "uses Wiki pipeline for markdown files" do allow(@wiki).to receive(:format).and_return(:markdown) - expect(helper).to receive(:markdown_unsafe).with('wiki content', pipeline: :wiki, project: project, project_wiki: @wiki, page_slug: "nested/page") + expect(helper).to receive(:markdown_unsafe).with('wiki content', pipeline: :wiki, project: project, project_wiki: @wiki, page_slug: "nested/page", issuable_state_filter_enabled: true) helper.render_wiki_content(@wiki) end diff --git a/spec/javascripts/boards/issue_spec.js b/spec/javascripts/boards/issue_spec.js index ccde657789a..10b88878c2a 100644 --- a/spec/javascripts/boards/issue_spec.js +++ b/spec/javascripts/boards/issue_spec.js @@ -146,6 +146,12 @@ describe('Issue model', () => { expect(issue.isFetching.subscriptions).toBe(false); }); + it('sets loading state', () => { + issue.setLoadingState('foo', true); + + expect(issue.isLoading.foo).toBe(true); + }); + describe('update', () => { it('passes assignee ids when there are assignees', (done) => { spyOn(Vue.http, 'patch').and.callFake((url, data) => { diff --git a/spec/javascripts/notes/components/issue_note_actions_spec.js b/spec/javascripts/notes/components/note_actions_spec.js index 7bcc061f167..ab81aabb992 100644 --- a/spec/javascripts/notes/components/issue_note_actions_spec.js +++ b/spec/javascripts/notes/components/note_actions_spec.js @@ -1,6 +1,6 @@ import Vue from 'vue'; import store from '~/notes/stores'; -import issueActions from '~/notes/components/issue_note_actions.vue'; +import noteActions from '~/notes/components/note_actions.vue'; import { userDataMock } from '../mock_data'; describe('issse_note_actions component', () => { @@ -8,7 +8,7 @@ describe('issse_note_actions component', () => { let Component; beforeEach(() => { - Component = Vue.extend(issueActions); + Component = Vue.extend(noteActions); }); afterEach(() => { diff --git a/spec/javascripts/notes/components/issue_note_attachment_spec.js b/spec/javascripts/notes/components/note_attachment_spec.js index 8f33b874ad6..b14a518b622 100644 --- a/spec/javascripts/notes/components/issue_note_attachment_spec.js +++ b/spec/javascripts/notes/components/note_attachment_spec.js @@ -1,5 +1,5 @@ import Vue from 'vue'; -import issueNoteAttachment from '~/notes/components/issue_note_attachment.vue'; +import noteAttachment from '~/notes/components/note_attachment.vue'; describe('issue note attachment', () => { it('should render properly', () => { @@ -11,7 +11,7 @@ describe('issue note attachment', () => { }, }; - const Component = Vue.extend(issueNoteAttachment); + const Component = Vue.extend(noteAttachment); const vm = new Component({ propsData: props, }).$mount(); diff --git a/spec/javascripts/notes/components/issue_note_awards_list_spec.js b/spec/javascripts/notes/components/note_awards_list_spec.js index c689c452143..15995ec5a05 100644 --- a/spec/javascripts/notes/components/issue_note_awards_list_spec.js +++ b/spec/javascripts/notes/components/note_awards_list_spec.js @@ -1,9 +1,9 @@ import Vue from 'vue'; import store from '~/notes/stores'; -import awardsNote from '~/notes/components/issue_note_awards_list.vue'; +import awardsNote from '~/notes/components/note_awards_list.vue'; import { noteableDataMock, notesDataMock } from '../mock_data'; -describe('issue_note_awards_list component', () => { +describe('note_awards_list component', () => { let vm; let awardsMock; diff --git a/spec/javascripts/notes/components/issue_note_edited_text_spec.js b/spec/javascripts/notes/components/note_edited_text_spec.js index 6603241eb64..e0b991c32ec 100644 --- a/spec/javascripts/notes/components/issue_note_edited_text_spec.js +++ b/spec/javascripts/notes/components/note_edited_text_spec.js @@ -1,12 +1,12 @@ import Vue from 'vue'; -import issueNoteEditedText from '~/notes/components/issue_note_edited_text.vue'; +import noteEditedText from '~/notes/components/note_edited_text.vue'; -describe('issue_note_edited_text', () => { +describe('note_edited_text', () => { let vm; let props; beforeEach(() => { - const Component = Vue.extend(issueNoteEditedText); + const Component = Vue.extend(noteEditedText); props = { actionText: 'Edited', className: 'foo-bar', diff --git a/spec/javascripts/notes/components/issue_note_header_spec.js b/spec/javascripts/notes/components/note_header_spec.js index 83ea18508ae..16a76b11321 100644 --- a/spec/javascripts/notes/components/issue_note_header_spec.js +++ b/spec/javascripts/notes/components/note_header_spec.js @@ -1,13 +1,13 @@ import Vue from 'vue'; -import issueNoteHeader from '~/notes/components/issue_note_header.vue'; +import noteHeader from '~/notes/components/note_header.vue'; import store from '~/notes/stores'; -describe('issue_note_header component', () => { +describe('note_header component', () => { let vm; let Component; beforeEach(() => { - Component = Vue.extend(issueNoteHeader); + Component = Vue.extend(noteHeader); }); afterEach(() => { diff --git a/spec/javascripts/notes/components/issue_note_signed_out_widget_spec.js b/spec/javascripts/notes/components/note_signed_out_widget_spec.js index f20d9ce9268..6cba8053888 100644 --- a/spec/javascripts/notes/components/issue_note_signed_out_widget_spec.js +++ b/spec/javascripts/notes/components/note_signed_out_widget_spec.js @@ -1,13 +1,13 @@ import Vue from 'vue'; -import issueNoteSignedOut from '~/notes/components/issue_note_signed_out_widget.vue'; +import noteSignedOut from '~/notes/components/note_signed_out_widget.vue'; import store from '~/notes/stores'; import { notesDataMock } from '../mock_data'; -describe('issue_note_signed_out_widget component', () => { +describe('note_signed_out_widget component', () => { let vm; beforeEach(() => { - const Component = Vue.extend(issueNoteSignedOut); + const Component = Vue.extend(noteSignedOut); store.dispatch('setNotesData', notesDataMock); vm = new Component({ diff --git a/spec/javascripts/pipelines/graph/job_component_spec.js b/spec/javascripts/pipelines/graph/job_component_spec.js index 342ee6c1242..23c87610d83 100644 --- a/spec/javascripts/pipelines/graph/job_component_spec.js +++ b/spec/javascripts/pipelines/graph/job_component_spec.js @@ -1,8 +1,10 @@ import Vue from 'vue'; import jobComponent from '~/pipelines/components/graph/job_component.vue'; +import mountComponent from '../../helpers/vue_mount_component_helper'; describe('pipeline graph job component', () => { let JobComponent; + let component; const mockJob = { id: 4256, @@ -13,6 +15,7 @@ describe('pipeline graph job component', () => { label: 'passed', group: 'success', details_path: '/root/ci-mock/builds/4256', + has_details: true, action: { icon: 'retry', title: 'Retry', @@ -26,13 +29,13 @@ describe('pipeline graph job component', () => { JobComponent = Vue.extend(jobComponent); }); + afterEach(() => { + component.$destroy(); + }); + describe('name with link', () => { it('should render the job name and status with a link', (done) => { - const component = new JobComponent({ - propsData: { - job: mockJob, - }, - }).$mount(); + component = mountComponent(JobComponent, { job: mockJob }); Vue.nextTick(() => { const link = component.$el.querySelector('a'); @@ -56,23 +59,23 @@ describe('pipeline graph job component', () => { describe('name without link', () => { it('it should render status and name', () => { - const component = new JobComponent({ - propsData: { - job: { - id: 4256, - name: 'test', - status: { - icon: 'icon_status_success', - text: 'passed', - label: 'passed', - group: 'success', - details_path: '/root/ci-mock/builds/4256', - }, + component = mountComponent(JobComponent, { + job: { + id: 4256, + name: 'test', + status: { + icon: 'icon_status_success', + text: 'passed', + label: 'passed', + group: 'success', + details_path: '/root/ci-mock/builds/4256', + has_details: false, }, }, - }).$mount(); + }); expect(component.$el.querySelector('.js-status-icon-success')).toBeDefined(); + expect(component.$el.querySelector('a')).toBeNull(); expect( component.$el.querySelector('.ci-status-text').textContent.trim(), @@ -82,11 +85,7 @@ describe('pipeline graph job component', () => { describe('action icon', () => { it('it should render the action icon', () => { - const component = new JobComponent({ - propsData: { - job: mockJob, - }, - }).$mount(); + component = mountComponent(JobComponent, { job: mockJob }); expect(component.$el.querySelector('a.ci-action-icon-container')).toBeDefined(); expect(component.$el.querySelector('i.ci-action-icon-wrapper')).toBeDefined(); @@ -95,24 +94,20 @@ describe('pipeline graph job component', () => { describe('dropdown', () => { it('should render the dropdown action icon', () => { - const component = new JobComponent({ - propsData: { - job: mockJob, - isDropdown: true, - }, - }).$mount(); + component = mountComponent(JobComponent, { + job: mockJob, + isDropdown: true, + }); expect(component.$el.querySelector('a.ci-action-icon-wrapper')).toBeDefined(); }); }); it('should render provided class name', () => { - const component = new JobComponent({ - propsData: { - job: mockJob, - cssClassJobName: 'css-class-job-name', - }, - }).$mount(); + component = mountComponent(JobComponent, { + job: mockJob, + cssClassJobName: 'css-class-job-name', + }); expect( component.$el.querySelector('a').classList.contains('css-class-job-name'), diff --git a/spec/javascripts/sidebar/mock_data.js b/spec/javascripts/sidebar/mock_data.js index 0682b463043..3b094d20838 100644 --- a/spec/javascripts/sidebar/mock_data.js +++ b/spec/javascripts/sidebar/mock_data.js @@ -1,6 +1,6 @@ /* eslint-disable quote-props*/ -const sidebarMockData = { +const RESPONSE_MAP = { 'GET': { '/gitlab-org/gitlab-shell/issues/5.json': { id: 45, @@ -66,6 +66,65 @@ const sidebarMockData = { }, labels: [], }, + '/gitlab-org/gitlab-shell/issues/5.json?serializer=sidebar': { + assignees: [ + { + name: 'User 0', + username: 'user0', + id: 22, + state: 'active', + avatar_url: 'http: //www.gravatar.com/avatar/52e4ce24a915fb7e51e1ad3b57f4b00a?s=80\u0026d=identicon', + web_url: 'http: //localhost:3001/user0', + }, + { + name: 'Marguerite Bartell', + username: 'tajuana', + id: 18, + state: 'active', + avatar_url: 'http: //www.gravatar.com/avatar/4852a41fb41616bf8f140d3701673f53?s=80\u0026d=identicon', + web_url: 'http: //localhost:3001/tajuana', + }, + { + name: 'Laureen Ritchie', + username: 'michaele.will', + id: 16, + state: 'active', + avatar_url: 'http: //www.gravatar.com/avatar/e301827eb03be955c9c172cb9a8e4e8a?s=80\u0026d=identicon', + web_url: 'http: //localhost:3001/michaele.will', + }, + ], + human_time_estimate: null, + human_total_time_spent: null, + participants: [ + { + name: 'User 0', + username: 'user0', + id: 22, + state: 'active', + avatar_url: 'http: //www.gravatar.com/avatar/52e4ce24a915fb7e51e1ad3b57f4b00a?s=80\u0026d=identicon', + web_url: 'http: //localhost:3001/user0', + }, + { + name: 'Marguerite Bartell', + username: 'tajuana', + id: 18, + state: 'active', + avatar_url: 'http: //www.gravatar.com/avatar/4852a41fb41616bf8f140d3701673f53?s=80\u0026d=identicon', + web_url: 'http: //localhost:3001/tajuana', + }, + { + name: 'Laureen Ritchie', + username: 'michaele.will', + id: 16, + state: 'active', + avatar_url: 'http: //www.gravatar.com/avatar/e301827eb03be955c9c172cb9a8e4e8a?s=80\u0026d=identicon', + web_url: 'http: //localhost:3001/michaele.will', + }, + ], + subscribed: true, + time_estimate: 0, + total_time_spent: 0, + }, '/autocomplete/projects?project_id=15': [ { 'id': 0, @@ -113,9 +172,10 @@ const sidebarMockData = { }, }; -export default { +const mockData = { + responseMap: RESPONSE_MAP, mediator: { - endpoint: '/gitlab-org/gitlab-shell/issues/5.json', + endpoint: '/gitlab-org/gitlab-shell/issues/5.json?serializer=sidebar', toggleSubscriptionEndpoint: '/gitlab-org/gitlab-shell/issues/5/toggle_subscription', moveIssueEndpoint: '/gitlab-org/gitlab-shell/issues/5/move', projectsAutocompleteEndpoint: '/autocomplete/projects?project_id=15', @@ -141,12 +201,14 @@ export default { name: 'Administrator', username: 'root', }, +}; - sidebarMockInterceptor(request, next) { - const body = sidebarMockData[request.method.toUpperCase()][request.url]; +mockData.sidebarMockInterceptor = function (request, next) { + const body = this.responseMap[request.method.toUpperCase()][request.url]; - next(request.respondWith(JSON.stringify(body), { - status: 200, - })); - }, -}; + next(request.respondWith(JSON.stringify(body), { + status: 200, + })); +}.bind(mockData); + +export default mockData; diff --git a/spec/javascripts/sidebar/sidebar_mediator_spec.js b/spec/javascripts/sidebar/sidebar_mediator_spec.js index 7deb1fd2118..14c34d5a78c 100644 --- a/spec/javascripts/sidebar/sidebar_mediator_spec.js +++ b/spec/javascripts/sidebar/sidebar_mediator_spec.js @@ -33,10 +33,29 @@ describe('Sidebar mediator', () => { .catch(done.fail); }); - it('fetches the data', () => { - spyOn(this.mediator.service, 'get').and.callThrough(); - this.mediator.fetch(); - expect(this.mediator.service.get).toHaveBeenCalled(); + it('fetches the data', (done) => { + const mockData = Mock.responseMap.GET['/gitlab-org/gitlab-shell/issues/5.json?serializer=sidebar']; + spyOn(this.mediator, 'processFetchedData').and.callThrough(); + + this.mediator.fetch() + .then(() => { + expect(this.mediator.processFetchedData).toHaveBeenCalledWith(mockData); + }) + .then(done) + .catch(done.fail); + }); + + it('processes fetched data', () => { + const mockData = Mock.responseMap.GET['/gitlab-org/gitlab-shell/issues/5.json?serializer=sidebar']; + this.mediator.processFetchedData(mockData); + + expect(this.mediator.store.assignees).toEqual(mockData.assignees); + expect(this.mediator.store.humanTimeEstimate).toEqual(mockData.human_time_estimate); + expect(this.mediator.store.humanTotalTimeSpent).toEqual(mockData.human_total_time_spent); + expect(this.mediator.store.participants).toEqual(mockData.participants); + expect(this.mediator.store.subscribed).toEqual(mockData.subscribed); + expect(this.mediator.store.timeEstimate).toEqual(mockData.time_estimate); + expect(this.mediator.store.totalTimeSpent).toEqual(mockData.total_time_spent); }); it('sets moveToProjectId', () => { diff --git a/spec/javascripts/sidebar/sidebar_store_spec.js b/spec/javascripts/sidebar/sidebar_store_spec.js index 51dee64fb93..ea4eae1e23f 100644 --- a/spec/javascripts/sidebar/sidebar_store_spec.js +++ b/spec/javascripts/sidebar/sidebar_store_spec.js @@ -120,6 +120,12 @@ describe('Sidebar store', () => { expect(this.store.isFetching.participants).toEqual(false); }); + it('sets loading state', () => { + this.store.setLoadingState('assignees', true); + + expect(this.store.isLoading.assignees).toEqual(true); + }); + it('set time tracking data', () => { this.store.setTimeTrackingData(Mock.time); expect(this.store.timeEstimate).toEqual(Mock.time.time_estimate); diff --git a/spec/lib/api/helpers/pagination_spec.rb b/spec/lib/api/helpers/pagination_spec.rb index 59deca7757b..a547988d631 100644 --- a/spec/lib/api/helpers/pagination_spec.rb +++ b/spec/lib/api/helpers/pagination_spec.rb @@ -92,6 +92,27 @@ describe API::Helpers::Pagination do subject.paginate(resource) end end + + context 'if order' do + it 'is not present it adds default order(:id) if no order is present' do + resource.order_values = [] + + paginated_relation = subject.paginate(resource) + + expect(resource.order_values).to be_empty + expect(paginated_relation.order_values).to be_present + expect(paginated_relation.order_values.first).to be_ascending + expect(paginated_relation.order_values.first.expr.name).to eq :id + end + + it 'is present it does not add anything' do + paginated_relation = subject.paginate(resource.order(created_at: :desc)) + + expect(paginated_relation.order_values).to be_present + expect(paginated_relation.order_values.first).to be_descending + expect(paginated_relation.order_values.first.expr.name).to eq :created_at + end + end end context 'when resource empty' do diff --git a/spec/lib/gitlab/git/storage/circuit_breaker_spec.rb b/spec/lib/gitlab/git/storage/circuit_breaker_spec.rb index 72dabca793a..f34c9f09057 100644 --- a/spec/lib/gitlab/git/storage/circuit_breaker_spec.rb +++ b/spec/lib/gitlab/git/storage/circuit_breaker_spec.rb @@ -27,6 +27,7 @@ describe Gitlab::Git::Storage::CircuitBreaker, clean_gitlab_redis_shared_state: def set_in_redis(name, value) Gitlab::Git::Storage.redis.with do |redis| + redis.zadd(Gitlab::Git::Storage::REDIS_KNOWN_KEYS, 0, cache_key) redis.hmset(cache_key, name, value) end.first end @@ -181,6 +182,24 @@ describe Gitlab::Git::Storage::CircuitBreaker, clean_gitlab_redis_shared_state: expect(circuit_breaker.last_failure).to be_nil end + it 'maintains known storage keys' do + Timecop.freeze do + # Insert an old key to expire + old_entry = Time.now.to_i - 3.days.to_i + Gitlab::Git::Storage.redis.with do |redis| + redis.zadd(Gitlab::Git::Storage::REDIS_KNOWN_KEYS, old_entry, 'to_be_removed') + end + + circuit_breaker.perform { '' } + + known_keys = Gitlab::Git::Storage.redis.with do |redis| + redis.zrange(Gitlab::Git::Storage::REDIS_KNOWN_KEYS, 0, -1) + end + + expect(known_keys).to contain_exactly(cache_key) + end + end + it 'only performs the accessibility check once' do expect(Gitlab::Git::Storage::ForkedStorageCheck) .to receive(:storage_available?).once.and_call_original diff --git a/spec/lib/gitlab/git/storage/health_spec.rb b/spec/lib/gitlab/git/storage/health_spec.rb index 4a14a5201d1..d7a52a04fbb 100644 --- a/spec/lib/gitlab/git/storage/health_spec.rb +++ b/spec/lib/gitlab/git/storage/health_spec.rb @@ -6,6 +6,7 @@ describe Gitlab::Git::Storage::Health, clean_gitlab_redis_shared_state: true, br def set_in_redis(cache_key, value) Gitlab::Git::Storage.redis.with do |redis| + redis.zadd(Gitlab::Git::Storage::REDIS_KNOWN_KEYS, 0, cache_key) redis.hmset(cache_key, :failure_count, value) end.first end diff --git a/spec/lib/gitlab/shell_spec.rb b/spec/lib/gitlab/shell_spec.rb index 2158b2837e2..eec6858a5de 100644 --- a/spec/lib/gitlab/shell_spec.rb +++ b/spec/lib/gitlab/shell_spec.rb @@ -200,18 +200,18 @@ describe Gitlab::Shell do describe '#fork_repository' do it 'returns true when the command succeeds' do expect(Gitlab::Popen).to receive(:popen) - .with([projects_path, 'fork-project', 'current/storage', 'project/path.git', 'new/storage', 'new-namespace'], + .with([projects_path, 'fork-repository', 'current/storage', 'project/path.git', 'new/storage', 'fork/path.git'], nil, popen_vars).and_return([nil, 0]) - expect(gitlab_shell.fork_repository('current/storage', 'project/path', 'new/storage', 'new-namespace')).to be true + expect(gitlab_shell.fork_repository('current/storage', 'project/path', 'new/storage', 'fork/path')).to be true end it 'return false when the command fails' do expect(Gitlab::Popen).to receive(:popen) - .with([projects_path, 'fork-project', 'current/storage', 'project/path.git', 'new/storage', 'new-namespace'], + .with([projects_path, 'fork-repository', 'current/storage', 'project/path.git', 'new/storage', 'fork/path.git'], nil, popen_vars).and_return(["error", 1]) - expect(gitlab_shell.fork_repository('current/storage', 'project/path', 'new/storage', 'new-namespace')).to be false + expect(gitlab_shell.fork_repository('current/storage', 'project/path', 'new/storage', 'fork/path')).to be false end end diff --git a/spec/lib/google_api/cloud_platform/client_spec.rb b/spec/lib/google_api/cloud_platform/client_spec.rb index fac23dce44d..ecb4034ec8b 100644 --- a/spec/lib/google_api/cloud_platform/client_spec.rb +++ b/spec/lib/google_api/cloud_platform/client_spec.rb @@ -3,6 +3,7 @@ require 'spec_helper' describe GoogleApi::CloudPlatform::Client do let(:token) { 'token' } let(:client) { described_class.new(token, nil) } + let(:user_agent_options) { client.instance_eval { user_agent_header } } describe '.session_key_for_redirect_uri' do let(:state) { 'random_string' } @@ -55,7 +56,8 @@ describe GoogleApi::CloudPlatform::Client do before do allow_any_instance_of(Google::Apis::ContainerV1::ContainerService) - .to receive(:get_zone_cluster).and_return(gke_cluster) + .to receive(:get_zone_cluster).with(any_args, options: user_agent_options) + .and_return(gke_cluster) end it { is_expected.to eq(gke_cluster) } @@ -74,7 +76,8 @@ describe GoogleApi::CloudPlatform::Client do before do allow_any_instance_of(Google::Apis::ContainerV1::ContainerService) - .to receive(:create_cluster).and_return(operation) + .to receive(:create_cluster).with(any_args, options: user_agent_options) + .and_return(operation) end it { is_expected.to eq(operation) } @@ -102,7 +105,8 @@ describe GoogleApi::CloudPlatform::Client do before do allow_any_instance_of(Google::Apis::ContainerV1::ContainerService) - .to receive(:get_zone_operation).and_return(operation) + .to receive(:get_zone_operation).with(any_args, options: user_agent_options) + .and_return(operation) end it { is_expected.to eq(operation) } @@ -125,4 +129,18 @@ describe GoogleApi::CloudPlatform::Client do it { is_expected.to be_nil } end end + + describe '#user_agent_header' do + subject { client.instance_eval { user_agent_header } } + + it 'returns a RequestOptions object' do + expect(subject).to be_instance_of(Google::Apis::RequestOptions) + end + + it 'has the correct GitLab version in User-Agent header' do + stub_const('Gitlab::VERSION', '10.3.0-pre') + + expect(subject.header).to eq({ 'User-Agent': 'GitLab/10.3 (GPN:GitLab;)' }) + end + end end diff --git a/spec/mailers/notify_spec.rb b/spec/mailers/notify_spec.rb index f942a22b6d1..e1d71a9573b 100644 --- a/spec/mailers/notify_spec.rb +++ b/spec/mailers/notify_spec.rb @@ -602,7 +602,7 @@ describe Notify do it 'has the correct subject and body' do aggregate_failures do - is_expected.to have_subject("Re: #{project.name} | #{commit.title.strip} (#{commit.short_id})") + is_expected.to have_subject("Re: #{project.name} | #{commit.title} (#{commit.short_id})") is_expected.to have_body_text(commit.short_id) end end @@ -712,7 +712,7 @@ describe Notify do it_behaves_like 'a user cannot unsubscribe through footer link' it 'has the correct subject' do - is_expected.to have_subject "Re: #{project.name} | #{commit.title.strip} (#{commit.short_id})" + is_expected.to have_subject "Re: #{project.name} | #{commit.title} (#{commit.short_id})" end it 'contains a link to the commit' do diff --git a/spec/models/namespace_spec.rb b/spec/models/namespace_spec.rb index 90b768f595e..3817f20bfe7 100644 --- a/spec/models/namespace_spec.rb +++ b/spec/models/namespace_spec.rb @@ -531,7 +531,7 @@ describe Namespace do end end - describe '#has_forks_of?' do + describe '#find_fork_of?' do let(:project) { create(:project, :public) } let!(:forked_project) { fork_project(project, namespace.owner, namespace: namespace) } @@ -550,5 +550,13 @@ describe Namespace do expect(other_namespace.find_fork_of(project)).to eq(other_fork) end + + context 'with request store enabled', :request_store do + it 'only queries once' do + expect(project.fork_network).to receive(:find_forks_in).once.and_call_original + + 2.times { namespace.find_fork_of(project) } + end + end end end diff --git a/spec/models/project_spec.rb b/spec/models/project_spec.rb index 521b7bd70ba..000d5b7126d 100644 --- a/spec/models/project_spec.rb +++ b/spec/models/project_spec.rb @@ -315,7 +315,6 @@ describe Project do it { is_expected.to delegate_method(:empty_repo?).to(:repository) } it { is_expected.to delegate_method(:members).to(:team).with_prefix(true) } - it { is_expected.to delegate_method(:count).to(:forks).with_prefix(true) } it { is_expected.to delegate_method(:name).to(:owner).with_prefix(true).with_arguments(allow_nil: true) } end @@ -1717,8 +1716,7 @@ describe Project do expect(RepositoryForkWorker).to receive(:perform_async).with( project.id, forked_from_project.repository_storage_path, - forked_from_project.disk_path, - project.namespace.full_path).and_return(import_jid) + forked_from_project.disk_path).and_return(import_jid) expect(project.add_import_job).to eq(import_jid) end @@ -2473,7 +2471,7 @@ describe Project do it 'returns the number of forks' do project = build(:project) - allow(project.forks).to receive(:count).and_return(1) + expect_any_instance_of(Projects::ForksCountService).to receive(:count).and_return(1) expect(project.forks_count).to eq(1) end diff --git a/spec/models/repository_spec.rb b/spec/models/repository_spec.rb index 27f0a99b2fa..af0c86abe86 100644 --- a/spec/models/repository_spec.rb +++ b/spec/models/repository_spec.rb @@ -1408,42 +1408,52 @@ describe Repository do end describe '#cherry_pick' do - let(:conflict_commit) { repository.commit('c642fe9b8b9f28f9225d7ea953fe14e74748d53b') } - let(:pickable_commit) { repository.commit('7d3b0f7cff5f37573aea97cebfd5692ea1689924') } - let(:pickable_merge) { repository.commit('e56497bb5f03a90a51293fc6d516788730953899') } - let(:message) { 'cherry-pick message' } - - context 'when there is a conflict' do - it 'raises an error' do - expect { repository.cherry_pick(user, conflict_commit, 'master', message) }.to raise_error(Gitlab::Git::Repository::CreateTreeError) + shared_examples 'cherry-picking a commit' do + let(:conflict_commit) { repository.commit('c642fe9b8b9f28f9225d7ea953fe14e74748d53b') } + let(:pickable_commit) { repository.commit('7d3b0f7cff5f37573aea97cebfd5692ea1689924') } + let(:pickable_merge) { repository.commit('e56497bb5f03a90a51293fc6d516788730953899') } + let(:message) { 'cherry-pick message' } + + context 'when there is a conflict' do + it 'raises an error' do + expect { repository.cherry_pick(user, conflict_commit, 'master', message) }.to raise_error(Gitlab::Git::Repository::CreateTreeError) + end end - end - context 'when commit was already cherry-picked' do - it 'raises an error' do - repository.cherry_pick(user, pickable_commit, 'master', message) + context 'when commit was already cherry-picked' do + it 'raises an error' do + repository.cherry_pick(user, pickable_commit, 'master', message) - expect { repository.cherry_pick(user, pickable_commit, 'master', message) }.to raise_error(Gitlab::Git::Repository::CreateTreeError) + expect { repository.cherry_pick(user, pickable_commit, 'master', message) }.to raise_error(Gitlab::Git::Repository::CreateTreeError) + end end - end - context 'when commit can be cherry-picked' do - it 'cherry-picks the changes' do - expect(repository.cherry_pick(user, pickable_commit, 'master', message)).to be_truthy + context 'when commit can be cherry-picked' do + it 'cherry-picks the changes' do + expect(repository.cherry_pick(user, pickable_commit, 'master', message)).to be_truthy + end end - end - context 'cherry-picking a merge commit' do - it 'cherry-picks the changes' do - expect(repository.blob_at_branch('improve/awesome', 'foo/bar/.gitkeep')).to be_nil + context 'cherry-picking a merge commit' do + it 'cherry-picks the changes' do + expect(repository.blob_at_branch('improve/awesome', 'foo/bar/.gitkeep')).to be_nil - cherry_pick_commit_sha = repository.cherry_pick(user, pickable_merge, 'improve/awesome', message) - cherry_pick_commit_message = project.commit(cherry_pick_commit_sha).message + cherry_pick_commit_sha = repository.cherry_pick(user, pickable_merge, 'improve/awesome', message) + cherry_pick_commit_message = project.commit(cherry_pick_commit_sha).message - expect(repository.blob_at_branch('improve/awesome', 'foo/bar/.gitkeep')).not_to be_nil - expect(cherry_pick_commit_message).to eq(message) + expect(repository.blob_at_branch('improve/awesome', 'foo/bar/.gitkeep')).not_to be_nil + expect(cherry_pick_commit_message).to eq(message) + end end end + + context 'when Gitaly cherry_pick feature is enabled' do + it_behaves_like 'cherry-picking a commit' + end + + context 'when Gitaly cherry_pick feature is disabled', :disable_gitaly do + it_behaves_like 'cherry-picking a commit' + end end describe '#before_delete' do diff --git a/spec/requests/api/groups_spec.rb b/spec/requests/api/groups_spec.rb index 04a658cd6c3..554723d6b1e 100644 --- a/spec/requests/api/groups_spec.rb +++ b/spec/requests/api/groups_spec.rb @@ -401,6 +401,20 @@ describe API::Groups do expect(response).to have_gitlab_http_status(404) end + + it 'avoids N+1 queries' do + get api("/groups/#{group1.id}/projects", admin) + + control_count = ActiveRecord::QueryRecorder.new do + get api("/groups/#{group1.id}/projects", admin) + end.count + + create(:project, namespace: group1) + + expect do + get api("/groups/#{group1.id}/projects", admin) + end.not_to exceed_query_limit(control_count) + end end context 'when using group path in URL' do diff --git a/spec/services/notification_service_spec.rb b/spec/services/notification_service_spec.rb index db5de572b6d..43e2643f709 100644 --- a/spec/services/notification_service_spec.rb +++ b/spec/services/notification_service_spec.rb @@ -12,6 +12,8 @@ describe NotificationService, :mailer do shared_examples 'notifications for new mentions' do def send_notifications(*new_mentions) + mentionable.description = new_mentions.map(&:to_reference).join(' ') + notification.send(notification_method, mentionable, new_mentions, @u_disabled) end @@ -20,13 +22,13 @@ describe NotificationService, :mailer do should_not_email_anyone end - it 'emails new mentions with a watch level higher than participant' do - send_notifications(@u_watcher, @u_participant_mentioned, @u_custom_global) - should_only_email(@u_watcher, @u_participant_mentioned, @u_custom_global) + it 'emails new mentions with a watch level higher than mention' do + send_notifications(@u_watcher, @u_participant_mentioned, @u_custom_global, @u_mentioned) + should_only_email(@u_watcher, @u_participant_mentioned, @u_custom_global, @u_mentioned) end - it 'does not email new mentions with a watch level equal to or less than participant' do - send_notifications(@u_participating, @u_mentioned) + it 'does not email new mentions with a watch level equal to or less than mention' do + send_notifications(@u_disabled) should_not_email_anyone end end @@ -509,6 +511,14 @@ describe NotificationService, :mailer do should_not_email(issue.assignees.first) end + it "emails any mentioned users with the mention level" do + issue.description = @u_mentioned.to_reference + + notification.new_issue(issue, @u_disabled) + + should_email(@u_mentioned) + end + it "emails the author if they've opted into notifications about their activity" do issue.author.notified_of_own_activity = true @@ -900,6 +910,14 @@ describe NotificationService, :mailer do should_not_email(@u_lazy_participant) end + it "emails any mentioned users with the mention level" do + merge_request.description = @u_mentioned.to_reference + + notification.new_merge_request(merge_request, @u_disabled) + + should_email(@u_mentioned) + end + it "emails the author if they've opted into notifications about their activity" do merge_request.author.notified_of_own_activity = true diff --git a/spec/services/projects/count_service_spec.rb b/spec/services/projects/count_service_spec.rb index cc496501bad..183f6128c7b 100644 --- a/spec/services/projects/count_service_spec.rb +++ b/spec/services/projects/count_service_spec.rb @@ -4,9 +4,17 @@ describe Projects::CountService do let(:project) { build(:project, id: 1) } let(:service) { described_class.new(project) } - describe '#relation_for_count' do + describe '.query' do it 'raises NotImplementedError' do - expect { service.relation_for_count }.to raise_error(NotImplementedError) + expect { described_class.query(project.id) }.to raise_error(NotImplementedError) + end + end + + describe '#relation_for_count' do + it 'calls the class method query with the project id' do + expect(described_class).to receive(:query).with(project.id) + + service.relation_for_count end end diff --git a/spec/support/query_recorder.rb b/spec/support/query_recorder.rb index 369775db462..8cf8f45a8b2 100644 --- a/spec/support/query_recorder.rb +++ b/spec/support/query_recorder.rb @@ -41,7 +41,8 @@ RSpec::Matchers.define :exceed_query_limit do |expected| supports_block_expectations match do |block| - query_count(&block) > expected_count + threshold + @subject_block = block + actual_count > expected_count + threshold end failure_message_when_negated do |actual| @@ -55,6 +56,11 @@ RSpec::Matchers.define :exceed_query_limit do |expected| self end + def for_query(query) + @query = query + self + end + def threshold @threshold.to_i end @@ -68,12 +74,15 @@ RSpec::Matchers.define :exceed_query_limit do |expected| end def actual_count - @recorder.count + @actual_count ||= if @query + recorder.log.select { |recorded| recorded =~ @query }.size + else + recorder.count + end end - def query_count(&block) - @recorder = ActiveRecord::QueryRecorder.new(&block) - @recorder.count + def recorder + @recorder ||= ActiveRecord::QueryRecorder.new(&@subject_block) end def count_queries(queries) diff --git a/spec/workers/repository_fork_worker_spec.rb b/spec/workers/repository_fork_worker_spec.rb index e881ec37ae5..74c85848b7e 100644 --- a/spec/workers/repository_fork_worker_spec.rb +++ b/spec/workers/repository_fork_worker_spec.rb @@ -1,8 +1,8 @@ require 'spec_helper' describe RepositoryForkWorker do - let(:project) { create(:project, :repository, :import_scheduled) } - let(:fork_project) { create(:project, :repository, forked_from_project: project) } + let(:project) { create(:project, :repository) } + let(:fork_project) { create(:project, :repository, :import_scheduled, forked_from_project: project) } let(:shell) { Gitlab::Shell.new } subject { described_class.new } @@ -12,50 +12,39 @@ describe RepositoryForkWorker do end describe "#perform" do + def perform! + subject.perform(fork_project.id, '/test/path', project.disk_path) + end + + def expect_fork_repository + expect(shell).to receive(:fork_repository).with( + '/test/path', + project.disk_path, + fork_project.repository_storage_path, + fork_project.disk_path + ) + end + describe 'when a worker was reset without cleanup' do let(:jid) { '12345678' } - let(:started_project) { create(:project, :repository, :import_started) } it 'creates a new repository from a fork' do allow(subject).to receive(:jid).and_return(jid) - expect(shell).to receive(:fork_repository).with( - '/test/path', - project.full_path, - project.repository_storage_path, - fork_project.namespace.full_path - ).and_return(true) - - subject.perform( - project.id, - '/test/path', - project.full_path, - fork_project.namespace.full_path) + expect_fork_repository.and_return(true) + + perform! end end it "creates a new repository from a fork" do - expect(shell).to receive(:fork_repository).with( - '/test/path', - project.full_path, - project.repository_storage_path, - fork_project.namespace.full_path - ).and_return(true) + expect_fork_repository.and_return(true) - subject.perform( - project.id, - '/test/path', - project.full_path, - fork_project.namespace.full_path) + perform! end it 'flushes various caches' do - expect(shell).to receive(:fork_repository).with( - '/test/path', - project.full_path, - project.repository_storage_path, - fork_project.namespace.full_path - ).and_return(true) + expect_fork_repository.and_return(true) expect_any_instance_of(Repository).to receive(:expire_emptiness_caches) .and_call_original @@ -63,32 +52,22 @@ describe RepositoryForkWorker do expect_any_instance_of(Repository).to receive(:expire_exists_cache) .and_call_original - subject.perform(project.id, '/test/path', project.full_path, - fork_project.namespace.full_path) + perform! end it "handles bad fork" do - source_path = project.full_path - target_path = fork_project.namespace.full_path - error_message = "Unable to fork project #{project.id} for repository #{source_path} -> #{target_path}" + error_message = "Unable to fork project #{fork_project.id} for repository #{project.full_path} -> #{fork_project.full_path}" - expect(shell).to receive(:fork_repository).and_return(false) + expect_fork_repository.and_return(false) - expect do - subject.perform(project.id, '/test/path', source_path, target_path) - end.to raise_error(RepositoryForkWorker::ForkError, error_message) + expect { perform! }.to raise_error(RepositoryForkWorker::ForkError, error_message) end it 'handles unexpected error' do - source_path = project.full_path - target_path = fork_project.namespace.full_path - - allow_any_instance_of(Gitlab::Shell).to receive(:fork_repository).and_raise(RuntimeError) + expect_fork_repository.and_raise(RuntimeError) - expect do - subject.perform(project.id, '/test/path', source_path, target_path) - end.to raise_error(RepositoryForkWorker::ForkError) - expect(project.reload.import_status).to eq('failed') + expect { perform! }.to raise_error(RepositoryForkWorker::ForkError) + expect(fork_project.reload.import_status).to eq('failed') end end end |