From 24d019352e82b4f2c29a5d8cf86068e1ffd0dea3 Mon Sep 17 00:00:00 2001
From: Bojan Marjanovic <bmarjanovic@gitlab.com>
Date: Thu, 8 Feb 2024 15:03:21 +0000
Subject: [PATCH] Add basic testing functionality for group level

Changelog: added
EE: true
---
 .../concerns/integrations/actions.rb          | 27 +++++++-
 app/models/integrations/jira.rb               |  4 ++
 .../concerns/integrations/group_test_data.rb  | 13 ++++
 .../integrations/test/group_service.rb        | 25 +++++++
 ee/app/models/ee/integrations/jira.rb         |  9 +--
 ee/spec/models/ee/integrations/jira_spec.rb   |  6 +-
 .../integrations/test/group_service_spec.rb   | 65 +++++++++++++++++++
 .../integrations_actions_shared_examples.rb   | 29 +++++++++
 8 files changed, 168 insertions(+), 10 deletions(-)
 create mode 100644 app/services/concerns/integrations/group_test_data.rb
 create mode 100644 app/services/integrations/test/group_service.rb
 create mode 100644 spec/services/integrations/test/group_service_spec.rb

diff --git a/app/controllers/concerns/integrations/actions.rb b/app/controllers/concerns/integrations/actions.rb
index 10e86bcc98d0c..d936247d8c897 100644
--- a/app/controllers/concerns/integrations/actions.rb
+++ b/app/controllers/concerns/integrations/actions.rb
@@ -46,7 +46,11 @@ def update
   end
 
   def test
-    render json: {}, status: :ok
+    if integration.testable?
+      render json: integration_test_response, status: :ok
+    else
+      render json: {}, status: :not_found
+    end
   end
 
   def reset
@@ -80,4 +84,25 @@ def serialize_as_json
       .as_json(only: integration.json_fields)
       .merge(errors: integration.errors.as_json)
   end
+
+  def integration_test_response
+    result = if integration.project_level?
+               ::Integrations::Test::ProjectService.new(integration, current_user, params[:event]).execute
+             elsif integration.group_level?
+               ::Integrations::Test::GroupService.new(integration, current_user, params[:event]).execute
+             else
+               {}
+             end
+
+    unless result[:success]
+      return {
+        error: true,
+        message: s_('Integrations|Connection failed. Check your integration settings.'),
+        service_response: result[:result].to_s,
+        test_failed: true
+      }
+    end
+
+    result[:data].presence || {}
+  end
 end
diff --git a/app/models/integrations/jira.rb b/app/models/integrations/jira.rb
index 268c36b351777..a30e3d868da72 100644
--- a/app/models/integrations/jira.rb
+++ b/app/models/integrations/jira.rb
@@ -408,6 +408,10 @@ def avatar_url
       ActionController::Base.helpers.image_path('illustrations/third-party-logos/integrations-logos/jira.svg')
     end
 
+    def testable?
+      group_level? || project_level?
+    end
+
     private
 
     def jira_issue_match_regex
diff --git a/app/services/concerns/integrations/group_test_data.rb b/app/services/concerns/integrations/group_test_data.rb
new file mode 100644
index 0000000000000..716580e07b566
--- /dev/null
+++ b/app/services/concerns/integrations/group_test_data.rb
@@ -0,0 +1,13 @@
+# frozen_string_literal: true
+
+module Integrations
+  module GroupTestData
+    NoDataError = Class.new(ArgumentError)
+
+    private
+
+    def push_events_data
+      Gitlab::DataBuilder::Push.sample_data
+    end
+  end
+end
diff --git a/app/services/integrations/test/group_service.rb b/app/services/integrations/test/group_service.rb
new file mode 100644
index 0000000000000..d10b460919958
--- /dev/null
+++ b/app/services/integrations/test/group_service.rb
@@ -0,0 +1,25 @@
+# frozen_string_literal: true
+
+module Integrations
+  module Test
+    class GroupService < Integrations::Test::BaseService
+      include Integrations::GroupTestData
+      include Gitlab::Utils::StrongMemoize
+
+      def group
+        integration.group
+      end
+      strong_memoize_attr :group
+
+      private
+
+      def data
+        case event || integration.default_test_event
+        when 'push', 'tag_push'
+          push_events_data
+        end
+      end
+      strong_memoize_attr :data
+    end
+  end
+end
diff --git a/ee/app/models/ee/integrations/jira.rb b/ee/app/models/ee/integrations/jira.rb
index db37579e1697e..e5211b28548e3 100644
--- a/ee/app/models/ee/integrations/jira.rb
+++ b/ee/app/models/ee/integrations/jira.rb
@@ -39,7 +39,7 @@ def configured_to_create_issues_from_vulnerabilities?
       def test(_)
         super.then do |result|
           next result unless result[:success]
-          next result unless project.jira_vulnerabilities_integration_enabled?
+          next result unless jira_vulnerabilities_integration_enabled?
 
           result.merge(data: { issuetypes: issue_types })
         end
@@ -103,12 +103,9 @@ def jira_project
       #
       # @return [Array] the array of objects with JIRA Issuetype ID, Name and Description
       def issue_types
-        return [] if jira_project.blank?
+        issuetypes = jira_project.blank? ? client.Issuetype.all : jira_project.issuetypes
 
-        jira_project
-          .issuetypes
-          .reject(&:subtask)
-          .map do |issue_type|
+        issuetypes.reject(&:subtask).map do |issue_type|
           {
             id: issue_type.id,
             name: issue_type.name,
diff --git a/ee/spec/models/ee/integrations/jira_spec.rb b/ee/spec/models/ee/integrations/jira_spec.rb
index e1d908e476a7c..ce35199222306 100644
--- a/ee/spec/models/ee/integrations/jira_spec.rb
+++ b/ee/spec/models/ee/integrations/jira_spec.rb
@@ -2,7 +2,7 @@
 
 require 'spec_helper'
 
-RSpec.describe Integrations::Jira do
+RSpec.describe Integrations::Jira, feature_category: :integrations do
   let(:jira_integration) { build(:jira_integration, **options) }
   let(:headers) { { 'Content-Type' => 'application/json' } }
 
@@ -105,7 +105,7 @@
 
       context 'when vulnerabilities integration is not enabled' do
         before do
-          allow(jira_integration.project).to receive(:jira_vulnerabilities_integration_enabled?).and_return(false)
+          allow(jira_integration).to receive(:jira_vulnerabilities_integration_enabled?).and_return(false)
         end
 
         it { is_expected.to eq(success: true, result: { jira: true }) }
@@ -113,7 +113,7 @@
 
       context 'when vulnerabilities integration is enabled' do
         before do
-          allow(jira_integration.project).to receive(:jira_vulnerabilities_integration_enabled?).and_return(true)
+          allow(jira_integration).to receive(:jira_vulnerabilities_integration_enabled?).and_return(true)
         end
 
         context 'when deployment type is cloud' do
diff --git a/spec/services/integrations/test/group_service_spec.rb b/spec/services/integrations/test/group_service_spec.rb
new file mode 100644
index 0000000000000..abfdf96987d8d
--- /dev/null
+++ b/spec/services/integrations/test/group_service_spec.rb
@@ -0,0 +1,65 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+
+RSpec.describe Integrations::Test::GroupService, feature_category: :integrations do
+  include AfterNextHelpers
+
+  describe '#execute' do
+    let_it_be(:group) { create(:group) }
+    let_it_be(:integration) { create(:integrations_slack, :group, group: group) }
+    let_it_be(:user) { create(:user) }
+
+    let(:event) { nil }
+    let(:sample_data) { { data: 'sample' } }
+    let(:success_result) { { success: true, result: {} } }
+
+    subject(:test_service) { described_class.new(integration, user, event).execute }
+
+    before_all do
+      group.add_owner(user)
+    end
+
+    context 'without event specified' do
+      it 'tests the integration with default data' do
+        allow(Gitlab::DataBuilder::Push).to receive(:sample_data).and_return(sample_data)
+
+        expect(integration).to receive(:test).with(sample_data).and_return(success_result)
+        expect(test_service).to eq(success_result)
+      end
+    end
+
+    context 'with event specified' do
+      context 'if event is not supported by integration' do
+        let_it_be(:integration) { create(:jira_integration, :group, group: group) }
+        let(:event) { 'push' }
+
+        it 'returns error message' do
+          expect(test_service).to include({ status: :error, message: 'Testing not available for this event' })
+        end
+      end
+
+      context 'for `push` event' do
+        let(:event) { 'push' }
+
+        it 'executes integration' do
+          allow(Gitlab::DataBuilder::Push).to receive(:sample_data).and_return(sample_data)
+
+          expect(integration).to receive(:test).with(sample_data).and_return(success_result)
+          expect(test_service).to eq(success_result)
+        end
+      end
+
+      context 'for `tag_push` event' do
+        let(:event) { 'tag_push' }
+
+        it 'executes integration' do
+          allow(Gitlab::DataBuilder::Push).to receive(:sample_data).and_return(sample_data)
+
+          expect(integration).to receive(:test).with(sample_data).and_return(success_result)
+          expect(test_service).to eq(success_result)
+        end
+      end
+    end
+  end
+end
diff --git a/spec/support/shared_examples/controllers/concerns/integrations/integrations_actions_shared_examples.rb b/spec/support/shared_examples/controllers/concerns/integrations/integrations_actions_shared_examples.rb
index 106260e644fc9..fb8eb34651b7f 100644
--- a/spec/support/shared_examples/controllers/concerns/integrations/integrations_actions_shared_examples.rb
+++ b/spec/support/shared_examples/controllers/concerns/integrations/integrations_actions_shared_examples.rb
@@ -77,5 +77,34 @@
     end
 
     it_behaves_like 'unknown integration'
+
+    context 'with untestable integration' do
+      before do
+        allow_next_found_instance_of(integration.class) do |integration|
+          allow(integration).to receive(:testable?).and_return(false)
+        end
+
+        put :test, params: routing_params
+      end
+
+      it 'returns 404 Not Found' do
+        expect(response).to have_gitlab_http_status(:not_found)
+      end
+    end
+
+    context 'with testable integration' do
+      before do
+        allow_next_found_instance_of(integration.class) do |integration|
+          allow(integration).to receive(:testable?).and_return(true)
+          allow(integration).to receive(:test).and_return({ success: true, data: [] })
+        end
+
+        put :test, params: routing_params
+      end
+
+      it 'returns 200' do
+        expect(response).to have_gitlab_http_status(:ok)
+      end
+    end
   end
 end
-- 
GitLab