TEST_F(RouterTest, Shadow) {
  ShadowPolicyPtr policy = std::make_unique<TestShadowPolicy>("foo", "bar");
  callbacks_.route_->route_entry_.shadow_policies_.push_back(std::move(policy));
  policy = std::make_unique<TestShadowPolicy>("fizz", "buzz", envoy::type::v3::FractionalPercent(),
                                              false);
  callbacks_.route_->route_entry_.shadow_policies_.push_back(std::move(policy));
  ON_CALL(callbacks_, streamId()).WillByDefault(Return(43));

  NiceMock<Http::MockRequestEncoder> encoder;
  Http::ResponseDecoder* response_decoder = nullptr;
  EXPECT_CALL(cm_.thread_local_cluster_.conn_pool_, newStream(_, _))
      .WillOnce(Invoke(
          [&](Http::ResponseDecoder& decoder,
              Http::ConnectionPool::Callbacks& callbacks) -> Http::ConnectionPool::Cancellable* {
            response_decoder = &decoder;
            callbacks.onPoolReady(encoder, cm_.thread_local_cluster_.conn_pool_.host_,
                                  upstream_stream_info_, Http::Protocol::Http10);
            return nullptr;
          }));
  expectResponseTimerCreate();

  EXPECT_CALL(runtime_.snapshot_, featureEnabled("bar", 0, 43, 10000)).WillOnce(Return(true));
  EXPECT_CALL(runtime_.snapshot_, featureEnabled("buzz", 0, 43, 10000)).WillOnce(Return(true));

  Http::TestRequestHeaderMapImpl headers;
  HttpTestUtility::addDefaultHeaders(headers);
  router_.decodeHeaders(headers, false);

  Buffer::InstancePtr body_data(new Buffer::OwnedImpl("hello"));
  EXPECT_CALL(callbacks_, addDecodedData(_, true));
  EXPECT_EQ(Http::FilterDataStatus::StopIterationNoBuffer, router_.decodeData(*body_data, false));

  Http::TestRequestTrailerMapImpl trailers{{"some", "trailer"}};
  EXPECT_CALL(callbacks_, decodingBuffer())
      .Times(AtLeast(2))
      .WillRepeatedly(Return(body_data.get()));
  EXPECT_CALL(*shadow_writer_, shadow_("foo", _, _))
      .WillOnce(Invoke([](const std::string&, Http::RequestMessagePtr& request,
                          const Http::AsyncClient::RequestOptions& options) -> void {
        EXPECT_NE(request->body().length(), 0);
        EXPECT_NE(nullptr, request->trailers());
        EXPECT_EQ(absl::optional<std::chrono::milliseconds>(10), options.timeout);
        EXPECT_TRUE(options.sampled_);
      }));
  EXPECT_CALL(*shadow_writer_, shadow_("fizz", _, _))
      .WillOnce(Invoke([](const std::string&, Http::RequestMessagePtr& request,
                          const Http::AsyncClient::RequestOptions& options) -> void {
        EXPECT_NE(request->body().length(), 0);
        EXPECT_NE(nullptr, request->trailers());
        EXPECT_EQ(absl::optional<std::chrono::milliseconds>(10), options.timeout);
        EXPECT_FALSE(options.sampled_);
      }));
  router_.decodeTrailers(trailers);
  EXPECT_EQ(1U,
            callbacks_.route_->route_entry_.virtual_cluster_.stats().upstream_rq_total_.value());

  Http::ResponseHeaderMapPtr response_headers(
      new Http::TestResponseHeaderMapImpl{{":status", "200"}});
  response_decoder->decodeHeaders(std::move(response_headers), true);
  EXPECT_TRUE(verifyHostUpstreamStats(1, 0));
}
TEST_F(RouterTest, Shadow) {
  ShadowPolicyPtr policy = std::make_unique<TestShadowPolicy>("foo", "bar");
  callbacks_.route_->route_entry_.shadow_policies_.push_back(std::move(policy));
  policy = std::make_unique<TestShadowPolicy>("fizz", "buzz", envoy::type::v3::FractionalPercent(),
                                              false);
  callbacks_.route_->route_entry_.shadow_policies_.push_back(std::move(policy));
  ON_CALL(callbacks_, streamId()).WillByDefault(Return(43));

  NiceMock<Http::MockRequestEncoder> encoder;
  Http::ResponseDecoder* response_decoder = nullptr;
  EXPECT_CALL(cm_.thread_local_cluster_.conn_pool_, newStream(_, _))
      .WillOnce(Invoke(
          [&](Http::ResponseDecoder& decoder,
              Http::ConnectionPool::Callbacks& callbacks) -> Http::ConnectionPool::Cancellable* {
            response_decoder = &decoder;
            callbacks.onPoolReady(encoder, cm_.thread_local_cluster_.conn_pool_.host_,
                                  upstream_stream_info_, Http::Protocol::Http10);
            return nullptr;
          }));
  expectResponseTimerCreate();

  EXPECT_CALL(runtime_.snapshot_, featureEnabled("bar", 0, 43, 10000)).WillOnce(Return(true));
  EXPECT_CALL(runtime_.snapshot_, featureEnabled("buzz", 0, 43, 10000)).WillOnce(Return(true));

  Http::TestRequestHeaderMapImpl headers;
  HttpTestUtility::addDefaultHeaders(headers);
  router_.decodeHeaders(headers, false);

  Buffer::InstancePtr body_data(new Buffer::OwnedImpl("hello"));
  EXPECT_CALL(callbacks_, addDecodedData(_, true));
  EXPECT_EQ(Http::FilterDataStatus::StopIterationNoBuffer, router_.decodeData(*body_data, false));

  Http::TestRequestTrailerMapImpl trailers{{"some", "trailer"}};
  EXPECT_CALL(callbacks_, decodingBuffer())
      .Times(AtLeast(2))
      .WillRepeatedly(Return(body_data.get()));
  EXPECT_CALL(*shadow_writer_, shadow_("foo", _, _))
      .WillOnce(Invoke([](const std::string&, Http::RequestMessagePtr& request,
                          const Http::AsyncClient::RequestOptions& options) -> void {
        EXPECT_NE(request->body().length(), 0);
        EXPECT_NE(nullptr, request->trailers());
        EXPECT_EQ(absl::optional<std::chrono::milliseconds>(10), options.timeout);
        EXPECT_TRUE(options.sampled_);
      }));
  EXPECT_CALL(*shadow_writer_, shadow_("fizz", _, _))
      .WillOnce(Invoke([](const std::string&, Http::RequestMessagePtr& request,
                          const Http::AsyncClient::RequestOptions& options) -> void {
        EXPECT_NE(request->body().length(), 0);
        EXPECT_NE(nullptr, request->trailers());
        EXPECT_EQ(absl::optional<std::chrono::milliseconds>(10), options.timeout);
        EXPECT_FALSE(options.sampled_);
      }));
  router_.decodeTrailers(trailers);
  EXPECT_EQ(1U,
            callbacks_.route_->route_entry_.virtual_cluster_.stats().upstream_rq_total_.value());

  Http::ResponseHeaderMapPtr response_headers(
      new Http::TestResponseHeaderMapImpl{{":status", "200"}});
  response_decoder->decodeHeaders(std::move(response_headers), true);
  EXPECT_TRUE(verifyHostUpstreamStats(1, 0));
}