@Test
  public void testHeadersAreRemoved() throws Exception {
    HttpBasicAuthFilter filter = new HttpBasicAuthFilter(null, null, failureHandler);
    filter.setCacheHeader(false);

    Exchange exchange = newExchange();
    Request request = newRequest();
    request.getHeaders().putSingle(AUTHORIZATION_HEADER, "Basic azerty");

    doAnswer(
            new Answer<Promise<Response, NeverThrowsException>>() {
              @Override
              public Promise<Response, NeverThrowsException> answer(
                  final InvocationOnMock invocation) throws Throwable {
                // Produce a valid response with an authentication challenge
                Response response = new Response();
                response.setStatus(Status.OK);
                response.getHeaders().putSingle(AUTHENTICATE_HEADER, "Realm toto");
                return Promises.newResultPromise(response);
              }
            })
        .when(terminalHandler)
        .handle(eq(exchange), argThat(new AbsenceOfHeaderInRequest(AUTHORIZATION_HEADER)));

    Response response = filter.filter(exchange, request, terminalHandler).getOrThrow();

    // Verify that the outgoing message has no authenticate header
    assertThat(response.getHeaders().get(AUTHENTICATE_HEADER)).isNull();
  }
  /**
   * 2 consecutive requests are sharing the same session. The first one should build and cache the
   * Authorization header after a round trip to the next handler (firstly answer with a challenge).
   * The second should simply re-use the cached value
   *
   * @throws Exception
   */
  @Test
  public void tesAuthorizationHeaderCaching() throws Exception {
    HttpBasicAuthFilter filter =
        new HttpBasicAuthFilter(
            Expression.valueOf("bjensen", String.class),
            Expression.valueOf("hifalutin", String.class),
            failureHandler);
    filter.setCacheHeader(true);

    // No value cached for the first call
    // Subsequent invocations get the cached value
    when(session.get(endsWith(":userpass"))).thenReturn(null, INITIAL_CREDENTIALS);

    basicAuthServerAnswersUnauthorizedThenSuccess(INITIAL_CREDENTIALS);

    Exchange first = newExchange();
    Request firstRequest = newRequest();
    Response firstResponse = filter.filter(first, firstRequest, terminalHandler).getOrThrow();

    Exchange second = newExchange();
    Request secondRequest = newRequest();
    Response secondResponse = filter.filter(second, secondRequest, terminalHandler).getOrThrow();

    // Terminal handler should be called 3 times, not 4
    verify(terminalHandler, times(3)).handle(any(Exchange.class), any(Request.class));
    // Session should be updated with cached value
    verify(session).put(endsWith(":userpass"), eq(INITIAL_CREDENTIALS));

    // Responses should be OK for all outgoing responses
    assertThat(firstResponse.getStatus()).isEqualTo(Status.OK);
    assertThat(secondResponse.getStatus()).isEqualTo(Status.OK);
  }
  @Test
  public void testRefreshAuthenticationHeader() throws Exception {

    HttpBasicAuthFilter filter =
        new HttpBasicAuthFilter(
            Expression.valueOf("bjensen", String.class),
            Expression.valueOf("${exchange.password}", String.class),
            failureHandler);
    filter.setCacheHeader(true);

    // Mock cache content for credentials
    when(session.get(endsWith(":userpass")))
        .thenReturn(null, INITIAL_CREDENTIALS, INITIAL_CREDENTIALS, REFRESHED_CREDENTIALS);

    // Scenario:
    //  first request (cache the value after initial round-trip)
    //  second request (cached value is OK)
    //  third request (cached value is no longer valid, trigger a refresh)
    doAnswer(new UnauthorizedAnswer())
        .doAnswer(new AuthorizedAnswer(INITIAL_CREDENTIALS))
        .doAnswer(new AuthorizedAnswer(INITIAL_CREDENTIALS))
        .doAnswer(new UnauthorizedAnswer())
        .doAnswer(new AuthorizedAnswer(REFRESHED_CREDENTIALS))
        .when(terminalHandler)
        .handle(any(Exchange.class), any(Request.class));

    // Initial round-trip
    Exchange first = newExchange();
    first.put("password", "hifalutin");
    Response firstResponse = filter.filter(first, newRequest(), terminalHandler).getOrThrow();

    // Usage of cached value
    Exchange second = newExchange();
    Response secondResponse = filter.filter(second, newRequest(), terminalHandler).getOrThrow();

    // Cached value is no longer valid, trigger a user/pass refresh
    Exchange third = newExchange();
    third.put("password", "hifalutin2");
    Response thirdResponse = filter.filter(third, newRequest(), terminalHandler).getOrThrow();

    // Terminal handler should be called 5 times, not 6
    // first: 2 times
    // second: 1 time
    // third: 2 times
    verify(terminalHandler, times(5)).handle(any(Exchange.class), any(Request.class));
    // Session should be updated with cached value 2 times
    verify(session).put(endsWith(":userpass"), eq(INITIAL_CREDENTIALS));
    verify(session).put(endsWith(":userpass"), eq(REFRESHED_CREDENTIALS));

    // Responses should be OK for all outgoing responses
    assertThat(firstResponse.getStatus()).isEqualTo(Status.OK);
    assertThat(secondResponse.getStatus()).isEqualTo(Status.OK);
    assertThat(thirdResponse.getStatus()).isEqualTo(Status.OK);
  }
  @Test
  public void testNominalInteraction() throws Exception {
    HttpBasicAuthFilter filter =
        new HttpBasicAuthFilter(
            Expression.valueOf("bjensen", String.class),
            Expression.valueOf("hifalutin", String.class),
            failureHandler);
    filter.setCacheHeader(false);

    basicAuthServerAnswersUnauthorizedThenSuccess(INITIAL_CREDENTIALS);

    Exchange exchange = newExchange();
    Request request = newRequest();
    Response response = filter.filter(exchange, request, terminalHandler).getOrThrow();

    assertThat(response.getStatus()).isEqualTo(Status.OK);
  }
  @Test(dataProvider = "invalidUserNames")
  public void testConformanceErrorIsProducedWhenUsernameContainsColon(final String username)
      throws Exception {
    HttpBasicAuthFilter filter =
        new HttpBasicAuthFilter(
            Expression.valueOf(username, String.class),
            Expression.valueOf("dont-care", String.class),
            failureHandler);
    filter.setCacheHeader(false);

    basicAuthServerAnswersUnauthorizedThenSuccess(INITIAL_CREDENTIALS);

    Response response = filter.filter(newExchange(), newRequest(), terminalHandler).get();
    assertThat(response.getStatus()).isEqualTo(Status.INTERNAL_SERVER_ERROR);
    assertThat(response.getEntity().getString())
        .contains("username must not contain a colon ':' character");
  }
  /**
   * If there is no credentials provided, the filter should not try to forward the request more than
   * once
   */
  @Test
  public void testNoCredentialsProvided() throws Exception {
    HttpBasicAuthFilter filter =
        new HttpBasicAuthFilter(
            Expression.valueOf("${null}", String.class),
            Expression.valueOf("${null}", String.class),
            failureHandler);
    filter.setCacheHeader(false);

    // Always answer with 401
    doAnswer(new UnauthorizedAnswer())
        .when(terminalHandler)
        .handle(any(Exchange.class), any(Request.class));

    Exchange exchange = newExchange();
    Request request = newRequest();
    filter.filter(exchange, request, terminalHandler);

    verify(terminalHandler, times(1)).handle(exchange, request);
    verify(failureHandler).handle(exchange, request);
  }
  /**
   * If there are credentials but invalid ones (not accepted by the target application), this filter
   * should try 2 times: one with the old credential (or no credential if none are cached), and
   * another one with the refreshed credentials.
   */
  @Test
  public void testInvalidCredentialsProvided() throws Exception {
    HttpBasicAuthFilter filter =
        new HttpBasicAuthFilter(
            Expression.valueOf("bjensen", String.class),
            Expression.valueOf("hifalutin", String.class),
            failureHandler);
    filter.setCacheHeader(false);

    // Always answer with 401
    doAnswer(new UnauthorizedAnswer())
        .when(terminalHandler)
        .handle(any(Exchange.class), any(Request.class));

    Exchange exchange = newExchange();
    Request request = newRequest();
    filter.filter(exchange, request, terminalHandler);

    // if credentials were rejected all the times, the failure Handler is invoked
    verify(terminalHandler, times(2)).handle(exchange, request);
    verify(failureHandler).handle(exchange, request);
  }