private Message buildMessage(String blobIdString, Class clazz, boolean hasEncryption, Long crcInMsgInfo,
        boolean divergeInfoFromData, boolean brokenMetadataChunk, String... dataChunkIds)
        throws IOException, MessageFormatException {
      buildRandom = new Random(randomStaticSeed);

      //If there are datachunks, it's a metadata blob.
      //If not, its a data blob
      InputStream blobStream;
      long blobStreamSize;
      long blobPropertiesSize;
      ByteBuffer byteBuffer;
      BlobType blobType;
      if (dataChunkIds == null) {
        blobStreamSize = BLOB_STREAM_SIZE;
        blobPropertiesSize = blobStreamSize;
        blobStream = createBlobStream();
        blobType = BlobType.DataBlob;
      } else {
        byteBuffer = createMetadataByteBuffer(dataChunkIds);
        blobStreamSize = byteBuffer.remaining();
        if (brokenMetadataChunk) {
          blobPropertiesSize = blobStreamSize;
        } else {
          blobPropertiesSize = COMPOSITE_BLOB_SIZE;
        }
        blobStream = new ByteBufferInputStream(byteBuffer);
        blobType = BlobType.MetadataBlob;
      }

      BlobId blobId = createBlobId(blobIdString);
      ByteBuffer blobEncryptionKey = randomByteBuffer(BLOB_ENCRYPTION_KEY_SIZE);
      if (!hasEncryption) {
        blobEncryptionKey = null;
      }
      ByteBuffer userMetaData = randomByteBuffer(USER_META_DATA_SIZE);
      InputStream inputStream;
      int inputStreamSize;
      MessageInfo messageInfo;
      BlobProperties blobProperties =
          new BlobProperties(blobPropertiesSize, "serviceId", "ownerId", "contentType", false, 0, 0,
              blobId.getAccountId(), blobId.getContainerId(), hasEncryption, null, "gzip", "filename", null);
      if (clazz != null) {
        MessageFormatInputStream messageFormatInputStream;
        if (clazz == PutMessageFormatInputStream.class) {
          messageFormatInputStream =
              new PutMessageFormatInputStream(blobId, blobEncryptionKey, blobProperties, userMetaData, blobStream,
                  blobStreamSize, blobType);
        } else if (clazz == DeleteMessageFormatInputStream.class) {
          messageFormatInputStream =
              new DeleteMessageFormatInputStream(blobId, blobId.getAccountId(), blobId.getContainerId(), 0);
        } else {//if (clazz == PutMessageFormatBlobV1InputStream.class) {
          messageFormatInputStream =
              new PutMessageFormatBlobV1InputStream(blobId, blobProperties, userMetaData, blobStream, blobStreamSize,
                  blobType);
        }
        inputStreamSize = (int) messageFormatInputStream.getSize();
        inputStream = messageFormatInputStream;
      } else {
        inputStream = new ByteArrayInputStream(randomByteArray(100));
        inputStreamSize = 100;
      }

      boolean ttlUpdated = false;
      long expiryTimeMs =
          Utils.addSecondsToEpochTime(blobProperties.getCreationTimeInMs(), blobProperties.getTimeToLiveInSeconds());
      if (divergeInfoFromData) {
        ttlUpdated = true;
        expiryTimeMs = Utils.Infinite_Time;
      }
      messageInfo =
          new MessageInfo(blobId, inputStreamSize, false, ttlUpdated, expiryTimeMs, crcInMsgInfo, blobId.getAccountId(),
              blobId.getContainerId(), blobProperties.getCreationTimeInMs());
      return new Message(messageInfo, inputStream);
    }
Constructs the input and expected
@param pair the pair of blob ids (old, new)
@param clazz the put message input stream class to use
@param divergeInfoFromData if {@code true}, changes some fields in the info to be different from what is in the
                           data
@throws IOException
@throws MessageFormatException
ambry-replication/src/test/java/com/github/ambry/replication/BlobIdTransformerTest.java