Files
sub2api/backend/internal/service/openai_responses_namespace_test.go
T

114 lines
5.5 KiB
Go

package service
import (
"strconv"
"testing"
"github.com/stretchr/testify/require"
"github.com/tidwall/gjson"
)
func TestShouldFlattenOpenAIResponsesNamespaces(t *testing.T) {
oauth := &Account{Platform: PlatformOpenAI, Type: AccountTypeOAuth}
apiKey := &Account{Platform: PlatformOpenAI, Type: AccountTypeAPIKey}
grokOAuth := &Account{Platform: PlatformGrok, Type: AccountTypeOAuth}
tests := []struct {
name string
account *Account
transport OpenAIUpstreamTransport
passthroughEnabled bool
want bool
}{
{name: "oauth_http", account: oauth, transport: OpenAIUpstreamTransportHTTPSSE, want: true},
{name: "oauth_http_passthrough", account: oauth, transport: OpenAIUpstreamTransportHTTPSSE, passthroughEnabled: true, want: true},
// WSv2 出口原样转发上游事件、不做回程还原,摊平会让客户端收到无法匹配的平名。
{name: "oauth_wsv2", account: oauth, transport: OpenAIUpstreamTransportResponsesWebsocketV2, want: false},
// 透传账号先于 WSv2 分支经 HTTP 转发返回,仍需摊平。
{name: "oauth_wsv2_passthrough", account: oauth, transport: OpenAIUpstreamTransportResponsesWebsocketV2, passthroughEnabled: true, want: true},
{name: "apikey_http", account: apiKey, transport: OpenAIUpstreamTransportHTTPSSE, want: false},
{name: "grok_oauth_http", account: grokOAuth, transport: OpenAIUpstreamTransportHTTPSSE, want: false},
{name: "nil_account", account: nil, transport: OpenAIUpstreamTransportHTTPSSE, want: false},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
require.Equal(t, tt.want, shouldFlattenOpenAIResponsesNamespaces(tt.account, tt.transport, tt.passthroughEnabled))
})
}
}
func TestShouldStripOpenAIResponsesInputNamespaces(t *testing.T) {
oauth := &Account{Platform: PlatformOpenAI, Type: AccountTypeOAuth}
apiKey := &Account{Platform: PlatformOpenAI, Type: AccountTypeAPIKey}
setupToken := &Account{Platform: PlatformOpenAI, Type: AccountTypeSetupToken}
grokOAuth := &Account{Platform: PlatformGrok, Type: AccountTypeOAuth}
tests := []struct {
name string
account *Account
transport OpenAIUpstreamTransport
passthroughEnabled bool
want bool
}{
{name: "oauth_http", account: oauth, transport: OpenAIUpstreamTransportHTTPSSE, want: true},
{name: "apikey_http", account: apiKey, transport: OpenAIUpstreamTransportHTTPSSE, want: true},
{name: "oauth_wsv2", account: oauth, transport: OpenAIUpstreamTransportResponsesWebsocketV2, want: false},
{name: "apikey_wsv2", account: apiKey, transport: OpenAIUpstreamTransportResponsesWebsocketV2, want: false},
{name: "oauth_wsv2_passthrough", account: oauth, transport: OpenAIUpstreamTransportResponsesWebsocketV2, passthroughEnabled: true, want: true},
{name: "apikey_wsv2_passthrough", account: apiKey, transport: OpenAIUpstreamTransportResponsesWebsocketV2, passthroughEnabled: true, want: true},
{name: "setup_token_http", account: setupToken, transport: OpenAIUpstreamTransportHTTPSSE, want: false},
{name: "grok_oauth_http", account: grokOAuth, transport: OpenAIUpstreamTransportHTTPSSE, want: false},
{name: "nil_account", account: nil, transport: OpenAIUpstreamTransportHTTPSSE, want: false},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
require.Equal(t, tt.want, shouldStripOpenAIResponsesInputNamespaces(tt.account, tt.transport, tt.passthroughEnabled))
})
}
}
func TestStripOpenAIResponsesInputNamespaces(t *testing.T) {
body := []byte(`{
"meta":9007199254740993,
"scientific":1.25e+42,
"escaped":"line\\n\\u003ctag\\u003e",
"tools":[{"type":"function","name":"keep","namespace":"tool-namespace"}],
"input":[
{"type":"function_call","namespace":"n0","name":"one","content":{"namespace":"nested"},"large":9007199254740993},
{"type":"message","namespace":"n1","content":[{"type":"input_text","text":"hello","namespace":"nested-content"}]},
{"type":"custom_tool_call","namespace":"n2","input":"{}"},
{"type":"function_call_output","namespace":"n3","output":"ok"},
{"type":"item","namespace":"n4"},
{"type":"item","namespace":"n5"},
{"type":"item","namespace":"n6"},
{"type":"item","namespace":"n7"}
]
}`)
stripped, err := stripOpenAIResponsesInputNamespaces(body)
require.NoError(t, err)
for index := 0; index < 8; index++ {
require.False(t, gjson.GetBytes(stripped, "input."+strconv.Itoa(index)+".namespace").Exists())
}
require.Equal(t, "nested", gjson.GetBytes(stripped, "input.0.content.namespace").String())
require.Equal(t, "nested-content", gjson.GetBytes(stripped, "input.1.content.0.namespace").String())
require.Equal(t, "tool-namespace", gjson.GetBytes(stripped, "tools.0.namespace").String())
require.Equal(t, gjson.GetBytes(body, "meta").Raw, gjson.GetBytes(stripped, "meta").Raw)
require.Equal(t, gjson.GetBytes(body, "scientific").Raw, gjson.GetBytes(stripped, "scientific").Raw)
require.Equal(t, gjson.GetBytes(body, "escaped").Raw, gjson.GetBytes(stripped, "escaped").Raw)
require.Equal(t, gjson.GetBytes(body, "input.0.large").Raw, gjson.GetBytes(stripped, "input.0.large").Raw)
}
func TestStripOpenAIResponsesInputNamespacesLeavesOtherShapesByteExact(t *testing.T) {
tests := [][]byte{
[]byte(`{"input":"text","namespace":"top-level"}`),
[]byte(`{"input":{"namespace":"single-object"}}`),
[]byte(`{"input":[{"content":{"namespace":"nested-only"}}],"tools":[{"namespace":"keep"}]}`),
}
for _, body := range tests {
stripped, err := stripOpenAIResponsesInputNamespaces(body)
require.NoError(t, err)
require.Equal(t, body, stripped)
}
}