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,206 @@
|
||||
package service
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/require"
|
||||
)
|
||||
|
||||
func TestBuildSelectedSet(t *testing.T) {
|
||||
tests := []struct {
|
||||
name string
|
||||
ids []string
|
||||
wantNil bool
|
||||
wantSize int
|
||||
}{
|
||||
{
|
||||
name: "nil input returns nil (backward compatible: create all)",
|
||||
ids: nil,
|
||||
wantNil: true,
|
||||
},
|
||||
{
|
||||
name: "empty slice returns empty map (create none)",
|
||||
ids: []string{},
|
||||
wantNil: false,
|
||||
wantSize: 0,
|
||||
},
|
||||
{
|
||||
name: "single ID",
|
||||
ids: []string{"abc-123"},
|
||||
wantNil: false,
|
||||
wantSize: 1,
|
||||
},
|
||||
{
|
||||
name: "multiple IDs",
|
||||
ids: []string{"a", "b", "c"},
|
||||
wantNil: false,
|
||||
wantSize: 3,
|
||||
},
|
||||
{
|
||||
name: "duplicate IDs are deduplicated",
|
||||
ids: []string{"a", "a", "b"},
|
||||
wantNil: false,
|
||||
wantSize: 2,
|
||||
},
|
||||
}
|
||||
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
got := buildSelectedSet(tt.ids)
|
||||
if tt.wantNil {
|
||||
if got != nil {
|
||||
t.Errorf("buildSelectedSet(%v) = %v, want nil", tt.ids, got)
|
||||
}
|
||||
return
|
||||
}
|
||||
if got == nil {
|
||||
t.Fatalf("buildSelectedSet(%v) = nil, want non-nil map", tt.ids)
|
||||
}
|
||||
if len(got) != tt.wantSize {
|
||||
t.Errorf("buildSelectedSet(%v) has %d entries, want %d", tt.ids, len(got), tt.wantSize)
|
||||
}
|
||||
// Verify all unique IDs are present
|
||||
for _, id := range tt.ids {
|
||||
if _, ok := got[id]; !ok {
|
||||
t.Errorf("buildSelectedSet(%v) missing key %q", tt.ids, id)
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestShouldCreateAccount(t *testing.T) {
|
||||
tests := []struct {
|
||||
name string
|
||||
crsID string
|
||||
selectedSet map[string]struct{}
|
||||
want bool
|
||||
}{
|
||||
{
|
||||
name: "nil set allows all (backward compatible)",
|
||||
crsID: "any-id",
|
||||
selectedSet: nil,
|
||||
want: true,
|
||||
},
|
||||
{
|
||||
name: "empty set blocks all",
|
||||
crsID: "any-id",
|
||||
selectedSet: map[string]struct{}{},
|
||||
want: false,
|
||||
},
|
||||
{
|
||||
name: "ID in set is allowed",
|
||||
crsID: "abc-123",
|
||||
selectedSet: map[string]struct{}{"abc-123": {}, "def-456": {}},
|
||||
want: true,
|
||||
},
|
||||
{
|
||||
name: "ID not in set is blocked",
|
||||
crsID: "xyz-789",
|
||||
selectedSet: map[string]struct{}{"abc-123": {}, "def-456": {}},
|
||||
want: false,
|
||||
},
|
||||
}
|
||||
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
got := shouldCreateAccount(tt.crsID, tt.selectedSet)
|
||||
if got != tt.want {
|
||||
t.Errorf("shouldCreateAccount(%q, %v) = %v, want %v",
|
||||
tt.crsID, tt.selectedSet, got, tt.want)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestReconcileCRSUpstreamBillingProbeExtra(t *testing.T) {
|
||||
remote := map[string]any{
|
||||
"crs_account_id": "remote-1",
|
||||
UpstreamBillingProbeEnabledExtraKey: true,
|
||||
UpstreamBillingRateSyncEnabledExtraKey: true,
|
||||
UpstreamBillingProbeExtraKey: map[string]any{"status": "remote"},
|
||||
}
|
||||
|
||||
t.Run("create drops remote managed fields", func(t *testing.T) {
|
||||
extra := mergeMap(nil, remote)
|
||||
reconcileCRSUpstreamBillingProbeExtra(nil, PlatformOpenAI, AccountTypeAPIKey, map[string]any{"api_key": "new"}, extra)
|
||||
require.NotContains(t, extra, UpstreamBillingProbeEnabledExtraKey)
|
||||
require.NotContains(t, extra, UpstreamBillingRateSyncEnabledExtraKey)
|
||||
require.NotContains(t, extra, UpstreamBillingProbeExtraKey)
|
||||
})
|
||||
|
||||
existing := &Account{
|
||||
Platform: PlatformOpenAI,
|
||||
Type: AccountTypeAPIKey,
|
||||
Credentials: map[string]any{"api_key": "local", "base_url": "http://127.0.0.1:8080"},
|
||||
Extra: map[string]any{
|
||||
UpstreamBillingProbeEnabledExtraKey: false,
|
||||
UpstreamBillingRateSyncEnabledExtraKey: false,
|
||||
UpstreamBillingProbeExtraKey: map[string]any{"status": "local"},
|
||||
},
|
||||
}
|
||||
|
||||
t.Run("same identity keeps local state", func(t *testing.T) {
|
||||
extra := mergeMap(existing.Extra, remote)
|
||||
reconcileCRSUpstreamBillingProbeExtra(existing, existing.Platform, existing.Type, mergeMap(existing.Credentials, nil), extra)
|
||||
require.Equal(t, false, extra[UpstreamBillingProbeEnabledExtraKey])
|
||||
require.Equal(t, false, extra[UpstreamBillingRateSyncEnabledExtraKey])
|
||||
require.Equal(t, map[string]any{"status": "local"}, extra[UpstreamBillingProbeExtraKey])
|
||||
})
|
||||
|
||||
t.Run("same identity preserves enabled rate sync", func(t *testing.T) {
|
||||
enabled := *existing
|
||||
enabled.Extra = mergeMap(existing.Extra, map[string]any{
|
||||
UpstreamBillingProbeEnabledExtraKey: true,
|
||||
UpstreamBillingRateSyncEnabledExtraKey: true,
|
||||
})
|
||||
extra := mergeMap(enabled.Extra, remote)
|
||||
reconcileCRSUpstreamBillingProbeExtra(&enabled, enabled.Platform, enabled.Type, mergeMap(enabled.Credentials, nil), extra)
|
||||
require.Equal(t, true, extra[UpstreamBillingProbeEnabledExtraKey])
|
||||
require.Equal(t, true, extra[UpstreamBillingRateSyncEnabledExtraKey])
|
||||
})
|
||||
|
||||
t.Run("identity change keeps enabled and clears snapshot", func(t *testing.T) {
|
||||
extra := mergeMap(existing.Extra, remote)
|
||||
reconcileCRSUpstreamBillingProbeExtra(existing, PlatformOpenAI, AccountTypeAPIKey, map[string]any{"api_key": "changed"}, extra)
|
||||
require.Equal(t, false, extra[UpstreamBillingProbeEnabledExtraKey])
|
||||
require.Equal(t, false, extra[UpstreamBillingRateSyncEnabledExtraKey])
|
||||
require.NotContains(t, extra, UpstreamBillingProbeExtraKey)
|
||||
})
|
||||
|
||||
// API-key 平台间切换:探测资格保留(放宽后不再限 OpenAI),开关沿用本地值,
|
||||
// 但平台属于探测身份,快照必须作废。
|
||||
for _, target := range []struct {
|
||||
name string
|
||||
platform string
|
||||
}{
|
||||
{name: "anthropic api key", platform: PlatformAnthropic},
|
||||
{name: "gemini api key", platform: PlatformGemini},
|
||||
} {
|
||||
t.Run(target.name+" keeps enabled and clears snapshot", func(t *testing.T) {
|
||||
extra := mergeMap(existing.Extra, remote)
|
||||
reconcileCRSUpstreamBillingProbeExtra(existing, target.platform, AccountTypeAPIKey, existing.Credentials, extra)
|
||||
require.Equal(t, false, extra[UpstreamBillingProbeEnabledExtraKey])
|
||||
require.Equal(t, false, extra[UpstreamBillingRateSyncEnabledExtraKey])
|
||||
require.NotContains(t, extra, UpstreamBillingProbeExtraKey)
|
||||
})
|
||||
}
|
||||
|
||||
for _, target := range []struct {
|
||||
name string
|
||||
platform string
|
||||
typeName string
|
||||
}{
|
||||
{name: "anthropic oauth", platform: PlatformAnthropic, typeName: AccountTypeOAuth},
|
||||
{name: "openai oauth", platform: PlatformOpenAI, typeName: AccountTypeOAuth},
|
||||
{name: "gemini oauth", platform: PlatformGemini, typeName: AccountTypeOAuth},
|
||||
} {
|
||||
t.Run(target.name+" removes inapplicable state", func(t *testing.T) {
|
||||
extra := mergeMap(existing.Extra, remote)
|
||||
reconcileCRSUpstreamBillingProbeExtra(existing, target.platform, target.typeName, existing.Credentials, extra)
|
||||
require.NotContains(t, extra, UpstreamBillingProbeEnabledExtraKey)
|
||||
require.NotContains(t, extra, UpstreamBillingRateSyncEnabledExtraKey)
|
||||
require.NotContains(t, extra, UpstreamBillingProbeExtraKey)
|
||||
})
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user