require "spec_helper"

module Omnibus
  describe Licensing do
    let(:license) { nil }
    let(:license_file_path) { nil }
    let(:license_file) { nil }
    let(:zlib_version_override) { nil }

    let(:install_dir) { File.join(tmp_path, "install_dir") }
    let(:software_project_dir) { File.join(tmp_path, "software_project_dir") }

    let(:expected_project_license_path) { "LICENSE" }
    let(:expected_project_license) { "Unspecified" }
    let(:expected_project_license_content) { "" }

    before do
      FileUtils.mkdir_p(install_dir)
      FileUtils.mkdir_p(software_project_dir)

      allow_any_instance_of(Software).to receive(:project_dir).and_return(software_project_dir)
      %w{LICENSE NOTICE APACHE}.each do |file|
        File.open(File.join(software_project_dir, file), "w+") do |f|
          f.puts "This file is #{file}."
        end
      end
    end

    let(:project) do
      Project.new.tap do |project|
        project.name("test-project")
        project.install_dir(install_dir)
        project.license(license) unless license.nil?
        project.license_file_path(license_file_path) unless license_file_path.nil?
        project.license_file(license_file) unless license_file.nil?
        project.build_version("1.2.3")
        if zlib_version_override
          project.override :zlib, version: zlib_version_override
        end
      end
    end

    let(:private_code) do
      Software.new(project, "private_code.rb").evaluate do
        name "private_code"
        default_version "1.7.2"
        skip_transitive_dependency_licensing true
      end
    end

    let(:zlib) do
      Software.new(project, "zlib.rb").evaluate do
        name "zlib"
        default_version "1.7.2"
        skip_transitive_dependency_licensing true

        license "Zlib"
        license_file "LICENSE"

        version "1.8.0" do
          license "Apache-2.0"
          license_file "APACHE"
        end
      end
    end

    let(:snoopy) do
      Software.new(project, "snoopy.rb").evaluate do
        name "snoopy"
        default_version "1.0.0"
        skip_transitive_dependency_licensing true

        license "GPL v2"
        license_file "http://dev.perl.org/licenses/artistic.html"
        license_file "NOTICE"
      end
    end

    let(:preparation) do
      Software.new(project, "preparation.rb").evaluate do
        name "preparation"
        default_version "1.0.0"
        license :project_license
        skip_transitive_dependency_licensing true
      end
    end

    let(:software_with_warnings) { nil }

    let(:softwares) do
      s = [preparation, snoopy, zlib, private_code]
      s << software_with_warnings if software_with_warnings
      s
    end

    def create_licenses
      softwares.each { |s| project.library.component_added(s) }

      Licensing.create_incrementally(project) do |licensing|
        yield licensing if block_given?

        project.softwares.each do |software|
          licensing.execute_post_build(software)
        end
      end
    end

    describe "prepare step" do

      let(:licenses_dir) { File.join(install_dir, "LICENSES") }
      let(:dot_gitkeep) { File.join(licenses_dir, ".gitkeep") }
      let(:cache_dir) { File.join(install_dir, "license-cache") }
      let(:cache_dot_gitkeep) { File.join(cache_dir, ".gitkeep") }

      it "creates a LICENSES dir with a .gitkeep file inside the install directory" do
        Licensing.new(project).prepare
        expect(File).to exist(licenses_dir)
        expect(File).to exist(dot_gitkeep)
      end

      it "creates a licenses-cache dir with a .gitkeep file inside the install directory" do
        Licensing.new(project).prepare
        expect(File).to exist(cache_dir)
        expect(File).to exist(cache_dot_gitkeep)
      end

    end

    describe "without license definitions in the project" do
      it "warns for missing project license" do
        output = capture_logging { create_licenses }
        expect(output).to include("Project 'test-project' does not contain licensing information.")
      end
    end

    describe "with license definitions in the project" do
      let(:license) { "Custom Chef" }
      let(:license_file_path) { "CHEF.LICENSE" }
      let(:license_file) { "CUSTOM_CHEF" }

      let(:expected_project_license_path) { license_file_path }
      let(:expected_project_license) { license }
      let(:expected_project_license_content) { "Chef Custom License" }

      before do
        File.open(File.join(Config.project_root, license_file), "w+") do |f|
          f.puts "Chef Custom License is awesome."
        end
      end

      after do
        FileUtils.rm_rf(license_file)
      end

      it "warns for non-standard project license" do
        output = capture_logging { create_licenses }
        expect(output).to include("Project 'test-project' is using 'Custom Chef' which is not one of the standard licenses")
      end
    end

    describe "with a local license file that does not exist" do
      let(:software_with_warnings) do
        Software.new(project, "problematic.rb").evaluate do
          name "problematic"
          default_version "0.10.2"
          license_file "NOT_EXISTS"
          skip_transitive_dependency_licensing true
        end
      end

      it "should log a warning for the missing file" do
        output = capture_logging { create_licenses }
        expect(output).to match /License file (.*)NOT_EXISTS' does not exist for software 'problematic'./
      end
    end

    describe "with a remote license file that does not exist" do
      before do
        Omnibus::Config.fetcher_retries(1)
      end

      let(:software_with_warnings) do
        Software.new(project, "problematic.rb").evaluate do
          name "problematic"
          default_version "0.10.2"
          license_file "https://downloads.chef.io/LICENSE"
          skip_transitive_dependency_licensing true
        end
      end

      it "should log a warning for the missing file" do
        output = capture_logging { create_licenses }
        expect(output).to match(/Retrying failed download/)
        expect(output).to match(%r{Can not download license file 'https://downloads.chef.io/LICENSE' for software 'problematic'.})
      end
    end

    describe "with a software with no license files" do
      let(:software_with_warnings) do
        Software.new(project, "problematic.rb").evaluate do
          name "problematic"
          default_version "0.10.2"
          license "Zlib"
          skip_transitive_dependency_licensing true
        end
      end

      it "should log a warning for the missing file pointers" do
        output = capture_logging { create_licenses }
        expect(output).to include("Software 'problematic' does not point to any license files.")
      end
    end

    describe "with a project with no license files" do
      let(:license) { "Zlib" }

      let(:expected_project_license_path) { "LICENSE" }
      let(:expected_project_license) { license }
      let(:expected_project_license_content) { "" }

      it "warns for missing license files" do
        output = capture_logging { create_licenses }
        expect(output).to include("Project 'test-project' does not point to a license file.")
      end
    end

    describe "with :fatal_licensing_warnings set and without license definitions in the project" do
      before do
        Omnibus::Config.fatal_licensing_warnings(true)
      end

      it "fails the omnibus build" do
        expect { create_licenses }.to raise_error(Omnibus::LicensingError, /Project 'test-project' does not contain licensing information.\s{1,}Software 'private_code' does not contain licensing information./)
      end
    end

    describe "when all software is setting skip_transitive_dependency_licensing " do
      # This is achieved by the default values of the let() parameters

      it "does not collect transitive licensing info for any software" do
        softwares.each { |s| project.library.component_added(s) }
        create_licenses do |licensing|
          expect(licensing).not_to receive(:collect_transitive_dependency_licenses_for)
        end
      end
    end

    describe "when a project has transitive dependencies" do
      let(:license) { "Custom Chef" }
      let(:license_file_path) { "CHEF.LICENSE" }
      let(:license_file) { "CUSTOM_CHEF" }

      let(:expected_project_license_path) { license_file_path }

      let(:softwares) { [zlib] }

      before do
        File.open(File.join(Config.project_root, license_file), "w+") do |f|
          f.puts "Chef Custom License is awesome."
        end
      end

      after do
        FileUtils.rm_rf(license_file)
      end

      let(:zlib) do
        Software.new(project, "zlib.rb").evaluate do
          name "zlib"
          default_version "1.7.2"

          license "Zlib"
          license_file "LICENSE"
        end
      end

      let(:snoopy) do
        Software.new(project, "snoopy.rb").evaluate do
          name "snoopy"
          default_version "1.0.0"

          license "GPL v2"
          license_file "NOTICE"
        end
      end
    end
  end
end
