cloudfoundry/cloud_controller_ng

View on GitHub
spec/unit/presenters/v3/droplet_presenter_spec.rb

Summary

Maintainability
A
2 hrs
Test Coverage
require 'spec_helper'
require 'presenters/v3/droplet_presenter'

module VCAP::CloudController::Presenters::V3
  RSpec.describe DropletPresenter do
    let(:droplet) do
      VCAP::CloudController::DropletModel.make(
        :buildpack,
        state: VCAP::CloudController::DropletModel::STAGED_STATE,
        error_id: 'FAILED',
        error_description: 'things went all sorts of bad',
        process_types: { 'web' => 'npm start', 'worker' => 'start worker' },
        execution_metadata: 'black-box-string',
        package_guid: 'abcdefabcdef12345',
        droplet_hash: 'droplet-sha1-checksum',
        sha256_checksum: 'droplet-sha256-checksum'
      )
    end

    describe '#to_hash' do
      let(:result) { DropletPresenter.new(droplet).to_hash }
      let(:buildpack) { 'the-happiest-buildpack' }
      let(:buildpack_receipt_buildpack) { 'the-happiest-buildpack' }
      let(:buildpack2) { 'shaq' }

      context 'buildpack lifecycle' do
        before do
          droplet.lifecycle_data.buildpacks = [buildpack, buildpack2]
          droplet.lifecycle_data.stack = 'the-happiest-stack'
          droplet.buildpack_receipt_buildpack = buildpack_receipt_buildpack
          droplet.buildpack_receipt_detect_output = 'the-happiest-buildpack-detect-output'
          droplet.save
        end

        it 'presents the droplet as a hash' do
          links = {
            self: { href: "#{link_prefix}/v3/droplets/#{droplet.guid}" },
            package: { href: "#{link_prefix}/v3/packages/#{droplet.package_guid}" },
            app: { href: "#{link_prefix}/v3/apps/#{droplet.app_guid}" },
            download: { href: "#{link_prefix}/v3/droplets/#{droplet.guid}/download" },
            assign_current_droplet: { href: "#{link_prefix}/v3/apps/#{droplet.app_guid}/relationships/current_droplet", method: 'PATCH' }
          }

          expect(result[:guid]).to eq(droplet.guid)
          expect(result[:state]).to eq('STAGED')
          expect(result[:error]).to eq('FAILED - things went all sorts of bad')

          expect(result[:lifecycle][:type]).to eq('buildpack')
          expect(result[:lifecycle][:data]).to eq({})

          expect(result[:checksum]).to eq(type: 'sha256', value: 'droplet-sha256-checksum')
          expect(result[:stack]).to eq('the-happiest-stack')
          expect(result[:buildpacks]).to contain_exactly({ name: 'the-happiest-buildpack',
                                                           detect_output: 'the-happiest-buildpack-detect-output',
                                                           buildpack_name: nil,
                                                           version: nil }, { name: 'shaq',
                                                                             detect_output: nil,
                                                                             buildpack_name: nil,
                                                                             version: nil })

          expect(result[:created_at]).to be_a(Time)
          expect(result[:updated_at]).to be_a(Time)
          expect(result[:links]).to eq(links)
          expect(result[:relationships][:app][:data][:guid]).to eq(droplet.app_guid)
        end

        it 'does not redacts the process_types and execution_metadata by default' do
          expect(result[:process_types]).to eq({ 'web' => 'npm start', 'worker' => 'start worker' })
          expect(result[:execution_metadata]).to eq('black-box-string')
        end

        context 'when buildpack contains username and password' do
          let(:buildpack) { 'https://amelia:meow@neopets.com' }
          let(:buildpack_receipt_buildpack) { 'https://amelia:meow@neopets.com' }

          it 'obfuscates the username and password' do
            expect(result[:buildpacks]).to contain_exactly({
                                                             name: 'shaq',
                                                             detect_output: nil,
                                                             buildpack_name: nil,
                                                             version: nil
                                                           }, {
                                                             name: 'https://***:***@neopets.com',
                                                             detect_output: 'the-happiest-buildpack-detect-output',
                                                             buildpack_name: nil,
                                                             version: nil
                                                           })
          end
        end

        context 'when show_secrets is false' do
          let(:result) { DropletPresenter.new(droplet, show_secrets: false).to_hash }

          it 'redacts the process_types and execution_metadata' do
            expect(result[:process_types]).to eq({ 'redacted_message' => '[PRIVATE DATA HIDDEN]' })
            expect(result[:execution_metadata]).to eq('[PRIVATE DATA HIDDEN]')
          end
        end

        context 'when droplet has no checksum' do
          before { droplet.update(droplet_hash: nil, sha256_checksum: nil) }

          it 'sets checksum to nil' do
            expect(result[:checksum]).to be_nil
          end
        end

        context 'when the droplet does not have a sha256 checksum calculated' do
          before { droplet.update(sha256_checksum: nil) }

          it 'presents the sha1 checksum' do
            expect(result[:checksum]).to eq(type: 'sha1', value: 'droplet-sha1-checksum')
          end
        end

        describe 'links' do
          context 'when there is no package guid' do
            let(:droplet) { VCAP::CloudController::DropletModel.make(:buildpack, package_guid: nil) }

            it 'links to nil' do
              expect(result[:links][:package]).to be_nil
            end
          end
        end
      end

      context 'buildpack lifecycle with versions' do
        let(:buildpack1_name) { 'rosanna' }
        let(:buildpack1_other_name) { 'toto' }
        let(:buildpack1_version) { '1.9.82' }
        let!(:buildpack1) { VCAP::CloudController::Buildpack.make(name: buildpack1_name, sha256_checksum: 'mammoth') }
        let(:buildpack2_name) { 'chris-cross' }
        let(:buildpack2_other_name) { 'sailing' }
        let(:buildpack2_version) { '1.9.79' }
        let!(:buildpack2) { VCAP::CloudController::Buildpack.make(name: buildpack2_name, sha256_checksum: 'languid') }
        let(:buildpack_receipt_buildpack) { buildpack2_name }
        let(:buildpack_receipt_detect_output) { 'black cow' }

        let(:lifecycle_buildpacks) do
          [
            {
              name: buildpack1_other_name,
              version: buildpack1_version,
              key: buildpack1.key
            },
            {
              name: buildpack2_other_name,
              key: buildpack2.key
            }
          ]
        end

        before do
          droplet.lifecycle_data.buildpacks = lifecycle_buildpacks
          droplet.lifecycle_data.stack = 'the-happiest-stack'
          droplet.buildpack_receipt_buildpack = buildpack_receipt_buildpack
          droplet.buildpack_receipt_detect_output = buildpack_receipt_detect_output
          droplet.save
        end

        it 'presents the droplet as a hash and presents new buildpack info' do
          links = {
            self: { href: "#{link_prefix}/v3/droplets/#{droplet.guid}" },
            package: { href: "#{link_prefix}/v3/packages/#{droplet.package_guid}" },
            app: { href: "#{link_prefix}/v3/apps/#{droplet.app_guid}" },
            download: { href: "#{link_prefix}/v3/droplets/#{droplet.guid}/download" },
            assign_current_droplet: { href: "#{link_prefix}/v3/apps/#{droplet.app_guid}/relationships/current_droplet", method: 'PATCH' }
          }

          expect(result[:guid]).to eq(droplet.guid)
          expect(result[:state]).to eq('STAGED')
          expect(result[:error]).to eq('FAILED - things went all sorts of bad')

          expect(result[:lifecycle][:type]).to eq('buildpack')
          expect(result[:lifecycle][:data]).to eq({})

          expect(result[:checksum]).to eq(type: 'sha256', value: 'droplet-sha256-checksum')
          expect(result[:stack]).to eq('the-happiest-stack')
          expect(result[:buildpacks]).to contain_exactly({ name: 'rosanna',
                                                           detect_output: nil,
                                                           version: '1.9.82',
                                                           buildpack_name: 'toto' }, { name: 'chris-cross',
                                                                                       detect_output: 'black cow',
                                                                                       buildpack_name: 'sailing',
                                                                                       version: nil })

          expect(result[:created_at]).to be_a(Time)
          expect(result[:updated_at]).to be_a(Time)
          expect(result[:links]).to eq(links)
          expect(result[:relationships][:app][:data][:guid]).to eq(droplet.app_guid)
        end
      end

      context 'docker lifecycle' do
        let(:droplet) do
          VCAP::CloudController::DropletModel.make(
            :docker,
            state: VCAP::CloudController::DropletModel::STAGED_STATE
          )
        end

        before do
          droplet.docker_receipt_image = 'test-image'
          droplet.save
        end

        it 'presents the docker image and lifecycle data' do
          expect(result[:image]).to eq('test-image')
          expect(result[:lifecycle][:type]).to eq('docker')
          expect(result[:lifecycle][:data]).to eq({})
          expect(result[:links][:download]).to be_nil
        end

        context 'when show_secrets is false' do
          let(:result) { DropletPresenter.new(droplet, show_secrets: false).to_hash }

          it 'redacts the process_types and execution_metadata' do
            expect(result[:process_types]).to eq({ 'redacted_message' => '[PRIVATE DATA HIDDEN]' })
            expect(result[:execution_metadata]).to eq('[PRIVATE DATA HIDDEN]')
          end
        end
      end

      context 'cnb lifecycle' do
        let(:droplet) do
          VCAP::CloudController::DropletModel.make(
            :cnb,
            state: VCAP::CloudController::DropletModel::STAGED_STATE
          )
        end
        let(:first_buildpack) { 'docker://first-buildpack:latest' }
        let(:second_buildpack) { 'https://second-buildpack.io' }

        let(:lifecycle_buildpacks) do
          [
            first_buildpack,
            second_buildpack
          ]
        end

        before do
          droplet.lifecycle_data.buildpacks = lifecycle_buildpacks
          droplet.lifecycle_data.stack = 'the-happiest-stack'
          droplet.save
        end

        it 'presents cnb lifecycle data' do
          expect(result[:lifecycle][:type]).to eq('cnb')
          expect(result[:buildpacks]).to contain_exactly(
            { buildpack_name: nil, detect_output: nil, name: 'docker://first-buildpack:latest', version: nil },
            { buildpack_name: nil, detect_output: nil, name: 'https://second-buildpack.io', version: nil }
          )
        end
      end

      context 'when there are labels and annotations for the droplet' do
        let!(:release_label) do
          VCAP::CloudController::DropletLabelModel.make(
            key_name: 'release',
            value: 'stable',
            resource_guid: droplet.guid
          )
        end

        let!(:potato_label) do
          VCAP::CloudController::DropletLabelModel.make(
            key_prefix: 'maine.gov',
            key_name: 'potato',
            value: 'mashed',
            resource_guid: droplet.guid
          )
        end

        let!(:annotation) do
          VCAP::CloudController::DropletAnnotationModel.make(
            resource_guid: droplet.guid,
            key_name: 'contacts',
            value: 'Bill tel(1111111) email(bill@fixme), Bob tel(222222) pager(3333333#555) email(bob@fixme)'
          )
        end

        it 'includes the metadata on the presented droplet' do
          expect(result[:guid]).to eq(droplet.guid)
          expect(result[:metadata][:labels]).to eq('release' => 'stable', 'maine.gov/potato' => 'mashed')
          expect(result[:metadata][:annotations]).to eq('contacts' => 'Bill tel(1111111) email(bill@fixme), Bob tel(222222) pager(3333333#555) email(bob@fixme)')
        end
      end

      context 'when the droplet is AWAITING_UPLOAD' do
        before do
          droplet.state = VCAP::CloudController::DropletModel::AWAITING_UPLOAD_STATE
          droplet.save
        end

        it 'adds the upload link' do
          links = {
            self: { href: "#{link_prefix}/v3/droplets/#{droplet.guid}" },
            package: { href: "#{link_prefix}/v3/packages/#{droplet.package_guid}" },
            app: { href: "#{link_prefix}/v3/apps/#{droplet.app_guid}" },
            assign_current_droplet: { href: "#{link_prefix}/v3/apps/#{droplet.app_guid}/relationships/current_droplet", method: 'PATCH' },
            upload: { href: "#{link_prefix}/v3/droplets/#{droplet.guid}/upload", method: 'POST' }
          }

          expect(result[:guid]).to eq(droplet.guid)
          expect(result[:state]).to eq('AWAITING_UPLOAD')
          expect(result[:created_at]).to be_a(Time)
          expect(result[:updated_at]).to be_a(Time)
          expect(result[:links]).to eq(links)
        end
      end

      context 'when the droplet is STAGED' do
        before do
          droplet.state = VCAP::CloudController::DropletModel::STAGED_STATE
          droplet.save
        end

        it 'adds the upload link' do
          links = {
            self: { href: "#{link_prefix}/v3/droplets/#{droplet.guid}" },
            package: { href: "#{link_prefix}/v3/packages/#{droplet.package_guid}" },
            app: { href: "#{link_prefix}/v3/apps/#{droplet.app_guid}" },
            assign_current_droplet: { href: "#{link_prefix}/v3/apps/#{droplet.app_guid}/relationships/current_droplet", method: 'PATCH' },
            download: { href: "#{link_prefix}/v3/droplets/#{droplet.guid}/download" }
          }

          expect(result[:guid]).to eq(droplet.guid)
          expect(result[:state]).to eq('STAGED')
          expect(result[:created_at]).to be_a(Time)
          expect(result[:updated_at]).to be_a(Time)
          expect(result[:links]).to eq(links)
        end
      end
    end
  end
end