@Test
  public void testS3RegularPutObject() throws Exception {
    int chunkNumber = 9;
    // composite blob with "chunkNumber" of data chunks
    maxPutChunkSize = PUT_CONTENT_SIZE / chunkNumber;
    if (PUT_CONTENT_SIZE % maxPutChunkSize != 0) {
      maxPutChunkSize++;
    }
    setRouter();
    setOperationParams();

    // regular PutObject
    RestRequest request = createRestRequestForPutOperation();
    request.setArg(RestUtils.InternalKeys.REQUEST_PATH, RequestPath.parse(request, null, CLUSTER_NAME));
    PutBlobOptions putBlobOptions = new PutBlobOptionsBuilder().restRequest(request).build();
    String blobID = router.putBlob(putBlobProperties, putUserMetadata, putChannel, putBlobOptions)
        .get(AWAIT_TIMEOUT_MS, TimeUnit.MILLISECONDS);

    // Get Data Chunk List
    GetBlobResult getChunkResult = router.getBlob(blobID,
        new GetBlobOptionsBuilder().operationType(GetBlobOptions.OperationType.BlobChunkIds).build(), null, null).get();
    List<StoreKey> chunkIds = getChunkResult.getBlobChunkIds();

    // verify the content, one data chunk after another
    int chunkSize = maxPutChunkSize;
    for (int i = 0; i < chunkIds.size(); i++) {
      BlobId chunkId = (BlobId) chunkIds.get(i);
      GetBlobResult chunkResult =
          router.getBlob(chunkId.getID(), new GetBlobOptionsBuilder().build(), null, null).get();
      // last chunk
      if (i == chunkNumber - 1) {
        chunkSize = PUT_CONTENT_SIZE - maxPutChunkSize * i;
      }

      // verify the reserved metadata chunk id is null
      BlobProperties blobProperties = chunkResult.getBlobInfo().getBlobProperties();
      Assert.assertEquals(blobProperties.getReservedMetadataBlobId(), null);

      // verify data content
      RetainingAsyncWritableChannel retainingAsyncWritableChannel = new RetainingAsyncWritableChannel();
      chunkResult.getBlobDataChannel().readInto(retainingAsyncWritableChannel, null).get();
      InputStream input = retainingAsyncWritableChannel.consumeContentAsInputStream();
      retainingAsyncWritableChannel.close();
      Assert.assertEquals(chunkSize, input.available());
      byte[] readContent = Utils.readBytesFromStream(input, chunkSize);
      input.close();

      byte[] originSlice = Arrays.copyOfRange(putContent, i * maxPutChunkSize, i * maxPutChunkSize + chunkSize);
      Assert.assertArrayEquals(originSlice, readContent);
    }
  }
Test the S3 regular PutObject. It returns the Ambry blob id.
No matter if router enables "reserved metadata chunk" feature, S3 regular upload doesn't reserve metadata chunk.
@throws Exception
ambry-router/src/test/java/com/github/ambry/router/S3NonBlockingRouterTest.java