@Test
  @Configuration(
      "{"
          + "  \"limit\" : 1000,"
          + "  \"direction\" : \"download\","
          + "  \"granularity\" : \"Api\","
          + "  \"period\" : \"Minute\""
          + "}")
  @BackEndApi(DownloadTestBackEndApi.class)
  public void testDownloadLimitNoHeaderConfig() throws Throwable {
    PolicyTestRequest request =
        PolicyTestRequest.build(PolicyTestRequestType.GET, "/some/resource");
    request.header("X-Payload-Size", "389");

    PolicyTestResponse response = send(request);
    Assert.assertNotNull(response.body());
    Assert.assertEquals("1000", response.header("X-TransferQuota-Remaining"));
    Assert.assertEquals("1000", response.header("X-TransferQuota-Limit"));

    send(request);
    send(request);

    // Now if we try it one more time, we'll get a failure!
    try {
      send(request);
      Assert.fail("Expected a policy failure!");
    } catch (PolicyFailureError e) {
      PolicyFailure failure = e.getFailure();
      Assert.assertEquals(PolicyFailureCodes.BYTE_QUOTA_EXCEEDED, failure.getFailureCode());
      Assert.assertEquals("Transfer quota exceeded.", failure.getMessage());
      Assert.assertEquals(429, failure.getResponseCode());
    }
  }
  /**
   * This utility method:
   *
   * <ul>
   *   <li>sets up a valid session
   *   <li>sends a request with a cookie containing the session ID
   *   <li>expects a 200 OK status code
   *   <li>fetches the (potentially updated) session
   * </ul>
   *
   * @param originalSession the session before the request is made
   * @param addCookie whether to add the session cookie to the request
   * @return the updated session
   * @throws Throwable
   */
  private Session makeSessionRequest(Session originalSession, boolean addCookie) throws Throwable {
    // wait for the clock to tick to ensure we can test the updated expiration time
    Thread.sleep(100);

    final PolicyTestRequest request = PolicyTestRequest.build(PolicyTestRequestType.GET, RESOURCE);

    if (addCookie) {
      // send request with cookie
      request.header(Constants.HEADER_COOKIE, CommonTestUtil.buildCookieHeader(originalSession));
    }

    final PolicyTestResponse response = send(request);
    assertEquals(HttpURLConnection.HTTP_OK, response.code());

    // content should be returned
    assertNotNull(response.header(Constants.HEADER_CONTENT_LENGTH));

    // verify the session data in the shared state has changed
    final Session updatedSession = CommonTestUtil.fetchSession(originalSession.getSessionId());
    assertNotNull(updatedSession);
    assertEquals(originalSession.getSessionId(), updatedSession.getSessionId());

    // these should not change from request to request
    assertEquals(originalSession.getAbsoluteExpiry(), updatedSession.getAbsoluteExpiry());
    assertEquals(originalSession.getValidityPeriod(), updatedSession.getValidityPeriod());

    return updatedSession;
  }
  /**
   * Expects that a ConfigurationException is thrown as the policy is not configured correctly.
   *
   * @throws Throwable
   */
  @Test(expected = InvalidConfigurationException.class)
  @Configuration(EMPTY_CONFIG)
  @BackEndApi(RequiresAuthHeaderBackEndApi.class)
  public void testAuthenticatedRequestExceptionEmptyConfig() throws Throwable {
    // test data - session expires in 60s
    final Session originalSession = CommonTestUtil.insertTestSession(60, true);

    // send request with cookie
    final PolicyTestRequest request = PolicyTestRequest.build(PolicyTestRequestType.GET, RESOURCE);
    request.header(Constants.HEADER_COOKIE, CommonTestUtil.buildCookieHeader(originalSession));

    send(request);
    fail(InvalidConfigurationException.class + " expected");
  }
  /**
   * Expect that a policy with the configuration of 'ValidationRequired' rejects the request with a
   * 401 code if the session cookie is absent.
   *
   * @throws Throwable
   */
  @Test
  @Configuration(classpathConfigFile = "standard-config.json")
  @BackEndApi(RequiresAuthHeaderBackEndApi.class)
  public void testAuthenticatedRequestFailureNoCookie() throws Throwable {
    // test data - session expires in 60s
    final Session originalSession = CommonTestUtil.insertTestSession(60, true);

    // wait for the clock to tick to ensure we can test the updated expiration time
    Thread.sleep(100);

    // send request without cookie
    final PolicyTestRequest request = PolicyTestRequest.build(PolicyTestRequestType.GET, RESOURCE);
    request.header(Constants.HEADER_COOKIE, "");

    sendAndExpect401(request, originalSession);
  }
  @Test
  @Configuration(
      "{"
          + "  \"limit\" : 10485760,"
          + "  \"direction\" : \"upload\","
          + "  \"granularity\" : \"Api\","
          + "  \"period\" : \"Day\","
          + "  \"headerRemaining\" : \"X-Data-Remaining\","
          + "  \"headerLimit\" : \"X-Data-Limit\","
          + "  \"headerReset\" : \"X-Data-Reset\""
          + "}")
  public void testLargeUploadLimit() throws Throwable {
    PolicyTestRequest request =
        PolicyTestRequest.build(PolicyTestRequestType.POST, "/some/large-resource");
    // The 4th of these should exceed our limits
    byte[] data = new byte[11000000 / 4];
    Arrays.fill(data, (byte) 80);
    request.body(new String(data));

    PolicyTestResponse response = send(request);
    EchoResponse echo = response.entity(EchoResponse.class);
    Assert.assertNotNull(echo);
    Assert.assertEquals("7735760", response.header("X-Data-Remaining"));
    Assert.assertEquals("10485760", response.header("X-Data-Limit"));

    send(request);
    send(request);
    send(request);

    // Now if we try it one more time, we'll get a failure!
    try {
      send(request);
      Assert.fail("Expected a policy failure!");
    } catch (PolicyFailureError e) {
      PolicyFailure failure = e.getFailure();
      Assert.assertEquals(PolicyFailureCodes.BYTE_QUOTA_EXCEEDED, failure.getFailureCode());
      Assert.assertEquals("Transfer quota exceeded.", failure.getMessage());
      Assert.assertEquals(429, failure.getResponseCode());
      String remaining = failure.getHeaders().get("X-Data-Remaining");
      Assert.assertEquals("-514240", remaining);
    }
  }
  @Test
  @Configuration(
      "{"
          + "  \"limit\" : 100,"
          + "  \"direction\" : \"upload\","
          + "  \"granularity\" : \"Api\","
          + "  \"period\" : \"Day\","
          + "  \"headerRemaining\" : \"X-Bytes-Remaining\","
          + "  \"headerLimit\" : \"X-Bytes-Limit\","
          + "  \"headerReset\" : \"X-Bytes-Reset\""
          + "}")
  public void testUploadLimit() throws Throwable {
    PolicyTestRequest request =
        PolicyTestRequest.build(PolicyTestRequestType.POST, "/some/resource");
    request.body("0123456789");

    PolicyTestResponse response = send(request);
    EchoResponse echo = response.entity(EchoResponse.class);
    Assert.assertNotNull(echo);
    Assert.assertEquals("90", response.header("X-Bytes-Remaining"));
    Assert.assertEquals("100", response.header("X-Bytes-Limit"));

    // Now try sending a few more times to get closer to the limit
    for (int i = 0; i < 9; i++) {
      response = send(request);
    }
    echo = response.entity(EchoResponse.class);
    Assert.assertNotNull(echo);
    Assert.assertEquals("0", response.header("X-Bytes-Remaining"));
    Assert.assertEquals("100", response.header("X-Bytes-Limit"));

    // Now if we try it one more time, we'll get a failure!
    try {
      send(request);
      Assert.fail("Expected a policy failure!");
    } catch (PolicyFailureError e) {
      PolicyFailure failure = e.getFailure();
      Assert.assertEquals(PolicyFailureCodes.BYTE_QUOTA_EXCEEDED, failure.getFailureCode());
      Assert.assertEquals("Transfer quota exceeded.", failure.getMessage());
      Assert.assertEquals(429, failure.getResponseCode());
    }
  }
  @Test
  @Configuration(
      "{"
          + "  \"limit\" : 500,"
          + "  \"direction\" : \"both\","
          + "  \"granularity\" : \"Api\","
          + "  \"period\" : \"Day\","
          + "  \"headerRemaining\" : \"X-Bytes-Remaining\","
          + "  \"headerLimit\" : \"X-Bytes-Limit\","
          + "  \"headerReset\" : \"X-Bytes-Reset\""
          + "}")
  @BackEndApi(DownloadTestBackEndApi.class)
  public void testBothLimit() throws Throwable {
    PolicyTestRequest request =
        PolicyTestRequest.build(PolicyTestRequestType.PUT, "/some/resource");
    request.header("X-Payload-Size", "50");
    byte[] data = new byte[50];
    Arrays.fill(data, (byte) 80);
    request.body(new String(data));

    PolicyTestResponse response = send(request);
    Assert.assertNotNull(response.body());
    Assert.assertEquals("450", response.header("X-Bytes-Remaining"));
    Assert.assertEquals("500", response.header("X-Bytes-Limit"));

    send(request);
    send(request);
    send(request);
    send(request);

    // Now if we try it one more time, we'll get a failure!
    try {
      send(request);
      Assert.fail("Expected a policy failure!");
    } catch (PolicyFailureError e) {
      PolicyFailure failure = e.getFailure();
      Assert.assertEquals(PolicyFailureCodes.BYTE_QUOTA_EXCEEDED, failure.getFailureCode());
      Assert.assertEquals("Transfer quota exceeded.", failure.getMessage());
      Assert.assertEquals(429, failure.getResponseCode());
    }
  }