Repository: envoyproxy/envoy
File: test/extensions/filters/http/grpc_field_extraction/filter_test.cc (C++)

Code before:
                               {":path", "/apikeys.ApiKeys/CreateApiKey"},
                               {"content-type", "application/grpc"}};
  EXPECT_EQ(Envoy::Http::FilterHeadersStatus::StopIteration,
            filter_->decodeHeaders(req_headers, true));

  CreateApiKeyRequest request = makeCreateApiKeyRequest("");
  Envoy::Buffer::InstancePtr request_data = Envoy::Grpc::Common::serializeToGrpcFrame(request);
  EXPECT_CALL(mock_decoder_callbacks_.stream_info_, setDynamicMetadata(_, _))
      .WillOnce(Invoke([](const std::string& ns, const Protobuf::Struct& new_dynamic_metadata) {
        EXPECT_EQ(ns, "envoy.filters.http.grpc_field_extraction");
        checkProtoStruct(new_dynamic_metadata, R"pb(
fields {
  key: "parent"
  value {
    list_value {
    }
  }
})pb");
      }));
  EXPECT_EQ(Envoy::Http::FilterDataStatus::Continue, filter_->decodeData(*request_data, true));

  // No data modification.
  checkSerializedData<CreateApiKeyRequest>(*request_data, {request});
}

TEST_F(FilterTestExtractOk, MissingFieldProducesListValue) {
  setUp(R"pb(
    extractions_by_method: {
      key: "apikeys.ApiKeys.CreateApiKey"
      value: {
        request_field_extractions: {
          key: "key.display_name"
          value: {}
        }
      }
    })pb");
  TestRequestHeaderMapImpl req_headers =
      TestRequestHeaderMapImpl{{":method", "POST"},
                               {":path", "/apikeys.ApiKeys/CreateApiKey"},
                               {"content-type", "application/grpc"}};
  EXPECT_EQ(Envoy::Http::FilterHeadersStatus::StopIteration,
            filter_->decodeHeaders(req_headers, true));

  CreateApiKeyRequest request = makeCreateApiKeyRequest(R"pb(
    parent: "project-id"
  )pb");
  Envoy::Buffer::InstancePtr request_data = Envoy::Grpc::Common::serializeToGrpcFrame(request);
  EXPECT_CALL(mock_decoder_callbacks_.stream_info_, setDynamicMetadata(_, _))
      .WillOnce([](const std::string& ns, const Protobuf::Struct& new_dynamic_metadata) {
        EXPECT_EQ(ns, "envoy.filters.http.grpc_field_extraction");
        EXPECT_TRUE(new_dynamic_metadata.fields().contains("key.display_name"));

Review comment (refactor):
A note on testing, the `checkProtoStruct` method in this source ([link](https://github.com/envoyproxy/envoy/blob/main/test/extensions/filters/http/grpc_field_extraction/filter_test.cc#L92-L98)) seems to check for proto equivalence, not strict equality. So it will consider an empty `Value` and an empty `ListValue` as equivalent and as such couldn't be used to test this.

Code after:
                               {":path", "/apikeys.ApiKeys/CreateApiKey"},
                               {"content-type", "application/grpc"}};
  EXPECT_EQ(Envoy::Http::FilterHeadersStatus::StopIteration,
            filter_->decodeHeaders(req_headers, true));

  CreateApiKeyRequest request = makeCreateApiKeyRequest("");
  Envoy::Buffer::InstancePtr request_data = Envoy::Grpc::Common::serializeToGrpcFrame(request);
  EXPECT_CALL(mock_decoder_callbacks_.stream_info_, setDynamicMetadata(_, _))
      .WillOnce(Invoke([](const std::string& ns, const Protobuf::Struct& new_dynamic_metadata) {
        EXPECT_EQ(ns, "envoy.filters.http.grpc_field_extraction");
        checkProtoStruct(new_dynamic_metadata, R"pb(
fields {
  key: "parent"
  value {
    list_value {
    }
  }
})pb");
      }));
  EXPECT_EQ(Envoy::Http::FilterDataStatus::Continue, filter_->decodeData(*request_data, true));

  // No data modification.
  checkSerializedData<CreateApiKeyRequest>(*request_data, {request});
}

TEST_F(FilterTestExtractOk, MissingFieldProducesListValue) {
  setUp(R"pb(
    extractions_by_method: {
      key: "apikeys.ApiKeys.CreateApiKey"
      value: {
        request_field_extractions: {
          key: "key.display_name"
          value: {}
        }
      }
    })pb");
  TestRequestHeaderMapImpl req_headers =
      TestRequestHeaderMapImpl{{":method", "POST"},
                               {":path", "/apikeys.ApiKeys/CreateApiKey"},
                               {"content-type", "application/grpc"}};
  EXPECT_EQ(Envoy::Http::FilterHeadersStatus::StopIteration,
            filter_->decodeHeaders(req_headers, true));

  CreateApiKeyRequest request = makeCreateApiKeyRequest(R"pb(
    parent: "project-id"
  )pb");
  Envoy::Buffer::InstancePtr request_data = Envoy::Grpc::Common::serializeToGrpcFrame(request);
  EXPECT_CALL(mock_decoder_callbacks_.stream_info_, setDynamicMetadata(_, _))
      .WillOnce([](const std::string& ns, const Protobuf::Struct& new_dynamic_metadata) {
        EXPECT_EQ(ns, "envoy.filters.http.grpc_field_extraction");
        const auto it = new_dynamic_metadata.fields().find("key.display_name");