From 386126b1b2101158a3db7b5fadaacbb4039abcf5 Mon Sep 17 00:00:00 2001 From: erio Date: Sat, 7 Feb 2026 14:39:25 +0800 Subject: [PATCH] test(antigravity): add missing unit tests for upstream and custom model_mapping - Add GetAccessToken upstream branch tests (success/failure/empty/nil) - Add mapAntigravityModel wildcard-target-equals-request edge case tests - Add upstream account smart retry test case - Add GeminiMessagesCompatService custom model_mapping and empty model tests --- .../service/antigravity_model_mapping_test.go | 55 +++++++++++ .../service/antigravity_rate_limit_test.go | 18 ++++ .../antigravity_token_provider_test.go | 97 +++++++++++++++++++ .../service/gemini_multiplatform_test.go | 33 +++++++ 4 files changed, 203 insertions(+) create mode 100644 backend/internal/service/antigravity_token_provider_test.go diff --git a/backend/internal/service/antigravity_model_mapping_test.go b/backend/internal/service/antigravity_model_mapping_test.go index 5b1a4341..55debb53 100644 --- a/backend/internal/service/antigravity_model_mapping_test.go +++ b/backend/internal/service/antigravity_model_mapping_test.go @@ -265,3 +265,58 @@ func TestAntigravityGatewayService_IsModelSupported(t *testing.T) { }) } } + +// 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", + }, + } + + 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) + }) + } +} diff --git a/backend/internal/service/antigravity_rate_limit_test.go b/backend/internal/service/antigravity_rate_limit_test.go index 4e5ad24d..8606332f 100644 --- a/backend/internal/service/antigravity_rate_limit_test.go +++ b/backend/internal/service/antigravity_rate_limit_test.go @@ -526,6 +526,7 @@ func TestParseAntigravitySmartRetryInfo(t *testing.T) { func TestShouldTriggerAntigravitySmartRetry(t *testing.T) { oauthAccount := &Account{Type: AccountTypeOAuth, Platform: PlatformAntigravity} setupTokenAccount := &Account{Type: AccountTypeSetupToken, Platform: PlatformAntigravity} + upstreamAccount := &Account{Type: AccountTypeUpstream, Platform: PlatformAntigravity} apiKeyAccount := &Account{Type: AccountTypeAPIKey} tests := []struct { @@ -587,6 +588,23 @@ func TestShouldTriggerAntigravitySmartRetry(t *testing.T) { expectedShouldRateLimit: true, modelName: "claude-sonnet-4-5", }, + { + name: "Upstream account with short delay - smart retry", + account: upstreamAccount, + body: `{ + "error": { + "status": "RESOURCE_EXHAUSTED", + "details": [ + {"@type": "type.googleapis.com/google.rpc.ErrorInfo", "metadata": {"model": "claude-sonnet-4-5"}, "reason": "RATE_LIMIT_EXCEEDED"}, + {"@type": "type.googleapis.com/google.rpc.RetryInfo", "retryDelay": "2s"} + ] + } + }`, + expectedShouldRetry: true, + expectedShouldRateLimit: false, + minWait: 2 * time.Second, + modelName: "claude-sonnet-4-5", + }, { name: "API Key account - should not trigger", account: apiKeyAccount, diff --git a/backend/internal/service/antigravity_token_provider_test.go b/backend/internal/service/antigravity_token_provider_test.go new file mode 100644 index 00000000..c9d38cf6 --- /dev/null +++ b/backend/internal/service/antigravity_token_provider_test.go @@ -0,0 +1,97 @@ +//go:build unit + +package service + +import ( + "context" + "testing" + + "github.com/stretchr/testify/require" +) + +func TestAntigravityTokenProvider_GetAccessToken_Upstream(t *testing.T) { + provider := &AntigravityTokenProvider{} + + t.Run("upstream account with valid api_key", func(t *testing.T) { + account := &Account{ + Platform: PlatformAntigravity, + Type: AccountTypeUpstream, + Credentials: map[string]any{ + "api_key": "sk-test-key-12345", + }, + } + token, err := provider.GetAccessToken(context.Background(), account) + require.NoError(t, err) + require.Equal(t, "sk-test-key-12345", token) + }) + + t.Run("upstream account missing api_key", func(t *testing.T) { + account := &Account{ + Platform: PlatformAntigravity, + Type: AccountTypeUpstream, + Credentials: map[string]any{}, + } + token, err := provider.GetAccessToken(context.Background(), account) + require.Error(t, err) + require.Contains(t, err.Error(), "upstream account missing api_key") + require.Empty(t, token) + }) + + t.Run("upstream account with empty api_key", func(t *testing.T) { + account := &Account{ + Platform: PlatformAntigravity, + Type: AccountTypeUpstream, + Credentials: map[string]any{ + "api_key": "", + }, + } + token, err := provider.GetAccessToken(context.Background(), account) + require.Error(t, err) + require.Contains(t, err.Error(), "upstream account missing api_key") + require.Empty(t, token) + }) + + t.Run("upstream account with nil credentials", func(t *testing.T) { + account := &Account{ + Platform: PlatformAntigravity, + Type: AccountTypeUpstream, + } + token, err := provider.GetAccessToken(context.Background(), account) + require.Error(t, err) + require.Contains(t, err.Error(), "upstream account missing api_key") + require.Empty(t, token) + }) +} + +func TestAntigravityTokenProvider_GetAccessToken_Guards(t *testing.T) { + provider := &AntigravityTokenProvider{} + + t.Run("nil account", func(t *testing.T) { + token, err := provider.GetAccessToken(context.Background(), nil) + require.Error(t, err) + require.Contains(t, err.Error(), "account is nil") + require.Empty(t, token) + }) + + t.Run("non-antigravity platform", func(t *testing.T) { + account := &Account{ + Platform: PlatformAnthropic, + Type: AccountTypeOAuth, + } + token, err := provider.GetAccessToken(context.Background(), account) + require.Error(t, err) + require.Contains(t, err.Error(), "not an antigravity account") + require.Empty(t, token) + }) + + t.Run("unsupported account type", func(t *testing.T) { + account := &Account{ + Platform: PlatformAntigravity, + Type: AccountTypeAPIKey, + } + token, err := provider.GetAccessToken(context.Background(), account) + require.Error(t, err) + require.Contains(t, err.Error(), "not an antigravity oauth account") + require.Empty(t, token) + }) +} diff --git a/backend/internal/service/gemini_multiplatform_test.go b/backend/internal/service/gemini_multiplatform_test.go index 1df59d1a..601e7e2c 100644 --- a/backend/internal/service/gemini_multiplatform_test.go +++ b/backend/internal/service/gemini_multiplatform_test.go @@ -905,6 +905,39 @@ func TestGeminiMessagesCompatService_isModelSupportedByAccount(t *testing.T) { model: "gpt-4", expected: false, }, + { + name: "Antigravity平台-空模型允许", + account: &Account{Platform: PlatformAntigravity}, + model: "", + expected: true, + }, + { + name: "Antigravity平台-自定义映射-支持自定义模型", + account: &Account{ + Platform: PlatformAntigravity, + Credentials: map[string]any{ + "model_mapping": map[string]any{ + "my-custom-model": "upstream-model", + "gpt-4o": "some-model", + }, + }, + }, + model: "my-custom-model", + expected: true, + }, + { + name: "Antigravity平台-自定义映射-不在映射中的模型不支持", + account: &Account{ + Platform: PlatformAntigravity, + Credentials: map[string]any{ + "model_mapping": map[string]any{ + "my-custom-model": "upstream-model", + }, + }, + }, + model: "claude-sonnet-4-5", + expected: false, + }, { name: "Gemini平台-无映射配置-支持所有模型", account: &Account{Platform: PlatformGemini},