Testing rack_attack gem

Rack::Attack is a rack middleware to protect your web app from bad clients. It allows safelisting, blocklisting, throttling, and tracking based on arbitrary properties of the request.

Although the configuration is quite simple it would be nice to have some tests to ensure the gem is working as expected. This is highlighted as learning tests in the Clean code book:

The learning tests end up costing nothing. We had to learn the API anyway, and writing those tests was an easy and isolated way to get that knowledge. The learning tests were precise experiments that helped increase our understanding.

Not only are learning tests free, they have a positive return on investment. When there are new releases of the third-party package, we run the learning tests to see whether there are behavioral differences.

Testing this could be tricky and time consuming for your CI server. Instead of performing 300 requests you could play a bit with the cache store using by the gem (knowing the internals of what you’re testing is a no-no but in this case I see this as preferable option)

Example test:

require "rails_helper"

describe Rack::Attack, type: :request do
  before(:each) do
    setup_rack_attack_cache_store
    avoid_test_overlaps_in_cache
  end

  def setup_rack_attack_cache_store
    Rack::Attack.cache.store = ActiveSupport::Cache::MemoryStore.new
  end

  def avoid_test_overlaps_in_cache
    Rails.cache.clear
  end

  it "throttle excessive requests by IP address" do
    limit = 300
    period = 300
    ip = "1.2.3.4"
    limit.times do
      Rack::Attack.cache.count("requests/ip:#{ip}", period)
    end

    get "/", headers: { REMOTE_ADDR: ip }

    expect(response).to have_http_status(:too_many_requests)
  end
end

Code being tested:

throttle("requests/ip", limit: 300, period: 5.minutes, &:ip)

Lastly, if you want to test this in your production server you could perform this poor man DoS attack:

curl -I -s "YOUR_URL/?[1-350]" | grep HTTP/

After the 300th request you should see the HTTP response 429 too many requests.

Happy Coding!