Skip to content

[ProtoApiScrubber] Add Integration Tests#42121

Merged
adisuissa merged 15 commits intoenvoyproxy:mainfrom
sumitkmr2:it
Dec 2, 2025
Merged

[ProtoApiScrubber] Add Integration Tests#42121
adisuissa merged 15 commits intoenvoyproxy:mainfrom
sumitkmr2:it

Conversation

@sumitkmr2
Copy link
Copy Markdown
Contributor

@sumitkmr2 sumitkmr2 commented Nov 19, 2025

Commit Message: Add Integration tests.
Additional Description: Currently, the tests are added using envoy_cc_extension as envoy_extension_cc_test fails to recognize the extension as it's not registered. Once the filter's core functionality is complete, we'll do the registration and update the build rule. Currently, only request integration tests are added as response flow is not ready. Response ITs will be added once response flow code is checked-in.
Risk Level: None.
Testing: ITs added.
Docs Changes: None.
Release Notes: None.
Platform Specific Features: None.
[Optional Runtime guard:]
[Optional Fixes #Issue]
[Optional Fixes commit #PR or SHA]
[Optional Deprecated:]
[Optional API Considerations:]

Signed-off-by: Sumit Kumar <sumitkmr@google.com>
Signed-off-by: Sumit Kumar <sumitkmr@google.com>
Signed-off-by: Sumit Kumar <sumitkmr@google.com>
Signed-off-by: Sumit Kumar <sumitkmr@google.com>
Signed-off-by: Sumit Kumar <sumitkmr@google.com>
Signed-off-by: Sumit Kumar <sumitkmr@google.com>
@repokitteh-read-only
Copy link
Copy Markdown

As a reminder, PRs marked as draft will not be automatically assigned reviewers,
or be handled by maintainer-oncall triage.

Please mark your PR as ready when you want it to be reviewed!

🐱

Caused by: #42121 was opened by sumitkmr2.

see: more, trace.

@sumitkmr2 sumitkmr2 changed the title It [ProtoApiScrubber] Add Integration Tests Nov 19, 2025
Signed-off-by: Sumit Kumar <sumitkmr@google.com>
@sumitkmr2 sumitkmr2 marked this pull request as ready for review November 19, 2025 14:27
@sumitkmr2 sumitkmr2 requested a review from adisuissa as a code owner November 19, 2025 14:27
Signed-off-by: sumitkmr2 <sumitkmr@google.com>
@sumitkmr2
Copy link
Copy Markdown
Contributor Author

/retest

Copy link
Copy Markdown
Contributor

@adisuissa adisuissa left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks, overall LGTM.
Left a few minor comments.

1: 0
)EOF";

class ProtoApiScrubberIntegrationTest : public HttpProtocolIntegrationTest {
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What's the reason for extending HttpProtocolIntegrationTest and not HttpIntegrationTest? Does it require AsyncLB?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actually, I'm not sure about the exact reason. I followed proto_message_extraction and grpc_field_extraction filters and both use HttpProtocolIntegrationTest. In my view, I think it's better for extensibility when we add support for non-grpc requests carrying protobuf payloads as well. In that case, tests can easily be extended with a one-line change. Let me know if you think this is too-much future proofing and needs to be removed.

typed_config:
"@type": type.googleapis.com/xds.type.matcher.v3.CelMatcher
expr_match:
{2}
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can the expr_match be added in a non copy-paste method?
The reason is that it now depends on the spaces in the config, which makes it brittle to maintain.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done. Added in a json format so that indentation is not a problem. Tried doing in a builder fashion but that wasn't much readable (i.e., one can't figure out easily the config while looking at the builder). Hope that json format is ok.

descriptor_set:
data_source:
filename: {0}
{1}
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Same as expr_match above.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done.

restrictions_yaml = fmt::format(
R"EOF(
restrictions:
method_restrictions:
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Does message_restrictions also need integrations tests?
Also does the method_restriction need to be validated?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Does message_restrictions also need integrations tests?

Yes, it does. @pranavrautgoogle is working on it and the PR will be out once this PR gets merged.

Also does the method_restriction need to be validated?

Yes, it is already getting validated in the filter_config.cc as of today.

// TEST GROUP 1: PASS THROUGH
// ============================================================================

TEST_P(ProtoApiScrubberIntegrationTest, UnaryRequestPassesThrough) {
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please add a one-liner comment describing what the goal of each test.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done.

ASSERT_TRUE(response->waitForEndStream());
}

TEST_P(ProtoApiScrubberIntegrationTest, ScrubNestedField) {
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please add a test that attempts to scrub a field that is not found in the data.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

By not found in the data., do you mean not found in the descriptors? eg, a newer client library passing a field to an old envoy build? If yes, that case is not handled yet in the code. Planning to add that in next few days. The expectation in that case is that it passes through without scrubbing. I'll add the tests along with it in the UTs itself.

If you mean that it's not found in the filter config (ie, no restrictions configured for the field), then that case is already covered in the UTs and it passes through without scrubbing.

getFilterConfig(apikeysDescriptorPath(), kCreateApiKeyMethod, "key.display_name"));
initialize();

auto original_proto = makeCreateApiKeyRequest(R"pb(
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Will it be also useful to test the following cases:

  • more than 1 key exists (say another_name in addition to display_name) and only one (display_name) is scrubbed?
  • not sure if it's valid but a case where there's a double-nested field with the same name key { key: { display_name: "sensitive" } }.

These may already be addressed by the unit tests, and in that case, that should be sufficient.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The first one is already covered in the field checker UT.
The second one is interesting and is not covered anywhere, thanks for pointing out! It's a valid case - the field path for respective fields would be key, key.key and key.key.display_name. It's not possible to repro with the ApiKeys service that we are using currently for testing here in IT. I'll that case in the field checker UT where it is much simpler to repro that case. SG?

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

SGTM

Signed-off-by: Sumit Kumar <sumitkmr@google.com>
Copy link
Copy Markdown
Contributor Author

@sumitkmr2 sumitkmr2 left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Addressed comments, thanks! Will add the requested tests in the field checker UT in a separate PR to avoid conflicts as there are a lot of pending changes going on by myself and Pranav so trying to keep the scope of each PR limited to as less number of files as possible.

restrictions_yaml = fmt::format(
R"EOF(
restrictions:
method_restrictions:
Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Does message_restrictions also need integrations tests?

Yes, it does. @pranavrautgoogle is working on it and the PR will be out once this PR gets merged.

Also does the method_restriction need to be validated?

Yes, it is already getting validated in the filter_config.cc as of today.

typed_config:
"@type": type.googleapis.com/xds.type.matcher.v3.CelMatcher
expr_match:
{2}
Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done. Added in a json format so that indentation is not a problem. Tried doing in a builder fashion but that wasn't much readable (i.e., one can't figure out easily the config while looking at the builder). Hope that json format is ok.

descriptor_set:
data_source:
filename: {0}
{1}
Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done.

// TEST GROUP 1: PASS THROUGH
// ============================================================================

TEST_P(ProtoApiScrubberIntegrationTest, UnaryRequestPassesThrough) {
Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done.

ASSERT_TRUE(response->waitForEndStream());
}

TEST_P(ProtoApiScrubberIntegrationTest, ScrubNestedField) {
Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

By not found in the data., do you mean not found in the descriptors? eg, a newer client library passing a field to an old envoy build? If yes, that case is not handled yet in the code. Planning to add that in next few days. The expectation in that case is that it passes through without scrubbing. I'll add the tests along with it in the UTs itself.

If you mean that it's not found in the filter config (ie, no restrictions configured for the field), then that case is already covered in the UTs and it passes through without scrubbing.

getFilterConfig(apikeysDescriptorPath(), kCreateApiKeyMethod, "key.display_name"));
initialize();

auto original_proto = makeCreateApiKeyRequest(R"pb(
Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The first one is already covered in the field checker UT.
The second one is interesting and is not covered anywhere, thanks for pointing out! It's a valid case - the field path for respective fields would be key, key.key and key.key.display_name. It's not possible to repro with the ApiKeys service that we are using currently for testing here in IT. I'll that case in the field checker UT where it is much simpler to repro that case. SG?

1: 0
)EOF";

class ProtoApiScrubberIntegrationTest : public HttpProtocolIntegrationTest {
Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actually, I'm not sure about the exact reason. I followed proto_message_extraction and grpc_field_extraction filters and both use HttpProtocolIntegrationTest. In my view, I think it's better for extensibility when we add support for non-grpc requests carrying protobuf payloads as well. In that case, tests can easily be extended with a one-line change. Let me know if you think this is too-much future proofing and needs to be removed.

Signed-off-by: Sumit Kumar <sumitkmr@google.com>
Signed-off-by: Sumit Kumar <sumitkmr@google.com>
Signed-off-by: Sumit Kumar <sumitkmr@google.com>
Signed-off-by: Sumit Kumar <sumitkmr@google.com>
Copy link
Copy Markdown
Contributor

@adisuissa adisuissa left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks, overall LGTM.
Can you please run this under tsan with runs_per_test=1000 to reduce the chances of introducing flaky tests?

const std::string kCreateApiKeyMethod = "/apikeys.ApiKeys/CreateApiKey";

// CEL Matcher Config (Protobuf Text Format) which evaluates to TRUE.
const std::string kCelAlwaysTrue = R"pb(
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nit: preffer constexpr absl::string_view

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

done


// Format the full config.
full_config_text = fmt::format(R"pb(
filtering_mode: OVERRIDE
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nit: use yaml instead of this (no need for double curly brackets everywhere, and makes the code a bit more concise - example).

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yeah, it was in yaml before but changed it to json due to spacing constraints while doing replacement of parameters {0}, {1}, etc. as discussed here.
In json, spacing doesn't matter so it's not brittle now.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Another approach is using a different approach than format replace.
What about using a format replacement for simple elements (strings, numbers), and using the protobuf methods (e.g., PackFrom()) for non-simple elements?

Copy link
Copy Markdown
Contributor Author

@sumitkmr2 sumitkmr2 Nov 26, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I've refactored the code to make it more configurable and readable at the same time. Let me know if this looks better now. Also, I'll be adding more Integration tests with different input types and different CEL expressions, etc. in follow-up CLs.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think we are back to square 1 with this, but as you will be the codeowner on this and it is only test code, then I can live with that.

EXPECT_EQ("3", grpc_status->value().getStringView()); // 3 = Invalid Argument
}

INSTANTIATE_TEST_SUITE_P(Protocols, ProtoApiScrubberIntegrationTest,
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

style-nit: please move right after the class declaration.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

done.

Signed-off-by: Sumit Kumar <sumitkmr@google.com>
Copy link
Copy Markdown
Contributor Author

@sumitkmr2 sumitkmr2 left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Addressed comments, thanks!
Also, ran the following command to test flakiness:

envoy$ bazel test   --config=tsan   --runs_per_test=1000   --cache_test_results=no   --test_output=errors    //test/extensions/filters/http/proto_api_scrubber:integration_test
INFO: Invocation ID: d9aed359-0255-4f37-964a-ce8f86721e2f
INFO: Analyzed target //test/extensions/filters/http/proto_api_scrubber:integration_test (0 packages loaded, 20 targets configured).
INFO: Found 1 test target...
Target //test/extensions/filters/http/proto_api_scrubber:integration_test up-to-date:
  *****/execroot/envoy/bazel-out/k8-fastbuild/bin/test/extensions/filters/http/proto_api_scrubber/integration_test
INFO: Elapsed time: 786.311s, Critical Path: 114.53s
INFO: 1001 processes: 1 internal, 1000 linux-sandbox.
INFO: Build completed successfully, 1001 total actions

Executed 1 out of 1 test: 1 test passes.


// Format the full config.
full_config_text = fmt::format(R"pb(
filtering_mode: OVERRIDE
Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yeah, it was in yaml before but changed it to json due to spacing constraints while doing replacement of parameters {0}, {1}, etc. as discussed here.
In json, spacing doesn't matter so it's not brittle now.

EXPECT_EQ("3", grpc_status->value().getStringView()); // 3 = Invalid Argument
}

INSTANTIATE_TEST_SUITE_P(Protocols, ProtoApiScrubberIntegrationTest,
Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

done.

const std::string kCreateApiKeyMethod = "/apikeys.ApiKeys/CreateApiKey";

// CEL Matcher Config (Protobuf Text Format) which evaluates to TRUE.
const std::string kCelAlwaysTrue = R"pb(
Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

done

Signed-off-by: Sumit Kumar <sumitkmr@google.com>
Copy link
Copy Markdown
Contributor Author

@sumitkmr2 sumitkmr2 left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Addressed pending comment.


// Format the full config.
full_config_text = fmt::format(R"pb(
filtering_mode: OVERRIDE
Copy link
Copy Markdown
Contributor Author

@sumitkmr2 sumitkmr2 Nov 26, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I've refactored the code to make it more configurable and readable at the same time. Let me know if this looks better now. Also, I'll be adding more Integration tests with different input types and different CEL expressions, etc. in follow-up CLs.

Copy link
Copy Markdown
Contributor

@adisuissa adisuissa left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks, merging.


// Format the full config.
full_config_text = fmt::format(R"pb(
filtering_mode: OVERRIDE
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think we are back to square 1 with this, but as you will be the codeowner on this and it is only test code, then I can live with that.

getFilterConfig(apikeysDescriptorPath(), kCreateApiKeyMethod, "key.display_name"));
initialize();

auto original_proto = makeCreateApiKeyRequest(R"pb(
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

SGTM

@adisuissa adisuissa merged commit 5aecf40 into envoyproxy:main Dec 2, 2025
24 checks passed
grnmeira pushed a commit to grnmeira/envoy that referenced this pull request Mar 20, 2026
Signed-off-by: Sumit Kumar <sumitkmr@google.com>
Signed-off-by: sumitkmr2 <sumitkmr@google.com>
Signed-off-by: Gustavo <grnmeira@gmail.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants