[bbr] add integration tests for response plugin execution#2450
[bbr] add integration tests for response plugin execution#2450k8s-ci-robot merged 1 commit intokubernetes-sigs:mainfrom
Conversation
|
@abdallahsamabd: The label(s) DetailsIn response to this:
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. |
✅ Deploy Preview for gateway-api-inference-extension ready!
To edit notification comments on pull requests, go to your Netlify project configuration. |
|
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 Regular contributors should join the org to skip this step. Once the patch is verified, the new status will be reflected by the I understand the commands that are listed here. DetailsInstructions 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. |
8507b0a to
2277e1f
Compare
2277e1f to
1e257e1
Compare
|
/ok-to-test |
1e257e1 to
a2697c8
Compare
2ee1c8e to
8bfb3d6
Compare
|
@abdallahsamabd can you please rebase? |
| 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} | ||
| } |
There was a problem hiding this comment.
why do we need this part?
There was a problem hiding this comment.
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
There was a problem hiding this comment.
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.
8bfb3d6 to
26b612b
Compare
26b612b to
a71a09a
Compare
| baseModelToHeaderPlugin := &basemodelextractor.BaseModelToHeaderPlugin{AdaptersStore: store} | ||
|
|
||
| return NewBBRHarnessWithPlugins(t, ctx, streaming, []framework.RequestProcessor{modelToHeaderPlugin, baseModelToHeaderPlugin}) | ||
| return NewBBRHarnessWithPlugins(t, ctx, streaming, []framework.RequestProcessor{modelToHeaderPlugin, baseModelToHeaderPlugin}, nil) |
There was a problem hiding this comment.
nit: pass empty slice, not nil.
| return NewBBRHarnessWithPlugins(t, ctx, streaming, []framework.RequestProcessor{modelToHeaderPlugin, baseModelToHeaderPlugin}, nil) | |
| return NewBBRHarnessWithPlugins(t, ctx, streaming, []framework.RequestProcessor{modelToHeaderPlugin, baseModelToHeaderPlugin}, []framework.ResponseProcessor{}) |
a71a09a to
f7876e0
Compare
| 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) |
There was a problem hiding this comment.
can we fix here too?
| 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) |
There was a problem hiding this comment.
ditto:
| h := NewBBRHarnessWithPlugins(t, ctx, true, []framework.RequestProcessor{plugin, baseModelToHeaderPlugin}, nil) | |
| h := NewBBRHarnessWithPlugins(t, ctx, true, []framework.RequestProcessor{plugin, baseModelToHeaderPlugin}, []framework.ResponseProcessor{}) |
f7876e0 to
b432abf
Compare
|
|
||
| // 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) { |
There was a problem hiding this comment.
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?
|
|
||
| // 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) { |
There was a problem hiding this comment.
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.
b432abf to
3b994d6
Compare
| guardrailPlugin := &testResponsePlugin{ | ||
| name: "guardrail", |
There was a problem hiding this comment.
can we call it with a name making it clear it's a response plugin?
e.g., responsePlugin?
3b994d6 to
a9173f1
Compare
| } | ||
| require.NoError(t, err, "unexpected error during request processing") | ||
|
|
||
| responses, err := integration.StreamedRequest(t, h.Client, tc.reqs, expectedCount) |
There was a problem hiding this comment.
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.
There was a problem hiding this comment.
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.
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
a9173f1 to
4bf8870
Compare
| respHeaders, | ||
| respBodyReq(map[string]any{"choices": []any{map[string]any{"text": "Hello!"}}}), |
There was a problem hiding this comment.
non blocking - maybe it's worth extracting these to integration package and expose as public functions?
we might reuse them in different tests.
nirrozenbaum
left a comment
There was a problem hiding this comment.
/lgtm
/approve
left one minor nit. no need to block the PR on that.
|
[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 DetailsNeeds approval from an approver in each of these files:
Approvers can indicate their approval by writing |
…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>
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:
These tests exercise the full
Processloop (request body → response headers → response body) to ensure end-to-end correctness.Also adds:
NewBBRHarnessWithPluginshelper to support configuring request/response plugins in test harnessesExpectResponseHeadersPassThrough,ExpectResponseBodyPassThrough,ExpectResponseBodyMutation)Which issue(s) this PR fixes:
Fixes #2449
Does this PR introduce a user-facing change?: