discourse/spec/components/rate_limiter_spec.rb
Sam Saffron 4ea21fa2d0 DEV: use #frozen_string_literal: true on all spec
This change both speeds up specs (less strings to allocate) and helps catch
cases where methods in Discourse are mutating inputs.

Overall we will be migrating everything to use #frozen_string_literal: true
it will take a while, but this is the first and safest move in this direction
2019-04-30 10:27:42 +10:00

165 lines
4.1 KiB
Ruby

# frozen_string_literal: true
require 'rails_helper'
require 'rate_limiter'
describe RateLimiter do
let(:user) { Fabricate(:user) }
let(:rate_limiter) { RateLimiter.new(user, "peppermint-butler", 2, 60) }
context 'disabled' do
before do
rate_limiter.performed!
rate_limiter.performed!
end
it "should be disabled" do
expect(RateLimiter.disabled?).to eq(true)
end
it "returns true for can_perform?" do
expect(rate_limiter.can_perform?).to eq(true)
end
it "doesn't raise an error on performed!" do
expect { rate_limiter.performed! }.not_to raise_error
end
end
context 'enabled' do
before do
RateLimiter.enable
rate_limiter.clear!
end
after do
RateLimiter.disable
end
context 'global rate limiter' do
it 'can operate in global mode' do
limiter = RateLimiter.new(nil, "test", 2, 30, global: true)
limiter.clear!
thrown = false
limiter.performed!
limiter.performed!
begin
limiter.performed!
rescue RateLimiter::LimitExceeded => e
expect(Integer === e.available_in).to eq(true)
expect(e.available_in).to be > 28
expect(e.available_in).to be < 32
thrown = true
end
expect(thrown).to be(true)
end
end
context 'handles readonly' do
before do
$redis.without_namespace.slaveof '10.0.0.1', '99999'
end
after do
$redis.without_namespace.slaveof 'no', 'one'
end
it 'does not explode' do
expect { rate_limiter.performed! }.not_to raise_error
end
end
context 'never done' do
it "should perform right away" do
expect(rate_limiter.can_perform?).to eq(true)
end
it "performs without an error" do
expect { rate_limiter.performed! }.not_to raise_error
end
end
context "remaining" do
it "updates correctly" do
expect(rate_limiter.remaining).to eq(2)
rate_limiter.performed!
expect(rate_limiter.remaining).to eq(1)
rate_limiter.performed!
expect(rate_limiter.remaining).to eq(0)
end
end
context 'max is less than or equal to zero' do
it 'should raise the right error' do
[-1, 0, nil].each do |max|
expect do
RateLimiter.new(user, "a", max, 60).performed!
end.to raise_error(RateLimiter::LimitExceeded)
end
end
end
context "multiple calls" do
before do
rate_limiter.performed!
rate_limiter.performed!
end
it "returns false for can_perform when the limit has been hit" do
expect(rate_limiter.can_perform?).to eq(false)
expect(rate_limiter.remaining).to eq(0)
end
it "raises an error the third time called" do
expect { rate_limiter.performed! }.to raise_error(RateLimiter::LimitExceeded)
end
context "as an admin/moderator" do
it "returns true for can_perform if the user is an admin" do
user.admin = true
expect(rate_limiter.can_perform?).to eq(true)
expect(rate_limiter.remaining).to eq(2)
end
it "doesn't raise an error when an admin performs the task" do
user.admin = true
expect { rate_limiter.performed! }.not_to raise_error
end
it "returns true for can_perform if the user is a mod" do
user.moderator = true
expect(rate_limiter.can_perform?).to eq(true)
end
it "doesn't raise an error when a moderator performs the task" do
user.moderator = true
expect { rate_limiter.performed! }.not_to raise_error
end
end
context "rollback!" do
before do
rate_limiter.rollback!
end
it "returns true for can_perform since there is now room" do
expect(rate_limiter.can_perform?).to eq(true)
end
it "raises no error now that there is room" do
expect { rate_limiter.performed! }.not_to raise_error
end
end
end
end
end