Sub2API v1.0 - AI API 网关(二开初始版本,基于上游 Wei-Shaw/sub2api)
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
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
This commit is contained in:
@@ -0,0 +1,116 @@
|
||||
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"))
|
||||
}
|
||||
Reference in New Issue
Block a user