Commit 1982e005 authored by Peter Leitzen's avatar Peter Leitzen

Merge branch '290116-test-webhook-optimisations' into 'master'

Fix timeout problems on test webhooks

See merge request gitlab-org/gitlab!52646
parents bea68b43 bc7b447c
...@@ -8,22 +8,41 @@ module Integrations ...@@ -8,22 +8,41 @@ module Integrations
Gitlab::DataBuilder::Push.build_sample(project, current_user) Gitlab::DataBuilder::Push.build_sample(project, current_user)
end end
def use_optimal_query?
Feature.enabled?(:integrations_test_webhook_optimizations, project)
end
def note_events_data def note_events_data
note = project.notes.first note = if use_optimal_query?
NotesFinder.new(current_user, project: project, target: project).execute.reorder(nil).last # rubocop: disable CodeReuse/ActiveRecord
else
project.notes.first
end
return { error: s_('TestHooks|Ensure the project has notes.') } unless note.present? return { error: s_('TestHooks|Ensure the project has notes.') } unless note.present?
Gitlab::DataBuilder::Note.build(note, current_user) Gitlab::DataBuilder::Note.build(note, current_user)
end end
def issues_events_data def issues_events_data
issue = project.issues.first issue = if use_optimal_query?
IssuesFinder.new(current_user, project_id: project.id, sort: 'created_desc').execute.first
else
project.issues.first
end
return { error: s_('TestHooks|Ensure the project has issues.') } unless issue.present? return { error: s_('TestHooks|Ensure the project has issues.') } unless issue.present?
issue.to_hook_data(current_user) issue.to_hook_data(current_user)
end end
def merge_requests_events_data def merge_requests_events_data
merge_request = project.merge_requests.first merge_request = if use_optimal_query?
MergeRequestsFinder.new(current_user, project_id: project.id, sort: 'created_desc').execute.first
else
project.merge_requests.first
end
return { error: s_('TestHooks|Ensure the project has merge requests.') } unless merge_request.present? return { error: s_('TestHooks|Ensure the project has merge requests.') } unless merge_request.present?
merge_request.to_hook_data(current_user) merge_request.to_hook_data(current_user)
......
---
name: integrations_test_webhook_optimizations
introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/52646
rollout_issue_url: https://gitlab.com/gitlab-org/gitlab/-/issues/300105
milestone: '13.9'
type: development
group: group::ecosystem
default_enabled: false
...@@ -3,11 +3,10 @@ ...@@ -3,11 +3,10 @@
require 'spec_helper' require 'spec_helper'
RSpec.describe Integrations::Test::ProjectService do RSpec.describe Integrations::Test::ProjectService do
let(:user) { double('user') }
describe '#execute' do describe '#execute' do
let(:project) { create(:project) } let_it_be(:project) { create(:project) }
let(:integration) { create(:slack_service, project: project) } let(:integration) { create(:slack_service, project: project) }
let(:user) { project.owner }
let(:event) { nil } let(:event) { nil }
let(:sample_data) { { data: 'sample' } } let(:sample_data) { { data: 'sample' } }
let(:success_result) { { success: true, result: {} } } let(:success_result) { { success: true, result: {} } }
...@@ -70,16 +69,34 @@ RSpec.describe Integrations::Test::ProjectService do ...@@ -70,16 +69,34 @@ RSpec.describe Integrations::Test::ProjectService do
end end
it 'executes integration' do it 'executes integration' do
allow(project).to receive(:notes).and_return([Note.new]) create(:note, project: project)
allow(Gitlab::DataBuilder::Note).to receive(:build).and_return(sample_data) allow(Gitlab::DataBuilder::Note).to receive(:build).and_return(sample_data)
allow_next_instance_of(NotesFinder) do |finder|
allow(finder).to receive(:execute).and_return(Note.all)
end
expect(integration).to receive(:test).with(sample_data).and_return(success_result) expect(integration).to receive(:test).with(sample_data).and_return(success_result)
expect(subject).to eq(success_result) expect(subject).to eq(success_result)
end end
context 'when the query optimization feature flag is disabled' do
before do
stub_feature_flags(integrations_test_webhook_optimizations: false)
end
it 'executes the old query' do
allow(Gitlab::DataBuilder::Note).to receive(:build).and_return(sample_data)
expect(NotesFinder).not_to receive(:new)
expect(project).to receive(:notes).and_return([Note.new])
expect(integration).to receive(:test).with(sample_data).and_return(success_result)
expect(subject).to eq(success_result)
end
end
end end
context 'issue' do shared_examples_for 'a test of an integration that operates on issues' do
let(:event) { 'issue' }
let(:issue) { build(:issue) } let(:issue) { build(:issue) }
it 'returns error message if not enough data' do it 'returns error message if not enough data' do
...@@ -90,32 +107,45 @@ RSpec.describe Integrations::Test::ProjectService do ...@@ -90,32 +107,45 @@ RSpec.describe Integrations::Test::ProjectService do
it 'executes integration' do it 'executes integration' do
allow(project).to receive(:issues).and_return([issue]) allow(project).to receive(:issues).and_return([issue])
allow(issue).to receive(:to_hook_data).and_return(sample_data) allow(issue).to receive(:to_hook_data).and_return(sample_data)
allow_next_instance_of(IssuesFinder) do |finder|
allow(finder).to receive(:execute).and_return([issue])
end
expect(integration).to receive(:test).with(sample_data).and_return(success_result) expect(integration).to receive(:test).with(sample_data).and_return(success_result)
expect(subject).to eq(success_result) expect(subject).to eq(success_result)
end end
end
context 'confidential_issue' do context 'when the query optimization feature flag is disabled' do
let(:event) { 'confidential_issue' } before do
let(:issue) { build(:issue) } stub_feature_flags(integrations_test_webhook_optimizations: false)
end
it 'returns error message if not enough data' do it 'executes the old query' do
expect(integration).not_to receive(:test) allow(issue).to receive(:to_hook_data).and_return(sample_data)
expect(subject).to include({ status: :error, message: 'Ensure the project has issues.' })
expect(IssuesFinder).not_to receive(:new)
expect(project).to receive(:issues).and_return([issue])
expect(integration).to receive(:test).with(sample_data).and_return(success_result)
expect(subject).to eq(success_result)
end
end end
end
it 'executes integration' do context 'issue' do
allow(project).to receive(:issues).and_return([issue]) let(:event) { 'issue' }
allow(issue).to receive(:to_hook_data).and_return(sample_data)
expect(integration).to receive(:test).with(sample_data).and_return(success_result) it_behaves_like 'a test of an integration that operates on issues'
expect(subject).to eq(success_result) end
end
context 'confidential_issue' do
let(:event) { 'confidential_issue' }
it_behaves_like 'a test of an integration that operates on issues'
end end
context 'merge_request' do context 'merge_request' do
let(:event) { 'merge_request' } let(:event) { 'merge_request' }
let(:merge_request) { build(:merge_request) }
it 'returns error message if not enough data' do it 'returns error message if not enough data' do
expect(integration).not_to receive(:test) expect(integration).not_to receive(:test)
...@@ -123,16 +153,34 @@ RSpec.describe Integrations::Test::ProjectService do ...@@ -123,16 +153,34 @@ RSpec.describe Integrations::Test::ProjectService do
end end
it 'executes integration' do it 'executes integration' do
create(:merge_request, source_project: project) allow(merge_request).to receive(:to_hook_data).and_return(sample_data)
allow_any_instance_of(MergeRequest).to receive(:to_hook_data).and_return(sample_data) allow_next_instance_of(MergeRequestsFinder) do |finder|
allow(finder).to receive(:execute).and_return([merge_request])
end
expect(integration).to receive(:test).with(sample_data).and_return(success_result) expect(integration).to receive(:test).with(sample_data).and_return(success_result)
expect(subject).to eq(success_result) expect(subject).to include(success_result)
end
context 'when the query optimization feature flag is disabled' do
before do
stub_feature_flags(integrations_test_webhook_optimizations: false)
end
it 'executes the old query' do
expect(MergeRequestsFinder).not_to receive(:new)
expect(project).to receive(:merge_requests).and_return([merge_request])
allow(merge_request).to receive(:to_hook_data).and_return(sample_data)
expect(integration).to receive(:test).with(sample_data).and_return(success_result)
expect(subject).to eq(success_result)
end
end end
end end
context 'deployment' do context 'deployment' do
let(:project) { create(:project, :test_repo) } let_it_be(:project) { create(:project, :test_repo) }
let(:event) { 'deployment' } let(:event) { 'deployment' }
it 'returns error message if not enough data' do it 'returns error message if not enough data' do
...@@ -167,7 +215,7 @@ RSpec.describe Integrations::Test::ProjectService do ...@@ -167,7 +215,7 @@ RSpec.describe Integrations::Test::ProjectService do
end end
context 'wiki_page' do context 'wiki_page' do
let(:project) { create(:project, :wiki_repo) } let_it_be(:project) { create(:project, :wiki_repo) }
let(:event) { 'wiki_page' } let(:event) { 'wiki_page' }
it 'returns error message if wiki disabled' do it 'returns error message if wiki disabled' do
......
...@@ -6,8 +6,8 @@ RSpec.describe TestHooks::ProjectService do ...@@ -6,8 +6,8 @@ RSpec.describe TestHooks::ProjectService do
let(:current_user) { create(:user) } let(:current_user) { create(:user) }
describe '#execute' do describe '#execute' do
let(:project) { create(:project, :repository) } let_it_be(:project) { create(:project, :repository) }
let(:hook) { create(:project_hook, project: project) } let(:hook) { create(:project_hook, project: project) }
let(:trigger) { 'not_implemented_events' } let(:trigger) { 'not_implemented_events' }
let(:service) { described_class.new(hook, current_user, trigger) } let(:service) { described_class.new(hook, current_user, trigger) }
let(:sample_data) { { data: 'sample' } } let(:sample_data) { { data: 'sample' } }
...@@ -61,17 +61,34 @@ RSpec.describe TestHooks::ProjectService do ...@@ -61,17 +61,34 @@ RSpec.describe TestHooks::ProjectService do
end end
it 'executes hook' do it 'executes hook' do
allow(project).to receive(:notes).and_return([Note.new]) create(:note, project: project)
allow(Gitlab::DataBuilder::Note).to receive(:build).and_return(sample_data) allow(Gitlab::DataBuilder::Note).to receive(:build).and_return(sample_data)
allow_next_instance_of(NotesFinder) do |finder|
allow(finder).to receive(:execute).and_return(Note.all)
end
expect(hook).to receive(:execute).with(sample_data, trigger_key).and_return(success_result) expect(hook).to receive(:execute).with(sample_data, trigger_key).and_return(success_result)
expect(service.execute).to include(success_result) expect(service.execute).to include(success_result)
end end
context 'when the query optimization feature flag is disabled' do
before do
stub_feature_flags(integrations_test_webhook_optimizations: false)
end
it 'executes the old query' do
allow(Gitlab::DataBuilder::Note).to receive(:build).and_return(sample_data)
expect(NotesFinder).not_to receive(:new)
expect(project).to receive(:notes).and_return([Note.new])
expect(hook).to receive(:execute).with(sample_data, trigger_key).and_return(success_result)
expect(service.execute).to include(success_result)
end
end
end end
context 'issues_events' do shared_examples_for 'a test webhook that operates on issues' do
let(:trigger) { 'issues_events' }
let(:trigger_key) { :issue_hooks }
let(:issue) { build(:issue) } let(:issue) { build(:issue) }
it 'returns error message if not enough data' do it 'returns error message if not enough data' do
...@@ -80,36 +97,49 @@ RSpec.describe TestHooks::ProjectService do ...@@ -80,36 +97,49 @@ RSpec.describe TestHooks::ProjectService do
end end
it 'executes hook' do it 'executes hook' do
allow(project).to receive(:issues).and_return([issue])
allow(issue).to receive(:to_hook_data).and_return(sample_data) allow(issue).to receive(:to_hook_data).and_return(sample_data)
allow_next_instance_of(IssuesFinder) do |finder|
allow(finder).to receive(:execute).and_return([issue])
end
expect(hook).to receive(:execute).with(sample_data, trigger_key).and_return(success_result) expect(hook).to receive(:execute).with(sample_data, trigger_key).and_return(success_result)
expect(service.execute).to include(success_result) expect(service.execute).to include(success_result)
end end
context 'when the query optimization feature flag is disabled' do
before do
stub_feature_flags(integrations_test_webhook_optimizations: false)
end
it 'executes the old query' do
allow(issue).to receive(:to_hook_data).and_return(sample_data)
expect(IssuesFinder).not_to receive(:new)
expect(project).to receive(:issues).and_return([issue])
expect(hook).to receive(:execute).with(sample_data, trigger_key).and_return(success_result)
expect(service.execute).to include(success_result)
end
end
end
context 'issues_events' do
let(:trigger) { 'issues_events' }
let(:trigger_key) { :issue_hooks }
it_behaves_like 'a test webhook that operates on issues'
end end
context 'confidential_issues_events' do context 'confidential_issues_events' do
let(:trigger) { 'confidential_issues_events' } let(:trigger) { 'confidential_issues_events' }
let(:trigger_key) { :confidential_issue_hooks } let(:trigger_key) { :confidential_issue_hooks }
let(:issue) { build(:issue) }
it 'returns error message if not enough data' do it_behaves_like 'a test webhook that operates on issues'
expect(hook).not_to receive(:execute)
expect(service.execute).to include({ status: :error, message: 'Ensure the project has issues.' })
end
it 'executes hook' do
allow(project).to receive(:issues).and_return([issue])
allow(issue).to receive(:to_hook_data).and_return(sample_data)
expect(hook).to receive(:execute).with(sample_data, trigger_key).and_return(success_result)
expect(service.execute).to include(success_result)
end
end end
context 'merge_requests_events' do context 'merge_requests_events' do
let(:trigger) { 'merge_requests_events' } let(:trigger) { 'merge_requests_events' }
let(:trigger_key) { :merge_request_hooks } let(:trigger_key) { :merge_request_hooks }
let(:merge_request) { build(:merge_request) }
it 'returns error message if not enough data' do it 'returns error message if not enough data' do
expect(hook).not_to receive(:execute) expect(hook).not_to receive(:execute)
...@@ -117,12 +147,29 @@ RSpec.describe TestHooks::ProjectService do ...@@ -117,12 +147,29 @@ RSpec.describe TestHooks::ProjectService do
end end
it 'executes hook' do it 'executes hook' do
create(:merge_request, source_project: project) allow(merge_request).to receive(:to_hook_data).and_return(sample_data)
allow_any_instance_of(MergeRequest).to receive(:to_hook_data).and_return(sample_data) allow_next_instance_of(MergeRequestsFinder) do |finder|
allow(finder).to receive(:execute).and_return([merge_request])
end
expect(hook).to receive(:execute).with(sample_data, trigger_key).and_return(success_result) expect(hook).to receive(:execute).with(sample_data, trigger_key).and_return(success_result)
expect(service.execute).to include(success_result) expect(service.execute).to include(success_result)
end end
context 'when the query optimization feature flag is disabled' do
before do
stub_feature_flags(integrations_test_webhook_optimizations: false)
end
it 'executes the old query' do
allow(merge_request).to receive(:to_hook_data).and_return(sample_data)
expect(MergeRequestsFinder).not_to receive(:new)
expect(project).to receive(:merge_requests).and_return([merge_request])
expect(hook).to receive(:execute).with(sample_data, trigger_key).and_return(success_result)
expect(service.execute).to include(success_result)
end
end
end end
context 'job_events' do context 'job_events' do
...@@ -162,7 +209,7 @@ RSpec.describe TestHooks::ProjectService do ...@@ -162,7 +209,7 @@ RSpec.describe TestHooks::ProjectService do
end end
context 'wiki_page_events' do context 'wiki_page_events' do
let(:project) { create(:project, :wiki_repo) } let_it_be(:project) { create(:project, :wiki_repo) }
let(:trigger) { 'wiki_page_events' } let(:trigger) { 'wiki_page_events' }
let(:trigger_key) { :wiki_page_hooks } let(:trigger_key) { :wiki_page_hooks }
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment