Skip to content

Commit f783d51

Browse files
committed
test: add unit tests for ProcessRequestHeaders and ProcessRequestBody methods in responsesProcessorUpstreamFilter
Signed-off-by: Sivanantham Chinnaiyan <sivanantham.chinnaiyan@ideas2it.com>
1 parent a588b67 commit f783d51

File tree

1 file changed

+149
-0
lines changed

1 file changed

+149
-0
lines changed

internal/extproc/responses_processor_test.go

Lines changed: 149 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,151 @@ 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() {
370+
_, _ = p.ProcessRequestBody(t.Context(), &extprocv3.HttpBody{})
371+
})
372+
}
373+
374+
// processorFunc is a lightweight test helper implementing Processor via function fields.
375+
type processorFunc struct {
376+
processRequestHeadersFunc func(context.Context, *corev3.HeaderMap) (*extprocv3.ProcessingResponse, error)
377+
processRequestBodyFunc func(context.Context, *extprocv3.HttpBody) (*extprocv3.ProcessingResponse, error)
378+
processResponseHeadersFunc func(context.Context, *corev3.HeaderMap) (*extprocv3.ProcessingResponse, error)
379+
processResponseBodyFunc func(context.Context, *extprocv3.HttpBody) (*extprocv3.ProcessingResponse, error)
380+
setBackendFunc func(context.Context, *filterapi.Backend, backendauth.Handler, Processor) error
381+
}
382+
383+
func (p processorFunc) ProcessRequestHeaders(ctx context.Context, h *corev3.HeaderMap) (*extprocv3.ProcessingResponse, error) {
384+
if p.processRequestHeadersFunc != nil {
385+
return p.processRequestHeadersFunc(ctx, h)
386+
}
387+
return &extprocv3.ProcessingResponse{Response: &extprocv3.ProcessingResponse_RequestHeaders{}}, nil
388+
}
389+
390+
func (p processorFunc) ProcessRequestBody(ctx context.Context, b *extprocv3.HttpBody) (*extprocv3.ProcessingResponse, error) {
391+
if p.processRequestBodyFunc != nil {
392+
return p.processRequestBodyFunc(ctx, b)
393+
}
394+
return &extprocv3.ProcessingResponse{Response: &extprocv3.ProcessingResponse_RequestBody{}}, nil
395+
}
396+
397+
func (p processorFunc) ProcessResponseHeaders(ctx context.Context, h *corev3.HeaderMap) (*extprocv3.ProcessingResponse, error) {
398+
if p.processResponseHeadersFunc != nil {
399+
return p.processResponseHeadersFunc(ctx, h)
400+
}
401+
return &extprocv3.ProcessingResponse{Response: &extprocv3.ProcessingResponse_ResponseHeaders{}}, nil
402+
}
403+
404+
func (p processorFunc) ProcessResponseBody(ctx context.Context, b *extprocv3.HttpBody) (*extprocv3.ProcessingResponse, error) {
405+
if p.processResponseBodyFunc != nil {
406+
return p.processResponseBodyFunc(ctx, b)
407+
}
408+
return &extprocv3.ProcessingResponse{Response: &extprocv3.ProcessingResponse_ResponseBody{}}, nil
409+
}
410+
411+
func (p processorFunc) SetBackend(ctx context.Context, be *filterapi.Backend, h backendauth.Handler, rp Processor) error {
412+
if p.setBackendFunc != nil {
413+
return p.setBackendFunc(ctx, be, h, rp)
414+
}
415+
return nil
416+
}
417+
418+
func Test_responsesProcessorRouterFilter_ResponseDelegation(t *testing.T) {
419+
t.Run("passThrough when upstreamFilter nil", func(t *testing.T) {
420+
// router filter with nil upstreamFilter should delegate to passThroughProcessor
421+
r := &responsesProcessorRouterFilter{}
422+
423+
// ProcessResponseHeaders should return a ResponseHeaders-type ProcessingResponse
424+
hdrResp, err := r.ProcessResponseHeaders(t.Context(), nil)
425+
require.NoError(t, err)
426+
require.NotNil(t, hdrResp)
427+
_, ok := hdrResp.Response.(*extprocv3.ProcessingResponse_ResponseHeaders)
428+
require.True(t, ok)
429+
430+
// ProcessResponseBody should return a ResponseBody-type ProcessingResponse
431+
bodyResp, err := r.ProcessResponseBody(t.Context(), &extprocv3.HttpBody{})
432+
require.NoError(t, err)
433+
require.NotNil(t, bodyResp)
434+
_, ok = bodyResp.Response.(*extprocv3.ProcessingResponse_ResponseBody)
435+
require.True(t, ok)
436+
})
437+
438+
t.Run("delegates to upstreamFilter when set", func(t *testing.T) {
439+
// Create a spy upstream filter that records calls and returns distinct responses
440+
called := struct{ hdr, body bool }{}
441+
// Replace methods via function values by wrapping an implementation of Processor
442+
var upstream Processor = processorFunc{
443+
processResponseHeadersFunc: func(context.Context, *corev3.HeaderMap) (*extprocv3.ProcessingResponse, error) {
444+
called.hdr = true
445+
return &extprocv3.ProcessingResponse{Response: &extprocv3.ProcessingResponse_ResponseHeaders{}}, nil
446+
},
447+
processResponseBodyFunc: func(context.Context, *extprocv3.HttpBody) (*extprocv3.ProcessingResponse, error) {
448+
called.body = true
449+
return &extprocv3.ProcessingResponse{Response: &extprocv3.ProcessingResponse_ResponseBody{}}, nil
450+
},
451+
}
452+
453+
r := &responsesProcessorRouterFilter{upstreamFilter: upstream}
454+
455+
hdrResp, err := r.ProcessResponseHeaders(t.Context(), nil)
456+
require.NoError(t, err)
457+
require.True(t, called.hdr)
458+
_, ok := hdrResp.Response.(*extprocv3.ProcessingResponse_ResponseHeaders)
459+
require.True(t, ok)
460+
461+
bodyResp, err := r.ProcessResponseBody(t.Context(), &extprocv3.HttpBody{})
462+
require.NoError(t, err)
463+
require.True(t, called.body)
464+
_, ok = bodyResp.Response.(*extprocv3.ProcessingResponse_ResponseBody)
465+
require.True(t, ok)
466+
})
467+
}

0 commit comments

Comments
 (0)