@Test
  public void shouldUseDifferentBucketsWhenUsingValidPartitionKey() throws Exception {
    FakeTimeService time = new FakeTimeService(0);
    Expression<String> expr =
        Expression.valueOf("${matches(exchange.foo, 'bar-00') ?'bucket-00' :''}", String.class);
    ThrottlingFilter filter = new ThrottlingFilter(time, 1, duration("3 seconds"), expr);

    Handler handler = new ResponseHandler(Status.OK);

    // The time does not need to advance
    Exchange exchange = new Exchange();
    Promise<Response, NeverThrowsException> promise;

    exchange.put("foo", "bar-00");
    promise = filter.filter(exchange, new Request(), handler);
    assertThat(promise.get().getStatus()).isEqualTo(Status.OK);

    exchange.put("foo", "bar-00");
    promise = filter.filter(exchange, new Request(), handler);
    assertThat(promise.get().getStatus()).isEqualTo(Status.TOO_MANY_REQUESTS);

    exchange.put("foo", "bar-01");
    promise = filter.filter(exchange, new Request(), handler);
    assertThat(promise.get().getStatus()).isEqualTo(Status.OK);
  }
  @Test
  public void shouldThrottleRequests() throws Exception {
    FakeTimeService time = new FakeTimeService(0);
    ThrottlingFilter filter =
        new ThrottlingFilter(time, 1, duration("3 seconds"), DEFAULT_PARTITION_EXPR);

    // This one has to call the handler as there are enough tokens in the bucket.
    Handler handler1 = mock(Handler.class, "handler1");
    filter.filter(mock(Context.class), new Request(), handler1);
    verify(handler1).handle(any(Context.class), any(Request.class));

    time.advance(duration("2 seconds"));
    // This one does not have to be called as there is no token anymore in the bucket.
    Handler handler2 = mock(Handler.class, "handler2");
    Promise<Response, NeverThrowsException> promise2 =
        filter.filter(mock(Context.class), new Request(), handler2);
    verifyZeroInteractions(handler2);
    assertThat(promise2.get().getStatus()).isEqualTo(Status.TOO_MANY_REQUESTS);

    time.advance(duration("4 seconds"));
    // This one has to call the handler as the bucket has been refilled.
    Handler handler3 = mock(Handler.class, "handler3");
    filter.filter(mock(Context.class), new Request(), handler3);
    verify(handler3).handle(any(Context.class), any(Request.class));
  }
  @Test
  public void shouldUseDefaultValueWithExpressionEvaluatingNull() throws Exception {
    FakeTimeService time = new FakeTimeService(0);
    Expression<String> expr = Expression.valueOf("${exchange.bar}", String.class);
    ThrottlingFilter filter = new ThrottlingFilter(time, 1, duration("3 seconds"), expr);

    Handler handler = new ResponseHandler(Status.OK);

    // The time does not need to advance
    Exchange exchange = new Exchange();
    Promise<Response, NeverThrowsException> promise;

    promise = filter.filter(exchange, new Request(), handler);
    assertThat(promise.get().getStatus()).isEqualTo(Status.INTERNAL_SERVER_ERROR);
  }
  @Test
  public void shouldThrottleConcurrentRequests() throws Exception {
    CountDownLatch latch1 = new CountDownLatch(1);
    CountDownLatch latch2 = new CountDownLatch(1);

    FakeTimeService time = new FakeTimeService(0);
    final ThrottlingFilter filter =
        new ThrottlingFilter(time, 1, duration("3 seconds"), DEFAULT_PARTITION_EXPR);

    // This one has to be called as there are enough tokens in the bucket.
    final Handler handler1 = new LatchHandler(latch1, latch2);

    Runnable r =
        new Runnable() {

          @Override
          public void run() {
            filter.filter(mock(Context.class), new Request(), handler1);
          }
        };
    Thread t1 = new Thread(r);
    t1.setName("Filter for request #1");
    t1.start();
    latch2.await();

    time.advance(duration("2 seconds"));
    try {
      // This one does not have to be called as there no token anymore in the bucket.
      Handler handler2 = mock(Handler.class, "handler2");
      Promise<Response, NeverThrowsException> promise2 =
          filter.filter(mock(Context.class), new Request(), handler2);

      verifyZeroInteractions(handler2);

      Response response = promise2.get(20, TimeUnit.SECONDS);
      assertThat(response.getStatus()).isEqualTo(Status.TOO_MANY_REQUESTS);
      assertThat(response.getHeaders().getFirst("Retry-After")).isEqualTo("1");
    } finally {
      latch1.countDown();
      t1.join();
    }
  }
  @Test(dataProvider = "annotatedRequestHandlerData")
  public void testQueryCollectionAnnotatedRequestHandler(
      Class<?> requestHandler,
      boolean collection,
      boolean create,
      boolean read,
      boolean update,
      boolean delete,
      boolean patch,
      boolean resourceAction,
      boolean collectionAction,
      boolean query)
      throws Exception {

    // Given
    Object provider = requestHandler.newInstance();
    Connection connection = Resources.newInternalConnection(createHandler(collection, provider));
    QueryRequest req = Requests.newQueryRequest("/test");

    // When
    Promise<QueryResult, ResourceException> promise =
        connection.queryAsync(new RootContext(), req, mock(QueryResourceHandler.class));

    // Then
    if (query && collection) {
      AssertJPromiseAssert.assertThat(promise).succeeded();
      QueryResult result = promise.get();
      Assertions.assertThat(result.getPagedResultsCookie()).isEqualTo("query");
    } else if (collection) {
      AssertJPromiseAssert.assertThat(promise)
          .failedWithException()
          .isInstanceOf(NotSupportedException.class);
    } else {
      AssertJPromiseAssert.assertThat(promise)
          .failedWithException()
          .isInstanceOf(BadRequestException.class);
    }
  }