#
# Author:: Adam Edwards (<adamed@getchef.com>)
# Copyright:: Copyright (c) 2014 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'

describe Chef::GuardInterpreter::ResourceGuardInterpreter do
  let(:node) do
    node = Chef::Node.new

    node.default["kernel"] = Hash.new
    node.default["kernel"][:machine] = :x86_64.to_s
    node
  end

  let(:run_context) { Chef::RunContext.new(node, nil, nil) }

  let(:parent_resource) do
    parent_resource = Chef::Resource.new("powershell_unit_test", run_context)
    allow(parent_resource).to receive(:run_action)
    allow(parent_resource).to receive(:updated).and_return(true)
    parent_resource
  end

  let(:guard_interpreter) { Chef::GuardInterpreter::ResourceGuardInterpreter.new(parent_resource, "echo hi", nil) }

  describe "get_interpreter_resource" do
    it "allows the guard interpreter to be set to Chef::Resource::Script" do
      parent_resource.guard_interpreter(:script)
      expect { guard_interpreter }.not_to raise_error
    end

    it "allows the guard interpreter to be set to Chef::Resource::PowershellScript derived indirectly from Chef::Resource::Script" do
      parent_resource.guard_interpreter(:powershell_script)
      expect { guard_interpreter }.not_to raise_error
    end

    it "raises an exception if guard_interpreter is set to a resource not derived from Chef::Resource::Script" do
      parent_resource.guard_interpreter(:file)
      expect { guard_interpreter }.to raise_error(ArgumentError, 'Specified guard interpreter class Chef::Resource::File must be a kind of Chef::Resource::Execute resource')
    end

    context "when the resource cannot be found for the platform" do
      before do
        expect(Chef::Resource).to receive(:resource_for_node).with(:foobar, node).and_return(nil)
      end

      it "raises an exception" do
        parent_resource.guard_interpreter(:foobar)
        expect { guard_interpreter }.to raise_error(ArgumentError, 'Specified guard_interpreter resource foobar unknown for this platform')
      end
    end

    it "fails when parent_resource is nil" do
      expect { Chef::GuardInterpreter::ResourceGuardInterpreter.new(nil, "echo hi", nil) }.to raise_error(ArgumentError, /Node for guard resource parent must not be nil/)
    end

  end

  describe "#evaluate" do
    let(:guard_interpreter) { Chef::GuardInterpreter::ResourceGuardInterpreter.new(parent_resource, "echo hi", {}) }
    let(:parent_resource) do
      parent_resource = Chef::Resource.new("execute resource", run_context)
      parent_resource.guard_interpreter(:execute)
      parent_resource
    end

    it "successfully evaluates the resource" do
      expect(guard_interpreter.evaluate).to eq(true)
    end

    describe "script command opts switch" do
      let(:command_opts) { {} }
      let(:guard_interpreter) { Chef::GuardInterpreter::ResourceGuardInterpreter.new(parent_resource, "exit 0", command_opts) }

      context "resource is a Script" do
        context "and guard_interpreter is a :script" do
          let(:parent_resource) do
            parent_resource = Chef::Resource::Script.new("resource", run_context)
            # Ruby scripts are cross platform to both Linux and Windows
            parent_resource.guard_interpreter(:ruby)
            parent_resource
          end

          let(:shell_out) {
            instance_double(Mixlib::ShellOut, :live_stream => true, :run_command => true, :error! => nil)
          }

          before do
            # TODO for some reason Windows is failing on executing a ruby script
            expect(Mixlib::ShellOut).to receive(:new) do |*args|
              expect(args[0]).to match(/^"ruby"/)
              shell_out
            end
          end

          it "merges to :code" do
            expect(command_opts).to receive(:merge).with({:code => "exit 0"}).and_call_original
            expect(guard_interpreter.evaluate).to eq(true)
          end
        end

        context "and guard_interpreter is :execute" do
          let(:parent_resource) do
            parent_resource = Chef::Resource::Script.new("resource", run_context)
            parent_resource.guard_interpreter(:execute)
            parent_resource
          end

          it "merges to :code" do
            expect(command_opts).to receive(:merge).with({:command => "exit 0"}).and_call_original
            expect(guard_interpreter.evaluate).to eq(true)
          end
        end
      end

      context "resource is not a Script" do
        let(:parent_resource) do
          parent_resource = Chef::Resource::Execute.new("resource", run_context)
          parent_resource.guard_interpreter(:execute)
          parent_resource
        end

        it "merges to :command" do
          expect(command_opts).to receive(:merge).with({:command => "exit 0"}).and_call_original
          expect(guard_interpreter.evaluate).to eq(true)
        end
      end

    end
  end
end

