require "spec"
require "oauth2"
require "http/server"
require "../http/spec_helper"

describe OAuth2::Client do
  describe "authorization uri" do
    it "gets with default endpoint" do
      client = OAuth2::Client.new "localhost", "client_id", "client_secret", redirect_uri: "uri"
      uri = client.get_authorize_uri(scope: "foo bar")
      uri.should eq("https://localhost/oauth2/authorize?client_id=client_id&redirect_uri=uri&response_type=code&scope=foo+bar")
    end

    it "gets with custom endpoint" do
      client = OAuth2::Client.new "localhost", "client_id", "client_secret", redirect_uri: "uri", authorize_uri: "/baz"
      uri = client.get_authorize_uri(scope: "foo bar")
      uri.should eq("https://localhost/baz?client_id=client_id&redirect_uri=uri&response_type=code&scope=foo+bar")
    end

    it "gets with state" do
      client = OAuth2::Client.new "localhost", "client_id", "client_secret", redirect_uri: "uri"
      uri = client.get_authorize_uri(scope: "foo bar", state: "xyz")
      uri.should eq("https://localhost/oauth2/authorize?client_id=client_id&redirect_uri=uri&response_type=code&scope=foo+bar&state=xyz")
    end

    it "gets with block" do
      client = OAuth2::Client.new "localhost", "client_id", "client_secret", redirect_uri: "uri"
      uri = client.get_authorize_uri(scope: "foo bar") do |form|
        form.add "baz", "qux"
      end
      uri.should eq("https://localhost/oauth2/authorize?client_id=client_id&redirect_uri=uri&response_type=code&scope=foo+bar&baz=qux")
    end

    it "gets with absolute uri" do
      client = OAuth2::Client.new "localhost", "client_id", "client_secret",
        redirect_uri: "uri",
        authorize_uri: "https://example2.com:1234/foo?bar=baz"
      uri = client.get_authorize_uri(scope: "foo bar")
      uri.should eq("https://example2.com:1234/foo?client_id=client_id&redirect_uri=uri&response_type=code&scope=foo+bar&bar=baz")
    end
  end

  describe "get_access_token_using_*" do
    describe "using HTTP Basic authentication to pass credentials" do
      describe "#get_access_token_using_authorization_code" do
        it "gets a valid token successfully" do
          handler = HTTP::Handler::HandlerProc.new do |context|
            body = context.request.body.should_not(be_nil).gets_to_end
            response = {access_token: "access_token", body: body}
            context.response.print response.to_json
          end

          run_handler(handler) do |http_client|
            client = OAuth2::Client.new "127.0.0.1", "client_id", "client_secret", scheme: "http"
            client.http_client = http_client

            token = client.get_access_token_using_authorization_code(authorization_code: "SDFhw39fwfg23flSfpawbef")
            token.extra.should_not(be_nil)["body"].should eq %("redirect_uri=&grant_type=authorization_code&code=SDFhw39fwfg23flSfpawbef")
            token.access_token.should eq "access_token"
          end
        end

        it "returns an error if the response has an error status" do
          handler = HTTP::Handler::HandlerProc.new do |context|
            context.response.status = :bad_request
          end

          run_handler(handler) do |http_client|
            client = OAuth2::Client.new "127.0.0.1", "client_id", "client_secret", scheme: "http"
            client.http_client = http_client

            expect_raises OAuth2::Error do
              client.get_access_token_using_authorization_code(authorization_code: "asdf")
            end
          end
        end

        it "returns an error if the payload is an error" do
          handler = HTTP::Handler::HandlerProc.new do |context|
            {error: "oops"}.to_json context.response
          end

          run_handler(handler) do |http_client|
            client = OAuth2::Client.new "127.0.0.1", "client_id", "client_secret", scheme: "http"
            client.http_client = http_client

            expect_raises OAuth2::Error do
              client.get_access_token_using_authorization_code(authorization_code: "asdf")
            end
          end
        end
      end

      it "configures HTTP::Client" do
        server = HTTP::Server.new do |context|
          body = context.request.body.should_not(be_nil).gets_to_end
          response = {access_token: "access_token", body: body}
          context.response.print response.to_json
        end
        address = server.bind_tcp 0

        run_server(server) do
          client = OAuth2::Client.new "127.0.0.1", "client_id", "client_secret", port: address.port, scheme: "http"
          client.http_client.port.should eq address.port
          client.http_client.host.should eq "127.0.0.1"

          token = client.get_access_token_using_authorization_code(authorization_code: "SDFhw39fwfg23flSfpawbef")
          token.extra.should_not(be_nil)["body"].should eq %("redirect_uri=&grant_type=authorization_code&code=SDFhw39fwfg23flSfpawbef")
          token.access_token.should eq "access_token"
        end
      end

      it "#get_access_token_using_resource_owner_credentials" do
        handler = HTTP::Handler::HandlerProc.new do |context|
          body = context.request.body.should_not(be_nil).gets_to_end
          response = {access_token: "access_token", body: body}
          context.response.print response.to_json
        end

        run_handler(handler) do |http_client|
          client = OAuth2::Client.new "127.0.0.1", "client_id", "client_secret", scheme: "http"
          client.http_client = http_client

          token = client.get_access_token_using_resource_owner_credentials(username: "user123", password: "monkey", scope: "read_posts")
          token.extra.should_not(be_nil)["body"].should eq %("grant_type=password&username=user123&password=monkey&scope=read_posts")
          token.access_token.should eq "access_token"
        end
      end

      it "#get_access_token_using_client_credentials" do
        handler = HTTP::Handler::HandlerProc.new do |context|
          body = context.request.body.should_not(be_nil).gets_to_end
          response = {access_token: "access_token", body: body}
          context.response.print response.to_json
        end

        run_handler(handler) do |http_client|
          client = OAuth2::Client.new "127.0.0.1", "client_id", "client_secret", scheme: "http"
          client.http_client = http_client

          token = client.get_access_token_using_client_credentials(scope: "read_posts")
          token.extra.should_not(be_nil)["body"].should eq %("grant_type=client_credentials&scope=read_posts")
          token.access_token.should eq "access_token"
        end
      end

      it "#get_access_token_using_refresh_token" do
        handler = HTTP::Handler::HandlerProc.new do |context|
          body = context.request.body.should_not(be_nil).gets_to_end
          response = {access_token: "access_token", body: body}
          context.response.print response.to_json
        end

        run_handler(handler) do |http_client|
          client = OAuth2::Client.new "127.0.0.1", "client_id", "client_secret", scheme: "http"
          client.http_client = http_client

          token = client.get_access_token_using_refresh_token(scope: "read_posts", refresh_token: "some_refresh_token")
          token.extra.should_not(be_nil)["body"].should eq %("grant_type=refresh_token&refresh_token=some_refresh_token&scope=read_posts")
          token.access_token.should eq "access_token"
        end
      end

      it "#make_token_request" do
        handler = HTTP::Handler::HandlerProc.new do |context|
          body = context.request.body.should_not(be_nil).gets_to_end
          dpop = context.request.headers.get?("DPoP")
          response = {access_token: "access_token", body: body, dpop: dpop}
          context.response.print response.to_json
        end

        run_handler(handler) do |http_client|
          client = OAuth2::Client.new "127.0.0.1", "client_id", "client_secret", scheme: "http"
          client.http_client = http_client

          token_response = client.make_token_request do |form, headers|
            form.add("redirect_uri", client.redirect_uri)
            form.add("grant_type", "authorization_code")
            form.add("code", "some_authorization_code")
            form.add("code_verifier", "a_code_verifier")
            form.add("nonce", "a_nonce")
            headers.add("DPoP", "a_DPoP_jwt_token")
          end
          token_response.status_code.should eq(200)
          token = OAuth2::AccessToken.from_json(token_response.body)
          token.extra.should_not(be_nil)["body"].should eq %("redirect_uri=&grant_type=authorization_code&code=some_authorization_code&code_verifier=a_code_verifier&nonce=a_nonce")
          token.extra.should_not(be_nil)["dpop"].should eq %(["a_DPoP_jwt_token"])
          token.access_token.should eq "access_token"
        end
      end
    end
    describe "using Request Body to pass credentials" do
      it "#get_access_token_using_authorization_code" do
        handler = HTTP::Handler::HandlerProc.new do |context|
          body = context.request.body.should_not(be_nil).gets_to_end
          response = {access_token: "access_token", body: body}
          context.response.print response.to_json
        end

        run_handler(handler) do |http_client|
          client = OAuth2::Client.new "127.0.0.1", "client_id", "client_secret", scheme: "http", auth_scheme: OAuth2::AuthScheme::RequestBody
          client.http_client = http_client

          token = client.get_access_token_using_authorization_code(authorization_code: "SDFhw39fwfg23flSfpawbef")
          token.extra.should_not(be_nil)["body"].should eq %("client_id=client_id&client_secret=client_secret&redirect_uri=&grant_type=authorization_code&code=SDFhw39fwfg23flSfpawbef")
          token.access_token.should eq "access_token"
        end
      end

      it "#get_access_token_using_resource_owner_credentials" do
        handler = HTTP::Handler::HandlerProc.new do |context|
          body = context.request.body.should_not(be_nil).gets_to_end
          response = {access_token: "access_token", body: body}
          context.response.print response.to_json
        end

        run_handler(handler) do |http_client|
          client = OAuth2::Client.new "127.0.0.1", "client_id", "client_secret", scheme: "http", auth_scheme: OAuth2::AuthScheme::RequestBody
          client.http_client = http_client

          token = client.get_access_token_using_resource_owner_credentials(username: "user123", password: "monkey", scope: "read_posts")
          token.extra.should_not(be_nil)["body"].should eq %("client_id=client_id&client_secret=client_secret&grant_type=password&username=user123&password=monkey&scope=read_posts")
          token.access_token.should eq "access_token"
        end
      end

      it "#get_access_token_using_client_credentials" do
        handler = HTTP::Handler::HandlerProc.new do |context|
          body = context.request.body.should_not(be_nil).gets_to_end
          response = {access_token: "access_token", body: body}
          context.response.print response.to_json
        end

        run_handler(handler) do |http_client|
          client = OAuth2::Client.new "127.0.0.1", "client_id", "client_secret", scheme: "http", auth_scheme: OAuth2::AuthScheme::RequestBody
          client.http_client = http_client

          token = client.get_access_token_using_client_credentials(scope: "read_posts")
          token.extra.should_not(be_nil)["body"].should eq %("client_id=client_id&client_secret=client_secret&grant_type=client_credentials&scope=read_posts")
          token.access_token.should eq "access_token"
        end
      end

      it "#get_access_token_using_refresh_token" do
        handler = HTTP::Handler::HandlerProc.new do |context|
          body = context.request.body.should_not(be_nil).gets_to_end
          response = {access_token: "access_token", body: body}
          context.response.print response.to_json
        end

        run_handler(handler) do |http_client|
          client = OAuth2::Client.new "127.0.0.1", "client_id", "client_secret", scheme: "http", auth_scheme: OAuth2::AuthScheme::RequestBody
          client.http_client = http_client

          token = client.get_access_token_using_refresh_token(scope: "read_posts", refresh_token: "some_refresh_token")
          token.extra.should_not(be_nil)["body"].should eq %("client_id=client_id&client_secret=client_secret&grant_type=refresh_token&refresh_token=some_refresh_token&scope=read_posts")
          token.access_token.should eq "access_token"
        end
      end

      it "#make_token_request" do
        handler = HTTP::Handler::HandlerProc.new do |context|
          body = context.request.body.should_not(be_nil).gets_to_end
          dpop = context.request.headers.get?("DPoP")
          response = {access_token: "access_token", body: body, dpop: dpop}
          context.response.print response.to_json
        end

        run_handler(handler) do |http_client|
          client = OAuth2::Client.new "127.0.0.1", "client_id", "client_secret", scheme: "http", auth_scheme: OAuth2::AuthScheme::RequestBody
          client.http_client = http_client

          token_response = client.make_token_request do |form, headers|
            form.add("grant_type", "refresh_token")
            form.add("refresh_token", "some_refresh_token")
            form.add("scope", "read_posts")
            form.add("nonce", "a_nonce")
            headers.add("DPoP", "a_DPoP_jwt_token")
          end
          token_response.status_code.should eq(200)
          token = OAuth2::AccessToken.from_json(token_response.body)
          token.extra.should_not(be_nil)["body"].should eq %("client_id=client_id&client_secret=client_secret&grant_type=refresh_token&refresh_token=some_refresh_token&scope=read_posts&nonce=a_nonce")
          token.extra.should_not(be_nil)["dpop"].should eq %(["a_DPoP_jwt_token"])
          token.access_token.should eq "access_token"
        end
      end
    end
  end

  typeof(begin
    client = OAuth2::Client.new "localhost", "client_id", "client_secret", redirect_uri: "uri", authorize_uri: "/baz"
    client.get_access_token_using_authorization_code("some_code")
    client.get_access_token_using_refresh_token("some_refresh_token")
    client.get_access_token_using_refresh_token("some_refresh_token", scope: "some scope")
    client.get_access_token_using_client_credentials(scope: "some scope")
    client.get_access_token_using_resource_owner_credentials(username: "user123", password: "monkey")
    client.get_access_token_using_resource_owner_credentials(username: "user123", password: "monkey", scope: "foo")
  end)
end
