@Test
  public void testOutbound() throws Exception {
    List<Interceptor> interceptors = new ArrayList<>();

    MockInterceptor interceptor1 = new MockInterceptor();
    MockInterceptor interceptor2 = new MockInterceptor();

    interceptors.add(interceptor1);
    interceptors.add(interceptor2);

    ResourceRequest request =
        new DefaultResourceRequest.Builder(RequestType.READ, new ResourcePath("/foo/bar")).build();
    ResourceResponse response =
        new DefaultResourceErrorResponse(request, ResourceErrorResponse.ErrorType.NOT_AUTHORIZED);

    EmbeddedChannel channel =
        new EmbeddedChannel(
            new ChannelDuplexHandler() {
              @Override
              public void write(ChannelHandlerContext ctx, Object msg, ChannelPromise promise)
                  throws Exception {
                InterceptorChain chain =
                    new InterceptorChain(ctx, interceptors, (ResourceResponse) msg);
                chain.fireOutbound();
              }
            });

    channel.writeInbound(request);
    ResourceRequest endRequest = (ResourceRequest) channel.readInbound();

    channel.writeAndFlush(response);
    ResourceResponse endResponse = (ResourceResponse) channel.readOutbound();

    assertThat(endResponse).isNotNull();
    assertThat(interceptor2.responses()).hasSize(1);
    assertThat(interceptor2.responses()).contains(response);
    assertThat(interceptor1.responses()).hasSize(1);
    assertThat(interceptor1.responses()).contains(response);
  }
  @Test
  public void testCorrelationWithRequestReplacement() throws Exception {

    List<Interceptor> interceptors = new ArrayList<>();

    MockInterceptor interceptor1 = new MockInterceptor();
    DefaultInterceptor interceptor2 =
        new DefaultInterceptor() {
          @Override
          public void onInbound(InboundInterceptorContext context) throws Exception {
            ResourceRequest replacement =
                new DefaultResourceRequest.Builder(context.request()).build();
            context.forward(replacement);
          }

          @Override
          public void onOutbound(OutboundInterceptorContext context) throws Exception {
            ResourceResponse replacement =
                new DefaultResourceResponse(context.request(), ResourceResponse.ResponseType.ERROR);
            context.forward(replacement);
          }
        };
    MockInterceptor interceptor3 = new MockInterceptor();

    interceptors.add(interceptor1);
    interceptors.add(interceptor2);
    interceptors.add(interceptor3);

    EmbeddedChannel channel =
        new EmbeddedChannel(
            new ChannelDuplexHandler() {
              @Override
              public void write(ChannelHandlerContext ctx, Object msg, ChannelPromise promise)
                  throws Exception {
                InterceptorChain chain =
                    new InterceptorChain(ctx, interceptors, (ResourceResponse) msg);
                chain.fireOutbound();
              }

              @Override
              public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {
                InterceptorChain chain =
                    new InterceptorChain(ctx, interceptors, (ResourceRequest) msg);
                chain.fireInbound();
              }
            });

    ResourceRequest request =
        new DefaultResourceRequest.Builder(RequestType.READ, new ResourcePath("/foo/bar")).build();
    channel.writeInbound(request);
    ResourceRequest endRequest = (ResourceRequest) channel.readInbound();

    assertThat(request).isNotSameAs(endRequest);
    assertThat(request.requestId()).isEqualTo(endRequest.requestId());

    ResourceResponse response =
        new DefaultResourceResponse(endRequest, ResourceResponse.ResponseType.READ);
    channel.writeAndFlush(response);
    ResourceResponse endResponse = (ResourceResponse) channel.readOutbound();

    assertThat(response).isNotSameAs(endResponse);
    assertThat(response.inReplyTo()).isNotSameAs(request);
    assertThat(response.requestId()).isEqualTo(request.requestId());
  }