require "spec_helper"

describe Rack::API::Middleware::Limit do
  let(:action) { proc {|env| [200, {}, ["success"]] } }
  let(:env) {
    Rack::MockRequest.env_for("/v1",
      "REMOTE_ADDR"        => "127.0.0.1",
      "X-API_KEY"          => "fo7hy7ra",
      "HTTP_AUTHORIZATION" => basic_auth("admin", "test")
    )
  }

  subject { Rack::API::Middleware::Limit.new(action) }

  before do
    Time.stub :now => Time.parse("2011-04-08 00:00:00")
    @stamp = Time.now.strftime("%Y%m%d%H")

    begin
      $redis = Redis.new
      $redis.del "api:127.0.0.1:#{@stamp}"
      $redis.del "api:fo7hy7ra:#{@stamp}"
      $redis.del "api:admin:#{@stamp}"
      $redis.del "api:whitelist"
      subject.options.merge!(:with => $redis)
    rescue Errno::ECONNREFUSED => e
      pending "Redis is not running"
    end
  end

  context "using default options" do
    it "renders action when limit wasn't exceeded" do
      results = 60.times.collect { subject.call(env) }

      $redis.get("api:127.0.0.1:#{@stamp}").to_i.should == 60
      results.last[0].should == 200
    end

    it "renders 503 when limit was exceeded" do
      results = 61.times.collect { subject.call(env) }

      $redis.get("api:127.0.0.1:#{@stamp}").to_i.should == 61
      results.last[0].should == 503
    end
  end

  context "using custom options" do
    it "respects limit" do
      subject.options.merge!(:limit => 20)

      results = 20.times.collect { subject.call(env) }

      $redis.get("api:127.0.0.1:#{@stamp}").to_i.should == 20
      results.last[0].should == 200
    end

    it "uses custom string key" do
      subject.options.merge!(:key => "X-API_KEY")
      status, headers, result = subject.call(env)

      $redis.get("api:fo7hy7ra:#{@stamp}").to_i.should == 1
      status.should == 200
    end

    it "uses custom block key" do
      subject.options.merge! :key => proc {|env|
        request = Rack::Auth::Basic::Request.new(env)
        request.credentials[0]
      }

      status, headers, result = subject.call(env)

      $redis.get("api:admin:#{@stamp}").to_i.should == 1
      status.should == 200
    end
  end

  context "whitelist" do
    it "bypasses API limit" do
      $redis.sadd("api:whitelist", "127.0.0.1")

      subject.options.merge!(:limit => 5)
      results = 10.times.collect { subject.call(env) }

      $redis.get("api:127.0.0.1:#{@stamp}").to_i.should == 10
      results.last[0].should == 200
    end
  end
end
