Welcome to mirror list, hosted at ThFree Co, Russian Federation.

gitlab.com/gitlab-org/gitlab-foss.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
Diffstat (limited to 'spec/models/hooks')
-rw-r--r--spec/models/hooks/project_hook_spec.rb9
-rw-r--r--spec/models/hooks/service_hook_spec.rb12
-rw-r--r--spec/models/hooks/system_hook_spec.rb8
-rw-r--r--spec/models/hooks/web_hook_log_archived_spec.rb52
-rw-r--r--spec/models/hooks/web_hook_spec.rb198
5 files changed, 271 insertions, 8 deletions
diff --git a/spec/models/hooks/project_hook_spec.rb b/spec/models/hooks/project_hook_spec.rb
index 69fbc4c3b4f..88149465232 100644
--- a/spec/models/hooks/project_hook_spec.rb
+++ b/spec/models/hooks/project_hook_spec.rb
@@ -30,4 +30,13 @@ RSpec.describe ProjectHook do
expect(described_class.tag_push_hooks).to eq([hook])
end
end
+
+ describe '#rate_limit' do
+ let_it_be(:hook) { create(:project_hook) }
+ let_it_be(:plan_limits) { create(:plan_limits, :default_plan, web_hook_calls: 100) }
+
+ it 'returns the default limit' do
+ expect(hook.rate_limit).to be(100)
+ end
+ end
end
diff --git a/spec/models/hooks/service_hook_spec.rb b/spec/models/hooks/service_hook_spec.rb
index f7045d7ac5e..651716c3280 100644
--- a/spec/models/hooks/service_hook_spec.rb
+++ b/spec/models/hooks/service_hook_spec.rb
@@ -4,11 +4,11 @@ require 'spec_helper'
RSpec.describe ServiceHook do
describe 'associations' do
- it { is_expected.to belong_to :service }
+ it { is_expected.to belong_to :integration }
end
describe 'validations' do
- it { is_expected.to validate_presence_of(:service) }
+ it { is_expected.to validate_presence_of(:integration) }
end
describe 'execute' do
@@ -22,4 +22,12 @@ RSpec.describe ServiceHook do
hook.execute(data)
end
end
+
+ describe '#rate_limit' do
+ let(:hook) { build(:service_hook) }
+
+ it 'returns nil' do
+ expect(hook.rate_limit).to be_nil
+ end
+ end
end
diff --git a/spec/models/hooks/system_hook_spec.rb b/spec/models/hooks/system_hook_spec.rb
index 02e630cbf27..a72034f1ac5 100644
--- a/spec/models/hooks/system_hook_spec.rb
+++ b/spec/models/hooks/system_hook_spec.rb
@@ -169,4 +169,12 @@ RSpec.describe SystemHook do
hook.async_execute(data, hook_name)
end
end
+
+ describe '#rate_limit' do
+ let(:hook) { build(:system_hook) }
+
+ it 'returns nil' do
+ expect(hook.rate_limit).to be_nil
+ end
+ end
end
diff --git a/spec/models/hooks/web_hook_log_archived_spec.rb b/spec/models/hooks/web_hook_log_archived_spec.rb
new file mode 100644
index 00000000000..ac726dbaf4f
--- /dev/null
+++ b/spec/models/hooks/web_hook_log_archived_spec.rb
@@ -0,0 +1,52 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+
+RSpec.describe WebHookLogArchived do
+ let(:source_table) { WebHookLog }
+ let(:destination_table) { described_class }
+
+ it 'has the same columns as the source table' do
+ column_names_from_source_table = column_names(source_table)
+ column_names_from_destination_table = column_names(destination_table)
+
+ expect(column_names_from_destination_table).to match_array(column_names_from_source_table)
+ end
+
+ it 'has the same null constraints as the source table' do
+ constraints_from_source_table = null_constraints(source_table)
+ constraints_from_destination_table = null_constraints(destination_table)
+
+ expect(constraints_from_destination_table.to_a).to match_array(constraints_from_source_table.to_a)
+ end
+
+ it 'inserts the same record as the one in the source table', :aggregate_failures do
+ expect { create(:web_hook_log) }.to change { destination_table.count }.by(1)
+
+ event_from_source_table = source_table.connection.select_one(
+ "SELECT * FROM #{source_table.table_name} ORDER BY created_at desc LIMIT 1"
+ )
+ event_from_destination_table = destination_table.connection.select_one(
+ "SELECT * FROM #{destination_table.table_name} ORDER BY created_at desc LIMIT 1"
+ )
+
+ expect(event_from_destination_table).to eq(event_from_source_table)
+ end
+
+ def column_names(table)
+ table.connection.select_all(<<~SQL)
+ SELECT c.column_name
+ FROM information_schema.columns c
+ WHERE c.table_name = '#{table.table_name}'
+ SQL
+ end
+
+ def null_constraints(table)
+ table.connection.select_all(<<~SQL)
+ SELECT c.column_name, c.is_nullable
+ FROM information_schema.columns c
+ WHERE c.table_name = '#{table.table_name}'
+ AND c.column_name != 'created_at'
+ SQL
+ end
+end
diff --git a/spec/models/hooks/web_hook_spec.rb b/spec/models/hooks/web_hook_spec.rb
index 413e69fb071..b528dbedd2c 100644
--- a/spec/models/hooks/web_hook_spec.rb
+++ b/spec/models/hooks/web_hook_spec.rb
@@ -3,7 +3,15 @@
require 'spec_helper'
RSpec.describe WebHook do
- let(:hook) { build(:project_hook) }
+ include AfterNextHelpers
+
+ let_it_be(:project) { create(:project) }
+
+ let(:hook) { build(:project_hook, project: project) }
+
+ around do |example|
+ freeze_time { example.run }
+ end
describe 'associations' do
it { is_expected.to have_many(:web_hook_logs) }
@@ -69,18 +77,30 @@ RSpec.describe WebHook do
let(:data) { { key: 'value' } }
let(:hook_name) { 'project hook' }
- before do
- expect(WebHookService).to receive(:new).with(hook, data, hook_name).and_call_original
+ it '#execute' do
+ expect_next(WebHookService).to receive(:execute)
+
+ hook.execute(data, hook_name)
end
- it '#execute' do
- expect_any_instance_of(WebHookService).to receive(:execute)
+ it 'does not execute non-executable hooks' do
+ hook.update!(disabled_until: 1.day.from_now)
+
+ expect(WebHookService).not_to receive(:new)
hook.execute(data, hook_name)
end
it '#async_execute' do
- expect_any_instance_of(WebHookService).to receive(:async_execute)
+ expect_next(WebHookService).to receive(:async_execute)
+
+ hook.async_execute(data, hook_name)
+ end
+
+ it 'does not async execute non-executable hooks' do
+ hook.update!(disabled_until: 1.day.from_now)
+
+ expect(WebHookService).not_to receive(:new)
hook.async_execute(data, hook_name)
end
@@ -94,4 +114,170 @@ RSpec.describe WebHook do
expect { web_hook.destroy! }.to change(web_hook.web_hook_logs, :count).by(-3)
end
end
+
+ describe '.executable' do
+ let(:not_executable) do
+ [
+ [0, Time.current],
+ [0, 1.minute.from_now],
+ [1, 1.minute.from_now],
+ [3, 1.minute.from_now],
+ [4, nil],
+ [4, 1.day.ago],
+ [4, 1.minute.from_now]
+ ].map do |(recent_failures, disabled_until)|
+ create(:project_hook, project: project, recent_failures: recent_failures, disabled_until: disabled_until)
+ end
+ end
+
+ let(:executables) do
+ [
+ [0, nil],
+ [0, 1.day.ago],
+ [1, nil],
+ [1, 1.day.ago],
+ [3, nil],
+ [3, 1.day.ago]
+ ].map do |(recent_failures, disabled_until)|
+ create(:project_hook, project: project, recent_failures: recent_failures, disabled_until: disabled_until)
+ end
+ end
+
+ it 'finds the correct set of project hooks' do
+ expect(described_class.where(project_id: project.id).executable).to match_array executables
+ end
+
+ context 'when the feature flag is not enabled' do
+ before do
+ stub_feature_flags(web_hooks_disable_failed: false)
+ end
+
+ it 'is the same as all' do
+ expect(described_class.where(project_id: project.id).executable).to match_array(executables + not_executable)
+ end
+ end
+ end
+
+ describe '#executable?' do
+ let(:web_hook) { create(:project_hook, project: project) }
+
+ where(:recent_failures, :not_until, :executable) do
+ [
+ [0, :not_set, true],
+ [0, :past, true],
+ [0, :future, false],
+ [0, :now, false],
+ [1, :not_set, true],
+ [1, :past, true],
+ [1, :future, false],
+ [3, :not_set, true],
+ [3, :past, true],
+ [3, :future, false],
+ [4, :not_set, false],
+ [4, :past, false],
+ [4, :future, false]
+ ]
+ end
+
+ with_them do
+ # Phasing means we cannot put these values in the where block,
+ # which is not subject to the frozen time context.
+ let(:disabled_until) do
+ case not_until
+ when :not_set
+ nil
+ when :past
+ 1.minute.ago
+ when :future
+ 1.minute.from_now
+ when :now
+ Time.current
+ end
+ end
+
+ before do
+ web_hook.update!(recent_failures: recent_failures, disabled_until: disabled_until)
+ end
+
+ it 'has the correct state' do
+ expect(web_hook.executable?).to eq(executable)
+ end
+
+ context 'when the feature flag is enabled for a project' do
+ before do
+ stub_feature_flags(web_hooks_disable_failed: project)
+ end
+
+ it 'has the expected value' do
+ expect(web_hook.executable?).to eq(executable)
+ end
+ end
+
+ context 'when the feature flag is not enabled' do
+ before do
+ stub_feature_flags(web_hooks_disable_failed: false)
+ end
+
+ it 'is executable' do
+ expect(web_hook).to be_executable
+ end
+ end
+ end
+ end
+
+ describe '#next_backoff' do
+ context 'when there was no last backoff' do
+ before do
+ hook.backoff_count = 0
+ end
+
+ it 'is 10 minutes' do
+ expect(hook.next_backoff).to eq(described_class::INITIAL_BACKOFF)
+ end
+ end
+
+ context 'when we have backed off once' do
+ before do
+ hook.backoff_count = 1
+ end
+
+ it 'is twice the initial value' do
+ expect(hook.next_backoff).to eq(20.minutes)
+ end
+ end
+
+ context 'when we have backed off 3 times' do
+ before do
+ hook.backoff_count = 3
+ end
+
+ it 'grows exponentially' do
+ expect(hook.next_backoff).to eq(80.minutes)
+ end
+ end
+
+ context 'when the previous backoff was large' do
+ before do
+ hook.backoff_count = 8 # last value before MAX_BACKOFF
+ end
+
+ it 'does not exceed the max backoff value' do
+ expect(hook.next_backoff).to eq(described_class::MAX_BACKOFF)
+ end
+ end
+ end
+
+ describe '#enable!' do
+ it 'makes a hook executable' do
+ hook.recent_failures = 1000
+
+ expect { hook.enable! }.to change(hook, :executable?).from(false).to(true)
+ end
+ end
+
+ describe '#disable!' do
+ it 'disables a hook' do
+ expect { hook.disable! }.to change(hook, :executable?).from(true).to(false)
+ end
+ end
end