Files
sub2api/backend/internal/service/openai_account_runtime_transient_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

117 lines
5.0 KiB
Go

package service
import (
"context"
"net/http"
"testing"
"time"
"github.com/Wei-Shaw/sub2api/internal/config"
"github.com/stretchr/testify/require"
)
type transientCooldownAccountRepo struct {
AccountRepository
}
func (transientCooldownAccountRepo) SetOverloaded(context.Context, int64, time.Time) error {
return nil
}
func TestHandleOpenAITransientError_BlocksOnlyRequestedModel(t *testing.T) {
svc := &OpenAIGatewayService{}
svc.rateLimitService = NewRateLimitService(transientCooldownAccountRepo{}, nil, &config.Config{}, nil, nil)
account := &Account{
ID: 5105,
Platform: PlatformOpenAI,
Type: AccountTypeAPIKey,
}
firstShouldDisable := svc.handleOpenAIAccountUpstreamError(context.Background(), account, http.StatusBadGateway, http.Header{}, []byte(`{"error":{"message":"Upstream request failed","type":"upstream_error"}}`), "gpt-5.5")
secondShouldDisable := svc.handleOpenAIAccountUpstreamError(context.Background(), account, http.StatusBadGateway, http.Header{}, []byte(`{"error":{"message":"Upstream request failed","type":"upstream_error"}}`), "gpt-5.5")
require.False(t, firstShouldDisable)
require.False(t, secondShouldDisable)
require.False(t, svc.isOpenAIAccountRuntimeBlocked(account))
require.True(t, svc.isOpenAIAccountModelRuntimeBlocked(account, "gpt-5.5"))
require.False(t, svc.isOpenAIAccountModelRuntimeBlocked(account, "gpt-5.6-terra"))
}
func TestHandleOpenAITransientError_TransientStatusesUseModelScope(t *testing.T) {
for _, statusCode := range []int{http.StatusInternalServerError, http.StatusBadGateway, http.StatusServiceUnavailable, http.StatusGatewayTimeout, 520, 521, 522, 523, 524} {
t.Run(http.StatusText(statusCode), func(t *testing.T) {
svc := &OpenAIGatewayService{}
svc.rateLimitService = NewRateLimitService(transientCooldownAccountRepo{}, nil, &config.Config{}, nil, nil)
account := &Account{
ID: int64(5100 + statusCode),
Platform: PlatformOpenAI,
Type: AccountTypeAPIKey,
}
firstShouldDisable := svc.handleOpenAIAccountUpstreamError(context.Background(), account, statusCode, http.Header{}, []byte(`{"error":{"message":"temporary upstream failure"}}`), "gpt-5.5")
secondShouldDisable := svc.handleOpenAIAccountUpstreamError(context.Background(), account, statusCode, http.Header{}, []byte(`{"error":{"message":"temporary upstream failure"}}`), "gpt-5.5")
require.False(t, firstShouldDisable)
require.False(t, secondShouldDisable)
require.False(t, svc.isOpenAIAccountRuntimeBlocked(account), "status %d must not block the whole account", statusCode)
require.True(t, svc.isOpenAIAccountModelRuntimeBlocked(account, "gpt-5.5"), "status %d should block the failing model", statusCode)
})
}
}
func TestHandleOpenAITransientError_529RemainsOverloadOnly(t *testing.T) {
require.False(t, shouldCooldownOpenAITransientUpstreamError(529, []byte(`{"error":{"message":"overloaded"}}`)))
}
func TestHandleOpenAITransientError_CanonicalModelIsNotMappedTwice(t *testing.T) {
svc := &OpenAIGatewayService{}
svc.rateLimitService = NewRateLimitService(transientCooldownAccountRepo{}, nil, &config.Config{}, nil, nil)
account := &Account{
ID: 5107,
Platform: PlatformOpenAI,
Type: AccountTypeAPIKey,
Credentials: map[string]any{
"model_mapping": map[string]any{
"public-alias": "upstream-a",
"upstream-a": "upstream-b",
},
},
}
canonicalModel := account.GetMappedModel("public-alias")
require.Equal(t, "upstream-a", canonicalModel)
for range 2 {
svc.handleOpenAIAccountUpstreamError(context.Background(), account, http.StatusBadGateway, http.Header{}, []byte(`{"error":{"message":"temporary upstream failure"}}`), canonicalModel)
}
require.True(t, svc.isOpenAIAccountModelRuntimeBlocked(account, "public-alias"))
svc.ReportOpenAIAccountScheduleResult(account.ID, canonicalModel, true, nil)
require.False(t, svc.isOpenAIAccountModelRuntimeBlocked(account, "public-alias"))
}
func TestHandleOpenAITransientError_DoesNotBlockParameter400(t *testing.T) {
svc := &OpenAIGatewayService{}
svc.rateLimitService = NewRateLimitService(transientCooldownAccountRepo{}, nil, &config.Config{}, nil, nil)
account := &Account{
ID: 5103,
Platform: PlatformOpenAI,
Type: AccountTypeAPIKey,
}
shouldDisable := svc.handleOpenAIAccountUpstreamError(context.Background(), account, http.StatusBadRequest, http.Header{}, []byte(`{"error":{"message":"Invalid type for input[0].arguments"}}`), "gpt-5.5")
require.False(t, shouldDisable)
require.False(t, svc.isOpenAIAccountRuntimeBlocked(account))
require.False(t, svc.isOpenAIAccountModelRuntimeBlocked(account, "gpt-5.5"))
}
func TestHandleOpenAITransientError_HardDisableStillBlocksWholeAccount(t *testing.T) {
svc := &OpenAIGatewayService{}
account := &Account{ID: 5106, Platform: PlatformOpenAI, Type: AccountTypeAPIKey}
svc.BlockAccountScheduling(account, time.Now().Add(time.Minute), "upstream_disable")
require.True(t, svc.isOpenAIAccountRequestRuntimeBlocked(account, "gpt-5.5"))
require.True(t, svc.isOpenAIAccountRequestRuntimeBlocked(account, "gpt-5.6-sol"))
}