#
# 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'
require 'chef-dk/policyfile_services/install'

describe ChefDK::PolicyfileServices::Install do

  let(:working_dir) do
    path = File.join(tempdir, "policyfile_services_test_working_dir")
    Dir.mkdir(path)
    path
  end

  let(:policyfile_rb_explicit_name) { nil }

  let(:policyfile_rb_name) { policyfile_rb_explicit_name || "Policyfile.rb" }

  let(:policyfile_lock_name) { "Policyfile.lock.json" }

  let(:policyfile_rb_path) { File.join(working_dir, policyfile_rb_name) }

  let(:policyfile_lock_path) { File.join(working_dir, policyfile_lock_name) }

  let(:local_cookbooks_root) do
    File.join(fixtures_path, "local_path_cookbooks")
  end

  let(:policyfile_content) do
    <<-E
name 'install-example'

run_list 'local-cookbook'

cookbook 'local-cookbook', path: '#{local_cookbooks_root}/local-cookbook'
E
  end

  let(:ui) { TestHelpers::TestUI.new }

  let(:install_service) { described_class.new(policyfile: policyfile_rb_name, ui: ui, root_dir: working_dir) }

  let(:storage_config) do
    ChefDK::Policyfile::StorageConfig.new( cache_path: nil, relative_paths_root: local_cookbooks_root )
  end

  def result_policyfile_lock
    expect(File).to exist(policyfile_lock_path)
    content = IO.read(policyfile_lock_path)
    lock_data = FFI_Yajl::Parser.parse(content)
    ChefDK::PolicyfileLock.new(storage_config).build_from_lock_data(lock_data)
  end

  context "when no Policyfile is present or specified" do

    it "errors out" do
      expect { install_service.run }.to raise_error(ChefDK::PolicyfileNotFound, "Policyfile not found at path #{policyfile_rb_path}")
    end

  end

  context "when a Policyfile exists" do

    before do
      File.open(policyfile_rb_path, "w+") { |f| f.print(policyfile_content) }
    end

    it "infers that the Policyfile.rb is located at $CWD/Policyfile.rb" do
      expect(install_service.policyfile_path).to eq(policyfile_rb_path)
    end

    it "reads the policyfile from disk" do
      expect(install_service.policyfile_content).to eq(policyfile_content)
    end

    context "and the policyfile has an error" do

      let(:policyfile_content) { 'raise "borkbork"' }

      it "errors out and creates no lockfile" do
        expect { install_service.run }.to raise_error(ChefDK::PolicyfileInstallError)
        expect(File).to_not exist(policyfile_lock_path)
      end

    end

    context "and no lockfile exists" do

      it "solves the Policyfile demands, installs cookbooks, emits a lockfile" do
        install_service.run
        generated_lock = result_policyfile_lock
        expect(generated_lock.name).to eq('install-example')
        expect(generated_lock.cookbook_locks).to have_key("local-cookbook")
      end

    end

    context "and a lockfile exists" do

      before do
        install_service.dup.run
      end

      it "reads the policyfile lock from disk" do
        lock = install_service.policyfile_lock
        expect(lock).to be_an_instance_of(ChefDK::PolicyfileLock)
        expect(lock.name).to eq('install-example')
        expect(lock.cookbook_locks).to have_key("local-cookbook")
      end

      it "ensures that cookbooks are installed" do
        expect(install_service.policyfile_lock).to receive(:install_cookbooks).and_call_original
        install_service.run
      end

      describe "when an error occurs during the install" do

        before do
          expect(install_service.policyfile_lock).to receive(:install_cookbooks).and_raise("some error")
        end

        it "raises a PolicyfileInstallError" do
          expect { install_service.run }.to raise_error(ChefDK::PolicyfileInstallError)
        end


      end

      context "and the Policyfile has updated dependendencies" do

        # For very first iteration, we won't tackle this case if it's hard
        it "Conservatively updates deps, recomputes lock, and installs"

      end

    end

    context "and an explicit Policyfile name is given" do

      let(:policyfile_rb_explicit_name) { "MyPolicy.rb" }

      let(:policyfile_lock_name) { "MyPolicy.lock.json" }

      it "infers that the Policyfile.rb is located at $CWD/$POLICYFILE_NAME" do
        expect(install_service.policyfile_path).to eq(policyfile_rb_path)
      end

      it "reads the policyfile from disk" do
        expect(install_service.policyfile_content).to eq(policyfile_content)
      end

    end
  end

end