Files
sub2api/backend/internal/service/antigravity_model_mapping_test.go
T
erio db6413039a Merge tag 'v0.1.108' into release/custom-0.1.108
彻底移除 Sora 平台功能,精简代码库约 24000 行;修复渠道平台定价隔离问题。

- 完全移除 Sora 平台:删除所有 Sora 相关的后端服务、前端组件、数据库 Schema 和 ent 代码
- 移除 Sora 相关的 S3 存储、媒体管理、配额管理、生成服务等模块
- 清理前端 Sora 视图、组件、composables、API 调用和国际化文本
- 移除 Sora OAuth 选项,简化 OpenAI OAuth 流程
- 更新 antigravity User-Agent 版本号至 1.21.9

- 修复 antigravity 分组错误匹配其他平台(anthropic/gemini)定价和模型映射的问题
- 修复渠道未映射模型时 BillingModel 被错误还原导致零计费的问题

# Conflicts:
#	backend/cmd/server/VERSION
#	backend/cmd/server/wire_gen.go
#	backend/ent/group.go
#	backend/ent/mutation.go
#	backend/ent/runtime/runtime.go
#	backend/ent/schema/group.go
#	backend/internal/handler/admin/channel_handler.go
#	backend/internal/handler/admin/channel_handler_test.go
#	backend/internal/handler/gateway_handler.go
#	backend/internal/pkg/antigravity/stream_transformer.go
#	backend/internal/server/api_contract_test.go
#	backend/internal/service/billing_service.go
#	backend/internal/service/channel_service.go
#	backend/internal/service/channel_service_test.go
#	backend/internal/service/gateway_service.go
#	backend/internal/service/openai_account_scheduler.go
#	backend/internal/service/openai_gateway_chat_completions.go
#	backend/internal/service/openai_gateway_service.go
#	backend/internal/service/openai_oauth_service.go
2026-04-05 23:11:04 +08:00

292 lines
8.5 KiB
Go
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
//go:build unit
package service
import (
"testing"
"github.com/stretchr/testify/require"
)
func TestAntigravityGatewayService_GetMappedModel(t *testing.T) {
svc := &AntigravityGatewayService{}
tests := []struct {
name string
requestedModel string
accountMapping map[string]string
expected string
}{
// 1. 账户级映射优先
{
name: "账户映射优先",
requestedModel: "claude-3-5-sonnet-20241022",
accountMapping: map[string]string{"claude-3-5-sonnet-20241022": "custom-model"},
expected: "custom-model",
},
{
name: "账户映射 - 可覆盖默认映射的模型",
requestedModel: "claude-sonnet-4-5",
accountMapping: map[string]string{"claude-sonnet-4-5": "my-custom-sonnet"},
expected: "my-custom-sonnet",
},
{
name: "账户映射 - 可覆盖未知模型",
requestedModel: "claude-opus-4",
accountMapping: map[string]string{"claude-opus-4": "my-opus"},
expected: "my-opus",
},
// 2. 默认映射(DefaultAntigravityModelMapping)
{
name: "默认映射 - claude-opus-4-6 → claude-opus-4-6-thinking",
requestedModel: "claude-opus-4-6",
accountMapping: nil,
expected: "claude-opus-4-6-thinking",
},
{
name: "默认映射 - claude-opus-4-5-20251101 → claude-opus-4-6-thinking",
requestedModel: "claude-opus-4-5-20251101",
accountMapping: nil,
expected: "claude-opus-4-6-thinking",
},
{
name: "默认映射 - claude-opus-4-5-thinking → claude-opus-4-6-thinking",
requestedModel: "claude-opus-4-5-thinking",
accountMapping: nil,
expected: "claude-opus-4-6-thinking",
},
{
name: "默认映射 - claude-haiku-4-5 → claude-sonnet-4-6",
requestedModel: "claude-haiku-4-5",
accountMapping: nil,
expected: "claude-sonnet-4-6",
},
{
name: "默认映射 - claude-haiku-4-5-20251001 → claude-sonnet-4-6",
requestedModel: "claude-haiku-4-5-20251001",
accountMapping: nil,
expected: "claude-sonnet-4-6",
},
{
name: "默认映射 - claude-sonnet-4-5-20250929 → claude-sonnet-4-6",
requestedModel: "claude-sonnet-4-5-20250929",
accountMapping: nil,
expected: "claude-sonnet-4-6",
},
// 3. 默认映射中的透传(映射到自己)
{
name: "默认映射透传 - claude-sonnet-4-6",
requestedModel: "claude-sonnet-4-6",
accountMapping: nil,
expected: "claude-sonnet-4-6",
},
{
name: "默认映射 - claude-sonnet-4-5 → claude-sonnet-4-6",
requestedModel: "claude-sonnet-4-5",
accountMapping: nil,
expected: "claude-sonnet-4-6",
},
{
name: "默认映射透传 - claude-opus-4-6-thinking",
requestedModel: "claude-opus-4-6-thinking",
accountMapping: nil,
expected: "claude-opus-4-6-thinking",
},
{
name: "默认映射 - claude-sonnet-4-5-thinking → claude-sonnet-4-6",
requestedModel: "claude-sonnet-4-5-thinking",
accountMapping: nil,
expected: "claude-sonnet-4-6",
},
{
name: "默认映射透传 - gemini-2.5-flash",
requestedModel: "gemini-2.5-flash",
accountMapping: nil,
expected: "gemini-2.5-flash",
},
{
name: "默认映射透传 - gemini-2.5-pro",
requestedModel: "gemini-2.5-pro",
accountMapping: nil,
expected: "gemini-2.5-pro",
},
{
name: "默认映射透传 - gemini-3-flash",
requestedModel: "gemini-3-flash",
accountMapping: nil,
expected: "gemini-3-flash",
},
// 4. 未在默认映射中的模型返回空字符串(不支持)
{
name: "未知模型 - claude-unknown 返回空",
requestedModel: "claude-unknown",
accountMapping: nil,
expected: "",
},
{
name: "未知模型 - claude-3-5-sonnet-20241022 返回空(未在默认映射)",
requestedModel: "claude-3-5-sonnet-20241022",
accountMapping: nil,
expected: "",
},
{
name: "未知模型 - claude-3-opus-20240229 返回空",
requestedModel: "claude-3-opus-20240229",
accountMapping: nil,
expected: "",
},
{
name: "未知模型 - claude-opus-4 返回空",
requestedModel: "claude-opus-4",
accountMapping: nil,
expected: "",
},
{
name: "未知模型 - gemini-future-model 返回空",
requestedModel: "gemini-future-model",
accountMapping: nil,
expected: "",
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
account := &Account{
Platform: PlatformAntigravity,
}
if tt.accountMapping != nil {
// GetModelMapping 期望 model_mapping 是 map[string]any 格式
mappingAny := make(map[string]any)
for k, v := range tt.accountMapping {
mappingAny[k] = v
}
account.Credentials = map[string]any{
"model_mapping": mappingAny,
}
}
got := svc.getMappedModel(account, tt.requestedModel)
require.Equal(t, tt.expected, got, "model: %s", tt.requestedModel)
})
}
}
func TestAntigravityGatewayService_GetMappedModel_EdgeCases(t *testing.T) {
svc := &AntigravityGatewayService{}
tests := []struct {
name string
requestedModel string
expected string
}{
// 空字符串和非 claude/gemini 前缀返回空字符串
{"空字符串", "", ""},
{"非claude/gemini前缀 - gpt", "gpt-4", ""},
{"非claude/gemini前缀 - llama", "llama-3", ""},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
account := &Account{Platform: PlatformAntigravity}
got := svc.getMappedModel(account, tt.requestedModel)
require.Equal(t, tt.expected, got)
})
}
}
func TestAntigravityGatewayService_IsModelSupported(t *testing.T) {
svc := &AntigravityGatewayService{}
tests := []struct {
name string
model string
expected bool
}{
// 直接支持
{"直接支持 - claude-sonnet-4-5", "claude-sonnet-4-5", true},
{"直接支持 - gemini-3-flash", "gemini-3-flash", true},
// 可映射(有明确前缀映射)
{"可映射 - claude-opus-4-6", "claude-opus-4-6", true},
// 前缀透传(claude 和 gemini 前缀)
{"Gemini前缀", "gemini-unknown", true},
{"Claude前缀", "claude-unknown", true},
// 不支持
{"不支持 - gpt-4", "gpt-4", false},
{"不支持 - 空字符串", "", false},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
got := svc.IsModelSupported(tt.model)
require.Equal(t, tt.expected, got)
})
}
}
// TestMapAntigravityModel_WildcardTargetEqualsRequest 测试通配符映射目标恰好等于请求模型名的 edge case
// 例如 {"claude-*": "claude-sonnet-4-5"},请求 "claude-sonnet-4-5" 时应该通过
func TestMapAntigravityModel_WildcardTargetEqualsRequest(t *testing.T) {
tests := []struct {
name string
modelMapping map[string]any
requestedModel string
expected string
}{
{
name: "wildcard target equals request model",
modelMapping: map[string]any{"claude-*": "claude-sonnet-4-5"},
requestedModel: "claude-sonnet-4-5",
expected: "claude-sonnet-4-5",
},
{
name: "wildcard target differs from request model",
modelMapping: map[string]any{"claude-*": "claude-sonnet-4-5"},
requestedModel: "claude-opus-4-6",
expected: "claude-sonnet-4-5",
},
{
name: "wildcard no match",
modelMapping: map[string]any{"claude-*": "claude-sonnet-4-5"},
requestedModel: "gpt-4o",
expected: "",
},
{
name: "explicit passthrough same name",
modelMapping: map[string]any{"claude-sonnet-4-5": "claude-sonnet-4-5"},
requestedModel: "claude-sonnet-4-5",
expected: "claude-sonnet-4-5",
},
{
name: "multiple wildcards target equals one request",
modelMapping: map[string]any{"claude-*": "claude-sonnet-4-5", "gemini-*": "gemini-2.5-flash"},
requestedModel: "gemini-2.5-flash",
expected: "gemini-2.5-flash",
},
{
name: "customtools alias falls back to normalized preview mapping",
modelMapping: map[string]any{"gemini-3.1-pro-preview": "gemini-3.1-pro-high"},
requestedModel: "gemini-3.1-pro-preview-customtools",
expected: "gemini-3.1-pro-high",
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
account := &Account{
Platform: PlatformAntigravity,
Credentials: map[string]any{
"model_mapping": tt.modelMapping,
},
}
got := mapAntigravityModel(account, tt.requestedModel)
require.Equal(t, tt.expected, got, "mapAntigravityModel(%q) = %q, want %q", tt.requestedModel, got, tt.expected)
})
}
}