require 'spec_helper'
require 'puppet/util/command_line/puppet_option_parser'

describe Puppet::Util::CommandLine::PuppetOptionParser do
  let(:option_parser) { described_class.new }

  describe "an option with a value" do
    it "parses a 'long' option with a value" do
      parses(
        :option => ["--angry", "Angry", :REQUIRED],
        :from_arguments => ["--angry", "foo"],
        :expects => "foo"
      )
    end

    it "parses a 'short' option with a value" do
      parses(
        :option => ["--angry", "-a", "Angry", :REQUIRED],
        :from_arguments => ["-a", "foo"],
        :expects => "foo"
      )
    end

    it "overrides a previous argument with a later one" do
      parses(
        :option => ["--later", "Later", :REQUIRED],
        :from_arguments => ["--later", "tomorrow", "--later", "morgen"],
        :expects => "morgen"
      )
    end
  end

  describe "an option without a value" do
    it "parses a 'long' option" do
      parses(
        :option => ["--angry", "Angry", :NONE],
        :from_arguments => ["--angry"],
        :expects => true
      )
    end

    it "parses a 'short' option" do
      parses(
        :option => ["--angry", "-a", "Angry", :NONE],
        :from_arguments => ["-a"],
        :expects => true
      )
    end

    it "supports the '--no-blah' syntax" do
      parses(
        :option => ["--[no-]rage", "Rage", :NONE],
        :from_arguments => ["--no-rage"],
        :expects => false
      )
    end

    it "overrides a previous argument with a later one" do
      parses(
        :option => ["--[no-]rage", "Rage", :NONE],
        :from_arguments => ["--rage", "--no-rage"],
        :expects => false
      )
    end
  end

  it "does not accept an unknown option specification" do
    expect {
      option_parser.on("not", "enough")
    }.to raise_error(ArgumentError, /this method only takes 3 or 4 arguments/)
  end

  it "does not modify the original argument array" do
    option_parser.on("--foo", "Foo", :NONE) { |val| }
    args = ["--foo"]

    option_parser.parse(args)

    args.length.should == 1
  end

  # The ruby stdlib OptionParser has an awesome "feature" that you cannot disable, whereby if
  #  it sees a short option that you haven't specifically registered with it (e.g., "-r"), it
  #  will automatically attempt to expand it out to whatever long options that you might have
  #  registered.  Since we need to do our option parsing in two passes (one pass against only
  #  the global/puppet-wide settings definitions, and then a second pass that includes the
  #  application or face settings--because we can't load the app/face until we've determined
  #  the libdir), it is entirely possible that we intend to define our "short" option as part
  #  of the second pass.  Therefore, if the option parser attempts to expand it out into a
  #  long option during the first pass, terrible things will happen.
  #
  # A long story short: we need to have the ability to control this kind of behavior in our
  #  option parser, and this test simply affirms that we do.
  it "does not try to expand short options that weren't explicitly registered" do

    [
     ["--ridiculous", "This is ridiculous", :REQUIRED],
     ["--rage-inducing", "This is rage-inducing", :REQUIRED]
    ].each do |option|
      option_parser.on(*option) {}
    end

    expect { option_parser.parse(["-r"]) }.to raise_error(Puppet::Util::CommandLine::PuppetOptionError)
  end

  it "respects :ignore_invalid_options" do
    option_parser.ignore_invalid_options = true
    expect { option_parser.parse(["--unknown-option"]) }.not_to raise_error
  end

  it "raises if there is an invalid option and :ignore_invalid_options is not set" do
    expect { option_parser.parse(["--unknown-option"]) }.to raise_error(Puppet::Util::CommandLine::PuppetOptionError)
  end

  def parses(option_case)
    option = option_case[:option]
    expected_value = option_case[:expects]
    arguments = option_case[:from_arguments]

    seen_value = nil
    option_parser.on(*option) do |val|
      seen_value = val
    end

    option_parser.parse(arguments)

    seen_value.should == expected_value
  end
end
