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
77 lines
3.7 KiB
Go
77 lines
3.7 KiB
Go
package handler
|
||
|
||
// CN 分组 /v1/messages 调度闸门回归(修复:正常途径创建的 CN 分组曾恒 403):
|
||
// sanitizeGroupMessagesDispatchFields 对非 openai 平台强制 AllowMessagesDispatch
|
||
// =false,故 CN 分组必须与 grok 一样在闸门处豁免,否则原生 Anthropic 直通
|
||
//(Claude Code 主用例)永远不可达。composite 分组同理:sanitize 对 composite
|
||
// 恒置 false,解析到 grok/CN 目标时必须按目标平台豁免,解析到 openai 目标
|
||
// 仍受开关控制。
|
||
|
||
import (
|
||
"net/http/httptest"
|
||
"testing"
|
||
|
||
"github.com/Wei-Shaw/sub2api/internal/service"
|
||
"github.com/gin-gonic/gin"
|
||
"github.com/stretchr/testify/require"
|
||
)
|
||
|
||
func TestAllowOpenAICompatibleMessagesDispatch_CNProvidersExempt(t *testing.T) {
|
||
require.True(t, allowOpenAICompatibleMessagesDispatch(nil, nil), "无 key 保持放行")
|
||
|
||
for _, platform := range []string{service.PlatformKimi, service.PlatformZhipu, service.PlatformDeepseek, service.PlatformGrok} {
|
||
apiKey := &service.APIKey{Group: &service.Group{Platform: platform, AllowMessagesDispatch: false}}
|
||
require.True(t, allowOpenAICompatibleMessagesDispatch(nil, apiKey),
|
||
"%s 分组必须豁免 allow_messages_dispatch 闸门", platform)
|
||
}
|
||
|
||
// 非回归:openai 分组仍受开关控制。
|
||
openaiOff := &service.APIKey{Group: &service.Group{Platform: service.PlatformOpenAI, AllowMessagesDispatch: false}}
|
||
require.False(t, allowOpenAICompatibleMessagesDispatch(nil, openaiOff))
|
||
openaiOn := &service.APIKey{Group: &service.Group{Platform: service.PlatformOpenAI, AllowMessagesDispatch: true}}
|
||
require.True(t, allowOpenAICompatibleMessagesDispatch(nil, openaiOn))
|
||
}
|
||
|
||
func TestAllowOpenAICompatibleMessagesDispatch_CompositeResolvedTargets(t *testing.T) {
|
||
gin.SetMode(gin.TestMode)
|
||
|
||
newCompositeCtx := func(model string) (*gin.Context, *service.APIKey) {
|
||
c, _ := gin.CreateTestContext(httptest.NewRecorder())
|
||
c.Request = httptest.NewRequest("POST", "/v1/messages", nil)
|
||
apiKey := &service.APIKey{Group: &service.Group{Platform: service.PlatformComposite, AllowMessagesDispatch: false}}
|
||
ensureCompositeTargetPlatform(c, apiKey, model)
|
||
return c, apiKey
|
||
}
|
||
|
||
// 解析到 grok/CN 目标:与对应独立分组同语义豁免。
|
||
for _, model := range []string{"grok-4.3", "kimi-k2-thinking", "glm-5.2", "deepseek-v3.2"} {
|
||
c, apiKey := newCompositeCtx(model)
|
||
require.True(t, allowOpenAICompatibleMessagesDispatch(c, apiKey), "model=%s", model)
|
||
}
|
||
|
||
// 解析到 openai 目标:仍受开关控制(composite 被 sanitize 恒置 false ⇒ 拒绝)。
|
||
c, apiKey := newCompositeCtx("gpt-5.5")
|
||
require.False(t, allowOpenAICompatibleMessagesDispatch(c, apiKey))
|
||
|
||
// 未解析出目标平台:保持拒绝,不放宽。
|
||
cNone, _ := gin.CreateTestContext(httptest.NewRecorder())
|
||
cNone.Request = httptest.NewRequest("POST", "/v1/messages", nil)
|
||
require.False(t, allowOpenAICompatibleMessagesDispatch(cNone,
|
||
&service.APIKey{Group: &service.Group{Platform: service.PlatformComposite, AllowMessagesDispatch: false}}))
|
||
}
|
||
|
||
// composite 解析到 grok/CN 目标时,Group 级调度映射(gpt-5.x 默认值为 openai
|
||
// 专属)不得注入,模型改写完全交给账号级 model_mapping。
|
||
func TestResolveOpenAIMessagesDispatchMappedModel_CompositeCNTargetsSkipGroupMapping(t *testing.T) {
|
||
gin.SetMode(gin.TestMode)
|
||
|
||
for _, model := range []string{"kimi-k2-thinking", "glm-5.2", "deepseek-v3.2", "grok-4.3"} {
|
||
c, _ := gin.CreateTestContext(httptest.NewRecorder())
|
||
c.Request = httptest.NewRequest("POST", "/v1/messages", nil)
|
||
apiKey := &service.APIKey{Group: &service.Group{Platform: service.PlatformComposite}}
|
||
ensureCompositeTargetPlatform(c, apiKey, model)
|
||
|
||
require.Empty(t, resolveOpenAIMessagesDispatchMappedModel(c, apiKey, "claude-sonnet-4-5-20250929"), "model=%s", model)
|
||
}
|
||
}
|