new-api/relay/common/relay_info_test.go
zizi 67c173cbcb Preserve Muse relay tags without hijacking new-api request ids
Muse now forwards business context alongside each per-user token request, so new-api needs to ingest those tags for relay selection, billing audit, and log correlation without breaking its own internal request-id semantics.

This adds a dedicated Muse request-context middleware, carries Muse tags into RelayInfo, and appends them into consume/error log payloads. Muse request ids are stored as separate business metadata instead of overwriting the internal request id used for pre-consume idempotency.

Constraint: The relay layer must keep working for both /v1 and /v1beta request paths
Constraint: Client-supplied Muse request ids cannot replace the internal request id used by new-api billing/idempotency logic
Rejected: Overwriting common.RequestIdKey with X-Request-Id | mixes external business ids with internal idempotency ids
Rejected: Adding new log table columns in phase 1 | this task only needs relay/log propagation, so other JSON is the minimal compatible path
Confidence: medium
Scope-risk: moderate
Reversibility: clean
Directive: Keep Muse business identifiers in dedicated context keys and log other fields; do not reuse internal request-id slots for external correlation ids
Tested: go test ./middleware ./relay/common ./controller -run MuseRequestContext -count=1
Tested: go test ./middleware ./relay/common ./controller -count=1
Not-tested: Live relay call from Muse into a running new-api instance
2026-04-17 17:14:41 +08:00

74 lines
2.7 KiB
Go

package common
import (
"net/http"
"net/http/httptest"
"testing"
"github.com/QuantumNous/new-api/common"
"github.com/QuantumNous/new-api/constant"
"github.com/QuantumNous/new-api/dto"
"github.com/QuantumNous/new-api/types"
"github.com/gin-gonic/gin"
"github.com/stretchr/testify/require"
)
func TestRelayInfoGetFinalRequestRelayFormatPrefersExplicitFinal(t *testing.T) {
info := &RelayInfo{
RelayFormat: types.RelayFormatOpenAI,
RequestConversionChain: []types.RelayFormat{types.RelayFormatOpenAI, types.RelayFormatClaude},
FinalRequestRelayFormat: types.RelayFormatOpenAIResponses,
}
require.Equal(t, types.RelayFormat(types.RelayFormatOpenAIResponses), info.GetFinalRequestRelayFormat())
}
func TestRelayInfoGetFinalRequestRelayFormatFallsBackToConversionChain(t *testing.T) {
info := &RelayInfo{
RelayFormat: types.RelayFormatOpenAI,
RequestConversionChain: []types.RelayFormat{types.RelayFormatOpenAI, types.RelayFormatClaude},
}
require.Equal(t, types.RelayFormat(types.RelayFormatClaude), info.GetFinalRequestRelayFormat())
}
func TestRelayInfoGetFinalRequestRelayFormatFallsBackToRelayFormat(t *testing.T) {
info := &RelayInfo{
RelayFormat: types.RelayFormatGemini,
}
require.Equal(t, types.RelayFormat(types.RelayFormatGemini), info.GetFinalRequestRelayFormat())
}
func TestRelayInfoGetFinalRequestRelayFormatNilReceiver(t *testing.T) {
var info *RelayInfo
require.Equal(t, types.RelayFormat(""), info.GetFinalRequestRelayFormat())
}
func TestMuseRequestContextGenRelayInfoCopiesMuseFields(t *testing.T) {
gin.SetMode(gin.TestMode)
recorder := httptest.NewRecorder()
ctx, _ := gin.CreateTestContext(recorder)
ctx.Request = httptest.NewRequest(http.MethodPost, "/v1/chat/completions", nil)
ctx.Set(common.RequestIdKey, "req-1")
common.SetContextKey(ctx, constant.ContextKeyUserId, 11)
common.SetContextKey(ctx, constant.ContextKeyUserGroup, "default")
common.SetContextKey(ctx, constant.ContextKeyUsingGroup, "default")
common.SetContextKey(ctx, constant.ContextKeyMuseUserID, "muse-user-1")
common.SetContextKey(ctx, constant.ContextKeyMuseWorkID, "42")
common.SetContextKey(ctx, constant.ContextKeyMuseRequestID, "muse-req-1")
common.SetContextKey(ctx, constant.ContextKeyMuseScene, "suggestion_generation")
common.SetContextKey(ctx, constant.ContextKeyMuseTraceID, "trace-1")
info := GenRelayInfoOpenAI(ctx, &dto.GeneralOpenAIRequest{})
require.NotNil(t, info)
require.Equal(t, "req-1", info.RequestId)
require.Equal(t, "muse-user-1", info.MuseUserID)
require.Equal(t, "42", info.MuseWorkID)
require.Equal(t, "muse-req-1", info.MuseRequestID)
require.Equal(t, "suggestion_generation", info.MuseScene)
require.Equal(t, "trace-1", info.MuseTraceID)
}