ccpoad / internal /storage /sqlite /cooldown_consistency_test.go
anyalerob's picture
Upload folder using huggingface_hub
2986042 verified
Raw
History Blame Contribute Delete
8.09 kB
package sqlite_test
import (
"context"
"testing"
"time"
"ccLoad/internal/model"
"ccLoad/internal/storage"
"ccLoad/internal/util"
)
// TestCooldownConsistency_401Error 验证401错误时Key级别和渠道级别冷却时间一致性
// 设计目标:确保相同错误码在不同级别产生相同的冷却时长
func TestCooldownConsistency_401Error(t *testing.T) {
tmpDB := t.TempDir() + "/test-cooldown-consistency.db"
store, err := storage.CreateSQLiteStore(tmpDB)
if err != nil {
t.Fatalf("创建测试数据库失败: %v", err)
}
defer func() { _ = store.Close() }()
ctx := context.Background()
now := time.Now()
// 测试场景:首次401错误
t.Run("初始401错误冷却时间一致性", func(t *testing.T) {
// 创建两个独立的测试渠道
channelCfg := &model.Config{
Name: "channel-level-test",
URL: "https://api.example.com",
Enabled: true,
}
channelCreated, err := store.CreateConfig(ctx, channelCfg)
if err != nil {
t.Fatalf("创建渠道测试配置失败: %v", err)
}
keyCfg := &model.Config{
Name: "key-level-test",
URL: "https://api.example.com",
Enabled: true,
}
keyCreated, err := store.CreateConfig(ctx, keyCfg)
if err != nil {
t.Fatalf("创建Key测试配置失败: %v", err)
}
// 为Key测试渠道创建2个API Keys
_ = store.CreateAPIKeysBatch(ctx, []*model.APIKey{
{ChannelID: keyCreated.ID, KeyIndex: 0, APIKey: "sk-key1", KeyStrategy: model.KeyStrategySequential},
{ChannelID: keyCreated.ID, KeyIndex: 1, APIKey: "sk-key2", KeyStrategy: model.KeyStrategySequential},
})
// 触发渠道级401错误
channelDuration, err := store.BumpChannelCooldown(ctx, channelCreated.ID, now, 401)
if err != nil {
t.Fatalf("渠道级BumpCooldownOnError失败: %v", err)
}
// 触发Key级401错误
keyDuration, err := store.BumpKeyCooldown(ctx, keyCreated.ID, 0, now, 401)
if err != nil {
t.Fatalf("Key级BumpKeyCooldownOnError失败: %v", err)
}
// 验证冷却时长完全一致
if channelDuration != keyDuration {
t.Errorf("❌ 401错误冷却时间不一致:\n 渠道级: %v\n Key级: %v",
channelDuration, keyDuration)
}
// 验证都是5分钟(util.AuthErrorInitialCooldown)
expectedDuration := util.AuthErrorInitialCooldown
tolerance := 10 * time.Millisecond
if abs(channelDuration-expectedDuration) > tolerance {
t.Errorf("渠道级冷却时间错误: 期望%v,实际%v", expectedDuration, channelDuration)
}
if abs(keyDuration-expectedDuration) > tolerance {
t.Errorf("Key级冷却时间错误: 期望%v,实际%v", expectedDuration, keyDuration)
}
})
// 测试场景:指数退避序列一致性
t.Run("401错误指数退避序列一致性", func(t *testing.T) {
// 创建两个测试渠道
channelCfg := &model.Config{
Name: "channel-backoff-test",
URL: "https://api.example.com",
Enabled: true,
}
channelCreated, err := store.CreateConfig(ctx, channelCfg)
if err != nil {
t.Fatalf("创建渠道测试配置失败: %v", err)
}
keyCfg := &model.Config{
Name: "key-backoff-test",
URL: "https://api.example.com",
Enabled: true,
}
keyCreated, err := store.CreateConfig(ctx, keyCfg)
if err != nil {
t.Fatalf("创建Key测试配置失败: %v", err)
}
// 为Key测试渠道创建2个API Keys
_ = store.CreateAPIKeysBatch(ctx, []*model.APIKey{
{ChannelID: keyCreated.ID, KeyIndex: 0, APIKey: "sk-key1", KeyStrategy: model.KeyStrategySequential},
{ChannelID: keyCreated.ID, KeyIndex: 1, APIKey: "sk-key2", KeyStrategy: model.KeyStrategySequential},
})
// 预期序列:5min → 10min → 20min → 30min
expectedSequence := []time.Duration{
5 * time.Minute,
10 * time.Minute,
20 * time.Minute,
30 * time.Minute,
}
currentTime := now
for i, expected := range expectedSequence {
// 渠道级错误
channelDuration, err := store.BumpChannelCooldown(ctx, channelCreated.ID, currentTime, 401)
if err != nil {
t.Fatalf("第%d次渠道级错误失败: %v", i+1, err)
}
// Key级错误
keyDuration, err := store.BumpKeyCooldown(ctx, keyCreated.ID, 0, currentTime, 401)
if err != nil {
t.Fatalf("第%d次Key级错误失败: %v", i+1, err)
}
// 验证一致性
if channelDuration != keyDuration {
t.Errorf("❌ 第%d次错误冷却时间不一致:\n 渠道级: %v\n Key级: %v",
i+1, channelDuration, keyDuration)
}
// 验证符合预期
tolerance := 100 * time.Millisecond
if abs(channelDuration-expected) > tolerance {
t.Errorf("第%d次错误冷却时间错误: 期望%v,渠道级%v,Key级%v",
i+1, expected, channelDuration, keyDuration)
}
// 推进时间(确保不被当作同一次错误)
currentTime = currentTime.Add(expected + 1*time.Second)
}
})
// 测试场景:403错误一致性
t.Run("403错误冷却时间一致性", func(t *testing.T) {
channelCfg := &model.Config{
Name: "channel-403-test",
URL: "https://api.example.com",
Enabled: true,
}
channelCreated, err := store.CreateConfig(ctx, channelCfg)
if err != nil {
t.Fatalf("创建渠道测试配置失败: %v", err)
}
keyCfg := &model.Config{
Name: "key-403-test",
URL: "https://api.example.com",
Enabled: true,
}
keyCreated, err := store.CreateConfig(ctx, keyCfg)
if err != nil {
t.Fatalf("创建Key测试配置失败: %v", err)
}
// 为Key测试渠道创建2个API Keys
_ = store.CreateAPIKeysBatch(ctx, []*model.APIKey{
{ChannelID: keyCreated.ID, KeyIndex: 0, APIKey: "sk-key1", KeyStrategy: model.KeyStrategySequential},
{ChannelID: keyCreated.ID, KeyIndex: 1, APIKey: "sk-key2", KeyStrategy: model.KeyStrategySequential},
})
// 触发403错误
channelDuration, _ := store.BumpChannelCooldown(ctx, channelCreated.ID, now, 403)
keyDuration, _ := store.BumpKeyCooldown(ctx, keyCreated.ID, 0, now, 403)
if channelDuration != keyDuration {
t.Errorf("❌ 403错误冷却时间不一致: 渠道级=%v, Key级=%v",
channelDuration, keyDuration)
}
if channelDuration != util.AuthErrorInitialCooldown {
t.Errorf("403错误初始冷却时间错误: 期望%v,实际%v",
util.AuthErrorInitialCooldown, channelDuration)
}
})
// 测试场景:其他错误码一致性(429/500)
t.Run("其他错误码冷却时间一致性", func(t *testing.T) {
testCases := []struct {
name string
statusCode int
expected time.Duration
}{
{"429限流错误", 429, util.RateLimitErrorCooldown},
{"500服务器错误", 500, util.ServerErrorInitialCooldown},
{"502网关错误", 502, util.ServerErrorInitialCooldown},
}
for _, tc := range testCases {
t.Run(tc.name, func(t *testing.T) {
channelCfg := &model.Config{
Name: "channel-" + tc.name,
URL: "https://api.example.com",
Enabled: true,
}
channelCreated, _ := store.CreateConfig(ctx, channelCfg)
keyCfg := &model.Config{
Name: "key-" + tc.name,
URL: "https://api.example.com",
Enabled: true,
}
keyCreated, _ := store.CreateConfig(ctx, keyCfg)
// 为Key测试渠道创建2个API Keys
_ = store.CreateAPIKeysBatch(ctx, []*model.APIKey{
{ChannelID: keyCreated.ID, KeyIndex: 0, APIKey: "sk-key1", KeyStrategy: model.KeyStrategySequential},
{ChannelID: keyCreated.ID, KeyIndex: 1, APIKey: "sk-key2", KeyStrategy: model.KeyStrategySequential},
})
channelDuration, _ := store.BumpChannelCooldown(ctx, channelCreated.ID, now, tc.statusCode)
keyDuration, _ := store.BumpKeyCooldown(ctx, keyCreated.ID, 0, now, tc.statusCode)
if channelDuration != keyDuration {
t.Errorf("❌ %s冷却时间不一致: 渠道级=%v, Key级=%v",
tc.name, channelDuration, keyDuration)
}
if channelDuration != tc.expected {
t.Errorf("%s初始冷却时间错误: 期望%v,实际%v",
tc.name, tc.expected, channelDuration)
}
})
}
})
}
// abs 计算time.Duration的绝对值
func abs(d time.Duration) time.Duration {
if d < 0 {
return -d
}
return d
}