2021-11-18 22:05:49 +05:30
# frozen_string_literal: true
require 'spec_helper'
2023-05-27 22:25:52 +05:30
RSpec . describe RateLimitedService , feature_category : :rate_limiting do
2021-11-18 22:05:49 +05:30
let ( :key ) { :issues_create }
2023-04-23 21:23:45 +05:30
let ( :scope ) { [ :container , :current_user ] }
2021-11-18 22:05:49 +05:30
let ( :opts ) { { scope : scope , users_allowlist : - > { [ User . support_bot . username ] } } }
2022-04-04 11:22:00 +05:30
let ( :rate_limiter ) { :: Gitlab :: ApplicationRateLimiter }
2021-11-18 22:05:49 +05:30
describe 'RateLimitedError' do
2022-04-04 11:22:00 +05:30
subject { described_class :: RateLimitedError . new ( key : key , rate_limiter : rate_limiter ) }
2021-11-18 22:05:49 +05:30
describe '#headers' do
it 'returns a Hash of HTTP headers' do
# TODO: This will be fleshed out in https://gitlab.com/gitlab-org/gitlab/-/issues/342370
expected_headers = { }
expect ( subject . headers ) . to eq ( expected_headers )
end
end
describe '#log_request' do
it 'logs the request' do
request = instance_double ( Grape :: Request )
user = instance_double ( User )
2022-04-04 11:22:00 +05:30
expect ( rate_limiter ) . to receive ( :log_request ) . with ( request , " #{ key } _request_limit " . to_sym , user )
2021-11-18 22:05:49 +05:30
subject . log_request ( request , user )
end
end
end
describe 'RateLimiterScopedAndKeyed' do
2022-04-04 11:22:00 +05:30
subject { described_class :: RateLimiterScopedAndKeyed . new ( key : key , opts : opts , rate_limiter : rate_limiter ) }
2021-11-18 22:05:49 +05:30
describe '#rate_limit!' do
2022-05-07 20:08:51 +05:30
let_it_be ( :project ) { create ( :project ) }
let_it_be ( :current_user ) { create ( :user ) }
2021-11-18 22:05:49 +05:30
2023-04-23 21:23:45 +05:30
let ( :service ) { instance_double ( Issues :: CreateService , container : project , current_user : current_user ) }
2021-11-18 22:05:49 +05:30
let ( :evaluated_scope ) { [ project , current_user ] }
let ( :evaluated_opts ) { { scope : evaluated_scope , users_allowlist : %w[ support-bot ] } }
2022-05-07 20:08:51 +05:30
context 'when rate limiting is not in effect' do
let ( :throttled ) { false }
2021-11-18 22:05:49 +05:30
2022-05-07 20:08:51 +05:30
it 'does not raise an exception' do
2021-11-18 22:05:49 +05:30
expect ( subject . rate_limit! ( service ) ) . to be_nil
end
end
2022-05-07 20:08:51 +05:30
context 'when rate limiting is in effect' do
2021-11-18 22:05:49 +05:30
before do
2022-05-07 20:08:51 +05:30
allow ( rate_limiter ) . to receive ( :throttled? ) . and_return ( true )
2021-11-18 22:05:49 +05:30
end
2022-05-07 20:08:51 +05:30
it 'raises a RateLimitedError exception' do
expect { subject . rate_limit! ( service ) } . to raise_error ( described_class :: RateLimitedError , 'This endpoint has been requested too many times. Try again later.' )
2021-11-18 22:05:49 +05:30
end
end
end
end
describe '#execute_without_rate_limiting' do
let ( :rate_limiter_scoped_and_keyed ) { instance_double ( RateLimitedService :: RateLimiterScopedAndKeyed ) }
let ( :subject ) do
local_key = key
local_opts = opts
Class . new do
prepend RateLimitedService
rate_limit key : local_key , opts : local_opts
def execute ( * args , ** kwargs )
'main logic here'
end
end . new
end
before do
2022-04-04 11:22:00 +05:30
allow ( RateLimitedService :: RateLimiterScopedAndKeyed ) . to receive ( :new ) . with ( key : key , opts : opts , rate_limiter : rate_limiter ) . and_return ( rate_limiter_scoped_and_keyed )
2021-11-18 22:05:49 +05:30
end
context 'bypasses rate limiting' do
it 'calls super' do
expect ( rate_limiter_scoped_and_keyed ) . not_to receive ( :rate_limit! ) . with ( subject )
expect ( subject . execute_without_rate_limiting ) . to eq ( 'main logic here' )
end
end
end
describe '#execute' do
context 'when rate_limit has not been called' do
let ( :subject ) { Class . new { prepend RateLimitedService } . new }
it 'raises an RateLimitedNotSetupError exception' do
expect { subject . execute } . to raise_error ( described_class :: RateLimitedNotSetupError )
end
end
context 'when rate_limit has been called' do
let ( :rate_limiter_scoped_and_keyed ) { instance_double ( RateLimitedService :: RateLimiterScopedAndKeyed ) }
let ( :subject ) do
local_key = key
local_opts = opts
Class . new do
prepend RateLimitedService
rate_limit key : local_key , opts : local_opts
def execute ( * args , ** kwargs )
'main logic here'
end
end . new
end
before do
2022-04-04 11:22:00 +05:30
allow ( RateLimitedService :: RateLimiterScopedAndKeyed ) . to receive ( :new ) . with ( key : key , opts : opts , rate_limiter : rate_limiter ) . and_return ( rate_limiter_scoped_and_keyed )
2021-11-18 22:05:49 +05:30
end
context 'and applies rate limiting' do
it 'raises an RateLimitedService::RateLimitedError exception' do
2022-04-04 11:22:00 +05:30
expect ( rate_limiter_scoped_and_keyed ) . to receive ( :rate_limit! ) . with ( subject ) . and_raise ( RateLimitedService :: RateLimitedError . new ( key : key , rate_limiter : rate_limiter ) )
2021-11-18 22:05:49 +05:30
expect { subject . execute } . to raise_error ( RateLimitedService :: RateLimitedError )
end
end
context 'but does not apply rate limiting' do
it 'calls super' do
expect ( rate_limiter_scoped_and_keyed ) . to receive ( :rate_limit! ) . with ( subject ) . and_return ( nil )
expect ( subject . execute ) . to eq ( 'main logic here' )
end
end
end
end
end