diff options
Diffstat (limited to 'spec/controllers/projects')
11 files changed, 495 insertions, 33 deletions
diff --git a/spec/controllers/projects/artifacts_controller_spec.rb b/spec/controllers/projects/artifacts_controller_spec.rb index 4ea6f869aa3..6091185e252 100644 --- a/spec/controllers/projects/artifacts_controller_spec.rb +++ b/spec/controllers/projects/artifacts_controller_spec.rb @@ -19,10 +19,44 @@ describe Projects::ArtifactsController do end describe 'GET download' do - it 'sends the artifacts file' do - expect(controller).to receive(:send_file).with(job.artifacts_file.path, hash_including(disposition: 'attachment')).and_call_original + def download_artifact(extra_params = {}) + params = { namespace_id: project.namespace, project_id: project, job_id: job }.merge(extra_params) - get :download, namespace_id: project.namespace, project_id: project, job_id: job + get :download, params + end + + context 'when no file type is supplied' do + it 'sends the artifacts file' do + expect(controller).to receive(:send_file).with(job.artifacts_file.path, hash_including(disposition: 'attachment')).and_call_original + + download_artifact + end + end + + context 'when a file type is supplied' do + context 'when an invalid file type is supplied' do + let(:file_type) { 'invalid' } + + it 'returns 404' do + download_artifact(file_type: file_type) + + expect(response).to have_gitlab_http_status(404) + end + end + + context 'when codequality file type is supplied' do + let(:file_type) { 'codequality' } + + before do + create(:ci_job_artifact, :codequality, job: job) + end + + it 'sends the codequality report' do + expect(controller).to receive(:send_file).with(job.job_artifacts_codequality.file.path, hash_including(disposition: 'attachment')).and_call_original + + download_artifact(file_type: file_type) + end + end end end diff --git a/spec/controllers/projects/clusters_controller_spec.rb b/spec/controllers/projects/clusters_controller_spec.rb index 42917d0d505..97ac11fd171 100644 --- a/spec/controllers/projects/clusters_controller_spec.rb +++ b/spec/controllers/projects/clusters_controller_spec.rb @@ -170,12 +170,14 @@ describe Projects::ClustersController do end describe 'POST create for new cluster' do + let(:legacy_abac_param) { 'true' } let(:params) do { cluster: { name: 'new-cluster', provider_gcp_attributes: { - gcp_project_id: 'gcp-project-12345' + gcp_project_id: 'gcp-project-12345', + legacy_abac: legacy_abac_param } } } @@ -201,6 +203,18 @@ describe Projects::ClustersController do expect(response).to redirect_to(project_cluster_path(project, project.clusters.first)) expect(project.clusters.first).to be_gcp expect(project.clusters.first).to be_kubernetes + expect(project.clusters.first.provider_gcp).to be_legacy_abac + end + + context 'when legacy_abac param is false' do + let(:legacy_abac_param) { 'false' } + + it 'creates a new cluster with legacy_abac_disabled' do + expect(ClusterProvisionWorker).to receive(:perform_async) + expect { go }.to change { Clusters::Cluster.count } + .and change { Clusters::Providers::Gcp.count } + expect(project.clusters.first.provider_gcp).not_to be_legacy_abac + end end end @@ -274,11 +288,43 @@ describe Projects::ClustersController do context 'when creates a cluster' do it 'creates a new cluster' do expect(ClusterProvisionWorker).to receive(:perform_async) + expect { go }.to change { Clusters::Cluster.count } .and change { Clusters::Platforms::Kubernetes.count } + expect(response).to redirect_to(project_cluster_path(project, project.clusters.first)) + + expect(project.clusters.first).to be_user + expect(project.clusters.first).to be_kubernetes + end + end + + context 'when creates a RBAC-enabled cluster' do + let(:params) do + { + cluster: { + name: 'new-cluster', + platform_kubernetes_attributes: { + api_url: 'http://my-url', + token: 'test', + namespace: 'aaa', + authorization_type: 'rbac' + } + } + } + end + + it 'creates a new cluster' do + expect(ClusterProvisionWorker).to receive(:perform_async) + + expect { go }.to change { Clusters::Cluster.count } + .and change { Clusters::Platforms::Kubernetes.count } + + expect(response).to redirect_to(project_cluster_path(project, project.clusters.first)) + expect(project.clusters.first).to be_user expect(project.clusters.first).to be_kubernetes + expect(project.clusters.first).to be_platform_kubernetes_rbac end end end diff --git a/spec/controllers/projects/hooks_controller_spec.rb b/spec/controllers/projects/hooks_controller_spec.rb index 0f3033b0933..7d3a8c3d0d3 100644 --- a/spec/controllers/projects/hooks_controller_spec.rb +++ b/spec/controllers/projects/hooks_controller_spec.rb @@ -30,6 +30,7 @@ describe Projects::HooksController do tag_push_events: true, merge_requests_events: true, issues_events: true, + confidential_note_events: true, confidential_issues_events: true, note_events: true, job_events: true, diff --git a/spec/controllers/projects/issues_controller_spec.rb b/spec/controllers/projects/issues_controller_spec.rb index 5b347b1109a..9df77560320 100644 --- a/spec/controllers/projects/issues_controller_spec.rb +++ b/spec/controllers/projects/issues_controller_spec.rb @@ -637,6 +637,18 @@ describe Projects::IssuesController do project_id: project, id: id end + + it 'avoids (most) N+1s loading labels', :request_store do + label = create(:label, project: project).to_reference + labels = create_list(:label, 10, project: project).map(&:to_reference) + issue = create(:issue, project: project, description: 'Test issue') + + control_count = ActiveRecord::QueryRecorder.new { issue.update(description: [issue.description, label].join(' ')) }.count + + # Follow-up to get rid of this `2 * label.count` requirement: https://gitlab.com/gitlab-org/gitlab-ce/issues/52230 + expect { issue.update(description: [issue.description, labels].join(' ')) } + .not_to exceed_query_limit(control_count + 2 * labels.count) + end end describe 'GET #realtime_changes' do diff --git a/spec/controllers/projects/jobs_controller_spec.rb b/spec/controllers/projects/jobs_controller_spec.rb index 1aca44c6e74..383d6c1a2a9 100644 --- a/spec/controllers/projects/jobs_controller_spec.rb +++ b/spec/controllers/projects/jobs_controller_spec.rb @@ -86,7 +86,7 @@ describe Projects::JobsController, :clean_gitlab_redis_shared_state do def create_job(name, status) pipeline = create(:ci_pipeline, project: project) create(:ci_build, :tags, :triggered, :artifacts, - pipeline: pipeline, name: name, status: status) + pipeline: pipeline, name: name, status: status) end end @@ -147,12 +147,255 @@ describe Projects::JobsController, :clean_gitlab_redis_shared_state do get_show(id: job.id, format: :json) end - it 'exposes needed information' do - expect(response).to have_gitlab_http_status(:ok) - expect(json_response['raw_path']).to match(%r{jobs/\d+/raw\z}) - expect(json_response.dig('merge_request', 'path')).to match(%r{merge_requests/\d+\z}) - expect(json_response['new_issue_path']) - .to include('/issues/new') + context 'when job failed' do + it 'exposes needed information' do + expect(response).to have_gitlab_http_status(:ok) + expect(response).to match_response_schema('job/job_details') + expect(json_response['raw_path']).to match(%r{jobs/\d+/raw\z}) + expect(json_response.dig('merge_request', 'path')).to match(%r{merge_requests/\d+\z}) + expect(json_response['new_issue_path']).to include('/issues/new') + end + end + + context 'when job has artifacts' do + context 'with not expiry date' do + let(:job) { create(:ci_build, :success, :artifacts, pipeline: pipeline) } + + it 'exposes needed information' do + expect(response).to have_gitlab_http_status(:ok) + expect(response).to match_response_schema('job/job_details') + expect(json_response['artifact']['download_path']).to match(%r{artifacts/download}) + expect(json_response['artifact']['browse_path']).to match(%r{artifacts/browse}) + expect(json_response['artifact']).not_to have_key('expired') + expect(json_response['artifact']).not_to have_key('expired_at') + end + end + + context 'with expiry date' do + let(:job) { create(:ci_build, :success, :artifacts, :expired, pipeline: pipeline) } + + it 'exposes needed information' do + expect(response).to have_gitlab_http_status(:ok) + expect(response).to match_response_schema('job/job_details') + expect(json_response['artifact']).not_to have_key('download_path') + expect(json_response['artifact']).not_to have_key('browse_path') + expect(json_response['artifact']['expired']).to eq(true) + expect(json_response['artifact']['expire_at']).not_to be_empty + end + end + end + + context 'when job has terminal' do + let(:job) { create(:ci_build, :running, :with_runner_session, pipeline: pipeline) } + + it 'exposes the terminal path' do + expect(response).to have_gitlab_http_status(:ok) + expect(response).to match_response_schema('job/job_details') + expect(json_response['terminal_path']).to match(%r{/terminal}) + end + end + + context 'when job passed with no trace' do + let(:job) { create(:ci_build, :success, :artifacts, pipeline: pipeline) } + + it 'exposes empty state illustrations' do + expect(response).to have_gitlab_http_status(:ok) + expect(response).to match_response_schema('job/job_details') + expect(json_response['status']['illustration']).to have_key('image') + expect(json_response['status']['illustration']).to have_key('size') + expect(json_response['status']['illustration']).to have_key('title') + end + end + + context 'with no deployment' do + let(:job) { create(:ci_build, :success, pipeline: pipeline) } + + it 'does not exposes the deployment information' do + expect(response).to have_gitlab_http_status(:ok) + expect(json_response['deployment_status']).to be_nil + end + end + + context 'with deployment' do + let(:merge_request) { create(:merge_request, source_project: project) } + let(:environment) { create(:environment, project: project, name: 'staging', state: :available) } + let(:job) { create(:ci_build, :success, environment: environment.name, pipeline: pipeline) } + + it 'exposes the deployment information' do + expect(response).to have_gitlab_http_status(:ok) + expect(json_response).to match_schema('job/job_details') + expect(json_response['deployment_status']["status"]).to eq 'creating' + expect(json_response['deployment_status']["environment"]).not_to be_nil + end + end + + context 'when user can edit runner' do + context 'that belongs to the project' do + let(:runner) { create(:ci_runner, :project, projects: [project]) } + let(:job) { create(:ci_build, :success, pipeline: pipeline, runner: runner) } + + before do + project.add_maintainer(user) + sign_in(user) + + get_show(id: job.id, format: :json) + end + + it 'user can edit runner' do + expect(response).to have_gitlab_http_status(:ok) + expect(response).to match_response_schema('job/job_details') + expect(json_response['runner']).to have_key('edit_path') + end + end + + context 'that belongs to group' do + let(:group) { create(:group) } + let(:runner) { create(:ci_runner, :group, groups: [group]) } + let(:job) { create(:ci_build, :success, pipeline: pipeline, runner: runner) } + let(:user) { create(:user, :admin) } + + before do + project.add_maintainer(user) + sign_in(user) + + get_show(id: job.id, format: :json) + end + + it 'user can not edit runner' do + expect(response).to have_gitlab_http_status(:ok) + expect(response).to match_response_schema('job/job_details') + expect(json_response['runner']).not_to have_key('edit_path') + end + end + + context 'that belongs to instance' do + let(:runner) { create(:ci_runner, :instance) } + let(:job) { create(:ci_build, :success, pipeline: pipeline, runner: runner) } + let(:user) { create(:user, :admin) } + + before do + project.add_maintainer(user) + sign_in(user) + + get_show(id: job.id, format: :json) + end + + it 'user can not edit runner' do + expect(response).to have_gitlab_http_status(:ok) + expect(response).to match_response_schema('job/job_details') + expect(json_response['runner']).not_to have_key('edit_path') + end + end + end + + context 'when no runners are available' do + let(:runner) { create(:ci_runner, :instance, active: false) } + let(:job) { create(:ci_build, :pending, pipeline: pipeline, runner: runner) } + + it 'exposes needed information' do + expect(response).to have_gitlab_http_status(:ok) + expect(response).to match_response_schema('job/job_details') + expect(json_response['runners']['online']).to be false + expect(json_response['runners']['available']).to be false + end + end + + context 'when no runner is online' do + let(:runner) { create(:ci_runner, :instance) } + let(:job) { create(:ci_build, :pending, pipeline: pipeline, runner: runner) } + + it 'exposes needed information' do + expect(response).to have_gitlab_http_status(:ok) + expect(response).to match_response_schema('job/job_details') + expect(json_response['runners']['online']).to be false + expect(json_response['runners']['available']).to be true + end + end + + context 'settings_path' do + context 'when user is developer' do + it 'settings_path is not available' do + expect(response).to have_gitlab_http_status(:ok) + expect(response).to match_response_schema('job/job_details') + expect(json_response['runners']).not_to have_key('settings_path') + end + end + + context 'when user is maintainer' do + let(:user) { create(:user, :admin) } + + before do + project.add_maintainer(user) + sign_in(user) + end + + it 'settings_path is available' do + expect(response).to have_gitlab_http_status(:ok) + expect(response).to match_response_schema('job/job_details') + expect(json_response['runners']['settings_path']).to match(/runners/) + end + end + end + + context 'when no trace is available' do + it 'has_trace is false' do + expect(response).to match_response_schema('job/job_details') + expect(json_response['has_trace']).to be false + end + end + + context 'when job has trace' do + let(:job) { create(:ci_build, :running, :trace_live, pipeline: pipeline) } + + it "has_trace is true" do + expect(response).to match_response_schema('job/job_details') + expect(json_response['has_trace']).to be true + end + end + end + + context 'when requesting JSON job is triggered' do + let!(:merge_request) { create(:merge_request, source_project: project) } + let(:trigger) { create(:ci_trigger, project: project) } + let(:trigger_request) { create(:ci_trigger_request, pipeline: pipeline, trigger: trigger) } + let(:job) { create(:ci_build, pipeline: pipeline, trigger_request: trigger_request) } + + before do + project.add_developer(user) + sign_in(user) + + allow_any_instance_of(Ci::Build).to receive(:merge_request).and_return(merge_request) + end + + context 'with no variables' do + before do + get_show(id: job.id, format: :json) + end + + it 'exposes trigger information' do + expect(response).to have_gitlab_http_status(:ok) + expect(response).to match_response_schema('job/job_details') + expect(json_response['trigger']['short_token']).to eq 'toke' + expect(json_response['trigger']['variables'].length).to eq 0 + end + end + + context 'with variables' do + before do + create(:ci_pipeline_variable, pipeline: pipeline, key: :TRIGGER_KEY_1, value: 'TRIGGER_VALUE_1') + + get_show(id: job.id, format: :json) + end + + it 'exposes trigger information and variables' do + expect(response).to have_gitlab_http_status(:ok) + expect(response).to match_response_schema('job/job_details') + expect(json_response['trigger']['short_token']).to eq 'toke' + expect(json_response['trigger']['variables'].length).to eq 1 + expect(json_response['trigger']['variables'].first['key']).to eq "TRIGGER_KEY_1" + expect(json_response['trigger']['variables'].first['value']).to eq "TRIGGER_VALUE_1" + expect(json_response['trigger']['variables'].first['public']).to eq false + end end end @@ -388,6 +631,46 @@ describe Projects::JobsController, :clean_gitlab_redis_shared_state do end end + describe 'POST unschedule' do + before do + project.add_developer(user) + + create(:protected_branch, :developers_can_merge, + name: 'master', project: project) + + sign_in(user) + + post_unschedule + end + + context 'when job is scheduled' do + let(:job) { create(:ci_build, :scheduled, pipeline: pipeline) } + + it 'redirects to the unscheduled job page' do + expect(response).to have_gitlab_http_status(:found) + expect(response).to redirect_to(namespace_project_job_path(id: job.id)) + end + + it 'transits to manual' do + expect(job.reload).to be_manual + end + end + + context 'when job is not scheduled' do + let(:job) { create(:ci_build, pipeline: pipeline) } + + it 'renders unprocessable_entity' do + expect(response).to have_gitlab_http_status(:unprocessable_entity) + end + end + + def post_unschedule + post :unschedule, namespace_id: project.namespace, + project_id: project, + id: job.id + end + end + describe 'POST cancel_all' do before do project.add_developer(user) diff --git a/spec/controllers/projects/merge_requests_controller_spec.rb b/spec/controllers/projects/merge_requests_controller_spec.rb index d9bb3981539..f2467bfd525 100644 --- a/spec/controllers/projects/merge_requests_controller_spec.rb +++ b/spec/controllers/projects/merge_requests_controller_spec.rb @@ -763,25 +763,35 @@ describe Projects::MergeRequestsController do describe 'GET ci_environments_status' do context 'the environment is from a forked project' do - let!(:forked) { fork_project(project, user, repository: true) } - let!(:environment) { create(:environment, project: forked) } - let!(:deployment) { create(:deployment, environment: environment, sha: forked.commit.id, ref: 'master') } - let(:admin) { create(:admin) } + let!(:forked) { fork_project(project, user, repository: true) } + let!(:environment) { create(:environment, project: forked) } + let!(:deployment) { create(:deployment, environment: environment, sha: forked.commit.id, ref: 'master') } + let(:admin) { create(:admin) } let(:merge_request) do create(:merge_request, source_project: forked, target_project: project) end - before do + it 'links to the environment on that project' do + get_ci_environments_status + + expect(json_response.first['url']).to match /#{forked.full_path}/ + end + + # we're trying to reduce the overall number of queries for this method. + # set a hard limit for now. https://gitlab.com/gitlab-org/gitlab-ce/issues/52287 + it 'keeps queries in check' do + control_count = ActiveRecord::QueryRecorder.new { get_ci_environments_status }.count + + expect(control_count).to be <= 137 + end + + def get_ci_environments_status get :ci_environments_status, namespace_id: merge_request.project.namespace.to_param, project_id: merge_request.project, id: merge_request.iid, format: 'json' end - - it 'links to the environment on that project' do - expect(json_response.first['url']).to match /#{forked.full_path}/ - end end end @@ -885,4 +895,18 @@ describe Projects::MergeRequestsController do end end end + + describe 'GET edit' do + it 'responds successfully' do + get :edit, namespace_id: project.namespace, project_id: project, id: merge_request + + expect(response).to have_gitlab_http_status(:success) + end + + it 'assigns the noteable to make sure autocompletes work' do + get :edit, namespace_id: project.namespace, project_id: project, id: merge_request + + expect(assigns(:noteable)).not_to be_nil + end + end end diff --git a/spec/controllers/projects/notes_controller_spec.rb b/spec/controllers/projects/notes_controller_spec.rb index 1458113b90c..e48c9dea976 100644 --- a/spec/controllers/projects/notes_controller_spec.rb +++ b/spec/controllers/projects/notes_controller_spec.rb @@ -154,7 +154,7 @@ describe Projects::NotesController do get :index, request_params expect(parsed_response[:notes].count).to eq(1) - expect(note_json[:id]).to eq(note.id) + expect(note_json[:id]).to eq(note.id.to_s) end it 'does not result in N+1 queries' do @@ -207,6 +207,14 @@ describe Projects::NotesController do expect(response).to have_gitlab_http_status(200) end + it 'returns discussion JSON when the return_discussion param is set' do + post :create, request_params.merge(format: :json, return_discussion: 'true') + + expect(response).to have_gitlab_http_status(200) + expect(json_response).to have_key 'discussion' + expect(json_response['discussion']['notes'][0]['note']).to eq(request_params[:note][:note]) + end + context 'when merge_request_diff_head_sha present' do before do service_params = { diff --git a/spec/controllers/projects/pipelines_controller_spec.rb b/spec/controllers/projects/pipelines_controller_spec.rb index d89716b1b50..5c7415a318d 100644 --- a/spec/controllers/projects/pipelines_controller_spec.rb +++ b/spec/controllers/projects/pipelines_controller_spec.rb @@ -90,6 +90,11 @@ describe Projects::PipelinesController do context 'when performing gitaly calls', :request_store do it 'limits the Gitaly requests' do + # Isolate from test preparation (Repository#exists? is also cached in RequestStore) + RequestStore.end! + RequestStore.clear! + RequestStore.begin! + expect { get_pipelines_index_json } .to change { Gitlab::GitalyClient.get_request_count }.by(2) end @@ -193,14 +198,34 @@ describe Projects::PipelinesController do context 'when accessing existing stage' do before do + create(:ci_build, :retried, :failed, pipeline: pipeline, stage: 'build') create(:ci_build, pipeline: pipeline, stage: 'build') + end + + context 'without retried' do + before do + get_stage('build') + end - get_stage('build') + it 'returns pipeline jobs without the retried builds' do + expect(response).to have_gitlab_http_status(:ok) + expect(response).to match_response_schema('pipeline_stage') + expect(json_response['latest_statuses'].length).to eq 1 + expect(json_response).not_to have_key('retried') + end end - it 'returns html source for stage dropdown' do - expect(response).to have_gitlab_http_status(:ok) - expect(response).to match_response_schema('pipeline_stage') + context 'with retried' do + before do + get_stage('build', retried: true) + end + + it 'returns pipelines jobs with the retried builds' do + expect(response).to have_gitlab_http_status(:ok) + expect(response).to match_response_schema('pipeline_stage') + expect(json_response['latest_statuses'].length).to eq 1 + expect(json_response['retried'].length).to eq 1 + end end end @@ -214,12 +239,13 @@ describe Projects::PipelinesController do end end - def get_stage(name) - get :stage, namespace_id: project.namespace, - project_id: project, - id: pipeline.id, - stage: name, - format: :json + def get_stage(name, params = {}) + get :stage, **params.merge( + namespace_id: project.namespace, + project_id: project, + id: pipeline.id, + stage: name, + format: :json) end end diff --git a/spec/controllers/projects/registry/repositories_controller_spec.rb b/spec/controllers/projects/registry/repositories_controller_spec.rb index 17769a14def..d11e42b411b 100644 --- a/spec/controllers/projects/registry/repositories_controller_spec.rb +++ b/spec/controllers/projects/registry/repositories_controller_spec.rb @@ -86,9 +86,10 @@ describe Projects::Registry::RepositoriesController do stub_container_registry_tags(repository: :any, tags: []) end - it 'deletes a repository' do - expect { delete_repository(repository) }.to change { ContainerRepository.all.count }.by(-1) + it 'schedules a job to delete a repository' do + expect(DeleteContainerRepositoryWorker).to receive(:perform_async).with(user.id, repository.id) + delete_repository(repository) expect(response).to have_gitlab_http_status(:no_content) end end diff --git a/spec/controllers/projects/settings/ci_cd_controller_spec.rb b/spec/controllers/projects/settings/ci_cd_controller_spec.rb index 1f14a0cc381..4629929f9af 100644 --- a/spec/controllers/projects/settings/ci_cd_controller_spec.rb +++ b/spec/controllers/projects/settings/ci_cd_controller_spec.rb @@ -74,6 +74,19 @@ describe Projects::Settings::CiCdController do end end + describe 'PUT #reset_registration_token' do + subject { put :reset_registration_token, namespace_id: project.namespace, project_id: project } + it 'resets runner registration token' do + expect { subject }.to change { project.reload.runners_token } + end + + it 'redirects the user to admin runners page' do + subject + + expect(response).to redirect_to(namespace_project_settings_ci_cd_path) + end + end + describe 'PATCH update' do let(:params) { { ci_config_path: '' } } diff --git a/spec/controllers/projects/uploads_controller_spec.rb b/spec/controllers/projects/uploads_controller_spec.rb index 325ee53aafb..9802e4d5b1e 100644 --- a/spec/controllers/projects/uploads_controller_spec.rb +++ b/spec/controllers/projects/uploads_controller_spec.rb @@ -18,6 +18,20 @@ describe Projects::UploadsController do end end + context "when exception occurs" do + before do + allow(FileUploader).to receive(:workhorse_authorize).and_raise(SocketError.new) + sign_in(create(:user)) + end + + it "responds with status internal_server_error" do + post_authorize + + expect(response).to have_gitlab_http_status(500) + expect(response.body).to eq('Error uploading file') + end + end + def post_authorize(verified: true) request.headers.merge!(workhorse_internal_api_request_header) if verified |