# frozen_string_literal: true

RSpec.describe Dispatch::Adapter::Claude::PKCE do
  describe ".generate" do
    subject(:pair) { described_class.generate }

    it "returns a hash with :verifier and :challenge keys" do
      expect(pair).to have_key(:verifier)
      expect(pair).to have_key(:challenge)
    end

    it "verifier is 43 characters of URL-safe base64 without padding" do
      verifier = pair[:verifier]
      expect(verifier.length).to eq(43)
      expect(verifier).to match(/\A[A-Za-z0-9\-_]+\z/)
      expect(verifier).not_to include("=")
    end

    it "challenge is 43 characters of SHA-256(verifier) base64url without padding" do
      challenge = pair[:challenge]
      expect(challenge.length).to eq(43)
      expect(challenge).to match(/\A[A-Za-z0-9\-_]+\z/)
      expect(challenge).not_to include("=")
    end

    it "challenge matches SHA-256(verifier) manually computed" do
      verifier = pair[:verifier]
      expected_challenge = Base64.urlsafe_encode64(Digest::SHA256.digest(verifier)).delete("=")
      expect(pair[:challenge]).to eq(expected_challenge)
    end

    it "two consecutive calls return different verifiers" do
      pair1 = described_class.generate
      pair2 = described_class.generate
      expect(pair1[:verifier]).not_to eq(pair2[:verifier])
    end

    it "two consecutive calls return different challenges" do
      pair1 = described_class.generate
      pair2 = described_class.generate
      expect(pair1[:challenge]).not_to eq(pair2[:challenge])
    end
  end

  describe ".base64url" do
    it "encodes bytes as URL-safe base64 without padding" do
      bytes = "\xFF\xFE\xFD"
      result = described_class.base64url(bytes)
      expect(result).not_to include("=")
      expect(result).not_to include("+")
      expect(result).not_to include("/")
    end

    it "produces URL-safe characters only" do
      result = described_class.base64url(SecureRandom.bytes(32))
      expect(result).to match(/\A[A-Za-z0-9\-_]+\z/)
    end
  end
end
