#
# Copyright:: 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 "stringio"
require "chef-cli/chef_runner"

describe ChefCLI::ChefRunner do

  let(:stdout_io) { StringIO.new }

  let(:stderr_io) { StringIO.new }

  let(:default_cookbook_path) do
    File.expand_path("chef-runner-cookbooks", fixtures_path)
  end

  let(:run_list) { [ "recipe[test_cookbook::recipe_one]", "recipe[test_cookbook::recipe_two]" ] }

  subject(:chef_runner) do
    r = ChefCLI::ChefRunner.new(default_cookbook_path, run_list)
    allow(r).to receive(:stdout).and_return(stdout_io)
    allow(r).to receive(:stderr).and_return(stderr_io)
    r
  end

  it "sets up Chef::Config" do
    chef_runner.configure
    expect(Chef::Config.solo_legacy_mode).to be true
    expect(Chef::Config.cookbook_path).to eq(default_cookbook_path)
    expect(Chef::Config.color).to be true
    expect(Chef::Config.diff_disabled).to be true
    expect(Chef::Config.file_staging_uses_destdir).to be true
  end

  it "disables atomic file updates on Windows" do
    allow(Chef::Platform).to receive(:windows?) { true }
    chef_runner.configure
    expect(Chef::Config.file_atomic_update).to be false
  end

  it "configures a formatter for the chef run" do
    expect(chef_runner.event_dispatcher).to be_a(Chef::EventDispatch::Dispatcher)

    subscribers = chef_runner.event_dispatcher.subscribers

    expect(subscribers.size).to eq(1)
    expect(subscribers.first).to be_a(ChefCLI::QuieterDocFormatter)
  end

  it "extends the recipe DSL with ChefCLI's extensions" do
    expect(Chef::DSL::Recipe.included_modules).to include(ChefCLI::RecipeDSLExt)
  end

  it "detects the platform with ohai" do
    expect(chef_runner.ohai).to be_a(Ohai::System)
    expect(chef_runner.ohai.data["platform"]).to_not be_nil
    expect(chef_runner.ohai.data["platform_version"]).to_not be_nil
  end

  it "sets up chef policy" do
    chef_runner.configure
    expect(chef_runner.policy.node.run_list).to eq(run_list)
  end

  it "runs a chef converge" do
    chef_runner.converge
    expect(test_state[:loaded_recipes]).to eq(%w{recipe_one recipe_two})
    expect(test_state[:converged_recipes]).to eq(%w{recipe_one recipe_two})
  end

  context "when policyfile options are set in the workstation config" do

    before do
      Chef::Config.use_policyfile true
      Chef::Config.policy_name "workstation"
      Chef::Config.policy_group "test"

      # chef-client ignores `deployment_group` unless
      # `policy_document_native_api` is set to false
      Chef::Config.deployment_group "workstation-test"
      Chef::Config.policy_document_native_api false
    end

    it "unsets the options" do
      chef_runner.configure

      expect(Chef::Config.use_policyfile).to be(false)
      expect(Chef::Config.policy_name).to be_nil
      expect(Chef::Config.policy_group).to be_nil
      expect(Chef::Config.deployment_group).to be_nil
    end

    it "converges successfully" do
      expect { chef_runner.converge }.to_not raise_error
    end

  end

  context "when the embedded chef run fails" do

    let(:embedded_runner) { instance_double("Chef::Runner") }

    before do
      allow(Chef::Runner).to receive(:new).and_return(embedded_runner)
      allow(embedded_runner).to receive(:converge).and_raise("oops")
    end

    it "wraps the exception in a ChefConvergeError" do
      expect { chef_runner.converge }.to raise_error(ChefCLI::ChefConvergeError)
    end

  end

  context "when cookbook_path is relative" do

    let(:default_cookbook_path) { "~/heres_some_cookbooks" }

    it "expands the path" do
      expect(chef_runner.cookbook_path).to eq(File.expand_path(default_cookbook_path))
    end

  end

end