require "helper"
require "inspec/input"

describe "type validation" do
  let(:opts) { {} }
  let(:input) { Inspec::Input.new("test_input", opts) }

  #==============================================================#
  #                      Requiredness
  #==============================================================#
  describe "enforce_required_validation" do
    it "does not error if a value is set" do
      input = Inspec::Input.new("test_input", value: "some_value", required: true)
      _(input.value).must_equal "some_value"
    end

    it "does not error if a value is specified by value=" do
      input = Inspec::Input.new("test_input", required: true)
      input.value = "test_value"
      _(input.value).must_equal "test_value"
    end

    it "returns an error if no value is set" do
      # Assigning the cli_command is needed because in check mode, we don't error
      # on unset inputs. This is how you tell the input system we are not in
      # check mode, apparently.
      Inspec::BaseCLI.inspec_cli_command = :exec
      input = Inspec::Input.new("test_input", required: true)
      ex = assert_raises(Inspec::Input::RequiredError) { input.value }
      _(ex.message).must_match(/Input 'test_input' is required and does not have a value./)
      Inspec::BaseCLI.inspec_cli_command = nil
    end
  end

  #==============================================================#
  #                   Type Validation
  #==============================================================#
  describe "enforce_type_validation" do
    {
      "string" => { good: "a_string", bad: 123.3, norm: "String" },
      "numeric" => { good: 123, bad: "not a number", norm: "Numeric" },
      "regex" => { good: /\d+.+/, bad: "/(.+/", norm: "Regexp" },
      "array" => { good: [1, 2, 3], bad: { a: 1, b: 2, c: 3 }, norm: "Array" },
      "hash" => { good: { a: 1, b: 2, c: 3 }, bad: "i am not a hash", norm: "Hash" },
      "boolean" => { good: true, bad: "i am not a boolean", norm: "Boolean" },
    }.each do |type, examples|
      it "validates a #{type} in the constructor - (good)" do
        opts = { type: type, value: examples[:good] }
        Inspec::Input.new("test_input", opts) # No exception
      end

      it "validates a #{type} in the constructor - (bad)" do
        opts = { type: type, value: examples[:bad] }
        ex = assert_raises(Inspec::Input::ValidationError) { Inspec::Input.new("test_input", opts) }
        _(ex.message).must_include "test_input"
        _(ex.message).must_include "'#{examples[:bad]}'"
        _(ex.message).must_include "does not validate to type '#{examples[:norm]}'"
      end

      it "validates a #{type} in value= (good)" do
        att = Inspec::Input.new("test_input", type: type)
        att.value = examples[:good]
      end

      it "validates a #{type} in the value= - (bad)" do
        att = Inspec::Input.new("test_input", type: type)
        ex = assert_raises(Inspec::Input::ValidationError) { att.value = examples[:bad] }
        _(ex.message).must_include "test_input"
        _(ex.message).must_include "'#{examples[:bad]}'"
        _(ex.message).must_include "does not validate to type '#{examples[:norm]}'"
      end
    end
    it "validates the Any type" do
      Inspec::Input.new("test_input", type: "any", value: false) # No exception
      Inspec::Input.new("test_input", type: "any", value: true) # No exception
      Inspec::Input.new("test_input", type: "any", value: "bob") # No exception
      Inspec::Input.new("test_input", type: "any", value: 1) # No exception
    end
  end

  #==============================================================#
  #           Type Option Validation and Normalization
  #==============================================================#
  describe "validate type option" do
    it "converts regex to Regexp" do
      opts[:type] = "regex"
      _(input.type).must_equal "Regexp"
    end

    it "returns the same value if there is nothing to clean" do
      opts[:type] = "String"
      _(input.type).must_equal "String"
    end

    it "returns an error if a invalid type is sent" do
      opts[:type] = "dressing"
      ex = assert_raises(Inspec::Input::TypeError) { input }
      _(ex.message).must_match(/Type 'Dressing' is not a valid input type./)
    end
  end

  describe "valid_regexp? method" do
    it "validates a string regex" do
      _(input.send(:valid_regexp?, "/.*/")).must_equal true
    end

    it "validates a slash regex" do
      _(input.send(:valid_regexp?, /.*/)).must_equal true
    end

    it "does not validate a invalid regex" do
      _(input.send(:valid_regexp?, "/.*(/")).must_equal false
    end
  end

  describe "valid_numeric? method" do
    it "validates a string number" do
      _(input.send(:valid_numeric?, "123")).must_equal true
    end

    it "validates a float number" do
      _(input.send(:valid_numeric?, 44.55)).must_equal true
    end

    it "validats a wrong padded number" do
      _(input.send(:valid_numeric?, "00080")).must_equal true
    end

    it "does not vaildate a invalid number" do
      _(input.send(:valid_numeric?, "55.55.55.5")).must_equal false
    end

    it "does not validate a invalid string" do
      _(input.send(:valid_numeric?, "one")).must_equal false
    end

    it "does not validate a fraction" do
      _(input.send(:valid_numeric?, "1/2")).must_equal false
    end
  end
end
