Skip to content

Commit 0906299

Browse files
committed
test: add unit tests for ProcessRequestHeaders and ProcessRequestBody methods in responsesProcessorUpstreamFilter
Signed-off-by: Sivanantham Chinnaiyan <[email protected]>
1 parent a588b67 commit 0906299

File tree

1 file changed

+143
-0
lines changed

1 file changed

+143
-0
lines changed

internal/extproc/responses_processor_test.go

Lines changed: 143 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@ import (
1818
"github.com/stretchr/testify/require"
1919

2020
"github.com/envoyproxy/ai-gateway/internal/apischema/openai"
21+
"github.com/envoyproxy/ai-gateway/internal/extproc/backendauth"
2122
"github.com/envoyproxy/ai-gateway/internal/extproc/translator"
2223
"github.com/envoyproxy/ai-gateway/internal/filterapi"
2324
"github.com/envoyproxy/ai-gateway/internal/internalapi"
@@ -316,3 +317,145 @@ func Test_responsesProcessorUpstreamFilter_SetBackend(t *testing.T) {
316317
require.True(t, p2.stream)
317318
require.NotNil(t, p2.translator)
318319
}
320+
321+
func Test_responsesProcessorUpstreamFilter_ProcessRequestHeaders(t *testing.T) {
322+
t.Run("ok sets header/body mutation and metrics", func(t *testing.T) {
323+
// prepare translator to expect raw body and return header/body mutation
324+
mt := &mockResponsesTranslator{t: t, expBody: []byte(`{"model":"m1"}`)}
325+
expHeadMut := &extprocv3.HeaderMutation{
326+
SetHeaders: []*corev3.HeaderValueOption{{Header: &corev3.HeaderValue{Key: "x-test", Value: "v"}}},
327+
}
328+
expBodyMut := &extprocv3.BodyMutation{}
329+
mt.retHeaderMutation = expHeadMut
330+
mt.retBodyMutation = expBodyMut
331+
332+
mm := &mockResponsesMetrics{}
333+
334+
// build upstream filter with original request body present (as router would set)
335+
headers := map[string]string{":path": "/foo"}
336+
p := &responsesProcessorUpstreamFilter{translator: mt, metrics: mm, requestHeaders: headers, config: &processorConfig{}}
337+
p.originalRequestBodyRaw = []byte(`{"model":"m1"}`)
338+
p.originalRequestBody = &openai.ResponseRequest{Model: "m1"}
339+
340+
res, err := p.ProcessRequestHeaders(t.Context(), nil)
341+
require.NoError(t, err)
342+
require.NotNil(t, res)
343+
rh := res.Response.(*extprocv3.ProcessingResponse_RequestHeaders).RequestHeaders.Response
344+
require.Equal(t, expHeadMut, rh.HeaderMutation)
345+
require.Equal(t, expBodyMut, rh.BodyMutation)
346+
// metrics should have been started and models set
347+
require.True(t, mm.started)
348+
require.Equal(t, internalapi.OriginalModel("m1"), mm.originalModel)
349+
require.Equal(t, internalapi.RequestModel("m1"), mm.requestModel)
350+
})
351+
352+
t.Run("translator error records failure", func(t *testing.T) {
353+
mt := &mockResponsesTranslator{t: t}
354+
mt.retErr = errors.New("translate fail")
355+
mm := &mockResponsesMetrics{}
356+
// Ensure logger is non-nil so deferred error logging doesn't panic
357+
p := &responsesProcessorUpstreamFilter{translator: mt, metrics: mm, requestHeaders: map[string]string{}, config: &processorConfig{}, logger: slog.Default()}
358+
p.originalRequestBodyRaw = []byte(`{"model":"m1"}`)
359+
p.originalRequestBody = &openai.ResponseRequest{Model: "m1"}
360+
361+
_, err := p.ProcessRequestHeaders(t.Context(), nil)
362+
require.Error(t, err)
363+
mm.RequireRequestFailure(t)
364+
})
365+
}
366+
367+
func Test_responsesProcessorUpstreamFilter_ProcessRequestBody_panics(t *testing.T) {
368+
p := &responsesProcessorUpstreamFilter{}
369+
require.Panics(t, func() { p.ProcessRequestBody(t.Context(), &extprocv3.HttpBody{}) })
370+
}
371+
372+
// processorFunc is a lightweight test helper implementing Processor via function fields.
373+
type processorFunc struct {
374+
processRequestHeadersFunc func(context.Context, *corev3.HeaderMap) (*extprocv3.ProcessingResponse, error)
375+
processRequestBodyFunc func(context.Context, *extprocv3.HttpBody) (*extprocv3.ProcessingResponse, error)
376+
processResponseHeadersFunc func(context.Context, *corev3.HeaderMap) (*extprocv3.ProcessingResponse, error)
377+
processResponseBodyFunc func(context.Context, *extprocv3.HttpBody) (*extprocv3.ProcessingResponse, error)
378+
setBackendFunc func(context.Context, *filterapi.Backend, backendauth.Handler, Processor) error
379+
}
380+
381+
func (p processorFunc) ProcessRequestHeaders(ctx context.Context, h *corev3.HeaderMap) (*extprocv3.ProcessingResponse, error) {
382+
if p.processRequestHeadersFunc != nil {
383+
return p.processRequestHeadersFunc(ctx, h)
384+
}
385+
return &extprocv3.ProcessingResponse{Response: &extprocv3.ProcessingResponse_RequestHeaders{}}, nil
386+
}
387+
func (p processorFunc) ProcessRequestBody(ctx context.Context, b *extprocv3.HttpBody) (*extprocv3.ProcessingResponse, error) {
388+
if p.processRequestBodyFunc != nil {
389+
return p.processRequestBodyFunc(ctx, b)
390+
}
391+
return &extprocv3.ProcessingResponse{Response: &extprocv3.ProcessingResponse_RequestBody{}}, nil
392+
}
393+
func (p processorFunc) ProcessResponseHeaders(ctx context.Context, h *corev3.HeaderMap) (*extprocv3.ProcessingResponse, error) {
394+
if p.processResponseHeadersFunc != nil {
395+
return p.processResponseHeadersFunc(ctx, h)
396+
}
397+
return &extprocv3.ProcessingResponse{Response: &extprocv3.ProcessingResponse_ResponseHeaders{}}, nil
398+
}
399+
func (p processorFunc) ProcessResponseBody(ctx context.Context, b *extprocv3.HttpBody) (*extprocv3.ProcessingResponse, error) {
400+
if p.processResponseBodyFunc != nil {
401+
return p.processResponseBodyFunc(ctx, b)
402+
}
403+
return &extprocv3.ProcessingResponse{Response: &extprocv3.ProcessingResponse_ResponseBody{}}, nil
404+
}
405+
func (p processorFunc) SetBackend(ctx context.Context, be *filterapi.Backend, h backendauth.Handler, rp Processor) error {
406+
if p.setBackendFunc != nil {
407+
return p.setBackendFunc(ctx, be, h, rp)
408+
}
409+
return nil
410+
}
411+
412+
func Test_responsesProcessorRouterFilter_ResponseDelegation(t *testing.T) {
413+
t.Run("passThrough when upstreamFilter nil", func(t *testing.T) {
414+
// router filter with nil upstreamFilter should delegate to passThroughProcessor
415+
r := &responsesProcessorRouterFilter{}
416+
417+
// ProcessResponseHeaders should return a ResponseHeaders-type ProcessingResponse
418+
hdrResp, err := r.ProcessResponseHeaders(t.Context(), nil)
419+
require.NoError(t, err)
420+
require.NotNil(t, hdrResp)
421+
_, ok := hdrResp.Response.(*extprocv3.ProcessingResponse_ResponseHeaders)
422+
require.True(t, ok)
423+
424+
// ProcessResponseBody should return a ResponseBody-type ProcessingResponse
425+
bodyResp, err := r.ProcessResponseBody(t.Context(), &extprocv3.HttpBody{})
426+
require.NoError(t, err)
427+
require.NotNil(t, bodyResp)
428+
_, ok = bodyResp.Response.(*extprocv3.ProcessingResponse_ResponseBody)
429+
require.True(t, ok)
430+
})
431+
432+
t.Run("delegates to upstreamFilter when set", func(t *testing.T) {
433+
// Create a spy upstream filter that records calls and returns distinct responses
434+
called := struct{ hdr, body bool }{}
435+
// Replace methods via function values by wrapping an implementation of Processor
436+
var upstream Processor = processorFunc{
437+
processResponseHeadersFunc: func(context.Context, *corev3.HeaderMap) (*extprocv3.ProcessingResponse, error) {
438+
called.hdr = true
439+
return &extprocv3.ProcessingResponse{Response: &extprocv3.ProcessingResponse_ResponseHeaders{}}, nil
440+
},
441+
processResponseBodyFunc: func(context.Context, *extprocv3.HttpBody) (*extprocv3.ProcessingResponse, error) {
442+
called.body = true
443+
return &extprocv3.ProcessingResponse{Response: &extprocv3.ProcessingResponse_ResponseBody{}}, nil
444+
},
445+
}
446+
447+
r := &responsesProcessorRouterFilter{upstreamFilter: upstream}
448+
449+
hdrResp, err := r.ProcessResponseHeaders(t.Context(), nil)
450+
require.NoError(t, err)
451+
require.True(t, called.hdr)
452+
_, ok := hdrResp.Response.(*extprocv3.ProcessingResponse_ResponseHeaders)
453+
require.True(t, ok)
454+
455+
bodyResp, err := r.ProcessResponseBody(t.Context(), &extprocv3.HttpBody{})
456+
require.NoError(t, err)
457+
require.True(t, called.body)
458+
_, ok = bodyResp.Response.(*extprocv3.ProcessingResponse_ResponseBody)
459+
require.True(t, ok)
460+
})
461+
}

0 commit comments

Comments
 (0)