require 'spec_helper'

module CloudController::Presenters::V2
  RSpec.describe ProcessModelPresenter do
    let(:app_presenter) { ProcessModelPresenter.new }
    let(:controller) { double(:controller, url_for_guid: 'controller-url') }
    let(:opts) { {} }
    let(:depth) { 'depth' }
    let(:parents) { 'parents' }
    let(:orphans) { 'orphans' }
    let(:relations_presenter) { instance_double(RelationsPresenter, to_hash: relations_hash) }
    let(:relations_hash) { { 'relationship_key' => 'relationship_value' } }

    describe '#entity_hash' do
      before do
        allow(RelationsPresenter).to receive(:new).and_return(relations_presenter)
        VCAP::CloudController::Buildpack.make(name: 'schmuby')
        process.app.lifecycle_data.update(
          buildpacks:
        )
        process.desired_droplet.update(
          buildpack_receipt_detect_output: 'detected buildpack',
          buildpack_receipt_buildpack_guid: 'i am a buildpack guid'
        )
        VCAP::CloudController::DropletModel.make(app: process.app, package: process.latest_package, error_description: 'because')
      end

      let(:space) { VCAP::CloudController::Space.make }
      let(:stack) { VCAP::CloudController::Stack.make }
      let(:process) do
        VCAP::CloudController::ProcessModelFactory.make(
          name: 'utako',
          space: space,
          stack: stack,
          environment_json: { UNICORNS: 'RAINBOWS' },
          memory: 1024,
          disk_quota: 1024,
          ports: [1234],
          state: 'STOPPED',
          command: 'start',
          enable_ssh: true,
          diego: diego,
          created_at: Time.now,
          updated_at: Time.now,
          revision: revision
        )
      end
      let(:diego) { true }
      let(:buildpack) { 'https://github.com/custombuildpack' }
      let(:buildpacks) { [buildpack] }
      let(:revision) do
        VCAP::CloudController::RevisionModel.make(
          environment_variables: {}
        )
      end

      it 'returns the app entity and associated urls' do
        expected_entity_hash = {
          'name' => 'utako',
          'production' => anything,
          'space_guid' => space.guid,
          'stack_guid' => stack.guid,
          'buildpack' => 'https://github.com/custombuildpack',
          'detected_buildpack' => 'detected buildpack',
          'detected_buildpack_guid' => 'i am a buildpack guid',
          'environment_json' => { 'redacted_message' => '[PRIVATE DATA HIDDEN]' },
          'memory' => 1024,
          'instances' => 1,
          'disk_quota' => 1024,
          'log_rate_limit' => 1_048_576,
          'state' => 'STOPPED',
          'version' => process.version,
          'command' => 'start',
          'console' => anything,
          'debug' => anything,
          'staging_task_id' => process.latest_build.guid,
          'package_state' => 'PENDING',
          'health_check_type' => 'port',
          'health_check_timeout' => nil,
          'health_check_http_endpoint' => nil,
          'staging_failed_reason' => anything,
          'staging_failed_description' => 'because',
          'diego' => true,
          'docker_image' => nil,
          'docker_credentials' => {
            'username' => nil,
            'password' => nil
          },
          'package_updated_at' => anything,
          'detected_start_command' => anything,
          'enable_ssh' => true,
          'ports' => [1234],
          'relationship_key' => 'relationship_value'
        }

        actual_entity_hash = app_presenter.entity_hash(controller, process, opts, depth, parents, orphans)

        expect(actual_entity_hash).to be_a_response_like(expected_entity_hash)
        expect(relations_presenter).to have_received(:to_hash).with(controller, process, opts, depth, parents, orphans)
      end

      it 'displays the app guid in its metadata hash' do
        v2_process_hash = app_presenter.to_hash(controller, process, opts, depth, parents, orphans)

        expect(v2_process_hash['metadata']).to eq(
          {
            'guid' => process.app.guid,
            'url' => 'controller-url',
            'created_at' => process.created_at,
            'updated_at' => process.updated_at
          }
        )

        expect(controller).to have_received(:url_for_guid).with(process.app.guid, process)
      end

      describe 'nil associated objects' do
        context 'when an associated object is not present' do
          before do
            parent_app = process.app
            process.destroy
            parent_app.builds.map(&:destroy)
            parent_app.packages.map(&:destroy)
            parent_app.update(droplet_guid: nil)
            parent_app.droplets.map(&:destroy)
            parent_app.buildpack_lifecycle_data.buildpack_lifecycle_buildpacks.map(&:destroy)
            parent_app.destroy
          end

          it 'returns nil' do
            actual_entity_hash = app_presenter.entity_hash(controller, process, opts, depth, parents, orphans)
            expect(actual_entity_hash).to be_nil
          end
        end
      end

      describe 'buildpacks' do
        context 'with a custom buildpack' do
          it 'displays the correct url' do
            actual_entity_hash = app_presenter.entity_hash(controller, process, opts, depth, parents, orphans)

            expect(actual_entity_hash['buildpack']).to eq 'https://github.com/custombuildpack'
            expect(relations_presenter).to have_received(:to_hash).with(controller, process, opts, depth, parents, orphans)
          end

          it 'calls out to the UrlSecretObfuscator' do
            allow(CloudController::UrlSecretObfuscator).to receive(:obfuscate)

            app_presenter.entity_hash(controller, process, opts, depth, parents, orphans)

            expect(CloudController::UrlSecretObfuscator).to have_received(:obfuscate).exactly :once
          end
        end

        context 'with an admin buildpack' do
          let(:buildpack) { 'schmuby' }

          it 'displays the correct url' do
            actual_entity_hash = app_presenter.entity_hash(controller, process, opts, depth, parents, orphans)

            expect(actual_entity_hash['buildpack']).to eq 'schmuby'
            expect(relations_presenter).to have_received(:to_hash).with(controller, process, opts, depth, parents, orphans)
          end
        end
      end

      describe 'docker' do
        context 'with no credentials' do
          before do
            VCAP::CloudController::PackageModel.make(:docker, app: process.app, docker_image: 'someimage')
            process.reload
          end

          it 'displays the docker_image' do
            actual_entity_hash = app_presenter.entity_hash(controller, process, opts, depth, parents, orphans)

            expect(actual_entity_hash['docker_image']).to eq('someimage')
            expect(actual_entity_hash['docker_credentials']['username']).to be_nil
            expect(actual_entity_hash['docker_credentials']['password']).to be_nil
            expect(relations_presenter).to have_received(:to_hash).with(controller, process, opts, depth, parents, orphans)
          end
        end

        context 'with credentials' do
          before do
            VCAP::CloudController::PackageModel.make(:docker, app: process.app, docker_image: 'someimage', docker_username: 'user', docker_password: 'secret')
            process.reload
          end

          it 'displays the docker image and username and redacts the password' do
            actual_entity_hash = app_presenter.entity_hash(controller, process, opts, depth, parents, orphans)

            expect(actual_entity_hash['docker_image']).to eq('someimage')
            expect(actual_entity_hash['docker_credentials']['username']).to eq('user')
            expect(actual_entity_hash['docker_credentials']['password']).to eq('***')
            expect(relations_presenter).to have_received(:to_hash).with(controller, process, opts, depth, parents, orphans)
          end
        end
      end

      context 'redacting' do
        context 'when the user is an admin' do
          before { set_current_user_as_admin }

          it 'displays environment_json' do
            actual_entity_hash = app_presenter.entity_hash(controller, process, opts, depth, parents, orphans)

            expect(actual_entity_hash['environment_json']).to eq({ 'UNICORNS' => 'RAINBOWS' })
          end
        end

        context 'when the user is an admin-read-only' do
          before { set_current_user_as_admin_read_only }

          it 'displays environment_json' do
            actual_entity_hash = app_presenter.entity_hash(controller, process, opts, depth, parents, orphans)

            expect(actual_entity_hash['environment_json']).to eq({ 'UNICORNS' => 'RAINBOWS' })
          end
        end

        context 'when the user is a space developer' do
          before { allow(process.space).to receive(:has_developer?).and_return(true) }

          it 'displays the environment json' do
            actual_entity_hash = app_presenter.entity_hash(controller, process, opts, depth, parents, orphans)

            expect(actual_entity_hash['environment_json']).to eq({ 'UNICORNS' => 'RAINBOWS' })
          end
        end

        context 'when the user is any other role' do
          before { allow(process.space).to receive(:has_developer?).and_return(false) }

          it 'redacts the environment json' do
            actual_entity_hash = app_presenter.entity_hash(controller, process, opts, depth, parents, orphans)

            expect(actual_entity_hash['environment_json']).to eq({ 'redacted_message' => '[PRIVATE DATA HIDDEN]' })
          end
        end
      end
    end
  end
end
