Speed-up on `Rack::Attack` spec (#25542)

main
Matt Jankowski 2023-10-13 10:14:23 -04:00 committed by GitHub
parent 71cfdd7865
commit 1b195ce115
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
1 changed files with 38 additions and 10 deletions

View File

@ -16,37 +16,63 @@ describe Rack::Attack, type: :request do
# https://github.com/rack/rack-attack/blob/v6.6.1/lib/rack/attack/cache.rb#L64-L66 # https://github.com/rack/rack-attack/blob/v6.6.1/lib/rack/attack/cache.rb#L64-L66
# So we want to minimize `Time.now.to_i % period` # So we want to minimize `Time.now.to_i % period`
travel_to Time.zone.at((Time.now.to_i / period.seconds).to_i * period.seconds) travel_to Time.zone.at(counter_prefix * period.seconds)
end end
context 'when the number of requests is lower than the limit' do context 'when the number of requests is lower than the limit' do
before do
below_limit.times { increment_counter }
end
it 'does not change the request status' do it 'does not change the request status' do
limit.times do expect { request.call }.to change { throttle_count }.by(1)
request.call
expect(response).to_not have_http_status(429) expect(response).to_not have_http_status(429)
end
end end
end end
context 'when the number of requests is higher than the limit' do context 'when the number of requests is higher than the limit' do
before do
above_limit.times { increment_counter }
end
it 'returns http too many requests after limit and returns to normal status after period' do it 'returns http too many requests after limit and returns to normal status after period' do
(limit * 2).times do |i| expect { request.call }.to change { throttle_count }.by(1)
request.call expect(response).to have_http_status(429)
expect(response).to have_http_status(429) if i > limit
end
travel period travel period
request.call expect { request.call }.to change { throttle_count }.by(1)
expect(response).to_not have_http_status(429) expect(response).to_not have_http_status(429)
end end
end end
def below_limit
limit - 1
end
def above_limit
limit * 2
end
def throttle_count
described_class.cache.read("#{counter_prefix}:#{throttle}:#{remote_ip}") || 0
end
def counter_prefix
(Time.now.to_i / period.seconds).to_i
end
def increment_counter
described_class.cache.count("#{throttle}:#{remote_ip}", period)
end
end end
let(:remote_ip) { '1.2.3.5' } let(:remote_ip) { '1.2.3.5' }
describe 'throttle excessive sign-up requests by IP address' do describe 'throttle excessive sign-up requests by IP address' do
context 'when accessed through the website' do context 'when accessed through the website' do
let(:throttle) { 'throttle_sign_up_attempts/ip' }
let(:limit) { 25 } let(:limit) { 25 }
let(:period) { 5.minutes } let(:period) { 5.minutes }
let(:request) { -> { post path, headers: { 'REMOTE_ADDR' => remote_ip } } } let(:request) { -> { post path, headers: { 'REMOTE_ADDR' => remote_ip } } }
@ -65,6 +91,7 @@ describe Rack::Attack, type: :request do
end end
context 'when accessed through the API' do context 'when accessed through the API' do
let(:throttle) { 'throttle_api_sign_up' }
let(:limit) { 5 } let(:limit) { 5 }
let(:period) { 30.minutes } let(:period) { 30.minutes }
let(:request) { -> { post path, headers: { 'REMOTE_ADDR' => remote_ip } } } let(:request) { -> { post path, headers: { 'REMOTE_ADDR' => remote_ip } } }
@ -87,6 +114,7 @@ describe Rack::Attack, type: :request do
end end
describe 'throttle excessive sign-in requests by IP address' do describe 'throttle excessive sign-in requests by IP address' do
let(:throttle) { 'throttle_login_attempts/ip' }
let(:limit) { 25 } let(:limit) { 25 }
let(:period) { 5.minutes } let(:period) { 5.minutes }
let(:request) { -> { post path, headers: { 'REMOTE_ADDR' => remote_ip } } } let(:request) { -> { post path, headers: { 'REMOTE_ADDR' => remote_ip } } }