private void stitchBlobAndVerify(Account account, Container container, List<String> signedChunkIds,
      byte[] fullContentArray, long stitchedBlobSize) throws Exception {
    // stitchBlob
    HttpHeaders stitchHeaders = new DefaultHttpHeaders();
    setAmbryHeadersForPut(stitchHeaders, TTL_SECS, !container.isCacheable(), "stitcher", "video/mp4",
        "stitchedUploadTest", account.getName(), container.getName());
    HttpRequest httpRequest = buildRequest(HttpMethod.POST, Operations.STITCH, stitchHeaders,
        ByteBuffer.wrap(StitchRequestSerDe.toJson(signedChunkIds).toString().getBytes(StandardCharsets.UTF_8)));
    ResponseParts responseParts = nettyClient.sendRequest(httpRequest, null, null).get();
    String stitchedBlobId = verifyPostAndReturnBlobId(responseParts, stitchedBlobSize, true);
    HttpHeaders expectedGetHeaders = new DefaultHttpHeaders().add(stitchHeaders);
    // Test different request types on stitched blob ID
    // (getBlobInfo, getBlob, getBlob w/ range, head, updateBlobTtl, deleteBlob)
    expectedGetHeaders.add(RestUtils.Headers.BLOB_SIZE, fullContentArray.length);
    expectedGetHeaders.set(RestUtils.Headers.LIFE_VERSION, "0");
    getBlobInfoAndVerify(stitchedBlobId, GetOption.None, expectedGetHeaders, !container.isCacheable(),
        account.getName(), container.getName(), null, container);
    List<ByteRange> ranges = new ArrayList<>();
    ranges.add(null);
    ranges.add(ByteRanges.fromLastNBytes(ThreadLocalRandom.current().nextLong(fullContentArray.length + 1)));
    ranges.add(ByteRanges.fromStartOffset(ThreadLocalRandom.current().nextLong(fullContentArray.length)));
    long random1 = ThreadLocalRandom.current().nextLong(fullContentArray.length);
    long random2 = ThreadLocalRandom.current().nextLong(fullContentArray.length);
    ranges.add(ByteRanges.fromOffsetRange(Math.min(random1, random2), Math.max(random1, random2)));
    for (ByteRange range : ranges) {
      getBlobAndVerify(stitchedBlobId, range, GetOption.None, false, expectedGetHeaders, !container.isCacheable(),
          ByteBuffer.wrap(fullContentArray), account.getName(), container.getName(), container);
      getHeadAndVerify(stitchedBlobId, range, GetOption.None, expectedGetHeaders, !container.isCacheable(),
          account.getName(), container.getName());
    }
    updateBlobTtlAndVerify(stitchedBlobId, expectedGetHeaders, !container.isCacheable(), account.getName(),
        container.getName(), null, container);
    // Delete stitched blob.
    deleteBlobAndVerify(stitchedBlobId);
    verifyOperationsAfterDelete(stitchedBlobId, expectedGetHeaders, !container.isCacheable(), account.getName(),
        container.getName(), ByteBuffer.wrap(fullContentArray), null, false, container);
  }
Test the stitched upload flow for a specified chunk size and number of chunks.
@param account the {@link Account} to upload into.
@param container the {@link Container} to upload into.
@param signedChunkIds the list of signed chunk IDs to stitch together.
@param fullContentArray the content to compare the stitched blob against.
@throws Exception
ambry-frontend/src/integration-test/java/com/github/ambry/frontend/FrontendIntegrationTest.java