require 'spec_helper'
require 'cloud_controller/diego/buildpack_entry_generator'

module VCAP::CloudController
  module Diego
    RSpec.describe BuildpackEntryGenerator do
      subject(:buildpack_entry_generator) { BuildpackEntryGenerator.new(blobstore_url_generator, Lifecycles::BUILDPACK) }

      let(:admin_buildpack_download_url) { 'http://admin-buildpack.example.com' }
      let(:app_package_download_url) { 'http://app-package.example.com' }
      let(:build_artifacts_cache_download_uri) { 'http://buildpack-artifacts-cache.example.com' }

      let(:blobstore_url_generator) { double('fake url generator') }
      let(:stack) { Stack.make }
      let(:stack2) { Stack.make }

      let!(:java_buildpack) do
        VCAP::CloudController::Buildpack.create(name: 'java', stack: stack.name, key: 'java-buildpack-key', position: 1, sha256_checksum: 'checksum')
      end
      let!(:ruby_buildpack) do
        VCAP::CloudController::Buildpack.create(name: 'ruby', stack: stack.name, key: 'ruby-buildpack-key', position: 2, sha256_checksum: 'checksum')
      end
      let!(:ruby_buildpack_other_stack) do
        VCAP::CloudController::Buildpack.create(name: 'ruby', stack: stack2.name, key: 'ruby-buildpack-stack2-key', position: 3, sha256_checksum: 'checksum')
      end

      before do
        allow(blobstore_url_generator).to receive_messages(app_package_download_url: app_package_download_url, admin_buildpack_download_url: admin_buildpack_download_url,
                                                           buildpack_cache_download_url: build_artifacts_cache_download_uri)

        allow(EM).to receive(:add_timer)
        allow(EM).to receive(:defer).and_yield
      end

      describe '#buildpack_entries' do
        let(:v3_app) { AppModel.make }
        let(:package) { PackageModel.make(app_guid: v3_app.guid) }
        let(:buildpack_info) { BuildpackInfo.new(buildpack, VCAP::CloudController::Buildpack.find(name: buildpack)) }
        let(:buildpack_infos) { [buildpack_info] }

        context 'when the user has requested a custom and admin buildpack' do
          let(:custom_buildpack_info) { BuildpackInfo.new('http://example.com/my_buildpack_url.zip', nil) }
          let(:admin_buildpack_info) { BuildpackInfo.new('java', VCAP::CloudController::Buildpack.find(name: 'java')) }
          let(:buildpack_infos) { [custom_buildpack_info, admin_buildpack_info] }

          it 'returns both buildpacks for the stack' do
            expect(buildpack_entry_generator.buildpack_entries(buildpack_infos, stack.name)).to eq([
              { name: 'custom', key: 'http://example.com/my_buildpack_url.zip', url: 'http://example.com/my_buildpack_url.zip', skip_detect: true },
              { name: 'java', key: 'java-buildpack-key', url: admin_buildpack_download_url, skip_detect: true, sha256: 'checksum' }
            ])
          end
        end

        context 'when the user has requested a custom buildpack' do
          context 'when the buildpack_uri ends with .zip' do
            let(:buildpack) { 'http://example.com/my_buildpack_url.zip' }

            it "uses the buildpack_uri and name it 'custom', and use the url as the key" do
              expect(buildpack_entry_generator.buildpack_entries(buildpack_infos, stack.name)).to eq([
                { name: 'custom', key: 'http://example.com/my_buildpack_url.zip', url: 'http://example.com/my_buildpack_url.zip', skip_detect: true }
              ])
            end
          end

          context 'when the buildpack_uri does not end with .zip' do
            let(:buildpack) { 'http://example.com/my_buildpack_url' }

            it "uses the buildpack_uri and name it 'custom', and use the url as the key" do
              expect(buildpack_entry_generator.buildpack_entries(buildpack_infos, stack.name)).to eq([
                { name: 'custom', key: 'http://example.com/my_buildpack_url', url: 'http://example.com/my_buildpack_url', skip_detect: true }
              ])
            end
          end
        end

        context 'when the user has requested an admin buildpack' do
          let(:buildpack) { 'java' }

          it 'uses that buildpack' do
            expect(buildpack_entry_generator.buildpack_entries(buildpack_infos, stack.name)).to eq([
              { name: 'java', key: 'java-buildpack-key', url: admin_buildpack_download_url, skip_detect: true, sha256: 'checksum' }
            ])
          end

          context 'when the buildpack is disabled' do
            before do
              java_buildpack.update(enabled: false)
            end

            it 'fails fast with a clear error' do
              expect { buildpack_entry_generator.buildpack_entries(buildpack_infos, stack.name) }.to raise_error(/Unsupported buildpack type/)
            end
          end
        end

        context 'when the user has not requested a buildpack' do
          let(:buildpack_infos) { [] }

          it 'uses the list of admin buildpacks for the specified stack' do
            expect(buildpack_entry_generator.buildpack_entries(buildpack_infos, stack.name)).to eq([
              { name: 'java', key: 'java-buildpack-key', url: admin_buildpack_download_url, sha256: 'checksum', skip_detect: false },
              { name: 'ruby', key: 'ruby-buildpack-key', url: admin_buildpack_download_url, sha256: 'checksum', skip_detect: false }
            ])
          end

          it 'uses the list of all admin buildpacks if no stack is specified' do
            expect(buildpack_entry_generator.buildpack_entries(buildpack_infos, nil)).to eq([
              { name: 'java', key: 'java-buildpack-key', url: admin_buildpack_download_url, sha256: 'checksum', skip_detect: false },
              { name: 'ruby', key: 'ruby-buildpack-key', url: admin_buildpack_download_url, sha256: 'checksum', skip_detect: false },
              { name: 'ruby', key: 'ruby-buildpack-stack2-key', url: admin_buildpack_download_url, sha256: 'checksum', skip_detect: false }
            ])
          end
        end

        context 'when an invalid buildpack type is returned for some reason' do
          let(:buildpack) { '???' }

          it 'fails fast with a clear error' do
            expect { buildpack_entry_generator.buildpack_entries(buildpack_infos, stack.name) }.to raise_error(/Unsupported buildpack type/)
          end
        end
      end
    end
  end
end
