@Test
  public void testMetadataRecoveryClientFailure() throws Exception {
    final int NUM_ERRORS = 5; // Every n-th page is bad
    class ExceptionCallback implements RecoveryNetworkClientCallback {
      int numCalls = 0;
      public void onListBlobs(ReplicaMetadataRequestInfo request) {
        numCalls += 1;
        /**
         * If we want a constant number of errors, then the frequency needs to adjusted based on the num of iterations.
         * In this test, we have 100 blobs fetched as 34 pages with 3 blobs per page at max.
         * If every 5-th page throws a retriable error, then we need 40 iterations to fetch all blobs.
         *          * 34 + (34/5) = 40
         * Now, among those 40 iterations, we want 5 erroneous pages. And that is 40/6, hence +1 below.
         * In other words, for a constant number of errors, the frequency with which they occur changes depending on
         * the number of calls or iterations.
         */
        if (numCalls % (NUM_ERRORS + 1) == 0) {
          throw new RuntimeException("Exception on Azure Storage list-blobs call");
        }
      }
    }
    int numGoodPages = (NUM_BLOBS/AZURE_BLOB_STORAGE_MAX_RESULTS_PER_PAGE) +
        (NUM_BLOBS % AZURE_BLOB_STORAGE_MAX_RESULTS_PER_PAGE == 0 ? 0 : 1);
    int numBadPages = numGoodPages/NUM_ERRORS;
    List<MessageInfo> metadataList = fetchPaginatedMetadata(new ExceptionCallback(),
        numGoodPages + numBadPages);
    // Assert we recovered all blobIds intact
    metadataList.forEach(messageInfo -> {
      assertEquals(String.format("Azure blob metadata does not match that in local disk"),
          azureBlobs.get(messageInfo.getStoreKey().getID()), messageInfo);
    });
  }
Tests that all blob metadata is downloaded in spite of errors.
@throws Exception
ambry-cloud/src/test/java/com/github/ambry/cloud/RecoveryNetworkClientTest.java