private void doPostGetHeadUpdateDeleteUndeleteTest(Account toPostAccount, Container toPostContainer, String serviceId,
      boolean isPrivate, Account expectedAccount, Container expectedContainer) throws Exception {
    ByteBuffer content = ByteBuffer.wrap(TestUtils.getRandomBytes(CONTENT_LENGTH));
    String contentType = "application/octet-stream";
    String ownerId = "postGetHeadDeleteOwnerID";
    JSONObject headers = new JSONObject();
    String accountNameInPost = toPostAccount != null ? toPostAccount.getName() : null;
    String containerNameInPost = toPostContainer != null ? toPostContainer.getName() : null;
    setAmbryHeadersForPut(headers, TTL_SECS, isPrivate, serviceId, contentType, ownerId, accountNameInPost,
        containerNameInPost, null);
    Map<String, String> userMetadata = new HashMap<>();
    userMetadata.put(RestUtils.Headers.USER_META_DATA_HEADER_PREFIX + "key1", "value1");
    userMetadata.put(RestUtils.Headers.USER_META_DATA_HEADER_PREFIX + "key2", "value2");
    RestUtilsTest.setUserMetadataHeaders(headers, userMetadata);
    String blobId = postBlobAndVerify(headers, content, expectedAccount, expectedContainer);

    headers.put(RestUtils.Headers.BLOB_SIZE, (long) CONTENT_LENGTH);
    getBlobAndVerify(blobId, null, null, headers, content, expectedAccount, expectedContainer);
    getBlobAndVerify(blobId, null, GetOption.None, headers, content, expectedAccount, expectedContainer);
    getHeadAndVerify(blobId, null, null, headers, expectedAccount, expectedContainer);
    getHeadAndVerify(blobId, null, GetOption.None, headers, expectedAccount, expectedContainer);

    ByteRange range = ByteRanges.fromStartOffset(ThreadLocalRandom.current().nextLong(CONTENT_LENGTH));
    getBlobAndVerify(blobId, range, null, headers, content, expectedAccount, expectedContainer);
    getHeadAndVerify(blobId, range, null, headers, expectedAccount, expectedContainer);

    range = ByteRanges.fromLastNBytes(ThreadLocalRandom.current().nextLong(CONTENT_LENGTH + 1));
    getBlobAndVerify(blobId, range, null, headers, content, expectedAccount, expectedContainer);
    getHeadAndVerify(blobId, range, null, headers, expectedAccount, expectedContainer);

    long random1 = ThreadLocalRandom.current().nextLong(CONTENT_LENGTH);
    long random2 = ThreadLocalRandom.current().nextLong(CONTENT_LENGTH);
    range = ByteRanges.fromOffsetRange(Math.min(random1, random2), Math.max(random1, random2));
    getBlobAndVerify(blobId, range, null, headers, content, expectedAccount, expectedContainer);
    getHeadAndVerify(blobId, range, null, headers, expectedAccount, expectedContainer);

    getNotModifiedBlobAndVerify(blobId, null);
    getUserMetadataAndVerify(blobId, null, headers);
    getBlobInfoAndVerify(blobId, null, headers, expectedAccount, expectedContainer);
    updateBlobTtlAndVerify(blobId, headers, expectedAccount, expectedContainer, false);

    // Before delete, let's undelete it, should fail
    undeleteBlobAndVerify(blobId, headers, content, expectedAccount, expectedContainer, false,
        RestServiceErrorCode.Conflict);
    deleteBlobAndVerify(blobId, headers, content, expectedAccount, expectedContainer, false);
    // After delete, let's undelete it, should succeed
    undeleteBlobAndVerify(blobId, headers, content, expectedAccount, expectedContainer, false, null);
  }
Tests blob POST, GET, HEAD, TTL Update and DELETE operations on the given {@code container}.
@param toPostAccount the {@link Account} to use in post headers. Can be {@code null} if only using service ID.
@param toPostContainer the {@link Container} to use in post headers. Can be {@code null} if only using service ID.
@param serviceId the serviceId to use for the POST
@param isPrivate the isPrivate flag to pass as part of the POST
@param expectedAccount the {@link Account} details that are eventually expected to be populated.
@param expectedContainer the {@link Container} details that are eventually expected to be populated.
@throws Exception
ambry-frontend/src/test/java/com/github/ambry/frontend/FrontendRestRequestServiceTest.java