409 lines
		
	
	
		
			15 KiB
		
	
	
	
		
			Ruby
		
	
	
	
			
		
		
	
	
			409 lines
		
	
	
		
			15 KiB
		
	
	
	
		
			Ruby
		
	
	
	
# frozen_string_literal: true
 | 
						|
 | 
						|
require "spec_helper"
 | 
						|
 | 
						|
RSpec.describe API::Integrations, feature_category: :integrations do
 | 
						|
  include Integrations::TestHelpers
 | 
						|
 | 
						|
  let_it_be(:user) { create(:user) }
 | 
						|
  let_it_be(:user2) { create(:user) }
 | 
						|
  let_it_be(:project, reload: true) { create(:project, creator_id: user.id, namespace: user.namespace) }
 | 
						|
 | 
						|
  let_it_be(:available_integration_names) do
 | 
						|
    excluded_integrations = [Integrations::GitlabSlackApplication.to_param, Integrations::Zentao.to_param]
 | 
						|
 | 
						|
    Integration.available_integration_names(include_instance_specific: false) - excluded_integrations
 | 
						|
  end
 | 
						|
 | 
						|
  let_it_be(:project_integrations_map) do
 | 
						|
    available_integration_names.index_with do |name|
 | 
						|
      create(integration_factory(name), :inactive, project: project)
 | 
						|
    end
 | 
						|
  end
 | 
						|
 | 
						|
  let_it_be(:project2) { create(:project, creator_id: user.id, namespace: user.namespace) }
 | 
						|
 | 
						|
  %w[integrations services].each do |endpoint|
 | 
						|
    describe "GET /projects/:id/#{endpoint}" do
 | 
						|
      it 'returns authentication error when unauthenticated' do
 | 
						|
        get api("/projects/#{project.id}/#{endpoint}")
 | 
						|
 | 
						|
        expect(response).to have_gitlab_http_status(:unauthorized)
 | 
						|
      end
 | 
						|
 | 
						|
      it "returns error when authenticated but user is not a project owner" do
 | 
						|
        project.add_developer(user2)
 | 
						|
        get api("/projects/#{project.id}/#{endpoint}", user2)
 | 
						|
 | 
						|
        expect(response).to have_gitlab_http_status(:forbidden)
 | 
						|
      end
 | 
						|
 | 
						|
      context 'with integrations' do
 | 
						|
        it "returns a list of all active integrations" do
 | 
						|
          get api("/projects/#{project.id}/#{endpoint}", user)
 | 
						|
 | 
						|
          aggregate_failures 'expect successful response with all active integrations' do
 | 
						|
            expect(response).to have_gitlab_http_status(:ok)
 | 
						|
            expect(json_response).to be_an Array
 | 
						|
            expect(json_response.count).to eq(1)
 | 
						|
            expect(json_response.first['slug']).to eq('prometheus')
 | 
						|
            expect(response).to match_response_schema('public_api/v4/integrations')
 | 
						|
          end
 | 
						|
        end
 | 
						|
      end
 | 
						|
    end
 | 
						|
 | 
						|
    where(:integration) do
 | 
						|
      # The integrations API supports all project integrations.
 | 
						|
      # You cannot create a GitLab for Slack app. You must install the app from the GitLab UI.
 | 
						|
      unavailable_integration_names = [
 | 
						|
        Integrations::GitlabSlackApplication.to_param,
 | 
						|
        Integrations::Zentao.to_param
 | 
						|
      ]
 | 
						|
 | 
						|
      names = Integration.available_integration_names(include_instance_specific: false)
 | 
						|
      names.reject { |name| name.in?(unavailable_integration_names) }
 | 
						|
    end
 | 
						|
 | 
						|
    with_them do
 | 
						|
      integration = params[:integration]
 | 
						|
 | 
						|
      describe "PUT /projects/:id/#{endpoint}/#{integration.dasherize}" do
 | 
						|
        it_behaves_like 'set up an integration', endpoint: endpoint, integration: integration
 | 
						|
      end
 | 
						|
 | 
						|
      describe "DELETE /projects/:id/#{endpoint}/#{integration.dasherize}" do
 | 
						|
        it_behaves_like 'disable an integration', endpoint: endpoint, integration: integration
 | 
						|
      end
 | 
						|
 | 
						|
      describe "GET /projects/:id/#{endpoint}/#{integration.dasherize}" do
 | 
						|
        it_behaves_like 'get an integration settings', endpoint: endpoint, integration: integration
 | 
						|
      end
 | 
						|
    end
 | 
						|
 | 
						|
    describe "POST /projects/:id/#{endpoint}/:slug/trigger" do
 | 
						|
      describe 'Mattermost integration' do
 | 
						|
        let(:integration_name) { 'mattermost_slash_commands' }
 | 
						|
 | 
						|
        context 'when no integration is available' do
 | 
						|
          it 'returns a not found message' do
 | 
						|
            post api("/projects/#{project.id}/#{endpoint}/idonotexist/trigger")
 | 
						|
 | 
						|
            expect(response).to have_gitlab_http_status(:not_found)
 | 
						|
            expect(json_response["error"]).to eq("404 Not Found")
 | 
						|
          end
 | 
						|
        end
 | 
						|
 | 
						|
        context 'when the integration exists' do
 | 
						|
          let(:params) { { token: 'secrettoken' } }
 | 
						|
 | 
						|
          context 'when the integration is not active' do
 | 
						|
            before do
 | 
						|
              project_integrations_map[integration_name].deactivate!
 | 
						|
            end
 | 
						|
 | 
						|
            it 'when the integration is inactive' do
 | 
						|
              post api("/projects/#{project.id}/#{endpoint}/#{integration_name}/trigger"), params: params
 | 
						|
 | 
						|
              expect(response).to have_gitlab_http_status(:not_found)
 | 
						|
            end
 | 
						|
          end
 | 
						|
 | 
						|
          context 'when the integration is active' do
 | 
						|
            before do
 | 
						|
              project_integrations_map[integration_name].activate!
 | 
						|
            end
 | 
						|
 | 
						|
            it 'returns status 200' do
 | 
						|
              post api("/projects/#{project.id}/#{endpoint}/#{integration_name}/trigger"), params: params
 | 
						|
 | 
						|
              expect(response).to have_gitlab_http_status(:ok)
 | 
						|
            end
 | 
						|
          end
 | 
						|
 | 
						|
          context 'when the project can not be found' do
 | 
						|
            it 'returns a generic 404' do
 | 
						|
              post api("/projects/404/#{endpoint}/#{integration_name}/trigger"), params: params
 | 
						|
 | 
						|
              expect(response).to have_gitlab_http_status(:not_found)
 | 
						|
              expect(json_response["message"]).to eq("404 Integration Not Found")
 | 
						|
            end
 | 
						|
          end
 | 
						|
        end
 | 
						|
      end
 | 
						|
 | 
						|
      describe 'Slack Integration' do
 | 
						|
        let(:integration_name) { 'slack_slash_commands' }
 | 
						|
        let(:params) { { token: 'secrettoken', text: 'help' } }
 | 
						|
 | 
						|
        context 'when no integration is available' do
 | 
						|
          it 'returns a not found message' do
 | 
						|
            post api("/projects/#{project.id}/#{endpoint}/idonotexist/trigger")
 | 
						|
 | 
						|
            expect(response).to have_gitlab_http_status(:not_found)
 | 
						|
            expect(json_response["error"]).to eq("404 Not Found")
 | 
						|
          end
 | 
						|
        end
 | 
						|
 | 
						|
        context 'when the integration exists' do
 | 
						|
          context 'when the integration is not active' do
 | 
						|
            before do
 | 
						|
              project_integrations_map[integration_name].deactivate!
 | 
						|
            end
 | 
						|
 | 
						|
            it 'when the integration is inactive' do
 | 
						|
              post api("/projects/#{project.id}/#{endpoint}/#{integration_name}/trigger"), params: params
 | 
						|
 | 
						|
              expect(response).to have_gitlab_http_status(:not_found)
 | 
						|
            end
 | 
						|
          end
 | 
						|
 | 
						|
          context 'when the integration is active' do
 | 
						|
            before do
 | 
						|
              project_integrations_map[integration_name].activate!
 | 
						|
            end
 | 
						|
 | 
						|
            it 'returns status 200' do
 | 
						|
              post api("/projects/#{project.id}/#{endpoint}/#{integration_name}/trigger"), params: params
 | 
						|
 | 
						|
              expect(response).to have_gitlab_http_status(:ok)
 | 
						|
              expect(json_response['response_type']).to eq("ephemeral")
 | 
						|
            end
 | 
						|
          end
 | 
						|
 | 
						|
          context 'when the project can not be found' do
 | 
						|
            it 'returns a generic 404' do
 | 
						|
              post api("/projects/404/#{endpoint}/#{integration_name}/trigger"), params: params
 | 
						|
 | 
						|
              expect(response).to have_gitlab_http_status(:not_found)
 | 
						|
              expect(json_response["message"]).to eq("404 Integration Not Found")
 | 
						|
            end
 | 
						|
          end
 | 
						|
        end
 | 
						|
      end
 | 
						|
    end
 | 
						|
 | 
						|
    describe 'Mattermost integration' do
 | 
						|
      let(:integration_name) { 'mattermost' }
 | 
						|
      let(:params) do
 | 
						|
        { webhook: 'https://hook.example.com', username: 'username' }
 | 
						|
      end
 | 
						|
 | 
						|
      before do
 | 
						|
        project_integrations_map[integration_name].activate!
 | 
						|
      end
 | 
						|
 | 
						|
      it 'accepts a username for update' do
 | 
						|
        put api("/projects/#{project.id}/#{endpoint}/#{integration_name}", user), params: params.merge(username: 'new_username')
 | 
						|
 | 
						|
        expect(response).to have_gitlab_http_status(:ok)
 | 
						|
        expect(json_response['properties']['username']).to eq('new_username')
 | 
						|
      end
 | 
						|
    end
 | 
						|
 | 
						|
    describe 'Microsoft Teams integration' do
 | 
						|
      let_it_be(:group) { create(:group) }
 | 
						|
      let(:integration_name) { 'microsoft-teams' }
 | 
						|
      let(:params) do
 | 
						|
        {
 | 
						|
          webhook: 'https://hook.example.com',
 | 
						|
          branches_to_be_notified: 'default',
 | 
						|
          notify_only_broken_pipelines: false
 | 
						|
        }
 | 
						|
      end
 | 
						|
 | 
						|
      before do
 | 
						|
        create(:microsoft_teams_integration, group: group, project: nil)
 | 
						|
        project.update!(namespace: group)
 | 
						|
        project_integrations_map[integration_name.underscore].activate!
 | 
						|
      end
 | 
						|
 | 
						|
      it 'accepts branches_to_be_notified for update' do
 | 
						|
        put api("/projects/#{project.id}/#{endpoint}/#{integration_name}", user),
 | 
						|
          params: params.merge(branches_to_be_notified: 'all')
 | 
						|
 | 
						|
        expect(response).to have_gitlab_http_status(:ok)
 | 
						|
        expect(json_response['properties']['branches_to_be_notified']).to eq('all')
 | 
						|
      end
 | 
						|
 | 
						|
      it 'accepts notify_only_broken_pipelines for update' do
 | 
						|
        put api("/projects/#{project.id}/#{endpoint}/#{integration_name}", user),
 | 
						|
          params: params.merge(notify_only_broken_pipelines: true)
 | 
						|
 | 
						|
        expect(response).to have_gitlab_http_status(:ok)
 | 
						|
        expect(json_response['properties']['notify_only_broken_pipelines']).to eq(true)
 | 
						|
      end
 | 
						|
 | 
						|
      it 'accepts `use_inherited_settings` for inheritance' do
 | 
						|
        expect do
 | 
						|
          put api("/projects/#{project.id}/#{endpoint}/#{integration_name}", user),
 | 
						|
            params: params.merge(use_inherited_settings: true)
 | 
						|
        end.to change { project_integrations_map[integration_name.underscore].reload.inherit_from_id }.from(nil)
 | 
						|
 | 
						|
        expect(response).to have_gitlab_http_status(:ok)
 | 
						|
        expect(json_response['inherited']).to eq(true)
 | 
						|
      end
 | 
						|
    end
 | 
						|
 | 
						|
    describe 'Hangouts Chat integration' do
 | 
						|
      let(:integration_name) { 'hangouts-chat' }
 | 
						|
      let(:params) do
 | 
						|
        {
 | 
						|
          webhook: 'https://hook.example.com',
 | 
						|
          branches_to_be_notified: 'default'
 | 
						|
        }
 | 
						|
      end
 | 
						|
 | 
						|
      before do
 | 
						|
        project_integrations_map[integration_name.underscore].activate!
 | 
						|
      end
 | 
						|
 | 
						|
      it 'accepts branches_to_be_notified for update', :aggregate_failures do
 | 
						|
        put api("/projects/#{project.id}/#{endpoint}/#{integration_name}", user), params: params.merge(branches_to_be_notified: 'all')
 | 
						|
 | 
						|
        expect(response).to have_gitlab_http_status(:ok)
 | 
						|
        expect(json_response['properties']['branches_to_be_notified']).to eq('all')
 | 
						|
      end
 | 
						|
 | 
						|
      it 'only requires the webhook param' do
 | 
						|
        put api("/projects/#{project.id}/#{endpoint}/#{integration_name}", user), params: { webhook: 'https://hook.example.com' }
 | 
						|
 | 
						|
        expect(response).to have_gitlab_http_status(:ok)
 | 
						|
      end
 | 
						|
    end
 | 
						|
 | 
						|
    describe 'Jira integration' do
 | 
						|
      let(:integration_name) { 'jira' }
 | 
						|
      let(:params) do
 | 
						|
        { url: 'https://jira.example.com', username: 'username', password: 'password', jira_auth_type: 0 }
 | 
						|
      end
 | 
						|
 | 
						|
      before do
 | 
						|
        project_integrations_map[integration_name].properties = params
 | 
						|
        project_integrations_map[integration_name].activate!
 | 
						|
      end
 | 
						|
 | 
						|
      it 'returns the jira_issue_transition_id for get request' do
 | 
						|
        get api("/projects/#{project.id}/#{endpoint}/#{integration_name}", user)
 | 
						|
 | 
						|
        expect(response).to have_gitlab_http_status(:ok)
 | 
						|
        expect(json_response['properties']).to include('jira_issue_transition_id' => '56-1')
 | 
						|
      end
 | 
						|
 | 
						|
      it 'returns the jira_issue_transition_id for put request' do
 | 
						|
        put api("/projects/#{project.id}/#{endpoint}/#{integration_name}", user), params: params.merge(jira_issue_transition_id: '1')
 | 
						|
 | 
						|
        expect(response).to have_gitlab_http_status(:ok)
 | 
						|
        expect(json_response['properties']['jira_issue_transition_id']).to eq('1')
 | 
						|
      end
 | 
						|
    end
 | 
						|
 | 
						|
    describe 'Pipelines Email Integration' do
 | 
						|
      let(:integration_name) { 'pipelines-email' }
 | 
						|
 | 
						|
      context 'notify_only_broken_pipelines property was saved as a string' do
 | 
						|
        before do
 | 
						|
          project_integrations_map[integration_name.underscore].activate!
 | 
						|
        end
 | 
						|
 | 
						|
        it 'returns boolean values for notify_only_broken_pipelines' do
 | 
						|
          get api("/projects/#{project.id}/#{endpoint}/#{integration_name}", user)
 | 
						|
 | 
						|
          expect(json_response['properties']['notify_only_broken_pipelines']).to eq(true)
 | 
						|
        end
 | 
						|
      end
 | 
						|
    end
 | 
						|
 | 
						|
    describe 'GitLab for Slack app integration' do
 | 
						|
      before do
 | 
						|
        stub_application_setting(slack_app_enabled: true)
 | 
						|
        create(:gitlab_slack_application_integration, project: project)
 | 
						|
      end
 | 
						|
 | 
						|
      describe "PUT /projects/:id/#{endpoint}/gitlab-slack-application" do
 | 
						|
        context 'for integration creation' do
 | 
						|
          before do
 | 
						|
            project.gitlab_slack_application_integration.destroy!
 | 
						|
          end
 | 
						|
 | 
						|
          it 'returns 422' do
 | 
						|
            put api("/projects/#{project.id}/#{endpoint}/gitlab-slack-application", user)
 | 
						|
 | 
						|
            expect(response).to have_gitlab_http_status(:unprocessable_entity)
 | 
						|
          end
 | 
						|
        end
 | 
						|
 | 
						|
        context 'for integration update' do
 | 
						|
          before do
 | 
						|
            project.gitlab_slack_application_integration.update!(active: false)
 | 
						|
          end
 | 
						|
 | 
						|
          it "does not enable the integration" do
 | 
						|
            put api("/projects/#{project.id}/#{endpoint}/gitlab-slack-application", user)
 | 
						|
 | 
						|
            expect(response).to have_gitlab_http_status(:ok)
 | 
						|
            expect(project.gitlab_slack_application_integration.reload).to have_attributes(active: false)
 | 
						|
          end
 | 
						|
        end
 | 
						|
      end
 | 
						|
 | 
						|
      describe "GET /projects/:id/#{endpoint}/gitlab-slack-application" do
 | 
						|
        it "fetches the integration and returns the correct fields" do
 | 
						|
          get api("/projects/#{project.id}/#{endpoint}/gitlab-slack-application", user)
 | 
						|
 | 
						|
          expect(response).to have_gitlab_http_status(:ok)
 | 
						|
          assert_correct_response_fields(json_response['properties'].keys, project.gitlab_slack_application_integration)
 | 
						|
        end
 | 
						|
      end
 | 
						|
 | 
						|
      describe "DELETE /projects/:id/#{endpoint}/gitlab-slack-application" do
 | 
						|
        it "disables the integration" do
 | 
						|
          expect { delete api("/projects/#{project.id}/#{endpoint}/gitlab-slack-application", user) }
 | 
						|
            .to change { project.gitlab_slack_application_integration.reload.activated? }.from(true).to(false)
 | 
						|
 | 
						|
          expect(response).to have_gitlab_http_status(:no_content)
 | 
						|
        end
 | 
						|
      end
 | 
						|
    end
 | 
						|
 | 
						|
    private
 | 
						|
 | 
						|
    def assert_correct_response_fields(response_keys, integration)
 | 
						|
      assert_fields_match_integration(response_keys, integration)
 | 
						|
      assert_secret_fields_filtered(response_keys, integration)
 | 
						|
    end
 | 
						|
 | 
						|
    def assert_fields_match_integration(response_keys, integration)
 | 
						|
      expect(response_keys).to match_array(integration.api_field_names)
 | 
						|
    end
 | 
						|
 | 
						|
    def assert_secret_fields_filtered(response_keys, integration)
 | 
						|
      expect(response_keys).not_to include(*integration.secret_fields)
 | 
						|
    end
 | 
						|
  end
 | 
						|
 | 
						|
  describe 'POST /slack/trigger' do
 | 
						|
    before_all do
 | 
						|
      create(:gitlab_slack_application_integration, project: project)
 | 
						|
    end
 | 
						|
 | 
						|
    before do
 | 
						|
      stub_application_setting(slack_app_verification_token: 'token')
 | 
						|
    end
 | 
						|
 | 
						|
    it 'returns status 200' do
 | 
						|
      post api('/slack/trigger'), params: { token: 'token', text: 'help' }
 | 
						|
 | 
						|
      expect(response).to have_gitlab_http_status(:ok)
 | 
						|
      expect(json_response['response_type']).to eq("ephemeral")
 | 
						|
    end
 | 
						|
 | 
						|
    it 'returns status 404 when token is invalid' do
 | 
						|
      post api('/slack/trigger'), params: { token: 'invalid', text: 'foo' }
 | 
						|
 | 
						|
      expect(response).to have_gitlab_http_status(:not_found)
 | 
						|
      expect(json_response['response_type']).to be_blank
 | 
						|
    end
 | 
						|
  end
 | 
						|
end
 |