summaryrefslogtreecommitdiff
path: root/spec
diff options
context:
space:
mode:
authorFilipa Lacerda <filipa@gitlab.com>2018-04-06 09:55:35 +0000
committerFilipa Lacerda <filipa@gitlab.com>2018-04-06 09:55:35 +0000
commit18e97a335c9f248d8e6ae61312d2a0ab15124972 (patch)
tree1fe39ff16a07660ca5f214c5f08e028f6a798060 /spec
parent8ce1526652ec1feb818ef9a5558826987645fa19 (diff)
parenta63c76faa09b5c4b7d10a9beebd46a117b10648a (diff)
downloadgitlab-ce-18e97a335c9f248d8e6ae61312d2a0ab15124972.tar.gz
Merge branch '10-7-stable-prepare-rc2' into '10-7-stable'
Prepare 10.7 RC2 release See merge request gitlab-org/gitlab-ce!18214
Diffstat (limited to 'spec')
-rw-r--r--spec/controllers/projects/discussions_controller_spec.rb47
-rw-r--r--spec/controllers/projects/jobs_controller_spec.rb19
-rw-r--r--spec/controllers/projects/pipelines_settings_controller_spec.rb79
-rw-r--r--spec/controllers/projects/settings/ci_cd_controller_spec.rb108
-rw-r--r--spec/factories/ci/builds.rb6
-rw-r--r--spec/features/projects/badges/list_spec.rb2
-rw-r--r--spec/features/projects/jobs/user_browses_job_spec.rb22
-rw-r--r--spec/features/projects/jobs/user_browses_jobs_spec.rb11
-rw-r--r--spec/features/projects/pipelines/pipeline_spec.rb16
-rw-r--r--spec/features/projects/pipelines/pipelines_spec.rb17
-rw-r--r--spec/initializers/artifacts_direct_upload_support_spec.rb71
-rw-r--r--spec/javascripts/ide/components/ide_file_buttons_spec.js (renamed from spec/javascripts/ide/components/repo_file_buttons_spec.js)26
-rw-r--r--spec/javascripts/ide/components/repo_editor_spec.js128
-rw-r--r--spec/javascripts/ide/lib/editor_spec.js55
-rw-r--r--spec/javascripts/ide/stores/mutations/file_spec.js11
-rw-r--r--spec/javascripts/pipelines/graph/job_component_spec.js2
-rw-r--r--spec/javascripts/vue_shared/components/content_viewer/content_viewer_spec.js41
-rw-r--r--spec/lib/banzai/cross_project_reference_spec.rb10
-rw-r--r--spec/lib/banzai/filter/label_reference_filter_spec.rb22
-rw-r--r--spec/lib/banzai/reference_parser/issue_parser_spec.rb23
-rw-r--r--spec/lib/banzai/reference_parser/merge_request_parser_spec.rb30
-rw-r--r--spec/lib/gitlab/ci/status/build/action_spec.rb10
-rw-r--r--spec/lib/gitlab/ci/status/build/cancelable_spec.rb18
-rw-r--r--spec/lib/gitlab/ci/status/build/factory_spec.rb8
-rw-r--r--spec/lib/gitlab/ci/status/build/failed_allowed_spec.rb23
-rw-r--r--spec/lib/gitlab/ci/status/build/failed_spec.rb83
-rw-r--r--spec/lib/gitlab/ci/status/build/play_spec.rb16
-rw-r--r--spec/lib/gitlab/ci/status/build/retried_spec.rb96
-rw-r--r--spec/lib/gitlab/ci/status/build/retryable_spec.rb18
-rw-r--r--spec/lib/gitlab/ci/status/build/stop_spec.rb20
-rw-r--r--spec/lib/gitlab/ci/status/success_warning_spec.rb4
-rw-r--r--spec/lib/gitlab/git_access_spec.rb24
-rw-r--r--spec/lib/gitlab/import_export/project.json61
-rw-r--r--spec/lib/gitlab/import_export/project_tree_restorer_spec.rb22
-rw-r--r--spec/lib/gitlab/import_export/project_tree_saver_spec.rb5
-rw-r--r--spec/lib/uploaded_file_spec.rb116
-rw-r--r--spec/migrations/rename_users_with_renamed_namespace_spec.rb4
-rw-r--r--spec/migrations/reschedule_builds_stages_migration_spec.rb (renamed from spec/migrations/schedule_build_stage_migration_spec.rb)8
-rw-r--r--spec/models/concerns/chronic_duration_attribute_spec.rb34
-rw-r--r--spec/models/project_spec.rb1
-rw-r--r--spec/models/user_spec.rb2
-rw-r--r--spec/policies/project_policy_spec.rb12
-rw-r--r--spec/presenters/ci/build_presenter_spec.rb93
-rw-r--r--spec/requests/api/project_import_spec.rb45
-rw-r--r--spec/requests/api/runner_spec.rb117
-rw-r--r--spec/requests/lfs_http_spec.rb19
-rw-r--r--spec/serializers/build_serializer_spec.rb22
-rw-r--r--spec/serializers/job_entity_spec.rb26
-rw-r--r--spec/serializers/pipeline_entity_spec.rb2
-rw-r--r--spec/serializers/stage_entity_spec.rb2
-rw-r--r--spec/serializers/status_entity_spec.rb2
-rw-r--r--spec/services/ci/register_job_service_spec.rb83
-rw-r--r--spec/services/projects/gitlab_projects_import_service_spec.rb16
-rw-r--r--spec/support/reference_parser_helpers.rb30
-rw-r--r--spec/support/stub_configuration.rb4
-rw-r--r--spec/uploaders/object_storage_spec.rb193
-rw-r--r--spec/views/projects/jobs/show.html.haml_spec.rb3
-rw-r--r--spec/views/projects/settings/ci_cd/_form.html.haml_spec.rb (renamed from spec/views/projects/pipelines_settings/_show.html.haml_spec.rb)2
58 files changed, 1601 insertions, 389 deletions
diff --git a/spec/controllers/projects/discussions_controller_spec.rb b/spec/controllers/projects/discussions_controller_spec.rb
index fcb0c2f28c8..53647749a60 100644
--- a/spec/controllers/projects/discussions_controller_spec.rb
+++ b/spec/controllers/projects/discussions_controller_spec.rb
@@ -16,6 +16,53 @@ describe Projects::DiscussionsController do
}
end
+ describe 'GET show' do
+ before do
+ sign_in user
+ end
+
+ context 'when user is not authorized to read the MR' do
+ it 'returns 404' do
+ get :show, request_params, format: :json
+
+ expect(response).to have_gitlab_http_status(404)
+ end
+ end
+
+ context 'when user is authorized to read the MR' do
+ before do
+ project.add_reporter(user)
+ end
+
+ it 'returns status 200' do
+ get :show, request_params, format: :json
+
+ expect(response).to have_gitlab_http_status(200)
+ end
+
+ it 'returns status 404 if MR does not exists' do
+ merge_request.destroy!
+
+ get :show, request_params, format: :json
+
+ expect(response).to have_gitlab_http_status(404)
+ end
+ end
+
+ context 'when user is authorized but note is LegacyDiffNote' do
+ before do
+ project.add_developer(user)
+ note.update!(type: 'LegacyDiffNote')
+ end
+
+ it 'returns status 200' do
+ get :show, request_params, format: :json
+
+ expect(response).to have_gitlab_http_status(200)
+ end
+ end
+ end
+
describe 'POST resolve' do
before do
sign_in user
diff --git a/spec/controllers/projects/jobs_controller_spec.rb b/spec/controllers/projects/jobs_controller_spec.rb
index 31046c202e6..b9a979044fe 100644
--- a/spec/controllers/projects/jobs_controller_spec.rb
+++ b/spec/controllers/projects/jobs_controller_spec.rb
@@ -513,13 +513,30 @@ describe Projects::JobsController do
end
end
+ context 'when job has a trace in database' do
+ let(:job) { create(:ci_build, pipeline: pipeline) }
+
+ before do
+ job.update_column(:trace, 'Sample trace')
+ end
+
+ it 'send a trace file' do
+ response = subject
+
+ expect(response).to have_gitlab_http_status(:ok)
+ expect(response.content_type).to eq 'text/plain; charset=utf-8'
+ expect(response.body).to eq 'Sample trace'
+ end
+ end
+
context 'when job does not have a trace file' do
let(:job) { create(:ci_build, pipeline: pipeline) }
it 'returns not_found' do
response = subject
- expect(response).to have_gitlab_http_status(:not_found)
+ expect(response).to have_gitlab_http_status(:ok)
+ expect(response.body).to eq ''
end
end
diff --git a/spec/controllers/projects/pipelines_settings_controller_spec.rb b/spec/controllers/projects/pipelines_settings_controller_spec.rb
index 913b9bd804a..694896b6bcf 100644
--- a/spec/controllers/projects/pipelines_settings_controller_spec.rb
+++ b/spec/controllers/projects/pipelines_settings_controller_spec.rb
@@ -11,82 +11,11 @@ describe Projects::PipelinesSettingsController do
sign_in(user)
end
- describe 'PATCH update' do
- subject do
- patch :update,
- namespace_id: project.namespace.to_param,
- project_id: project,
- project: {
- auto_devops_attributes: params
- }
- end
-
- context 'when updating the auto_devops settings' do
- let(:params) { { enabled: '', domain: 'mepmep.md' } }
-
- it 'redirects to the settings page' do
- subject
-
- expect(response).to have_gitlab_http_status(302)
- expect(flash[:notice]).to eq("Pipelines settings for '#{project.name}' were successfully updated.")
- end
-
- context 'following the instance default' do
- let(:params) { { enabled: '' } }
-
- it 'allows enabled to be set to nil' do
- subject
- project_auto_devops.reload
-
- expect(project_auto_devops.enabled).to be_nil
- end
- end
-
- context 'when run_auto_devops_pipeline is true' do
- before do
- expect_any_instance_of(Projects::UpdateService).to receive(:run_auto_devops_pipeline?).and_return(true)
- end
-
- context 'when the project repository is empty' do
- it 'sets a warning flash' do
- expect(subject).to set_flash[:warning]
- end
-
- it 'does not queue a CreatePipelineWorker' do
- expect(CreatePipelineWorker).not_to receive(:perform_async).with(project.id, user.id, project.default_branch, :web, any_args)
-
- subject
- end
- end
-
- context 'when the project repository is not empty' do
- let(:project) { create(:project, :repository) }
-
- it 'sets a success flash' do
- allow(CreatePipelineWorker).to receive(:perform_async).with(project.id, user.id, project.default_branch, :web, any_args)
-
- expect(subject).to set_flash[:success]
- end
-
- it 'queues a CreatePipelineWorker' do
- expect(CreatePipelineWorker).to receive(:perform_async).with(project.id, user.id, project.default_branch, :web, any_args)
-
- subject
- end
- end
- end
-
- context 'when run_auto_devops_pipeline is not true' do
- before do
- expect_any_instance_of(Projects::UpdateService).to receive(:run_auto_devops_pipeline?).and_return(false)
- end
-
- it 'does not queue a CreatePipelineWorker' do
- expect(CreatePipelineWorker).not_to receive(:perform_async).with(project.id, user.id, :web, any_args)
+ describe 'GET show' do
+ it 'redirects with 302 status code' do
+ get :show, namespace_id: project.namespace, project_id: project
- subject
- end
- end
+ expect(response).to have_gitlab_http_status(302)
end
end
end
diff --git a/spec/controllers/projects/settings/ci_cd_controller_spec.rb b/spec/controllers/projects/settings/ci_cd_controller_spec.rb
index 293e76798ae..7dae9b85d78 100644
--- a/spec/controllers/projects/settings/ci_cd_controller_spec.rb
+++ b/spec/controllers/projects/settings/ci_cd_controller_spec.rb
@@ -1,8 +1,9 @@
require('spec_helper')
describe Projects::Settings::CiCdController do
- let(:project) { create(:project, :public, :access_requestable) }
- let(:user) { create(:user) }
+ set(:user) { create(:user) }
+ set(:project_auto_devops) { create(:project_auto_devops) }
+ let(:project) { project_auto_devops.project }
before do
project.add_master(user)
@@ -55,4 +56,107 @@ describe Projects::Settings::CiCdController do
end
end
end
+
+ describe 'PATCH update' do
+ let(:params) { { ci_config_path: '' } }
+
+ subject do
+ patch :update,
+ namespace_id: project.namespace.to_param,
+ project_id: project,
+ project: params
+ end
+
+ it 'redirects to the settings page' do
+ subject
+
+ expect(response).to have_gitlab_http_status(302)
+ expect(flash[:notice]).to eq("Pipelines settings for '#{project.name}' were successfully updated.")
+ end
+
+ context 'when updating the auto_devops settings' do
+ let(:params) { { auto_devops_attributes: { enabled: '', domain: 'mepmep.md' } } }
+
+ context 'following the instance default' do
+ let(:params) { { auto_devops_attributes: { enabled: '' } } }
+
+ it 'allows enabled to be set to nil' do
+ subject
+ project_auto_devops.reload
+
+ expect(project_auto_devops.enabled).to be_nil
+ end
+ end
+
+ context 'when run_auto_devops_pipeline is true' do
+ before do
+ expect_any_instance_of(Projects::UpdateService).to receive(:run_auto_devops_pipeline?).and_return(true)
+ end
+
+ context 'when the project repository is empty' do
+ it 'sets a warning flash' do
+ expect(subject).to set_flash[:warning]
+ end
+
+ it 'does not queue a CreatePipelineWorker' do
+ expect(CreatePipelineWorker).not_to receive(:perform_async).with(project.id, user.id, project.default_branch, :web, any_args)
+
+ subject
+ end
+ end
+
+ context 'when the project repository is not empty' do
+ let(:project) { create(:project, :repository) }
+
+ it 'sets a success flash' do
+ allow(CreatePipelineWorker).to receive(:perform_async).with(project.id, user.id, project.default_branch, :web, any_args)
+
+ expect(subject).to set_flash[:success]
+ end
+
+ it 'queues a CreatePipelineWorker' do
+ expect(CreatePipelineWorker).to receive(:perform_async).with(project.id, user.id, project.default_branch, :web, any_args)
+
+ subject
+ end
+ end
+ end
+
+ context 'when run_auto_devops_pipeline is not true' do
+ before do
+ expect_any_instance_of(Projects::UpdateService).to receive(:run_auto_devops_pipeline?).and_return(false)
+ end
+
+ it 'does not queue a CreatePipelineWorker' do
+ expect(CreatePipelineWorker).not_to receive(:perform_async).with(project.id, user.id, :web, any_args)
+
+ subject
+ end
+ end
+ end
+
+ context 'when updating general settings' do
+ context 'when build_timeout_human_readable is not specified' do
+ let(:params) { { build_timeout_human_readable: '' } }
+
+ it 'set default timeout' do
+ subject
+
+ project.reload
+ expect(project.build_timeout).to eq(3600)
+ end
+ end
+
+ context 'when build_timeout_human_readable is specified' do
+ let(:params) { { build_timeout_human_readable: '1h 30m' } }
+
+ it 'set specified timeout' do
+ subject
+
+ project.reload
+ expect(project.build_timeout).to eq(5400)
+ end
+ end
+ end
+ end
end
diff --git a/spec/factories/ci/builds.rb b/spec/factories/ci/builds.rb
index f6ba3a581ca..fdacbe6c3f1 100644
--- a/spec/factories/ci/builds.rb
+++ b/spec/factories/ci/builds.rb
@@ -62,6 +62,7 @@ FactoryBot.define do
end
trait :pending do
+ queued_at 'Di 29. Okt 09:50:59 CET 2013'
status 'pending'
end
@@ -237,5 +238,10 @@ FactoryBot.define do
trait :protected do
protected true
end
+
+ trait :script_failure do
+ failed
+ failure_reason 1
+ end
end
end
diff --git a/spec/features/projects/badges/list_spec.rb b/spec/features/projects/badges/list_spec.rb
index c705e479690..0abef4bc447 100644
--- a/spec/features/projects/badges/list_spec.rb
+++ b/spec/features/projects/badges/list_spec.rb
@@ -6,7 +6,7 @@ feature 'list of badges' do
project = create(:project, :repository)
project.add_master(user)
sign_in(user)
- visit project_pipelines_settings_path(project)
+ visit project_settings_ci_cd_path(project)
end
scenario 'user wants to see build status badge' do
diff --git a/spec/features/projects/jobs/user_browses_job_spec.rb b/spec/features/projects/jobs/user_browses_job_spec.rb
index 4c49cff30d4..b7eee39052a 100644
--- a/spec/features/projects/jobs/user_browses_job_spec.rb
+++ b/spec/features/projects/jobs/user_browses_job_spec.rb
@@ -34,4 +34,26 @@ describe 'User browses a job', :js do
expect(build.project.running_or_pending_build_count).to eq(build.project.builds.running_or_pending.count(:all))
end
+
+ context 'with a failed job' do
+ let!(:build) { create(:ci_build, :failed, pipeline: pipeline) }
+
+ it 'displays the failure reason' do
+ within('.builds-container') do
+ build_link = first('.build-job > a')
+ expect(build_link['data-title']).to eq('test - failed <br> (unknown failure)')
+ end
+ end
+ end
+
+ context 'when a failed job has been retried' do
+ let!(:build) { create(:ci_build, :failed, :retried, pipeline: pipeline) }
+
+ it 'displays the failure reason and retried label' do
+ within('.builds-container') do
+ build_link = first('.build-job > a')
+ expect(build_link['data-title']).to eq('test - failed <br> (unknown failure) (retried)')
+ end
+ end
+ end
end
diff --git a/spec/features/projects/jobs/user_browses_jobs_spec.rb b/spec/features/projects/jobs/user_browses_jobs_spec.rb
index 767777f3bf9..36ebbeadd4a 100644
--- a/spec/features/projects/jobs/user_browses_jobs_spec.rb
+++ b/spec/features/projects/jobs/user_browses_jobs_spec.rb
@@ -29,4 +29,15 @@ describe 'User browses jobs' do
expect(ci_lint_tool_link[:href]).to end_with(ci_lint_path)
end
end
+
+ context 'with a failed job' do
+ let!(:build) { create(:ci_build, :coverage, :failed, pipeline: pipeline) }
+
+ it 'displays a tooltip with the failure reason' do
+ page.within('.ci-table') do
+ failed_job_link = page.find('.ci-failed')
+ expect(failed_job_link[:title]).to eq('Failed <br> (unknown failure)')
+ end
+ end
+ end
end
diff --git a/spec/features/projects/pipelines/pipeline_spec.rb b/spec/features/projects/pipelines/pipeline_spec.rb
index 266ef693d0b..990e5c4d9df 100644
--- a/spec/features/projects/pipelines/pipeline_spec.rb
+++ b/spec/features/projects/pipelines/pipeline_spec.rb
@@ -115,6 +115,13 @@ describe 'Pipeline', :js do
expect(page).not_to have_content('Retry job')
end
+
+ it 'should include the failure reason' do
+ page.within('#ci-badge-test') do
+ build_link = page.find('.js-pipeline-graph-job-link')
+ expect(build_link['data-original-title']).to eq('test - failed <br> (unknown failure)')
+ end
+ end
end
context 'when pipeline has manual jobs' do
@@ -289,6 +296,15 @@ describe 'Pipeline', :js do
it { expect(build_manual.reload).to be_pending }
end
+
+ context 'failed jobs' do
+ it 'displays a tooltip with the failure reason' do
+ page.within('.ci-table') do
+ failed_job_link = page.find('.ci-failed')
+ expect(failed_job_link[:title]).to eq('Failed <br> (unknown failure)')
+ end
+ end
+ end
end
describe 'GET /:project/pipelines/:id/failures' do
diff --git a/spec/features/projects/pipelines/pipelines_spec.rb b/spec/features/projects/pipelines/pipelines_spec.rb
index 0e81c6c629a..6e63e0f0b49 100644
--- a/spec/features/projects/pipelines/pipelines_spec.rb
+++ b/spec/features/projects/pipelines/pipelines_spec.rb
@@ -394,6 +394,23 @@ describe 'Pipelines', :js do
expect(build.reload).to be_canceled
end
end
+
+ context 'for a failed pipeline' do
+ let!(:build) do
+ create(:ci_build, :failed, pipeline: pipeline,
+ stage: 'build',
+ name: 'build')
+ end
+
+ it 'should display the failure reason' do
+ find('.js-builds-dropdown-button').click
+
+ within('.js-builds-dropdown-list') do
+ build_element = page.find('.mini-pipeline-graph-dropdown-item')
+ expect(build_element['data-title']).to eq('build - failed <br> (unknown failure)')
+ end
+ end
+ end
end
context 'with pagination' do
diff --git a/spec/initializers/artifacts_direct_upload_support_spec.rb b/spec/initializers/artifacts_direct_upload_support_spec.rb
new file mode 100644
index 00000000000..bfb71da3388
--- /dev/null
+++ b/spec/initializers/artifacts_direct_upload_support_spec.rb
@@ -0,0 +1,71 @@
+require 'spec_helper'
+
+describe 'Artifacts direct upload support' do
+ subject do
+ load Rails.root.join('config/initializers/artifacts_direct_upload_support.rb')
+ end
+
+ let(:connection) do
+ { provider: provider }
+ end
+
+ before do
+ stub_artifacts_setting(
+ object_store: {
+ enabled: enabled,
+ direct_upload: direct_upload,
+ connection: connection
+ })
+ end
+
+ context 'when object storage is enabled' do
+ let(:enabled) { true }
+
+ context 'when direct upload is enabled' do
+ let(:direct_upload) { true }
+
+ context 'when provider is Google' do
+ let(:provider) { 'Google' }
+
+ it 'succeeds' do
+ expect { subject }.not_to raise_error
+ end
+ end
+
+ context 'when connection is empty' do
+ let(:connection) { nil }
+
+ it 'raises an error' do
+ expect { subject }.to raise_error /object storage provider when 'direct_upload' of artifacts is used/
+ end
+ end
+
+ context 'when other provider is used' do
+ let(:provider) { 'AWS' }
+
+ it 'raises an error' do
+ expect { subject }.to raise_error /object storage provider when 'direct_upload' of artifacts is used/
+ end
+ end
+ end
+
+ context 'when direct upload is disabled' do
+ let(:direct_upload) { false }
+ let(:provider) { 'AWS' }
+
+ it 'succeeds' do
+ expect { subject }.not_to raise_error
+ end
+ end
+ end
+
+ context 'when object storage is disabled' do
+ let(:enabled) { false }
+ let(:direct_upload) { false }
+ let(:provider) { 'AWS' }
+
+ it 'succeeds' do
+ expect { subject }.not_to raise_error
+ end
+ end
+end
diff --git a/spec/javascripts/ide/components/repo_file_buttons_spec.js b/spec/javascripts/ide/components/ide_file_buttons_spec.js
index c86bdb132b4..8ac8d1b2acf 100644
--- a/spec/javascripts/ide/components/repo_file_buttons_spec.js
+++ b/spec/javascripts/ide/components/ide_file_buttons_spec.js
@@ -1,5 +1,5 @@
import Vue from 'vue';
-import repoFileButtons from '~/ide/components/repo_file_buttons.vue';
+import repoFileButtons from '~/ide/components/ide_file_buttons.vue';
import createVueComponent from '../../helpers/vue_mount_component_helper';
import { file } from '../helpers';
@@ -23,7 +23,7 @@ describe('RepoFileButtons', () => {
vm.$destroy();
});
- it('renders Raw, Blame, History, Permalink and Preview toggle', done => {
+ it('renders Raw, Blame, History and Permalink', done => {
vm = createComponent();
vm.$nextTick(() => {
@@ -32,16 +32,30 @@ describe('RepoFileButtons', () => {
const history = vm.$el.querySelector('.history');
expect(raw.href).toMatch(`/${activeFile.rawPath}`);
- expect(raw.textContent.trim()).toEqual('Raw');
+ expect(raw.getAttribute('data-original-title')).toEqual('Raw');
expect(blame.href).toMatch(`/${activeFile.blamePath}`);
- expect(blame.textContent.trim()).toEqual('Blame');
+ expect(blame.getAttribute('data-original-title')).toEqual('Blame');
expect(history.href).toMatch(`/${activeFile.commitsPath}`);
- expect(history.textContent.trim()).toEqual('History');
- expect(vm.$el.querySelector('.permalink').textContent.trim()).toEqual(
+ expect(history.getAttribute('data-original-title')).toEqual('History');
+ expect(vm.$el.querySelector('.permalink').getAttribute('data-original-title')).toEqual(
'Permalink',
);
done();
});
});
+
+ it('renders Download', done => {
+ activeFile.binary = true;
+ vm = createComponent();
+
+ vm.$nextTick(() => {
+ const raw = vm.$el.querySelector('.raw');
+
+ expect(raw.href).toMatch(`/${activeFile.rawPath}`);
+ expect(raw.getAttribute('data-original-title')).toEqual('Download');
+
+ done();
+ });
+ });
});
diff --git a/spec/javascripts/ide/components/repo_editor_spec.js b/spec/javascripts/ide/components/repo_editor_spec.js
index 9d3fa1280f4..63a3d2c6cd5 100644
--- a/spec/javascripts/ide/components/repo_editor_spec.js
+++ b/spec/javascripts/ide/components/repo_editor_spec.js
@@ -19,7 +19,6 @@ describe('RepoEditor', () => {
f.active = true;
f.tempFile = true;
- f.html = 'testing';
vm.$store.state.openFiles.push(f);
vm.$store.state.entries[f.path] = f;
vm.monaco = true;
@@ -47,6 +46,61 @@ describe('RepoEditor', () => {
});
});
+ it('renders only an edit tab', done => {
+ Vue.nextTick(() => {
+ const tabs = vm.$el.querySelectorAll('.ide-mode-tabs .nav-links li');
+ expect(tabs.length).toBe(1);
+ expect(tabs[0].textContent.trim()).toBe('Edit');
+
+ done();
+ });
+ });
+
+ describe('when file is markdown', () => {
+ beforeEach(done => {
+ vm.file.previewMode = {
+ id: 'markdown',
+ previewTitle: 'Preview Markdown',
+ };
+
+ vm.$nextTick(done);
+ });
+
+ it('renders an Edit and a Preview Tab', done => {
+ Vue.nextTick(() => {
+ const tabs = vm.$el.querySelectorAll('.ide-mode-tabs .nav-links li');
+ expect(tabs.length).toBe(2);
+ expect(tabs[0].textContent.trim()).toBe('Edit');
+ expect(tabs[1].textContent.trim()).toBe('Preview Markdown');
+
+ done();
+ });
+ });
+ });
+
+ describe('when file is markdown and viewer mode is review', () => {
+ beforeEach(done => {
+ vm.file.previewMode = {
+ id: 'markdown',
+ previewTitle: 'Preview Markdown',
+ };
+ vm.$store.state.viewer = 'diff';
+
+ vm.$nextTick(done);
+ });
+
+ it('renders an Edit and a Preview Tab', done => {
+ Vue.nextTick(() => {
+ const tabs = vm.$el.querySelectorAll('.ide-mode-tabs .nav-links li');
+ expect(tabs.length).toBe(2);
+ expect(tabs[0].textContent.trim()).toBe('Review');
+ expect(tabs[1].textContent.trim()).toBe('Preview Markdown');
+
+ done();
+ });
+ });
+ });
+
describe('when open file is binary and not raw', () => {
beforeEach(done => {
vm.file.binary = true;
@@ -57,10 +111,6 @@ describe('RepoEditor', () => {
it('does not render the IDE', () => {
expect(vm.shouldHideEditor).toBeTruthy();
});
-
- it('shows activeFile html', () => {
- expect(vm.$el.textContent).toContain('testing');
- });
});
describe('createEditorInstance', () => {
@@ -149,47 +199,49 @@ describe('RepoEditor', () => {
});
});
- describe('setup editor for merge request viewing', () => {
- beforeEach(done => {
- // Resetting as the main test setup has already done it
- vm.$destroy();
- resetStore(vm.$store);
- Editor.editorInstance.modelManager.dispose();
-
- const f = {
- ...file(),
- active: true,
- tempFile: true,
- html: 'testing',
- mrChange: { diff: 'ABC' },
- baseRaw: 'testing',
- content: 'test',
- };
- const RepoEditor = Vue.extend(repoEditor);
- vm = createComponentWithStore(RepoEditor, store, {
- file: f,
- });
-
- vm.$store.state.openFiles.push(f);
- vm.$store.state.entries[f.path] = f;
-
- vm.$store.state.viewer = 'mrdiff';
+ describe('editor updateDimensions', () => {
+ beforeEach(() => {
+ spyOn(vm.editor, 'updateDimensions').and.callThrough();
+ spyOn(vm.editor, 'updateDiffView');
+ });
- vm.monaco = true;
+ it('calls updateDimensions when rightPanelCollapsed is changed', done => {
+ vm.$store.state.rightPanelCollapsed = true;
- vm.$mount();
+ vm.$nextTick(() => {
+ expect(vm.editor.updateDimensions).toHaveBeenCalled();
+ expect(vm.editor.updateDiffView).toHaveBeenCalled();
- monacoLoader(['vs/editor/editor.main'], () => {
- setTimeout(done, 0);
+ done();
});
});
- it('attaches merge request model to editor when merge request diff', () => {
- spyOn(vm.editor, 'attachMergeRequestModel').and.callThrough();
+ it('calls updateDimensions when panelResizing is false', done => {
+ vm.$store.state.panelResizing = true;
+
+ vm
+ .$nextTick()
+ .then(() => {
+ vm.$store.state.panelResizing = false;
+ })
+ .then(vm.$nextTick)
+ .then(() => {
+ expect(vm.editor.updateDimensions).toHaveBeenCalled();
+ expect(vm.editor.updateDiffView).toHaveBeenCalled();
+ })
+ .then(done)
+ .catch(done.fail);
+ });
- vm.setupEditor();
+ it('does not call updateDimensions when panelResizing is true', done => {
+ vm.$store.state.panelResizing = true;
+
+ vm.$nextTick(() => {
+ expect(vm.editor.updateDimensions).not.toHaveBeenCalled();
+ expect(vm.editor.updateDiffView).not.toHaveBeenCalled();
- expect(vm.editor.attachMergeRequestModel).toHaveBeenCalledWith(vm.model);
+ done();
+ });
});
});
});
diff --git a/spec/javascripts/ide/lib/editor_spec.js b/spec/javascripts/ide/lib/editor_spec.js
index ec56ebc0341..75e6f0f54ec 100644
--- a/spec/javascripts/ide/lib/editor_spec.js
+++ b/spec/javascripts/ide/lib/editor_spec.js
@@ -76,7 +76,8 @@ describe('Multi-file editor library', () => {
occurrencesHighlight: false,
renderLineHighlight: 'none',
hideCursorInOverviewRuler: true,
- wordWrap: 'bounded',
+ wordWrap: 'on',
+ renderSideBySide: true,
});
});
});
@@ -215,4 +216,56 @@ describe('Multi-file editor library', () => {
expect(instance.decorationsController.dispose).not.toHaveBeenCalled();
});
});
+
+ describe('updateDiffView', () => {
+ describe('edit mode', () => {
+ it('does not update options', () => {
+ instance.createInstance(holder);
+
+ spyOn(instance.instance, 'updateOptions');
+
+ instance.updateDiffView();
+
+ expect(instance.instance.updateOptions).not.toHaveBeenCalled();
+ });
+ });
+
+ describe('diff mode', () => {
+ beforeEach(() => {
+ instance.createDiffInstance(holder);
+
+ spyOn(instance.instance, 'updateOptions').and.callThrough();
+ });
+
+ it('sets renderSideBySide to false if el is less than 700 pixels', () => {
+ spyOnProperty(instance.instance.getDomNode(), 'offsetWidth').and.returnValue(600);
+
+ expect(instance.instance.updateOptions).not.toHaveBeenCalledWith({
+ renderSideBySide: false,
+ });
+ });
+
+ it('sets renderSideBySide to false if el is more than 700 pixels', () => {
+ spyOnProperty(instance.instance.getDomNode(), 'offsetWidth').and.returnValue(800);
+
+ expect(instance.instance.updateOptions).not.toHaveBeenCalledWith({
+ renderSideBySide: true,
+ });
+ });
+ });
+ });
+
+ describe('isDiffEditorType', () => {
+ it('returns true when diff editor', () => {
+ instance.createDiffInstance(holder);
+
+ expect(instance.isDiffEditorType).toBe(true);
+ });
+
+ it('returns false when not diff editor', () => {
+ instance.createInstance(holder);
+
+ expect(instance.isDiffEditorType).toBe(false);
+ });
+ });
});
diff --git a/spec/javascripts/ide/stores/mutations/file_spec.js b/spec/javascripts/ide/stores/mutations/file_spec.js
index 88285ee409f..bf9d5166d0a 100644
--- a/spec/javascripts/ide/stores/mutations/file_spec.js
+++ b/spec/javascripts/ide/stores/mutations/file_spec.js
@@ -194,6 +194,17 @@ describe('IDE store file mutations', () => {
});
});
+ describe('SET_FILE_VIEWMODE', () => {
+ it('updates file view mode', () => {
+ mutations.SET_FILE_VIEWMODE(localState, {
+ file: localFile,
+ viewMode: 'preview',
+ });
+
+ expect(localFile.viewMode).toBe('preview');
+ });
+ });
+
describe('ADD_PENDING_TAB', () => {
beforeEach(() => {
const f = {
diff --git a/spec/javascripts/pipelines/graph/job_component_spec.js b/spec/javascripts/pipelines/graph/job_component_spec.js
index ce181a1e515..c9677ae209a 100644
--- a/spec/javascripts/pipelines/graph/job_component_spec.js
+++ b/spec/javascripts/pipelines/graph/job_component_spec.js
@@ -13,6 +13,7 @@ describe('pipeline graph job component', () => {
icon: 'icon_status_success',
text: 'passed',
label: 'passed',
+ tooltip: 'passed',
group: 'success',
details_path: '/root/ci-mock/builds/4256',
has_details: true,
@@ -137,6 +138,7 @@ describe('pipeline graph job component', () => {
status: {
icon: 'icon_status_success',
label: 'success',
+ tooltip: 'success',
},
},
});
diff --git a/spec/javascripts/vue_shared/components/content_viewer/content_viewer_spec.js b/spec/javascripts/vue_shared/components/content_viewer/content_viewer_spec.js
new file mode 100644
index 00000000000..c7c454a0b45
--- /dev/null
+++ b/spec/javascripts/vue_shared/components/content_viewer/content_viewer_spec.js
@@ -0,0 +1,41 @@
+import Vue from 'vue';
+import MockAdapter from 'axios-mock-adapter';
+import axios from '~/lib/utils/axios_utils';
+import contentViewer from '~/vue_shared/components/content_viewer/content_viewer.vue';
+import mountComponent from 'spec/helpers/vue_mount_component_helper';
+
+describe('ContentViewer', () => {
+ let vm;
+ let mock;
+
+ function createComponent(props) {
+ const ContentViewer = Vue.extend(contentViewer);
+ vm = mountComponent(ContentViewer, props);
+ }
+
+ afterEach(() => {
+ vm.$destroy();
+ if (mock) mock.restore();
+ });
+
+ it('markdown preview renders + loads rendered markdown from server', done => {
+ mock = new MockAdapter(axios);
+ mock.onPost(`${gon.relative_url_root}/testproject/preview_markdown`).reply(200, {
+ body: '<b>testing</b>',
+ });
+
+ createComponent({
+ path: 'test.md',
+ content: '* Test',
+ projectPath: 'testproject',
+ });
+
+ const previewContainer = vm.$el.querySelector('.md-previewer');
+
+ setTimeout(() => {
+ expect(previewContainer.textContent).toContain('testing');
+
+ done();
+ });
+ });
+});
diff --git a/spec/lib/banzai/cross_project_reference_spec.rb b/spec/lib/banzai/cross_project_reference_spec.rb
index 68ca960caab..aadfe7637dd 100644
--- a/spec/lib/banzai/cross_project_reference_spec.rb
+++ b/spec/lib/banzai/cross_project_reference_spec.rb
@@ -14,6 +14,16 @@ describe Banzai::CrossProjectReference do
end
end
+ context 'when no project was referenced in group context' do
+ it 'returns the group from context' do
+ group = double
+
+ allow(self).to receive(:context).and_return({ group: group })
+
+ expect(parent_from_ref(nil)).to eq group
+ end
+ end
+
context 'when referenced project does not exist' do
it 'returns nil' do
expect(parent_from_ref('invalid/reference')).to be_nil
diff --git a/spec/lib/banzai/filter/label_reference_filter_spec.rb b/spec/lib/banzai/filter/label_reference_filter_spec.rb
index 0c524a1551f..392905076dc 100644
--- a/spec/lib/banzai/filter/label_reference_filter_spec.rb
+++ b/spec/lib/banzai/filter/label_reference_filter_spec.rb
@@ -596,6 +596,27 @@ describe Banzai::Filter::LabelReferenceFilter do
end
describe 'group context' do
+ it 'points to the page defined in label_url_method' do
+ group = create(:group)
+ label = create(:group_label, group: group)
+ reference = "~#{label.name}"
+
+ result = reference_filter("See #{reference}", { project: nil, group: group, label_url_method: :group_url } )
+
+ expect(result.css('a').first.attr('href')).to eq(urls.group_url(group, label_name: label.name))
+ end
+
+ it 'finds labels also in ancestor groups' do
+ group = create(:group)
+ label = create(:group_label, group: group)
+ subgroup = create(:group, parent: group)
+ reference = "~#{label.name}"
+
+ result = reference_filter("See #{reference}", { project: nil, group: subgroup, label_url_method: :group_url } )
+
+ expect(result.css('a').first.attr('href')).to eq(urls.group_url(subgroup, label_name: label.name))
+ end
+
it 'points to referenced project issues page' do
project = create(:project)
label = create(:label, project: project)
@@ -604,6 +625,7 @@ describe Banzai::Filter::LabelReferenceFilter do
result = reference_filter("See #{reference}", { project: nil, group: create(:group) } )
expect(result.css('a').first.attr('href')).to eq(urls.project_issues_url(project, label_name: label.name))
+ expect(result.css('a').first.text).to eq "#{label.name} in #{project.full_name}"
end
end
end
diff --git a/spec/lib/banzai/reference_parser/issue_parser_spec.rb b/spec/lib/banzai/reference_parser/issue_parser_spec.rb
index 0a63567ee40..cb7f8b20dda 100644
--- a/spec/lib/banzai/reference_parser/issue_parser_spec.rb
+++ b/spec/lib/banzai/reference_parser/issue_parser_spec.rb
@@ -117,4 +117,27 @@ describe Banzai::ReferenceParser::IssueParser do
expect(subject.records_for_nodes(nodes)).to eq({ link => issue })
end
end
+
+ context 'when checking multiple merge requests on another project' do
+ let(:other_project) { create(:project, :public) }
+ let(:other_issue) { create(:issue, project: other_project) }
+
+ let(:control_links) do
+ [issue_link(other_issue)]
+ end
+
+ let(:actual_links) do
+ control_links + [issue_link(create(:issue, project: other_project))]
+ end
+
+ def issue_link(issue)
+ Nokogiri::HTML.fragment(%Q{<a data-issue="#{issue.id}"></a>}).children[0]
+ end
+
+ before do
+ project.add_developer(user)
+ end
+
+ it_behaves_like 'no N+1 queries'
+ end
end
diff --git a/spec/lib/banzai/reference_parser/merge_request_parser_spec.rb b/spec/lib/banzai/reference_parser/merge_request_parser_spec.rb
index 775749ae3a7..14542342cf6 100644
--- a/spec/lib/banzai/reference_parser/merge_request_parser_spec.rb
+++ b/spec/lib/banzai/reference_parser/merge_request_parser_spec.rb
@@ -4,14 +4,13 @@ describe Banzai::ReferenceParser::MergeRequestParser do
include ReferenceParserHelpers
let(:user) { create(:user) }
- let(:merge_request) { create(:merge_request) }
- subject { described_class.new(merge_request.target_project, user) }
+ let(:project) { create(:project, :public) }
+ let(:merge_request) { create(:merge_request, source_project: project) }
+ subject { described_class.new(project, user) }
let(:link) { empty_html_link }
describe '#nodes_visible_to_user' do
context 'when the link has a data-issue attribute' do
- let(:project) { merge_request.target_project }
-
before do
project.update_attribute(:visibility_level, Gitlab::VisibilityLevel::PUBLIC)
link['data-merge-request'] = merge_request.id.to_s
@@ -40,4 +39,27 @@ describe Banzai::ReferenceParser::MergeRequestParser do
end
end
end
+
+ context 'when checking multiple merge requests on another project' do
+ let(:other_project) { create(:project, :public) }
+ let(:other_merge_request) { create(:merge_request, source_project: other_project) }
+
+ let(:control_links) do
+ [merge_request_link(other_merge_request)]
+ end
+
+ let(:actual_links) do
+ control_links + [merge_request_link(create(:merge_request, :conflict, source_project: other_project))]
+ end
+
+ def merge_request_link(merge_request)
+ Nokogiri::HTML.fragment(%Q{<a data-merge-request="#{merge_request.id}"></a>}).children[0]
+ end
+
+ before do
+ project.add_developer(user)
+ end
+
+ it_behaves_like 'no N+1 queries'
+ end
end
diff --git a/spec/lib/gitlab/ci/status/build/action_spec.rb b/spec/lib/gitlab/ci/status/build/action_spec.rb
index d612d29e3e0..bdec582b57b 100644
--- a/spec/lib/gitlab/ci/status/build/action_spec.rb
+++ b/spec/lib/gitlab/ci/status/build/action_spec.rb
@@ -53,4 +53,14 @@ describe Gitlab::Ci::Status::Build::Action do
end
end
end
+
+ describe '#badge_tooltip' do
+ let(:user) { create(:user) }
+ let(:build) { create(:ci_build, :non_playable) }
+ let(:status) { Gitlab::Ci::Status::Core.new(build, user) }
+
+ it 'returns the status' do
+ expect(subject.badge_tooltip).to eq('created')
+ end
+ end
end
diff --git a/spec/lib/gitlab/ci/status/build/cancelable_spec.rb b/spec/lib/gitlab/ci/status/build/cancelable_spec.rb
index 9cdebaa5cf2..3ef0b6817e9 100644
--- a/spec/lib/gitlab/ci/status/build/cancelable_spec.rb
+++ b/spec/lib/gitlab/ci/status/build/cancelable_spec.rb
@@ -40,6 +40,24 @@ describe Gitlab::Ci::Status::Build::Cancelable do
end
end
+ describe '#status_tooltip' do
+ it 'does not override status status_tooltip' do
+ expect(status).to receive(:status_tooltip)
+
+ subject.status_tooltip
+ end
+ end
+
+ describe '#badge_tooltip' do
+ let(:user) { create(:user) }
+ let(:build) { create(:ci_build) }
+ let(:status) { Gitlab::Ci::Status::Core.new(build, user) }
+
+ it 'returns the status' do
+ expect(subject.badge_tooltip).to eq('pending')
+ end
+ end
+
describe 'action details' do
let(:user) { create(:user) }
let(:build) { create(:ci_build) }
diff --git a/spec/lib/gitlab/ci/status/build/factory_spec.rb b/spec/lib/gitlab/ci/status/build/factory_spec.rb
index d196bc6a4c2..bbfa60169a1 100644
--- a/spec/lib/gitlab/ci/status/build/factory_spec.rb
+++ b/spec/lib/gitlab/ci/status/build/factory_spec.rb
@@ -48,11 +48,11 @@ describe Gitlab::Ci::Status::Build::Factory do
it 'matches correct extended statuses' do
expect(factory.extended_statuses)
- .to eq [Gitlab::Ci::Status::Build::Retryable]
+ .to eq [Gitlab::Ci::Status::Build::Retryable, Gitlab::Ci::Status::Build::Failed]
end
- it 'fabricates a retryable build status' do
- expect(status).to be_a Gitlab::Ci::Status::Build::Retryable
+ it 'fabricates a failed build status' do
+ expect(status).to be_a Gitlab::Ci::Status::Build::Failed
end
it 'fabricates status with correct details' do
@@ -60,6 +60,7 @@ describe Gitlab::Ci::Status::Build::Factory do
expect(status.icon).to eq 'status_failed'
expect(status.favicon).to eq 'favicon_status_failed'
expect(status.label).to eq 'failed'
+ expect(status.status_tooltip).to eq 'failed <br> (unknown failure)'
expect(status).to have_details
expect(status).to have_action
end
@@ -75,6 +76,7 @@ describe Gitlab::Ci::Status::Build::Factory do
it 'matches correct extended statuses' do
expect(factory.extended_statuses)
.to eq [Gitlab::Ci::Status::Build::Retryable,
+ Gitlab::Ci::Status::Build::Failed,
Gitlab::Ci::Status::Build::FailedAllowed]
end
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 99a5a7e4aca..bfaa508785e 100644
--- a/spec/lib/gitlab/ci/status/build/failed_allowed_spec.rb
+++ b/spec/lib/gitlab/ci/status/build/failed_allowed_spec.rb
@@ -3,6 +3,7 @@ require 'spec_helper'
describe Gitlab::Ci::Status::Build::FailedAllowed do
let(:status) { double('core status') }
let(:user) { double('user') }
+ let(:build) { create(:ci_build, :failed, :allowed_to_fail) }
subject do
described_class.new(status)
@@ -68,6 +69,28 @@ describe Gitlab::Ci::Status::Build::FailedAllowed do
end
end
+ describe '#badge_tooltip' do
+ let(:user) { create(:user) }
+ let(:failed_status) { Gitlab::Ci::Status::Failed.new(build, user) }
+ let(:build_status) { Gitlab::Ci::Status::Build::Failed.new(failed_status) }
+ let(:status) { described_class.new(build_status) }
+
+ it 'does override badge_tooltip' do
+ expect(status.badge_tooltip).to eq('failed <br> (unknown failure)')
+ end
+ end
+
+ describe '#status_tooltip' do
+ let(:user) { create(:user) }
+ let(:failed_status) { Gitlab::Ci::Status::Failed.new(build, user) }
+ let(:build_status) { Gitlab::Ci::Status::Build::Failed.new(failed_status) }
+ let(:status) { described_class.new(build_status) }
+
+ it 'does override status_tooltip' do
+ expect(status.status_tooltip).to eq 'failed <br> (unknown failure) (allowed to fail)'
+ end
+ end
+
describe '.matches?' do
subject { described_class.matches?(build, user) }
diff --git a/spec/lib/gitlab/ci/status/build/failed_spec.rb b/spec/lib/gitlab/ci/status/build/failed_spec.rb
new file mode 100644
index 00000000000..cadb424ea2c
--- /dev/null
+++ b/spec/lib/gitlab/ci/status/build/failed_spec.rb
@@ -0,0 +1,83 @@
+require 'spec_helper'
+
+describe Gitlab::Ci::Status::Build::Failed do
+ let(:build) { create(:ci_build, :script_failure) }
+ let(:status) { double('core status') }
+ let(:user) { double('user') }
+
+ subject { described_class.new(status) }
+
+ describe '#text' do
+ it 'does not override status text' do
+ expect(status).to receive(:text)
+
+ subject.text
+ end
+ end
+
+ describe '#icon' do
+ it 'does not override status icon' do
+ expect(status).to receive(:icon)
+
+ subject.icon
+ end
+ end
+
+ describe '#group' do
+ it 'does not override status group' do
+ expect(status).to receive(:group)
+
+ subject.group
+ end
+ end
+
+ describe '#favicon' do
+ it 'does not override status label' do
+ expect(status).to receive(:favicon)
+
+ subject.favicon
+ end
+ end
+
+ describe '#label' do
+ it 'does not override label' do
+ expect(status).to receive(:label)
+
+ subject.label
+ end
+ end
+
+ describe '#badge_tooltip' do
+ let(:user) { create(:user) }
+ let(:status) { Gitlab::Ci::Status::Failed.new(build, user) }
+
+ it 'does override badge_tooltip' do
+ expect(subject.badge_tooltip).to eq 'failed <br> (script failure)'
+ end
+ end
+
+ describe '#status_tooltip' do
+ let(:user) { create(:user) }
+ let(:status) { Gitlab::Ci::Status::Failed.new(build, user) }
+
+ it 'does override status_tooltip' do
+ expect(subject.status_tooltip).to eq 'failed <br> (script failure)'
+ end
+ end
+
+ describe '.matches?' do
+ context 'with a failed build' do
+ it 'returns true' do
+ expect(described_class.matches?(build, user)).to be_truthy
+ end
+ end
+
+ context 'with any other type of build' do
+ let(:build) { create(:ci_build, :success) }
+
+ it 'returns false' do
+ expect(described_class.matches?(build, user)).to be_falsy
+ end
+ end
+ end
+end
diff --git a/spec/lib/gitlab/ci/status/build/play_spec.rb b/spec/lib/gitlab/ci/status/build/play_spec.rb
index 81d5f553fd1..35e47cd2526 100644
--- a/spec/lib/gitlab/ci/status/build/play_spec.rb
+++ b/spec/lib/gitlab/ci/status/build/play_spec.rb
@@ -14,6 +14,22 @@ describe Gitlab::Ci::Status::Build::Play do
end
end
+ describe '#status_tooltip' do
+ it 'does not override status status_tooltip' do
+ expect(status).to receive(:status_tooltip)
+
+ subject.status_tooltip
+ end
+ end
+
+ describe '#badge_tooltip' do
+ it 'does not override status badge_tooltip' do
+ expect(status).to receive(:badge_tooltip)
+
+ subject.badge_tooltip
+ end
+ end
+
describe '#has_action?' do
context 'when user is allowed to update build' do
context 'when user is allowed to trigger protected action' do
diff --git a/spec/lib/gitlab/ci/status/build/retried_spec.rb b/spec/lib/gitlab/ci/status/build/retried_spec.rb
new file mode 100644
index 00000000000..ee9acaf1c21
--- /dev/null
+++ b/spec/lib/gitlab/ci/status/build/retried_spec.rb
@@ -0,0 +1,96 @@
+require 'spec_helper'
+
+describe Gitlab::Ci::Status::Build::Retried do
+ let(:build) { create(:ci_build, :retried) }
+ let(:status) { double('core status') }
+ let(:user) { double('user') }
+
+ subject { described_class.new(status) }
+
+ describe '#text' do
+ it 'does not override status text' do
+ expect(status).to receive(:text)
+
+ subject.text
+ end
+ end
+
+ describe '#icon' do
+ it 'does not override status icon' do
+ expect(status).to receive(:icon)
+
+ subject.icon
+ end
+ end
+
+ describe '#group' do
+ it 'does not override status group' do
+ expect(status).to receive(:group)
+
+ subject.group
+ end
+ end
+
+ describe '#favicon' do
+ it 'does not override status label' do
+ expect(status).to receive(:favicon)
+
+ subject.favicon
+ end
+ end
+
+ describe '#label' do
+ it 'does not override status label' do
+ expect(status).to receive(:label)
+
+ subject.label
+ end
+ end
+
+ describe '#badge_tooltip' do
+ let(:user) { create(:user) }
+ let(:build) { create(:ci_build, :retried) }
+ let(:status) { Gitlab::Ci::Status::Success.new(build, user) }
+
+ it 'returns status' do
+ expect(status.badge_tooltip).to eq('pending')
+ end
+ end
+
+ describe '#status_tooltip' do
+ let(:user) { create(:user) }
+
+ context 'with a failed build' do
+ let(:build) { create(:ci_build, :failed, :retried) }
+ let(:failed_status) { Gitlab::Ci::Status::Failed.new(build, user) }
+ let(:status) { Gitlab::Ci::Status::Build::Failed.new(failed_status) }
+
+ it 'does override status_tooltip' do
+ expect(subject.status_tooltip).to eq 'failed <br> (unknown failure) (retried)'
+ end
+ end
+
+ context 'with another build' do
+ let(:build) { create(:ci_build, :retried) }
+ let(:status) { Gitlab::Ci::Status::Success.new(build, user) }
+
+ it 'does override status_tooltip' do
+ expect(subject.status_tooltip).to eq 'passed (retried)'
+ end
+ end
+ end
+
+ describe '.matches?' do
+ subject { described_class.matches?(build, user) }
+
+ context 'with a retried build' do
+ it { is_expected.to be_truthy }
+ end
+
+ context 'with a build that has not been retried' do
+ let(:build) { create(:ci_build, :success) }
+
+ it { is_expected.to be_falsy }
+ end
+ end
+end
diff --git a/spec/lib/gitlab/ci/status/build/retryable_spec.rb b/spec/lib/gitlab/ci/status/build/retryable_spec.rb
index 14d42e0d70f..0c5099b7da5 100644
--- a/spec/lib/gitlab/ci/status/build/retryable_spec.rb
+++ b/spec/lib/gitlab/ci/status/build/retryable_spec.rb
@@ -40,6 +40,24 @@ describe Gitlab::Ci::Status::Build::Retryable do
end
end
+ describe '#status_tooltip' do
+ it 'does not override status status_tooltip' do
+ expect(status).to receive(:status_tooltip)
+
+ subject.status_tooltip
+ end
+ end
+
+ describe '#badge_tooltip' do
+ let(:user) { create(:user) }
+ let(:build) { create(:ci_build) }
+ let(:status) { Gitlab::Ci::Status::Core.new(build, user) }
+
+ it 'does return status' do
+ expect(status.badge_tooltip).to eq('pending')
+ end
+ end
+
describe 'action details' do
let(:user) { create(:user) }
let(:build) { create(:ci_build) }
diff --git a/spec/lib/gitlab/ci/status/build/stop_spec.rb b/spec/lib/gitlab/ci/status/build/stop_spec.rb
index 18e250772f0..f16fc5c9205 100644
--- a/spec/lib/gitlab/ci/status/build/stop_spec.rb
+++ b/spec/lib/gitlab/ci/status/build/stop_spec.rb
@@ -77,4 +77,24 @@ describe Gitlab::Ci::Status::Build::Stop do
end
end
end
+
+ describe '#status_tooltip' do
+ it 'does not override status status_tooltip' do
+ expect(status).to receive(:status_tooltip)
+
+ subject.status_tooltip
+ end
+ end
+
+ describe '#badge_tooltip' do
+ let(:user) { create(:user) }
+ let(:build) { create(:ci_build, :playable) }
+ let(:status) { Gitlab::Ci::Status::Core.new(build, user) }
+
+ it 'does not override status badge_tooltip' do
+ expect(status).to receive(:badge_tooltip)
+
+ subject.badge_tooltip
+ end
+ 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 4582354e739..6d05545d1d8 100644
--- a/spec/lib/gitlab/ci/status/success_warning_spec.rb
+++ b/spec/lib/gitlab/ci/status/success_warning_spec.rb
@@ -1,8 +1,10 @@
require 'spec_helper'
describe Gitlab::Ci::Status::SuccessWarning do
+ let(:status) { double('status') }
+
subject do
- described_class.new(double('status'))
+ described_class.new(status)
end
describe '#test' do
diff --git a/spec/lib/gitlab/git_access_spec.rb b/spec/lib/gitlab/git_access_spec.rb
index f8f09d29c73..b845abab5ef 100644
--- a/spec/lib/gitlab/git_access_spec.rb
+++ b/spec/lib/gitlab/git_access_spec.rb
@@ -10,12 +10,13 @@ describe Gitlab::GitAccess do
let(:protocol) { 'ssh' }
let(:authentication_abilities) { %i[read_project download_code push_code] }
let(:redirected_path) { nil }
+ let(:auth_result_type) { nil }
let(:access) do
described_class.new(actor, project,
protocol, authentication_abilities: authentication_abilities,
namespace_path: namespace_path, project_path: project_path,
- redirected_path: redirected_path)
+ redirected_path: redirected_path, auth_result_type: auth_result_type)
end
let(:changes) { '_any' }
@@ -45,6 +46,7 @@ describe Gitlab::GitAccess do
before do
disable_protocol('http')
+ project.add_master(user)
end
it 'blocks http push and pull' do
@@ -53,6 +55,26 @@ describe Gitlab::GitAccess do
expect { pull_access_check }.to raise_unauthorized('Git access over HTTP is not allowed')
end
end
+
+ context 'when request is made from CI' do
+ let(:auth_result_type) { :build }
+
+ it "doesn't block http pull" do
+ aggregate_failures do
+ expect { pull_access_check }.not_to raise_unauthorized('Git access over HTTP is not allowed')
+ end
+ end
+
+ context 'when legacy CI credentials are used' do
+ let(:auth_result_type) { :ci }
+
+ it "doesn't block http pull" do
+ aggregate_failures do
+ expect { pull_access_check }.not_to raise_unauthorized('Git access over HTTP is not allowed')
+ end
+ end
+ end
+ end
end
end
diff --git a/spec/lib/gitlab/import_export/project.json b/spec/lib/gitlab/import_export/project.json
index 4a51777ba9b..6d63749296e 100644
--- a/spec/lib/gitlab/import_export/project.json
+++ b/spec/lib/gitlab/import_export/project.json
@@ -2,7 +2,6 @@
"description": "Nisi et repellendus ut enim quo accusamus vel magnam.",
"visibility_level": 10,
"archived": false,
- "description_html": "description",
"labels": [
{
"id": 2,
@@ -6181,12 +6180,6 @@
"user_id": null,
"target_url": null,
"description": null,
- "artifacts_file": {
- "url": null
- },
- "artifacts_metadata": {
- "url": null
- },
"erased_by_id": null,
"erased_at": null,
"type": "Ci::Build",
@@ -6219,12 +6212,6 @@
"user_id": null,
"target_url": null,
"description": null,
- "artifacts_file": {
- "url": "/Users/Test/Test/gitlab-development-kit/gitlab/shared/artifacts/2016_03/5/72/p5_build_artifacts.zip"
- },
- "artifacts_metadata": {
- "url": "/Users/Test/Test/gitlab-development-kit/gitlab/shared/artifacts/2016_03/5/72/p5_build_artifacts_metadata.gz"
- },
"erased_by_id": null,
"erased_at": null
}
@@ -6293,12 +6280,6 @@
"user_id": null,
"target_url": null,
"description": null,
- "artifacts_file": {
- "url": "/Users/Test/Test/gitlab-development-kit/gitlab/shared/artifacts/2016_03/5/74/p5_build_artifacts.zip"
- },
- "artifacts_metadata": {
- "url": "/Users/Test/Test/gitlab-development-kit/gitlab/shared/artifacts/2016_03/5/74/p5_build_artifacts_metadata.gz"
- },
"erased_by_id": null,
"erased_at": null
},
@@ -6328,12 +6309,6 @@
"user_id": null,
"target_url": null,
"description": null,
- "artifacts_file": {
- "url": null
- },
- "artifacts_metadata": {
- "url": null
- },
"erased_by_id": null,
"erased_at": null
}
@@ -6393,12 +6368,6 @@
"user_id": null,
"target_url": null,
"description": null,
- "artifacts_file": {
- "url": "/Users/Test/Test/gitlab-development-kit/gitlab/shared/artifacts/2016_03/5/76/p5_build_artifacts.zip"
- },
- "artifacts_metadata": {
- "url": "/Users/Test/Test/gitlab-development-kit/gitlab/shared/artifacts/2016_03/5/76/p5_build_artifacts_metadata.gz"
- },
"erased_by_id": null,
"erased_at": null
},
@@ -6428,12 +6397,6 @@
"user_id": null,
"target_url": null,
"description": null,
- "artifacts_file": {
- "url": "/Users/Test/Test/gitlab-development-kit/gitlab/shared/artifacts/2016_03/5/75/p5_build_artifacts.zip"
- },
- "artifacts_metadata": {
- "url": "/Users/Test/Test/gitlab-development-kit/gitlab/shared/artifacts/2016_03/5/75/p5_build_artifacts_metadata.gz"
- },
"erased_by_id": null,
"erased_at": null
}
@@ -6493,12 +6456,6 @@
"user_id": null,
"target_url": null,
"description": null,
- "artifacts_file": {
- "url": "/Users/Test/Test/gitlab-development-kit/gitlab/shared/artifacts/2016_03/5/78/p5_build_artifacts.zip"
- },
- "artifacts_metadata": {
- "url": "/Users/Test/Test/gitlab-development-kit/gitlab/shared/artifacts/2016_03/5/78/p5_build_artifacts_metadata.gz"
- },
"erased_by_id": null,
"erased_at": null
},
@@ -6528,12 +6485,6 @@
"user_id": null,
"target_url": null,
"description": null,
- "artifacts_file": {
- "url": "/Users/Test/Test/gitlab-development-kit/gitlab/shared/artifacts/2016_03/5/77/p5_build_artifacts.zip"
- },
- "artifacts_metadata": {
- "url": "/Users/Test/Test/gitlab-development-kit/gitlab/shared/artifacts/2016_03/5/77/p5_build_artifacts_metadata.gz"
- },
"erased_by_id": null,
"erased_at": null
}
@@ -6593,12 +6544,6 @@
"user_id": null,
"target_url": null,
"description": null,
- "artifacts_file": {
- "url": null
- },
- "artifacts_metadata": {
- "url": null
- },
"erased_by_id": null,
"erased_at": null
},
@@ -6628,12 +6573,6 @@
"user_id": null,
"target_url": null,
"description": null,
- "artifacts_file": {
- "url": "/Users/Test/Test/gitlab-development-kit/gitlab/shared/artifacts/2016_03/5/80/p5_build_artifacts.zip"
- },
- "artifacts_metadata": {
- "url": "/Users/Test/Test/gitlab-development-kit/gitlab/shared/artifacts/2016_03/5/80/p5_build_artifacts_metadata.gz"
- },
"erased_by_id": null,
"erased_at": null
}
diff --git a/spec/lib/gitlab/import_export/project_tree_restorer_spec.rb b/spec/lib/gitlab/import_export/project_tree_restorer_spec.rb
index 8e25cd26c2f..13a8c9adcee 100644
--- a/spec/lib/gitlab/import_export/project_tree_restorer_spec.rb
+++ b/spec/lib/gitlab/import_export/project_tree_restorer_spec.rb
@@ -46,10 +46,6 @@ describe Gitlab::ImportExport::ProjectTreeRestorer do
expect(Project.find_by_path('project').description).to eq('Nisi et repellendus ut enim quo accusamus vel magnam.')
end
- it 'has the project html description' do
- expect(Project.find_by_path('project').description_html).to eq('description')
- end
-
it 'has the same label associated to two issues' do
expect(ProjectLabel.find_by_title('test2').issues.count).to eq(2)
end
@@ -317,6 +313,24 @@ describe Gitlab::ImportExport::ProjectTreeRestorer do
end
end
+ context 'when the project has overriden params in import data' do
+ it 'overwrites the params stored in the JSON' do
+ project.create_import_data(data: { override_params: { description: "Overridden" } })
+
+ restored_project_json
+
+ expect(project.description).to eq("Overridden")
+ end
+
+ it 'does not allow setting params that are excluded from import_export settings' do
+ project.create_import_data(data: { override_params: { lfs_enabled: true } })
+
+ restored_project_json
+
+ expect(project.lfs_enabled).to be_nil
+ end
+ end
+
context 'with a project that has a group' do
let!(:project) do
create(:project,
diff --git a/spec/lib/gitlab/import_export/project_tree_saver_spec.rb b/spec/lib/gitlab/import_export/project_tree_saver_spec.rb
index 0d20a551e2a..2b8a11ce8f9 100644
--- a/spec/lib/gitlab/import_export/project_tree_saver_spec.rb
+++ b/spec/lib/gitlab/import_export/project_tree_saver_spec.rb
@@ -245,10 +245,6 @@ describe Gitlab::ImportExport::ProjectTreeSaver do
end
context 'project attributes' do
- it 'contains the html description' do
- expect(saved_project_json).to include("description_html" => 'description')
- end
-
it 'does not contain the runners token' do
expect(saved_project_json).not_to include("runners_token" => 'token')
end
@@ -274,7 +270,6 @@ describe Gitlab::ImportExport::ProjectTreeSaver do
releases: [release],
group: group
)
- project.update_column(:description_html, 'description')
project_label = create(:label, project: project)
group_label = create(:group_label, group: group)
create(:label_link, label: project_label, target: issue)
diff --git a/spec/lib/uploaded_file_spec.rb b/spec/lib/uploaded_file_spec.rb
new file mode 100644
index 00000000000..cc99e7e8911
--- /dev/null
+++ b/spec/lib/uploaded_file_spec.rb
@@ -0,0 +1,116 @@
+require 'spec_helper'
+
+describe UploadedFile do
+ describe ".from_params" do
+ let(:temp_dir) { Dir.tmpdir }
+ let(:temp_file) { Tempfile.new("test", temp_dir) }
+ let(:upload_path) { nil }
+
+ subject do
+ described_class.from_params(params, :file, upload_path)
+ end
+
+ before do
+ FileUtils.touch(temp_file)
+ end
+
+ after do
+ FileUtils.rm_f(temp_file)
+ FileUtils.rm_r(upload_path) if upload_path
+ end
+
+ context 'when valid file is specified' do
+ context 'only local path is specified' do
+ let(:params) do
+ { 'file.path' => temp_file.path }
+ end
+
+ it "succeeds" do
+ is_expected.not_to be_nil
+ end
+
+ it "generates filename from path" do
+ expect(subject.original_filename).to eq(::File.basename(temp_file.path))
+ end
+ end
+
+ context 'all parameters are specified' do
+ let(:params) do
+ { 'file.path' => temp_file.path,
+ 'file.name' => 'my_file.txt',
+ 'file.type' => 'my/type',
+ 'file.sha256' => 'sha256',
+ 'file.remote_id' => 'remote_id' }
+ end
+
+ it "succeeds" do
+ is_expected.not_to be_nil
+ end
+
+ it "generates filename from path" do
+ expect(subject.original_filename).to eq('my_file.txt')
+ expect(subject.content_type).to eq('my/type')
+ expect(subject.sha256).to eq('sha256')
+ expect(subject.remote_id).to eq('remote_id')
+ end
+ end
+ end
+
+ context 'when no params are specified' do
+ let(:params) do
+ {}
+ end
+
+ it "does not return an object" do
+ is_expected.to be_nil
+ end
+ end
+
+ context 'when only remote id is specified' do
+ let(:params) do
+ { 'file.remote_id' => 'remote_id' }
+ end
+
+ it "raises an error" do
+ expect { subject }.to raise_error(UploadedFile::InvalidPathError, /file is invalid/)
+ end
+ end
+
+ context 'when verifying allowed paths' do
+ let(:params) do
+ { 'file.path' => temp_file.path }
+ end
+
+ context 'when file is stored in system temporary folder' do
+ let(:temp_dir) { Dir.tmpdir }
+
+ it "succeeds" do
+ is_expected.not_to be_nil
+ end
+ end
+
+ context 'when file is stored in user provided upload path' do
+ let(:upload_path) { Dir.mktmpdir }
+ let(:temp_dir) { upload_path }
+
+ it "succeeds" do
+ is_expected.not_to be_nil
+ end
+ end
+
+ context 'when file is stored outside of user provided upload path' do
+ let!(:generated_dir) { Dir.mktmpdir }
+ let!(:temp_dir) { Dir.mktmpdir }
+
+ before do
+ # We overwrite default temporary path
+ allow(Dir).to receive(:tmpdir).and_return(generated_dir)
+ end
+
+ it "raises an error" do
+ expect { subject }.to raise_error(UploadedFile::InvalidPathError, /insecure path used/)
+ end
+ end
+ end
+ end
+end
diff --git a/spec/migrations/rename_users_with_renamed_namespace_spec.rb b/spec/migrations/rename_users_with_renamed_namespace_spec.rb
index cbc0ebeb44d..e2994103ed7 100644
--- a/spec/migrations/rename_users_with_renamed_namespace_spec.rb
+++ b/spec/migrations/rename_users_with_renamed_namespace_spec.rb
@@ -7,9 +7,9 @@ describe RenameUsersWithRenamedNamespace, :delete do
other_user1 = create(:user, username: 'api0')
user = create(:user, username: "Users0")
- user.update_attribute(:username, 'Users')
+ user.update_column(:username, 'Users')
user1 = create(:user, username: "import0")
- user1.update_attribute(:username, 'import')
+ user1.update_column(:username, 'import')
described_class.new.up
diff --git a/spec/migrations/schedule_build_stage_migration_spec.rb b/spec/migrations/reschedule_builds_stages_migration_spec.rb
index e2ca35447fb..3bfd9dd9f6b 100644
--- a/spec/migrations/schedule_build_stage_migration_spec.rb
+++ b/spec/migrations/reschedule_builds_stages_migration_spec.rb
@@ -1,7 +1,8 @@
require 'spec_helper'
-require Rails.root.join('db', 'post_migrate', '20180212101928_schedule_build_stage_migration')
+require Rails.root.join('db', 'post_migrate', '20180405101928_reschedule_builds_stages_migration')
-describe ScheduleBuildStageMigration, :sidekiq, :migration do
+describe RescheduleBuildsStagesMigration, :sidekiq, :migration do
+ let(:namespaces) { table(:namespaces) }
let(:projects) { table(:projects) }
let(:pipelines) { table(:ci_pipelines) }
let(:stages) { table(:ci_stages) }
@@ -10,7 +11,8 @@ describe ScheduleBuildStageMigration, :sidekiq, :migration do
before do
stub_const("#{described_class}::BATCH_SIZE", 1)
- projects.create!(id: 123, name: 'gitlab', path: 'gitlab-ce')
+ namespaces.create(id: 12, name: 'gitlab-org', path: 'gitlab-org')
+ projects.create!(id: 123, namespace_id: 12, name: 'gitlab', path: 'gitlab')
pipelines.create!(id: 1, project_id: 123, ref: 'master', sha: 'adf43c3a')
stages.create!(id: 1, project_id: 123, pipeline_id: 1, name: 'test')
diff --git a/spec/models/concerns/chronic_duration_attribute_spec.rb b/spec/models/concerns/chronic_duration_attribute_spec.rb
index 27c86e60e60..8847623f705 100644
--- a/spec/models/concerns/chronic_duration_attribute_spec.rb
+++ b/spec/models/concerns/chronic_duration_attribute_spec.rb
@@ -63,8 +63,8 @@ shared_examples 'ChronicDurationAttribute writer' do
subject.send("#{virtual_field}=", '')
end
- it 'writes nil' do
- expect(subject.send(source_field)).to be_nil
+ it 'writes default value' do
+ expect(subject.send(source_field)).to eq(default_value)
end
it 'passes validation' do
@@ -77,8 +77,8 @@ shared_examples 'ChronicDurationAttribute writer' do
subject.send("#{virtual_field}=", nil)
end
- it 'writes nil' do
- expect(subject.send(source_field)).to be_nil
+ it 'writes default value' do
+ expect(subject.send(source_field)).to eq(default_value)
end
it 'passes validation' do
@@ -92,20 +92,34 @@ shared_examples 'ChronicDurationAttribute writer' do
end
describe 'ChronicDurationAttribute' do
- let(:source_field) {:maximum_timeout}
- let(:virtual_field) {:maximum_timeout_human_readable}
+ context 'when default value is not set' do
+ let(:source_field) {:maximum_timeout}
+ let(:virtual_field) {:maximum_timeout_human_readable}
+ let(:default_value) { nil }
- subject { Ci::Runner.new }
+ subject { create(:ci_runner) }
- it_behaves_like 'ChronicDurationAttribute reader'
- it_behaves_like 'ChronicDurationAttribute writer'
+ it_behaves_like 'ChronicDurationAttribute reader'
+ it_behaves_like 'ChronicDurationAttribute writer'
+ end
+
+ context 'when default value is set' do
+ let(:source_field) {:build_timeout}
+ let(:virtual_field) {:build_timeout_human_readable}
+ let(:default_value) { 3600 }
+
+ subject { create(:project) }
+
+ it_behaves_like 'ChronicDurationAttribute reader'
+ it_behaves_like 'ChronicDurationAttribute writer'
+ end
end
describe 'ChronicDurationAttribute - reader' do
let(:source_field) {:timeout}
let(:virtual_field) {:timeout_human_readable}
- subject {Ci::BuildMetadata.new}
+ subject { create(:ci_build).ensure_metadata }
it "doesn't contain dynamically created writer method" do
expect(subject.class).not_to be_public_method_defined("#{virtual_field}=")
diff --git a/spec/models/project_spec.rb b/spec/models/project_spec.rb
index 8bd62dcdccb..7007f78e702 100644
--- a/spec/models/project_spec.rb
+++ b/spec/models/project_spec.rb
@@ -3243,6 +3243,7 @@ describe Project do
expect(project).to receive(:update_project_counter_caches)
expect(project).to receive(:remove_import_jid)
expect(project).to receive(:after_create_default_branch)
+ expect(project).to receive(:refresh_markdown_cache!)
project.after_import
end
diff --git a/spec/models/user_spec.rb b/spec/models/user_spec.rb
index 4027c420e47..a600987d0bf 100644
--- a/spec/models/user_spec.rb
+++ b/spec/models/user_spec.rb
@@ -2071,6 +2071,8 @@ describe User do
expect(ghost).to be_ghost
expect(ghost).to be_persisted
+ expect(ghost.namespace).not_to be_nil
+ expect(ghost.namespace).to be_persisted
end
it "does not create a second ghost user if one is already present" do
diff --git a/spec/policies/project_policy_spec.rb b/spec/policies/project_policy_spec.rb
index ea76e604153..905d82b3bb1 100644
--- a/spec/policies/project_policy_spec.rb
+++ b/spec/policies/project_policy_spec.rb
@@ -11,10 +11,10 @@ describe ProjectPolicy do
let(:base_guest_permissions) do
%i[
- read_project read_board read_list read_wiki read_issue read_label
- read_milestone read_project_snippet read_project_member
- read_note create_project create_issue create_note
- upload_file
+ read_project read_board read_list read_wiki read_issue
+ read_project_for_iids read_issue_iid read_merge_request_iid read_label
+ read_milestone read_project_snippet read_project_member read_note
+ create_project create_issue create_note upload_file
]
end
@@ -120,7 +120,7 @@ describe ProjectPolicy do
project.issues_enabled = false
project.save!
- expect_disallowed :read_issue, :create_issue, :update_issue, :admin_issue
+ expect_disallowed :read_issue, :read_issue_iid, :create_issue, :update_issue, :admin_issue
end
end
@@ -131,7 +131,7 @@ describe ProjectPolicy do
project.issues_enabled = false
project.save!
- expect_disallowed :read_issue, :create_issue, :update_issue, :admin_issue
+ expect_disallowed :read_issue, :read_issue_iid, :create_issue, :update_issue, :admin_issue
end
end
end
diff --git a/spec/presenters/ci/build_presenter_spec.rb b/spec/presenters/ci/build_presenter_spec.rb
index 1a8001be6ab..cc16d0f156b 100644
--- a/spec/presenters/ci/build_presenter_spec.rb
+++ b/spec/presenters/ci/build_presenter_spec.rb
@@ -72,13 +72,44 @@ describe Ci::BuildPresenter do
end
end
- context 'when build is not auto-canceled' do
- before do
- expect(build).to receive(:auto_canceled?).and_return(false)
+ context 'when build failed' do
+ let(:build) { create(:ci_build, :failed, pipeline: pipeline) }
+
+ it 'returns the reason of failure' do
+ status_title = presenter.status_title
+
+ expect(status_title).to eq('Failed <br> (unknown failure)')
end
+ end
+
+ context 'when build has failed && retried' do
+ let(:build) { create(:ci_build, :failed, :retried, pipeline: pipeline) }
- it 'does not have a status title' do
- expect(presenter.status_title).to be_nil
+ it 'does not include retried title' do
+ status_title = presenter.status_title
+
+ expect(status_title).not_to include('(retried)')
+ expect(status_title).to eq('Failed <br> (unknown failure)')
+ end
+ end
+
+ context 'when build has failed and is allowed to' do
+ let(:build) { create(:ci_build, :failed, :allowed_to_fail, pipeline: pipeline) }
+
+ it 'returns the reason of failure' do
+ status_title = presenter.status_title
+
+ expect(status_title).to eq('Failed <br> (unknown failure)')
+ end
+ end
+
+ context 'For any other build' do
+ let(:build) { create(:ci_build, :success, pipeline: pipeline) }
+
+ it 'returns the status' do
+ tooltip_description = presenter.status_title
+
+ expect(tooltip_description).to eq('Success')
end
end
end
@@ -134,4 +165,56 @@ describe Ci::BuildPresenter do
end
end
end
+
+ describe '#tooltip_message' do
+ context 'When build has failed' do
+ let(:build) { create(:ci_build, :script_failure, pipeline: pipeline) }
+
+ it 'returns the reason of failure' do
+ tooltip = subject.tooltip_message
+
+ expect(tooltip).to eq("#{build.name} - failed <br> (script failure)")
+ end
+ end
+
+ context 'When build has failed and retried' do
+ let(:build) { create(:ci_build, :script_failure, :retried, pipeline: pipeline) }
+
+ it 'should include the reason of failure and the retried title' do
+ tooltip = subject.tooltip_message
+
+ expect(tooltip).to eq("#{build.name} - failed <br> (script failure) (retried)")
+ end
+ end
+
+ context 'When build has failed and is allowed to' do
+ let(:build) { create(:ci_build, :script_failure, :allowed_to_fail, pipeline: pipeline) }
+
+ it 'should include the reason of failure' do
+ tooltip = subject.tooltip_message
+
+ expect(tooltip).to eq("#{build.name} - failed <br> (script failure) (allowed to fail)")
+ end
+ end
+
+ context 'For any other build (no retried)' do
+ let(:build) { create(:ci_build, :success, pipeline: pipeline) }
+
+ it 'should include build name and status' do
+ tooltip = subject.tooltip_message
+
+ expect(tooltip).to eq("#{build.name} - passed")
+ end
+ end
+
+ context 'For any other build (retried)' do
+ let(:build) { create(:ci_build, :success, :retried, pipeline: pipeline) }
+
+ it 'should include build name and status' do
+ tooltip = subject.tooltip_message
+
+ expect(tooltip).to eq("#{build.name} - passed (retried)")
+ end
+ end
+ end
end
diff --git a/spec/requests/api/project_import_spec.rb b/spec/requests/api/project_import_spec.rb
index 987f6e26971..5d13e6de741 100644
--- a/spec/requests/api/project_import_spec.rb
+++ b/spec/requests/api/project_import_spec.rb
@@ -40,7 +40,7 @@ describe API::ProjectImport do
expect(response).to have_gitlab_http_status(201)
end
- it 'schedules an import at the user namespace level' do
+ it 'does not shedule an import for a nampespace that does not exist' do
expect_any_instance_of(Project).not_to receive(:import_schedule)
expect(::Projects::CreateService).not_to receive(:new)
@@ -71,6 +71,49 @@ describe API::ProjectImport do
expect(json_response['error']).to eq('file is invalid')
end
+ it 'stores params that can be overridden' do
+ stub_import(namespace)
+ override_params = { 'description' => 'Hello world' }
+
+ post api('/projects/import', user),
+ path: 'test-import',
+ file: fixture_file_upload(file),
+ namespace: namespace.id,
+ override_params: override_params
+ import_project = Project.find(json_response['id'])
+
+ expect(import_project.import_data.data['override_params']).to eq(override_params)
+ end
+
+ it 'does not store params that are not allowed' do
+ stub_import(namespace)
+ override_params = { 'not_allowed' => 'Hello world' }
+
+ post api('/projects/import', user),
+ path: 'test-import',
+ file: fixture_file_upload(file),
+ namespace: namespace.id,
+ override_params: override_params
+ import_project = Project.find(json_response['id'])
+
+ expect(import_project.import_data.data['override_params']).to be_empty
+ end
+
+ it 'correctly overrides params during the import' do
+ override_params = { 'description' => 'Hello world' }
+
+ Sidekiq::Testing.inline! do
+ post api('/projects/import', user),
+ path: 'test-import',
+ file: fixture_file_upload(file),
+ namespace: namespace.id,
+ override_params: override_params
+ end
+ import_project = Project.find(json_response['id'])
+
+ expect(import_project.description).to eq('Hello world')
+ end
+
def stub_import(namespace)
expect_any_instance_of(Project).to receive(:import_schedule)
expect(::Projects::CreateService).to receive(:new).with(user, hash_including(namespace_id: namespace.id)).and_call_original
diff --git a/spec/requests/api/runner_spec.rb b/spec/requests/api/runner_spec.rb
index 4f3420cc0ad..28d51ac86c6 100644
--- a/spec/requests/api/runner_spec.rb
+++ b/spec/requests/api/runner_spec.rb
@@ -950,12 +950,53 @@ describe API::Runner do
describe 'POST /api/v4/jobs/:id/artifacts/authorize' do
context 'when using token as parameter' do
- it 'authorizes posting artifacts to running job' do
- authorize_artifacts_with_token_in_params
+ context 'posting artifacts to running job' do
+ subject do
+ authorize_artifacts_with_token_in_params
+ end
- expect(response).to have_gitlab_http_status(200)
- expect(response.content_type.to_s).to eq(Gitlab::Workhorse::INTERNAL_API_CONTENT_TYPE)
- expect(json_response['TempPath']).not_to be_nil
+ shared_examples 'authorizes local file' do
+ it 'succeeds' do
+ subject
+
+ expect(response).to have_gitlab_http_status(200)
+ expect(response.content_type.to_s).to eq(Gitlab::Workhorse::INTERNAL_API_CONTENT_TYPE)
+ expect(json_response['TempPath']).to eq(JobArtifactUploader.workhorse_local_upload_path)
+ expect(json_response['RemoteObject']).to be_nil
+ end
+ end
+
+ context 'when using local storage' do
+ it_behaves_like 'authorizes local file'
+ end
+
+ context 'when using remote storage' do
+ context 'when direct upload is enabled' do
+ before do
+ stub_artifacts_object_storage(enabled: true, direct_upload: true)
+ end
+
+ it 'succeeds' do
+ subject
+
+ expect(response).to have_gitlab_http_status(200)
+ expect(response.content_type.to_s).to eq(Gitlab::Workhorse::INTERNAL_API_CONTENT_TYPE)
+ expect(json_response['TempPath']).to eq(JobArtifactUploader.workhorse_local_upload_path)
+ expect(json_response['RemoteObject']).to have_key('ID')
+ expect(json_response['RemoteObject']).to have_key('GetURL')
+ expect(json_response['RemoteObject']).to have_key('StoreURL')
+ expect(json_response['RemoteObject']).to have_key('DeleteURL')
+ end
+ end
+
+ context 'when direct upload is disabled' do
+ before do
+ stub_artifacts_object_storage(enabled: true, direct_upload: false)
+ end
+
+ it_behaves_like 'authorizes local file'
+ end
+ end
end
it 'fails to post too large artifact' do
@@ -1051,20 +1092,45 @@ describe API::Runner do
end
end
- context 'when uses regular file post' do
- before do
- upload_artifacts(file_upload, headers_with_token, false)
+ context 'when uses accelerated file post' do
+ context 'for file stored locally' do
+ before do
+ upload_artifacts(file_upload, headers_with_token)
+ end
+
+ it_behaves_like 'successful artifacts upload'
end
- it_behaves_like 'successful artifacts upload'
- end
+ context 'for file stored remotelly' do
+ let!(:fog_connection) do
+ stub_artifacts_object_storage(direct_upload: true)
+ end
- context 'when uses accelerated file post' do
- before do
- upload_artifacts(file_upload, headers_with_token, true)
- end
+ before do
+ fog_connection.directories.get('artifacts').files.create(
+ key: 'tmp/upload/12312300',
+ body: 'content'
+ )
- it_behaves_like 'successful artifacts upload'
+ upload_artifacts(file_upload, headers_with_token,
+ { 'file.remote_id' => remote_id })
+ end
+
+ context 'when valid remote_id is used' do
+ let(:remote_id) { '12312300' }
+
+ it_behaves_like 'successful artifacts upload'
+ end
+
+ context 'when invalid remote_id is used' do
+ let(:remote_id) { 'invalid id' }
+
+ it 'responds with bad request' do
+ expect(response).to have_gitlab_http_status(500)
+ expect(json_response['message']).to eq("Missing file")
+ end
+ end
+ end
end
context 'when using runners token' do
@@ -1208,15 +1274,19 @@ describe API::Runner do
end
context 'when artifacts are being stored outside of tmp path' do
+ let(:new_tmpdir) { Dir.mktmpdir }
+
before do
+ # init before overwriting tmp dir
+ file_upload
+
# by configuring this path we allow to pass file from @tmpdir only
# but all temporary files are stored in system tmp directory
- @tmpdir = Dir.mktmpdir
- allow(JobArtifactUploader).to receive(:workhorse_upload_path).and_return(@tmpdir)
+ allow(Dir).to receive(:tmpdir).and_return(new_tmpdir)
end
after do
- FileUtils.remove_entry @tmpdir
+ FileUtils.remove_entry(new_tmpdir)
end
it' "fails to post artifacts for outside of tmp path"' do
@@ -1226,12 +1296,11 @@ describe API::Runner do
end
end
- def upload_artifacts(file, headers = {}, accelerated = true)
- params = if accelerated
- { 'file.path' => file.path, 'file.name' => file.original_filename }
- else
- { 'file' => file }
- end
+ def upload_artifacts(file, headers = {}, params = {})
+ params = params.merge({
+ 'file.path' => file.path,
+ 'file.name' => file.original_filename
+ })
post api("/jobs/#{job.id}/artifacts"), params, headers
end
diff --git a/spec/requests/lfs_http_spec.rb b/spec/requests/lfs_http_spec.rb
index 1e6bd993c08..f80abb06fca 100644
--- a/spec/requests/lfs_http_spec.rb
+++ b/spec/requests/lfs_http_spec.rb
@@ -1016,7 +1016,7 @@ describe 'Git LFS API and storage' do
it_behaves_like 'a valid response' do
it 'responds with status 200, location of lfs remote store and object details' do
- expect(json_response['TempPath']).to be_nil
+ expect(json_response['TempPath']).to eq(LfsObjectUploader.workhorse_local_upload_path)
expect(json_response['RemoteObject']).to have_key('ID')
expect(json_response['RemoteObject']).to have_key('GetURL')
expect(json_response['RemoteObject']).to have_key('StoreURL')
@@ -1073,7 +1073,9 @@ describe 'Git LFS API and storage' do
['123123', '../../123123'].each do |remote_id|
context "with invalid remote_id: #{remote_id}" do
subject do
- put_finalize_with_args('file.remote_id' => remote_id)
+ put_finalize(with_tempfile: true, args: {
+ 'file.remote_id' => remote_id
+ })
end
it 'responds with status 403' do
@@ -1093,9 +1095,10 @@ describe 'Git LFS API and storage' do
end
subject do
- put_finalize_with_args(
+ put_finalize(with_tempfile: true, args: {
'file.remote_id' => '12312300',
- 'file.name' => 'name')
+ 'file.name' => 'name'
+ })
end
it 'responds with status 200' do
@@ -1331,7 +1334,7 @@ describe 'Git LFS API and storage' do
put "#{project.http_url_to_repo}/gitlab-lfs/objects/#{sample_oid}/#{sample_size}/authorize", nil, authorize_headers
end
- def put_finalize(lfs_tmp = lfs_tmp_file, with_tempfile: false)
+ def put_finalize(lfs_tmp = lfs_tmp_file, with_tempfile: false, args: {})
upload_path = LfsObjectUploader.workhorse_local_upload_path
file_path = upload_path + '/' + lfs_tmp if lfs_tmp
@@ -1340,12 +1343,12 @@ describe 'Git LFS API and storage' do
FileUtils.touch(file_path)
end
- args = {
+ extra_args = {
'file.path' => file_path,
'file.name' => File.basename(file_path)
- }.compact
+ }
- put_finalize_with_args(args)
+ put_finalize_with_args(args.merge(extra_args).compact)
end
def put_finalize_with_args(args)
diff --git a/spec/serializers/build_serializer_spec.rb b/spec/serializers/build_serializer_spec.rb
index 9673b11c2a2..98cd15e248b 100644
--- a/spec/serializers/build_serializer_spec.rb
+++ b/spec/serializers/build_serializer_spec.rb
@@ -28,15 +28,31 @@ describe BuildSerializer do
end
describe '#represent_status' do
- context 'when represents only status' do
- let(:resource) { create(:ci_build) }
+ context 'for a failed build' do
+ let(:resource) { create(:ci_build, :failed) }
+ let(:status) { resource.detailed_status(double('user')) }
+
+ subject { serializer.represent_status(resource) }
+
+ it 'serializes only status' do
+ expect(subject[:text]).to eq(status.text)
+ expect(subject[:label]).to eq('failed')
+ expect(subject[:tooltip]).to eq('failed <br> (unknown failure)')
+ expect(subject[:icon]).to eq(status.icon)
+ expect(subject[:favicon]).to match_asset_path("/assets/ci_favicons/#{status.favicon}.ico")
+ end
+ end
+
+ context 'for any other type of build' do
+ let(:resource) { create(:ci_build, :success) }
let(:status) { resource.detailed_status(double('user')) }
subject { serializer.represent_status(resource) }
it 'serializes only status' do
expect(subject[:text]).to eq(status.text)
- expect(subject[:label]).to eq(status.label)
+ expect(subject[:label]).to eq('passed')
+ expect(subject[:tooltip]).to eq('passed')
expect(subject[:icon]).to eq(status.icon)
expect(subject[:favicon]).to match_asset_path("/assets/ci_favicons/#{status.favicon}.ico")
end
diff --git a/spec/serializers/job_entity_spec.rb b/spec/serializers/job_entity_spec.rb
index 026360e91a3..24a6f1a2a8a 100644
--- a/spec/serializers/job_entity_spec.rb
+++ b/spec/serializers/job_entity_spec.rb
@@ -38,7 +38,7 @@ describe JobEntity do
it 'contains details' do
expect(subject).to include :status
- expect(subject[:status]).to include :icon, :favicon, :text, :label
+ expect(subject[:status]).to include :icon, :favicon, :text, :label, :tooltip
end
context 'when job is retryable' do
@@ -126,7 +126,29 @@ describe JobEntity do
it 'contains details' do
expect(subject).to include :status
- expect(subject[:status]).to include :icon, :favicon, :text, :label
+ expect(subject[:status]).to include :icon, :favicon, :text, :label, :tooltip
+ end
+ end
+
+ context 'when job failed' do
+ let(:job) { create(:ci_build, :script_failure) }
+
+ describe 'status' do
+ it 'should contain the failure reason inside label' do
+ expect(subject[:status]).to include :icon, :favicon, :text, :label, :tooltip
+ expect(subject[:status][:label]).to eq('failed')
+ expect(subject[:status][:tooltip]).to eq('failed <br> (script failure)')
+ end
+ end
+ end
+
+ context 'when job passed' do
+ let(:job) { create(:ci_build, :success) }
+
+ describe 'status' do
+ it 'should not contain the failure reason inside label' do
+ expect(subject[:status][:label]).to eq('passed')
+ end
end
end
end
diff --git a/spec/serializers/pipeline_entity_spec.rb b/spec/serializers/pipeline_entity_spec.rb
index 248552d1858..2473c561f4b 100644
--- a/spec/serializers/pipeline_entity_spec.rb
+++ b/spec/serializers/pipeline_entity_spec.rb
@@ -30,7 +30,7 @@ describe PipelineEntity do
expect(subject).to include :details
expect(subject[:details])
.to include :duration, :finished_at
- expect(subject[:details][:status]).to include :icon, :favicon, :text, :label
+ expect(subject[:details][:status]).to include :icon, :favicon, :text, :label, :tooltip
end
it 'contains flags' do
diff --git a/spec/serializers/stage_entity_spec.rb b/spec/serializers/stage_entity_spec.rb
index 40e303f7b89..2034c7891ef 100644
--- a/spec/serializers/stage_entity_spec.rb
+++ b/spec/serializers/stage_entity_spec.rb
@@ -26,7 +26,7 @@ describe StageEntity do
end
it 'contains detailed status' do
- expect(subject[:status]).to include :text, :label, :group, :icon
+ expect(subject[:status]).to include :text, :label, :group, :icon, :tooltip
expect(subject[:status][:label]).to eq 'passed'
end
diff --git a/spec/serializers/status_entity_spec.rb b/spec/serializers/status_entity_spec.rb
index 70402bac2e2..559475e571c 100644
--- a/spec/serializers/status_entity_spec.rb
+++ b/spec/serializers/status_entity_spec.rb
@@ -16,7 +16,7 @@ describe StatusEntity do
subject { entity.as_json }
it 'contains status details' do
- expect(subject).to include :text, :icon, :favicon, :label, :group
+ expect(subject).to include :text, :icon, :favicon, :label, :group, :tooltip
expect(subject).to include :has_details, :details_path
expect(subject[:favicon]).to match_asset_path('/assets/ci_favicons/favicon_status_success.ico')
end
diff --git a/spec/services/ci/register_job_service_spec.rb b/spec/services/ci/register_job_service_spec.rb
index 97a563c1ce1..aa7cc268dd7 100644
--- a/spec/services/ci/register_job_service_spec.rb
+++ b/spec/services/ci/register_job_service_spec.rb
@@ -370,10 +370,89 @@ module Ci
it_behaves_like 'validation is not active'
end
end
+ end
- def execute(runner)
- described_class.new(runner).execute.build
+ describe '#register_success' do
+ let!(:current_time) { Time.new(2018, 4, 5, 14, 0, 0) }
+ let!(:attempt_counter) { double('Gitlab::Metrics::NullMetric') }
+ let!(:job_queue_duration_seconds) { double('Gitlab::Metrics::NullMetric') }
+
+ before do
+ allow(Time).to receive(:now).and_return(current_time)
+
+ # Stub defaults for any metrics other than the ones we're testing
+ allow(Gitlab::Metrics).to receive(:counter)
+ .with(any_args)
+ .and_return(Gitlab::Metrics::NullMetric.instance)
+ allow(Gitlab::Metrics).to receive(:histogram)
+ .with(any_args)
+ .and_return(Gitlab::Metrics::NullMetric.instance)
+
+ # Stub tested metrics
+ allow(Gitlab::Metrics).to receive(:counter)
+ .with(:job_register_attempts_total, anything)
+ .and_return(attempt_counter)
+ allow(Gitlab::Metrics).to receive(:histogram)
+ .with(:job_queue_duration_seconds, anything, anything, anything)
+ .and_return(job_queue_duration_seconds)
+
+ project.update(shared_runners_enabled: true)
+ pending_job.update(created_at: current_time - 3600, queued_at: current_time - 1800)
end
+
+ shared_examples 'metrics collector' do
+ it 'increments attempt counter' do
+ allow(job_queue_duration_seconds).to receive(:observe)
+ expect(attempt_counter).to receive(:increment)
+
+ execute(runner)
+ end
+
+ it 'counts job queuing time histogram with expected labels' do
+ allow(attempt_counter).to receive(:increment)
+ expect(job_queue_duration_seconds).to receive(:observe)
+ .with({ shared_runner: expected_shared_runner,
+ jobs_running_for_project: expected_jobs_running_for_project_first_job }, 1800)
+
+ execute(runner)
+ end
+
+ context 'when project already has running jobs' do
+ let!(:build2) { create( :ci_build, :running, pipeline: pipeline, runner: shared_runner) }
+ let!(:build3) { create( :ci_build, :running, pipeline: pipeline, runner: shared_runner) }
+
+ it 'counts job queuing time histogram with expected labels' do
+ allow(attempt_counter).to receive(:increment)
+ expect(job_queue_duration_seconds).to receive(:observe)
+ .with({ shared_runner: expected_shared_runner,
+ jobs_running_for_project: expected_jobs_running_for_project_third_job }, 1800)
+
+ execute(runner)
+ end
+ end
+ end
+
+ context 'when shared runner is used' do
+ let(:runner) { shared_runner }
+ let(:expected_shared_runner) { true }
+ let(:expected_jobs_running_for_project_first_job) { 0 }
+ let(:expected_jobs_running_for_project_third_job) { 2 }
+
+ it_behaves_like 'metrics collector'
+ end
+
+ context 'when specific runner is used' do
+ let(:runner) { specific_runner }
+ let(:expected_shared_runner) { false }
+ let(:expected_jobs_running_for_project_first_job) { '+Inf' }
+ let(:expected_jobs_running_for_project_third_job) { '+Inf' }
+
+ it_behaves_like 'metrics collector'
+ end
+ end
+
+ def execute(runner)
+ described_class.new(runner).execute.build
end
end
end
diff --git a/spec/services/projects/gitlab_projects_import_service_spec.rb b/spec/services/projects/gitlab_projects_import_service_spec.rb
index 6b8f9619bc4..880b2aae66a 100644
--- a/spec/services/projects/gitlab_projects_import_service_spec.rb
+++ b/spec/services/projects/gitlab_projects_import_service_spec.rb
@@ -2,8 +2,10 @@ require 'spec_helper'
describe Projects::GitlabProjectsImportService do
set(:namespace) { create(:namespace) }
+ let(:path) { 'test-path' }
let(:file) { fixture_file_upload(Rails.root + 'spec/fixtures/doc_sample.txt', 'text/plain') }
- subject { described_class.new(namespace.owner, { namespace_id: namespace.id, path: path, file: file }) }
+ let(:import_params) { { namespace_id: namespace.id, path: path, file: file } }
+ subject { described_class.new(namespace.owner, import_params) }
describe '#execute' do
context 'with an invalid path' do
@@ -18,8 +20,6 @@ describe Projects::GitlabProjectsImportService do
end
context 'with a valid path' do
- let(:path) { 'test-path' }
-
it 'creates a project' do
project = subject.execute
@@ -27,5 +27,15 @@ describe Projects::GitlabProjectsImportService do
expect(project).to be_valid
end
end
+
+ context 'override params' do
+ it 'stores them as import data when passed' do
+ project = described_class
+ .new(namespace.owner, import_params, description: 'Hello')
+ .execute
+
+ expect(project.import_data.data['override_params']['description']).to eq('Hello')
+ end
+ end
end
end
diff --git a/spec/support/reference_parser_helpers.rb b/spec/support/reference_parser_helpers.rb
index 01689194eac..5d5e80851e6 100644
--- a/spec/support/reference_parser_helpers.rb
+++ b/spec/support/reference_parser_helpers.rb
@@ -2,4 +2,34 @@ module ReferenceParserHelpers
def empty_html_link
Nokogiri::HTML.fragment('<a></a>').children[0]
end
+
+ shared_examples 'no N+1 queries' do
+ it 'avoids N+1 queries in #nodes_visible_to_user', :request_store do
+ record_queries = lambda do |links|
+ ActiveRecord::QueryRecorder.new do
+ described_class.new(project, user).nodes_visible_to_user(user, links)
+ end
+ end
+
+ control = record_queries.call(control_links)
+ actual = record_queries.call(actual_links)
+
+ expect(actual.count).to be <= control.count
+ expect(actual.cached_count).to be <= control.cached_count
+ end
+
+ it 'avoids N+1 queries in #records_for_nodes', :request_store do
+ record_queries = lambda do |links|
+ ActiveRecord::QueryRecorder.new do
+ described_class.new(project, user).records_for_nodes(links)
+ end
+ end
+
+ control = record_queries.call(control_links)
+ actual = record_queries.call(actual_links)
+
+ expect(actual.count).to be <= control.count
+ expect(actual.cached_count).to be <= control.cached_count
+ end
+ end
end
diff --git a/spec/support/stub_configuration.rb b/spec/support/stub_configuration.rb
index bad1d34df3a..a75a3eaefcb 100644
--- a/spec/support/stub_configuration.rb
+++ b/spec/support/stub_configuration.rb
@@ -45,6 +45,10 @@ module StubConfiguration
allow(Gitlab.config.lfs).to receive_messages(to_settings(messages))
end
+ def stub_artifacts_setting(messages)
+ allow(Gitlab.config.artifacts).to receive_messages(to_settings(messages))
+ end
+
def stub_storage_settings(messages)
messages.deep_stringify_keys!
diff --git a/spec/uploaders/object_storage_spec.rb b/spec/uploaders/object_storage_spec.rb
index 59e02fecbce..16455e2517b 100644
--- a/spec/uploaders/object_storage_spec.rb
+++ b/spec/uploaders/object_storage_spec.rb
@@ -62,10 +62,12 @@ describe ObjectStorage do
end
describe '#object_store' do
+ subject { uploader.object_store }
+
it "delegates to <mount>_store on model" do
expect(object).to receive(:file_store)
- uploader.object_store
+ subject
end
context 'when store is null' do
@@ -73,8 +75,36 @@ describe ObjectStorage do
expect(object).to receive(:file_store).and_return(nil)
end
- it "returns Store::LOCAL" do
- expect(uploader.object_store).to eq(described_class::Store::LOCAL)
+ context 'when object storage is enabled' do
+ context 'when direct uploads are enabled' do
+ before do
+ stub_uploads_object_storage(uploader_class, enabled: true, direct_upload: true)
+ end
+
+ it "uses Store::REMOTE" do
+ is_expected.to eq(described_class::Store::REMOTE)
+ end
+ end
+
+ context 'when direct uploads are disabled' do
+ before do
+ stub_uploads_object_storage(uploader_class, enabled: true, direct_upload: false)
+ end
+
+ it "uses Store::LOCAL" do
+ is_expected.to eq(described_class::Store::LOCAL)
+ end
+ end
+ end
+
+ context 'when object storage is disabled' do
+ before do
+ stub_uploads_object_storage(uploader_class, enabled: false)
+ end
+
+ it "uses Store::LOCAL" do
+ is_expected.to eq(described_class::Store::LOCAL)
+ end
end
end
@@ -84,7 +114,7 @@ describe ObjectStorage do
end
it "returns the given value" do
- expect(uploader.object_store).to eq(described_class::Store::REMOTE)
+ is_expected.to eq(described_class::Store::REMOTE)
end
end
end
@@ -486,108 +516,46 @@ describe ObjectStorage do
end
end
- describe '#store_workhorse_file!' do
+ describe '#cache!' do
subject do
- uploader.store_workhorse_file!(params, :file)
+ uploader.cache!(uploaded_file)
end
context 'when local file is used' do
context 'when valid file is used' do
- let(:target_path) do
- File.join(uploader_class.root, uploader_class::TMP_UPLOAD_PATH)
- end
-
- before do
- FileUtils.mkdir_p(target_path)
+ let(:uploaded_file) do
+ fixture_file_upload(Rails.root + 'spec/fixtures/rails_sample.jpg', 'image/jpg')
end
- context 'when no filename is specified' do
- let(:params) do
- { "file.path" => "test/file" }
- end
-
- it 'raises an error' do
- expect { subject }.to raise_error(uploader_class::RemoteStoreError, /Missing filename/)
- end
- end
-
- context 'when invalid file is specified' do
- let(:file_path) do
- File.join(target_path, "..", "test.file")
- end
-
- before do
- FileUtils.touch(file_path)
- end
-
- let(:params) do
- { "file.path" => file_path,
- "file.name" => "my_file.txt" }
- end
-
- it 'raises an error' do
- expect { subject }.to raise_error(uploader_class::RemoteStoreError, /Bad file path/)
- end
- end
-
- context 'when filename is specified' do
- let(:params) do
- { "file.path" => tmp_file,
- "file.name" => "my_file.txt" }
- end
-
- let(:tmp_file) { Tempfile.new('filename', target_path) }
+ it "properly caches the file" do
+ subject
- before do
- FileUtils.touch(tmp_file)
- end
-
- after do
- FileUtils.rm_f(tmp_file)
- end
-
- it 'succeeds' do
- expect { subject }.not_to raise_error
-
- expect(uploader).to be_exists
- end
-
- it 'proper path is being used' do
- subject
-
- expect(uploader.path).to start_with(uploader_class.root)
- expect(uploader.path).to end_with("my_file.txt")
- end
-
- it 'source file to not exist' do
- subject
-
- expect(File.exist?(tmp_file.path)).to be_falsey
- end
+ expect(uploader).to be_exists
+ expect(uploader.path).to start_with(uploader_class.root)
+ expect(uploader.filename).to eq('rails_sample.jpg')
end
end
end
context 'when remote file is used' do
+ let(:temp_file) { Tempfile.new("test") }
+
let!(:fog_connection) do
stub_uploads_object_storage(uploader_class)
end
- context 'when valid file is used' do
- context 'when no filename is specified' do
- let(:params) do
- { "file.remote_id" => "test/123123" }
- end
+ before do
+ FileUtils.touch(temp_file)
+ end
- it 'raises an error' do
- expect { subject }.to raise_error(uploader_class::RemoteStoreError, /Missing filename/)
- end
- end
+ after do
+ FileUtils.rm_f(temp_file)
+ end
+ context 'when valid file is used' do
context 'when invalid file is specified' do
- let(:params) do
- { "file.remote_id" => "../test/123123",
- "file.name" => "my_file.txt" }
+ let(:uploaded_file) do
+ UploadedFile.new(temp_file.path, remote_id: "../test/123123")
end
it 'raises an error' do
@@ -596,9 +564,8 @@ describe ObjectStorage do
end
context 'when non existing file is specified' do
- let(:params) do
- { "file.remote_id" => "test/12312300",
- "file.name" => "my_file.txt" }
+ let(:uploaded_file) do
+ UploadedFile.new(temp_file.path, remote_id: "test/123123")
end
it 'raises an error' do
@@ -606,10 +573,9 @@ describe ObjectStorage do
end
end
- context 'when filename is specified' do
- let(:params) do
- { "file.remote_id" => "test/123123",
- "file.name" => "my_file.txt" }
+ context 'when valid file is specified' do
+ let(:uploaded_file) do
+ UploadedFile.new(temp_file.path, filename: "my_file.txt", remote_id: "test/123123")
end
let!(:fog_file) do
@@ -619,36 +585,37 @@ describe ObjectStorage do
)
end
- it 'succeeds' do
+ it 'file to be cached and remote stored' do
expect { subject }.not_to raise_error
expect(uploader).to be_exists
- end
-
- it 'path to not be temporary' do
- subject
-
+ expect(uploader).to be_cached
expect(uploader.path).not_to be_nil
- expect(uploader.path).not_to include('tmp/upload')
- expect(uploader.url).to include('/my_file.txt')
+ expect(uploader.path).not_to include('tmp/cache')
+ expect(uploader.url).not_to be_nil
+ expect(uploader.path).not_to include('tmp/cache')
+ expect(uploader.object_store).to eq(described_class::Store::REMOTE)
end
- it 'url is used' do
- subject
+ context 'when file is stored' do
+ subject do
+ uploader.store!(uploaded_file)
+ end
- expect(uploader.url).not_to be_nil
- expect(uploader.url).to include('/my_file.txt')
+ it 'file to be remotely stored in permament location' do
+ subject
+
+ expect(uploader).to be_exists
+ expect(uploader).not_to be_cached
+ expect(uploader.path).not_to be_nil
+ expect(uploader.path).not_to include('tmp/upload')
+ expect(uploader.path).not_to include('tmp/cache')
+ expect(uploader.url).to include('/my_file.txt')
+ expect(uploader.object_store).to eq(described_class::Store::REMOTE)
+ end
end
end
end
end
-
- context 'when no file is used' do
- let(:params) { {} }
-
- it 'raises an error' do
- expect { subject }.to raise_error(uploader_class::RemoteStoreError, /Bad file/)
- end
- end
end
end
diff --git a/spec/views/projects/jobs/show.html.haml_spec.rb b/spec/views/projects/jobs/show.html.haml_spec.rb
index 6a67da79ec5..9e692159bd0 100644
--- a/spec/views/projects/jobs/show.html.haml_spec.rb
+++ b/spec/views/projects/jobs/show.html.haml_spec.rb
@@ -1,8 +1,10 @@
require 'spec_helper'
describe 'projects/jobs/show' do
+ let(:user) { create(:user) }
let(:project) { create(:project, :repository) }
let(:build) { create(:ci_build, pipeline: pipeline) }
+ let(:builds) { project.builds.present(current_user: user) }
let(:pipeline) do
create(:ci_pipeline, project: project, sha: project.commit.id)
@@ -11,6 +13,7 @@ describe 'projects/jobs/show' do
before do
assign(:build, build.present)
assign(:project, project)
+ assign(:builds, builds)
allow(view).to receive(:can?).and_return(true)
end
diff --git a/spec/views/projects/pipelines_settings/_show.html.haml_spec.rb b/spec/views/projects/settings/ci_cd/_form.html.haml_spec.rb
index 7b300150874..be9a4d9c57c 100644
--- a/spec/views/projects/pipelines_settings/_show.html.haml_spec.rb
+++ b/spec/views/projects/settings/ci_cd/_form.html.haml_spec.rb
@@ -1,6 +1,6 @@
require 'spec_helper'
-describe 'projects/pipelines_settings/_show' do
+describe 'projects/settings/ci_cd/_form' do
let(:project) { create(:project, :repository) }
before do