Testing the Protocol, Subdomain, and Host of a Rack App with Rack::Test
I’ve used Rack::Test to test Rails and other rack-based frameworks for a long time, but the first time I needed to test the protocol (“http” or “https”) of an app I was stumped.
I wanted to write a test that ensured that non-HTTPS requests were rejected in production, but the request methods in Rack::Test take a path (e.g. get "/blog"
) and the protocol/host always defaults to "http://example.org"
.
The code being tested looked like this.
class API < Grape::API
helpers do
def reject_http!
error!("The API is only accessible over HTTPS.", 403) if http_request?
end
def http_request?
request.scheme == "http"
end
def production?
ENV["RACK_ENV"] == "production"
end
end
before do
reject_http! if production?
end
get "/" do
...
end
end
After doing some spelunking in the relevant parts of Rack::Test I discovered that although it doesn’t seem to be explicitly documented anywhere and I’d never seen it done before, Rack::Test actually supports making requests to full URLs, which means you can use it to test the protocol, subdomain, and host of a request!
describe API do
before do
ENV["RACK_ENV"] = "production"
end
after do
ENV["RACK_ENV"] = "test"
end
it "allows HTTPS requests in production" do
get "https://example.dev"
last_response.status.must_equal 200
end
it "does not allow HTTP requests in production" do
get "http://example.dev"
last_response.status.must_equal 403
error = JSON.parse(last_response.body)["error"]
error.must_equal "The API is only accessible over HTTPS."
end
end