# frozen_string_literal: true

require_relative "helpers"

require "base64"

# Tests for KBSecret::Generator functionality
class KBSecretGeneratorsTest < Minitest::Test
  include Helpers

  def test_generators_list
    # generator_labels should always be an Array
    assert_instance_of Array, KBSecret::Config.generator_labels

    # ...and each element of that array should be a symbol that both satisfies
    # generator? and refers to a configured generator hash
    KBSecret::Config.generator_labels.each do |label|
      assert_instance_of Symbol, label
      assert_instance_of Hash, KBSecret::Config.generator(label)
      assert KBSecret::Config.generator?(label)
    end
  end

  def test_default_generator
    # the default generator should always exist, and generator? should take
    # both a string and a symbol
    assert KBSecret::Config.generator? :default
    assert KBSecret::Config.generator? "default"

    hsh = KBSecret::Config.generator :default

    # the default generator has a configured hash, and that hash is the same
    # whether looked up by string or symbol
    assert_instance_of Hash, hsh
    assert_equal hsh, KBSecret::Config.generator("default")

    # however, generator_labels should only have the symbol form
    assert_includes KBSecret::Config.generator_labels, :default
    refute_includes KBSecret::Config.generator_labels, "default"

    # instantiating the generator should work, in either implicit or explicit form
    KBSecret::Generator.new
    gen = KBSecret::Generator.new :default

    # running secret on the instance should return a string
    assert_instance_of String, gen.secret

    # subsequent runs should be unique
    refute_equal gen.secret, gen.secret
  end

  def test_nonexistent_generator
    # attempting to access a nonexistent generator with Config.generator should fail
    assert_raises KBSecret::Exceptions::GeneratorUnknownError do
      KBSecret::Config.generator :thisshouldnotexist
    end

    refute KBSecret::Config.generator? :thisshouldnotexist
    refute_includes KBSecret::Config.generator_labels, :thisshouldnotexist
  end

  def test_bad_generators
    # creating a generator with a bad format should work...
    KBSecret::Config.configure_generator :test_bad_gen,
                                         format: "doesnotexist",
                                         length: 8

    assert KBSecret::Config.generator? :test_bad_gen
    assert_instance_of Hash, KBSecret::Config.generator(:test_bad_gen)
    assert_includes KBSecret::Config.generator_labels, :test_bad_gen

    # ...but attempting to instantiate it should fail
    assert_raises KBSecret::Exceptions::GeneratorFormatError do
      KBSecret::Generator.new :test_bad_gen
    end

    # similarly, configuring a generator with a bad length should work...
    KBSecret::Config.configure_generator :test_bad_gen2,
                                         format: "base64",
                                         length: -100

    assert KBSecret::Config.generator? :test_bad_gen2
    assert_instance_of Hash, KBSecret::Config.generator(:test_bad_gen2)
    assert_includes KBSecret::Config.generator_labels, :test_bad_gen2

    # ...but attempting to instantiate it should fail
    assert_raises KBSecret::Exceptions::GeneratorLengthError do
      KBSecret::Generator.new :test_bad_gen2
    end
  ensure
    KBSecret::Config.deconfigure_generator :test_bad_gen
    KBSecret::Config.deconfigure_generator :test_bad_gen2
  end

  def test_hex_generator
    # configuring a new hex generator should work
    KBSecret::Config.configure_generator :test_hex_gen,
                                         format: "hex",
                                         length: 32

    assert KBSecret::Config.generator? :test_hex_gen
    assert_instance_of Hash, KBSecret::Config.generator(:test_hex_gen)
    assert_includes KBSecret::Config.generator_labels, :test_hex_gen

    # instantiating the new generator should work
    gen = KBSecret::Generator.new :test_hex_gen

    # the secrets generated by the new generator should be the right size
    assert_equal 64, gen.secret.size # 32 bytes * 2 hex characters/byte
  ensure
    KBSecret::Config.deconfigure_generator :test_hex_gen
  end

  def test_base64_generator
    # configuring a new base64 generator should work
    KBSecret::Config.configure_generator :test_b64_gen,
                                         format: "base64",
                                         length: 32

    assert KBSecret::Config.generator? :test_b64_gen
    assert_instance_of Hash, KBSecret::Config.generator(:test_b64_gen)
    assert_includes KBSecret::Config.generator_labels, :test_b64_gen

    # instantiating the new generator should work
    gen = KBSecret::Generator.new :test_b64_gen

    # the secrets generated by the new generator should be the right size
    assert_equal 32, Base64.decode64(gen.secret).size
  ensure
    KBSecret::Config.deconfigure_generator :test_b64_gen
  end
end
