require 'spec_helper' require 'stringio' describe Rack::Contrib::StaleCall do let (:response) { [200, {}, ['foo', 'bar']]} let (:log_string) { StringIO.new } let (:logger) do l = Logger.new(log_string, Logger::DEBUG) l.formatter = proc do |severity, datetime, progname, msg| "#{severity} - #{msg}\n" end l end describe "#initialize" do it "accepts an app" do expect { ware = Rack::Contrib::StaleCall.new nil }.not_to raise_error end it "requires an app" do expect { ware = Rack::Contrib::StaleCall.new }.to raise_error end it "accepts a hash of options" do expect { ware = Rack::Contrib::StaleCall.new(nil, {}) }.not_to raise_error end end describe "#opts" do it "returns all default options" do ware = Rack::Contrib::StaleCall.new nil opts = ware.opts opts[:logger].should be_an_instance_of Logger opts[:grace].should eq(5) opts[:header].should eq('Date') end it "accounts for options passed in the initialize" do ware = Rack::Contrib::StaleCall.new nil, :grace => 6, :header => "foo", :logger => logger opts = ware.opts opts.should eq({ :header => "foo", :grace => 6, :logger => logger }) end end describe "#header_variable" do it "converts a header name to an environment variable" do ware = Rack::Contrib::StaleCall.new nil, :header => 'FoO-Ba-r-Date' variable_name = ware.header_variable variable_name.should eq('HTTP_FOO_BA_R_DATE') end end describe "#date_error" do it "returns a 401 with no headers and body" do ware = Rack::Contrib::StaleCall.new nil, :header => 'FoO-Ba-r-Date' code, headers, body = ware.date_error code.should eq(401) headers.should eq({}) body.should eq([]) end end describe "#date_to_diff_seconds" do context "converts an HTTPDate to the differing number of seconds" do it "handles dates in the future" do ware = Rack::Contrib::StaleCall.new nil time = (Time.now + 301).httpdate diff = ware.date_to_diff_seconds time diff.should eq(300) end it "absolute values the dates in the past" do ware = Rack::Contrib::StaleCall.new nil time = (Time.now - 300).httpdate diff = ware.date_to_diff_seconds time diff.should eq(300) end end end describe "#date_is_recent" do it "is recent with a time less than grace" do ware = Rack::Contrib::StaleCall.new nil, :grace => 500 is_recent = ware.date_is_recent 400 is_recent.should eq(true) end it "is recent with a time equal to grace" do ware = Rack::Contrib::StaleCall.new nil, :grace => 500 is_recent = ware.date_is_recent 500 is_recent.should eq(true) end it "is recent with a time greater than grace" do ware = Rack::Contrib::StaleCall.new nil, :grace => 1 is_recent = ware.date_is_recent 2 is_recent.should eq(false) end end describe "#call" do it "requires an env" do ware = Rack::Contrib::StaleCall.new nil expect { ware.call }.to raise_error end context "there is an invalid date header" do context "the date is missing" do it "returns a 401" do ware = Rack::Contrib::StaleCall.new nil env = {} code, headers, body = ware.call env code.should eq(401) end it "logs the failure" do ware = Rack::Contrib::StaleCall.new nil, :logger => logger, :header => 'My-Date' env = {} ware.call env log_string.string.should eq("INFO - Denied: My-Date header missing.\n") end end context "the date is older than the grace" do it "returns a 401" do ware = Rack::Contrib::StaleCall.new nil env = {'HTTP_DATE' => (Time.now - 10).httpdate} ware.stub(:date_is_recent => false) code, headers, body = ware.call env code.should eq(401) end it "logs the failure" do ware = Rack::Contrib::StaleCall.new nil, :header => 'My-Date', :logger => logger env = {'HTTP_MY_DATE' => (Time.now - 10).httpdate} ware.stub(:date_is_recent => false) ware.call env expected_message = "INFO - Denied: My-Date header is 5s over the " + "5s grace period.\n" log_string.string.should eq(expected_message) end end end context "there is a valid date header" do it "calls the app's .call method" do app = double(nil, call: response) env = {'foo' => 'bar', 'HTTP_DATE' => Time.now.httpdate} ware = Rack::Contrib::StaleCall.new app ware.call env app.should have_received(:call).once.with(env) end it "returns the apps' return" do app = double(nil, call: response) env = {'foo' => 'bar', 'HTTP_DATE' => Time.now.httpdate} ware = Rack::Contrib::StaleCall.new app ret = ware.call env ret.should eq(response) end end end end