Repository: envoyproxy/envoy
File: test/common/router/router_test.cc (C++)

Code before:
        EXPECT_EQ("static-value", shadow_headers->get(Http::LowerCaseString("x-mirror-static"))[0]
                                      ->value()
                                      .getStringView());

        // Verify headers were removed
        EXPECT_TRUE(shadow_headers->get(Http::LowerCaseString("x-sensitive-header")).empty());
        EXPECT_TRUE(shadow_headers->get(Http::LowerCaseString("authorization")).empty());

        return &foo_request;
      }));

  router_->decodeHeaders(headers, false);

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

  response_decoder->decodeHeaders(std::make_unique<Http::TestResponseHeaderMapImpl>(
                                      Http::TestResponseHeaderMapImpl{{":status", "200"}}),
                                  true);
  EXPECT_TRUE(verifyHostUpstreamStats(1, 0));

  router_->onDestroy();
}

TEST_P(RouterShadowingTest, ShadowWithAdvancedHeaderMutations) {
  const std::vector<std::string> mutation_yamls = {
      // Test ADD_IF_ABSENT - should only add if header doesn't exist
      R"EOF(
append:
  header:
    key: "x-new-header"
    value: "new-value"
  append_action: "ADD_IF_ABSENT"
)EOF",
      R"EOF(
append:
  header:
    key: "x-existing-header"
    value: "should-not-add"
  append_action: "ADD_IF_ABSENT"
)EOF",

      // Test OVERWRITE_IF_EXISTS - should only overwrite existing headers
      R"EOF(
append:
  header:
    key: "x-existing-header"
    value: "overwritten-value"
  append_action: "OVERWRITE_IF_EXISTS"
)EOF",

Review comment (suggestion):
I think this test is unnecessary because it should be covered by `HeaderEvaluator` self's test. We needn't to test these logic again and again at every position where the `HeaderMutation` is used.

Code after:
                                      .getStringView());
        EXPECT_EQ("static-value", shadow_headers->get(Http::LowerCaseString("x-mirror-static"))[0]
                                      ->value()
                                      .getStringView());

        // Verify headers were removed
        EXPECT_TRUE(shadow_headers->get(Http::LowerCaseString("x-sensitive-header")).empty());
        EXPECT_TRUE(shadow_headers->get(Http::LowerCaseString("authorization")).empty());

        return &foo_request;
      }));

  router_->decodeHeaders(headers, false);

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

  response_decoder->decodeHeaders(std::make_unique<Http::TestResponseHeaderMapImpl>(
                                      Http::TestResponseHeaderMapImpl{{":status", "200"}}),
                                  true);
  EXPECT_TRUE(verifyHostUpstreamStats(1, 0));

  router_->onDestroy();
}

TEST_P(RouterShadowingTest, ShadowWithMixedMutationsAndHostRewrite) {
  const std::vector<std::string> mutation_yamls = {
      R"EOF(
append:
  header:
    key: "x-test-env"
    value: "shadow"
  append_action: "APPEND_IF_EXISTS_OR_ADD"
)EOF",
      R"EOF(
append:
  header:
    key: "x-shadow-id"
    value: "12345"
  append_action: "APPEND_IF_EXISTS_OR_ADD"
)EOF",
      R"EOF(
remove: "x-remove-me"
)EOF"};

  std::vector<envoy::config::common::mutation_rules::v3::HeaderMutation> mutations;
  for (const auto& yaml : mutation_yamls) {
    envoy::config::common::mutation_rules::v3::HeaderMutation mutation;
    TestUtility::loadFromYaml(yaml, mutation);
    mutations.push_back(mutation);