require 'spec_helper'

describe Mongo::Server::AppMetadata do

  let(:app_metadata) do
    described_class.new(cluster.options)
  end

  let(:cluster) do
    authorized_client.cluster
  end

  describe '#initialize' do

    context 'when the cluster has an app name option set' do

      let(:client) do
        authorized_client.with(app_name: :app_metadata_test)
      end

      let(:cluster) do
        client.cluster
      end

      after do
        client.close(true)
      end

      it 'sets the app name' do
        expect(app_metadata.send(:full_client_document)[:application][:name]).to eq('app_metadata_test')
      end

      context 'when the app name exceeds the max length of 128' do

        let(:client) do
          authorized_client.with(app_name: "\u3042"*43)
        end

        let(:cluster) do
          client.cluster
        end

        after do
          client.close(true)
        end

        it 'raises an error' do
          expect {
            app_metadata.send(:validate!)
          }.to raise_exception(Mongo::Error::InvalidApplicationName)
        end
      end
    end

    context 'when the cluster does not have an app name option set' do

      it 'does not set the app name' do
        expect(app_metadata.send(:full_client_document)[:application]).to be(nil)
      end
    end

    context 'when the client document exceeds the max of 512 bytes' do

      context 'when the os.type length is too long' do

        before do
          allow(app_metadata).to receive(:type).and_return('x'*500)
        end

        it 'truncates the document' do
          expect(app_metadata.send(:ismaster_bytes)).to be_a(String)
        end
      end

      context 'when the os.name length is too long' do

        before do
          allow(app_metadata).to receive(:name).and_return('x'*500)
        end

        it 'truncates the document' do
          expect(app_metadata.send(:ismaster_bytes)).to be_a(String)
        end
      end

      context 'when the os.architecture length is too long' do

        before do
          allow(app_metadata).to receive(:architecture).and_return('x'*500)
        end

        it 'truncates the document' do
          expect(app_metadata.send(:ismaster_bytes)).to be_a(String)
        end
      end

      context 'when the platform length is too long' do

        before do
          allow(app_metadata).to receive(:platform).and_return('x'*500)
        end

        it 'truncates the document to be just an ismaster command' do
          expect(app_metadata.send(:ismaster_bytes)).to be_a(String)
        end
      end

      context 'when the driver info is too long' do
        require_no_compression

        before do
          allow(app_metadata).to receive(:driver_doc).and_return('x'*500)
        end

        it 'truncates the document to be just an ismaster command and the compressors' do
          # Because we sometimes request that the server provide a list of valid auth mechanisms for
          # the user, we need to conditionally add the length of that metadata to the expected
          # length of the isMaster document.
          sasl_supported_mechs_size = 0
          sasl_supported_mechs = app_metadata.instance_variable_get(:@request_auth_mech)

          if sasl_supported_mechs
            sasl_supported_mechs_size += 1                               # length of BSON type byte
            sasl_supported_mechs_size += 'saslSupportedMechs'.length + 1        # length of BSON key
            sasl_supported_mechs_size += 4                               # length of BSON string length
            sasl_supported_mechs_size += sasl_supported_mechs.length + 1 # length of BSON string
          end

          expect(app_metadata.ismaster_bytes.length).to eq(Mongo::Server::Monitor::Connection::ISMASTER_BYTES.length + sasl_supported_mechs_size + 26)
        end
      end
    end
  end

  describe '#document' do
    context 'when user is given and auth_mech is not given' do
      let(:app_metadata) do
        described_class.new(user: 'foo')
      end

      it 'includes saslSupportedMechs' do
        expect(app_metadata.send(:document)[:saslSupportedMechs]).to eq('admin.foo')
      end
    end
  end
end
