diff options
Diffstat (limited to 'spec/controllers/projects')
25 files changed, 696 insertions, 945 deletions
diff --git a/spec/controllers/projects/autocomplete_sources_controller_spec.rb b/spec/controllers/projects/autocomplete_sources_controller_spec.rb index 79edc261809..a5274b6543e 100644 --- a/spec/controllers/projects/autocomplete_sources_controller_spec.rb +++ b/spec/controllers/projects/autocomplete_sources_controller_spec.rb @@ -114,17 +114,5 @@ RSpec.describe Projects::AutocompleteSourcesController do end end end - - context 'when feature flag is disabled' do - before do - stub_feature_flags(customer_relations: false) - end - - it 'renders 404' do - get :contacts, format: :json, params: { namespace_id: group.path, project_id: project.path } - - expect(response).to have_gitlab_http_status(:not_found) - end - end end end diff --git a/spec/controllers/projects/clusters_controller_spec.rb b/spec/controllers/projects/clusters_controller_spec.rb index 01420e30d24..d45ea268e64 100644 --- a/spec/controllers/projects/clusters_controller_spec.rb +++ b/spec/controllers/projects/clusters_controller_spec.rb @@ -272,150 +272,6 @@ RSpec.describe Projects::ClustersController do end end - describe 'POST #create_aws' do - let(:params) do - { - cluster: { - name: 'new-cluster', - provider_aws_attributes: { - key_name: 'key', - role_arn: 'arn:role', - region: 'region', - vpc_id: 'vpc', - instance_type: 'instance type', - num_nodes: 3, - security_group_id: 'security group', - subnet_ids: %w(subnet1 subnet2) - } - } - } - end - - def post_create_aws - post :create_aws, params: params.merge(namespace_id: project.namespace, project_id: project) - end - - include_examples ':certificate_based_clusters feature flag controller responses' do - let(:subject) { post_create_aws } - end - - it 'creates a new cluster' do - expect(ClusterProvisionWorker).to receive(:perform_async) - expect { post_create_aws }.to change { Clusters::Cluster.count } - .and change { Clusters::Providers::Aws.count } - - cluster = project.clusters.first - - expect(response).to have_gitlab_http_status(:created) - expect(response.location).to eq(project_cluster_path(project, cluster)) - expect(cluster).to be_aws - expect(cluster).to be_kubernetes - end - - context 'params are invalid' do - let(:params) do - { - cluster: { name: '' } - } - end - - it 'does not create a cluster' do - expect { post_create_aws }.not_to change { Clusters::Cluster.count } - - expect(response).to have_gitlab_http_status(:unprocessable_entity) - expect(response.media_type).to eq('application/json') - expect(response.body).to include('is invalid') - end - end - - describe 'security' do - before do - allow(WaitForClusterCreationWorker).to receive(:perform_in) - end - - it 'is allowed for admin when admin mode enabled', :enable_admin_mode do - expect { post_create_aws }.to be_allowed_for(:admin) - end - it 'is disabled for admin when admin mode disabled' do - expect { post_create_aws }.to be_denied_for(:admin) - end - it { expect { post_create_aws }.to be_allowed_for(:owner).of(project) } - it { expect { post_create_aws }.to be_allowed_for(:maintainer).of(project) } - it { expect { post_create_aws }.to be_denied_for(:developer).of(project) } - it { expect { post_create_aws }.to be_denied_for(:reporter).of(project) } - it { expect { post_create_aws }.to be_denied_for(:guest).of(project) } - it { expect { post_create_aws }.to be_denied_for(:user) } - it { expect { post_create_aws }.to be_denied_for(:external) } - end - end - - describe 'POST authorize AWS role for EKS cluster' do - let!(:role) { create(:aws_role, user: user) } - - let(:role_arn) { 'arn:new-role' } - let(:params) do - { - cluster: { - role_arn: role_arn - } - } - end - - def go - post :authorize_aws_role, params: params.merge(namespace_id: project.namespace, project_id: project) - end - - before do - allow(Clusters::Aws::FetchCredentialsService).to receive(:new) - .and_return(double(execute: double)) - end - - include_examples ':certificate_based_clusters feature flag controller responses' do - let(:subject) { go } - end - - it 'updates the associated role with the supplied ARN' do - go - - expect(response).to have_gitlab_http_status(:ok) - expect(role.reload.role_arn).to eq(role_arn) - end - - context 'supplied role is invalid' do - let(:role_arn) { 'invalid-role' } - - it 'does not update the associated role' do - expect { go }.not_to change { role.role_arn } - - expect(response).to have_gitlab_http_status(:unprocessable_entity) - end - end - - describe 'security' do - before do - allow_next_instance_of(Clusters::Aws::AuthorizeRoleService) do |service| - response = double(status: :ok, body: double) - - allow(service).to receive(:execute).and_return(response) - end - end - - it 'is allowed for admin when admin mode enabled', :enable_admin_mode do - expect { go }.to be_allowed_for(:admin) - end - it 'is disabled for admin when admin mode disabled' do - expect { go }.to be_denied_for(:admin) - end - it { expect { go }.to be_allowed_for(:owner).of(project) } - it { expect { go }.to be_allowed_for(:maintainer).of(project) } - it { expect { go }.to be_denied_for(:developer).of(project) } - it { expect { go }.to be_denied_for(:reporter).of(project) } - it { expect { go }.to be_denied_for(:guest).of(project) } - it { expect { go }.to be_denied_for(:user) } - it { expect { go }.to be_denied_for(:external) } - end - end - describe 'DELETE clear cluster cache' do let(:cluster) { create(:cluster, :project, projects: [project]) } let!(:kubernetes_namespace) { create(:cluster_kubernetes_namespace, cluster: cluster) } diff --git a/spec/controllers/projects/commits_controller_spec.rb b/spec/controllers/projects/commits_controller_spec.rb index c7f98406201..26d4725656f 100644 --- a/spec/controllers/projects/commits_controller_spec.rb +++ b/spec/controllers/projects/commits_controller_spec.rb @@ -166,6 +166,14 @@ RSpec.describe Projects::CommitsController do end end end + + context 'with markdown cache' do + it 'preloads markdown cache for commits' do + expect(Commit).to receive(:preload_markdown_cache!).and_call_original + + get :show, params: { namespace_id: project.namespace, project_id: project, id: 'master/README.md' } + end + end end describe "GET /commits/:id/signatures" do diff --git a/spec/controllers/projects/compare_controller_spec.rb b/spec/controllers/projects/compare_controller_spec.rb index 9821618df8d..e6e0307d0ca 100644 --- a/spec/controllers/projects/compare_controller_spec.rb +++ b/spec/controllers/projects/compare_controller_spec.rb @@ -44,6 +44,14 @@ RSpec.describe Projects::CompareController do expect(response).to be_successful end end + + context 'with missing parameters' do + let(:params) { super().merge(from: '', to: '') } + + it 'returns successfully' do + expect(response).to be_successful + end + end end describe 'GET show' do @@ -102,6 +110,23 @@ RSpec.describe Projects::CompareController do end end + context 'when refs have CI::Pipeline' do + let(:from_project_id) { nil } + let(:from_ref) { '08f22f25' } + let(:to_ref) { '59e29889' } + + before do + create(:ci_pipeline, project: project) + end + + it 'avoids N+1 queries' do + control = ActiveRecord::QueryRecorder.new { show_request } + + # Only 1 query to ci/pipeline.rb is allowed + expect(control.find_query(/pipeline\.rb/, 1)).to be_empty + end + end + context 'when the refs exist in different projects that the user can see' do let(:from_project_id) { public_fork.id } let(:from_ref) { 'improve%2Fmore-awesome' } @@ -434,7 +459,7 @@ RSpec.describe Projects::CompareController do expect(CompareService).to receive(:new).with(project, escaped_to_ref).and_return(compare_service) expect(compare_service).to receive(:execute).with(project, escaped_from_ref).and_return(compare) - expect(compare).to receive(:commits).and_return([signature_commit, non_signature_commit]) + expect(compare).to receive(:commits).and_return(CommitCollection.new(project, [signature_commit, non_signature_commit])) expect(non_signature_commit).to receive(:has_signature?).and_return(false) end diff --git a/spec/controllers/projects/environments_controller_spec.rb b/spec/controllers/projects/environments_controller_spec.rb index f63e0cea04c..f4cad5790a3 100644 --- a/spec/controllers/projects/environments_controller_spec.rb +++ b/spec/controllers/projects/environments_controller_spec.rb @@ -208,17 +208,6 @@ RSpec.describe Projects::EnvironmentsController do expect(response).to have_gitlab_http_status(:not_found) end end - - it_behaves_like 'avoids N+1 queries on environment detail page' - - def create_deployment_with_associations(sequence:) - commit = project.commit("HEAD~#{sequence}") - create(:user, email: commit.author_email) - - deployer = create(:user) - build = create(:ci_build, environment: environment.name, pipeline: create(:ci_pipeline, project: environment.project), user: deployer) - create(:deployment, :success, environment: environment, deployable: build, user: deployer, project: project, sha: commit.sha) - end end describe 'GET edit' do diff --git a/spec/controllers/projects/import/jira_controller_spec.rb b/spec/controllers/projects/import/jira_controller_spec.rb index 5288c0fcf21..3f149afbb02 100644 --- a/spec/controllers/projects/import/jira_controller_spec.rb +++ b/spec/controllers/projects/import/jira_controller_spec.rb @@ -3,7 +3,7 @@ require 'spec_helper' RSpec.describe Projects::Import::JiraController do - include JiraServiceHelper + include JiraIntegrationHelpers let_it_be(:user) { create(:user) } let_it_be(:project) { create(:project) } diff --git a/spec/controllers/projects/incidents_controller_spec.rb b/spec/controllers/projects/incidents_controller_spec.rb index 20cf0dcfd3a..460821634b0 100644 --- a/spec/controllers/projects/incidents_controller_spec.rb +++ b/spec/controllers/projects/incidents_controller_spec.rb @@ -43,7 +43,6 @@ RSpec.describe Projects::IncidentsController do expect(response).to have_gitlab_http_status(:ok) expect(response).to render_template(:index) - expect(Gon.features).to include('incidentEscalations' => true) end context 'when user is unauthorized' do diff --git a/spec/controllers/projects/issues_controller_spec.rb b/spec/controllers/projects/issues_controller_spec.rb index 8a03c1e709b..1305693372c 100644 --- a/spec/controllers/projects/issues_controller_spec.rb +++ b/spec/controllers/projects/issues_controller_spec.rb @@ -12,10 +12,6 @@ RSpec.describe Projects::IssuesController do let(:issue) { create(:issue, project: project) } let(:spam_action_response_fields) { { 'stub_spam_action_response_fields' => true } } - before do - stub_feature_flags(vue_issues_list: true) - end - describe "GET #index" do context 'external issue tracker' do before do @@ -145,13 +141,104 @@ RSpec.describe Projects::IssuesController do project.add_developer(user) end - it "returns issue_email_participants" do + it "returns issue attributes" do participants = create_list(:issue_email_participant, 2, issue: issue) get :show, params: { namespace_id: project.namespace, project_id: project, id: issue.iid }, format: :json expect(response).to have_gitlab_http_status(:ok) - expect(json_response['issue_email_participants']).to contain_exactly({ "email" => participants[0].email }, { "email" => participants[1].email }) + expect(json_response).to include( + 'issue_email_participants' => contain_exactly( + { "email" => participants[0].email }, { "email" => participants[1].email } + ), + 'type' => 'ISSUE' + ) + end + + context 'when issue is not a task and work items feature flag is enabled' do + it 'does not redirect to work items route' do + get :show, params: { namespace_id: project.namespace, project_id: project, id: issue.iid } + + expect(response).to render_template(:show) + end + end + + context 'when issue is of type task' do + let(:query) { {} } + + let_it_be(:task) { create(:issue, :task, project: project) } + + context 'when work_items feature flag is enabled' do + shared_examples 'redirects to show work item page' do + it 'redirects to work item page' do + expect(response).to redirect_to(project_work_items_path(project, task.id, query)) + end + end + + context 'show action' do + let(:query) { { query: 'any' } } + + before do + get :show, params: { namespace_id: project.namespace, project_id: project, id: task.iid, **query } + end + + it_behaves_like 'redirects to show work item page' + end + + context 'edit action' do + let(:query) { { query: 'any' } } + + before do + get :edit, params: { namespace_id: project.namespace, project_id: project, id: task.iid, **query } + end + + it_behaves_like 'redirects to show work item page' + end + + context 'update action' do + before do + put :update, params: { namespace_id: project.namespace, project_id: project, id: task.iid, issue: { title: 'New title' } } + end + + it_behaves_like 'redirects to show work item page' + end + end + + context 'when work_items feature flag is disabled' do + before do + stub_feature_flags(work_items: false) + end + + shared_examples 'renders 404' do + it 'renders 404 for show action' do + expect(response).to have_gitlab_http_status(:not_found) + end + end + + context 'show action' do + before do + get :show, params: { namespace_id: project.namespace, project_id: project, id: task.iid } + end + + it_behaves_like 'renders 404' + end + + context 'edit action' do + before do + get :edit, params: { namespace_id: project.namespace, project_id: project, id: task.iid } + end + + it_behaves_like 'renders 404' + end + + context 'update action' do + before do + put :update, params: { namespace_id: project.namespace, project_id: project, id: task.iid, issue: { title: 'New title' } } + end + + it_behaves_like 'renders 404' + end + end end end diff --git a/spec/controllers/projects/jobs_controller_spec.rb b/spec/controllers/projects/jobs_controller_spec.rb index f0fbbb65fa5..107eb1ed3a3 100644 --- a/spec/controllers/projects/jobs_controller_spec.rb +++ b/spec/controllers/projects/jobs_controller_spec.rb @@ -1075,63 +1075,81 @@ RSpec.describe Projects::JobsController, :clean_gitlab_redis_shared_state do before do project.add_role(user, role) sign_in(user) - - post_erase end - shared_examples_for 'erases' do - it 'redirects to the erased job page' do - expect(response).to have_gitlab_http_status(:found) - expect(response).to redirect_to(namespace_project_job_path(id: job.id)) + context 'when project is not undergoing stats refresh' do + before do + post_erase end - it 'erases artifacts' do - expect(job.artifacts_file.present?).to be_falsey - expect(job.artifacts_metadata.present?).to be_falsey - end + shared_examples_for 'erases' do + it 'redirects to the erased 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 'erases trace' do - expect(job.trace.exist?).to be_falsey + it 'erases artifacts' do + expect(job.artifacts_file.present?).to be_falsey + expect(job.artifacts_metadata.present?).to be_falsey + end + + it 'erases trace' do + expect(job.trace.exist?).to be_falsey + end end - end - context 'when job is successful and has artifacts' do - let(:job) { create(:ci_build, :erasable, :trace_artifact, pipeline: pipeline) } + context 'when job is successful and has artifacts' do + let(:job) { create(:ci_build, :erasable, :trace_artifact, pipeline: pipeline) } - it_behaves_like 'erases' - end + it_behaves_like 'erases' + end - context 'when job has live trace and unarchived artifact' do - let(:job) { create(:ci_build, :success, :trace_live, :unarchived_trace_artifact, pipeline: pipeline) } + context 'when job has live trace and unarchived artifact' do + let(:job) { create(:ci_build, :success, :trace_live, :unarchived_trace_artifact, pipeline: pipeline) } - it_behaves_like 'erases' - end + it_behaves_like 'erases' + end - context 'when job is erased' do - let(:job) { create(:ci_build, :erased, pipeline: pipeline) } + context 'when job is erased' do + let(:job) { create(:ci_build, :erased, pipeline: pipeline) } - it 'returns unprocessable_entity' do - expect(response).to have_gitlab_http_status(:unprocessable_entity) + it 'returns unprocessable_entity' do + expect(response).to have_gitlab_http_status(:unprocessable_entity) + end end - end - context 'when user is developer' do - let(:role) { :developer } - let(:job) { create(:ci_build, :erasable, :trace_artifact, pipeline: pipeline, user: triggered_by) } + context 'when user is developer' do + let(:role) { :developer } + let(:job) { create(:ci_build, :erasable, :trace_artifact, pipeline: pipeline, user: triggered_by) } - context 'when triggered by same user' do - let(:triggered_by) { user } + context 'when triggered by same user' do + let(:triggered_by) { user } - it 'has successful status' do - expect(response).to have_gitlab_http_status(:found) + it 'has successful status' do + expect(response).to have_gitlab_http_status(:found) + end + end + + context 'when triggered by different user' do + let(:triggered_by) { create(:user) } + + it 'does not have successful status' do + expect(response).not_to have_gitlab_http_status(:found) + end end end + end + + context 'when project is undergoing stats refresh' do + it_behaves_like 'preventing request because of ongoing project stats refresh' do + let(:job) { create(:ci_build, :erasable, :trace_artifact, pipeline: pipeline) } + let(:make_request) { post_erase } - context 'when triggered by different user' do - let(:triggered_by) { create(:user) } + it 'does not erase artifacts' do + make_request - it 'does not have successful status' do - expect(response).not_to have_gitlab_http_status(:found) + expect(job.artifacts_file).to be_present + expect(job.artifacts_metadata).to be_present end end end diff --git a/spec/controllers/projects/mattermosts_controller_spec.rb b/spec/controllers/projects/mattermosts_controller_spec.rb index 596cd5c1a20..19a04654114 100644 --- a/spec/controllers/projects/mattermosts_controller_spec.rb +++ b/spec/controllers/projects/mattermosts_controller_spec.rb @@ -62,7 +62,7 @@ RSpec.describe Projects::MattermostsController do subject integration = project.integrations.last - expect(subject).to redirect_to(edit_project_integration_path(project, integration)) + expect(subject).to redirect_to(edit_project_settings_integration_path(project, integration)) end end end diff --git a/spec/controllers/projects/merge_requests/drafts_controller_spec.rb b/spec/controllers/projects/merge_requests/drafts_controller_spec.rb index 222bb977beb..b9ede84157d 100644 --- a/spec/controllers/projects/merge_requests/drafts_controller_spec.rb +++ b/spec/controllers/projects/merge_requests/drafts_controller_spec.rb @@ -385,6 +385,38 @@ RSpec.describe Projects::MergeRequests::DraftsController do expect(discussion.resolved?).to eq(false) end end + + context 'publish with note' do + before do + create(:draft_note, merge_request: merge_request, author: user) + end + + context 'when feature flag is disabled' do + before do + stub_feature_flags(mr_review_submit_comment: false) + end + + it 'does not create note' do + post :publish, params: params.merge!(note: 'Hello world') + + expect(merge_request.notes.reload.size).to be(1) + end + end + + context 'when feature flag is enabled' do + it 'creates note' do + post :publish, params: params.merge!(note: 'Hello world') + + expect(merge_request.notes.reload.size).to be(2) + end + + it 'does not create note when note param is empty' do + post :publish, params: params.merge!(note: '') + + expect(merge_request.notes.reload.size).to be(1) + end + end + end end describe 'DELETE #destroy' do diff --git a/spec/controllers/projects/merge_requests_controller_spec.rb b/spec/controllers/projects/merge_requests_controller_spec.rb index f6db809c2e3..8ccbc0d3fe2 100644 --- a/spec/controllers/projects/merge_requests_controller_spec.rb +++ b/spec/controllers/projects/merge_requests_controller_spec.rb @@ -1094,7 +1094,7 @@ RSpec.describe Projects::MergeRequestsController do end context 'when processing coverage reports is completed' do - let(:report) { { status: :parsed, data: pipeline.coverage_reports } } + let(:report) { { status: :parsed, data: { 'files' => {} } } } it 'returns coverage reports' do subject @@ -1730,7 +1730,7 @@ RSpec.describe Projects::MergeRequestsController do describe 'POST remove_wip' do before do - merge_request.title = merge_request.wip_title + merge_request.title = merge_request.draft_title merge_request.save! post :remove_wip, @@ -1743,8 +1743,8 @@ RSpec.describe Projects::MergeRequestsController do xhr: true end - it 'removes the wip status' do - expect(merge_request.reload.title).to eq(merge_request.wipless_title) + it 'removes the draft status' do + expect(merge_request.reload.title).to eq(merge_request.draftless_title) end it 'renders MergeRequest as JSON' do diff --git a/spec/controllers/projects/notes_controller_spec.rb b/spec/controllers/projects/notes_controller_spec.rb index 07874c8a8af..85e5de46afd 100644 --- a/spec/controllers/projects/notes_controller_spec.rb +++ b/spec/controllers/projects/notes_controller_spec.rb @@ -84,100 +84,6 @@ RSpec.describe Projects::NotesController do end end - context 'for multiple pages of notes', :aggregate_failures do - # 3 pages worth: 1 normal page, 1 oversized due to clashing updated_at, - # and a final, short page - let!(:page_1) { create_list(:note, 2, noteable: issue, project: project, updated_at: 3.days.ago) } - let!(:page_2) { create_list(:note, 3, noteable: issue, project: project, updated_at: 2.days.ago) } - let!(:page_3) { create_list(:note, 2, noteable: issue, project: project, updated_at: 1.day.ago) } - - # Include a resource event in the middle page as well - let!(:resource_event) { create(:resource_state_event, issue: issue, user: user, created_at: 2.days.ago) } - - let(:page_1_boundary) { microseconds(page_1.last.updated_at + NotesFinder::FETCH_OVERLAP) } - let(:page_2_boundary) { microseconds(page_2.last.updated_at + NotesFinder::FETCH_OVERLAP) } - - around do |example| - freeze_time do - example.run - end - end - - before do - stub_const('Gitlab::UpdatedNotesPaginator::LIMIT', 2) - end - - context 'feature flag enabled' do - before do - stub_feature_flags(paginated_notes: true) - end - - it 'returns the first page of notes' do - expect(Gitlab::EtagCaching::Middleware).to receive(:skip!) - - get :index, params: request_params - - expect(json_response['notes'].count).to eq(page_1.count) - expect(json_response['more']).to be_truthy - expect(json_response['last_fetched_at']).to eq(page_1_boundary) - expect(response.headers['Poll-Interval'].to_i).to eq(1) - end - - it 'returns the second page of notes' do - expect(Gitlab::EtagCaching::Middleware).to receive(:skip!) - - request.headers['X-Last-Fetched-At'] = page_1_boundary - - get :index, params: request_params - - expect(json_response['notes'].count).to eq(page_2.count + 1) # resource event - expect(json_response['more']).to be_truthy - expect(json_response['last_fetched_at']).to eq(page_2_boundary) - expect(response.headers['Poll-Interval'].to_i).to eq(1) - end - - it 'returns the final page of notes' do - expect(Gitlab::EtagCaching::Middleware).to receive(:skip!) - - request.headers['X-Last-Fetched-At'] = page_2_boundary - - get :index, params: request_params - - expect(json_response['notes'].count).to eq(page_3.count) - expect(json_response['more']).to be_falsy - expect(json_response['last_fetched_at']).to eq(microseconds(Time.zone.now)) - expect(response.headers['Poll-Interval'].to_i).to be > 1 - end - - it 'returns an empty page of notes' do - expect(Gitlab::EtagCaching::Middleware).not_to receive(:skip!) - - request.headers['X-Last-Fetched-At'] = microseconds(Time.zone.now) - - get :index, params: request_params - - expect(json_response['notes']).to be_empty - expect(json_response['more']).to be_falsy - expect(json_response['last_fetched_at']).to eq(microseconds(Time.zone.now)) - expect(response.headers['Poll-Interval'].to_i).to be > 1 - end - end - - context 'feature flag disabled' do - before do - stub_feature_flags(paginated_notes: false) - end - - it 'returns all notes' do - get :index, params: request_params - - expect(json_response['notes'].count).to eq((page_1 + page_2 + page_3).size + 1) - expect(json_response['more']).to be_falsy - expect(json_response['last_fetched_at']).to eq(microseconds(Time.zone.now)) - end - end - end - context 'for a discussion note' do let(:project) { create(:project, :repository) } let!(:note) { create(:discussion_note_on_merge_request, project: project) } diff --git a/spec/controllers/projects/pipelines/tests_controller_spec.rb b/spec/controllers/projects/pipelines/tests_controller_spec.rb index 113781bab7c..2db54dbe671 100644 --- a/spec/controllers/projects/pipelines/tests_controller_spec.rb +++ b/spec/controllers/projects/pipelines/tests_controller_spec.rb @@ -51,18 +51,6 @@ RSpec.describe Projects::Pipelines::TestsController do expect(response).to have_gitlab_http_status(:not_found) expect(json_response['errors']).to eq('Test report artifacts have expired') end - - context 'when ci_test_report_artifacts_expired is disabled' do - before do - stub_feature_flags(ci_test_report_artifacts_expired: false) - end - it 'renders test suite', :aggregate_failures do - get_tests_show_json(build_ids) - - expect(response).to have_gitlab_http_status(:ok) - expect(json_response['name']).to eq('test') - end - end end context 'when artifacts are not expired' do diff --git a/spec/controllers/projects/pipelines_controller_spec.rb b/spec/controllers/projects/pipelines_controller_spec.rb index 1be4177acd1..b3b803649d1 100644 --- a/spec/controllers/projects/pipelines_controller_spec.rb +++ b/spec/controllers/projects/pipelines_controller_spec.rb @@ -1289,6 +1289,18 @@ RSpec.describe Projects::PipelinesController do expect(response).to have_gitlab_http_status(:not_found) end end + + context 'and project is undergoing stats refresh' do + it_behaves_like 'preventing request because of ongoing project stats refresh' do + let(:make_request) { delete_pipeline } + + it 'does not delete the pipeline' do + make_request + + expect(Ci::Pipeline.exists?(pipeline.id)).to be_truthy + end + end + end end context 'when user has no privileges' do diff --git a/spec/controllers/projects/project_members_controller_spec.rb b/spec/controllers/projects/project_members_controller_spec.rb index 20a114bbe8c..9bb34a38005 100644 --- a/spec/controllers/projects/project_members_controller_spec.rb +++ b/spec/controllers/projects/project_members_controller_spec.rb @@ -170,6 +170,46 @@ RSpec.describe Projects::ProjectMembersController do expect(requester.reload.human_access).to eq(label) end end + + describe 'managing project direct owners' do + context 'when a Maintainer tries to elevate another user to OWNER' do + it 'does not allow the operation' do + params = { + project_member: { access_level: Gitlab::Access::OWNER }, + namespace_id: project.namespace, + project_id: project, + id: requester + } + + put :update, params: params, xhr: true + + expect(response).to have_gitlab_http_status(:forbidden) + end + end + + context 'when a user with OWNER access tries to elevate another user to OWNER' do + # inherited owner role via personal project association + let(:user) { project.first_owner } + + before do + sign_in(user) + end + + it 'returns success' do + params = { + project_member: { access_level: Gitlab::Access::OWNER }, + namespace_id: project.namespace, + project_id: project, + id: requester + } + + put :update, params: params, xhr: true + + expect(response).to have_gitlab_http_status(:ok) + expect(requester.reload.access_level).to eq(Gitlab::Access::OWNER) + end + end + end end context 'access expiry date' do @@ -275,19 +315,40 @@ RSpec.describe Projects::ProjectMembersController do context 'when member is found' do context 'when user does not have enough rights' do - before do - project.add_developer(user) + context 'when user does not have rights to manage other members' do + before do + project.add_developer(user) + end + + it 'returns 404', :aggregate_failures do + delete :destroy, params: { + namespace_id: project.namespace, + project_id: project, + id: member + } + + expect(response).to have_gitlab_http_status(:not_found) + expect(project.members).to include member + end end - it 'returns 404', :aggregate_failures do - delete :destroy, params: { - namespace_id: project.namespace, - project_id: project, - id: member - } + context 'when user does not have rights to manage Owner members' do + let_it_be(:member) { create(:project_member, project: project, access_level: Gitlab::Access::OWNER) } - expect(response).to have_gitlab_http_status(:not_found) - expect(project.members).to include member + before do + project.add_maintainer(user) + end + + it 'returns 403', :aggregate_failures do + delete :destroy, params: { + namespace_id: project.namespace, + project_id: project, + id: member + } + + expect(response).to have_gitlab_http_status(:forbidden) + expect(project.members).to include member + end end end @@ -434,7 +495,7 @@ RSpec.describe Projects::ProjectMembersController do end context 'when member is found' do - context 'when user does not have enough rights' do + context 'when user does not have rights to manage other members' do before do project.add_developer(user) end diff --git a/spec/controllers/projects/prometheus/alerts_controller_spec.rb b/spec/controllers/projects/prometheus/alerts_controller_spec.rb index f42119e7811..2c2c8180143 100644 --- a/spec/controllers/projects/prometheus/alerts_controller_spec.rb +++ b/spec/controllers/projects/prometheus/alerts_controller_spec.rb @@ -53,112 +53,6 @@ RSpec.describe Projects::Prometheus::AlertsController do end end - describe 'GET #index' do - def make_request(opts = {}) - get :index, params: request_params(opts, environment_id: environment) - end - - context 'when project has no prometheus alert' do - it 'returns an empty response' do - make_request - - expect(response).to have_gitlab_http_status(:ok) - expect(json_response).to be_empty - end - end - - context 'when project has prometheus alerts' do - let(:production) { create(:environment, project: project) } - let(:staging) { create(:environment, project: project) } - let(:json_alert_ids) { json_response.map { |alert| alert['id'] } } - - let!(:production_alerts) do - create_list(:prometheus_alert, 2, project: project, environment: production) - end - - let!(:staging_alerts) do - create_list(:prometheus_alert, 1, project: project, environment: staging) - end - - it 'contains prometheus alerts only for the production environment' do - make_request(environment_id: production) - - expect(response).to have_gitlab_http_status(:ok) - expect(json_response.count).to eq(2) - expect(json_alert_ids).to eq(production_alerts.map(&:id)) - end - - it 'contains prometheus alerts only for the staging environment' do - make_request(environment_id: staging) - - expect(response).to have_gitlab_http_status(:ok) - expect(json_response.count).to eq(1) - expect(json_alert_ids).to eq(staging_alerts.map(&:id)) - end - - it 'does not return prometheus alerts without environment' do - make_request(environment_id: nil) - - expect(response).to have_gitlab_http_status(:ok) - expect(json_response).to be_empty - end - end - - it_behaves_like 'unprivileged' - it_behaves_like 'project non-specific environment', :ok - end - - describe 'GET #show' do - let(:alert) do - create(:prometheus_alert, - :with_runbook_url, - project: project, - environment: environment, - prometheus_metric: metric) - end - - def make_request(opts = {}) - get :show, params: request_params( - opts, - id: alert.prometheus_metric_id, - environment_id: environment - ) - end - - context 'when alert does not exist' do - it 'returns not_found' do - make_request(id: 0) - - expect(response).to have_gitlab_http_status(:not_found) - end - end - - context 'when alert exists' do - let(:alert_params) do - { - 'id' => alert.id, - 'title' => alert.title, - 'query' => alert.query, - 'operator' => alert.computed_operator, - 'threshold' => alert.threshold, - 'runbook_url' => alert.runbook_url, - 'alert_path' => alert_path(alert) - } - end - - it 'renders the alert' do - make_request - - expect(response).to have_gitlab_http_status(:ok) - expect(json_response).to include(alert_params) - end - - it_behaves_like 'unprivileged' - it_behaves_like 'project non-specific environment', :not_found - it_behaves_like 'project non-specific metric', :not_found - end - end - describe 'POST #notify' do let(:alert_1) { build(:alert_management_alert, :prometheus, project: project) } let(:alert_2) { build(:alert_management_alert, :prometheus, project: project) } diff --git a/spec/controllers/projects/prometheus/metrics_controller_spec.rb b/spec/controllers/projects/prometheus/metrics_controller_spec.rb index 7dfa283195e..cd195b95100 100644 --- a/spec/controllers/projects/prometheus/metrics_controller_spec.rb +++ b/spec/controllers/projects/prometheus/metrics_controller_spec.rb @@ -141,7 +141,7 @@ RSpec.describe Projects::Prometheus::MetricsController do expect(flash[:notice]).to include('Metric was successfully added.') - expect(response).to redirect_to(edit_project_integration_path(project, ::Integrations::Prometheus)) + expect(response).to redirect_to(edit_project_settings_integration_path(project, ::Integrations::Prometheus)) end end @@ -168,7 +168,7 @@ RSpec.describe Projects::Prometheus::MetricsController do expect(metric.reload.title).to eq('new_title') expect(flash[:notice]).to include('Metric was successfully updated.') - expect(response).to redirect_to(edit_project_integration_path(project, ::Integrations::Prometheus)) + expect(response).to redirect_to(edit_project_settings_integration_path(project, ::Integrations::Prometheus)) end end end @@ -180,7 +180,7 @@ RSpec.describe Projects::Prometheus::MetricsController do it 'destroys the metric' do delete :destroy, params: project_params(id: metric.id) - expect(response).to redirect_to(edit_project_integration_path(project, ::Integrations::Prometheus)) + expect(response).to redirect_to(edit_project_settings_integration_path(project, ::Integrations::Prometheus)) expect(PrometheusMetric.find_by(id: metric.id)).to be_nil end end diff --git a/spec/controllers/projects/releases_controller_spec.rb b/spec/controllers/projects/releases_controller_spec.rb index 0dba7dab643..ad6682601f3 100644 --- a/spec/controllers/projects/releases_controller_spec.rb +++ b/spec/controllers/projects/releases_controller_spec.rb @@ -115,15 +115,6 @@ RSpec.describe Projects::ReleasesController do expect(json_response.map { |release| release["id"] } ).to eq([release_2.id, release_1.id]) end - # TODO: remove in https://gitlab.com/gitlab-org/gitlab/-/issues/360903 - it "returns release sha when remove_sha_from_releases_json is disabled" do - stub_feature_flags(remove_sha_from_releases_json: false) - - get_index - - expect(json_response).to eq([release_2, release_1].as_json) - end - it_behaves_like 'common access controls' context 'when the project is private and the user is not logged in' do @@ -157,19 +148,19 @@ RSpec.describe Projects::ReleasesController do end let(:release) { create(:release, project: project) } - let(:tag) { CGI.escape(release.tag) } + let(:tag) { release.tag } it_behaves_like 'successful request' context 'when tag name contains slash' do let(:release) { create(:release, project: project, tag: 'awesome/v1.0') } - let(:tag) { CGI.escape(release.tag) } + let(:tag) { release.tag } it_behaves_like 'successful request' it 'is accesible at a URL encoded path' do expect(edit_project_release_path(project, release)) - .to eq("/#{project.namespace.path}/#{project.name}/-/releases/awesome%252Fv1.0/edit") + .to eq("/#{project.namespace.path}/#{project.name}/-/releases/awesome%2Fv1.0/edit") end end @@ -196,19 +187,19 @@ RSpec.describe Projects::ReleasesController do end let(:release) { create(:release, project: project) } - let(:tag) { CGI.escape(release.tag) } + let(:tag) { release.tag } it_behaves_like 'successful request' context 'when tag name contains slash' do let(:release) { create(:release, project: project, tag: 'awesome/v1.0') } - let(:tag) { CGI.escape(release.tag) } + let(:tag) { release.tag } it_behaves_like 'successful request' it 'is accesible at a URL encoded path' do expect(project_release_path(project, release)) - .to eq("/#{project.namespace.path}/#{project.name}/-/releases/awesome%252Fv1.0") + .to eq("/#{project.namespace.path}/#{project.name}/-/releases/awesome%2Fv1.0") end end @@ -248,7 +239,7 @@ RSpec.describe Projects::ReleasesController do end let(:release) { create(:release, project: project) } - let(:tag) { CGI.escape(release.tag) } + let(:tag) { release.tag } context 'when user is a guest' do let(:project) { private_project } diff --git a/spec/controllers/projects/services_controller_spec.rb b/spec/controllers/projects/services_controller_spec.rb deleted file mode 100644 index 6802ebeb63e..00000000000 --- a/spec/controllers/projects/services_controller_spec.rb +++ /dev/null @@ -1,356 +0,0 @@ -# frozen_string_literal: true - -require 'spec_helper' - -RSpec.describe Projects::ServicesController do - include JiraServiceHelper - include AfterNextHelpers - - let_it_be(:project) { create(:project, :repository) } - let_it_be(:user) { create(:user) } - let_it_be(:jira_integration) { create(:jira_integration, project: project) } - - let(:integration) { jira_integration } - let(:integration_params) { { username: 'username', password: 'password', url: 'http://example.com' } } - - before do - sign_in(user) - project.add_maintainer(user) - end - - it_behaves_like Integrations::Actions do - let(:integration_attributes) { { project: project } } - - let(:routing_params) do - { - namespace_id: project.namespace, - project_id: project, - id: integration.to_param - } - end - end - - describe '#test' do - context 'when the integration is not testable' do - it 'renders 404' do - allow_any_instance_of(Integration).to receive(:testable?).and_return(false) - - put :test, params: project_params - - expect(response).to have_gitlab_http_status(:not_found) - end - end - - context 'when validations fail' do - let(:integration_params) { { active: 'true', url: '' } } - - it 'returns error messages in JSON response' do - put :test, params: project_params(service: integration_params) - - expect(json_response['message']).to eq 'Validations failed.' - expect(json_response['service_response']).to include "Url can't be blank" - expect(response).to be_successful - end - end - - context 'when successful' do - context 'with empty project' do - let_it_be(:project) { create(:project) } - - context 'with chat notification integration' do - let_it_be(:teams_integration) { project.create_microsoft_teams_integration(webhook: 'http://webhook.com') } - - let(:integration) { teams_integration } - - it 'returns success' do - allow_next(::MicrosoftTeams::Notifier).to receive(:ping).and_return(true) - - put :test, params: project_params - - expect(response).to be_successful - end - end - - it 'returns success' do - stub_jira_integration_test - - expect(Gitlab::HTTP).to receive(:get).with('/rest/api/2/serverInfo', any_args).and_call_original - - put :test, params: project_params(service: integration_params) - - expect(response).to be_successful - end - end - - it 'returns success' do - stub_jira_integration_test - - expect(Gitlab::HTTP).to receive(:get).with('/rest/api/2/serverInfo', any_args).and_call_original - - put :test, params: project_params(service: integration_params) - - expect(response).to be_successful - end - - context 'when service is configured for the first time' do - let(:integration_params) do - { - 'active' => '1', - 'push_events' => '1', - 'token' => 'token', - 'project_url' => 'https://buildkite.com/organization/pipeline' - } - end - - before do - allow_any_instance_of(ServiceHook).to receive(:execute).and_return(true) - end - - it 'persist the object' do - do_put - - expect(response).to be_successful - expect(json_response).to be_empty - expect(Integrations::Buildkite.first).to be_present - end - - it 'creates the ServiceHook object' do - do_put - - expect(response).to be_successful - expect(json_response).to be_empty - expect(Integrations::Buildkite.first.service_hook).to be_present - end - - def do_put - put :test, params: project_params(id: 'buildkite', - service: integration_params) - end - end - end - - context 'when unsuccessful' do - it 'returns an error response when the integration test fails' do - stub_request(:get, 'http://example.com/rest/api/2/serverInfo') - .to_return(status: 404) - - put :test, params: project_params(service: integration_params) - - expect(response).to be_successful - expect(json_response).to eq( - 'error' => true, - 'message' => 'Connection failed. Please check your settings.', - 'service_response' => '', - 'test_failed' => true - ) - end - - context 'with the Slack integration' do - let_it_be(:integration) { build(:integrations_slack) } - - it 'returns an error response when the URL is blocked' do - put :test, params: project_params(service: { webhook: 'http://127.0.0.1' }) - - expect(response).to be_successful - expect(json_response).to eq( - 'error' => true, - 'message' => 'Connection failed. Please check your settings.', - 'service_response' => "URL 'http://127.0.0.1' is blocked: Requests to localhost are not allowed", - 'test_failed' => true - ) - end - - it 'returns an error response when a network exception is raised' do - expect_next(Integrations::Slack).to receive(:test).and_raise(Errno::ECONNREFUSED) - - put :test, params: project_params - - expect(response).to be_successful - expect(json_response).to eq( - 'error' => true, - 'message' => 'Connection failed. Please check your settings.', - 'service_response' => 'Connection refused', - 'test_failed' => true - ) - end - end - end - end - - describe 'PUT #update' do - describe 'as HTML' do - let(:integration_params) { { active: true } } - let(:params) { project_params(service: integration_params) } - - let(:message) { 'Jira settings saved and active.' } - let(:redirect_url) { edit_project_integration_path(project, integration) } - - before do - stub_jira_integration_test - - put :update, params: params - end - - shared_examples 'integration update' do - it 'redirects to the correct url with a flash message' do - expect(response).to redirect_to(redirect_url) - expect(flash[:notice]).to eq(message) - end - end - - context 'when param `active` is set to true' do - let(:params) { project_params(service: integration_params, redirect_to: redirect) } - - context 'when redirect_to param is present' do - let(:redirect) { '/redirect_here' } - let(:redirect_url) { redirect } - - it_behaves_like 'integration update' - end - - context 'when redirect_to is an external domain' do - let(:redirect) { 'http://examle.com' } - - it_behaves_like 'integration update' - end - - context 'when redirect_to param is an empty string' do - let(:redirect) { '' } - - it_behaves_like 'integration update' - end - end - - context 'when param `active` is set to false' do - let(:integration_params) { { active: false } } - let(:message) { 'Jira settings saved, but not active.' } - - it_behaves_like 'integration update' - end - - context 'when param `inherit_from_id` is set to empty string' do - let(:integration_params) { { inherit_from_id: '' } } - - it 'sets inherit_from_id to nil' do - expect(integration.reload.inherit_from_id).to eq(nil) - end - end - - context 'when param `inherit_from_id` is set to an instance integration' do - let(:instance_integration) { create(:jira_integration, :instance, url: 'http://instance.com', password: 'instance') } - let(:integration_params) { { inherit_from_id: instance_integration.id, url: 'http://custom.com', password: 'custom' } } - - it 'ignores submitted params and inherits instance settings' do - expect(integration.reload).to have_attributes( - inherit_from_id: instance_integration.id, - url: instance_integration.url, - password: instance_integration.password - ) - end - end - - context 'when param `inherit_from_id` is set to a group integration' do - let_it_be(:group) { create(:group) } - let_it_be(:project) { create(:project, group: group) } - let_it_be(:jira_integration) { create(:jira_integration, project: project) } - - let(:group_integration) { create(:jira_integration, :group, group: group, url: 'http://group.com', password: 'group') } - let(:integration_params) { { inherit_from_id: group_integration.id, url: 'http://custom.com', password: 'custom' } } - - it 'ignores submitted params and inherits group settings' do - expect(integration.reload).to have_attributes( - inherit_from_id: group_integration.id, - url: group_integration.url, - password: group_integration.password - ) - end - end - - context 'when param `inherit_from_id` is set to an unrelated group' do - let_it_be(:group) { create(:group) } - - let(:group_integration) { create(:jira_integration, :group, group: group, url: 'http://group.com', password: 'group') } - let(:integration_params) { { inherit_from_id: group_integration.id, url: 'http://custom.com', password: 'custom' } } - - it 'ignores the param and saves the submitted settings' do - expect(integration.reload).to have_attributes( - inherit_from_id: nil, - url: 'http://custom.com', - password: 'custom' - ) - end - end - end - - describe 'as JSON' do - before do - stub_jira_integration_test - put :update, params: project_params(service: integration_params, format: :json) - end - - context 'when update succeeds' do - let(:integration_params) { { url: 'http://example.com', password: 'password' } } - - it 'returns success response' do - expect(response).to be_successful - expect(json_response).to include( - 'active' => true, - 'errors' => {} - ) - end - end - - context 'when update fails with missing password' do - let(:integration_params) { { url: 'http://example.com' } } - - it 'returns JSON response errors' do - expect(response).not_to be_successful - expect(json_response).to include( - 'active' => true, - 'errors' => { - 'password' => ["can't be blank"] - } - ) - end - end - - context 'when update fails with invalid URL' do - let(:integration_params) { { url: '', password: 'password' } } - - it 'returns JSON response with errors' do - expect(response).to have_gitlab_http_status(:unprocessable_entity) - expect(json_response).to include( - 'active' => true, - 'errors' => { 'url' => ['must be a valid URL', "can't be blank"] } - ) - end - end - end - end - - describe 'GET #edit' do - context 'with Jira service' do - let(:integration_param) { 'jira' } - - before do - get :edit, params: project_params(id: integration_param) - end - - context 'with approved services' do - it 'renders edit page' do - expect(response).to be_successful - end - end - end - end - - private - - def project_params(opts = {}) - opts.reverse_merge( - namespace_id: project.namespace, - project_id: project, - id: integration.to_param - ) - 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 7e96e99640a..d50f1aa1dd8 100644 --- a/spec/controllers/projects/settings/ci_cd_controller_spec.rb +++ b/spec/controllers/projects/settings/ci_cd_controller_spec.rb @@ -25,19 +25,6 @@ RSpec.describe Projects::Settings::CiCdController do expect(response).to render_template(:show) end - context 'when the FF ci_owned_runners_cross_joins_fix is disabled' do - before do - stub_feature_flags(ci_owned_runners_cross_joins_fix: false) - end - - it 'renders show with 200 status code' do - get :show, params: { namespace_id: project.namespace, project_id: project } - - expect(response).to have_gitlab_http_status(:ok) - expect(response).to render_template(:show) - end - end - context 'with CI/CD disabled' do before do project.project_feature.update_attribute(:builds_access_level, ProjectFeature::DISABLED) diff --git a/spec/controllers/projects/service_hook_logs_controller_spec.rb b/spec/controllers/projects/settings/integration_hook_logs_controller_spec.rb index be78668aa88..8261461e8aa 100644 --- a/spec/controllers/projects/service_hook_logs_controller_spec.rb +++ b/spec/controllers/projects/settings/integration_hook_logs_controller_spec.rb @@ -2,7 +2,7 @@ require 'spec_helper' -RSpec.describe Projects::ServiceHookLogsController do +RSpec.describe Projects::Settings::IntegrationHookLogsController do let(:project) { create(:project, :repository) } let(:user) { create(:user) } let(:integration) { create(:drone_ci_integration, project: project) } @@ -44,7 +44,8 @@ RSpec.describe Projects::ServiceHookLogsController do it 'executes the hook and redirects to the service form' do expect_any_instance_of(ServiceHook).to receive(:execute) expect_any_instance_of(described_class).to receive(:set_hook_execution_notice) - expect(subject).to redirect_to(edit_project_integration_path(project, integration)) + + expect(subject).to redirect_to(edit_project_settings_integration_path(project, integration)) end it 'renders a 404 if the hook does not exist' do diff --git a/spec/controllers/projects/settings/integrations_controller_spec.rb b/spec/controllers/projects/settings/integrations_controller_spec.rb index 0652786c787..e6ca088a533 100644 --- a/spec/controllers/projects/settings/integrations_controller_spec.rb +++ b/spec/controllers/projects/settings/integrations_controller_spec.rb @@ -3,20 +3,388 @@ require 'spec_helper' RSpec.describe Projects::Settings::IntegrationsController do - let(:project) { create(:project, :public) } - let(:user) { create(:user) } + include JiraIntegrationHelpers + include AfterNextHelpers + + let_it_be(:project) { create(:project, :repository) } + let_it_be(:user) { create(:user) } + let_it_be(:jira_integration) { create(:jira_integration, project: project) } + + let(:integration) { jira_integration } + let(:integration_params) { { username: 'username', password: 'password', url: 'http://example.com' } } before do - project.add_maintainer(user) sign_in(user) + project.add_maintainer(user) + end + + it_behaves_like Integrations::Actions do + let(:integration_attributes) { { project: project } } + + let(:routing_params) do + { + namespace_id: project.namespace, + project_id: project, + id: integration.to_param + } + end end - describe 'GET show' do - it 'renders show with 200 status code' do - get :show, params: { namespace_id: project.namespace, project_id: project } + describe 'GET index' do + it 'renders index with 200 status code' do + get :index, params: { namespace_id: project.namespace, project_id: project } expect(response).to have_gitlab_http_status(:ok) - expect(response).to render_template(:show) + expect(response).to render_template(:index) + end + end + + describe '#test' do + context 'when the integration is not testable' do + it 'renders 404' do + allow_any_instance_of(Integration).to receive(:testable?).and_return(false) + + put :test, params: project_params + + expect(response).to have_gitlab_http_status(:not_found) + end + end + + context 'when validations fail' do + let(:integration_params) { { active: 'true', url: '' } } + + it 'returns error messages in JSON response' do + put :test, params: project_params(service: integration_params) + + expect(json_response['message']).to eq 'Validations failed.' + expect(json_response['service_response']).to include "Url can't be blank" + expect(response).to be_successful + end + end + + context 'when successful' do + context 'with empty project' do + let_it_be(:project) { create(:project) } + + context 'with chat notification integration' do + let_it_be(:teams_integration) { project.create_microsoft_teams_integration(webhook: 'http://webhook.com') } + + let(:integration) { teams_integration } + + it 'returns success' do + allow_next(::MicrosoftTeams::Notifier).to receive(:ping).and_return(true) + + put :test, params: project_params + + expect(response).to be_successful + end + end + + it 'returns success' do + stub_jira_integration_test + + expect(Gitlab::HTTP).to receive(:get).with('/rest/api/2/serverInfo', any_args).and_call_original + + put :test, params: project_params(service: integration_params) + + expect(response).to be_successful + end + end + + it 'returns success' do + stub_jira_integration_test + + expect(Gitlab::HTTP).to receive(:get).with('/rest/api/2/serverInfo', any_args).and_call_original + + put :test, params: project_params(service: integration_params) + + expect(response).to be_successful + end + + context 'when service is configured for the first time' do + let(:integration_params) do + { + 'active' => '1', + 'push_events' => '1', + 'token' => 'token', + 'project_url' => 'https://buildkite.com/organization/pipeline' + } + end + + before do + allow_next(ServiceHook).to receive(:execute).and_return(true) + end + + it 'persist the object' do + do_put + + expect(response).to be_successful + expect(json_response).to be_empty + expect(Integrations::Buildkite.first).to be_present + end + + it 'creates the ServiceHook object' do + do_put + + expect(response).to be_successful + expect(json_response).to be_empty + expect(Integrations::Buildkite.first.service_hook).to be_present + end + + def do_put + put :test, params: project_params(id: 'buildkite', + service: integration_params) + end + end + end + + context 'when unsuccessful' do + it 'returns an error response when the integration test fails' do + stub_request(:get, 'http://example.com/rest/api/2/serverInfo') + .to_return(status: 404) + + put :test, params: project_params(service: integration_params) + + expect(response).to be_successful + expect(json_response).to eq( + 'error' => true, + 'message' => 'Connection failed. Please check your settings.', + 'service_response' => '', + 'test_failed' => true + ) + end + + context 'with the Slack integration' do + let_it_be(:integration) { build(:integrations_slack) } + + it 'returns an error response when the URL is blocked' do + put :test, params: project_params(service: { webhook: 'http://127.0.0.1' }) + + expect(response).to be_successful + expect(json_response).to eq( + 'error' => true, + 'message' => 'Connection failed. Please check your settings.', + 'service_response' => "URL 'http://127.0.0.1' is blocked: Requests to localhost are not allowed", + 'test_failed' => true + ) + end + + it 'returns an error response when a network exception is raised' do + expect_next(Integrations::Slack).to receive(:test).and_raise(Errno::ECONNREFUSED) + + put :test, params: project_params + + expect(response).to be_successful + expect(json_response).to eq( + 'error' => true, + 'message' => 'Connection failed. Please check your settings.', + 'service_response' => 'Connection refused', + 'test_failed' => true + ) + end + end + end + end + + describe 'PUT #update' do + describe 'as HTML' do + let(:integration_params) { { active: true } } + let(:params) { project_params(service: integration_params) } + + let(:message) { 'Jira settings saved and active.' } + let(:redirect_url) { edit_project_settings_integration_path(project, integration) } + + before do + stub_jira_integration_test + + put :update, params: params + end + + shared_examples 'integration update' do + it 'redirects to the correct url with a flash message' do + expect(response).to redirect_to(redirect_url) + expect(flash[:notice]).to eq(message) + end + end + + context 'when update fails' do + let(:integration_params) { { url: 'https://new.com', password: '' } } + + it 'renders the edit form' do + expect(response).to have_gitlab_http_status(:ok) + expect(response).to render_template(:edit) + expect(integration.reload.url).not_to eq('https://new.com') + end + end + + context 'when param `active` is set to true' do + let(:params) { project_params(service: integration_params, redirect_to: redirect) } + + context 'when redirect_to param is present' do + let(:redirect) { '/redirect_here' } + let(:redirect_url) { redirect } + + it_behaves_like 'integration update' + end + + context 'when redirect_to is an external domain' do + let(:redirect) { 'http://examle.com' } + + it_behaves_like 'integration update' + end + + context 'when redirect_to param is an empty string' do + let(:redirect) { '' } + + it_behaves_like 'integration update' + end + end + + context 'when param `active` is set to false' do + let(:integration_params) { { active: false } } + let(:message) { 'Jira settings saved, but not active.' } + + it_behaves_like 'integration update' + end + + context 'when param `inherit_from_id` is set to empty string' do + let(:integration_params) { { inherit_from_id: '' } } + + it 'sets inherit_from_id to nil' do + expect(integration.reload.inherit_from_id).to eq(nil) + end + end + + context 'when param `inherit_from_id` is set to an instance integration' do + let(:instance_integration) do + create(:jira_integration, :instance, url: 'http://instance.com', password: 'instance') + end + + let(:integration_params) do + { inherit_from_id: instance_integration.id, url: 'http://custom.com', password: 'custom' } + end + + it 'ignores submitted params and inherits instance settings' do + expect(integration.reload).to have_attributes( + inherit_from_id: instance_integration.id, + url: instance_integration.url, + password: instance_integration.password + ) + end + end + + context 'when param `inherit_from_id` is set to a group integration' do + let_it_be(:group) { create(:group) } + let_it_be(:project) { create(:project, group: group) } + let_it_be(:jira_integration) { create(:jira_integration, project: project) } + + let(:group_integration) do + create(:jira_integration, :group, group: group, url: 'http://group.com', password: 'group') + end + + let(:integration_params) do + { inherit_from_id: group_integration.id, url: 'http://custom.com', password: 'custom' } + end + + it 'ignores submitted params and inherits group settings' do + expect(integration.reload).to have_attributes( + inherit_from_id: group_integration.id, + url: group_integration.url, + password: group_integration.password + ) + end + end + + context 'when param `inherit_from_id` is set to an unrelated group' do + let_it_be(:group) { create(:group) } + + let(:group_integration) do + create(:jira_integration, :group, group: group, url: 'http://group.com', password: 'group') + end + + let(:integration_params) do + { inherit_from_id: group_integration.id, url: 'http://custom.com', password: 'custom' } + end + + it 'ignores the param and saves the submitted settings' do + expect(integration.reload).to have_attributes( + inherit_from_id: nil, + url: 'http://custom.com', + password: 'custom' + ) + end + end + end + + describe 'as JSON' do + before do + stub_jira_integration_test + put :update, params: project_params(service: integration_params, format: :json) + end + + context 'when update succeeds' do + let(:integration_params) { { url: 'http://example.com', password: 'password' } } + + it 'returns success response' do + expect(response).to be_successful + expect(json_response).to include( + 'active' => true, + 'errors' => {} + ) + end + end + + context 'when update fails with missing password' do + let(:integration_params) { { url: 'http://example.com' } } + + it 'returns JSON response errors' do + expect(response).not_to be_successful + expect(json_response).to include( + 'active' => true, + 'errors' => { + 'password' => ["can't be blank"] + } + ) + end + end + + context 'when update fails with invalid URL' do + let(:integration_params) { { url: '', password: 'password' } } + + it 'returns JSON response with errors' do + expect(response).to have_gitlab_http_status(:unprocessable_entity) + expect(json_response).to include( + 'active' => true, + 'errors' => { 'url' => ['must be a valid URL', "can't be blank"] } + ) + end + end + end + end + + describe 'GET #edit' do + context 'with Jira service' do + let(:integration_param) { 'jira' } + + before do + get :edit, params: project_params(id: integration_param) + end + + context 'with approved services' do + it 'renders edit page' do + expect(response).to be_successful + end + end end end + + private + + def project_params(opts = {}) + opts.reverse_merge( + namespace_id: project.namespace, + project_id: project, + id: integration.to_param + ) + end end diff --git a/spec/controllers/projects/static_site_editor_controller_spec.rb b/spec/controllers/projects/static_site_editor_controller_spec.rb deleted file mode 100644 index e1f25589eeb..00000000000 --- a/spec/controllers/projects/static_site_editor_controller_spec.rb +++ /dev/null @@ -1,101 +0,0 @@ -# frozen_string_literal: true - -require 'spec_helper' - -RSpec.describe Projects::StaticSiteEditorController do - let_it_be(:project) { create(:project, :public, :repository) } - let_it_be(:user) { create(:user) } - - let(:data) { { key: 'value' } } - - describe 'GET index' do - let(:default_params) do - { - namespace_id: project.namespace, - project_id: project - } - end - - it 'responds with 404 page' do - get :index, params: default_params - - expect(response).to have_gitlab_http_status(:not_found) - end - end - - describe 'GET show' do - render_views - - let(:default_params) do - { - namespace_id: project.namespace, - project_id: project, - id: 'master/README.md', - return_url: 'http://example.com' - } - end - - let(:service_response) do - ServiceResponse.success(payload: data) - end - - before do - allow_next_instance_of(::StaticSiteEditor::ConfigService) do |instance| - allow(instance).to receive(:execute).and_return(service_response) - end - end - - context 'User roles' do - context 'anonymous' do - before do - get :show, params: default_params - end - - it 'redirects to sign in and returns' do - expect(response).to redirect_to(new_user_session_path) - end - end - - context 'as guest' do - before do - project.add_guest(user) - sign_in(user) - get :show, params: default_params - end - - it 'responds with 404 page' do - expect(response).to have_gitlab_http_status(:not_found) - end - end - - context "as developer" do - before do - allow(Gitlab::UsageDataCounters::StaticSiteEditorCounter).to receive(:increment_views_count) - project.add_role(user, 'developer') - sign_in(user) - get :show, params: default_params - end - - it 'redirects to the Web IDE' do - get :show, params: default_params - - expected_path_regex = %r[-/ide/project/#{project.full_path}/edit/master/-/README.md] - expect(response).to redirect_to(expected_path_regex) - end - - it 'assigns ref and path variables' do - expect(assigns(:ref)).to eq('master') - expect(assigns(:path)).to eq('README.md') - end - - context 'when combination of ref and path is incorrect' do - let(:default_params) { super().merge(id: 'unknown') } - - it 'responds with 404 page' do - expect(response).to have_gitlab_http_status(:not_found) - end - end - end - end - end -end diff --git a/spec/controllers/projects/tags_controller_spec.rb b/spec/controllers/projects/tags_controller_spec.rb index d0971e96910..3d1f8c12022 100644 --- a/spec/controllers/projects/tags_controller_spec.rb +++ b/spec/controllers/projects/tags_controller_spec.rb @@ -205,15 +205,13 @@ RSpec.describe Projects::TagsController do before do project.add_developer(user) sign_in(user) - end - - it 'deletes tag' do request + end - expect(response).to be_successful - expect(response.body).to include("Tag was removed") - + it 'deletes tag and redirects to tags path' do expect(project.repository.find_tag(tag.name)).not_to be_present + expect(controller).to set_flash[:notice].to(/Tag was removed/) + expect(response).to redirect_to(project_tags_path(project)) end end end |