@Test
  void testExponentialBackoffDelay() throws InterruptedException {
    long acceptableDelay = 25; // Delay to forgive if tests run long.

    long startTime = System.nanoTime();
    handler.imposeBackoffExponentialDelay(1, "TEST FAILURE: 1");
    long elapsedTime = (System.nanoTime() - startTime) / 1000000;
    assert (elapsedTime >= 49) : elapsedTime;
    assertTrue(elapsedTime < 50 + acceptableDelay);

    startTime = System.nanoTime();
    handler.imposeBackoffExponentialDelay(2, "TEST FAILURE: 2");
    elapsedTime = (System.nanoTime() - startTime) / 1000000;
    assert (elapsedTime >= 199) : elapsedTime;
    assertTrue(elapsedTime < 200 + acceptableDelay);

    startTime = System.nanoTime();
    handler.imposeBackoffExponentialDelay(3, "TEST FAILURE: 3");
    elapsedTime = (System.nanoTime() - startTime) / 1000000;
    assert (elapsedTime >= 449) : elapsedTime;
    assertTrue(elapsedTime < 450 + acceptableDelay);

    startTime = System.nanoTime();
    handler.imposeBackoffExponentialDelay(4, "TEST FAILURE: 4");
    elapsedTime = (System.nanoTime() - startTime) / 1000000;
    assert (elapsedTime >= 799) : elapsedTime;
    assertTrue(elapsedTime < 800 + acceptableDelay * 2);

    startTime = System.nanoTime();
    handler.imposeBackoffExponentialDelay(5, "TEST FAILURE: 5");
    elapsedTime = (System.nanoTime() - startTime) / 1000000;
    assert (elapsedTime >= 1249) : elapsedTime;
    assertTrue(elapsedTime < 1250 + acceptableDelay * 2);
  }
  @Test
  void testIncrementsFailureCount()
      throws InterruptedException, IOException, SecurityException, NoSuchMethodException {
    HttpCommand command = createCommand();
    HttpResponse response = new HttpResponse(400, null, null);

    handler.shouldRetryRequest(command, response);
    assertEquals(command.getFailureCount(), 1);

    handler.shouldRetryRequest(command, response);
    assertEquals(command.getFailureCount(), 2);

    handler.shouldRetryRequest(command, response);
    assertEquals(command.getFailureCount(), 3);
  }
  @Test
  void testClosesInputStream()
      throws InterruptedException, IOException, SecurityException, NoSuchMethodException {
    HttpCommand command = createCommand();

    HttpResponse response = new HttpResponse(400, null, null);

    InputStream inputStream =
        new InputStream() {
          boolean isOpen = true;

          @Override
          public void close() {
            this.isOpen = false;
          }

          int count = 1;

          @Override
          public int read() throws IOException {
            if (this.isOpen) return (count > -1) ? count-- : -1;
            else return -1;
          }

          @Override
          public int available() throws IOException {
            if (this.isOpen) return count;
            else return 0;
          }
        };
    response.setPayload(Payloads.newInputStreamPayload(inputStream));
    response.getPayload().getContentMetadata().setContentLength(1l);
    assertEquals(response.getPayload().getInput().available(), 1);
    assertEquals(response.getPayload().getInput().read(), 1);

    handler.shouldRetryRequest(command, response);

    assertEquals(response.getPayload().getInput().available(), 0);
    assertEquals(response.getPayload().getInput().read(), -1);
  }
  @Test
  void testDisallowsExcessiveRetries()
      throws InterruptedException, IOException, SecurityException, NoSuchMethodException {
    HttpCommand command = createCommand();
    HttpResponse response = new HttpResponse(400, null, null);

    assertEquals(handler.shouldRetryRequest(command, response), true); // Failure 1

    assertEquals(handler.shouldRetryRequest(command, response), true); // Failure 2

    assertEquals(handler.shouldRetryRequest(command, response), true); // Failure 3

    assertEquals(handler.shouldRetryRequest(command, response), true); // Failure 4

    assertEquals(handler.shouldRetryRequest(command, response), true); // Failure 5

    assertEquals(handler.shouldRetryRequest(command, response), false); // Failure 6
  }