# frozen_string_literal: true require 'spec_helper' RSpec.describe Projects::Settings::OperationsController do let_it_be(:user) { create(:user) } let_it_be(:project, reload: true) { create(:project) } before_all do project.add_maintainer(user) end before do sign_in(user) end shared_examples 'PATCHable' do let(:operations_update_service) { instance_double(::Projects::Operations::UpdateService) } let(:operations_url) { project_settings_operations_url(project) } let(:permitted_params) do ActionController::Parameters.new(params).permit! end context 'format json' do context 'when update succeeds' do it 'returns success status' do stub_operations_update_service_returning(status: :success) patch :update, params: project_params(project, params), format: :json expect(response).to have_gitlab_http_status(:ok) expect(json_response).to eq('status' => 'success') expect(flash[:notice]).to eq('Your changes have been saved') end end context 'when update fails' do it 'returns error' do stub_operations_update_service_returning( status: :error, message: 'error message' ) patch :update, params: project_params(project, params), format: :json expect(response).to have_gitlab_http_status(:bad_request) expect(json_response['message']).to eq('error message') end end end private def stub_operations_update_service_returning(return_value = {}) expect(::Projects::Operations::UpdateService) .to receive(:new).with(project, user, permitted_params) .and_return(operations_update_service) expect(operations_update_service).to receive(:execute) .and_return(return_value) end end describe 'GET #show' do it 'renders show template' do get :show, params: project_params(project) expect(response).to have_gitlab_http_status(:ok) expect(response).to render_template(:show) end context 'with insufficient permissions' do before do project.add_reporter(user) end it 'renders 404' do get :show, params: project_params(project) expect(response).to have_gitlab_http_status(:not_found) end end context 'as an anonymous user' do before do sign_out(user) end it 'redirects to signup page' do get :show, params: project_params(project) expect(response).to redirect_to(new_user_session_path) end end end describe 'PATCH #update' do context 'with insufficient permissions' do before do project.add_reporter(user) end it 'renders 404' do patch :update, params: project_params(project) expect(response).to have_gitlab_http_status(:not_found) end end context 'as an anonymous user' do before do sign_out(user) end it 'redirects to signup page' do patch :update, params: project_params(project) expect(response).to redirect_to(new_user_session_path) end end end context 'incident management' do describe 'GET #show' do context 'with existing setting' do let!(:incident_management_setting) do create(:project_incident_management_setting, project: project) end it 'loads existing setting' do get :show, params: project_params(project) expect(controller.helpers.project_incident_management_setting) .to eq(incident_management_setting) end end context 'without an existing setting' do it 'builds a new setting' do get :show, params: project_params(project) expect(controller.helpers.project_incident_management_setting).to be_new_record end end end describe 'PATCH #update' do let(:params) do { incident_management_setting_attributes: { create_issue: 'false', send_email: 'false', issue_template_key: 'some-other-template', pagerduty_active: 'true', auto_close_incident: 'true' } } end it_behaves_like 'PATCHable' context 'updating each incident management setting' do let(:new_incident_management_settings) { {} } shared_examples 'a gitlab tracking event' do |params, event_key, **args| it "creates a gitlab tracking event #{event_key}", :snowplow do new_incident_management_settings = params patch :update, params: project_params(project, incident_management_setting_attributes: new_incident_management_settings) project.reload expect_snowplow_event(category: 'IncidentManagement::Settings', action: event_key, **args) end end it_behaves_like 'a gitlab tracking event', { create_issue: '1' }, 'enabled_issue_auto_creation_on_alerts' it_behaves_like 'a gitlab tracking event', { create_issue: '0' }, 'disabled_issue_auto_creation_on_alerts' it_behaves_like 'a gitlab tracking event', { issue_template_key: 'template' }, 'enabled_issue_template_on_alerts', label: "Template name", property: "template" it_behaves_like 'a gitlab tracking event', { issue_template_key: nil }, 'disabled_issue_template_on_alerts', label: "Template name", property: "" it_behaves_like 'a gitlab tracking event', { send_email: '1' }, 'enabled_sending_emails' it_behaves_like 'a gitlab tracking event', { send_email: '0' }, 'disabled_sending_emails' it_behaves_like 'a gitlab tracking event', { pagerduty_active: '1' }, 'enabled_pagerduty_webhook' it_behaves_like 'a gitlab tracking event', { pagerduty_active: '0' }, 'disabled_pagerduty_webhook' it_behaves_like 'a gitlab tracking event', { auto_close_incident: '1' }, 'enabled_auto_close_incident' it_behaves_like 'a gitlab tracking event', { auto_close_incident: '0' }, 'disabled_auto_close_incident' end end describe 'POST #reset_pagerduty_token' do context 'with existing incident management setting has active PagerDuty webhook' do let!(:incident_management_setting) do create(:project_incident_management_setting, project: project, pagerduty_active: true) end let!(:old_token) { incident_management_setting.pagerduty_token } it 'returns newly reset token' do reset_pagerduty_token new_token = incident_management_setting.reload.pagerduty_token new_webhook_url = project_incidents_integrations_pagerduty_url(project, token: new_token) expect(response).to have_gitlab_http_status(:ok) expect(json_response['pagerduty_webhook_url']).to eq(new_webhook_url) expect(json_response['pagerduty_token']).to eq(new_token) expect(old_token).not_to eq(new_token) end end context 'without existing incident management setting' do it 'does not reset a token' do reset_pagerduty_token new_webhook_url = project_incidents_integrations_pagerduty_url(project, token: nil) expect(response).to have_gitlab_http_status(:ok) expect(json_response['pagerduty_webhook_url']).to eq(new_webhook_url) expect(project.incident_management_setting.pagerduty_token).to be_nil end end context 'when update fails' do let(:operations_update_service) { spy(:operations_update_service) } let(:pagerduty_token_params) do { incident_management_setting_attributes: { regenerate_token: true } } end before do expect(::Projects::Operations::UpdateService) .to receive(:new).with(project, user, pagerduty_token_params) .and_return(operations_update_service) expect(operations_update_service).to receive(:execute) .and_return(status: :error) end it 'returns unprocessable_entity' do reset_pagerduty_token expect(response).to have_gitlab_http_status(:unprocessable_entity) expect(json_response).to be_empty end end context 'with insufficient permissions' do before do project.add_reporter(user) end it 'returns 404' do reset_pagerduty_token expect(response).to have_gitlab_http_status(:not_found) end end context 'as an anonymous user' do before do sign_out(user) end it 'returns a redirect' do reset_pagerduty_token expect(response).to have_gitlab_http_status(:redirect) end end private def reset_pagerduty_token post :reset_pagerduty_token, params: project_params(project), format: :json end end end context 'error tracking' do describe 'GET #show' do context 'with existing setting' do let!(:error_tracking_setting) do create(:project_error_tracking_setting, project: project) end it 'loads existing setting' do get :show, params: project_params(project) expect(controller.helpers.error_tracking_setting) .to eq(error_tracking_setting) end end context 'without an existing setting' do it 'builds a new setting' do get :show, params: project_params(project) expect(controller.helpers.error_tracking_setting).to be_new_record end end end describe 'PATCH #update' do let(:params) do { error_tracking_setting_attributes: { enabled: '1', api_host: 'http://url', token: 'token', project: { slug: 'sentry-project', name: 'Sentry Project', organization_slug: 'sentry-org', organization_name: 'Sentry Org' } } } end it_behaves_like 'PATCHable' end end context 'metrics dashboard setting' do describe 'PATCH #update' do let(:params) do { metrics_setting_attributes: { external_dashboard_url: 'https://gitlab.com' } } end it_behaves_like 'PATCHable' end end context 'grafana integration' do describe 'PATCH #update' do let(:params) do { grafana_integration_attributes: { grafana_url: 'https://grafana.gitlab.com', token: 'eyJrIjoicDRlRTREdjhhOEZ5WjZPWXUzazJOSW0zZHJUejVOd3IiLCJuIjoiVGVzdCBLZXkiLCJpZCI6MX0=', enabled: 'true' } } end it_behaves_like 'PATCHable' end end context 'prometheus integration' do describe 'PATCH #update' do let(:params) do { prometheus_integration_attributes: { manual_configuration: '0', api_url: 'https://gitlab.prometheus.rocks' } } end context 'feature flag :settings_operations_prometheus_service is enabled' do before do stub_feature_flags(settings_operations_prometheus_service: true) end it_behaves_like 'PATCHable' end context 'feature flag :settings_operations_prometheus_service is disabled' do before do stub_feature_flags(settings_operations_prometheus_service: false) end it_behaves_like 'PATCHable' do let(:permitted_params) do ActionController::Parameters.new(params.except(:prometheus_integration_attributes)).permit! end end end end describe 'POST #reset_alerting_token' do context 'with existing alerting setting' do let!(:alerting_setting) do create(:project_alerting_setting, project: project) end let!(:old_token) { alerting_setting.token } it 'returns newly reset token' do reset_alerting_token expect(response).to have_gitlab_http_status(:ok) expect(json_response['token']).to eq(alerting_setting.reload.token) expect(old_token).not_to eq(alerting_setting.token) end end context 'without existing alerting setting' do it 'creates a token' do reset_alerting_token expect(response).to have_gitlab_http_status(:ok) expect(project.alerting_setting).not_to be_nil expect(json_response['token']).to eq(project.alerting_setting.token) end end context 'when update fails' do let(:operations_update_service) { spy(:operations_update_service) } let(:alerting_params) do { alerting_setting_attributes: { regenerate_token: true } } end before do expect(::Projects::Operations::UpdateService) .to receive(:new).with(project, user, alerting_params) .and_return(operations_update_service) expect(operations_update_service).to receive(:execute) .and_return(status: :error) end it 'returns unprocessable_entity' do reset_alerting_token expect(response).to have_gitlab_http_status(:unprocessable_entity) expect(json_response).to be_empty end end context 'with insufficient permissions' do before do project.add_reporter(user) end it 'returns 404' do reset_alerting_token expect(response).to have_gitlab_http_status(:not_found) end end context 'as an anonymous user' do before do sign_out(user) end it 'returns a redirect' do reset_alerting_token expect(response).to have_gitlab_http_status(:redirect) end end private def reset_alerting_token post :reset_alerting_token, params: project_params(project), format: :json end end end context 'tracing integration' do describe 'GET #show' do context 'with existing setting' do let_it_be(:setting) do create(:project_tracing_setting, project: project) end it 'loads existing setting' do get :show, params: project_params(project) expect(controller.helpers.tracing_setting).to eq(setting) end end context 'without an existing setting' do it 'builds a new setting' do get :show, params: project_params(project) expect(controller.helpers.tracing_setting).to be_new_record end end end describe 'PATCH #update' do let_it_be(:external_url) { 'https://gitlab.com' } let(:params) do { tracing_setting_attributes: { external_url: external_url } } end it_behaves_like 'PATCHable' describe 'gitlab tracking', :snowplow do shared_examples 'event tracking' do it 'tracks an event' do expect_snowplow_event( category: 'project:operations:tracing', action: 'external_url_populated', user: user, project: project, namespace: project.namespace ) end end shared_examples 'no event tracking' do it 'does not track an event' do expect_no_snowplow_event end end before do make_request end subject(:make_request) do patch :update, params: project_params(project, params), format: :json end context 'without existing setting' do context 'when creating a new setting' do it_behaves_like 'event tracking' end context 'with invalid external_url' do let_it_be(:external_url) { nil } it_behaves_like 'no event tracking' end end context 'with existing setting' do let_it_be(:existing_setting) do create(:project_tracing_setting, project: project, external_url: external_url) end context 'when changing external_url' do let_it_be(:external_url) { 'https://example.com' } it_behaves_like 'no event tracking' end context 'with unchanged external_url' do it_behaves_like 'no event tracking' end context 'with invalid external_url' do let_it_be(:external_url) { nil } it_behaves_like 'no event tracking' end end end end end private def project_params(project, params = {}) { namespace_id: project.namespace, project_id: project, project: params } end end