Files
sub2api/backend/internal/service/openai_reasoning_effort_policy_test.go
T
李建琦 6d655c9903
Release / update-version (push) Has been cancelled
Release / build-frontend (push) Has been cancelled
Release / release (push) Has been cancelled
Release / sync-version-file (push) Has been cancelled
CI / shell (push) Canceled after 0s
CI / test (push) Canceled after 0s
CI / frontend (push) Canceled after 0s
CI / golangci-lint (push) Canceled after 0s
Security Scan / backend-security (push) Canceled after 0s
Security Scan / frontend-security (push) Canceled after 0s
Sub2API v1.0 - AI API 网关(二开初始版本,基于上游 Wei-Shaw/sub2api)
2026-08-21 18:30:13 +08:00

138 lines
6.1 KiB
Go

package service
import (
"context"
"testing"
"github.com/stretchr/testify/require"
"github.com/tidwall/gjson"
)
func TestNormalizeMaxReasoningEffort(t *testing.T) {
tests := []struct {
name string
in string
want string
}{
{name: "empty", in: "", want: ""},
{name: "separator", in: "x-high", want: "xhigh"},
{name: "max is distinct", in: "max", want: "max"},
{name: "none is unsupported", in: "none", want: ""},
{name: "invalid", in: "banana", want: ""},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
require.Equal(t, tt.want, NormalizeMaxReasoningEffort(tt.in))
})
}
}
func TestNormalizeReasoningEffortMappings(t *testing.T) {
t.Run("canonicalizes fixed OpenAI values", func(t *testing.T) {
for _, platform := range []string{PlatformOpenAI, PlatformComposite} {
got, err := NormalizeReasoningEffortMappings(platform, []ReasoningEffortMapping{
{From: " MAX ", To: " x-high "},
{From: "minimal", To: "high"},
})
require.NoError(t, err)
require.Equal(t, []ReasoningEffortMapping{
{From: "max", To: "xhigh"},
{From: "minimal", To: "high"},
}, got)
}
})
t.Run("rejects empty values", func(t *testing.T) {
_, err := NormalizeReasoningEffortMappings(PlatformOpenAI, []ReasoningEffortMapping{{From: "max"}})
require.ErrorContains(t, err, "empty or unknown")
})
t.Run("rejects duplicate sources case insensitively", func(t *testing.T) {
_, err := NormalizeReasoningEffortMappings(PlatformOpenAI, []ReasoningEffortMapping{
{From: "max", To: "xhigh"},
{From: " MAX ", To: "high"},
})
require.ErrorContains(t, err, "duplicate")
})
t.Run("rejects mappings for non OpenAI platforms", func(t *testing.T) {
for _, platform := range []string{PlatformAnthropic, PlatformGemini, PlatformAntigravity, PlatformGrok} {
_, err := NormalizeReasoningEffortMappings(platform, []ReasoningEffortMapping{{From: "low", To: "high"}})
require.ErrorContains(t, err, "only supported for platforms \"openai\" and \"composite\"")
}
_, err := NormalizeReasoningEffortMappings(PlatformOpenAI, []ReasoningEffortMapping{{From: "none", To: "low"}})
require.ErrorContains(t, err, "empty or unknown")
_, err = NormalizeReasoningEffortMappings(PlatformOpenAI, []ReasoningEffortMapping{{From: "ultra", To: "high"}})
require.ErrorContains(t, err, "empty or unknown")
})
}
func TestNormalizeMaxReasoningEffortForPlatform(t *testing.T) {
value, err := normalizeMaxReasoningEffortForPlatform(PlatformOpenAI, "max")
require.NoError(t, err)
require.Equal(t, "max", value)
value, err = normalizeMaxReasoningEffortForPlatform(PlatformComposite, "max")
require.NoError(t, err)
require.Equal(t, "max", value)
for _, platform := range []string{PlatformAnthropic, PlatformGemini, PlatformAntigravity, PlatformGrok} {
_, err = normalizeMaxReasoningEffortForPlatform(platform, "low")
require.ErrorContains(t, err, "only supported for platforms \"openai\" and \"composite\"")
}
_, err = normalizeMaxReasoningEffortForPlatform(PlatformOpenAI, "none")
require.ErrorContains(t, err, "not supported")
}
func TestOpenAIReasoningEffortPolicyContext(t *testing.T) {
body := []byte(`{"reasoning":{"effort":"max"}}`)
unbound, changed := ApplyOpenAIReasoningEffortPolicyFromContext(context.Background(), body)
require.False(t, changed)
require.Equal(t, body, unbound)
mappings := []ReasoningEffortMapping{{From: "max", To: "xhigh"}}
ctx := WithOpenAIReasoningEffortPolicy(context.Background(), "medium", mappings)
mappings[0].To = "low"
got, changed := ApplyOpenAIReasoningEffortPolicyFromContext(ctx, body)
require.True(t, changed)
require.Equal(t, "medium", gjson.GetBytes(got, "reasoning.effort").String())
}
func TestApplyOpenAIReasoningEffortPolicy(t *testing.T) {
tests := []struct {
name string
body string
max string
mappings []ReasoningEffortMapping
path string
want string
changed bool
}{
{name: "nested caps high", body: `{"reasoning":{"effort":"xhigh"}}`, max: "medium", path: "reasoning.effort", want: "medium", changed: true},
{name: "flat caps high", body: `{"reasoning_effort":"high"}`, max: "low", path: "reasoning_effort", want: "low", changed: true},
{name: "does not raise omitted", body: `{"model":"gpt-5"}`, max: "low", path: "reasoning_effort", want: "", changed: false},
{name: "keeps lower value", body: `{"reasoning_effort":"low"}`, max: "high", path: "reasoning_effort", want: "low", changed: false},
{name: "normalizes request alias", body: `{"reasoning_effort":"x-high"}`, max: "xhigh", path: "reasoning_effort", want: "xhigh", changed: true},
{name: "caps max below its distinct rank", body: `{"reasoning_effort":"max"}`, max: "xhigh", path: "reasoning_effort", want: "xhigh", changed: true},
{name: "keeps xhigh below max", body: `{"reasoning_effort":"xhigh"}`, max: "max", path: "reasoning_effort", want: "xhigh", changed: false},
{name: "ignores stale none ceiling", body: `{"reasoning_effort":"high"}`, max: "none", path: "reasoning_effort", want: "high", changed: false},
{name: "caps both shapes", body: `{"reasoning":{"effort":"high"},"reasoning_effort":"xhigh"}`, max: "low", path: "reasoning.effort", want: "low", changed: true},
{name: "maps before cap", body: `{"reasoning":{"effort":"MAX"}}`, max: "medium", mappings: []ReasoningEffortMapping{{From: "max", To: "xhigh"}}, path: "reasoning.effort", want: "medium", changed: true},
{name: "does not chain mappings", body: `{"reasoning_effort":"max"}`, mappings: []ReasoningEffortMapping{{From: "max", To: "xhigh"}, {From: "xhigh", To: "low"}}, path: "reasoning_effort", want: "xhigh", changed: true},
{name: "keeps unknown without mapping", body: `{"reasoning_effort":"future"}`, max: "low", path: "reasoning_effort", want: "future", changed: false},
{name: "keeps non string value", body: `{"reasoning_effort":{"level":"high"}}`, max: "low", path: "reasoning_effort.level", want: "high", changed: false},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
got, changed := ApplyOpenAIReasoningEffortPolicy([]byte(tt.body), tt.max, tt.mappings)
require.Equal(t, tt.changed, changed)
if tt.path != "" {
require.Equal(t, tt.want, gjson.GetBytes(got, tt.path).String())
}
})
}
}