protected void doTtlUpdateTest(int numChunks) throws Exception {
    Assert.assertEquals("This test works only if the number of chunks is a perfect divisor of PUT_CONTENT_SIZE", 0,
        PUT_CONTENT_SIZE % numChunks);
    maxPutChunkSize = PUT_CONTENT_SIZE / numChunks;
    String updateServiceId = "update-service";
    TtlUpdateNotificationSystem notificationSystem = new TtlUpdateNotificationSystem();
    setRouter(getNonBlockingRouterProperties("DC1"), new MockServerLayout(mockClusterMap), notificationSystem);
    setOperationParams();
    Assert.assertFalse("The original ttl should not be infinite for this test to work",
        putBlobProperties.getTimeToLiveInSeconds() == Utils.Infinite_Time);
    String blobId = router.putBlob(putBlobProperties, putUserMetadata, putChannel, new PutBlobOptionsBuilder().build())
        .get(AWAIT_TIMEOUT_MS, TimeUnit.MILLISECONDS);
    assertTtl(router, Collections.singleton(blobId), TTL_SECS);
    router.updateBlobTtl(blobId, updateServiceId, Utils.Infinite_Time).get(AWAIT_TIMEOUT_MS, TimeUnit.MILLISECONDS);
    // if more than one chunk is created, also account for metadata blob
    notificationSystem.checkNotifications(numChunks == 1 ? 1 : numChunks + 1, updateServiceId, Utils.Infinite_Time);
    assertTtl(router, Collections.singleton(blobId), Utils.Infinite_Time);
    Assert.assertEquals("All operations should have completed", 0, router.getOperationsCount());
    if (numChunks == 1) {
      Assert.assertEquals("Get should have been skipped", 1, routerMetrics.skippedGetBlobCount.getCount());
    } else {
      Assert.assertEquals("Get should NOT have been skipped", 0, routerMetrics.skippedGetBlobCount.getCount());
    }
    router.close();
    // check that ttl update won't work after router close
    CompletableFuture<Void> future = router.updateBlobTtl(blobId, updateServiceId, Utils.Infinite_Time);
    Assert.assertTrue(future.isDone());
    try {
      future.join();
      Assert.fail("Expecting a failure");
    } catch (Exception e) {
      RouterException routerException = (RouterException) Utils.extractFutureExceptionCause(e);
      Assert.assertEquals(routerException.getErrorCode(), RouterErrorCode.RouterClosed);
    }
  }
Does the TTL update test by putting a blob, checking its TTL, updating TTL and then rechecking the TTL again.
@param numChunks the number of chunks required when the blob is put. Has to divide {@link #PUT_CONTENT_SIZE}
                 perfectly for test to work.
@throws Exception
ambry-router/src/test/java/com/github/ambry/router/NonBlockingRouterTestBase.java