require "test/setup"


# Tests the Server API
class ServerTest < Test::Unit::TestCase
  def setup
    super
  end

  context "configuration" do
    should "set oauth.database" do
      assert_equal DATABASE, Server.database
    end

    should "set oauth.host" do
      assert_equal "example.org", Server.options.host
    end

    should "set oauth.collection_prefix" do
      assert_equal "oauth2_prefix", Server.options.collection_prefix
    end
  end

  context "get_auth_request" do
    setup { @request = Server::AuthRequest.create(client, client.scope.join(" "), client.redirect_uri, "token", nil) }
    should "return authorization request" do
      assert_equal @request.id, Server.get_auth_request(@request.id).id
    end

    should "return nil if no request found" do
      assert !Server.get_auth_request("4ce2488e3321e87ac1000004")
    end
  end


  context "get_client" do
    should "return authorization request" do
      assert_equal client.display_name, Server.get_client(client.id).display_name
    end

    should "return nil if no client found" do
      assert !Server.get_client("4ce2488e3321e87ac1000004")
    end
  end


  context "register" do
    context "no client ID" do
      setup do
        @client = Server.register(:display_name=>"MyApp", :link=>"http://example.org", :image_url=>"http://example.org/favicon.ico",
                                  :redirect_uri=>"http://example.org/oauth/callback", :scope=>%w{read write})
      end

      should "create new client" do
        assert_equal 2, Server::Client.collection.count
        assert_contains Server::Client.all.map(&:id), @client.id
      end

      should "set display name" do
        assert_equal "MyApp", Server.get_client(@client.id).display_name
      end

      should "set link" do
        assert_equal "http://example.org", Server.get_client(@client.id).link
      end

      should "set image URL" do
        assert_equal "http://example.org/favicon.ico", Server.get_client(@client.id).image_url
      end

      should "set redirect URI" do
        assert_equal "http://example.org/oauth/callback", Server.get_client(@client.id).redirect_uri
      end

      should "set scope" do
        assert_equal %w{read write}, Server.get_client(@client.id).scope
      end

      should "assign client an ID" do
        assert_match /[0-9a-f]{24}/, @client.id.to_s
      end

      should "assign client a secret" do
        assert_match /[0-9a-f]{64}/, @client.secret
      end
    end

    context "with client ID" do

      context "no such client" do
        setup do
          @client = Server.register(:id=>"4ce24c423321e88ac5000015", :secret=>"foobar", :display_name=>"MyApp")
        end

        should "create new client" do
          assert_equal 2, Server::Client.collection.count
        end

        should "should assign it the client identifier" do
          assert_equal "4ce24c423321e88ac5000015", @client.id.to_s
        end

        should "should assign it the client secret" do
          assert_equal "foobar", @client.secret
        end

        should "should assign it the other properties" do
          assert_equal "MyApp", @client.display_name
        end
      end

      context "existing client" do
        setup do
          Server.register(:id=>"4ce24c423321e88ac5000015", :secret=>"foobar", :display_name=>"MyApp")
          @client = Server.register(:id=>"4ce24c423321e88ac5000015", :secret=>"foobar", :display_name=>"Rock Star")
        end

        should "not create new client" do
          assert_equal 2, Server::Client.collection.count
        end

        should "should not change the client identifier" do
          assert_equal "4ce24c423321e88ac5000015", @client.id.to_s
        end

        should "should not change the client secret" do
          assert_equal "foobar", @client.secret
        end

        should "should change all the other properties" do
          assert_equal "Rock Star", @client.display_name
        end
      end

      context "secret mismatch" do
        setup do
          Server.register(:id=>"4ce24c423321e88ac5000015", :secret=>"foobar", :display_name=>"MyApp")
        end

        should "raise error" do
          assert_raises RuntimeError do
            Server.register(:id=>"4ce24c423321e88ac5000015", :secret=>"wrong", :display_name=>"MyApp")
          end
        end
      end

    end
  end

  
  context "access_grant" do
    setup do
      code = Server.access_grant("Batman", client.id, %w{read})
      basic_authorize client.id, client.secret
      post "/oauth/access_token", :scope=>"read", :grant_type=>"authorization_code", :code=>code, :redirect_uri=>client.redirect_uri
      @token = JSON.parse(last_response.body)["access_token"]
    end

    should "resolve into an access token" do
      assert Server.get_access_token(@token)
    end

    should "resolve into access token with grant identity" do
      assert_equal "Batman", Server.get_access_token(@token).identity
    end

    should "resolve into access token with grant scope" do
      assert_equal %w{read}, Server.get_access_token(@token).scope
    end

    should "resolve into access token with grant client" do
      assert_equal client.id, Server.get_access_token(@token).client_id
    end

    context "with no scope" do
      setup { @code = Server.access_grant("Batman", client.id) }

      should "pick client scope" do
        assert_equal %w{oauth-admin read write}, Server::AccessGrant.from_code(@code).scope
      end
    end

    context "no expiration" do
      setup do
        @code = Server.access_grant("Batman", client.id)
      end

      should "not expire in a minute" do
        Timecop.travel 60 do
          basic_authorize client.id, client.secret
          post "/oauth/access_token", :scope=>"read", :grant_type=>"authorization_code", :code=>@code, :redirect_uri=>client.redirect_uri
          assert_equal 200, last_response.status
        end
      end

      should "expire after 5 minutes" do
        Timecop.travel 300 do
          basic_authorize client.id, client.secret
          post "/oauth/access_token", :scope=>"read", :grant_type=>"authorization_code", :code=>@code, :redirect_uri=>client.redirect_uri
          assert_equal 400, last_response.status
        end
      end
    end

    context "expiration set" do
      setup do
        @code = Server.access_grant("Batman", client.id, nil, 1800)
      end

      should "not expire prematurely" do
        Timecop.travel 1750 do
          basic_authorize client.id, client.secret
          post "/oauth/access_token", :scope=>"read", :grant_type=>"authorization_code", :code=>@code, :redirect_uri=>client.redirect_uri
          assert_equal 200, last_response.status
        end
      end

      should "expire after specified seconds" do
        Timecop.travel 1800 do
          basic_authorize client.id, client.secret
          post "/oauth/access_token", :scope=>"read", :grant_type=>"authorization_code", :code=>@code, :redirect_uri=>client.redirect_uri
          assert_equal 400, last_response.status
        end
      end
    end

  end


  context "get_access_token" do
    setup { @token = Server.token_for("Batman", client.id, %w{read}) }
    should "return authorization request" do
      assert_equal @token, Server.get_access_token(@token).token
    end

    should "return nil if no client found" do
      assert !Server.get_access_token("4ce2488e3321e87ac1000004")
    end

    context "with no scope" do
      setup { @token = Server.token_for("Batman", client.id) }

      should "pick client scope" do
        assert_equal %w{oauth-admin read write}, Server::AccessToken.from_token(@token).scope
      end
    end
  end


  context "token_for" do
    setup { @token = Server.token_for("Batman", client.id, %w{read write}) }

    should "return access token" do
      assert_match /[0-9a-f]{32}/, @token
    end

    should "associate token with client" do
      assert_equal client.id, Server.get_access_token(@token).client_id
    end

    should "associate token with identity" do
      assert_equal "Batman", Server.get_access_token(@token).identity
    end

    should "associate token with scope" do
      assert_equal %w{read write}, Server.get_access_token(@token).scope
    end

    should "return same token for same parameters" do
      assert_equal @token, Server.token_for("Batman", client.id, %w{write read})
    end

    should "return different token for different identity" do
      assert @token != Server.token_for("Superman", client.id, %w{read write})
    end

    should "return different token for different client" do
      client = Server.register(:display_name=>"MyApp")
      assert @token != Server.token_for("Batman", client.id, %w{read write})
    end

    should "return different token for different scope" do
      assert @token != Server.token_for("Batman", client.id, %w{read})
    end

    should 'expire token after the specified amount of time' do
      Server::AccessToken.collection.drop
      token = Server.token_for("Batman", client.id, %w{read write}, 60)

      Timecop.travel 120 do
        assert token != Server.token_for("Batman", client.id, %w{read write})
      end
    end
  end

  context "list access tokens" do
    setup do
      @one = Server.token_for("Batman", client.id, %w{read})
      @two = Server.token_for("Superman", client.id, %w{read})
      @three = Server.token_for("Batman", client.id, %w{write})
    end

    should "return all tokens for identity" do
      assert_contains Server.list_access_tokens("Batman").map(&:token), @one
      assert_contains Server.list_access_tokens("Batman").map(&:token), @three
    end

    should "not return tokens for other identities" do
      assert !Server.list_access_tokens("Batman").map(&:token).include?(@two)
    end

  end

  context "issuers" do
    setup do
      @issuer = Server.register_issuer(:identifier => "http://www.hmacissuer.com", :hmac_secret => "foo", :notes => "Test HMAC Issuer")
    end

    should "return a known issuer" do
      issuer = Server.get_issuer("http://www.hmacissuer.com")
      assert issuer
      assert issuer.hmac_secret == "foo"
      assert issuer.notes == "Test HMAC Issuer"
    end
  end
end
