# frozen_string_literal: true

require_relative "helper"
require "rubygems/gemcutter_utilities/webauthn_poller"
require "rubygems/gemcutter_utilities"

class WebauthnPollerTest < Gem::TestCase
  def setup
    super

    @host = Gem.host
    @webauthn_url = "#{@host}/api/v1/webauthn_verification/odow34b93t6aPCdY"
    @fetcher = Gem::FakeFetcher.new
    Gem::RemoteFetcher.fetcher = @fetcher
    @credentials = {
      identifier: "email@example.com",
      password: "password",
    }
  end

  def test_poll_thread_success
    @fetcher.data["#{@webauthn_url}/status.json"] = Gem::HTTPResponseFactory.create(
      body: "{\"status\":\"success\",\"code\":\"Uvh6T57tkWuUnWYo\"}",
      code: 200,
      msg: "OK"
    )

    thread = Gem::GemcutterUtilities::WebauthnPoller.poll_thread({}, @host, @webauthn_url, @credentials)
    thread.join

    assert_equal thread[:otp], "Uvh6T57tkWuUnWYo"
  end

  def test_poll_thread_webauthn_verification_error
    @fetcher.data["#{@webauthn_url}/status.json"] = Gem::HTTPResponseFactory.create(
      body: "HTTP Basic: Access denied.",
      code: 401,
      msg: "Unauthorized"
    )

    thread = Gem::GemcutterUtilities::WebauthnPoller.poll_thread({}, @host, @webauthn_url, @credentials)
    thread.join

    assert_equal thread[:error].message, "Security device verification failed: Unauthorized"
  end

  def test_poll_thread_timeout_error
    raise_error = ->(*_args) { raise Gem::Timeout::Error, "execution expired" }
    Gem::Timeout.stub(:timeout, raise_error) do
      thread = Gem::GemcutterUtilities::WebauthnPoller.poll_thread({}, @host, @webauthn_url, @credentials)
      thread.join
      assert_equal thread[:error].message, "execution expired"
    end
  end

  def test_poll_for_otp_success
    @fetcher.data["#{@webauthn_url}/status.json"] = Gem::HTTPResponseFactory.create(
      body: "{\"status\":\"success\",\"code\":\"Uvh6T57tkWuUnWYo\"}",
      code: 200,
      msg: "OK"
    )

    otp = Gem::GemcutterUtilities::WebauthnPoller.new({}, @host).poll_for_otp(@webauthn_url, @credentials)

    assert_equal otp, "Uvh6T57tkWuUnWYo"
  end

  def test_poll_for_otp_pending_sleeps
    @fetcher.data["#{@webauthn_url}/status.json"] = Gem::HTTPResponseFactory.create(
      body: "{\"status\":\"pending\",\"message\":\"Security device authentication is still pending.\"}",
      code: 200,
      msg: "OK"
    )

    assert_raise Gem::Timeout::Error do
      Gem::Timeout.timeout(0.1) do
        Gem::GemcutterUtilities::WebauthnPoller.new({}, @host).poll_for_otp(@webauthn_url, @credentials)
      end
    end
  end

  def test_poll_for_otp_not_http_success
    @fetcher.data["#{@webauthn_url}/status.json"] = Gem::HTTPResponseFactory.create(
      body: "HTTP Basic: Access denied.",
      code: 401,
      msg: "Unauthorized"
    )

    error = assert_raise Gem::WebauthnVerificationError do
      Gem::GemcutterUtilities::WebauthnPoller.new({}, @host).poll_for_otp(@webauthn_url, @credentials)
    end

    assert_equal error.message, "Security device verification failed: Unauthorized"
  end

  def test_poll_for_otp_invalid_format
    @fetcher.data["#{@webauthn_url}/status.json"] = Gem::HTTPResponseFactory.create(
      body: "{}",
      code: 200,
      msg: "OK"
    )

    error = assert_raise Gem::WebauthnVerificationError do
      Gem::GemcutterUtilities::WebauthnPoller.new({}, @host).poll_for_otp(@webauthn_url, @credentials)
    end

    assert_equal error.message, "Security device verification failed: Invalid response from server"
  end

  def test_poll_for_otp_invalid_status
    @fetcher.data["#{@webauthn_url}/status.json"] = Gem::HTTPResponseFactory.create(
      body: "{\"status\":\"expired\",\"message\":\"The token in the link you used has either expired or been used already.\"}",
      code: 200,
      msg: "OK"
    )

    error = assert_raise Gem::WebauthnVerificationError do
      Gem::GemcutterUtilities::WebauthnPoller.new({}, @host).poll_for_otp(@webauthn_url, @credentials)
    end

    assert_equal error.message,
      "Security device verification failed: The token in the link you used has either expired or been used already."
  end

  def test_poll_for_otp_missing_credentials
    @credentials = { password: "password" }

    error = assert_raise Gem::WebauthnVerificationError do
      Gem::GemcutterUtilities::WebauthnPoller.new({}, @host).poll_for_otp(@webauthn_url, @credentials)
    end

    assert_equal error.message, "Security device verification failed: Provided missing credentials"
  end
end
