Skip to content

[bbr] add integration tests for response plugin execution#2450

Merged
k8s-ci-robot merged 1 commit intokubernetes-sigs:mainfrom
abdallahsamabd:feat/2449
Mar 31, 2026
Merged

[bbr] add integration tests for response plugin execution#2450
k8s-ci-robot merged 1 commit intokubernetes-sigs:mainfrom
abdallahsamabd:feat/2449

Conversation

@abdallahsamabd
Copy link
Copy Markdown
Contributor

@abdallahsamabd abdallahsamabd commented Mar 2, 2026

What this PR does / why we need it:

Follow-up to PR #2369 ([pluggable bbr] mirror request plugins execution to the response path).

This PR adds integration tests that validate response plugin execution over a real gRPC ext_proc stream, covering:

  • Unary mode with plugin: a response plugin mutates the response body (e.g., guardrail adding a field)
  • Unary mode without plugins: no response plugins configured results in passthrough behavior

These tests exercise the full Process loop (request body → response headers → response body) to ensure end-to-end correctness.

Also adds:

  • NewBBRHarnessWithPlugins helper to support configuring request/response plugins in test harnesses
  • Response phase expectation helpers (ExpectResponseHeadersPassThrough, ExpectResponseBodyPassThrough, ExpectResponseBodyMutation)

Which issue(s) this PR fixes:

Fixes #2449

Does this PR introduce a user-facing change?:

@k8s-ci-robot
Copy link
Copy Markdown
Contributor

@abdallahsamabd: The label(s) kind/test cannot be applied, because the repository doesn't have them.

Details

In response to this:

/kind test

What this PR does / why we need it:

Follow-up to PR #2369 ([pluggable bbr] mirror request plugins execution to the response path).

This PR adds integration tests that validate response plugin execution over a real gRPC ext_proc stream, covering:

  • Unary mode with plugin: a response plugin mutates the response body (e.g., guardrail adding a field)
  • Unary mode without plugins: no response plugins configured results in passthrough behavior

These tests exercise the full Process loop (request body → response headers → response body) to ensure end-to-end correctness.

Also adds:

  • NewBBRHarnessWithPlugins helper to support configuring request/response plugins in test harnesses
  • Response phase expectation helpers (ExpectResponseHeadersPassThrough, ExpectResponseBodyPassThrough, ExpectResponseBodyMutation)

Which issue(s) this PR fixes:

Fixes #2449

Does this PR introduce a user-facing change?:

Instructions for interacting with me using PR comments are available here. If you have questions or suggestions related to my behavior, please file an issue against the kubernetes-sigs/prow repository.

@netlify
Copy link
Copy Markdown

netlify Bot commented Mar 2, 2026

Deploy Preview for gateway-api-inference-extension ready!

Name Link
🔨 Latest commit 4bf8870
🔍 Latest deploy log https://app.netlify.com/projects/gateway-api-inference-extension/deploys/69ca8da3f8b5c10008dae9ca
😎 Deploy Preview https://deploy-preview-2450--gateway-api-inference-extension.netlify.app
📱 Preview on mobile
Toggle QR Code...

QR Code

Use your smartphone camera to open QR code link.

To edit notification comments on pull requests, go to your Netlify project configuration.

@k8s-ci-robot k8s-ci-robot added the cncf-cla: yes Indicates the PR's author has signed the CNCF CLA. label Mar 2, 2026
@k8s-ci-robot k8s-ci-robot requested review from liu-cong and shmuelk March 2, 2026 13:33
@k8s-ci-robot k8s-ci-robot added the needs-ok-to-test Indicates a PR that requires an org member to verify it is safe to test. label Mar 2, 2026
@k8s-ci-robot
Copy link
Copy Markdown
Contributor

Hi @abdallahsamabd. Thanks for your PR.

I'm waiting for a kubernetes-sigs member to verify that this patch is reasonable to test. If it is, they should reply with /ok-to-test on its own line. Until that is done, I will not automatically test new commits in this PR, but the usual testing commands by org members will still work.

Regular contributors should join the org to skip this step.

Once the patch is verified, the new status will be reflected by the ok-to-test label.

I understand the commands that are listed here.

Details

Instructions for interacting with me using PR comments are available here. If you have questions or suggestions related to my behavior, please file an issue against the kubernetes-sigs/prow repository.

@k8s-ci-robot k8s-ci-robot added the size/XL Denotes a PR that changes 500-999 lines, ignoring generated files. label Mar 2, 2026
@abdallahsamabd abdallahsamabd marked this pull request as draft March 2, 2026 13:55
@k8s-ci-robot k8s-ci-robot added do-not-merge/work-in-progress Indicates that a PR should not merge because it is a work in progress. needs-rebase Indicates a PR cannot be merged because it has merge conflicts with HEAD. labels Mar 2, 2026
@abdallahsamabd abdallahsamabd changed the title [pluggable bbr] add integration tests for response plugin execution [bbr] add integration tests for response plugin execution Mar 9, 2026
@abdallahsamabd abdallahsamabd marked this pull request as ready for review March 9, 2026 11:15
@k8s-ci-robot k8s-ci-robot removed the do-not-merge/work-in-progress Indicates that a PR should not merge because it is a work in progress. label Mar 9, 2026
@k8s-ci-robot k8s-ci-robot added size/L Denotes a PR that changes 100-499 lines, ignoring generated files. and removed needs-rebase Indicates a PR cannot be merged because it has merge conflicts with HEAD. size/XL Denotes a PR that changes 500-999 lines, ignoring generated files. labels Mar 9, 2026
@abdallahsamabd abdallahsamabd marked this pull request as draft March 9, 2026 11:24
@k8s-ci-robot k8s-ci-robot added the do-not-merge/work-in-progress Indicates that a PR should not merge because it is a work in progress. label Mar 9, 2026
@abdallahsamabd abdallahsamabd marked this pull request as ready for review March 12, 2026 10:17
@k8s-ci-robot k8s-ci-robot removed the do-not-merge/work-in-progress Indicates that a PR should not merge because it is a work in progress. label Mar 12, 2026
@nirrozenbaum
Copy link
Copy Markdown
Contributor

/ok-to-test

@k8s-ci-robot k8s-ci-robot added ok-to-test Indicates a non-member PR verified by an org member that is safe to test. needs-rebase Indicates a PR cannot be merged because it has merge conflicts with HEAD. and removed needs-ok-to-test Indicates a PR that requires an org member to verify it is safe to test. labels Mar 12, 2026
@k8s-ci-robot k8s-ci-robot added size/L Denotes a PR that changes 100-499 lines, ignoring generated files. and removed size/XXL Denotes a PR that changes 1000+ lines, ignoring generated files. labels Mar 18, 2026
@k8s-ci-robot k8s-ci-robot added the needs-rebase Indicates a PR cannot be merged because it has merge conflicts with HEAD. label Mar 27, 2026
@nirrozenbaum
Copy link
Copy Markdown
Contributor

@abdallahsamabd can you please rebase?

Comment thread test/integration/bbr/harness.go Outdated
Comment on lines +118 to +124
if requestPlugins == nil {
modelToHeaderPlugin, err := plugins.NewBodyFieldToHeaderPlugin(handlers.ModelField, handlers.ModelHeader)
require.NoError(t, err, "failed to create body-field-to-header plugin")

baseModelPlugin := &basemodelextractor.BaseModelToHeaderPlugin{AdaptersStore: basemodelextractor.NewAdaptersStore()}
requestPlugins = []framework.RequestProcessor{modelToHeaderPlugin, baseModelPlugin}
}
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.

why do we need this part?

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.

Without it, the server would start with no request plugins at all, breaking basic model header extraction. The fallback ensures a working minimum request pipeline regardless of what the caller passes

Copy link
Copy Markdown
Contributor

@nirrozenbaum nirrozenbaum Mar 30, 2026

Choose a reason for hiding this comment

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

not sure I understand.
This function is called from few places. all these places instantiate request plugins and pass them as arg to this function.
it's the caller responsibility to pass plugins. we shouldn't fallback here.
I also double checked the code and all callers (3 places) are calling with plugins, as expected.

@k8s-ci-robot k8s-ci-robot removed the needs-rebase Indicates a PR cannot be merged because it has merge conflicts with HEAD. label Mar 29, 2026
Comment thread test/integration/bbr/harness.go Outdated
baseModelToHeaderPlugin := &basemodelextractor.BaseModelToHeaderPlugin{AdaptersStore: store}

return NewBBRHarnessWithPlugins(t, ctx, streaming, []framework.RequestProcessor{modelToHeaderPlugin, baseModelToHeaderPlugin})
return NewBBRHarnessWithPlugins(t, ctx, streaming, []framework.RequestProcessor{modelToHeaderPlugin, baseModelToHeaderPlugin}, nil)
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: pass empty slice, not nil.

Suggested change
return NewBBRHarnessWithPlugins(t, ctx, streaming, []framework.RequestProcessor{modelToHeaderPlugin, baseModelToHeaderPlugin}, nil)
return NewBBRHarnessWithPlugins(t, ctx, streaming, []framework.RequestProcessor{modelToHeaderPlugin, baseModelToHeaderPlugin}, []framework.ResponseProcessor{})

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

plugin := &bodyMutatingPlugin{fieldName: "injected", fieldValue: "test-value"}
baseModelToHeaderPlugin := &basemodelextractor.BaseModelToHeaderPlugin{AdaptersStore: basemodelextractor.NewAdaptersStore()}
h := NewBBRHarnessWithPlugins(t, ctx, false, []framework.RequestProcessor{plugin, baseModelToHeaderPlugin})
h := NewBBRHarnessWithPlugins(t, ctx, false, []framework.RequestProcessor{plugin, baseModelToHeaderPlugin}, nil)
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 we fix here too?

Suggested change
h := NewBBRHarnessWithPlugins(t, ctx, false, []framework.RequestProcessor{plugin, baseModelToHeaderPlugin}, nil)
h := NewBBRHarnessWithPlugins(t, ctx, false, []framework.RequestProcessor{plugin, baseModelToHeaderPlugin}, []framework.ResponseProcessor{})

plugin := &bodyMutatingPlugin{fieldName: "injected", fieldValue: "test-value"}
baseModelToHeaderPlugin := &basemodelextractor.BaseModelToHeaderPlugin{AdaptersStore: basemodelextractor.NewAdaptersStore()}
h := NewBBRHarnessWithPlugins(t, ctx, true, []framework.RequestProcessor{plugin, baseModelToHeaderPlugin})
h := NewBBRHarnessWithPlugins(t, ctx, true, []framework.RequestProcessor{plugin, baseModelToHeaderPlugin}, nil)
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.

ditto:

Suggested change
h := NewBBRHarnessWithPlugins(t, ctx, true, []framework.RequestProcessor{plugin, baseModelToHeaderPlugin}, nil)
h := NewBBRHarnessWithPlugins(t, ctx, true, []framework.RequestProcessor{plugin, baseModelToHeaderPlugin}, []framework.ResponseProcessor{})

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

Comment thread test/integration/bbr/hermetic_test.go Outdated

// TestResponsePlugins_NoPlugins_Unary validates that when no response plugins
// are configured, the response body is passed through without mutation.
func TestResponsePlugins_NoPlugins_Unary(t *testing.T) {
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.

this is already tested in TestBodyBasedRouting, isn't it?
if something is missing from that test - can be extend the test instead of reimplementing it?

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

Comment thread test/integration/bbr/hermetic_test.go Outdated

// TestResponsePlugins_Unary validates that response plugins can mutate the
// response body in unary (non-streaming) mode over a real gRPC ext_proc stream.
func TestResponsePlugins_Unary(t *testing.T) {
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 we reuse TestBodyBasedRouting test suite?
e.g., extend it to have createHarnessFunc func() *BBRHarness. then in different tests call it differently (in some case with defaults, in other with your response test plugins).
ideally we should have single test suite driving the tests, and just different configurations.

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

Comment thread test/integration/bbr/hermetic_test.go Outdated
Comment on lines +47 to +48
guardrailPlugin := &testResponsePlugin{
name: "guardrail",
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 we call it with a name making it clear it's a response plugin?
e.g., responsePlugin?

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

Comment thread test/integration/bbr/hermetic_test.go Outdated
}
require.NoError(t, err, "unexpected error during request processing")

responses, err := integration.StreamedRequest(t, h.Client, tc.reqs, expectedCount)
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 there's a bit confusion in the PR.
this test is for unary request. it should use a single request object and call function integration.SendRequest as it was before.

integration/StreamedRequest is for testing streaming case.

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 reason we switched to StreamedRequest is that testing the response phase (ResponseHeaders → ResponseBody) requires sending 3 messages on the same gRPC stream, which SendRequest can't do.

will be fixed:
refactor: restore TestBodyBasedRouting to use SendRequest for request-phase-only tests, and create a separate TestResponsePlugins using StreamedRequest for full lifecycle tests.

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

Follow-up to kubernetes-sigs#2354 / PR kubernetes-sigs#2369. Exercises the full Process loop
(request body -> response headers -> response body) to validate that
response plugins can mutate the response body over a real gRPC
ext_proc stream.

Covers:
- Unary mode: response plugin mutates the response body
- Unary mode: no response plugins results in passthrough behavior

Signed-off-by: Abdallah Samara <abdallahsamabd@gmail.com>
Made-with: Cursor
Comment on lines +166 to +167
respHeaders,
respBodyReq(map[string]any{"choices": []any{map[string]any{"text": "Hello!"}}}),
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.

non blocking - maybe it's worth extracting these to integration package and expose as public functions?
we might reuse them in different tests.

Copy link
Copy Markdown
Contributor

@nirrozenbaum nirrozenbaum left a comment

Choose a reason for hiding this comment

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

/lgtm
/approve

left one minor nit. no need to block the PR on that.

@k8s-ci-robot k8s-ci-robot added the lgtm "Looks good to me", indicates that a PR is ready to be merged. label Mar 31, 2026
@k8s-ci-robot
Copy link
Copy Markdown
Contributor

[APPROVALNOTIFIER] This PR is APPROVED

This pull-request has been approved by: abdallahsamabd, nirrozenbaum

The full list of commands accepted by this bot can be found here.

The pull request process is described here

Details Needs approval from an approver in each of these files:

Approvers can indicate their approval by writing /approve in a comment
Approvers can cancel approval by writing /approve cancel in a comment

@k8s-ci-robot k8s-ci-robot added the approved Indicates a PR has been approved by an approver from all required OWNERS files. label Mar 31, 2026
@k8s-ci-robot k8s-ci-robot merged commit 94fb67c into kubernetes-sigs:main Mar 31, 2026
11 checks passed
nirrozenbaum pushed a commit to llm-d/llm-d-inference-payload-processor that referenced this pull request Apr 28, 2026
…gateway-api-inference-extension#2450)

Follow-up to kubernetes-sigs/gateway-api-inference-extension#2354 / PR kubernetes-sigs/gateway-api-inference-extension#2369. Exercises the full Process loop
(request body -> response headers -> response body) to validate that
response plugins can mutate the response body over a real gRPC
ext_proc stream.

Covers:
- Unary mode: response plugin mutates the response body
- Unary mode: no response plugins results in passthrough behavior


Made-with: Cursor

Signed-off-by: Abdallah Samara <abdallahsamabd@gmail.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

approved Indicates a PR has been approved by an approver from all required OWNERS files. cncf-cla: yes Indicates the PR's author has signed the CNCF CLA. lgtm "Looks good to me", indicates that a PR is ready to be merged. ok-to-test Indicates a non-member PR verified by an org member that is safe to test. size/L Denotes a PR that changes 100-499 lines, ignoring generated files.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[pluggable bbr] add integration tests for response plugin execution

3 participants