2022-06-16 05:09:15 -04:00
|
|
|
# frozen_string_literal: true
|
|
|
|
|
|
|
|
require 'spec_helper'
|
|
|
|
|
|
|
|
RSpec.describe Gitlab::WebHooks::RateLimiter, :clean_gitlab_redis_rate_limiting do
|
|
|
|
let_it_be(:plan) { create(:default_plan) }
|
|
|
|
let_it_be_with_reload(:project_hook) { create(:project_hook) }
|
|
|
|
let_it_be_with_reload(:system_hook) { create(:system_hook) }
|
2022-09-08 17:13:09 -04:00
|
|
|
let_it_be_with_reload(:integration_hook) { create(:jenkins_integration).service_hook }
|
2022-06-16 05:09:15 -04:00
|
|
|
let_it_be(:limit) { 1 }
|
|
|
|
|
|
|
|
using RSpec::Parameterized::TableSyntax
|
|
|
|
|
|
|
|
describe '#rate_limit!' do
|
|
|
|
def rate_limit!(hook)
|
|
|
|
described_class.new(hook).rate_limit!
|
|
|
|
end
|
|
|
|
|
|
|
|
shared_examples 'a hook that is never rate limited' do
|
|
|
|
specify do
|
|
|
|
expect(Gitlab::ApplicationRateLimiter).not_to receive(:throttled?)
|
|
|
|
|
|
|
|
expect(rate_limit!(hook)).to eq(false)
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
|
|
|
context 'when there is no plan limit' do
|
|
|
|
where(:hook) { [ref(:project_hook), ref(:system_hook), ref(:integration_hook)] }
|
|
|
|
|
|
|
|
with_them { it_behaves_like 'a hook that is never rate limited' }
|
|
|
|
end
|
|
|
|
|
|
|
|
context 'when there is a plan limit' do
|
|
|
|
before_all do
|
|
|
|
create(:plan_limits, plan: plan, web_hook_calls: limit)
|
|
|
|
end
|
|
|
|
|
|
|
|
where(:hook, :limitless_hook_type) do
|
|
|
|
ref(:project_hook) | false
|
|
|
|
ref(:system_hook) | true
|
|
|
|
ref(:integration_hook) | true
|
|
|
|
end
|
|
|
|
|
|
|
|
with_them do
|
|
|
|
if params[:limitless_hook_type]
|
|
|
|
it_behaves_like 'a hook that is never rate limited'
|
|
|
|
else
|
|
|
|
it 'rate limits the hook, returning true when rate limited' do
|
|
|
|
expect(Gitlab::ApplicationRateLimiter).to receive(:throttled?)
|
|
|
|
.exactly(3).times
|
|
|
|
.and_call_original
|
|
|
|
|
|
|
|
freeze_time do
|
|
|
|
limit.times { expect(rate_limit!(hook)).to eq(false) }
|
|
|
|
expect(rate_limit!(hook)).to eq(true)
|
|
|
|
end
|
|
|
|
|
|
|
|
travel_to(1.day.from_now) do
|
|
|
|
expect(rate_limit!(hook)).to eq(false)
|
|
|
|
end
|
|
|
|
end
|
|
|
|
end
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
|
|
|
describe 'rate limit scope' do
|
|
|
|
it 'rate limits all hooks from the same namespace', :freeze_time do
|
|
|
|
create(:plan_limits, plan: plan, web_hook_calls: limit)
|
|
|
|
project_hook_in_different_namespace = create(:project_hook)
|
|
|
|
project_hook_in_same_namespace = create(:project_hook,
|
|
|
|
project: create(:project, namespace: project_hook.project.namespace)
|
|
|
|
)
|
|
|
|
|
|
|
|
limit.times { expect(rate_limit!(project_hook)).to eq(false) }
|
|
|
|
expect(rate_limit!(project_hook)).to eq(true)
|
|
|
|
expect(rate_limit!(project_hook_in_same_namespace)).to eq(true)
|
|
|
|
expect(rate_limit!(project_hook_in_different_namespace)).to eq(false)
|
|
|
|
end
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
|
|
|
describe '#rate_limited?' do
|
|
|
|
subject { described_class.new(hook).rate_limited? }
|
|
|
|
|
|
|
|
context 'when no plan limit has been defined' do
|
|
|
|
where(:hook) { [ref(:project_hook), ref(:system_hook), ref(:integration_hook)] }
|
|
|
|
|
|
|
|
with_them do
|
|
|
|
it { is_expected.to eq(false) }
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
|
|
|
context 'when there is a plan limit' do
|
|
|
|
before_all do
|
|
|
|
create(:plan_limits, plan: plan, web_hook_calls: limit)
|
|
|
|
end
|
|
|
|
|
|
|
|
context 'when hook is not rate-limited' do
|
|
|
|
where(:hook) { [ref(:project_hook), ref(:system_hook), ref(:integration_hook)] }
|
|
|
|
|
|
|
|
with_them do
|
|
|
|
it { is_expected.to eq(false) }
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
|
|
|
context 'when hook is rate-limited' do
|
|
|
|
before do
|
|
|
|
allow(Gitlab::ApplicationRateLimiter).to receive(:throttled?).and_return(true)
|
|
|
|
end
|
|
|
|
|
|
|
|
where(:hook, :limitless_hook_type) do
|
|
|
|
ref(:project_hook) | false
|
|
|
|
ref(:system_hook) | true
|
|
|
|
ref(:integration_hook) | true
|
|
|
|
end
|
|
|
|
|
|
|
|
with_them do
|
|
|
|
it { is_expected.to eq(!limitless_hook_type) }
|
|
|
|
end
|
|
|
|
end
|
|
|
|
end
|
|
|
|
end
|
|
|
|
end
|