# frozen_string_literal: true

require "spec_helper"

describe "Revoke Token Flow" do
  before do
    Doorkeeper.configure { orm DOORKEEPER_ORM }
  end

  context "with default parameters" do
    let(:client_application) { FactoryBot.create :application }
    let(:resource_owner) { User.create!(name: "John", password: "sekret") }
    let(:access_token) do
      FactoryBot.create(
        :access_token,
        application: client_application,
        resource_owner_id: resource_owner.id,
        use_refresh_token: true,
      )
    end

    context "with authenticated, confidential OAuth 2.0 client/application" do
      let(:headers) do
        client_id = client_application.uid
        client_secret = client_application.secret
        credentials = Base64.encode64("#{client_id}:#{client_secret}")
        { "HTTP_AUTHORIZATION" => "Basic #{credentials}" }
      end

      it "should revoke the access token provided" do
        post revocation_token_endpoint_url, params: { token: access_token.token }, headers: headers

        expect(response).to be_successful
        expect(access_token.reload.revoked?).to be_truthy
      end

      it "should revoke the refresh token provided" do
        post revocation_token_endpoint_url, params: { token: access_token.refresh_token }, headers: headers

        expect(response).to be_successful
        expect(access_token.reload.revoked?).to be_truthy
      end

      context "with invalid token to revoke" do
        it "should not revoke any tokens and respond with forbidden" do
          expect do
            post revocation_token_endpoint_url,
                 params: { token: "I_AM_AN_INVALID_TOKEN" },
                 headers: headers
          end.not_to(change { Doorkeeper::AccessToken.where(revoked_at: nil).count })

          expect(response).to be_forbidden
        end
      end

      context "with bad credentials and a valid token" do
        let(:headers) do
          client_id = client_application.uid
          credentials = Base64.encode64("#{client_id}:poop")
          { "HTTP_AUTHORIZATION" => "Basic #{credentials}" }
        end
        it "should not revoke any tokens and respond with forbidden" do
          post revocation_token_endpoint_url, params: { token: access_token.token }, headers: headers

          expect(response).to be_forbidden
          expect(response.body).to include("unauthorized_client")
          expect(response.body).to include(I18n.t("doorkeeper.errors.messages.revoke.unauthorized"))
          expect(access_token.reload.revoked?).to be_falsey
        end
      end

      context "with no credentials and a valid token" do
        it "should not revoke any tokens and respond with forbidden" do
          post revocation_token_endpoint_url, params: { token: access_token.token }

          expect(response).to be_forbidden
          expect(response.body).to include("unauthorized_client")
          expect(response.body).to include(I18n.t("doorkeeper.errors.messages.revoke.unauthorized"))
          expect(access_token.reload.revoked?).to be_falsey
        end
      end

      context "with valid token for another client application" do
        let(:other_client_application) { FactoryBot.create :application }
        let(:headers) do
          client_id = other_client_application.uid
          client_secret = other_client_application.secret
          credentials = Base64.encode64("#{client_id}:#{client_secret}")
          { "HTTP_AUTHORIZATION" => "Basic #{credentials}" }
        end

        it "should not revoke the token as its unauthorized" do
          post revocation_token_endpoint_url, params: { token: access_token.token }, headers: headers

          expect(response).to be_forbidden
          expect(response.body).to include("unauthorized_client")
          expect(response.body).to include(I18n.t("doorkeeper.errors.messages.revoke.unauthorized"))
          expect(access_token.reload.revoked?).to be_falsey
        end
      end
    end

    context "with public OAuth 2.0 client/application" do
      let(:access_token) do
        FactoryBot.create(
          :access_token,
          application: nil,
          resource_owner_id: resource_owner.id,
          use_refresh_token: true,
        )
      end

      it "should revoke the access token provided" do
        post revocation_token_endpoint_url, params: { token: access_token.token }

        expect(response).to be_successful
        expect(access_token.reload.revoked?).to be_truthy
      end

      it "should revoke the refresh token provided" do
        post revocation_token_endpoint_url, params: { token: access_token.refresh_token }

        expect(response).to be_successful
        expect(access_token.reload.revoked?).to be_truthy
      end

      context "with a valid token issued for a confidential client" do
        let(:access_token) do
          FactoryBot.create(
            :access_token,
            application: client_application,
            resource_owner_id: resource_owner.id,
            use_refresh_token: true,
          )
        end

        it "should not revoke the access token provided" do
          post revocation_token_endpoint_url, params: { token: access_token.token }

          expect(response).to be_forbidden
          expect(response.body).to include("unauthorized_client")
          expect(response.body).to include(I18n.t("doorkeeper.errors.messages.revoke.unauthorized"))
          expect(access_token.reload.revoked?).to be_falsey
        end

        it "should not revoke the refresh token provided" do
          post revocation_token_endpoint_url, params: { token: access_token.token }

          expect(response).to be_forbidden
          expect(response.body).to include("unauthorized_client")
          expect(response.body).to include(I18n.t("doorkeeper.errors.messages.revoke.unauthorized"))
          expect(access_token.reload.revoked?).to be_falsey
        end
      end
    end
  end
end
