#
# Copyright:: Copyright (c) 2014-2018 Chef Software Inc.
# License:: Apache License, Version 2.0
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
#     http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
#

require "spec_helper"
require "chef-cli/command/verify"

describe ChefCLI::Command::Base do
  class TestCommand < ChefCLI::Command::Base
    banner "use me please"

    option :argue,
      short:       "-a ARG",
      long:        "--arg ARG",
      description: "An option with a required argument"

    option :user,
      short: "-u",
      long: "--user",
      description: "If the user exists",
      boolean: true

    def run(params)
      parse_options(params)
      msg("thanks for passing me #{config[:user]}")
    end
  end

  let(:stderr_io) { StringIO.new }
  let(:stdout_io) { StringIO.new }
  let(:command_instance) { TestCommand.new }
  let(:enforce_license) { false }

  def stdout
    stdout_io.string
  end

  def stderr
    stderr_io.string
  end

  before do
    allow(command_instance).to receive(:stdout).and_return(stdout_io)
    allow(command_instance).to receive(:stderr).and_return(stderr_io)
  end

  def run_command(options)
    command_instance.run_with_default_options(enforce_license, options)
  end

  it "should print the banner for -h" do
    run_command(["-h"])
    expect(stdout).to include("use me please\n")
  end

  it "should print the banner for --help" do
    run_command(["--help"])
    expect(stdout).to include("use me please\n")
  end

  it "prints the options along with the banner when displaying the help message" do
    run_command(["--help"])
    expect(stdout).to include("-u, --user                       If the user exists")
  end

  it "should print the version for -v" do
    run_command(["-v"])
    expect(stdout).to eq("#{ChefCLI::Dist::PRODUCT} version: #{ChefCLI::VERSION}\n")
  end

  it "should print the version for --version" do
    run_command(["--version"])
    expect(stdout).to eq("#{ChefCLI::Dist::PRODUCT} version: #{ChefCLI::VERSION}\n")
  end

  it "should run the command passing in the custom options for long custom options" do
    run_command(["--user"])
    expect(stdout).to eq("thanks for passing me true\n")
  end

  it "should run the command passing in the custom options for short custom options" do
    run_command(["-u"])
    expect(stdout).to eq("thanks for passing me true\n")
  end

  describe "when enforce_license is true" do
    let(:enforce_license) { true }

    it "calls the license acceptance library" do
      expect(LicenseAcceptance::Acceptor).to receive(:check_and_persist!).with("chef-workstation", ChefCLI::VERSION.to_s)
      run_command([])
      expect(stdout).to eq("thanks for passing me \n")
    end
  end

  describe "when enforce_license is false" do
    let(:enforce_license) { false }

    it "does not call the license acceptance library" do
      expect(LicenseAcceptance::Acceptor).to_not receive(:check_and_persist!)
      run_command([])
      expect(stdout).to eq("thanks for passing me \n")
    end
  end

  describe "when given invalid options" do

    it "prints the help banner and exits gracefully" do
      expect(run_command(%w{-foo})).to eq(1)

      expect(stderr).to eq("ERROR: invalid option: -foo\n\n")

      expected = <<~E
        use me please
            -a, --arg ARG                    An option with a required argument
                --chef-license ACCEPTANCE    Accept the license for this product and any contained products ('accept', 'accept-no-persist', or 'accept-silent')
            -h, --help                       Show this message
            -u, --user                       If the user exists
            -v, --version                    Show #{ChefCLI::Dist::PRODUCT} version

      E
      expect(stdout).to eq(expected)
    end

  end

  describe "when given an option that requires an argument with no argument" do

    it "prints the help banner and exits gracefully" do
      expect(run_command(%w{-a})).to eq(1)

      expect(stderr).to eq("ERROR: missing argument: -a\n\n")

      expected = <<~E
        use me please
            -a, --arg ARG                    An option with a required argument
                --chef-license ACCEPTANCE    Accept the license for this product and any contained products ('accept', 'accept-no-persist', or 'accept-silent')
            -h, --help                       Show this message
            -u, --user                       If the user exists
            -v, --version                    Show #{ChefCLI::Dist::PRODUCT} version

      E
      expect(stdout).to eq(expected)

    end

  end

  describe "when parsing Chef's configuration fails" do

    let(:exception_message) do
      <<~MESSAGE
        You have an error in your config file /Users/ddeleo/.chef/config.rb (Chef::Exceptions::ConfigurationError)

        Mixlib::Config::UnknownConfigOptionError: Cannot set unsupported config value foo.
          /Users/person/.chef/config.rb:50:in `from_string'
        Relevant file content:
         49: chefcli.generator_cookbook "~/.chef/code_generator"
         50: chefcli.foo "bar"
         51:

      MESSAGE
    end

    let(:exception) { Chef::Exceptions::ConfigurationError.new(exception_message) }

    before do
      allow(command_instance).to receive(:run).and_raise(exception)
    end

    it "exits non-zero" do
      expect(run_command([])).to eq(1)
    end

    it "prints the exception message to stderr" do
      run_command([])
      expect(stderr).to include(exception_message)
    end

  end

end