diff options
Diffstat (limited to 'spec')
94 files changed, 1349 insertions, 496 deletions
diff --git a/spec/controllers/admin/clusters_controller_spec.rb b/spec/controllers/admin/clusters_controller_spec.rb index 7b77cb186a4..7709f525119 100644 --- a/spec/controllers/admin/clusters_controller_spec.rb +++ b/spec/controllers/admin/clusters_controller_spec.rb @@ -396,6 +396,7 @@ describe Admin::ClustersController do cluster: { enabled: false, name: 'my-new-cluster-name', + managed: false, base_domain: domain } } @@ -409,6 +410,7 @@ describe Admin::ClustersController do expect(flash[:notice]).to eq('Kubernetes cluster was successfully updated.') expect(cluster.enabled).to be_falsey expect(cluster.name).to eq('my-new-cluster-name') + expect(cluster).not_to be_managed expect(cluster.domain).to eq('test-domain.com') end @@ -433,6 +435,7 @@ describe Admin::ClustersController do cluster: { enabled: false, name: 'my-new-cluster-name', + managed: false, domain: domain } } @@ -445,6 +448,7 @@ describe Admin::ClustersController do expect(response).to have_http_status(:no_content) expect(cluster.enabled).to be_falsey expect(cluster.name).to eq('my-new-cluster-name') + expect(cluster).not_to be_managed end end diff --git a/spec/controllers/concerns/send_file_upload_spec.rb b/spec/controllers/concerns/send_file_upload_spec.rb index a3ce08f736c..3bf0ec799c7 100644 --- a/spec/controllers/concerns/send_file_upload_spec.rb +++ b/spec/controllers/concerns/send_file_upload_spec.rb @@ -115,7 +115,7 @@ describe SendFileUpload do it 'sends a file with a custom type' do headers = double - expected_headers = /response-content-disposition=attachment%3B%20filename%3D%22test.js%22%3B%20filename%2A%3DUTF-8%27%27test.js&response-content-type=application%2Fecmascript/ + expected_headers = /response-content-disposition=attachment%3B%20filename%3D%22test.js%22%3B%20filename%2A%3DUTF-8%27%27test.js&response-content-type=application%2Fjavascript/ expect(Gitlab::Workhorse).to receive(:send_url).with(expected_headers).and_call_original expect(headers).to receive(:store).with(Gitlab::Workhorse::SEND_DATA_HEADER, /^send-url:/) diff --git a/spec/controllers/groups/clusters_controller_spec.rb b/spec/controllers/groups/clusters_controller_spec.rb index 7349cb7094c..2f64c7f3460 100644 --- a/spec/controllers/groups/clusters_controller_spec.rb +++ b/spec/controllers/groups/clusters_controller_spec.rb @@ -463,6 +463,7 @@ describe Groups::ClustersController do cluster: { enabled: false, name: 'my-new-cluster-name', + managed: false, base_domain: domain } } @@ -476,6 +477,7 @@ describe Groups::ClustersController do expect(flash[:notice]).to eq('Kubernetes cluster was successfully updated.') expect(cluster.enabled).to be_falsey expect(cluster.name).to eq('my-new-cluster-name') + expect(cluster).not_to be_managed expect(cluster.domain).to eq('test-domain.com') end @@ -500,6 +502,7 @@ describe Groups::ClustersController do cluster: { enabled: false, name: 'my-new-cluster-name', + managed: false, domain: domain } } @@ -512,6 +515,7 @@ describe Groups::ClustersController do expect(response).to have_http_status(:no_content) expect(cluster.enabled).to be_falsey expect(cluster.name).to eq('my-new-cluster-name') + expect(cluster).not_to be_managed end end diff --git a/spec/controllers/projects/branches_controller_spec.rb b/spec/controllers/projects/branches_controller_spec.rb index c778b7888dc..cf201c9f735 100644 --- a/spec/controllers/projects/branches_controller_spec.rb +++ b/spec/controllers/projects/branches_controller_spec.rb @@ -123,7 +123,11 @@ describe Projects::BranchesController do expect(response).to redirect_to project_tree_path(project, branch) end - shared_examples 'same behavior between KubernetesService and Platform::Kubernetes' do + context 'when user configured kubernetes from CI/CD > Clusters' do + before do + create(:cluster, :provided_by_gcp, projects: [project]) + end + it 'redirects to autodeploy setup page' do result = { status: :success, branch: double(name: branch) } @@ -143,22 +147,6 @@ describe Projects::BranchesController do end end - context 'when user configured kubernetes from Integration > Kubernetes' do - before do - project.services << build(:kubernetes_service) - end - - it_behaves_like 'same behavior between KubernetesService and Platform::Kubernetes' - end - - context 'when user configured kubernetes from CI/CD > Clusters' do - before do - create(:cluster, :provided_by_gcp, projects: [project]) - end - - it_behaves_like 'same behavior between KubernetesService and Platform::Kubernetes' - end - it 'redirects to autodeploy setup page' do result = { status: :success, branch: double(name: branch) } diff --git a/spec/controllers/projects/clusters_controller_spec.rb b/spec/controllers/projects/clusters_controller_spec.rb index 8d37bd82d21..fa49438287f 100644 --- a/spec/controllers/projects/clusters_controller_spec.rb +++ b/spec/controllers/projects/clusters_controller_spec.rb @@ -449,6 +449,7 @@ describe Projects::ClustersController do cluster: { enabled: false, name: 'my-new-cluster-name', + managed: false, platform_kubernetes_attributes: { namespace: 'my-namespace' } @@ -464,6 +465,7 @@ describe Projects::ClustersController do expect(flash[:notice]).to eq('Kubernetes cluster was successfully updated.') expect(cluster.enabled).to be_falsey expect(cluster.name).to eq('my-new-cluster-name') + expect(cluster).not_to be_managed expect(cluster.platform_kubernetes.namespace).to eq('my-namespace') end @@ -475,6 +477,7 @@ describe Projects::ClustersController do cluster: { enabled: false, name: 'my-new-cluster-name', + managed: false, platform_kubernetes_attributes: { namespace: 'my-namespace' } @@ -489,6 +492,7 @@ describe Projects::ClustersController do expect(response).to have_http_status(:no_content) expect(cluster.enabled).to be_falsey expect(cluster.name).to eq('my-new-cluster-name') + expect(cluster).not_to be_managed expect(cluster.platform_kubernetes.namespace).to eq('my-namespace') end end diff --git a/spec/controllers/projects/jobs_controller_spec.rb b/spec/controllers/projects/jobs_controller_spec.rb index 490e9841492..0dabe27977a 100644 --- a/spec/controllers/projects/jobs_controller_spec.rb +++ b/spec/controllers/projects/jobs_controller_spec.rb @@ -540,7 +540,7 @@ describe Projects::JobsController, :clean_gitlab_redis_shared_state do expect(response).to have_gitlab_http_status(:ok) expect(json_response['id']).to eq job.id expect(json_response['status']).to eq job.status - expect(json_response['html']).to eq('BUILD TRACE') + expect(json_response['html']).to eq('<span class="">BUILD TRACE</span>') end end diff --git a/spec/controllers/projects/services_controller_spec.rb b/spec/controllers/projects/services_controller_spec.rb index 3608d175d50..5c7f8d95f82 100644 --- a/spec/controllers/projects/services_controller_spec.rb +++ b/spec/controllers/projects/services_controller_spec.rb @@ -141,20 +141,6 @@ describe Projects::ServicesController do end end - context 'with a deprecated service' do - let(:service) { create(:kubernetes_service, project: project) } - - before do - put :update, - params: { namespace_id: project.namespace, project_id: project, id: service.to_param, service: { namespace: 'updated_namespace' } } - end - - it 'does not update the service' do - service.reload - expect(service.namespace).not_to eq('updated_namespace') - end - end - context 'when activating JIRA service from a template' do let(:template_service) { create(:jira_service, project: project, template: true) } @@ -168,20 +154,10 @@ describe Projects::ServicesController do describe "GET #edit" do before do - get :edit, params: { namespace_id: project.namespace, project_id: project, id: service_id } + get :edit, params: { namespace_id: project.namespace, project_id: project, id: 'jira' } end context 'with approved services' do - let(:service_id) { 'jira' } - - it 'renders edit page' do - expect(response).to be_success - end - end - - context 'with a deprecated service' do - let(:service_id) { 'kubernetes' } - it 'renders edit page' do expect(response).to be_success end diff --git a/spec/db/schema_spec.rb b/spec/db/schema_spec.rb index 25616812814..6cfec5f4017 100644 --- a/spec/db/schema_spec.rb +++ b/spec/db/schema_spec.rb @@ -39,6 +39,7 @@ describe 'Database schema' do geo_upload_deleted_events: %w[upload_id model_id], identities: %w[user_id], issues: %w[last_edited_by_id state_id], + jira_tracker_data: %w[jira_issue_transition_id], keys: %w[user_id], label_links: %w[target_id], lfs_objects_projects: %w[lfs_object_id project_id], diff --git a/spec/factories/ci/builds.rb b/spec/factories/ci/builds.rb index a473136b57b..5f7c75a3a92 100644 --- a/spec/factories/ci/builds.rb +++ b/spec/factories/ci/builds.rb @@ -228,6 +228,26 @@ FactoryBot.define do end end + trait :trace_with_duplicate_sections do + after(:create) do |build, evaluator| + trace = File.binread( + File.expand_path( + Rails.root.join('spec/fixtures/trace/trace_with_duplicate_sections'))) + + build.trace.set(trace) + end + end + + trait :trace_with_sections do + after(:create) do |build, evaluator| + trace = File.binread( + File.expand_path( + Rails.root.join('spec/fixtures/trace/trace_with_sections'))) + + build.trace.set(trace) + end + end + trait :unicode_trace_live do after(:create) do |build, evaluator| trace = File.binread( diff --git a/spec/factories/clusters/kubernetes_namespaces.rb b/spec/factories/clusters/kubernetes_namespaces.rb index 3b50a57433f..042be7b4c4a 100644 --- a/spec/factories/clusters/kubernetes_namespaces.rb +++ b/spec/factories/clusters/kubernetes_namespaces.rb @@ -16,5 +16,9 @@ FactoryBot.define do trait :with_token do service_account_token { FFaker::Lorem.characters(10) } end + + trait :without_token do + service_account_token nil + end end end diff --git a/spec/factories/lfs_objects_projects.rb b/spec/factories/lfs_objects_projects.rb index c225387a5de..4804d0bb884 100644 --- a/spec/factories/lfs_objects_projects.rb +++ b/spec/factories/lfs_objects_projects.rb @@ -2,5 +2,6 @@ FactoryBot.define do factory :lfs_objects_project do lfs_object project + repository_type :project end end diff --git a/spec/factories/services.rb b/spec/factories/services.rb index 0d8c26a2ee9..763909f30bd 100644 --- a/spec/factories/services.rb +++ b/spec/factories/services.rb @@ -24,6 +24,8 @@ FactoryBot.define do api_url: 'https://kubernetes.example.com', token: 'a' * 40 }) + + skip_deprecation_validation true end factory :mock_deployment_service do diff --git a/spec/factories/services_data.rb b/spec/factories/services_data.rb new file mode 100644 index 00000000000..387e130a743 --- /dev/null +++ b/spec/factories/services_data.rb @@ -0,0 +1,18 @@ +# frozen_string_literal: true + +FactoryBot.define do + factory :jira_tracker_data do + service + url 'http://jira.example.com' + api_url 'http://api-jira.example.com' + username 'jira_username' + password 'jira_password' + end + + factory :issue_tracker_data do + service + project_url 'http://issuetracker.example.com' + issues_url 'http://issues.example.com' + new_issue_url 'http://new-issue.example.com' + end +end diff --git a/spec/factories/users.rb b/spec/factories/users.rb index 1d2b724a5e5..4f3392cdcbf 100644 --- a/spec/factories/users.rb +++ b/spec/factories/users.rb @@ -66,6 +66,16 @@ FactoryBot.define do end end + transient do + developer_projects [] + end + + after(:create) do |user, evaluator| + evaluator.developer_projects.each do |project| + project.add_developer(user) + end + end + factory :omniauth_user do transient do extern_uid '123456' diff --git a/spec/features/commits_spec.rb b/spec/features/commits_spec.rb index e6f44aa7d20..a2dd34e7f7c 100644 --- a/spec/features/commits_spec.rb +++ b/spec/features/commits_spec.rb @@ -10,8 +10,7 @@ describe 'Commits' do stub_ci_pipeline_to_return_yaml_file end - let(:creator) { create(:user) } - + let(:creator) { create(:user, developer_projects: [project]) } let!(:pipeline) do create(:ci_pipeline, project: project, @@ -77,10 +76,11 @@ describe 'Commits' do describe 'Commit builds', :js do before do + project.add_developer(user) visit pipeline_path(pipeline) end - it 'shows pipeline`s data' do + it 'shows pipeline data' do expect(page).to have_content pipeline.sha[0..7] expect(page).to have_content pipeline.git_commit_message.gsub!(/\s+/, ' ') expect(page).to have_content pipeline.user.name diff --git a/spec/features/cycle_analytics_spec.rb b/spec/features/cycle_analytics_spec.rb index 48edc764a8e..4108a0f370d 100644 --- a/spec/features/cycle_analytics_spec.rb +++ b/spec/features/cycle_analytics_spec.rb @@ -58,7 +58,7 @@ describe 'Cycle Analytics', :js do expect_issue_to_be_present click_stage('Plan') - expect(find('.stage-events')).to have_content(mr.commits.last.title) + expect_issue_to_be_present click_stage('Code') expect_merge_request_to_be_present diff --git a/spec/features/merge_request/user_merges_when_pipeline_succeeds_spec.rb b/spec/features/merge_request/user_merges_when_pipeline_succeeds_spec.rb index 586b3ba170d..85c4d778fd0 100644 --- a/spec/features/merge_request/user_merges_when_pipeline_succeeds_spec.rb +++ b/spec/features/merge_request/user_merges_when_pipeline_succeeds_spec.rb @@ -52,7 +52,7 @@ describe 'Merge request > User merges when pipeline succeeds', :js do # so we have to wait for asynchronous call to reload it # and have_content expectation handles that. # - expect(page).to have_content "Pipeline ##{pipeline.id} (##{pipeline.iid}) running" + expect(page).to have_content "Pipeline ##{pipeline.id} running" end it_behaves_like 'Merge when pipeline succeeds activator' diff --git a/spec/features/merge_request/user_sees_merge_widget_spec.rb b/spec/features/merge_request/user_sees_merge_widget_spec.rb index 393077a916f..733e8aa3eba 100644 --- a/spec/features/merge_request/user_sees_merge_widget_spec.rb +++ b/spec/features/merge_request/user_sees_merge_widget_spec.rb @@ -160,7 +160,7 @@ describe 'Merge request > User sees merge widget', :js do it 'shows head pipeline information' do within '.ci-widget-content' do - expect(page).to have_content("Pipeline ##{pipeline.id} (##{pipeline.iid}) pending " \ + expect(page).to have_content("Pipeline ##{pipeline.id} pending " \ "for #{pipeline.short_sha} " \ "on #{pipeline.ref}") end @@ -189,7 +189,7 @@ describe 'Merge request > User sees merge widget', :js do it 'shows head pipeline information' do within '.ci-widget-content' do - expect(page).to have_content("Pipeline ##{pipeline.id} (##{pipeline.iid}) pending " \ + expect(page).to have_content("Pipeline ##{pipeline.id} pending " \ "for #{pipeline.short_sha} " \ "on #{merge_request.to_reference} " \ "with #{merge_request.source_branch}") @@ -201,7 +201,7 @@ describe 'Merge request > User sees merge widget', :js do it 'shows head pipeline information' do within '.ci-widget-content' do - expect(page).to have_content("Pipeline ##{pipeline.id} (##{pipeline.iid}) pending " \ + expect(page).to have_content("Pipeline ##{pipeline.id} pending " \ "for #{pipeline.short_sha} " \ "on #{merge_request.to_reference} " \ "with #{merge_request.source_branch}") @@ -234,7 +234,7 @@ describe 'Merge request > User sees merge widget', :js do it 'shows head pipeline information' do within '.ci-widget-content' do - expect(page).to have_content("Pipeline ##{pipeline.id} (##{pipeline.iid}) pending " \ + expect(page).to have_content("Pipeline ##{pipeline.id} pending " \ "for #{pipeline.short_sha} " \ "on #{merge_request.to_reference} " \ "with #{merge_request.source_branch} " \ @@ -248,7 +248,7 @@ describe 'Merge request > User sees merge widget', :js do it 'shows head pipeline information' do within '.ci-widget-content' do - expect(page).to have_content("Pipeline ##{pipeline.id} (##{pipeline.iid}) pending " \ + expect(page).to have_content("Pipeline ##{pipeline.id} pending " \ "for #{pipeline.short_sha} " \ "on #{merge_request.to_reference} " \ "with #{merge_request.source_branch} " \ diff --git a/spec/features/projects/clusters/interchangeability_spec.rb b/spec/features/projects/clusters/interchangeability_spec.rb deleted file mode 100644 index 0033e12b6b1..00000000000 --- a/spec/features/projects/clusters/interchangeability_spec.rb +++ /dev/null @@ -1,16 +0,0 @@ -require 'spec_helper' - -describe 'Interchangeability between KubernetesService and Platform::Kubernetes' do - EXCEPT_METHODS = %i[test title description help fields initialize_properties namespace namespace= api_url api_url= deprecated? deprecation_message].freeze - EXCEPT_METHODS_GREP_V = %w[_touched? _changed? _was].freeze - - it 'Clusters::Platform::Kubernetes covers core interfaces in KubernetesService' do - expected_interfaces = KubernetesService.instance_methods(false) - expected_interfaces = expected_interfaces - EXCEPT_METHODS - EXCEPT_METHODS_GREP_V.each do |g| - expected_interfaces = expected_interfaces.grep_v(/#{Regexp.escape(g)}\z/) - end - - expect(expected_interfaces - Clusters::Platforms::Kubernetes.instance_methods).to be_empty - end -end diff --git a/spec/features/projects/commits/user_browses_commits_spec.rb b/spec/features/projects/commits/user_browses_commits_spec.rb index a84fee34669..fc74a370e72 100644 --- a/spec/features/projects/commits/user_browses_commits_spec.rb +++ b/spec/features/projects/commits/user_browses_commits_spec.rb @@ -61,7 +61,7 @@ describe 'User browses commits' do it 'renders commit ci info' do visit project_commit_path(project, sample_commit.id) - expect(page).to have_content "Pipeline ##{pipeline.id} (##{pipeline.iid}) pending" + expect(page).to have_content "Pipeline ##{pipeline.id} pending" end end diff --git a/spec/features/projects/environments/environment_spec.rb b/spec/features/projects/environments/environment_spec.rb index da4ef6428d4..fbaf12be64e 100644 --- a/spec/features/projects/environments/environment_spec.rb +++ b/spec/features/projects/environments/environment_spec.rb @@ -155,7 +155,10 @@ describe 'Environment' do end context 'with terminal' do - shared_examples 'same behavior between KubernetesService and Platform::Kubernetes' do + context 'when user configured kubernetes from CI/CD > Clusters' do + let!(:cluster) { create(:cluster, :project, :provided_by_gcp) } + let(:project) { cluster.project } + context 'for project maintainer' do let(:role) { :maintainer } @@ -191,19 +194,6 @@ describe 'Environment' do end end end - - context 'when user configured kubernetes from Integration > Kubernetes' do - let(:project) { create(:kubernetes_project, :test_repo) } - - it_behaves_like 'same behavior between KubernetesService and Platform::Kubernetes' - end - - context 'when user configured kubernetes from CI/CD > Clusters' do - let!(:cluster) { create(:cluster, :project, :provided_by_gcp) } - let(:project) { cluster.project } - - it_behaves_like 'same behavior between KubernetesService and Platform::Kubernetes' - end end context 'when environment is available' do diff --git a/spec/features/projects/environments/environments_spec.rb b/spec/features/projects/environments/environments_spec.rb index 7b7e45312d9..1b5d9083932 100644 --- a/spec/features/projects/environments/environments_spec.rb +++ b/spec/features/projects/environments/environments_spec.rb @@ -248,7 +248,10 @@ describe 'Environments page', :js do end context 'when kubernetes terminal is available' do - shared_examples 'same behavior between KubernetesService and Platform::Kubernetes' do + context 'when user configured kubernetes from CI/CD > Clusters' do + let(:cluster) { create(:cluster, :provided_by_gcp, projects: [create(:project, :repository)]) } + let(:project) { cluster.project } + context 'for project maintainer' do let(:role) { :maintainer } @@ -265,19 +268,6 @@ describe 'Environments page', :js do end end end - - context 'when user configured kubernetes from Integration > Kubernetes' do - let(:project) { create(:kubernetes_project, :test_repo) } - - it_behaves_like 'same behavior between KubernetesService and Platform::Kubernetes' - end - - context 'when user configured kubernetes from CI/CD > Clusters' do - let(:cluster) { create(:cluster, :provided_by_gcp, projects: [create(:project, :repository)]) } - let(:project) { cluster.project } - - it_behaves_like 'same behavior between KubernetesService and Platform::Kubernetes' - end end end diff --git a/spec/features/projects/jobs/user_browses_job_spec.rb b/spec/features/projects/jobs/user_browses_job_spec.rb index 54b462da87a..fbe765d4c44 100644 --- a/spec/features/projects/jobs/user_browses_job_spec.rb +++ b/spec/features/projects/jobs/user_browses_job_spec.rb @@ -34,6 +34,52 @@ describe 'User browses a job', :js do expect(page).to have_content('Job has been erased') end + shared_examples 'has collapsible sections' do + it 'collapses the section clicked' do + wait_for_requests + text_to_hide = "Cloning into '/nolith/ci-tests'" + text_to_show = 'Waiting for pod' + + expect(page).to have_content(text_to_hide) + expect(page).to have_content(text_to_show) + + first('.js-section-start[data-section="get-sources"]').click + + expect(page).not_to have_content(text_to_hide) + expect(page).to have_content(text_to_show) + end + end + + context 'when job trace contains sections' do + let!(:build) { create(:ci_build, :success, :trace_with_sections, :coverage, pipeline: pipeline) } + + it_behaves_like 'has collapsible sections' + end + + context 'when job trace contains duplicate sections' do + let!(:build) { create(:ci_build, :success, :trace_with_duplicate_sections, :coverage, pipeline: pipeline) } + + it_behaves_like 'has collapsible sections' + end + + context 'when job trace contains sections' do + let!(:build) { create(:ci_build, :success, :trace_with_duplicate_sections, :coverage, pipeline: pipeline) } + + it 'collapses a section' do + wait_for_requests + text_to_hide = "Cloning into '/nolith/ci-tests'" + text_to_show = 'Waiting for pod' + + expect(page).to have_content(text_to_hide) + expect(page).to have_content(text_to_show) + + first('.js-section-start[data-section="get-sources"]').click + + expect(page).not_to have_content(text_to_hide) + expect(page).to have_content(text_to_show) + end + end + context 'with a failed job' do let!(:build) { create(:ci_build, :failed, :trace_artifact, pipeline: pipeline) } diff --git a/spec/features/projects/jobs/user_browses_jobs_spec.rb b/spec/features/projects/jobs/user_browses_jobs_spec.rb index bd6c73f4b85..ebc20d15d67 100644 --- a/spec/features/projects/jobs/user_browses_jobs_spec.rb +++ b/spec/features/projects/jobs/user_browses_jobs_spec.rb @@ -16,12 +16,6 @@ describe 'User browses jobs' do visit(project_jobs_path(project)) end - it 'shows pipeline id and IID' do - page.within('td.pipeline-link') do - expect(page).to have_content("##{pipeline.id} (##{pipeline.iid})") - end - end - it 'shows the coverage' do page.within('td.coverage') do expect(page).to have_content('99.9%') diff --git a/spec/features/projects/jobs_spec.rb b/spec/features/projects/jobs_spec.rb index 03562bd382e..f4ed89adc0f 100644 --- a/spec/features/projects/jobs_spec.rb +++ b/spec/features/projects/jobs_spec.rb @@ -129,7 +129,7 @@ describe 'Jobs', :clean_gitlab_redis_shared_state do visit project_job_path(project, job) within '.js-pipeline-info' do - expect(page).to have_content("Pipeline ##{pipeline.id} (##{pipeline.iid}) for #{pipeline.ref}") + expect(page).to have_content("Pipeline ##{pipeline.id} for #{pipeline.ref}") end end diff --git a/spec/features/projects/pipelines/pipeline_spec.rb b/spec/features/projects/pipelines/pipeline_spec.rb index 77f0f237d0a..9759fd04ad2 100644 --- a/spec/features/projects/pipelines/pipeline_spec.rb +++ b/spec/features/projects/pipelines/pipeline_spec.rb @@ -5,6 +5,7 @@ require 'spec_helper' describe 'Pipeline', :js do include RoutesHelpers include ProjectForksHelper + include ::ExclusiveLeaseHelpers let(:project) { create(:project) } let(:user) { create(:user) } @@ -539,6 +540,44 @@ describe 'Pipeline', :js do expect(page).to have_selector('.pipeline-visualization') expect(page).to have_content('cross-build') end + + context 'when a scheduled pipeline is created by a blocked user' do + let(:project) { create(:project, :repository) } + + let(:schedule) do + create(:ci_pipeline_schedule, + project: project, + owner: project.owner, + description: 'blocked user schedule' + ).tap do |schedule| + schedule.update_column(:next_run_at, 1.minute.ago) + end + end + + before do + schedule.owner.block! + + begin + PipelineScheduleWorker.new.perform + rescue Ci::CreatePipelineService::CreateError + # Do nothing, assert view code after the Pipeline failed to create. + end + end + + it 'displays the PipelineSchedule in an active state' do + visit project_pipeline_schedules_path(project) + page.click_link('Active') + + expect(page).to have_selector('table.ci-table > tbody > tr > td', text: 'blocked user schedule') + end + + it 'does not create a new Pipeline' do + visit project_pipelines_path(project) + + expect(page).not_to have_selector('.ci-table') + expect(schedule.last_pipeline).to be_nil + end + end end describe 'GET /:project/pipelines/:id/builds' do diff --git a/spec/features/projects/pipelines/pipelines_spec.rb b/spec/features/projects/pipelines/pipelines_spec.rb index de780f13681..885d5f85989 100644 --- a/spec/features/projects/pipelines/pipelines_spec.rb +++ b/spec/features/projects/pipelines/pipelines_spec.rb @@ -469,7 +469,7 @@ describe 'Pipelines', :js do visit_project_pipelines end - it 'has artifats' do + it 'has artifacts' do expect(page).to have_selector('.build-artifacts') end diff --git a/spec/finders/fork_projects_finder_spec.rb b/spec/finders/fork_projects_finder_spec.rb index b3fdffc3331..98cff37205e 100644 --- a/spec/finders/fork_projects_finder_spec.rb +++ b/spec/finders/fork_projects_finder_spec.rb @@ -12,6 +12,8 @@ describe ForkProjectsFinder do let(:private_fork_member) { create(:user) } before do + stub_feature_flags(object_pools: { enabled: false, thing: source_project }) + private_fork.update!(visibility_level: Gitlab::VisibilityLevel::PRIVATE) private_fork.add_developer(private_fork_member) diff --git a/spec/finders/pipelines_finder_spec.rb b/spec/finders/pipelines_finder_spec.rb index c2c304589c9..b23fd8ccdc6 100644 --- a/spec/finders/pipelines_finder_spec.rb +++ b/spec/finders/pipelines_finder_spec.rb @@ -170,8 +170,9 @@ describe PipelinesFinder do context 'when order_by and sort are specified' do context 'when order_by user_id' do - let(:params) { { order_by: 'user_id', sort: 'asc' } } - let!(:pipelines) { Array.new(2) { create(:ci_pipeline, project: project, user: create(:user)) } } + let(:params) { { order_by: 'user_id', sort: 'asc' } } + let(:users) { Array.new(2) { create(:user, developer_projects: [project]) } } + let!(:pipelines) { users.map { |user| create(:ci_pipeline, project: project, user: user) } } it 'sorts as user_id: :asc' do is_expected.to match_array(pipelines) diff --git a/spec/fixtures/trace/trace_with_duplicate_sections b/spec/fixtures/trace/trace_with_duplicate_sections new file mode 100644 index 00000000000..7a894e80e38 --- /dev/null +++ b/spec/fixtures/trace/trace_with_duplicate_sections @@ -0,0 +1,30 @@ +[0KRunning with gitlab-runner dev (HEAD) + on kitsune minikube (a21b584f) +[0;m[0;33mWARNING: Namespace is empty, therefore assuming 'default'. +[0;m[0KUsing Kubernetes namespace: default +[0;m[0KUsing Kubernetes executor with image alpine:3.4 ... +[0;msection_start:1506004954:prepare_script
[0KWaiting for pod default/runner-a21b584f-project-1208199-concurrent-0sg03f to be running, status is Pending +Running on runner-a21b584f-project-1208199-concurrent-0sg03f via kitsune.local... +section_end:1506004957:prepare_script
[0Ksection_start:1506004957:get_sources
[0K[32;1mCloning repository...[0;m +Cloning into '/nolith/ci-tests'... +[32;1mChecking out dddd7a6e as master...[0;m +[32;1mSkipping Git submodules setup[0;m +section_end:1506004958:get_sources
[0Ksection_start:1506004958:restore_cache
[0Ksection_end:1506004958:restore_cache
[0Ksection_start:1506004958:download_artifacts
[0Ksection_end:1506004958:download_artifacts
[0Ksection_start:1506004958:build_script
[0K[32;1m$ whoami[0;m +root +section_end:1506004959:build_script
[0Ksection_start:1506004959:after_script
[0Ksection_end:1506004959:after_script
[0Ksection_start:1506004959:archive_cache
[0Ksection_end:1506004959:archive_cache
[0Ksection_start:1506004959:upload_artifacts
[0Ksection_end:1506004959:upload_artifacts
[0K[32;1mJob succeeded +[0;m +[0KRunning with gitlab-runner dev (HEAD) + on kitsune minikube (a21b584f) +[0;m[0;33mWARNING: Namespace is empty, therefore assuming 'default'. +[0;m[0KUsing Kubernetes namespace: default +[0;m[0KUsing Kubernetes executor with image alpine:3.4 ... +[0;msection_start:1506004954:prepare_script
[0KWaiting for pod default/runner-a21b584f-project-1208199-concurrent-0sg03f to be running, status is Pending +Running on runner-a21b584f-project-1208199-concurrent-0sg03f via kitsune.local... +section_end:1506004957:prepare_script
[0Ksection_start:1506004957:get_sources
[0K[32;1mCloning repository...[0;m +Cloning into '/nolith/ci-tests'... +[32;1mChecking out dddd7a6e as master...[0;m +[32;1mSkipping Git submodules setup[0;m +section_end:1506004958:get_sources
[0Ksection_start:1506004958:restore_cache
[0Ksection_end:1506004958:restore_cache
[0Ksection_start:1506004958:download_artifacts
[0Ksection_end:1506004958:download_artifacts
[0Ksection_start:1506004958:build_script
[0K[32;1m$ whoami[0;m +root +section_end:1506004959:build_script
[0Ksection_start:1506004959:after_script
[0Ksection_end:1506004959:after_script
[0Ksection_start:1506004959:archive_cache
[0Ksection_end:1506004959:archive_cache
[0Ksection_start:1506004959:upload_artifacts
[0Ksection_end:1506004959:upload_artifacts
[0K[32;1mJob succeeded +[0;m diff --git a/spec/javascripts/fixtures/pipelines.rb b/spec/javascripts/fixtures/pipelines.rb index de6fcfe10f4..6b6b0eefab9 100644 --- a/spec/javascripts/fixtures/pipelines.rb +++ b/spec/javascripts/fixtures/pipelines.rb @@ -8,7 +8,7 @@ describe Projects::PipelinesController, '(JavaScript fixtures)', type: :controll let(:project) { create(:project, :repository, namespace: namespace, path: 'pipelines-project') } let(:commit) { create(:commit, project: project) } let(:commit_without_author) { RepoHelpers.another_sample_commit } - let!(:user) { create(:user, email: commit.author_email) } + let!(:user) { create(:user, developer_projects: [project], email: commit.author_email) } let!(:pipeline) { create(:ci_pipeline, project: project, sha: commit.id, user: user) } let!(:pipeline_without_author) { create(:ci_pipeline, project: project, sha: commit_without_author.id) } let!(:pipeline_without_commit) { create(:ci_pipeline, project: project, sha: '0000') } diff --git a/spec/javascripts/jobs/components/job_log_spec.js b/spec/javascripts/jobs/components/job_log_spec.js index dc0f77ceb80..7e2ec2ec3f7 100644 --- a/spec/javascripts/jobs/components/job_log_spec.js +++ b/spec/javascripts/jobs/components/job_log_spec.js @@ -3,6 +3,7 @@ import component from '~/jobs/components/job_log.vue'; import createStore from '~/jobs/store'; import { mountComponentWithStore } from 'spec/helpers/vue_mount_component_helper'; import { resetStore } from '../store/helpers'; +import { logWithCollapsibleSections } from '../mock_data'; describe('Job Log', () => { const Component = Vue.extend(component); @@ -62,4 +63,40 @@ describe('Job Log', () => { expect(vm.$el.querySelector('.js-log-animation')).toBeNull(); }); }); + + describe('Collapsible sections', () => { + beforeEach(() => { + vm = mountComponentWithStore(Component, { + props: { + trace: logWithCollapsibleSections.html, + isComplete: true, + }, + store, + }); + }); + + it('renders open arrow', () => { + expect(vm.$el.querySelector('.fa-caret-down')).not.toBeNull(); + }); + + it('toggles hidden class to the sibilings rows when arrow is clicked', done => { + vm.$nextTick() + .then(() => { + const { section } = vm.$el.querySelector('.js-section-start').dataset; + vm.$el.querySelector('.js-section-start').click(); + + vm.$el.querySelectorAll(`.js-s-${section}:not(.js-section-header)`).forEach(el => { + expect(el.classList.contains('hidden')).toEqual(true); + }); + + vm.$el.querySelector('.js-section-start').click(); + + vm.$el.querySelectorAll(`.js-s-${section}:not(.js-section-header)`).forEach(el => { + expect(el.classList.contains('hidden')).toEqual(false); + }); + }) + .then(done) + .catch(done.fail); + }); + }); }); diff --git a/spec/javascripts/jobs/components/stages_dropdown_spec.js b/spec/javascripts/jobs/components/stages_dropdown_spec.js index e98639bf21e..52bb5161123 100644 --- a/spec/javascripts/jobs/components/stages_dropdown_spec.js +++ b/spec/javascripts/jobs/components/stages_dropdown_spec.js @@ -9,7 +9,6 @@ describe('Stages Dropdown', () => { const mockPipelineData = { id: 28029444, - iid: 123, details: { status: { details_path: '/gitlab-org/gitlab-ce/pipelines/28029444', @@ -78,8 +77,8 @@ describe('Stages Dropdown', () => { expect(vm.$el.querySelector('.dropdown .js-selected-stage').textContent).toContain('deploy'); }); - it(`renders the pipeline info text like "Pipeline #123 (#12) for source_branch"`, () => { - const expected = `Pipeline #${pipeline.id} (#${pipeline.iid}) for ${pipeline.ref.name}`; + it(`renders the pipeline info text like "Pipeline #123 for source_branch"`, () => { + const expected = `Pipeline #${pipeline.id} for ${pipeline.ref.name}`; const actual = trimText(vm.$el.querySelector('.js-pipeline-info').innerText); expect(actual).toBe(expected); @@ -101,10 +100,10 @@ describe('Stages Dropdown', () => { }); }); - it(`renders the pipeline info text like "Pipeline #123 (#12) for !456 with source_branch into target_branch"`, () => { - const expected = `Pipeline #${pipeline.id} (#${pipeline.iid}) for !${ - pipeline.merge_request.iid - } with ${pipeline.merge_request.source_branch} into ${pipeline.merge_request.target_branch}`; + it(`renders the pipeline info text like "Pipeline #123 for !456 with source_branch into target_branch"`, () => { + const expected = `Pipeline #${pipeline.id} for !${pipeline.merge_request.iid} with ${ + pipeline.merge_request.source_branch + } into ${pipeline.merge_request.target_branch}`; const actual = trimText(vm.$el.querySelector('.js-pipeline-info').innerText); expect(actual).toBe(expected); @@ -144,10 +143,10 @@ describe('Stages Dropdown', () => { }); }); - it(`renders the pipeline info like "Pipeline #123 (#12) for !456 with source_branch"`, () => { - const expected = `Pipeline #${pipeline.id} (#${pipeline.iid}) for !${ - pipeline.merge_request.iid - } with ${pipeline.merge_request.source_branch}`; + it(`renders the pipeline info like "Pipeline #123 for !456 with source_branch"`, () => { + const expected = `Pipeline #${pipeline.id} for !${pipeline.merge_request.iid} with ${ + pipeline.merge_request.source_branch + }`; const actual = trimText(vm.$el.querySelector('.js-pipeline-info').innerText); expect(actual).toBe(expected); diff --git a/spec/javascripts/jobs/mock_data.js b/spec/javascripts/jobs/mock_data.js index 88b0bb206ee..c5022d3e93d 100644 --- a/spec/javascripts/jobs/mock_data.js +++ b/spec/javascripts/jobs/mock_data.js @@ -960,7 +960,6 @@ export default { }, pipeline: { id: 140, - iid: 13, user: { name: 'Root', username: 'root', @@ -1190,3 +1189,18 @@ export const jobsInStage = { path: '/gitlab-org/gitlab-shell/pipelines/27#build', dropdown_path: '/gitlab-org/gitlab-shell/pipelines/27/stage.json?stage=build', }; + +export const logWithCollapsibleSections = { + append: false, + complete: true, + html: + '<div class="js-section-start fa fa-caret-down append-right-8 cursor-pointer" data-timestamp="1559571405" data-section="after-script" role="button"></div><span class="term-fg-l-green term-bold section js-section-header js-s-after-script">Running after script...</span><span class="section js-section-header js-s-after-script"><br /></span><span class="section s_after-script line"></span><span class="section js-s-after-script"></span><span class="term-fg-l-green term-bold section js-s-after-script">$ date</span><span class="section js-s-after-script"><br /></span><span class="section s_after-script line"></span><span class="section js-s-after-script">Mon Jun 3 14:16:46 UTC 2019<br /></span><span class="section s_after-script line"></span><span class="section js-s-after-script"></span><div class="section-end" data-section="after-script"></div><div class="js-section-start fa fa-caret-down append-right-8 cursor-pointer"data-timestamp="1559571408" data-section="archive-cache" role="button" ></div><span class="term-fg-l-green term-bold section js-section-header js-s-archive-cache">Not uploading cache debian-stretch-ruby-2.6.3-node-10.x-3 due to policy</span><span class="section js-section-header js-s-archive-cache"><br /></span><span class="section s_archive-cache line"></span><span class="section js-s-archive-cache"></span><div class="section-end" data-section="archive-cache"></div><div class="js-section-start fa fa-caret-down append-right-8 cursor-pointer" data-timestamp="1559571409" data-section="upload-artifacts-on-success" role="button"></div><span class="term-fg-l-green term-bold section js-section-header js-s-upload-artifacts-on-success">Uploading artifacts...</span><span class="section js-section-header js-s-upload-artifacts-on-success"><br /></span><span class="section s_upload-artifacts-on-success line"></span><span class="section js-s-upload-artifacts-on-success">coverage/: found 5 matching files </span><span class="section js-s-upload-artifacts-on-success"> <br /></span><span class="section s_upload-artifacts-on-success line"></span><span class="section js-s-upload-artifacts-on-success">knapsack/: found 4 matching files </span><span class="section js-s-upload-artifacts-on-success"> <br /></span><span class="section s_upload-artifacts-on-success line"></span><span class="section js-s-upload-artifacts-on-success">rspec_flaky/: found 4 matching files </span><span class="section js-s-upload-artifacts-on-success"> <br /></span><span class="section s_upload-artifacts-on-success line"></span><span class="section js-s-upload-artifacts-on-success">rspec_profiling/: found 1 matching files </span><span class="section js-s-upload-artifacts-on-success"> <br /></span><span class="section s_upload-artifacts-on-success line"></span><span class="section js-s-upload-artifacts-on-success"></span><span class="term-fg-yellow section js-s-upload-artifacts-on-success">WARNING: tmp/capybara/: no matching files </span><span class="section js-s-upload-artifacts-on-success"> <br /></span><span class="section s_upload-artifacts-on-success line"></span><span class="section js-s-upload-artifacts-on-success">Uploading artifacts to coordinator... ok </span><span class="section js-s-upload-artifacts-on-success"> id</span><span class="section js-s-upload-artifacts-on-success">=224162288 responseStatus</span><span class="section js-s-upload-artifacts-on-success">=201 Created token</span><span class="section js-s-upload-artifacts-on-success">=bBmyXJNW<br /></span><span class="section s_upload-artifacts-on-success line"></span><span class="section js-s-upload-artifacts-on-success"></span><span class="term-fg-l-green term-bold section js-s-upload-artifacts-on-success">Uploading artifacts...</span><span class="section js-s-upload-artifacts-on-success"><br /></span><span class="section s_upload-artifacts-on-success line"></span><span class="section js-s-upload-artifacts-on-success">junit_rspec.xml: found 1 matching files </span><span class="section js-s-upload-artifacts-on-success"> <br /></span><span class="section s_upload-artifacts-on-success line"></span><span class="section js-s-upload-artifacts-on-success">Uploading artifacts to coordinator... ok </span><span class="section js-s-upload-artifacts-on-success"> id</span><span class="section js-s-upload-artifacts-on-success">=224162288 responseStatus</span><span class="section js-s-upload-artifacts-on-success">=201 Created token</span><span class="section js-s-upload-artifacts-on-success">=bBmyXJNW<br /></span><span class="section s_upload-artifacts-on-success line"></span><span class="section js-s-upload-artifacts-on-success"></span><div class="section-end" data-section="upload-artifacts-on-success"></div><span class="term-fg-l-green term-bold">Job succeeded<br /><span class="term-fg-l-green term-bold"></span></span>', + id: 1385, + offset: 0, + size: 78815, + state: + 'eyJvZmZzZXQiOjc4ODE1LCJuX29wZW5fdGFncyI6MCwiZmdfY29sb3IiOm51bGwsImJnX2NvbG9yIjpudWxsLCJzdHlsZV9tYXNrIjowLCJzZWN0aW9ucyI6W10sImxpbmVub19pbl9zZWN0aW9uIjoxMX0=', + status: 'success', + total: 78815, + truncated: false, +}; diff --git a/spec/javascripts/pipelines/mock_data.js b/spec/javascripts/pipelines/mock_data.js index 8eef9166b8d..03ead6cd8ba 100644 --- a/spec/javascripts/pipelines/mock_data.js +++ b/spec/javascripts/pipelines/mock_data.js @@ -1,6 +1,5 @@ export const pipelineWithStages = { id: 20333396, - iid: 304399, user: { id: 128633, name: 'Rémy Coutable', diff --git a/spec/javascripts/pipelines/pipeline_url_spec.js b/spec/javascripts/pipelines/pipeline_url_spec.js index 88c0137dc58..aa196af2f33 100644 --- a/spec/javascripts/pipelines/pipeline_url_spec.js +++ b/spec/javascripts/pipelines/pipeline_url_spec.js @@ -13,7 +13,6 @@ describe('Pipeline Url Component', () => { propsData: { pipeline: { id: 1, - iid: 1, path: 'foo', flags: {}, }, @@ -29,7 +28,6 @@ describe('Pipeline Url Component', () => { propsData: { pipeline: { id: 1, - iid: 1, path: 'foo', flags: {}, }, @@ -49,7 +47,6 @@ describe('Pipeline Url Component', () => { propsData: { pipeline: { id: 1, - iid: 1, path: 'foo', flags: { latest: true, @@ -81,7 +78,6 @@ describe('Pipeline Url Component', () => { propsData: { pipeline: { id: 1, - iid: 1, path: 'foo', flags: { latest: true, @@ -104,7 +100,6 @@ describe('Pipeline Url Component', () => { propsData: { pipeline: { id: 1, - iid: 1, path: 'foo', flags: { failure_reason: true, diff --git a/spec/javascripts/vue_mr_widget/components/mr_widget_pipeline_spec.js b/spec/javascripts/vue_mr_widget/components/mr_widget_pipeline_spec.js index a2308b0dfdb..75017d20473 100644 --- a/spec/javascripts/vue_mr_widget/components/mr_widget_pipeline_spec.js +++ b/spec/javascripts/vue_mr_widget/components/mr_widget_pipeline_spec.js @@ -103,7 +103,7 @@ describe('MRWidgetPipeline', () => { it('should render pipeline ID', () => { expect(vm.$el.querySelector('.pipeline-id').textContent.trim()).toEqual( - `#${mockData.pipeline.id} (#${mockData.pipeline.iid})`, + `#${mockData.pipeline.id}`, ); }); @@ -150,7 +150,7 @@ describe('MRWidgetPipeline', () => { it('should render pipeline ID', () => { expect(vm.$el.querySelector('.pipeline-id').textContent.trim()).toEqual( - `#${mockData.pipeline.id} (#${mockData.pipeline.iid})`, + `#${mockData.pipeline.id}`, ); }); @@ -222,9 +222,9 @@ describe('MRWidgetPipeline', () => { sourceBranchLink: mockCopy.source_branch_link, }); - const expected = `Pipeline #${pipeline.id} (#${pipeline.iid}) ${ - pipeline.details.status.label - } for ${pipeline.commit.short_id} on ${mockCopy.source_branch_link}`; + const expected = `Pipeline #${pipeline.id} ${pipeline.details.status.label} for ${ + pipeline.commit.short_id + } on ${mockCopy.source_branch_link}`; const actual = trimText(vm.$el.querySelector('.js-pipeline-info-container').innerText); @@ -247,11 +247,11 @@ describe('MRWidgetPipeline', () => { sourceBranchLink: mockCopy.source_branch_link, }); - const expected = `Pipeline #${pipeline.id} (#${pipeline.iid}) ${ - pipeline.details.status.label - } for ${pipeline.commit.short_id} on !${pipeline.merge_request.iid} with ${ - pipeline.merge_request.source_branch - } into ${pipeline.merge_request.target_branch}`; + const expected = `Pipeline #${pipeline.id} ${pipeline.details.status.label} for ${ + pipeline.commit.short_id + } on !${pipeline.merge_request.iid} with ${pipeline.merge_request.source_branch} into ${ + pipeline.merge_request.target_branch + }`; const actual = trimText(vm.$el.querySelector('.js-pipeline-info-container').innerText); @@ -274,11 +274,9 @@ describe('MRWidgetPipeline', () => { sourceBranchLink: mockCopy.source_branch_link, }); - const expected = `Pipeline #${pipeline.id} (#${pipeline.iid}) ${ - pipeline.details.status.label - } for ${pipeline.commit.short_id} on !${pipeline.merge_request.iid} with ${ - pipeline.merge_request.source_branch - }`; + const expected = `Pipeline #${pipeline.id} ${pipeline.details.status.label} for ${ + pipeline.commit.short_id + } on !${pipeline.merge_request.iid} with ${pipeline.merge_request.source_branch}`; const actual = trimText(vm.$el.querySelector('.js-pipeline-info-container').innerText); diff --git a/spec/javascripts/vue_mr_widget/mock_data.js b/spec/javascripts/vue_mr_widget/mock_data.js index 3c9a5cece90..48f812f0db4 100644 --- a/spec/javascripts/vue_mr_widget/mock_data.js +++ b/spec/javascripts/vue_mr_widget/mock_data.js @@ -61,7 +61,6 @@ export default { "Merge branch 'daaaa' into 'master'\n\nUpdate README.md\n\nSee merge request !22", pipeline: { id: 172, - iid: 32, user: { name: 'Administrator', username: 'root', @@ -243,8 +242,6 @@ export default { export const mockStore = { pipeline: { id: 0, - iid: 0, - path: '/root/acets-app/pipelines/0', details: { status: { details_path: '/root/review-app-tester/pipelines/66', @@ -262,8 +259,6 @@ export const mockStore = { }, mergePipeline: { id: 1, - iid: 1, - path: '/root/acets-app/pipelines/0', details: { status: { details_path: '/root/review-app-tester/pipelines/66', diff --git a/spec/javascripts/vue_mr_widget/mr_widget_options_spec.js b/spec/javascripts/vue_mr_widget/mr_widget_options_spec.js index 08f7a17515e..ac2fb16bd10 100644 --- a/spec/javascripts/vue_mr_widget/mr_widget_options_spec.js +++ b/spec/javascripts/vue_mr_widget/mr_widget_options_spec.js @@ -544,7 +544,6 @@ describe('mrWidgetOptions', () => { ]; const deploymentMockData = { id: 15, - iid: 7, name: 'review/diplo', url: '/root/acets-review-apps/environments/15', stop_url: '/root/acets-review-apps/environments/15/stop', @@ -591,7 +590,6 @@ describe('mrWidgetOptions', () => { vm.mr.state = 'merged'; vm.mr.mergePipeline = { id: 127, - iid: 35, user: { id: 1, name: 'Administrator', diff --git a/spec/lib/gitlab/ci/ansi2html_spec.rb b/spec/lib/gitlab/ci/ansi2html_spec.rb index 5a5c071c639..ac4612dda92 100644 --- a/spec/lib/gitlab/ci/ansi2html_spec.rb +++ b/spec/lib/gitlab/ci/ansi2html_spec.rb @@ -4,11 +4,11 @@ describe Gitlab::Ci::Ansi2html do subject { described_class } it "prints non-ansi as-is" do - expect(convert_html("Hello")).to eq('Hello') + expect(convert_html("Hello")).to eq('<span class="">Hello</span>') end it "strips non-color-changing control sequences" do - expect(convert_html("Hello \e[2Kworld")).to eq('Hello world') + expect(convert_html("Hello \e[2Kworld")).to eq('<span class="">Hello world</span>') end it "prints simply red" do @@ -32,7 +32,7 @@ describe Gitlab::Ci::Ansi2html do end it "resets colors after red on blue" do - expect(convert_html("\e[31;44mHello\e[0m world")).to eq('<span class="term-fg-red term-bg-blue">Hello</span> world') + expect(convert_html("\e[31;44mHello\e[0m world")).to eq('<span class="term-fg-red term-bg-blue">Hello</span><span class=""> world</span>') end it "performs color change from red/blue to yellow/blue" do @@ -44,11 +44,11 @@ describe Gitlab::Ci::Ansi2html do end it "performs color change from red/blue to reset to yellow/green" do - expect(convert_html("\e[31;44mHello\e[0m \e[33;42mworld")).to eq('<span class="term-fg-red term-bg-blue">Hello</span> <span class="term-fg-yellow term-bg-green">world</span>') + expect(convert_html("\e[31;44mHello\e[0m \e[33;42mworld")).to eq('<span class="term-fg-red term-bg-blue">Hello</span><span class=""> </span><span class="term-fg-yellow term-bg-green">world</span>') end it "ignores unsupported codes" do - expect(convert_html("\e[51mHello\e[0m")).to eq('Hello') + expect(convert_html("\e[51mHello\e[0m")).to eq('<span class="">Hello</span>') end it "prints light red" do @@ -72,8 +72,8 @@ describe Gitlab::Ci::Ansi2html do end it "resets bold text" do - expect(convert_html("\e[1mHello\e[21m world")).to eq('<span class="term-bold">Hello</span> world') - expect(convert_html("\e[1mHello\e[22m world")).to eq('<span class="term-bold">Hello</span> world') + expect(convert_html("\e[1mHello\e[21m world")).to eq('<span class="term-bold">Hello</span><span class=""> world</span>') + expect(convert_html("\e[1mHello\e[22m world")).to eq('<span class="term-bold">Hello</span><span class=""> world</span>') end it "prints italic text" do @@ -81,7 +81,7 @@ describe Gitlab::Ci::Ansi2html do end it "resets italic text" do - expect(convert_html("\e[3mHello\e[23m world")).to eq('<span class="term-italic">Hello</span> world') + expect(convert_html("\e[3mHello\e[23m world")).to eq('<span class="term-italic">Hello</span><span class=""> world</span>') end it "prints underlined text" do @@ -89,7 +89,7 @@ describe Gitlab::Ci::Ansi2html do end it "resets underlined text" do - expect(convert_html("\e[4mHello\e[24m world")).to eq('<span class="term-underline">Hello</span> world') + expect(convert_html("\e[4mHello\e[24m world")).to eq('<span class="term-underline">Hello</span><span class=""> world</span>') end it "prints concealed text" do @@ -97,7 +97,7 @@ describe Gitlab::Ci::Ansi2html do end it "resets concealed text" do - expect(convert_html("\e[8mHello\e[28m world")).to eq('<span class="term-conceal">Hello</span> world') + expect(convert_html("\e[8mHello\e[28m world")).to eq('<span class="term-conceal">Hello</span><span class=""> world</span>') end it "prints crossed-out text" do @@ -105,7 +105,7 @@ describe Gitlab::Ci::Ansi2html do end it "resets crossed-out text" do - expect(convert_html("\e[9mHello\e[29m world")).to eq('<span class="term-cross">Hello</span> world') + expect(convert_html("\e[9mHello\e[29m world")).to eq('<span class="term-cross">Hello</span><span class=""> world</span>') end it "can print 256 xterm fg colors" do @@ -137,15 +137,15 @@ describe Gitlab::Ci::Ansi2html do end it "prints <" do - expect(convert_html("<")).to eq('<') + expect(convert_html("<")).to eq('<span class=""><</span>') end it "replaces newlines with line break tags" do - expect(convert_html("\n")).to eq('<br>') + expect(convert_html("\n")).to eq('<span class=""><br/><span class=""></span></span>') end it "groups carriage returns with newlines" do - expect(convert_html("\r\n")).to eq('<br>') + expect(convert_html("\r\n")).to eq('<span class=""><br/><span class=""></span></span>') end describe "incremental update" do @@ -166,14 +166,14 @@ describe Gitlab::Ci::Ansi2html do let(:pre_text) { "\e[1mHello" } let(:pre_html) { "<span class=\"term-bold\">Hello</span>" } let(:text) { "\e[1mWorld" } - let(:html) { "<span class=\"term-bold\"></span><span class=\"term-bold\">World</span>" } + let(:html) { "<span class=\"term-bold\">World</span>" } it_behaves_like 'stateable converter' end context "with split sequence" do let(:pre_text) { "\e[1m" } - let(:pre_html) { "<span class=\"term-bold\"></span>" } + let(:pre_html) { "" } let(:text) { "Hello" } let(:html) { "<span class=\"term-bold\">Hello</span>" } @@ -182,7 +182,7 @@ describe Gitlab::Ci::Ansi2html do context "with partial sequence" do let(:pre_text) { "Hello\e" } - let(:pre_html) { "Hello" } + let(:pre_html) { "<span class=\"\">Hello</span>" } let(:text) { "[1m World" } let(:html) { "<span class=\"term-bold\"> World</span>" } @@ -191,9 +191,9 @@ describe Gitlab::Ci::Ansi2html do context 'with new line' do let(:pre_text) { "Hello\r" } - let(:pre_html) { "Hello\r" } + let(:pre_html) { "<span class=\"\">Hello\r</span>" } let(:text) { "\nWorld" } - let(:html) { "<br>World" } + let(:html) { "<span class=\"\"><br/><span class=\"\">World</span></span>" } it_behaves_like 'stateable converter' end @@ -207,20 +207,20 @@ describe Gitlab::Ci::Ansi2html do let(:section_start) { "section_start:#{section_start_time.to_i}:#{section_name}\r\033[0K"} let(:section_end) { "section_end:#{section_end_time.to_i}:#{section_name}\r\033[0K"} let(:section_start_html) do - '<div class="hidden" data-action="start"'\ - " data-timestamp=\"#{section_start_time.to_i}\" data-section=\"#{section_name}\">"\ - "#{section_start[0...-5]}</div>" + '<div class="js-section-start fa fa-caret-down append-right-8 cursor-pointer"' \ + " data-timestamp=\"#{section_start_time.to_i}\" data-section=\"#{class_name(section_name)}\"" \ + ' role="button"></div>' end let(:section_end_html) do - '<div class="hidden" data-action="end"'\ - " data-timestamp=\"#{section_end_time.to_i}\" data-section=\"#{section_name}\">"\ - "#{section_end[0...-5]}</div>" + "<div class=\"section-end\" data-section=\"#{class_name(section_name)}\"></div>" end shared_examples 'forbidden char in section_name' do it 'ignores sections' do text = "#{section_start}Some text#{section_end}" - html = text.gsub("\033[0K", '').gsub('<', '<') + class_name_start = section_start.gsub("\033[0K", '').gsub('<', '<') + class_name_end = section_end.gsub("\033[0K", '').gsub('<', '<') + html = %{<span class="">#{class_name_start}Some text#{class_name_end}</span>} expect(convert_html(text)).to eq(html) end @@ -231,7 +231,11 @@ describe Gitlab::Ci::Ansi2html do it 'prints light red' do text = "#{section_start}\e[91mHello\e[0m\n#{section_end}" - html = %{#{section_start_html}<span class="term-fg-l-red">Hello</span><br>#{section_end_html}} + header = %{<span class="term-fg-l-red section js-section-header js-s-#{class_name(section_name)}">Hello</span>} + line_break = %{<span class="section js-section-header js-s-#{class_name(section_name)}"><br/></span>} + line = %{<span class="section line s_#{class_name(section_name)}"></span>} + empty_line = %{<span class="section js-s-#{class_name(section_name)}"></span>} + html = "#{section_start_html}#{header}#{line_break}#{line}#{empty_line}#{section_end_html}" expect(convert_html(text)).to eq(html) end @@ -294,4 +298,8 @@ describe Gitlab::Ci::Ansi2html do stream = StringIO.new(data) subject.convert(stream).html end + + def class_name(section) + subject::Converter.new.section_to_class_name(section) + end end diff --git a/spec/lib/gitlab/ci/build/policy/kubernetes_spec.rb b/spec/lib/gitlab/ci/build/policy/kubernetes_spec.rb index 4884d5f8ba4..4510b82ca9d 100644 --- a/spec/lib/gitlab/ci/build/policy/kubernetes_spec.rb +++ b/spec/lib/gitlab/ci/build/policy/kubernetes_spec.rb @@ -4,24 +4,14 @@ describe Gitlab::Ci::Build::Policy::Kubernetes do let(:pipeline) { create(:ci_pipeline, project: project) } context 'when kubernetes service is active' do - shared_examples 'same behavior between KubernetesService and Platform::Kubernetes' do - it 'is satisfied by a kubernetes pipeline' do - expect(described_class.new('active')) - .to be_satisfied_by(pipeline) - end - end - - context 'when user configured kubernetes from Integration > Kubernetes' do - let(:project) { create(:kubernetes_project) } - - it_behaves_like 'same behavior between KubernetesService and Platform::Kubernetes' - end - context 'when user configured kubernetes from CI/CD > Clusters' do let!(:cluster) { create(:cluster, :project, :provided_by_gcp) } let(:project) { cluster.project } - it_behaves_like 'same behavior between KubernetesService and Platform::Kubernetes' + it 'is satisfied by a kubernetes pipeline' do + expect(described_class.new('active')) + .to be_satisfied_by(pipeline) + end end end diff --git a/spec/lib/gitlab/ci/build/prerequisite/kubernetes_namespace_spec.rb b/spec/lib/gitlab/ci/build/prerequisite/kubernetes_namespace_spec.rb index 5387863bd07..5ac5122e800 100644 --- a/spec/lib/gitlab/ci/build/prerequisite/kubernetes_namespace_spec.rb +++ b/spec/lib/gitlab/ci/build/prerequisite/kubernetes_namespace_spec.rb @@ -35,9 +35,15 @@ describe Gitlab::Ci::Build::Prerequisite::KubernetesNamespace do end context 'and a namespace is already created for this project' do - let!(:kubernetes_namespace) { create(:cluster_kubernetes_namespace, cluster: cluster, project: build.project) } + let!(:kubernetes_namespace) { create(:cluster_kubernetes_namespace, :with_token, cluster: cluster, project: build.project) } it { is_expected.to be_falsey } + + context 'and the service_account_token is blank' do + let!(:kubernetes_namespace) { create(:cluster_kubernetes_namespace, :without_token, cluster: cluster, project: build.project) } + + it { is_expected.to be_truthy } + end end context 'and cluster is project type' do diff --git a/spec/lib/gitlab/ci/pipeline/chain/build_spec.rb b/spec/lib/gitlab/ci/pipeline/chain/build_spec.rb index 3debd42ac65..50cb45c39d1 100644 --- a/spec/lib/gitlab/ci/pipeline/chain/build_spec.rb +++ b/spec/lib/gitlab/ci/pipeline/chain/build_spec.rb @@ -2,7 +2,7 @@ require 'spec_helper' describe Gitlab::Ci::Pipeline::Chain::Build do set(:project) { create(:project, :repository) } - set(:user) { create(:user) } + set(:user) { create(:user, developer_projects: [project]) } let(:pipeline) { Ci::Pipeline.new } let(:variables_attributes) do diff --git a/spec/lib/gitlab/ci/pipeline/expression/lexeme/matches_spec.rb b/spec/lib/gitlab/ci/pipeline/expression/lexeme/matches_spec.rb index 97da66d2bcc..a6fdec832a3 100644 --- a/spec/lib/gitlab/ci/pipeline/expression/lexeme/matches_spec.rb +++ b/spec/lib/gitlab/ci/pipeline/expression/lexeme/matches_spec.rb @@ -50,21 +50,21 @@ describe Gitlab::Ci::Pipeline::Expression::Lexeme::Matches do let(:left_value) { 'my-string' } let(:right_value) { Gitlab::UntrustedRegexp.new('something') } - it { is_expected.to eq(nil) } + it { is_expected.to eq(false) } end context 'when left and right match' do let(:left_value) { 'my-awesome-string' } let(:right_value) { Gitlab::UntrustedRegexp.new('awesome.string$') } - it { is_expected.to eq(3) } + it { is_expected.to eq(true) } end context 'when left is nil' do let(:left_value) { nil } let(:right_value) { Gitlab::UntrustedRegexp.new('pattern') } - it { is_expected.to eq(nil) } + it { is_expected.to eq(false) } end context 'when left is a multiline string and matches right' do @@ -78,7 +78,7 @@ describe Gitlab::Ci::Pipeline::Expression::Lexeme::Matches do let(:right_value) { Gitlab::UntrustedRegexp.new('text-string') } - it { is_expected.to eq(24) } + it { is_expected.to eq(true) } end context 'when left is a multiline string and does not match right' do @@ -92,7 +92,7 @@ describe Gitlab::Ci::Pipeline::Expression::Lexeme::Matches do let(:right_value) { Gitlab::UntrustedRegexp.new('text-string') } - it { is_expected.to eq(nil) } + it { is_expected.to eq(false) } end context 'when a matching pattern uses regex flags' do @@ -104,7 +104,7 @@ describe Gitlab::Ci::Pipeline::Expression::Lexeme::Matches do let(:right_value) { Gitlab::UntrustedRegexp.new('(?i)awesome') } - it { is_expected.to eq(3) } + it { is_expected.to eq(true) } end context 'when a non-matching pattern uses regex flags' do @@ -116,7 +116,7 @@ describe Gitlab::Ci::Pipeline::Expression::Lexeme::Matches do let(:right_value) { Gitlab::UntrustedRegexp.new('(?i)terrible') } - it { is_expected.to eq(nil) } + it { is_expected.to eq(false) } end end end diff --git a/spec/lib/gitlab/ci/pipeline/expression/statement_spec.rb b/spec/lib/gitlab/ci/pipeline/expression/statement_spec.rb index 057e2f3fbe8..a2c2e3653d5 100644 --- a/spec/lib/gitlab/ci/pipeline/expression/statement_spec.rb +++ b/spec/lib/gitlab/ci/pipeline/expression/statement_spec.rb @@ -41,17 +41,17 @@ describe Gitlab::Ci::Pipeline::Expression::Statement do 'null == $UNDEFINED_VARIABLE' | true '$PRESENT_VARIABLE' | 'my variable' '$UNDEFINED_VARIABLE' | nil - "$PRESENT_VARIABLE =~ /var.*e$/" | 3 - '$PRESENT_VARIABLE =~ /va\r.*e$/' | nil - '$PRESENT_VARIABLE =~ /va\/r.*e$/' | nil - "$PRESENT_VARIABLE =~ /var.*e$/" | 3 - "$PRESENT_VARIABLE =~ /^var.*/" | nil - "$EMPTY_VARIABLE =~ /var.*/" | nil - "$UNDEFINED_VARIABLE =~ /var.*/" | nil - "$PRESENT_VARIABLE =~ /VAR.*/i" | 3 - '$PATH_VARIABLE =~ /path\/variable/' | 2 - '$FULL_PATH_VARIABLE =~ /^\/a\/full\/path\/variable\/value$/' | 0 - '$FULL_PATH_VARIABLE =~ /\\/path\\/variable\\/value$/' | 7 + "$PRESENT_VARIABLE =~ /var.*e$/" | true + '$PRESENT_VARIABLE =~ /va\r.*e$/' | false + '$PRESENT_VARIABLE =~ /va\/r.*e$/' | false + "$PRESENT_VARIABLE =~ /var.*e$/" | true + "$PRESENT_VARIABLE =~ /^var.*/" | false + "$EMPTY_VARIABLE =~ /var.*/" | false + "$UNDEFINED_VARIABLE =~ /var.*/" | false + "$PRESENT_VARIABLE =~ /VAR.*/i" | true + '$PATH_VARIABLE =~ /path\/variable/' | true + '$FULL_PATH_VARIABLE =~ /^\/a\/full\/path\/variable\/value$/' | true + '$FULL_PATH_VARIABLE =~ /\\/path\\/variable\\/value$/' | true '$PRESENT_VARIABLE != "my variable"' | false '"my variable" != $PRESENT_VARIABLE' | false '$PRESENT_VARIABLE != null' | true @@ -82,7 +82,7 @@ describe Gitlab::Ci::Pipeline::Expression::Statement do '"string" && "string"' | 'string' 'null && null' | nil - '$PRESENT_VARIABLE =~ /my var/ && $EMPTY_VARIABLE =~ /nope/' | nil + '$PRESENT_VARIABLE =~ /my var/ && $EMPTY_VARIABLE =~ /nope/' | false '$EMPTY_VARIABLE == "" && $PRESENT_VARIABLE' | 'my variable' '$EMPTY_VARIABLE == "" && $PRESENT_VARIABLE != "nope"' | true '$PRESENT_VARIABLE && $EMPTY_VARIABLE' | '' @@ -90,17 +90,17 @@ describe Gitlab::Ci::Pipeline::Expression::Statement do '$UNDEFINED_VARIABLE && $EMPTY_VARIABLE' | nil '$UNDEFINED_VARIABLE && $PRESENT_VARIABLE' | nil - '$FULL_PATH_VARIABLE =~ /^\/a\/full\/path\/variable\/value$/ && $PATH_VARIABLE =~ /path\/variable/' | 2 - '$FULL_PATH_VARIABLE =~ /^\/a\/bad\/path\/variable\/value$/ && $PATH_VARIABLE =~ /path\/variable/' | nil - '$FULL_PATH_VARIABLE =~ /^\/a\/full\/path\/variable\/value$/ && $PATH_VARIABLE =~ /bad\/path\/variable/' | nil - '$FULL_PATH_VARIABLE =~ /^\/a\/bad\/path\/variable\/value$/ && $PATH_VARIABLE =~ /bad\/path\/variable/' | nil + '$FULL_PATH_VARIABLE =~ /^\/a\/full\/path\/variable\/value$/ && $PATH_VARIABLE =~ /path\/variable/' | true + '$FULL_PATH_VARIABLE =~ /^\/a\/bad\/path\/variable\/value$/ && $PATH_VARIABLE =~ /path\/variable/' | false + '$FULL_PATH_VARIABLE =~ /^\/a\/full\/path\/variable\/value$/ && $PATH_VARIABLE =~ /bad\/path\/variable/' | false + '$FULL_PATH_VARIABLE =~ /^\/a\/bad\/path\/variable\/value$/ && $PATH_VARIABLE =~ /bad\/path\/variable/' | false - '$FULL_PATH_VARIABLE =~ /^\/a\/full\/path\/variable\/value$/ || $PATH_VARIABLE =~ /path\/variable/' | 0 - '$FULL_PATH_VARIABLE =~ /^\/a\/bad\/path\/variable\/value$/ || $PATH_VARIABLE =~ /path\/variable/' | 2 - '$FULL_PATH_VARIABLE =~ /^\/a\/full\/path\/variable\/value$/ || $PATH_VARIABLE =~ /bad\/path\/variable/' | 0 - '$FULL_PATH_VARIABLE =~ /^\/a\/bad\/path\/variable\/value$/ || $PATH_VARIABLE =~ /bad\/path\/variable/' | nil + '$FULL_PATH_VARIABLE =~ /^\/a\/full\/path\/variable\/value$/ || $PATH_VARIABLE =~ /path\/variable/' | true + '$FULL_PATH_VARIABLE =~ /^\/a\/bad\/path\/variable\/value$/ || $PATH_VARIABLE =~ /path\/variable/' | true + '$FULL_PATH_VARIABLE =~ /^\/a\/full\/path\/variable\/value$/ || $PATH_VARIABLE =~ /bad\/path\/variable/' | true + '$FULL_PATH_VARIABLE =~ /^\/a\/bad\/path\/variable\/value$/ || $PATH_VARIABLE =~ /bad\/path\/variable/' | false - '$PRESENT_VARIABLE =~ /my var/ || $EMPTY_VARIABLE =~ /nope/' | 0 + '$PRESENT_VARIABLE =~ /my var/ || $EMPTY_VARIABLE =~ /nope/' | true '$EMPTY_VARIABLE == "" || $PRESENT_VARIABLE' | true '$PRESENT_VARIABLE != "nope" || $EMPTY_VARIABLE == ""' | true @@ -117,21 +117,6 @@ describe Gitlab::Ci::Pipeline::Expression::Statement do it "evaluates to `#{params[:value].inspect}`" do expect(subject.evaluate).to eq(value) end - - # This test is used to ensure that our parser - # returns exactly the same results as if we - # were evaluating using ruby's `eval` - context 'when using Ruby eval' do - let(:expression_ruby) do - expression - .gsub(/null/, 'nil') - .gsub(/\$([a-zA-Z_][a-zA-Z0-9_]*)/) { "variables['#{Regexp.last_match(1)}']" } - end - - it 'behaves exactly the same' do - expect(instance_eval(expression_ruby)).to eq(subject.evaluate) - end - end end context 'with the ci_variables_complex_expressions feature flag disabled' do diff --git a/spec/lib/gitlab/ci/trace/stream_spec.rb b/spec/lib/gitlab/ci/trace/stream_spec.rb index e45ea1c2528..35250632e86 100644 --- a/spec/lib/gitlab/ci/trace/stream_spec.rb +++ b/spec/lib/gitlab/ci/trace/stream_spec.rb @@ -64,7 +64,10 @@ describe Gitlab::Ci::Trace::Stream, :clean_gitlab_redis_cache do result = stream.html - expect(result).to eq("ヾ(´༎ຶД༎ຶ`)ノ<br><span class=\"term-fg-green\">許功蓋</span><br>") + expect(result).to eq( + "<span class=\"\">ヾ(´༎ຶД༎ຶ`)ノ<br/><span class=\"\"></span></span>"\ + "<span class=\"term-fg-green\">許功蓋</span><span class=\"\"><br/>"\ + "<span class=\"\"></span></span>") expect(result.encoding).to eq(Encoding.default_external) end end @@ -250,7 +253,7 @@ describe Gitlab::Ci::Trace::Stream, :clean_gitlab_redis_cache do it 'returns html content with state' do result = stream.html_with_state - expect(result.html).to eq("1234") + expect(result.html).to eq("<span class=\"\">1234</span>") end context 'follow-up state' do @@ -266,7 +269,7 @@ describe Gitlab::Ci::Trace::Stream, :clean_gitlab_redis_cache do result = stream.html_with_state(last_result.state) expect(result.append).to be_truthy - expect(result.html).to eq("5678") + expect(result.html).to eq("<span class=\"\">5678</span>") end end end @@ -302,11 +305,13 @@ describe Gitlab::Ci::Trace::Stream, :clean_gitlab_redis_cache do describe '#html' do shared_examples_for 'htmls' do it "returns html" do - expect(stream.html).to eq("12<br>34<br>56") + expect(stream.html).to eq( + "<span class=\"\">12<br/><span class=\"\">34<br/>"\ + "<span class=\"\">56</span></span></span>") end it "returns html for last line only" do - expect(stream.html(last_lines: 1)).to eq("56") + expect(stream.html(last_lines: 1)).to eq("<span class=\"\">56</span>") end end diff --git a/spec/lib/gitlab/cycle_analytics/code_stage_spec.rb b/spec/lib/gitlab/cycle_analytics/code_stage_spec.rb index e8fc67acf05..c738cc49c1f 100644 --- a/spec/lib/gitlab/cycle_analytics/code_stage_spec.rb +++ b/spec/lib/gitlab/cycle_analytics/code_stage_spec.rb @@ -4,5 +4,41 @@ require 'lib/gitlab/cycle_analytics/shared_stage_spec' describe Gitlab::CycleAnalytics::CodeStage do let(:stage_name) { :code } + let(:project) { create(:project) } + let!(:issue_1) { create(:issue, project: project, created_at: 90.minutes.ago) } + let!(:issue_2) { create(:issue, project: project, created_at: 60.minutes.ago) } + let!(:issue_3) { create(:issue, project: project, created_at: 60.minutes.ago) } + let!(:mr_1) { create(:merge_request, source_project: project, created_at: 15.minutes.ago) } + let!(:mr_2) { create(:merge_request, source_project: project, created_at: 10.minutes.ago, source_branch: 'A') } + let!(:mr_3) { create(:merge_request, source_project: project, created_at: 10.minutes.ago, source_branch: 'B') } + let(:stage) { described_class.new(project: project, options: { from: 2.days.ago, current_user: project.creator }) } + + before do + issue_1.metrics.update!(first_associated_with_milestone_at: 60.minutes.ago, first_mentioned_in_commit_at: 45.minutes.ago) + issue_2.metrics.update!(first_added_to_board_at: 60.minutes.ago, first_mentioned_in_commit_at: 40.minutes.ago) + issue_3.metrics.update!(first_added_to_board_at: 60.minutes.ago, first_mentioned_in_commit_at: 40.minutes.ago) + create(:merge_requests_closing_issues, merge_request: mr_1, issue: issue_1) + create(:merge_requests_closing_issues, merge_request: mr_2, issue: issue_2) + end + it_behaves_like 'base stage' + + describe '#median' do + around do |example| + Timecop.freeze { example.run } + end + + it 'counts median from issues with metrics' do + expect(stage.median).to eq(ISSUES_MEDIAN) + end + end + + describe '#events' do + it 'exposes merge requests that closes issues' do + result = stage.events + + expect(result.count).to eq(2) + expect(result.map { |event| event[:title] }).to contain_exactly(mr_1.title, mr_2.title) + end + end end diff --git a/spec/lib/gitlab/cycle_analytics/events_spec.rb b/spec/lib/gitlab/cycle_analytics/events_spec.rb index 397dd4e5d2c..f8b103c0fab 100644 --- a/spec/lib/gitlab/cycle_analytics/events_spec.rb +++ b/spec/lib/gitlab/cycle_analytics/events_spec.rb @@ -53,20 +53,28 @@ describe 'cycle analytics events' do describe '#plan_events' do let(:stage) { :plan } - it 'has a title' do - expect(events.first[:title]).not_to be_nil + before do + create_commit_referencing_issue(context) end - it 'has a sha short ID' do - expect(events.first[:short_sha]).not_to be_nil + it 'has the total time' do + expect(events.first[:total_time]).not_to be_empty + end + + it 'has a title' do + expect(events.first[:title]).to eq(context.title) end it 'has the URL' do - expect(events.first[:commit_url]).not_to be_nil + expect(events.first[:url]).not_to be_nil end - it 'has the total time' do - expect(events.first[:total_time]).not_to be_empty + it 'has an iid' do + expect(events.first[:iid]).to eq(context.iid.to_s) + end + + it 'has a created_at timestamp' do + expect(events.first[:created_at]).to end_with('ago') end it "has the author's URL" do @@ -78,12 +86,13 @@ describe 'cycle analytics events' do end it "has the author's name" do - expect(events.first[:author][:name]).not_to be_nil + expect(events.first[:author][:name]).to eq(context.author.name) end end describe '#code_events' do let(:stage) { :code } + let!(:merge_request) { MergeRequest.first } before do create_commit_referencing_issue(context) @@ -122,6 +131,7 @@ describe 'cycle analytics events' do let(:stage) { :test } let(:merge_request) { MergeRequest.first } + let!(:context) { create(:issue, project: project, created_at: 2.days.ago) } let!(:pipeline) do create(:ci_pipeline, @@ -137,6 +147,7 @@ describe 'cycle analytics events' do pipeline.run! pipeline.succeed! + merge_merge_requests_closing_issue(user, project, context) end it 'has the name' do @@ -180,6 +191,10 @@ describe 'cycle analytics events' do let(:stage) { :review } let!(:context) { create(:issue, project: project, created_at: 2.days.ago) } + before do + merge_merge_requests_closing_issue(user, project, context) + end + it 'has the total time' do expect(events.first[:total_time]).not_to be_empty end diff --git a/spec/lib/gitlab/cycle_analytics/issue_stage_spec.rb b/spec/lib/gitlab/cycle_analytics/issue_stage_spec.rb index 3127f01989d..3b6af9cbaed 100644 --- a/spec/lib/gitlab/cycle_analytics/issue_stage_spec.rb +++ b/spec/lib/gitlab/cycle_analytics/issue_stage_spec.rb @@ -3,6 +3,37 @@ require 'lib/gitlab/cycle_analytics/shared_stage_spec' describe Gitlab::CycleAnalytics::IssueStage do let(:stage_name) { :issue } + let(:project) { create(:project) } + let!(:issue_1) { create(:issue, project: project, created_at: 90.minutes.ago) } + let!(:issue_2) { create(:issue, project: project, created_at: 60.minutes.ago) } + let!(:issue_3) { create(:issue, project: project, created_at: 30.minutes.ago) } + let!(:issue_without_milestone) { create(:issue, project: project, created_at: 1.minute.ago) } + let(:stage) { described_class.new(project: project, options: { from: 2.days.ago, current_user: project.creator }) } + + before do + issue_1.metrics.update!(first_associated_with_milestone_at: 60.minutes.ago ) + issue_2.metrics.update!(first_added_to_board_at: 30.minutes.ago) + issue_3.metrics.update!(first_added_to_board_at: 15.minutes.ago) + end it_behaves_like 'base stage' + + describe '#median' do + around do |example| + Timecop.freeze { example.run } + end + + it 'counts median from issues with metrics' do + expect(stage.median).to eq(ISSUES_MEDIAN) + end + end + + describe '#events' do + it 'exposes issues with metrics' do + result = stage.events + + expect(result.count).to eq(3) + expect(result.map { |event| event[:title] }).to contain_exactly(issue_1.title, issue_2.title, issue_3.title) + end + end end diff --git a/spec/lib/gitlab/cycle_analytics/plan_stage_spec.rb b/spec/lib/gitlab/cycle_analytics/plan_stage_spec.rb index 4c715921ad6..506a8160412 100644 --- a/spec/lib/gitlab/cycle_analytics/plan_stage_spec.rb +++ b/spec/lib/gitlab/cycle_analytics/plan_stage_spec.rb @@ -3,6 +3,37 @@ require 'lib/gitlab/cycle_analytics/shared_stage_spec' describe Gitlab::CycleAnalytics::PlanStage do let(:stage_name) { :plan } + let(:project) { create(:project) } + let!(:issue_1) { create(:issue, project: project, created_at: 90.minutes.ago) } + let!(:issue_2) { create(:issue, project: project, created_at: 60.minutes.ago) } + let!(:issue_3) { create(:issue, project: project, created_at: 30.minutes.ago) } + let!(:issue_without_milestone) { create(:issue, project: project, created_at: 1.minute.ago) } + let(:stage) { described_class.new(project: project, options: { from: 2.days.ago, current_user: project.creator }) } + + before do + issue_1.metrics.update!(first_associated_with_milestone_at: 60.minutes.ago, first_mentioned_in_commit_at: 10.minutes.ago) + issue_2.metrics.update!(first_added_to_board_at: 30.minutes.ago, first_mentioned_in_commit_at: 20.minutes.ago) + issue_3.metrics.update!(first_added_to_board_at: 15.minutes.ago) + end it_behaves_like 'base stage' + + describe '#median' do + around do |example| + Timecop.freeze { example.run } + end + + it 'counts median from issues with metrics' do + expect(stage.median).to eq(ISSUES_MEDIAN) + end + end + + describe '#events' do + it 'exposes issues with metrics' do + result = stage.events + + expect(result.count).to eq(2) + expect(result.map { |event| event[:title] }).to contain_exactly(issue_1.title, issue_2.title) + end + end end diff --git a/spec/lib/gitlab/cycle_analytics/review_stage_spec.rb b/spec/lib/gitlab/cycle_analytics/review_stage_spec.rb index 1412c8dfa08..f072a9644e8 100644 --- a/spec/lib/gitlab/cycle_analytics/review_stage_spec.rb +++ b/spec/lib/gitlab/cycle_analytics/review_stage_spec.rb @@ -3,6 +3,43 @@ require 'lib/gitlab/cycle_analytics/shared_stage_spec' describe Gitlab::CycleAnalytics::ReviewStage do let(:stage_name) { :review } + let(:project) { create(:project) } + let!(:issue_1) { create(:issue, project: project, created_at: 90.minutes.ago) } + let!(:issue_2) { create(:issue, project: project, created_at: 60.minutes.ago) } + let!(:issue_3) { create(:issue, project: project, created_at: 60.minutes.ago) } + let!(:mr_1) { create(:merge_request, :closed, source_project: project, created_at: 60.minutes.ago) } + let!(:mr_2) { create(:merge_request, :closed, source_project: project, created_at: 40.minutes.ago, source_branch: 'A') } + let!(:mr_3) { create(:merge_request, source_project: project, created_at: 10.minutes.ago, source_branch: 'B') } + let!(:mr_4) { create(:merge_request, source_project: project, created_at: 10.minutes.ago, source_branch: 'C') } + let(:stage) { described_class.new(project: project, options: { from: 2.days.ago, current_user: project.creator }) } + + before do + mr_1.metrics.update!(merged_at: 30.minutes.ago) + mr_2.metrics.update!(merged_at: 10.minutes.ago) + + create(:merge_requests_closing_issues, merge_request: mr_1, issue: issue_1) + create(:merge_requests_closing_issues, merge_request: mr_2, issue: issue_2) + create(:merge_requests_closing_issues, merge_request: mr_3, issue: issue_3) + end it_behaves_like 'base stage' + + describe '#median' do + around do |example| + Timecop.freeze { example.run } + end + + it 'counts median from issues with metrics' do + expect(stage.median).to eq(ISSUES_MEDIAN) + end + end + + describe '#events' do + it 'exposes merge requests that close issues' do + result = stage.events + + expect(result.count).to eq(2) + expect(result.map { |event| event[:title] }).to contain_exactly(mr_1.title, mr_2.title) + end + end end diff --git a/spec/lib/gitlab/cycle_analytics/shared_stage_spec.rb b/spec/lib/gitlab/cycle_analytics/shared_stage_spec.rb index 08425acbfc8..1a4b572cc11 100644 --- a/spec/lib/gitlab/cycle_analytics/shared_stage_spec.rb +++ b/spec/lib/gitlab/cycle_analytics/shared_stage_spec.rb @@ -1,6 +1,8 @@ require 'spec_helper' shared_examples 'base stage' do + ISSUES_MEDIAN = 30.minutes.to_i + let(:stage) { described_class.new(project: double, options: {}) } before do diff --git a/spec/lib/gitlab/cycle_analytics/staging_stage_spec.rb b/spec/lib/gitlab/cycle_analytics/staging_stage_spec.rb index 8154b3ac701..17d5fbb9733 100644 --- a/spec/lib/gitlab/cycle_analytics/staging_stage_spec.rb +++ b/spec/lib/gitlab/cycle_analytics/staging_stage_spec.rb @@ -4,5 +4,46 @@ require 'lib/gitlab/cycle_analytics/shared_stage_spec' describe Gitlab::CycleAnalytics::StagingStage do let(:stage_name) { :staging } + let(:project) { create(:project) } + let!(:issue_1) { create(:issue, project: project, created_at: 90.minutes.ago) } + let!(:issue_2) { create(:issue, project: project, created_at: 60.minutes.ago) } + let!(:issue_3) { create(:issue, project: project, created_at: 60.minutes.ago) } + let!(:mr_1) { create(:merge_request, :closed, source_project: project, created_at: 60.minutes.ago) } + let!(:mr_2) { create(:merge_request, :closed, source_project: project, created_at: 40.minutes.ago, source_branch: 'A') } + let!(:mr_3) { create(:merge_request, source_project: project, created_at: 10.minutes.ago, source_branch: 'B') } + let(:build_1) { create(:ci_build, project: project) } + let(:build_2) { create(:ci_build, project: project) } + + let(:stage) { described_class.new(project: project, options: { from: 2.days.ago, current_user: project.creator }) } + + before do + mr_1.metrics.update!(merged_at: 80.minutes.ago, first_deployed_to_production_at: 50.minutes.ago, pipeline_id: build_1.commit_id) + mr_2.metrics.update!(merged_at: 60.minutes.ago, first_deployed_to_production_at: 30.minutes.ago, pipeline_id: build_2.commit_id) + mr_3.metrics.update!(merged_at: 10.minutes.ago, first_deployed_to_production_at: 3.days.ago, pipeline_id: create(:ci_build, project: project).commit_id) + + create(:merge_requests_closing_issues, merge_request: mr_1, issue: issue_1) + create(:merge_requests_closing_issues, merge_request: mr_2, issue: issue_2) + create(:merge_requests_closing_issues, merge_request: mr_3, issue: issue_3) + end + it_behaves_like 'base stage' + + describe '#median' do + around do |example| + Timecop.freeze { example.run } + end + + it 'counts median from issues with metrics' do + expect(stage.median).to eq(ISSUES_MEDIAN) + end + end + + describe '#events' do + it 'exposes builds connected to merge request' do + result = stage.events + + expect(result.count).to eq(2) + expect(result.map { |event| event[:name] }).to contain_exactly(build_1.name, build_2.name) + end + end end diff --git a/spec/lib/gitlab/gpg/commit_spec.rb b/spec/lib/gitlab/gpg/commit_spec.rb index 8229f0eb794..47e6f5d4220 100644 --- a/spec/lib/gitlab/gpg/commit_spec.rb +++ b/spec/lib/gitlab/gpg/commit_spec.rb @@ -109,6 +109,89 @@ describe Gitlab::Gpg::Commit do end end + context 'valid key signed using recent version of Gnupg' do + let!(:commit) { create :commit, project: project, sha: commit_sha, committer_email: GpgHelpers::User1.emails.first } + + let!(:user) { create(:user, email: GpgHelpers::User1.emails.first) } + + let!(:gpg_key) do + create :gpg_key, key: GpgHelpers::User1.public_key, user: user + end + + let!(:crypto) { instance_double(GPGME::Crypto) } + + before do + fake_signature = [ + GpgHelpers::User1.signed_commit_signature, + GpgHelpers::User1.signed_commit_base_data + ] + + allow(Gitlab::Git::Commit).to receive(:extract_signature_lazily) + .with(Gitlab::Git::Repository, commit_sha) + .and_return(fake_signature) + end + + it 'returns a valid signature' do + verified_signature = double('verified-signature', fingerprint: GpgHelpers::User1.fingerprint, valid?: true) + allow(GPGME::Crypto).to receive(:new).and_return(crypto) + allow(crypto).to receive(:verify).and_return(verified_signature) + + signature = described_class.new(commit).signature + + expect(signature).to have_attributes( + commit_sha: commit_sha, + project: project, + gpg_key: gpg_key, + gpg_key_primary_keyid: GpgHelpers::User1.primary_keyid, + gpg_key_user_name: GpgHelpers::User1.names.first, + gpg_key_user_email: GpgHelpers::User1.emails.first, + verification_status: 'verified' + ) + end + end + + context 'valid key signed using older version of Gnupg' do + let!(:commit) { create :commit, project: project, sha: commit_sha, committer_email: GpgHelpers::User1.emails.first } + + let!(:user) { create(:user, email: GpgHelpers::User1.emails.first) } + + let!(:gpg_key) do + create :gpg_key, key: GpgHelpers::User1.public_key, user: user + end + + let!(:crypto) { instance_double(GPGME::Crypto) } + + before do + fake_signature = [ + GpgHelpers::User1.signed_commit_signature, + GpgHelpers::User1.signed_commit_base_data + ] + + allow(Gitlab::Git::Commit).to receive(:extract_signature_lazily) + .with(Gitlab::Git::Repository, commit_sha) + .and_return(fake_signature) + end + + it 'returns a valid signature' do + keyid = GpgHelpers::User1.fingerprint.last(16) + verified_signature = double('verified-signature', fingerprint: keyid, valid?: true) + allow(GPGME::Crypto).to receive(:new).and_return(crypto) + allow(crypto).to receive(:verify).and_return(verified_signature) + + signature = described_class.new(commit).signature + + expect(signature).to have_attributes( + commit_sha: commit_sha, + project: project, + gpg_key: gpg_key, + gpg_key_primary_keyid: GpgHelpers::User1.primary_keyid, + gpg_key_user_name: GpgHelpers::User1.names.first, + gpg_key_user_email: GpgHelpers::User1.emails.first, + verification_status: 'verified' + ) + end + end + context 'commit signed with a subkey' do let!(:commit) { create :commit, project: project, sha: commit_sha, committer_email: GpgHelpers::User3.emails.first } diff --git a/spec/lib/gitlab/import_export/all_models.yml b/spec/lib/gitlab/import_export/all_models.yml index 41ef60dc6f8..7a250603b6b 100644 --- a/spec/lib/gitlab/import_export/all_models.yml +++ b/spec/lib/gitlab/import_export/all_models.yml @@ -203,6 +203,8 @@ deploy_keys: services: - project - service_hook +- jira_tracker_data +- issue_tracker_data hooks: - project - web_hook_logs diff --git a/spec/migrations/migrate_k8s_service_integration_spec.rb b/spec/migrations/migrate_k8s_service_integration_spec.rb new file mode 100644 index 00000000000..4dd0c09632a --- /dev/null +++ b/spec/migrations/migrate_k8s_service_integration_spec.rb @@ -0,0 +1,161 @@ +# frozen_string_literal: true + +require 'spec_helper' +require Rails.root.join('db', 'post_migrate', '20190517153211_migrate_k8s_service_integration.rb') + +describe MigrateK8sServiceIntegration, :migration do + context 'template service' do + context 'with namespace' do + let!(:service) do + MigrateK8sServiceIntegration::Service.create!( + active: true, + template: true, + category: 'deployment', + type: 'KubernetesService', + properties: "{\"namespace\":\"prod\",\"api_url\":\"https://sample.kubernetes.com\",\"ca_pem\":\"ca_pem-sample\",\"token\":\"token-sample\"}" + ) + end + + let(:cluster) { MigrateK8sServiceIntegration::Cluster.instance_type.last! } + let(:platform) { cluster.platform_kubernetes } + + it 'migrates the KubernetesService template to Platform::Kubernetes' do + expect { migrate! }.to change { MigrateK8sServiceIntegration::Cluster.count }.by(1) + + expect(cluster).to be_enabled + expect(cluster).to be_user + expect(cluster).not_to be_managed + expect(cluster.environment_scope).to eq('*') + expect(platform.api_url).to eq('https://sample.kubernetes.com') + expect(platform.ca_cert).to eq('ca_pem-sample') + expect(platform.namespace).to eq('prod') + expect(platform.token).to eq('token-sample') + end + end + + context 'without namespace' do + let!(:service) do + MigrateK8sServiceIntegration::Service.create!( + active: true, + template: true, + category: 'deployment', + type: 'KubernetesService', + properties: "{\"namespace\":\"\",\"api_url\":\"https://sample.kubernetes.com\",\"ca_pem\":\"ca_pem-sample\",\"token\":\"token-sample\"}" + ) + end + + let(:cluster) { MigrateK8sServiceIntegration::Cluster.instance_type.last! } + let(:platform) { cluster.platform_kubernetes } + + it 'migrates the KubernetesService template to Platform::Kubernetes' do + expect { migrate! }.to change { MigrateK8sServiceIntegration::Cluster.count }.by(1) + + expect(cluster).to be_enabled + expect(cluster).to be_user + expect(cluster).not_to be_managed + expect(cluster.environment_scope).to eq('*') + expect(platform.api_url).to eq('https://sample.kubernetes.com') + expect(platform.ca_cert).to eq('ca_pem-sample') + expect(platform.namespace).to be_nil + expect(platform.token).to eq('token-sample') + end + end + + context 'with nullified parameters' do + let!(:service) do + MigrateK8sServiceIntegration::Service.create!( + active: true, + template: true, + category: 'deployment', + type: 'KubernetesService', + properties: "{}" + ) + end + + it 'does not migrate the KubernetesService' do + expect { migrate! }.not_to change { MigrateK8sServiceIntegration::Cluster.count } + end + end + + context 'when disabled' do + let!(:service) do + MigrateK8sServiceIntegration::Service.create!( + active: false, + template: true, + category: 'deployment', + type: 'KubernetesService', + properties: "{\"namespace\":\"prod\",\"api_url\":\"https://sample.kubernetes.com\",\"ca_pem\":\"ca_pem-sample\",\"token\":\"token-sample\"}" + ) + end + + let(:cluster) { MigrateK8sServiceIntegration::Cluster.instance_type.last! } + let(:platform) { cluster.platform_kubernetes } + + it 'migrates the KubernetesService template to Platform::Kubernetes' do + expect { migrate! }.to change { MigrateK8sServiceIntegration::Cluster.count }.by(1) + + expect(cluster).not_to be_enabled + expect(cluster).to be_user + expect(cluster).not_to be_managed + expect(cluster.environment_scope).to eq('*') + expect(platform.api_url).to eq('https://sample.kubernetes.com') + expect(platform.ca_cert).to eq('ca_pem-sample') + expect(platform.namespace).to eq('prod') + expect(platform.token).to eq('token-sample') + end + end + + context 'when an instance cluster already exists' do + let!(:service) do + MigrateK8sServiceIntegration::Service.create!( + active: true, + template: true, + category: 'deployment', + type: 'KubernetesService', + properties: "{\"namespace\":\"prod\",\"api_url\":\"https://sample.kubernetes.com\",\"ca_pem\":\"ca_pem-sample\",\"token\":\"token-sample\"}" + ) + end + + let!(:existing_cluster) do + MigrateK8sServiceIntegration::Cluster.create!( + name: 'test-cluster', + cluster_type: :instance_type, + managed: true, + provider_type: :user, + platform_type: :kubernetes + ) + end + let(:new_cluster) { MigrateK8sServiceIntegration::Cluster.instance_type.last! } + let(:platform) { new_cluster.platform_kubernetes } + + it 'migrates the KubernetesService template to disabled Platform::Kubernetes' do + expect { migrate! }.to change { MigrateK8sServiceIntegration::Cluster.count }.by(1) + + expect(new_cluster).not_to be_enabled + expect(new_cluster).to be_user + expect(new_cluster).not_to be_managed + expect(new_cluster.environment_scope).to eq('*') + expect(platform.api_url).to eq('https://sample.kubernetes.com') + expect(platform.ca_cert).to eq('ca_pem-sample') + expect(platform.namespace).to eq('prod') + expect(platform.token).to eq('token-sample') + end + end + end + + context 'non-template service' do + let!(:service) do + MigrateK8sServiceIntegration::Service.create!( + active: true, + template: false, + category: 'deployment', + type: 'KubernetesService', + properties: "{\"namespace\":\"prod\",\"api_url\":\"https://sample.kubernetes.com\",\"ca_pem\":\"ca_pem-sample\",\"token\":\"token-sample\"}" + ) + end + + it 'does not migrate the KubernetesService' do + expect { migrate! }.not_to change { MigrateK8sServiceIntegration::Cluster.count } + end + end +end diff --git a/spec/models/ci/pipeline_spec.rb b/spec/models/ci/pipeline_spec.rb index a8701f0efa4..6ebc6337d50 100644 --- a/spec/models/ci/pipeline_spec.rb +++ b/spec/models/ci/pipeline_spec.rb @@ -962,7 +962,11 @@ describe Ci::Pipeline, :mailer do end context 'when kubernetes is active' do - shared_examples 'same behavior between KubernetesService and Platform::Kubernetes' do + context 'when user configured kubernetes from CI/CD > Clusters' do + let!(:cluster) { create(:cluster, :project, :provided_by_gcp) } + let(:project) { cluster.project } + let(:pipeline) { build(:ci_pipeline, project: project, config: config) } + it 'returns seeds for kubernetes dependent job' do seeds = pipeline.stage_seeds @@ -971,21 +975,6 @@ describe Ci::Pipeline, :mailer do expect(seeds.dig(1, 0, :name)).to eq 'production' end end - - context 'when user configured kubernetes from Integration > Kubernetes' do - let(:project) { create(:kubernetes_project) } - let(:pipeline) { build(:ci_pipeline, project: project, config: config) } - - it_behaves_like 'same behavior between KubernetesService and Platform::Kubernetes' - end - - context 'when user configured kubernetes from CI/CD > Clusters' do - let!(:cluster) { create(:cluster, :project, :provided_by_gcp) } - let(:project) { cluster.project } - let(:pipeline) { build(:ci_pipeline, project: project, config: config) } - - it_behaves_like 'same behavior between KubernetesService and Platform::Kubernetes' - end end context 'when kubernetes is not active' do @@ -1679,23 +1668,13 @@ describe Ci::Pipeline, :mailer do describe '#has_kubernetes_active?' do context 'when kubernetes is active' do - shared_examples 'same behavior between KubernetesService and Platform::Kubernetes' do - it 'returns true' do - expect(pipeline).to have_kubernetes_active - end - end - - context 'when user configured kubernetes from Integration > Kubernetes' do - let(:project) { create(:kubernetes_project) } - - it_behaves_like 'same behavior between KubernetesService and Platform::Kubernetes' - end - context 'when user configured kubernetes from CI/CD > Clusters' do let!(:cluster) { create(:cluster, :project, :provided_by_gcp) } let(:project) { cluster.project } - it_behaves_like 'same behavior between KubernetesService and Platform::Kubernetes' + it 'returns true' do + expect(pipeline).to have_kubernetes_active + end end end @@ -2736,7 +2715,7 @@ describe Ci::Pipeline, :mailer do create(:ci_pipeline, project: project, sha: project.commit('master').sha, - user: create(:user)) + user: project.owner) end before do diff --git a/spec/models/clusters/cluster_spec.rb b/spec/models/clusters/cluster_spec.rb index f206bb41f45..52661178d76 100644 --- a/spec/models/clusters/cluster_spec.rb +++ b/spec/models/clusters/cluster_spec.rb @@ -514,19 +514,43 @@ describe Clusters::Cluster, :use_clean_rails_memory_store_caching do subject { cluster.allow_user_defined_namespace? } context 'project type cluster' do - it { is_expected.to be_truthy } + context 'gitlab managed' do + it { is_expected.to be_truthy } + end + + context 'not managed' do + let(:cluster) { create(:cluster, :provided_by_gcp, managed: false) } + + it { is_expected.to be_truthy } + end end context 'group type cluster' do - let(:cluster) { create(:cluster, :provided_by_gcp, :group) } + context 'gitlab managed' do + let(:cluster) { create(:cluster, :provided_by_gcp, :group) } - it { is_expected.to be_falsey } + it { is_expected.to be_falsey } + end + + context 'not managed' do + let(:cluster) { create(:cluster, :provided_by_gcp, :group, managed: false) } + + it { is_expected.to be_truthy } + end end context 'instance type cluster' do - let(:cluster) { create(:cluster, :provided_by_gcp, :instance) } + context 'gitlab managed' do + let(:cluster) { create(:cluster, :provided_by_gcp, :instance) } - it { is_expected.to be_falsey } + it { is_expected.to be_falsey } + end + + context 'not managed' do + let(:cluster) { create(:cluster, :provided_by_gcp, :instance, managed: false) } + + it { is_expected.to be_truthy } + end end end @@ -555,6 +579,63 @@ describe Clusters::Cluster, :use_clean_rails_memory_store_caching do end end + describe '#find_or_initialize_kubernetes_namespace_for_project' do + let(:cluster) { create(:cluster, :project, :provided_by_gcp) } + let(:project) { cluster.projects.first } + + subject { cluster.find_or_initialize_kubernetes_namespace_for_project(project) } + + context 'kubernetes namespace exists' do + context 'with no service account token' do + let!(:kubernetes_namespace) { create(:cluster_kubernetes_namespace, project: project, cluster: cluster) } + + it { is_expected.to eq kubernetes_namespace } + end + + context 'with a service account token' do + let!(:kubernetes_namespace) { create(:cluster_kubernetes_namespace, :with_token, project: project, cluster: cluster) } + + it { is_expected.to eq kubernetes_namespace } + end + end + + context 'kubernetes namespace does not exist' do + it 'initializes a new namespace and sets default values' do + expect(subject).to be_new_record + expect(subject.project).to eq project + expect(subject.cluster).to eq cluster + expect(subject.namespace).to be_present + expect(subject.service_account_name).to be_present + end + end + + context 'a custom scope is provided' do + let(:scope) { cluster.kubernetes_namespaces.has_service_account_token } + + subject { cluster.find_or_initialize_kubernetes_namespace_for_project(project, scope: scope) } + + context 'kubernetes namespace exists' do + context 'with no service account token' do + let!(:kubernetes_namespace) { create(:cluster_kubernetes_namespace, project: project, cluster: cluster) } + + it 'initializes a new namespace and sets default values' do + expect(subject).to be_new_record + expect(subject.project).to eq project + expect(subject.cluster).to eq cluster + expect(subject.namespace).to be_present + expect(subject.service_account_name).to be_present + end + end + + context 'with a service account token' do + let!(:kubernetes_namespace) { create(:cluster_kubernetes_namespace, :with_token, project: project, cluster: cluster) } + + it { is_expected.to eq kubernetes_namespace } + end + end + end + end + describe '#predefined_variables' do subject { cluster.predefined_variables } diff --git a/spec/models/clusters/platforms/kubernetes_spec.rb b/spec/models/clusters/platforms/kubernetes_spec.rb index c485850c16e..1fb3a8de808 100644 --- a/spec/models/clusters/platforms/kubernetes_spec.rb +++ b/spec/models/clusters/platforms/kubernetes_spec.rb @@ -223,19 +223,33 @@ describe Clusters::Platforms::Kubernetes, :use_clean_rails_memory_store_caching let(:namespace) { 'namespace-123' } it { is_expected.to eq(namespace) } + + context 'kubernetes namespace is present but has no service account token' do + let!(:kubernetes_namespace) { create(:cluster_kubernetes_namespace, cluster: cluster) } + + it { is_expected.to eq(namespace) } + end end context 'with no namespace assigned' do let(:namespace) { nil } context 'when kubernetes namespace is present' do - let(:kubernetes_namespace) { create(:cluster_kubernetes_namespace, cluster: cluster) } + let(:kubernetes_namespace) { create(:cluster_kubernetes_namespace, :with_token, cluster: cluster) } before do kubernetes_namespace end it { is_expected.to eq(kubernetes_namespace.namespace) } + + context 'kubernetes namespace has no service account token' do + before do + kubernetes_namespace.update!(namespace: 'old-namespace', service_account_token: nil) + end + + it { is_expected.to eq("#{project.path}-#{project.id}") } + end end context 'when kubernetes namespace is not present' do @@ -284,6 +298,46 @@ describe Clusters::Platforms::Kubernetes, :use_clean_rails_memory_store_caching { key: 'KUBE_TOKEN', value: kubernetes_namespace.service_account_token, public: false, masked: true } ) end + + context 'the cluster has been set to unmanaged after the namespace was created' do + before do + cluster.update!(managed: false) + end + + it_behaves_like 'setting variables' + + it 'sets KUBE_TOKEN from the platform' do + expect(subject).to include( + { key: 'KUBE_TOKEN', value: kubernetes.token, public: false, masked: true } + ) + end + + context 'the platform has a custom namespace set' do + before do + kubernetes.update!(namespace: 'custom-namespace') + end + + it 'sets KUBE_NAMESPACE from the platform' do + expect(subject).to include( + { key: 'KUBE_NAMESPACE', value: kubernetes.namespace, public: true, masked: false } + ) + end + end + + context 'there is no namespace specified on the platform' do + let(:project) { cluster.project } + + before do + kubernetes.update!(namespace: nil) + end + + it 'sets KUBE_NAMESPACE to a default for the project' do + expect(subject).to include( + { key: 'KUBE_NAMESPACE', value: "#{project.path}-#{project.id}", public: true, masked: false } + ) + end + end + end end context 'namespace is provided' do diff --git a/spec/models/environment_spec.rb b/spec/models/environment_spec.rb index 7233d2454c6..379dda1f5c4 100644 --- a/spec/models/environment_spec.rb +++ b/spec/models/environment_spec.rb @@ -515,29 +515,19 @@ describe Environment do context 'when the environment is available' do context 'with a deployment service' do - shared_examples 'same behavior between KubernetesService and Platform::Kubernetes' do - context 'and a deployment' do + context 'when user configured kubernetes from CI/CD > Clusters' do + let!(:cluster) { create(:cluster, :project, :provided_by_gcp) } + let(:project) { cluster.project } + + context 'with deployment' do let!(:deployment) { create(:deployment, :success, environment: environment) } it { is_expected.to be_truthy } end - context 'but no deployments' do + context 'without deployments' do it { is_expected.to be_falsy } end end - - context 'when user configured kubernetes from Integration > Kubernetes' do - let(:project) { create(:kubernetes_project) } - - it_behaves_like 'same behavior between KubernetesService and Platform::Kubernetes' - end - - context 'when user configured kubernetes from CI/CD > Clusters' do - let!(:cluster) { create(:cluster, :project, :provided_by_gcp) } - let(:project) { cluster.project } - - it_behaves_like 'same behavior between KubernetesService and Platform::Kubernetes' - end end context 'without a deployment service' do @@ -546,8 +536,6 @@ describe Environment do end context 'when the environment is unavailable' do - let(:project) { create(:kubernetes_project) } - before do environment.stop end @@ -590,7 +578,10 @@ describe Environment do allow(environment).to receive(:has_terminals?).and_return(true) end - shared_examples 'same behavior between KubernetesService and Platform::Kubernetes' do + context 'when user configured kubernetes from CI/CD > Clusters' do + let!(:cluster) { create(:cluster, :project, :provided_by_gcp) } + let(:project) { cluster.project } + it 'returns the terminals from the deployment service' do expect(environment.deployment_platform) .to receive(:terminals).with(environment) @@ -599,19 +590,6 @@ describe Environment do is_expected.to eq(:fake_terminals) end end - - context 'when user configured kubernetes from Integration > Kubernetes' do - let(:project) { create(:kubernetes_project) } - - it_behaves_like 'same behavior between KubernetesService and Platform::Kubernetes' - end - - context 'when user configured kubernetes from CI/CD > Clusters' do - let!(:cluster) { create(:cluster, :project, :provided_by_gcp) } - let(:project) { cluster.project } - - it_behaves_like 'same behavior between KubernetesService and Platform::Kubernetes' - end end context 'when the environment does not have terminals' do diff --git a/spec/models/lfs_object_spec.rb b/spec/models/lfs_object_spec.rb index 3d4d4b7d795..85bfc3f1387 100644 --- a/spec/models/lfs_object_spec.rb +++ b/spec/models/lfs_object_spec.rb @@ -3,6 +3,20 @@ require 'spec_helper' describe LfsObject do + it 'has a distinct has_many :projects relation through lfs_objects_projects' do + lfs_object = create(:lfs_object) + project = create(:project) + [:project, :design].each do |repository_type| + create(:lfs_objects_project, project: project, + lfs_object: lfs_object, + repository_type: repository_type) + end + + expect(lfs_object.lfs_objects_projects.size).to eq(2) + expect(lfs_object.projects.size).to eq(1) + expect(lfs_object.projects.to_a).to eql([project]) + end + describe '#local_store?' do it 'returns true when file_store is equal to LfsObjectUploader::Store::LOCAL' do subject.file_store = LfsObjectUploader::Store::LOCAL diff --git a/spec/models/lfs_objects_project_spec.rb b/spec/models/lfs_objects_project_spec.rb index 3e86ee38566..e320f873989 100644 --- a/spec/models/lfs_objects_project_spec.rb +++ b/spec/models/lfs_objects_project_spec.rb @@ -20,8 +20,8 @@ describe LfsObjectsProject do it 'validates object id' do is_expected.to validate_uniqueness_of(:lfs_object_id) - .scoped_to(:project_id) - .with_message("already exists in project") + .scoped_to(:project_id, :repository_type) + .with_message("already exists in repository") end end diff --git a/spec/models/project_services/hipchat_service_spec.rb b/spec/models/project_services/hipchat_service_spec.rb index a04b984c1f6..a1bd0855708 100644 --- a/spec/models/project_services/hipchat_service_spec.rb +++ b/spec/models/project_services/hipchat_service_spec.rb @@ -301,7 +301,7 @@ describe HipchatService do end context 'pipeline events' do - let(:pipeline) { create(:ci_empty_pipeline, user: create(:user)) } + let(:pipeline) { create(:ci_empty_pipeline, user: project.owner) } let(:data) { Gitlab::DataBuilder::Pipeline.build(pipeline) } context 'for failed' do diff --git a/spec/models/project_services/issue_tracker_data_spec.rb b/spec/models/project_services/issue_tracker_data_spec.rb new file mode 100644 index 00000000000..aaab654f874 --- /dev/null +++ b/spec/models/project_services/issue_tracker_data_spec.rb @@ -0,0 +1,35 @@ +# frozen_string_literal: true + +require 'spec_helper' + +describe IssueTrackerData do + let(:service) { create(:custom_issue_tracker_service, active: false, properties: {}) } + + describe 'Associations' do + it { is_expected.to belong_to :service } + end + + describe 'Validations' do + subject { described_class.new(service: service) } + + context 'url validations' do + context 'when service is inactive' do + it { is_expected.not_to validate_presence_of(:project_url) } + it { is_expected.not_to validate_presence_of(:issues_url) } + end + + context 'when service is active' do + before do + service.update(active: true) + end + + it_behaves_like 'issue tracker service URL attribute', :project_url + it_behaves_like 'issue tracker service URL attribute', :issues_url + it_behaves_like 'issue tracker service URL attribute', :new_issue_url + + it { is_expected.to validate_presence_of(:project_url) } + it { is_expected.to validate_presence_of(:issues_url) } + end + end + end +end diff --git a/spec/models/project_services/jira_tracker_data_spec.rb b/spec/models/project_services/jira_tracker_data_spec.rb new file mode 100644 index 00000000000..1b6ece8531b --- /dev/null +++ b/spec/models/project_services/jira_tracker_data_spec.rb @@ -0,0 +1,42 @@ +# frozen_string_literal: true + +require 'spec_helper' + +describe JiraTrackerData do + let(:service) { create(:jira_service, active: false, properties: {}) } + + describe 'Associations' do + it { is_expected.to belong_to(:service) } + end + + describe 'Validations' do + subject { described_class.new(service: service) } + + context 'jira_issue_transition_id' do + it { is_expected.to allow_value(nil).for(:jira_issue_transition_id) } + it { is_expected.to allow_value('1,2,3').for(:jira_issue_transition_id) } + it { is_expected.to allow_value('1;2;3').for(:jira_issue_transition_id) } + it { is_expected.not_to allow_value('a,b,cd').for(:jira_issue_transition_id) } + end + + context 'url validations' do + context 'when service is inactive' do + it { is_expected.not_to validate_presence_of(:url) } + it { is_expected.not_to validate_presence_of(:username) } + it { is_expected.not_to validate_presence_of(:password) } + end + + context 'when service is active' do + before do + service.update(active: true) + end + + it_behaves_like 'issue tracker service URL attribute', :url + + it { is_expected.to validate_presence_of(:url) } + it { is_expected.to validate_presence_of(:username) } + it { is_expected.to validate_presence_of(:password) } + end + end + end +end diff --git a/spec/models/project_services/kubernetes_service_spec.rb b/spec/models/project_services/kubernetes_service_spec.rb index 2fce120381b..34ee1eafd5c 100644 --- a/spec/models/project_services/kubernetes_service_spec.rb +++ b/spec/models/project_services/kubernetes_service_spec.rb @@ -17,6 +17,7 @@ describe KubernetesService, :use_clean_rails_memory_store_caching do context 'when service is active' do before do subject.active = true + subject.skip_deprecation_validation = true end it { is_expected.not_to validate_presence_of(:namespace) } @@ -67,6 +68,7 @@ describe KubernetesService, :use_clean_rails_memory_store_caching do before do kubernetes_service.update_attribute(:active, false) + kubernetes_service.skip_deprecation_validation = false kubernetes_service.properties['namespace'] = "foo" end @@ -80,19 +82,11 @@ describe KubernetesService, :use_clean_rails_memory_store_caching do end end - context 'with a non-deprecated service' do - let(:kubernetes_service) { create(:kubernetes_service) } - - it 'updates attributes' do - kubernetes_service.properties['namespace'] = 'foo' - expect(kubernetes_service.save).to be_truthy - end - end - context 'with an active and deprecated service' do let(:kubernetes_service) { create(:kubernetes_service) } before do + kubernetes_service.skip_deprecation_validation = false kubernetes_service.active = false kubernetes_service.properties['namespace'] = 'foo' kubernetes_service.save @@ -110,19 +104,6 @@ describe KubernetesService, :use_clean_rails_memory_store_caching do expect(kubernetes_service.properties['namespace']).to eq("foo") end end - - context 'with a template service' do - let(:kubernetes_service) { create(:kubernetes_service, template: true, active: false) } - - before do - kubernetes_service.properties['namespace'] = 'foo' - end - - it 'updates attributes' do - expect(kubernetes_service.save).to be_truthy - expect(kubernetes_service.properties['namespace']).to eq('foo') - end - end end describe '#initialize_properties' do @@ -393,17 +374,8 @@ describe KubernetesService, :use_clean_rails_memory_store_caching do describe "#deprecated?" do let(:kubernetes_service) { create(:kubernetes_service) } - context 'with an active kubernetes service' do - it 'returns false' do - expect(kubernetes_service.deprecated?).to be_falsy - end - end - - context 'with a inactive kubernetes service' do - it 'returns true' do - kubernetes_service.update_attribute(:active, false) - expect(kubernetes_service.deprecated?).to be_truthy - end + it 'returns true' do + expect(kubernetes_service.deprecated?).to be_truthy end end @@ -414,12 +386,6 @@ describe KubernetesService, :use_clean_rails_memory_store_caching do expect(kubernetes_service.deprecation_message).to match(/Kubernetes service integration has been deprecated/) end - context 'if the services is active' do - it 'returns a message' do - expect(kubernetes_service.deprecation_message).to match(/Your Kubernetes cluster information on this page is still editable/) - end - end - context 'if the service is not active' do it 'returns a message' do kubernetes_service.update_attribute(:active, false) diff --git a/spec/models/project_spec.rb b/spec/models/project_spec.rb index aad08b9d4aa..20b98b5eb85 100644 --- a/spec/models/project_spec.rb +++ b/spec/models/project_spec.rb @@ -103,6 +103,20 @@ describe Project do expect(described_class.reflect_on_association(:merge_requests).has_inverse?).to eq(:target_project) end + it 'has a distinct has_many :lfs_objects relation through lfs_objects_projects' do + project = create(:project) + lfs_object = create(:lfs_object) + [:project, :design].each do |repository_type| + create(:lfs_objects_project, project: project, + lfs_object: lfs_object, + repository_type: repository_type) + end + + expect(project.lfs_objects_projects.size).to eq(2) + expect(project.lfs_objects.size).to eq(1) + expect(project.lfs_objects.to_a).to eql([lfs_object]) + end + context 'after initialized' do it "has a project_feature" do expect(described_class.new.project_feature).to be_present @@ -1479,11 +1493,28 @@ describe Project do end context 'when set to INTERNAL in application settings' do + using RSpec::Parameterized::TableSyntax + before do stub_application_setting(default_project_visibility: Gitlab::VisibilityLevel::INTERNAL) end it { is_expected.to eq(Gitlab::VisibilityLevel::INTERNAL) } + + where(:attribute_name, :value) do + :visibility | 'public' + :visibility_level | Gitlab::VisibilityLevel::PUBLIC + 'visibility' | 'public' + 'visibility_level' | Gitlab::VisibilityLevel::PUBLIC + end + + with_them do + it 'sets the visibility level' do + proj = described_class.new(attribute_name => value, name: 'test', path: 'test') + + expect(proj.visibility_level).to eq(Gitlab::VisibilityLevel::PUBLIC) + end + end end end @@ -2621,7 +2652,10 @@ describe Project do end context 'when project has a deployment service' do - shared_examples 'same behavior between KubernetesService and Platform::Kubernetes' do + context 'when user configured kubernetes from CI/CD > Clusters and KubernetesNamespace migration has not been executed' do + let!(:cluster) { create(:cluster, :project, :provided_by_gcp) } + let(:project) { cluster.project } + it 'returns variables from this service' do expect(project.deployment_variables).to include( { key: 'KUBE_TOKEN', value: project.deployment_platform.token, public: false, masked: true } @@ -2629,19 +2663,6 @@ describe Project do end end - context 'when user configured kubernetes from Integration > Kubernetes' do - let(:project) { create(:kubernetes_project) } - - it_behaves_like 'same behavior between KubernetesService and Platform::Kubernetes' - end - - context 'when user configured kubernetes from CI/CD > Clusters and KubernetesNamespace migration has not been executed' do - let!(:cluster) { create(:cluster, :project, :provided_by_gcp) } - let(:project) { cluster.project } - - it_behaves_like 'same behavior between KubernetesService and Platform::Kubernetes' - end - context 'when user configured kubernetes from CI/CD > Clusters and KubernetesNamespace migration has been executed' do let!(:kubernetes_namespace) { create(:cluster_kubernetes_namespace, :with_token) } let!(:cluster) { kubernetes_namespace.cluster } @@ -3447,6 +3468,7 @@ describe Project do before do allow(project).to receive(:gitlab_shell).and_return(gitlab_shell) + stub_application_setting(hashed_storage_enabled: false) end describe '#base_dir' do @@ -3553,10 +3575,6 @@ describe Project do let(:hashed_prefix) { File.join('@hashed', hash[0..1], hash[2..3]) } let(:hashed_path) { File.join(hashed_prefix, hash) } - before do - stub_application_setting(hashed_storage_enabled: true) - end - describe '#legacy_storage?' do it 'returns false' do expect(project.legacy_storage?).to be_falsey @@ -4698,10 +4716,6 @@ describe Project do subject { project.object_pool_params } - before do - stub_application_setting(hashed_storage_enabled: true) - end - context 'when the objects cannot be pooled' do let(:project) { create(:project, :repository, :private) } @@ -4747,10 +4761,6 @@ describe Project do context 'when objects are poolable' do let(:project) { create(:project, :repository, :public) } - before do - stub_application_setting(hashed_storage_enabled: true) - end - it { is_expected.to be_git_objects_poolable } end end diff --git a/spec/models/service_spec.rb b/spec/models/service_spec.rb index 64db32781fe..d442c73c118 100644 --- a/spec/models/service_spec.rb +++ b/spec/models/service_spec.rb @@ -6,6 +6,8 @@ describe Service do describe "Associations" do it { is_expected.to belong_to :project } it { is_expected.to have_one :service_hook } + it { is_expected.to have_one :jira_tracker_data } + it { is_expected.to have_one :issue_tracker_data } end describe 'Validations' do @@ -80,7 +82,7 @@ describe Service do context 'when template is invalid' do it 'sets service template to inactive when template is invalid' do project = create(:project) - template = KubernetesService.new(template: true, active: true) + template = build(:prometheus_service, template: true, active: true, properties: {}) template.save(validate: false) service = described_class.build_from_template(project.id, template) @@ -307,10 +309,10 @@ describe Service do end describe '.find_by_template' do - let!(:kubernetes_service) { create(:kubernetes_service, template: true) } + let!(:service) { create(:service, template: true) } it 'returns service template' do - expect(KubernetesService.find_by_template).to eq(kubernetes_service) + expect(described_class.find_by_template).to eq(service) end end diff --git a/spec/policies/project_policy_spec.rb b/spec/policies/project_policy_spec.rb index ed0e82ef179..4b723a52b51 100644 --- a/spec/policies/project_policy_spec.rb +++ b/spec/policies/project_policy_spec.rb @@ -39,7 +39,7 @@ describe ProjectPolicy do admin_milestone admin_merge_request update_merge_request create_commit_status update_commit_status create_build update_build create_pipeline update_pipeline create_merge_request_from create_wiki push_code - resolve_note create_container_image update_container_image + resolve_note create_container_image update_container_image destroy_container_image create_environment create_deployment create_release update_release ] end diff --git a/spec/requests/api/container_registry_spec.rb b/spec/requests/api/container_registry_spec.rb index ea035a8be4a..4ad15ed6bea 100644 --- a/spec/requests/api/container_registry_spec.rb +++ b/spec/requests/api/container_registry_spec.rb @@ -201,10 +201,10 @@ describe API::ContainerRegistry do describe 'DELETE /projects/:id/registry/repositories/:repository_id/tags/:tag_name' do subject { delete api("/projects/#{project.id}/registry/repositories/#{root_repository.id}/tags/rootA", api_user) } - it_behaves_like 'being disallowed', :developer + it_behaves_like 'being disallowed', :reporter - context 'for maintainer' do - let(:api_user) { maintainer } + context 'for developer' do + let(:api_user) { developer } before do stub_container_registry_tags(repository: root_repository.path, tags: %w(rootA), with_manifest: true) diff --git a/spec/requests/api/graphql/tasks/task_completion_status_spec.rb b/spec/requests/api/graphql/tasks/task_completion_status_spec.rb new file mode 100644 index 00000000000..c457a6d7c25 --- /dev/null +++ b/spec/requests/api/graphql/tasks/task_completion_status_spec.rb @@ -0,0 +1,60 @@ +# frozen_string_literal: true + +require 'spec_helper' + +describe 'getting task completion status information' do + include GraphqlHelpers + + DESCRIPTION_0_DONE = '- [ ] task 1\n- [ ] task 2' + DESCRIPTION_1_DONE = '- [x] task 1\n- [ ] task 2' + DESCRIPTION_2_DONE = '- [x] task 1\n- [x] task 2' + + set(:user1) { create(:user) } + set(:project) { create(:project, :repository, :public) } + + let(:fields) do + <<~QUERY + taskCompletionStatus { + count, + completedCount + } + QUERY + end + + def create_task_completion_status_query_for(type, iid) + graphql_query_for( + 'project', + { 'fullPath' => project.full_path }, + query_graphql_field(type, { iid: iid }, fields) + ) + end + + shared_examples_for 'graphql task completion status provider' do |type| + it 'returns the expected task completion status' do + post_graphql(create_task_completion_status_query_for(type, item.iid), current_user: user1) + + expect(response).to have_gitlab_http_status(200) + + task_completion_status = graphql_data.dig('project', type, 'taskCompletionStatus') + expect(task_completion_status).not_to be_nil + expect(task_completion_status['count']).to eq(item.task_completion_status[:count]) + expect(task_completion_status['completedCount']).to eq(item.task_completion_status[:completed_count]) + end + end + + [DESCRIPTION_0_DONE, DESCRIPTION_1_DONE, DESCRIPTION_2_DONE].each do |desc| + context "with description #{desc}" do + context 'when type is issue' do + it_behaves_like 'graphql task completion status provider', 'issue' do + let(:item) { create(:issue, project: project, description: desc) } + end + end + + context 'when type is merge request' do + it_behaves_like 'graphql task completion status provider', 'mergeRequest' do + let(:item) { create(:merge_request, author: user1, source_project: project, description: desc) } + end + end + end + end +end diff --git a/spec/requests/api/services_spec.rb b/spec/requests/api/services_spec.rb index e260aa21e25..3f79e332b90 100644 --- a/spec/requests/api/services_spec.rb +++ b/spec/requests/api/services_spec.rb @@ -10,7 +10,10 @@ describe API::Services do end Service.available_services_names.each do |service| - describe "PUT /projects/:id/services/#{service.dasherize}" do + # TODO: Remove below `if: (service != "kubernetes")` in the next release + # KubernetesService was deprecated and it can't be updated. Right now it's + # only readable. It should be completely removed in the next iteration. + describe "PUT /projects/:id/services/#{service.dasherize}", if: (service != "kubernetes") do include_context service it "updates #{service} settings" do @@ -19,13 +22,22 @@ describe API::Services do expect(response).to have_gitlab_http_status(200) current_service = project.services.first - event = current_service.event_names.empty? ? "foo" : current_service.event_names.first - state = current_service[event] || false + events = current_service.event_names.empty? ? ["foo"].freeze : current_service.event_names + query_strings = [] + events.each do |event| + query_strings << "#{event}=#{!current_service[event]}" + end + query_strings = query_strings.join('&') - put api("/projects/#{project.id}/services/#{dashed_service}?#{event}=#{!state}", user), params: service_attrs + put api("/projects/#{project.id}/services/#{dashed_service}?#{query_strings}", user), params: service_attrs expect(response).to have_gitlab_http_status(200) - expect(project.services.first[event]).not_to eq(state) unless event == "foo" + events.each do |event| + next if event == "foo" + + expect(project.services.first[event]).not_to eq(current_service[event]), + "expected #{!current_service[event]} for event #{event} for service #{current_service.title}, got #{current_service[event]}" + end end it "returns if required fields missing" do @@ -50,7 +62,10 @@ describe API::Services do end end - describe "DELETE /projects/:id/services/#{service.dasherize}" do + # TODO: Remove below `if: (service != "kubernetes")` in the next release + # KubernetesService was deprecated and it can't be updated. Right now it's + # only readable. It should be completely removed in the next iteration. + describe "DELETE /projects/:id/services/#{service.dasherize}", if: (service != "kubernetes") do include_context service before do diff --git a/spec/requests/projects/cycle_analytics_events_spec.rb b/spec/requests/projects/cycle_analytics_events_spec.rb index 49412b628b3..25390f8a23e 100644 --- a/spec/requests/projects/cycle_analytics_events_spec.rb +++ b/spec/requests/projects/cycle_analytics_events_spec.rb @@ -32,10 +32,10 @@ describe 'cycle analytics events' do it 'lists the plan events' do get project_cycle_analytics_plan_path(project, format: :json) - first_mr_short_sha = project.merge_requests.sort_by_attribute(:created_asc).first.commits.first.short_id + first_issue_iid = project.issues.sort_by_attribute(:created_desc).pluck(:iid).first.to_s expect(json_response['events']).not_to be_empty - expect(json_response['events'].first['short_sha']).to eq(first_mr_short_sha) + expect(json_response['events'].first['iid']).to eq(first_issue_iid) end it 'lists the code events' do diff --git a/spec/serializers/environment_entity_spec.rb b/spec/serializers/environment_entity_spec.rb index c2312734042..906449f470b 100644 --- a/spec/serializers/environment_entity_spec.rb +++ b/spec/serializers/environment_entity_spec.rb @@ -59,15 +59,5 @@ describe EnvironmentEntity do expect(subject[:cluster_type]).to eq('project_type') end end - - context 'when deployment platform is a Kubernetes Service' do - before do - create(:kubernetes_service, project: project) - end - - it 'does not include cluster_type' do - expect(subject).not_to include(:cluster_type) - end - end end end diff --git a/spec/services/ci/create_pipeline_service_spec.rb b/spec/services/ci/create_pipeline_service_spec.rb index 867692d4d64..d9b61dfe503 100644 --- a/spec/services/ci/create_pipeline_service_spec.rb +++ b/spec/services/ci/create_pipeline_service_spec.rb @@ -1132,5 +1132,17 @@ describe Ci::CreatePipelineService do .with_message('Insufficient permissions to create a new pipeline') end end + + context 'when a user with permissions has been blocked' do + before do + user.block! + end + + it 'raises an error' do + expect { subject } + .to raise_error(described_class::CreateError) + .with_message('Insufficient permissions to create a new pipeline') + end + end end end diff --git a/spec/services/ci/play_build_service_spec.rb b/spec/services/ci/play_build_service_spec.rb index 634f865e2d8..1e68b7956ea 100644 --- a/spec/services/ci/play_build_service_spec.rb +++ b/spec/services/ci/play_build_service_spec.rb @@ -3,7 +3,7 @@ require 'spec_helper' describe Ci::PlayBuildService, '#execute' do - let(:user) { create(:user) } + let(:user) { create(:user, developer_projects: [project]) } let(:project) { create(:project) } let(:pipeline) { create(:ci_pipeline, project: project) } let(:build) { create(:ci_build, :manual, pipeline: pipeline) } @@ -16,8 +16,6 @@ describe Ci::PlayBuildService, '#execute' do let(:project) { create(:project) } it 'allows user to play build if protected branch rules are met' do - project.add_developer(user) - create(:protected_branch, :developers_can_merge, name: build.ref, project: project) @@ -27,8 +25,6 @@ describe Ci::PlayBuildService, '#execute' do end it 'does not allow user with developer role to play build' do - project.add_developer(user) - expect { service.execute(build) } .to raise_error Gitlab::Access::AccessDeniedError end @@ -38,23 +34,21 @@ describe Ci::PlayBuildService, '#execute' do let(:project) { create(:project, :repository) } it 'allows user with developer role to play a build' do - project.add_developer(user) - service.execute(build) expect(build.reload).to be_pending end + + it 'prevents a blocked developer from playing a build' do + user.block! + + expect { service.execute(build) }.to raise_error(Gitlab::Access::AccessDeniedError) + end end context 'when build is a playable manual action' do let(:build) { create(:ci_build, :manual, pipeline: pipeline) } - - before do - project.add_developer(user) - - create(:protected_branch, :developers_can_merge, - name: build.ref, project: project) - end + let!(:branch) { create(:protected_branch, :developers_can_merge, name: build.ref, project: project) } it 'enqueues the build' do expect(service.execute(build)).to eq build @@ -70,13 +64,7 @@ describe Ci::PlayBuildService, '#execute' do context 'when build is not a playable manual action' do let(:build) { create(:ci_build, when: :manual, pipeline: pipeline) } - - before do - project.add_developer(user) - - create(:protected_branch, :developers_can_merge, - name: build.ref, project: project) - end + let!(:branch) { create(:protected_branch, :developers_can_merge, name: build.ref, project: project) } it 'duplicates the build' do duplicate = service.execute(build) @@ -94,6 +82,7 @@ describe Ci::PlayBuildService, '#execute' do end context 'when build is not action' do + let(:user) { create(:user) } let(:build) { create(:ci_build, :success, pipeline: pipeline) } it 'raises an error' do @@ -103,10 +92,8 @@ describe Ci::PlayBuildService, '#execute' do end context 'when user does not have ability to trigger action' do - before do - create(:protected_branch, :no_one_can_push, - name: build.ref, project: project) - end + let(:user) { create(:user) } + let!(:branch) { create(:protected_branch, :developers_can_merge, name: build.ref, project: project) } it 'raises an error' do expect { service.execute(build) } diff --git a/spec/services/lfs/file_transformer_spec.rb b/spec/services/lfs/file_transformer_spec.rb index 888eea6e91e..9973d64930b 100644 --- a/spec/services/lfs/file_transformer_spec.rb +++ b/spec/services/lfs/file_transformer_spec.rb @@ -3,13 +3,13 @@ require "spec_helper" describe Lfs::FileTransformer do - let(:project) { create(:project, :repository) } + let(:project) { create(:project, :repository, :wiki_repo) } let(:repository) { project.repository } let(:file_content) { 'Test file content' } let(:branch_name) { 'lfs' } let(:file_path) { 'test_file.lfs' } - subject { described_class.new(project, branch_name) } + subject { described_class.new(project, repository, branch_name) } describe '#new_file' do context 'with lfs disabled' do @@ -100,6 +100,12 @@ describe Lfs::FileTransformer do end.to change { project.lfs_objects.count }.by(1) end + it 'saves the repository_type to LfsObjectsProject' do + subject.new_file(file_path, file_content) + + expect(project.lfs_objects_projects.first.repository_type).to eq('project') + end + context 'when LfsObject already exists' do let(:lfs_pointer) { Gitlab::Git::LfsPointerFile.new(file_content) } @@ -113,6 +119,56 @@ describe Lfs::FileTransformer do end.to change { project.lfs_objects.count }.by(1) end end + + context 'when the LfsObject is already linked to project' do + before do + subject.new_file(file_path, file_content) + end + + shared_examples 'a new LfsObject is not created' do + it do + expect do + second_service.new_file(file_path, file_content) + end.not_to change { project.lfs_objects.count } + end + end + + context 'and the service is called again with the same repository type' do + let(:second_service) { described_class.new(project, repository, branch_name) } + + include_examples 'a new LfsObject is not created' + + it 'does not create a new LfsObjectsProject record' do + expect do + second_service.new_file(file_path, file_content) + end.not_to change { project.lfs_objects_projects.count } + end + end + + context 'and the service is called again with a different repository type' do + let(:second_service) { described_class.new(project, project.wiki.repository, branch_name) } + + before do + expect(second_service).to receive(:lfs_file?).and_return(true) + end + + include_examples 'a new LfsObject is not created' + + it 'creates a new LfsObjectsProject record' do + expect do + second_service.new_file(file_path, file_content) + end.to change { project.lfs_objects_projects.count }.by(1) + end + + it 'sets the correct repository_type on the new LfsObjectsProject record' do + second_service.new_file(file_path, file_content) + + repository_types = project.lfs_objects_projects.order(:id).pluck(:repository_type) + + expect(repository_types).to eq(%w(project wiki)) + end + end + end end end end diff --git a/spec/services/notification_service_spec.rb b/spec/services/notification_service_spec.rb index 4b40c86574f..f25e2fe5e2b 100644 --- a/spec/services/notification_service_spec.rb +++ b/spec/services/notification_service_spec.rb @@ -2217,10 +2217,12 @@ describe NotificationService, :mailer do let(:pipeline) { create(:ci_pipeline, :failed, project: project, user: pipeline_user) } it 'emails project owner and user that triggered the pipeline' do + project.add_developer(pipeline_user) + notification.autodevops_disabled(pipeline, [owner.email, pipeline_user.email]) - should_email(owner) - should_email(pipeline_user) + should_email(owner, times: 1) # Once for the disable pipeline. + should_email(pipeline_user, times: 2) # Once for the new permission, once for the disable. end end end diff --git a/spec/services/projects/after_rename_service_spec.rb b/spec/services/projects/after_rename_service_spec.rb index b8055a285f2..8585d495ffb 100644 --- a/spec/services/projects/after_rename_service_spec.rb +++ b/spec/services/projects/after_rename_service_spec.rb @@ -23,6 +23,7 @@ describe Projects::AfterRenameService do allow(project).to receive(:gitlab_shell).and_return(gitlab_shell) stub_feature_flags(skip_hashed_storage_upgrade: false) + stub_application_setting(hashed_storage_enabled: false) end it 'renames a repository' do diff --git a/spec/services/projects/create_service_spec.rb b/spec/services/projects/create_service_spec.rb index f54f9200661..b0b74407812 100644 --- a/spec/services/projects/create_service_spec.rb +++ b/spec/services/projects/create_service_spec.rb @@ -152,6 +152,33 @@ describe Projects::CreateService, '#execute' do end end + context 'default visibility level' do + let(:group) { create(:group, :private) } + + before do + stub_application_setting(default_project_visibility: Gitlab::VisibilityLevel::INTERNAL) + group.add_developer(user) + + opts.merge!( + visibility: 'private', + name: 'test', + namespace: group, + path: 'foo' + ) + end + + it 'creates a private project' do + project = create_project(user, opts) + + expect(project).to respond_to(:errors) + + expect(project.errors.any?).to be(false) + expect(project.visibility_level).to eq(Gitlab::VisibilityLevel::PRIVATE) + expect(project.saved?).to be(true) + expect(project.valid?).to be(true) + end + end + context 'restricted visibility level' do before do stub_application_setting(restricted_visibility_levels: [Gitlab::VisibilityLevel::PUBLIC]) @@ -201,6 +228,7 @@ describe Projects::CreateService, '#execute' do context 'with legacy storage' do before do + stub_application_setting(hashed_storage_enabled: false) gitlab_shell.create_repository(repository_storage, "#{user.namespace.full_path}/existing", 'group/project') end @@ -232,7 +260,6 @@ describe Projects::CreateService, '#execute' do let(:hashed_path) { '@hashed/6b/86/6b86b273ff34fce19d6b804eff5a3f5747ada4eaa22f1d49c01e52ddb7875b4b' } before do - stub_application_setting(hashed_storage_enabled: true) allow(Digest::SHA2).to receive(:hexdigest) { hash } end diff --git a/spec/services/projects/fork_service_spec.rb b/spec/services/projects/fork_service_spec.rb index 6afc91d5e95..0c109e26a6a 100644 --- a/spec/services/projects/fork_service_spec.rb +++ b/spec/services/projects/fork_service_spec.rb @@ -116,11 +116,12 @@ describe Projects::ForkService do end end - context 'repository already exists' do + context 'repository in legacy storage already exists' do let(:repository_storage) { 'default' } let(:repository_storage_path) { Gitlab.config.repositories.storages[repository_storage].legacy_disk_path } before do + stub_application_setting(hashed_storage_enabled: false) gitlab_shell.create_repository(repository_storage, "#{@to_user.namespace.full_path}/#{@from_project.path}", "#{@to_user.namespace.full_path}/#{@from_project.path}") end diff --git a/spec/support/helpers/graphql_helpers.rb b/spec/support/helpers/graphql_helpers.rb index e95c7f2a6d6..bcf6669f37d 100644 --- a/spec/support/helpers/graphql_helpers.rb +++ b/spec/support/helpers/graphql_helpers.rb @@ -157,7 +157,7 @@ module GraphqlHelpers when Array # multiplexed queries json_response.map { |response| response['errors'] } else - raise "Unkown GraphQL response type #{json_response.class}" + raise "Unknown GraphQL response type #{json_response.class}" end end diff --git a/spec/support/helpers/query_recorder.rb b/spec/support/helpers/query_recorder.rb index c4ae62b25e4..d45377267f3 100644 --- a/spec/support/helpers/query_recorder.rb +++ b/spec/support/helpers/query_recorder.rb @@ -11,7 +11,7 @@ module ActiveRecord def show_backtrace(values) Rails.logger.debug("QueryRecorder SQL: #{values[:sql]}") - caller.each { |line| Rails.logger.debug(" --> #{line}") } + Gitlab::Profiler.clean_backtrace(caller).each { |line| Rails.logger.debug(" --> #{line}") } end def callback(name, start, finish, message_id, values) diff --git a/spec/support/helpers/test_env.rb b/spec/support/helpers/test_env.rb index 06b5ecdf150..77f22d9dd24 100644 --- a/spec/support/helpers/test_env.rb +++ b/spec/support/helpers/test_env.rb @@ -146,19 +146,6 @@ module TestEnv install_dir: Gitlab.config.gitlab_shell.path, version: Gitlab::Shell.version_required, task: 'gitlab:shell:install') - - # gitlab-shell hooks don't work in our test environment because they try to make internal API calls - sabotage_gitlab_shell_hooks - end - - def sabotage_gitlab_shell_hooks - create_fake_git_hooks(Gitlab::Shell.new.hooks_path) - end - - def create_fake_git_hooks(hooks_dir) - %w[pre-receive post-receive update].each do |hook| - File.open(File.join(hooks_dir, hook), 'w', 0755) { |f| f.puts '#!/bin/sh' } - end end def setup_gitaly @@ -172,7 +159,6 @@ module TestEnv task: "gitlab:gitaly:install[#{install_gitaly_args}]") do Gitlab::SetupHelper.create_gitaly_configuration(gitaly_dir, { 'default' => repos_path }, force: true) - create_fake_git_hooks(File.join(gitaly_dir, 'ruby/git-hooks')) start_gitaly(gitaly_dir) end end diff --git a/spec/support/prometheus/additional_metrics_shared_examples.rb b/spec/support/prometheus/additional_metrics_shared_examples.rb index 8044b061ca5..de21e808932 100644 --- a/spec/support/prometheus/additional_metrics_shared_examples.rb +++ b/spec/support/prometheus/additional_metrics_shared_examples.rb @@ -44,7 +44,9 @@ RSpec.shared_examples 'additional metrics query' do end describe 'project has Kubernetes service' do - shared_examples 'same behavior between KubernetesService and Platform::Kubernetes' do + context 'when user configured kubernetes from CI/CD > Clusters' do + let!(:cluster) { create(:cluster, :project, :provided_by_gcp) } + let(:project) { cluster.project } let(:environment) { create(:environment, slug: 'environment-slug', project: project) } let(:kube_namespace) { project.deployment_platform.kubernetes_namespace_for(project) } @@ -56,19 +58,6 @@ RSpec.shared_examples 'additional metrics query' do subject.query(*query_params) end end - - context 'when user configured kubernetes from Integration > Kubernetes' do - let(:project) { create(:kubernetes_project) } - - it_behaves_like 'same behavior between KubernetesService and Platform::Kubernetes' - end - - context 'when user configured kubernetes from CI/CD > Clusters' do - let!(:cluster) { create(:cluster, :project, :provided_by_gcp) } - let(:project) { cluster.project } - - it_behaves_like 'same behavior between KubernetesService and Platform::Kubernetes' - end end describe 'project without Kubernetes service' do diff --git a/spec/support/shared_contexts/services_shared_context.rb b/spec/support/shared_contexts/services_shared_context.rb index 089f1798cd2..0c3a24d206f 100644 --- a/spec/support/shared_contexts/services_shared_context.rb +++ b/spec/support/shared_contexts/services_shared_context.rb @@ -37,8 +37,7 @@ Service.available_services_names.each do |service| def initialize_service(service) service_item = project.find_or_initialize_service(service) service_item.properties = service_attrs - service_item.active = true if service == "kubernetes" - service_item.save + service_item.save! service_item end end diff --git a/spec/support/shared_examples/ci_trace_shared_examples.rb b/spec/support/shared_examples/ci_trace_shared_examples.rb index db935bcb388..f985b2dcbba 100644 --- a/spec/support/shared_examples/ci_trace_shared_examples.rb +++ b/spec/support/shared_examples/ci_trace_shared_examples.rb @@ -5,11 +5,11 @@ shared_examples_for 'common trace features' do end it "returns formatted html" do - expect(trace.html).to eq("12<br>34") + expect(trace.html).to eq("<span class=\"\">12<br/><span class=\"\">34</span></span>") end it "returns last line of formatted html" do - expect(trace.html(last_lines: 1)).to eq("34") + expect(trace.html(last_lines: 1)).to eq("<span class=\"\">34</span>") end end diff --git a/spec/views/notify/pipeline_failed_email.html.haml_spec.rb b/spec/views/notify/pipeline_failed_email.html.haml_spec.rb index d04affc7df1..623237b111a 100644 --- a/spec/views/notify/pipeline_failed_email.html.haml_spec.rb +++ b/spec/views/notify/pipeline_failed_email.html.haml_spec.rb @@ -3,7 +3,7 @@ require 'spec_helper' describe 'notify/pipeline_failed_email.html.haml' do include Devise::Test::ControllerHelpers - let(:user) { create(:user) } + let(:user) { create(:user, developer_projects: [project]) } let(:project) { create(:project, :repository) } let(:merge_request) { create(:merge_request, :simple, source_project: project) } diff --git a/spec/views/notify/pipeline_failed_email.text.erb_spec.rb b/spec/views/notify/pipeline_failed_email.text.erb_spec.rb index 079fb865d7b..81245239eba 100644 --- a/spec/views/notify/pipeline_failed_email.text.erb_spec.rb +++ b/spec/views/notify/pipeline_failed_email.text.erb_spec.rb @@ -4,7 +4,7 @@ require 'spec_helper' describe 'notify/pipeline_failed_email.text.erb' do include Devise::Test::ControllerHelpers - let(:user) { create(:user) } + let(:user) { create(:user, developer_projects: [project]) } let(:project) { create(:project, :repository) } let(:merge_request) { create(:merge_request, :simple, source_project: project) } diff --git a/spec/views/notify/pipeline_success_email.html.haml_spec.rb b/spec/views/notify/pipeline_success_email.html.haml_spec.rb index 8ee7f954d70..a876bf13e11 100644 --- a/spec/views/notify/pipeline_success_email.html.haml_spec.rb +++ b/spec/views/notify/pipeline_success_email.html.haml_spec.rb @@ -3,7 +3,7 @@ require 'spec_helper' describe 'notify/pipeline_success_email.html.haml' do include Devise::Test::ControllerHelpers - let(:user) { create(:user) } + let(:user) { create(:user, developer_projects: [project]) } let(:project) { create(:project, :repository) } let(:merge_request) { create(:merge_request, :simple, source_project: project) } diff --git a/spec/views/projects/commit/_commit_box.html.haml_spec.rb b/spec/views/projects/commit/_commit_box.html.haml_spec.rb index 457dd2e940f..1086546c10d 100644 --- a/spec/views/projects/commit/_commit_box.html.haml_spec.rb +++ b/spec/views/projects/commit/_commit_box.html.haml_spec.rb @@ -27,7 +27,7 @@ describe 'projects/commit/_commit_box.html.haml' do render - expect(rendered).to have_text("Pipeline ##{third_pipeline.id} (##{third_pipeline.iid}) failed") + expect(rendered).to have_text("Pipeline ##{third_pipeline.id} failed") end end @@ -40,7 +40,7 @@ describe 'projects/commit/_commit_box.html.haml' do it 'shows correct pipeline description' do render - expect(rendered).to have_text "Pipeline ##{pipeline.id} (##{pipeline.iid}) " \ + expect(rendered).to have_text "Pipeline ##{pipeline.id} " \ 'waiting for manual action' end end diff --git a/spec/views/projects/jobs/_build.html.haml_spec.rb b/spec/views/projects/jobs/_build.html.haml_spec.rb index 97b25a6976f..1d58891036e 100644 --- a/spec/views/projects/jobs/_build.html.haml_spec.rb +++ b/spec/views/projects/jobs/_build.html.haml_spec.rb @@ -4,7 +4,7 @@ describe 'projects/ci/jobs/_build' do include Devise::Test::ControllerHelpers let(:project) { create(:project, :repository) } - let(:pipeline) { create(:ci_empty_pipeline, id: 1337, iid: 57, project: project, sha: project.commit.id) } + let(:pipeline) { create(:ci_empty_pipeline, id: 1337, project: project, sha: project.commit.id) } let(:build) { create(:ci_build, pipeline: pipeline, stage: 'test', stage_idx: 1, name: 'rspec 0:2', status: :pending) } before do @@ -15,14 +15,14 @@ describe 'projects/ci/jobs/_build' do it 'won\'t include a column with a link to its pipeline by default' do render partial: 'projects/ci/builds/build', locals: { build: build } - expect(rendered).not_to have_link('#1337 (#57)') - expect(rendered).not_to have_text('#1337 (#57) by API') + expect(rendered).not_to have_link('#1337') + expect(rendered).not_to have_text('#1337 by API') end it 'can include a column with a link to its pipeline' do render partial: 'projects/ci/builds/build', locals: { build: build, pipeline_link: true } - expect(rendered).to have_link('#1337 (#57)') - expect(rendered).to have_text('#1337 (#57) by API') + expect(rendered).to have_link('#1337') + expect(rendered).to have_text('#1337 by API') end end diff --git a/spec/workers/auto_devops/disable_worker_spec.rb b/spec/workers/auto_devops/disable_worker_spec.rb index 800a07e41a5..53113f286ba 100644 --- a/spec/workers/auto_devops/disable_worker_spec.rb +++ b/spec/workers/auto_devops/disable_worker_spec.rb @@ -2,7 +2,7 @@ require 'spec_helper' describe AutoDevops::DisableWorker, '#perform' do - let(:user) { create(:user) } + let(:user) { create(:user, developer_projects: [project]) } let(:project) { create(:project, :repository, :auto_devops) } let(:auto_devops) { project.auto_devops } let(:pipeline) { create(:ci_pipeline, :failed, :auto_devops_source, project: project, user: user) } @@ -10,6 +10,7 @@ describe AutoDevops::DisableWorker, '#perform' do subject { described_class.new } before do + project.add_developer(user) stub_application_setting(auto_devops_enabled: true) auto_devops.update_attribute(:enabled, nil) end diff --git a/spec/workers/reactive_caching_worker_spec.rb b/spec/workers/reactive_caching_worker_spec.rb index 2395e6ec947..b8ca6063ccd 100644 --- a/spec/workers/reactive_caching_worker_spec.rb +++ b/spec/workers/reactive_caching_worker_spec.rb @@ -6,16 +6,6 @@ describe ReactiveCachingWorker do let(:service) { project.deployment_platform } describe '#perform' do - context 'when user configured kubernetes from Integration > Kubernetes' do - let(:project) { create(:kubernetes_project) } - - it 'calls #exclusively_update_reactive_cache!' do - expect_any_instance_of(KubernetesService).to receive(:exclusively_update_reactive_cache!) - - described_class.new.perform("KubernetesService", service.id) - end - end - context 'when user configured kubernetes from CI/CD > Clusters' do let!(:cluster) { create(:cluster, :project, :provided_by_gcp) } let(:project) { cluster.project } |